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 : }
|