Linux build fixed
[public/netxms.git] / src / db / dbdrv / pgsql / pgsql.cpp
1 /*
2 ** PostgreSQL Database Driver
3 ** Copyright (C) 2003 - 2010 Victor Kirhenshtein and Alex 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: pgsql.cpp
20 **
21 **/
22
23 #include "pgsqldrv.h"
24
25 #ifdef _WIN32
26 #pragma warning(disable : 4996)
27 #endif
28
29 DECLARE_DRIVER_HEADER("PGSQL")
30
31
32 //
33 // Prepare string for using in SQL query - enclose in quotes and escape as needed
34 //
35
36 extern "C" WCHAR EXPORT *DrvPrepareStringW(const WCHAR *str)
37 {
38 int len = (int)wcslen(str) + 3; // + two quotes and \0 at the end
39 int bufferSize = len + 128;
40 WCHAR *out = (WCHAR *)malloc(bufferSize * sizeof(WCHAR));
41 out[0] = L'\'';
42
43 const WCHAR *src = str;
44 int outPos;
45 for(outPos = 1; *src != 0; src++)
46 {
47 long chval = *src;
48 if (chval < 32)
49 {
50 WCHAR buffer[8];
51
52 swprintf(buffer, 8, L"\\%03o", chval);
53 len += 4;
54 if (len >= bufferSize)
55 {
56 bufferSize += 128;
57 out = (WCHAR *)realloc(out, bufferSize * sizeof(WCHAR));
58 }
59 memcpy(&out[outPos], buffer, 4 * sizeof(WCHAR));
60 outPos += 4;
61 }
62 else if (*src == L'\'')
63 {
64 len++;
65 if (len >= bufferSize)
66 {
67 bufferSize += 128;
68 out = (WCHAR *)realloc(out, bufferSize * sizeof(WCHAR));
69 }
70 out[outPos++] = L'\'';
71 out[outPos++] = L'\'';
72 }
73 else if (*src == L'\\')
74 {
75 len++;
76 if (len >= bufferSize)
77 {
78 bufferSize += 128;
79 out = (WCHAR *)realloc(out, bufferSize * sizeof(WCHAR));
80 }
81 out[outPos++] = L'\\';
82 out[outPos++] = L'\\';
83 }
84 else
85 {
86 out[outPos++] = *src;
87 }
88 }
89 out[outPos++] = L'\'';
90 out[outPos++] = 0;
91
92 return out;
93 }
94
95 extern "C" char EXPORT *DrvPrepareStringA(const char *str)
96 {
97 int len = (int)_tcslen(str) + 3; // + two quotes and \0 at the end
98 int bufferSize = len + 128;
99 char *out = (char *)malloc(bufferSize);
100 out[0] = '\'';
101
102 const char *src = str;
103 int outPos;
104 for(outPos = 1; *src != 0; src++)
105 {
106 long chval = (long)(*((unsigned char *)src));
107 if (chval < 32)
108 {
109 TCHAR buffer[8];
110
111 snprintf(buffer, 8, "\\%03o", chval);
112 len += 4;
113 if (len >= bufferSize)
114 {
115 bufferSize += 128;
116 out = (char *)realloc(out, bufferSize);
117 }
118 memcpy(&out[outPos], buffer, 4);
119 outPos += 4;
120 }
121 else if (*src == '\'')
122 {
123 len++;
124 if (len >= bufferSize)
125 {
126 bufferSize += 128;
127 out = (char *)realloc(out, bufferSize);
128 }
129 out[outPos++] = '\'';
130 out[outPos++] = '\'';
131 }
132 else if (*src == '\\')
133 {
134 len++;
135 if (len >= bufferSize)
136 {
137 bufferSize += 128;
138 out = (char *)realloc(out, bufferSize);
139 }
140 out[outPos++] = '\\';
141 out[outPos++] = '\\';
142 }
143 else
144 {
145 out[outPos++] = *src;
146 }
147 }
148 out[outPos++] = '\'';
149 out[outPos++] = 0;
150
151 return out;
152 }
153
154
155 //
156 // Initialize driver
157 //
158
159 extern "C" BOOL EXPORT DrvInit(const char *cmdLine)
160 {
161 return TRUE;
162 }
163
164 //
165 // Unload handler
166 //
167
168 extern "C" void EXPORT DrvUnload()
169 {
170 }
171
172
173 //
174 // Connect to database
175 //
176
177 extern "C" DBDRV_CONNECTION EXPORT DrvConnect(
178 char *szHost,
179 char *szLogin,
180 char *szPassword,
181 char *szDatabase)
182 {
183 PG_CONN *pConn;
184
185 if (szDatabase == NULL || *szDatabase == 0)
186 {
187 return NULL;
188 }
189
190 pConn = (PG_CONN *)malloc(sizeof(PG_CONN));
191 if (pConn != NULL)
192 {
193 // should be replaced with PQconnectdb();
194 pConn->pHandle = PQsetdbLogin(szHost, NULL, NULL, NULL,
195 szDatabase, szLogin, szPassword);
196
197 if (PQstatus(pConn->pHandle) == CONNECTION_BAD)
198 {
199 free(pConn);
200 pConn = NULL;
201 }
202 else
203 {
204 PGresult *pResult;
205
206 pResult = PQexec(pConn->pHandle, "SET standard_conforming_strings TO off");
207 PQclear(pResult);
208
209 pResult = PQexec(pConn->pHandle, "SET escape_string_warning TO off");
210 PQclear(pResult);
211
212 PQsetClientEncoding(pConn->pHandle, "UTF8");
213
214 pConn->mutexQueryLock = MutexCreate();
215 pConn->pFetchBuffer = NULL;
216 }
217 }
218
219 return (DBDRV_CONNECTION)pConn;
220 }
221
222
223 //
224 // Disconnect from database
225 //
226
227 extern "C" void EXPORT DrvDisconnect(DBDRV_CONNECTION pConn)
228 {
229 if (pConn != NULL)
230 {
231 PQfinish(((PG_CONN *)pConn)->pHandle);
232 MutexDestroy(((PG_CONN *)pConn)->mutexQueryLock);
233 free(pConn);
234 }
235 }
236
237
238 //
239 // Perform non-SELECT query
240 //
241
242 static BOOL UnsafeDrvQuery(PG_CONN *pConn, const char *szQuery, WCHAR *errorText)
243 {
244 PGresult *pResult;
245
246 pResult = PQexec(pConn->pHandle, szQuery);
247
248 if (pResult == NULL)
249 {
250 if (errorText != NULL)
251 wcsncpy(errorText, L"Internal error (pResult is NULL in UnsafeDrvQuery)", DBDRV_MAX_ERROR_TEXT);
252 return FALSE;
253 }
254
255 if (PQresultStatus(pResult) != PGRES_COMMAND_OK)
256 {
257 if (errorText != NULL)
258 {
259 MultiByteToWideChar(CP_UTF8, 0, PQerrorMessage(pConn->pHandle), -1, errorText, DBDRV_MAX_ERROR_TEXT);
260 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
261 RemoveTrailingCRLFW(errorText);
262 }
263 PQclear(pResult);
264 return FALSE;
265 }
266
267 PQclear(pResult);
268 if (errorText != NULL)
269 *errorText = 0;
270 return TRUE;
271 }
272
273 extern "C" DWORD EXPORT DrvQuery(PG_CONN *pConn, WCHAR *pwszQuery, WCHAR *errorText)
274 {
275 DWORD dwRet = DBERR_INVALID_HANDLE;
276 char *pszQueryUTF8;
277
278 if ((pConn != NULL) && (pwszQuery != NULL))
279 {
280 pszQueryUTF8 = UTF8StringFromWideString(pwszQuery);
281 MutexLock(pConn->mutexQueryLock, INFINITE);
282 if (UnsafeDrvQuery(pConn, pszQueryUTF8, errorText))
283 {
284 dwRet = DBERR_SUCCESS;
285 }
286 else
287 {
288 dwRet = (PQstatus(pConn->pHandle) == CONNECTION_BAD) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR;
289 }
290 MutexUnlock(pConn->mutexQueryLock);
291 free(pszQueryUTF8);
292 }
293
294 return dwRet;
295 }
296
297
298 //
299 // Perform SELECT query
300 //
301
302 static DBDRV_RESULT UnsafeDrvSelect(PG_CONN *pConn, const char *szQuery, WCHAR *errorText)
303 {
304 PGresult *pResult;
305
306 pResult = PQexec(((PG_CONN *)pConn)->pHandle, szQuery);
307
308 if (pResult == NULL)
309 {
310 if (errorText != NULL)
311 wcsncpy(errorText, L"Internal error (pResult is NULL in UnsafeDrvSelect)", DBDRV_MAX_ERROR_TEXT);
312 return NULL;
313 }
314
315 if ((PQresultStatus(pResult) != PGRES_COMMAND_OK) &&
316 (PQresultStatus(pResult) != PGRES_TUPLES_OK))
317 {
318
319 if (errorText != NULL)
320 {
321 MultiByteToWideChar(CP_UTF8, 0, PQerrorMessage(pConn->pHandle), -1, errorText, DBDRV_MAX_ERROR_TEXT);
322 errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0;
323 RemoveTrailingCRLFW(errorText);
324 }
325 PQclear(pResult);
326 return NULL;
327 }
328
329 if (errorText != NULL)
330 *errorText = 0;
331 return (DBDRV_RESULT)pResult;
332 }
333
334 extern "C" DBDRV_RESULT EXPORT DrvSelect(PG_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText)
335 {
336 DBDRV_RESULT pResult;
337 char *pszQueryUTF8;
338
339 if (pConn == NULL)
340 {
341 *pdwError = DBERR_INVALID_HANDLE;
342 return NULL;
343 }
344
345 pszQueryUTF8 = UTF8StringFromWideString(pwszQuery);
346 MutexLock(pConn->mutexQueryLock, INFINITE);
347 pResult = UnsafeDrvSelect(pConn, pszQueryUTF8, errorText);
348 if (pResult != NULL)
349 {
350 *pdwError = DBERR_SUCCESS;
351 }
352 else
353 {
354 *pdwError = (PQstatus(pConn->pHandle) == CONNECTION_BAD) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR;
355 }
356 MutexUnlock(pConn->mutexQueryLock);
357 free(pszQueryUTF8);
358
359 return pResult;
360 }
361
362
363 //
364 // Get field length from result
365 //
366
367 extern "C" LONG EXPORT DrvGetFieldLength(DBDRV_RESULT pResult, int nRow, int nColumn)
368 {
369 char *pszValue;
370
371 if (pResult == NULL)
372 return -1;
373
374 pszValue = PQgetvalue((PGresult *)pResult, nRow, nColumn);
375 return (pszValue != NULL) ? (LONG)strlen(pszValue) : (LONG)-1;
376 }
377
378
379 //
380 // Get field value from result
381 //
382
383 extern "C" WCHAR EXPORT *DrvGetField(DBDRV_RESULT pResult, int nRow, int nColumn,
384 WCHAR *pBuffer, int nBufLen)
385 {
386 if (pResult == NULL)
387 return NULL;
388
389 MultiByteToWideChar(CP_UTF8, 0, PQgetvalue((PGresult *)pResult, nRow, nColumn),
390 -1, pBuffer, nBufLen);
391 pBuffer[nBufLen - 1] = 0;
392 return pBuffer;
393 }
394
395
396 //
397 // Get number of rows in result
398 //
399
400 extern "C" int EXPORT DrvGetNumRows(DBDRV_RESULT pResult)
401 {
402 return (pResult != NULL) ? PQntuples((PGresult *)pResult) : 0;
403 }
404
405
406 //
407 // Get column count in query result
408 //
409
410 extern "C" int EXPORT DrvGetColumnCount(DBDRV_RESULT hResult)
411 {
412 return (hResult != NULL) ? PQnfields((PGresult *)hResult) : 0;
413 }
414
415
416 //
417 // Get column name in query result
418 //
419
420 extern "C" const char EXPORT *DrvGetColumnName(DBDRV_RESULT hResult, int column)
421 {
422 return (hResult != NULL) ? PQfname((PGresult *)hResult, column) : NULL;
423 }
424
425
426 //
427 // Free SELECT results
428 //
429
430 extern "C" void EXPORT DrvFreeResult(DBDRV_RESULT pResult)
431 {
432 if (pResult != NULL)
433 {
434 PQclear((PGresult *)pResult);
435 }
436 }
437
438
439 //
440 // Perform asynchronous SELECT query
441 //
442
443 extern "C" DBDRV_ASYNC_RESULT EXPORT DrvAsyncSelect(PG_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText)
444 {
445 BOOL bSuccess = FALSE;
446 char *pszReq, *pszQueryUTF8;
447 static char szDeclareCursor[] = "DECLARE cur1 CURSOR FOR ";
448
449 if (pConn == NULL)
450 return NULL;
451
452 MutexLock(pConn->mutexQueryLock, INFINITE);
453
454 if (UnsafeDrvQuery(pConn, "BEGIN", errorText))
455 {
456 pszQueryUTF8 = UTF8StringFromWideString(pwszQuery);
457 pszReq = (char *)malloc(strlen(pszQueryUTF8) + sizeof(szDeclareCursor));
458 if (pszReq != NULL)
459 {
460 strcpy(pszReq, szDeclareCursor);
461 strcat(pszReq, pszQueryUTF8);
462 if (UnsafeDrvQuery(pConn, pszReq, errorText))
463 {
464 bSuccess = TRUE;
465 }
466 free(pszReq);
467 }
468 free(pszQueryUTF8);
469
470 if (!bSuccess)
471 UnsafeDrvQuery(pConn, "ROLLBACK", NULL);
472 }
473
474 if (bSuccess)
475 {
476 *pdwError = DBERR_SUCCESS;
477 }
478 else
479 {
480 *pdwError = (PQstatus(pConn->pHandle) == CONNECTION_BAD) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR;
481 MutexUnlock(pConn->mutexQueryLock);
482 return NULL;
483 }
484
485 return (DBDRV_ASYNC_RESULT)pConn;
486 }
487
488
489 //
490 // Fetch next result line from asynchronous SELECT results
491 //
492
493 extern "C" BOOL EXPORT DrvFetch(DBDRV_ASYNC_RESULT pConn)
494 {
495 BOOL bResult = TRUE;
496
497 if (pConn == NULL)
498 {
499 bResult = FALSE;
500 }
501 else
502 {
503 if (((PG_CONN *)pConn)->pFetchBuffer != NULL)
504 PQclear(((PG_CONN *)pConn)->pFetchBuffer);
505 ((PG_CONN *)pConn)->pFetchBuffer =
506 (PGresult *)UnsafeDrvSelect((PG_CONN *)pConn, "FETCH cur1", NULL);
507 if (((PG_CONN *)pConn)->pFetchBuffer != NULL)
508 {
509 if (DrvGetNumRows(((PG_CONN *)pConn)->pFetchBuffer) <= 0)
510 {
511 PQclear(((PG_CONN *)pConn)->pFetchBuffer);
512 ((PG_CONN *)pConn)->pFetchBuffer = NULL;
513 bResult = FALSE;
514 }
515 }
516 else
517 {
518 bResult = FALSE;
519 }
520 }
521 return bResult;
522 }
523
524
525 //
526 // Get field length from async quety result
527 //
528
529 extern "C" LONG EXPORT DrvGetFieldLengthAsync(PG_CONN *pConn, int nColumn)
530 {
531 if ((pConn == NULL) || (pConn->pFetchBuffer == NULL))
532 {
533 return 0;
534 }
535
536 // validate column index
537 if (nColumn >= PQnfields(pConn->pFetchBuffer))
538 {
539 return 0;
540 }
541
542 char *value = PQgetvalue(pConn->pFetchBuffer, 0, nColumn);
543 if (value == NULL)
544 {
545 return 0;
546 }
547
548 return strlen(value);
549 }
550
551
552 //
553 // Get field from current row in async query result
554 //
555
556 extern "C" WCHAR EXPORT *DrvGetFieldAsync(
557 PG_CONN *pConn,
558 int nColumn,
559 WCHAR *pBuffer,
560 int nBufSize)
561 {
562 char *pszResult;
563
564 if ((pConn == NULL) || (pConn->pFetchBuffer == NULL))
565 {
566 return NULL;
567 }
568
569 // validate column index
570 if (nColumn >= PQnfields(pConn->pFetchBuffer))
571 {
572 return NULL;
573 }
574
575 // FIXME: correct processing of binary fields
576 // PQfformat not supported in 7.3
577 #ifdef HAVE_PQFFORMAT
578 if (PQfformat(pConn->pFetchBuffer, nColumn) != 0)
579 #else
580 if (PQbinaryTuples(pConn->pFetchBuffer) != 0)
581 #endif
582 {
583 //fprintf(stderr, "db:postgres:binary fields not supported\n");
584 //fflush(stderr);
585 // abort();
586 return NULL;
587 }
588
589 pszResult = PQgetvalue(pConn->pFetchBuffer, 0, nColumn);
590 if (pszResult == NULL)
591 {
592 return NULL;
593 }
594
595 // Now get column data
596 MultiByteToWideChar(CP_UTF8, 0, pszResult, -1, pBuffer, nBufSize);
597 pBuffer[nBufSize - 1] = 0;
598
599 return pBuffer;
600 }
601
602
603 //
604 // Get column count in async query result
605 //
606
607 extern "C" int EXPORT DrvGetColumnCountAsync(DBDRV_ASYNC_RESULT hResult)
608 {
609 return ((hResult != NULL) && (((PG_CONN *)hResult)->pFetchBuffer != NULL))? PQnfields(((PG_CONN *)hResult)->pFetchBuffer) : 0;
610 }
611
612
613 //
614 // Get column name in async query result
615 //
616
617 extern "C" const char EXPORT *DrvGetColumnNameAsync(DBDRV_ASYNC_RESULT hResult, int column)
618 {
619 return ((hResult != NULL) && (((PG_CONN *)hResult)->pFetchBuffer != NULL))? PQfname(((PG_CONN *)hResult)->pFetchBuffer, column) : NULL;
620 }
621
622
623 //
624 // Destroy result of async query
625 //
626
627 extern "C" void EXPORT DrvFreeAsyncResult(PG_CONN *pConn)
628 {
629 if (pConn != NULL)
630 {
631 if (pConn->pFetchBuffer != NULL)
632 {
633 PQclear(pConn->pFetchBuffer);
634 pConn->pFetchBuffer = NULL;
635 }
636 UnsafeDrvQuery(pConn, "CLOSE cur1", NULL);
637 UnsafeDrvQuery(pConn, "COMMIT", NULL);
638 }
639 MutexUnlock(pConn->mutexQueryLock);
640 }
641
642
643 //
644 // Begin transaction
645 //
646
647 extern "C" DWORD EXPORT DrvBegin(PG_CONN *pConn)
648 {
649 DWORD dwResult;
650
651 if (pConn == NULL)
652 return DBERR_INVALID_HANDLE;
653
654 MutexLock(pConn->mutexQueryLock, INFINITE);
655 if (UnsafeDrvQuery(pConn, "BEGIN", NULL))
656 {
657 dwResult = DBERR_SUCCESS;
658 }
659 else
660 {
661 dwResult = (PQstatus(pConn->pHandle) == CONNECTION_BAD) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR;
662 }
663 MutexUnlock(pConn->mutexQueryLock);
664 return dwResult;
665 }
666
667
668 //
669 // Commit transaction
670 //
671
672 extern "C" DWORD EXPORT DrvCommit(PG_CONN *pConn)
673 {
674 BOOL bRet;
675
676 if (pConn == NULL)
677 return DBERR_INVALID_HANDLE;
678
679 MutexLock(pConn->mutexQueryLock, INFINITE);
680 bRet = UnsafeDrvQuery(pConn, "COMMIT", NULL);
681 MutexUnlock(pConn->mutexQueryLock);
682 return bRet ? DBERR_SUCCESS : DBERR_OTHER_ERROR;
683 }
684
685
686 //
687 // Rollback transaction
688 //
689
690 extern "C" DWORD EXPORT DrvRollback(PG_CONN *pConn)
691 {
692 BOOL bRet;
693
694 if (pConn == NULL)
695 return DBERR_INVALID_HANDLE;
696
697 MutexLock(pConn->mutexQueryLock, INFINITE);
698 bRet = UnsafeDrvQuery(pConn, "ROLLBACK", NULL);
699 MutexUnlock(pConn->mutexQueryLock);
700 return bRet ? DBERR_SUCCESS : DBERR_OTHER_ERROR;
701 }
702
703
704 //
705 // DLL Entry point
706 //
707
708 #ifdef _WIN32
709
710 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
711 {
712 if (dwReason == DLL_PROCESS_ATTACH)
713 DisableThreadLibraryCalls(hInstance);
714 return TRUE;
715 }
716
717 #endif