cosmetic change
[public/netxms.git] / src / db / dbdrv / oracle / oracle.cpp
CommitLineData
5039dede
AK
1/*
2** Oracle Database Driver
792038b9 3** Copyright (C) 2007-2016 Victor Kirhenshtein
5039dede
AK
4**
5** This program is free software; you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation; either version 2 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program; if not, write to the Free Software
17** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18**
19** File: oracle.cpp
20**
21**/
22
23#include "oracledrv.h"
24
f3c30cf5 25DECLARE_DRIVER_HEADER("ORACLE")
5039dede 26
9b4a2a0e 27static DWORD DrvQueryInternal(ORACLE_CONN *pConn, const WCHAR *pwszQuery, WCHAR *errorText);
5d255340 28
d1c1c522
VK
29/**
30 * Prepare string for using in SQL query - enclose in quotes and escape as needed
31 */
a3166a07
VK
32extern "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
74d4ba34
VK
65/**
66 * Prepare string for using in SQL query - enclose in quotes and escape as needed
67 */
a3166a07 68extern "C" char EXPORT *DrvPrepareStringA(const char *str)
643c9dcb 69{
20ee0bf4 70 int len = (int)strlen(str) + 3; // + two quotes and \0 at the end
643c9dcb 71 int bufferSize = len + 128;
a3166a07
VK
72 char *out = (char *)malloc(bufferSize);
73 out[0] = '\'';
643c9dcb 74
a3166a07 75 const char *src = str;
643c9dcb 76 int outPos;
f694a7bc 77 for(outPos = 1; *src != 0; src++)
643c9dcb 78 {
a3166a07 79 if (*src == '\'')
643c9dcb
VK
80 {
81 len++;
82 if (len >= bufferSize)
83 {
84 bufferSize += 128;
a3166a07 85 out = (char *)realloc(out, bufferSize);
643c9dcb 86 }
a3166a07
VK
87 out[outPos++] = '\'';
88 out[outPos++] = '\'';
643c9dcb
VK
89 }
90 else
91 {
92 out[outPos++] = *src;
93 }
94 }
a3166a07 95 out[outPos++] = '\'';
643c9dcb
VK
96 out[outPos++] = 0;
97
98 return out;
99}
100
d1c1c522
VK
101/**
102 * Initialize driver
103 */
2df047f4 104extern "C" bool EXPORT DrvInit(const char *cmdLine)
5039dede 105{
750d59f2 106 return true;
5039dede
AK
107}
108
d1c1c522
VK
109/**
110 * Unload handler
111 */
08b214c6 112extern "C" void EXPORT DrvUnload()
5039dede
AK
113{
114 OCITerminate(OCI_DEFAULT);
115}
116
96e87c92
VK
117/**
118 * Get error text from error handle
119 */
526ae8b8 120static void GetErrorFromHandle(OCIError *handle, sb4 *errorCode, WCHAR *errorText)
5039dede 121{
a3166a07 122#if UNICODE_UCS2
526ae8b8 123 OCIErrorGet(handle, 1, NULL, errorCode, (text *)errorText, DBDRV_MAX_ERROR_TEXT, OCI_HTYPE_ERROR);
465b3f2d 124 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
5039dede 125#else
39945910 126 UCS2CHAR buffer[DBDRV_MAX_ERROR_TEXT];
5039dede 127
526ae8b8 128 OCIErrorGet(handle, 1, NULL, errorCode, (text *)buffer, DBDRV_MAX_ERROR_TEXT, OCI_HTYPE_ERROR);
5039dede 129 buffer[DBDRV_MAX_ERROR_TEXT - 1] = 0;
465b3f2d
VK
130 ucs2_to_ucs4(buffer, ucs2_strlen(buffer) + 1, errorText, DBDRV_MAX_ERROR_TEXT);
131 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
5039dede 132#endif
465b3f2d
VK
133 RemoveTrailingCRLFW(errorText);
134}
135
96e87c92
VK
136/**
137 * Set last error text
138 */
526ae8b8 139static void SetLastError(ORACLE_CONN *pConn)
465b3f2d 140{
526ae8b8 141 GetErrorFromHandle(pConn->handleError, &pConn->lastErrorCode, pConn->lastErrorText);
5039dede
AK
142}
143
96e87c92
VK
144/**
145 * Check if last error was caused by lost connection to server
146 */
526ae8b8 147static DWORD IsConnectionError(ORACLE_CONN *conn)
5039dede
AK
148{
149 ub4 nStatus = 0;
526ae8b8 150 OCIAttrGet(conn->handleServer, OCI_HTYPE_SERVER, &nStatus, NULL, OCI_ATTR_SERVER_STATUS, conn->handleError);
5039dede
AK
151 return (nStatus == OCI_SERVER_NOT_CONNECTED) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR;
152}
153
d1c1c522
VK
154/**
155 * Destroy query result
156 */
5039dede
AK
157static void DestroyQueryResult(ORACLE_RESULT *pResult)
158{
159 int i, nCount;
160
161 nCount = pResult->nCols * pResult->nRows;
162 for(i = 0; i < nCount; i++)
792038b9
VK
163 free(pResult->pData[i]);
164 free(pResult->pData);
2a8e6a7b
VK
165
166 for(i = 0; i < pResult->nCols; i++)
792038b9
VK
167 free(pResult->columnNames[i]);
168 free(pResult->columnNames);
2a8e6a7b 169
5039dede
AK
170 free(pResult);
171}
172
d1c1c522
VK
173/**
174 * Connect to database
175 */
f3c30cf5
VK
176extern "C" DBDRV_CONNECTION EXPORT DrvConnect(const char *host, const char *login, const char *password,
177 const char *database, const char *schema, WCHAR *errorText)
5039dede
AK
178{
179 ORACLE_CONN *pConn;
39945910 180 UCS2CHAR *pwszStr;
5039dede 181
5039dede
AK
182 pConn = (ORACLE_CONN *)malloc(sizeof(ORACLE_CONN));
183 if (pConn != NULL)
184 {
2a8e6a7b
VK
185 memset(pConn, 0, sizeof(ORACLE_CONN));
186
5039dede
AK
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);
f3c30cf5 192 pwszStr = UCS2StringFromMBString(host);
5039dede 193 if (OCIServerAttach(pConn->handleServer, pConn->handleError,
39945910 194 (text *)pwszStr, (sb4)ucs2_strlen(pwszStr) * sizeof(UCS2CHAR), OCI_DEFAULT) == OCI_SUCCESS)
5039dede
AK
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);
f3c30cf5 204 pwszStr = UCS2StringFromMBString(login);
5039dede 205 OCIAttrSet(pConn->handleSession, OCI_HTYPE_SESSION, pwszStr,
39945910 206 (ub4)ucs2_strlen(pwszStr) * sizeof(UCS2CHAR), OCI_ATTR_USERNAME, pConn->handleError);
5039dede 207 free(pwszStr);
f3c30cf5 208 pwszStr = UCS2StringFromMBString(password);
5039dede 209 OCIAttrSet(pConn->handleSession, OCI_HTYPE_SESSION, pwszStr,
39945910 210 (ub4)ucs2_strlen(pwszStr) * sizeof(UCS2CHAR), OCI_ATTR_PASSWORD, pConn->handleError);
5039dede
AK
211
212 // Authenticate
213 if (OCISessionBegin(pConn->handleService, pConn->handleError,
8228010e 214 pConn->handleSession, OCI_CRED_RDBMS, OCI_STMT_CACHE) == OCI_SUCCESS)
5039dede 215 {
acc231ba 216 OCIAttrSet(pConn->handleService, OCI_HTYPE_SVCCTX, pConn->handleSession, 0, OCI_ATTR_SESSION, pConn->handleError);
5039dede
AK
217 pConn->mutexQueryLock = MutexCreate();
218 pConn->nTransLevel = 0;
526ae8b8
VK
219 pConn->lastErrorCode = 0;
220 pConn->lastErrorText[0] = 0;
85dfb586 221 pConn->prefetchLimit = 10;
f3c30cf5
VK
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 }
5d255340
VK
230
231 // Setup session
9b4a2a0e 232 DrvQueryInternal(pConn, L"ALTER SESSION SET NLS_LANGUAGE='AMERICAN' NLS_NUMERIC_CHARACTERS='.,'", NULL);
2df047f4
VK
233
234 UCS2CHAR version[1024];
235 if (OCIServerVersion(pConn->handleService, pConn->handleError, (OraText *)version, sizeof(version), OCI_HTYPE_SVCCTX) == OCI_SUCCESS)
236 {
cb8940b0 237#ifdef UNICODE
2df047f4
VK
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);
cb8940b0
VK
244#endif
245#else
246 char *mbver = MBStringFromUCS2String(version);
247 nxlog_debug(5, _T("ORACLE: connected to %s"), mbver);
248 free(mbver);
2df047f4
VK
249#endif
250 }
5039dede
AK
251 }
252 else
253 {
526ae8b8 254 GetErrorFromHandle(pConn->handleError, &pConn->lastErrorCode, errorText);
acc231ba 255 OCIServerDetach(pConn->handleServer, pConn->handleError, OCI_DEFAULT);
5039dede
AK
256 OCIHandleFree(pConn->handleEnv, OCI_HTYPE_ENV);
257 free(pConn);
258 pConn = NULL;
259 }
260 }
261 else
262 {
526ae8b8 263 GetErrorFromHandle(pConn->handleError, &pConn->lastErrorCode, errorText);
5039dede
AK
264 OCIHandleFree(pConn->handleEnv, OCI_HTYPE_ENV);
265 free(pConn);
266 pConn = NULL;
267 }
268 free(pwszStr);
269 }
270 else
271 {
465b3f2d 272 wcscpy(errorText, L"Cannot allocate environment handle");
5039dede
AK
273 free(pConn);
274 pConn = NULL;
275 }
276 }
465b3f2d
VK
277 else
278 {
279 wcscpy(errorText, L"Memory allocation error");
280 }
5039dede 281
b8c1ec69 282 return (DBDRV_CONNECTION)pConn;
5039dede
AK
283}
284
74d4ba34
VK
285/**
286 * Disconnect from database
287 */
5039dede
AK
288extern "C" void EXPORT DrvDisconnect(ORACLE_CONN *pConn)
289{
acc231ba
VK
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);
5039dede
AK
298}
299
85dfb586
VK
300/**
301 * Set prefetch limit
302 */
303extern "C" void EXPORT DrvSetPrefetchLimit(ORACLE_CONN *pConn, int limit)
304{
305 if (pConn != NULL)
306 pConn->prefetchLimit = limit;
307}
308
74d4ba34
VK
309/**
310 * Convert query from NetXMS portable format to native Oracle format
311 */
47a89886
VK
312static 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
987533db 329 UCS2CHAR *dstQuery = (UCS2CHAR *)malloc((ucs2_strlen(srcQuery) + count * 3 + 1) * sizeof(UCS2CHAR));
47a89886
VK
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 }
987533db 357 else if (pos < 100)
47a89886
VK
358 {
359 *dst++ = pos / 10 + '0';
360 *dst++ = pos % 10 + '0';
361 }
987533db
VK
362 else
363 {
364 *dst++ = pos / 100 + '0';
365 *dst++ = (pos % 100) / 10 + '0';
366 *dst++ = pos % 10 + '0';
367 }
47a89886
VK
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
96e87c92
VK
383/**
384 * Prepare statement
385 */
de1d708f 386extern "C" ORACLE_STATEMENT EXPORT *DrvPrepare(ORACLE_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText)
ec19ded8
VK
387{
388 ORACLE_STATEMENT *stmt = NULL;
389 OCIStmt *handleStmt;
390
47a89886 391 UCS2CHAR *ucs2Query = ConvertQuery(pwszQuery);
ec19ded8 392
c17f6cbc 393 MutexLock(pConn->mutexQueryLock);
ec19ded8
VK
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;
47a89886 400 stmt->bindings = new Array(8, 8, false);
66c11d49 401 stmt->batchBindings = NULL;
47a89886 402 stmt->buffers = new Array(8, 8, true);
d0b5d358 403 stmt->batchMode = false;
66c11d49 404 stmt->batchSize = 0;
47a89886 405 OCIHandleAlloc(pConn->handleEnv, (void **)&stmt->handleError, OCI_HTYPE_ERROR, 0, NULL);
de1d708f 406 *pdwError = DBERR_SUCCESS;
ec19ded8
VK
407 }
408 else
409 {
526ae8b8 410 SetLastError(pConn);
de1d708f 411 *pdwError = IsConnectionError(pConn);
ec19ded8
VK
412 }
413
414 if (errorText != NULL)
415 {
526ae8b8 416 wcsncpy(errorText, pConn->lastErrorText, DBDRV_MAX_ERROR_TEXT);
ec19ded8
VK
417 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
418 }
419 MutexUnlock(pConn->mutexQueryLock);
420
ec19ded8 421 free(ucs2Query);
ec19ded8
VK
422 return stmt;
423}
424
91eefa56 425/**
66c11d49 426 * Open batch
91eefa56 427 */
66c11d49
VK
428extern "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 */
443extern "C" void EXPORT DrvNextBatchRow(ORACLE_STATEMENT *stmt)
ec19ded8 444{
66c11d49
VK
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 */
460static DWORD s_bufferSize[] = { 0, sizeof(LONG), sizeof(DWORD), sizeof(INT64), sizeof(QWORD), sizeof(double) };
461
462/**
463 * Corresponding Oracle types for C types
464 */
465static ub2 s_oracleType[] = { SQLT_STR, SQLT_INT, SQLT_UIN, SQLT_INT, SQLT_UIN, SQLT_FLT };
47a89886 466
66c11d49
VK
467/**
468 * Bind parameter to statement - normal mode (non-batch)
469 */
470static void BindNormal(ORACLE_STATEMENT *stmt, int pos, int sqlType, int cType, void *buffer, int allocType)
471{
47a89886
VK
472 OCIBind *handleBind = (OCIBind *)stmt->bindings->get(pos - 1);
473 void *sqlBuffer;
511bea22 474 switch(cType)
47a89886 475 {
511bea22 476 case DB_CTYPE_STRING:
47a89886 477#if UNICODE_UCS4
511bea22 478 sqlBuffer = UCS2StringFromUCS4String((WCHAR *)buffer);
66c11d49 479 stmt->buffers->set(pos - 1, sqlBuffer);
636e0c72 480 if (allocType == DB_BIND_DYNAMIC)
511bea22
VK
481 free(buffer);
482#else
66c11d49 483 if (allocType == DB_BIND_TRANSIENT)
511bea22
VK
484 {
485 sqlBuffer = wcsdup((WCHAR *)buffer);
66c11d49 486 stmt->buffers->set(pos - 1, sqlBuffer);
511bea22
VK
487 }
488 else
489 {
47a89886 490 sqlBuffer = buffer;
511bea22
VK
491 if (allocType == DB_BIND_DYNAMIC)
492 stmt->buffers->set(pos - 1, sqlBuffer);
493 }
494#endif
66c11d49
VK
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);
511bea22 499 break;
b8849590
VK
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;
511bea22 514 case DB_CTYPE_INT64: // OCI prior to 11.2 cannot bind 64 bit integers
96dd17a0 515 sqlBuffer = malloc(sizeof(OCINumber));
6492e559 516 stmt->buffers->set(pos - 1, sqlBuffer);
96dd17a0
VK
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);
511bea22
VK
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
96dd17a0 524 sqlBuffer = malloc(sizeof(OCINumber));
6492e559 525 stmt->buffers->set(pos - 1, sqlBuffer);
96dd17a0
VK
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);
511bea22
VK
531 break;
532 default:
66c11d49
VK
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 */
560OracleBatchBind::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 */
586OracleBatchBind::~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 */
600void 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 */
635void 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 */
657void *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 */
678static 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
511bea22 709 {
66c11d49 710 sqlBuffer = wcsdup((WCHAR *)buffer);
511bea22 711 }
66c11d49
VK
712#endif
713 bind->set(sqlBuffer);
714 break;
b8849590
VK
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;
66c11d49
VK
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);
511bea22 759 break;
47a89886 760 }
66c11d49
VK
761}
762
763/**
764 * Bind parameter to statement
765 */
766extern "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);
ec19ded8
VK
772}
773
a9671f4b
VK
774/**
775 * Execute prepared non-select statement
776 */
ec19ded8
VK
777extern "C" DWORD EXPORT DrvExecute(ORACLE_CONN *pConn, ORACLE_STATEMENT *stmt, WCHAR *errorText)
778{
779 DWORD dwResult;
780
66c11d49
VK
781 if (stmt->batchMode)
782 {
24bc6f13
VK
783 if (stmt->batchSize == 0)
784 {
785 stmt->batchMode = false;
786 stmt->batchBindings->clear();
787 return DBERR_SUCCESS; // empty batch
788 }
789
66c11d49
VK
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
c17f6cbc 802 MutexLock(pConn->mutexQueryLock);
66c11d49
VK
803 if (OCIStmtExecute(pConn->handleService, stmt->handleStmt, pConn->handleError,
804 stmt->batchMode ? stmt->batchSize : 1, 0, NULL, NULL,
ec19ded8
VK
805 (pConn->nTransLevel == 0) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT) == OCI_SUCCESS)
806 {
807 dwResult = DBERR_SUCCESS;
808 }
809 else
810 {
526ae8b8 811 SetLastError(pConn);
ec19ded8
VK
812 dwResult = IsConnectionError(pConn);
813 }
814
815 if (errorText != NULL)
816 {
526ae8b8 817 wcsncpy(errorText, pConn->lastErrorText, DBDRV_MAX_ERROR_TEXT);
ec19ded8
VK
818 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
819 }
820 MutexUnlock(pConn->mutexQueryLock);
66c11d49
VK
821
822 if (stmt->batchMode)
823 {
824 stmt->batchMode = false;
825 stmt->batchBindings->clear();
826 }
827
ec19ded8
VK
828 return dwResult;
829}
830
d1c1c522
VK
831/**
832 * Destroy prepared statement
833 */
ec19ded8
VK
834extern "C" void EXPORT DrvFreeStatement(ORACLE_STATEMENT *stmt)
835{
836 if (stmt == NULL)
837 return;
838
c17f6cbc 839 MutexLock(stmt->connection->mutexQueryLock);
47a89886
VK
840 OCIStmtRelease(stmt->handleStmt, stmt->handleError, NULL, 0, OCI_DEFAULT);
841 OCIHandleFree(stmt->handleError, OCI_HTYPE_ERROR);
ec19ded8 842 MutexUnlock(stmt->connection->mutexQueryLock);
47a89886 843 delete stmt->bindings;
66c11d49 844 delete stmt->batchBindings;
47a89886 845 delete stmt->buffers;
ec19ded8
VK
846 free(stmt);
847}
848
69bb7f47
VK
849/**
850 * Perform non-SELECT query
851 */
9b4a2a0e 852static DWORD DrvQueryInternal(ORACLE_CONN *pConn, const WCHAR *pwszQuery, WCHAR *errorText)
5039dede
AK
853{
854 OCIStmt *handleStmt;
855 DWORD dwResult;
856
39945910
VK
857#if UNICODE_UCS4
858 UCS2CHAR *ucs2Query = UCS2StringFromUCS4String(pwszQuery);
859#else
51d31f95 860 const UCS2CHAR *ucs2Query = pwszQuery;
39945910
VK
861#endif
862
c17f6cbc 863 MutexLock(pConn->mutexQueryLock);
6f7c88fb
VK
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)
5039dede
AK
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 {
526ae8b8 874 SetLastError(pConn);
5039dede
AK
875 dwResult = IsConnectionError(pConn);
876 }
6f7c88fb 877 OCIStmtRelease(handleStmt, pConn->handleError, NULL, 0, OCI_DEFAULT);
5039dede
AK
878 }
879 else
880 {
526ae8b8 881 SetLastError(pConn);
5039dede
AK
882 dwResult = IsConnectionError(pConn);
883 }
5039dede 884 if (errorText != NULL)
a3166a07 885 {
526ae8b8 886 wcsncpy(errorText, pConn->lastErrorText, DBDRV_MAX_ERROR_TEXT);
a3166a07
VK
887 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
888 }
5039dede 889 MutexUnlock(pConn->mutexQueryLock);
39945910
VK
890
891#if UNICODE_UCS4
892 free(ucs2Query);
893#endif
5039dede 894 return dwResult;
9b4a2a0e
VK
895}
896
897/**
898 * Perform non-SELECT query - entry point
899 */
900extern "C" DWORD EXPORT DrvQuery(ORACLE_CONN *conn, const WCHAR *query, WCHAR *errorText)
901{
902 return DrvQueryInternal(conn, query, errorText);
5039dede
AK
903}
904
d1c1c522
VK
905/**
906 * Process SELECT results
907 */
ec19ded8 908static ORACLE_RESULT *ProcessQueryResults(ORACLE_CONN *pConn, OCIStmt *handleStmt, DWORD *pdwError)
5039dede 909{
5039dede
AK
910 OCIParam *handleParam;
911 OCIDefine *handleDefine;
912 ub4 nCount;
913 ub2 nWidth;
914 sword nStatus;
915 ORACLE_FETCH_BUFFER *pBuffers;
2a8e6a7b 916 text *colName;
5039dede 917
ec19ded8
VK
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)
5039dede 925 {
ec19ded8 926 // Prepare receive buffers and fetch column names
acec59f5
VK
927 pResult->columnNames = (char **)calloc(pResult->nCols, sizeof(char *));
928 pBuffers = (ORACLE_FETCH_BUFFER *)calloc(pResult->nCols, sizeof(ORACLE_FETCH_BUFFER));
ec19ded8 929 for(int i = 0; i < pResult->nCols; i++)
5039dede 930 {
ec19ded8
VK
931 if ((nStatus = OCIParamGet(handleStmt, OCI_HTYPE_STMT, pConn->handleError,
932 (void **)&handleParam, (ub4)(i + 1))) == OCI_SUCCESS)
5039dede 933 {
ec19ded8
VK
934 // Column name
935 if (OCIAttrGet(handleParam, OCI_DTYPE_PARAM, &colName, &nCount, OCI_ATTR_NAME, pConn->handleError) == OCI_SUCCESS)
5039dede 936 {
ec19ded8
VK
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("");
5039dede
AK
943 }
944
ec19ded8 945 // Data size
0fb0952d
VK
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 {
526ae8b8 970 SetLastError(pConn);
0fb0952d
VK
971 *pdwError = IsConnectionError(pConn);
972 }
ec19ded8
VK
973 OCIDescriptorFree(handleParam, OCI_DTYPE_PARAM);
974 }
975 else
976 {
526ae8b8 977 SetLastError(pConn);
ec19ded8
VK
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 {
526ae8b8 996 SetLastError(pConn);
ec19ded8
VK
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 }
0fb0952d
VK
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 }
ec19ded8 1028 else
5039dede 1029 {
ec19ded8
VK
1030 int length = pBuffers[i].nLength / sizeof(UCS2CHAR);
1031 pResult->pData[nPos] = (WCHAR *)malloc((length + 1) * sizeof(WCHAR));
39945910 1032#if UNICODE_UCS4
ec19ded8 1033 ucs2_to_ucs4(pBuffers[i].pData, length, pResult->pData[nPos], length + 1);
39945910 1034#else
ec19ded8 1035 memcpy(pResult->pData[nPos], pBuffers[i].pData, pBuffers[i].nLength);
39945910 1036#endif
ec19ded8 1037 pResult->pData[nPos][length] = 0;
5039dede 1038 }
ec19ded8 1039 nPos++;
5039dede 1040 }
ec19ded8
VK
1041 }
1042 }
5039dede 1043
ec19ded8
VK
1044 // Cleanup
1045 for(int i = 0; i < pResult->nCols; i++)
0fb0952d 1046 {
7f8be779 1047 free(pBuffers[i].pData);
0fb0952d
VK
1048 if (pBuffers[i].lobLocator != NULL)
1049 {
1050 OCIDescriptorFree(pBuffers[i].lobLocator, OCI_DTYPE_LOB);
1051 }
1052 }
ec19ded8 1053 free(pBuffers);
5039dede 1054
ec19ded8
VK
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
d1c1c522
VK
1066/**
1067 * Perform SELECT query
1068 */
ec19ded8
VK
1069extern "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
c17f6cbc 1080 MutexLock(pConn->mutexQueryLock);
ec19ded8
VK
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 {
ede6a257 1084 OCIAttrSet(handleStmt, OCI_HTYPE_STMT, &pConn->prefetchLimit, 0, OCI_ATTR_PREFETCH_ROWS, pConn->handleError);
ec19ded8
VK
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);
5039dede
AK
1089 }
1090 else
1091 {
526ae8b8 1092 SetLastError(pConn);
5039dede
AK
1093 *pdwError = IsConnectionError(pConn);
1094 }
6f7c88fb 1095 OCIStmtRelease(handleStmt, pConn->handleError, NULL, 0, OCI_DEFAULT);
5039dede
AK
1096 }
1097 else
1098 {
526ae8b8 1099 SetLastError(pConn);
5039dede
AK
1100 *pdwError = IsConnectionError(pConn);
1101 }
5039dede 1102 if (errorText != NULL)
a3166a07 1103 {
526ae8b8 1104 wcsncpy(errorText, pConn->lastErrorText, DBDRV_MAX_ERROR_TEXT);
a3166a07
VK
1105 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
1106 }
5039dede 1107 MutexUnlock(pConn->mutexQueryLock);
39945910
VK
1108
1109#if UNICODE_UCS4
1110 free(ucs2Query);
1111#endif
5039dede
AK
1112 return pResult;
1113}
1114
d1c1c522
VK
1115/**
1116 * Perform SELECT query using prepared statement
1117 */
ec19ded8
VK
1118extern "C" DBDRV_RESULT EXPORT DrvSelectPrepared(ORACLE_CONN *pConn, ORACLE_STATEMENT *stmt, DWORD *pdwError, WCHAR *errorText)
1119{
1120 ORACLE_RESULT *pResult = NULL;
1121
c17f6cbc 1122 MutexLock(pConn->mutexQueryLock);
bf600f06 1123 OCIAttrSet(stmt->handleStmt, OCI_HTYPE_STMT, &pConn->prefetchLimit, 0, OCI_ATTR_PREFETCH_ROWS, pConn->handleError);
ec19ded8
VK
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 {
526ae8b8 1131 SetLastError(pConn);
ec19ded8
VK
1132 *pdwError = IsConnectionError(pConn);
1133 }
1134
1135 if (errorText != NULL)
1136 {
526ae8b8 1137 wcsncpy(errorText, pConn->lastErrorText, DBDRV_MAX_ERROR_TEXT);
ec19ded8
VK
1138 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
1139 }
1140 MutexUnlock(pConn->mutexQueryLock);
1141
1142 return pResult;
1143}
1144
d1c1c522
VK
1145/**
1146 * Get field length from result
1147 */
5039dede
AK
1148extern "C" LONG EXPORT DrvGetFieldLength(ORACLE_RESULT *pResult, int nRow, int nColumn)
1149{
1150 if (pResult == NULL)
07c9f3b0 1151 return -1;
5039dede
AK
1152
1153 if ((nRow >= 0) && (nRow < pResult->nRows) &&
1154 (nColumn >= 0) && (nColumn < pResult->nCols))
caac6e38 1155 return (LONG)wcslen(pResult->pData[pResult->nCols * nRow + nColumn]);
5039dede 1156
07c9f3b0 1157 return -1;
5039dede
AK
1158}
1159
d1c1c522
VK
1160/**
1161 * Get field value from result
1162 */
5039dede
AK
1163extern "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 {
5502f4aa
VK
1173#ifdef _WIN32
1174 wcsncpy_s(pBuffer, nBufLen, pResult->pData[nRow * pResult->nCols + nColumn], _TRUNCATE);
1175#else
5039dede
AK
1176 wcsncpy(pBuffer, pResult->pData[nRow * pResult->nCols + nColumn], nBufLen);
1177 pBuffer[nBufLen - 1] = 0;
5502f4aa 1178#endif
5039dede
AK
1179 pValue = pBuffer;
1180 }
1181 }
1182 return pValue;
1183}
1184
0fb0952d
VK
1185/**
1186 * Get number of rows in result
1187 */
5039dede
AK
1188extern "C" int EXPORT DrvGetNumRows(ORACLE_RESULT *pResult)
1189{
1190 return (pResult != NULL) ? pResult->nRows : 0;
1191}
1192
0fb0952d
VK
1193/**
1194 * Get column count in query result
1195 */
2a8e6a7b
VK
1196extern "C" int EXPORT DrvGetColumnCount(ORACLE_RESULT *pResult)
1197{
1198 return (pResult != NULL) ? pResult->nCols : 0;
1199}
1200
0fb0952d
VK
1201/**
1202 * Get column name in query result
1203 */
2a8e6a7b
VK
1204extern "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
d1c1c522
VK
1209/**
1210 * Free SELECT results
1211 */
5039dede
AK
1212extern "C" void EXPORT DrvFreeResult(ORACLE_RESULT *pResult)
1213{
1214 if (pResult != NULL)
1215 DestroyQueryResult(pResult);
1216}
1217
792038b9
VK
1218/**
1219 * Destroy unbuffered query result
1220 */
1221static 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
d1c1c522 1245/**
f17cf019 1246 * Process results of unbuffered query execution (prepare for fetching results)
d1c1c522 1247 */
f17cf019 1248static ORACLE_UNBUFFERED_RESULT *ProcessUnbufferedQueryResults(ORACLE_CONN *pConn, OCIStmt *handleStmt, DWORD *pdwError)
5039dede 1249{
f17cf019
VK
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
792038b9
VK
1260 result->columnNames = (char **)calloc(result->nCols, sizeof(char *));
1261 result->pBuffers = (ORACLE_FETCH_BUFFER *)calloc(result->nCols, sizeof(ORACLE_FETCH_BUFFER));
f17cf019
VK
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 }
792038b9 1308 OCIDescriptorFree(handleParam, OCI_DTYPE_PARAM);
f17cf019
VK
1309 if (nStatus == OCI_SUCCESS)
1310 {
1311 *pdwError = DBERR_SUCCESS;
1312 }
1313 else
1314 {
1315 SetLastError(pConn);
1316 *pdwError = IsConnectionError(pConn);
792038b9
VK
1317 DestroyUnbufferedQueryResult(result, false);
1318 result = NULL;
1319 break;
f17cf019 1320 }
f17cf019
VK
1321 }
1322 else
1323 {
1324 SetLastError(pConn);
1325 *pdwError = IsConnectionError(pConn);
792038b9 1326 DestroyUnbufferedQueryResult(result, false);
f17cf019
VK
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 */
1344extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectUnbuffered(ORACLE_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText)
1345{
1346 ORACLE_UNBUFFERED_RESULT *result = NULL;
5039dede 1347
1d3c6b50
VK
1348#if UNICODE_UCS4
1349 UCS2CHAR *ucs2Query = UCS2StringFromUCS4String(pwszQuery);
1350#else
1351 UCS2CHAR *ucs2Query = pwszQuery;
1352#endif
1353
c17f6cbc 1354 MutexLock(pConn->mutexQueryLock);
5039dede 1355
f17cf019
VK
1356 OCIStmt *handleStmt;
1357 if (OCIStmtPrepare2(pConn->handleService, &handleStmt, pConn->handleError, (text *)ucs2Query,
6f7c88fb 1358 (ub4)ucs2_strlen(ucs2Query) * sizeof(UCS2CHAR), NULL, 0, OCI_NTV_SYNTAX, OCI_DEFAULT) == OCI_SUCCESS)
5039dede 1359 {
f17cf019
VK
1360 OCIAttrSet(handleStmt, OCI_HTYPE_STMT, &pConn->prefetchLimit, 0, OCI_ATTR_PREFETCH_ROWS, pConn->handleError);
1361 if (OCIStmtExecute(pConn->handleService, handleStmt, pConn->handleError,
1f9abbf3 1362 0, 0, NULL, NULL, (pConn->nTransLevel == 0) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT) == OCI_SUCCESS)
5039dede 1363 {
f17cf019 1364 result = ProcessUnbufferedQueryResults(pConn, handleStmt, pdwError);
5039dede
AK
1365 }
1366 else
1367 {
526ae8b8 1368 SetLastError(pConn);
5039dede
AK
1369 *pdwError = IsConnectionError(pConn);
1370 }
1371 }
1372 else
1373 {
526ae8b8 1374 SetLastError(pConn);
5039dede
AK
1375 *pdwError = IsConnectionError(pConn);
1376 }
1377
1d3c6b50
VK
1378#if UNICODE_UCS4
1379 free(ucs2Query);
1380#endif
1381
f17cf019
VK
1382 if ((*pdwError == DBERR_SUCCESS) && (result != NULL))
1383 return result;
5039dede
AK
1384
1385 // On failure, unlock query mutex and do cleanup
f17cf019 1386 OCIStmtRelease(handleStmt, pConn->handleError, NULL, 0, OCI_DEFAULT);
5039dede 1387 if (errorText != NULL)
a3166a07 1388 {
526ae8b8 1389 wcsncpy(errorText, pConn->lastErrorText, DBDRV_MAX_ERROR_TEXT);
a3166a07
VK
1390 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
1391 }
5039dede
AK
1392 MutexUnlock(pConn->mutexQueryLock);
1393 return NULL;
1394}
1395
0fb0952d 1396/**
f17cf019 1397 * Perform SELECT query using prepared statement
0fb0952d 1398 */
f17cf019
VK
1399extern "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 */
1433extern "C" bool EXPORT DrvFetch(ORACLE_UNBUFFERED_RESULT *result)
5039dede 1434{
750d59f2 1435 bool success;
f694a7bc 1436
f17cf019 1437 if (result == NULL)
750d59f2 1438 return false;
f694a7bc 1439
f17cf019 1440 sword rc = OCIStmtFetch2(result->handleStmt, result->connection->handleError, 1, OCI_FETCH_NEXT, 0, OCI_DEFAULT);
f694a7bc
VK
1441 if ((rc == OCI_SUCCESS) || (rc == OCI_SUCCESS_WITH_INFO))
1442 {
750d59f2 1443 success = true;
f694a7bc
VK
1444 }
1445 else
1446 {
f17cf019 1447 SetLastError(result->connection);
750d59f2 1448 success = false;
f694a7bc
VK
1449 }
1450 return success;
5039dede
AK
1451}
1452
0fb0952d 1453/**
f17cf019 1454 * Get field length from current row in unbuffered query result
0fb0952d 1455 */
f17cf019 1456extern "C" LONG EXPORT DrvGetFieldLengthUnbuffered(ORACLE_UNBUFFERED_RESULT *result, int nColumn)
61f032f5 1457{
f17cf019 1458 if (result == NULL)
61f032f5
VK
1459 return 0;
1460
f17cf019 1461 if ((nColumn < 0) || (nColumn >= result->nCols))
61f032f5
VK
1462 return 0;
1463
f17cf019 1464 if (result->pBuffers[nColumn].isNull)
61f032f5
VK
1465 return 0;
1466
f17cf019 1467 if (result->pBuffers[nColumn].lobLocator != NULL)
0fb0952d
VK
1468 {
1469 ub4 length = 0;
f17cf019 1470 OCILobGetLength(result->connection->handleService, result->connection->handleError, result->pBuffers[nColumn].lobLocator, &length);
0fb0952d
VK
1471 return (LONG)length;
1472 }
1473
f17cf019 1474 return (LONG)(result->pBuffers[nColumn].nLength / sizeof(UCS2CHAR));
61f032f5
VK
1475}
1476
0fb0952d 1477/**
f17cf019 1478 * Get field from current row in unbuffered query result
0fb0952d 1479 */
f17cf019 1480extern "C" WCHAR EXPORT *DrvGetFieldUnbuffered(ORACLE_UNBUFFERED_RESULT *result, int nColumn, WCHAR *pBuffer, int nBufSize)
5039dede
AK
1481{
1482 int nLen;
1483
f17cf019 1484 if (result == NULL)
5039dede
AK
1485 return NULL;
1486
f17cf019 1487 if ((nColumn < 0) || (nColumn >= result->nCols))
5039dede
AK
1488 return NULL;
1489
f17cf019 1490 if (result->pBuffers[nColumn].isNull)
5039dede
AK
1491 {
1492 *pBuffer = 0;
1493 }
f17cf019 1494 else if (result->pBuffers[nColumn].lobLocator != NULL)
0fb0952d
VK
1495 {
1496 ub4 length = 0;
f17cf019 1497 OCILobGetLength(result->connection->handleService, result->connection->handleError, result->pBuffers[nColumn].lobLocator, &length);
0fb0952d 1498
383c8f2a 1499 nLen = min(nBufSize - 1, (int)length);
0fb0952d
VK
1500 ub4 amount = nLen;
1501#if UNICODE_UCS4
1502 UCS2CHAR *ucs2buffer = (UCS2CHAR *)malloc(nLen * sizeof(UCS2CHAR));
f17cf019 1503 OCILobRead(result->connection->handleService, result->connection->handleError, result->pBuffers[nColumn].lobLocator, &amount, 1,
0fb0952d
VK
1504 ucs2buffer, nLen * sizeof(UCS2CHAR), NULL, NULL, OCI_UCS2ID, SQLCS_IMPLICIT);
1505 ucs2_to_ucs4(ucs2buffer, nLen, pBuffer, nLen);
1506 free(ucs2buffer);
1507#else
f17cf019 1508 OCILobRead(result->connection->handleService, result->connection->handleError, result->pBuffers[nColumn].lobLocator, &amount, 1,
0fb0952d
VK
1509 pBuffer, nBufSize * sizeof(WCHAR), NULL, NULL, OCI_UCS2ID, SQLCS_IMPLICIT);
1510#endif
1511 pBuffer[nLen] = 0;
1512 }
5039dede
AK
1513 else
1514 {
f17cf019 1515 nLen = min(nBufSize - 1, ((int)(result->pBuffers[nColumn].nLength / sizeof(UCS2CHAR))));
39945910 1516#if UNICODE_UCS4
f17cf019 1517 ucs2_to_ucs4(result->pBuffers[nColumn].pData, nLen, pBuffer, nLen + 1);
39945910 1518#else
f17cf019 1519 memcpy(pBuffer, result->pBuffers[nColumn].pData, nLen * sizeof(WCHAR));
39945910 1520#endif
5039dede
AK
1521 pBuffer[nLen] = 0;
1522 }
1523
1524 return pBuffer;
1525}
1526
0fb0952d 1527/**
f17cf019 1528 * Get column count in unbuffered query result
0fb0952d 1529 */
f17cf019 1530extern "C" int EXPORT DrvGetColumnCountUnbuffered(ORACLE_UNBUFFERED_RESULT *result)
2a8e6a7b 1531{
f17cf019 1532 return (result != NULL) ? result->nCols : 0;
2a8e6a7b
VK
1533}
1534
0fb0952d 1535/**
f17cf019 1536 * Get column name in unbuffered query result
0fb0952d 1537 */
f17cf019 1538extern "C" const char EXPORT *DrvGetColumnNameUnbuffered(ORACLE_UNBUFFERED_RESULT *result, int column)
2a8e6a7b 1539{
f17cf019 1540 return ((result != NULL) && (column >= 0) && (column < result->nCols)) ? result->columnNames[column] : NULL;
2a8e6a7b
VK
1541}
1542
0fb0952d 1543/**
f17cf019 1544 * Destroy result of unbuffered query
0fb0952d 1545 */
f17cf019 1546extern "C" void EXPORT DrvFreeUnbufferedResult(ORACLE_UNBUFFERED_RESULT *result)
5039dede 1547{
f17cf019 1548 if (result == NULL)
5039dede
AK
1549 return;
1550
792038b9
VK
1551 MUTEX mutex = result->connection->mutexQueryLock;
1552 DestroyUnbufferedQueryResult(result, true);
1553 MutexUnlock(mutex);
5039dede
AK
1554}
1555
0fb0952d
VK
1556/**
1557 * Begin transaction
1558 */
5039dede
AK
1559extern "C" DWORD EXPORT DrvBegin(ORACLE_CONN *pConn)
1560{
5039dede 1561 if (pConn == NULL)
1d3c6b50 1562 return DBERR_INVALID_HANDLE;
5039dede 1563
c17f6cbc 1564 MutexLock(pConn->mutexQueryLock);
1f9abbf3 1565 pConn->nTransLevel++;
5039dede 1566 MutexUnlock(pConn->mutexQueryLock);
1f9abbf3 1567 return DBERR_SUCCESS;
5039dede
AK
1568}
1569
0fb0952d
VK
1570/**
1571 * Commit transaction
1572 */
5039dede
AK
1573extern "C" DWORD EXPORT DrvCommit(ORACLE_CONN *pConn)
1574{
1d3c6b50 1575 DWORD dwResult;
5039dede
AK
1576
1577 if (pConn == NULL)
1d3c6b50 1578 return DBERR_INVALID_HANDLE;
5039dede 1579
c17f6cbc 1580 MutexLock(pConn->mutexQueryLock);
5039dede
AK
1581 if (pConn->nTransLevel > 0)
1582 {
1583 if (OCITransCommit(pConn->handleService, pConn->handleError, OCI_DEFAULT) == OCI_SUCCESS)
1584 {
1585 dwResult = DBERR_SUCCESS;
1f9abbf3 1586 pConn->nTransLevel = 0;
5039dede
AK
1587 }
1588 else
1589 {
526ae8b8 1590 SetLastError(pConn);
5039dede
AK
1591 dwResult = IsConnectionError(pConn);
1592 }
1593 }
1594 else
1595 {
1596 dwResult = DBERR_SUCCESS;
1597 }
1598 MutexUnlock(pConn->mutexQueryLock);
1d3c6b50 1599 return dwResult;
5039dede
AK
1600}
1601
0fb0952d
VK
1602/**
1603 * Rollback transaction
1604 */
5039dede
AK
1605extern "C" DWORD EXPORT DrvRollback(ORACLE_CONN *pConn)
1606{
1d3c6b50 1607 DWORD dwResult;
5039dede
AK
1608
1609 if (pConn == NULL)
1d3c6b50 1610 return DBERR_INVALID_HANDLE;
5039dede 1611
c17f6cbc 1612 MutexLock(pConn->mutexQueryLock);
5039dede
AK
1613 if (pConn->nTransLevel > 0)
1614 {
1615 if (OCITransRollback(pConn->handleService, pConn->handleError, OCI_DEFAULT) == OCI_SUCCESS)
1616 {
1617 dwResult = DBERR_SUCCESS;
1f9abbf3 1618 pConn->nTransLevel = 0;
5039dede
AK
1619 }
1620 else
1621 {
526ae8b8 1622 SetLastError(pConn);
5039dede
AK
1623 dwResult = IsConnectionError(pConn);
1624 }
1625 }
1626 else
1627 {
1628 dwResult = DBERR_SUCCESS;
1629 }
1630 MutexUnlock(pConn->mutexQueryLock);
1d3c6b50 1631 return dwResult;
5039dede
AK
1632}
1633
daf3c104
VK
1634/**
1635 * Check if table exist
1636 */
1637extern "C" int EXPORT DrvIsTableExist(ORACLE_CONN *pConn, const WCHAR *name)
1638{
1639 WCHAR query[256];
fdf1ed50 1640 swprintf(query, 256, L"SELECT count(*) FROM user_tables WHERE table_name=upper('%ls')", name);
daf3c104
VK
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
5039dede
AK
1655#ifdef _WIN32
1656
5d255340
VK
1657/**
1658 * DLL Entry point
1659 */
750d59f2 1660bool WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
5039dede 1661{
1d3c6b50
VK
1662 if (dwReason == DLL_PROCESS_ATTACH)
1663 DisableThreadLibraryCalls(hInstance);
750d59f2 1664 return true;
5039dede
AK
1665}
1666
1667#endif