-
Notifications
You must be signed in to change notification settings - Fork 3.9k
GH-46098 : [C++][FlightRPC] ODBC Environment Attribute Implementation #47760
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -100,16 +100,105 @@ SQLRETURN SQLGetEnvAttr(SQLHENV env, SQLINTEGER attr, SQLPOINTER value_ptr, | |
ARROW_LOG(DEBUG) << "SQLGetEnvAttr called with env: " << env << ", attr: " << attr | ||
<< ", value_ptr: " << value_ptr << ", buffer_length: " << buffer_length | ||
<< ", str_len_ptr: " << static_cast<const void*>(str_len_ptr); | ||
// GH-46575 TODO: Implement SQLGetEnvAttr | ||
return SQL_INVALID_HANDLE; | ||
|
||
using ODBC::ODBCEnvironment; | ||
|
||
ODBCEnvironment* environment = reinterpret_cast<ODBCEnvironment*>(env); | ||
|
||
return ODBCEnvironment::ExecuteWithDiagnostics(environment, SQL_ERROR, [=]() { | ||
switch (attr) { | ||
case SQL_ATTR_ODBC_VERSION: { | ||
if (!value_ptr && !str_len_ptr) { | ||
throw DriverException("Invalid null pointer for attribute.", "HY000"); | ||
} | ||
Comment on lines
+111
to
+113
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Test this case as well? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a test Potentially this can be tested on mac/linux depending on the driver manager on these systems
Comment on lines
+111
to
+113
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we check this out of this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably not in this case. I think attribute needs to be checked first before the pointers, and ODBC needs to error out on attribute value first. |
||
|
||
if (value_ptr) { | ||
SQLINTEGER* value = reinterpret_cast<SQLINTEGER*>(value_ptr); | ||
*value = static_cast<SQLSMALLINT>(environment->GetODBCVersion()); | ||
} | ||
|
||
if (str_len_ptr) { | ||
*str_len_ptr = sizeof(SQLINTEGER); | ||
} | ||
|
||
return SQL_SUCCESS; | ||
} | ||
|
||
case SQL_ATTR_OUTPUT_NTS: { | ||
if (!value_ptr && !str_len_ptr) { | ||
throw DriverException("Invalid null pointer for attribute.", "HY000"); | ||
} | ||
|
||
if (value_ptr) { | ||
// output nts always returns SQL_TRUE | ||
SQLINTEGER* value = reinterpret_cast<SQLINTEGER*>(value_ptr); | ||
*value = SQL_TRUE; | ||
} | ||
|
||
if (str_len_ptr) { | ||
*str_len_ptr = sizeof(SQLINTEGER); | ||
} | ||
|
||
return SQL_SUCCESS; | ||
} | ||
|
||
case SQL_ATTR_CONNECTION_POOLING: { | ||
throw DriverException("Optional feature not supported.", "HYC00"); | ||
} | ||
|
||
default: { | ||
throw DriverException("Invalid attribute", "HYC00"); | ||
} | ||
} | ||
}); | ||
} | ||
|
||
SQLRETURN SQLSetEnvAttr(SQLHENV env, SQLINTEGER attr, SQLPOINTER value_ptr, | ||
SQLINTEGER str_len) { | ||
ARROW_LOG(DEBUG) << "SQLSetEnvAttr called with env: " << env << ", attr: " << attr | ||
<< ", value_ptr: " << value_ptr << ", str_len: " << str_len; | ||
// GH-46575 TODO: Implement SQLSetEnvAttr | ||
return SQL_INVALID_HANDLE; | ||
|
||
using ODBC::ODBCEnvironment; | ||
|
||
ODBCEnvironment* environment = reinterpret_cast<ODBCEnvironment*>(env); | ||
|
||
return ODBCEnvironment::ExecuteWithDiagnostics(environment, SQL_ERROR, [=]() { | ||
if (!value_ptr) { | ||
throw DriverException("Invalid null pointer for attribute.", "HY024"); | ||
} | ||
|
||
switch (attr) { | ||
case SQL_ATTR_ODBC_VERSION: { | ||
SQLINTEGER version = | ||
static_cast<SQLINTEGER>(reinterpret_cast<intptr_t>(value_ptr)); | ||
if (version == SQL_OV_ODBC2 || version == SQL_OV_ODBC3) { | ||
environment->SetODBCVersion(version); | ||
|
||
return SQL_SUCCESS; | ||
} else { | ||
throw DriverException("Invalid value for attribute", "HY024"); | ||
} | ||
} | ||
|
||
case SQL_ATTR_OUTPUT_NTS: { | ||
// output nts can not be set to SQL_FALSE, is always SQL_TRUE | ||
SQLINTEGER value = static_cast<SQLINTEGER>(reinterpret_cast<intptr_t>(value_ptr)); | ||
if (value == SQL_TRUE) { | ||
return SQL_SUCCESS; | ||
} else { | ||
throw DriverException("Invalid value for attribute", "HY024"); | ||
} | ||
} | ||
|
||
case SQL_ATTR_CONNECTION_POOLING: { | ||
throw DriverException("Optional feature not supported.", "HYC00"); | ||
} | ||
|
||
default: { | ||
throw DriverException("Invalid attribute", "HY092"); | ||
} | ||
} | ||
}); | ||
} | ||
|
||
SQLRETURN SQLGetConnectAttr(SQLHDBC conn, SQLINTEGER attribute, SQLPOINTER value_ptr, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
// 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/tests/odbc_test_suite.h" | ||
|
||
#include "arrow/flight/sql/odbc/odbc_impl/platform.h" | ||
|
||
#include <sql.h> | ||
#include <sqltypes.h> | ||
#include <sqlucode.h> | ||
|
||
#include <gtest/gtest.h> | ||
|
||
namespace arrow::flight::sql::odbc { | ||
|
||
template <typename T> | ||
class ConnectionTest : public T { | ||
public: | ||
using List = std::list<T>; | ||
}; | ||
|
||
class ConnectionRemoteTest : public FlightSQLODBCRemoteTestBase {}; | ||
using TestTypes = ::testing::Types<FlightSQLODBCMockTestBase, ConnectionRemoteTest>; | ||
TYPED_TEST_SUITE(ConnectionTest, TestTypes); | ||
|
||
TEST(SQLGetEnvAttr, TestSQLGetEnvAttrODBCVersion) { | ||
SQLHENV env; | ||
|
||
SQLINTEGER version; | ||
|
||
// Allocate an environment handle | ||
ASSERT_EQ(SQL_SUCCESS, SQLAllocEnv(&env)); | ||
|
||
ASSERT_EQ(SQL_SUCCESS, SQLGetEnvAttr(env, SQL_ATTR_ODBC_VERSION, &version, 0, 0)); | ||
|
||
ASSERT_EQ(SQL_OV_ODBC2, version); | ||
|
||
ASSERT_EQ(SQL_SUCCESS, SQLFreeEnv(env)); | ||
} | ||
|
||
TEST(SQLSetEnvAttr, TestSQLSetEnvAttrODBCVersionValid) { | ||
SQLHENV env; | ||
|
||
// Allocate an environment handle | ||
ASSERT_EQ(SQL_SUCCESS, SQLAllocEnv(&env)); | ||
|
||
// Attempt to set to supported version | ||
ASSERT_EQ(SQL_SUCCESS, SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, | ||
reinterpret_cast<void*>(SQL_OV_ODBC2), 0)); | ||
|
||
SQLINTEGER version; | ||
// Check ODBC version is set | ||
ASSERT_EQ(SQL_SUCCESS, SQLGetEnvAttr(env, SQL_ATTR_ODBC_VERSION, &version, 0, 0)); | ||
|
||
ASSERT_EQ(SQL_OV_ODBC2, version); | ||
|
||
ASSERT_EQ(SQL_SUCCESS, SQLFreeEnv(env)); | ||
} | ||
|
||
TEST(SQLSetEnvAttr, TestSQLSetEnvAttrODBCVersionInvalid) { | ||
SQLHENV env; | ||
|
||
// Allocate an environment handle | ||
ASSERT_EQ(SQL_SUCCESS, SQLAllocEnv(&env)); | ||
|
||
// Attempt to set to unsupported version | ||
ASSERT_EQ(SQL_ERROR, | ||
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, reinterpret_cast<void*>(1), 0)); | ||
|
||
ASSERT_EQ(SQL_SUCCESS, SQLFreeEnv(env)); | ||
} | ||
|
||
TYPED_TEST(ConnectionTest, TestSQLGetEnvAttrOutputNTS) { | ||
SQLINTEGER output_nts; | ||
|
||
ASSERT_EQ(SQL_SUCCESS, | ||
SQLGetEnvAttr(this->env, SQL_ATTR_OUTPUT_NTS, &output_nts, 0, 0)); | ||
|
||
ASSERT_EQ(SQL_TRUE, output_nts); | ||
} | ||
|
||
TYPED_TEST(ConnectionTest, DISABLED_TestSQLGetEnvAttrGetLength) { | ||
// Test is disabled because call to SQLGetEnvAttr is handled by the driver manager on | ||
// Windows. Windows driver manager ignores the length pointer. | ||
// This test case can be potentially used on macOS/Linux | ||
SQLINTEGER length; | ||
ASSERT_EQ(SQL_SUCCESS, | ||
SQLGetEnvAttr(this->env, SQL_ATTR_ODBC_VERSION, nullptr, 0, &length)); | ||
|
||
EXPECT_EQ(sizeof(SQLINTEGER), length); | ||
} | ||
|
||
TYPED_TEST(ConnectionTest, DISABLED_TestSQLGetEnvAttrNullValuePointer) { | ||
// Test is disabled because call to SQLGetEnvAttr is handled by the driver manager on | ||
// Windows. The Windows driver manager doesn't error out when null pointer is passed. | ||
// This test case can be potentially used on macOS/Linux | ||
ASSERT_EQ(SQL_ERROR, | ||
SQLGetEnvAttr(this->env, SQL_ATTR_ODBC_VERSION, nullptr, 0, nullptr)); | ||
} | ||
|
||
TEST(SQLSetEnvAttr, TestSQLSetEnvAttrOutputNTSValid) { | ||
SQLHENV env; | ||
|
||
// Allocate an environment handle | ||
ASSERT_EQ(SQL_SUCCESS, SQLAllocEnv(&env)); | ||
|
||
// Attempt to set to output nts to supported version | ||
ASSERT_EQ(SQL_SUCCESS, SQLSetEnvAttr(env, SQL_ATTR_OUTPUT_NTS, | ||
reinterpret_cast<void*>(SQL_TRUE), 0)); | ||
|
||
ASSERT_EQ(SQL_SUCCESS, SQLFreeEnv(env)); | ||
} | ||
|
||
TEST(SQLSetEnvAttr, TestSQLSetEnvAttrOutputNTSInvalid) { | ||
SQLHENV env; | ||
|
||
// Allocate an environment handle | ||
ASSERT_EQ(SQL_SUCCESS, SQLAllocEnv(&env)); | ||
|
||
// Attempt to set to output nts to unsupported false | ||
ASSERT_EQ(SQL_ERROR, SQLSetEnvAttr(env, SQL_ATTR_OUTPUT_NTS, | ||
reinterpret_cast<void*>(SQL_FALSE), 0)); | ||
|
||
ASSERT_EQ(SQL_SUCCESS, SQLFreeEnv(env)); | ||
} | ||
|
||
TEST(SQLSetEnvAttr, TestSQLSetEnvAttrNullValuePointer) { | ||
SQLHENV env; | ||
|
||
// Allocate an environment handle | ||
ASSERT_EQ(SQL_SUCCESS, SQLAllocEnv(&env)); | ||
|
||
// Attempt to set using bad data pointer | ||
ASSERT_EQ(SQL_ERROR, SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, nullptr, 0)); | ||
|
||
ASSERT_EQ(SQL_SUCCESS, SQLFreeEnv(env)); | ||
} | ||
|
||
} // namespace arrow::flight::sql::odbc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we remove this debug print now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We generally log the ODBC API calls for debugging so we know which ODBC APIs got passed to the driver, is it ok to keep these logs?