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