fixed memory leak in Oracle DB driver
[public/netxms.git] / src / db / dbdrv / oracle / oracle.cpp
1 /*
2 ** Oracle Database Driver
3 ** Copyright (C) 2007-2016 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: oracle.cpp
20 **
21 **/
22
23 #include "oracledrv.h"
24
25 DECLARE_DRIVER_HEADER("ORACLE")
26
27 static DWORD DrvQueryInternal(ORACLE_CONN *pConn, const WCHAR *pwszQuery, WCHAR *errorText);
28
29 /**
30 * Prepare string for using in SQL query - enclose in quotes and escape as needed
31 */
32 extern "C" WCHAR EXPORT *DrvPrepareStringW(const WCHAR *str)
33 {
34 int len = (int)wcslen(str) + 3; // + two quotes and \0 at the end
35 int bufferSize = len + 128;
36 WCHAR *out = (WCHAR *)malloc(bufferSize * sizeof(WCHAR));
37 out[0] = L'\'';
38
39 const WCHAR *src = str;
40 int outPos;
41 for(outPos = 1; *src != 0; src++)
42 {
43 if (*src == L'\'')
44 {
45 len++;
46 if (len >= bufferSize)
47 {
48 bufferSize += 128;
49 out = (WCHAR *)realloc(out, bufferSize * sizeof(WCHAR));
50 }
51 out[outPos++] = L'\'';
52 out[outPos++] = L'\'';
53 }
54 else
55 {
56 out[outPos++] = *src;
57 }
58 }
59 out[outPos++] = L'\'';
60 out[outPos++] = 0;
61
62 return out;
63 }
64
65 /**
66 * Prepare string for using in SQL query - enclose in quotes and escape as needed
67 */
68 extern "C" char EXPORT *DrvPrepareStringA(const char *str)
69 {
70 int len = (int)strlen(str) + 3; // + two quotes and \0 at the end
71 int bufferSize = len + 128;
72 char *out = (char *)malloc(bufferSize);
73 out[0] = '\'';
74
75 const char *src = str;
76 int outPos;
77 for(outPos = 1; *src != 0; src++)
78 {
79 if (*src == '\'')
80 {
81 len++;
82 if (len >= bufferSize)
83 {
84 bufferSize += 128;
85 out = (char *)realloc(out, bufferSize);
86 }
87 out[outPos++] = '\'';
88 out[outPos++] = '\'';
89 }
90 else
91 {
92 out[outPos++] = *src;
93 }
94 }
95 out[outPos++] = '\'';
96 out[outPos++] = 0;
97
98 return out;
99 }
100
101 /**
102 * Initialize driver
103 */
104 extern "C" bool EXPORT DrvInit(const char *cmdLine)
105 {
106 return true;
107 }
108
109 /**
110 * Unload handler
111 */
112 extern "C" void EXPORT DrvUnload()
113 {
114 OCITerminate(OCI_DEFAULT);
115 }
116
117 /**
118 * Get error text from error handle
119 */
120 static void GetErrorFromHandle(OCIError *handle, sb4 *errorCode, WCHAR *errorText)
121 {
122 #if UNICODE_UCS2
123 OCIErrorGet(handle, 1, NULL, errorCode, (text *)errorText, DBDRV_MAX_ERROR_TEXT, OCI_HTYPE_ERROR);
124 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
125 #else
126 UCS2CHAR buffer[DBDRV_MAX_ERROR_TEXT];
127
128 OCIErrorGet(handle, 1, NULL, errorCode, (text *)buffer, DBDRV_MAX_ERROR_TEXT, OCI_HTYPE_ERROR);
129 buffer[DBDRV_MAX_ERROR_TEXT - 1] = 0;
130 ucs2_to_ucs4(buffer, ucs2_strlen(buffer) + 1, errorText, DBDRV_MAX_ERROR_TEXT);
131 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
132 #endif
133 RemoveTrailingCRLFW(errorText);
134 }
135
136 /**
137 * Set last error text
138 */
139 static void SetLastError(ORACLE_CONN *pConn)
140 {
141 GetErrorFromHandle(pConn->handleError, &pConn->lastErrorCode, pConn->lastErrorText);
142 }
143
144 /**
145 * Check if last error was caused by lost connection to server
146 */
147 static DWORD IsConnectionError(ORACLE_CONN *conn)
148 {
149 ub4 nStatus = 0;
150 OCIAttrGet(conn->handleServer, OCI_HTYPE_SERVER, &nStatus, NULL, OCI_ATTR_SERVER_STATUS, conn->handleError);
151 return (nStatus == OCI_SERVER_NOT_CONNECTED) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR;
152 }
153
154 /**
155 * Destroy query result
156 */
157 static void DestroyQueryResult(ORACLE_RESULT *pResult)
158 {
159 int i, nCount;
160
161 nCount = pResult->nCols * pResult->nRows;
162 for(i = 0; i < nCount; i++)
163 free(pResult->pData[i]);
164 free(pResult->pData);
165
166 for(i = 0; i < pResult->nCols; i++)
167 free(pResult->columnNames[i]);
168 free(pResult->columnNames);
169
170 free(pResult);
171 }
172
173 /**
174 * Connect to database
175 */
176 extern "C" DBDRV_CONNECTION EXPORT DrvConnect(const char *host, const char *login, const char *password,
177 const char *database, const char *schema, WCHAR *errorText)
178 {
179 ORACLE_CONN *pConn;
180 UCS2CHAR *pwszStr;
181
182 pConn = (ORACLE_CONN *)malloc(sizeof(ORACLE_CONN));
183 if (pConn != NULL)
184 {
185 memset(pConn, 0, sizeof(ORACLE_CONN));
186
187 if (OCIEnvNlsCreate(&pConn->handleEnv, OCI_THREADED | OCI_NCHAR_LITERAL_REPLACE_OFF,
188 NULL, NULL, NULL, NULL, 0, NULL, OCI_UTF16ID, OCI_UTF16ID) == OCI_SUCCESS)
189 {
190 OCIHandleAlloc(pConn->handleEnv, (void **)&pConn->handleError, OCI_HTYPE_ERROR, 0, NULL);
191 OCIHandleAlloc(pConn->handleEnv, (void **)&pConn->handleServer, OCI_HTYPE_SERVER, 0, NULL);
192 pwszStr = UCS2StringFromMBString(host);
193 if (OCIServerAttach(pConn->handleServer, pConn->handleError,
194 (text *)pwszStr, (sb4)ucs2_strlen(pwszStr) * sizeof(UCS2CHAR), OCI_DEFAULT) == OCI_SUCCESS)
195 {
196 free(pwszStr);
197
198 // Initialize service handle
199 OCIHandleAlloc(pConn->handleEnv, (void **)&pConn->handleService, OCI_HTYPE_SVCCTX, 0, NULL);
200 OCIAttrSet(pConn->handleService, OCI_HTYPE_SVCCTX, pConn->handleServer, 0, OCI_ATTR_SERVER, pConn->handleError);
201
202 // Initialize session handle
203 OCIHandleAlloc(pConn->handleEnv, (void **)&pConn->handleSession, OCI_HTYPE_SESSION, 0, NULL);
204 pwszStr = UCS2StringFromMBString(login);
205 OCIAttrSet(pConn->handleSession, OCI_HTYPE_SESSION, pwszStr,
206 (ub4)ucs2_strlen(pwszStr) * sizeof(UCS2CHAR), OCI_ATTR_USERNAME, pConn->handleError);
207 free(pwszStr);
208 pwszStr = UCS2StringFromMBString(password);
209 OCIAttrSet(pConn->handleSession, OCI_HTYPE_SESSION, pwszStr,
210 (ub4)ucs2_strlen(pwszStr) * sizeof(UCS2CHAR), OCI_ATTR_PASSWORD, pConn->handleError);
211
212 // Authenticate
213 if (OCISessionBegin(pConn->handleService, pConn->handleError,
214 pConn->handleSession, OCI_CRED_RDBMS, OCI_STMT_CACHE) == OCI_SUCCESS)
215 {
216 OCIAttrSet(pConn->handleService, OCI_HTYPE_SVCCTX, pConn->handleSession, 0, OCI_ATTR_SESSION, pConn->handleError);
217 pConn->mutexQueryLock = MutexCreate();
218 pConn->nTransLevel = 0;
219 pConn->lastErrorCode = 0;
220 pConn->lastErrorText[0] = 0;
221 pConn->prefetchLimit = 10;
222
223 if ((schema != NULL) && (schema[0] != 0))
224 {
225 free(pwszStr);
226 pwszStr = UCS2StringFromMBString(schema);
227 OCIAttrSet(pConn->handleSession, OCI_HTYPE_SESSION, pwszStr,
228 (ub4)ucs2_strlen(pwszStr) * sizeof(UCS2CHAR), OCI_ATTR_CURRENT_SCHEMA, pConn->handleError);
229 }
230
231 // Setup session
232 DrvQueryInternal(pConn, L"ALTER SESSION SET NLS_LANGUAGE='AMERICAN' NLS_NUMERIC_CHARACTERS='.,'", NULL);
233
234 UCS2CHAR version[1024];
235 if (OCIServerVersion(pConn->handleService, pConn->handleError, (OraText *)version, sizeof(version), OCI_HTYPE_SVCCTX) == OCI_SUCCESS)
236 {
237 #if UNICODE_UCS4
238 WCHAR *wver = UCS4StringFromUCS2String(version);
239 nxlog_debug(5, _T("ORACLE: connected to %s"), wver);
240 free(wver);
241 #else
242 nxlog_debug(5, _T("ORACLE: connected to %s"), version);
243 #endif
244 }
245 }
246 else
247 {
248 GetErrorFromHandle(pConn->handleError, &pConn->lastErrorCode, errorText);
249 OCIServerDetach(pConn->handleServer, pConn->handleError, OCI_DEFAULT);
250 OCIHandleFree(pConn->handleEnv, OCI_HTYPE_ENV);
251 free(pConn);
252 pConn = NULL;
253 }
254 }
255 else
256 {
257 GetErrorFromHandle(pConn->handleError, &pConn->lastErrorCode, errorText);
258 OCIHandleFree(pConn->handleEnv, OCI_HTYPE_ENV);
259 free(pConn);
260 pConn = NULL;
261 }
262 free(pwszStr);
263 }
264 else
265 {
266 wcscpy(errorText, L"Cannot allocate environment handle");
267 free(pConn);
268 pConn = NULL;
269 }
270 }
271 else
272 {
273 wcscpy(errorText, L"Memory allocation error");
274 }
275
276 return (DBDRV_CONNECTION)pConn;
277 }
278
279 /**
280 * Disconnect from database
281 */
282 extern "C" void EXPORT DrvDisconnect(ORACLE_CONN *pConn)
283 {
284 if (pConn == NULL)
285 return;
286
287 OCISessionEnd(pConn->handleService, pConn->handleError, NULL, OCI_DEFAULT);
288 OCIServerDetach(pConn->handleServer, pConn->handleError, OCI_DEFAULT);
289 OCIHandleFree(pConn->handleEnv, OCI_HTYPE_ENV);
290 MutexDestroy(pConn->mutexQueryLock);
291 free(pConn);
292 }
293
294 /**
295 * Set prefetch limit
296 */
297 extern "C" void EXPORT DrvSetPrefetchLimit(ORACLE_CONN *pConn, int limit)
298 {
299 if (pConn != NULL)
300 pConn->prefetchLimit = limit;
301 }
302
303 /**
304 * Convert query from NetXMS portable format to native Oracle format
305 */
306 static UCS2CHAR *ConvertQuery(WCHAR *query)
307 {
308 #if UNICODE_UCS4
309 UCS2CHAR *srcQuery = UCS2StringFromUCS4String(query);
310 #else
311 UCS2CHAR *srcQuery = query;
312 #endif
313 int count = NumCharsW(query, L'?');
314 if (count == 0)
315 {
316 #if UNICODE_UCS4
317 return srcQuery;
318 #else
319 return wcsdup(query);
320 #endif
321 }
322
323 UCS2CHAR *dstQuery = (UCS2CHAR *)malloc((ucs2_strlen(srcQuery) + count * 3 + 1) * sizeof(UCS2CHAR));
324 bool inString = false;
325 int pos = 1;
326 UCS2CHAR *src, *dst;
327 for(src = srcQuery, dst = dstQuery; *src != 0; src++)
328 {
329 switch(*src)
330 {
331 case '\'':
332 *dst++ = *src;
333 inString = !inString;
334 break;
335 case '\\':
336 *dst++ = *src++;
337 *dst++ = *src;
338 break;
339 case '?':
340 if (inString)
341 {
342 *dst++ = '?';
343 }
344 else
345 {
346 *dst++ = ':';
347 if (pos < 10)
348 {
349 *dst++ = pos + '0';
350 }
351 else if (pos < 100)
352 {
353 *dst++ = pos / 10 + '0';
354 *dst++ = pos % 10 + '0';
355 }
356 else
357 {
358 *dst++ = pos / 100 + '0';
359 *dst++ = (pos % 100) / 10 + '0';
360 *dst++ = pos % 10 + '0';
361 }
362 pos++;
363 }
364 break;
365 default:
366 *dst++ = *src;
367 break;
368 }
369 }
370 *dst = 0;
371 #if UNICODE_UCS4
372 free(srcQuery);
373 #endif
374 return dstQuery;
375 }
376
377 /**
378 * Prepare statement
379 */
380 extern "C" ORACLE_STATEMENT EXPORT *DrvPrepare(ORACLE_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText)
381 {
382 ORACLE_STATEMENT *stmt = NULL;
383 OCIStmt *handleStmt;
384
385 UCS2CHAR *ucs2Query = ConvertQuery(pwszQuery);
386
387 MutexLock(pConn->mutexQueryLock);
388 if (OCIStmtPrepare2(pConn->handleService, &handleStmt, pConn->handleError, (text *)ucs2Query,
389 (ub4)ucs2_strlen(ucs2Query) * sizeof(UCS2CHAR), NULL, 0, OCI_NTV_SYNTAX, OCI_DEFAULT) == OCI_SUCCESS)
390 {
391 stmt = (ORACLE_STATEMENT *)malloc(sizeof(ORACLE_STATEMENT));
392 stmt->connection = pConn;
393 stmt->handleStmt = handleStmt;
394 stmt->bindings = new Array(8, 8, false);
395 stmt->batchBindings = NULL;
396 stmt->buffers = new Array(8, 8, true);
397 stmt->batchMode = false;
398 stmt->batchSize = 0;
399 OCIHandleAlloc(pConn->handleEnv, (void **)&stmt->handleError, OCI_HTYPE_ERROR, 0, NULL);
400 *pdwError = DBERR_SUCCESS;
401 }
402 else
403 {
404 SetLastError(pConn);
405 *pdwError = IsConnectionError(pConn);
406 }
407
408 if (errorText != NULL)
409 {
410 wcsncpy(errorText, pConn->lastErrorText, DBDRV_MAX_ERROR_TEXT);
411 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
412 }
413 MutexUnlock(pConn->mutexQueryLock);
414
415 free(ucs2Query);
416 return stmt;
417 }
418
419 /**
420 * Open batch
421 */
422 extern "C" bool EXPORT DrvOpenBatch(ORACLE_STATEMENT *stmt)
423 {
424 stmt->buffers->clear();
425 if (stmt->batchBindings != NULL)
426 stmt->batchBindings->clear();
427 else
428 stmt->batchBindings = new ObjectArray<OracleBatchBind>(16, 16, true);
429 stmt->batchMode = true;
430 stmt->batchSize = 0;
431 return true;
432 }
433
434 /**
435 * Start next batch row
436 */
437 extern "C" void EXPORT DrvNextBatchRow(ORACLE_STATEMENT *stmt)
438 {
439 if (!stmt->batchMode)
440 return;
441
442 for(int i = 0; i < stmt->batchBindings->size(); i++)
443 {
444 OracleBatchBind *bind = stmt->batchBindings->get(i);
445 if (bind != NULL)
446 bind->addRow();
447 }
448 stmt->batchSize++;
449 }
450
451 /**
452 * Buffer sizes for different C types
453 */
454 static DWORD s_bufferSize[] = { 0, sizeof(LONG), sizeof(DWORD), sizeof(INT64), sizeof(QWORD), sizeof(double) };
455
456 /**
457 * Corresponding Oracle types for C types
458 */
459 static ub2 s_oracleType[] = { SQLT_STR, SQLT_INT, SQLT_UIN, SQLT_INT, SQLT_UIN, SQLT_FLT };
460
461 /**
462 * Bind parameter to statement - normal mode (non-batch)
463 */
464 static void BindNormal(ORACLE_STATEMENT *stmt, int pos, int sqlType, int cType, void *buffer, int allocType)
465 {
466 OCIBind *handleBind = (OCIBind *)stmt->bindings->get(pos - 1);
467 void *sqlBuffer;
468 switch(cType)
469 {
470 case DB_CTYPE_STRING:
471 #if UNICODE_UCS4
472 sqlBuffer = UCS2StringFromUCS4String((WCHAR *)buffer);
473 stmt->buffers->set(pos - 1, sqlBuffer);
474 if (allocType == DB_BIND_DYNAMIC)
475 free(buffer);
476 #else
477 if (allocType == DB_BIND_TRANSIENT)
478 {
479 sqlBuffer = wcsdup((WCHAR *)buffer);
480 stmt->buffers->set(pos - 1, sqlBuffer);
481 }
482 else
483 {
484 sqlBuffer = buffer;
485 if (allocType == DB_BIND_DYNAMIC)
486 stmt->buffers->set(pos - 1, sqlBuffer);
487 }
488 #endif
489 OCIBindByPos(stmt->handleStmt, &handleBind, stmt->handleError, pos, sqlBuffer,
490 ((sb4)ucs2_strlen((UCS2CHAR *)sqlBuffer) + 1) * sizeof(UCS2CHAR),
491 (sqlType == DB_SQLTYPE_TEXT) ? SQLT_LNG : SQLT_STR,
492 NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);
493 break;
494 case DB_CTYPE_INT64: // OCI prior to 11.2 cannot bind 64 bit integers
495 #ifdef UNICODE_UCS2
496 sqlBuffer = malloc(64 * sizeof(WCHAR));
497 swprintf((WCHAR *)sqlBuffer, 64, INT64_FMTW, *((INT64 *)buffer));
498 #else
499 {
500 char temp[64];
501 snprintf(temp, 64, INT64_FMTA, *((INT64 *)buffer));
502 sqlBuffer = UCS2StringFromMBString(temp);
503 }
504 #endif
505 stmt->buffers->set(pos - 1, sqlBuffer);
506 OCIBindByPos(stmt->handleStmt, &handleBind, stmt->handleError, pos, sqlBuffer,
507 ((sb4)ucs2_strlen((UCS2CHAR *)sqlBuffer) + 1) * sizeof(UCS2CHAR),
508 SQLT_STR, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);
509 if (allocType == DB_BIND_DYNAMIC)
510 free(buffer);
511 break;
512 case DB_CTYPE_UINT64: // OCI prior to 11.2 cannot bind 64 bit integers
513 #ifdef UNICODE_UCS2
514 sqlBuffer = malloc(64 * sizeof(WCHAR));
515 swprintf((WCHAR *)sqlBuffer, 64, UINT64_FMTW, *((QWORD *)buffer));
516 #else
517 {
518 char temp[64];
519 snprintf(temp, 64, UINT64_FMTA, *((QWORD *)buffer));
520 sqlBuffer = UCS2StringFromMBString(temp);
521 }
522 #endif
523 stmt->buffers->set(pos - 1, sqlBuffer);
524 OCIBindByPos(stmt->handleStmt, &handleBind, stmt->handleError, pos, sqlBuffer,
525 ((sb4)ucs2_strlen((UCS2CHAR *)sqlBuffer) + 1) * sizeof(UCS2CHAR),
526 SQLT_STR, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);
527 if (allocType == DB_BIND_DYNAMIC)
528 free(buffer);
529 break;
530 default:
531 switch(allocType)
532 {
533 case DB_BIND_STATIC:
534 sqlBuffer = buffer;
535 break;
536 case DB_BIND_DYNAMIC:
537 sqlBuffer = buffer;
538 stmt->buffers->set(pos - 1, buffer);
539 break;
540 case DB_BIND_TRANSIENT:
541 sqlBuffer = nx_memdup(buffer, s_bufferSize[cType]);
542 stmt->buffers->set(pos - 1, sqlBuffer);
543 break;
544 default:
545 return; // Invalid call
546 }
547 OCIBindByPos(stmt->handleStmt, &handleBind, stmt->handleError, pos, sqlBuffer, s_bufferSize[cType],
548 s_oracleType[cType], NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);
549 break;
550 }
551
552 stmt->bindings->set(pos - 1, handleBind);
553 }
554
555 /**
556 * Batch bind - constructor
557 */
558 OracleBatchBind::OracleBatchBind(int cType, int sqlType)
559 {
560 m_cType = cType;
561 m_size = 0;
562 m_allocated = 256;
563 if ((cType == DB_CTYPE_STRING) || (cType == DB_CTYPE_INT64) || (cType == DB_CTYPE_UINT64))
564 {
565 m_elementSize = sizeof(UCS2CHAR);
566 m_string = true;
567 m_oraType = (sqlType == DB_SQLTYPE_TEXT) ? SQLT_LNG : SQLT_STR;
568 m_data = NULL;
569 m_strings = (UCS2CHAR **)calloc(m_allocated, sizeof(UCS2CHAR *));
570 }
571 else
572 {
573 m_elementSize = s_bufferSize[cType];
574 m_string = false;
575 m_oraType = s_oracleType[cType];
576 m_data = calloc(m_allocated, m_elementSize);
577 m_strings = NULL;
578 }
579 }
580
581 /**
582 * Batch bind - destructor
583 */
584 OracleBatchBind::~OracleBatchBind()
585 {
586 if (m_strings != NULL)
587 {
588 for(int i = 0; i < m_size; i++)
589 safe_free(m_strings[i]);
590 free(m_strings);
591 }
592 safe_free(m_data);
593 }
594
595 /**
596 * Batch bind - add row
597 */
598 void OracleBatchBind::addRow()
599 {
600 if (m_size == m_allocated)
601 {
602 m_allocated += 256;
603 if (m_string)
604 {
605 m_strings = (UCS2CHAR **)realloc(m_strings, m_allocated * sizeof(UCS2CHAR *));
606 memset(m_strings + m_size, 0, (m_allocated - m_size) * sizeof(UCS2CHAR *));
607 }
608 else
609 {
610 m_data = realloc(m_data, m_allocated * m_elementSize);
611 memset((char *)m_data + m_size * m_elementSize, 0, (m_allocated - m_size) * m_elementSize);
612 }
613 }
614 if (m_size > 0)
615 {
616 // clone last element
617 if (m_string)
618 {
619 UCS2CHAR *p = m_strings[m_size - 1];
620 m_strings[m_size] = (p != NULL) ? ucs2_strdup(p) : NULL;
621 }
622 else
623 {
624 memcpy((char *)m_data + m_size * m_elementSize, (char *)m_data + (m_size - 1) * m_elementSize, m_elementSize);
625 }
626 }
627 m_size++;
628 }
629
630 /**
631 * Batch bind - set value
632 */
633 void OracleBatchBind::set(void *value)
634 {
635 if (m_string)
636 {
637 safe_free(m_strings[m_size - 1]);
638 m_strings[m_size - 1] = (UCS2CHAR *)value;
639 if (value != NULL)
640 {
641 int l = (int)(ucs2_strlen((UCS2CHAR *)value) + 1) * sizeof(UCS2CHAR);
642 if (l > m_elementSize)
643 m_elementSize = l;
644 }
645 }
646 else
647 {
648 memcpy((char *)m_data + (m_size - 1) * m_elementSize, value, m_elementSize);
649 }
650 }
651
652 /**
653 * Get data for OCI bind
654 */
655 void *OracleBatchBind::getData()
656 {
657 if (!m_string)
658 return m_data;
659
660 safe_free(m_data);
661 m_data = calloc(m_size, m_elementSize);
662 char *p = (char *)m_data;
663 for(int i = 0; i < m_size; i++)
664 {
665 if (m_strings[i] == NULL)
666 continue;
667 memcpy(p, m_strings[i], ucs2_strlen(m_strings[i]) * sizeof(UCS2CHAR));
668 p += m_elementSize;
669 }
670 return m_data;
671 }
672
673 /**
674 * Bind parameter to statement - batch mode
675 */
676 static void BindBatch(ORACLE_STATEMENT *stmt, int pos, int sqlType, int cType, void *buffer, int allocType)
677 {
678 if (stmt->batchSize == 0)
679 return; // no batch rows added yet
680
681 OracleBatchBind *bind = stmt->batchBindings->get(pos - 1);
682 if (bind == NULL)
683 {
684 bind = new OracleBatchBind(cType, sqlType);
685 stmt->batchBindings->set(pos - 1, bind);
686 for(int i = 0; i < stmt->batchSize; i++)
687 bind->addRow();
688 }
689
690 if (bind->getCType() != cType)
691 return;
692
693 void *sqlBuffer;
694 switch(bind->getCType())
695 {
696 case DB_CTYPE_STRING:
697 #if UNICODE_UCS4
698 sqlBuffer = UCS2StringFromUCS4String((WCHAR *)buffer);
699 if (allocType == DB_BIND_DYNAMIC)
700 free(buffer);
701 #else
702 if (allocType == DB_BIND_DYNAMIC)
703 {
704 sqlBuffer = buffer;
705 }
706 else
707 {
708 sqlBuffer = wcsdup((WCHAR *)buffer);
709 }
710 #endif
711 bind->set(sqlBuffer);
712 break;
713 case DB_CTYPE_INT64: // OCI prior to 11.2 cannot bind 64 bit integers
714 #ifdef UNICODE_UCS2
715 sqlBuffer = malloc(64 * sizeof(WCHAR));
716 swprintf((WCHAR *)sqlBuffer, 64, INT64_FMTW, *((INT64 *)buffer));
717 #else
718 {
719 char temp[64];
720 snprintf(temp, 64, INT64_FMTA, *((INT64 *)buffer));
721 sqlBuffer = UCS2StringFromMBString(temp);
722 }
723 #endif
724 bind->set(sqlBuffer);
725 if (allocType == DB_BIND_DYNAMIC)
726 free(buffer);
727 break;
728 case DB_CTYPE_UINT64: // OCI prior to 11.2 cannot bind 64 bit integers
729 #ifdef UNICODE_UCS2
730 sqlBuffer = malloc(64 * sizeof(WCHAR));
731 swprintf((WCHAR *)sqlBuffer, 64, UINT64_FMTW, *((QWORD *)buffer));
732 #else
733 {
734 char temp[64];
735 snprintf(temp, 64, UINT64_FMTA, *((QWORD *)buffer));
736 sqlBuffer = UCS2StringFromMBString(temp);
737 }
738 #endif
739 bind->set(sqlBuffer);
740 if (allocType == DB_BIND_DYNAMIC)
741 free(buffer);
742 break;
743 default:
744 bind->set(buffer);
745 if (allocType == DB_BIND_DYNAMIC)
746 free(buffer);
747 break;
748 }
749 }
750
751 /**
752 * Bind parameter to statement
753 */
754 extern "C" void EXPORT DrvBind(ORACLE_STATEMENT *stmt, int pos, int sqlType, int cType, void *buffer, int allocType)
755 {
756 if (stmt->batchMode)
757 BindBatch(stmt, pos, sqlType, cType, buffer, allocType);
758 else
759 BindNormal(stmt, pos, sqlType, cType, buffer, allocType);
760 }
761
762 /**
763 * Execute prepared non-select statement
764 */
765 extern "C" DWORD EXPORT DrvExecute(ORACLE_CONN *pConn, ORACLE_STATEMENT *stmt, WCHAR *errorText)
766 {
767 DWORD dwResult;
768
769 if (stmt->batchMode)
770 {
771 if (stmt->batchSize == 0)
772 {
773 stmt->batchMode = false;
774 stmt->batchBindings->clear();
775 return DBERR_SUCCESS; // empty batch
776 }
777
778 for(int i = 0; i < stmt->batchBindings->size(); i++)
779 {
780 OracleBatchBind *b = stmt->batchBindings->get(i);
781 if (b == NULL)
782 continue;
783
784 OCIBind *handleBind = NULL;
785 OCIBindByPos(stmt->handleStmt, &handleBind, stmt->handleError, i + 1, b->getData(),
786 b->getElementSize(), b->getOraType(), NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);
787 }
788 }
789
790 MutexLock(pConn->mutexQueryLock);
791 if (OCIStmtExecute(pConn->handleService, stmt->handleStmt, pConn->handleError,
792 stmt->batchMode ? stmt->batchSize : 1, 0, NULL, NULL,
793 (pConn->nTransLevel == 0) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT) == OCI_SUCCESS)
794 {
795 dwResult = DBERR_SUCCESS;
796 }
797 else
798 {
799 SetLastError(pConn);
800 dwResult = IsConnectionError(pConn);
801 }
802
803 if (errorText != NULL)
804 {
805 wcsncpy(errorText, pConn->lastErrorText, DBDRV_MAX_ERROR_TEXT);
806 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
807 }
808 MutexUnlock(pConn->mutexQueryLock);
809
810 if (stmt->batchMode)
811 {
812 stmt->batchMode = false;
813 stmt->batchBindings->clear();
814 }
815
816 return dwResult;
817 }
818
819 /**
820 * Destroy prepared statement
821 */
822 extern "C" void EXPORT DrvFreeStatement(ORACLE_STATEMENT *stmt)
823 {
824 if (stmt == NULL)
825 return;
826
827 MutexLock(stmt->connection->mutexQueryLock);
828 OCIStmtRelease(stmt->handleStmt, stmt->handleError, NULL, 0, OCI_DEFAULT);
829 OCIHandleFree(stmt->handleError, OCI_HTYPE_ERROR);
830 MutexUnlock(stmt->connection->mutexQueryLock);
831 delete stmt->bindings;
832 delete stmt->batchBindings;
833 delete stmt->buffers;
834 free(stmt);
835 }
836
837 /**
838 * Perform non-SELECT query
839 */
840 static DWORD DrvQueryInternal(ORACLE_CONN *pConn, const WCHAR *pwszQuery, WCHAR *errorText)
841 {
842 OCIStmt *handleStmt;
843 DWORD dwResult;
844
845 #if UNICODE_UCS4
846 UCS2CHAR *ucs2Query = UCS2StringFromUCS4String(pwszQuery);
847 #else
848 const UCS2CHAR *ucs2Query = pwszQuery;
849 #endif
850
851 MutexLock(pConn->mutexQueryLock);
852 if (OCIStmtPrepare2(pConn->handleService, &handleStmt, pConn->handleError, (text *)ucs2Query,
853 (ub4)ucs2_strlen(ucs2Query) * sizeof(UCS2CHAR), NULL, 0, OCI_NTV_SYNTAX, OCI_DEFAULT) == OCI_SUCCESS)
854 {
855 if (OCIStmtExecute(pConn->handleService, handleStmt, pConn->handleError, 1, 0, NULL, NULL,
856 (pConn->nTransLevel == 0) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT) == OCI_SUCCESS)
857 {
858 dwResult = DBERR_SUCCESS;
859 }
860 else
861 {
862 SetLastError(pConn);
863 dwResult = IsConnectionError(pConn);
864 }
865 OCIStmtRelease(handleStmt, pConn->handleError, NULL, 0, OCI_DEFAULT);
866 }
867 else
868 {
869 SetLastError(pConn);
870 dwResult = IsConnectionError(pConn);
871 }
872 if (errorText != NULL)
873 {
874 wcsncpy(errorText, pConn->lastErrorText, DBDRV_MAX_ERROR_TEXT);
875 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
876 }
877 MutexUnlock(pConn->mutexQueryLock);
878
879 #if UNICODE_UCS4
880 free(ucs2Query);
881 #endif
882 return dwResult;
883 }
884
885 /**
886 * Perform non-SELECT query - entry point
887 */
888 extern "C" DWORD EXPORT DrvQuery(ORACLE_CONN *conn, const WCHAR *query, WCHAR *errorText)
889 {
890 return DrvQueryInternal(conn, query, errorText);
891 }
892
893 /**
894 * Process SELECT results
895 */
896 static ORACLE_RESULT *ProcessQueryResults(ORACLE_CONN *pConn, OCIStmt *handleStmt, DWORD *pdwError)
897 {
898 OCIParam *handleParam;
899 OCIDefine *handleDefine;
900 ub4 nCount;
901 ub2 nWidth;
902 sword nStatus;
903 ORACLE_FETCH_BUFFER *pBuffers;
904 text *colName;
905
906 ORACLE_RESULT *pResult = (ORACLE_RESULT *)malloc(sizeof(ORACLE_RESULT));
907 pResult->nRows = 0;
908 pResult->pData = NULL;
909 pResult->columnNames = NULL;
910 OCIAttrGet(handleStmt, OCI_HTYPE_STMT, &nCount, NULL, OCI_ATTR_PARAM_COUNT, pConn->handleError);
911 pResult->nCols = nCount;
912 if (pResult->nCols > 0)
913 {
914 // Prepare receive buffers and fetch column names
915 pResult->columnNames = (char **)malloc(sizeof(char *) * pResult->nCols);
916 pBuffers = (ORACLE_FETCH_BUFFER *)malloc(sizeof(ORACLE_FETCH_BUFFER) * pResult->nCols);
917 memset(pBuffers, 0, sizeof(ORACLE_FETCH_BUFFER) * pResult->nCols);
918 for(int i = 0; i < pResult->nCols; i++)
919 {
920 if ((nStatus = OCIParamGet(handleStmt, OCI_HTYPE_STMT, pConn->handleError,
921 (void **)&handleParam, (ub4)(i + 1))) == OCI_SUCCESS)
922 {
923 // Column name
924 if (OCIAttrGet(handleParam, OCI_DTYPE_PARAM, &colName, &nCount, OCI_ATTR_NAME, pConn->handleError) == OCI_SUCCESS)
925 {
926 // We are in UTF-16 mode, so OCIAttrGet will return UTF-16 strings
927 pResult->columnNames[i] = MBStringFromUCS2String((UCS2CHAR *)colName);
928 }
929 else
930 {
931 pResult->columnNames[i] = strdup("");
932 }
933
934 // Data size
935 ub2 type = 0;
936 OCIAttrGet(handleParam, OCI_DTYPE_PARAM, &type, NULL, OCI_ATTR_DATA_TYPE, pConn->handleError);
937 if (type == OCI_TYPECODE_CLOB)
938 {
939 pBuffers[i].pData = NULL;
940 OCIDescriptorAlloc(pConn->handleEnv, (void **)&pBuffers[i].lobLocator, OCI_DTYPE_LOB, 0, NULL);
941 handleDefine = NULL;
942 nStatus = OCIDefineByPos(handleStmt, &handleDefine, pConn->handleError, i + 1,
943 &pBuffers[i].lobLocator, 0, SQLT_CLOB, &pBuffers[i].isNull,
944 NULL, NULL, OCI_DEFAULT);
945 }
946 else
947 {
948 pBuffers[i].lobLocator = NULL;
949 OCIAttrGet(handleParam, OCI_DTYPE_PARAM, &nWidth, NULL, OCI_ATTR_DATA_SIZE, pConn->handleError);
950 pBuffers[i].pData = (UCS2CHAR *)malloc((nWidth + 31) * sizeof(UCS2CHAR));
951 handleDefine = NULL;
952 nStatus = OCIDefineByPos(handleStmt, &handleDefine, pConn->handleError, i + 1,
953 pBuffers[i].pData, (nWidth + 31) * sizeof(UCS2CHAR),
954 SQLT_CHR, &pBuffers[i].isNull, &pBuffers[i].nLength,
955 &pBuffers[i].nCode, OCI_DEFAULT);
956 }
957 if (nStatus != OCI_SUCCESS)
958 {
959 SetLastError(pConn);
960 *pdwError = IsConnectionError(pConn);
961 }
962 OCIDescriptorFree(handleParam, OCI_DTYPE_PARAM);
963 }
964 else
965 {
966 SetLastError(pConn);
967 *pdwError = IsConnectionError(pConn);
968 }
969 }
970
971 // Fetch data
972 if (nStatus == OCI_SUCCESS)
973 {
974 int nPos = 0;
975 while(1)
976 {
977 nStatus = OCIStmtFetch2(handleStmt, pConn->handleError, 1, OCI_FETCH_NEXT, 0, OCI_DEFAULT);
978 if (nStatus == OCI_NO_DATA)
979 {
980 *pdwError = DBERR_SUCCESS; // EOF
981 break;
982 }
983 if ((nStatus != OCI_SUCCESS) && (nStatus != OCI_SUCCESS_WITH_INFO))
984 {
985 SetLastError(pConn);
986 *pdwError = IsConnectionError(pConn);
987 break;
988 }
989
990 // New row
991 pResult->nRows++;
992 pResult->pData = (WCHAR **)realloc(pResult->pData, sizeof(WCHAR *) * pResult->nCols * pResult->nRows);
993 for(int i = 0; i < pResult->nCols; i++)
994 {
995 if (pBuffers[i].isNull)
996 {
997 pResult->pData[nPos] = (WCHAR *)nx_memdup("\0\0\0", sizeof(WCHAR));
998 }
999 else if (pBuffers[i].lobLocator != NULL)
1000 {
1001 ub4 length = 0;
1002 ub4 amount = length;
1003 OCILobGetLength(pConn->handleService, pConn->handleError, pBuffers[i].lobLocator, &length);
1004 pResult->pData[nPos] = (WCHAR *)malloc((length + 1) * sizeof(WCHAR));
1005 #if UNICODE_UCS4
1006 UCS2CHAR *ucs2buffer = (UCS2CHAR *)malloc(sizeof(UCS2CHAR) * length);
1007 OCILobRead(pConn->handleService, pConn->handleError, pBuffers[i].lobLocator, &amount, 1,
1008 ucs2buffer, length * sizeof(UCS2CHAR), NULL, NULL, OCI_UCS2ID, SQLCS_IMPLICIT);
1009 ucs2_to_ucs4(ucs2buffer, length, pResult->pData[nPos], length + 1);
1010 free(ucs2buffer);
1011 #else
1012 OCILobRead(pConn->handleService, pConn->handleError, pBuffers[i].lobLocator, &amount, 1,
1013 pResult->pData[nPos], (length + 1) * sizeof(WCHAR), NULL, NULL, OCI_UCS2ID, SQLCS_IMPLICIT);
1014 #endif
1015 pResult->pData[nPos][length] = 0;
1016 }
1017 else
1018 {
1019 int length = pBuffers[i].nLength / sizeof(UCS2CHAR);
1020 pResult->pData[nPos] = (WCHAR *)malloc((length + 1) * sizeof(WCHAR));
1021 #if UNICODE_UCS4
1022 ucs2_to_ucs4(pBuffers[i].pData, length, pResult->pData[nPos], length + 1);
1023 #else
1024 memcpy(pResult->pData[nPos], pBuffers[i].pData, pBuffers[i].nLength);
1025 #endif
1026 pResult->pData[nPos][length] = 0;
1027 }
1028 nPos++;
1029 }
1030 }
1031 }
1032
1033 // Cleanup
1034 for(int i = 0; i < pResult->nCols; i++)
1035 {
1036 safe_free(pBuffers[i].pData);
1037 if (pBuffers[i].lobLocator != NULL)
1038 {
1039 OCIDescriptorFree(pBuffers[i].lobLocator, OCI_DTYPE_LOB);
1040 }
1041 }
1042 free(pBuffers);
1043
1044 // Destroy results in case of error
1045 if (*pdwError != DBERR_SUCCESS)
1046 {
1047 DestroyQueryResult(pResult);
1048 pResult = NULL;
1049 }
1050 }
1051
1052 return pResult;
1053 }
1054
1055 /**
1056 * Perform SELECT query
1057 */
1058 extern "C" DBDRV_RESULT EXPORT DrvSelect(ORACLE_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText)
1059 {
1060 ORACLE_RESULT *pResult = NULL;
1061 OCIStmt *handleStmt;
1062
1063 #if UNICODE_UCS4
1064 UCS2CHAR *ucs2Query = UCS2StringFromUCS4String(pwszQuery);
1065 #else
1066 UCS2CHAR *ucs2Query = pwszQuery;
1067 #endif
1068
1069 MutexLock(pConn->mutexQueryLock);
1070 if (OCIStmtPrepare2(pConn->handleService, &handleStmt, pConn->handleError, (text *)ucs2Query,
1071 (ub4)ucs2_strlen(ucs2Query) * sizeof(UCS2CHAR), NULL, 0, OCI_NTV_SYNTAX, OCI_DEFAULT) == OCI_SUCCESS)
1072 {
1073 OCIAttrSet(handleStmt, OCI_HTYPE_STMT, &pConn->prefetchLimit, 0, OCI_ATTR_PREFETCH_ROWS, pConn->handleError);
1074 if (OCIStmtExecute(pConn->handleService, handleStmt, pConn->handleError,
1075 0, 0, NULL, NULL, (pConn->nTransLevel == 0) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT) == OCI_SUCCESS)
1076 {
1077 pResult = ProcessQueryResults(pConn, handleStmt, pdwError);
1078 }
1079 else
1080 {
1081 SetLastError(pConn);
1082 *pdwError = IsConnectionError(pConn);
1083 }
1084 OCIStmtRelease(handleStmt, pConn->handleError, NULL, 0, OCI_DEFAULT);
1085 }
1086 else
1087 {
1088 SetLastError(pConn);
1089 *pdwError = IsConnectionError(pConn);
1090 }
1091 if (errorText != NULL)
1092 {
1093 wcsncpy(errorText, pConn->lastErrorText, DBDRV_MAX_ERROR_TEXT);
1094 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
1095 }
1096 MutexUnlock(pConn->mutexQueryLock);
1097
1098 #if UNICODE_UCS4
1099 free(ucs2Query);
1100 #endif
1101 return pResult;
1102 }
1103
1104 /**
1105 * Perform SELECT query using prepared statement
1106 */
1107 extern "C" DBDRV_RESULT EXPORT DrvSelectPrepared(ORACLE_CONN *pConn, ORACLE_STATEMENT *stmt, DWORD *pdwError, WCHAR *errorText)
1108 {
1109 ORACLE_RESULT *pResult = NULL;
1110
1111 MutexLock(pConn->mutexQueryLock);
1112 OCIAttrSet(stmt->handleStmt, OCI_HTYPE_STMT, &pConn->prefetchLimit, 0, OCI_ATTR_PREFETCH_ROWS, pConn->handleError);
1113 if (OCIStmtExecute(pConn->handleService, stmt->handleStmt, pConn->handleError,
1114 0, 0, NULL, NULL, (pConn->nTransLevel == 0) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT) == OCI_SUCCESS)
1115 {
1116 pResult = ProcessQueryResults(pConn, stmt->handleStmt, pdwError);
1117 }
1118 else
1119 {
1120 SetLastError(pConn);
1121 *pdwError = IsConnectionError(pConn);
1122 }
1123
1124 if (errorText != NULL)
1125 {
1126 wcsncpy(errorText, pConn->lastErrorText, DBDRV_MAX_ERROR_TEXT);
1127 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
1128 }
1129 MutexUnlock(pConn->mutexQueryLock);
1130
1131 return pResult;
1132 }
1133
1134 /**
1135 * Get field length from result
1136 */
1137 extern "C" LONG EXPORT DrvGetFieldLength(ORACLE_RESULT *pResult, int nRow, int nColumn)
1138 {
1139 if (pResult == NULL)
1140 return -1;
1141
1142 if ((nRow >= 0) && (nRow < pResult->nRows) &&
1143 (nColumn >= 0) && (nColumn < pResult->nCols))
1144 return (LONG)wcslen(pResult->pData[pResult->nCols * nRow + nColumn]);
1145
1146 return -1;
1147 }
1148
1149 /**
1150 * Get field value from result
1151 */
1152 extern "C" WCHAR EXPORT *DrvGetField(ORACLE_RESULT *pResult, int nRow, int nColumn,
1153 WCHAR *pBuffer, int nBufLen)
1154 {
1155 WCHAR *pValue = NULL;
1156
1157 if (pResult != NULL)
1158 {
1159 if ((nRow < pResult->nRows) && (nRow >= 0) &&
1160 (nColumn < pResult->nCols) && (nColumn >= 0))
1161 {
1162 #ifdef _WIN32
1163 wcsncpy_s(pBuffer, nBufLen, pResult->pData[nRow * pResult->nCols + nColumn], _TRUNCATE);
1164 #else
1165 wcsncpy(pBuffer, pResult->pData[nRow * pResult->nCols + nColumn], nBufLen);
1166 pBuffer[nBufLen - 1] = 0;
1167 #endif
1168 pValue = pBuffer;
1169 }
1170 }
1171 return pValue;
1172 }
1173
1174 /**
1175 * Get number of rows in result
1176 */
1177 extern "C" int EXPORT DrvGetNumRows(ORACLE_RESULT *pResult)
1178 {
1179 return (pResult != NULL) ? pResult->nRows : 0;
1180 }
1181
1182 /**
1183 * Get column count in query result
1184 */
1185 extern "C" int EXPORT DrvGetColumnCount(ORACLE_RESULT *pResult)
1186 {
1187 return (pResult != NULL) ? pResult->nCols : 0;
1188 }
1189
1190 /**
1191 * Get column name in query result
1192 */
1193 extern "C" const char EXPORT *DrvGetColumnName(ORACLE_RESULT *pResult, int column)
1194 {
1195 return ((pResult != NULL) && (column >= 0) && (column < pResult->nCols)) ? pResult->columnNames[column] : NULL;
1196 }
1197
1198 /**
1199 * Free SELECT results
1200 */
1201 extern "C" void EXPORT DrvFreeResult(ORACLE_RESULT *pResult)
1202 {
1203 if (pResult != NULL)
1204 DestroyQueryResult(pResult);
1205 }
1206
1207 /**
1208 * Destroy unbuffered query result
1209 */
1210 static void DestroyUnbufferedQueryResult(ORACLE_UNBUFFERED_RESULT *result, bool freeStatement)
1211 {
1212 int i;
1213
1214 if (freeStatement)
1215 OCIStmtRelease(result->handleStmt, result->connection->handleError, NULL, 0, OCI_DEFAULT);
1216
1217 for(i = 0; i < result->nCols; i++)
1218 {
1219 free(result->pBuffers[i].pData);
1220 if (result->pBuffers[i].lobLocator != NULL)
1221 {
1222 OCIDescriptorFree(result->pBuffers[i].lobLocator, OCI_DTYPE_LOB);
1223 }
1224 }
1225 free(result->pBuffers);
1226
1227 for(i = 0; i < result->nCols; i++)
1228 free(result->columnNames[i]);
1229 free(result->columnNames);
1230
1231 free(result);
1232 }
1233
1234 /**
1235 * Process results of unbuffered query execution (prepare for fetching results)
1236 */
1237 static ORACLE_UNBUFFERED_RESULT *ProcessUnbufferedQueryResults(ORACLE_CONN *pConn, OCIStmt *handleStmt, DWORD *pdwError)
1238 {
1239 ORACLE_UNBUFFERED_RESULT *result = (ORACLE_UNBUFFERED_RESULT *)malloc(sizeof(ORACLE_UNBUFFERED_RESULT));
1240 result->handleStmt = handleStmt;
1241 result->connection = pConn;
1242
1243 ub4 nCount;
1244 OCIAttrGet(result->handleStmt, OCI_HTYPE_STMT, &nCount, NULL, OCI_ATTR_PARAM_COUNT, pConn->handleError);
1245 result->nCols = nCount;
1246 if (result->nCols > 0)
1247 {
1248 // Prepare receive buffers and fetch column names
1249 result->columnNames = (char **)calloc(result->nCols, sizeof(char *));
1250 result->pBuffers = (ORACLE_FETCH_BUFFER *)calloc(result->nCols, sizeof(ORACLE_FETCH_BUFFER));
1251 for(int i = 0; i < result->nCols; i++)
1252 {
1253 OCIParam *handleParam;
1254
1255 result->pBuffers[i].isNull = 1; // Mark all columns as NULL initially
1256 if (OCIParamGet(result->handleStmt, OCI_HTYPE_STMT, pConn->handleError,
1257 (void **)&handleParam, (ub4)(i + 1)) == OCI_SUCCESS)
1258 {
1259 // Column name
1260 text *colName;
1261 if (OCIAttrGet(handleParam, OCI_DTYPE_PARAM, &colName, &nCount, OCI_ATTR_NAME, pConn->handleError) == OCI_SUCCESS)
1262 {
1263 // We are in UTF-16 mode, so OCIAttrGet will return UTF-16 strings
1264 result->columnNames[i] = MBStringFromUCS2String((UCS2CHAR *)colName);
1265 }
1266 else
1267 {
1268 result->columnNames[i] = strdup("");
1269 }
1270
1271 // Data size
1272 sword nStatus;
1273 ub2 type = 0;
1274 OCIAttrGet(handleParam, OCI_DTYPE_PARAM, &type, NULL, OCI_ATTR_DATA_TYPE, pConn->handleError);
1275 OCIDefine *handleDefine;
1276 if (type == OCI_TYPECODE_CLOB)
1277 {
1278 result->pBuffers[i].pData = NULL;
1279 OCIDescriptorAlloc(pConn->handleEnv, (void **)&result->pBuffers[i].lobLocator, OCI_DTYPE_LOB, 0, NULL);
1280 handleDefine = NULL;
1281 nStatus = OCIDefineByPos(result->handleStmt, &handleDefine, pConn->handleError, i + 1,
1282 &result->pBuffers[i].lobLocator, 0, SQLT_CLOB, &result->pBuffers[i].isNull,
1283 NULL, NULL, OCI_DEFAULT);
1284 }
1285 else
1286 {
1287 ub2 nWidth;
1288 result->pBuffers[i].lobLocator = NULL;
1289 OCIAttrGet(handleParam, OCI_DTYPE_PARAM, &nWidth, NULL, OCI_ATTR_DATA_SIZE, pConn->handleError);
1290 result->pBuffers[i].pData = (UCS2CHAR *)malloc((nWidth + 31) * sizeof(UCS2CHAR));
1291 handleDefine = NULL;
1292 nStatus = OCIDefineByPos(result->handleStmt, &handleDefine, pConn->handleError, i + 1,
1293 result->pBuffers[i].pData, (nWidth + 31) * sizeof(UCS2CHAR),
1294 SQLT_CHR, &result->pBuffers[i].isNull, &result->pBuffers[i].nLength,
1295 &result->pBuffers[i].nCode, OCI_DEFAULT);
1296 }
1297 OCIDescriptorFree(handleParam, OCI_DTYPE_PARAM);
1298 if (nStatus == OCI_SUCCESS)
1299 {
1300 *pdwError = DBERR_SUCCESS;
1301 }
1302 else
1303 {
1304 SetLastError(pConn);
1305 *pdwError = IsConnectionError(pConn);
1306 DestroyUnbufferedQueryResult(result, false);
1307 result = NULL;
1308 break;
1309 }
1310 }
1311 else
1312 {
1313 SetLastError(pConn);
1314 *pdwError = IsConnectionError(pConn);
1315 DestroyUnbufferedQueryResult(result, false);
1316 result = NULL;
1317 break;
1318 }
1319 }
1320 }
1321 else
1322 {
1323 free(result);
1324 result = NULL;
1325 }
1326
1327 return result;
1328 }
1329
1330 /**
1331 * Perform unbuffered SELECT query
1332 */
1333 extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectUnbuffered(ORACLE_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText)
1334 {
1335 ORACLE_UNBUFFERED_RESULT *result = NULL;
1336
1337 #if UNICODE_UCS4
1338 UCS2CHAR *ucs2Query = UCS2StringFromUCS4String(pwszQuery);
1339 #else
1340 UCS2CHAR *ucs2Query = pwszQuery;
1341 #endif
1342
1343 MutexLock(pConn->mutexQueryLock);
1344
1345 OCIStmt *handleStmt;
1346 if (OCIStmtPrepare2(pConn->handleService, &handleStmt, pConn->handleError, (text *)ucs2Query,
1347 (ub4)ucs2_strlen(ucs2Query) * sizeof(UCS2CHAR), NULL, 0, OCI_NTV_SYNTAX, OCI_DEFAULT) == OCI_SUCCESS)
1348 {
1349 OCIAttrSet(handleStmt, OCI_HTYPE_STMT, &pConn->prefetchLimit, 0, OCI_ATTR_PREFETCH_ROWS, pConn->handleError);
1350 if (OCIStmtExecute(pConn->handleService, handleStmt, pConn->handleError,
1351 0, 0, NULL, NULL, (pConn->nTransLevel == 0) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT) == OCI_SUCCESS)
1352 {
1353 result = ProcessUnbufferedQueryResults(pConn, handleStmt, pdwError);
1354 }
1355 else
1356 {
1357 SetLastError(pConn);
1358 *pdwError = IsConnectionError(pConn);
1359 }
1360 }
1361 else
1362 {
1363 SetLastError(pConn);
1364 *pdwError = IsConnectionError(pConn);
1365 }
1366
1367 #if UNICODE_UCS4
1368 free(ucs2Query);
1369 #endif
1370
1371 if ((*pdwError == DBERR_SUCCESS) && (result != NULL))
1372 return result;
1373
1374 // On failure, unlock query mutex and do cleanup
1375 OCIStmtRelease(handleStmt, pConn->handleError, NULL, 0, OCI_DEFAULT);
1376 if (errorText != NULL)
1377 {
1378 wcsncpy(errorText, pConn->lastErrorText, DBDRV_MAX_ERROR_TEXT);
1379 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
1380 }
1381 MutexUnlock(pConn->mutexQueryLock);
1382 return NULL;
1383 }
1384
1385 /**
1386 * Perform SELECT query using prepared statement
1387 */
1388 extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectPreparedUnbuffered(ORACLE_CONN *pConn, ORACLE_STATEMENT *stmt, DWORD *pdwError, WCHAR *errorText)
1389 {
1390 ORACLE_UNBUFFERED_RESULT *result = NULL;
1391
1392 MutexLock(pConn->mutexQueryLock);
1393
1394 OCIAttrSet(stmt->handleStmt, OCI_HTYPE_STMT, &pConn->prefetchLimit, 0, OCI_ATTR_PREFETCH_ROWS, pConn->handleError);
1395 if (OCIStmtExecute(pConn->handleService, stmt->handleStmt, pConn->handleError,
1396 0, 0, NULL, NULL, (pConn->nTransLevel == 0) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT) == OCI_SUCCESS)
1397 {
1398 result = ProcessUnbufferedQueryResults(pConn, stmt->handleStmt, pdwError);
1399 }
1400 else
1401 {
1402 SetLastError(pConn);
1403 *pdwError = IsConnectionError(pConn);
1404 }
1405
1406 if ((*pdwError == DBERR_SUCCESS) && (result != NULL))
1407 return result;
1408
1409 // On failure, unlock query mutex and do cleanup
1410 if (errorText != NULL)
1411 {
1412 wcsncpy(errorText, pConn->lastErrorText, DBDRV_MAX_ERROR_TEXT);
1413 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
1414 }
1415 MutexUnlock(pConn->mutexQueryLock);
1416 return NULL;
1417 }
1418
1419 /**
1420 * Fetch next result line from unbuffered SELECT results
1421 */
1422 extern "C" bool EXPORT DrvFetch(ORACLE_UNBUFFERED_RESULT *result)
1423 {
1424 bool success;
1425
1426 if (result == NULL)
1427 return false;
1428
1429 sword rc = OCIStmtFetch2(result->handleStmt, result->connection->handleError, 1, OCI_FETCH_NEXT, 0, OCI_DEFAULT);
1430 if ((rc == OCI_SUCCESS) || (rc == OCI_SUCCESS_WITH_INFO))
1431 {
1432 success = true;
1433 }
1434 else
1435 {
1436 SetLastError(result->connection);
1437 success = false;
1438 }
1439 return success;
1440 }
1441
1442 /**
1443 * Get field length from current row in unbuffered query result
1444 */
1445 extern "C" LONG EXPORT DrvGetFieldLengthUnbuffered(ORACLE_UNBUFFERED_RESULT *result, int nColumn)
1446 {
1447 if (result == NULL)
1448 return 0;
1449
1450 if ((nColumn < 0) || (nColumn >= result->nCols))
1451 return 0;
1452
1453 if (result->pBuffers[nColumn].isNull)
1454 return 0;
1455
1456 if (result->pBuffers[nColumn].lobLocator != NULL)
1457 {
1458 ub4 length = 0;
1459 OCILobGetLength(result->connection->handleService, result->connection->handleError, result->pBuffers[nColumn].lobLocator, &length);
1460 return (LONG)length;
1461 }
1462
1463 return (LONG)(result->pBuffers[nColumn].nLength / sizeof(UCS2CHAR));
1464 }
1465
1466 /**
1467 * Get field from current row in unbuffered query result
1468 */
1469 extern "C" WCHAR EXPORT *DrvGetFieldUnbuffered(ORACLE_UNBUFFERED_RESULT *result, int nColumn, WCHAR *pBuffer, int nBufSize)
1470 {
1471 int nLen;
1472
1473 if (result == NULL)
1474 return NULL;
1475
1476 if ((nColumn < 0) || (nColumn >= result->nCols))
1477 return NULL;
1478
1479 if (result->pBuffers[nColumn].isNull)
1480 {
1481 *pBuffer = 0;
1482 }
1483 else if (result->pBuffers[nColumn].lobLocator != NULL)
1484 {
1485 ub4 length = 0;
1486 OCILobGetLength(result->connection->handleService, result->connection->handleError, result->pBuffers[nColumn].lobLocator, &length);
1487
1488 nLen = min(nBufSize - 1, (int)length);
1489 ub4 amount = nLen;
1490 #if UNICODE_UCS4
1491 UCS2CHAR *ucs2buffer = (UCS2CHAR *)malloc(nLen * sizeof(UCS2CHAR));
1492 OCILobRead(result->connection->handleService, result->connection->handleError, result->pBuffers[nColumn].lobLocator, &amount, 1,
1493 ucs2buffer, nLen * sizeof(UCS2CHAR), NULL, NULL, OCI_UCS2ID, SQLCS_IMPLICIT);
1494 ucs2_to_ucs4(ucs2buffer, nLen, pBuffer, nLen);
1495 free(ucs2buffer);
1496 #else
1497 OCILobRead(result->connection->handleService, result->connection->handleError, result->pBuffers[nColumn].lobLocator, &amount, 1,
1498 pBuffer, nBufSize * sizeof(WCHAR), NULL, NULL, OCI_UCS2ID, SQLCS_IMPLICIT);
1499 #endif
1500 pBuffer[nLen] = 0;
1501 }
1502 else
1503 {
1504 nLen = min(nBufSize - 1, ((int)(result->pBuffers[nColumn].nLength / sizeof(UCS2CHAR))));
1505 #if UNICODE_UCS4
1506 ucs2_to_ucs4(result->pBuffers[nColumn].pData, nLen, pBuffer, nLen + 1);
1507 #else
1508 memcpy(pBuffer, result->pBuffers[nColumn].pData, nLen * sizeof(WCHAR));
1509 #endif
1510 pBuffer[nLen] = 0;
1511 }
1512
1513 return pBuffer;
1514 }
1515
1516 /**
1517 * Get column count in unbuffered query result
1518 */
1519 extern "C" int EXPORT DrvGetColumnCountUnbuffered(ORACLE_UNBUFFERED_RESULT *result)
1520 {
1521 return (result != NULL) ? result->nCols : 0;
1522 }
1523
1524 /**
1525 * Get column name in unbuffered query result
1526 */
1527 extern "C" const char EXPORT *DrvGetColumnNameUnbuffered(ORACLE_UNBUFFERED_RESULT *result, int column)
1528 {
1529 return ((result != NULL) && (column >= 0) && (column < result->nCols)) ? result->columnNames[column] : NULL;
1530 }
1531
1532 /**
1533 * Destroy result of unbuffered query
1534 */
1535 extern "C" void EXPORT DrvFreeUnbufferedResult(ORACLE_UNBUFFERED_RESULT *result)
1536 {
1537 if (result == NULL)
1538 return;
1539
1540 MUTEX mutex = result->connection->mutexQueryLock;
1541 DestroyUnbufferedQueryResult(result, true);
1542 MutexUnlock(mutex);
1543 }
1544
1545 /**
1546 * Begin transaction
1547 */
1548 extern "C" DWORD EXPORT DrvBegin(ORACLE_CONN *pConn)
1549 {
1550 if (pConn == NULL)
1551 return DBERR_INVALID_HANDLE;
1552
1553 MutexLock(pConn->mutexQueryLock);
1554 pConn->nTransLevel++;
1555 MutexUnlock(pConn->mutexQueryLock);
1556 return DBERR_SUCCESS;
1557 }
1558
1559 /**
1560 * Commit transaction
1561 */
1562 extern "C" DWORD EXPORT DrvCommit(ORACLE_CONN *pConn)
1563 {
1564 DWORD dwResult;
1565
1566 if (pConn == NULL)
1567 return DBERR_INVALID_HANDLE;
1568
1569 MutexLock(pConn->mutexQueryLock);
1570 if (pConn->nTransLevel > 0)
1571 {
1572 if (OCITransCommit(pConn->handleService, pConn->handleError, OCI_DEFAULT) == OCI_SUCCESS)
1573 {
1574 dwResult = DBERR_SUCCESS;
1575 pConn->nTransLevel = 0;
1576 }
1577 else
1578 {
1579 SetLastError(pConn);
1580 dwResult = IsConnectionError(pConn);
1581 }
1582 }
1583 else
1584 {
1585 dwResult = DBERR_SUCCESS;
1586 }
1587 MutexUnlock(pConn->mutexQueryLock);
1588 return dwResult;
1589 }
1590
1591 /**
1592 * Rollback transaction
1593 */
1594 extern "C" DWORD EXPORT DrvRollback(ORACLE_CONN *pConn)
1595 {
1596 DWORD dwResult;
1597
1598 if (pConn == NULL)
1599 return DBERR_INVALID_HANDLE;
1600
1601 MutexLock(pConn->mutexQueryLock);
1602 if (pConn->nTransLevel > 0)
1603 {
1604 if (OCITransRollback(pConn->handleService, pConn->handleError, OCI_DEFAULT) == OCI_SUCCESS)
1605 {
1606 dwResult = DBERR_SUCCESS;
1607 pConn->nTransLevel = 0;
1608 }
1609 else
1610 {
1611 SetLastError(pConn);
1612 dwResult = IsConnectionError(pConn);
1613 }
1614 }
1615 else
1616 {
1617 dwResult = DBERR_SUCCESS;
1618 }
1619 MutexUnlock(pConn->mutexQueryLock);
1620 return dwResult;
1621 }
1622
1623 /**
1624 * Check if table exist
1625 */
1626 extern "C" int EXPORT DrvIsTableExist(ORACLE_CONN *pConn, const WCHAR *name)
1627 {
1628 WCHAR query[256];
1629 swprintf(query, 256, L"SELECT count(*) FROM user_tables WHERE table_name=upper('%ls')", name);
1630 DWORD error;
1631 WCHAR errorText[DBDRV_MAX_ERROR_TEXT];
1632 int rc = DBIsTableExist_Failure;
1633 ORACLE_RESULT *hResult = (ORACLE_RESULT *)DrvSelect(pConn, query, &error, errorText);
1634 if (hResult != NULL)
1635 {
1636 WCHAR buffer[64] = L"";
1637 DrvGetField(hResult, 0, 0, buffer, 64);
1638 rc = (wcstol(buffer, NULL, 10) > 0) ? DBIsTableExist_Found : DBIsTableExist_NotFound;
1639 DrvFreeResult(hResult);
1640 }
1641 return rc;
1642 }
1643
1644 #ifdef _WIN32
1645
1646 /**
1647 * DLL Entry point
1648 */
1649 bool WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
1650 {
1651 if (dwReason == DLL_PROCESS_ATTACH)
1652 DisableThreadLibraryCalls(hInstance);
1653 return true;
1654 }
1655
1656 #endif