LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/csv - ogrcsvdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 497 629 79.0 %
Date: 2025-08-01 10:10:57 Functions: 25 26 96.2 %

          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         211 :     OGRCSVEditableLayerSynchronizer(OGRCSVLayer *poCSVLayer,
      50             :                                     CSLConstList papszOpenOptions)
      51         211 :         : m_poCSVLayer(poCSVLayer),
      52         211 :           m_papszOpenOptions(CSLDuplicate(papszOpenOptions))
      53             :     {
      54         211 :     }
      55             : 
      56             :     virtual ~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         422 : OGRCSVEditableLayerSynchronizer::~OGRCSVEditableLayerSynchronizer()
      72             : {
      73         211 :     CSLDestroy(m_papszOpenOptions);
      74         422 : }
      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         331 :     OGRLayer *GetLayer() override
     311             :     {
     312         331 :         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             :     virtual OGRErr DeleteField(int iField) override;
     325             :     virtual OGRErr AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
     326             :                                   int nFlagsIn) override;
     327             :     virtual GIntBig GetFeatureCount(int bForce = TRUE) override;
     328             : };
     329             : 
     330             : /************************************************************************/
     331             : /*                       OGRCSVEditableLayer()                          */
     332             : /************************************************************************/
     333             : 
     334         211 : OGRCSVEditableLayer::OGRCSVEditableLayer(OGRCSVLayer *poCSVLayer,
     335         211 :                                          CSLConstList papszOpenOptions)
     336             :     : OGREditableLayer(
     337             :           poCSVLayer, true,
     338         211 :           new OGRCSVEditableLayerSynchronizer(poCSVLayer, papszOpenOptions),
     339         422 :           true)
     340             : {
     341         211 :     SetSupportsCreateGeomField(true);
     342         211 :     SetSupportsCurveGeometries(true);
     343         211 : }
     344             : 
     345             : /************************************************************************/
     346             : /*                            CreateField()                             */
     347             : /************************************************************************/
     348             : 
     349         412 : OGRErr OGRCSVEditableLayer::CreateField(const OGRFieldDefn *poNewField,
     350             :                                         int bApproxOK)
     351             : 
     352             : {
     353         412 :     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         412 :     if (m_oSetFields.empty())
     360             :     {
     361         104 :         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         824 :     const OGRCSVCreateFieldAction eAction = OGRCSVLayer::PreCreateField(
     371         412 :         m_poEditableFeatureDefn, m_oSetFields, poNewField, bApproxOK);
     372         412 :     if (eAction == CREATE_FIELD_DO_NOTHING)
     373           0 :         return OGRERR_NONE;
     374         412 :     if (eAction == CREATE_FIELD_ERROR)
     375           1 :         return OGRERR_FAILURE;
     376         411 :     OGRErr eErr = OGREditableLayer::CreateField(poNewField, bApproxOK);
     377         411 :     if (eErr == OGRERR_NONE)
     378             :     {
     379         411 :         m_oSetFields.insert(CPLString(poNewField->GetNameRef()).toupper());
     380             :     }
     381         411 :     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        2632 : OGRCSVDataSource::~OGRCSVDataSource()
     427             : 
     428             : {
     429        1316 :     m_apoLayers.clear();
     430             : 
     431        1316 :     if (bUpdate)
     432         250 :         OGRCSVDriverRemoveFromMap(pszName, this);
     433             : 
     434        1316 :     CPLFree(pszName);
     435        2632 : }
     436             : 
     437             : /************************************************************************/
     438             : /*                           TestCapability()                           */
     439             : /************************************************************************/
     440             : 
     441         206 : int OGRCSVDataSource::TestCapability(const char *pszCap)
     442             : 
     443             : {
     444         206 :     if (EQUAL(pszCap, ODsCCreateLayer))
     445          67 :         return bUpdate;
     446         139 :     else if (EQUAL(pszCap, ODsCDeleteLayer))
     447          19 :         return bUpdate;
     448         120 :     else if (EQUAL(pszCap, ODsCCreateGeomFieldAfterCreateLayer))
     449          32 :         return bUpdate && bEnableGeometryFields;
     450          88 :     else if (EQUAL(pszCap, ODsCCurveGeometries))
     451           3 :         return TRUE;
     452          85 :     else if (EQUAL(pszCap, ODsCMeasuredGeometries))
     453           3 :         return TRUE;
     454          82 :     else if (EQUAL(pszCap, ODsCZGeometries))
     455           2 :         return TRUE;
     456          80 :     else if (EQUAL(pszCap, ODsCRandomLayerWrite))
     457           0 :         return bUpdate;
     458             :     else
     459          80 :         return FALSE;
     460             : }
     461             : 
     462             : /************************************************************************/
     463             : /*                              GetLayer()                              */
     464             : /************************************************************************/
     465             : 
     466         923 : OGRLayer *OGRCSVDataSource::GetLayer(int iLayer)
     467             : 
     468             : {
     469         923 :     if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
     470           2 :         return nullptr;
     471             : 
     472         921 :     return m_apoLayers[iLayer]->GetLayer();
     473             : }
     474             : 
     475             : /************************************************************************/
     476             : /*                          GetRealExtension()                          */
     477             : /************************************************************************/
     478             : 
     479        7944 : CPLString OGRCSVDataSource::GetRealExtension(CPLString osFilename)
     480             : {
     481       15888 :     CPLString osExt = CPLGetExtensionSafe(osFilename);
     482        7944 :     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        7944 :     return osExt;
     495             : }
     496             : 
     497             : /************************************************************************/
     498             : /*                                Open()                                */
     499             : /************************************************************************/
     500             : 
     501        1255 : bool OGRCSVDataSource::Open(const char *pszFilename, bool bUpdateIn,
     502             :                             bool bForceOpen, CSLConstList papszOpenOptionsIn,
     503             :                             bool bSingleDriver)
     504             : 
     505             : {
     506        1255 :     pszName = CPLStrdup(pszFilename);
     507        1255 :     bUpdate = CPL_TO_BOOL(bUpdateIn);
     508             : 
     509        1255 :     if (bUpdate && bForceOpen && EQUAL(pszFilename, "/vsistdout/"))
     510           1 :         return TRUE;
     511             : 
     512             :     // For writable /vsizip/, do nothing more.
     513        1254 :     if (bUpdate && bForceOpen && STARTS_WITH(pszFilename, "/vsizip/"))
     514           0 :         return TRUE;
     515             : 
     516        2508 :     CPLString osFilename(pszFilename);
     517        2508 :     const CPLString osBaseFilename = CPLGetFilename(pszFilename);
     518        2508 :     const CPLString osExt = GetRealExtension(osFilename);
     519             : 
     520        1254 :     bool bUSGeonamesFile = false;
     521        1254 :     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        1254 :     if (EQUAL(osBaseFilename, "NfdcFacilities.xls") ||
     530        1254 :         EQUAL(osBaseFilename, "NfdcRunways.xls") ||
     531        3762 :         EQUAL(osBaseFilename, "NfdcRemarks.xls") ||
     532        1254 :         EQUAL(osBaseFilename, "NfdcSchedules.xls"))
     533             :     {
     534           0 :         if (bUpdate)
     535           0 :             return FALSE;
     536           0 :         bSingleDriver = true;
     537             :     }
     538        1254 :     else if ((STARTS_WITH_CI(osBaseFilename, "NationalFile_") ||
     539        1254 :               STARTS_WITH_CI(osBaseFilename, "POP_PLACES_") ||
     540        1254 :               STARTS_WITH_CI(osBaseFilename, "HIST_FEATURES_") ||
     541        1254 :               STARTS_WITH_CI(osBaseFilename, "US_CONCISE_") ||
     542        1254 :               STARTS_WITH_CI(osBaseFilename, "AllNames_") ||
     543        1254 :               STARTS_WITH_CI(osBaseFilename, "Feature_Description_History_") ||
     544        1254 :               STARTS_WITH_CI(osBaseFilename, "ANTARCTICA_") ||
     545        1254 :               STARTS_WITH_CI(osBaseFilename, "GOVT_UNITS_") ||
     546        1254 :               STARTS_WITH_CI(osBaseFilename, "NationalFedCodes_") ||
     547        1254 :               STARTS_WITH_CI(osBaseFilename, "AllStates_") ||
     548        2508 :               STARTS_WITH_CI(osBaseFilename, "AllStatesFedCodes_") ||
     549        1254 :               (osBaseFilename.size() > 2 &&
     550        2488 :                STARTS_WITH_CI(osBaseFilename + 2, "_Features_")) ||
     551        1254 :               (osBaseFilename.size() > 2 &&
     552        2508 :                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        2507 :     else if (EQUAL(osBaseFilename, "allCountries.txt") ||
     566        1253 :              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        1254 :     if (VSIStatExL(osFilename, &sStatBuf, VSI_STAT_NATURE_FLAG) != 0)
     582           0 :         return FALSE;
     583             : 
     584             :     // Is this a single CSV file?
     585        1664 :     if (VSI_ISREG(sStatBuf.st_mode) &&
     586         410 :         (bSingleDriver || EQUAL(osExt, "csv") || EQUAL(osExt, "tsv") ||
     587           2 :          EQUAL(osExt, "psv")))
     588             :     {
     589         501 :         if (EQUAL(CPLGetFilename(osFilename), "NfdcFacilities.xls"))
     590             :         {
     591           0 :             return OpenTable(osFilename, papszOpenOptionsIn, "ARP");
     592             :         }
     593         501 :         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         501 :         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         501 :         return OpenTable(osFilename, papszOpenOptionsIn);
     627             :     }
     628             : 
     629             :     // Is this a single a ZIP file with only a CSV file inside?
     630         758 :     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         752 :     if (!VSI_ISDIR(sStatBuf.st_mode))
     647           0 :         return FALSE;
     648             : 
     649             :     // Scan through for entries ending in .csv.
     650         752 :     int nNotCSVCount = 0;
     651         752 :     const CPLStringList aosFilenames(VSIReadDir(osFilename));
     652             : 
     653       26790 :     for (int i = 0; i < aosFilenames.size(); i++)
     654             :     {
     655       26038 :         if (EQUAL(aosFilenames[i], ".") || EQUAL(aosFilenames[i], ".."))
     656       25895 :             continue;
     657             : 
     658       25290 :         const std::string osThisExt = CPLGetExtensionSafe(aosFilenames[i]);
     659       25290 :         if (EQUAL(osThisExt.c_str(), "csvt") || EQUAL(osThisExt.c_str(), "prj"))
     660          94 :             continue;
     661             : 
     662             :         const CPLString oSubFilename =
     663       25196 :             CPLFormFilenameSafe(osFilename, aosFilenames[i], nullptr);
     664       50392 :         if (VSIStatL(oSubFilename, &sStatBuf) != 0 ||
     665       25196 :             !VSI_ISREG(sStatBuf.st_mode))
     666             :         {
     667         143 :             nNotCSVCount++;
     668         143 :             continue;
     669             :         }
     670             : 
     671       25053 :         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       24910 :         else if (strlen(aosFilenames[i]) > 2 &&
     682       24910 :                  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       24910 :         else if (strlen(aosFilenames[i]) > 2 &&
     698       24910 :                  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       24910 :             nNotCSVCount++;
     712       24910 :             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         754 :     return bForceOpen || nNotCSVCount < GetLayerCount() ||
     719         754 :            (bSingleDriver && GetLayerCount() > 0);
     720             : }
     721             : 
     722      210501 : const std::vector<int> &OGRCSVDataSource::DeletedFieldIndexes() const
     723             : {
     724      210501 :     return m_oDeletedFieldIndexes;
     725             : }
     726             : 
     727             : /************************************************************************/
     728             : /*                      DealWithOgrSchemaOpenOption()                   */
     729             : /************************************************************************/
     730         556 : bool OGRCSVDataSource::DealWithOgrSchemaOpenOption(
     731             :     CSLConstList papszOpenOptionsIn)
     732             : {
     733             :     std::string osFieldsSchemaOverrideParam =
     734        1112 :         CSLFetchNameValueDef(papszOpenOptionsIn, "OGR_SCHEMA", "");
     735             : 
     736         556 :     if (!osFieldsSchemaOverrideParam.empty())
     737             :     {
     738           8 :         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           8 :         OGRSchemaOverride osSchemaOverride;
     746          15 :         if (!osSchemaOverride.LoadFromJSON(osFieldsSchemaOverrideParam) ||
     747           7 :             !osSchemaOverride.IsValid())
     748             :         {
     749           1 :             return false;
     750             :         }
     751             : 
     752           7 :         const auto &oLayerOverrides = osSchemaOverride.GetLayerOverrides();
     753          14 :         for (const auto &oLayer : oLayerOverrides)
     754             :         {
     755           7 :             const auto &oLayerName = oLayer.first;
     756           7 :             const auto &oLayerFieldOverride = oLayer.second;
     757           7 :             const bool bIsFullOverride{oLayerFieldOverride.IsFullOverride()};
     758           7 :             auto oFieldOverrides = oLayerFieldOverride.GetFieldOverrides();
     759           7 :             std::vector<OGRFieldDefn *> aoFields;
     760             : 
     761           7 :             CPLDebug("CSV", "Applying schema override for layer %s",
     762             :                      oLayerName.c_str());
     763             : 
     764             :             // Fail if the layer name does not exist
     765           7 :             auto poLayer = GetLayerByName(oLayerName.c_str());
     766           7 :             if (poLayer == nullptr)
     767             :             {
     768           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     769             :                          "Layer %s not found in CSV file", oLayerName.c_str());
     770           0 :                 return false;
     771             :             }
     772             : 
     773             :             // Patch field definitions
     774           7 :             auto poLayerDefn = poLayer->GetLayerDefn();
     775          48 :             for (int i = 0; i < poLayerDefn->GetFieldCount(); i++)
     776             :             {
     777          41 :                 auto poFieldDefn = poLayerDefn->GetFieldDefn(i);
     778             :                 auto oFieldOverride =
     779          41 :                     oFieldOverrides.find(poFieldDefn->GetNameRef());
     780          41 :                 if (oFieldOverride != oFieldOverrides.cend())
     781             :                 {
     782           9 :                     if (oFieldOverride->second.GetFieldType().has_value())
     783           6 :                         whileUnsealing(poFieldDefn)
     784           3 :                             ->SetType(
     785           3 :                                 oFieldOverride->second.GetFieldType().value());
     786           9 :                     if (oFieldOverride->second.GetFieldWidth().has_value())
     787           2 :                         whileUnsealing(poFieldDefn)
     788           1 :                             ->SetWidth(
     789           1 :                                 oFieldOverride->second.GetFieldWidth().value());
     790           9 :                     if (oFieldOverride->second.GetFieldPrecision().has_value())
     791           2 :                         whileUnsealing(poFieldDefn)
     792           1 :                             ->SetPrecision(
     793           2 :                                 oFieldOverride->second.GetFieldPrecision()
     794           1 :                                     .value());
     795           9 :                     if (oFieldOverride->second.GetFieldSubType().has_value())
     796           6 :                         whileUnsealing(poFieldDefn)
     797           3 :                             ->SetSubType(
     798           6 :                                 oFieldOverride->second.GetFieldSubType()
     799           3 :                                     .value());
     800           9 :                     if (oFieldOverride->second.GetFieldName().has_value())
     801           0 :                         whileUnsealing(poFieldDefn)
     802           0 :                             ->SetName(oFieldOverride->second.GetFieldName()
     803           0 :                                           .value()
     804             :                                           .c_str());
     805             : 
     806           9 :                     if (bIsFullOverride)
     807             :                     {
     808           4 :                         aoFields.push_back(poFieldDefn);
     809             :                     }
     810           9 :                     oFieldOverrides.erase(oFieldOverride);
     811             :                 }
     812             :             }
     813             : 
     814             :             // Error if any field override is not found
     815           7 :             if (!oFieldOverrides.empty())
     816             :             {
     817           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     818             :                          "Field %s not found in layer %s",
     819           0 :                          oFieldOverrides.cbegin()->first.c_str(),
     820             :                          oLayerName.c_str());
     821           0 :                 return false;
     822             :             }
     823             : 
     824             :             // Remove fields not in the override
     825           7 :             if (bIsFullOverride)
     826             :             {
     827          17 :                 for (int i = poLayerDefn->GetFieldCount() - 1; i >= 0; i--)
     828             :                 {
     829          14 :                     auto poFieldDefn = poLayerDefn->GetFieldDefn(i);
     830          14 :                     if (std::find(aoFields.begin(), aoFields.end(),
     831          14 :                                   poFieldDefn) == aoFields.end())
     832             :                     {
     833          10 :                         whileUnsealing(poLayerDefn)->DeleteFieldDefn(i);
     834          10 :                         m_oDeletedFieldIndexes.push_back(i);
     835             :                     }
     836             :                 }
     837             :             }
     838             :         }
     839             :     }
     840         555 :     return true;
     841             : }
     842             : 
     843             : /************************************************************************/
     844             : /*                              OpenTable()                             */
     845             : /************************************************************************/
     846             : 
     847         644 : bool OGRCSVDataSource::OpenTable(const char *pszFilename,
     848             :                                  CSLConstList papszOpenOptionsIn,
     849             :                                  const char *pszNfdcRunwaysGeomField,
     850             :                                  const char *pszGeonamesGeomFieldPrefix)
     851             : 
     852             : {
     853             :     // Open the file.
     854         644 :     VSILFILE *fp = nullptr;
     855             : 
     856         644 :     if (bUpdate)
     857          88 :         fp = VSIFOpenExL(pszFilename, "rb+", true);
     858             :     else
     859         556 :         fp = VSIFOpenExL(pszFilename, "rb", true);
     860         644 :     if (fp == nullptr)
     861             :     {
     862           0 :         CPLError(CE_Warning, CPLE_OpenFailed, "Failed to open %s.",
     863             :                  VSIGetLastErrorMsg());
     864           0 :         return false;
     865             :     }
     866             : 
     867         644 :     if (!bUpdate && strstr(pszFilename, "/vsigzip/") == nullptr &&
     868         556 :         strstr(pszFilename, "/vsizip/") == nullptr)
     869         472 :         fp = VSICreateBufferedReaderHandle(fp);
     870             : 
     871        1288 :     CPLString osLayerName = CPLGetBasenameSafe(pszFilename);
     872        1288 :     CPLString osExt = CPLGetExtensionSafe(pszFilename);
     873         644 :     if (STARTS_WITH(pszFilename, "/vsigzip/") && EQUAL(osExt, "gz"))
     874             :     {
     875           0 :         if (strlen(pszFilename) > 7 &&
     876           0 :             EQUAL(pszFilename + strlen(pszFilename) - 7, ".csv.gz"))
     877             :         {
     878           0 :             osLayerName = osLayerName.substr(0, osLayerName.size() - 4);
     879           0 :             osExt = "csv";
     880             :         }
     881           0 :         else if (strlen(pszFilename) > 7 &&
     882           0 :                  EQUAL(pszFilename + strlen(pszFilename) - 7, ".tsv.gz"))
     883             :         {
     884           0 :             osLayerName = osLayerName.substr(0, osLayerName.size() - 4);
     885           0 :             osExt = "tsv";
     886             :         }
     887           0 :         else if (strlen(pszFilename) > 7 &&
     888           0 :                  EQUAL(pszFilename + strlen(pszFilename) - 7, ".psv.gz"))
     889             :         {
     890           0 :             osLayerName = osLayerName.substr(0, osLayerName.size() - 4);
     891           0 :             osExt = "psv";
     892             :         }
     893             :     }
     894             : 
     895         644 :     int nMaxLineSize = atoi(CPLGetConfigOption(
     896             :         "OGR_CSV_MAX_LINE_SIZE",
     897             :         CSLFetchNameValueDef(papszOpenOptionsIn, "MAX_LINE_SIZE",
     898         644 :                              CPLSPrintf("%d", OGR_CSV_DEFAULT_MAX_LINE_SIZE))));
     899         644 :     size_t nMaxLineSizeAsSize_t = static_cast<size_t>(nMaxLineSize);
     900         644 :     if (nMaxLineSize == 0)
     901             :     {
     902           0 :         nMaxLineSize = -1;
     903           0 :         nMaxLineSizeAsSize_t = static_cast<size_t>(-1);
     904             :     }
     905             : 
     906             :     // Read and parse a line to detect separator.
     907             : 
     908        1288 :     std::string osLine;
     909             :     {
     910         644 :         const char *pszLine = CPLReadLine2L(fp, nMaxLineSize, nullptr);
     911         644 :         if (pszLine == nullptr)
     912             :         {
     913           0 :             VSIFCloseL(fp);
     914           0 :             return false;
     915             :         }
     916         644 :         osLine = pszLine;
     917             :     }
     918             : 
     919         644 :     char chDelimiter = ',';
     920             :     const char *pszDelimiter =
     921         644 :         CSLFetchNameValueDef(papszOpenOptionsIn, "SEPARATOR", "AUTO");
     922         644 :     if (EQUAL(pszDelimiter, "AUTO"))
     923             :     {
     924         636 :         chDelimiter = CSVDetectSeperator(osLine.c_str());
     925         636 :         if (chDelimiter != '\t' && osLine.find('\t') != std::string::npos)
     926             :         {
     927             :             // Force the delimiter to be TAB for a .tsv file that has a tabulation
     928             :             // in its first line */
     929           0 :             if (EQUAL(osExt, "tsv"))
     930             :             {
     931           0 :                 chDelimiter = '\t';
     932             :             }
     933             :             else
     934             :             {
     935           0 :                 for (int nDontHonourStrings = 0; nDontHonourStrings <= 1;
     936             :                      nDontHonourStrings++)
     937             :                 {
     938             :                     const bool bHonourStrings =
     939           0 :                         !CPL_TO_BOOL(nDontHonourStrings);
     940             :                     // Read the first 2 lines to see if they have the same number
     941             :                     // of fields, if using tabulation.
     942           0 :                     VSIRewindL(fp);
     943           0 :                     char **papszTokens = CSVReadParseLine3L(
     944             :                         fp, nMaxLineSizeAsSize_t, "\t", bHonourStrings,
     945             :                         false,  // bKeepLeadingAndClosingQuotes
     946             :                         false,  // bMergeDelimiter
     947             :                         true    // bSkipBOM
     948             :                     );
     949           0 :                     const int nTokens1 = CSLCount(papszTokens);
     950           0 :                     CSLDestroy(papszTokens);
     951           0 :                     papszTokens = CSVReadParseLine3L(
     952             :                         fp, nMaxLineSizeAsSize_t, "\t", bHonourStrings,
     953             :                         false,  // bKeepLeadingAndClosingQuotes
     954             :                         false,  // bMergeDelimiter
     955             :                         true    // bSkipBOM
     956             :                     );
     957           0 :                     const int nTokens2 = CSLCount(papszTokens);
     958           0 :                     CSLDestroy(papszTokens);
     959           0 :                     if (nTokens1 >= 2 && nTokens1 == nTokens2)
     960             :                     {
     961           0 :                         chDelimiter = '\t';
     962           0 :                         break;
     963             :                     }
     964             :                 }
     965             :             }
     966             :         }
     967             : 
     968             :         // GNIS specific.
     969         636 :         if (pszGeonamesGeomFieldPrefix != nullptr &&
     970           0 :             osLine.find('|') != std::string::npos)
     971           0 :             chDelimiter = '|';
     972             :     }
     973           8 :     else if (EQUAL(pszDelimiter, "COMMA"))
     974           4 :         chDelimiter = ',';
     975           4 :     else if (EQUAL(pszDelimiter, "SEMICOLON"))
     976           1 :         chDelimiter = ';';
     977           3 :     else if (EQUAL(pszDelimiter, "TAB"))
     978           1 :         chDelimiter = '\t';
     979           2 :     else if (EQUAL(pszDelimiter, "SPACE"))
     980           1 :         chDelimiter = ' ';
     981           1 :     else if (EQUAL(pszDelimiter, "PIPE"))
     982           1 :         chDelimiter = '|';
     983             :     else
     984             :     {
     985           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     986             :                  "SEPARATOR=%s not understood, use one of COMMA, "
     987             :                  "SEMICOLON, TAB, SPACE or PIPE.",
     988             :                  pszDelimiter);
     989             :     }
     990             : 
     991         644 :     VSIRewindL(fp);
     992             : 
     993             :     // Create a layer.
     994         644 :     if (pszNfdcRunwaysGeomField != nullptr)
     995             :     {
     996           0 :         osLayerName += "_";
     997           0 :         osLayerName += pszNfdcRunwaysGeomField;
     998             :     }
     999         644 :     else if (pszGeonamesGeomFieldPrefix != nullptr &&
    1000           0 :              !EQUAL(pszGeonamesGeomFieldPrefix, ""))
    1001             :     {
    1002           0 :         osLayerName += "_";
    1003           0 :         osLayerName += pszGeonamesGeomFieldPrefix;
    1004             :     }
    1005         644 :     if (EQUAL(pszFilename, "/vsistdin/"))
    1006           0 :         osLayerName = "layer";
    1007             : 
    1008             :     auto poCSVLayer =
    1009             :         std::make_unique<OGRCSVLayer>(this, osLayerName, fp, nMaxLineSize,
    1010        1288 :                                       pszFilename, FALSE, bUpdate, chDelimiter);
    1011         644 :     poCSVLayer->BuildFeatureDefn(pszNfdcRunwaysGeomField,
    1012             :                                  pszGeonamesGeomFieldPrefix,
    1013             :                                  papszOpenOptionsIn);
    1014         644 :     if (bUpdate)
    1015             :     {
    1016          88 :         m_apoLayers.emplace_back(std::make_unique<OGRCSVEditableLayer>(
    1017         176 :             poCSVLayer.release(), papszOpenOptionsIn));
    1018             :     }
    1019             :     else
    1020             :     {
    1021         556 :         m_apoLayers.emplace_back(std::move(poCSVLayer));
    1022             : 
    1023         556 :         if (!DealWithOgrSchemaOpenOption(papszOpenOptionsIn))
    1024             :         {
    1025           1 :             m_apoLayers.pop_back();
    1026           1 :             return false;
    1027             :         }
    1028             :     }
    1029             : 
    1030         643 :     return true;
    1031             : }
    1032             : 
    1033             : /************************************************************************/
    1034             : /*                           ICreateLayer()                             */
    1035             : /************************************************************************/
    1036             : 
    1037             : OGRLayer *
    1038         128 : OGRCSVDataSource::ICreateLayer(const char *pszLayerName,
    1039             :                                const OGRGeomFieldDefn *poSrcGeomFieldDefn,
    1040             :                                CSLConstList papszOptions)
    1041             : {
    1042             :     // Verify we are in update mode.
    1043         128 :     if (!bUpdate)
    1044             :     {
    1045           1 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    1046             :                  "Data source %s opened read-only.\n"
    1047             :                  "New layer %s cannot be created.",
    1048             :                  pszName, pszLayerName);
    1049             : 
    1050           1 :         return nullptr;
    1051             :     }
    1052             : 
    1053             :     const auto eGType =
    1054         127 :         poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetType() : wkbNone;
    1055             :     const auto poSpatialRef =
    1056         127 :         poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetSpatialRef() : nullptr;
    1057             : 
    1058             :     // Verify that the datasource is a directory.
    1059             :     VSIStatBufL sStatBuf;
    1060             : 
    1061         127 :     if (STARTS_WITH(pszName, "/vsizip/"))
    1062             :     {
    1063             :         // Do nothing.
    1064             :     }
    1065         253 :     else if (!EQUAL(pszName, "/vsistdout/") &&
    1066         126 :              (VSIStatL(pszName, &sStatBuf) != 0 ||
    1067         126 :               !VSI_ISDIR(sStatBuf.st_mode)))
    1068             :     {
    1069           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1070             :                  "Attempt to create csv layer (file) against a "
    1071             :                  "non-directory datasource.");
    1072           0 :         return nullptr;
    1073             :     }
    1074             : 
    1075             :     const bool bCreateCSVT =
    1076         127 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "CREATE_CSVT", "NO"));
    1077             : 
    1078             :     // What filename would we use?
    1079         254 :     CPLString osFilename;
    1080             : 
    1081         127 :     if (strcmp(pszName, "/vsistdout/") == 0)
    1082             :     {
    1083           1 :         osFilename = pszName;
    1084           1 :         if (bCreateCSVT)
    1085             :         {
    1086           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1087             :                      "CREATE_CSVT is not compatible with /vsistdout/ output");
    1088           0 :             return nullptr;
    1089             :         }
    1090             :     }
    1091         126 :     else if (osDefaultCSVName != "")
    1092             :     {
    1093          62 :         osFilename = CPLFormFilenameSafe(pszName, osDefaultCSVName, nullptr);
    1094          62 :         osDefaultCSVName = "";
    1095             :     }
    1096             :     else
    1097             :     {
    1098          64 :         osFilename = CPLFormFilenameSafe(pszName, pszLayerName, "csv");
    1099             :     }
    1100             : 
    1101             :     // Does this directory/file already exist?
    1102         127 :     if (VSIStatL(osFilename, &sStatBuf) == 0)
    1103             :     {
    1104           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1105             :                  "Attempt to create layer %s, but %s already exists.",
    1106             :                  pszLayerName, osFilename.c_str());
    1107           1 :         return nullptr;
    1108             :     }
    1109             : 
    1110             :     // Create the empty file.
    1111             : 
    1112         126 :     const char *pszDelimiter = CSLFetchNameValue(papszOptions, "SEPARATOR");
    1113         126 :     char chDelimiter = ',';
    1114         126 :     if (pszDelimiter != nullptr)
    1115             :     {
    1116           2 :         if (EQUAL(pszDelimiter, "COMMA"))
    1117           0 :             chDelimiter = ',';
    1118           2 :         else if (EQUAL(pszDelimiter, "SEMICOLON"))
    1119           1 :             chDelimiter = ';';
    1120           1 :         else if (EQUAL(pszDelimiter, "TAB"))
    1121           0 :             chDelimiter = '\t';
    1122           1 :         else if (EQUAL(pszDelimiter, "SPACE"))
    1123           1 :             chDelimiter = ' ';
    1124           0 :         else if (EQUAL(pszDelimiter, "PIPE"))
    1125           0 :             chDelimiter = '|';
    1126             :         else
    1127             :         {
    1128           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1129             :                      "SEPARATOR=%s not understood, use one of COMMA, "
    1130             :                      "SEMICOLON, TAB, SPACE or PIPE.",
    1131             :                      pszDelimiter);
    1132             :         }
    1133             :     }
    1134             : 
    1135             :     // Create a layer.
    1136             : 
    1137             :     auto poCSVLayer = std::make_unique<OGRCSVLayer>(
    1138         252 :         this, pszLayerName, nullptr, -1, osFilename, true, true, chDelimiter);
    1139             : 
    1140         126 :     poCSVLayer->BuildFeatureDefn();
    1141             : 
    1142             :     // Was a particular CRLF order requested?
    1143         126 :     const char *pszCRLFFormat = CSLFetchNameValue(papszOptions, "LINEFORMAT");
    1144         126 :     bool bUseCRLF = false;
    1145             : 
    1146         126 :     if (pszCRLFFormat == nullptr)
    1147             :     {
    1148             : #ifdef _WIN32
    1149             :         bUseCRLF = true;
    1150             : #endif
    1151             :     }
    1152          18 :     else if (EQUAL(pszCRLFFormat, "CRLF"))
    1153             :     {
    1154           1 :         bUseCRLF = true;
    1155             :     }
    1156          17 :     else if (!EQUAL(pszCRLFFormat, "LF"))
    1157             :     {
    1158           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1159             :                  "LINEFORMAT=%s not understood, use one of CRLF or LF.",
    1160             :                  pszCRLFFormat);
    1161             : #ifdef _WIN32
    1162             :         bUseCRLF = true;
    1163             : #endif
    1164             :     }
    1165             : 
    1166         126 :     poCSVLayer->SetCRLF(bUseCRLF);
    1167             : 
    1168             :     const char *pszStringQuoting =
    1169         126 :         CSLFetchNameValueDef(papszOptions, "STRING_QUOTING", "IF_AMBIGUOUS");
    1170         252 :     poCSVLayer->SetStringQuoting(
    1171         126 :         EQUAL(pszStringQuoting, "IF_NEEDED")
    1172             :             ? OGRCSVLayer::StringQuoting::IF_NEEDED
    1173         113 :         : EQUAL(pszStringQuoting, "ALWAYS")
    1174         113 :             ? OGRCSVLayer::StringQuoting::ALWAYS
    1175             :             : OGRCSVLayer::StringQuoting::IF_AMBIGUOUS);
    1176             : 
    1177             :     // Should we write the geometry?
    1178         126 :     const char *pszGeometry = CSLFetchNameValue(papszOptions, "GEOMETRY");
    1179         126 :     if (bEnableGeometryFields)
    1180             :     {
    1181          11 :         poCSVLayer->SetWriteGeometry(
    1182             :             eGType, OGR_CSV_GEOM_AS_WKT,
    1183             :             CSLFetchNameValueDef(papszOptions, "GEOMETRY_NAME", "WKT"));
    1184             :     }
    1185         115 :     else if (pszGeometry != nullptr)
    1186             :     {
    1187          35 :         if (EQUAL(pszGeometry, "AS_WKT"))
    1188             :         {
    1189          28 :             poCSVLayer->SetWriteGeometry(
    1190             :                 eGType, OGR_CSV_GEOM_AS_WKT,
    1191             :                 CSLFetchNameValueDef(papszOptions, "GEOMETRY_NAME", "WKT"));
    1192             :         }
    1193           7 :         else if (EQUAL(pszGeometry, "AS_XYZ") || EQUAL(pszGeometry, "AS_XY") ||
    1194           2 :                  EQUAL(pszGeometry, "AS_YX"))
    1195             :         {
    1196           6 :             if (eGType == wkbUnknown || wkbFlatten(eGType) == wkbPoint)
    1197             :             {
    1198          10 :                 poCSVLayer->SetWriteGeometry(
    1199           5 :                     eGType, EQUAL(pszGeometry, "AS_XYZ")  ? OGR_CSV_GEOM_AS_XYZ
    1200           3 :                             : EQUAL(pszGeometry, "AS_XY") ? OGR_CSV_GEOM_AS_XY
    1201             :                                                           : OGR_CSV_GEOM_AS_YX);
    1202             :             }
    1203             :             else
    1204             :             {
    1205           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1206             :                          "Geometry type %s is not compatible with "
    1207             :                          "GEOMETRY=%s.",
    1208             :                          OGRGeometryTypeToName(eGType), pszGeometry);
    1209           1 :                 return nullptr;
    1210             :             }
    1211             :         }
    1212             :         else
    1213             :         {
    1214           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    1215             :                      "Unsupported value %s for creation option GEOMETRY",
    1216             :                      pszGeometry);
    1217           1 :             return nullptr;
    1218             :         }
    1219             :     }
    1220             : 
    1221             :     // Should we create a CSVT file?
    1222         124 :     if (bCreateCSVT)
    1223             :     {
    1224          29 :         poCSVLayer->SetCreateCSVT(true);
    1225             : 
    1226             :         // Create .prj file.
    1227          29 :         if (poSpatialRef != nullptr)
    1228             :         {
    1229           8 :             char *pszWKT = nullptr;
    1230           8 :             poSpatialRef->exportToWkt(&pszWKT);
    1231           8 :             if (pszWKT)
    1232             :             {
    1233           8 :                 VSILFILE *fpPRJ = VSIFOpenL(
    1234          16 :                     CPLResetExtensionSafe(osFilename, "prj").c_str(), "wb");
    1235           8 :                 if (fpPRJ)
    1236             :                 {
    1237           8 :                     CPL_IGNORE_RET_VAL(VSIFPrintfL(fpPRJ, "%s\n", pszWKT));
    1238           8 :                     VSIFCloseL(fpPRJ);
    1239             :                 }
    1240           8 :                 CPLFree(pszWKT);
    1241             :             }
    1242             :         }
    1243             :     }
    1244             : 
    1245             :     // Should we write a UTF8 BOM?
    1246         124 :     const char *pszWriteBOM = CSLFetchNameValue(papszOptions, "WRITE_BOM");
    1247         124 :     if (pszWriteBOM)
    1248           2 :         poCSVLayer->SetWriteBOM(CPLTestBool(pszWriteBOM));
    1249             : 
    1250         124 :     if (poCSVLayer->GetLayerDefn()->GetGeomFieldCount() > 0 &&
    1251             :         poSrcGeomFieldDefn)
    1252             :     {
    1253          39 :         auto poGeomFieldDefn = poCSVLayer->GetLayerDefn()->GetGeomFieldDefn(0);
    1254          39 :         poGeomFieldDefn->SetCoordinatePrecision(
    1255             :             poSrcGeomFieldDefn->GetCoordinatePrecision());
    1256             :     }
    1257             : 
    1258         124 :     poCSVLayer->SetWriteHeader(CPLTestBool(CSLFetchNameValueDef(
    1259             :         papszOptions, "HEADER",
    1260             :         CSLFetchNameValueDef(papszOptions, "HEADERS", "YES"))));
    1261             : 
    1262         124 :     if (osFilename != "/vsistdout/")
    1263         123 :         m_apoLayers.emplace_back(std::make_unique<OGRCSVEditableLayer>(
    1264         246 :             poCSVLayer.release(), nullptr));
    1265             :     else
    1266           1 :         m_apoLayers.emplace_back(std::move(poCSVLayer));
    1267             : 
    1268         124 :     return m_apoLayers.back()->GetLayer();
    1269             : }
    1270             : 
    1271             : /************************************************************************/
    1272             : /*                            DeleteLayer()                             */
    1273             : /************************************************************************/
    1274             : 
    1275          20 : OGRErr OGRCSVDataSource::DeleteLayer(int iLayer)
    1276             : 
    1277             : {
    1278             :     // Verify we are in update mode.
    1279          20 :     if (!bUpdate)
    1280             :     {
    1281           1 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    1282             :                  "Data source %s opened read-only.\n"
    1283             :                  "Layer %d cannot be deleted.",
    1284             :                  pszName, iLayer);
    1285             : 
    1286           1 :         return OGRERR_FAILURE;
    1287             :     }
    1288             : 
    1289          19 :     if (iLayer < 0 || iLayer >= GetLayerCount())
    1290             :     {
    1291           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    1292             :                  "Layer %d not in legal range of 0 to %d.", iLayer,
    1293           2 :                  GetLayerCount() - 1);
    1294           2 :         return OGRERR_FAILURE;
    1295             :     }
    1296             : 
    1297          34 :     for (const auto &osFilename : m_apoLayers[iLayer]->GetFileList())
    1298             :     {
    1299          17 :         VSIUnlink(osFilename.c_str());
    1300             :     }
    1301             : 
    1302          17 :     m_apoLayers.erase(m_apoLayers.begin() + iLayer);
    1303             : 
    1304          17 :     return OGRERR_NONE;
    1305             : }
    1306             : 
    1307             : /************************************************************************/
    1308             : /*                       CreateForSingleFile()                          */
    1309             : /************************************************************************/
    1310             : 
    1311          62 : void OGRCSVDataSource::CreateForSingleFile(const char *pszDirname,
    1312             :                                            const char *pszFilename)
    1313             : {
    1314          62 :     pszName = CPLStrdup(pszDirname);
    1315          62 :     bUpdate = true;
    1316          62 :     osDefaultCSVName = CPLGetFilename(pszFilename);
    1317          62 : }
    1318             : 
    1319             : /************************************************************************/
    1320             : /*                            GetFileList()                             */
    1321             : /************************************************************************/
    1322             : 
    1323           7 : char **OGRCSVDataSource::GetFileList()
    1324             : {
    1325          14 :     CPLStringList oFileList;
    1326          19 :     for (auto &poLayer : m_apoLayers)
    1327             :     {
    1328          35 :         for (const auto &osFilename : poLayer->GetFileList())
    1329             :         {
    1330          23 :             oFileList.AddString(osFilename.c_str());
    1331             :         }
    1332             :     }
    1333          14 :     return oFileList.StealList();
    1334             : }

Generated by: LCOV version 1.14