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

Generated by: LCOV version 1.14