LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/sqlite - ogrsqliteutility.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 363 400 90.8 %
Date: 2024-11-25 13:07:18 Functions: 20 21 95.2 %

          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             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "ogrsqliteutility.h"
      16             : 
      17             : #include <cstdlib>
      18             : #include <string>
      19             : #include <iostream>
      20             : #include <sstream>
      21             : 
      22             : #include "cpl_error.h"
      23             : #include "ogr_p.h"
      24             : #include "gdal_priv.h"
      25             : 
      26       15415 : SQLResult::SQLResult(char **result, int nRow, int nCol)
      27       15415 :     : papszResult(result), nRowCount(nRow), nColCount(nCol)
      28             : {
      29       15415 : }
      30             : 
      31       30830 : SQLResult::~SQLResult()
      32             : {
      33       15415 :     if (papszResult)
      34             :     {
      35       15415 :         sqlite3_free_table(papszResult);
      36             :     }
      37       15415 : }
      38             : 
      39           2 : void SQLResult::LimitRowCount(int nLimit)
      40             : {
      41           2 :     nRowCount = nLimit;
      42           2 : }
      43             : 
      44             : /* Runs a SQL command and ignores the result (good for INSERT/UPDATE/CREATE) */
      45       28177 : OGRErr SQLCommand(sqlite3 *poDb, const char *pszSQL)
      46             : {
      47       28177 :     CPLAssert(poDb != nullptr);
      48       28177 :     CPLAssert(pszSQL != nullptr);
      49             : 
      50       28177 :     char *pszErrMsg = nullptr;
      51             : #ifdef DEBUG_VERBOSE
      52             :     CPLDebug("GPKG", "exec(%s)", pszSQL);
      53             : #endif
      54       28177 :     int rc = sqlite3_exec(poDb, pszSQL, nullptr, nullptr, &pszErrMsg);
      55             : 
      56       28177 :     if (rc != SQLITE_OK)
      57             :     {
      58           7 :         CPLError(CE_Failure, CPLE_AppDefined, "sqlite3_exec(%s) failed: %s",
      59           7 :                  pszSQL, pszErrMsg ? pszErrMsg : "");
      60           7 :         sqlite3_free(pszErrMsg);
      61           7 :         return OGRERR_FAILURE;
      62             :     }
      63             : 
      64       28170 :     return OGRERR_NONE;
      65             : }
      66             : 
      67       15416 : std::unique_ptr<SQLResult> SQLQuery(sqlite3 *poDb, const char *pszSQL)
      68             : {
      69       15416 :     CPLAssert(poDb != nullptr);
      70       15416 :     CPLAssert(pszSQL != nullptr);
      71             : 
      72             : #ifdef DEBUG_VERBOSE
      73             :     CPLDebug("GPKG", "get_table(%s)", pszSQL);
      74             : #endif
      75             : 
      76       15416 :     char **papszResult = nullptr;
      77       15416 :     char *pszErrMsg = nullptr;
      78             :     int nRowCount, nColCount;
      79       15416 :     int rc = sqlite3_get_table(poDb, pszSQL, &(papszResult), &(nRowCount),
      80             :                                &(nColCount), &(pszErrMsg));
      81             : 
      82       15416 :     if (rc != SQLITE_OK)
      83             :     {
      84           1 :         CPLError(CE_Failure, CPLE_AppDefined,
      85             :                  "sqlite3_get_table(%s) failed: %s", pszSQL, pszErrMsg);
      86           1 :         sqlite3_free(pszErrMsg);
      87           1 :         return nullptr;
      88             :     }
      89             : 
      90       15415 :     return std::make_unique<SQLResult>(papszResult, nRowCount, nColCount);
      91             : }
      92             : 
      93      105950 : const char *SQLResult::GetValue(int iColNum, int iRowNum) const
      94             : {
      95      105950 :     const int nCols = nColCount;
      96             : #ifdef DEBUG
      97      105950 :     const int nRows = nRowCount;
      98      105950 :     CPL_IGNORE_RET_VAL(nRows);
      99             : 
     100      105950 :     CPLAssert(iColNum >= 0 && iColNum < nCols);
     101      105950 :     CPLAssert(iRowNum >= 0 && iRowNum < nRows);
     102             : #endif
     103      105950 :     return papszResult[nCols + iRowNum * nCols + iColNum];
     104             : }
     105             : 
     106       17993 : int SQLResult::GetValueAsInteger(int iColNum, int iRowNum) const
     107             : {
     108       17993 :     const char *pszValue = GetValue(iColNum, iRowNum);
     109       17993 :     if (!pszValue)
     110         100 :         return 0;
     111             : 
     112       17893 :     return atoi(pszValue);
     113             : }
     114             : 
     115             : /* Returns the first row of first column of SQL as integer */
     116       43976 : GIntBig SQLGetInteger64(sqlite3 *poDb, const char *pszSQL, OGRErr *err)
     117             : {
     118       43976 :     CPLAssert(poDb != nullptr);
     119             : 
     120       43976 :     sqlite3_stmt *poStmt = nullptr;
     121             : 
     122             :     /* Prepare the SQL */
     123             : #ifdef DEBUG_VERBOSE
     124             :     CPLDebug("GPKG", "get(%s)", pszSQL);
     125             : #endif
     126       43976 :     int rc = sqlite3_prepare_v2(poDb, pszSQL, -1, &poStmt, nullptr);
     127       43976 :     if (rc != SQLITE_OK)
     128             :     {
     129           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     130             :                  "sqlite3_prepare_v2(%s) failed: %s", pszSQL,
     131             :                  sqlite3_errmsg(poDb));
     132           2 :         if (err)
     133           1 :             *err = OGRERR_FAILURE;
     134           2 :         return 0;
     135             :     }
     136             : 
     137             :     /* Execute and fetch first row */
     138       43974 :     rc = sqlite3_step(poStmt);
     139       43974 :     if (rc != SQLITE_ROW)
     140             :     {
     141        6041 :         if (err)
     142        1953 :             *err = OGRERR_FAILURE;
     143        6041 :         sqlite3_finalize(poStmt);
     144        6041 :         return 0;
     145             :     }
     146             : 
     147             :     /* Read the integer from the row */
     148       37933 :     GIntBig i = sqlite3_column_int64(poStmt, 0);
     149       37933 :     sqlite3_finalize(poStmt);
     150             : 
     151       37933 :     if (err)
     152       26432 :         *err = OGRERR_NONE;
     153       37933 :     return i;
     154             : }
     155             : 
     156       20540 : int SQLGetInteger(sqlite3 *poDb, const char *pszSQL, OGRErr *err)
     157             : {
     158       20540 :     return static_cast<int>(SQLGetInteger64(poDb, pszSQL, err));
     159             : }
     160             : 
     161             : /************************************************************************/
     162             : /*                             SQLUnescape()                            */
     163             : /************************************************************************/
     164             : 
     165       17222 : CPLString SQLUnescape(const char *pszVal)
     166             : {
     167       17222 :     char chQuoteChar = pszVal[0];
     168       17222 :     if (chQuoteChar != '\'' && chQuoteChar != '"')
     169       16764 :         return pszVal;
     170             : 
     171         916 :     CPLString osRet;
     172         458 :     pszVal++;
     173        3693 :     while (*pszVal != '\0')
     174             :     {
     175        3693 :         if (*pszVal == chQuoteChar)
     176             :         {
     177         467 :             if (pszVal[1] == chQuoteChar)
     178           9 :                 pszVal++;
     179             :             else
     180         458 :                 break;
     181             :         }
     182        3235 :         osRet += *pszVal;
     183        3235 :         pszVal++;
     184             :     }
     185         458 :     return osRet;
     186             : }
     187             : 
     188             : /************************************************************************/
     189             : /*                          SQLEscapeLiteral()                          */
     190             : /************************************************************************/
     191             : 
     192        6435 : CPLString SQLEscapeLiteral(const char *pszLiteral)
     193             : {
     194        6435 :     CPLString osVal;
     195       70879 :     for (int i = 0; pszLiteral[i] != '\0'; i++)
     196             :     {
     197       64444 :         if (pszLiteral[i] == '\'')
     198           1 :             osVal += '\'';
     199       64444 :         osVal += pszLiteral[i];
     200             :     }
     201        6435 :     return osVal;
     202             : }
     203             : 
     204             : /************************************************************************/
     205             : /*                           SQLEscapeName()                            */
     206             : /************************************************************************/
     207             : 
     208      233015 : CPLString SQLEscapeName(const char *pszName)
     209             : {
     210      233015 :     CPLString osRet;
     211     1854020 :     while (*pszName != '\0')
     212             :     {
     213     1621010 :         if (*pszName == '"')
     214          46 :             osRet += "\"\"";
     215             :         else
     216     1620960 :             osRet += *pszName;
     217     1621010 :         pszName++;
     218             :     }
     219      233015 :     return osRet;
     220             : }
     221             : 
     222             : /************************************************************************/
     223             : /*                             SQLTokenize()                            */
     224             : /************************************************************************/
     225             : 
     226          31 : char **SQLTokenize(const char *pszStr)
     227             : {
     228          31 :     char **papszTokens = nullptr;
     229          31 :     bool bInQuote = false;
     230          31 :     char chQuoteChar = '\0';
     231          31 :     bool bInSpace = true;
     232          31 :     CPLString osCurrentToken;
     233        1191 :     while (*pszStr != '\0')
     234             :     {
     235        1160 :         if (*pszStr == ' ' && !bInQuote)
     236             :         {
     237         120 :             if (!bInSpace)
     238             :             {
     239         108 :                 papszTokens = CSLAddString(papszTokens, osCurrentToken);
     240         108 :                 osCurrentToken.clear();
     241             :             }
     242         120 :             bInSpace = true;
     243             :         }
     244        1040 :         else if ((*pszStr == '(' || *pszStr == ')' || *pszStr == ',') &&
     245           4 :                  !bInQuote)
     246             :         {
     247           4 :             if (!bInSpace)
     248             :             {
     249           3 :                 papszTokens = CSLAddString(papszTokens, osCurrentToken);
     250           3 :                 osCurrentToken.clear();
     251             :             }
     252           4 :             osCurrentToken.clear();
     253           4 :             osCurrentToken += *pszStr;
     254           4 :             papszTokens = CSLAddString(papszTokens, osCurrentToken);
     255           4 :             osCurrentToken.clear();
     256           4 :             bInSpace = true;
     257             :         }
     258        1036 :         else if (*pszStr == '"' || *pszStr == '\'')
     259             :         {
     260          59 :             if (bInQuote && *pszStr == chQuoteChar && pszStr[1] == chQuoteChar)
     261             :             {
     262           8 :                 osCurrentToken += *pszStr;
     263           8 :                 osCurrentToken += *pszStr;
     264           8 :                 pszStr += 2;
     265           8 :                 continue;
     266             :             }
     267          51 :             else if (bInQuote && *pszStr == chQuoteChar)
     268             :             {
     269          22 :                 osCurrentToken += *pszStr;
     270          22 :                 papszTokens = CSLAddString(papszTokens, osCurrentToken);
     271          22 :                 osCurrentToken.clear();
     272          22 :                 bInSpace = true;
     273          22 :                 bInQuote = false;
     274          22 :                 chQuoteChar = '\0';
     275             :             }
     276          29 :             else if (bInQuote)
     277             :             {
     278           7 :                 osCurrentToken += *pszStr;
     279             :             }
     280             :             else
     281             :             {
     282          22 :                 chQuoteChar = *pszStr;
     283          22 :                 osCurrentToken.clear();
     284          22 :                 osCurrentToken += chQuoteChar;
     285          22 :                 bInQuote = true;
     286          22 :                 bInSpace = false;
     287             :             }
     288             :         }
     289             :         else
     290             :         {
     291         977 :             osCurrentToken += *pszStr;
     292         977 :             bInSpace = false;
     293             :         }
     294        1152 :         pszStr++;
     295             :     }
     296             : 
     297          31 :     if (!osCurrentToken.empty())
     298          15 :         papszTokens = CSLAddString(papszTokens, osCurrentToken);
     299             : 
     300          62 :     return papszTokens;
     301             : }
     302             : 
     303             : /************************************************************************/
     304             : /*                    SQLGetUniqueFieldUCConstraints()                  */
     305             : /************************************************************************/
     306             : 
     307             : /* Return set of field names (in upper case) that have a UNIQUE constraint,
     308             :  * only on that single column.
     309             :  */
     310             : 
     311        1481 : std::set<std::string> SQLGetUniqueFieldUCConstraints(
     312             :     sqlite3 *poDb, const char *pszTableName,
     313             :     const std::vector<SQLSqliteMasterContent> &sqliteMasterContent)
     314             : {
     315             :     // set names (in upper case) of fields with unique constraint
     316        1481 :     std::set<std::string> uniqueFieldsUC;
     317             : 
     318             :     // Unique fields detection
     319        2962 :     const std::string upperTableName{CPLString(pszTableName).toupper()};
     320        2962 :     std::string tableDefinition;
     321             : 
     322        1481 :     if (sqliteMasterContent.empty())
     323             :     {
     324        1479 :         char *pszTableDefinitionSQL = sqlite3_mprintf(
     325             :             "SELECT sql, type FROM sqlite_master "
     326             :             "WHERE type IN ('table', 'view') AND UPPER(name)='%q'",
     327             :             upperTableName.c_str());
     328        1479 :         auto oResultTable = SQLQuery(poDb, pszTableDefinitionSQL);
     329        1479 :         sqlite3_free(pszTableDefinitionSQL);
     330             : 
     331        1479 :         if (!oResultTable || oResultTable->RowCount() == 0)
     332             :         {
     333           2 :             if (oResultTable)
     334           2 :                 CPLError(CE_Failure, CPLE_AppDefined, "Cannot find table %s",
     335             :                          pszTableName);
     336             : 
     337           2 :             return uniqueFieldsUC;
     338             :         }
     339        1477 :         if (std::string(oResultTable->GetValue(1, 0)) == "view")
     340             :         {
     341           1 :             return uniqueFieldsUC;
     342             :         }
     343        1476 :         tableDefinition = oResultTable->GetValue(0, 0);
     344             :     }
     345             :     else
     346             :     {
     347         107 :         for (const auto &row : sqliteMasterContent)
     348             :         {
     349         142 :             if (row.osType == "table" &&
     350         142 :                 CPLString(row.osTableName).toupper() == upperTableName)
     351             :             {
     352           2 :                 tableDefinition = row.osSQL;
     353           2 :                 break;
     354             :             }
     355         105 :             else if (row.osType == "view" &&
     356         105 :                      CPLString(row.osTableName).toupper() == upperTableName)
     357             :             {
     358           0 :                 return uniqueFieldsUC;
     359             :             }
     360             :         }
     361           2 :         if (tableDefinition.empty())
     362             :         {
     363           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot find table %s",
     364             :                      pszTableName);
     365             : 
     366           0 :             return uniqueFieldsUC;
     367             :         }
     368             :     }
     369             : 
     370             :     // Parses strings like "colum_name1" KEYWORD1 KEYWORD2 'some string',
     371             :     // `column_name2`,"column_name3"
     372             :     const auto GetNextToken =
     373        1293 :         [](const std::string &osStr, size_t &pos, bool keepQuotes)
     374             :     {
     375        1293 :         if (pos >= osStr.size())
     376         160 :             return std::string();
     377        1133 :         pos = osStr.find_first_not_of(" \t\n\r", pos);
     378        1133 :         if (pos == std::string::npos)
     379           1 :             return std::string();
     380             : 
     381        2264 :         std::string osToken;
     382        1132 :         if (osStr[pos] == '"' || osStr[pos] == '\'' || osStr[pos] == '`')
     383             :         {
     384         168 :             const char chQuoteChar = osStr[pos];
     385         168 :             if (keepQuotes)
     386          81 :                 osToken += chQuoteChar;
     387         168 :             ++pos;
     388        1611 :             while (pos < osStr.size())
     389             :             {
     390        1611 :                 if (osStr[pos] == chQuoteChar)
     391             :                 {
     392         168 :                     if (pos + 1 < osStr.size() && osStr[pos + 1] == chQuoteChar)
     393             :                     {
     394           0 :                         osToken += chQuoteChar;
     395           0 :                         pos += 2;
     396             :                     }
     397             :                     else
     398             :                     {
     399         168 :                         if (keepQuotes)
     400          81 :                             osToken += chQuoteChar;
     401         168 :                         pos++;
     402         168 :                         break;
     403             :                     }
     404             :                 }
     405             :                 else
     406             :                 {
     407        1443 :                     osToken += osStr[pos];
     408        1443 :                     pos++;
     409             :                 }
     410             :             }
     411             :         }
     412         964 :         else if (osStr[pos] == ',' || osStr[pos] == '(' || osStr[pos] == ')')
     413             :         {
     414         246 :             osToken = osStr[pos];
     415         246 :             pos++;
     416             :         }
     417             :         else
     418             :         {
     419         718 :             size_t pos2 = osStr.find_first_of(") \t\n\r,", pos);
     420         718 :             if (pos2 == std::string::npos)
     421          46 :                 osToken = osStr.substr(pos);
     422             :             else
     423         672 :                 osToken = osStr.substr(pos, pos2 - pos);
     424         718 :             pos = pos2;
     425             :         }
     426        1132 :         return osToken;
     427             :     };
     428             : 
     429             :     // Parses CREATE TABLE definition for column UNIQUE keywords
     430             :     {
     431        1478 :         const auto nPosStart = tableDefinition.find('(');
     432        1478 :         const auto nPosEnd = tableDefinition.rfind(')');
     433        1478 :         if (nPosStart != std::string::npos && nPosEnd != std::string::npos &&
     434        2956 :             nPosEnd > nPosStart &&
     435        2956 :             CPLString(tableDefinition).toupper().find("UNIQUE") !=
     436             :                 std::string::npos)
     437             :         {
     438             :             tableDefinition =
     439          45 :                 tableDefinition.substr(nPosStart + 1, nPosEnd - nPosStart - 1);
     440          45 :             size_t pos = 0;
     441          45 :             bool bHasConstraint = false;
     442             :             while (true)
     443             :             {
     444         285 :                 size_t posBackup = pos;
     445         285 :                 if (EQUAL(
     446             :                         GetNextToken(tableDefinition, posBackup, true).c_str(),
     447             :                         "CONSTRAINT"))
     448             :                 {
     449           1 :                     bHasConstraint = true;
     450           1 :                     break;
     451             :                 }
     452             : 
     453             :                 const std::string osColName =
     454         284 :                     GetNextToken(tableDefinition, pos, false);
     455         284 :                 if (osColName.empty())
     456             :                 {
     457          44 :                     break;
     458             :                 }
     459             :                 while (true)
     460             :                 {
     461             :                     const std::string osToken =
     462         641 :                         GetNextToken(tableDefinition, pos, true);
     463         641 :                     if (osToken.empty() || osToken == ",")
     464         240 :                         break;
     465         401 :                     if (EQUAL(osToken.c_str(), "UNIQUE"))
     466             :                     {
     467          29 :                         uniqueFieldsUC.insert(CPLString(osColName).toupper());
     468             :                     }
     469         401 :                 }
     470         240 :             }
     471             : 
     472             :             // Process https://www.sqlite.org/syntax/table-constraint.html
     473          45 :             if (bHasConstraint)
     474             :             {
     475             :                 while (true)
     476             :                 {
     477           3 :                     if (!EQUAL(GetNextToken(tableDefinition, pos, true).c_str(),
     478             :                                "CONSTRAINT"))
     479             :                     {
     480           1 :                         break;
     481             :                     }
     482             : 
     483             :                     const std::string osConstraintName =
     484           3 :                         GetNextToken(tableDefinition, pos, false);
     485           3 :                     if (osConstraintName.empty())
     486             :                     {
     487           0 :                         break;
     488             :                     }
     489             : 
     490             :                     const std::string osConstraintType =
     491           3 :                         GetNextToken(tableDefinition, pos, true);
     492           3 :                     if (osConstraintType.empty())
     493             :                     {
     494           0 :                         break;
     495             :                     }
     496             : 
     497           3 :                     if (EQUAL(osConstraintType.c_str(), "UNIQUE"))
     498             :                     {
     499             :                         std::string osToken =
     500           2 :                             GetNextToken(tableDefinition, pos, true);
     501           2 :                         if (osToken != "(")
     502           0 :                             break;
     503             : 
     504             :                         const std::string osColName =
     505           2 :                             GetNextToken(tableDefinition, pos, false);
     506           2 :                         osToken = GetNextToken(tableDefinition, pos, true);
     507           2 :                         if (osToken == ")")
     508             :                         {
     509             :                             // Only takes into account single column unique constraint
     510             :                             uniqueFieldsUC.insert(
     511           1 :                                 CPLString(osColName).toupper());
     512             :                         }
     513             :                         else
     514             :                         {
     515             :                             // Skip tokens until ')'
     516           1 :                             if (!osToken.empty())
     517             :                             {
     518           1 :                                 do
     519             :                                 {
     520           4 :                                     osToken = GetNextToken(tableDefinition, pos,
     521           2 :                                                            true);
     522           2 :                                 } while (!osToken.empty() && osToken != ")");
     523             :                             }
     524           1 :                             if (osToken.empty())
     525           0 :                                 break;
     526             :                         }
     527           2 :                         osToken = GetNextToken(tableDefinition, pos, true);
     528           2 :                         if (osToken != ",")
     529           1 :                             break;
     530             :                     }
     531             :                     else
     532             :                     {
     533             :                         // Skip ignored constraint types by looking for the
     534             :                         // next "," token, that is not inside parenthesis.
     535           1 :                         int nCountParenthesis = 0;
     536           1 :                         std::string osToken;
     537             :                         while (true)
     538             :                         {
     539           6 :                             osToken = GetNextToken(tableDefinition, pos, true);
     540           6 :                             if (osToken.empty())
     541           0 :                                 break;
     542           6 :                             if (nCountParenthesis == 0 && osToken == ",")
     543           1 :                                 break;
     544           5 :                             else if (osToken == "(")
     545           1 :                                 nCountParenthesis++;
     546           4 :                             else if (osToken == ")")
     547             :                             {
     548           1 :                                 nCountParenthesis--;
     549           1 :                                 if (nCountParenthesis < 0)
     550             :                                 {
     551           0 :                                     break;
     552             :                                 }
     553             :                             }
     554             :                         }
     555           1 :                         if (!(nCountParenthesis == 0 && osToken == ","))
     556           0 :                             break;
     557             :                     }
     558           2 :                 }
     559             :             }
     560             :         }
     561             :     }
     562             : 
     563             :     // Search indexes:
     564             : 
     565             :     const auto ProcessIndexDefinition =
     566         115 :         [&uniqueFieldsUC, &GetNextToken](const std::string &indexDefinitionIn)
     567             :     {
     568          29 :         const auto nPosStart = indexDefinitionIn.find('(');
     569          29 :         const auto nPosEnd = indexDefinitionIn.rfind(')');
     570          29 :         if (nPosStart != std::string::npos && nPosEnd != std::string::npos &&
     571             :             nPosEnd > nPosStart)
     572             :         {
     573             :             std::string indexDefinitionMod = indexDefinitionIn.substr(
     574          58 :                 nPosStart + 1, nPosEnd - nPosStart - 1);
     575          29 :             size_t pos = 0;
     576             :             const std::string osColName =
     577          58 :                 GetNextToken(indexDefinitionMod, pos, false);
     578             :             // Only matches index on single columns
     579          29 :             if (GetNextToken(indexDefinitionMod, pos, false).empty())
     580             :             {
     581          28 :                 uniqueFieldsUC.insert(CPLString(osColName).toupper());
     582             :             }
     583             :         }
     584          29 :     };
     585             : 
     586        1478 :     if (sqliteMasterContent.empty())
     587             :     {
     588        1476 :         char *pszTableDefinitionSQL = sqlite3_mprintf(
     589             :             "SELECT sql FROM sqlite_master WHERE type='index' AND"
     590             :             " UPPER(tbl_name)='%q' AND UPPER(sql) "
     591             :             "LIKE 'CREATE UNIQUE INDEX%%'",
     592             :             upperTableName.c_str());
     593        2952 :         auto oResultTable = SQLQuery(poDb, pszTableDefinitionSQL);
     594        1476 :         sqlite3_free(pszTableDefinitionSQL);
     595             : 
     596        1476 :         if (!oResultTable)
     597             :         {
     598           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     599             :                      "Error searching indexes for table %s", pszTableName);
     600             :         }
     601        1476 :         else if (oResultTable->RowCount() >= 0)
     602             :         {
     603        1503 :             for (int rowCnt = 0; rowCnt < oResultTable->RowCount(); ++rowCnt)
     604             :             {
     605          54 :                 std::string indexDefinition{oResultTable->GetValue(0, rowCnt)};
     606          27 :                 ProcessIndexDefinition(indexDefinition);
     607             :             }
     608             :         }
     609             :     }
     610             :     else
     611             :     {
     612         116 :         for (const auto &row : sqliteMasterContent)
     613             :         {
     614          58 :             if (row.osType == "index" &&
     615         172 :                 CPLString(row.osTableName).toupper() == upperTableName &&
     616           4 :                 STARTS_WITH_CI(row.osSQL.c_str(), "CREATE UNIQUE INDEX"))
     617             :             {
     618           4 :                 std::string indexDefinition = row.osSQL;
     619           2 :                 ProcessIndexDefinition(indexDefinition);
     620             :             }
     621             :         }
     622             :     }
     623             : 
     624        1478 :     return uniqueFieldsUC;
     625             : }
     626             : 
     627             : /************************************************************************/
     628             : /*               OGRSQLiteRTreeRequiresTrustedSchemaOn()                */
     629             : /************************************************************************/
     630             : 
     631             : /** Whether the use of a RTree in triggers or views requires trusted_schema
     632             :  * PRAGMA to be set to ON */
     633         958 : bool OGRSQLiteRTreeRequiresTrustedSchemaOn()
     634             : {
     635          43 :     static bool b = []()
     636             :     {
     637          43 :         sqlite3 *hDB = nullptr;
     638             :         int rc =
     639          43 :             sqlite3_open_v2(":memory:", &hDB, SQLITE_OPEN_READWRITE, nullptr);
     640          43 :         if (rc != SQLITE_OK)
     641             :         {
     642           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     643             :                      "sqlite3_open_v2(:memory:) failed");
     644           0 :             sqlite3_close(hDB);
     645           0 :             return false;
     646             :         }
     647          43 :         rc = sqlite3_exec(hDB,
     648             :                           "CREATE VIRTUAL TABLE foo_rtree USING rtree(id, "
     649             :                           "minx, miny, maxx, maxy);",
     650             :                           nullptr, nullptr, nullptr);
     651          43 :         if (rc != SQLITE_OK)
     652             :         {
     653           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     654             :                      "CREATE VIRTUAL TABLE foo_rtree failed");
     655           0 :             sqlite3_close(hDB);
     656           0 :             return false;
     657             :         }
     658          43 :         rc = sqlite3_exec(hDB, "CREATE VIEW v AS SELECT * FROM foo_rtree;",
     659             :                           nullptr, nullptr, nullptr);
     660          43 :         if (rc != SQLITE_OK)
     661             :         {
     662           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     663             :                      "CREATE VIEW v AS SELECT * FROM foo_rtree failed");
     664           0 :             sqlite3_close(hDB);
     665           0 :             return false;
     666             :         }
     667             :         // Try to read the virtual table from a view. As of today (sqlite 3.43.1)
     668             :         // this require trusted_schema = ON
     669          43 :         rc = sqlite3_exec(hDB, "SELECT * FROM v", nullptr, nullptr, nullptr);
     670          43 :         bool bRequiresTrustedSchemaOn = false;
     671          43 :         if (rc != SQLITE_OK)
     672             :         {
     673          43 :             CPL_IGNORE_RET_VAL(sqlite3_exec(hDB, "PRAGMA trusted_schema = ON",
     674             :                                             nullptr, nullptr, nullptr));
     675             :             rc =
     676          43 :                 sqlite3_exec(hDB, "SELECT * FROM v", nullptr, nullptr, nullptr);
     677          43 :             if (rc == SQLITE_OK)
     678          43 :                 bRequiresTrustedSchemaOn = true;
     679             :         }
     680          43 :         sqlite3_close(hDB);
     681          43 :         return bRequiresTrustedSchemaOn;
     682         958 :     }();
     683         958 :     return b;
     684             : }
     685             : 
     686             : /************************************************************************/
     687             : /*               OGRSQLiteIsSpatialFunctionReturningGeometry()          */
     688             : /************************************************************************/
     689             : 
     690        2208 : bool OGRSQLiteIsSpatialFunctionReturningGeometry(const char *pszName)
     691             : {
     692        2208 :     const char *const apszFunctions[] = {
     693             :         "SetSRID(",
     694             :         "IsValidDetail(",
     695             :         "Boundary(",
     696             :         "Envelope(",
     697             :         "ST_Expand(",
     698             :         "ST_Reverse(",
     699             :         "ST_ForceLHR(",
     700             :         "ST_ForcePolygonCW(",
     701             :         "ST_ForcePolygonCCW(",
     702             :         "SanitizeGeometry(",
     703             :         "EnsureClosedRings(",
     704             :         "RemoveRepeatedPoints(",
     705             :         "CastToPoint(",
     706             :         "CastToLinestring(",
     707             :         "CastToPolygon(",
     708             :         "CastToMultiPoint(",
     709             :         "CastToMultiLinestring(",
     710             :         "CastToMultiPolygon(",
     711             :         "CastToGeometryCollection(",
     712             :         "CastToMulti(",
     713             :         "ST_Multi(",
     714             :         "CastToSingle(",
     715             :         "CastToXY(",
     716             :         "CastToXYZ(",
     717             :         "CastToXYM(",
     718             :         "CastToXYZM(",
     719             :         "StartPoint(",
     720             :         "ST_EndPoint(",
     721             :         "PointOnSurface(",
     722             :         "Simplify(",
     723             :         "ST_Generalize(",
     724             :         "SimplifyPreserveTopology(",
     725             :         "PointN(",
     726             :         "AddPoint(",
     727             :         "SetPoint(",
     728             :         "SetStartPoint(",
     729             :         "SetEndPoint(",
     730             :         "RemovePoint(",
     731             :         "Centroid(",
     732             :         "ExteriorRing(",
     733             :         "InteriorRingN(",
     734             :         "GeometryN(",
     735             :         "ST_AddMeasure(",
     736             :         "ST_Locate_Along_Measure(",
     737             :         "ST_LocateAlong(",
     738             :         "ST_Locate_Between_Measures(",
     739             :         "ST_LocateBetween(",
     740             :         "ST_TrajectoryInterpolarePoint(",
     741             :         "Intersection(",
     742             :         "Difference(",
     743             :         "GUnion(",
     744             :         "ST_Union(",  // UNION is not a valid function name
     745             :         "SymDifference(",
     746             :         "Buffer(",
     747             :         "ConvexHull(",
     748             :         "OffsetCurve(",
     749             :         "SingleSidedBuffer(",
     750             :         "SharedPaths(",
     751             :         "Line_Interpolate_Point(",
     752             :         "Line_Interpolate_Equidistant_Points(",
     753             :         "Line_Substring(",
     754             :         "ClosestPoint(",
     755             :         "ShortestLine(",
     756             :         "Snap(",
     757             :         "Collect(",
     758             :         "LineMerge(",
     759             :         "BuildArea(",
     760             :         "Polygonize(",
     761             :         "MakePolygon(",
     762             :         "UnaryUnion(",
     763             :         "UnaryUnion(",
     764             :         "DrapeLine(",
     765             :         "DrapeLineExceptions(",
     766             :         "DissolveSegments(",
     767             :         "DissolvePoints(",
     768             :         "LinesFromRings(",
     769             :         "LinesCutAtNodes(",
     770             :         "RingsCutAtNodes(",
     771             :         "CollectionExtract(",
     772             :         "ExtractMultiPoint(",
     773             :         "ExtractMultiLinestring(",
     774             :         "ExtractMultiPolygon(",
     775             :         "DelaunayTriangulation(",
     776             :         "VoronojDiagram(",
     777             :         "ConcaveHull(",
     778             :         "MakeValid(",
     779             :         "MakeValidDiscarded(",
     780             :         "Segmentize(",
     781             :         "Split(",
     782             :         "SplitLeft(",
     783             :         "SplitRight(",
     784             :         "SnapAndSplit(",
     785             :         "Project(",
     786             :         "SnapToGrid(",
     787             :         "ST_Node(",
     788             :         "SelfIntersections(",
     789             :         "ST_Subdivide(",
     790             :         "Transform(",
     791             :         "TransformXY(",
     792             :         "TransformXYZ(",
     793             :         "ShiftCoords(",
     794             :         "ShiftCoordinates(",
     795             :         "ST_Translate(",
     796             :         "ST_Shift_Longitude(",
     797             :         "NormalizeLonLat(",
     798             :         "ScaleCoords(",
     799             :         "ScaleCoordinates(",
     800             :         "RotateCoords(",
     801             :         "RotateCoordinates(",
     802             :         "ReflectCoords(",
     803             :         "ReflectCoordinates(",
     804             :         "SwapCoords(",
     805             :         "SwapCoordinates(",
     806             :         "ATM_Transform(",
     807             :         "gpkgMakePoint(",
     808             :         "gpkgMakePointZ(",
     809             :         "gpkgMakePointZM(",
     810             :         "gpkgMakePointM(",
     811             :         "AsGPB(",
     812             :         "GeomFromGPB(",
     813             :         "CastAutomagic(",
     814             :     };
     815      268520 :     for (const char *pszFunction : apszFunctions)
     816             :     {
     817      266328 :         if (STARTS_WITH_CI(pszName, pszFunction) ||
     818      266327 :             (!STARTS_WITH_CI(pszFunction, "ST_") &&
     819      224435 :              STARTS_WITH_CI(pszName, "ST_") &&
     820        4015 :              STARTS_WITH_CI(pszName + strlen("ST_"), pszFunction)))
     821             :         {
     822          16 :             return true;
     823             :         }
     824             :     }
     825        2192 :     return false;
     826             : }
     827             : 
     828           0 : double SQLResult::GetValueAsDouble(int iColNum, int iRowNum) const
     829             : {
     830           0 :     const char *pszValue = GetValue(iColNum, iRowNum);
     831           0 :     if (!pszValue)
     832           0 :         return 0;
     833             : 
     834           0 :     return CPLStrtod(pszValue, nullptr);
     835             : }
     836             : 
     837             : /************************************************************************/
     838             : /*                  OGRSQLite_gdal_get_pixel_value_common()             */
     839             : /************************************************************************/
     840             : 
     841          27 : void OGRSQLite_gdal_get_pixel_value_common(const char *pszFunctionName,
     842             :                                            sqlite3_context *pContext, int argc,
     843             :                                            sqlite3_value **argv,
     844             :                                            GDALDataset *poDS)
     845             : {
     846          27 :     if (sqlite3_value_type(argv[1]) != SQLITE_INTEGER ||
     847          25 :         sqlite3_value_type(argv[2]) != SQLITE_TEXT ||
     848          23 :         (sqlite3_value_type(argv[3]) != SQLITE_INTEGER &&
     849           2 :          sqlite3_value_type(argv[3]) != SQLITE_FLOAT) ||
     850          21 :         (sqlite3_value_type(argv[4]) != SQLITE_INTEGER &&
     851          54 :          sqlite3_value_type(argv[4]) != SQLITE_FLOAT) ||
     852           2 :         (argc == 6 && sqlite3_value_type(argv[5]) != SQLITE_TEXT))
     853             :     {
     854           8 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid arguments to %s()",
     855             :                  pszFunctionName);
     856           8 :         sqlite3_result_null(pContext);
     857           8 :         return;
     858             :     }
     859             : 
     860          19 :     const int nBand = sqlite3_value_int(argv[1]);
     861          19 :     auto poBand = poDS->GetRasterBand(nBand);
     862          19 :     if (!poBand)
     863             :     {
     864           2 :         sqlite3_result_null(pContext);
     865           2 :         return;
     866             :     }
     867             : 
     868             :     const char *pszCoordType =
     869          17 :         reinterpret_cast<const char *>(sqlite3_value_text(argv[2]));
     870             :     double x, y;
     871          17 :     if (EQUAL(pszCoordType, "georef"))
     872             :     {
     873           6 :         const double X = sqlite3_value_double(argv[3]);
     874           6 :         const double Y = sqlite3_value_double(argv[4]);
     875             :         double adfGeoTransform[6];
     876           6 :         if (poDS->GetGeoTransform(adfGeoTransform) != CE_None)
     877             :         {
     878           0 :             sqlite3_result_null(pContext);
     879           0 :             return;
     880             :         }
     881             :         double adfInvGT[6];
     882           6 :         if (!GDALInvGeoTransform(adfGeoTransform, adfInvGT))
     883             :         {
     884           0 :             sqlite3_result_null(pContext);
     885           0 :             return;
     886             :         }
     887           6 :         x = adfInvGT[0] + X * adfInvGT[1] + Y * adfInvGT[2];
     888           6 :         y = adfInvGT[3] + X * adfInvGT[4] + Y * adfInvGT[5];
     889             :     }
     890          11 :     else if (EQUAL(pszCoordType, "pixel"))
     891             :     {
     892           9 :         x = sqlite3_value_int(argv[3]);
     893           9 :         y = sqlite3_value_int(argv[4]);
     894             :     }
     895             :     else
     896             :     {
     897           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     898             :                  "Invalid value for 3rd argument of gdal_get_pixel_value(): "
     899             :                  "only 'georef' or 'pixel' are supported");
     900           2 :         sqlite3_result_null(pContext);
     901           2 :         return;
     902             :     }
     903          28 :     if (x < 0 || x >= poDS->GetRasterXSize() || y < 0 ||
     904          13 :         y >= poDS->GetRasterYSize())
     905             :     {
     906           2 :         sqlite3_result_null(pContext);
     907           2 :         return;
     908             :     }
     909             : 
     910             :     const auto eInterpolation =
     911          15 :         argc == 6 ? GDALRasterIOGetResampleAlg(reinterpret_cast<const char *>(
     912           2 :                         sqlite3_value_text(argv[5])))
     913          13 :                   : GRIORA_NearestNeighbour;
     914             : 
     915          13 :     const auto eDT = poBand->GetRasterDataType();
     916          13 :     if (eDT == GDT_Int64 && eInterpolation == GRIORA_NearestNeighbour)
     917             :     {
     918           1 :         int64_t nValue = 0;
     919           1 :         if (poBand->RasterIO(GF_Read, static_cast<int>(x), static_cast<int>(y),
     920             :                              1, 1, &nValue, 1, 1, GDT_Int64, 0, 0,
     921           1 :                              nullptr) != CE_None)
     922             :         {
     923           0 :             sqlite3_result_null(pContext);
     924           0 :             return;
     925             :         }
     926           1 :         return sqlite3_result_int64(pContext, nValue);
     927             :     }
     928          12 :     else if (eDT == GDT_UInt64 && eInterpolation == GRIORA_NearestNeighbour)
     929             :     {
     930           2 :         uint64_t nValue = 0;
     931           2 :         if (poBand->RasterIO(GF_Read, static_cast<int>(x), static_cast<int>(y),
     932             :                              1, 1, &nValue, 1, 1, GDT_UInt64, 0, 0,
     933           2 :                              nullptr) != CE_None)
     934             :         {
     935           0 :             sqlite3_result_null(pContext);
     936           0 :             return;
     937             :         }
     938           2 :         if (nValue > static_cast<uint64_t>(INT64_MAX))
     939             :         {
     940             :             // Not ideal
     941           1 :             return sqlite3_result_double(pContext, static_cast<double>(nValue));
     942             :         }
     943             :         else
     944             :         {
     945           1 :             return sqlite3_result_int64(pContext, static_cast<int64_t>(nValue));
     946             :         }
     947             :     }
     948             :     else
     949             :     {
     950          10 :         double dfValue = 0;
     951          20 :         if (poBand->InterpolateAtPoint(x, y, eInterpolation, &dfValue,
     952          10 :                                        nullptr) != CE_None)
     953             :         {
     954           0 :             sqlite3_result_null(pContext);
     955           0 :             return;
     956             :         }
     957          10 :         return sqlite3_result_double(pContext, dfValue);
     958             :     }
     959             : }

Generated by: LCOV version 1.14