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