Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements OGRPGeoDataSource class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "ogr_pgeo.h"
31 : #include "cpl_conv.h"
32 : #include "cpl_string.h"
33 : #include <vector>
34 : #include <unordered_set>
35 : #include "filegdb_fielddomain.h"
36 : #include "filegdb_relationship.h"
37 :
38 : #ifdef __linux
39 : #include <sys/types.h>
40 : #include <sys/stat.h>
41 : #include <fcntl.h>
42 : #endif
43 :
44 : /************************************************************************/
45 : /* OGRPGeoDataSource() */
46 : /************************************************************************/
47 :
48 0 : OGRPGeoDataSource::OGRPGeoDataSource()
49 0 : : papoLayers(nullptr), nLayers(0), pszName(nullptr)
50 : {
51 : // Retrieve numeric values from MS Access files using ODBC numeric types, to
52 : // avoid loss of precision and missing values on Windows (see
53 : // https://github.com/OSGeo/gdal/issues/3885)
54 0 : m_nStatementFlags |= CPLODBCStatement::Flag::RetrieveNumericColumnsAsDouble;
55 0 : }
56 :
57 : /************************************************************************/
58 : /* ~OGRPGeoDataSource() */
59 : /************************************************************************/
60 :
61 0 : OGRPGeoDataSource::~OGRPGeoDataSource()
62 :
63 : {
64 0 : CPLFree(pszName);
65 :
66 0 : for (int i = 0; i < nLayers; i++)
67 0 : delete papoLayers[i];
68 :
69 0 : CPLFree(papoLayers);
70 0 : }
71 :
72 : /************************************************************************/
73 : /* CheckDSNStringTemplate() */
74 : /* The string will be used as the formatting argument of sprintf with */
75 : /* a string in vararg. So let's check there's only one '%s', and nothing*/
76 : /* else */
77 : /************************************************************************/
78 :
79 0 : static int CheckDSNStringTemplate(const char *pszStr)
80 : {
81 0 : int nPercentSFound = FALSE;
82 0 : while (*pszStr)
83 : {
84 0 : if (*pszStr == '%')
85 : {
86 0 : if (pszStr[1] != 's')
87 : {
88 0 : return FALSE;
89 : }
90 : else
91 : {
92 0 : if (nPercentSFound)
93 0 : return FALSE;
94 0 : nPercentSFound = TRUE;
95 : }
96 : }
97 0 : pszStr++;
98 : }
99 0 : return TRUE;
100 : }
101 :
102 : /************************************************************************/
103 : /* Open() */
104 : /************************************************************************/
105 :
106 0 : int OGRPGeoDataSource::Open(GDALOpenInfo *poOpenInfo)
107 : {
108 0 : CPLAssert(nLayers == 0);
109 :
110 0 : const char *pszNewName = poOpenInfo->pszFilename;
111 : /* -------------------------------------------------------------------- */
112 : /* If this is the name of an MDB file, then construct the */
113 : /* appropriate connection string. Otherwise clip of PGEO: to */
114 : /* get the DSN. */
115 : /* */
116 : /* -------------------------------------------------------------------- */
117 0 : if (STARTS_WITH_CI(pszNewName, "PGEO:"))
118 : {
119 0 : char *pszDSN = CPLStrdup(pszNewName + 5);
120 0 : CPLDebug("PGeo", "EstablishSession(%s)", pszDSN);
121 0 : if (!oSession.EstablishSession(pszDSN, nullptr, nullptr))
122 : {
123 0 : CPLError(CE_Failure, CPLE_AppDefined,
124 : "Unable to initialize ODBC connection to DSN for %s,\n"
125 : "%s",
126 : pszDSN, oSession.GetLastError());
127 0 : CPLFree(pszDSN);
128 0 : return FALSE;
129 : }
130 : }
131 : else
132 : {
133 : const char *pszDSNStringTemplate =
134 0 : CPLGetConfigOption("PGEO_DRIVER_TEMPLATE", nullptr);
135 0 : if (pszDSNStringTemplate &&
136 0 : !CheckDSNStringTemplate(pszDSNStringTemplate))
137 : {
138 0 : CPLError(CE_Failure, CPLE_AppDefined,
139 : "Illegal value for PGEO_DRIVER_TEMPLATE option");
140 0 : return FALSE;
141 : }
142 0 : if (!oSession.ConnectToMsAccess(pszNewName, pszDSNStringTemplate))
143 : {
144 0 : return FALSE;
145 : }
146 : }
147 :
148 0 : pszName = CPLStrdup(pszNewName);
149 :
150 : /* -------------------------------------------------------------------- */
151 : /* Collect list of tables and their supporting info from */
152 : /* GDB_GeomColumns. */
153 : /* -------------------------------------------------------------------- */
154 0 : std::vector<char **> apapszGeomColumns;
155 0 : CPLODBCStatement oStmt(&oSession);
156 :
157 0 : oStmt.Append(
158 : "SELECT TableName, FieldName, ShapeType, ExtentLeft, ExtentRight, "
159 : "ExtentBottom, ExtentTop, SRID, HasZ, HasM FROM GDB_GeomColumns");
160 :
161 0 : if (!oStmt.ExecuteSQL())
162 : {
163 0 : CPLDebug("PGEO",
164 : "SELECT on GDB_GeomColumns fails, perhaps not a personal "
165 : "geodatabase?\n%s",
166 : oSession.GetLastError());
167 0 : return FALSE;
168 : }
169 :
170 0 : while (oStmt.Fetch())
171 : {
172 0 : int i, iNew = static_cast<int>(apapszGeomColumns.size());
173 0 : char **papszRecord = nullptr;
174 0 : for (i = 0; i < 10; i++)
175 0 : papszRecord = CSLAddString(papszRecord, oStmt.GetColData(i));
176 0 : apapszGeomColumns.resize(iNew + 1);
177 0 : apapszGeomColumns[iNew] = papszRecord;
178 : }
179 :
180 0 : const bool bListAllTables = CPLTestBool(CSLFetchNameValueDef(
181 0 : poOpenInfo->papszOpenOptions, "LIST_ALL_TABLES", "NO"));
182 :
183 : // Collate a list of all tables in the data source, skipping over internal
184 : // and system tables
185 0 : CPLODBCStatement oTableList(&oSession);
186 0 : std::vector<CPLString> aosTableNames;
187 0 : if (oTableList.GetTables())
188 : {
189 0 : while (oTableList.Fetch())
190 : {
191 0 : const CPLString osTableName = CPLString(oTableList.GetColData(2));
192 0 : const CPLString osLCTableName(CPLString(osTableName).tolower());
193 0 : m_aosAllLCTableNames.insert(osLCTableName);
194 :
195 0 : if (osLCTableName == "gdb_items")
196 : {
197 0 : m_bHasGdbItemsTable = true;
198 : }
199 :
200 0 : if (!osTableName.empty() &&
201 0 : (bListAllTables || !IsPrivateLayerName(osTableName)))
202 : {
203 0 : aosTableNames.emplace_back(osTableName);
204 : }
205 : }
206 : }
207 :
208 : // Create a layer for each spatial table.
209 0 : papoLayers =
210 0 : (OGRPGeoLayer **)CPLCalloc(apapszGeomColumns.size(), sizeof(void *));
211 :
212 0 : std::unordered_set<std::string> oSetSpatialTableNames;
213 0 : for (unsigned int iTable = 0; iTable < apapszGeomColumns.size(); iTable++)
214 : {
215 0 : char **papszRecord = apapszGeomColumns[iTable];
216 0 : if (EQUAL(papszRecord[0], "GDB_Items"))
217 : {
218 : // don't expose this internal layer
219 0 : CSLDestroy(papszRecord);
220 0 : continue;
221 : }
222 :
223 : OGRPGeoTableLayer *poLayer =
224 0 : new OGRPGeoTableLayer(this, m_nStatementFlags);
225 :
226 0 : if (poLayer->Initialize(papszRecord[0], // TableName
227 0 : papszRecord[1], // FieldName
228 0 : atoi(papszRecord[2]), // ShapeType
229 0 : CPLAtof(papszRecord[3]), // ExtentLeft
230 0 : CPLAtof(papszRecord[4]), // ExtentRight
231 0 : CPLAtof(papszRecord[5]), // ExtentBottom
232 0 : CPLAtof(papszRecord[6]), // ExtentTop
233 0 : atoi(papszRecord[7]), // SRID
234 0 : atoi(papszRecord[8]), // HasZ
235 0 : atoi(papszRecord[9])) // HasM
236 0 : != CE_None)
237 : {
238 0 : delete poLayer;
239 : }
240 : else
241 : {
242 0 : papoLayers[nLayers++] = poLayer;
243 0 : oSetSpatialTableNames.insert(CPLString(papszRecord[0]));
244 : }
245 :
246 0 : CSLDestroy(papszRecord);
247 : }
248 :
249 : // Add non-spatial tables.
250 0 : for (const CPLString &osTableName : aosTableNames)
251 : {
252 0 : if (oSetSpatialTableNames.find(osTableName) !=
253 0 : oSetSpatialTableNames.end())
254 : {
255 : // a spatial table, already handled above
256 0 : continue;
257 : }
258 :
259 : OGRPGeoTableLayer *poLayer =
260 0 : new OGRPGeoTableLayer(this, m_nStatementFlags);
261 :
262 0 : if (poLayer->Initialize(osTableName.c_str(), // TableName
263 : nullptr, // FieldName
264 : 0, // ShapeType (ESRI_LAYERGEOMTYPE_NULL)
265 : 0, // ExtentLeft
266 : 0, // ExtentRight
267 : 0, // ExtentBottom
268 : 0, // ExtentTop
269 : 0, // SRID
270 : 0, // HasZ
271 : 0) // HasM
272 0 : != CE_None)
273 : {
274 0 : delete poLayer;
275 : }
276 : else
277 : {
278 0 : papoLayers = static_cast<OGRPGeoLayer **>(
279 0 : CPLRealloc(papoLayers, sizeof(void *) * (nLayers + 1)));
280 0 : papoLayers[nLayers++] = poLayer;
281 : }
282 : }
283 :
284 : // collect domains and relationships
285 0 : if (m_bHasGdbItemsTable)
286 : {
287 0 : CPLODBCStatement oItemsStmt(&oSession);
288 0 : oItemsStmt.Append("SELECT Definition FROM GDB_Items");
289 0 : if (oItemsStmt.ExecuteSQL())
290 : {
291 0 : while (oItemsStmt.Fetch())
292 : {
293 : const CPLString osDefinition =
294 0 : CPLString(oItemsStmt.GetColData(0, ""));
295 0 : if (strstr(osDefinition, "GPCodedValueDomain2") != nullptr ||
296 0 : strstr(osDefinition, "GPRangeDomain2") != nullptr)
297 : {
298 0 : if (auto poDomain = ParseXMLFieldDomainDef(osDefinition))
299 : {
300 0 : const std::string domainName(poDomain->GetName());
301 0 : m_oMapFieldDomains[domainName] = std::move(poDomain);
302 : }
303 : }
304 0 : else if (strstr(osDefinition, "DERelationshipClassInfo") !=
305 : nullptr)
306 : {
307 0 : if (auto poRelationship =
308 0 : ParseXMLRelationshipDef(osDefinition))
309 : {
310 : const std::string relationshipName(
311 0 : poRelationship->GetName());
312 0 : m_osMapRelationships[relationshipName] =
313 0 : std::move(poRelationship);
314 : }
315 : }
316 : }
317 : }
318 : }
319 :
320 0 : return TRUE;
321 : }
322 :
323 : /************************************************************************/
324 : /* TestCapability() */
325 : /************************************************************************/
326 :
327 0 : int OGRPGeoDataSource::TestCapability(CPL_UNUSED const char *pszCap)
328 : {
329 0 : if (EQUAL(pszCap, ODsCMeasuredGeometries))
330 0 : return TRUE;
331 0 : else if (EQUAL(pszCap, ODsCCurveGeometries))
332 0 : return TRUE;
333 0 : else if (EQUAL(pszCap, ODsCZGeometries))
334 0 : return TRUE;
335 :
336 0 : return FALSE;
337 : }
338 :
339 : /************************************************************************/
340 : /* GetLayer() */
341 : /************************************************************************/
342 :
343 0 : OGRLayer *OGRPGeoDataSource::GetLayer(int iLayer)
344 :
345 : {
346 0 : if (iLayer < 0 || iLayer >= nLayers)
347 0 : return nullptr;
348 : else
349 0 : return papoLayers[iLayer];
350 : }
351 :
352 0 : OGRLayer *OGRPGeoDataSource::GetLayerByName(const char *pszLayerName)
353 : {
354 0 : OGRLayer *poLayer = GDALDataset::GetLayerByName(pszLayerName);
355 0 : if (poLayer != nullptr)
356 0 : return poLayer;
357 :
358 : // if table name doesn't exist in database, don't try any further
359 0 : const CPLString osLCTableName(CPLString(pszLayerName).tolower());
360 0 : if (m_aosAllLCTableNames.find(osLCTableName) == m_aosAllLCTableNames.end())
361 0 : return nullptr;
362 :
363 0 : for (const auto &poInvisibleLayer : m_apoInvisibleLayers)
364 : {
365 0 : if (EQUAL(poInvisibleLayer->GetName(), pszLayerName))
366 0 : return poInvisibleLayer.get();
367 : }
368 :
369 : std::unique_ptr<OGRPGeoTableLayer> poInvisibleLayer(
370 0 : new OGRPGeoTableLayer(this, m_nStatementFlags));
371 :
372 0 : if (poInvisibleLayer->Initialize(pszLayerName, // TableName
373 : nullptr, // FieldName
374 : 0, // ShapeType (ESRI_LAYERGEOMTYPE_NULL)
375 : 0, // ExtentLeft
376 : 0, // ExtentRight
377 : 0, // ExtentBottom
378 : 0, // ExtentTop
379 : 0, // SRID
380 : 0, // HasZ
381 : 0) // HasM
382 0 : == CE_None)
383 : {
384 0 : m_apoInvisibleLayers.emplace_back(std::move(poInvisibleLayer));
385 0 : poLayer = m_apoInvisibleLayers.back().get();
386 : }
387 0 : return poLayer;
388 : }
389 :
390 : /************************************************************************/
391 : /* IsPrivateLayerName() */
392 : /************************************************************************/
393 :
394 0 : bool OGRPGeoDataSource::IsPrivateLayerName(const CPLString &osName)
395 : {
396 0 : const CPLString osLCTableName(CPLString(osName).tolower());
397 :
398 : return (
399 0 : (osLCTableName.size() >= 4 &&
400 0 : osLCTableName.substr(0, 4) == "msys") // MS Access internal tables
401 0 : ||
402 0 : osLCTableName.endsWith(
403 : "_shape_index") // gdb spatial index tables, internal details only
404 0 : || (osLCTableName.size() >= 4 &&
405 0 : osLCTableName.substr(0, 4) == "gdb_") // gdb private tables
406 0 : || osLCTableName == "selections" ||
407 0 : osLCTableName ==
408 : "selectedobjects" // found in older personal geodatabases
409 0 : );
410 : }
411 :
412 : /************************************************************************/
413 : /* IsLayerPrivate() */
414 : /************************************************************************/
415 :
416 0 : bool OGRPGeoDataSource::IsLayerPrivate(int iLayer) const
417 : {
418 0 : if (iLayer < 0 || iLayer >= nLayers)
419 0 : return false;
420 :
421 0 : const std::string osName(papoLayers[iLayer]->GetName());
422 0 : return IsPrivateLayerName(osName);
423 : }
424 :
425 : /************************************************************************/
426 : /* OGRPGeoSingleFeatureLayer */
427 : /************************************************************************/
428 :
429 : class OGRPGeoSingleFeatureLayer final : public OGRLayer
430 : {
431 : private:
432 : char *pszVal;
433 : OGRFeatureDefn *poFeatureDefn;
434 : int iNextShapeId;
435 :
436 : public:
437 : OGRPGeoSingleFeatureLayer(const char *pszLayerName, const char *pszVal);
438 : virtual ~OGRPGeoSingleFeatureLayer();
439 :
440 0 : virtual void ResetReading() override
441 : {
442 0 : iNextShapeId = 0;
443 0 : }
444 :
445 : virtual OGRFeature *GetNextFeature() override;
446 :
447 0 : virtual OGRFeatureDefn *GetLayerDefn() override
448 : {
449 0 : return poFeatureDefn;
450 : }
451 :
452 0 : virtual int TestCapability(const char *) override
453 : {
454 0 : return FALSE;
455 : }
456 : };
457 :
458 : /************************************************************************/
459 : /* OGRPGeoSingleFeatureLayer() */
460 : /************************************************************************/
461 :
462 0 : OGRPGeoSingleFeatureLayer::OGRPGeoSingleFeatureLayer(const char *pszLayerName,
463 0 : const char *pszValIn)
464 0 : : pszVal(pszValIn ? CPLStrdup(pszValIn) : nullptr),
465 0 : poFeatureDefn(new OGRFeatureDefn(pszLayerName)), iNextShapeId(0)
466 : {
467 0 : SetDescription(poFeatureDefn->GetName());
468 0 : poFeatureDefn->Reference();
469 0 : OGRFieldDefn oField("FIELD_1", OFTString);
470 0 : poFeatureDefn->AddFieldDefn(&oField);
471 0 : }
472 :
473 : /************************************************************************/
474 : /* ~OGRPGeoSingleFeatureLayer() */
475 : /************************************************************************/
476 :
477 0 : OGRPGeoSingleFeatureLayer::~OGRPGeoSingleFeatureLayer()
478 : {
479 0 : if (poFeatureDefn != nullptr)
480 0 : poFeatureDefn->Release();
481 0 : CPLFree(pszVal);
482 0 : }
483 :
484 : /************************************************************************/
485 : /* GetNextFeature() */
486 : /************************************************************************/
487 :
488 0 : OGRFeature *OGRPGeoSingleFeatureLayer::GetNextFeature()
489 : {
490 0 : if (iNextShapeId != 0)
491 0 : return nullptr;
492 :
493 0 : OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
494 0 : if (pszVal)
495 0 : poFeature->SetField(0, pszVal);
496 0 : poFeature->SetFID(iNextShapeId++);
497 0 : return poFeature;
498 : }
499 :
500 : /************************************************************************/
501 : /* ExecuteSQL() */
502 : /************************************************************************/
503 :
504 0 : OGRLayer *OGRPGeoDataSource::ExecuteSQL(const char *pszSQLCommand,
505 : OGRGeometry *poSpatialFilter,
506 : const char *pszDialect)
507 :
508 : {
509 :
510 : /* -------------------------------------------------------------------- */
511 : /* Special case GetLayerDefinition */
512 : /* -------------------------------------------------------------------- */
513 0 : if (STARTS_WITH_CI(pszSQLCommand, "GetLayerDefinition "))
514 : {
515 0 : OGRPGeoTableLayer *poLayer = cpl::down_cast<OGRPGeoTableLayer *>(
516 : GetLayerByName(pszSQLCommand + strlen("GetLayerDefinition ")));
517 0 : if (poLayer)
518 : {
519 : OGRLayer *poRet = new OGRPGeoSingleFeatureLayer(
520 0 : "LayerDefinition", poLayer->GetXMLDefinition().c_str());
521 0 : return poRet;
522 : }
523 :
524 0 : return nullptr;
525 : }
526 :
527 : /* -------------------------------------------------------------------- */
528 : /* Special case GetLayerMetadata */
529 : /* -------------------------------------------------------------------- */
530 0 : if (STARTS_WITH_CI(pszSQLCommand, "GetLayerMetadata "))
531 : {
532 0 : OGRPGeoTableLayer *poLayer = cpl::down_cast<OGRPGeoTableLayer *>(
533 : GetLayerByName(pszSQLCommand + strlen("GetLayerMetadata ")));
534 0 : if (poLayer)
535 : {
536 : OGRLayer *poRet = new OGRPGeoSingleFeatureLayer(
537 0 : "LayerMetadata", poLayer->GetXMLDocumentation().c_str());
538 0 : return poRet;
539 : }
540 :
541 0 : return nullptr;
542 : }
543 :
544 : /* -------------------------------------------------------------------- */
545 : /* Use generic implementation for recognized dialects */
546 : /* -------------------------------------------------------------------- */
547 0 : if (IsGenericSQLDialect(pszDialect))
548 0 : return OGRDataSource::ExecuteSQL(pszSQLCommand, poSpatialFilter,
549 0 : pszDialect);
550 :
551 : /* -------------------------------------------------------------------- */
552 : /* Execute statement. */
553 : /* -------------------------------------------------------------------- */
554 : CPLODBCStatement *poStmt =
555 0 : new CPLODBCStatement(&oSession, m_nStatementFlags);
556 :
557 0 : poStmt->Append(pszSQLCommand);
558 0 : if (!poStmt->ExecuteSQL())
559 : {
560 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", oSession.GetLastError());
561 0 : delete poStmt;
562 0 : return nullptr;
563 : }
564 :
565 : /* -------------------------------------------------------------------- */
566 : /* Are there result columns for this statement? */
567 : /* -------------------------------------------------------------------- */
568 0 : if (poStmt->GetColCount() == 0)
569 : {
570 0 : delete poStmt;
571 0 : CPLErrorReset();
572 0 : return nullptr;
573 : }
574 :
575 : /* -------------------------------------------------------------------- */
576 : /* Create a results layer. It will take ownership of the */
577 : /* statement. */
578 : /* -------------------------------------------------------------------- */
579 0 : OGRPGeoSelectLayer *poLayer = new OGRPGeoSelectLayer(this, poStmt);
580 :
581 0 : if (poSpatialFilter != nullptr)
582 0 : poLayer->SetSpatialFilter(poSpatialFilter);
583 :
584 0 : return poLayer;
585 : }
586 :
587 : /************************************************************************/
588 : /* GetRelationshipNames() */
589 : /************************************************************************/
590 :
591 0 : std::vector<std::string> OGRPGeoDataSource::GetRelationshipNames(
592 : CPL_UNUSED CSLConstList papszOptions) const
593 :
594 : {
595 0 : std::vector<std::string> oasNames;
596 0 : oasNames.reserve(m_osMapRelationships.size());
597 0 : for (auto it = m_osMapRelationships.begin();
598 0 : it != m_osMapRelationships.end(); ++it)
599 : {
600 0 : oasNames.emplace_back(it->first);
601 : }
602 0 : return oasNames;
603 : }
604 :
605 : /************************************************************************/
606 : /* GetRelationship() */
607 : /************************************************************************/
608 :
609 : const GDALRelationship *
610 0 : OGRPGeoDataSource::GetRelationship(const std::string &name) const
611 :
612 : {
613 0 : auto it = m_osMapRelationships.find(name);
614 0 : if (it == m_osMapRelationships.end())
615 0 : return nullptr;
616 :
617 0 : return it->second.get();
618 : }
619 :
620 : /************************************************************************/
621 : /* ReleaseResultSet() */
622 : /************************************************************************/
623 :
624 0 : void OGRPGeoDataSource::ReleaseResultSet(OGRLayer *poLayer)
625 :
626 : {
627 0 : delete poLayer;
628 0 : }
629 :
630 : /************************************************************************/
631 : /* ReleaseResultSet() */
632 : /************************************************************************/
633 :
634 0 : bool OGRPGeoDataSource::CountStarWorking() const
635 : {
636 : #ifdef _WIN32
637 : return true;
638 : #else
639 : // SELECT COUNT(*) worked in mdbtools 0.9.0 to 0.9.2, but got broken in
640 : // 0.9.3. So test if it is working
641 : // See https://github.com/OSGeo/gdal/issues/4103
642 0 : if (!m_COUNT_STAR_state_known)
643 : {
644 0 : m_COUNT_STAR_state_known = true;
645 :
646 : #ifdef __linux
647 : // Temporarily redirect stderr to /dev/null
648 0 : int new_fd = open("/dev/null", O_WRONLY | O_CREAT | O_TRUNC, 0666);
649 0 : int old_stderr = -1;
650 0 : if (new_fd != -1)
651 : {
652 0 : old_stderr = dup(fileno(stderr));
653 0 : if (old_stderr != -1)
654 : {
655 0 : dup2(new_fd, fileno(stderr));
656 : }
657 0 : close(new_fd);
658 : }
659 : #endif
660 :
661 : CPLErrorStateBackuper oStateBackuper(CPLErrorHandlerPusher);
662 :
663 0 : CPLODBCStatement oStmt(&oSession);
664 0 : oStmt.Append("SELECT COUNT(*) FROM GDB_GeomColumns");
665 0 : if (oStmt.ExecuteSQL() && oStmt.Fetch())
666 : {
667 0 : m_COUNT_STAR_working = true;
668 : }
669 :
670 : #ifdef __linux
671 0 : if (old_stderr != -1)
672 : {
673 0 : dup2(old_stderr, fileno(stderr));
674 0 : close(old_stderr);
675 : }
676 : #endif
677 : }
678 0 : return m_COUNT_STAR_working;
679 : #endif
680 : }
|