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