8327824be640c79e51db7088fa3fdf187151301c
[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 };
342 static SQLSMALLINT odbcCTypeA[] = { SQL_C_CHAR, SQL_C_SLONG, SQL_C_ULONG, SQL_C_SBIGINT, SQL_C_UBIGINT, SQL_C_DOUBLE };
343 static DWORD bufferSize[] = { 0, sizeof(LONG), sizeof(DWORD), sizeof(INT64), sizeof(QWORD), sizeof(double) };
344
345 int length = (int)wcslen((NETXMS_WCHAR *)buffer) + 1;
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 sqlBuffer = buffer;
355 #else
356 if (cType == DB_CTYPE_STRING)
357 {
358 sqlBuffer = UCS2StringFromUCS4String((NETXMS_WCHAR *)buffer);
359 stmt->buffers->add(sqlBuffer);
360 }
361 else
362 {
363 sqlBuffer = buffer;
364 }
365 #endif
366 }
367 else
368 {
369 if (cType == DB_CTYPE_STRING)
370 {
371 sqlBuffer = MBStringFromWideString((NETXMS_WCHAR *)buffer);
372 stmt->buffers->add(sqlBuffer);
373 }
374 else
375 {
376 sqlBuffer = buffer;
377 }
378 }
379 break;
380 case DB_BIND_DYNAMIC:
381 if (m_useUnicode)
382 {
383 #if defined(_WIN32) || defined(UNICODE_UCS2)
384 sqlBuffer = buffer;
385 #else
386 if (cType == DB_CTYPE_STRING)
387 {
388 sqlBuffer = UCS2StringFromUCS4String((NETXMS_WCHAR *)buffer);
389 free(buffer);
390 }
391 else
392 {
393 sqlBuffer = buffer;
394 }
395 #endif
396 }
397 else
398 {
399 if (cType == DB_CTYPE_STRING)
400 {
401 sqlBuffer = MBStringFromWideString((NETXMS_WCHAR *)buffer);
402 free(buffer);
403 }
404 else
405 {
406 sqlBuffer = buffer;
407 }
408 }
409 stmt->buffers->add(sqlBuffer);
410 break;
411 case DB_BIND_TRANSIENT:
412 if (m_useUnicode)
413 {
414 #if defined(_WIN32) || defined(UNICODE_UCS2)
415 sqlBuffer = nx_memdup(buffer, (cType == DB_CTYPE_STRING) ? (DWORD)(length * sizeof(WCHAR)) : bufferSize[cType]);
416 #else
417 if (cType == DB_CTYPE_STRING)
418 {
419 sqlBuffer = UCS2StringFromUCS4String((NETXMS_WCHAR *)buffer);
420 }
421 else
422 {
423 sqlBuffer = nx_memdup(buffer, bufferSize[cType]);
424 }
425 #endif
426 }
427 else
428 {
429 if (cType == DB_CTYPE_STRING)
430 {
431 sqlBuffer = MBStringFromWideString((NETXMS_WCHAR *)buffer);
432 }
433 else
434 {
435 sqlBuffer = nx_memdup(buffer, bufferSize[cType]);
436 }
437 }
438 stmt->buffers->add(sqlBuffer);
439 break;
440 default:
441 return; // Invalid call
442 }
443 SQLBindParameter(stmt->handle, pos, SQL_PARAM_INPUT, m_useUnicode ? odbcCTypeW[cType] : odbcCTypeA[cType], odbcSqlType[sqlType],
444 (cType == DB_CTYPE_STRING) ? length : 0, 0, sqlBuffer, 0, NULL);
445 }
446
447 /**
448 * Execute prepared statement
449 */
450 extern "C" DWORD EXPORT DrvExecute(ODBCDRV_CONN *pConn, ODBCDRV_STATEMENT *stmt, NETXMS_WCHAR *errorText)
451 {
452 DWORD dwResult;
453
454 MutexLock(pConn->mutexQuery);
455 long rc = SQLExecute(stmt->handle);
456 if ((rc == SQL_SUCCESS) ||
457 (rc == SQL_SUCCESS_WITH_INFO) ||
458 (rc == SQL_NO_DATA))
459 {
460 ClearPendingResults(stmt->handle);
461 dwResult = DBERR_SUCCESS;
462 }
463 else
464 {
465 dwResult = GetSQLErrorInfo(SQL_HANDLE_STMT, stmt->handle, errorText);
466 }
467 MutexUnlock(pConn->mutexQuery);
468 return dwResult;
469 }
470
471 /**
472 * Destroy prepared statement
473 */
474 extern "C" void EXPORT DrvFreeStatement(ODBCDRV_STATEMENT *stmt)
475 {
476 if (stmt == NULL)
477 return;
478
479 MutexLock(stmt->connection->mutexQuery);
480 SQLFreeHandle(SQL_HANDLE_STMT, stmt->handle);
481 MutexUnlock(stmt->connection->mutexQuery);
482 delete stmt->buffers;
483 free(stmt);
484 }
485
486 /**
487 * Perform non-SELECT query
488 */
489 extern "C" DWORD EXPORT DrvQuery(ODBCDRV_CONN *pConn, NETXMS_WCHAR *pwszQuery, NETXMS_WCHAR *errorText)
490 {
491 DWORD dwResult;
492
493 MutexLock(pConn->mutexQuery);
494
495 // Allocate statement handle
496 SQLHSTMT sqlStatement;
497 SQLRETURN iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &sqlStatement);
498 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
499 {
500 // Execute statement
501 if (m_useUnicode)
502 {
503 #if defined(_WIN32) || defined(UNICODE_UCS2)
504 iResult = SQLExecDirectW(sqlStatement, (SQLWCHAR *)pwszQuery, SQL_NTS);
505 #else
506 SQLWCHAR *temp = UCS2StringFromUCS4String(pwszQuery);
507 iResult = SQLExecDirectW(sqlStatement, temp, SQL_NTS);
508 free(temp);
509 #endif
510 }
511 else
512 {
513 char *temp = MBStringFromWideString(pwszQuery);
514 iResult = SQLExecDirectA(sqlStatement, (SQLCHAR *)temp, SQL_NTS);
515 free(temp);
516 }
517 if ((iResult == SQL_SUCCESS) ||
518 (iResult == SQL_SUCCESS_WITH_INFO) ||
519 (iResult == SQL_NO_DATA))
520 {
521 dwResult = DBERR_SUCCESS;
522 }
523 else
524 {
525 dwResult = GetSQLErrorInfo(SQL_HANDLE_STMT, sqlStatement, errorText);
526 }
527 SQLFreeHandle(SQL_HANDLE_STMT, sqlStatement);
528 }
529 else
530 {
531 dwResult = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
532 }
533
534 MutexUnlock(pConn->mutexQuery);
535 return dwResult;
536 }
537
538 /**
539 * Get complete field data
540 */
541 static NETXMS_WCHAR *GetFieldData(SQLHSTMT sqlStatement, short column)
542 {
543 NETXMS_WCHAR *result = NULL;
544 SQLLEN dataSize;
545 if (m_useUnicode)
546 {
547 #if defined(_WIN32) || defined(UNICODE_UCS2)
548 WCHAR buffer[256];
549 SQLRETURN rc = SQLGetData(sqlStatement, column, SQL_C_WCHAR, buffer, sizeof(buffer), &dataSize);
550 if (((rc == SQL_SUCCESS) || ((rc == SQL_SUCCESS_WITH_INFO) && (dataSize >= 0) && (dataSize <= (SQLLEN)(sizeof(buffer) - sizeof(WCHAR))))) && (dataSize != SQL_NULL_DATA))
551 {
552 result = wcsdup(buffer);
553 }
554 else if ((rc == SQL_SUCCESS_WITH_INFO) && (dataSize != SQL_NULL_DATA))
555 {
556 if (dataSize > (SQLLEN)(sizeof(buffer) - sizeof(WCHAR)))
557 {
558 WCHAR *temp = (WCHAR *)malloc(dataSize + sizeof(WCHAR));
559 memcpy(temp, buffer, sizeof(buffer));
560 rc = SQLGetData(sqlStatement, column, SQL_C_WCHAR, &temp[255], dataSize - 254 * sizeof(WCHAR), &dataSize);
561 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
562 {
563 result = temp;
564 }
565 else
566 {
567 free(temp);
568 }
569 }
570 else if (dataSize == SQL_NO_TOTAL)
571 {
572 size_t tempSize = sizeof(buffer) * 4;
573 WCHAR *temp = (WCHAR *)malloc(tempSize);
574 memcpy(temp, buffer, sizeof(buffer));
575 size_t offset = sizeof(buffer) - sizeof(WCHAR);
576 while(true)
577 {
578 SQLLEN readSize = tempSize - offset;
579 rc = SQLGetData(sqlStatement, column, SQL_C_WCHAR, (char *)temp + offset, readSize, &dataSize);
580 if ((rc == SQL_SUCCESS) || (rc == SQL_NO_DATA))
581 break;
582 if (dataSize == SQL_NO_TOTAL)
583 {
584 tempSize += sizeof(buffer) * 4;
585 }
586 else
587 {
588 tempSize += dataSize - readSize;
589 }
590 temp = (WCHAR *)realloc(temp, tempSize);
591 offset += readSize - sizeof(WCHAR);
592 }
593 result = temp;
594 }
595 }
596 #else
597 UCS2CHAR buffer[256];
598 SQLRETURN rc = SQLGetData(sqlStatement, column, SQL_C_WCHAR, buffer, sizeof(buffer), &dataSize);
599 if (((rc == SQL_SUCCESS) || ((rc == SQL_SUCCESS_WITH_INFO) && (dataSize >= 0) && (dataSize <= (SQLLEN)(sizeof(buffer) - sizeof(UCS2CHAR))))) && (dataSize != SQL_NULL_DATA))
600 {
601 int len = ucs2_strlen(buffer);
602 result = (NETXMS_WCHAR *)malloc((len + 1) * sizeof(NETXMS_WCHAR));
603 ucs2_to_ucs4(buffer, -1, result, len + 1);
604 }
605 else if ((rc == SQL_SUCCESS_WITH_INFO) && (dataSize != SQL_NULL_DATA))
606 {
607 if (dataSize > (SQLLEN)(sizeof(buffer) - sizeof(UCS2CHAR)))
608 {
609 UCS2CHAR *temp = (UCS2CHAR *)malloc(dataSize + sizeof(UCS2CHAR));
610 memcpy(temp, buffer, sizeof(buffer));
611 rc = SQLGetData(sqlStatement, column, SQL_C_WCHAR, &temp[255], dataSize - 254 * sizeof(UCS2CHAR), &dataSize);
612 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
613 {
614 int len = ucs2_strlen(temp);
615 result = (NETXMS_WCHAR *)malloc((len + 1) * sizeof(NETXMS_WCHAR));
616 ucs2_to_ucs4(temp, -1, result, len + 1);
617 }
618 free(temp);
619 }
620 else if (dataSize == SQL_NO_TOTAL)
621 {
622 size_t tempSize = sizeof(buffer) * 4;
623 UCS2CHAR *temp = (UCS2CHAR *)malloc(tempSize);
624 memcpy(temp, buffer, sizeof(buffer));
625 size_t offset = sizeof(buffer) - sizeof(UCS2CHAR);
626 while(true)
627 {
628 SQLLEN readSize = tempSize - offset;
629 rc = SQLGetData(sqlStatement, column, SQL_C_WCHAR, (char *)temp + offset, readSize, &dataSize);
630 if ((rc == SQL_SUCCESS) || (rc == SQL_NO_DATA))
631 break;
632 if (dataSize == SQL_NO_TOTAL)
633 {
634 tempSize += sizeof(buffer) * 4;
635 }
636 else
637 {
638 tempSize += dataSize - readSize;
639 }
640 temp = (UCS2CHAR *)realloc(temp, tempSize);
641 offset += readSize - sizeof(UCS2CHAR);
642 }
643 int len = ucs2_strlen(temp);
644 result = (NETXMS_WCHAR *)malloc((len + 1) * sizeof(NETXMS_WCHAR));
645 ucs2_to_ucs4(temp, -1, result, len + 1);
646 free(temp);
647 }
648 }
649 #endif
650 }
651 else
652 {
653 char buffer[256];
654 SQLRETURN rc = SQLGetData(sqlStatement, column, SQL_C_CHAR, buffer, sizeof(buffer), &dataSize);
655 if (((rc == SQL_SUCCESS) || ((rc == SQL_SUCCESS_WITH_INFO) && (dataSize >= 0) && (dataSize <= (SQLLEN)(sizeof(buffer) - 1)))) && (dataSize != SQL_NULL_DATA))
656 {
657 result = WideStringFromMBString(buffer);
658 }
659 else if ((rc == SQL_SUCCESS_WITH_INFO) && (dataSize != SQL_NULL_DATA))
660 {
661 if (dataSize > (SQLLEN)(sizeof(buffer) - 1))
662 {
663 char *temp = (char *)malloc(dataSize + 1);
664 memcpy(temp, buffer, sizeof(buffer));
665 rc = SQLGetData(sqlStatement, column, SQL_C_CHAR, &temp[255], dataSize - 254, &dataSize);
666 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
667 {
668 result = WideStringFromMBString(temp);
669 }
670 free(temp);
671 }
672 else if (dataSize == SQL_NO_TOTAL)
673 {
674 size_t tempSize = sizeof(buffer) * 4;
675 char *temp = (char *)malloc(tempSize);
676 memcpy(temp, buffer, sizeof(buffer));
677 size_t offset = sizeof(buffer) - 1;
678 while(true)
679 {
680 SQLLEN readSize = tempSize - offset;
681 rc = SQLGetData(sqlStatement, column, SQL_C_CHAR, &temp[offset], readSize, &dataSize);
682 if ((rc == SQL_SUCCESS) || (rc == SQL_NO_DATA))
683 break;
684 if (dataSize == SQL_NO_TOTAL)
685 {
686 tempSize += sizeof(buffer) * 4;
687 }
688 else
689 {
690 tempSize += dataSize - readSize;
691 }
692 temp = (char *)realloc(temp, tempSize);
693 offset += readSize - 1;
694 }
695 result = WideStringFromMBString(temp);
696 free(temp);
697 }
698 }
699 }
700 return (result != NULL) ? result : wcsdup(L"");
701 }
702
703 /**
704 * Process results of SELECT query
705 */
706 static ODBCDRV_QUERY_RESULT *ProcessSelectResults(SQLHSTMT stmt)
707 {
708 // Allocate result buffer and determine column info
709 ODBCDRV_QUERY_RESULT *pResult = (ODBCDRV_QUERY_RESULT *)malloc(sizeof(ODBCDRV_QUERY_RESULT));
710 short wNumCols;
711 SQLNumResultCols(stmt, &wNumCols);
712 pResult->numColumns = wNumCols;
713 pResult->numRows = 0;
714 pResult->pValues = NULL;
715
716 // Get column names
717 pResult->columnNames = (char **)malloc(sizeof(char *) * pResult->numColumns);
718 for(int i = 0; i < (int)pResult->numColumns; i++)
719 {
720 char name[256];
721 SQLSMALLINT len;
722
723 SQLRETURN iResult = SQLColAttributeA(stmt, (SQLSMALLINT)(i + 1), SQL_DESC_NAME, name, 256, &len, NULL);
724 if ((iResult == SQL_SUCCESS) ||
725 (iResult == SQL_SUCCESS_WITH_INFO))
726 {
727 name[len] = 0;
728 pResult->columnNames[i] = strdup(name);
729 }
730 else
731 {
732 pResult->columnNames[i] = strdup("");
733 }
734 }
735
736 // Fetch all data
737 long iCurrValue = 0;
738 SQLRETURN iResult;
739 while(iResult = SQLFetch(stmt),
740 (iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
741 {
742 pResult->numRows++;
743 pResult->pValues = (NETXMS_WCHAR **)realloc(pResult->pValues,
744 sizeof(NETXMS_WCHAR *) * (pResult->numRows * pResult->numColumns));
745 for(int i = 1; i <= (int)pResult->numColumns; i++)
746 {
747 pResult->pValues[iCurrValue++] = GetFieldData(stmt, (short)i);
748 }
749 }
750
751 return pResult;
752 }
753
754 /**
755 * Perform SELECT query
756 */
757 extern "C" DBDRV_RESULT EXPORT DrvSelect(ODBCDRV_CONN *pConn, NETXMS_WCHAR *pwszQuery, DWORD *pdwError, NETXMS_WCHAR *errorText)
758 {
759 ODBCDRV_QUERY_RESULT *pResult = NULL;
760
761 MutexLock(pConn->mutexQuery);
762
763 // Allocate statement handle
764 SQLHSTMT sqlStatement;
765 SQLRETURN iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &sqlStatement);
766 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
767 {
768 // Execute statement
769 if (m_useUnicode)
770 {
771 #if defined(_WIN32) || defined(UNICODE_UCS2)
772 iResult = SQLExecDirectW(sqlStatement, (SQLWCHAR *)pwszQuery, SQL_NTS);
773 #else
774 SQLWCHAR *temp = UCS2StringFromUCS4String(pwszQuery);
775 iResult = SQLExecDirectW(sqlStatement, temp, SQL_NTS);
776 free(temp);
777 #endif
778 }
779 else
780 {
781 char *temp = MBStringFromWideString(pwszQuery);
782 iResult = SQLExecDirectA(sqlStatement, (SQLCHAR *)temp, SQL_NTS);
783 free(temp);
784 }
785 if ((iResult == SQL_SUCCESS) ||
786 (iResult == SQL_SUCCESS_WITH_INFO))
787 {
788 pResult = ProcessSelectResults(sqlStatement);
789 *pdwError = DBERR_SUCCESS;
790 }
791 else
792 {
793 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, sqlStatement, errorText);
794 }
795 SQLFreeHandle(SQL_HANDLE_STMT, sqlStatement);
796 }
797 else
798 {
799 *pdwError = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
800 }
801
802 MutexUnlock(pConn->mutexQuery);
803 return pResult;
804 }
805
806 /**
807 * Perform SELECT query using prepared statement
808 */
809 extern "C" DBDRV_RESULT EXPORT DrvSelectPrepared(ODBCDRV_CONN *pConn, ODBCDRV_STATEMENT *stmt, DWORD *pdwError, NETXMS_WCHAR *errorText)
810 {
811 ODBCDRV_QUERY_RESULT *pResult = NULL;
812
813 MutexLock(pConn->mutexQuery);
814 long rc = SQLExecute(stmt->handle);
815 if ((rc == SQL_SUCCESS) ||
816 (rc == SQL_SUCCESS_WITH_INFO))
817 {
818 pResult = ProcessSelectResults(stmt->handle);
819 *pdwError = DBERR_SUCCESS;
820 }
821 else
822 {
823 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, stmt->handle, errorText);
824 }
825 MutexUnlock(pConn->mutexQuery);
826 return pResult;
827 }
828
829 /**
830 * Get field length from result
831 */
832 extern "C" LONG EXPORT DrvGetFieldLength(ODBCDRV_QUERY_RESULT *pResult, int iRow, int iColumn)
833 {
834 LONG nLen = -1;
835
836 if (pResult != NULL)
837 {
838 if ((iRow < pResult->numRows) && (iRow >= 0) &&
839 (iColumn < pResult->numColumns) && (iColumn >= 0))
840 nLen = (LONG)wcslen(pResult->pValues[iRow * pResult->numColumns + iColumn]);
841 }
842 return nLen;
843 }
844
845 /**
846 * Get field value from result
847 */
848 extern "C" NETXMS_WCHAR EXPORT *DrvGetField(ODBCDRV_QUERY_RESULT *pResult, int iRow, int iColumn,
849 NETXMS_WCHAR *pBuffer, int nBufSize)
850 {
851 NETXMS_WCHAR *pValue = NULL;
852
853 if (pResult != NULL)
854 {
855 if ((iRow < pResult->numRows) && (iRow >= 0) &&
856 (iColumn < pResult->numColumns) && (iColumn >= 0))
857 {
858 #ifdef _WIN32
859 wcsncpy_s(pBuffer, nBufSize, pResult->pValues[iRow * pResult->numColumns + iColumn], _TRUNCATE);
860 #else
861 wcsncpy(pBuffer, pResult->pValues[iRow * pResult->numColumns + iColumn], nBufSize);
862 pBuffer[nBufSize - 1] = 0;
863 #endif
864 pValue = pBuffer;
865 }
866 }
867 return pValue;
868 }
869
870 /**
871 * Get number of rows in result
872 */
873 extern "C" int EXPORT DrvGetNumRows(ODBCDRV_QUERY_RESULT *pResult)
874 {
875 return (pResult != NULL) ? pResult->numRows : 0;
876 }
877
878 /**
879 * Get column count in query result
880 */
881 extern "C" int EXPORT DrvGetColumnCount(ODBCDRV_QUERY_RESULT *pResult)
882 {
883 return (pResult != NULL) ? pResult->numColumns : 0;
884 }
885
886 /**
887 * Get column name in query result
888 */
889 extern "C" const char EXPORT *DrvGetColumnName(ODBCDRV_QUERY_RESULT *pResult, int column)
890 {
891 return ((pResult != NULL) && (column >= 0) && (column < pResult->numColumns)) ? pResult->columnNames[column] : NULL;
892 }
893
894 /**
895 * Free SELECT results
896 */
897 extern "C" void EXPORT DrvFreeResult(ODBCDRV_QUERY_RESULT *pResult)
898 {
899 if (pResult != NULL)
900 {
901 int i, iNumValues;
902
903 iNumValues = pResult->numColumns * pResult->numRows;
904 for(i = 0; i < iNumValues; i++)
905 safe_free(pResult->pValues[i]);
906 safe_free(pResult->pValues);
907
908 for(i = 0; i < pResult->numColumns; i++)
909 safe_free(pResult->columnNames[i]);
910 safe_free(pResult->columnNames);
911
912 free(pResult);
913 }
914 }
915
916 /**
917 * Perform unbuffered SELECT query
918 */
919 extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectUnbuffered(ODBCDRV_CONN *pConn, NETXMS_WCHAR *pwszQuery, DWORD *pdwError, NETXMS_WCHAR *errorText)
920 {
921 ODBCDRV_UNBUFFERED_QUERY_RESULT *pResult = NULL;
922 long iResult;
923 short wNumCols;
924 int i;
925
926 MutexLock(pConn->mutexQuery);
927
928 // Allocate statement handle
929 SQLHSTMT sqlStatement;
930 iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &sqlStatement);
931 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
932 {
933 // Execute statement
934 if (m_useUnicode)
935 {
936 #if defined(_WIN32) || defined(UNICODE_UCS2)
937 iResult = SQLExecDirectW(sqlStatement, (SQLWCHAR *)pwszQuery, SQL_NTS);
938 #else
939 SQLWCHAR *temp = UCS2StringFromUCS4String(pwszQuery);
940 iResult = SQLExecDirectW(sqlStatement, temp, SQL_NTS);
941 free(temp);
942 #endif
943 }
944 else
945 {
946 char *temp = MBStringFromWideString(pwszQuery);
947 iResult = SQLExecDirectA(sqlStatement, (SQLCHAR *)temp, SQL_NTS);
948 free(temp);
949 }
950 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
951 {
952 // Allocate result buffer and determine column info
953 pResult = (ODBCDRV_UNBUFFERED_QUERY_RESULT *)malloc(sizeof(ODBCDRV_UNBUFFERED_QUERY_RESULT));
954 pResult->sqlStatement = sqlStatement;
955 pResult->isPrepared = false;
956
957 SQLNumResultCols(sqlStatement, &wNumCols);
958 pResult->numColumns = wNumCols;
959 pResult->pConn = pConn;
960 pResult->noMoreRows = false;
961
962 // Get column names
963 pResult->columnNames = (char **)malloc(sizeof(char *) * pResult->numColumns);
964 for(i = 0; i < pResult->numColumns; i++)
965 {
966 char name[256];
967 SQLSMALLINT len;
968
969 iResult = SQLColAttributeA(sqlStatement, (SQLSMALLINT)(i + 1),
970 SQL_DESC_NAME, name, 256, &len, NULL);
971 if ((iResult == SQL_SUCCESS) ||
972 (iResult == SQL_SUCCESS_WITH_INFO))
973 {
974 name[len] = 0;
975 pResult->columnNames[i] = strdup(name);
976 }
977 else
978 {
979 pResult->columnNames[i] = strdup("");
980 }
981 }
982
983 // Column values cache
984 pResult->values = (NETXMS_WCHAR **)malloc(sizeof(NETXMS_WCHAR *) * pResult->numColumns);
985 memset(pResult->values, 0, sizeof(NETXMS_WCHAR *) * pResult->numColumns);
986
987 *pdwError = DBERR_SUCCESS;
988 }
989 else
990 {
991 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, sqlStatement, errorText);
992 // Free statement handle if query failed
993 SQLFreeHandle(SQL_HANDLE_STMT, sqlStatement);
994 }
995 }
996 else
997 {
998 *pdwError = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
999 }
1000
1001 if (pResult == NULL) // Unlock mutex if query has failed
1002 MutexUnlock(pConn->mutexQuery);
1003 return pResult;
1004 }
1005
1006 /**
1007 * Perform unbuffered SELECT query using prepared statement
1008 */
1009 extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectPreparedUnbuffered(ODBCDRV_CONN *pConn, ODBCDRV_STATEMENT *stmt, DWORD *pdwError, NETXMS_WCHAR *errorText)
1010 {
1011 ODBCDRV_UNBUFFERED_QUERY_RESULT *pResult = NULL;
1012
1013 MutexLock(pConn->mutexQuery);
1014 SQLRETURN rc = SQLExecute(stmt->handle);
1015 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
1016 {
1017 // Allocate result buffer and determine column info
1018 pResult = (ODBCDRV_UNBUFFERED_QUERY_RESULT *)malloc(sizeof(ODBCDRV_UNBUFFERED_QUERY_RESULT));
1019 pResult->sqlStatement = stmt->handle;
1020 pResult->isPrepared = true;
1021
1022 short wNumCols;
1023 SQLNumResultCols(pResult->sqlStatement, &wNumCols);
1024 pResult->numColumns = wNumCols;
1025 pResult->pConn = pConn;
1026 pResult->noMoreRows = false;
1027 pResult->values = (NETXMS_WCHAR **)malloc(sizeof(NETXMS_WCHAR *) * pResult->numColumns);
1028 memset(pResult->values, 0, sizeof(NETXMS_WCHAR *) * pResult->numColumns);
1029
1030 // Get column names
1031 pResult->columnNames = (char **)malloc(sizeof(char *) * pResult->numColumns);
1032 for(int i = 0; i < pResult->numColumns; i++)
1033 {
1034 char name[256];
1035 SQLSMALLINT len;
1036
1037 rc = SQLColAttributeA(pResult->sqlStatement, (SQLSMALLINT)(i + 1), SQL_DESC_NAME, name, 256, &len, NULL);
1038 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
1039 {
1040 name[len] = 0;
1041 pResult->columnNames[i] = strdup(name);
1042 }
1043 else
1044 {
1045 pResult->columnNames[i] = strdup("");
1046 }
1047 }
1048 *pdwError = DBERR_SUCCESS;
1049 }
1050 else
1051 {
1052 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, stmt->handle, errorText);
1053 }
1054
1055 if (pResult == NULL) // Unlock mutex if query has failed
1056 MutexUnlock(pConn->mutexQuery);
1057 return pResult;
1058 }
1059
1060 /**
1061 * Fetch next result line from asynchronous SELECT results
1062 */
1063 extern "C" bool EXPORT DrvFetch(ODBCDRV_UNBUFFERED_QUERY_RESULT *pResult)
1064 {
1065 bool success = false;
1066
1067 if (pResult != NULL)
1068 {
1069 SQLRETURN rc = SQLFetch(pResult->sqlStatement);
1070 success = ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO));
1071 if (success)
1072 {
1073 for(int i = 0; i < pResult->numColumns; i++)
1074 {
1075 free(pResult->values[i]);
1076 pResult->values[i] = GetFieldData(pResult->sqlStatement, (short)i + 1);
1077 }
1078 }
1079 else
1080 {
1081 pResult->noMoreRows = true;
1082 }
1083 }
1084 return success;
1085 }
1086
1087 /**
1088 * Get field length from async query result
1089 */
1090 extern "C" LONG EXPORT DrvGetFieldLengthUnbuffered(ODBCDRV_UNBUFFERED_QUERY_RESULT *result, int col)
1091 {
1092 if (result == NULL)
1093 return -1;
1094
1095 if ((col >= result->numColumns) || (col < 0))
1096 return -1;
1097
1098 return (result->values[col] != NULL) ? (LONG)wcslen(result->values[col]) : -1;
1099 }
1100
1101 /**
1102 * Get field from current row in async query result
1103 */
1104 extern "C" NETXMS_WCHAR EXPORT *DrvGetFieldUnbuffered(ODBCDRV_UNBUFFERED_QUERY_RESULT *result, int col, NETXMS_WCHAR *buffer, int bufferSize)
1105 {
1106 // Check if we have valid result handle
1107 if (result == NULL)
1108 return NULL;
1109
1110 // Check if there are valid fetched row
1111 if (result->noMoreRows)
1112 return NULL;
1113
1114 if ((col >= 0) && (col < result->numColumns) && (result->values[col] != NULL))
1115 {
1116 wcsncpy(buffer, result->values[col], bufferSize - 1);
1117 buffer[bufferSize - 1] = 0;
1118 }
1119 else
1120 {
1121 buffer[0] = 0;
1122 }
1123 return buffer;
1124 }
1125
1126 /**
1127 * Get column count in async query result
1128 */
1129 extern "C" int EXPORT DrvGetColumnCountUnbuffered(ODBCDRV_UNBUFFERED_QUERY_RESULT *pResult)
1130 {
1131 return (pResult != NULL) ? pResult->numColumns : 0;
1132 }
1133
1134 /**
1135 * Get column name in async query result
1136 */
1137 extern "C" const char EXPORT *DrvGetColumnNameUnbuffered(ODBCDRV_UNBUFFERED_QUERY_RESULT *pResult, int column)
1138 {
1139 return ((pResult != NULL) && (column >= 0) && (column < pResult->numColumns)) ? pResult->columnNames[column] : NULL;
1140 }
1141
1142 /**
1143 * Destroy result of async query
1144 */
1145 extern "C" void EXPORT DrvFreeUnbufferedResult(ODBCDRV_UNBUFFERED_QUERY_RESULT *pResult)
1146 {
1147 if (pResult == NULL)
1148 return;
1149
1150 if (pResult->isPrepared)
1151 SQLCloseCursor(pResult->sqlStatement);
1152 else
1153 SQLFreeHandle(SQL_HANDLE_STMT, pResult->sqlStatement);
1154 for(int i = 0; i < pResult->numColumns; i++)
1155 {
1156 safe_free(pResult->columnNames[i]);
1157 safe_free(pResult->values[i]);
1158 }
1159 free(pResult->columnNames);
1160 free(pResult->values);
1161 MutexUnlock(pResult->pConn->mutexQuery);
1162 free(pResult);
1163 }
1164
1165 /**
1166 * Begin transaction
1167 */
1168 extern "C" DWORD EXPORT DrvBegin(ODBCDRV_CONN *pConn)
1169 {
1170 SQLRETURN nRet;
1171 DWORD dwResult;
1172
1173 if (pConn == NULL)
1174 return DBERR_INVALID_HANDLE;
1175
1176 MutexLock(pConn->mutexQuery);
1177 nRet = SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, 0);
1178 if ((nRet == SQL_SUCCESS) || (nRet == SQL_SUCCESS_WITH_INFO))
1179 {
1180 dwResult = DBERR_SUCCESS;
1181 }
1182 else
1183 {
1184 dwResult = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, NULL);
1185 }
1186 MutexUnlock(pConn->mutexQuery);
1187 return dwResult;
1188 }
1189
1190 /**
1191 * Commit transaction
1192 */
1193 extern "C" DWORD EXPORT DrvCommit(ODBCDRV_CONN *pConn)
1194 {
1195 SQLRETURN nRet;
1196
1197 if (pConn == NULL)
1198 return DBERR_INVALID_HANDLE;
1199
1200 MutexLock(pConn->mutexQuery);
1201 nRet = SQLEndTran(SQL_HANDLE_DBC, pConn->sqlConn, SQL_COMMIT);
1202 SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0);
1203 MutexUnlock(pConn->mutexQuery);
1204 return ((nRet == SQL_SUCCESS) || (nRet == SQL_SUCCESS_WITH_INFO)) ? DBERR_SUCCESS : DBERR_OTHER_ERROR;
1205 }
1206
1207 /**
1208 * Rollback transaction
1209 */
1210 extern "C" DWORD EXPORT DrvRollback(ODBCDRV_CONN *pConn)
1211 {
1212 SQLRETURN nRet;
1213
1214 if (pConn == NULL)
1215 return DBERR_INVALID_HANDLE;
1216
1217 MutexLock(pConn->mutexQuery);
1218 nRet = SQLEndTran(SQL_HANDLE_DBC, pConn->sqlConn, SQL_ROLLBACK);
1219 SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0);
1220 MutexUnlock(pConn->mutexQuery);
1221 return ((nRet == SQL_SUCCESS) || (nRet == SQL_SUCCESS_WITH_INFO)) ? DBERR_SUCCESS : DBERR_OTHER_ERROR;
1222 }
1223
1224 /**
1225 * Check if table exist
1226 */
1227 extern "C" int EXPORT DrvIsTableExist(ODBCDRV_CONN *pConn, const NETXMS_WCHAR *name)
1228 {
1229 int rc = DBIsTableExist_Failure;
1230
1231 MutexLock(pConn->mutexQuery);
1232
1233 SQLHSTMT sqlStatement;
1234 SQLRETURN iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &sqlStatement);
1235 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
1236 {
1237 if (m_useUnicode)
1238 {
1239 #if defined(_WIN32) || defined(UNICODE_UCS2)
1240 iResult = SQLTablesW(sqlStatement, NULL, 0, NULL, 0, (SQLWCHAR *)name, SQL_NTS, NULL, 0);
1241 #else
1242 SQLWCHAR *temp = UCS2StringFromUCS4String(name);
1243 iResult = SQLTablesW(sqlStatement, NULL, 0, NULL, 0, (SQLWCHAR *)temp, SQL_NTS, NULL, 0);
1244 free(temp);
1245 #endif
1246 }
1247 else
1248 {
1249 char *temp = MBStringFromWideString(name);
1250 iResult = SQLTablesA(sqlStatement, NULL, 0, NULL, 0, (SQLCHAR *)temp, SQL_NTS, NULL, 0);
1251 free(temp);
1252 }
1253 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
1254 {
1255 ODBCDRV_QUERY_RESULT *pResult = ProcessSelectResults(sqlStatement);
1256 rc = (DrvGetNumRows(pResult) > 0) ? DBIsTableExist_Found : DBIsTableExist_NotFound;
1257 DrvFreeResult(pResult);
1258 }
1259 SQLFreeHandle(SQL_HANDLE_STMT, sqlStatement);
1260 }
1261
1262 MutexUnlock(pConn->mutexQuery);
1263 return rc;
1264 }
1265
1266 #ifdef _WIN32
1267
1268 /**
1269 * DLL Entry point
1270 */
1271 bool WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
1272 {
1273 if (dwReason == DLL_PROCESS_ATTACH)
1274 DisableThreadLibraryCalls(hInstance);
1275 return true;
1276 }
1277
1278 #endif