Coverage Report

Created: 2025-06-15 06:31

/src/postgres/src/backend/access/gin/ginlogic.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * ginlogic.c
4
 *    routines for performing binary- and ternary-logic consistent checks.
5
 *
6
 * A GIN operator class can provide a boolean or ternary consistent
7
 * function, or both.  This file provides both boolean and ternary
8
 * interfaces to the rest of the GIN code, even if only one of them is
9
 * implemented by the opclass.
10
 *
11
 * Providing a boolean interface when the opclass implements only the
12
 * ternary function is straightforward - just call the ternary function
13
 * with the check-array as is, and map the GIN_TRUE, GIN_FALSE, GIN_MAYBE
14
 * return codes to TRUE, FALSE and TRUE+recheck, respectively.  Providing
15
 * a ternary interface when the opclass only implements a boolean function
16
 * is implemented by calling the boolean function many times, with all the
17
 * MAYBE arguments set to all combinations of TRUE and FALSE (up to a
18
 * certain number of MAYBE arguments).
19
 *
20
 * (A boolean function is enough to determine if an item matches, but a
21
 * GIN scan can apply various optimizations if it can determine that an
22
 * item matches or doesn't match, even if it doesn't know if some of the
23
 * keys are present or not.  That's what the ternary consistent function
24
 * is used for.)
25
 *
26
 *
27
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
28
 * Portions Copyright (c) 1994, Regents of the University of California
29
 *
30
 * IDENTIFICATION
31
 *      src/backend/access/gin/ginlogic.c
32
 *-------------------------------------------------------------------------
33
 */
34
35
#include "postgres.h"
36
37
#include "access/gin_private.h"
38
39
40
/*
41
 * Maximum number of MAYBE inputs that shimTriConsistentFn will try to
42
 * resolve by calling all combinations.
43
 */
44
0
#define MAX_MAYBE_ENTRIES 4
45
46
/*
47
 * Dummy consistent functions for an EVERYTHING key.  Just claim it matches.
48
 */
49
static bool
50
trueConsistentFn(GinScanKey key)
51
0
{
52
0
  key->recheckCurItem = false;
53
0
  return true;
54
0
}
55
static GinTernaryValue
56
trueTriConsistentFn(GinScanKey key)
57
0
{
58
0
  return GIN_TRUE;
59
0
}
60
61
/*
62
 * A helper function for calling a regular, binary logic, consistent function.
63
 */
64
static bool
65
directBoolConsistentFn(GinScanKey key)
66
0
{
67
  /*
68
   * Initialize recheckCurItem in case the consistentFn doesn't know it
69
   * should set it.  The safe assumption in that case is to force recheck.
70
   */
71
0
  key->recheckCurItem = true;
72
73
0
  return DatumGetBool(FunctionCall8Coll(key->consistentFmgrInfo,
74
0
                      key->collation,
75
0
                      PointerGetDatum(key->entryRes),
76
0
                      UInt16GetDatum(key->strategy),
77
0
                      key->query,
78
0
                      UInt32GetDatum(key->nuserentries),
79
0
                      PointerGetDatum(key->extra_data),
80
0
                      PointerGetDatum(&key->recheckCurItem),
81
0
                      PointerGetDatum(key->queryValues),
82
0
                      PointerGetDatum(key->queryCategories)));
83
0
}
84
85
/*
86
 * A helper function for calling a native ternary logic consistent function.
87
 */
88
static GinTernaryValue
89
directTriConsistentFn(GinScanKey key)
90
0
{
91
0
  return DatumGetGinTernaryValue(FunctionCall7Coll(key->triConsistentFmgrInfo,
92
0
                           key->collation,
93
0
                           PointerGetDatum(key->entryRes),
94
0
                           UInt16GetDatum(key->strategy),
95
0
                           key->query,
96
0
                           UInt32GetDatum(key->nuserentries),
97
0
                           PointerGetDatum(key->extra_data),
98
0
                           PointerGetDatum(key->queryValues),
99
0
                           PointerGetDatum(key->queryCategories)));
100
0
}
101
102
/*
103
 * This function implements a binary logic consistency check, using a ternary
104
 * logic consistent function provided by the opclass. GIN_MAYBE return value
105
 * is interpreted as true with recheck flag.
106
 */
