2 ** PostgreSQL Database Driver
3 ** Copyright (C) 2003 - 2016 Victor Kirhenshtein and Alex Kirhenshtein
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.
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.
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.
30 #pragma warning(disable : 4996)
33 DECLARE_DRIVER_HEADER("PGSQL")
35 extern "C" void EXPORT
DrvDisconnect(DBDRV_CONNECTION pConn
);
36 static bool UnsafeDrvQuery(PG_CONN
*pConn
, const char *szQuery
, WCHAR
*errorText
);
39 static void *s_libpq
= NULL
;
40 static int (*s_PQsetSingleRowMode
)(PGconn
*) = NULL
;
43 #if !HAVE_DECL_PGRES_SINGLE_TUPLE
44 #define PGRES_SINGLE_TUPLE 9
50 static VolatileCounter s_statementId
= 0;
53 * Prepare string for using in SQL query - enclose in quotes and escape as needed
55 extern "C" WCHAR EXPORT
*DrvPrepareStringW(const WCHAR
*str
)
57 int len
= (int)wcslen(str
) + 3; // + two quotes and \0 at the end
58 int bufferSize
= len
+ 128;
59 WCHAR
*out
= (WCHAR
*)malloc(bufferSize
* sizeof(WCHAR
));
62 const WCHAR
*src
= str
;
64 for(outPos
= 1; *src
!= 0; src
++)
71 swprintf(buffer
, 8, L
"\\%03o", chval
);
73 if (len
>= bufferSize
)
76 out
= (WCHAR
*)realloc(out
, bufferSize
* sizeof(WCHAR
));
78 memcpy(&out
[outPos
], buffer
, 4 * sizeof(WCHAR
));
81 else if (*src
== L
'\'')
84 if (len
>= bufferSize
)
87 out
= (WCHAR
*)realloc(out
, bufferSize
* sizeof(WCHAR
));
89 out
[outPos
++] = L
'\'';
90 out
[outPos
++] = L
'\'';
92 else if (*src
== L
'\\')
95 if (len
>= bufferSize
)
98 out
= (WCHAR
*)realloc(out
, bufferSize
* sizeof(WCHAR
));
100 out
[outPos
++] = L
'\\';
101 out
[outPos
++] = L
'\\';
105 out
[outPos
++] = *src
;
108 out
[outPos
++] = L
'\'';
115 * Prepare string for using in SQL query - enclose in quotes and escape as needed
117 extern "C" char EXPORT
*DrvPrepareStringA(const char *str
)
119 int len
= (int)strlen(str
) + 3; // + two quotes and \0 at the end
120 int bufferSize
= len
+ 128;
121 char *out
= (char *)malloc(bufferSize
);
124 const char *src
= str
;
126 for(outPos
= 1; *src
!= 0; src
++)
128 UINT32 chval
= (UINT32
)(*((unsigned char *)src
));
133 snprintf(buffer
, 8, "\\%03o", chval
);
135 if (len
>= bufferSize
)
138 out
= (char *)realloc(out
, bufferSize
);
140 memcpy(&out
[outPos
], buffer
, 4);
143 else if (*src
== '\'')
146 if (len
>= bufferSize
)
149 out
= (char *)realloc(out
, bufferSize
);
151 out
[outPos
++] = '\'';
152 out
[outPos
++] = '\'';
154 else if (*src
== '\\')
157 if (len
>= bufferSize
)
160 out
= (char *)realloc(out
, bufferSize
);
162 out
[outPos
++] = '\\';
163 out
[outPos
++] = '\\';
167 out
[outPos
++] = *src
;
170 out
[outPos
++] = '\'';
179 extern "C" bool EXPORT
DrvInit(const char *cmdLine
)
182 s_libpq
= dlopen("libpq.so.5", RTLD_NOW
);
184 s_PQsetSingleRowMode
= (int (*)(PGconn
*))dlsym(s_libpq
, "PQsetSingleRowMode");
185 nxlog_debug(2, _T("PostgreSQL driver: single row mode %s"), (s_PQsetSingleRowMode
!= NULL
) ? _T("enabled") : _T("disabled"));
193 extern "C" void EXPORT
DrvUnload()
202 * Connect to database
204 extern "C" DBDRV_CONNECTION EXPORT
DrvConnect(const char *szHost
, const char *szLogin
, const char *szPassword
,
205 const char *szDatabase
, const char *schema
, WCHAR
*errorText
)
210 if (szDatabase
== NULL
|| *szDatabase
== 0)
212 wcscpy(errorText
, L
"Database name is empty");
215 if((port
= (char *)strchr(szHost
, ':'))!=NULL
)
221 pConn
= (PG_CONN
*)malloc(sizeof(PG_CONN
));
225 // should be replaced with PQconnectdb();
226 pConn
->handle
= PQsetdbLogin(szHost
, port
, NULL
, NULL
, szDatabase
, szLogin
, szPassword
);
228 if (PQstatus(pConn
->handle
) == CONNECTION_BAD
)
230 MultiByteToWideChar(CP_UTF8
, 0, PQerrorMessage(pConn
->handle
), -1, errorText
, DBDRV_MAX_ERROR_TEXT
);
231 errorText
[DBDRV_MAX_ERROR_TEXT
- 1] = 0;
232 RemoveTrailingCRLFW(errorText
);
233 PQfinish(pConn
->handle
);
241 pResult
= PQexec(pConn
->handle
, "SET standard_conforming_strings TO off");
244 pResult
= PQexec(pConn
->handle
, "SET escape_string_warning TO off");
247 PQsetClientEncoding(pConn
->handle
, "UTF8");
249 pConn
->mutexQueryLock
= MutexCreate();
251 if ((schema
!= NULL
) && (schema
[0] != 0))
254 snprintf(query
, 256, "SET search_path=%s", schema
);
255 if (!UnsafeDrvQuery(pConn
, query
, errorText
))
257 DrvDisconnect(pConn
);
265 wcscpy(errorText
, L
"Memory allocation error");
268 return (DBDRV_CONNECTION
)pConn
;
272 * Disconnect from database
274 extern "C" void EXPORT
DrvDisconnect(DBDRV_CONNECTION pConn
)
278 PQfinish(((PG_CONN
*)pConn
)->handle
);
279 MutexDestroy(((PG_CONN
*)pConn
)->mutexQueryLock
);
285 * Convert query from NetXMS portable format to native PostgreSQL format
287 static char *ConvertQuery(WCHAR
*query
)
289 char *srcQuery
= UTF8StringFromWideString(query
);
290 int count
= NumCharsA(srcQuery
, '?');
294 char *dstQuery
= (char *)malloc(strlen(srcQuery
) + count
* 3 + 1);
295 bool inString
= false;
298 for(src
= srcQuery
, dst
= dstQuery
; *src
!= 0; src
++)
304 inString
= !inString
;
324 *dst
++ = pos
/ 10 + '0';
325 *dst
++ = pos
% 10 + '0';
329 *dst
++ = pos
/ 100 + '0';
330 *dst
++ = (pos
% 100) / 10 + '0';
331 *dst
++ = pos
% 10 + '0';
349 extern "C" DBDRV_STATEMENT EXPORT
DrvPrepare(PG_CONN
*pConn
, WCHAR
*pwszQuery
, DWORD
*pdwError
, WCHAR
*errorText
)
351 char *pszQueryUTF8
= ConvertQuery(pwszQuery
);
352 PG_STATEMENT
*hStmt
= (PG_STATEMENT
*)malloc(sizeof(PG_STATEMENT
));
353 hStmt
->connection
= pConn
;
354 snprintf(hStmt
->name
, 64, "netxms_stmt_%p_%d", hStmt
, (int)InterlockedIncrement(&s_statementId
));
356 MutexLock(pConn
->mutexQueryLock
);
357 PGresult
*pResult
= PQprepare(pConn
->handle
, hStmt
->name
, pszQueryUTF8
, 0, NULL
);
358 if ((pResult
== NULL
) || (PQresultStatus(pResult
) != PGRES_COMMAND_OK
))
363 *pdwError
= (PQstatus(pConn
->handle
) == CONNECTION_BAD
) ? DBERR_CONNECTION_LOST
: DBERR_OTHER_ERROR
;
365 if (errorText
!= NULL
)
367 MultiByteToWideChar(CP_UTF8
, 0, PQerrorMessage(pConn
->handle
), -1, errorText
, DBDRV_MAX_ERROR_TEXT
);
368 errorText
[DBDRV_MAX_ERROR_TEXT
- 1] = 0;
369 RemoveTrailingCRLFW(errorText
);
374 hStmt
->allocated
= 0;
376 hStmt
->buffers
= NULL
;
377 *pdwError
= DBERR_SUCCESS
;
379 MutexUnlock(pConn
->mutexQueryLock
);
387 * Bind parameter to prepared statement
389 extern "C" void EXPORT
DrvBind(PG_STATEMENT
*hStmt
, int pos
, int sqlType
, int cType
, void *buffer
, int allocType
)
394 if (hStmt
->allocated
< pos
)
396 int newAllocated
= max(hStmt
->allocated
+ 16, pos
);
397 hStmt
->buffers
= (char **)realloc(hStmt
->buffers
, sizeof(char *) * newAllocated
);
398 for(int i
= hStmt
->allocated
; i
< newAllocated
; i
++)
399 hStmt
->buffers
[i
] = NULL
;
400 hStmt
->allocated
= newAllocated
;
402 if (hStmt
->pcount
< pos
)
405 free(hStmt
->buffers
[pos
- 1]);
409 case DB_CTYPE_STRING
:
410 hStmt
->buffers
[pos
- 1] = UTF8StringFromWideString((WCHAR
*)buffer
);
412 case DB_CTYPE_UTF8_STRING
:
413 if (allocType
== DB_BIND_DYNAMIC
)
415 hStmt
->buffers
[pos
- 1] = (char *)buffer
;
416 buffer
= NULL
; // prevent deallocation
420 hStmt
->buffers
[pos
- 1] = strdup((char *)buffer
);
424 hStmt
->buffers
[pos
- 1] = (char *)malloc(16);
425 sprintf(hStmt
->buffers
[pos
- 1], "%d", *((int *)buffer
));
427 case DB_CTYPE_UINT32
:
428 hStmt
->buffers
[pos
- 1] = (char *)malloc(16);
429 sprintf(hStmt
->buffers
[pos
- 1], "%u", *((unsigned int *)buffer
));
432 hStmt
->buffers
[pos
- 1] = (char *)malloc(32);
433 sprintf(hStmt
->buffers
[pos
- 1], INT64_FMTA
, *((INT64
*)buffer
));
435 case DB_CTYPE_UINT64
:
436 hStmt
->buffers
[pos
- 1] = (char *)malloc(32);
437 sprintf(hStmt
->buffers
[pos
- 1], UINT64_FMTA
, *((QWORD
*)buffer
));
439 case DB_CTYPE_DOUBLE
:
440 hStmt
->buffers
[pos
- 1] = (char *)malloc(32);
441 sprintf(hStmt
->buffers
[pos
- 1], "%f", *((double *)buffer
));
444 hStmt
->buffers
[pos
- 1] = strdup("");
448 if (allocType
== DB_BIND_DYNAMIC
)
453 * Execute prepared statement
455 extern "C" DWORD EXPORT
DrvExecute(PG_CONN
*pConn
, PG_STATEMENT
*hStmt
, WCHAR
*errorText
)
459 MutexLock(pConn
->mutexQueryLock
);
465 PGresult
*pResult
= PQexecPrepared(pConn
->handle
, hStmt
->name
, hStmt
->pcount
, hStmt
->buffers
, NULL
, NULL
, 0);
468 if (PQresultStatus(pResult
) == PGRES_COMMAND_OK
)
470 if (errorText
!= NULL
)
476 const char *sqlState
= PQresultErrorField(pResult
, PG_DIAG_SQLSTATE
);
477 if ((PQstatus(pConn
->handle
) != CONNECTION_BAD
) &&
478 (sqlState
!= NULL
) && (!strcmp(sqlState
, "53000") || !strcmp(sqlState
, "53200")) && (retryCount
> 0))
486 if (errorText
!= NULL
)
488 MultiByteToWideChar(CP_UTF8
, 0, CHECK_NULL_EX_A(sqlState
), -1, errorText
, DBDRV_MAX_ERROR_TEXT
);
489 int len
= (int)wcslen(errorText
);
492 errorText
[len
] = L
' ';
495 MultiByteToWideChar(CP_UTF8
, 0, PQerrorMessage(pConn
->handle
), -1, &errorText
[len
], DBDRV_MAX_ERROR_TEXT
- len
);
496 errorText
[DBDRV_MAX_ERROR_TEXT
- 1] = 0;
497 RemoveTrailingCRLFW(errorText
);
499 rc
= (PQstatus(pConn
->handle
) == CONNECTION_BAD
) ? DBERR_CONNECTION_LOST
: DBERR_OTHER_ERROR
;
507 if (errorText
!= NULL
)
508 wcsncpy(errorText
, L
"Internal error (pResult is NULL in DrvExecute)", DBDRV_MAX_ERROR_TEXT
);
509 rc
= DBERR_OTHER_ERROR
;
513 MutexUnlock(pConn
->mutexQueryLock
);
518 * Destroy prepared statement
520 extern "C" void EXPORT
DrvFreeStatement(PG_STATEMENT
*hStmt
)
526 snprintf(query
, 256, "DEALLOCATE \"%s\"", hStmt
->name
);
528 MutexLock(hStmt
->connection
->mutexQueryLock
);
529 UnsafeDrvQuery(hStmt
->connection
, query
, NULL
);
530 MutexUnlock(hStmt
->connection
->mutexQueryLock
);
532 for(int i
= 0; i
< hStmt
->allocated
; i
++)
533 safe_free(hStmt
->buffers
[i
]);
534 safe_free(hStmt
->buffers
);
540 * Perform non-SELECT query - internal implementation
542 static bool UnsafeDrvQuery(PG_CONN
*pConn
, const char *szQuery
, WCHAR
*errorText
)
547 PGresult
*pResult
= PQexec(pConn
->handle
, szQuery
);
551 if (errorText
!= NULL
)
552 wcsncpy(errorText
, L
"Internal error (pResult is NULL in UnsafeDrvQuery)", DBDRV_MAX_ERROR_TEXT
);
556 if (PQresultStatus(pResult
) != PGRES_COMMAND_OK
)
558 const char *sqlState
= PQresultErrorField(pResult
, PG_DIAG_SQLSTATE
);
559 if ((PQstatus(pConn
->handle
) != CONNECTION_BAD
) &&
560 (sqlState
!= NULL
) && (!strcmp(sqlState
, "53000") || !strcmp(sqlState
, "53200")) && (retryCount
> 0))
569 if (errorText
!= NULL
)
571 MultiByteToWideChar(CP_UTF8
, 0, CHECK_NULL_EX_A(sqlState
), -1, errorText
, DBDRV_MAX_ERROR_TEXT
);
572 int len
= (int)wcslen(errorText
);
575 errorText
[len
] = L
' ';
578 MultiByteToWideChar(CP_UTF8
, 0, PQerrorMessage(pConn
->handle
), -1, &errorText
[len
], DBDRV_MAX_ERROR_TEXT
- len
);
579 errorText
[DBDRV_MAX_ERROR_TEXT
- 1] = 0;
580 RemoveTrailingCRLFW(errorText
);
588 if (errorText
!= NULL
)
594 * Perform non-SELECT query
596 extern "C" DWORD EXPORT
DrvQuery(PG_CONN
*pConn
, WCHAR
*pwszQuery
, WCHAR
*errorText
)
600 char *pszQueryUTF8
= UTF8StringFromWideString(pwszQuery
);
601 MutexLock(pConn
->mutexQueryLock
);
602 if (UnsafeDrvQuery(pConn
, pszQueryUTF8
, errorText
))
604 dwRet
= DBERR_SUCCESS
;
608 dwRet
= (PQstatus(pConn
->handle
) == CONNECTION_BAD
) ? DBERR_CONNECTION_LOST
: DBERR_OTHER_ERROR
;
610 MutexUnlock(pConn
->mutexQueryLock
);
617 * Perform SELECT query - internal implementation
619 static DBDRV_RESULT
UnsafeDrvSelect(PG_CONN
*pConn
, const char *szQuery
, WCHAR
*errorText
)
624 PGresult
*pResult
= PQexec(((PG_CONN
*)pConn
)->handle
, szQuery
);
628 if (errorText
!= NULL
)
629 wcsncpy(errorText
, L
"Internal error (pResult is NULL in UnsafeDrvSelect)", DBDRV_MAX_ERROR_TEXT
);
633 if ((PQresultStatus(pResult
) != PGRES_COMMAND_OK
) &&
634 (PQresultStatus(pResult
) != PGRES_TUPLES_OK
))
636 const char *sqlState
= PQresultErrorField(pResult
, PG_DIAG_SQLSTATE
);
637 if ((PQstatus(pConn
->handle
) != CONNECTION_BAD
) &&
638 (sqlState
!= NULL
) && (!strcmp(sqlState
, "53000") || !strcmp(sqlState
, "53200")) && (retryCount
> 0))
647 if (errorText
!= NULL
)
649 MultiByteToWideChar(CP_UTF8
, 0, CHECK_NULL_EX_A(sqlState
), -1, errorText
, DBDRV_MAX_ERROR_TEXT
);
650 int len
= (int)wcslen(errorText
);
653 errorText
[len
] = L
' ';
656 MultiByteToWideChar(CP_UTF8
, 0, PQerrorMessage(pConn
->handle
), -1, &errorText
[len
], DBDRV_MAX_ERROR_TEXT
- len
);
657 errorText
[DBDRV_MAX_ERROR_TEXT
- 1] = 0;
658 RemoveTrailingCRLFW(errorText
);
665 if (errorText
!= NULL
)
667 return (DBDRV_RESULT
)pResult
;
671 * Perform SELECT query
673 extern "C" DBDRV_RESULT EXPORT
DrvSelect(PG_CONN
*pConn
, WCHAR
*pwszQuery
, DWORD
*pdwError
, WCHAR
*errorText
)
675 DBDRV_RESULT pResult
;
678 pszQueryUTF8
= UTF8StringFromWideString(pwszQuery
);
679 MutexLock(pConn
->mutexQueryLock
);
680 pResult
= UnsafeDrvSelect(pConn
, pszQueryUTF8
, errorText
);
683 *pdwError
= DBERR_SUCCESS
;
687 *pdwError
= (PQstatus(pConn
->handle
) == CONNECTION_BAD
) ? DBERR_CONNECTION_LOST
: DBERR_OTHER_ERROR
;
689 MutexUnlock(pConn
->mutexQueryLock
);
696 * Perform SELECT query using prepared statement
698 extern "C" DBDRV_RESULT EXPORT
DrvSelectPrepared(PG_CONN
*pConn
, PG_STATEMENT
*hStmt
, DWORD
*pdwError
, WCHAR
*errorText
)
700 PGresult
*pResult
= NULL
;
704 MutexLock(pConn
->mutexQueryLock
);
708 pResult
= PQexecPrepared(pConn
->handle
, hStmt
->name
, hStmt
->pcount
, hStmt
->buffers
, NULL
, NULL
, 0);
711 if ((PQresultStatus(pResult
) == PGRES_COMMAND_OK
) ||
712 (PQresultStatus(pResult
) == PGRES_TUPLES_OK
))
714 if (errorText
!= NULL
)
716 *pdwError
= DBERR_SUCCESS
;
720 const char *sqlState
= PQresultErrorField(pResult
, PG_DIAG_SQLSTATE
);
721 if ((PQstatus(pConn
->handle
) != CONNECTION_BAD
) &&
722 (sqlState
!= NULL
) && (!strcmp(sqlState
, "53000") || !strcmp(sqlState
, "53200")) && (retryCount
> 0))
730 if (errorText
!= NULL
)
732 MultiByteToWideChar(CP_UTF8
, 0, CHECK_NULL_EX_A(sqlState
), -1, errorText
, DBDRV_MAX_ERROR_TEXT
);
733 int len
= (int)wcslen(errorText
);
736 errorText
[len
] = L
' ';
739 MultiByteToWideChar(CP_UTF8
, 0, PQerrorMessage(pConn
->handle
), -1, &errorText
[len
], DBDRV_MAX_ERROR_TEXT
- len
);
740 errorText
[DBDRV_MAX_ERROR_TEXT
- 1] = 0;
741 RemoveTrailingCRLFW(errorText
);
746 *pdwError
= (PQstatus(pConn
->handle
) == CONNECTION_BAD
) ? DBERR_CONNECTION_LOST
: DBERR_OTHER_ERROR
;
751 if (errorText
!= NULL
)
752 wcsncpy(errorText
, L
"Internal error (pResult is NULL in UnsafeDrvSelect)", DBDRV_MAX_ERROR_TEXT
);
753 *pdwError
= (PQstatus(pConn
->handle
) == CONNECTION_BAD
) ? DBERR_CONNECTION_LOST
: DBERR_OTHER_ERROR
;
757 MutexUnlock(pConn
->mutexQueryLock
);
759 return (DBDRV_RESULT
)pResult
;
763 * Get field length from result
765 extern "C" LONG EXPORT
DrvGetFieldLength(DBDRV_RESULT pResult
, int nRow
, int nColumn
)
770 const char *value
= PQgetvalue((PGresult
*)pResult
, nRow
, nColumn
);
771 return (value
!= NULL
) ? (LONG
)strlen(value
) : (LONG
)-1;
775 * Get field value from result
777 extern "C" WCHAR EXPORT
*DrvGetField(DBDRV_RESULT pResult
, int nRow
, int nColumn
, WCHAR
*pBuffer
, int nBufLen
)
782 if (PQfformat((PGresult
*)pResult
, nColumn
) != 0)
785 const char *value
= PQgetvalue((PGresult
*)pResult
, nRow
, nColumn
);
789 MultiByteToWideChar(CP_UTF8
, 0, value
, -1, pBuffer
, nBufLen
);
790 pBuffer
[nBufLen
- 1] = 0;
795 * Get field value from result as UTF8 string
797 extern "C" char EXPORT
*DrvGetFieldUTF8(DBDRV_RESULT pResult
, int nRow
, int nColumn
, char *pBuffer
, int nBufLen
)
802 const char *value
= PQgetvalue((PGresult
*)pResult
, nRow
, nColumn
);
806 strncpy(pBuffer
, value
, nBufLen
);
807 pBuffer
[nBufLen
- 1] = 0;
812 * Get number of rows in result
814 extern "C" int EXPORT
DrvGetNumRows(DBDRV_RESULT pResult
)
816 return (pResult
!= NULL
) ? PQntuples((PGresult
*)pResult
) : 0;
820 * Get column count in query result
822 extern "C" int EXPORT
DrvGetColumnCount(DBDRV_RESULT hResult
)
824 return (hResult
!= NULL
) ? PQnfields((PGresult
*)hResult
) : 0;
828 * Get column name in query result
830 extern "C" const char EXPORT
*DrvGetColumnName(DBDRV_RESULT hResult
, int column
)
832 return (hResult
!= NULL
) ? PQfname((PGresult
*)hResult
, column
) : NULL
;
836 * Free SELECT results
838 extern "C" void EXPORT
DrvFreeResult(DBDRV_RESULT pResult
)
842 PQclear((PGresult
*)pResult
);
847 * Perform unbuffered SELECT query
849 extern "C" DBDRV_UNBUFFERED_RESULT EXPORT
DrvSelectUnbuffered(PG_CONN
*pConn
, WCHAR
*pwszQuery
, DWORD
*pdwError
, WCHAR
*errorText
)
854 PG_UNBUFFERED_RESULT
*result
= (PG_UNBUFFERED_RESULT
*)malloc(sizeof(PG_UNBUFFERED_RESULT
));
855 result
->conn
= pConn
;
856 result
->fetchBuffer
= NULL
;
857 result
->keepFetchBuffer
= true;
859 MutexLock(pConn
->mutexQueryLock
);
861 bool success
= false;
864 char *queryUTF8
= UTF8StringFromWideString(pwszQuery
);
868 if (PQsendQuery(pConn
->handle
, queryUTF8
))
871 if (PQsetSingleRowMode(pConn
->handle
))
873 result
->singleRowMode
= true;
875 if ((s_PQsetSingleRowMode
== NULL
) || s_PQsetSingleRowMode(pConn
->handle
))
877 result
->singleRowMode
= (s_PQsetSingleRowMode
!= NULL
);
881 // Fetch first row (to check for errors in Select instead of Fetch call)
882 result
->fetchBuffer
= PQgetResult(pConn
->handle
);
883 if ((PQresultStatus(result
->fetchBuffer
) == PGRES_COMMAND_OK
) ||
884 (PQresultStatus(result
->fetchBuffer
) == PGRES_TUPLES_OK
) ||
885 (PQresultStatus(result
->fetchBuffer
) == PGRES_SINGLE_TUPLE
))
887 if (errorText
!= NULL
)
889 *pdwError
= DBERR_SUCCESS
;
894 const char *sqlState
= PQresultErrorField(result
->fetchBuffer
, PG_DIAG_SQLSTATE
);
895 if ((PQstatus(pConn
->handle
) != CONNECTION_BAD
) &&
896 (sqlState
!= NULL
) && (!strcmp(sqlState
, "53000") || !strcmp(sqlState
, "53200")) && (retryCount
> 0))
904 if (errorText
!= NULL
)
906 MultiByteToWideChar(CP_UTF8
, 0, CHECK_NULL_EX_A(sqlState
), -1, errorText
, DBDRV_MAX_ERROR_TEXT
);
907 int len
= (int)wcslen(errorText
);
910 errorText
[len
] = L
' ';
913 MultiByteToWideChar(CP_UTF8
, 0, PQerrorMessage(pConn
->handle
), -1, &errorText
[len
], DBDRV_MAX_ERROR_TEXT
- len
);
914 errorText
[DBDRV_MAX_ERROR_TEXT
- 1] = 0;
915 RemoveTrailingCRLFW(errorText
);
918 PQclear(result
->fetchBuffer
);
919 result
->fetchBuffer
= NULL
;
920 *pdwError
= (PQstatus(pConn
->handle
) == CONNECTION_BAD
) ? DBERR_CONNECTION_LOST
: DBERR_OTHER_ERROR
;
925 if (errorText
!= NULL
)
926 wcsncpy(errorText
, L
"Internal error (call to PQsetSingleRowMode failed)", DBDRV_MAX_ERROR_TEXT
);
927 *pdwError
= (PQstatus(pConn
->handle
) == CONNECTION_BAD
) ? DBERR_CONNECTION_LOST
: DBERR_OTHER_ERROR
;
932 if (errorText
!= NULL
)
933 wcsncpy(errorText
, L
"Internal error (call to PQsendQuery failed)", DBDRV_MAX_ERROR_TEXT
);
934 *pdwError
= (PQstatus(pConn
->handle
) == CONNECTION_BAD
) ? DBERR_CONNECTION_LOST
: DBERR_OTHER_ERROR
;
945 return (DBDRV_UNBUFFERED_RESULT
)result
;
949 * Perform unbuffered SELECT query using prepared statement
951 extern "C" DBDRV_UNBUFFERED_RESULT EXPORT
DrvSelectPreparedUnbuffered(PG_CONN
*pConn
, PG_STATEMENT
*hStmt
, DWORD
*pdwError
, WCHAR
*errorText
)
956 PG_UNBUFFERED_RESULT
*result
= (PG_UNBUFFERED_RESULT
*)malloc(sizeof(PG_UNBUFFERED_RESULT
));
957 result
->conn
= pConn
;
958 result
->fetchBuffer
= NULL
;
959 result
->keepFetchBuffer
= true;
961 MutexLock(pConn
->mutexQueryLock
);
963 bool success
= false;
969 if (PQsendQueryPrepared(pConn
->handle
, hStmt
->name
, hStmt
->pcount
, hStmt
->buffers
, NULL
, NULL
, 0))
972 if (PQsetSingleRowMode(pConn
->handle
))
974 result
->singleRowMode
= true;
976 if ((s_PQsetSingleRowMode
== NULL
) || s_PQsetSingleRowMode(pConn
->handle
))
978 result
->singleRowMode
= (s_PQsetSingleRowMode
!= NULL
);
982 // Fetch first row (to check for errors in Select instead of Fetch call)
983 result
->fetchBuffer
= PQgetResult(pConn
->handle
);
984 if ((PQresultStatus(result
->fetchBuffer
) == PGRES_COMMAND_OK
) ||
985 (PQresultStatus(result
->fetchBuffer
) == PGRES_TUPLES_OK
) ||
986 (PQresultStatus(result
->fetchBuffer
) == PGRES_SINGLE_TUPLE
))
988 if (errorText
!= NULL
)
990 *pdwError
= DBERR_SUCCESS
;
995 const char *sqlState
= PQresultErrorField(result
->fetchBuffer
, PG_DIAG_SQLSTATE
);
996 if ((PQstatus(pConn
->handle
) != CONNECTION_BAD
) &&
997 (sqlState
!= NULL
) && (!strcmp(sqlState
, "53000") || !strcmp(sqlState
, "53200")) && (retryCount
> 0))
1005 if (errorText
!= NULL
)
1007 MultiByteToWideChar(CP_UTF8
, 0, CHECK_NULL_EX_A(sqlState
), -1, errorText
, DBDRV_MAX_ERROR_TEXT
);
1008 int len
= (int)wcslen(errorText
);
1011 errorText
[len
] = L
' ';
1014 MultiByteToWideChar(CP_UTF8
, 0, PQerrorMessage(pConn
->handle
), -1, &errorText
[len
], DBDRV_MAX_ERROR_TEXT
- len
);
1015 errorText
[DBDRV_MAX_ERROR_TEXT
- 1] = 0;
1016 RemoveTrailingCRLFW(errorText
);
1019 PQclear(result
->fetchBuffer
);
1020 result
->fetchBuffer
= NULL
;
1021 *pdwError
= (PQstatus(pConn
->handle
) == CONNECTION_BAD
) ? DBERR_CONNECTION_LOST
: DBERR_OTHER_ERROR
;
1026 if (errorText
!= NULL
)
1027 wcsncpy(errorText
, L
"Internal error (call to PQsetSingleRowMode failed)", DBDRV_MAX_ERROR_TEXT
);
1028 *pdwError
= (PQstatus(pConn
->handle
) == CONNECTION_BAD
) ? DBERR_CONNECTION_LOST
: DBERR_OTHER_ERROR
;
1033 if (errorText
!= NULL
)
1034 wcsncpy(errorText
, L
"Internal error (call to PQsendQueryPrepared failed)", DBDRV_MAX_ERROR_TEXT
);
1035 *pdwError
= (PQstatus(pConn
->handle
) == CONNECTION_BAD
) ? DBERR_CONNECTION_LOST
: DBERR_OTHER_ERROR
;
1045 return (DBDRV_UNBUFFERED_RESULT
)result
;
1049 * Fetch next result line from asynchronous SELECT results
1051 extern "C" bool EXPORT
DrvFetch(PG_UNBUFFERED_RESULT
*result
)
1056 if (!result
->keepFetchBuffer
)
1058 if (result
->singleRowMode
)
1060 if (result
->fetchBuffer
!= NULL
)
1061 PQclear(result
->fetchBuffer
);
1062 result
->fetchBuffer
= PQgetResult(result
->conn
->handle
);
1066 if (result
->fetchBuffer
!= NULL
)
1069 if (result
->currRow
>= PQntuples(result
->fetchBuffer
))
1071 PQclear(result
->fetchBuffer
);
1072 result
->fetchBuffer
= PQgetResult(result
->conn
->handle
);
1073 result
->currRow
= 0;
1078 result
->currRow
= 0;
1084 result
->keepFetchBuffer
= false;
1087 if (result
->fetchBuffer
== NULL
)
1091 if ((PQresultStatus(result
->fetchBuffer
) == PGRES_SINGLE_TUPLE
) || (PQresultStatus(result
->fetchBuffer
) == PGRES_TUPLES_OK
))
1093 success
= (PQntuples(result
->fetchBuffer
) > 0);
1097 PQclear(result
->fetchBuffer
);
1098 result
->fetchBuffer
= NULL
;
1106 * Get field length from async quety result
1108 extern "C" LONG EXPORT
DrvGetFieldLengthUnbuffered(PG_UNBUFFERED_RESULT
*result
, int nColumn
)
1110 if ((result
== NULL
) || (result
->fetchBuffer
== NULL
))
1113 // validate column index
1114 if (nColumn
>= PQnfields(result
->fetchBuffer
))
1117 char *value
= PQgetvalue(result
->fetchBuffer
, result
->currRow
, nColumn
);
1121 return (LONG
)strlen(value
);
1125 * Get field from current row in async query result
1127 extern "C" WCHAR EXPORT
*DrvGetFieldUnbuffered(PG_UNBUFFERED_RESULT
*result
, int nColumn
, WCHAR
*pBuffer
, int nBufSize
)
1129 if ((result
== NULL
) || (result
->fetchBuffer
== NULL
))
1132 // validate column index
1133 if (nColumn
>= PQnfields(result
->fetchBuffer
))
1136 if (PQfformat(result
->fetchBuffer
, nColumn
) != 0)
1139 char *value
= PQgetvalue(result
->fetchBuffer
, result
->currRow
, nColumn
);
1143 MultiByteToWideChar(CP_UTF8
, 0, value
, -1, pBuffer
, nBufSize
);
1144 pBuffer
[nBufSize
- 1] = 0;
1150 * Get field from current row in async query result as UTF-8 string
1152 extern "C" char EXPORT
*DrvGetFieldUnbufferedUTF8(PG_UNBUFFERED_RESULT
*result
, int nColumn
, char *pBuffer
, int nBufSize
)
1154 if ((result
== NULL
) || (result
->fetchBuffer
== NULL
))
1157 // validate column index
1158 if (nColumn
>= PQnfields(result
->fetchBuffer
))
1161 if (PQfformat(result
->fetchBuffer
, nColumn
) != 0)
1164 char *value
= PQgetvalue(result
->fetchBuffer
, result
->currRow
, nColumn
);
1168 strncpy(pBuffer
, value
, nBufSize
);
1169 pBuffer
[nBufSize
- 1] = 0;
1175 * Get column count in async query result
1177 extern "C" int EXPORT
DrvGetColumnCountUnbuffered(PG_UNBUFFERED_RESULT
*result
)
1179 return ((result
!= NULL
) && (result
->fetchBuffer
!= NULL
)) ? PQnfields(result
->fetchBuffer
) : 0;
1183 * Get column name in async query result
1185 extern "C" const char EXPORT
*DrvGetColumnNameUnbuffered(PG_UNBUFFERED_RESULT
*result
, int column
)
1187 return ((result
!= NULL
) && (result
->fetchBuffer
!= NULL
))? PQfname(result
->fetchBuffer
, column
) : NULL
;
1191 * Destroy result of async query
1193 extern "C" void EXPORT
DrvFreeUnbufferedResult(PG_UNBUFFERED_RESULT
*result
)
1198 if (result
->fetchBuffer
!= NULL
)
1199 PQclear(result
->fetchBuffer
);
1201 // read all outstanding results
1204 result
->fetchBuffer
= PQgetResult(result
->conn
->handle
);
1205 if (result
->fetchBuffer
== NULL
)
1207 PQclear(result
->fetchBuffer
);
1210 MutexUnlock(result
->conn
->mutexQueryLock
);
1217 extern "C" DWORD EXPORT
DrvBegin(PG_CONN
*pConn
)
1222 return DBERR_INVALID_HANDLE
;
1224 MutexLock(pConn
->mutexQueryLock
);
1225 if (UnsafeDrvQuery(pConn
, "BEGIN", NULL
))
1227 dwResult
= DBERR_SUCCESS
;
1231 dwResult
= (PQstatus(pConn
->handle
) == CONNECTION_BAD
) ? DBERR_CONNECTION_LOST
: DBERR_OTHER_ERROR
;
1233 MutexUnlock(pConn
->mutexQueryLock
);
1238 * Commit transaction
1240 extern "C" DWORD EXPORT
DrvCommit(PG_CONN
*pConn
)
1245 return DBERR_INVALID_HANDLE
;
1247 MutexLock(pConn
->mutexQueryLock
);
1248 bRet
= UnsafeDrvQuery(pConn
, "COMMIT", NULL
);
1249 MutexUnlock(pConn
->mutexQueryLock
);
1250 return bRet
? DBERR_SUCCESS
: DBERR_OTHER_ERROR
;
1254 * Rollback transaction
1256 extern "C" DWORD EXPORT
DrvRollback(PG_CONN
*pConn
)
1261 return DBERR_INVALID_HANDLE
;
1263 MutexLock(pConn
->mutexQueryLock
);
1264 bRet
= UnsafeDrvQuery(pConn
, "ROLLBACK", NULL
);
1265 MutexUnlock(pConn
->mutexQueryLock
);
1266 return bRet
? DBERR_SUCCESS
: DBERR_OTHER_ERROR
;
1270 * Check if table exist
1272 extern "C" int EXPORT
DrvIsTableExist(PG_CONN
*pConn
, const WCHAR
*name
)
1275 swprintf(query
, 256, L
"SELECT count(*) FROM information_schema.tables WHERE table_catalog=current_database() AND table_schema=current_schema() AND lower(table_name)=lower('%ls')", name
);
1277 WCHAR errorText
[DBDRV_MAX_ERROR_TEXT
];
1278 int rc
= DBIsTableExist_Failure
;
1279 DBDRV_RESULT hResult
= DrvSelect(pConn
, query
, &error
, errorText
);
1280 if (hResult
!= NULL
)
1282 WCHAR buffer
[64] = L
"";
1283 DrvGetField(hResult
, 0, 0, buffer
, 64);
1284 rc
= (wcstol(buffer
, NULL
, 10) > 0) ? DBIsTableExist_Found
: DBIsTableExist_NotFound
;
1285 DrvFreeResult(hResult
);
1295 bool WINAPI
DllMain(HINSTANCE hInstance
, DWORD dwReason
, LPVOID lpReserved
)
1297 if (dwReason
== DLL_PROCESS_ATTACH
)
1298 DisableThreadLibraryCalls(hInstance
);