Age Owner Branch data TLA Line data Source code
1 : : /*
2 : : * contrib/pg_trgm/trgm_gin.c
3 : : */
4 : : #include "postgres.h"
5 : :
6 : : #include "access/gin.h"
7 : : #include "access/stratnum.h"
8 : : #include "fmgr.h"
9 : : #include "trgm.h"
10 : : #include "varatt.h"
11 : :
6941 teodor@sigaev.ru 12 :UBC 0 : PG_FUNCTION_INFO_V1(gin_extract_trgm);
5522 tgl@sss.pgh.pa.us 13 :CBC 4 : PG_FUNCTION_INFO_V1(gin_extract_value_trgm);
14 : 4 : PG_FUNCTION_INFO_V1(gin_extract_query_trgm);
6941 teodor@sigaev.ru 15 : 4 : PG_FUNCTION_INFO_V1(gin_trgm_consistent);
3891 16 : 4 : PG_FUNCTION_INFO_V1(gin_trgm_triconsistent);
17 : :
18 : : /*
19 : : * This function can only be called if a pre-9.1 version of the GIN operator
20 : : * class definition is present in the catalogs (probably as a consequence
21 : : * of upgrade-in-place). Cope.
22 : : */
23 : : Datum
6941 teodor@sigaev.ru 24 :UBC 0 : gin_extract_trgm(PG_FUNCTION_ARGS)
25 : : {
5505 tgl@sss.pgh.pa.us 26 [ # # ]: 0 : if (PG_NARGS() == 3)
27 : 0 : return gin_extract_value_trgm(fcinfo);
28 [ # # ]: 0 : if (PG_NARGS() == 7)
29 : 0 : return gin_extract_query_trgm(fcinfo);
30 [ # # ]: 0 : elog(ERROR, "unexpected number of arguments to gin_extract_trgm");
31 : : PG_RETURN_NULL();
32 : : }
33 : :
34 : : Datum
5522 tgl@sss.pgh.pa.us 35 :CBC 2404 : gin_extract_value_trgm(PG_FUNCTION_ARGS)
36 : : {
3290 noah@leadboat.com 37 : 2404 : text *val = (text *) PG_GETARG_TEXT_PP(0);
6695 bruce@momjian.us 38 : 2404 : int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
39 : 2404 : Datum *entries = NULL;
40 : : TRGM *trg;
41 : : int32 trglen;
42 : :
6941 teodor@sigaev.ru 43 : 2404 : *nentries = 0;
44 : :
3290 noah@leadboat.com 45 [ - + - - : 2404 : trg = generate_trgm(VARDATA_ANY(val), VARSIZE_ANY_EXHDR(val));
- - - - +
- + - ]
6941 teodor@sigaev.ru 46 : 2404 : trglen = ARRNELEM(trg);
47 : :
48 [ + - ]: 2404 : if (trglen > 0)
49 : : {
50 : : trgm *ptr;
51 : : int32 i;
52 : :
5544 tgl@sss.pgh.pa.us 53 : 2404 : *nentries = trglen;
100 michael@paquier.xyz 54 :GNC 2404 : entries = palloc_array(Datum, trglen);
55 : :
6941 teodor@sigaev.ru 56 :CBC 2404 : ptr = GETARR(trg);
5544 tgl@sss.pgh.pa.us 57 [ + + ]: 35631 : for (i = 0; i < trglen; i++)
58 : : {
5453 bruce@momjian.us 59 : 33227 : int32 item = trgm2int(ptr);
60 : :
5544 tgl@sss.pgh.pa.us 61 : 33227 : entries[i] = Int32GetDatum(item);
6941 teodor@sigaev.ru 62 : 33227 : ptr++;
63 : : }
64 : : }
65 : :
66 : 2404 : PG_RETURN_POINTER(entries);
67 : : }
68 : :
69 : : Datum
5522 tgl@sss.pgh.pa.us 70 : 188 : gin_extract_query_trgm(PG_FUNCTION_ARGS)
71 : : {
3290 noah@leadboat.com 72 : 188 : text *val = (text *) PG_GETARG_TEXT_PP(0);
5522 tgl@sss.pgh.pa.us 73 : 188 : int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
74 : 188 : StrategyNumber strategy = PG_GETARG_UINT16(2);
75 : : #ifdef NOT_USED
76 : : bool **pmatch = (bool **) PG_GETARG_POINTER(3);
77 : : #endif
4723 78 : 188 : Pointer **extra_data = (Pointer **) PG_GETARG_POINTER(4);
79 : : #ifdef NOT_USED
80 : : bool **nullFlags = (bool **) PG_GETARG_POINTER(5);
81 : : #endif
5453 bruce@momjian.us 82 : 188 : int32 *searchMode = (int32 *) PG_GETARG_POINTER(6);
5522 tgl@sss.pgh.pa.us 83 : 188 : Datum *entries = NULL;
84 : : TRGM *trg;
85 : : int32 trglen;
86 : : trgm *ptr;
87 : : TrgmPackedGraph *graph;
88 : : int32 i;
89 : :
90 [ + + + - ]: 188 : switch (strategy)
91 : : {
92 : 96 : case SimilarityStrategyNumber:
93 : : case WordSimilarityStrategyNumber:
94 : : case StrictWordSimilarityStrategyNumber:
95 : : case EqualStrategyNumber:
3290 noah@leadboat.com 96 [ - + - - : 96 : trg = generate_trgm(VARDATA_ANY(val), VARSIZE_ANY_EXHDR(val));
- - - - -
+ - + ]
5522 tgl@sss.pgh.pa.us 97 : 96 : break;
98 : 48 : case ILikeStrategyNumber:
99 : : #ifndef IGNORECASE
100 : : elog(ERROR, "cannot handle ~~* with case-sensitive trigrams");
101 : : #endif
102 : : pg_fallthrough;
103 : : case LikeStrategyNumber:
104 : :
105 : : /*
106 : : * For wildcard search we extract all the trigrams that every
107 : : * potentially-matching string must include.
108 : : */
3290 noah@leadboat.com 109 [ - + ]: 48 : trg = generate_wildcard_trgm(VARDATA_ANY(val),
110 [ - + - - : 48 : VARSIZE_ANY_EXHDR(val));
- - - - -
+ ]
5522 tgl@sss.pgh.pa.us 111 : 48 : break;
4723 112 : 44 : case RegExpICaseStrategyNumber:
113 : : #ifndef IGNORECASE
114 : : elog(ERROR, "cannot handle ~* with case-sensitive trigrams");
115 : : #endif
116 : : pg_fallthrough;
117 : : case RegExpStrategyNumber:
4722 118 : 44 : trg = createTrgmNFA(val, PG_GET_COLLATION(),
119 : : &graph, CurrentMemoryContext);
4723 120 [ + + + + ]: 44 : if (trg && ARRNELEM(trg) > 0)
121 : : {
122 : : /*
123 : : * Successful regex processing: store NFA-like graph as
124 : : * extra_data. GIN API requires an array of nentries
125 : : * Pointers, but we just put the same value in each element.
126 : : */
127 : 34 : trglen = ARRNELEM(trg);
100 michael@paquier.xyz 128 :GNC 34 : *extra_data = palloc_array(Pointer, trglen);
4723 tgl@sss.pgh.pa.us 129 [ + + ]:CBC 848 : for (i = 0; i < trglen; i++)
130 : 814 : (*extra_data)[i] = (Pointer) graph;
131 : : }
132 : : else
133 : : {
134 : : /* No result: have to do full index scan. */
135 : 10 : *nentries = 0;
136 : 10 : *searchMode = GIN_SEARCH_MODE_ALL;
137 : 10 : PG_RETURN_POINTER(entries);
138 : : }
139 : 34 : break;
5522 tgl@sss.pgh.pa.us 140 :UBC 0 : default:
141 [ # # ]: 0 : elog(ERROR, "unrecognized strategy number: %d", strategy);
142 : : trg = NULL; /* keep compiler quiet */
143 : : break;
144 : : }
145 : :
5522 tgl@sss.pgh.pa.us 146 :CBC 178 : trglen = ARRNELEM(trg);
147 : 178 : *nentries = trglen;
148 : :
149 [ + + ]: 178 : if (trglen > 0)
150 : : {
100 michael@paquier.xyz 151 :GNC 146 : entries = palloc_array(Datum, trglen);
5522 tgl@sss.pgh.pa.us 152 :CBC 146 : ptr = GETARR(trg);
153 [ + + ]: 1748 : for (i = 0; i < trglen; i++)
154 : : {
5453 bruce@momjian.us 155 : 1602 : int32 item = trgm2int(ptr);
156 : :
5522 tgl@sss.pgh.pa.us 157 : 1602 : entries[i] = Int32GetDatum(item);
158 : 1602 : ptr++;
159 : : }
160 : : }
161 : :
162 : : /*
163 : : * If no trigram was extracted then we have to scan all the index.
164 : : */
165 [ + + ]: 178 : if (trglen == 0)
166 : 32 : *searchMode = GIN_SEARCH_MODE_ALL;
167 : :
168 : 178 : PG_RETURN_POINTER(entries);
169 : : }
170 : :
171 : : Datum
6941 teodor@sigaev.ru 172 : 12 : gin_trgm_consistent(PG_FUNCTION_ARGS)
173 : : {
6695 bruce@momjian.us 174 : 12 : bool *check = (bool *) PG_GETARG_POINTER(0);
5522 tgl@sss.pgh.pa.us 175 : 12 : StrategyNumber strategy = PG_GETARG_UINT16(1);
176 : : #ifdef NOT_USED
177 : : text *query = PG_GETARG_TEXT_PP(2);
178 : : #endif
5544 179 : 12 : int32 nkeys = PG_GETARG_INT32(3);
4723 180 : 12 : Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4);
6199 181 : 12 : bool *recheck = (bool *) PG_GETARG_POINTER(5);
182 : : bool res;
183 : : int32 i,
184 : : ntrue;
185 : : double nlimit;
186 : :
187 : : /* All cases served by this function are inexact */
6544 188 : 12 : *recheck = true;
189 : :
5522 190 [ + + - - ]: 12 : switch (strategy)
191 : : {
192 : 4 : case SimilarityStrategyNumber:
193 : : case WordSimilarityStrategyNumber:
194 : : case StrictWordSimilarityStrategyNumber:
2916 teodor@sigaev.ru 195 : 4 : nlimit = index_strategy_get_limit(strategy);
196 : :
197 : : /* Count the matches */
5522 tgl@sss.pgh.pa.us 198 : 4 : ntrue = 0;
199 [ + + ]: 18 : for (i = 0; i < nkeys; i++)
200 : : {
201 [ + + ]: 14 : if (check[i])
202 : 12 : ntrue++;
203 : : }
204 : :
205 : : /*--------------------
206 : : * If DIVUNION is defined then similarity formula is:
207 : : * c / (len1 + len2 - c)
208 : : * where c is number of common trigrams and it stands as ntrue in
209 : : * this code. Here we don't know value of len2 but we can assume
210 : : * that c (ntrue) is a lower bound of len2, so upper bound of
211 : : * similarity is:
212 : : * c / (len1 + c - c) => c / len1
213 : : * If DIVUNION is not defined then similarity formula is:
214 : : * c / max(len1, len2)
215 : : * And again, c (ntrue) is a lower bound of len2, but c <= len1
216 : : * just by definition and, consequently, upper bound of
217 : : * similarity is just c / len1.
218 : : * So, independently on DIVUNION the upper bound formula is the same.
219 : : */
3651 teodor@sigaev.ru 220 [ + + ]: 6 : res = (nkeys == 0) ? false :
221 [ + - ]: 2 : (((((float4) ntrue) / ((float4) nkeys))) >= nlimit);
5522 tgl@sss.pgh.pa.us 222 : 4 : break;
223 : 8 : case ILikeStrategyNumber:
224 : : #ifndef IGNORECASE
225 : : elog(ERROR, "cannot handle ~~* with case-sensitive trigrams");
226 : : #endif
227 : : pg_fallthrough;
228 : : case LikeStrategyNumber:
229 : : case EqualStrategyNumber:
230 : : /* Check if all extracted trigrams are presented. */
231 : 8 : res = true;
232 [ + + ]: 16 : for (i = 0; i < nkeys; i++)
233 : : {
234 [ - + ]: 8 : if (!check[i])
235 : : {
5522 tgl@sss.pgh.pa.us 236 :UBC 0 : res = false;
237 : 0 : break;
238 : : }
239 : : }
5522 tgl@sss.pgh.pa.us 240 :CBC 8 : break;
4723 tgl@sss.pgh.pa.us 241 :UBC 0 : case RegExpICaseStrategyNumber:
242 : : #ifndef IGNORECASE
243 : : elog(ERROR, "cannot handle ~* with case-sensitive trigrams");
244 : : #endif
245 : : pg_fallthrough;
246 : : case RegExpStrategyNumber:
247 [ # # ]: 0 : if (nkeys < 1)
248 : : {
249 : : /* Regex processing gave no result: do full index scan */
250 : 0 : res = true;
251 : : }
252 : : else
101 peter@eisentraut.org 253 :UNC 0 : res = trigramsMatchGraph(extra_data[0], check);
4723 tgl@sss.pgh.pa.us 254 :UBC 0 : break;
5522 255 : 0 : default:
256 [ # # ]: 0 : elog(ERROR, "unrecognized strategy number: %d", strategy);
257 : : res = false; /* keep compiler quiet */
258 : : break;
259 : : }
260 : :
6941 teodor@sigaev.ru 261 :CBC 12 : PG_RETURN_BOOL(res);
262 : : }
263 : :
264 : : /*
265 : : * In all cases, GIN_TRUE is at least as favorable to inclusion as
266 : : * GIN_MAYBE. If no better option is available, simply treat
267 : : * GIN_MAYBE as if it were GIN_TRUE and apply the same test as the binary
268 : : * consistent function.
269 : : */
270 : : Datum
3891 271 : 16357 : gin_trgm_triconsistent(PG_FUNCTION_ARGS)
272 : : {
3566 rhaas@postgresql.org 273 : 16357 : GinTernaryValue *check = (GinTernaryValue *) PG_GETARG_POINTER(0);
3891 teodor@sigaev.ru 274 : 16357 : StrategyNumber strategy = PG_GETARG_UINT16(1);
275 : : #ifdef NOT_USED
276 : : text *query = PG_GETARG_TEXT_PP(2);
277 : : #endif
278 : 16357 : int32 nkeys = PG_GETARG_INT32(3);
279 : 16357 : Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4);
3566 rhaas@postgresql.org 280 : 16357 : GinTernaryValue res = GIN_MAYBE;
281 : : int32 i,
282 : : ntrue;
283 : : bool *boolcheck;
284 : : double nlimit;
285 : :
3891 teodor@sigaev.ru 286 [ + + + - ]: 16357 : switch (strategy)
287 : : {
288 : 10197 : case SimilarityStrategyNumber:
289 : : case WordSimilarityStrategyNumber:
290 : : case StrictWordSimilarityStrategyNumber:
2916 291 : 10197 : nlimit = index_strategy_get_limit(strategy);
292 : :
293 : : /* Count the matches */
3891 294 : 10197 : ntrue = 0;
295 [ + + ]: 95957 : for (i = 0; i < nkeys; i++)
296 : : {
297 [ + + ]: 85760 : if (check[i] != GIN_FALSE)
298 : 39202 : ntrue++;
299 : : }
300 : :
301 : : /*
302 : : * See comment in gin_trgm_consistent() about * upper bound
303 : : * formula
304 : : */
3651 305 [ + + ]: 19394 : res = (nkeys == 0)
306 [ + + ]: 9197 : ? GIN_FALSE : (((((float4) ntrue) / ((float4) nkeys)) >= nlimit)
307 : : ? GIN_MAYBE : GIN_FALSE);
3891 308 : 10197 : break;
309 : 4039 : case ILikeStrategyNumber:
310 : : #ifndef IGNORECASE
311 : : elog(ERROR, "cannot handle ~~* with case-sensitive trigrams");
312 : : #endif
313 : : pg_fallthrough;
314 : : case LikeStrategyNumber:
315 : : case EqualStrategyNumber:
316 : : /* Check if all extracted trigrams are presented. */
317 : 4039 : res = GIN_MAYBE;
318 [ + + ]: 8175 : for (i = 0; i < nkeys; i++)
319 : : {
320 [ + + ]: 4156 : if (check[i] == GIN_FALSE)
321 : : {
322 : 20 : res = GIN_FALSE;
323 : 20 : break;
324 : : }
325 : : }
326 : 4039 : break;
327 : 2121 : case RegExpICaseStrategyNumber:
328 : : #ifndef IGNORECASE
329 : : elog(ERROR, "cannot handle ~* with case-sensitive trigrams");
330 : : #endif
331 : : pg_fallthrough;
332 : : case RegExpStrategyNumber:
333 [ + + ]: 2121 : if (nkeys < 1)
334 : : {
335 : : /* Regex processing gave no result: do full index scan */
336 : 736 : res = GIN_MAYBE;
337 : : }
338 : : else
339 : : {
340 : : /*
341 : : * As trigramsMatchGraph implements a monotonic boolean
342 : : * function, promoting all GIN_MAYBE keys to GIN_TRUE will
343 : : * give a conservative result.
344 : : */
100 michael@paquier.xyz 345 :GNC 1385 : boolcheck = palloc_array(bool, nkeys);
3891 teodor@sigaev.ru 346 [ + + ]:CBC 319289 : for (i = 0; i < nkeys; i++)
347 : 317904 : boolcheck[i] = (check[i] != GIN_FALSE);
101 peter@eisentraut.org 348 [ + + ]:GNC 1385 : if (!trigramsMatchGraph(extra_data[0], boolcheck))
3891 teodor@sigaev.ru 349 :CBC 6 : res = GIN_FALSE;
350 : 1385 : pfree(boolcheck);
351 : : }
352 : 2121 : break;
3891 teodor@sigaev.ru 353 :UBC 0 : default:
354 [ # # ]: 0 : elog(ERROR, "unrecognized strategy number: %d", strategy);
355 : : res = GIN_FALSE; /* keep compiler quiet */
356 : : break;
357 : : }
358 : :
359 : : /* All cases served by this function are inexact */
3891 teodor@sigaev.ru 360 [ - + ]:CBC 16357 : Assert(res != GIN_TRUE);
361 : 16357 : PG_RETURN_GIN_TERNARY_VALUE(res);
362 : : }
|