Skip to content

Commit 1aa526f

Browse files
committed
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.
1 parent f6abf8f commit 1aa526f

File tree

1 file changed

+90
-41
lines changed

1 file changed

+90
-41
lines changed

src/backend/utils/adt/tsquery_op.c

+90-41
Original file line numberDiff line numberDiff line change
@@ -213,63 +213,112 @@ makeTSQuerySign(TSQuery a)
213213
return sign;
214214
}
215215

216-
Datum
217-
tsq_mcontains(PG_FUNCTION_ARGS)
216+
static char **
217+
collectTSQueryValues(TSQuery a, int *nvalues_p)
218218
{
219-
TSQuery query = PG_GETARG_TSQUERY(0);
220-
TSQuery ex = PG_GETARG_TSQUERY(1);
221-
TSQuerySign sq,
222-
se;
223-
int i,
224-
j;
225-
QueryItem *iq,
226-
*ie;
227-
228-
if (query->size < ex->size)
219+
QueryItem *ptr = GETQUERY(a);
220+
char *operand = GETOPERAND(a);
221+
char **values;
222+
int nvalues = 0;
223+
int i;
224+
225+
values = (char **) palloc(sizeof(char *) * a->size);
226+
227+
for (i = 0; i < a->size; i++)
229228
{
230-
PG_FREE_IF_COPY(query, 0);
231-
PG_FREE_IF_COPY(ex, 1);
229+
if (ptr->type == QI_VAL)
230+
{
231+
int len = ptr->qoperand.length;
232+
char *val;
233+
234+
val = palloc(len + 1);
235+
memcpy(val, operand + ptr->qoperand.distance, len);
236+
val[len] = '\0';
232237

233-
PG_RETURN_BOOL(false);
238+
values[nvalues++] = val;
239+
}
240+
ptr++;
234241
}
235242

236-
sq = makeTSQuerySign(query);
237-
se = makeTSQuerySign(ex);
243+
*nvalues_p = nvalues;
244+
return values;
245+
}
246+
247+
static int
248+
cmp_string(const void *a, const void *b)
249+
{
250+
const char *sa = *((const char **) a);
251+
const char *sb = *((const char **) b);
252+
return strcmp(sa, sb);
253+
}
238254

239-
if ((sq & se) != se)
255+
static int
256+
remove_duplicates(char **strings, int n)
257+
{
258+
if (n <= 1)
259+
return n;
260+
else
240261
{
241-
PG_FREE_IF_COPY(query, 0);
242-
PG_FREE_IF_COPY(ex, 1);
262+
int i;
263+
char *prev = strings[0];
264+
int new_n = 1;
243265

244-
PG_RETURN_BOOL(false);
266+
for (i = 1; i < n; i++)
267+
{
268+
if (strcmp(strings[i], prev) != 0)
269+
{
270+
strings[new_n++] = strings[i];
271+
prev = strings[i];
272+
}
273+
}
274+
return new_n;
245275
}
276+
}
246277

247-
iq = GETQUERY(query);
248-
ie = GETQUERY(ex);
249-
250-
for (i = 0; i < ex->size; i++)
278+
Datum
279+
tsq_mcontains(PG_FUNCTION_ARGS)
280+
{
281+
TSQuery query = PG_GETARG_TSQUERY(0);
282+
TSQuery ex = PG_GETARG_TSQUERY(1);
283+
char **query_values;
284+
int query_nvalues;
285+
char **ex_values;
286+
int ex_nvalues;
287+
bool result = true;
288+
289+
/* Extract the query terms into arrays */
290+
query_values = collectTSQueryValues(query, &query_nvalues);
291+
ex_values = collectTSQueryValues(ex, &ex_nvalues);
292+
293+
/* Sort and remove duplicates from both arrays */
294+
qsort(query_values, query_nvalues, sizeof(char *), cmp_string);
295+
query_nvalues = remove_duplicates(query_values, query_nvalues);
296+
qsort(ex_values, ex_nvalues, sizeof(char *), cmp_string);
297+
ex_nvalues = remove_duplicates(ex_values, ex_nvalues);
298+
299+
if (ex_nvalues > query_nvalues)
300+
result = false;
301+
else
251302
{
252-
if (ie[i].type != QI_VAL)
253-
continue;
254-
for (j = 0; j < query->size; j++)
303+
int i;
304+
int j = 0;
305+
306+
for (i = 0; i < ex_nvalues; i++)
255307
{
256-
if (iq[j].type == QI_VAL &&
257-
ie[i].qoperand.valcrc == iq[j].qoperand.valcrc)
308+
for (; j < query_nvalues; j++)
309+
{
310+
if (strcmp(ex_values[i], query_values[j]) == 0)
311+
break;
312+
}
313+
if (j == query_nvalues)
314+
{
315+
result = false;
258316
break;
259-
}
260-
if (j >= query->size)
261-
{
262-
PG_FREE_IF_COPY(query, 0);
263-
PG_FREE_IF_COPY(ex, 1);
264-
265-
PG_RETURN_BOOL(false);
317+
}
266318
}
267319
}
268320

269-
PG_FREE_IF_COPY(query, 0);
270-
PG_FREE_IF_COPY(ex, 1);
271-
272-
PG_RETURN_BOOL(true);
321+
PG_RETURN_BOOL(result);
273322
}
274323

275324
Datum

0 commit comments

Comments
 (0)