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