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 5a16c0361f3..f0c9d451271 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbc_impl/CMakeLists.txt +++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/CMakeLists.txt @@ -186,6 +186,7 @@ add_arrow_test(odbc_spi_impl_test json_converter_test.cc record_batch_transformer_test.cc util_test.cc + odbc_descriptor_test.cc EXTRA_LINK_LIBS arrow_odbc_spi_impl ${ODBC_SPI_IMPL_TEST_LINK_LIBS}) 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 142dac53ab6..e452008b27b 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 @@ -465,9 +465,9 @@ void ODBCConnection::SetConnectAttr(SQLINTEGER attribute, SQLPOINTER value, case SQL_ATTR_CURRENT_CATALOG: { std::string catalog; if (is_unicode) { - SetAttributeUTF8(value, string_length, catalog); - } else { SetAttributeSQLWCHAR(value, string_length, catalog); + } else { + SetAttributeUTF8(value, string_length, catalog); } if (!spi_connection_->SetAttribute(Connection::CURRENT_CATALOG, catalog)) { throw DriverException("Option value changed.", "01S02"); 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 11e4512eb8d..d58d8b93ba0 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 @@ -215,7 +215,7 @@ void ODBCDescriptor::SetField(SQLSMALLINT record_number, SQLSMALLINT field_ident has_bindings_changed_ = true; break; case SQL_DESC_NAME: - SetAttributeUTF8(value, buffer_length, record.name); + SetAttributeSQLWCHAR(value, buffer_length, record.name); has_bindings_changed_ = true; break; case SQL_DESC_OCTET_LENGTH: diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_descriptor_test.cc b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_descriptor_test.cc new file mode 100644 index 00000000000..20bed78bdb5 --- /dev/null +++ b/cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_descriptor_test.cc @@ -0,0 +1,54 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include "arrow/flight/sql/odbc/odbc_impl/odbc_descriptor.h" + +#include "arrow/flight/sql/odbc/odbc_impl/diagnostics.h" +#include "arrow/flight/sql/odbc/odbc_impl/encoding.h" +#include "arrow/flight/sql/odbc/odbc_impl/encoding_utils.h" + +#include +#include + +#include "gtest/gtest.h" + +namespace arrow::flight::sql::odbc { + +using ODBC::ODBCDescriptor; + +TEST(ODBCDescriptorTest, SetGetNameWideRoundTrips) { + arrow::flight::sql::odbc::Diagnostics diagnostics("vendor", "component", + OdbcVersion::V_3); + ODBCDescriptor desc(diagnostics, nullptr, nullptr, /*is_app_descriptor=*/true, + /*is_writable=*/true, /*is_2x_connection=*/false); + + SQLSMALLINT count = 1; + desc.SetHeaderField(SQL_DESC_COUNT, reinterpret_cast(&count), 0); + + std::vector wide; + Utf8ToWcs("my_column", &wide); + desc.SetField(1, SQL_DESC_NAME, wide.data(), static_cast(wide.size())); + + SQLWCHAR out[256]; + SQLINTEGER out_len = 0; + desc.GetField(1, SQL_DESC_NAME, out, sizeof(out), &out_len); + std::string name = + ODBC::SqlWcharToString(out, static_cast(out_len / GetSqlWCharSize())); + EXPECT_EQ("my_column", name); +} + +} // namespace arrow::flight::sql::odbc 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 65aadf1d8f9..63b55def911 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 @@ -410,4 +410,23 @@ TYPED_TEST(ConnectionAttributeTest, TestSQLSetConnectAttrPacketSizeValid) { #endif } +// A multi-character catalog set through the wide entry point must round-trip +// intact. +TYPED_TEST(ConnectionAttributeTest, TestSQLSetGetConnectAttrCurrentCatalogWide) { + ASSIGN_SQLWCHAR_ARR_AND_LEN(catalog, L"my_catalog"); + + ASSERT_EQ(SQL_SUCCESS, SQLSetConnectAttr(this->conn, SQL_ATTR_CURRENT_CATALOG, catalog, + catalog_len * GetSqlWCharSize())); + + SQLWCHAR out_str[kOdbcBufferSize]; + SQLINTEGER out_str_len; + ASSERT_EQ(SQL_SUCCESS, SQLGetConnectAttr(this->conn, SQL_ATTR_CURRENT_CATALOG, out_str, + kOdbcBufferSize, &out_str_len)); + // SQLGetConnectAttr returns the length in bytes; convert to characters. + out_str_len /= GetSqlWCharSize(); + std::string out_catalog = + ODBC::SqlWcharToString(out_str, static_cast(out_str_len)); + EXPECT_EQ("my_catalog", out_catalog); +} + } // namespace arrow::flight::sql::odbc