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