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: 2024-11-25 13:07:18 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 = (const char *)sqlite3_value_text(argv[0]);
     213             :     if (!re)
     214             :     {
     215             :         sqlite3_result_error(ctx, "no regexp", -1);
     216             :         return;
     217             :     }
     218             : 
     219             :     if (sqlite3_value_type(argv[1]) == SQLITE_NULL)
     220             :     {
     221             :         sqlite3_result_int(ctx, 0);
     222             :         return;
     223             :     }
     224             : 
     225             :     const char *str = (const char *)sqlite3_value_text(argv[1]);
     226             :     if (!str)
     227             :     {
     228             :         sqlite3_result_error(ctx, "no string", -1);
     229             :         return;
     230             :     }
     231             : 
     232             :     /* simple LRU cache */
     233             :     cache_entry *cache = (cache_entry *)sqlite3_user_data(ctx);
     234             :     CPLAssert(cache);
     235             : 
     236             :     bool found = false;
     237             :     int i = 0;  // Used after for.
     238             :     for (; i < CACHE_SIZE && cache[i].s; i++)
     239             :     {
     240             :         if (strcmp(re, cache[i].s) == 0)
     241             :         {
     242             :             found = true;
     243             :             break;
     244             :         }
     245             :     }
     246             : 
     247             :     if (found)
     248             :     {
     249             :         if (i > 0)
     250             :         {
     251             :             cache_entry c = cache[i];
     252             :             memmove(cache + 1, cache, i * sizeof(cache_entry));
     253             :             cache[0] = c;
     254             :         }
     255             :     }
     256             :     else
     257             :     {
     258             :         cache_entry c;
     259             :         const char *err = nullptr;
     260             :         int pos = 0;
     261             :         c.p = pcre_compile(re, 0, &err, &pos, nullptr);
     262             :         if (!c.p)
     263             :         {
     264             :             char *e2 = sqlite3_mprintf("%s: %s (offset %d)", re, err, pos);
     265             :             sqlite3_result_error(ctx, e2, -1);
     266             :             sqlite3_free(e2);
     267             :             return;
     268             :         }
     269             :         c.e = pcre_study(c.p, 0, &err);
     270             :         c.s = VSIStrdup(re);
     271             :         if (!c.s)
     272             :         {
     273             :             sqlite3_result_error(ctx, "strdup: ENOMEM", -1);
     274             :             pcre_free(c.p);
     275             :             pcre_free(c.e);
     276             :             return;
     277             :         }
     278             :         i = CACHE_SIZE - 1;
     279             :         if (cache[i].s)
     280             :         {
     281             :             CPLFree(cache[i].s);
     282             :             CPLAssert(cache[i].p);
     283             :             pcre_free(cache[i].p);
     284             :             pcre_free(cache[i].e);
     285             :         }
     286             :         memmove(cache + 1, cache, i * sizeof(cache_entry));
     287             :         cache[0] = c;
     288             :     }
     289             :     pcre *p = cache[0].p;
     290             :     CPLAssert(p);
     291             :     pcre_extra *e = cache[0].e;
     292             : 
     293             :     int rc =
     294             :         pcre_exec(p, e, str, static_cast<int>(strlen(str)), 0, 0, nullptr, 0);
     295             :     sqlite3_result_int(ctx, rc >= 0);
     296             : }
     297             : 
     298             : #endif  // HAVE_PCRE
     299             : 
     300             : /************************************************************************/
     301             : /*                        OGRSQLiteRegisterRegExpFunction()             */
     302             : /************************************************************************/
     303             : 
     304        3140 : static void *OGRSQLiteRegisterRegExpFunction(sqlite3 *
     305             : #if defined(HAVE_PCRE) || defined(HAVE_PCRE2)
     306             :                                                  hDB
     307             : #endif
     308             : )
     309             : {
     310             : #if defined(HAVE_PCRE) || defined(HAVE_PCRE2)
     311             : 
     312             :     /* For debugging purposes mostly */
     313        1317 :     if (!CPLTestBool(CPLGetConfigOption("OGR_SQLITE_REGEXP", "YES")))
     314           0 :         return nullptr;
     315             : 
     316             :     /* Check if we really need to define our own REGEXP function */
     317             :     int rc =
     318        1317 :         sqlite3_exec(hDB, "SELECT 'a' REGEXP 'a'", nullptr, nullptr, nullptr);
     319        1317 :     if (rc == SQLITE_OK)
     320             :     {
     321           0 :         CPLDebug("SQLITE", "REGEXP already available");
     322           0 :         return nullptr;
     323             :     }
     324             : 
     325             :     cache_entry *cache =
     326        1317 :         static_cast<cache_entry *>(CPLCalloc(CACHE_SIZE, sizeof(cache_entry)));
     327        1317 :     sqlite3_create_function(hDB, "REGEXP", 2, SQLITE_UTF8, cache,
     328             :                             OGRSQLiteREGEXPFunction, nullptr, nullptr);
     329             : 
     330             :     /* To clear the error flag */
     331        1317 :     sqlite3_exec(hDB, "SELECT 1", nullptr, nullptr, nullptr);
     332             : 
     333        1317 :     return cache;
     334             : #else   // HAVE_PCRE
     335        1823 :     return nullptr;
     336             : #endif  // HAVE_PCRE
     337             : }
     338             : 
     339             : /************************************************************************/
     340             : /*                         OGRSQLiteFreeRegExpCache()                   */
     341             : /************************************************************************/
     342             : 
     343        3131 : static void OGRSQLiteFreeRegExpCache(void *
     344             : #if defined(HAVE_PCRE) || defined(HAVE_PCRE2)
     345             :                                          hRegExpCache
     346             : #endif
     347             : )
     348             : {
     349             : #ifdef HAVE_PCRE2
     350        1308 :     if (hRegExpCache == nullptr)
     351           0 :         return;
     352             : 
     353        1308 :     cache_entry *cache = static_cast<cache_entry *>(hRegExpCache);
     354        1324 :     for (int i = 0; i < CACHE_SIZE && cache[i].s; i++)
     355             :     {
     356          16 :         CPLFree(cache[i].s);
     357          16 :         CPLAssert(cache[i].p);
     358          16 :         pcre2_code_free(cache[i].p);
     359             :     }
     360        1308 :     CPLFree(cache);
     361             : #elif defined(HAVE_PCRE)
     362             :     if (hRegExpCache == nullptr)
     363             :         return;
     364             : 
     365             :     cache_entry *cache = static_cast<cache_entry *>(hRegExpCache);
     366             :     for (int i = 0; i < CACHE_SIZE && cache[i].s; i++)
     367             :     {
     368             :         CPLFree(cache[i].s);
     369             :         CPLAssert(cache[i].p);
     370             :         pcre_free(cache[i].p);
     371             :         pcre_free(cache[i].e);
     372             :     }
     373             :     CPLFree(cache);
     374             : #endif  // HAVE_PCRE
     375        1823 : }

Generated by: LCOV version 1.14