Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cpp/src/arrow/flight/sql/odbc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ add_arrow_lib(arrow_flight_sql_odbc
DEPENDENCIES
arrow_flight_sql
DEFINITIONS
FMT_HEADER_ONLY
UNICODE
SHARED_LINK_FLAGS
${ARROW_VERSION_SCRIPT_FLAGS} # Defined in cpp/arrow/CMakeLists.txt
SHARED_LINK_LIBS
Expand Down
4 changes: 2 additions & 2 deletions cpp/src/arrow/flight/sql/odbc/odbc.def
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@

LIBRARY arrow_flight_sql_odbc
EXPORTS
; TODO: update to ConfigDSNW when driver has unicode implemented
; ConfigDSN
; GH-46574 TODO enable DSN window
; ConfigDSNW
SQLAllocConnect
SQLAllocEnv
SQLAllocHandle
Expand Down
17 changes: 8 additions & 9 deletions cpp/src/arrow/flight/sql/odbc/odbc_impl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,16 @@ add_library(arrow_odbc_spi_impl
blocking_queue.h
calendar_utils.cc
calendar_utils.h
config/configuration.cc
config/configuration.h
config/connection_string_parser.cc
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
Expand Down Expand Up @@ -106,26 +110,21 @@ add_library(arrow_odbc_spi_impl
type_utilities.h
util.cc
util.h)
target_compile_definitions(arrow_odbc_spi_impl PUBLIC UNICODE)
target_include_directories(arrow_odbc_spi_impl PUBLIC include include/odbc_impl)
target_include_directories(arrow_odbc_spi_impl PUBLIC ${CMAKE_CURRENT_LIST_DIR})

if(WIN32)
target_sources(arrow_odbc_spi_impl
PRIVATE config/configuration.cc
config/configuration.h
config/connection_string_parser.cc
config/connection_string_parser.h
ui/add_property_window.cc
PRIVATE ui/add_property_window.cc
ui/add_property_window.h
ui/custom_window.cc
ui/custom_window.h
ui/dsn_configuration_window.cc
ui/dsn_configuration_window.h
ui/window.cc
ui/window.h
win_system_dsn.cc
system_dsn.cc
system_dsn.h)
system_dsn.cc)
endif()

target_link_libraries(arrow_odbc_spi_impl PUBLIC arrow_flight_sql_shared
Expand Down
11 changes: 7 additions & 4 deletions cpp/src/arrow/flight/sql/odbc/odbc_impl/attribute_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,18 +83,21 @@ template <typename O>
inline SQLRETURN GetAttributeSQLWCHAR(const std::string& attribute_value,
bool is_length_in_bytes, SQLPOINTER output,
O output_size, O* output_len_ptr) {
size_t result = ConvertToSqlWChar(
size_t length = ConvertToSqlWChar(
attribute_value, reinterpret_cast<SQLWCHAR*>(output),
is_length_in_bytes ? output_size : output_size * GetSqlWCharSize());

if (!is_length_in_bytes) {
length = length / GetSqlWCharSize();
}

if (output_len_ptr) {
*output_len_ptr =
static_cast<O>(is_length_in_bytes ? result : result / GetSqlWCharSize());
*output_len_ptr = static_cast<O>(length);
}

if (output &&
output_size <
static_cast<O>(result + (is_length_in_bytes ? GetSqlWCharSize() : 1))) {
static_cast<O>(length + (is_length_in_bytes ? GetSqlWCharSize() : 1))) {
return SQL_SUCCESS_WITH_INFO;
}
return SQL_SUCCESS;
Expand Down
49 changes: 32 additions & 17 deletions cpp/src/arrow/flight/sql/odbc/odbc_impl/config/configuration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@

#include "arrow/flight/sql/odbc/odbc_impl/config/configuration.h"
#include "arrow/flight/sql/odbc/odbc_impl/flight_sql_connection.h"
#include "arrow/flight/sql/odbc/odbc_impl/util.h"
#include "arrow/result.h"
#include "arrow/util/utf8.h"

#include <odbcinst.h>
#include <boost/range/adaptor/map.hpp>
Expand All @@ -26,7 +29,6 @@

namespace arrow::flight::sql::odbc {
namespace config {

static const char DEFAULT_DSN[] = "Apache Arrow Flight SQL";
static const char DEFAULT_ENABLE_ENCRYPTION[] = TRUE_STR;
static const char DEFAULT_USE_CERT_STORE[] = TRUE_STR;
Expand All @@ -35,23 +37,27 @@ static const char DEFAULT_DISABLE_CERT_VERIFICATION[] = FALSE_STR;
namespace {
std::string ReadDsnString(const std::string& dsn, const std::string_view& key,
const std::string& dflt = "") {
#define BUFFER_SIZE (1024)
std::vector<char> buf(BUFFER_SIZE);
CONVERT_WIDE_STR(const std::wstring wdsn, dsn);
CONVERT_WIDE_STR(const std::wstring wkey, key);
CONVERT_WIDE_STR(const std::wstring wdflt, dflt);

std::string key_str = std::string(key);
#define BUFFER_SIZE (1024)
std::vector<wchar_t> buf(BUFFER_SIZE);
int ret =
SQLGetPrivateProfileString(dsn.c_str(), key_str.c_str(), dflt.c_str(), buf.data(),
static_cast<int>(buf.size()), "ODBC.INI");
SQLGetPrivateProfileString(wdsn.c_str(), wkey.c_str(), wdflt.c_str(), buf.data(),
static_cast<int>(buf.size()), L"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(dsn.c_str(), key_str.c_str(), dflt.c_str(), buf.data(),
static_cast<int>(buf.size()), "ODBC.INI");
SQLGetPrivateProfileString(wdsn.c_str(), wkey.c_str(), wdflt.c_str(), buf.data(),
static_cast<int>(buf.size()), L"ODBC.INI");
}

return std::string(buf.data(), ret);
std::wstring wresult = std::wstring(buf.data(), ret);
CONVERT_UTF8_STR(const std::string result, wresult);
return result;
}

void RemoveAllKnownKeys(std::vector<std::string>& keys) {
Expand All @@ -68,28 +74,32 @@ void RemoveAllKnownKeys(std::vector<std::string>& keys) {
}

std::vector<std::string> ReadAllKeys(const std::string& dsn) {
std::vector<char> buf(BUFFER_SIZE);
CONVERT_WIDE_STR(const std::wstring wdsn, dsn);

std::vector<wchar_t> buf(BUFFER_SIZE);

int ret = SQLGetPrivateProfileString(dsn.c_str(), NULL, "", buf.data(),
static_cast<int>(buf.size()), "ODBC.INI");
int ret = SQLGetPrivateProfileString(wdsn.c_str(), NULL, L"", buf.data(),
static_cast<int>(buf.size()), L"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(dsn.c_str(), NULL, "", buf.data(),
static_cast<int>(buf.size()), "ODBC.INI");
ret = SQLGetPrivateProfileString(wdsn.c_str(), NULL, L"", buf.data(),
static_cast<int>(buf.size()), L"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;
char* begin = buf.data();
wchar_t* begin = buf.data();
while (begin && *begin != '\0') {
char* cur;
wchar_t* cur;
for (cur = begin; *cur != '\0'; ++cur) {
}
keys.emplace_back(begin, cur);

CONVERT_UTF8_STR(const std::string key, std::wstring(begin, cur));
keys.emplace_back(key);
begin = ++cur;
}
return keys;
Expand Down Expand Up @@ -153,6 +163,11 @@ const std::string& Configuration::Get(const std::string_view& key) const {
return itr->second;
}

void Configuration::Set(const std::string_view& key, const std::wstring& wvalue) {
CONVERT_UTF8_STR(const std::string value, wvalue);
Set(key, value);
}

void Configuration::Set(const std::string_view& key, const std::string& value) {
const std::string copy = boost::trim_copy(value);
if (!copy.empty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class Configuration {
void Clear();
bool IsSet(const std::string_view& key) const;
const std::string& Get(const std::string_view& key) const;
void Set(const std::string_view& key, const std::wstring& wvalue);
void Set(const std::string_view& key, const std::string& value);

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ namespace {

#if _WIN32 || _WIN64
constexpr auto SYSTEM_TRUST_STORE_DEFAULT = true;
constexpr auto STORES = {"CA", "MY", "ROOT", "SPC"};
constexpr auto STORES = {L"CA", L"MY", L"ROOT", L"SPC"};

inline std::string GetCerts() {
std::string certs;
Expand Down
42 changes: 24 additions & 18 deletions cpp/src/arrow/flight/sql/odbc/odbc_impl/odbc_connection.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@

#include "arrow/flight/sql/odbc/odbc_impl/odbc_connection.h"

#include "arrow/result.h"
#include "arrow/util/utf8.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"
#include "arrow/flight/sql/odbc/odbc_impl/odbc_environment.h"
#include "arrow/flight/sql/odbc/odbc_impl/odbc_statement.h"
#include "arrow/flight/sql/odbc/odbc_impl/spi/connection.h"
#include "arrow/flight/sql/odbc/odbc_impl/spi/statement.h"
#include "arrow/flight/sql/odbc/odbc_impl/util.h"

#include <odbcinst.h>
#include <sql.h>
Expand Down Expand Up @@ -56,41 +60,43 @@ const boost::xpressive::sregex CONNECTION_STR_REGEX(
void loadPropertiesFromDSN(const std::string& dsn,
Connection::ConnPropertyMap& properties) {
const size_t BUFFER_SIZE = 1024 * 10;
std::vector<char> outputBuffer;
outputBuffer.resize(BUFFER_SIZE, '\0');
std::vector<wchar_t> output_buffer;
output_buffer.resize(BUFFER_SIZE, '\0');
SQLSetConfigMode(ODBC_BOTH_DSN);

SQLGetPrivateProfileString(dsn.c_str(), NULL, "", &outputBuffer[0], BUFFER_SIZE,
"odbc.ini");
CONVERT_WIDE_STR(const std::wstring wdsn, dsn);

SQLGetPrivateProfileString(wdsn.c_str(), NULL, L"", &output_buffer[0], BUFFER_SIZE,
L"odbc.ini");

// The output buffer holds the list of keys in a series of NUL-terminated strings.
// The series is terminated with an empty string (eg a NUL-terminator terminating the
// last key followed by a NUL terminator after).
std::vector<std::string_view> keys;
std::vector<std::wstring_view> keys;
size_t pos = 0;
while (pos < BUFFER_SIZE) {
std::string key(&outputBuffer[pos]);
if (key.empty()) {
std::wstring wkey(&output_buffer[pos]);
if (wkey.empty()) {
break;
}
size_t len = key.size();
size_t len = wkey.size();

// Skip over Driver or DSN keys.
if (!boost::iequals(key, "DSN") && !boost::iequals(key, "Driver")) {
keys.emplace_back(std::move(key));
if (!boost::iequals(wkey, L"DSN") && !boost::iequals(wkey, L"Driver")) {
keys.emplace_back(std::move(wkey));
}
pos += len + 1;
}

for (auto& key : keys) {
outputBuffer.clear();
outputBuffer.resize(BUFFER_SIZE, '\0');

std::string key_str = std::string(key);
SQLGetPrivateProfileString(dsn.c_str(), key_str.c_str(), "", &outputBuffer[0],
BUFFER_SIZE, "odbc.ini");
for (auto& wkey : keys) {
output_buffer.clear();
output_buffer.resize(BUFFER_SIZE, '\0');
SQLGetPrivateProfileString(wdsn.c_str(), wkey.data(), L"", &output_buffer[0],
BUFFER_SIZE, L"odbc.ini");

std::string value = std::string(&outputBuffer[0]);
std::wstring wvalue = std::wstring(&output_buffer[0]);
CONVERT_UTF8_STR(const std::string value, wvalue);
CONVERT_UTF8_STR(const std::string key, std::wstring(wkey));
auto propIter = properties.find(key);
if (propIter == properties.end()) {
properties.emplace(std::make_pair(std::move(key), std::move(value)));
Expand Down
Loading
Loading