Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
404d074
Implement SQLGetInfo
rscales Jun 19, 2025
17417e1
Add build related directories to git ignore
rscales Jun 20, 2025
ddef36c
Update based on comments from draft review
rscales Jun 20, 2025
9b86d58
Add tests for DBMS Product Information
rscales Jun 20, 2025
14948b5
Move validation check to within ExecuteWithDiagnostics lambda
rscales Jun 20, 2025
50c6ae0
Move helper functions to top of file
rscales Jun 21, 2025
01aca6f
Add tests for Data Source Information
rscales Jun 21, 2025
6ee5e08
Add tests for Supported SQL
rscales Jun 23, 2025
0856fc4
Add tests for SQL Limits
rscales Jun 23, 2025
f532c12
Add tests for Scalar Function Information
rscales Jun 23, 2025
562057b
Add tests for Conversion Information
rscales Jun 23, 2025
f9e6f54
Move connection info test to be above connection test file
rscales Jun 23, 2025
27f5b5f
Change to use compact namespace inclusion
rscales Jun 23, 2025
e8747d3
Clean up comments
rscales Jun 23, 2025
7c5c27d
Allow validation of wchar string when truncation occurs
rscales Jun 23, 2025
74a406b
Fix comment for failing test case
rscales Jun 23, 2025
9026501
Add default values in get info cache for missing types
rscales Jun 25, 2025
824ec82
Reformat connection info test cases
rscales Jun 25, 2025
5311299
Update default value for SQL_TIMEDATE_FUNCTIONS to show support for a…
rscales Jun 25, 2025
dc767b2
Merge branch 'apache-odbc' into sql-get-info
rscales Jun 25, 2025
214f1a4
Empty commit to force running workflows
rscales Jun 25, 2025
44d9f86
Merge branch 'sql-get-info' of https://github.com/Bit-Quill/arrow int…
rscales Jun 25, 2025
4ad8ef8
Merge branch 'apache-odbc' into sql-get-info
rscales Jun 26, 2025
ce4b702
Use bitmasks to create value for SQL_AGGREGATE_FUNCTIONS
rscales Jun 26, 2025
e7f7493
Use bitmasks to create value for SQL_TIMEDATE_ADD_INTERVALS
rscales Jun 26, 2025
ca3137b
Use bitmask to create default value for SQL_TIMEDATE_DIFF_INTERVALS
rscales Jun 26, 2025
443f4dc
Use bitmask to create default value for SQL_GETDATA_EXTENSIONS
rscales Jun 26, 2025
070ec90
Use default value for SQL_CURSOR_SENSITIVITY
rscales Jun 26, 2025
bca7792
Add error code to default return for odbc connection get info
rscales Jun 26, 2025
8dbb6bd
Update connection info test to use arrow::flight::sql::odbc namespace
rscales Jun 26, 2025
5b3348e
Update to use defined values from sqlext.h
rscales Jun 27, 2025
e385eed
Return error code HY096 for invalid attributes in or out of expected …
rscales Jul 3, 2025
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ dependency-reduced-pom.xml
MANIFEST
compile_commands.json
build.ninja
*/build*/

