LCOV - code coverage report
Current view: directory - ogr/ogrsf_frmts/sqlite - ogrsqliteselectlayer.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 216 195 90.3 %
Date: 2013-03-30 Functions: 11 11 100.0 %

       1                 : /******************************************************************************
       2                 :  * $Id: ogrsqliteselectlayer.cpp 25694 2013-03-01 17:26:40Z rouault $
       3                 :  *
       4                 :  * Project:  OpenGIS Simple Features Reference Implementation
       5                 :  * Purpose:  Implements OGRSQLiteSelectLayer class, layer access to the results
       6                 :  *           of a SELECT statement executed via ExecuteSQL().
       7                 :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       8                 :  *
       9                 :  ******************************************************************************
      10                 :  * Copyright (c) 2004, Frank Warmerdam
      11                 :  *
      12                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      13                 :  * copy of this software and associated documentation files (the "Software"),
      14                 :  * to deal in the Software without restriction, including without limitation
      15                 :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      16                 :  * and/or sell copies of the Software, and to permit persons to whom the
      17                 :  * Software is furnished to do so, subject to the following conditions:
      18                 :  *
      19                 :  * The above copyright notice and this permission notice shall be included
      20                 :  * in all copies or substantial portions of the Software.
      21                 :  *
      22                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      23                 :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      24                 :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      25                 :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      26                 :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      27                 :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      28                 :  * DEALINGS IN THE SOFTWARE.
      29                 :  ****************************************************************************/
      30                 : 
      31                 : #include "cpl_conv.h"
      32                 : #include "ogr_sqlite.h"
      33                 : 
      34                 : CPL_CVSID("$Id: ogrsqliteselectlayer.cpp 25694 2013-03-01 17:26:40Z rouault $");
      35                 : /************************************************************************/
      36                 : /*                        OGRSQLiteSelectLayer()                        */
      37                 : /************************************************************************/
      38                 : 
      39             807 : OGRSQLiteSelectLayer::OGRSQLiteSelectLayer( OGRSQLiteDataSource *poDSIn,
      40                 :                                             CPLString osSQLIn,
      41                 :                                             sqlite3_stmt *hStmtIn,
      42                 :                                             int bUseStatementForGetNextFeature,
      43             807 :                                             int bEmptyLayer )
      44                 : 
      45                 : {
      46             807 :     poDS = poDSIn;
      47                 : 
      48             807 :     iNextShapeId = 0;
      49             807 :     poFeatureDefn = NULL;
      50             807 :     bAllowResetReadingEvenIfIndexAtZero = FALSE;
      51                 : 
      52             807 :     std::set<CPLString> aosEmpty;
      53             807 :     BuildFeatureDefn( "SELECT", hStmtIn, aosEmpty );
      54                 : 
      55             807 :     if( bUseStatementForGetNextFeature )
      56                 :     {
      57             652 :         hStmt = hStmtIn;
      58             652 :         bDoStep = FALSE;
      59                 : 
      60                 :         // Try to extract SRS from first geometry
      61             652 :         if( !bEmptyLayer && osGeomColumn.size() != 0 )
      62                 :         {
      63             202 :             int    nRawColumns = sqlite3_column_count( hStmt );
      64             750 :             for( int iCol = 0; iCol < nRawColumns; iCol++ )
      65                 :             {
      66                 :                 int nBytes;
      67             699 :                 if( sqlite3_column_type( hStmt, iCol ) == SQLITE_BLOB &&
      68                 :                     strcmp(OGRSQLiteParamsUnquote(sqlite3_column_name( hStmt, iCol )).c_str(), osGeomColumn.c_str()) == 0 &&
      69                 :                     (nBytes = sqlite3_column_bytes( hStmt, iCol )) > 39 )
      70                 :                 {
      71             151 :                     const GByte* pabyBlob = (const GByte*)sqlite3_column_blob( hStmt, iCol );
      72             151 :                     int eByteOrder = pabyBlob[1];
      73             437 :                     if( pabyBlob[0] == 0x00 &&
      74                 :                         (eByteOrder == wkbNDR || eByteOrder == wkbXDR) &&
      75             143 :                         pabyBlob[38] == 0x7C )
      76                 :                     {
      77             143 :                         memcpy(&nSRSId, pabyBlob + 2, 4);
      78                 : #ifdef CPL_LSB
      79             143 :                         if( eByteOrder != wkbNDR)
      80               0 :                             CPL_SWAP32PTR(&nSRSId);
      81                 : #else
      82                 :                         if( eByteOrder == wkbNDR)
      83                 :                             CPL_SWAP32PTR(&nSRSId);
      84                 : #endif
      85             143 :                         CPLPushErrorHandler(CPLQuietErrorHandler);
      86             143 :                         poSRS = poDS->FetchSRS( nSRSId );
      87             143 :                         CPLPopErrorHandler();
      88             143 :                         if( poSRS != NULL )
      89             116 :                             poSRS->Reference();
      90                 :                         else
      91              27 :                             CPLErrorReset();
      92                 :                     }
      93                 : #ifdef SQLITE_HAS_COLUMN_METADATA
      94                 :                     else
      95                 :                     {
      96               8 :                         const char* pszTableName = sqlite3_column_table_name( hStmt, iCol );
      97               8 :                         if( pszTableName != NULL )
      98                 :                         {
      99               8 :                             OGRLayer* poLayer = poDS->GetLayerByName(pszTableName);
     100               8 :                             if( poLayer != NULL )
     101                 :                             {
     102               8 :                                 poSRS = poLayer->GetSpatialRef();
     103               8 :                                 if( poSRS != NULL )
     104               4 :                                     poSRS->Reference();
     105                 :                             }
     106                 :                         }
     107                 :                     }
     108                 : #endif
     109             151 :                     break;
     110                 :                 }
     111                 :             }
     112                 :         }
     113                 :     }
     114                 :     else
     115             155 :         sqlite3_finalize( hStmtIn );
     116                 : 
     117             807 :     osSQLBase = osSQLIn;
     118             807 :     osSQLCurrent = osSQLIn;
     119             807 :     this->bEmptyLayer = bEmptyLayer;
     120             807 :     bSpatialFilterInSQL = TRUE;
     121             807 : }
     122                 : 
     123                 : /************************************************************************/
     124                 : /*                            ResetReading()                            */
     125                 : /************************************************************************/
     126                 : 
     127             144 : void OGRSQLiteSelectLayer::ResetReading()
     128                 : 
     129                 : {
     130             144 :     if( iNextShapeId > 0 || bAllowResetReadingEvenIfIndexAtZero )
     131                 :     {
     132              91 :         OGRSQLiteLayer::ResetReading();
     133              91 :         bAllowResetReadingEvenIfIndexAtZero = FALSE;
     134                 :     }
     135             144 : }
     136                 : 
     137                 : /************************************************************************/
     138                 : /*                           GetNextFeature()                           */
     139                 : /************************************************************************/
     140                 : 
     141            1630 : OGRFeature *OGRSQLiteSelectLayer::GetNextFeature()
     142                 : {
     143            1630 :     if( bEmptyLayer )
     144             116 :         return NULL;
     145                 : 
     146            1514 :     return OGRSQLiteLayer::GetNextFeature();
     147                 : }
     148                 : 
     149                 : /************************************************************************/
     150                 : /*                           GetNextFeature()                           */
     151                 : /************************************************************************/
     152                 : 
     153              84 : int OGRSQLiteSelectLayer::GetFeatureCount( int bForce )
     154                 : {
     155              84 :     if( bEmptyLayer )
     156               0 :         return 0;
     157                 : 
     158              84 :     if( m_poAttrQuery == NULL &&
     159                 :         EQUALN(osSQLCurrent, "SELECT COUNT(*) FROM", strlen("SELECT COUNT(*) FROM")) &&
     160                 :         osSQLCurrent.ifind(" GROUP BY ") == std::string::npos &&
     161                 :         osSQLCurrent.ifind(" UNION ") == std::string::npos &&
     162                 :         osSQLCurrent.ifind(" INTERSECT ") == std::string::npos &&
     163                 :         osSQLCurrent.ifind(" EXCEPT ") == std::string::npos )
     164               2 :         return 1;
     165                 : 
     166              82 :     if( m_poAttrQuery != NULL || (m_poFilterGeom != NULL && !bSpatialFilterInSQL) )
     167              12 :         return OGRLayer::GetFeatureCount(bForce);
     168                 : 
     169              70 :     CPLString osFeatureCountSQL("SELECT COUNT(*) FROM (");
     170              70 :     osFeatureCountSQL += osSQLCurrent;
     171              70 :     osFeatureCountSQL += ")";
     172                 : 
     173              70 :     CPLDebug("SQLITE", "Running %s", osFeatureCountSQL.c_str());
     174                 : 
     175                 : /* -------------------------------------------------------------------- */
     176                 : /*      Execute.                                                        */
     177                 : /* -------------------------------------------------------------------- */
     178              70 :     char *pszErrMsg = NULL;
     179                 :     char **papszResult;
     180                 :     int nRowCount, nColCount;
     181              70 :     int nResult = -1;
     182                 : 
     183              70 :     if( sqlite3_get_table( poDS->GetDB(), osFeatureCountSQL, &papszResult, 
     184                 :                            &nRowCount, &nColCount, &pszErrMsg ) != SQLITE_OK )
     185                 :     {
     186               1 :         CPLDebug("SQLITE", "Error: %s", pszErrMsg);
     187               1 :         sqlite3_free(pszErrMsg);
     188               1 :         return OGRLayer::GetFeatureCount(bForce);
     189                 :     }
     190                 : 
     191              69 :     if( nRowCount == 1 && nColCount == 1 )
     192                 :     {
     193              69 :         nResult = atoi(papszResult[1]);
     194                 :     }
     195                 : 
     196              69 :     sqlite3_free_table( papszResult );
     197                 : 
     198              69 :     return nResult;
     199                 : }
     200                 : 
     201                 : /************************************************************************/
     202                 : /*                           ResetStatement()                           */
     203                 : /************************************************************************/
     204                 : 
     205             110 : OGRErr OGRSQLiteSelectLayer::ResetStatement()
     206                 : 
     207                 : {
     208                 :     int rc;
     209                 : 
     210             110 :     ClearStatement();
     211                 : 
     212             110 :     iNextShapeId = 0;
     213             110 :     bDoStep = TRUE;
     214                 : 
     215                 : #ifdef DEBUG
     216             110 :     CPLDebug( "OGR_SQLITE", "prepare(%s)", osSQLCurrent.c_str() );
     217                 : #endif
     218                 : 
     219                 :     rc = sqlite3_prepare( poDS->GetDB(), osSQLCurrent, osSQLCurrent.size(),
     220             110 :                           &hStmt, NULL );
     221                 : 
     222             110 :     if( rc == SQLITE_OK )
     223                 :     {
     224             110 :         return OGRERR_NONE;
     225                 :     }
     226                 :     else
     227                 :     {
     228                 :         CPLError( CE_Failure, CPLE_AppDefined, 
     229                 :                   "In ResetStatement(): sqlite3_prepare(%s):\n  %s", 
     230               0 :                   osSQLCurrent.c_str(), sqlite3_errmsg(poDS->GetDB()) );
     231               0 :         hStmt = NULL;
     232               0 :         return OGRERR_FAILURE;
     233                 :     }
     234                 : }
     235                 : 
     236                 : /************************************************************************/
     237                 : /*                          SetSpatialFilter()                          */
     238                 : /************************************************************************/
     239                 : 
     240              25 : void OGRSQLiteSelectLayer::SetSpatialFilter( OGRGeometry * poGeomIn )
     241                 : 
     242                 : {
     243              25 :     bAllowResetReadingEvenIfIndexAtZero = TRUE;
     244                 : 
     245              25 :     if( InstallFilter( poGeomIn ) )
     246                 :     {
     247              17 :         bSpatialFilterInSQL = RebuildSQLWithSpatialClause();
     248                 : 
     249              17 :         ResetReading();
     250                 :     }
     251              25 : }
     252                 : 
     253                 : /************************************************************************/
     254                 : /*                            GetBaseLayer()                            */
     255                 : /************************************************************************/
     256                 : 
     257              17 : OGRSQLiteLayer* OGRSQLiteSelectLayer::GetBaseLayer(size_t& i)
     258                 : {
     259              17 :     char** papszTokens = CSLTokenizeString(osSQLBase.c_str());
     260              17 :     int bCanInsertSpatialFilter = TRUE;
     261              17 :     int nCountSelect = 0, nCountFrom = 0, nCountWhere = 0;
     262                 : 
     263             110 :     for(int iToken = 0; papszTokens[iToken] != NULL; iToken++)
     264                 :     {
     265              93 :         if (EQUAL(papszTokens[iToken], "SELECT"))
     266              17 :             nCountSelect ++;
     267              76 :         else if (EQUAL(papszTokens[iToken], "FROM"))
     268              17 :             nCountFrom ++;
     269              59 :         else if (EQUAL(papszTokens[iToken], "WHERE"))
     270               4 :             nCountWhere ++;
     271             220 :         else if (EQUAL(papszTokens[iToken], "UNION") ||
     272              55 :                  EQUAL(papszTokens[iToken], "JOIN") ||
     273              55 :                  EQUAL(papszTokens[iToken], "INTERSECT") ||
     274              55 :                  EQUAL(papszTokens[iToken], "EXCEPT"))
     275                 :         {
     276               0 :             bCanInsertSpatialFilter = FALSE;
     277                 :         }
     278                 :     }
     279              17 :     CSLDestroy(papszTokens);
     280                 : 
     281              17 :     if (!(bCanInsertSpatialFilter && nCountSelect == 1 && nCountFrom == 1 && nCountWhere <= 1))
     282                 :     {
     283               0 :         CPLDebug("SQLITE", "SQL expression too complex to analyse");
     284               0 :         return NULL;
     285                 :     }
     286                 : 
     287              17 :     size_t nFromPos = osSQLBase.ifind(" from ");
     288              17 :     if (nFromPos == std::string::npos)
     289                 :     {
     290               0 :         return NULL;
     291                 :     }
     292                 : 
     293              17 :     int bInSingleQuotes = (osSQLBase[nFromPos + 6] == '\'');
     294              17 :     CPLString osBaseLayerName;
     295             197 :     for( i = nFromPos + 6 + (bInSingleQuotes ? 1 : 0);
     296                 :          i < osSQLBase.size(); i++ )
     297                 :     {
     298             188 :         if (osSQLBase[i] == '\'' && i + 1 < osSQLBase.size() &&
     299                 :             osSQLBase[i + 1] == '\'' )
     300                 :         {
     301               0 :             osBaseLayerName += osSQLBase[i];
     302               0 :             i++;
     303                 :         }
     304             188 :         else if (osSQLBase[i] == '\'' && bInSingleQuotes)
     305                 :         {
     306               2 :             i++;
     307               2 :             break;
     308                 :         }
     309             186 :         else if (osSQLBase[i] == ' ' && !bInSingleQuotes)
     310               6 :             break;
     311                 :         else
     312             180 :             osBaseLayerName += osSQLBase[i];
     313                 :     }
     314                 : 
     315              17 :     OGRSQLiteLayer* poUnderlyingLayer = NULL;
     316              17 :     if( strchr(osBaseLayerName, '(') == NULL &&
     317                 :         osGeomColumn.size() != 0 )
     318                 :     {
     319              17 :         CPLString osNewUnderlyingTableName;
     320                 :         osNewUnderlyingTableName.Printf("%s(%s)",
     321                 :                                         osBaseLayerName.c_str(),
     322              17 :                                         osGeomColumn.c_str());
     323                 :         poUnderlyingLayer =
     324              17 :             (OGRSQLiteLayer*) poDS->GetLayerByName(osNewUnderlyingTableName);
     325                 :     }
     326              17 :     if( poUnderlyingLayer == NULL )
     327              16 :         poUnderlyingLayer = (OGRSQLiteLayer*) poDS->GetLayerByName(osBaseLayerName);
     328                 : 
     329              35 :     if( poUnderlyingLayer != NULL && poSRS != NULL &&
     330               9 :         poUnderlyingLayer->GetSpatialRef() != NULL &&
     331               9 :         poSRS != poUnderlyingLayer->GetSpatialRef() &&
     332               0 :         !poSRS->IsSame(poUnderlyingLayer->GetSpatialRef()) )
     333                 :     {
     334               0 :         CPLDebug("SQLITE", "Result layer and base layer don't have the same SRS.");
     335               0 :         return NULL;
     336                 :     }
     337                 : 
     338              17 :     return poUnderlyingLayer;
     339                 : }
     340                 : 
     341                 : /************************************************************************/
     342                 : /*                    RebuildSQLWithSpatialClause()                     */
     343                 : /************************************************************************/
     344                 : 
     345              17 : int OGRSQLiteSelectLayer::RebuildSQLWithSpatialClause()
     346                 : 
     347                 : {
     348              17 :     osSQLCurrent = osSQLBase;
     349                 : 
     350              17 :     if (m_poFilterGeom == NULL)
     351                 :     {
     352               4 :         return TRUE;
     353                 :     }
     354                 : 
     355              13 :     size_t i = 0;
     356              13 :     OGRSQLiteLayer* poBaseLayer = GetBaseLayer(i);
     357              13 :     if (poBaseLayer == NULL)
     358                 :     {
     359               0 :         CPLDebug("SQLITE", "Cannot find base layer");
     360               0 :         return FALSE;
     361                 :     }
     362                 : 
     363              13 :     CPLString    osSpatialWhere = poBaseLayer->GetSpatialWhere(m_poFilterGeom);
     364              13 :     if (osSpatialWhere.size() == 0)
     365                 :     {
     366               4 :         CPLDebug("SQLITE", "Cannot get spatial where clause");
     367               4 :         return FALSE;
     368                 :     }
     369                 : 
     370              21 :     while (i < osSQLBase.size() && osSQLBase[i] == ' ')
     371               3 :         i ++;
     372                 : 
     373               9 :     if (i < osSQLBase.size() && EQUALN(osSQLBase.c_str() + i, "WHERE ", 6))
     374                 :     {
     375               2 :         osSQLCurrent = osSQLBase.substr(0, i + 6);
     376               2 :         osSQLCurrent += osSpatialWhere;
     377               2 :         osSQLCurrent += " AND (";
     378                 : 
     379               2 :         size_t nEndOfWhere = osSQLBase.ifind(" GROUP ");
     380               2 :         if (nEndOfWhere == std::string::npos)
     381               2 :             nEndOfWhere = osSQLBase.ifind(" ORDER ");
     382               2 :         if (nEndOfWhere == std::string::npos)
     383               1 :             nEndOfWhere = osSQLBase.ifind(" LIMIT ");
     384                 : 
     385               2 :         if (nEndOfWhere == std::string::npos)
     386                 :         {
     387               1 :             osSQLCurrent += osSQLBase.substr(i + 6);
     388               1 :             osSQLCurrent += ")";
     389                 :         }
     390                 :         else
     391                 :         {
     392               1 :             osSQLCurrent += osSQLBase.substr(i + 6, nEndOfWhere - (i + 6));
     393               1 :             osSQLCurrent += ")";
     394               1 :             osSQLCurrent += osSQLBase.substr(nEndOfWhere);
     395                 :         }
     396                 :     }
     397               7 :     else if (i < osSQLBase.size() &&
     398                 :              (EQUALN(osSQLBase.c_str() + i, "GROUP ", 6) ||
     399                 :               EQUALN(osSQLBase.c_str() + i, "ORDER ", 6) ||
     400                 :               EQUALN(osSQLBase.c_str() + i, "LIMIT ", 6)))
     401                 :     {
     402               1 :         osSQLCurrent = osSQLBase.substr(0, i);
     403               1 :         osSQLCurrent += " WHERE ";
     404               1 :         osSQLCurrent += osSpatialWhere;
     405               1 :         osSQLCurrent += " ";
     406               1 :         osSQLCurrent += osSQLBase.substr(i);
     407                 :     }
     408               6 :     else if (i == osSQLBase.size())
     409                 :     {
     410               6 :         osSQLCurrent = osSQLBase.substr(0, i);
     411               6 :         osSQLCurrent += " WHERE ";
     412               6 :         osSQLCurrent += osSpatialWhere;
     413                 :     }
     414                 :     else
     415                 :     {
     416               0 :         CPLDebug("SQLITE", "SQL expression too complex for the driver to insert spatial filter in it");
     417               0 :         return FALSE;
     418                 :     }
     419                 : 
     420               9 :     return TRUE;
     421                 : }
     422                 : 
     423                 : /************************************************************************/
     424                 : /*                           TestCapability()                           */
     425                 : /************************************************************************/
     426                 : 
     427              40 : int OGRSQLiteSelectLayer::TestCapability( const char * pszCap )
     428                 : 
     429                 : {
     430              40 :     if (EQUAL(pszCap,OLCFastSpatialFilter))
     431                 :     {
     432               8 :         if (osSQLCurrent != osSQLBase)
     433               4 :             return TRUE;
     434                 : 
     435               4 :         size_t i = 0;
     436               4 :         OGRSQLiteLayer* poBaseLayer = GetBaseLayer(i);
     437               4 :         if (poBaseLayer == NULL)
     438                 :         {
     439               0 :             CPLDebug("SQLITE", "Cannot find base layer");
     440               0 :             return FALSE;
     441                 :         }
     442                 : 
     443               4 :         OGRPolygon oFakePoly;
     444               4 :         const char* pszWKT = "POLYGON((0 0,0 1,1 1,1 0,0 0))";
     445               4 :         oFakePoly.importFromWkt((char**) &pszWKT);
     446               4 :         CPLString    osSpatialWhere = poBaseLayer->GetSpatialWhere(&oFakePoly);
     447                 : 
     448               4 :         return osSpatialWhere.size() != 0;
     449                 :     }
     450                 :     else
     451              32 :         return OGRSQLiteLayer::TestCapability( pszCap );
     452                 : }
     453                 : 
     454                 : /************************************************************************/
     455                 : /*                             GetExtent()                              */
     456                 : /************************************************************************/
     457                 : 
     458              14 : OGRErr OGRSQLiteSelectLayer::GetExtent(OGREnvelope *psExtent, int bForce)
     459                 : {
     460              14 :     if (GetGeomType() == wkbNone)
     461               0 :         return OGRERR_FAILURE;
     462                 : 
     463                 :     /* Caching of extent by SQL string is interesting to speed-up the */
     464                 :     /* establishment of the WFS GetCapabilities document for a MapServer mapfile */
     465                 :     /* which has several layers, only differing by scale rules */
     466              14 :     const OGREnvelope* psCachedExtent = poDS->GetEnvelopeFromSQL(osSQLBase);
     467              14 :     if (psCachedExtent)
     468                 :     {
     469               3 :         memcpy(psExtent, psCachedExtent, sizeof(*psCachedExtent));
     470               3 :         return OGRERR_NONE;
     471                 :     }
     472                 : 
     473              11 :     CPLString osSQLCommand = osSQLBase;
     474                 : 
     475                 :     /* ORDER BY are costly to evaluate and are not necessary to establish */
     476                 :     /* the layer extent. */
     477              11 :     size_t nOrderByPos = osSQLCommand.ifind(" ORDER BY ");
     478              11 :     if( osSQLCommand.ifind("SELECT ") == 0 &&
     479                 :         nOrderByPos != std::string::npos &&
     480                 :         osSQLCommand.ifind(" LIMIT ") == std::string::npos &&
     481                 :         osSQLCommand.ifind(" UNION ") == std::string::npos &&
     482                 :         osSQLCommand.ifind(" INTERSECT ") == std::string::npos &&
     483                 :         osSQLCommand.ifind(" EXCEPT ") == std::string::npos)
     484                 :     {
     485               4 :         osSQLCommand.resize(nOrderByPos);
     486                 : 
     487               4 :         OGRLayer* poTmpLayer = poDS->ExecuteSQL(osSQLCommand.c_str(), NULL, NULL);
     488               4 :         if (poTmpLayer)
     489                 :         {
     490               4 :             OGRErr eErr = poTmpLayer->GetExtent(psExtent, bForce);
     491               4 :             poDS->ReleaseResultSet(poTmpLayer);
     492               4 :             return eErr;
     493                 :         }
     494                 :     }
     495                 : 
     496               7 :     OGRErr eErr = OGRSQLiteLayer::GetExtent(psExtent, bForce);
     497               7 :     if (eErr == OGRERR_NONE && poDS->GetUpdate() == FALSE)
     498               7 :         poDS->SetEnvelopeForSQL(osSQLBase, *psExtent);
     499               7 :     return eErr;
     500                 : }

Generated by: LCOV version 1.7