Implemented DB driver call PrepareString; alarms table converted to new format
[public/netxms.git] / src / server / dbdrv / oracle / oracle.cpp
CommitLineData
5039dede
AK
1/* $Id$ */
2/*
3** Oracle Database Driver
2a8e6a7b 4** Copyright (C) 2007, 2008, 2009 Victor Kirhenshtein
5039dede
AK
5**
6** This program is free software; you can redistribute it and/or modify
7** it under the terms of the GNU General Public License as published by
8** the Free Software Foundation; either version 2 of the License, or
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
16** You should have received a copy of the GNU General Public License
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19**
20** File: oracle.cpp
21**
22**/
23
24#include "oracledrv.h"
25
26
27//
28// API version
29//
30
31extern "C" int EXPORT drvAPIVersion;
32int EXPORT drvAPIVersion = DBDRV_API_VERSION;
33
34
35//
643c9dcb
VK
36// Prepare string for using in SQL query - enclose in quotes and escape as needed
37//
38
39extern "C" TCHAR EXPORT *DrvPrepareString(const TCHAR *str)
40{
41 int len = (int)_tcslen(str) + 3; // + two quotes and \0 at the end
42 int bufferSize = len + 128;
43 TCHAR *out = (TCHAR *)malloc(bufferSize * sizeof(TCHAR));
44 out[0] = _T('\'');
45
46 const TCHAR *src = str;
47 int outPos;
48 for(outPos = 1; *src != NULL; src++)
49 {
50 if (*src == _T('\''))
51 {
52 len++;
53 if (len >= bufferSize)
54 {
55 bufferSize += 128;
56 out = (TCHAR *)realloc(out, bufferSize * sizeof(TCHAR));
57 }
58 out[outPos++] = _T('\'');
59 out[outPos++] = _T('\'');
60 }
61 else
62 {
63 out[outPos++] = *src;
64 }
65 }
66 out[outPos++] = _T('\'');
67 out[outPos++] = 0;
68
69 return out;
70}
71
72
73//
5039dede
AK
74// Initialize driver
75//
76
77extern "C" BOOL EXPORT DrvInit(char *szCmdLine)
78{
79 return TRUE;
80}
81
82//
83// Unload handler
84//
85
86extern "C" void EXPORT DrvUnload(void)
87{
88 OCITerminate(OCI_DEFAULT);
89}
90
91
92//
93// Set last error text
94//
95
96static void SetLastErrorText(ORACLE_CONN *pConn)
97{
98 sb4 nCode;
99
100#ifdef UNICODE
101 OCIErrorGet(pConn->handleError, 1, NULL, &nCode, (text *)pConn->szLastError,
102 DBDRV_MAX_ERROR_TEXT, OCI_HTYPE_ERROR);
103 pConn->szLastError[DBDRV_MAX_ERROR_TEXT - 1] = 0;
104#else
39945910 105 UCS2CHAR buffer[DBDRV_MAX_ERROR_TEXT];
5039dede
AK
106
107 OCIErrorGet(pConn->handleError, 1, NULL, &nCode, (text *)buffer,
108 DBDRV_MAX_ERROR_TEXT, OCI_HTYPE_ERROR);
109 buffer[DBDRV_MAX_ERROR_TEXT - 1] = 0;
39945910
VK
110#if UNICODE_UCS4
111 ucs2_to_mb(buffer, ucs2_strlen(buffer) + 1, pConn->szLastError, DBDRV_MAX_ERROR_TEXT);
112 pConn->szLastError[DBDRV_MAX_ERROR_TEXT - 1] = 0;
113#else
5039dede
AK
114 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, buffer, -1,
115 pConn->szLastError, DBDRV_MAX_ERROR_TEXT, NULL, NULL);
116#endif
39945910 117#endif
3783d300 118 RemoveTrailingCRLF(pConn->szLastError);
5039dede
AK
119}
120
121
122//
123// Check if last error was caused by lost connection to server
124//
125
126static DWORD IsConnectionError(ORACLE_CONN *pConn)
127{
128 ub4 nStatus = 0;
129
130 OCIAttrGet(pConn->handleServer, OCI_HTYPE_SERVER, &nStatus,
131 NULL, OCI_ATTR_SERVER_STATUS, pConn->handleError);
132 return (nStatus == OCI_SERVER_NOT_CONNECTED) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR;
133}
134
135
136//
137// Destroy query result
138//
139
140static void DestroyQueryResult(ORACLE_RESULT *pResult)
141{
142 int i, nCount;
143
144 nCount = pResult->nCols * pResult->nRows;
145 for(i = 0; i < nCount; i++)
146 safe_free(pResult->pData[i]);
147 safe_free(pResult->pData);
2a8e6a7b
VK
148
149 for(i = 0; i < pResult->nCols; i++)
150 safe_free(pResult->columnNames[i]);
151 safe_free(pResult->columnNames);
152
5039dede
AK
153 free(pResult);
154}
155
156
157//
158// Connect to database
159//
160
161extern "C" DB_CONNECTION EXPORT DrvConnect(char *pszHost, char *pszLogin,
39945910 162 char *pszPassword, char *pszDatabase)
5039dede
AK
163{
164 ORACLE_CONN *pConn;
39945910 165 UCS2CHAR *pwszStr;
5039dede
AK
166
167 if ((pszDatabase == NULL) || (*pszDatabase == 0))
168 {
169 return NULL;
170 }
171
172 pConn = (ORACLE_CONN *)malloc(sizeof(ORACLE_CONN));
173 if (pConn != NULL)
174 {
2a8e6a7b
VK
175 memset(pConn, 0, sizeof(ORACLE_CONN));
176
5039dede
AK
177 if (OCIEnvNlsCreate(&pConn->handleEnv, OCI_THREADED | OCI_NCHAR_LITERAL_REPLACE_OFF,
178 NULL, NULL, NULL, NULL, 0, NULL, OCI_UTF16ID, OCI_UTF16ID) == OCI_SUCCESS)
179 {
180 OCIHandleAlloc(pConn->handleEnv, (void **)&pConn->handleError, OCI_HTYPE_ERROR, 0, NULL);
181 OCIHandleAlloc(pConn->handleEnv, (void **)&pConn->handleServer, OCI_HTYPE_SERVER, 0, NULL);
39945910 182 pwszStr = UCS2StringFromMBString(pszHost);
5039dede 183 if (OCIServerAttach(pConn->handleServer, pConn->handleError,
39945910 184 (text *)pwszStr, (sb4)ucs2_strlen(pwszStr) * sizeof(UCS2CHAR), OCI_DEFAULT) == OCI_SUCCESS)
5039dede
AK
185 {
186 free(pwszStr);
187
188 // Initialize service handle
189 OCIHandleAlloc(pConn->handleEnv, (void **)&pConn->handleService, OCI_HTYPE_SVCCTX, 0, NULL);
190 OCIAttrSet(pConn->handleService, OCI_HTYPE_SVCCTX, pConn->handleServer, 0, OCI_ATTR_SERVER, pConn->handleError);
191
192 // Initialize session handle
193 OCIHandleAlloc(pConn->handleEnv, (void **)&pConn->handleSession, OCI_HTYPE_SESSION, 0, NULL);
39945910 194 pwszStr = UCS2StringFromMBString(pszLogin);
5039dede 195 OCIAttrSet(pConn->handleSession, OCI_HTYPE_SESSION, pwszStr,
39945910 196 (ub4)ucs2_strlen(pwszStr) * sizeof(UCS2CHAR), OCI_ATTR_USERNAME, pConn->handleError);
5039dede 197 free(pwszStr);
39945910 198 pwszStr = UCS2StringFromMBString(pszPassword);
5039dede 199 OCIAttrSet(pConn->handleSession, OCI_HTYPE_SESSION, pwszStr,
39945910 200 (ub4)ucs2_strlen(pwszStr) * sizeof(UCS2CHAR), OCI_ATTR_PASSWORD, pConn->handleError);
5039dede
AK
201
202 // Authenticate
203 if (OCISessionBegin(pConn->handleService, pConn->handleError,
204 pConn->handleSession, OCI_CRED_RDBMS, OCI_DEFAULT) == OCI_SUCCESS)
205 {
206 OCIAttrSet(pConn->handleService, OCI_HTYPE_SVCCTX, pConn->handleSession, 0,
207 OCI_ATTR_SESSION, pConn->handleError);
208 pConn->mutexQueryLock = MutexCreate();
209 pConn->nTransLevel = 0;
210 pConn->szLastError[0] = 0;
211 }
212 else
213 {
39945910
VK
214 text error[1024];
215 sb4 nCode;
216 memset(error, 0, sizeof(error));
217 OCIErrorGet(pConn->handleError, 1, NULL, &nCode, (text *)error, DBDRV_MAX_ERROR_TEXT, OCI_HTYPE_ERROR);
218 printf("%s\n", MBStringFromUCS2String((UCS2CHAR *)error));
5039dede
AK
219 OCIHandleFree(pConn->handleEnv, OCI_HTYPE_ENV);
220 free(pConn);
221 pConn = NULL;
222 }
223 }
224 else
225 {
39945910
VK
226 text error[1024];
227 sb4 nCode;
228 memset(error, 0, sizeof(error));
229 OCIErrorGet(pConn->handleError, 1, NULL, &nCode, (text *)error, DBDRV_MAX_ERROR_TEXT, OCI_HTYPE_ERROR);
230 printf("%s\n", MBStringFromUCS2String((UCS2CHAR *)error));
5039dede
AK
231 OCIHandleFree(pConn->handleEnv, OCI_HTYPE_ENV);
232 free(pConn);
233 pConn = NULL;
234 }
235 free(pwszStr);
236 }
237 else
238 {
239 free(pConn);
240 pConn = NULL;
241 }
242 }
243
244 return (DB_CONNECTION)pConn;
245}
246
247
248//
249// Disconnect from database
250//
251
252extern "C" void EXPORT DrvDisconnect(ORACLE_CONN *pConn)
253{
254 if (pConn != NULL)
255 {
256 OCISessionEnd(pConn->handleService, pConn->handleError, NULL, OCI_DEFAULT);
257 OCIServerDetach(pConn->handleServer, pConn->handleError, OCI_DEFAULT);
258 OCIHandleFree(pConn->handleEnv, OCI_HTYPE_ENV);
259 MutexDestroy(pConn->mutexQueryLock);
260 free(pConn);
261 }
262}
263
264
265//
266// Perform non-SELECT query
267//
268
269extern "C" DWORD EXPORT DrvQuery(ORACLE_CONN *pConn, WCHAR *pwszQuery, TCHAR *errorText)
270{
271 OCIStmt *handleStmt;
272 DWORD dwResult;
273
39945910
VK
274#if UNICODE_UCS4
275 UCS2CHAR *ucs2Query = UCS2StringFromUCS4String(pwszQuery);
276#else
277 UCS2CHAR *ucs2Query = pwszQuery;
278#endif
279
5039dede
AK
280 MutexLock(pConn->mutexQueryLock, INFINITE);
281 OCIHandleAlloc(pConn->handleEnv, (void **)&handleStmt, OCI_HTYPE_STMT, 0, NULL);
39945910
VK
282 if (OCIStmtPrepare(handleStmt, pConn->handleError, (text *)ucs2Query,
283 (ub4)ucs2_strlen(ucs2Query) * sizeof(UCS2CHAR), OCI_NTV_SYNTAX, OCI_DEFAULT) == OCI_SUCCESS)
5039dede
AK
284 {
285 if (OCIStmtExecute(pConn->handleService, handleStmt, pConn->handleError, 1, 0, NULL, NULL,
286 (pConn->nTransLevel == 0) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT) == OCI_SUCCESS)
287 {
288 dwResult = DBERR_SUCCESS;
289 }
290 else
291 {
292 SetLastErrorText(pConn);
293 dwResult = IsConnectionError(pConn);
294 }
295 }
296 else
297 {
298 dwResult = IsConnectionError(pConn);
299 }
300 OCIHandleFree(handleStmt, OCI_HTYPE_STMT);
301 if (errorText != NULL)
5502f4aa 302 nx_strncpy(errorText, pConn->szLastError, DBDRV_MAX_ERROR_TEXT);
5039dede 303 MutexUnlock(pConn->mutexQueryLock);
39945910
VK
304
305#if UNICODE_UCS4
306 free(ucs2Query);
307#endif
5039dede
AK
308 return dwResult;
309}
310
311
312//
313// Perform SELECT query
314//
315
316extern "C" DB_RESULT EXPORT DrvSelect(ORACLE_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, TCHAR *errorText)
317{
318 ORACLE_RESULT *pResult = NULL;
319 OCIStmt *handleStmt;
320 OCIParam *handleParam;
321 OCIDefine *handleDefine;
322 ub4 nCount;
323 ub2 nWidth;
324 sword nStatus;
325 ORACLE_FETCH_BUFFER *pBuffers;
2a8e6a7b 326 text *colName;
5039dede
AK
327 int i, nPos;
328
39945910
VK
329#if UNICODE_UCS4
330 UCS2CHAR *ucs2Query = UCS2StringFromUCS4String(pwszQuery);
331#else
332 UCS2CHAR *ucs2Query = pwszQuery;
333#endif
334
5039dede
AK
335 MutexLock(pConn->mutexQueryLock, INFINITE);
336 OCIHandleAlloc(pConn->handleEnv, (void **)&handleStmt, OCI_HTYPE_STMT, 0, NULL);
39945910
VK
337 if (OCIStmtPrepare(handleStmt, pConn->handleError, (text *)ucs2Query,
338 (ub4)ucs2_strlen(ucs2Query) * sizeof(UCS2CHAR), OCI_NTV_SYNTAX, OCI_DEFAULT) == OCI_SUCCESS)
5039dede
AK
339 {
340 if (OCIStmtExecute(pConn->handleService, handleStmt, pConn->handleError,
341 0, 0, NULL, NULL, OCI_DEFAULT) == OCI_SUCCESS)
342 {
343 pResult = (ORACLE_RESULT *)malloc(sizeof(ORACLE_RESULT));
344 pResult->nRows = 0;
345 pResult->pData = NULL;
2a8e6a7b 346 pResult->columnNames = NULL;
5039dede
AK
347 OCIAttrGet(handleStmt, OCI_HTYPE_STMT, &nCount, NULL, OCI_ATTR_PARAM_COUNT, pConn->handleError);
348 pResult->nCols = nCount;
349 if (pResult->nCols > 0)
350 {
2a8e6a7b
VK
351 // Prepare receive buffers and fetch column names
352 pResult->columnNames = (char **)malloc(sizeof(char *) * pResult->nCols);
5039dede
AK
353 pBuffers = (ORACLE_FETCH_BUFFER *)malloc(sizeof(ORACLE_FETCH_BUFFER) * pResult->nCols);
354 memset(pBuffers, 0, sizeof(ORACLE_FETCH_BUFFER) * pResult->nCols);
355 for(i = 0; i < pResult->nCols; i++)
356 {
357 if ((nStatus = OCIParamGet(handleStmt, OCI_HTYPE_STMT, pConn->handleError,
358 (void **)&handleParam, (ub4)(i + 1))) == OCI_SUCCESS)
359 {
2a8e6a7b
VK
360 // Column name
361 if (OCIAttrGet(handleParam, OCI_DTYPE_PARAM, &colName, &nCount, OCI_ATTR_NAME, pConn->handleError) == OCI_SUCCESS)
362 {
363 // We are in UTF-16 mode, so OCIAttrGet will return UTF-16 strings
39945910 364 pResult->columnNames[i] = MBStringFromUCS2String((UCS2CHAR *)colName);
2a8e6a7b
VK
365 }
366 else
367 {
368 pResult->columnNames[i] = strdup("");
369 }
370
371 // Data size
5039dede 372 OCIAttrGet(handleParam, OCI_DTYPE_PARAM, &nWidth, NULL, OCI_ATTR_DATA_SIZE, pConn->handleError);
39945910 373 pBuffers[i].pData = (UCS2CHAR *)malloc((nWidth + 1) * sizeof(UCS2CHAR));
5039dede
AK
374 handleDefine = NULL;
375 if ((nStatus = OCIDefineByPos(handleStmt, &handleDefine, pConn->handleError, i + 1,
39945910 376 pBuffers[i].pData, (nWidth + 1) * sizeof(UCS2CHAR),
5039dede
AK
377 SQLT_CHR, &pBuffers[i].isNull, &pBuffers[i].nLength,
378 &pBuffers[i].nCode, OCI_DEFAULT)) != OCI_SUCCESS)
379 {
380 SetLastErrorText(pConn);
381 *pdwError = IsConnectionError(pConn);
382 }
2a8e6a7b 383 OCIDescriptorFree(handleParam, OCI_DTYPE_PARAM);
5039dede
AK
384 }
385 else
386 {
387 SetLastErrorText(pConn);
388 *pdwError = IsConnectionError(pConn);
389 }
390 }
391
392 // Fetch data
393 if (nStatus == OCI_SUCCESS)
394 {
395 nPos = 0;
396 while(1)
397 {
398 nStatus = OCIStmtFetch(handleStmt, pConn->handleError, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
399 if (nStatus == OCI_NO_DATA)
400 {
401 *pdwError = DBERR_SUCCESS; // EOF
402 break;
403 }
404 if ((nStatus != OCI_SUCCESS) && (nStatus != OCI_SUCCESS_WITH_INFO))
405 {
406 SetLastErrorText(pConn);
407 *pdwError = IsConnectionError(pConn);
408 break;
409 }
410
411 // New row
412 pResult->nRows++;
413 pResult->pData = (WCHAR **)realloc(pResult->pData, sizeof(WCHAR *) * pResult->nCols * pResult->nRows);
414 for(i = 0; i < pResult->nCols; i++)
415 {
416 if (pBuffers[i].isNull)
417 {
39945910 418 pResult->pData[nPos] = (WCHAR *)nx_memdup("\0\0\0", sizeof(WCHAR));
5039dede
AK
419 }
420 else
421 {
39945910
VK
422 int length = pBuffers[i].nLength / sizeof(UCS2CHAR);
423 pResult->pData[nPos] = (WCHAR *)malloc((length + 1) * sizeof(WCHAR));
424#if UNICODE_UCS4
425 ucs2_to_ucs4(pBuffers[i].pData, length, pResult->pData[nPos], length);
426#else
5039dede 427 memcpy(pResult->pData[nPos], pBuffers[i].pData, pBuffers[i].nLength);
39945910
VK
428#endif
429 pResult->pData[nPos][length] = 0;
5039dede
AK
430 }
431 nPos++;
432 }
433 }
434 }
435
436 // Cleanup
437 for(i = 0; i < pResult->nCols; i++)
438 safe_free(pBuffers[i].pData);
439 free(pBuffers);
440
441 // Destroy results in case of error
442 if (*pdwError != DBERR_SUCCESS)
443 {
444 DestroyQueryResult(pResult);
445 pResult = NULL;
446 }
447 }
448 }
449 else
450 {
451 SetLastErrorText(pConn);
452 *pdwError = IsConnectionError(pConn);
453 }
454 }
455 else
456 {
457 SetLastErrorText(pConn);
458 *pdwError = IsConnectionError(pConn);
459 }
460 OCIHandleFree(handleStmt, OCI_HTYPE_STMT);
461 if (errorText != NULL)
5502f4aa 462 nx_strncpy(errorText, pConn->szLastError, DBDRV_MAX_ERROR_TEXT);
5039dede 463 MutexUnlock(pConn->mutexQueryLock);
39945910
VK
464
465#if UNICODE_UCS4
466 free(ucs2Query);
467#endif
5039dede
AK
468 return pResult;
469}
470
471
472//
473// Get field length from result
474//
475
476extern "C" LONG EXPORT DrvGetFieldLength(ORACLE_RESULT *pResult, int nRow, int nColumn)
477{
478 if (pResult == NULL)
479 return 0;
480
481 if ((nRow >= 0) && (nRow < pResult->nRows) &&
482 (nColumn >= 0) && (nColumn < pResult->nCols))
caac6e38 483 return (LONG)wcslen(pResult->pData[pResult->nCols * nRow + nColumn]);
5039dede
AK
484
485 return 0;
486}
487
488
489//
490// Get field value from result
491//
492
493extern "C" WCHAR EXPORT *DrvGetField(ORACLE_RESULT *pResult, int nRow, int nColumn,
494 WCHAR *pBuffer, int nBufLen)
495{
496 WCHAR *pValue = NULL;
497
498 if (pResult != NULL)
499 {
500 if ((nRow < pResult->nRows) && (nRow >= 0) &&
501 (nColumn < pResult->nCols) && (nColumn >= 0))
502 {
5502f4aa
VK
503#ifdef _WIN32
504 wcsncpy_s(pBuffer, nBufLen, pResult->pData[nRow * pResult->nCols + nColumn], _TRUNCATE);
505#else
5039dede
AK
506 wcsncpy(pBuffer, pResult->pData[nRow * pResult->nCols + nColumn], nBufLen);
507 pBuffer[nBufLen - 1] = 0;
5502f4aa 508#endif
5039dede
AK
509 pValue = pBuffer;
510 }
511 }
512 return pValue;
513}
514
515
516//
517// Get number of rows in result
518//
519
520extern "C" int EXPORT DrvGetNumRows(ORACLE_RESULT *pResult)
521{
522 return (pResult != NULL) ? pResult->nRows : 0;
523}
524
525
526//
2a8e6a7b
VK
527// Get column count in query result
528//
529
530extern "C" int EXPORT DrvGetColumnCount(ORACLE_RESULT *pResult)
531{
532 return (pResult != NULL) ? pResult->nCols : 0;
533}
534
535
536//
537// Get column name in query result
538//
539
540extern "C" const char EXPORT *DrvGetColumnName(ORACLE_RESULT *pResult, int column)
541{
542 return ((pResult != NULL) && (column >= 0) && (column < pResult->nCols)) ? pResult->columnNames[column] : NULL;
543}
544
545
546//
5039dede
AK
547// Free SELECT results
548//
549
550extern "C" void EXPORT DrvFreeResult(ORACLE_RESULT *pResult)
551{
552 if (pResult != NULL)
553 DestroyQueryResult(pResult);
554}
555
556
557//
558// Perform asynchronous SELECT query
559//
560
561extern "C" DB_ASYNC_RESULT EXPORT DrvAsyncSelect(ORACLE_CONN *pConn, WCHAR *pwszQuery,
562 DWORD *pdwError, TCHAR *errorText)
563{
564 OCIParam *handleParam;
565 OCIDefine *handleDefine;
566 ub4 nCount;
567 ub2 nWidth;
2a8e6a7b 568 text *colName;
5039dede
AK
569 int i;
570
571 MutexLock(pConn->mutexQueryLock, INFINITE);
572
573 pConn->pBuffers = NULL;
574 pConn->nCols = 0;
575
576 OCIHandleAlloc(pConn->handleEnv, (void **)&pConn->handleStmt, OCI_HTYPE_STMT, 0, NULL);
577 if (OCIStmtPrepare(pConn->handleStmt, pConn->handleError, (text *)pwszQuery,
caac6e38 578 (ub4)wcslen(pwszQuery) * sizeof(WCHAR), OCI_NTV_SYNTAX, OCI_DEFAULT) == OCI_SUCCESS)
5039dede
AK
579 {
580 if (OCIStmtExecute(pConn->handleService, pConn->handleStmt, pConn->handleError,
581 0, 0, NULL, NULL, OCI_DEFAULT) == OCI_SUCCESS)
582 {
583 OCIAttrGet(pConn->handleStmt, OCI_HTYPE_STMT, &nCount, NULL, OCI_ATTR_PARAM_COUNT, pConn->handleError);
584 pConn->nCols = nCount;
585 if (pConn->nCols > 0)
586 {
2a8e6a7b
VK
587 // Prepare receive buffers and fetch column names
588 pConn->columnNames = (char **)malloc(sizeof(char *) * pConn->nCols);
5039dede
AK
589 pConn->pBuffers = (ORACLE_FETCH_BUFFER *)malloc(sizeof(ORACLE_FETCH_BUFFER) * pConn->nCols);
590 memset(pConn->pBuffers, 0, sizeof(ORACLE_FETCH_BUFFER) * pConn->nCols);
591 for(i = 0; i < pConn->nCols; i++)
592 {
593 pConn->pBuffers[i].isNull = 1; // Mark all columns as NULL initially
594 if (OCIParamGet(pConn->handleStmt, OCI_HTYPE_STMT, pConn->handleError,
595 (void **)&handleParam, (ub4)(i + 1)) == OCI_SUCCESS)
596 {
2a8e6a7b
VK
597 // Column name
598 if (OCIAttrGet(handleParam, OCI_DTYPE_PARAM, &colName, &nCount, OCI_ATTR_NAME, pConn->handleError) == OCI_SUCCESS)
599 {
600 // We are in UTF-16 mode, so OCIAttrGet will return UTF-16 strings
601 pConn->columnNames[i] = MBStringFromWideString((WCHAR *)colName);
602 }
603 else
604 {
605 pConn->columnNames[i] = strdup("");
606 }
607
608 // Data size
5039dede 609 OCIAttrGet(handleParam, OCI_DTYPE_PARAM, &nWidth, NULL, OCI_ATTR_DATA_SIZE, pConn->handleError);
39945910 610 pConn->pBuffers[i].pData = (UCS2CHAR *)malloc((nWidth + 1) * sizeof(UCS2CHAR));
5039dede
AK
611 handleDefine = NULL;
612 if (OCIDefineByPos(pConn->handleStmt, &handleDefine, pConn->handleError, i + 1,
39945910 613 pConn->pBuffers[i].pData, (nWidth + 1) * sizeof(UCS2CHAR),
5039dede
AK
614 SQLT_CHR, &pConn->pBuffers[i].isNull, &pConn->pBuffers[i].nLength,
615 &pConn->pBuffers[i].nCode, OCI_DEFAULT) == OCI_SUCCESS)
616 {
617 *pdwError = DBERR_SUCCESS;
618 }
619 else
620 {
621 SetLastErrorText(pConn);
622 *pdwError = IsConnectionError(pConn);
623 }
2a8e6a7b
VK
624
625 OCIDescriptorFree(handleParam, OCI_DTYPE_PARAM);
5039dede
AK
626 }
627 else
628 {
629 SetLastErrorText(pConn);
630 *pdwError = IsConnectionError(pConn);
631 }
632 }
633 }
634 }
635 else
636 {
637 SetLastErrorText(pConn);
638 *pdwError = IsConnectionError(pConn);
639 }
640 }
641 else
642 {
643 SetLastErrorText(pConn);
644 *pdwError = IsConnectionError(pConn);
645 }
646
647 if (*pdwError == DBERR_SUCCESS)
648 return pConn;
649
650 // On failure, unlock query mutex and do cleanup
651 for(i = 0; i < pConn->nCols; i++)
652 safe_free(pConn->pBuffers[i].pData);
653 safe_free(pConn->pBuffers);
654 if (errorText != NULL)
5502f4aa 655 nx_strncpy(errorText, pConn->szLastError, DBDRV_MAX_ERROR_TEXT);
5039dede
AK
656 MutexUnlock(pConn->mutexQueryLock);
657 return NULL;
658}
659
660
661//
662// Fetch next result line from asynchronous SELECT results
663//
664
665extern "C" BOOL EXPORT DrvFetch(ORACLE_CONN *pConn)
666{
667 if (pConn == NULL)
668 return FALSE;
669 return OCIStmtFetch(pConn->handleStmt, pConn->handleError, 1, OCI_FETCH_NEXT, OCI_DEFAULT) == OCI_SUCCESS;
670}
671
672
673//
61f032f5
VK
674// Get field length from current row in async query result
675//
676
677extern "C" LONG EXPORT DrvGetFieldLengthAsync(ORACLE_CONN *pConn, int nColumn)
678{
679 if (pConn == NULL)
680 return 0;
681
682 if ((nColumn < 0) || (nColumn >= pConn->nCols))
683 return 0;
684
685 if (pConn->pBuffers[nColumn].isNull)
686 return 0;
687
39945910 688 return (LONG)(pConn->pBuffers[nColumn].nLength / sizeof(UCS2CHAR));
61f032f5
VK
689}
690
691
692//
5039dede
AK
693// Get field from current row in async query result
694//
695
696extern "C" WCHAR EXPORT *DrvGetFieldAsync(ORACLE_CONN *pConn, int nColumn,
697 WCHAR *pBuffer, int nBufSize)
698{
699 int nLen;
700
701 if (pConn == NULL)
702 return NULL;
703
704 if ((nColumn < 0) || (nColumn >= pConn->nCols))
705 return NULL;
706
707 if (pConn->pBuffers[nColumn].isNull)
708 {
709 *pBuffer = 0;
710 }
711 else
712 {
39945910
VK
713 nLen = min(nBufSize - 1, (int)(pConn->pBuffers[nColumn].nLength / sizeof(UCS2CHAR)));
714#if UNICODE_UCS4
715 ucs2_to_ucs4(pConn->pBuffers[nColumn].pData, nLen, pBuffer, nLen);
716#else
5039dede 717 memcpy(pBuffer, pConn->pBuffers[nColumn].pData, nLen * sizeof(WCHAR));
39945910 718#endif
5039dede
AK
719 pBuffer[nLen] = 0;
720 }
721
722 return pBuffer;
723}
724
725
726//
2a8e6a7b
VK
727// Get column count in async query result
728//
729
730extern "C" int EXPORT DrvGetColumnCountAsync(ORACLE_CONN *pConn)
731{
732 return (pConn != NULL) ? pConn->nCols : 0;
733}
734
735
736//
737// Get column name in async query result
738//
739
740extern "C" const char EXPORT *DrvGetColumnNameAsync(ORACLE_CONN *pConn, int column)
741{
742 return ((pConn != NULL) && (column >= 0) && (column < pConn->nCols)) ? pConn->columnNames[column] : NULL;
743}
744
745
746//
5039dede
AK
747// Destroy result of async query
748//
749
750extern "C" void EXPORT DrvFreeAsyncResult(ORACLE_CONN *pConn)
751{
752 int i;
753
754 if (pConn == NULL)
755 return;
756
757 for(i = 0; i < pConn->nCols; i++)
758 safe_free(pConn->pBuffers[i].pData);
759 safe_free_and_null(pConn->pBuffers);
2a8e6a7b
VK
760
761 for(i = 0; i < pConn->nCols; i++)
762 safe_free(pConn->columnNames[i]);
763 safe_free_and_null(pConn->columnNames);
764
5039dede
AK
765 pConn->nCols = 0;
766 MutexUnlock(pConn->mutexQueryLock);
767}
768
769
770//
771// Begin transaction
772//
773
774extern "C" DWORD EXPORT DrvBegin(ORACLE_CONN *pConn)
775{
776 DWORD dwResult;
777
778 if (pConn == NULL)
779 return DBERR_INVALID_HANDLE;
780
781 MutexLock(pConn->mutexQueryLock, INFINITE);
782 if (OCITransStart(pConn->handleService, pConn->handleError, 300, OCI_TRANS_NEW) == OCI_SUCCESS)
783 {
784 pConn->nTransLevel++;
785 dwResult = DBERR_SUCCESS;
786 }
787 else
788 {
789 SetLastErrorText(pConn);
790 dwResult = IsConnectionError(pConn);
791 }
792 MutexUnlock(pConn->mutexQueryLock);
793 return dwResult;
794}
795
796
797//
798// Commit transaction
799//
800
801extern "C" DWORD EXPORT DrvCommit(ORACLE_CONN *pConn)
802{
803 DWORD dwResult;
804
805 if (pConn == NULL)
806 return DBERR_INVALID_HANDLE;
807
808 MutexLock(pConn->mutexQueryLock, INFINITE);
809 if (pConn->nTransLevel > 0)
810 {
811 if (OCITransCommit(pConn->handleService, pConn->handleError, OCI_DEFAULT) == OCI_SUCCESS)
812 {
813 dwResult = DBERR_SUCCESS;
814 pConn->nTransLevel--;
815 }
816 else
817 {
818 SetLastErrorText(pConn);
819 dwResult = IsConnectionError(pConn);
820 }
821 }
822 else
823 {
824 dwResult = DBERR_SUCCESS;
825 }
826 MutexUnlock(pConn->mutexQueryLock);
827 return dwResult;
828}
829
830
831//
832// Rollback transaction
833//
834
835extern "C" DWORD EXPORT DrvRollback(ORACLE_CONN *pConn)
836{
837 DWORD dwResult;
838
839 if (pConn == NULL)
840 return DBERR_INVALID_HANDLE;
841
842 MutexLock(pConn->mutexQueryLock, INFINITE);
843 if (pConn->nTransLevel > 0)
844 {
845 if (OCITransRollback(pConn->handleService, pConn->handleError, OCI_DEFAULT) == OCI_SUCCESS)
846 {
847 dwResult = DBERR_SUCCESS;
848 pConn->nTransLevel--;
849 }
850 else
851 {
852 SetLastErrorText(pConn);
853 dwResult = IsConnectionError(pConn);
854 }
855 }
856 else
857 {
858 dwResult = DBERR_SUCCESS;
859 }
860 MutexUnlock(pConn->mutexQueryLock);
861 return dwResult;
862}
863
864
865//
866// DLL Entry point
867//
868
869#ifdef _WIN32
870
871BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
872{
873 if (dwReason == DLL_PROCESS_ATTACH)
874 DisableThreadLibraryCalls(hInstance);
875 return TRUE;
876}
877
878#endif