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

Generated by: LCOV version 1.14