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 255 0.0 %
Date: 2024-05-04 12:52:34 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             :  * 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             : }

Generated by: LCOV version 1.14