Age Owner TLA 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-2023, 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
2886 alvherre 94 CBC 3058 : brin_inclusion_opcinfo(PG_FUNCTION_ARGS)
95 : {
96 3058 : Oid typoid = PG_GETARG_OID(0);
97 : BrinOpcInfo *result;
98 3058 : 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 3058 : result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque));
110 3058 : result->oi_nstored = 3;
747 tomas.vondra 111 3058 : result->oi_regular_nulls = true;
2886 alvherre 112 3058 : result->oi_opaque = (InclusionOpaque *)
113 3058 : MAXALIGN((char *) result + SizeofBrinOpcInfo(3));
114 :
115 : /* the union */
116 3058 : result->oi_typcache[INCLUSION_UNION] =
117 3058 : lookup_type_cache(typoid, 0);
118 :
119 : /* includes elements that are not mergeable */
120 3058 : result->oi_typcache[INCLUSION_UNMERGEABLE] = bool_typcache;
121 :
122 : /* includes the empty element */
123 3058 : result->oi_typcache[INCLUSION_CONTAINS_EMPTY] = bool_typcache;
124 :
125 3058 : 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
138 4113 : brin_inclusion_add_value(PG_FUNCTION_ARGS)
139 : {
140 4113 : BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
141 4113 : BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
142 4113 : Datum newval = PG_GETARG_DATUM(2);
747 tomas.vondra 143 4113 : bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3);
2886 alvherre 144 4113 : Oid colloid = PG_GET_COLLATION();
145 : FmgrInfo *finfo;
146 : Datum result;
147 4113 : bool new = false;
148 : AttrNumber attno;
149 : Form_pg_attribute attr;
150 :
747 tomas.vondra 151 4113 : Assert(!isnull);
152 :
2886 alvherre 153 4113 : attno = column->bv_attno;
2058 andres 154 4113 : 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 : */
2886 alvherre 160 4113 : if (column->bv_allnulls)
161 : {
162 2580 : column->bv_values[INCLUSION_UNION] =
163 1290 : datumCopy(newval, attr->attbyval, attr->attlen);
164 1290 : column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(false);
165 1290 : column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(false);
166 1290 : column->bv_allnulls = false;
167 1290 : new = true;
168 : }
169 :
170 : /*
171 : * No need for further processing if the block range is marked as
172 : * containing unmergeable values.
173 : */
174 4113 : if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE]))
175 210 : 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 3903 : finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_EMPTY);
183 3903 : if (finfo != NULL && DatumGetBool(FunctionCall1Coll(finfo, colloid, newval)))
184 : {
185 558 : if (!DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY]))
186 : {
187 174 : column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true);
188 174 : PG_RETURN_BOOL(true);
189 : }
190 :
191 384 : PG_RETURN_BOOL(false);
192 : }
193 :
194 3345 : if (new)
195 1131 : PG_RETURN_BOOL(true);
196 :
197 : /* Check if the new value is already contained. */
198 2214 : finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_CONTAINS);
199 4428 : if (finfo != NULL &&
200 2214 : DatumGetBool(FunctionCall2Coll(finfo, colloid,
201 2214 : column->bv_values[INCLUSION_UNION],
202 : newval)))
203 2142 : 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 72 : finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE);
214 72 : if (finfo != NULL &&
215 66 : !DatumGetBool(FunctionCall2Coll(finfo, colloid,
216 66 : column->bv_values[INCLUSION_UNION],
217 : newval)))
218 : {
219 54 : column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true);
220 54 : PG_RETURN_BOOL(true);
221 : }
222 :
223 : /* Finally, merge the new value to the existing union. */
224 18 : finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE);
225 18 : Assert(finfo != NULL);
226 18 : result = FunctionCall2Coll(finfo, colloid,
227 18 : column->bv_values[INCLUSION_UNION], newval);
1175 heikki.linnakangas 228 36 : if (!attr->attbyval &&
229 18 : DatumGetPointer(result) != DatumGetPointer(column->bv_values[INCLUSION_UNION]))
230 : {
2886 alvherre 231 18 : pfree(DatumGetPointer(column->bv_values[INCLUSION_UNION]));
232 :
1175 heikki.linnakangas 233 18 : if (result == newval)
234 3 : result = datumCopy(result, attr->attbyval, attr->attlen);
235 : }
2886 alvherre 236 18 : column->bv_values[INCLUSION_UNION] = result;
237 :
238 18 : 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
251 21300 : brin_inclusion_consistent(PG_FUNCTION_ARGS)
252 : {
253 21300 : BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
254 21300 : BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
744 tomas.vondra 255 21300 : ScanKey key = (ScanKey) PG_GETARG_POINTER(2);
256 21300 : 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 21300 : Assert(PG_NARGS() == 3);
266 :
267 : /* Should not be dealing with all-NULL ranges. */
268 21300 : Assert(!column->bv_allnulls);
269 :
270 : /* It has to be checked, if it contains elements that are not mergeable. */
2886 alvherre 271 21300 : if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE]))
272 819 : PG_RETURN_BOOL(true);
273 :
744 tomas.vondra 274 20481 : attno = key->sk_attno;
275 20481 : subtype = key->sk_subtype;
276 20481 : query = key->sk_argument;
277 20481 : unionval = column->bv_values[INCLUSION_UNION];
2886 alvherre 278 20481 : 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 600 : case RTLeftStrategyNumber:
294 600 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
295 : RTOverRightStrategyNumber);
296 600 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
744 tomas.vondra 297 600 : PG_RETURN_BOOL(!DatumGetBool(result));
298 :
2886 alvherre 299 600 : case RTOverLeftStrategyNumber:
300 600 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
301 : RTRightStrategyNumber);
302 600 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
744 tomas.vondra 303 600 : PG_RETURN_BOOL(!DatumGetBool(result));
304 :
2886 alvherre 305 600 : case RTOverRightStrategyNumber:
306 600 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
307 : RTLeftStrategyNumber);
308 600 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
744 tomas.vondra 309 600 : PG_RETURN_BOOL(!DatumGetBool(result));
310 :
2886 alvherre 311 600 : case RTRightStrategyNumber:
312 600 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
313 : RTOverLeftStrategyNumber);
314 600 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
744 tomas.vondra 315 600 : PG_RETURN_BOOL(!DatumGetBool(result));
316 :
2886 alvherre 317 300 : case RTBelowStrategyNumber:
318 300 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
319 : RTOverAboveStrategyNumber);
320 300 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
744 tomas.vondra 321 300 : PG_RETURN_BOOL(!DatumGetBool(result));
322 :
2886 alvherre 323 300 : case RTOverBelowStrategyNumber:
324 300 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
325 : RTAboveStrategyNumber);
326 300 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
744 tomas.vondra 327 300 : PG_RETURN_BOOL(!DatumGetBool(result));
328 :
2886 alvherre 329 300 : case RTOverAboveStrategyNumber:
330 300 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
331 : RTBelowStrategyNumber);
332 300 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
744 tomas.vondra 333 300 : PG_RETURN_BOOL(!DatumGetBool(result));
334 :
2886 alvherre 335 300 : case RTAboveStrategyNumber:
336 300 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
337 : RTOverBelowStrategyNumber);
338 300 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
744 tomas.vondra 339 300 : 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 :
2886 alvherre 349 7680 : case RTOverlapStrategyNumber:
350 : case RTContainsStrategyNumber:
351 : case RTContainsElemStrategyNumber:
352 : case RTSubStrategyNumber:
353 : case RTSubEqualStrategyNumber:
354 7680 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
355 7680 : key->sk_strategy);
356 7680 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
744 tomas.vondra 357 7680 : 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 :
2886 alvherre 370 4248 : case RTContainedByStrategyNumber:
371 : case RTSuperStrategyNumber:
372 : case RTSuperEqualStrategyNumber:
373 4248 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
374 : RTOverlapStrategyNumber);
375 4248 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
376 4248 : if (DatumGetBool(result))
744 tomas.vondra 377 2532 : PG_RETURN_BOOL(true);
378 :
379 1716 : 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 :
2886 alvherre 391 UBC 0 : case RTAdjacentStrategyNumber:
392 0 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
393 : RTOverlapStrategyNumber);
394 0 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
395 0 : if (DatumGetBool(result))
744 tomas.vondra 396 0 : PG_RETURN_BOOL(true);
397 :
2886 alvherre 398 0 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
399 : RTAdjacentStrategyNumber);
400 0 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
744 tomas.vondra 401 0 : 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 :
2886 alvherre 425 CBC 900 : case RTLessStrategyNumber:
426 : case RTLessEqualStrategyNumber:
427 900 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
428 : RTRightStrategyNumber);
429 900 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
430 900 : if (!DatumGetBool(result))
744 tomas.vondra 431 750 : PG_RETURN_BOOL(true);
432 :
433 150 : PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
434 :
2886 alvherre 435 2853 : case RTSameStrategyNumber:
436 : case RTEqualStrategyNumber:
437 2853 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
438 : RTContainsStrategyNumber);
439 2853 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
440 2853 : if (DatumGetBool(result))
744 tomas.vondra 441 351 : PG_RETURN_BOOL(true);
442 :
443 2502 : PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
444 :
2886 alvherre 445 600 : case RTGreaterEqualStrategyNumber:
446 600 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
447 : RTLeftStrategyNumber);
448 600 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
449 600 : if (!DatumGetBool(result))
744 tomas.vondra 450 600 : PG_RETURN_BOOL(true);
451 :
744 tomas.vondra 452 UBC 0 : PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
453 :
2886 alvherre 454 CBC 600 : case RTGreaterStrategyNumber:
455 : /* no need to check for empty elements */
456 600 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
457 : RTLeftStrategyNumber);
458 600 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
744 tomas.vondra 459 600 : PG_RETURN_BOOL(!DatumGetBool(result));
460 :
2886 alvherre 461 UBC 0 : default:
462 : /* shouldn't happen */
463 0 : 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
475 0 : brin_inclusion_union(PG_FUNCTION_ARGS)
476 : {
477 0 : BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
478 0 : BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1);
479 0 : BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
480 0 : Oid colloid = PG_GET_COLLATION();
481 : AttrNumber attno;
482 : Form_pg_attribute attr;
483 : FmgrInfo *finfo;
484 : Datum result;
485 :
486 0 : Assert(col_a->bv_attno == col_b->bv_attno);
747 tomas.vondra 487 0 : Assert(!col_a->bv_allnulls && !col_b->bv_allnulls);
488 :
2886 alvherre 489 0 : attno = col_a->bv_attno;
2058 andres 490 0 : attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
491 :
492 : /* If B includes empty elements, mark A similarly, if needed. */
2886 alvherre 493 0 : if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) &&
494 0 : DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY]))
495 0 : col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true);
496 :
497 : /* Check if A includes elements that are not mergeable. */
498 0 : if (DatumGetBool(col_a->bv_values[INCLUSION_UNMERGEABLE]))
499 0 : PG_RETURN_VOID();
500 :
501 : /* If B includes elements that are not mergeable, mark A similarly. */
502 0 : if (DatumGetBool(col_b->bv_values[INCLUSION_UNMERGEABLE]))
503 : {
504 0 : col_a->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true);
505 0 : PG_RETURN_VOID();
506 : }
507 :
508 : /* Check if A and B are mergeable; if not, mark A unmergeable. */
509 0 : finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE);
510 0 : if (finfo != NULL &&
511 0 : !DatumGetBool(FunctionCall2Coll(finfo, colloid,
512 0 : col_a->bv_values[INCLUSION_UNION],
513 0 : col_b->bv_values[INCLUSION_UNION])))
514 : {
515 0 : col_a->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true);
516 0 : PG_RETURN_VOID();
517 : }
518 :
519 : /* Finally, merge B to A. */
520 0 : finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE);
521 0 : Assert(finfo != NULL);
522 0 : result = FunctionCall2Coll(finfo, colloid,
523 0 : col_a->bv_values[INCLUSION_UNION],
524 0 : col_b->bv_values[INCLUSION_UNION]);
1175 heikki.linnakangas 525 0 : if (!attr->attbyval &&
526 0 : DatumGetPointer(result) != DatumGetPointer(col_a->bv_values[INCLUSION_UNION]))
527 : {
2886 alvherre 528 0 : pfree(DatumGetPointer(col_a->bv_values[INCLUSION_UNION]));
529 :
1175 heikki.linnakangas 530 0 : if (result == col_b->bv_values[INCLUSION_UNION])
531 0 : result = datumCopy(result, attr->attbyval, attr->attlen);
532 : }
2886 alvherre 533 0 : col_a->bv_values[INCLUSION_UNION] = result;
534 :
535 0 : 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 *
2886 alvherre 545 CBC 6207 : inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum)
546 : {
547 : InclusionOpaque *opaque;
548 6207 : uint16 basenum = procnum - PROCNUM_BASE;
549 :
550 : /*
551 : * We cache these in the opaque struct, to avoid repetitive syscache
552 : * lookups.
553 : */
554 6207 : 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 6207 : if (opaque->extra_proc_missing[basenum])
561 2796 : return NULL;
562 :
563 3411 : if (opaque->extra_procinfos[basenum].fn_oid == InvalidOid)
564 : {
565 156 : if (RegProcedureIsValid(index_getprocid(bdesc->bd_index, attno,
566 : procnum)))
567 : {
568 105 : fmgr_info_copy(&opaque->extra_procinfos[basenum],
569 : index_getprocinfo(bdesc->bd_index, attno, procnum),
570 : bdesc->bd_context);
571 : }
572 : else
573 : {
574 51 : opaque->extra_proc_missing[basenum] = true;
575 51 : return NULL;
576 : }
577 : }
578 :
579 3360 : 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 *
603 20481 : inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype,
604 : uint16 strategynum)
605 : {
606 : InclusionOpaque *opaque;
607 :
608 20481 : Assert(strategynum >= 1 &&
609 : strategynum <= RTMaxStrategyNumber);
610 :
611 20481 : 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 20481 : if (opaque->cached_subtype != subtype)
619 : {
620 : uint16 i;
621 :
622 6603 : for (i = 1; i <= RTMaxStrategyNumber; i++)
623 6390 : opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid;
624 213 : opaque->cached_subtype = subtype;
625 : }
626 :
627 20481 : if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid)
628 : {
629 : Form_pg_attribute attr;
630 : HeapTuple tuple;
631 : Oid opfamily,
632 : oprid;
2886 alvherre 633 ECB :
2886 alvherre 634 CBC 213 : opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
2058 andres 635 213 : attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
2886 alvherre 636 GIC 213 : tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
637 : ObjectIdGetDatum(attr->atttypid),
638 : ObjectIdGetDatum(subtype),
639 : Int16GetDatum(strategynum));
2886 alvherre 640 ECB :
2886 alvherre 641 GBC 213 : if (!HeapTupleIsValid(tuple))
2886 alvherre 642 UIC 0 : elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
643 : strategynum, attr->atttypid, subtype, opfamily);
2886 alvherre 644 ECB :
15 dgustafsson 645 GNC 213 : oprid = DatumGetObjectId(SysCacheGetAttrNotNull(AMOPSTRATEGY, tuple,
646 : Anum_pg_amop_amopopr));
2886 alvherre 647 CBC 213 : ReleaseSysCache(tuple);
15 dgustafsson 648 GNC 213 : Assert(RegProcedureIsValid(oprid));
2886 alvherre 649 ECB :
2886 alvherre 650 CBC 213 : fmgr_info_cxt(get_opcode(oprid),
2886 alvherre 651 GIC 213 : &opaque->strategy_procinfos[strategynum - 1],
652 : bdesc->bd_context);
653 : }
2886 alvherre 654 ECB :
2886 alvherre 655 GIC 20481 : return &opaque->strategy_procinfos[strategynum - 1];
656 : }
|