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 d5e58658ca GH-49463: [C++][FlightRPC] Add Ubuntu ODBC Support (#49564)
d5e58658ca is described below

commit d5e58658ca8c927063aa0a92a85b99dacedd197f
Author: Alina (Xi) Li <[email protected]>
AuthorDate: Sat Mar 28 01:24:56 2026 -0700

    GH-49463: [C++][FlightRPC] Add Ubuntu ODBC Support (#49564)
    
    ### Rationale for this change
    GH-49463
    Add Ubuntu support so users can connect using odbc on Linux.
    
    ### What changes are included in this PR?
    - Enable Linux ODBC build with unicode support
    - Add ODBC Ubuntu build in CI
    - Added `docker-compose` for Flight SQL ODBC
    - Register ODBC after build
    - Replaced `boost::lexicographical_compare` with 
`std::lexicographical_compare`
    - Fixed conversion bugs in `SetAttributeSQLWCHAR`
    - Convert from std::string to std::u16string directly without involving 
wide string (wstring)
    - Enabling ODBC Linux test build will be added in a separate PR
    
    ### Are these changes tested?
    - Ubuntu build is tested in CI
    - ODBC is tested with `isql` on local docker, and confirmed to be able to 
connect to online remote instance
    
    ### Are there any user-facing changes?
    N/A
    
    * GitHub Issue: #49463
    
    Lead-authored-by: Alina (Xi) Li <[email protected]>
    Co-authored-by: Alina (Xi) Li <[email protected]>
    Co-authored-by: Alina (Xi) Li <[email protected]>
    Signed-off-by: Sutou Kouhei <[email protected]>
---
 .github/workflows/cpp_extra.yml                    | 59 ++++++++++++++-
 .pre-commit-config.yaml                            |  2 +-
 ci/docker/ubuntu-24.04-cpp.dockerfile              |  1 +
 compose.yaml                                       | 30 +++++++-
 cpp/cmake_modules/DefineOptions.cmake              |  4 -
 cpp/cmake_modules/ThirdpartyToolchain.cmake        |  2 +-
 cpp/src/arrow/flight/sql/odbc/CMakeLists.txt       |  7 +-
 .../sql/odbc/install/{mac => unix}/install_odbc.sh | 14 +++-
 cpp/src/arrow/flight/sql/odbc/odbc_api.cc          |  2 +
 cpp/src/arrow/flight/sql/odbc/odbc_api_internal.h  |  3 +-
 .../arrow/flight/sql/odbc/odbc_impl/CMakeLists.txt | 20 +++--
 .../accessors/binary_array_accessor_test.cc        |  2 +-
 .../accessors/boolean_array_accessor_test.cc       |  2 +-
 .../accessors/decimal_array_accessor_test.cc       |  2 +-
 .../accessors/primitive_array_accessor_test.cc     |  2 +-
 .../accessors/string_array_accessor_test.cc        |  4 +-
 .../flight/sql/odbc/odbc_impl/accessors/types.h    | 10 +--
 .../flight/sql/odbc/odbc_impl/attribute_utils.h    | 10 +++
 .../flight/sql/odbc/odbc_impl/blocking_queue.h     |  2 +-
 .../sql/odbc/odbc_impl/config/configuration.cc     | 66 ++++++++++------
 .../flight/sql/odbc/odbc_impl/encoding_utils.h     | 37 ++++++++-
 .../sql/odbc/odbc_impl/flight_sql_connection.cc    |  7 +-
 .../sql/odbc/odbc_impl/flight_sql_result_set.cc    |  2 +-
 .../sql/odbc/odbc_impl/flight_sql_statement.cc     |  4 +-
 .../flight/sql/odbc/odbc_impl/get_info_cache.cc    | 11 +--
 cpp/src/arrow/flight/sql/odbc/odbc_impl/main.cc    |  6 +-
 .../flight/sql/odbc/odbc_impl/odbc_connection.cc   |  9 ++-
 .../flight/sql/odbc/odbc_impl/odbc_descriptor.cc   | 14 ++--
 .../flight/sql/odbc/odbc_impl/odbc_includes.h      | 28 +++++++
 .../flight/sql/odbc/odbc_impl/odbc_statement.cc    | 14 ++--
 .../flight/sql/odbc/odbc_impl/odbc_statement.h     |  2 +-
 .../flight/sql/odbc/odbc_impl/spi/connection.h     |  7 +-
 .../flight/sql/odbc/odbc_impl/spi/result_set.h     |  2 +-
 .../flight/sql/odbc/odbc_impl/spi/statement.h      |  1 +
 .../arrow/flight/sql/odbc/odbc_impl/system_dsn.cc  | 88 +++++++++++++---------
 cpp/src/arrow/flight/sql/odbc/odbc_impl/util.cc    |  3 +
 cpp/src/arrow/flight/sql/odbc/odbc_impl/util.h     | 16 ++++
 cpp/src/arrow/flight/sql/odbc/tests/errors_test.cc |  1 -
 cpp/src/arrow/symbols.map                          |  2 +
 39 files changed, 372 insertions(+), 126 deletions(-)

diff --git a/.github/workflows/cpp_extra.yml b/.github/workflows/cpp_extra.yml
index f13b01041a..199c7e2d49 100644
--- a/.github/workflows/cpp_extra.yml
+++ b/.github/workflows/cpp_extra.yml
@@ -346,6 +346,62 @@ jobs:
           cd cpp/examples/minimal_build
           ../minimal_build.build/arrow-example
 
+  odbc-linux:
+    needs: check-labels
+    name: ODBC Linux
+    runs-on: ubuntu-latest
+    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
+    env:
+      ARCH: amd64
+      ARCHERY_DEBUG: 1
+      ARROW_ENABLE_TIMING_TESTS: OFF
+      DOCKER_VOLUME_PREFIX: ".docker/"
+      UBUNTU: 24.04
+    steps:
+      - name: Checkout Arrow
+        uses: actions/checkout@v6
+        with:
+          fetch-depth: 0
+          submodules: recursive
+      - name: Cache Docker Volumes
+        uses: actions/cache@v5
+        with:
+          path: .docker
+          key: ubuntu-cpp-odbc-${{ hashFiles('cpp/**') }}
+          restore-keys: ubuntu-cpp-odbc-
+      - name: Setup Python on hosted runner
+        uses: actions/setup-python@v6
+        with:
+          python-version: 3
+      - name: Setup Archery
+        run: python3 -m pip install -e dev/archery[docker]
+      - name: Execute Docker Build
+        env:
+          ARCHERY_DOCKER_USER: ${{ secrets.DOCKERHUB_USER }}
+          ARCHERY_DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }}
+        run: |
+          # GH-40558: reduce ASLR to avoid ASAN/LSAN crashes
+          sudo sysctl -w vm.mmap_rnd_bits=28
+          source ci/scripts/util_enable_core_dumps.sh
+          archery docker run ubuntu-cpp-odbc
+      - name: Docker Push
+        if: >-
+          success() &&
+          github.event_name == 'push' &&
+          github.repository == 'apache/arrow' &&
+          github.ref_name == 'main'
+        env:
+          ARCHERY_DOCKER_USER: ${{ secrets.DOCKERHUB_USER }}
+          ARCHERY_DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }}
+        continue-on-error: true
+        run: archery docker push ubuntu-cpp-odbc
+
   odbc-macos:
     needs: check-labels
     name: ODBC ${{ matrix.build-type }} ${{ matrix.architecture }} macOS ${{ 
matrix.macos-version }}
@@ -445,7 +501,7 @@ jobs:
             "$(pwd)/build/cpp/${{ matrix.build-type 
}}/libarrow_flight_sql_odbc.dylib"
       - name: Register Flight SQL ODBC Driver
         run: |
