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