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