-          sudo cpp/src/arrow/flight/sql/odbc/install/mac/install_odbc.sh 
$(pwd)/build/cpp/${{ matrix.build-type }}/libarrow_flight_sql_odbc.dylib
+          sudo cpp/src/arrow/flight/sql/odbc/install/unix/install_odbc.sh 
$(pwd)/build/cpp/${{ matrix.build-type }}/libarrow_flight_sql_odbc.dylib
       - name: Test
         shell: bash
         run: |
@@ -708,6 +764,7 @@ jobs:
       - jni-linux
       - jni-macos
       - msvc-arm64
+      - odbc-linux
       - odbc-macos
       - odbc-msvc
       - odbc-nightly
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index a33aa3acb4..67fb3f34f2 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -353,7 +353,7 @@ repos:
           ?^cpp/build-support/update-thrift\.sh$|
           ?^cpp/examples/minimal_build/run\.sh$|
           ?^cpp/examples/tutorial_examples/run\.sh$|
-          ?^cpp/src/arrow/flight/sql/odbc/install/mac/install_odbc\.sh$|
+          ?^cpp/src/arrow/flight/sql/odbc/install/unix/install_odbc\.sh$|
           ?^dev/release/05-binary-upload\.sh$|
           ?^dev/release/08-binary-verify\.sh$|
           ?^dev/release/binary-recover\.sh$|
diff --git a/ci/docker/ubuntu-24.04-cpp.dockerfile 
b/ci/docker/ubuntu-24.04-cpp.dockerfile
index 7703046c75..5953c49ff9 100644
--- a/ci/docker/ubuntu-24.04-cpp.dockerfile
+++ b/ci/docker/ubuntu-24.04-cpp.dockerfile
@@ -121,6 +121,7 @@ RUN apt-get update -y -q && \
         rsync \
         tzdata \
         tzdata-legacy \
+        unixodbc-dev \
         uuid-runtime \
         unzip \
         wget && \
diff --git a/compose.yaml b/compose.yaml
index 16b44c2edf..47d135face 100644
--- a/compose.yaml
+++ b/compose.yaml
@@ -151,6 +151,7 @@ x-hierarchy:
     - ubuntu-r-only-r
   - ubuntu-cpp-bundled
   - ubuntu-cpp-bundled-offline
+  - ubuntu-cpp-odbc
   - ubuntu-cpp-minimal
   - ubuntu-cuda-cpp:
     - ubuntu-cuda-python
@@ -371,7 +372,7 @@ services:
         /arrow/ci/scripts/cpp_build.sh /arrow /build &&
         /arrow/ci/scripts/cpp_test.sh /arrow /build"
 
-  ubuntu-cpp:
+  ubuntu-cpp: &ubuntu-cpp-base
     # Usage:
     #   docker compose build ubuntu-cpp
     #   docker compose run --rm ubuntu-cpp
@@ -496,6 +497,33 @@ services:
     volumes: *ubuntu-volumes
     command: *cpp-command
 
+  ubuntu-cpp-odbc:
+    # Arrow Flight SQL ODBC build with BUNDLED dependencies with downloaded 
dependencies.
+    <<: *ubuntu-cpp-base
+    environment:
+      <<: [*common, *ccache, *sccache, *cpp]
+      ARROW_ACERO: "OFF"
+      ARROW_AZURE: "OFF"
+      ARROW_BUILD_TYPE: RELEASE
+      ARROW_CSV: "OFF"
+      ARROW_DATASET: "OFF"
+      ARROW_DEPENDENCY_SOURCE: BUNDLED
+      ARROW_DEPENDENCY_USE_SHARED: "OFF"
+      ARROW_FLIGHT_SQL_ODBC: "ON"
+      ARROW_GANDIVA: "OFF"
+      ARROW_GCS: "OFF"
+      ARROW_HDFS: "OFF"
+      ARROW_ORC: "OFF"
+      ARROW_PARQUET: "OFF"
+      ARROW_S3: "OFF"
+      ARROW_SUBSTRAIT: "OFF"
+    # Register ODBC before running tests
+    command: >
+      /bin/bash -c "
+        /arrow/ci/scripts/cpp_build.sh /arrow /build &&
+        sudo /arrow/cpp/src/arrow/flight/sql/odbc/install/unix/install_odbc.sh 
/usr/local/lib/libarrow_flight_sql_odbc.so &&
+        /arrow/ci/scripts/cpp_test.sh /arrow /build"
+
   ubuntu-cpp-minimal:
     # Arrow build with minimal components/dependencies
     image: ${REPO}:${ARCH}-ubuntu-${UBUNTU}-cpp-minimal
diff --git a/cpp/cmake_modules/DefineOptions.cmake 
b/cpp/cmake_modules/DefineOptions.cmake
index 5d34ff50e3..017a5a6efb 100644
--- a/cpp/cmake_modules/DefineOptions.cmake
+++ b/cpp/cmake_modules/DefineOptions.cmake
@@ -107,10 +107,6 @@ macro(tsort_bool_option_dependencies)
 endmacro()
 
 macro(resolve_option_dependencies)
-  # 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)
     set(ARROW_USE_GLOG OFF)
   endif()
diff --git a/cpp/cmake_modules/ThirdpartyToolchain.cmake 
b/cpp/cmake_modules/ThirdpartyToolchain.cmake
index 3a18b7c84c..0a4c60e3ce 100644
--- a/cpp/cmake_modules/ThirdpartyToolchain.cmake
+++ b/cpp/cmake_modules/ThirdpartyToolchain.cmake
@@ -1090,7 +1090,7 @@ function(build_boost)
     set(ARROW_BOOST_NEED_MULTIPRECISION FALSE)
   endif()
   if(ARROW_ENABLE_THREADING)
-    if(ARROW_WITH_THRIFT OR (ARROW_FLIGHT_SQL_ODBC AND MSVC))
+    if(ARROW_WITH_THRIFT OR ARROW_FLIGHT_SQL_ODBC)
       list(APPEND BOOST_INCLUDE_LIBRARIES locale)
     endif()
     if(ARROW_BOOST_NEED_MULTIPRECISION)
diff --git a/cpp/src/arrow/flight/sql/odbc/CMakeLists.txt 
b/cpp/src/arrow/flight/sql/odbc/CMakeLists.txt
index 2560cdccd0..4227873706 100644
--- a/cpp/src/arrow/flight/sql/odbc/CMakeLists.txt
+++ b/cpp/src/arrow/flight/sql/odbc/CMakeLists.txt
@@ -28,7 +28,12 @@ else()
 endif()
 
 add_subdirectory(odbc_impl)
-add_subdirectory(tests)
+if(ARROW_BUILD_TESTS)
+  if(WIN32 OR APPLE)
+    # GH-49552 TODO: Enable Linux test build
+    add_subdirectory(tests)
+  endif()
+endif()
 
 arrow_install_all_headers("arrow/flight/sql/odbc")
 
diff --git a/cpp/src/arrow/flight/sql/odbc/install/mac/install_odbc.sh 
b/cpp/src/arrow/flight/sql/odbc/install/unix/install_odbc.sh
similarity index 92%
rename from cpp/src/arrow/flight/sql/odbc/install/mac/install_odbc.sh
rename to cpp/src/arrow/flight/sql/odbc/install/unix/install_odbc.sh
index 069c534c29..5ddcc8a4cb 100755
--- a/cpp/src/arrow/flight/sql/odbc/install/mac/install_odbc.sh
+++ b/cpp/src/arrow/flight/sql/odbc/install/unix/install_odbc.sh
@@ -40,10 +40,18 @@ if [ ! -f "$ODBC_64BIT" ]; then
   exit 1
 fi
 
-USER_ODBCINST_FILE="$HOME/Library/ODBC/odbcinst.ini"
-DRIVER_NAME="Apache Arrow Flight SQL ODBC Driver"
+case "$(uname)" in
+  Linux)
+    USER_ODBCINST_FILE="/etc/odbcinst.ini"
+    ;;
+  *)
+    # macOS
+    USER_ODBCINST_FILE="$HOME/Library/ODBC/odbcinst.ini"
+    mkdir -p "$HOME"/Library/ODBC
+    ;;
+esac
 
