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

Generated by: LCOV version 1.14