LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/pgeo - ogrpgeodatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 0 251 0.0 %
Date: 2024-11-21 22:18:42 Functions: 0 22 0.0 %

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

Generated by: LCOV version 1.14