107
static bool
108
shimBoolConsistentFn(GinScanKey key)
109
0
{
110
0
  GinTernaryValue result;
111
112
0
  result = DatumGetGinTernaryValue(FunctionCall7Coll(key->triConsistentFmgrInfo,
113
0
                             key->collation,
114
0
                             PointerGetDatum(key->entryRes),
115
0
                             UInt16GetDatum(key->strategy),
116
0
                             key->query,
117
0
                             UInt32GetDatum(key->nuserentries),
118
0
                             PointerGetDatum(key->extra_data),
119
0
                             PointerGetDatum(key->queryValues),
120
0
                             PointerGetDatum(key->queryCategories)));
121
0
  if (result == GIN_MAYBE)
122
0
  {
123
0
    key->recheckCurItem = true;
124
0
    return true;
125
0
  }
126
0
  else
127
0
  {
128
0
    key->recheckCurItem = false;
129
0
    return result;
130
0
  }
131
0
}
132
133
/*
134
 * This function implements a tri-state consistency check, using a boolean
135
 * consistent function provided by the opclass.
136
 *
137
 * Our strategy is to call consistentFn with MAYBE inputs replaced with every
138
 * combination of TRUE/FALSE. If consistentFn returns the same value for every
139
 * combination, that's the overall result. Otherwise, return MAYBE. Testing
140
 * every combination is O(n^2), so this is only feasible for a small number of
141
 * MAYBE inputs.
142
 *
143
 * NB: This function modifies the key->entryRes array.  For now that's okay
144
 * so long as we restore the entry-time contents before returning.  This may
145
 * need revisiting if we ever invent multithreaded GIN scans, though.
146
 */
147
static GinTernaryValue
148
shimTriConsistentFn(GinScanKey key)
149
0
{
150
0
  int     nmaybe;
151
0
  int     maybeEntries[MAX_MAYBE_ENTRIES];
152
0
  int     i;
153
0
  bool    boolResult;
154
0
  bool    recheck;
155
0
  GinTernaryValue curResult;
156
157
  /*
158
   * Count how many MAYBE inputs there are, and store their indexes in
159
   * maybeEntries. If there are too many MAYBE inputs, it's not feasible to
160
   * test all combinations, so give up and return MAYBE.
161
   */
162
0
  nmaybe = 0;
163
0
  for (i = 0; i < key->nentries; i++)
164
0
  {
165
0
    if (key->entryRes[i] == GIN_MAYBE)
166
0
    {
167
0
      if (nmaybe >= MAX_MAYBE_ENTRIES)
168
0
        return GIN_MAYBE;
169
0
      maybeEntries[nmaybe++] = i;
170
0
    }
171
0
  }
172
173
  /*
174
   * If none of the inputs were MAYBE, we can just call the consistent
175
   * function as-is.
176
   */
177
0
  if (nmaybe == 0)
178
0
    return directBoolConsistentFn(key);
179
180
  /* First call consistent function with all the maybe-inputs set FALSE */
181
0
  for (i = 0; i < nmaybe; i++)
182
0
    key->entryRes[maybeEntries[i]] = GIN_FALSE;
183
0
  curResult = directBoolConsistentFn(key);
184
0
  recheck = key->recheckCurItem;
185
186
0
  for (;;)
187
0
  {
188
    /* Twiddle the entries for next combination. */
189
0
    for (i = 0; i < nmaybe; i++)
190
0
    {
191
0
      if (key->entryRes[maybeEntries[i]] == GIN_FALSE)
192
0
      {
193
0
        key->entryRes[maybeEntries[i]] = GIN_TRUE;
194
0
        break;
195
0
      }
196
0
      else
197
0
        key->entryRes[maybeEntries[i]] = GIN_FALSE;
198
0
    }
199
0
    if (i == nmaybe)
200
0
      break;
201
202
0
    boolResult = directBoolConsistentFn(key);
203
0
    recheck |= key->recheckCurItem;
204
205
0
    if (curResult != boolResult)
206
0
    {
207
0
      curResult = GIN_MAYBE;
208
0
      break;
209
0
    }
210
0
  }
211
212
  /* TRUE with recheck is taken to mean MAYBE */
213
0
  if (curResult == GIN_TRUE && recheck)
214
0
    curResult = GIN_MAYBE;
215
216
  /* We must restore the original state of the entryRes array */
217
0
  for (i = 0; i < nmaybe; i++)
218
0
    key->entryRes[maybeEntries[i]] = GIN_MAYBE;
219
220
0
  return curResult;
221
0
}
222
223
/*
224
 * Set up the implementation of the consistent functions for a scan key.
225
 */
226
void
227
ginInitConsistentFunction(GinState *ginstate, GinScanKey key)
228
0
{
229
0
  if (key->searchMode == GIN_SEARCH_MODE_EVERYTHING)
230
0
  {
231
0
    key->boolConsistentFn = trueConsistentFn;
232
0
    key->triConsistentFn = trueTriConsistentFn;
233
0
  }
234
0
  else
235
0
  {
236
0
    key->consistentFmgrInfo = &ginstate->consistentFn[key->attnum - 1];
237
0
    key->triConsistentFmgrInfo = &ginstate->triConsistentFn[key->attnum - 1];
238
0
    key->collation = ginstate->supportCollation[key->attnum - 1];
239
240
0
    if (OidIsValid(ginstate->consistentFn[key->attnum - 1].fn_oid))
241
0
      key->boolConsistentFn = directBoolConsistentFn;
242
0
    else
243
0
      key->boolConsistentFn = shimBoolConsistentFn;
244
245
0
    if (OidIsValid(ginstate->triConsistentFn[key->attnum - 1].fn_oid))
246
0
      key->triConsistentFn = directTriConsistentFn;
247
0
    else
248
0
      key->triConsistentFn = shimTriConsistentFn;
249
0
  }
250
0
}