LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/pg - ogrpgtablelayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1680 1915 87.7 %
Date: 2025-07-09 17:50:03 Functions: 67 67 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             : 
       3             :  *
       4             :  * Project:  OpenGIS Simple Features Reference Implementation
       5             :  * Purpose:  Implements OGRPGTableLayer class, access to an existing table.
       6             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2000, Frank Warmerdam
      10             :  * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "ogr_pg.h"
      16             : #include "cpl_conv.h"
      17             : #include "cpl_string.h"
      18             : #include "cpl_error.h"
      19             : #include "ogr_p.h"
      20             : 
      21             : #include <chrono>
      22             : #include <condition_variable>
      23             : #include <mutex>
      24             : #include <thread>
      25             : 
      26             : #define PQexec this_is_an_error
      27             : 
      28             : #define UNSUPPORTED_OP_READ_ONLY                                               \
      29             :     "%s : unsupported operation on a read-only datasource."
      30             : 
      31         951 : void OGRPGFeatureDefn::UnsetLayer()
      32             : {
      33         951 :     const int nGeomFieldCount = GetGeomFieldCount();
      34        1515 :     for (int i = 0; i < nGeomFieldCount; i++)
      35         564 :         cpl::down_cast<OGRPGGeomFieldDefn *>(apoGeomFieldDefn[i].get())
      36         564 :             ->UnsetLayer();
      37         951 : }
      38             : 
      39             : /************************************************************************/
      40             : /*                        OGRPGTableFeatureDefn                         */
      41             : /************************************************************************/
      42             : 
      43             : class OGRPGTableFeatureDefn final : public OGRPGFeatureDefn
      44             : {
      45             :   private:
      46             :     OGRPGTableFeatureDefn(const OGRPGTableFeatureDefn &) = delete;
      47             :     OGRPGTableFeatureDefn &operator=(const OGRPGTableFeatureDefn &) = delete;
      48             : 
      49             :     OGRPGTableLayer *poLayer = nullptr;
      50             : 
      51             :     void SolveFields() const;
      52             : 
      53             :   public:
      54         735 :     explicit OGRPGTableFeatureDefn(OGRPGTableLayer *poLayerIn,
      55             :                                    const char *pszName = nullptr)
      56         735 :         : OGRPGFeatureDefn(pszName), poLayer(poLayerIn)
      57             :     {
      58         735 :     }
      59             : 
      60             :     virtual void UnsetLayer() override;
      61             : 
      62      133372 :     virtual int GetFieldCount() const override
      63             :     {
      64      133372 :         SolveFields();
      65      133372 :         return OGRPGFeatureDefn::GetFieldCount();
      66             :     }
      67             : 
      68       50788 :     virtual OGRFieldDefn *GetFieldDefn(int i) override
      69             :     {
      70       50788 :         SolveFields();
      71       50788 :         return OGRPGFeatureDefn::GetFieldDefn(i);
      72             :     }
      73             : 
      74       36411 :     virtual const OGRFieldDefn *GetFieldDefn(int i) const override
      75             :     {
      76       36411 :         SolveFields();
      77       36411 :         return OGRPGFeatureDefn::GetFieldDefn(i);
      78             :     }
      79             : 
      80        7882 :     virtual int GetFieldIndex(const char *pszName) const override
      81             :     {
      82        7882 :         SolveFields();
      83        7882 :         return OGRPGFeatureDefn::GetFieldIndex(pszName);
      84             :     }
      85             : 
      86       56481 :     virtual int GetGeomFieldCount() const override
      87             :     {
      88       56481 :         if (poLayer != nullptr && !poLayer->HasGeometryInformation())
      89        1182 :             SolveFields();
      90       56481 :         return OGRPGFeatureDefn::GetGeomFieldCount();
      91             :     }
      92             : 
      93       14649 :     virtual OGRPGGeomFieldDefn *GetGeomFieldDefn(int i) override
      94             :     {
      95       14649 :         if (poLayer != nullptr && !poLayer->HasGeometryInformation())
      96         162 :             SolveFields();
      97       14649 :         return OGRPGFeatureDefn::GetGeomFieldDefn(i);
      98             :     }
      99             : 
     100        1111 :     virtual const OGRPGGeomFieldDefn *GetGeomFieldDefn(int i) const override
     101             :     {
     102        1111 :         if (poLayer != nullptr && !poLayer->HasGeometryInformation())
     103           0 :             SolveFields();
     104        1111 :         return OGRPGFeatureDefn::GetGeomFieldDefn(i);
     105             :     }
     106             : 
     107         891 :     virtual int GetGeomFieldIndex(const char *pszName) const override
     108             :     {
     109         891 :         if (poLayer != nullptr && !poLayer->HasGeometryInformation())
     110           0 :             SolveFields();
     111         891 :         return OGRPGFeatureDefn::GetGeomFieldIndex(pszName);
     112             :     }
     113             : };
     114             : 
     115         735 : void OGRPGTableFeatureDefn::UnsetLayer()
     116             : {
     117         735 :     poLayer = nullptr;
     118         735 :     OGRPGFeatureDefn::UnsetLayer();
     119         735 : }
     120             : 
     121             : /************************************************************************/
     122             : /*                           SolveFields()                              */
     123             : /************************************************************************/
     124             : 
     125      229797 : void OGRPGTableFeatureDefn::SolveFields() const
     126             : {
     127      229797 :     if (poLayer == nullptr)
     128           0 :         return;
     129             : 
     130      229797 :     poLayer->ReadTableDefinition();
     131             : }
     132             : 
     133             : /************************************************************************/
     134             : /*                            GetFIDColumn()                            */
     135             : /************************************************************************/
     136             : 
     137          86 : const char *OGRPGTableLayer::GetFIDColumn()
     138             : 
     139             : {
     140          86 :     ReadTableDefinition();
     141             : 
     142          86 :     if (pszFIDColumn != nullptr)
     143          84 :         return pszFIDColumn;
     144             :     else
     145           2 :         return "";
     146             : }
     147             : 
     148             : /************************************************************************/
     149             : /*                          OGRPGTableLayer()                           */
     150             : /************************************************************************/
     151             : 
     152         735 : OGRPGTableLayer::OGRPGTableLayer(OGRPGDataSource *poDSIn,
     153             :                                  CPLString &osCurrentSchema,
     154             :                                  const char *pszTableNameIn,
     155             :                                  const char *pszSchemaNameIn,
     156             :                                  const char *pszDescriptionIn,
     157         735 :                                  const char *pszGeomColForcedIn, int bUpdate)
     158        1470 :     : bUpdateAccess(bUpdate), pszTableName(CPLStrdup(pszTableNameIn)),
     159         735 :       pszSchemaName(CPLStrdup(pszSchemaNameIn ? pszSchemaNameIn
     160         386 :                                               : osCurrentSchema.c_str())),
     161         735 :       m_pszTableDescription(pszDescriptionIn ? CPLStrdup(pszDescriptionIn)
     162             :                                              : nullptr),
     163             :       osPrimaryKey(CPLGetConfigOption("PGSQL_OGR_FID", "ogc_fid")),
     164         735 :       pszGeomColForced(pszGeomColForcedIn ? CPLStrdup(pszGeomColForcedIn)
     165             :                                           : nullptr),
     166             :       // Just in provision for people yelling about broken backward
     167             :       // compatibility.
     168             :       bRetrieveFID(
     169         735 :           CPLTestBool(CPLGetConfigOption("OGR_PG_RETRIEVE_FID", "TRUE"))),
     170             :       bSkipConflicts(
     171        3675 :           CPLTestBool(CPLGetConfigOption("OGR_PG_SKIP_CONFLICTS", "FALSE")))
     172             : {
     173         735 :     poDS = poDSIn;
     174         735 :     pszQueryStatement = nullptr;
     175             : 
     176             :     /* -------------------------------------------------------------------- */
     177             :     /*      Build the layer defn name.                                      */
     178             :     /* -------------------------------------------------------------------- */
     179        1470 :     CPLString osDefnName;
     180         735 :     if (pszSchemaNameIn && osCurrentSchema != pszSchemaNameIn)
     181             :     {
     182          53 :         osDefnName.Printf("%s.%s", pszSchemaNameIn, pszTableName);
     183         159 :         pszSqlTableName = CPLStrdup(CPLString().Printf(
     184         106 :             "%s.%s", OGRPGEscapeColumnName(pszSchemaNameIn).c_str(),
     185         159 :             OGRPGEscapeColumnName(pszTableName).c_str()));
     186             :     }
     187             :     else
     188             :     {
     189             :         // no prefix for current_schema in layer name, for backwards
     190             :         // compatibility.
     191         682 :         osDefnName = pszTableName;
     192         682 :         pszSqlTableName = CPLStrdup(OGRPGEscapeColumnName(pszTableName));
     193             :     }
     194         735 :     if (pszGeomColForced != nullptr)
     195             :     {
     196           7 :         osDefnName += "(";
     197           7 :         osDefnName += pszGeomColForced;
     198           7 :         osDefnName += ")";
     199             :     }
     200             : 
     201         735 :     poFeatureDefn = new OGRPGTableFeatureDefn(this, osDefnName);
     202         735 :     SetDescription(poFeatureDefn->GetName());
     203         735 :     poFeatureDefn->Reference();
     204             : 
     205             :     // bSealFields = false because we do lazy resolution of fields
     206         735 :     poFeatureDefn->Seal(/* bSealFields = */ false);
     207             : 
     208         735 :     if (pszDescriptionIn != nullptr && !EQUAL(pszDescriptionIn, ""))
     209             :     {
     210           2 :         OGRLayer::SetMetadataItem("DESCRIPTION", pszDescriptionIn);
     211             :     }
     212         735 : }
     213             : 
     214             : //************************************************************************/
     215             : /*                          ~OGRPGTableLayer()                          */
     216             : /************************************************************************/
     217             : 
     218        1470 : OGRPGTableLayer::~OGRPGTableLayer()
     219             : 
     220             : {
     221         735 :     if (bDeferredCreation)
     222           0 :         RunDeferredCreationIfNecessary();
     223         735 :     if (bCopyActive)
     224           0 :         EndCopy();
     225         735 :     UpdateSequenceIfNeeded();
     226         735 :     SerializeMetadata();
     227             : 
     228         735 :     CPLFree(pszSqlTableName);
     229         735 :     CPLFree(pszTableName);
     230         735 :     CPLFree(pszSqlGeomParentTableName);
     231         735 :     CPLFree(pszSchemaName);
     232         735 :     CPLFree(m_pszTableDescription);
     233         735 :     CPLFree(pszGeomColForced);
     234         735 :     CSLDestroy(papszOverrideColumnTypes);
     235        1470 : }
     236             : 
     237             : /************************************************************************/
     238             : /*                              LoadMetadata()                          */
     239             : /************************************************************************/
     240             : 
     241         122 : void OGRPGTableLayer::LoadMetadata()
     242             : {
     243         122 :     if (m_bMetadataLoaded)
     244         104 :         return;
     245          37 :     m_bMetadataLoaded = true;
     246             : 
     247          37 :     if (!poDS->HasOgrSystemTablesMetadataTable())
     248          19 :         return;
     249             : 
     250          18 :     PGconn *hPGConn = poDS->GetPGConn();
     251             : 
     252             :     const std::string osSQL(
     253             :         CPLSPrintf("SELECT metadata FROM ogr_system_tables.metadata WHERE "
     254             :                    "schema_name = %s AND table_name = %s",
     255          36 :                    OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
     256          72 :                    OGRPGEscapeString(hPGConn, pszTableName).c_str()));
     257          18 :     auto poSqlLyr = poDS->ExecuteSQL(osSQL.c_str(), nullptr, nullptr);
     258          18 :     if (poSqlLyr)
     259             :     {
     260             :         auto poFeature =
     261          36 :             std::unique_ptr<OGRFeature>(poSqlLyr->GetNextFeature());
     262          18 :         if (poFeature)
     263             :         {
     264           2 :             if (poFeature->IsFieldSetAndNotNull(0))
     265             :             {
     266           2 :                 const char *pszXML = poFeature->GetFieldAsString(0);
     267           2 :                 if (pszXML)
     268             :                 {
     269           2 :                     auto psRoot = CPLParseXMLString(pszXML);
     270           2 :                     if (psRoot)
     271             :                     {
     272           2 :                         oMDMD.XMLInit(psRoot, true);
     273           2 :                         CPLDestroyXMLNode(psRoot);
     274             :                     }
     275             :                 }
     276             :             }
     277             :         }
     278          18 :         poDS->ReleaseResultSet(poSqlLyr);
     279             :     }
     280             : }
     281             : 
     282             : /************************************************************************/
     283             : /*                         SerializeMetadata()                          */
     284             : /************************************************************************/
     285             : 
     286         735 : void OGRPGTableLayer::SerializeMetadata()
     287             : {
     288         752 :     if (!m_bMetadataModified ||
     289          17 :         !CPLTestBool(CPLGetConfigOption("OGR_PG_ENABLE_METADATA", "YES")))
     290             :     {
     291         719 :         return;
     292             :     }
     293             : 
     294          16 :     PGconn *hPGConn = poDS->GetPGConn();
     295          16 :     CPLXMLNode *psMD = oMDMD.Serialize();
     296             : 
     297          16 :     if (psMD)
     298             :     {
     299             :         // Remove DESCRIPTION and OLMD_FID64 items from metadata
     300             : 
     301          12 :         CPLXMLNode *psPrev = nullptr;
     302          24 :         for (CPLXMLNode *psIter = psMD; psIter;)
     303             :         {
     304          12 :             CPLXMLNode *psNext = psIter->psNext;
     305          36 :             if (psIter->eType == CXT_Element &&
     306          24 :                 strcmp(psIter->pszValue, "Metadata") == 0 &&
     307          12 :                 CPLGetXMLNode(psIter, "domain") == nullptr)
     308             :             {
     309          12 :                 bool bFoundInterestingItems = false;
     310          28 :                 for (CPLXMLNode *psIter2 = psIter->psChild; psIter2;)
     311             :                 {
     312          16 :                     CPLXMLNode *psNext2 = psIter2->psNext;
     313          48 :                     if (psIter2->eType == CXT_Element &&
     314          32 :                         strcmp(psIter2->pszValue, "MDI") == 0 &&
     315          16 :                         (EQUAL(CPLGetXMLValue(psIter2, "key", ""),
     316          14 :                                OLMD_FID64) ||
     317          14 :                          EQUAL(CPLGetXMLValue(psIter2, "key", ""),
     318             :                                "DESCRIPTION")))
     319             :                     {
     320          10 :                         CPLRemoveXMLChild(psIter, psIter2);
     321             :                     }
     322             :                     else
     323             :                     {
     324           6 :                         bFoundInterestingItems = true;
     325             :                     }
     326          16 :                     psIter2 = psNext2;
     327             :                 }
     328          12 :                 if (!bFoundInterestingItems)
     329             :                 {
     330           8 :                     if (psPrev)
     331           0 :                         psPrev->psNext = psNext;
     332             :                     else
     333           8 :                         psMD = psNext;
     334           8 :                     psIter->psNext = nullptr;
     335           8 :                     CPLDestroyXMLNode(psIter);
     336             :                 }
     337             :             }
     338          12 :             psIter = psNext;
     339          12 :             psPrev = psIter;
     340             :         }
     341             :     }
     342             : 
     343          16 :     const bool bIsUserTransactionActive = poDS->IsUserTransactionActive();
     344             :     {
     345          16 :         PGresult *hResult = OGRPG_PQexec(
     346             :             hPGConn, bIsUserTransactionActive
     347             :                          ? "SAVEPOINT ogr_system_tables_metadata_savepoint"
     348          16 :                          : "BEGIN");
     349          16 :         OGRPGClearResult(hResult);
     350             :     }
     351             : 
     352          16 :     if (psMD)
     353             :     {
     354           8 :         if (poDS->CreateMetadataTableIfNeeded() &&
     355           4 :             poDS->HasWritePermissionsOnMetadataTable())
     356             :         {
     357           6 :             CPLString osCommand;
     358             :             osCommand.Printf("DELETE FROM ogr_system_tables.metadata WHERE "
     359             :                              "schema_name = %s AND table_name = %s",
     360           6 :                              OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
     361           9 :                              OGRPGEscapeString(hPGConn, pszTableName).c_str());
     362           3 :             PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
     363           3 :             OGRPGClearResult(hResult);
     364             : 
     365             :             CPLXMLNode *psRoot =
     366           3 :                 CPLCreateXMLNode(nullptr, CXT_Element, "GDALMetadata");
     367           3 :             CPLAddXMLChild(psRoot, psMD);
     368           3 :             char *pszXML = CPLSerializeXMLTree(psRoot);
     369             :             // CPLDebug("PG", "Serializing %s", pszXML);
     370             : 
     371             :             osCommand.Printf(
     372             :                 "INSERT INTO ogr_system_tables.metadata (schema_name, "
     373             :                 "table_name, metadata) VALUES (%s, %s, %s)",
     374           6 :                 OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
     375           6 :                 OGRPGEscapeString(hPGConn, pszTableName).c_str(),
     376          12 :                 OGRPGEscapeString(hPGConn, pszXML).c_str());
     377           3 :             hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
     378           3 :             OGRPGClearResult(hResult);
     379             : 
     380           3 :             CPLDestroyXMLNode(psRoot);
     381           3 :             CPLFree(pszXML);
     382             :         }
     383             :     }
     384          19 :     else if (poDS->HasOgrSystemTablesMetadataTable() &&
     385           7 :              poDS->HasWritePermissionsOnMetadataTable())
     386             :     {
     387          14 :         CPLString osCommand;
     388             :         osCommand.Printf("DELETE FROM ogr_system_tables.metadata WHERE "
     389             :                          "schema_name = %s AND table_name = %s",
     390          14 :                          OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
     391          21 :                          OGRPGEscapeString(hPGConn, pszTableName).c_str());
     392             :         PGresult *hResult =
     393           7 :             OGRPG_PQexec(hPGConn, osCommand.c_str(), false, true);
     394           7 :         OGRPGClearResult(hResult);
     395             :     }
     396             : 
     397             :     {
     398          16 :         PGresult *hResult = OGRPG_PQexec(
     399             :             hPGConn,
     400             :             bIsUserTransactionActive
     401             :                 ? "RELEASE SAVEPOINT ogr_system_tables_metadata_savepoint"
     402          16 :                 : "COMMIT");
     403          16 :         OGRPGClearResult(hResult);
     404             :     }
     405             : }
     406             : 
     407             : /************************************************************************/
     408             : /*                          GetMetadataDomainList()                     */
     409             : /************************************************************************/
     410             : 
     411           7 : char **OGRPGTableLayer::GetMetadataDomainList()
     412             : {
     413           7 :     LoadMetadata();
     414             : 
     415           7 :     if (m_pszTableDescription == nullptr)
     416           4 :         GetMetadata();
     417           7 :     if (m_pszTableDescription != nullptr && m_pszTableDescription[0] != '\0')
     418           4 :         return CSLAddString(nullptr, "");
     419           3 :     return nullptr;
     420             : }
     421             : 
     422             : /************************************************************************/
     423             : /*                              GetMetadata()                           */
     424             : /************************************************************************/
     425             : 
     426          54 : char **OGRPGTableLayer::GetMetadata(const char *pszDomain)
     427             : {
     428          54 :     LoadMetadata();
     429             : 
     430          54 :     if ((pszDomain == nullptr || EQUAL(pszDomain, "")) &&
     431          53 :         m_pszTableDescription == nullptr)
     432             :     {
     433          22 :         PGconn *hPGConn = poDS->GetPGConn();
     434          44 :         CPLString osCommand;
     435             :         osCommand.Printf("SELECT d.description FROM pg_class c "
     436             :                          "JOIN pg_namespace n ON c.relnamespace=n.oid "
     437             :                          "JOIN pg_description d "
     438             :                          "ON d.objoid = c.oid AND d.classoid = "
     439             :                          "'pg_class'::regclass::oid AND d.objsubid = 0 "
     440             :                          "WHERE c.relname = %s AND n.nspname = %s AND "
     441             :                          "c.relkind in ('r', 'v') ",
     442          44 :                          OGRPGEscapeString(hPGConn, pszTableName).c_str(),
     443          66 :                          OGRPGEscapeString(hPGConn, pszSchemaName).c_str());
     444          22 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
     445             : 
     446          22 :         const char *pszDesc = nullptr;
     447          44 :         if (hResult && PGRES_TUPLES_OK == PQresultStatus(hResult) &&
     448          22 :             PQntuples(hResult) == 1)
     449             :         {
     450           8 :             pszDesc = PQgetvalue(hResult, 0, 0);
     451           8 :             if (pszDesc)
     452           8 :                 OGRLayer::SetMetadataItem("DESCRIPTION", pszDesc);
     453             :         }
     454          22 :         m_pszTableDescription = CPLStrdup(pszDesc ? pszDesc : "");
     455             : 
     456          22 :         OGRPGClearResult(hResult);
     457             :     }
     458             : 
     459          54 :     return OGRLayer::GetMetadata(pszDomain);
     460             : }
     461             : 
     462             : /************************************************************************/
     463             : /*                            GetMetadataItem()                         */
     464             : /************************************************************************/
     465             : 
     466          25 : const char *OGRPGTableLayer::GetMetadataItem(const char *pszName,
     467             :                                              const char *pszDomain)
     468             : {
     469          25 :     LoadMetadata();
     470             : 
     471          25 :     GetMetadata(pszDomain);
     472          25 :     return OGRLayer::GetMetadataItem(pszName, pszDomain);
     473             : }
     474             : 
     475             : /************************************************************************/
     476             : /*                              SetMetadata()                           */
     477             : /************************************************************************/
     478             : 
     479          21 : CPLErr OGRPGTableLayer::SetMetadata(char **papszMD, const char *pszDomain)
     480             : {
     481          21 :     LoadMetadata();
     482             : 
     483          21 :     OGRLayer::SetMetadata(papszMD, pszDomain);
     484          21 :     m_bMetadataModified = true;
     485             : 
     486          26 :     if (!osForcedDescription.empty() &&
     487           5 :         (pszDomain == nullptr || EQUAL(pszDomain, "")))
     488             :     {
     489           5 :         OGRLayer::SetMetadataItem("DESCRIPTION", osForcedDescription);
     490             :     }
     491             : 
     492          21 :     if (!bDeferredCreation && (pszDomain == nullptr || EQUAL(pszDomain, "")))
     493             :     {
     494          20 :         const char *pszDescription = OGRLayer::GetMetadataItem("DESCRIPTION");
     495          20 :         if (pszDescription == nullptr)
     496          10 :             pszDescription = "";
     497          20 :         PGconn *hPGConn = poDS->GetPGConn();
     498          20 :         CPLString osCommand;
     499             : 
     500             :         osCommand.Printf(
     501             :             "COMMENT ON TABLE %s IS %s", pszSqlTableName,
     502          20 :             pszDescription[0] != '\0'
     503          30 :                 ? OGRPGEscapeString(hPGConn, pszDescription).c_str()
     504          30 :                 : "NULL");
     505          20 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
     506          20 :         OGRPGClearResult(hResult);
     507             : 
     508          20 :         CPLFree(m_pszTableDescription);
     509          20 :         m_pszTableDescription = CPLStrdup(pszDescription);
     510             :     }
     511             : 
     512          21 :     return CE_None;
     513             : }
     514             : 
     515             : /************************************************************************/
     516             : /*                            SetMetadataItem()                         */
     517             : /************************************************************************/
     518             : 
     519          15 : CPLErr OGRPGTableLayer::SetMetadataItem(const char *pszName,
     520             :                                         const char *pszValue,
     521             :                                         const char *pszDomain)
     522             : {
     523          15 :     LoadMetadata();
     524             : 
     525          15 :     if ((pszDomain == nullptr || EQUAL(pszDomain, "")) && pszName != nullptr &&
     526          30 :         EQUAL(pszName, "DESCRIPTION") && !osForcedDescription.empty())
     527             :     {
     528           4 :         pszValue = osForcedDescription;
     529             :     }
     530             : 
     531          15 :     OGRLayer::SetMetadataItem(pszName, pszValue, pszDomain);
     532          15 :     m_bMetadataModified = true;
     533             : 
     534          15 :     if (!bDeferredCreation && (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
     535          12 :         pszName != nullptr && EQUAL(pszName, "DESCRIPTION"))
     536             :     {
     537           8 :         SetMetadata(GetMetadata());
     538             :     }
     539             : 
     540          15 :     return CE_None;
     541             : }
     542             : 
     543             : /************************************************************************/
     544             : /*                      SetForcedDescription()                          */
     545             : /************************************************************************/
     546             : 
     547           2 : void OGRPGTableLayer::SetForcedDescription(const char *pszDescriptionIn)
     548             : {
     549           2 :     osForcedDescription = pszDescriptionIn;
     550           2 :     CPLFree(m_pszTableDescription);
     551           2 :     m_pszTableDescription = CPLStrdup(pszDescriptionIn);
     552           2 :     SetMetadataItem("DESCRIPTION", osForcedDescription);
     553           2 : }
     554             : 
     555             : /************************************************************************/
     556             : /*                      SetGeometryInformation()                        */
     557             : /************************************************************************/
     558             : 
     559          46 : void OGRPGTableLayer::SetGeometryInformation(PGGeomColumnDesc *pasDesc,
     560             :                                              int nGeomFieldCount)
     561             : {
     562             :     // Flag must be set before instantiating geometry fields.
     563          46 :     bGeometryInformationSet = TRUE;
     564          92 :     auto oTemporaryUnsealer(poFeatureDefn->GetTemporaryUnsealer(false));
     565             : 
     566         106 :     for (int i = 0; i < nGeomFieldCount; i++)
     567             :     {
     568             :         auto poGeomFieldDefn =
     569          60 :             std::make_unique<OGRPGGeomFieldDefn>(this, pasDesc[i].pszName);
     570          60 :         poGeomFieldDefn->SetNullable(pasDesc[i].bNullable);
     571          60 :         poGeomFieldDefn->nSRSId = pasDesc[i].nSRID;
     572          60 :         poGeomFieldDefn->GeometryTypeFlags = pasDesc[i].GeometryTypeFlags;
     573          60 :         poGeomFieldDefn->ePostgisType = pasDesc[i].ePostgisType;
     574          60 :         if (pasDesc[i].pszGeomType != nullptr)
     575             :         {
     576             :             OGRwkbGeometryType eGeomType =
     577          58 :                 OGRFromOGCGeomType(pasDesc[i].pszGeomType);
     578          58 :             if ((poGeomFieldDefn->GeometryTypeFlags & OGRGeometry::OGR_G_3D) &&
     579             :                 (eGeomType != wkbUnknown))
     580          21 :                 eGeomType = wkbSetZ(eGeomType);
     581          58 :             if ((poGeomFieldDefn->GeometryTypeFlags &
     582          58 :                  OGRGeometry::OGR_G_MEASURED) &&
     583             :                 (eGeomType != wkbUnknown))
     584           1 :                 eGeomType = wkbSetM(eGeomType);
     585          58 :             poGeomFieldDefn->SetType(eGeomType);
     586             :         }
     587          60 :         poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
     588             :     }
     589          46 : }
     590             : 
     591             : /************************************************************************/
     592             : /*                        ReadTableDefinition()                         */
     593             : /*                                                                      */
     594             : /*      Build a schema from the named table.  Done by querying the      */
     595             : /*      catalog.                                                        */
     596             : /************************************************************************/
     597             : 
     598      230299 : int OGRPGTableLayer::ReadTableDefinition()
     599             : 
     600             : {
     601      230299 :     PGconn *hPGConn = poDS->GetPGConn();
     602             : 
     603      230299 :     if (bTableDefinitionValid >= 0)
     604      229845 :         return bTableDefinitionValid;
     605         454 :     bTableDefinitionValid = FALSE;
     606             : 
     607         454 :     poDS->EndCopy();
     608             : 
     609         908 :     auto oTemporaryUnsealer(poFeatureDefn->GetTemporaryUnsealer());
     610             : 
     611             :     /* -------------------------------------------------------------------- */
     612             :     /*      Get the OID of the table.                                       */
     613             :     /* -------------------------------------------------------------------- */
     614             : 
     615         908 :     CPLString osCommand;
     616             :     osCommand.Printf("SELECT c.oid FROM pg_class c "
     617             :                      "JOIN pg_namespace n ON c.relnamespace=n.oid "
     618             :                      "WHERE c.relname = %s AND n.nspname = %s",
     619         908 :                      OGRPGEscapeString(hPGConn, pszTableName).c_str(),
     620        1362 :                      OGRPGEscapeString(hPGConn, pszSchemaName).c_str());
     621         454 :     unsigned int nTableOID = 0;
     622             :     {
     623         454 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
     624         454 :         if (hResult && PGRES_TUPLES_OK == PQresultStatus(hResult))
     625             :         {
     626         454 :             if (PQntuples(hResult) == 1 && PQgetisnull(hResult, 0, 0) == false)
     627             :             {
     628         221 :                 nTableOID = static_cast<unsigned>(
     629         221 :                     CPLAtoGIntBig(PQgetvalue(hResult, 0, 0)));
     630         221 :                 OGRPGClearResult(hResult);
     631             :             }
     632             :             else
     633             :             {
     634         233 :                 CPLDebug("PG", "Could not retrieve table oid for %s",
     635             :                          pszTableName);
     636         233 :                 OGRPGClearResult(hResult);
     637         233 :                 return FALSE;
     638             :             }
     639             :         }
     640             :         else
     641             :         {
     642           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s",
     643             :                      PQerrorMessage(hPGConn));
     644           0 :             return FALSE;
     645             :         }
     646             :     }
     647             : 
     648             :     /* -------------------------------------------------------------------- */
     649             :     /*      Identify the integer primary key.                               */
     650             :     /* -------------------------------------------------------------------- */
     651             : 
     652             :     osCommand.Printf(
     653             :         "SELECT a.attname, a.attnum, t.typname, "
     654             :         "t.typname = ANY(ARRAY['int2','int4','int8','serial','bigserial']) AS "
     655             :         "isfid "
     656             :         "FROM pg_attribute a "
     657             :         "JOIN pg_type t ON t.oid = a.atttypid "
     658             :         "JOIN pg_index i ON i.indrelid = a.attrelid "
     659             :         "WHERE a.attnum > 0 AND a.attrelid = %u "
     660             :         "AND i.indisprimary = 't' "
     661             :         "AND t.typname !~ '^geom' "
     662             :         "AND a.attnum = ANY(i.indkey) ORDER BY a.attnum",
     663         221 :         nTableOID);
     664             : 
     665         221 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
     666             : 
     667         221 :     if (hResult && PGRES_TUPLES_OK == PQresultStatus(hResult))
     668             :     {
     669         221 :         if (PQntuples(hResult) == 1 && PQgetisnull(hResult, 0, 0) == false)
     670             :         {
     671             :             /* Check if single-field PK can be represented as integer. */
     672         344 :             CPLString osValue(PQgetvalue(hResult, 0, 3));
     673         172 :             if (osValue == "t")
     674             :             {
     675         172 :                 osPrimaryKey.Printf("%s", PQgetvalue(hResult, 0, 0));
     676         172 :                 const char *pszFIDType = PQgetvalue(hResult, 0, 2);
     677         172 :                 CPLDebug("PG", "Primary key name (FID): %s, type : %s",
     678             :                          osPrimaryKey.c_str(), pszFIDType);
     679         172 :                 if (EQUAL(pszFIDType, "int8"))
     680           2 :                     OGRLayer::SetMetadataItem(OLMD_FID64, "YES");
     681             :             }
     682             :         }
     683          49 :         else if (PQntuples(hResult) > 1)
     684             :         {
     685           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     686             :                      "Multi-column primary key in \'%s\' detected but not "
     687             :                      "supported.",
     688             :                      pszTableName);
     689             :         }
     690             : 
     691         221 :         OGRPGClearResult(hResult);
     692             :         /* Zero tuples means no PK is defined, perfectly valid case. */
     693             :     }
     694             :     else
     695             :     {
     696           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
     697             :     }
     698             : 
     699             :     /* -------------------------------------------------------------------- */
     700             :     /*      Fire off commands to get back the columns of the table.         */
     701             :     /* -------------------------------------------------------------------- */
     702             :     osCommand.Printf(
     703             :         "SELECT a.attname, t.typname, a.attlen,"
     704             :         "       format_type(a.atttypid,a.atttypmod), a.attnotnull, def.def, "
     705             :         "i.indisunique, descr.description%s "
     706             :         "FROM pg_attribute a "
     707             :         "JOIN pg_type t ON t.oid = a.atttypid "
     708             :         "LEFT JOIN "
     709             :         "(SELECT adrelid, adnum, pg_get_expr(adbin, adrelid) AS def FROM "
     710             :         "pg_attrdef) def "
     711             :         "ON def.adrelid = a.attrelid AND def.adnum = a.attnum "
     712             :         // Find unique constraints that are on a single column only
     713             :         "LEFT JOIN "
     714             :         "(SELECT DISTINCT indrelid, indkey, indisunique FROM pg_index WHERE "
     715             :         "indisunique) i "
     716             :         "ON i.indrelid = a.attrelid AND i.indkey[0] = a.attnum AND i.indkey[1] "
     717             :         "IS NULL "
     718             :         "LEFT JOIN pg_description descr "
     719             :         "ON descr.objoid = a.attrelid "
     720             :         "AND descr.classoid = 'pg_class'::regclass::oid "
     721             :         "AND descr.objsubid = a.attnum "
     722             :         "WHERE a.attnum > 0 AND a.attrelid = %u "
     723             :         "ORDER BY a.attnum",
     724         221 :         (poDS->sPostgreSQLVersion.nMajor >= 12 ? ", a.attgenerated" : ""),
     725         221 :         nTableOID);
     726             : 
     727         221 :     hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
     728             : 
     729         221 :     if (!hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK)
     730             :     {
     731           0 :         OGRPGClearResult(hResult);
     732             : 
     733           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
     734           0 :         return bTableDefinitionValid;
     735             :     }
     736             : 
     737         221 :     if (PQntuples(hResult) == 0)
     738             :     {
     739           0 :         OGRPGClearResult(hResult);
     740             : 
     741           0 :         CPLDebug("PG", "No field definitions found for '%s', is it a table?",
     742             :                  pszTableName);
     743           0 :         return bTableDefinitionValid;
     744             :     }
     745             : 
     746             :     /* -------------------------------------------------------------------- */
     747             :     /*      Parse the returned table information.                           */
     748             :     /* -------------------------------------------------------------------- */
     749        1368 :     for (int iRecord = 0; iRecord < PQntuples(hResult); iRecord++)
     750             :     {
     751        1147 :         OGRFieldDefn oField(PQgetvalue(hResult, iRecord, 0), OFTString);
     752             : 
     753        1147 :         const char *pszType = PQgetvalue(hResult, iRecord, 1);
     754        1147 :         int nWidth = atoi(PQgetvalue(hResult, iRecord, 2));
     755        1147 :         const char *pszFormatType = PQgetvalue(hResult, iRecord, 3);
     756        1147 :         const char *pszNotNull = PQgetvalue(hResult, iRecord, 4);
     757        1147 :         const char *pszDefault = PQgetisnull(hResult, iRecord, 5)
     758        1147 :                                      ? nullptr
     759         193 :                                      : PQgetvalue(hResult, iRecord, 5);
     760        1147 :         const char *pszIsUnique = PQgetvalue(hResult, iRecord, 6);
     761        1147 :         const char *pszDescription = PQgetvalue(hResult, iRecord, 7);
     762        1147 :         const char *pszGenerated = poDS->sPostgreSQLVersion.nMajor >= 12
     763        1147 :                                        ? PQgetvalue(hResult, iRecord, 8)
     764        1147 :                                        : "";
     765             : 
     766        1147 :         if (pszNotNull && EQUAL(pszNotNull, "t"))
     767         186 :             oField.SetNullable(FALSE);
     768        1147 :         if (pszIsUnique && EQUAL(pszIsUnique, "t"))
     769         188 :             oField.SetUnique(TRUE);
     770             : 
     771        1147 :         if (EQUAL(oField.GetNameRef(), osPrimaryKey))
     772             :         {
     773         192 :             pszFIDColumn = CPLStrdup(oField.GetNameRef());
     774         192 :             CPLDebug("PG", "Using column '%s' as FID for table '%s'",
     775             :                      pszFIDColumn, pszTableName);
     776         192 :             continue;
     777             :         }
     778        1770 :         else if (EQUAL(pszType, "geometry") || EQUAL(pszType, "geography") ||
     779         815 :                  EQUAL(oField.GetNameRef(), "WKB_GEOMETRY"))
     780             :         {
     781             :             const auto InitGeomField =
     782         528 :                 [this, &pszType, &oField](OGRPGGeomFieldDefn *poGeomFieldDefn)
     783             :             {
     784         195 :                 if (EQUAL(pszType, "geometry"))
     785         126 :                     poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOMETRY;
     786          69 :                 else if (EQUAL(pszType, "geography"))
     787             :                 {
     788           5 :                     poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOGRAPHY;
     789           5 :                     if (!(poDS->sPostGISVersion.nMajor >= 3 ||
     790           0 :                           (poDS->sPostGISVersion.nMajor == 2 &&
     791           0 :                            poDS->sPostGISVersion.nMinor >= 2)))
     792             :                     {
     793             :                         // EPSG:4326 was a requirement for geography before
     794             :                         // PostGIS 2.2
     795           0 :                         poGeomFieldDefn->nSRSId = 4326;
     796             :                     }
     797             :                 }
     798             :                 else
     799             :                 {
     800          64 :                     poGeomFieldDefn->ePostgisType = GEOM_TYPE_WKB;
     801          64 :                     if (EQUAL(pszType, "OID"))
     802           0 :                         bWkbAsOid = TRUE;
     803             :                 }
     804         195 :                 poGeomFieldDefn->SetNullable(oField.IsNullable());
     805         399 :             };
     806             : 
     807         204 :             if (!bGeometryInformationSet)
     808             :             {
     809         192 :                 if (pszGeomColForced == nullptr ||
     810          14 :                     EQUAL(pszGeomColForced, oField.GetNameRef()))
     811             :                 {
     812             :                     auto poGeomFieldDefn = std::make_unique<OGRPGGeomFieldDefn>(
     813         169 :                         this, oField.GetNameRef());
     814         169 :                     InitGeomField(poGeomFieldDefn.get());
     815         169 :                     poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
     816             :                 }
     817             :             }
     818             :             else
     819             :             {
     820          26 :                 int idx = poFeatureDefn->GetGeomFieldIndex(oField.GetNameRef());
     821          26 :                 if (idx >= 0)
     822             :                 {
     823          26 :                     auto poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(idx);
     824          26 :                     InitGeomField(poGeomFieldDefn);
     825             :                 }
     826             :             }
     827             : 
     828         204 :             continue;
     829             :         }
     830             : 
     831         751 :         OGRPGCommonLayerSetType(oField, pszType, pszFormatType, nWidth);
     832             : 
     833         751 :         if (pszDefault)
     834             :         {
     835          27 :             OGRPGCommonLayerNormalizeDefault(&oField, pszDefault);
     836             :         }
     837         751 :         if (pszDescription)
     838         751 :             oField.SetComment(pszDescription);
     839             : 
     840         751 :         oField.SetGenerated(pszGenerated != nullptr && pszGenerated[0] != '\0');
     841             : 
     842             :         // CPLDebug("PG", "name=%s, type=%s", oField.GetNameRef(), pszType);
     843         751 :         poFeatureDefn->AddFieldDefn(&oField);
     844             :     }
     845             : 
     846         221 :     OGRPGClearResult(hResult);
     847             : 
     848         221 :     bTableDefinitionValid = TRUE;
     849             : 
     850         221 :     ResetReading();
     851             : 
     852             :     /* If geometry type, SRID, etc... have always been set by
     853             :      * SetGeometryInformation() */
     854             :     /* no need to issue a new SQL query. Just record the geom type in the layer
     855             :      * definition */
     856         221 :     if (bGeometryInformationSet)
     857             :     {
     858          19 :         return TRUE;
     859             :     }
     860         202 :     bGeometryInformationSet = TRUE;
     861             : 
     862             :     // get layer geometry type (for PostGIS dataset)
     863         371 :     for (int iField = 0; iField < poFeatureDefn->GetGeomFieldCount(); iField++)
     864             :     {
     865             :         OGRPGGeomFieldDefn *poGeomFieldDefn =
     866         169 :             poFeatureDefn->GetGeomFieldDefn(iField);
     867             : 
     868             :         /* Get the geometry type and dimensions from the table, or */
     869             :         /* from its parents if it is a derived table, or from the parent of the
     870             :          * parent, etc.. */
     871         169 :         int bGoOn = poDS->m_bHasGeometryColumns;
     872         169 :         const bool bHasPostGISGeometry =
     873         169 :             (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY);
     874             : 
     875         341 :         while (bGoOn)
     876             :         {
     877             :             const CPLString osEscapedThisOrParentTableName(OGRPGEscapeString(
     878         172 :                 hPGConn, (pszSqlGeomParentTableName) ? pszSqlGeomParentTableName
     879         344 :                                                      : pszTableName));
     880             :             osCommand.Printf("SELECT type, coord_dimension, srid FROM %s WHERE "
     881             :                              "f_table_name = %s",
     882             :                              (bHasPostGISGeometry) ? "geometry_columns"
     883             :                                                    : "geography_columns",
     884         172 :                              osEscapedThisOrParentTableName.c_str());
     885             : 
     886         172 :             osCommand += CPLString().Printf(
     887             :                 " AND %s=%s",
     888             :                 (bHasPostGISGeometry) ? "f_geometry_column"
     889             :                                       : "f_geography_column",
     890         344 :                 OGRPGEscapeString(hPGConn, poGeomFieldDefn->GetNameRef())
     891         172 :                     .c_str());
     892             : 
     893         172 :             osCommand += CPLString().Printf(
     894             :                 " AND f_table_schema = %s",
     895         172 :                 OGRPGEscapeString(hPGConn, pszSchemaName).c_str());
     896             : 
     897         172 :             hResult = OGRPG_PQexec(hPGConn, osCommand);
     898             : 
     899         277 :             if (hResult && PQntuples(hResult) == 1 &&
     900         105 :                 !PQgetisnull(hResult, 0, 0))
     901             :             {
     902         105 :                 const char *pszType = PQgetvalue(hResult, 0, 0);
     903             : 
     904         105 :                 int dim = atoi(PQgetvalue(hResult, 0, 1));
     905         105 :                 bool bHasM = pszType[strlen(pszType) - 1] == 'M';
     906         105 :                 int GeometryTypeFlags = 0;
     907         105 :                 if (dim == 3)
     908             :                 {
     909          29 :                     if (bHasM)
     910           0 :                         GeometryTypeFlags |= OGRGeometry::OGR_G_MEASURED;
     911             :                     else
     912          29 :                         GeometryTypeFlags |= OGRGeometry::OGR_G_3D;
     913             :                 }
     914          76 :                 else if (dim == 4)
     915           3 :                     GeometryTypeFlags |=
     916             :                         OGRGeometry::OGR_G_3D | OGRGeometry::OGR_G_MEASURED;
     917             : 
     918         105 :                 int nSRSId = atoi(PQgetvalue(hResult, 0, 2));
     919             : 
     920         105 :                 poGeomFieldDefn->GeometryTypeFlags = GeometryTypeFlags;
     921         105 :                 if (nSRSId > 0)
     922          18 :                     poGeomFieldDefn->nSRSId = nSRSId;
     923         105 :                 OGRwkbGeometryType eGeomType = OGRFromOGCGeomType(pszType);
     924         105 :                 if (poGeomFieldDefn->GeometryTypeFlags &
     925          32 :                         OGRGeometry::OGR_G_3D &&
     926             :                     eGeomType != wkbUnknown)
     927           8 :                     eGeomType = wkbSetZ(eGeomType);
     928         105 :                 if (poGeomFieldDefn->GeometryTypeFlags &
     929           3 :                         OGRGeometry::OGR_G_MEASURED &&
     930             :                     eGeomType != wkbUnknown)
     931           0 :                     eGeomType = wkbSetM(eGeomType);
     932         105 :                 poGeomFieldDefn->SetType(eGeomType);
     933             : 
     934         105 :                 bGoOn = FALSE;
     935             :             }
     936             :             else
     937             :             {
     938             :                 /* Fetch the name of the parent table */
     939             :                 osCommand.Printf(
     940             :                     "SELECT pg_class.relname FROM pg_class WHERE oid = "
     941             :                     "(SELECT pg_inherits.inhparent FROM pg_inherits WHERE "
     942             :                     "inhrelid = "
     943             :                     "(SELECT c.oid FROM pg_class c, pg_namespace n "
     944             :                     "WHERE c.relname = %s AND c.relnamespace=n.oid AND "
     945             :                     "n.nspname = %s))",
     946             :                     osEscapedThisOrParentTableName.c_str(),
     947          67 :                     OGRPGEscapeString(hPGConn, pszSchemaName).c_str());
     948             : 
     949          67 :                 OGRPGClearResult(hResult);
     950          67 :                 hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
     951             : 
     952          70 :                 if (hResult && PQntuples(hResult) == 1 &&
     953           3 :                     !PQgetisnull(hResult, 0, 0))
     954             :                 {
     955           3 :                     CPLFree(pszSqlGeomParentTableName);
     956           3 :                     pszSqlGeomParentTableName =
     957           3 :                         CPLStrdup(PQgetvalue(hResult, 0, 0));
     958             :                 }
     959             :                 else
     960             :                 {
     961             :                     /* No more parent : stop recursion */
     962          64 :                     bGoOn = FALSE;
     963             :                 }
     964             :             }
     965             : 
     966         172 :             OGRPGClearResult(hResult);
     967             :         }
     968             :     }
     969             : 
     970         202 :     return bTableDefinitionValid;
     971             : }
     972             : 
     973             : /************************************************************************/
     974             : /*                         SetTableDefinition()                         */
     975             : /************************************************************************/
     976             : 
     977         225 : void OGRPGTableLayer::SetTableDefinition(const char *pszFIDColumnName,
     978             :                                          const char *pszGFldName,
     979             :                                          OGRwkbGeometryType eType,
     980             :                                          const char *pszGeomType, int nSRSId,
     981             :                                          int GeometryTypeFlags)
     982             : {
     983         225 :     bTableDefinitionValid = TRUE;
     984         225 :     bGeometryInformationSet = TRUE;
     985         225 :     pszFIDColumn = CPLStrdup(pszFIDColumnName);
     986         450 :     auto oTemporaryUnsealer(poFeatureDefn->GetTemporaryUnsealer());
     987         225 :     poFeatureDefn->SetGeomType(wkbNone);
     988         225 :     if (eType != wkbNone)
     989             :     {
     990             :         auto poGeomFieldDefn =
     991         196 :             std::make_unique<OGRPGGeomFieldDefn>(this, pszGFldName);
     992         196 :         poGeomFieldDefn->SetType(eType);
     993         196 :         poGeomFieldDefn->GeometryTypeFlags = GeometryTypeFlags;
     994             : 
     995         196 :         if (EQUAL(pszGeomType, "geometry"))
     996             :         {
     997         110 :             poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOMETRY;
     998         110 :             poGeomFieldDefn->nSRSId = nSRSId;
     999             :         }
    1000          86 :         else if (EQUAL(pszGeomType, "geography"))
    1001             :         {
    1002           4 :             poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOGRAPHY;
    1003           4 :             poGeomFieldDefn->nSRSId = nSRSId;
    1004             :         }
    1005             :         else
    1006             :         {
    1007          82 :             poGeomFieldDefn->ePostgisType = GEOM_TYPE_WKB;
    1008          82 :             if (EQUAL(pszGeomType, "OID"))
    1009           0 :                 bWkbAsOid = TRUE;
    1010             :         }
    1011         196 :         poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
    1012             :     }
    1013          29 :     else if (pszGFldName != nullptr)
    1014             :     {
    1015           2 :         m_osFirstGeometryFieldName = pszGFldName;
    1016             :     }
    1017         225 :     m_osLCOGeomType = pszGeomType;
    1018         225 : }
    1019             : 
    1020             : /************************************************************************/
    1021             : /*                         ISetSpatialFilter()                          */
    1022             : /************************************************************************/
    1023             : 
    1024          82 : OGRErr OGRPGTableLayer::ISetSpatialFilter(int iGeomField,
    1025             :                                           const OGRGeometry *poGeomIn)
    1026             : 
    1027             : {
    1028          82 :     m_iGeomFieldFilter = iGeomField;
    1029             : 
    1030          82 :     if (InstallFilter(poGeomIn))
    1031             :     {
    1032          41 :         BuildWhere();
    1033             : 
    1034          41 :         ResetReading();
    1035             :     }
    1036             : 
    1037          82 :     return OGRERR_NONE;
    1038             : }
    1039             : 
    1040             : /************************************************************************/
    1041             : /*                             BuildWhere()                             */
    1042             : /*                                                                      */
    1043             : /*      Build the WHERE statement appropriate to the current set of     */
    1044             : /*      criteria (spatial and attribute queries).                       */
    1045             : /************************************************************************/
    1046             : 
    1047         207 : void OGRPGTableLayer::BuildWhere()
    1048             : 
    1049             : {
    1050         207 :     osWHERE = "";
    1051         207 :     OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
    1052         207 :     if (poFeatureDefn->GetGeomFieldCount() != 0)
    1053         203 :         poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
    1054             : 
    1055         207 :     if (m_poFilterGeom != nullptr && poGeomFieldDefn != nullptr &&
    1056          53 :         poDS->sPostGISVersion.nMajor >= 0 &&
    1057          49 :         (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ||
    1058          18 :          poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY))
    1059             :     {
    1060             :         char szBox3D_1[128];
    1061             :         char szBox3D_2[128];
    1062          31 :         OGREnvelope sEnvelope;
    1063             : 
    1064          31 :         m_poFilterGeom->getEnvelope(&sEnvelope);
    1065          31 :         if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY)
    1066             :         {
    1067           0 :             if (sEnvelope.MinX < -180.0)
    1068           0 :                 sEnvelope.MinX = -180.0;
    1069           0 :             if (sEnvelope.MinY < -90.0)
    1070           0 :                 sEnvelope.MinY = -90.0;
    1071           0 :             if (sEnvelope.MaxX > 180.0)
    1072           0 :                 sEnvelope.MaxX = 180.0;
    1073           0 :             if (sEnvelope.MaxY > 90.0)
    1074           0 :                 sEnvelope.MaxY = 90.0;
    1075             :         }
    1076          31 :         CPLsnprintf(szBox3D_1, sizeof(szBox3D_1), "%.17g %.17g", sEnvelope.MinX,
    1077             :                     sEnvelope.MinY);
    1078          31 :         CPLsnprintf(szBox3D_2, sizeof(szBox3D_2), "%.17g %.17g", sEnvelope.MaxX,
    1079             :                     sEnvelope.MaxY);
    1080             :         osWHERE.Printf(
    1081             :             "WHERE %s && ST_SetSRID('BOX3D(%s, %s)'::box3d,%d) ",
    1082          62 :             OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(),
    1083          62 :             szBox3D_1, szBox3D_2, poGeomFieldDefn->nSRSId);
    1084             :     }
    1085             : 
    1086         207 :     if (!osQuery.empty())
    1087             :     {
    1088          89 :         if (osWHERE.empty())
    1089             :         {
    1090          82 :             osWHERE.Printf("WHERE %s ", osQuery.c_str());
    1091             :         }
    1092             :         else
    1093             :         {
    1094           7 :             osWHERE += "AND (";
    1095           7 :             osWHERE += osQuery;
    1096           7 :             osWHERE += ")";
    1097             :         }
    1098             :     }
    1099         207 : }
    1100             : 
    1101             : /************************************************************************/
    1102             : /*                      BuildFullQueryStatement()                       */
    1103             : /************************************************************************/
    1104             : 
    1105         996 : void OGRPGTableLayer::BuildFullQueryStatement()
    1106             : 
    1107             : {
    1108         996 :     CPLString osFields = BuildFields();
    1109         996 :     if (pszQueryStatement != nullptr)
    1110             :     {
    1111         586 :         CPLFree(pszQueryStatement);
    1112         586 :         pszQueryStatement = nullptr;
    1113             :     }
    1114        1992 :     pszQueryStatement = static_cast<char *>(CPLMalloc(
    1115         996 :         osFields.size() + osWHERE.size() + strlen(pszSqlTableName) + 40));
    1116         996 :     snprintf(pszQueryStatement,
    1117         996 :              osFields.size() + osWHERE.size() + strlen(pszSqlTableName) + 40,
    1118             :              "SELECT %s FROM %s %s", osFields.c_str(), pszSqlTableName,
    1119             :              osWHERE.c_str());
    1120         996 : }
    1121             : 
    1122             : /************************************************************************/
    1123             : /*                            ResetReading()                            */
    1124             : /************************************************************************/
    1125             : 
    1126        1027 : void OGRPGTableLayer::ResetReading()
    1127             : 
    1128             : {
    1129        1027 :     if (bInResetReading)
    1130          31 :         return;
    1131         996 :     bInResetReading = TRUE;
    1132             : 
    1133         996 :     if (bDeferredCreation)
    1134          16 :         RunDeferredCreationIfNecessary();
    1135         996 :     poDS->EndCopy();
    1136         996 :     bUseCopyByDefault = FALSE;
    1137             : 
    1138         996 :     BuildFullQueryStatement();
    1139             : 
    1140         996 :     OGRPGLayer::ResetReading();
    1141             : 
    1142         996 :     bInResetReading = FALSE;
    1143             : }
    1144             : 
    1145             : /************************************************************************/
    1146             : /*                           GetNextFeature()                           */
    1147             : /************************************************************************/
    1148             : 
    1149        1211 : OGRFeature *OGRPGTableLayer::GetNextFeature()
    1150             : 
    1151             : {
    1152        1211 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    1153           0 :         return nullptr;
    1154        1211 :     poDS->EndCopy();
    1155             : 
    1156        1211 :     if (pszQueryStatement == nullptr)
    1157           6 :         ResetReading();
    1158             : 
    1159        1211 :     OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
    1160        1211 :     if (poFeatureDefn->GetGeomFieldCount() != 0)
    1161        1139 :         poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
    1162        1211 :     poFeatureDefn->GetFieldCount();
    1163             : 
    1164             :     while (true)
    1165             :     {
    1166        1307 :         OGRFeature *poFeature = GetNextRawFeature();
    1167        1307 :         if (poFeature == nullptr)
    1168         122 :             return nullptr;
    1169             : 
    1170             :         /* We just have to look if there is a geometry filter */
    1171             :         /* If there's a PostGIS geometry column, the spatial filter */
    1172             :         /* is already taken into account in the select request */
    1173             :         /* The attribute filter is always taken into account by the select
    1174             :          * request */
    1175         341 :         if (m_poFilterGeom == nullptr || poGeomFieldDefn == nullptr ||
    1176         341 :             poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ||
    1177        1730 :             poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ||
    1178         204 :             FilterGeometry(poFeature->GetGeomFieldRef(m_iGeomFieldFilter)))
    1179             :         {
    1180        1089 :             if (iFIDAsRegularColumnIndex >= 0)
    1181             :             {
    1182           1 :                 poFeature->SetField(iFIDAsRegularColumnIndex,
    1183             :                                     poFeature->GetFID());
    1184             :             }
    1185        1089 :             return poFeature;
    1186             :         }
    1187             : 
    1188          96 :         delete poFeature;
    1189          96 :     }
    1190             : }
    1191             : 
    1192             : /************************************************************************/
    1193             : /*                            BuildFields()                             */
    1194             : /*                                                                      */
    1195             : /*      Build list of fields to fetch, performing any required          */
    1196             : /*      transformations (such as on geometry).                          */
    1197             : /************************************************************************/
    1198             : 
    1199        1139 : CPLString OGRPGTableLayer::BuildFields()
    1200             : 
    1201             : {
    1202        1139 :     int i = 0;
    1203        1139 :     CPLString osFieldList;
    1204             : 
    1205        1139 :     poFeatureDefn->GetFieldCount();
    1206             : 
    1207        2235 :     if (pszFIDColumn != nullptr &&
    1208        1096 :         poFeatureDefn->GetFieldIndex(pszFIDColumn) == -1)
    1209             :     {
    1210        1092 :         osFieldList += OGRPGEscapeColumnName(pszFIDColumn);
    1211             :     }
    1212             : 
    1213        2188 :     for (i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    1214             :     {
    1215             :         OGRPGGeomFieldDefn *poGeomFieldDefn =
    1216        1049 :             poFeatureDefn->GetGeomFieldDefn(i);
    1217             :         CPLString osEscapedGeom =
    1218        2098 :             OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
    1219             : 
    1220        1049 :         if (!osFieldList.empty())
    1221        1022 :             osFieldList += ", ";
    1222             : 
    1223        1049 :         if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY)
    1224             :         {
    1225         625 :             if (!poDS->HavePostGIS() || poDS->bUseBinaryCursor)
    1226             :             {
    1227           8 :                 osFieldList += osEscapedGeom;
    1228             :             }
    1229         617 :             else if (CPLTestBool(CPLGetConfigOption("PG_USE_BASE64", "NO")))
    1230             :             {
    1231           1 :                 osFieldList += "encode(ST_AsEWKB(";
    1232           1 :                 osFieldList += osEscapedGeom;
    1233           1 :                 osFieldList += "), 'base64') AS ";
    1234           2 :                 osFieldList += OGRPGEscapeColumnName(
    1235           1 :                     CPLSPrintf("EWKBBase64_%s", poGeomFieldDefn->GetNameRef()));
    1236             :             }
    1237             :             else
    1238             :             {
    1239             :                 /* This will return EWKB in an hex encoded form */
    1240         616 :                 osFieldList += osEscapedGeom;
    1241             :             }
    1242             :         }
    1243         424 :         else if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY)
    1244             :         {
    1245             : #if defined(BINARY_CURSOR_ENABLED)
    1246             :             if (poDS->bUseBinaryCursor)
    1247             :             {
    1248             :                 osFieldList += "ST_AsBinary(";
    1249             :                 osFieldList += osEscapedGeom;
    1250             :                 osFieldList += ") AS";
    1251             :                 osFieldList += OGRPGEscapeColumnName(
    1252             :                     CPLSPrintf("AsBinary_%s", poGeomFieldDefn->GetNameRef()));
    1253             :             }
    1254             :             else
    1255             : #endif
    1256          15 :                 if (CPLTestBool(CPLGetConfigOption("PG_USE_BASE64", "NO")))
    1257             :             {
    1258           0 :                 osFieldList += "encode(ST_AsEWKB(";
    1259           0 :                 osFieldList += osEscapedGeom;
    1260           0 :                 osFieldList += "::geometry), 'base64') AS ";
    1261           0 :                 osFieldList += OGRPGEscapeColumnName(
    1262           0 :                     CPLSPrintf("EWKBBase64_%s", poGeomFieldDefn->GetNameRef()));
    1263             :             }
    1264             :             else
    1265             :             {
    1266          15 :                 osFieldList += osEscapedGeom;
    1267             :             }
    1268             :         }
    1269             :         else
    1270             :         {
    1271         409 :             osFieldList += osEscapedGeom;
    1272             :         }
    1273             :     }
    1274             : 
    1275        5333 :     for (i = 0; i < poFeatureDefn->GetFieldCount(); i++)
    1276             :     {
    1277        4194 :         const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
    1278             : 
    1279        4194 :         if (!osFieldList.empty())
    1280        4174 :             osFieldList += ", ";
    1281             : 
    1282             : #if defined(BINARY_CURSOR_ENABLED)
    1283             :         /* With a binary cursor, it is not possible to get the time zone */
    1284             :         /* of a timestamptz column. So we fallback to asking it in text mode */
    1285             :         if (poDS->bUseBinaryCursor &&
    1286             :             poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDateTime)
    1287             :         {
    1288             :             osFieldList += "CAST (";
    1289             :             osFieldList += OGRPGEscapeColumnName(pszName);
    1290             :             osFieldList += " AS text)";
    1291             :         }
    1292             :         else
    1293             : #endif
    1294             :         {
    1295        4194 :             osFieldList += OGRPGEscapeColumnName(pszName);
    1296             :         }
    1297             :     }
    1298             : 
    1299        1139 :     return osFieldList;
    1300             : }
    1301             : 
    1302             : /************************************************************************/
    1303             : /*                         SetAttributeFilter()                         */
    1304             : /************************************************************************/
    1305             : 
    1306         166 : OGRErr OGRPGTableLayer::SetAttributeFilter(const char *pszQuery)
    1307             : 
    1308             : {
    1309         166 :     CPLFree(m_pszAttrQueryString);
    1310         166 :     m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : nullptr;
    1311             : 
    1312         166 :     if (pszQuery == nullptr)
    1313          77 :         osQuery = "";
    1314             :     else
    1315          89 :         osQuery = pszQuery;
    1316             : 
    1317         166 :     BuildWhere();
    1318             : 
    1319         166 :     ResetReading();
    1320             : 
    1321         166 :     return OGRERR_NONE;
    1322             : }
    1323             : 
    1324             : /************************************************************************/
    1325             : /*                           DeleteFeature()                            */
    1326             : /************************************************************************/
    1327             : 
    1328          26 : OGRErr OGRPGTableLayer::DeleteFeature(GIntBig nFID)
    1329             : 
    1330             : {
    1331          26 :     PGconn *hPGConn = poDS->GetPGConn();
    1332          52 :     CPLString osCommand;
    1333             : 
    1334          26 :     GetLayerDefn()->GetFieldCount();
    1335             : 
    1336          26 :     if (!bUpdateAccess)
    1337             :     {
    1338           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1339             :                  "DeleteFeature");
    1340           0 :         return OGRERR_FAILURE;
    1341             :     }
    1342             : 
    1343          26 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    1344           0 :         return OGRERR_FAILURE;
    1345          26 :     poDS->EndCopy();
    1346          26 :     bAutoFIDOnCreateViaCopy = FALSE;
    1347             : 
    1348             :     /* -------------------------------------------------------------------- */
    1349             :     /*      We can only delete features if we have a well defined FID       */
    1350             :     /*      column to target.                                               */
    1351             :     /* -------------------------------------------------------------------- */
    1352          26 :     if (pszFIDColumn == nullptr)
    1353             :     {
    1354           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1355             :                  "DeleteFeature(" CPL_FRMT_GIB
    1356             :                  ") failed.  Unable to delete features in tables without\n"
    1357             :                  "a recognised FID column.",
    1358             :                  nFID);
    1359           0 :         return OGRERR_FAILURE;
    1360             :     }
    1361             : 
    1362             :     /* -------------------------------------------------------------------- */
    1363             :     /*      Form the statement to drop the record.                          */
    1364             :     /* -------------------------------------------------------------------- */
    1365             :     osCommand.Printf("DELETE FROM %s WHERE %s = " CPL_FRMT_GIB, pszSqlTableName,
    1366          26 :                      OGRPGEscapeColumnName(pszFIDColumn).c_str(), nFID);
    1367             : 
    1368             :     /* -------------------------------------------------------------------- */
    1369             :     /*      Execute the delete.                                             */
    1370             :     /* -------------------------------------------------------------------- */
    1371             :     OGRErr eErr;
    1372             : 
    1373          26 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    1374             : 
    1375          26 :     if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    1376             :     {
    1377           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1378             :                  "DeleteFeature() DELETE statement failed.\n%s",
    1379             :                  PQerrorMessage(hPGConn));
    1380             : 
    1381           0 :         eErr = OGRERR_FAILURE;
    1382             :     }
    1383             :     else
    1384             :     {
    1385          26 :         if (EQUAL(PQcmdStatus(hResult), "DELETE 0"))
    1386          12 :             eErr = OGRERR_NON_EXISTING_FEATURE;
    1387             :         else
    1388          14 :             eErr = OGRERR_NONE;
    1389             :     }
    1390             : 
    1391          26 :     OGRPGClearResult(hResult);
    1392             : 
    1393          26 :     return eErr;
    1394             : }
    1395             : 
    1396             : /************************************************************************/
    1397             : /*                             ISetFeature()                             */
    1398             : /*                                                                      */
    1399             : /*      SetFeature() is implemented by an UPDATE SQL command            */
    1400             : /************************************************************************/
    1401             : 
    1402        2053 : OGRErr OGRPGTableLayer::ISetFeature(OGRFeature *poFeature)
    1403             : 
    1404             : {
    1405        2053 :     return IUpdateFeature(poFeature, -1, nullptr, -1, nullptr, false);
    1406             : }
    1407             : 
    1408             : /************************************************************************/
    1409             : /*                           UpdateFeature()                            */
    1410             : /************************************************************************/
    1411             : 
    1412        2071 : OGRErr OGRPGTableLayer::IUpdateFeature(OGRFeature *poFeature,
    1413             :                                        int nUpdatedFieldsCount,
    1414             :                                        const int *panUpdatedFieldsIdx,
    1415             :                                        int nUpdatedGeomFieldsCount,
    1416             :                                        const int *panUpdatedGeomFieldsIdx,
    1417             :                                        bool /* bUpdateStyleString*/)
    1418             : 
    1419             : {
    1420        2071 :     PGconn *hPGConn = poDS->GetPGConn();
    1421        4142 :     CPLString osCommand;
    1422        2071 :     bool bNeedComma = false;
    1423        2071 :     OGRErr eErr = OGRERR_FAILURE;
    1424             : 
    1425        2071 :     GetLayerDefn()->GetFieldCount();
    1426             : 
    1427        2071 :     const char *pszMethodName =
    1428        2071 :         nUpdatedFieldsCount >= 0 ? "UpdateFeature" : "SetFeature";
    1429        2071 :     if (!bUpdateAccess)
    1430             :     {
    1431           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1432             :                  pszMethodName);
    1433           0 :         return OGRERR_FAILURE;
    1434             :     }
    1435             : 
    1436        2071 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    1437           0 :         return OGRERR_FAILURE;
    1438        2071 :     poDS->EndCopy();
    1439             : 
    1440        2071 :     if (nullptr == poFeature)
    1441             :     {
    1442           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1443             :                  "NULL pointer to OGRFeature passed to %s().", pszMethodName);
    1444           0 :         return eErr;
    1445             :     }
    1446             : 
    1447        2071 :     if (poFeature->GetFID() == OGRNullFID)
    1448             :     {
    1449           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    1450             :                  "FID required on features given to %s().", pszMethodName);
    1451           2 :         return eErr;
    1452             :     }
    1453             : 
    1454        2069 :     if (pszFIDColumn == nullptr)
    1455             :     {
    1456           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1457             :                  "Unable to update features in tables without\n"
    1458             :                  "a recognised FID column.");
    1459           0 :         return eErr;
    1460             :     }
    1461             : 
    1462             :     /* In case the FID column has also been created as a regular field */
    1463        2069 :     if (iFIDAsRegularColumnIndex >= 0)
    1464             :     {
    1465           5 :         if (!poFeature->IsFieldSetAndNotNull(iFIDAsRegularColumnIndex) ||
    1466           2 :             poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex) !=
    1467           2 :                 poFeature->GetFID())
    1468             :         {
    1469           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    1470             :                      "Inconsistent values of FID and field of same name");
    1471           2 :             return OGRERR_FAILURE;
    1472             :         }
    1473             :     }
    1474             : 
    1475             :     /* -------------------------------------------------------------------- */
    1476             :     /*      Form the UPDATE command.                                        */
    1477             :     /* -------------------------------------------------------------------- */
    1478        2067 :     osCommand.Printf("UPDATE %s SET ", pszSqlTableName);
    1479             : 
    1480             :     /* Set the geometry */
    1481             :     const int nGeomStop = nUpdatedGeomFieldsCount >= 0
    1482        2067 :                               ? nUpdatedGeomFieldsCount
    1483        2051 :                               : poFeatureDefn->GetGeomFieldCount();
    1484        4119 :     for (int i = 0; i < nGeomStop; i++)
    1485             :     {
    1486        2052 :         const int iField =
    1487        2052 :             nUpdatedGeomFieldsCount >= 0 ? panUpdatedGeomFieldsIdx[i] : i;
    1488             :         OGRPGGeomFieldDefn *poGeomFieldDefn =
    1489        2052 :             poFeatureDefn->GetGeomFieldDefn(iField);
    1490        2052 :         OGRGeometry *poGeom = poFeature->GetGeomFieldRef(iField);
    1491        2052 :         if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_WKB)
    1492             :         {
    1493        1017 :             if (bNeedComma)
    1494           0 :                 osCommand += ", ";
    1495             :             else
    1496        1017 :                 bNeedComma = true;
    1497             : 
    1498        1017 :             osCommand += OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
    1499        1017 :             osCommand += " = ";
    1500        1017 :             if (poGeom != nullptr)
    1501             :             {
    1502          11 :                 if (!bWkbAsOid)
    1503             :                 {
    1504             :                     char *pszBytea =
    1505          22 :                         GeometryToBYTEA(poGeom, poDS->sPostGISVersion.nMajor,
    1506          11 :                                         poDS->sPostGISVersion.nMinor);
    1507             : 
    1508          11 :                     if (pszBytea != nullptr)
    1509             :                     {
    1510          11 :                         osCommand += "E'";
    1511          11 :                         osCommand += pszBytea;
    1512          11 :                         osCommand += '\'';
    1513          11 :                         CPLFree(pszBytea);
    1514             :                     }
    1515             :                     else
    1516           0 :                         osCommand += "NULL";
    1517             :                 }
    1518             :                 else
    1519             :                 {
    1520           0 :                     Oid oid = GeometryToOID(poGeom);
    1521             : 
    1522           0 :                     if (oid != 0)
    1523             :                     {
    1524           0 :                         osCommand += CPLString().Printf("'%d' ", oid);
    1525             :                     }
    1526             :                     else
    1527           0 :                         osCommand += "NULL";
    1528             :                 }
    1529             :             }
    1530             :             else
    1531        1006 :                 osCommand += "NULL";
    1532             :         }
    1533        1035 :         else if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ||
    1534        1034 :                  poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY)
    1535             :         {
    1536        1035 :             if (bNeedComma)
    1537           0 :                 osCommand += ", ";
    1538             :             else
    1539        1035 :                 bNeedComma = true;
    1540             : 
    1541        1035 :             osCommand += OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
    1542        1035 :             osCommand += " = ";
    1543        1035 :             if (poGeom != nullptr)
    1544             :             {
    1545          29 :                 poGeom->closeRings();
    1546          29 :                 poGeom->set3D(poGeomFieldDefn->GeometryTypeFlags &
    1547          29 :                               OGRGeometry::OGR_G_3D);
    1548          29 :                 poGeom->setMeasured(poGeomFieldDefn->GeometryTypeFlags &
    1549          29 :                                     OGRGeometry::OGR_G_MEASURED);
    1550             :             }
    1551             : 
    1552        1035 :             if (poGeom != nullptr)
    1553             :             {
    1554          58 :                 char *pszHexEWKB = OGRGeometryToHexEWKB(
    1555             :                     poGeom, poGeomFieldDefn->nSRSId,
    1556          29 :                     poDS->sPostGISVersion.nMajor, poDS->sPostGISVersion.nMinor);
    1557          29 :                 if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY)
    1558             :                     osCommand +=
    1559           1 :                         CPLString().Printf("'%s'::GEOGRAPHY", pszHexEWKB);
    1560             :                 else
    1561             :                     osCommand +=
    1562          28 :                         CPLString().Printf("'%s'::GEOMETRY", pszHexEWKB);
    1563          29 :                 CPLFree(pszHexEWKB);
    1564             :             }
    1565             :             else
    1566        1006 :                 osCommand += "NULL";
    1567             :         }
    1568             :     }
    1569             : 
    1570        2067 :     const int nStop = nUpdatedFieldsCount >= 0 ? nUpdatedFieldsCount
    1571        2051 :                                                : poFeatureDefn->GetFieldCount();
    1572        4260 :     for (int i = 0; i < nStop; i++)
    1573             :     {
    1574        2193 :         const int iField =
    1575        2193 :             nUpdatedFieldsCount >= 0 ? panUpdatedFieldsIdx[i] : i;
    1576        2193 :         if (iFIDAsRegularColumnIndex == iField)
    1577           1 :             continue;
    1578        2192 :         if (!poFeature->IsFieldSet(iField))
    1579           0 :             continue;
    1580        2192 :         if (poFeature->GetDefnRef()->GetFieldDefn(iField)->IsGenerated())
    1581           2 :             continue;
    1582             : 
    1583        2190 :         if (bNeedComma)
    1584        2175 :             osCommand += ", ";
    1585             :         else
    1586          15 :             bNeedComma = true;
    1587             : 
    1588        4380 :         osCommand += OGRPGEscapeColumnName(
    1589        4380 :             poFeatureDefn->GetFieldDefn(iField)->GetNameRef());
    1590        2190 :         osCommand += " = ";
    1591             : 
    1592        2190 :         if (poFeature->IsFieldNull(iField))
    1593             :         {
    1594          64 :             osCommand += "NULL";
    1595             :         }
    1596             :         else
    1597             :         {
    1598        2126 :             OGRPGCommonAppendFieldValue(osCommand, poFeature, iField,
    1599             :                                         OGRPGEscapeString, hPGConn);
    1600             :         }
    1601             :     }
    1602        2067 :     if (!bNeedComma)  // nothing to do
    1603           0 :         return OGRERR_NONE;
    1604             : 
    1605             :     /* Add the WHERE clause */
    1606        2067 :     osCommand += " WHERE ";
    1607        2067 :     osCommand += OGRPGEscapeColumnName(pszFIDColumn);
    1608        2067 :     osCommand += +" = ";
    1609        2067 :     osCommand += CPLString().Printf(CPL_FRMT_GIB, poFeature->GetFID());
    1610             : 
    1611             :     /* -------------------------------------------------------------------- */
    1612             :     /*      Execute the update.                                             */
    1613             :     /* -------------------------------------------------------------------- */
    1614        2067 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    1615        2067 :     if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    1616             :     {
    1617           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1618             :                  "UPDATE command for feature " CPL_FRMT_GIB
    1619             :                  " failed.\n%s\nCommand: %s",
    1620             :                  poFeature->GetFID(), PQerrorMessage(hPGConn),
    1621             :                  osCommand.c_str());
    1622             : 
    1623           0 :         OGRPGClearResult(hResult);
    1624             : 
    1625           0 :         return OGRERR_FAILURE;
    1626             :     }
    1627             : 
    1628        2067 :     if (EQUAL(PQcmdStatus(hResult), "UPDATE 0"))
    1629           9 :         eErr = OGRERR_NON_EXISTING_FEATURE;
    1630             :     else
    1631        2058 :         eErr = OGRERR_NONE;
    1632             : 
    1633        2067 :     OGRPGClearResult(hResult);
    1634             : 
    1635        2067 :     return eErr;
    1636             : }
    1637             : 
    1638             : /************************************************************************/
    1639             : /*                           ICreateFeature()                            */
    1640             : /************************************************************************/
    1641             : 
    1642        3866 : OGRErr OGRPGTableLayer::ICreateFeature(OGRFeature *poFeature)
    1643             : {
    1644        3866 :     GetLayerDefn()->GetFieldCount();
    1645             : 
    1646        3866 :     if (!bUpdateAccess)
    1647             :     {
    1648           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    1649             :                  "CreateFeature");
    1650           0 :         return OGRERR_FAILURE;
    1651             :     }
    1652             : 
    1653        3866 :     if (nullptr == poFeature)
    1654             :     {
    1655           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1656             :                  "NULL pointer to OGRFeature passed to CreateFeature().");
    1657           0 :         return OGRERR_FAILURE;
    1658             :     }
    1659             : 
    1660        3866 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    1661           0 :         return OGRERR_FAILURE;
    1662             : 
    1663             :     /* In case the FID column has also been created as a regular field */
    1664        3866 :     GIntBig nFID = poFeature->GetFID();
    1665        3866 :     if (iFIDAsRegularColumnIndex >= 0)
    1666             :     {
    1667           4 :         if (nFID == OGRNullFID)
    1668             :         {
    1669           3 :             if (poFeature->IsFieldSetAndNotNull(iFIDAsRegularColumnIndex))
    1670             :             {
    1671           2 :                 poFeature->SetFID(
    1672           2 :                     poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex));
    1673             :             }
    1674             :         }
    1675             :         else
    1676             :         {
    1677           2 :             if (!poFeature->IsFieldSetAndNotNull(iFIDAsRegularColumnIndex) ||
    1678           1 :                 poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex) !=
    1679             :                     nFID)
    1680             :             {
    1681           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1682             :                          "Inconsistent values of FID and field of same name");
    1683           1 :                 return OGRERR_FAILURE;
    1684             :             }
    1685             :         }
    1686             :     }
    1687             : 
    1688             :     /* Auto-promote FID column to 64bit if necessary */
    1689        3869 :     if (pszFIDColumn != nullptr && !CPL_INT64_FITS_ON_INT32(nFID) &&
    1690           4 :         OGRLayer::GetMetadataItem(OLMD_FID64) == nullptr)
    1691             :     {
    1692           2 :         poDS->EndCopy();
    1693             : 
    1694           2 :         CPLString osCommand;
    1695             :         osCommand.Printf("ALTER TABLE %s ALTER COLUMN %s TYPE INT8",
    1696             :                          pszSqlTableName,
    1697           2 :                          OGRPGEscapeColumnName(pszFIDColumn).c_str());
    1698           2 :         PGconn *hPGConn = poDS->GetPGConn();
    1699           2 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    1700           2 :         if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    1701             :         {
    1702           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    1703             :                      PQerrorMessage(hPGConn));
    1704             : 
    1705           0 :             OGRPGClearResult(hResult);
    1706             : 
    1707           0 :             return OGRERR_FAILURE;
    1708             :         }
    1709           2 :         OGRPGClearResult(hResult);
    1710             : 
    1711           2 :         OGRLayer::SetMetadataItem(OLMD_FID64, "YES");
    1712             :     }
    1713             : 
    1714        3865 :     if (bFirstInsertion)
    1715             :     {
    1716         195 :         bFirstInsertion = FALSE;
    1717         195 :         if (CPLTestBool(CPLGetConfigOption("OGR_TRUNCATE", "NO")))
    1718             :         {
    1719           1 :             PGconn *hPGConn = poDS->GetPGConn();
    1720           2 :             CPLString osCommand;
    1721             : 
    1722           1 :             osCommand.Printf("TRUNCATE TABLE %s", pszSqlTableName);
    1723           1 :             PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    1724           1 :             OGRPGClearResult(hResult);
    1725             :         }
    1726             :     }
    1727             : 
    1728             :     // We avoid testing the config option too often.
    1729        3865 :     if (bUseCopy == USE_COPY_UNSET)
    1730          53 :         bUseCopy = CPLTestBool(CPLGetConfigOption("PG_USE_COPY", "NO"));
    1731             : 
    1732             :     OGRErr eErr;
    1733        3865 :     if (!bUseCopy)
    1734             :     {
    1735         120 :         eErr = CreateFeatureViaInsert(poFeature);
    1736             :     }
    1737             :     else
    1738             :     {
    1739             :         /* If there's a unset field with a default value, then we must use */
    1740             :         /* a specific INSERT statement to avoid unset fields to be bound to NULL
    1741             :          */
    1742        3745 :         bool bHasDefaultValue = false;
    1743        3745 :         const int nFieldCount = poFeatureDefn->GetFieldCount();
    1744        8972 :         for (int iField = 0; iField < nFieldCount; iField++)
    1745             :         {
    1746        6487 :             if (!poFeature->IsFieldSet(iField) &&
    1747        1259 :                 poFeature->GetFieldDefnRef(iField)->GetDefault() != nullptr)
    1748             :             {
    1749           1 :                 bHasDefaultValue = true;
    1750           1 :                 break;
    1751             :             }
    1752             :         }
    1753        3745 :         if (bHasDefaultValue)
    1754             :         {
    1755           1 :             eErr = CreateFeatureViaInsert(poFeature);
    1756             :         }
    1757             :         else
    1758             :         {
    1759             :             bool bFIDSet =
    1760        3744 :                 (pszFIDColumn != nullptr && poFeature->GetFID() != OGRNullFID);
    1761        3744 :             if (bCopyActive && bFIDSet != bFIDColumnInCopyFields)
    1762             :             {
    1763           1 :                 eErr = CreateFeatureViaInsert(poFeature);
    1764             :             }
    1765         189 :             else if (!bCopyActive && poFeatureDefn->GetFieldCount() == 0 &&
    1766        3932 :                      poFeatureDefn->GetGeomFieldCount() == 0 && !bFIDSet)
    1767             :             {
    1768           4 :                 eErr = CreateFeatureViaInsert(poFeature);
    1769             :             }
    1770             :             else
    1771             :             {
    1772        3739 :                 if (!bCopyActive)
    1773             :                 {
    1774             :                     /* This is a heuristics. If the first feature to be copied
    1775             :                      * has a */
    1776             :                     /* FID set (and that a FID column has been identified), then
    1777             :                      * we will */
    1778             :                     /* try to copy FID values from features. Otherwise, we will
    1779             :                      * not */
    1780             :                     /* do and assume that the FID column is an autoincremented
    1781             :                      * column. */
    1782         185 :                     bFIDColumnInCopyFields = bFIDSet;
    1783         185 :                     bNeedToUpdateSequence = bFIDSet;
    1784             :                 }
    1785             : 
    1786        3739 :                 eErr = CreateFeatureViaCopy(poFeature);
    1787        3739 :                 if (bFIDSet)
    1788          14 :                     bAutoFIDOnCreateViaCopy = FALSE;
    1789        3739 :                 if (eErr == OGRERR_NONE && bAutoFIDOnCreateViaCopy)
    1790             :                 {
    1791        3704 :                     poFeature->SetFID(++iNextShapeId);
    1792             :                 }
    1793             :             }
    1794             :         }
    1795             :     }
    1796             : 
    1797        3865 :     if (eErr == OGRERR_NONE && iFIDAsRegularColumnIndex >= 0)
    1798             :     {
    1799           3 :         poFeature->SetField(iFIDAsRegularColumnIndex, poFeature->GetFID());
    1800             :     }
    1801             : 
    1802        3865 :     return eErr;
    1803             : }
    1804             : 
    1805             : /************************************************************************/
    1806             : /*                       OGRPGEscapeColumnName( )                       */
    1807             : /************************************************************************/
    1808             : 
    1809       16795 : CPLString OGRPGEscapeColumnName(const char *pszColumnName)
    1810             : {
    1811       16795 :     CPLString osStr = "\"";
    1812             : 
    1813       16795 :     char ch = '\0';
    1814      210049 :     for (int i = 0; (ch = pszColumnName[i]) != '\0'; i++)
    1815             :     {
    1816      193254 :         if (ch == '"')
    1817           0 :             osStr.append(1, ch);
    1818      193254 :         osStr.append(1, ch);
    1819             :     }
    1820             : 
    1821       16795 :     osStr += "\"";
    1822             : 
    1823       16795 :     return osStr;
    1824             : }
    1825             : 
    1826             : /************************************************************************/
    1827             : /*                         OGRPGEscapeString( )                         */
    1828             : /************************************************************************/
    1829             : 
    1830        3659 : CPLString OGRPGEscapeString(void *hPGConnIn, const char *pszStrValue,
    1831             :                             int /* nMaxWidth */, const char * /*pszTableName*/,
    1832             :                             const char * /*pszFieldName*/)
    1833             : {
    1834        3659 :     PGconn *hPGConn = reinterpret_cast<PGconn *>(hPGConnIn);
    1835        3659 :     CPLString osCommand;
    1836             : 
    1837             :     /* We need to quote and escape string fields. */
    1838        3659 :     osCommand += "'";
    1839             : 
    1840        3659 :     const size_t nSrcLen = strlen(pszStrValue);
    1841        3659 :     char *pszDestStr = static_cast<char *>(CPLMalloc(2 * nSrcLen + 1));
    1842             : 
    1843        3659 :     int nError = 0;
    1844        3659 :     PQescapeStringConn(hPGConn, pszDestStr, pszStrValue, nSrcLen, &nError);
    1845        3659 :     if (nError == 0)
    1846        3659 :         osCommand += pszDestStr;
    1847             :     else
    1848           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1849             :                  "PQescapeString(): %s\n"
    1850             :                  "  input: '%s'\n"
    1851             :                  "    got: '%s'\n",
    1852             :                  PQerrorMessage(hPGConn), pszStrValue, pszDestStr);
    1853             : 
    1854        3659 :     CPLFree(pszDestStr);
    1855             : 
    1856        3659 :     osCommand += "'";
    1857             : 
    1858        7318 :     return osCommand;
    1859             : }
    1860             : 
    1861             : /************************************************************************/
    1862             : /*                       CreateFeatureViaInsert()                       */
    1863             : /************************************************************************/
    1864             : 
    1865         126 : OGRErr OGRPGTableLayer::CreateFeatureViaInsert(OGRFeature *poFeature)
    1866             : 
    1867             : {
    1868         126 :     PGconn *hPGConn = poDS->GetPGConn();
    1869         252 :     CPLString osCommand;
    1870         126 :     int bNeedComma = FALSE;
    1871             : 
    1872         126 :     int bEmptyInsert = FALSE;
    1873             : 
    1874         126 :     poDS->EndCopy();
    1875             : 
    1876             :     /* -------------------------------------------------------------------- */
    1877             :     /*      Form the INSERT command.                                        */
    1878             :     /* -------------------------------------------------------------------- */
    1879         126 :     osCommand.Printf("INSERT INTO %s (", pszSqlTableName);
    1880             : 
    1881         237 :     for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    1882             :     {
    1883         111 :         OGRGeomFieldDefn *poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(i);
    1884         111 :         OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
    1885         111 :         if (poGeom == nullptr)
    1886          28 :             continue;
    1887          83 :         if (!bNeedComma)
    1888          83 :             bNeedComma = TRUE;
    1889             :         else
    1890           0 :             osCommand += ", ";
    1891          83 :         osCommand += OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()) + " ";
    1892             :     }
    1893             : 
    1894             :     /* Use case of ogr_pg_60 test */
    1895         126 :     if (poFeature->GetFID() != OGRNullFID && pszFIDColumn != nullptr)
    1896             :     {
    1897           7 :         bNeedToUpdateSequence = true;
    1898             : 
    1899           7 :         if (bNeedComma)
    1900           5 :             osCommand += ", ";
    1901             : 
    1902           7 :         osCommand = osCommand + OGRPGEscapeColumnName(pszFIDColumn) + " ";
    1903           7 :         bNeedComma = TRUE;
    1904             :     }
    1905             :     else
    1906             :     {
    1907         119 :         UpdateSequenceIfNeeded();
    1908             :     }
    1909             : 
    1910         126 :     const int nFieldCount = poFeatureDefn->GetFieldCount();
    1911         572 :     for (int i = 0; i < nFieldCount; i++)
    1912             :     {
    1913         446 :         if (iFIDAsRegularColumnIndex == i)
    1914           1 :             continue;
    1915         445 :         if (!poFeature->IsFieldSet(i))
    1916         185 :             continue;
    1917         260 :         if (poFeature->GetDefnRef()->GetFieldDefn(i)->IsGenerated())
    1918           2 :             continue;
    1919             : 
    1920         258 :         if (!bNeedComma)
    1921          28 :             bNeedComma = TRUE;
    1922             :         else
    1923         230 :             osCommand += ", ";
    1924             : 
    1925             :         osCommand +=
    1926         258 :             OGRPGEscapeColumnName(poFeatureDefn->GetFieldDefn(i)->GetNameRef());
    1927             :     }
    1928             : 
    1929         126 :     if (!bNeedComma)
    1930          13 :         bEmptyInsert = TRUE;
    1931             : 
    1932         126 :     osCommand += ") VALUES (";
    1933             : 
    1934             :     /* Set the geometry */
    1935         126 :     bNeedComma = FALSE;
    1936         237 :     for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    1937             :     {
    1938             :         const OGRPGGeomFieldDefn *poGeomFieldDefn =
    1939         111 :             poFeatureDefn->GetGeomFieldDefn(i);
    1940         111 :         OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
    1941         111 :         if (poGeom == nullptr)
    1942          28 :             continue;
    1943          83 :         if (bNeedComma)
    1944           0 :             osCommand += ", ";
    1945             :         else
    1946          83 :             bNeedComma = TRUE;
    1947             : 
    1948          83 :         if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ||
    1949          83 :             poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY)
    1950             :         {
    1951          54 :             CheckGeomTypeCompatibility(i, poGeom);
    1952             : 
    1953          54 :             poGeom->closeRings();
    1954          54 :             poGeom->set3D(poGeomFieldDefn->GeometryTypeFlags &
    1955          54 :                           OGRGeometry::OGR_G_3D);
    1956          54 :             poGeom->setMeasured(poGeomFieldDefn->GeometryTypeFlags &
    1957          54 :                                 OGRGeometry::OGR_G_MEASURED);
    1958             : 
    1959          54 :             int nSRSId = poGeomFieldDefn->nSRSId;
    1960             : 
    1961         108 :             char *pszHexEWKB = OGRGeometryToHexEWKB(
    1962          54 :                 poGeom, nSRSId, poDS->sPostGISVersion.nMajor,
    1963          54 :                 poDS->sPostGISVersion.nMinor);
    1964          54 :             if (!pszHexEWKB || pszHexEWKB[0] == 0)
    1965             :             {
    1966           0 :                 CPLFree(pszHexEWKB);
    1967           0 :                 return OGRERR_FAILURE;
    1968             :             }
    1969          54 :             osCommand += '\'';
    1970             :             try
    1971             :             {
    1972          54 :                 osCommand += pszHexEWKB;
    1973             :             }
    1974           0 :             catch (const std::bad_alloc &)
    1975             :             {
    1976           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory,
    1977             :                          "Out of memory: too large geometry");
    1978           0 :                 CPLFree(pszHexEWKB);
    1979           0 :                 return OGRERR_FAILURE;
    1980             :             }
    1981          54 :             osCommand += "'::";
    1982          54 :             if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY)
    1983           0 :                 osCommand += "GEOGRAPHY";
    1984             :             else
    1985          54 :                 osCommand += "GEOMETRY";
    1986          54 :             CPLFree(pszHexEWKB);
    1987             :         }
    1988          29 :         else if (!bWkbAsOid)
    1989             :         {
    1990             :             char *pszBytea =
    1991          58 :                 GeometryToBYTEA(poGeom, poDS->sPostGISVersion.nMajor,
    1992          29 :                                 poDS->sPostGISVersion.nMinor);
    1993             : 
    1994          29 :             if (!pszBytea)
    1995             :             {
    1996           0 :                 return OGRERR_FAILURE;
    1997             :             }
    1998          29 :             osCommand += "E'";
    1999             :             try
    2000             :             {
    2001          29 :                 osCommand += pszBytea;
    2002             :             }
    2003           0 :             catch (const std::bad_alloc &)
    2004             :             {
    2005           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory,
    2006             :                          "Out of memory: too large geometry");
    2007           0 :                 CPLFree(pszBytea);
    2008           0 :                 return OGRERR_FAILURE;
    2009             :             }
    2010          29 :             osCommand += '\'';
    2011          29 :             CPLFree(pszBytea);
    2012             :         }
    2013           0 :         else if (poGeomFieldDefn->ePostgisType ==
    2014             :                  GEOM_TYPE_WKB /* && bWkbAsOid */)
    2015             :         {
    2016           0 :             Oid oid = GeometryToOID(poGeom);
    2017             : 
    2018           0 :             if (oid != 0)
    2019             :             {
    2020           0 :                 osCommand += CPLString().Printf("'%d' ", oid);
    2021             :             }
    2022             :             else
    2023           0 :                 osCommand += "''";
    2024             :         }
    2025             :     }
    2026             : 
    2027         126 :     if (poFeature->GetFID() != OGRNullFID && pszFIDColumn != nullptr)
    2028             :     {
    2029           7 :         if (bNeedComma)
    2030           5 :             osCommand += ", ";
    2031           7 :         osCommand += CPLString().Printf(CPL_FRMT_GIB " ", poFeature->GetFID());
    2032           7 :         bNeedComma = TRUE;
    2033             :     }
    2034             : 
    2035         572 :     for (int i = 0; i < nFieldCount; i++)
    2036             :     {
    2037         446 :         if (iFIDAsRegularColumnIndex == i)
    2038           1 :             continue;
    2039         445 :         if (!poFeature->IsFieldSet(i))
    2040         185 :             continue;
    2041         260 :         if (poFeature->GetDefnRef()->GetFieldDefn(i)->IsGenerated())
    2042           2 :             continue;
    2043             : 
    2044         258 :         if (bNeedComma)
    2045         230 :             osCommand += ", ";
    2046             :         else
    2047          28 :             bNeedComma = TRUE;
    2048             : 
    2049         258 :         OGRPGCommonAppendFieldValue(osCommand, poFeature, i, OGRPGEscapeString,
    2050             :                                     hPGConn);
    2051             :     }
    2052             : 
    2053         126 :     osCommand += ")";
    2054             : 
    2055         126 :     if (bEmptyInsert)
    2056          13 :         osCommand.Printf("INSERT INTO %s DEFAULT VALUES", pszSqlTableName);
    2057             : 
    2058         126 :     int bReturnRequested = FALSE;
    2059             :     /* We only get the FID, but we also could add the unset fields to get */
    2060             :     /* the default values */
    2061         245 :     if (bRetrieveFID && pszFIDColumn != nullptr &&
    2062         119 :         poFeature->GetFID() == OGRNullFID)
    2063             :     {
    2064         112 :         if (bSkipConflicts)
    2065             :         {
    2066           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    2067             :                      "fid retrieval and skipping conflicts are not supported "
    2068             :                      "at the same time.");
    2069           1 :             return OGRERR_FAILURE;
    2070             :         }
    2071         111 :         bReturnRequested = TRUE;
    2072         111 :         osCommand += " RETURNING ";
    2073         111 :         osCommand += OGRPGEscapeColumnName(pszFIDColumn);
    2074             :     }
    2075          14 :     else if (bSkipConflicts)
    2076           2 :         osCommand += " ON CONFLICT DO NOTHING";
    2077             : 
    2078             :     /* -------------------------------------------------------------------- */
    2079             :     /*      Execute the insert.                                             */
    2080             :     /* -------------------------------------------------------------------- */
    2081         125 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    2082         111 :     if (bReturnRequested && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
    2083         236 :         PQntuples(hResult) == 1 && PQnfields(hResult) == 1)
    2084             :     {
    2085         109 :         const char *pszFID = PQgetvalue(hResult, 0, 0);
    2086         109 :         poFeature->SetFID(CPLAtoGIntBig(pszFID));
    2087             :     }
    2088          16 :     else if (bReturnRequested || PQresultStatus(hResult) != PGRES_COMMAND_OK)
    2089             :     {
    2090           3 :         CPLError(CE_Failure, CPLE_AppDefined,
    2091             :                  "INSERT command for new feature failed.\n%s\nCommand: %s",
    2092           6 :                  PQerrorMessage(hPGConn), osCommand.substr(0, 1024).c_str());
    2093             : 
    2094           3 :         if (!bHasWarnedAlreadySetFID && poFeature->GetFID() != OGRNullFID &&
    2095           0 :             pszFIDColumn != nullptr)
    2096             :         {
    2097           0 :             bHasWarnedAlreadySetFID = TRUE;
    2098           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2099             :                      "You've inserted feature with an already set FID and "
    2100             :                      "that's perhaps the reason for the failure. "
    2101             :                      "If so, this can happen if you reuse the same feature "
    2102             :                      "object for sequential insertions. "
    2103             :                      "Indeed, since GDAL 1.8.0, the FID of an inserted feature "
    2104             :                      "is got from the server, so it is not a good idea"
    2105             :                      "to reuse it afterwards... All in all, try unsetting the "
    2106             :                      "FID with SetFID(-1) before calling CreateFeature()");
    2107             :         }
    2108             : 
    2109           3 :         OGRPGClearResult(hResult);
    2110             : 
    2111           3 :         return OGRERR_FAILURE;
    2112             :     }
    2113             : 
    2114         122 :     OGRPGClearResult(hResult);
    2115             : 
    2116         122 :     return OGRERR_NONE;
    2117             : }
    2118             : 
    2119             : /************************************************************************/
    2120             : /*                        CreateFeatureViaCopy()                        */
    2121             : /************************************************************************/
    2122             : 
    2123        3739 : OGRErr OGRPGTableLayer::CreateFeatureViaCopy(OGRFeature *poFeature)
    2124             : {
    2125        3739 :     PGconn *hPGConn = poDS->GetPGConn();
    2126        7478 :     CPLString osCommand;
    2127             : 
    2128             :     /* Tell the datasource we are now planning to copy data */
    2129        3739 :     poDS->StartCopy(this);
    2130             : 
    2131             :     /* First process geometry */
    2132        7454 :     for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    2133             :     {
    2134             :         const OGRPGGeomFieldDefn *poGeomFieldDefn =
    2135        3715 :             poFeatureDefn->GetGeomFieldDefn(i);
    2136        3715 :         OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
    2137             : 
    2138        3715 :         char *pszGeom = nullptr;
    2139        3715 :         if (nullptr != poGeom)
    2140             :         {
    2141        1648 :             CheckGeomTypeCompatibility(i, poGeom);
    2142             : 
    2143        1648 :             poGeom->closeRings();
    2144        1648 :             poGeom->set3D(poGeomFieldDefn->GeometryTypeFlags &
    2145        1648 :                           OGRGeometry::OGR_G_3D);
    2146        1648 :             poGeom->setMeasured(poGeomFieldDefn->GeometryTypeFlags &
    2147        1648 :                                 OGRGeometry::OGR_G_MEASURED);
    2148             : 
    2149        1648 :             if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_WKB)
    2150         789 :                 pszGeom = GeometryToBYTEA(poGeom, poDS->sPostGISVersion.nMajor,
    2151         789 :                                           poDS->sPostGISVersion.nMinor);
    2152             :             else
    2153         859 :                 pszGeom = OGRGeometryToHexEWKB(poGeom, poGeomFieldDefn->nSRSId,
    2154         859 :                                                poDS->sPostGISVersion.nMajor,
    2155         859 :                                                poDS->sPostGISVersion.nMinor);
    2156        1648 :             if (!pszGeom || pszGeom[0] == 0)
    2157             :             {
    2158           0 :                 CPLFree(pszGeom);
    2159           0 :                 return OGRERR_FAILURE;
    2160             :             }
    2161             :         }
    2162             : 
    2163        3715 :         if (!osCommand.empty())
    2164          12 :             osCommand += "\t";
    2165             : 
    2166        3715 :         if (pszGeom)
    2167             :         {
    2168             :             try
    2169             :             {
    2170        1648 :                 osCommand += pszGeom;
    2171             :             }
    2172           0 :             catch (const std::bad_alloc &)
    2173             :             {
    2174           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory,
    2175             :                          "Out of memory: too large geometry");
    2176           0 :                 CPLFree(pszGeom);
    2177           0 :                 return OGRERR_FAILURE;
    2178             :             }
    2179        1648 :             CPLFree(pszGeom);
    2180             :         }
    2181             :         else
    2182             :         {
    2183        2067 :             osCommand += "\\N";
    2184             :         }
    2185             :     }
    2186             : 
    2187        7478 :     std::vector<bool> abFieldsToInclude(poFeature->GetFieldCount(), true);
    2188        8963 :     for (size_t i = 0; i < abFieldsToInclude.size(); i++)
    2189       10448 :         abFieldsToInclude[i] = !poFeature->GetDefnRef()
    2190        5224 :                                     ->GetFieldDefn(static_cast<int>(i))
    2191        5224 :                                     ->IsGenerated();
    2192             : 
    2193        3739 :     if (bFIDColumnInCopyFields)
    2194             :     {
    2195          14 :         OGRPGCommonAppendCopyFID(osCommand, poFeature);
    2196             :     }
    2197        3739 :     OGRPGCommonAppendCopyRegularFields(osCommand, poFeature, pszFIDColumn,
    2198             :                                        abFieldsToInclude, OGRPGEscapeString,
    2199             :                                        hPGConn);
    2200             : 
    2201             :     /* Add end of line marker */
    2202        3739 :     osCommand += "\n";
    2203             : 
    2204             :     // PostgreSQL doesn't provide very helpful reporting of invalid UTF-8
    2205             :     // content in COPY mode.
    2206        7478 :     if (poDS->IsUTF8ClientEncoding() &&
    2207        3739 :         !CPLIsUTF8(osCommand.c_str(), static_cast<int>(osCommand.size())))
    2208             :     {
    2209           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2210             :                  "Non UTF-8 content found when writing feature " CPL_FRMT_GIB
    2211             :                  " of layer %s: %s",
    2212           0 :                  poFeature->GetFID(), poFeatureDefn->GetName(),
    2213             :                  osCommand.c_str());
    2214           0 :         return OGRERR_FAILURE;
    2215             :     }
    2216             : 
    2217             :     /* ------------------------------------------------------------ */
    2218             :     /*      Execute the copy.                                       */
    2219             :     /* ------------------------------------------------------------ */
    2220             : 
    2221        3739 :     OGRErr result = OGRERR_NONE;
    2222             : 
    2223        3739 :     int copyResult = PQputCopyData(hPGConn, osCommand.c_str(),
    2224        3739 :                                    static_cast<int>(osCommand.size()));
    2225             : #ifdef DEBUG_VERBOSE
    2226             :     CPLDebug("PG", "PQputCopyData(%s)", osCommand.c_str());
    2227             : #endif
    2228             : 
    2229        3739 :     switch (copyResult)
    2230             :     {
    2231           0 :         case 0:
    2232           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Writing COPY data blocked.");
    2233           0 :             result = OGRERR_FAILURE;
    2234           0 :             break;
    2235           0 :         case -1:
    2236           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s",
    2237             :                      PQerrorMessage(hPGConn));
    2238           0 :             result = OGRERR_FAILURE;
    2239           0 :             break;
    2240             :     }
    2241             : 
    2242        3739 :     return result;
    2243             : }
    2244             : 
    2245             : /************************************************************************/
    2246             : /*                           TestCapability()                           */
    2247             : /************************************************************************/
    2248             : 
    2249         807 : int OGRPGTableLayer::TestCapability(const char *pszCap)
    2250             : 
    2251             : {
    2252         807 :     if (bUpdateAccess)
    2253             :     {
    2254         802 :         if (EQUAL(pszCap, OLCSequentialWrite) ||
    2255         795 :             EQUAL(pszCap, OLCCreateField) ||
    2256         783 :             EQUAL(pszCap, OLCCreateGeomField) ||
    2257         782 :             EQUAL(pszCap, OLCDeleteField) || EQUAL(pszCap, OLCAlterFieldDefn) ||
    2258         758 :             EQUAL(pszCap, OLCAlterGeomFieldDefn) || EQUAL(pszCap, OLCRename))
    2259          53 :             return TRUE;
    2260             : 
    2261         749 :         else if (EQUAL(pszCap, OLCRandomWrite) ||
    2262         742 :                  EQUAL(pszCap, OLCUpdateFeature) ||
    2263         740 :                  EQUAL(pszCap, OLCDeleteFeature))
    2264             :         {
    2265          19 :             GetLayerDefn()->GetFieldCount();
    2266          19 :             return pszFIDColumn != nullptr;
    2267             :         }
    2268             :     }
    2269             : 
    2270         735 :     if (EQUAL(pszCap, OLCRandomRead))
    2271             :     {
    2272          12 :         GetLayerDefn()->GetFieldCount();
    2273          12 :         return pszFIDColumn != nullptr;
    2274             :     }
    2275             : 
    2276         723 :     else if (EQUAL(pszCap, OLCFastFeatureCount) ||
    2277         569 :              EQUAL(pszCap, OLCFastSetNextByIndex))
    2278             :     {
    2279         185 :         if (m_poFilterGeom == nullptr)
    2280         159 :             return TRUE;
    2281          26 :         OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
    2282          26 :         if (poFeatureDefn->GetGeomFieldCount() > 0)
    2283             :             poGeomFieldDefn =
    2284          26 :                 poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
    2285          52 :         return poGeomFieldDefn == nullptr ||
    2286          26 :                (poDS->sPostGISVersion.nMajor >= 0 &&
    2287          23 :                 (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ||
    2288          34 :                  poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY));
    2289             :     }
    2290             : 
    2291         538 :     else if (EQUAL(pszCap, OLCFastSpatialFilter))
    2292             :     {
    2293           2 :         OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
    2294           2 :         if (poFeatureDefn->GetGeomFieldCount() > 0)
    2295             :             poGeomFieldDefn =
    2296           2 :                 poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
    2297           4 :         return poGeomFieldDefn == nullptr ||
    2298           2 :                (poDS->sPostGISVersion.nMajor >= 0 &&
    2299           1 :                 (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ||
    2300           2 :                  poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY));
    2301             :     }
    2302             : 
    2303         536 :     else if (EQUAL(pszCap, OLCTransactions))
    2304           7 :         return TRUE;
    2305             : 
    2306         529 :     else if (EQUAL(pszCap, OLCFastGetExtent) ||
    2307         494 :              EQUAL(pszCap, OLCFastGetExtent3D))
    2308             :     {
    2309          37 :         OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
    2310          37 :         if (poFeatureDefn->GetGeomFieldCount() > 0)
    2311          37 :             poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(0);
    2312          37 :         return poGeomFieldDefn != nullptr &&
    2313          70 :                poDS->sPostGISVersion.nMajor >= 0 &&
    2314          70 :                poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY;
    2315             :     }
    2316             : 
    2317         492 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
    2318           7 :         return TRUE;
    2319             : 
    2320         485 :     else if (EQUAL(pszCap, OLCCurveGeometries))
    2321         239 :         return TRUE;
    2322             : 
    2323         246 :     else if (EQUAL(pszCap, OLCMeasuredGeometries))
    2324         213 :         return TRUE;
    2325             : 
    2326          33 :     else if (EQUAL(pszCap, OLCZGeometries))
    2327          15 :         return TRUE;
    2328             : 
    2329             :     else
    2330          18 :         return FALSE;
    2331             : }
    2332             : 
    2333             : /************************************************************************/
    2334             : /*                            CreateField()                             */
    2335             : /************************************************************************/
    2336             : 
    2337         607 : OGRErr OGRPGTableLayer::CreateField(const OGRFieldDefn *poFieldIn,
    2338             :                                     int bApproxOK)
    2339             : 
    2340             : {
    2341         607 :     PGconn *hPGConn = poDS->GetPGConn();
    2342        1214 :     CPLString osCommand;
    2343        1214 :     CPLString osFieldType;
    2344        1214 :     OGRFieldDefn oField(poFieldIn);
    2345             : 
    2346         607 :     GetLayerDefn()->GetFieldCount();
    2347             : 
    2348         607 :     if (!bUpdateAccess)
    2349             :     {
    2350           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    2351             :                  "CreateField");
    2352           0 :         return OGRERR_FAILURE;
    2353             :     }
    2354             : 
    2355         609 :     if (pszFIDColumn != nullptr && EQUAL(oField.GetNameRef(), pszFIDColumn) &&
    2356        1216 :         oField.GetType() != OFTInteger && oField.GetType() != OFTInteger64)
    2357             :     {
    2358           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s",
    2359             :                  oField.GetNameRef());
    2360           1 :         return OGRERR_FAILURE;
    2361             :     }
    2362             : 
    2363             :     /* -------------------------------------------------------------------- */
    2364             :     /*      Do we want to "launder" the column names into Postgres          */
    2365             :     /*      friendly format?                                                */
    2366             :     /* -------------------------------------------------------------------- */
    2367         606 :     if (bLaunderColumnNames)
    2368             :     {
    2369             :         char *pszSafeName =
    2370         604 :             OGRPGCommonLaunderName(oField.GetNameRef(), "PG", m_bUTF8ToASCII);
    2371             : 
    2372         604 :         oField.SetName(pszSafeName);
    2373         604 :         CPLFree(pszSafeName);
    2374             : 
    2375         604 :         if (EQUAL(oField.GetNameRef(), "oid"))
    2376             :         {
    2377           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2378             :                      "Renaming field 'oid' to 'oid_' to avoid conflict with "
    2379             :                      "internal oid field.");
    2380           0 :             oField.SetName("oid_");
    2381             :         }
    2382             :     }
    2383             : 
    2384             :     const char *pszOverrideType =
    2385         606 :         CSLFetchNameValue(papszOverrideColumnTypes, oField.GetNameRef());
    2386         606 :     if (pszOverrideType != nullptr)
    2387           3 :         osFieldType = pszOverrideType;
    2388             :     else
    2389             :     {
    2390        1206 :         osFieldType = OGRPGCommonLayerGetType(
    2391        1206 :             oField, CPL_TO_BOOL(bPreservePrecision), CPL_TO_BOOL(bApproxOK));
    2392         603 :         if (osFieldType.empty())
    2393           0 :             return OGRERR_FAILURE;
    2394             :     }
    2395             : 
    2396        1212 :     CPLString osConstraints;
    2397         606 :     if (!oField.IsNullable())
    2398           1 :         osConstraints += " NOT NULL";
    2399         606 :     if (oField.IsUnique())
    2400           4 :         osConstraints += " UNIQUE";
    2401         606 :     if (oField.GetDefault() != nullptr && !oField.IsDefaultDriverSpecific())
    2402             :     {
    2403           8 :         osConstraints += " DEFAULT ";
    2404           8 :         osConstraints += OGRPGCommonLayerGetPGDefault(&oField);
    2405             :     }
    2406             : 
    2407        1212 :     std::string osCommentON;
    2408         606 :     if (!oField.GetComment().empty())
    2409             :     {
    2410           4 :         osCommentON = "COMMENT ON COLUMN ";
    2411           4 :         osCommentON += pszSqlTableName;
    2412           4 :         osCommentON += '.';
    2413           4 :         osCommentON += OGRPGEscapeColumnName(oField.GetNameRef());
    2414           4 :         osCommentON += " IS ";
    2415           4 :         osCommentON += OGRPGEscapeString(hPGConn, oField.GetComment().c_str());
    2416             :     }
    2417             : 
    2418             :     /* -------------------------------------------------------------------- */
    2419             :     /*      Create the new field.                                           */
    2420             :     /* -------------------------------------------------------------------- */
    2421         606 :     if (bDeferredCreation)
    2422             :     {
    2423         582 :         if (!(pszFIDColumn != nullptr &&
    2424         291 :               EQUAL(pszFIDColumn, oField.GetNameRef())))
    2425             :         {
    2426         290 :             osCreateTable += ", ";
    2427         290 :             osCreateTable += OGRPGEscapeColumnName(oField.GetNameRef());
    2428         290 :             osCreateTable += " ";
    2429         290 :             osCreateTable += osFieldType;
    2430         290 :             osCreateTable += osConstraints;
    2431             : 
    2432         290 :             if (!osCommentON.empty())
    2433           1 :                 m_aosDeferredCommentOnColumns.push_back(std::move(osCommentON));
    2434             :         }
    2435             :     }
    2436             :     else
    2437             :     {
    2438         315 :         poDS->EndCopy();
    2439             : 
    2440             :         osCommand.Printf("ALTER TABLE %s ADD COLUMN %s %s", pszSqlTableName,
    2441         630 :                          OGRPGEscapeColumnName(oField.GetNameRef()).c_str(),
    2442         630 :                          osFieldType.c_str());
    2443         315 :         osCommand += osConstraints;
    2444             : 
    2445         315 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    2446         315 :         if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    2447             :         {
    2448           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    2449             :                      PQerrorMessage(hPGConn));
    2450             : 
    2451           0 :             OGRPGClearResult(hResult);
    2452             : 
    2453           0 :             return OGRERR_FAILURE;
    2454             :         }
    2455             : 
    2456         315 :         OGRPGClearResult(hResult);
    2457             : 
    2458         315 :         if (!osCommentON.empty())
    2459             :         {
    2460           3 :             hResult = OGRPG_PQexec(hPGConn, osCommentON.c_str());
    2461           3 :             OGRPGClearResult(hResult);
    2462             :         }
    2463             :     }
    2464             : 
    2465         606 :     whileUnsealing(poFeatureDefn)->AddFieldDefn(&oField);
    2466             : 
    2467         606 :     if (pszFIDColumn != nullptr && EQUAL(oField.GetNameRef(), pszFIDColumn))
    2468             :     {
    2469           1 :         iFIDAsRegularColumnIndex = poFeatureDefn->GetFieldCount() - 1;
    2470             :     }
    2471             : 
    2472         606 :     return OGRERR_NONE;
    2473             : }
    2474             : 
    2475             : /************************************************************************/
    2476             : /*                        RunAddGeometryColumn()                        */
    2477             : /************************************************************************/
    2478             : 
    2479             : OGRErr
    2480           8 : OGRPGTableLayer::RunAddGeometryColumn(const OGRPGGeomFieldDefn *poGeomField)
    2481             : {
    2482           8 :     PGconn *hPGConn = poDS->GetPGConn();
    2483             : 
    2484           8 :     const char *pszGeometryType = OGRToOGCGeomType(poGeomField->GetType());
    2485           8 :     const char *suffix = "";
    2486           8 :     int dim = 2;
    2487           8 :     if ((poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_3D) &&
    2488           4 :         (poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
    2489           2 :         dim = 4;
    2490           6 :     else if ((poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
    2491             :     {
    2492           1 :         if (!(wkbFlatten(poGeomField->GetType()) == wkbUnknown))
    2493           1 :             suffix = "M";
    2494           1 :         dim = 3;
    2495             :     }
    2496           5 :     else if (poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_3D)
    2497           2 :         dim = 3;
    2498             : 
    2499          16 :     CPLString osCommand;
    2500             :     osCommand.Printf(
    2501             :         "SELECT AddGeometryColumn(%s,%s,%s,%d,'%s%s',%d)",
    2502          16 :         OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
    2503          16 :         OGRPGEscapeString(hPGConn, pszTableName).c_str(),
    2504          16 :         OGRPGEscapeString(hPGConn, poGeomField->GetNameRef()).c_str(),
    2505          32 :         poGeomField->nSRSId, pszGeometryType, suffix, dim);
    2506             : 
    2507           8 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    2508             : 
    2509           8 :     if (!hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK)
    2510             :     {
    2511           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2512           0 :                  "AddGeometryColumn failed for layer %s.", GetName());
    2513             : 
    2514           0 :         OGRPGClearResult(hResult);
    2515             : 
    2516           0 :         return OGRERR_FAILURE;
    2517             :     }
    2518             : 
    2519           8 :     OGRPGClearResult(hResult);
    2520             : 
    2521           8 :     if (!poGeomField->IsNullable())
    2522             :     {
    2523             :         osCommand.Printf(
    2524             :             "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL", pszSqlTableName,
    2525           0 :             OGRPGEscapeColumnName(poGeomField->GetNameRef()).c_str());
    2526             : 
    2527           0 :         hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    2528           0 :         OGRPGClearResult(hResult);
    2529             :     }
    2530             : 
    2531           8 :     return OGRERR_NONE;
    2532             : }
    2533             : 
    2534             : /************************************************************************/
    2535             : /*                        RunCreateSpatialIndex()                       */
    2536             : /************************************************************************/
    2537             : 
    2538             : OGRErr
    2539         130 : OGRPGTableLayer::RunCreateSpatialIndex(const OGRPGGeomFieldDefn *poGeomField,
    2540             :                                        int nIdx)
    2541             : {
    2542         130 :     PGconn *hPGConn = poDS->GetPGConn();
    2543         260 :     CPLString osCommand;
    2544             : 
    2545             :     const std::string osIndexName(OGRPGCommonGenerateSpatialIndexName(
    2546         260 :         pszTableName, poGeomField->GetNameRef(), nIdx));
    2547             : 
    2548             :     osCommand.Printf("CREATE INDEX %s ON %s USING %s (%s)",
    2549         260 :                      OGRPGEscapeColumnName(osIndexName.c_str()).c_str(),
    2550             :                      pszSqlTableName, osSpatialIndexType.c_str(),
    2551         390 :                      OGRPGEscapeColumnName(poGeomField->GetNameRef()).c_str());
    2552             : 
    2553         130 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    2554             : 
    2555         130 :     if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
    2556             :     {
    2557           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2558           0 :                  "CREATE INDEX failed for layer %s.", GetName());
    2559             : 
    2560           0 :         OGRPGClearResult(hResult);
    2561             : 
    2562           0 :         return OGRERR_FAILURE;
    2563             :     }
    2564             : 
    2565         130 :     OGRPGClearResult(hResult);
    2566             : 
    2567         130 :     return OGRERR_NONE;
    2568             : }
    2569             : 
    2570             : /************************************************************************/
    2571             : /*                           CreateGeomField()                          */
    2572             : /************************************************************************/
    2573             : 
    2574          18 : OGRErr OGRPGTableLayer::CreateGeomField(const OGRGeomFieldDefn *poGeomFieldIn,
    2575             :                                         CPL_UNUSED int bApproxOK)
    2576             : {
    2577          18 :     OGRwkbGeometryType eType = poGeomFieldIn->GetType();
    2578          18 :     if (eType == wkbNone)
    2579             :     {
    2580           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2581             :                  "Cannot create geometry field of type wkbNone");
    2582           0 :         return OGRERR_FAILURE;
    2583             :     }
    2584             : 
    2585             :     // Check if GEOMETRY_NAME layer creation option was set, but no initial
    2586             :     // column was created in ICreateLayer()
    2587          18 :     CPLString osGeomFieldName = (m_osFirstGeometryFieldName.size())
    2588           2 :                                     ? m_osFirstGeometryFieldName
    2589          36 :                                     : CPLString(poGeomFieldIn->GetNameRef());
    2590          18 :     m_osFirstGeometryFieldName = "";  // reset for potential next geom columns
    2591             : 
    2592             :     auto poGeomField =
    2593          36 :         std::make_unique<OGRPGGeomFieldDefn>(this, osGeomFieldName);
    2594          18 :     if (EQUAL(poGeomField->GetNameRef(), ""))
    2595             :     {
    2596           0 :         if (poFeatureDefn->GetGeomFieldCount() == 0)
    2597           0 :             poGeomField->SetName(EQUAL(m_osLCOGeomType.c_str(), "geography")
    2598             :                                      ? "the_geog"
    2599             :                                      : "wkb_geometry");
    2600             :         else
    2601           0 :             poGeomField->SetName(CPLSPrintf(
    2602           0 :                 "wkb_geometry%d", poFeatureDefn->GetGeomFieldCount() + 1));
    2603             :     }
    2604          18 :     const auto poSRSIn = poGeomFieldIn->GetSpatialRef();
    2605          18 :     if (poSRSIn)
    2606             :     {
    2607           4 :         auto l_poSRS = poSRSIn->Clone();
    2608           4 :         l_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2609           4 :         poGeomField->SetSpatialRef(l_poSRS);
    2610           4 :         l_poSRS->Release();
    2611             :     }
    2612             :     /* -------------------------------------------------------------------- */
    2613             :     /*      Do we want to "launder" the column names into Postgres          */
    2614             :     /*      friendly format?                                                */
    2615             :     /* -------------------------------------------------------------------- */
    2616          18 :     if (bLaunderColumnNames)
    2617             :     {
    2618          18 :         char *pszSafeName = OGRPGCommonLaunderName(poGeomField->GetNameRef(),
    2619          18 :                                                    "PG", m_bUTF8ToASCII);
    2620             : 
    2621          18 :         poGeomField->SetName(pszSafeName);
    2622          18 :         CPLFree(pszSafeName);
    2623             :     }
    2624             : 
    2625          18 :     const OGRSpatialReference *poSRS = poGeomField->GetSpatialRef();
    2626          18 :     int nSRSId = poDS->GetUndefinedSRID();
    2627          18 :     if (nForcedSRSId != UNDETERMINED_SRID)
    2628           0 :         nSRSId = nForcedSRSId;
    2629          18 :     else if (poSRS != nullptr)
    2630           4 :         nSRSId = poDS->FetchSRSId(poSRS);
    2631             : 
    2632          18 :     int GeometryTypeFlags = 0;
    2633          18 :     if (OGR_GT_HasZ(eType))
    2634           4 :         GeometryTypeFlags |= OGRGeometry::OGR_G_3D;
    2635          18 :     if (OGR_GT_HasM(eType))
    2636           2 :         GeometryTypeFlags |= OGRGeometry::OGR_G_MEASURED;
    2637          18 :     if (nForcedGeometryTypeFlags >= 0)
    2638             :     {
    2639           2 :         GeometryTypeFlags = nForcedGeometryTypeFlags;
    2640             :         eType =
    2641           2 :             OGR_GT_SetModifier(eType, GeometryTypeFlags & OGRGeometry::OGR_G_3D,
    2642             :                                GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED);
    2643             :     }
    2644          18 :     poGeomField->SetType(eType);
    2645          18 :     poGeomField->SetNullable(poGeomFieldIn->IsNullable());
    2646          18 :     poGeomField->nSRSId = nSRSId;
    2647          18 :     poGeomField->GeometryTypeFlags = GeometryTypeFlags;
    2648          36 :     poGeomField->ePostgisType = EQUAL(m_osLCOGeomType.c_str(), "geography")
    2649          18 :                                     ? GEOM_TYPE_GEOGRAPHY
    2650             :                                     : GEOM_TYPE_GEOMETRY;
    2651             : 
    2652             :     /* -------------------------------------------------------------------- */
    2653             :     /*      Create the new field.                                           */
    2654             :     /* -------------------------------------------------------------------- */
    2655          18 :     if (!bDeferredCreation)
    2656             :     {
    2657           8 :         poDS->EndCopy();
    2658             : 
    2659           8 :         if (RunAddGeometryColumn(poGeomField.get()) != OGRERR_NONE)
    2660             :         {
    2661           0 :             return OGRERR_FAILURE;
    2662             :         }
    2663             : 
    2664           8 :         if (bCreateSpatialIndexFlag)
    2665             :         {
    2666           8 :             if (RunCreateSpatialIndex(poGeomField.get(), 0) != OGRERR_NONE)
    2667             :             {
    2668           0 :                 return OGRERR_FAILURE;
    2669             :             }
    2670             :         }
    2671             :     }
    2672             : 
    2673          18 :     whileUnsealing(poFeatureDefn)->AddGeomFieldDefn(std::move(poGeomField));
    2674             : 
    2675          18 :     return OGRERR_NONE;
    2676             : }
    2677             : 
    2678             : /************************************************************************/
    2679             : /*                            DeleteField()                             */
    2680             : /************************************************************************/
    2681             : 
    2682           4 : OGRErr OGRPGTableLayer::DeleteField(int iField)
    2683             : {
    2684           4 :     PGconn *hPGConn = poDS->GetPGConn();
    2685           8 :     CPLString osCommand;
    2686             : 
    2687           4 :     GetLayerDefn()->GetFieldCount();
    2688             : 
    2689           4 :     if (!bUpdateAccess)
    2690             :     {
    2691           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    2692             :                  "DeleteField");
    2693           0 :         return OGRERR_FAILURE;
    2694             :     }
    2695             : 
    2696           4 :     if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
    2697             :     {
    2698           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    2699           0 :         return OGRERR_FAILURE;
    2700             :     }
    2701             : 
    2702           4 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    2703           0 :         return OGRERR_FAILURE;
    2704           4 :     poDS->EndCopy();
    2705             : 
    2706             :     osCommand.Printf(
    2707             :         "ALTER TABLE %s DROP COLUMN %s", pszSqlTableName,
    2708           8 :         OGRPGEscapeColumnName(poFeatureDefn->GetFieldDefn(iField)->GetNameRef())
    2709           4 :             .c_str());
    2710           4 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    2711           4 :     if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    2712             :     {
    2713           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    2714             :                  PQerrorMessage(hPGConn));
    2715             : 
    2716           0 :         OGRPGClearResult(hResult);
    2717             : 
    2718           0 :         return OGRERR_FAILURE;
    2719             :     }
    2720             : 
    2721           4 :     OGRPGClearResult(hResult);
    2722             : 
    2723           4 :     return whileUnsealing(poFeatureDefn)->DeleteFieldDefn(iField);
    2724             : }
    2725             : 
    2726             : /************************************************************************/
    2727             : /*                           AlterFieldDefn()                           */
    2728             : /************************************************************************/
    2729             : 
    2730          20 : OGRErr OGRPGTableLayer::AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
    2731             :                                        int nFlagsIn)
    2732             : {
    2733          20 :     PGconn *hPGConn = poDS->GetPGConn();
    2734          40 :     CPLString osCommand;
    2735             : 
    2736          20 :     GetLayerDefn()->GetFieldCount();
    2737             : 
    2738          20 :     if (!bUpdateAccess)
    2739             :     {
    2740           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    2741             :                  "AlterFieldDefn");
    2742           0 :         return OGRERR_FAILURE;
    2743             :     }
    2744             : 
    2745          20 :     if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
    2746             :     {
    2747           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    2748           0 :         return OGRERR_FAILURE;
    2749             :     }
    2750             : 
    2751          20 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    2752           0 :         return OGRERR_FAILURE;
    2753          20 :     poDS->EndCopy();
    2754             : 
    2755          20 :     OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    2756          40 :     auto oTemporaryUnsealer(poFieldDefn->GetTemporaryUnsealer());
    2757          40 :     OGRFieldDefn oField(poNewFieldDefn);
    2758             : 
    2759          20 :     poDS->SoftStartTransaction();
    2760             : 
    2761          20 :     if (!(nFlagsIn & ALTER_TYPE_FLAG))
    2762             :     {
    2763          14 :         oField.SetSubType(OFSTNone);
    2764          14 :         oField.SetType(poFieldDefn->GetType());
    2765          14 :         oField.SetSubType(poFieldDefn->GetSubType());
    2766             :     }
    2767             : 
    2768          20 :     if (!(nFlagsIn & ALTER_WIDTH_PRECISION_FLAG))
    2769             :     {
    2770          14 :         oField.SetWidth(poFieldDefn->GetWidth());
    2771          14 :         oField.SetPrecision(poFieldDefn->GetPrecision());
    2772             :     }
    2773             : 
    2774          20 :     if ((nFlagsIn & ALTER_TYPE_FLAG) || (nFlagsIn & ALTER_WIDTH_PRECISION_FLAG))
    2775             :     {
    2776             :         CPLString osFieldType = OGRPGCommonLayerGetType(
    2777           6 :             oField, CPL_TO_BOOL(bPreservePrecision), true);
    2778           6 :         if (osFieldType.empty())
    2779             :         {
    2780           0 :             poDS->SoftRollbackTransaction();
    2781             : 
    2782           0 :             return OGRERR_FAILURE;
    2783             :         }
    2784             : 
    2785             :         osCommand.Printf(
    2786             :             "ALTER TABLE %s ALTER COLUMN %s TYPE %s", pszSqlTableName,
    2787          12 :             OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
    2788          12 :             osFieldType.c_str());
    2789             : 
    2790           6 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    2791           6 :         if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    2792             :         {
    2793           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    2794             :                      PQerrorMessage(hPGConn));
    2795             : 
    2796           0 :             OGRPGClearResult(hResult);
    2797             : 
    2798           0 :             poDS->SoftRollbackTransaction();
    2799             : 
    2800           0 :             return OGRERR_FAILURE;
    2801             :         }
    2802           6 :         OGRPGClearResult(hResult);
    2803             :     }
    2804             : 
    2805          26 :     if ((nFlagsIn & ALTER_NULLABLE_FLAG) &&
    2806           6 :         poFieldDefn->IsNullable() != poNewFieldDefn->IsNullable())
    2807             :     {
    2808           2 :         oField.SetNullable(poNewFieldDefn->IsNullable());
    2809             : 
    2810           2 :         if (poNewFieldDefn->IsNullable())
    2811             :             osCommand.Printf(
    2812             :                 "ALTER TABLE %s ALTER COLUMN %s DROP NOT NULL", pszSqlTableName,
    2813           1 :                 OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
    2814             :         else
    2815             :             osCommand.Printf(
    2816             :                 "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL", pszSqlTableName,
    2817           1 :                 OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
    2818             : 
    2819           2 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    2820           2 :         if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    2821             :         {
    2822           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    2823             :                      PQerrorMessage(hPGConn));
    2824             : 
    2825           0 :             OGRPGClearResult(hResult);
    2826             : 
    2827           0 :             poDS->SoftRollbackTransaction();
    2828             : 
    2829           0 :             return OGRERR_FAILURE;
    2830             :         }
    2831           2 :         OGRPGClearResult(hResult);
    2832             :     }
    2833             : 
    2834             :     // Only supports adding a unique constraint
    2835          30 :     if ((nFlagsIn & ALTER_UNIQUE_FLAG) && !poFieldDefn->IsUnique() &&
    2836          10 :         poNewFieldDefn->IsUnique())
    2837             :     {
    2838           2 :         oField.SetUnique(poNewFieldDefn->IsUnique());
    2839             : 
    2840             :         osCommand.Printf(
    2841             :             "ALTER TABLE %s ADD UNIQUE (%s)", pszSqlTableName,
    2842           2 :             OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
    2843             : 
    2844           2 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    2845           2 :         if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    2846             :         {
    2847           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    2848             :                      PQerrorMessage(hPGConn));
    2849             : 
    2850           0 :             OGRPGClearResult(hResult);
    2851             : 
    2852           0 :             poDS->SoftRollbackTransaction();
    2853             : 
    2854           0 :             return OGRERR_FAILURE;
    2855             :         }
    2856           2 :         OGRPGClearResult(hResult);
    2857             :     }
    2858          22 :     else if ((nFlagsIn & ALTER_UNIQUE_FLAG) && poFieldDefn->IsUnique() &&
    2859           4 :              !poNewFieldDefn->IsUnique())
    2860             :     {
    2861           2 :         oField.SetUnique(TRUE);
    2862           2 :         CPLError(CE_Warning, CPLE_NotSupported,
    2863             :                  "Dropping a UNIQUE constraint is not supported currently");
    2864             :     }
    2865             : 
    2866          28 :     if ((nFlagsIn & ALTER_DEFAULT_FLAG) &&
    2867           8 :         ((poFieldDefn->GetDefault() == nullptr &&
    2868           6 :           poNewFieldDefn->GetDefault() != nullptr) ||
    2869           8 :          (poFieldDefn->GetDefault() != nullptr &&
    2870           2 :           poNewFieldDefn->GetDefault() == nullptr) ||
    2871           7 :          (poFieldDefn->GetDefault() != nullptr &&
    2872           1 :           poNewFieldDefn->GetDefault() != nullptr &&
    2873           1 :           strcmp(poFieldDefn->GetDefault(), poNewFieldDefn->GetDefault()) !=
    2874             :               0)))
    2875             :     {
    2876           2 :         oField.SetDefault(poNewFieldDefn->GetDefault());
    2877             : 
    2878           2 :         if (poNewFieldDefn->GetDefault() == nullptr)
    2879             :             osCommand.Printf(
    2880             :                 "ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT", pszSqlTableName,
    2881           1 :                 OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
    2882             :         else
    2883             :             osCommand.Printf(
    2884             :                 "ALTER TABLE %s ALTER COLUMN %s SET DEFAULT %s",
    2885             :                 pszSqlTableName,
    2886           2 :                 OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
    2887           3 :                 OGRPGCommonLayerGetPGDefault(poNewFieldDefn).c_str());
    2888             : 
    2889           2 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    2890           2 :         if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    2891             :         {
    2892           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    2893             :                      PQerrorMessage(hPGConn));
    2894             : 
    2895           0 :             OGRPGClearResult(hResult);
    2896             : 
    2897           0 :             poDS->SoftRollbackTransaction();
    2898             : 
    2899           0 :             return OGRERR_FAILURE;
    2900             :         }
    2901           2 :         OGRPGClearResult(hResult);
    2902             :     }
    2903             : 
    2904          30 :     if ((nFlagsIn & ALTER_COMMENT_FLAG) &&
    2905          10 :         poFieldDefn->GetComment() != poNewFieldDefn->GetComment())
    2906             :     {
    2907           6 :         oField.SetComment(poNewFieldDefn->GetComment());
    2908             : 
    2909           6 :         if (!poNewFieldDefn->GetComment().empty())
    2910             :         {
    2911             :             osCommand.Printf(
    2912             :                 "COMMENT ON COLUMN %s.%s IS %s", pszSqlTableName,
    2913           8 :                 OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
    2914           8 :                 OGRPGEscapeString(hPGConn, poNewFieldDefn->GetComment().c_str())
    2915           8 :                     .c_str());
    2916             :         }
    2917             :         else
    2918             :         {
    2919             :             osCommand.Printf(
    2920             :                 "COMMENT ON COLUMN %s.%s IS NULL", pszSqlTableName,
    2921           2 :                 OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
    2922             :         }
    2923             : 
    2924           6 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    2925           6 :         if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    2926             :         {
    2927           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    2928             :                      PQerrorMessage(hPGConn));
    2929             : 
    2930           0 :             OGRPGClearResult(hResult);
    2931             : 
    2932           0 :             poDS->SoftRollbackTransaction();
    2933             : 
    2934           0 :             return OGRERR_FAILURE;
    2935             :         }
    2936           6 :         OGRPGClearResult(hResult);
    2937             :     }
    2938             : 
    2939          20 :     if ((nFlagsIn & ALTER_NAME_FLAG))
    2940             :     {
    2941           6 :         if (bLaunderColumnNames)
    2942             :         {
    2943           6 :             char *pszSafeName = OGRPGCommonLaunderName(oField.GetNameRef(),
    2944           6 :                                                        "PG", m_bUTF8ToASCII);
    2945           6 :             oField.SetName(pszSafeName);
    2946           6 :             CPLFree(pszSafeName);
    2947             :         }
    2948             : 
    2949           6 :         if (EQUAL(oField.GetNameRef(), "oid"))
    2950             :         {
    2951           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2952             :                      "Renaming field 'oid' to 'oid_' to avoid conflict with "
    2953             :                      "internal oid field.");
    2954           0 :             oField.SetName("oid_");
    2955             :         }
    2956             : 
    2957           6 :         if (strcmp(poFieldDefn->GetNameRef(), oField.GetNameRef()) != 0)
    2958             :         {
    2959             :             osCommand.Printf(
    2960             :                 "ALTER TABLE %s RENAME COLUMN %s TO %s", pszSqlTableName,
    2961          12 :                 OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
    2962          18 :                 OGRPGEscapeColumnName(oField.GetNameRef()).c_str());
    2963           6 :             PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    2964           6 :             if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    2965             :             {
    2966           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s",
    2967             :                          osCommand.c_str(), PQerrorMessage(hPGConn));
    2968             : 
    2969           0 :                 OGRPGClearResult(hResult);
    2970             : 
    2971           0 :                 poDS->SoftRollbackTransaction();
    2972             : 
    2973           0 :                 return OGRERR_FAILURE;
    2974             :             }
    2975           6 :             OGRPGClearResult(hResult);
    2976             :         }
    2977             :     }
    2978             : 
    2979          20 :     poDS->SoftCommitTransaction();
    2980             : 
    2981          20 :     if (nFlagsIn & ALTER_NAME_FLAG)
    2982           6 :         poFieldDefn->SetName(oField.GetNameRef());
    2983          20 :     if (nFlagsIn & ALTER_TYPE_FLAG)
    2984             :     {
    2985           6 :         poFieldDefn->SetSubType(OFSTNone);
    2986           6 :         poFieldDefn->SetType(oField.GetType());
    2987           6 :         poFieldDefn->SetSubType(oField.GetSubType());
    2988             :     }
    2989          20 :     if (nFlagsIn & ALTER_WIDTH_PRECISION_FLAG)
    2990             :     {
    2991           6 :         poFieldDefn->SetWidth(oField.GetWidth());
    2992           6 :         poFieldDefn->SetPrecision(oField.GetPrecision());
    2993             :     }
    2994          20 :     if (nFlagsIn & ALTER_NULLABLE_FLAG)
    2995           6 :         poFieldDefn->SetNullable(oField.IsNullable());
    2996          20 :     if (nFlagsIn & ALTER_DEFAULT_FLAG)
    2997           8 :         poFieldDefn->SetDefault(oField.GetDefault());
    2998          20 :     if (nFlagsIn & ALTER_UNIQUE_FLAG)
    2999          14 :         poFieldDefn->SetUnique(oField.IsUnique());
    3000          20 :     if (nFlagsIn & ALTER_COMMENT_FLAG)
    3001          10 :         poFieldDefn->SetComment(oField.GetComment());
    3002             : 
    3003          20 :     return OGRERR_NONE;
    3004             : }
    3005             : 
    3006             : /************************************************************************/
    3007             : /*                         AlterGeomFieldDefn()                         */
    3008             : /************************************************************************/
    3009             : 
    3010             : OGRErr
    3011           6 : OGRPGTableLayer::AlterGeomFieldDefn(int iGeomFieldToAlter,
    3012             :                                     const OGRGeomFieldDefn *poNewGeomFieldDefn,
    3013             :                                     int nFlagsIn)
    3014             : {
    3015           6 :     PGconn *hPGConn = poDS->GetPGConn();
    3016          12 :     CPLString osCommand;
    3017             : 
    3018           6 :     if (!bUpdateAccess)
    3019             :     {
    3020           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    3021             :                  "AlterGeomFieldDefn");
    3022           0 :         return OGRERR_FAILURE;
    3023             :     }
    3024             : 
    3025          11 :     if (iGeomFieldToAlter < 0 ||
    3026           5 :         iGeomFieldToAlter >= GetLayerDefn()->GetGeomFieldCount())
    3027             :     {
    3028           1 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    3029           1 :         return OGRERR_FAILURE;
    3030             :     }
    3031             : 
    3032           5 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3033           0 :         return OGRERR_FAILURE;
    3034           5 :     poDS->EndCopy();
    3035             : 
    3036           5 :     auto poGeomFieldDefn = cpl::down_cast<OGRPGGeomFieldDefn *>(
    3037           5 :         poFeatureDefn->GetGeomFieldDefn(iGeomFieldToAlter));
    3038          10 :     auto oTemporaryUnsealer(poGeomFieldDefn->GetTemporaryUnsealer());
    3039             : 
    3040           5 :     if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG)
    3041             :     {
    3042           5 :         const auto poNewSRSRef = poNewGeomFieldDefn->GetSpatialRef();
    3043           5 :         if (poNewSRSRef && poNewSRSRef->GetCoordinateEpoch() > 0)
    3044             :         {
    3045           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    3046             :                      "Setting a coordinate epoch is not supported for "
    3047             :                      "PostGIS");
    3048           1 :             return OGRERR_FAILURE;
    3049             :         }
    3050             :     }
    3051             : 
    3052           8 :     const OGRGeomFieldDefn oGeomField(poNewGeomFieldDefn);
    3053           4 :     poDS->SoftStartTransaction();
    3054             : 
    3055           4 :     int nGeomTypeFlags = poGeomFieldDefn->GeometryTypeFlags;
    3056             : 
    3057           8 :     if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_TYPE_FLAG) &&
    3058           4 :         poGeomFieldDefn->GetType() != poNewGeomFieldDefn->GetType())
    3059             :     {
    3060             :         const char *pszGeometryType =
    3061           2 :             OGRToOGCGeomType(poNewGeomFieldDefn->GetType());
    3062           2 :         std::string osType;
    3063           2 :         if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY)
    3064           2 :             osType += "geometry(";
    3065             :         else
    3066           0 :             osType += "geography(";
    3067           2 :         osType += pszGeometryType;
    3068           2 :         nGeomTypeFlags = 0;
    3069           2 :         if (OGR_GT_HasZ(poNewGeomFieldDefn->GetType()))
    3070           0 :             nGeomTypeFlags |= OGRGeometry::OGR_G_3D;
    3071           2 :         if (OGR_GT_HasM(poNewGeomFieldDefn->GetType()))
    3072           0 :             nGeomTypeFlags |= OGRGeometry::OGR_G_MEASURED;
    3073           2 :         if (nGeomTypeFlags & OGRGeometry::OGR_G_3D)
    3074           0 :             osType += "Z";
    3075           2 :         else if (nGeomTypeFlags & OGRGeometry::OGR_G_MEASURED)
    3076           0 :             osType += "M";
    3077           2 :         if (poGeomFieldDefn->nSRSId > 0)
    3078           2 :             osType += CPLSPrintf(",%d", poGeomFieldDefn->nSRSId);
    3079           2 :         osType += ")";
    3080             : 
    3081             :         osCommand.Printf(
    3082             :             "ALTER TABLE %s ALTER COLUMN %s TYPE %s", pszSqlTableName,
    3083           4 :             OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(),
    3084           4 :             osType.c_str());
    3085             : 
    3086           2 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    3087           2 :         if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    3088             :         {
    3089           1 :             CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    3090             :                      PQerrorMessage(hPGConn));
    3091             : 
    3092           1 :             OGRPGClearResult(hResult);
    3093             : 
    3094           1 :             poDS->SoftRollbackTransaction();
    3095             : 
    3096           1 :             return OGRERR_FAILURE;
    3097             :         }
    3098           1 :         OGRPGClearResult(hResult);
    3099             :     }
    3100             : 
    3101           3 :     const auto poOldSRS = poGeomFieldDefn->GetSpatialRef();
    3102           3 :     int nSRID = poGeomFieldDefn->nSRSId;
    3103             : 
    3104           3 :     if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG))
    3105             :     {
    3106           3 :         const auto poNewSRS = poNewGeomFieldDefn->GetSpatialRef();
    3107           3 :         const char *const apszOptions[] = {
    3108             :             "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES", nullptr};
    3109           3 :         if ((poOldSRS == nullptr && poNewSRS != nullptr) ||
    3110           7 :             (poOldSRS != nullptr && poNewSRS == nullptr) ||
    3111           1 :             (poOldSRS != nullptr && poNewSRS != nullptr &&
    3112           1 :              !poOldSRS->IsSame(poNewSRS, apszOptions)))
    3113             :         {
    3114           3 :             if (poNewSRS)
    3115           2 :                 nSRID = poDS->FetchSRSId(poNewSRS);
    3116             :             else
    3117           1 :                 nSRID = 0;
    3118             : 
    3119             :             osCommand.Printf(
    3120             :                 "SELECT UpdateGeometrySRID(%s,%s,%s,%d)",
    3121           6 :                 OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
    3122           6 :                 OGRPGEscapeString(hPGConn, pszTableName).c_str(),
    3123           6 :                 OGRPGEscapeString(hPGConn, poGeomFieldDefn->GetNameRef())
    3124             :                     .c_str(),
    3125           9 :                 nSRID);
    3126             : 
    3127           3 :             PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    3128           3 :             if (PQresultStatus(hResult) != PGRES_TUPLES_OK)
    3129             :             {
    3130           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s",
    3131             :                          osCommand.c_str(), PQerrorMessage(hPGConn));
    3132             : 
    3133           0 :                 OGRPGClearResult(hResult);
    3134             : 
    3135           0 :                 poDS->SoftRollbackTransaction();
    3136             : 
    3137           0 :                 return OGRERR_FAILURE;
    3138             :             }
    3139           3 :             OGRPGClearResult(hResult);
    3140             :         }
    3141             :     }
    3142             : 
    3143           6 :     if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_NULLABLE_FLAG) &&
    3144           3 :         poGeomFieldDefn->IsNullable() != poNewGeomFieldDefn->IsNullable())
    3145             :     {
    3146           2 :         if (poNewGeomFieldDefn->IsNullable())
    3147             :             osCommand.Printf(
    3148             :                 "ALTER TABLE %s ALTER COLUMN %s DROP NOT NULL", pszSqlTableName,
    3149           1 :                 OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str());
    3150             :         else
    3151             :             osCommand.Printf(
    3152             :                 "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL", pszSqlTableName,
    3153           1 :                 OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str());
    3154             : 
    3155           2 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    3156           2 :         if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    3157             :         {
    3158           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    3159             :                      PQerrorMessage(hPGConn));
    3160             : 
    3161           0 :             OGRPGClearResult(hResult);
    3162             : 
    3163           0 :             poDS->SoftRollbackTransaction();
    3164             : 
    3165           0 :             return OGRERR_FAILURE;
    3166             :         }
    3167           2 :         OGRPGClearResult(hResult);
    3168             :     }
    3169             : 
    3170           6 :     if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_NAME_FLAG) &&
    3171           3 :         strcmp(poGeomFieldDefn->GetNameRef(),
    3172             :                poNewGeomFieldDefn->GetNameRef()) != 0)
    3173             :     {
    3174             :         osCommand.Printf(
    3175             :             "ALTER TABLE %s RENAME COLUMN %s TO %s", pszSqlTableName,
    3176           2 :             OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(),
    3177           3 :             OGRPGEscapeColumnName(oGeomField.GetNameRef()).c_str());
    3178           1 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    3179           1 :         if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    3180             :         {
    3181           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    3182             :                      PQerrorMessage(hPGConn));
    3183             : 
    3184           0 :             OGRPGClearResult(hResult);
    3185             : 
    3186           0 :             poDS->SoftRollbackTransaction();
    3187             : 
    3188           0 :             return OGRERR_FAILURE;
    3189             :         }
    3190           1 :         OGRPGClearResult(hResult);
    3191             :     }
    3192             : 
    3193           3 :     poDS->SoftCommitTransaction();
    3194             : 
    3195           3 :     if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NAME_FLAG)
    3196           3 :         poGeomFieldDefn->SetName(oGeomField.GetNameRef());
    3197           3 :     if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_TYPE_FLAG)
    3198             :     {
    3199           3 :         poGeomFieldDefn->GeometryTypeFlags = nGeomTypeFlags;
    3200           3 :         poGeomFieldDefn->SetType(oGeomField.GetType());
    3201             :     }
    3202           3 :     if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NULLABLE_FLAG)
    3203           3 :         poGeomFieldDefn->SetNullable(oGeomField.IsNullable());
    3204           3 :     if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG)
    3205             :     {
    3206           3 :         const auto poSRSRef = oGeomField.GetSpatialRef();
    3207           3 :         if (poSRSRef)
    3208             :         {
    3209           2 :             auto poSRSNew = poSRSRef->Clone();
    3210           2 :             poGeomFieldDefn->SetSpatialRef(poSRSNew);
    3211           2 :             poSRSNew->Release();
    3212             :         }
    3213             :         else
    3214             :         {
    3215           1 :             poGeomFieldDefn->SetSpatialRef(nullptr);
    3216             :         }
    3217           3 :         poGeomFieldDefn->nSRSId = nSRID;
    3218             :     }
    3219             : 
    3220           3 :     return OGRERR_NONE;
    3221             : }
    3222             : 
    3223             : /************************************************************************/
    3224             : /*                             GetFeature()                             */
    3225             : /************************************************************************/
    3226             : 
    3227         146 : OGRFeature *OGRPGTableLayer::GetFeature(GIntBig nFeatureId)
    3228             : 
    3229             : {
    3230         146 :     GetLayerDefn()->GetFieldCount();
    3231             : 
    3232         146 :     if (pszFIDColumn == nullptr)
    3233           3 :         return OGRLayer::GetFeature(nFeatureId);
    3234             : 
    3235             :     /* -------------------------------------------------------------------- */
    3236             :     /*      Issue query for a single record.                                */
    3237             :     /* -------------------------------------------------------------------- */
    3238         143 :     OGRFeature *poFeature = nullptr;
    3239         143 :     PGconn *hPGConn = poDS->GetPGConn();
    3240         286 :     CPLString osFieldList = BuildFields();
    3241         143 :     CPLString osCommand;
    3242             : 
    3243         143 :     poDS->EndCopy();
    3244         143 :     poDS->SoftStartTransaction();
    3245             : 
    3246             :     osCommand.Printf("DECLARE getfeaturecursor %s for "
    3247             :                      "SELECT %s FROM %s WHERE %s = " CPL_FRMT_GIB,
    3248         143 :                      (poDS->bUseBinaryCursor) ? "BINARY CURSOR" : "CURSOR",
    3249             :                      osFieldList.c_str(), pszSqlTableName,
    3250         143 :                      OGRPGEscapeColumnName(pszFIDColumn).c_str(), nFeatureId);
    3251             : 
    3252         143 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    3253             : 
    3254         143 :     if (hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK)
    3255             :     {
    3256         143 :         OGRPGClearResult(hResult);
    3257             : 
    3258         143 :         hResult = OGRPG_PQexec(hPGConn, "FETCH ALL in getfeaturecursor");
    3259             : 
    3260         143 :         if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK)
    3261             :         {
    3262         143 :             int nRows = PQntuples(hResult);
    3263         143 :             if (nRows > 0)
    3264             :             {
    3265         121 :                 int *panTempMapFieldNameToIndex = nullptr;
    3266         121 :                 int *panTempMapFieldNameToGeomIndex = nullptr;
    3267         121 :                 CreateMapFromFieldNameToIndex(hResult, poFeatureDefn,
    3268             :                                               panTempMapFieldNameToIndex,
    3269             :                                               panTempMapFieldNameToGeomIndex);
    3270         121 :                 poFeature = RecordToFeature(hResult, panTempMapFieldNameToIndex,
    3271             :                                             panTempMapFieldNameToGeomIndex, 0);
    3272         121 :                 CPLFree(panTempMapFieldNameToIndex);
    3273         121 :                 CPLFree(panTempMapFieldNameToGeomIndex);
    3274         121 :                 if (poFeature && iFIDAsRegularColumnIndex >= 0)
    3275             :                 {
    3276           1 :                     poFeature->SetField(iFIDAsRegularColumnIndex,
    3277             :                                         poFeature->GetFID());
    3278             :                 }
    3279             : 
    3280         121 :                 if (nRows > 1)
    3281             :                 {
    3282           0 :                     CPLError(
    3283             :                         CE_Warning, CPLE_AppDefined,
    3284             :                         "%d rows in response to the WHERE %s = " CPL_FRMT_GIB
    3285             :                         " clause !",
    3286             :                         nRows, pszFIDColumn, nFeatureId);
    3287             :                 }
    3288             :             }
    3289             :             else
    3290             :             {
    3291          22 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3292             :                          "Attempt to read feature with unknown feature id "
    3293             :                          "(" CPL_FRMT_GIB ").",
    3294             :                          nFeatureId);
    3295             :             }
    3296             :         }
    3297             :     }
    3298           0 :     else if (hResult && PQresultStatus(hResult) == PGRES_FATAL_ERROR)
    3299             :     {
    3300           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
    3301             :                  PQresultErrorMessage(hResult));
    3302             :     }
    3303             : 
    3304             :     /* -------------------------------------------------------------------- */
    3305             :     /*      Cleanup                                                         */
    3306             :     /* -------------------------------------------------------------------- */
    3307         143 :     OGRPGClearResult(hResult);
    3308             : 
    3309         143 :     hResult = OGRPG_PQexec(hPGConn, "CLOSE getfeaturecursor");
    3310         143 :     OGRPGClearResult(hResult);
    3311             : 
    3312         143 :     poDS->SoftCommitTransaction();
    3313             : 
    3314         143 :     return poFeature;
    3315             : }
    3316             : 
    3317             : /************************************************************************/
    3318             : /*                          GetFeatureCount()                           */
    3319             : /************************************************************************/
    3320             : 
    3321         152 : GIntBig OGRPGTableLayer::GetFeatureCount(int bForce)
    3322             : 
    3323             : {
    3324         152 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3325           0 :         return 0;
    3326         152 :     poDS->EndCopy();
    3327             : 
    3328         152 :     if (TestCapability(OLCFastFeatureCount) == FALSE)
    3329          11 :         return OGRPGLayer::GetFeatureCount(bForce);
    3330             : 
    3331             :     /* -------------------------------------------------------------------- */
    3332             :     /*      In theory it might be wise to cache this result, but it         */
    3333             :     /*      won't be trivial to work out the lifetime of the value.         */
    3334             :     /*      After all someone else could be adding records from another     */
    3335             :     /*      application when working against a database.                    */
    3336             :     /* -------------------------------------------------------------------- */
    3337         141 :     PGconn *hPGConn = poDS->GetPGConn();
    3338         141 :     CPLString osCommand;
    3339         141 :     GIntBig nCount = 0;
    3340             : 
    3341             :     osCommand.Printf("SELECT count(*) FROM %s %s", pszSqlTableName,
    3342         141 :                      osWHERE.c_str());
    3343             : 
    3344         141 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    3345         141 :     if (hResult != nullptr && PQresultStatus(hResult) == PGRES_TUPLES_OK)
    3346         141 :         nCount = CPLAtoGIntBig(PQgetvalue(hResult, 0, 0));
    3347             :     else
    3348           0 :         CPLDebug("PG", "%s; failed.", osCommand.c_str());
    3349         141 :     OGRPGClearResult(hResult);
    3350             : 
    3351         141 :     return nCount;
    3352             : }
    3353             : 
    3354             : /************************************************************************/
    3355             : /*                             ResolveSRID()                            */
    3356             : /************************************************************************/
    3357             : 
    3358          83 : void OGRPGTableLayer::ResolveSRID(const OGRPGGeomFieldDefn *poGFldDefn)
    3359             : 
    3360             : {
    3361          83 :     PGconn *hPGConn = poDS->GetPGConn();
    3362          83 :     CPLString osCommand;
    3363             : 
    3364          83 :     int nSRSId = poDS->GetUndefinedSRID();
    3365          83 :     if (!poDS->m_bHasGeometryColumns)
    3366             :     {
    3367           0 :         poGFldDefn->nSRSId = nSRSId;
    3368           0 :         return;
    3369             :     }
    3370             : 
    3371             :     osCommand.Printf(
    3372             :         "SELECT srid FROM geometry_columns "
    3373             :         "WHERE f_table_name = %s AND "
    3374             :         "f_geometry_column = %s",
    3375         166 :         OGRPGEscapeString(hPGConn, pszTableName).c_str(),
    3376         249 :         OGRPGEscapeString(hPGConn, poGFldDefn->GetNameRef()).c_str());
    3377             : 
    3378             :     osCommand +=
    3379          83 :         CPLString().Printf(" AND f_table_schema = %s",
    3380          83 :                            OGRPGEscapeString(hPGConn, pszSchemaName).c_str());
    3381             : 
    3382          83 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    3383             : 
    3384         166 :     if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
    3385          83 :         PQntuples(hResult) == 1)
    3386             :     {
    3387          39 :         nSRSId = atoi(PQgetvalue(hResult, 0, 0));
    3388             :     }
    3389             : 
    3390          83 :     OGRPGClearResult(hResult);
    3391             : 
    3392             :     /* With PostGIS 2.0, SRID = 0 can also mean that there's no constraint */
    3393             :     /* so we need to fetch from values */
    3394             :     /* We assume that all geometry of this column have identical SRID */
    3395          83 :     if (nSRSId <= 0 && poGFldDefn->ePostgisType == GEOM_TYPE_GEOMETRY &&
    3396          39 :         poDS->sPostGISVersion.nMajor >= 0)
    3397             :     {
    3398          72 :         CPLString osGetSRID;
    3399          36 :         osGetSRID += "SELECT ST_SRID(";
    3400          36 :         osGetSRID += OGRPGEscapeColumnName(poGFldDefn->GetNameRef());
    3401          36 :         osGetSRID += ") FROM ";
    3402          36 :         osGetSRID += pszSqlTableName;
    3403          36 :         osGetSRID += " WHERE (";
    3404          36 :         osGetSRID += OGRPGEscapeColumnName(poGFldDefn->GetNameRef());
    3405          36 :         osGetSRID += " IS NOT NULL) LIMIT 1";
    3406             : 
    3407          36 :         hResult = OGRPG_PQexec(poDS->GetPGConn(), osGetSRID);
    3408          72 :         if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
    3409          36 :             PQntuples(hResult) == 1)
    3410             :         {
    3411          34 :             nSRSId = atoi(PQgetvalue(hResult, 0, 0));
    3412             :         }
    3413             : 
    3414          36 :         OGRPGClearResult(hResult);
    3415             :     }
    3416             : 
    3417          83 :     poGFldDefn->nSRSId = nSRSId;
    3418             : }
    3419             : 
    3420             : /************************************************************************/
    3421             : /*                             StartCopy()                              */
    3422             : /************************************************************************/
    3423             : 
    3424         185 : OGRErr OGRPGTableLayer::StartCopy()
    3425             : 
    3426             : {
    3427             :     /*CPLDebug("PG", "OGRPGDataSource(%p)::StartCopy(%p)", poDS, this);*/
    3428             : 
    3429         185 :     CPLString osFields = BuildCopyFields();
    3430             : 
    3431         185 :     size_t size = osFields.size() + strlen(pszSqlTableName) + 100;
    3432         185 :     char *pszCommand = static_cast<char *>(CPLMalloc(size));
    3433             : 
    3434         185 :     snprintf(pszCommand, size, "COPY %s (%s) FROM STDIN;", pszSqlTableName,
    3435             :              osFields.c_str());
    3436             : 
    3437         185 :     PGconn *hPGConn = poDS->GetPGConn();
    3438         185 :     PGresult *hResult = OGRPG_PQexec(hPGConn, pszCommand);
    3439             : 
    3440         185 :     if (!hResult || (PQresultStatus(hResult) != PGRES_COPY_IN))
    3441             :     {
    3442           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
    3443             :     }
    3444             :     else
    3445         185 :         bCopyActive = TRUE;
    3446             : 
    3447         185 :     OGRPGClearResult(hResult);
    3448         185 :     CPLFree(pszCommand);
    3449             : 
    3450         370 :     return OGRERR_NONE;
    3451             : }
    3452             : 
    3453             : /************************************************************************/
    3454             : /*                              EndCopy()                               */
    3455             : /************************************************************************/
    3456             : 
    3457         185 : OGRErr OGRPGTableLayer::EndCopy()
    3458             : 
    3459             : {
    3460         185 :     if (!bCopyActive)
    3461           0 :         return OGRERR_NONE;
    3462             :     /*CPLDebug("PG", "OGRPGDataSource(%p)::EndCopy(%p)", poDS, this);*/
    3463             : 
    3464             :     /* This method is called from the datasource when
    3465             :        a COPY operation is ended */
    3466         185 :     OGRErr result = OGRERR_NONE;
    3467             : 
    3468         185 :     PGconn *hPGConn = poDS->GetPGConn();
    3469         185 :     CPLDebug("PG", "PQputCopyEnd()");
    3470             : 
    3471         185 :     bCopyActive = FALSE;
    3472             : 
    3473         185 :     int copyResult = PQputCopyEnd(hPGConn, nullptr);
    3474             : 
    3475         185 :     switch (copyResult)
    3476             :     {
    3477           0 :         case 0:
    3478           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Writing COPY data blocked.");
    3479           0 :             result = OGRERR_FAILURE;
    3480           0 :             break;
    3481           0 :         case -1:
    3482           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s",
    3483             :                      PQerrorMessage(hPGConn));
    3484           0 :             result = OGRERR_FAILURE;
    3485           0 :             break;
    3486             :     }
    3487             : 
    3488             :     /* Now check the results of the copy */
    3489         185 :     PGresult *hResult = PQgetResult(hPGConn);
    3490             : 
    3491         185 :     if (hResult && PQresultStatus(hResult) != PGRES_COMMAND_OK)
    3492             :     {
    3493           1 :         CPLError(CE_Failure, CPLE_AppDefined, "COPY statement failed.\n%s",
    3494             :                  PQerrorMessage(hPGConn));
    3495             : 
    3496           1 :         result = OGRERR_FAILURE;
    3497             :     }
    3498             : 
    3499         185 :     OGRPGClearResult(hResult);
    3500             : 
    3501         185 :     if (!bUseCopyByDefault)
    3502          34 :         bUseCopy = USE_COPY_UNSET;
    3503             : 
    3504         185 :     UpdateSequenceIfNeeded();
    3505             : 
    3506         185 :     return result;
    3507             : }
    3508             : 
    3509             : /************************************************************************/
    3510             : /*                       UpdateSequenceIfNeeded()                       */
    3511             : /************************************************************************/
    3512             : 
    3513        1039 : void OGRPGTableLayer::UpdateSequenceIfNeeded()
    3514             : {
    3515        1039 :     if (bNeedToUpdateSequence && pszFIDColumn != nullptr)
    3516             :     {
    3517          18 :         PGconn *hPGConn = poDS->GetPGConn();
    3518          18 :         CPLString osCommand;
    3519             :         // setval() only works if the value is in [1,INT_MAX] range
    3520             :         // so do not update it if MAX(fid) <= 0
    3521             :         osCommand.Printf(
    3522             :             "SELECT setval(pg_get_serial_sequence(%s, %s), MAX(%s)) FROM %s "
    3523             :             "WHERE EXISTS (SELECT 1 FROM %s WHERE %s > 0 LIMIT 1)",
    3524          36 :             OGRPGEscapeString(hPGConn, pszSqlTableName).c_str(),
    3525          36 :             OGRPGEscapeString(hPGConn, pszFIDColumn).c_str(),
    3526          18 :             OGRPGEscapeColumnName(pszFIDColumn).c_str(), pszSqlTableName,
    3527          72 :             pszSqlTableName, OGRPGEscapeColumnName(pszFIDColumn).c_str());
    3528          18 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    3529          18 :         OGRPGClearResult(hResult);
    3530          18 :         bNeedToUpdateSequence = false;
    3531             :     }
    3532        1039 : }
    3533             : 
    3534             : /************************************************************************/
    3535             : /*                          BuildCopyFields()                           */
    3536             : /************************************************************************/
    3537             : 
    3538         185 : CPLString OGRPGTableLayer::BuildCopyFields()
    3539             : {
    3540         185 :     int i = 0;
    3541         185 :     int nFIDIndex = -1;
    3542         185 :     CPLString osFieldList;
    3543             : 
    3544         354 :     for (i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    3545             :     {
    3546         169 :         OGRGeomFieldDefn *poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(i);
    3547         169 :         if (!osFieldList.empty())
    3548           6 :             osFieldList += ", ";
    3549         169 :         osFieldList += OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
    3550             :     }
    3551             : 
    3552         185 :     if (bFIDColumnInCopyFields)
    3553             :     {
    3554          11 :         if (!osFieldList.empty())
    3555           7 :             osFieldList += ", ";
    3556             : 
    3557          11 :         nFIDIndex = poFeatureDefn->GetFieldIndex(pszFIDColumn);
    3558             : 
    3559          11 :         osFieldList += OGRPGEscapeColumnName(pszFIDColumn);
    3560             :     }
    3561             : 
    3562         708 :     for (i = 0; i < poFeatureDefn->GetFieldCount(); i++)
    3563             :     {
    3564         523 :         if (i == nFIDIndex)
    3565           2 :             continue;
    3566             : 
    3567         521 :         if (poFeatureDefn->GetFieldDefn(i)->IsGenerated())
    3568           2 :             continue;
    3569             : 
    3570         519 :         const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
    3571             : 
    3572         519 :         if (!osFieldList.empty())
    3573         501 :             osFieldList += ", ";
    3574             : 
    3575         519 :         osFieldList += OGRPGEscapeColumnName(pszName);
    3576             :     }
    3577             : 
    3578         185 :     return osFieldList;
    3579             : }
    3580             : 
    3581             : /************************************************************************/
    3582             : /*                    CheckGeomTypeCompatibility()                      */
    3583             : /************************************************************************/
    3584             : 
    3585        1702 : void OGRPGTableLayer::CheckGeomTypeCompatibility(int iGeomField,
    3586             :                                                  OGRGeometry *poGeom)
    3587             : {
    3588        1702 :     if (bHasWarnedIncompatibleGeom)
    3589           0 :         return;
    3590             : 
    3591             :     OGRwkbGeometryType eExpectedGeomType =
    3592        1702 :         poFeatureDefn->GetGeomFieldDefn(iGeomField)->GetType();
    3593        1702 :     OGRwkbGeometryType eFlatLayerGeomType = wkbFlatten(eExpectedGeomType);
    3594        1702 :     OGRwkbGeometryType eFlatGeomType = wkbFlatten(poGeom->getGeometryType());
    3595        1702 :     if (eFlatLayerGeomType == wkbUnknown)
    3596         676 :         return;
    3597             : 
    3598        1026 :     if (eFlatLayerGeomType == wkbGeometryCollection)
    3599           0 :         bHasWarnedIncompatibleGeom = eFlatGeomType != wkbMultiPoint &&
    3600           0 :                                      eFlatGeomType != wkbMultiLineString &&
    3601           0 :                                      eFlatGeomType != wkbMultiPolygon &&
    3602             :                                      eFlatGeomType != wkbGeometryCollection;
    3603             :     else
    3604        1026 :         bHasWarnedIncompatibleGeom = (eFlatGeomType != eFlatLayerGeomType);
    3605             : 
    3606        1026 :     if (bHasWarnedIncompatibleGeom)
    3607             :     {
    3608           2 :         CPLError(CE_Warning, CPLE_AppDefined,
    3609             :                  "Geometry to be inserted is of type %s, whereas the layer "
    3610             :                  "geometry type is %s.\n"
    3611             :                  "Insertion is likely to fail",
    3612           1 :                  OGRGeometryTypeToName(poGeom->getGeometryType()),
    3613             :                  OGRGeometryTypeToName(eExpectedGeomType));
    3614             :     }
    3615             : }
    3616             : 
    3617             : /************************************************************************/
    3618             : /*                        SetOverrideColumnTypes()                      */
    3619             : /************************************************************************/
    3620             : 
    3621         225 : void OGRPGTableLayer::SetOverrideColumnTypes(const char *pszOverrideColumnTypes)
    3622             : {
    3623         225 :     if (pszOverrideColumnTypes == nullptr)
    3624         224 :         return;
    3625             : 
    3626           1 :     const char *pszIter = pszOverrideColumnTypes;
    3627           2 :     CPLString osCur;
    3628          44 :     while (*pszIter != '\0')
    3629             :     {
    3630          44 :         if (*pszIter == '(')
    3631             :         {
    3632             :             /* Ignore commas inside ( ) pair */
    3633          12 :             while (*pszIter != '\0')
    3634             :             {
    3635          12 :                 if (*pszIter == ')')
    3636             :                 {
    3637           2 :                     osCur += *pszIter;
    3638           2 :                     pszIter++;
    3639           2 :                     break;
    3640             :                 }
    3641          10 :                 osCur += *pszIter;
    3642          10 :                 pszIter++;
    3643             :             }
    3644           2 :             if (*pszIter == '\0')
    3645           1 :                 break;
    3646             :         }
    3647             : 
    3648          43 :         if (*pszIter == ',')
    3649             :         {
    3650           3 :             papszOverrideColumnTypes =
    3651           3 :                 CSLAddString(papszOverrideColumnTypes, osCur);
    3652           3 :             osCur = "";
    3653             :         }
    3654             :         else
    3655          40 :             osCur += *pszIter;
    3656          43 :         pszIter++;
    3657             :     }
    3658           1 :     if (!osCur.empty())
    3659           1 :         papszOverrideColumnTypes =
    3660           1 :             CSLAddString(papszOverrideColumnTypes, osCur);
    3661             : }
    3662             : 
    3663             : /************************************************************************/
    3664             : /*                            IGetExtent()                              */
    3665             : /*                                                                      */
    3666             : /*      For PostGIS use internal ST_EstimatedExtent(geometry) function  */
    3667             : /*      if bForce == 0                                                  */
    3668             : /************************************************************************/
    3669             : 
    3670          22 : OGRErr OGRPGTableLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
    3671             :                                    bool bForce)
    3672             : {
    3673          44 :     CPLString osCommand;
    3674             : 
    3675          22 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3676           0 :         return OGRERR_FAILURE;
    3677          22 :     poDS->EndCopy();
    3678             : 
    3679             :     OGRPGGeomFieldDefn *poGeomFieldDefn =
    3680          22 :         poFeatureDefn->GetGeomFieldDefn(iGeomField);
    3681             : 
    3682             :     // if bForce is 0 and ePostgisType is not GEOM_TYPE_GEOGRAPHY we can use
    3683             :     // the ST_EstimatedExtent function which is quicker
    3684             :     // ST_EstimatedExtent was called ST_Estimated_Extent up to PostGIS 2.0.x
    3685             :     // ST_EstimatedExtent returns NULL in absence of statistics (an exception
    3686             :     // before
    3687             :     //   PostGIS 1.5.4)
    3688          22 :     if (bForce == 0 && TestCapability(OLCFastGetExtent))
    3689             :     {
    3690           1 :         PGconn *hPGConn = poDS->GetPGConn();
    3691             : 
    3692           2 :         const char *pszExtentFct = poDS->sPostGISVersion.nMajor > 2 ||
    3693           0 :                                            (poDS->sPostGISVersion.nMajor == 2 &&
    3694           0 :                                             poDS->sPostGISVersion.nMinor >= 1)
    3695           1 :                                        ? "ST_EstimatedExtent"
    3696             :                                        : "ST_Estimated_Extent";
    3697             : 
    3698             :         osCommand.Printf(
    3699             :             "SELECT %s(%s, %s, %s)", pszExtentFct,
    3700           2 :             OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
    3701           2 :             OGRPGEscapeString(hPGConn, pszTableName).c_str(),
    3702           4 :             OGRPGEscapeString(hPGConn, poGeomFieldDefn->GetNameRef()).c_str());
    3703             : 
    3704             :         /* Quiet error: ST_Estimated_Extent may return an error if statistics */
    3705             :         /* have not been computed */
    3706           1 :         if (RunGetExtentRequest(*psExtent, bForce, osCommand, TRUE) ==
    3707             :             OGRERR_NONE)
    3708           1 :             return OGRERR_NONE;
    3709             : 
    3710           0 :         CPLDebug(
    3711             :             "PG",
    3712             :             "Unable to get estimated extent by PostGIS. Trying real extent.");
    3713             :     }
    3714             : 
    3715          21 :     return OGRPGLayer::IGetExtent(iGeomField, psExtent, bForce);
    3716             : }
    3717             : 
    3718             : /************************************************************************/
    3719             : /*                           Rename()                                   */
    3720             : /************************************************************************/
    3721             : 
    3722           6 : OGRErr OGRPGTableLayer::Rename(const char *pszNewName)
    3723             : {
    3724           6 :     if (!TestCapability(OLCRename))
    3725           0 :         return OGRERR_FAILURE;
    3726             : 
    3727           6 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3728           0 :         return OGRERR_FAILURE;
    3729           6 :     poDS->EndCopy();
    3730           6 :     ResetReading();
    3731             : 
    3732           6 :     char *pszNewSqlTableName = CPLStrdup(OGRPGEscapeColumnName(pszNewName));
    3733           6 :     PGconn *hPGConn = poDS->GetPGConn();
    3734           6 :     CPLString osCommand;
    3735             :     osCommand.Printf("ALTER TABLE %s RENAME TO %s", pszSqlTableName,
    3736           6 :                      pszNewSqlTableName);
    3737           6 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    3738             : 
    3739           6 :     OGRErr eRet = OGRERR_NONE;
    3740           6 :     if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
    3741             :     {
    3742           2 :         eRet = OGRERR_FAILURE;
    3743           2 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
    3744             : 
    3745           2 :         CPLFree(pszNewSqlTableName);
    3746             :     }
    3747             :     else
    3748             :     {
    3749           4 :         CPLFree(pszTableName);
    3750           4 :         pszTableName = CPLStrdup(pszNewName);
    3751             : 
    3752           4 :         CPLFree(pszSqlTableName);
    3753           4 :         pszSqlTableName = pszNewSqlTableName;
    3754             : 
    3755           4 :         SetDescription(pszNewName);
    3756           4 :         whileUnsealing(poFeatureDefn)->SetName(pszNewName);
    3757             :     }
    3758             : 
    3759           6 :     OGRPGClearResult(hResult);
    3760             : 
    3761           6 :     return eRet;
    3762             : }
    3763             : 
    3764             : /************************************************************************/
    3765             : /*                        SetDeferredCreation()                         */
    3766             : /************************************************************************/
    3767             : 
    3768         225 : void OGRPGTableLayer::SetDeferredCreation(int bDeferredCreationIn,
    3769             :                                           const std::string &osCreateTableIn)
    3770             : {
    3771         225 :     bDeferredCreation = bDeferredCreationIn;
    3772         225 :     osCreateTable = osCreateTableIn;
    3773         225 : }
    3774             : 
    3775             : /************************************************************************/
    3776             : /*                      RunDeferredCreationIfNecessary()                */
    3777             : /************************************************************************/
    3778             : 
    3779        1530 : OGRErr OGRPGTableLayer::RunDeferredCreationIfNecessary()
    3780             : {
    3781        1530 :     if (!bDeferredCreation)
    3782        1397 :         return OGRERR_NONE;
    3783         133 :     bDeferredCreation = FALSE;
    3784             : 
    3785         133 :     poDS->EndCopy();
    3786             : 
    3787         255 :     for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    3788             :     {
    3789         122 :         OGRPGGeomFieldDefn *poGeomField = poFeatureDefn->GetGeomFieldDefn(i);
    3790             : 
    3791         122 :         if (poDS->HavePostGIS() ||
    3792           0 :             poGeomField->ePostgisType == GEOM_TYPE_GEOGRAPHY)
    3793             :         {
    3794             :             const char *pszGeometryType =
    3795         122 :                 OGRToOGCGeomType(poGeomField->GetType());
    3796             : 
    3797         122 :             osCreateTable += ", ";
    3798         122 :             osCreateTable += OGRPGEscapeColumnName(poGeomField->GetNameRef());
    3799         122 :             osCreateTable += " ";
    3800         122 :             if (poGeomField->ePostgisType == GEOM_TYPE_GEOMETRY)
    3801         118 :                 osCreateTable += "geometry(";
    3802             :             else
    3803           4 :                 osCreateTable += "geography(";
    3804         122 :             osCreateTable += pszGeometryType;
    3805         122 :             if ((poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_3D) &&
    3806          46 :                 (poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
    3807           3 :                 osCreateTable += "ZM";
    3808         119 :             else if (poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_3D)
    3809          43 :                 osCreateTable += "Z";
    3810          76 :             else if (poGeomField->GeometryTypeFlags &
    3811             :                      OGRGeometry::OGR_G_MEASURED)
    3812           4 :                 osCreateTable += "M";
    3813         122 :             if (poGeomField->nSRSId > 0)
    3814          16 :                 osCreateTable += CPLSPrintf(",%d", poGeomField->nSRSId);
    3815         122 :             osCreateTable += ")";
    3816         122 :             if (!poGeomField->IsNullable())
    3817           1 :                 osCreateTable += " NOT NULL";
    3818             :         }
    3819             :     }
    3820             : 
    3821         133 :     osCreateTable += " )";
    3822         266 :     CPLString osCommand(osCreateTable);
    3823             : 
    3824         133 :     PGconn *hPGConn = poDS->GetPGConn();
    3825             : 
    3826         133 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    3827         133 :     if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    3828             :     {
    3829           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    3830             :                  PQerrorMessage(hPGConn));
    3831             : 
    3832           0 :         OGRPGClearResult(hResult);
    3833           0 :         return OGRERR_FAILURE;
    3834             :     }
    3835             : 
    3836         133 :     OGRPGClearResult(hResult);
    3837             : 
    3838         134 :     for (const auto &osSQL : m_aosDeferredCommentOnColumns)
    3839             :     {
    3840           1 :         hResult = OGRPG_PQexec(hPGConn, osSQL.c_str());
    3841           1 :         OGRPGClearResult(hResult);
    3842             :     }
    3843         133 :     m_aosDeferredCommentOnColumns.clear();
    3844             : 
    3845         133 :     if (bCreateSpatialIndexFlag)
    3846             :     {
    3847         255 :         for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    3848             :         {
    3849             :             OGRPGGeomFieldDefn *poGeomField =
    3850         122 :                 poFeatureDefn->GetGeomFieldDefn(i);
    3851         122 :             if (RunCreateSpatialIndex(poGeomField, i) != OGRERR_NONE)
    3852             :             {
    3853           0 :                 return OGRERR_FAILURE;
    3854             :             }
    3855             :         }
    3856             :     }
    3857             : 
    3858         133 :     char **papszMD = OGRLayer::GetMetadata();
    3859         133 :     if (papszMD != nullptr)
    3860           2 :         SetMetadata(papszMD);
    3861             : 
    3862         133 :     return OGRERR_NONE;
    3863             : }
    3864             : 
    3865             : /************************************************************************/
    3866             : /*                         GetGeometryTypes()                           */
    3867             : /************************************************************************/
    3868             : 
    3869          27 : OGRGeometryTypeCounter *OGRPGTableLayer::GetGeometryTypes(
    3870             :     int iGeomField, int nFlagsGGT, int &nEntryCountOut,
    3871             :     GDALProgressFunc pfnProgress, void *pProgressData)
    3872             : {
    3873          27 :     if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount())
    3874             :     {
    3875           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    3876             :                  "Invalid geometry field index : %d", iGeomField);
    3877           1 :         nEntryCountOut = 0;
    3878           1 :         return nullptr;
    3879             :     }
    3880             : 
    3881          26 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3882             :     {
    3883           0 :         nEntryCountOut = 0;
    3884           0 :         return nullptr;
    3885             :     }
    3886          26 :     poDS->EndCopy();
    3887             : 
    3888             :     const OGRPGGeomFieldDefn *poGeomFieldDefn =
    3889          26 :         GetLayerDefn()->GetGeomFieldDefn(iGeomField);
    3890             :     const auto osEscapedGeom =
    3891          52 :         OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
    3892          52 :     CPLString osSQL;
    3893          26 :     if ((nFlagsGGT & OGR_GGT_GEOMCOLLECTIONZ_TINZ) != 0)
    3894             :     {
    3895          18 :         CPLString osFilter;
    3896             :         osFilter.Printf("(ST_Zmflag(%s) = 2 AND "
    3897             :                         "((GeometryType(%s) = 'GEOMETRYCOLLECTION' AND "
    3898             :                         "ST_NumGeometries(%s) >= 1 AND "
    3899             :                         "geometrytype(ST_GeometryN(%s, 1)) = 'TIN') OR "
    3900             :                         "GeometryType(%s) = 'TIN'))",
    3901             :                         osEscapedGeom.c_str(), osEscapedGeom.c_str(),
    3902             :                         osEscapedGeom.c_str(), osEscapedGeom.c_str(),
    3903           9 :                         osEscapedGeom.c_str());
    3904             : 
    3905          18 :         std::string l_osWHERE(osWHERE);
    3906           9 :         if (l_osWHERE.empty())
    3907           6 :             l_osWHERE = " WHERE ";
    3908             :         else
    3909           3 :             l_osWHERE += " AND ";
    3910           9 :         l_osWHERE += "(NOT (";
    3911           9 :         l_osWHERE += osFilter;
    3912           9 :         l_osWHERE += ") OR ";
    3913           9 :         l_osWHERE += osEscapedGeom;
    3914           9 :         l_osWHERE += " IS NULL)";
    3915             : 
    3916          18 :         std::string l_osWHEREFilter(osWHERE);
    3917           9 :         if (l_osWHEREFilter.empty())
    3918           6 :             l_osWHEREFilter = " WHERE ";
    3919             :         else
    3920           3 :             l_osWHEREFilter += " AND ";
    3921           9 :         l_osWHEREFilter += osFilter;
    3922             : 
    3923             :         osSQL.Printf(
    3924             :             "(SELECT GeometryType(%s), ST_Zmflag(%s), COUNT(*) FROM %s %s "
    3925             :             "GROUP BY GeometryType(%s), ST_Zmflag(%s)) UNION ALL "
    3926             :             "(SELECT * FROM (SELECT 'TIN', 2, COUNT(*) AS count FROM %s %s) "
    3927             :             "tinsubselect WHERE tinsubselect.count != 0)",
    3928             :             osEscapedGeom.c_str(), osEscapedGeom.c_str(), pszSqlTableName,
    3929             :             l_osWHERE.c_str(), osEscapedGeom.c_str(), osEscapedGeom.c_str(),
    3930           9 :             pszSqlTableName, l_osWHEREFilter.c_str());
    3931             :     }
    3932          17 :     else if ((nFlagsGGT & OGR_GGT_STOP_IF_MIXED) != 0)
    3933             :     {
    3934           8 :         std::string l_osWHERE(osWHERE);
    3935           4 :         if (l_osWHERE.empty())
    3936           2 :             l_osWHERE = " WHERE ";
    3937             :         else
    3938           2 :             l_osWHERE += " AND ";
    3939           4 :         l_osWHERE += osEscapedGeom;
    3940           4 :         l_osWHERE += " IS NOT NULL";
    3941             : 
    3942           8 :         std::string l_osWHERE_NULL(osWHERE);
    3943           4 :         if (l_osWHERE_NULL.empty())
    3944           2 :             l_osWHERE_NULL = " WHERE ";
    3945             :         else
    3946           2 :             l_osWHERE_NULL += " AND ";
    3947           4 :         l_osWHERE_NULL += osEscapedGeom;
    3948           4 :         l_osWHERE_NULL += " IS NULL";
    3949             : 
    3950             :         osSQL.Printf("(SELECT DISTINCT GeometryType(%s), ST_Zmflag(%s), 0 FROM "
    3951             :                      "%s %s LIMIT 2) "
    3952             :                      "UNION ALL (SELECT NULL, NULL, 0 FROM %s %s LIMIT 1)",
    3953             :                      osEscapedGeom.c_str(), osEscapedGeom.c_str(),
    3954             :                      pszSqlTableName, l_osWHERE.c_str(), pszSqlTableName,
    3955           4 :                      l_osWHERE_NULL.c_str());
    3956             :     }
    3957             :     else
    3958             :     {
    3959             :         const bool bDebug =
    3960          13 :             CPLTestBool(CPLGetConfigOption("OGR_PG_DEBUG_GGT_CANCEL", "NO"));
    3961             :         osSQL.Printf(
    3962             :             "SELECT GeometryType(%s), ST_Zmflag(%s), COUNT(*)%s FROM %s %s "
    3963             :             "GROUP BY GeometryType(%s), ST_Zmflag(%s)",
    3964             :             osEscapedGeom.c_str(), osEscapedGeom.c_str(),
    3965             :             bDebug ? ", pg_sleep(1)" : "", pszSqlTableName, osWHERE.c_str(),
    3966          13 :             osEscapedGeom.c_str(), osEscapedGeom.c_str());
    3967             :     }
    3968             : 
    3969          52 :     std::thread thread;
    3970          26 :     std::mutex mutex;
    3971          26 :     std::condition_variable cv;
    3972          26 :     bool stopThread = false;
    3973          26 :     if (pfnProgress && pfnProgress != GDALDummyProgress)
    3974             :     {
    3975           4 :         thread = std::thread(
    3976           2 :             [&]()
    3977             :             {
    3978           4 :                 std::unique_lock<std::mutex> lock(mutex);
    3979           4 :                 while (!stopThread)
    3980             :                 {
    3981           2 :                     if (!pfnProgress(0.0, "", pProgressData))
    3982           1 :                         poDS->AbortSQL();
    3983           2 :                     cv.wait_for(lock, std::chrono::milliseconds(100));
    3984             :                 }
    3985           4 :             });
    3986             :     }
    3987             : 
    3988          26 :     PGconn *hPGConn = poDS->GetPGConn();
    3989          26 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osSQL.c_str());
    3990             : 
    3991          26 :     if (pfnProgress && pfnProgress != GDALDummyProgress)
    3992             :     {
    3993             :         {
    3994           4 :             std::unique_lock<std::mutex> lock(mutex);
    3995           2 :             stopThread = true;
    3996           2 :             cv.notify_one();
    3997             :         }
    3998           2 :         thread.join();
    3999             :     }
    4000             : 
    4001          26 :     nEntryCountOut = 0;
    4002          26 :     OGRGeometryTypeCounter *pasRet = nullptr;
    4003          26 :     if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK)
    4004             :     {
    4005          25 :         const int nTuples = PQntuples(hResult);
    4006          25 :         nEntryCountOut = nTuples;
    4007             :         pasRet = static_cast<OGRGeometryTypeCounter *>(
    4008          25 :             CPLCalloc(1 + nEntryCountOut, sizeof(OGRGeometryTypeCounter)));
    4009          69 :         for (int i = 0; i < nTuples; ++i)
    4010             :         {
    4011          44 :             const char *pszGeomType = PQgetvalue(hResult, i, 0);
    4012          44 :             const char *pszZMFlag = PQgetvalue(hResult, i, 1);
    4013          44 :             const char *pszCount = PQgetvalue(hResult, i, 2);
    4014          44 :             if (pszCount)
    4015             :             {
    4016          44 :                 if (pszGeomType == nullptr || pszGeomType[0] == '\0')
    4017             :                 {
    4018          16 :                     pasRet[i].eGeomType = wkbNone;
    4019             :                 }
    4020          28 :                 else if (pszZMFlag != nullptr)
    4021             :                 {
    4022          28 :                     const int nZMFlag = atoi(pszZMFlag);
    4023          28 :                     pasRet[i].eGeomType = OGRFromOGCGeomType(pszGeomType);
    4024          28 :                     int nModifier = 0;
    4025          28 :                     if (nZMFlag == 1)
    4026           1 :                         nModifier = OGRGeometry::OGR_G_MEASURED;
    4027          27 :                     else if (nZMFlag == 2)
    4028           9 :                         nModifier = OGRGeometry::OGR_G_3D;
    4029          18 :                     else if (nZMFlag == 3)
    4030           1 :                         nModifier =
    4031             :                             OGRGeometry::OGR_G_MEASURED | OGRGeometry::OGR_G_3D;
    4032          28 :                     pasRet[i].eGeomType = OGR_GT_SetModifier(
    4033          28 :                         pasRet[i].eGeomType, nModifier & OGRGeometry::OGR_G_3D,
    4034             :                         nModifier & OGRGeometry::OGR_G_MEASURED);
    4035             :                 }
    4036          44 :                 pasRet[i].nCount =
    4037          44 :                     static_cast<int64_t>(std::strtoll(pszCount, nullptr, 10));
    4038             :             }
    4039             :         }
    4040             :     }
    4041             : 
    4042          26 :     OGRPGClearResult(hResult);
    4043             : 
    4044          26 :     return pasRet;
    4045             : }
    4046             : 
    4047             : /************************************************************************/
    4048             : /*                          FindFieldIndex()                            */
    4049             : /************************************************************************/
    4050             : 
    4051          24 : int OGRPGTableLayer::FindFieldIndex(const char *pszFieldName, int bExactMatch)
    4052             : {
    4053          24 :     const auto poLayerDefn = GetLayerDefn();
    4054          24 :     int iField = poLayerDefn->GetFieldIndex(pszFieldName);
    4055             : 
    4056          24 :     if (!bExactMatch && iField < 0 && bLaunderColumnNames)
    4057             :     {
    4058          10 :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
    4059             :         char *pszSafeName =
    4060           5 :             OGRPGCommonLaunderName(pszFieldName, "PG", m_bUTF8ToASCII);
    4061           5 :         iField = poLayerDefn->GetFieldIndex(pszSafeName);
    4062           5 :         CPLFree(pszSafeName);
    4063             :     }
    4064             : 
    4065          24 :     return iField;
    4066             : }
    4067             : 
    4068             : #undef PQexec

Generated by: LCOV version 1.14