-mkdir -p "$HOME"/Library/ODBC
+DRIVER_NAME="Apache Arrow Flight SQL ODBC Driver"
 
 touch "$USER_ODBCINST_FILE"
 
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc 
b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
index 75b35b688a..7c64dee21f 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc
@@ -37,6 +37,8 @@
 #endif  // defined(_WIN32)
 
 namespace arrow::flight::sql::odbc {
+void LoadPropertiesFromDSN(const std::string& dsn, 
Connection::ConnPropertyMap& props);
+
 SQLRETURN SQLAllocHandle(SQLSMALLINT type, SQLHANDLE parent, SQLHANDLE* 
result) {
   ARROW_LOG(DEBUG) << "SQLAllocHandle called with type: " << type
                    << ", parent: " << parent
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api_internal.h 
b/cpp/src/arrow/flight/sql/odbc/odbc_api_internal.h
index f9d8d887cb..0f581d734e 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_api_internal.h
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_api_internal.h
@@ -20,8 +20,7 @@
 #include "arrow/flight/sql/odbc/odbc_impl/platform.h"
 
 #include <sql.h>
-#include <sqltypes.h>
-#include <sqlucode.h>
+#include <sqlext.h>
 
 //  \file odbc_api_internal.h
 //
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 74c60cd916..5a16c0361f 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/CMakeLists.txt
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/CMakeLists.txt
@@ -130,7 +130,14 @@ if(WIN32)
                          win_system_dsn.cc)
 endif()
 
-if(APPLE)
+if(WIN32)
+  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})
+else()
+  # Unix
   target_include_directories(arrow_odbc_spi_impl SYSTEM BEFORE PUBLIC 
${ODBC_INCLUDE_DIR})
   target_link_libraries(arrow_odbc_spi_impl
                         PUBLIC arrow_flight_sql_static
@@ -138,12 +145,11 @@ if(APPLE)
                                Boost::locale
                                Boost::headers
                                RapidJSON)
-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})
+
+  if(NOT APPLE)
+    # Explicitly link to unix-odbc on Linux
+    target_link_libraries(arrow_odbc_spi_impl PUBLIC ${ODBCINST})
+  endif()
 endif()
 
 # CLI
diff --git 
a/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/binary_array_accessor_test.cc
 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/binary_array_accessor_test.cc
index 39d03692da..55ef46458e 100644
--- 
a/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/binary_array_accessor_test.cc
+++ 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/binary_array_accessor_test.cc
@@ -42,7 +42,7 @@ TEST(BinaryArrayAccessor, Test_CDataType_BINARY_Basic) {
             accessor.GetColumnarData(&binding, 0, values.size(), value_offset, 
false,
                                      diagnostics, nullptr));
 
