Commit 22926e00 authored by Heikki Linnakangas's avatar Heikki Linnakangas

Fix two bugs in tsquery @> operator.

1. The comparison for matching terms used only the CRC to decide if there's
a match. Two different terms with the same CRC gave a match.

2. It assumed that if the second operand has more terms than the first, it's
never a match. That assumption is bogus, because there can be duplicate
terms in either operand.

Rewrite the implementation in a way that doesn't have those bugs.

Backpatch to all supported versions.
parent a4da35a0
...@@ -213,63 +213,112 @@ makeTSQuerySign(TSQuery a) ...@@ -213,63 +213,112 @@ makeTSQuerySign(TSQuery a)
return sign; return sign;
} }
Datum static char **
tsq_mcontains(PG_FUNCTION_ARGS) collectTSQueryValues(TSQuery a, int *nvalues_p)
{ {
TSQuery query = PG_GETARG_TSQUERY(0); QueryItem *ptr = GETQUERY(a);
TSQuery ex = PG_GETARG_TSQUERY(1); char *operand = GETOPERAND(a);
TSQuerySign sq, char **values;
se; int nvalues = 0;
int i, int i;
j;
QueryItem *iq, values = (char **) palloc(sizeof(char *) * a->size);
*ie;
for (i = 0; i < a->size; i++)
if (query->size < ex->size)
{ {
PG_FREE_IF_COPY(query, 0); if (ptr->type == QI_VAL)
PG_FREE_IF_COPY(ex, 1); {
int len = ptr->qoperand.length;
char *val;
val = palloc(len + 1);
memcpy(val, operand + ptr->qoperand.distance, len);
val[len] = '\0';
PG_RETURN_BOOL(false); values[nvalues++] = val;
} }
ptr++;
}
*nvalues_p = nvalues;
return values;
}
sq = makeTSQuerySign(query); static int
se = makeTSQuerySign(ex); cmp_string(const void *a, const void *b)
{
const char *sa = *((const char **) a);
const char *sb = *((const char **) b);
return strcmp(sa, sb);
}
if ((sq & se) != se) static int
remove_duplicates(char **strings, int n)
{
if (n <= 1)
return n;
else
{ {
PG_FREE_IF_COPY(query, 0); int i;
PG_FREE_IF_COPY(ex, 1); char *prev = strings[0];
int new_n = 1;
PG_RETURN_BOOL(false); for (i = 1; i < n; i++)
{
if (strcmp(strings[i], prev) != 0)
{
strings[new_n++] = strings[i];
prev = strings[i];
}
} }
return new_n;
}
}
iq = GETQUERY(query); Datum
ie = GETQUERY(ex); tsq_mcontains(PG_FUNCTION_ARGS)
{
TSQuery query = PG_GETARG_TSQUERY(0);
TSQuery ex = PG_GETARG_TSQUERY(1);
char **query_values;
int query_nvalues;
char **ex_values;
int ex_nvalues;
bool result = true;
/* Extract the query terms into arrays */
query_values = collectTSQueryValues(query, &query_nvalues);
ex_values = collectTSQueryValues(ex, &ex_nvalues);
/* Sort and remove duplicates from both arrays */
qsort(query_values, query_nvalues, sizeof(char *), cmp_string);
query_nvalues = remove_duplicates(query_values, query_nvalues);
qsort(ex_values, ex_nvalues, sizeof(char *), cmp_string);
ex_nvalues = remove_duplicates(ex_values, ex_nvalues);
if (ex_nvalues > query_nvalues)
result = false;
else
{
int i;
int j = 0;
for (i = 0; i < ex->size; i++) for (i = 0; i < ex_nvalues; i++)
{ {
if (ie[i].type != QI_VAL) for (; j < query_nvalues; j++)
continue;
for (j = 0; j < query->size; j++)
{ {
if (iq[j].type == QI_VAL && if (strcmp(ex_values[i], query_values[j]) == 0)
ie[i].qoperand.valcrc == iq[j].qoperand.valcrc)
break; break;
} }
if (j >= query->size) if (j == query_nvalues)
{ {
PG_FREE_IF_COPY(query, 0); result = false;
PG_FREE_IF_COPY(ex, 1); break;
}
PG_RETURN_BOOL(false);
} }
} }
PG_FREE_IF_COPY(query, 0); PG_RETURN_BOOL(result);
PG_FREE_IF_COPY(ex, 1);
PG_RETURN_BOOL(true);
} }
Datum Datum
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment