LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/pg - ogrpgtablelayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1686 1929 87.4 %
Date: 2025-01-18 12:42:00 Functions: 66 66 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             : 
       3             :  *
       4             :  * Project:  OpenGIS Simple Features Reference Implementation
       5             :  * Purpose:  Implements OGRPGTableLayer class, access to an existing table.
       6             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2000, Frank Warmerdam
      10             :  * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "ogr_pg.h"
      16             : #include "cpl_conv.h"
      17             : #include "cpl_string.h"
      18             : #include "cpl_error.h"
      19             : #include "ogr_p.h"
      20             : 
      21             : #include <chrono>
      22             : #include <condition_variable>
      23             : #include <mutex>
      24             : #include <thread>
      25             : 
      26             : #define PQexec this_is_an_error
      27             : 
      28             : #define UNSUPPORTED_OP_READ_ONLY                                               \
      29             :     "%s : unsupported operation on a read-only datasource."
      30             : 
      31             : /************************************************************************/
      32             : /*                        OGRPGTableFeatureDefn                         */
      33             : /************************************************************************/
      34             : 
      35             : class OGRPGTableFeatureDefn final : public OGRPGFeatureDefn
      36             : {
      37             :   private:
      38             :     OGRPGTableFeatureDefn(const OGRPGTableFeatureDefn &) = delete;
      39             :     OGRPGTableFeatureDefn &operator=(const OGRPGTableFeatureDefn &) = delete;
      40             : 
      41             :     OGRPGTableLayer *poLayer = nullptr;
      42             : 
      43             :     void SolveFields() const;
      44             : 
      45             :   public:
      46         731 :     explicit OGRPGTableFeatureDefn(OGRPGTableLayer *poLayerIn,
      47             :                                    const char *pszName = nullptr)
      48         731 :         : OGRPGFeatureDefn(pszName), poLayer(poLayerIn)
      49             :     {
      50         731 :     }
      51             : 
      52         731 :     virtual void UnsetLayer() override
      53             :     {
      54         731 :         poLayer = nullptr;
      55         731 :         OGRPGFeatureDefn::UnsetLayer();
      56         731 :     }
      57             : 
      58      123768 :     virtual int GetFieldCount() const override
      59             :     {
      60      123768 :         SolveFields();
      61      123768 :         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        7880 :     virtual int GetFieldIndex(const char *pszName) const override
      77             :     {
      78        7880 :         SolveFields();
      79        7880 :         return OGRPGFeatureDefn::GetFieldIndex(pszName);
      80             :     }
      81             : 
      82       56640 :     virtual int GetGeomFieldCount() const override
      83             :     {
      84       56640 :         if (poLayer != nullptr && !poLayer->HasGeometryInformation())
      85        1178 :             SolveFields();
      86       56640 :         return OGRPGFeatureDefn::GetGeomFieldCount();
      87             :     }
      88             : 
      89       14753 :     virtual OGRPGGeomFieldDefn *GetGeomFieldDefn(int i) override
      90             :     {
      91       14753 :         if (poLayer != nullptr && !poLayer->HasGeometryInformation())
      92         162 :             SolveFields();
      93       14753 :         return OGRPGFeatureDefn::GetGeomFieldDefn(i);
      94             :     }
      95             : 
      96        1109 :     virtual const OGRPGGeomFieldDefn *GetGeomFieldDefn(int i) const override
      97             :     {
      98        1109 :         if (poLayer != nullptr && !poLayer->HasGeometryInformation())
      99           0 :             SolveFields();
     100        1109 :         return OGRPGFeatureDefn::GetGeomFieldDefn(i);
     101             :     }
     102             : 
     103         891 :     virtual int GetGeomFieldIndex(const char *pszName) const override
     104             :     {
     105         891 :         if (poLayer != nullptr && !poLayer->HasGeometryInformation())
     106           0 :             SolveFields();
     107         891 :         return OGRPGFeatureDefn::GetGeomFieldIndex(pszName);
     108             :     }
     109             : };
     110             : 
     111             : /************************************************************************/
     112             : /*                           SolveFields()                              */
     113             : /************************************************************************/
     114             : 
     115      213730 : void OGRPGTableFeatureDefn::SolveFields() const
     116             : {
     117      213730 :     if (poLayer == nullptr)
     118           0 :         return;
     119             : 
     120      213730 :     poLayer->ReadTableDefinition();
     121             : }
     122             : 
     123             : /************************************************************************/
     124             : /*                            GetFIDColumn()                            */
     125             : /************************************************************************/
     126             : 
     127          86 : const char *OGRPGTableLayer::GetFIDColumn()
     128             : 
     129             : {
     130          86 :     ReadTableDefinition();
     131             : 
     132          86 :     if (pszFIDColumn != nullptr)
     133          84 :         return pszFIDColumn;
     134             :     else
     135           2 :         return "";
     136             : }
     137             : 
     138             : /************************************************************************/
     139             : /*                          OGRPGTableLayer()                           */
     140             : /************************************************************************/
     141             : 
     142         731 : OGRPGTableLayer::OGRPGTableLayer(OGRPGDataSource *poDSIn,
     143             :                                  CPLString &osCurrentSchema,
     144             :                                  const char *pszTableNameIn,
     145             :                                  const char *pszSchemaNameIn,
     146             :                                  const char *pszDescriptionIn,
     147         731 :                                  const char *pszGeomColForcedIn, int bUpdate)
     148        1462 :     : bUpdateAccess(bUpdate), pszTableName(CPLStrdup(pszTableNameIn)),
     149         731 :       pszSchemaName(CPLStrdup(pszSchemaNameIn ? pszSchemaNameIn
     150         384 :                                               : osCurrentSchema.c_str())),
     151         731 :       m_pszTableDescription(pszDescriptionIn ? CPLStrdup(pszDescriptionIn)
     152             :                                              : nullptr),
     153             :       osPrimaryKey(CPLGetConfigOption("PGSQL_OGR_FID", "ogc_fid")),
     154         731 :       pszGeomColForced(pszGeomColForcedIn ? CPLStrdup(pszGeomColForcedIn)
     155             :                                           : nullptr),
     156             :       // Just in provision for people yelling about broken backward
     157             :       // compatibility.
     158             :       bRetrieveFID(
     159         731 :           CPLTestBool(CPLGetConfigOption("OGR_PG_RETRIEVE_FID", "TRUE"))),
     160             :       bSkipConflicts(
     161        3655 :           CPLTestBool(CPLGetConfigOption("OGR_PG_SKIP_CONFLICTS", "FALSE")))
     162             : {
     163         731 :     poDS = poDSIn;
     164         731 :     pszQueryStatement = nullptr;
     165             : 
     166             :     /* -------------------------------------------------------------------- */
     167             :     /*      Build the layer defn name.                                      */
     168             :     /* -------------------------------------------------------------------- */
     169        1462 :     CPLString osDefnName;
     170         731 :     if (pszSchemaNameIn && osCurrentSchema != pszSchemaNameIn)
     171             :     {
     172          53 :         osDefnName.Printf("%s.%s", pszSchemaNameIn, pszTableName);
     173         159 :         pszSqlTableName = CPLStrdup(CPLString().Printf(
     174         106 :             "%s.%s", OGRPGEscapeColumnName(pszSchemaNameIn).c_str(),
     175         159 :             OGRPGEscapeColumnName(pszTableName).c_str()));
     176             :     }
     177             :     else
     178             :     {
     179             :         // no prefix for current_schema in layer name, for backwards
     180             :         // compatibility.
     181         678 :         osDefnName = pszTableName;
     182         678 :         pszSqlTableName = CPLStrdup(OGRPGEscapeColumnName(pszTableName));
     183             :     }
     184         731 :     if (pszGeomColForced != nullptr)
     185             :     {
     186           7 :         osDefnName += "(";
     187           7 :         osDefnName += pszGeomColForced;
     188           7 :         osDefnName += ")";
     189             :     }
     190             : 
     191         731 :     poFeatureDefn = new OGRPGTableFeatureDefn(this, osDefnName);
     192         731 :     SetDescription(poFeatureDefn->GetName());
     193         731 :     poFeatureDefn->Reference();
     194             : 
     195             :     // bSealFields = false because we do lazy resolution of fields
     196         731 :     poFeatureDefn->Seal(/* bSealFields = */ false);
     197             : 
     198         731 :     if (pszDescriptionIn != nullptr && !EQUAL(pszDescriptionIn, ""))
     199             :     {
     200           2 :         OGRLayer::SetMetadataItem("DESCRIPTION", pszDescriptionIn);
     201             :     }
     202         731 : }
     203             : 
     204             : //************************************************************************/
     205             : /*                          ~OGRPGTableLayer()                          */
     206             : /************************************************************************/
     207             : 
     208        1462 : OGRPGTableLayer::~OGRPGTableLayer()
     209             : 
     210             : {
     211         731 :     if (bDeferredCreation)
     212           0 :         RunDeferredCreationIfNecessary();
     213         731 :     if (bCopyActive)
     214           0 :         EndCopy();
     215         731 :     UpdateSequenceIfNeeded();
     216         731 :     SerializeMetadata();
     217             : 
     218         731 :     CPLFree(pszSqlTableName);
     219         731 :     CPLFree(pszTableName);
     220         731 :     CPLFree(pszSqlGeomParentTableName);
     221         731 :     CPLFree(pszSchemaName);
     222         731 :     CPLFree(m_pszTableDescription);
     223         731 :     CPLFree(pszGeomColForced);
     224         731 :     CSLDestroy(papszOverrideColumnTypes);
     225        1462 : }
     226             : 
     227             : /************************************************************************/
     228             : /*                              LoadMetadata()                          */
     229             : /************************************************************************/
     230             : 
     231         122 : void OGRPGTableLayer::LoadMetadata()
     232             : {
     233         122 :     if (m_bMetadataLoaded)
     234         104 :         return;
     235          37 :     m_bMetadataLoaded = true;
     236             : 
     237          37 :     if (!poDS->HasOgrSystemTablesMetadataTable())
     238          19 :         return;
     239             : 
     240          18 :     PGconn *hPGConn = poDS->GetPGConn();
     241             : 
     242             :     const std::string osSQL(
     243             :         CPLSPrintf("SELECT metadata FROM ogr_system_tables.metadata WHERE "
     244             :                    "schema_name = %s AND table_name = %s",
     245          36 :                    OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
     246          72 :                    OGRPGEscapeString(hPGConn, pszTableName).c_str()));
     247          18 :     auto poSqlLyr = poDS->ExecuteSQL(osSQL.c_str(), nullptr, nullptr);
     248          18 :     if (poSqlLyr)
     249             :     {
     250             :         auto poFeature =
     251          36 :             std::unique_ptr<OGRFeature>(poSqlLyr->GetNextFeature());
     252          18 :         if (poFeature)
     253             :         {
     254           2 :             if (poFeature->IsFieldSetAndNotNull(0))
     255             :             {
     256           2 :                 const char *pszXML = poFeature->GetFieldAsString(0);
     257           2 :                 if (pszXML)
     258             :                 {
     259           2 :                     auto psRoot = CPLParseXMLString(pszXML);
     260           2 :                     if (psRoot)
     261             :                     {
     262           2 :                         oMDMD.XMLInit(psRoot, true);
     263           2 :                         CPLDestroyXMLNode(psRoot);
     264             :                     }
     265             :                 }
     266             :             }
     267             :         }
     268          18 :         poDS->ReleaseResultSet(poSqlLyr);
     269             :     }
     270             : }
     271             : 
     272             : /************************************************************************/
     273             : /*                         SerializeMetadata()                          */
     274             : /************************************************************************/
     275             : 
     276         731 : void OGRPGTableLayer::SerializeMetadata()
     277             : {
     278         748 :     if (!m_bMetadataModified ||
     279          17 :         !CPLTestBool(CPLGetConfigOption("OGR_PG_ENABLE_METADATA", "YES")))
     280             :     {
     281         715 :         return;
     282             :     }
     283             : 
     284          16 :     PGconn *hPGConn = poDS->GetPGConn();
     285          16 :     CPLXMLNode *psMD = oMDMD.Serialize();
     286             : 
     287          16 :     if (psMD)
     288             :     {
     289             :         // Remove DESCRIPTION and OLMD_FID64 items from metadata
     290             : 
     291          12 :         CPLXMLNode *psPrev = nullptr;
     292          24 :         for (CPLXMLNode *psIter = psMD; psIter;)
     293             :         {
     294          12 :             CPLXMLNode *psNext = psIter->psNext;
     295          36 :             if (psIter->eType == CXT_Element &&
     296          24 :                 strcmp(psIter->pszValue, "Metadata") == 0 &&
     297          12 :                 CPLGetXMLNode(psIter, "domain") == nullptr)
     298             :             {
     299          12 :                 bool bFoundInterestingItems = false;
     300          28 :                 for (CPLXMLNode *psIter2 = psIter->psChild; psIter2;)
     301             :                 {
     302          16 :                     CPLXMLNode *psNext2 = psIter2->psNext;
     303          48 :                     if (psIter2->eType == CXT_Element &&
     304          32 :                         strcmp(psIter2->pszValue, "MDI") == 0 &&
     305          16 :                         (EQUAL(CPLGetXMLValue(psIter2, "key", ""),
     306          14 :                                OLMD_FID64) ||
     307          14 :                          EQUAL(CPLGetXMLValue(psIter2, "key", ""),
     308             :                                "DESCRIPTION")))
     309             :                     {
     310          10 :                         CPLRemoveXMLChild(psIter, psIter2);
     311             :                     }
     312             :                     else
     313             :                     {
     314           6 :                         bFoundInterestingItems = true;
     315             :                     }
     316          16 :                     psIter2 = psNext2;
     317             :                 }
     318          12 :                 if (!bFoundInterestingItems)
     319             :                 {
     320           8 :                     if (psPrev)
     321           0 :                         psPrev->psNext = psNext;
     322             :                     else
     323           8 :                         psMD = psNext;
     324           8 :                     psIter->psNext = nullptr;
     325           8 :                     CPLDestroyXMLNode(psIter);
     326             :                 }
     327             :             }
     328          12 :             psIter = psNext;
     329          12 :             psPrev = psIter;
     330             :         }
     331             :     }
     332             : 
     333          16 :     const bool bIsUserTransactionActive = poDS->IsUserTransactionActive();
     334             :     {
     335          16 :         PGresult *hResult = OGRPG_PQexec(
     336             :             hPGConn, bIsUserTransactionActive
     337             :                          ? "SAVEPOINT ogr_system_tables_metadata_savepoint"
     338          16 :                          : "BEGIN");
     339          16 :         OGRPGClearResult(hResult);
     340             :     }
     341             : 
     342          16 :     if (psMD)
     343             :     {
     344           8 :         if (poDS->CreateMetadataTableIfNeeded() &&
     345           4 :             poDS->HasWritePermissionsOnMetadataTable())
     346             :         {
     347           6 :             CPLString osCommand;
     348             :             osCommand.Printf("DELETE FROM ogr_system_tables.metadata WHERE "
     349             :                              "schema_name = %s AND table_name = %s",
     350           6 :                              OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
     351           9 :                              OGRPGEscapeString(hPGConn, pszTableName).c_str());
     352           3 :             PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
     353           3 :             OGRPGClearResult(hResult);
     354             : 
     355             :             CPLXMLNode *psRoot =
     356           3 :                 CPLCreateXMLNode(nullptr, CXT_Element, "GDALMetadata");
     357           3 :             CPLAddXMLChild(psRoot, psMD);
     358           3 :             char *pszXML = CPLSerializeXMLTree(psRoot);
     359             :             // CPLDebug("PG", "Serializing %s", pszXML);
     360             : 
     361             :             osCommand.Printf(
     362             :                 "INSERT INTO ogr_system_tables.metadata (schema_name, "
     363             :                 "table_name, metadata) VALUES (%s, %s, %s)",
     364           6 :                 OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
     365           6 :                 OGRPGEscapeString(hPGConn, pszTableName).c_str(),
     366          12 :                 OGRPGEscapeString(hPGConn, pszXML).c_str());
     367           3 :             hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
     368           3 :             OGRPGClearResult(hResult);
     369             : 
     370           3 :             CPLDestroyXMLNode(psRoot);
     371           3 :             CPLFree(pszXML);
     372             :         }
     373             :     }
     374          19 :     else if (poDS->HasOgrSystemTablesMetadataTable() &&
     375           7 :              poDS->HasWritePermissionsOnMetadataTable())
     376             :     {
     377          14 :         CPLString osCommand;
     378             :         osCommand.Printf("DELETE FROM ogr_system_tables.metadata WHERE "
     379             :                          "schema_name = %s AND table_name = %s",
     380          14 :                          OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
     381          21 :                          OGRPGEscapeString(hPGConn, pszTableName).c_str());
     382             :         PGresult *hResult =
     383           7 :             OGRPG_PQexec(hPGConn, osCommand.c_str(), false, true);
     384           7 :         OGRPGClearResult(hResult);
     385             :     }
     386             : 
     387             :     {
     388          16 :         PGresult *hResult = OGRPG_PQexec(
     389             :             hPGConn,
     390             :             bIsUserTransactionActive
     391             :                 ? "RELEASE SAVEPOINT ogr_system_tables_metadata_savepoint"
     392          16 :                 : "COMMIT");
     393          16 :         OGRPGClearResult(hResult);
     394             :     }
     395             : }
     396             : 
     397             : /************************************************************************/
     398             : /*                          GetMetadataDomainList()                     */
     399             : /************************************************************************/
     400             : 
     401           7 : char **OGRPGTableLayer::GetMetadataDomainList()
     402             : {
     403           7 :     LoadMetadata();
     404             : 
     405           7 :     if (m_pszTableDescription == nullptr)
     406           4 :         GetMetadata();
     407           7 :     if (m_pszTableDescription != nullptr && m_pszTableDescription[0] != '\0')
     408           4 :         return CSLAddString(nullptr, "");
     409           3 :     return nullptr;
     410             : }
     411             : 
     412             : /************************************************************************/
     413             : /*                              GetMetadata()                           */
     414             : /************************************************************************/
     415             : 
     416          54 : char **OGRPGTableLayer::GetMetadata(const char *pszDomain)
     417             : {
     418          54 :     LoadMetadata();
     419             : 
     420          54 :     if ((pszDomain == nullptr || EQUAL(pszDomain, "")) &&
     421          53 :         m_pszTableDescription == nullptr)
     422             :     {
     423          22 :         PGconn *hPGConn = poDS->GetPGConn();
     424          44 :         CPLString osCommand;
     425             :         osCommand.Printf("SELECT d.description FROM pg_class c "
     426             :                          "JOIN pg_namespace n ON c.relnamespace=n.oid "
     427             :                          "JOIN pg_description d "
     428             :                          "ON d.objoid = c.oid AND d.classoid = "
     429             :                          "'pg_class'::regclass::oid AND d.objsubid = 0 "
     430             :                          "WHERE c.relname = %s AND n.nspname = %s AND "
     431             :                          "c.relkind in ('r', 'v') ",
     432          44 :                          OGRPGEscapeString(hPGConn, pszTableName).c_str(),
     433          66 :                          OGRPGEscapeString(hPGConn, pszSchemaName).c_str());
     434          22 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
     435             : 
     436          22 :         const char *pszDesc = nullptr;
     437          44 :         if (hResult && PGRES_TUPLES_OK == PQresultStatus(hResult) &&
     438          22 :             PQntuples(hResult) == 1)
     439             :         {
     440           8 :             pszDesc = PQgetvalue(hResult, 0, 0);
     441           8 :             if (pszDesc)
     442           8 :                 OGRLayer::SetMetadataItem("DESCRIPTION", pszDesc);
     443             :         }
     444          22 :         m_pszTableDescription = CPLStrdup(pszDesc ? pszDesc : "");
     445             : 
     446          22 :         OGRPGClearResult(hResult);
     447             :     }
     448             : 
     449          54 :     return OGRLayer::GetMetadata(pszDomain);
     450             : }
     451             : 
     452             : /************************************************************************/
     453             : /*                            GetMetadataItem()                         */
     454             : /************************************************************************/
     455             : 
     456          25 : const char *OGRPGTableLayer::GetMetadataItem(const char *pszName,
     457             :                                              const char *pszDomain)
     458             : {
     459          25 :     LoadMetadata();
     460             : 
     461          25 :     GetMetadata(pszDomain);
     462          25 :     return OGRLayer::GetMetadataItem(pszName, pszDomain);
     463             : }
     464             : 
     465             : /************************************************************************/
     466             : /*                              SetMetadata()                           */
     467             : /************************************************************************/
     468             : 
     469          21 : CPLErr OGRPGTableLayer::SetMetadata(char **papszMD, const char *pszDomain)
     470             : {
     471          21 :     LoadMetadata();
     472             : 
     473          21 :     OGRLayer::SetMetadata(papszMD, pszDomain);
     474          21 :     m_bMetadataModified = true;
     475             : 
     476          26 :     if (!osForcedDescription.empty() &&
     477           5 :         (pszDomain == nullptr || EQUAL(pszDomain, "")))
     478             :     {
     479           5 :         OGRLayer::SetMetadataItem("DESCRIPTION", osForcedDescription);
     480             :     }
     481             : 
     482          21 :     if (!bDeferredCreation && (pszDomain == nullptr || EQUAL(pszDomain, "")))
     483             :     {
     484          20 :         const char *pszDescription = OGRLayer::GetMetadataItem("DESCRIPTION");
     485          20 :         if (pszDescription == nullptr)
     486          10 :             pszDescription = "";
     487          20 :         PGconn *hPGConn = poDS->GetPGConn();
     488          20 :         CPLString osCommand;
     489             : 
     490             :         osCommand.Printf(
     491             :             "COMMENT ON TABLE %s IS %s", pszSqlTableName,
     492          20 :             pszDescription[0] != '\0'
     493          30 :                 ? OGRPGEscapeString(hPGConn, pszDescription).c_str()
     494          30 :                 : "NULL");
     495          20 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
     496          20 :         OGRPGClearResult(hResult);
     497             : 
     498          20 :         CPLFree(m_pszTableDescription);
     499          20 :         m_pszTableDescription = CPLStrdup(pszDescription);
     500             :     }
     501             : 
     502          21 :     return CE_None;
     503             : }
     504             : 
     505             : /************************************************************************/
     506             : /*                            SetMetadataItem()                         */
     507             : /************************************************************************/
     508             : 
     509          15 : CPLErr OGRPGTableLayer::SetMetadataItem(const char *pszName,
     510             :                                         const char *pszValue,
     511             :                                         const char *pszDomain)
     512             : {
     513          15 :     LoadMetadata();
     514             : 
     515          15 :     if ((pszDomain == nullptr || EQUAL(pszDomain, "")) && pszName != nullptr &&
     516          30 :         EQUAL(pszName, "DESCRIPTION") && !osForcedDescription.empty())
     517             :     {
     518           4 :         pszValue = osForcedDescription;
     519             :     }
     520             : 
     521          15 :     OGRLayer::SetMetadataItem(pszName, pszValue, pszDomain);
     522          15 :     m_bMetadataModified = true;
     523             : 
     524          15 :     if (!bDeferredCreation && (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
     525          12 :         pszName != nullptr && EQUAL(pszName, "DESCRIPTION"))
     526             :     {
     527           8 :         SetMetadata(GetMetadata());
     528             :     }
     529             : 
     530          15 :     return CE_None;
     531             : }
     532             : 
     533             : /************************************************************************/
     534             : /*                      SetForcedDescription()                          */
     535             : /************************************************************************/
     536             : 
     537           2 : void OGRPGTableLayer::SetForcedDescription(const char *pszDescriptionIn)
     538             : {
     539           2 :     osForcedDescription = pszDescriptionIn;
     540           2 :     CPLFree(m_pszTableDescription);
     541           2 :     m_pszTableDescription = CPLStrdup(pszDescriptionIn);
     542           2 :     SetMetadataItem("DESCRIPTION", osForcedDescription);
     543           2 : }
     544             : 
     545             : /************************************************************************/
     546             : /*                      SetGeometryInformation()                        */
     547             : /************************************************************************/
     548             : 
     549          46 : void OGRPGTableLayer::SetGeometryInformation(PGGeomColumnDesc *pasDesc,
     550             :                                              int nGeomFieldCount)
     551             : {
     552             :     // Flag must be set before instantiating geometry fields.
     553          46 :     bGeometryInformationSet = TRUE;
     554          92 :     auto oTemporaryUnsealer(poFeatureDefn->GetTemporaryUnsealer(false));
     555             : 
     556         106 :     for (int i = 0; i < nGeomFieldCount; i++)
     557             :     {
     558             :         auto poGeomFieldDefn =
     559          60 :             std::make_unique<OGRPGGeomFieldDefn>(this, pasDesc[i].pszName);
     560          60 :         poGeomFieldDefn->SetNullable(pasDesc[i].bNullable);
     561          60 :         poGeomFieldDefn->nSRSId = pasDesc[i].nSRID;
     562          60 :         poGeomFieldDefn->GeometryTypeFlags = pasDesc[i].GeometryTypeFlags;
     563          60 :         poGeomFieldDefn->ePostgisType = pasDesc[i].ePostgisType;
     564          60 :         if (pasDesc[i].pszGeomType != nullptr)
     565             :         {
     566             :             OGRwkbGeometryType eGeomType =
     567          58 :                 OGRFromOGCGeomType(pasDesc[i].pszGeomType);
     568          58 :             if ((poGeomFieldDefn->GeometryTypeFlags & OGRGeometry::OGR_G_3D) &&
     569             :                 (eGeomType != wkbUnknown))
     570          21 :                 eGeomType = wkbSetZ(eGeomType);
     571          58 :             if ((poGeomFieldDefn->GeometryTypeFlags &
     572          58 :                  OGRGeometry::OGR_G_MEASURED) &&
     573             :                 (eGeomType != wkbUnknown))
     574           1 :                 eGeomType = wkbSetM(eGeomType);
     575          58 :             poGeomFieldDefn->SetType(eGeomType);
     576             :         }
     577          60 :         poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
     578             :     }
     579          46 : }
     580             : 
     581             : /************************************************************************/
     582             : /*                        ReadTableDefinition()                         */
     583             : /*                                                                      */
     584             : /*      Build a schema from the named table.  Done by querying the      */
     585             : /*      catalog.                                                        */
     586             : /************************************************************************/
     587             : 
     588      214230 : int OGRPGTableLayer::ReadTableDefinition()
     589             : 
     590             : {
     591      214230 :     PGconn *hPGConn = poDS->GetPGConn();
     592             : 
     593      214230 :     if (bTableDefinitionValid >= 0)
     594      213778 :         return bTableDefinitionValid;
     595         452 :     bTableDefinitionValid = FALSE;
     596             : 
     597         452 :     poDS->EndCopy();
     598             : 
     599         904 :     auto oTemporaryUnsealer(poFeatureDefn->GetTemporaryUnsealer());
     600             : 
     601             :     /* -------------------------------------------------------------------- */
     602             :     /*      Get the OID of the table.                                       */
     603             :     /* -------------------------------------------------------------------- */
     604             : 
     605         904 :     CPLString osCommand;
     606             :     osCommand.Printf("SELECT c.oid FROM pg_class c "
     607             :                      "JOIN pg_namespace n ON c.relnamespace=n.oid "
     608             :                      "WHERE c.relname = %s AND n.nspname = %s",
     609         904 :                      OGRPGEscapeString(hPGConn, pszTableName).c_str(),
     610        1356 :                      OGRPGEscapeString(hPGConn, pszSchemaName).c_str());
     611         452 :     unsigned int nTableOID = 0;
     612             :     {
     613         452 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
     614         452 :         if (hResult && PGRES_TUPLES_OK == PQresultStatus(hResult))
     615             :         {
     616         452 :             if (PQntuples(hResult) == 1 && PQgetisnull(hResult, 0, 0) == false)
     617             :             {
     618         221 :                 nTableOID = static_cast<unsigned>(
     619         221 :                     CPLAtoGIntBig(PQgetvalue(hResult, 0, 0)));
     620         221 :                 OGRPGClearResult(hResult);
     621             :             }
     622             :             else
     623             :             {
     624         231 :                 CPLDebug("PG", "Could not retrieve table oid for %s",
     625             :                          pszTableName);
     626         231 :                 OGRPGClearResult(hResult);
     627         231 :                 return FALSE;
     628             :             }
     629             :         }
     630             :         else
     631             :         {
     632           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s",
     633             :                      PQerrorMessage(hPGConn));
     634           0 :             return FALSE;
     635             :         }
     636             :     }
     637             : 
     638             :     /* -------------------------------------------------------------------- */
     639             :     /*      Identify the integer primary key.                               */
     640             :     /* -------------------------------------------------------------------- */
     641             : 
     642             :     osCommand.Printf(
     643             :         "SELECT a.attname, a.attnum, t.typname, "
     644             :         "t.typname = ANY(ARRAY['int2','int4','int8','serial','bigserial']) AS "
     645             :         "isfid "
     646             :         "FROM pg_attribute a "
     647             :         "JOIN pg_type t ON t.oid = a.atttypid "
     648             :         "JOIN pg_index i ON i.indrelid = a.attrelid "
     649             :         "WHERE a.attnum > 0 AND a.attrelid = %u "
     650             :         "AND i.indisprimary = 't' "
     651             :         "AND t.typname !~ '^geom' "
     652             :         "AND a.attnum = ANY(i.indkey) ORDER BY a.attnum",
     653         221 :         nTableOID);
     654             : 
     655         221 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
     656             : 
     657         221 :     if (hResult && PGRES_TUPLES_OK == PQresultStatus(hResult))
     658             :     {
     659         221 :         if (PQntuples(hResult) == 1 && PQgetisnull(hResult, 0, 0) == false)
     660             :         {
     661             :             /* Check if single-field PK can be represented as integer. */
     662         344 :             CPLString osValue(PQgetvalue(hResult, 0, 3));
     663         172 :             if (osValue == "t")
     664             :             {
     665         172 :                 osPrimaryKey.Printf("%s", PQgetvalue(hResult, 0, 0));
     666         172 :                 const char *pszFIDType = PQgetvalue(hResult, 0, 2);
     667         172 :                 CPLDebug("PG", "Primary key name (FID): %s, type : %s",
     668             :                          osPrimaryKey.c_str(), pszFIDType);
     669         172 :                 if (EQUAL(pszFIDType, "int8"))
     670           2 :                     OGRLayer::SetMetadataItem(OLMD_FID64, "YES");
     671             :             }
     672             :         }
     673          49 :         else if (PQntuples(hResult) > 1)
     674             :         {
     675           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     676             :                      "Multi-column primary key in \'%s\' detected but not "
     677             :                      "supported.",
     678             :                      pszTableName);
     679             :         }
     680             : 
     681         221 :         OGRPGClearResult(hResult);
     682             :         /* Zero tuples means no PK is defined, perfectly valid case. */
     683             :     }
     684             :     else
     685             :     {
     686           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
     687             :     }
     688             : 
     689             :     /* -------------------------------------------------------------------- */
     690             :     /*      Fire off commands to get back the columns of the table.         */
     691             :     /* -------------------------------------------------------------------- */
     692             :     osCommand.Printf(
     693             :         "SELECT a.attname, t.typname, a.attlen,"
     694             :         "       format_type(a.atttypid,a.atttypmod), a.attnotnull, def.def, "
     695             :         "i.indisunique, descr.description%s "
     696             :         "FROM pg_attribute a "
     697             :         "JOIN pg_type t ON t.oid = a.atttypid "
     698             :         "LEFT JOIN "
     699             :         "(SELECT adrelid, adnum, pg_get_expr(adbin, adrelid) AS def FROM "
     700             :         "pg_attrdef) def "
     701             :         "ON def.adrelid = a.attrelid AND def.adnum = a.attnum "
     702             :         // Find unique constraints that are on a single column only
     703             :         "LEFT JOIN "
     704             :         "(SELECT DISTINCT indrelid, indkey, indisunique FROM pg_index WHERE "
     705             :         "indisunique) i "
     706             :         "ON i.indrelid = a.attrelid AND i.indkey[0] = a.attnum AND i.indkey[1] "
     707             :         "IS NULL "
     708             :         "LEFT JOIN pg_description descr "
     709             :         "ON descr.objoid = a.attrelid "
     710             :         "AND descr.classoid = 'pg_class'::regclass::oid "
     711             :         "AND descr.objsubid = a.attnum "
     712             :         "WHERE a.attnum > 0 AND a.attrelid = %u "
     713             :         "ORDER BY a.attnum",
     714         221 :         (poDS->sPostgreSQLVersion.nMajor >= 12 ? ", a.attgenerated" : ""),
     715         221 :         nTableOID);
     716             : 
     717         221 :     hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
     718             : 
     719         221 :     if (!hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK)
     720             :     {
     721           0 :         OGRPGClearResult(hResult);
     722             : 
     723           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
     724           0 :         return bTableDefinitionValid;
     725             :     }
     726             : 
     727         221 :     if (PQntuples(hResult) == 0)
     728             :     {
     729           0 :         OGRPGClearResult(hResult);
     730             : 
     731           0 :         CPLDebug("PG", "No field definitions found for '%s', is it a table?",
     732             :                  pszTableName);
     733           0 :         return bTableDefinitionValid;
     734             :     }
     735             : 
     736             :     /* -------------------------------------------------------------------- */
     737             :     /*      Parse the returned table information.                           */
     738             :     /* -------------------------------------------------------------------- */
     739        1368 :     for (int iRecord = 0; iRecord < PQntuples(hResult); iRecord++)
     740             :     {
     741        1147 :         OGRFieldDefn oField(PQgetvalue(hResult, iRecord, 0), OFTString);
     742             : 
     743        1147 :         const char *pszType = PQgetvalue(hResult, iRecord, 1);
     744        1147 :         int nWidth = atoi(PQgetvalue(hResult, iRecord, 2));
     745        1147 :         const char *pszFormatType = PQgetvalue(hResult, iRecord, 3);
     746        1147 :         const char *pszNotNull = PQgetvalue(hResult, iRecord, 4);
     747        1147 :         const char *pszDefault = PQgetisnull(hResult, iRecord, 5)
     748        1147 :                                      ? nullptr
     749         193 :                                      : PQgetvalue(hResult, iRecord, 5);
     750        1147 :         const char *pszIsUnique = PQgetvalue(hResult, iRecord, 6);
     751        1147 :         const char *pszDescription = PQgetvalue(hResult, iRecord, 7);
     752        1147 :         const char *pszGenerated = poDS->sPostgreSQLVersion.nMajor >= 12
     753        1147 :                                        ? PQgetvalue(hResult, iRecord, 8)
     754        1147 :                                        : "";
     755             : 
     756        1147 :         if (pszNotNull && EQUAL(pszNotNull, "t"))
     757         186 :             oField.SetNullable(FALSE);
     758        1147 :         if (pszIsUnique && EQUAL(pszIsUnique, "t"))
     759         188 :             oField.SetUnique(TRUE);
     760             : 
     761        1147 :         if (EQUAL(oField.GetNameRef(), osPrimaryKey))
     762             :         {
     763         192 :             pszFIDColumn = CPLStrdup(oField.GetNameRef());
     764         192 :             CPLDebug("PG", "Using column '%s' as FID for table '%s'",
     765             :                      pszFIDColumn, pszTableName);
     766         192 :             continue;
     767             :         }
     768        1770 :         else if (EQUAL(pszType, "geometry") || EQUAL(pszType, "geography") ||
     769         815 :                  EQUAL(oField.GetNameRef(), "WKB_GEOMETRY"))
     770             :         {
     771             :             const auto InitGeomField =
     772         528 :                 [this, &pszType, &oField](OGRPGGeomFieldDefn *poGeomFieldDefn)
     773             :             {
     774         195 :                 if (EQUAL(pszType, "geometry"))
     775         126 :                     poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOMETRY;
     776          69 :                 else if (EQUAL(pszType, "geography"))
     777             :                 {
     778           5 :                     poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOGRAPHY;
     779           5 :                     if (!(poDS->sPostGISVersion.nMajor >= 3 ||
     780           0 :                           (poDS->sPostGISVersion.nMajor == 2 &&
     781           0 :                            poDS->sPostGISVersion.nMinor >= 2)))
     782             :                     {
     783             :                         // EPSG:4326 was a requirement for geography before
     784             :                         // PostGIS 2.2
     785           0 :                         poGeomFieldDefn->nSRSId = 4326;
     786             :                     }
     787             :                 }
     788             :                 else
     789             :                 {
     790          64 :                     poGeomFieldDefn->ePostgisType = GEOM_TYPE_WKB;
     791          64 :                     if (EQUAL(pszType, "OID"))
     792           0 :                         bWkbAsOid = TRUE;
     793             :                 }
     794         195 :                 poGeomFieldDefn->SetNullable(oField.IsNullable());
     795         399 :             };
     796             : 
     797         204 :             if (!bGeometryInformationSet)
     798             :             {
     799         192 :                 if (pszGeomColForced == nullptr ||
     800          14 :                     EQUAL(pszGeomColForced, oField.GetNameRef()))
     801             :                 {
     802             :                     auto poGeomFieldDefn = std::make_unique<OGRPGGeomFieldDefn>(
     803         169 :                         this, oField.GetNameRef());
     804         169 :                     InitGeomField(poGeomFieldDefn.get());
     805         169 :                     poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
     806             :                 }
     807             :             }
     808             :             else
     809             :             {
     810          26 :                 int idx = poFeatureDefn->GetGeomFieldIndex(oField.GetNameRef());
     811          26 :                 if (idx >= 0)
     812             :                 {
     813          26 :                     auto poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(idx);
     814          26 :                     InitGeomField(poGeomFieldDefn);
     815             :                 }
     816             :             }
     817             : 
     818         204 :             continue;
     819             :         }
     820             : 
     821         751 :         OGRPGCommonLayerSetType(oField, pszType, pszFormatType, nWidth);
     822             : 
     823         751 :         if (pszDefault)
     824             :         {
     825          27 :             OGRPGCommonLayerNormalizeDefault(&oField, pszDefault);
     826             :         }
     827         751 :         if (pszDescription)
     828         751 :             oField.SetComment(pszDescription);
     829             : 
     830             :         // 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         223 : void OGRPGTableLayer::SetTableDefinition(const char *pszFIDColumnName,
     968             :                                          const char *pszGFldName,
     969             :                                          OGRwkbGeometryType eType,
     970             :                                          const char *pszGeomType, int nSRSId,
     971             :                                          int GeometryTypeFlags)
     972             : {
     973         223 :     bTableDefinitionValid = TRUE;
     974         223 :     bGeometryInformationSet = TRUE;
     975         223 :     pszFIDColumn = CPLStrdup(pszFIDColumnName);
     976         446 :     auto oTemporaryUnsealer(poFeatureDefn->GetTemporaryUnsealer());
     977         223 :     poFeatureDefn->SetGeomType(wkbNone);
     978         223 :     if (eType != wkbNone)
     979             :     {
     980             :         auto poGeomFieldDefn =
     981         194 :             std::make_unique<OGRPGGeomFieldDefn>(this, pszGFldName);
     982         194 :         poGeomFieldDefn->SetType(eType);
     983         194 :         poGeomFieldDefn->GeometryTypeFlags = GeometryTypeFlags;
     984             : 
     985         194 :         if (EQUAL(pszGeomType, "geometry"))
     986             :         {
     987         109 :             poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOMETRY;
     988         109 :             poGeomFieldDefn->nSRSId = nSRSId;
     989             :         }
     990          85 :         else if (EQUAL(pszGeomType, "geography"))
     991             :         {
     992           4 :             poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOGRAPHY;
     993           4 :             poGeomFieldDefn->nSRSId = nSRSId;
     994             :         }
     995             :         else
     996             :         {
     997          81 :             poGeomFieldDefn->ePostgisType = GEOM_TYPE_WKB;
     998          81 :             if (EQUAL(pszGeomType, "OID"))
     999           0 :                 bWkbAsOid = TRUE;
    1000             :         }
    1001         194 :         poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
    1002             :     }
    1003          29 :     else if (pszGFldName != nullptr)
    1004             :     {
    1005           2 :         m_osFirstGeometryFieldName = pszGFldName;
    1006             :     }
    1007         223 :     m_osLCOGeomType = pszGeomType;
    1008         223 : }
    1009             : 
    1010             : /************************************************************************/
    1011             : /*                          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         994 : void OGRPGTableLayer::BuildFullQueryStatement()
    1103             : 
    1104             : {
    1105         994 :     CPLString osFields = BuildFields();
    1106         994 :     if (pszQueryStatement != nullptr)
    1107             :     {
    1108         586 :         CPLFree(pszQueryStatement);
    1109         586 :         pszQueryStatement = nullptr;
    1110             :     }
    1111        1988 :     pszQueryStatement = static_cast<char *>(CPLMalloc(
    1112         994 :         osFields.size() + osWHERE.size() + strlen(pszSqlTableName) + 40));
    1113         994 :     snprintf(pszQueryStatement,
    1114         994 :              osFields.size() + osWHERE.size() + strlen(pszSqlTableName) + 40,
    1115             :              "SELECT %s FROM %s %s", osFields.c_str(), pszSqlTableName,
    1116             :              osWHERE.c_str());
    1117         994 : }
    1118             : 
    1119             : /************************************************************************/
    1120             : /*                            ResetReading()                            */
    1121             : /************************************************************************/
    1122             : 
    1123        1025 : void OGRPGTableLayer::ResetReading()
    1124             : 
    1125             : {
    1126        1025 :     if (bInResetReading)
    1127          31 :         return;
    1128         994 :     bInResetReading = TRUE;
    1129             : 
    1130         994 :     if (bDeferredCreation)
    1131          15 :         RunDeferredCreationIfNecessary();
    1132         994 :     poDS->EndCopy();
    1133         994 :     bUseCopyByDefault = FALSE;
    1134             : 
    1135         994 :     BuildFullQueryStatement();
    1136             : 
    1137         994 :     OGRPGLayer::ResetReading();
    1138             : 
    1139         994 :     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        1137 : CPLString OGRPGTableLayer::BuildFields()
    1197             : 
    1198             : {
    1199        1137 :     int i = 0;
    1200        1137 :     CPLString osFieldList;
    1201             : 
    1202        1137 :     poFeatureDefn->GetFieldCount();
    1203             : 
    1204        2231 :     if (pszFIDColumn != nullptr &&
    1205        1094 :         poFeatureDefn->GetFieldIndex(pszFIDColumn) == -1)
    1206             :     {
    1207        1090 :         osFieldList += OGRPGEscapeColumnName(pszFIDColumn);
    1208             :     }
    1209             : 
    1210        2184 :     for (i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    1211             :     {
    1212             :         OGRPGGeomFieldDefn *poGeomFieldDefn =
    1213        1047 :             poFeatureDefn->GetGeomFieldDefn(i);
    1214             :         CPLString osEscapedGeom =
    1215        2094 :             OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
    1216             : 
    1217        1047 :         if (!osFieldList.empty())
    1218        1020 :             osFieldList += ", ";
    1219             : 
    1220        1047 :         if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY)
    1221             :         {
    1222         624 :             if (!poDS->HavePostGIS() || poDS->bUseBinaryCursor)
    1223             :             {
    1224           8 :                 osFieldList += osEscapedGeom;
    1225             :             }
    1226         616 :             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         615 :                 osFieldList += osEscapedGeom;
    1238             :             }
    1239             :         }
    1240         423 :         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         408 :             osFieldList += osEscapedGeom;
    1269             :         }
    1270             :     }
    1271             : 
    1272        5331 :     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        1137 :     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       16778 : CPLString OGRPGEscapeColumnName(const char *pszColumnName)
    1807             : {
    1808       16778 :     CPLString osStr = "\"";
    1809             : 
    1810       16778 :     char ch = '\0';
    1811      209823 :     for (int i = 0; (ch = pszColumnName[i]) != '\0'; i++)
    1812             :     {
    1813      193045 :         if (ch == '"')
    1814           0 :             osStr.append(1, ch);
    1815      193045 :         osStr.append(1, ch);
    1816             :     }
    1817             : 
    1818       16778 :     osStr += "\"";
    1819             : 
    1820       16778 :     return osStr;
    1821             : }
    1822             : 
    1823             : /************************************************************************/
    1824             : /*                         OGRPGEscapeString( )                         */
    1825             : /************************************************************************/
    1826             : 
    1827        3647 : CPLString OGRPGEscapeString(void *hPGConnIn, const char *pszStrValue,
    1828             :                             int nMaxLength, const char *pszTableName,
    1829             :                             const char *pszFieldName)
    1830             : {
    1831        3647 :     PGconn *hPGConn = reinterpret_cast<PGconn *>(hPGConnIn);
    1832        3647 :     CPLString osCommand;
    1833             : 
    1834             :     /* We need to quote and escape string fields. */
    1835        3647 :     osCommand += "'";
    1836             : 
    1837        3647 :     int nSrcLen = static_cast<int>(strlen(pszStrValue));
    1838        3647 :     int nSrcLenUTF = CPLStrlenUTF8(pszStrValue);
    1839             : 
    1840        3647 :     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        3647 :     char *pszDestStr = static_cast<char *>(CPLMalloc(2 * nSrcLen + 1));
    1862             : 
    1863        3647 :     int nError = 0;
    1864        3647 :     PQescapeStringConn(hPGConn, pszDestStr, pszStrValue, nSrcLen, &nError);
    1865        3647 :     if (nError == 0)
    1866        3647 :         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        3647 :     CPLFree(pszDestStr);
    1875             : 
    1876        3647 :     osCommand += "'";
    1877             : 
    1878        7294 :     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 :             OGRPGEscapeColumnName(poFeatureDefn->GetFieldDefn(i)->GetNameRef());
    1947             :     }
    1948             : 
    1949         126 :     if (!bNeedComma)
    1950          13 :         bEmptyInsert = TRUE;
    1951             : 
    1952         126 :     osCommand += ") VALUES (";
    1953             : 
    1954             :     /* Set the geometry */
    1955         126 :     bNeedComma = FALSE;
    1956         237 :     for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    1957             :     {
    1958             :         const OGRPGGeomFieldDefn *poGeomFieldDefn =
    1959         111 :             poFeatureDefn->GetGeomFieldDefn(i);
    1960         111 :         OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
    1961         111 :         if (poGeom == nullptr)
    1962          28 :             continue;
    1963          83 :         if (bNeedComma)
    1964           0 :             osCommand += ", ";
    1965             :         else
    1966          83 :             bNeedComma = TRUE;
    1967             : 
    1968          83 :         if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ||
    1969          83 :             poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY)
    1970             :         {
    1971          54 :             CheckGeomTypeCompatibility(i, poGeom);
    1972             : 
    1973          54 :             poGeom->closeRings();
    1974          54 :             poGeom->set3D(poGeomFieldDefn->GeometryTypeFlags &
    1975          54 :                           OGRGeometry::OGR_G_3D);
    1976          54 :             poGeom->setMeasured(poGeomFieldDefn->GeometryTypeFlags &
    1977          54 :                                 OGRGeometry::OGR_G_MEASURED);
    1978             : 
    1979          54 :             int nSRSId = poGeomFieldDefn->nSRSId;
    1980             : 
    1981         108 :             char *pszHexEWKB = OGRGeometryToHexEWKB(
    1982          54 :                 poGeom, nSRSId, poDS->sPostGISVersion.nMajor,
    1983          54 :                 poDS->sPostGISVersion.nMinor);
    1984          54 :             if (!pszHexEWKB || pszHexEWKB[0] == 0)
    1985             :             {
    1986           0 :                 CPLFree(pszHexEWKB);
    1987           0 :                 return OGRERR_FAILURE;
    1988             :             }
    1989          54 :             osCommand += '\'';
    1990             :             try
    1991             :             {
    1992          54 :                 osCommand += pszHexEWKB;
    1993             :             }
    1994           0 :             catch (const std::bad_alloc &)
    1995             :             {
    1996           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory,
    1997             :                          "Out of memory: too large geometry");
    1998           0 :                 CPLFree(pszHexEWKB);
    1999           0 :                 return OGRERR_FAILURE;
    2000             :             }
    2001          54 :             osCommand += "'::";
    2002          54 :             if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY)
    2003           0 :                 osCommand += "GEOGRAPHY";
    2004             :             else
    2005          54 :                 osCommand += "GEOMETRY";
    2006          54 :             CPLFree(pszHexEWKB);
    2007             :         }
    2008          29 :         else if (!bWkbAsOid)
    2009             :         {
    2010             :             char *pszBytea =
    2011          58 :                 GeometryToBYTEA(poGeom, poDS->sPostGISVersion.nMajor,
    2012          29 :                                 poDS->sPostGISVersion.nMinor);
    2013             : 
    2014          29 :             if (!pszBytea)
    2015             :             {
    2016           0 :                 return OGRERR_FAILURE;
    2017             :             }
    2018          29 :             osCommand += "E'";
    2019             :             try
    2020             :             {
    2021          29 :                 osCommand += pszBytea;
    2022             :             }
    2023           0 :             catch (const std::bad_alloc &)
    2024             :             {
    2025           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory,
    2026             :                          "Out of memory: too large geometry");
    2027           0 :                 CPLFree(pszBytea);
    2028           0 :                 return OGRERR_FAILURE;
    2029             :             }
    2030          29 :             osCommand += '\'';
    2031          29 :             CPLFree(pszBytea);
    2032             :         }
    2033           0 :         else if (poGeomFieldDefn->ePostgisType ==
    2034             :                  GEOM_TYPE_WKB /* && bWkbAsOid */)
    2035             :         {
    2036           0 :             Oid oid = GeometryToOID(poGeom);
    2037             : 
    2038           0 :             if (oid != 0)
    2039             :             {
    2040           0 :                 osCommand += CPLString().Printf("'%d' ", oid);
    2041             :             }
    2042             :             else
    2043           0 :                 osCommand += "''";
    2044             :         }
    2045             :     }
    2046             : 
    2047         126 :     if (poFeature->GetFID() != OGRNullFID && pszFIDColumn != nullptr)
    2048             :     {
    2049           7 :         if (bNeedComma)
    2050           5 :             osCommand += ", ";
    2051           7 :         osCommand += CPLString().Printf(CPL_FRMT_GIB " ", poFeature->GetFID());
    2052           7 :         bNeedComma = TRUE;
    2053             :     }
    2054             : 
    2055         572 :     for (int i = 0; i < nFieldCount; i++)
    2056             :     {
    2057         446 :         if (iFIDAsRegularColumnIndex == i)
    2058           1 :             continue;
    2059         445 :         if (!poFeature->IsFieldSet(i))
    2060         185 :             continue;
    2061         260 :         if (m_abGeneratedColumns[i])
    2062           2 :             continue;
    2063             : 
    2064         258 :         if (bNeedComma)
    2065         230 :             osCommand += ", ";
    2066             :         else
    2067          28 :             bNeedComma = TRUE;
    2068             : 
    2069         258 :         OGRPGCommonAppendFieldValue(osCommand, poFeature, i, OGRPGEscapeString,
    2070             :                                     hPGConn);
    2071             :     }
    2072             : 
    2073         126 :     osCommand += ")";
    2074             : 
    2075         126 :     if (bEmptyInsert)
    2076          13 :         osCommand.Printf("INSERT INTO %s DEFAULT VALUES", pszSqlTableName);
    2077             : 
    2078         126 :     int bReturnRequested = FALSE;
    2079             :     /* We only get the FID, but we also could add the unset fields to get */
    2080             :     /* the default values */
    2081         245 :     if (bRetrieveFID && pszFIDColumn != nullptr &&
    2082         119 :         poFeature->GetFID() == OGRNullFID)
    2083             :     {
    2084         112 :         if (bSkipConflicts)
    2085             :         {
    2086           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    2087             :                      "fid retrieval and skipping conflicts are not supported "
    2088             :                      "at the same time.");
    2089           1 :             return OGRERR_FAILURE;
    2090             :         }
    2091         111 :         bReturnRequested = TRUE;
    2092         111 :         osCommand += " RETURNING ";
    2093         111 :         osCommand += OGRPGEscapeColumnName(pszFIDColumn);
    2094             :     }
    2095          14 :     else if (bSkipConflicts)
    2096           2 :         osCommand += " ON CONFLICT DO NOTHING";
    2097             : 
    2098             :     /* -------------------------------------------------------------------- */
    2099             :     /*      Execute the insert.                                             */
    2100             :     /* -------------------------------------------------------------------- */
    2101         125 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    2102         111 :     if (bReturnRequested && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
    2103         236 :         PQntuples(hResult) == 1 && PQnfields(hResult) == 1)
    2104             :     {
    2105         109 :         const char *pszFID = PQgetvalue(hResult, 0, 0);
    2106         109 :         poFeature->SetFID(CPLAtoGIntBig(pszFID));
    2107             :     }
    2108          16 :     else if (bReturnRequested || PQresultStatus(hResult) != PGRES_COMMAND_OK)
    2109             :     {
    2110           3 :         CPLError(CE_Failure, CPLE_AppDefined,
    2111             :                  "INSERT command for new feature failed.\n%s\nCommand: %s",
    2112           6 :                  PQerrorMessage(hPGConn), osCommand.substr(0, 1024).c_str());
    2113             : 
    2114           3 :         if (!bHasWarnedAlreadySetFID && poFeature->GetFID() != OGRNullFID &&
    2115           0 :             pszFIDColumn != nullptr)
    2116             :         {
    2117           0 :             bHasWarnedAlreadySetFID = TRUE;
    2118           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2119             :                      "You've inserted feature with an already set FID and "
    2120             :                      "that's perhaps the reason for the failure. "
    2121             :                      "If so, this can happen if you reuse the same feature "
    2122             :                      "object for sequential insertions. "
    2123             :                      "Indeed, since GDAL 1.8.0, the FID of an inserted feature "
    2124             :                      "is got from the server, so it is not a good idea"
    2125             :                      "to reuse it afterwards... All in all, try unsetting the "
    2126             :                      "FID with SetFID(-1) before calling CreateFeature()");
    2127             :         }
    2128             : 
    2129           3 :         OGRPGClearResult(hResult);
    2130             : 
    2131           3 :         return OGRERR_FAILURE;
    2132             :     }
    2133             : 
    2134         122 :     OGRPGClearResult(hResult);
    2135             : 
    2136         122 :     return OGRERR_NONE;
    2137             : }
    2138             : 
    2139             : /************************************************************************/
    2140             : /*                        CreateFeatureViaCopy()                        */
    2141             : /************************************************************************/
    2142             : 
    2143        3739 : OGRErr OGRPGTableLayer::CreateFeatureViaCopy(OGRFeature *poFeature)
    2144             : {
    2145        3739 :     PGconn *hPGConn = poDS->GetPGConn();
    2146        7478 :     CPLString osCommand;
    2147             : 
    2148             :     /* Tell the datasource we are now planning to copy data */
    2149        3739 :     poDS->StartCopy(this);
    2150             : 
    2151             :     /* First process geometry */
    2152        7454 :     for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    2153             :     {
    2154             :         const OGRPGGeomFieldDefn *poGeomFieldDefn =
    2155        3715 :             poFeatureDefn->GetGeomFieldDefn(i);
    2156        3715 :         OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
    2157             : 
    2158        3715 :         char *pszGeom = nullptr;
    2159        3715 :         if (nullptr != poGeom)
    2160             :         {
    2161        1648 :             CheckGeomTypeCompatibility(i, poGeom);
    2162             : 
    2163        1648 :             poGeom->closeRings();
    2164        1648 :             poGeom->set3D(poGeomFieldDefn->GeometryTypeFlags &
    2165        1648 :                           OGRGeometry::OGR_G_3D);
    2166        1648 :             poGeom->setMeasured(poGeomFieldDefn->GeometryTypeFlags &
    2167        1648 :                                 OGRGeometry::OGR_G_MEASURED);
    2168             : 
    2169        1648 :             if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_WKB)
    2170         789 :                 pszGeom = GeometryToBYTEA(poGeom, poDS->sPostGISVersion.nMajor,
    2171         789 :                                           poDS->sPostGISVersion.nMinor);
    2172             :             else
    2173         859 :                 pszGeom = OGRGeometryToHexEWKB(poGeom, poGeomFieldDefn->nSRSId,
    2174         859 :                                                poDS->sPostGISVersion.nMajor,
    2175         859 :                                                poDS->sPostGISVersion.nMinor);
    2176        1648 :             if (!pszGeom || pszGeom[0] == 0)
    2177             :             {
    2178           0 :                 CPLFree(pszGeom);
    2179           0 :                 return OGRERR_FAILURE;
    2180             :             }
    2181             :         }
    2182             : 
    2183        3715 :         if (!osCommand.empty())
    2184          12 :             osCommand += "\t";
    2185             : 
    2186        3715 :         if (pszGeom)
    2187             :         {
    2188             :             try
    2189             :             {
    2190        1648 :                 osCommand += pszGeom;
    2191             :             }
    2192           0 :             catch (const std::bad_alloc &)
    2193             :             {
    2194           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory,
    2195             :                          "Out of memory: too large geometry");
    2196           0 :                 CPLFree(pszGeom);
    2197           0 :                 return OGRERR_FAILURE;
    2198             :             }
    2199        1648 :             CPLFree(pszGeom);
    2200             :         }
    2201             :         else
    2202             :         {
    2203        2067 :             osCommand += "\\N";
    2204             :         }
    2205             :     }
    2206             : 
    2207        7478 :     std::vector<bool> abFieldsToInclude(m_abGeneratedColumns.size(), true);
    2208        8963 :     for (size_t i = 0; i < abFieldsToInclude.size(); i++)
    2209        5224 :         abFieldsToInclude[i] = !m_abGeneratedColumns[i];
    2210             : 
    2211        3739 :     if (bFIDColumnInCopyFields)
    2212             :     {
    2213          14 :         OGRPGCommonAppendCopyFID(osCommand, poFeature);
    2214             :     }
    2215        3739 :     OGRPGCommonAppendCopyRegularFields(osCommand, poFeature, pszFIDColumn,
    2216             :                                        abFieldsToInclude, OGRPGEscapeString,
    2217             :                                        hPGConn);
    2218             : 
    2219             :     /* Add end of line marker */
    2220        3739 :     osCommand += "\n";
    2221             : 
    2222             :     // PostgreSQL doesn't provide very helpful reporting of invalid UTF-8
    2223             :     // content in COPY mode.
    2224        7478 :     if (poDS->IsUTF8ClientEncoding() &&
    2225        3739 :         !CPLIsUTF8(osCommand.c_str(), static_cast<int>(osCommand.size())))
    2226             :     {
    2227           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2228             :                  "Non UTF-8 content found when writing feature " CPL_FRMT_GIB
    2229             :                  " of layer %s: %s",
    2230           0 :                  poFeature->GetFID(), poFeatureDefn->GetName(),
    2231             :                  osCommand.c_str());
    2232           0 :         return OGRERR_FAILURE;
    2233             :     }
    2234             : 
    2235             :     /* ------------------------------------------------------------ */
    2236             :     /*      Execute the copy.                                       */
    2237             :     /* ------------------------------------------------------------ */
    2238             : 
    2239        3739 :     OGRErr result = OGRERR_NONE;
    2240             : 
    2241        3739 :     int copyResult = PQputCopyData(hPGConn, osCommand.c_str(),
    2242        3739 :                                    static_cast<int>(osCommand.size()));
    2243             : #ifdef DEBUG_VERBOSE
    2244             :     CPLDebug("PG", "PQputCopyData(%s)", osCommand.c_str());
    2245             : #endif
    2246             : 
    2247        3739 :     switch (copyResult)
    2248             :     {
    2249           0 :         case 0:
    2250           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Writing COPY data blocked.");
    2251           0 :             result = OGRERR_FAILURE;
    2252           0 :             break;
    2253           0 :         case -1:
    2254           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s",
    2255             :                      PQerrorMessage(hPGConn));
    2256           0 :             result = OGRERR_FAILURE;
    2257           0 :             break;
    2258             :     }
    2259             : 
    2260        3739 :     return result;
    2261             : }
    2262             : 
    2263             : /************************************************************************/
    2264             : /*                           TestCapability()                           */
    2265             : /************************************************************************/
    2266             : 
    2267         807 : int OGRPGTableLayer::TestCapability(const char *pszCap)
    2268             : 
    2269             : {
    2270         807 :     if (bUpdateAccess)
    2271             :     {
    2272         802 :         if (EQUAL(pszCap, OLCSequentialWrite) ||
    2273         795 :             EQUAL(pszCap, OLCCreateField) ||
    2274         783 :             EQUAL(pszCap, OLCCreateGeomField) ||
    2275         782 :             EQUAL(pszCap, OLCDeleteField) || EQUAL(pszCap, OLCAlterFieldDefn) ||
    2276         758 :             EQUAL(pszCap, OLCAlterGeomFieldDefn) || EQUAL(pszCap, OLCRename))
    2277          53 :             return TRUE;
    2278             : 
    2279         749 :         else if (EQUAL(pszCap, OLCRandomWrite) ||
    2280         742 :                  EQUAL(pszCap, OLCUpdateFeature) ||
    2281         740 :                  EQUAL(pszCap, OLCDeleteFeature))
    2282             :         {
    2283          19 :             GetLayerDefn()->GetFieldCount();
    2284          19 :             return pszFIDColumn != nullptr;
    2285             :         }
    2286             :     }
    2287             : 
    2288         735 :     if (EQUAL(pszCap, OLCRandomRead))
    2289             :     {
    2290          12 :         GetLayerDefn()->GetFieldCount();
    2291          12 :         return pszFIDColumn != nullptr;
    2292             :     }
    2293             : 
    2294         723 :     else if (EQUAL(pszCap, OLCFastFeatureCount) ||
    2295         569 :              EQUAL(pszCap, OLCFastSetNextByIndex))
    2296             :     {
    2297         185 :         if (m_poFilterGeom == nullptr)
    2298         159 :             return TRUE;
    2299          26 :         OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
    2300          26 :         if (poFeatureDefn->GetGeomFieldCount() > 0)
    2301             :             poGeomFieldDefn =
    2302          26 :                 poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
    2303          52 :         return poGeomFieldDefn == nullptr ||
    2304          26 :                (poDS->sPostGISVersion.nMajor >= 0 &&
    2305          23 :                 (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ||
    2306          34 :                  poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY));
    2307             :     }
    2308             : 
    2309         538 :     else if (EQUAL(pszCap, OLCFastSpatialFilter))
    2310             :     {
    2311           2 :         OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
    2312           2 :         if (poFeatureDefn->GetGeomFieldCount() > 0)
    2313             :             poGeomFieldDefn =
    2314           2 :                 poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter);
    2315           4 :         return poGeomFieldDefn == nullptr ||
    2316           2 :                (poDS->sPostGISVersion.nMajor >= 0 &&
    2317           1 :                 (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ||
    2318           2 :                  poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY));
    2319             :     }
    2320             : 
    2321         536 :     else if (EQUAL(pszCap, OLCTransactions))
    2322           7 :         return TRUE;
    2323             : 
    2324         529 :     else if (EQUAL(pszCap, OLCFastGetExtent) ||
    2325         494 :              EQUAL(pszCap, OLCFastGetExtent3D))
    2326             :     {
    2327          37 :         OGRPGGeomFieldDefn *poGeomFieldDefn = nullptr;
    2328          37 :         if (poFeatureDefn->GetGeomFieldCount() > 0)
    2329          37 :             poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(0);
    2330          37 :         return poGeomFieldDefn != nullptr &&
    2331          70 :                poDS->sPostGISVersion.nMajor >= 0 &&
    2332          70 :                poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY;
    2333             :     }
    2334             : 
    2335         492 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
    2336           7 :         return TRUE;
    2337             : 
    2338         485 :     else if (EQUAL(pszCap, OLCCurveGeometries))
    2339         239 :         return TRUE;
    2340             : 
    2341         246 :     else if (EQUAL(pszCap, OLCMeasuredGeometries))
    2342         213 :         return TRUE;
    2343             : 
    2344          33 :     else if (EQUAL(pszCap, OLCZGeometries))
    2345          15 :         return TRUE;
    2346             : 
    2347             :     else
    2348          18 :         return FALSE;
    2349             : }
    2350             : 
    2351             : /************************************************************************/
    2352             : /*                            CreateField()                             */
    2353             : /************************************************************************/
    2354             : 
    2355         607 : OGRErr OGRPGTableLayer::CreateField(const OGRFieldDefn *poFieldIn,
    2356             :                                     int bApproxOK)
    2357             : 
    2358             : {
    2359         607 :     PGconn *hPGConn = poDS->GetPGConn();
    2360        1214 :     CPLString osCommand;
    2361        1214 :     CPLString osFieldType;
    2362        1214 :     OGRFieldDefn oField(poFieldIn);
    2363             : 
    2364         607 :     GetLayerDefn()->GetFieldCount();
    2365             : 
    2366         607 :     if (!bUpdateAccess)
    2367             :     {
    2368           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    2369             :                  "CreateField");
    2370           0 :         return OGRERR_FAILURE;
    2371             :     }
    2372             : 
    2373         609 :     if (pszFIDColumn != nullptr && EQUAL(oField.GetNameRef(), pszFIDColumn) &&
    2374        1216 :         oField.GetType() != OFTInteger && oField.GetType() != OFTInteger64)
    2375             :     {
    2376           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s",
    2377             :                  oField.GetNameRef());
    2378           1 :         return OGRERR_FAILURE;
    2379             :     }
    2380             : 
    2381             :     /* -------------------------------------------------------------------- */
    2382             :     /*      Do we want to "launder" the column names into Postgres          */
    2383             :     /*      friendly format?                                                */
    2384             :     /* -------------------------------------------------------------------- */
    2385         606 :     if (bLaunderColumnNames)
    2386             :     {
    2387             :         char *pszSafeName =
    2388         604 :             OGRPGCommonLaunderName(oField.GetNameRef(), "PG", m_bUTF8ToASCII);
    2389             : 
    2390         604 :         oField.SetName(pszSafeName);
    2391         604 :         CPLFree(pszSafeName);
    2392             : 
    2393         604 :         if (EQUAL(oField.GetNameRef(), "oid"))
    2394             :         {
    2395           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2396             :                      "Renaming field 'oid' to 'oid_' to avoid conflict with "
    2397             :                      "internal oid field.");
    2398           0 :             oField.SetName("oid_");
    2399             :         }
    2400             :     }
    2401             : 
    2402             :     const char *pszOverrideType =
    2403         606 :         CSLFetchNameValue(papszOverrideColumnTypes, oField.GetNameRef());
    2404         606 :     if (pszOverrideType != nullptr)
    2405           3 :         osFieldType = pszOverrideType;
    2406             :     else
    2407             :     {
    2408        1206 :         osFieldType = OGRPGCommonLayerGetType(
    2409        1206 :             oField, CPL_TO_BOOL(bPreservePrecision), CPL_TO_BOOL(bApproxOK));
    2410         603 :         if (osFieldType.empty())
    2411           0 :             return OGRERR_FAILURE;
    2412             :     }
    2413             : 
    2414        1212 :     CPLString osConstraints;
    2415         606 :     if (!oField.IsNullable())
    2416           1 :         osConstraints += " NOT NULL";
    2417         606 :     if (oField.IsUnique())
    2418           4 :         osConstraints += " UNIQUE";
    2419         606 :     if (oField.GetDefault() != nullptr && !oField.IsDefaultDriverSpecific())
    2420             :     {
    2421           8 :         osConstraints += " DEFAULT ";
    2422           8 :         osConstraints += OGRPGCommonLayerGetPGDefault(&oField);
    2423             :     }
    2424             : 
    2425        1212 :     std::string osCommentON;
    2426         606 :     if (!oField.GetComment().empty())
    2427             :     {
    2428           4 :         osCommentON = "COMMENT ON COLUMN ";
    2429           4 :         osCommentON += pszSqlTableName;
    2430           4 :         osCommentON += '.';
    2431           4 :         osCommentON += OGRPGEscapeColumnName(oField.GetNameRef());
    2432           4 :         osCommentON += " IS ";
    2433           4 :         osCommentON += OGRPGEscapeString(hPGConn, oField.GetComment().c_str());
    2434             :     }
    2435             : 
    2436             :     /* -------------------------------------------------------------------- */
    2437             :     /*      Create the new field.                                           */
    2438             :     /* -------------------------------------------------------------------- */
    2439         606 :     if (bDeferredCreation)
    2440             :     {
    2441         582 :         if (!(pszFIDColumn != nullptr &&
    2442         291 :               EQUAL(pszFIDColumn, oField.GetNameRef())))
    2443             :         {
    2444         290 :             osCreateTable += ", ";
    2445         290 :             osCreateTable += OGRPGEscapeColumnName(oField.GetNameRef());
    2446         290 :             osCreateTable += " ";
    2447         290 :             osCreateTable += osFieldType;
    2448         290 :             osCreateTable += osConstraints;
    2449             : 
    2450         290 :             if (!osCommentON.empty())
    2451           1 :                 m_aosDeferredCommentOnColumns.push_back(osCommentON);
    2452             :         }
    2453             :     }
    2454             :     else
    2455             :     {
    2456         315 :         poDS->EndCopy();
    2457             : 
    2458             :         osCommand.Printf("ALTER TABLE %s ADD COLUMN %s %s", pszSqlTableName,
    2459         630 :                          OGRPGEscapeColumnName(oField.GetNameRef()).c_str(),
    2460         630 :                          osFieldType.c_str());
    2461         315 :         osCommand += osConstraints;
    2462             : 
    2463         315 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    2464         315 :         if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    2465             :         {
    2466           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    2467             :                      PQerrorMessage(hPGConn));
    2468             : 
    2469           0 :             OGRPGClearResult(hResult);
    2470             : 
    2471           0 :             return OGRERR_FAILURE;
    2472             :         }
    2473             : 
    2474         315 :         OGRPGClearResult(hResult);
    2475             : 
    2476         315 :         if (!osCommentON.empty())
    2477             :         {
    2478           3 :             hResult = OGRPG_PQexec(hPGConn, osCommentON.c_str());
    2479           3 :             OGRPGClearResult(hResult);
    2480             :         }
    2481             :     }
    2482             : 
    2483         606 :     whileUnsealing(poFeatureDefn)->AddFieldDefn(&oField);
    2484         606 :     m_abGeneratedColumns.resize(poFeatureDefn->GetFieldCount());
    2485             : 
    2486         606 :     if (pszFIDColumn != nullptr && EQUAL(oField.GetNameRef(), pszFIDColumn))
    2487             :     {
    2488           1 :         iFIDAsRegularColumnIndex = poFeatureDefn->GetFieldCount() - 1;
    2489             :     }
    2490             : 
    2491         606 :     return OGRERR_NONE;
    2492             : }
    2493             : 
    2494             : /************************************************************************/
    2495             : /*                        RunAddGeometryColumn()                        */
    2496             : /************************************************************************/
    2497             : 
    2498             : OGRErr
    2499           8 : OGRPGTableLayer::RunAddGeometryColumn(const OGRPGGeomFieldDefn *poGeomField)
    2500             : {
    2501           8 :     PGconn *hPGConn = poDS->GetPGConn();
    2502             : 
    2503           8 :     const char *pszGeometryType = OGRToOGCGeomType(poGeomField->GetType());
    2504           8 :     const char *suffix = "";
    2505           8 :     int dim = 2;
    2506           8 :     if ((poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_3D) &&
    2507           4 :         (poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
    2508           2 :         dim = 4;
    2509           6 :     else if ((poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
    2510             :     {
    2511           1 :         if (!(wkbFlatten(poGeomField->GetType()) == wkbUnknown))
    2512           1 :             suffix = "M";
    2513           1 :         dim = 3;
    2514             :     }
    2515           5 :     else if (poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_3D)
    2516           2 :         dim = 3;
    2517             : 
    2518          16 :     CPLString osCommand;
    2519             :     osCommand.Printf(
    2520             :         "SELECT AddGeometryColumn(%s,%s,%s,%d,'%s%s',%d)",
    2521          16 :         OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
    2522          16 :         OGRPGEscapeString(hPGConn, pszTableName).c_str(),
    2523          16 :         OGRPGEscapeString(hPGConn, poGeomField->GetNameRef()).c_str(),
    2524          32 :         poGeomField->nSRSId, pszGeometryType, suffix, dim);
    2525             : 
    2526           8 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    2527             : 
    2528           8 :     if (!hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK)
    2529             :     {
    2530           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2531           0 :                  "AddGeometryColumn failed for layer %s.", GetName());
    2532             : 
    2533           0 :         OGRPGClearResult(hResult);
    2534             : 
    2535           0 :         return OGRERR_FAILURE;
    2536             :     }
    2537             : 
    2538           8 :     OGRPGClearResult(hResult);
    2539             : 
    2540           8 :     if (!poGeomField->IsNullable())
    2541             :     {
    2542             :         osCommand.Printf(
    2543             :             "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL", pszSqlTableName,
    2544           0 :             OGRPGEscapeColumnName(poGeomField->GetNameRef()).c_str());
    2545             : 
    2546           0 :         hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    2547           0 :         OGRPGClearResult(hResult);
    2548             :     }
    2549             : 
    2550           8 :     return OGRERR_NONE;
    2551             : }
    2552             : 
    2553             : /************************************************************************/
    2554             : /*                        RunCreateSpatialIndex()                       */
    2555             : /************************************************************************/
    2556             : 
    2557             : OGRErr
    2558         129 : OGRPGTableLayer::RunCreateSpatialIndex(const OGRPGGeomFieldDefn *poGeomField,
    2559             :                                        int nIdx)
    2560             : {
    2561         129 :     PGconn *hPGConn = poDS->GetPGConn();
    2562         258 :     CPLString osCommand;
    2563             : 
    2564             :     const std::string osIndexName(OGRPGCommonGenerateSpatialIndexName(
    2565         258 :         pszTableName, poGeomField->GetNameRef(), nIdx));
    2566             : 
    2567             :     osCommand.Printf("CREATE INDEX %s ON %s USING %s (%s)",
    2568         258 :                      OGRPGEscapeColumnName(osIndexName.c_str()).c_str(),
    2569             :                      pszSqlTableName, osSpatialIndexType.c_str(),
    2570         387 :                      OGRPGEscapeColumnName(poGeomField->GetNameRef()).c_str());
    2571             : 
    2572         129 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    2573             : 
    2574         129 :     if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
    2575             :     {
    2576           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2577           0 :                  "CREATE INDEX failed for layer %s.", GetName());
    2578             : 
    2579           0 :         OGRPGClearResult(hResult);
    2580             : 
    2581           0 :         return OGRERR_FAILURE;
    2582             :     }
    2583             : 
    2584         129 :     OGRPGClearResult(hResult);
    2585             : 
    2586         129 :     return OGRERR_NONE;
    2587             : }
    2588             : 
    2589             : /************************************************************************/
    2590             : /*                           CreateGeomField()                          */
    2591             : /************************************************************************/
    2592             : 
    2593          18 : OGRErr OGRPGTableLayer::CreateGeomField(const OGRGeomFieldDefn *poGeomFieldIn,
    2594             :                                         CPL_UNUSED int bApproxOK)
    2595             : {
    2596          18 :     OGRwkbGeometryType eType = poGeomFieldIn->GetType();
    2597          18 :     if (eType == wkbNone)
    2598             :     {
    2599           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2600             :                  "Cannot create geometry field of type wkbNone");
    2601           0 :         return OGRERR_FAILURE;
    2602             :     }
    2603             : 
    2604             :     // Check if GEOMETRY_NAME layer creation option was set, but no initial
    2605             :     // column was created in ICreateLayer()
    2606          18 :     CPLString osGeomFieldName = (m_osFirstGeometryFieldName.size())
    2607           2 :                                     ? m_osFirstGeometryFieldName
    2608          36 :                                     : CPLString(poGeomFieldIn->GetNameRef());
    2609          18 :     m_osFirstGeometryFieldName = "";  // reset for potential next geom columns
    2610             : 
    2611             :     auto poGeomField =
    2612          36 :         std::make_unique<OGRPGGeomFieldDefn>(this, osGeomFieldName);
    2613          18 :     if (EQUAL(poGeomField->GetNameRef(), ""))
    2614             :     {
    2615           0 :         if (poFeatureDefn->GetGeomFieldCount() == 0)
    2616           0 :             poGeomField->SetName(EQUAL(m_osLCOGeomType.c_str(), "geography")
    2617             :                                      ? "the_geog"
    2618             :                                      : "wkb_geometry");
    2619             :         else
    2620           0 :             poGeomField->SetName(CPLSPrintf(
    2621           0 :                 "wkb_geometry%d", poFeatureDefn->GetGeomFieldCount() + 1));
    2622             :     }
    2623          18 :     const auto poSRSIn = poGeomFieldIn->GetSpatialRef();
    2624          18 :     if (poSRSIn)
    2625             :     {
    2626           4 :         auto l_poSRS = poSRSIn->Clone();
    2627           4 :         l_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2628           4 :         poGeomField->SetSpatialRef(l_poSRS);
    2629           4 :         l_poSRS->Release();
    2630             :     }
    2631             :     /* -------------------------------------------------------------------- */
    2632             :     /*      Do we want to "launder" the column names into Postgres          */
    2633             :     /*      friendly format?                                                */
    2634             :     /* -------------------------------------------------------------------- */
    2635          18 :     if (bLaunderColumnNames)
    2636             :     {
    2637          18 :         char *pszSafeName = OGRPGCommonLaunderName(poGeomField->GetNameRef(),
    2638          18 :                                                    "PG", m_bUTF8ToASCII);
    2639             : 
    2640          18 :         poGeomField->SetName(pszSafeName);
    2641          18 :         CPLFree(pszSafeName);
    2642             :     }
    2643             : 
    2644          18 :     const OGRSpatialReference *poSRS = poGeomField->GetSpatialRef();
    2645          18 :     int nSRSId = poDS->GetUndefinedSRID();
    2646          18 :     if (nForcedSRSId != UNDETERMINED_SRID)
    2647           0 :         nSRSId = nForcedSRSId;
    2648          18 :     else if (poSRS != nullptr)
    2649           4 :         nSRSId = poDS->FetchSRSId(poSRS);
    2650             : 
    2651          18 :     int GeometryTypeFlags = 0;
    2652          18 :     if (OGR_GT_HasZ(eType))
    2653           4 :         GeometryTypeFlags |= OGRGeometry::OGR_G_3D;
    2654          18 :     if (OGR_GT_HasM(eType))
    2655           2 :         GeometryTypeFlags |= OGRGeometry::OGR_G_MEASURED;
    2656          18 :     if (nForcedGeometryTypeFlags >= 0)
    2657             :     {
    2658           2 :         GeometryTypeFlags = nForcedGeometryTypeFlags;
    2659             :         eType =
    2660           2 :             OGR_GT_SetModifier(eType, GeometryTypeFlags & OGRGeometry::OGR_G_3D,
    2661             :                                GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED);
    2662             :     }
    2663          18 :     poGeomField->SetType(eType);
    2664          18 :     poGeomField->SetNullable(poGeomFieldIn->IsNullable());
    2665          18 :     poGeomField->nSRSId = nSRSId;
    2666          18 :     poGeomField->GeometryTypeFlags = GeometryTypeFlags;
    2667          36 :     poGeomField->ePostgisType = EQUAL(m_osLCOGeomType.c_str(), "geography")
    2668          18 :                                     ? GEOM_TYPE_GEOGRAPHY
    2669             :                                     : GEOM_TYPE_GEOMETRY;
    2670             : 
    2671             :     /* -------------------------------------------------------------------- */
    2672             :     /*      Create the new field.                                           */
    2673             :     /* -------------------------------------------------------------------- */
    2674          18 :     if (!bDeferredCreation)
    2675             :     {
    2676           8 :         poDS->EndCopy();
    2677             : 
    2678           8 :         if (RunAddGeometryColumn(poGeomField.get()) != OGRERR_NONE)
    2679             :         {
    2680           0 :             return OGRERR_FAILURE;
    2681             :         }
    2682             : 
    2683           8 :         if (bCreateSpatialIndexFlag)
    2684             :         {
    2685           8 :             if (RunCreateSpatialIndex(poGeomField.get(), 0) != OGRERR_NONE)
    2686             :             {
    2687           0 :                 return OGRERR_FAILURE;
    2688             :             }
    2689             :         }
    2690             :     }
    2691             : 
    2692          18 :     whileUnsealing(poFeatureDefn)->AddGeomFieldDefn(std::move(poGeomField));
    2693             : 
    2694          18 :     return OGRERR_NONE;
    2695             : }
    2696             : 
    2697             : /************************************************************************/
    2698             : /*                            DeleteField()                             */
    2699             : /************************************************************************/
    2700             : 
    2701           4 : OGRErr OGRPGTableLayer::DeleteField(int iField)
    2702             : {
    2703           4 :     PGconn *hPGConn = poDS->GetPGConn();
    2704           8 :     CPLString osCommand;
    2705             : 
    2706           4 :     GetLayerDefn()->GetFieldCount();
    2707             : 
    2708           4 :     if (!bUpdateAccess)
    2709             :     {
    2710           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    2711             :                  "DeleteField");
    2712           0 :         return OGRERR_FAILURE;
    2713             :     }
    2714             : 
    2715           4 :     if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
    2716             :     {
    2717           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    2718           0 :         return OGRERR_FAILURE;
    2719             :     }
    2720             : 
    2721           4 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    2722           0 :         return OGRERR_FAILURE;
    2723           4 :     poDS->EndCopy();
    2724             : 
    2725             :     osCommand.Printf(
    2726             :         "ALTER TABLE %s DROP COLUMN %s", pszSqlTableName,
    2727           8 :         OGRPGEscapeColumnName(poFeatureDefn->GetFieldDefn(iField)->GetNameRef())
    2728           4 :             .c_str());
    2729           4 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    2730           4 :     if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    2731             :     {
    2732           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    2733             :                  PQerrorMessage(hPGConn));
    2734             : 
    2735           0 :         OGRPGClearResult(hResult);
    2736             : 
    2737           0 :         return OGRERR_FAILURE;
    2738             :     }
    2739             : 
    2740           4 :     OGRPGClearResult(hResult);
    2741             : 
    2742           4 :     m_abGeneratedColumns.erase(m_abGeneratedColumns.begin() + iField);
    2743             : 
    2744           4 :     return whileUnsealing(poFeatureDefn)->DeleteFieldDefn(iField);
    2745             : }
    2746             : 
    2747             : /************************************************************************/
    2748             : /*                           AlterFieldDefn()                           */
    2749             : /************************************************************************/
    2750             : 
    2751          20 : OGRErr OGRPGTableLayer::AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
    2752             :                                        int nFlagsIn)
    2753             : {
    2754          20 :     PGconn *hPGConn = poDS->GetPGConn();
    2755          40 :     CPLString osCommand;
    2756             : 
    2757          20 :     GetLayerDefn()->GetFieldCount();
    2758             : 
    2759          20 :     if (!bUpdateAccess)
    2760             :     {
    2761           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    2762             :                  "AlterFieldDefn");
    2763           0 :         return OGRERR_FAILURE;
    2764             :     }
    2765             : 
    2766          20 :     if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
    2767             :     {
    2768           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    2769           0 :         return OGRERR_FAILURE;
    2770             :     }
    2771             : 
    2772          20 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    2773           0 :         return OGRERR_FAILURE;
    2774          20 :     poDS->EndCopy();
    2775             : 
    2776          20 :     OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    2777          40 :     auto oTemporaryUnsealer(poFieldDefn->GetTemporaryUnsealer());
    2778          40 :     OGRFieldDefn oField(poNewFieldDefn);
    2779             : 
    2780          20 :     poDS->SoftStartTransaction();
    2781             : 
    2782          20 :     if (!(nFlagsIn & ALTER_TYPE_FLAG))
    2783             :     {
    2784          14 :         oField.SetSubType(OFSTNone);
    2785          14 :         oField.SetType(poFieldDefn->GetType());
    2786          14 :         oField.SetSubType(poFieldDefn->GetSubType());
    2787             :     }
    2788             : 
    2789          20 :     if (!(nFlagsIn & ALTER_WIDTH_PRECISION_FLAG))
    2790             :     {
    2791          14 :         oField.SetWidth(poFieldDefn->GetWidth());
    2792          14 :         oField.SetPrecision(poFieldDefn->GetPrecision());
    2793             :     }
    2794             : 
    2795          20 :     if ((nFlagsIn & ALTER_TYPE_FLAG) || (nFlagsIn & ALTER_WIDTH_PRECISION_FLAG))
    2796             :     {
    2797             :         CPLString osFieldType = OGRPGCommonLayerGetType(
    2798           6 :             oField, CPL_TO_BOOL(bPreservePrecision), true);
    2799           6 :         if (osFieldType.empty())
    2800             :         {
    2801           0 :             poDS->SoftRollbackTransaction();
    2802             : 
    2803           0 :             return OGRERR_FAILURE;
    2804             :         }
    2805             : 
    2806             :         osCommand.Printf(
    2807             :             "ALTER TABLE %s ALTER COLUMN %s TYPE %s", pszSqlTableName,
    2808          12 :             OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
    2809          12 :             osFieldType.c_str());
    2810             : 
    2811           6 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    2812           6 :         if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    2813             :         {
    2814           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    2815             :                      PQerrorMessage(hPGConn));
    2816             : 
    2817           0 :             OGRPGClearResult(hResult);
    2818             : 
    2819           0 :             poDS->SoftRollbackTransaction();
    2820             : 
    2821           0 :             return OGRERR_FAILURE;
    2822             :         }
    2823           6 :         OGRPGClearResult(hResult);
    2824             :     }
    2825             : 
    2826          26 :     if ((nFlagsIn & ALTER_NULLABLE_FLAG) &&
    2827           6 :         poFieldDefn->IsNullable() != poNewFieldDefn->IsNullable())
    2828             :     {
    2829           2 :         oField.SetNullable(poNewFieldDefn->IsNullable());
    2830             : 
    2831           2 :         if (poNewFieldDefn->IsNullable())
    2832             :             osCommand.Printf(
    2833             :                 "ALTER TABLE %s ALTER COLUMN %s DROP NOT NULL", pszSqlTableName,
    2834           1 :                 OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
    2835             :         else
    2836             :             osCommand.Printf(
    2837             :                 "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL", pszSqlTableName,
    2838           1 :                 OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
    2839             : 
    2840           2 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    2841           2 :         if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    2842             :         {
    2843           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    2844             :                      PQerrorMessage(hPGConn));
    2845             : 
    2846           0 :             OGRPGClearResult(hResult);
    2847             : 
    2848           0 :             poDS->SoftRollbackTransaction();
    2849             : 
    2850           0 :             return OGRERR_FAILURE;
    2851             :         }
    2852           2 :         OGRPGClearResult(hResult);
    2853             :     }
    2854             : 
    2855             :     // Only supports adding a unique constraint
    2856          30 :     if ((nFlagsIn & ALTER_UNIQUE_FLAG) && !poFieldDefn->IsUnique() &&
    2857          10 :         poNewFieldDefn->IsUnique())
    2858             :     {
    2859           2 :         oField.SetUnique(poNewFieldDefn->IsUnique());
    2860             : 
    2861             :         osCommand.Printf(
    2862             :             "ALTER TABLE %s ADD UNIQUE (%s)", pszSqlTableName,
    2863           2 :             OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
    2864             : 
    2865           2 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    2866           2 :         if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    2867             :         {
    2868           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    2869             :                      PQerrorMessage(hPGConn));
    2870             : 
    2871           0 :             OGRPGClearResult(hResult);
    2872             : 
    2873           0 :             poDS->SoftRollbackTransaction();
    2874             : 
    2875           0 :             return OGRERR_FAILURE;
    2876             :         }
    2877           2 :         OGRPGClearResult(hResult);
    2878             :     }
    2879          22 :     else if ((nFlagsIn & ALTER_UNIQUE_FLAG) && poFieldDefn->IsUnique() &&
    2880           4 :              !poNewFieldDefn->IsUnique())
    2881             :     {
    2882           2 :         oField.SetUnique(TRUE);
    2883           2 :         CPLError(CE_Warning, CPLE_NotSupported,
    2884             :                  "Dropping a UNIQUE constraint is not supported currently");
    2885             :     }
    2886             : 
    2887          28 :     if ((nFlagsIn & ALTER_DEFAULT_FLAG) &&
    2888           8 :         ((poFieldDefn->GetDefault() == nullptr &&
    2889           6 :           poNewFieldDefn->GetDefault() != nullptr) ||
    2890           8 :          (poFieldDefn->GetDefault() != nullptr &&
    2891           2 :           poNewFieldDefn->GetDefault() == nullptr) ||
    2892           7 :          (poFieldDefn->GetDefault() != nullptr &&
    2893           1 :           poNewFieldDefn->GetDefault() != nullptr &&
    2894           1 :           strcmp(poFieldDefn->GetDefault(), poNewFieldDefn->GetDefault()) !=
    2895             :               0)))
    2896             :     {
    2897           2 :         oField.SetDefault(poNewFieldDefn->GetDefault());
    2898             : 
    2899           2 :         if (poNewFieldDefn->GetDefault() == nullptr)
    2900             :             osCommand.Printf(
    2901             :                 "ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT", pszSqlTableName,
    2902           1 :                 OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
    2903             :         else
    2904             :             osCommand.Printf(
    2905             :                 "ALTER TABLE %s ALTER COLUMN %s SET DEFAULT %s",
    2906             :                 pszSqlTableName,
    2907           2 :                 OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
    2908           3 :                 OGRPGCommonLayerGetPGDefault(poNewFieldDefn).c_str());
    2909             : 
    2910           2 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    2911           2 :         if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    2912             :         {
    2913           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    2914             :                      PQerrorMessage(hPGConn));
    2915             : 
    2916           0 :             OGRPGClearResult(hResult);
    2917             : 
    2918           0 :             poDS->SoftRollbackTransaction();
    2919             : 
    2920           0 :             return OGRERR_FAILURE;
    2921             :         }
    2922           2 :         OGRPGClearResult(hResult);
    2923             :     }
    2924             : 
    2925          30 :     if ((nFlagsIn & ALTER_COMMENT_FLAG) &&
    2926          10 :         poFieldDefn->GetComment() != poNewFieldDefn->GetComment())
    2927             :     {
    2928           6 :         oField.SetComment(poNewFieldDefn->GetComment());
    2929             : 
    2930           6 :         if (!poNewFieldDefn->GetComment().empty())
    2931             :         {
    2932             :             osCommand.Printf(
    2933             :                 "COMMENT ON COLUMN %s.%s IS %s", pszSqlTableName,
    2934           8 :                 OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
    2935           8 :                 OGRPGEscapeString(hPGConn, poNewFieldDefn->GetComment().c_str())
    2936           8 :                     .c_str());
    2937             :         }
    2938             :         else
    2939             :         {
    2940             :             osCommand.Printf(
    2941             :                 "COMMENT ON COLUMN %s.%s IS NULL", pszSqlTableName,
    2942           2 :                 OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str());
    2943             :         }
    2944             : 
    2945           6 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    2946           6 :         if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    2947             :         {
    2948           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    2949             :                      PQerrorMessage(hPGConn));
    2950             : 
    2951           0 :             OGRPGClearResult(hResult);
    2952             : 
    2953           0 :             poDS->SoftRollbackTransaction();
    2954             : 
    2955           0 :             return OGRERR_FAILURE;
    2956             :         }
    2957           6 :         OGRPGClearResult(hResult);
    2958             :     }
    2959             : 
    2960          20 :     if ((nFlagsIn & ALTER_NAME_FLAG))
    2961             :     {
    2962           6 :         if (bLaunderColumnNames)
    2963             :         {
    2964           6 :             char *pszSafeName = OGRPGCommonLaunderName(oField.GetNameRef(),
    2965           6 :                                                        "PG", m_bUTF8ToASCII);
    2966           6 :             oField.SetName(pszSafeName);
    2967           6 :             CPLFree(pszSafeName);
    2968             :         }
    2969             : 
    2970           6 :         if (EQUAL(oField.GetNameRef(), "oid"))
    2971             :         {
    2972           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2973             :                      "Renaming field 'oid' to 'oid_' to avoid conflict with "
    2974             :                      "internal oid field.");
    2975           0 :             oField.SetName("oid_");
    2976             :         }
    2977             : 
    2978           6 :         if (strcmp(poFieldDefn->GetNameRef(), oField.GetNameRef()) != 0)
    2979             :         {
    2980             :             osCommand.Printf(
    2981             :                 "ALTER TABLE %s RENAME COLUMN %s TO %s", pszSqlTableName,
    2982          12 :                 OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(),
    2983          18 :                 OGRPGEscapeColumnName(oField.GetNameRef()).c_str());
    2984           6 :             PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    2985           6 :             if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    2986             :             {
    2987           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s",
    2988             :                          osCommand.c_str(), PQerrorMessage(hPGConn));
    2989             : 
    2990           0 :                 OGRPGClearResult(hResult);
    2991             : 
    2992           0 :                 poDS->SoftRollbackTransaction();
    2993             : 
    2994           0 :                 return OGRERR_FAILURE;
    2995             :             }
    2996           6 :             OGRPGClearResult(hResult);
    2997             :         }
    2998             :     }
    2999             : 
    3000          20 :     poDS->SoftCommitTransaction();
    3001             : 
    3002          20 :     if (nFlagsIn & ALTER_NAME_FLAG)
    3003           6 :         poFieldDefn->SetName(oField.GetNameRef());
    3004          20 :     if (nFlagsIn & ALTER_TYPE_FLAG)
    3005             :     {
    3006           6 :         poFieldDefn->SetSubType(OFSTNone);
    3007           6 :         poFieldDefn->SetType(oField.GetType());
    3008           6 :         poFieldDefn->SetSubType(oField.GetSubType());
    3009             :     }
    3010          20 :     if (nFlagsIn & ALTER_WIDTH_PRECISION_FLAG)
    3011             :     {
    3012           6 :         poFieldDefn->SetWidth(oField.GetWidth());
    3013           6 :         poFieldDefn->SetPrecision(oField.GetPrecision());
    3014             :     }
    3015          20 :     if (nFlagsIn & ALTER_NULLABLE_FLAG)
    3016           6 :         poFieldDefn->SetNullable(oField.IsNullable());
    3017          20 :     if (nFlagsIn & ALTER_DEFAULT_FLAG)
    3018           8 :         poFieldDefn->SetDefault(oField.GetDefault());
    3019          20 :     if (nFlagsIn & ALTER_UNIQUE_FLAG)
    3020          14 :         poFieldDefn->SetUnique(oField.IsUnique());
    3021          20 :     if (nFlagsIn & ALTER_COMMENT_FLAG)
    3022          10 :         poFieldDefn->SetComment(oField.GetComment());
    3023             : 
    3024          20 :     return OGRERR_NONE;
    3025             : }
    3026             : 
    3027             : /************************************************************************/
    3028             : /*                         AlterGeomFieldDefn()                         */
    3029             : /************************************************************************/
    3030             : 
    3031             : OGRErr
    3032           6 : OGRPGTableLayer::AlterGeomFieldDefn(int iGeomFieldToAlter,
    3033             :                                     const OGRGeomFieldDefn *poNewGeomFieldDefn,
    3034             :                                     int nFlagsIn)
    3035             : {
    3036           6 :     PGconn *hPGConn = poDS->GetPGConn();
    3037          12 :     CPLString osCommand;
    3038             : 
    3039           6 :     if (!bUpdateAccess)
    3040             :     {
    3041           0 :         CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
    3042             :                  "AlterGeomFieldDefn");
    3043           0 :         return OGRERR_FAILURE;
    3044             :     }
    3045             : 
    3046          11 :     if (iGeomFieldToAlter < 0 ||
    3047           5 :         iGeomFieldToAlter >= GetLayerDefn()->GetGeomFieldCount())
    3048             :     {
    3049           1 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    3050           1 :         return OGRERR_FAILURE;
    3051             :     }
    3052             : 
    3053           5 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3054           0 :         return OGRERR_FAILURE;
    3055           5 :     poDS->EndCopy();
    3056             : 
    3057           5 :     auto poGeomFieldDefn = cpl::down_cast<OGRPGGeomFieldDefn *>(
    3058           5 :         poFeatureDefn->GetGeomFieldDefn(iGeomFieldToAlter));
    3059          10 :     auto oTemporaryUnsealer(poGeomFieldDefn->GetTemporaryUnsealer());
    3060             : 
    3061           5 :     if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG)
    3062             :     {
    3063           5 :         const auto poNewSRSRef = poNewGeomFieldDefn->GetSpatialRef();
    3064           5 :         if (poNewSRSRef && poNewSRSRef->GetCoordinateEpoch() > 0)
    3065             :         {
    3066           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    3067             :                      "Setting a coordinate epoch is not supported for "
    3068             :                      "PostGIS");
    3069           1 :             return OGRERR_FAILURE;
    3070             :         }
    3071             :     }
    3072             : 
    3073           8 :     const OGRGeomFieldDefn oGeomField(poNewGeomFieldDefn);
    3074           4 :     poDS->SoftStartTransaction();
    3075             : 
    3076           4 :     int nGeomTypeFlags = poGeomFieldDefn->GeometryTypeFlags;
    3077             : 
    3078           8 :     if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_TYPE_FLAG) &&
    3079           4 :         poGeomFieldDefn->GetType() != poNewGeomFieldDefn->GetType())
    3080             :     {
    3081             :         const char *pszGeometryType =
    3082           2 :             OGRToOGCGeomType(poNewGeomFieldDefn->GetType());
    3083           2 :         std::string osType;
    3084           2 :         if (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY)
    3085           2 :             osType += "geometry(";
    3086             :         else
    3087           0 :             osType += "geography(";
    3088           2 :         osType += pszGeometryType;
    3089           2 :         nGeomTypeFlags = 0;
    3090           2 :         if (OGR_GT_HasZ(poNewGeomFieldDefn->GetType()))
    3091           0 :             nGeomTypeFlags |= OGRGeometry::OGR_G_3D;
    3092           2 :         if (OGR_GT_HasM(poNewGeomFieldDefn->GetType()))
    3093           0 :             nGeomTypeFlags |= OGRGeometry::OGR_G_MEASURED;
    3094           2 :         if (nGeomTypeFlags & OGRGeometry::OGR_G_3D)
    3095           0 :             osType += "Z";
    3096           2 :         else if (nGeomTypeFlags & OGRGeometry::OGR_G_MEASURED)
    3097           0 :             osType += "M";
    3098           2 :         if (poGeomFieldDefn->nSRSId > 0)
    3099           2 :             osType += CPLSPrintf(",%d", poGeomFieldDefn->nSRSId);
    3100           2 :         osType += ")";
    3101             : 
    3102             :         osCommand.Printf(
    3103             :             "ALTER TABLE %s ALTER COLUMN %s TYPE %s", pszSqlTableName,
    3104           4 :             OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(),
    3105           4 :             osType.c_str());
    3106             : 
    3107           2 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    3108           2 :         if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    3109             :         {
    3110           1 :             CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    3111             :                      PQerrorMessage(hPGConn));
    3112             : 
    3113           1 :             OGRPGClearResult(hResult);
    3114             : 
    3115           1 :             poDS->SoftRollbackTransaction();
    3116             : 
    3117           1 :             return OGRERR_FAILURE;
    3118             :         }
    3119           1 :         OGRPGClearResult(hResult);
    3120             :     }
    3121             : 
    3122           3 :     const auto poOldSRS = poGeomFieldDefn->GetSpatialRef();
    3123           3 :     int nSRID = poGeomFieldDefn->nSRSId;
    3124             : 
    3125           3 :     if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG))
    3126             :     {
    3127           3 :         const auto poNewSRS = poNewGeomFieldDefn->GetSpatialRef();
    3128           3 :         const char *const apszOptions[] = {
    3129             :             "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES", nullptr};
    3130           3 :         if ((poOldSRS == nullptr && poNewSRS != nullptr) ||
    3131           7 :             (poOldSRS != nullptr && poNewSRS == nullptr) ||
    3132           1 :             (poOldSRS != nullptr && poNewSRS != nullptr &&
    3133           1 :              !poOldSRS->IsSame(poNewSRS, apszOptions)))
    3134             :         {
    3135           3 :             if (poNewSRS)
    3136           2 :                 nSRID = poDS->FetchSRSId(poNewSRS);
    3137             :             else
    3138           1 :                 nSRID = 0;
    3139             : 
    3140             :             osCommand.Printf(
    3141             :                 "SELECT UpdateGeometrySRID(%s,%s,%s,%d)",
    3142           6 :                 OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
    3143           6 :                 OGRPGEscapeString(hPGConn, pszTableName).c_str(),
    3144           6 :                 OGRPGEscapeString(hPGConn, poGeomFieldDefn->GetNameRef())
    3145             :                     .c_str(),
    3146           9 :                 nSRID);
    3147             : 
    3148           3 :             PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    3149           3 :             if (PQresultStatus(hResult) != PGRES_TUPLES_OK)
    3150             :             {
    3151           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s",
    3152             :                          osCommand.c_str(), PQerrorMessage(hPGConn));
    3153             : 
    3154           0 :                 OGRPGClearResult(hResult);
    3155             : 
    3156           0 :                 poDS->SoftRollbackTransaction();
    3157             : 
    3158           0 :                 return OGRERR_FAILURE;
    3159             :             }
    3160           3 :             OGRPGClearResult(hResult);
    3161             :         }
    3162             :     }
    3163             : 
    3164           6 :     if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_NULLABLE_FLAG) &&
    3165           3 :         poGeomFieldDefn->IsNullable() != poNewGeomFieldDefn->IsNullable())
    3166             :     {
    3167           2 :         if (poNewGeomFieldDefn->IsNullable())
    3168             :             osCommand.Printf(
    3169             :                 "ALTER TABLE %s ALTER COLUMN %s DROP NOT NULL", pszSqlTableName,
    3170           1 :                 OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str());
    3171             :         else
    3172             :             osCommand.Printf(
    3173             :                 "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL", pszSqlTableName,
    3174           1 :                 OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str());
    3175             : 
    3176           2 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    3177           2 :         if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    3178             :         {
    3179           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    3180             :                      PQerrorMessage(hPGConn));
    3181             : 
    3182           0 :             OGRPGClearResult(hResult);
    3183             : 
    3184           0 :             poDS->SoftRollbackTransaction();
    3185             : 
    3186           0 :             return OGRERR_FAILURE;
    3187             :         }
    3188           2 :         OGRPGClearResult(hResult);
    3189             :     }
    3190             : 
    3191           6 :     if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_NAME_FLAG) &&
    3192           3 :         strcmp(poGeomFieldDefn->GetNameRef(),
    3193             :                poNewGeomFieldDefn->GetNameRef()) != 0)
    3194             :     {
    3195             :         osCommand.Printf(
    3196             :             "ALTER TABLE %s RENAME COLUMN %s TO %s", pszSqlTableName,
    3197           2 :             OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(),
    3198           3 :             OGRPGEscapeColumnName(oGeomField.GetNameRef()).c_str());
    3199           1 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    3200           1 :         if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    3201             :         {
    3202           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    3203             :                      PQerrorMessage(hPGConn));
    3204             : 
    3205           0 :             OGRPGClearResult(hResult);
    3206             : 
    3207           0 :             poDS->SoftRollbackTransaction();
    3208             : 
    3209           0 :             return OGRERR_FAILURE;
    3210             :         }
    3211           1 :         OGRPGClearResult(hResult);
    3212             :     }
    3213             : 
    3214           3 :     poDS->SoftCommitTransaction();
    3215             : 
    3216           3 :     if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NAME_FLAG)
    3217           3 :         poGeomFieldDefn->SetName(oGeomField.GetNameRef());
    3218           3 :     if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_TYPE_FLAG)
    3219             :     {
    3220           3 :         poGeomFieldDefn->GeometryTypeFlags = nGeomTypeFlags;
    3221           3 :         poGeomFieldDefn->SetType(oGeomField.GetType());
    3222             :     }
    3223           3 :     if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NULLABLE_FLAG)
    3224           3 :         poGeomFieldDefn->SetNullable(oGeomField.IsNullable());
    3225           3 :     if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG)
    3226             :     {
    3227           3 :         const auto poSRSRef = oGeomField.GetSpatialRef();
    3228           3 :         if (poSRSRef)
    3229             :         {
    3230           2 :             auto poSRSNew = poSRSRef->Clone();
    3231           2 :             poGeomFieldDefn->SetSpatialRef(poSRSNew);
    3232           2 :             poSRSNew->Release();
    3233             :         }
    3234             :         else
    3235             :         {
    3236           1 :             poGeomFieldDefn->SetSpatialRef(nullptr);
    3237             :         }
    3238           3 :         poGeomFieldDefn->nSRSId = nSRID;
    3239             :     }
    3240             : 
    3241           3 :     return OGRERR_NONE;
    3242             : }
    3243             : 
    3244             : /************************************************************************/
    3245             : /*                             GetFeature()                             */
    3246             : /************************************************************************/
    3247             : 
    3248         146 : OGRFeature *OGRPGTableLayer::GetFeature(GIntBig nFeatureId)
    3249             : 
    3250             : {
    3251         146 :     GetLayerDefn()->GetFieldCount();
    3252             : 
    3253         146 :     if (pszFIDColumn == nullptr)
    3254           3 :         return OGRLayer::GetFeature(nFeatureId);
    3255             : 
    3256             :     /* -------------------------------------------------------------------- */
    3257             :     /*      Issue query for a single record.                                */
    3258             :     /* -------------------------------------------------------------------- */
    3259         143 :     OGRFeature *poFeature = nullptr;
    3260         143 :     PGconn *hPGConn = poDS->GetPGConn();
    3261         286 :     CPLString osFieldList = BuildFields();
    3262         143 :     CPLString osCommand;
    3263             : 
    3264         143 :     poDS->EndCopy();
    3265         143 :     poDS->SoftStartTransaction();
    3266             : 
    3267             :     osCommand.Printf("DECLARE getfeaturecursor %s for "
    3268             :                      "SELECT %s FROM %s WHERE %s = " CPL_FRMT_GIB,
    3269         143 :                      (poDS->bUseBinaryCursor) ? "BINARY CURSOR" : "CURSOR",
    3270             :                      osFieldList.c_str(), pszSqlTableName,
    3271         143 :                      OGRPGEscapeColumnName(pszFIDColumn).c_str(), nFeatureId);
    3272             : 
    3273         143 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    3274             : 
    3275         143 :     if (hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK)
    3276             :     {
    3277         143 :         OGRPGClearResult(hResult);
    3278             : 
    3279         143 :         hResult = OGRPG_PQexec(hPGConn, "FETCH ALL in getfeaturecursor");
    3280             : 
    3281         143 :         if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK)
    3282             :         {
    3283         143 :             int nRows = PQntuples(hResult);
    3284         143 :             if (nRows > 0)
    3285             :             {
    3286         121 :                 int *panTempMapFieldNameToIndex = nullptr;
    3287         121 :                 int *panTempMapFieldNameToGeomIndex = nullptr;
    3288         121 :                 CreateMapFromFieldNameToIndex(hResult, poFeatureDefn,
    3289             :                                               panTempMapFieldNameToIndex,
    3290             :                                               panTempMapFieldNameToGeomIndex);
    3291         121 :                 poFeature = RecordToFeature(hResult, panTempMapFieldNameToIndex,
    3292             :                                             panTempMapFieldNameToGeomIndex, 0);
    3293         121 :                 CPLFree(panTempMapFieldNameToIndex);
    3294         121 :                 CPLFree(panTempMapFieldNameToGeomIndex);
    3295         121 :                 if (poFeature && iFIDAsRegularColumnIndex >= 0)
    3296             :                 {
    3297           1 :                     poFeature->SetField(iFIDAsRegularColumnIndex,
    3298             :                                         poFeature->GetFID());
    3299             :                 }
    3300             : 
    3301         121 :                 if (nRows > 1)
    3302             :                 {
    3303           0 :                     CPLError(
    3304             :                         CE_Warning, CPLE_AppDefined,
    3305             :                         "%d rows in response to the WHERE %s = " CPL_FRMT_GIB
    3306             :                         " clause !",
    3307             :                         nRows, pszFIDColumn, nFeatureId);
    3308             :                 }
    3309             :             }
    3310             :             else
    3311             :             {
    3312          22 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3313             :                          "Attempt to read feature with unknown feature id "
    3314             :                          "(" CPL_FRMT_GIB ").",
    3315             :                          nFeatureId);
    3316             :             }
    3317             :         }
    3318             :     }
    3319           0 :     else if (hResult && PQresultStatus(hResult) == PGRES_FATAL_ERROR)
    3320             :     {
    3321           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
    3322             :                  PQresultErrorMessage(hResult));
    3323             :     }
    3324             : 
    3325             :     /* -------------------------------------------------------------------- */
    3326             :     /*      Cleanup                                                         */
    3327             :     /* -------------------------------------------------------------------- */
    3328         143 :     OGRPGClearResult(hResult);
    3329             : 
    3330         143 :     hResult = OGRPG_PQexec(hPGConn, "CLOSE getfeaturecursor");
    3331         143 :     OGRPGClearResult(hResult);
    3332             : 
    3333         143 :     poDS->SoftCommitTransaction();
    3334             : 
    3335         143 :     return poFeature;
    3336             : }
    3337             : 
    3338             : /************************************************************************/
    3339             : /*                          GetFeatureCount()                           */
    3340             : /************************************************************************/
    3341             : 
    3342         152 : GIntBig OGRPGTableLayer::GetFeatureCount(int bForce)
    3343             : 
    3344             : {
    3345         152 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3346           0 :         return 0;
    3347         152 :     poDS->EndCopy();
    3348             : 
    3349         152 :     if (TestCapability(OLCFastFeatureCount) == FALSE)
    3350          11 :         return OGRPGLayer::GetFeatureCount(bForce);
    3351             : 
    3352             :     /* -------------------------------------------------------------------- */
    3353             :     /*      In theory it might be wise to cache this result, but it         */
    3354             :     /*      won't be trivial to work out the lifetime of the value.         */
    3355             :     /*      After all someone else could be adding records from another     */
    3356             :     /*      application when working against a database.                    */
    3357             :     /* -------------------------------------------------------------------- */
    3358         141 :     PGconn *hPGConn = poDS->GetPGConn();
    3359         141 :     CPLString osCommand;
    3360         141 :     GIntBig nCount = 0;
    3361             : 
    3362             :     osCommand.Printf("SELECT count(*) FROM %s %s", pszSqlTableName,
    3363         141 :                      osWHERE.c_str());
    3364             : 
    3365         141 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    3366         141 :     if (hResult != nullptr && PQresultStatus(hResult) == PGRES_TUPLES_OK)
    3367         141 :         nCount = CPLAtoGIntBig(PQgetvalue(hResult, 0, 0));
    3368             :     else
    3369           0 :         CPLDebug("PG", "%s; failed.", osCommand.c_str());
    3370         141 :     OGRPGClearResult(hResult);
    3371             : 
    3372         141 :     return nCount;
    3373             : }
    3374             : 
    3375             : /************************************************************************/
    3376             : /*                             ResolveSRID()                            */
    3377             : /************************************************************************/
    3378             : 
    3379          83 : void OGRPGTableLayer::ResolveSRID(const OGRPGGeomFieldDefn *poGFldDefn)
    3380             : 
    3381             : {
    3382          83 :     PGconn *hPGConn = poDS->GetPGConn();
    3383          83 :     CPLString osCommand;
    3384             : 
    3385          83 :     int nSRSId = poDS->GetUndefinedSRID();
    3386          83 :     if (!poDS->m_bHasGeometryColumns)
    3387             :     {
    3388           0 :         poGFldDefn->nSRSId = nSRSId;
    3389           0 :         return;
    3390             :     }
    3391             : 
    3392             :     osCommand.Printf(
    3393             :         "SELECT srid FROM geometry_columns "
    3394             :         "WHERE f_table_name = %s AND "
    3395             :         "f_geometry_column = %s",
    3396         166 :         OGRPGEscapeString(hPGConn, pszTableName).c_str(),
    3397         249 :         OGRPGEscapeString(hPGConn, poGFldDefn->GetNameRef()).c_str());
    3398             : 
    3399             :     osCommand +=
    3400          83 :         CPLString().Printf(" AND f_table_schema = %s",
    3401          83 :                            OGRPGEscapeString(hPGConn, pszSchemaName).c_str());
    3402             : 
    3403          83 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    3404             : 
    3405         166 :     if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
    3406          83 :         PQntuples(hResult) == 1)
    3407             :     {
    3408          39 :         nSRSId = atoi(PQgetvalue(hResult, 0, 0));
    3409             :     }
    3410             : 
    3411          83 :     OGRPGClearResult(hResult);
    3412             : 
    3413             :     /* With PostGIS 2.0, SRID = 0 can also mean that there's no constraint */
    3414             :     /* so we need to fetch from values */
    3415             :     /* We assume that all geometry of this column have identical SRID */
    3416          83 :     if (nSRSId <= 0 && poGFldDefn->ePostgisType == GEOM_TYPE_GEOMETRY &&
    3417          39 :         poDS->sPostGISVersion.nMajor >= 0)
    3418             :     {
    3419          72 :         CPLString osGetSRID;
    3420          36 :         osGetSRID += "SELECT ST_SRID(";
    3421          36 :         osGetSRID += OGRPGEscapeColumnName(poGFldDefn->GetNameRef());
    3422          36 :         osGetSRID += ") FROM ";
    3423          36 :         osGetSRID += pszSqlTableName;
    3424          36 :         osGetSRID += " WHERE (";
    3425          36 :         osGetSRID += OGRPGEscapeColumnName(poGFldDefn->GetNameRef());
    3426          36 :         osGetSRID += " IS NOT NULL) LIMIT 1";
    3427             : 
    3428          36 :         hResult = OGRPG_PQexec(poDS->GetPGConn(), osGetSRID);
    3429          72 :         if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK &&
    3430          36 :             PQntuples(hResult) == 1)
    3431             :         {
    3432          34 :             nSRSId = atoi(PQgetvalue(hResult, 0, 0));
    3433             :         }
    3434             : 
    3435          36 :         OGRPGClearResult(hResult);
    3436             :     }
    3437             : 
    3438          83 :     poGFldDefn->nSRSId = nSRSId;
    3439             : }
    3440             : 
    3441             : /************************************************************************/
    3442             : /*                             StartCopy()                              */
    3443             : /************************************************************************/
    3444             : 
    3445         185 : OGRErr OGRPGTableLayer::StartCopy()
    3446             : 
    3447             : {
    3448             :     /*CPLDebug("PG", "OGRPGDataSource(%p)::StartCopy(%p)", poDS, this);*/
    3449             : 
    3450         185 :     CPLString osFields = BuildCopyFields();
    3451             : 
    3452         185 :     size_t size = osFields.size() + strlen(pszSqlTableName) + 100;
    3453         185 :     char *pszCommand = static_cast<char *>(CPLMalloc(size));
    3454             : 
    3455         185 :     snprintf(pszCommand, size, "COPY %s (%s) FROM STDIN;", pszSqlTableName,
    3456             :              osFields.c_str());
    3457             : 
    3458         185 :     PGconn *hPGConn = poDS->GetPGConn();
    3459         185 :     PGresult *hResult = OGRPG_PQexec(hPGConn, pszCommand);
    3460             : 
    3461         185 :     if (!hResult || (PQresultStatus(hResult) != PGRES_COPY_IN))
    3462             :     {
    3463           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
    3464             :     }
    3465             :     else
    3466         185 :         bCopyActive = TRUE;
    3467             : 
    3468         185 :     OGRPGClearResult(hResult);
    3469         185 :     CPLFree(pszCommand);
    3470             : 
    3471         370 :     return OGRERR_NONE;
    3472             : }
    3473             : 
    3474             : /************************************************************************/
    3475             : /*                              EndCopy()                               */
    3476             : /************************************************************************/
    3477             : 
    3478         185 : OGRErr OGRPGTableLayer::EndCopy()
    3479             : 
    3480             : {
    3481         185 :     if (!bCopyActive)
    3482           0 :         return OGRERR_NONE;
    3483             :     /*CPLDebug("PG", "OGRPGDataSource(%p)::EndCopy(%p)", poDS, this);*/
    3484             : 
    3485             :     /* This method is called from the datasource when
    3486             :        a COPY operation is ended */
    3487         185 :     OGRErr result = OGRERR_NONE;
    3488             : 
    3489         185 :     PGconn *hPGConn = poDS->GetPGConn();
    3490         185 :     CPLDebug("PG", "PQputCopyEnd()");
    3491             : 
    3492         185 :     bCopyActive = FALSE;
    3493             : 
    3494         185 :     int copyResult = PQputCopyEnd(hPGConn, nullptr);
    3495             : 
    3496         185 :     switch (copyResult)
    3497             :     {
    3498           0 :         case 0:
    3499           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Writing COPY data blocked.");
    3500           0 :             result = OGRERR_FAILURE;
    3501           0 :             break;
    3502           0 :         case -1:
    3503           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s",
    3504             :                      PQerrorMessage(hPGConn));
    3505           0 :             result = OGRERR_FAILURE;
    3506           0 :             break;
    3507             :     }
    3508             : 
    3509             :     /* Now check the results of the copy */
    3510         185 :     PGresult *hResult = PQgetResult(hPGConn);
    3511             : 
    3512         185 :     if (hResult && PQresultStatus(hResult) != PGRES_COMMAND_OK)
    3513             :     {
    3514           1 :         CPLError(CE_Failure, CPLE_AppDefined, "COPY statement failed.\n%s",
    3515             :                  PQerrorMessage(hPGConn));
    3516             : 
    3517           1 :         result = OGRERR_FAILURE;
    3518             :     }
    3519             : 
    3520         185 :     OGRPGClearResult(hResult);
    3521             : 
    3522         185 :     if (!bUseCopyByDefault)
    3523          34 :         bUseCopy = USE_COPY_UNSET;
    3524             : 
    3525         185 :     UpdateSequenceIfNeeded();
    3526             : 
    3527         185 :     return result;
    3528             : }
    3529             : 
    3530             : /************************************************************************/
    3531             : /*                       UpdateSequenceIfNeeded()                       */
    3532             : /************************************************************************/
    3533             : 
    3534        1035 : void OGRPGTableLayer::UpdateSequenceIfNeeded()
    3535             : {
    3536        1035 :     if (bNeedToUpdateSequence && pszFIDColumn != nullptr)
    3537             :     {
    3538          18 :         PGconn *hPGConn = poDS->GetPGConn();
    3539          18 :         CPLString osCommand;
    3540             :         // setval() only works if the value is in [1,INT_MAX] range
    3541             :         // so do not update it if MAX(fid) <= 0
    3542             :         osCommand.Printf(
    3543             :             "SELECT setval(pg_get_serial_sequence(%s, %s), MAX(%s)) FROM %s "
    3544             :             "WHERE EXISTS (SELECT 1 FROM %s WHERE %s > 0 LIMIT 1)",
    3545          36 :             OGRPGEscapeString(hPGConn, pszSqlTableName).c_str(),
    3546          36 :             OGRPGEscapeString(hPGConn, pszFIDColumn).c_str(),
    3547          18 :             OGRPGEscapeColumnName(pszFIDColumn).c_str(), pszSqlTableName,
    3548          72 :             pszSqlTableName, OGRPGEscapeColumnName(pszFIDColumn).c_str());
    3549          18 :         PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    3550          18 :         OGRPGClearResult(hResult);
    3551          18 :         bNeedToUpdateSequence = false;
    3552             :     }
    3553        1035 : }
    3554             : 
    3555             : /************************************************************************/
    3556             : /*                          BuildCopyFields()                           */
    3557             : /************************************************************************/
    3558             : 
    3559         185 : CPLString OGRPGTableLayer::BuildCopyFields()
    3560             : {
    3561         185 :     int i = 0;
    3562         185 :     int nFIDIndex = -1;
    3563         185 :     CPLString osFieldList;
    3564             : 
    3565         354 :     for (i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    3566             :     {
    3567         169 :         OGRGeomFieldDefn *poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(i);
    3568         169 :         if (!osFieldList.empty())
    3569           6 :             osFieldList += ", ";
    3570         169 :         osFieldList += OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
    3571             :     }
    3572             : 
    3573         185 :     if (bFIDColumnInCopyFields)
    3574             :     {
    3575          11 :         if (!osFieldList.empty())
    3576           7 :             osFieldList += ", ";
    3577             : 
    3578          11 :         nFIDIndex = poFeatureDefn->GetFieldIndex(pszFIDColumn);
    3579             : 
    3580          11 :         osFieldList += OGRPGEscapeColumnName(pszFIDColumn);
    3581             :     }
    3582             : 
    3583         708 :     for (i = 0; i < poFeatureDefn->GetFieldCount(); i++)
    3584             :     {
    3585         523 :         if (i == nFIDIndex)
    3586           2 :             continue;
    3587         521 :         if (m_abGeneratedColumns[i])
    3588           2 :             continue;
    3589             : 
    3590         519 :         const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
    3591             : 
    3592         519 :         if (!osFieldList.empty())
    3593         501 :             osFieldList += ", ";
    3594             : 
    3595         519 :         osFieldList += OGRPGEscapeColumnName(pszName);
    3596             :     }
    3597             : 
    3598         185 :     return osFieldList;
    3599             : }
    3600             : 
    3601             : /************************************************************************/
    3602             : /*                    CheckGeomTypeCompatibility()                      */
    3603             : /************************************************************************/
    3604             : 
    3605        1702 : void OGRPGTableLayer::CheckGeomTypeCompatibility(int iGeomField,
    3606             :                                                  OGRGeometry *poGeom)
    3607             : {
    3608        1702 :     if (bHasWarnedIncompatibleGeom)
    3609           0 :         return;
    3610             : 
    3611             :     OGRwkbGeometryType eExpectedGeomType =
    3612        1702 :         poFeatureDefn->GetGeomFieldDefn(iGeomField)->GetType();
    3613        1702 :     OGRwkbGeometryType eFlatLayerGeomType = wkbFlatten(eExpectedGeomType);
    3614        1702 :     OGRwkbGeometryType eFlatGeomType = wkbFlatten(poGeom->getGeometryType());
    3615        1702 :     if (eFlatLayerGeomType == wkbUnknown)
    3616         676 :         return;
    3617             : 
    3618        1026 :     if (eFlatLayerGeomType == wkbGeometryCollection)
    3619           0 :         bHasWarnedIncompatibleGeom = eFlatGeomType != wkbMultiPoint &&
    3620           0 :                                      eFlatGeomType != wkbMultiLineString &&
    3621           0 :                                      eFlatGeomType != wkbMultiPolygon &&
    3622             :                                      eFlatGeomType != wkbGeometryCollection;
    3623             :     else
    3624        1026 :         bHasWarnedIncompatibleGeom = (eFlatGeomType != eFlatLayerGeomType);
    3625             : 
    3626        1026 :     if (bHasWarnedIncompatibleGeom)
    3627             :     {
    3628           2 :         CPLError(CE_Warning, CPLE_AppDefined,
    3629             :                  "Geometry to be inserted is of type %s, whereas the layer "
    3630             :                  "geometry type is %s.\n"
    3631             :                  "Insertion is likely to fail",
    3632           1 :                  OGRGeometryTypeToName(poGeom->getGeometryType()),
    3633             :                  OGRGeometryTypeToName(eExpectedGeomType));
    3634             :     }
    3635             : }
    3636             : 
    3637             : /************************************************************************/
    3638             : /*                        SetOverrideColumnTypes()                      */
    3639             : /************************************************************************/
    3640             : 
    3641         223 : void OGRPGTableLayer::SetOverrideColumnTypes(const char *pszOverrideColumnTypes)
    3642             : {
    3643         223 :     if (pszOverrideColumnTypes == nullptr)
    3644         222 :         return;
    3645             : 
    3646           1 :     const char *pszIter = pszOverrideColumnTypes;
    3647           2 :     CPLString osCur;
    3648          44 :     while (*pszIter != '\0')
    3649             :     {
    3650          44 :         if (*pszIter == '(')
    3651             :         {
    3652             :             /* Ignore commas inside ( ) pair */
    3653          12 :             while (*pszIter != '\0')
    3654             :             {
    3655          12 :                 if (*pszIter == ')')
    3656             :                 {
    3657           2 :                     osCur += *pszIter;
    3658           2 :                     pszIter++;
    3659           2 :                     break;
    3660             :                 }
    3661          10 :                 osCur += *pszIter;
    3662          10 :                 pszIter++;
    3663             :             }
    3664           2 :             if (*pszIter == '\0')
    3665           1 :                 break;
    3666             :         }
    3667             : 
    3668          43 :         if (*pszIter == ',')
    3669             :         {
    3670           3 :             papszOverrideColumnTypes =
    3671           3 :                 CSLAddString(papszOverrideColumnTypes, osCur);
    3672           3 :             osCur = "";
    3673             :         }
    3674             :         else
    3675          40 :             osCur += *pszIter;
    3676          43 :         pszIter++;
    3677             :     }
    3678           1 :     if (!osCur.empty())
    3679           1 :         papszOverrideColumnTypes =
    3680           1 :             CSLAddString(papszOverrideColumnTypes, osCur);
    3681             : }
    3682             : 
    3683             : /************************************************************************/
    3684             : /*                             GetExtent()                              */
    3685             : /*                                                                      */
    3686             : /*      For PostGIS use internal ST_EstimatedExtent(geometry) function  */
    3687             : /*      if bForce == 0                                                  */
    3688             : /************************************************************************/
    3689             : 
    3690          32 : OGRErr OGRPGTableLayer::GetExtent(int iGeomField, OGREnvelope *psExtent,
    3691             :                                   int bForce)
    3692             : {
    3693          64 :     CPLString osCommand;
    3694             : 
    3695          54 :     if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ||
    3696          22 :         GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone)
    3697             :     {
    3698          10 :         if (iGeomField != 0)
    3699             :         {
    3700          10 :             CPLError(CE_Failure, CPLE_AppDefined,
    3701             :                      "Invalid geometry field index : %d", iGeomField);
    3702             :         }
    3703          10 :         return OGRERR_FAILURE;
    3704             :     }
    3705             : 
    3706          22 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3707           0 :         return OGRERR_FAILURE;
    3708          22 :     poDS->EndCopy();
    3709             : 
    3710             :     OGRPGGeomFieldDefn *poGeomFieldDefn =
    3711          22 :         poFeatureDefn->GetGeomFieldDefn(iGeomField);
    3712             : 
    3713             :     // if bForce is 0 and ePostgisType is not GEOM_TYPE_GEOGRAPHY we can use
    3714             :     // the ST_EstimatedExtent function which is quicker
    3715             :     // ST_EstimatedExtent was called ST_Estimated_Extent up to PostGIS 2.0.x
    3716             :     // ST_EstimatedExtent returns NULL in absence of statistics (an exception
    3717             :     // before
    3718             :     //   PostGIS 1.5.4)
    3719          22 :     if (bForce == 0 && TestCapability(OLCFastGetExtent))
    3720             :     {
    3721           1 :         PGconn *hPGConn = poDS->GetPGConn();
    3722             : 
    3723           2 :         const char *pszExtentFct = poDS->sPostGISVersion.nMajor > 2 ||
    3724           0 :                                            (poDS->sPostGISVersion.nMajor == 2 &&
    3725           0 :                                             poDS->sPostGISVersion.nMinor >= 1)
    3726           1 :                                        ? "ST_EstimatedExtent"
    3727             :                                        : "ST_Estimated_Extent";
    3728             : 
    3729             :         osCommand.Printf(
    3730             :             "SELECT %s(%s, %s, %s)", pszExtentFct,
    3731           2 :             OGRPGEscapeString(hPGConn, pszSchemaName).c_str(),
    3732           2 :             OGRPGEscapeString(hPGConn, pszTableName).c_str(),
    3733           4 :             OGRPGEscapeString(hPGConn, poGeomFieldDefn->GetNameRef()).c_str());
    3734             : 
    3735             :         /* Quiet error: ST_Estimated_Extent may return an error if statistics */
    3736             :         /* have not been computed */
    3737           1 :         if (RunGetExtentRequest(*psExtent, bForce, osCommand, TRUE) ==
    3738             :             OGRERR_NONE)
    3739           1 :             return OGRERR_NONE;
    3740             : 
    3741           0 :         CPLDebug(
    3742             :             "PG",
    3743             :             "Unable to get estimated extent by PostGIS. Trying real extent.");
    3744             :     }
    3745             : 
    3746          21 :     return OGRPGLayer::GetExtent(iGeomField, psExtent, bForce);
    3747             : }
    3748             : 
    3749             : /************************************************************************/
    3750             : /*                           Rename()                                   */
    3751             : /************************************************************************/
    3752             : 
    3753           6 : OGRErr OGRPGTableLayer::Rename(const char *pszNewName)
    3754             : {
    3755           6 :     if (!TestCapability(OLCRename))
    3756           0 :         return OGRERR_FAILURE;
    3757             : 
    3758           6 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3759           0 :         return OGRERR_FAILURE;
    3760           6 :     poDS->EndCopy();
    3761           6 :     ResetReading();
    3762             : 
    3763           6 :     char *pszNewSqlTableName = CPLStrdup(OGRPGEscapeColumnName(pszNewName));
    3764           6 :     PGconn *hPGConn = poDS->GetPGConn();
    3765           6 :     CPLString osCommand;
    3766             :     osCommand.Printf("ALTER TABLE %s RENAME TO %s", pszSqlTableName,
    3767           6 :                      pszNewSqlTableName);
    3768           6 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand);
    3769             : 
    3770           6 :     OGRErr eRet = OGRERR_NONE;
    3771           6 :     if (!hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK)
    3772             :     {
    3773           2 :         eRet = OGRERR_FAILURE;
    3774           2 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn));
    3775             : 
    3776           2 :         CPLFree(pszNewSqlTableName);
    3777             :     }
    3778             :     else
    3779             :     {
    3780           4 :         CPLFree(pszTableName);
    3781           4 :         pszTableName = CPLStrdup(pszNewName);
    3782             : 
    3783           4 :         CPLFree(pszSqlTableName);
    3784           4 :         pszSqlTableName = pszNewSqlTableName;
    3785             : 
    3786           4 :         SetDescription(pszNewName);
    3787           4 :         whileUnsealing(poFeatureDefn)->SetName(pszNewName);
    3788             :     }
    3789             : 
    3790           6 :     OGRPGClearResult(hResult);
    3791             : 
    3792           6 :     return eRet;
    3793             : }
    3794             : 
    3795             : /************************************************************************/
    3796             : /*                        SetDeferredCreation()                         */
    3797             : /************************************************************************/
    3798             : 
    3799         223 : void OGRPGTableLayer::SetDeferredCreation(int bDeferredCreationIn,
    3800             :                                           const std::string &osCreateTableIn)
    3801             : {
    3802         223 :     bDeferredCreation = bDeferredCreationIn;
    3803         223 :     osCreateTable = osCreateTableIn;
    3804         223 : }
    3805             : 
    3806             : /************************************************************************/
    3807             : /*                      RunDeferredCreationIfNecessary()                */
    3808             : /************************************************************************/
    3809             : 
    3810        1525 : OGRErr OGRPGTableLayer::RunDeferredCreationIfNecessary()
    3811             : {
    3812        1525 :     if (!bDeferredCreation)
    3813        1393 :         return OGRERR_NONE;
    3814         132 :     bDeferredCreation = FALSE;
    3815             : 
    3816         132 :     poDS->EndCopy();
    3817             : 
    3818         253 :     for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    3819             :     {
    3820         121 :         OGRPGGeomFieldDefn *poGeomField = poFeatureDefn->GetGeomFieldDefn(i);
    3821             : 
    3822         121 :         if (poDS->HavePostGIS() ||
    3823           0 :             poGeomField->ePostgisType == GEOM_TYPE_GEOGRAPHY)
    3824             :         {
    3825             :             const char *pszGeometryType =
    3826         121 :                 OGRToOGCGeomType(poGeomField->GetType());
    3827             : 
    3828         121 :             osCreateTable += ", ";
    3829         121 :             osCreateTable += OGRPGEscapeColumnName(poGeomField->GetNameRef());
    3830         121 :             osCreateTable += " ";
    3831         121 :             if (poGeomField->ePostgisType == GEOM_TYPE_GEOMETRY)
    3832         117 :                 osCreateTable += "geometry(";
    3833             :             else
    3834           4 :                 osCreateTable += "geography(";
    3835         121 :             osCreateTable += pszGeometryType;
    3836         121 :             if ((poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_3D) &&
    3837          46 :                 (poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
    3838           3 :                 osCreateTable += "ZM";
    3839         118 :             else if (poGeomField->GeometryTypeFlags & OGRGeometry::OGR_G_3D)
    3840          43 :                 osCreateTable += "Z";
    3841          75 :             else if (poGeomField->GeometryTypeFlags &
    3842             :                      OGRGeometry::OGR_G_MEASURED)
    3843           4 :                 osCreateTable += "M";
    3844         121 :             if (poGeomField->nSRSId > 0)
    3845          16 :                 osCreateTable += CPLSPrintf(",%d", poGeomField->nSRSId);
    3846         121 :             osCreateTable += ")";
    3847         121 :             if (!poGeomField->IsNullable())
    3848           1 :                 osCreateTable += " NOT NULL";
    3849             :         }
    3850             :     }
    3851             : 
    3852         132 :     osCreateTable += " )";
    3853         264 :     CPLString osCommand(osCreateTable);
    3854             : 
    3855         132 :     PGconn *hPGConn = poDS->GetPGConn();
    3856             : 
    3857         132 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand.c_str());
    3858         132 :     if (PQresultStatus(hResult) != PGRES_COMMAND_OK)
    3859             :     {
    3860           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s\n%s", osCommand.c_str(),
    3861             :                  PQerrorMessage(hPGConn));
    3862             : 
    3863           0 :         OGRPGClearResult(hResult);
    3864           0 :         return OGRERR_FAILURE;
    3865             :     }
    3866             : 
    3867         132 :     OGRPGClearResult(hResult);
    3868             : 
    3869         133 :     for (const auto &osSQL : m_aosDeferredCommentOnColumns)
    3870             :     {
    3871           1 :         hResult = OGRPG_PQexec(hPGConn, osSQL.c_str());
    3872           1 :         OGRPGClearResult(hResult);
    3873             :     }
    3874         132 :     m_aosDeferredCommentOnColumns.clear();
    3875             : 
    3876         132 :     if (bCreateSpatialIndexFlag)
    3877             :     {
    3878         253 :         for (int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++)
    3879             :         {
    3880             :             OGRPGGeomFieldDefn *poGeomField =
    3881         121 :                 poFeatureDefn->GetGeomFieldDefn(i);
    3882         121 :             if (RunCreateSpatialIndex(poGeomField, i) != OGRERR_NONE)
    3883             :             {
    3884           0 :                 return OGRERR_FAILURE;
    3885             :             }
    3886             :         }
    3887             :     }
    3888             : 
    3889         132 :     char **papszMD = OGRLayer::GetMetadata();
    3890         132 :     if (papszMD != nullptr)
    3891           2 :         SetMetadata(papszMD);
    3892             : 
    3893         132 :     return OGRERR_NONE;
    3894             : }
    3895             : 
    3896             : /************************************************************************/
    3897             : /*                         GetGeometryTypes()                           */
    3898             : /************************************************************************/
    3899             : 
    3900          27 : OGRGeometryTypeCounter *OGRPGTableLayer::GetGeometryTypes(
    3901             :     int iGeomField, int nFlagsGGT, int &nEntryCountOut,
    3902             :     GDALProgressFunc pfnProgress, void *pProgressData)
    3903             : {
    3904          27 :     if (iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount())
    3905             :     {
    3906           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    3907             :                  "Invalid geometry field index : %d", iGeomField);
    3908           1 :         nEntryCountOut = 0;
    3909           1 :         return nullptr;
    3910             :     }
    3911             : 
    3912          26 :     if (bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
    3913             :     {
    3914           0 :         nEntryCountOut = 0;
    3915           0 :         return nullptr;
    3916             :     }
    3917          26 :     poDS->EndCopy();
    3918             : 
    3919             :     const OGRPGGeomFieldDefn *poGeomFieldDefn =
    3920          26 :         GetLayerDefn()->GetGeomFieldDefn(iGeomField);
    3921             :     const auto osEscapedGeom =
    3922          52 :         OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef());
    3923          52 :     CPLString osSQL;
    3924          26 :     if ((nFlagsGGT & OGR_GGT_GEOMCOLLECTIONZ_TINZ) != 0)
    3925             :     {
    3926          18 :         CPLString osFilter;
    3927             :         osFilter.Printf("(ST_Zmflag(%s) = 2 AND "
    3928             :                         "((GeometryType(%s) = 'GEOMETRYCOLLECTION' AND "
    3929             :                         "ST_NumGeometries(%s) >= 1 AND "
    3930             :                         "geometrytype(ST_GeometryN(%s, 1)) = 'TIN') OR "
    3931             :                         "GeometryType(%s) = 'TIN'))",
    3932             :                         osEscapedGeom.c_str(), osEscapedGeom.c_str(),
    3933             :                         osEscapedGeom.c_str(), osEscapedGeom.c_str(),
    3934           9 :                         osEscapedGeom.c_str());
    3935             : 
    3936          18 :         std::string l_osWHERE(osWHERE);
    3937           9 :         if (l_osWHERE.empty())
    3938           6 :             l_osWHERE = " WHERE ";
    3939             :         else
    3940           3 :             l_osWHERE += " AND ";
    3941           9 :         l_osWHERE += "(NOT (";
    3942           9 :         l_osWHERE += osFilter;
    3943           9 :         l_osWHERE += ") OR ";
    3944           9 :         l_osWHERE += osEscapedGeom;
    3945           9 :         l_osWHERE += " IS NULL)";
    3946             : 
    3947          18 :         std::string l_osWHEREFilter(osWHERE);
    3948           9 :         if (l_osWHEREFilter.empty())
    3949           6 :             l_osWHEREFilter = " WHERE ";
    3950             :         else
    3951           3 :             l_osWHEREFilter += " AND ";
    3952           9 :         l_osWHEREFilter += osFilter;
    3953             : 
    3954             :         osSQL.Printf(
    3955             :             "(SELECT GeometryType(%s), ST_Zmflag(%s), COUNT(*) FROM %s %s "
    3956             :             "GROUP BY GeometryType(%s), ST_Zmflag(%s)) UNION ALL "
    3957             :             "(SELECT * FROM (SELECT 'TIN', 2, COUNT(*) AS count FROM %s %s) "
    3958             :             "tinsubselect WHERE tinsubselect.count != 0)",
    3959             :             osEscapedGeom.c_str(), osEscapedGeom.c_str(), pszSqlTableName,
    3960             :             l_osWHERE.c_str(), osEscapedGeom.c_str(), osEscapedGeom.c_str(),
    3961           9 :             pszSqlTableName, l_osWHEREFilter.c_str());
    3962             :     }
    3963          17 :     else if ((nFlagsGGT & OGR_GGT_STOP_IF_MIXED) != 0)
    3964             :     {
    3965           8 :         std::string l_osWHERE(osWHERE);
    3966           4 :         if (l_osWHERE.empty())
    3967           2 :             l_osWHERE = " WHERE ";
    3968             :         else
    3969           2 :             l_osWHERE += " AND ";
    3970           4 :         l_osWHERE += osEscapedGeom;
    3971           4 :         l_osWHERE += " IS NOT NULL";
    3972             : 
    3973           8 :         std::string l_osWHERE_NULL(osWHERE);
    3974           4 :         if (l_osWHERE_NULL.empty())
    3975           2 :             l_osWHERE_NULL = " WHERE ";
    3976             :         else
    3977           2 :             l_osWHERE_NULL += " AND ";
    3978           4 :         l_osWHERE_NULL += osEscapedGeom;
    3979           4 :         l_osWHERE_NULL += " IS NULL";
    3980             : 
    3981             :         osSQL.Printf("(SELECT DISTINCT GeometryType(%s), ST_Zmflag(%s), 0 FROM "
    3982             :                      "%s %s LIMIT 2) "
    3983             :                      "UNION ALL (SELECT NULL, NULL, 0 FROM %s %s LIMIT 1)",
    3984             :                      osEscapedGeom.c_str(), osEscapedGeom.c_str(),
    3985             :                      pszSqlTableName, l_osWHERE.c_str(), pszSqlTableName,
    3986           4 :                      l_osWHERE_NULL.c_str());
    3987             :     }
    3988             :     else
    3989             :     {
    3990             :         const bool bDebug =
    3991          13 :             CPLTestBool(CPLGetConfigOption("OGR_PG_DEBUG_GGT_CANCEL", "NO"));
    3992             :         osSQL.Printf(
    3993             :             "SELECT GeometryType(%s), ST_Zmflag(%s), COUNT(*)%s FROM %s %s "
    3994             :             "GROUP BY GeometryType(%s), ST_Zmflag(%s)",
    3995             :             osEscapedGeom.c_str(), osEscapedGeom.c_str(),
    3996             :             bDebug ? ", pg_sleep(1)" : "", pszSqlTableName, osWHERE.c_str(),
    3997          13 :             osEscapedGeom.c_str(), osEscapedGeom.c_str());
    3998             :     }
    3999             : 
    4000          52 :     std::thread thread;
    4001          26 :     std::mutex mutex;
    4002          26 :     std::condition_variable cv;
    4003          26 :     bool stopThread = false;
    4004          26 :     if (pfnProgress && pfnProgress != GDALDummyProgress)
    4005             :     {
    4006           4 :         thread = std::thread(
    4007           2 :             [&]()
    4008             :             {
    4009           4 :                 std::unique_lock<std::mutex> lock(mutex);
    4010           4 :                 while (!stopThread)
    4011             :                 {
    4012           2 :                     if (!pfnProgress(0.0, "", pProgressData))
    4013           1 :                         poDS->AbortSQL();
    4014           2 :                     cv.wait_for(lock, std::chrono::milliseconds(100));
    4015             :                 }
    4016           4 :             });
    4017             :     }
    4018             : 
    4019          26 :     PGconn *hPGConn = poDS->GetPGConn();
    4020          26 :     PGresult *hResult = OGRPG_PQexec(hPGConn, osSQL.c_str());
    4021             : 
    4022          26 :     if (pfnProgress && pfnProgress != GDALDummyProgress)
    4023             :     {
    4024             :         {
    4025           4 :             std::unique_lock<std::mutex> lock(mutex);
    4026           2 :             stopThread = true;
    4027           2 :             cv.notify_one();
    4028             :         }
    4029           2 :         thread.join();
    4030             :     }
    4031             : 
    4032          26 :     nEntryCountOut = 0;
    4033          26 :     OGRGeometryTypeCounter *pasRet = nullptr;
    4034          26 :     if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK)
    4035             :     {
    4036          25 :         const int nTuples = PQntuples(hResult);
    4037          25 :         nEntryCountOut = nTuples;
    4038             :         pasRet = static_cast<OGRGeometryTypeCounter *>(
    4039          25 :             CPLCalloc(1 + nEntryCountOut, sizeof(OGRGeometryTypeCounter)));
    4040          69 :         for (int i = 0; i < nTuples; ++i)
    4041             :         {
    4042          44 :             const char *pszGeomType = PQgetvalue(hResult, i, 0);
    4043          44 :             const char *pszZMFlag = PQgetvalue(hResult, i, 1);
    4044          44 :             const char *pszCount = PQgetvalue(hResult, i, 2);
    4045          44 :             if (pszCount)
    4046             :             {
    4047          44 :                 if (pszGeomType == nullptr || pszGeomType[0] == '\0')
    4048             :                 {
    4049          16 :                     pasRet[i].eGeomType = wkbNone;
    4050             :                 }
    4051          28 :                 else if (pszZMFlag != nullptr)
    4052             :                 {
    4053          28 :                     const int nZMFlag = atoi(pszZMFlag);
    4054          28 :                     pasRet[i].eGeomType = OGRFromOGCGeomType(pszGeomType);
    4055          28 :                     int nModifier = 0;
    4056          28 :                     if (nZMFlag == 1)
    4057           1 :                         nModifier = OGRGeometry::OGR_G_MEASURED;
    4058          27 :                     else if (nZMFlag == 2)
    4059           9 :                         nModifier = OGRGeometry::OGR_G_3D;
    4060          18 :                     else if (nZMFlag == 3)
    4061           1 :                         nModifier =
    4062             :                             OGRGeometry::OGR_G_MEASURED | OGRGeometry::OGR_G_3D;
    4063          28 :                     pasRet[i].eGeomType = OGR_GT_SetModifier(
    4064          28 :                         pasRet[i].eGeomType, nModifier & OGRGeometry::OGR_G_3D,
    4065             :                         nModifier & OGRGeometry::OGR_G_MEASURED);
    4066             :                 }
    4067          44 :                 pasRet[i].nCount =
    4068          44 :                     static_cast<int64_t>(std::strtoll(pszCount, nullptr, 10));
    4069             :             }
    4070             :         }
    4071             :     }
    4072             : 
    4073          26 :     OGRPGClearResult(hResult);
    4074             : 
    4075          26 :     return pasRet;
    4076             : }
    4077             : 
    4078             : /************************************************************************/
    4079             : /*                          FindFieldIndex()                            */
    4080             : /************************************************************************/
    4081             : 
    4082          24 : int OGRPGTableLayer::FindFieldIndex(const char *pszFieldName, int bExactMatch)
    4083             : {
    4084          24 :     const auto poLayerDefn = GetLayerDefn();
    4085          24 :     int iField = poLayerDefn->GetFieldIndex(pszFieldName);
    4086             : 
    4087          24 :     if (!bExactMatch && iField < 0 && bLaunderColumnNames)
    4088             :     {
    4089          10 :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
    4090             :         char *pszSafeName =
    4091           5 :             OGRPGCommonLaunderName(pszFieldName, "PG", m_bUTF8ToASCII);
    4092           5 :         iField = poLayerDefn->GetFieldIndex(pszSafeName);
    4093           5 :         CPLFree(pszSafeName);
    4094             :     }
    4095             : 
    4096          24 :     return iField;
    4097             : }
    4098             : 
    4099             : #undef PQexec

Generated by: LCOV version 1.14