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