Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
9 changes: 2 additions & 7 deletions cpp/src/arrow/flight/sql/odbc/entry_points.cc
Original file line number Diff line number Diff line change
Expand Up @@ -254,13 +254,8 @@ SQLRETURN SQL_API SQLNativeSql(SQLHDBC connectionHandle, SQLWCHAR* inStatementTe
SQLINTEGER inStatementTextLength,
SQLWCHAR* outStatementText, SQLINTEGER bufferLength,
SQLINTEGER* outStatementTextLength) {
LOG_DEBUG(
"SQLNativeSqlW called with connectionHandle: {}, inStatementText: {}, "
"inStatementTextLength: "
"{}, outStatementText: {}, bufferLength: {}, outStatementTextLength: {}",
connectionHandle, fmt::ptr(inStatementText), inStatementTextLength,
fmt::ptr(outStatementText), bufferLength, fmt::ptr(outStatementTextLength));
return SQL_ERROR;
return arrow::SQLNativeSql(connectionHandle, inStatementText, inStatementTextLength,
outStatementText, bufferLength, outStatementTextLength);
}

SQLRETURN SQL_API SQLNumResultCols(SQLHSTMT stmt, SQLSMALLINT* columnCountPtr) {
Expand Down
29 changes: 29 additions & 0 deletions cpp/src/arrow/flight/sql/odbc/odbc_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1273,4 +1273,33 @@ SQLRETURN SQLColAttribute(SQLHSTMT stmt, SQLUSMALLINT recordNumber,
return SQL_SUCCESS;
});
}

SQLRETURN SQLNativeSql(SQLHDBC connectionHandle, SQLWCHAR* inStatementText,
SQLINTEGER inStatementTextLength, SQLWCHAR* outStatementText,
SQLINTEGER bufferLength, SQLINTEGER* outStatementTextLength) {
LOG_DEBUG(
"SQLNativeSqlW called with connectionHandle: {}, inStatementText: {}, "
"inStatementTextLength: {}, outStatementText: {}, bufferLength: {}, "
"outStatementTextLength: {}",
connectionHandle, fmt::ptr(inStatementText), inStatementTextLength,
fmt::ptr(outStatementText), bufferLength, fmt::ptr(outStatementTextLength));

using driver::odbcabstraction::Diagnostics;
using ODBC::GetAttributeSQLWCHAR;
using ODBC::ODBCConnection;
using ODBC::SqlWcharToString;

return ODBCConnection::ExecuteWithDiagnostics(connectionHandle, SQL_ERROR, [=]() {
const bool isLengthInBytes = false;

ODBCConnection* connection = reinterpret_cast<ODBCConnection*>(connectionHandle);
Diagnostics& diagnostics = connection->GetDiagnostics();

std::string inStatementStr = SqlWcharToString(inStatementText, inStatementTextLength);

return GetAttributeSQLWCHAR(inStatementStr, isLengthInBytes, outStatementText,
bufferLength, outStatementTextLength, diagnostics);
});
}

} // namespace arrow
3 changes: 3 additions & 0 deletions cpp/src/arrow/flight/sql/odbc/odbc_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,7 @@ SQLRETURN SQLColAttribute(SQLHSTMT stmt, SQLUSMALLINT recordNumber,
SQLUSMALLINT fieldIdentifier, SQLPOINTER characterAttributePtr,
SQLSMALLINT bufferLength, SQLSMALLINT* outputLength,
SQLLEN* numericAttributePtr);
SQLRETURN SQLNativeSql(SQLHDBC connectionHandle, SQLWCHAR* inStatementText,
SQLINTEGER inStatementTextLength, SQLWCHAR* outStatementText,
SQLINTEGER bufferLength, SQLINTEGER* outStatementTextLength);
} // namespace arrow
2 changes: 2 additions & 0 deletions cpp/src/arrow/flight/sql/odbc/tests/odbc_test_suite.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,10 @@ static constexpr std::string_view error_state_22002 = "22002";
static constexpr std::string_view error_state_24000 = "24000";
static constexpr std::string_view error_state_28000 = "28000";
static constexpr std::string_view error_state_HY000 = "HY000";
static constexpr std::string_view error_state_HY009 = "HY009";
static constexpr std::string_view error_state_HY010 = "HY010";
static constexpr std::string_view error_state_HY024 = "HY024";
static constexpr std::string_view error_state_HY090 = "HY090";
static constexpr std::string_view error_state_HY092 = "HY092";
static constexpr std::string_view error_state_HYC00 = "HYC00";
static constexpr std::string_view error_state_HY106 = "HY106";
Expand Down
133 changes: 133 additions & 0 deletions cpp/src/arrow/flight/sql/odbc/tests/statement_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2234,4 +2234,137 @@ TEST_F(FlightSQLODBCRemoteTestBase, TestSQLExtendedFetchQueryNullIndicator) {

this->disconnect();
}

