Age Owner Branch data 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-2025, 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/datum.h"
34 : : #include "utils/fmgrprotos.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, bool missing_ok);
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
3767 alvherre@alvh.no-ip. 94 :CBC 3070 : brin_inclusion_opcinfo(PG_FUNCTION_ARGS)
95 : : {
96 : 3070 : Oid typoid = PG_GETARG_OID(0);
97 : : BrinOpcInfo *result;
98 : 3070 : 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 : 3070 : result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque));
110 : 3070 : result->oi_nstored = 3;
1628 tomas.vondra@postgre 111 : 3070 : result->oi_regular_nulls = true;
3767 alvherre@alvh.no-ip. 112 : 3070 : result->oi_opaque = (InclusionOpaque *)
113 : 3070 : MAXALIGN((char *) result + SizeofBrinOpcInfo(3));
114 : :
115 : : /* the union */
116 : 3070 : result->oi_typcache[INCLUSION_UNION] =
117 : 3070 : lookup_type_cache(typoid, 0);
118 : :
119 : : /* includes elements that are not mergeable */
120 : 3070 : result->oi_typcache[INCLUSION_UNMERGEABLE] = bool_typcache;
121 : :
122 : : /* includes the empty element */
123 : 3070 : result->oi_typcache[INCLUSION_CONTAINS_EMPTY] = bool_typcache;
124 : :
125 : 3070 : 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);
1628 tomas.vondra@postgre 143 : 4113 : bool isnull PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_BOOL(3);
3767 alvherre@alvh.no-ip. 144 : 4113 : Oid colloid = PG_GET_COLLATION();
145 : : FmgrInfo *finfo;
146 : : Datum result;
147 : 4113 : bool new = false;
148 : : AttrNumber attno;
149 : : CompactAttribute *attr;
150 : :
1628 tomas.vondra@postgre 151 [ - + ]: 4113 : Assert(!isnull);
152 : :
3767 alvherre@alvh.no-ip. 153 : 4113 : attno = column->bv_attno;
260 drowley@postgresql.o 154 : 4113 : attr = TupleDescCompactAttr(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 : : */
3767 alvherre@alvh.no-ip. 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 : : */
179 182 : 3903 : finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_EMPTY, true);
3767 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. */
179 198 : 2214 : finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_CONTAINS, true);
3767 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 : : */
179 213 : 72 : finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE, true);
3767 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. */
179 224 : 18 : finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE, false);
3767 225 : 18 : result = FunctionCall2Coll(finfo, colloid,
226 : 18 : column->bv_values[INCLUSION_UNION], newval);
2056 heikki.linnakangas@i 227 [ + - + - ]: 36 : if (!attr->attbyval &&
228 : 18 : DatumGetPointer(result) != DatumGetPointer(column->bv_values[INCLUSION_UNION]))
229 : : {
3767 alvherre@alvh.no-ip. 230 : 18 : pfree(DatumGetPointer(column->bv_values[INCLUSION_UNION]));
231 : :
2056 heikki.linnakangas@i 232 [ + + ]: 18 : if (result == newval)
233 : 3 : result = datumCopy(result, attr->attbyval, attr->attlen);
234 : : }
3767 alvherre@alvh.no-ip. 235 : 18 : column->bv_values[INCLUSION_UNION] = result;
236 : :
237 : 18 : PG_RETURN_BOOL(true);
238 : : }
239 : :
240 : : /*
241 : : * BRIN inclusion consistent function
242 : : *
243 : : * We're no longer dealing with NULL keys in the consistent function, that is
244 : : * now handled by the AM code. That means we should not get any all-NULL ranges
245 : : * either, because those can't be consistent with regular (not [IS] NULL) keys.
246 : : *
247 : : * All of the strategies are optional.
248 : : */
249 : : Datum
250 : 21300 : brin_inclusion_consistent(PG_FUNCTION_ARGS)
251 : : {
252 : 21300 : BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
253 : 21300 : BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
1625 tomas.vondra@postgre 254 : 21300 : ScanKey key = (ScanKey) PG_GETARG_POINTER(2);
255 : 21300 : Oid colloid = PG_GET_COLLATION(),
256 : : subtype;
257 : : Datum unionval;
258 : : AttrNumber attno;
259 : : Datum query;
260 : : FmgrInfo *finfo;
261 : : Datum result;
262 : :
263 : : /* This opclass uses the old signature with only three arguments. */
264 [ - + ]: 21300 : Assert(PG_NARGS() == 3);
265 : :
266 : : /* Should not be dealing with all-NULL ranges. */
267 [ - + ]: 21300 : Assert(!column->bv_allnulls);
268 : :
269 : : /* It has to be checked, if it contains elements that are not mergeable. */
3767 alvherre@alvh.no-ip. 270 [ + + ]: 21300 : if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE]))
271 : 819 : PG_RETURN_BOOL(true);
272 : :
1625 tomas.vondra@postgre 273 : 20481 : attno = key->sk_attno;
274 : 20481 : subtype = key->sk_subtype;
275 : 20481 : query = key->sk_argument;
276 : 20481 : unionval = column->bv_values[INCLUSION_UNION];
3767 alvherre@alvh.no-ip. 277 [ + + + + : 20481 : switch (key->sk_strategy)
+ + + + +
+ - + + +
+ - ]
278 : : {
279 : : /*
280 : : * Placement strategies
281 : : *
282 : : * These are implemented by logically negating the result of the
283 : : * converse placement operator; for this to work, the converse
284 : : * operator must be part of the opclass. An error will be thrown
285 : : * by inclusion_get_strategy_procinfo() if the required strategy
286 : : * is not part of the opclass.
287 : : *
288 : : * These all return false if either argument is empty, so there is
289 : : * no need to check for empty elements.
290 : : */
291 : :
292 : 600 : case RTLeftStrategyNumber:
293 : 600 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
294 : : RTOverRightStrategyNumber);
295 : 600 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
1625 tomas.vondra@postgre 296 : 600 : PG_RETURN_BOOL(!DatumGetBool(result));
297 : :
3767 alvherre@alvh.no-ip. 298 : 600 : case RTOverLeftStrategyNumber:
299 : 600 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
300 : : RTRightStrategyNumber);
301 : 600 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
1625 tomas.vondra@postgre 302 : 600 : PG_RETURN_BOOL(!DatumGetBool(result));
303 : :
3767 alvherre@alvh.no-ip. 304 : 600 : case RTOverRightStrategyNumber:
305 : 600 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
306 : : RTLeftStrategyNumber);
307 : 600 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
1625 tomas.vondra@postgre 308 : 600 : PG_RETURN_BOOL(!DatumGetBool(result));
309 : :
3767 alvherre@alvh.no-ip. 310 : 600 : case RTRightStrategyNumber:
311 : 600 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
312 : : RTOverLeftStrategyNumber);
313 : 600 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
1625 tomas.vondra@postgre 314 : 600 : PG_RETURN_BOOL(!DatumGetBool(result));
315 : :
3767 alvherre@alvh.no-ip. 316 : 300 : case RTBelowStrategyNumber:
317 : 300 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
318 : : RTOverAboveStrategyNumber);
319 : 300 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
1625 tomas.vondra@postgre 320 : 300 : PG_RETURN_BOOL(!DatumGetBool(result));
321 : :
3767 alvherre@alvh.no-ip. 322 : 300 : case RTOverBelowStrategyNumber:
323 : 300 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
324 : : RTAboveStrategyNumber);
325 : 300 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
1625 tomas.vondra@postgre 326 : 300 : PG_RETURN_BOOL(!DatumGetBool(result));
327 : :
3767 alvherre@alvh.no-ip. 328 : 300 : case RTOverAboveStrategyNumber:
329 : 300 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
330 : : RTBelowStrategyNumber);
331 : 300 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
1625 tomas.vondra@postgre 332 : 300 : PG_RETURN_BOOL(!DatumGetBool(result));
333 : :
3767 alvherre@alvh.no-ip. 334 : 300 : case RTAboveStrategyNumber:
335 : 300 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
336 : : RTOverBelowStrategyNumber);
337 : 300 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
1625 tomas.vondra@postgre 338 : 300 : PG_RETURN_BOOL(!DatumGetBool(result));
339 : :
340 : : /*
341 : : * Overlap and contains strategies
342 : : *
343 : : * These strategies are simple enough that we can simply call the
344 : : * operator and return its result. Empty elements don't change
345 : : * the result.
346 : : */
347 : :
3767 alvherre@alvh.no-ip. 348 : 7680 : case RTOverlapStrategyNumber:
349 : : case RTContainsStrategyNumber:
350 : : case RTContainsElemStrategyNumber:
351 : : case RTSubStrategyNumber:
352 : : case RTSubEqualStrategyNumber:
353 : 7680 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
354 : 7680 : key->sk_strategy);
355 : 7680 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
1625 tomas.vondra@postgre 356 : 7680 : PG_RETURN_DATUM(result);
357 : :
358 : : /*
359 : : * Contained by strategies
360 : : *
361 : : * We cannot just call the original operator for the contained by
362 : : * strategies because some elements can be contained even though
363 : : * the union is not; instead we use the overlap operator.
364 : : *
365 : : * We check for empty elements separately as they are not merged
366 : : * to the union but contained by everything.
367 : : */
368 : :
3767 alvherre@alvh.no-ip. 369 : 4248 : case RTContainedByStrategyNumber:
370 : : case RTSuperStrategyNumber:
371 : : case RTSuperEqualStrategyNumber:
372 : 4248 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
373 : : RTOverlapStrategyNumber);
374 : 4248 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
375 [ + + ]: 4248 : if (DatumGetBool(result))
1625 tomas.vondra@postgre 376 : 2532 : PG_RETURN_BOOL(true);
377 : :
378 : 1716 : PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
379 : :
380 : : /*
381 : : * Adjacent strategy
382 : : *
383 : : * We test for overlap first but to be safe we need to call the
384 : : * actual adjacent operator also.
385 : : *
386 : : * An empty element cannot be adjacent to any other, so there is
387 : : * no need to check for it.
388 : : */
389 : :
3767 alvherre@alvh.no-ip. 390 :UBC 0 : case RTAdjacentStrategyNumber:
391 : 0 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
392 : : RTOverlapStrategyNumber);
393 : 0 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
394 [ # # ]: 0 : if (DatumGetBool(result))
1625 tomas.vondra@postgre 395 : 0 : PG_RETURN_BOOL(true);
396 : :
3767 alvherre@alvh.no-ip. 397 : 0 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
398 : : RTAdjacentStrategyNumber);
399 : 0 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
1625 tomas.vondra@postgre 400 : 0 : PG_RETURN_DATUM(result);
401 : :
402 : : /*
403 : : * Basic comparison strategies
404 : : *
405 : : * It is straightforward to support the equality strategies with
406 : : * the contains operator. Generally, inequality strategies do not
407 : : * make much sense for the types which will be used with the
408 : : * inclusion BRIN family of opclasses, but it is possible to
409 : : * implement them with logical negation of the left-of and
410 : : * right-of operators.
411 : : *
412 : : * NB: These strategies cannot be used with geometric datatypes
413 : : * that use comparison of areas! The only exception is the "same"
414 : : * strategy.
415 : : *
416 : : * Empty elements are considered to be less than the others. We
417 : : * cannot use the empty support function to check the query is an
418 : : * empty element, because the query can be another data type than
419 : : * the empty support function argument. So we will return true,
420 : : * if there is a possibility that empty elements will change the
421 : : * result.
422 : : */
423 : :
3767 alvherre@alvh.no-ip. 424 :CBC 900 : case RTLessStrategyNumber:
425 : : case RTLessEqualStrategyNumber:
426 : 900 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
427 : : RTRightStrategyNumber);
428 : 900 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
429 [ + + ]: 900 : if (!DatumGetBool(result))
1625 tomas.vondra@postgre 430 : 750 : PG_RETURN_BOOL(true);
431 : :
432 : 150 : PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
433 : :
3767 alvherre@alvh.no-ip. 434 : 2853 : case RTSameStrategyNumber:
435 : : case RTEqualStrategyNumber:
436 : 2853 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
437 : : RTContainsStrategyNumber);
438 : 2853 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
439 [ + + ]: 2853 : if (DatumGetBool(result))
1625 tomas.vondra@postgre 440 : 351 : PG_RETURN_BOOL(true);
441 : :
442 : 2502 : PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
443 : :
3767 alvherre@alvh.no-ip. 444 : 600 : case RTGreaterEqualStrategyNumber:
445 : 600 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
446 : : RTLeftStrategyNumber);
447 : 600 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
448 [ + - ]: 600 : if (!DatumGetBool(result))
1625 tomas.vondra@postgre 449 : 600 : PG_RETURN_BOOL(true);
450 : :
1625 tomas.vondra@postgre 451 :UBC 0 : PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]);
452 : :
3767 alvherre@alvh.no-ip. 453 :CBC 600 : case RTGreaterStrategyNumber:
454 : : /* no need to check for empty elements */
455 : 600 : finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype,
456 : : RTLeftStrategyNumber);
457 : 600 : result = FunctionCall2Coll(finfo, colloid, unionval, query);
1625 tomas.vondra@postgre 458 : 600 : PG_RETURN_BOOL(!DatumGetBool(result));
459 : :
3767 alvherre@alvh.no-ip. 460 :UBC 0 : default:
461 : : /* shouldn't happen */
462 [ # # ]: 0 : elog(ERROR, "invalid strategy number %d", key->sk_strategy);
463 : : PG_RETURN_BOOL(false);
464 : : }
465 : : }
466 : :
467 : : /*
468 : : * BRIN inclusion union function
469 : : *
470 : : * Given two BrinValues, update the first of them as a union of the summary
471 : : * values contained in both. The second one is untouched.
472 : : */
473 : : Datum
474 : 0 : brin_inclusion_union(PG_FUNCTION_ARGS)
475 : : {
476 : 0 : BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
477 : 0 : BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1);
478 : 0 : BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
479 : 0 : Oid colloid = PG_GET_COLLATION();
480 : : AttrNumber attno;
481 : : CompactAttribute *attr;
482 : : FmgrInfo *finfo;
483 : : Datum result;
484 : :
485 [ # # ]: 0 : Assert(col_a->bv_attno == col_b->bv_attno);
1628 tomas.vondra@postgre 486 [ # # # # ]: 0 : Assert(!col_a->bv_allnulls && !col_b->bv_allnulls);
487 : :
3767 alvherre@alvh.no-ip. 488 : 0 : attno = col_a->bv_attno;
260 drowley@postgresql.o 489 : 0 : attr = TupleDescCompactAttr(bdesc->bd_tupdesc, attno - 1);
490 : :
491 : : /* If B includes empty elements, mark A similarly, if needed. */
3767 alvherre@alvh.no-ip. 492 [ # # # # ]: 0 : if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) &&
493 : 0 : DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY]))
494 : 0 : col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true);
495 : :
496 : : /* Check if A includes elements that are not mergeable. */
497 [ # # ]: 0 : if (DatumGetBool(col_a->bv_values[INCLUSION_UNMERGEABLE]))
498 : 0 : PG_RETURN_VOID();
499 : :
500 : : /* If B includes elements that are not mergeable, mark A similarly. */
501 [ # # ]: 0 : if (DatumGetBool(col_b->bv_values[INCLUSION_UNMERGEABLE]))
502 : : {
503 : 0 : col_a->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true);
504 : 0 : PG_RETURN_VOID();
505 : : }
506 : :
507 : : /* Check if A and B are mergeable; if not, mark A unmergeable. */
179 508 : 0 : finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE, true);
3767 509 [ # # ]: 0 : if (finfo != NULL &&
510 [ # # ]: 0 : !DatumGetBool(FunctionCall2Coll(finfo, colloid,
511 : 0 : col_a->bv_values[INCLUSION_UNION],
512 : 0 : col_b->bv_values[INCLUSION_UNION])))
513 : : {
514 : 0 : col_a->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true);
515 : 0 : PG_RETURN_VOID();
516 : : }
517 : :
518 : : /* Finally, merge B to A. */
179 519 : 0 : finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE, false);
3767 520 : 0 : result = FunctionCall2Coll(finfo, colloid,
521 : 0 : col_a->bv_values[INCLUSION_UNION],
522 : 0 : col_b->bv_values[INCLUSION_UNION]);
2056 heikki.linnakangas@i 523 [ # # # # ]: 0 : if (!attr->attbyval &&
524 : 0 : DatumGetPointer(result) != DatumGetPointer(col_a->bv_values[INCLUSION_UNION]))
525 : : {
3767 alvherre@alvh.no-ip. 526 : 0 : pfree(DatumGetPointer(col_a->bv_values[INCLUSION_UNION]));
527 : :
2056 heikki.linnakangas@i 528 [ # # ]: 0 : if (result == col_b->bv_values[INCLUSION_UNION])
529 : 0 : result = datumCopy(result, attr->attbyval, attr->attlen);
530 : : }
3767 alvherre@alvh.no-ip. 531 : 0 : col_a->bv_values[INCLUSION_UNION] = result;
532 : :
533 : 0 : PG_RETURN_VOID();
534 : : }
535 : :
536 : : /*
537 : : * Cache and return inclusion opclass support procedure
538 : : *
539 : : * Return the procedure corresponding to the given function support number
540 : : * or null if it is not exists. If missing_ok is true and the procedure
541 : : * isn't set up for this opclass, return NULL instead of raising an error.
542 : : */
543 : : static FmgrInfo *
179 alvherre@alvh.no-ip. 544 :CBC 6207 : inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum,
545 : : bool missing_ok)
546 : : {
547 : : InclusionOpaque *opaque;
3767 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 : 105 : fmgr_info_copy(&opaque->extra_procinfos[basenum],
568 : : index_getprocinfo(bdesc->bd_index, attno, procnum),
569 : : bdesc->bd_context);
570 : : else
571 : : {
179 572 [ - + ]: 51 : if (!missing_ok)
179 alvherre@alvh.no-ip. 573 [ # # ]:UBC 0 : ereport(ERROR,
574 : : errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
575 : : errmsg_internal("invalid opclass definition"),
576 : : errdetail_internal("The operator class is missing support function %d for column %d.",
577 : : procnum, attno));
578 : :
3767 alvherre@alvh.no-ip. 579 :CBC 51 : opaque->extra_proc_missing[basenum] = true;
580 : 51 : return NULL;
581 : : }
582 : : }
583 : :
584 : 3360 : return &opaque->extra_procinfos[basenum];
585 : : }
586 : :
587 : : /*
588 : : * Cache and return the procedure of the given strategy
589 : : *
590 : : * Return the procedure corresponding to the given sub-type and strategy
591 : : * number. The data type of the index will be used as the left hand side of
592 : : * the operator and the given sub-type will be used as the right hand side.
593 : : * Throws an error if the pg_amop row does not exist, but that should not
594 : : * happen with a properly configured opclass.
595 : : *
596 : : * It always throws an error when the data type of the opclass is different
597 : : * from the data type of the column or the expression. That happens when the
598 : : * column data type has implicit cast to the opclass data type. We don't
599 : : * bother casting types, because this situation can easily be avoided by
600 : : * setting storage data type to that of the opclass. The same problem does not
601 : : * apply to the data type of the right hand side, because the type in the
602 : : * ScanKey always matches the opclass' one.
603 : : *
604 : : * Note: this function mirrors minmax_get_strategy_procinfo; if changes are
605 : : * made here, see that function too.
606 : : */
607 : : static FmgrInfo *
608 : 20481 : inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype,
609 : : uint16 strategynum)
610 : : {
611 : : InclusionOpaque *opaque;
612 : :
613 [ + - - + ]: 20481 : Assert(strategynum >= 1 &&
614 : : strategynum <= RTMaxStrategyNumber);
615 : :
616 : 20481 : opaque = (InclusionOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
617 : :
618 : : /*
619 : : * We cache the procedures for the last sub-type in the opaque struct, to
620 : : * avoid repetitive syscache lookups. If the sub-type is changed,
621 : : * invalidate all the cached entries.
622 : : */
623 [ + + ]: 20481 : if (opaque->cached_subtype != subtype)
624 : : {
625 : : uint16 i;
626 : :
627 [ + + ]: 6603 : for (i = 1; i <= RTMaxStrategyNumber; i++)
628 : 6390 : opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid;
629 : 213 : opaque->cached_subtype = subtype;
630 : : }
631 : :
632 [ + + ]: 20481 : if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid)
633 : : {
634 : : Form_pg_attribute attr;
635 : : HeapTuple tuple;
636 : : Oid opfamily,
637 : : oprid;
638 : :
639 : 213 : opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
2939 andres@anarazel.de 640 : 213 : attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
3767 alvherre@alvh.no-ip. 641 : 213 : tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
642 : : ObjectIdGetDatum(attr->atttypid),
643 : : ObjectIdGetDatum(subtype),
644 : : Int16GetDatum(strategynum));
645 : :
646 [ - + ]: 213 : if (!HeapTupleIsValid(tuple))
3767 alvherre@alvh.no-ip. 647 [ # # ]:UBC 0 : elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
648 : : strategynum, attr->atttypid, subtype, opfamily);
649 : :
896 dgustafsson@postgres 650 :CBC 213 : oprid = DatumGetObjectId(SysCacheGetAttrNotNull(AMOPSTRATEGY, tuple,
651 : : Anum_pg_amop_amopopr));
3767 alvherre@alvh.no-ip. 652 : 213 : ReleaseSysCache(tuple);
896 dgustafsson@postgres 653 [ - + ]: 213 : Assert(RegProcedureIsValid(oprid));
654 : :
3767 alvherre@alvh.no-ip. 655 : 213 : fmgr_info_cxt(get_opcode(oprid),
656 : 213 : &opaque->strategy_procinfos[strategynum - 1],
657 : : bdesc->bd_context);
658 : : }
659 : :
660 : 20481 : return &opaque->strategy_procinfos[strategynum - 1];
661 : : }
|