Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements OGRPGLayer class which implements shared handling
5 : * of feature geometry and so forth needed by OGRPGResultLayer and
6 : * OGRPGTableLayer.
7 : * Author: Frank Warmerdam, warmerdam@pobox.com
8 : *
9 : ******************************************************************************
10 : * Copyright (c) 2000, Frank Warmerdam
11 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
12 : *
13 : * SPDX-License-Identifier: MIT
14 : ****************************************************************************/
15 :
16 : /* Some functions have been extracted from PostgreSQL code base */
17 : /* The applicable copyright & licence notice is the following one : */
18 : /*
19 : PostgreSQL Database Management System
20 : (formerly known as Postgres, then as Postgres95)
21 :
22 : Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
23 :
24 : Portions Copyright (c) 1994, The Regents of the University of California
25 :
26 : Permission to use, copy, modify, and distribute this software and its
27 : documentation for any purpose, without fee, and without a written agreement
28 : is hereby granted, provided that the above copyright notice and this
29 : paragraph and the following two paragraphs appear in all copies.
30 :
31 : IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
32 : DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
33 : LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
34 : DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
35 : POSSIBILITY OF SUCH DAMAGE.
36 :
37 : THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
38 : INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
39 : AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
40 : ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
41 : PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
42 : */
43 :
44 : #include "ogr_pg.h"
45 : #include "ogr_p.h"
46 : #include "cpl_conv.h"
47 : #include "cpl_string.h"
48 :
49 : #include <limits>
50 :
51 : #define PQexec this_is_an_error
52 :
53 : // These originally are defined in libpq-fs.h.
54 :
55 : #ifndef INV_WRITE
56 : #define INV_WRITE 0x00020000
57 : #define INV_READ 0x00040000
58 : #endif
59 :
60 : /************************************************************************/
61 : /* OGRPGLayer() */
62 : /************************************************************************/
63 :
64 954 : OGRPGLayer::OGRPGLayer()
65 954 : : nCursorPage(atoi(CPLGetConfigOption("OGR_PG_CURSOR_PAGE", "500")))
66 : {
67 954 : pszCursorName = CPLStrdup(CPLSPrintf("OGRPGLayerReader%p", this));
68 954 : }
69 :
70 : /************************************************************************/
71 : /* ~OGRPGLayer() */
72 : /************************************************************************/
73 :
74 954 : OGRPGLayer::~OGRPGLayer()
75 :
76 : {
77 954 : if (m_nFeaturesRead > 0 && poFeatureDefn != nullptr)
78 : {
79 299 : CPLDebug("PG", CPL_FRMT_GIB " features read on layer '%s'.",
80 299 : m_nFeaturesRead, poFeatureDefn->GetName());
81 : }
82 :
83 954 : CloseCursor();
84 :
85 954 : CPLFree(pszFIDColumn);
86 954 : CPLFree(pszQueryStatement);
87 954 : CPLFree(m_panMapFieldNameToIndex);
88 954 : CPLFree(m_panMapFieldNameToGeomIndex);
89 954 : CPLFree(pszCursorName);
90 :
91 954 : if (poFeatureDefn)
92 : {
93 954 : poFeatureDefn->UnsetLayer();
94 954 : poFeatureDefn->Release();
95 : }
96 954 : }
97 :
98 : /************************************************************************/
99 : /* CloseCursor() */
100 : /************************************************************************/
101 :
102 2359 : void OGRPGLayer::CloseCursor()
103 : {
104 2359 : PGconn *hPGConn = poDS->GetPGConn();
105 :
106 2359 : if (hCursorResult != nullptr)
107 : {
108 515 : OGRPGClearResult(hCursorResult);
109 :
110 515 : CPLString osCommand;
111 515 : osCommand.Printf("CLOSE %s", pszCursorName);
112 :
113 : /* In case of interleaving read in different layers we might have */
114 : /* close the transaction, and thus implicitly the cursor, so be */
115 : /* quiet about errors. This is potentially an issue by the way */
116 515 : hCursorResult = OGRPG_PQexec(hPGConn, osCommand.c_str(), FALSE, TRUE);
117 515 : OGRPGClearResult(hCursorResult);
118 :
119 515 : poDS->SoftCommitTransaction();
120 :
121 515 : hCursorResult = nullptr;
122 : }
123 2359 : }
124 :
125 : /************************************************************************/
126 : /* InvalidateCursor() */
127 : /************************************************************************/
128 :
129 5 : void OGRPGLayer::InvalidateCursor()
130 : {
131 5 : CloseCursor();
132 5 : bInvalidated = TRUE;
133 5 : }
134 :
135 : /************************************************************************/
136 : /* ResetReading() */
137 : /************************************************************************/
138 :
139 1184 : void OGRPGLayer::ResetReading()
140 :
141 : {
142 1184 : GetLayerDefn();
143 :
144 1184 : iNextShapeId = 0;
145 :
146 1184 : CloseCursor();
147 1184 : bInvalidated = FALSE;
148 1184 : }
149 :
150 : #if defined(BINARY_CURSOR_ENABLED)
151 : /************************************************************************/
152 : /* OGRPGGetStrFromBinaryNumeric() */
153 : /************************************************************************/
154 :
155 : /* Adaptation of get_str_from_var() from pgsql/src/backend/utils/adt/numeric.c
156 : */
157 :
158 : typedef short NumericDigit;
159 :
160 : typedef struct NumericVar
161 : {
162 : int ndigits; /* # of digits in digits[] - can be 0! */
163 : int weight; /* weight of first digit */
164 : int sign; /* NUMERIC_POS, NUMERIC_NEG, or NUMERIC_NAN */
165 : int dscale; /* display scale */
166 : NumericDigit *digits; /* base-NBASE digits */
167 : } NumericVar;
168 :
169 : #define NUMERIC_POS 0x0000
170 : #define NUMERIC_NEG 0x4000
171 : #define NUMERIC_NAN 0xC000
172 :
173 : #define DEC_DIGITS 4
174 :
175 : /*
176 : * get_str_from_var() -
177 : *
178 : * Convert a var to text representation (guts of numeric_out).
179 : * CAUTION: var's contents may be modified by rounding!
180 : * Returns a malloc'd string.
181 : */
182 : static char *OGRPGGetStrFromBinaryNumeric(NumericVar *var)
183 : {
184 : const int dscale = var->dscale;
185 :
186 : /*
187 : * Allocate space for the result.
188 : *
189 : * i is set to to # of decimal digits before decimal point. dscale is the
190 : * # of decimal digits we will print after decimal point. We may generate
191 : * as many as DEC_DIGITS-1 excess digits at the end, and in addition we
192 : * need room for sign, decimal point, null terminator.
193 : */
194 : int i = (var->weight + 1) * DEC_DIGITS;
195 : if (i <= 0)
196 : i = 1;
197 :
198 : char *str = (char *)CPLMalloc(i + dscale + DEC_DIGITS + 2);
199 : char *cp = str;
200 :
201 : /*
202 : * Output a dash for negative values
203 : */
204 : if (var->sign == NUMERIC_NEG)
205 : *cp++ = '-';
206 :
207 : /*
208 : * Output all digits before the decimal point
209 : */
210 : int d = 0;
211 : if (var->weight < 0)
212 : {
213 : d = var->weight + 1;
214 : *cp++ = '0';
215 : }
216 : else
217 : {
218 : for (d = 0; d <= var->weight; d++)
219 : {
220 : NumericDigit dig = (d < var->ndigits) ? var->digits[d] : 0;
221 : CPL_MSBPTR16(&dig);
222 : // In the first digit, suppress extra leading decimal zeroes.
223 : {
224 : bool putit = (d > 0);
225 :
226 : NumericDigit d1;
227 : d1 = dig / 1000;
228 : dig -= d1 * 1000;
229 : putit |= (d1 > 0);
230 : if (putit)
231 : *cp++ = (char)(d1 + '0');
232 : d1 = dig / 100;
233 : dig -= d1 * 100;
234 : putit |= (d1 > 0);
235 : if (putit)
236 : *cp++ = (char)(d1 + '0');
237 : d1 = dig / 10;
238 : dig -= d1 * 10;
239 : putit |= (d1 > 0);
240 : if (putit)
241 : *cp++ = (char)(d1 + '0');
242 : *cp++ = (char)(dig + '0');
243 : }
244 : }
245 : }
246 :
247 : /*
248 : * If requested, output a decimal point and all the digits that follow it.
249 : * We initially put out a multiple of DEC_DIGITS digits, then truncate if
250 : * needed.
251 : */
252 : if (dscale > 0)
253 : {
254 : *cp++ = '.';
255 : char *endcp = cp + dscale;
256 : for (i = 0; i < dscale; d++, i += DEC_DIGITS)
257 : {
258 : NumericDigit dig =
259 : (d >= 0 && d < var->ndigits) ? var->digits[d] : 0;
260 : CPL_MSBPTR16(&dig);
261 : NumericDigit d1 = dig / 1000;
262 : dig -= d1 * 1000;
263 : *cp++ = (char)(d1 + '0');
264 : d1 = dig / 100;
265 : dig -= d1 * 100;
266 : *cp++ = (char)(d1 + '0');
267 : d1 = dig / 10;
268 : dig -= d1 * 10;
269 : *cp++ = (char)(d1 + '0');
270 : *cp++ = (char)(dig + '0');
271 : }
272 : cp = endcp;
273 : }
274 :
275 : /*
276 : * terminate the string and return it
277 : */
278 : *cp = '\0';
279 : return str;
280 : }
281 :
282 : /************************************************************************/
283 : /* OGRPGj2date() */
284 : /************************************************************************/
285 :
286 : /* Coming from j2date() in pgsql/src/backend/utils/adt/datetime.c */
287 :
288 : #define POSTGRES_EPOCH_JDATE 2451545 /* == date2j(2000, 1, 1) */
289 :
290 : static void OGRPGj2date(int jd, int *year, int *month, int *day)
291 : {
292 : unsigned int julian = jd + 32044;
293 : unsigned int quad = julian / 146097;
294 : const unsigned int extra = (julian - quad * 146097) * 4 + 3;
295 : julian += 60 + quad * 3 + extra / 146097;
296 : quad = julian / 1461;
297 : julian -= quad * 1461;
298 : int y = julian * 4 / 1461;
299 : julian = ((y != 0) ? ((julian + 305) % 365) : ((julian + 306) % 366)) + 123;
300 : y += quad * 4;
301 : *year = y - 4800;
302 : quad = julian * 2141 / 65536;
303 : *day = julian - 7834 * quad / 256;
304 : *month = (quad + 10) % 12 + 1;
305 :
306 : return;
307 : } /* j2date() */
308 :
309 : /************************************************************************/
310 : /* OGRPGdt2time() */
311 : /************************************************************************/
312 :
313 : #define USECS_PER_SEC 1000000
314 : #define USECS_PER_MIN ((GIntBig)60 * USECS_PER_SEC)
315 : #define USECS_PER_HOUR ((GIntBig)3600 * USECS_PER_SEC)
316 : #define USECS_PER_DAY ((GIntBig)3600 * 24 * USECS_PER_SEC)
317 :
318 : /* Coming from dt2time() in pgsql/src/backend/utils/adt/timestamp.c */
319 :
320 : static void OGRPGdt2timeInt8(GIntBig jd, int *hour, int *min, int *sec,
321 : double *fsec)
322 : {
323 : GIntBig time = jd;
324 :
325 : *hour = (int)(time / USECS_PER_HOUR);
326 : time -= (GIntBig)(*hour) * USECS_PER_HOUR;
327 : *min = (int)(time / USECS_PER_MIN);
328 : time -= (GIntBig)(*min) * USECS_PER_MIN;
329 : *sec = (int)time / USECS_PER_SEC;
330 : *fsec = (double)(time - *sec * USECS_PER_SEC);
331 : } /* dt2time() */
332 :
333 : static void OGRPGdt2timeFloat8(double jd, int *hour, int *min, int *sec,
334 : double *fsec)
335 : {
336 : double time = jd;
337 :
338 : *hour = (int)(time / 3600.);
339 : time -= (*hour) * 3600.;
340 : *min = (int)(time / 60.);
341 : time -= (*min) * 60.;
342 : *sec = (int)time;
343 : *fsec = time - *sec;
344 : }
345 :
346 : /************************************************************************/
347 : /* OGRPGTimeStamp2DMYHMS() */
348 : /************************************************************************/
349 :
350 : #define TMODULO(t, q, u) \
351 : do \
352 : { \
353 : (q) = ((t) / (u)); \
354 : if ((q) != 0) \
355 : (t) -= ((q) * (u)); \
356 : } while (false)
357 :
358 : /* Coming from timestamp2tm() in pgsql/src/backend/utils/adt/timestamp.c */
359 :
360 : static int OGRPGTimeStamp2DMYHMS(GIntBig dt, int *year, int *month, int *day,
361 : int *hour, int *min, double *pdfSec)
362 : {
363 : GIntBig time = dt;
364 : GIntBig date = 0;
365 : TMODULO(time, date, USECS_PER_DAY);
366 :
367 : if (time < 0)
368 : {
369 : time += USECS_PER_DAY;
370 : date -= 1;
371 : }
372 :
373 : /* add offset to go from J2000 back to standard Julian date */
374 : date += POSTGRES_EPOCH_JDATE;
375 :
376 : /* Julian day routine does not work for negative Julian days */
377 : if (date < 0 || date > (double)INT_MAX)
378 : return -1;
379 :
380 : OGRPGj2date((int)date, year, month, day);
381 : int nSec = 0;
382 : double dfSec = 0.0;
383 : OGRPGdt2timeInt8(time, hour, min, &nSec, &dfSec);
384 : *pdfSec += nSec + dfSec;
385 :
386 : return 0;
387 : }
388 :
389 : #endif // defined(BINARY_CURSOR_ENABLED)
390 :
391 : /************************************************************************/
392 : /* TokenizeStringListFromText() */
393 : /* */
394 : /* Tokenize a varchar[] returned as a text */
395 : /************************************************************************/
396 :
397 72 : static void OGRPGTokenizeStringListUnescapeToken(char *pszToken)
398 : {
399 72 : if (EQUAL(pszToken, "NULL"))
400 : {
401 0 : pszToken[0] = '\0';
402 0 : return;
403 : }
404 :
405 72 : int iSrc = 0, iDst = 0;
406 192 : for (iSrc = 0; pszToken[iSrc] != '\0'; iSrc++)
407 : {
408 120 : pszToken[iDst] = pszToken[iSrc];
409 120 : if (pszToken[iSrc] != '\\')
410 120 : iDst++;
411 : }
412 72 : pszToken[iDst] = '\0';
413 : }
414 :
415 : /* {"a\",b",d,NULL,e} should be tokenized into 3 pieces : a",b d
416 : * empty_string e */
417 36 : static char **OGRPGTokenizeStringListFromText(const char *pszText)
418 : {
419 36 : char **papszTokens = nullptr;
420 36 : const char *pszCur = strchr(pszText, '{');
421 36 : if (pszCur == nullptr)
422 : {
423 0 : CPLError(CE_Warning, CPLE_AppDefined, "Incorrect string list : %s",
424 : pszText);
425 0 : return papszTokens;
426 : }
427 :
428 36 : const char *pszNewTokenStart = nullptr;
429 36 : int bInDoubleQuotes = FALSE;
430 36 : pszCur++;
431 192 : while (*pszCur)
432 : {
433 192 : if (*pszCur == '\\')
434 : {
435 0 : pszCur++;
436 0 : if (*pszCur == 0)
437 0 : break;
438 0 : pszCur++;
439 0 : continue;
440 : }
441 :
442 192 : if (*pszCur == '"')
443 : {
444 0 : bInDoubleQuotes = !bInDoubleQuotes;
445 0 : if (bInDoubleQuotes)
446 0 : pszNewTokenStart = pszCur + 1;
447 : else
448 : {
449 0 : if (pszCur[1] == ',' || pszCur[1] == '}')
450 : {
451 0 : if (pszNewTokenStart != nullptr &&
452 : pszCur > pszNewTokenStart)
453 : {
454 : char *pszNewToken = static_cast<char *>(
455 0 : CPLMalloc(pszCur - pszNewTokenStart + 1));
456 0 : memcpy(pszNewToken, pszNewTokenStart,
457 0 : pszCur - pszNewTokenStart);
458 0 : pszNewToken[pszCur - pszNewTokenStart] = 0;
459 0 : OGRPGTokenizeStringListUnescapeToken(pszNewToken);
460 0 : papszTokens = CSLAddString(papszTokens, pszNewToken);
461 0 : CPLFree(pszNewToken);
462 : }
463 0 : pszNewTokenStart = nullptr;
464 0 : if (pszCur[1] == ',')
465 0 : pszCur++;
466 : else
467 0 : return papszTokens;
468 : }
469 : else
470 : {
471 : /* error */
472 : break;
473 : }
474 : }
475 : }
476 192 : if (!bInDoubleQuotes)
477 : {
478 192 : if (*pszCur == '{')
479 : {
480 : /* error */
481 0 : break;
482 : }
483 192 : else if (*pszCur == '}')
484 : {
485 36 : if (pszNewTokenStart != nullptr && pszCur > pszNewTokenStart)
486 : {
487 : char *pszNewToken = static_cast<char *>(
488 36 : CPLMalloc(pszCur - pszNewTokenStart + 1));
489 36 : memcpy(pszNewToken, pszNewTokenStart,
490 36 : pszCur - pszNewTokenStart);
491 36 : pszNewToken[pszCur - pszNewTokenStart] = 0;
492 36 : OGRPGTokenizeStringListUnescapeToken(pszNewToken);
493 36 : papszTokens = CSLAddString(papszTokens, pszNewToken);
494 36 : CPLFree(pszNewToken);
495 : }
496 36 : return papszTokens;
497 : }
498 156 : else if (*pszCur == ',')
499 : {
500 36 : if (pszNewTokenStart != nullptr && pszCur > pszNewTokenStart)
501 : {
502 : char *pszNewToken = static_cast<char *>(
503 36 : CPLMalloc(pszCur - pszNewTokenStart + 1));
504 36 : memcpy(pszNewToken, pszNewTokenStart,
505 36 : pszCur - pszNewTokenStart);
506 36 : pszNewToken[pszCur - pszNewTokenStart] = 0;
507 36 : OGRPGTokenizeStringListUnescapeToken(pszNewToken);
508 36 : papszTokens = CSLAddString(papszTokens, pszNewToken);
509 36 : CPLFree(pszNewToken);
510 : }
511 36 : pszNewTokenStart = pszCur + 1;
512 : }
513 120 : else if (pszNewTokenStart == nullptr)
514 36 : pszNewTokenStart = pszCur;
515 : }
516 156 : pszCur++;
517 : }
518 :
519 0 : CPLError(CE_Warning, CPLE_AppDefined, "Incorrect string list : %s",
520 : pszText);
521 0 : return papszTokens;
522 : }
523 :
524 : /************************************************************************/
525 : /* RecordToFeature() */
526 : /* */
527 : /* Convert the indicated record of the current result set into */
528 : /* a feature. */
529 : /************************************************************************/
530 :
531 5083 : OGRFeature *OGRPGLayer::RecordToFeature(PGresult *hResult,
532 : const int *panMapFieldNameToIndex,
533 : const int *panMapFieldNameToGeomIndex,
534 : int iRecord)
535 :
536 : {
537 : /* -------------------------------------------------------------------- */
538 : /* Create a feature from the current result. */
539 : /* -------------------------------------------------------------------- */
540 5083 : OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
541 :
542 5083 : poFeature->SetFID(iNextShapeId);
543 5083 : m_nFeaturesRead++;
544 :
545 : /* ==================================================================== */
546 : /* Transfer all result fields we can. */
547 : /* ==================================================================== */
548 26103 : for (int iField = 0; iField < PQnfields(hResult); iField++)
549 : {
550 : #if defined(BINARY_CURSOR_ENABLED)
551 : const Oid nTypeOID = PQftype(hResult, iField);
552 : #endif
553 21020 : const char *pszFieldName = PQfname(hResult, iField);
554 :
555 : /* --------------------------------------------------------------------
556 : */
557 : /* Handle FID. */
558 : /* --------------------------------------------------------------------
559 : */
560 21020 : if (pszFIDColumn != nullptr && EQUAL(pszFieldName, pszFIDColumn))
561 : {
562 : #if defined(BINARY_CURSOR_ENABLED)
563 : if (PQfformat(hResult, iField) == 1) // Binary data representation
564 : {
565 : if (nTypeOID == INT4OID)
566 : {
567 : int nVal = 0;
568 : CPLAssert(PQgetlength(hResult, iRecord, iField) ==
569 : sizeof(int));
570 : memcpy(&nVal, PQgetvalue(hResult, iRecord, iField),
571 : sizeof(int));
572 : CPL_MSBPTR32(&nVal);
573 : poFeature->SetFID(nVal);
574 : }
575 : else if (nTypeOID == INT8OID)
576 : {
577 : GIntBig nVal = 0;
578 : CPLAssert(PQgetlength(hResult, iRecord, iField) ==
579 : sizeof(GIntBig));
580 : memcpy(&nVal, PQgetvalue(hResult, iRecord, iField),
581 : sizeof(GIntBig));
582 : CPL_MSBPTR64(&nVal);
583 : poFeature->SetFID(nVal);
584 : }
585 : else
586 : {
587 : CPLDebug("PG", "FID. Unhandled OID %d.", nTypeOID);
588 : continue;
589 : }
590 : }
591 : else
592 : #endif /* defined(BINARY_CURSOR_ENABLED) */
593 : {
594 4884 : char *pabyData = PQgetvalue(hResult, iRecord, iField);
595 : /* ogr_pg_20 may crash if PostGIS is unavailable and we don't
596 : * test pabyData */
597 4884 : if (pabyData)
598 4884 : poFeature->SetFID(CPLAtoGIntBig(pabyData));
599 : else
600 0 : continue;
601 : }
602 : }
603 :
604 : /* --------------------------------------------------------------------
605 : */
606 : /* Handle PostGIS geometry */
607 : /* --------------------------------------------------------------------
608 : */
609 21020 : int iOGRGeomField = panMapFieldNameToGeomIndex[iField];
610 21020 : OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
611 21020 : if (iOGRGeomField >= 0)
612 4933 : poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(iOGRGeomField);
613 21020 : if (poGeomFieldDefn &&
614 4933 : (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ||
615 2419 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY))
616 : {
617 2520 : if (STARTS_WITH_CI(pszFieldName, "ST_AsBinary") ||
618 2519 : STARTS_WITH_CI(pszFieldName, "AsBinary"))
619 : {
620 1 : const char *pszVal = PQgetvalue(hResult, iRecord, iField);
621 :
622 1 : int nLength = PQgetlength(hResult, iRecord, iField);
623 :
624 : /* No geometry */
625 1 : if (nLength == 0)
626 0 : continue;
627 :
628 1 : OGRGeometry *poGeom = nullptr;
629 1 : if (!poDS->bUseBinaryCursor && nLength >= 4 &&
630 : /* escaped byea data */
631 1 : (STARTS_WITH(pszVal, "\\000") ||
632 1 : STARTS_WITH(pszVal, "\\001") ||
633 : /* hex bytea data (PostgreSQL >= 9.0) */
634 1 : STARTS_WITH(pszVal, "\\x00") ||
635 1 : STARTS_WITH(pszVal, "\\x01")))
636 : {
637 1 : poGeom = BYTEAToGeometry(pszVal);
638 : }
639 : else
640 : {
641 0 : const GByte *pabyVal =
642 : reinterpret_cast<const GByte *>(pszVal);
643 0 : OGRGeometryFactory::createFromWkb(
644 : pabyVal, nullptr, &poGeom, nLength, wkbVariantOldOgc);
645 : }
646 :
647 1 : if (poGeom != nullptr)
648 : {
649 1 : poGeom->assignSpatialReference(
650 1 : poGeomFieldDefn->GetSpatialRef());
651 1 : poFeature->SetGeomFieldDirectly(iOGRGeomField, poGeom);
652 : }
653 :
654 1 : continue;
655 : }
656 2519 : else if (!poDS->bUseBinaryCursor &&
657 2519 : STARTS_WITH_CI(pszFieldName, "EWKBBase64"))
658 : {
659 : const GByte *pabyData = reinterpret_cast<const GByte *>(
660 1 : PQgetvalue(hResult, iRecord, iField));
661 :
662 1 : int nLength = PQgetlength(hResult, iRecord, iField);
663 :
664 : /* No geometry */
665 1 : if (nLength == 0)
666 0 : continue;
667 :
668 : // Potentially dangerous to modify the result of PQgetvalue...
669 1 : nLength = CPLBase64DecodeInPlace(const_cast<GByte *>(pabyData));
670 1 : OGRGeometry *poGeom = OGRGeometryFromEWKB(
671 : const_cast<GByte *>(pabyData), nLength, nullptr, false);
672 :
673 1 : if (poGeom != nullptr)
674 : {
675 1 : poGeom->assignSpatialReference(
676 1 : poGeomFieldDefn->GetSpatialRef());
677 1 : poFeature->SetGeomFieldDirectly(iOGRGeomField, poGeom);
678 : }
679 :
680 1 : continue;
681 : }
682 2518 : else if (poDS->bUseBinaryCursor ||
683 2518 : EQUAL(pszFieldName, "ST_AsEWKB") ||
684 2517 : EQUAL(pszFieldName, "AsEWKB"))
685 : {
686 : /* Handle HEX result or EWKB binary cursor result */
687 1 : const char *pabyData = PQgetvalue(hResult, iRecord, iField);
688 :
689 1 : int nLength = PQgetlength(hResult, iRecord, iField);
690 :
691 : /* No geometry */
692 1 : if (nLength == 0)
693 0 : continue;
694 :
695 1 : OGRGeometry *poGeom = nullptr;
696 :
697 1 : if (!poDS->bUseBinaryCursor &&
698 1 : (STARTS_WITH(pabyData, "\\x00") ||
699 1 : STARTS_WITH(pabyData, "\\x01") ||
700 0 : STARTS_WITH(pabyData, "\\000") ||
701 0 : STARTS_WITH(pabyData, "\\001")))
702 : {
703 1 : GByte *pabyEWKB = BYTEAToGByteArray(pabyData, &nLength);
704 : poGeom =
705 1 : OGRGeometryFromEWKB(pabyEWKB, nLength, nullptr, false);
706 1 : CPLFree(pabyEWKB);
707 : }
708 0 : else if (nLength >= 2 && (STARTS_WITH_CI(pabyData, "00") ||
709 0 : STARTS_WITH_CI(pabyData, "01")))
710 : {
711 0 : poGeom = OGRGeometryFromHexEWKB(pabyData, nullptr, false);
712 : }
713 : else
714 : {
715 : // Potentially dangerous to modify the result of
716 : // PQgetvalue...
717 0 : poGeom = OGRGeometryFromEWKB(
718 : const_cast<GByte *>(
719 : reinterpret_cast<const GByte *>(pabyData)),
720 : nLength, nullptr, false);
721 : }
722 :
723 1 : if (poGeom != nullptr)
724 : {
725 1 : poGeom->assignSpatialReference(
726 1 : poGeomFieldDefn->GetSpatialRef());
727 1 : poFeature->SetGeomFieldDirectly(iOGRGeomField, poGeom);
728 : }
729 :
730 1 : continue;
731 : }
732 : else /*if (EQUAL(pszFieldName,"asEWKT") ||
733 : EQUAL(pszFieldName,"asText") ||
734 : EQUAL(pszFieldName,"ST_AsEWKT") ||
735 : EQUAL(pszFieldName,"ST_AsText") )*/
736 : {
737 : /* Handle WKT */
738 2517 : const char *pszWKT = PQgetvalue(hResult, iRecord, iField);
739 2517 : const char *pszPostSRID = pszWKT;
740 :
741 : // optionally strip off PostGIS SRID identifier. This
742 : // happens if we got a raw geometry field.
743 2517 : if (STARTS_WITH_CI(pszPostSRID, "SRID="))
744 : {
745 0 : while (*pszPostSRID != '\0' && *pszPostSRID != ';')
746 0 : pszPostSRID++;
747 0 : if (*pszPostSRID == ';')
748 0 : pszPostSRID++;
749 : }
750 :
751 2517 : OGRGeometry *poGeometry = nullptr;
752 2517 : if (STARTS_WITH_CI(pszPostSRID, "00") ||
753 2517 : STARTS_WITH_CI(pszPostSRID, "01"))
754 : {
755 1424 : poGeometry = OGRGeometryFromHexEWKB(pszWKT, nullptr, false);
756 : }
757 : else
758 1093 : OGRGeometryFactory::createFromWkt(pszPostSRID, nullptr,
759 : &poGeometry);
760 2517 : if (poGeometry != nullptr)
761 : {
762 1471 : poGeometry->assignSpatialReference(
763 1471 : poGeomFieldDefn->GetSpatialRef());
764 1471 : poFeature->SetGeomFieldDirectly(iOGRGeomField, poGeometry);
765 : }
766 :
767 2517 : continue;
768 : }
769 : }
770 : /* --------------------------------------------------------------------
771 : */
772 : /* Handle raw binary geometry ... this hasn't been tested in a */
773 : /* while. */
774 : /* --------------------------------------------------------------------
775 : */
776 18500 : else if (poGeomFieldDefn &&
777 2413 : poGeomFieldDefn->ePostgisType == GEOM_TYPE_WKB)
778 : {
779 2413 : OGRGeometry *poGeometry = nullptr;
780 2413 : const char *pszData = PQgetvalue(hResult, iRecord, iField);
781 :
782 2413 : if (bWkbAsOid)
783 : {
784 0 : poGeometry = OIDToGeometry(static_cast<Oid>(atoi(pszData)));
785 : }
786 : else
787 : {
788 : #if defined(BINARY_CURSOR_ENABLED)
789 : if (poDS->bUseBinaryCursor && PQfformat(hResult, iField) == 1)
790 : {
791 : int nLength = PQgetlength(hResult, iRecord, iField);
792 : const GByte *pabyData =
793 : reinterpret_cast<const GByte *>(pszData);
794 : poGeometry =
795 : OGRGeometryFromEWKB(pabyData, nLength, NULL, false);
796 : }
797 : if (poGeometry == nullptr)
798 : #endif
799 : {
800 2413 : poGeometry = BYTEAToGeometry(pszData);
801 : }
802 : }
803 :
804 2413 : if (poGeometry != nullptr)
805 : {
806 1385 : poGeometry->assignSpatialReference(
807 1385 : poGeomFieldDefn->GetSpatialRef());
808 1385 : poFeature->SetGeomFieldDirectly(iOGRGeomField, poGeometry);
809 : }
810 :
811 2413 : continue;
812 : }
813 :
814 : /* --------------------------------------------------------------------
815 : */
816 : /* Transfer regular data fields. */
817 : /* --------------------------------------------------------------------
818 : */
819 16087 : const int iOGRField = panMapFieldNameToIndex[iField];
820 :
821 16087 : if (iOGRField < 0)
822 4882 : continue;
823 :
824 11205 : if (PQgetisnull(hResult, iRecord, iField))
825 : {
826 3468 : poFeature->SetFieldNull(iOGRField);
827 3468 : continue;
828 : }
829 :
830 : OGRFieldType eOGRType =
831 7737 : poFeatureDefn->GetFieldDefn(iOGRField)->GetType();
832 :
833 7737 : if (eOGRType == OFTIntegerList)
834 : {
835 : int *panList, nCount, i;
836 :
837 : #if defined(BINARY_CURSOR_ENABLED)
838 : if (PQfformat(hResult, iField) == 1) // Binary data representation
839 : {
840 : if (nTypeOID == INT2ARRAYOID || nTypeOID == INT4ARRAYOID)
841 : {
842 : char *pData = PQgetvalue(hResult, iRecord, iField);
843 :
844 : // goto number of array elements
845 : pData += 3 * sizeof(int);
846 : memcpy(&nCount, pData, sizeof(int));
847 : CPL_MSBPTR32(&nCount);
848 :
849 : panList =
850 : static_cast<int *>(CPLCalloc(sizeof(int), nCount));
851 :
852 : // goto first array element
853 : pData += 2 * sizeof(int);
854 :
855 : for (i = 0; i < nCount; i++)
856 : {
857 : // get element size
858 : int nSize = *(int *)(pData);
859 : CPL_MSBPTR32(&nSize);
860 : pData += sizeof(int);
861 :
862 : if (nTypeOID == INT4ARRAYOID)
863 : {
864 : CPLAssert(nSize == sizeof(int));
865 : memcpy(&panList[i], pData, nSize);
866 : CPL_MSBPTR32(&panList[i]);
867 : }
868 : else
869 : {
870 : CPLAssert(nSize == sizeof(GInt16));
871 : GInt16 nVal = 0;
872 : memcpy(&nVal, pData, nSize);
873 : CPL_MSBPTR16(&nVal);
874 : panList[i] = nVal;
875 : }
876 :
877 : pData += nSize;
878 : }
879 : }
880 : else
881 : {
882 : CPLDebug(
883 : "PG",
884 : "Field %d: Incompatible OID (%d) with OFTIntegerList.",
885 : iOGRField, nTypeOID);
886 : continue;
887 : }
888 : }
889 : else
890 : #endif
891 : {
892 96 : char **papszTokens = CSLTokenizeStringComplex(
893 48 : PQgetvalue(hResult, iRecord, iField), "{,}", FALSE, FALSE);
894 :
895 48 : nCount = CSLCount(papszTokens);
896 48 : panList = static_cast<int *>(CPLCalloc(sizeof(int), nCount));
897 :
898 48 : if (poFeatureDefn->GetFieldDefn(iOGRField)->GetSubType() ==
899 : OFSTBoolean)
900 : {
901 36 : for (i = 0; i < nCount; i++)
902 24 : panList[i] = EQUAL(papszTokens[i], "t");
903 : }
904 : else
905 : {
906 108 : for (i = 0; i < nCount; i++)
907 72 : panList[i] = atoi(papszTokens[i]);
908 : }
909 48 : CSLDestroy(papszTokens);
910 : }
911 48 : poFeature->SetField(iOGRField, nCount, panList);
912 48 : CPLFree(panList);
913 : }
914 :
915 7689 : else if (eOGRType == OFTInteger64List)
916 : {
917 12 : int nCount = 0;
918 12 : GIntBig *panList = nullptr;
919 :
920 : #if defined(BINARY_CURSOR_ENABLED)
921 : if (PQfformat(hResult, iField) == 1) // Binary data representation
922 : {
923 : if (nTypeOID == INT8ARRAYOID)
924 : {
925 : char *pData = PQgetvalue(hResult, iRecord, iField);
926 :
927 : // goto number of array elements
928 : pData += 3 * sizeof(int);
929 : memcpy(&nCount, pData, sizeof(int));
930 : CPL_MSBPTR32(&nCount);
931 :
932 : panList = static_cast<GIntBig *>(
933 : CPLCalloc(sizeof(GIntBig), nCount));
934 :
935 : // goto first array element
936 : pData += 2 * sizeof(int);
937 :
938 : for (int i = 0; i < nCount; i++)
939 : {
940 : // get element size
941 : int nSize = *(int *)(pData);
942 : CPL_MSBPTR32(&nSize);
943 :
944 : CPLAssert(nSize == sizeof(GIntBig));
945 :
946 : pData += sizeof(int);
947 :
948 : memcpy(&panList[i], pData, nSize);
949 : CPL_MSBPTR64(&panList[i]);
950 :
951 : pData += nSize;
952 : }
953 : }
954 : else
955 : {
956 : CPLDebug("PG",
957 : "Field %d: Incompatible OID (%d) with "
958 : "OFTInteger64List.",
959 : iOGRField, nTypeOID);
960 : continue;
961 : }
962 : }
963 : else
964 : #endif
965 : {
966 24 : char **papszTokens = CSLTokenizeStringComplex(
967 12 : PQgetvalue(hResult, iRecord, iField), "{,}", FALSE, FALSE);
968 :
969 12 : nCount = CSLCount(papszTokens);
970 : panList =
971 12 : static_cast<GIntBig *>(CPLCalloc(sizeof(GIntBig), nCount));
972 :
973 12 : if (poFeatureDefn->GetFieldDefn(iOGRField)->GetSubType() ==
974 : OFSTBoolean)
975 : {
976 0 : for (int i = 0; i < nCount; i++)
977 0 : panList[i] = EQUAL(papszTokens[i], "t");
978 : }
979 : else
980 : {
981 24 : for (int i = 0; i < nCount; i++)
982 12 : panList[i] = CPLAtoGIntBig(papszTokens[i]);
983 : }
984 12 : CSLDestroy(papszTokens);
985 : }
986 12 : poFeature->SetField(iOGRField, nCount, panList);
987 12 : CPLFree(panList);
988 : }
989 :
990 7677 : else if (eOGRType == OFTRealList)
991 : {
992 : int nCount, i;
993 60 : double *padfList = nullptr;
994 :
995 : #if defined(BINARY_CURSOR_ENABLED)
996 : if (PQfformat(hResult, iField) == 1) // Binary data representation
997 : {
998 : if (nTypeOID == FLOAT8ARRAYOID || nTypeOID == FLOAT4ARRAYOID)
999 : {
1000 : char *pData = PQgetvalue(hResult, iRecord, iField);
1001 :
1002 : // goto number of array elements
1003 : pData += 3 * sizeof(int);
1004 : memcpy(&nCount, pData, sizeof(int));
1005 : CPL_MSBPTR32(&nCount);
1006 :
1007 : padfList = static_cast<double *>(
1008 : CPLCalloc(sizeof(double), nCount));
1009 :
1010 : // goto first array element
1011 : pData += 2 * sizeof(int);
1012 :
1013 : for (i = 0; i < nCount; i++)
1014 : {
1015 : // get element size
1016 : int nSize = *(int *)(pData);
1017 : CPL_MSBPTR32(&nSize);
1018 :
1019 : pData += sizeof(int);
1020 :
1021 : if (nTypeOID == FLOAT8ARRAYOID)
1022 : {
1023 : CPLAssert(nSize == sizeof(double));
1024 :
1025 : memcpy(&padfList[i], pData, nSize);
1026 : CPL_MSBPTR64(&padfList[i]);
1027 : }
1028 : else
1029 : {
1030 : CPLAssert(nSize == sizeof(float));
1031 :
1032 : float fVal = 0.0f;
1033 : memcpy(&fVal, pData, nSize);
1034 : CPL_MSBPTR32(&fVal);
1035 :
1036 : padfList[i] = fVal;
1037 : }
1038 :
1039 : pData += nSize;
1040 : }
1041 : }
1042 : else
1043 : {
1044 : CPLDebug(
1045 : "PG",
1046 : "Field %d: Incompatible OID (%d) with OFTRealList.",
1047 : iOGRField, nTypeOID);
1048 : continue;
1049 : }
1050 : }
1051 : else
1052 : #endif
1053 : {
1054 120 : char **papszTokens = CSLTokenizeStringComplex(
1055 60 : PQgetvalue(hResult, iRecord, iField), "{,}", FALSE, FALSE);
1056 :
1057 60 : nCount = CSLCount(papszTokens);
1058 : padfList =
1059 60 : static_cast<double *>(CPLCalloc(sizeof(double), nCount));
1060 :
1061 180 : for (i = 0; i < nCount; i++)
1062 120 : padfList[i] = CPLAtof(papszTokens[i]);
1063 60 : CSLDestroy(papszTokens);
1064 : }
1065 :
1066 60 : poFeature->SetField(iOGRField, nCount, padfList);
1067 60 : CPLFree(padfList);
1068 : }
1069 :
1070 7617 : else if (eOGRType == OFTStringList)
1071 : {
1072 36 : char **papszTokens = nullptr;
1073 :
1074 : #if defined(BINARY_CURSOR_ENABLED)
1075 : if (PQfformat(hResult, iField) == 1) // Binary data representation
1076 : {
1077 : char *pData = PQgetvalue(hResult, iRecord, iField);
1078 : int nCount, i;
1079 :
1080 : // goto number of array elements
1081 : pData += 3 * sizeof(int);
1082 : memcpy(&nCount, pData, sizeof(int));
1083 : CPL_MSBPTR32(&nCount);
1084 :
1085 : // goto first array element
1086 : pData += 2 * sizeof(int);
1087 :
1088 : for (i = 0; i < nCount; i++)
1089 : {
1090 : // get element size
1091 : int nSize = *(int *)(pData);
1092 : CPL_MSBPTR32(&nSize);
1093 :
1094 : pData += sizeof(int);
1095 :
1096 : if (nSize <= 0)
1097 : papszTokens = CSLAddString(papszTokens, "");
1098 : else
1099 : {
1100 : if (pData[nSize] == '\0')
1101 : papszTokens = CSLAddString(papszTokens, pData);
1102 : else
1103 : {
1104 : char *pszToken = (char *)CPLMalloc(nSize + 1);
1105 : memcpy(pszToken, pData, nSize);
1106 : pszToken[nSize] = '\0';
1107 : papszTokens = CSLAddString(papszTokens, pszToken);
1108 : CPLFree(pszToken);
1109 : }
1110 :
1111 : pData += nSize;
1112 : }
1113 : }
1114 : }
1115 : else
1116 : #endif
1117 : {
1118 72 : papszTokens = OGRPGTokenizeStringListFromText(
1119 36 : PQgetvalue(hResult, iRecord, iField));
1120 : }
1121 :
1122 36 : if (papszTokens)
1123 : {
1124 36 : poFeature->SetField(iOGRField, papszTokens);
1125 36 : CSLDestroy(papszTokens);
1126 : }
1127 : }
1128 :
1129 7581 : else if (eOGRType == OFTDate || eOGRType == OFTTime ||
1130 : eOGRType == OFTDateTime)
1131 : {
1132 : #if defined(BINARY_CURSOR_ENABLED)
1133 : if (PQfformat(hResult, iField) == 1) // Binary data
1134 : {
1135 : if (nTypeOID == DATEOID)
1136 : {
1137 : int nVal, nYear, nMonth, nDay;
1138 : CPLAssert(PQgetlength(hResult, iRecord, iField) ==
1139 : sizeof(int));
1140 : memcpy(&nVal, PQgetvalue(hResult, iRecord, iField),
1141 : sizeof(int));
1142 : CPL_MSBPTR32(&nVal);
1143 : OGRPGj2date(nVal + POSTGRES_EPOCH_JDATE, &nYear, &nMonth,
1144 : &nDay);
1145 : poFeature->SetField(iOGRField, nYear, nMonth, nDay);
1146 : }
1147 : else if (nTypeOID == TIMEOID)
1148 : {
1149 : int nHour = 0;
1150 : int nMinute = 0;
1151 : int nSecond = 0;
1152 : char szTime[32];
1153 : double dfsec = 0.0f;
1154 : CPLAssert(PQgetlength(hResult, iRecord, iField) == 8);
1155 : if (poDS->bBinaryTimeFormatIsInt8)
1156 : {
1157 : unsigned int nVal[2];
1158 : GIntBig llVal = 0;
1159 : memcpy(nVal, PQgetvalue(hResult, iRecord, iField), 8);
1160 : CPL_MSBPTR32(&nVal[0]);
1161 : CPL_MSBPTR32(&nVal[1]);
1162 : llVal =
1163 : (GIntBig)((((GUIntBig)nVal[0]) << 32) | nVal[1]);
1164 : OGRPGdt2timeInt8(llVal, &nHour, &nMinute, &nSecond,
1165 : &dfsec);
1166 : }
1167 : else
1168 : {
1169 : double dfVal = 0.0;
1170 : memcpy(&dfVal, PQgetvalue(hResult, iRecord, iField), 8);
1171 : CPL_MSBPTR64(&dfVal);
1172 : OGRPGdt2timeFloat8(dfVal, &nHour, &nMinute, &nSecond,
1173 : &dfsec);
1174 : }
1175 : snprintf(szTime, sizeof(szTime), "%02d:%02d:%02d", nHour,
1176 : nMinute, nSecond);
1177 : poFeature->SetField(iOGRField, szTime);
1178 : }
1179 : else if (nTypeOID == TIMESTAMPOID || nTypeOID == TIMESTAMPTZOID)
1180 : {
1181 : unsigned int nVal[2];
1182 : GIntBig llVal = 0;
1183 : int nYear = 0;
1184 : int nMonth = 0;
1185 : int nDay = 0;
1186 : int nHour = 0;
1187 : int nMinute = 0;
1188 : double dfSecond = 0.0;
1189 : CPLAssert(PQgetlength(hResult, iRecord, iField) == 8);
1190 : memcpy(nVal, PQgetvalue(hResult, iRecord, iField), 8);
1191 : CPL_MSBPTR32(&nVal[0]);
1192 : CPL_MSBPTR32(&nVal[1]);
1193 : llVal = (GIntBig)((((GUIntBig)nVal[0]) << 32) | nVal[1]);
1194 : if (OGRPGTimeStamp2DMYHMS(llVal, &nYear, &nMonth, &nDay,
1195 : &nHour, &nMinute, &dfSecond) == 0)
1196 : poFeature->SetField(iOGRField, nYear, nMonth, nDay,
1197 : nHour, nMinute, (float)dfSecond,
1198 : 100);
1199 : }
1200 : else if (nTypeOID == TEXTOID)
1201 : {
1202 : OGRField sFieldValue;
1203 :
1204 : if (OGRParseDate(PQgetvalue(hResult, iRecord, iField),
1205 : &sFieldValue, 0))
1206 : {
1207 : poFeature->SetField(iOGRField, &sFieldValue);
1208 : }
1209 : }
1210 : else
1211 : {
1212 : CPLDebug("PG",
1213 : "Binary DATE format not yet implemented. OID = %d",
1214 : nTypeOID);
1215 : }
1216 : }
1217 : else
1218 : #endif
1219 : {
1220 : OGRField sFieldValue;
1221 :
1222 105 : if (OGRParseDate(PQgetvalue(hResult, iRecord, iField),
1223 105 : &sFieldValue, 0))
1224 : {
1225 105 : poFeature->SetField(iOGRField, &sFieldValue);
1226 : }
1227 105 : }
1228 : }
1229 7476 : else if (eOGRType == OFTBinary)
1230 : {
1231 : #if defined(BINARY_CURSOR_ENABLED)
1232 : if (PQfformat(hResult, iField) == 1)
1233 : {
1234 : int nLength = PQgetlength(hResult, iRecord, iField);
1235 : GByte *pabyData = reinterpret_cast<GByte *>(
1236 : PQgetvalue(hResult, iRecord, iField));
1237 : poFeature->SetField(iOGRField, nLength, pabyData);
1238 : }
1239 : else
1240 : #endif /* defined(BINARY_CURSOR_ENABLED) */
1241 : {
1242 14 : int nLength = PQgetlength(hResult, iRecord, iField);
1243 14 : const char *pszBytea = PQgetvalue(hResult, iRecord, iField);
1244 14 : GByte *pabyData = BYTEAToGByteArray(pszBytea, &nLength);
1245 14 : poFeature->SetField(iOGRField, nLength, pabyData);
1246 14 : CPLFree(pabyData);
1247 : }
1248 : }
1249 : else
1250 : {
1251 : #if defined(BINARY_CURSOR_ENABLED)
1252 : if (PQfformat(hResult, iField) == 1 &&
1253 : eOGRType != OFTString) // Binary data
1254 : {
1255 : if (nTypeOID == BOOLOID)
1256 : {
1257 : CPLAssert(PQgetlength(hResult, iRecord, iField) ==
1258 : sizeof(char));
1259 : const char cVal = *PQgetvalue(hResult, iRecord, iField);
1260 : poFeature->SetField(iOGRField, cVal);
1261 : }
1262 : else if (nTypeOID == NUMERICOID)
1263 : {
1264 : char *pabyData = PQgetvalue(hResult, iRecord, iField);
1265 : unsigned short sLen = 0;
1266 : memcpy(&sLen, pabyData, sizeof(short));
1267 : pabyData += sizeof(short);
1268 : CPL_MSBPTR16(&sLen);
1269 : short sWeight = 0;
1270 : memcpy(&sWeight, pabyData, sizeof(short));
1271 : pabyData += sizeof(short);
1272 : CPL_MSBPTR16(&sWeight);
1273 : unsigned short sSign = 0;
1274 : memcpy(&sSign, pabyData, sizeof(short));
1275 : pabyData += sizeof(short);
1276 : CPL_MSBPTR16(&sSign);
1277 : unsigned short sDscale = 0;
1278 : memcpy(&sDscale, pabyData, sizeof(short));
1279 : pabyData += sizeof(short);
1280 : CPL_MSBPTR16(&sDscale);
1281 : CPLAssert(PQgetlength(hResult, iRecord, iField) ==
1282 : (int)((4 + sLen) * sizeof(short)));
1283 :
1284 : NumericVar var;
1285 : var.ndigits = sLen;
1286 : var.weight = sWeight;
1287 : var.sign = sSign;
1288 : var.dscale = sDscale;
1289 : var.digits = (NumericDigit *)pabyData;
1290 : char *str = OGRPGGetStrFromBinaryNumeric(&var);
1291 : poFeature->SetField(iOGRField, CPLAtof(str));
1292 : CPLFree(str);
1293 : }
1294 : else if (nTypeOID == INT2OID)
1295 : {
1296 : CPLAssert(PQgetlength(hResult, iRecord, iField) ==
1297 : sizeof(short));
1298 : short sVal = 0;
1299 : memcpy(&sVal, PQgetvalue(hResult, iRecord, iField),
1300 : sizeof(short));
1301 : CPL_MSBPTR16(&sVal);
1302 : poFeature->SetField(iOGRField, sVal);
1303 : }
1304 : else if (nTypeOID == INT4OID)
1305 : {
1306 : CPLAssert(PQgetlength(hResult, iRecord, iField) ==
1307 : sizeof(int));
1308 : int nVal = 0;
1309 : memcpy(&nVal, PQgetvalue(hResult, iRecord, iField),
1310 : sizeof(int));
1311 : CPL_MSBPTR32(&nVal);
1312 : poFeature->SetField(iOGRField, nVal);
1313 : }
1314 : else if (nTypeOID == INT8OID)
1315 : {
1316 : CPLAssert(PQgetlength(hResult, iRecord, iField) == 8);
1317 : unsigned int nVal[2] = {0, 0};
1318 : memcpy(nVal, PQgetvalue(hResult, iRecord, iField), 8);
1319 : CPL_MSBPTR32(&nVal[0]);
1320 : CPL_MSBPTR32(&nVal[1]);
1321 : GIntBig llVal =
1322 : (GIntBig)((((GUIntBig)nVal[0]) << 32) | nVal[1]);
1323 : poFeature->SetField(iOGRField, llVal);
1324 : }
1325 : else if (nTypeOID == FLOAT4OID)
1326 : {
1327 : CPLAssert(PQgetlength(hResult, iRecord, iField) ==
1328 : sizeof(float));
1329 : float fVal = 0.0f;
1330 : memcpy(&fVal, PQgetvalue(hResult, iRecord, iField),
1331 : sizeof(float));
1332 : CPL_MSBPTR32(&fVal);
1333 : poFeature->SetField(iOGRField, fVal);
1334 : }
1335 : else if (nTypeOID == FLOAT8OID)
1336 : {
1337 : CPLAssert(PQgetlength(hResult, iRecord, iField) ==
1338 : sizeof(double));
1339 : double dfVal = 0.0;
1340 : memcpy(&dfVal, PQgetvalue(hResult, iRecord, iField),
1341 : sizeof(double));
1342 : CPL_MSBPTR64(&dfVal);
1343 : poFeature->SetField(iOGRField, dfVal);
1344 : }
1345 : else
1346 : {
1347 : CPLDebug(
1348 : "PG", "Field %d(%s): Incompatible OID (%d) with %s.",
1349 : iOGRField,
1350 : poFeatureDefn->GetFieldDefn(iOGRField)->GetNameRef(),
1351 : nTypeOID, OGRFieldDefn::GetFieldTypeName(eOGRType));
1352 : continue;
1353 : }
1354 : }
1355 : else
1356 : #endif /* defined(BINARY_CURSOR_ENABLED) */
1357 : {
1358 11251 : if (eOGRType == OFTInteger &&
1359 3789 : poFeatureDefn->GetFieldDefn(iOGRField)->GetWidth() == 1)
1360 : {
1361 14 : char *pabyData = PQgetvalue(hResult, iRecord, iField);
1362 14 : if (STARTS_WITH_CI(pabyData, "T"))
1363 14 : poFeature->SetField(iOGRField, 1);
1364 0 : else if (STARTS_WITH_CI(pabyData, "F"))
1365 0 : poFeature->SetField(iOGRField, 0);
1366 : else
1367 : {
1368 : // coverity[tainted_data]
1369 0 : poFeature->SetField(iOGRField, pabyData);
1370 : }
1371 : }
1372 7448 : else if (eOGRType == OFTReal)
1373 : {
1374 1748 : poFeature->SetField(
1375 : iOGRField,
1376 1748 : CPLAtof(PQgetvalue(hResult, iRecord, iField)));
1377 : }
1378 : else
1379 : {
1380 5700 : poFeature->SetField(iOGRField,
1381 5700 : PQgetvalue(hResult, iRecord, iField));
1382 : }
1383 : }
1384 : }
1385 : }
1386 :
1387 5083 : return poFeature;
1388 : }
1389 :
1390 : /************************************************************************/
1391 : /* OGRPGIsKnownGeomFuncPrefix() */
1392 : /************************************************************************/
1393 :
1394 : static const char *const apszKnownGeomFuncPrefixes[] = {
1395 : "ST_AsBinary", "ST_AsEWKT", "ST_AsEWKB", "EWKBBase64", "ST_AsText",
1396 : "AsBinary", "asEWKT", "asEWKB", "asText"};
1397 :
1398 1027 : static int OGRPGIsKnownGeomFuncPrefix(const char *pszFieldName)
1399 : {
1400 10013 : for (size_t i = 0; i < sizeof(apszKnownGeomFuncPrefixes) / sizeof(char *);
1401 : i++)
1402 : {
1403 9036 : if (EQUALN(pszFieldName, apszKnownGeomFuncPrefixes[i],
1404 : static_cast<int>(strlen(apszKnownGeomFuncPrefixes[i]))))
1405 50 : return static_cast<int>(i);
1406 : }
1407 977 : return -1;
1408 : }
1409 :
1410 : /************************************************************************/
1411 : /* CreateMapFromFieldNameToIndex() */
1412 : /************************************************************************/
1413 :
1414 : /* Evaluating GetFieldIndex() on each field of each feature can be very */
1415 : /* expensive if the layer has many fields (total complexity of O(n^2) where */
1416 : /* n is the number of fields), so it is valuable to compute the map from */
1417 : /* the fetched fields to the OGR field index */
1418 732 : void OGRPGLayer::CreateMapFromFieldNameToIndex(PGresult *hResult,
1419 : OGRFeatureDefn *poFeatureDefn,
1420 : int *&panMapFieldNameToIndex,
1421 : int *&panMapFieldNameToGeomIndex)
1422 : {
1423 732 : CPLFree(panMapFieldNameToIndex);
1424 732 : panMapFieldNameToIndex = nullptr;
1425 732 : CPLFree(panMapFieldNameToGeomIndex);
1426 732 : panMapFieldNameToGeomIndex = nullptr;
1427 732 : if (PQresultStatus(hResult) == PGRES_TUPLES_OK)
1428 : {
1429 732 : panMapFieldNameToIndex =
1430 732 : static_cast<int *>(CPLMalloc(sizeof(int) * PQnfields(hResult)));
1431 732 : panMapFieldNameToGeomIndex =
1432 732 : static_cast<int *>(CPLMalloc(sizeof(int) * PQnfields(hResult)));
1433 4530 : for (int iField = 0; iField < PQnfields(hResult); iField++)
1434 : {
1435 3798 : const char *pszName = PQfname(hResult, iField);
1436 7596 : panMapFieldNameToIndex[iField] =
1437 3798 : poFeatureDefn->GetFieldIndex(pszName);
1438 3798 : if (panMapFieldNameToIndex[iField] < 0)
1439 : {
1440 2308 : panMapFieldNameToGeomIndex[iField] =
1441 1154 : poFeatureDefn->GetGeomFieldIndex(pszName);
1442 1154 : if (panMapFieldNameToGeomIndex[iField] < 0)
1443 : {
1444 557 : int iKnownPrefix = OGRPGIsKnownGeomFuncPrefix(pszName);
1445 557 : if (iKnownPrefix >= 0 &&
1446 1 : pszName[strlen(
1447 1 : apszKnownGeomFuncPrefixes[iKnownPrefix])] == '_')
1448 : {
1449 2 : panMapFieldNameToGeomIndex[iField] =
1450 1 : poFeatureDefn->GetGeomFieldIndex(
1451 : pszName +
1452 1 : strlen(
1453 1 : apszKnownGeomFuncPrefixes[iKnownPrefix]) +
1454 1 : 1);
1455 : }
1456 : }
1457 : }
1458 : else
1459 2644 : panMapFieldNameToGeomIndex[iField] = -1;
1460 : }
1461 : }
1462 732 : }
1463 :
1464 : /************************************************************************/
1465 : /* SetInitialQueryCursor() */
1466 : /************************************************************************/
1467 :
1468 515 : void OGRPGLayer::SetInitialQueryCursor()
1469 : {
1470 515 : PGconn *hPGConn = poDS->GetPGConn();
1471 515 : CPLString osCommand;
1472 :
1473 515 : CPLAssert(pszQueryStatement != nullptr);
1474 :
1475 515 : poDS->SoftStartTransaction();
1476 :
1477 : #if defined(BINARY_CURSOR_ENABLED)
1478 : if (poDS->bUseBinaryCursor && bCanUseBinaryCursor)
1479 : osCommand.Printf("DECLARE %s BINARY CURSOR for %s", pszCursorName,
1480 : pszQueryStatement);
1481 : else
1482 : #endif
1483 : osCommand.Printf("DECLARE %s CURSOR for %s", pszCursorName,
1484 515 : pszQueryStatement);
1485 :
1486 515 : hCursorResult = OGRPG_PQexec(hPGConn, osCommand);
1487 515 : if (!hCursorResult || PQresultStatus(hCursorResult) != PGRES_COMMAND_OK)
1488 : {
1489 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
1490 0 : poDS->SoftRollbackTransaction();
1491 : }
1492 515 : OGRPGClearResult(hCursorResult);
1493 :
1494 515 : osCommand.Printf("FETCH %d in %s", nCursorPage, pszCursorName);
1495 515 : hCursorResult = OGRPG_PQexec(hPGConn, osCommand);
1496 :
1497 515 : CreateMapFromFieldNameToIndex(hCursorResult, poFeatureDefn,
1498 515 : m_panMapFieldNameToIndex,
1499 515 : m_panMapFieldNameToGeomIndex);
1500 :
1501 515 : nResultOffset = 0;
1502 515 : }
1503 :
1504 : /************************************************************************/
1505 : /* GetNextRawFeature() */
1506 : /************************************************************************/
1507 :
1508 5098 : OGRFeature *OGRPGLayer::GetNextRawFeature()
1509 :
1510 : {
1511 5098 : if (iNextShapeId < 0)
1512 14 : return nullptr;
1513 :
1514 5084 : PGconn *hPGConn = poDS->GetPGConn();
1515 10168 : CPLString osCommand;
1516 :
1517 5084 : if (bInvalidated)
1518 : {
1519 2 : CPLError(CE_Failure, CPLE_AppDefined,
1520 : "Cursor used to read layer has been closed due to a COMMIT. "
1521 : "ResetReading() must be explicitly called to restart reading");
1522 2 : return nullptr;
1523 : }
1524 :
1525 : /* -------------------------------------------------------------------- */
1526 : /* Do we need to establish an initial query? */
1527 : /* -------------------------------------------------------------------- */
1528 5082 : if (iNextShapeId == 0 && hCursorResult == nullptr)
1529 : {
1530 508 : SetInitialQueryCursor();
1531 : }
1532 :
1533 : /* -------------------------------------------------------------------- */
1534 : /* Are we in some sort of error condition? */
1535 : /* -------------------------------------------------------------------- */
1536 10157 : if (hCursorResult == nullptr ||
1537 5075 : PQresultStatus(hCursorResult) != PGRES_TUPLES_OK)
1538 : {
1539 7 : CPLDebug("PG", "PQclear() on an error condition");
1540 :
1541 7 : OGRPGClearResult(hCursorResult);
1542 :
1543 7 : iNextShapeId = MAX(1, iNextShapeId);
1544 7 : return nullptr;
1545 : }
1546 :
1547 : /* -------------------------------------------------------------------- */
1548 : /* Do we need to fetch more records? */
1549 : /* -------------------------------------------------------------------- */
1550 :
1551 : /* We test for PQntuples(hCursorResult) == 1 in the case the previous */
1552 : /* request was a SetNextByIndex() */
1553 5075 : if ((PQntuples(hCursorResult) == 1 ||
1554 8339 : PQntuples(hCursorResult) == nCursorPage) &&
1555 3264 : nResultOffset == PQntuples(hCursorResult))
1556 : {
1557 54 : OGRPGClearResult(hCursorResult);
1558 :
1559 54 : osCommand.Printf("FETCH %d in %s", nCursorPage, pszCursorName);
1560 54 : hCursorResult = OGRPG_PQexec(hPGConn, osCommand);
1561 :
1562 54 : nResultOffset = 0;
1563 : }
1564 :
1565 : /* -------------------------------------------------------------------- */
1566 : /* Are we out of results? If so complete the transaction, and */
1567 : /* cleanup, but don't reset the next shapeid. */
1568 : /* -------------------------------------------------------------------- */
1569 5075 : if (nResultOffset == PQntuples(hCursorResult))
1570 : {
1571 209 : CloseCursor();
1572 :
1573 209 : iNextShapeId = MAX(1, iNextShapeId);
1574 :
1575 209 : return nullptr;
1576 : }
1577 :
1578 : /* -------------------------------------------------------------------- */
1579 : /* Create a feature from the current result. */
1580 : /* -------------------------------------------------------------------- */
1581 : OGRFeature *poFeature =
1582 9732 : RecordToFeature(hCursorResult, m_panMapFieldNameToIndex,
1583 4866 : m_panMapFieldNameToGeomIndex, nResultOffset);
1584 :
1585 4866 : nResultOffset++;
1586 4866 : iNextShapeId++;
1587 :
1588 4866 : return poFeature;
1589 : }
1590 :
1591 : /************************************************************************/
1592 : /* SetNextByIndex() */
1593 : /************************************************************************/
1594 :
1595 37 : OGRErr OGRPGLayer::SetNextByIndex(GIntBig nIndex)
1596 :
1597 : {
1598 37 : GetLayerDefn();
1599 :
1600 37 : if (!TestCapability(OLCFastSetNextByIndex))
1601 0 : return OGRLayer::SetNextByIndex(nIndex);
1602 :
1603 37 : if (nIndex == iNextShapeId)
1604 : {
1605 7 : return OGRERR_NONE;
1606 : }
1607 :
1608 30 : if (nIndex < 0)
1609 : {
1610 7 : iNextShapeId = -1;
1611 7 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid index");
1612 7 : return OGRERR_NON_EXISTING_FEATURE;
1613 : }
1614 :
1615 23 : if (nIndex == 0)
1616 : {
1617 0 : ResetReading();
1618 0 : return OGRERR_NONE;
1619 : }
1620 :
1621 23 : PGconn *hPGConn = poDS->GetPGConn();
1622 46 : CPLString osCommand;
1623 :
1624 23 : if (hCursorResult == nullptr)
1625 : {
1626 7 : SetInitialQueryCursor();
1627 : }
1628 :
1629 23 : OGRPGClearResult(hCursorResult);
1630 :
1631 : osCommand.Printf("FETCH ABSOLUTE " CPL_FRMT_GIB " in %s", nIndex + 1,
1632 23 : pszCursorName);
1633 23 : hCursorResult = OGRPG_PQexec(hPGConn, osCommand);
1634 :
1635 46 : if (PQresultStatus(hCursorResult) != PGRES_TUPLES_OK ||
1636 23 : PQntuples(hCursorResult) != 1)
1637 : {
1638 7 : CPLError(CE_Failure, CPLE_AppDefined,
1639 : "Attempt to read feature at invalid index (" CPL_FRMT_GIB ").",
1640 : nIndex);
1641 :
1642 7 : CloseCursor();
1643 :
1644 7 : iNextShapeId = -1;
1645 :
1646 7 : return OGRERR_NON_EXISTING_FEATURE;
1647 : }
1648 :
1649 16 : nResultOffset = 0;
1650 16 : iNextShapeId = nIndex;
1651 :
1652 16 : return OGRERR_NONE;
1653 : }
1654 :
1655 : /************************************************************************/
1656 : /* BYTEAToGByteArray() */
1657 : /************************************************************************/
1658 :
1659 2429 : GByte *OGRPGLayer::BYTEAToGByteArray(const char *pszBytea, int *pnLength)
1660 : {
1661 2429 : if (pszBytea == nullptr)
1662 : {
1663 0 : if (pnLength)
1664 0 : *pnLength = 0;
1665 0 : return nullptr;
1666 : }
1667 :
1668 : /* hex bytea data (PostgreSQL >= 9.0) */
1669 2429 : if (pszBytea[0] == '\\' && pszBytea[1] == 'x')
1670 1401 : return CPLHexToBinary(pszBytea + 2, pnLength);
1671 :
1672 : /* +1 just to please Coverity that thinks we allocate for a null-terminate
1673 : * string */
1674 1028 : GByte *pabyData = static_cast<GByte *>(CPLMalloc(strlen(pszBytea) + 1));
1675 :
1676 1028 : int iSrc = 0;
1677 1028 : int iDst = 0;
1678 1028 : while (pszBytea[iSrc] != '\0')
1679 : {
1680 0 : if (pszBytea[iSrc] == '\\')
1681 : {
1682 0 : if (pszBytea[iSrc + 1] >= '0' && pszBytea[iSrc + 1] <= '9')
1683 : {
1684 0 : if (pszBytea[iSrc + 2] == '\0' || pszBytea[iSrc + 3] == '\0')
1685 : break;
1686 :
1687 0 : pabyData[iDst++] = (pszBytea[iSrc + 1] - 48) * 64 +
1688 0 : (pszBytea[iSrc + 2] - 48) * 8 +
1689 0 : (pszBytea[iSrc + 3] - 48) * 1;
1690 0 : iSrc += 4;
1691 : }
1692 : else
1693 : {
1694 0 : if (pszBytea[iSrc + 1] == '\0')
1695 0 : break;
1696 :
1697 0 : pabyData[iDst++] = pszBytea[iSrc + 1];
1698 0 : iSrc += 2;
1699 : }
1700 : }
1701 : else
1702 : {
1703 0 : pabyData[iDst++] = pszBytea[iSrc++];
1704 : }
1705 : }
1706 1028 : if (pnLength)
1707 1028 : *pnLength = iDst;
1708 :
1709 1028 : return pabyData;
1710 : }
1711 :
1712 : /************************************************************************/
1713 : /* BYTEAToGeometry() */
1714 : /************************************************************************/
1715 :
1716 2414 : OGRGeometry *OGRPGLayer::BYTEAToGeometry(const char *pszBytea)
1717 :
1718 : {
1719 2414 : if (pszBytea == nullptr)
1720 0 : return nullptr;
1721 :
1722 2414 : int nLen = 0;
1723 2414 : GByte *pabyWKB = BYTEAToGByteArray(pszBytea, &nLen);
1724 :
1725 2414 : OGRGeometry *poGeometry = nullptr;
1726 2414 : OGRGeometryFactory::createFromWkb(pabyWKB, nullptr, &poGeometry, nLen,
1727 : wkbVariantOldOgc);
1728 :
1729 2414 : CPLFree(pabyWKB);
1730 2414 : return poGeometry;
1731 : }
1732 :
1733 : /************************************************************************/
1734 : /* GeometryToBYTEA() */
1735 : /************************************************************************/
1736 :
1737 829 : char *OGRPGLayer::GeometryToBYTEA(const OGRGeometry *poGeometry,
1738 : int nPostGISMajor, int nPostGISMinor)
1739 :
1740 : {
1741 829 : const size_t nWkbSize = poGeometry->WkbSize();
1742 :
1743 829 : GByte *pabyWKB = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nWkbSize));
1744 829 : if (pabyWKB == nullptr)
1745 0 : return CPLStrdup("");
1746 :
1747 807 : if ((nPostGISMajor > 2 || (nPostGISMajor == 2 && nPostGISMinor >= 2)) &&
1748 1636 : wkbFlatten(poGeometry->getGeometryType()) == wkbPoint &&
1749 0 : poGeometry->IsEmpty())
1750 : {
1751 0 : if (poGeometry->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso) !=
1752 : OGRERR_NONE)
1753 : {
1754 0 : CPLFree(pabyWKB);
1755 0 : return CPLStrdup("");
1756 : }
1757 : }
1758 829 : else if (poGeometry->exportToWkb(wkbNDR, pabyWKB,
1759 : (nPostGISMajor < 2)
1760 : ? wkbVariantPostGIS1
1761 829 : : wkbVariantOldOgc) != OGRERR_NONE)
1762 : {
1763 0 : CPLFree(pabyWKB);
1764 0 : return CPLStrdup("");
1765 : }
1766 :
1767 829 : char *pszTextBuf = OGRPGCommonGByteArrayToBYTEA(pabyWKB, nWkbSize);
1768 829 : CPLFree(pabyWKB);
1769 :
1770 829 : return pszTextBuf;
1771 : }
1772 :
1773 : /************************************************************************/
1774 : /* OIDToGeometry() */
1775 : /************************************************************************/
1776 :
1777 0 : OGRGeometry *OGRPGLayer::OIDToGeometry(Oid oid)
1778 :
1779 : {
1780 0 : if (oid == 0)
1781 0 : return nullptr;
1782 :
1783 0 : PGconn *hPGConn = poDS->GetPGConn();
1784 0 : const int fd = lo_open(hPGConn, oid, INV_READ);
1785 0 : if (fd < 0)
1786 0 : return nullptr;
1787 :
1788 0 : constexpr int MAX_WKB = 500000;
1789 0 : GByte *pabyWKB = static_cast<GByte *>(CPLMalloc(MAX_WKB));
1790 : const int nBytes =
1791 0 : lo_read(hPGConn, fd, reinterpret_cast<char *>(pabyWKB), MAX_WKB);
1792 0 : lo_close(hPGConn, fd);
1793 :
1794 0 : OGRGeometry *poGeometry = nullptr;
1795 0 : OGRGeometryFactory::createFromWkb(pabyWKB, nullptr, &poGeometry, nBytes,
1796 : wkbVariantOldOgc);
1797 :
1798 0 : CPLFree(pabyWKB);
1799 :
1800 0 : return poGeometry;
1801 : }
1802 :
1803 : /************************************************************************/
1804 : /* GeometryToOID() */
1805 : /************************************************************************/
1806 :
1807 0 : Oid OGRPGLayer::GeometryToOID(OGRGeometry *poGeometry)
1808 :
1809 : {
1810 0 : PGconn *hPGConn = poDS->GetPGConn();
1811 0 : const size_t nWkbSize = poGeometry->WkbSize();
1812 0 : if (nWkbSize > static_cast<size_t>(std::numeric_limits<int>::max()))
1813 : {
1814 0 : CPLError(CE_Failure, CPLE_NotSupported, "Too large geometry");
1815 0 : return 0;
1816 : }
1817 :
1818 0 : GByte *pabyWKB = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nWkbSize));
1819 0 : if (pabyWKB == nullptr)
1820 0 : return 0;
1821 0 : if (poGeometry->exportToWkb(wkbNDR, pabyWKB, wkbVariantOldOgc) !=
1822 : OGRERR_NONE)
1823 0 : return 0;
1824 :
1825 0 : Oid oid = lo_creat(hPGConn, INV_READ | INV_WRITE);
1826 :
1827 0 : const int fd = lo_open(hPGConn, oid, INV_WRITE);
1828 : const int nBytesWritten =
1829 0 : lo_write(hPGConn, fd, reinterpret_cast<char *>(pabyWKB),
1830 : static_cast<int>(nWkbSize));
1831 0 : lo_close(hPGConn, fd);
1832 :
1833 0 : if (nBytesWritten != static_cast<int>(nWkbSize))
1834 : {
1835 0 : CPLDebug("PG",
1836 : "Only wrote %d bytes of %d intended for (fd=%d,oid=%d).\n",
1837 : nBytesWritten, static_cast<int>(nWkbSize), fd, oid);
1838 : }
1839 :
1840 0 : CPLFree(pabyWKB);
1841 :
1842 0 : return oid;
1843 : }
1844 :
1845 : /************************************************************************/
1846 : /* StartTransaction() */
1847 : /************************************************************************/
1848 :
1849 27 : OGRErr OGRPGLayer::StartTransaction()
1850 :
1851 : {
1852 27 : return poDS->StartTransaction();
1853 : }
1854 :
1855 : /************************************************************************/
1856 : /* CommitTransaction() */
1857 : /************************************************************************/
1858 :
1859 17 : OGRErr OGRPGLayer::CommitTransaction()
1860 :
1861 : {
1862 17 : return poDS->CommitTransaction();
1863 : }
1864 :
1865 : /************************************************************************/
1866 : /* RollbackTransaction() */
1867 : /************************************************************************/
1868 :
1869 10 : OGRErr OGRPGLayer::RollbackTransaction()
1870 :
1871 : {
1872 10 : return poDS->RollbackTransaction();
1873 : }
1874 :
1875 : /************************************************************************/
1876 : /* GetFIDColumn() */
1877 : /************************************************************************/
1878 :
1879 42 : const char *OGRPGLayer::GetFIDColumn() const
1880 :
1881 : {
1882 42 : GetLayerDefn();
1883 :
1884 42 : if (pszFIDColumn != nullptr)
1885 42 : return pszFIDColumn;
1886 : else
1887 0 : return "";
1888 : }
1889 :
1890 : /************************************************************************/
1891 : /* IGetExtent() */
1892 : /* */
1893 : /* For PostGIS use internal Extend(geometry) function */
1894 : /* in other cases we use standard OGRLayer::GetExtent() */
1895 : /************************************************************************/
1896 :
1897 30 : OGRErr OGRPGLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
1898 : bool bForce)
1899 : {
1900 60 : CPLString osCommand;
1901 :
1902 : OGRPGGeomFieldDefn *poGeomFieldDefn =
1903 30 : poFeatureDefn->GetGeomFieldDefn(iGeomField);
1904 :
1905 30 : if (TestCapability(OLCFastGetExtent))
1906 : {
1907 : /* Do not take the spatial filter into account */
1908 : osCommand.Printf(
1909 : "SELECT ST_Extent(%s) FROM %s AS ogrpgextent",
1910 38 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(),
1911 57 : GetFromClauseForGetExtent().c_str());
1912 : }
1913 11 : else if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY)
1914 : {
1915 : /* Probably not very efficient, but more efficient than client-side
1916 : * implementation */
1917 : osCommand.Printf(
1918 : "SELECT ST_Extent(ST_GeomFromWKB(ST_AsBinary(%s))) FROM %s AS "
1919 : "ogrpgextent",
1920 4 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(),
1921 6 : GetFromClauseForGetExtent().c_str());
1922 : }
1923 :
1924 30 : if (!osCommand.empty())
1925 : {
1926 21 : if (RunGetExtentRequest(*psExtent, bForce, osCommand, FALSE) ==
1927 : OGRERR_NONE)
1928 21 : return OGRERR_NONE;
1929 : }
1930 :
1931 9 : return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
1932 : }
1933 :
1934 3 : OGRErr OGRPGLayer::IGetExtent3D(int iGeomField, OGREnvelope3D *psExtent3D,
1935 : bool bForce)
1936 : {
1937 3 : auto poLayerDefn = GetLayerDefn();
1938 :
1939 : // If the geometry field is not 3D go for 2D
1940 6 : if (poLayerDefn->GetGeomFieldCount() > iGeomField &&
1941 3 : !OGR_GT_HasZ(CPLAssertNotNull(poLayerDefn->GetGeomFieldDefn(iGeomField))
1942 : ->GetType()))
1943 : {
1944 1 : const OGRErr retVal{GetExtent(iGeomField, psExtent3D, bForce)};
1945 1 : psExtent3D->MinZ = std::numeric_limits<double>::infinity();
1946 1 : psExtent3D->MaxZ = -std::numeric_limits<double>::infinity();
1947 1 : return retVal;
1948 : }
1949 :
1950 4 : CPLString osCommand;
1951 :
1952 : const OGRPGGeomFieldDefn *poGeomFieldDefn =
1953 2 : poLayerDefn->GetGeomFieldDefn(iGeomField);
1954 :
1955 2 : if (TestCapability(OLCFastGetExtent3D))
1956 : {
1957 : /* Do not take the spatial filter into account */
1958 : osCommand.Printf(
1959 : "SELECT ST_Extent(%s) FROM %s AS ogrpgextent",
1960 2 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(),
1961 3 : GetFromClauseForGetExtent().c_str());
1962 : }
1963 1 : else if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY)
1964 : {
1965 : /* Probably not very efficient, but more efficient than client-side
1966 : * implementation */
1967 : osCommand.Printf(
1968 : "SELECT ST_Extent(ST_GeomFromWKB(ST_AsBinary(%s))) FROM %s AS "
1969 : "ogrpgextent",
1970 2 : OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(),
1971 3 : GetFromClauseForGetExtent().c_str());
1972 : }
1973 :
1974 2 : if (!osCommand.empty())
1975 : {
1976 2 : if (RunGetExtent3DRequest(*psExtent3D, osCommand, FALSE) == OGRERR_NONE)
1977 0 : return OGRERR_NONE;
1978 : }
1979 :
1980 2 : return OGRLayer::IGetExtent3D(iGeomField, psExtent3D, bForce);
1981 : }
1982 :
1983 : /************************************************************************/
1984 : /* GetExtent() */
1985 : /************************************************************************/
1986 :
1987 22 : OGRErr OGRPGLayer::RunGetExtentRequest(OGREnvelope &sExtent,
1988 : CPL_UNUSED int bForce,
1989 : const std::string &osCommand,
1990 : int bErrorAsDebug)
1991 : {
1992 22 : PGconn *hPGConn = poDS->GetPGConn();
1993 : PGresult *hResult =
1994 22 : OGRPG_PQexec(hPGConn, osCommand.c_str(), FALSE, bErrorAsDebug);
1995 44 : if (!hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK ||
1996 22 : PQgetisnull(hResult, 0, 0))
1997 : {
1998 0 : OGRPGClearResult(hResult);
1999 0 : CPLDebug("PG", "Unable to get extent by PostGIS.");
2000 0 : return OGRERR_FAILURE;
2001 : }
2002 :
2003 22 : char *pszBox = PQgetvalue(hResult, 0, 0);
2004 : char *ptr, *ptrEndParenthesis;
2005 : char szVals[64 * 6 + 6];
2006 :
2007 22 : ptr = strchr(pszBox, '(');
2008 22 : if (ptr)
2009 22 : ptr++;
2010 44 : if (ptr == nullptr || (ptrEndParenthesis = strchr(ptr, ')')) == nullptr ||
2011 22 : ptrEndParenthesis - ptr > static_cast<int>(sizeof(szVals) - 1))
2012 : {
2013 0 : CPLError(CE_Failure, CPLE_IllegalArg, "Bad extent representation: '%s'",
2014 : pszBox);
2015 :
2016 0 : OGRPGClearResult(hResult);
2017 0 : return OGRERR_FAILURE;
2018 : }
2019 :
2020 22 : strncpy(szVals, ptr, ptrEndParenthesis - ptr);
2021 22 : szVals[ptrEndParenthesis - ptr] = '\0';
2022 :
2023 : const CPLStringList aosTokens(
2024 44 : CSLTokenizeString2(szVals, " ,", CSLT_HONOURSTRINGS));
2025 22 : constexpr int nTokenCnt = 4;
2026 :
2027 22 : if (aosTokens.size() != nTokenCnt)
2028 : {
2029 0 : CPLError(CE_Failure, CPLE_IllegalArg, "Bad extent representation: '%s'",
2030 : pszBox);
2031 :
2032 0 : OGRPGClearResult(hResult);
2033 0 : return OGRERR_FAILURE;
2034 : }
2035 :
2036 : // Take X,Y coords
2037 : // For PostGIS ver >= 1.0.0 -> Tokens: X1 Y1 X2 Y2 (nTokenCnt = 4)
2038 : // For PostGIS ver < 1.0.0 -> Tokens: X1 Y1 Z1 X2 Y2 Z2 (nTokenCnt = 6)
2039 : // => X2 index calculated as nTokenCnt/2
2040 : // Y2 index calculated as nTokenCnt/2+1
2041 :
2042 22 : sExtent.MinX = CPLAtof(aosTokens[0]);
2043 22 : sExtent.MinY = CPLAtof(aosTokens[1]);
2044 22 : sExtent.MaxX = CPLAtof(aosTokens[nTokenCnt / 2]);
2045 22 : sExtent.MaxY = CPLAtof(aosTokens[nTokenCnt / 2 + 1]);
2046 :
2047 22 : OGRPGClearResult(hResult);
2048 :
2049 22 : return OGRERR_NONE;
2050 : }
2051 :
2052 2 : OGRErr OGRPGLayer::RunGetExtent3DRequest(OGREnvelope3D &sExtent3D,
2053 : const std::string &osCommand,
2054 : int bErrorAsDebug)
2055 : {
2056 2 : PGconn *hPGConn = poDS->GetPGConn();
2057 : PGresult *hResult =
2058 2 : OGRPG_PQexec(hPGConn, osCommand.c_str(), FALSE, bErrorAsDebug);
2059 4 : if (!hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK ||
2060 2 : PQgetisnull(hResult, 0, 0))
2061 : {
2062 0 : OGRPGClearResult(hResult);
2063 0 : CPLDebug("PG", "Unable to get extent 3D by PostGIS.");
2064 0 : return OGRERR_FAILURE;
2065 : }
2066 :
2067 2 : char *pszBox = PQgetvalue(hResult, 0, 0);
2068 : char *ptr, *ptrEndParenthesis;
2069 : char szVals[64 * 6 + 6];
2070 :
2071 2 : ptr = strchr(pszBox, '(');
2072 2 : if (ptr)
2073 2 : ptr++;
2074 4 : if (ptr == nullptr || (ptrEndParenthesis = strchr(ptr, ')')) == nullptr ||
2075 2 : ptrEndParenthesis - ptr > static_cast<int>(sizeof(szVals) - 1))
2076 : {
2077 0 : CPLError(CE_Failure, CPLE_IllegalArg, "Bad extent representation: '%s'",
2078 : pszBox);
2079 :
2080 0 : OGRPGClearResult(hResult);
2081 0 : return OGRERR_FAILURE;
2082 : }
2083 :
2084 2 : strncpy(szVals, ptr, ptrEndParenthesis - ptr);
2085 2 : szVals[ptrEndParenthesis - ptr] = '\0';
2086 :
2087 2 : char **papszTokens = CSLTokenizeString2(szVals, " ,", CSLT_HONOURSTRINGS);
2088 2 : if (CSLCount(papszTokens) != 6)
2089 : {
2090 2 : CPLError(CE_Failure, CPLE_IllegalArg,
2091 : "Bad extent 3D representation: '%s'", pszBox);
2092 2 : CSLDestroy(papszTokens);
2093 :
2094 2 : OGRPGClearResult(hResult);
2095 2 : return OGRERR_FAILURE;
2096 : }
2097 :
2098 0 : sExtent3D.MinX = CPLAtof(papszTokens[0]);
2099 0 : sExtent3D.MinY = CPLAtof(papszTokens[1]);
2100 0 : sExtent3D.MinZ = CPLAtof(papszTokens[2]);
2101 0 : sExtent3D.MaxX = CPLAtof(papszTokens[3]);
2102 0 : sExtent3D.MaxY = CPLAtof(papszTokens[4]);
2103 0 : sExtent3D.MaxZ = CPLAtof(papszTokens[5]);
2104 :
2105 0 : CSLDestroy(papszTokens);
2106 0 : OGRPGClearResult(hResult);
2107 :
2108 0 : return OGRERR_NONE;
2109 : }
2110 :
2111 : /************************************************************************/
2112 : /* ReadResultDefinition() */
2113 : /* */
2114 : /* Build a schema from the current resultset. */
2115 : /************************************************************************/
2116 :
2117 216 : int OGRPGLayer::ReadResultDefinition(PGresult *hInitialResultIn)
2118 :
2119 : {
2120 216 : PGresult *hResult = hInitialResultIn;
2121 :
2122 : /* -------------------------------------------------------------------- */
2123 : /* Parse the returned table information. */
2124 : /* -------------------------------------------------------------------- */
2125 216 : poFeatureDefn = new OGRPGFeatureDefn("sql_statement");
2126 216 : SetDescription(poFeatureDefn->GetName());
2127 :
2128 216 : poFeatureDefn->Reference();
2129 :
2130 727 : for (int iRawField = 0; iRawField < PQnfields(hResult); iRawField++)
2131 : {
2132 511 : OGRFieldDefn oField(PQfname(hResult, iRawField), OFTString);
2133 511 : const Oid nTypeOID = PQftype(hResult, iRawField);
2134 :
2135 511 : int iGeomFuncPrefix = 0;
2136 511 : if (EQUAL(oField.GetNameRef(), "ogc_fid"))
2137 : {
2138 41 : if (pszFIDColumn)
2139 : {
2140 0 : CPLError(CE_Warning, CPLE_AppDefined,
2141 : "More than one ogc_fid column was found in the result "
2142 : "of the SQL request. Only last one will be used");
2143 : }
2144 41 : CPLFree(pszFIDColumn);
2145 41 : pszFIDColumn = CPLStrdup(oField.GetNameRef());
2146 41 : continue;
2147 : }
2148 470 : else if ((iGeomFuncPrefix =
2149 891 : OGRPGIsKnownGeomFuncPrefix(oField.GetNameRef())) >= 0 ||
2150 891 : nTypeOID == poDS->GetGeometryOID() ||
2151 367 : nTypeOID == poDS->GetGeographyOID())
2152 : {
2153 : auto poGeomFieldDefn =
2154 208 : std::make_unique<OGRPGGeomFieldDefn>(this, oField.GetNameRef());
2155 153 : if (iGeomFuncPrefix >= 0 &&
2156 49 : oField.GetNameRef()[strlen(
2157 49 : apszKnownGeomFuncPrefixes[iGeomFuncPrefix])] == '_')
2158 : {
2159 0 : poGeomFieldDefn->SetName(
2160 0 : oField.GetNameRef() +
2161 0 : strlen(apszKnownGeomFuncPrefixes[iGeomFuncPrefix]) + 1);
2162 : }
2163 104 : if (nTypeOID == poDS->GetGeographyOID())
2164 : {
2165 1 : poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOGRAPHY;
2166 1 : if (!(poDS->sPostGISVersion.nMajor >= 3 ||
2167 0 : (poDS->sPostGISVersion.nMajor == 2 &&
2168 0 : poDS->sPostGISVersion.nMinor >= 2)))
2169 : {
2170 : // EPSG:4326 was a requirement for geography before
2171 : // PostGIS 2.2
2172 0 : poGeomFieldDefn->nSRSId = 4326;
2173 : }
2174 : }
2175 : else
2176 103 : poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOMETRY;
2177 104 : poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
2178 104 : continue;
2179 : }
2180 366 : else if (EQUAL(oField.GetNameRef(), "WKB_GEOMETRY"))
2181 : {
2182 17 : if (nTypeOID == OIDOID)
2183 0 : bWkbAsOid = TRUE;
2184 : auto poGeomFieldDefn =
2185 34 : std::make_unique<OGRPGGeomFieldDefn>(this, oField.GetNameRef());
2186 17 : poGeomFieldDefn->ePostgisType = GEOM_TYPE_WKB;
2187 17 : poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
2188 17 : continue;
2189 : }
2190 :
2191 : // CPLDebug("PG", "Field %s, oid %d", oField.GetNameRef(), nTypeOID);
2192 :
2193 349 : if (nTypeOID == BYTEAOID)
2194 : {
2195 2 : oField.SetType(OFTBinary);
2196 : }
2197 347 : else if (nTypeOID == CHAROID || nTypeOID == TEXTOID ||
2198 283 : nTypeOID == BPCHAROID || nTypeOID == VARCHAROID)
2199 : {
2200 127 : oField.SetType(OFTString);
2201 :
2202 : /* See
2203 : * http://www.mail-archive.com/pgsql-hackers@postgresql.org/msg57726.html
2204 : */
2205 : /* nTypmod = width + 4 */
2206 127 : int nTypmod = PQfmod(hResult, iRawField);
2207 127 : if (nTypmod >= 4 &&
2208 30 : (nTypeOID == BPCHAROID || nTypeOID == VARCHAROID))
2209 : {
2210 32 : oField.SetWidth(nTypmod - 4);
2211 127 : }
2212 : }
2213 220 : else if (nTypeOID == BOOLOID)
2214 : {
2215 6 : oField.SetType(OFTInteger);
2216 6 : oField.SetSubType(OFSTBoolean);
2217 6 : oField.SetWidth(1);
2218 : }
2219 214 : else if (nTypeOID == INT2OID)
2220 : {
2221 2 : oField.SetType(OFTInteger);
2222 2 : oField.SetSubType(OFSTInt16);
2223 2 : oField.SetWidth(5);
2224 : }
2225 212 : else if (nTypeOID == INT4OID)
2226 : {
2227 51 : oField.SetType(OFTInteger);
2228 : }
2229 161 : else if (nTypeOID == INT8OID)
2230 : {
2231 14 : oField.SetType(OFTInteger64);
2232 : }
2233 147 : else if (nTypeOID == FLOAT4OID)
2234 : {
2235 4 : oField.SetType(OFTReal);
2236 4 : oField.SetSubType(OFSTFloat32);
2237 : }
2238 143 : else if (nTypeOID == FLOAT8OID)
2239 : {
2240 32 : oField.SetType(OFTReal);
2241 : }
2242 111 : else if (nTypeOID == NUMERICOID || nTypeOID == NUMERICARRAYOID)
2243 : {
2244 : /* See
2245 : * http://www.mail-archive.com/pgsql-hackers@postgresql.org/msg57726.html
2246 : */
2247 : /* typmod = (width << 16) + precision + 4 */
2248 12 : int nTypmod = PQfmod(hResult, iRawField);
2249 12 : if (nTypmod >= 4)
2250 : {
2251 8 : int nWidth = (nTypmod - 4) >> 16;
2252 8 : int nPrecision = (nTypmod - 4) & 0xFFFF;
2253 8 : if (nWidth <= 10 && nPrecision == 0)
2254 : {
2255 4 : oField.SetType((nTypeOID == NUMERICOID) ? OFTInteger
2256 : : OFTIntegerList);
2257 4 : oField.SetWidth(nWidth);
2258 : }
2259 : else
2260 : {
2261 4 : oField.SetType((nTypeOID == NUMERICOID) ? OFTReal
2262 : : OFTRealList);
2263 4 : oField.SetWidth(nWidth);
2264 4 : oField.SetPrecision(nPrecision);
2265 : }
2266 : }
2267 : else
2268 4 : oField.SetType((nTypeOID == NUMERICOID) ? OFTReal
2269 12 : : OFTRealList);
2270 : }
2271 99 : else if (nTypeOID == BOOLARRAYOID)
2272 : {
2273 2 : oField.SetType(OFTIntegerList);
2274 2 : oField.SetSubType(OFSTBoolean);
2275 2 : oField.SetWidth(1);
2276 : }
2277 97 : else if (nTypeOID == INT2ARRAYOID)
2278 : {
2279 2 : oField.SetType(OFTIntegerList);
2280 2 : oField.SetSubType(OFSTInt16);
2281 : }
2282 95 : else if (nTypeOID == INT4ARRAYOID)
2283 : {
2284 2 : oField.SetType(OFTIntegerList);
2285 : }
2286 93 : else if (nTypeOID == INT8ARRAYOID)
2287 : {
2288 2 : oField.SetType(OFTInteger64List);
2289 : }
2290 91 : else if (nTypeOID == FLOAT4ARRAYOID)
2291 : {
2292 2 : oField.SetType(OFTRealList);
2293 2 : oField.SetSubType(OFSTFloat32);
2294 : }
2295 89 : else if (nTypeOID == FLOAT8ARRAYOID)
2296 : {
2297 30 : oField.SetType(OFTRealList);
2298 : }
2299 59 : else if (nTypeOID == TEXTARRAYOID || nTypeOID == BPCHARARRAYOID ||
2300 : nTypeOID == VARCHARARRAYOID)
2301 : {
2302 6 : oField.SetType(OFTStringList);
2303 : }
2304 53 : else if (nTypeOID == DATEOID)
2305 : {
2306 2 : oField.SetType(OFTDate);
2307 : }
2308 51 : else if (nTypeOID == TIMEOID)
2309 : {
2310 2 : oField.SetType(OFTTime);
2311 : }
2312 49 : else if (nTypeOID == TIMESTAMPOID || nTypeOID == TIMESTAMPTZOID)
2313 : {
2314 : #if defined(BINARY_CURSOR_ENABLED)
2315 : /* We can't deserialize properly timestamp with time zone */
2316 : /* with binary cursors */
2317 : if (nTypeOID == TIMESTAMPTZOID)
2318 : bCanUseBinaryCursor = FALSE;
2319 : #endif
2320 :
2321 4 : oField.SetType(OFTDateTime);
2322 : }
2323 45 : else if (nTypeOID == JSONOID || nTypeOID == JSONBOID)
2324 : {
2325 2 : oField.SetType(OFTString);
2326 2 : oField.SetSubType(OFSTJSON);
2327 : }
2328 43 : else if (nTypeOID == UUIDOID)
2329 : {
2330 0 : oField.SetType(OFTString);
2331 0 : oField.SetSubType(OFSTUUID);
2332 : }
2333 : else /* unknown type */
2334 : {
2335 43 : CPLDebug("PG",
2336 : "Unhandled OID (%d) for column %s. Defaulting to String.",
2337 : nTypeOID, oField.GetNameRef());
2338 43 : oField.SetType(OFTString);
2339 : }
2340 :
2341 349 : poFeatureDefn->AddFieldDefn(&oField);
2342 : }
2343 :
2344 216 : return TRUE;
2345 : }
2346 :
2347 : /************************************************************************/
2348 : /* GetSpatialRef() */
2349 : /************************************************************************/
2350 :
2351 3201 : const OGRSpatialReference *OGRPGGeomFieldDefn::GetSpatialRef() const
2352 : {
2353 3201 : if (poLayer == nullptr)
2354 0 : return nullptr;
2355 3201 : if (nSRSId == UNDETERMINED_SRID)
2356 1025 : poLayer->ResolveSRID(this);
2357 :
2358 3201 : if (poSRS == nullptr && nSRSId > 0)
2359 : {
2360 33 : poSRS = poLayer->GetDS()->FetchSRS(nSRSId);
2361 33 : if (poSRS != nullptr)
2362 33 : const_cast<OGRSpatialReference *>(poSRS)->Reference();
2363 : }
2364 3201 : return poSRS;
2365 : }
2366 :
2367 : /************************************************************************/
2368 : /* GetDataset() */
2369 : /************************************************************************/
2370 :
2371 2 : GDALDataset *OGRPGLayer::GetDataset()
2372 : {
2373 2 : return poDS;
2374 : }
|