LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/pgdump - ogrpgdumplayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 930 1028 90.5 %
Date: 2025-07-01 22:47:05 Functions: 39 39 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements OGRPGDumpLayer class
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "ogr_pgdump.h"
      14             : #include "cpl_conv.h"
      15             : #include "cpl_md5.h"
      16             : #include "cpl_string.h"
      17             : #include "ogr_p.h"
      18             : 
      19             : #include <cmath>
      20             : #include <limits>
      21             : 
      22             : //
      23             : static CPLString
      24             : OGRPGDumpEscapeStringList(char **papszItems, bool bForInsertOrUpdate,
      25             :                           OGRPGCommonEscapeStringCbk pfnEscapeString,
      26             :                           void *userdata);
      27             : 
      28         148 : static CPLString OGRPGDumpEscapeStringWithUserData(
      29             :     CPL_UNUSED void *user_data, const char *pszStrValue, int nMaxLength,
      30             :     CPL_UNUSED const char *pszLayerName, const char *pszFieldName)
      31             : {
      32         148 :     return OGRPGDumpEscapeString(pszStrValue, nMaxLength, pszFieldName);
      33             : }
      34             : 
      35             : OGRPGDumpGeomFieldDefn::~OGRPGDumpGeomFieldDefn() = default;
      36             : 
      37             : /************************************************************************/
      38             : /*                        OGRPGDumpLayer()                              */
      39             : /************************************************************************/
      40             : 
      41         118 : OGRPGDumpLayer::OGRPGDumpLayer(OGRPGDumpDataSource *poDSIn,
      42             :                                const char *pszSchemaNameIn,
      43             :                                const char *pszTableName,
      44             :                                const char *pszFIDColumnIn, int bWriteAsHexIn,
      45         118 :                                int bCreateTableIn, bool bSkipConflictsIn)
      46         236 :     : m_pszSchemaName(CPLStrdup(pszSchemaNameIn)),
      47         118 :       m_pszSqlTableName(CPLStrdup(CPLString().Printf(
      48         118 :           "%s.%s", OGRPGDumpEscapeColumnName(m_pszSchemaName).c_str(),
      49         354 :           OGRPGDumpEscapeColumnName(pszTableName).c_str()))),
      50         118 :       m_pszFIDColumn(pszFIDColumnIn ? CPLStrdup(pszFIDColumnIn) : nullptr),
      51         118 :       m_poFeatureDefn(new OGRFeatureDefn(pszTableName)), m_poDS(poDSIn),
      52         118 :       m_bWriteAsHex(CPL_TO_BOOL(bWriteAsHexIn)), m_bCreateTable(bCreateTableIn),
      53         590 :       m_bSkipConflicts(bSkipConflictsIn)
      54             : {
      55         118 :     SetDescription(m_poFeatureDefn->GetName());
      56         118 :     m_poFeatureDefn->SetGeomType(wkbNone);
      57         118 :     m_poFeatureDefn->Reference();
      58         118 : }
      59             : 
      60             : /************************************************************************/
      61             : /*                          ~OGRPGDumpLayer()                           */
      62             : /************************************************************************/
      63             : 
      64         236 : OGRPGDumpLayer::~OGRPGDumpLayer()
      65             : {
      66         118 :     EndCopy();
      67         118 :     LogDeferredFieldCreationIfNeeded();
      68         118 :     UpdateSequenceIfNeeded();
      69         225 :     for (const auto &osSQL : m_aosSpatialIndexCreationCommands)
      70             :     {
      71         107 :         m_poDS->Log(osSQL.c_str());
      72             :     }
      73             : 
      74         118 :     m_poFeatureDefn->Release();
      75         118 :     CPLFree(m_pszSchemaName);
      76         118 :     CPLFree(m_pszSqlTableName);
      77         118 :     CPLFree(m_pszFIDColumn);
      78         236 : }
      79             : 
      80             : /************************************************************************/
      81             : /*                           GetNextFeature()                           */
      82             : /************************************************************************/
      83             : 
      84          16 : OGRFeature *OGRPGDumpLayer::GetNextFeature()
      85             : {
      86          16 :     CPLError(CE_Failure, CPLE_NotSupported, "PGDump driver is write only");
      87          16 :     return nullptr;
      88             : }
      89             : 
      90             : /************************************************************************/
      91             : /*                           GetNextFeature()                           */
      92             : /************************************************************************/
      93             : 
      94         226 : int OGRPGDumpLayer::TestCapability(const char *pszCap)
      95             : {
      96         226 :     if (EQUAL(pszCap, OLCSequentialWrite) || EQUAL(pszCap, OLCCreateField) ||
      97         194 :         EQUAL(pszCap, OLCCreateGeomField) ||
      98         193 :         EQUAL(pszCap, OLCCurveGeometries) || EQUAL(pszCap, OLCZGeometries) ||
      99          93 :         EQUAL(pszCap, OLCMeasuredGeometries))
     100         226 :         return TRUE;
     101             :     else
     102           0 :         return FALSE;
     103             : }
     104             : 
     105             : /************************************************************************/
     106             : /*                   LogDeferredFieldCreationIfNeeded()                 */
     107             : /************************************************************************/
     108             : 
     109         314 : void OGRPGDumpLayer::LogDeferredFieldCreationIfNeeded()
     110             : {
     111             :     // Emit column creation
     112         623 :     if (!m_aosDeferrentNonGeomFieldCreationCommands.empty() ||
     113         309 :         !m_aosDeferredGeomFieldCreationCommands.empty())
     114             :     {
     115           5 :         CPLAssert(m_bCreateTable);
     116           5 :         CPLAssert(!m_bGeomColumnPositionImmediate);
     117             :         // In non-immediate mode, we put geometry fields after non-geometry
     118             :         // ones
     119          10 :         for (const auto &osSQL : m_aosDeferrentNonGeomFieldCreationCommands)
     120           5 :             m_poDS->Log(osSQL.c_str());
     121          10 :         for (const auto &osSQL : m_aosDeferredGeomFieldCreationCommands)
     122           5 :             m_poDS->Log(osSQL.c_str());
     123           5 :         m_aosDeferrentNonGeomFieldCreationCommands.clear();
     124           5 :         m_aosDeferredGeomFieldCreationCommands.clear();
     125             :     }
     126         314 : }
     127             : 
     128             : /************************************************************************/
     129             : /*                           GetNextFeature()                           */
     130             : /************************************************************************/
     131             : 
     132         196 : OGRErr OGRPGDumpLayer::ICreateFeature(OGRFeature *poFeature)
     133             : {
     134         196 :     if (nullptr == poFeature)
     135             :     {
     136           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     137             :                  "NULL pointer to OGRFeature passed to CreateFeature().");
     138           0 :         return OGRERR_FAILURE;
     139             :     }
     140             : 
     141         196 :     LogDeferredFieldCreationIfNeeded();
     142             : 
     143             :     /* In case the FID column has also been created as a regular field */
     144         196 :     if (m_iFIDAsRegularColumnIndex >= 0)
     145             :     {
     146           8 :         if (poFeature->GetFID() == OGRNullFID)
     147             :         {
     148           6 :             if (poFeature->IsFieldSetAndNotNull(m_iFIDAsRegularColumnIndex))
     149             :             {
     150           4 :                 poFeature->SetFID(
     151           4 :                     poFeature->GetFieldAsInteger64(m_iFIDAsRegularColumnIndex));
     152             :             }
     153             :         }
     154             :         else
     155             :         {
     156           4 :             if (!poFeature->IsFieldSetAndNotNull(m_iFIDAsRegularColumnIndex) ||
     157           2 :                 poFeature->GetFieldAsInteger64(m_iFIDAsRegularColumnIndex) !=
     158           2 :                     poFeature->GetFID())
     159             :             {
     160           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
     161             :                          "Inconsistent values of FID and field of same name");
     162           2 :                 return OGRERR_FAILURE;
     163             :             }
     164             :         }
     165             :     }
     166             : 
     167         194 :     if (!poFeature->Validate((OGR_F_VAL_ALL & ~OGR_F_VAL_WIDTH) |
     168             :                                  OGR_F_VAL_ALLOW_DIFFERENT_GEOM_DIM,
     169             :                              TRUE))
     170          16 :         return OGRERR_FAILURE;
     171             : 
     172             :     // We avoid testing the config option too often.
     173         178 :     if (m_bUseCopy == USE_COPY_UNSET)
     174          95 :         m_bUseCopy = CPLTestBool(CPLGetConfigOption("PG_USE_COPY", "NO"));
     175             : 
     176             :     OGRErr eErr;
     177         178 :     if (!m_bUseCopy)
     178             :     {
     179         143 :         eErr = CreateFeatureViaInsert(poFeature);
     180             :     }
     181             :     else
     182             :     {
     183             :         // If there's a unset field with a default value, then we must use a
     184             :         // specific INSERT statement to avoid unset fields to be bound to NULL.
     185          35 :         bool bHasDefaultValue = false;
     186          35 :         const int nFieldCount = m_poFeatureDefn->GetFieldCount();
     187         163 :         for (int iField = 0; iField < nFieldCount; iField++)
     188             :         {
     189         160 :             if (!poFeature->IsFieldSetAndNotNull(iField) &&
     190          31 :                 poFeature->GetFieldDefnRef(iField)->GetDefault() != nullptr)
     191             :             {
     192           1 :                 bHasDefaultValue = true;
     193           1 :                 break;
     194             :             }
     195             :         }
     196          35 :         if (bHasDefaultValue)
     197             :         {
     198           1 :             EndCopy();
     199           1 :             eErr = CreateFeatureViaInsert(poFeature);
     200             :         }
     201             :         else
     202             :         {
     203          34 :             const bool bFIDSet = poFeature->GetFID() != OGRNullFID;
     204          34 :             if (m_bCopyActive && bFIDSet != m_bCopyStatementWithFID)
     205             :             {
     206           3 :                 EndCopy();
     207           3 :                 eErr = CreateFeatureViaInsert(poFeature);
     208             :             }
     209             :             else
     210             :             {
     211          31 :                 if (!m_bCopyActive)
     212             :                 {
     213             :                     // This is a heuristics. If the first feature to be copied
     214             :                     // has a FID set (and that a FID column has been
     215             :                     // identified), then we will try to copy FID values from
     216             :                     // features. Otherwise, we will not do and assume that the
     217             :                     // FID column is an autoincremented column.
     218          10 :                     StartCopy(bFIDSet);
     219          10 :                     m_bCopyStatementWithFID = bFIDSet;
     220          10 :                     m_bNeedToUpdateSequence = bFIDSet;
     221             :                 }
     222             : 
     223          31 :                 eErr = CreateFeatureViaCopy(poFeature);
     224          31 :                 if (bFIDSet)
     225           3 :                     m_bAutoFIDOnCreateViaCopy = false;
     226          31 :                 if (eErr == OGRERR_NONE && m_bAutoFIDOnCreateViaCopy)
     227             :                 {
     228          28 :                     poFeature->SetFID(++m_iNextShapeId);
     229             :                 }
     230             :             }
     231             :         }
     232             :     }
     233             : 
     234         178 :     if (eErr == OGRERR_NONE && m_iFIDAsRegularColumnIndex >= 0)
     235             :     {
     236           6 :         poFeature->SetField(m_iFIDAsRegularColumnIndex, poFeature->GetFID());
     237             :     }
     238         178 :     return eErr;
     239             : }
     240             : 
     241             : /************************************************************************/
     242             : /*                       CreateFeatureViaInsert()                       */
     243             : /************************************************************************/
     244             : 
     245         147 : OGRErr OGRPGDumpLayer::CreateFeatureViaInsert(OGRFeature *poFeature)
     246             : 
     247             : {
     248         147 :     OGRErr eErr = OGRERR_FAILURE;
     249             : 
     250         147 :     if (nullptr == poFeature)
     251             :     {
     252           0 :         CPLError(
     253             :             CE_Failure, CPLE_AppDefined,
     254             :             "NULL pointer to OGRFeature passed to CreateFeatureViaInsert().");
     255           0 :         return eErr;
     256             :     }
     257             : 
     258             :     /* -------------------------------------------------------------------- */
     259             :     /*      Form the INSERT command.                                        */
     260             :     /* -------------------------------------------------------------------- */
     261         294 :     CPLString osCommand;
     262         147 :     osCommand.Printf("INSERT INTO %s (", m_pszSqlTableName);
     263             : 
     264         147 :     bool bNeedComma = false;
     265             : 
     266         147 :     if (poFeature->GetFID() != OGRNullFID && m_pszFIDColumn != nullptr)
     267             :     {
     268           7 :         m_bNeedToUpdateSequence = true;
     269             : 
     270           7 :         osCommand += OGRPGDumpEscapeColumnName(m_pszFIDColumn);
     271           7 :         bNeedComma = true;
     272             :     }
     273             :     else
     274             :     {
     275         140 :         UpdateSequenceIfNeeded();
     276             :     }
     277             : 
     278         970 :     const auto AddGeomFieldsName = [this, poFeature, &bNeedComma, &osCommand]()
     279             :     {
     280         280 :         for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
     281             :         {
     282         133 :             OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
     283         133 :             if (poGeom != nullptr)
     284             :             {
     285         100 :                 if (bNeedComma)
     286          10 :                     osCommand += ", ";
     287             : 
     288             :                 OGRGeomFieldDefn *poGFldDefn =
     289         100 :                     poFeature->GetGeomFieldDefnRef(i);
     290             :                 osCommand +=
     291         100 :                     OGRPGDumpEscapeColumnName(poGFldDefn->GetNameRef());
     292         100 :                 bNeedComma = true;
     293             :             }
     294             :         }
     295         147 :     };
     296             : 
     297         147 :     if (m_bGeomColumnPositionImmediate)
     298         140 :         AddGeomFieldsName();
     299             : 
     300         520 :     for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
     301             :     {
     302         373 :         if (i == m_iFIDAsRegularColumnIndex)
     303           4 :             continue;
     304         369 :         if (!poFeature->IsFieldSet(i))
     305         123 :             continue;
     306             : 
     307         246 :         if (!bNeedComma)
     308          30 :             bNeedComma = true;
     309             :         else
     310         216 :             osCommand += ", ";
     311             : 
     312         492 :         osCommand += OGRPGDumpEscapeColumnName(
     313         492 :             m_poFeatureDefn->GetFieldDefn(i)->GetNameRef());
     314             :     }
     315             : 
     316         147 :     if (!m_bGeomColumnPositionImmediate)
     317           7 :         AddGeomFieldsName();
     318             : 
     319         147 :     const bool bEmptyInsert = !bNeedComma;
     320             : 
     321         147 :     osCommand += ") VALUES (";
     322             : 
     323         147 :     bNeedComma = false;
     324             : 
     325             :     /* Set the geometry */
     326        1368 :     const auto AddGeomFieldsValue = [this, poFeature, &bNeedComma, &osCommand]()
     327             :     {
     328         280 :         for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
     329             :         {
     330         133 :             OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i);
     331         133 :             if (poGeom != nullptr)
     332             :             {
     333         100 :                 char *pszWKT = nullptr;
     334             : 
     335             :                 OGRPGDumpGeomFieldDefn *poGFldDefn =
     336         100 :                     cpl::down_cast<OGRPGDumpGeomFieldDefn *>(
     337             :                         poFeature->GetGeomFieldDefnRef(i));
     338             : 
     339         100 :                 poGeom->closeRings();
     340         100 :                 poGeom->set3D(poGFldDefn->m_nGeometryTypeFlags &
     341         100 :                               OGRGeometry::OGR_G_3D);
     342         100 :                 poGeom->setMeasured(poGFldDefn->m_nGeometryTypeFlags &
     343         100 :                                     OGRGeometry::OGR_G_MEASURED);
     344             : 
     345         100 :                 if (bNeedComma)
     346          10 :                     osCommand += ", ";
     347             : 
     348         100 :                 if (m_bWriteAsHex)
     349             :                 {
     350             :                     char *pszHex =
     351          98 :                         OGRGeometryToHexEWKB(poGeom, poGFldDefn->m_nSRSId,
     352             :                                              m_nPostGISMajor, m_nPostGISMinor);
     353          98 :                     osCommand += "'";
     354          98 :                     if (!pszHex || pszHex[0] == 0)
     355             :                     {
     356           0 :                         CPLFree(pszHex);
     357           0 :                         return false;
     358             :                     }
     359             :                     try
     360             :                     {
     361          98 :                         osCommand += pszHex;
     362             :                     }
     363           0 :                     catch (const std::bad_alloc &)
     364             :                     {
     365           0 :                         CPLError(CE_Failure, CPLE_OutOfMemory,
     366             :                                  "Out of memory: too large geometry");
     367           0 :                         CPLFree(pszHex);
     368           0 :                         return false;
     369             :                     }
     370          98 :                     osCommand += "'";
     371          98 :                     CPLFree(pszHex);
     372             :                 }
     373             :                 else
     374             :                 {
     375           2 :                     poGeom->exportToWkt(&pszWKT, wkbVariantIso);
     376             : 
     377           2 :                     if (!pszWKT)
     378             :                     {
     379           0 :                         return false;
     380             :                     }
     381             :                     try
     382             :                     {
     383             :                         osCommand += CPLSPrintf("GeomFromEWKT('SRID=%d;",
     384           2 :                                                 poGFldDefn->m_nSRSId);
     385           2 :                         osCommand += pszWKT;
     386           2 :                         osCommand += "'::TEXT) ";
     387             :                     }
     388           0 :                     catch (const std::bad_alloc &)
     389             :                     {
     390           0 :                         CPLError(CE_Failure, CPLE_OutOfMemory,
     391             :                                  "Out of memory: too large geometry");
     392           0 :                         CPLFree(pszWKT);
     393           0 :                         return false;
     394             :                     }
     395           2 :                     CPLFree(pszWKT);
     396             :                 }
     397             : 
     398         100 :                 bNeedComma = true;
     399             :             }
     400             :         }
     401         147 :         return true;
     402         147 :     };
     403             : 
     404             :     /* Set the FID */
     405         147 :     if (poFeature->GetFID() != OGRNullFID && m_pszFIDColumn != nullptr)
     406             :     {
     407           7 :         if (bNeedComma)
     408           0 :             osCommand += ", ";
     409           7 :         osCommand += CPLString().Printf(CPL_FRMT_GIB, poFeature->GetFID());
     410           7 :         bNeedComma = true;
     411             :     }
     412             : 
     413         147 :     if (m_bGeomColumnPositionImmediate)
     414             :     {
     415         140 :         if (!AddGeomFieldsValue())
     416           0 :             return OGRERR_FAILURE;
     417             :     }
     418             : 
     419         520 :     for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
     420             :     {
     421         373 :         if (i == m_iFIDAsRegularColumnIndex)
     422           4 :             continue;
     423         369 :         if (!poFeature->IsFieldSet(i))
     424         123 :             continue;
     425             : 
     426         246 :         if (bNeedComma)
     427         216 :             osCommand += ", ";
     428             :         else
     429          30 :             bNeedComma = true;
     430             : 
     431         246 :         OGRPGCommonAppendFieldValue(osCommand, poFeature, i,
     432             :                                     OGRPGDumpEscapeStringWithUserData, nullptr);
     433             :     }
     434             : 
     435         147 :     if (!m_bGeomColumnPositionImmediate)
     436             :     {
     437           7 :         if (!AddGeomFieldsValue())
     438           0 :             return OGRERR_FAILURE;
     439             :     }
     440             : 
     441         147 :     osCommand += ")";
     442             : 
     443         147 :     if (m_bSkipConflicts)
     444           1 :         osCommand += " ON CONFLICT DO NOTHING";
     445             : 
     446         147 :     if (bEmptyInsert)
     447          20 :         osCommand.Printf("INSERT INTO %s DEFAULT VALUES", m_pszSqlTableName);
     448             : 
     449             :     /* -------------------------------------------------------------------- */
     450             :     /*      Execute the insert.                                             */
     451             :     /* -------------------------------------------------------------------- */
     452         147 :     m_poDS->Log(osCommand);
     453             : 
     454         147 :     if (poFeature->GetFID() == OGRNullFID)
     455         138 :         poFeature->SetFID(++m_iNextShapeId);
     456             : 
     457         147 :     return OGRERR_NONE;
     458             : }
     459             : 
     460             : /************************************************************************/
     461             : /*                        CreateFeatureViaCopy()                        */
     462             : /************************************************************************/
     463             : 
     464          31 : OGRErr OGRPGDumpLayer::CreateFeatureViaCopy(OGRFeature *poFeature)
     465             : {
     466          62 :     CPLString osCommand;
     467             : 
     468          31 :     if (m_bFIDColumnInCopyFields)
     469           3 :         OGRPGCommonAppendCopyFID(osCommand, poFeature);
     470             : 
     471         136 :     const auto AddGeomFieldsValue = [this, poFeature, &osCommand]()
     472             :     {
     473          43 :         for (int i = 0; i < poFeature->GetGeomFieldCount(); i++)
     474             :         {
     475          12 :             OGRGeometry *poGeometry = poFeature->GetGeomFieldRef(i);
     476          12 :             char *pszGeom = nullptr;
     477          12 :             if (nullptr !=
     478             :                 poGeometry /* && (bHasWkb || bHasPostGISGeometry || bHasPostGISGeography) */)
     479             :             {
     480             :                 OGRPGDumpGeomFieldDefn *poGFldDefn =
     481          12 :                     cpl::down_cast<OGRPGDumpGeomFieldDefn *>(
     482             :                         poFeature->GetGeomFieldDefnRef(i));
     483             : 
     484          12 :                 poGeometry->closeRings();
     485          12 :                 poGeometry->set3D(poGFldDefn->m_nGeometryTypeFlags &
     486          12 :                                   OGRGeometry::OGR_G_3D);
     487          12 :                 poGeometry->setMeasured(poGFldDefn->m_nGeometryTypeFlags &
     488          12 :                                         OGRGeometry::OGR_G_MEASURED);
     489             : 
     490             :                 pszGeom =
     491          12 :                     OGRGeometryToHexEWKB(poGeometry, poGFldDefn->m_nSRSId,
     492             :                                          m_nPostGISMajor, m_nPostGISMinor);
     493             :             }
     494             : 
     495          12 :             if (!osCommand.empty())
     496           2 :                 osCommand += "\t";
     497          12 :             if (!pszGeom || pszGeom[0] == 0)
     498             :             {
     499           0 :                 CPLFree(pszGeom);
     500           0 :                 return false;
     501             :             }
     502             :             try
     503             :             {
     504          12 :                 osCommand += pszGeom;
     505             :             }
     506           0 :             catch (const std::bad_alloc &)
     507             :             {
     508           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory,
     509             :                          "Out of memory: too large geometry");
     510           0 :                 CPLFree(pszGeom);
     511           0 :                 return false;
     512             :             }
     513          12 :             CPLFree(pszGeom);
     514             :         }
     515          31 :         return true;
     516          31 :     };
     517             : 
     518          31 :     if (m_bGeomColumnPositionImmediate)
     519             :     {
     520          29 :         if (!AddGeomFieldsValue())
     521           0 :             return OGRERR_FAILURE;
     522             :     }
     523             : 
     524          31 :     OGRPGCommonAppendCopyRegularFields(
     525          31 :         osCommand, poFeature, m_pszFIDColumn,
     526          62 :         std::vector<bool>(m_poFeatureDefn->GetFieldCount(), true),
     527             :         OGRPGDumpEscapeStringWithUserData, nullptr);
     528             : 
     529          31 :     if (!m_bGeomColumnPositionImmediate)
     530             :     {
     531           2 :         if (!AddGeomFieldsValue())
     532           0 :             return OGRERR_FAILURE;
     533             :     }
     534             : 
     535             :     /* ------------------------------------------------------------ */
     536             :     /*      Execute the copy.                                       */
     537             :     /* ------------------------------------------------------------ */
     538             : 
     539          31 :     OGRErr result = OGRERR_NONE;
     540             : 
     541          31 :     m_poDS->Log(osCommand, false);
     542             : 
     543          31 :     return result;
     544             : }
     545             : 
     546             : /************************************************************************/
     547             : /*                      OGRPGCommonAppendCopyFID()                      */
     548             : /************************************************************************/
     549             : 
     550          17 : void OGRPGCommonAppendCopyFID(CPLString &osCommand, OGRFeature *poFeature)
     551             : {
     552          17 :     if (!osCommand.empty())
     553           8 :         osCommand += "\t";
     554             : 
     555             :     /* Set the FID */
     556          17 :     if (poFeature->GetFID() != OGRNullFID)
     557             :     {
     558          17 :         osCommand += CPLString().Printf(CPL_FRMT_GIB, poFeature->GetFID());
     559             :     }
     560             :     else
     561             :     {
     562           0 :         osCommand += "\\N";
     563             :     }
     564          17 : }
     565             : 
     566             : /************************************************************************/
     567             : /*                OGRPGCommonAppendCopyRegularFields()                  */
     568             : /************************************************************************/
     569             : 
     570        3770 : void OGRPGCommonAppendCopyRegularFields(
     571             :     CPLString &osCommand, OGRFeature *poFeature, const char *pszFIDColumn,
     572             :     const std::vector<bool> &abFieldsToInclude,
     573             :     OGRPGCommonEscapeStringCbk pfnEscapeString, void *userdata)
     574             : {
     575        3770 :     const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
     576             :     const int nFIDIndex =
     577        3770 :         pszFIDColumn ? poFeatureDefn->GetFieldIndex(pszFIDColumn) : -1;
     578             : 
     579        3770 :     const int nFieldCount = poFeatureDefn->GetFieldCount();
     580        3770 :     bool bAddTab = !osCommand.empty();
     581             : 
     582        3770 :     CPLAssert(nFieldCount == static_cast<int>(abFieldsToInclude.size()));
     583             : 
     584        9117 :     for (int i = 0; i < nFieldCount; i++)
     585             :     {
     586        5347 :         if (i == nFIDIndex)
     587           4 :             continue;
     588        5343 :         if (!abFieldsToInclude[i])
     589           2 :             continue;
     590             : 
     591        5341 :         const char *pszStrValue = poFeature->GetFieldAsString(i);
     592        5341 :         char *pszNeedToFree = nullptr;
     593             : 
     594        5341 :         if (bAddTab)
     595        5293 :             osCommand += "\t";
     596        5341 :         bAddTab = true;
     597             : 
     598        5341 :         if (!poFeature->IsFieldSetAndNotNull(i))
     599             :         {
     600        1287 :             osCommand += "\\N";
     601             : 
     602        1287 :             continue;
     603             :         }
     604             : 
     605        4054 :         const int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
     606             : 
     607             :         // We need special formatting for integer list values.
     608        4054 :         if (nOGRFieldType == OFTIntegerList)
     609             :         {
     610           8 :             int nCount, nOff = 0;
     611           8 :             const int *panItems = poFeature->GetFieldAsIntegerList(i, &nCount);
     612             : 
     613           8 :             const size_t nLen = nCount * 13 + 10;
     614           8 :             pszNeedToFree = static_cast<char *>(CPLMalloc(nLen));
     615           8 :             strcpy(pszNeedToFree, "{");
     616          24 :             for (int j = 0; j < nCount; j++)
     617             :             {
     618          16 :                 if (j != 0)
     619           8 :                     strcat(pszNeedToFree + nOff, ",");
     620             : 
     621          16 :                 nOff += static_cast<int>(strlen(pszNeedToFree + nOff));
     622          16 :                 snprintf(pszNeedToFree + nOff, nLen - nOff, "%d", panItems[j]);
     623             :             }
     624           8 :             strcat(pszNeedToFree + nOff, "}");
     625           8 :             pszStrValue = pszNeedToFree;
     626             :         }
     627             : 
     628        4046 :         else if (nOGRFieldType == OFTInteger64List)
     629             :         {
     630           2 :             int nCount, nOff = 0;
     631             :             const GIntBig *panItems =
     632           2 :                 poFeature->GetFieldAsInteger64List(i, &nCount);
     633             : 
     634           2 :             const size_t nLen = nCount * 26 + 10;
     635           2 :             pszNeedToFree = static_cast<char *>(CPLMalloc(nLen));
     636           2 :             strcpy(pszNeedToFree, "{");
     637           4 :             for (int j = 0; j < nCount; j++)
     638             :             {
     639           2 :                 if (j != 0)
     640           0 :                     strcat(pszNeedToFree + nOff, ",");
     641             : 
     642           2 :                 nOff += static_cast<int>(strlen(pszNeedToFree + nOff));
     643           2 :                 snprintf(pszNeedToFree + nOff, nLen - nOff, CPL_FRMT_GIB,
     644           2 :                          panItems[j]);
     645             :             }
     646           2 :             strcat(pszNeedToFree + nOff, "}");
     647           2 :             pszStrValue = pszNeedToFree;
     648             :         }
     649             : 
     650             :         // We need special formatting for real list values.
     651        4044 :         else if (nOGRFieldType == OFTRealList)
     652             :         {
     653          20 :             int nOff = 0;
     654          20 :             int nCount = 0;
     655             :             const double *padfItems =
     656          20 :                 poFeature->GetFieldAsDoubleList(i, &nCount);
     657             : 
     658          20 :             const size_t nLen = nCount * 40 + 10;
     659          20 :             pszNeedToFree = static_cast<char *>(CPLMalloc(nLen));
     660          20 :             strcpy(pszNeedToFree, "{");
     661          60 :             for (int j = 0; j < nCount; j++)
     662             :             {
     663          40 :                 if (j != 0)
     664          20 :                     strcat(pszNeedToFree + nOff, ",");
     665             : 
     666          40 :                 nOff += static_cast<int>(strlen(pszNeedToFree + nOff));
     667             :                 // Check for special values. They need to be quoted.
     668          40 :                 if (std::isnan(padfItems[j]))
     669           8 :                     snprintf(pszNeedToFree + nOff, nLen - nOff, "NaN");
     670          32 :                 else if (std::isinf(padfItems[j]))
     671          16 :                     snprintf(pszNeedToFree + nOff, nLen - nOff,
     672          16 :                              (padfItems[j] > 0) ? "Infinity" : "-Infinity");
     673             :                 else
     674          16 :                     CPLsnprintf(pszNeedToFree + nOff, nLen - nOff, "%.16g",
     675          16 :                                 padfItems[j]);
     676             :             }
     677          20 :             strcat(pszNeedToFree + nOff, "}");
     678          20 :             pszStrValue = pszNeedToFree;
     679             :         }
     680             : 
     681             :         // We need special formatting for string list values.
     682        4024 :         else if (nOGRFieldType == OFTStringList)
     683             :         {
     684           6 :             CPLString osStr;
     685           6 :             char **papszItems = poFeature->GetFieldAsStringList(i);
     686             : 
     687           6 :             pszStrValue = pszNeedToFree = CPLStrdup(OGRPGDumpEscapeStringList(
     688             :                 papszItems, false, pfnEscapeString, userdata));
     689             :         }
     690             : 
     691             :         // Binary formatting
     692        4018 :         else if (nOGRFieldType == OFTBinary)
     693             :         {
     694           3 :             int nLen = 0;
     695           3 :             GByte *pabyData = poFeature->GetFieldAsBinary(i, &nLen);
     696           3 :             char *pszBytea = OGRPGCommonGByteArrayToBYTEA(pabyData, nLen);
     697             : 
     698           3 :             pszStrValue = pszNeedToFree = pszBytea;
     699             :         }
     700             : 
     701        4015 :         else if (nOGRFieldType == OFTReal)
     702             :         {
     703             :             // Check for special values. They need to be quoted.
     704         632 :             double dfVal = poFeature->GetFieldAsDouble(i);
     705         632 :             if (std::isnan(dfVal))
     706           4 :                 pszStrValue = "NaN";
     707         628 :             else if (std::isinf(dfVal))
     708           8 :                 pszStrValue = (dfVal > 0) ? "Infinity" : "-Infinity";
     709             :         }
     710             : 
     711        4054 :         if (nOGRFieldType != OFTIntegerList &&
     712        4044 :             nOGRFieldType != OFTInteger64List && nOGRFieldType != OFTRealList &&
     713        1401 :             nOGRFieldType != OFTInteger && nOGRFieldType != OFTInteger64 &&
     714         767 :             nOGRFieldType != OFTReal && nOGRFieldType != OFTBinary)
     715             :         {
     716         764 :             int iUTFChar = 0;
     717         764 :             const int nMaxWidth = poFeatureDefn->GetFieldDefn(i)->GetWidth();
     718             : 
     719        6624 :             for (int iChar = 0; pszStrValue[iChar] != '\0'; iChar++)
     720             :             {
     721             :                 // count of utf chars
     722        5865 :                 if (nOGRFieldType != OFTStringList &&
     723        5803 :                     (pszStrValue[iChar] & 0xc0) != 0x80)
     724             :                 {
     725        5790 :                     if (nMaxWidth > 0 && iUTFChar == nMaxWidth)
     726             :                     {
     727           5 :                         CPLDebug("PG",
     728             :                                  "Truncated %s field value, it was too long.",
     729           5 :                                  poFeatureDefn->GetFieldDefn(i)->GetNameRef());
     730           5 :                         break;
     731             :                     }
     732        5785 :                     iUTFChar++;
     733             :                 }
     734             : 
     735             :                 /* Escape embedded \, \t, \n, \r since they will cause COPY
     736             :                    to misinterpret a line of text and thus abort */
     737        5860 :                 if (pszStrValue[iChar] == '\\' || pszStrValue[iChar] == '\t' ||
     738        5860 :                     pszStrValue[iChar] == '\r' || pszStrValue[iChar] == '\n')
     739             :                 {
     740           0 :                     osCommand += '\\';
     741             :                 }
     742             : 
     743        5860 :                 osCommand += pszStrValue[iChar];
     744         764 :             }
     745             :         }
     746             :         else
     747             :         {
     748        3290 :             osCommand += pszStrValue;
     749             :         }
     750             : 
     751        4054 :         if (pszNeedToFree)
     752          39 :             CPLFree(pszNeedToFree);
     753             :     }
     754        3770 : }
     755             : 
     756             : /************************************************************************/
     757             : /*                             StartCopy()                              */
     758             : /************************************************************************/
     759             : 
     760          10 : OGRErr OGRPGDumpLayer::StartCopy(int bSetFID)
     761             : 
     762             : {
     763             :     /* Tell the datasource we are now planning to copy data */
     764          10 :     m_poDS->StartCopy(this);
     765             : 
     766          10 :     CPLString osFields = BuildCopyFields(bSetFID);
     767             : 
     768          10 :     size_t size = osFields.size() + strlen(m_pszSqlTableName) + 100;
     769          10 :     char *pszCommand = static_cast<char *>(CPLMalloc(size));
     770             : 
     771          10 :     snprintf(pszCommand, size, "COPY %s (%s) FROM STDIN", m_pszSqlTableName,
     772             :              osFields.c_str());
     773             : 
     774          10 :     m_poDS->Log(pszCommand);
     775          10 :     m_bCopyActive = true;
     776             : 
     777          10 :     CPLFree(pszCommand);
     778             : 
     779          20 :     return OGRERR_NONE;
     780             : }
     781             : 
     782             : /************************************************************************/
     783             : /*                              EndCopy()                               */
     784             : /************************************************************************/
     785             : 
     786         132 : OGRErr OGRPGDumpLayer::EndCopy()
     787             : 
     788             : {
     789         132 :     if (!m_bCopyActive)
     790         122 :         return OGRERR_NONE;
     791             : 
     792          10 :     m_bCopyActive = false;
     793             : 
     794          10 :     m_poDS->Log("\\.", false);
     795             : 
     796          10 :     m_bUseCopy = USE_COPY_UNSET;
     797             : 
     798          10 :     UpdateSequenceIfNeeded();
     799             : 
     800          10 :     return OGRERR_NONE;
     801             : }
     802             : 
     803             : /************************************************************************/
     804             : /*                       UpdateSequenceIfNeeded()                       */
     805             : /************************************************************************/
     806             : 
     807         268 : void OGRPGDumpLayer::UpdateSequenceIfNeeded()
     808             : {
     809         268 :     if (m_bNeedToUpdateSequence && m_pszFIDColumn != nullptr)
     810             :     {
     811          10 :         CPLString osCommand;
     812             :         osCommand.Printf(
     813             :             "SELECT setval(pg_get_serial_sequence(%s, %s), MAX(%s)) FROM %s",
     814          20 :             OGRPGDumpEscapeString(m_pszSqlTableName).c_str(),
     815          20 :             OGRPGDumpEscapeString(m_pszFIDColumn).c_str(),
     816          10 :             OGRPGDumpEscapeColumnName(m_pszFIDColumn).c_str(),
     817          30 :             m_pszSqlTableName);
     818          10 :         m_poDS->Log(osCommand);
     819          10 :         m_bNeedToUpdateSequence = false;
     820             :     }
     821         268 : }
     822             : 
     823             : /************************************************************************/
     824             : /*                          BuildCopyFields()                           */
     825             : /************************************************************************/
     826             : 
     827          10 : CPLString OGRPGDumpLayer::BuildCopyFields(int bSetFID)
     828             : {
     829          10 :     CPLString osFieldList;
     830             : 
     831          10 :     int nFIDIndex = -1;
     832          10 :     m_bFIDColumnInCopyFields = m_pszFIDColumn != nullptr && bSetFID;
     833          10 :     if (m_bFIDColumnInCopyFields)
     834             :     {
     835           3 :         nFIDIndex = m_poFeatureDefn->GetFieldIndex(m_pszFIDColumn);
     836             : 
     837           3 :         osFieldList += OGRPGDumpEscapeColumnName(m_pszFIDColumn);
     838             :     }
     839             : 
     840          34 :     const auto AddGeomFields = [this, &osFieldList]()
     841             :     {
     842          13 :         for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); i++)
     843             :         {
     844           3 :             if (!osFieldList.empty())
     845           2 :                 osFieldList += ", ";
     846             : 
     847           3 :             OGRGeomFieldDefn *poGFldDefn = m_poFeatureDefn->GetGeomFieldDefn(i);
     848             : 
     849           3 :             osFieldList += OGRPGDumpEscapeColumnName(poGFldDefn->GetNameRef());
     850             :         }
     851          10 :     };
     852             : 
     853          10 :     if (m_bGeomColumnPositionImmediate)
     854           8 :         AddGeomFields();
     855             : 
     856          46 :     for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
     857             :     {
     858          36 :         if (i == nFIDIndex)
     859           2 :             continue;
     860             : 
     861          34 :         const char *pszName = m_poFeatureDefn->GetFieldDefn(i)->GetNameRef();
     862             : 
     863          34 :         if (!osFieldList.empty())
     864          28 :             osFieldList += ", ";
     865             : 
     866          34 :         osFieldList += OGRPGDumpEscapeColumnName(pszName);
     867             :     }
     868             : 
     869          10 :     if (!m_bGeomColumnPositionImmediate)
     870           2 :         AddGeomFields();
     871             : 
     872          20 :     return osFieldList;
     873             : }
     874             : 
     875             : /************************************************************************/
     876             : /*                       OGRPGDumpEscapeColumnName( )                   */
     877             : /************************************************************************/
     878             : 
     879        1492 : CPLString OGRPGDumpEscapeColumnName(const char *pszColumnName)
     880             : {
     881        1492 :     CPLString osStr = "\"";
     882             : 
     883        1492 :     char ch = '\0';
     884       15326 :     for (int i = 0; (ch = pszColumnName[i]) != '\0'; i++)
     885             :     {
     886       13834 :         if (ch == '"')
     887          10 :             osStr.append(1, ch);
     888       13834 :         osStr.append(1, ch);
     889             :     }
     890             : 
     891        1492 :     osStr += "\"";
     892             : 
     893        1492 :     return osStr;
     894             : }
     895             : 
     896             : /************************************************************************/
     897             : /*                             EscapeString( )                          */
     898             : /************************************************************************/
     899             : 
     900         511 : CPLString OGRPGDumpEscapeString(const char *pszStrValue, int nMaxLength,
     901             :                                 const char *pszFieldName)
     902             : {
     903         511 :     CPLString osCommand;
     904             : 
     905             :     /* We need to quote and escape string fields. */
     906         511 :     osCommand += '\'';
     907             : 
     908         511 :     size_t nBytesLen = strlen(pszStrValue);
     909         515 :     if (nMaxLength > 0 &&
     910           4 :         CPLStrlenUTF8Ex(pszStrValue) > static_cast<size_t>(nMaxLength))
     911             :     {
     912           3 :         CPLDebug("PG", "Truncated %s field value, it was too long.",
     913             :                  pszFieldName);
     914             : 
     915           3 :         size_t iUTF8Char = 0;
     916          27 :         for (size_t iChar = 0; pszStrValue[iChar]; iChar++)
     917             :         {
     918          27 :             if ((reinterpret_cast<const unsigned char *>(pszStrValue)[iChar] &
     919             :                  0xc0) != 0x80)
     920             :             {
     921          18 :                 if (iUTF8Char == static_cast<size_t>(nMaxLength))
     922             :                 {
     923           3 :                     nBytesLen = iChar;
     924           3 :                     break;
     925             :                 }
     926          15 :                 iUTF8Char++;
     927             :             }
     928             :         }
     929             :     }
     930             : 
     931        5226 :     for (size_t i = 0; i < nBytesLen; i++)
     932             :     {
     933        4715 :         if (pszStrValue[i] == '\'')
     934             :         {
     935           0 :             osCommand += '\'';
     936           0 :             osCommand += '\'';
     937             :         }
     938             :         else
     939             :         {
     940        4715 :             osCommand += pszStrValue[i];
     941             :         }
     942             :     }
     943             : 
     944         511 :     osCommand += '\'';
     945             : 
     946         511 :     return osCommand;
     947             : }
     948             : 
     949             : /************************************************************************/
     950             : /*                    OGRPGDumpEscapeStringList( )                      */
     951             : /************************************************************************/
     952             : 
     953             : static CPLString
     954          12 : OGRPGDumpEscapeStringList(char **papszItems, bool bForInsertOrUpdate,
     955             :                           OGRPGCommonEscapeStringCbk pfnEscapeString,
     956             :                           void *userdata)
     957             : {
     958          12 :     bool bFirstItem = true;
     959          12 :     CPLString osStr;
     960          12 :     if (bForInsertOrUpdate)
     961           6 :         osStr += "ARRAY[";
     962             :     else
     963           6 :         osStr += "{";
     964          36 :     while (papszItems && *papszItems)
     965             :     {
     966          24 :         if (!bFirstItem)
     967             :         {
     968          12 :             osStr += ',';
     969             :         }
     970             : 
     971          24 :         char *pszStr = *papszItems;
     972          24 :         if (*pszStr != '\0')
     973             :         {
     974          24 :             if (bForInsertOrUpdate)
     975          12 :                 osStr += pfnEscapeString(userdata, pszStr, 0, "", "");
     976             :             else
     977             :             {
     978          12 :                 osStr += '"';
     979             : 
     980          32 :                 while (*pszStr)
     981             :                 {
     982          20 :                     if (*pszStr == '"')
     983           0 :                         osStr += "\\";
     984          20 :                     osStr += *pszStr;
     985          20 :                     pszStr++;
     986             :                 }
     987             : 
     988          12 :                 osStr += '"';
     989             :             }
     990             :         }
     991             :         else
     992           0 :             osStr += "NULL";
     993             : 
     994          24 :         bFirstItem = false;
     995             : 
     996          24 :         papszItems++;
     997             :     }
     998          12 :     if (bForInsertOrUpdate)
     999             :     {
    1000           6 :         osStr += "]";
    1001           6 :         if (papszItems == nullptr)
    1002           0 :             osStr += "::varchar[]";
    1003             :     }
    1004             :     else
    1005           6 :         osStr += "}";
    1006          12 :     return osStr;
    1007             : }
    1008             : 
    1009             : /************************************************************************/
    1010             : /*                          AppendFieldValue()                          */
    1011             : /*                                                                      */
    1012             : /* Used by CreateFeatureViaInsert() and SetFeature() to format a        */
    1013             : /* non-empty field value                                                */
    1014             : /************************************************************************/
    1015             : 
    1016        2630 : void OGRPGCommonAppendFieldValue(CPLString &osCommand, OGRFeature *poFeature,
    1017             :                                  int i,
    1018             :                                  OGRPGCommonEscapeStringCbk pfnEscapeString,
    1019             :                                  void *userdata)
    1020             : {
    1021        2630 :     if (poFeature->IsFieldNull(i))
    1022             :     {
    1023          11 :         osCommand += "NULL";
    1024          11 :         return;
    1025             :     }
    1026             : 
    1027        2619 :     OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
    1028        2619 :     OGRFieldType nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
    1029        2619 :     OGRFieldSubType eSubType = poFeatureDefn->GetFieldDefn(i)->GetSubType();
    1030             : 
    1031             :     // We need special formatting for integer list values.
    1032        2619 :     if (nOGRFieldType == OFTIntegerList)
    1033             :     {
    1034           8 :         int nCount, nOff = 0, j;
    1035           8 :         const int *panItems = poFeature->GetFieldAsIntegerList(i, &nCount);
    1036             : 
    1037           8 :         const size_t nLen = nCount * 13 + 10;
    1038           8 :         char *pszNeedToFree = static_cast<char *>(CPLMalloc(nLen));
    1039           8 :         strcpy(pszNeedToFree, "'{");
    1040          24 :         for (j = 0; j < nCount; j++)
    1041             :         {
    1042          16 :             if (j != 0)
    1043           8 :                 strcat(pszNeedToFree + nOff, ",");
    1044             : 
    1045          16 :             nOff += static_cast<int>(strlen(pszNeedToFree + nOff));
    1046          16 :             snprintf(pszNeedToFree + nOff, nLen - nOff, "%d", panItems[j]);
    1047             :         }
    1048           8 :         strcat(pszNeedToFree + nOff, "}'");
    1049             : 
    1050           8 :         osCommand += pszNeedToFree;
    1051           8 :         CPLFree(pszNeedToFree);
    1052             : 
    1053           8 :         return;
    1054             :     }
    1055             : 
    1056        2611 :     else if (nOGRFieldType == OFTInteger64List)
    1057             :     {
    1058           2 :         int nCount, nOff = 0, j;
    1059             :         const GIntBig *panItems =
    1060           2 :             poFeature->GetFieldAsInteger64List(i, &nCount);
    1061             : 
    1062           2 :         const size_t nLen = nCount * 26 + 10;
    1063           2 :         char *pszNeedToFree = static_cast<char *>(CPLMalloc(nLen));
    1064           2 :         strcpy(pszNeedToFree, "'{");
    1065           4 :         for (j = 0; j < nCount; j++)
    1066             :         {
    1067           2 :             if (j != 0)
    1068           0 :                 strcat(pszNeedToFree + nOff, ",");
    1069             : 
    1070           2 :             nOff += static_cast<int>(strlen(pszNeedToFree + nOff));
    1071           2 :             snprintf(pszNeedToFree + nOff, nLen - nOff, CPL_FRMT_GIB,
    1072           2 :                      panItems[j]);
    1073             :         }
    1074           2 :         strcat(pszNeedToFree + nOff, "}'");
    1075             : 
    1076           2 :         osCommand += pszNeedToFree;
    1077           2 :         CPLFree(pszNeedToFree);
    1078             : 
    1079           2 :         return;
    1080             :     }
    1081             : 
    1082             :     // We need special formatting for real list values.
    1083        2609 :     else if (nOGRFieldType == OFTRealList)
    1084             :     {
    1085           8 :         int nCount = 0;
    1086           8 :         int nOff = 0;
    1087           8 :         const double *padfItems = poFeature->GetFieldAsDoubleList(i, &nCount);
    1088             : 
    1089           8 :         const size_t nLen = nCount * 40 + 10;
    1090           8 :         char *pszNeedToFree = static_cast<char *>(CPLMalloc(nLen));
    1091           8 :         strcpy(pszNeedToFree, "'{");
    1092          24 :         for (int j = 0; j < nCount; j++)
    1093             :         {
    1094          16 :             if (j != 0)
    1095           8 :                 strcat(pszNeedToFree + nOff, ",");
    1096             : 
    1097          16 :             nOff += static_cast<int>(strlen(pszNeedToFree + nOff));
    1098             :             // Check for special values. They need to be quoted.
    1099          16 :             if (std::isnan(padfItems[j]))
    1100           0 :                 snprintf(pszNeedToFree + nOff, nLen - nOff, "NaN");
    1101          16 :             else if (std::isinf(padfItems[j]))
    1102           0 :                 snprintf(pszNeedToFree + nOff, nLen - nOff,
    1103           0 :                          (padfItems[j] > 0) ? "Infinity" : "-Infinity");
    1104             :             else
    1105          16 :                 CPLsnprintf(pszNeedToFree + nOff, nLen - nOff, "%.16g",
    1106          16 :                             padfItems[j]);
    1107             :         }
    1108           8 :         strcat(pszNeedToFree + nOff, "}'");
    1109             : 
    1110           8 :         osCommand += pszNeedToFree;
    1111           8 :         CPLFree(pszNeedToFree);
    1112             : 
    1113           8 :         return;
    1114             :     }
    1115             : 
    1116             :     // We need special formatting for string list values.
    1117        2601 :     else if (nOGRFieldType == OFTStringList)
    1118             :     {
    1119           6 :         char **papszItems = poFeature->GetFieldAsStringList(i);
    1120             : 
    1121          12 :         osCommand += OGRPGDumpEscapeStringList(papszItems, true,
    1122           6 :                                                pfnEscapeString, userdata);
    1123             : 
    1124           6 :         return;
    1125             :     }
    1126             : 
    1127             :     // Binary formatting
    1128        2595 :     else if (nOGRFieldType == OFTBinary)
    1129             :     {
    1130           3 :         osCommand += "E'";
    1131             : 
    1132           3 :         int nLen = 0;
    1133           3 :         GByte *pabyData = poFeature->GetFieldAsBinary(i, &nLen);
    1134           3 :         char *pszBytea = OGRPGCommonGByteArrayToBYTEA(pabyData, nLen);
    1135             : 
    1136           3 :         osCommand += pszBytea;
    1137             : 
    1138           3 :         CPLFree(pszBytea);
    1139           3 :         osCommand += "'";
    1140             : 
    1141           3 :         return;
    1142             :     }
    1143             : 
    1144             :     // Flag indicating NULL or not-a-date date value
    1145             :     // e.g. 0000-00-00 - there is no year 0
    1146        2592 :     bool bIsDateNull = false;
    1147             : 
    1148        2592 :     const char *pszStrValue = poFeature->GetFieldAsString(i);
    1149             : 
    1150             :     // Check if date is NULL: 0000-00-00
    1151        2592 :     if (nOGRFieldType == OFTDate)
    1152             :     {
    1153          36 :         if (STARTS_WITH_CI(pszStrValue, "0000"))
    1154             :         {
    1155           0 :             pszStrValue = "NULL";
    1156           0 :             bIsDateNull = true;
    1157             :         }
    1158             :     }
    1159        2556 :     else if (nOGRFieldType == OFTReal)
    1160             :     {
    1161             :         // Check for special values. They need to be quoted.
    1162         151 :         double dfVal = poFeature->GetFieldAsDouble(i);
    1163         151 :         if (std::isnan(dfVal))
    1164           0 :             pszStrValue = "'NaN'";
    1165         151 :         else if (std::isinf(dfVal))
    1166           0 :             pszStrValue = (dfVal > 0) ? "'Infinity'" : "'-Infinity'";
    1167             :     }
    1168        2405 :     else if ((nOGRFieldType == OFTInteger || nOGRFieldType == OFTInteger64) &&
    1169             :              eSubType == OFSTBoolean)
    1170           2 :         pszStrValue = poFeature->GetFieldAsInteger(i) ? "'t'" : "'f'";
    1171             : 
    1172        2592 :     if (nOGRFieldType != OFTInteger && nOGRFieldType != OFTInteger64 &&
    1173         293 :         nOGRFieldType != OFTReal && nOGRFieldType != OFTStringList &&
    1174         293 :         !bIsDateNull)
    1175             :     {
    1176         879 :         osCommand += pfnEscapeString(
    1177         293 :             userdata, pszStrValue, poFeatureDefn->GetFieldDefn(i)->GetWidth(),
    1178         293 :             poFeatureDefn->GetName(),
    1179         586 :             poFeatureDefn->GetFieldDefn(i)->GetNameRef());
    1180             :     }
    1181             :     else
    1182             :     {
    1183        2299 :         osCommand += pszStrValue;
    1184             :     }
    1185             : }
    1186             : 
    1187             : /************************************************************************/
    1188             : /*                      OGRPGCommonGByteArrayToBYTEA()                  */
    1189             : /************************************************************************/
    1190             : 
    1191         835 : char *OGRPGCommonGByteArrayToBYTEA(const GByte *pabyData, size_t nLen)
    1192             : {
    1193         835 :     if (nLen > (std::numeric_limits<size_t>::max() - 1) / 5)
    1194             :     {
    1195           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Too big byte array");
    1196           0 :         return CPLStrdup("");
    1197             :     }
    1198         835 :     const size_t nTextBufLen = nLen * 5 + 1;
    1199         835 :     char *pszTextBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(nTextBufLen));
    1200         835 :     if (pszTextBuf == nullptr)
    1201           0 :         return CPLStrdup("");
    1202             : 
    1203         835 :     size_t iDst = 0;
    1204             : 
    1205      197821 :     for (size_t iSrc = 0; iSrc < nLen; iSrc++)
    1206             :     {
    1207      196986 :         if (pabyData[iSrc] < 40 || pabyData[iSrc] > 126 ||
    1208       47692 :             pabyData[iSrc] == '\\')
    1209             :         {
    1210      149452 :             snprintf(pszTextBuf + iDst, nTextBufLen - iDst, "\\\\%03o",
    1211      149452 :                      pabyData[iSrc]);
    1212      149452 :             iDst += 5;
    1213             :         }
    1214             :         else
    1215       47534 :             pszTextBuf[iDst++] = pabyData[iSrc];
    1216             :     }
    1217         835 :     pszTextBuf[iDst] = '\0';
    1218             : 
    1219         835 :     return pszTextBuf;
    1220             : }
    1221             : 
    1222             : /************************************************************************/
    1223             : /*                       OGRPGCommonLayerGetType()                      */
    1224             : /************************************************************************/
    1225             : 
    1226         772 : CPLString OGRPGCommonLayerGetType(const OGRFieldDefn &oField,
    1227             :                                   bool bPreservePrecision, bool bApproxOK)
    1228             : {
    1229         772 :     const char *pszFieldType = "";
    1230             : 
    1231             :     /* -------------------------------------------------------------------- */
    1232             :     /*      Work out the PostgreSQL type.                                   */
    1233             :     /* -------------------------------------------------------------------- */
    1234         772 :     if (oField.GetType() == OFTInteger)
    1235             :     {
    1236         130 :         if (oField.GetSubType() == OFSTBoolean)
    1237          14 :             pszFieldType = "BOOLEAN";
    1238         116 :         else if (oField.GetSubType() == OFSTInt16)
    1239          12 :             pszFieldType = "SMALLINT";
    1240         104 :         else if (oField.GetWidth() > 0 && bPreservePrecision)
    1241           4 :             pszFieldType = CPLSPrintf("NUMERIC(%d,0)", oField.GetWidth());
    1242             :         else
    1243         100 :             pszFieldType = "INTEGER";
    1244             :     }
    1245         642 :     else if (oField.GetType() == OFTInteger64)
    1246             :     {
    1247          12 :         if (oField.GetWidth() > 0 && bPreservePrecision)
    1248           0 :             pszFieldType = CPLSPrintf("NUMERIC(%d,0)", oField.GetWidth());
    1249             :         else
    1250          12 :             pszFieldType = "INT8";
    1251             :     }
    1252         630 :     else if (oField.GetType() == OFTReal)
    1253             :     {
    1254         118 :         if (oField.GetSubType() == OFSTFloat32)
    1255          16 :             pszFieldType = "REAL";
    1256         102 :         else if (oField.GetWidth() > 0 && oField.GetPrecision() > 0 &&
    1257             :                  bPreservePrecision)
    1258           7 :             pszFieldType = CPLSPrintf("NUMERIC(%d,%d)", oField.GetWidth(),
    1259             :                                       oField.GetPrecision());
    1260             :         else
    1261          95 :             pszFieldType = "FLOAT8";
    1262             :     }
    1263         512 :     else if (oField.GetType() == OFTString)
    1264             :     {
    1265         305 :         if (oField.GetSubType() == OFSTJSON)
    1266           2 :             pszFieldType = CPLGetConfigOption("OGR_PG_JSON_TYPE", "JSON");
    1267         303 :         else if (oField.GetSubType() == OFSTUUID)
    1268           2 :             pszFieldType = CPLGetConfigOption("OGR_PG_UUID_TYPE", "UUID");
    1269         301 :         else if (oField.GetWidth() > 0 && oField.GetWidth() < 10485760 &&
    1270             :                  bPreservePrecision)
    1271          77 :             pszFieldType = CPLSPrintf("VARCHAR(%d)", oField.GetWidth());
    1272             :         else
    1273         224 :             pszFieldType = CPLGetConfigOption("OGR_PG_STRING_TYPE", "VARCHAR");
    1274             :     }
    1275         207 :     else if (oField.GetType() == OFTIntegerList)
    1276             :     {
    1277          32 :         if (oField.GetSubType() == OFSTBoolean)
    1278          12 :             pszFieldType = "BOOLEAN[]";
    1279          20 :         else if (oField.GetSubType() == OFSTInt16)
    1280          12 :             pszFieldType = "INT2[]";
    1281             :         else
    1282           8 :             pszFieldType = "INTEGER[]";
    1283             :     }
    1284         175 :     else if (oField.GetType() == OFTInteger64List)
    1285             :     {
    1286          12 :         pszFieldType = "INT8[]";
    1287             :     }
    1288         163 :     else if (oField.GetType() == OFTRealList)
    1289             :     {
    1290          82 :         if (oField.GetSubType() == OFSTFloat32)
    1291          12 :             pszFieldType = "REAL[]";
    1292             :         else
    1293          70 :             pszFieldType = "FLOAT8[]";
    1294             :     }
    1295          81 :     else if (oField.GetType() == OFTStringList)
    1296             :     {
    1297          12 :         pszFieldType = "varchar[]";
    1298             :     }
    1299          69 :     else if (oField.GetType() == OFTDate)
    1300             :     {
    1301          24 :         pszFieldType = "date";
    1302             :     }
    1303          45 :     else if (oField.GetType() == OFTTime)
    1304             :     {
    1305           8 :         pszFieldType = "time";
    1306             :     }
    1307          37 :     else if (oField.GetType() == OFTDateTime)
    1308             :     {
    1309          31 :         pszFieldType = "timestamp with time zone";
    1310             :     }
    1311           6 :     else if (oField.GetType() == OFTBinary)
    1312             :     {
    1313           6 :         pszFieldType = "bytea";
    1314             :     }
    1315           0 :     else if (bApproxOK)
    1316             :     {
    1317           0 :         CPLError(CE_Warning, CPLE_NotSupported,
    1318             :                  "Can't create field %s with type %s on PostgreSQL layers.  "
    1319             :                  "Creating as VARCHAR.",
    1320             :                  oField.GetNameRef(),
    1321             :                  OGRFieldDefn::GetFieldTypeName(oField.GetType()));
    1322           0 :         pszFieldType = "VARCHAR";
    1323             :     }
    1324             :     else
    1325             :     {
    1326           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1327             :                  "Can't create field %s with type %s on PostgreSQL layers.",
    1328             :                  oField.GetNameRef(),
    1329             :                  OGRFieldDefn::GetFieldTypeName(oField.GetType()));
    1330             :     }
    1331             : 
    1332        1544 :     return pszFieldType;
    1333             : }
    1334             : 
    1335             : /************************************************************************/
    1336             : /*                         OGRPGCommonLayerSetType()                    */
    1337             : /************************************************************************/
    1338             : 
    1339         797 : bool OGRPGCommonLayerSetType(OGRFieldDefn &oField, const char *pszType,
    1340             :                              const char *pszFormatType, int nWidth)
    1341             : {
    1342         797 :     if (EQUAL(pszType, "text"))
    1343             :     {
    1344           9 :         oField.SetType(OFTString);
    1345             :     }
    1346         788 :     else if (EQUAL(pszType, "_bpchar") || EQUAL(pszType, "_varchar") ||
    1347         762 :              EQUAL(pszType, "_text"))
    1348             :     {
    1349          33 :         oField.SetType(OFTStringList);
    1350             :     }
    1351         755 :     else if (EQUAL(pszType, "bpchar") || EQUAL(pszType, "varchar"))
    1352             :     {
    1353         270 :         if (nWidth == -1)
    1354             :         {
    1355         261 :             if (STARTS_WITH_CI(pszFormatType, "character("))
    1356          36 :                 nWidth = atoi(pszFormatType + 10);
    1357         225 :             else if (STARTS_WITH_CI(pszFormatType, "character varying("))
    1358          54 :                 nWidth = atoi(pszFormatType + 18);
    1359             :             else
    1360         171 :                 nWidth = 0;
    1361             :         }
    1362         270 :         oField.SetType(OFTString);
    1363         270 :         oField.SetWidth(nWidth);
    1364             :     }
    1365         485 :     else if (EQUAL(pszType, "bool"))
    1366             :     {
    1367          20 :         oField.SetType(OFTInteger);
    1368          20 :         oField.SetSubType(OFSTBoolean);
    1369          20 :         oField.SetWidth(1);
    1370             :     }
    1371         465 :     else if (EQUAL(pszType, "_numeric"))
    1372             :     {
    1373          21 :         if (EQUAL(pszFormatType, "numeric[]"))
    1374           7 :             oField.SetType(OFTRealList);
    1375             :         else
    1376             :         {
    1377          14 :             const char *pszPrecision = strstr(pszFormatType, ",");
    1378          14 :             int nPrecision = 0;
    1379             : 
    1380          14 :             nWidth = atoi(pszFormatType + 8);
    1381          14 :             if (pszPrecision != nullptr)
    1382          14 :                 nPrecision = atoi(pszPrecision + 1);
    1383             : 
    1384          14 :             if (nPrecision == 0)
    1385             :             {
    1386           7 :                 if (nWidth >= 10)
    1387           0 :                     oField.SetType(OFTInteger64List);
    1388             :                 else
    1389           7 :                     oField.SetType(OFTIntegerList);
    1390             :             }
    1391             :             else
    1392           7 :                 oField.SetType(OFTRealList);
    1393             : 
    1394          14 :             oField.SetWidth(nWidth);
    1395          14 :             oField.SetPrecision(nPrecision);
    1396             :         }
    1397             :     }
    1398         444 :     else if (EQUAL(pszType, "numeric"))
    1399             :     {
    1400          31 :         if (EQUAL(pszFormatType, "numeric"))
    1401           7 :             oField.SetType(OFTReal);
    1402             :         else
    1403             :         {
    1404          24 :             const char *pszPrecision = strstr(pszFormatType, ",");
    1405          24 :             int nPrecision = 0;
    1406             : 
    1407          24 :             nWidth = atoi(pszFormatType + 8);
    1408          24 :             if (pszPrecision != nullptr)
    1409          24 :                 nPrecision = atoi(pszPrecision + 1);
    1410             : 
    1411          24 :             if (nPrecision == 0)
    1412             :             {
    1413          12 :                 if (nWidth >= 10)
    1414           1 :                     oField.SetType(OFTInteger64);
    1415             :                 else
    1416          11 :                     oField.SetType(OFTInteger);
    1417             :             }
    1418             :             else
    1419          12 :                 oField.SetType(OFTReal);
    1420             : 
    1421          24 :             oField.SetWidth(nWidth);
    1422          24 :             oField.SetPrecision(nPrecision);
    1423             :         }
    1424             :     }
    1425         413 :     else if (EQUAL(pszFormatType, "integer[]"))
    1426             :     {
    1427          15 :         oField.SetType(OFTIntegerList);
    1428             :     }
    1429         398 :     else if (EQUAL(pszFormatType, "smallint[]"))
    1430             :     {
    1431          11 :         oField.SetType(OFTIntegerList);
    1432          11 :         oField.SetSubType(OFSTInt16);
    1433             :     }
    1434         387 :     else if (EQUAL(pszFormatType, "boolean[]"))
    1435             :     {
    1436          11 :         oField.SetType(OFTIntegerList);
    1437          11 :         oField.SetSubType(OFSTBoolean);
    1438             :     }
    1439         376 :     else if (EQUAL(pszFormatType, "float[]") || EQUAL(pszFormatType, "real[]"))
    1440             :     {
    1441          11 :         oField.SetType(OFTRealList);
    1442          11 :         oField.SetSubType(OFSTFloat32);
    1443             :     }
    1444         365 :     else if (EQUAL(pszFormatType, "double precision[]"))
    1445             :     {
    1446          51 :         oField.SetType(OFTRealList);
    1447             :     }
    1448         314 :     else if (EQUAL(pszType, "int2"))
    1449             :     {
    1450          11 :         oField.SetType(OFTInteger);
    1451          11 :         oField.SetSubType(OFSTInt16);
    1452          11 :         oField.SetWidth(5);
    1453             :     }
    1454         303 :     else if (EQUAL(pszType, "int8"))
    1455             :     {
    1456          12 :         oField.SetType(OFTInteger64);
    1457             :     }
    1458         291 :     else if (EQUAL(pszFormatType, "bigint[]"))
    1459             :     {
    1460          11 :         oField.SetType(OFTInteger64List);
    1461             :     }
    1462         280 :     else if (STARTS_WITH_CI(pszType, "int"))
    1463             :     {
    1464          94 :         oField.SetType(OFTInteger);
    1465             :     }
    1466         186 :     else if (EQUAL(pszType, "float4"))
    1467             :     {
    1468          22 :         oField.SetType(OFTReal);
    1469          22 :         oField.SetSubType(OFSTFloat32);
    1470             :     }
    1471         164 :     else if (STARTS_WITH_CI(pszType, "float") ||
    1472         104 :              STARTS_WITH_CI(pszType, "double") || EQUAL(pszType, "real"))
    1473             :     {
    1474          60 :         oField.SetType(OFTReal);
    1475             :     }
    1476         104 :     else if (STARTS_WITH_CI(pszType, "timestamp"))
    1477             :     {
    1478          43 :         oField.SetType(OFTDateTime);
    1479             :     }
    1480          61 :     else if (STARTS_WITH_CI(pszType, "date"))
    1481             :     {
    1482          17 :         oField.SetType(OFTDate);
    1483             :     }
    1484          44 :     else if (STARTS_WITH_CI(pszType, "time"))
    1485             :     {
    1486          17 :         oField.SetType(OFTTime);
    1487             :     }
    1488          27 :     else if (EQUAL(pszType, "bytea"))
    1489             :     {
    1490          11 :         oField.SetType(OFTBinary);
    1491             :     }
    1492          16 :     else if (EQUAL(pszType, "json") || EQUAL(pszType, "jsonb"))
    1493             :     {
    1494           2 :         oField.SetType(OFTString);
    1495           2 :         oField.SetSubType(OFSTJSON);
    1496             :     }
    1497          14 :     else if (EQUAL(pszType, "uuid"))
    1498             :     {
    1499           2 :         oField.SetType(OFTString);
    1500           2 :         oField.SetSubType(OFSTUUID);
    1501             :     }
    1502             :     else
    1503             :     {
    1504          12 :         CPLDebug("PGCommon", "Field %s is of unknown format type %s (type=%s).",
    1505             :                  oField.GetNameRef(), pszFormatType, pszType);
    1506          12 :         return false;
    1507             :     }
    1508         785 :     return true;
    1509             : }
    1510             : 
    1511             : /************************************************************************/
    1512             : /*                  OGRPGCommonLayerNormalizeDefault()                  */
    1513             : /************************************************************************/
    1514             : 
    1515          30 : void OGRPGCommonLayerNormalizeDefault(OGRFieldDefn *poFieldDefn,
    1516             :                                       const char *pszDefault)
    1517             : {
    1518          30 :     if (pszDefault == nullptr)
    1519           0 :         return;
    1520          60 :     CPLString osDefault(pszDefault);
    1521          30 :     size_t nPos = osDefault.find("::character varying");
    1522          32 :     if (nPos != std::string::npos &&
    1523           2 :         nPos + strlen("::character varying") == osDefault.size())
    1524             :     {
    1525           2 :         osDefault.resize(nPos);
    1526             :     }
    1527          28 :     else if ((nPos = osDefault.find("::text")) != std::string::npos &&
    1528           0 :              nPos + strlen("::text") == osDefault.size())
    1529             :     {
    1530           0 :         osDefault.resize(nPos);
    1531             :     }
    1532          28 :     else if (strcmp(osDefault, "now()") == 0)
    1533           0 :         osDefault = "CURRENT_TIMESTAMP";
    1534          28 :     else if (strcmp(osDefault, "('now'::text)::date") == 0)
    1535           0 :         osDefault = "CURRENT_DATE";
    1536          28 :     else if (strcmp(osDefault, "('now'::text)::time with time zone") == 0)
    1537           0 :         osDefault = "CURRENT_TIME";
    1538             :     else
    1539             :     {
    1540          28 :         nPos = osDefault.find("::timestamp with time zone");
    1541          28 :         if (poFieldDefn->GetType() == OFTDateTime && nPos != std::string::npos)
    1542             :         {
    1543           4 :             osDefault.resize(nPos);
    1544           4 :             nPos = osDefault.find("'+");
    1545           4 :             if (nPos != std::string::npos)
    1546             :             {
    1547           0 :                 osDefault.resize(nPos);
    1548           0 :                 osDefault += "'";
    1549             :             }
    1550           4 :             int nYear = 0;
    1551           4 :             int nMonth = 0;
    1552           4 :             int nDay = 0;
    1553           4 :             int nHour = 0;
    1554           4 :             int nMinute = 0;
    1555           4 :             float fSecond = 0.0f;
    1556           4 :             if (sscanf(osDefault, "'%d-%d-%d %d:%d:%f'", &nYear, &nMonth, &nDay,
    1557           4 :                        &nHour, &nMinute, &fSecond) == 6 ||
    1558           0 :                 sscanf(osDefault, "'%d-%d-%d %d:%d:%f+00'", &nYear, &nMonth,
    1559             :                        &nDay, &nHour, &nMinute, &fSecond) == 6)
    1560             :             {
    1561           4 :                 if (osDefault.find('.') == std::string::npos)
    1562             :                     osDefault = CPLSPrintf("'%04d/%02d/%02d %02d:%02d:%02d'",
    1563             :                                            nYear, nMonth, nDay, nHour, nMinute,
    1564           2 :                                            static_cast<int>(fSecond + 0.5f));
    1565             :                 else
    1566             :                     osDefault =
    1567             :                         CPLSPrintf("'%04d/%02d/%02d %02d:%02d:%06.3f'", nYear,
    1568           2 :                                    nMonth, nDay, nHour, nMinute, fSecond);
    1569             :             }
    1570             :         }
    1571             :     }
    1572          30 :     poFieldDefn->SetDefault(osDefault);
    1573             : }
    1574             : 
    1575             : /************************************************************************/
    1576             : /*                     OGRPGCommonLayerGetPGDefault()                   */
    1577             : /************************************************************************/
    1578             : 
    1579          16 : CPLString OGRPGCommonLayerGetPGDefault(OGRFieldDefn *poFieldDefn)
    1580             : {
    1581          16 :     CPLString osRet = poFieldDefn->GetDefault();
    1582          16 :     int nYear = 0;
    1583          16 :     int nMonth = 0;
    1584          16 :     int nDay = 0;
    1585          16 :     int nHour = 0;
    1586          16 :     int nMinute = 0;
    1587          16 :     float fSecond = 0.0f;
    1588          16 :     if (sscanf(osRet, "'%d/%d/%d %d:%d:%f'", &nYear, &nMonth, &nDay, &nHour,
    1589          16 :                &nMinute, &fSecond) == 6)
    1590             :     {
    1591           3 :         osRet.pop_back();
    1592           3 :         osRet += "+00'::timestamp with time zone";
    1593             :     }
    1594          32 :     return osRet;
    1595             : }
    1596             : 
    1597             : /************************************************************************/
    1598             : /*                OGRPGCommonGenerateShortEnoughIdentifier()            */
    1599             : /************************************************************************/
    1600             : 
    1601           5 : std::string OGRPGCommonGenerateShortEnoughIdentifier(const char *pszIdentifier)
    1602             : {
    1603           5 :     if (CPLStrlenUTF8Ex(pszIdentifier) <=
    1604             :         static_cast<size_t>(OGR_PG_NAMEDATALEN - 1))
    1605           3 :         return pszIdentifier;
    1606             : 
    1607             :     // Truncate string by making sure we don't cut in the
    1608             :     // middle of a UTF-8 multibyte character
    1609             :     // Continuation bytes of such characters are of the form
    1610             :     // 10xxxxxx (0x80), whereas single-byte are 0xxxxxxx
    1611             :     // and the start of a multi-byte is 11xxxxxx
    1612           4 :     std::string osRet;
    1613           2 :     constexpr int FIRST_8_CHARS_OF_MD5 = 8;
    1614           2 :     int iUTF8Char = 0;
    1615         159 :     for (size_t i = 0; pszIdentifier[i]; ++i)
    1616             :     {
    1617         159 :         if ((pszIdentifier[i] & 0xc0) != 0x80)
    1618             :         {
    1619         110 :             ++iUTF8Char;
    1620         110 :             if (iUTF8Char == OGR_PG_NAMEDATALEN - 1 - FIRST_8_CHARS_OF_MD5)
    1621           2 :                 break;
    1622             :         }
    1623         157 :         osRet += pszIdentifier[i];
    1624             :     }
    1625           2 :     osRet += '_';
    1626           2 :     osRet += std::string(CPLMD5String(pszIdentifier), FIRST_8_CHARS_OF_MD5);
    1627           2 :     return osRet;
    1628             : }
    1629             : 
    1630             : /************************************************************************/
    1631             : /*                 OGRPGCommonGenerateSpatialIndexName()                 */
    1632             : /************************************************************************/
    1633             : 
    1634             : /** Generates the name of the spatial index on table pszTableName
    1635             :  * using pszGeomFieldName, such that it fits in OGR_PG_NAMEDATALEN - 1 bytes.
    1636             :  * The index of the geometry field may be used if the geometry field name
    1637             :  * is too long.
    1638             :  */
    1639         239 : std::string OGRPGCommonGenerateSpatialIndexName(const char *pszTableName,
    1640             :                                                 const char *pszGeomFieldName,
    1641             :                                                 int nGeomFieldIdx)
    1642             : {
    1643             :     // Nominal case: use full table and geometry field name
    1644         265 :     for (const char *pszSuffix : {"_geom_idx", "_idx"})
    1645             :     {
    1646         252 :         if (CPLStrlenUTF8Ex(pszTableName) + 1 +
    1647         252 :                 CPLStrlenUTF8Ex(pszGeomFieldName) + strlen(pszSuffix) <=
    1648             :             static_cast<size_t>(OGR_PG_NAMEDATALEN - 1))
    1649             :         {
    1650         452 :             std::string osRet(pszTableName);
    1651         226 :             osRet += '_';
    1652         226 :             osRet += pszGeomFieldName;
    1653         226 :             osRet += pszSuffix;
    1654         226 :             return osRet;
    1655             :         }
    1656             :     }
    1657             : 
    1658             :     // Slightly degraded case: use table name and geometry field index
    1659          26 :     const std::string osGeomFieldIdx(CPLSPrintf("%d", nGeomFieldIdx));
    1660          13 :     if (CPLStrlenUTF8Ex(pszTableName) + 1 + osGeomFieldIdx.size() +
    1661          13 :             strlen("_geom_idx") <=
    1662             :         static_cast<size_t>(OGR_PG_NAMEDATALEN - 1))
    1663             :     {
    1664           6 :         std::string osRet(pszTableName);
    1665           3 :         osRet += '_';
    1666           3 :         osRet += osGeomFieldIdx;
    1667           3 :         osRet += "_geom_idx";
    1668           3 :         return osRet;
    1669             :     }
    1670             : 
    1671             :     // Fallback case: use first characters of table name,
    1672             :     // first 8 chars of its MD5 and then the geometry field index.
    1673          10 :     constexpr int FIRST_8_CHARS_OF_MD5 = 8;
    1674          20 :     std::string osSuffix("_");
    1675          10 :     osSuffix += std::string(CPLMD5String(pszTableName), FIRST_8_CHARS_OF_MD5);
    1676          10 :     osSuffix += '_';
    1677          10 :     osSuffix += osGeomFieldIdx;
    1678          10 :     osSuffix += "_geom_idx";
    1679             : 
    1680          20 :     std::string osRet;
    1681          10 :     size_t iUTF8Char = 0;
    1682         516 :     for (size_t i = 0; pszTableName[i]; ++i)
    1683             :     {
    1684         516 :         if ((pszTableName[i] & 0xc0) != 0x80)
    1685             :         {
    1686         440 :             ++iUTF8Char;
    1687         440 :             if (iUTF8Char == OGR_PG_NAMEDATALEN - osSuffix.size())
    1688          10 :                 break;
    1689             :         }
    1690         506 :         osRet += pszTableName[i];
    1691             :     }
    1692          10 :     osRet += osSuffix;
    1693          10 :     return osRet;
    1694             : }
    1695             : 
    1696             : /************************************************************************/
    1697             : /*                           GetNextFeature()                           */
    1698             : /************************************************************************/
    1699             : 
    1700         160 : OGRErr OGRPGDumpLayer::CreateField(const OGRFieldDefn *poFieldIn, int bApproxOK)
    1701             : {
    1702         160 :     if (m_poFeatureDefn->GetFieldCount() +
    1703         160 :             m_poFeatureDefn->GetGeomFieldCount() ==
    1704             :         1600)
    1705             :     {
    1706           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1707             :                  "Maximum number of fields supported is 1600.");
    1708           0 :         return OGRERR_FAILURE;
    1709             :     }
    1710             : 
    1711         320 :     CPLString osFieldType;
    1712         320 :     OGRFieldDefn oField(poFieldIn);
    1713             : 
    1714             :     // Can be set to NO to test ogr2ogr default behavior
    1715             :     const bool bAllowCreationOfFieldWithFIDName =
    1716         160 :         CPLTestBool(CPLGetConfigOption(
    1717             :             "PGDUMP_DEBUG_ALLOW_CREATION_FIELD_WITH_FID_NAME", "YES"));
    1718             : 
    1719         158 :     if (bAllowCreationOfFieldWithFIDName && m_pszFIDColumn != nullptr &&
    1720         160 :         EQUAL(oField.GetNameRef(), m_pszFIDColumn) &&
    1721         322 :         oField.GetType() != OFTInteger && oField.GetType() != OFTInteger64)
    1722             :     {
    1723           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s",
    1724             :                  oField.GetNameRef());
    1725           2 :         return OGRERR_FAILURE;
    1726             :     }
    1727             : 
    1728             :     /* -------------------------------------------------------------------- */
    1729             :     /*      Do we want to "launder" the column names into Postgres          */
    1730             :     /*      friendly format?                                                */
    1731             :     /* -------------------------------------------------------------------- */
    1732         158 :     if (m_bLaunderColumnNames)
    1733             :     {
    1734         155 :         char *pszSafeName = OGRPGCommonLaunderName(oField.GetNameRef(),
    1735         155 :                                                    "PGDump", m_bUTF8ToASCII);
    1736             : 
    1737         155 :         oField.SetName(pszSafeName);
    1738         155 :         CPLFree(pszSafeName);
    1739             : 
    1740         155 :         if (EQUAL(oField.GetNameRef(), "oid"))
    1741             :         {
    1742           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1743             :                      "Renaming field 'oid' to 'oid_' to avoid conflict with "
    1744             :                      "internal oid field.");
    1745           0 :             oField.SetName("oid_");
    1746             :         }
    1747             :     }
    1748             : 
    1749             :     const char *pszOverrideType =
    1750         158 :         m_apszOverrideColumnTypes.FetchNameValue(oField.GetNameRef());
    1751         158 :     if (pszOverrideType != nullptr)
    1752             :     {
    1753           0 :         osFieldType = pszOverrideType;
    1754             :     }
    1755             :     else
    1756             :     {
    1757         316 :         osFieldType = OGRPGCommonLayerGetType(oField, m_bPreservePrecision,
    1758         316 :                                               CPL_TO_BOOL(bApproxOK));
    1759         158 :         if (osFieldType.empty())
    1760           0 :             return OGRERR_FAILURE;
    1761             :     }
    1762             : 
    1763             :     /* -------------------------------------------------------------------- */
    1764             :     /*      Create the new field.                                           */
    1765             :     /* -------------------------------------------------------------------- */
    1766         158 :     CPLString osCommand;
    1767             :     osCommand.Printf("ALTER TABLE %s ADD COLUMN %s %s", m_pszSqlTableName,
    1768         316 :                      OGRPGDumpEscapeColumnName(oField.GetNameRef()).c_str(),
    1769         316 :                      osFieldType.c_str());
    1770         158 :     if (!oField.IsNullable())
    1771           2 :         osCommand += " NOT NULL";
    1772         158 :     if (oField.IsUnique())
    1773           1 :         osCommand += " UNIQUE";
    1774         158 :     if (oField.GetDefault() != nullptr && !oField.IsDefaultDriverSpecific())
    1775             :     {
    1776           7 :         osCommand += " DEFAULT ";
    1777           7 :         osCommand += OGRPGCommonLayerGetPGDefault(&oField);
    1778             :     }
    1779             : 
    1780         158 :     m_poFeatureDefn->AddFieldDefn(&oField);
    1781             : 
    1782         312 :     if (bAllowCreationOfFieldWithFIDName && m_pszFIDColumn != nullptr &&
    1783         154 :         EQUAL(oField.GetNameRef(), m_pszFIDColumn))
    1784             :     {
    1785           2 :         m_iFIDAsRegularColumnIndex = m_poFeatureDefn->GetFieldCount() - 1;
    1786             :     }
    1787         156 :     else if (m_bCreateTable)
    1788             :     {
    1789         312 :         const auto Log = [this](const std::string &osSQL)
    1790             :         {
    1791         156 :             if (m_bGeomColumnPositionImmediate)
    1792         151 :                 m_poDS->Log(osSQL.c_str());
    1793             :             else
    1794           5 :                 m_aosDeferrentNonGeomFieldCreationCommands.push_back(osSQL);
    1795         311 :         };
    1796             : 
    1797         155 :         Log(osCommand);
    1798             : 
    1799         155 :         if (!oField.GetComment().empty())
    1800             :         {
    1801           2 :             std::string osCommentON;
    1802           1 :             osCommentON = "COMMENT ON COLUMN ";
    1803           1 :             osCommentON += m_pszSqlTableName;
    1804           1 :             osCommentON += '.';
    1805           1 :             osCommentON += OGRPGDumpEscapeColumnName(oField.GetNameRef());
    1806           1 :             osCommentON += " IS ";
    1807           1 :             osCommentON += OGRPGDumpEscapeString(oField.GetComment().c_str());
    1808           1 :             Log(osCommentON);
    1809             :         }
    1810             :     }
    1811             : 
    1812         158 :     return OGRERR_NONE;
    1813             : }
    1814             : 
    1815             : /************************************************************************/
    1816             : /*                           CreateGeomField()                          */
    1817             : /************************************************************************/
    1818             : 
    1819          20 : OGRErr OGRPGDumpLayer::CreateGeomField(const OGRGeomFieldDefn *poGeomFieldIn,
    1820             :                                        int /* bApproxOK */)
    1821             : {
    1822          20 :     if (m_poFeatureDefn->GetFieldCount() +
    1823          20 :             m_poFeatureDefn->GetGeomFieldCount() ==
    1824             :         1600)
    1825             :     {
    1826           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1827             :                  "Maximum number of fields supported is 1600.");
    1828           0 :         return OGRERR_FAILURE;
    1829             :     }
    1830             : 
    1831          20 :     OGRwkbGeometryType eType = poGeomFieldIn->GetType();
    1832          20 :     if (eType == wkbNone)
    1833             :     {
    1834           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1835             :                  "Cannot create geometry field of type wkbNone");
    1836           0 :         return OGRERR_FAILURE;
    1837             :     }
    1838             : 
    1839             :     // Check if GEOMETRY_NAME layer creation option was set, but no initial
    1840             :     // column was created in ICreateLayer()
    1841             :     const CPLString osGeomFieldName =
    1842          20 :         !m_osFirstGeometryFieldName.empty()
    1843           6 :             ? m_osFirstGeometryFieldName
    1844          40 :             : CPLString(poGeomFieldIn->GetNameRef());
    1845             : 
    1846          20 :     m_osFirstGeometryFieldName = "";  // reset for potential next geom columns
    1847             : 
    1848          40 :     OGRGeomFieldDefn oTmpGeomFieldDefn(poGeomFieldIn);
    1849          20 :     oTmpGeomFieldDefn.SetName(osGeomFieldName);
    1850             : 
    1851          40 :     CPLString osCommand;
    1852             :     auto poGeomField =
    1853          20 :         std::make_unique<OGRPGDumpGeomFieldDefn>(&oTmpGeomFieldDefn);
    1854             : 
    1855             :     /* -------------------------------------------------------------------- */
    1856             :     /*      Do we want to "launder" the column names into Postgres          */
    1857             :     /*      friendly format?                                                */
    1858             :     /* -------------------------------------------------------------------- */
    1859          20 :     if (m_bLaunderColumnNames)
    1860             :     {
    1861          20 :         char *pszSafeName = OGRPGCommonLaunderName(poGeomField->GetNameRef(),
    1862          20 :                                                    "PGDump", m_bUTF8ToASCII);
    1863             : 
    1864          20 :         poGeomField->SetName(pszSafeName);
    1865          20 :         CPLFree(pszSafeName);
    1866             :     }
    1867             : 
    1868          20 :     const OGRSpatialReference *poSRS = poGeomField->GetSpatialRef();
    1869          20 :     int nSRSId = m_nUnknownSRSId;
    1870          20 :     if (m_nForcedSRSId != -2)
    1871           0 :         nSRSId = m_nForcedSRSId;
    1872          20 :     else if (poSRS != nullptr)
    1873             :     {
    1874           1 :         const char *pszAuthorityName = poSRS->GetAuthorityName(nullptr);
    1875           1 :         if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG"))
    1876             :         {
    1877             :             /* Assume the EPSG Id is the SRS ID. Might be a wrong guess ! */
    1878           1 :             nSRSId = atoi(poSRS->GetAuthorityCode(nullptr));
    1879             :         }
    1880             :         else
    1881             :         {
    1882           0 :             const char *pszGeogCSName = poSRS->GetAttrValue("GEOGCS");
    1883           0 :             if (pszGeogCSName != nullptr &&
    1884           0 :                 EQUAL(pszGeogCSName, "GCS_WGS_1984"))
    1885           0 :                 nSRSId = 4326;
    1886             :         }
    1887             :     }
    1888             : 
    1889          20 :     poGeomField->m_nSRSId = nSRSId;
    1890             : 
    1891          20 :     int nGeometryTypeFlags = 0;
    1892          20 :     if (OGR_GT_HasZ(static_cast<OGRwkbGeometryType>(eType)))
    1893           3 :         nGeometryTypeFlags |= OGRGeometry::OGR_G_3D;
    1894          20 :     if (OGR_GT_HasM(static_cast<OGRwkbGeometryType>(eType)))
    1895           2 :         nGeometryTypeFlags |= OGRGeometry::OGR_G_MEASURED;
    1896          20 :     if (m_nForcedGeometryTypeFlags >= 0)
    1897             :     {
    1898           6 :         nGeometryTypeFlags = m_nForcedGeometryTypeFlags;
    1899           6 :         eType = OGR_GT_SetModifier(
    1900             :             eType, nGeometryTypeFlags & OGRGeometry::OGR_G_3D,
    1901             :             nGeometryTypeFlags & OGRGeometry::OGR_G_MEASURED);
    1902             :     }
    1903          20 :     poGeomField->SetType(eType);
    1904          20 :     poGeomField->m_nGeometryTypeFlags = nGeometryTypeFlags;
    1905             : 
    1906             :     /* -------------------------------------------------------------------- */
    1907             :     /*      Create the new field.                                           */
    1908             :     /* -------------------------------------------------------------------- */
    1909          20 :     if (m_bCreateTable)
    1910             :     {
    1911          20 :         const char *suffix = "";
    1912          20 :         int dim = 2;
    1913          27 :         if ((poGeomField->m_nGeometryTypeFlags & OGRGeometry::OGR_G_3D) &&
    1914           7 :             (poGeomField->m_nGeometryTypeFlags & OGRGeometry::OGR_G_MEASURED))
    1915           3 :             dim = 4;
    1916          17 :         else if (poGeomField->m_nGeometryTypeFlags &
    1917          17 :                  OGRGeometry::OGR_G_MEASURED)
    1918             :         {
    1919           3 :             if (wkbFlatten(poGeomField->GetType()) != wkbUnknown)
    1920           2 :                 suffix = "M";
    1921           3 :             dim = 3;
    1922             :         }
    1923          14 :         else if (poGeomField->m_nGeometryTypeFlags & OGRGeometry::OGR_G_3D)
    1924           4 :             dim = 3;
    1925             : 
    1926          20 :         const char *pszGeometryType = OGRToOGCGeomType(poGeomField->GetType());
    1927             :         osCommand.Printf(
    1928             :             "SELECT AddGeometryColumn(%s,%s,%s,%d,'%s%s',%d)",
    1929          40 :             OGRPGDumpEscapeString(m_pszSchemaName).c_str(),
    1930          40 :             OGRPGDumpEscapeString(m_poFeatureDefn->GetName()).c_str(),
    1931          40 :             OGRPGDumpEscapeString(poGeomField->GetNameRef()).c_str(), nSRSId,
    1932          60 :             pszGeometryType, suffix, dim);
    1933             : 
    1934          20 :         if (m_bGeomColumnPositionImmediate)
    1935          20 :             m_poDS->Log(osCommand);
    1936             :         else
    1937           0 :             m_aosDeferredGeomFieldCreationCommands.push_back(osCommand);
    1938             : 
    1939          20 :         if (!poGeomField->IsNullable())
    1940             :         {
    1941             :             osCommand.Printf(
    1942             :                 "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL",
    1943           2 :                 OGRPGDumpEscapeColumnName(m_poFeatureDefn->GetName()).c_str(),
    1944           3 :                 OGRPGDumpEscapeColumnName(poGeomField->GetNameRef()).c_str());
    1945             : 
    1946           1 :             if (m_bGeomColumnPositionImmediate)
    1947           1 :                 m_poDS->Log(osCommand);
    1948             :             else
    1949           0 :                 m_aosDeferredGeomFieldCreationCommands.push_back(osCommand);
    1950             :         }
    1951             : 
    1952          20 :         if (m_bCreateSpatialIndexFlag)
    1953             :         {
    1954             :             const std::string osIndexName(OGRPGCommonGenerateSpatialIndexName(
    1955          40 :                 GetName(), poGeomField->GetNameRef(),
    1956          40 :                 m_poFeatureDefn->GetGeomFieldCount()));
    1957             : 
    1958             :             osCommand.Printf(
    1959             :                 "CREATE INDEX %s ON %s USING %s (%s)",
    1960          40 :                 OGRPGDumpEscapeColumnName(osIndexName.c_str()).c_str(),
    1961             :                 m_pszSqlTableName, m_osSpatialIndexType.c_str(),
    1962          60 :                 OGRPGDumpEscapeColumnName(poGeomField->GetNameRef()).c_str());
    1963             : 
    1964          20 :             m_aosSpatialIndexCreationCommands.push_back(std::move(osCommand));
    1965             :         }
    1966             :     }
    1967             : 
    1968          20 :     m_poFeatureDefn->AddGeomFieldDefn(std::move(poGeomField));
    1969             : 
    1970          20 :     return OGRERR_NONE;
    1971             : }
    1972             : 
    1973             : /************************************************************************/
    1974             : /*                        SetOverrideColumnTypes()                      */
    1975             : /************************************************************************/
    1976             : 
    1977         118 : void OGRPGDumpLayer::SetOverrideColumnTypes(const char *pszOverrideColumnTypes)
    1978             : {
    1979         118 :     if (pszOverrideColumnTypes == nullptr)
    1980         118 :         return;
    1981             : 
    1982           0 :     const char *pszIter = pszOverrideColumnTypes;
    1983           0 :     std::string osCur;
    1984           0 :     while (*pszIter != '\0')
    1985             :     {
    1986           0 :         if (*pszIter == '(')
    1987             :         {
    1988             :             /* Ignore commas inside ( ) pair */
    1989           0 :             while (*pszIter != '\0')
    1990             :             {
    1991           0 :                 if (*pszIter == ')')
    1992             :                 {
    1993           0 :                     osCur += *pszIter;
    1994           0 :                     pszIter++;
    1995           0 :                     break;
    1996             :                 }
    1997           0 :                 osCur += *pszIter;
    1998           0 :                 pszIter++;
    1999             :             }
    2000           0 :             if (*pszIter == '\0')
    2001           0 :                 break;
    2002             :         }
    2003             : 
    2004           0 :         if (*pszIter == ',')
    2005             :         {
    2006           0 :             m_apszOverrideColumnTypes.AddString(osCur.c_str());
    2007           0 :             osCur.clear();
    2008             :         }
    2009             :         else
    2010           0 :             osCur += *pszIter;
    2011           0 :         pszIter++;
    2012             :     }
    2013           0 :     if (!osCur.empty())
    2014           0 :         m_apszOverrideColumnTypes.AddString(osCur.c_str());
    2015             : }
    2016             : 
    2017             : /************************************************************************/
    2018             : /*                              SetMetadata()                           */
    2019             : /************************************************************************/
    2020             : 
    2021           5 : CPLErr OGRPGDumpLayer::SetMetadata(char **papszMD, const char *pszDomain)
    2022             : {
    2023           5 :     OGRLayer::SetMetadata(papszMD, pszDomain);
    2024           6 :     if (!m_osForcedDescription.empty() &&
    2025           1 :         (pszDomain == nullptr || EQUAL(pszDomain, "")))
    2026             :     {
    2027           1 :         OGRLayer::SetMetadataItem("DESCRIPTION", m_osForcedDescription);
    2028             :     }
    2029             : 
    2030           9 :     if ((pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    2031           4 :         m_osForcedDescription.empty())
    2032             :     {
    2033           3 :         const char *l_pszDescription = OGRLayer::GetMetadataItem("DESCRIPTION");
    2034           6 :         CPLString osCommand;
    2035             : 
    2036             :         osCommand.Printf("COMMENT ON TABLE %s IS %s", m_pszSqlTableName,
    2037           2 :                          l_pszDescription && l_pszDescription[0] != '\0'
    2038           5 :                              ? OGRPGDumpEscapeString(l_pszDescription).c_str()
    2039           7 :                              : "NULL");
    2040           3 :         m_poDS->Log(osCommand);
    2041             :     }
    2042             : 
    2043           5 :     return CE_None;
    2044             : }
    2045             : 
    2046             : /************************************************************************/
    2047             : /*                            SetMetadataItem()                         */
    2048             : /************************************************************************/
    2049             : 
    2050           2 : CPLErr OGRPGDumpLayer::SetMetadataItem(const char *pszName,
    2051             :                                        const char *pszValue,
    2052             :                                        const char *pszDomain)
    2053             : {
    2054           2 :     if ((pszDomain == nullptr || EQUAL(pszDomain, "")) && pszName != nullptr &&
    2055           4 :         EQUAL(pszName, "DESCRIPTION") && !m_osForcedDescription.empty())
    2056             :     {
    2057           1 :         return CE_None;
    2058             :     }
    2059           1 :     OGRLayer::SetMetadataItem(pszName, pszValue, pszDomain);
    2060           1 :     if ((pszDomain == nullptr || EQUAL(pszDomain, "")) && pszName != nullptr &&
    2061           1 :         EQUAL(pszName, "DESCRIPTION"))
    2062             :     {
    2063           1 :         SetMetadata(GetMetadata());
    2064             :     }
    2065           1 :     return CE_None;
    2066             : }
    2067             : 
    2068             : /************************************************************************/
    2069             : /*                      SetForcedDescription()                          */
    2070             : /************************************************************************/
    2071             : 
    2072           1 : void OGRPGDumpLayer::SetForcedDescription(const char *pszDescriptionIn)
    2073             : {
    2074           1 :     m_osForcedDescription = pszDescriptionIn;
    2075           1 :     OGRLayer::SetMetadataItem("DESCRIPTION", m_osForcedDescription);
    2076             : 
    2077           1 :     if (pszDescriptionIn[0] != '\0')
    2078             :     {
    2079           2 :         CPLString osCommand;
    2080             :         osCommand.Printf("COMMENT ON TABLE %s IS %s", m_pszSqlTableName,
    2081           1 :                          OGRPGDumpEscapeString(pszDescriptionIn).c_str());
    2082           1 :         m_poDS->Log(osCommand);
    2083             :     }
    2084           1 : }
    2085             : 
    2086             : /************************************************************************/
    2087             : /*                             GetDataset()                             */
    2088             : /************************************************************************/
    2089             : 
    2090          17 : GDALDataset *OGRPGDumpLayer::GetDataset()
    2091             : {
    2092          17 :     return m_poDS;
    2093             : }

Generated by: LCOV version 1.14