LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/sqlite - ogrsqliteregexp.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 73 87 83.9 %
Date: 2025-07-09 17:50:03 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  SQLite REGEXP function
       5             :  * Author:   Even Rouault, even dot rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2012, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : /* WARNING: VERY IMPORTANT NOTE: This file MUST not be directly compiled as */
      14             : /* a standalone object. It must be included from ogrsqlitevirtualogr.cpp */
      15             : /* (actually from ogrsqlitesqlfunctions.cpp) */
      16             : #ifndef COMPILATION_ALLOWED
      17             : #error See comment in file
      18             : #endif
      19             : 
      20             : /* This code originates from pcre.c from the sqlite3-pcre extension */
      21             : /* from http://laltromondo.dynalias.net/~iki/informatica/soft/sqlite3-pcre/ */
      22             : /* whose header is : */
      23             : /*
      24             :  * Written by Alexey Tourbin <at@altlinux.org>.
      25             :  *
      26             :  * The author has dedicated the code to the public domain.  Anyone is free
      27             :  * to copy, modify, publish, use, compile, sell, or distribute the original
      28             :  * code, either in source code form or as a compiled binary, for any purpose,
      29             :  * commercial or non-commercial, and by any means.
      30             :  */
      31             : 
      32             : // The pcre2 variant has been ported from
      33             : // https://github.com/pfmoore/sqlite-pcre2/blob/main/src/pcre.c which has the
      34             : // same license as above.
      35             : 
      36             : #include "ogrsqliteregexp.h"
      37             : #include "sqlite3.h"
      38             : 
      39             : #ifdef HAVE_PCRE2
      40             : 
      41             : #define PCRE2_CODE_UNIT_WIDTH 8
      42             : #include <pcre2.h>
      43             : 
      44             : typedef struct
      45             : {
      46             :     char *s;
      47             :     pcre2_code *p;
      48             : } cache_entry;
      49             : 
      50             : constexpr int CACHE_SIZE = 16;
      51             : 
      52          38 : static pcre2_code *re_compile_with_cache(sqlite3_context *ctx, const char *re)
      53             : {
      54          38 :     cache_entry *cache = static_cast<cache_entry *>(sqlite3_user_data(ctx));
      55             : 
      56          38 :     CPLAssert(cache);
      57             : 
      58          38 :     bool found = false;
      59             :     int i;
      60         449 :     for (i = 0; i < CACHE_SIZE && cache[i].s; i++)
      61         414 :         if (strcmp(re, cache[i].s) == 0)
      62             :         {
      63           3 :             found = true;
      64           3 :             break;
      65             :         }
      66             : 
      67          38 :     if (found)
      68             :     {
      69           3 :         if (i > 0)
      70             :         {
      71             :             /* Get the found entry */
      72           2 :             cache_entry c = cache[i];
      73             :             /* Move 0..i-1 up one - args are (dest, src, size) */
      74           2 :             memmove(cache + 1, cache, i * sizeof(cache_entry));
      75             :             /* Put the found entry at the start */
      76           2 :             cache[0] = c;
      77             :         }
      78             :     }
      79             :     else
      80             :     {
      81             :         /* Create a new entry */
      82          35 :         int errorcode = 0;
      83          35 :         PCRE2_SIZE pos = 0;
      84          35 :         uint32_t has_jit = 0;
      85             :         PCRE2_UCHAR8 err_buff[256];
      86             : 
      87             : #ifdef HAVE_GCC_DIAGNOSTIC_PUSH
      88             : #pragma GCC diagnostic push
      89             : #pragma GCC diagnostic ignored "-Wold-style-cast"
      90             : #endif
      91             :         pcre2_code *pat =
      92          35 :             pcre2_compile(reinterpret_cast<const PCRE2_UCHAR8 *>(re),
      93             :                           PCRE2_ZERO_TERMINATED, 0, &errorcode, &pos, nullptr);
      94             : #ifdef HAVE_GCC_DIAGNOSTIC_PUSH
      95             : #pragma GCC diagnostic pop
      96             : #endif
      97          35 :         if (!pat)
      98             :         {
      99           1 :             pcre2_get_error_message(errorcode, err_buff, sizeof(err_buff));
     100           1 :             char *e2 = sqlite3_mprintf("%s: %s (offset %d)", re, err_buff, pos);
     101           1 :             sqlite3_result_error(ctx, e2, -1);
     102           1 :             sqlite3_free(e2);
     103           1 :             return nullptr;
     104             :         }
     105          34 :         pcre2_config(PCRE2_CONFIG_JIT, &has_jit);
     106          34 :         if (has_jit)
     107             :         {
     108          34 :             errorcode = pcre2_jit_compile(pat, 0);
     109          34 :             if (errorcode)
     110             :             {
     111           0 :                 pcre2_get_error_message(errorcode, err_buff, sizeof(err_buff));
     112           0 :                 char *e2 = sqlite3_mprintf("%s: %s", re, err_buff);
     113           0 :                 sqlite3_result_error(ctx, e2, -1);
     114           0 :                 sqlite3_free(e2);
     115           0 :                 pcre2_code_free(pat);
     116           0 :                 return nullptr;
     117             :             }
     118             :         }
     119             :         /* Free the last cache entry if necessary */
     120          34 :         i = CACHE_SIZE - 1;
     121          34 :         if (cache[i].s)
     122             :         {
     123          18 :             VSIFree(cache[i].s);
     124          18 :             CPLAssert(cache[i].p);
     125          18 :             pcre2_code_free(cache[i].p);
     126             :         }
     127             :         /* Move everything up to make space */
     128          34 :         memmove(cache + 1, cache, i * sizeof(cache_entry));
     129          34 :         cache[0].s = VSIStrdup(re);
     130          34 :         cache[0].p = pat;
     131             :     }
     132             : 
     133          37 :     return cache[0].p;
     134             : }
     135             : 
     136             : /************************************************************************/
     137             : /*                         OGRSQLiteREGEXPFunction()                    */
     138             : /************************************************************************/
     139             : 
     140          40 : static void OGRSQLiteREGEXPFunction(sqlite3_context *ctx, CPL_UNUSED int argc,
     141             :                                     sqlite3_value **argv)
     142             : {
     143          40 :     CPLAssert(argc == 2);
     144             : 
     145             :     const char *re =
     146          40 :         reinterpret_cast<const char *>(sqlite3_value_text(argv[0]));
     147          40 :     if (!re)
     148             :     {
     149           1 :         sqlite3_result_error(ctx, "no regexp", -1);
     150           1 :         return;
     151             :     }
     152             : 
     153          39 :     if (sqlite3_value_type(argv[1]) == SQLITE_NULL)
     154             :     {
     155           1 :         sqlite3_result_int(ctx, 0);
     156           1 :         return;
     157             :     }
     158             : 
     159             :     const char *str =
     160          38 :         reinterpret_cast<const char *>(sqlite3_value_text(argv[1]));
     161          38 :     if (!str)
     162             :     {
     163           0 :         sqlite3_result_error(ctx, "no string", -1);
     164           0 :         return;
     165             :     }
     166             : 
     167          38 :     pcre2_code *p = re_compile_with_cache(ctx, re);
     168          38 :     if (!p)
     169           1 :         return;
     170             : 
     171          37 :     pcre2_match_data *md = pcre2_match_data_create_from_pattern(p, nullptr);
     172          37 :     if (!md)
     173             :     {
     174           0 :         sqlite3_result_error(ctx, "could not create match data block", -1);
     175           0 :         return;
     176             :     }
     177             : 
     178             : #ifdef HAVE_GCC_DIAGNOSTIC_PUSH
     179             : #pragma GCC diagnostic push
     180             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     181             : #endif
     182          37 :     int rc = pcre2_match(p, reinterpret_cast<const PCRE2_UCHAR8 *>(str),
     183             :                          PCRE2_ZERO_TERMINATED, 0, 0, md, nullptr);
     184             : #ifdef HAVE_GCC_DIAGNOSTIC_PUSH
     185             : #pragma GCC diagnostic pop
     186             : #endif
     187             : 
     188          37 :     pcre2_match_data_free(md);
     189             : 
     190          37 :     sqlite3_result_int(ctx, rc >= 0);
     191             : }
     192             : 
     193             : #elif defined(HAVE_PCRE)
     194             : 
     195             : #include <pcre.h>
     196             : 
     197             : typedef struct
     198             : {
     199             :     char *s;
     200             :     pcre *p;
     201             :     pcre_extra *e;
     202             : } cache_entry;
     203             : 
     204             : constexpr int CACHE_SIZE = 16;
     205             : 
     206             : /************************************************************************/
     207             : /*                         OGRSQLiteREGEXPFunction()                    */
     208             : /************************************************************************/
     209             : 
     210             : static void OGRSQLiteREGEXPFunction(sqlite3_context *ctx, CPL_UNUSED int argc,
     211             :                                     sqlite3_value **argv)
     212             : {
     213             :     CPLAssert(argc == 2);
     214             : 
     215             :     const char *re =
     216             :         reinterpret_cast<const char *>(sqlite3_value_text(argv[0]));
     217             :     if (!re)
     218             :     {
     219             :         sqlite3_result_error(ctx, "no regexp", -1);
     220             :         return;
     221             :     }
     222             : 
     223             :     if (sqlite3_value_type(argv[1]) == SQLITE_NULL)
     224             :     {
     225             :         sqlite3_result_int(ctx, 0);
     226             :         return;
     227             :     }
     228             : 
     229             :     const char *str =
     230             :         reinterpret_cast<const char *>(sqlite3_value_text(argv[1]));
     231             :     if (!str)
     232             :     {
     233             :         sqlite3_result_error(ctx, "no string", -1);
     234             :         return;
     235             :     }
     236             : 
     237             :     /* simple LRU cache */
     238             :     cache_entry *cache = static_cast<cache_entry *>(sqlite3_user_data(ctx));
     239             :     CPLAssert(cache);
     240             : 
     241             :     bool found = false;
     242             :     int i = 0;  // Used after for.
     243             :     for (; i < CACHE_SIZE && cache[i].s; i++)
     244             :     {
     245             :         if (strcmp(re, cache[i].s) == 0)
     246             :         {
     247             :             found = true;
     248             :             break;
     249             :         }
     250             :     }
     251             : 
     252             :     if (found)
     253             :     {
     254             :         if (i > 0)
     255             :         {
     256             :             cache_entry c = cache[i];
     257             :             memmove(cache + 1, cache, i * sizeof(cache_entry));
     258             :             cache[0] = c;
     259             :         }
     260             :     }
     261             :     else
     262             :     {
     263             :         cache_entry c;
     264             :         const char *err = nullptr;
     265             :         int pos = 0;
     266             :         c.p = pcre_compile(re, 0, &err, &pos, nullptr);
     267             :         if (!c.p)
     268             :         {
     269             :             char *e2 = sqlite3_mprintf("%s: %s (offset %d)", re, err, pos);
     270             :             sqlite3_result_error(ctx, e2, -1);
     271             :             sqlite3_free(e2);
     272             :             return;
     273             :         }
     274             :         c.e = pcre_study(c.p, 0, &err);
     275             :         c.s = VSIStrdup(re);
     276             :         if (!c.s)
     277             :         {
     278             :             sqlite3_result_error(ctx, "strdup: ENOMEM", -1);
     279             :             pcre_free(c.p);
     280             :             pcre_free(c.e);
     281             :             return;
     282             :         }
     283             :         i = CACHE_SIZE - 1;
     284             :         if (cache[i].s)
     285             :         {
     286             :             CPLFree(cache[i].s);
     287             :             CPLAssert(cache[i].p);
     288             :             pcre_free(cache[i].p);
     289             :             pcre_free(cache[i].e);
     290             :         }
     291             :         memmove(cache + 1, cache, i * sizeof(cache_entry));
     292             :         cache[0] = c;
     293             :     }
     294             :     pcre *p = cache[0].p;
     295             :     CPLAssert(p);
     296             :     pcre_extra *e = cache[0].e;
     297             : 
     298             :     int rc =
     299             :         pcre_exec(p, e, str, static_cast<int>(strlen(str)), 0, 0, nullptr, 0);
     300             :     sqlite3_result_int(ctx, rc >= 0);
     301             : }
     302             : 
     303             : #endif  // HAVE_PCRE
     304             : 
     305             : /************************************************************************/
     306             : /*                        OGRSQLiteRegisterRegExpFunction()             */
     307             : /************************************************************************/
     308             : 
     309        3366 : static void *OGRSQLiteRegisterRegExpFunction(sqlite3 *
     310             : #if defined(HAVE_PCRE) || defined(HAVE_PCRE2)
     311             :                                                  hDB
     312             : #endif
     313             : )
     314             : {
     315             : #if defined(HAVE_PCRE) || defined(HAVE_PCRE2)
     316             : 
     317             :     /* For debugging purposes mostly */
     318        1293 :     if (!CPLTestBool(CPLGetConfigOption("OGR_SQLITE_REGEXP", "YES")))
     319           0 :         return nullptr;
     320             : 
     321             :     /* Check if we really need to define our own REGEXP function */
     322             :     int rc =
     323        1293 :         sqlite3_exec(hDB, "SELECT 'a' REGEXP 'a'", nullptr, nullptr, nullptr);
     324        1293 :     if (rc == SQLITE_OK)
     325             :     {
     326           0 :         CPLDebug("SQLITE", "REGEXP already available");
     327           0 :         return nullptr;
     328             :     }
     329             : 
     330             :     cache_entry *cache =
     331        1293 :         static_cast<cache_entry *>(CPLCalloc(CACHE_SIZE, sizeof(cache_entry)));
     332        1293 :     sqlite3_create_function(hDB, "REGEXP", 2, SQLITE_UTF8, cache,
     333             :                             OGRSQLiteREGEXPFunction, nullptr, nullptr);
     334             : 
     335             :     /* To clear the error flag */
     336        1293 :     sqlite3_exec(hDB, "SELECT 1", nullptr, nullptr, nullptr);
     337             : 
     338        1293 :     return cache;
     339             : #else   // HAVE_PCRE
     340        2073 :     return nullptr;
     341             : #endif  // HAVE_PCRE
     342             : }
     343             : 
     344             : /************************************************************************/
     345             : /*                         OGRSQLiteFreeRegExpCache()                   */
     346             : /************************************************************************/
     347             : 
     348        3366 : static void OGRSQLiteFreeRegExpCache(void *
     349             : #if defined(HAVE_PCRE) || defined(HAVE_PCRE2)
     350             :                                          hRegExpCache
     351             : #endif
     352             : )
     353             : {
     354             : #ifdef HAVE_PCRE2
     355        1293 :     if (hRegExpCache == nullptr)
     356           0 :         return;
     357             : 
     358        1293 :     cache_entry *cache = static_cast<cache_entry *>(hRegExpCache);
     359        1309 :     for (int i = 0; i < CACHE_SIZE && cache[i].s; i++)
     360             :     {
     361          16 :         CPLFree(cache[i].s);
     362          16 :         CPLAssert(cache[i].p);
     363          16 :         pcre2_code_free(cache[i].p);
     364             :     }
     365        1293 :     CPLFree(cache);
     366             : #elif defined(HAVE_PCRE)
     367             :     if (hRegExpCache == nullptr)
     368             :         return;
     369             : 
     370             :     cache_entry *cache = static_cast<cache_entry *>(hRegExpCache);
     371             :     for (int i = 0; i < CACHE_SIZE && cache[i].s; i++)
     372             :     {
     373             :         CPLFree(cache[i].s);
     374             :         CPLAssert(cache[i].p);
     375             :         pcre_free(cache[i].p);
     376             :         pcre_free(cache[i].e);
     377             :     }
     378             :     CPLFree(cache);
     379             : #endif  // HAVE_PCRE
     380        2073 : }

Generated by: LCOV version 1.14