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

Generated by: LCOV version 1.14