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-2021, 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_regular_nulls = true;
112 	result->oi_opaque = (InclusionOpaque *)
113 		MAXALIGN((char *) result + SizeofBrinOpcInfo(3));
114 
115 	/* the union */
116 	result->oi_typcache[INCLUSION_UNION] =
117 		lookup_type_cache(typoid, 0);
118 
119 	/* includes elements that are not mergeable */
120 	result->oi_typcache[INCLUSION_UNMERGEABLE] = bool_typcache;
121 
122 	/* includes the empty element */
123 	result->oi_typcache[INCLUSION_CONTAINS_EMPTY] = bool_typcache;
124 
125 	PG_RETURN_POINTER(result);
126 }
127 
128 /*
129  * BRIN inclusion add value function
130  *
131  * Examine the given index tuple (which contains partial status of a certain
132  * page range) by comparing it to the given value that comes from another heap
133  * tuple.  If the new value is outside the union specified by the existing
134  * tuple values, update the index tuple and return true.  Otherwise, return
135  * false and do not modify in this case.
136  */
137 Datum
brin_inclusion_add_value(PG_FUNCTION_ARGS)138 brin_inclusion_add_value(PG_FUNCTION_ARGS)
139 {
140 	BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
141 	BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
142 	Datum		newval = PG_GETARG_DATUM(2);
143 	bool		isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3);
144 	Oid			colloid = PG_GET_COLLATION();
145 	FmgrInfo   *finfo;
146 	Datum		result;
147 	bool		new = false;
148 	AttrNumber	attno;
149 	Form_pg_attribute attr;
150 
151 	Assert(!isnull);
152 
153 	attno = column->bv_attno;
154 	attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
155 
156 	/*
157 	 * If the recorded value is null, copy the new value (which we know to be
158 	 * not null), and we're almost done.
159 	 */
160 	if (column->bv_allnulls)
161 	{
162 		column->bv_values[INCLUSION_UNION] =
163 			datumCopy(newval, attr->attbyval, attr->attlen);
164 		column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(false);
165 		column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(false);
166 		column->bv_allnulls = false;
167 		new = true;
168 	}
169 
170 	/*
171 	 * No need for further processing if the block range is marked as
172 	 * containing unmergeable values.
173 	 */
174 	if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE]))
175 		PG_RETURN_BOOL(false);
176 
177 	/*
178 	 * If the opclass supports the concept of empty values, test the passed
179 	 * new value for emptiness; if it returns true, we need to set the
180 	 * "contains empty" flag in the element (unless already set).
181 	 */
182 	finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_EMPTY);
183 	if (finfo != NULL && DatumGetBool(FunctionCall1Coll(finfo, colloid, newval)))
184 	{
185 		if (!DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]))
186 		{
187 			column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true);
188 			PG_RETURN_BOOL(true);
189 		}
190 
191 		PG_RETURN_BOOL(false);
192 	}
193 
194 	if (new)
195 		PG_RETURN_BOOL(true);
196 
197 	/* Check if the new value is already contained. */
198 	finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_CONTAINS);
199 	if (finfo != NULL &&
200 		DatumGetBool(FunctionCall2Coll(finfo, colloid,
201 									   column->bv_values[INCLUSION_UNION],
202 									   newval)))
203 		PG_RETURN_BOOL(false);
204 
205 	/*
206 	 * Check if the new value is mergeable to the existing union.  If it is
207 	 * not, mark the value as containing unmergeable elements and get out.
208 	 *
209 	 * Note: at this point we could remove the value from the union, since
210 	 * it's not going to be used any longer.  However, the BRIN framework
211 	 * doesn't allow for the value not being present.  Improve someday.
212 	 */
213 	finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE);
214 	if (finfo != NULL &&
215 		!DatumGetBool(FunctionCall2Coll(finfo, colloid,
216 										column->bv_values[INCLUSION_UNION],
217 										newval)))
218 	{
219 		column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true);
220 		PG_RETURN_BOOL(true);
221 	}
222 
223 	/* Finally, merge the new value to the existing union. */
224 	finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE);
225 	Assert(finfo != NULL);
226 	result = FunctionCall2Coll(finfo, colloid,
227 							   column->bv_values[INCLUSION_UNION], newval);
228 	if (!attr->attbyval &&
229 		DatumGetPointer(result) != DatumGetPointer(column->bv_values[INCLUSION_UNION]))
230 	{
231 		pfree(DatumGetPointer(column->bv_values[INCLUSION_UNION]));
232 
233 		if (result == newval)
234 			result = datumCopy(result, attr->attbyval, attr->attlen);
235 	}
236 	column->bv_values[INCLUSION_UNION] = result;
237 
238 	PG_RETURN_BOOL(true);
239 }
240 
241 /*
242  * BRIN inclusion consistent function
243  *
244  * We're no longer dealing with NULL keys in the consistent function, that is
245  * now handled by the AM code. That means we should not get any all-NULL ranges
246  * either, because those can't be consistent with regular (not [IS] NULL) keys.
247  *
248  * All of the strategies are optional.
249  */
250 Datum
brin_inclusion_consistent(PG_FUNCTION_ARGS)251 brin_inclusion_consistent(PG_FUNCTION_ARGS)
252 {
253 	BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
254 	BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
255 	ScanKey		key = (ScanKey) PG_GETARG_POINTER(2);
256 	Oid			colloid = PG_GET_COLLATION(),
257 				subtype;
258 	Datum		unionval;
259 	AttrNumber	attno;
260 	Datum		query;
261 	FmgrInfo   *finfo;
262 	Datum		result;
263 
264 	/* This opclass uses the old signature with only three arguments. */
265 	Assert(PG_NARGS() == 3);
266 
267 	/* Should not be dealing with all-NULL ranges. */
268 	Assert(!column->bv_allnulls);
269 
270 	/* It has to be checked, if it contains elements that are not mergeable. */
271 	if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE]))
272 		PG_RETURN_BOOL(true);
273 
274 	attno = key->sk_attno;
275 	subtype = key->sk_subtype;
276 	query = key->sk_argument;
277 	unionval = column->bv_values[INCLUSION_UNION];
278 	switch (key->sk_strategy)
279 	{
280 			/*
281 			 * Placement strategies
282 			 *
283 			 * These are implemented by logically negating the result of the
284 			 * converse placement operator; for this to work, the converse
285 			 * operator must be part of the opclass.  An error will be thrown
286 			 * by inclusion_get_strategy_procinfo() if the required strategy
287 			 * is not part of the opclass.
288 			 *
289 			 * These all return false if either argument is empty, so there is
290 			 * no need to check for empty elements.
291 			 */
292 
293 		case RTLeftStrategyNumber:
294 			finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
295 													RTOverRightStrategyNumber);
296 			result = FunctionCall2Coll(finfo, colloid, unionval, query);
297 			PG_RETURN_BOOL(!DatumGetBool(result));
298 
299 		case RTOverLeftStrategyNumber:
300 			finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
301 													RTRightStrategyNumber);
302 			result = FunctionCall2Coll(finfo, colloid, unionval, query);
303 			PG_RETURN_BOOL(!DatumGetBool(result));
304 
305 		case RTOverRightStrategyNumber:
306 			finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
307 													RTLeftStrategyNumber);
308 			result = FunctionCall2Coll(finfo, colloid, unionval, query);
309 			PG_RETURN_BOOL(!DatumGetBool(result));
310 
311 		case RTRightStrategyNumber:
312 			finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
313 													RTOverLeftStrategyNumber);
314 			result = FunctionCall2Coll(finfo, colloid, unionval, query);
315 			PG_RETURN_BOOL(!DatumGetBool(result));
316 
317 		case RTBelowStrategyNumber:
318 			finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
319 													RTOverAboveStrategyNumber);
320 			result = FunctionCall2Coll(finfo, colloid, unionval, query);
321 			PG_RETURN_BOOL(!DatumGetBool(result));
322 
323 		case RTOverBelowStrategyNumber:
324 			finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
325 													RTAboveStrategyNumber);
326 			result = FunctionCall2Coll(finfo, colloid, unionval, query);
327 			PG_RETURN_BOOL(!DatumGetBool(result));
328 
329 		case RTOverAboveStrategyNumber:
330 			finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
331 													RTBelowStrategyNumber);
332 			result = FunctionCall2Coll(finfo, colloid, unionval, query);
333 			PG_RETURN_BOOL(!DatumGetBool(result));
334 
335 		case RTAboveStrategyNumber:
336 			finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
337 													RTOverBelowStrategyNumber);
338 			result = FunctionCall2Coll(finfo, colloid, unionval, query);
339 			PG_RETURN_BOOL(!DatumGetBool(result));
340 
341 			/*
342 			 * Overlap and contains strategies
343 			 *
344 			 * These strategies are simple enough that we can simply call the
345 			 * operator and return its result.  Empty elements don't change
346 			 * the result.
347 			 */
348 
349 		case RTOverlapStrategyNumber:
350 		case RTContainsStrategyNumber:
351 		case RTContainsElemStrategyNumber:
352 		case RTSubStrategyNumber:
353 		case RTSubEqualStrategyNumber:
354 			finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
355 													key->sk_strategy);
356 			result = FunctionCall2Coll(finfo, colloid, unionval, query);
357 			PG_RETURN_DATUM(result);
358 
359 			/*
360 			 * Contained by strategies
361 			 *
362 			 * We cannot just call the original operator for the contained by
363 			 * strategies because some elements can be contained even though
364 			 * the union is not; instead we use the overlap operator.
365 			 *
366 			 * We check for empty elements separately as they are not merged
367 			 * to the union but contained by everything.
368 			 */
369 
370 		case RTContainedByStrategyNumber:
371 		case RTSuperStrategyNumber:
372 		case RTSuperEqualStrategyNumber:
373 			finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
374 													RTOverlapStrategyNumber);
375 			result = FunctionCall2Coll(finfo, colloid, unionval, query);
376 			if (DatumGetBool(result))
377 				PG_RETURN_BOOL(true);
378 
379 			PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
380 
381 			/*
382 			 * Adjacent strategy
383 			 *
384 			 * We test for overlap first but to be safe we need to call the
385 			 * actual adjacent operator also.
386 			 *
387 			 * An empty element cannot be adjacent to any other, so there is
388 			 * no need to check for it.
389 			 */
390 
391 		case RTAdjacentStrategyNumber:
392 			finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
393 													RTOverlapStrategyNumber);
394 			result = FunctionCall2Coll(finfo, colloid, unionval, query);
395 			if (DatumGetBool(result))
396 				PG_RETURN_BOOL(true);
397 
398 			finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
399 													RTAdjacentStrategyNumber);
400 			result = FunctionCall2Coll(finfo, colloid, unionval, query);
401 			PG_RETURN_DATUM(result);
402 
403 			/*
404 			 * Basic comparison strategies
405 			 *
406 			 * It is straightforward to support the equality strategies with
407 			 * the contains operator.  Generally, inequality strategies do not
408 			 * make much sense for the types which will be used with the
409 			 * inclusion BRIN family of opclasses, but it is possible to
410 			 * implement them with logical negation of the left-of and
411 			 * right-of operators.
412 			 *
413 			 * NB: These strategies cannot be used with geometric datatypes
414 			 * that use comparison of areas!  The only exception is the "same"
415 			 * strategy.
416 			 *
417 			 * Empty elements are considered to be less than the others.  We
418 			 * cannot use the empty support function to check the query is an
419 			 * empty element, because the query can be another data type than
420 			 * the empty support function argument.  So we will return true,
421 			 * if there is a possibility that empty elements will change the
422 			 * result.
423 			 */
424 
425 		case RTLessStrategyNumber:
426 		case RTLessEqualStrategyNumber:
427 			finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
428 													RTRightStrategyNumber);
429 			result = FunctionCall2Coll(finfo, colloid, unionval, query);
430 			if (!DatumGetBool(result))
431 				PG_RETURN_BOOL(true);
432 
433 			PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
434 
435 		case RTSameStrategyNumber:
436 		case RTEqualStrategyNumber:
437 			finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
438 													RTContainsStrategyNumber);
439 			result = FunctionCall2Coll(finfo, colloid, unionval, query);
440 			if (DatumGetBool(result))
441 				PG_RETURN_BOOL(true);
442 
443 			PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
444 
445 		case RTGreaterEqualStrategyNumber:
446 			finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
447 													RTLeftStrategyNumber);
448 			result = FunctionCall2Coll(finfo, colloid, unionval, query);
449 			if (!DatumGetBool(result))
450 				PG_RETURN_BOOL(true);
451 
452 			PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
453 
454 		case RTGreaterStrategyNumber:
455 			/* no need to check for empty elements */
456 			finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
457 													RTLeftStrategyNumber);
458 			result = FunctionCall2Coll(finfo, colloid, unionval, query);
459 			PG_RETURN_BOOL(!DatumGetBool(result));
460 
461 		default:
462 			/* shouldn't happen */
463 			elog(ERROR, "invalid strategy number %d", key->sk_strategy);
464 			PG_RETURN_BOOL(false);
465 	}
466 }
467 
468 /*
469  * BRIN inclusion union function
470  *
471  * Given two BrinValues, update the first of them as a union of the summary
472  * values contained in both.  The second one is untouched.
473  */
474 Datum
brin_inclusion_union(PG_FUNCTION_ARGS)475 brin_inclusion_union(PG_FUNCTION_ARGS)
476 {
477 	BrinDesc   *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
478 	BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1);
479 	BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
480 	Oid			colloid = PG_GET_COLLATION();
481 	AttrNumber	attno;
482 	Form_pg_attribute attr;
483 	FmgrInfo   *finfo;
484 	Datum		result;
485 
486 	Assert(col_a->bv_attno == col_b->bv_attno);
487 	Assert(!col_a->bv_allnulls && !col_b->bv_allnulls);
488 
489 	attno = col_a->bv_attno;
490 	attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
491 
492 	/* If B includes empty elements, mark A similarly, if needed. */
493 	if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) &&
494 		DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY]))
495 		col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true);
496 
497 	/* Check if A includes elements that are not mergeable. */
498 	if (DatumGetBool(col_a->bv_values[INCLUSION_UNMERGEABLE]))
499 		PG_RETURN_VOID();
500 
501 	/* If B includes elements that are not mergeable, mark A similarly. */
502 	if (DatumGetBool(col_b->bv_values[INCLUSION_UNMERGEABLE]))
503 	{
504 		col_a->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true);
505 		PG_RETURN_VOID();
506 	}
507 
508 	/* Check if A and B are mergeable; if not, mark A unmergeable. */
509 	finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE);
510 	if (finfo != NULL &&
511 		!DatumGetBool(FunctionCall2Coll(finfo, colloid,
512 										col_a->bv_values[INCLUSION_UNION],
513 										col_b->bv_values[INCLUSION_UNION])))
514 	{
515 		col_a->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true);
516 		PG_RETURN_VOID();
517 	}
518 
519 	/* Finally, merge B to A. */
520 	finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE);
521 	Assert(finfo != NULL);
522 	result = FunctionCall2Coll(finfo, colloid,
523 							   col_a->bv_values[INCLUSION_UNION],
524 							   col_b->bv_values[INCLUSION_UNION]);
525 	if (!attr->attbyval &&
526 		DatumGetPointer(result) != DatumGetPointer(col_a->bv_values[INCLUSION_UNION]))
527 	{
528 		pfree(DatumGetPointer(col_a->bv_values[INCLUSION_UNION]));
529 
530 		if (result == col_b->bv_values[INCLUSION_UNION])
531 			result = datumCopy(result, attr->attbyval, attr->attlen);
532 	}
533 	col_a->bv_values[INCLUSION_UNION] = result;
534 
535 	PG_RETURN_VOID();
536 }
537 
538 /*
539  * Cache and return inclusion opclass support procedure
540  *
541  * Return the procedure corresponding to the given function support number
542  * or null if it is not exists.
543  */
544 static FmgrInfo *
inclusion_get_procinfo(BrinDesc * bdesc,uint16 attno,uint16 procnum)545 inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum)
546 {
547 	InclusionOpaque *opaque;
548 	uint16		basenum = procnum - PROCNUM_BASE;
549 
550 	/*
551 	 * We cache these in the opaque struct, to avoid repetitive syscache
552 	 * lookups.
553 	 */
554 	opaque = (InclusionOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
555 
556 	/*
557 	 * If we already searched for this proc and didn't find it, don't bother
558 	 * searching again.
559 	 */
560 	if (opaque->extra_proc_missing[basenum])
561 		return NULL;
562 
563 	if (opaque->extra_procinfos[basenum].fn_oid == InvalidOid)
564 	{
565 		if (RegProcedureIsValid(index_getprocid(bdesc->bd_index, attno,
566 												procnum)))
567 		{
568 			fmgr_info_copy(&opaque->extra_procinfos[basenum],
569 						   index_getprocinfo(bdesc->bd_index, attno, procnum),
570 						   bdesc->bd_context);
571 		}
572 		else
573 		{
574 			opaque->extra_proc_missing[basenum] = true;
575 			return NULL;
576 		}
577 	}
578 
579 	return &opaque->extra_procinfos[basenum];
580 }
581 
582 /*
583  * Cache and return the procedure of the given strategy
584  *
585  * Return the procedure corresponding to the given sub-type and strategy
586  * number.  The data type of the index will be used as the left hand side of
587  * the operator and the given sub-type will be used as the right hand side.
588  * Throws an error if the pg_amop row does not exist, but that should not
589  * happen with a properly configured opclass.
590  *
591  * It always throws an error when the data type of the opclass is different
592  * from the data type of the column or the expression.  That happens when the
593  * column data type has implicit cast to the opclass data type.  We don't
594  * bother casting types, because this situation can easily be avoided by
595  * setting storage data type to that of the opclass.  The same problem does not
596  * apply to the data type of the right hand side, because the type in the
597  * ScanKey always matches the opclass' one.
598  *
599  * Note: this function mirrors minmax_get_strategy_procinfo; if changes are
600  * made here, see that function too.
601  */
602 static FmgrInfo *
inclusion_get_strategy_procinfo(BrinDesc * bdesc,uint16 attno,Oid subtype,uint16 strategynum)603 inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype,
604 								uint16 strategynum)
605 {
606 	InclusionOpaque *opaque;
607 
608 	Assert(strategynum >= 1 &&
609 		   strategynum <= RTMaxStrategyNumber);
610 
611 	opaque = (InclusionOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
612 
613 	/*
614 	 * We cache the procedures for the last sub-type in the opaque struct, to
615 	 * avoid repetitive syscache lookups.  If the sub-type is changed,
616 	 * invalidate all the cached entries.
617 	 */
618 	if (opaque->cached_subtype != subtype)
619 	{
620 		uint16		i;
621 
622 		for (i = 1; i <= RTMaxStrategyNumber; i++)
623 			opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid;
624 		opaque->cached_subtype = subtype;
625 	}
626 
627 	if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid)
628 	{
629 		Form_pg_attribute attr;
630 		HeapTuple	tuple;
631 		Oid			opfamily,
632 					oprid;
633 		bool		isNull;
634 
635 		opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
636 		attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
637 		tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
638 								ObjectIdGetDatum(attr->atttypid),
639 								ObjectIdGetDatum(subtype),
640 								Int16GetDatum(strategynum));
641 
642 		if (!HeapTupleIsValid(tuple))
643 			elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
644 				 strategynum, attr->atttypid, subtype, opfamily);
645 
646 		oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple,
647 												 Anum_pg_amop_amopopr, &isNull));
648 		ReleaseSysCache(tuple);
649 		Assert(!isNull && RegProcedureIsValid(oprid));
650 
651 		fmgr_info_cxt(get_opcode(oprid),
652 					  &opaque->strategy_procinfos[strategynum - 1],
653 					  bdesc->bd_context);
654 	}
655 
656 	return &opaque->strategy_procinfos[strategynum - 1];
657 }
658