Age Owner Branch data TLA Line data Source code
1 : : /*
2 : : * contrib/pg_trgm/trgm_op.c
3 : : */
4 : : #include "postgres.h"
5 : :
6 : : #include <ctype.h>
7 : :
8 : : #include "catalog/pg_type.h"
9 : : #include "common/int.h"
10 : : #include "lib/qunique.h"
11 : : #include "miscadmin.h"
12 : : #include "trgm.h"
13 : : #include "tsearch/ts_locale.h"
14 : : #include "utils/guc.h"
15 : : #include "utils/lsyscache.h"
16 : : #include "utils/memutils.h"
17 : : #include "utils/pg_crc.h"
18 : :
6529 tgl@sss.pgh.pa.us 19 :CBC 3 : PG_MODULE_MAGIC;
20 : :
21 : : /* GUC variables */
22 : : double similarity_threshold = 0.3f;
23 : : double word_similarity_threshold = 0.6f;
24 : : double strict_word_similarity_threshold = 0.5f;
25 : :
7258 teodor@sigaev.ru 26 : 2 : PG_FUNCTION_INFO_V1(set_limit);
4822 tgl@sss.pgh.pa.us 27 : 2 : PG_FUNCTION_INFO_V1(show_limit);
28 : 2 : PG_FUNCTION_INFO_V1(show_trgm);
29 : 2 : PG_FUNCTION_INFO_V1(similarity);
2951 teodor@sigaev.ru 30 : 2 : PG_FUNCTION_INFO_V1(word_similarity);
2216 31 : 2 : PG_FUNCTION_INFO_V1(strict_word_similarity);
4822 tgl@sss.pgh.pa.us 32 : 2 : PG_FUNCTION_INFO_V1(similarity_dist);
33 : 2 : PG_FUNCTION_INFO_V1(similarity_op);
2951 teodor@sigaev.ru 34 : 2 : PG_FUNCTION_INFO_V1(word_similarity_op);
35 : 2 : PG_FUNCTION_INFO_V1(word_similarity_commutator_op);
36 : 1 : PG_FUNCTION_INFO_V1(word_similarity_dist_op);
37 : 2 : PG_FUNCTION_INFO_V1(word_similarity_dist_commutator_op);
2216 38 : 2 : PG_FUNCTION_INFO_V1(strict_word_similarity_op);
39 : 2 : PG_FUNCTION_INFO_V1(strict_word_similarity_commutator_op);
40 : 1 : PG_FUNCTION_INFO_V1(strict_word_similarity_dist_op);
41 : 2 : PG_FUNCTION_INFO_V1(strict_word_similarity_dist_commutator_op);
42 : :
43 : : /* Trigram with position */
44 : : typedef struct
45 : : {
46 : : trgm trg;
47 : : int index;
48 : : } pos_trgm;
49 : :
50 : : /* Trigram bound type */
51 : : typedef uint8 TrgmBound;
52 : : #define TRGM_BOUND_LEFT 0x01 /* trigram is left bound of word */
53 : : #define TRGM_BOUND_RIGHT 0x02 /* trigram is right bound of word */
54 : :
55 : : /* Word similarity flags */
56 : : #define WORD_SIMILARITY_CHECK_ONLY 0x01 /* only check existence of similar
57 : : * search pattern in text */
58 : : #define WORD_SIMILARITY_STRICT 0x02 /* force bounds of extent to match
59 : : * word bounds */
60 : :
61 : : /*
62 : : * Module load callback
63 : : */
64 : : void
2951 65 : 3 : _PG_init(void)
66 : : {
67 : : /* Define custom GUC variables. */
68 : 3 : DefineCustomRealVariable("pg_trgm.similarity_threshold",
69 : : "Sets the threshold used by the % operator.",
70 : : "Valid range is 0.0 .. 1.0.",
71 : : &similarity_threshold,
72 : : 0.3f,
73 : : 0.0,
74 : : 1.0,
75 : : PGC_USERSET,
76 : : 0,
77 : : NULL,
78 : : NULL,
79 : : NULL);
80 : 3 : DefineCustomRealVariable("pg_trgm.word_similarity_threshold",
81 : : "Sets the threshold used by the <% operator.",
82 : : "Valid range is 0.0 .. 1.0.",
83 : : &word_similarity_threshold,
84 : : 0.6f,
85 : : 0.0,
86 : : 1.0,
87 : : PGC_USERSET,
88 : : 0,
89 : : NULL,
90 : : NULL,
91 : : NULL);
2216 92 : 3 : DefineCustomRealVariable("pg_trgm.strict_word_similarity_threshold",
93 : : "Sets the threshold used by the <<% operator.",
94 : : "Valid range is 0.0 .. 1.0.",
95 : : &strict_word_similarity_threshold,
96 : : 0.5f,
97 : : 0.0,
98 : : 1.0,
99 : : PGC_USERSET,
100 : : 0,
101 : : NULL,
102 : : NULL,
103 : : NULL);
104 : :
783 tgl@sss.pgh.pa.us 105 : 3 : MarkGUCPrefixReserved("pg_trgm");
2951 teodor@sigaev.ru 106 : 3 : }
107 : :
108 : : /*
109 : : * Deprecated function.
110 : : * Use "pg_trgm.similarity_threshold" GUC variable instead of this function.
111 : : */
112 : : Datum
7168 bruce@momjian.us 113 : 2 : set_limit(PG_FUNCTION_ARGS)
114 : : {
115 : 2 : float4 nlimit = PG_GETARG_FLOAT4(0);
116 : : char *nlimit_str;
117 : : Oid func_out_oid;
118 : : bool is_varlena;
119 : :
2949 teodor@sigaev.ru 120 : 2 : getTypeOutputInfo(FLOAT4OID, &func_out_oid, &is_varlena);
121 : :
122 : 2 : nlimit_str = OidOutputFunctionCall(func_out_oid, Float4GetDatum(nlimit));
123 : :
124 : 2 : SetConfigOption("pg_trgm.similarity_threshold", nlimit_str,
125 : : PGC_USERSET, PGC_S_SESSION);
126 : :
2951 127 : 2 : PG_RETURN_FLOAT4(similarity_threshold);
128 : : }
129 : :
130 : :
131 : : /*
132 : : * Get similarity threshold for given index scan strategy number.
133 : : */
134 : : double
2216 135 : 43500 : index_strategy_get_limit(StrategyNumber strategy)
136 : : {
137 [ + + + - ]: 43500 : switch (strategy)
138 : : {
139 : 32704 : case SimilarityStrategyNumber:
140 : 32704 : return similarity_threshold;
141 : 4818 : case WordSimilarityStrategyNumber:
142 : 4818 : return word_similarity_threshold;
143 : 5978 : case StrictWordSimilarityStrategyNumber:
144 : 5978 : return strict_word_similarity_threshold;
2216 teodor@sigaev.ru 145 :UBC 0 : default:
146 [ # # ]: 0 : elog(ERROR, "unrecognized strategy number: %d", strategy);
147 : : break;
148 : : }
149 : :
150 : : return 0.0; /* keep compiler quiet */
151 : : }
152 : :
153 : : /*
154 : : * Deprecated function.
155 : : * Use "pg_trgm.similarity_threshold" GUC variable instead of this function.
156 : : */
157 : : Datum
7168 bruce@momjian.us 158 :CBC 20000 : show_limit(PG_FUNCTION_ARGS)
159 : : {
2951 teodor@sigaev.ru 160 : 20000 : PG_RETURN_FLOAT4(similarity_threshold);
161 : : }
162 : :
163 : : static int
7168 bruce@momjian.us 164 : 3132620 : comp_trgm(const void *a, const void *b)
165 : : {
166 [ + + + + : 3132620 : return CMPTRGM(a, b);
+ + + + +
+ + + ]
167 : : }
168 : :
169 : : /*
170 : : * Finds first word in string, returns pointer to the word,
171 : : * endword points to the character after word
172 : : */
173 : : static char *
5421 174 : 239411 : find_word(char *str, int lenstr, char **endword, int *charlen)
175 : : {
176 : 239411 : char *beginword = str;
177 : :
4023 tgl@sss.pgh.pa.us 178 [ + + + + ]: 253075 : while (beginword - str < lenstr && !ISWORDCHR(beginword))
5632 teodor@sigaev.ru 179 : 13664 : beginword += pg_mblen(beginword);
180 : :
181 [ + + ]: 239411 : if (beginword - str >= lenstr)
182 : 113080 : return NULL;
183 : :
184 : 126331 : *endword = beginword;
185 : 126331 : *charlen = 0;
4023 tgl@sss.pgh.pa.us 186 [ + + + + ]: 1087678 : while (*endword - str < lenstr && ISWORDCHR(*endword))
187 : : {
5632 teodor@sigaev.ru 188 : 961347 : *endword += pg_mblen(*endword);
189 : 961347 : (*charlen)++;
190 : : }
191 : :
192 : 126331 : return beginword;
193 : : }
194 : :
195 : : /*
196 : : * Reduce a trigram (three possibly multi-byte characters) to a trgm,
197 : : * which is always exactly three bytes. If we have three single-byte
198 : : * characters, we just use them as-is; otherwise we form a hash value.
199 : : */
200 : : void
4025 tgl@sss.pgh.pa.us 201 : 1459 : compact_trigram(trgm *tptr, char *str, int bytelen)
202 : : {
5421 bruce@momjian.us 203 [ + - ]: 1459 : if (bytelen == 3)
204 : : {
205 : 1459 : CPTRGM(tptr, str);
206 : : }
207 : : else
208 : : {
209 : : pg_crc32 crc;
210 : :
3449 heikki.linnakangas@i 211 :UBC 0 : INIT_LEGACY_CRC32(crc);
212 [ # # ]: 0 : COMP_LEGACY_CRC32(crc, str, bytelen);
213 : 0 : FIN_LEGACY_CRC32(crc);
214 : :
215 : : /*
216 : : * use only 3 upper bytes from crc, hope, it's good enough hashing
217 : : */
5632 teodor@sigaev.ru 218 : 0 : CPTRGM(tptr, &crc);
219 : : }
5632 teodor@sigaev.ru 220 :CBC 1459 : }
221 : :
222 : : /*
223 : : * Adds trigrams from words (already padded).
224 : : */
225 : : static trgm *
5421 bruce@momjian.us 226 : 126395 : make_trigrams(trgm *tptr, char *str, int bytelen, int charlen)
227 : : {
228 : 126395 : char *ptr = str;
229 : :
230 [ + + ]: 126395 : if (charlen < 3)
5632 teodor@sigaev.ru 231 : 27 : return tptr;
232 : :
4025 tgl@sss.pgh.pa.us 233 [ - + ]: 126368 : if (bytelen > charlen)
234 : : {
235 : : /* Find multibyte character boundaries and apply compact_trigram */
5421 bruce@momjian.us 236 :UBC 0 : int lenfirst = pg_mblen(str),
237 : 0 : lenmiddle = pg_mblen(str + lenfirst),
238 : 0 : lenlast = pg_mblen(str + lenfirst + lenmiddle);
239 : :
240 [ # # ]: 0 : while ((ptr - str) + lenfirst + lenmiddle + lenlast <= bytelen)
241 : : {
4025 tgl@sss.pgh.pa.us 242 : 0 : compact_trigram(tptr, ptr, lenfirst + lenmiddle + lenlast);
243 : :
5632 teodor@sigaev.ru 244 : 0 : ptr += lenfirst;
245 : 0 : tptr++;
246 : :
5421 bruce@momjian.us 247 : 0 : lenfirst = lenmiddle;
248 : 0 : lenmiddle = lenlast;
249 : 0 : lenlast = pg_mblen(ptr + lenfirst + lenmiddle);
250 : : }
251 : : }
252 : : else
253 : : {
254 : : /* Fast path when there are no multibyte characters */
5421 bruce@momjian.us 255 [ - + ]:CBC 126368 : Assert(bytelen == charlen);
256 : :
5632 teodor@sigaev.ru 257 [ + + ]: 1214137 : while (ptr - str < bytelen - 2 /* number of trigrams = strlen - 2 */ )
258 : : {
259 : 1087769 : CPTRGM(tptr, ptr);
260 : 1087769 : ptr++;
261 : 1087769 : tptr++;
262 : : }
263 : : }
264 : :
265 : 126368 : return tptr;
266 : : }
267 : :
268 : : /*
269 : : * Make array of trigrams without sorting and removing duplicate items.
270 : : *
271 : : * trg: where to return the array of trigrams.
272 : : * str: source string, of length slen bytes.
273 : : * bounds: where to return bounds of trigrams (if needed).
274 : : *
275 : : * Returns length of the generated array.
276 : : */
277 : : static int
2216 278 : 113081 : generate_trgm_only(trgm *trg, char *str, int slen, TrgmBound *bounds)
279 : : {
280 : : trgm *tptr;
281 : : char *buf;
282 : : int charlen,
283 : : bytelen;
284 : : char *bword,
285 : : *eword;
286 : :
7168 bruce@momjian.us 287 [ + - + + ]: 113081 : if (slen + LPADDING + RPADDING < 3 || slen == 0)
2951 teodor@sigaev.ru 288 : 1 : return 0;
289 : :
290 : 113080 : tptr = trg;
291 : :
292 : : /* Allocate a buffer for case-folded, blank-padded words */
3744 tgl@sss.pgh.pa.us 293 : 113080 : buf = (char *) palloc(slen * pg_database_encoding_max_length() + 4);
294 : :
295 : : if (LPADDING > 0)
296 : : {
7258 teodor@sigaev.ru 297 : 113080 : *buf = ' ';
298 : : if (LPADDING > 1)
7168 bruce@momjian.us 299 : 113080 : *(buf + 1) = ' ';
300 : : }
301 : :
5632 teodor@sigaev.ru 302 : 113080 : eword = str;
5421 bruce@momjian.us 303 [ + + ]: 239411 : while ((bword = find_word(eword, slen - (eword - str), &eword, &charlen)) != NULL)
304 : : {
305 : : #ifdef IGNORECASE
5632 teodor@sigaev.ru 306 : 126331 : bword = lowerstr_with_len(bword, eword - bword);
307 : 126331 : bytelen = strlen(bword);
308 : : #else
309 : : bytelen = eword - bword;
310 : : #endif
311 : :
312 : 126331 : memcpy(buf + LPADDING, bword, bytelen);
313 : :
314 : : #ifdef IGNORECASE
315 : 126331 : pfree(bword);
316 : : #endif
317 : :
5421 bruce@momjian.us 318 : 126331 : buf[LPADDING + bytelen] = ' ';
319 : 126331 : buf[LPADDING + bytelen + 1] = ' ';
320 : :
321 : : /* Calculate trigrams marking their bounds if needed */
2216 teodor@sigaev.ru 322 [ + + ]: 126331 : if (bounds)
323 : 12398 : bounds[tptr - trg] |= TRGM_BOUND_LEFT;
5421 bruce@momjian.us 324 : 126331 : tptr = make_trigrams(tptr, buf, bytelen + LPADDING + RPADDING,
325 : : charlen + LPADDING + RPADDING);
2216 teodor@sigaev.ru 326 [ + + ]: 126331 : if (bounds)
327 : 12398 : bounds[tptr - trg - 1] |= TRGM_BOUND_RIGHT;
328 : : }
329 : :
7258 330 : 113080 : pfree(buf);
331 : :
2951 332 : 113080 : return tptr - trg;
333 : : }
334 : :
335 : : /*
336 : : * Guard against possible overflow in the palloc requests below. (We
337 : : * don't worry about the additive constants, since palloc can detect
338 : : * requests that are a little above MaxAllocSize --- we just need to
339 : : * prevent integer overflow in the multiplications.)
340 : : */
341 : : static void
342 : 101010 : protect_out_of_mem(int slen)
343 : : {
344 [ + - ]: 101010 : if ((Size) (slen / 2) >= (MaxAllocSize / (sizeof(trgm) * 3)) ||
345 [ - + ]: 101010 : (Size) slen >= (MaxAllocSize / pg_database_encoding_max_length()))
2951 teodor@sigaev.ru 346 [ # # ]:UBC 0 : ereport(ERROR,
347 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
348 : : errmsg("out of memory")));
2951 teodor@sigaev.ru 349 :CBC 101010 : }
350 : :
351 : : /*
352 : : * Make array of trigrams with sorting and removing duplicate items.
353 : : *
354 : : * str: source string, of length slen bytes.
355 : : *
356 : : * Returns the sorted array of unique trigrams.
357 : : */
358 : : TRGM *
359 : 88829 : generate_trgm(char *str, int slen)
360 : : {
361 : : TRGM *trg;
362 : : int len;
363 : :
364 : 88829 : protect_out_of_mem(slen);
365 : :
2489 tgl@sss.pgh.pa.us 366 : 88829 : trg = (TRGM *) palloc(TRGMHDRSIZE + sizeof(trgm) * (slen / 2 + 1) * 3);
2951 teodor@sigaev.ru 367 : 88829 : trg->flag = ARRKEY;
368 : :
2216 369 : 88829 : len = generate_trgm_only(GETARR(trg), str, slen, NULL);
2951 370 : 88829 : SET_VARSIZE(trg, CALCGTSIZE(ARRKEY, len));
371 : :
372 [ + + ]: 88829 : if (len == 0)
7258 373 : 4 : return trg;
374 : :
375 : : /*
376 : : * Make trigrams unique.
377 : : */
3744 tgl@sss.pgh.pa.us 378 [ + - ]: 88825 : if (len > 1)
379 : : {
432 peter@eisentraut.org 380 : 88825 : qsort(GETARR(trg), len, sizeof(trgm), comp_trgm);
1620 tmunro@postgresql.or 381 : 88825 : len = qunique(GETARR(trg), len, sizeof(trgm), comp_trgm);
382 : : }
383 : :
6255 tgl@sss.pgh.pa.us 384 : 88825 : SET_VARSIZE(trg, CALCGTSIZE(ARRKEY, len));
385 : :
7258 teodor@sigaev.ru 386 : 88825 : return trg;
387 : : }
388 : :
389 : : /*
390 : : * Make array of positional trigrams from two trigram arrays trg1 and trg2.
391 : : *
392 : : * trg1: trigram array of search pattern, of length len1. trg1 is required
393 : : * word which positions don't matter and replaced with -1.
394 : : * trg2: trigram array of text, of length len2. trg2 is haystack where we
395 : : * search and have to store its positions.
396 : : *
397 : : * Returns concatenated trigram array.
398 : : */
399 : : static pos_trgm *
2951 400 : 12126 : make_positional_trgm(trgm *trg1, int len1, trgm *trg2, int len2)
401 : : {
402 : : pos_trgm *result;
403 : : int i,
2866 rhaas@postgresql.org 404 : 12126 : len = len1 + len2;
405 : :
2951 teodor@sigaev.ru 406 : 12126 : result = (pos_trgm *) palloc(sizeof(pos_trgm) * len);
407 : :
408 [ + + ]: 120864 : for (i = 0; i < len1; i++)
409 : : {
410 : 108738 : memcpy(&result[i].trg, &trg1[i], sizeof(trgm));
411 : 108738 : result[i].index = -1;
412 : : }
413 : :
414 [ + + ]: 192216 : for (i = 0; i < len2; i++)
415 : : {
416 : 180090 : memcpy(&result[i + len1].trg, &trg2[i], sizeof(trgm));
417 : 180090 : result[i + len1].index = i;
418 : : }
419 : :
420 : 12126 : return result;
421 : : }
422 : :
423 : : /*
424 : : * Compare position trigrams: compare trigrams first and position second.
425 : : */
426 : : static int
427 : 1307720 : comp_ptrgm(const void *v1, const void *v2)
428 : : {
2866 rhaas@postgresql.org 429 : 1307720 : const pos_trgm *p1 = (const pos_trgm *) v1;
430 : 1307720 : const pos_trgm *p2 = (const pos_trgm *) v2;
431 : : int cmp;
432 : :
2951 teodor@sigaev.ru 433 [ + + + + : 1307720 : cmp = CMPTRGM(p1->trg, p2->trg);
+ + + + +
+ + + ]
434 [ + + ]: 1307720 : if (cmp != 0)
435 : 1268016 : return cmp;
436 : :
58 nathan@postgresql.or 437 :GNC 39704 : return pg_cmp_s32(p1->index, p2->index);
438 : : }
439 : :
440 : : /*
441 : : * Iterative search function which calculates maximum similarity with word in
442 : : * the string. Maximum similarity is only calculated only if the flag
443 : : * WORD_SIMILARITY_CHECK_ONLY isn't set.
444 : : *
445 : : * trg2indexes: array which stores indexes of the array "found".
446 : : * found: array which stores true of false values.
447 : : * ulen1: count of unique trigrams of array "trg1".
448 : : * len2: length of array "trg2" and array "trg2indexes".
449 : : * len: length of the array "found".
450 : : * flags: set of boolean flags parameterizing similarity calculation.
451 : : * bounds: whether each trigram is left/right bound of word.
452 : : *
453 : : * Returns word similarity.
454 : : */
455 : : static float4
2951 teodor@sigaev.ru 456 :CBC 12126 : iterate_word_similarity(int *trg2indexes,
457 : : bool *found,
458 : : int ulen1,
459 : : int len2,
460 : : int len,
461 : : uint8 flags,
462 : : TrgmBound *bounds)
463 : : {
464 : : int *lastpos,
465 : : i,
466 : 12126 : ulen2 = 0,
467 : 12126 : count = 0,
468 : 12126 : upper = -1,
469 : : lower;
470 : : float4 smlr_cur,
471 : 12126 : smlr_max = 0.0f;
472 : : double threshold;
473 : :
2216 474 [ + + - + ]: 12126 : Assert(bounds || !(flags & WORD_SIMILARITY_STRICT));
475 : :
476 : : /* Select appropriate threshold */
477 : 24252 : threshold = (flags & WORD_SIMILARITY_STRICT) ?
2180 tgl@sss.pgh.pa.us 478 [ + + ]: 12126 : strict_word_similarity_threshold :
479 : : word_similarity_threshold;
480 : :
481 : : /*
482 : : * Consider first trigram as initial lower bound for strict word
483 : : * similarity, or initialize it later with first trigram present for plain
484 : : * word similarity.
485 : : */
2216 teodor@sigaev.ru 486 [ + + ]: 12126 : lower = (flags & WORD_SIMILARITY_STRICT) ? 0 : -1;
487 : :
488 : : /* Memorise last position of each trigram */
2951 489 : 12126 : lastpos = (int *) palloc(sizeof(int) * len);
490 : 12126 : memset(lastpos, -1, sizeof(int) * len);
491 : :
492 [ + + ]: 183646 : for (i = 0; i < len2; i++)
493 : : {
494 : : int trgindex;
495 : :
587 dgustafsson@postgres 496 [ - + ]: 173304 : CHECK_FOR_INTERRUPTS();
497 : :
498 : : /* Get index of next trigram */
499 : 173304 : trgindex = trg2indexes[i];
500 : :
501 : : /* Update last position of this trigram */
2951 teodor@sigaev.ru 502 [ + + + + ]: 173304 : if (lower >= 0 || found[trgindex])
503 : : {
504 [ + + ]: 135794 : if (lastpos[trgindex] < 0)
505 : : {
506 : 133942 : ulen2++;
507 [ + + ]: 133942 : if (found[trgindex])
508 : 30756 : count++;
509 : : }
510 : 135794 : lastpos[trgindex] = i;
511 : : }
512 : :
513 : : /*
514 : : * Adjust upper bound if trigram is upper bound of word for strict
515 : : * word similarity, or if trigram is present in required substring for
516 : : * plain word similarity
517 : : */
2216 518 [ + + + + ]: 250347 : if ((flags & WORD_SIMILARITY_STRICT) ? (bounds[i] & TRGM_BOUND_RIGHT)
2180 tgl@sss.pgh.pa.us 519 : 77043 : : found[trgindex])
520 : : {
521 : : int prev_lower,
522 : : tmp_ulen2,
523 : : tmp_lower,
524 : : tmp_count;
525 : :
2951 teodor@sigaev.ru 526 : 25635 : upper = i;
527 [ + + ]: 25635 : if (lower == -1)
528 : : {
529 : 4695 : lower = i;
530 : 4695 : ulen2 = 1;
531 : : }
532 : :
533 : 25635 : smlr_cur = CALCSML(count, ulen1, ulen2);
534 : :
535 : : /* Also try to adjust lower bound for greater similarity */
536 : 25635 : tmp_count = count;
537 : 25635 : tmp_ulen2 = ulen2;
538 : 25635 : prev_lower = lower;
539 [ + + ]: 208579 : for (tmp_lower = lower; tmp_lower <= upper; tmp_lower++)
540 : : {
541 : : float smlr_tmp;
542 : : int tmp_trgindex;
543 : :
544 : : /*
545 : : * Adjust lower bound only if trigram is lower bound of word
546 : : * for strict word similarity, or consider every trigram as
547 : : * lower bound for plain word similarity.
548 : : */
2216 549 [ + + ]: 184728 : if (!(flags & WORD_SIMILARITY_STRICT)
550 [ + + ]: 145180 : || (bounds[tmp_lower] & TRGM_BOUND_LEFT))
551 : : {
552 : 59680 : smlr_tmp = CALCSML(tmp_count, ulen1, tmp_ulen2);
553 [ + + ]: 59680 : if (smlr_tmp > smlr_cur)
554 : : {
555 : 3511 : smlr_cur = smlr_tmp;
556 : 3511 : ulen2 = tmp_ulen2;
557 : 3511 : lower = tmp_lower;
558 : 3511 : count = tmp_count;
559 : : }
560 : :
561 : : /*
562 : : * If we only check that word similarity is greater than
563 : : * threshold we do not need to calculate a maximum
564 : : * similarity.
565 : : */
566 [ + + ]: 59680 : if ((flags & WORD_SIMILARITY_CHECK_ONLY)
567 [ + + ]: 37114 : && smlr_cur >= threshold)
568 : 1784 : break;
569 : : }
570 : :
2951 571 : 182944 : tmp_trgindex = trg2indexes[tmp_lower];
572 [ + + ]: 182944 : if (lastpos[tmp_trgindex] == tmp_lower)
573 : : {
574 : 180685 : tmp_ulen2--;
575 [ + + ]: 180685 : if (found[tmp_trgindex])
576 : 46579 : tmp_count--;
577 : : }
578 : : }
579 : :
580 [ + + ]: 25635 : smlr_max = Max(smlr_max, smlr_cur);
581 : :
582 : : /*
583 : : * if we only check that word similarity is greater than threshold
584 : : * we do not need to calculate a maximum similarity.
585 : : */
2216 586 [ + + + + ]: 25635 : if ((flags & WORD_SIMILARITY_CHECK_ONLY) && smlr_max >= threshold)
2951 587 : 1784 : break;
588 : :
589 [ + + ]: 40599 : for (tmp_lower = prev_lower; tmp_lower < lower; tmp_lower++)
590 : : {
591 : : int tmp_trgindex;
592 : :
593 : 16748 : tmp_trgindex = trg2indexes[tmp_lower];
594 [ + + ]: 16748 : if (lastpos[tmp_trgindex] == tmp_lower)
595 : 16000 : lastpos[tmp_trgindex] = -1;
596 : : }
597 : : }
598 : : }
599 : :
600 : 12126 : pfree(lastpos);
601 : :
602 : 12126 : return smlr_max;
603 : : }
604 : :
605 : : /*
606 : : * Calculate word similarity.
607 : : * This function prepare two arrays: "trg2indexes" and "found". Then this arrays
608 : : * are used to calculate word similarity using iterate_word_similarity().
609 : : *
610 : : * "trg2indexes" is array which stores indexes of the array "found".
611 : : * In other words:
612 : : * trg2indexes[j] = i;
613 : : * found[i] = true (or false);
614 : : * If found[i] == true then there is trigram trg2[j] in array "trg1".
615 : : * If found[i] == false then there is not trigram trg2[j] in array "trg1".
616 : : *
617 : : * str1: search pattern string, of length slen1 bytes.
618 : : * str2: text in which we are looking for a word, of length slen2 bytes.
619 : : * flags: set of boolean flags parameterizing similarity calculation.
620 : : *
621 : : * Returns word similarity.
622 : : */
623 : : static float4
624 : 12126 : calc_word_similarity(char *str1, int slen1, char *str2, int slen2,
625 : : uint8 flags)
626 : : {
627 : : bool *found;
628 : : pos_trgm *ptrg;
629 : : trgm *trg1;
630 : : trgm *trg2;
631 : : int len1,
632 : : len2,
633 : : len,
634 : : i,
635 : : j,
636 : : ulen1;
637 : : int *trg2indexes;
638 : : float4 result;
639 : : TrgmBound *bounds;
640 : :
641 : 12126 : protect_out_of_mem(slen1 + slen2);
642 : :
643 : : /* Make positional trigrams */
2489 tgl@sss.pgh.pa.us 644 : 12126 : trg1 = (trgm *) palloc(sizeof(trgm) * (slen1 / 2 + 1) * 3);
645 : 12126 : trg2 = (trgm *) palloc(sizeof(trgm) * (slen2 / 2 + 1) * 3);
2216 teodor@sigaev.ru 646 [ + + ]: 12126 : if (flags & WORD_SIMILARITY_STRICT)
647 : 6662 : bounds = (TrgmBound *) palloc0(sizeof(TrgmBound) * (slen2 / 2 + 1) * 3);
648 : : else
649 : 5464 : bounds = NULL;
650 : :
651 : 12126 : len1 = generate_trgm_only(trg1, str1, slen1, NULL);
652 : 12126 : len2 = generate_trgm_only(trg2, str2, slen2, bounds);
653 : :
2951 654 : 12126 : ptrg = make_positional_trgm(trg1, len1, trg2, len2);
655 : 12126 : len = len1 + len2;
656 : 12126 : qsort(ptrg, len, sizeof(pos_trgm), comp_ptrgm);
657 : :
658 : 12126 : pfree(trg1);
659 : 12126 : pfree(trg2);
660 : :
661 : : /*
662 : : * Merge positional trigrams array: enumerate each trigram and find its
663 : : * presence in required word.
664 : : */
665 : 12126 : trg2indexes = (int *) palloc(sizeof(int) * len2);
666 : 12126 : found = (bool *) palloc0(sizeof(bool) * len);
667 : :
668 : 12126 : ulen1 = 0;
669 : 12126 : j = 0;
670 [ + + ]: 300954 : for (i = 0; i < len; i++)
671 : : {
672 [ + + ]: 288828 : if (i > 0)
673 : : {
2866 rhaas@postgresql.org 674 [ + + + - : 276702 : int cmp = CMPTRGM(ptrg[i - 1].trg, ptrg[i].trg);
+ + + - +
+ + - ]
675 : :
2951 teodor@sigaev.ru 676 [ + + ]: 276702 : if (cmp != 0)
677 : : {
678 [ + + ]: 242502 : if (found[j])
679 : 101139 : ulen1++;
680 : 242502 : j++;
681 : : }
682 : : }
683 : :
684 [ + + ]: 288828 : if (ptrg[i].index >= 0)
685 : : {
686 : 180090 : trg2indexes[ptrg[i].index] = j;
687 : : }
688 : : else
689 : : {
690 : 108738 : found[j] = true;
691 : : }
692 : : }
693 [ + + ]: 12126 : if (found[j])
694 : 7599 : ulen1++;
695 : :
696 : : /* Run iterative procedure to find maximum similarity with word */
697 : 12126 : result = iterate_word_similarity(trg2indexes, found, ulen1, len2, len,
698 : : flags, bounds);
699 : :
700 : 12126 : pfree(trg2indexes);
701 : 12126 : pfree(found);
702 : 12126 : pfree(ptrg);
703 : :
704 : 12126 : return result;
705 : : }
706 : :
707 : :
708 : : /*
709 : : * Extract the next non-wildcard part of a search string, i.e. a word bounded
710 : : * by '_' or '%' meta-characters, non-word characters or string end.
711 : : *
712 : : * str: source string, of length lenstr bytes (need not be null-terminated)
713 : : * buf: where to return the substring (must be long enough)
714 : : * *bytelen: receives byte length of the found substring
715 : : * *charlen: receives character length of the found substring
716 : : *
717 : : * Returns pointer to end+1 of the found substring in the source string.
718 : : * Returns NULL if no word found (in which case buf, bytelen, charlen not set)
719 : : *
720 : : * If the found word is bounded by non-word characters or string boundaries
721 : : * then this function will include corresponding padding spaces into buf.
722 : : */
723 : : static const char *
4822 tgl@sss.pgh.pa.us 724 : 119 : get_wildcard_part(const char *str, int lenstr,
725 : : char *buf, int *bytelen, int *charlen)
726 : : {
727 : 119 : const char *beginword = str;
728 : : const char *endword;
729 : 119 : char *s = buf;
4255 730 : 119 : bool in_leading_wildcard_meta = false;
731 : 119 : bool in_trailing_wildcard_meta = false;
4753 bruce@momjian.us 732 : 119 : bool in_escape = false;
733 : : int clen;
734 : :
735 : : /*
736 : : * Find the first word character, remembering whether preceding character
737 : : * was wildcard meta-character. Note that the in_escape state persists
738 : : * from this loop to the next one, since we may exit at a word character
739 : : * that is in_escape.
740 : : */
4822 tgl@sss.pgh.pa.us 741 [ + + ]: 241 : while (beginword - str < lenstr)
742 : : {
743 [ + + ]: 186 : if (in_escape)
744 : : {
4023 745 [ + - ]: 3 : if (ISWORDCHR(beginword))
4822 746 : 3 : break;
4255 tgl@sss.pgh.pa.us 747 :UBC 0 : in_escape = false;
748 : 0 : in_leading_wildcard_meta = false;
749 : : }
750 : : else
751 : : {
4822 tgl@sss.pgh.pa.us 752 [ + + ]:CBC 183 : if (ISESCAPECHAR(beginword))
753 : 3 : in_escape = true;
754 [ + - + + ]: 180 : else if (ISWILDCARDCHAR(beginword))
4255 755 : 104 : in_leading_wildcard_meta = true;
4023 756 [ + + ]: 76 : else if (ISWORDCHR(beginword))
4822 757 : 61 : break;
758 : : else
4255 759 : 15 : in_leading_wildcard_meta = false;
760 : : }
4822 761 : 122 : beginword += pg_mblen(beginword);
762 : : }
763 : :
764 : : /*
765 : : * Handle string end.
766 : : */
767 [ + + ]: 119 : if (beginword - str >= lenstr)
768 : 55 : return NULL;
769 : :
770 : : /*
771 : : * Add left padding spaces if preceding character wasn't wildcard
772 : : * meta-character.
773 : : */
774 : 64 : *charlen = 0;
4255 775 [ + + ]: 64 : if (!in_leading_wildcard_meta)
776 : : {
777 : : if (LPADDING > 0)
778 : : {
4822 779 : 15 : *s++ = ' ';
780 : 15 : (*charlen)++;
781 : : if (LPADDING > 1)
782 : : {
783 : 15 : *s++ = ' ';
784 : 15 : (*charlen)++;
785 : : }
786 : : }
787 : : }
788 : :
789 : : /*
790 : : * Copy data into buf until wildcard meta-character, non-word character or
791 : : * string boundary. Strip escapes during copy.
792 : : */
793 : 64 : endword = beginword;
794 [ + - ]: 244 : while (endword - str < lenstr)
795 : : {
796 : 244 : clen = pg_mblen(endword);
797 [ + + ]: 244 : if (in_escape)
798 : : {
4023 799 [ + - ]: 3 : if (ISWORDCHR(endword))
800 : : {
4822 801 : 3 : memcpy(s, endword, clen);
802 : 3 : (*charlen)++;
803 : 3 : s += clen;
804 : : }
805 : : else
806 : : {
807 : : /*
808 : : * Back up endword to the escape character when stopping at an
809 : : * escaped char, so that subsequent get_wildcard_part will
810 : : * restart from the escape character. We assume here that
811 : : * escape chars are single-byte.
812 : : */
4255 tgl@sss.pgh.pa.us 813 :UBC 0 : endword--;
4822 814 : 0 : break;
815 : : }
4255 tgl@sss.pgh.pa.us 816 :CBC 3 : in_escape = false;
817 : : }
818 : : else
819 : : {
4822 820 [ - + ]: 241 : if (ISESCAPECHAR(endword))
4822 tgl@sss.pgh.pa.us 821 :UBC 0 : in_escape = true;
4822 tgl@sss.pgh.pa.us 822 [ + - + + ]:CBC 241 : else if (ISWILDCARDCHAR(endword))
823 : : {
4255 824 : 55 : in_trailing_wildcard_meta = true;
4822 825 : 55 : break;
826 : : }
4023 827 [ + + ]: 186 : else if (ISWORDCHR(endword))
828 : : {
4822 829 : 177 : memcpy(s, endword, clen);
830 : 177 : (*charlen)++;
831 : 177 : s += clen;
832 : : }
833 : : else
834 : 9 : break;
835 : : }
836 : 180 : endword += clen;
837 : : }
838 : :
839 : : /*
840 : : * Add right padding spaces if next character isn't wildcard
841 : : * meta-character.
842 : : */
4255 843 [ + + ]: 64 : if (!in_trailing_wildcard_meta)
844 : : {
845 : : if (RPADDING > 0)
846 : : {
4822 847 : 9 : *s++ = ' ';
848 : 9 : (*charlen)++;
849 : : if (RPADDING > 1)
850 : : {
851 : : *s++ = ' ';
852 : : (*charlen)++;
853 : : }
854 : : }
855 : : }
856 : :
857 : 64 : *bytelen = s - buf;
858 : 64 : return endword;
859 : : }
860 : :
861 : : /*
862 : : * Generates trigrams for wildcard search string.
863 : : *
864 : : * Returns array of trigrams that must occur in any string that matches the
865 : : * wildcard string. For example, given pattern "a%bcd%" the trigrams
866 : : * " a", "bcd" would be extracted.
867 : : */
868 : : TRGM *
869 : 55 : generate_wildcard_trgm(const char *str, int slen)
870 : : {
871 : : TRGM *trg;
872 : : char *buf,
873 : : *buf2;
874 : : trgm *tptr;
875 : : int len,
876 : : charlen,
877 : : bytelen;
878 : : const char *eword;
879 : :
2951 teodor@sigaev.ru 880 : 55 : protect_out_of_mem(slen);
881 : :
2489 tgl@sss.pgh.pa.us 882 : 55 : trg = (TRGM *) palloc(TRGMHDRSIZE + sizeof(trgm) * (slen / 2 + 1) * 3);
4822 883 : 55 : trg->flag = ARRKEY;
884 : 55 : SET_VARSIZE(trg, TRGMHDRSIZE);
885 : :
886 [ + - - + ]: 55 : if (slen + LPADDING + RPADDING < 3 || slen == 0)
4822 tgl@sss.pgh.pa.us 887 :UBC 0 : return trg;
888 : :
4822 tgl@sss.pgh.pa.us 889 :CBC 55 : tptr = GETARR(trg);
890 : :
891 : : /* Allocate a buffer for blank-padded, but not yet case-folded, words */
892 : 55 : buf = palloc(sizeof(char) * (slen + 4));
893 : :
894 : : /*
895 : : * Extract trigrams from each substring extracted by get_wildcard_part.
896 : : */
897 : 55 : eword = str;
898 : 119 : while ((eword = get_wildcard_part(eword, slen - (eword - str),
899 [ + + ]: 119 : buf, &bytelen, &charlen)) != NULL)
900 : : {
901 : : #ifdef IGNORECASE
902 : 64 : buf2 = lowerstr_with_len(buf, bytelen);
903 : 64 : bytelen = strlen(buf2);
904 : : #else
905 : : buf2 = buf;
906 : : #endif
907 : :
908 : : /*
909 : : * count trigrams
910 : : */
911 : 64 : tptr = make_trigrams(tptr, buf2, bytelen, charlen);
912 : :
913 : : #ifdef IGNORECASE
914 : 64 : pfree(buf2);
915 : : #endif
916 : : }
917 : :
918 : 55 : pfree(buf);
919 : :
920 [ + + ]: 55 : if ((len = tptr - GETARR(trg)) == 0)
921 : 24 : return trg;
922 : :
923 : : /*
924 : : * Make trigrams unique.
925 : : */
3744 926 [ + + ]: 31 : if (len > 1)
927 : : {
432 peter@eisentraut.org 928 : 17 : qsort(GETARR(trg), len, sizeof(trgm), comp_trgm);
1620 tmunro@postgresql.or 929 : 17 : len = qunique(GETARR(trg), len, sizeof(trgm), comp_trgm);
930 : : }
931 : :
4822 tgl@sss.pgh.pa.us 932 : 31 : SET_VARSIZE(trg, CALCGTSIZE(ARRKEY, len));
933 : :
934 : 31 : return trg;
935 : : }
936 : :
937 : : uint32
5632 teodor@sigaev.ru 938 : 34773 : trgm2int(trgm *ptr)
939 : : {
5421 bruce@momjian.us 940 : 34773 : uint32 val = 0;
941 : :
942 : 34773 : val |= *(((unsigned char *) ptr));
5632 teodor@sigaev.ru 943 : 34773 : val <<= 8;
5421 bruce@momjian.us 944 : 34773 : val |= *(((unsigned char *) ptr) + 1);
5632 teodor@sigaev.ru 945 : 34773 : val <<= 8;
5421 bruce@momjian.us 946 : 34773 : val |= *(((unsigned char *) ptr) + 2);
947 : :
5632 teodor@sigaev.ru 948 : 34773 : return val;
949 : : }
950 : :
951 : : Datum
7168 bruce@momjian.us 952 : 7 : show_trgm(PG_FUNCTION_ARGS)
953 : : {
2590 noah@leadboat.com 954 : 7 : text *in = PG_GETARG_TEXT_PP(0);
955 : : TRGM *trg;
956 : : Datum *d;
957 : : ArrayType *a;
958 : : trgm *ptr;
959 : : int i;
960 : :
961 [ - + - - : 7 : trg = generate_trgm(VARDATA_ANY(in), VARSIZE_ANY_EXHDR(in));
- - - - -
+ - + ]
7168 bruce@momjian.us 962 : 7 : d = (Datum *) palloc(sizeof(Datum) * (1 + ARRNELEM(trg)));
963 : :
6121 tgl@sss.pgh.pa.us 964 [ + + ]: 44 : for (i = 0, ptr = GETARR(trg); i < ARRNELEM(trg); i++, ptr++)
965 : : {
5421 bruce@momjian.us 966 [ + - ]: 37 : text *item = (text *) palloc(VARHDRSZ + Max(12, pg_database_encoding_max_length() * 3));
967 : :
968 [ + - + - : 37 : if (pg_database_encoding_max_length() > 1 && !ISPRINTABLETRGM(ptr))
+ + + - +
- + + + -
+ - + + -
+ ]
969 : : {
5632 teodor@sigaev.ru 970 :UBC 0 : snprintf(VARDATA(item), 12, "0x%06x", trgm2int(ptr));
971 : 0 : SET_VARSIZE(item, VARHDRSZ + strlen(VARDATA(item)));
972 : : }
973 : : else
974 : : {
5632 teodor@sigaev.ru 975 :CBC 37 : SET_VARSIZE(item, VARHDRSZ + 3);
976 : 37 : CPTRGM(VARDATA(item), ptr);
977 : : }
6121 tgl@sss.pgh.pa.us 978 : 37 : d[i] = PointerGetDatum(item);
979 : : }
980 : :
653 peter@eisentraut.org 981 : 7 : a = construct_array_builtin(d, ARRNELEM(trg), TEXTOID);
982 : :
6121 tgl@sss.pgh.pa.us 983 [ + + ]: 44 : for (i = 0; i < ARRNELEM(trg); i++)
984 : 37 : pfree(DatumGetPointer(d[i]));
985 : :
7258 teodor@sigaev.ru 986 : 7 : pfree(d);
987 : 7 : pfree(trg);
7168 bruce@momjian.us 988 [ - + ]: 7 : PG_FREE_IF_COPY(in, 0);
989 : :
7258 teodor@sigaev.ru 990 : 7 : PG_RETURN_POINTER(a);
991 : : }
992 : :
993 : : float4
2951 994 : 68994 : cnt_sml(TRGM *trg1, TRGM *trg2, bool inexact)
995 : : {
996 : : trgm *ptr1,
997 : : *ptr2;
7168 bruce@momjian.us 998 : 68994 : int count = 0;
999 : : int len1,
1000 : : len2;
1001 : :
7258 teodor@sigaev.ru 1002 : 68994 : ptr1 = GETARR(trg1);
1003 : 68994 : ptr2 = GETARR(trg2);
1004 : :
1005 : 68994 : len1 = ARRNELEM(trg1);
1006 : 68994 : len2 = ARRNELEM(trg2);
1007 : :
1008 : : /* explicit test is needed to avoid 0/0 division when both lengths are 0 */
4078 tgl@sss.pgh.pa.us 1009 [ + + - + ]: 68994 : if (len1 <= 0 || len2 <= 0)
1010 : 1 : return (float4) 0.0;
1011 : :
7168 bruce@momjian.us 1012 [ + + + + ]: 878157 : while (ptr1 - GETARR(trg1) < len1 && ptr2 - GETARR(trg2) < len2)
1013 : : {
1014 [ + + + + : 809164 : int res = CMPTRGM(ptr1, ptr2);
+ + + + +
+ + + ]
1015 : :
1016 [ + + ]: 809164 : if (res < 0)
7258 teodor@sigaev.ru 1017 : 183669 : ptr1++;
7168 bruce@momjian.us 1018 [ + + ]: 625495 : else if (res > 0)
7258 teodor@sigaev.ru 1019 : 214677 : ptr2++;
1020 : : else
1021 : : {
1022 : 410818 : ptr1++;
1023 : 410818 : ptr2++;
1024 : 410818 : count++;
1025 : : }
1026 : : }
1027 : :
1028 : : /*
1029 : : * If inexact then len2 is equal to count, because we don't know actual
1030 : : * length of second string in inexact search and we can assume that count
1031 : : * is a lower bound of len2.
1032 : : */
2951 1033 [ + + ]: 68993 : return CALCSML(count, len1, inexact ? count : len2);
1034 : : }
1035 : :
1036 : :
1037 : : /*
1038 : : * Returns whether trg2 contains all trigrams in trg1.
1039 : : * This relies on the trigram arrays being sorted.
1040 : : */
1041 : : bool
4822 tgl@sss.pgh.pa.us 1042 : 190 : trgm_contained_by(TRGM *trg1, TRGM *trg2)
1043 : : {
1044 : : trgm *ptr1,
1045 : : *ptr2;
1046 : : int len1,
1047 : : len2;
1048 : :
1049 : 190 : ptr1 = GETARR(trg1);
1050 : 190 : ptr2 = GETARR(trg2);
1051 : :
1052 : 190 : len1 = ARRNELEM(trg1);
1053 : 190 : len2 = ARRNELEM(trg2);
1054 : :
1055 [ + + + + ]: 622 : while (ptr1 - GETARR(trg1) < len1 && ptr2 - GETARR(trg2) < len2)
1056 : : {
1057 [ + + + + : 599 : int res = CMPTRGM(ptr1, ptr2);
+ + + + +
+ + + ]
1058 : :
1059 [ + + ]: 599 : if (res < 0)
1060 : 167 : return false;
1061 [ + + ]: 432 : else if (res > 0)
1062 : 320 : ptr2++;
1063 : : else
1064 : : {
1065 : 112 : ptr1++;
1066 : 112 : ptr2++;
1067 : : }
1068 : : }
1069 [ + + ]: 23 : if (ptr1 - GETARR(trg1) < len1)
1070 : 4 : return false;
1071 : : else
1072 : 19 : return true;
1073 : : }
1074 : :
1075 : : /*
1076 : : * Return a palloc'd boolean array showing, for each trigram in "query",
1077 : : * whether it is present in the trigram array "key".
1078 : : * This relies on the "key" array being sorted, but "query" need not be.
1079 : : */
1080 : : bool *
4022 1081 : 2150 : trgm_presence_map(TRGM *query, TRGM *key)
1082 : : {
1083 : : bool *result;
1084 : 2150 : trgm *ptrq = GETARR(query),
1085 : 2150 : *ptrk = GETARR(key);
1086 : 2150 : int lenq = ARRNELEM(query),
1087 : 2150 : lenk = ARRNELEM(key),
1088 : : i;
1089 : :
1090 : 2150 : result = (bool *) palloc0(lenq * sizeof(bool));
1091 : :
1092 : : /* for each query trigram, do a binary search in the key array */
1093 [ + + ]: 507560 : for (i = 0; i < lenq; i++)
1094 : : {
1095 : 505410 : int lo = 0;
1096 : 505410 : int hi = lenk;
1097 : :
1098 [ + + ]: 2373653 : while (lo < hi)
1099 : : {
1100 : 1876282 : int mid = (lo + hi) / 2;
1101 [ + + + + : 1876282 : int res = CMPTRGM(ptrq, ptrk + mid);
+ + + + +
+ + + ]
1102 : :
1103 [ + + ]: 1876282 : if (res < 0)
1104 : 784082 : hi = mid;
1105 [ + + ]: 1092200 : else if (res > 0)
1106 : 1084161 : lo = mid + 1;
1107 : : else
1108 : : {
1109 : 8039 : result[i] = true;
1110 : 8039 : break;
1111 : : }
1112 : : }
1113 : 505410 : ptrq++;
1114 : : }
1115 : :
1116 : 2150 : return result;
1117 : : }
1118 : :
1119 : : Datum
7168 bruce@momjian.us 1120 : 31452 : similarity(PG_FUNCTION_ARGS)
1121 : : {
2590 noah@leadboat.com 1122 : 31452 : text *in1 = PG_GETARG_TEXT_PP(0);
1123 : 31452 : text *in2 = PG_GETARG_TEXT_PP(1);
1124 : : TRGM *trg1,
1125 : : *trg2;
1126 : : float4 res;
1127 : :
1128 [ - + - - : 31452 : trg1 = generate_trgm(VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1));
- - - - +
+ + + ]
1129 [ - + - - : 31452 : trg2 = generate_trgm(VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2));
- - - - -
+ - + ]
1130 : :
2951 teodor@sigaev.ru 1131 : 31452 : res = cnt_sml(trg1, trg2, false);
1132 : :
7258 1133 : 31452 : pfree(trg1);
1134 : 31452 : pfree(trg2);
7168 bruce@momjian.us 1135 [ - + ]: 31452 : PG_FREE_IF_COPY(in1, 0);
1136 [ - + ]: 31452 : PG_FREE_IF_COPY(in2, 1);
1137 : :
7258 teodor@sigaev.ru 1138 : 31452 : PG_RETURN_FLOAT4(res);
1139 : : }
1140 : :
1141 : : Datum
2951 1142 : 902 : word_similarity(PG_FUNCTION_ARGS)
1143 : : {
1144 : 902 : text *in1 = PG_GETARG_TEXT_PP(0);
1145 : 902 : text *in2 = PG_GETARG_TEXT_PP(1);
1146 : : float4 res;
1147 : :
1148 [ - + - - : 1804 : res = calc_word_similarity(VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1),
- - - - -
+ - + ]
2866 rhaas@postgresql.org 1149 [ - + - - : 1804 : VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2),
- - - - +
- + - ]
1150 : : 0);
1151 : :
2216 teodor@sigaev.ru 1152 [ - + ]: 902 : PG_FREE_IF_COPY(in1, 0);
1153 [ - + ]: 902 : PG_FREE_IF_COPY(in2, 1);
1154 : 902 : PG_RETURN_FLOAT4(res);
1155 : : }
1156 : :
1157 : : Datum
1158 : 882 : strict_word_similarity(PG_FUNCTION_ARGS)
1159 : : {
1160 : 882 : text *in1 = PG_GETARG_TEXT_PP(0);
1161 : 882 : text *in2 = PG_GETARG_TEXT_PP(1);
1162 : : float4 res;
1163 : :
1164 [ - + - - : 1764 : res = calc_word_similarity(VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1),
- - - - -
+ - + ]
1165 [ - + - - : 1764 : VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2),
- - - - +
- + - ]
1166 : : WORD_SIMILARITY_STRICT);
1167 : :
2951 1168 [ - + ]: 882 : PG_FREE_IF_COPY(in1, 0);
1169 [ - + ]: 882 : PG_FREE_IF_COPY(in2, 1);
1170 : 882 : PG_RETURN_FLOAT4(res);
1171 : : }
1172 : :
1173 : : Datum
4880 tgl@sss.pgh.pa.us 1174 : 1004 : similarity_dist(PG_FUNCTION_ARGS)
1175 : : {
1176 : 1004 : float4 res = DatumGetFloat4(DirectFunctionCall2(similarity,
1177 : : PG_GETARG_DATUM(0),
1178 : : PG_GETARG_DATUM(1)));
1179 : :
1180 : 1004 : PG_RETURN_FLOAT4(1.0 - res);
1181 : : }
1182 : :
1183 : : Datum
7168 bruce@momjian.us 1184 : 6000 : similarity_op(PG_FUNCTION_ARGS)
1185 : : {
4880 tgl@sss.pgh.pa.us 1186 : 6000 : float4 res = DatumGetFloat4(DirectFunctionCall2(similarity,
1187 : : PG_GETARG_DATUM(0),
1188 : : PG_GETARG_DATUM(1)));
1189 : :
2951 teodor@sigaev.ru 1190 : 6000 : PG_RETURN_BOOL(res >= similarity_threshold);
1191 : : }
1192 : :
1193 : : Datum
1194 : 1924 : word_similarity_op(PG_FUNCTION_ARGS)
1195 : : {
1196 : 1924 : text *in1 = PG_GETARG_TEXT_PP(0);
1197 : 1924 : text *in2 = PG_GETARG_TEXT_PP(1);
1198 : : float4 res;
1199 : :
1200 [ - + - - : 3848 : res = calc_word_similarity(VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1),
- - - - -
+ - + ]
2866 rhaas@postgresql.org 1201 [ - + - - : 3848 : VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2),
- - - - +
- + - ]
1202 : : WORD_SIMILARITY_CHECK_ONLY);
1203 : :
2951 teodor@sigaev.ru 1204 [ - + ]: 1924 : PG_FREE_IF_COPY(in1, 0);
1205 [ - + ]: 1924 : PG_FREE_IF_COPY(in2, 1);
1206 : 1924 : PG_RETURN_BOOL(res >= word_similarity_threshold);
1207 : : }
1208 : :
1209 : : Datum
1210 : 1924 : word_similarity_commutator_op(PG_FUNCTION_ARGS)
1211 : : {
1212 : 1924 : text *in1 = PG_GETARG_TEXT_PP(0);
1213 : 1924 : text *in2 = PG_GETARG_TEXT_PP(1);
1214 : : float4 res;
1215 : :
1216 [ - + - - : 3848 : res = calc_word_similarity(VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2),
- - - - -
+ - + ]
2866 rhaas@postgresql.org 1217 [ - + - - : 3848 : VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1),
- - - - +
- + - ]
1218 : : WORD_SIMILARITY_CHECK_ONLY);
1219 : :
2951 teodor@sigaev.ru 1220 [ - + ]: 1924 : PG_FREE_IF_COPY(in1, 0);
1221 [ - + ]: 1924 : PG_FREE_IF_COPY(in2, 1);
1222 : 1924 : PG_RETURN_BOOL(res >= word_similarity_threshold);
1223 : : }
1224 : :
1225 : : Datum
2951 teodor@sigaev.ru 1226 :UBC 0 : word_similarity_dist_op(PG_FUNCTION_ARGS)
1227 : : {
1228 : 0 : text *in1 = PG_GETARG_TEXT_PP(0);
1229 : 0 : text *in2 = PG_GETARG_TEXT_PP(1);
1230 : : float4 res;
1231 : :
1232 [ # # # # : 0 : res = calc_word_similarity(VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1),
# # # # #
# # # ]
2866 rhaas@postgresql.org 1233 [ # # # # : 0 : VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2),
# # # # #
# # # ]
1234 : : 0);
1235 : :
2951 teodor@sigaev.ru 1236 [ # # ]: 0 : PG_FREE_IF_COPY(in1, 0);
1237 [ # # ]: 0 : PG_FREE_IF_COPY(in2, 1);
1238 : 0 : PG_RETURN_FLOAT4(1.0 - res);
1239 : : }
1240 : :
1241 : : Datum
2951 teodor@sigaev.ru 1242 :CBC 714 : word_similarity_dist_commutator_op(PG_FUNCTION_ARGS)
1243 : : {
1244 : 714 : text *in1 = PG_GETARG_TEXT_PP(0);
1245 : 714 : text *in2 = PG_GETARG_TEXT_PP(1);
1246 : : float4 res;
1247 : :
1248 [ - + - - : 1428 : res = calc_word_similarity(VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2),
- - - - -
+ - + ]
2866 rhaas@postgresql.org 1249 [ - + - - : 1428 : VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1),
- - - - +
- + - ]
1250 : : 0);
1251 : :
2216 teodor@sigaev.ru 1252 [ - + ]: 714 : PG_FREE_IF_COPY(in1, 0);
1253 [ - + ]: 714 : PG_FREE_IF_COPY(in2, 1);
1254 : 714 : PG_RETURN_FLOAT4(1.0 - res);
1255 : : }
1256 : :
1257 : : Datum
1258 : 2530 : strict_word_similarity_op(PG_FUNCTION_ARGS)
1259 : : {
1260 : 2530 : text *in1 = PG_GETARG_TEXT_PP(0);
1261 : 2530 : text *in2 = PG_GETARG_TEXT_PP(1);
1262 : : float4 res;
1263 : :
1264 [ - + - - : 5060 : res = calc_word_similarity(VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1),
- - - - -
+ - + ]
1265 [ - + - - : 5060 : VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2),
- - - - +
- + - ]
1266 : : WORD_SIMILARITY_CHECK_ONLY | WORD_SIMILARITY_STRICT);
1267 : :
1268 [ - + ]: 2530 : PG_FREE_IF_COPY(in1, 0);
1269 [ - + ]: 2530 : PG_FREE_IF_COPY(in2, 1);
1270 : 2530 : PG_RETURN_BOOL(res >= strict_word_similarity_threshold);
1271 : : }
1272 : :
1273 : : Datum
1274 : 2530 : strict_word_similarity_commutator_op(PG_FUNCTION_ARGS)
1275 : : {
1276 : 2530 : text *in1 = PG_GETARG_TEXT_PP(0);
1277 : 2530 : text *in2 = PG_GETARG_TEXT_PP(1);
1278 : : float4 res;
1279 : :
1280 [ - + - - : 5060 : res = calc_word_similarity(VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2),
- - - - -
+ - + ]
1281 [ - + - - : 5060 : VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1),
- - - - +
- + - ]
1282 : : WORD_SIMILARITY_CHECK_ONLY | WORD_SIMILARITY_STRICT);
1283 : :
1284 [ - + ]: 2530 : PG_FREE_IF_COPY(in1, 0);
1285 [ - + ]: 2530 : PG_FREE_IF_COPY(in2, 1);
1286 : 2530 : PG_RETURN_BOOL(res >= strict_word_similarity_threshold);
1287 : : }
1288 : :
1289 : : Datum
2216 teodor@sigaev.ru 1290 :UBC 0 : strict_word_similarity_dist_op(PG_FUNCTION_ARGS)
1291 : : {
1292 : 0 : text *in1 = PG_GETARG_TEXT_PP(0);
1293 : 0 : text *in2 = PG_GETARG_TEXT_PP(1);
1294 : : float4 res;
1295 : :
1296 [ # # # # : 0 : res = calc_word_similarity(VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1),
# # # # #
# # # ]
1297 [ # # # # : 0 : VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2),
# # # # #
# # # ]
1298 : : WORD_SIMILARITY_STRICT);
1299 : :
1300 [ # # ]: 0 : PG_FREE_IF_COPY(in1, 0);
1301 [ # # ]: 0 : PG_FREE_IF_COPY(in2, 1);
1302 : 0 : PG_RETURN_FLOAT4(1.0 - res);
1303 : : }
1304 : :
1305 : : Datum
2216 teodor@sigaev.ru 1306 :CBC 720 : strict_word_similarity_dist_commutator_op(PG_FUNCTION_ARGS)
1307 : : {
1308 : 720 : text *in1 = PG_GETARG_TEXT_PP(0);
1309 : 720 : text *in2 = PG_GETARG_TEXT_PP(1);
1310 : : float4 res;
1311 : :
1312 [ - + - - : 1440 : res = calc_word_similarity(VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2),
- - - - -
+ - + ]
1313 [ - + - - : 1440 : VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1),
- - - - +
- + - ]
1314 : : WORD_SIMILARITY_STRICT);
1315 : :
2951 1316 [ - + ]: 720 : PG_FREE_IF_COPY(in1, 0);
1317 [ - + ]: 720 : PG_FREE_IF_COPY(in2, 1);
1318 : 720 : PG_RETURN_FLOAT4(1.0 - res);
1319 : : }
|