Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * nbtpreprocesskeys.c
4 : * Preprocessing for Postgres btree scan keys.
5 : *
6 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/access/nbtree/nbtpreprocesskeys.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 :
16 : #include "postgres.h"
17 :
18 : #include "access/nbtree.h"
19 : #include "common/int.h"
20 : #include "lib/qunique.h"
21 : #include "utils/array.h"
22 : #include "utils/lsyscache.h"
23 : #include "utils/memutils.h"
24 :
25 : typedef struct BTScanKeyPreproc
26 : {
27 : ScanKey inkey;
28 : int inkeyi;
29 : int arrayidx;
30 : } BTScanKeyPreproc;
31 :
32 : typedef struct BTSortArrayContext
33 : {
34 : FmgrInfo *sortproc;
35 : Oid collation;
36 : bool reverse;
37 : } BTSortArrayContext;
38 :
39 : static bool _bt_fix_scankey_strategy(ScanKey skey, int16 *indoption);
40 : static void _bt_mark_scankey_required(ScanKey skey);
41 : static bool _bt_compare_scankey_args(IndexScanDesc scan, ScanKey op,
42 : ScanKey leftarg, ScanKey rightarg,
43 : BTArrayKeyInfo *array, FmgrInfo *orderproc,
44 : bool *result);
45 : static bool _bt_compare_array_scankey_args(IndexScanDesc scan,
46 : ScanKey arraysk, ScanKey skey,
47 : FmgrInfo *orderproc, BTArrayKeyInfo *array,
48 : bool *qual_ok);
49 : static bool _bt_saoparray_shrink(IndexScanDesc scan, ScanKey arraysk,
50 : ScanKey skey, FmgrInfo *orderproc,
51 : BTArrayKeyInfo *array, bool *qual_ok);
52 : static bool _bt_skiparray_shrink(IndexScanDesc scan, ScanKey skey,
53 : BTArrayKeyInfo *array, bool *qual_ok);
54 : static void _bt_skiparray_strat_adjust(IndexScanDesc scan, ScanKey arraysk,
55 : BTArrayKeyInfo *array);
56 : static void _bt_skiparray_strat_decrement(IndexScanDesc scan, ScanKey arraysk,
57 : BTArrayKeyInfo *array);
58 : static void _bt_skiparray_strat_increment(IndexScanDesc scan, ScanKey arraysk,
59 : BTArrayKeyInfo *array);
60 : static void _bt_unmark_keys(IndexScanDesc scan, int *keyDataMap);
61 : static int _bt_reorder_array_cmp(const void *a, const void *b);
62 : static ScanKey _bt_preprocess_array_keys(IndexScanDesc scan, int *new_numberOfKeys);
63 : static void _bt_preprocess_array_keys_final(IndexScanDesc scan, int *keyDataMap);
64 : static int _bt_num_array_keys(IndexScanDesc scan, Oid *skip_eq_ops_out,
65 : int *numSkipArrayKeys_out);
66 : static Datum _bt_find_extreme_element(IndexScanDesc scan, ScanKey skey,
67 : Oid elemtype, StrategyNumber strat,
68 : Datum *elems, int nelems);
69 : static void _bt_setup_array_cmp(IndexScanDesc scan, ScanKey skey, Oid elemtype,
70 : FmgrInfo *orderproc, FmgrInfo **sortprocp);
71 : static int _bt_sort_array_elements(ScanKey skey, FmgrInfo *sortproc,
72 : bool reverse, Datum *elems, int nelems);
73 : static bool _bt_merge_arrays(IndexScanDesc scan, ScanKey skey,
74 : FmgrInfo *sortproc, bool reverse,
75 : Oid origelemtype, Oid nextelemtype,
76 : Datum *elems_orig, int *nelems_orig,
77 : Datum *elems_next, int nelems_next);
78 : static int _bt_compare_array_elements(const void *a, const void *b, void *arg);
79 :
80 :
81 : /*
82 : * _bt_preprocess_keys() -- Preprocess scan keys
83 : *
84 : * The given search-type keys (taken from scan->keyData[])
85 : * are copied to so->keyData[] with possible transformation.
86 : * scan->numberOfKeys is the number of input keys, so->numberOfKeys gets
87 : * the number of output keys. Calling here a second or subsequent time
88 : * (during the same btrescan) is a no-op.
89 : *
90 : * The output keys are marked with additional sk_flags bits beyond the
91 : * system-standard bits supplied by the caller. The DESC and NULLS_FIRST
92 : * indoption bits for the relevant index attribute are copied into the flags.
93 : * Also, for a DESC column, we commute (flip) all the sk_strategy numbers
94 : * so that the index sorts in the desired direction.
95 : *
96 : * One key purpose of this routine is to discover which scan keys must be
97 : * satisfied to continue the scan. It also attempts to eliminate redundant
98 : * keys and detect contradictory keys. (If the index opfamily provides
99 : * incomplete sets of cross-type operators, we may fail to detect redundant
100 : * or contradictory keys, but we can survive that.)
101 : *
102 : * Required output keys are sorted by index attribute. Presently we expect
103 : * (but verify) that the input keys are already so sorted --- this is done
104 : * by match_clauses_to_index() in indxpath.c. Some reordering of the keys
105 : * within each attribute may be done as a byproduct of the processing here.
106 : * That process must leave array scan keys (within an attribute) in the same
107 : * order as corresponding entries from the scan's BTArrayKeyInfo array info.
108 : * We might also construct skip array scan keys that weren't present in the
109 : * original input keys; these are also output in standard attribute order.
110 : *
111 : * The output keys are marked with flags SK_BT_REQFWD and/or SK_BT_REQBKWD
112 : * if they must be satisfied in order to continue the scan forward or backward
113 : * respectively. _bt_checkkeys uses these flags. For example, if the quals
114 : * are "x = 1 AND y < 4 AND z < 5", then _bt_checkkeys will reject a tuple
115 : * (1,2,7), but we must continue the scan in case there are tuples (1,3,z).
116 : * But once we reach tuples like (1,4,z) we can stop scanning because no
117 : * later tuples could match. This is reflected by marking the x and y keys,
118 : * but not the z key, with SK_BT_REQFWD. In general, the keys for leading
119 : * attributes with "=" keys are marked both SK_BT_REQFWD and SK_BT_REQBKWD.
120 : * For the first attribute without an "=" key, any "<" and "<=" keys are
121 : * marked SK_BT_REQFWD while any ">" and ">=" keys are marked SK_BT_REQBKWD.
122 : * This can be seen to be correct by considering the above example.
123 : *
124 : * If we never generated skip array scan keys, it would be possible for "gaps"
125 : * to appear that make it unsafe to mark any subsequent input scan keys
126 : * (copied from scan->keyData[]) as required to continue the scan. Prior to
127 : * Postgres 18, a qual like "WHERE y = 4" always resulted in a full scan.
128 : * This qual now becomes "WHERE x = ANY('{every possible x value}') and y = 4"
129 : * on output. In other words, preprocessing now adds a skip array on "x".
130 : * This has the potential to be much more efficient than a full index scan
131 : * (though it behaves like a full scan when there's many distinct "x" values).
132 : *
133 : * Typically, redundant keys are eliminated: we keep only the tightest
134 : * >/>= bound and the tightest </<= bound, and if there's an = key then
135 : * that's the only one returned. (So, we return either a single = key,
136 : * or one or two boundary-condition keys for each attr.) However, if we
137 : * cannot compare two keys for lack of a suitable cross-type operator,
138 : * we cannot eliminate either key.
139 : *
140 : * When all redundant keys could not be eliminated, we'll output a key array
141 : * that can more or less be treated as if it had no redundant keys. Suppose
142 : * we have "x > 4::int AND x > 10::bigint AND x < 70", and we are unable to
143 : * determine which > key is more restrictive for lack of a suitable cross-type
144 : * operator. We'll arbitrarily pick one of the > keys; the other > key won't
145 : * be marked required. Obviously, the scan will be less efficient if we
146 : * choose x > 4 over x > 10 -- but it can still largely proceed as if there
147 : * was only a single > condition. "x > 10" will be placed at the end of the
148 : * so->keyData[] output array. It'll always be evaluated last, after the keys
149 : * that could be marked required in the usual way (after "x > 4 AND x < 70").
150 : * This can sometimes result in so->keyData[] keys that aren't even in index
151 : * attribute order (if the qual involves multiple attributes). The scan's
152 : * required keys will still be in attribute order, though, so it can't matter.
153 : *
154 : * This scheme ensures that _bt_first always uses the same set of keys at the
155 : * start of a forwards scan as those _bt_checkkeys uses to determine when to
156 : * end a similar backwards scan (and vice-versa). _bt_advance_array_keys
157 : * depends on this: it expects to be able to reliably predict what the next
158 : * _bt_first call will do by testing whether _bt_checkkeys' routines report
159 : * that the final tuple on the page is past the end of matches for the scan's
160 : * keys with the scan direction flipped. If it is (if continuescan=false),
161 : * then it follows that calling _bt_first will, at a minimum, relocate the
162 : * scan to the very next leaf page (in the current scan direction).
163 : *
164 : * As a byproduct of this work, we can detect contradictory quals such
165 : * as "x = 1 AND x > 2". If we see that, we return so->qual_ok = false,
166 : * indicating the scan need not be run at all since no tuples can match.
167 : * (In this case we do not bother completing the output key array!)
168 : * Again, missing cross-type operators might cause us to fail to prove the
169 : * quals contradictory when they really are, but the scan will work correctly.
170 : *
171 : * Skip array = keys will even be generated in the presence of "contradictory"
172 : * inequality quals when it'll enable marking later input quals as required.
173 : * We'll merge any such inequalities into the generated skip array by setting
174 : * its array.low_compare or array.high_compare key field. The resulting skip
175 : * array will generate its array elements from a range that's constrained by
176 : * any merged input inequalities (which won't get output in so->keyData[]).
177 : *
178 : * Row comparison keys currently have a couple of notable limitations.
179 : * Right now we just transfer them into the preprocessed array without any
180 : * editorialization. We can treat them the same as an ordinary inequality
181 : * comparison on the row's first index column, for the purposes of the logic
182 : * about required keys. Also, we are unable to merge a row comparison key
183 : * into a skip array (only ordinary inequalities are merged). A key that
184 : * comes after a Row comparison key is therefore never marked as required.
185 : *
186 : * Note: the reason we have to copy the preprocessed scan keys into private
187 : * storage is that we are modifying the array based on comparisons of the
188 : * key argument values, which could change on a rescan. Therefore we can't
189 : * overwrite the source data.
190 : */
191 : void
192 16085712 : _bt_preprocess_keys(IndexScanDesc scan)
193 : {
194 16085712 : BTScanOpaque so = (BTScanOpaque) scan->opaque;
195 16085712 : int numberOfKeys = scan->numberOfKeys;
196 16085712 : int16 *indoption = scan->indexRelation->rd_indoption;
197 : int new_numberOfKeys;
198 : int numberOfEqualCols;
199 : ScanKey inkeys;
200 : BTScanKeyPreproc xform[BTMaxStrategyNumber];
201 : bool test_result,
202 16085712 : redundant_key_kept = false;
203 : AttrNumber attno;
204 : ScanKey arrayKeyData;
205 16085712 : int *keyDataMap = NULL;
206 16085712 : int arrayidx = 0;
207 :
208 16085712 : if (so->numberOfKeys > 0)
209 : {
210 : /*
211 : * Only need to do preprocessing once per btrescan, at most. All
212 : * calls after the first are handled as no-ops.
213 : */
214 8373380 : return;
215 : }
216 :
217 : /* initialize result variables */
218 16068196 : so->qual_ok = true;
219 16068196 : so->numberOfKeys = 0;
220 :
221 16068196 : if (numberOfKeys < 1)
222 13320 : return; /* done if qual-less scan */
223 :
224 : /* If any keys are SK_SEARCHARRAY type, set up array-key info */
225 16054876 : arrayKeyData = _bt_preprocess_array_keys(scan, &numberOfKeys);
226 16054876 : if (!so->qual_ok)
227 : {
228 : /* unmatchable array, so give up */
229 18 : return;
230 : }
231 :
232 : /*
233 : * Treat arrayKeyData[] (a partially preprocessed copy of scan->keyData[])
234 : * as our input if _bt_preprocess_array_keys just allocated it, else just
235 : * use scan->keyData[]
236 : */
237 16054858 : if (arrayKeyData)
238 : {
239 70976 : inkeys = arrayKeyData;
240 :
241 : /* Also maintain keyDataMap for remapping so->orderProcs[] later */
242 70976 : keyDataMap = MemoryContextAlloc(so->arrayContext,
243 : numberOfKeys * sizeof(int));
244 :
245 : /*
246 : * Also enlarge output array when it might otherwise not have room for
247 : * a skip array's scan key
248 : */
249 70976 : if (numberOfKeys > scan->numberOfKeys)
250 3712 : so->keyData = repalloc(so->keyData,
251 : numberOfKeys * sizeof(ScanKeyData));
252 : }
253 : else
254 15983882 : inkeys = scan->keyData;
255 :
256 : /* we check that input keys are correctly ordered */
257 16054858 : if (inkeys[0].sk_attno < 1)
258 0 : elog(ERROR, "btree index keys must be ordered by attribute");
259 :
260 : /* We can short-circuit most of the work if there's just one key */
261 16054858 : if (numberOfKeys == 1)
262 : {
263 : /* Apply indoption to scankey (might change sk_strategy!) */
264 8341830 : if (!_bt_fix_scankey_strategy(&inkeys[0], indoption))
265 1082 : so->qual_ok = false;
266 8341830 : memcpy(&so->keyData[0], &inkeys[0], sizeof(ScanKeyData));
267 8341830 : so->numberOfKeys = 1;
268 : /* We can mark the qual as required if it's for first index col */
269 8341830 : if (inkeys[0].sk_attno == 1)
270 8341830 : _bt_mark_scankey_required(&so->keyData[0]);
271 : if (arrayKeyData)
272 : {
273 : /*
274 : * Don't call _bt_preprocess_array_keys_final in this fast path
275 : * (we'll miss out on the single value array transformation, but
276 : * that's not nearly as important when there's only one scan key)
277 : */
278 : Assert(so->keyData[0].sk_flags & SK_SEARCHARRAY);
279 : Assert(so->keyData[0].sk_strategy != BTEqualStrategyNumber ||
280 : (so->arrayKeys[0].scan_key == 0 &&
281 : !(so->keyData[0].sk_flags & SK_BT_SKIP) &&
282 : OidIsValid(so->orderProcs[0].fn_oid)));
283 : }
284 :
285 8341830 : return;
286 : }
287 :
288 : /*
289 : * Otherwise, do the full set of pushups.
290 : */
291 7713028 : new_numberOfKeys = 0;
292 7713028 : numberOfEqualCols = 0;
293 :
294 : /*
295 : * Initialize for processing of keys for attr 1.
296 : *
297 : * xform[i] points to the currently best scan key of strategy type i+1; it
298 : * is NULL if we haven't yet found such a key for this attr.
299 : */
300 7713028 : attno = 1;
301 7713028 : memset(xform, 0, sizeof(xform));
302 :
303 : /*
304 : * Loop iterates from 0 to numberOfKeys inclusive; we use the last pass to
305 : * handle after-last-key processing. Actual exit from the loop is at the
306 : * "break" statement below.
307 : */
308 7713028 : for (int i = 0;; i++)
309 16983952 : {
310 24696980 : ScanKey inkey = inkeys + i;
311 : int j;
312 :
313 24696980 : if (i < numberOfKeys)
314 : {
315 : /* Apply indoption to scankey (might change sk_strategy!) */
316 16984612 : if (!_bt_fix_scankey_strategy(inkey, indoption))
317 : {
318 : /* NULL can't be matched, so give up */
319 654 : so->qual_ok = false;
320 654 : return;
321 : }
322 : }
323 :
324 : /*
325 : * If we are at the end of the keys for a particular attr, finish up
326 : * processing and emit the cleaned-up keys.
327 : */
328 24696326 : if (i == numberOfKeys || inkey->sk_attno != attno)
329 : {
330 16981602 : int priorNumberOfEqualCols = numberOfEqualCols;
331 :
332 : /* check input keys are correctly ordered */
333 16981602 : if (i < numberOfKeys && inkey->sk_attno < attno)
334 0 : elog(ERROR, "btree index keys must be ordered by attribute");
335 :
336 : /*
337 : * If = has been specified, all other keys can be eliminated as
338 : * redundant. Note that this is no less true if the = key is
339 : * SEARCHARRAY; the only real difference is that the inequality
340 : * key _becomes_ redundant by making _bt_compare_scankey_args
341 : * eliminate the subset of elements that won't need to be matched
342 : * (with SAOP arrays and skip arrays alike).
343 : *
344 : * If we have a case like "key = 1 AND key > 2", we set qual_ok to
345 : * false and abandon further processing. We'll do the same thing
346 : * given a case like "key IN (0, 1) AND key > 2".
347 : *
348 : * We also have to deal with the case of "key IS NULL", which is
349 : * unsatisfiable in combination with any other index condition. By
350 : * the time we get here, that's been classified as an equality
351 : * check, and we've rejected any combination of it with a regular
352 : * equality condition; but not with other types of conditions.
353 : */
354 16981602 : if (xform[BTEqualStrategyNumber - 1].inkey)
355 : {
356 15329208 : ScanKey eq = xform[BTEqualStrategyNumber - 1].inkey;
357 15329208 : BTArrayKeyInfo *array = NULL;
358 15329208 : FmgrInfo *orderproc = NULL;
359 :
360 15329208 : if (arrayKeyData && (eq->sk_flags & SK_SEARCHARRAY))
361 : {
362 : int eq_in_ikey,
363 : eq_arrayidx;
364 :
365 4546 : eq_in_ikey = xform[BTEqualStrategyNumber - 1].inkeyi;
366 4546 : eq_arrayidx = xform[BTEqualStrategyNumber - 1].arrayidx;
367 4546 : array = &so->arrayKeys[eq_arrayidx - 1];
368 4546 : orderproc = so->orderProcs + eq_in_ikey;
369 :
370 : Assert(array->scan_key == eq_in_ikey);
371 : Assert(OidIsValid(orderproc->fn_oid));
372 : }
373 :
374 91975092 : for (j = BTMaxStrategyNumber; --j >= 0;)
375 : {
376 76645920 : ScanKey chk = xform[j].inkey;
377 :
378 76645920 : if (!chk || j == (BTEqualStrategyNumber - 1))
379 76645494 : continue;
380 :
381 426 : if (eq->sk_flags & SK_SEARCHNULL)
382 : {
383 : /* IS NULL is contradictory to anything else */
384 24 : so->qual_ok = false;
385 24 : return;
386 : }
387 :
388 402 : if (_bt_compare_scankey_args(scan, chk, eq, chk,
389 : array, orderproc,
390 : &test_result))
391 : {
392 396 : if (!test_result)
393 : {
394 : /* keys proven mutually contradictory */
395 12 : so->qual_ok = false;
396 12 : return;
397 : }
398 : /* else discard the redundant non-equality key */
399 384 : xform[j].inkey = NULL;
400 384 : xform[j].inkeyi = -1;
401 : }
402 : else
403 6 : redundant_key_kept = true;
404 : }
405 : /* track number of attrs for which we have "=" keys */
406 15329172 : numberOfEqualCols++;
407 : }
408 :
409 : /* try to keep only one of <, <= */
410 16981566 : if (xform[BTLessStrategyNumber - 1].inkey &&
411 1912 : xform[BTLessEqualStrategyNumber - 1].inkey)
412 : {
413 6 : ScanKey lt = xform[BTLessStrategyNumber - 1].inkey;
414 6 : ScanKey le = xform[BTLessEqualStrategyNumber - 1].inkey;
415 :
416 6 : if (_bt_compare_scankey_args(scan, le, lt, le, NULL, NULL,
417 : &test_result))
418 : {
419 6 : if (test_result)
420 6 : xform[BTLessEqualStrategyNumber - 1].inkey = NULL;
421 : else
422 0 : xform[BTLessStrategyNumber - 1].inkey = NULL;
423 : }
424 : else
425 0 : redundant_key_kept = true;
426 : }
427 :
428 : /* try to keep only one of >, >= */
429 16981566 : if (xform[BTGreaterStrategyNumber - 1].inkey &&
430 1648106 : xform[BTGreaterEqualStrategyNumber - 1].inkey)
431 : {
432 6 : ScanKey gt = xform[BTGreaterStrategyNumber - 1].inkey;
433 6 : ScanKey ge = xform[BTGreaterEqualStrategyNumber - 1].inkey;
434 :
435 6 : if (_bt_compare_scankey_args(scan, ge, gt, ge, NULL, NULL,
436 : &test_result))
437 : {
438 6 : if (test_result)
439 0 : xform[BTGreaterEqualStrategyNumber - 1].inkey = NULL;
440 : else
441 6 : xform[BTGreaterStrategyNumber - 1].inkey = NULL;
442 : }
443 : else
444 0 : redundant_key_kept = true;
445 : }
446 :
447 : /*
448 : * Emit the cleaned-up keys into the so->keyData[] array, and then
449 : * mark them if they are required. They are required (possibly
450 : * only in one direction) if all attrs before this one had "=".
451 : *
452 : * In practice we'll rarely output non-required scan keys here;
453 : * typically, _bt_preprocess_array_keys has already added "=" keys
454 : * sufficient to form an unbroken series of "=" constraints on all
455 : * attrs prior to the attr from the final scan->keyData[] key.
456 : */
457 101889396 : for (j = BTMaxStrategyNumber; --j >= 0;)
458 : {
459 84907830 : if (xform[j].inkey)
460 : {
461 16983406 : ScanKey outkey = &so->keyData[new_numberOfKeys++];
462 :
463 16983406 : memcpy(outkey, xform[j].inkey, sizeof(ScanKeyData));
464 16983406 : if (arrayKeyData)
465 9314 : keyDataMap[new_numberOfKeys - 1] = xform[j].inkeyi;
466 16983406 : if (priorNumberOfEqualCols == attno - 1)
467 16983406 : _bt_mark_scankey_required(outkey);
468 : }
469 : }
470 :
471 : /*
472 : * Exit loop here if done.
473 : */
474 16981566 : if (i == numberOfKeys)
475 7712332 : break;
476 :
477 : /* Re-initialize for new attno */
478 9269234 : attno = inkey->sk_attno;
479 9269234 : memset(xform, 0, sizeof(xform));
480 : }
481 :
482 : /* check strategy this key's operator corresponds to */
483 16983958 : j = inkey->sk_strategy - 1;
484 :
485 16983958 : if (inkey->sk_strategy == BTEqualStrategyNumber &&
486 15329250 : (inkey->sk_flags & SK_SEARCHARRAY))
487 : {
488 : /* must track how input scan keys map to arrays */
489 : Assert(arrayKeyData);
490 4552 : arrayidx++;
491 : }
492 :
493 : /*
494 : * have we seen a scan key for this same attribute and using this same
495 : * operator strategy before now?
496 : */
497 16983958 : if (xform[j].inkey == NULL)
498 : {
499 : /* nope, so this scan key wins by default (at least for now) */
500 16983892 : xform[j].inkey = inkey;
501 16983892 : xform[j].inkeyi = i;
502 16983892 : xform[j].arrayidx = arrayidx;
503 : }
504 : else
505 : {
506 66 : FmgrInfo *orderproc = NULL;
507 66 : BTArrayKeyInfo *array = NULL;
508 :
509 : /*
510 : * Seen one of these before, so keep only the more restrictive key
511 : * if possible
512 : */
513 66 : if (j == (BTEqualStrategyNumber - 1) && arrayKeyData)
514 : {
515 : /*
516 : * Have to set up array keys
517 : */
518 18 : if (inkey->sk_flags & SK_SEARCHARRAY)
519 : {
520 0 : array = &so->arrayKeys[arrayidx - 1];
521 0 : orderproc = so->orderProcs + i;
522 :
523 : Assert(array->scan_key == i);
524 : Assert(OidIsValid(orderproc->fn_oid));
525 : Assert(!(inkey->sk_flags & SK_BT_SKIP));
526 : }
527 18 : else if (xform[j].inkey->sk_flags & SK_SEARCHARRAY)
528 : {
529 12 : array = &so->arrayKeys[xform[j].arrayidx - 1];
530 12 : orderproc = so->orderProcs + xform[j].inkeyi;
531 :
532 : Assert(array->scan_key == xform[j].inkeyi);
533 : Assert(OidIsValid(orderproc->fn_oid));
534 : Assert(!(xform[j].inkey->sk_flags & SK_BT_SKIP));
535 : }
536 :
537 : /*
538 : * Both scan keys might have arrays, in which case we'll
539 : * arbitrarily pass only one of the arrays. That won't
540 : * matter, since _bt_compare_scankey_args is aware that two
541 : * SEARCHARRAY scan keys mean that _bt_preprocess_array_keys
542 : * failed to eliminate redundant arrays through array merging.
543 : * _bt_compare_scankey_args just returns false when it sees
544 : * this; it won't even try to examine either array.
545 : */
546 : }
547 :
548 66 : if (_bt_compare_scankey_args(scan, inkey, inkey, xform[j].inkey,
549 : array, orderproc, &test_result))
550 : {
551 : /* Have all we need to determine redundancy */
552 66 : if (test_result)
553 : {
554 : /*
555 : * New key is more restrictive, and so replaces old key...
556 : */
557 54 : if (j != (BTEqualStrategyNumber - 1) ||
558 18 : !(xform[j].inkey->sk_flags & SK_SEARCHARRAY))
559 : {
560 48 : xform[j].inkey = inkey;
561 48 : xform[j].inkeyi = i;
562 48 : xform[j].arrayidx = arrayidx;
563 : }
564 : else
565 : {
566 : /*
567 : * ...unless we have to keep the old key because it's
568 : * an array that rendered the new key redundant. We
569 : * need to make sure that we don't throw away an array
570 : * scan key. _bt_preprocess_array_keys_final expects
571 : * us to keep all of the arrays that weren't already
572 : * eliminated by _bt_preprocess_array_keys earlier on.
573 : */
574 : Assert(!(inkey->sk_flags & SK_SEARCHARRAY));
575 : }
576 : }
577 12 : else if (j == (BTEqualStrategyNumber - 1))
578 : {
579 : /* key == a && key == b, but a != b */
580 6 : so->qual_ok = false;
581 6 : return;
582 : }
583 : /* else old key is more restrictive, keep it */
584 : }
585 : else
586 : {
587 : /*
588 : * We can't determine which key is more restrictive. Push
589 : * xform[j] directly to the output array, then set xform[j] to
590 : * the new scan key.
591 : *
592 : * Note: We do things this way around so that our arrays are
593 : * always in the same order as their corresponding scan keys.
594 : * _bt_preprocess_array_keys_final expects this.
595 : */
596 0 : ScanKey outkey = &so->keyData[new_numberOfKeys++];
597 :
598 0 : memcpy(outkey, xform[j].inkey, sizeof(ScanKeyData));
599 0 : if (arrayKeyData)
600 0 : keyDataMap[new_numberOfKeys - 1] = xform[j].inkeyi;
601 0 : if (numberOfEqualCols == attno - 1)
602 0 : _bt_mark_scankey_required(outkey);
603 0 : xform[j].inkey = inkey;
604 0 : xform[j].inkeyi = i;
605 0 : xform[j].arrayidx = arrayidx;
606 0 : redundant_key_kept = true;
607 : }
608 : }
609 : }
610 :
611 7712332 : so->numberOfKeys = new_numberOfKeys;
612 :
613 : /*
614 : * Now that we've built a temporary mapping from so->keyData[] (output
615 : * scan keys) to arrayKeyData[] (our input scan keys), fix array->scan_key
616 : * references. Also consolidate the so->orderProcs[] array such that it
617 : * can be subscripted using so->keyData[]-wise offsets.
618 : */
619 7712332 : if (arrayKeyData)
620 4196 : _bt_preprocess_array_keys_final(scan, keyDataMap);
621 :
622 : /*
623 : * If there are remaining redundant inequality keys, we must make sure
624 : * that each index attribute has no more than one required >/>= key, and
625 : * no more than one required </<= key. Attributes that have one or more
626 : * required = keys now must keep only one required key (the first = key).
627 : */
628 7712332 : if (unlikely(redundant_key_kept) && so->qual_ok)
629 6 : _bt_unmark_keys(scan, keyDataMap);
630 :
631 : /* Could pfree arrayKeyData/keyDataMap now, but not worth the cycles */
632 : }
633 :
634 : /*
635 : * Adjust a scankey's strategy and flags setting as needed for indoptions.
636 : *
637 : * We copy the appropriate indoption value into the scankey sk_flags
638 : * (shifting to avoid clobbering system-defined flag bits). Also, if
639 : * the DESC option is set, commute (flip) the operator strategy number.
640 : *
641 : * A secondary purpose is to check for IS NULL/NOT NULL scankeys and set up
642 : * the strategy field correctly for them.
643 : *
644 : * Lastly, for ordinary scankeys (not IS NULL/NOT NULL), we check for a
645 : * NULL comparison value. Since all btree operators are assumed strict,
646 : * a NULL means that the qual cannot be satisfied. We return true if the
647 : * comparison value isn't NULL, or false if the scan should be abandoned.
648 : *
649 : * This function is applied to the *input* scankey structure; therefore
650 : * on a rescan we will be looking at already-processed scankeys. Hence
651 : * we have to be careful not to re-commute the strategy if we already did it.
652 : * It's a bit ugly to modify the caller's copy of the scankey but in practice
653 : * there shouldn't be any problem, since the index's indoptions are certainly
654 : * not going to change while the scankey survives.
655 : */
656 : static bool
657 25326442 : _bt_fix_scankey_strategy(ScanKey skey, int16 *indoption)
658 : {
659 : int addflags;
660 :
661 25326442 : addflags = indoption[skey->sk_attno - 1] << SK_BT_INDOPTION_SHIFT;
662 :
663 : /*
664 : * We treat all btree operators as strict (even if they're not so marked
665 : * in pg_proc). This means that it is impossible for an operator condition
666 : * with a NULL comparison constant to succeed, and we can reject it right
667 : * away.
668 : *
669 : * However, we now also support "x IS NULL" clauses as search conditions,
670 : * so in that case keep going. The planner has not filled in any
671 : * particular strategy in this case, so set it to BTEqualStrategyNumber
672 : * --- we can treat IS NULL as an equality operator for purposes of search
673 : * strategy.
674 : *
675 : * Likewise, "x IS NOT NULL" is supported. We treat that as either "less
676 : * than NULL" in a NULLS LAST index, or "greater than NULL" in a NULLS
677 : * FIRST index.
678 : *
679 : * Note: someday we might have to fill in sk_collation from the index
680 : * column's collation. At the moment this is a non-issue because we'll
681 : * never actually call the comparison operator on a NULL.
682 : */
683 25326442 : if (skey->sk_flags & SK_ISNULL)
684 : {
685 : /* SK_ISNULL shouldn't be set in a row header scankey */
686 : Assert(!(skey->sk_flags & SK_ROW_HEADER));
687 :
688 : /* Set indoption flags in scankey (might be done already) */
689 127680 : skey->sk_flags |= addflags;
690 :
691 : /* Set correct strategy for IS NULL or NOT NULL search */
692 127680 : if (skey->sk_flags & SK_SEARCHNULL)
693 : {
694 152 : skey->sk_strategy = BTEqualStrategyNumber;
695 152 : skey->sk_subtype = InvalidOid;
696 152 : skey->sk_collation = InvalidOid;
697 : }
698 127528 : else if (skey->sk_flags & SK_SEARCHNOTNULL)
699 : {
700 125798 : if (skey->sk_flags & SK_BT_NULLS_FIRST)
701 36 : skey->sk_strategy = BTGreaterStrategyNumber;
702 : else
703 125762 : skey->sk_strategy = BTLessStrategyNumber;
704 125798 : skey->sk_subtype = InvalidOid;
705 125798 : skey->sk_collation = InvalidOid;
706 : }
707 : else
708 : {
709 : /* regular qual, so it cannot be satisfied */
710 1730 : return false;
711 : }
712 :
713 : /* Needn't do the rest */
714 125950 : return true;
715 : }
716 :
717 : /* Adjust strategy for DESC, if we didn't already */
718 25198762 : if ((addflags & SK_BT_DESC) && !(skey->sk_flags & SK_BT_DESC))
719 78 : skey->sk_strategy = BTCommuteStrategyNumber(skey->sk_strategy);
720 25198762 : skey->sk_flags |= addflags;
721 :
722 : /* If it's a row header, fix row member flags and strategies similarly */
723 25198762 : if (skey->sk_flags & SK_ROW_HEADER)
724 : {
725 84 : ScanKey subkey = (ScanKey) DatumGetPointer(skey->sk_argument);
726 :
727 84 : if (subkey->sk_flags & SK_ISNULL)
728 : {
729 : /* First row member is NULL, so RowCompare is unsatisfiable */
730 : Assert(subkey->sk_flags & SK_ROW_MEMBER);
731 6 : return false;
732 : }
733 :
734 : for (;;)
735 : {
736 78 : Assert(subkey->sk_flags & SK_ROW_MEMBER);
737 156 : addflags = indoption[subkey->sk_attno - 1] << SK_BT_INDOPTION_SHIFT;
738 156 : if ((addflags & SK_BT_DESC) && !(subkey->sk_flags & SK_BT_DESC))
739 0 : subkey->sk_strategy = BTCommuteStrategyNumber(subkey->sk_strategy);
740 156 : subkey->sk_flags |= addflags;
741 156 : if (subkey->sk_flags & SK_ROW_END)
742 78 : break;
743 78 : subkey++;
744 : }
745 : }
746 :
747 25198756 : return true;
748 : }
749 :
750 : /*
751 : * Mark a scankey as "required to continue the scan".
752 : *
753 : * Depending on the operator type, the key may be required for both scan
754 : * directions or just one. Also, if the key is a row comparison header,
755 : * we have to mark the appropriate subsidiary ScanKeys as required. In such
756 : * cases, the first subsidiary key is required, but subsequent ones are
757 : * required only as long as they correspond to successive index columns and
758 : * match the leading column as to sort direction. Otherwise the row
759 : * comparison ordering is different from the index ordering and so we can't
760 : * stop the scan on the basis of those lower-order columns.
761 : *
762 : * Note: when we set required-key flag bits in a subsidiary scankey, we are
763 : * scribbling on a data structure belonging to the index AM's caller, not on
764 : * our private copy. This should be OK because the marking will not change
765 : * from scan to scan within a query, and so we'd just re-mark the same way
766 : * anyway on a rescan. Something to keep an eye on though.
767 : */
768 : static void
769 25325236 : _bt_mark_scankey_required(ScanKey skey)
770 : {
771 : int addflags;
772 :
773 25325236 : switch (skey->sk_strategy)
774 : {
775 128668 : case BTLessStrategyNumber:
776 : case BTLessEqualStrategyNumber:
777 128668 : addflags = SK_BT_REQFWD;
778 128668 : break;
779 23542958 : case BTEqualStrategyNumber:
780 23542958 : addflags = SK_BT_REQFWD | SK_BT_REQBKWD;
781 23542958 : break;
782 1653610 : case BTGreaterEqualStrategyNumber:
783 : case BTGreaterStrategyNumber:
784 1653610 : addflags = SK_BT_REQBKWD;
785 1653610 : break;
786 0 : default:
787 0 : elog(ERROR, "unrecognized StrategyNumber: %d",
788 : (int) skey->sk_strategy);
789 : addflags = 0; /* keep compiler quiet */
790 : break;
791 : }
792 :
793 25325236 : skey->sk_flags |= addflags;
794 :
795 25325236 : if (skey->sk_flags & SK_ROW_HEADER)
796 : {
797 84 : ScanKey subkey = (ScanKey) DatumGetPointer(skey->sk_argument);
798 84 : AttrNumber attno = skey->sk_attno;
799 :
800 : /* First subkey should be same column/operator as the header */
801 : Assert(subkey->sk_attno == attno);
802 : Assert(subkey->sk_strategy == skey->sk_strategy);
803 :
804 : for (;;)
805 : {
806 84 : Assert(subkey->sk_flags & SK_ROW_MEMBER);
807 168 : if (subkey->sk_attno != attno)
808 12 : break; /* non-adjacent key, so not required */
809 156 : if (subkey->sk_strategy != skey->sk_strategy)
810 0 : break; /* wrong direction, so not required */
811 156 : subkey->sk_flags |= addflags;
812 156 : if (subkey->sk_flags & SK_ROW_END)
813 72 : break;
814 84 : subkey++;
815 84 : attno++;
816 : }
817 : }
818 25325236 : }
819 :
820 : /*
821 : * Compare two scankey values using a specified operator.
822 : *
823 : * The test we want to perform is logically "leftarg op rightarg", where
824 : * leftarg and rightarg are the sk_argument values in those ScanKeys, and
825 : * the comparison operator is the one in the op ScanKey. However, in
826 : * cross-data-type situations we may need to look up the correct operator in
827 : * the index's opfamily: it is the one having amopstrategy = op->sk_strategy
828 : * and amoplefttype/amoprighttype equal to the two argument datatypes.
829 : *
830 : * If the opfamily doesn't supply a complete set of cross-type operators we
831 : * may not be able to make the comparison. If we can make the comparison
832 : * we store the operator result in *result and return true. We return false
833 : * if the comparison could not be made.
834 : *
835 : * If either leftarg or rightarg are an array, we'll apply array-specific
836 : * rules to determine which array elements are redundant on behalf of caller.
837 : * It is up to our caller to save whichever of the two scan keys is the array,
838 : * and discard the non-array scan key (the non-array scan key is guaranteed to
839 : * be redundant with any complete opfamily). Caller isn't expected to call
840 : * here with a pair of array scan keys provided we're dealing with a complete
841 : * opfamily (_bt_preprocess_array_keys will merge array keys together to make
842 : * sure of that).
843 : *
844 : * Note: we'll also shrink caller's array as needed to eliminate redundant
845 : * array elements. One reason why caller should prefer to discard non-array
846 : * scan keys is so that we'll have the opportunity to shrink the array
847 : * multiple times, in multiple calls (for each of several other scan keys on
848 : * the same index attribute).
849 : *
850 : * Note: op always points at the same ScanKey as either leftarg or rightarg.
851 : * Since we don't scribble on the scankeys themselves, this aliasing should
852 : * cause no trouble.
853 : *
854 : * Note: this routine needs to be insensitive to any DESC option applied
855 : * to the index column. For example, "x < 4" is a tighter constraint than
856 : * "x < 5" regardless of which way the index is sorted.
857 : */
858 : static bool
859 492 : _bt_compare_scankey_args(IndexScanDesc scan, ScanKey op,
860 : ScanKey leftarg, ScanKey rightarg,
861 : BTArrayKeyInfo *array, FmgrInfo *orderproc,
862 : bool *result)
863 : {
864 492 : Relation rel = scan->indexRelation;
865 : Oid lefttype,
866 : righttype,
867 : optype,
868 : opcintype,
869 : cmp_op;
870 : StrategyNumber strat;
871 :
872 : Assert(!((leftarg->sk_flags | rightarg->sk_flags) & SK_ROW_MEMBER));
873 :
874 : /*
875 : * First, deal with cases where one or both args are NULL. This should
876 : * only happen when the scankeys represent IS NULL/NOT NULL conditions.
877 : */
878 492 : if ((leftarg->sk_flags | rightarg->sk_flags) & SK_ISNULL)
879 : {
880 : bool leftnull,
881 : rightnull;
882 :
883 : /* Handle skip array comparison with IS NOT NULL scan key */
884 174 : if ((leftarg->sk_flags | rightarg->sk_flags) & SK_BT_SKIP)
885 : {
886 : /* Shouldn't generate skip array in presence of IS NULL key */
887 : Assert(!((leftarg->sk_flags | rightarg->sk_flags) & SK_SEARCHNULL));
888 : Assert((leftarg->sk_flags | rightarg->sk_flags) & SK_SEARCHNOTNULL);
889 :
890 : /* Skip array will have no NULL element/IS NULL scan key */
891 : Assert(array->num_elems == -1);
892 36 : array->null_elem = false;
893 :
894 : /* IS NOT NULL key (could be leftarg or rightarg) now redundant */
895 36 : *result = true;
896 36 : return true;
897 : }
898 :
899 138 : if (leftarg->sk_flags & SK_ISNULL)
900 : {
901 : Assert(leftarg->sk_flags & (SK_SEARCHNULL | SK_SEARCHNOTNULL));
902 6 : leftnull = true;
903 : }
904 : else
905 132 : leftnull = false;
906 138 : if (rightarg->sk_flags & SK_ISNULL)
907 : {
908 : Assert(rightarg->sk_flags & (SK_SEARCHNULL | SK_SEARCHNOTNULL));
909 138 : rightnull = true;
910 : }
911 : else
912 0 : rightnull = false;
913 :
914 : /*
915 : * We treat NULL as either greater than or less than all other values.
916 : * Since true > false, the tests below work correctly for NULLS LAST
917 : * logic. If the index is NULLS FIRST, we need to flip the strategy.
918 : */
919 138 : strat = op->sk_strategy;
920 138 : if (op->sk_flags & SK_BT_NULLS_FIRST)
921 0 : strat = BTCommuteStrategyNumber(strat);
922 :
923 138 : switch (strat)
924 : {
925 132 : case BTLessStrategyNumber:
926 132 : *result = (leftnull < rightnull);
927 132 : break;
928 0 : case BTLessEqualStrategyNumber:
929 0 : *result = (leftnull <= rightnull);
930 0 : break;
931 6 : case BTEqualStrategyNumber:
932 6 : *result = (leftnull == rightnull);
933 6 : break;
934 0 : case BTGreaterEqualStrategyNumber:
935 0 : *result = (leftnull >= rightnull);
936 0 : break;
937 0 : case BTGreaterStrategyNumber:
938 0 : *result = (leftnull > rightnull);
939 0 : break;
940 0 : default:
941 0 : elog(ERROR, "unrecognized StrategyNumber: %d", (int) strat);
942 : *result = false; /* keep compiler quiet */
943 : break;
944 : }
945 138 : return true;
946 : }
947 :
948 : /*
949 : * We don't yet know how to determine redundancy when it involves a row
950 : * compare key (barring simple cases involving IS NULL/IS NOT NULL)
951 : */
952 318 : if ((leftarg->sk_flags | rightarg->sk_flags) & SK_ROW_HEADER)
953 : {
954 : Assert(!((leftarg->sk_flags | rightarg->sk_flags) & SK_BT_SKIP));
955 6 : return false;
956 : }
957 :
958 : /*
959 : * If either leftarg or rightarg are equality-type array scankeys, we need
960 : * specialized handling (since by now we know that IS NULL wasn't used)
961 : */
962 312 : if (array)
963 : {
964 : bool leftarray,
965 : rightarray;
966 :
967 356 : leftarray = ((leftarg->sk_flags & SK_SEARCHARRAY) &&
968 172 : leftarg->sk_strategy == BTEqualStrategyNumber);
969 196 : rightarray = ((rightarg->sk_flags & SK_SEARCHARRAY) &&
970 12 : rightarg->sk_strategy == BTEqualStrategyNumber);
971 :
972 : /*
973 : * _bt_preprocess_array_keys is responsible for merging together array
974 : * scan keys, and will do so whenever the opfamily has the required
975 : * cross-type support. If it failed to do that, we handle it just
976 : * like the case where we can't make the comparison ourselves.
977 : */
978 184 : if (leftarray && rightarray)
979 : {
980 : /* Can't make the comparison */
981 0 : *result = false; /* suppress compiler warnings */
982 : Assert(!((leftarg->sk_flags | rightarg->sk_flags) & SK_BT_SKIP));
983 0 : return false;
984 : }
985 :
986 : /*
987 : * Otherwise we need to determine if either one of leftarg or rightarg
988 : * uses an array, then pass this through to a dedicated helper
989 : * function.
990 : */
991 184 : if (leftarray)
992 172 : return _bt_compare_array_scankey_args(scan, leftarg, rightarg,
993 : orderproc, array, result);
994 12 : else if (rightarray)
995 12 : return _bt_compare_array_scankey_args(scan, rightarg, leftarg,
996 : orderproc, array, result);
997 :
998 : /* FALL THRU */
999 : }
1000 :
1001 : /*
1002 : * The opfamily we need to worry about is identified by the index column.
1003 : */
1004 : Assert(leftarg->sk_attno == rightarg->sk_attno);
1005 :
1006 128 : opcintype = rel->rd_opcintype[leftarg->sk_attno - 1];
1007 :
1008 : /*
1009 : * Determine the actual datatypes of the ScanKey arguments. We have to
1010 : * support the convention that sk_subtype == InvalidOid means the opclass
1011 : * input type; this is a hack to simplify life for ScanKeyInit().
1012 : */
1013 128 : lefttype = leftarg->sk_subtype;
1014 128 : if (lefttype == InvalidOid)
1015 0 : lefttype = opcintype;
1016 128 : righttype = rightarg->sk_subtype;
1017 128 : if (righttype == InvalidOid)
1018 0 : righttype = opcintype;
1019 128 : optype = op->sk_subtype;
1020 128 : if (optype == InvalidOid)
1021 0 : optype = opcintype;
1022 :
1023 : /*
1024 : * If leftarg and rightarg match the types expected for the "op" scankey,
1025 : * we can use its already-looked-up comparison function.
1026 : */
1027 128 : if (lefttype == opcintype && righttype == optype)
1028 : {
1029 122 : *result = DatumGetBool(FunctionCall2Coll(&op->sk_func,
1030 : op->sk_collation,
1031 : leftarg->sk_argument,
1032 : rightarg->sk_argument));
1033 122 : return true;
1034 : }
1035 :
1036 : /*
1037 : * Otherwise, we need to go to the syscache to find the appropriate
1038 : * operator. (This cannot result in infinite recursion, since no
1039 : * indexscan initiated by syscache lookup will use cross-data-type
1040 : * operators.)
1041 : *
1042 : * If the sk_strategy was flipped by _bt_fix_scankey_strategy, we have to
1043 : * un-flip it to get the correct opfamily member.
1044 : */
1045 6 : strat = op->sk_strategy;
1046 6 : if (op->sk_flags & SK_BT_DESC)
1047 0 : strat = BTCommuteStrategyNumber(strat);
1048 :
1049 6 : cmp_op = get_opfamily_member(rel->rd_opfamily[leftarg->sk_attno - 1],
1050 : lefttype,
1051 : righttype,
1052 : strat);
1053 6 : if (OidIsValid(cmp_op))
1054 : {
1055 6 : RegProcedure cmp_proc = get_opcode(cmp_op);
1056 :
1057 6 : if (RegProcedureIsValid(cmp_proc))
1058 : {
1059 6 : *result = DatumGetBool(OidFunctionCall2Coll(cmp_proc,
1060 : op->sk_collation,
1061 : leftarg->sk_argument,
1062 : rightarg->sk_argument));
1063 6 : return true;
1064 : }
1065 : }
1066 :
1067 : /* Can't make the comparison */
1068 0 : *result = false; /* suppress compiler warnings */
1069 0 : return false;
1070 : }
1071 :
1072 : /*
1073 : * Compare an array scan key to a scalar scan key, eliminating contradictory
1074 : * array elements such that the scalar scan key becomes redundant.
1075 : *
1076 : * If the opfamily is incomplete we may not be able to determine which
1077 : * elements are contradictory. When we return true we'll have validly set
1078 : * *qual_ok, guaranteeing that at least the scalar scan key can be considered
1079 : * redundant. We return false if the comparison could not be made (caller
1080 : * must keep both scan keys when this happens).
1081 : *
1082 : * Note: it's up to caller to deal with IS [NOT] NULL scan keys, as well as
1083 : * row comparison scan keys. We only deal with scalar scan keys.
1084 : */
1085 : static bool
1086 184 : _bt_compare_array_scankey_args(IndexScanDesc scan, ScanKey arraysk, ScanKey skey,
1087 : FmgrInfo *orderproc, BTArrayKeyInfo *array,
1088 : bool *qual_ok)
1089 : {
1090 : Assert(arraysk->sk_attno == skey->sk_attno);
1091 : Assert(!(arraysk->sk_flags & (SK_ISNULL | SK_ROW_HEADER | SK_ROW_MEMBER)));
1092 : Assert((arraysk->sk_flags & SK_SEARCHARRAY) &&
1093 : arraysk->sk_strategy == BTEqualStrategyNumber);
1094 : /* don't expect to have to deal with NULLs/row comparison scan keys */
1095 : Assert(!(skey->sk_flags & (SK_ISNULL | SK_ROW_HEADER | SK_ROW_MEMBER)));
1096 : Assert(!(skey->sk_flags & SK_SEARCHARRAY) ||
1097 : skey->sk_strategy != BTEqualStrategyNumber);
1098 :
1099 : /*
1100 : * Just call the appropriate helper function based on whether it's a SAOP
1101 : * array or a skip array. Both helpers will set *qual_ok in passing.
1102 : */
1103 184 : if (array->num_elems != -1)
1104 30 : return _bt_saoparray_shrink(scan, arraysk, skey, orderproc, array,
1105 : qual_ok);
1106 : else
1107 154 : return _bt_skiparray_shrink(scan, skey, array, qual_ok);
1108 : }
1109 :
1110 : /*
1111 : * Preprocessing of SAOP array scan key, used to determine which array
1112 : * elements are eliminated as contradictory by a non-array scalar key.
1113 : *
1114 : * _bt_compare_array_scankey_args helper function.
1115 : *
1116 : * Array elements can be eliminated as contradictory when excluded by some
1117 : * other operator on the same attribute. For example, with an index scan qual
1118 : * "WHERE a IN (1, 2, 3) AND a < 2", all array elements except the value "1"
1119 : * are eliminated, and the < scan key is eliminated as redundant. Cases where
1120 : * every array element is eliminated by a redundant scalar scan key have an
1121 : * unsatisfiable qual, which we handle by setting *qual_ok=false for caller.
1122 : */
1123 : static bool
1124 30 : _bt_saoparray_shrink(IndexScanDesc scan, ScanKey arraysk, ScanKey skey,
1125 : FmgrInfo *orderproc, BTArrayKeyInfo *array, bool *qual_ok)
1126 : {
1127 30 : Relation rel = scan->indexRelation;
1128 30 : Oid opcintype = rel->rd_opcintype[arraysk->sk_attno - 1];
1129 30 : int cmpresult = 0,
1130 30 : cmpexact = 0,
1131 : matchelem,
1132 30 : new_nelems = 0;
1133 : FmgrInfo crosstypeproc;
1134 30 : FmgrInfo *orderprocp = orderproc;
1135 :
1136 : Assert(array->num_elems > 0);
1137 : Assert(!(arraysk->sk_flags & SK_BT_SKIP));
1138 :
1139 : /*
1140 : * _bt_binsrch_array_skey searches an array for the entry best matching a
1141 : * datum of opclass input type for the index's attribute (on-disk type).
1142 : * We can reuse the array's ORDER proc whenever the non-array scan key's
1143 : * type is a match for the corresponding attribute's input opclass type.
1144 : * Otherwise, we have to do another ORDER proc lookup so that our call to
1145 : * _bt_binsrch_array_skey applies the correct comparator.
1146 : *
1147 : * Note: we have to support the convention that sk_subtype == InvalidOid
1148 : * means the opclass input type; this is a hack to simplify life for
1149 : * ScanKeyInit().
1150 : */
1151 30 : if (skey->sk_subtype != opcintype && skey->sk_subtype != InvalidOid)
1152 : {
1153 : RegProcedure cmp_proc;
1154 : Oid arraysk_elemtype;
1155 :
1156 : /*
1157 : * Need an ORDER proc lookup to detect redundancy/contradictoriness
1158 : * with this pair of scankeys.
1159 : *
1160 : * Scalar scan key's argument will be passed to _bt_compare_array_skey
1161 : * as its tupdatum/lefthand argument (rhs arg is for array elements).
1162 : */
1163 6 : arraysk_elemtype = arraysk->sk_subtype;
1164 6 : if (arraysk_elemtype == InvalidOid)
1165 0 : arraysk_elemtype = rel->rd_opcintype[arraysk->sk_attno - 1];
1166 6 : cmp_proc = get_opfamily_proc(rel->rd_opfamily[arraysk->sk_attno - 1],
1167 : skey->sk_subtype, arraysk_elemtype,
1168 : BTORDER_PROC);
1169 6 : if (!RegProcedureIsValid(cmp_proc))
1170 : {
1171 : /* Can't make the comparison */
1172 0 : *qual_ok = false; /* suppress compiler warnings */
1173 0 : return false;
1174 : }
1175 :
1176 : /* We have all we need to determine redundancy/contradictoriness */
1177 6 : orderprocp = &crosstypeproc;
1178 6 : fmgr_info(cmp_proc, orderprocp);
1179 : }
1180 :
1181 30 : matchelem = _bt_binsrch_array_skey(orderprocp, false,
1182 : NoMovementScanDirection,
1183 : skey->sk_argument, false, array,
1184 : arraysk, &cmpresult);
1185 :
1186 30 : switch (skey->sk_strategy)
1187 : {
1188 6 : case BTLessStrategyNumber:
1189 6 : cmpexact = 1; /* exclude exact match, if any */
1190 : /* FALL THRU */
1191 6 : case BTLessEqualStrategyNumber:
1192 6 : if (cmpresult >= cmpexact)
1193 0 : matchelem++;
1194 : /* Resize, keeping elements from the start of the array */
1195 6 : new_nelems = matchelem;
1196 6 : break;
1197 12 : case BTEqualStrategyNumber:
1198 12 : if (cmpresult != 0)
1199 : {
1200 : /* qual is unsatisfiable */
1201 6 : new_nelems = 0;
1202 : }
1203 : else
1204 : {
1205 : /* Shift matching element to the start of the array, resize */
1206 6 : array->elem_values[0] = array->elem_values[matchelem];
1207 6 : new_nelems = 1;
1208 : }
1209 12 : break;
1210 6 : case BTGreaterEqualStrategyNumber:
1211 6 : cmpexact = 1; /* include exact match, if any */
1212 : /* FALL THRU */
1213 12 : case BTGreaterStrategyNumber:
1214 12 : if (cmpresult >= cmpexact)
1215 6 : matchelem++;
1216 : /* Shift matching elements to the start of the array, resize */
1217 12 : new_nelems = array->num_elems - matchelem;
1218 12 : memmove(array->elem_values, array->elem_values + matchelem,
1219 : sizeof(Datum) * new_nelems);
1220 12 : break;
1221 0 : default:
1222 0 : elog(ERROR, "unrecognized StrategyNumber: %d",
1223 : (int) skey->sk_strategy);
1224 : break;
1225 : }
1226 :
1227 : Assert(new_nelems >= 0);
1228 : Assert(new_nelems <= array->num_elems);
1229 :
1230 30 : array->num_elems = new_nelems;
1231 30 : *qual_ok = new_nelems > 0;
1232 :
1233 30 : return true;
1234 : }
1235 :
1236 : /*
1237 : * Preprocessing of skip array scan key, used to determine redundancy against
1238 : * a non-array scalar scan key (must be an inequality).
1239 : *
1240 : * _bt_compare_array_scankey_args helper function.
1241 : *
1242 : * Skip arrays work by procedurally generating their elements as needed, so we
1243 : * just store the inequality as the skip array's low_compare or high_compare
1244 : * (except when there's already a more restrictive low_compare/high_compare).
1245 : * The array's final elements are the range of values that still satisfy the
1246 : * array's final low_compare and high_compare.
1247 : */
1248 : static bool
1249 154 : _bt_skiparray_shrink(IndexScanDesc scan, ScanKey skey, BTArrayKeyInfo *array,
1250 : bool *qual_ok)
1251 : {
1252 : bool test_result;
1253 :
1254 : Assert(array->num_elems == -1);
1255 :
1256 : /*
1257 : * Array's index attribute will be constrained by a strict operator/key.
1258 : * Array must not "contain a NULL element" (i.e. the scan must not apply
1259 : * "IS NULL" qual when it reaches the end of the index that stores NULLs).
1260 : */
1261 154 : array->null_elem = false;
1262 154 : *qual_ok = true;
1263 :
1264 : /*
1265 : * Consider if we should treat caller's scalar scan key as the skip
1266 : * array's high_compare or low_compare.
1267 : *
1268 : * In general the current array element must either be a copy of a value
1269 : * taken from an index tuple, or a derivative value generated by opclass's
1270 : * skip support function. That way the scan can always safely assume that
1271 : * it's okay to use the only-input-opclass-type proc from so->orderProcs[]
1272 : * (they can be cross-type with SAOP arrays, but never with skip arrays).
1273 : *
1274 : * This approach is enabled by MINVAL/MAXVAL sentinel key markings, which
1275 : * can be thought of as representing either the lowest or highest matching
1276 : * array element (excluding the NULL element, where applicable, though as
1277 : * just discussed it isn't applicable to this range skip array anyway).
1278 : * Array keys marked MINVAL/MAXVAL never have a valid datum in their
1279 : * sk_argument field. The scan directly applies the array's low_compare
1280 : * key when it encounters MINVAL in the array key proper (just as it
1281 : * applies high_compare when it sees MAXVAL set in the array key proper).
1282 : * The scan must never use the array's so->orderProcs[] proc against
1283 : * low_compare's/high_compare's sk_argument, either (so->orderProcs[] is
1284 : * only intended to be used with rhs datums from the array proper/index).
1285 : */
1286 154 : switch (skey->sk_strategy)
1287 : {
1288 80 : case BTLessStrategyNumber:
1289 : case BTLessEqualStrategyNumber:
1290 80 : if (array->high_compare)
1291 : {
1292 : /* replace existing high_compare with caller's key? */
1293 6 : if (!_bt_compare_scankey_args(scan, array->high_compare, skey,
1294 : array->high_compare, NULL, NULL,
1295 : &test_result))
1296 0 : return false; /* can't determine more restrictive key */
1297 :
1298 6 : if (!test_result)
1299 6 : return true; /* no, just discard caller's key */
1300 :
1301 : /* yes, replace existing high_compare with caller's key */
1302 : }
1303 :
1304 : /* caller's key becomes skip array's high_compare */
1305 74 : array->high_compare = skey;
1306 74 : break;
1307 74 : case BTGreaterEqualStrategyNumber:
1308 : case BTGreaterStrategyNumber:
1309 74 : if (array->low_compare)
1310 : {
1311 : /* replace existing low_compare with caller's key? */
1312 6 : if (!_bt_compare_scankey_args(scan, array->low_compare, skey,
1313 : array->low_compare, NULL, NULL,
1314 : &test_result))
1315 0 : return false; /* can't determine more restrictive key */
1316 :
1317 6 : if (!test_result)
1318 0 : return true; /* no, just discard caller's key */
1319 :
1320 : /* yes, replace existing low_compare with caller's key */
1321 : }
1322 :
1323 : /* caller's key becomes skip array's low_compare */
1324 74 : array->low_compare = skey;
1325 74 : break;
1326 0 : case BTEqualStrategyNumber:
1327 : default:
1328 0 : elog(ERROR, "unrecognized StrategyNumber: %d",
1329 : (int) skey->sk_strategy);
1330 : break;
1331 : }
1332 :
1333 148 : return true;
1334 : }
1335 :
1336 : /*
1337 : * Applies the opfamily's skip support routine to convert the skip array's >
1338 : * low_compare key (if any) into a >= key, and to convert its < high_compare
1339 : * key (if any) into a <= key. Decrements the high_compare key's sk_argument,
1340 : * and/or increments the low_compare key's sk_argument (also adjusts their
1341 : * operator strategies, while changing the operator as appropriate).
1342 : *
1343 : * This optional optimization reduces the number of descents required within
1344 : * _bt_first. Whenever _bt_first is called with a skip array whose current
1345 : * array element is the sentinel value MINVAL, using a transformed >= key
1346 : * instead of using the original > key makes it safe to include lower-order
1347 : * scan keys in the insertion scan key (there must be lower-order scan keys
1348 : * after the skip array). We will avoid an extra _bt_first to find the first
1349 : * value in the index > sk_argument -- at least when the first real matching
1350 : * value in the index happens to be an exact match for the sk_argument value
1351 : * that we produced here by incrementing the original input key's sk_argument.
1352 : * (Backwards scans derive the same benefit when they encounter the sentinel
1353 : * value MAXVAL, by converting the high_compare key from < to <=.)
1354 : *
1355 : * Note: The transformation is only correct when it cannot allow the scan to
1356 : * overlook matching tuples, but we don't have enough semantic information to
1357 : * safely make sure that can't happen during scans with cross-type operators.
1358 : * That's why we'll never apply the transformation in cross-type scenarios.
1359 : * For example, if we attempted to convert "sales_ts > '2024-01-01'::date"
1360 : * into "sales_ts >= '2024-01-02'::date" given a "sales_ts" attribute whose
1361 : * input opclass is timestamp_ops, the scan would overlook almost all (or all)
1362 : * tuples for sales that fell on '2024-01-01'.
1363 : *
1364 : * Note: We can safely modify array->low_compare/array->high_compare in place
1365 : * because they just point to copies of our scan->keyData[] input scan keys
1366 : * (namely the copies returned by _bt_preprocess_array_keys to be used as
1367 : * input into the standard preprocessing steps in _bt_preprocess_keys).
1368 : * Everything will be reset if there's a rescan.
1369 : */
1370 : static void
1371 78 : _bt_skiparray_strat_adjust(IndexScanDesc scan, ScanKey arraysk,
1372 : BTArrayKeyInfo *array)
1373 : {
1374 78 : BTScanOpaque so = (BTScanOpaque) scan->opaque;
1375 : MemoryContext oldContext;
1376 :
1377 : /*
1378 : * Called last among all preprocessing steps, when the skip array's final
1379 : * low_compare and high_compare have both been chosen
1380 : */
1381 : Assert(arraysk->sk_flags & SK_BT_SKIP);
1382 : Assert(array->num_elems == -1 && !array->null_elem && array->sksup);
1383 :
1384 78 : oldContext = MemoryContextSwitchTo(so->arrayContext);
1385 :
1386 78 : if (array->high_compare &&
1387 24 : array->high_compare->sk_strategy == BTLessStrategyNumber)
1388 18 : _bt_skiparray_strat_decrement(scan, arraysk, array);
1389 :
1390 78 : if (array->low_compare &&
1391 18 : array->low_compare->sk_strategy == BTGreaterStrategyNumber)
1392 12 : _bt_skiparray_strat_increment(scan, arraysk, array);
1393 :
1394 78 : MemoryContextSwitchTo(oldContext);
1395 78 : }
1396 :
1397 : /*
1398 : * Convert skip array's > low_compare key into a >= key
1399 : */
1400 : static void
1401 18 : _bt_skiparray_strat_decrement(IndexScanDesc scan, ScanKey arraysk,
1402 : BTArrayKeyInfo *array)
1403 : {
1404 18 : Relation rel = scan->indexRelation;
1405 18 : Oid opfamily = rel->rd_opfamily[arraysk->sk_attno - 1],
1406 18 : opcintype = rel->rd_opcintype[arraysk->sk_attno - 1],
1407 : leop;
1408 : RegProcedure cmp_proc;
1409 18 : ScanKey high_compare = array->high_compare;
1410 18 : Datum orig_sk_argument = high_compare->sk_argument,
1411 : new_sk_argument;
1412 : bool uflow;
1413 :
1414 : Assert(high_compare->sk_strategy == BTLessStrategyNumber);
1415 :
1416 : /*
1417 : * Only perform the transformation when the operator type matches the
1418 : * index attribute's input opclass type
1419 : */
1420 18 : if (high_compare->sk_subtype != opcintype &&
1421 0 : high_compare->sk_subtype != InvalidOid)
1422 0 : return;
1423 :
1424 : /* Decrement, handling underflow by marking the qual unsatisfiable */
1425 18 : new_sk_argument = array->sksup->decrement(rel, orig_sk_argument, &uflow);
1426 18 : if (uflow)
1427 : {
1428 0 : BTScanOpaque so = (BTScanOpaque) scan->opaque;
1429 :
1430 0 : so->qual_ok = false;
1431 0 : return;
1432 : }
1433 :
1434 : /* Look up <= operator (might fail) */
1435 18 : leop = get_opfamily_member(opfamily, opcintype, opcintype,
1436 : BTLessEqualStrategyNumber);
1437 18 : if (!OidIsValid(leop))
1438 0 : return;
1439 18 : cmp_proc = get_opcode(leop);
1440 18 : if (RegProcedureIsValid(cmp_proc))
1441 : {
1442 : /* Transform < high_compare key into <= key */
1443 18 : fmgr_info(cmp_proc, &high_compare->sk_func);
1444 18 : high_compare->sk_argument = new_sk_argument;
1445 18 : high_compare->sk_strategy = BTLessEqualStrategyNumber;
1446 : }
1447 : }
1448 :
1449 : /*
1450 : * Convert skip array's < low_compare key into a <= key
1451 : */
1452 : static void
1453 12 : _bt_skiparray_strat_increment(IndexScanDesc scan, ScanKey arraysk,
1454 : BTArrayKeyInfo *array)
1455 : {
1456 12 : Relation rel = scan->indexRelation;
1457 12 : Oid opfamily = rel->rd_opfamily[arraysk->sk_attno - 1],
1458 12 : opcintype = rel->rd_opcintype[arraysk->sk_attno - 1],
1459 : geop;
1460 : RegProcedure cmp_proc;
1461 12 : ScanKey low_compare = array->low_compare;
1462 12 : Datum orig_sk_argument = low_compare->sk_argument,
1463 : new_sk_argument;
1464 : bool oflow;
1465 :
1466 : Assert(low_compare->sk_strategy == BTGreaterStrategyNumber);
1467 :
1468 : /*
1469 : * Only perform the transformation when the operator type matches the
1470 : * index attribute's input opclass type
1471 : */
1472 12 : if (low_compare->sk_subtype != opcintype &&
1473 0 : low_compare->sk_subtype != InvalidOid)
1474 0 : return;
1475 :
1476 : /* Increment, handling overflow by marking the qual unsatisfiable */
1477 12 : new_sk_argument = array->sksup->increment(rel, orig_sk_argument, &oflow);
1478 12 : if (oflow)
1479 : {
1480 0 : BTScanOpaque so = (BTScanOpaque) scan->opaque;
1481 :
1482 0 : so->qual_ok = false;
1483 0 : return;
1484 : }
1485 :
1486 : /* Look up >= operator (might fail) */
1487 12 : geop = get_opfamily_member(opfamily, opcintype, opcintype,
1488 : BTGreaterEqualStrategyNumber);
1489 12 : if (!OidIsValid(geop))
1490 0 : return;
1491 12 : cmp_proc = get_opcode(geop);
1492 12 : if (RegProcedureIsValid(cmp_proc))
1493 : {
1494 : /* Transform > low_compare key into >= key */
1495 12 : fmgr_info(cmp_proc, &low_compare->sk_func);
1496 12 : low_compare->sk_argument = new_sk_argument;
1497 12 : low_compare->sk_strategy = BTGreaterEqualStrategyNumber;
1498 : }
1499 : }
1500 :
1501 : /*
1502 : * _bt_unmark_keys() -- make superfluous required keys nonrequired after all
1503 : *
1504 : * When _bt_preprocess_keys fails to eliminate one or more redundant keys, it
1505 : * calls here to make sure that no index attribute has more than one > or >=
1506 : * key marked required, and no more than one required < or <= key. Attributes
1507 : * with = keys will always get one = key as their required key. All other
1508 : * keys that were initially marked required get "unmarked" here. That way,
1509 : * _bt_first and _bt_checkkeys will reliably agree on which keys to use to
1510 : * start and/or to end the scan.
1511 : *
1512 : * We also relocate keys that become/started out nonrequired to the end of
1513 : * so->keyData[]. That way, _bt_first and _bt_checkkeys cannot fail to reach
1514 : * a required key due to some earlier nonrequired key getting in the way.
1515 : *
1516 : * Only call here when _bt_compare_scankey_args returned false at least once
1517 : * (otherwise, calling here will just waste cycles).
1518 : */
1519 : static void
1520 6 : _bt_unmark_keys(IndexScanDesc scan, int *keyDataMap)
1521 : {
1522 6 : BTScanOpaque so = (BTScanOpaque) scan->opaque;
1523 : AttrNumber attno;
1524 : bool *unmarkikey;
1525 : int nunmark,
1526 : nunmarked,
1527 : nkept,
1528 : firsti;
1529 : ScanKey keepKeys,
1530 : unmarkKeys;
1531 6 : FmgrInfo *keepOrderProcs = NULL,
1532 6 : *unmarkOrderProcs = NULL;
1533 : bool haveReqEquals,
1534 : haveReqForward,
1535 : haveReqBackward;
1536 :
1537 : /*
1538 : * Do an initial pass over so->keyData[] that determines which keys to
1539 : * keep as required. We expect so->keyData[] to still be in attribute
1540 : * order when we're called (though we don't expect any particular order
1541 : * among each attribute's keys).
1542 : *
1543 : * When both equality and inequality keys remain on a single attribute, we
1544 : * *must* make sure that exactly one of the equalities remains required.
1545 : * Any requiredness markings that we might leave on later keys/attributes
1546 : * are predicated on there being required = keys on all prior columns.
1547 : */
1548 6 : unmarkikey = palloc0(so->numberOfKeys * sizeof(bool));
1549 6 : nunmark = 0;
1550 :
1551 : /* Set things up for first key's attribute */
1552 6 : attno = so->keyData[0].sk_attno;
1553 6 : firsti = 0;
1554 6 : haveReqEquals = false;
1555 6 : haveReqForward = false;
1556 6 : haveReqBackward = false;
1557 30 : for (int i = 0; i < so->numberOfKeys; i++)
1558 : {
1559 24 : ScanKey origkey = &so->keyData[i];
1560 :
1561 24 : if (origkey->sk_attno != attno)
1562 : {
1563 : /* Reset for next attribute */
1564 12 : attno = origkey->sk_attno;
1565 12 : firsti = i;
1566 :
1567 12 : haveReqEquals = false;
1568 12 : haveReqForward = false;
1569 12 : haveReqBackward = false;
1570 : }
1571 :
1572 : /* Equalities get priority over inequalities */
1573 24 : if (haveReqEquals)
1574 : {
1575 : /*
1576 : * We already found the first "=" key for this attribute. We've
1577 : * already decided that all its other keys will be unmarked.
1578 : */
1579 : Assert(!(origkey->sk_flags & SK_SEARCHNULL));
1580 0 : unmarkikey[i] = true;
1581 0 : nunmark++;
1582 0 : continue;
1583 : }
1584 24 : else if ((origkey->sk_flags & SK_BT_REQFWD) &&
1585 18 : (origkey->sk_flags & SK_BT_REQBKWD))
1586 : {
1587 : /*
1588 : * Found the first "=" key for attno. All other attno keys will
1589 : * be unmarked.
1590 : */
1591 : Assert(origkey->sk_strategy == BTEqualStrategyNumber);
1592 :
1593 18 : haveReqEquals = true;
1594 24 : for (int j = firsti; j < i; j++)
1595 : {
1596 : /* Unmark any prior inequality keys on attno after all */
1597 6 : if (!unmarkikey[j])
1598 : {
1599 6 : unmarkikey[j] = true;
1600 6 : nunmark++;
1601 : }
1602 : }
1603 18 : continue;
1604 : }
1605 :
1606 : /* Deal with inequalities next */
1607 6 : if ((origkey->sk_flags & SK_BT_REQFWD) && !haveReqForward)
1608 : {
1609 0 : haveReqForward = true;
1610 0 : continue;
1611 : }
1612 6 : else if ((origkey->sk_flags & SK_BT_REQBKWD) && !haveReqBackward)
1613 : {
1614 6 : haveReqBackward = true;
1615 6 : continue;
1616 : }
1617 :
1618 : /*
1619 : * We have either a redundant inequality key that will be unmarked, or
1620 : * we have a key that wasn't marked required in the first place
1621 : */
1622 0 : unmarkikey[i] = true;
1623 0 : nunmark++;
1624 : }
1625 :
1626 : /* Should only be called when _bt_compare_scankey_args reported failure */
1627 : Assert(nunmark > 0);
1628 :
1629 : /*
1630 : * Next, allocate temp arrays: one for required keys that'll remain
1631 : * required, the other for all remaining keys
1632 : */
1633 6 : unmarkKeys = palloc(nunmark * sizeof(ScanKeyData));
1634 6 : keepKeys = palloc((so->numberOfKeys - nunmark) * sizeof(ScanKeyData));
1635 6 : nunmarked = 0;
1636 6 : nkept = 0;
1637 6 : if (so->numArrayKeys)
1638 : {
1639 6 : unmarkOrderProcs = palloc(nunmark * sizeof(FmgrInfo));
1640 6 : keepOrderProcs = palloc((so->numberOfKeys - nunmark) * sizeof(FmgrInfo));
1641 : }
1642 :
1643 : /*
1644 : * Next, copy the contents of so->keyData[] into the appropriate temp
1645 : * array.
1646 : *
1647 : * Scans with = array keys need us to maintain invariants around the order
1648 : * of so->orderProcs[] and so->arrayKeys[] relative to so->keyData[]. See
1649 : * _bt_preprocess_array_keys_final for a full explanation.
1650 : */
1651 30 : for (int i = 0; i < so->numberOfKeys; i++)
1652 : {
1653 24 : ScanKey origkey = &so->keyData[i];
1654 : ScanKey unmark;
1655 :
1656 24 : if (!unmarkikey[i])
1657 : {
1658 : /*
1659 : * Key gets to keep its original requiredness markings.
1660 : *
1661 : * Key will stay in its original position, unless we're going to
1662 : * unmark an earlier key (in which case this key gets moved back).
1663 : */
1664 18 : memcpy(keepKeys + nkept, origkey, sizeof(ScanKeyData));
1665 :
1666 18 : if (so->numArrayKeys)
1667 : {
1668 18 : keyDataMap[i] = nkept;
1669 18 : memcpy(keepOrderProcs + nkept, &so->orderProcs[i],
1670 : sizeof(FmgrInfo));
1671 : }
1672 :
1673 18 : nkept++;
1674 18 : continue;
1675 : }
1676 :
1677 : /*
1678 : * Key will be unmarked as needed, and moved to the end of the array,
1679 : * next to other keys that will become (or always were) nonrequired
1680 : */
1681 6 : unmark = unmarkKeys + nunmarked;
1682 6 : memcpy(unmark, origkey, sizeof(ScanKeyData));
1683 :
1684 6 : if (so->numArrayKeys)
1685 : {
1686 6 : keyDataMap[i] = (so->numberOfKeys - nunmark) + nunmarked;
1687 6 : memcpy(&unmarkOrderProcs[nunmarked], &so->orderProcs[i],
1688 : sizeof(FmgrInfo));
1689 : }
1690 :
1691 : /*
1692 : * Preprocessing only generates skip arrays when it knows that they'll
1693 : * be the only required = key on the attr. We'll never unmark them.
1694 : */
1695 : Assert(!(unmark->sk_flags & SK_BT_SKIP));
1696 :
1697 : /*
1698 : * Also shouldn't have to unmark an IS NULL or an IS NOT NULL key.
1699 : * They aren't cross-type, so an incomplete opfamily can't matter.
1700 : */
1701 : Assert(!(unmark->sk_flags & SK_ISNULL) ||
1702 : !(unmark->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD)));
1703 :
1704 : /* Clear requiredness flags on redundant key (and on any subkeys) */
1705 6 : unmark->sk_flags &= ~(SK_BT_REQFWD | SK_BT_REQBKWD);
1706 6 : if (unmark->sk_flags & SK_ROW_HEADER)
1707 : {
1708 6 : ScanKey subkey = (ScanKey) DatumGetPointer(unmark->sk_argument);
1709 :
1710 : Assert(subkey->sk_strategy == unmark->sk_strategy);
1711 : for (;;)
1712 : {
1713 6 : Assert(subkey->sk_flags & SK_ROW_MEMBER);
1714 12 : subkey->sk_flags &= ~(SK_BT_REQFWD | SK_BT_REQBKWD);
1715 12 : if (subkey->sk_flags & SK_ROW_END)
1716 6 : break;
1717 6 : subkey++;
1718 : }
1719 : }
1720 :
1721 6 : nunmarked++;
1722 : }
1723 :
1724 : /* Copy both temp arrays back into so->keyData[] to reorder */
1725 : Assert(nkept == so->numberOfKeys - nunmark);
1726 : Assert(nunmarked == nunmark);
1727 6 : memcpy(so->keyData, keepKeys, sizeof(ScanKeyData) * nkept);
1728 6 : memcpy(so->keyData + nkept, unmarkKeys, sizeof(ScanKeyData) * nunmarked);
1729 :
1730 : /* Done with temp arrays */
1731 6 : pfree(unmarkikey);
1732 6 : pfree(keepKeys);
1733 6 : pfree(unmarkKeys);
1734 :
1735 : /*
1736 : * Now copy so->orderProcs[] temp entries needed by scans with = array
1737 : * keys back (just like with the so->keyData[] temp arrays)
1738 : */
1739 6 : if (so->numArrayKeys)
1740 : {
1741 6 : memcpy(so->orderProcs, keepOrderProcs, sizeof(FmgrInfo) * nkept);
1742 6 : memcpy(so->orderProcs + nkept, unmarkOrderProcs,
1743 : sizeof(FmgrInfo) * nunmarked);
1744 :
1745 : /* Also fix-up array->scan_key references */
1746 18 : for (int arridx = 0; arridx < so->numArrayKeys; arridx++)
1747 : {
1748 12 : BTArrayKeyInfo *array = &so->arrayKeys[arridx];
1749 :
1750 12 : array->scan_key = keyDataMap[array->scan_key];
1751 : }
1752 :
1753 : /*
1754 : * Sort so->arrayKeys[] based on its new BTArrayKeyInfo.scan_key
1755 : * offsets, so that its order matches so->keyData[] order as expected
1756 : */
1757 6 : qsort(so->arrayKeys, so->numArrayKeys, sizeof(BTArrayKeyInfo),
1758 : _bt_reorder_array_cmp);
1759 :
1760 : /* Done with temp arrays */
1761 6 : pfree(unmarkOrderProcs);
1762 6 : pfree(keepOrderProcs);
1763 : }
1764 6 : }
1765 :
1766 : /*
1767 : * qsort comparator for reordering so->arrayKeys[] BTArrayKeyInfo entries
1768 : */
1769 : static int
1770 6 : _bt_reorder_array_cmp(const void *a, const void *b)
1771 : {
1772 6 : BTArrayKeyInfo *arraya = (BTArrayKeyInfo *) a;
1773 6 : BTArrayKeyInfo *arrayb = (BTArrayKeyInfo *) b;
1774 :
1775 6 : return pg_cmp_s32(arraya->scan_key, arrayb->scan_key);
1776 : }
1777 :
1778 : /*
1779 : * _bt_preprocess_array_keys() -- Preprocess SK_SEARCHARRAY scan keys
1780 : *
1781 : * If there are any SK_SEARCHARRAY scan keys, deconstruct the array(s) and
1782 : * set up BTArrayKeyInfo info for each one that is an equality-type key.
1783 : * Returns modified scan keys as input for further, standard preprocessing.
1784 : *
1785 : * Currently we perform two kinds of preprocessing to deal with redundancies.
1786 : * For inequality array keys, it's sufficient to find the extreme element
1787 : * value and replace the whole array with that scalar value. This eliminates
1788 : * all but one array element as redundant. Similarly, we are capable of
1789 : * "merging together" multiple equality array keys (from two or more input
1790 : * scan keys) into a single output scan key containing only the intersecting
1791 : * array elements. This can eliminate many redundant array elements, as well
1792 : * as eliminating whole array scan keys as redundant. It can also allow us to
1793 : * detect contradictory quals.
1794 : *
1795 : * Caller must pass *new_numberOfKeys to give us a way to change the number of
1796 : * scan keys that caller treats as input to standard preprocessing steps. The
1797 : * returned array is smaller than scan->keyData[] when we could eliminate a
1798 : * redundant array scan key (redundant with another array scan key). It is
1799 : * convenient for _bt_preprocess_keys caller to have to deal with no more than
1800 : * one equality strategy array scan key per index attribute. We'll always be
1801 : * able to set things up that way when complete opfamilies are used.
1802 : *
1803 : * We're also responsible for generating skip arrays (and their associated
1804 : * scan keys) here. This enables skip scan. We do this for index attributes
1805 : * that initially lacked an equality condition within scan->keyData[], iff
1806 : * doing so allows a later scan key (that was passed to us in scan->keyData[])
1807 : * to be marked required by our _bt_preprocess_keys caller.
1808 : *
1809 : * We set the scan key references from the scan's BTArrayKeyInfo info array to
1810 : * offsets into the temp modified input array returned to caller. Scans that
1811 : * have array keys should call _bt_preprocess_array_keys_final when standard
1812 : * preprocessing steps are complete. This will convert the scan key offset
1813 : * references into references to the scan's so->keyData[] output scan keys.
1814 : *
1815 : * Note: the reason we need to return a temp scan key array, rather than just
1816 : * modifying scan->keyData[], is that callers are permitted to call btrescan
1817 : * without supplying a new set of scankey data. Certain other preprocessing
1818 : * routines (e.g., _bt_fix_scankey_strategy) _can_ modify scan->keyData[], but
1819 : * we can't make that work here because our modifications are non-idempotent.
1820 : */
1821 : static ScanKey
1822 16054876 : _bt_preprocess_array_keys(IndexScanDesc scan, int *new_numberOfKeys)
1823 : {
1824 16054876 : BTScanOpaque so = (BTScanOpaque) scan->opaque;
1825 16054876 : Relation rel = scan->indexRelation;
1826 16054876 : int16 *indoption = rel->rd_indoption;
1827 : Oid skip_eq_ops[INDEX_MAX_KEYS];
1828 : int numArrayKeys,
1829 : numSkipArrayKeys,
1830 : numArrayKeyData;
1831 16054876 : AttrNumber attno_skip = 1;
1832 16054876 : int origarrayatt = InvalidAttrNumber,
1833 16054876 : origarraykey = -1;
1834 16054876 : Oid origelemtype = InvalidOid;
1835 : MemoryContext oldContext;
1836 : ScanKey arrayKeyData; /* modified copy of scan->keyData */
1837 :
1838 : /*
1839 : * Check the number of input array keys within scan->keyData[] input keys
1840 : * (also checks if we should add extra skip arrays based on input keys)
1841 : */
1842 16054876 : numArrayKeys = _bt_num_array_keys(scan, skip_eq_ops, &numSkipArrayKeys);
1843 :
1844 : /* Quit if nothing to do. */
1845 16054876 : if (numArrayKeys == 0)
1846 15983882 : return NULL;
1847 :
1848 : /*
1849 : * Estimated final size of arrayKeyData[] array we'll return to our caller
1850 : * is the size of the original scan->keyData[] input array, plus space for
1851 : * any additional skip array scan keys we'll need to generate below
1852 : */
1853 70994 : numArrayKeyData = scan->numberOfKeys + numSkipArrayKeys;
1854 :
1855 : /*
1856 : * Make a scan-lifespan context to hold array-associated data, or reset it
1857 : * if we already have one from a previous rescan cycle.
1858 : */
1859 70994 : if (so->arrayContext == NULL)
1860 4504 : so->arrayContext = AllocSetContextCreate(CurrentMemoryContext,
1861 : "BTree array context",
1862 : ALLOCSET_SMALL_SIZES);
1863 : else
1864 66490 : MemoryContextReset(so->arrayContext);
1865 :
1866 70994 : oldContext = MemoryContextSwitchTo(so->arrayContext);
1867 :
1868 : /* Create output scan keys in the workspace context */
1869 70994 : arrayKeyData = (ScanKey) palloc(numArrayKeyData * sizeof(ScanKeyData));
1870 :
1871 : /* Allocate space for per-array data in the workspace context */
1872 70994 : so->skipScan = (numSkipArrayKeys > 0);
1873 70994 : so->arrayKeys = (BTArrayKeyInfo *) palloc(numArrayKeys * sizeof(BTArrayKeyInfo));
1874 :
1875 : /* Allocate space for ORDER procs used to help _bt_checkkeys */
1876 70994 : so->orderProcs = (FmgrInfo *) palloc(numArrayKeyData * sizeof(FmgrInfo));
1877 :
1878 70994 : numArrayKeys = 0;
1879 70994 : numArrayKeyData = 0;
1880 143632 : for (int input_ikey = 0; input_ikey < scan->numberOfKeys; input_ikey++)
1881 : {
1882 72656 : ScanKey inkey = scan->keyData + input_ikey,
1883 : cur;
1884 : FmgrInfo sortproc;
1885 72656 : FmgrInfo *sortprocp = &sortproc;
1886 : Oid elemtype;
1887 : bool reverse;
1888 : ArrayType *arrayval;
1889 : int16 elmlen;
1890 : bool elmbyval;
1891 : char elmalign;
1892 : int num_elems;
1893 : Datum *elem_values;
1894 : bool *elem_nulls;
1895 : int num_nonnulls;
1896 :
1897 : /* set up next output scan key */
1898 72656 : cur = &arrayKeyData[numArrayKeyData];
1899 :
1900 : /* Backfill skip arrays for attrs < or <= input key's attr? */
1901 76374 : while (numSkipArrayKeys && attno_skip <= inkey->sk_attno)
1902 : {
1903 4544 : Oid opfamily = rel->rd_opfamily[attno_skip - 1];
1904 4544 : Oid opcintype = rel->rd_opcintype[attno_skip - 1];
1905 4544 : Oid collation = rel->rd_indcollation[attno_skip - 1];
1906 4544 : Oid eq_op = skip_eq_ops[attno_skip - 1];
1907 : CompactAttribute *attr;
1908 : RegProcedure cmp_proc;
1909 :
1910 4544 : if (!OidIsValid(eq_op))
1911 : {
1912 : /*
1913 : * Attribute already has an = input key, so don't output a
1914 : * skip array for attno_skip. Just copy attribute's = input
1915 : * key into arrayKeyData[] once outside this inner loop.
1916 : *
1917 : * Note: When we get here there must be a later attribute that
1918 : * lacks an equality input key, and still needs a skip array
1919 : * (if there wasn't then numSkipArrayKeys would be 0 by now).
1920 : */
1921 : Assert(attno_skip == inkey->sk_attno);
1922 : /* inkey can't be last input key to be marked required: */
1923 : Assert(input_ikey < scan->numberOfKeys - 1);
1924 : #if 0
1925 : /* Could be a redundant input scan key, so can't do this: */
1926 : Assert(inkey->sk_strategy == BTEqualStrategyNumber ||
1927 : (inkey->sk_flags & SK_SEARCHNULL));
1928 : #endif
1929 :
1930 826 : attno_skip++;
1931 826 : break;
1932 : }
1933 :
1934 3718 : cmp_proc = get_opcode(eq_op);
1935 3718 : if (!RegProcedureIsValid(cmp_proc))
1936 0 : elog(ERROR, "missing oprcode for skipping equals operator %u", eq_op);
1937 :
1938 3718 : ScanKeyEntryInitialize(cur,
1939 : SK_SEARCHARRAY | SK_BT_SKIP, /* flags */
1940 : attno_skip, /* skipped att number */
1941 : BTEqualStrategyNumber, /* equality strategy */
1942 : InvalidOid, /* opclass input subtype */
1943 : collation, /* index column's collation */
1944 : cmp_proc, /* equality operator's proc */
1945 : (Datum) 0); /* constant */
1946 :
1947 : /* Initialize generic BTArrayKeyInfo fields */
1948 3718 : so->arrayKeys[numArrayKeys].scan_key = numArrayKeyData;
1949 3718 : so->arrayKeys[numArrayKeys].num_elems = -1;
1950 :
1951 : /* Initialize skip array specific BTArrayKeyInfo fields */
1952 3718 : attr = TupleDescCompactAttr(RelationGetDescr(rel), attno_skip - 1);
1953 3718 : reverse = (indoption[attno_skip - 1] & INDOPTION_DESC) != 0;
1954 3718 : so->arrayKeys[numArrayKeys].attlen = attr->attlen;
1955 3718 : so->arrayKeys[numArrayKeys].attbyval = attr->attbyval;
1956 3718 : so->arrayKeys[numArrayKeys].null_elem = true; /* for now */
1957 7436 : so->arrayKeys[numArrayKeys].sksup =
1958 3718 : PrepareSkipSupportFromOpclass(opfamily, opcintype, reverse);
1959 3718 : so->arrayKeys[numArrayKeys].low_compare = NULL; /* for now */
1960 3718 : so->arrayKeys[numArrayKeys].high_compare = NULL; /* for now */
1961 :
1962 : /*
1963 : * We'll need a 3-way ORDER proc. Set that up now.
1964 : */
1965 3718 : _bt_setup_array_cmp(scan, cur, opcintype,
1966 3718 : &so->orderProcs[numArrayKeyData], NULL);
1967 :
1968 3718 : numArrayKeys++;
1969 3718 : numArrayKeyData++; /* keep this scan key/array */
1970 :
1971 : /* set up next output scan key */
1972 3718 : cur = &arrayKeyData[numArrayKeyData];
1973 :
1974 : /* remember having output this skip array and scan key */
1975 3718 : numSkipArrayKeys--;
1976 3718 : attno_skip++;
1977 : }
1978 :
1979 : /*
1980 : * Provisionally copy scan key into arrayKeyData[] array we'll return
1981 : * to _bt_preprocess_keys caller
1982 : */
1983 72656 : *cur = *inkey;
1984 :
1985 72656 : if (!(cur->sk_flags & SK_SEARCHARRAY))
1986 : {
1987 5054 : numArrayKeyData++; /* keep this non-array scan key */
1988 5072 : continue;
1989 : }
1990 :
1991 : /*
1992 : * Process SAOP array scan key
1993 : */
1994 : Assert(!(cur->sk_flags & (SK_ROW_HEADER | SK_SEARCHNULL | SK_SEARCHNOTNULL)));
1995 :
1996 : /* If array is null as a whole, the scan qual is unsatisfiable */
1997 67602 : if (cur->sk_flags & SK_ISNULL)
1998 : {
1999 6 : so->qual_ok = false;
2000 18 : break;
2001 : }
2002 :
2003 : /*
2004 : * Deconstruct the array into elements
2005 : */
2006 67596 : arrayval = DatumGetArrayTypeP(cur->sk_argument);
2007 : /* We could cache this data, but not clear it's worth it */
2008 67596 : get_typlenbyvalalign(ARR_ELEMTYPE(arrayval),
2009 : &elmlen, &elmbyval, &elmalign);
2010 67596 : deconstruct_array(arrayval,
2011 : ARR_ELEMTYPE(arrayval),
2012 : elmlen, elmbyval, elmalign,
2013 : &elem_values, &elem_nulls, &num_elems);
2014 :
2015 : /*
2016 : * Compress out any null elements. We can ignore them since we assume
2017 : * all btree operators are strict.
2018 : */
2019 67596 : num_nonnulls = 0;
2020 267672 : for (int j = 0; j < num_elems; j++)
2021 : {
2022 200076 : if (!elem_nulls[j])
2023 200058 : elem_values[num_nonnulls++] = elem_values[j];
2024 : }
2025 :
2026 : /* We could pfree(elem_nulls) now, but not worth the cycles */
2027 :
2028 : /* If there's no non-nulls, the scan qual is unsatisfiable */
2029 67596 : if (num_nonnulls == 0)
2030 : {
2031 6 : so->qual_ok = false;
2032 6 : break;
2033 : }
2034 :
2035 : /*
2036 : * Determine the nominal datatype of the array elements. We have to
2037 : * support the convention that sk_subtype == InvalidOid means the
2038 : * opclass input type; this is a hack to simplify life for
2039 : * ScanKeyInit().
2040 : */
2041 67590 : elemtype = cur->sk_subtype;
2042 67590 : if (elemtype == InvalidOid)
2043 0 : elemtype = rel->rd_opcintype[cur->sk_attno - 1];
2044 :
2045 : /*
2046 : * If the comparison operator is not equality, then the array qual
2047 : * degenerates to a simple comparison against the smallest or largest
2048 : * non-null array element, as appropriate.
2049 : */
2050 67590 : switch (cur->sk_strategy)
2051 : {
2052 6 : case BTLessStrategyNumber:
2053 : case BTLessEqualStrategyNumber:
2054 6 : cur->sk_argument =
2055 6 : _bt_find_extreme_element(scan, cur, elemtype,
2056 : BTGreaterStrategyNumber,
2057 : elem_values, num_nonnulls);
2058 6 : numArrayKeyData++; /* keep this transformed scan key */
2059 6 : continue;
2060 67578 : case BTEqualStrategyNumber:
2061 : /* proceed with rest of loop */
2062 67578 : break;
2063 6 : case BTGreaterEqualStrategyNumber:
2064 : case BTGreaterStrategyNumber:
2065 6 : cur->sk_argument =
2066 6 : _bt_find_extreme_element(scan, cur, elemtype,
2067 : BTLessStrategyNumber,
2068 : elem_values, num_nonnulls);
2069 6 : numArrayKeyData++; /* keep this transformed scan key */
2070 6 : continue;
2071 0 : default:
2072 0 : elog(ERROR, "unrecognized StrategyNumber: %d",
2073 : (int) cur->sk_strategy);
2074 : break;
2075 : }
2076 :
2077 : /*
2078 : * We'll need a 3-way ORDER proc to perform binary searches for the
2079 : * next matching array element. Set that up now.
2080 : *
2081 : * Array scan keys with cross-type equality operators will require a
2082 : * separate same-type ORDER proc for sorting their array. Otherwise,
2083 : * sortproc just points to the same proc used during binary searches.
2084 : */
2085 67578 : _bt_setup_array_cmp(scan, cur, elemtype,
2086 67578 : &so->orderProcs[numArrayKeyData], &sortprocp);
2087 :
2088 : /*
2089 : * Sort the non-null elements and eliminate any duplicates. We must
2090 : * sort in the same ordering used by the index column, so that the
2091 : * arrays can be advanced in lockstep with the scan's progress through
2092 : * the index's key space.
2093 : */
2094 67578 : reverse = (indoption[cur->sk_attno - 1] & INDOPTION_DESC) != 0;
2095 67578 : num_elems = _bt_sort_array_elements(cur, sortprocp, reverse,
2096 : elem_values, num_nonnulls);
2097 :
2098 67578 : if (origarrayatt == cur->sk_attno)
2099 : {
2100 12 : BTArrayKeyInfo *orig = &so->arrayKeys[origarraykey];
2101 :
2102 : /*
2103 : * This array scan key is redundant with a previous equality
2104 : * operator array scan key. Merge the two arrays together to
2105 : * eliminate contradictory non-intersecting elements (or try to).
2106 : *
2107 : * We merge this next array back into attribute's original array.
2108 : */
2109 : Assert(arrayKeyData[orig->scan_key].sk_attno == cur->sk_attno);
2110 : Assert(arrayKeyData[orig->scan_key].sk_collation ==
2111 : cur->sk_collation);
2112 12 : if (_bt_merge_arrays(scan, cur, sortprocp, reverse,
2113 : origelemtype, elemtype,
2114 : orig->elem_values, &orig->num_elems,
2115 : elem_values, num_elems))
2116 : {
2117 : /* Successfully eliminated this array */
2118 12 : pfree(elem_values);
2119 :
2120 : /*
2121 : * If no intersecting elements remain in the original array,
2122 : * the scan qual is unsatisfiable
2123 : */
2124 12 : if (orig->num_elems == 0)
2125 : {
2126 6 : so->qual_ok = false;
2127 6 : break;
2128 : }
2129 :
2130 : /* Throw away this scan key/array */
2131 6 : continue;
2132 : }
2133 :
2134 : /*
2135 : * Unable to merge this array with previous array due to a lack of
2136 : * suitable cross-type opfamily support. Will need to keep both
2137 : * scan keys/arrays.
2138 : */
2139 : }
2140 : else
2141 : {
2142 : /*
2143 : * This array is the first for current index attribute.
2144 : *
2145 : * If it turns out to not be the last array (that is, if the next
2146 : * array is redundantly applied to this same index attribute),
2147 : * we'll then treat this array as the attribute's "original" array
2148 : * when merging.
2149 : */
2150 67566 : origarrayatt = cur->sk_attno;
2151 67566 : origarraykey = numArrayKeys;
2152 67566 : origelemtype = elemtype;
2153 : }
2154 :
2155 : /* Initialize generic BTArrayKeyInfo fields */
2156 67566 : so->arrayKeys[numArrayKeys].scan_key = numArrayKeyData;
2157 67566 : so->arrayKeys[numArrayKeys].num_elems = num_elems;
2158 :
2159 : /* Initialize SAOP array specific BTArrayKeyInfo fields */
2160 67566 : so->arrayKeys[numArrayKeys].elem_values = elem_values;
2161 67566 : so->arrayKeys[numArrayKeys].cur_elem = -1; /* i.e. invalid */
2162 :
2163 67566 : numArrayKeys++;
2164 67566 : numArrayKeyData++; /* keep this scan key/array */
2165 : }
2166 :
2167 : Assert(numSkipArrayKeys == 0 || !so->qual_ok);
2168 :
2169 : /* Set final number of equality-type array keys */
2170 70994 : so->numArrayKeys = numArrayKeys;
2171 : /* Set number of scan keys in arrayKeyData[] */
2172 70994 : *new_numberOfKeys = numArrayKeyData;
2173 :
2174 70994 : MemoryContextSwitchTo(oldContext);
2175 :
2176 70994 : return arrayKeyData;
2177 : }
2178 :
2179 : /*
2180 : * _bt_preprocess_array_keys_final() -- fix up array scan key references
2181 : *
2182 : * When _bt_preprocess_array_keys performed initial array preprocessing, it
2183 : * set each array's array->scan_key to its scankey's arrayKeyData[] offset.
2184 : * This function handles translation of the scan key references from the
2185 : * BTArrayKeyInfo info array, from input scan key references (to the keys in
2186 : * arrayKeyData[]), into output references (to the keys in so->keyData[]).
2187 : * Caller's keyDataMap[] array tells us how to perform this remapping.
2188 : *
2189 : * Also finalizes so->orderProcs[] for the scan. Arrays already have an ORDER
2190 : * proc, which might need to be repositioned to its so->keyData[]-wise offset
2191 : * (very much like the remapping that we apply to array->scan_key references).
2192 : * Non-array equality strategy scan keys (that survived preprocessing) don't
2193 : * yet have an so->orderProcs[] entry, so we set one for them here.
2194 : *
2195 : * Also converts single-element array scan keys into equivalent non-array
2196 : * equality scan keys, which decrements so->numArrayKeys. It's possible that
2197 : * this will leave this new btrescan without any arrays at all. This isn't
2198 : * necessary for correctness; it's just an optimization. Non-array equality
2199 : * scan keys are slightly faster than equivalent array scan keys at runtime.
2200 : */
2201 : static void
2202 4196 : _bt_preprocess_array_keys_final(IndexScanDesc scan, int *keyDataMap)
2203 : {
2204 4196 : BTScanOpaque so = (BTScanOpaque) scan->opaque;
2205 4196 : Relation rel = scan->indexRelation;
2206 4196 : int arrayidx = 0;
2207 4196 : int last_equal_output_ikey PG_USED_FOR_ASSERTS_ONLY = -1;
2208 :
2209 : Assert(so->qual_ok);
2210 :
2211 : /*
2212 : * Nothing for us to do when _bt_preprocess_array_keys only had to deal
2213 : * with array inequalities
2214 : */
2215 4196 : if (so->numArrayKeys == 0)
2216 0 : return;
2217 :
2218 13474 : for (int output_ikey = 0; output_ikey < so->numberOfKeys; output_ikey++)
2219 : {
2220 9290 : ScanKey outkey = so->keyData + output_ikey;
2221 : int input_ikey;
2222 9290 : bool found PG_USED_FOR_ASSERTS_ONLY = false;
2223 :
2224 : Assert(outkey->sk_strategy != InvalidStrategy);
2225 :
2226 9290 : if (outkey->sk_strategy != BTEqualStrategyNumber)
2227 106 : continue;
2228 :
2229 9184 : input_ikey = keyDataMap[output_ikey];
2230 :
2231 : Assert(last_equal_output_ikey < output_ikey);
2232 : Assert(last_equal_output_ikey < input_ikey);
2233 9184 : last_equal_output_ikey = output_ikey;
2234 :
2235 : /*
2236 : * We're lazy about looking up ORDER procs for non-array keys, since
2237 : * not all input keys become output keys. Take care of it now.
2238 : */
2239 9184 : if (!(outkey->sk_flags & SK_SEARCHARRAY))
2240 4620 : {
2241 : Oid elemtype;
2242 :
2243 : /* No need for an ORDER proc given an IS NULL scan key */
2244 4674 : if (outkey->sk_flags & SK_SEARCHNULL)
2245 54 : continue;
2246 :
2247 : /*
2248 : * A non-required scan key doesn't need an ORDER proc, either
2249 : * (unless it's associated with an array, which this one isn't)
2250 : */
2251 4620 : if (!(outkey->sk_flags & SK_BT_REQFWD))
2252 0 : continue;
2253 :
2254 4620 : elemtype = outkey->sk_subtype;
2255 4620 : if (elemtype == InvalidOid)
2256 2520 : elemtype = rel->rd_opcintype[outkey->sk_attno - 1];
2257 :
2258 4620 : _bt_setup_array_cmp(scan, outkey, elemtype,
2259 4620 : &so->orderProcs[output_ikey], NULL);
2260 4620 : continue;
2261 : }
2262 :
2263 : /*
2264 : * Reorder existing array scan key so->orderProcs[] entries.
2265 : *
2266 : * Doing this in-place is safe because preprocessing is required to
2267 : * output all equality strategy scan keys in original input order
2268 : * (among each group of entries against the same index attribute).
2269 : * This is also the order that the arrays themselves appear in.
2270 : */
2271 4510 : so->orderProcs[output_ikey] = so->orderProcs[input_ikey];
2272 :
2273 : /* Fix-up array->scan_key references for arrays */
2274 4510 : for (; arrayidx < so->numArrayKeys; arrayidx++)
2275 : {
2276 4510 : BTArrayKeyInfo *array = &so->arrayKeys[arrayidx];
2277 :
2278 : /*
2279 : * All skip arrays must be marked required, and final column can
2280 : * never have a skip array
2281 : */
2282 : Assert(array->num_elems > 0 || array->num_elems == -1);
2283 : Assert(array->num_elems != -1 || outkey->sk_flags & SK_BT_REQFWD);
2284 : Assert(array->num_elems != -1 ||
2285 : outkey->sk_attno < IndexRelationGetNumberOfKeyAttributes(rel));
2286 :
2287 4510 : if (array->scan_key == input_ikey)
2288 : {
2289 : /* found it */
2290 4510 : array->scan_key = output_ikey;
2291 4510 : found = true;
2292 :
2293 : /*
2294 : * Transform array scan keys that have exactly 1 element
2295 : * remaining (following all prior preprocessing) into
2296 : * equivalent non-array scan keys.
2297 : */
2298 4510 : if (array->num_elems == 1)
2299 : {
2300 18 : outkey->sk_flags &= ~SK_SEARCHARRAY;
2301 18 : outkey->sk_argument = array->elem_values[0];
2302 18 : so->numArrayKeys--;
2303 :
2304 : /* If we're out of array keys, we can quit right away */
2305 18 : if (so->numArrayKeys == 0)
2306 12 : return;
2307 :
2308 : /* Shift other arrays forward */
2309 6 : memmove(array, array + 1,
2310 : sizeof(BTArrayKeyInfo) *
2311 6 : (so->numArrayKeys - arrayidx));
2312 :
2313 : /*
2314 : * Don't increment arrayidx (there was an entry that was
2315 : * just shifted forward to the offset at arrayidx, which
2316 : * will still need to be matched)
2317 : */
2318 : }
2319 : else
2320 : {
2321 : /*
2322 : * Any skip array low_compare and high_compare scan keys
2323 : * are now final. Transform the array's > low_compare key
2324 : * into a >= key (and < high_compare keys into a <= key).
2325 : */
2326 4492 : if (array->num_elems == -1 && array->sksup &&
2327 3240 : !array->null_elem)
2328 78 : _bt_skiparray_strat_adjust(scan, outkey, array);
2329 :
2330 : /* Match found, so done with this array */
2331 4492 : arrayidx++;
2332 : }
2333 :
2334 4498 : break;
2335 : }
2336 : }
2337 :
2338 : Assert(found);
2339 : }
2340 :
2341 : /*
2342 : * Parallel index scans require space in shared memory to store the
2343 : * current array elements (for arrays kept by preprocessing) to schedule
2344 : * the next primitive index scan. The underlying structure is protected
2345 : * using an LWLock, so defensively limit its size. In practice this can
2346 : * only affect parallel scans that use an incomplete opfamily.
2347 : */
2348 4184 : if (scan->parallel_scan && so->numArrayKeys > INDEX_MAX_KEYS)
2349 0 : ereport(ERROR,
2350 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2351 : errmsg_internal("number of array scan keys left by preprocessing (%d) exceeds the maximum allowed by parallel btree index scans (%d)",
2352 : so->numArrayKeys, INDEX_MAX_KEYS)));
2353 : }
2354 :
2355 : /*
2356 : * _bt_num_array_keys() -- determine # of BTArrayKeyInfo entries
2357 : *
2358 : * _bt_preprocess_array_keys helper function. Returns the estimated size of
2359 : * the scan's BTArrayKeyInfo array, which is guaranteed to be large enough to
2360 : * fit every so->arrayKeys[] entry.
2361 : *
2362 : * Also sets *numSkipArrayKeys_out to the number of skip arrays caller must
2363 : * add to the scan keys it'll output. Caller must add this many skip arrays:
2364 : * one array for each of the most significant attributes that lack a = input
2365 : * key (IS NULL keys count as = input keys here). The specific attributes
2366 : * that need skip arrays are indicated by initializing skip_eq_ops_out[] arg
2367 : * 0-based attribute offset to a valid = op strategy Oid. We'll only ever set
2368 : * skip_eq_ops_out[] entries to InvalidOid for attributes that already have an
2369 : * equality key in scan->keyData[] input keys -- and only when there's some
2370 : * later "attribute gap" for us to "fill-in" with a skip array.
2371 : *
2372 : * We're optimistic about skipping working out: we always add exactly the skip
2373 : * arrays needed to maximize the number of input scan keys that can ultimately
2374 : * be marked as required to continue the scan (but no more). Given a
2375 : * multi-column index on (a, b, c, d), we add skip arrays as follows:
2376 : *
2377 : * Input keys Output keys (after all preprocessing)
2378 : * ---------- -------------------------------------
2379 : * a = 1 a = 1 (no skip arrays)
2380 : * b = 42 skip a AND b = 42
2381 : * a = 1 AND b = 42 a = 1 AND b = 42 (no skip arrays)
2382 : * a >= 1 AND b = 42 range skip a AND b = 42
2383 : * a = 1 AND b > 42 a = 1 AND b > 42 (no skip arrays)
2384 : * a >= 1 AND a <= 3 AND b = 42 range skip a AND b = 42
2385 : * a = 1 AND c <= 27 a = 1 AND skip b AND c <= 27
2386 : * a = 1 AND d >= 1 a = 1 AND skip b AND skip c AND d >= 1
2387 : * a = 1 AND b >= 42 AND d > 1 a = 1 AND range skip b AND skip c AND d > 1
2388 : */
2389 : static int
2390 16054876 : _bt_num_array_keys(IndexScanDesc scan, Oid *skip_eq_ops_out,
2391 : int *numSkipArrayKeys_out)
2392 : {
2393 16054876 : Relation rel = scan->indexRelation;
2394 16054876 : AttrNumber attno_skip = 1,
2395 16054876 : attno_inkey = 1;
2396 16054876 : bool attno_has_equal = false,
2397 16054876 : attno_has_rowcompare = false;
2398 : int numSAOPArrayKeys,
2399 : numSkipArrayKeys,
2400 : prev_numSkipArrayKeys;
2401 :
2402 : Assert(scan->numberOfKeys);
2403 :
2404 : /* Initial pass over input scan keys counts the number of SAOP arrays */
2405 16054876 : numSAOPArrayKeys = 0;
2406 16054876 : *numSkipArrayKeys_out = prev_numSkipArrayKeys = numSkipArrayKeys = 0;
2407 41378272 : for (int i = 0; i < scan->numberOfKeys; i++)
2408 : {
2409 25323396 : ScanKey inkey = scan->keyData + i;
2410 :
2411 25323396 : if (inkey->sk_flags & SK_SEARCHARRAY)
2412 67602 : numSAOPArrayKeys++;
2413 : }
2414 :
2415 : #ifdef DEBUG_DISABLE_SKIP_SCAN
2416 : /* don't attempt to add skip arrays */
2417 : return numSAOPArrayKeys;
2418 : #endif
2419 :
2420 16054876 : for (int i = 0;; i++)
2421 25323366 : {
2422 41378242 : ScanKey inkey = scan->keyData + i;
2423 :
2424 : /*
2425 : * Backfill skip arrays for any wholly omitted attributes prior to
2426 : * attno_inkey
2427 : */
2428 41378804 : while (attno_skip < attno_inkey)
2429 : {
2430 562 : Oid opfamily = rel->rd_opfamily[attno_skip - 1];
2431 562 : Oid opcintype = rel->rd_opcintype[attno_skip - 1];
2432 :
2433 : /* Look up input opclass's equality operator (might fail) */
2434 1124 : skip_eq_ops_out[attno_skip - 1] =
2435 562 : get_opfamily_member(opfamily, opcintype, opcintype,
2436 : BTEqualStrategyNumber);
2437 562 : if (!OidIsValid(skip_eq_ops_out[attno_skip - 1]))
2438 : {
2439 : /*
2440 : * Cannot generate a skip array for this or later attributes
2441 : * (input opclass lacks an equality strategy operator)
2442 : */
2443 0 : *numSkipArrayKeys_out = prev_numSkipArrayKeys;
2444 0 : return numSAOPArrayKeys + prev_numSkipArrayKeys;
2445 : }
2446 :
2447 : /* plan on adding a backfill skip array for this attribute */
2448 562 : numSkipArrayKeys++;
2449 562 : attno_skip++;
2450 : }
2451 :
2452 41378242 : prev_numSkipArrayKeys = numSkipArrayKeys;
2453 :
2454 : /*
2455 : * Stop once past the final input scan key. We deliberately never add
2456 : * a skip array for the last input scan key's attribute -- even when
2457 : * there are only inequality keys on that attribute.
2458 : */
2459 41378242 : if (i == scan->numberOfKeys)
2460 16054858 : break;
2461 :
2462 : /*
2463 : * Later preprocessing steps cannot merge a RowCompare into a skip
2464 : * array, so stop adding skip arrays once we see one. (Note that we
2465 : * can backfill skip arrays before a RowCompare, which will allow keys
2466 : * up to and including the RowCompare to be marked required.)
2467 : *
2468 : * Skip arrays work by maintaining a current array element value,
2469 : * which anchors lower-order keys via an implied equality constraint.
2470 : * This is incompatible with the current nbtree row comparison design,
2471 : * which compares all columns together, as an indivisible group.
2472 : * Alternative designs that can be used alongside skip arrays are
2473 : * possible, but it's not clear that they're really worth pursuing.
2474 : *
2475 : * A RowCompare qual "(a, b, c) > (10, 'foo', 42)" is equivalent to
2476 : * "(a=10 AND b='foo' AND c>42) OR (a=10 AND b>'foo') OR (a>10)".
2477 : * Decomposing this RowCompare into these 3 disjuncts allows each
2478 : * disjunct to be executed as a separate "single value" index scan.
2479 : * That'll give all 3 scans the ability to add skip arrays in the
2480 : * usual way (when there are any scalar keys after the RowCompare).
2481 : * Under this scheme, a qual "(a, b, c) > (10, 'foo', 42) AND d = 99"
2482 : * performs 3 separate scans, each of which can mark keys up to and
2483 : * including its "d = 99" key as required to continue the scan.
2484 : */
2485 25323384 : if (attno_has_rowcompare)
2486 18 : break;
2487 :
2488 : /*
2489 : * Now consider next attno_inkey (or keep going if this is an
2490 : * additional scan key against the same attribute)
2491 : */
2492 25323366 : if (attno_inkey < inkey->sk_attno)
2493 : {
2494 : /*
2495 : * Now add skip array for previous scan key's attribute, though
2496 : * only if the attribute has no equality strategy scan keys
2497 : */
2498 9269314 : if (attno_has_equal)
2499 : {
2500 : /* Attributes with an = key must have InvalidOid eq_op set */
2501 9266158 : skip_eq_ops_out[attno_skip - 1] = InvalidOid;
2502 : }
2503 : else
2504 : {
2505 3156 : Oid opfamily = rel->rd_opfamily[attno_skip - 1];
2506 3156 : Oid opcintype = rel->rd_opcintype[attno_skip - 1];
2507 :
2508 : /* Look up input opclass's equality operator (might fail) */
2509 6312 : skip_eq_ops_out[attno_skip - 1] =
2510 3156 : get_opfamily_member(opfamily, opcintype, opcintype,
2511 : BTEqualStrategyNumber);
2512 :
2513 3156 : if (!OidIsValid(skip_eq_ops_out[attno_skip - 1]))
2514 : {
2515 : /*
2516 : * Input opclass lacks an equality strategy operator, so
2517 : * don't generate a skip array that definitely won't work
2518 : */
2519 0 : break;
2520 : }
2521 :
2522 : /* plan on adding a backfill skip array for this attribute */
2523 3156 : numSkipArrayKeys++;
2524 : }
2525 :
2526 : /* Set things up for this new attribute */
2527 9269314 : attno_skip++;
2528 9269314 : attno_inkey = inkey->sk_attno;
2529 9269314 : attno_has_equal = false;
2530 : }
2531 :
2532 : /*
2533 : * Track if this attribute's scan keys include any equality strategy
2534 : * scan keys (IS NULL keys count as equality keys here). Also track
2535 : * if it has any RowCompare keys.
2536 : */
2537 25323366 : if (inkey->sk_strategy == BTEqualStrategyNumber ||
2538 1782884 : (inkey->sk_flags & SK_SEARCHNULL))
2539 23540626 : attno_has_equal = true;
2540 25323366 : if (inkey->sk_flags & SK_ROW_HEADER)
2541 84 : attno_has_rowcompare = true;
2542 : }
2543 :
2544 16054876 : *numSkipArrayKeys_out = numSkipArrayKeys;
2545 16054876 : return numSAOPArrayKeys + numSkipArrayKeys;
2546 : }
2547 :
2548 : /*
2549 : * _bt_find_extreme_element() -- get least or greatest array element
2550 : *
2551 : * scan and skey identify the index column, whose opfamily determines the
2552 : * comparison semantics. strat should be BTLessStrategyNumber to get the
2553 : * least element, or BTGreaterStrategyNumber to get the greatest.
2554 : */
2555 : static Datum
2556 12 : _bt_find_extreme_element(IndexScanDesc scan, ScanKey skey, Oid elemtype,
2557 : StrategyNumber strat,
2558 : Datum *elems, int nelems)
2559 : {
2560 12 : Relation rel = scan->indexRelation;
2561 : Oid cmp_op;
2562 : RegProcedure cmp_proc;
2563 : FmgrInfo flinfo;
2564 : Datum result;
2565 : int i;
2566 :
2567 : /*
2568 : * Look up the appropriate comparison operator in the opfamily.
2569 : *
2570 : * Note: it's possible that this would fail, if the opfamily is
2571 : * incomplete, but it seems quite unlikely that an opfamily would omit
2572 : * non-cross-type comparison operators for any datatype that it supports
2573 : * at all.
2574 : */
2575 : Assert(skey->sk_strategy != BTEqualStrategyNumber);
2576 : Assert(OidIsValid(elemtype));
2577 12 : cmp_op = get_opfamily_member(rel->rd_opfamily[skey->sk_attno - 1],
2578 : elemtype,
2579 : elemtype,
2580 : strat);
2581 12 : if (!OidIsValid(cmp_op))
2582 0 : elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
2583 : strat, elemtype, elemtype,
2584 : rel->rd_opfamily[skey->sk_attno - 1]);
2585 12 : cmp_proc = get_opcode(cmp_op);
2586 12 : if (!RegProcedureIsValid(cmp_proc))
2587 0 : elog(ERROR, "missing oprcode for operator %u", cmp_op);
2588 :
2589 12 : fmgr_info(cmp_proc, &flinfo);
2590 :
2591 : Assert(nelems > 0);
2592 12 : result = elems[0];
2593 36 : for (i = 1; i < nelems; i++)
2594 : {
2595 24 : if (DatumGetBool(FunctionCall2Coll(&flinfo,
2596 : skey->sk_collation,
2597 24 : elems[i],
2598 : result)))
2599 6 : result = elems[i];
2600 : }
2601 :
2602 12 : return result;
2603 : }
2604 :
2605 : /*
2606 : * _bt_setup_array_cmp() -- Set up array comparison functions
2607 : *
2608 : * Sets ORDER proc in caller's orderproc argument, which is used during binary
2609 : * searches of arrays during the index scan. Also sets a same-type ORDER proc
2610 : * in caller's *sortprocp argument, which is used when sorting the array.
2611 : *
2612 : * Preprocessing calls here with all equality strategy scan keys (when scan
2613 : * uses equality array keys), including those not associated with any array.
2614 : * See _bt_advance_array_keys for an explanation of why it'll need to treat
2615 : * simple scalar equality scan keys as degenerate single element arrays.
2616 : *
2617 : * Caller should pass an orderproc pointing to space that'll store the ORDER
2618 : * proc for the scan, and a *sortprocp pointing to its own separate space.
2619 : * When calling here for a non-array scan key, sortprocp arg should be NULL.
2620 : *
2621 : * In the common case where we don't need to deal with cross-type operators,
2622 : * only one ORDER proc is actually required by caller. We'll set *sortprocp
2623 : * to point to the same memory that caller's orderproc continues to point to.
2624 : * Otherwise, *sortprocp will continue to point to caller's own space. Either
2625 : * way, *sortprocp will point to a same-type ORDER proc (since that's the only
2626 : * safe way to sort/deduplicate the array associated with caller's scan key).
2627 : */
2628 : static void
2629 75916 : _bt_setup_array_cmp(IndexScanDesc scan, ScanKey skey, Oid elemtype,
2630 : FmgrInfo *orderproc, FmgrInfo **sortprocp)
2631 : {
2632 75916 : BTScanOpaque so = (BTScanOpaque) scan->opaque;
2633 75916 : Relation rel = scan->indexRelation;
2634 : RegProcedure cmp_proc;
2635 75916 : Oid opcintype = rel->rd_opcintype[skey->sk_attno - 1];
2636 :
2637 : Assert(skey->sk_strategy == BTEqualStrategyNumber);
2638 : Assert(OidIsValid(elemtype));
2639 :
2640 : /*
2641 : * If scankey operator is not a cross-type comparison, we can use the
2642 : * cached comparison function; otherwise gotta look it up in the catalogs
2643 : */
2644 75916 : if (elemtype == opcintype)
2645 : {
2646 : /* Set same-type ORDER procs for caller */
2647 75646 : *orderproc = *index_getprocinfo(rel, skey->sk_attno, BTORDER_PROC);
2648 75646 : if (sortprocp)
2649 67572 : *sortprocp = orderproc;
2650 :
2651 75646 : return;
2652 : }
2653 :
2654 : /*
2655 : * Look up the appropriate cross-type comparison function in the opfamily.
2656 : *
2657 : * Use the opclass input type as the left hand arg type, and the array
2658 : * element type as the right hand arg type (since binary searches use an
2659 : * index tuple's attribute value to search for a matching array element).
2660 : *
2661 : * Note: it's possible that this would fail, if the opfamily is
2662 : * incomplete, but only in cases where it's quite likely that _bt_first
2663 : * would fail in just the same way (had we not failed before it could).
2664 : */
2665 270 : cmp_proc = get_opfamily_proc(rel->rd_opfamily[skey->sk_attno - 1],
2666 : opcintype, elemtype, BTORDER_PROC);
2667 270 : if (!RegProcedureIsValid(cmp_proc))
2668 0 : elog(ERROR, "missing support function %d(%u,%u) for attribute %d of index \"%s\"",
2669 : BTORDER_PROC, opcintype, elemtype, skey->sk_attno,
2670 : RelationGetRelationName(rel));
2671 :
2672 : /* Set cross-type ORDER proc for caller */
2673 270 : fmgr_info_cxt(cmp_proc, orderproc, so->arrayContext);
2674 :
2675 : /* Done if caller doesn't actually have an array they'll need to sort */
2676 270 : if (!sortprocp)
2677 264 : return;
2678 :
2679 : /*
2680 : * Look up the appropriate same-type comparison function in the opfamily.
2681 : *
2682 : * Note: it's possible that this would fail, if the opfamily is
2683 : * incomplete, but it seems quite unlikely that an opfamily would omit
2684 : * non-cross-type comparison procs for any datatype that it supports at
2685 : * all.
2686 : */
2687 6 : cmp_proc = get_opfamily_proc(rel->rd_opfamily[skey->sk_attno - 1],
2688 : elemtype, elemtype, BTORDER_PROC);
2689 6 : if (!RegProcedureIsValid(cmp_proc))
2690 0 : elog(ERROR, "missing support function %d(%u,%u) for attribute %d of index \"%s\"",
2691 : BTORDER_PROC, elemtype, elemtype,
2692 : skey->sk_attno, RelationGetRelationName(rel));
2693 :
2694 : /* Set same-type ORDER proc for caller */
2695 6 : fmgr_info_cxt(cmp_proc, *sortprocp, so->arrayContext);
2696 : }
2697 :
2698 : /*
2699 : * _bt_sort_array_elements() -- sort and de-dup array elements
2700 : *
2701 : * The array elements are sorted in-place, and the new number of elements
2702 : * after duplicate removal is returned.
2703 : *
2704 : * skey identifies the index column whose opfamily determines the comparison
2705 : * semantics, and sortproc is a corresponding ORDER proc. If reverse is true,
2706 : * we sort in descending order.
2707 : */
2708 : static int
2709 67578 : _bt_sort_array_elements(ScanKey skey, FmgrInfo *sortproc, bool reverse,
2710 : Datum *elems, int nelems)
2711 : {
2712 : BTSortArrayContext cxt;
2713 :
2714 67578 : if (nelems <= 1)
2715 52 : return nelems; /* no work to do */
2716 :
2717 : /* Sort the array elements */
2718 67526 : cxt.sortproc = sortproc;
2719 67526 : cxt.collation = skey->sk_collation;
2720 67526 : cxt.reverse = reverse;
2721 67526 : qsort_arg(elems, nelems, sizeof(Datum),
2722 : _bt_compare_array_elements, &cxt);
2723 :
2724 : /* Now scan the sorted elements and remove duplicates */
2725 67526 : return qunique_arg(elems, nelems, sizeof(Datum),
2726 : _bt_compare_array_elements, &cxt);
2727 : }
2728 :
2729 : /*
2730 : * _bt_merge_arrays() -- merge next array's elements into an original array
2731 : *
2732 : * Called when preprocessing encounters a pair of array equality scan keys,
2733 : * both against the same index attribute (during initial array preprocessing).
2734 : * Merging reorganizes caller's original array (the left hand arg) in-place,
2735 : * without ever copying elements from one array into the other. (Mixing the
2736 : * elements together like this would be wrong, since they don't necessarily
2737 : * use the same underlying element type, despite all the other similarities.)
2738 : *
2739 : * Both arrays must have already been sorted and deduplicated by calling
2740 : * _bt_sort_array_elements. sortproc is the same-type ORDER proc that was
2741 : * just used to sort and deduplicate caller's "next" array. We'll usually be
2742 : * able to reuse that order PROC to merge the arrays together now. If not,
2743 : * then we'll perform a separate ORDER proc lookup.
2744 : *
2745 : * If the opfamily doesn't supply a complete set of cross-type ORDER procs we
2746 : * may not be able to determine which elements are contradictory. If we have
2747 : * the required ORDER proc then we return true (and validly set *nelems_orig),
2748 : * guaranteeing that at least the next array can be considered redundant. We
2749 : * return false if the required comparisons cannot be made (caller must keep
2750 : * both arrays when this happens).
2751 : */
2752 : static bool
2753 12 : _bt_merge_arrays(IndexScanDesc scan, ScanKey skey, FmgrInfo *sortproc,
2754 : bool reverse, Oid origelemtype, Oid nextelemtype,
2755 : Datum *elems_orig, int *nelems_orig,
2756 : Datum *elems_next, int nelems_next)
2757 : {
2758 12 : Relation rel = scan->indexRelation;
2759 12 : BTScanOpaque so = (BTScanOpaque) scan->opaque;
2760 : BTSortArrayContext cxt;
2761 12 : int nelems_orig_start = *nelems_orig,
2762 12 : nelems_orig_merged = 0;
2763 12 : FmgrInfo *mergeproc = sortproc;
2764 : FmgrInfo crosstypeproc;
2765 :
2766 : Assert(skey->sk_strategy == BTEqualStrategyNumber);
2767 : Assert(OidIsValid(origelemtype) && OidIsValid(nextelemtype));
2768 :
2769 12 : if (origelemtype != nextelemtype)
2770 : {
2771 : RegProcedure cmp_proc;
2772 :
2773 : /*
2774 : * Cross-array-element-type merging is required, so can't just reuse
2775 : * sortproc when merging
2776 : */
2777 6 : cmp_proc = get_opfamily_proc(rel->rd_opfamily[skey->sk_attno - 1],
2778 : origelemtype, nextelemtype, BTORDER_PROC);
2779 6 : if (!RegProcedureIsValid(cmp_proc))
2780 : {
2781 : /* Can't make the required comparisons */
2782 0 : return false;
2783 : }
2784 :
2785 : /* We have all we need to determine redundancy/contradictoriness */
2786 6 : mergeproc = &crosstypeproc;
2787 6 : fmgr_info_cxt(cmp_proc, mergeproc, so->arrayContext);
2788 : }
2789 :
2790 12 : cxt.sortproc = mergeproc;
2791 12 : cxt.collation = skey->sk_collation;
2792 12 : cxt.reverse = reverse;
2793 :
2794 54 : for (int i = 0, j = 0; i < nelems_orig_start && j < nelems_next;)
2795 : {
2796 42 : Datum *oelem = elems_orig + i,
2797 42 : *nelem = elems_next + j;
2798 42 : int res = _bt_compare_array_elements(oelem, nelem, &cxt);
2799 :
2800 42 : if (res == 0)
2801 : {
2802 6 : elems_orig[nelems_orig_merged++] = *oelem;
2803 6 : i++;
2804 6 : j++;
2805 : }
2806 36 : else if (res < 0)
2807 24 : i++;
2808 : else /* res > 0 */
2809 12 : j++;
2810 : }
2811 :
2812 12 : *nelems_orig = nelems_orig_merged;
2813 :
2814 12 : return true;
2815 : }
2816 :
2817 : /*
2818 : * qsort_arg comparator for sorting array elements
2819 : */
2820 : static int
2821 300468 : _bt_compare_array_elements(const void *a, const void *b, void *arg)
2822 : {
2823 300468 : Datum da = *((const Datum *) a);
2824 300468 : Datum db = *((const Datum *) b);
2825 300468 : BTSortArrayContext *cxt = (BTSortArrayContext *) arg;
2826 : int32 compare;
2827 :
2828 300468 : compare = DatumGetInt32(FunctionCall2Coll(cxt->sortproc,
2829 : cxt->collation,
2830 : da, db));
2831 300468 : if (cxt->reverse)
2832 30 : INVERT_COMPARE_RESULT(compare);
2833 300468 : return compare;
2834 : }
|