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