LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/csv - ogrcsvdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 461 587 78.5 %
Date: 2025-10-27 00:14:23 Functions: 26 27 96.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  CSV Translator
       4             :  * Purpose:  Implements OGRCSVDataSource class
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "ogr_csv.h"
      16             : 
      17             : #include <cerrno>
      18             : #include <cstring>
      19             : #include <string>
      20             : #include <algorithm>
      21             : 
      22             : #include "cpl_conv.h"
      23             : #include "cpl_csv.h"
      24             : #include "cpl_error.h"
      25             : #include "cpl_string.h"
      26             : #include "cpl_vsi.h"
      27             : #include "cpl_vsi_virtual.h"
      28             : #include "ogr_core.h"
      29             : #include "ogr_feature.h"
      30             : #include "ogr_geometry.h"
      31             : #include "ogr_spatialref.h"
      32             : #include "ogreditablelayer.h"
      33             : #include "ogrsf_frmts.h"
      34             : #include "ogr_schema_override.h"
      35             : 
      36             : /************************************************************************/
      37             : /*                     OGRCSVEditableLayerSynchronizer                  */
      38             : /************************************************************************/
      39             : 
      40             : class OGRCSVEditableLayerSynchronizer final
      41             :     : public IOGREditableLayerSynchronizer
      42             : {
      43             :     OGRCSVLayer *m_poCSVLayer = nullptr;
      44             :     char **m_papszOpenOptions = nullptr;
      45             : 
      46             :     CPL_DISALLOW_COPY_ASSIGN(OGRCSVEditableLayerSynchronizer)
      47             : 
      48             :   public:
      49         214 :     OGRCSVEditableLayerSynchronizer(OGRCSVLayer *poCSVLayer,
      50             :                                     CSLConstList papszOpenOptions)
      51         214 :         : m_poCSVLayer(poCSVLayer),
      52         214 :           m_papszOpenOptions(CSLDuplicate(papszOpenOptions))
      53             :     {
      54         214 :     }
      55             : 
      56             :     ~OGRCSVEditableLayerSynchronizer() override;
      57             : 
      58             :     virtual OGRErr EditableSyncToDisk(OGRLayer *poEditableLayer,
      59             :                                       OGRLayer **ppoDecoratedLayer) override;
      60             : 
      61          18 :     std::vector<std::string> GetFileList()
      62             :     {
      63          18 :         return m_poCSVLayer->GetFileList();
      64             :     }
      65             : };
      66             : 
      67             : /************************************************************************/
      68             : /*                     ~OGRCSVEditableLayerSynchronizer()               */
      69             : /************************************************************************/
      70             : 
      71         428 : OGRCSVEditableLayerSynchronizer::~OGRCSVEditableLayerSynchronizer()
      72             : {
      73         214 :     CSLDestroy(m_papszOpenOptions);
      74         428 : }
      75             : 
      76             : /************************************************************************/
      77             : /*                       EditableSyncToDisk()                           */
      78             : /************************************************************************/
      79             : 
      80           9 : OGRErr OGRCSVEditableLayerSynchronizer::EditableSyncToDisk(
      81             :     OGRLayer *poEditableLayer, OGRLayer **ppoDecoratedLayer)
      82             : {
      83           9 :     CPLAssert(m_poCSVLayer == *ppoDecoratedLayer);
      84             : 
      85           9 :     GDALDataset *poDS = m_poCSVLayer->GetDataset();
      86          18 :     const CPLString osLayerName(m_poCSVLayer->GetName());
      87          18 :     const CPLString osFilename(m_poCSVLayer->GetFilename());
      88           9 :     const bool bCreateCSVT = m_poCSVLayer->GetCreateCSVT();
      89          18 :     const CPLString osCSVTFilename(CPLResetExtensionSafe(osFilename, "csvt"));
      90             :     VSIStatBufL sStatBuf;
      91           9 :     const bool bHasCSVT = VSIStatL(osCSVTFilename, &sStatBuf) == 0;
      92          18 :     CPLString osTmpFilename(osFilename);
      93          18 :     CPLString osTmpCSVTFilename(osFilename);
      94           9 :     if (VSIStatL(osFilename, &sStatBuf) == 0)
      95             :     {
      96           9 :         osTmpFilename += "_ogr_tmp.csv";
      97           9 :         osTmpCSVTFilename += "_ogr_tmp.csvt";
      98             :     }
      99           9 :     const char chDelimiter = m_poCSVLayer->GetDelimiter();
     100             :     OGRCSVLayer *poCSVTmpLayer = new OGRCSVLayer(
     101           9 :         poDS, osLayerName, nullptr, -1, osTmpFilename, true, true, chDelimiter);
     102           9 :     poCSVTmpLayer->BuildFeatureDefn(nullptr, nullptr, m_papszOpenOptions);
     103           9 :     poCSVTmpLayer->SetCRLF(m_poCSVLayer->GetCRLF());
     104           9 :     poCSVTmpLayer->SetCreateCSVT(bCreateCSVT || bHasCSVT);
     105           9 :     poCSVTmpLayer->SetWriteBOM(m_poCSVLayer->GetWriteBOM());
     106           9 :     poCSVTmpLayer->SetStringQuoting(m_poCSVLayer->GetStringQuoting());
     107             : 
     108           9 :     if (m_poCSVLayer->GetGeometryFormat() == OGR_CSV_GEOM_AS_WKT)
     109           5 :         poCSVTmpLayer->SetWriteGeometry(wkbNone, OGR_CSV_GEOM_AS_WKT, nullptr);
     110             : 
     111             :     const bool bKeepGeomColmuns =
     112           9 :         CPLFetchBool(m_papszOpenOptions, "KEEP_GEOM_COLUMNS", true);
     113             : 
     114           9 :     OGRErr eErr = OGRERR_NONE;
     115           9 :     OGRFeatureDefn *poEditableFDefn = poEditableLayer->GetLayerDefn();
     116          31 :     for (int i = 0; eErr == OGRERR_NONE && i < poEditableFDefn->GetFieldCount();
     117             :          i++)
     118             :     {
     119          44 :         OGRFieldDefn oFieldDefn(poEditableFDefn->GetFieldDefn(i));
     120          22 :         int iGeomFieldIdx = 0;
     121          70 :         if ((EQUAL(oFieldDefn.GetNameRef(), "WKT") &&
     122          38 :              (iGeomFieldIdx = poEditableFDefn->GetGeomFieldIndex("")) >= 0) ||
     123          32 :             (bKeepGeomColmuns &&
     124          16 :              (iGeomFieldIdx = poEditableFDefn->GetGeomFieldIndex(
     125          38 :                   (std::string("geom_") + oFieldDefn.GetNameRef()).c_str())) >=
     126             :                  0))
     127             :         {
     128             :             OGRGeomFieldDefn oGeomFieldDefn(
     129           5 :                 poEditableFDefn->GetGeomFieldDefn(iGeomFieldIdx));
     130           5 :             eErr = poCSVTmpLayer->CreateGeomField(&oGeomFieldDefn);
     131             :         }
     132             :         else
     133             :         {
     134          17 :             eErr = poCSVTmpLayer->CreateField(&oFieldDefn);
     135             :         }
     136             :     }
     137             : 
     138          12 :     const bool bHasXY = !m_poCSVLayer->GetXField().empty() &&
     139           3 :                         !m_poCSVLayer->GetYField().empty();
     140           9 :     const bool bHasZ = !m_poCSVLayer->GetZField().empty();
     141           9 :     if (bHasXY && !bKeepGeomColmuns)
     142             :     {
     143           4 :         if (poCSVTmpLayer->GetLayerDefn()->GetFieldIndex(
     144           4 :                 m_poCSVLayer->GetXField()) < 0)
     145             :         {
     146           4 :             OGRFieldDefn oFieldDefn(m_poCSVLayer->GetXField(), OFTReal);
     147           2 :             if (eErr == OGRERR_NONE)
     148           2 :                 eErr = poCSVTmpLayer->CreateField(&oFieldDefn);
     149             :         }
     150           4 :         if (poCSVTmpLayer->GetLayerDefn()->GetFieldIndex(
     151           4 :                 m_poCSVLayer->GetYField()) < 0)
     152             :         {
     153           4 :             OGRFieldDefn oFieldDefn(m_poCSVLayer->GetYField(), OFTReal);
     154           2 :             if (eErr == OGRERR_NONE)
     155           2 :                 eErr = poCSVTmpLayer->CreateField(&oFieldDefn);
     156             :         }
     157           3 :         if (bHasZ && poCSVTmpLayer->GetLayerDefn()->GetFieldIndex(
     158           1 :                          m_poCSVLayer->GetZField()) < 0)
     159             :         {
     160           2 :             OGRFieldDefn oFieldDefn(m_poCSVLayer->GetZField(), OFTReal);
     161           1 :             if (eErr == OGRERR_NONE)
     162           1 :                 eErr = poCSVTmpLayer->CreateField(&oFieldDefn);
     163             :         }
     164             :     }
     165             : 
     166           9 :     int nFirstGeomColIdx = 0;
     167           9 :     if (m_poCSVLayer->HasHiddenWKTColumn())
     168             :     {
     169           2 :         poCSVTmpLayer->SetWriteGeometry(
     170           1 :             poEditableFDefn->GetGeomFieldDefn(0)->GetType(),
     171             :             OGR_CSV_GEOM_AS_WKT,
     172           1 :             poEditableFDefn->GetGeomFieldDefn(0)->GetNameRef());
     173           1 :         nFirstGeomColIdx = 1;
     174             :     }
     175             : 
     176           9 :     if (!(poEditableFDefn->GetGeomFieldCount() == 1 && bHasXY))
     177             :     {
     178          12 :         for (int i = nFirstGeomColIdx;
     179          12 :              eErr == OGRERR_NONE && i < poEditableFDefn->GetGeomFieldCount();
     180             :              i++)
     181             :         {
     182             :             OGRGeomFieldDefn oGeomFieldDefn(
     183           6 :                 poEditableFDefn->GetGeomFieldDefn(i));
     184          12 :             if (poCSVTmpLayer->GetLayerDefn()->GetGeomFieldIndex(
     185          12 :                     oGeomFieldDefn.GetNameRef()) >= 0)
     186           5 :                 continue;
     187           1 :             eErr = poCSVTmpLayer->CreateGeomField(&oGeomFieldDefn);
     188             :         }
     189             :     }
     190             : 
     191           9 :     poEditableLayer->ResetReading();
     192             : 
     193             :     // Disable all filters.
     194           9 :     const char *pszQueryStringConst = poEditableLayer->GetAttrQueryString();
     195             :     char *pszQueryStringBak =
     196           9 :         pszQueryStringConst ? CPLStrdup(pszQueryStringConst) : nullptr;
     197           9 :     poEditableLayer->SetAttributeFilter(nullptr);
     198             : 
     199           9 :     const int iFilterGeomIndexBak = poEditableLayer->GetGeomFieldFilter();
     200           9 :     OGRGeometry *poFilterGeomBak = poEditableLayer->GetSpatialFilter();
     201           9 :     if (poFilterGeomBak)
     202           1 :         poFilterGeomBak = poFilterGeomBak->clone();
     203           9 :     poEditableLayer->SetSpatialFilter(nullptr);
     204             : 
     205             :     auto aoMapSrcToTargetIdx =
     206             :         poCSVTmpLayer->GetLayerDefn()->ComputeMapForSetFrom(
     207          18 :             poEditableLayer->GetLayerDefn(), true);
     208           9 :     aoMapSrcToTargetIdx.push_back(
     209           9 :         -1);  // add dummy entry to be sure that .data() is valid
     210             : 
     211          21 :     for (auto &&poFeature : poEditableLayer)
     212             :     {
     213          12 :         if (eErr != OGRERR_NONE)
     214           0 :             break;
     215             :         OGRFeature *poNewFeature =
     216          12 :             new OGRFeature(poCSVTmpLayer->GetLayerDefn());
     217          12 :         poNewFeature->SetFrom(poFeature.get(), aoMapSrcToTargetIdx.data(),
     218             :                               true);
     219          12 :         if (bHasXY)
     220             :         {
     221           3 :             OGRGeometry *poGeom = poFeature->GetGeometryRef();
     222           6 :             if (poGeom != nullptr &&
     223           3 :                 wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
     224             :             {
     225           3 :                 auto poPoint = poGeom->toPoint();
     226           3 :                 poNewFeature->SetField(m_poCSVLayer->GetXField(),
     227             :                                        poPoint->getX());
     228           3 :                 poNewFeature->SetField(m_poCSVLayer->GetYField(),
     229             :                                        poPoint->getY());
     230           3 :                 if (bHasZ)
     231             :                 {
     232           1 :                     poNewFeature->SetField(m_poCSVLayer->GetZField(),
     233             :                                            poPoint->getZ());
     234             :                 }
     235             :             }
     236             :         }
     237          12 :         eErr = poCSVTmpLayer->CreateFeature(poNewFeature);
     238          12 :         delete poNewFeature;
     239             :     }
     240           9 :     delete poCSVTmpLayer;
     241             : 
     242             :     // Restore filters.
     243           9 :     poEditableLayer->SetAttributeFilter(pszQueryStringBak);
     244           9 :     CPLFree(pszQueryStringBak);
     245           9 :     poEditableLayer->SetSpatialFilter(iFilterGeomIndexBak, poFilterGeomBak);
     246           9 :     delete poFilterGeomBak;
     247             : 
     248           9 :     if (eErr != OGRERR_NONE)
     249             :     {
     250           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Error while creating %s",
     251             :                  osTmpFilename.c_str());
     252           0 :         VSIUnlink(osTmpFilename);
     253           0 :         VSIUnlink(CPLResetExtensionSafe(osTmpFilename, "csvt").c_str());
     254           0 :         return eErr;
     255             :     }
     256             : 
     257           9 :     delete m_poCSVLayer;
     258             : 
     259           9 :     if (osFilename != osTmpFilename)
     260             :     {
     261           9 :         const CPLString osTmpOriFilename(osFilename + ".ogr_bak");
     262           9 :         const CPLString osTmpOriCSVTFilename(osCSVTFilename + ".ogr_bak");
     263          18 :         if (VSIRename(osFilename, osTmpOriFilename) != 0 ||
     264           4 :             (bHasCSVT &&
     265           4 :              VSIRename(osCSVTFilename, osTmpOriCSVTFilename) != 0) ||
     266          22 :             VSIRename(osTmpFilename, osFilename) != 0 ||
     267           4 :             (bHasCSVT && VSIRename(osTmpCSVTFilename, osCSVTFilename) != 0))
     268             :         {
     269           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot rename files");
     270           0 :             *ppoDecoratedLayer = nullptr;
     271           0 :             m_poCSVLayer = nullptr;
     272           0 :             return OGRERR_FAILURE;
     273             :         }
     274           9 :         VSIUnlink(osTmpOriFilename);
     275           9 :         if (bHasCSVT)
     276           4 :             VSIUnlink(osTmpOriCSVTFilename);
     277             :     }
     278             : 
     279           9 :     VSILFILE *fp = VSIFOpenL(osFilename, "rb+");
     280           9 :     if (fp == nullptr)
     281             :     {
     282           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot reopen updated %s",
     283             :                  osFilename.c_str());
     284           0 :         *ppoDecoratedLayer = nullptr;
     285           0 :         m_poCSVLayer = nullptr;
     286           0 :         return OGRERR_FAILURE;
     287             :     }
     288             : 
     289           9 :     m_poCSVLayer =
     290             :         new OGRCSVLayer(poDS, osLayerName, fp, -1, osFilename, false, /* new */
     291             :                         true, /* update */
     292           9 :                         chDelimiter);
     293           9 :     m_poCSVLayer->BuildFeatureDefn(nullptr, nullptr, m_papszOpenOptions);
     294           9 :     *ppoDecoratedLayer = m_poCSVLayer;
     295             : 
     296           9 :     return OGRERR_NONE;
     297             : }
     298             : 
     299             : /************************************************************************/
     300             : /*                        OGRCSVEditableLayer                           */
     301             : /************************************************************************/
     302             : 
     303             : class OGRCSVEditableLayer final : public IOGRCSVLayer, public OGREditableLayer
     304             : {
     305             :     std::set<CPLString> m_oSetFields{};
     306             : 
     307             :   public:
     308             :     OGRCSVEditableLayer(OGRCSVLayer *poCSVLayer, CSLConstList papszOpenOptions);
     309             : 
     310         336 :     OGRLayer *GetLayer() override
     311             :     {
     312         336 :         return this;
     313             :     }
     314             : 
     315          18 :     std::vector<std::string> GetFileList() override
     316             :     {
     317             :         return cpl::down_cast<OGRCSVEditableLayerSynchronizer *>(
     318             :                    m_poSynchronizer)
     319          18 :             ->GetFileList();
     320             :     }
     321             : 
     322             :     virtual OGRErr CreateField(const OGRFieldDefn *poField,
     323             :                                int bApproxOK = TRUE) override;
     324             :     OGRErr DeleteField(int iField) override;
     325             :     virtual OGRErr AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
     326             :                                   int nFlagsIn) override;
     327             :     GIntBig GetFeatureCount(int bForce = TRUE) override;
     328             : };
     329             : 
     330             : /************************************************************************/
     331             : /*                       OGRCSVEditableLayer()                          */
     332             : /************************************************************************/
     333             : 
     334         214 : OGRCSVEditableLayer::OGRCSVEditableLayer(OGRCSVLayer *poCSVLayer,
     335         214 :                                          CSLConstList papszOpenOptions)
     336             :     : OGREditableLayer(
     337             :           poCSVLayer, true,
     338         214 :           new OGRCSVEditableLayerSynchronizer(poCSVLayer, papszOpenOptions),
     339         428 :           true)
     340             : {
     341         214 :     SetSupportsCreateGeomField(true);
     342         214 :     SetSupportsCurveGeometries(true);
     343         214 : }
     344             : 
     345             : /************************************************************************/
     346             : /*                            CreateField()                             */
     347             : /************************************************************************/
     348             : 
     349         415 : OGRErr OGRCSVEditableLayer::CreateField(const OGRFieldDefn *poNewField,
     350             :                                         int bApproxOK)
     351             : 
     352             : {
     353         415 :     if (m_poEditableFeatureDefn->GetFieldCount() >= 10000)
     354             :     {
     355           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Limiting to 10000 fields");
     356           0 :         return OGRERR_FAILURE;
     357             :     }
     358             : 
     359         415 :     if (m_oSetFields.empty())
     360             :     {
     361         107 :         for (int i = 0; i < m_poEditableFeatureDefn->GetFieldCount(); i++)
     362             :         {
     363             :             m_oSetFields.insert(
     364           8 :                 CPLString(
     365           4 :                     m_poEditableFeatureDefn->GetFieldDefn(i)->GetNameRef())
     366           8 :                     .toupper());
     367             :         }
     368             :     }
     369             : 
     370         830 :     const OGRCSVCreateFieldAction eAction = OGRCSVLayer::PreCreateField(
     371         415 :         m_poEditableFeatureDefn, m_oSetFields, poNewField, bApproxOK);
     372         415 :     if (eAction == CREATE_FIELD_DO_NOTHING)
     373           0 :         return OGRERR_NONE;
     374         415 :     if (eAction == CREATE_FIELD_ERROR)
     375           1 :         return OGRERR_FAILURE;
     376         414 :     OGRErr eErr = OGREditableLayer::CreateField(poNewField, bApproxOK);
     377         414 :     if (eErr == OGRERR_NONE)
     378             :     {
     379         414 :         m_oSetFields.insert(CPLString(poNewField->GetNameRef()).toupper());
     380             :     }
     381         414 :     return eErr;
     382             : }
     383             : 
     384           3 : OGRErr OGRCSVEditableLayer::DeleteField(int iField)
     385             : {
     386           3 :     m_oSetFields.clear();
     387           3 :     return OGREditableLayer::DeleteField(iField);
     388             : }
     389             : 
     390           2 : OGRErr OGRCSVEditableLayer::AlterFieldDefn(int iField,
     391             :                                            OGRFieldDefn *poNewFieldDefn,
     392             :                                            int nFlagsIn)
     393             : {
     394           2 :     m_oSetFields.clear();
     395           2 :     return OGREditableLayer::AlterFieldDefn(iField, poNewFieldDefn, nFlagsIn);
     396             : }
     397             : 
     398             : /************************************************************************/
     399             : /*                        GetFeatureCount()                             */
     400             : /************************************************************************/
     401             : 
     402          28 : GIntBig OGRCSVEditableLayer::GetFeatureCount(int bForce)
     403             : {
     404          28 :     const GIntBig nRet = OGREditableLayer::GetFeatureCount(bForce);
     405          28 :     if (m_poDecoratedLayer != nullptr && m_nNextFID <= 0)
     406             :     {
     407             :         const GIntBig nTotalFeatureCount =
     408           8 :             static_cast<OGRCSVLayer *>(m_poDecoratedLayer)
     409           4 :                 ->GetTotalFeatureCount();
     410           4 :         if (nTotalFeatureCount >= 0)
     411           3 :             SetNextFID(nTotalFeatureCount + 1);
     412             :     }
     413          28 :     return nRet;
     414             : }
     415             : 
     416             : /************************************************************************/
     417             : /*                          OGRCSVDataSource()                          */
     418             : /************************************************************************/
     419             : 
     420             : OGRCSVDataSource::OGRCSVDataSource() = default;
     421             : 
     422             : /************************************************************************/
     423             : /*                         ~OGRCSVDataSource()                          */
     424             : /************************************************************************/
     425             : 
     426        2642 : OGRCSVDataSource::~OGRCSVDataSource()
     427             : 
     428             : {
     429        1321 :     m_apoLayers.clear();
     430             : 
     431        1321 :     if (bUpdate)
     432         254 :         OGRCSVDriverRemoveFromMap(pszName, this);
     433             : 
     434        1321 :     CPLFree(pszName);
     435        2642 : }
     436             : 
     437             : /************************************************************************/
     438             : /*                           TestCapability()                           */
     439             : /************************************************************************/
     440             : 
     441         210 : int OGRCSVDataSource::TestCapability(const char *pszCap) const
     442             : 
     443             : {
     444         210 :     if (EQUAL(pszCap, ODsCCreateLayer))
     445          68 :         return bUpdate;
     446         142 :     else if (EQUAL(pszCap, ODsCDeleteLayer))
     447          19 :         return bUpdate;
     448         123 :     else if (EQUAL(pszCap, ODsCCreateGeomFieldAfterCreateLayer))
     449          33 :         return bUpdate && bEnableGeometryFields;
     450          90 :     else if (EQUAL(pszCap, ODsCCurveGeometries))
     451           3 :         return TRUE;
     452          87 :     else if (EQUAL(pszCap, ODsCMeasuredGeometries))
     453           3 :         return TRUE;
     454          84 :     else if (EQUAL(pszCap, ODsCZGeometries))
     455           2 :         return TRUE;
     456          82 :     else if (EQUAL(pszCap, ODsCRandomLayerWrite))
     457           0 :         return bUpdate;
     458             :     else
     459          82 :         return FALSE;
     460             : }
     461             : 
     462             : /************************************************************************/
     463             : /*                              GetLayer()                              */
     464             : /************************************************************************/
     465             : 
     466         949 : const OGRLayer *OGRCSVDataSource::GetLayer(int iLayer) const
     467             : 
     468             : {
     469         949 :     if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
     470           2 :         return nullptr;
     471             : 
     472         947 :     return m_apoLayers[iLayer]->GetLayer();
     473             : }
     474             : 
     475             : /************************************************************************/
     476             : /*                          GetRealExtension()                          */
     477             : /************************************************************************/
     478             : 
     479        8126 : CPLString OGRCSVDataSource::GetRealExtension(CPLString osFilename)
     480             : {
     481       16252 :     CPLString osExt = CPLGetExtensionSafe(osFilename);
     482        8126 :     if (STARTS_WITH(osFilename, "/vsigzip/") && EQUAL(osExt, "gz"))
     483             :     {
     484          14 :         if (osFilename.size() > 7 &&
     485           7 :             EQUAL(osFilename + osFilename.size() - 7, ".csv.gz"))
     486           0 :             return "csv";
     487          14 :         else if (osFilename.size() > 7 &&
     488           7 :                  EQUAL(osFilename + osFilename.size() - 7, ".tsv.gz"))
     489           0 :             return "tsv";
     490          14 :         else if (osFilename.size() > 7 &&
     491           7 :                  EQUAL(osFilename + osFilename.size() - 7, ".psv.gz"))
     492           0 :             return "psv";
     493             :     }
     494        8126 :     return osExt;
     495             : }
     496             : 
     497             : /************************************************************************/
     498             : /*                                Open()                                */
     499             : /************************************************************************/
     500             : 
     501        1257 : bool OGRCSVDataSource::Open(const char *pszFilename, bool bUpdateIn,
     502             :                             bool bForceOpen, CSLConstList papszOpenOptionsIn,
     503             :                             bool bSingleDriver)
     504             : 
     505             : {
     506        1257 :     pszName = CPLStrdup(pszFilename);
     507        1257 :     bUpdate = CPL_TO_BOOL(bUpdateIn);
     508             : 
     509        1257 :     if (bUpdate && bForceOpen && EQUAL(pszFilename, "/vsistdout/"))
     510           1 :         return TRUE;
     511             : 
     512             :     // For writable /vsizip/, do nothing more.
     513        1256 :     if (bUpdate && bForceOpen && STARTS_WITH(pszFilename, "/vsizip/"))
     514           0 :         return TRUE;
     515             : 
     516        2512 :     CPLString osFilename(pszFilename);
     517        2512 :     const CPLString osBaseFilename = CPLGetFilename(pszFilename);
     518        2512 :     const CPLString osExt = GetRealExtension(osFilename);
     519             : 
     520        1256 :     bool bUSGeonamesFile = false;
     521        1256 :     if (STARTS_WITH_CI(osFilename, "CSV:"))
     522             :     {
     523           1 :         bSingleDriver = true;
     524           1 :         osFilename = osFilename.substr(strlen("CSV:"));
     525             :     }
     526             : 
     527             :     // Those are *not* real .XLS files, but text file with tab as column
     528             :     // separator.
     529        1256 :     if (EQUAL(osBaseFilename, "NfdcFacilities.xls") ||
     530        1256 :         EQUAL(osBaseFilename, "NfdcRunways.xls") ||
     531        3768 :         EQUAL(osBaseFilename, "NfdcRemarks.xls") ||
     532        1256 :         EQUAL(osBaseFilename, "NfdcSchedules.xls"))
     533             :     {
     534           0 :         if (bUpdate)
     535           0 :             return FALSE;
     536           0 :         bSingleDriver = true;
     537             :     }
     538        1256 :     else if ((STARTS_WITH_CI(osBaseFilename, "NationalFile_") ||
     539        1256 :               STARTS_WITH_CI(osBaseFilename, "POP_PLACES_") ||
     540        1256 :               STARTS_WITH_CI(osBaseFilename, "HIST_FEATURES_") ||
     541        1256 :               STARTS_WITH_CI(osBaseFilename, "US_CONCISE_") ||
     542        1256 :               STARTS_WITH_CI(osBaseFilename, "AllNames_") ||
     543        1256 :               STARTS_WITH_CI(osBaseFilename, "Feature_Description_History_") ||
     544        1256 :               STARTS_WITH_CI(osBaseFilename, "ANTARCTICA_") ||
     545        1256 :               STARTS_WITH_CI(osBaseFilename, "GOVT_UNITS_") ||
     546        1256 :               STARTS_WITH_CI(osBaseFilename, "NationalFedCodes_") ||
     547        1256 :               STARTS_WITH_CI(osBaseFilename, "AllStates_") ||
     548        2512 :               STARTS_WITH_CI(osBaseFilename, "AllStatesFedCodes_") ||
     549        1256 :               (osBaseFilename.size() > 2 &&
     550        2492 :                STARTS_WITH_CI(osBaseFilename + 2, "_Features_")) ||
     551        1256 :               (osBaseFilename.size() > 2 &&
     552        2512 :                STARTS_WITH_CI(osBaseFilename + 2, "_FedCodes_"))) &&
     553           0 :              (EQUAL(osExt, "txt") || EQUAL(osExt, "zip")))
     554             :     {
     555           0 :         if (bUpdate)
     556           0 :             return FALSE;
     557           0 :         bSingleDriver = true;
     558           0 :         bUSGeonamesFile = true;
     559             : 
     560           0 :         if (EQUAL(osExt, "zip") && strstr(osFilename, "/vsizip/") == nullptr)
     561             :         {
     562           0 :             osFilename = "/vsizip/" + osFilename;
     563             :         }
     564             :     }
     565        2511 :     else if (EQUAL(osBaseFilename, "allCountries.txt") ||
     566        1255 :              EQUAL(osBaseFilename, "allCountries.zip"))
     567             :     {
     568           1 :         if (bUpdate)
     569           0 :             return FALSE;
     570           1 :         bSingleDriver = true;
     571             : 
     572           1 :         if (EQUAL(osExt, "zip") && strstr(osFilename, "/vsizip/") == nullptr)
     573             :         {
     574           0 :             osFilename = "/vsizip/" + osFilename;
     575             :         }
     576             :     }
     577             : 
     578             :     // Determine what sort of object this is.
     579             :     VSIStatBufL sStatBuf;
     580             : 
     581        1256 :     if (VSIStatExL(osFilename, &sStatBuf, VSI_STAT_NATURE_FLAG) != 0)
     582           0 :         return FALSE;
     583             : 
     584             :     // Is this a single CSV file?
     585        1666 :     if (VSI_ISREG(sStatBuf.st_mode) &&
     586         410 :         (bSingleDriver || EQUAL(osExt, "csv") || EQUAL(osExt, "tsv") ||
     587           2 :          EQUAL(osExt, "psv")))
     588             :     {
     589         502 :         if (EQUAL(CPLGetFilename(osFilename), "NfdcFacilities.xls"))
     590             :         {
     591           0 :             return OpenTable(osFilename, papszOpenOptionsIn, "ARP");
     592             :         }
     593         502 :         else if (EQUAL(CPLGetFilename(osFilename), "NfdcRunways.xls"))
     594             :         {
     595           0 :             OpenTable(osFilename, papszOpenOptionsIn, "BaseEndPhysical");
     596           0 :             OpenTable(osFilename, papszOpenOptionsIn, "BaseEndDisplaced");
     597           0 :             OpenTable(osFilename, papszOpenOptionsIn, "ReciprocalEndPhysical");
     598           0 :             OpenTable(osFilename, papszOpenOptionsIn, "ReciprocalEndDisplaced");
     599           0 :             return !m_apoLayers.empty();
     600             :         }
     601         502 :         else if (bUSGeonamesFile)
     602             :         {
     603             :             // GNIS specific.
     604           0 :             if (STARTS_WITH_CI(osBaseFilename, "NationalFedCodes_") ||
     605           0 :                 STARTS_WITH_CI(osBaseFilename, "AllStatesFedCodes_") ||
     606           0 :                 STARTS_WITH_CI(osBaseFilename, "ANTARCTICA_") ||
     607           0 :                 (osBaseFilename.size() > 2 &&
     608           0 :                  STARTS_WITH_CI(osBaseFilename + 2, "_FedCodes_")))
     609             :             {
     610           0 :                 OpenTable(osFilename, papszOpenOptionsIn, nullptr, "PRIMARY");
     611             :             }
     612           0 :             else if (STARTS_WITH_CI(osBaseFilename, "GOVT_UNITS_") ||
     613           0 :                      STARTS_WITH_CI(osBaseFilename,
     614             :                                     "Feature_Description_History_"))
     615             :             {
     616           0 :                 OpenTable(osFilename, papszOpenOptionsIn, nullptr, "");
     617             :             }
     618             :             else
     619             :             {
     620           0 :                 OpenTable(osFilename, papszOpenOptionsIn, nullptr, "PRIM");
     621           0 :                 OpenTable(osFilename, papszOpenOptionsIn, nullptr, "SOURCE");
     622             :             }
     623           0 :             return !m_apoLayers.empty();
     624             :         }
     625             : 
     626         502 :         return OpenTable(osFilename, papszOpenOptionsIn);
     627             :     }
     628             : 
     629             :     // Is this a single a ZIP file with only a CSV file inside?
     630         759 :     if (STARTS_WITH(osFilename, "/vsizip/") && EQUAL(osExt, "zip") &&
     631           5 :         VSI_ISREG(sStatBuf.st_mode))
     632             :     {
     633           1 :         char **papszFiles = VSIReadDir(osFilename);
     634           2 :         if (CSLCount(papszFiles) != 1 ||
     635           2 :             !EQUAL(CPLGetExtensionSafe(papszFiles[0]).c_str(), "CSV"))
     636             :         {
     637           1 :             CSLDestroy(papszFiles);
     638           1 :             return FALSE;
     639             :         }
     640           0 :         osFilename = CPLFormFilenameSafe(osFilename, papszFiles[0], nullptr);
     641           0 :         CSLDestroy(papszFiles);
     642           0 :         return OpenTable(osFilename, papszOpenOptionsIn);
     643             :     }
     644             : 
     645             :     // Otherwise it has to be a directory.
     646         753 :     if (!VSI_ISDIR(sStatBuf.st_mode))
     647           0 :         return FALSE;
     648             : 
     649             :     // Scan through for entries ending in .csv.
     650         753 :     int nNotCSVCount = 0;
     651         753 :     const CPLStringList aosFilenames(VSIReadDir(osFilename));
     652             : 
     653       26792 :     for (int i = 0; i < aosFilenames.size(); i++)
     654             :     {
     655       26039 :         if (EQUAL(aosFilenames[i], ".") || EQUAL(aosFilenames[i], ".."))
     656       25896 :             continue;
     657             : 
     658       25291 :         const std::string osThisExt = CPLGetExtensionSafe(aosFilenames[i]);
     659       25291 :         if (EQUAL(osThisExt.c_str(), "csvt") || EQUAL(osThisExt.c_str(), "prj"))
     660          94 :             continue;
     661             : 
     662             :         const CPLString oSubFilename =
     663       25197 :             CPLFormFilenameSafe(osFilename, aosFilenames[i], nullptr);
     664       50394 :         if (VSIStatL(oSubFilename, &sStatBuf) != 0 ||
     665       25197 :             !VSI_ISREG(sStatBuf.st_mode))
     666             :         {
     667         143 :             nNotCSVCount++;
     668         143 :             continue;
     669             :         }
     670             : 
     671       25054 :         if (EQUAL(osThisExt.c_str(), "csv"))
     672             :         {
     673         143 :             if (!OpenTable(oSubFilename, papszOpenOptionsIn))
     674             :             {
     675           0 :                 CPLDebug("CSV", "Cannot open %s", oSubFilename.c_str());
     676           0 :                 nNotCSVCount++;
     677           0 :                 continue;
     678             :             }
     679             :         }
     680             :         // GNIS specific.
     681       24911 :         else if (strlen(aosFilenames[i]) > 2 &&
     682       24911 :                  STARTS_WITH_CI(aosFilenames[i] + 2, "_Features_") &&
     683           0 :                  EQUAL(osThisExt.c_str(), "txt"))
     684             :         {
     685             :             bool bRet =
     686           0 :                 OpenTable(oSubFilename, papszOpenOptionsIn, nullptr, "PRIM");
     687           0 :             bRet |=
     688           0 :                 OpenTable(oSubFilename, papszOpenOptionsIn, nullptr, "SOURCE");
     689           0 :             if (!bRet)
     690             :             {
     691           0 :                 CPLDebug("CSV", "Cannot open %s", oSubFilename.c_str());
     692           0 :                 nNotCSVCount++;
     693           0 :                 continue;
     694             :             }
     695             :         }
     696             :         // GNIS specific.
     697       24911 :         else if (strlen(aosFilenames[i]) > 2 &&
     698       24911 :                  STARTS_WITH_CI(aosFilenames[i] + 2, "_FedCodes_") &&
     699           0 :                  EQUAL(osThisExt.c_str(), "txt"))
     700             :         {
     701           0 :             if (!OpenTable(oSubFilename, papszOpenOptionsIn, nullptr,
     702             :                            "PRIMARY"))
     703             :             {
     704           0 :                 CPLDebug("CSV", "Cannot open %s", oSubFilename.c_str());
     705           0 :                 nNotCSVCount++;
     706           0 :                 continue;
     707             :             }
     708             :         }
     709             :         else
     710             :         {
     711       24911 :             nNotCSVCount++;
     712       24911 :             continue;
     713             :         }
     714             :     }
     715             : 
     716             :     // We presume that this is indeed intended to be a CSV
     717             :     // datasource if over half the files were .csv files.
     718         755 :     return bForceOpen || nNotCSVCount < GetLayerCount() ||
     719         755 :            (bSingleDriver && GetLayerCount() > 0);
     720             : }
     721             : 
     722      215231 : const std::vector<int> &OGRCSVDataSource::DeletedFieldIndexes() const
     723             : {
     724      215231 :     return m_oDeletedFieldIndexes;
     725             : }
     726             : 
     727             : /************************************************************************/
     728             : /*                      DealWithOgrSchemaOpenOption()                   */
     729             : /************************************************************************/
     730         557 : bool OGRCSVDataSource::DealWithOgrSchemaOpenOption(
     731             :     CSLConstList papszOpenOptionsIn)
     732             : {
     733             :     const std::string osFieldsSchemaOverrideParam =
     734        1114 :         CSLFetchNameValueDef(papszOpenOptionsIn, "OGR_SCHEMA", "");
     735             : 
     736         557 :     if (!osFieldsSchemaOverrideParam.empty())
     737             :     {
     738          10 :         if (bUpdate)
     739             :         {
     740           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     741             :                      "OGR_SCHEMA open option is not supported in update mode.");
     742           1 :             return false;
     743             :         }
     744             : 
     745          10 :         OGRSchemaOverride oSchemaOverride;
     746          10 :         const auto nErrorCount = CPLGetErrorCounter();
     747          19 :         if (!oSchemaOverride.LoadFromJSON(osFieldsSchemaOverrideParam) ||
     748           9 :             !oSchemaOverride.IsValid())
     749             :         {
     750           1 :             if (nErrorCount == CPLGetErrorCounter())
     751             :             {
     752           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     753             :                          "Content of OGR_SCHEMA in %s is not valid",
     754             :                          osFieldsSchemaOverrideParam.c_str());
     755             :             }
     756           1 :             return false;
     757             :         }
     758             : 
     759           9 :         if (!oSchemaOverride.DefaultApply(
     760             :                 this, "CSV",
     761          10 :                 [this](OGRLayer *, int iField)
     762          10 :                 { m_oDeletedFieldIndexes.push_back(iField); }))
     763             :         {
     764           0 :             return false;
     765             :         }
     766             :     }
     767         556 :     return true;
     768             : }
     769             : 
     770             : /************************************************************************/
     771             : /*                              OpenTable()                             */
     772             : /************************************************************************/
     773             : 
     774         645 : bool OGRCSVDataSource::OpenTable(const char *pszFilename,
     775             :                                  CSLConstList papszOpenOptionsIn,
     776             :                                  const char *pszNfdcRunwaysGeomField,
     777             :                                  const char *pszGeonamesGeomFieldPrefix)
     778             : 
     779             : {
     780             :     // Open the file.
     781         645 :     VSILFILE *fp = nullptr;
     782             : 
     783         645 :     if (bUpdate)
     784          88 :         fp = VSIFOpenExL(pszFilename, "rb+", true);
     785             :     else
     786         557 :         fp = VSIFOpenExL(pszFilename, "rb", true);
     787         645 :     if (fp == nullptr)
     788             :     {
     789           0 :         CPLError(CE_Warning, CPLE_OpenFailed, "Failed to open %s.",
     790             :                  VSIGetLastErrorMsg());
     791           0 :         return false;
     792             :     }
     793             : 
     794         645 :     if (!bUpdate && strstr(pszFilename, "/vsigzip/") == nullptr &&
     795         557 :         strstr(pszFilename, "/vsizip/") == nullptr)
     796         473 :         fp = VSICreateBufferedReaderHandle(fp);
     797             : 
     798        1290 :     CPLString osLayerName = CPLGetBasenameSafe(pszFilename);
     799        1290 :     CPLString osExt = CPLGetExtensionSafe(pszFilename);
     800         645 :     if (STARTS_WITH(pszFilename, "/vsigzip/") && EQUAL(osExt, "gz"))
     801             :     {
     802           0 :         if (strlen(pszFilename) > 7 &&
     803           0 :             EQUAL(pszFilename + strlen(pszFilename) - 7, ".csv.gz"))
     804             :         {
     805           0 :             osLayerName = osLayerName.substr(0, osLayerName.size() - 4);
     806           0 :             osExt = "csv";
     807             :         }
     808           0 :         else if (strlen(pszFilename) > 7 &&
     809           0 :                  EQUAL(pszFilename + strlen(pszFilename) - 7, ".tsv.gz"))
     810             :         {
     811           0 :             osLayerName = osLayerName.substr(0, osLayerName.size() - 4);
     812           0 :             osExt = "tsv";
     813             :         }
     814           0 :         else if (strlen(pszFilename) > 7 &&
     815           0 :                  EQUAL(pszFilename + strlen(pszFilename) - 7, ".psv.gz"))
     816             :         {
     817           0 :             osLayerName = osLayerName.substr(0, osLayerName.size() - 4);
     818           0 :             osExt = "psv";
     819             :         }
     820             :     }
     821             : 
     822         645 :     int nMaxLineSize = atoi(CPLGetConfigOption(
     823             :         "OGR_CSV_MAX_LINE_SIZE",
     824             :         CSLFetchNameValueDef(papszOpenOptionsIn, "MAX_LINE_SIZE",
     825         645 :                              CPLSPrintf("%d", OGR_CSV_DEFAULT_MAX_LINE_SIZE))));
     826         645 :     size_t nMaxLineSizeAsSize_t = static_cast<size_t>(nMaxLineSize);
     827         645 :     if (nMaxLineSize == 0)
     828             :     {
     829           0 :         nMaxLineSize = -1;
     830           0 :         nMaxLineSizeAsSize_t = static_cast<size_t>(-1);
     831             :     }
     832             : 
     833             :     // Read and parse a line to detect separator.
     834             : 
     835        1290 :     std::string osLine;
     836             :     {
     837         645 :         const char *pszLine = CPLReadLine2L(fp, nMaxLineSize, nullptr);
     838         645 :         if (pszLine == nullptr)
     839             :         {
     840           0 :             VSIFCloseL(fp);
     841           0 :             return false;
     842             :         }
     843         645 :         osLine = pszLine;
     844             :     }
     845             : 
     846         645 :     char chDelimiter = ',';
     847             :     const char *pszDelimiter =
     848         645 :         CSLFetchNameValueDef(papszOpenOptionsIn, "SEPARATOR", "AUTO");
     849         645 :     if (EQUAL(pszDelimiter, "AUTO"))
     850             :     {
     851         637 :         chDelimiter = CSVDetectSeperator(osLine.c_str());
     852         637 :         if (chDelimiter != '\t' && osLine.find('\t') != std::string::npos)
     853             :         {
     854             :             // Force the delimiter to be TAB for a .tsv file that has a tabulation
     855             :             // in its first line */
     856           0 :             if (EQUAL(osExt, "tsv"))
     857             :             {
     858           0 :                 chDelimiter = '\t';
     859             :             }
     860             :             else
     861             :             {
     862           0 :                 for (int nDontHonourStrings = 0; nDontHonourStrings <= 1;
     863             :                      nDontHonourStrings++)
     864             :                 {
     865             :                     const bool bHonourStrings =
     866           0 :                         !CPL_TO_BOOL(nDontHonourStrings);
     867             :                     // Read the first 2 lines to see if they have the same number
     868             :                     // of fields, if using tabulation.
     869           0 :                     VSIRewindL(fp);
     870           0 :                     char **papszTokens = CSVReadParseLine3L(
     871             :                         fp, nMaxLineSizeAsSize_t, "\t", bHonourStrings,
     872             :                         false,  // bKeepLeadingAndClosingQuotes
     873             :                         false,  // bMergeDelimiter
     874             :                         true    // bSkipBOM
     875             :                     );
     876           0 :                     const int nTokens1 = CSLCount(papszTokens);
     877           0 :                     CSLDestroy(papszTokens);
     878           0 :                     papszTokens = CSVReadParseLine3L(
     879             :                         fp, nMaxLineSizeAsSize_t, "\t", bHonourStrings,
     880             :                         false,  // bKeepLeadingAndClosingQuotes
     881             :                         false,  // bMergeDelimiter
     882             :                         true    // bSkipBOM
     883             :                     );
     884           0 :                     const int nTokens2 = CSLCount(papszTokens);
     885           0 :                     CSLDestroy(papszTokens);
     886           0 :                     if (nTokens1 >= 2 && nTokens1 == nTokens2)
     887             :                     {
     888           0 :                         chDelimiter = '\t';
     889           0 :                         break;
     890             :                     }
     891             :                 }
     892             :             }
     893             :         }
     894             : 
     895             :         // GNIS specific.
     896         637 :         if (pszGeonamesGeomFieldPrefix != nullptr &&
     897           0 :             osLine.find('|') != std::string::npos)
     898           0 :             chDelimiter = '|';
     899             :     }
     900           8 :     else if (EQUAL(pszDelimiter, "COMMA"))
     901           4 :         chDelimiter = ',';
     902           4 :     else if (EQUAL(pszDelimiter, "SEMICOLON"))
     903           1 :         chDelimiter = ';';
     904           3 :     else if (EQUAL(pszDelimiter, "TAB"))
     905           1 :         chDelimiter = '\t';
     906           2 :     else if (EQUAL(pszDelimiter, "SPACE"))
     907           1 :         chDelimiter = ' ';
     908           1 :     else if (EQUAL(pszDelimiter, "PIPE"))
     909           1 :         chDelimiter = '|';
     910             :     else
     911             :     {
     912           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     913             :                  "SEPARATOR=%s not understood, use one of COMMA, "
     914             :                  "SEMICOLON, TAB, SPACE or PIPE.",
     915             :                  pszDelimiter);
     916             :     }
     917             : 
     918         645 :     VSIRewindL(fp);
     919             : 
     920             :     // Create a layer.
     921         645 :     if (pszNfdcRunwaysGeomField != nullptr)
     922             :     {
     923           0 :         osLayerName += "_";
     924           0 :         osLayerName += pszNfdcRunwaysGeomField;
     925             :     }
     926         645 :     else if (pszGeonamesGeomFieldPrefix != nullptr &&
     927           0 :              !EQUAL(pszGeonamesGeomFieldPrefix, ""))
     928             :     {
     929           0 :         osLayerName += "_";
     930           0 :         osLayerName += pszGeonamesGeomFieldPrefix;
     931             :     }
     932         645 :     if (EQUAL(pszFilename, "/vsistdin/"))
     933           0 :         osLayerName = "layer";
     934             : 
     935             :     auto poCSVLayer =
     936             :         std::make_unique<OGRCSVLayer>(this, osLayerName, fp, nMaxLineSize,
     937        1290 :                                       pszFilename, FALSE, bUpdate, chDelimiter);
     938         645 :     poCSVLayer->BuildFeatureDefn(pszNfdcRunwaysGeomField,
     939             :                                  pszGeonamesGeomFieldPrefix,
     940             :                                  papszOpenOptionsIn);
     941         645 :     if (bUpdate)
     942             :     {
     943          88 :         m_apoLayers.emplace_back(std::make_unique<OGRCSVEditableLayer>(
     944         176 :             poCSVLayer.release(), papszOpenOptionsIn));
     945             :     }
     946             :     else
     947             :     {
     948         557 :         m_apoLayers.emplace_back(std::move(poCSVLayer));
     949             : 
     950         557 :         if (!DealWithOgrSchemaOpenOption(papszOpenOptionsIn))
     951             :         {
     952           1 :             m_apoLayers.pop_back();
     953           1 :             return false;
     954             :         }
     955             :     }
     956             : 
     957         644 :     return true;
     958             : }
     959             : 
     960             : /************************************************************************/
     961             : /*                           ICreateLayer()                             */
     962             : /************************************************************************/
     963             : 
     964             : OGRLayer *
     965         132 : OGRCSVDataSource::ICreateLayer(const char *pszLayerName,
     966             :                                const OGRGeomFieldDefn *poSrcGeomFieldDefn,
     967             :                                CSLConstList papszOptions)
     968             : {
     969             :     // Verify we are in update mode.
     970         132 :     if (!bUpdate)
     971             :     {
     972           1 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     973             :                  "Data source %s opened read-only.\n"
     974             :                  "New layer %s cannot be created.",
     975             :                  pszName, pszLayerName);
     976             : 
     977           1 :         return nullptr;
     978             :     }
     979             : 
     980             :     const auto eGType =
     981         131 :         poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetType() : wkbNone;
     982             :     const auto poSpatialRef =
     983         131 :         poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetSpatialRef() : nullptr;
     984             : 
     985             :     // Verify that the datasource is a directory.
     986             :     VSIStatBufL sStatBuf;
     987             : 
     988         131 :     if (STARTS_WITH(pszName, "/vsizip/"))
     989             :     {
     990             :         // Do nothing.
     991             :     }
     992         261 :     else if (!EQUAL(pszName, "/vsistdout/") &&
     993         130 :              (VSIStatL(pszName, &sStatBuf) != 0 ||
     994         130 :               !VSI_ISDIR(sStatBuf.st_mode)))
     995             :     {
     996           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     997             :                  "Attempt to create csv layer (file) against a "
     998             :                  "non-directory datasource.");
     999           0 :         return nullptr;
    1000             :     }
    1001             : 
    1002             :     const bool bCreateCSVT =
    1003         131 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "CREATE_CSVT", "NO"));
    1004             : 
    1005             :     // What filename would we use?
    1006         262 :     CPLString osFilename;
    1007             : 
    1008         131 :     if (strcmp(pszName, "/vsistdout/") == 0)
    1009             :     {
    1010           1 :         osFilename = pszName;
    1011           1 :         if (bCreateCSVT)
    1012             :         {
    1013           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1014             :                      "CREATE_CSVT is not compatible with /vsistdout/ output");
    1015           0 :             return nullptr;
    1016             :         }
    1017             :     }
    1018         130 :     else if (osDefaultCSVName != "")
    1019             :     {
    1020          65 :         osFilename = CPLFormFilenameSafe(pszName, osDefaultCSVName, nullptr);
    1021          65 :         osDefaultCSVName = "";
    1022             :     }
    1023             :     else
    1024             :     {
    1025          65 :         if (CPLLaunderForFilenameSafe(pszLayerName, nullptr) != pszLayerName)
    1026             :         {
    1027           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    1028             :                      "Illegal characters in '%s' to form a valid filename",
    1029             :                      pszLayerName);
    1030           1 :             return nullptr;
    1031             :         }
    1032          64 :         osFilename = CPLFormFilenameSafe(pszName, pszLayerName, "csv");
    1033             :     }
    1034             : 
    1035             :     // Does this directory/file already exist?
    1036         130 :     if (VSIStatL(osFilename, &sStatBuf) == 0)
    1037             :     {
    1038           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1039             :                  "Attempt to create layer %s, but %s already exists.",
    1040             :                  pszLayerName, osFilename.c_str());
    1041           1 :         return nullptr;
    1042             :     }
    1043             : 
    1044             :     // Create the empty file.
    1045             : 
    1046         129 :     const char *pszDelimiter = CSLFetchNameValue(papszOptions, "SEPARATOR");
    1047         129 :     char chDelimiter = ',';
    1048         129 :     if (pszDelimiter != nullptr)
    1049             :     {
    1050           2 :         if (EQUAL(pszDelimiter, "COMMA"))
    1051           0 :             chDelimiter = ',';
    1052           2 :         else if (EQUAL(pszDelimiter, "SEMICOLON"))
    1053           1 :             chDelimiter = ';';
    1054           1 :         else if (EQUAL(pszDelimiter, "TAB"))
    1055           0 :             chDelimiter = '\t';
    1056           1 :         else if (EQUAL(pszDelimiter, "SPACE"))
    1057           1 :             chDelimiter = ' ';
    1058           0 :         else if (EQUAL(pszDelimiter, "PIPE"))
    1059           0 :             chDelimiter = '|';
    1060             :         else
    1061             :         {
    1062           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1063             :                      "SEPARATOR=%s not understood, use one of COMMA, "
    1064             :                      "SEMICOLON, TAB, SPACE or PIPE.",
    1065             :                      pszDelimiter);
    1066             :         }
    1067             :     }
    1068             : 
    1069             :     // Create a layer.
    1070             : 
    1071             :     auto poCSVLayer = std::make_unique<OGRCSVLayer>(
    1072         258 :         this, pszLayerName, nullptr, -1, osFilename, true, true, chDelimiter);
    1073             : 
    1074         129 :     poCSVLayer->BuildFeatureDefn();
    1075             : 
    1076             :     // Was a particular CRLF order requested?
    1077         129 :     const char *pszCRLFFormat = CSLFetchNameValue(papszOptions, "LINEFORMAT");
    1078         129 :     bool bUseCRLF = false;
    1079             : 
    1080         129 :     if (pszCRLFFormat == nullptr)
    1081             :     {
    1082             : #ifdef _WIN32
    1083             :         bUseCRLF = true;
    1084             : #endif
    1085             :     }
    1086          18 :     else if (EQUAL(pszCRLFFormat, "CRLF"))
    1087             :     {
    1088           1 :         bUseCRLF = true;
    1089             :     }
    1090          17 :     else if (!EQUAL(pszCRLFFormat, "LF"))
    1091             :     {
    1092           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1093             :                  "LINEFORMAT=%s not understood, use one of CRLF or LF.",
    1094             :                  pszCRLFFormat);
    1095             : #ifdef _WIN32
    1096             :         bUseCRLF = true;
    1097             : #endif
    1098             :     }
    1099             : 
    1100         129 :     poCSVLayer->SetCRLF(bUseCRLF);
    1101             : 
    1102             :     const char *pszStringQuoting =
    1103         129 :         CSLFetchNameValueDef(papszOptions, "STRING_QUOTING", "IF_AMBIGUOUS");
    1104         258 :     poCSVLayer->SetStringQuoting(
    1105         129 :         EQUAL(pszStringQuoting, "IF_NEEDED")
    1106             :             ? OGRCSVLayer::StringQuoting::IF_NEEDED
    1107         116 :         : EQUAL(pszStringQuoting, "ALWAYS")
    1108         116 :             ? OGRCSVLayer::StringQuoting::ALWAYS
    1109             :             : OGRCSVLayer::StringQuoting::IF_AMBIGUOUS);
    1110             : 
    1111             :     // Should we write the geometry?
    1112         129 :     const char *pszGeometry = CSLFetchNameValue(papszOptions, "GEOMETRY");
    1113         129 :     if (bEnableGeometryFields)
    1114             :     {
    1115          11 :         poCSVLayer->SetWriteGeometry(
    1116             :             eGType, OGR_CSV_GEOM_AS_WKT,
    1117             :             CSLFetchNameValueDef(papszOptions, "GEOMETRY_NAME", "WKT"));
    1118             :     }
    1119         118 :     else if (pszGeometry != nullptr)
    1120             :     {
    1121          35 :         if (EQUAL(pszGeometry, "AS_WKT"))
    1122             :         {
    1123          28 :             poCSVLayer->SetWriteGeometry(
    1124             :                 eGType, OGR_CSV_GEOM_AS_WKT,
    1125             :                 CSLFetchNameValueDef(papszOptions, "GEOMETRY_NAME", "WKT"));
    1126             :         }
    1127           7 :         else if (EQUAL(pszGeometry, "AS_XYZ") || EQUAL(pszGeometry, "AS_XY") ||
    1128           2 :                  EQUAL(pszGeometry, "AS_YX"))
    1129             :         {
    1130           6 :             if (eGType == wkbUnknown || wkbFlatten(eGType) == wkbPoint)
    1131             :             {
    1132          10 :                 poCSVLayer->SetWriteGeometry(
    1133           5 :                     eGType, EQUAL(pszGeometry, "AS_XYZ")  ? OGR_CSV_GEOM_AS_XYZ
    1134           3 :                             : EQUAL(pszGeometry, "AS_XY") ? OGR_CSV_GEOM_AS_XY
    1135             :                                                           : OGR_CSV_GEOM_AS_YX);
    1136             :             }
    1137             :             else
    1138             :             {
    1139           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1140             :                          "Geometry type %s is not compatible with "
    1141             :                          "GEOMETRY=%s.",
    1142             :                          OGRGeometryTypeToName(eGType), pszGeometry);
    1143           1 :                 return nullptr;
    1144             :             }
    1145             :         }
    1146             :         else
    1147             :         {
    1148           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    1149             :                      "Unsupported value %s for creation option GEOMETRY",
    1150             :                      pszGeometry);
    1151           1 :             return nullptr;
    1152             :         }
    1153             :     }
    1154             : 
    1155             :     // Should we create a CSVT file?
    1156         127 :     if (bCreateCSVT)
    1157             :     {
    1158          29 :         poCSVLayer->SetCreateCSVT(true);
    1159             : 
    1160             :         // Create .prj file.
    1161          29 :         if (poSpatialRef != nullptr)
    1162             :         {
    1163           8 :             char *pszWKT = nullptr;
    1164           8 :             poSpatialRef->exportToWkt(&pszWKT);
    1165           8 :             if (pszWKT)
    1166             :             {
    1167           8 :                 VSILFILE *fpPRJ = VSIFOpenL(
    1168          16 :                     CPLResetExtensionSafe(osFilename, "prj").c_str(), "wb");
    1169           8 :                 if (fpPRJ)
    1170             :                 {
    1171           8 :                     CPL_IGNORE_RET_VAL(VSIFPrintfL(fpPRJ, "%s\n", pszWKT));
    1172           8 :                     VSIFCloseL(fpPRJ);
    1173             :                 }
    1174           8 :                 CPLFree(pszWKT);
    1175             :             }
    1176             :         }
    1177             :     }
    1178             : 
    1179             :     // Should we write a UTF8 BOM?
    1180         127 :     const char *pszWriteBOM = CSLFetchNameValue(papszOptions, "WRITE_BOM");
    1181         127 :     if (pszWriteBOM)
    1182           2 :         poCSVLayer->SetWriteBOM(CPLTestBool(pszWriteBOM));
    1183             : 
    1184         127 :     auto poFeatureDefn = poCSVLayer->GetLayerDefn();
    1185         127 :     if (poFeatureDefn->GetGeomFieldCount() > 0 && poSrcGeomFieldDefn)
    1186             :     {
    1187          39 :         auto poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(0);
    1188          39 :         poGeomFieldDefn->SetCoordinatePrecision(
    1189             :             poSrcGeomFieldDefn->GetCoordinatePrecision());
    1190             :     }
    1191             : 
    1192         127 :     poCSVLayer->SetWriteHeader(CPLTestBool(CSLFetchNameValueDef(
    1193             :         papszOptions, "HEADER",
    1194             :         CSLFetchNameValueDef(papszOptions, "HEADERS", "YES"))));
    1195             : 
    1196         127 :     if (osFilename != "/vsistdout/")
    1197         126 :         m_apoLayers.emplace_back(std::make_unique<OGRCSVEditableLayer>(
    1198         252 :             poCSVLayer.release(), nullptr));
    1199             :     else
    1200           1 :         m_apoLayers.emplace_back(std::move(poCSVLayer));
    1201             : 
    1202         127 :     return m_apoLayers.back()->GetLayer();
    1203             : }
    1204             : 
    1205             : /************************************************************************/
    1206             : /*                            DeleteLayer()                             */
    1207             : /************************************************************************/
    1208             : 
    1209          20 : OGRErr OGRCSVDataSource::DeleteLayer(int iLayer)
    1210             : 
    1211             : {
    1212             :     // Verify we are in update mode.
    1213          20 :     if (!bUpdate)
    1214             :     {
    1215           1 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    1216             :                  "Data source %s opened read-only.\n"
    1217             :                  "Layer %d cannot be deleted.",
    1218             :                  pszName, iLayer);
    1219             : 
    1220           1 :         return OGRERR_FAILURE;
    1221             :     }
    1222             : 
    1223          19 :     if (iLayer < 0 || iLayer >= GetLayerCount())
    1224             :     {
    1225           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    1226             :                  "Layer %d not in legal range of 0 to %d.", iLayer,
    1227           2 :                  GetLayerCount() - 1);
    1228           2 :         return OGRERR_FAILURE;
    1229             :     }
    1230             : 
    1231          34 :     for (const auto &osFilename : m_apoLayers[iLayer]->GetFileList())
    1232             :     {
    1233          17 :         VSIUnlink(osFilename.c_str());
    1234             :     }
    1235             : 
    1236          17 :     m_apoLayers.erase(m_apoLayers.begin() + iLayer);
    1237             : 
    1238          17 :     return OGRERR_NONE;
    1239             : }
    1240             : 
    1241             : /************************************************************************/
    1242             : /*                       CreateForSingleFile()                          */
    1243             : /************************************************************************/
    1244             : 
    1245          65 : void OGRCSVDataSource::CreateForSingleFile(const char *pszDirname,
    1246             :                                            const char *pszFilename)
    1247             : {
    1248          65 :     pszName = CPLStrdup(pszDirname);
    1249          65 :     bUpdate = true;
    1250          65 :     osDefaultCSVName = CPLGetFilename(pszFilename);
    1251          65 : }
    1252             : 
    1253             : /************************************************************************/
    1254             : /*                            GetFileList()                             */
    1255             : /************************************************************************/
    1256             : 
    1257           7 : char **OGRCSVDataSource::GetFileList()
    1258             : {
    1259          14 :     CPLStringList oFileList;
    1260          19 :     for (auto &poLayer : m_apoLayers)
    1261             :     {
    1262          35 :         for (const auto &osFilename : poLayer->GetFileList())
    1263             :         {
    1264          23 :             oFileList.AddString(osFilename.c_str());
    1265             :         }
    1266             :     }
    1267          14 :     return oFileList.StealList();
    1268             : }

Generated by: LCOV version 1.14