LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/pgdump - ogrpgdumplayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 913 1011 90.3 %
Date: 2025-05-31 00:00:17 Functions: 39 39 100.0 %

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

Generated by: LCOV version 1.14