LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/pg - ogrpgtablelayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1708 1939 88.1 %
Date: 2026-02-06 10:43:15 Functions: 67 67 100.0 %

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

Generated by: LCOV version 1.14