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

Generated by: LCOV version 1.14