1c0be4a0e1077ff7f568de98e011c63cb8ebf5bf
[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 sqlBuffer = malloc(sizeof(OCINumber));
496 OCINumberFromInt(stmt->handleError, buffer, sizeof(INT64), OCI_NUMBER_SIGNED, (OCINumber *)sqlBuffer);
497 OCIBindByPos(stmt->handleStmt, &handleBind, stmt->handleError, pos, sqlBuffer, sizeof(OCINumber),
498 SQLT_VNU, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);
499 if (allocType == DB_BIND_DYNAMIC)
500 free(buffer);
501 break;
502 case DB_CTYPE_UINT64: // OCI prior to 11.2 cannot bind 64 bit integers
503 sqlBuffer = malloc(sizeof(OCINumber));
504 OCINumberFromInt(stmt->handleError, buffer, sizeof(INT64), OCI_NUMBER_UNSIGNED, (OCINumber *)sqlBuffer);
505 OCIBindByPos(stmt->handleStmt, &handleBind, stmt->handleError, pos, sqlBuffer, sizeof(OCINumber),
506 SQLT_VNU, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);
507 if (allocType == DB_BIND_DYNAMIC)
508 free(buffer);
509 break;
510 default:
511 switch(allocType)
512 {
513 case DB_BIND_STATIC:
514 sqlBuffer = buffer;
515 break;
516 case DB_BIND_DYNAMIC:
517 sqlBuffer = buffer;
518 stmt->buffers->set(pos - 1, buffer);
519 break;
520 case DB_BIND_TRANSIENT:
521 sqlBuffer = nx_memdup(buffer, s_bufferSize[cType]);
522 stmt->buffers->set(pos - 1, sqlBuffer);
523 break;
524 default:
525 return; // Invalid call
526 }
527 OCIBindByPos(stmt->handleStmt, &handleBind, stmt->handleError, pos, sqlBuffer, s_bufferSize[cType],
528 s_oracleType[cType], NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);
529 break;
530 }
531
532 stmt->bindings->set(pos - 1, handleBind);
533 }
534
535 /**
536 * Batch bind - constructor
537 */
538 OracleBatchBind::OracleBatchBind(int cType, int sqlType)
539 {
540 m_cType = cType;
541 m_size = 0;
542 m_allocated = 256;
543 if ((cType == DB_CTYPE_STRING) || (cType == DB_CTYPE_INT64) || (cType == DB_CTYPE_UINT64))
544 {
545 m_elementSize = sizeof(UCS2CHAR);
546 m_string = true;
547 m_oraType = (sqlType == DB_SQLTYPE_TEXT) ? SQLT_LNG : SQLT_STR;
548 m_data = NULL;
549 m_strings = (UCS2CHAR **)calloc(m_allocated, sizeof(UCS2CHAR *));
550 }
551 else
552 {
553 m_elementSize = s_bufferSize[cType];
554 m_string = false;
555 m_oraType = s_oracleType[cType];
556 m_data = calloc(m_allocated, m_elementSize);
557 m_strings = NULL;
558 }
559 }
560
561 /**
562 * Batch bind - destructor
563 */
564 OracleBatchBind::~OracleBatchBind()
565 {
566 if (m_strings != NULL)
567 {
568 for(int i = 0; i < m_size; i++)
569 safe_free(m_strings[i]);
570 free(m_strings);
571 }
572 safe_free(m_data);
573 }
574
575 /**
576 * Batch bind - add row
577 */
578 void OracleBatchBind::addRow()
579 {
580 if (m_size == m_allocated)
581 {
582 m_allocated += 256;
583 if (m_string)
584 {
585 m_strings = (UCS2CHAR **)realloc(m_strings, m_allocated * sizeof(UCS2CHAR *));
586 memset(m_strings + m_size, 0, (m_allocated - m_size) * sizeof(UCS2CHAR *));
587 }
588 else
589 {
590 m_data = realloc(m_data, m_allocated * m_elementSize);
591 memset((char *)m_data + m_size * m_elementSize, 0, (m_allocated - m_size) * m_elementSize);
592 }
593 }
594 if (m_size > 0)
595 {
596 // clone last element
597 if (m_string)
598 {
599 UCS2CHAR *p = m_strings[m_size - 1];
600 m_strings[m_size] = (p != NULL) ? ucs2_strdup(p) : NULL;
601 }
602 else
603 {
604 memcpy((char *)m_data + m_size * m_elementSize, (char *)m_data + (m_size - 1) * m_elementSize, m_elementSize);
605 }
606 }
607 m_size++;
608 }
609
610 /**
611 * Batch bind - set value
612 */
613 void OracleBatchBind::set(void *value)
614 {
615 if (m_string)
616 {
617 safe_free(m_strings[m_size - 1]);
618 m_strings[m_size - 1] = (UCS2CHAR *)value;
619 if (value != NULL)
620 {
621 int l = (int)(ucs2_strlen((UCS2CHAR *)value) + 1) * sizeof(UCS2CHAR);
622 if (l > m_elementSize)
623 m_elementSize = l;
624 }
625 }
626 else
627 {
628 memcpy((char *)m_data + (m_size - 1) * m_elementSize, value, m_elementSize);
629 }
630 }
631
632 /**
633 * Get data for OCI bind
634 */
635 void *OracleBatchBind::getData()
636 {
637 if (!m_string)
638 return m_data;
639
640 safe_free(m_data);
641 m_data = calloc(m_size, m_elementSize);
642 char *p = (char *)m_data;
643 for(int i = 0; i < m_size; i++)
644 {
645 if (m_strings[i] == NULL)
646 continue;
647 memcpy(p, m_strings[i], ucs2_strlen(m_strings[i]) * sizeof(UCS2CHAR));
648 p += m_elementSize;
649 }
650 return m_data;
651 }
652
653 /**
654 * Bind parameter to statement - batch mode
655 */
656 static void BindBatch(ORACLE_STATEMENT *stmt, int pos, int sqlType, int cType, void *buffer, int allocType)
657 {
658 if (stmt->batchSize == 0)
659 return; // no batch rows added yet
660
661 OracleBatchBind *bind = stmt->batchBindings->get(pos - 1);
662 if (bind == NULL)
663 {
664 bind = new OracleBatchBind(cType, sqlType);
665 stmt->batchBindings->set(pos - 1, bind);
666 for(int i = 0; i < stmt->batchSize; i++)
667 bind->addRow();
668 }
669
670 if (bind->getCType() != cType)
671 return;
672
673 void *sqlBuffer;
674 switch(bind->getCType())
675 {
676 case DB_CTYPE_STRING:
677 #if UNICODE_UCS4
678 sqlBuffer = UCS2StringFromUCS4String((WCHAR *)buffer);
679 if (allocType == DB_BIND_DYNAMIC)
680 free(buffer);
681 #else
682 if (allocType == DB_BIND_DYNAMIC)
683 {
684 sqlBuffer = buffer;
685 }
686 else
687 {
688 sqlBuffer = wcsdup((WCHAR *)buffer);
689 }
690 #endif
691 bind->set(sqlBuffer);
692 break;
693 case DB_CTYPE_INT64: // OCI prior to 11.2 cannot bind 64 bit integers
694 #ifdef UNICODE_UCS2
695 sqlBuffer = malloc(64 * sizeof(WCHAR));
696 swprintf((WCHAR *)sqlBuffer, 64, INT64_FMTW, *((INT64 *)buffer));
697 #else
698 {
699 char temp[64];
700 snprintf(temp, 64, INT64_FMTA, *((INT64 *)buffer));
701 sqlBuffer = UCS2StringFromMBString(temp);
702 }
703 #endif
704 bind->set(sqlBuffer);
705 if (allocType == DB_BIND_DYNAMIC)
706 free(buffer);
707 break;
708 case DB_CTYPE_UINT64: // OCI prior to 11.2 cannot bind 64 bit integers
709 #ifdef UNICODE_UCS2
710 sqlBuffer = malloc(64 * sizeof(WCHAR));
711 swprintf((WCHAR *)sqlBuffer, 64, UINT64_FMTW, *((QWORD *)buffer));
712 #else
713 {
714 char temp[64];
715 snprintf(temp, 64, UINT64_FMTA, *((QWORD *)buffer));
716 sqlBuffer = UCS2StringFromMBString(temp);
717 }
718 #endif
719 bind->set(sqlBuffer);
720 if (allocType == DB_BIND_DYNAMIC)
721 free(buffer);
722 break;
723 default:
724 bind->set(buffer);
725 if (allocType == DB_BIND_DYNAMIC)
726 free(buffer);
727 break;
728 }
729 }
730
731 /**
732 * Bind parameter to statement
733 */
734 extern "C" void EXPORT DrvBind(ORACLE_STATEMENT *stmt, int pos, int sqlType, int cType, void *buffer, int allocType)
735 {
736 if (stmt->batchMode)
737 BindBatch(stmt, pos, sqlType, cType, buffer, allocType);
738 else
739 BindNormal(stmt, pos, sqlType, cType, buffer, allocType);
740 }
741
742 /**
743 * Execute prepared non-select statement
744 */
745 extern "C" DWORD EXPORT DrvExecute(ORACLE_CONN *pConn, ORACLE_STATEMENT *stmt, WCHAR *errorText)
746 {
747 DWORD dwResult;
748
749 if (stmt->batchMode)
750 {
751 if (stmt->batchSize == 0)
752 {
753 stmt->batchMode = false;
754 stmt->batchBindings->clear();
755 return DBERR_SUCCESS; // empty batch
756 }
757
758 for(int i = 0; i < stmt->batchBindings->size(); i++)
759 {
760 OracleBatchBind *b = stmt->batchBindings->get(i);
761 if (b == NULL)
762 continue;
763
764 OCIBind *handleBind = NULL;
765 OCIBindByPos(stmt->handleStmt, &handleBind, stmt->handleError, i + 1, b->getData(),
766 b->getElementSize(), b->getOraType(), NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);
767 }
768 }
769
770 MutexLock(pConn->mutexQueryLock);
771 if (OCIStmtExecute(pConn->handleService, stmt->handleStmt, pConn->handleError,
772 stmt->batchMode ? stmt->batchSize : 1, 0, NULL, NULL,
773 (pConn->nTransLevel == 0) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT) == OCI_SUCCESS)
774 {
775 dwResult = DBERR_SUCCESS;
776 }
777 else
778 {
779 SetLastError(pConn);
780 dwResult = IsConnectionError(pConn);
781 }
782
783 if (errorText != NULL)
784 {
785 wcsncpy(errorText, pConn->lastErrorText, DBDRV_MAX_ERROR_TEXT);
786 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
787 }
788 MutexUnlock(pConn->mutexQueryLock);
789
790 if (stmt->batchMode)
791 {
792 stmt->batchMode = false;
793 stmt->batchBindings->clear();
794 }
795
796 return dwResult;
797 }
798
799 /**
800 * Destroy prepared statement
801 */
802 extern "C" void EXPORT DrvFreeStatement(ORACLE_STATEMENT *stmt)
803 {
804 if (stmt == NULL)
805 return;
806
807 MutexLock(stmt->connection->mutexQueryLock);
808 OCIStmtRelease(stmt->handleStmt, stmt->handleError, NULL, 0, OCI_DEFAULT);
809 OCIHandleFree(stmt->handleError, OCI_HTYPE_ERROR);
810 MutexUnlock(stmt->connection->mutexQueryLock);
811 delete stmt->bindings;
812 delete stmt->batchBindings;
813 delete stmt->buffers;
814 free(stmt);
815 }
816
817 /**
818 * Perform non-SELECT query
819 */
820 static DWORD DrvQueryInternal(ORACLE_CONN *pConn, const WCHAR *pwszQuery, WCHAR *errorText)
821 {
822 OCIStmt *handleStmt;
823 DWORD dwResult;
824
825 #if UNICODE_UCS4
826 UCS2CHAR *ucs2Query = UCS2StringFromUCS4String(pwszQuery);
827 #else
828 const UCS2CHAR *ucs2Query = pwszQuery;
829 #endif
830
831 MutexLock(pConn->mutexQueryLock);
832 if (OCIStmtPrepare2(pConn->handleService, &handleStmt, pConn->handleError, (text *)ucs2Query,
833 (ub4)ucs2_strlen(ucs2Query) * sizeof(UCS2CHAR), NULL, 0, OCI_NTV_SYNTAX, OCI_DEFAULT) == OCI_SUCCESS)
834 {
835 if (OCIStmtExecute(pConn->handleService, handleStmt, pConn->handleError, 1, 0, NULL, NULL,
836 (pConn->nTransLevel == 0) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT) == OCI_SUCCESS)
837 {
838 dwResult = DBERR_SUCCESS;
839 }
840 else
841 {
842 SetLastError(pConn);
843 dwResult = IsConnectionError(pConn);
844 }
845 OCIStmtRelease(handleStmt, pConn->handleError, NULL, 0, OCI_DEFAULT);
846 }
847 else
848 {
849 SetLastError(pConn);
850 dwResult = IsConnectionError(pConn);
851 }
852 if (errorText != NULL)
853 {
854 wcsncpy(errorText, pConn->lastErrorText, DBDRV_MAX_ERROR_TEXT);
855 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
856 }
857 MutexUnlock(pConn->mutexQueryLock);
858
859 #if UNICODE_UCS4
860 free(ucs2Query);
861 #endif
862 return dwResult;
863 }
864
865 /**
866 * Perform non-SELECT query - entry point
867 */
868 extern "C" DWORD EXPORT DrvQuery(ORACLE_CONN *conn, const WCHAR *query, WCHAR *errorText)
869 {
870 return DrvQueryInternal(conn, query, errorText);
871 }
872
873 /**
874 * Process SELECT results
875 */
876 static ORACLE_RESULT *ProcessQueryResults(ORACLE_CONN *pConn, OCIStmt *handleStmt, DWORD *pdwError)
877 {
878 OCIParam *handleParam;
879 OCIDefine *handleDefine;
880 ub4 nCount;
881 ub2 nWidth;
882 sword nStatus;
883 ORACLE_FETCH_BUFFER *pBuffers;
884 text *colName;
885
886 ORACLE_RESULT *pResult = (ORACLE_RESULT *)malloc(sizeof(ORACLE_RESULT));
887 pResult->nRows = 0;
888 pResult->pData = NULL;
889 pResult->columnNames = NULL;
890 OCIAttrGet(handleStmt, OCI_HTYPE_STMT, &nCount, NULL, OCI_ATTR_PARAM_COUNT, pConn->handleError);
891 pResult->nCols = nCount;
892 if (pResult->nCols > 0)
893 {
894 // Prepare receive buffers and fetch column names
895 pResult->columnNames = (char **)calloc(pResult->nCols, sizeof(char *));
896 pBuffers = (ORACLE_FETCH_BUFFER *)calloc(pResult->nCols, sizeof(ORACLE_FETCH_BUFFER));
897 for(int i = 0; i < pResult->nCols; i++)
898 {
899 if ((nStatus = OCIParamGet(handleStmt, OCI_HTYPE_STMT, pConn->handleError,
900 (void **)&handleParam, (ub4)(i + 1))) == OCI_SUCCESS)
901 {
902 // Column name
903 if (OCIAttrGet(handleParam, OCI_DTYPE_PARAM, &colName, &nCount, OCI_ATTR_NAME, pConn->handleError) == OCI_SUCCESS)
904 {
905 // We are in UTF-16 mode, so OCIAttrGet will return UTF-16 strings
906 pResult->columnNames[i] = MBStringFromUCS2String((UCS2CHAR *)colName);
907 }
908 else
909 {
910 pResult->columnNames[i] = strdup("");
911 }
912
913 // Data size
914 ub2 type = 0;
915 OCIAttrGet(handleParam, OCI_DTYPE_PARAM, &type, NULL, OCI_ATTR_DATA_TYPE, pConn->handleError);
916 if (type == OCI_TYPECODE_CLOB)
917 {
918 pBuffers[i].pData = NULL;
919 OCIDescriptorAlloc(pConn->handleEnv, (void **)&pBuffers[i].lobLocator, OCI_DTYPE_LOB, 0, NULL);
920 handleDefine = NULL;
921 nStatus = OCIDefineByPos(handleStmt, &handleDefine, pConn->handleError, i + 1,
922 &pBuffers[i].lobLocator, 0, SQLT_CLOB, &pBuffers[i].isNull,
923 NULL, NULL, OCI_DEFAULT);
924 }
925 else
926 {
927 pBuffers[i].lobLocator = NULL;
928 OCIAttrGet(handleParam, OCI_DTYPE_PARAM, &nWidth, NULL, OCI_ATTR_DATA_SIZE, pConn->handleError);
929 pBuffers[i].pData = (UCS2CHAR *)malloc((nWidth + 31) * sizeof(UCS2CHAR));
930 handleDefine = NULL;
931 nStatus = OCIDefineByPos(handleStmt, &handleDefine, pConn->handleError, i + 1,
932 pBuffers[i].pData, (nWidth + 31) * sizeof(UCS2CHAR),
933 SQLT_CHR, &pBuffers[i].isNull, &pBuffers[i].nLength,
934 &pBuffers[i].nCode, OCI_DEFAULT);
935 }
936 if (nStatus != OCI_SUCCESS)
937 {
938 SetLastError(pConn);
939 *pdwError = IsConnectionError(pConn);
940 }
941 OCIDescriptorFree(handleParam, OCI_DTYPE_PARAM);
942 }
943 else
944 {
945 SetLastError(pConn);
946 *pdwError = IsConnectionError(pConn);
947 }
948 }
949
950 // Fetch data
951 if (nStatus == OCI_SUCCESS)
952 {
953 int nPos = 0;
954 while(1)
955 {
956 nStatus = OCIStmtFetch2(handleStmt, pConn->handleError, 1, OCI_FETCH_NEXT, 0, OCI_DEFAULT);
957 if (nStatus == OCI_NO_DATA)
958 {
959 *pdwError = DBERR_SUCCESS; // EOF
960 break;
961 }
962 if ((nStatus != OCI_SUCCESS) && (nStatus != OCI_SUCCESS_WITH_INFO))
963 {
964 SetLastError(pConn);
965 *pdwError = IsConnectionError(pConn);
966 break;
967 }
968
969 // New row
970 pResult->nRows++;
971 pResult->pData = (WCHAR **)realloc(pResult->pData, sizeof(WCHAR *) * pResult->nCols * pResult->nRows);
972 for(int i = 0; i < pResult->nCols; i++)
973 {
974 if (pBuffers[i].isNull)
975 {
976 pResult->pData[nPos] = (WCHAR *)nx_memdup("\0\0\0", sizeof(WCHAR));
977 }
978 else if (pBuffers[i].lobLocator != NULL)
979 {
980 ub4 length = 0;
981 ub4 amount = length;
982 OCILobGetLength(pConn->handleService, pConn->handleError, pBuffers[i].lobLocator, &length);
983 pResult->pData[nPos] = (WCHAR *)malloc((length + 1) * sizeof(WCHAR));
984 #if UNICODE_UCS4
985 UCS2CHAR *ucs2buffer = (UCS2CHAR *)malloc(sizeof(UCS2CHAR) * length);
986 OCILobRead(pConn->handleService, pConn->handleError, pBuffers[i].lobLocator, &amount, 1,
987 ucs2buffer, length * sizeof(UCS2CHAR), NULL, NULL, OCI_UCS2ID, SQLCS_IMPLICIT);
988 ucs2_to_ucs4(ucs2buffer, length, pResult->pData[nPos], length + 1);
989 free(ucs2buffer);
990 #else
991 OCILobRead(pConn->handleService, pConn->handleError, pBuffers[i].lobLocator, &amount, 1,
992 pResult->pData[nPos], (length + 1) * sizeof(WCHAR), NULL, NULL, OCI_UCS2ID, SQLCS_IMPLICIT);
993 #endif
994 pResult->pData[nPos][length] = 0;
995 }
996 else
997 {
998 int length = pBuffers[i].nLength / sizeof(UCS2CHAR);
999 pResult->pData[nPos] = (WCHAR *)malloc((length + 1) * sizeof(WCHAR));
1000 #if UNICODE_UCS4
1001 ucs2_to_ucs4(pBuffers[i].pData, length, pResult->pData[nPos], length + 1);
1002 #else
1003 memcpy(pResult->pData[nPos], pBuffers[i].pData, pBuffers[i].nLength);
1004 #endif
1005 pResult->pData[nPos][length] = 0;
1006 }
1007 nPos++;
1008 }
1009 }
1010 }
1011
1012 // Cleanup
1013 for(int i = 0; i < pResult->nCols; i++)
1014 {
1015 safe_free(pBuffers[i].pData);
1016 if (pBuffers[i].lobLocator != NULL)
1017 {
1018 OCIDescriptorFree(pBuffers[i].lobLocator, OCI_DTYPE_LOB);
1019 }
1020 }
1021 free(pBuffers);
1022
1023 // Destroy results in case of error
1024 if (*pdwError != DBERR_SUCCESS)
1025 {
1026 DestroyQueryResult(pResult);
1027 pResult = NULL;
1028 }
1029 }
1030
1031 return pResult;
1032 }
1033
1034 /**
1035 * Perform SELECT query
1036 */
1037 extern "C" DBDRV_RESULT EXPORT DrvSelect(ORACLE_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText)
1038 {
1039 ORACLE_RESULT *pResult = NULL;
1040 OCIStmt *handleStmt;
1041
1042 #if UNICODE_UCS4
1043 UCS2CHAR *ucs2Query = UCS2StringFromUCS4String(pwszQuery);
1044 #else
1045 UCS2CHAR *ucs2Query = pwszQuery;
1046 #endif
1047
1048 MutexLock(pConn->mutexQueryLock);
1049 if (OCIStmtPrepare2(pConn->handleService, &handleStmt, pConn->handleError, (text *)ucs2Query,
1050 (ub4)ucs2_strlen(ucs2Query) * sizeof(UCS2CHAR), NULL, 0, OCI_NTV_SYNTAX, OCI_DEFAULT) == OCI_SUCCESS)
1051 {
1052 OCIAttrSet(handleStmt, OCI_HTYPE_STMT, &pConn->prefetchLimit, 0, OCI_ATTR_PREFETCH_ROWS, pConn->handleError);
1053 if (OCIStmtExecute(pConn->handleService, handleStmt, pConn->handleError,
1054 0, 0, NULL, NULL, (pConn->nTransLevel == 0) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT) == OCI_SUCCESS)
1055 {
1056 pResult = ProcessQueryResults(pConn, handleStmt, pdwError);
1057 }
1058 else
1059 {
1060 SetLastError(pConn);
1061 *pdwError = IsConnectionError(pConn);
1062 }
1063 OCIStmtRelease(handleStmt, pConn->handleError, NULL, 0, OCI_DEFAULT);
1064 }
1065 else
1066 {
1067 SetLastError(pConn);
1068 *pdwError = IsConnectionError(pConn);
1069 }
1070 if (errorText != NULL)
1071 {
1072 wcsncpy(errorText, pConn->lastErrorText, DBDRV_MAX_ERROR_TEXT);
1073 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
1074 }
1075 MutexUnlock(pConn->mutexQueryLock);
1076
1077 #if UNICODE_UCS4
1078 free(ucs2Query);
1079 #endif
1080 return pResult;
1081 }
1082
1083 /**
1084 * Perform SELECT query using prepared statement
1085 */
1086 extern "C" DBDRV_RESULT EXPORT DrvSelectPrepared(ORACLE_CONN *pConn, ORACLE_STATEMENT *stmt, DWORD *pdwError, WCHAR *errorText)
1087 {
1088 ORACLE_RESULT *pResult = NULL;
1089
1090 MutexLock(pConn->mutexQueryLock);
1091 OCIAttrSet(stmt->handleStmt, OCI_HTYPE_STMT, &pConn->prefetchLimit, 0, OCI_ATTR_PREFETCH_ROWS, pConn->handleError);
1092 if (OCIStmtExecute(pConn->handleService, stmt->handleStmt, pConn->handleError,
1093 0, 0, NULL, NULL, (pConn->nTransLevel == 0) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT) == OCI_SUCCESS)
1094 {
1095 pResult = ProcessQueryResults(pConn, stmt->handleStmt, pdwError);
1096 }
1097 else
1098 {
1099 SetLastError(pConn);
1100 *pdwError = IsConnectionError(pConn);
1101 }
1102
1103 if (errorText != NULL)
1104 {
1105 wcsncpy(errorText, pConn->lastErrorText, DBDRV_MAX_ERROR_TEXT);
1106 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
1107 }
1108 MutexUnlock(pConn->mutexQueryLock);
1109
1110 return pResult;
1111 }
1112
1113 /**
1114 * Get field length from result
1115 */
1116 extern "C" LONG EXPORT DrvGetFieldLength(ORACLE_RESULT *pResult, int nRow, int nColumn)
1117 {
1118 if (pResult == NULL)
1119 return -1;
1120
1121 if ((nRow >= 0) && (nRow < pResult->nRows) &&
1122 (nColumn >= 0) && (nColumn < pResult->nCols))
1123 return (LONG)wcslen(pResult->pData[pResult->nCols * nRow + nColumn]);
1124
1125 return -1;
1126 }
1127
1128 /**
1129 * Get field value from result
1130 */
1131 extern "C" WCHAR EXPORT *DrvGetField(ORACLE_RESULT *pResult, int nRow, int nColumn,
1132 WCHAR *pBuffer, int nBufLen)
1133 {
1134 WCHAR *pValue = NULL;
1135
1136 if (pResult != NULL)
1137 {
1138 if ((nRow < pResult->nRows) && (nRow >= 0) &&
1139 (nColumn < pResult->nCols) && (nColumn >= 0))
1140 {
1141 #ifdef _WIN32
1142 wcsncpy_s(pBuffer, nBufLen, pResult->pData[nRow * pResult->nCols + nColumn], _TRUNCATE);
1143 #else
1144 wcsncpy(pBuffer, pResult->pData[nRow * pResult->nCols + nColumn], nBufLen);
1145 pBuffer[nBufLen - 1] = 0;
1146 #endif
1147 pValue = pBuffer;
1148 }
1149 }
1150 return pValue;
1151 }
1152
1153 /**
1154 * Get number of rows in result
1155 */
1156 extern "C" int EXPORT DrvGetNumRows(ORACLE_RESULT *pResult)
1157 {
1158 return (pResult != NULL) ? pResult->nRows : 0;
1159 }
1160
1161 /**
1162 * Get column count in query result
1163 */
1164 extern "C" int EXPORT DrvGetColumnCount(ORACLE_RESULT *pResult)
1165 {
1166 return (pResult != NULL) ? pResult->nCols : 0;
1167 }
1168
1169 /**
1170 * Get column name in query result
1171 */
1172 extern "C" const char EXPORT *DrvGetColumnName(ORACLE_RESULT *pResult, int column)
1173 {
1174 return ((pResult != NULL) && (column >= 0) && (column < pResult->nCols)) ? pResult->columnNames[column] : NULL;
1175 }
1176
1177 /**
1178 * Free SELECT results
1179 */
1180 extern "C" void EXPORT DrvFreeResult(ORACLE_RESULT *pResult)
1181 {
1182 if (pResult != NULL)
1183 DestroyQueryResult(pResult);
1184 }
1185
1186 /**
1187 * Destroy unbuffered query result
1188 */
1189 static void DestroyUnbufferedQueryResult(ORACLE_UNBUFFERED_RESULT *result, bool freeStatement)
1190 {
1191 int i;
1192
1193 if (freeStatement)
1194 OCIStmtRelease(result->handleStmt, result->connection->handleError, NULL, 0, OCI_DEFAULT);
1195
1196 for(i = 0; i < result->nCols; i++)
1197 {
1198 free(result->pBuffers[i].pData);
1199 if (result->pBuffers[i].lobLocator != NULL)
1200 {
1201 OCIDescriptorFree(result->pBuffers[i].lobLocator, OCI_DTYPE_LOB);
1202 }
1203 }
1204 free(result->pBuffers);
1205
1206 for(i = 0; i < result->nCols; i++)
1207 free(result->columnNames[i]);
1208 free(result->columnNames);
1209
1210 free(result);
1211 }
1212
1213 /**
1214 * Process results of unbuffered query execution (prepare for fetching results)
1215 */
1216 static ORACLE_UNBUFFERED_RESULT *ProcessUnbufferedQueryResults(ORACLE_CONN *pConn, OCIStmt *handleStmt, DWORD *pdwError)
1217 {
1218 ORACLE_UNBUFFERED_RESULT *result = (ORACLE_UNBUFFERED_RESULT *)malloc(sizeof(ORACLE_UNBUFFERED_RESULT));
1219 result->handleStmt = handleStmt;
1220 result->connection = pConn;
1221
1222 ub4 nCount;
1223 OCIAttrGet(result->handleStmt, OCI_HTYPE_STMT, &nCount, NULL, OCI_ATTR_PARAM_COUNT, pConn->handleError);
1224 result->nCols = nCount;
1225 if (result->nCols > 0)
1226 {
1227 // Prepare receive buffers and fetch column names
1228 result->columnNames = (char **)calloc(result->nCols, sizeof(char *));
1229 result->pBuffers = (ORACLE_FETCH_BUFFER *)calloc(result->nCols, sizeof(ORACLE_FETCH_BUFFER));
1230 for(int i = 0; i < result->nCols; i++)
1231 {
1232 OCIParam *handleParam;
1233
1234 result->pBuffers[i].isNull = 1; // Mark all columns as NULL initially
1235 if (OCIParamGet(result->handleStmt, OCI_HTYPE_STMT, pConn->handleError,
1236 (void **)&handleParam, (ub4)(i + 1)) == OCI_SUCCESS)
1237 {
1238 // Column name
1239 text *colName;
1240 if (OCIAttrGet(handleParam, OCI_DTYPE_PARAM, &colName, &nCount, OCI_ATTR_NAME, pConn->handleError) == OCI_SUCCESS)
1241 {
1242 // We are in UTF-16 mode, so OCIAttrGet will return UTF-16 strings
1243 result->columnNames[i] = MBStringFromUCS2String((UCS2CHAR *)colName);
1244 }
1245 else
1246 {
1247 result->columnNames[i] = strdup("");
1248 }
1249
1250 // Data size
1251 sword nStatus;
1252 ub2 type = 0;
1253 OCIAttrGet(handleParam, OCI_DTYPE_PARAM, &type, NULL, OCI_ATTR_DATA_TYPE, pConn->handleError);
1254 OCIDefine *handleDefine;
1255 if (type == OCI_TYPECODE_CLOB)
1256 {
1257 result->pBuffers[i].pData = NULL;
1258 OCIDescriptorAlloc(pConn->handleEnv, (void **)&result->pBuffers[i].lobLocator, OCI_DTYPE_LOB, 0, NULL);
1259 handleDefine = NULL;
1260 nStatus = OCIDefineByPos(result->handleStmt, &handleDefine, pConn->handleError, i + 1,
1261 &result->pBuffers[i].lobLocator, 0, SQLT_CLOB, &result->pBuffers[i].isNull,
1262 NULL, NULL, OCI_DEFAULT);
1263 }
1264 else
1265 {
1266 ub2 nWidth;
1267 result->pBuffers[i].lobLocator = NULL;
1268 OCIAttrGet(handleParam, OCI_DTYPE_PARAM, &nWidth, NULL, OCI_ATTR_DATA_SIZE, pConn->handleError);
1269 result->pBuffers[i].pData = (UCS2CHAR *)malloc((nWidth + 31) * sizeof(UCS2CHAR));
1270 handleDefine = NULL;
1271 nStatus = OCIDefineByPos(result->handleStmt, &handleDefine, pConn->handleError, i + 1,
1272 result->pBuffers[i].pData, (nWidth + 31) * sizeof(UCS2CHAR),
1273 SQLT_CHR, &result->pBuffers[i].isNull, &result->pBuffers[i].nLength,
1274 &result->pBuffers[i].nCode, OCI_DEFAULT);
1275 }
1276 OCIDescriptorFree(handleParam, OCI_DTYPE_PARAM);
1277 if (nStatus == OCI_SUCCESS)
1278 {
1279 *pdwError = DBERR_SUCCESS;
1280 }
1281 else
1282 {
1283 SetLastError(pConn);
1284 *pdwError = IsConnectionError(pConn);
1285 DestroyUnbufferedQueryResult(result, false);
1286 result = NULL;
1287 break;
1288 }
1289 }
1290 else
1291 {
1292 SetLastError(pConn);
1293 *pdwError = IsConnectionError(pConn);
1294 DestroyUnbufferedQueryResult(result, false);
1295 result = NULL;
1296 break;
1297 }
1298 }
1299 }
1300 else
1301 {
1302 free(result);
1303 result = NULL;
1304 }
1305
1306 return result;
1307 }
1308
1309 /**
1310 * Perform unbuffered SELECT query
1311 */
1312 extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectUnbuffered(ORACLE_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText)
1313 {
1314 ORACLE_UNBUFFERED_RESULT *result = NULL;
1315
1316 #if UNICODE_UCS4
1317 UCS2CHAR *ucs2Query = UCS2StringFromUCS4String(pwszQuery);
1318 #else
1319 UCS2CHAR *ucs2Query = pwszQuery;
1320 #endif
1321
1322 MutexLock(pConn->mutexQueryLock);
1323
1324 OCIStmt *handleStmt;
1325 if (OCIStmtPrepare2(pConn->handleService, &handleStmt, pConn->handleError, (text *)ucs2Query,
1326 (ub4)ucs2_strlen(ucs2Query) * sizeof(UCS2CHAR), NULL, 0, OCI_NTV_SYNTAX, OCI_DEFAULT) == OCI_SUCCESS)
1327 {
1328 OCIAttrSet(handleStmt, OCI_HTYPE_STMT, &pConn->prefetchLimit, 0, OCI_ATTR_PREFETCH_ROWS, pConn->handleError);
1329 if (OCIStmtExecute(pConn->handleService, handleStmt, pConn->handleError,
1330 0, 0, NULL, NULL, (pConn->nTransLevel == 0) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT) == OCI_SUCCESS)
1331 {
1332 result = ProcessUnbufferedQueryResults(pConn, handleStmt, pdwError);
1333 }
1334 else
1335 {
1336 SetLastError(pConn);
1337 *pdwError = IsConnectionError(pConn);
1338 }
1339 }
1340 else
1341 {
1342 SetLastError(pConn);
1343 *pdwError = IsConnectionError(pConn);
1344 }
1345
1346 #if UNICODE_UCS4
1347 free(ucs2Query);
1348 #endif
1349
1350 if ((*pdwError == DBERR_SUCCESS) && (result != NULL))
1351 return result;
1352
1353 // On failure, unlock query mutex and do cleanup
1354 OCIStmtRelease(handleStmt, pConn->handleError, NULL, 0, OCI_DEFAULT);
1355 if (errorText != NULL)
1356 {
1357 wcsncpy(errorText, pConn->lastErrorText, DBDRV_MAX_ERROR_TEXT);
1358 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
1359 }
1360 MutexUnlock(pConn->mutexQueryLock);
1361 return NULL;
1362 }
1363
1364 /**
1365 * Perform SELECT query using prepared statement
1366 */
1367 extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectPreparedUnbuffered(ORACLE_CONN *pConn, ORACLE_STATEMENT *stmt, DWORD *pdwError, WCHAR *errorText)
1368 {
1369 ORACLE_UNBUFFERED_RESULT *result = NULL;
1370
1371 MutexLock(pConn->mutexQueryLock);
1372
1373 OCIAttrSet(stmt->handleStmt, OCI_HTYPE_STMT, &pConn->prefetchLimit, 0, OCI_ATTR_PREFETCH_ROWS, pConn->handleError);
1374 if (OCIStmtExecute(pConn->handleService, stmt->handleStmt, pConn->handleError,
1375 0, 0, NULL, NULL, (pConn->nTransLevel == 0) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT) == OCI_SUCCESS)
1376 {
1377 result = ProcessUnbufferedQueryResults(pConn, stmt->handleStmt, pdwError);
1378 }
1379 else
1380 {
1381 SetLastError(pConn);
1382 *pdwError = IsConnectionError(pConn);
1383 }
1384
1385 if ((*pdwError == DBERR_SUCCESS) && (result != NULL))
1386 return result;
1387
1388 // On failure, unlock query mutex and do cleanup
1389 if (errorText != NULL)
1390 {
1391 wcsncpy(errorText, pConn->lastErrorText, DBDRV_MAX_ERROR_TEXT);
1392 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
1393 }
1394 MutexUnlock(pConn->mutexQueryLock);
1395 return NULL;
1396 }
1397
1398 /**
1399 * Fetch next result line from unbuffered SELECT results
1400 */
1401 extern "C" bool EXPORT DrvFetch(ORACLE_UNBUFFERED_RESULT *result)
1402 {
1403 bool success;
1404
1405 if (result == NULL)
1406 return false;
1407
1408 sword rc = OCIStmtFetch2(result->handleStmt, result->connection->handleError, 1, OCI_FETCH_NEXT, 0, OCI_DEFAULT);
1409 if ((rc == OCI_SUCCESS) || (rc == OCI_SUCCESS_WITH_INFO))
1410 {
1411 success = true;
1412 }
1413 else
1414 {
1415 SetLastError(result->connection);
1416 success = false;
1417 }
1418 return success;
1419 }
1420
1421 /**
1422 * Get field length from current row in unbuffered query result
1423 */
1424 extern "C" LONG EXPORT DrvGetFieldLengthUnbuffered(ORACLE_UNBUFFERED_RESULT *result, int nColumn)
1425 {
1426 if (result == NULL)
1427 return 0;
1428
1429 if ((nColumn < 0) || (nColumn >= result->nCols))
1430 return 0;
1431
1432 if (result->pBuffers[nColumn].isNull)
1433 return 0;
1434
1435 if (result->pBuffers[nColumn].lobLocator != NULL)
1436 {
1437 ub4 length = 0;
1438 OCILobGetLength(result->connection->handleService, result->connection->handleError, result->pBuffers[nColumn].lobLocator, &length);
1439 return (LONG)length;
1440 }
1441
1442 return (LONG)(result->pBuffers[nColumn].nLength / sizeof(UCS2CHAR));
1443 }
1444
1445 /**
1446 * Get field from current row in unbuffered query result
1447 */
1448 extern "C" WCHAR EXPORT *DrvGetFieldUnbuffered(ORACLE_UNBUFFERED_RESULT *result, int nColumn, WCHAR *pBuffer, int nBufSize)
1449 {
1450 int nLen;
1451
1452 if (result == NULL)
1453 return NULL;
1454
1455 if ((nColumn < 0) || (nColumn >= result->nCols))
1456 return NULL;
1457
1458 if (result->pBuffers[nColumn].isNull)
1459 {
1460 *pBuffer = 0;
1461 }
1462 else if (result->pBuffers[nColumn].lobLocator != NULL)
1463 {
1464 ub4 length = 0;
1465 OCILobGetLength(result->connection->handleService, result->connection->handleError, result->pBuffers[nColumn].lobLocator, &length);
1466
1467 nLen = min(nBufSize - 1, (int)length);
1468 ub4 amount = nLen;
1469 #if UNICODE_UCS4
1470 UCS2CHAR *ucs2buffer = (UCS2CHAR *)malloc(nLen * sizeof(UCS2CHAR));
1471 OCILobRead(result->connection->handleService, result->connection->handleError, result->pBuffers[nColumn].lobLocator, &amount, 1,
1472 ucs2buffer, nLen * sizeof(UCS2CHAR), NULL, NULL, OCI_UCS2ID, SQLCS_IMPLICIT);
1473 ucs2_to_ucs4(ucs2buffer, nLen, pBuffer, nLen);
1474 free(ucs2buffer);
1475 #else
1476 OCILobRead(result->connection->handleService, result->connection->handleError, result->pBuffers[nColumn].lobLocator, &amount, 1,
1477 pBuffer, nBufSize * sizeof(WCHAR), NULL, NULL, OCI_UCS2ID, SQLCS_IMPLICIT);
1478 #endif
1479 pBuffer[nLen] = 0;
1480 }
1481 else
1482 {
1483 nLen = min(nBufSize - 1, ((int)(result->pBuffers[nColumn].nLength / sizeof(UCS2CHAR))));
1484 #if UNICODE_UCS4
1485 ucs2_to_ucs4(result->pBuffers[nColumn].pData, nLen, pBuffer, nLen + 1);
1486 #else
1487 memcpy(pBuffer, result->pBuffers[nColumn].pData, nLen * sizeof(WCHAR));
1488 #endif
1489 pBuffer[nLen] = 0;
1490 }
1491
1492 return pBuffer;
1493 }
1494
1495 /**
1496 * Get column count in unbuffered query result
1497 */
1498 extern "C" int EXPORT DrvGetColumnCountUnbuffered(ORACLE_UNBUFFERED_RESULT *result)
1499 {
1500 return (result != NULL) ? result->nCols : 0;
1501 }
1502
1503 /**
1504 * Get column name in unbuffered query result
1505 */
1506 extern "C" const char EXPORT *DrvGetColumnNameUnbuffered(ORACLE_UNBUFFERED_RESULT *result, int column)
1507 {
1508 return ((result != NULL) && (column >= 0) && (column < result->nCols)) ? result->columnNames[column] : NULL;
1509 }
1510
1511 /**
1512 * Destroy result of unbuffered query
1513 */
1514 extern "C" void EXPORT DrvFreeUnbufferedResult(ORACLE_UNBUFFERED_RESULT *result)
1515 {
1516 if (result == NULL)
1517 return;
1518
1519 MUTEX mutex = result->connection->mutexQueryLock;
1520 DestroyUnbufferedQueryResult(result, true);
1521 MutexUnlock(mutex);
1522 }
1523
1524 /**
1525 * Begin transaction
1526 */
1527 extern "C" DWORD EXPORT DrvBegin(ORACLE_CONN *pConn)
1528 {
1529 if (pConn == NULL)
1530 return DBERR_INVALID_HANDLE;
1531
1532 MutexLock(pConn->mutexQueryLock);
1533 pConn->nTransLevel++;
1534 MutexUnlock(pConn->mutexQueryLock);
1535 return DBERR_SUCCESS;
1536 }
1537
1538 /**
1539 * Commit transaction
1540 */
1541 extern "C" DWORD EXPORT DrvCommit(ORACLE_CONN *pConn)
1542 {
1543 DWORD dwResult;
1544
1545 if (pConn == NULL)
1546 return DBERR_INVALID_HANDLE;
1547
1548 MutexLock(pConn->mutexQueryLock);
1549 if (pConn->nTransLevel > 0)
1550 {
1551 if (OCITransCommit(pConn->handleService, pConn->handleError, OCI_DEFAULT) == OCI_SUCCESS)
1552 {
1553 dwResult = DBERR_SUCCESS;
1554 pConn->nTransLevel = 0;
1555 }
1556 else
1557 {
1558 SetLastError(pConn);
1559 dwResult = IsConnectionError(pConn);
1560 }
1561 }
1562 else
1563 {
1564 dwResult = DBERR_SUCCESS;
1565 }
1566 MutexUnlock(pConn->mutexQueryLock);
1567 return dwResult;
1568 }
1569
1570 /**
1571 * Rollback transaction
1572 */
1573 extern "C" DWORD EXPORT DrvRollback(ORACLE_CONN *pConn)
1574 {
1575 DWORD dwResult;
1576
1577 if (pConn == NULL)
1578 return DBERR_INVALID_HANDLE;
1579
1580 MutexLock(pConn->mutexQueryLock);
1581 if (pConn->nTransLevel > 0)
1582 {
1583 if (OCITransRollback(pConn->handleService, pConn->handleError, OCI_DEFAULT) == OCI_SUCCESS)
1584 {
1585 dwResult = DBERR_SUCCESS;
1586 pConn->nTransLevel = 0;
1587 }
1588 else
1589 {
1590 SetLastError(pConn);
1591 dwResult = IsConnectionError(pConn);
1592 }
1593 }
1594 else
1595 {
1596 dwResult = DBERR_SUCCESS;
1597 }
1598 MutexUnlock(pConn->mutexQueryLock);
1599 return dwResult;
1600 }
1601
1602 /**
1603 * Check if table exist
1604 */
1605 extern "C" int EXPORT DrvIsTableExist(ORACLE_CONN *pConn, const WCHAR *name)
1606 {
1607 WCHAR query[256];
1608 swprintf(query, 256, L"SELECT count(*) FROM user_tables WHERE table_name=upper('%ls')", name);
1609 DWORD error;
1610 WCHAR errorText[DBDRV_MAX_ERROR_TEXT];
1611 int rc = DBIsTableExist_Failure;
1612 ORACLE_RESULT *hResult = (ORACLE_RESULT *)DrvSelect(pConn, query, &error, errorText);
1613 if (hResult != NULL)
1614 {
1615 WCHAR buffer[64] = L"";
1616 DrvGetField(hResult, 0, 0, buffer, 64);
1617 rc = (wcstol(buffer, NULL, 10) > 0) ? DBIsTableExist_Found : DBIsTableExist_NotFound;
1618 DrvFreeResult(hResult);
1619 }
1620 return rc;
1621 }
1622
1623 #ifdef _WIN32
1624
1625 /**
1626 * DLL Entry point
1627 */
1628 bool WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
1629 {
1630 if (dwReason == DLL_PROCESS_ATTACH)
1631 DisableThreadLibraryCalls(hInstance);
1632 return true;
1633 }
1634
1635 #endif