Skip to content

Commit 4d1c738

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 94de3a6 commit 4d1c738

File tree

1 file changed

+90
-41
lines changed

1 file changed

+90
-41
lines changed

src/backend/utils/adt/tsquery_op.c

Lines changed: 90 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -216,63 +216,112 @@ makeTSQuerySign(TSQuery a)
216216
return sign;
217217
}
218218

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

236-
PG_RETURN_BOOL(false);
241+
values[nvalues++] = val;
242+
}
243+
ptr++;
237244
}
238245

239-
sq = makeTSQuerySign(query);
240-
se = makeTSQuerySign(ex);
246+
*nvalues_p = nvalues;
247+
return values;
248+
}
249+
250+
static int
251+
cmp_string(const void *a, const void *b)
252+
{
253+
const char *sa = *((const char **) a);
254+
const char *sb = *((const char **) b);
255+
return strcmp(sa, sb);
256+
}
241257

242-
if ((sq & se) != se)
258+
static int
259+
remove_duplicates(char **strings, int n)
260+
{
261+
if (n <= 1)
262+
return n;
263+
else
243264
{
244-
PG_FREE_IF_COPY(query, 0);
245-
PG_FREE_IF_COPY(ex, 1);
265+
int i;
266+
char *prev = strings[0];
267+
int new_n = 1;
246268

247-
PG_RETURN_BOOL(false);
269+
for (i = 1; i < n; i++)
270+
{
271+
if (strcmp(strings[i], prev) != 0)
272+
{
273+
strings[new_n++] = strings[i];
274+
prev = strings[i];
275+
}
276+
}
277+
return new_n;
248278
}
279+
}
249280

250-
iq = GETQUERY(query);
251-
ie = GETQUERY(ex);
252-
253-
for (i = 0; i < ex->size; i++)
281+
Datum
282+
tsq_mcontains(PG_FUNCTION_ARGS)
283+
{
284+
TSQuery query = PG_GETARG_TSQUERY(0);
285+
TSQuery ex = PG_GETARG_TSQUERY(1);
286+
char **query_values;
287+
int query_nvalues;
288+
char **ex_values;
289+
int ex_nvalues;
290+
bool result = true;
291+
292+
/* Extract the query terms into arrays */
293+
query_values = collectTSQueryValues(query, &query_nvalues);
294+
ex_values = collectTSQueryValues(ex, &ex_nvalues);
295+
296+
/* Sort and remove duplicates from both arrays */
297+
qsort(query_values, query_nvalues, sizeof(char *), cmp_string);
298+
query_nvalues = remove_duplicates(query_values, query_nvalues);
299+
qsort(ex_values, ex_nvalues, sizeof(char *), cmp_string);
300+
ex_nvalues = remove_duplicates(ex_values, ex_nvalues);
301+
302+
if (ex_nvalues > query_nvalues)
303+
result = false;
304+
else
254305
{
255-
if (ie[i].type != QI_VAL)
256-
continue;
257-
for (j = 0; j < query->size; j++)
306+
int i;
307+
int j = 0;
308+
309+
for (i = 0; i < ex_nvalues; i++)
258310
{
259-
if (iq[j].type == QI_VAL &&
260-
ie[i].qoperand.valcrc == iq[j].qoperand.valcrc)
311+
for (; j < query_nvalues; j++)
312+
{
313+
if (strcmp(ex_values[i], query_values[j]) == 0)
314+
break;
315+
}
316+
if (j == query_nvalues)
317+
{
318+
result = false;
261319
break;
262-
}
263-
if (j >= query->size)
264-
{
265-
PG_FREE_IF_COPY(query, 0);
266-
PG_FREE_IF_COPY(ex, 1);
267-
268-
PG_RETURN_BOOL(false);
320+
}
269321
}
270322
}
271323

272-
PG_FREE_IF_COPY(query, 0);
273-
PG_FREE_IF_COPY(ex, 1);
274-
275-
PG_RETURN_BOOL(true);
324+
PG_RETURN_BOOL(result);
276325
}
277326

278327
Datum

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy