LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/sqlite - ogrsqliteregexp.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 72 86 83.7 %
Date: 2025-05-31 00:00:17 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          37 :     sqlite3_result_int(ctx, rc >= 0);
     188             : }
     189             : 
     190             : #elif defined(HAVE_PCRE)
     191             : 
     192             : #include <pcre.h>
     193             : 
     194             : typedef struct
     195             : {
     196             :     char *s;
     197             :     pcre *p;
     198             :     pcre_extra *e;
     199             : } cache_entry;
     200             : 
     201             : constexpr int CACHE_SIZE = 16;
     202             : 
     203             : /************************************************************************/
     204             : /*                         OGRSQLiteREGEXPFunction()                    */
     205             : /************************************************************************/
     206             : 
     207             : static void OGRSQLiteREGEXPFunction(sqlite3_context *ctx, CPL_UNUSED int argc,
     208             :                                     sqlite3_value **argv)
     209             : {
     210             :     CPLAssert(argc == 2);
     211             : 
     212             :     const char *re =
     213             :         reinterpret_cast<const char *>(sqlite3_value_text(argv[0]));
     214             :     if (!re)
     215             :     {
     216             :         sqlite3_result_error(ctx, "no regexp", -1);
     217             :         return;
     218             :     }
     219             : 
     220             :     if (sqlite3_value_type(argv[1]) == SQLITE_NULL)
     221             :     {
     222             :         sqlite3_result_int(ctx, 0);
     223             :         return;
     224             :     }
     225             : 
     226             :     const char *str =
     227             :         reinterpret_cast<const char *>(sqlite3_value_text(argv[1]));
     228             :     if (!str)
     229             :     {
     230             :         sqlite3_result_error(ctx, "no string", -1);
     231             :         return;
     232             :     }
     233             : 
     234             :     /* simple LRU cache */
     235             :     cache_entry *cache = static_cast<cache_entry *>(sqlite3_user_data(ctx));
     236             :     CPLAssert(cache);
     237             : 
     238             :     bool found = false;
     239             :     int i = 0;  // Used after for.
     240             :     for (; i < CACHE_SIZE && cache[i].s; i++)
     241             :     {
     242             :         if (strcmp(re, cache[i].s) == 0)
     243             :         {
     244             :             found = true;
     245             :             break;
     246             :         }
     247             :     }
     248             : 
     249             :     if (found)
     250             :     {
     251             :         if (i > 0)
     252             :         {
     253             :             cache_entry c = cache[i];
     254             :             memmove(cache + 1, cache, i * sizeof(cache_entry));
     255             :             cache[0] = c;
     256             :         }
     257             :     }
     258             :     else
     259             :     {
     260             :         cache_entry c;
     261             :         const char *err = nullptr;
     262             :         int pos = 0;
     263             :         c.p = pcre_compile(re, 0, &err, &pos, nullptr);
     264             :         if (!c.p)
     265             :         {
     266             :             char *e2 = sqlite3_mprintf("%s: %s (offset %d)", re, err, pos);
     267             :             sqlite3_result_error(ctx, e2, -1);
     268             :             sqlite3_free(e2);
     269             :             return;
     270             :         }
     271             :         c.e = pcre_study(c.p, 0, &err);
     272             :         c.s = VSIStrdup(re);
     273             :         if (!c.s)
     274             :         {
     275             :             sqlite3_result_error(ctx, "strdup: ENOMEM", -1);
     276             :             pcre_free(c.p);
     277             :             pcre_free(c.e);
     278             :             return;
     279             :         }
     280             :         i = CACHE_SIZE - 1;
     281             :         if (cache[i].s)
     282             :         {
     283             :             CPLFree(cache[i].s);
     284             :             CPLAssert(cache[i].p);
     285             :             pcre_free(cache[i].p);
     286             :             pcre_free(cache[i].e);
     287             :         }
     288             :         memmove(cache + 1, cache, i * sizeof(cache_entry));
     289             :         cache[0] = c;
     290             :     }
     291             :     pcre *p = cache[0].p;
     292             :     CPLAssert(p);
     293             :     pcre_extra *e = cache[0].e;
     294             : 
     295             :     int rc =
     296             :         pcre_exec(p, e, str, static_cast<int>(strlen(str)), 0, 0, nullptr, 0);
     297             :     sqlite3_result_int(ctx, rc >= 0);
     298             : }
     299             : 
     300             : #endif  // HAVE_PCRE
     301             : 
     302             : /************************************************************************/
     303             : /*                        OGRSQLiteRegisterRegExpFunction()             */
     304             : /************************************************************************/
     305             : 
     306        3359 : static void *OGRSQLiteRegisterRegExpFunction(sqlite3 *
     307             : #if defined(HAVE_PCRE) || defined(HAVE_PCRE2)
     308             :                                                  hDB
     309             : #endif
     310             : )
     311             : {
     312             : #if defined(HAVE_PCRE) || defined(HAVE_PCRE2)
     313             : 
     314             :     /* For debugging purposes mostly */
     315        1289 :     if (!CPLTestBool(CPLGetConfigOption("OGR_SQLITE_REGEXP", "YES")))
     316           0 :         return nullptr;
     317             : 
     318             :     /* Check if we really need to define our own REGEXP function */
     319             :     int rc =
     320        1289 :         sqlite3_exec(hDB, "SELECT 'a' REGEXP 'a'", nullptr, nullptr, nullptr);
     321        1289 :     if (rc == SQLITE_OK)
     322             :     {
     323           0 :         CPLDebug("SQLITE", "REGEXP already available");
     324           0 :         return nullptr;
     325             :     }
     326             : 
     327             :     cache_entry *cache =
     328        1289 :         static_cast<cache_entry *>(CPLCalloc(CACHE_SIZE, sizeof(cache_entry)));
     329        1289 :     sqlite3_create_function(hDB, "REGEXP", 2, SQLITE_UTF8, cache,
     330             :                             OGRSQLiteREGEXPFunction, nullptr, nullptr);
     331             : 
     332             :     /* To clear the error flag */
     333        1289 :     sqlite3_exec(hDB, "SELECT 1", nullptr, nullptr, nullptr);
     334             : 
     335        1289 :     return cache;
     336             : #else   // HAVE_PCRE
     337        2070 :     return nullptr;
     338             : #endif  // HAVE_PCRE
     339             : }
     340             : 
     341             : /************************************************************************/
     342             : /*                         OGRSQLiteFreeRegExpCache()                   */
     343             : /************************************************************************/
     344             : 
     345        3358 : static void OGRSQLiteFreeRegExpCache(void *
     346             : #if defined(HAVE_PCRE) || defined(HAVE_PCRE2)
     347             :                                          hRegExpCache
     348             : #endif
     349             : )
     350             : {
     351             : #ifdef HAVE_PCRE2
     352        1288 :     if (hRegExpCache == nullptr)
     353           0 :         return;
     354             : 
     355        1288 :     cache_entry *cache = static_cast<cache_entry *>(hRegExpCache);
     356        1304 :     for (int i = 0; i < CACHE_SIZE && cache[i].s; i++)
     357             :     {
     358          16 :         CPLFree(cache[i].s);
     359          16 :         CPLAssert(cache[i].p);
     360          16 :         pcre2_code_free(cache[i].p);
     361             :     }
     362        1288 :     CPLFree(cache);
     363             : #elif defined(HAVE_PCRE)
     364             :     if (hRegExpCache == nullptr)
     365             :         return;
     366             : 
     367             :     cache_entry *cache = static_cast<cache_entry *>(hRegExpCache);
     368             :     for (int i = 0; i < CACHE_SIZE && cache[i].s; i++)
     369             :     {
     370             :         CPLFree(cache[i].s);
     371             :         CPLAssert(cache[i].p);
     372             :         pcre_free(cache[i].p);
     373             :         pcre_free(cache[i].e);
     374             :     }
     375             :     CPLFree(cache);
     376             : #endif  // HAVE_PCRE
     377        2070 : }

Generated by: LCOV version 1.14