LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/pg - ogrpglayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 600 729 82.3 %
Date: 2026-11-13 18:19:58 Functions: 27 30 90.0 %

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

Generated by: LCOV version 1.14