Line data Source code
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 : #define MAX_MAYBE_ENTRIES 4
45 :
46 : /*
47 : * Dummy consistent functions for an EVERYTHING key. Just claim it matches.
48 : */
49 : static bool
50 0 : trueConsistentFn(GinScanKey key)
51 : {
52 0 : key->recheckCurItem = false;
53 0 : return true;
54 : }
55 : static GinTernaryValue
56 0 : trueTriConsistentFn(GinScanKey key)
57 : {
58 0 : return GIN_TRUE;
59 : }
60 :
61 : /*
62 : * A helper function for calling a regular, binary logic, consistent function.
63 : */
64 : static bool
65 37486 : directBoolConsistentFn(GinScanKey key)
66 : {
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 37486 : key->recheckCurItem = true;
72 :
73 74972 : return DatumGetBool(FunctionCall8Coll(key->consistentFmgrInfo,
74 : key->collation,
75 37486 : PointerGetDatum(key->entryRes),
76 37486 : UInt16GetDatum(key->strategy),
77 : key->query,
78 : UInt32GetDatum(key->nuserentries),
79 37486 : PointerGetDatum(key->extra_data),
80 37486 : PointerGetDatum(&key->recheckCurItem),
81 37486 : PointerGetDatum(key->queryValues),
82 37486 : PointerGetDatum(key->queryCategories)));
83 : }
84 :
85 : /*
86 : * A helper function for calling a native ternary logic consistent function.
87 : */
88 : static GinTernaryValue
89 969098 : directTriConsistentFn(GinScanKey key)
90 : {
91 1938196 : return DatumGetGinTernaryValue(FunctionCall7Coll(key->triConsistentFmgrInfo,
92 : key->collation,
93 969098 : PointerGetDatum(key->entryRes),
94 969098 : UInt16GetDatum(key->strategy),
95 : key->query,
96 : UInt32GetDatum(key->nuserentries),
97 969098 : PointerGetDatum(key->extra_data),
98 969098 : PointerGetDatum(key->queryValues),
99 969098 : PointerGetDatum(key->queryCategories)));
100 : }
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 0 : shimBoolConsistentFn(GinScanKey key)
109 : {
110 : GinTernaryValue result;
111 :
112 0 : result = DatumGetGinTernaryValue(FunctionCall7Coll(key->triConsistentFmgrInfo,
113 : key->collation,
114 0 : PointerGetDatum(key->entryRes),
115 0 : UInt16GetDatum(key->strategy),
116 : key->query,
117 : 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 : {
123 0 : key->recheckCurItem = true;
124 0 : return true;
125 : }
126 : else
127 : {
128 0 : key->recheckCurItem = false;
129 0 : return result;
130 : }
131 : }
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 36540 : shimTriConsistentFn(GinScanKey key)
149 : {
150 : int nmaybe;
151 : int maybeEntries[MAX_MAYBE_ENTRIES];
152 : int i;
153 : bool boolResult;
154 : bool recheck;
155 : 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 36540 : nmaybe = 0;
163 141154 : for (i = 0; i < key->nentries; i++)
164 : {
165 104614 : if (key->entryRes[i] == GIN_MAYBE)
166 : {
167 76 : if (nmaybe >= MAX_MAYBE_ENTRIES)
168 0 : return GIN_MAYBE;
169 76 : maybeEntries[nmaybe++] = i;
170 : }
171 : }
172 :
173 : /*
174 : * If none of the inputs were MAYBE, we can just call the consistent
175 : * function as-is.
176 : */
177 36540 : if (nmaybe == 0)
178 36488 : return directBoolConsistentFn(key);
179 :
180 : /* First call consistent function with all the maybe-inputs set FALSE */
181 128 : for (i = 0; i < nmaybe; i++)
182 76 : key->entryRes[maybeEntries[i]] = GIN_FALSE;
183 52 : curResult = directBoolConsistentFn(key);
184 52 : recheck = key->recheckCurItem;
185 :
186 : for (;;)
187 : {
188 : /* Twiddle the entries for next combination. */
189 216 : for (i = 0; i < nmaybe; i++)
190 : {
191 176 : if (key->entryRes[maybeEntries[i]] == GIN_FALSE)
192 : {
193 96 : key->entryRes[maybeEntries[i]] = GIN_TRUE;
194 96 : break;
195 : }
196 : else
197 80 : key->entryRes[maybeEntries[i]] = GIN_FALSE;
198 : }
199 136 : if (i == nmaybe)
200 40 : break;
201 :
202 96 : boolResult = directBoolConsistentFn(key);
203 96 : recheck |= key->recheckCurItem;
204 :
205 96 : if (curResult != boolResult)
206 : {
207 12 : curResult = GIN_MAYBE;
208 12 : break;
209 : }
210 : }
211 :
212 : /* TRUE with recheck is taken to mean MAYBE */
213 52 : if (curResult == GIN_TRUE && recheck)
214 6 : curResult = GIN_MAYBE;
215 :
216 : /* We must restore the original state of the entryRes array */
217 128 : for (i = 0; i < nmaybe; i++)
218 76 : key->entryRes[maybeEntries[i]] = GIN_MAYBE;
219 :
220 52 : return curResult;
221 : }
222 :
223 : /*
224 : * Set up the implementation of the consistent functions for a scan key.
225 : */
226 : void
227 1716 : ginInitConsistentFunction(GinState *ginstate, GinScanKey key)
228 : {
229 1716 : if (key->searchMode == GIN_SEARCH_MODE_EVERYTHING)
230 : {
231 0 : key->boolConsistentFn = trueConsistentFn;
232 0 : key->triConsistentFn = trueTriConsistentFn;
233 : }
234 : else
235 : {
236 1716 : key->consistentFmgrInfo = &ginstate->consistentFn[key->attnum - 1];
237 1716 : key->triConsistentFmgrInfo = &ginstate->triConsistentFn[key->attnum - 1];
238 1716 : key->collation = ginstate->supportCollation[key->attnum - 1];
239 :
240 1716 : if (OidIsValid(ginstate->consistentFn[key->attnum - 1].fn_oid))
241 1716 : key->boolConsistentFn = directBoolConsistentFn;
242 : else
243 0 : key->boolConsistentFn = shimBoolConsistentFn;
244 :
245 1716 : if (OidIsValid(ginstate->triConsistentFn[key->attnum - 1].fn_oid))
246 1374 : key->triConsistentFn = directTriConsistentFn;
247 : else
248 342 : key->triConsistentFn = shimTriConsistentFn;
249 : }
250 1716 : }
|