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-2019, 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 [INCLUSION_MAX_PROCNUMS]; |
79 | bool [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 |
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 |
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 | pfree(DatumGetPointer(column->bv_values[INCLUSION_UNION])); |
240 | column->bv_values[INCLUSION_UNION] = result; |
241 | |
242 | PG_RETURN_BOOL(true); |
243 | } |
244 | |
245 | /* |
246 | * BRIN inclusion consistent function |
247 | * |
248 | * All of the strategies are optional. |
249 | */ |
250 | Datum |
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 | Assert(key->sk_attno == column->bv_attno); |
265 | |
266 | /* Handle IS NULL/IS NOT NULL tests. */ |
267 | if (key->sk_flags & SK_ISNULL) |
268 | { |
269 | if (key->sk_flags & SK_SEARCHNULL) |
270 | { |
271 | if (column->bv_allnulls || column->bv_hasnulls) |
272 | PG_RETURN_BOOL(true); |
273 | PG_RETURN_BOOL(false); |
274 | } |
275 | |
276 | /* |
277 | * For IS NOT NULL, we can only skip ranges that are known to have |
278 | * only nulls. |
279 | */ |
280 | if (key->sk_flags & SK_SEARCHNOTNULL) |
281 | PG_RETURN_BOOL(!column->bv_allnulls); |
282 | |
283 | /* |
284 | * Neither IS NULL nor IS NOT NULL was used; assume all indexable |
285 | * operators are strict and return false. |
286 | */ |
287 | PG_RETURN_BOOL(false); |
288 | } |
289 | |
290 | /* If it is all nulls, it cannot possibly be consistent. */ |
291 | if (column->bv_allnulls) |
292 | PG_RETURN_BOOL(false); |
293 | |
294 | /* It has to be checked, if it contains elements that are not mergeable. */ |
295 | if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) |
296 | PG_RETURN_BOOL(true); |
297 | |
298 | attno = key->sk_attno; |
299 | subtype = key->sk_subtype; |
300 | query = key->sk_argument; |
301 | unionval = column->bv_values[INCLUSION_UNION]; |
302 | switch (key->sk_strategy) |
303 | { |
304 | /* |
305 | * Placement strategies |
306 | * |
307 | * These are implemented by logically negating the result of the |
308 | * converse placement operator; for this to work, the converse |
309 | * operator must be part of the opclass. An error will be thrown |
310 | * by inclusion_get_strategy_procinfo() if the required strategy |
311 | * is not part of the opclass. |
312 | * |
313 | * These all return false if either argument is empty, so there is |
314 | * no need to check for empty elements. |
315 | */ |
316 | |
317 | case RTLeftStrategyNumber: |
318 | finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
319 | RTOverRightStrategyNumber); |
320 | result = FunctionCall2Coll(finfo, colloid, unionval, query); |
321 | PG_RETURN_BOOL(!DatumGetBool(result)); |
322 | |
323 | case RTOverLeftStrategyNumber: |
324 | finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
325 | RTRightStrategyNumber); |
326 | result = FunctionCall2Coll(finfo, colloid, unionval, query); |
327 | PG_RETURN_BOOL(!DatumGetBool(result)); |
328 | |
329 | case RTOverRightStrategyNumber: |
330 | finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
331 | RTLeftStrategyNumber); |
332 | result = FunctionCall2Coll(finfo, colloid, unionval, query); |
333 | PG_RETURN_BOOL(!DatumGetBool(result)); |
334 | |
335 | case RTRightStrategyNumber: |
336 | finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
337 | RTOverLeftStrategyNumber); |
338 | result = FunctionCall2Coll(finfo, colloid, unionval, query); |
339 | PG_RETURN_BOOL(!DatumGetBool(result)); |
340 | |
341 | case RTBelowStrategyNumber: |
342 | finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
343 | RTOverAboveStrategyNumber); |
344 | result = FunctionCall2Coll(finfo, colloid, unionval, query); |
345 | PG_RETURN_BOOL(!DatumGetBool(result)); |
346 | |
347 | case RTOverBelowStrategyNumber: |
348 | finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
349 | RTAboveStrategyNumber); |
350 | result = FunctionCall2Coll(finfo, colloid, unionval, query); |
351 | PG_RETURN_BOOL(!DatumGetBool(result)); |
352 | |
353 | case RTOverAboveStrategyNumber: |
354 | finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
355 | RTBelowStrategyNumber); |
356 | result = FunctionCall2Coll(finfo, colloid, unionval, query); |
357 | PG_RETURN_BOOL(!DatumGetBool(result)); |
358 | |
359 | case RTAboveStrategyNumber: |
360 | finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
361 | RTOverBelowStrategyNumber); |
362 | result = FunctionCall2Coll(finfo, colloid, unionval, query); |
363 | PG_RETURN_BOOL(!DatumGetBool(result)); |
364 | |
365 | /* |
366 | * Overlap and contains strategies |
367 | * |
368 | * These strategies are simple enough that we can simply call the |
369 | * operator and return its result. Empty elements don't change |
370 | * the result. |
371 | */ |
372 | |
373 | case RTOverlapStrategyNumber: |
374 | case RTContainsStrategyNumber: |
375 | case RTOldContainsStrategyNumber: |
376 | case RTContainsElemStrategyNumber: |
377 | case RTSubStrategyNumber: |
378 | case RTSubEqualStrategyNumber: |
379 | finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
380 | key->sk_strategy); |
381 | result = FunctionCall2Coll(finfo, colloid, unionval, query); |
382 | PG_RETURN_DATUM(result); |
383 | |
384 | /* |
385 | * Contained by strategies |
386 | * |
387 | * We cannot just call the original operator for the contained by |
388 | * strategies because some elements can be contained even though |
389 | * the union is not; instead we use the overlap operator. |
390 | * |
391 | * We check for empty elements separately as they are not merged |
392 | * to the union but contained by everything. |
393 | */ |
394 | |
395 | case RTContainedByStrategyNumber: |
396 | case RTOldContainedByStrategyNumber: |
397 | case RTSuperStrategyNumber: |
398 | case RTSuperEqualStrategyNumber: |
399 | finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
400 | RTOverlapStrategyNumber); |
401 | result = FunctionCall2Coll(finfo, colloid, unionval, query); |
402 | if (DatumGetBool(result)) |
403 | PG_RETURN_BOOL(true); |
404 | |
405 | PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); |
406 | |
407 | /* |
408 | * Adjacent strategy |
409 | * |
410 | * We test for overlap first but to be safe we need to call the |
411 | * actual adjacent operator also. |
412 | * |
413 | * An empty element cannot be adjacent to any other, so there is |
414 | * no need to check for it. |
415 | */ |
416 | |
417 | case RTAdjacentStrategyNumber: |
418 | finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
419 | RTOverlapStrategyNumber); |
420 | result = FunctionCall2Coll(finfo, colloid, unionval, query); |
421 | if (DatumGetBool(result)) |
422 | PG_RETURN_BOOL(true); |
423 | |
424 | finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
425 | RTAdjacentStrategyNumber); |
426 | result = FunctionCall2Coll(finfo, colloid, unionval, query); |
427 | PG_RETURN_DATUM(result); |
428 | |
429 | /* |
430 | * Basic comparison strategies |
431 | * |
432 | * It is straightforward to support the equality strategies with |
433 | * the contains operator. Generally, inequality strategies do not |
434 | * make much sense for the types which will be used with the |
435 | * inclusion BRIN family of opclasses, but it is possible to |
436 | * implement them with logical negation of the left-of and |
437 | * right-of operators. |
438 | * |
439 | * NB: These strategies cannot be used with geometric datatypes |
440 | * that use comparison of areas! The only exception is the "same" |
441 | * strategy. |
442 | * |
443 | * Empty elements are considered to be less than the others. We |
444 | * cannot use the empty support function to check the query is an |
445 | * empty element, because the query can be another data type than |
446 | * the empty support function argument. So we will return true, |
447 | * if there is a possibility that empty elements will change the |
448 | * result. |
449 | */ |
450 | |
451 | case RTLessStrategyNumber: |
452 | case RTLessEqualStrategyNumber: |
453 | finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
454 | RTRightStrategyNumber); |
455 | result = FunctionCall2Coll(finfo, colloid, unionval, query); |
456 | if (!DatumGetBool(result)) |
457 | PG_RETURN_BOOL(true); |
458 | |
459 | PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); |
460 | |
461 | case RTSameStrategyNumber: |
462 | case RTEqualStrategyNumber: |
463 | finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
464 | RTContainsStrategyNumber); |
465 | result = FunctionCall2Coll(finfo, colloid, unionval, query); |
466 | if (DatumGetBool(result)) |
467 | PG_RETURN_BOOL(true); |
468 | |
469 | PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); |
470 | |
471 | case RTGreaterEqualStrategyNumber: |
472 | finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
473 | RTLeftStrategyNumber); |
474 | result = FunctionCall2Coll(finfo, colloid, unionval, query); |
475 | if (!DatumGetBool(result)) |
476 | PG_RETURN_BOOL(true); |
477 | |
478 | PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); |
479 | |
480 | case RTGreaterStrategyNumber: |
481 | /* no need to check for empty elements */ |
482 | finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
483 | RTLeftStrategyNumber); |
484 | result = FunctionCall2Coll(finfo, colloid, unionval, query); |
485 | PG_RETURN_BOOL(!DatumGetBool(result)); |
486 | |
487 | default: |
488 | /* shouldn't happen */ |
489 | elog(ERROR, "invalid strategy number %d" , key->sk_strategy); |
490 | PG_RETURN_BOOL(false); |
491 | } |
492 | } |
493 | |
494 | /* |
495 | * BRIN inclusion union function |
496 | * |
497 | * Given two BrinValues, update the first of them as a union of the summary |
498 | * values contained in both. The second one is untouched. |
499 | */ |
500 | Datum |
501 | brin_inclusion_union(PG_FUNCTION_ARGS) |
502 | { |
503 | BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); |
504 | BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1); |
505 | BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2); |
506 | Oid colloid = PG_GET_COLLATION(); |
507 | AttrNumber attno; |
508 | Form_pg_attribute attr; |
509 | FmgrInfo *finfo; |
510 | Datum result; |
511 | |
512 | Assert(col_a->bv_attno == col_b->bv_attno); |
513 | |
514 | /* Adjust "hasnulls". */ |
515 | if (!col_a->bv_hasnulls && col_b->bv_hasnulls) |
516 | col_a->bv_hasnulls = true; |
517 | |
518 | /* If there are no values in B, there's nothing left to do. */ |
519 | if (col_b->bv_allnulls) |
520 | PG_RETURN_VOID(); |
521 | |
522 | attno = col_a->bv_attno; |
523 | attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); |
524 | |
525 | /* |
526 | * Adjust "allnulls". If A doesn't have values, just copy the values from |
527 | * B into A, and we're done. We cannot run the operators in this case, |
528 | * because values in A might contain garbage. Note we already established |
529 | * that B contains values. |
530 | */ |
531 | if (col_a->bv_allnulls) |
532 | { |
533 | col_a->bv_allnulls = false; |
534 | col_a->bv_values[INCLUSION_UNION] = |
535 | datumCopy(col_b->bv_values[INCLUSION_UNION], |
536 | attr->attbyval, attr->attlen); |
537 | col_a->bv_values[INCLUSION_UNMERGEABLE] = |
538 | col_b->bv_values[INCLUSION_UNMERGEABLE]; |
539 | col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = |
540 | col_b->bv_values[INCLUSION_CONTAINS_EMPTY]; |
541 | PG_RETURN_VOID(); |
542 | } |
543 | |
544 | /* If B includes empty elements, mark A similarly, if needed. */ |
545 | if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) && |
546 | DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY])) |
547 | col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true); |
548 | |
549 | /* Check if A includes elements that are not mergeable. */ |
550 | if (DatumGetBool(col_a->bv_values[INCLUSION_UNMERGEABLE])) |
551 | PG_RETURN_VOID(); |
552 | |
553 | /* If B includes elements that are not mergeable, mark A similarly. */ |
554 | if (DatumGetBool(col_b->bv_values[INCLUSION_UNMERGEABLE])) |
555 | { |
556 | col_a->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true); |
557 | PG_RETURN_VOID(); |
558 | } |
559 | |
560 | /* Check if A and B are mergeable; if not, mark A unmergeable. */ |
561 | finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE); |
562 | if (finfo != NULL && |
563 | !DatumGetBool(FunctionCall2Coll(finfo, colloid, |
564 | col_a->bv_values[INCLUSION_UNION], |
565 | col_b->bv_values[INCLUSION_UNION]))) |
566 | { |
567 | col_a->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true); |
568 | PG_RETURN_VOID(); |
569 | } |
570 | |
571 | /* Finally, merge B to A. */ |
572 | finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE); |
573 | Assert(finfo != NULL); |
574 | result = FunctionCall2Coll(finfo, colloid, |
575 | col_a->bv_values[INCLUSION_UNION], |
576 | col_b->bv_values[INCLUSION_UNION]); |
577 | if (!attr->attbyval) |
578 | pfree(DatumGetPointer(col_a->bv_values[INCLUSION_UNION])); |
579 | col_a->bv_values[INCLUSION_UNION] = result; |
580 | |
581 | PG_RETURN_VOID(); |
582 | } |
583 | |
584 | /* |
585 | * Cache and return inclusion opclass support procedure |
586 | * |
587 | * Return the procedure corresponding to the given function support number |
588 | * or null if it is not exists. |
589 | */ |
590 | static FmgrInfo * |
591 | inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum) |
592 | { |
593 | InclusionOpaque *opaque; |
594 | uint16 basenum = procnum - PROCNUM_BASE; |
595 | |
596 | /* |
597 | * We cache these in the opaque struct, to avoid repetitive syscache |
598 | * lookups. |
599 | */ |
600 | opaque = (InclusionOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; |
601 | |
602 | /* |
603 | * If we already searched for this proc and didn't find it, don't bother |
604 | * searching again. |
605 | */ |
606 | if (opaque->extra_proc_missing[basenum]) |
607 | return NULL; |
608 | |
609 | if (opaque->extra_procinfos[basenum].fn_oid == InvalidOid) |
610 | { |
611 | if (RegProcedureIsValid(index_getprocid(bdesc->bd_index, attno, |
612 | procnum))) |
613 | { |
614 | fmgr_info_copy(&opaque->extra_procinfos[basenum], |
615 | index_getprocinfo(bdesc->bd_index, attno, procnum), |
616 | bdesc->bd_context); |
617 | } |
618 | else |
619 | { |
620 | opaque->extra_proc_missing[basenum] = true; |
621 | return NULL; |
622 | } |
623 | } |
624 | |
625 | return &opaque->extra_procinfos[basenum]; |
626 | } |
627 | |
628 | /* |
629 | * Cache and return the procedure of the given strategy |
630 | * |
631 | * Return the procedure corresponding to the given sub-type and strategy |
632 | * number. The data type of the index will be used as the left hand side of |
633 | * the operator and the given sub-type will be used as the right hand side. |
634 | * Throws an error if the pg_amop row does not exist, but that should not |
635 | * happen with a properly configured opclass. |
636 | * |
637 | * It always throws an error when the data type of the opclass is different |
638 | * from the data type of the column or the expression. That happens when the |
639 | * column data type has implicit cast to the opclass data type. We don't |
640 | * bother casting types, because this situation can easily be avoided by |
641 | * setting storage data type to that of the opclass. The same problem does not |
642 | * apply to the data type of the right hand side, because the type in the |
643 | * ScanKey always matches the opclass' one. |
644 | * |
645 | * Note: this function mirrors minmax_get_strategy_procinfo; if changes are |
646 | * made here, see that function too. |
647 | */ |
648 | static FmgrInfo * |
649 | inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, |
650 | uint16 strategynum) |
651 | { |
652 | InclusionOpaque *opaque; |
653 | |
654 | Assert(strategynum >= 1 && |
655 | strategynum <= RTMaxStrategyNumber); |
656 | |
657 | opaque = (InclusionOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; |
658 | |
659 | /* |
660 | * We cache the procedures for the last sub-type in the opaque struct, to |
661 | * avoid repetitive syscache lookups. If the sub-type is changed, |
662 | * invalidate all the cached entries. |
663 | */ |
664 | if (opaque->cached_subtype != subtype) |
665 | { |
666 | uint16 i; |
667 | |
668 | for (i = 1; i <= RTMaxStrategyNumber; i++) |
669 | opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid; |
670 | opaque->cached_subtype = subtype; |
671 | } |
672 | |
673 | if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid) |
674 | { |
675 | Form_pg_attribute attr; |
676 | HeapTuple tuple; |
677 | Oid opfamily, |
678 | oprid; |
679 | bool isNull; |
680 | |
681 | opfamily = bdesc->bd_index->rd_opfamily[attno - 1]; |
682 | attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1); |
683 | tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily), |
684 | ObjectIdGetDatum(attr->atttypid), |
685 | ObjectIdGetDatum(subtype), |
686 | Int16GetDatum(strategynum)); |
687 | |
688 | if (!HeapTupleIsValid(tuple)) |
689 | elog(ERROR, "missing operator %d(%u,%u) in opfamily %u" , |
690 | strategynum, attr->atttypid, subtype, opfamily); |
691 | |
692 | oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple, |
693 | Anum_pg_amop_amopopr, &isNull)); |
694 | ReleaseSysCache(tuple); |
695 | Assert(!isNull && RegProcedureIsValid(oprid)); |
696 | |
697 | fmgr_info_cxt(get_opcode(oprid), |
698 | &opaque->strategy_procinfos[strategynum - 1], |
699 | bdesc->bd_context); |
700 | } |
701 | |
702 | return &opaque->strategy_procinfos[strategynum - 1]; |
703 | } |
704 | |