LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/odbc - ogrodbcdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 27 219 12.3 %
Date: 2025-05-31 00:00:17 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 :                 m_aosAllLCTableNames.insert(CPLString(osLayerName).tolower());
     133             : 
     134           0 :                 aosTableNames.emplace_back(osLayerName);
     135             :             }
     136             :         }
     137             :     }
     138             :     else
     139             :     {
     140           0 :         return FALSE;
     141             :     }
     142             : 
     143             :     /* -------------------------------------------------------------------- */
     144             :     /*      Check if it is a PGeo MDB.                    */
     145             :     /* -------------------------------------------------------------------- */
     146           0 :     for (const CPLString &osTableName : aosTableNames)
     147             :     {
     148           0 :         const CPLString osLCTableName(CPLString(osTableName).tolower());
     149           0 :         if (osLCTableName == "gdb_geomcolumns" /* PGeo */)
     150           0 :             return FALSE;
     151             :     }
     152             : 
     153           0 :     const bool bListAllTables = CPLTestBool(CSLFetchNameValueDef(
     154           0 :         poOpenInfo->papszOpenOptions, "LIST_ALL_TABLES", "NO"));
     155             : 
     156             :     /* -------------------------------------------------------------------- */
     157             :     /*      Return all tables as non-spatial tables.                       */
     158             :     /* -------------------------------------------------------------------- */
     159           0 :     for (const CPLString &osTableName : aosTableNames)
     160             :     {
     161           0 :         const CPLString osLCTableName(CPLString(osTableName).tolower());
     162           0 :         if (bListAllTables || !(osLCTableName.size() >= 4 &&
     163           0 :                                 osLCTableName.substr(0, 4) ==
     164             :                                     "msys")  // MS Access internal tables
     165             :         )
     166             :         {
     167           0 :             OpenTable(osTableName, nullptr);
     168             :         }
     169             :     }
     170             : 
     171           0 :     return TRUE;
     172             : }
     173             : 
     174             : /************************************************************************/
     175             : /*                                Open()                                */
     176             : /************************************************************************/
     177             : 
     178           2 : int OGRODBCDataSource::Open(GDALOpenInfo *poOpenInfo)
     179             : {
     180           2 :     CPLAssert(nLayers == 0);
     181             : 
     182           2 :     const char *pszNewName = poOpenInfo->pszFilename;
     183             : 
     184           2 :     constexpr const char *ODBC_PREFIX = "ODBC:";
     185           4 :     if (!STARTS_WITH_CI(pszNewName, ODBC_PREFIX) &&
     186           2 :         OGRODBCDriverIsSupportedMsAccessFileExtension(
     187           4 :             CPLGetExtensionSafe(pszNewName).c_str()))
     188           2 :         return OpenMDB(poOpenInfo);
     189             : 
     190             :     /* -------------------------------------------------------------------- */
     191             :     /*      Start parsing dataset name from the end of string, fetching     */
     192             :     /*      the name of spatial reference table and names for SRID and      */
     193             :     /*      SRTEXT columns first.                                           */
     194             :     /* -------------------------------------------------------------------- */
     195           0 :     char *pszWrkName = CPLStrdup(pszNewName + strlen(ODBC_PREFIX));
     196           0 :     char **papszTables = nullptr;
     197           0 :     char **papszGeomCol = nullptr;
     198           0 :     char *pszSRSTableName = nullptr;
     199           0 :     char *pszSRIDCol = nullptr;
     200           0 :     char *pszSRTextCol = nullptr;
     201           0 :     char *pszDelimiter = nullptr;
     202             : 
     203           0 :     if ((pszDelimiter = strrchr(pszWrkName, ':')) != nullptr)
     204             :     {
     205           0 :         char *pszOBracket = strchr(pszDelimiter + 1, '(');
     206             : 
     207           0 :         if (strchr(pszDelimiter, '\\') != nullptr ||
     208           0 :             strchr(pszDelimiter, '/') != nullptr)
     209             :         {
     210             :             /*
     211             :             ** if there are special tokens then this isn't really
     212             :             ** the srs table name, so avoid further processing.
     213             :             */
     214             :         }
     215           0 :         else if (pszOBracket == nullptr)
     216             :         {
     217           0 :             pszSRSTableName = CPLStrdup(pszDelimiter + 1);
     218           0 :             *pszDelimiter = '\0';
     219             :         }
     220             :         else
     221             :         {
     222           0 :             char *pszCBracket = strchr(pszOBracket, ')');
     223           0 :             if (pszCBracket != nullptr)
     224           0 :                 *pszCBracket = '\0';
     225             : 
     226           0 :             char *pszComma = strchr(pszOBracket, ',');
     227           0 :             if (pszComma != nullptr)
     228             :             {
     229           0 :                 *pszComma = '\0';
     230           0 :                 pszSRIDCol = CPLStrdup(pszComma + 1);
     231             :             }
     232             : 
     233           0 :             *pszOBracket = '\0';
     234           0 :             pszSRSTableName = CPLStrdup(pszDelimiter + 1);
     235           0 :             pszSRTextCol = CPLStrdup(pszOBracket + 1);
     236             : 
     237           0 :             *pszDelimiter = '\0';
     238             :         }
     239             :     }
     240             : 
     241             :     /* -------------------------------------------------------------------- */
     242             :     /*      Strip off any comma delimited set of tables names to access     */
     243             :     /*      from the end of the string first.  Also allow an optional       */
     244             :     /*      bracketed geometry column name after the table name.            */
     245             :     /* -------------------------------------------------------------------- */
     246           0 :     while ((pszDelimiter = strrchr(pszWrkName, ',')) != nullptr)
     247             :     {
     248           0 :         char *pszOBracket = strstr(pszDelimiter + 1, "(");
     249           0 :         if (pszOBracket == nullptr)
     250             :         {
     251           0 :             papszTables = CSLAddString(papszTables, pszDelimiter + 1);
     252           0 :             papszGeomCol = CSLAddString(papszGeomCol, "");
     253             :         }
     254             :         else
     255             :         {
     256           0 :             char *pszCBracket = strstr(pszOBracket, ")");
     257             : 
     258           0 :             if (pszCBracket != nullptr)
     259           0 :                 *pszCBracket = '\0';
     260             : 
     261           0 :             *pszOBracket = '\0';
     262           0 :             papszTables = CSLAddString(papszTables, pszDelimiter + 1);
     263           0 :             papszGeomCol = CSLAddString(papszGeomCol, pszOBracket + 1);
     264             :         }
     265           0 :         *pszDelimiter = '\0';
     266             :     }
     267             : 
     268             :     /* -------------------------------------------------------------------- */
     269             :     /*      Split out userid, password and DSN.  The general form is        */
     270             :     /*      user/password@dsn.  But if there are no @ characters the        */
     271             :     /*      whole thing is assumed to be a DSN.                             */
     272             :     /* -------------------------------------------------------------------- */
     273           0 :     std::string osUserId;
     274           0 :     std::string osPassword;
     275           0 :     std::string osDSN;
     276             : 
     277           0 :     const char *pszAt = strchr(pszWrkName, '@');
     278           0 :     if (pszAt == nullptr)
     279             :     {
     280           0 :         osDSN = pszWrkName;
     281             :     }
     282             :     else
     283             :     {
     284           0 :         osDSN = pszAt + 1;
     285           0 :         osUserId.assign(pszWrkName, pszAt - pszWrkName);
     286           0 :         const auto nSlashPos = osUserId.find('/');
     287           0 :         if (nSlashPos != std::string::npos)
     288             :         {
     289           0 :             osPassword = osUserId.substr(nSlashPos + 1);
     290           0 :             osUserId.resize(nSlashPos);
     291             :         }
     292             :     }
     293             : 
     294           0 :     CPLFree(pszWrkName);
     295             : 
     296             :     /* -------------------------------------------------------------------- */
     297             :     /*      Initialize based on the DSN.                                    */
     298             :     /* -------------------------------------------------------------------- */
     299           0 :     CPLDebug("OGR_ODBC",
     300             :              "EstablishSession(DSN:\"%s\", userid:\"%s\", password:\"%s\")",
     301             :              osDSN.c_str(), osUserId.c_str(), osPassword.c_str());
     302             : 
     303           0 :     if (!oSession.EstablishSession(
     304           0 :             osDSN.c_str(), osUserId.empty() ? nullptr : osUserId.c_str(),
     305           0 :             osPassword.empty() ? nullptr : osPassword.c_str()))
     306             :     {
     307           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     308             :                  "Unable to initialize ODBC connection to DSN for %s,\n"
     309             :                  "%s",
     310             :                  pszNewName + strlen(ODBC_PREFIX), oSession.GetLastError());
     311           0 :         CSLDestroy(papszTables);
     312           0 :         CSLDestroy(papszGeomCol);
     313           0 :         CPLFree(pszSRIDCol);
     314           0 :         CPLFree(pszSRTextCol);
     315           0 :         CPLFree(pszSRSTableName);
     316           0 :         return FALSE;
     317             :     }
     318             : 
     319             :     /* -------------------------------------------------------------------- */
     320             :     /*      If no explicit list of tables was given, check for a list in    */
     321             :     /*      a geometry_columns table.                                       */
     322             :     /* -------------------------------------------------------------------- */
     323           0 :     if (papszTables == nullptr)
     324             :     {
     325           0 :         CPLODBCStatement oStmt(&oSession);
     326             : 
     327           0 :         oStmt.Append("SELECT f_table_name, f_geometry_column, geometry_type"
     328             :                      " FROM geometry_columns");
     329           0 :         if (oStmt.ExecuteSQL())
     330             :         {
     331           0 :             while (oStmt.Fetch())
     332             :             {
     333           0 :                 papszTables = CSLAddString(papszTables, oStmt.GetColData(0));
     334           0 :                 papszGeomCol = CSLAddString(papszGeomCol, oStmt.GetColData(1));
     335             :             }
     336             :         }
     337             :     }
     338             : 
     339             :     /* -------------------------------------------------------------------- */
     340             :     /*      Otherwise our final resort is to return all tables as           */
     341             :     /*      non-spatial tables.                                             */
     342             :     /* -------------------------------------------------------------------- */
     343           0 :     if (papszTables == nullptr)
     344             :     {
     345           0 :         CPLODBCStatement oTableList(&oSession);
     346             : 
     347           0 :         if (oTableList.GetTables())
     348             :         {
     349           0 :             while (oTableList.Fetch())
     350             :             {
     351           0 :                 const char *pszSchema = oTableList.GetColData(1);
     352           0 :                 CPLString osLayerName;
     353             : 
     354           0 :                 if (pszSchema != nullptr && strlen(pszSchema) > 0)
     355             :                 {
     356           0 :                     osLayerName = pszSchema;
     357           0 :                     osLayerName += ".";
     358             :                 }
     359             : 
     360           0 :                 osLayerName += oTableList.GetColData(2);
     361             : 
     362           0 :                 papszTables = CSLAddString(papszTables, osLayerName);
     363             : 
     364           0 :                 papszGeomCol = CSLAddString(papszGeomCol, "");
     365             :             }
     366             :         }
     367             :     }
     368             : 
     369             :     /* -------------------------------------------------------------------- */
     370             :     /*      If we have an explicit list of requested tables, use them       */
     371             :     /*      (non-spatial).                                                  */
     372             :     /* -------------------------------------------------------------------- */
     373           0 :     for (int iTable = 0;
     374           0 :          papszTables != nullptr && papszTables[iTable] != nullptr; iTable++)
     375             :     {
     376           0 :         if (strlen(papszGeomCol[iTable]) > 0)
     377           0 :             OpenTable(papszTables[iTable], papszGeomCol[iTable]);
     378             :         else
     379           0 :             OpenTable(papszTables[iTable], nullptr);
     380             :     }
     381             : 
     382           0 :     CSLDestroy(papszTables);
     383           0 :     CSLDestroy(papszGeomCol);
     384             : 
     385             : #if 0
     386             :     // NOTE: nothing uses the SRS cache currently. Hence disabled.
     387             : 
     388             :     /* -------------------------------------------------------------------- */
     389             :     /*      If no explicit list of tables was given, check for a list in    */
     390             :     /*      a geometry_columns table.                                       */
     391             :     /* -------------------------------------------------------------------- */
     392             :     if (pszSRSTableName)
     393             :     {
     394             :         CPLODBCStatement oSRSList(&oSession);
     395             : 
     396             :         if (!pszSRTextCol)
     397             :             pszSRTextCol = CPLStrdup("srtext");
     398             :         if (!pszSRIDCol)
     399             :             pszSRIDCol = CPLStrdup("srid");
     400             : 
     401             :         oSRSList.Append("SELECT ");
     402             :         oSRSList.Append(pszSRIDCol);
     403             :         oSRSList.Append(",");
     404             :         oSRSList.Append(pszSRTextCol);
     405             :         oSRSList.Append(" FROM ");
     406             :         oSRSList.Append(pszSRSTableName);
     407             : 
     408             :         CPLDebug("OGR_ODBC", "ExecuteSQL(%s) to read SRS table",
     409             :                  oSRSList.GetCommand());
     410             :         if (oSRSList.ExecuteSQL())
     411             :         {
     412             :             while (oSRSList.Fetch())
     413             :             {
     414             :                 const char *pszSRID = oSRSList.GetColData(pszSRIDCol);
     415             :                 if (!pszSRID)
     416             :                     continue;
     417             : 
     418             :                 const char *pszSRText = oSRSList.GetColData(pszSRTextCol);
     419             : 
     420             :                 if (pszSRText)
     421             :                 {
     422             :                     std::unique_ptr<OGRSpatialReference, OGRSpatialReferenceReleaser> poSRS(new OGRSpatialReference());
     423             :                     poSRS->SetAxisMappingStrategy(
     424             :                         OAMS_TRADITIONAL_GIS_ORDER);
     425             :                     if (poSRS->importFromWkt(pszSRText) == OGRERR_NONE )
     426             :                     {
     427             :                         m_oSRSCache[atoi(pszSRID)] = std::move(poSRS);
     428             :                     }
     429             :                 }
     430             :             }
     431             :         }
     432             :     }
     433             : #endif
     434             : 
     435           0 :     if (pszSRIDCol)
     436           0 :         CPLFree(pszSRIDCol);
     437           0 :     if (pszSRTextCol)
     438           0 :         CPLFree(pszSRTextCol);
     439           0 :     if (pszSRSTableName)
     440           0 :         CPLFree(pszSRSTableName);
     441             : 
     442           0 :     return TRUE;
     443             : }
     444             : 
     445             : /************************************************************************/
     446             : /*                             OpenTable()                              */
     447             : /************************************************************************/
     448             : 
     449           0 : int OGRODBCDataSource::OpenTable(const char *pszNewName, const char *pszGeomCol)
     450             : {
     451             :     /* -------------------------------------------------------------------- */
     452             :     /*      Create the layer object.                                        */
     453             :     /* -------------------------------------------------------------------- */
     454           0 :     OGRODBCTableLayer *poLayer = new OGRODBCTableLayer(this, m_nStatementFlags);
     455             : 
     456           0 :     if (poLayer->Initialize(pszNewName, pszGeomCol))
     457             :     {
     458           0 :         delete poLayer;
     459           0 :         return FALSE;
     460             :     }
     461             : 
     462             :     /* -------------------------------------------------------------------- */
     463             :     /*      Add layer to data source layer list.                            */
     464             :     /* -------------------------------------------------------------------- */
     465           0 :     papoLayers = (OGRODBCLayer **)CPLRealloc(
     466           0 :         papoLayers, sizeof(OGRODBCLayer *) * (nLayers + 1));
     467           0 :     papoLayers[nLayers++] = poLayer;
     468             : 
     469           0 :     return TRUE;
     470             : }
     471             : 
     472             : /************************************************************************/
     473             : /*                           TestCapability()                           */
     474             : /************************************************************************/
     475             : 
     476           0 : int OGRODBCDataSource::TestCapability(CPL_UNUSED const char *pszCap)
     477             : {
     478           0 :     return FALSE;
     479             : }
     480             : 
     481             : /************************************************************************/
     482             : /*                              GetLayer()                              */
     483             : /************************************************************************/
     484             : 
     485           0 : OGRLayer *OGRODBCDataSource::GetLayer(int iLayer)
     486             : 
     487             : {
     488           0 :     if (iLayer < 0 || iLayer >= nLayers)
     489           0 :         return nullptr;
     490             :     else
     491           0 :         return papoLayers[iLayer];
     492             : }
     493             : 
     494             : /************************************************************************/
     495             : /*                              GetLayerByName()                        */
     496             : /************************************************************************/
     497             : 
     498           0 : OGRLayer *OGRODBCDataSource::GetLayerByName(const char *pszLayerName)
     499             : {
     500           0 :     OGRLayer *poLayer = GDALDataset::GetLayerByName(pszLayerName);
     501           0 :     if (poLayer != nullptr)
     502           0 :         return poLayer;
     503             : 
     504             :     // if table name doesn't exist in database, don't try any further
     505           0 :     const CPLString osLCTableName(CPLString(pszLayerName).tolower());
     506           0 :     if (m_aosAllLCTableNames.find(osLCTableName) == m_aosAllLCTableNames.end())
     507           0 :         return nullptr;
     508             : 
     509             :     // try to open the table -- if successful the table will be added to
     510             :     // papoLayers as the last item
     511           0 :     if (OpenTable(pszLayerName, nullptr))
     512           0 :         return papoLayers[nLayers - 1];
     513             :     else
     514           0 :         return nullptr;
     515             : }
     516             : 
     517             : /************************************************************************/
     518             : /*                    IsPrivateLayerName()                              */
     519             : /************************************************************************/
     520             : 
     521           0 : bool OGRODBCDataSource::IsPrivateLayerName(const CPLString &osName)
     522             : {
     523           0 :     const CPLString osLCTableName(CPLString(osName).tolower());
     524             : 
     525           0 :     return osLCTableName.size() >= 4 &&
     526           0 :            osLCTableName.substr(0, 4) == "msys";  // MS Access internal tables
     527             : }
     528             : 
     529             : /************************************************************************/
     530             : /*                    IsLayerPrivate()                                  */
     531             : /************************************************************************/
     532             : 
     533           0 : bool OGRODBCDataSource::IsLayerPrivate(int iLayer) const
     534             : {
     535           0 :     if (iLayer < 0 || iLayer >= nLayers)
     536           0 :         return false;
     537             : 
     538           0 :     const std::string osName(papoLayers[iLayer]->GetName());
     539           0 :     return IsPrivateLayerName(osName);
     540             : }
     541             : 
     542             : /************************************************************************/
     543             : /*                             ExecuteSQL()                             */
     544             : /************************************************************************/
     545             : 
     546           0 : OGRLayer *OGRODBCDataSource::ExecuteSQL(const char *pszSQLCommand,
     547             :                                         OGRGeometry *poSpatialFilter,
     548             :                                         const char *pszDialect)
     549             : 
     550             : {
     551             :     /* -------------------------------------------------------------------- */
     552             :     /*      Use generic implementation for recognized dialects              */
     553             :     /* -------------------------------------------------------------------- */
     554           0 :     if (IsGenericSQLDialect(pszDialect))
     555           0 :         return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter,
     556           0 :                                        pszDialect);
     557             : 
     558             :     /* -------------------------------------------------------------------- */
     559             :     /*      Execute statement.                                              */
     560             :     /* -------------------------------------------------------------------- */
     561             :     CPLODBCStatement *poStmt =
     562           0 :         new CPLODBCStatement(&oSession, m_nStatementFlags);
     563             : 
     564           0 :     CPLDebug("ODBC", "ExecuteSQL(%s) called.", pszSQLCommand);
     565           0 :     poStmt->Append(pszSQLCommand);
     566           0 :     if (!poStmt->ExecuteSQL())
     567             :     {
     568           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", oSession.GetLastError());
     569           0 :         delete poStmt;
     570           0 :         return nullptr;
     571             :     }
     572             : 
     573             :     /* -------------------------------------------------------------------- */
     574             :     /*      Are there result columns for this statement?                    */
     575             :     /* -------------------------------------------------------------------- */
     576           0 :     if (poStmt->GetColCount() == 0)
     577             :     {
     578           0 :         delete poStmt;
     579           0 :         CPLErrorReset();
     580           0 :         return nullptr;
     581             :     }
     582             : 
     583             :     /* -------------------------------------------------------------------- */
     584             :     /*      Create a results layer.  It will take ownership of the          */
     585             :     /*      statement.                                                      */
     586             :     /* -------------------------------------------------------------------- */
     587             : 
     588           0 :     OGRODBCSelectLayer *poLayer = new OGRODBCSelectLayer(this, poStmt);
     589             : 
     590           0 :     if (poSpatialFilter != nullptr)
     591           0 :         poLayer->SetSpatialFilter(poSpatialFilter);
     592             : 
     593           0 :     return poLayer;
     594             : }
     595             : 
     596             : /************************************************************************/
     597             : /*                          ReleaseResultSet()                          */
     598             : /************************************************************************/
     599             : 
     600           0 : void OGRODBCDataSource::ReleaseResultSet(OGRLayer *poLayer)
     601             : 
     602             : {
     603           0 :     delete poLayer;
     604           0 : }

Generated by: LCOV version 1.14