Age Owner Branch data TLA 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
4229 heikki.linnakangas@i 50 :UBC 0 : trueConsistentFn(GinScanKey key)
51 : : {
52 : 0 : key->recheckCurItem = false;
53 : 0 : return true;
54 : : }
55 : : static GinTernaryValue
56 : 0 : trueTriConsistentFn(GinScanKey key)
57 : : {
4191 58 : 0 : return GIN_TRUE;
59 : : }
60 : :
61 : : /*
62 : : * A helper function for calling a regular, binary logic, consistent function.
63 : : */
64 : : static bool
4196 heikki.linnakangas@i 65 :CBC 19278 : 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 : : */
4229 71 : 19278 : key->recheckCurItem = true;
72 : :
73 : 38556 : return DatumGetBool(FunctionCall8Coll(key->consistentFmgrInfo,
74 : : key->collation,
75 : 19278 : PointerGetDatum(key->entryRes),
76 : 19278 : UInt16GetDatum(key->strategy),
77 : : key->query,
78 : : UInt32GetDatum(key->nuserentries),
79 : 19278 : PointerGetDatum(key->extra_data),
2999 tgl@sss.pgh.pa.us 80 : 19278 : PointerGetDatum(&key->recheckCurItem),
4229 heikki.linnakangas@i 81 : 19278 : PointerGetDatum(key->queryValues),
2999 tgl@sss.pgh.pa.us 82 : 19278 : PointerGetDatum(key->queryCategories)));
83 : : }
84 : :
85 : : /*
86 : : * A helper function for calling a native ternary logic consistent function.
87 : : */
88 : : static GinTernaryValue
4196 heikki.linnakangas@i 89 : 484842 : directTriConsistentFn(GinScanKey key)
90 : : {
2046 alvherre@alvh.no-ip. 91 : 969684 : return DatumGetGinTernaryValue(FunctionCall7Coll(key->triConsistentFmgrInfo,
92 : : key->collation,
2999 tgl@sss.pgh.pa.us 93 : 484842 : PointerGetDatum(key->entryRes),
94 : 484842 : UInt16GetDatum(key->strategy),
95 : : key->query,
96 : : UInt32GetDatum(key->nuserentries),
97 : 484842 : PointerGetDatum(key->extra_data),
98 : 484842 : PointerGetDatum(key->queryValues),
99 : 484842 : 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
4196 heikki.linnakangas@i 108 :UBC 0 : shimBoolConsistentFn(GinScanKey key)
109 : : {
110 : : GinTernaryValue result;
111 : :
2046 alvherre@alvh.no-ip. 112 : 0 : result = DatumGetGinTernaryValue(FunctionCall7Coll(key->triConsistentFmgrInfo,
113 : : key->collation,
2999 tgl@sss.pgh.pa.us 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)));
4196 heikki.linnakangas@i 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
4229 heikki.linnakangas@i 148 :CBC 18801 : 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 : 18801 : nmaybe = 0;
163 [ + + ]: 71639 : for (i = 0; i < key->nentries; i++)
164 : : {
165 [ + + ]: 52838 : if (key->entryRes[i] == GIN_MAYBE)
166 : : {
167 [ - + ]: 38 : if (nmaybe >= MAX_MAYBE_ENTRIES)
4229 heikki.linnakangas@i 168 :UBC 0 : return GIN_MAYBE;
4229 heikki.linnakangas@i 169 :CBC 38 : 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 [ + + ]: 18801 : if (nmaybe == 0)
4196 178 : 18775 : return directBoolConsistentFn(key);
179 : :
180 : : /* First call consistent function with all the maybe-inputs set FALSE */
4229 181 [ + + ]: 64 : for (i = 0; i < nmaybe; i++)
182 : 38 : key->entryRes[maybeEntries[i]] = GIN_FALSE;
4196 183 : 26 : curResult = directBoolConsistentFn(key);
147 tgl@sss.pgh.pa.us 184 : 26 : recheck = key->recheckCurItem;
185 : :
186 : : for (;;)
187 : : {
188 : : /* Twiddle the entries for next combination. */
4229 heikki.linnakangas@i 189 [ + + ]: 108 : for (i = 0; i < nmaybe; i++)
190 : : {
191 [ + + ]: 88 : if (key->entryRes[maybeEntries[i]] == GIN_FALSE)
192 : : {
193 : 48 : key->entryRes[maybeEntries[i]] = GIN_TRUE;
194 : 48 : break;
195 : : }
196 : : else
197 : 40 : key->entryRes[maybeEntries[i]] = GIN_FALSE;
198 : : }
199 [ + + ]: 68 : if (i == nmaybe)
200 : 20 : break;
201 : :
4196 202 : 48 : boolResult = directBoolConsistentFn(key);
4229 203 : 48 : recheck |= key->recheckCurItem;
204 : :
205 [ + + ]: 48 : if (curResult != boolResult)
206 : : {
147 tgl@sss.pgh.pa.us 207 : 6 : curResult = GIN_MAYBE;
208 : 6 : break;
209 : : }
210 : : }
211 : :
212 : : /* TRUE with recheck is taken to mean MAYBE */
4229 heikki.linnakangas@i 213 [ + + + + ]: 26 : if (curResult == GIN_TRUE && recheck)
214 : 3 : curResult = GIN_MAYBE;
215 : :
216 : : /* We must restore the original state of the entryRes array */
147 tgl@sss.pgh.pa.us 217 [ + + ]: 64 : for (i = 0; i < nmaybe; i++)
218 : 38 : key->entryRes[maybeEntries[i]] = GIN_MAYBE;
219 : :
4229 heikki.linnakangas@i 220 : 26 : return curResult;
221 : : }
222 : :
223 : : /*
224 : : * Set up the implementation of the consistent functions for a scan key.
225 : : */
226 : : void
227 : 1032 : ginInitConsistentFunction(GinState *ginstate, GinScanKey key)
228 : : {
229 [ - + ]: 1032 : if (key->searchMode == GIN_SEARCH_MODE_EVERYTHING)
230 : : {
4229 heikki.linnakangas@i 231 :UBC 0 : key->boolConsistentFn = trueConsistentFn;
232 : 0 : key->triConsistentFn = trueTriConsistentFn;
233 : : }
234 : : else
235 : : {
4229 heikki.linnakangas@i 236 :CBC 1032 : key->consistentFmgrInfo = &ginstate->consistentFn[key->attnum - 1];
4196 237 : 1032 : key->triConsistentFmgrInfo = &ginstate->triConsistentFn[key->attnum - 1];
4229 238 : 1032 : key->collation = ginstate->supportCollation[key->attnum - 1];
239 : :
4196 240 [ + - ]: 1032 : if (OidIsValid(ginstate->consistentFn[key->attnum - 1].fn_oid))
241 : 1032 : key->boolConsistentFn = directBoolConsistentFn;
242 : : else
4196 heikki.linnakangas@i 243 :UBC 0 : key->boolConsistentFn = shimBoolConsistentFn;
244 : :
4196 heikki.linnakangas@i 245 [ + + ]:CBC 1032 : if (OidIsValid(ginstate->triConsistentFn[key->attnum - 1].fn_oid))
4141 bruce@momjian.us 246 : 686 : key->triConsistentFn = directTriConsistentFn;
247 : : else
248 : 346 : key->triConsistentFn = shimTriConsistentFn;
249 : : }
4229 heikki.linnakangas@i 250 : 1032 : }
|