Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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: 2 additions & 0 deletions cpp/src/arrow/flight/sql/odbc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ else()
set(ODBCINST odbcinst)
endif()

add_definitions(-DUNICODE=1)

add_subdirectory(odbc_impl)

arrow_install_all_headers("arrow/flight/sql/odbc")
Expand Down
3 changes: 1 addition & 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,7 @@

LIBRARY arrow_flight_sql_odbc
EXPORTS
; TODO: update to ConfigDSNW when driver has unicode implemented
; ConfigDSN
ConfigDSNW
SQLAllocConnect
SQLAllocEnv
SQLAllocHandle
Expand Down
16 changes: 7 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 @@ -111,21 +115,15 @@ 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
48 changes: 31 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,8 @@

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

#include <odbcinst.h>
#include <boost/range/adaptor/map.hpp>
Expand All @@ -26,7 +28,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 +36,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);
std::wstring wdsn = arrow::util::UTF8ToWideString(dsn).ValueOr(L"");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we ignoring errors here?

I suppose the return type doesn't allow for errors. But I'd expect that we update the return type to Result<std::string>.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You raise a good point, these errors should not be ignored. I have added throws for the Result errors.
The caller will catch the exceptions thrown

std::wstring wkey = arrow::util::UTF8ToWideString(key).ValueOr(L"");
std::wstring wdflt = arrow::util::UTF8ToWideString(dflt).ValueOr(L"");

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);
std::string result = arrow::util::WideStringToUTF8(wresult).ValueOr("");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above comment, I have added throws

return result;
}

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

std::vector<std::string> ReadAllKeys(const std::string& dsn) {
std::vector<char> buf(BUFFER_SIZE);
std::wstring wDsn = arrow::util::UTF8ToWideString(dsn).ValueOr(L"");

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);

std::string key = arrow::util::WideStringToUTF8(std::wstring(begin, cur)).ValueOr("");
keys.emplace_back(key);
begin = ++cur;
}
return keys;
Expand Down Expand Up @@ -153,6 +162,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) {
std::string value = arrow::util::WideStringToUTF8(wValue).ValueOr("");
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
41 changes: 23 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,6 +17,9 @@

#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"
Expand Down Expand Up @@ -56,41 +59,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");
std::wstring wdsn = arrow::util::UTF8ToWideString(dsn).ValueOr(L"");

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]);
std::string value = arrow::util::WideStringToUTF8(wvalue).ValueOr("");
std::string key = arrow::util::WideStringToUTF8(std::wstring(wkey)).ValueOr("");
auto propIter = properties.find(key);
if (propIter == properties.end()) {
properties.emplace(std::make_pair(std::move(key), std::move(value)));
Expand Down
Loading
Loading