1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-11 20:28:21 +03:00

Support LIKE and ILIKE index searches via contrib/pg_trgm indexes.

Unlike Btree-based LIKE optimization, this works for non-left-anchored
search patterns.  The effectiveness of the search depends on how many
trigrams can be extracted from the pattern.  (The worst case, with no
trigrams, degrades to a full-table scan, so this isn't a panacea.  But
it can be very useful.)

Alexander Korotkov, reviewed by Jan Urbanski
This commit is contained in:
Tom Lane
2011-01-31 21:33:55 -05:00
parent 6238473adb
commit 6e2f3ae884
9 changed files with 639 additions and 49 deletions

View File

@ -195,31 +195,61 @@ gtrgm_consistent(PG_FUNCTION_ARGS)
TRGM *key = (TRGM *) DatumGetPointer(entry->key);
TRGM *qtrg;
bool res;
char *cache = (char *) fcinfo->flinfo->fn_extra;
char *cache = (char *) fcinfo->flinfo->fn_extra,
*cacheContents = cache + MAXALIGN(sizeof(StrategyNumber));
/* All cases served by this function are exact */
*recheck = false;
if (cache == NULL || VARSIZE(cache) != VARSIZE(query) || memcmp(cache, query, VARSIZE(query)) != 0)
/*
* Store both the strategy number and extracted trigrams in cache, because
* trigram extraction is relatively CPU-expensive. We must include
* strategy number because trigram extraction depends on strategy.
*/
if (cache == NULL || strategy != *((StrategyNumber *) cache) ||
VARSIZE(cacheContents) != VARSIZE(query) ||
memcmp(cacheContents, query, VARSIZE(query)) != 0)
{
qtrg = generate_trgm(VARDATA(query), VARSIZE(query) - VARHDRSZ);
switch (strategy)
{
case SimilarityStrategyNumber:
qtrg = generate_trgm(VARDATA(query), VARSIZE(query) - VARHDRSZ);
break;
case ILikeStrategyNumber:
#ifndef IGNORECASE
elog(ERROR, "cannot handle ~~* with case-sensitive trigrams");
#endif
/* FALL THRU */
case LikeStrategyNumber:
qtrg = generate_wildcard_trgm(VARDATA(query), VARSIZE(query) - VARHDRSZ);
break;
default:
elog(ERROR, "unrecognized strategy number: %d", strategy);
qtrg = NULL; /* keep compiler quiet */
break;
}
if (cache)
pfree(cache);
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
MAXALIGN(VARSIZE(query)) + VARSIZE(qtrg));
fcinfo->flinfo->fn_extra =
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
MAXALIGN(sizeof(StrategyNumber)) +
MAXALIGN(VARSIZE(query)) +
VARSIZE(qtrg));
cache = (char *) fcinfo->flinfo->fn_extra;
cacheContents = cache + MAXALIGN(sizeof(StrategyNumber));
memcpy(cache, query, VARSIZE(query));
memcpy(cache + MAXALIGN(VARSIZE(query)), qtrg, VARSIZE(qtrg));
*((StrategyNumber *) cache) = strategy;
memcpy(cacheContents, query, VARSIZE(query));
memcpy(cacheContents + MAXALIGN(VARSIZE(query)), qtrg, VARSIZE(qtrg));
}
qtrg = (TRGM *) (cache + MAXALIGN(VARSIZE(query)));
qtrg = (TRGM *) (cacheContents + MAXALIGN(VARSIZE(query)));
switch (strategy)
{
case SimilarityStrategyNumber:
/* Similarity search is exact */
*recheck = false;
if (GIST_LEAF(entry))
{ /* all leafs contains orig trgm */
float4 tmpsml = cnt_sml(key, qtrg);
@ -242,6 +272,47 @@ gtrgm_consistent(PG_FUNCTION_ARGS)
res = (((((float8) count) / ((float8) len))) >= trgm_limit) ? true : false;
}
break;
case ILikeStrategyNumber:
#ifndef IGNORECASE
elog(ERROR, "cannot handle ~~* with case-sensitive trigrams");
#endif
/* FALL THRU */
case LikeStrategyNumber:
/* Wildcard search is inexact */
*recheck = true;
/*
* Check if all the extracted trigrams can be present in child
* nodes.
*/
if (GIST_LEAF(entry))
{ /* all leafs contains orig trgm */
res = trgm_contained_by(qtrg, key);
}
else if (ISALLTRUE(key))
{ /* non-leaf contains signature */
res = true;
}
else
{ /* non-leaf contains signature */
int32 k,
tmp = 0,
len = ARRNELEM(qtrg);
trgm *ptr = GETARR(qtrg);
BITVECP sign = GETSIGN(key);
res = true;
for (k = 0; k < len; k++)
{
CPTRGM(((char *) &tmp), ptr + k);
if (!GETBIT(sign, HASHVAL(tmp)))
{
res = false;
break;
}
}
}
break;
default:
elog(ERROR, "unrecognized strategy number: %d", strategy);
res = false; /* keep compiler quiet */