Skip to content

Commit 22926e0

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 a4da35a commit 22926e0

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
@@ -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)
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