Commit | Line | Data |
---|---|---|
5039dede AK |
1 | /* |
2 | ** PostgreSQL Database Driver | |
2df047f4 | 3 | ** Copyright (C) 2003 - 2016 Victor Kirhenshtein and Alex 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: pgsql.cpp | |
20 | ** | |
21 | **/ | |
22 | ||
23 | #include "pgsqldrv.h" | |
24 | ||
7e19eef6 VK |
25 | #ifndef _WIN32 |
26 | #include <dlfcn.h> | |
27 | #endif | |
28 | ||
d717b69c VK |
29 | #ifdef _WIN32 |
30 | #pragma warning(disable : 4996) | |
31 | #endif | |
32 | ||
17b35ccc | 33 | DECLARE_DRIVER_HEADER("PGSQL") |
5039dede | 34 | |
f3c30cf5 | 35 | extern "C" void EXPORT DrvDisconnect(DBDRV_CONNECTION pConn); |
750d59f2 | 36 | static bool UnsafeDrvQuery(PG_CONN *pConn, const char *szQuery, WCHAR *errorText); |
f3c30cf5 | 37 | |
7e19eef6 VK |
38 | #ifndef _WIN32 |
39 | static void *s_libpq = NULL; | |
40 | static int (*s_PQsetSingleRowMode)(PGconn *) = NULL; | |
41 | #endif | |
42 | ||
43 | #if !HAVE_DECL_PGRES_SINGLE_TUPLE | |
44 | #define PGRES_SINGLE_TUPLE 9 | |
45 | #endif | |
46 | ||
a8484a14 VK |
47 | /** |
48 | * Statement ID | |
49 | */ | |
50 | static VolatileCounter s_statementId = 0; | |
51 | ||
1c20f4d7 VK |
52 | /** |
53 | * Prepare string for using in SQL query - enclose in quotes and escape as needed | |
54 | */ | |
35f836fe | 55 | extern "C" WCHAR EXPORT *DrvPrepareStringW(const WCHAR *str) |
643c9dcb | 56 | { |
35f836fe | 57 | int len = (int)wcslen(str) + 3; // + two quotes and \0 at the end |
643c9dcb | 58 | int bufferSize = len + 128; |
35f836fe VK |
59 | WCHAR *out = (WCHAR *)malloc(bufferSize * sizeof(WCHAR)); |
60 | out[0] = L'\''; | |
643c9dcb | 61 | |
35f836fe | 62 | const WCHAR *src = str; |
643c9dcb | 63 | int outPos; |
17b35ccc | 64 | for(outPos = 1; *src != 0; src++) |
643c9dcb | 65 | { |
58a9b61f | 66 | UINT32 chval = *src; |
35f836fe VK |
67 | if (chval < 32) |
68 | { | |
69 | WCHAR buffer[8]; | |
70 | ||
17b35ccc | 71 | swprintf(buffer, 8, L"\\%03o", chval); |
35f836fe VK |
72 | len += 4; |
73 | if (len >= bufferSize) | |
74 | { | |
75 | bufferSize += 128; | |
76 | out = (WCHAR *)realloc(out, bufferSize * sizeof(WCHAR)); | |
77 | } | |
78 | memcpy(&out[outPos], buffer, 4 * sizeof(WCHAR)); | |
79 | outPos += 4; | |
80 | } | |
81 | else if (*src == L'\'') | |
82 | { | |
83 | len++; | |
84 | if (len >= bufferSize) | |
85 | { | |
86 | bufferSize += 128; | |
87 | out = (WCHAR *)realloc(out, bufferSize * sizeof(WCHAR)); | |
88 | } | |
89 | out[outPos++] = L'\''; | |
90 | out[outPos++] = L'\''; | |
91 | } | |
92 | else if (*src == L'\\') | |
93 | { | |
94 | len++; | |
95 | if (len >= bufferSize) | |
96 | { | |
97 | bufferSize += 128; | |
98 | out = (WCHAR *)realloc(out, bufferSize * sizeof(WCHAR)); | |
99 | } | |
100 | out[outPos++] = L'\\'; | |
101 | out[outPos++] = L'\\'; | |
102 | } | |
103 | else | |
104 | { | |
105 | out[outPos++] = *src; | |
106 | } | |
107 | } | |
108 | out[outPos++] = L'\''; | |
109 | out[outPos++] = 0; | |
110 | ||
111 | return out; | |
112 | } | |
113 | ||
1c20f4d7 VK |
114 | /** |
115 | * Prepare string for using in SQL query - enclose in quotes and escape as needed | |
116 | */ | |
35f836fe VK |
117 | extern "C" char EXPORT *DrvPrepareStringA(const char *str) |
118 | { | |
dda7c270 | 119 | int len = (int)strlen(str) + 3; // + two quotes and \0 at the end |
35f836fe VK |
120 | int bufferSize = len + 128; |
121 | char *out = (char *)malloc(bufferSize); | |
122 | out[0] = '\''; | |
123 | ||
124 | const char *src = str; | |
125 | int outPos; | |
17b35ccc | 126 | for(outPos = 1; *src != 0; src++) |
35f836fe | 127 | { |
58a9b61f | 128 | UINT32 chval = (UINT32)(*((unsigned char *)src)); |
b63cc959 | 129 | if (chval < 32) |
643c9dcb | 130 | { |
465b3f2d | 131 | char buffer[8]; |
643c9dcb | 132 | |
35f836fe | 133 | snprintf(buffer, 8, "\\%03o", chval); |
643c9dcb VK |
134 | len += 4; |
135 | if (len >= bufferSize) | |
136 | { | |
137 | bufferSize += 128; | |
35f836fe | 138 | out = (char *)realloc(out, bufferSize); |
643c9dcb | 139 | } |
35f836fe | 140 | memcpy(&out[outPos], buffer, 4); |
643c9dcb VK |
141 | outPos += 4; |
142 | } | |
35f836fe | 143 | else if (*src == '\'') |
643c9dcb VK |
144 | { |
145 | len++; | |
146 | if (len >= bufferSize) | |
147 | { | |
148 | bufferSize += 128; | |
35f836fe | 149 | out = (char *)realloc(out, bufferSize); |
643c9dcb | 150 | } |
35f836fe VK |
151 | out[outPos++] = '\''; |
152 | out[outPos++] = '\''; | |
643c9dcb | 153 | } |
35f836fe | 154 | else if (*src == '\\') |
643c9dcb VK |
155 | { |
156 | len++; | |
157 | if (len >= bufferSize) | |
158 | { | |
159 | bufferSize += 128; | |
35f836fe | 160 | out = (char *)realloc(out, bufferSize); |
643c9dcb | 161 | } |
35f836fe VK |
162 | out[outPos++] = '\\'; |
163 | out[outPos++] = '\\'; | |
643c9dcb VK |
164 | } |
165 | else | |
166 | { | |
167 | out[outPos++] = *src; | |
168 | } | |
169 | } | |
35f836fe | 170 | out[outPos++] = '\''; |
643c9dcb VK |
171 | out[outPos++] = 0; |
172 | ||
173 | return out; | |
174 | } | |
175 | ||
06492844 VK |
176 | /** |
177 | * Initialize driver | |
178 | */ | |
2df047f4 | 179 | extern "C" bool EXPORT DrvInit(const char *cmdLine) |
5039dede | 180 | { |
7e19eef6 VK |
181 | #ifndef _WIN32 |
182 | s_libpq = dlopen("libpq.so.5", RTLD_NOW); | |
183 | if (s_libpq != NULL) | |
184 | s_PQsetSingleRowMode = (int (*)(PGconn *))dlsym(s_libpq, "PQsetSingleRowMode"); | |
2df047f4 | 185 | nxlog_debug(2, _T("PostgreSQL driver: single row mode %s"), (s_PQsetSingleRowMode != NULL) ? _T("enabled") : _T("disabled")); |
7e19eef6 | 186 | #endif |
750d59f2 | 187 | return true; |
5039dede AK |
188 | } |
189 | ||
06492844 VK |
190 | /** |
191 | * Unload handler | |
192 | */ | |
08b214c6 | 193 | extern "C" void EXPORT DrvUnload() |
5039dede | 194 | { |
7e19eef6 VK |
195 | #ifndef _WIN32 |
196 | if (s_libpq != NULL) | |
197 | dlclose(s_libpq); | |
198 | #endif | |
5039dede AK |
199 | } |
200 | ||
06492844 VK |
201 | /** |
202 | * Connect to database | |
203 | */ | |
465b3f2d | 204 | extern "C" DBDRV_CONNECTION EXPORT DrvConnect(const char *szHost, const char *szLogin, const char *szPassword, |
f3c30cf5 | 205 | const char *szDatabase, const char *schema, WCHAR *errorText) |
5039dede | 206 | { |
7c2247b8 | 207 | PG_CONN *pConn; |
272c3799 | 208 | char *port = NULL; |
5039dede AK |
209 | |
210 | if (szDatabase == NULL || *szDatabase == 0) | |
211 | { | |
465b3f2d | 212 | wcscpy(errorText, L"Database name is empty"); |
5039dede AK |
213 | return NULL; |
214 | } | |
272c3799 | 215 | if((port = (char *)strchr(szHost, ':'))!=NULL) |
216 | { | |
217 | port[0]=0; | |
218 | port++; | |
219 | } | |
220 | ||
5039dede | 221 | pConn = (PG_CONN *)malloc(sizeof(PG_CONN)); |
272c3799 | 222 | |
5039dede AK |
223 | if (pConn != NULL) |
224 | { | |
225 | // should be replaced with PQconnectdb(); | |
f17cf019 | 226 | pConn->handle = PQsetdbLogin(szHost, port, NULL, NULL, szDatabase, szLogin, szPassword); |
5039dede | 227 | |
f17cf019 | 228 | if (PQstatus(pConn->handle) == CONNECTION_BAD) |
5039dede | 229 | { |
f17cf019 | 230 | MultiByteToWideChar(CP_UTF8, 0, PQerrorMessage(pConn->handle), -1, errorText, DBDRV_MAX_ERROR_TEXT); |
465b3f2d VK |
231 | errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0; |
232 | RemoveTrailingCRLFW(errorText); | |
f17cf019 | 233 | PQfinish(pConn->handle); |
5039dede AK |
234 | free(pConn); |
235 | pConn = NULL; | |
236 | } | |
237 | else | |
238 | { | |
643c9dcb VK |
239 | PGresult *pResult; |
240 | ||
f17cf019 | 241 | pResult = PQexec(pConn->handle, "SET standard_conforming_strings TO off"); |
643c9dcb VK |
242 | PQclear(pResult); |
243 | ||
f17cf019 | 244 | pResult = PQexec(pConn->handle, "SET escape_string_warning TO off"); |
643c9dcb VK |
245 | PQclear(pResult); |
246 | ||
f17cf019 | 247 | PQsetClientEncoding(pConn->handle, "UTF8"); |
17b055bb | 248 | |
5039dede | 249 | pConn->mutexQueryLock = MutexCreate(); |
f3c30cf5 VK |
250 | |
251 | if ((schema != NULL) && (schema[0] != 0)) | |
252 | { | |
253 | char query[256]; | |
254 | snprintf(query, 256, "SET search_path=%s", schema); | |
255 | if (!UnsafeDrvQuery(pConn, query, errorText)) | |
256 | { | |
257 | DrvDisconnect(pConn); | |
258 | pConn = NULL; | |
259 | } | |
260 | } | |
5039dede AK |
261 | } |
262 | } | |
465b3f2d VK |
263 | else |
264 | { | |
265 | wcscpy(errorText, L"Memory allocation error"); | |
266 | } | |
5039dede | 267 | |
b8c1ec69 | 268 | return (DBDRV_CONNECTION)pConn; |
5039dede AK |
269 | } |
270 | ||
06492844 VK |
271 | /** |
272 | * Disconnect from database | |
273 | */ | |
b8c1ec69 | 274 | extern "C" void EXPORT DrvDisconnect(DBDRV_CONNECTION pConn) |
5039dede AK |
275 | { |
276 | if (pConn != NULL) | |
277 | { | |
f17cf019 | 278 | PQfinish(((PG_CONN *)pConn)->handle); |
5039dede AK |
279 | MutexDestroy(((PG_CONN *)pConn)->mutexQueryLock); |
280 | free(pConn); | |
281 | } | |
282 | } | |
283 | ||
06492844 VK |
284 | /** |
285 | * Convert query from NetXMS portable format to native PostgreSQL format | |
286 | */ | |
3583a400 VK |
287 | static char *ConvertQuery(WCHAR *query) |
288 | { | |
289 | char *srcQuery = UTF8StringFromWideString(query); | |
290 | int count = NumCharsA(srcQuery, '?'); | |
291 | if (count == 0) | |
292 | return srcQuery; | |
293 | ||
987533db | 294 | char *dstQuery = (char *)malloc(strlen(srcQuery) + count * 3 + 1); |
3583a400 VK |
295 | bool inString = false; |
296 | int pos = 1; | |
297 | char *src, *dst; | |
298 | for(src = srcQuery, dst = dstQuery; *src != 0; src++) | |
299 | { | |
300 | switch(*src) | |
301 | { | |
302 | case '\'': | |
303 | *dst++ = *src; | |
304 | inString = !inString; | |
305 | break; | |
306 | case '\\': | |
307 | *dst++ = *src++; | |
308 | *dst++ = *src; | |
309 | break; | |
310 | case '?': | |
311 | if (inString) | |
312 | { | |
313 | *dst++ = '?'; | |
314 | } | |
315 | else | |
316 | { | |
317 | *dst++ = '$'; | |
318 | if (pos < 10) | |
319 | { | |
320 | *dst++ = pos + '0'; | |
321 | } | |
987533db | 322 | else if (pos < 100) |
3583a400 VK |
323 | { |
324 | *dst++ = pos / 10 + '0'; | |
325 | *dst++ = pos % 10 + '0'; | |
326 | } | |
987533db VK |
327 | else |
328 | { | |
329 | *dst++ = pos / 100 + '0'; | |
330 | *dst++ = (pos % 100) / 10 + '0'; | |
331 | *dst++ = pos % 10 + '0'; | |
332 | } | |
cd3cdf46 | 333 | pos++; |
3583a400 VK |
334 | } |
335 | break; | |
336 | default: | |
337 | *dst++ = *src; | |
338 | break; | |
339 | } | |
340 | } | |
341 | *dst = 0; | |
342 | free(srcQuery); | |
343 | return dstQuery; | |
344 | } | |
345 | ||
06492844 VK |
346 | /** |
347 | * Prepare statement | |
348 | */ | |
de1d708f | 349 | extern "C" DBDRV_STATEMENT EXPORT DrvPrepare(PG_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText) |
3583a400 | 350 | { |
3583a400 VK |
351 | char *pszQueryUTF8 = ConvertQuery(pwszQuery); |
352 | PG_STATEMENT *hStmt = (PG_STATEMENT *)malloc(sizeof(PG_STATEMENT)); | |
353 | hStmt->connection = pConn; | |
a8484a14 | 354 | snprintf(hStmt->name, 64, "netxms_stmt_%p_%d", hStmt, (int)InterlockedIncrement(&s_statementId)); |
3583a400 | 355 | |
c17f6cbc | 356 | MutexLock(pConn->mutexQueryLock); |
f17cf019 | 357 | PGresult *pResult = PQprepare(pConn->handle, hStmt->name, pszQueryUTF8, 0, NULL); |
3583a400 VK |
358 | if ((pResult == NULL) || (PQresultStatus(pResult) != PGRES_COMMAND_OK)) |
359 | { | |
360 | free(hStmt); | |
361 | hStmt = NULL; | |
fd4a9f34 | 362 | |
f17cf019 | 363 | *pdwError = (PQstatus(pConn->handle) == CONNECTION_BAD) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR; |
de1d708f | 364 | |
fd4a9f34 VK |
365 | if (errorText != NULL) |
366 | { | |
f17cf019 | 367 | MultiByteToWideChar(CP_UTF8, 0, PQerrorMessage(pConn->handle), -1, errorText, DBDRV_MAX_ERROR_TEXT); |
fd4a9f34 VK |
368 | errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0; |
369 | RemoveTrailingCRLFW(errorText); | |
370 | } | |
3583a400 | 371 | } |
fd4a9f34 VK |
372 | else |
373 | { | |
374 | hStmt->allocated = 0; | |
375 | hStmt->pcount = 0; | |
376 | hStmt->buffers = NULL; | |
de1d708f | 377 | *pdwError = DBERR_SUCCESS; |
fd4a9f34 VK |
378 | } |
379 | MutexUnlock(pConn->mutexQueryLock); | |
3583a400 VK |
380 | if (pResult != NULL) |
381 | PQclear(pResult); | |
382 | free(pszQueryUTF8); | |
383 | return hStmt; | |
384 | } | |
385 | ||
06492844 VK |
386 | /** |
387 | * Bind parameter to prepared statement | |
388 | */ | |
3583a400 VK |
389 | extern "C" void EXPORT DrvBind(PG_STATEMENT *hStmt, int pos, int sqlType, int cType, void *buffer, int allocType) |
390 | { | |
fd4a9f34 VK |
391 | if (pos <= 0) |
392 | return; | |
393 | ||
394 | if (hStmt->allocated < pos) | |
395 | { | |
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; | |
51683aba | 400 | hStmt->allocated = newAllocated; |
fd4a9f34 VK |
401 | } |
402 | if (hStmt->pcount < pos) | |
403 | hStmt->pcount = pos; | |
404 | ||
b8849590 | 405 | free(hStmt->buffers[pos - 1]); |
fd4a9f34 VK |
406 | |
407 | switch(cType) | |
408 | { | |
409 | case DB_CTYPE_STRING: | |
410 | hStmt->buffers[pos - 1] = UTF8StringFromWideString((WCHAR *)buffer); | |
411 | break; | |
b8849590 VK |
412 | case DB_CTYPE_UTF8_STRING: |
413 | if (allocType == DB_BIND_DYNAMIC) | |
414 | { | |
415 | hStmt->buffers[pos - 1] = (char *)buffer; | |
416 | buffer = NULL; // prevent deallocation | |
417 | } | |
418 | else | |
419 | { | |
420 | hStmt->buffers[pos - 1] = strdup((char *)buffer); | |
421 | } | |
422 | break; | |
fd4a9f34 VK |
423 | case DB_CTYPE_INT32: |
424 | hStmt->buffers[pos - 1] = (char *)malloc(16); | |
425 | sprintf(hStmt->buffers[pos - 1], "%d", *((int *)buffer)); | |
426 | break; | |
427 | case DB_CTYPE_UINT32: | |
428 | hStmt->buffers[pos - 1] = (char *)malloc(16); | |
429 | sprintf(hStmt->buffers[pos - 1], "%u", *((unsigned int *)buffer)); | |
430 | break; | |
431 | case DB_CTYPE_INT64: | |
432 | hStmt->buffers[pos - 1] = (char *)malloc(32); | |
dda7c270 | 433 | sprintf(hStmt->buffers[pos - 1], INT64_FMTA, *((INT64 *)buffer)); |
fd4a9f34 VK |
434 | break; |
435 | case DB_CTYPE_UINT64: | |
436 | hStmt->buffers[pos - 1] = (char *)malloc(32); | |
dda7c270 | 437 | sprintf(hStmt->buffers[pos - 1], UINT64_FMTA, *((QWORD *)buffer)); |
fd4a9f34 VK |
438 | break; |
439 | case DB_CTYPE_DOUBLE: | |
440 | hStmt->buffers[pos - 1] = (char *)malloc(32); | |
441 | sprintf(hStmt->buffers[pos - 1], "%f", *((double *)buffer)); | |
442 | break; | |
443 | default: | |
dda7c270 | 444 | hStmt->buffers[pos - 1] = strdup(""); |
fd4a9f34 VK |
445 | break; |
446 | } | |
447 | ||
448 | if (allocType == DB_BIND_DYNAMIC) | |
449 | free(buffer); | |
3583a400 VK |
450 | } |
451 | ||
06492844 VK |
452 | /** |
453 | * Execute prepared statement | |
454 | */ | |
3583a400 VK |
455 | extern "C" DWORD EXPORT DrvExecute(PG_CONN *pConn, PG_STATEMENT *hStmt, WCHAR *errorText) |
456 | { | |
fd4a9f34 | 457 | DWORD rc; |
3583a400 | 458 | |
c17f6cbc | 459 | MutexLock(pConn->mutexQueryLock); |
06492844 VK |
460 | bool retry; |
461 | int retryCount = 60; | |
462 | do | |
463 | { | |
464 | retry = false; | |
f17cf019 | 465 | PGresult *pResult = PQexecPrepared(pConn->handle, hStmt->name, hStmt->pcount, hStmt->buffers, NULL, NULL, 0); |
06492844 VK |
466 | if (pResult != NULL) |
467 | { | |
468 | if (PQresultStatus(pResult) == PGRES_COMMAND_OK) | |
469 | { | |
470 | if (errorText != NULL) | |
471 | *errorText = 0; | |
472 | rc = DBERR_SUCCESS; | |
473 | } | |
474 | else | |
475 | { | |
476 | const char *sqlState = PQresultErrorField(pResult, PG_DIAG_SQLSTATE); | |
f17cf019 | 477 | if ((PQstatus(pConn->handle) != CONNECTION_BAD) && |
7da71731 | 478 | (sqlState != NULL) && (!strcmp(sqlState, "53000") || !strcmp(sqlState, "53200")) && (retryCount > 0)) |
06492844 VK |
479 | { |
480 | ThreadSleep(500); | |
481 | retry = true; | |
482 | retryCount--; | |
483 | } | |
484 | else | |
485 | { | |
486 | if (errorText != NULL) | |
487 | { | |
7da71731 | 488 | MultiByteToWideChar(CP_UTF8, 0, CHECK_NULL_EX_A(sqlState), -1, errorText, DBDRV_MAX_ERROR_TEXT); |
06492844 VK |
489 | int len = (int)wcslen(errorText); |
490 | if (len > 0) | |
491 | { | |
492 | errorText[len] = L' '; | |
493 | len++; | |
494 | } | |
f17cf019 | 495 | MultiByteToWideChar(CP_UTF8, 0, PQerrorMessage(pConn->handle), -1, &errorText[len], DBDRV_MAX_ERROR_TEXT - len); |
06492844 VK |
496 | errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0; |
497 | RemoveTrailingCRLFW(errorText); | |
498 | } | |
f17cf019 | 499 | rc = (PQstatus(pConn->handle) == CONNECTION_BAD) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR; |
06492844 VK |
500 | } |
501 | } | |
502 | ||
503 | PQclear(pResult); | |
504 | } | |
505 | else | |
506 | { | |
507 | if (errorText != NULL) | |
508 | wcsncpy(errorText, L"Internal error (pResult is NULL in DrvExecute)", DBDRV_MAX_ERROR_TEXT); | |
509 | rc = DBERR_OTHER_ERROR; | |
510 | } | |
511 | } | |
512 | while(retry); | |
fd4a9f34 VK |
513 | MutexUnlock(pConn->mutexQueryLock); |
514 | return rc; | |
3583a400 VK |
515 | } |
516 | ||
06492844 VK |
517 | /** |
518 | * Destroy prepared statement | |
519 | */ | |
3583a400 VK |
520 | extern "C" void EXPORT DrvFreeStatement(PG_STATEMENT *hStmt) |
521 | { | |
522 | if (hStmt == NULL) | |
523 | return; | |
524 | ||
525 | char query[256]; | |
fd4a9f34 | 526 | snprintf(query, 256, "DEALLOCATE \"%s\"", hStmt->name); |
3583a400 | 527 | |
c17f6cbc | 528 | MutexLock(hStmt->connection->mutexQueryLock); |
3583a400 VK |
529 | UnsafeDrvQuery(hStmt->connection, query, NULL); |
530 | MutexUnlock(hStmt->connection->mutexQueryLock); | |
531 | ||
fd4a9f34 VK |
532 | for(int i = 0; i < hStmt->allocated; i++) |
533 | safe_free(hStmt->buffers[i]); | |
534 | safe_free(hStmt->buffers); | |
535 | ||
3583a400 VK |
536 | free(hStmt); |
537 | } | |
538 | ||
06492844 VK |
539 | /** |
540 | * Perform non-SELECT query - internal implementation | |
541 | */ | |
750d59f2 | 542 | static bool UnsafeDrvQuery(PG_CONN *pConn, const char *szQuery, WCHAR *errorText) |
5039dede | 543 | { |
06492844 VK |
544 | int retryCount = 60; |
545 | ||
546 | retry: | |
f17cf019 | 547 | PGresult *pResult = PQexec(pConn->handle, szQuery); |
5039dede AK |
548 | |
549 | if (pResult == NULL) | |
550 | { | |
551 | if (errorText != NULL) | |
35f836fe | 552 | wcsncpy(errorText, L"Internal error (pResult is NULL in UnsafeDrvQuery)", DBDRV_MAX_ERROR_TEXT); |
750d59f2 | 553 | return false; |
5039dede AK |
554 | } |
555 | ||
556 | if (PQresultStatus(pResult) != PGRES_COMMAND_OK) | |
557 | { | |
06492844 | 558 | const char *sqlState = PQresultErrorField(pResult, PG_DIAG_SQLSTATE); |
f17cf019 | 559 | if ((PQstatus(pConn->handle) != CONNECTION_BAD) && |
7da71731 | 560 | (sqlState != NULL) && (!strcmp(sqlState, "53000") || !strcmp(sqlState, "53200")) && (retryCount > 0)) |
06492844 VK |
561 | { |
562 | ThreadSleep(500); | |
563 | retryCount--; | |
564 | PQclear(pResult); | |
565 | goto retry; | |
566 | } | |
567 | else | |
568 | { | |
569 | if (errorText != NULL) | |
570 | { | |
7da71731 | 571 | MultiByteToWideChar(CP_UTF8, 0, CHECK_NULL_EX_A(sqlState), -1, errorText, DBDRV_MAX_ERROR_TEXT); |
06492844 VK |
572 | int len = (int)wcslen(errorText); |
573 | if (len > 0) | |
574 | { | |
575 | errorText[len] = L' '; | |
576 | len++; | |
577 | } | |
f17cf019 | 578 | MultiByteToWideChar(CP_UTF8, 0, PQerrorMessage(pConn->handle), -1, &errorText[len], DBDRV_MAX_ERROR_TEXT - len); |
06492844 VK |
579 | errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0; |
580 | RemoveTrailingCRLFW(errorText); | |
581 | } | |
582 | } | |
5039dede | 583 | PQclear(pResult); |
750d59f2 | 584 | return false; |
5039dede AK |
585 | } |
586 | ||
587 | PQclear(pResult); | |
588 | if (errorText != NULL) | |
589 | *errorText = 0; | |
750d59f2 | 590 | return true; |
5039dede AK |
591 | } |
592 | ||
06492844 VK |
593 | /** |
594 | * Perform non-SELECT query | |
595 | */ | |
35f836fe | 596 | extern "C" DWORD EXPORT DrvQuery(PG_CONN *pConn, WCHAR *pwszQuery, WCHAR *errorText) |
5039dede | 597 | { |
fd4a9f34 | 598 | DWORD dwRet; |
5039dede | 599 | |
fd4a9f34 | 600 | char *pszQueryUTF8 = UTF8StringFromWideString(pwszQuery); |
c17f6cbc | 601 | MutexLock(pConn->mutexQueryLock); |
fd4a9f34 VK |
602 | if (UnsafeDrvQuery(pConn, pszQueryUTF8, errorText)) |
603 | { | |
604 | dwRet = DBERR_SUCCESS; | |
605 | } | |
606 | else | |
607 | { | |
f17cf019 | 608 | dwRet = (PQstatus(pConn->handle) == CONNECTION_BAD) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR; |
fd4a9f34 VK |
609 | } |
610 | MutexUnlock(pConn->mutexQueryLock); | |
611 | free(pszQueryUTF8); | |
5039dede AK |
612 | |
613 | return dwRet; | |
614 | } | |
615 | ||
06492844 VK |
616 | /** |
617 | * Perform SELECT query - internal implementation | |
618 | */ | |
17b35ccc | 619 | static DBDRV_RESULT UnsafeDrvSelect(PG_CONN *pConn, const char *szQuery, WCHAR *errorText) |
5039dede | 620 | { |
06492844 VK |
621 | int retryCount = 60; |
622 | ||
623 | retry: | |
f17cf019 | 624 | PGresult *pResult = PQexec(((PG_CONN *)pConn)->handle, szQuery); |
5039dede AK |
625 | |
626 | if (pResult == NULL) | |
627 | { | |
628 | if (errorText != NULL) | |
35f836fe | 629 | wcsncpy(errorText, L"Internal error (pResult is NULL in UnsafeDrvSelect)", DBDRV_MAX_ERROR_TEXT); |
5039dede AK |
630 | return NULL; |
631 | } | |
632 | ||
633 | if ((PQresultStatus(pResult) != PGRES_COMMAND_OK) && | |
634 | (PQresultStatus(pResult) != PGRES_TUPLES_OK)) | |
06492844 VK |
635 | { |
636 | const char *sqlState = PQresultErrorField(pResult, PG_DIAG_SQLSTATE); | |
f17cf019 | 637 | if ((PQstatus(pConn->handle) != CONNECTION_BAD) && |
7da71731 | 638 | (sqlState != NULL) && (!strcmp(sqlState, "53000") || !strcmp(sqlState, "53200")) && (retryCount > 0)) |
06492844 VK |
639 | { |
640 | ThreadSleep(500); | |
641 | retryCount--; | |
642 | PQclear(pResult); | |
643 | goto retry; | |
644 | } | |
645 | else | |
646 | { | |
647 | if (errorText != NULL) | |
648 | { | |
7da71731 | 649 | MultiByteToWideChar(CP_UTF8, 0, CHECK_NULL_EX_A(sqlState), -1, errorText, DBDRV_MAX_ERROR_TEXT); |
06492844 VK |
650 | int len = (int)wcslen(errorText); |
651 | if (len > 0) | |
652 | { | |
653 | errorText[len] = L' '; | |
654 | len++; | |
655 | } | |
f17cf019 | 656 | MultiByteToWideChar(CP_UTF8, 0, PQerrorMessage(pConn->handle), -1, &errorText[len], DBDRV_MAX_ERROR_TEXT - len); |
06492844 VK |
657 | errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0; |
658 | RemoveTrailingCRLFW(errorText); | |
659 | } | |
660 | } | |
5039dede AK |
661 | PQclear(pResult); |
662 | return NULL; | |
663 | } | |
664 | ||
665 | if (errorText != NULL) | |
666 | *errorText = 0; | |
b8c1ec69 | 667 | return (DBDRV_RESULT)pResult; |
5039dede AK |
668 | } |
669 | ||
06492844 VK |
670 | /** |
671 | * Perform SELECT query | |
672 | */ | |
35f836fe | 673 | extern "C" DBDRV_RESULT EXPORT DrvSelect(PG_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText) |
5039dede | 674 | { |
b8c1ec69 | 675 | DBDRV_RESULT pResult; |
5039dede AK |
676 | char *pszQueryUTF8; |
677 | ||
5039dede | 678 | pszQueryUTF8 = UTF8StringFromWideString(pwszQuery); |
c17f6cbc | 679 | MutexLock(pConn->mutexQueryLock); |
5039dede AK |
680 | pResult = UnsafeDrvSelect(pConn, pszQueryUTF8, errorText); |
681 | if (pResult != NULL) | |
682 | { | |
683 | *pdwError = DBERR_SUCCESS; | |
684 | } | |
685 | else | |
686 | { | |
f17cf019 | 687 | *pdwError = (PQstatus(pConn->handle) == CONNECTION_BAD) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR; |
5039dede AK |
688 | } |
689 | MutexUnlock(pConn->mutexQueryLock); | |
690 | free(pszQueryUTF8); | |
691 | ||
692 | return pResult; | |
693 | } | |
694 | ||
06492844 VK |
695 | /** |
696 | * Perform SELECT query using prepared statement | |
697 | */ | |
fd4a9f34 VK |
698 | extern "C" DBDRV_RESULT EXPORT DrvSelectPrepared(PG_CONN *pConn, PG_STATEMENT *hStmt, DWORD *pdwError, WCHAR *errorText) |
699 | { | |
06492844 VK |
700 | PGresult *pResult = NULL; |
701 | bool retry; | |
702 | int retryCount = 60; | |
fd4a9f34 | 703 | |
06492844 VK |
704 | MutexLock(pConn->mutexQueryLock); |
705 | do | |
706 | { | |
707 | retry = false; | |
f17cf019 | 708 | pResult = PQexecPrepared(pConn->handle, hStmt->name, hStmt->pcount, hStmt->buffers, NULL, NULL, 0); |
06492844 VK |
709 | if (pResult != NULL) |
710 | { | |
711 | if ((PQresultStatus(pResult) == PGRES_COMMAND_OK) || | |
712 | (PQresultStatus(pResult) == PGRES_TUPLES_OK)) | |
713 | { | |
714 | if (errorText != NULL) | |
715 | *errorText = 0; | |
716 | *pdwError = DBERR_SUCCESS; | |
717 | } | |
718 | else | |
719 | { | |
720 | const char *sqlState = PQresultErrorField(pResult, PG_DIAG_SQLSTATE); | |
f17cf019 | 721 | if ((PQstatus(pConn->handle) != CONNECTION_BAD) && |
7da71731 | 722 | (sqlState != NULL) && (!strcmp(sqlState, "53000") || !strcmp(sqlState, "53200")) && (retryCount > 0)) |
06492844 VK |
723 | { |
724 | ThreadSleep(500); | |
725 | retry = true; | |
726 | retryCount--; | |
727 | } | |
728 | else | |
729 | { | |
730 | if (errorText != NULL) | |
731 | { | |
7da71731 | 732 | MultiByteToWideChar(CP_UTF8, 0, CHECK_NULL_EX_A(sqlState), -1, errorText, DBDRV_MAX_ERROR_TEXT); |
06492844 VK |
733 | int len = (int)wcslen(errorText); |
734 | if (len > 0) | |
735 | { | |
736 | errorText[len] = L' '; | |
737 | len++; | |
738 | } | |
f17cf019 | 739 | MultiByteToWideChar(CP_UTF8, 0, PQerrorMessage(pConn->handle), -1, &errorText[len], DBDRV_MAX_ERROR_TEXT - len); |
06492844 VK |
740 | errorText[DBDRV_MAX_ERROR_TEXT - 1] = 0; |
741 | RemoveTrailingCRLFW(errorText); | |
742 | } | |
743 | } | |
744 | PQclear(pResult); | |
745 | pResult = NULL; | |
f17cf019 | 746 | *pdwError = (PQstatus(pConn->handle) == CONNECTION_BAD) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR; |
06492844 VK |
747 | } |
748 | } | |
749 | else | |
750 | { | |
751 | if (errorText != NULL) | |
752 | wcsncpy(errorText, L"Internal error (pResult is NULL in UnsafeDrvSelect)", DBDRV_MAX_ERROR_TEXT); | |
f17cf019 | 753 | *pdwError = (PQstatus(pConn->handle) == CONNECTION_BAD) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR; |
06492844 VK |
754 | } |
755 | } | |
756 | while(retry); | |
fd4a9f34 VK |
757 | MutexUnlock(pConn->mutexQueryLock); |
758 | ||
759 | return (DBDRV_RESULT)pResult; | |
760 | } | |
761 | ||
07c9f3b0 VK |
762 | /** |
763 | * Get field length from result | |
764 | */ | |
b8c1ec69 | 765 | extern "C" LONG EXPORT DrvGetFieldLength(DBDRV_RESULT pResult, int nRow, int nColumn) |
5039dede | 766 | { |
5039dede AK |
767 | if (pResult == NULL) |
768 | return -1; | |
769 | ||
e6b49fd7 VK |
770 | const char *value = PQgetvalue((PGresult *)pResult, nRow, nColumn); |
771 | return (value != NULL) ? (LONG)strlen(value) : (LONG)-1; | |
5039dede AK |
772 | } |
773 | ||
07c9f3b0 VK |
774 | /** |
775 | * Get field value from result | |
776 | */ | |
777 | extern "C" WCHAR EXPORT *DrvGetField(DBDRV_RESULT pResult, int nRow, int nColumn, WCHAR *pBuffer, int nBufLen) | |
5039dede AK |
778 | { |
779 | if (pResult == NULL) | |
780 | return NULL; | |
781 | ||
8ca7cb8e VK |
782 | if (PQfformat((PGresult *)pResult, nColumn) != 0) |
783 | return NULL; | |
784 | ||
e6b49fd7 VK |
785 | const char *value = PQgetvalue((PGresult *)pResult, nRow, nColumn); |
786 | if (value == NULL) | |
787 | return NULL; | |
788 | ||
789 | MultiByteToWideChar(CP_UTF8, 0, value, -1, pBuffer, nBufLen); | |
43526096 VK |
790 | pBuffer[nBufLen - 1] = 0; |
791 | return pBuffer; | |
792 | } | |
793 | ||
794 | /** | |
795 | * Get field value from result as UTF8 string | |
796 | */ | |
797 | extern "C" char EXPORT *DrvGetFieldUTF8(DBDRV_RESULT pResult, int nRow, int nColumn, char *pBuffer, int nBufLen) | |
798 | { | |
799 | if (pResult == NULL) | |
800 | return NULL; | |
801 | ||
e6b49fd7 VK |
802 | const char *value = PQgetvalue((PGresult *)pResult, nRow, nColumn); |
803 | if (value == NULL) | |
804 | return NULL; | |
805 | ||
806 | strncpy(pBuffer, value, nBufLen); | |
5039dede AK |
807 | pBuffer[nBufLen - 1] = 0; |
808 | return pBuffer; | |
809 | } | |
810 | ||
07c9f3b0 VK |
811 | /** |
812 | * Get number of rows in result | |
813 | */ | |
b8c1ec69 | 814 | extern "C" int EXPORT DrvGetNumRows(DBDRV_RESULT pResult) |
5039dede | 815 | { |
00d17c5a | 816 | return (pResult != NULL) ? PQntuples((PGresult *)pResult) : 0; |
5039dede AK |
817 | } |
818 | ||
e6b49fd7 VK |
819 | /** |
820 | * Get column count in query result | |
821 | */ | |
b8c1ec69 | 822 | extern "C" int EXPORT DrvGetColumnCount(DBDRV_RESULT hResult) |
ea6f474a | 823 | { |
00d17c5a | 824 | return (hResult != NULL) ? PQnfields((PGresult *)hResult) : 0; |
ea6f474a VK |
825 | } |
826 | ||
e6b49fd7 VK |
827 | /** |
828 | * Get column name in query result | |
829 | */ | |
b8c1ec69 | 830 | extern "C" const char EXPORT *DrvGetColumnName(DBDRV_RESULT hResult, int column) |
ea6f474a | 831 | { |
00d17c5a | 832 | return (hResult != NULL) ? PQfname((PGresult *)hResult, column) : NULL; |
ea6f474a VK |
833 | } |
834 | ||
f17cf019 VK |
835 | /** |
836 | * Free SELECT results | |
837 | */ | |
b8c1ec69 | 838 | extern "C" void EXPORT DrvFreeResult(DBDRV_RESULT pResult) |
5039dede AK |
839 | { |
840 | if (pResult != NULL) | |
841 | { | |
842 | PQclear((PGresult *)pResult); | |
843 | } | |
844 | } | |
845 | ||
2e2ffa55 | 846 | /** |
f17cf019 | 847 | * Perform unbuffered SELECT query |
2e2ffa55 | 848 | */ |
f17cf019 | 849 | extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectUnbuffered(PG_CONN *pConn, WCHAR *pwszQuery, DWORD *pdwError, WCHAR *errorText) |
5039dede | 850 | { |
5039dede AK |
851 | if (pConn == NULL) |
852 | return NULL; | |
853 | ||
7e19eef6 VK |
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; | |
858 | ||
c17f6cbc | 859 | MutexLock(pConn->mutexQueryLock); |
5039dede | 860 | |
f17cf019 VK |
861 | bool success = false; |
862 | bool retry; | |
863 | int retryCount = 60; | |
864 | char *queryUTF8 = UTF8StringFromWideString(pwszQuery); | |
865 | do | |
5039dede | 866 | { |
f17cf019 VK |
867 | retry = false; |
868 | if (PQsendQuery(pConn->handle, queryUTF8)) | |
869 | { | |
8ca7cb8e VK |
870 | #ifdef _WIN32 |
871 | if (PQsetSingleRowMode(pConn->handle)) | |
872 | { | |
873 | result->singleRowMode = true; | |
874 | #else | |
7e19eef6 | 875 | if ((s_PQsetSingleRowMode == NULL) || s_PQsetSingleRowMode(pConn->handle)) |
f17cf019 | 876 | { |
7e19eef6 | 877 | result->singleRowMode = (s_PQsetSingleRowMode != NULL); |
8ca7cb8e | 878 | #endif |
7e19eef6 VK |
879 | result->currRow = 0; |
880 | ||
f17cf019 | 881 | // Fetch first row (to check for errors in Select instead of Fetch call) |
7e19eef6 VK |
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)) | |
f17cf019 VK |
886 | { |
887 | if (errorText != NULL) | |
888 | *errorText = 0; | |
889 | *pdwError = DBERR_SUCCESS; | |
890 | success = true; | |
891 | } | |
892 | else | |
893 | { | |
7e19eef6 | 894 | const char *sqlState = PQresultErrorField(result->fetchBuffer, PG_DIAG_SQLSTATE); |
f17cf019 VK |
895 | if ((PQstatus(pConn->handle) != CONNECTION_BAD) && |
896 | (sqlState != NULL) && (!strcmp(sqlState, "53000") || !strcmp(sqlState, "53200")) && (retryCount > 0)) | |
897 | { | |
898 | ThreadSleep(500); | |
899 | retry = true; | |
900 | retryCount--; | |
901 | } | |
902 | else | |
903 | { | |
904 | if (errorText != NULL) | |
905 | { | |
906 | MultiByteToWideChar(CP_UTF8, 0, CHECK_NULL_EX_A(sqlState), -1, errorText, DBDRV_MAX_ERROR_TEXT); | |
907 | int len = (int)wcslen(errorText); | |
908 | if (len > 0) | |
909 | { | |
910 | errorText[len] = L' '; | |
911 | len++; | |
912 | } | |
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); | |
916 | } | |
917 | } | |
7e19eef6 VK |
918 | PQclear(result->fetchBuffer); |
919 | result->fetchBuffer = NULL; | |
f17cf019 VK |
920 | *pdwError = (PQstatus(pConn->handle) == CONNECTION_BAD) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR; |
921 | } | |
922 | } | |
923 | else | |
924 | { | |
925 | if (errorText != NULL) | |
7e19eef6 | 926 | wcsncpy(errorText, L"Internal error (call to PQsetSingleRowMode failed)", DBDRV_MAX_ERROR_TEXT); |
f17cf019 VK |
927 | *pdwError = (PQstatus(pConn->handle) == CONNECTION_BAD) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR; |
928 | } | |
929 | } | |
7e19eef6 VK |
930 | else |
931 | { | |
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; | |
935 | } | |
5039dede | 936 | } |
f17cf019 | 937 | while(retry); |
0a1e33e5 | 938 | free(queryUTF8); |
7e19eef6 VK |
939 | |
940 | if (!success) | |
941 | { | |
942 | free(result); | |
943 | result = NULL; | |
944 | } | |
945 | return (DBDRV_UNBUFFERED_RESULT)result; | |
f17cf019 VK |
946 | } |
947 | ||
948 | /** | |
949 | * Perform unbuffered SELECT query using prepared statement | |
950 | */ | |
951 | extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectPreparedUnbuffered(PG_CONN *pConn, PG_STATEMENT *hStmt, DWORD *pdwError, WCHAR *errorText) | |
952 | { | |
953 | if (pConn == NULL) | |
954 | return NULL; | |
955 | ||
7e19eef6 VK |
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; | |
960 | ||
f17cf019 | 961 | MutexLock(pConn->mutexQueryLock); |
5039dede | 962 | |
f17cf019 VK |
963 | bool success = false; |
964 | bool retry; | |
965 | int retryCount = 60; | |
966 | do | |
5039dede | 967 | { |
f17cf019 VK |
968 | retry = false; |
969 | if (PQsendQueryPrepared(pConn->handle, hStmt->name, hStmt->pcount, hStmt->buffers, NULL, NULL, 0)) | |
970 | { | |
8ca7cb8e VK |
971 | #ifdef _WIN32 |
972 | if (PQsetSingleRowMode(pConn->handle)) | |
973 | { | |
974 | result->singleRowMode = true; | |
975 | #else | |
7e19eef6 | 976 | if ((s_PQsetSingleRowMode == NULL) || s_PQsetSingleRowMode(pConn->handle)) |
f17cf019 | 977 | { |
7e19eef6 | 978 | result->singleRowMode = (s_PQsetSingleRowMode != NULL); |
8ca7cb8e | 979 | #endif |
7e19eef6 VK |
980 | result->currRow = 0; |
981 | ||
f17cf019 | 982 | // Fetch first row (to check for errors in Select instead of Fetch call) |
7e19eef6 VK |
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)) | |
f17cf019 VK |
987 | { |
988 | if (errorText != NULL) | |
989 | *errorText = 0; | |
990 | *pdwError = DBERR_SUCCESS; | |
991 | success = true; | |
992 | } | |
993 | else | |
994 | { | |
7e19eef6 | 995 | const char *sqlState = PQresultErrorField(result->fetchBuffer, PG_DIAG_SQLSTATE); |
f17cf019 VK |
996 | if ((PQstatus(pConn->handle) != CONNECTION_BAD) && |
997 | (sqlState != NULL) && (!strcmp(sqlState, "53000") || !strcmp(sqlState, "53200")) && (retryCount > 0)) | |
998 | { | |
999 | ThreadSleep(500); | |
1000 | retry = true; | |
1001 | retryCount--; | |
1002 | } | |
1003 | else | |
1004 | { | |
1005 | if (errorText != NULL) | |
1006 | { | |
1007 | MultiByteToWideChar(CP_UTF8, 0, CHECK_NULL_EX_A(sqlState), -1, errorText, DBDRV_MAX_ERROR_TEXT); | |
1008 | int len = (int)wcslen(errorText); | |
1009 | if (len > 0) | |
1010 | { | |
1011 | errorText[len] = L' '; | |
1012 | len++; | |
1013 | } | |
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); | |
1017 | } | |
1018 | } | |
7e19eef6 VK |
1019 | PQclear(result->fetchBuffer); |
1020 | result->fetchBuffer = NULL; | |
f17cf019 VK |
1021 | *pdwError = (PQstatus(pConn->handle) == CONNECTION_BAD) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR; |
1022 | } | |
1023 | } | |
1024 | else | |
1025 | { | |
1026 | if (errorText != NULL) | |
7e19eef6 | 1027 | wcsncpy(errorText, L"Internal error (call to PQsetSingleRowMode failed)", DBDRV_MAX_ERROR_TEXT); |
f17cf019 VK |
1028 | *pdwError = (PQstatus(pConn->handle) == CONNECTION_BAD) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR; |
1029 | } | |
1030 | } | |
7e19eef6 VK |
1031 | else |
1032 | { | |
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; | |
1036 | } | |
5039dede | 1037 | } |
f17cf019 | 1038 | while(retry); |
7e19eef6 VK |
1039 | |
1040 | if (!success) | |
1041 | { | |
1042 | free(result); | |
1043 | result = NULL; | |
1044 | } | |
1045 | return (DBDRV_UNBUFFERED_RESULT)result; | |
5039dede AK |
1046 | } |
1047 | ||
2e2ffa55 VK |
1048 | /** |
1049 | * Fetch next result line from asynchronous SELECT results | |
1050 | */ | |
7e19eef6 | 1051 | extern "C" bool EXPORT DrvFetch(PG_UNBUFFERED_RESULT *result) |
5039dede | 1052 | { |
7e19eef6 | 1053 | if (result == NULL) |
f17cf019 VK |
1054 | return false; |
1055 | ||
7e19eef6 | 1056 | if (!result->keepFetchBuffer) |
5039dede | 1057 | { |
7e19eef6 VK |
1058 | if (result->singleRowMode) |
1059 | { | |
1060 | if (result->fetchBuffer != NULL) | |
1061 | PQclear(result->fetchBuffer); | |
1062 | result->fetchBuffer = PQgetResult(result->conn->handle); | |
1063 | } | |
1064 | else | |
1065 | { | |
1066 | if (result->fetchBuffer != NULL) | |
1067 | { | |
1068 | result->currRow++; | |
1069 | if (result->currRow >= PQntuples(result->fetchBuffer)) | |
1070 | { | |
1071 | PQclear(result->fetchBuffer); | |
1072 | result->fetchBuffer = PQgetResult(result->conn->handle); | |
1073 | result->currRow = 0; | |
1074 | } | |
1075 | } | |
1076 | else | |
1077 | { | |
1078 | result->currRow = 0; | |
1079 | } | |
1080 | } | |
5039dede AK |
1081 | } |
1082 | else | |
1083 | { | |
7e19eef6 | 1084 | result->keepFetchBuffer = false; |
f17cf019 VK |
1085 | } |
1086 | ||
7e19eef6 | 1087 | if (result->fetchBuffer == NULL) |
f17cf019 VK |
1088 | return false; |
1089 | ||
1090 | bool success; | |
7e19eef6 | 1091 | if ((PQresultStatus(result->fetchBuffer) == PGRES_SINGLE_TUPLE) || (PQresultStatus(result->fetchBuffer) == PGRES_TUPLES_OK)) |
f17cf019 | 1092 | { |
7e19eef6 | 1093 | success = (PQntuples(result->fetchBuffer) > 0); |
f17cf019 VK |
1094 | } |
1095 | else | |
1096 | { | |
7e19eef6 VK |
1097 | PQclear(result->fetchBuffer); |
1098 | result->fetchBuffer = NULL; | |
f17cf019 | 1099 | success = false; |
5039dede | 1100 | } |
f17cf019 VK |
1101 | |
1102 | return success; | |
5039dede AK |
1103 | } |
1104 | ||
2e2ffa55 VK |
1105 | /** |
1106 | * Get field length from async quety result | |
1107 | */ | |
7e19eef6 | 1108 | extern "C" LONG EXPORT DrvGetFieldLengthUnbuffered(PG_UNBUFFERED_RESULT *result, int nColumn) |
61f032f5 | 1109 | { |
7e19eef6 | 1110 | if ((result == NULL) || (result->fetchBuffer == NULL)) |
61f032f5 | 1111 | return 0; |
61f032f5 VK |
1112 | |
1113 | // validate column index | |
7e19eef6 | 1114 | if (nColumn >= PQnfields(result->fetchBuffer)) |
61f032f5 | 1115 | return 0; |
61f032f5 | 1116 | |
7e19eef6 | 1117 | char *value = PQgetvalue(result->fetchBuffer, result->currRow, nColumn); |
61f032f5 | 1118 | if (value == NULL) |
61f032f5 | 1119 | return 0; |
61f032f5 | 1120 | |
b260e88e | 1121 | return (LONG)strlen(value); |
61f032f5 VK |
1122 | } |
1123 | ||
2e2ffa55 VK |
1124 | /** |
1125 | * Get field from current row in async query result | |
1126 | */ | |
7e19eef6 | 1127 | extern "C" WCHAR EXPORT *DrvGetFieldUnbuffered(PG_UNBUFFERED_RESULT *result, int nColumn, WCHAR *pBuffer, int nBufSize) |
5039dede | 1128 | { |
7e19eef6 | 1129 | if ((result == NULL) || (result->fetchBuffer == NULL)) |
5039dede | 1130 | return NULL; |
5039dede AK |
1131 | |
1132 | // validate column index | |
7e19eef6 | 1133 | if (nColumn >= PQnfields(result->fetchBuffer)) |
5039dede | 1134 | return NULL; |
5039dede | 1135 | |
8ca7cb8e | 1136 | if (PQfformat(result->fetchBuffer, nColumn) != 0) |
5039dede | 1137 | return NULL; |
5039dede | 1138 | |
7e19eef6 VK |
1139 | char *value = PQgetvalue(result->fetchBuffer, result->currRow, nColumn); |
1140 | if (value == NULL) | |
5039dede | 1141 | return NULL; |
5039dede | 1142 | |
7e19eef6 | 1143 | MultiByteToWideChar(CP_UTF8, 0, value, -1, pBuffer, nBufSize); |
5039dede AK |
1144 | pBuffer[nBufSize - 1] = 0; |
1145 | ||
1146 | return pBuffer; | |
1147 | } | |
1148 | ||
7f8e3ccf VK |
1149 | /** |
1150 | * Get field from current row in async query result as UTF-8 string | |
1151 | */ | |
1152 | extern "C" char EXPORT *DrvGetFieldUnbufferedUTF8(PG_UNBUFFERED_RESULT *result, int nColumn, char *pBuffer, int nBufSize) | |
1153 | { | |
1154 | if ((result == NULL) || (result->fetchBuffer == NULL)) | |
1155 | return NULL; | |
1156 | ||
1157 | // validate column index | |
1158 | if (nColumn >= PQnfields(result->fetchBuffer)) | |
1159 | return NULL; | |
1160 | ||
1161 | if (PQfformat(result->fetchBuffer, nColumn) != 0) | |
1162 | return NULL; | |
1163 | ||
1164 | char *value = PQgetvalue(result->fetchBuffer, result->currRow, nColumn); | |
1165 | if (value == NULL) | |
1166 | return NULL; | |
1167 | ||
1168 | strncpy(pBuffer, value, nBufSize); | |
1169 | pBuffer[nBufSize - 1] = 0; | |
1170 | ||
1171 | return pBuffer; | |
1172 | } | |
1173 | ||
2e2ffa55 VK |
1174 | /** |
1175 | * Get column count in async query result | |
1176 | */ | |
7e19eef6 | 1177 | extern "C" int EXPORT DrvGetColumnCountUnbuffered(PG_UNBUFFERED_RESULT *result) |
ea6f474a | 1178 | { |
7e19eef6 | 1179 | return ((result != NULL) && (result->fetchBuffer != NULL)) ? PQnfields(result->fetchBuffer) : 0; |
ea6f474a VK |
1180 | } |
1181 | ||
926c5e72 VK |
1182 | /** |
1183 | * Get column name in async query result | |
1184 | */ | |
7e19eef6 | 1185 | extern "C" const char EXPORT *DrvGetColumnNameUnbuffered(PG_UNBUFFERED_RESULT *result, int column) |
ea6f474a | 1186 | { |
7e19eef6 | 1187 | return ((result != NULL) && (result->fetchBuffer != NULL))? PQfname(result->fetchBuffer, column) : NULL; |
ea6f474a VK |
1188 | } |
1189 | ||
926c5e72 VK |
1190 | /** |
1191 | * Destroy result of async query | |
1192 | */ | |
7e19eef6 | 1193 | extern "C" void EXPORT DrvFreeUnbufferedResult(PG_UNBUFFERED_RESULT *result) |
5039dede | 1194 | { |
7e19eef6 | 1195 | if (result == NULL) |
926c5e72 VK |
1196 | return; |
1197 | ||
7e19eef6 VK |
1198 | if (result->fetchBuffer != NULL) |
1199 | PQclear(result->fetchBuffer); | |
f17cf019 VK |
1200 | |
1201 | // read all outstanding results | |
7e19eef6 VK |
1202 | while(true) |
1203 | { | |
1204 | result->fetchBuffer = PQgetResult(result->conn->handle); | |
1205 | if (result->fetchBuffer == NULL) | |
1206 | break; | |
1207 | PQclear(result->fetchBuffer); | |
1208 | } | |
f17cf019 | 1209 | |
7e19eef6 VK |
1210 | MutexUnlock(result->conn->mutexQueryLock); |
1211 | free(result); | |
5039dede AK |
1212 | } |
1213 | ||
926c5e72 VK |
1214 | /** |
1215 | * Begin transaction | |
1216 | */ | |
5039dede AK |
1217 | extern "C" DWORD EXPORT DrvBegin(PG_CONN *pConn) |
1218 | { | |
1219 | DWORD dwResult; | |
1220 | ||
1221 | if (pConn == NULL) | |
1222 | return DBERR_INVALID_HANDLE; | |
1223 | ||
c17f6cbc | 1224 | MutexLock(pConn->mutexQueryLock); |
5039dede AK |
1225 | if (UnsafeDrvQuery(pConn, "BEGIN", NULL)) |
1226 | { | |
1227 | dwResult = DBERR_SUCCESS; | |
1228 | } | |
1229 | else | |
1230 | { | |
f17cf019 | 1231 | dwResult = (PQstatus(pConn->handle) == CONNECTION_BAD) ? DBERR_CONNECTION_LOST : DBERR_OTHER_ERROR; |
5039dede AK |
1232 | } |
1233 | MutexUnlock(pConn->mutexQueryLock); | |
1234 | return dwResult; | |
1235 | } | |
1236 | ||
f17cf019 VK |
1237 | /** |
1238 | * Commit transaction | |
1239 | */ | |
5039dede AK |
1240 | extern "C" DWORD EXPORT DrvCommit(PG_CONN *pConn) |
1241 | { | |
750d59f2 | 1242 | bool bRet; |
5039dede AK |
1243 | |
1244 | if (pConn == NULL) | |
1245 | return DBERR_INVALID_HANDLE; | |
1246 | ||
c17f6cbc | 1247 | MutexLock(pConn->mutexQueryLock); |
5039dede AK |
1248 | bRet = UnsafeDrvQuery(pConn, "COMMIT", NULL); |
1249 | MutexUnlock(pConn->mutexQueryLock); | |
1250 | return bRet ? DBERR_SUCCESS : DBERR_OTHER_ERROR; | |
1251 | } | |
1252 | ||
b4805ef1 VK |
1253 | /** |
1254 | * Rollback transaction | |
1255 | */ | |
5039dede AK |
1256 | extern "C" DWORD EXPORT DrvRollback(PG_CONN *pConn) |
1257 | { | |
750d59f2 | 1258 | bool bRet; |
5039dede AK |
1259 | |
1260 | if (pConn == NULL) | |
1261 | return DBERR_INVALID_HANDLE; | |
1262 | ||
c17f6cbc | 1263 | MutexLock(pConn->mutexQueryLock); |
5039dede AK |
1264 | bRet = UnsafeDrvQuery(pConn, "ROLLBACK", NULL); |
1265 | MutexUnlock(pConn->mutexQueryLock); | |
1266 | return bRet ? DBERR_SUCCESS : DBERR_OTHER_ERROR; | |
1267 | } | |
1268 | ||
b4805ef1 VK |
1269 | /** |
1270 | * Check if table exist | |
1271 | */ | |
1272 | extern "C" int EXPORT DrvIsTableExist(PG_CONN *pConn, const WCHAR *name) | |
1273 | { | |
1274 | WCHAR query[256]; | |
4b3ca4bb | 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); |
b4805ef1 VK |
1276 | DWORD error; |
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) | |
1281 | { | |
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); | |
1286 | } | |
1287 | return rc; | |
1288 | } | |
5039dede AK |
1289 | |
1290 | #ifdef _WIN32 | |
1291 | ||
b4805ef1 VK |
1292 | /** |
1293 | * DLL Entry point | |
1294 | */ | |
750d59f2 | 1295 | bool WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) |
5039dede AK |
1296 | { |
1297 | if (dwReason == DLL_PROCESS_ATTACH) | |
1298 | DisableThreadLibraryCalls(hInstance); | |
750d59f2 | 1299 | return true; |
5039dede AK |
1300 | } |
1301 | ||
1302 | #endif |