Skip to content

Commit e8ec979

Browse files
authored
Implement SQLSetEnvAttr and SQLGetEnvAttr
1 parent 9e8d0be commit e8ec979

File tree

4 files changed

+310
-12
lines changed

4 files changed

+310
-12
lines changed

cpp/src/arrow/flight/sql/odbc/entry_points.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ SQLRETURN SQL_API SQLFreeConnect(SQLHDBC conn) {
5151
return arrow::SQLFreeHandle(SQL_HANDLE_DBC, conn);
5252
}
5353

54+
SQLRETURN SQL_API SQLGetEnvAttr(SQLHENV env, SQLINTEGER attr, SQLPOINTER valuePtr,
55+
SQLINTEGER bufferLen, SQLINTEGER* strLenPtr) {
56+
return arrow::SQLGetEnvAttr(env, attr, valuePtr, bufferLen, strLenPtr);
57+
}
58+
59+
SQLRETURN SQL_API SQLSetEnvAttr(SQLHENV env, SQLINTEGER attr, SQLPOINTER valuePtr,
60+
SQLINTEGER strLen) {
61+
return arrow::SQLSetEnvAttr(env, attr, valuePtr, strLen);
62+
}
63+
5464
SQLRETURN SQL_API SQLDriverConnect(SQLHDBC conn, SQLHWND windowHandle,
5565
SQLWCHAR* inConnectionString,
5666
SQLSMALLINT inConnectionStringLen,

cpp/src/arrow/flight/sql/odbc/odbc_api.cc

Lines changed: 140 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,19 @@ SQLRETURN SQLAllocHandle(SQLSMALLINT type, SQLHANDLE parent, SQLHANDLE* result)
3434
using driver::flight_sql::FlightSqlDriver;
3535
using ODBC::ODBCEnvironment;
3636

37-
static std::shared_ptr<FlightSqlDriver> odbc_driver =
38-
std::make_shared<FlightSqlDriver>();
39-
*result = reinterpret_cast<SQLHENV>(new ODBCEnvironment(odbc_driver));
40-
41-
return SQL_SUCCESS;
37+
*result = SQL_NULL_HENV;
38+
39+
try {
40+
static std::shared_ptr<FlightSqlDriver> odbc_driver =
41+
std::make_shared<FlightSqlDriver>();
42+
*result = reinterpret_cast<SQLHENV>(new ODBCEnvironment(odbc_driver));
43+
44+
return SQL_SUCCESS;
45+
} catch (const std::bad_alloc&) {
46+
// Will be caught in odbc abstration layer if using execute with diagnostics
47+
// allocating environment failed so cannot log diagnostic error here
48+
return SQL_ERROR;
49+
}
4250
}
4351

4452
case SQL_HANDLE_DBC: {
@@ -53,15 +61,18 @@ SQLRETURN SQLAllocHandle(SQLSMALLINT type, SQLHANDLE parent, SQLHANDLE* result)
5361
return SQL_INVALID_HANDLE;
5462
}
5563

56-
std::shared_ptr<ODBCConnection> conn = environment->CreateConnection();
57-
58-
if (!conn) {
59-
return SQL_INVALID_HANDLE;
60-
}
64+
try {
65+
std::shared_ptr<ODBCConnection> conn = environment->CreateConnection();
66+
*result = reinterpret_cast<SQLHDBC>(conn.get());
6167

62-
*result = reinterpret_cast<SQLHDBC>(conn.get());
68+
return SQL_SUCCESS;
69+
} catch (const std::bad_alloc&) {
70+
// Will be caught in odbc abstration layer if using execute with diagnostics
71+
environment->GetDiagnostics().AddError(driver::odbcabstraction::DriverException(
72+
"A memory allocation error occurred.", "HY001"));
6373

64-
return SQL_SUCCESS;
74+
return SQL_ERROR;
75+
}
6576
}
6677

