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