1 /*-------------------------------------------------------------------------
4 * GIN support functions for tsvector_ops
6 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
10 * src/backend/utils/adt/tsginidx.c
12 *-------------------------------------------------------------------------
16 #include "access/gin.h"
17 #include "access/stratnum.h"
18 #include "miscadmin.h"
19 #include "tsearch/ts_type.h"
20 #include "tsearch/ts_utils.h"
21 #include "utils/builtins.h"
25 gin_cmp_tslexeme(PG_FUNCTION_ARGS)
27 text *a = PG_GETARG_TEXT_PP(0);
28 text *b = PG_GETARG_TEXT_PP(1);
31 cmp = tsCompareString(VARDATA_ANY(a), VARSIZE_ANY_EXHDR(a),
32 VARDATA_ANY(b), VARSIZE_ANY_EXHDR(b),
35 PG_FREE_IF_COPY(a, 0);
36 PG_FREE_IF_COPY(b, 1);
41 gin_cmp_prefix(PG_FUNCTION_ARGS)
43 text *a = PG_GETARG_TEXT_PP(0);
44 text *b = PG_GETARG_TEXT_PP(1);
47 StrategyNumber strategy = PG_GETARG_UINT16(2);
48 Pointer extra_data = PG_GETARG_POINTER(3);
52 cmp = tsCompareString(VARDATA_ANY(a), VARSIZE_ANY_EXHDR(a),
53 VARDATA_ANY(b), VARSIZE_ANY_EXHDR(b),
57 cmp = 1; /* prevent continue scan */
59 PG_FREE_IF_COPY(a, 0);
60 PG_FREE_IF_COPY(b, 1);
65 gin_extract_tsvector(PG_FUNCTION_ARGS)
67 TSVector vector = PG_GETARG_TSVECTOR(0);
68 int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
69 Datum *entries = NULL;
71 *nentries = vector->size;
75 WordEntry *we = ARRPTR(vector);
77 entries = (Datum *) palloc(sizeof(Datum) * vector->size);
79 for (i = 0; i < vector->size; i++)
83 txt = cstring_to_text_with_len(STRPTR(vector) + we->pos, we->len);
84 entries[i] = PointerGetDatum(txt);
90 PG_FREE_IF_COPY(vector, 0);
91 PG_RETURN_POINTER(entries);
95 gin_extract_tsquery(PG_FUNCTION_ARGS)
97 TSQuery query = PG_GETARG_TSQUERY(0);
98 int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
100 /* StrategyNumber strategy = PG_GETARG_UINT16(2); */
101 bool **ptr_partialmatch = (bool **) PG_GETARG_POINTER(3);
102 Pointer **extra_data = (Pointer **) PG_GETARG_POINTER(4);
104 /* bool **nullFlags = (bool **) PG_GETARG_POINTER(5); */
105 int32 *searchMode = (int32 *) PG_GETARG_POINTER(6);
106 Datum *entries = NULL;
112 QueryItem *item = GETQUERY(query);
116 int *map_item_operand;
119 * If the query doesn't have any required positive matches (for
120 * instance, it's something like '! foo'), we have to do a full index
123 if (tsquery_requires_match(item))
124 *searchMode = GIN_SEARCH_MODE_DEFAULT;
126 *searchMode = GIN_SEARCH_MODE_ALL;
128 /* count number of VAL items */
130 for (i = 0; i < query->size; i++)
132 if (item[i].type == QI_VAL)
137 entries = (Datum *) palloc(sizeof(Datum) * j);
138 partialmatch = *ptr_partialmatch = (bool *) palloc(sizeof(bool) * j);
141 * Make map to convert item's number to corresponding operand's (the
142 * same, entry's) number. Entry's number is used in check array in
143 * consistent method. We use the same map for each entry.
145 *extra_data = (Pointer *) palloc(sizeof(Pointer) * j);
146 map_item_operand = (int *) palloc0(sizeof(int) * query->size);
148 /* Now rescan the VAL items and fill in the arrays */
150 for (i = 0; i < query->size; i++)
152 if (item[i].type == QI_VAL)
154 QueryOperand *val = &item[i].qoperand;
157 txt = cstring_to_text_with_len(GETOPERAND(query) + val->distance,
159 entries[j] = PointerGetDatum(txt);
160 partialmatch[j] = val->prefix;
161 (*extra_data)[j] = (Pointer) map_item_operand;
162 map_item_operand[i] = j;
168 PG_FREE_IF_COPY(query, 0);
170 PG_RETURN_POINTER(entries);
175 QueryItem *first_item;
176 GinTernaryValue *check;
177 int *map_item_operand;
181 static GinTernaryValue
182 checkcondition_gin_internal(GinChkVal *gcv, QueryOperand *val, ExecPhraseData *data)
187 * if any val requiring a weight is used or caller needs position
188 * information then set recheck flag
190 if (val->weight != 0 || data != NULL)
191 *(gcv->need_recheck) = true;
193 /* convert item's number to corresponding entry's (operand's) number */
194 j = gcv->map_item_operand[((QueryItem *) val) - gcv->first_item];
196 /* return presence of current entry in indexed value */
197 return gcv->check[j];
201 * Wrapper of check condition function for TS_execute.
204 checkcondition_gin(void *checkval, QueryOperand *val, ExecPhraseData *data)
206 return checkcondition_gin_internal((GinChkVal *) checkval,
212 * Evaluate tsquery boolean expression using ternary logic.
214 static GinTernaryValue
215 TS_execute_ternary(GinChkVal *gcv, QueryItem *curitem, bool in_phrase)
217 GinTernaryValue val1,
221 /* since this function recurses, it could be driven to stack overflow */
224 if (curitem->type == QI_VAL)
226 checkcondition_gin_internal(gcv,
227 (QueryOperand *) curitem,
228 NULL /* don't have position info */ );
230 switch (curitem->qoperator.oper)
233 /* In phrase search, always return MAYBE since we lack positions */
236 result = TS_execute_ternary(gcv, curitem + 1, in_phrase);
237 if (result == GIN_MAYBE)
244 * GIN doesn't contain any information about positions, so treat
245 * OP_PHRASE as OP_AND with recheck requirement
247 *(gcv->need_recheck) = true;
248 /* Pass down in_phrase == true in case there's a NOT below */
254 val1 = TS_execute_ternary(gcv, curitem + curitem->qoperator.left,
256 if (val1 == GIN_FALSE)
258 val2 = TS_execute_ternary(gcv, curitem + 1, in_phrase);
259 if (val2 == GIN_FALSE)
261 if (val1 == GIN_TRUE && val2 == GIN_TRUE)
267 val1 = TS_execute_ternary(gcv, curitem + curitem->qoperator.left,
269 if (val1 == GIN_TRUE)
271 val2 = TS_execute_ternary(gcv, curitem + 1, in_phrase);
272 if (val2 == GIN_TRUE)
274 if (val1 == GIN_FALSE && val2 == GIN_FALSE)
280 elog(ERROR, "unrecognized operator: %d", curitem->qoperator.oper);
283 /* not reachable, but keep compiler quiet */
288 gin_tsquery_consistent(PG_FUNCTION_ARGS)
290 bool *check = (bool *) PG_GETARG_POINTER(0);
292 /* StrategyNumber strategy = PG_GETARG_UINT16(1); */
293 TSQuery query = PG_GETARG_TSQUERY(2);
295 /* int32 nkeys = PG_GETARG_INT32(3); */
296 Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4);
297 bool *recheck = (bool *) PG_GETARG_POINTER(5);
300 /* Initially assume query doesn't require recheck */
308 * check-parameter array has one entry for each value (operand) in the
311 gcv.first_item = GETQUERY(query);
312 StaticAssertStmt(sizeof(GinTernaryValue) == sizeof(bool),
313 "sizes of GinTernaryValue and bool are not equal");
314 gcv.check = (GinTernaryValue *) check;
315 gcv.map_item_operand = (int *) (extra_data[0]);
316 gcv.need_recheck = recheck;
318 res = TS_execute(GETQUERY(query),
320 TS_EXEC_CALC_NOT | TS_EXEC_PHRASE_NO_POS,
328 gin_tsquery_triconsistent(PG_FUNCTION_ARGS)
330 GinTernaryValue *check = (GinTernaryValue *) PG_GETARG_POINTER(0);
332 /* StrategyNumber strategy = PG_GETARG_UINT16(1); */
333 TSQuery query = PG_GETARG_TSQUERY(2);
335 /* int32 nkeys = PG_GETARG_INT32(3); */
336 Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4);
337 GinTernaryValue res = GIN_FALSE;
340 /* Initially assume query doesn't require recheck */
348 * check-parameter array has one entry for each value (operand) in the
351 gcv.first_item = GETQUERY(query);
353 gcv.map_item_operand = (int *) (extra_data[0]);
354 gcv.need_recheck = &recheck;
356 res = TS_execute_ternary(&gcv, GETQUERY(query), false);
358 if (res == GIN_TRUE && recheck)
362 PG_RETURN_GIN_TERNARY_VALUE(res);
366 * Formerly, gin_extract_tsvector had only two arguments. Now it has three,
367 * but we still need a pg_proc entry with two args to support reloading
368 * pre-9.1 contrib/tsearch2 opclass declarations. This compatibility
369 * function should go away eventually. (Note: you might say "hey, but the
370 * code above is only *using* two args, so let's just declare it that way".
371 * If you try that you'll find the opr_sanity regression test complains.)
374 gin_extract_tsvector_2args(PG_FUNCTION_ARGS)
376 if (PG_NARGS() < 3) /* should not happen */
377 elog(ERROR, "gin_extract_tsvector requires three arguments");
378 return gin_extract_tsvector(fcinfo);
382 * Likewise, we need a stub version of gin_extract_tsquery declared with
383 * only five arguments.
386 gin_extract_tsquery_5args(PG_FUNCTION_ARGS)
388 if (PG_NARGS() < 7) /* should not happen */
389 elog(ERROR, "gin_extract_tsquery requires seven arguments");
390 return gin_extract_tsquery(fcinfo);
394 * Likewise, we need a stub version of gin_tsquery_consistent declared with
395 * only six arguments.
398 gin_tsquery_consistent_6args(PG_FUNCTION_ARGS)
400 if (PG_NARGS() < 8) /* should not happen */
401 elog(ERROR, "gin_tsquery_consistent requires eight arguments");
402 return gin_tsquery_consistent(fcinfo);
406 * Likewise, a stub version of gin_extract_tsquery declared with argument
407 * types that are no longer considered appropriate.
410 gin_extract_tsquery_oldsig(PG_FUNCTION_ARGS)
412 return gin_extract_tsquery(fcinfo);
416 * Likewise, a stub version of gin_tsquery_consistent declared with argument
417 * types that are no longer considered appropriate.
420 gin_tsquery_consistent_oldsig(PG_FUNCTION_ARGS)
422 return gin_tsquery_consistent(fcinfo);