Coverage Report

Created: 2026-02-14 09:00

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
1.09k
        : m_poCSVLayer(poCSVLayer),
52
1.09k
          m_papszOpenOptions(CSLDuplicate(papszOpenOptions))
53
1.09k
    {
54
1.09k
    }
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
1.09k
{
73
1.09k
    CSLDestroy(m_papszOpenOptions);
74
1.09k
}
75
76
/************************************************************************/
77
/*                         EditableSyncToDisk()                         */
78
/************************************************************************/
79
80
OGRErr OGRCSVEditableLayerSynchronizer::EditableSyncToDisk(
81
    OGRLayer *poEditableLayer, OGRLayer **ppoDecoratedLayer)
82
219
{
83
219
    CPLAssert(m_poCSVLayer == *ppoDecoratedLayer);
84
85
219
    GDALDataset *poDS = m_poCSVLayer->GetDataset();
86
219
    const CPLString osLayerName(m_poCSVLayer->GetName());
87
219
    const CPLString osFilename(m_poCSVLayer->GetFilename());
88
219
    const bool bCreateCSVT = m_poCSVLayer->GetCreateCSVT();
89
219
    const CPLString osCSVTFilename(CPLResetExtensionSafe(osFilename, "csvt"));
90
219
    VSIStatBufL sStatBuf;
91
219
    const bool bHasCSVT = VSIStatL(osCSVTFilename, &sStatBuf) == 0;
92
219
    CPLString osTmpFilename(osFilename);
93
219
    CPLString osTmpCSVTFilename(osFilename);
94
219
    if (VSIStatL(osFilename, &sStatBuf) == 0)
95
211
    {
96
211
        osTmpFilename += "_ogr_tmp.csv";
97
211
        osTmpCSVTFilename += "_ogr_tmp.csvt";
98
211
    }
99
219
    const char chDelimiter = m_poCSVLayer->GetDelimiter();
100
219
    OGRCSVLayer *poCSVTmpLayer = new OGRCSVLayer(
101
219
        poDS, osLayerName, nullptr, -1, osTmpFilename, true, true, chDelimiter);
102
219
    poCSVTmpLayer->BuildFeatureDefn(nullptr, nullptr, m_papszOpenOptions);
103
219
    poCSVTmpLayer->SetCRLF(m_poCSVLayer->GetCRLF());
104
219
    poCSVTmpLayer->SetCreateCSVT(bCreateCSVT || bHasCSVT);
105
219
    poCSVTmpLayer->SetWriteBOM(m_poCSVLayer->GetWriteBOM());
106
219
    poCSVTmpLayer->SetStringQuoting(m_poCSVLayer->GetStringQuoting());
107
108
219
    if (m_poCSVLayer->GetGeometryFormat() == OGR_CSV_GEOM_AS_WKT)
109
0
        poCSVTmpLayer->SetWriteGeometry(wkbNone, OGR_CSV_GEOM_AS_WKT, nullptr);
110
111
219
    const bool bKeepGeomColmuns =
112
219
        CPLFetchBool(m_papszOpenOptions, "KEEP_GEOM_COLUMNS", true);
113
114
219
    OGRErr eErr = OGRERR_NONE;
115
219
    OGRFeatureDefn *poEditableFDefn = poEditableLayer->GetLayerDefn();
116
3.88k
    for (int i = 0; eErr == OGRERR_NONE && i < poEditableFDefn->GetFieldCount();
117
3.66k
         i++)
118
3.66k
    {
119
3.66k
        OGRFieldDefn oFieldDefn(poEditableFDefn->GetFieldDefn(i));
120
3.66k
        int iGeomFieldIdx = 0;
121
3.66k
        if ((EQUAL(oFieldDefn.GetNameRef(), "WKT") &&
122
39
             (iGeomFieldIdx = poEditableFDefn->GetGeomFieldIndex("")) >= 0) ||
123
3.66k
            (bKeepGeomColmuns &&
124
3.66k
             (iGeomFieldIdx = poEditableFDefn->GetGeomFieldIndex(
125
3.66k
                  (std::string("geom_") + oFieldDefn.GetNameRef()).c_str())) >=
126
3.66k
                 0))
127
0
        {
128
0
            OGRGeomFieldDefn oGeomFieldDefn(
129
0
                poEditableFDefn->GetGeomFieldDefn(iGeomFieldIdx));
130
0
            eErr = poCSVTmpLayer->CreateGeomField(&oGeomFieldDefn);
131
0
        }
132
3.66k
        else
133
3.66k
        {
134
3.66k
            eErr = poCSVTmpLayer->CreateField(&oFieldDefn);
135
3.66k
        }
136
3.66k
    }
137
138
219
    const bool bHasXY = !m_poCSVLayer->GetXField().empty() &&
139
0
                        !m_poCSVLayer->GetYField().empty();
140
219
    const bool bHasZ = !m_poCSVLayer->GetZField().empty();
141
219
    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
219
    int nFirstGeomColIdx = 0;
167
219
    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
219
    if (!(poEditableFDefn->GetGeomFieldCount() == 1 && bHasXY))
177
219
    {
178
219
        for (int i = nFirstGeomColIdx;
179
219
             eErr == OGRERR_NONE && i < poEditableFDefn->GetGeomFieldCount();
180
219
             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
219
    }
190
191
219
    poEditableLayer->ResetReading();
192
193
    // Disable all filters.
194
219
    const char *pszQueryStringConst = poEditableLayer->GetAttrQueryString();
195
219
    char *pszQueryStringBak =
196
219
        pszQueryStringConst ? CPLStrdup(pszQueryStringConst) : nullptr;
197
219
    poEditableLayer->SetAttributeFilter(nullptr);
198
199
219
    const int iFilterGeomIndexBak = poEditableLayer->GetGeomFieldFilter();
200
219
    OGRGeometry *poFilterGeomBak = poEditableLayer->GetSpatialFilter();
201
219
    if (poFilterGeomBak)
202
0
        poFilterGeomBak = poFilterGeomBak->clone();
203
219
    poEditableLayer->SetSpatialFilter(nullptr);
204
205
219
    auto aoMapSrcToTargetIdx =
206
219
        poCSVTmpLayer->GetLayerDefn()->ComputeMapForSetFrom(
207
219
            poEditableLayer->GetLayerDefn(), true);
208
219
    aoMapSrcToTargetIdx.push_back(
209
219
        -1);  // add dummy entry to be sure that .data() is valid
210
211
219
    for (auto &&poFeature : poEditableLayer)
212
174k
    {
213
174k
        if (eErr != OGRERR_NONE)
214
1
            break;
215
174k
        OGRFeature *poNewFeature =
216
174k
            new OGRFeature(poCSVTmpLayer->GetLayerDefn());
217
174k
        poNewFeature->SetFrom(poFeature.get(), aoMapSrcToTargetIdx.data(),
218
174k
                              true);
219
174k
        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
174k
        eErr = poCSVTmpLayer->CreateFeature(poNewFeature);
238
174k
        delete poNewFeature;
239
174k
    }
240
219
    delete poCSVTmpLayer;
241
242
    // Restore filters.
243
219
    poEditableLayer->SetAttributeFilter(pszQueryStringBak);
244
219
    CPLFree(pszQueryStringBak);
245
219
    poEditableLayer->SetSpatialFilter(iFilterGeomIndexBak, poFilterGeomBak);
246
219
    delete poFilterGeomBak;
247
248
219
    if (eErr != OGRERR_NONE)
249
1
    {
250
1
        CPLError(CE_Failure, CPLE_AppDefined, "Error while creating %s",
251
1
                 osTmpFilename.c_str());
252
1
        VSIUnlink(osTmpFilename);
253
1
        VSIUnlink(CPLResetExtensionSafe(osTmpFilename, "csvt").c_str());
254
1
        return eErr;
255
1
    }
256
257
218
    delete m_poCSVLayer;
258
259
218
    if (osFilename != osTmpFilename)
260
211
    {
261
211
        const CPLString osTmpOriFilename(osFilename + ".ogr_bak");
262
211
        const CPLString osTmpOriCSVTFilename(osCSVTFilename + ".ogr_bak");
263
211
        if (VSIRename(osFilename, osTmpOriFilename) != 0 ||
264
211
            (bHasCSVT &&
265
0
             VSIRename(osCSVTFilename, osTmpOriCSVTFilename) != 0) ||
266
211
            VSIRename(osTmpFilename, osFilename) != 0 ||
267
211
            (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
211
        VSIUnlink(osTmpOriFilename);
275
211
        if (bHasCSVT)
276
0
            VSIUnlink(osTmpOriCSVTFilename);
277
211
    }
278
279
218
    VSILFILE *fp = VSIFOpenL(osFilename, "rb+");
280
218
    if (fp == nullptr)
281
1
    {
282
1
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot reopen updated %s",
283
1
                 osFilename.c_str());
284
1
        *ppoDecoratedLayer = nullptr;
285
1
        m_poCSVLayer = nullptr;
286
1
        return OGRERR_FAILURE;
287
1
    }
288
289
217
    m_poCSVLayer =
290
217
        new OGRCSVLayer(poDS, osLayerName, fp, -1, osFilename, false, /* new */
291
217
                        true, /* update */
292
217
                        chDelimiter);
293
217
    m_poCSVLayer->BuildFeatureDefn(nullptr, nullptr, m_papszOpenOptions);
294
217
    *ppoDecoratedLayer = m_poCSVLayer;
295
296
217
    return OGRERR_NONE;
297
218
}
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
11.9k
    {
312
11.9k
        return this;
313
11.9k
    }
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
1.09k
    : OGREditableLayer(
337
1.09k
          poCSVLayer, true,
338
1.09k
          new OGRCSVEditableLayerSynchronizer(poCSVLayer, papszOpenOptions),
339
1.09k
          true)
340
1.09k
{
341
1.09k
    SetSupportsCreateGeomField(true);
342
1.09k
    SetSupportsCurveGeometries(true);
343
1.09k
}
Unexecuted instantiation: OGRCSVEditableLayer::OGRCSVEditableLayer(OGRCSVLayer*, char const* const*)
OGRCSVEditableLayer::OGRCSVEditableLayer(OGRCSVLayer*, char const* const*)
Line
Count
Source
336
1.09k
    : OGREditableLayer(
337
1.09k
          poCSVLayer, true,
338
1.09k
          new OGRCSVEditableLayerSynchronizer(poCSVLayer, papszOpenOptions),
339
1.09k
          true)
340
1.09k
{
341
1.09k
    SetSupportsCreateGeomField(true);
342
1.09k
    SetSupportsCurveGeometries(true);
343
1.09k
}
344
345
/************************************************************************/
346
/*                            CreateField()                             */
347
/************************************************************************/
348
349
OGRErr OGRCSVEditableLayer::CreateField(const OGRFieldDefn *poNewField,
350
                                        int bApproxOK)
351
352
12.8k
{
353
12.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
12.8k
    if (m_oSetFields.empty())
360
1.08k
    {
361
1.08k
        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
1.08k
    }
369
370
12.8k
    const OGRCSVCreateFieldAction eAction = OGRCSVLayer::PreCreateField(
371
12.8k
        m_poEditableFeatureDefn, m_oSetFields, poNewField, bApproxOK);
372
12.8k
    if (eAction == CREATE_FIELD_DO_NOTHING)
373
0
        return OGRERR_NONE;
374
12.8k
    if (eAction == CREATE_FIELD_ERROR)
375
0
        return OGRERR_FAILURE;
376
12.8k
    OGRErr eErr = OGREditableLayer::CreateField(poNewField, bApproxOK);
377
12.8k
    if (eErr == OGRERR_NONE)
378
12.8k
    {
379
12.8k
        m_oSetFields.insert(CPLString(poNewField->GetNameRef()).toupper());
380
12.8k
    }
381
12.8k
    return eErr;
382
12.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
35.8k
OGRCSVDataSource::OGRCSVDataSource() = default;
421
422
/************************************************************************/
423
/*                         ~OGRCSVDataSource()                          */
424
/************************************************************************/
425
426
OGRCSVDataSource::~OGRCSVDataSource()
427
428
35.8k
{
429
35.8k
    m_apoLayers.clear();
430
431
35.8k
    if (bUpdate)
432
326
        OGRCSVDriverRemoveFromMap(pszName, this);
433
434
35.8k
    CPLFree(pszName);
435
35.8k
}
436
437
/************************************************************************/
438
/*                           TestCapability()                           */
439
/************************************************************************/
440
441
int OGRCSVDataSource::TestCapability(const char *pszCap) const
442
443
10.4k
{
444
10.4k
    if (EQUAL(pszCap, ODsCCreateLayer))
445
1.24k
        return bUpdate;
446
9.25k
    else if (EQUAL(pszCap, ODsCDeleteLayer))
447
0
        return bUpdate;
448
9.25k
    else if (EQUAL(pszCap, ODsCCreateGeomFieldAfterCreateLayer))
449
1.40k
        return bUpdate && bEnableGeometryFields;
450
7.85k
    else if (EQUAL(pszCap, ODsCCurveGeometries))
451
12
        return TRUE;
452
7.84k
    else if (EQUAL(pszCap, ODsCMeasuredGeometries))
453
0
        return TRUE;
454
7.84k
    else if (EQUAL(pszCap, ODsCZGeometries))
455
0
        return TRUE;
456
7.84k
    else if (EQUAL(pszCap, ODsCRandomLayerWrite))
457
0
        return bUpdate;
458
7.84k
    else
459
7.84k
        return FALSE;
460
10.4k
}
461
462
/************************************************************************/
463
/*                              GetLayer()                              */
464
/************************************************************************/
465
466
const OGRLayer *OGRCSVDataSource::GetLayer(int iLayer) const
467
468
165k
{
469
165k
    if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
470
0
        return nullptr;
471
472
165k
    return m_apoLayers[iLayer]->GetLayer();
473
165k
}
474
475
/************************************************************************/
476
/*                          GetRealExtension()                          */
477
/************************************************************************/
478
479
CPLString OGRCSVDataSource::GetRealExtension(CPLString osFilename)
480
245k
{
481
245k
    CPLString osExt = CPLGetExtensionSafe(osFilename);
482
245k
    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
245k
    return osExt;
495
245k
}
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
35.6k
{
506
35.6k
    pszName = CPLStrdup(pszFilename);
507
35.6k
    bUpdate = CPL_TO_BOOL(bUpdateIn);
508
509
35.6k
    if (bUpdate && bForceOpen && EQUAL(pszFilename, "/vsistdout/"))
510
0
        return TRUE;
511
512
    // For writable /vsizip/, do nothing more.
513
35.6k
    if (bUpdate && bForceOpen && STARTS_WITH(pszFilename, "/vsizip/"))
514
0
        return TRUE;
515
516
35.6k
    CPLString osFilename(pszFilename);
517
35.6k
    const CPLString osBaseFilename = CPLGetFilename(pszFilename);
518
35.6k
    const CPLString osExt = GetRealExtension(osFilename);
519
520
35.6k
    bool bUSGeonamesFile = false;
521
35.6k
    if (STARTS_WITH_CI(osFilename, "CSV:"))
522
5.25k
    {
523
5.25k
        bSingleDriver = true;
524
5.25k
        osFilename = osFilename.substr(strlen("CSV:"));
525
5.25k
    }
526
527
    // Those are *not* real .XLS files, but text file with tab as column
528
    // separator.
529
35.6k
    if (EQUAL(osBaseFilename, "NfdcFacilities.xls") ||
530
35.6k
        EQUAL(osBaseFilename, "NfdcRunways.xls") ||
531
35.5k
        EQUAL(osBaseFilename, "NfdcRemarks.xls") ||
532
35.5k
        EQUAL(osBaseFilename, "NfdcSchedules.xls"))
533
101
    {
534
101
        if (bUpdate)
535
0
            return FALSE;
536
101
        bSingleDriver = true;
537
101
    }
538
35.5k
    else if ((STARTS_WITH_CI(osBaseFilename, "NationalFile_") ||
539
35.5k
              STARTS_WITH_CI(osBaseFilename, "POP_PLACES_") ||
540
35.5k
              STARTS_WITH_CI(osBaseFilename, "HIST_FEATURES_") ||
541
35.5k
              STARTS_WITH_CI(osBaseFilename, "US_CONCISE_") ||
542
35.5k
              STARTS_WITH_CI(osBaseFilename, "AllNames_") ||
543
35.5k
              STARTS_WITH_CI(osBaseFilename, "Feature_Description_History_") ||
544
35.5k
              STARTS_WITH_CI(osBaseFilename, "ANTARCTICA_") ||
545
35.5k
              STARTS_WITH_CI(osBaseFilename, "GOVT_UNITS_") ||
546
35.5k
              STARTS_WITH_CI(osBaseFilename, "NationalFedCodes_") ||
547
35.5k
              STARTS_WITH_CI(osBaseFilename, "AllStates_") ||
548
35.5k
              STARTS_WITH_CI(osBaseFilename, "AllStatesFedCodes_") ||
549
35.4k
              (osBaseFilename.size() > 2 &&
550
35.4k
               STARTS_WITH_CI(osBaseFilename + 2, "_Features_")) ||
551
35.4k
              (osBaseFilename.size() > 2 &&
552
35.4k
               STARTS_WITH_CI(osBaseFilename + 2, "_FedCodes_"))) &&
553
116
             (EQUAL(osExt, "txt") || EQUAL(osExt, "zip")))
554
89
    {
555
89
        if (bUpdate)
556
0
            return FALSE;
557
89
        bSingleDriver = true;
558
89
        bUSGeonamesFile = true;
559
560
89
        if (EQUAL(osExt, "zip") && strstr(osFilename, "/vsizip/") == nullptr)
561
7
        {
562
7
            osFilename = "/vsizip/" + osFilename;
563
7
        }
564
89
    }
565
35.4k
    else if (EQUAL(osBaseFilename, "allCountries.txt") ||
566
35.4k
             EQUAL(osBaseFilename, "allCountries.zip"))
567
13
    {
568
13
        if (bUpdate)
569
0
            return FALSE;
570
13
        bSingleDriver = true;
571
572
13
        if (EQUAL(osExt, "zip") && strstr(osFilename, "/vsizip/") == nullptr)
573
13
        {
574
13
            osFilename = "/vsizip/" + osFilename;
575
13
        }
576
13
    }
577
578
    // Determine what sort of object this is.
579
35.6k
    VSIStatBufL sStatBuf;
580
581
35.6k
    if (VSIStatExL(osFilename, &sStatBuf, VSI_STAT_NATURE_FLAG) != 0)
582
528
        return FALSE;
583
584
    // Is this a single CSV file?
585
35.1k
    if (VSI_ISREG(sStatBuf.st_mode) &&
586
21.5k
        (bSingleDriver || EQUAL(osExt, "csv") || EQUAL(osExt, "tsv") ||
587
2
         EQUAL(osExt, "psv")))
588
21.5k
    {
589
21.5k
        if (EQUAL(CPLGetFilename(osFilename), "NfdcFacilities.xls"))
590
0
        {
591
0
            return OpenTable(osFilename, papszOpenOptionsIn, "ARP");
592
0
        }
593
21.5k
        else if (EQUAL(CPLGetFilename(osFilename), "NfdcRunways.xls"))
594
97
        {
595
97
            OpenTable(osFilename, papszOpenOptionsIn, "BaseEndPhysical");
596
97
            OpenTable(osFilename, papszOpenOptionsIn, "BaseEndDisplaced");
597
97
            OpenTable(osFilename, papszOpenOptionsIn, "ReciprocalEndPhysical");
598
97
            OpenTable(osFilename, papszOpenOptionsIn, "ReciprocalEndDisplaced");
599
97
            return !m_apoLayers.empty();
600
97
        }
601
21.4k
        else if (bUSGeonamesFile)
602
82
        {
603
            // GNIS specific.
604
82
            if (STARTS_WITH_CI(osBaseFilename, "NationalFedCodes_") ||
605
82
                STARTS_WITH_CI(osBaseFilename, "AllStatesFedCodes_") ||
606
82
                STARTS_WITH_CI(osBaseFilename, "ANTARCTICA_") ||
607
82
                (osBaseFilename.size() > 2 &&
608
82
                 STARTS_WITH_CI(osBaseFilename + 2, "_FedCodes_")))
609
0
            {
610
0
                OpenTable(osFilename, papszOpenOptionsIn, nullptr, "PRIMARY");
611
0
            }
612
82
            else if (STARTS_WITH_CI(osBaseFilename, "GOVT_UNITS_") ||
613
55
                     STARTS_WITH_CI(osBaseFilename,
614
82
                                    "Feature_Description_History_"))
615
30
            {
616
30
                OpenTable(osFilename, papszOpenOptionsIn, nullptr, "");
617
30
            }
618
52
            else
619
52
            {
620
52
                OpenTable(osFilename, papszOpenOptionsIn, nullptr, "PRIM");
621
52
                OpenTable(osFilename, papszOpenOptionsIn, nullptr, "SOURCE");
622
52
            }
623
82
            return !m_apoLayers.empty();
624
82
        }
625
626
21.3k
        return OpenTable(osFilename, papszOpenOptionsIn);
627
21.5k
    }
628
629
    // Is this a single a ZIP file with only a CSV file inside?
630
13.5k
    if (STARTS_WITH(osFilename, "/vsizip/") && EQUAL(osExt, "zip") &&
631
13.5k
        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
13.5k
    if (!VSI_ISDIR(sStatBuf.st_mode))
647
0
        return FALSE;
648
649
    // Scan through for entries ending in .csv.
650
13.5k
    int nNotCSVCount = 0;
651
13.5k
    const CPLStringList aosFilenames(VSIReadDir(osFilename));
652
653
164k
    for (int i = 0; i < aosFilenames.size(); i++)
654
150k
    {
655
150k
        if (EQUAL(aosFilenames[i], ".") || EQUAL(aosFilenames[i], ".."))
656
38
            continue;
657
658
150k
        const std::string osThisExt = CPLGetExtensionSafe(aosFilenames[i]);
659
150k
        if (EQUAL(osThisExt.c_str(), "csvt") || EQUAL(osThisExt.c_str(), "prj"))
660
5.74k
            continue;
661
662
144k
        const CPLString oSubFilename =
663
144k
            CPLFormFilenameSafe(osFilename, aosFilenames[i], nullptr);
664
144k
        if (VSIStatL(oSubFilename, &sStatBuf) != 0 ||
665
144k
            !VSI_ISREG(sStatBuf.st_mode))
666
10.5k
        {
667
10.5k
            nNotCSVCount++;
668
10.5k
            continue;
669
10.5k
        }
670
671
134k
        if (EQUAL(osThisExt.c_str(), "csv"))
672
69.0k
        {
673
69.0k
            if (!OpenTable(oSubFilename, papszOpenOptionsIn))
674
24
            {
675
24
                CPLDebug("CSV", "Cannot open %s", oSubFilename.c_str());
676
24
                nNotCSVCount++;
677
24
                continue;
678
24
            }
679
69.0k
        }
680
        // GNIS specific.
681
65.1k
        else if (strlen(aosFilenames[i]) > 2 &&
682
65.1k
                 STARTS_WITH_CI(aosFilenames[i] + 2, "_Features_") &&
683
57
                 EQUAL(osThisExt.c_str(), "txt"))
684
35
        {
685
35
            bool bRet =
686
35
                OpenTable(oSubFilename, papszOpenOptionsIn, nullptr, "PRIM");
687
35
            bRet |=
688
35
                OpenTable(oSubFilename, papszOpenOptionsIn, nullptr, "SOURCE");
689
35
            if (!bRet)
690
1
            {
691
1
                CPLDebug("CSV", "Cannot open %s", oSubFilename.c_str());
692
1
                nNotCSVCount++;
693
1
                continue;
694
1
            }
695
35
        }
696
        // GNIS specific.
697
65.1k
        else if (strlen(aosFilenames[i]) > 2 &&
698
65.1k
                 STARTS_WITH_CI(aosFilenames[i] + 2, "_FedCodes_") &&
699
0
                 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
65.1k
        else
710
65.1k
        {
711
65.1k
            nNotCSVCount++;
712
65.1k
            continue;
713
65.1k
        }
714
134k
    }
715
716
    // We presume that this is indeed intended to be a CSV
717
    // datasource if over half the files were .csv files.
718
13.5k
    return bForceOpen || nNotCSVCount < GetLayerCount() ||
719
3.63k
           (bSingleDriver && GetLayerCount() > 0);
720
13.5k
}
721
722
const std::vector<int> &OGRCSVDataSource::DeletedFieldIndexes() const
723
21.3M
{
724
21.3M
    return m_oDeletedFieldIndexes;
725
21.3M
}
726
727
/************************************************************************/
728
/*                    DealWithOgrSchemaOpenOption()                     */
729
/************************************************************************/
730
bool OGRCSVDataSource::DealWithOgrSchemaOpenOption(
731
    CSLConstList papszOpenOptionsIn)
732
90.9k
{
733
90.9k
    const std::string osFieldsSchemaOverrideParam =
734
90.9k
        CSLFetchNameValueDef(papszOpenOptionsIn, "OGR_SCHEMA", "");
735
736
90.9k
    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
90.9k
    return true;
767
90.9k
}
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
91.0k
{
779
    // Open the file.
780
91.0k
    VSILFILE *fp = nullptr;
781
782
91.0k
    if (bUpdate)
783
0
        fp = VSIFOpenExL(pszFilename, "rb+", true);
784
91.0k
    else
785
91.0k
        fp = VSIFOpenExL(pszFilename, "rb", true);
786
91.0k
    if (fp == nullptr)
787
16
    {
788
16
        CPLError(CE_Warning, CPLE_OpenFailed, "Failed to open %s.",
789
16
                 VSIGetLastErrorMsg());
790
16
        return false;
791
16
    }
792
793
90.9k
    if (!bUpdate && strstr(pszFilename, "/vsigzip/") == nullptr &&
794
90.9k
        strstr(pszFilename, "/vsizip/") == nullptr)
795
90.9k
        fp = VSICreateBufferedReaderHandle(fp);
796
797
90.9k
    CPLString osLayerName = CPLGetBasenameSafe(pszFilename);
798
90.9k
    CPLString osExt = CPLGetExtensionSafe(pszFilename);
799
90.9k
    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
90.9k
    int nMaxLineSize = atoi(CPLGetConfigOption(
822
90.9k
        "OGR_CSV_MAX_LINE_SIZE",
823
90.9k
        CSLFetchNameValueDef(papszOpenOptionsIn, "MAX_LINE_SIZE",
824
90.9k
                             CPLSPrintf("%d", OGR_CSV_DEFAULT_MAX_LINE_SIZE))));
825
90.9k
    size_t nMaxLineSizeAsSize_t = static_cast<size_t>(nMaxLineSize);
826
90.9k
    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
90.9k
    std::string osLine;
835
90.9k
    {
836
90.9k
        const char *pszLine = CPLReadLine2L(fp, nMaxLineSize, nullptr);
837
90.9k
        if (pszLine == nullptr)
838
12
        {
839
12
            VSIFCloseL(fp);
840
12
            return false;
841
12
        }
842
90.9k
        osLine = pszLine;
843
90.9k
    }
844
845
0
    char chDelimiter = ',';
846
90.9k
    const char *pszDelimiter =
847
90.9k
        CSLFetchNameValueDef(papszOpenOptionsIn, "SEPARATOR", "AUTO");
848
90.9k
    if (EQUAL(pszDelimiter, "AUTO"))
849
90.9k
    {
850
90.9k
        chDelimiter = CSVDetectSeperator(osLine.c_str());
851
90.9k
        if (chDelimiter != '\t' && osLine.find('\t') != std::string::npos)
852
1.90k
        {
853
            // Force the delimiter to be TAB for a .tsv file that has a tabulation
854
            // in its first line */
855
1.90k
            if (EQUAL(osExt, "tsv"))
856
45
            {
857
45
                chDelimiter = '\t';
858
45
            }
859
1.85k
            else
860
1.85k
            {
861
4.31k
                for (int nDontHonourStrings = 0; nDontHonourStrings <= 1;
862
2.45k
                     nDontHonourStrings++)
863
3.08k
                {
864
3.08k
                    const bool bHonourStrings =
865
3.08k
                        !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.08k
                    VSIRewindL(fp);
869
3.08k
                    char **papszTokens = CSVReadParseLine3L(
870
3.08k
                        fp, nMaxLineSizeAsSize_t, "\t", bHonourStrings,
871
3.08k
                        false,  // bKeepLeadingAndClosingQuotes
872
3.08k
                        false,  // bMergeDelimiter
873
3.08k
                        true    // bSkipBOM
874
3.08k
                    );
875
3.08k
                    const int nTokens1 = CSLCount(papszTokens);
876
3.08k
                    CSLDestroy(papszTokens);
877
3.08k
                    papszTokens = CSVReadParseLine3L(
878
3.08k
                        fp, nMaxLineSizeAsSize_t, "\t", bHonourStrings,
879
3.08k
                        false,  // bKeepLeadingAndClosingQuotes
880
3.08k
                        false,  // bMergeDelimiter
881
3.08k
                        true    // bSkipBOM
882
3.08k
                    );
883
3.08k
                    const int nTokens2 = CSLCount(papszTokens);
884
3.08k
                    CSLDestroy(papszTokens);
885
3.08k
                    if (nTokens1 >= 2 && nTokens1 == nTokens2)
886
636
                    {
887
636
                        chDelimiter = '\t';
888
636
                        break;
889
636
                    }
890
3.08k
                }
891
1.85k
            }
892
1.90k
        }
893
894
        // GNIS specific.
895
90.9k
        if (pszGeonamesGeomFieldPrefix != nullptr &&
896
202
            osLine.find('|') != std::string::npos)
897
10
            chDelimiter = '|';
898
90.9k
    }
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
90.9k
    VSIRewindL(fp);
918
919
    // Create a layer.
920
90.9k
    if (pszNfdcRunwaysGeomField != nullptr)
921
388
    {
922
388
        osLayerName += "_";
923
388
        osLayerName += pszNfdcRunwaysGeomField;
924
388
    }
925
90.5k
    else if (pszGeonamesGeomFieldPrefix != nullptr &&
926
202
             !EQUAL(pszGeonamesGeomFieldPrefix, ""))
927
172
    {
928
172
        osLayerName += "_";
929
172
        osLayerName += pszGeonamesGeomFieldPrefix;
930
172
    }
931
90.9k
    if (EQUAL(pszFilename, "/vsistdin/"))
932
0
        osLayerName = "layer";
933
934
90.9k
    auto poCSVLayer =
935
90.9k
        std::make_unique<OGRCSVLayer>(this, osLayerName, fp, nMaxLineSize,
936
90.9k
                                      pszFilename, FALSE, bUpdate, chDelimiter);
937
90.9k
    poCSVLayer->BuildFeatureDefn(pszNfdcRunwaysGeomField,
938
90.9k
                                 pszGeonamesGeomFieldPrefix,
939
90.9k
                                 papszOpenOptionsIn);
940
90.9k
    if (bUpdate)
941
0
    {
942
0
        m_apoLayers.emplace_back(std::make_unique<OGRCSVEditableLayer>(
943
0
            poCSVLayer.release(), papszOpenOptionsIn));
944
0
    }
945
90.9k
    else
946
90.9k
    {
947
90.9k
        m_apoLayers.emplace_back(std::move(poCSVLayer));
948
949
90.9k
        if (!DealWithOgrSchemaOpenOption(papszOpenOptionsIn))
950
0
        {
951
0
            m_apoLayers.pop_back();
952
0
            return false;
953
0
        }
954
90.9k
    }
955
956
90.9k
    return true;
957
90.9k
}
958
959
/************************************************************************/
960
/*                            ICreateLayer()                            */
961
/************************************************************************/
962
963
OGRLayer *
964
OGRCSVDataSource::ICreateLayer(const char *pszLayerName,
965
                               const OGRGeomFieldDefn *poSrcGeomFieldDefn,
966
                               CSLConstList papszOptions)
967
1.24k
{
968
    // Verify we are in update mode.
969
1.24k
    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.24k
    const auto eGType =
980
1.24k
        poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetType() : wkbNone;
981
1.24k
    const auto poSpatialRef =
982
1.24k
        poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetSpatialRef() : nullptr;
983
984
    // Verify that the datasource is a directory.
985
1.24k
    VSIStatBufL sStatBuf;
986
987
1.24k
    if (STARTS_WITH(pszName, "/vsizip/"))
988
0
    {
989
        // Do nothing.
990
0
    }
991
1.24k
    else if (!EQUAL(pszName, "/vsistdout/") &&
992
1.24k
             (VSIStatL(pszName, &sStatBuf) != 0 ||
993
1.18k
              !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.18k
    const bool bCreateCSVT =
1002
1.18k
        CPLTestBool(CSLFetchNameValueDef(papszOptions, "CREATE_CSVT", "NO"));
1003
1004
    // What filename would we use?
1005
1.18k
    CPLString osFilename;
1006
1007
1.18k
    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.18k
    else if (osDefaultCSVName != "")
1018
251
    {
1019
251
        osFilename = CPLFormFilenameSafe(pszName, osDefaultCSVName, nullptr);
1020
251
        osDefaultCSVName = "";
1021
251
    }
1022
938
    else
1023
938
    {
1024
938
        if (CPLLaunderForFilenameSafe(pszLayerName, nullptr) != pszLayerName)
1025
90
        {
1026
90
            CPLError(CE_Failure, CPLE_AppDefined,
1027
90
                     "Illegal characters in '%s' to form a valid filename",
1028
90
                     pszLayerName);
1029
90
            return nullptr;
1030
90
        }
1031
848
        osFilename = CPLFormFilenameSafe(pszName, pszLayerName, "csv");
1032
848
    }
1033
1034
    // Does this directory/file already exist?
1035
1.09k
    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
1.09k
    const char *pszDelimiter = CSLFetchNameValue(papszOptions, "SEPARATOR");
1046
1.09k
    char chDelimiter = ',';
1047
1.09k
    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
1.09k
    auto poCSVLayer = std::make_unique<OGRCSVLayer>(
1071
1.09k
        this, pszLayerName, nullptr, -1, osFilename, true, true, chDelimiter);
1072
1073
1.09k
    poCSVLayer->BuildFeatureDefn();
1074
1075
    // Was a particular CRLF order requested?
1076
1.09k
    const char *pszCRLFFormat = CSLFetchNameValue(papszOptions, "LINEFORMAT");
1077
1.09k
    bool bUseCRLF = false;
1078
1079
1.09k
    if (pszCRLFFormat == nullptr)
1080
1.09k
    {
1081
#ifdef _WIN32
1082
        bUseCRLF = true;
1083
#endif
1084
1.09k
    }
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
1.09k
    poCSVLayer->SetCRLF(bUseCRLF);
1100
1101
1.09k
    const char *pszStringQuoting =
1102
1.09k
        CSLFetchNameValueDef(papszOptions, "STRING_QUOTING", "IF_AMBIGUOUS");
1103
1.09k
    poCSVLayer->SetStringQuoting(
1104
1.09k
        EQUAL(pszStringQuoting, "IF_NEEDED")
1105
1.09k
            ? OGRCSVLayer::StringQuoting::IF_NEEDED
1106
1.09k
        : EQUAL(pszStringQuoting, "ALWAYS")
1107
1.09k
            ? OGRCSVLayer::StringQuoting::ALWAYS
1108
1.09k
            : OGRCSVLayer::StringQuoting::IF_AMBIGUOUS);
1109
1110
    // Should we write the geometry?
1111
1.09k
    const char *pszGeometry = CSLFetchNameValue(papszOptions, "GEOMETRY");
1112
1.09k
    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
1.09k
    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
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
1154
    // Should we create a CSVT file?
1155
1.09k
    if (bCreateCSVT)
1156
0
    {
1157
0
        poCSVLayer->SetCreateCSVT(true);
1158
1159
        // Create .prj file.
1160
0
        if (poSpatialRef != nullptr)
1161
0
        {
1162
0
            char *pszWKT = nullptr;
1163
0
            poSpatialRef->exportToWkt(&pszWKT);
1164
0
            if (pszWKT)
1165
0
            {
1166
0
                VSILFILE *fpPRJ = VSIFOpenL(
1167
0
                    CPLResetExtensionSafe(osFilename, "prj").c_str(), "wb");
1168
0
                if (fpPRJ)
1169
0
                {
1170
0
                    CPL_IGNORE_RET_VAL(VSIFPrintfL(fpPRJ, "%s\n", pszWKT));
1171
0
                    VSIFCloseL(fpPRJ);
1172
0
                }
1173
0
                CPLFree(pszWKT);
1174
0
            }
1175
0
        }
1176
0
    }
1177
1178
    // Should we write a UTF8 BOM?
1179
1.09k
    const char *pszWriteBOM = CSLFetchNameValue(papszOptions, "WRITE_BOM");
1180
1.09k
    if (pszWriteBOM)
1181
0
        poCSVLayer->SetWriteBOM(CPLTestBool(pszWriteBOM));
1182
1183
1.09k
    auto poFeatureDefn = poCSVLayer->GetLayerDefn();
1184
1.09k
    if (poFeatureDefn->GetGeomFieldCount() > 0 && poSrcGeomFieldDefn)
1185
0
    {
1186
0
        auto poGeomFieldDefn = poFeatureDefn->GetGeomFieldDefn(0);
1187
0
        poGeomFieldDefn->SetCoordinatePrecision(
1188
0
            poSrcGeomFieldDefn->GetCoordinatePrecision());
1189
0
    }
1190
1191
1.09k
    poCSVLayer->SetWriteHeader(CPLTestBool(CSLFetchNameValueDef(
1192
1.09k
        papszOptions, "HEADER",
1193
1.09k
        CSLFetchNameValueDef(papszOptions, "HEADERS", "YES"))));
1194
1195
1.09k
    if (osFilename != "/vsistdout/")
1196
1.09k
        m_apoLayers.emplace_back(std::make_unique<OGRCSVEditableLayer>(
1197
1.09k
            poCSVLayer.release(), nullptr));
1198
0
    else
1199
0
        m_apoLayers.emplace_back(std::move(poCSVLayer));
1200
1201
1.09k
    return m_apoLayers.back()->GetLayer();
1202
1.09k
}
1203
1204
/************************************************************************/
1205
/*                            DeleteLayer()                             */
1206
/************************************************************************/
1207
1208
OGRErr OGRCSVDataSource::DeleteLayer(int iLayer)
1209
1210
0
{
1211
    // Verify we are in update mode.
1212
0
    if (!bUpdate)
1213
0
    {
1214
0
        CPLError(CE_Failure, CPLE_NoWriteAccess,
1215
0
                 "Data source %s opened read-only.\n"
1216
0
                 "Layer %d cannot be deleted.",
1217
0
                 pszName, iLayer);
1218
1219
0
        return OGRERR_FAILURE;
1220
0
    }
1221
1222
0
    if (iLayer < 0 || iLayer >= GetLayerCount())
1223
0
    {
1224
0
        CPLError(CE_Failure, CPLE_AppDefined,
1225
0
                 "Layer %d not in legal range of 0 to %d.", iLayer,
1226
0
                 GetLayerCount() - 1);
1227
0
        return OGRERR_FAILURE;
1228
0
    }
1229
1230
0
    for (const auto &osFilename : m_apoLayers[iLayer]->GetFileList())
1231
0
    {
1232
0
        VSIUnlink(osFilename.c_str());
1233
0
    }
1234
1235
0
    m_apoLayers.erase(m_apoLayers.begin() + iLayer);
1236
1237
0
    return OGRERR_NONE;
1238
0
}
1239
1240
/************************************************************************/
1241
/*                        CreateForSingleFile()                         */
1242
/************************************************************************/
1243
1244
void OGRCSVDataSource::CreateForSingleFile(const char *pszDirname,
1245
                                           const char *pszFilename)
1246
263
{
1247
263
    pszName = CPLStrdup(pszDirname);
1248
263
    bUpdate = true;
1249
263
    osDefaultCSVName = CPLGetFilename(pszFilename);
1250
263
}
1251
1252
/************************************************************************/
1253
/*                            GetFileList()                             */
1254
/************************************************************************/
1255
1256
char **OGRCSVDataSource::GetFileList()
1257
1
{
1258
1
    CPLStringList oFileList;
1259
1
    for (auto &poLayer : m_apoLayers)
1260
2
    {
1261
2
        for (const auto &osFilename : poLayer->GetFileList())
1262
2
        {
1263
2
            oFileList.AddString(osFilename.c_str());
1264
2
        }
1265
2
    }
1266
1
    return oFileList.StealList();
1267
1
}