6778
case SQL_HANDLE_STMT: {
@@ -118,4 +129,121 @@ SQLRETURN SQLFreeHandle(SQLSMALLINT type, SQLHANDLE handle) {
118129
return SQL_ERROR;
119130
}
120131

132+
SQLRETURN SQLGetEnvAttr(SQLHENV env, SQLINTEGER attr, SQLPOINTER valuePtr,
133+
SQLINTEGER bufferLen, SQLINTEGER* strLenPtr) {
134+
using ODBC::ODBCEnvironment;
135+
136+
ODBCEnvironment* environment = reinterpret_cast<ODBCEnvironment*>(env);
137+
138+
if (!environment) {
139+
return SQL_INVALID_HANDLE;
140+
}
141+
142+
switch (attr) {
143+
case SQL_ATTR_ODBC_VERSION: {
144+
if (valuePtr) {
145+
SQLINTEGER* value = reinterpret_cast<SQLINTEGER*>(valuePtr);
146+
*value = static_cast<SQLSMALLINT>(environment->getODBCVersion());
147+
return SQL_SUCCESS;
148+
} else if (strLenPtr) {
149+
*strLenPtr = sizeof(SQLINTEGER);
150+
return SQL_SUCCESS;
151+
} else {
152+
environment->GetDiagnostics().AddError(driver::odbcabstraction::DriverException(
153+
"Invalid null pointer for attribute.", "HY000"));
154+
return SQL_ERROR;
155+
}
156+
}
157+
158+
case SQL_ATTR_OUTPUT_NTS: {
159+
if (valuePtr) {
160+
// output nts always returns SQL_TRUE
161+
SQLINTEGER* value = reinterpret_cast<SQLINTEGER*>(valuePtr);
162+
*value = SQL_TRUE;
163+
return SQL_SUCCESS;
164+
} else if (strLenPtr) {
165+
*strLenPtr = sizeof(SQLINTEGER);
166+
return SQL_SUCCESS;
167+
} else {
168+
environment->GetDiagnostics().AddError(driver::odbcabstraction::DriverException(
169+
"Invalid null pointer for attribute.", "HY000"));
170+
return SQL_ERROR;
171+
}
172+
}
173+
174+
case SQL_ATTR_CONNECTION_POOLING:
175+
case SQL_ATTR_APP_ROW_DESC: {
176+
environment->GetDiagnostics().AddError(driver::odbcabstraction::DriverException(
177+
"Optional feature not supported.", "HYC00"));
178+
return SQL_ERROR;
179+
}
180+
181+
default: {
182+
environment->GetDiagnostics().AddError(
183+
driver::odbcabstraction::DriverException("Invalid attribute", "HYC00"));
184+
return SQL_ERROR;
185+
}
186+
}
187+
188+
return SQL_ERROR;
189+
}
190+
191+
SQLRETURN SQLSetEnvAttr(SQLHENV env, SQLINTEGER attr, SQLPOINTER valuePtr,
192+
SQLINTEGER strLen) {
193+
using ODBC::ODBCEnvironment;
194+
195+
ODBCEnvironment* environment = reinterpret_cast<ODBCEnvironment*>(env);
196+
197+
if (!environment) {
198+
return SQL_INVALID_HANDLE;
199+
}
200+
201+
if (!valuePtr) {
202+
environment->GetDiagnostics().AddError(driver::odbcabstraction::DriverException(
203+
"Invalid null pointer for attribute.", "HY024"));
204+
205+
return SQL_ERROR;
206+
}
207+
208+
switch (attr) {
209+
case SQL_ATTR_ODBC_VERSION: {
210+
SQLINTEGER version = static_cast<SQLINTEGER>(reinterpret_cast<intptr_t>(valuePtr));
211+
if (version == SQL_OV_ODBC2 || version == SQL_OV_ODBC3) {
212+
environment->setODBCVersion(version);
213+
return SQL_SUCCESS;
214+
} else {
215+
environment->GetDiagnostics().AddError(driver::odbcabstraction::DriverException(
216+
"Invalid value for attribute", "HY024"));
217+
return SQL_ERROR;
218+
}
219+
}
220+
221+
case SQL_ATTR_OUTPUT_NTS: {
222+
// output nts can not be set to SQL_FALSE, is always SQL_TRUE
223+
SQLINTEGER value = static_cast<SQLINTEGER>(reinterpret_cast<intptr_t>(valuePtr));
224+
if (value == SQL_TRUE) {
225+
return SQL_SUCCESS;
226+
} else {
227+
environment->GetDiagnostics().AddError(driver::odbcabstraction::DriverException(
228+
"Invalid value for attribute", "HY024"));
229+
return SQL_ERROR;
230+
}
231+
}
232+
233+
case SQL_ATTR_CONNECTION_POOLING:
234+
case SQL_ATTR_APP_ROW_DESC: {
235+
environment->GetDiagnostics().AddError(driver::odbcabstraction::DriverException(
236+
"Optional feature not supported.", "HYC00"));
237+
return SQL_ERROR;
238+
}
239+
240+
default: {
241+
environment->GetDiagnostics().AddError(
242+
driver::odbcabstraction::DriverException("Invalid attribute", "HY092"));
243+
return SQL_ERROR;
244+
}
245+
}
246+
247+
return SQL_ERROR;
248+
}
121249
} // namespace arrow

