LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/csv - ogrcsvdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 496 626 79.2 %
Date: 2025-01-18 12:42:00 Functions: 25 25 100.0 %

          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;
      44             :     char **m_papszOpenOptions;
      45             : 
      46             :   public:
      47         208 :     OGRCSVEditableLayerSynchronizer(OGRCSVLayer *poCSVLayer,
      48             :                                     CSLConstList papszOpenOptions)
      49         208 :         : m_poCSVLayer(poCSVLayer),
      50         208 :           m_papszOpenOptions(CSLDuplicate(papszOpenOptions))
      51             :     {
      52         208 :     }
      53             : 
      54             :     virtual ~OGRCSVEditableLayerSynchronizer() override;
      55             : 
      56             :     virtual OGRErr EditableSyncToDisk(OGRLayer *poEditableLayer,
      57             :                                       OGRLayer **ppoDecoratedLayer) override;
      58             : 
      59          18 :     std::vector<std::string> GetFileList()
      60             :     {
      61          18 :         return m_poCSVLayer->GetFileList();
      62             :     }
      63             : };
      64             : 
      65             : /************************************************************************/
      66             : /*                     ~OGRCSVEditableLayerSynchronizer()               */
      67             : /************************************************************************/
      68             : 
      69         416 : OGRCSVEditableLayerSynchronizer::~OGRCSVEditableLayerSynchronizer()
      70             : {
      71         208 :     CSLDestroy(m_papszOpenOptions);
      72         416 : }
      73             : 
      74             : /************************************************************************/
      75             : /*                       EditableSyncToDisk()                           */
      76             : /************************************************************************/
      77             : 
      78           9 : OGRErr OGRCSVEditableLayerSynchronizer::EditableSyncToDisk(
      79             :     OGRLayer *poEditableLayer, OGRLayer **ppoDecoratedLayer)
      80             : {
      81           9 :     CPLAssert(m_poCSVLayer == *ppoDecoratedLayer);
      82             : 
      83           9 :     GDALDataset *poDS = m_poCSVLayer->GetDataset();
      84          18 :     const CPLString osLayerName(m_poCSVLayer->GetName());
      85          18 :     const CPLString osFilename(m_poCSVLayer->GetFilename());
      86           9 :     const bool bCreateCSVT = m_poCSVLayer->GetCreateCSVT();
      87          18 :     const CPLString osCSVTFilename(CPLResetExtensionSafe(osFilename, "csvt"));
      88             :     VSIStatBufL sStatBuf;
      89           9 :     const bool bHasCSVT = VSIStatL(osCSVTFilename, &sStatBuf) == 0;
      90          18 :     CPLString osTmpFilename(osFilename);
      91          18 :     CPLString osTmpCSVTFilename(osFilename);
      92           9 :     if (VSIStatL(osFilename, &sStatBuf) == 0)
      93             :     {
      94           9 :         osTmpFilename += "_ogr_tmp.csv";
      95           9 :         osTmpCSVTFilename += "_ogr_tmp.csvt";
      96             :     }
      97           9 :     const char chDelimiter = m_poCSVLayer->GetDelimiter();
      98             :     OGRCSVLayer *poCSVTmpLayer = new OGRCSVLayer(
      99           9 :         poDS, osLayerName, nullptr, -1, osTmpFilename, true, true, chDelimiter);
     100           9 :     poCSVTmpLayer->BuildFeatureDefn(nullptr, nullptr, m_papszOpenOptions);
     101           9 :     poCSVTmpLayer->SetCRLF(m_poCSVLayer->GetCRLF());
     102           9 :     poCSVTmpLayer->SetCreateCSVT(bCreateCSVT || bHasCSVT);
     103           9 :     poCSVTmpLayer->SetWriteBOM(m_poCSVLayer->GetWriteBOM());
     104           9 :     poCSVTmpLayer->SetStringQuoting(m_poCSVLayer->GetStringQuoting());
     105             : 
     106           9 :     if (m_poCSVLayer->GetGeometryFormat() == OGR_CSV_GEOM_AS_WKT)
     107           5 :         poCSVTmpLayer->SetWriteGeometry(wkbNone, OGR_CSV_GEOM_AS_WKT, nullptr);
     108             : 
     109             :     const bool bKeepGeomColmuns =
     110           9 :         CPLFetchBool(m_papszOpenOptions, "KEEP_GEOM_COLUMNS", true);
     111             : 
     112           9 :     OGRErr eErr = OGRERR_NONE;
     113           9 :     OGRFeatureDefn *poEditableFDefn = poEditableLayer->GetLayerDefn();
     114          31 :     for (int i = 0; eErr == OGRERR_NONE && i < poEditableFDefn->GetFieldCount();
     115             :          i++)
     116             :     {
     117          44 :         OGRFieldDefn oFieldDefn(poEditableFDefn->GetFieldDefn(i));
     118          22 :         int iGeomFieldIdx = 0;
     119          70 :         if ((EQUAL(oFieldDefn.GetNameRef(), "WKT") &&
     120          38 :              (iGeomFieldIdx = poEditableFDefn->GetGeomFieldIndex("")) >= 0) ||
     121          32 :             (bKeepGeomColmuns &&
     122          16 :              (iGeomFieldIdx = poEditableFDefn->GetGeomFieldIndex(
     123          38 :                   (std::string("geom_") + oFieldDefn.GetNameRef()).c_str())) >=
     124             :                  0))
     125             :         {
     126             :             OGRGeomFieldDefn oGeomFieldDefn(
     127           5 :                 poEditableFDefn->GetGeomFieldDefn(iGeomFieldIdx));
     128           5 :             eErr = poCSVTmpLayer->CreateGeomField(&oGeomFieldDefn);
     129             :         }
     130             :         else
     131             :         {
     132          17 :             eErr = poCSVTmpLayer->CreateField(&oFieldDefn);
     133             :         }
     134             :     }
     135             : 
     136          12 :     const bool bHasXY = !m_poCSVLayer->GetXField().empty() &&
     137           3 :                         !m_poCSVLayer->GetYField().empty();
     138           9 :     const bool bHasZ = !m_poCSVLayer->GetZField().empty();
     139           9 :     if (bHasXY && !bKeepGeomColmuns)
     140             :     {
     141           4 :         if (poCSVTmpLayer->GetLayerDefn()->GetFieldIndex(
     142           4 :                 m_poCSVLayer->GetXField()) < 0)
     143             :         {
     144           4 :             OGRFieldDefn oFieldDefn(m_poCSVLayer->GetXField(), OFTReal);
     145           2 :             if (eErr == OGRERR_NONE)
     146           2 :                 eErr = poCSVTmpLayer->CreateField(&oFieldDefn);
     147             :         }
     148           4 :         if (poCSVTmpLayer->GetLayerDefn()->GetFieldIndex(
     149           4 :                 m_poCSVLayer->GetYField()) < 0)
     150             :         {
     151           4 :             OGRFieldDefn oFieldDefn(m_poCSVLayer->GetYField(), OFTReal);
     152           2 :             if (eErr == OGRERR_NONE)
     153           2 :                 eErr = poCSVTmpLayer->CreateField(&oFieldDefn);
     154             :         }
     155           3 :         if (bHasZ && poCSVTmpLayer->GetLayerDefn()->GetFieldIndex(
     156           1 :                          m_poCSVLayer->GetZField()) < 0)
     157             :         {
     158           2 :             OGRFieldDefn oFieldDefn(m_poCSVLayer->GetZField(), OFTReal);
     159           1 :             if (eErr == OGRERR_NONE)
     160           1 :                 eErr = poCSVTmpLayer->CreateField(&oFieldDefn);
     161             :         }
     162             :     }
     163             : 
     164           9 :     int nFirstGeomColIdx = 0;
     165           9 :     if (m_poCSVLayer->HasHiddenWKTColumn())
     166             :     {
     167           2 :         poCSVTmpLayer->SetWriteGeometry(
     168           1 :             poEditableFDefn->GetGeomFieldDefn(0)->GetType(),
     169             :             OGR_CSV_GEOM_AS_WKT,
     170           1 :             poEditableFDefn->GetGeomFieldDefn(0)->GetNameRef());
     171           1 :         nFirstGeomColIdx = 1;
     172             :     }
     173             : 
     174           9 :     if (!(poEditableFDefn->GetGeomFieldCount() == 1 && bHasXY))
     175             :     {
     176          12 :         for (int i = nFirstGeomColIdx;
     177          12 :              eErr == OGRERR_NONE && i < poEditableFDefn->GetGeomFieldCount();
     178             :              i++)
     179             :         {
     180             :             OGRGeomFieldDefn oGeomFieldDefn(
     181           6 :                 poEditableFDefn->GetGeomFieldDefn(i));
     182          12 :             if (poCSVTmpLayer->GetLayerDefn()->GetGeomFieldIndex(
     183          12 :                     oGeomFieldDefn.GetNameRef()) >= 0)
     184           5 :                 continue;
     185           1 :             eErr = poCSVTmpLayer->CreateGeomField(&oGeomFieldDefn);
     186             :         }
     187             :     }
     188             : 
     189           9 :     poEditableLayer->ResetReading();
     190             : 
     191             :     // Disable all filters.
     192           9 :     const char *pszQueryStringConst = poEditableLayer->GetAttrQueryString();
     193             :     char *pszQueryStringBak =
     194           9 :         pszQueryStringConst ? CPLStrdup(pszQueryStringConst) : nullptr;
     195           9 :     poEditableLayer->SetAttributeFilter(nullptr);
     196             : 
     197           9 :     const int iFilterGeomIndexBak = poEditableLayer->GetGeomFieldFilter();
     198           9 :     OGRGeometry *poFilterGeomBak = poEditableLayer->GetSpatialFilter();
     199           9 :     if (poFilterGeomBak)
     200           1 :         poFilterGeomBak = poFilterGeomBak->clone();
     201           9 :     poEditableLayer->SetSpatialFilter(nullptr);
     202             : 
     203             :     auto aoMapSrcToTargetIdx =
     204             :         poCSVTmpLayer->GetLayerDefn()->ComputeMapForSetFrom(
     205          18 :             poEditableLayer->GetLayerDefn(), true);
     206           9 :     aoMapSrcToTargetIdx.push_back(
     207           9 :         -1);  // add dummy entry to be sure that .data() is valid
     208             : 
     209          21 :     for (auto &&poFeature : poEditableLayer)
     210             :     {
     211          12 :         if (eErr != OGRERR_NONE)
     212           0 :             break;
     213             :         OGRFeature *poNewFeature =
     214          12 :             new OGRFeature(poCSVTmpLayer->GetLayerDefn());
     215          12 :         poNewFeature->SetFrom(poFeature.get(), aoMapSrcToTargetIdx.data(),
     216             :                               true);
     217          12 :         if (bHasXY)
     218             :         {
     219           3 :             OGRGeometry *poGeom = poFeature->GetGeometryRef();
     220           6 :             if (poGeom != nullptr &&
     221           3 :                 wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
     222             :             {
     223           3 :                 auto poPoint = poGeom->toPoint();
     224           3 :                 poNewFeature->SetField(m_poCSVLayer->GetXField(),
     225             :                                        poPoint->getX());
     226           3 :                 poNewFeature->SetField(m_poCSVLayer->GetYField(),
     227             :                                        poPoint->getY());
     228           3 :                 if (bHasZ)
     229             :                 {
     230           1 :                     poNewFeature->SetField(m_poCSVLayer->GetZField(),
     231             :                                            poPoint->getZ());
     232             :                 }
     233             :             }
     234             :         }
     235          12 :         eErr = poCSVTmpLayer->CreateFeature(poNewFeature);
     236          12 :         delete poNewFeature;
     237             :     }
     238           9 :     delete poCSVTmpLayer;
     239             : 
     240             :     // Restore filters.
     241           9 :     poEditableLayer->SetAttributeFilter(pszQueryStringBak);
     242           9 :     CPLFree(pszQueryStringBak);
     243           9 :     poEditableLayer->SetSpatialFilter(iFilterGeomIndexBak, poFilterGeomBak);
     244           9 :     delete poFilterGeomBak;
     245             : 
     246           9 :     if (eErr != OGRERR_NONE)
     247             :     {
     248           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Error while creating %s",
     249             :                  osTmpFilename.c_str());
     250           0 :         VSIUnlink(osTmpFilename);
     251           0 :         VSIUnlink(CPLResetExtensionSafe(osTmpFilename, "csvt").c_str());
     252           0 :         return eErr;
     253             :     }
     254             : 
     255           9 :     delete m_poCSVLayer;
     256             : 
     257           9 :     if (osFilename != osTmpFilename)
     258             :     {
     259           9 :         const CPLString osTmpOriFilename(osFilename + ".ogr_bak");
     260           9 :         const CPLString osTmpOriCSVTFilename(osCSVTFilename + ".ogr_bak");
     261          18 :         if (VSIRename(osFilename, osTmpOriFilename) != 0 ||
     262           4 :             (bHasCSVT &&
     263           4 :              VSIRename(osCSVTFilename, osTmpOriCSVTFilename) != 0) ||
     264          22 :             VSIRename(osTmpFilename, osFilename) != 0 ||
     265           4 :             (bHasCSVT && VSIRename(osTmpCSVTFilename, osCSVTFilename) != 0))
     266             :         {
     267           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot rename files");
     268           0 :             *ppoDecoratedLayer = nullptr;
     269           0 :             m_poCSVLayer = nullptr;
     270           0 :             return OGRERR_FAILURE;
     271             :         }
     272           9 :         VSIUnlink(osTmpOriFilename);
     273           9 :         if (bHasCSVT)
     274           4 :             VSIUnlink(osTmpOriCSVTFilename);
     275             :     }
     276             : 
     277           9 :     VSILFILE *fp = VSIFOpenL(osFilename, "rb+");
     278           9 :     if (fp == nullptr)
     279             :     {
     280           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot reopen updated %s",
     281             :                  osFilename.c_str());
     282           0 :         *ppoDecoratedLayer = nullptr;
     283           0 :         m_poCSVLayer = nullptr;
     284           0 :         return OGRERR_FAILURE;
     285             :     }
     286             : 
     287           9 :     m_poCSVLayer =
     288             :         new OGRCSVLayer(poDS, osLayerName, fp, -1, osFilename, false, /* new */
     289             :                         true, /* update */
     290           9 :                         chDelimiter);
     291           9 :     m_poCSVLayer->BuildFeatureDefn(nullptr, nullptr, m_papszOpenOptions);
     292           9 :     *ppoDecoratedLayer = m_poCSVLayer;
     293             : 
     294           9 :     return OGRERR_NONE;
     295             : }
     296             : 
     297             : /************************************************************************/
     298             : /*                        OGRCSVEditableLayer                           */
     299             : /************************************************************************/
     300             : 
     301             : class OGRCSVEditableLayer final : public IOGRCSVLayer, public OGREditableLayer
     302             : {
     303             :     std::set<CPLString> m_oSetFields;
     304             : 
     305             :   public:
     306             :     OGRCSVEditableLayer(OGRCSVLayer *poCSVLayer, CSLConstList papszOpenOptions);
     307             : 
     308         326 :     OGRLayer *GetLayer() override
     309             :     {
     310         326 :         return this;
     311             :     }
     312             : 
     313          18 :     std::vector<std::string> GetFileList() override
     314             :     {
     315             :         return cpl::down_cast<OGRCSVEditableLayerSynchronizer *>(
     316             :                    m_poSynchronizer)
     317          18 :             ->GetFileList();
     318             :     }
     319             : 
     320             :     virtual OGRErr CreateField(const OGRFieldDefn *poField,
     321             :                                int bApproxOK = TRUE) override;
     322             :     virtual OGRErr DeleteField(int iField) override;
     323             :     virtual OGRErr AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
     324             :                                   int nFlagsIn) override;
     325             :     virtual GIntBig GetFeatureCount(int bForce = TRUE) override;
     326             : };
     327             : 
     328             : /************************************************************************/
     329             : /*                       GRCSVEditableLayer()                           */
     330             : /************************************************************************/
     331             : 
     332         208 : OGRCSVEditableLayer::OGRCSVEditableLayer(OGRCSVLayer *poCSVLayer,
     333         208 :                                          CSLConstList papszOpenOptions)
     334             :     : OGREditableLayer(
     335             :           poCSVLayer, true,
     336         208 :           new OGRCSVEditableLayerSynchronizer(poCSVLayer, papszOpenOptions),
     337         416 :           true)
     338             : {
     339         208 :     SetSupportsCreateGeomField(true);
     340         208 :     SetSupportsCurveGeometries(true);
     341         208 : }
     342             : 
     343             : /************************************************************************/
     344             : /*                            CreateField()                             */
     345             : /************************************************************************/
     346             : 
     347         399 : OGRErr OGRCSVEditableLayer::CreateField(const OGRFieldDefn *poNewField,
     348             :                                         int bApproxOK)
     349             : 
     350             : {
     351         399 :     if (m_poEditableFeatureDefn->GetFieldCount() >= 10000)
     352             :     {
     353           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Limiting to 10000 fields");
     354           0 :         return OGRERR_FAILURE;
     355             :     }
     356             : 
     357         399 :     if (m_oSetFields.empty())
     358             :     {
     359         101 :         for (int i = 0; i < m_poEditableFeatureDefn->GetFieldCount(); i++)
     360             :         {
     361             :             m_oSetFields.insert(
     362           8 :                 CPLString(
     363           4 :                     m_poEditableFeatureDefn->GetFieldDefn(i)->GetNameRef())
     364           8 :                     .toupper());
     365             :         }
     366             :     }
     367             : 
     368         798 :     const OGRCSVCreateFieldAction eAction = OGRCSVLayer::PreCreateField(
     369         399 :         m_poEditableFeatureDefn, m_oSetFields, poNewField, bApproxOK);
     370         399 :     if (eAction == CREATE_FIELD_DO_NOTHING)
     371           0 :         return OGRERR_NONE;
     372         399 :     if (eAction == CREATE_FIELD_ERROR)
     373           1 :         return OGRERR_FAILURE;
     374         398 :     OGRErr eErr = OGREditableLayer::CreateField(poNewField, bApproxOK);
     375         398 :     if (eErr == OGRERR_NONE)
     376             :     {
     377         398 :         m_oSetFields.insert(CPLString(poNewField->GetNameRef()).toupper());
     378             :     }
     379         398 :     return eErr;
     380             : }
     381             : 
     382           3 : OGRErr OGRCSVEditableLayer::DeleteField(int iField)
     383             : {
     384           3 :     m_oSetFields.clear();
     385           3 :     return OGREditableLayer::DeleteField(iField);
     386             : }
     387             : 
     388           2 : OGRErr OGRCSVEditableLayer::AlterFieldDefn(int iField,
     389             :                                            OGRFieldDefn *poNewFieldDefn,
     390             :                                            int nFlagsIn)
     391             : {
     392           2 :     m_oSetFields.clear();
     393           2 :     return OGREditableLayer::AlterFieldDefn(iField, poNewFieldDefn, nFlagsIn);
     394             : }
     395             : 
     396             : /************************************************************************/
     397             : /*                        GetFeatureCount()                             */
     398             : /************************************************************************/
     399             : 
     400          28 : GIntBig OGRCSVEditableLayer::GetFeatureCount(int bForce)
     401             : {
     402          28 :     const GIntBig nRet = OGREditableLayer::GetFeatureCount(bForce);
     403          28 :     if (m_poDecoratedLayer != nullptr && m_nNextFID <= 0)
     404             :     {
     405             :         const GIntBig nTotalFeatureCount =
     406           8 :             static_cast<OGRCSVLayer *>(m_poDecoratedLayer)
     407           4 :                 ->GetTotalFeatureCount();
     408           4 :         if (nTotalFeatureCount >= 0)
     409           3 :             SetNextFID(nTotalFeatureCount + 1);
     410             :     }
     411          28 :     return nRet;
     412             : }
     413             : 
     414             : /************************************************************************/
     415             : /*                          OGRCSVDataSource()                          */
     416             : /************************************************************************/
     417             : 
     418             : OGRCSVDataSource::OGRCSVDataSource() = default;
     419             : 
     420             : /************************************************************************/
     421             : /*                         ~OGRCSVDataSource()                          */
     422             : /************************************************************************/
     423             : 
     424        2422 : OGRCSVDataSource::~OGRCSVDataSource()
     425             : 
     426             : {
     427        1211 :     m_apoLayers.clear();
     428             : 
     429        1211 :     if (bUpdate)
     430         249 :         OGRCSVDriverRemoveFromMap(pszName, this);
     431             : 
     432        1211 :     CPLFree(pszName);
     433        2422 : }
     434             : 
     435             : /************************************************************************/
     436             : /*                           TestCapability()                           */
     437             : /************************************************************************/
     438             : 
     439         200 : int OGRCSVDataSource::TestCapability(const char *pszCap)
     440             : 
     441             : {
     442         200 :     if (EQUAL(pszCap, ODsCCreateLayer))
     443          65 :         return bUpdate;
     444         135 :     else if (EQUAL(pszCap, ODsCDeleteLayer))
     445          19 :         return bUpdate;
     446         116 :     else if (EQUAL(pszCap, ODsCCreateGeomFieldAfterCreateLayer))
     447          30 :         return bUpdate && bEnableGeometryFields;
     448          86 :     else if (EQUAL(pszCap, ODsCCurveGeometries))
     449           3 :         return TRUE;
     450          83 :     else if (EQUAL(pszCap, ODsCMeasuredGeometries))
     451           3 :         return TRUE;
     452          80 :     else if (EQUAL(pszCap, ODsCZGeometries))
     453           2 :         return TRUE;
     454          78 :     else if (EQUAL(pszCap, ODsCRandomLayerWrite))
     455           0 :         return bUpdate;
     456             :     else
     457          78 :         return FALSE;
     458             : }
     459             : 
     460             : /************************************************************************/
     461             : /*                              GetLayer()                              */
     462             : /************************************************************************/
     463             : 
     464         892 : OGRLayer *OGRCSVDataSource::GetLayer(int iLayer)
     465             : 
     466             : {
     467         892 :     if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
     468           2 :         return nullptr;
     469             : 
     470         890 :     return m_apoLayers[iLayer]->GetLayer();
     471             : }
     472             : 
     473             : /************************************************************************/
     474             : /*                          GetRealExtension()                          */
     475             : /************************************************************************/
     476             : 
     477        7233 : CPLString OGRCSVDataSource::GetRealExtension(CPLString osFilename)
     478             : {
     479       14466 :     const CPLString osExt = CPLGetExtensionSafe(osFilename);
     480        7233 :     if (STARTS_WITH(osFilename, "/vsigzip/") && EQUAL(osExt, "gz"))
     481             :     {
     482          14 :         if (osFilename.size() > 7 &&
     483           7 :             EQUAL(osFilename + osFilename.size() - 7, ".csv.gz"))
     484           0 :             return "csv";
     485          14 :         else if (osFilename.size() > 7 &&
     486           7 :                  EQUAL(osFilename + osFilename.size() - 7, ".tsv.gz"))
     487           0 :             return "tsv";
     488          14 :         else if (osFilename.size() > 7 &&
     489           7 :                  EQUAL(osFilename + osFilename.size() - 7, ".psv.gz"))
     490           0 :             return "psv";
     491             :     }
     492        7233 :     return osExt;
     493             : }
     494             : 
     495             : /************************************************************************/
     496             : /*                                Open()                                */
     497             : /************************************************************************/
     498             : 
     499        1153 : bool OGRCSVDataSource::Open(const char *pszFilename, bool bUpdateIn,
     500             :                             bool bForceOpen, CSLConstList papszOpenOptionsIn,
     501             :                             bool bSingleDriver)
     502             : 
     503             : {
     504        1153 :     pszName = CPLStrdup(pszFilename);
     505        1153 :     bUpdate = CPL_TO_BOOL(bUpdateIn);
     506             : 
     507        1153 :     if (bUpdate && bForceOpen && EQUAL(pszFilename, "/vsistdout/"))
     508           1 :         return TRUE;
     509             : 
     510             :     // For writable /vsizip/, do nothing more.
     511        1152 :     if (bUpdate && bForceOpen && STARTS_WITH(pszFilename, "/vsizip/"))
     512           0 :         return TRUE;
     513             : 
     514        2304 :     CPLString osFilename(pszFilename);
     515        2304 :     const CPLString osBaseFilename = CPLGetFilename(pszFilename);
     516        2304 :     const CPLString osExt = GetRealExtension(osFilename);
     517             : 
     518        1152 :     bool bIgnoreExtension = bSingleDriver;
     519        1152 :     bool bUSGeonamesFile = false;
     520        1152 :     if (STARTS_WITH_CI(osFilename, "CSV:"))
     521             :     {
     522           0 :         bIgnoreExtension = true;
     523           0 :         osFilename = osFilename.substr(strlen("CSV:"));
     524             :     }
     525             : 
     526             :     // Those are *not* real .XLS files, but text file with tab as column
     527             :     // separator.
     528        1152 :     if (EQUAL(osBaseFilename, "NfdcFacilities.xls") ||
     529        1152 :         EQUAL(osBaseFilename, "NfdcRunways.xls") ||
     530        3456 :         EQUAL(osBaseFilename, "NfdcRemarks.xls") ||
     531        1152 :         EQUAL(osBaseFilename, "NfdcSchedules.xls"))
     532             :     {
     533           0 :         if (bUpdate)
     534           0 :             return FALSE;
     535           0 :         bIgnoreExtension = true;
     536             :     }
     537        1152 :     else if ((STARTS_WITH_CI(osBaseFilename, "NationalFile_") ||
     538        1152 :               STARTS_WITH_CI(osBaseFilename, "POP_PLACES_") ||
     539        1152 :               STARTS_WITH_CI(osBaseFilename, "HIST_FEATURES_") ||
     540        1152 :               STARTS_WITH_CI(osBaseFilename, "US_CONCISE_") ||
     541        1152 :               STARTS_WITH_CI(osBaseFilename, "AllNames_") ||
     542        1152 :               STARTS_WITH_CI(osBaseFilename, "Feature_Description_History_") ||
     543        1152 :               STARTS_WITH_CI(osBaseFilename, "ANTARCTICA_") ||
     544        1152 :               STARTS_WITH_CI(osBaseFilename, "GOVT_UNITS_") ||
     545        1152 :               STARTS_WITH_CI(osBaseFilename, "NationalFedCodes_") ||
     546        1152 :               STARTS_WITH_CI(osBaseFilename, "AllStates_") ||
     547        2304 :               STARTS_WITH_CI(osBaseFilename, "AllStatesFedCodes_") ||
     548        1152 :               (osBaseFilename.size() > 2 &&
     549        2288 :                STARTS_WITH_CI(osBaseFilename + 2, "_Features_")) ||
     550        1152 :               (osBaseFilename.size() > 2 &&
     551        2304 :                STARTS_WITH_CI(osBaseFilename + 2, "_FedCodes_"))) &&
     552           0 :              (EQUAL(osExt, "txt") || EQUAL(osExt, "zip")))
     553             :     {
     554           0 :         if (bUpdate)
     555           0 :             return FALSE;
     556           0 :         bIgnoreExtension = true;
     557           0 :         bUSGeonamesFile = true;
     558             : 
     559           0 :         if (EQUAL(osExt, "zip") && strstr(osFilename, "/vsizip/") == nullptr)
     560             :         {
     561           0 :             osFilename = "/vsizip/" + osFilename;
     562             :         }
     563             :     }
     564        2303 :     else if (EQUAL(osBaseFilename, "allCountries.txt") ||
     565        1151 :              EQUAL(osBaseFilename, "allCountries.zip"))
     566             :     {
     567           1 :         if (bUpdate)
     568           0 :             return FALSE;
     569           1 :         bIgnoreExtension = true;
     570             : 
     571           1 :         if (EQUAL(osExt, "zip") && strstr(osFilename, "/vsizip/") == nullptr)
     572             :         {
     573           0 :             osFilename = "/vsizip/" + osFilename;
     574             :         }
     575             :     }
     576             : 
     577             :     // Determine what sort of object this is.
     578             :     VSIStatBufL sStatBuf;
     579             : 
     580        1152 :     if (VSIStatExL(osFilename, &sStatBuf, VSI_STAT_NATURE_FLAG) != 0)
     581           0 :         return FALSE;
     582             : 
     583             :     // Is this a single CSV file?
     584        1524 :     if (VSI_ISREG(sStatBuf.st_mode) &&
     585         372 :         (bIgnoreExtension || EQUAL(osExt, "csv") || EQUAL(osExt, "tsv") ||
     586           2 :          EQUAL(osExt, "psv")))
     587             :     {
     588         463 :         if (EQUAL(CPLGetFilename(osFilename), "NfdcFacilities.xls"))
     589             :         {
     590           0 :             return OpenTable(osFilename, papszOpenOptionsIn, "ARP");
     591             :         }
     592         463 :         else if (EQUAL(CPLGetFilename(osFilename), "NfdcRunways.xls"))
     593             :         {
     594           0 :             OpenTable(osFilename, papszOpenOptionsIn, "BaseEndPhysical");
     595           0 :             OpenTable(osFilename, papszOpenOptionsIn, "BaseEndDisplaced");
     596           0 :             OpenTable(osFilename, papszOpenOptionsIn, "ReciprocalEndPhysical");
     597           0 :             OpenTable(osFilename, papszOpenOptionsIn, "ReciprocalEndDisplaced");
     598           0 :             return !m_apoLayers.empty();
     599             :         }
     600         463 :         else if (bUSGeonamesFile)
     601             :         {
     602             :             // GNIS specific.
     603           0 :             if (STARTS_WITH_CI(osBaseFilename, "NationalFedCodes_") ||
     604           0 :                 STARTS_WITH_CI(osBaseFilename, "AllStatesFedCodes_") ||
     605           0 :                 STARTS_WITH_CI(osBaseFilename, "ANTARCTICA_") ||
     606           0 :                 (osBaseFilename.size() > 2 &&
     607           0 :                  STARTS_WITH_CI(osBaseFilename + 2, "_FedCodes_")))
     608             :             {
     609           0 :                 OpenTable(osFilename, papszOpenOptionsIn, nullptr, "PRIMARY");
     610             :             }
     611           0 :             else if (STARTS_WITH_CI(osBaseFilename, "GOVT_UNITS_") ||
     612           0 :                      STARTS_WITH_CI(osBaseFilename,
     613             :                                     "Feature_Description_History_"))
     614             :             {
     615           0 :                 OpenTable(osFilename, papszOpenOptionsIn, nullptr, "");
     616             :             }
     617             :             else
     618             :             {
     619           0 :                 OpenTable(osFilename, papszOpenOptionsIn, nullptr, "PRIM");
     620           0 :                 OpenTable(osFilename, papszOpenOptionsIn, nullptr, "SOURCE");
     621             :             }
     622           0 :             return !m_apoLayers.empty();
     623             :         }
     624             : 
     625         463 :         return OpenTable(osFilename, papszOpenOptionsIn);
     626             :     }
     627             : 
     628             :     // Is this a single a ZIP file with only a CSV file inside?
     629         693 :     if (STARTS_WITH(osFilename, "/vsizip/") && EQUAL(osExt, "zip") &&
     630           4 :         VSI_ISREG(sStatBuf.st_mode))
     631             :     {
     632           1 :         char **papszFiles = VSIReadDir(osFilename);
     633           2 :         if (CSLCount(papszFiles) != 1 ||
     634           2 :             !EQUAL(CPLGetExtensionSafe(papszFiles[0]).c_str(), "CSV"))
     635             :         {
     636           1 :             CSLDestroy(papszFiles);
     637           1 :             return FALSE;
     638             :         }
     639           0 :         osFilename = CPLFormFilenameSafe(osFilename, papszFiles[0], nullptr);
     640           0 :         CSLDestroy(papszFiles);
     641           0 :         return OpenTable(osFilename, papszOpenOptionsIn);
     642             :     }
     643             : 
     644             :     // Otherwise it has to be a directory.
     645         688 :     if (!VSI_ISDIR(sStatBuf.st_mode))
     646           0 :         return FALSE;
     647             : 
     648             :     // Scan through for entries ending in .csv.
     649         688 :     int nNotCSVCount = 0;
     650         688 :     char **papszNames = VSIReadDir(osFilename);
     651             : 
     652       28008 :     for (int i = 0; papszNames != nullptr && papszNames[i] != nullptr; i++)
     653             :     {
     654             :         const CPLString oSubFilename =
     655       27320 :             CPLFormFilenameSafe(osFilename, papszNames[i], nullptr);
     656             : 
     657       27320 :         if (EQUAL(papszNames[i], ".") || EQUAL(papszNames[i], ".."))
     658         662 :             continue;
     659             : 
     660       26658 :         if (EQUAL(CPLGetExtensionSafe(oSubFilename).c_str(), "csvt"))
     661          79 :             continue;
     662             : 
     663       53158 :         if (VSIStatL(oSubFilename, &sStatBuf) != 0 ||
     664       26579 :             !VSI_ISREG(sStatBuf.st_mode))
     665             :         {
     666         110 :             nNotCSVCount++;
     667         110 :             continue;
     668             :         }
     669             : 
     670       26469 :         if (EQUAL(CPLGetExtensionSafe(oSubFilename).c_str(), "csv"))
     671             :         {
     672         139 :             if (!OpenTable(oSubFilename, papszOpenOptionsIn))
     673             :             {
     674           0 :                 CPLDebug("CSV", "Cannot open %s", oSubFilename.c_str());
     675           0 :                 nNotCSVCount++;
     676           0 :                 continue;
     677             :             }
     678             :         }
     679             :         // GNIS specific.
     680       78990 :         else if (strlen(papszNames[i]) > 2 &&
     681       26330 :                  STARTS_WITH_CI(papszNames[i] + 2, "_Features_") &&
     682       26330 :                  EQUAL(CPLGetExtensionSafe(papszNames[i]).c_str(), "txt"))
     683             :         {
     684             :             bool bRet =
     685           0 :                 OpenTable(oSubFilename, papszOpenOptionsIn, nullptr, "PRIM");
     686           0 :             bRet |=
     687           0 :                 OpenTable(oSubFilename, papszOpenOptionsIn, nullptr, "SOURCE");
     688           0 :             if (!bRet)
     689             :             {
     690           0 :                 CPLDebug("CSV", "Cannot open %s", oSubFilename.c_str());
     691           0 :                 nNotCSVCount++;
     692           0 :                 continue;
     693             :             }
     694             :         }
     695             :         // GNIS specific.
     696      105320 :         else if (strlen(papszNames[i]) > 2 &&
     697       26330 :                  STARTS_WITH_CI(papszNames[i] + 2, "_FedCodes_") &&
     698       26330 :                  EQUAL(CPLGetExtensionSafe(papszNames[i]).c_str(), "txt"))
     699             :         {
     700           0 :             if (!OpenTable(oSubFilename, papszOpenOptionsIn, nullptr,
     701             :                            "PRIMARY"))
     702             :             {
     703           0 :                 CPLDebug("CSV", "Cannot open %s", oSubFilename.c_str());
     704           0 :                 nNotCSVCount++;
     705           0 :                 continue;
     706             :             }
     707             :         }
     708             :         else
     709             :         {
     710       26330 :             nNotCSVCount++;
     711       26330 :             continue;
     712             :         }
     713             :     }
     714             : 
     715         688 :     CSLDestroy(papszNames);
     716             : 
     717             :     // We presume that this is indeed intended to be a CSV
     718             :     // datasource if over half the files were .csv files.
     719         688 :     return bForceOpen || nNotCSVCount < GetLayerCount();
     720             : }
     721             : 
     722      210240 : const std::vector<int> &OGRCSVDataSource::DeletedFieldIndexes() const
     723             : {
     724      210240 :     return m_oDeletedFieldIndexes;
     725             : }
     726             : 
     727             : /************************************************************************/
     728             : /*                      DealWithOgrSchemaOpenOption()                   */
     729             : /************************************************************************/
     730         514 : bool OGRCSVDataSource::DealWithOgrSchemaOpenOption(
     731             :     CSLConstList papszOpenOptionsIn)
     732             : {
     733             :     std::string osFieldsSchemaOverrideParam =
     734        1028 :         CSLFetchNameValueDef(papszOpenOptionsIn, "OGR_SCHEMA", "");
     735             : 
     736         514 :     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         513 :     return true;
     841             : }
     842             : 
     843             : /************************************************************************/
     844             : /*                              OpenTable()                             */
     845             : /************************************************************************/
     846             : 
     847         602 : bool OGRCSVDataSource::OpenTable(const char *pszFilename,
     848             :                                  CSLConstList papszOpenOptionsIn,
     849             :                                  const char *pszNfdcRunwaysGeomField,
     850             :                                  const char *pszGeonamesGeomFieldPrefix)
     851             : 
     852             : {
     853             :     // Open the file.
     854         602 :     VSILFILE *fp = nullptr;
     855             : 
     856         602 :     if (bUpdate)
     857          88 :         fp = VSIFOpenExL(pszFilename, "rb+", true);
     858             :     else
     859         514 :         fp = VSIFOpenExL(pszFilename, "rb", true);
     860         602 :     if (fp == nullptr)
     861             :     {
     862           0 :         CPLError(CE_Warning, CPLE_OpenFailed, "Failed to open %s.",
     863             :                  VSIGetLastErrorMsg());
     864           0 :         return false;
     865             :     }
     866             : 
     867         602 :     if (!bUpdate && strstr(pszFilename, "/vsigzip/") == nullptr &&
     868         514 :         strstr(pszFilename, "/vsizip/") == nullptr)
     869         430 :         fp = VSICreateBufferedReaderHandle(fp);
     870             : 
     871        1204 :     CPLString osLayerName = CPLGetBasenameSafe(pszFilename);
     872        1204 :     CPLString osExt = CPLGetExtensionSafe(pszFilename);
     873         602 :     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         602 :     int nMaxLineSize = atoi(CPLGetConfigOption(
     896             :         "OGR_CSV_MAX_LINE_SIZE",
     897             :         CSLFetchNameValueDef(papszOpenOptionsIn, "MAX_LINE_SIZE",
     898         602 :                              CPLSPrintf("%d", OGR_CSV_DEFAULT_MAX_LINE_SIZE))));
     899         602 :     size_t nMaxLineSizeAsSize_t = static_cast<size_t>(nMaxLineSize);
     900         602 :     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        1204 :     std::string osLine;
     909             :     {
     910         602 :         const char *pszLine = CPLReadLine2L(fp, nMaxLineSize, nullptr);
     911         602 :         if (pszLine == nullptr)
     912             :         {
     913           0 :             VSIFCloseL(fp);
     914           0 :             return false;
     915             :         }
     916         602 :         osLine = pszLine;
     917             :     }
     918             : 
     919         602 :     char chDelimiter = ',';
     920             :     const char *pszDelimiter =
     921         602 :         CSLFetchNameValueDef(papszOpenOptionsIn, "SEPARATOR", "AUTO");
     922         602 :     if (EQUAL(pszDelimiter, "AUTO"))
     923             :     {
     924         594 :         chDelimiter = CSVDetectSeperator(osLine.c_str());
     925         594 :         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         594 :         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         602 :     VSIRewindL(fp);
     992             : 
     993             :     // Create a layer.
     994         602 :     if (pszNfdcRunwaysGeomField != nullptr)
     995             :     {
     996           0 :         osLayerName += "_";
     997           0 :         osLayerName += pszNfdcRunwaysGeomField;
     998             :     }
     999         602 :     else if (pszGeonamesGeomFieldPrefix != nullptr &&
    1000           0 :              !EQUAL(pszGeonamesGeomFieldPrefix, ""))
    1001             :     {
    1002           0 :         osLayerName += "_";
    1003           0 :         osLayerName += pszGeonamesGeomFieldPrefix;
    1004             :     }
    1005         602 :     if (EQUAL(pszFilename, "/vsistdin/"))
    1006           0 :         osLayerName = "layer";
    1007             : 
    1008             :     auto poCSVLayer =
    1009             :         std::make_unique<OGRCSVLayer>(this, osLayerName, fp, nMaxLineSize,
    1010        1204 :                                       pszFilename, FALSE, bUpdate, chDelimiter);
    1011         602 :     poCSVLayer->BuildFeatureDefn(pszNfdcRunwaysGeomField,
    1012             :                                  pszGeonamesGeomFieldPrefix,
    1013             :                                  papszOpenOptionsIn);
    1014         602 :     if (bUpdate)
    1015             :     {
    1016          88 :         m_apoLayers.emplace_back(std::make_unique<OGRCSVEditableLayer>(
    1017         176 :             poCSVLayer.release(), papszOpenOptionsIn));
    1018             :     }
    1019             :     else
    1020             :     {
    1021         514 :         m_apoLayers.emplace_back(std::move(poCSVLayer));
    1022             : 
    1023         514 :         if (!DealWithOgrSchemaOpenOption(papszOpenOptionsIn))
    1024             :         {
    1025           1 :             m_apoLayers.pop_back();
    1026           1 :             return false;
    1027             :         }
    1028             :     }
    1029             : 
    1030         601 :     return true;
    1031             : }
    1032             : 
    1033             : /************************************************************************/
    1034             : /*                           ICreateLayer()                             */
    1035             : /************************************************************************/
    1036             : 
    1037             : OGRLayer *
    1038         125 : OGRCSVDataSource::ICreateLayer(const char *pszLayerName,
    1039             :                                const OGRGeomFieldDefn *poSrcGeomFieldDefn,
    1040             :                                CSLConstList papszOptions)
    1041             : {
    1042             :     // Verify we are in update mode.
    1043         125 :     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         124 :         poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetType() : wkbNone;
    1055             :     const auto poSpatialRef =
    1056         124 :         poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetSpatialRef() : nullptr;
    1057             : 
    1058             :     // Verify that the datasource is a directory.
    1059             :     VSIStatBufL sStatBuf;
    1060             : 
    1061         124 :     if (STARTS_WITH(pszName, "/vsizip/"))
    1062             :     {
    1063             :         // Do nothing.
    1064             :     }
    1065         247 :     else if (!EQUAL(pszName, "/vsistdout/") &&
    1066         123 :              (VSIStatL(pszName, &sStatBuf) != 0 ||
    1067         123 :               !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         124 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "CREATE_CSVT", "NO"));
    1077             : 
    1078             :     // What filename would we use?
    1079         248 :     CPLString osFilename;
    1080             : 
    1081         124 :     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         123 :     else if (osDefaultCSVName != "")
    1092             :     {
    1093          59 :         osFilename = CPLFormFilenameSafe(pszName, osDefaultCSVName, nullptr);
    1094          59 :         osDefaultCSVName = "";
    1095             :     }
    1096             :     else
    1097             :     {
    1098          64 :         osFilename = CPLFormFilenameSafe(pszName, pszLayerName, "csv");
    1099             :     }
    1100             : 
    1101             :     // Does this directory/file already exist?
    1102         124 :     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         123 :     const char *pszDelimiter = CSLFetchNameValue(papszOptions, "SEPARATOR");
    1113         123 :     char chDelimiter = ',';
    1114         123 :     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             :         else
    1125             :         {
    1126           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1127             :                      "SEPARATOR=%s not understood, use one of "
    1128             :                      "COMMA, SEMICOLON, SPACE or TAB.",
    1129             :                      pszDelimiter);
    1130             :         }
    1131             :     }
    1132             : 
    1133             :     // Create a layer.
    1134             : 
    1135             :     auto poCSVLayer = std::make_unique<OGRCSVLayer>(
    1136         246 :         this, pszLayerName, nullptr, -1, osFilename, true, true, chDelimiter);
    1137             : 
    1138         123 :     poCSVLayer->BuildFeatureDefn();
    1139             : 
    1140             :     // Was a particular CRLF order requested?
    1141         123 :     const char *pszCRLFFormat = CSLFetchNameValue(papszOptions, "LINEFORMAT");
    1142         123 :     bool bUseCRLF = false;
    1143             : 
    1144         123 :     if (pszCRLFFormat == nullptr)
    1145             :     {
    1146             : #ifdef _WIN32
    1147             :         bUseCRLF = true;
    1148             : #endif
    1149             :     }
    1150          18 :     else if (EQUAL(pszCRLFFormat, "CRLF"))
    1151             :     {
    1152           1 :         bUseCRLF = true;
    1153             :     }
    1154          17 :     else if (!EQUAL(pszCRLFFormat, "LF"))
    1155             :     {
    1156           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1157             :                  "LINEFORMAT=%s not understood, use one of CRLF or LF.",
    1158             :                  pszCRLFFormat);
    1159             : #ifdef _WIN32
    1160             :         bUseCRLF = true;
    1161             : #endif
    1162             :     }
    1163             : 
    1164         123 :     poCSVLayer->SetCRLF(bUseCRLF);
    1165             : 
    1166             :     const char *pszStringQuoting =
    1167         123 :         CSLFetchNameValueDef(papszOptions, "STRING_QUOTING", "IF_AMBIGUOUS");
    1168         246 :     poCSVLayer->SetStringQuoting(
    1169         123 :         EQUAL(pszStringQuoting, "IF_NEEDED")
    1170             :             ? OGRCSVLayer::StringQuoting::IF_NEEDED
    1171         110 :         : EQUAL(pszStringQuoting, "ALWAYS")
    1172         110 :             ? OGRCSVLayer::StringQuoting::ALWAYS
    1173             :             : OGRCSVLayer::StringQuoting::IF_AMBIGUOUS);
    1174             : 
    1175             :     // Should we write the geometry?
    1176         123 :     const char *pszGeometry = CSLFetchNameValue(papszOptions, "GEOMETRY");
    1177         123 :     if (bEnableGeometryFields)
    1178             :     {
    1179          11 :         poCSVLayer->SetWriteGeometry(
    1180             :             eGType, OGR_CSV_GEOM_AS_WKT,
    1181             :             CSLFetchNameValueDef(papszOptions, "GEOMETRY_NAME", "WKT"));
    1182             :     }
    1183         112 :     else if (pszGeometry != nullptr)
    1184             :     {
    1185          35 :         if (EQUAL(pszGeometry, "AS_WKT"))
    1186             :         {
    1187          28 :             poCSVLayer->SetWriteGeometry(
    1188             :                 eGType, OGR_CSV_GEOM_AS_WKT,
    1189             :                 CSLFetchNameValueDef(papszOptions, "GEOMETRY_NAME", "WKT"));
    1190             :         }
    1191           7 :         else if (EQUAL(pszGeometry, "AS_XYZ") || EQUAL(pszGeometry, "AS_XY") ||
    1192           2 :                  EQUAL(pszGeometry, "AS_YX"))
    1193             :         {
    1194           6 :             if (eGType == wkbUnknown || wkbFlatten(eGType) == wkbPoint)
    1195             :             {
    1196          10 :                 poCSVLayer->SetWriteGeometry(
    1197           5 :                     eGType, EQUAL(pszGeometry, "AS_XYZ")  ? OGR_CSV_GEOM_AS_XYZ
    1198           3 :                             : EQUAL(pszGeometry, "AS_XY") ? OGR_CSV_GEOM_AS_XY
    1199             :                                                           : OGR_CSV_GEOM_AS_YX);
    1200             :             }
    1201             :             else
    1202             :             {
    1203           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1204             :                          "Geometry type %s is not compatible with "
    1205             :                          "GEOMETRY=%s.",
    1206             :                          OGRGeometryTypeToName(eGType), pszGeometry);
    1207           1 :                 return nullptr;
    1208             :             }
    1209             :         }
    1210             :         else
    1211             :         {
    1212           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    1213             :                      "Unsupported value %s for creation option GEOMETRY",
    1214             :                      pszGeometry);
    1215           1 :             return nullptr;
    1216             :         }
    1217             :     }
    1218             : 
    1219             :     // Should we create a CSVT file?
    1220         121 :     if (bCreateCSVT)
    1221             :     {
    1222          28 :         poCSVLayer->SetCreateCSVT(true);
    1223             : 
    1224             :         // Create .prj file.
    1225          28 :         if (poSpatialRef != nullptr)
    1226             :         {
    1227           7 :             char *pszWKT = nullptr;
    1228           7 :             poSpatialRef->exportToWkt(&pszWKT);
    1229           7 :             if (pszWKT)
    1230             :             {
    1231           7 :                 VSILFILE *fpPRJ = VSIFOpenL(
    1232          14 :                     CPLResetExtensionSafe(osFilename, "prj").c_str(), "wb");
    1233           7 :                 if (fpPRJ)
    1234             :                 {
    1235           7 :                     CPL_IGNORE_RET_VAL(VSIFPrintfL(fpPRJ, "%s\n", pszWKT));
    1236           7 :                     VSIFCloseL(fpPRJ);
    1237             :                 }
    1238           7 :                 CPLFree(pszWKT);
    1239             :             }
    1240             :         }
    1241             :     }
    1242             : 
    1243             :     // Should we write a UTF8 BOM?
    1244         121 :     const char *pszWriteBOM = CSLFetchNameValue(papszOptions, "WRITE_BOM");
    1245         121 :     if (pszWriteBOM)
    1246           2 :         poCSVLayer->SetWriteBOM(CPLTestBool(pszWriteBOM));
    1247             : 
    1248         121 :     if (poCSVLayer->GetLayerDefn()->GetGeomFieldCount() > 0 &&
    1249             :         poSrcGeomFieldDefn)
    1250             :     {
    1251          39 :         auto poGeomFieldDefn = poCSVLayer->GetLayerDefn()->GetGeomFieldDefn(0);
    1252          39 :         poGeomFieldDefn->SetCoordinatePrecision(
    1253             :             poSrcGeomFieldDefn->GetCoordinatePrecision());
    1254             :     }
    1255             : 
    1256         121 :     if (osFilename != "/vsistdout/")
    1257         120 :         m_apoLayers.emplace_back(std::make_unique<OGRCSVEditableLayer>(
    1258         240 :             poCSVLayer.release(), nullptr));
    1259             :     else
    1260           1 :         m_apoLayers.emplace_back(std::move(poCSVLayer));
    1261             : 
    1262         121 :     return m_apoLayers.back()->GetLayer();
    1263             : }
    1264             : 
    1265             : /************************************************************************/
    1266             : /*                            DeleteLayer()                             */
    1267             : /************************************************************************/
    1268             : 
    1269          20 : OGRErr OGRCSVDataSource::DeleteLayer(int iLayer)
    1270             : 
    1271             : {
    1272             :     // Verify we are in update mode.
    1273          20 :     if (!bUpdate)
    1274             :     {
    1275           1 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    1276             :                  "Data source %s opened read-only.\n"
    1277             :                  "Layer %d cannot be deleted.",
    1278             :                  pszName, iLayer);
    1279             : 
    1280           1 :         return OGRERR_FAILURE;
    1281             :     }
    1282             : 
    1283          19 :     if (iLayer < 0 || iLayer >= GetLayerCount())
    1284             :     {
    1285           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    1286             :                  "Layer %d not in legal range of 0 to %d.", iLayer,
    1287           2 :                  GetLayerCount() - 1);
    1288           2 :         return OGRERR_FAILURE;
    1289             :     }
    1290             : 
    1291          34 :     for (const auto &osFilename : m_apoLayers[iLayer]->GetFileList())
    1292             :     {
    1293          17 :         VSIUnlink(osFilename.c_str());
    1294             :     }
    1295             : 
    1296          17 :     m_apoLayers.erase(m_apoLayers.begin() + iLayer);
    1297             : 
    1298          17 :     return OGRERR_NONE;
    1299             : }
    1300             : 
    1301             : /************************************************************************/
    1302             : /*                       CreateForSingleFile()                          */
    1303             : /************************************************************************/
    1304             : 
    1305          59 : void OGRCSVDataSource::CreateForSingleFile(const char *pszDirname,
    1306             :                                            const char *pszFilename)
    1307             : {
    1308          59 :     pszName = CPLStrdup(pszDirname);
    1309          59 :     bUpdate = true;
    1310          59 :     osDefaultCSVName = CPLGetFilename(pszFilename);
    1311          59 : }
    1312             : 
    1313             : /************************************************************************/
    1314             : /*                            GetFileList()                             */
    1315             : /************************************************************************/
    1316             : 
    1317           7 : char **OGRCSVDataSource::GetFileList()
    1318             : {
    1319          14 :     CPLStringList oFileList;
    1320          19 :     for (auto &poLayer : m_apoLayers)
    1321             :     {
    1322          35 :         for (const auto &osFilename : poLayer->GetFileList())
    1323             :         {
    1324          23 :             oFileList.AddString(osFilename.c_str());
    1325             :         }
    1326             :     }
    1327          14 :     return oFileList.StealList();
    1328             : }

Generated by: LCOV version 1.14