This is an automated email from the ASF dual-hosted git repository.
kou pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git
The following commit(s) were added to refs/heads/main by this push:
new d5fa7cb610 GH-48575: [C++][FlightRPC] Standalone ODBC macOS CI (#48577)
d5fa7cb610 is described below
commit d5fa7cb610af764613773d626348b33859ef11dc
Author: Alina (Xi) Li <[email protected]>
AuthorDate: Sat Feb 7 03:54:25 2026 -0800
GH-48575: [C++][FlightRPC] Standalone ODBC macOS CI (#48577)
### Rationale for this change
#48575
### What changes are included in this PR?
- Add new ODBC workflow for macOS Intel 15 and 14 arm64.
- Added ODBC build fixes to enable build on macOS CI.
### Are these changes tested?
Tested in CI and local macOS Intel and M1 environments.
### Are there any user-facing changes?
N/A
* GitHub Issue: #48575
Lead-authored-by: Alina (Xi) Li <[email protected]>
Co-authored-by: justing-bq <[email protected]>
Co-authored-by: Victor Tsang <[email protected]>
Co-authored-by: Alina (Xi) Li <[email protected]>
Co-authored-by: vic-tsang <[email protected]>
Signed-off-by: Sutou Kouhei <[email protected]>
---
.github/workflows/cpp_extra.yml | 74 +++++++++++++++++++++-
ci/scripts/cpp_test.sh | 1 +
cpp/Brewfile | 1 +
cpp/CMakePresets.json | 22 +++++++
cpp/cmake_modules/DefineOptions.cmake | 4 +-
cpp/src/arrow/flight/sql/odbc/odbc_api.cc | 2 +-
.../arrow/flight/sql/odbc/odbc_impl/CMakeLists.txt | 17 +++--
.../flight/sql/odbc/odbc_impl/address_info.cc | 3 +-
.../sql/odbc/odbc_impl/config/configuration.h | 4 +-
.../arrow/flight/sql/odbc/odbc_impl/odbc_handle.h | 13 +++-
cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt | 8 +--
.../arrow/flight/sql/odbc/tests/connection_test.cc | 2 +-
.../arrow/flight/sql/odbc/tests/odbc_test_suite.cc | 6 +-
.../arrow/flight/sql/odbc/tests/odbc_test_suite.h | 5 +-
.../flight/sql/odbc/tests/statement_attr_test.cc | 4 ++
cpp/src/arrow/vendored/whereami/whereami.cc | 2 +-
16 files changed, 144 insertions(+), 24 deletions(-)
diff --git a/.github/workflows/cpp_extra.yml b/.github/workflows/cpp_extra.yml
index 3548381a9e..34b1251343 100644
--- a/.github/workflows/cpp_extra.yml
+++ b/.github/workflows/cpp_extra.yml
@@ -336,9 +336,76 @@ jobs:
cd cpp/examples/minimal_build
../minimal_build.build/arrow-example
- odbc:
+ odbc-macos:
needs: check-labels
- name: ODBC
+ name: ODBC ${{ matrix.architecture }} macOS ${{ matrix.macos-version }}
+ runs-on: macos-${{ matrix.macos-version }}
+ if: >-
+ needs.check-labels.outputs.force == 'true' ||
+ contains(fromJSON(needs.check-labels.outputs.ci-extra-labels || '[]'),
'CI: Extra') ||
+ contains(fromJSON(needs.check-labels.outputs.ci-extra-labels || '[]'),
'CI: Extra: C++')
+ timeout-minutes: 75
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - architecture: AMD64
+ macos-version: "15-intel"
+ - architecture: ARM64
+ macos-version: "14"
+ env:
+ ARROW_BUILD_TESTS: ON
+ ARROW_FLIGHT_SQL_ODBC: ON
+ ARROW_HOME: /tmp/local
+ steps:
+ - name: Checkout Arrow
+ uses: actions/[email protected]
+ with:
+ fetch-depth: 0
+ submodules: recursive
+ - name: Install Dependencies
+ run: |
+ brew bundle --file=cpp/Brewfile
+ - name: Setup ccache
+ run: |
+ ci/scripts/ccache_setup.sh
+ - name: ccache info
+ id: ccache-info
+ run: |
+ echo "cache-dir=$(ccache --get-config cache_dir)" >> $GITHUB_OUTPUT
+ - name: Cache ccache
+ uses: actions/[email protected]
+ with:
+ path: ${{ steps.ccache-info.outputs.cache-dir }}
+ key: cpp-odbc-ccache-macos-${{ matrix.macos-version }}-${{
hashFiles('cpp/**') }}
+ restore-keys: cpp-odbc-ccache-macos-${{ matrix.macos-version }}-
+ - name: Build
+ run: |
+ # Homebrew uses /usr/local as prefix. So packages
+ # installed by Homebrew also use /usr/local/include. We
+ # want to include headers for packages installed by
+ # Homebrew as system headers to ignore warnings in them.
+ # But "-isystem /usr/local/include" isn't used by CMake
+ # because /usr/local/include is marked as the default
+ # include path. So we disable -Werror to avoid build error
+ # by warnings from packages installed by Homebrew.
+ export BUILD_WARNING_LEVEL=PRODUCTION
+ LIBIODBC_DIR="$(brew --cellar libiodbc)/$(brew list --versions
libiodbc | awk '{print $2}')"
+ ODBC_INCLUDE_DIR=$LIBIODBC_DIR/include
+ export ARROW_CMAKE_ARGS="-DODBC_INCLUDE_DIR=$ODBC_INCLUDE_DIR"
+ export CXXFLAGS="$CXXFLAGS -I$ODBC_INCLUDE_DIR"
+ ci/scripts/cpp_build.sh $(pwd) $(pwd)/build
+ - name: Test
+ shell: bash
+ run: |
+ sudo sysctl -w kern.coredump=1
+ sudo sysctl -w kern.corefile=/tmp/core.%N.%P
+ ulimit -c unlimited # must enable within the same shell
+ ci/scripts/cpp_test.sh $(pwd) $(pwd)/build
+
+ odbc-msvc:
+ needs: check-labels
+ name: ODBC Windows
runs-on: windows-2022
if: >-
needs.check-labels.outputs.force == 'true' ||
@@ -519,6 +586,7 @@ jobs:
- jni-linux
- jni-macos
- msvc-arm64
- - odbc
+ - odbc-macos
+ - odbc-msvc
uses: ./.github/workflows/report_ci.yml
secrets: inherit
diff --git a/ci/scripts/cpp_test.sh b/ci/scripts/cpp_test.sh
index 5d6d5e099a..88239a0bd1 100755
--- a/ci/scripts/cpp_test.sh
+++ b/ci/scripts/cpp_test.sh
@@ -59,6 +59,7 @@ case "$(uname)" in
;;
Darwin)
n_jobs=$(sysctl -n hw.ncpu)
+ exclude_tests+=("arrow-flight-sql-odbc-test")
# TODO: https://github.com/apache/arrow/issues/40410
exclude_tests+=("arrow-s3fs-test")
;;
diff --git a/cpp/Brewfile b/cpp/Brewfile
index 4c42607568..811712516b 100644
--- a/cpp/Brewfile
+++ b/cpp/Brewfile
@@ -28,6 +28,7 @@ brew "git"
brew "glog"
brew "googletest"
brew "grpc"
+brew "libiodbc"
brew "llvm"
brew "lz4"
brew "mimalloc"
diff --git a/cpp/CMakePresets.json b/cpp/CMakePresets.json
index e2904db0de..c3499f6b00 100644
--- a/cpp/CMakePresets.json
+++ b/cpp/CMakePresets.json
@@ -315,6 +315,17 @@
"displayName": "Debug build with tests and Flight SQL",
"cacheVariables": {}
},
+ {
+ "name": "ninja-debug-flight-sql-odbc",
+ "inherits": [
+ "features-flight-sql",
+ "base-debug"
+ ],
+ "displayName": "Debug build with tests and Flight SQL ODBC",
+ "cacheVariables": {
+ "ARROW_FLIGHT_SQL_ODBC": "ON"
+ }
+ },
{
"name": "ninja-debug-gandiva",
"inherits": [
@@ -511,6 +522,17 @@
"displayName": "Release build with Flight SQL",
"cacheVariables": {}
},
+ {
+ "name": "ninja-release-flight-sql-odbc",
+ "inherits": [
+ "features-flight-sql",
+ "base-release"
+ ],
+ "displayName": "Release build with Flight SQL ODBC",
+ "cacheVariables": {
+ "ARROW_FLIGHT_SQL_ODBC": "ON"
+ }
+ },
{
"name": "ninja-release-gandiva",
"inherits": [
diff --git a/cpp/cmake_modules/DefineOptions.cmake
b/cpp/cmake_modules/DefineOptions.cmake
index 0f6674c714..5d34ff50e3 100644
--- a/cpp/cmake_modules/DefineOptions.cmake
+++ b/cpp/cmake_modules/DefineOptions.cmake
@@ -107,8 +107,8 @@ macro(tsort_bool_option_dependencies)
endmacro()
macro(resolve_option_dependencies)
- # Arrow Flight SQL ODBC is available only for Windows for now.
- if(NOT WIN32)
+ # Arrow Flight SQL ODBC is available only for Windows and macOS for now.
+ if(NOT WIN32 AND NOT APPLE)
set(ARROW_FLIGHT_SQL_ODBC OFF)
endif()
if(MSVC_TOOLCHAIN)
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
index b50c7db609..5676b9b05e 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
@@ -855,7 +855,7 @@ SQLRETURN SQLDriverConnect(SQLHDBC conn, SQLHWND
window_handle,
}
#else
// Attempt connection without loading DSN window on macOS/Linux
- connection->Connect(dsn, properties, missing_properties);
+ connection->Connect(dsn_value, properties, missing_properties);
#endif
// Copy connection string to out_connection_string after connection attempt
return ODBC::GetStringAttribute(true, connection_string, false,
out_connection_string,
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/CMakeLists.txt
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/CMakeLists.txt
index a1042cde97..e58558258d 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/CMakeLists.txt
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/CMakeLists.txt
@@ -45,10 +45,10 @@ add_library(arrow_odbc_spi_impl
config/connection_string_parser.h
diagnostics.cc
diagnostics.h
- error_codes.h
encoding.cc
encoding.h
encoding_utils.h
+ error_codes.h
exceptions.cc
exceptions.h
flight_sql_auth_method.cc
@@ -130,9 +130,18 @@ if(WIN32)
system_dsn.h)
endif()
-target_link_libraries(arrow_odbc_spi_impl
- PUBLIC arrow_flight_sql_shared arrow_compute_shared
Boost::locale
- ${ODBCINST})
+if(APPLE)
+ target_include_directories(arrow_odbc_spi_impl SYSTEM BEFORE PUBLIC
${ODBC_INCLUDE_DIR})
+ target_link_libraries(arrow_odbc_spi_impl
+ PUBLIC arrow_flight_sql_shared arrow_compute_shared
Boost::locale
+ iodbc)
+else()
+ find_package(ODBC REQUIRED)
+ target_include_directories(arrow_odbc_spi_impl PUBLIC ${ODBC_INCLUDE_DIR})
+ target_link_libraries(arrow_odbc_spi_impl
+ PUBLIC arrow_flight_sql_shared arrow_compute_shared
Boost::locale
+ ${ODBCINST})
+endif()
set_target_properties(arrow_odbc_spi_impl
PROPERTIES ARCHIVE_OUTPUT_DIRECTORY
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/address_info.cc
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/address_info.cc
index 5ee6674c3c..7bdb4d58cf 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/address_info.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/address_info.cc
@@ -16,6 +16,7 @@
// under the License.
#include "arrow/flight/sql/odbc/odbc_impl/address_info.h"
+#include <cstdint>
namespace driver {
@@ -34,7 +35,7 @@ bool AddressInfo::GetAddressInfo(const std::string& host,
char* host_name_info,
}
error = getnameinfo(addrinfo_result_->ai_addr, addrinfo_result_->ai_addrlen,
- host_name_info, static_cast<DWORD>(max_host), NULL, 0,
0);
+ host_name_info, static_cast<uint32_t>(max_host), NULL,
0, 0);
return error == 0;
}
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/config/configuration.h
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/config/configuration.h
index 9b59f346b2..66a2676399 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/config/configuration.h
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/config/configuration.h
@@ -22,8 +22,10 @@
#include "arrow/flight/sql/odbc/odbc_impl/platform.h"
#include "arrow/flight/sql/odbc/odbc_impl/spi/connection.h"
+#if defined _WIN32
// winuser.h needs to be included after windows.h, which is defined in
platform.h
-#include <winuser.h>
+# include <winuser.h>
+#endif
namespace arrow::flight::sql::odbc {
namespace config {
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_handle.h
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_handle.h
index b3fd6e371a..9dd8fe37ba 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_handle.h
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_handle.h
@@ -46,7 +46,18 @@ class ODBCHandle {
try {
GetDiagnostics().Clear();
rc = function();
- } catch (const arrow::flight::sql::odbc::DriverException& ex) {
+ } catch (const arrow::flight::sql::odbc::AuthenticationException& ex) {
+ GetDiagnostics().AddError(arrow::flight::sql::odbc::DriverException(
+ ex.GetMessageText(), ex.GetSqlState(), ex.GetNativeError()));
+ } catch (const arrow::flight::sql::odbc::NullWithoutIndicatorException&
ex) {
+ GetDiagnostics().AddError(arrow::flight::sql::odbc::DriverException(
+ ex.GetMessageText(), ex.GetSqlState(), ex.GetNativeError()));
+ }
+ // on mac, DriverException doesn't catch the subclass exceptions hence we
added
+ // the following above.
+ // GH-48278 TODO investigate if there is a way to catch all the subclass
exceptions
+ // under DriverException
+ catch (const arrow::flight::sql::odbc::DriverException& ex) {
GetDiagnostics().AddError(ex);
} catch (const std::bad_alloc&) {
GetDiagnostics().AddError(arrow::flight::sql::odbc::DriverException(
diff --git a/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt
b/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt
index 5485ef9b4d..ef0c7271ec 100644
--- a/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt
+++ b/cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt
@@ -15,11 +15,6 @@
# specific language governing permissions and limitations
# under the License.
-add_custom_target(tests)
-
-find_package(ODBC REQUIRED)
-include_directories(${ODBC_INCLUDE_DIRS})
-
find_package(SQLite3Alt REQUIRED)
set(ARROW_FLIGHT_SQL_MOCK_SERVER_SRCS
@@ -54,5 +49,8 @@ add_arrow_test(flight_sql_odbc_test
${SQLite3_LIBRARIES}
arrow_odbc_spi_impl)
+find_package(ODBC REQUIRED)
+target_link_libraries(arrow-flight-sql-odbc-test PRIVATE ODBC::ODBC)
+
# Disable unity build due to sqlite_sql_info.cc conflict with sql.h and
sqlext.h headers.
set_target_properties(arrow-flight-sql-odbc-test PROPERTIES UNITY_BUILD OFF)
diff --git a/cpp/src/arrow/flight/sql/odbc/tests/connection_test.cc
b/cpp/src/arrow/flight/sql/odbc/tests/connection_test.cc
index b1081bc1d6..3ca4a50ef7 100644
--- a/cpp/src/arrow/flight/sql/odbc/tests/connection_test.cc
+++ b/cpp/src/arrow/flight/sql/odbc/tests/connection_test.cc
@@ -442,7 +442,7 @@ TEST_F(ConnectionRemoteTest,
TestSQLDriverConnectInvalidUid) {
arrow::util::UTF8ToWideString(connect_str));
std::vector<SQLWCHAR> connect_str0(wconnect_str.begin(), wconnect_str.end());
- SQLWCHAR out_str[kOdbcBufferSize];
+ SQLWCHAR out_str[kOdbcBufferSize] = {0};
SQLSMALLINT out_str_len;
// Connecting to ODBC server.
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 3f12e35c6d..470a68b3be 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
@@ -130,9 +130,9 @@ std::wstring ODBCRemoteTestBase::GetQueryAllDataTypes() {
CAST(true AS BOOLEAN) AS bit_true,
--Character types
- 'Z' AS c_char, '你' AS c_wchar,
+ 'Z' AS c_char, _utf8'你' AS c_wchar,
- '你好' AS c_wvarchar,
+ _utf8'你好' AS c_wvarchar,
'XYZ' AS c_varchar,
@@ -245,7 +245,7 @@ std::string ODBCMockTestBase::GetConnectionString() {
std::string connect_str(
"driver={Apache Arrow Flight SQL ODBC Driver};HOST=localhost;port=" +
std::to_string(port) + ";token=" + std::string(kTestToken) +
- ";useEncryption=false;");
+ ";useEncryption=false;UseWideChar=true;");
return connect_str;
}
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 7dd77d8fa6..3115cd6275 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
@@ -216,8 +216,8 @@ bool CompareConnPropertyMap(Connection::ConnPropertyMap
map1,
std::string GetOdbcErrorMessage(SQLSMALLINT handle_type, SQLHANDLE handle);
static constexpr std::string_view kErrorState01004 = "01004";
-static constexpr std::string_view kErrorState01S07 = "01S07";
static constexpr std::string_view kErrorState01S02 = "01S02";
+static constexpr std::string_view kErrorState01S07 = "01S07";
static constexpr std::string_view kErrorState07009 = "07009";
static constexpr std::string_view kErrorState08003 = "08003";
static constexpr std::string_view kErrorState22002 = "22002";
@@ -236,7 +236,10 @@ static constexpr std::string_view kErrorStateHY106 =
"HY106";
static constexpr std::string_view kErrorStateHY114 = "HY114";
static constexpr std::string_view kErrorStateHY118 = "HY118";
static constexpr std::string_view kErrorStateHYC00 = "HYC00";
+static constexpr std::string_view kErrorStateS1002 = "S1002";
static constexpr std::string_view kErrorStateS1004 = "S1004";
+static constexpr std::string_view kErrorStateS1010 = "S1010";
+static constexpr std::string_view kErrorStateS1090 = "S1090";
/// Verify ODBC Error State
void VerifyOdbcErrorState(SQLSMALLINT handle_type, SQLHANDLE handle,
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 5b6821430a..0a4e99d33a 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
@@ -63,6 +63,8 @@ void GetStmtAttr(SQLHSTMT statement, SQLINTEGER attribute,
SQLPOINTER* value) {
SQLGetStmtAttr(statement, attribute, value, SQL_IS_POINTER,
&string_length));
}
+#if defined(SQL_ATTR_ASYNC_STMT_EVENT) ||
defined(SQL_ATTR_ASYNC_STMT_PCALLBACK) || \
+ defined(SQL_ATTR_ASYNC_STMT_PCONTEXT)
// Validate error return value and code
void ValidateGetStmtAttrErrorCode(SQLHSTMT statement, SQLINTEGER attribute,
std::string_view error_code) {
@@ -74,6 +76,8 @@ void ValidateGetStmtAttrErrorCode(SQLHSTMT statement,
SQLINTEGER attribute,
VerifyOdbcErrorState(SQL_HANDLE_STMT, statement, error_code);
}
+#endif // SQL_ATTR_ASYNC_STMT_EVENT || SQL_ATTR_ASYNC_STMT_PCALLBACK ||
+ // SQL_ATTR_ASYNC_STMT_PCONTEXT
// Validate return value for call to SQLSetStmtAttr with SQLULEN
void ValidateSetStmtAttr(SQLHSTMT statement, SQLINTEGER attribute, SQLULEN
new_value) {
diff --git a/cpp/src/arrow/vendored/whereami/whereami.cc
b/cpp/src/arrow/vendored/whereami/whereami.cc
index 945226193f..94437361ec 100644
--- a/cpp/src/arrow/vendored/whereami/whereami.cc
+++ b/cpp/src/arrow/vendored/whereami/whereami.cc
@@ -159,7 +159,7 @@ WAI_NOINLINE WAI_FUNCSPEC int
WAI_PREFIX(getModulePath)(char* out, int capacity,
return length;
}
-#elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun) || \
+#elif defined(__APPLE__) || defined(__linux__) || defined(__CYGWIN__) ||
defined(__sun) || \
defined(WAI_USE_PROC_SELF_EXE)
# include <stdio.h>