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