# Generated Visual Studio files
*.vcxproj
Expand Down
5 changes: 5 additions & 0 deletions cpp/src/arrow/flight/sql/odbc/flight_sql/get_info_cache.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,7 @@ void GetInfoCache::LoadDefaultsForMissingEntries() {
SetDefaultIfMissing(info_, SQL_CONVERT_DECIMAL, static_cast<uint32_t>(0));
SetDefaultIfMissing(info_, SQL_CONVERT_DOUBLE, static_cast<uint32_t>(0));
SetDefaultIfMissing(info_, SQL_CONVERT_FLOAT, static_cast<uint32_t>(0));
SetDefaultIfMissing(info_, SQL_CONVERT_FUNCTIONS, static_cast<uint32_t>(0));
SetDefaultIfMissing(info_, SQL_CONVERT_GUID, static_cast<uint32_t>(0));
SetDefaultIfMissing(info_, SQL_CONVERT_INTEGER, static_cast<uint32_t>(0));
Comment thread
rscales marked this conversation as resolved.
SetDefaultIfMissing(info_, SQL_CONVERT_INTERVAL_YEAR_MONTH, static_cast<uint32_t>(0));
Expand Down Expand Up @@ -1251,6 +1252,7 @@ void GetInfoCache::LoadDefaultsForMissingEntries() {
SetDefaultIfMissing(info_, SQL_MAX_COLUMNS_IN_ORDER_BY, static_cast<uint16_t>(0));
SetDefaultIfMissing(info_, SQL_MAX_COLUMNS_IN_SELECT, static_cast<uint16_t>(0));
SetDefaultIfMissing(info_, SQL_MAX_COLUMNS_IN_TABLE, static_cast<uint16_t>(0));
SetDefaultIfMissing(info_, SQL_MAX_CONCURRENT_ACTIVITIES, static_cast<uint16_t>(0));
SetDefaultIfMissing(info_, SQL_MAX_CURSOR_NAME_LEN, static_cast<uint16_t>(0));
SetDefaultIfMissing(info_, SQL_MAX_DRIVER_CONNECTIONS, static_cast<uint16_t>(0));
SetDefaultIfMissing(info_, SQL_MAX_IDENTIFIER_LEN, static_cast<uint16_t>(65535));
Expand All @@ -1270,6 +1272,7 @@ void GetInfoCache::LoadDefaultsForMissingEntries() {
SetDefaultIfMissing(info_, SQL_OJ_CAPABILITIES,
static_cast<uint32_t>(SQL_OJ_LEFT | SQL_OJ_RIGHT | SQL_OJ_FULL));
SetDefaultIfMissing(info_, SQL_ORDER_BY_COLUMNS_IN_SELECT, "Y");
SetDefaultIfMissing(info_, SQL_OUTER_JOINS, "N");
SetDefaultIfMissing(info_, SQL_PROCEDURE_TERM, "");
SetDefaultIfMissing(info_, SQL_PROCEDURES, "N");
SetDefaultIfMissing(info_, SQL_QUOTED_IDENTIFIER_CASE,
Expand All @@ -1278,6 +1281,7 @@ void GetInfoCache::LoadDefaultsForMissingEntries() {
SetDefaultIfMissing(info_, SQL_SCHEMA_USAGE,
static_cast<uint32_t>(SQL_SU_DML_STATEMENTS));
SetDefaultIfMissing(info_, SQL_SEARCH_PATTERN_ESCAPE, "\\");
SetDefaultIfMissing(info_, SQL_SPECIAL_CHARACTERS, "");
SetDefaultIfMissing(
info_, SQL_SERVER_NAME,
"Arrow Flight SQL Server"); // This might actually need to be the hostname.
Expand Down Expand Up @@ -1332,6 +1336,7 @@ void GetInfoCache::LoadDefaultsForMissingEntries() {
SQL_FN_TSI_FRAC_SECOND | SQL_FN_TSI_SECOND | SQL_FN_TSI_MINUTE |
SQL_FN_TSI_HOUR | SQL_FN_TSI_DAY | SQL_FN_TSI_WEEK |
SQL_FN_TSI_MONTH | SQL_FN_TSI_QUARTER | SQL_FN_TSI_YEAR));
SetDefaultIfMissing(info_, SQL_TIMEDATE_FUNCTIONS, static_cast<uint32_t>(0));
Comment thread
rscales marked this conversation as resolved.
Outdated
Comment thread
rscales marked this conversation as resolved.
Outdated
SetDefaultIfMissing(info_, SQL_UNION,
static_cast<uint32_t>(SQL_U_UNION | SQL_U_UNION_ALL));
SetDefaultIfMissing(info_, SQL_XOPEN_CLI_YEAR, "1995");
Expand Down
21 changes: 10 additions & 11 deletions cpp/src/arrow/flight/sql/odbc/odbc_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -846,27 +846,26 @@ SQLRETURN SQLDisconnect(SQLHDBC conn) {
}

SQLRETURN SQLGetInfo(SQLHDBC conn, SQLUSMALLINT infoType, SQLPOINTER infoValuePtr,
SQLSMALLINT bufLen, SQLSMALLINT* length) {
// TODO: complete implementation of SQLGetInfoW and write tests
SQLSMALLINT bufLen, SQLSMALLINT* stringLengthPtr) {
using ODBC::ODBCConnection;

LOG_DEBUG(
"SQLGetInfoW called with conn: {}, infoType: {}, infoValuePtr: {}, bufLen: {}, "
"length: {}",
conn, infoType, infoValuePtr, bufLen, fmt::ptr(length));
"SQLGetInfo called with conn: {}, infoType: {}, infoValuePtr: {}, bufLen: {}, "
"stringLengthPtr: {}",
conn, infoType, infoValuePtr, bufLen, fmt::ptr(stringLengthPtr));

return ODBCConnection::ExecuteWithDiagnostics(conn, SQL_ERROR, [=]() {
ODBCConnection* connection = reinterpret_cast<ODBCConnection*>(conn);

// Partially stubbed implementation of SQLGetInfoW
if (infoType == SQL_DRIVER_ODBC_VER) {
std::string_view ver("03.80");
// Set character type to be Unicode by default
const bool isUnicode = true;

return ODBC::GetStringAttribute(true, ver, true, infoValuePtr, bufLen, length,
connection->GetDiagnostics());
if (!infoValuePtr && !stringLengthPtr) {
return static_cast<SQLRETURN> SQL_ERROR;
}

return static_cast<SQLRETURN>(SQL_ERROR);
return connection->GetInfo(infoType, infoValuePtr, bufLen, stringLengthPtr,
isUnicode);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ class ODBCConnection : public ODBCHandle<ODBCConnection> {
const driver::odbcabstraction::Connection::ConnPropertyMap& properties,
std::vector<std::string_view>& missing_properties);

void GetInfo(SQLUSMALLINT infoType, SQLPOINTER value, SQLSMALLINT bufferLength,
SQLSMALLINT* outputLength, bool isUnicode);
SQLRETURN GetInfo(SQLUSMALLINT infoType, SQLPOINTER value, SQLSMALLINT bufferLength,
SQLSMALLINT* outputLength, bool isUnicode);
void SetConnectAttr(SQLINTEGER attribute, SQLPOINTER value, SQLINTEGER stringLength,
bool isUnicode);
SQLRETURN GetConnectAttr(SQLINTEGER attribute, SQLPOINTER value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,152 +136,144 @@ void ODBCConnection::connect(std::string dsn,
m_attributeTrackingStatement = std::make_shared<ODBCStatement>(*this, spiStatement);
}

void ODBCConnection::GetInfo(SQLUSMALLINT infoType, SQLPOINTER value,
SQLSMALLINT bufferLength, SQLSMALLINT* outputLength,
bool isUnicode) {
SQLRETURN ODBCConnection::GetInfo(SQLUSMALLINT infoType, SQLPOINTER value,
SQLSMALLINT bufferLength, SQLSMALLINT* outputLength,
bool isUnicode) {
switch (infoType) {
case SQL_ACTIVE_ENVIRONMENTS:
GetAttribute(static_cast<SQLUSMALLINT>(0), value, bufferLength, outputLength);
break;
return SQL_SUCCESS;
#ifdef SQL_ASYNC_DBC_FUNCTIONS
case SQL_ASYNC_DBC_FUNCTIONS:
GetAttribute(static_cast<SQLUINTEGER>(SQL_ASYNC_DBC_NOT_CAPABLE), value,
bufferLength, outputLength);
break;
return SQL_SUCCESS;
#endif
case SQL_ASYNC_MODE:
GetAttribute(static_cast<SQLUINTEGER>(SQL_AM_NONE), value, bufferLength,
outputLength);
break;
return SQL_SUCCESS;
#ifdef SQL_ASYNC_NOTIFICATION
case SQL_ASYNC_NOTIFICATION:
GetAttribute(static_cast<SQLUINTEGER>(SQL_ASYNC_NOTIFICATION_NOT_CAPABLE), value,
bufferLength, outputLength);
break;
return SQL_SUCCESS;
#endif
case SQL_BATCH_ROW_COUNT:
GetAttribute(static_cast<SQLUINTEGER>(0), value, bufferLength, outputLength);
break;
return SQL_SUCCESS;
case SQL_BATCH_SUPPORT:
GetAttribute(static_cast<SQLUINTEGER>(0), value, bufferLength, outputLength);
break;
return SQL_SUCCESS;
case SQL_DATA_SOURCE_NAME:
GetStringAttribute(isUnicode, m_dsn, true, value, bufferLength, outputLength,
GetDiagnostics());
break;
return GetStringAttribute(isUnicode, m_dsn, true, value, bufferLength, outputLength,
GetDiagnostics());
case SQL_DRIVER_ODBC_VER:
GetStringAttribute(isUnicode, "03.80", true, value, bufferLength, outputLength,
GetDiagnostics());
break;
return GetStringAttribute(isUnicode, "03.80", true, value, bufferLength, outputLength,
GetDiagnostics());
case SQL_DYNAMIC_CURSOR_ATTRIBUTES1:
GetAttribute(static_cast<SQLUINTEGER>(0), value, bufferLength, outputLength);
break;
return SQL_SUCCESS;
case SQL_DYNAMIC_CURSOR_ATTRIBUTES2:
GetAttribute(static_cast<SQLUINTEGER>(0), value, bufferLength, outputLength);
break;
return SQL_SUCCESS;
case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1:
GetAttribute(static_cast<SQLUINTEGER>(SQL_CA1_NEXT), value, bufferLength,
outputLength);
break;
return SQL_SUCCESS;
case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2:
GetAttribute(static_cast<SQLUINTEGER>(SQL_CA2_READ_ONLY_CONCURRENCY), value,
bufferLength, outputLength);
break;
return SQL_SUCCESS;
case SQL_FILE_USAGE:
GetAttribute(static_cast<SQLUSMALLINT>(SQL_FILE_NOT_SUPPORTED), value, bufferLength,
outputLength);
break;
return SQL_SUCCESS;
case SQL_KEYSET_CURSOR_ATTRIBUTES1:
GetAttribute(static_cast<SQLUINTEGER>(0), value, bufferLength, outputLength);
break;
return SQL_SUCCESS;
case SQL_KEYSET_CURSOR_ATTRIBUTES2:
GetAttribute(static_cast<SQLUINTEGER>(0), value, bufferLength, outputLength);
break;
return SQL_SUCCESS;
case SQL_MAX_ASYNC_CONCURRENT_STATEMENTS:
GetAttribute(static_cast<SQLUINTEGER>(0), value, bufferLength, outputLength);
break;
return SQL_SUCCESS;
case SQL_ODBC_INTERFACE_CONFORMANCE:
GetAttribute(static_cast<SQLUINTEGER>(SQL_OIC_CORE), value, bufferLength,
outputLength);
break;
return SQL_SUCCESS;
// case SQL_ODBC_STANDARD_CLI_CONFORMANCE: - mentioned in SQLGetInfo spec with no
// description and there is no constant for this.
case SQL_PARAM_ARRAY_ROW_COUNTS:
GetAttribute(static_cast<SQLUINTEGER>(SQL_PARC_NO_BATCH), value, bufferLength,
outputLength);
break;
return SQL_SUCCESS;
case SQL_PARAM_ARRAY_SELECTS:
GetAttribute(static_cast<SQLUINTEGER>(SQL_PAS_NO_SELECT), value, bufferLength,
outputLength);
break;
return SQL_SUCCESS;
case SQL_ROW_UPDATES:
GetStringAttribute(isUnicode, "N", true, value, bufferLength, outputLength,
GetDiagnostics());
break;
return GetStringAttribute(isUnicode, "N", true, value, bufferLength, outputLength,
GetDiagnostics());
case SQL_SCROLL_OPTIONS:
GetAttribute(static_cast<SQLUINTEGER>(SQL_SO_FORWARD_ONLY), value, bufferLength,
outputLength);
break;
return SQL_SUCCESS;
case SQL_STATIC_CURSOR_ATTRIBUTES1:
GetAttribute(static_cast<SQLUINTEGER>(0), value, bufferLength, outputLength);
break;
return SQL_SUCCESS;
case SQL_STATIC_CURSOR_ATTRIBUTES2:
GetAttribute(static_cast<SQLUINTEGER>(0), value, bufferLength, outputLength);
break;
return SQL_SUCCESS;
case SQL_BOOKMARK_PERSISTENCE:
GetAttribute(static_cast<SQLUINTEGER>(0), value, bufferLength, outputLength);
break;
return SQL_SUCCESS;
case SQL_DESCRIBE_PARAMETER:
GetStringAttribute(isUnicode, "N", true, value, bufferLength, outputLength,
GetDiagnostics());
break;
return GetStringAttribute(isUnicode, "N", true, value, bufferLength, outputLength,
GetDiagnostics());
case SQL_MULT_RESULT_SETS:
GetStringAttribute(isUnicode, "N", true, value, bufferLength, outputLength,
GetDiagnostics());
break;
return GetStringAttribute(isUnicode, "N", true, value, bufferLength, outputLength,
GetDiagnostics());
case SQL_MULTIPLE_ACTIVE_TXN:
GetStringAttribute(isUnicode, "N", true, value, bufferLength, outputLength,
GetDiagnostics());
break;
return GetStringAttribute(isUnicode, "N", true, value, bufferLength, outputLength,
GetDiagnostics());
case SQL_NEED_LONG_DATA_LEN:
GetStringAttribute(isUnicode, "N", true, value, bufferLength, outputLength,
GetDiagnostics());
break;
return GetStringAttribute(isUnicode, "N", true, value, bufferLength, outputLength,
GetDiagnostics());
case SQL_TXN_CAPABLE:
GetAttribute(static_cast<SQLUSMALLINT>(SQL_TC_NONE), value, bufferLength,
outputLength);
break;
return SQL_SUCCESS;
case SQL_TXN_ISOLATION_OPTION:
GetAttribute(static_cast<SQLUINTEGER>(0), value, bufferLength, outputLength);
break;
return SQL_SUCCESS;
case SQL_TABLE_TERM:
GetStringAttribute(isUnicode, "table", true, value, bufferLength, outputLength,
GetDiagnostics());
break;
return GetStringAttribute(isUnicode, "table", true, value, bufferLength, outputLength,
GetDiagnostics());
// Deprecated ODBC 2.x fields required for backwards compatibility.
case SQL_ODBC_API_CONFORMANCE:
GetAttribute(static_cast<SQLUSMALLINT>(SQL_OAC_LEVEL1), value, bufferLength,
outputLength);
break;
return SQL_SUCCESS;
case SQL_FETCH_DIRECTION:
GetAttribute(static_cast<SQLINTEGER>(SQL_FETCH_NEXT), value, bufferLength,
outputLength);
break;
return SQL_SUCCESS;
case SQL_LOCK_TYPES:
GetAttribute(static_cast<SQLINTEGER>(0), value, bufferLength, outputLength);
break;
return SQL_SUCCESS;
case SQL_POS_OPERATIONS:
GetAttribute(static_cast<SQLINTEGER>(0), value, bufferLength, outputLength);
break;
return SQL_SUCCESS;
case SQL_POSITIONED_STATEMENTS:
GetAttribute(static_cast<SQLINTEGER>(0), value, bufferLength, outputLength);
break;
return SQL_SUCCESS;
case SQL_SCROLL_CONCURRENCY:
GetAttribute(static_cast<SQLINTEGER>(0), value, bufferLength, outputLength);
break;
return SQL_SUCCESS;
case SQL_STATIC_SENSITIVITY:
GetAttribute(static_cast<SQLINTEGER>(0), value, bufferLength, outputLength);
break;
return SQL_SUCCESS;

// Driver-level string properties.
case SQL_USER_NAME:
Expand Down Expand Up @@ -316,9 +308,8 @@ void ODBCConnection::GetInfo(SQLUSMALLINT infoType, SQLPOINTER value,
case SQL_XOPEN_CLI_YEAR: {
const auto& info = m_spiConnection->GetInfo(infoType);
const std::string& infoValue = boost::get<std::string>(info);
GetStringAttribute(isUnicode, infoValue, true, value, bufferLength, outputLength,
GetDiagnostics());
break;
return GetStringAttribute(isUnicode, infoValue, true, value, bufferLength, outputLength,
GetDiagnostics());
}

// Driver-level 32-bit integer properties.
Expand Down Expand Up @@ -408,7 +399,7 @@ void ODBCConnection::GetInfo(SQLUSMALLINT infoType, SQLPOINTER value,
const auto& info = m_spiConnection->GetInfo(infoType);
uint32_t infoValue = boost::get<uint32_t>(info);
GetAttribute(infoValue, value, bufferLength, outputLength);
break;
return SQL_SUCCESS;
}

// Driver-level 16-bit integer properties.
Expand Down Expand Up @@ -443,7 +434,7 @@ void ODBCConnection::GetInfo(SQLUSMALLINT infoType, SQLPOINTER value,
const auto& info = m_spiConnection->GetInfo(infoType);
uint16_t infoValue = boost::get<uint16_t>(info);
GetAttribute(infoValue, value, bufferLength, outputLength);
break;
return SQL_SUCCESS;
}

// Special case - SQL_DATABASE_NAME is an alias for SQL_ATTR_CURRENT_CATALOG.
Expand All @@ -453,13 +444,14 @@ void ODBCConnection::GetInfo(SQLUSMALLINT infoType, SQLPOINTER value,
throw DriverException("Optional feature not supported.", "HYC00");
}
const std::string& infoValue = boost::get<std::string>(*attr);
GetStringAttribute(isUnicode, infoValue, true, value, bufferLength, outputLength,
GetDiagnostics());
break;
return GetStringAttribute(isUnicode, infoValue, true, value, bufferLength, outputLength,
GetDiagnostics());
}
default:
throw DriverException("Unknown SQLGetInfo type: " + std::to_string(infoType));
}

Comment thread
rscales marked this conversation as resolved.
return SQL_ERROR;
}

void ODBCConnection::SetConnectAttr(SQLINTEGER attribute, SQLPOINTER value,
Expand Down
3 changes: 2 additions & 1 deletion cpp/src/arrow/flight/sql/odbc/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ set(ARROW_FLIGHT_SQL_MOCK_SERVER_SRCS

add_arrow_test(connection_test
SOURCES
connection_test.cc
connection_attr_test.cc
connection_info_test.cc
connection_test.cc
odbc_test_suite.cc
odbc_test_suite.h
${ARROW_FLIGHT_SQL_MOCK_SERVER_SRCS}
Expand Down
Loading