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

Generated by: LCOV version 1.14