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