fixed DB/2 driver build error
[public/netxms.git] / src / db / dbdrv / db2 / db2.cpp
CommitLineData
84277a0f
AK
1/*
2** DB2 Database Driver
d40da24a 3** Copyright (C) 2010-2016 Raden Solutinos
84277a0f
AK
4**
5** This program is free software; you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation; either version 2 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program; if not, write to the Free Software
17** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18**
19**/
20
ab0028e7
VK
21#define _CRT_SECURE_NO_WARNINGS
22
84277a0f
AK
23#include "db2drv.h"
24
b6f7029f
VK
25DECLARE_DRIVER_HEADER("DB2")
26
ab0028e7
VK
27/**
28 * Convert DB2 state to NetXMS database error code and get error text
29 */
465b3f2d 30static DWORD GetSQLErrorInfo(SQLSMALLINT nHandleType, SQLHANDLE hHandle, NETXMS_WCHAR *errorText)
84277a0f 31{
e2a7ccac
AK
32 SQLRETURN nRet;
33 SQLSMALLINT nChars;
34 DWORD dwError;
e83d6e8f 35 SQLWCHAR buffer[DBDRV_MAX_ERROR_TEXT];
84277a0f
AK
36
37 // Get state information and convert it to NetXMS database error code
e2a7ccac
AK
38 nRet = SQLGetDiagFieldW(nHandleType, hHandle, 1, SQL_DIAG_SQLSTATE, buffer, 16, &nChars);
39 if (nRet == SQL_SUCCESS)
40 {
b8640ef7 41#if UNICODE_UCS2
e2a7ccac 42 if (
ab0028e7
VK
43 (!wcscmp((WCHAR *)buffer, L"08003")) || // Connection does not exist
44 (!wcscmp((WCHAR *)buffer, L"08S01")) || // Communication link failure
45 (!wcscmp((WCHAR *)buffer, L"HYT00")) || // Timeout expired
46 (!wcscmp((WCHAR *)buffer, L"HYT01")) || // Connection timeout expired
47 (!wcscmp((WCHAR *)buffer, L"08506"))) // SQL30108N: A connection failed but has been re-established.
e83d6e8f 48#else
b8640ef7
VK
49 char state[16];
50 ucs2_to_mb(buffer, -1, state, 16);
e2a7ccac
AK
51 if (
52 (!strcmp(state, "08003")) || // Connection does not exist
53 (!strcmp(state, "08S01")) || // Communication link failure
54 (!strcmp(state, "HYT00")) || // Timeout expired
55 (!strcmp(state, "HYT01")) || // Connection timeout expired
56 (!strcmp(state, "08506"))) // SQL30108N: A connection failed but has been re-established.
e83d6e8f 57#endif
e2a7ccac
AK
58 {
59 dwError = DBERR_CONNECTION_LOST;
60 }
61 else
62 {
63 dwError = DBERR_OTHER_ERROR;
64 }
65 }
66 else
67 {
68 dwError = DBERR_OTHER_ERROR;
69 }
84277a0f
AK
70
71 // Get error message
72 if (errorText != NULL)
73 {
465b3f2d
VK
74#if UNICODE_UCS2
75 nRet = SQLGetDiagFieldW(nHandleType, hHandle, 1, SQL_DIAG_MESSAGE_TEXT, errorText, DBDRV_MAX_ERROR_TEXT, &nChars);
76#else
c66d91ab 77 nRet = SQLGetDiagFieldW(nHandleType, hHandle, 1, SQL_DIAG_MESSAGE_TEXT, buffer, DBDRV_MAX_ERROR_TEXT, &nChars);
465b3f2d 78#endif
84277a0f
AK
79 if (nRet == SQL_SUCCESS)
80 {
465b3f2d 81#if UNICODE_UCS4
c66d91ab 82 buffer[DBDRV_MAX_ERROR_TEXT - 1] = 0;
465b3f2d 83 ucs2_to_ucs4(buffer, -1, errorText, DBDRV_MAX_ERROR_TEXT);
e83d6e8f 84#endif
6e5eae36 85 RemoveTrailingCRLFW(errorText);
84277a0f
AK
86 }
87 else
88 {
465b3f2d 89 wcscpy(errorText, L"Unable to obtain description for this error");
84277a0f 90 }
e2a7ccac
AK
91 }
92
93 return dwError;
84277a0f
AK
94}
95
750d59f2
VK
96/**
97 * Clear any pending result sets on given statement
98 */
4f894509
AK
99static void ClearPendingResults(SQLHSTMT statement)
100{
101 while(1)
102 {
103 SQLRETURN rc = SQLMoreResults(statement);
104 if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO))
105 {
106 break;
107 }
108 }
109}
110
750d59f2
VK
111/**
112 * Prepare string for using in SQL query - enclose in quotes and escape as needed
113 */
465b3f2d
VK
114extern "C" WCHAR EXPORT *DrvPrepareStringW(const WCHAR *str)
115{
116 int len = (int)wcslen(str) + 3; // + two quotes and \0 at the end
117 int bufferSize = len + 128;
118 WCHAR *out = (WCHAR *)malloc(bufferSize * sizeof(WCHAR));
119 out[0] = L'\'';
120
121 const WCHAR *src = str;
122 int outPos;
6e5eae36 123 for(outPos = 1; *src != 0; src++)
465b3f2d
VK
124 {
125 if (*src == L'\'')
126 {
127 len++;
128 if (len >= bufferSize)
129 {
130 bufferSize += 128;
131 out = (WCHAR *)realloc(out, bufferSize * sizeof(WCHAR));
132 }
133 out[outPos++] = L'\'';
134 out[outPos++] = L'\'';
135 }
136 else
137 {
138 out[outPos++] = *src;
139 }
140 }
141 out[outPos++] = L'\'';
142 out[outPos++] = 0;
143
144 return out;
145}
146
147extern "C" char EXPORT *DrvPrepareStringA(const char *str)
84277a0f 148{
465b3f2d 149 int len = (int)strlen(str) + 3; // + two quotes and \0 at the end
84277a0f 150 int bufferSize = len + 128;
465b3f2d
VK
151 char *out = (char *)malloc(bufferSize);
152 out[0] = '\'';
84277a0f 153
c66d91ab 154 const char *src = str;
84277a0f 155 int outPos;
6e5eae36 156 for(outPos = 1; *src != 0; src++)
84277a0f 157 {
465b3f2d 158 if (*src == '\'')
84277a0f
AK
159 {
160 len++;
161 if (len >= bufferSize)
162 {
163 bufferSize += 128;
465b3f2d 164 out = (char *)realloc(out, bufferSize);
84277a0f 165 }
465b3f2d
VK
166 out[outPos++] = '\'';
167 out[outPos++] = '\'';
84277a0f
AK
168 }
169 else
170 {
171 out[outPos++] = *src;
172 }
173 }
465b3f2d 174 out[outPos++] = '\'';
84277a0f
AK
175 out[outPos++] = 0;
176
177 return out;
178}
179
750d59f2
VK
180/**
181 * Initialize driver
182 */
2df047f4 183extern "C" bool EXPORT DrvInit(const char *cmdLine)
84277a0f 184{
750d59f2 185 return true;
84277a0f
AK
186}
187
750d59f2
VK
188/**
189 * Unload handler
190 */
c66d91ab 191extern "C" void EXPORT DrvUnload()
84277a0f
AK
192{
193}
194
750d59f2
VK
195/**
196 * Connect to database
197 * pszHost should be set to DB2 source name, and pszDatabase is ignored
198 */
84277a0f 199extern "C" DBDRV_CONNECTION EXPORT DrvConnect(char *pszHost, char *pszLogin,
f3c30cf5 200 char *pszPassword, char *pszDatabase, const char *schema, NETXMS_WCHAR *errorText)
84277a0f 201{
e2a7ccac
AK
202 long iResult;
203 DB2DRV_CONN *pConn;
481bae7b 204 SQLHSTMT sqlStatement;
84277a0f 205
e2a7ccac
AK
206 // Allocate our connection structure
207 pConn = (DB2DRV_CONN *)malloc(sizeof(DB2DRV_CONN));
84277a0f 208
e2a7ccac
AK
209 // Allocate environment
210 iResult = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &pConn->sqlEnv);
84277a0f 211 if ((iResult != SQL_SUCCESS) && (iResult != SQL_SUCCESS_WITH_INFO))
e2a7ccac
AK
212 {
213 wcscpy(errorText, L"Cannot allocate environment handle");
214 goto connect_failure_0;
215 }
84277a0f 216
e2a7ccac 217 // Set required DB2 version
84277a0f
AK
218 iResult = SQLSetEnvAttr(pConn->sqlEnv, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
219 if ((iResult != SQL_SUCCESS) && (iResult != SQL_SUCCESS_WITH_INFO))
e2a7ccac
AK
220 {
221 wcscpy(errorText, L"Call to SQLSetEnvAttr failed");
222 goto connect_failure_1;
223 }
84277a0f
AK
224
225 // Allocate connection handle, set timeout
226 iResult = SQLAllocHandle(SQL_HANDLE_DBC, pConn->sqlEnv, &pConn->sqlConn);
227 if ((iResult != SQL_SUCCESS) && (iResult != SQL_SUCCESS_WITH_INFO))
e2a7ccac
AK
228 {
229 wcscpy(errorText, L"Cannot allocate connection handle");
230 goto connect_failure_1;
231 }
84277a0f
AK
232 SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER *)15, 0);
233 SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *)30, 0);
234
235 // Connect to the datasource
3b74985b 236 iResult = SQLConnect(pConn->sqlConn, (SQLCHAR *)pszHost, SQL_NTS, (SQLCHAR *)pszLogin, SQL_NTS, (SQLCHAR *)pszPassword, SQL_NTS);
84277a0f 237 if ((iResult != SQL_SUCCESS) && (iResult != SQL_SUCCESS_WITH_INFO))
e2a7ccac
AK
238 {
239 GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
240 goto connect_failure_2;
241 }
84277a0f 242
f3c30cf5
VK
243 // Set current schema
244 if ((schema != NULL) && (schema[0] != 0))
245 {
246 char query[256];
247 snprintf(query, 256, "SET CURRENT SCHEMA = '%s'", schema);
248
3b74985b 249 iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &sqlStatement);
f3c30cf5
VK
250 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
251 {
252 // Execute statement
ab0028e7 253 SQLWCHAR *temp = (SQLWCHAR *)UCS2StringFromMBString(query);
3b74985b 254 iResult = SQLExecDirectW(sqlStatement, temp, SQL_NTS);
f3c30cf5
VK
255 free(temp);
256 if ((iResult != SQL_SUCCESS) &&
257 (iResult != SQL_SUCCESS_WITH_INFO) &&
258 (iResult != SQL_NO_DATA))
259 {
3b74985b 260 GetSQLErrorInfo(SQL_HANDLE_STMT, sqlStatement, errorText);
f3c30cf5
VK
261 goto connect_failure_3;
262 }
3b74985b 263 SQLFreeHandle(SQL_HANDLE_STMT, sqlStatement);
f3c30cf5
VK
264 }
265 else
266 {
267 GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
268 goto connect_failure_2;
269 }
270 }
271
e2a7ccac
AK
272 // Create mutex
273 pConn->mutexQuery = MutexCreate();
84277a0f 274
e2a7ccac
AK
275 // Success
276 return (DBDRV_CONNECTION)pConn;
84277a0f 277
e2a7ccac 278 // Handle failures
f3c30cf5 279connect_failure_3:
3b74985b 280 SQLFreeHandle(SQL_HANDLE_STMT, sqlStatement);
f3c30cf5 281
84277a0f 282connect_failure_2:
e2a7ccac 283 SQLFreeHandle(SQL_HANDLE_DBC, pConn->sqlConn);
84277a0f
AK
284
285connect_failure_1:
e2a7ccac 286 SQLFreeHandle(SQL_HANDLE_ENV, pConn->sqlEnv);
84277a0f
AK
287
288connect_failure_0:
e2a7ccac
AK
289 free(pConn);
290 return NULL;
84277a0f
AK
291}
292
750d59f2
VK
293/**
294 * Disconnect from database
295 */
e83d6e8f 296extern "C" void EXPORT DrvDisconnect(DB2DRV_CONN *pConn)
84277a0f 297{
c17f6cbc 298 MutexLock(pConn->mutexQuery);
e2a7ccac
AK
299 MutexUnlock(pConn->mutexQuery);
300 SQLDisconnect(pConn->sqlConn);
301 SQLFreeHandle(SQL_HANDLE_DBC, pConn->sqlConn);
302 SQLFreeHandle(SQL_HANDLE_ENV, pConn->sqlEnv);
303 MutexDestroy(pConn->mutexQuery);
304 free(pConn);
84277a0f
AK
305}
306
750d59f2
VK
307/**
308 * Prepare statement
309 */
de1d708f 310extern "C" DBDRV_STATEMENT EXPORT DrvPrepare(DB2DRV_CONN *pConn, NETXMS_WCHAR *pwszQuery, DWORD *pdwError, NETXMS_WCHAR *errorText)
4f894509
AK
311{
312 long iResult;
313 SQLHSTMT statement;
314 DB2DRV_STATEMENT *result;
315
c17f6cbc 316 MutexLock(pConn->mutexQuery);
4f894509
AK
317
318 // Allocate statement handle
319 iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &statement);
320 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
321 {
322 // Prepare statement
323#if defined(_WIN32) || defined(UNICODE_UCS2)
324 iResult = SQLPrepareW(statement, (SQLWCHAR *)pwszQuery, SQL_NTS);
325#else
326 SQLWCHAR *temp = UCS2StringFromUCS4String(pwszQuery);
327 iResult = SQLPrepareW(statement, temp, SQL_NTS);
328 free(temp);
329#endif
330 if ((iResult == SQL_SUCCESS) ||
331 (iResult == SQL_SUCCESS_WITH_INFO))
332 {
333 result = (DB2DRV_STATEMENT *)malloc(sizeof(DB2DRV_STATEMENT));
334 result->handle = statement;
335 result->buffers = new Array(0, 16, true);
336 result->connection = pConn;
de1d708f 337 *pdwError = DBERR_SUCCESS;
4f894509
AK
338 }
339 else
340 {
de1d708f 341 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, statement, errorText);
4f894509
AK
342 SQLFreeHandle(SQL_HANDLE_STMT, statement);
343 result = NULL;
344 }
345 }
346 else
347 {
de1d708f 348 *pdwError = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
4f894509
AK
349 result = NULL;
350 }
351
352 MutexUnlock(pConn->mutexQuery);
353 return result;
354}
355
356
357//
358// Bind parameter to statement
359//
360
361extern "C" void EXPORT DrvBind(DB2DRV_STATEMENT *statement, int pos, int sqlType, int cType, void *buffer, int allocType)
362{
cd3cdf46 363 static SQLSMALLINT odbcSqlType[] = { SQL_VARCHAR, SQL_INTEGER, SQL_BIGINT, SQL_DOUBLE, SQL_LONGVARCHAR };
b8849590
VK
364 static SQLSMALLINT odbcCType[] = { SQL_C_WCHAR, SQL_C_SLONG, SQL_C_ULONG, SQL_C_SBIGINT, SQL_C_UBIGINT, SQL_C_DOUBLE, SQL_C_WCHAR };
365 static DWORD bufferSize[] = { 0, sizeof(LONG), sizeof(DWORD), sizeof(INT64), sizeof(QWORD), sizeof(double), 0 };
4f894509 366
b8849590 367 int length = (cType == DB_CTYPE_STRING) ? (int)wcslen((WCHAR *)buffer) + 1 : 0;
4f894509 368
4f894509
AK
369 SQLPOINTER sqlBuffer;
370 switch(allocType)
371 {
372 case DB_BIND_STATIC:
373#if defined(_WIN32) || defined(UNICODE_UCS2)
b8849590
VK
374 if (cType == DB_CTYPE_UTF8_STRING)
375 {
376 sqlBuffer = WideStringFromUTF8String((char *)buffer);
377 statement->buffers->add(sqlBuffer);
688b5dc2 378 length = (int)strlen((char *)sqlBuffer) + 1;
b8849590
VK
379 }
380 else
381 {
382 sqlBuffer = buffer;
383 }
4f894509
AK
384#else
385 if (cType == DB_CTYPE_STRING)
386 {
387 sqlBuffer = UCS2StringFromUCS4String((WCHAR *)buffer);
388 statement->buffers->add(sqlBuffer);
389 }
b8849590
VK
390 else if (cType == DB_CTYPE_UTF8_STRING)
391 {
392 sqlBuffer = UCS2StringFromUTF8String((char *)buffer);
393 statement->buffers->add(sqlBuffer);
688b5dc2 394 length = (int)strlen((char *)sqlBuffer) + 1;
b8849590 395 }
4f894509
AK
396 else
397 {
398 sqlBuffer = buffer;
399 }
400#endif
401 break;
402 case DB_BIND_DYNAMIC:
403#if defined(_WIN32) || defined(UNICODE_UCS2)
b8849590
VK
404 if (cType == DB_CTYPE_UTF8_STRING)
405 {
406 sqlBuffer = WideStringFromUTF8String((char *)buffer);
407 free(buffer);
688b5dc2 408 length = (int)strlen((char *)sqlBuffer) + 1;
b8849590
VK
409 }
410 else
411 {
412 sqlBuffer = buffer;
413 }
4f894509
AK
414#else
415 if (cType == DB_CTYPE_STRING)
416 {
417 sqlBuffer = UCS2StringFromUCS4String((WCHAR *)buffer);
418 free(buffer);
419 }
b8849590
VK
420 else if (cType == DB_CTYPE_UTF8_STRING)
421 {
422 sqlBuffer = UCS2StringFromUTF8String((char *)buffer);
423 free(buffer);
688b5dc2 424 length = (int)strlen((char *)sqlBuffer) + 1;
b8849590 425 }
4f894509
AK
426 else
427 {
428 sqlBuffer = buffer;
429 }
430#endif
431 statement->buffers->add(sqlBuffer);
432 break;
433 case DB_BIND_TRANSIENT:
434#if defined(_WIN32) || defined(UNICODE_UCS2)
b8849590
VK
435 if (cType == DB_CTYPE_UTF8_STRING)
436 {
437 sqlBuffer = WideStringFromUTF8String((char *)buffer);
688b5dc2 438 length = (int)strlen((char *)sqlBuffer) + 1;
b8849590
VK
439 }
440 else
441 {
442 sqlBuffer = nx_memdup(buffer, (cType == DB_CTYPE_STRING) ? (DWORD)(length * sizeof(WCHAR)) : bufferSize[cType]);
443 }
4f894509
AK
444#else
445 if (cType == DB_CTYPE_STRING)
446 {
447 sqlBuffer = UCS2StringFromUCS4String((WCHAR *)buffer);
448 }
b8849590
VK
449 else if (cType == DB_CTYPE_UTF8_STRING)
450 {
451 sqlBuffer = UCS2StringFromUTF8String((char *)buffer);
688b5dc2 452 length = (int)strlen((char *)sqlBuffer) + 1;
b8849590 453 }
4f894509
AK
454 else
455 {
456 sqlBuffer = nx_memdup(buffer, bufferSize[cType]);
457 }
458#endif
459 statement->buffers->add(sqlBuffer);
460 break;
461 default:
462 return; // Invalid call
463 }
464 SQLBindParameter(statement->handle, pos, SQL_PARAM_INPUT, odbcCType[cType], odbcSqlType[sqlType],
b8849590 465 ((cType == DB_CTYPE_STRING) || (cType == DB_CTYPE_UTF8_STRING)) ? length : 0, 0, sqlBuffer, 0, NULL);
4f894509
AK
466}
467
688b5dc2
VK
468/**
469 * Execute prepared statement
470 */
4f894509
AK
471extern "C" DWORD EXPORT DrvExecute(DB2DRV_CONN *pConn, DB2DRV_STATEMENT *statement, NETXMS_WCHAR *errorText)
472{
473 DWORD dwResult;
474
c17f6cbc 475 MutexLock(pConn->mutexQuery);
4f894509
AK
476 long rc = SQLExecute(statement->handle);
477 if (
478 (rc == SQL_SUCCESS) ||
479 (rc == SQL_SUCCESS_WITH_INFO) ||
480 (rc == SQL_NO_DATA))
481 {
482 ClearPendingResults(statement->handle);
483 dwResult = DBERR_SUCCESS;
484 }
485 else
486 {
487 dwResult = GetSQLErrorInfo(SQL_HANDLE_STMT, statement->handle, errorText);
488 }
489 MutexUnlock(pConn->mutexQuery);
490 return dwResult;
491}
492
493
494//
495// Destroy prepared statement
496//
497
498extern "C" void EXPORT DrvFreeStatement(DB2DRV_STATEMENT *statement)
499{
500 if (statement == NULL)
501 {
502 return;
503 }
504
c17f6cbc 505 MutexLock(statement->connection->mutexQuery);
4f894509
AK
506 SQLFreeHandle(SQL_HANDLE_STMT, statement->handle);
507 MutexUnlock(statement->connection->mutexQuery);
508 delete statement->buffers;
509 free(statement);
510}
511
3b74985b
VK
512/**
513 * Perform non-SELECT query
514 */
465b3f2d 515extern "C" DWORD EXPORT DrvQuery(DB2DRV_CONN *pConn, NETXMS_WCHAR *pwszQuery, NETXMS_WCHAR *errorText)
84277a0f 516{
e2a7ccac
AK
517 long iResult;
518 DWORD dwResult;
84277a0f 519
c17f6cbc 520 MutexLock(pConn->mutexQuery);
84277a0f 521
e2a7ccac 522 // Allocate statement handle
3b74985b
VK
523 SQLHSTMT sqlStatement;
524 iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &sqlStatement);
84277a0f 525 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
e2a7ccac
AK
526 {
527 // Execute statement
84277a0f 528#if defined(_WIN32) || defined(UNICODE_UCS2)
3b74985b 529 iResult = SQLExecDirectW(sqlStatement, (SQLWCHAR *)pwszQuery, SQL_NTS);
84277a0f 530#else
e83d6e8f 531 SQLWCHAR *temp = UCS2StringFromUCS4String(pwszQuery);
3b74985b 532 iResult = SQLExecDirectW(sqlStatement, temp, SQL_NTS);
e2a7ccac 533 free(temp);
84277a0f 534#endif
3b74985b 535 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO) || (iResult == SQL_NO_DATA))
e2a7ccac
AK
536 {
537 dwResult = DBERR_SUCCESS;
538 }
539 else
540 {
3b74985b 541 dwResult = GetSQLErrorInfo(SQL_HANDLE_STMT, sqlStatement, errorText);
e2a7ccac 542 }
3b74985b 543 SQLFreeHandle(SQL_HANDLE_STMT, sqlStatement);
e2a7ccac
AK
544 }
545 else
546 {
547 dwResult = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
548 }
549
550 MutexUnlock(pConn->mutexQuery);
551 return dwResult;
84277a0f
AK
552}
553
d40da24a
VK
554/**
555 * Get complete field data
556 */
557static NETXMS_WCHAR *GetFieldData(SQLHSTMT sqlStatement, short column)
558{
559 NETXMS_WCHAR *result = NULL;
560 SQLINTEGER dataSize;
561#if defined(_WIN32) || defined(UNICODE_UCS2)
562 WCHAR buffer[256];
563 SQLRETURN rc = SQLGetData(sqlStatement, column, SQL_C_WCHAR, buffer, sizeof(buffer), &dataSize);
564 if (((rc == SQL_SUCCESS) || ((rc == SQL_SUCCESS_WITH_INFO) && (dataSize >= 0) && (dataSize <= (SQLLEN)(sizeof(buffer) - sizeof(WCHAR))))) && (dataSize != SQL_NULL_DATA))
565 {
566 result = wcsdup(buffer);
567 }
568 else if ((rc == SQL_SUCCESS_WITH_INFO) && (dataSize != SQL_NULL_DATA))
569 {
570 if (dataSize > (SQLLEN)(sizeof(buffer) - sizeof(WCHAR)))
571 {
572 WCHAR *temp = (WCHAR *)malloc(dataSize + sizeof(WCHAR));
573 memcpy(temp, buffer, sizeof(buffer));
574 rc = SQLGetData(sqlStatement, column, SQL_C_WCHAR, &temp[255], dataSize - 254 * sizeof(WCHAR), &dataSize);
575 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
576 {
577 result = temp;
578 }
579 else
580 {
581 free(temp);
582 }
583 }
584 else if (dataSize == SQL_NO_TOTAL)
585 {
25d450c8 586 size_t tempSize = sizeof(buffer) * 4; // temporary buffer size in bytes
d40da24a
VK
587 WCHAR *temp = (WCHAR *)malloc(tempSize);
588 memcpy(temp, buffer, sizeof(buffer));
25d450c8 589 size_t offset = sizeof(buffer) - sizeof(WCHAR); // offset in buffer in bytes
d40da24a
VK
590 while(true)
591 {
592 SQLINTEGER readSize = (SQLINTEGER)(tempSize - offset);
25d450c8 593 rc = SQLGetData(sqlStatement, column, SQL_C_WCHAR, (char *)temp + offset, readSize, &dataSize);
d40da24a
VK
594 if ((rc == SQL_SUCCESS) || (rc == SQL_NO_DATA))
595 break;
596 if (dataSize == SQL_NO_TOTAL)
597 {
598 tempSize += sizeof(buffer) * 4;
599 }
600 else
601 {
602 tempSize += dataSize - readSize;
603 }
604 temp = (WCHAR *)realloc(temp, tempSize);
25d450c8 605 offset += readSize - sizeof(WCHAR);
d40da24a
VK
606 }
607 result = temp;
608 }
609 }
610#else
611 UCS2CHAR buffer[256];
612 SQLRETURN rc = SQLGetData(sqlStatement, column, SQL_C_WCHAR, buffer, sizeof(buffer), &dataSize);
613 if (((rc == SQL_SUCCESS) || ((rc == SQL_SUCCESS_WITH_INFO) && (dataSize >= 0) && (dataSize <= (SQLLEN)(sizeof(buffer) - sizeof(UCS2CHAR))))) && (dataSize != SQL_NULL_DATA))
614 {
615 int len = ucs2_strlen(buffer);
616 result = (NETXMS_WCHAR *)malloc((len + 1) * sizeof(NETXMS_WCHAR));
481bae7b 617 ucs2_to_ucs4(buffer, -1, result, len + 1);
d40da24a
VK
618 }
619 else if ((rc == SQL_SUCCESS_WITH_INFO) && (dataSize != SQL_NULL_DATA))
620 {
621 if (dataSize > (SQLLEN)(sizeof(buffer) - sizeof(UCS2CHAR)))
622 {
623 UCS2CHAR *temp = (UCS2CHAR *)malloc(dataSize + sizeof(UCS2CHAR));
624 memcpy(temp, buffer, sizeof(buffer));
625 rc = SQLGetData(sqlStatement, column, SQL_C_WCHAR, &temp[255], dataSize - 254 * sizeof(UCS2CHAR), &dataSize);
626 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
627 {
628 int len = ucs2_strlen(temp);
629 result = (NETXMS_WCHAR *)malloc((len + 1) * sizeof(NETXMS_WCHAR));
630 ucs2_to_ucs4(temp, -1, result, len + 1);
631 }
632 free(temp);
633 }
634 else if (dataSize == SQL_NO_TOTAL)
635 {
636 size_t tempSize = sizeof(buffer) * 4;
637 UCS2CHAR *temp = (UCS2CHAR *)malloc(tempSize);
638 memcpy(temp, buffer, sizeof(buffer));
25d450c8 639 size_t offset = sizeof(buffer) - sizeof(UCS2CHAR);
d40da24a
VK
640 while(true)
641 {
642 SQLLEN readSize = tempSize - offset;
25d450c8 643 rc = SQLGetData(sqlStatement, column, SQL_C_WCHAR, (char *)temp + offset, readSize, &dataSize);
d40da24a
VK
644 if ((rc == SQL_SUCCESS) || (rc == SQL_NO_DATA))
645 break;
646 if (dataSize == SQL_NO_TOTAL)
647 {
648 tempSize += sizeof(buffer) * 4;
649 }
650 else
651 {
652 tempSize += dataSize - readSize;
653 }
654 temp = (UCS2CHAR *)realloc(temp, tempSize);
25d450c8 655 offset += readSize - sizeof(UCS2CHAR);
d40da24a
VK
656 }
657 int len = ucs2_strlen(temp);
658 result = (NETXMS_WCHAR *)malloc((len + 1) * sizeof(NETXMS_WCHAR));
659 ucs2_to_ucs4(temp, -1, result, len + 1);
660 free(temp);
661 }
662 }
663#endif
664 return (result != NULL) ? result : wcsdup(L"");
665}
666
3b74985b
VK
667/**
668 * Process results of SELECT query
669 */
4f894509
AK
670static DB2DRV_QUERY_RESULT *ProcessSelectResults(SQLHSTMT statement)
671{
672 // Allocate result buffer and determine column info
673 DB2DRV_QUERY_RESULT *pResult = (DB2DRV_QUERY_RESULT *)malloc(sizeof(DB2DRV_QUERY_RESULT));
674 short wNumCols;
675 SQLNumResultCols(statement, &wNumCols);
3b74985b
VK
676 pResult->numColumns = wNumCols;
677 pResult->numRows = 0;
678 pResult->values = NULL;
4f894509 679
4f894509 680 // Get column names
3b74985b
VK
681 pResult->columnNames = (char **)malloc(sizeof(char *) * pResult->numColumns);
682 for(int i = 0; i < (int)pResult->numColumns; i++)
4f894509
AK
683 {
684 UCS2CHAR name[256];
685 SQLSMALLINT len;
686
687 SQLRETURN iResult = SQLColAttributeW(statement, (SQLSMALLINT)(i + 1), SQL_DESC_NAME, (SQLPOINTER)name, 256, &len, NULL);
3b74985b 688 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
4f894509
AK
689 {
690 name[len] = 0;
691 pResult->columnNames[i] = MBStringFromUCS2String(name);
692 }
693 else
694 {
695 pResult->columnNames[i] = strdup("");
696 }
697 }
698
699 // Fetch all data
700 long iCurrValue = 0;
701 SQLRETURN iResult;
702 while(iResult = SQLFetch(statement), (iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
703 {
3b74985b
VK
704 pResult->numRows++;
705 pResult->values = (NETXMS_WCHAR **)realloc(pResult->values, sizeof(NETXMS_WCHAR *) * (pResult->numRows * pResult->numColumns));
706 for(int i = 1; i <= (int)pResult->numColumns; i++)
4f894509 707 {
d40da24a 708 pResult->values[iCurrValue++] = GetFieldData(statement, (short)i);
4f894509
AK
709 }
710 }
711
4f894509
AK
712 return pResult;
713}
714
3b74985b
VK
715/**
716 * Perform SELECT query
717 */
465b3f2d 718extern "C" DBDRV_RESULT EXPORT DrvSelect(DB2DRV_CONN *pConn, NETXMS_WCHAR *pwszQuery, DWORD *pdwError, NETXMS_WCHAR *errorText)
84277a0f 719{
e2a7ccac 720 DB2DRV_QUERY_RESULT *pResult = NULL;
84277a0f 721
c17f6cbc 722 MutexLock(pConn->mutexQuery);
84277a0f 723
e2a7ccac 724 // Allocate statement handle
3b74985b
VK
725 SQLHSTMT sqlStatement;
726 long iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &sqlStatement);
84277a0f 727 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
e2a7ccac
AK
728 {
729 // Execute statement
84277a0f 730#if defined(_WIN32) || defined(UNICODE_UCS2)
3b74985b 731 iResult = SQLExecDirectW(sqlStatement, (SQLWCHAR *)pwszQuery, SQL_NTS);
84277a0f 732#else
e83d6e8f 733 SQLWCHAR *temp = UCS2StringFromUCS4String(pwszQuery);
3b74985b 734 iResult = SQLExecDirectW(sqlStatement, temp, SQL_NTS);
e2a7ccac 735 free(temp);
84277a0f 736#endif
3b74985b 737 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
e2a7ccac 738 {
3b74985b 739 pResult = ProcessSelectResults(sqlStatement);
e2a7ccac
AK
740 *pdwError = DBERR_SUCCESS;
741 }
742 else
743 {
3b74985b 744 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, sqlStatement, errorText);
e2a7ccac 745 }
3b74985b 746 SQLFreeHandle(SQL_HANDLE_STMT, sqlStatement);
e2a7ccac
AK
747 }
748 else
749 {
750 *pdwError = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
751 }
752
753 MutexUnlock(pConn->mutexQuery);
e2a7ccac 754 return pResult;
84277a0f
AK
755}
756
3b74985b
VK
757/**
758 * Perform SELECT query using prepared statement
759 */
4f894509
AK
760extern "C" DBDRV_RESULT EXPORT DrvSelectPrepared(DB2DRV_CONN *pConn, DB2DRV_STATEMENT *statement, DWORD *pdwError, NETXMS_WCHAR *errorText)
761{
762 DB2DRV_QUERY_RESULT *pResult = NULL;
763
c17f6cbc 764 MutexLock(pConn->mutexQuery);
4f894509 765 long rc = SQLExecute(statement->handle);
3b74985b 766 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
4f894509
AK
767 {
768 pResult = ProcessSelectResults(statement->handle);
769 *pdwError = DBERR_SUCCESS;
770 }
771 else
772 {
773 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, statement->handle, errorText);
774 }
775 MutexUnlock(pConn->mutexQuery);
776 return pResult;
777}
778
3b74985b
VK
779/**
780 * Get field length from result
781 */
84277a0f
AK
782extern "C" LONG EXPORT DrvGetFieldLength(DB2DRV_QUERY_RESULT *pResult, int iRow, int iColumn)
783{
e2a7ccac
AK
784 LONG nLen = -1;
785
786 if (pResult != NULL)
787 {
3b74985b
VK
788 if ((iRow < pResult->numRows) && (iRow >= 0) &&
789 (iColumn < pResult->numColumns) && (iColumn >= 0))
e2a7ccac 790 {
3b74985b 791 nLen = (LONG)wcslen(pResult->values[iRow * pResult->numColumns + iColumn]);
e2a7ccac
AK
792 }
793 }
794 return nLen;
84277a0f
AK
795}
796
3b74985b
VK
797/**
798 * Get field value from result
799 */
800extern "C" NETXMS_WCHAR EXPORT *DrvGetField(DB2DRV_QUERY_RESULT *pResult, int iRow, int iColumn, NETXMS_WCHAR *pBuffer, int nBufSize)
84277a0f 801{
e2a7ccac 802 NETXMS_WCHAR *pValue = NULL;
84277a0f 803
e2a7ccac
AK
804 if (pResult != NULL)
805 {
3b74985b
VK
806 if ((iRow < pResult->numRows) && (iRow >= 0) &&
807 (iColumn < pResult->numColumns) && (iColumn >= 0))
e2a7ccac 808 {
84277a0f 809#ifdef _WIN32
3b74985b 810 wcsncpy_s(pBuffer, nBufSize, pResult->values[iRow * pResult->numColumns + iColumn], _TRUNCATE);
84277a0f 811#else
3b74985b 812 wcsncpy(pBuffer, pResult->values[iRow * pResult->numColumns + iColumn], nBufSize);
e2a7ccac 813 pBuffer[nBufSize - 1] = 0;
84277a0f 814#endif
e2a7ccac
AK
815 pValue = pBuffer;
816 }
817 }
818 return pValue;
84277a0f
AK
819}
820
3b74985b
VK
821/**
822 * Get number of rows in result
823 */
84277a0f
AK
824extern "C" int EXPORT DrvGetNumRows(DB2DRV_QUERY_RESULT *pResult)
825{
3b74985b 826 return (pResult != NULL) ? pResult->numRows : 0;
84277a0f
AK
827}
828
3b74985b
VK
829/**
830 * Get column count in query result
831 */
84277a0f
AK
832extern "C" int EXPORT DrvGetColumnCount(DB2DRV_QUERY_RESULT *pResult)
833{
3b74985b 834 return (pResult != NULL) ? pResult->numColumns : 0;
84277a0f
AK
835}
836
3b74985b
VK
837/**
838 * Get column name in query result
839 */
84277a0f
AK
840extern "C" const char EXPORT *DrvGetColumnName(DB2DRV_QUERY_RESULT *pResult, int column)
841{
3b74985b 842 return ((pResult != NULL) && (column >= 0) && (column < pResult->numColumns)) ? pResult->columnNames[column] : NULL;
84277a0f
AK
843}
844
3b74985b
VK
845/**
846 * Free SELECT results
847 */
84277a0f
AK
848extern "C" void EXPORT DrvFreeResult(DB2DRV_QUERY_RESULT *pResult)
849{
3b74985b
VK
850 if (pResult == NULL)
851 return;
84277a0f 852
3b74985b 853 int i, iNumValues;
84277a0f 854
3b74985b
VK
855 iNumValues = pResult->numColumns * pResult->numRows;
856 for(i = 0; i < iNumValues; i++)
857 {
858 free(pResult->values[i]);
859 }
860 safe_free(pResult->values);
84277a0f 861
3b74985b
VK
862 for(i = 0; i < pResult->numColumns; i++)
863 {
864 free(pResult->columnNames[i]);
e2a7ccac 865 }
3b74985b
VK
866 free(pResult->columnNames);
867 free(pResult);
84277a0f
AK
868}
869
3b74985b
VK
870/**
871 * Perform unbuffered SELECT query
872 */
873extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectUnbuffered(DB2DRV_CONN *pConn, NETXMS_WCHAR *pwszQuery, DWORD *pdwError, NETXMS_WCHAR *errorText)
84277a0f 874{
3b74985b 875 DB2DRV_UNBUFFERED_QUERY_RESULT *pResult = NULL;
84277a0f 876
c17f6cbc 877 MutexLock(pConn->mutexQuery);
84277a0f 878
e2a7ccac 879 // Allocate statement handle
3b74985b
VK
880 SQLHSTMT sqlStatement;
881 SQLRETURN iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &sqlStatement);
84277a0f 882 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
e2a7ccac
AK
883 {
884 // Execute statement
84277a0f 885#if defined(_WIN32) || defined(UNICODE_UCS2)
3b74985b 886 iResult = SQLExecDirectW(sqlStatement, (SQLWCHAR *)pwszQuery, SQL_NTS);
84277a0f 887#else
e83d6e8f 888 SQLWCHAR *temp = UCS2StringFromUCS4String(pwszQuery);
3b74985b 889 iResult = SQLExecDirectW(sqlStatement, temp, SQL_NTS);
e2a7ccac 890 free(temp);
84277a0f 891#endif
e2a7ccac
AK
892 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
893 {
894 // Allocate result buffer and determine column info
3b74985b
VK
895 pResult = (DB2DRV_UNBUFFERED_QUERY_RESULT *)malloc(sizeof(DB2DRV_UNBUFFERED_QUERY_RESULT));
896 pResult->sqlStatement = sqlStatement;
897 pResult->isPrepared = false;
898
899 short wNumCols;
900 SQLNumResultCols(sqlStatement, &wNumCols);
901 pResult->numColumns = wNumCols;
e2a7ccac 902 pResult->pConn = pConn;
750d59f2 903 pResult->noMoreRows = false;
84277a0f
AK
904
905 // Get column names
3b74985b
VK
906 pResult->columnNames = (char **)malloc(sizeof(char *) * pResult->numColumns);
907 for(int i = 0; i < pResult->numColumns; i++)
84277a0f 908 {
b8640ef7 909 SQLWCHAR name[256];
84277a0f
AK
910 SQLSMALLINT len;
911
3b74985b
VK
912 iResult = SQLColAttributeW(sqlStatement, (SQLSMALLINT)(i + 1), SQL_DESC_NAME, name, 256, &len, NULL);
913 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
84277a0f
AK
914 {
915 name[len] = 0;
ab0028e7 916 pResult->columnNames[i] = MBStringFromUCS2String((UCS2CHAR *)name);
84277a0f
AK
917 }
918 else
919 {
920 pResult->columnNames[i] = strdup("");
921 }
922 }
923
924 *pdwError = DBERR_SUCCESS;
e2a7ccac
AK
925 }
926 else
927 {
3b74985b 928 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, sqlStatement, errorText);
e2a7ccac 929 // Free statement handle if query failed
3b74985b 930 SQLFreeHandle(SQL_HANDLE_STMT, sqlStatement);
e2a7ccac
AK
931 }
932 }
933 else
934 {
935 *pdwError = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
936 }
937
938 if (pResult == NULL) // Unlock mutex if query has failed
939 {
940 MutexUnlock(pConn->mutexQuery);
941 }
942 return pResult;
84277a0f
AK
943}
944
3b74985b
VK
945/**
946 * Perform unbuffered SELECT query using prepared statement
947 */
948extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectPreparedUnbuffered(DB2DRV_CONN *pConn, DB2DRV_STATEMENT *stmt, DWORD *pdwError, WCHAR *errorText)
949{
950 DB2DRV_UNBUFFERED_QUERY_RESULT *pResult = NULL;
951
952 MutexLock(pConn->mutexQuery);
953 SQLRETURN rc = SQLExecute(stmt->handle);
954 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
955 {
956 // Allocate result buffer and determine column info
957 pResult = (DB2DRV_UNBUFFERED_QUERY_RESULT *)malloc(sizeof(DB2DRV_UNBUFFERED_QUERY_RESULT));
958 pResult->sqlStatement = stmt->handle;
959 pResult->isPrepared = true;
960
961 short wNumCols;
962 SQLNumResultCols(pResult->sqlStatement, &wNumCols);
963 pResult->numColumns = wNumCols;
964 pResult->pConn = pConn;
965 pResult->noMoreRows = false;
966
967 // Get column names
968 pResult->columnNames = (char **)malloc(sizeof(char *) * pResult->numColumns);
969 for(int i = 0; i < pResult->numColumns; i++)
970 {
971 SQLWCHAR name[256];
972 SQLSMALLINT len;
973
974 rc = SQLColAttributeW(pResult->sqlStatement, (SQLSMALLINT)(i + 1), SQL_DESC_NAME, name, 256, &len, NULL);
975 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
976 {
977 name[len] = 0;
978 pResult->columnNames[i] = MBStringFromUCS2String((UCS2CHAR *)name);
979 }
980 else
981 {
982 pResult->columnNames[i] = strdup("");
983 }
984 }
985
986 *pdwError = DBERR_SUCCESS;
987 }
988 else
989 {
990 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, stmt->handle, errorText);
991 }
992
993 if (pResult == NULL) // Unlock mutex if query has failed
994 MutexUnlock(pConn->mutexQuery);
995 return pResult;
996}
997
750d59f2
VK
998/**
999 * Fetch next result line from asynchronous SELECT results
1000 */
3b74985b 1001extern "C" bool EXPORT DrvFetch(DB2DRV_UNBUFFERED_QUERY_RESULT *pResult)
84277a0f 1002{
750d59f2 1003 bool bResult = false;
e2a7ccac
AK
1004
1005 if (pResult != NULL)
1006 {
1007 long iResult;
1008
3b74985b 1009 iResult = SQLFetch(pResult->sqlStatement);
e2a7ccac
AK
1010 bResult = ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO));
1011 if (!bResult)
1012 {
750d59f2 1013 pResult->noMoreRows = true;
e2a7ccac
AK
1014 }
1015 }
1016 return bResult;
84277a0f
AK
1017}
1018
750d59f2 1019/**
3b74985b 1020 * Get field length from unbuffered query result
750d59f2 1021 */
3b74985b 1022extern "C" LONG EXPORT DrvGetFieldLengthUnbuffered(DB2DRV_UNBUFFERED_QUERY_RESULT *pResult, int iColumn)
84277a0f 1023{
e2a7ccac 1024 LONG nLen = -1;
84277a0f 1025
e2a7ccac
AK
1026 if (pResult != NULL)
1027 {
3b74985b 1028 if ((iColumn < pResult->numColumns) && (iColumn >= 0))
84277a0f
AK
1029 {
1030 SQLLEN dataSize;
1031 char temp[1];
3b74985b 1032 long rc = SQLGetData(pResult->sqlStatement, (short)iColumn + 1, SQL_C_CHAR, temp, 0, &dataSize);
84277a0f
AK
1033 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
1034 {
1035 nLen = (LONG)dataSize;
1036 }
1037 }
e2a7ccac
AK
1038 }
1039 return nLen;
84277a0f
AK
1040}
1041
3b74985b
VK
1042/**
1043 * Get field from current row in unbuffered query result
1044 */
1045extern "C" NETXMS_WCHAR EXPORT *DrvGetFieldUnbuffered(DB2DRV_UNBUFFERED_QUERY_RESULT *pResult, int iColumn, NETXMS_WCHAR *pBuffer, int iBufSize)
84277a0f 1046{
e2a7ccac
AK
1047 SQLLEN iDataSize;
1048 long iResult;
84277a0f 1049
e2a7ccac
AK
1050 // Check if we have valid result handle
1051 if (pResult == NULL)
1052 {
1053 return NULL;
1054 }
84277a0f 1055
e2a7ccac 1056 // Check if there are valid fetched row
750d59f2 1057 if (pResult->noMoreRows)
e2a7ccac
AK
1058 {
1059 return NULL;
1060 }
84277a0f 1061
3b74985b 1062 if ((iColumn >= 0) && (iColumn < pResult->numColumns))
e2a7ccac 1063 {
84277a0f 1064#if defined(_WIN32) || defined(UNICODE_UCS2)
3b74985b 1065 iResult = SQLGetData(pResult->sqlStatement, (short)iColumn + 1, SQL_C_WCHAR,
e2a7ccac 1066 pBuffer, iBufSize * sizeof(WCHAR), &iDataSize);
84277a0f 1067#else
e83d6e8f 1068 SQLWCHAR *tempBuff = (SQLWCHAR *)malloc(iBufSize * sizeof(SQLWCHAR));
3b74985b 1069 iResult = SQLGetData(pResult->sqlStatement, (short)iColumn + 1, SQL_C_WCHAR,
e2a7ccac
AK
1070 tempBuff, iBufSize * sizeof(SQLWCHAR), &iDataSize);
1071 ucs2_to_ucs4(tempBuff, -1, pBuffer, iBufSize);
1072 pBuffer[iBufSize - 1] = 0;
1073 free(tempBuff);
84277a0f 1074#endif
e2a7ccac
AK
1075 if (((iResult != SQL_SUCCESS) && (iResult != SQL_SUCCESS_WITH_INFO)) || (iDataSize == SQL_NULL_DATA))
1076 {
1077 pBuffer[0] = 0;
1078 }
1079 }
1080 else
1081 {
1082 pBuffer[0] = 0;
1083 }
1084 return pBuffer;
84277a0f
AK
1085}
1086
750d59f2
VK
1087/**
1088 * Get column count in async query result
1089 */
3b74985b 1090extern "C" int EXPORT DrvGetColumnCountUnbuffered(DB2DRV_UNBUFFERED_QUERY_RESULT *pResult)
84277a0f 1091{
3b74985b 1092 return (pResult != NULL) ? pResult->numColumns : 0;
84277a0f
AK
1093}
1094
8bd0152e
VK
1095/**
1096 * Get column name in async query result
1097 */
3b74985b 1098extern "C" const char EXPORT *DrvGetColumnNameUnbuffered(DB2DRV_UNBUFFERED_QUERY_RESULT *pResult, int column)
84277a0f 1099{
3b74985b 1100 return ((pResult != NULL) && (column >= 0) && (column < pResult->numColumns)) ? pResult->columnNames[column] : NULL;
84277a0f
AK
1101}
1102
8bd0152e 1103/**
3b74985b 1104 * Destroy result of unbuffered query
8bd0152e 1105 */
3b74985b 1106extern "C" void EXPORT DrvFreeUnbufferedResult(DB2DRV_UNBUFFERED_QUERY_RESULT *pResult)
84277a0f 1107{
3b74985b
VK
1108 if (pResult == NULL)
1109 return;
1110
1111 if (pResult->isPrepared)
1112 SQLCloseCursor(pResult->sqlStatement);
1113 else
1114 SQLFreeHandle(SQL_HANDLE_STMT, pResult->sqlStatement);
1115 MutexUnlock(pResult->pConn->mutexQuery);
ff1cb353
VK
1116
1117 for(int i = 0; i < pResult->numColumns; i++)
1118 {
1119 free(pResult->columnNames[i]);
1120 }
1121 free(pResult->columnNames);
1122
1123 free(pResult);
84277a0f
AK
1124}
1125
8bd0152e
VK
1126/**
1127 * Begin transaction
1128 */
e83d6e8f 1129extern "C" DWORD EXPORT DrvBegin(DB2DRV_CONN *pConn)
84277a0f 1130{
e2a7ccac
AK
1131 SQLRETURN nRet;
1132 DWORD dwResult;
84277a0f
AK
1133
1134 if (pConn == NULL)
e2a7ccac
AK
1135 {
1136 return DBERR_INVALID_HANDLE;
1137 }
84277a0f 1138
c17f6cbc 1139 MutexLock(pConn->mutexQuery);
e2a7ccac
AK
1140 nRet = SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, 0);
1141 if ((nRet == SQL_SUCCESS) || (nRet == SQL_SUCCESS_WITH_INFO))
1142 {
1143 dwResult = DBERR_SUCCESS;
1144 }
1145 else
1146 {
1147 dwResult = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, NULL);
1148 }
84277a0f 1149 MutexUnlock(pConn->mutexQuery);
e2a7ccac 1150 return dwResult;
84277a0f
AK
1151}
1152
8bd0152e
VK
1153/**
1154 * Commit transaction
1155 */
e83d6e8f 1156extern "C" DWORD EXPORT DrvCommit(DB2DRV_CONN *pConn)
84277a0f 1157{
e2a7ccac 1158 SQLRETURN nRet;
84277a0f
AK
1159
1160 if (pConn == NULL)
e2a7ccac
AK
1161 {
1162 return DBERR_INVALID_HANDLE;
1163 }
84277a0f 1164
c17f6cbc 1165 MutexLock(pConn->mutexQuery);
e2a7ccac
AK
1166 nRet = SQLEndTran(SQL_HANDLE_DBC, pConn->sqlConn, SQL_COMMIT);
1167 SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0);
84277a0f 1168 MutexUnlock(pConn->mutexQuery);
e2a7ccac 1169 return ((nRet == SQL_SUCCESS) || (nRet == SQL_SUCCESS_WITH_INFO)) ? DBERR_SUCCESS : DBERR_OTHER_ERROR;
84277a0f
AK
1170}
1171
8bd0152e
VK
1172/**
1173 * Rollback transaction
1174 */
e83d6e8f 1175extern "C" DWORD EXPORT DrvRollback(DB2DRV_CONN *pConn)
84277a0f 1176{
e2a7ccac 1177 SQLRETURN nRet;
84277a0f
AK
1178
1179 if (pConn == NULL)
e2a7ccac
AK
1180 {
1181 return DBERR_INVALID_HANDLE;
1182 }
84277a0f 1183
c17f6cbc 1184 MutexLock(pConn->mutexQuery);
e2a7ccac
AK
1185 nRet = SQLEndTran(SQL_HANDLE_DBC, pConn->sqlConn, SQL_ROLLBACK);
1186 SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0);
84277a0f 1187 MutexUnlock(pConn->mutexQuery);
e2a7ccac 1188 return ((nRet == SQL_SUCCESS) || (nRet == SQL_SUCCESS_WITH_INFO)) ? DBERR_SUCCESS : DBERR_OTHER_ERROR;
84277a0f
AK
1189}
1190
1191
8bd0152e
VK
1192/**
1193 * Check if table exist
1194 */
a403c49d 1195extern "C" int EXPORT DrvIsTableExist(DB2DRV_CONN *pConn, const NETXMS_WCHAR *name)
8bd0152e
VK
1196{
1197 WCHAR query[256];
1198 swprintf(query, 256, L"SELECT count(*) FROM sysibm.systables WHERE type='T' AND upper(name)=upper('%ls')", name);
1199 DWORD error;
1200 WCHAR errorText[DBDRV_MAX_ERROR_TEXT];
1201 int rc = DBIsTableExist_Failure;
1202 DB2DRV_QUERY_RESULT *hResult = (DB2DRV_QUERY_RESULT *)DrvSelect(pConn, query, &error, errorText);
1203 if (hResult != NULL)
1204 {
1205 WCHAR buffer[64] = L"";
1206 DrvGetField(hResult, 0, 0, buffer, 64);
1207 rc = (wcstol(buffer, NULL, 10) > 0) ? DBIsTableExist_Found : DBIsTableExist_NotFound;
1208 DrvFreeResult(hResult);
1209 }
1210 return rc;
1211}
84277a0f
AK
1212
1213#ifdef _WIN32
1214
8bd0152e
VK
1215/**
1216 * DLL Entry point
1217 */
84277a0f
AK
1218BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
1219{
e2a7ccac
AK
1220 if (dwReason == DLL_PROCESS_ATTACH)
1221 {
1222 DisableThreadLibraryCalls(hInstance);
1223 }
1224 return TRUE;
84277a0f
AK
1225}
1226
1227#endif