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