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

Generated by: LCOV version 1.14