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