1 /*
2 * brin_inclusion.c
3 * Implementation of inclusion opclasses for BRIN
4 *
5 * This module provides framework BRIN support functions for the "inclusion"
6 * operator classes. A few SQL-level support functions are also required for
7 * each opclass.
8 *
9 * The "inclusion" BRIN strategy is useful for types that support R-Tree
10 * operations. This implementation is a straight mapping of those operations
11 * to the block-range nature of BRIN, with two exceptions: (a) we explicitly
12 * support "empty" elements: at least with range types, we need to consider
13 * emptiness separately from regular R-Tree strategies; and (b) we need to
14 * consider "unmergeable" elements, that is, a set of elements for whose union
15 * no representation exists. The only case where that happens as of this
16 * writing is the INET type, where IPv6 values cannot be merged with IPv4
17 * values.
18 *
19 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
20 * Portions Copyright (c) 1994, Regents of the University of California
21 *
22 * IDENTIFICATION
23 * src/backend/access/brin/brin_inclusion.c
24 */
25 #include "postgres.h"
26
27 #include "access/brin_internal.h"
28 #include "access/brin_tuple.h"
29 #include "access/genam.h"
30 #include "access/skey.h"
31 #include "catalog/pg_amop.h"
32 #include "catalog/pg_type.h"
33 #include "utils/builtins.h"
34 #include "utils/datum.h"
35 #include "utils/lsyscache.h"
36 #include "utils/rel.h"
37 #include "utils/syscache.h"
38
39
40 /*
41 * Additional SQL level support functions
42 *
43 * Procedure numbers must not use values reserved for BRIN itself; see
44 * brin_internal.h.
45 */
46 #define INCLUSION_MAX_PROCNUMS 4 /* maximum support procs we need */
47 #define PROCNUM_MERGE 11 /* required */
48 #define PROCNUM_MERGEABLE 12 /* optional */
49 #define PROCNUM_CONTAINS 13 /* optional */
50 #define PROCNUM_EMPTY 14 /* optional */
51
52
53 /*
54 * Subtract this from procnum to obtain index in InclusionOpaque arrays
55 * (Must be equal to minimum of private procnums).
56 */
57 #define PROCNUM_BASE 11
58
59 /*-
60 * The values stored in the bv_values arrays correspond to:
61 *
62 * INCLUSION_UNION
63 * the union of the values in the block range
64 * INCLUSION_UNMERGEABLE
65 * whether the values in the block range cannot be merged
66 * (e.g. an IPv6 address amidst IPv4 addresses)
67 * INCLUSION_CONTAINS_EMPTY
68 * whether an empty value is present in any tuple
69 * in the block range
70 */
71 #define INCLUSION_UNION 0
72 #define INCLUSION_UNMERGEABLE 1
73 #define INCLUSION_CONTAINS_EMPTY 2
74
75
76 typedef struct InclusionOpaque
77 {
78 FmgrInfo extra_procinfos[INCLUSION_MAX_PROCNUMS];
79 bool extra_proc_missing[INCLUSION_MAX_PROCNUMS];
80 Oid cached_subtype;
81 FmgrInfo strategy_procinfos[RTMaxStrategyNumber];
82 } InclusionOpaque;
83
84 static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno,
85 uint16 procnum);
86 static FmgrInfo *inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno,
87 Oid subtype, uint16 strategynum);
88
89
90 /*
91 * BRIN inclusion OpcInfo function
92 */
93 Datum
brin_inclusion_opcinfo(PG_FUNCTION_ARGS)94 brin_inclusion_opcinfo(PG_FUNCTION_ARGS)
95 {
96 Oid typoid = PG_GETARG_OID(0);
97 BrinOpcInfo *result;
98 TypeCacheEntry *bool_typcache = lookup_type_cache(BOOLOID, 0);
99
100 /*
101 * All members of opaque are initialized lazily; both procinfo arrays
102 * start out as non-initialized by having fn_oid be InvalidOid, and
103 * "missing" to false, by zeroing here. strategy_procinfos elements can
104 * be invalidated when cached_subtype changes by zeroing fn_oid.
105 * extra_procinfo entries are never invalidated, but if a lookup fails
106 * (which is expected), extra_proc_missing is set to true, indicating not
107 * to look it up again.
108 */
109 result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque));
110 result->oi_nstored = 3;
111 result->oi_opaque = (InclusionOpaque *)
112 MAXALIGN((char *) result + SizeofBrinOpcInfo(3));
113
114 /* the union */
115 result->oi_typcache[INCLUSION_UNION] =
116 lookup_type_cache(typoid, 0);
117
118 /* includes elements that are not mergeable */
119 result->oi_typcache[INCLUSION_UNMERGEABLE] = bool_typcache;
120
121 /* includes the empty element */
122 result->oi_typcache[INCLUSION_CONTAINS_EMPTY] = bool_typcache;
123
124 PG_RETURN_POINTER(result);
125 }
126
127 /*
128 * BRIN inclusion add value function
129 *
130 * Examine the given index tuple (which contains partial status of a certain
131 * page range) by comparing it to the given value that comes from another heap
132 * tuple. If the new value is outside the union specified by the existing
133 * tuple values, update the index tuple and return true. Otherwise, return
134 * false and do not modify in this case.
135 */
136 Datum
brin_inclusion_add_value(PG_FUNCTION_ARGS)137 brin_inclusion_add_value(PG_FUNCTION_ARGS)
138 {
139 BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
140 BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
141 Datum newval = PG_GETARG_DATUM(2);
142 bool isnull = PG_GETARG_BOOL(3);
143 Oid colloid = PG_GET_COLLATION();
144 FmgrInfo *finfo;
145 Datum result;
146 bool new = false;
147 AttrNumber attno;
148 Form_pg_attribute attr;
149
150 /*
151 * If the new value is null, we record that we saw it if it's the first
152 * one; otherwise, there's nothing to do.
153 */
154 if (isnull)
155 {
156 if (column->bv_hasnulls)
157 PG_RETURN_BOOL(false);
158
159 column->bv_hasnulls = true;
160 PG_RETURN_BOOL(true);
161 }
162
163 attno = column->bv_attno;
164 attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
165
166 /*
167 * If the recorded value is null, copy the new value (which we know to be
168 * not null), and we're almost done.
169 */
170 if (column->bv_allnulls)
171 {
172 column->bv_values[INCLUSION_UNION] =
173 datumCopy(newval, attr->attbyval, attr->attlen);
174 column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(false);
175 column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(false);
176 column->bv_allnulls = false;
177 new = true;
178 }
179
180 /*
181 * No need for further processing if the block range is marked as
182 * containing unmergeable values.
183 */
184 if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE]))
185 PG_RETURN_BOOL(false);
186
187 /*
188 * If the opclass supports the concept of empty values, test the passed
189 * new value for emptiness; if it returns true, we need to set the
190 * "contains empty" flag in the element (unless already set).
191 */
192 finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_EMPTY);
193 if (finfo != NULL && DatumGetBool(FunctionCall1Coll(finfo, colloid, newval)))
194 {
195 if (!DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]))
196 {
197 column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true);
198 PG_RETURN_BOOL(true);
199 }
200
201 PG_RETURN_BOOL(false);
202 }
203
204 if (new)
205 PG_RETURN_BOOL(true);
206
207 /* Check if the new value is already contained. */
208 finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_CONTAINS);
209 if (finfo != NULL &&
210 DatumGetBool(FunctionCall2Coll(finfo, colloid,
211 column->bv_values[INCLUSION_UNION],
212 newval)))
213 PG_RETURN_BOOL(false);
214
215 /*
216 * Check if the new value is mergeable to the existing union. If it is
217 * not, mark the value as containing unmergeable elements and get out.
218 *
219 * Note: at this point we could remove the value from the union, since
220 * it's not going to be used any longer. However, the BRIN framework
221 * doesn't allow for the value not being present. Improve someday.
222 */
223 finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE);
224 if (finfo != NULL &&
225 !DatumGetBool(FunctionCall2Coll(finfo, colloid,
226 column->bv_values[INCLUSION_UNION],
227 newval)))
228 {
229 column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true);
230 PG_RETURN_BOOL(true);
231 }
232
233 /* Finally, merge the new value to the existing union. */
234 finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE);
235 Assert(finfo != NULL);
236 result = FunctionCall2Coll(finfo, colloid,
237 column->bv_values[INCLUSION_UNION], newval);
238 if (!attr->attbyval &&
239 DatumGetPointer(result) != DatumGetPointer(column->bv_values[INCLUSION_UNION]))
240 {
241 pfree(DatumGetPointer(column->bv_values[INCLUSION_UNION]));
242
243 if (result == newval)
244 result = datumCopy(result, attr->attbyval, attr->attlen);
245 }
246 column->bv_values[INCLUSION_UNION] = result;
247
248 PG_RETURN_BOOL(true);
249 }
250
251 /*
252 * BRIN inclusion consistent function
253 *
254 * All of the strategies are optional.
255 */
256 Datum
brin_inclusion_consistent(PG_FUNCTION_ARGS)257 brin_inclusion_consistent(PG_FUNCTION_ARGS)
258 {
259 BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
260 BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
261 ScanKey key = (ScanKey) PG_GETARG_POINTER(2);
262 Oid colloid = PG_GET_COLLATION(),
263 subtype;
264 Datum unionval;
265 AttrNumber attno;
266 Datum query;
267 FmgrInfo *finfo;
268 Datum result;
269
270 Assert(key->sk_attno == column->bv_attno);
271
272 /* Handle IS NULL/IS NOT NULL tests. */
273 if (key->sk_flags & SK_ISNULL)
274 {
275 if (key->sk_flags & SK_SEARCHNULL)
276 {
277 if (column->bv_allnulls || column->bv_hasnulls)
278 PG_RETURN_BOOL(true);
279 PG_RETURN_BOOL(false);
280 }
281
282 /*
283 * For IS NOT NULL, we can only skip ranges that are known to have
284 * only nulls.
285 */
286 if (key->sk_flags & SK_SEARCHNOTNULL)
287 PG_RETURN_BOOL(!column->bv_allnulls);
288
289 /*
290 * Neither IS NULL nor IS NOT NULL was used; assume all indexable
291 * operators are strict and return false.
292 */
293 PG_RETURN_BOOL(false);
294 }
295
296 /* If it is all nulls, it cannot possibly be consistent. */
297 if (column->bv_allnulls)
298 PG_RETURN_BOOL(false);
299
300 /* It has to be checked, if it contains elements that are not mergeable. */
301 if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE]))
302 PG_RETURN_BOOL(true);
303
304 attno = key->sk_attno;
305 subtype = key->sk_subtype;
306 query = key->sk_argument;
307 unionval = column->bv_values[INCLUSION_UNION];
308 switch (key->sk_strategy)
309 {
310 /*
311 * Placement strategies
312 *
313 * These are implemented by logically negating the result of the
314 * converse placement operator; for this to work, the converse
315 * operator must be part of the opclass. An error will be thrown
316 * by inclusion_get_strategy_procinfo() if the required strategy
317 * is not part of the opclass.
318 *
319 * These all return false if either argument is empty, so there is
320 * no need to check for empty elements.
321 */
322
323 case RTLeftStrategyNumber:
324 finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
325 RTOverRightStrategyNumber);
326 result = FunctionCall2Coll(finfo, colloid, unionval, query);
327 PG_RETURN_BOOL(!DatumGetBool(result));
328
329 case RTOverLeftStrategyNumber:
330 finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
331 RTRightStrategyNumber);
332 result = FunctionCall2Coll(finfo, colloid, unionval, query);
333 PG_RETURN_BOOL(!DatumGetBool(result));
334
335 case RTOverRightStrategyNumber:
336 finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
337 RTLeftStrategyNumber);
338 result = FunctionCall2Coll(finfo, colloid, unionval, query);
339 PG_RETURN_BOOL(!DatumGetBool(result));
340
341 case RTRightStrategyNumber:
342 finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
343 RTOverLeftStrategyNumber);
344 result = FunctionCall2Coll(finfo, colloid, unionval, query);
345 PG_RETURN_BOOL(!DatumGetBool(result));
346
347 case RTBelowStrategyNumber:
348 finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
349 RTOverAboveStrategyNumber);
350 result = FunctionCall2Coll(finfo, colloid, unionval, query);
351 PG_RETURN_BOOL(!DatumGetBool(result));
352
353 case RTOverBelowStrategyNumber:
354 finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
355 RTAboveStrategyNumber);
356 result = FunctionCall2Coll(finfo, colloid, unionval, query);
357 PG_RETURN_BOOL(!DatumGetBool(result));
358
359 case RTOverAboveStrategyNumber:
360 finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
361 RTBelowStrategyNumber);
362 result = FunctionCall2Coll(finfo, colloid, unionval, query);
363 PG_RETURN_BOOL(!DatumGetBool(result));
364
365 case RTAboveStrategyNumber:
366 finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
367 RTOverBelowStrategyNumber);
368 result = FunctionCall2Coll(finfo, colloid, unionval, query);
369 PG_RETURN_BOOL(!DatumGetBool(result));
370
371 /*
372 * Overlap and contains strategies
373 *
374 * These strategies are simple enough that we can simply call the
375 * operator and return its result. Empty elements don't change
376 * the result.
377 */
378
379 case RTOverlapStrategyNumber:
380 case RTContainsStrategyNumber:
381 case RTOldContainsStrategyNumber:
382 case RTContainsElemStrategyNumber:
383 case RTSubStrategyNumber:
384 case RTSubEqualStrategyNumber:
385 finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
386 key->sk_strategy);
387 result = FunctionCall2Coll(finfo, colloid, unionval, query);
388 PG_RETURN_DATUM(result);
389
390 /*
391 * Contained by strategies
392 *
393 * We cannot just call the original operator for the contained by
394 * strategies because some elements can be contained even though
395 * the union is not; instead we use the overlap operator.
396 *
397 * We check for empty elements separately as they are not merged
398 * to the union but contained by everything.
399 */
400
401 case RTContainedByStrategyNumber:
402 case RTOldContainedByStrategyNumber:
403 case RTSuperStrategyNumber:
404 case RTSuperEqualStrategyNumber:
405 finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
406 RTOverlapStrategyNumber);
407 result = FunctionCall2Coll(finfo, colloid, unionval, query);
408 if (DatumGetBool(result))
409 PG_RETURN_BOOL(true);
410
411 PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
412
413 /*
414 * Adjacent strategy
415 *
416 * We test for overlap first but to be safe we need to call the
417 * actual adjacent operator also.
418 *
419 * An empty element cannot be adjacent to any other, so there is
420 * no need to check for it.
421 */
422
423 case RTAdjacentStrategyNumber:
424 finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
425 RTOverlapStrategyNumber);
426 result = FunctionCall2Coll(finfo, colloid, unionval, query);
427 if (DatumGetBool(result))
428 PG_RETURN_BOOL(true);
429
430 finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
431 RTAdjacentStrategyNumber);
432 result = FunctionCall2Coll(finfo, colloid, unionval, query);
433 PG_RETURN_DATUM(result);
434
435 /*
436 * Basic comparison strategies
437 *
438 * It is straightforward to support the equality strategies with
439 * the contains operator. Generally, inequality strategies do not
440 * make much sense for the types which will be used with the
441 * inclusion BRIN family of opclasses, but is possible to
442 * implement them with logical negation of the left-of and
443 * right-of operators.
444 *
445 * NB: These strategies cannot be used with geometric datatypes
446 * that use comparison of areas! The only exception is the "same"
447 * strategy.
448 *
449 * Empty elements are considered to be less than the others. We
450 * cannot use the empty support function to check the query is an
451 * empty element, because the query can be another data type than
452 * the empty support function argument. So we will return true,
453 * if there is a possibility that empty elements will change the
454 * result.
455 */
456
457 case RTLessStrategyNumber:
458 case RTLessEqualStrategyNumber:
459 finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
460 RTRightStrategyNumber);
461 result = FunctionCall2Coll(finfo, colloid, unionval, query);
462 if (!DatumGetBool(result))
463 PG_RETURN_BOOL(true);
464
465 PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
466
467 case RTSameStrategyNumber:
468 case RTEqualStrategyNumber:
469 finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
470 RTContainsStrategyNumber);
471 result = FunctionCall2Coll(finfo, colloid, unionval, query);
472 if (DatumGetBool(result))
473 PG_RETURN_BOOL(true);
474
475 PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
476
477 case RTGreaterEqualStrategyNumber:
478 finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
479 RTLeftStrategyNumber);
480 result = FunctionCall2Coll(finfo, colloid, unionval, query);
481 if (!DatumGetBool(result))
482 PG_RETURN_BOOL(true);
483
484 PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
485
486 case RTGreaterStrategyNumber:
487 /* no need to check for empty elements */
488 finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
489 RTLeftStrategyNumber);
490 result = FunctionCall2Coll(finfo, colloid, unionval, query);
491 PG_RETURN_BOOL(!DatumGetBool(result));
492
493 default:
494 /* shouldn't happen */
495 elog(ERROR, "invalid strategy number %d", key->sk_strategy);
496 PG_RETURN_BOOL(false);
497 }
498 }
499
500 /*
501 * BRIN inclusion union function
502 *
503 * Given two BrinValues, update the first of them as a union of the summary
504 * values contained in both. The second one is untouched.
505 */
506 Datum
brin_inclusion_union(PG_FUNCTION_ARGS)507 brin_inclusion_union(PG_FUNCTION_ARGS)
508 {
509 BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
510 BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1);
511 BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
512 Oid colloid = PG_GET_COLLATION();
513 AttrNumber attno;
514 Form_pg_attribute attr;
515 FmgrInfo *finfo;
516 Datum result;
517
518 Assert(col_a->bv_attno == col_b->bv_attno);
519
520 /* Adjust "hasnulls". */
521 if (!col_a->bv_hasnulls && col_b->bv_hasnulls)
522 col_a->bv_hasnulls = true;
523
524 /* If there are no values in B, there's nothing left to do. */
525 if (col_b->bv_allnulls)
526 PG_RETURN_VOID();
527
528 attno = col_a->bv_attno;
529 attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
530
531 /*
532 * Adjust "allnulls". If A doesn't have values, just copy the values from
533 * B into A, and we're done. We cannot run the operators in this case,
534 * because values in A might contain garbage. Note we already established
535 * that B contains values.
536 */
537 if (col_a->bv_allnulls)
538 {
539 col_a->bv_allnulls = false;
540 col_a->bv_values[INCLUSION_UNION] =
541 datumCopy(col_b->bv_values[INCLUSION_UNION],
542 attr->attbyval, attr->attlen);
543 col_a->bv_values[INCLUSION_UNMERGEABLE] =
544 col_b->bv_values[INCLUSION_UNMERGEABLE];
545 col_a->bv_values[INCLUSION_CONTAINS_EMPTY] =
546 col_b->bv_values[INCLUSION_CONTAINS_EMPTY];
547 PG_RETURN_VOID();
548 }
549
550 /* If B includes empty elements, mark A similarly, if needed. */
551 if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) &&
552 DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY]))
553 col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true);
554
555 /* Check if A includes elements that are not mergeable. */
556 if (DatumGetBool(col_a->bv_values[INCLUSION_UNMERGEABLE]))
557 PG_RETURN_VOID();
558
559 /* If B includes elements that are not mergeable, mark A similarly. */
560 if (DatumGetBool(col_b->bv_values[INCLUSION_UNMERGEABLE]))
561 {
562 col_a->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true);
563 PG_RETURN_VOID();
564 }
565
566 /* Check if A and B are mergeable; if not, mark A unmergeable. */
567 finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE);
568 if (finfo != NULL &&
569 !DatumGetBool(FunctionCall2Coll(finfo, colloid,
570 col_a->bv_values[INCLUSION_UNION],
571 col_b->bv_values[INCLUSION_UNION])))
572 {
573 col_a->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true);
574 PG_RETURN_VOID();
575 }
576
577 /* Finally, merge B to A. */
578 finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE);
579 Assert(finfo != NULL);
580 result = FunctionCall2Coll(finfo, colloid,
581 col_a->bv_values[INCLUSION_UNION],
582 col_b->bv_values[INCLUSION_UNION]);
583 if (!attr->attbyval &&
584 DatumGetPointer(result) != DatumGetPointer(col_a->bv_values[INCLUSION_UNION]))
585 {
586 pfree(DatumGetPointer(col_a->bv_values[INCLUSION_UNION]));
587
588 if (result == col_b->bv_values[INCLUSION_UNION])
589 result = datumCopy(result, attr->attbyval, attr->attlen);
590 }
591 col_a->bv_values[INCLUSION_UNION] = result;
592
593 PG_RETURN_VOID();
594 }
595
596 /*
597 * Cache and return inclusion opclass support procedure
598 *
599 * Return the procedure corresponding to the given function support number
600 * or null if it is not exists.
601 */
602 static FmgrInfo *
inclusion_get_procinfo(BrinDesc * bdesc,uint16 attno,uint16 procnum)603 inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum)
604 {
605 InclusionOpaque *opaque;
606 uint16 basenum = procnum - PROCNUM_BASE;
607
608 /*
609 * We cache these in the opaque struct, to avoid repetitive syscache
610 * lookups.
611 */
612 opaque = (InclusionOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
613
614 /*
615 * If we already searched for this proc and didn't find it, don't bother
616 * searching again.
617 */
618 if (opaque->extra_proc_missing[basenum])
619 return NULL;
620
621 if (opaque->extra_procinfos[basenum].fn_oid == InvalidOid)
622 {
623 if (RegProcedureIsValid(index_getprocid(bdesc->bd_index, attno,
624 procnum)))
625 {
626 fmgr_info_copy(&opaque->extra_procinfos[basenum],
627 index_getprocinfo(bdesc->bd_index, attno, procnum),
628 bdesc->bd_context);
629 }
630 else
631 {
632 opaque->extra_proc_missing[basenum] = true;
633 return NULL;
634 }
635 }
636
637 return &opaque->extra_procinfos[basenum];
638 }
639
640 /*
641 * Cache and return the procedure of the given strategy
642 *
643 * Return the procedure corresponding to the given sub-type and strategy
644 * number. The data type of the index will be used as the left hand side of
645 * the operator and the given sub-type will be used as the right hand side.
646 * Throws an error if the pg_amop row does not exist, but that should not
647 * happen with a properly configured opclass.
648 *
649 * It always throws an error when the data type of the opclass is different
650 * from the data type of the column or the expression. That happens when the
651 * column data type has implicit cast to the opclass data type. We don't
652 * bother casting types, because this situation can easily be avoided by
653 * setting storage data type to that of the opclass. The same problem does not
654 * apply to the data type of the right hand side, because the type in the
655 * ScanKey always matches the opclass' one.
656 *
657 * Note: this function mirrors minmax_get_strategy_procinfo; if changes are
658 * made here, see that function too.
659 */
660 static FmgrInfo *
inclusion_get_strategy_procinfo(BrinDesc * bdesc,uint16 attno,Oid subtype,uint16 strategynum)661 inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype,
662 uint16 strategynum)
663 {
664 InclusionOpaque *opaque;
665
666 Assert(strategynum >= 1 &&
667 strategynum <= RTMaxStrategyNumber);
668
669 opaque = (InclusionOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
670
671 /*
672 * We cache the procedures for the last sub-type in the opaque struct, to
673 * avoid repetitive syscache lookups. If the sub-type is changed,
674 * invalidate all the cached entries.
675 */
676 if (opaque->cached_subtype != subtype)
677 {
678 uint16 i;
679
680 for (i = 1; i <= RTMaxStrategyNumber; i++)
681 opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid;
682 opaque->cached_subtype = subtype;
683 }
684
685 if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid)
686 {
687 Form_pg_attribute attr;
688 HeapTuple tuple;
689 Oid opfamily,
690 oprid;
691 bool isNull;
692
693 opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
694 attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
695 tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
696 ObjectIdGetDatum(attr->atttypid),
697 ObjectIdGetDatum(subtype),
698 Int16GetDatum(strategynum));
699
700 if (!HeapTupleIsValid(tuple))
701 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
702 strategynum, attr->atttypid, subtype, opfamily);
703
704 oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple,
705 Anum_pg_amop_amopopr, &isNull));
706 ReleaseSysCache(tuple);
707 Assert(!isNull && RegProcedureIsValid(oprid));
708
709 fmgr_info_cxt(get_opcode(oprid),
710 &opaque->strategy_procinfos[strategynum - 1],
711 bdesc->bd_context);
712 }
713
714 return &opaque->strategy_procinfos[strategynum - 1];
715 }
716