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

Generated by: LCOV version 1.14