TYPED_TEST(FlightSQLODBCTestBase, TestSQLNativeSqlReturnsInputString) {
this->connect();

SQLWCHAR buf[1024];
constexpr SQLINTEGER bufCharLen = sizeof(buf) / ODBC::GetSqlWCharSize();
SQLWCHAR inputStr[] = L"SELECT * FROM mytable WHERE id == 1";
SQLINTEGER inputCharLen = static_cast<SQLINTEGER>(wcslen(inputStr));
SQLINTEGER outputCharLen = 0;
std::wstring expectedString = std::wstring(inputStr);

SQLRETURN ret =
SQLNativeSql(conn, inputStr, inputCharLen, buf, bufCharLen, &outputCharLen);

EXPECT_EQ(ret, SQL_SUCCESS);

EXPECT_EQ(outputCharLen, inputCharLen);

// returned length is in characters
std::wstring returnedString(buf, buf + outputCharLen);

EXPECT_EQ(returnedString, expectedString);

this->disconnect();
}

TYPED_TEST(FlightSQLODBCTestBase, TestSQLNativeSqlReturnsNTSInputString) {
this->connect();

SQLWCHAR buf[1024];
constexpr SQLINTEGER bufCharLen = sizeof(buf) / ODBC::GetSqlWCharSize();
SQLWCHAR inputStr[] = L"SELECT * FROM mytable WHERE id == 1";
SQLINTEGER inputCharLen = static_cast<SQLINTEGER>(wcslen(inputStr));
SQLINTEGER outputCharLen = 0;
std::wstring expectedString = std::wstring(inputStr);

SQLRETURN ret = SQLNativeSql(conn, inputStr, SQL_NTS, buf, bufCharLen, &outputCharLen);

EXPECT_EQ(ret, SQL_SUCCESS);

EXPECT_EQ(outputCharLen, inputCharLen);

// returned length is in characters
std::wstring returnedString(buf, buf + outputCharLen);

EXPECT_EQ(returnedString, expectedString);

this->disconnect();
}

TYPED_TEST(FlightSQLODBCTestBase, TestSQLNativeSqlReturnsInputStringLength) {
this->connect();

SQLWCHAR inputStr[] = L"SELECT * FROM mytable WHERE id == 1";
SQLINTEGER inputCharLen = static_cast<SQLINTEGER>(wcslen(inputStr));
SQLINTEGER outputCharLen = 0;
std::wstring expectedString = std::wstring(inputStr);

SQLRETURN ret = SQLNativeSql(conn, inputStr, inputCharLen, nullptr, 0, &outputCharLen);

EXPECT_EQ(ret, SQL_SUCCESS);

EXPECT_EQ(outputCharLen, inputCharLen);

ret = SQLNativeSql(conn, inputStr, SQL_NTS, nullptr, 0, &outputCharLen);

EXPECT_EQ(ret, SQL_SUCCESS);

EXPECT_EQ(outputCharLen, inputCharLen);

this->disconnect();
}

TYPED_TEST(FlightSQLODBCTestBase, TestSQLNativeSqlReturnsTruncatedString) {
this->connect();

const SQLINTEGER smallBufSizeInChar = 11;
SQLWCHAR smallBuf[smallBufSizeInChar];
constexpr SQLINTEGER smallBufCharLen = sizeof(smallBuf) / ODBC::GetSqlWCharSize();
SQLWCHAR inputStr[] = L"SELECT * FROM mytable WHERE id == 1";
SQLINTEGER inputCharLen = static_cast<SQLINTEGER>(wcslen(inputStr));
SQLINTEGER outputCharLen = 0;

// Create expected return string based on buf size
SQLWCHAR expectedStringBuf[smallBufSizeInChar];
wcsncpy(expectedStringBuf, inputStr, 10);
expectedStringBuf[10] = L'\0';
std::wstring expectedString(expectedStringBuf, expectedStringBuf + smallBufSizeInChar);

SQLRETURN ret = SQLNativeSql(conn, inputStr, inputCharLen, smallBuf, smallBufCharLen,
&outputCharLen);

EXPECT_EQ(ret, SQL_SUCCESS_WITH_INFO);
VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, error_state_01004);

// Returned text length represents full string char length regardless of truncation
EXPECT_EQ(outputCharLen, inputCharLen);

std::wstring returnedString(smallBuf, smallBuf + smallBufCharLen);

EXPECT_EQ(returnedString, expectedString);

this->disconnect();
}

TYPED_TEST(FlightSQLODBCTestBase, TestSQLNativeSqlReturnsErrorOnBadInputs) {
this->connect();

SQLWCHAR buf[1024];
constexpr SQLINTEGER bufCharLen = sizeof(buf) / ODBC::GetSqlWCharSize();
SQLWCHAR inputStr[] = L"SELECT * FROM mytable WHERE id == 1";
SQLINTEGER inputCharLen = static_cast<SQLINTEGER>(wcslen(inputStr));
SQLINTEGER outputCharLen = 0;

SQLRETURN ret =
SQLNativeSql(conn, nullptr, inputCharLen, buf, bufCharLen, &outputCharLen);

EXPECT_EQ(ret, SQL_ERROR);
VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, error_state_HY009);

ret = SQLNativeSql(conn, nullptr, SQL_NTS, buf, bufCharLen, &outputCharLen);

EXPECT_EQ(ret, SQL_ERROR);
VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, error_state_HY009);

ret = SQLNativeSql(conn, inputStr, -100, buf, bufCharLen, &outputCharLen);

EXPECT_EQ(ret, SQL_ERROR);
VerifyOdbcErrorState(SQL_HANDLE_DBC, this->conn, error_state_HY090);

this->disconnect();
}

} // namespace arrow::flight::sql::odbc