cpp/src/arrow/flight/sql/odbc/odbc_api.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,8 @@
3232
namespace arrow {
3333
SQLRETURN SQLAllocHandle(SQLSMALLINT type, SQLHANDLE parent, SQLHANDLE* result);
3434
SQLRETURN SQLFreeHandle(SQLSMALLINT type, SQLHANDLE handle);
35+
SQLRETURN SQLGetEnvAttr(SQLHENV env, SQLINTEGER attr, SQLPOINTER valuePtr,
36+
SQLINTEGER bufferLen, SQLINTEGER* strLenPtr);
37+
SQLRETURN SQLSetEnvAttr(SQLHENV env, SQLINTEGER attr, SQLPOINTER valuePtr,
38+
SQLINTEGER strLen);
3539
} // namespace arrow

cpp/src/arrow/flight/sql/odbc/tests/connection_test.cc

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,162 @@ TEST(SQLFreeConnect, TestSQLFreeConnect) {
149149
EXPECT_TRUE(return_free_connect == SQL_SUCCESS);
150150
}
151151

152+
TEST(SQLGetEnvAttr, TestSQLGetEnvAttrODBCVersion) {
153+
// ODBC Environment
154+
SQLHENV env;
155+
156+
SQLINTEGER version;
157+
158+
// Allocate an environment handle
159+
SQLRETURN return_env = SQLAllocEnv(&env);
160+
161+
EXPECT_TRUE(return_env == SQL_SUCCESS);
162+
163+
SQLRETURN return_get = SQLGetEnvAttr(env, SQL_ATTR_ODBC_VERSION, &version, 0, 0);
164+
165+
EXPECT_TRUE(return_get == SQL_SUCCESS);
166+
167+
EXPECT_EQ(version, SQL_OV_ODBC2);
168+
}
169+
170+
TEST(SQLSetEnvAttr, TestSQLSetEnvAttrODBCVersionValid) {
171+
// ODBC Environment
172+
SQLHENV env;
173+
174+
// Allocate an environment handle
175+
SQLRETURN return_env = SQLAllocEnv(&env);
176+
177+
EXPECT_TRUE(return_env == SQL_SUCCESS);
178+
179+
// Attempt to set to unsupported version
180+
SQLRETURN return_set =
181+
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, reinterpret_cast<void*>(SQL_OV_ODBC2), 0);
182+
183+
EXPECT_TRUE(return_set == SQL_SUCCESS);
184+
}
185+
186+
TEST(SQLSetEnvAttr, TestSQLSetEnvAttrODBCVersionInvalid) {
187+
// ODBC Environment
188+
SQLHENV env;
189+
190+
// Allocate an environment handle
191+
SQLRETURN return_env = SQLAllocEnv(&env);
192+
193+
EXPECT_TRUE(return_env == SQL_SUCCESS);
194+
195+
// Attempt to set to unsupported version
196+
SQLRETURN return_set =
197+
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, reinterpret_cast<void*>(1), 0);
198+
199+
EXPECT_TRUE(return_set == SQL_ERROR);
200+
}
201+
202+
TEST(SQLGetEnvAttr, TestSQLGetEnvAttrOutputNTS) {
203+
// ODBC Environment
204+
SQLHENV env;
205+
206+
SQLINTEGER output_nts;
207+
208+
// Allocate an environment handle
209+
SQLRETURN return_env = SQLAllocEnv(&env);
210+
211+
EXPECT_TRUE(return_env == SQL_SUCCESS);
212+
213+
SQLRETURN return_get = SQLGetEnvAttr(env, SQL_ATTR_OUTPUT_NTS, &output_nts, 0, 0);
214+
215+
EXPECT_TRUE(return_get == SQL_SUCCESS);
216+
217+
EXPECT_EQ(output_nts, SQL_TRUE);
218+
}
219+
220+
TEST(SQLGetEnvAttr, TestSQLGetEnvAttrGetLength) {
221+
// ODBC Environment
222+
SQLHENV env;
223+
224+
SQLINTEGER length;
225+
226+
// Allocate an environment handle
227+
SQLRETURN return_env = SQLAllocEnv(&env);
228+
229+
EXPECT_TRUE(return_env == SQL_SUCCESS);
230+
231+
SQLRETURN return_get = SQLGetEnvAttr(env, SQL_ATTR_ODBC_VERSION, nullptr, 0, &length);
232+
233+
EXPECT_TRUE(return_get == SQL_SUCCESS);
234+
235+
EXPECT_EQ(length, sizeof(SQLINTEGER));
236+
}
237+
238+
TEST(SQLGetEnvAttr, TestSQLGetEnvAttrNullValuePointer) {
239+
// ODBC Environment
240+
SQLHENV env;
241+
242+
// Allocate an environment handle
243+
SQLRETURN return_env = SQLAllocEnv(&env);
244+
245+
EXPECT_TRUE(return_env == SQL_SUCCESS);
246+
247+
SQLRETURN return_get = SQLGetEnvAttr(env, SQL_ATTR_ODBC_VERSION, nullptr, 0, nullptr);
248+
249+
EXPECT_TRUE(return_get == SQL_ERROR);
250+
}
251+
252+
TEST(SQLSetEnvAttr, TestSQLSetEnvAttrOutputNTSValid) {
253+
// ODBC Environment
254+
SQLHENV env;
255+
256+
// Allocate an environment handle
257+
SQLRETURN return_env = SQLAllocEnv(&env);
258+
259+
EXPECT_TRUE(return_env == SQL_SUCCESS);
260+
261+
// Attempt to set to output nts to supported version
262+
SQLRETURN return_set =
263+
SQLSetEnvAttr(env, SQL_ATTR_OUTPUT_NTS, reinterpret_cast<void*>(SQL_TRUE), 0);
264+
265+
EXPECT_TRUE(return_set == SQL_SUCCESS);
266+
}
267+
268+
TEST(SQLSetEnvAttr, TestSQLSetEnvAttrOutputNTSInvalid) {
269+
// ODBC Environment
270+
SQLHENV env;
271+
272+
// Allocate an environment handle
273+
SQLRETURN return_env = SQLAllocEnv(&env);
274+
275+
EXPECT_TRUE(return_env == SQL_SUCCESS);
276+
277+
// Attempt to set to output nts to unsupported false
278+
SQLRETURN return_set =
279+
SQLSetEnvAttr(env, SQL_ATTR_OUTPUT_NTS, reinterpret_cast<void*>(SQL_FALSE), 0);
280+
281+
EXPECT_TRUE(return_set == SQL_ERROR);
282+
}
283+
284+
TEST(SQLSetEnvAttr, TestSQLSetEnvAttrBadEnv) {
285+
// Attempt to set using bad environment pointer
286+
SQLRETURN return_set =
287+
SQLSetEnvAttr(nullptr, SQL_ATTR_ODBC_VERSION, reinterpret_cast<void*>(SQL_OV_ODBC2), 0);
288+
289+
EXPECT_TRUE(return_set == SQL_ERROR);
290+
}
291+
292+
TEST(SQLSetEnvAttr, TestSQLSetEnvAttrNullValuePointer) {
293+
// ODBC Environment
294+
SQLHENV env;
295+
296+
// Allocate an environment handle
297+
SQLRETURN return_env = SQLAllocEnv(&env);
298+
299+
EXPECT_TRUE(return_env == SQL_SUCCESS);
300+
301+
// Attempt to set using bad data pointer
302+
SQLRETURN return_set =
303+
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, nullptr, 0);
304+
305+
EXPECT_TRUE(return_set == SQL_ERROR);
306+
}
307+
152308
} // namespace integration_tests
153309
} // namespace odbc
154310
} // namespace flight

0 commit comments

Comments
 (0)