LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/sqlite - ogrsqliteregexp.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 74 89 83.1 %
Date: 2025-08-19 18:03:11 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,
     141             :                                     [[maybe_unused]] int argc,
     142             :                                     sqlite3_value **argv)
     143             : {
     144          40 :     CPLAssert(argc == 2);
     145             : 
     146             :     const char *re =
     147          40 :         reinterpret_cast<const char *>(sqlite3_value_text(argv[0]));
     148          40 :     if (!re)
     149             :     {
     150           1 :         CPLDebug("SQLITE", "REGEXP: no regexp");
     151           1 :         sqlite3_result_null(ctx);
     152           1 :         return;
     153             :     }
     154             : 
     155          39 :     if (sqlite3_value_type(argv[1]) == SQLITE_NULL)
     156             :     {
     157           1 :         sqlite3_result_null(ctx);
     158           1 :         return;
     159             :     }
     160             : 
     161             :     const char *str =
     162          38 :         reinterpret_cast<const char *>(sqlite3_value_text(argv[1]));
     163          38 :     if (!str)
     164             :     {
     165           0 :         CPLDebug("SQLITE", "REGEXP: no string");
     166           0 :         sqlite3_result_null(ctx);
     167           0 :         return;
     168             :     }
     169             : 
     170          38 :     pcre2_code *p = re_compile_with_cache(ctx, re);
     171          38 :     if (!p)
     172           1 :         return;
     173             : 
     174          37 :     pcre2_match_data *md = pcre2_match_data_create_from_pattern(p, nullptr);
     175          37 :     if (!md)
     176             :     {
     177           0 :         sqlite3_result_error(ctx, "could not create match data block", -1);
     178           0 :         return;
     179             :     }
     180             : 
     181             : #ifdef HAVE_GCC_DIAGNOSTIC_PUSH
     182             : #pragma GCC diagnostic push
     183             : #pragma GCC diagnostic ignored "-Wold-style-cast"
     184             : #endif
     185          37 :     int rc = pcre2_match(p, reinterpret_cast<const PCRE2_UCHAR8 *>(str),
     186             :                          PCRE2_ZERO_TERMINATED, 0, 0, md, nullptr);
     187             : #ifdef HAVE_GCC_DIAGNOSTIC_PUSH
     188             : #pragma GCC diagnostic pop
     189             : #endif
     190             : 
     191          37 :     pcre2_match_data_free(md);
     192             : 
     193          37 :     sqlite3_result_int(ctx, rc >= 0);
     194             : }
     195             : 
     196             : #elif defined(HAVE_PCRE)
     197             : 
     198             : #include <pcre.h>
     199             : 
     200             : typedef struct
     201             : {
     202             :     char *s;
     203             :     pcre *p;
     204             :     pcre_extra *e;
     205             : } cache_entry;
     206             : 
     207             : constexpr int CACHE_SIZE = 16;
     208             : 
     209             : /************************************************************************/
     210             : /*                         OGRSQLiteREGEXPFunction()                    */
     211             : /************************************************************************/
     212             : 
     213             : static void OGRSQLiteREGEXPFunction(sqlite3_context *ctx,
     214             :                                     [[maybe_unused]] int argc,
     215             :                                     sqlite3_value **argv)
     216             : {
     217             :     CPLAssert(argc == 2);
     218             : 
     219             :     const char *re =
     220             :         reinterpret_cast<const char *>(sqlite3_value_text(argv[0]));
     221             :     if (!re)
     222             :     {
     223             :         CPLDebug("SQLITE", "REGEXP: no regexp");
     224             :         sqlite3_result_null(ctx);
     225             :         return;
     226             :     }
     227             : 
     228             :     if (sqlite3_value_type(argv[1]) == SQLITE_NULL)
     229             :     {
     230             :         sqlite3_result_null(ctx);
     231             :         return;
     232             :     }
     233             : 
     234             :     const char *str =
     235             :         reinterpret_cast<const char *>(sqlite3_value_text(argv[1]));
     236             :     if (!str)
     237             :     {
     238             :         CPLDebug("SQLITE", "REGEXP: no string");
     239             :         sqlite3_result_null(ctx);
     240             :         return;
     241             :     }
     242             : 
     243             :     /* simple LRU cache */
     244             :     cache_entry *cache = static_cast<cache_entry *>(sqlite3_user_data(ctx));
     245             :     CPLAssert(cache);
     246             : 
     247             :     bool found = false;
     248             :     int i = 0;  // Used after for.
     249             :     for (; i < CACHE_SIZE && cache[i].s; i++)
     250             :     {
     251             :         if (strcmp(re, cache[i].s) == 0)
     252             :         {
     253             :             found = true;
     254             :             break;
     255             :         }
     256             :     }
     257             : 
     258             :     if (found)
     259             :     {
     260             :         if (i > 0)
     261             :         {
     262             :             cache_entry c = cache[i];
     263             :             memmove(cache + 1, cache, i * sizeof(cache_entry));
     264             :             cache[0] = c;
     265             :         }
     266             :     }
     267             :     else
     268             :     {
     269             :         cache_entry c;
     270             :         const char *err = nullptr;
     271             :         int pos = 0;
     272             :         c.p = pcre_compile(re, 0, &err, &pos, nullptr);
     273             :         if (!c.p)
     274             :         {
     275             :             char *e2 = sqlite3_mprintf("%s: %s (offset %d)", re, err, pos);
     276             :             sqlite3_result_error(ctx, e2, -1);
     277             :             sqlite3_free(e2);
     278             :             return;
     279             :         }
     280             :         c.e = pcre_study(c.p, 0, &err);
     281             :         c.s = VSIStrdup(re);
     282             :         if (!c.s)
     283             :         {
     284             :             sqlite3_result_error(ctx, "strdup: ENOMEM", -1);
     285             :             pcre_free(c.p);
     286             :             pcre_free(c.e);
     287             :             return;
     288             :         }
     289             :         i = CACHE_SIZE - 1;
     290             :         if (cache[i].s)
     291             :         {
     292             :             CPLFree(cache[i].s);
     293             :             CPLAssert(cache[i].p);
     294             :             pcre_free(cache[i].p);
     295             :             pcre_free(cache[i].e);
     296             :         }
     297             :         memmove(cache + 1, cache, i * sizeof(cache_entry));
     298             :         cache[0] = c;
     299             :     }
     300             :     pcre *p = cache[0].p;
     301             :     CPLAssert(p);
     302             :     pcre_extra *e = cache[0].e;
     303             : 
     304             :     int rc =
     305             :         pcre_exec(p, e, str, static_cast<int>(strlen(str)), 0, 0, nullptr, 0);
     306             :     sqlite3_result_int(ctx, rc >= 0);
     307             : }
     308             : 
     309             : #endif  // HAVE_PCRE
     310             : 
     311             : /************************************************************************/
     312             : /*                        OGRSQLiteRegisterRegExpFunction()             */
     313             : /************************************************************************/
     314             : 
     315        3366 : static void *OGRSQLiteRegisterRegExpFunction(sqlite3 *
     316             : #if defined(HAVE_PCRE) || defined(HAVE_PCRE2)
     317             :                                                  hDB
     318             : #endif
     319             : )
     320             : {
     321             : #if defined(HAVE_PCRE) || defined(HAVE_PCRE2)
     322             : 
     323             :     /* For debugging purposes mostly */
     324        1293 :     if (!CPLTestBool(CPLGetConfigOption("OGR_SQLITE_REGEXP", "YES")))
     325           0 :         return nullptr;
     326             : 
     327             :     /* Check if we really need to define our own REGEXP function */
     328             :     int rc =
     329        1293 :         sqlite3_exec(hDB, "SELECT 'a' REGEXP 'a'", nullptr, nullptr, nullptr);
     330        1293 :     if (rc == SQLITE_OK)
     331             :     {
     332           0 :         CPLDebug("SQLITE", "REGEXP already available");
     333           0 :         return nullptr;
     334             :     }
     335             : 
     336             :     cache_entry *cache =
     337        1293 :         static_cast<cache_entry *>(CPLCalloc(CACHE_SIZE, sizeof(cache_entry)));
     338        1293 :     sqlite3_create_function(hDB, "REGEXP", 2, SQLITE_UTF8, cache,
     339             :                             OGRSQLiteREGEXPFunction, nullptr, nullptr);
     340             : 
     341             :     /* To clear the error flag */
     342        1293 :     sqlite3_exec(hDB, "SELECT 1", nullptr, nullptr, nullptr);
     343             : 
     344        1293 :     return cache;
     345             : #else   // HAVE_PCRE
     346        2073 :     return nullptr;
     347             : #endif  // HAVE_PCRE
     348             : }
     349             : 
     350             : /************************************************************************/
     351             : /*                         OGRSQLiteFreeRegExpCache()                   */
     352             : /************************************************************************/
     353             : 
     354        3366 : static void OGRSQLiteFreeRegExpCache(void *
     355             : #if defined(HAVE_PCRE) || defined(HAVE_PCRE2)
     356             :                                          hRegExpCache
     357             : #endif
     358             : )
     359             : {
     360             : #ifdef HAVE_PCRE2
     361        1293 :     if (hRegExpCache == nullptr)
     362           0 :         return;
     363             : 
     364        1293 :     cache_entry *cache = static_cast<cache_entry *>(hRegExpCache);
     365        1309 :     for (int i = 0; i < CACHE_SIZE && cache[i].s; i++)
     366             :     {
     367          16 :         CPLFree(cache[i].s);
     368          16 :         CPLAssert(cache[i].p);
     369          16 :         pcre2_code_free(cache[i].p);
     370             :     }
     371        1293 :     CPLFree(cache);
     372             : #elif defined(HAVE_PCRE)
     373             :     if (hRegExpCache == nullptr)
     374             :         return;
     375             : 
     376             :     cache_entry *cache = static_cast<cache_entry *>(hRegExpCache);
     377             :     for (int i = 0; i < CACHE_SIZE && cache[i].s; i++)
     378             :     {
     379             :         CPLFree(cache[i].s);
     380             :         CPLAssert(cache[i].p);
     381             :         pcre_free(cache[i].p);
     382             :         pcre_free(cache[i].e);
     383             :     }
     384             :     CPLFree(cache);
     385             : #endif  // HAVE_PCRE
     386        2073 : }

Generated by: LCOV version 1.14