Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements OGRShapeDataSource class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Les Technologies SoftMap Inc.
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 "ogrshape.h"
16 :
17 : #include <algorithm>
18 : #include <cstddef>
19 : #include <cstdlib>
20 : #include <cstring>
21 : #include <memory>
22 : #include <set>
23 : #include <vector>
24 :
25 : #include "cpl_conv.h"
26 : #include "cpl_error.h"
27 : #include "cpl_string.h"
28 : #include "cpl_vsi.h"
29 : #include "cpl_vsi_error.h"
30 : #include "gdal.h"
31 : #include "gdal_priv.h"
32 : #include "ogr_core.h"
33 : #include "ogr_geometry.h"
34 : #include "ogr_spatialref.h"
35 : #include "ogrlayerpool.h"
36 : #include "ogrsf_frmts.h"
37 : #include "shapefil.h"
38 : #include "shp_vsi.h"
39 :
40 : // #define IMMEDIATE_OPENING 1
41 :
42 : constexpr int knREFRESH_LOCK_FILE_DELAY_SEC = 10;
43 :
44 : /************************************************************************/
45 : /* DS_SHPOpen() */
46 : /************************************************************************/
47 :
48 3901 : SHPHandle OGRShapeDataSource::DS_SHPOpen(const char *pszShapeFile,
49 : const char *pszAccess)
50 : {
51 : // Do lazy shx loading for /vsicurl/
52 3901 : if (STARTS_WITH(pszShapeFile, "/vsicurl/") && strcmp(pszAccess, "r") == 0)
53 0 : pszAccess = "rl";
54 :
55 : const bool bRestoreSHX =
56 3901 : CPLTestBool(CPLGetConfigOption("SHAPE_RESTORE_SHX", "FALSE"));
57 3901 : SHPHandle hSHP = SHPOpenLLEx(
58 : pszShapeFile, pszAccess,
59 3901 : const_cast<SAHooks *>(VSI_SHP_GetHook(m_b2GBLimit)), bRestoreSHX);
60 :
61 3901 : if (hSHP != nullptr)
62 3568 : SHPSetFastModeReadObject(hSHP, TRUE);
63 3901 : return hSHP;
64 : }
65 :
66 : /************************************************************************/
67 : /* DS_DBFOpen() */
68 : /************************************************************************/
69 :
70 3901 : DBFHandle OGRShapeDataSource::DS_DBFOpen(const char *pszDBFFile,
71 : const char *pszAccess)
72 : {
73 : DBFHandle hDBF =
74 3901 : DBFOpenLL(pszDBFFile, pszAccess,
75 3901 : const_cast<SAHooks *>(VSI_SHP_GetHook(m_b2GBLimit)));
76 3901 : return hDBF;
77 : }
78 :
79 : /************************************************************************/
80 : /* OGRShapeDataSource() */
81 : /************************************************************************/
82 :
83 3189 : OGRShapeDataSource::OGRShapeDataSource()
84 : : m_poPool(std::make_unique<OGRLayerPool>()),
85 3189 : m_b2GBLimit(CPLTestBool(CPLGetConfigOption("SHAPE_2GB_LIMIT", "FALSE")))
86 : {
87 3189 : }
88 :
89 : /************************************************************************/
90 : /* GetLayerNames() */
91 : /************************************************************************/
92 :
93 6 : std::vector<CPLString> OGRShapeDataSource::GetLayerNames() const
94 : {
95 6 : std::vector<CPLString> res;
96 6 : const_cast<OGRShapeDataSource *>(this)->GetLayerCount();
97 12 : for (const auto &poLayer : m_apoLayers)
98 : {
99 6 : res.emplace_back(poLayer->GetName());
100 : }
101 6 : return res;
102 : }
103 :
104 : /************************************************************************/
105 : /* ~OGRShapeDataSource() */
106 : /************************************************************************/
107 :
108 6364 : OGRShapeDataSource::~OGRShapeDataSource()
109 :
110 : {
111 6364 : std::vector<CPLString> layerNames;
112 3182 : if (!m_osTemporaryUnzipDir.empty())
113 : {
114 6 : layerNames = GetLayerNames();
115 : }
116 3182 : m_apoLayers.clear();
117 3182 : m_poPool.reset();
118 :
119 3182 : RecompressIfNeeded(layerNames);
120 3182 : RemoveLockFile();
121 :
122 : // Free mutex & cond
123 3182 : if (m_poRefreshLockFileMutex)
124 : {
125 0 : CPLDestroyMutex(m_poRefreshLockFileMutex);
126 0 : m_poRefreshLockFileMutex = nullptr;
127 : }
128 3182 : if (m_poRefreshLockFileCond)
129 : {
130 0 : CPLDestroyCond(m_poRefreshLockFileCond);
131 0 : m_poRefreshLockFileCond = nullptr;
132 : }
133 6364 : }
134 :
135 : /************************************************************************/
136 : /* OpenZip() */
137 : /************************************************************************/
138 :
139 8 : bool OGRShapeDataSource::OpenZip(GDALOpenInfo *poOpenInfo,
140 : const char *pszOriFilename)
141 : {
142 8 : if (!Open(poOpenInfo, true))
143 0 : return false;
144 :
145 8 : SetDescription(pszOriFilename);
146 :
147 8 : m_bIsZip = true;
148 8 : m_bSingleLayerZip =
149 8 : EQUAL(CPLGetExtensionSafe(pszOriFilename).c_str(), "shz");
150 :
151 8 : if (!m_bSingleLayerZip)
152 : {
153 2 : CPLString osLockFile(GetDescription());
154 1 : osLockFile += ".gdal.lock";
155 : VSIStatBufL sStat;
156 1 : if (VSIStatL(osLockFile, &sStat) == 0 &&
157 0 : sStat.st_mtime < time(nullptr) - 2 * knREFRESH_LOCK_FILE_DELAY_SEC)
158 : {
159 0 : CPLDebug("Shape", "Deleting stalled %s", osLockFile.c_str());
160 0 : VSIUnlink(osLockFile);
161 : }
162 : }
163 :
164 8 : return true;
165 : }
166 :
167 : /************************************************************************/
168 : /* CreateZip() */
169 : /************************************************************************/
170 :
171 4 : bool OGRShapeDataSource::CreateZip(const char *pszOriFilename)
172 : {
173 4 : CPLAssert(m_apoLayers.empty());
174 :
175 4 : void *hZIP = CPLCreateZip(pszOriFilename, nullptr);
176 4 : if (!hZIP)
177 1 : return false;
178 3 : if (CPLCloseZip(hZIP) != CE_None)
179 0 : return false;
180 3 : eAccess = GA_Update;
181 3 : m_bIsZip = true;
182 3 : m_bSingleLayerZip =
183 3 : EQUAL(CPLGetExtensionSafe(pszOriFilename).c_str(), "shz");
184 3 : return true;
185 : }
186 :
187 : /************************************************************************/
188 : /* Open() */
189 : /************************************************************************/
190 :
191 3185 : bool OGRShapeDataSource::Open(GDALOpenInfo *poOpenInfo, bool bTestOpen,
192 : bool bForceSingleFileDataSource)
193 :
194 : {
195 3185 : CPLAssert(m_apoLayers.empty());
196 :
197 3185 : const char *pszNewName = poOpenInfo->pszFilename;
198 3185 : const bool bUpdate = poOpenInfo->eAccess == GA_Update;
199 3185 : CPLAssert(papszOpenOptions == nullptr);
200 3185 : papszOpenOptions = CSLDuplicate(poOpenInfo->papszOpenOptions);
201 :
202 3185 : eAccess = poOpenInfo->eAccess;
203 :
204 3185 : m_bSingleFileDataSource = CPL_TO_BOOL(bForceSingleFileDataSource);
205 :
206 : /* -------------------------------------------------------------------- */
207 : /* If m_bSingleFileDataSource is TRUE we don't try to do anything */
208 : /* else. */
209 : /* This is only utilized when the OGRShapeDriver::Create() */
210 : /* method wants to create a stub OGRShapeDataSource for a */
211 : /* single shapefile. The driver will take care of creating the */
212 : /* file by calling ICreateLayer(). */
213 : /* -------------------------------------------------------------------- */
214 3185 : if (m_bSingleFileDataSource)
215 483 : return true;
216 :
217 : /* -------------------------------------------------------------------- */
218 : /* Is the given path a directory or a regular file? */
219 : /* -------------------------------------------------------------------- */
220 2702 : if (!poOpenInfo->bStatOK)
221 : {
222 0 : if (!bTestOpen)
223 0 : CPLError(CE_Failure, CPLE_AppDefined,
224 : "%s is neither a file or directory, Shape access failed.",
225 : pszNewName);
226 :
227 0 : return false;
228 : }
229 :
230 : /* -------------------------------------------------------------------- */
231 : /* Build a list of filenames we figure are Shape files. */
232 : /* -------------------------------------------------------------------- */
233 2702 : if (!poOpenInfo->bIsDirectory)
234 : {
235 1545 : if (!OpenFile(pszNewName, bUpdate))
236 : {
237 1 : if (!bTestOpen)
238 0 : CPLError(CE_Failure, CPLE_OpenFailed,
239 : "Failed to open shapefile %s. "
240 : "It may be corrupt or read-only file accessed in "
241 : "update mode.",
242 : pszNewName);
243 :
244 1 : return false;
245 : }
246 :
247 1544 : m_bSingleFileDataSource = true;
248 :
249 1544 : return true;
250 : }
251 : else
252 : {
253 2314 : const CPLStringList aosCandidates(VSIReadDir(pszNewName));
254 1157 : const int nCandidateCount = aosCandidates.size();
255 1157 : bool bMightBeOldCoverage = false;
256 1157 : std::set<CPLString> osLayerNameSet;
257 :
258 49798 : for (int iCan = 0; iCan < nCandidateCount; iCan++)
259 : {
260 48641 : const char *pszCandidate = aosCandidates[iCan];
261 48641 : CPLString osLayerName(CPLGetBasenameSafe(pszCandidate));
262 : #ifdef _WIN32
263 : // On Windows, as filenames are case insensitive, a shapefile layer
264 : // can be made of foo.shp and FOO.DBF, so to detect unique layer
265 : // names, put them upper case in the unique set used for detection.
266 : osLayerName.toupper();
267 : #endif
268 :
269 48641 : if (EQUAL(pszCandidate, "ARC"))
270 0 : bMightBeOldCoverage = true;
271 :
272 48641 : if (strlen(pszCandidate) < 4 ||
273 42914 : !EQUAL(pszCandidate + strlen(pszCandidate) - 4, ".shp"))
274 44914 : continue;
275 :
276 : std::string osFilename =
277 3727 : CPLFormFilenameSafe(pszNewName, pszCandidate, nullptr);
278 :
279 3727 : osLayerNameSet.insert(std::move(osLayerName));
280 : #ifdef IMMEDIATE_OPENING
281 : if (!OpenFile(osFilename.c_str(), bUpdate) && !bTestOpen)
282 : {
283 : CPLError(CE_Failure, CPLE_OpenFailed,
284 : "Failed to open shapefile %s. "
285 : "It may be corrupt or read-only file accessed in "
286 : "update mode.",
287 : osFilename.c_str());
288 : return false;
289 : }
290 : #else
291 3727 : m_oVectorLayerName.push_back(std::move(osFilename));
292 : #endif
293 : }
294 :
295 : // Try and .dbf files without apparent associated shapefiles.
296 49798 : for (int iCan = 0; iCan < nCandidateCount; iCan++)
297 : {
298 48641 : const char *pszCandidate = aosCandidates[iCan];
299 48641 : const std::string osLayerNameOri = CPLGetBasenameSafe(pszCandidate);
300 48641 : CPLString osLayerName(osLayerNameOri);
301 : #ifdef _WIN32
302 : osLayerName.toupper();
303 : #endif
304 :
305 : // We don't consume .dbf files in a directory that looks like
306 : // an old style Arc/Info (for PC?) that unless we found at least
307 : // some shapefiles. See Bug 493.
308 48641 : if (bMightBeOldCoverage && osLayerNameSet.empty())
309 0 : continue;
310 :
311 48641 : if (strlen(pszCandidate) < 4 ||
312 42914 : !EQUAL(pszCandidate + strlen(pszCandidate) - 4, ".dbf"))
313 44356 : continue;
314 :
315 4285 : if (osLayerNameSet.find(osLayerName) != osLayerNameSet.end())
316 3726 : continue;
317 :
318 : // We don't want to access .dbf files with an associated .tab
319 : // file, or it will never get recognised as a mapinfo dataset.
320 559 : bool bFoundTAB = false;
321 36463 : for (int iCan2 = 0; iCan2 < nCandidateCount; iCan2++)
322 : {
323 35904 : const char *pszCandidate2 = aosCandidates[iCan2];
324 :
325 35904 : if (EQUALN(pszCandidate2, osLayerNameOri.c_str(),
326 37194 : osLayerNameOri.size()) &&
327 1290 : EQUAL(pszCandidate2 + osLayerNameOri.size(), ".tab"))
328 0 : bFoundTAB = true;
329 : }
330 :
331 559 : if (bFoundTAB)
332 0 : continue;
333 :
334 : std::string osFilename =
335 559 : CPLFormFilenameSafe(pszNewName, pszCandidate, nullptr);
336 :
337 559 : osLayerNameSet.insert(std::move(osLayerName));
338 :
339 : #ifdef IMMEDIATE_OPENING
340 : if (!OpenFile(osFilename.c_str(), bUpdate) && !bTestOpen)
341 : {
342 : CPLError(CE_Failure, CPLE_OpenFailed,
343 : "Failed to open dbf file %s. "
344 : "It may be corrupt or read-only file accessed in "
345 : "update mode.",
346 : osFilename.c_str());
347 : return false;
348 : }
349 : #else
350 559 : m_oVectorLayerName.push_back(std::move(osFilename));
351 : #endif
352 : }
353 :
354 : #ifdef IMMEDIATE_OPENING
355 : const int nDirLayers = static_cast<int>(m_apoLayers.size());
356 : #else
357 1157 : const int nDirLayers = static_cast<int>(m_oVectorLayerName.size());
358 : #endif
359 :
360 1157 : CPLErrorReset();
361 :
362 1157 : return nDirLayers > 0 || !bTestOpen;
363 : }
364 : }
365 :
366 : /************************************************************************/
367 : /* OpenFile() */
368 : /************************************************************************/
369 :
370 3867 : bool OGRShapeDataSource::OpenFile(const char *pszNewName, bool bUpdate)
371 :
372 : {
373 7734 : const std::string osExtension = CPLGetExtensionSafe(pszNewName);
374 :
375 3867 : if (!EQUAL(osExtension.c_str(), "shp") &&
376 4218 : !EQUAL(osExtension.c_str(), "shx") &&
377 351 : !EQUAL(osExtension.c_str(), "dbf"))
378 0 : return false;
379 :
380 : /* -------------------------------------------------------------------- */
381 : /* SHPOpen() should include better (CPL based) error reporting, */
382 : /* and we should be trying to distinguish at this point whether */
383 : /* failure is a result of trying to open a non-shapefile, or */
384 : /* whether it was a shapefile and we want to report the error */
385 : /* up. */
386 : /* */
387 : /* Care is taken to suppress the error and only reissue it if */
388 : /* we think it is appropriate. */
389 : /* -------------------------------------------------------------------- */
390 : const bool bRealUpdateAccess =
391 3867 : bUpdate && (!IsZip() || !GetTemporaryUnzipDir().empty());
392 3867 : CPLErrorReset();
393 3867 : CPLPushErrorHandler(CPLQuietErrorHandler);
394 3867 : SHPHandle hSHP = bRealUpdateAccess ? DS_SHPOpen(pszNewName, "r+")
395 2213 : : DS_SHPOpen(pszNewName, "r");
396 3867 : CPLPopErrorHandler();
397 :
398 : const bool bRestoreSHX =
399 3867 : CPLTestBool(CPLGetConfigOption("SHAPE_RESTORE_SHX", "FALSE"));
400 3868 : if (bRestoreSHX && EQUAL(CPLGetExtensionSafe(pszNewName).c_str(), "dbf") &&
401 1 : CPLGetLastErrorMsg()[0] != '\0')
402 : {
403 2 : CPLString osMsg = CPLGetLastErrorMsg();
404 :
405 1 : CPLError(CE_Warning, CPLE_AppDefined, "%s", osMsg.c_str());
406 : }
407 : else
408 : {
409 4198 : if (hSHP == nullptr &&
410 4198 : (!EQUAL(CPLGetExtensionSafe(pszNewName).c_str(), "dbf") ||
411 331 : strstr(CPLGetLastErrorMsg(), ".shp") == nullptr))
412 : {
413 1 : CPLString osMsg = CPLGetLastErrorMsg();
414 :
415 1 : CPLError(CE_Failure, CPLE_OpenFailed, "%s", osMsg.c_str());
416 :
417 1 : return false;
418 : }
419 3865 : CPLErrorReset();
420 : }
421 :
422 : /* -------------------------------------------------------------------- */
423 : /* Open the .dbf file, if it exists. To open a dbf file, the */
424 : /* filename has to either refer to a successfully opened shp */
425 : /* file or has to refer to the actual .dbf file. */
426 : /* -------------------------------------------------------------------- */
427 3866 : DBFHandle hDBF = nullptr;
428 4198 : if (hSHP != nullptr ||
429 4198 : EQUAL(CPLGetExtensionSafe(pszNewName).c_str(), "dbf"))
430 : {
431 3866 : if (bRealUpdateAccess)
432 : {
433 1654 : hDBF = DS_DBFOpen(pszNewName, "r+");
434 1654 : if (hSHP != nullptr && hDBF == nullptr)
435 : {
436 22 : for (int i = 0; i < 2; i++)
437 : {
438 : VSIStatBufL sStat;
439 : const std::string osDBFName = CPLResetExtensionSafe(
440 15 : pszNewName, (i == 0) ? "dbf" : "DBF");
441 15 : VSILFILE *fp = nullptr;
442 15 : if (VSIStatExL(osDBFName.c_str(), &sStat,
443 15 : VSI_STAT_EXISTS_FLAG) == 0)
444 : {
445 1 : fp = VSIFOpenL(osDBFName.c_str(), "r+");
446 1 : if (fp == nullptr)
447 : {
448 1 : CPLError(CE_Failure, CPLE_OpenFailed,
449 : "%s exists, "
450 : "but cannot be opened in update mode",
451 : osDBFName.c_str());
452 1 : SHPClose(hSHP);
453 1 : return false;
454 : }
455 0 : VSIFCloseL(fp);
456 0 : break;
457 : }
458 : }
459 : }
460 : }
461 : else
462 : {
463 2212 : hDBF = DS_DBFOpen(pszNewName, "r");
464 : }
465 : }
466 : else
467 : {
468 0 : hDBF = nullptr;
469 : }
470 :
471 3865 : if (hDBF == nullptr && hSHP == nullptr)
472 0 : return false;
473 :
474 : /* -------------------------------------------------------------------- */
475 : /* Create the layer object. */
476 : /* -------------------------------------------------------------------- */
477 : auto poLayer = std::make_unique<OGRShapeLayer>(
478 : this, pszNewName, hSHP, hDBF,
479 0 : /* poSRS = */ nullptr,
480 0 : /* bSRSSet = */ false,
481 3865 : /* osPrjFilename = */ std::string(), bUpdate, wkbNone);
482 7730 : poLayer->SetModificationDate(
483 3865 : CSLFetchNameValue(papszOpenOptions, "DBF_DATE_LAST_UPDATE"));
484 3865 : poLayer->SetAutoRepack(CPLFetchBool(papszOpenOptions, "AUTO_REPACK", true));
485 3865 : poLayer->SetWriteDBFEOFChar(
486 3865 : CPLFetchBool(papszOpenOptions, "DBF_EOF_CHAR", true));
487 :
488 : /* -------------------------------------------------------------------- */
489 : /* Add layer to data source layer list. */
490 : /* -------------------------------------------------------------------- */
491 3865 : AddLayer(std::move(poLayer));
492 :
493 3865 : return true;
494 : }
495 :
496 : /************************************************************************/
497 : /* AddLayer() */
498 : /************************************************************************/
499 :
500 5523 : void OGRShapeDataSource::AddLayer(std::unique_ptr<OGRShapeLayer> poLayer)
501 : {
502 5523 : m_apoLayers.push_back(std::move(poLayer));
503 :
504 : // If we reach the limit, then register all the already opened layers
505 : // Technically this code would not be necessary if there was not the
506 : // following initial test in SetLastUsedLayer() :
507 : // if (static_cast<int>(m_apoLayers.size()) < MAX_SIMULTANEOUSLY_OPENED_LAYERS)
508 : // return;
509 5523 : if (static_cast<int>(m_apoLayers.size()) ==
510 5528 : m_poPool->GetMaxSimultaneouslyOpened() &&
511 5 : m_poPool->GetSize() == 0)
512 : {
513 505 : for (auto &poIterLayer : m_apoLayers)
514 500 : m_poPool->SetLastUsedLayer(poIterLayer.get());
515 : }
516 5523 : }
517 :
518 : /************************************************************************/
519 : /* LaunderLayerName() */
520 : /************************************************************************/
521 :
522 1182 : static CPLString LaunderLayerName(const char *pszLayerName)
523 : {
524 2364 : std::string osRet(CPLLaunderForFilenameSafe(pszLayerName, nullptr));
525 1182 : if (osRet != pszLayerName)
526 : {
527 1 : CPLError(CE_Warning, CPLE_AppDefined,
528 : "Invalid layer name for a shapefile: %s. Laundered to %s.",
529 : pszLayerName, osRet.c_str());
530 : }
531 2364 : return osRet;
532 : }
533 :
534 : /************************************************************************/
535 : /* ICreateLayer() */
536 : /************************************************************************/
537 :
538 : OGRLayer *
539 1670 : OGRShapeDataSource::ICreateLayer(const char *pszLayerName,
540 : const OGRGeomFieldDefn *poGeomFieldDefn,
541 : CSLConstList papszOptions)
542 :
543 : {
544 : // To ensure that existing layers are created.
545 1670 : GetLayerCount();
546 :
547 1670 : auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
548 : const auto poSRS =
549 1670 : poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
550 :
551 : /* -------------------------------------------------------------------- */
552 : /* Check that the layer doesn't already exist. */
553 : /* -------------------------------------------------------------------- */
554 1670 : if (GetLayerByName(pszLayerName) != nullptr)
555 : {
556 2 : CPLError(CE_Failure, CPLE_AppDefined, "Layer '%s' already exists",
557 : pszLayerName);
558 2 : return nullptr;
559 : }
560 :
561 : /* -------------------------------------------------------------------- */
562 : /* Verify we are in update mode. */
563 : /* -------------------------------------------------------------------- */
564 1668 : if (eAccess == GA_ReadOnly)
565 : {
566 0 : CPLError(CE_Failure, CPLE_NoWriteAccess,
567 : "Data source %s opened read-only. "
568 : "New layer %s cannot be created.",
569 0 : GetDescription(), pszLayerName);
570 :
571 0 : return nullptr;
572 : }
573 :
574 1668 : if (m_bIsZip && m_bSingleLayerZip && m_apoLayers.size() == 1)
575 : {
576 1 : CPLError(CE_Failure, CPLE_NotSupported,
577 : ".shz only supports one single layer");
578 1 : return nullptr;
579 : }
580 :
581 1667 : if (!UncompressIfNeeded())
582 0 : return nullptr;
583 :
584 : /* -------------------------------------------------------------------- */
585 : /* Figure out what type of layer we need. */
586 : /* -------------------------------------------------------------------- */
587 1667 : int nShapeType = -1;
588 :
589 1667 : if (wkbFlatten(eType) == wkbUnknown || eType == wkbLineString)
590 1292 : nShapeType = SHPT_ARC;
591 375 : else if (eType == wkbPoint)
592 42 : nShapeType = SHPT_POINT;
593 333 : else if (eType == wkbPolygon || eType == wkbTriangle)
594 147 : nShapeType = SHPT_POLYGON;
595 186 : else if (eType == wkbMultiPoint)
596 7 : nShapeType = SHPT_MULTIPOINT;
597 179 : else if (eType == wkbPoint25D)
598 6 : nShapeType = SHPT_POINTZ;
599 173 : else if (eType == wkbPointM)
600 2 : nShapeType = SHPT_POINTM;
601 171 : else if (eType == wkbPointZM)
602 2 : nShapeType = SHPT_POINTZ;
603 169 : else if (eType == wkbLineString25D)
604 12 : nShapeType = SHPT_ARCZ;
605 157 : else if (eType == wkbLineStringM)
606 2 : nShapeType = SHPT_ARCM;
607 155 : else if (eType == wkbLineStringZM)
608 2 : nShapeType = SHPT_ARCZ;
609 153 : else if (eType == wkbMultiLineString)
610 12 : nShapeType = SHPT_ARC;
611 141 : else if (eType == wkbMultiLineString25D)
612 5 : nShapeType = SHPT_ARCZ;
613 136 : else if (eType == wkbMultiLineStringM)
614 1 : nShapeType = SHPT_ARCM;
615 135 : else if (eType == wkbMultiLineStringZM)
616 1 : nShapeType = SHPT_ARCZ;
617 134 : else if (eType == wkbPolygon25D || eType == wkbTriangleZ)
618 12 : nShapeType = SHPT_POLYGONZ;
619 122 : else if (eType == wkbPolygonM || eType == wkbTriangleM)
620 2 : nShapeType = SHPT_POLYGONM;
621 120 : else if (eType == wkbPolygonZM || eType == wkbTriangleZM)
622 2 : nShapeType = SHPT_POLYGONZ;
623 118 : else if (eType == wkbMultiPolygon)
624 43 : nShapeType = SHPT_POLYGON;
625 75 : else if (eType == wkbMultiPolygon25D)
626 5 : nShapeType = SHPT_POLYGONZ;
627 70 : else if (eType == wkbMultiPolygonM)
628 1 : nShapeType = SHPT_POLYGONM;
629 69 : else if (eType == wkbMultiPolygonZM)
630 1 : nShapeType = SHPT_POLYGONZ;
631 68 : else if (eType == wkbMultiPoint25D)
632 6 : nShapeType = SHPT_MULTIPOINTZ;
633 62 : else if (eType == wkbMultiPointM)
634 2 : nShapeType = SHPT_MULTIPOINTM;
635 60 : else if (eType == wkbMultiPointZM)
636 2 : nShapeType = SHPT_MULTIPOINTZ;
637 112 : else if (wkbFlatten(eType) == wkbTIN ||
638 54 : wkbFlatten(eType) == wkbPolyhedralSurface)
639 4 : nShapeType = SHPT_MULTIPATCH;
640 54 : else if (eType == wkbNone)
641 50 : nShapeType = SHPT_NULL;
642 :
643 : /* -------------------------------------------------------------------- */
644 : /* Has the application overridden this with a special creation */
645 : /* option? */
646 : /* -------------------------------------------------------------------- */
647 1667 : const char *pszOverride = CSLFetchNameValue(papszOptions, "SHPT");
648 :
649 1667 : if (pszOverride == nullptr)
650 : {
651 : /* ignore */;
652 : }
653 28 : else if (EQUAL(pszOverride, "POINT"))
654 : {
655 1 : nShapeType = SHPT_POINT;
656 1 : eType = wkbPoint;
657 : }
658 27 : else if (EQUAL(pszOverride, "ARC"))
659 : {
660 2 : nShapeType = SHPT_ARC;
661 2 : eType = wkbLineString;
662 : }
663 25 : else if (EQUAL(pszOverride, "POLYGON"))
664 : {
665 2 : nShapeType = SHPT_POLYGON;
666 2 : eType = wkbPolygon;
667 : }
668 23 : else if (EQUAL(pszOverride, "MULTIPOINT"))
669 : {
670 1 : nShapeType = SHPT_MULTIPOINT;
671 1 : eType = wkbMultiPoint;
672 : }
673 22 : else if (EQUAL(pszOverride, "POINTZ"))
674 : {
675 1 : nShapeType = SHPT_POINTZ;
676 1 : eType = wkbPoint25D;
677 : }
678 21 : else if (EQUAL(pszOverride, "ARCZ"))
679 : {
680 2 : nShapeType = SHPT_ARCZ;
681 2 : eType = wkbLineString25D;
682 : }
683 19 : else if (EQUAL(pszOverride, "POLYGONZ"))
684 : {
685 4 : nShapeType = SHPT_POLYGONZ;
686 4 : eType = wkbPolygon25D;
687 : }
688 15 : else if (EQUAL(pszOverride, "MULTIPOINTZ"))
689 : {
690 1 : nShapeType = SHPT_MULTIPOINTZ;
691 1 : eType = wkbMultiPoint25D;
692 : }
693 14 : else if (EQUAL(pszOverride, "POINTM"))
694 : {
695 1 : nShapeType = SHPT_POINTM;
696 1 : eType = wkbPointM;
697 : }
698 13 : else if (EQUAL(pszOverride, "ARCM"))
699 : {
700 2 : nShapeType = SHPT_ARCM;
701 2 : eType = wkbLineStringM;
702 : }
703 11 : else if (EQUAL(pszOverride, "POLYGONM"))
704 : {
705 2 : nShapeType = SHPT_POLYGONM;
706 2 : eType = wkbPolygonM;
707 : }
708 9 : else if (EQUAL(pszOverride, "MULTIPOINTM"))
709 : {
710 1 : nShapeType = SHPT_MULTIPOINTM;
711 1 : eType = wkbMultiPointM;
712 : }
713 8 : else if (EQUAL(pszOverride, "POINTZM"))
714 : {
715 1 : nShapeType = SHPT_POINTZ;
716 1 : eType = wkbPointZM;
717 : }
718 7 : else if (EQUAL(pszOverride, "ARCZM"))
719 : {
720 2 : nShapeType = SHPT_ARCZ;
721 2 : eType = wkbLineStringZM;
722 : }
723 5 : else if (EQUAL(pszOverride, "POLYGONZM"))
724 : {
725 2 : nShapeType = SHPT_POLYGONZ;
726 2 : eType = wkbPolygonZM;
727 : }
728 3 : else if (EQUAL(pszOverride, "MULTIPOINTZM"))
729 : {
730 1 : nShapeType = SHPT_MULTIPOINTZ;
731 1 : eType = wkbMultiPointZM;
732 : }
733 2 : else if (EQUAL(pszOverride, "MULTIPATCH"))
734 : {
735 2 : nShapeType = SHPT_MULTIPATCH;
736 2 : eType = wkbUnknown; // not ideal...
737 : }
738 0 : else if (EQUAL(pszOverride, "NONE") || EQUAL(pszOverride, "NULL"))
739 : {
740 0 : nShapeType = SHPT_NULL;
741 0 : eType = wkbNone;
742 : }
743 : else
744 : {
745 0 : CPLError(CE_Failure, CPLE_NotSupported,
746 : "Unknown SHPT value of `%s' passed to Shapefile layer"
747 : "creation. Creation aborted.",
748 : pszOverride);
749 :
750 0 : return nullptr;
751 : }
752 :
753 1667 : if (nShapeType == -1)
754 : {
755 4 : CPLError(CE_Failure, CPLE_NotSupported,
756 : "Geometry type of `%s' not supported in shapefiles. "
757 : "Type can be overridden with a layer creation option "
758 : "of SHPT=POINT/ARC/POLYGON/MULTIPOINT/POINTZ/ARCZ/POLYGONZ/"
759 : "MULTIPOINTZ/MULTIPATCH.",
760 : OGRGeometryTypeToName(eType));
761 4 : return nullptr;
762 : }
763 :
764 : /* -------------------------------------------------------------------- */
765 : /* What filename do we use, excluding the extension? */
766 : /* -------------------------------------------------------------------- */
767 3326 : std::string osFilenameWithoutExt;
768 :
769 1663 : if (m_bSingleFileDataSource && m_apoLayers.empty())
770 : {
771 962 : const std::string osPath = CPLGetPathSafe(GetDescription());
772 481 : const std::string osFBasename = CPLGetBasenameSafe(GetDescription());
773 :
774 : osFilenameWithoutExt =
775 481 : CPLFormFilenameSafe(osPath.c_str(), osFBasename.c_str(), nullptr);
776 : }
777 1182 : else if (m_bSingleFileDataSource)
778 : {
779 : // This is a very weird use case : the user creates/open a datasource
780 : // made of a single shapefile 'foo.shp' and wants to add a new layer
781 : // to it, 'bar'. So we create a new shapefile 'bar.shp' in the same
782 : // directory as 'foo.shp'
783 : // So technically, we will not be any longer a single file
784 : // datasource ... Ahem ahem.
785 16 : const std::string osPath = CPLGetPathSafe(GetDescription());
786 32 : osFilenameWithoutExt = CPLFormFilenameSafe(
787 48 : osPath.c_str(), LaunderLayerName(pszLayerName).c_str(), nullptr);
788 : }
789 : else
790 : {
791 1166 : const std::string osDir(m_osTemporaryUnzipDir.empty()
792 1163 : ? std::string(GetDescription())
793 1166 : : m_osTemporaryUnzipDir);
794 2332 : osFilenameWithoutExt = CPLFormFilenameSafe(
795 3498 : osDir.c_str(), LaunderLayerName(pszLayerName).c_str(), nullptr);
796 : }
797 :
798 : /* -------------------------------------------------------------------- */
799 : /* Create the shapefile. */
800 : /* -------------------------------------------------------------------- */
801 : const bool l_b2GBLimit =
802 1663 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "2GB_LIMIT", "FALSE"));
803 :
804 1663 : SHPHandle hSHP = nullptr;
805 :
806 1663 : if (nShapeType != SHPT_NULL)
807 : {
808 : const std::string osFilename =
809 1613 : CPLFormFilenameSafe(nullptr, osFilenameWithoutExt.c_str(), "shp");
810 :
811 1613 : hSHP = SHPCreateLL(osFilename.c_str(), nShapeType,
812 : const_cast<SAHooks *>(VSI_SHP_GetHook(l_b2GBLimit)));
813 :
814 1613 : if (hSHP == nullptr)
815 : {
816 4 : return nullptr;
817 : }
818 :
819 1609 : SHPSetFastModeReadObject(hSHP, TRUE);
820 : }
821 :
822 : /* -------------------------------------------------------------------- */
823 : /* Has a specific LDID been specified by the caller? */
824 : /* -------------------------------------------------------------------- */
825 1659 : const char *pszLDID = CSLFetchNameValue(papszOptions, "ENCODING");
826 :
827 : /* -------------------------------------------------------------------- */
828 : /* Create a DBF file. */
829 : /* -------------------------------------------------------------------- */
830 : const std::string osDBFFilename =
831 3318 : CPLFormFilenameSafe(nullptr, osFilenameWithoutExt.c_str(), "dbf");
832 :
833 1659 : DBFHandle hDBF = DBFCreateLL(
834 : osDBFFilename.c_str(), (pszLDID != nullptr) ? pszLDID : "LDID/87",
835 1659 : const_cast<SAHooks *>(VSI_SHP_GetHook(m_b2GBLimit)));
836 :
837 1659 : if (hDBF == nullptr)
838 : {
839 1 : CPLError(CE_Failure, CPLE_OpenFailed,
840 : "Failed to create Shape DBF file `%s'.",
841 : osDBFFilename.c_str());
842 1 : SHPClose(hSHP);
843 1 : return nullptr;
844 : }
845 :
846 : /* -------------------------------------------------------------------- */
847 : /* Create the .prj file, if required. */
848 : /* -------------------------------------------------------------------- */
849 3316 : std::string osPrjFilename;
850 1658 : OGRSpatialReference *poSRSClone = nullptr;
851 1658 : if (poSRS != nullptr)
852 : {
853 : osPrjFilename =
854 213 : CPLFormFilenameSafe(nullptr, osFilenameWithoutExt.c_str(), "prj");
855 213 : poSRSClone = poSRS->Clone();
856 :
857 213 : char *pszWKT = nullptr;
858 213 : VSILFILE *fp = nullptr;
859 213 : const char *const apszOptions[] = {"FORMAT=WKT1_ESRI", nullptr};
860 422 : if (poSRSClone->exportToWkt(&pszWKT, apszOptions) == OGRERR_NONE &&
861 209 : (fp = VSIFOpenL(osPrjFilename.c_str(), "wt")) != nullptr)
862 : {
863 209 : VSIFWriteL(pszWKT, strlen(pszWKT), 1, fp);
864 209 : VSIFCloseL(fp);
865 : }
866 :
867 213 : CPLFree(pszWKT);
868 : }
869 :
870 : /* -------------------------------------------------------------------- */
871 : /* Create the layer object. */
872 : /* -------------------------------------------------------------------- */
873 : // OGRShapeLayer constructor expects a filename with an extension (that
874 : // could be random actually), otherwise this is going to cause problems with
875 : // layer names that have a dot (not speaking about the one before the shp)
876 : const std::string osSHPFilename =
877 3316 : CPLFormFilenameSafe(nullptr, osFilenameWithoutExt.c_str(), "shp");
878 :
879 : auto poLayer = std::make_unique<OGRShapeLayer>(
880 0 : this, osSHPFilename.c_str(), hSHP, hDBF, poSRSClone,
881 0 : /* bSRSSet = */ true, osPrjFilename,
882 3316 : /* bUpdate = */ true, eType);
883 1658 : if (poSRSClone != nullptr)
884 : {
885 213 : poSRSClone->Release();
886 : }
887 :
888 1658 : poLayer->SetResizeAtClose(CPLFetchBool(papszOptions, "RESIZE", false));
889 1658 : poLayer->CreateSpatialIndexAtClose(
890 1658 : CPLFetchBool(papszOptions, "SPATIAL_INDEX", false));
891 1658 : poLayer->SetModificationDate(
892 : CSLFetchNameValue(papszOptions, "DBF_DATE_LAST_UPDATE"));
893 1658 : poLayer->SetAutoRepack(CPLFetchBool(papszOptions, "AUTO_REPACK", true));
894 1658 : poLayer->SetWriteDBFEOFChar(
895 1658 : CPLFetchBool(papszOptions, "DBF_EOF_CHAR", true));
896 :
897 : /* -------------------------------------------------------------------- */
898 : /* Add layer to data source layer list. */
899 : /* -------------------------------------------------------------------- */
900 1658 : AddLayer(std::move(poLayer));
901 :
902 1658 : return m_apoLayers.back().get();
903 : }
904 :
905 : /************************************************************************/
906 : /* TestCapability() */
907 : /************************************************************************/
908 :
909 734 : int OGRShapeDataSource::TestCapability(const char *pszCap)
910 :
911 : {
912 734 : if (EQUAL(pszCap, ODsCCreateLayer))
913 356 : return eAccess == GA_Update &&
914 356 : !(m_bIsZip && m_bSingleLayerZip && m_apoLayers.size() == 1);
915 556 : else if (EQUAL(pszCap, ODsCDeleteLayer))
916 18 : return eAccess == GA_Update && !(m_bIsZip && m_bSingleLayerZip);
917 538 : else if (EQUAL(pszCap, ODsCMeasuredGeometries))
918 14 : return true;
919 524 : else if (EQUAL(pszCap, ODsCZGeometries))
920 14 : return true;
921 510 : else if (EQUAL(pszCap, ODsCRandomLayerWrite))
922 6 : return eAccess == GA_Update;
923 :
924 504 : return false;
925 : }
926 :
927 : /************************************************************************/
928 : /* GetLayerCount() */
929 : /************************************************************************/
930 :
931 1094970 : int OGRShapeDataSource::GetLayerCount()
932 :
933 : {
934 : #ifndef IMMEDIATE_OPENING
935 1094970 : if (!m_oVectorLayerName.empty())
936 : {
937 1601 : for (size_t i = 0; i < m_oVectorLayerName.size(); i++)
938 : {
939 1495 : const char *pszFilename = m_oVectorLayerName[i].c_str();
940 1495 : const std::string osLayerName = CPLGetBasenameSafe(pszFilename);
941 :
942 1495 : bool bFound = false;
943 253614 : for (auto &poLayer : m_apoLayers)
944 : {
945 252627 : if (poLayer->GetName() == osLayerName)
946 : {
947 508 : bFound = true;
948 508 : break;
949 : }
950 : }
951 1495 : if (bFound)
952 508 : continue;
953 :
954 987 : if (!OpenFile(pszFilename, eAccess == GA_Update))
955 : {
956 0 : CPLError(CE_Failure, CPLE_OpenFailed,
957 : "Failed to open file %s."
958 : "It may be corrupt or read-only file accessed in "
959 : "update mode.",
960 : pszFilename);
961 : }
962 : }
963 106 : m_oVectorLayerName.resize(0);
964 : }
965 : #endif
966 :
967 1094970 : return static_cast<int>(m_apoLayers.size());
968 : }
969 :
970 : /************************************************************************/
971 : /* GetLayer() */
972 : /************************************************************************/
973 :
974 544790 : OGRLayer *OGRShapeDataSource::GetLayer(int iLayer)
975 :
976 : {
977 : // To ensure that existing layers are created.
978 544790 : GetLayerCount();
979 :
980 544790 : if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
981 14 : return nullptr;
982 :
983 544776 : return m_apoLayers[iLayer].get();
984 : }
985 :
986 : /************************************************************************/
987 : /* GetLayerByName() */
988 : /************************************************************************/
989 :
990 5142 : OGRLayer *OGRShapeDataSource::GetLayerByName(const char *pszLayerNameIn)
991 : {
992 : #ifndef IMMEDIATE_OPENING
993 5142 : if (!m_oVectorLayerName.empty())
994 : {
995 257268 : for (auto &poLayer : m_apoLayers)
996 : {
997 255928 : if (strcmp(poLayer->GetName(), pszLayerNameIn) == 0)
998 : {
999 1081 : return poLayer.get();
1000 : }
1001 : }
1002 :
1003 1440 : for (int j = 0; j < 2; j++)
1004 : {
1005 252166 : for (size_t i = 0; i < m_oVectorLayerName.size(); i++)
1006 : {
1007 252066 : const char *pszFilename = m_oVectorLayerName[i].c_str();
1008 252066 : const std::string osLayerName = CPLGetBasenameSafe(pszFilename);
1009 :
1010 252066 : if (j == 0)
1011 : {
1012 251956 : if (osLayerName != pszLayerNameIn)
1013 250711 : continue;
1014 : }
1015 : else
1016 : {
1017 110 : if (!EQUAL(osLayerName.c_str(), pszLayerNameIn))
1018 20 : continue;
1019 : }
1020 :
1021 1335 : if (!OpenFile(pszFilename, eAccess == GA_Update))
1022 : {
1023 1 : CPLError(CE_Failure, CPLE_OpenFailed,
1024 : "Failed to open file %s. "
1025 : "It may be corrupt or read-only file accessed in "
1026 : "update mode.",
1027 : pszFilename);
1028 1 : return nullptr;
1029 : }
1030 :
1031 1334 : return m_apoLayers.back().get();
1032 : }
1033 : }
1034 :
1035 5 : return nullptr;
1036 : }
1037 : #endif
1038 :
1039 2721 : return GDALDataset::GetLayerByName(pszLayerNameIn);
1040 : }
1041 :
1042 : /************************************************************************/
1043 : /* ExecuteSQL() */
1044 : /* */
1045 : /* We override this to provide special handling of CREATE */
1046 : /* SPATIAL INDEX commands. Support forms are: */
1047 : /* */
1048 : /* CREATE SPATIAL INDEX ON layer_name [DEPTH n] */
1049 : /* DROP SPATIAL INDEX ON layer_name */
1050 : /* REPACK layer_name */
1051 : /* RECOMPUTE EXTENT ON layer_name */
1052 : /************************************************************************/
1053 :
1054 1001 : OGRLayer *OGRShapeDataSource::ExecuteSQL(const char *pszStatement,
1055 : OGRGeometry *poSpatialFilter,
1056 : const char *pszDialect)
1057 :
1058 : {
1059 1001 : if (EQUAL(pszStatement, "UNCOMPRESS"))
1060 : {
1061 0 : CPL_IGNORE_RET_VAL(UncompressIfNeeded());
1062 0 : return nullptr;
1063 : }
1064 :
1065 1001 : if (EQUAL(pszStatement, "RECOMPRESS"))
1066 : {
1067 0 : RecompressIfNeeded(GetLayerNames());
1068 0 : return nullptr;
1069 : }
1070 : /* ==================================================================== */
1071 : /* Handle command to drop a spatial index. */
1072 : /* ==================================================================== */
1073 1001 : if (STARTS_WITH_CI(pszStatement, "REPACK "))
1074 : {
1075 : OGRShapeLayer *poLayer =
1076 17 : cpl::down_cast<OGRShapeLayer *>(GetLayerByName(pszStatement + 7));
1077 :
1078 17 : if (poLayer != nullptr)
1079 : {
1080 17 : if (poLayer->Repack() != OGRERR_NONE)
1081 : {
1082 1 : CPLError(CE_Failure, CPLE_AppDefined,
1083 : "REPACK of layer '%s' failed.", pszStatement + 7);
1084 : }
1085 : }
1086 : else
1087 : {
1088 0 : CPLError(CE_Failure, CPLE_AppDefined,
1089 : "No such layer as '%s' in REPACK.", pszStatement + 7);
1090 : }
1091 17 : return nullptr;
1092 : }
1093 :
1094 : /* ==================================================================== */
1095 : /* Handle command to shrink columns to their minimum size. */
1096 : /* ==================================================================== */
1097 984 : if (STARTS_WITH_CI(pszStatement, "RESIZE "))
1098 : {
1099 : OGRShapeLayer *poLayer =
1100 1 : cpl::down_cast<OGRShapeLayer *>(GetLayerByName(pszStatement + 7));
1101 :
1102 1 : if (poLayer != nullptr)
1103 : {
1104 1 : poLayer->ResizeDBF();
1105 : }
1106 : else
1107 : {
1108 0 : CPLError(CE_Failure, CPLE_AppDefined,
1109 : "No such layer as '%s' in RESIZE.", pszStatement + 7);
1110 : }
1111 1 : return nullptr;
1112 : }
1113 :
1114 : /* ==================================================================== */
1115 : /* Handle command to recompute extent */
1116 : /* ==================================================================== */
1117 983 : if (STARTS_WITH_CI(pszStatement, "RECOMPUTE EXTENT ON "))
1118 : {
1119 : OGRShapeLayer *poLayer =
1120 6 : cpl::down_cast<OGRShapeLayer *>(GetLayerByName(pszStatement + 20));
1121 :
1122 6 : if (poLayer != nullptr)
1123 : {
1124 5 : poLayer->RecomputeExtent();
1125 : }
1126 : else
1127 : {
1128 1 : CPLError(CE_Failure, CPLE_AppDefined,
1129 : "No such layer as '%s' in RECOMPUTE EXTENT.",
1130 : pszStatement + 20);
1131 : }
1132 6 : return nullptr;
1133 : }
1134 :
1135 : /* ==================================================================== */
1136 : /* Handle command to drop a spatial index. */
1137 : /* ==================================================================== */
1138 977 : if (STARTS_WITH_CI(pszStatement, "DROP SPATIAL INDEX ON "))
1139 : {
1140 : OGRShapeLayer *poLayer =
1141 3 : cpl::down_cast<OGRShapeLayer *>(GetLayerByName(pszStatement + 22));
1142 :
1143 3 : if (poLayer != nullptr)
1144 : {
1145 3 : poLayer->DropSpatialIndex();
1146 : }
1147 : else
1148 : {
1149 0 : CPLError(CE_Failure, CPLE_AppDefined,
1150 : "No such layer as '%s' in DROP SPATIAL INDEX.",
1151 : pszStatement + 22);
1152 : }
1153 3 : return nullptr;
1154 : }
1155 :
1156 : /* ==================================================================== */
1157 : /* Handle all commands except spatial index creation generically. */
1158 : /* ==================================================================== */
1159 974 : if (!STARTS_WITH_CI(pszStatement, "CREATE SPATIAL INDEX ON "))
1160 : {
1161 958 : char **papszTokens = CSLTokenizeString(pszStatement);
1162 958 : if (CSLCount(papszTokens) >= 4 &&
1163 454 : (EQUAL(papszTokens[0], "CREATE") ||
1164 426 : EQUAL(papszTokens[0], "DROP")) &&
1165 1412 : EQUAL(papszTokens[1], "INDEX") && EQUAL(papszTokens[2], "ON"))
1166 : {
1167 : OGRShapeLayer *poLayer =
1168 38 : cpl::down_cast<OGRShapeLayer *>(GetLayerByName(papszTokens[3]));
1169 38 : if (poLayer != nullptr)
1170 38 : poLayer->InitializeIndexSupport(poLayer->GetFullName());
1171 : }
1172 958 : CSLDestroy(papszTokens);
1173 :
1174 958 : return GDALDataset::ExecuteSQL(pszStatement, poSpatialFilter,
1175 958 : pszDialect);
1176 : }
1177 :
1178 : /* -------------------------------------------------------------------- */
1179 : /* Parse into keywords. */
1180 : /* -------------------------------------------------------------------- */
1181 16 : char **papszTokens = CSLTokenizeString(pszStatement);
1182 :
1183 32 : if (CSLCount(papszTokens) < 5 || !EQUAL(papszTokens[0], "CREATE") ||
1184 16 : !EQUAL(papszTokens[1], "SPATIAL") || !EQUAL(papszTokens[2], "INDEX") ||
1185 48 : !EQUAL(papszTokens[3], "ON") || CSLCount(papszTokens) > 7 ||
1186 16 : (CSLCount(papszTokens) == 7 && !EQUAL(papszTokens[5], "DEPTH")))
1187 : {
1188 0 : CSLDestroy(papszTokens);
1189 0 : CPLError(CE_Failure, CPLE_AppDefined,
1190 : "Syntax error in CREATE SPATIAL INDEX command.\n"
1191 : "Was '%s'\n"
1192 : "Should be of form 'CREATE SPATIAL INDEX ON <table> "
1193 : "[DEPTH <n>]'",
1194 : pszStatement);
1195 0 : return nullptr;
1196 : }
1197 :
1198 : /* -------------------------------------------------------------------- */
1199 : /* Get depth if provided. */
1200 : /* -------------------------------------------------------------------- */
1201 16 : const int nDepth = CSLCount(papszTokens) == 7 ? atoi(papszTokens[6]) : 0;
1202 :
1203 : /* -------------------------------------------------------------------- */
1204 : /* What layer are we operating on. */
1205 : /* -------------------------------------------------------------------- */
1206 : OGRShapeLayer *poLayer =
1207 16 : cpl::down_cast<OGRShapeLayer *>(GetLayerByName(papszTokens[4]));
1208 :
1209 16 : if (poLayer == nullptr)
1210 : {
1211 0 : CPLError(CE_Failure, CPLE_AppDefined, "Layer %s not recognised.",
1212 0 : papszTokens[4]);
1213 0 : CSLDestroy(papszTokens);
1214 0 : return nullptr;
1215 : }
1216 :
1217 16 : CSLDestroy(papszTokens);
1218 :
1219 16 : poLayer->CreateSpatialIndex(nDepth);
1220 16 : return nullptr;
1221 : }
1222 :
1223 : /************************************************************************/
1224 : /* GetExtensionsForDeletion() */
1225 : /************************************************************************/
1226 :
1227 694 : const char *const *OGRShapeDataSource::GetExtensionsForDeletion()
1228 : {
1229 : static const char *const apszExtensions[] = {
1230 : "shp", "shx", "dbf", "sbn", "sbx", "prj", "idm", "ind", "qix", "cpg",
1231 : "qpj", // QGIS projection file
1232 : nullptr};
1233 694 : return apszExtensions;
1234 : }
1235 :
1236 : /************************************************************************/
1237 : /* DeleteLayer() */
1238 : /************************************************************************/
1239 :
1240 548 : OGRErr OGRShapeDataSource::DeleteLayer(int iLayer)
1241 :
1242 : {
1243 : /* -------------------------------------------------------------------- */
1244 : /* Verify we are in update mode. */
1245 : /* -------------------------------------------------------------------- */
1246 548 : if (eAccess != GA_Update)
1247 : {
1248 1 : CPLError(CE_Failure, CPLE_NoWriteAccess,
1249 : "Data source %s opened read-only. "
1250 : "Layer %d cannot be deleted.",
1251 1 : GetDescription(), iLayer);
1252 :
1253 1 : return OGRERR_FAILURE;
1254 : }
1255 :
1256 : // To ensure that existing layers are created.
1257 547 : GetLayerCount();
1258 :
1259 547 : if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
1260 : {
1261 2 : CPLError(CE_Failure, CPLE_AppDefined,
1262 : "Layer %d not in legal range of 0 to %d.", iLayer,
1263 2 : static_cast<int>(m_apoLayers.size()) - 1);
1264 2 : return OGRERR_FAILURE;
1265 : }
1266 :
1267 545 : if (m_bIsZip && m_bSingleLayerZip)
1268 : {
1269 3 : CPLError(CE_Failure, CPLE_NotSupported,
1270 : ".shz does not support layer deletion");
1271 3 : return OGRERR_FAILURE;
1272 : }
1273 :
1274 542 : if (!UncompressIfNeeded())
1275 0 : return OGRERR_FAILURE;
1276 :
1277 542 : const std::string osLayerFilename = m_apoLayers[iLayer]->GetFullName();
1278 :
1279 542 : m_apoLayers.erase(m_apoLayers.begin() + iLayer);
1280 :
1281 : const char *const *papszExtensions =
1282 542 : OGRShapeDataSource::GetExtensionsForDeletion();
1283 6504 : for (int iExt = 0; papszExtensions[iExt] != nullptr; iExt++)
1284 : {
1285 : const std::string osFile = CPLResetExtensionSafe(
1286 11924 : osLayerFilename.c_str(), papszExtensions[iExt]);
1287 : VSIStatBufL sStatBuf;
1288 5962 : if (VSIStatL(osFile.c_str(), &sStatBuf) == 0)
1289 1621 : VSIUnlink(osFile.c_str());
1290 : }
1291 :
1292 542 : return OGRERR_NONE;
1293 : }
1294 :
1295 : /************************************************************************/
1296 : /* SetLastUsedLayer() */
1297 : /************************************************************************/
1298 :
1299 225189 : void OGRShapeDataSource::SetLastUsedLayer(OGRShapeLayer *poLayer)
1300 : {
1301 : // We could remove that check and things would still work in
1302 : // 99.99% cases.
1303 : // The only rationale for that test is to avoid breaking applications that
1304 : // would deal with layers of the same datasource in different threads. In
1305 : // GDAL < 1.9.0, this would work in most cases I can imagine as shapefile
1306 : // layers are pretty much independent from each others (although it has
1307 : // never been guaranteed to be a valid use case, and the shape driver is
1308 : // likely more the exception than the rule in permitting accessing layers
1309 : // from different threads !) Anyway the LRU list mechanism leaves the door
1310 : // open to concurrent accesses to it so when the datasource has not many
1311 : // layers, we don't try to build the LRU list to avoid concurrency issues. I
1312 : // haven't bothered making the analysis of how a mutex could be used to
1313 : // protect that (my intuition is that it would need to be placed at the
1314 : // beginning of OGRShapeLayer::TouchLayer() ).
1315 450378 : if (static_cast<int>(m_apoLayers.size()) <
1316 225189 : m_poPool->GetMaxSimultaneouslyOpened())
1317 216347 : return;
1318 :
1319 8842 : m_poPool->SetLastUsedLayer(poLayer);
1320 : }
1321 :
1322 : /************************************************************************/
1323 : // GetFileList() */
1324 : /************************************************************************/
1325 :
1326 29 : char **OGRShapeDataSource::GetFileList()
1327 : {
1328 29 : if (m_bIsZip)
1329 : {
1330 1 : return CSLAddString(nullptr, GetDescription());
1331 : }
1332 56 : CPLStringList oFileList;
1333 28 : GetLayerCount();
1334 163 : for (auto &poLayer : m_apoLayers)
1335 : {
1336 135 : poLayer->AddToFileList(oFileList);
1337 : }
1338 28 : return oFileList.StealList();
1339 : }
1340 :
1341 : /************************************************************************/
1342 : // RefreshLockFile() */
1343 : /************************************************************************/
1344 :
1345 0 : void OGRShapeDataSource::RefreshLockFile(void *_self)
1346 : {
1347 0 : OGRShapeDataSource *self = static_cast<OGRShapeDataSource *>(_self);
1348 0 : CPLAssert(self->m_psLockFile);
1349 0 : CPLAcquireMutex(self->m_poRefreshLockFileMutex, 1000);
1350 0 : self->m_bRefreshLockFileThreadStarted = true;
1351 0 : CPLCondSignal(self->m_poRefreshLockFileCond);
1352 0 : unsigned int nInc = 0;
1353 0 : while (!(self->m_bExitRefreshLockFileThread))
1354 : {
1355 0 : auto ret = CPLCondTimedWait(self->m_poRefreshLockFileCond,
1356 : self->m_poRefreshLockFileMutex,
1357 : self->m_dfRefreshLockDelay);
1358 0 : if (ret == COND_TIMED_WAIT_TIME_OUT)
1359 : {
1360 0 : CPLAssert(self->m_psLockFile);
1361 0 : VSIFSeekL(self->m_psLockFile, 0, SEEK_SET);
1362 0 : CPLString osTime;
1363 0 : nInc++;
1364 : osTime.Printf(CPL_FRMT_GUIB ", %u\n",
1365 0 : static_cast<GUIntBig>(time(nullptr)), nInc);
1366 0 : VSIFWriteL(osTime.data(), 1, osTime.size(), self->m_psLockFile);
1367 0 : VSIFFlushL(self->m_psLockFile);
1368 : }
1369 : }
1370 0 : CPLReleaseMutex(self->m_poRefreshLockFileMutex);
1371 0 : }
1372 :
1373 : /************************************************************************/
1374 : // RemoveLockFile() */
1375 : /************************************************************************/
1376 :
1377 3188 : void OGRShapeDataSource::RemoveLockFile()
1378 : {
1379 3188 : if (!m_psLockFile)
1380 3188 : return;
1381 :
1382 : // Ask the thread to terminate
1383 0 : CPLAcquireMutex(m_poRefreshLockFileMutex, 1000);
1384 0 : m_bExitRefreshLockFileThread = true;
1385 0 : CPLCondSignal(m_poRefreshLockFileCond);
1386 0 : CPLReleaseMutex(m_poRefreshLockFileMutex);
1387 0 : CPLJoinThread(m_hRefreshLockFileThread);
1388 0 : m_hRefreshLockFileThread = nullptr;
1389 :
1390 : // Close and remove lock file
1391 0 : VSIFCloseL(m_psLockFile);
1392 0 : m_psLockFile = nullptr;
1393 0 : CPLString osLockFile(GetDescription());
1394 0 : osLockFile += ".gdal.lock";
1395 0 : VSIUnlink(osLockFile);
1396 : }
1397 :
1398 : /************************************************************************/
1399 : // UncompressIfNeeded() */
1400 : /************************************************************************/
1401 :
1402 72390 : bool OGRShapeDataSource::UncompressIfNeeded()
1403 : {
1404 72390 : if (eAccess != GA_Update || !m_bIsZip || !m_osTemporaryUnzipDir.empty())
1405 72384 : return true;
1406 :
1407 6 : GetLayerCount();
1408 :
1409 0 : auto returnError = [this]()
1410 : {
1411 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot uncompress %s",
1412 0 : GetDescription());
1413 0 : return false;
1414 6 : };
1415 :
1416 6 : if (m_apoLayers.size() > 1)
1417 : {
1418 0 : CPLString osLockFile(GetDescription());
1419 0 : osLockFile += ".gdal.lock";
1420 : VSIStatBufL sStat;
1421 0 : if (VSIStatL(osLockFile, &sStat) == 0 &&
1422 0 : sStat.st_mtime > time(nullptr) - 2 * knREFRESH_LOCK_FILE_DELAY_SEC)
1423 : {
1424 0 : CPLError(CE_Failure, CPLE_AppDefined,
1425 : "Cannot edit %s. Another task is editing it",
1426 0 : GetDescription());
1427 0 : return false;
1428 : }
1429 0 : if (!m_poRefreshLockFileMutex)
1430 : {
1431 0 : m_poRefreshLockFileMutex = CPLCreateMutex();
1432 0 : if (!m_poRefreshLockFileMutex)
1433 0 : return false;
1434 0 : CPLReleaseMutex(m_poRefreshLockFileMutex);
1435 : }
1436 0 : if (!m_poRefreshLockFileCond)
1437 : {
1438 0 : m_poRefreshLockFileCond = CPLCreateCond();
1439 0 : if (!m_poRefreshLockFileCond)
1440 0 : return false;
1441 : }
1442 0 : auto f = VSIFOpenL(osLockFile, "wb");
1443 0 : if (!f)
1444 : {
1445 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot create lock file");
1446 0 : return false;
1447 : }
1448 0 : m_psLockFile = f;
1449 0 : CPLAcquireMutex(m_poRefreshLockFileMutex, 1000);
1450 0 : m_bExitRefreshLockFileThread = false;
1451 0 : m_bRefreshLockFileThreadStarted = false;
1452 0 : CPLReleaseMutex(m_poRefreshLockFileMutex);
1453 : // Config option mostly for testing purposes
1454 : // coverity[tainted_data]
1455 0 : m_dfRefreshLockDelay = CPLAtof(CPLGetConfigOption(
1456 : "OGR_SHAPE_LOCK_DELAY",
1457 : CPLSPrintf("%d", knREFRESH_LOCK_FILE_DELAY_SEC)));
1458 0 : m_hRefreshLockFileThread =
1459 0 : CPLCreateJoinableThread(OGRShapeDataSource::RefreshLockFile, this);
1460 0 : if (!m_hRefreshLockFileThread)
1461 : {
1462 0 : VSIFCloseL(m_psLockFile);
1463 0 : m_psLockFile = nullptr;
1464 0 : VSIUnlink(osLockFile);
1465 : }
1466 : else
1467 : {
1468 0 : CPLAcquireMutex(m_poRefreshLockFileMutex, 1000);
1469 0 : while (!m_bRefreshLockFileThreadStarted)
1470 : {
1471 0 : CPLCondWait(m_poRefreshLockFileCond, m_poRefreshLockFileMutex);
1472 : }
1473 0 : CPLReleaseMutex(m_poRefreshLockFileMutex);
1474 : }
1475 : }
1476 :
1477 12 : CPLString osVSIZipDirname(GetVSIZipPrefixeDir());
1478 6 : vsi_l_offset nTotalUncompressedSize = 0;
1479 12 : CPLStringList aosFiles(VSIReadDir(osVSIZipDirname));
1480 20 : for (int i = 0; i < aosFiles.size(); i++)
1481 : {
1482 14 : const char *pszFilename = aosFiles[i];
1483 14 : if (!EQUAL(pszFilename, ".") && !EQUAL(pszFilename, ".."))
1484 : {
1485 : const CPLString osSrcFile(
1486 28 : CPLFormFilenameSafe(osVSIZipDirname, pszFilename, nullptr));
1487 : VSIStatBufL sStat;
1488 14 : if (VSIStatL(osSrcFile, &sStat) == 0)
1489 : {
1490 14 : nTotalUncompressedSize += sStat.st_size;
1491 : }
1492 : }
1493 : }
1494 :
1495 12 : CPLString osTemporaryDir(GetDescription());
1496 6 : osTemporaryDir += "_tmp_uncompressed";
1497 :
1498 : const char *pszUseVsimem =
1499 6 : CPLGetConfigOption("OGR_SHAPE_USE_VSIMEM_FOR_TEMP", "AUTO");
1500 12 : if (EQUAL(pszUseVsimem, "YES") ||
1501 6 : (EQUAL(pszUseVsimem, "AUTO") && nTotalUncompressedSize > 0 &&
1502 : nTotalUncompressedSize <
1503 3 : static_cast<GUIntBig>(CPLGetUsablePhysicalRAM() / 10)))
1504 : {
1505 3 : osTemporaryDir = VSIMemGenerateHiddenFilename("shapedriver");
1506 : }
1507 6 : CPLDebug("Shape", "Uncompressing to %s", osTemporaryDir.c_str());
1508 :
1509 6 : VSIRmdirRecursive(osTemporaryDir);
1510 6 : if (VSIMkdir(osTemporaryDir, 0755) != 0)
1511 0 : return returnError();
1512 20 : for (int i = 0; i < aosFiles.size(); i++)
1513 : {
1514 14 : const char *pszFilename = aosFiles[i];
1515 14 : if (!EQUAL(pszFilename, ".") && !EQUAL(pszFilename, ".."))
1516 : {
1517 : const CPLString osSrcFile(
1518 14 : CPLFormFilenameSafe(osVSIZipDirname, pszFilename, nullptr));
1519 : const CPLString osDestFile(
1520 14 : CPLFormFilenameSafe(osTemporaryDir, pszFilename, nullptr));
1521 14 : if (CPLCopyFile(osDestFile, osSrcFile) != 0)
1522 : {
1523 0 : VSIRmdirRecursive(osTemporaryDir);
1524 0 : return returnError();
1525 : }
1526 : }
1527 : }
1528 :
1529 6 : m_osTemporaryUnzipDir = std::move(osTemporaryDir);
1530 :
1531 9 : for (auto &poLayer : m_apoLayers)
1532 : {
1533 3 : poLayer->UpdateFollowingDeOrRecompression();
1534 : }
1535 :
1536 6 : return true;
1537 : }
1538 :
1539 : /************************************************************************/
1540 : // RecompressIfNeeded() */
1541 : /************************************************************************/
1542 :
1543 3182 : bool OGRShapeDataSource::RecompressIfNeeded(
1544 : const std::vector<CPLString> &layerNames)
1545 : {
1546 3182 : if (eAccess != GA_Update || !m_bIsZip || m_osTemporaryUnzipDir.empty())
1547 3176 : return true;
1548 :
1549 0 : auto returnError = [this]()
1550 : {
1551 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot recompress %s",
1552 0 : GetDescription());
1553 0 : RemoveLockFile();
1554 0 : return false;
1555 6 : };
1556 :
1557 12 : CPLStringList aosFiles(VSIReadDir(m_osTemporaryUnzipDir));
1558 12 : CPLString osTmpZip(m_osTemporaryUnzipDir + ".zip");
1559 6 : VSIUnlink(osTmpZip);
1560 18 : CPLString osTmpZipWithVSIZip("/vsizip/{" + osTmpZip + '}');
1561 :
1562 12 : std::map<CPLString, int> oMapLayerOrder;
1563 12 : for (size_t i = 0; i < layerNames.size(); i++)
1564 6 : oMapLayerOrder[layerNames[i]] = static_cast<int>(i);
1565 :
1566 12 : std::vector<CPLString> sortedFiles;
1567 6 : vsi_l_offset nTotalUncompressedSize = 0;
1568 36 : for (int i = 0; i < aosFiles.size(); i++)
1569 : {
1570 30 : sortedFiles.emplace_back(aosFiles[i]);
1571 : const CPLString osSrcFile(
1572 60 : CPLFormFilenameSafe(m_osTemporaryUnzipDir, aosFiles[i], nullptr));
1573 : VSIStatBufL sStat;
1574 30 : if (VSIStatL(osSrcFile, &sStat) == 0)
1575 : {
1576 30 : nTotalUncompressedSize += sStat.st_size;
1577 : }
1578 : }
1579 :
1580 : // Sort files by their layer orders, and then for files of the same layer,
1581 : // make shp appear first, and then by filename order
1582 6 : std::sort(sortedFiles.begin(), sortedFiles.end(),
1583 220 : [&oMapLayerOrder](const CPLString &a, const CPLString &b)
1584 : {
1585 55 : int iA = INT_MAX;
1586 : auto oIterA =
1587 55 : oMapLayerOrder.find(CPLGetBasenameSafe(a).c_str());
1588 55 : if (oIterA != oMapLayerOrder.end())
1589 47 : iA = oIterA->second;
1590 55 : int iB = INT_MAX;
1591 : auto oIterB =
1592 55 : oMapLayerOrder.find(CPLGetBasenameSafe(b).c_str());
1593 55 : if (oIterB != oMapLayerOrder.end())
1594 40 : iB = oIterB->second;
1595 55 : if (iA < iB)
1596 13 : return true;
1597 42 : if (iA > iB)
1598 6 : return false;
1599 36 : if (iA != INT_MAX)
1600 : {
1601 34 : if (EQUAL(CPLGetExtensionSafe(a).c_str(), "shp"))
1602 4 : return true;
1603 30 : if (EQUAL(CPLGetExtensionSafe(b).c_str(), "shp"))
1604 13 : return false;
1605 : }
1606 19 : return a < b;
1607 : });
1608 :
1609 : CPLConfigOptionSetter oZIP64Setter(
1610 : "CPL_CREATE_ZIP64",
1611 12 : nTotalUncompressedSize < 4000U * 1000 * 1000 ? "NO" : "YES", true);
1612 :
1613 : /* Maintain a handle on the ZIP opened */
1614 6 : VSILFILE *fpZIP = VSIFOpenExL(osTmpZipWithVSIZip, "wb", true);
1615 6 : if (fpZIP == nullptr)
1616 : {
1617 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s: %s",
1618 : osTmpZipWithVSIZip.c_str(), VSIGetLastErrorMsg());
1619 0 : return returnError();
1620 : }
1621 :
1622 36 : for (const auto &osFilename : sortedFiles)
1623 : {
1624 30 : const char *pszFilename = osFilename.c_str();
1625 30 : if (!EQUAL(pszFilename, ".") && !EQUAL(pszFilename, ".."))
1626 : {
1627 26 : const CPLString osSrcFile(CPLFormFilenameSafe(
1628 26 : m_osTemporaryUnzipDir, pszFilename, nullptr));
1629 : const CPLString osDestFile(
1630 26 : CPLFormFilenameSafe(osTmpZipWithVSIZip, pszFilename, nullptr));
1631 26 : if (CPLCopyFile(osDestFile, osSrcFile) != 0)
1632 : {
1633 0 : VSIFCloseL(fpZIP);
1634 0 : return returnError();
1635 : }
1636 : }
1637 : }
1638 :
1639 6 : VSIFCloseL(fpZIP);
1640 :
1641 : const bool bOverwrite =
1642 6 : CPLTestBool(CPLGetConfigOption("OGR_SHAPE_PACK_IN_PLACE",
1643 : #ifdef _WIN32
1644 : "YES"
1645 : #else
1646 : "NO"
1647 : #endif
1648 : ));
1649 6 : if (bOverwrite)
1650 : {
1651 0 : VSILFILE *fpTarget = nullptr;
1652 0 : for (int i = 0; i < 10; i++)
1653 : {
1654 0 : fpTarget = VSIFOpenL(GetDescription(), "rb+");
1655 0 : if (fpTarget)
1656 0 : break;
1657 0 : CPLSleep(0.1);
1658 : }
1659 0 : if (!fpTarget)
1660 0 : return returnError();
1661 0 : bool bCopyOK = CopyInPlace(fpTarget, osTmpZip);
1662 0 : VSIFCloseL(fpTarget);
1663 0 : VSIUnlink(osTmpZip);
1664 0 : if (!bCopyOK)
1665 : {
1666 0 : return returnError();
1667 : }
1668 : }
1669 : else
1670 : {
1671 12 : if (VSIUnlink(GetDescription()) != 0 ||
1672 6 : CPLMoveFile(GetDescription(), osTmpZip) != 0)
1673 : {
1674 0 : return returnError();
1675 : }
1676 : }
1677 :
1678 6 : VSIRmdirRecursive(m_osTemporaryUnzipDir);
1679 6 : m_osTemporaryUnzipDir.clear();
1680 :
1681 6 : for (auto &poLayer : m_apoLayers)
1682 : {
1683 0 : poLayer->UpdateFollowingDeOrRecompression();
1684 : }
1685 :
1686 6 : RemoveLockFile();
1687 :
1688 6 : return true;
1689 : }
1690 :
1691 : /************************************************************************/
1692 : /* CopyInPlace() */
1693 : /************************************************************************/
1694 :
1695 3 : bool OGRShapeDataSource::CopyInPlace(VSILFILE *fpTarget,
1696 : const CPLString &osSourceFilename)
1697 : {
1698 3 : return CPL_TO_BOOL(VSIOverwriteFile(fpTarget, osSourceFilename.c_str()));
1699 : }
|