LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/odbc - ogrodbcdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 26 219 11.9 %
Date: 2024-11-21 22:18:42 Functions: 5 14 35.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements OGRODBCDataSource class.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2003, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "ogr_odbc.h"
      15             : #include "cpl_conv.h"
      16             : #include "cpl_string.h"
      17             : #include "ogrodbcdrivercore.h"
      18             : 
      19             : /************************************************************************/
      20             : /*                         OGRODBCDataSource()                          */
      21             : /************************************************************************/
      22             : 
      23           2 : OGRODBCDataSource::OGRODBCDataSource() : papoLayers(nullptr), nLayers(0)
      24             : {
      25           2 : }
      26             : 
      27             : /************************************************************************/
      28             : /*                         ~OGRODBCDataSource()                         */
      29             : /************************************************************************/
      30             : 
      31           4 : OGRODBCDataSource::~OGRODBCDataSource()
      32             : 
      33             : {
      34           2 :     for (int i = 0; i < nLayers; i++)
      35           0 :         delete papoLayers[i];
      36             : 
      37           2 :     CPLFree(papoLayers);
      38           4 : }
      39             : 
      40             : /************************************************************************/
      41             : /*                  CheckDSNStringTemplate()                            */
      42             : /* The string will be used as the formatting argument of sprintf with   */
      43             : /* a string in vararg. So let's check there's only one '%s', and nothing*/
      44             : /* else                                                                 */
      45             : /************************************************************************/
      46             : 
      47           0 : static int CheckDSNStringTemplate(const char *pszStr)
      48             : {
      49           0 :     int nPercentSFound = FALSE;
      50           0 :     while (*pszStr)
      51             :     {
      52           0 :         if (*pszStr == '%')
      53             :         {
      54           0 :             if (pszStr[1] != 's')
      55             :             {
      56           0 :                 return FALSE;
      57             :             }
      58             :             else
      59             :             {
      60           0 :                 if (nPercentSFound)
      61           0 :                     return FALSE;
      62           0 :                 nPercentSFound = TRUE;
      63             :             }
      64             :         }
      65           0 :         pszStr++;
      66             :     }
      67           0 :     return TRUE;
      68             : }
      69             : 
      70             : /************************************************************************/
      71             : /*                              OpenMDB()                               */
      72             : /************************************************************************/
      73             : 
      74           2 : int OGRODBCDataSource::OpenMDB(GDALOpenInfo *poOpenInfo)
      75             : {
      76             : #ifndef _WIN32
      77             :     // Try to register MDB Tools driver
      78           2 :     CPLODBCDriverInstaller::InstallMdbToolsDriver();
      79             : #endif /* ndef WIN32 */
      80             : 
      81           2 :     const char *pszOptionName = "PGEO_DRIVER_TEMPLATE";
      82             :     const char *pszDSNStringTemplate =
      83           2 :         CPLGetConfigOption(pszOptionName, nullptr);
      84           2 :     if (pszDSNStringTemplate == nullptr)
      85             :     {
      86           2 :         pszOptionName = "MDB_DRIVER_TEMPLATE";
      87           2 :         pszDSNStringTemplate = CPLGetConfigOption(pszOptionName, nullptr);
      88           2 :         if (pszDSNStringTemplate == nullptr)
      89             :         {
      90           2 :             pszOptionName = "";
      91             :         }
      92             :     }
      93           2 :     if (pszDSNStringTemplate && !CheckDSNStringTemplate(pszDSNStringTemplate))
      94             :     {
      95           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Illegal value for %s option",
      96             :                  pszOptionName);
      97           0 :         return FALSE;
      98             :     }
      99             : 
     100           2 :     const char *pszNewName = poOpenInfo->pszFilename;
     101           2 :     if (!oSession.ConnectToMsAccess(pszNewName, pszDSNStringTemplate))
     102             :     {
     103           2 :         return FALSE;
     104             :     }
     105             : 
     106             :     // Retrieve numeric values from MS Access files using ODBC numeric types, to
     107             :     // avoid loss of precision and missing values on Windows (see
     108             :     // https://github.com/OSGeo/gdal/issues/3885)
     109           0 :     m_nStatementFlags |= CPLODBCStatement::Flag::RetrieveNumericColumnsAsDouble;
     110             : 
     111             :     // Collate a list of all tables in the data source
     112           0 :     CPLODBCStatement oTableList(&oSession);
     113           0 :     std::vector<CPLString> aosTableNames;
     114           0 :     if (oTableList.GetTables())
     115             :     {
     116           0 :         while (oTableList.Fetch())
     117             :         {
     118           0 :             const char *pszSchema = oTableList.GetColData(1);
     119           0 :             const char *pszTableName = oTableList.GetColData(2);
     120           0 :             if (pszTableName != nullptr)
     121             :             {
     122           0 :                 CPLString osLayerName;
     123             : 
     124           0 :                 if (pszSchema != nullptr && strlen(pszSchema) > 0)
     125             :                 {
     126           0 :                     osLayerName = pszSchema;
     127           0 :                     osLayerName += ".";
     128             :                 }
     129             : 
     130           0 :                 osLayerName += pszTableName;
     131             : 
     132           0 :                 const CPLString osLCTableName(CPLString(osLayerName).tolower());
     133           0 :                 m_aosAllLCTableNames.insert(osLCTableName);
     134             : 
     135           0 :                 aosTableNames.emplace_back(osLayerName);
     136             :             }
     137             :         }
     138             :     }
     139             :     else
     140             :     {
     141           0 :         return FALSE;
     142             :     }
     143             : 
     144             :     /* -------------------------------------------------------------------- */
     145             :     /*      Check if it is a PGeo MDB.                    */
     146             :     /* -------------------------------------------------------------------- */
     147           0 :     for (const CPLString &osTableName : aosTableNames)
     148             :     {
     149           0 :         const CPLString osLCTableName(CPLString(osTableName).tolower());
     150           0 :         if (osLCTableName == "gdb_geomcolumns" /* PGeo */)
     151           0 :             return FALSE;
     152             :     }
     153             : 
     154           0 :     const bool bListAllTables = CPLTestBool(CSLFetchNameValueDef(
     155           0 :         poOpenInfo->papszOpenOptions, "LIST_ALL_TABLES", "NO"));
     156             : 
     157             :     /* -------------------------------------------------------------------- */
     158             :     /*      Return all tables as non-spatial tables.                       */
     159             :     /* -------------------------------------------------------------------- */
     160           0 :     for (const CPLString &osTableName : aosTableNames)
     161             :     {
     162           0 :         const CPLString osLCTableName(CPLString(osTableName).tolower());
     163           0 :         if (bListAllTables || !(osLCTableName.size() >= 4 &&
     164           0 :                                 osLCTableName.substr(0, 4) ==
     165             :                                     "msys")  // MS Access internal tables
     166             :         )
     167             :         {
     168           0 :             OpenTable(osTableName, nullptr);
     169             :         }
     170             :     }
     171             : 
     172           0 :     return TRUE;
     173             : }
     174             : 
     175             : /************************************************************************/
     176             : /*                                Open()                                */
     177             : /************************************************************************/
     178             : 
     179           2 : int OGRODBCDataSource::Open(GDALOpenInfo *poOpenInfo)
     180             : {
     181           2 :     CPLAssert(nLayers == 0);
     182             : 
     183           2 :     const char *pszNewName = poOpenInfo->pszFilename;
     184             : 
     185           2 :     constexpr const char *ODBC_PREFIX = "ODBC:";
     186           4 :     if (!STARTS_WITH_CI(pszNewName, ODBC_PREFIX) &&
     187           2 :         OGRODBCDriverIsSupportedMsAccessFileExtension(
     188             :             CPLGetExtension(pszNewName)))
     189           2 :         return OpenMDB(poOpenInfo);
     190             : 
     191             :     /* -------------------------------------------------------------------- */
     192             :     /*      Start parsing dataset name from the end of string, fetching     */
     193             :     /*      the name of spatial reference table and names for SRID and      */
     194             :     /*      SRTEXT columns first.                                           */
     195             :     /* -------------------------------------------------------------------- */
     196           0 :     char *pszWrkName = CPLStrdup(pszNewName + strlen(ODBC_PREFIX));
     197           0 :     char **papszTables = nullptr;
     198           0 :     char **papszGeomCol = nullptr;
     199           0 :     char *pszSRSTableName = nullptr;
     200           0 :     char *pszSRIDCol = nullptr;
     201           0 :     char *pszSRTextCol = nullptr;
     202           0 :     char *pszDelimiter = nullptr;
     203             : 
     204           0 :     if ((pszDelimiter = strrchr(pszWrkName, ':')) != nullptr)
     205             :     {
     206           0 :         char *pszOBracket = strchr(pszDelimiter + 1, '(');
     207             : 
     208           0 :         if (strchr(pszDelimiter, '\\') != nullptr ||
     209           0 :             strchr(pszDelimiter, '/') != nullptr)
     210             :         {
     211             :             /*
     212             :             ** if there are special tokens then this isn't really
     213             :             ** the srs table name, so avoid further processing.
     214             :             */
     215             :         }
     216           0 :         else if (pszOBracket == nullptr)
     217             :         {
     218           0 :             pszSRSTableName = CPLStrdup(pszDelimiter + 1);
     219           0 :             *pszDelimiter = '\0';
     220             :         }
     221             :         else
     222             :         {
     223           0 :             char *pszCBracket = strchr(pszOBracket, ')');
     224           0 :             if (pszCBracket != nullptr)
     225           0 :                 *pszCBracket = '\0';
     226             : 
     227           0 :             char *pszComma = strchr(pszOBracket, ',');
     228           0 :             if (pszComma != nullptr)
     229             :             {
     230           0 :                 *pszComma = '\0';
     231           0 :                 pszSRIDCol = CPLStrdup(pszComma + 1);
     232             :             }
     233             : 
     234           0 :             *pszOBracket = '\0';
     235           0 :             pszSRSTableName = CPLStrdup(pszDelimiter + 1);
     236           0 :             pszSRTextCol = CPLStrdup(pszOBracket + 1);
     237             : 
     238           0 :             *pszDelimiter = '\0';
     239             :         }
     240             :     }
     241             : 
     242             :     /* -------------------------------------------------------------------- */
     243             :     /*      Strip off any comma delimited set of tables names to access     */
     244             :     /*      from the end of the string first.  Also allow an optional       */
     245             :     /*      bracketed geometry column name after the table name.            */
     246             :     /* -------------------------------------------------------------------- */
     247           0 :     while ((pszDelimiter = strrchr(pszWrkName, ',')) != nullptr)
     248             :     {
     249           0 :         char *pszOBracket = strstr(pszDelimiter + 1, "(");
     250           0 :         if (pszOBracket == nullptr)
     251             :         {
     252           0 :             papszTables = CSLAddString(papszTables, pszDelimiter + 1);
     253           0 :             papszGeomCol = CSLAddString(papszGeomCol, "");
     254             :         }
     255             :         else
     256             :         {
     257           0 :             char *pszCBracket = strstr(pszOBracket, ")");
     258             : 
     259           0 :             if (pszCBracket != nullptr)
     260           0 :                 *pszCBracket = '\0';
     261             : 
     262           0 :             *pszOBracket = '\0';
     263           0 :             papszTables = CSLAddString(papszTables, pszDelimiter + 1);
     264           0 :             papszGeomCol = CSLAddString(papszGeomCol, pszOBracket + 1);
     265             :         }
     266           0 :         *pszDelimiter = '\0';
     267             :     }
     268             : 
     269             :     /* -------------------------------------------------------------------- */
     270             :     /*      Split out userid, password and DSN.  The general form is        */
     271             :     /*      user/password@dsn.  But if there are no @ characters the        */
     272             :     /*      whole thing is assumed to be a DSN.                             */
     273             :     /* -------------------------------------------------------------------- */
     274           0 :     std::string osUserId;
     275           0 :     std::string osPassword;
     276           0 :     std::string osDSN;
     277             : 
     278           0 :     const char *pszAt = strchr(pszWrkName, '@');
     279           0 :     if (pszAt == nullptr)
     280             :     {
     281           0 :         osDSN = pszWrkName;
     282             :     }
     283             :     else
     284             :     {
     285           0 :         osDSN = pszAt + 1;
     286           0 :         osUserId.assign(pszWrkName, pszAt - pszWrkName);
     287           0 :         const auto nSlashPos = osUserId.find('/');
     288           0 :         if (nSlashPos != std::string::npos)
     289             :         {
     290           0 :             osPassword = osUserId.substr(nSlashPos + 1);
     291           0 :             osUserId.resize(nSlashPos);
     292             :         }
     293             :     }
     294             : 
     295           0 :     CPLFree(pszWrkName);
     296             : 
     297             :     /* -------------------------------------------------------------------- */
     298             :     /*      Initialize based on the DSN.                                    */
     299             :     /* -------------------------------------------------------------------- */
     300           0 :     CPLDebug("OGR_ODBC",
     301             :              "EstablishSession(DSN:\"%s\", userid:\"%s\", password:\"%s\")",
     302             :              osDSN.c_str(), osUserId.c_str(), osPassword.c_str());
     303             : 
     304           0 :     if (!oSession.EstablishSession(
     305           0 :             osDSN.c_str(), osUserId.empty() ? nullptr : osUserId.c_str(),
     306           0 :             osPassword.empty() ? nullptr : osPassword.c_str()))
     307             :     {
     308           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     309             :                  "Unable to initialize ODBC connection to DSN for %s,\n"
     310             :                  "%s",
     311             :                  pszNewName + strlen(ODBC_PREFIX), oSession.GetLastError());
     312           0 :         CSLDestroy(papszTables);
     313           0 :         CSLDestroy(papszGeomCol);
     314           0 :         CPLFree(pszSRIDCol);
     315           0 :         CPLFree(pszSRTextCol);
     316           0 :         CPLFree(pszSRSTableName);
     317           0 :         return FALSE;
     318             :     }
     319             : 
     320             :     /* -------------------------------------------------------------------- */
     321             :     /*      If no explicit list of tables was given, check for a list in    */
     322             :     /*      a geometry_columns table.                                       */
     323             :     /* -------------------------------------------------------------------- */
     324           0 :     if (papszTables == nullptr)
     325             :     {
     326           0 :         CPLODBCStatement oStmt(&oSession);
     327             : 
     328           0 :         oStmt.Append("SELECT f_table_name, f_geometry_column, geometry_type"
     329             :                      " FROM geometry_columns");
     330           0 :         if (oStmt.ExecuteSQL())
     331             :         {
     332           0 :             while (oStmt.Fetch())
     333             :             {
     334           0 :                 papszTables = CSLAddString(papszTables, oStmt.GetColData(0));
     335           0 :                 papszGeomCol = CSLAddString(papszGeomCol, oStmt.GetColData(1));
     336             :             }
     337             :         }
     338             :     }
     339             : 
     340             :     /* -------------------------------------------------------------------- */
     341             :     /*      Otherwise our final resort is to return all tables as           */
     342             :     /*      non-spatial tables.                                             */
     343             :     /* -------------------------------------------------------------------- */
     344           0 :     if (papszTables == nullptr)
     345             :     {
     346           0 :         CPLODBCStatement oTableList(&oSession);
     347             : 
     348           0 :         if (oTableList.GetTables())
     349             :         {
     350           0 :             while (oTableList.Fetch())
     351             :             {
     352           0 :                 const char *pszSchema = oTableList.GetColData(1);
     353           0 :                 CPLString osLayerName;
     354             : 
     355           0 :                 if (pszSchema != nullptr && strlen(pszSchema) > 0)
     356             :                 {
     357           0 :                     osLayerName = pszSchema;
     358           0 :                     osLayerName += ".";
     359             :                 }
     360             : 
     361           0 :                 osLayerName += oTableList.GetColData(2);
     362             : 
     363           0 :                 papszTables = CSLAddString(papszTables, osLayerName);
     364             : 
     365           0 :                 papszGeomCol = CSLAddString(papszGeomCol, "");
     366             :             }
     367             :         }
     368             :     }
     369             : 
     370             :     /* -------------------------------------------------------------------- */
     371             :     /*      If we have an explicit list of requested tables, use them       */
     372             :     /*      (non-spatial).                                                  */
     373             :     /* -------------------------------------------------------------------- */
     374           0 :     for (int iTable = 0;
     375           0 :          papszTables != nullptr && papszTables[iTable] != nullptr; iTable++)
     376             :     {
     377           0 :         if (strlen(papszGeomCol[iTable]) > 0)
     378           0 :             OpenTable(papszTables[iTable], papszGeomCol[iTable]);
     379             :         else
     380           0 :             OpenTable(papszTables[iTable], nullptr);
     381             :     }
     382             : 
     383           0 :     CSLDestroy(papszTables);
     384           0 :     CSLDestroy(papszGeomCol);
     385             : 
     386             : #if 0
     387             :     // NOTE: nothing uses the SRS cache currently. Hence disabled.
     388             : 
     389             :     /* -------------------------------------------------------------------- */
     390             :     /*      If no explicit list of tables was given, check for a list in    */
     391             :     /*      a geometry_columns table.                                       */
     392             :     /* -------------------------------------------------------------------- */
     393             :     if (pszSRSTableName)
     394             :     {
     395             :         CPLODBCStatement oSRSList(&oSession);
     396             : 
     397             :         if (!pszSRTextCol)
     398             :             pszSRTextCol = CPLStrdup("srtext");
     399             :         if (!pszSRIDCol)
     400             :             pszSRIDCol = CPLStrdup("srid");
     401             : 
     402             :         oSRSList.Append("SELECT ");
     403             :         oSRSList.Append(pszSRIDCol);
     404             :         oSRSList.Append(",");
     405             :         oSRSList.Append(pszSRTextCol);
     406             :         oSRSList.Append(" FROM ");
     407             :         oSRSList.Append(pszSRSTableName);
     408             : 
     409             :         CPLDebug("OGR_ODBC", "ExecuteSQL(%s) to read SRS table",
     410             :                  oSRSList.GetCommand());
     411             :         if (oSRSList.ExecuteSQL())
     412             :         {
     413             :             while (oSRSList.Fetch())
     414             :             {
     415             :                 const char *pszSRID = oSRSList.GetColData(pszSRIDCol);
     416             :                 if (!pszSRID)
     417             :                     continue;
     418             : 
     419             :                 const char *pszSRText = oSRSList.GetColData(pszSRTextCol);
     420             : 
     421             :                 if (pszSRText)
     422             :                 {
     423             :                     std::unique_ptr<OGRSpatialReference, OGRSpatialReferenceReleaser> poSRS(new OGRSpatialReference());
     424             :                     poSRS->SetAxisMappingStrategy(
     425             :                         OAMS_TRADITIONAL_GIS_ORDER);
     426             :                     if (poSRS->importFromWkt(pszSRText) == OGRERR_NONE )
     427             :                     {
     428             :                         m_oSRSCache[atoi(pszSRID)] = std::move(poSRS);
     429             :                     }
     430             :                 }
     431             :             }
     432             :         }
     433             :     }
     434             : #endif
     435             : 
     436           0 :     if (pszSRIDCol)
     437           0 :         CPLFree(pszSRIDCol);
     438           0 :     if (pszSRTextCol)
     439           0 :         CPLFree(pszSRTextCol);
     440           0 :     if (pszSRSTableName)
     441           0 :         CPLFree(pszSRSTableName);
     442             : 
     443           0 :     return TRUE;
     444             : }
     445             : 
     446             : /************************************************************************/
     447             : /*                             OpenTable()                              */
     448             : /************************************************************************/
     449             : 
     450           0 : int OGRODBCDataSource::OpenTable(const char *pszNewName, const char *pszGeomCol)
     451             : {
     452             :     /* -------------------------------------------------------------------- */
     453             :     /*      Create the layer object.                                        */
     454             :     /* -------------------------------------------------------------------- */
     455           0 :     OGRODBCTableLayer *poLayer = new OGRODBCTableLayer(this, m_nStatementFlags);
     456             : 
     457           0 :     if (poLayer->Initialize(pszNewName, pszGeomCol))
     458             :     {
     459           0 :         delete poLayer;
     460           0 :         return FALSE;
     461             :     }
     462             : 
     463             :     /* -------------------------------------------------------------------- */
     464             :     /*      Add layer to data source layer list.                            */
     465             :     /* -------------------------------------------------------------------- */
     466           0 :     papoLayers = (OGRODBCLayer **)CPLRealloc(
     467           0 :         papoLayers, sizeof(OGRODBCLayer *) * (nLayers + 1));
     468           0 :     papoLayers[nLayers++] = poLayer;
     469             : 
     470           0 :     return TRUE;
     471             : }
     472             : 
     473             : /************************************************************************/
     474             : /*                           TestCapability()                           */
     475             : /************************************************************************/
     476             : 
     477           0 : int OGRODBCDataSource::TestCapability(CPL_UNUSED const char *pszCap)
     478             : {
     479           0 :     return FALSE;
     480             : }
     481             : 
     482             : /************************************************************************/
     483             : /*                              GetLayer()                              */
     484             : /************************************************************************/
     485             : 
     486           0 : OGRLayer *OGRODBCDataSource::GetLayer(int iLayer)
     487             : 
     488             : {
     489           0 :     if (iLayer < 0 || iLayer >= nLayers)
     490           0 :         return nullptr;
     491             :     else
     492           0 :         return papoLayers[iLayer];
     493             : }
     494             : 
     495             : /************************************************************************/
     496             : /*                              GetLayerByName()                        */
     497             : /************************************************************************/
     498             : 
     499           0 : OGRLayer *OGRODBCDataSource::GetLayerByName(const char *pszLayerName)
     500             : {
     501           0 :     OGRLayer *poLayer = GDALDataset::GetLayerByName(pszLayerName);
     502           0 :     if (poLayer != nullptr)
     503           0 :         return poLayer;
     504             : 
     505             :     // if table name doesn't exist in database, don't try any further
     506           0 :     const CPLString osLCTableName(CPLString(pszLayerName).tolower());
     507           0 :     if (m_aosAllLCTableNames.find(osLCTableName) == m_aosAllLCTableNames.end())
     508           0 :         return nullptr;
     509             : 
     510             :     // try to open the table -- if successful the table will be added to
     511             :     // papoLayers as the last item
     512           0 :     if (OpenTable(pszLayerName, nullptr))
     513           0 :         return papoLayers[nLayers - 1];
     514             :     else
     515           0 :         return nullptr;
     516             : }
     517             : 
     518             : /************************************************************************/
     519             : /*                    IsPrivateLayerName()                              */
     520             : /************************************************************************/
     521             : 
     522           0 : bool OGRODBCDataSource::IsPrivateLayerName(const CPLString &osName)
     523             : {
     524           0 :     const CPLString osLCTableName(CPLString(osName).tolower());
     525             : 
     526           0 :     return osLCTableName.size() >= 4 &&
     527           0 :            osLCTableName.substr(0, 4) == "msys";  // MS Access internal tables
     528             : }
     529             : 
     530             : /************************************************************************/
     531             : /*                    IsLayerPrivate()                                  */
     532             : /************************************************************************/
     533             : 
     534           0 : bool OGRODBCDataSource::IsLayerPrivate(int iLayer) const
     535             : {
     536           0 :     if (iLayer < 0 || iLayer >= nLayers)
     537           0 :         return false;
     538             : 
     539           0 :     const std::string osName(papoLayers[iLayer]->GetName());
     540           0 :     return IsPrivateLayerName(osName);
     541             : }
     542             : 
     543             : /************************************************************************/
     544             : /*                             ExecuteSQL()                             */
     545             : /************************************************************************/
     546             : 
     547           0 : OGRLayer *OGRODBCDataSource::ExecuteSQL(const char *pszSQLCommand,
     548             :                                         OGRGeometry *poSpatialFilter,
     549             :                                         const char *pszDialect)
     550             : 
     551             : {
     552             :     /* -------------------------------------------------------------------- */
     553             :     /*      Use generic implementation for recognized dialects              */
     554             :     /* -------------------------------------------------------------------- */
     555           0 :     if (IsGenericSQLDialect(pszDialect))
     556           0 :         return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter,
     557           0 :                                        pszDialect);
     558             : 
     559             :     /* -------------------------------------------------------------------- */
     560             :     /*      Execute statement.                                              */
     561             :     /* -------------------------------------------------------------------- */
     562             :     CPLODBCStatement *poStmt =
     563           0 :         new CPLODBCStatement(&oSession, m_nStatementFlags);
     564             : 
     565           0 :     CPLDebug("ODBC", "ExecuteSQL(%s) called.", pszSQLCommand);
     566           0 :     poStmt->Append(pszSQLCommand);
     567           0 :     if (!poStmt->ExecuteSQL())
     568             :     {
     569           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", oSession.GetLastError());
     570           0 :         delete poStmt;
     571           0 :         return nullptr;
     572             :     }
     573             : 
     574             :     /* -------------------------------------------------------------------- */
     575             :     /*      Are there result columns for this statement?                    */
     576             :     /* -------------------------------------------------------------------- */
     577           0 :     if (poStmt->GetColCount() == 0)
     578             :     {
     579           0 :         delete poStmt;
     580           0 :         CPLErrorReset();
     581           0 :         return nullptr;
     582             :     }
     583             : 
     584             :     /* -------------------------------------------------------------------- */
     585             :     /*      Create a results layer.  It will take ownership of the          */
     586             :     /*      statement.                                                      */
     587             :     /* -------------------------------------------------------------------- */
     588             : 
     589           0 :     OGRODBCSelectLayer *poLayer = new OGRODBCSelectLayer(this, poStmt);
     590             : 
     591           0 :     if (poSpatialFilter != nullptr)
     592           0 :         poLayer->SetSpatialFilter(poSpatialFilter);
     593             : 
     594           0 :     return poLayer;
     595             : }
     596             : 
     597             : /************************************************************************/
     598             : /*                          ReleaseResultSet()                          */
     599             : /************************************************************************/
     600             : 
     601           0 : void OGRODBCDataSource::ReleaseResultSet(OGRLayer *poLayer)
     602             : 
     603             : {
     604           0 :     delete poLayer;
     605           0 : }

Generated by: LCOV version 1.14