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

Generated by: LCOV version 1.14