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

Generated by: LCOV version 1.14