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