7ba5b8a09fad67836bbeb88c01344928c50132e3
[public/netxms.git] / src / db / dbdrv / informix / informix.cpp
1 /*
2 ** Informix Database Driver
3 ** Copyright (C) 2010-2015 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 #include "informixdrv.h"
22
23 DECLARE_DRIVER_HEADER("INFORMIX")
24
25 /**
26 * Data buffer size
27 */
28 #define DATA_BUFFER_SIZE 65536
29
30 /**
31 * Convert INFORMIX state to NetXMS database error code and get error text
32 */
33 static DWORD GetSQLErrorInfo(SQLSMALLINT nHandleType, SQLHANDLE hHandle, WCHAR *errorText)
34 {
35 SQLRETURN nRet;
36 SQLSMALLINT nChars;
37 DWORD dwError;
38 SQLWCHAR sqlState[32];
39
40 // Get state information and convert it to NetXMS database error code
41 nRet = SQLGetDiagFieldW(nHandleType, hHandle, 1, SQL_DIAG_SQLSTATE, sqlState, 16, &nChars);
42 if (nRet == SQL_SUCCESS)
43 {
44 if (
45 (!wcscmp(sqlState, L"08003")) || // Connection does not exist
46 (!wcscmp(sqlState, L"08S01")) || // Communication link failure
47 (!wcscmp(sqlState, L"HYT00")) || // Timeout expired
48 (!wcscmp(sqlState, L"HYT01")) || // Connection timeout expired
49 (!wcscmp(sqlState, L"08506"))) // SQL30108N: A connection failed but has been re-established.
50 {
51 dwError = DBERR_CONNECTION_LOST;
52 }
53 else
54 {
55 dwError = DBERR_OTHER_ERROR;
56 }
57 }
58 else
59 {
60 dwError = DBERR_OTHER_ERROR;
61 }
62
63 // Get error message
64 if (errorText != NULL)
65 {
66 errorText[0] = L'[';
67 wcscpy(&errorText[1], sqlState);
68 int pos = (int)wcslen(errorText);
69 errorText[pos++] = L']';
70 errorText[pos++] = L' ';
71 nRet = SQLGetDiagFieldW(nHandleType, hHandle, 1, SQL_DIAG_MESSAGE_TEXT, &errorText[pos], DBDRV_MAX_ERROR_TEXT - pos, &nChars);
72 if (nRet == SQL_SUCCESS)
73 {
74 RemoveTrailingCRLFW(errorText);
75 }
76 else
77 {
78 wcscpy(&errorText[pos], L"Unable to obtain description for this error");
79 }
80 }
81
82 return dwError;
83 }
84
85
86 //
87 // Clear any pending result sets on given statement
88 //
89
90 static void ClearPendingResults(SQLHSTMT statement)
91 {
92 while(1)
93 {
94 SQLRETURN rc = SQLMoreResults(statement);
95 if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO))
96 {
97 break;
98 }
99 }
100 }
101
102
103 //
104 // Prepare string for using in SQL query - enclose in quotes and escape as needed
105 //
106
107 extern "C" WCHAR EXPORT *DrvPrepareStringW(const WCHAR *str)
108 {
109 int len = (int)wcslen(str) + 3; // + two quotes and \0 at the end
110 int bufferSize = len + 128;
111 WCHAR *out = (WCHAR *)malloc(bufferSize * sizeof(WCHAR));
112 out[0] = L'\'';
113
114 const WCHAR *src = str;
115 int outPos;
116 for(outPos = 1; *src != 0; src++)
117 {
118 if (*src == L'\'')
119 {
120 len++;
121 if (len >= bufferSize)
122 {
123 bufferSize += 128;
124 out = (WCHAR *)realloc(out, bufferSize * sizeof(WCHAR));
125 }
126 out[outPos++] = L'\'';
127 out[outPos++] = L'\'';
128 }
129 else
130 {
131 out[outPos++] = *src;
132 }
133 }
134 out[outPos++] = L'\'';
135 out[outPos++] = 0;
136
137 return out;
138 }
139
140 extern "C" char EXPORT *DrvPrepareStringA(const char *str)
141 {
142 int len = (int)strlen(str) + 3; // + two quotes and \0 at the end
143 int bufferSize = len + 128;
144 char *out = (char *)malloc(bufferSize);
145 out[0] = '\'';
146
147 const char *src = str;
148 int outPos;
149 for(outPos = 1; *src != 0; src++)
150 {
151 if (*src == '\'')
152 {
153 len++;
154 if (len >= bufferSize)
155 {
156 bufferSize += 128;
157 out = (char *)realloc(out, bufferSize);
158 }
159 out[outPos++] = '\'';
160 out[outPos++] = '\'';
161 }
162 else
163 {
164 out[outPos++] = *src;
165 }
166 }
167 out[outPos++] = '\'';
168 out[outPos++] = 0;
169
170 return out;
171 }
172
173 /**
174 * Initialize driver
175 */
176 extern "C" bool EXPORT DrvInit(const char *cmdLine)
177 {
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 * database should be set to Informix source name. Host and schema are ignored
191 */
192 extern "C" DBDRV_CONNECTION EXPORT DrvConnect(char *host, char *login, char *password, char *database, const char *schema, WCHAR *errorText)
193 {
194 long iResult;
195 INFORMIX_CONN *pConn;
196
197 // Allocate our connection structure
198 pConn = (INFORMIX_CONN *)malloc(sizeof(INFORMIX_CONN));
199
200 // Allocate environment
201 iResult = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &pConn->sqlEnv);
202 if ((iResult != SQL_SUCCESS) && (iResult != SQL_SUCCESS_WITH_INFO))
203 {
204 wcscpy(errorText, L"Cannot allocate environment handle");
205 goto connect_failure_0;
206 }
207
208 // Set required ODBC version
209 iResult = SQLSetEnvAttr(pConn->sqlEnv, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
210 if ((iResult != SQL_SUCCESS) && (iResult != SQL_SUCCESS_WITH_INFO))
211 {
212 wcscpy(errorText, L"Call to SQLSetEnvAttr failed");
213 goto connect_failure_1;
214 }
215
216 // Allocate connection handle, set timeout
217 iResult = SQLAllocHandle(SQL_HANDLE_DBC, pConn->sqlEnv, &pConn->sqlConn);
218 if ((iResult != SQL_SUCCESS) && (iResult != SQL_SUCCESS_WITH_INFO))
219 {
220 wcscpy(errorText, L"Cannot allocate connection handle");
221 goto connect_failure_1;
222 }
223 SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER *)15, 0);
224 SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *)30, 0);
225
226 // Connect to the database
227 SQLSMALLINT outLen;
228 char connectString[1024];
229 snprintf(connectString, 1024, "DSN=%s;UID=%s;PWD=%s", database, login, password);
230 iResult = SQLDriverConnect(pConn->sqlConn, NULL, (SQLCHAR *)connectString, SQL_NTS, NULL, 0, &outLen, SQL_DRIVER_NOPROMPT);
231 if ((iResult != SQL_SUCCESS) && (iResult != SQL_SUCCESS_WITH_INFO))
232 {
233 GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
234 goto connect_failure_2;
235 }
236
237 // Create mutex
238 pConn->mutexQuery = MutexCreate();
239
240 // Success
241 return (DBDRV_CONNECTION)pConn;
242
243 // Handle failures
244 connect_failure_2:
245 SQLFreeHandle(SQL_HANDLE_DBC, pConn->sqlConn);
246
247 connect_failure_1:
248 SQLFreeHandle(SQL_HANDLE_ENV, pConn->sqlEnv);
249
250 connect_failure_0:
251 free(pConn);
252 return NULL;
253 }
254
255 /**
256 * Disconnect from database
257 */
258 extern "C" void EXPORT DrvDisconnect(INFORMIX_CONN *pConn)
259 {
260 MutexLock(pConn->mutexQuery);
261 MutexUnlock(pConn->mutexQuery);
262 SQLDisconnect(pConn->sqlConn);
263 SQLFreeHandle(SQL_HANDLE_DBC, pConn->sqlConn);
264 SQLFreeHandle(SQL_HANDLE_ENV, pConn->sqlEnv);
265 MutexDestroy(pConn->mutexQuery);
266 free(pConn);
267 }
268
269 /**
270 * Prepare statement
271 */
272 extern "C" DBDRV_STATEMENT EXPORT DrvPrepare(INFORMIX_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText)
273 {
274 long iResult;
275 SQLHSTMT statement;
276 INFORMIX_STATEMENT *result;
277
278 MutexLock(pConn->mutexQuery);
279
280 // Allocate statement handle
281 iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &statement);
282 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
283 {
284 // Prepare statement
285 iResult = SQLPrepareW(statement, (SQLWCHAR *)pwszQuery, SQL_NTS);
286 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
287 {
288 result = (INFORMIX_STATEMENT *)malloc(sizeof(INFORMIX_STATEMENT));
289 result->handle = statement;
290 result->buffers = new Array(0, 16, true);
291 result->connection = pConn;
292 *pdwError = DBERR_SUCCESS;
293 }
294 else
295 {
296 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, statement, errorText);
297 SQLFreeHandle(SQL_HANDLE_STMT, statement);
298 result = NULL;
299 }
300 }
301 else
302 {
303 *pdwError = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
304 result = NULL;
305 }
306
307 MutexUnlock(pConn->mutexQuery);
308 return result;
309 }
310
311
312 //
313 // Bind parameter to statement
314 //
315
316 extern "C" void EXPORT DrvBind(INFORMIX_STATEMENT *statement, int pos, int sqlType, int cType, void *buffer, int allocType)
317 {
318 static SQLSMALLINT odbcSqlType[] = { SQL_VARCHAR, SQL_INTEGER, SQL_BIGINT, SQL_DOUBLE, SQL_LONGVARCHAR };
319 static SQLSMALLINT odbcCType[] = { SQL_C_WCHAR, SQL_C_SLONG, SQL_C_ULONG, SQL_C_SBIGINT, SQL_C_UBIGINT, SQL_C_DOUBLE };
320 static DWORD bufferSize[] = { 0, sizeof(LONG), sizeof(DWORD), sizeof(INT64), sizeof(QWORD), sizeof(double) };
321
322 int length = (int)wcslen((WCHAR *)buffer) + 1;
323
324 SQLPOINTER sqlBuffer;
325 switch(allocType)
326 {
327 case DB_BIND_STATIC:
328 sqlBuffer = buffer;
329 break;
330 case DB_BIND_DYNAMIC:
331 sqlBuffer = buffer;
332 statement->buffers->add(sqlBuffer);
333 break;
334 case DB_BIND_TRANSIENT:
335 sqlBuffer = nx_memdup(buffer, (cType == DB_CTYPE_STRING) ? (DWORD)(length * sizeof(WCHAR)) : bufferSize[cType]);
336 statement->buffers->add(sqlBuffer);
337 break;
338 default:
339 return; // Invalid call
340 }
341 SQLBindParameter(statement->handle, pos, SQL_PARAM_INPUT, odbcCType[cType], odbcSqlType[sqlType],
342 (cType == DB_CTYPE_STRING) ? length : 0, 0, sqlBuffer, 0, NULL);
343 }
344
345
346 //
347 // Execute prepared statement
348 //
349
350 extern "C" DWORD EXPORT DrvExecute(INFORMIX_CONN *pConn, INFORMIX_STATEMENT *statement, WCHAR *errorText)
351 {
352 DWORD dwResult;
353
354 MutexLock(pConn->mutexQuery);
355 long rc = SQLExecute(statement->handle);
356 if (
357 (rc == SQL_SUCCESS) ||
358 (rc == SQL_SUCCESS_WITH_INFO) ||
359 (rc == SQL_NO_DATA))
360 {
361 ClearPendingResults(statement->handle);
362 dwResult = DBERR_SUCCESS;
363 }
364 else
365 {
366 dwResult = GetSQLErrorInfo(SQL_HANDLE_STMT, statement->handle, errorText);
367 }
368 MutexUnlock(pConn->mutexQuery);
369 return dwResult;
370 }
371
372
373 //
374 // Destroy prepared statement
375 //
376
377 extern "C" void EXPORT DrvFreeStatement(INFORMIX_STATEMENT *statement)
378 {
379 if (statement == NULL)
380 {
381 return;
382 }
383
384 MutexLock(statement->connection->mutexQuery);
385 SQLFreeHandle(SQL_HANDLE_STMT, statement->handle);
386 MutexUnlock(statement->connection->mutexQuery);
387 delete statement->buffers;
388 free(statement);
389 }
390
391 /**
392 * Perform non-SELECT query
393 */
394 extern "C" DWORD EXPORT DrvQuery(INFORMIX_CONN *pConn, WCHAR *pwszQuery, WCHAR *errorText)
395 {
396 DWORD dwResult;
397
398 MutexLock(pConn->mutexQuery);
399
400 // Allocate statement handle
401 SQLHSTMT sqlStatement;
402 SQLRETURN iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &sqlStatement);
403 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
404 {
405 // Execute statement
406 iResult = SQLExecDirectW(sqlStatement, (SQLWCHAR *)pwszQuery, SQL_NTS);
407 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO) || (iResult == SQL_NO_DATA))
408 {
409 dwResult = DBERR_SUCCESS;
410 }
411 else
412 {
413 dwResult = GetSQLErrorInfo(SQL_HANDLE_STMT, sqlStatement, errorText);
414 }
415 SQLFreeHandle(SQL_HANDLE_STMT, sqlStatement);
416 }
417 else
418 {
419 dwResult = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
420 }
421
422 MutexUnlock(pConn->mutexQuery);
423 return dwResult;
424 }
425
426 /**
427 * Process results of SELECT query
428 */
429 static INFORMIX_QUERY_RESULT *ProcessSelectResults(SQLHSTMT statement)
430 {
431 // Allocate result buffer and determine column info
432 INFORMIX_QUERY_RESULT *pResult = (INFORMIX_QUERY_RESULT *)malloc(sizeof(INFORMIX_QUERY_RESULT));
433 short wNumCols;
434 SQLNumResultCols(statement, &wNumCols);
435 pResult->numColumns = wNumCols;
436 pResult->numRows = 0;
437 pResult->values = NULL;
438
439 BYTE *pDataBuffer = (BYTE *)malloc(DATA_BUFFER_SIZE * sizeof(wchar_t));
440
441 // Get column names
442 pResult->columnNames = (char **)malloc(sizeof(char *) * pResult->numColumns);
443 for(int i = 0; i < (int)pResult->numColumns; i++)
444 {
445 char name[256];
446 SQLSMALLINT len;
447
448 SQLRETURN iResult = SQLColAttribute(statement, (SQLSMALLINT)(i + 1), SQL_DESC_NAME, (SQLPOINTER)name, 256, &len, NULL);
449 if (
450 (iResult == SQL_SUCCESS) ||
451 (iResult == SQL_SUCCESS_WITH_INFO))
452 {
453 name[len] = 0;
454 pResult->columnNames[i] = strdup(name);
455 }
456 else
457 {
458 pResult->columnNames[i] = strdup("");
459 }
460 }
461
462 // Fetch all data
463 long iCurrValue = 0;
464 SQLRETURN iResult;
465 while(iResult = SQLFetch(statement), (iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
466 {
467 pResult->numRows++;
468 pResult->values = (WCHAR **)realloc(pResult->values, sizeof(WCHAR *) * (pResult->numRows * pResult->numColumns));
469 for(int i = 1; i <= (int)pResult->numColumns; i++)
470 {
471 SQLLEN iDataSize;
472
473 pDataBuffer[0] = 0;
474 iResult = SQLGetData(statement, (short)i, SQL_C_WCHAR, pDataBuffer, DATA_BUFFER_SIZE, &iDataSize);
475 if (iDataSize != SQL_NULL_DATA)
476 {
477 pResult->values[iCurrValue++] = wcsdup((const WCHAR *)pDataBuffer);
478 }
479 else
480 {
481 pResult->values[iCurrValue++] = wcsdup(L"");
482 }
483 }
484 }
485
486 free(pDataBuffer);
487 return pResult;
488 }
489
490 /**
491 * Perform SELECT query
492 */
493 extern "C" DBDRV_RESULT EXPORT DrvSelect(INFORMIX_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText)
494 {
495 INFORMIX_QUERY_RESULT *pResult = NULL;
496
497 MutexLock(pConn->mutexQuery);
498
499 // Allocate statement handle
500 SQLHSTMT sqlStatement;
501 SQLRETURN iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &sqlStatement);
502 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
503 {
504 // Execute statement
505 iResult = SQLExecDirectW(sqlStatement, (SQLWCHAR *)pwszQuery, SQL_NTS);
506 if (
507 (iResult == SQL_SUCCESS) ||
508 (iResult == SQL_SUCCESS_WITH_INFO))
509 {
510 pResult = ProcessSelectResults(sqlStatement);
511 *pdwError = DBERR_SUCCESS;
512 }
513 else
514 {
515 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, sqlStatement, errorText);
516 }
517 SQLFreeHandle(SQL_HANDLE_STMT, sqlStatement);
518 }
519 else
520 {
521 *pdwError = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
522 }
523
524 MutexUnlock(pConn->mutexQuery);
525 return pResult;
526 }
527
528 /**
529 * Perform SELECT query using prepared statement
530 */
531 extern "C" DBDRV_RESULT EXPORT DrvSelectPrepared(INFORMIX_CONN *pConn, INFORMIX_STATEMENT *statement, DWORD *pdwError, WCHAR *errorText)
532 {
533 INFORMIX_QUERY_RESULT *pResult = NULL;
534
535 MutexLock(pConn->mutexQuery);
536 long rc = SQLExecute(statement->handle);
537 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
538 {
539 pResult = ProcessSelectResults(statement->handle);
540 *pdwError = DBERR_SUCCESS;
541 }
542 else
543 {
544 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, statement->handle, errorText);
545 }
546 MutexUnlock(pConn->mutexQuery);
547 return pResult;
548 }
549
550 /**
551 * Get field length from result
552 */
553 extern "C" LONG EXPORT DrvGetFieldLength(INFORMIX_QUERY_RESULT *pResult, int iRow, int iColumn)
554 {
555 LONG nLen = -1;
556
557 if (pResult != NULL)
558 {
559 if ((iRow < pResult->numRows) && (iRow >= 0) &&
560 (iColumn < pResult->numColumns) && (iColumn >= 0))
561 {
562 nLen = (LONG)wcslen(pResult->values[iRow * pResult->numColumns + iColumn]);
563 }
564 }
565 return nLen;
566 }
567
568 /**
569 * Get field value from result
570 */
571 extern "C" WCHAR EXPORT *DrvGetField(INFORMIX_QUERY_RESULT *pResult, int iRow, int iColumn,
572 WCHAR *pBuffer, int nBufSize)
573 {
574 WCHAR *pValue = NULL;
575
576 if (pResult != NULL)
577 {
578 if ((iRow < pResult->numRows) && (iRow >= 0) &&
579 (iColumn < pResult->numColumns) && (iColumn >= 0))
580 {
581 #ifdef _WIN32
582 wcsncpy_s(pBuffer, nBufSize, pResult->values[iRow * pResult->numColumns + iColumn], _TRUNCATE);
583 #else
584 wcsncpy(pBuffer, pResult->values[iRow * pResult->numColumns + iColumn], nBufSize);
585 pBuffer[nBufSize - 1] = 0;
586 #endif
587 pValue = pBuffer;
588 }
589 }
590 return pValue;
591 }
592
593
594 //
595 // Get number of rows in result
596 //
597
598 extern "C" int EXPORT DrvGetNumRows(INFORMIX_QUERY_RESULT *pResult)
599 {
600 return (pResult != NULL) ? pResult->numRows : 0;
601 }
602
603
604 //
605 // Get column count in query result
606 //
607
608 extern "C" int EXPORT DrvGetColumnCount(INFORMIX_QUERY_RESULT *pResult)
609 {
610 return (pResult != NULL) ? pResult->numColumns : 0;
611 }
612
613 /**
614 * Get column name in query result
615 */
616 extern "C" const char EXPORT *DrvGetColumnName(INFORMIX_QUERY_RESULT *pResult, int column)
617 {
618 return ((pResult != NULL) && (column >= 0) && (column < pResult->numColumns)) ? pResult->columnNames[column] : NULL;
619 }
620
621 /**
622 * Free SELECT results
623 */
624 extern "C" void EXPORT DrvFreeResult(INFORMIX_QUERY_RESULT *pResult)
625 {
626 if (pResult == NULL)
627 return;
628
629 int i, iNumValues;
630
631 iNumValues = pResult->numColumns * pResult->numRows;
632 for(i = 0; i < iNumValues; i++)
633 {
634 free(pResult->values[i]);
635 }
636 free(pResult->values);
637
638 for(i = 0; i < pResult->numColumns; i++)
639 {
640 free(pResult->columnNames[i]);
641 }
642 free(pResult->columnNames);
643
644 free(pResult);
645 }
646
647 /**
648 * Perform unbuffered SELECT query
649 */
650 extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectUnbuffered(INFORMIX_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText)
651 {
652 INFORMIX_UNBUFFERED_QUERY_RESULT *pResult = NULL;
653 long iResult;
654 short wNumCols;
655 int i;
656
657 MutexLock(pConn->mutexQuery);
658
659 // Allocate statement handle
660 SQLHSTMT sqlStatement;
661 iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &sqlStatement);
662 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
663 {
664 // Execute statement
665 iResult = SQLExecDirectW(sqlStatement, (SQLWCHAR *)pwszQuery, SQL_NTS);
666 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
667 {
668 // Allocate result buffer and determine column info
669 pResult = (INFORMIX_UNBUFFERED_QUERY_RESULT *)malloc(sizeof(INFORMIX_UNBUFFERED_QUERY_RESULT));
670 pResult->sqlStatement = sqlStatement;
671 pResult->isPrepared = false;
672
673 SQLNumResultCols(sqlStatement, &wNumCols);
674 pResult->numColumns = wNumCols;
675 pResult->pConn = pConn;
676 pResult->noMoreRows = false;
677
678 // Get column names
679 pResult->columnNames = (char **)malloc(sizeof(char *) * pResult->numColumns);
680 for(i = 0; i < pResult->numColumns; i++)
681 {
682 SQLWCHAR name[256];
683 SQLSMALLINT len;
684
685 iResult = SQLColAttributeW(sqlStatement, (SQLSMALLINT)(i + 1), SQL_DESC_NAME, name, 256, &len, NULL);
686 if (
687 (iResult == SQL_SUCCESS) ||
688 (iResult == SQL_SUCCESS_WITH_INFO))
689 {
690 name[len] = 0;
691 pResult->columnNames[i] = MBStringFromWideString(name);
692 }
693 else
694 {
695 pResult->columnNames[i] = strdup("");
696 }
697 }
698
699 *pdwError = DBERR_SUCCESS;
700 }
701 else
702 {
703 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, sqlStatement, errorText);
704 // Free statement handle if query failed
705 SQLFreeHandle(SQL_HANDLE_STMT, sqlStatement);
706 }
707 }
708 else
709 {
710 *pdwError = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
711 }
712
713 if (pResult == NULL) // Unlock mutex if query has failed
714 {
715 MutexUnlock(pConn->mutexQuery);
716 }
717 return pResult;
718 }
719
720 /**
721 * Perform unbuffered SELECT query using prepared statement
722 */
723 extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectPreparedUnbuffered(INFORMIX_CONN *pConn, INFORMIX_STATEMENT *stmt, DWORD *pdwError, WCHAR *errorText)
724 {
725 INFORMIX_UNBUFFERED_QUERY_RESULT *pResult = NULL;
726
727 MutexLock(pConn->mutexQuery);
728 SQLRETURN rc = SQLExecute(stmt->handle);
729 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
730 {
731 // Allocate result buffer and determine column info
732 pResult = (INFORMIX_UNBUFFERED_QUERY_RESULT *)malloc(sizeof(INFORMIX_UNBUFFERED_QUERY_RESULT));
733 pResult->sqlStatement = stmt->handle;
734 pResult->isPrepared = true;
735
736 short wNumCols;
737 SQLNumResultCols(pResult->sqlStatement, &wNumCols);
738 pResult->numColumns = wNumCols;
739 pResult->pConn = pConn;
740 pResult->noMoreRows = false;
741
742 // Get column names
743 pResult->columnNames = (char **)malloc(sizeof(char *) * pResult->numColumns);
744 for(int i = 0; i < pResult->numColumns; i++)
745 {
746 SQLWCHAR name[256];
747 SQLSMALLINT len;
748
749 rc = SQLColAttributeW(pResult->sqlStatement, (SQLSMALLINT)(i + 1), SQL_DESC_NAME, name, 256, &len, NULL);
750 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
751 {
752 name[len] = 0;
753 pResult->columnNames[i] = MBStringFromUCS2String((UCS2CHAR *)name);
754 }
755 else
756 {
757 pResult->columnNames[i] = strdup("");
758 }
759 }
760
761 *pdwError = DBERR_SUCCESS;
762 }
763 else
764 {
765 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, stmt->handle, errorText);
766 }
767
768 if (pResult == NULL) // Unlock mutex if query has failed
769 MutexUnlock(pConn->mutexQuery);
770 return pResult;
771 }
772
773 /**
774 * Fetch next result line from unbuffered SELECT results
775 */
776 extern "C" bool EXPORT DrvFetch(INFORMIX_UNBUFFERED_QUERY_RESULT *pResult)
777 {
778 bool bResult = false;
779
780 if (pResult != NULL)
781 {
782 long iResult;
783
784 iResult = SQLFetch(pResult->sqlStatement);
785 bResult = ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO));
786 if (!bResult)
787 {
788 pResult->noMoreRows = true;
789 }
790 }
791 return bResult;
792 }
793
794 /**
795 * Get field length from unbuffered query result
796 */
797 extern "C" LONG EXPORT DrvGetFieldLengthUnbuffered(INFORMIX_UNBUFFERED_QUERY_RESULT *pResult, int iColumn)
798 {
799 LONG nLen = -1;
800
801 if (pResult != NULL)
802 {
803 if ((iColumn < pResult->numColumns) && (iColumn >= 0))
804 {
805 SQLLEN dataSize;
806 char temp[1];
807 long rc = SQLGetData(pResult->sqlStatement, (short)iColumn + 1, SQL_C_CHAR, temp, 0, &dataSize);
808 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
809 {
810 nLen = (LONG)dataSize;
811 }
812 }
813 }
814 return nLen;
815 }
816
817 /**
818 * Get field from current row in unbuffered query result
819 */
820 extern "C" WCHAR EXPORT *DrvGetFieldUnbuffered(INFORMIX_UNBUFFERED_QUERY_RESULT *pResult, int iColumn, WCHAR *pBuffer, int iBufSize)
821 {
822 SQLLEN iDataSize;
823 long iResult;
824
825 // Check if we have valid result handle
826 if (pResult == NULL)
827 {
828 return NULL;
829 }
830
831 // Check if there are valid fetched row
832 if (pResult->noMoreRows)
833 {
834 return NULL;
835 }
836
837 if ((iColumn >= 0) && (iColumn < pResult->numColumns))
838 {
839 // At least on HP-UX driver expects length in chars, not bytes
840 // otherwise it crashes
841 // TODO: check other platforms
842 iResult = SQLGetData(pResult->sqlStatement, (short)iColumn + 1, SQL_C_WCHAR, pBuffer, iBufSize, &iDataSize);
843 if (((iResult != SQL_SUCCESS) && (iResult != SQL_SUCCESS_WITH_INFO)) || (iDataSize == SQL_NULL_DATA))
844 {
845 pBuffer[0] = 0;
846 }
847 }
848 else
849 {
850 pBuffer[0] = 0;
851 }
852 return pBuffer;
853 }
854
855 /**
856 * Get column count in unbuffered query result
857 */
858 extern "C" int EXPORT DrvGetColumnCountUnbuffered(INFORMIX_UNBUFFERED_QUERY_RESULT *pResult)
859 {
860 return (pResult != NULL) ? pResult->numColumns : 0;
861 }
862
863 /**
864 * Get column name in unbuffered query result
865 */
866 extern "C" const char EXPORT *DrvGetColumnNameUnbuffered(INFORMIX_UNBUFFERED_QUERY_RESULT *pResult, int column)
867 {
868 return ((pResult != NULL) && (column >= 0) && (column < pResult->numColumns)) ? pResult->columnNames[column] : NULL;
869 }
870
871 /**
872 * Destroy result of unbuffered query
873 */
874 extern "C" void EXPORT DrvFreeUnbufferedResult(INFORMIX_UNBUFFERED_QUERY_RESULT *pResult)
875 {
876 if (pResult == NULL)
877 return;
878
879 if (pResult->isPrepared)
880 SQLCloseCursor(pResult->sqlStatement);
881 else
882 SQLFreeHandle(SQL_HANDLE_STMT, pResult->sqlStatement);
883 MutexUnlock(pResult->pConn->mutexQuery);
884
885 for(int i = 0; i < pResult->numColumns; i++)
886 {
887 free(pResult->columnNames[i]);
888 }
889 free(pResult->columnNames);
890
891 free(pResult);
892 }
893
894 /**
895 * Begin transaction
896 */
897 extern "C" DWORD EXPORT DrvBegin(INFORMIX_CONN *pConn)
898 {
899 SQLRETURN nRet;
900 DWORD dwResult;
901
902 if (pConn == NULL)
903 {
904 return DBERR_INVALID_HANDLE;
905 }
906
907 MutexLock(pConn->mutexQuery);
908 nRet = SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, 0);
909 if ((nRet == SQL_SUCCESS) || (nRet == SQL_SUCCESS_WITH_INFO))
910 {
911 dwResult = DBERR_SUCCESS;
912 }
913 else
914 {
915 dwResult = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, NULL);
916 }
917 MutexUnlock(pConn->mutexQuery);
918 return dwResult;
919 }
920
921 /**
922 * Commit transaction
923 */
924 extern "C" DWORD EXPORT DrvCommit(INFORMIX_CONN *pConn)
925 {
926 SQLRETURN nRet;
927
928 if (pConn == NULL)
929 {
930 return DBERR_INVALID_HANDLE;
931 }
932
933 MutexLock(pConn->mutexQuery);
934 nRet = SQLEndTran(SQL_HANDLE_DBC, pConn->sqlConn, SQL_COMMIT);
935 SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0);
936 MutexUnlock(pConn->mutexQuery);
937 return ((nRet == SQL_SUCCESS) || (nRet == SQL_SUCCESS_WITH_INFO)) ? DBERR_SUCCESS : DBERR_OTHER_ERROR;
938 }
939
940 /**
941 * Rollback transaction
942 */
943 extern "C" DWORD EXPORT DrvRollback(INFORMIX_CONN *pConn)
944 {
945 SQLRETURN nRet;
946
947 if (pConn == NULL)
948 {
949 return DBERR_INVALID_HANDLE;
950 }
951
952 MutexLock(pConn->mutexQuery);
953 nRet = SQLEndTran(SQL_HANDLE_DBC, pConn->sqlConn, SQL_ROLLBACK);
954 SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0);
955 MutexUnlock(pConn->mutexQuery);
956 return ((nRet == SQL_SUCCESS) || (nRet == SQL_SUCCESS_WITH_INFO)) ? DBERR_SUCCESS : DBERR_OTHER_ERROR;
957 }
958
959 /**
960 * Check if table exist
961 */
962 extern "C" int EXPORT DrvIsTableExist(INFORMIX_CONN *pConn, const WCHAR *name)
963 {
964 WCHAR query[256];
965 swprintf(query, 256, L"SELECT count(*) FROM informix.systables WHERE tabtype='T' AND upper(tabname)=upper('%ls')", name);
966 DWORD error;
967 WCHAR errorText[DBDRV_MAX_ERROR_TEXT];
968 int rc = DBIsTableExist_Failure;
969 INFORMIX_QUERY_RESULT *hResult = (INFORMIX_QUERY_RESULT *)DrvSelect(pConn, query, &error, errorText);
970 if (hResult != NULL)
971 {
972 WCHAR buffer[64] = L"";
973 DrvGetField(hResult, 0, 0, buffer, 64);
974 rc = (wcstol(buffer, NULL, 10) > 0) ? DBIsTableExist_Found : DBIsTableExist_NotFound;
975 DrvFreeResult(hResult);
976 }
977 return rc;
978 }
979
980 #ifdef _WIN32
981
982 /**
983 * DLL Entry point
984 */
985 bool WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
986 {
987 if (dwReason == DLL_PROCESS_ATTACH)
988 DisableThreadLibraryCalls(hInstance);
989 return true;
990 }
991
992 #endif