Coverage Report

Created: 2026-08-11 08:26

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