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