-  for (int i = 0; i < values.size(); ++i) {
+  for (size_t i = 0; i < values.size(); ++i) {
     ASSERT_EQ(values[i].length(), str_len_buffer[i]);
     // Beware that CDataType_BINARY values are not null terminated.
     // It's safe to create a std::string from this data because we know it's
diff --git 
a/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/boolean_array_accessor_test.cc
 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/boolean_array_accessor_test.cc
index 9b17a90459..68583b3f15 100644
--- 
a/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/boolean_array_accessor_test.cc
+++ 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/boolean_array_accessor_test.cc
@@ -39,7 +39,7 @@ TEST(BooleanArrayFlightSqlAccessor, 
Test_BooleanArray_CDataType_BIT) {
             accessor.GetColumnarData(&binding, 0, values.size(), value_offset, 
false,
                                      diagnostics, nullptr));
 
-  for (int i = 0; i < values.size(); ++i) {
+  for (size_t i = 0; i < values.size(); ++i) {
     ASSERT_EQ(sizeof(unsigned char), str_len_buffer[i]);
     ASSERT_EQ(values[i] ? 1 : 0, buffer[i]);
   }
diff --git 
a/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/decimal_array_accessor_test.cc
 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/decimal_array_accessor_test.cc
index b2eb9450c2..9833ea1d59 100644
--- 
a/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/decimal_array_accessor_test.cc
+++ 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/decimal_array_accessor_test.cc
@@ -83,7 +83,7 @@ void AssertNumericOutput(int input_precision, int input_scale,
             accessor.GetColumnarData(&binding, 0, values.size(), value_offset, 
false,
                                      diagnostics, nullptr));
 
-  for (int i = 0; i < values.size(); ++i) {
+  for (size_t i = 0; i < values.size(); ++i) {
     ASSERT_EQ(sizeof(NUMERIC_STRUCT), str_len_buffer[i]);
 
     ASSERT_EQ(output_precision, buffer[i].precision);
diff --git 
a/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/primitive_array_accessor_test.cc
 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/primitive_array_accessor_test.cc
index 2f04b7324a..bd9ecf3314 100644
--- 
a/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/primitive_array_accessor_test.cc
+++ 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/primitive_array_accessor_test.cc
@@ -46,7 +46,7 @@ void TestPrimitiveArraySqlAccessor() {
             accessor.GetColumnarData(&binding, 0, values.size(), value_offset, 
false,
                                      diagnostics, nullptr));
 
-  for (int i = 0; i < values.size(); ++i) {
+  for (size_t i = 0; i < values.size(); ++i) {
     ASSERT_EQ(sizeof(c_type), str_len_buffer[i]);
     ASSERT_EQ(values[i], buffer[i]);
   }
diff --git 
a/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/string_array_accessor_test.cc
 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/string_array_accessor_test.cc
index eb7a9c88b3..9316571dd7 100644
--- 
a/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/string_array_accessor_test.cc
+++ 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/string_array_accessor_test.cc
@@ -43,7 +43,7 @@ TEST(StringArrayAccessor, Test_CDataType_CHAR_Basic) {
             accessor.GetColumnarData(&binding, 0, values.size(), value_offset, 
false,
                                      diagnostics, nullptr));
 
-  for (int i = 0; i < values.size(); ++i) {
+  for (size_t i = 0; i < values.size(); ++i) {
     ASSERT_EQ(values[i].length(), str_len_buffer[i]);
     ASSERT_EQ(values[i], std::string(buffer.data() + i * max_str_len));
   }
@@ -102,7 +102,7 @@ TEST(StringArrayAccessor, Test_CDataType_WCHAR_Basic) {
             accessor->GetColumnarData(&binding, 0, values.size(), 
value_offset, false,
                                       diagnostics, nullptr));
 
-  for (int i = 0; i < values.size(); ++i) {
+  for (size_t i = 0; i < values.size(); ++i) {
     ASSERT_EQ(values[i].length() * GetSqlWCharSize(), str_len_buffer[i]);
     std::vector<uint8_t> expected;
     Utf8ToWcs(values[i].c_str(), &expected);
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/types.h 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/types.h
index ca33d872fa..f75d2894af 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/types.h
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/accessors/types.h
@@ -43,12 +43,12 @@ struct ColumnBinding {
 
   ColumnBinding(CDataType target_type, int precision, int scale, void* buffer,
                 size_t buffer_length, ssize_t* str_len_buffer)
-      : target_type(target_type),
-        precision(precision),
-        scale(scale),
-        buffer(buffer),
+      : buffer(buffer),
+        str_len_buffer(str_len_buffer),
         buffer_length(buffer_length),
-        str_len_buffer(str_len_buffer) {}
+        target_type(target_type),
+        precision(precision),
+        scale(scale) {}
 };
 
 /// \brief Accessor interface meant to provide a way of populating data of a
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/attribute_utils.h 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/attribute_utils.h
index 315d854b60..cc1521523d 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/attribute_utils.h
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/attribute_utils.h
@@ -158,11 +158,21 @@ inline void SetAttributeSQLWCHAR(SQLPOINTER new_value, 
SQLINTEGER input_length_i
   thread_local std::vector<uint8_t> utf8_str;
   if (input_length_in_bytes == SQL_NTS) {
     arrow::flight::sql::odbc::WcsToUtf8(new_value, &utf8_str);
+  } else if (input_length_in_bytes <= 0) {
+    // empty string
+    attribute_to_write.clear();
+    return;
   } else {
     arrow::flight::sql::odbc::WcsToUtf8(
         new_value, input_length_in_bytes / 
arrow::flight::sql::odbc::GetSqlWCharSize(),
         &utf8_str);
   }
+
+  // add null-terminator
+  if (utf8_str.back() != '\0') {
+    utf8_str.push_back('\0');
+  }
+
   attribute_to_write.assign((char*)utf8_str.data());
 }
 
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/blocking_queue.h 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/blocking_queue.h
index e52c305e46..55ec3a7bea 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/blocking_queue.h
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/blocking_queue.h
@@ -49,7 +49,7 @@ class BlockingQueue {
 
   void AddProducer(Supplier supplier) {
     active_threads_++;
-    threads_.emplace_back([=] {
+    threads_.emplace_back([this, supplier] {
       while (!closed_) {
         // Block while queue is full
         std::unique_lock<std::mutex> unique_lock(mtx_);
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/config/configuration.cc 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/config/configuration.cc
index 0e49613979..e18ab0bae8 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/config/configuration.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/config/configuration.cc
@@ -15,8 +15,12 @@
 // specific language governing permissions and limitations
 // under the License.
 
-#include "arrow/flight/sql/odbc/odbc_impl/config/configuration.h"
+// flight_sql_connection.h needs to be included first due to conflicts with 
windows.h
 #include "arrow/flight/sql/odbc/odbc_impl/flight_sql_connection.h"
+
+#include "arrow/flight/sql/odbc/odbc_impl/config/configuration.h"
+
+#include "arrow/flight/sql/odbc/odbc_impl/attribute_utils.h"
 #include "arrow/flight/sql/odbc/odbc_impl/util.h"
 #include "arrow/result.h"
 #include "arrow/util/utf8.h"
@@ -27,6 +31,8 @@
 #include <iterator>
 #include <sstream>
 
+using ODBC::SetAttributeSQLWCHAR;
+
 namespace arrow::flight::sql::odbc {
 namespace config {
 static const char DEFAULT_DSN[] = "Apache Arrow Flight SQL";
@@ -35,28 +41,30 @@ static const char DEFAULT_USE_CERT_STORE[] = TRUE_STR;
 static const char DEFAULT_DISABLE_CERT_VERIFICATION[] = FALSE_STR;
 
 namespace {
-std::string ReadDsnString(const std::string& dsn, std::string_view key,
+std::string ReadDsnString(const std::string& dsn, const std::string_view& key,
                           const std::string& dflt = "") {
-  CONVERT_WIDE_STR(const std::wstring wdsn, dsn);
-  CONVERT_WIDE_STR(const std::wstring wkey, key);
-  CONVERT_WIDE_STR(const std::wstring wdflt, dflt);
+  CONVERT_SQLWCHAR_STR(wdsn, dsn);
+  CONVERT_SQLWCHAR_STR(wkey, key);
+  CONVERT_SQLWCHAR_STR(wdflt, dflt);
 
 #define BUFFER_SIZE (1024)
-  std::vector<wchar_t> buf(BUFFER_SIZE);
-  int ret =
-      SQLGetPrivateProfileString(wdsn.c_str(), wkey.c_str(), wdflt.c_str(), 
buf.data(),
-                                 static_cast<int>(buf.size()), L"ODBC.INI");
+  std::vector<SQLWCHAR> buf(BUFFER_SIZE);
+  int ret = SQLGetPrivateProfileString(
+      reinterpret_cast<LPCWSTR>(wdsn.c_str()), 
reinterpret_cast<LPCWSTR>(wkey.c_str()),
+      reinterpret_cast<LPCWSTR>(wdflt.c_str()), buf.data(), 
static_cast<int>(buf.size()),
+      ODBC_INI);
 
   if (ret > BUFFER_SIZE) {
     // If there wasn't enough space, try again with the right size buffer.
     buf.resize(ret + 1);
-    ret =
-        SQLGetPrivateProfileString(wdsn.c_str(), wkey.c_str(), wdflt.c_str(), 
buf.data(),
-                                   static_cast<int>(buf.size()), L"ODBC.INI");
+    ret = SQLGetPrivateProfileString(reinterpret_cast<LPCWSTR>(wdsn.c_str()),
+                                     reinterpret_cast<LPCWSTR>(wkey.c_str()),
+                                     reinterpret_cast<LPCWSTR>(wdflt.c_str()), 
buf.data(),
+                                     static_cast<int>(buf.size()), ODBC_INI);
   }
 
-  std::wstring wresult = std::wstring(buf.data(), ret);
-  CONVERT_UTF8_STR(const std::string result, wresult);
+  std::string result("");
+  SetAttributeSQLWCHAR(buf.data(), ret * GetSqlWCharSize(), result);
   return result;
 }
 
@@ -74,31 +82,35 @@ void RemoveAllKnownKeys(std::vector<std::string>& keys) {
 }
 
 std::vector<std::string> ReadAllKeys(const std::string& dsn) {
-  CONVERT_WIDE_STR(const std::wstring wdsn, dsn);
+  CONVERT_SQLWCHAR_STR(wdsn, dsn);
 
-  std::vector<wchar_t> buf(BUFFER_SIZE);
+  std::vector<SQLWCHAR> buf(BUFFER_SIZE);
 
-  int ret = SQLGetPrivateProfileString(wdsn.c_str(), NULL, L"", buf.data(),
-                                       static_cast<int>(buf.size()), 
L"ODBC.INI");
+  int ret = 
SQLGetPrivateProfileString(reinterpret_cast<LPCWSTR>(wdsn.c_str()), NULL,
+                                       reinterpret_cast<LPCWSTR>(L""), 
buf.data(),
+                                       static_cast<int>(buf.size()), ODBC_INI);
 
   if (ret > BUFFER_SIZE) {
     // If there wasn't enough space, try again with the right size buffer.
     buf.resize(ret + 1);
-    ret = SQLGetPrivateProfileString(wdsn.c_str(), NULL, L"", buf.data(),
-                                     static_cast<int>(buf.size()), 
L"ODBC.INI");
+    ret = SQLGetPrivateProfileString(reinterpret_cast<LPCWSTR>(wdsn.c_str()), 
NULL,
+                                     reinterpret_cast<LPCWSTR>(L""), 
buf.data(),
+                                     static_cast<int>(buf.size()), ODBC_INI);
   }
 
   // When you pass NULL to SQLGetPrivateProfileString it gives back a \0 
delimited list of
   // all the keys. The below loop simply tokenizes all the keys and places 
them into a
   // vector.
   std::vector<std::string> keys;
-  wchar_t* begin = buf.data();
+  SQLWCHAR* begin = buf.data();
   while (begin && *begin != '\0') {
-    wchar_t* cur;
+    SQLWCHAR* cur;
     for (cur = begin; *cur != '\0'; ++cur) {
     }
 
-    CONVERT_UTF8_STR(const std::string key, std::wstring(begin, cur));
+    std::string key("");
+    SQLINTEGER key_len = static_cast<SQLINTEGER>(cur - begin);
+    SetAttributeSQLWCHAR(begin, key_len * GetSqlWCharSize(), key);
     keys.emplace_back(key);
     begin = ++cur;
   }
@@ -123,6 +135,10 @@ void Configuration::LoadDefaults() {
 }
 
 void Configuration::LoadDsn(const std::string& dsn) {
+  // Read keys before reading DSN to minimized unexpected behavior from ODBC 
driver
+  // managers.
+  auto customKeys = ReadAllKeys(dsn);
+
   Set(FlightSqlConnection::DSN, dsn);
   Set(FlightSqlConnection::HOST, ReadDsnString(dsn, 
FlightSqlConnection::HOST));
   Set(FlightSqlConnection::PORT, ReadDsnString(dsn, 
FlightSqlConnection::PORT));
@@ -131,6 +147,7 @@ void Configuration::LoadDsn(const std::string& dsn) {
   Set(FlightSqlConnection::PWD, ReadDsnString(dsn, FlightSqlConnection::PWD));
   Set(FlightSqlConnection::TRUSTED_CERTS,
       ReadDsnString(dsn, FlightSqlConnection::TRUSTED_CERTS));
+
 #ifdef __APPLE__
   // macOS iODBC treats non-empty defaults as the real values when reading 
from system
   // DSN, so we don't pass defaults on macOS.
@@ -151,9 +168,8 @@ void Configuration::LoadDsn(const std::string& dsn) {
   Set(FlightSqlConnection::DISABLE_CERTIFICATE_VERIFICATION,
       ReadDsnString(dsn, FlightSqlConnection::DISABLE_CERTIFICATE_VERIFICATION,
                     DEFAULT_DISABLE_CERT_VERIFICATION));
-#endif
+#endif  // __APPLE__
 
-  auto customKeys = ReadAllKeys(dsn);
   RemoveAllKnownKeys(customKeys);
   for (auto key : customKeys) {
     std::string_view key_sv(key);
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/encoding_utils.h 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/encoding_utils.h
index f6178c7f58..a99d2a8257 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/encoding_utils.h
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/encoding_utils.h
@@ -19,8 +19,8 @@
 #include "arrow/flight/sql/odbc/odbc_impl/encoding.h"
 #include "arrow/flight/sql/odbc/odbc_impl/platform.h"
 
-#include <sql.h>
-#include <sqlext.h>
+#include "arrow/flight/sql/odbc/odbc_impl/odbc_includes.h"
+
 #include <algorithm>
 #include <codecvt>
 #include <cstring>
@@ -28,8 +28,18 @@
 #include <memory>
 #include <string>
 
+// Include fwd.h headers after ODBC headers
+#include "arrow/flight/sql/odbc/odbc_impl/util.h"
+
 #define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
 
+#ifdef __linux__
+#  define ODBC_INI reinterpret_cast<LPCWSTR>(u"ODBC.INI")
+#else
+// Windows and macOS
+#  define ODBC_INI reinterpret_cast<LPCWSTR>(L"ODBC.INI")
+#endif
+
 namespace ODBC {
 using arrow::flight::sql::odbc::WcsToUtf8;
 
@@ -136,4 +146,27 @@ inline std::string SqlStringToString(const unsigned char* 
sql_str,
 
   return res;
 }
+
+inline std::vector<SQLWCHAR> ToSqlWCharVector(const std::wstring& ws) {
+  switch (arrow::flight::sql::odbc::GetSqlWCharSize()) {
+    case sizeof(wchar_t): {
+      return std::vector<SQLWCHAR>(ws.begin(), ws.end());
+    }
+#ifdef __linux__
+    case sizeof(char16_t): {
+      // Linux ODBC driver manager uses char16_t as SQLWCHAR
+      CONVERT_UTF8_STR(const std::string utf8s, ws);
+      CONVERT_UTF16_STR(const std::u16string utf16s, utf8s);
+      return std::vector<SQLWCHAR>(utf16s.begin(), utf16s.end());
+    }
+#endif  // __linux__
+    default: {
+      assert(false);
+      throw arrow::flight::sql::odbc::DriverException(
+          "Encoding is unsupported, SQLWCHAR size: " +
+          std::to_string(arrow::flight::sql::odbc::GetSqlWCharSize()));
+    }
+  }
+}
+
 }  // namespace ODBC
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/flight_sql_connection.cc 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/flight_sql_connection.cc
index 8b2b564d8d..f00ce85d9f 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/flight_sql_connection.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/flight_sql_connection.cc
@@ -27,6 +27,7 @@
 #include "arrow/flight/sql/odbc/odbc_impl/util.h"
 #include "arrow/flight/types.h"
 
+#define BOOST_NO_CXX98_FUNCTION_BASE  // ARROW-17805
 #include <boost/algorithm/string.hpp>
 #include <boost/algorithm/string/join.hpp>
 #include <boost/asio/ip/address.hpp>
@@ -297,7 +298,7 @@ FlightClientOptions 
FlightSqlConnection::BuildFlightClientOptions(
     }
   }
 
-  return std::move(options);
+  return options;
 }
 
 Location FlightSqlConnection::BuildLocation(
@@ -409,9 +410,9 @@ Connection::Info FlightSqlConnection::GetInfo(uint16_t 
info_type) {
 
 FlightSqlConnection::FlightSqlConnection(OdbcVersion odbc_version,
                                          const std::string& driver_version)
-    : diagnostics_("Apache Arrow", "Flight SQL", odbc_version),
+    : info_(client_options_, call_options_, sql_client_, driver_version),
+      diagnostics_("Apache Arrow", "Flight SQL", odbc_version),
       odbc_version_(odbc_version),
-      info_(client_options_, call_options_, sql_client_, driver_version),
       closed_(true) {
   attribute_[CONNECTION_DEAD] = static_cast<uint32_t>(SQL_TRUE);
   attribute_[LOGIN_TIMEOUT] = static_cast<uint32_t>(0);
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/flight_sql_result_set.cc 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/flight_sql_result_set.cc
index 56e5bb973f..fb743d1c1e 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/flight_sql_result_set.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/flight_sql_result_set.cc
@@ -81,7 +81,7 @@ size_t FlightSqlResultSet::Move(size_t rows, size_t 
bind_offset, size_t bind_typ
   }
 
   // Reset GetData value offsets.
-  if (num_binding_ != get_data_offsets_.size() && reset_get_data_) {
+  if (static_cast<size_t>(num_binding_) != get_data_offsets_.size() && 
reset_get_data_) {
     std::fill(get_data_offsets_.begin(), get_data_offsets_.end(), 0);
   }
 
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/flight_sql_statement.cc 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/flight_sql_statement.cc
index c35154b44e..46456e8c3c 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/flight_sql_statement.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/flight_sql_statement.cc
@@ -58,9 +58,9 @@ FlightSqlStatement::FlightSqlStatement(const Diagnostics& 
diagnostics,
                                        const MetadataSettings& 
metadata_settings)
     : diagnostics_("Apache Arrow", diagnostics.GetDataSourceComponent(),
                    diagnostics.GetOdbcVersion()),
-      sql_client_(sql_client),
       client_options_(std::move(client_options)),
       call_options_(std::move(call_options)),
+      sql_client_(sql_client),
       metadata_settings_(metadata_settings) {
   attribute_[METADATA_ID] = static_cast<size_t>(SQL_FALSE);
   attribute_[MAX_LENGTH] = static_cast<size_t>(0);
@@ -88,8 +88,8 @@ bool FlightSqlStatement::SetAttribute(StatementAttributeId 
attribute,
             TimeoutDuration{static_cast<double>(boost::get<size_t>(value))};
       } else {
         call_options_.timeout = TimeoutDuration{-1};
-        // Intentional fall-through.
       }
+      [[fallthrough]];
     default:
       attribute_[attribute] = value;
       return true;
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/get_info_cache.cc 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/get_info_cache.cc
index 7d6239f24a..6923d7fafb 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/get_info_cache.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/get_info_cache.cc
@@ -395,8 +395,9 @@ bool GetInfoCache::LoadInfoFromServer() {
               // Unused by ODBC.
               break;
             case SqlInfoOptions::SQL_DDL_SCHEMA: {
-              bool supports_schema_ddl =
-                  
reinterpret_cast<BooleanScalar*>(scalar->child_value().get())->value;
+              // GH-49500 TODO: use scalar bool to determine 
`SQL_CREATE_SCHEMA` and
+              // `SQL_DROP_SCHEMA` values
+
               // Note: this is a bitmask and we can't describe cascade or 
restrict
               // flags.
               info_[SQL_DROP_SCHEMA] = 
static_cast<uint32_t>(SQL_DS_DROP_SCHEMA);
@@ -407,8 +408,9 @@ bool GetInfoCache::LoadInfoFromServer() {
               break;
             }
             case SqlInfoOptions::SQL_DDL_TABLE: {
-              bool supports_table_ddl =
-                  
reinterpret_cast<BooleanScalar*>(scalar->child_value().get())->value;
+              // GH-49500 TODO: use scalar bool to determine 
`SQL_CREATE_TABLE` and
+              // `SQL_DROP_TABLE` values
+
               // This is a bitmask and we cannot describe all clauses.
               info_[SQL_CREATE_TABLE] = 
static_cast<uint32_t>(SQL_CT_CREATE_TABLE);
               info_[SQL_DROP_TABLE] = static_cast<uint32_t>(SQL_DT_DROP_TABLE);
@@ -697,7 +699,6 @@ bool GetInfoCache::LoadInfoFromServer() {
               break;
             }
             case SqlInfoOptions::SQL_DEFAULT_TRANSACTION_ISOLATION: {
-              constexpr int32_t NONE = 0;
               constexpr int32_t READ_UNCOMMITTED = 1;
               constexpr int32_t READ_COMMITTED = 2;
               constexpr int32_t REPEATABLE_READ = 3;
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/main.cc 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/main.cc
index 3336e0160e..4792983006 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/main.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/main.cc
@@ -69,7 +69,7 @@ void TestBindColumn(const std::shared_ptr<Connection>& 
connection) {
     total += fetched_rows;
     std::cout << "Total:" << total << std::endl;
 
-    for (int i = 0; i < fetched_rows; ++i) {
+    for (size_t i = 0; i < fetched_rows; ++i) {
       ARROW_LOG(DEBUG) << "Row[" << i << "] incidnt_num: '" << incidnt_num[i]
                        << "', Category: '" << category[i] << "'";
     }
@@ -138,7 +138,7 @@ void TestBindColumnBigInt(const 
std::shared_ptr<Connection>& connection) {
     total += fetched_rows;
     ARROW_LOG(DEBUG) << "Total:" << total;
 
-    for (int i = 0; i < fetched_rows; ++i) {
+    for (size_t i = 0; i < fetched_rows; ++i) {
       ARROW_LOG(DEBUG) << "Row[" << i << "] incidnt_num: '" << incidnt_num[i] 
<< "', "
                        << "double_field: '" << double_field[i] << "', "
                        << "category: '" << category[i] << "'";
@@ -183,7 +183,7 @@ void TestGetColumnsV3(const std::shared_ptr<Connection>& 
connection) {
   ssize_t result_length;
 
   while (result_set->Move(1, 0, 0, nullptr) == 1) {
-    for (int i = 0; i < column_count; ++i) {
+    for (size_t i = 0; i < column_count; ++i) {
       result_set->GetData(1 + i, arrow::flight::sql::odbc::CDataType_CHAR, 0, 
0,
                           result.data(), buffer_length, &result_length);
       std::cout << (result_length != -1 ? result.data() : "NULL") << '\t';
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_connection.cc 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_connection.cc
index 59cdc2e12d..142dac53ab 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_connection.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_connection.cc
@@ -15,11 +15,12 @@
 // specific language governing permissions and limitations
 // under the License.
 
-#include "arrow/flight/sql/odbc/odbc_impl/odbc_connection.h"
-
 #include "arrow/result.h"
 #include "arrow/util/utf8.h"
 
+// Include ODBC headers after arrow fwd type header to avoid conflicts
+#include "arrow/flight/sql/odbc/odbc_impl/odbc_connection.h"
+
 #include "arrow/flight/sql/odbc/odbc_impl/attribute_utils.h"
 #include "arrow/flight/sql/odbc/odbc_impl/exceptions.h"
 #include "arrow/flight/sql/odbc/odbc_impl/odbc_descriptor.h"
@@ -29,10 +30,12 @@
 #include "arrow/flight/sql/odbc/odbc_impl/spi/statement.h"
 #include "arrow/flight/sql/odbc/odbc_impl/util.h"
 
-// Include ODBC headers after arrow headers to avoid conflicts with 
sql_info_undef.h
+// Include ODBC headers after arrow headers to avoid conflicts
 #include <odbcinst.h>
 #include <sql.h>
 #include <sqlext.h>
+
+#define BOOST_NO_CXX98_FUNCTION_BASE  // ARROW-17805
 #include <boost/algorithm/string.hpp>
 #include <boost/xpressive/xpressive.hpp>
 #include <iterator>
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_descriptor.cc 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_descriptor.cc
index eae90ac2b7..e54fbf601e 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_descriptor.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_descriptor.cc
@@ -17,8 +17,6 @@
 
 #include "arrow/flight/sql/odbc/odbc_impl/odbc_descriptor.h"
 
-#include <sql.h>
-#include <sqlext.h>
 #include <algorithm>
 #include "arrow/flight/sql/odbc/odbc_impl/attribute_utils.h"
 #include "arrow/flight/sql/odbc/odbc_impl/exceptions.h"
@@ -28,6 +26,10 @@
 #include "arrow/flight/sql/odbc/odbc_impl/spi/statement.h"
 #include "arrow/flight/sql/odbc/odbc_impl/type_utilities.h"
 
+// Include ODBC headers after arrow headers to avoid conflicts
+#include <sql.h>
+#include <sqlext.h>
+
 using ODBC::DescriptorRecord;
 using ODBC::ODBCConnection;
 using ODBC::ODBCDescriptor;
@@ -155,7 +157,7 @@ void ODBCDescriptor::SetField(SQLSMALLINT record_number, 
SQLSMALLINT field_ident
     throw DriverException("Bookmarks are unsupported.", "07009");
   }
 
-  if (record_number > records_.size()) {
+  if (static_cast<size_t>(record_number) > records_.size()) {
     throw DriverException("Invalid descriptor index", "HY009");
   }
 
@@ -308,7 +310,7 @@ void ODBCDescriptor::GetField(SQLSMALLINT record_number, 
SQLSMALLINT field_ident
     throw DriverException("Bookmarks are unsupported.", "07009");
   }
 
-  if (record_number > records_.size()) {
+  if (static_cast<size_t>(record_number) > records_.size()) {
     throw DriverException("Invalid descriptor index", "07009");
   }
 
@@ -539,7 +541,7 @@ void ODBCDescriptor::BindCol(SQLSMALLINT record_number, 
SQLSMALLINT c_type,
   assert(is_writable_);
 
   // The set of records auto-expands to the supplied record number.
-  if (records_.size() < record_number) {
+  if (records_.size() < static_cast<size_t>(record_number)) {
     records_.resize(record_number);
   }
 
@@ -559,7 +561,7 @@ void ODBCDescriptor::BindCol(SQLSMALLINT record_number, 
SQLSMALLINT c_type,
 }
 
 void ODBCDescriptor::SetDataPtrOnRecord(SQLPOINTER data_ptr, SQLSMALLINT 
record_number) {
-  assert(record_number <= records_.size());
+  assert(static_cast<size_t>(record_number) <= records_.size());
   DescriptorRecord& record = records_[record_number - 1];
   if (data_ptr) {
     record.CheckConsistency();
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_includes.h 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_includes.h
new file mode 100644
index 0000000000..eba6845cac
--- /dev/null
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_includes.h
@@ -0,0 +1,28 @@
+// 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.
+
+#pragma once
+
+#include <sql.h>
+#include <sqlext.h>
+
+// Workaround for ODBC `BOOL` def conflict on Linux
+#ifdef __linux__
+#  ifdef BOOL
+#    undef BOOL
+#  endif  // BOOL
+#endif    // __linux__
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.cc 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.cc
index a19797e80f..51152c6478 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.cc
@@ -15,17 +15,21 @@
 // specific language governing permissions and limitations
 // under the License.
 
-#include "arrow/flight/sql/odbc/odbc_impl/odbc_statement.h"
+// platform.h platform.h includes windows.h so it needs to be included first
+#include "arrow/flight/sql/odbc/odbc_impl/platform.h"
+
+#include "arrow/type.h"
 
+// Include ODBC headers after arrow fwd type header to avoid conflicts
 #include "arrow/flight/sql/odbc/odbc_impl/attribute_utils.h"
 #include "arrow/flight/sql/odbc/odbc_impl/exceptions.h"
 #include "arrow/flight/sql/odbc/odbc_impl/odbc_connection.h"
 #include "arrow/flight/sql/odbc/odbc_impl/odbc_descriptor.h"
+#include "arrow/flight/sql/odbc/odbc_impl/odbc_statement.h"
 #include "arrow/flight/sql/odbc/odbc_impl/spi/result_set.h"
 #include "arrow/flight/sql/odbc/odbc_impl/spi/result_set_metadata.h"
 #include "arrow/flight/sql/odbc/odbc_impl/spi/statement.h"
 #include "arrow/flight/sql/odbc/odbc_impl/types.h"
-#include "arrow/type.h"
 
 #include <sql.h>
 #include <sqlext.h>
@@ -737,7 +741,7 @@ SQLRETURN ODBCStatement::GetData(SQLSMALLINT record_number, 
SQLSMALLINT c_type,
                                  SQLLEN* indicator_ptr) {
   if (record_number == 0) {
     throw DriverException("Bookmarks are not supported", "07009");
-  } else if (record_number > ird_->GetRecords().size()) {
+  } else if (static_cast<size_t>(record_number) > ird_->GetRecords().size()) {
     throw DriverException("Invalid column index: " + 
std::to_string(record_number),
                           "07009");
   }
@@ -752,7 +756,7 @@ SQLRETURN ODBCStatement::GetData(SQLSMALLINT record_number, 
SQLSMALLINT c_type,
   int scale = ird_record.scale;
 
   if (c_type == SQL_ARD_TYPE) {
-    if (record_number > current_ard_->GetRecords().size()) {
+    if (static_cast<size_t>(record_number) > 
current_ard_->GetRecords().size()) {
       throw DriverException("Invalid column index: " + 
std::to_string(record_number),
                             "07009");
     }
@@ -765,7 +769,7 @@ SQLRETURN ODBCStatement::GetData(SQLSMALLINT record_number, 
SQLSMALLINT c_type,
   // Note: this is intentionally not an else if, since the type can be 
SQL_C_DEFAULT in
   // the ARD.
   if (evaluated_c_type == SQL_C_DEFAULT) {
-    if (record_number <= current_ard_->GetRecords().size()) {
+    if (static_cast<size_t>(record_number) <= 
current_ard_->GetRecords().size()) {
       const DescriptorRecord& ard_record = 
current_ard_->GetRecords()[record_number - 1];
       precision = ard_record.precision;
       scale = ard_record.scale;
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.h 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.h
index 7f974b0a85..b5ee264f55 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.h
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_statement.h
@@ -84,7 +84,7 @@ class ODBCStatement : public ODBCHandle<ODBCStatement> {
   /// \brief Return number of columns from data set
   void GetColumnCount(SQLSMALLINT* column_count_ptr);
 
-  /// \brief Return number of rows affected by an UPDATE, INSERT, or DELETE 
statement\
+  /// \brief Return number of rows affected by an UPDATE, INSERT, or DELETE 
statement
   ///
   ///  -1 is returned as driver only supports SELECT statement
   void GetRowCount(SQLLEN* row_count_ptr);
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/spi/connection.h 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/spi/connection.h
index 5418489169..42dc4d8edb 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/spi/connection.h
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/spi/connection.h
@@ -17,6 +17,7 @@
 
 #pragma once
 
+#define BOOST_NO_CXX98_FUNCTION_BASE  // ARROW-17805
 #include <boost/algorithm/string.hpp>
 #include <boost/variant.hpp>
 #include <functional>
@@ -36,7 +37,11 @@ struct CaseInsensitiveComparator {
   using is_transparent = std::true_type;
 
   bool operator()(std::string_view s1, std::string_view s2) const {
-    return boost::lexicographical_compare(s1, s2, boost::is_iless());
+    return std::lexicographical_compare(
+        s1.begin(), s1.end(), s2.begin(), s2.end(), [](char a, char b) {
+          return std::tolower(static_cast<unsigned char>(a)) <
+                 std::tolower(static_cast<unsigned char>(b));
+        });
   }
 };
 
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/spi/result_set.h 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/spi/result_set.h
index b4a3994ca3..4c3ed19217 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/spi/result_set.h
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/spi/result_set.h
@@ -25,7 +25,7 @@
 #include "arrow/flight/sql/odbc/odbc_impl/type_fwd.h"
 #include "arrow/flight/sql/odbc/odbc_impl/types.h"
 
-#include <sqltypes.h>
+#include "arrow/flight/sql/odbc/odbc_impl/odbc_includes.h"
 
 namespace arrow::flight::sql::odbc {
 
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/spi/statement.h 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/spi/statement.h
index 240c51b672..bdfeea3b79 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/spi/statement.h
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/spi/statement.h
@@ -22,6 +22,7 @@
 #include <optional>
 #include <vector>
 
+#include "arrow/flight/sql/odbc/odbc_impl/diagnostics.h"
 #include "arrow/flight/sql/odbc/odbc_impl/type_fwd.h"
 
 namespace arrow::flight::sql::odbc {
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/system_dsn.cc 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/system_dsn.cc
index 7b974cc35a..b4ce11557f 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/system_dsn.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/system_dsn.cc
@@ -23,10 +23,28 @@
 #include <boost/algorithm/string/predicate.hpp>
 #include <sstream>
 
+#include "arrow/flight/sql/odbc/odbc_impl/encoding_utils.h"
+
 namespace arrow::flight::sql::odbc {
 
 using config::Configuration;
 
+#ifdef __linux__
+// Linux driver manager uses utf16string
+std::u16string ConvertToSQLWCHAR(const std::string_view var) {
+  auto result = arrow::util::UTF8StringToUTF16(var);
+#else
+std::wstring ConvertToSQLWCHAR(const std::string_view var) {
+  // Windows and macOS
+  auto result = arrow::util::UTF8ToWideString(var);
+#endif
+  if (!result.status().ok()) {
+    PostArrowUtilError(result.status());
+    return {};  // return empty string on error
+  }
+  return result.ValueOrDie();
+}
+
 void PostError(DWORD error_code, LPWSTR error_msg) {
 #if defined _WIN32
   MessageBox(NULL, error_msg, L"Error!", MB_ICONEXCLAMATION | MB_OK);
@@ -36,23 +54,32 @@ void PostError(DWORD error_code, LPWSTR error_msg) {
 
 void PostArrowUtilError(arrow::Status error_status) {
   std::string error_msg = error_status.message();
-  std::wstring werror_msg = arrow::util::UTF8ToWideString(error_msg).ValueOr(
-      L"Error during utf8 to wide string conversion");
+  CONVERT_SQLWCHAR_STR(werror_msg, error_msg);
 
-  PostError(ODBC_ERROR_GENERAL_ERR, const_cast<LPWSTR>(werror_msg.c_str()));
+  PostError(ODBC_ERROR_GENERAL_ERR,
+            const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(werror_msg.c_str())));
 }
 
 void PostLastInstallerError() {
 #define BUFFER_SIZE (1024)
   DWORD code;
-  wchar_t msg[BUFFER_SIZE];
-  SQLInstallerError(1, &code, msg, BUFFER_SIZE, NULL);
-
-  std::wstringstream buf;
-  buf << L"Message: \"" << msg << L"\", Code: " << code;
-  std::wstring error_msg = buf.str();
-
-  PostError(code, const_cast<LPWSTR>(error_msg.c_str()));
+  std::vector<SQLWCHAR> msg(BUFFER_SIZE);
+  SQLInstallerError(1, &code, msg.data(), BUFFER_SIZE, NULL);
+
+#ifdef __linux__
+  std::string code_str = std::to_string(code);
+  std::u16string code_u16 = arrow::util::UTF8StringToUTF16(code_str).ValueOr(
+      u"unknown code. Error during utf8 to utf16 conversion");
+  std::u16string error_msg = u"Message: \"" +
+                             
std::u16string(reinterpret_cast<char16_t*>(msg.data())) +
+                             u"\", Code: " + code_u16;
+#else
+  // Windows/macOS
+  std::wstring error_msg =
+      L"Message: \"" + std::wstring(msg.data()) + L"\", Code: " + 
std::to_wstring(code);
+#endif  // __linux__
+
+  PostError(code, 
const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(error_msg.c_str())));
 }
 
 /**
@@ -62,7 +89,14 @@ void PostLastInstallerError() {
  * @return True on success and false on fail.
  */
 bool UnregisterDsn(const std::wstring& dsn) {
-  if (SQLRemoveDSNFromIni(dsn.c_str())) {
+#ifdef __linux__
+  auto dsn_vec = ODBC::ToSqlWCharVector(dsn);
+  const SQLWCHAR* dsn_arr = dsn_vec.data();
+#else
+  // Windows and macOS
+  const SQLWCHAR* dsn_arr = dsn.c_str();
+#endif
+  if (SQLRemoveDSNFromIni(dsn_arr)) {
     return true;
   }
 
@@ -79,14 +113,9 @@ bool UnregisterDsn(const std::wstring& dsn) {
  */
 bool RegisterDsn(const Configuration& config, LPCWSTR driver) {
   const std::string& dsn = config.Get(FlightSqlConnection::DSN);
-  auto wdsn_result = arrow::util::UTF8ToWideString(dsn);
-  if (!wdsn_result.status().ok()) {
-    PostArrowUtilError(wdsn_result.status());
-    return false;
-  }
-  std::wstring wdsn = wdsn_result.ValueOrDie();
+  auto wdsn = ConvertToSQLWCHAR(dsn);
 
-  if (!SQLWriteDSNToIni(wdsn.c_str(), driver)) {
+  if (!SQLWriteDSNToIni(reinterpret_cast<LPCWSTR>(wdsn.c_str()), driver)) {
     PostLastInstallerError();
     return false;
   }
@@ -99,22 +128,13 @@ bool RegisterDsn(const Configuration& config, LPCWSTR 
driver) {
       continue;
     }
 
-    auto wkey_result = arrow::util::UTF8ToWideString(key);
-    if (!wkey_result.status().ok()) {
-      PostArrowUtilError(wkey_result.status());
-      return false;
-    }
-    std::wstring wkey = wkey_result.ValueOrDie();
-
-    auto wvalue_result = arrow::util::UTF8ToWideString(it->second);
-    if (!wvalue_result.status().ok()) {
-      PostArrowUtilError(wvalue_result.status());
-      return false;
-    }
-    std::wstring wvalue = wvalue_result.ValueOrDie();
+    auto wkey = ConvertToSQLWCHAR(key);
+    auto wvalue = ConvertToSQLWCHAR(it->second);
 
-    if (!SQLWritePrivateProfileString(wdsn.c_str(), wkey.c_str(), 
wvalue.c_str(),
-                                      L"ODBC.INI")) {
+    if (!SQLWritePrivateProfileString(reinterpret_cast<LPCWSTR>(wdsn.c_str()),
+                                      reinterpret_cast<LPCWSTR>(wkey.c_str()),
+                                      
reinterpret_cast<LPCWSTR>(wvalue.c_str()),
+                                      ODBC_INI)) {
       PostLastInstallerError();
       return false;
     }
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/util.cc 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/util.cc
index fa0a35274a..fe6a2d4d7c 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/util.cc
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/util.cc
@@ -136,6 +136,7 @@ SqlDataType GetDataTypeFromArrowFieldV3(const 
std::shared_ptr<Field>& field,
     case Type::LARGE_LIST:
     case Type::MAX_ID:
     case Type::NA:
+    default:
       break;
   }
 
@@ -803,6 +804,8 @@ std::shared_ptr<DataType> 
GetDefaultDataTypeForTypeId(Type::type type_id) {
       return arrow::time64(TimeUnit::MICRO);
     case Type::TIMESTAMP:
       return arrow::timestamp(TimeUnit::SECOND);
+    default:
+      break;
   }
 
   throw DriverException(std::string("Invalid type id: ") + 
std::to_string(type_id));
diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/util.h 
b/cpp/src/arrow/flight/sql/odbc/odbc_impl/util.h
index f513c8d340..63ce6d6549 100644
--- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/util.h
+++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/util.h
@@ -29,6 +29,15 @@
 #include <functional>
 #include <optional>
 
+#ifdef __linux__
+// Linux driver manager uses utf16string
+#  define CONVERT_SQLWCHAR_STR(wvar, var) \
+    CONVERT_UTF16_STR(const std::u16string wvar, var)
+#else
+// Windows and macOS uses wstring
+#  define CONVERT_SQLWCHAR_STR(wvar, var) CONVERT_WIDE_STR(const std::wstring 
wvar, var)
+#endif  // __linux__
+
 #define CONVERT_WIDE_STR(wstring_var, utf8_target)                             
   \
   wstring_var = [&] {                                                          
   \
     arrow::Result<std::wstring> res = 
arrow::util::UTF8ToWideString(utf8_target); \
@@ -43,6 +52,13 @@
     return res.ValueOrDie();                                                   
      \
   }()
 
+#define CONVERT_UTF16_STR(utf16string_var, utf8_target)                        
      \
+  utf16string_var = [&] {                                                      
      \
+    arrow::Result<std::u16string> res = 
arrow::util::UTF8StringToUTF16(utf8_target); \
+    arrow::flight::sql::odbc::util::ThrowIfNotOK(res.status());                
      \
+    return res.ValueOrDie();                                                   
      \
+  }()
+
 namespace arrow::flight::sql::odbc {
 namespace util {
 
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 38f8d342a0..c0d5f4919a 100644
--- a/cpp/src/arrow/flight/sql/odbc/tests/errors_test.cc
+++ b/cpp/src/arrow/flight/sql/odbc/tests/errors_test.cc
@@ -492,7 +492,6 @@ TYPED_TEST(ErrorsOdbcV2Test, 
TestSQLErrorEnvErrorFromDriverManager) {
   EXPECT_FALSE(std::wstring(message).empty());
 }
 
-// TODO: verify that `SQLGetConnectOption` is not required by Excel.
 #ifndef __APPLE__
 TYPED_TEST(ErrorsOdbcV2Test, TestSQLErrorConnError) {
   // Test ODBC 2.0 API SQLError with ODBC ver 2.
diff --git a/cpp/src/arrow/symbols.map b/cpp/src/arrow/symbols.map
index d3c38c22c9..397bd80060 100644
--- a/cpp/src/arrow/symbols.map
+++ b/cpp/src/arrow/symbols.map
@@ -35,6 +35,8 @@
     Arrow*;
     # ARROW-14771: export Protobuf symbol table
     descriptor_table_Flight*_2eproto;
+    # Export Arrow Flight SQL ODBC symbols
+    SQL*;
 
   # Symbols marked as 'local' are not exported by the DSO and thus may not
   # be used by client applications.  Everything except the above falls here.

Reply via email to