LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/sqlite - ogrsqliteutility.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 268 288 93.1 %
Date: 2024-04-29 01:40:10 Functions: 19 20 95.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  SQLite/GeoPackage Translator
       4             :  * Purpose:  Utility functions for OGR SQLite/GeoPackage driver.
       5             :  * Author:   Paul Ramsey, pramsey@boundlessgeo.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2013, Paul Ramsey <pramsey@boundlessgeo.com>
       9             :  * Copyright (c) 2020, Alessandro Pasotti <elpaso@itopen.it>
      10             :  *
      11             :  * Permission is hereby granted, free of charge, to any person obtaining a
      12             :  * copy of this software and associated documentation files (the "Software"),
      13             :  * to deal in the Software without restriction, including without limitation
      14             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15             :  * and/or sell copies of the Software, and to permit persons to whom the
      16             :  * Software is furnished to do so, subject to the following conditions:
      17             :  *
      18             :  * The above copyright notice and this permission notice shall be included
      19             :  * in all copies or substantial portions of the Software.
      20             :  *
      21             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27             :  * DEALINGS IN THE SOFTWARE.
      28             :  ****************************************************************************/
      29             : 
      30             : #include "cpl_port.h"
      31             : #include "ogrsqliteutility.h"
      32             : 
      33             : #include <cstdlib>
      34             : #include <string>
      35             : #include <iostream>
      36             : #include <sstream>
      37             : 
      38             : #include "cpl_error.h"
      39             : #include "ogr_p.h"
      40             : 
      41       13809 : SQLResult::SQLResult(char **result, int nRow, int nCol)
      42       13809 :     : papszResult(result), nRowCount(nRow), nColCount(nCol)
      43             : {
      44       13809 : }
      45             : 
      46       27618 : SQLResult::~SQLResult()
      47             : {
      48       13809 :     if (papszResult)
      49             :     {
      50       13809 :         sqlite3_free_table(papszResult);
      51             :     }
      52       13809 : }
      53             : 
      54           2 : void SQLResult::LimitRowCount(int nLimit)
      55             : {
      56           2 :     nRowCount = nLimit;
      57           2 : }
      58             : 
      59             : /* Runs a SQL command and ignores the result (good for INSERT/UPDATE/CREATE) */
      60       26773 : OGRErr SQLCommand(sqlite3 *poDb, const char *pszSQL)
      61             : {
      62       26773 :     CPLAssert(poDb != nullptr);
      63       26773 :     CPLAssert(pszSQL != nullptr);
      64             : 
      65       26773 :     char *pszErrMsg = nullptr;
      66             : #ifdef DEBUG_VERBOSE
      67             :     CPLDebug("GPKG", "exec(%s)", pszSQL);
      68             : #endif
      69       26773 :     int rc = sqlite3_exec(poDb, pszSQL, nullptr, nullptr, &pszErrMsg);
      70             : 
      71       26773 :     if (rc != SQLITE_OK)
      72             :     {
      73           7 :         CPLError(CE_Failure, CPLE_AppDefined, "sqlite3_exec(%s) failed: %s",
      74           7 :                  pszSQL, pszErrMsg ? pszErrMsg : "");
      75           7 :         sqlite3_free(pszErrMsg);
      76           7 :         return OGRERR_FAILURE;
      77             :     }
      78             : 
      79       26766 :     return OGRERR_NONE;
      80             : }
      81             : 
      82       13810 : std::unique_ptr<SQLResult> SQLQuery(sqlite3 *poDb, const char *pszSQL)
      83             : {
      84       13810 :     CPLAssert(poDb != nullptr);
      85       13810 :     CPLAssert(pszSQL != nullptr);
      86             : 
      87             : #ifdef DEBUG_VERBOSE
      88             :     CPLDebug("GPKG", "get_table(%s)", pszSQL);
      89             : #endif
      90             : 
      91       13810 :     char **papszResult = nullptr;
      92       13810 :     char *pszErrMsg = nullptr;
      93             :     int nRowCount, nColCount;
      94       13810 :     int rc = sqlite3_get_table(poDb, pszSQL, &(papszResult), &(nRowCount),
      95             :                                &(nColCount), &(pszErrMsg));
      96             : 
      97       13810 :     if (rc != SQLITE_OK)
      98             :     {
      99           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     100             :                  "sqlite3_get_table(%s) failed: %s", pszSQL, pszErrMsg);
     101           1 :         sqlite3_free(pszErrMsg);
     102           1 :         return nullptr;
     103             :     }
     104             : 
     105       13809 :     return std::make_unique<SQLResult>(papszResult, nRowCount, nColCount);
     106             : }
     107             : 
     108       96027 : const char *SQLResult::GetValue(int iColNum, int iRowNum) const
     109             : {
     110       96027 :     const int nCols = nColCount;
     111             : #ifdef DEBUG
     112       96027 :     const int nRows = nRowCount;
     113       96027 :     CPL_IGNORE_RET_VAL(nRows);
     114             : 
     115       96027 :     CPLAssert(iColNum >= 0 && iColNum < nCols);
     116       96027 :     CPLAssert(iRowNum >= 0 && iRowNum < nRows);
     117             : #endif
     118       96027 :     return papszResult[nCols + iRowNum * nCols + iColNum];
     119             : }
     120             : 
     121       16073 : int SQLResult::GetValueAsInteger(int iColNum, int iRowNum) const
     122             : {
     123       16073 :     const char *pszValue = GetValue(iColNum, iRowNum);
     124       16073 :     if (!pszValue)
     125         100 :         return 0;
     126             : 
     127       15973 :     return atoi(pszValue);
     128             : }
     129             : 
     130             : /* Returns the first row of first column of SQL as integer */
     131       42062 : GIntBig SQLGetInteger64(sqlite3 *poDb, const char *pszSQL, OGRErr *err)
     132             : {
     133       42062 :     CPLAssert(poDb != nullptr);
     134             : 
     135       42062 :     sqlite3_stmt *poStmt = nullptr;
     136             : 
     137             :     /* Prepare the SQL */
     138             : #ifdef DEBUG_VERBOSE
     139             :     CPLDebug("GPKG", "get(%s)", pszSQL);
     140             : #endif
     141       42062 :     int rc = sqlite3_prepare_v2(poDb, pszSQL, -1, &poStmt, nullptr);
     142       42062 :     if (rc != SQLITE_OK)
     143             :     {
     144           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     145             :                  "sqlite3_prepare_v2(%s) failed: %s", pszSQL,
     146             :                  sqlite3_errmsg(poDb));
     147           2 :         if (err)
     148           1 :             *err = OGRERR_FAILURE;
     149           2 :         return 0;
     150             :     }
     151             : 
     152             :     /* Execute and fetch first row */
     153       42060 :     rc = sqlite3_step(poStmt);
     154       42060 :     if (rc != SQLITE_ROW)
     155             :     {
     156        5618 :         if (err)
     157        1909 :             *err = OGRERR_FAILURE;
     158        5618 :         sqlite3_finalize(poStmt);
     159        5618 :         return 0;
     160             :     }
     161             : 
     162             :     /* Read the integer from the row */
     163       36442 :     GIntBig i = sqlite3_column_int64(poStmt, 0);
     164       36442 :     sqlite3_finalize(poStmt);
     165             : 
     166       36442 :     if (err)
     167       26064 :         *err = OGRERR_NONE;
     168       36442 :     return i;
     169             : }
     170             : 
     171       18738 : int SQLGetInteger(sqlite3 *poDb, const char *pszSQL, OGRErr *err)
     172             : {
     173       18738 :     return static_cast<int>(SQLGetInteger64(poDb, pszSQL, err));
     174             : }
     175             : 
     176             : /************************************************************************/
     177             : /*                             SQLUnescape()                            */
     178             : /************************************************************************/
     179             : 
     180       16430 : CPLString SQLUnescape(const char *pszVal)
     181             : {
     182       16430 :     char chQuoteChar = pszVal[0];
     183       16430 :     if (chQuoteChar != '\'' && chQuoteChar != '"')
     184       16055 :         return pszVal;
     185             : 
     186         750 :     CPLString osRet;
     187         375 :     pszVal++;
     188        3082 :     while (*pszVal != '\0')
     189             :     {
     190        3082 :         if (*pszVal == chQuoteChar)
     191             :         {
     192         381 :             if (pszVal[1] == chQuoteChar)
     193           6 :                 pszVal++;
     194             :             else
     195         375 :                 break;
     196             :         }
     197        2707 :         osRet += *pszVal;
     198        2707 :         pszVal++;
     199             :     }
     200         375 :     return osRet;
     201             : }
     202             : 
     203             : /************************************************************************/
     204             : /*                          SQLEscapeLiteral()                          */
     205             : /************************************************************************/
     206             : 
     207        6106 : CPLString SQLEscapeLiteral(const char *pszLiteral)
     208             : {
     209        6106 :     CPLString osVal;
     210       68537 :     for (int i = 0; pszLiteral[i] != '\0'; i++)
     211             :     {
     212       62431 :         if (pszLiteral[i] == '\'')
     213           1 :             osVal += '\'';
     214       62431 :         osVal += pszLiteral[i];
     215             :     }
     216        6106 :     return osVal;
     217             : }
     218             : 
     219             : /************************************************************************/
     220             : /*                           SQLEscapeName()                            */
     221             : /************************************************************************/
     222             : 
     223      229110 : CPLString SQLEscapeName(const char *pszName)
     224             : {
     225      229110 :     CPLString osRet;
     226     1823870 :     while (*pszName != '\0')
     227             :     {
     228     1594760 :         if (*pszName == '"')
     229           4 :             osRet += "\"\"";
     230             :         else
     231     1594750 :             osRet += *pszName;
     232     1594760 :         pszName++;
     233             :     }
     234      229110 :     return osRet;
     235             : }
     236             : 
     237             : /************************************************************************/
     238             : /*                             SQLTokenize()                            */
     239             : /************************************************************************/
     240             : 
     241           8 : char **SQLTokenize(const char *pszStr)
     242             : {
     243           8 :     char **papszTokens = nullptr;
     244           8 :     bool bInQuote = false;
     245           8 :     char chQuoteChar = '\0';
     246           8 :     bool bInSpace = true;
     247           8 :     CPLString osCurrentToken;
     248         416 :     while (*pszStr != '\0')
     249             :     {
     250         408 :         if (*pszStr == ' ' && !bInQuote)
     251             :         {
     252          40 :             if (!bInSpace)
     253             :             {
     254          35 :                 papszTokens = CSLAddString(papszTokens, osCurrentToken);
     255          35 :                 osCurrentToken.clear();
     256             :             }
     257          40 :             bInSpace = true;
     258             :         }
     259         368 :         else if ((*pszStr == '(' || *pszStr == ')' || *pszStr == ',') &&
     260           4 :                  !bInQuote)
     261             :         {
     262           4 :             if (!bInSpace)
     263             :             {
     264           3 :                 papszTokens = CSLAddString(papszTokens, osCurrentToken);
     265           3 :                 osCurrentToken.clear();
     266             :             }
     267           4 :             osCurrentToken.clear();
     268           4 :             osCurrentToken += *pszStr;
     269           4 :             papszTokens = CSLAddString(papszTokens, osCurrentToken);
     270           4 :             osCurrentToken.clear();
     271           4 :             bInSpace = true;
     272             :         }
     273         364 :         else if (*pszStr == '"' || *pszStr == '\'')
     274             :         {
     275          28 :             if (bInQuote && *pszStr == chQuoteChar && pszStr[1] == chQuoteChar)
     276             :             {
     277           5 :                 osCurrentToken += *pszStr;
     278           5 :                 osCurrentToken += *pszStr;
     279           5 :                 pszStr += 2;
     280           5 :                 continue;
     281             :             }
     282          23 :             else if (bInQuote && *pszStr == chQuoteChar)
     283             :             {
     284           9 :                 osCurrentToken += *pszStr;
     285           9 :                 papszTokens = CSLAddString(papszTokens, osCurrentToken);
     286           9 :                 osCurrentToken.clear();
     287           9 :                 bInSpace = true;
     288           9 :                 bInQuote = false;
     289           9 :                 chQuoteChar = '\0';
     290             :             }
     291          14 :             else if (bInQuote)
     292             :             {
     293           5 :                 osCurrentToken += *pszStr;
     294             :             }
     295             :             else
     296             :             {
     297           9 :                 chQuoteChar = *pszStr;
     298           9 :                 osCurrentToken.clear();
     299           9 :                 osCurrentToken += chQuoteChar;
     300           9 :                 bInQuote = true;
     301           9 :                 bInSpace = false;
     302             :             }
     303             :         }
     304             :         else
     305             :         {
     306         336 :             osCurrentToken += *pszStr;
     307         336 :             bInSpace = false;
     308             :         }
     309         403 :         pszStr++;
     310             :     }
     311             : 
     312           8 :     if (!osCurrentToken.empty())
     313           3 :         papszTokens = CSLAddString(papszTokens, osCurrentToken);
     314             : 
     315          16 :     return papszTokens;
     316             : }
     317             : 
     318             : /************************************************************************/
     319             : /*                    SQLGetUniqueFieldUCConstraints()                  */
     320             : /************************************************************************/
     321             : 
     322             : /* Return set of field names (in upper case) that have a UNIQUE constraint,
     323             :  * only on that single column.
     324             :  */
     325             : 
     326        1382 : std::set<std::string> SQLGetUniqueFieldUCConstraints(
     327             :     sqlite3 *poDb, const char *pszTableName,
     328             :     const std::vector<SQLSqliteMasterContent> &sqliteMasterContent)
     329             : {
     330             :     // set names (in upper case) of fields with unique constraint
     331        1382 :     std::set<std::string> uniqueFieldsUC;
     332             : 
     333             :     // Unique fields detection
     334        2764 :     const std::string upperTableName{CPLString(pszTableName).toupper()};
     335        2764 :     std::string tableDefinition;
     336             : 
     337        1382 :     if (sqliteMasterContent.empty())
     338             :     {
     339        1380 :         char *pszTableDefinitionSQL = sqlite3_mprintf(
     340             :             "SELECT sql, type FROM sqlite_master "
     341             :             "WHERE type IN ('table', 'view') AND UPPER(name)='%q'",
     342             :             upperTableName.c_str());
     343        1380 :         auto oResultTable = SQLQuery(poDb, pszTableDefinitionSQL);
     344        1380 :         sqlite3_free(pszTableDefinitionSQL);
     345             : 
     346        1380 :         if (!oResultTable || oResultTable->RowCount() == 0)
     347             :         {
     348           2 :             if (oResultTable)
     349           2 :                 CPLError(CE_Failure, CPLE_AppDefined, "Cannot find table %s",
     350             :                          pszTableName);
     351             : 
     352           2 :             return uniqueFieldsUC;
     353             :         }
     354        1378 :         if (std::string(oResultTable->GetValue(1, 0)) == "view")
     355             :         {
     356           1 :             return uniqueFieldsUC;
     357             :         }
     358        1377 :         tableDefinition = oResultTable->GetValue(0, 0);
     359             :     }
     360             :     else
     361             :     {
     362         107 :         for (const auto &row : sqliteMasterContent)
     363             :         {
     364         142 :             if (row.osType == "table" &&
     365         142 :                 CPLString(row.osTableName).toupper() == upperTableName)
     366             :             {
     367           2 :                 tableDefinition = row.osSQL;
     368           2 :                 break;
     369             :             }
     370         105 :             else if (row.osType == "view" &&
     371         105 :                      CPLString(row.osTableName).toupper() == upperTableName)
     372             :             {
     373           0 :                 return uniqueFieldsUC;
     374             :             }
     375             :         }
     376           2 :         if (tableDefinition.empty())
     377             :         {
     378           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot find table %s",
     379             :                      pszTableName);
     380             : 
     381           0 :             return uniqueFieldsUC;
     382             :         }
     383             :     }
     384             : 
     385             :     // Parses strings like "colum_name1" KEYWORD1 KEYWORD2 'some string',
     386             :     // `column_name2`,"column_name3"
     387             :     const auto GetNextToken =
     388         935 :         [](const std::string &osStr, size_t &pos, bool keepQuotes)
     389             :     {
     390         935 :         if (pos >= osStr.size())
     391         117 :             return std::string();
     392         818 :         pos = osStr.find_first_not_of(" \t\n\r", pos);
     393         818 :         if (pos == std::string::npos)
     394           1 :             return std::string();
     395             : 
     396        1634 :         std::string osToken;
     397         817 :         if (osStr[pos] == '"' || osStr[pos] == '\'' || osStr[pos] == '`')
     398             :         {
     399          88 :             const char chQuoteChar = osStr[pos];
     400          88 :             if (keepQuotes)
     401           1 :                 osToken += chQuoteChar;
     402          88 :             ++pos;
     403         864 :             while (pos < osStr.size())
     404             :             {
     405         864 :                 if (osStr[pos] == chQuoteChar)
     406             :                 {
     407          88 :                     if (pos + 1 < osStr.size() && osStr[pos + 1] == chQuoteChar)
     408             :                     {
     409           0 :                         osToken += chQuoteChar;
     410           0 :                         pos += 2;
     411             :                     }
     412             :                     else
     413             :                     {
     414          88 :                         if (keepQuotes)
     415           1 :                             osToken += chQuoteChar;
     416          88 :                         pos++;
     417          88 :                         break;
     418             :                     }
     419             :                 }
     420             :                 else
     421             :                 {
     422         776 :                     osToken += osStr[pos];
     423         776 :                     pos++;
     424             :                 }
     425             :             }
     426             :         }
     427         729 :         else if (osStr[pos] == ',')
     428             :         {
     429         193 :             osToken = ',';
     430         193 :             pos++;
     431             :         }
     432             :         else
     433             :         {
     434         536 :             size_t pos2 = osStr.find_first_of(" \t\n\r,", pos);
     435         536 :             if (pos2 == std::string::npos)
     436          66 :                 osToken = osStr.substr(pos);
     437             :             else
     438         470 :                 osToken = osStr.substr(pos, pos2 - pos);
     439         536 :             pos = pos2;
     440             :         }
     441         817 :         return osToken;
     442             :     };
     443             : 
     444             :     // Parses CREATE TABLE definition for column UNIQUE keywords
     445             :     {
     446        1379 :         const auto nPosStart = tableDefinition.find('(');
     447        1379 :         const auto nPosEnd = tableDefinition.rfind(')');
     448        1379 :         if (nPosStart != std::string::npos && nPosEnd != std::string::npos &&
     449        2758 :             nPosEnd > nPosStart &&
     450        2758 :             CPLString(tableDefinition).toupper().find("UNIQUE") !=
     451             :                 std::string::npos)
     452             :         {
     453             :             tableDefinition =
     454          45 :                 tableDefinition.substr(nPosStart + 1, nPosEnd - nPosStart - 1);
     455          45 :             size_t pos = 0;
     456             :             while (true)
     457             :             {
     458             :                 const std::string osColName =
     459         282 :                     GetNextToken(tableDefinition, pos, false);
     460         282 :                 if (osColName.empty())
     461             :                 {
     462          45 :                     break;
     463             :                 }
     464             :                 while (true)
     465             :                 {
     466             :                     const std::string osToken =
     467         595 :                         GetNextToken(tableDefinition, pos, true);
     468         595 :                     if (osToken.empty() || osToken == ",")
     469         237 :                         break;
     470         358 :                     if (EQUAL(osToken.c_str(), "UNIQUE"))
     471             :                     {
     472          29 :                         uniqueFieldsUC.insert(CPLString(osColName).toupper());
     473             :                     }
     474         358 :                 }
     475         237 :             }
     476             :         }
     477             :     }
     478             : 
     479             :     // Search indexes:
     480             : 
     481             :     const auto ProcessIndexDefinition =
     482         115 :         [&uniqueFieldsUC, &GetNextToken](const std::string &indexDefinitionIn)
     483             :     {
     484          29 :         const auto nPosStart = indexDefinitionIn.find('(');
     485          29 :         const auto nPosEnd = indexDefinitionIn.rfind(')');
     486          29 :         if (nPosStart != std::string::npos && nPosEnd != std::string::npos &&
     487             :             nPosEnd > nPosStart)
     488             :         {
     489             :             std::string indexDefinitionMod = indexDefinitionIn.substr(
     490          58 :                 nPosStart + 1, nPosEnd - nPosStart - 1);
     491          29 :             size_t pos = 0;
     492             :             const std::string osColName =
     493          58 :                 GetNextToken(indexDefinitionMod, pos, false);
     494             :             // Only matches index on single columns
     495          29 :             if (GetNextToken(indexDefinitionMod, pos, false).empty())
     496             :             {
     497          28 :                 uniqueFieldsUC.insert(CPLString(osColName).toupper());
     498             :             }
     499             :         }
     500          29 :     };
     501             : 
     502        1379 :     if (sqliteMasterContent.empty())
     503             :     {
     504        1377 :         char *pszTableDefinitionSQL = sqlite3_mprintf(
     505             :             "SELECT sql FROM sqlite_master WHERE type='index' AND"
     506             :             " UPPER(tbl_name)='%q' AND UPPER(sql) "
     507             :             "LIKE 'CREATE UNIQUE INDEX%%'",
     508             :             upperTableName.c_str());
     509        2754 :         auto oResultTable = SQLQuery(poDb, pszTableDefinitionSQL);
     510        1377 :         sqlite3_free(pszTableDefinitionSQL);
     511             : 
     512        1377 :         if (!oResultTable)
     513             :         {
     514           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     515             :                      "Error searching indexes for table %s", pszTableName);
     516             :         }
     517        1377 :         else if (oResultTable->RowCount() >= 0)
     518             :         {
     519        1404 :             for (int rowCnt = 0; rowCnt < oResultTable->RowCount(); ++rowCnt)
     520             :             {
     521          54 :                 std::string indexDefinition{oResultTable->GetValue(0, rowCnt)};
     522          27 :                 ProcessIndexDefinition(indexDefinition);
     523             :             }
     524             :         }
     525             :     }
     526             :     else
     527             :     {
     528         116 :         for (const auto &row : sqliteMasterContent)
     529             :         {
     530          58 :             if (row.osType == "index" &&
     531         172 :                 CPLString(row.osTableName).toupper() == upperTableName &&
     532           4 :                 STARTS_WITH_CI(row.osSQL.c_str(), "CREATE UNIQUE INDEX"))
     533             :             {
     534           4 :                 std::string indexDefinition = row.osSQL;
     535           2 :                 ProcessIndexDefinition(indexDefinition);
     536             :             }
     537             :         }
     538             :     }
     539             : 
     540        1379 :     return uniqueFieldsUC;
     541             : }
     542             : 
     543             : /************************************************************************/
     544             : /*               OGRSQLiteRTreeRequiresTrustedSchemaOn()                */
     545             : /************************************************************************/
     546             : 
     547             : /** Whether the use of a RTree in triggers or views requires trusted_schema
     548             :  * PRAGMA to be set to ON */
     549         905 : bool OGRSQLiteRTreeRequiresTrustedSchemaOn()
     550             : {
     551          40 :     static bool b = []()
     552             :     {
     553          40 :         sqlite3 *hDB = nullptr;
     554             :         int rc =
     555          40 :             sqlite3_open_v2(":memory:", &hDB, SQLITE_OPEN_READWRITE, nullptr);
     556          40 :         if (rc != SQLITE_OK)
     557             :         {
     558           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     559             :                      "sqlite3_open_v2(:memory:) failed");
     560           0 :             sqlite3_close(hDB);
     561           0 :             return false;
     562             :         }
     563          40 :         rc = sqlite3_exec(hDB,
     564             :                           "CREATE VIRTUAL TABLE foo_rtree USING rtree(id, "
     565             :                           "minx, miny, maxx, maxy);",
     566             :                           nullptr, nullptr, nullptr);
     567          40 :         if (rc != SQLITE_OK)
     568             :         {
     569           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     570             :                      "CREATE VIRTUAL TABLE foo_rtree failed");
     571           0 :             sqlite3_close(hDB);
     572           0 :             return false;
     573             :         }
     574          40 :         rc = sqlite3_exec(hDB, "CREATE VIEW v AS SELECT * FROM foo_rtree;",
     575             :                           nullptr, nullptr, nullptr);
     576          40 :         if (rc != SQLITE_OK)
     577             :         {
     578           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     579             :                      "CREATE VIEW v AS SELECT * FROM foo_rtree failed");
     580           0 :             sqlite3_close(hDB);
     581           0 :             return false;
     582             :         }
     583             :         // Try to read the virtual table from a view. As of today (sqlite 3.43.1)
     584             :         // this require trusted_schema = ON
     585          40 :         rc = sqlite3_exec(hDB, "SELECT * FROM v", nullptr, nullptr, nullptr);
     586          40 :         bool bRequiresTrustedSchemaOn = false;
     587          40 :         if (rc != SQLITE_OK)
     588             :         {
     589          40 :             CPL_IGNORE_RET_VAL(sqlite3_exec(hDB, "PRAGMA trusted_schema = ON",
     590             :                                             nullptr, nullptr, nullptr));
     591             :             rc =
     592          40 :                 sqlite3_exec(hDB, "SELECT * FROM v", nullptr, nullptr, nullptr);
     593          40 :             if (rc == SQLITE_OK)
     594          40 :                 bRequiresTrustedSchemaOn = true;
     595             :         }
     596          40 :         sqlite3_close(hDB);
     597          40 :         return bRequiresTrustedSchemaOn;
     598         905 :     }();
     599         905 :     return b;
     600             : }
     601             : 
     602             : /************************************************************************/
     603             : /*               OGRSQLiteIsSpatialFunctionReturningGeometry()          */
     604             : /************************************************************************/
     605             : 
     606        2033 : bool OGRSQLiteIsSpatialFunctionReturningGeometry(const char *pszName)
     607             : {
     608        2033 :     const char *const apszFunctions[] = {
     609             :         "SetSRID(",
     610             :         "IsValidDetail(",
     611             :         "Boundary(",
     612             :         "Envelope(",
     613             :         "ST_Expand(",
     614             :         "ST_Reverse(",
     615             :         "ST_ForceLHR(",
     616             :         "ST_ForcePolygonCW(",
     617             :         "ST_ForcePolygonCCW(",
     618             :         "SanitizeGeometry(",
     619             :         "EnsureClosedRings(",
     620             :         "RemoveRepeatedPoints(",
     621             :         "CastToPoint(",
     622             :         "CastToLinestring(",
     623             :         "CastToPolygon(",
     624             :         "CastToMultiPoint(",
     625             :         "CastToMultiLinestring(",
     626             :         "CastToMultiPolygon(",
     627             :         "CastToGeometryCollection(",
     628             :         "CastToMulti(",
     629             :         "ST_Multi(",
     630             :         "CastToSingle(",
     631             :         "CastToXY(",
     632             :         "CastToXYZ(",
     633             :         "CastToXYM(",
     634             :         "CastToXYZM(",
     635             :         "StartPoint(",
     636             :         "ST_EndPoint(",
     637             :         "PointOnSurface(",
     638             :         "Simplify(",
     639             :         "ST_Generalize(",
     640             :         "SimplifyPreserveTopology(",
     641             :         "PointN(",
     642             :         "AddPoint(",
     643             :         "SetPoint(",
     644             :         "SetStartPoint(",
     645             :         "SetEndPoint(",
     646             :         "RemovePoint(",
     647             :         "Centroid(",
     648             :         "ExteriorRing(",
     649             :         "InteriorRingN(",
     650             :         "GeometryN(",
     651             :         "ST_AddMeasure(",
     652             :         "ST_Locate_Along_Measure(",
     653             :         "ST_LocateAlong(",
     654             :         "ST_Locate_Between_Measures(",
     655             :         "ST_LocateBetween(",
     656             :         "ST_TrajectoryInterpolarePoint(",
     657             :         "Intersection(",
     658             :         "Difference(",
     659             :         "GUnion(",
     660             :         "ST_Union(",  // UNION is not a valid function name
     661             :         "SymDifference(",
     662             :         "Buffer(",
     663             :         "ConvexHull(",
     664             :         "OffsetCurve(",
     665             :         "SingleSidedBuffer(",
     666             :         "SharedPaths(",
     667             :         "Line_Interpolate_Point(",
     668             :         "Line_Interpolate_Equidistant_Points(",
     669             :         "Line_Substring(",
     670             :         "ClosestPoint(",
     671             :         "ShortestLine(",
     672             :         "Snap(",
     673             :         "Collect(",
     674             :         "LineMerge(",
     675             :         "BuildArea(",
     676             :         "Polygonize(",
     677             :         "MakePolygon(",
     678             :         "UnaryUnion(",
     679             :         "UnaryUnion(",
     680             :         "DrapeLine(",
     681             :         "DrapeLineExceptions(",
     682             :         "DissolveSegments(",
     683             :         "DissolvePoints(",
     684             :         "LinesFromRings(",
     685             :         "LinesCutAtNodes(",
     686             :         "RingsCutAtNodes(",
     687             :         "CollectionExtract(",
     688             :         "ExtractMultiPoint(",
     689             :         "ExtractMultiLinestring(",
     690             :         "ExtractMultiPolygon(",
     691             :         "DelaunayTriangulation(",
     692             :         "VoronojDiagram(",
     693             :         "ConcaveHull(",
     694             :         "MakeValid(",
     695             :         "MakeValidDiscarded(",
     696             :         "Segmentize(",
     697             :         "Split(",
     698             :         "SplitLeft(",
     699             :         "SplitRight(",
     700             :         "SnapAndSplit(",
     701             :         "Project(",
     702             :         "SnapToGrid(",
     703             :         "ST_Node(",
     704             :         "SelfIntersections(",
     705             :         "ST_Subdivide(",
     706             :         "Transform(",
     707             :         "TransformXY(",
     708             :         "TransformXYZ(",
     709             :         "ShiftCoords(",
     710             :         "ShiftCoordinates(",
     711             :         "ST_Translate(",
     712             :         "ST_Shift_Longitude(",
     713             :         "NormalizeLonLat(",
     714             :         "ScaleCoords(",
     715             :         "ScaleCoordinates(",
     716             :         "RotateCoords(",
     717             :         "RotateCoordinates(",
     718             :         "ReflectCoords(",
     719             :         "ReflectCoordinates(",
     720             :         "SwapCoords(",
     721             :         "SwapCoordinates(",
     722             :         "ATM_Transform(",
     723             :         "gpkgMakePoint(",
     724             :         "gpkgMakePointZ(",
     725             :         "gpkgMakePointZM(",
     726             :         "gpkgMakePointM(",
     727             :         "AsGPB(",
     728             :         "GeomFromGPB(",
     729             :         "CastAutomagic(",
     730             :     };
     731      247170 :     for (const char *pszFunction : apszFunctions)
     732             :     {
     733      245153 :         if (STARTS_WITH_CI(pszName, pszFunction) ||
     734      245152 :             (!STARTS_WITH_CI(pszFunction, "ST_") &&
     735      206585 :              STARTS_WITH_CI(pszName, "ST_") &&
     736        3301 :              STARTS_WITH_CI(pszName + strlen("ST_"), pszFunction)))
     737             :         {
     738          16 :             return true;
     739             :         }
     740             :     }
     741        2017 :     return false;
     742             : }
     743             : 
     744           0 : double SQLResult::GetValueAsDouble(int iColNum, int iRowNum) const
     745             : {
     746           0 :     const char *pszValue = GetValue(iColNum, iRowNum);
     747           0 :     if (!pszValue)
     748           0 :         return 0;
     749             : 
     750           0 :     return CPLStrtod(pszValue, nullptr);
     751             : }

Generated by: LCOV version 1.14