mirror of https://github.com/postgres/postgres
This lets BRIN be used with R-Tree-like indexing strategies. Also provided are operator classes for range types, box and inet/cidr. The infrastructure provided here should be sufficient to create operator classes for similar datatypes; for instance, opclasses for PostGIS geometries should be doable, though we didn't try to implement one. (A box/point opclass was also submitted, but we ripped it out before commit because the handling of floating point comparisons in existing code is inconsistent and would generate corrupt indexes.) Author: Emre Hasegeli. Cosmetic changes by me Review: Andreas Karlssonpull/14/head
parent
199f5973c5
commit
b0b7be6133
@ -0,0 +1,696 @@ |
||||
/*
|
||||
* brin_inclusion.c |
||||
* Implementation of inclusion opclasses for BRIN |
||||
* |
||||
* This module provides framework BRIN support functions for the "inclusion" |
||||
* operator classes. A few SQL-level support functions are also required for |
||||
* each opclass. |
||||
* |
||||
* The "inclusion" BRIN strategy is useful for types that support R-Tree |
||||
* operations. This implementation is a straight mapping of those operations |
||||
* to the block-range nature of BRIN, with two exceptions: (a) we explicitly |
||||
* support "empty" elements: at least with range types, we need to consider |
||||
* emptiness separately from regular R-Tree strategies; and (b) we need to |
||||
* consider "unmergeable" elements, that is, a set of elements for whose union |
||||
* no representation exists. The only case where that happens as of this |
||||
* writing is the INET type, where IPv6 values cannot be merged with IPv4 |
||||
* values. |
||||
* |
||||
* Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group |
||||
* Portions Copyright (c) 1994, Regents of the University of California |
||||
* |
||||
* IDENTIFICATION |
||||
* src/backend/access/brin/brin_inclusion.c |
||||
*/ |
||||
#include "postgres.h" |
||||
|
||||
#include "access/brin_internal.h" |
||||
#include "access/brin_tuple.h" |
||||
#include "access/genam.h" |
||||
#include "access/skey.h" |
||||
#include "catalog/pg_amop.h" |
||||
#include "catalog/pg_type.h" |
||||
#include "utils/datum.h" |
||||
#include "utils/lsyscache.h" |
||||
#include "utils/rel.h" |
||||
#include "utils/syscache.h" |
||||
|
||||
|
||||
/*
|
||||
* Additional SQL level support functions |
||||
* |
||||
* Procedure numbers must not use values reserved for BRIN itself; see |
||||
* brin_internal.h. |
||||
*/ |
||||
#define INCLUSION_MAX_PROCNUMS 4 /* maximum support procs we need */ |
||||
#define PROCNUM_MERGE 11 /* required */ |
||||
#define PROCNUM_MERGEABLE 12 /* optional */ |
||||
#define PROCNUM_CONTAINS 13 /* optional */ |
||||
#define PROCNUM_EMPTY 14 /* optional */ |
||||
|
||||
|
||||
/*
|
||||
* Subtract this from procnum to obtain index in InclusionOpaque arrays |
||||
* (Must be equal to minimum of private procnums). |
||||
*/ |
||||
#define PROCNUM_BASE 11 |
||||
|
||||
/*-
|
||||
* The values stored in the bv_values arrays correspond to: |
||||
* |
||||
* 0 - the union of the values in the block range |
||||
* 1 - whether an empty value is present in any tuple in the block range |
||||
* 2 - whether the values in the block range cannot be merged (e.g. an IPv6 |
||||
* address amidst IPv4 addresses). |
||||
*/ |
||||
#define INCLUSION_UNION 0 |
||||
#define INCLUSION_UNMERGEABLE 1 |
||||
#define INCLUSION_CONTAINS_EMPTY 2 |
||||
|
||||
|
||||
typedef struct InclusionOpaque |
||||
{ |
||||
FmgrInfo extra_procinfos[INCLUSION_MAX_PROCNUMS]; |
||||
bool extra_proc_missing[INCLUSION_MAX_PROCNUMS]; |
||||
Oid cached_subtype; |
||||
FmgrInfo strategy_procinfos[RTMaxStrategyNumber]; |
||||
} InclusionOpaque; |
||||
|
||||
Datum brin_inclusion_opcinfo(PG_FUNCTION_ARGS); |
||||
Datum brin_inclusion_add_value(PG_FUNCTION_ARGS); |
||||
Datum brin_inclusion_consistent(PG_FUNCTION_ARGS); |
||||
Datum brin_inclusion_union(PG_FUNCTION_ARGS); |
||||
static FmgrInfo *inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, |
||||
uint16 procnum); |
||||
static FmgrInfo *inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, |
||||
Oid subtype, uint16 strategynum); |
||||
|
||||
|
||||
/*
|
||||
* BRIN inclusion OpcInfo function |
||||
*/ |
||||
Datum |
||||
brin_inclusion_opcinfo(PG_FUNCTION_ARGS) |
||||
{ |
||||
Oid typoid = PG_GETARG_OID(0); |
||||
BrinOpcInfo *result; |
||||
TypeCacheEntry *bool_typcache = lookup_type_cache(BOOLOID, 0); |
||||
|
||||
/*
|
||||
* All members of opaque are initialized lazily; both procinfo arrays |
||||
* start out as non-initialized by having fn_oid be InvalidOid, and |
||||
* "missing" to false, by zeroing here. strategy_procinfos elements can |
||||
* be invalidated when cached_subtype changes by zeroing fn_oid. |
||||
* extra_procinfo entries are never invalidated, but if a lookup fails |
||||
* (which is expected), extra_proc_missing is set to true, indicating not |
||||
* to look it up again. |
||||
*/ |
||||
result = palloc0(MAXALIGN(SizeofBrinOpcInfo(3)) + sizeof(InclusionOpaque)); |
||||
result->oi_nstored = 3; |
||||
result->oi_opaque = (InclusionOpaque *) |
||||
MAXALIGN((char *) result + SizeofBrinOpcInfo(3)); |
||||
|
||||
/* the union */ |
||||
result->oi_typcache[INCLUSION_UNION] = |
||||
lookup_type_cache(typoid, 0); |
||||
|
||||
/* includes elements that are not mergeable */ |
||||
result->oi_typcache[INCLUSION_UNMERGEABLE] = bool_typcache; |
||||
|
||||
/* includes the empty element */ |
||||
result->oi_typcache[INCLUSION_CONTAINS_EMPTY] = bool_typcache; |
||||
|
||||
PG_RETURN_POINTER(result); |
||||
} |
||||
|
||||
/*
|
||||
* BRIN inclusion add value function |
||||
* |
||||
* Examine the given index tuple (which contains partial status of a certain |
||||
* page range) by comparing it to the given value that comes from another heap |
||||
* tuple. If the new value is outside the union specified by the existing |
||||
* tuple values, update the index tuple and return true. Otherwise, return |
||||
* false and do not modify in this case. |
||||
*/ |
||||
Datum |
||||
brin_inclusion_add_value(PG_FUNCTION_ARGS) |
||||
{ |
||||
BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); |
||||
BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); |
||||
Datum newval = PG_GETARG_DATUM(2); |
||||
bool isnull = PG_GETARG_BOOL(3); |
||||
Oid colloid = PG_GET_COLLATION(); |
||||
FmgrInfo *finfo; |
||||
Datum result; |
||||
bool new = false; |
||||
AttrNumber attno; |
||||
Form_pg_attribute attr; |
||||
|
||||
/*
|
||||
* If the new value is null, we record that we saw it if it's the first |
||||
* one; otherwise, there's nothing to do. |
||||
*/ |
||||
if (isnull) |
||||
{ |
||||
if (column->bv_hasnulls) |
||||
PG_RETURN_BOOL(false); |
||||
|
||||
column->bv_hasnulls = true; |
||||
PG_RETURN_BOOL(true); |
||||
} |
||||
|
||||
attno = column->bv_attno; |
||||
attr = bdesc->bd_tupdesc->attrs[attno - 1]; |
||||
|
||||
/*
|
||||
* If the recorded value is null, copy the new value (which we know to be |
||||
* not null), and we're almost done. |
||||
*/ |
||||
if (column->bv_allnulls) |
||||
{ |
||||
column->bv_values[INCLUSION_UNION] = |
||||
datumCopy(newval, attr->attbyval, attr->attlen); |
||||
column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(false); |
||||
column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(false); |
||||
column->bv_allnulls = false; |
||||
new = true; |
||||
} |
||||
|
||||
/*
|
||||
* No need for further processing if the block range is marked as |
||||
* containing unmergeable values. |
||||
*/ |
||||
if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) |
||||
PG_RETURN_BOOL(false); |
||||
|
||||
/*
|
||||
* If the opclass supports the concept of empty values, test the passed |
||||
* new value for emptiness; if it returns true, we need to set the |
||||
* "contains empty" flag in the element (unless already set). |
||||
*/ |
||||
finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_EMPTY); |
||||
if (finfo != NULL && DatumGetBool(FunctionCall1Coll(finfo, colloid, newval))) |
||||
{ |
||||
if (!DatumGetBool(column->bv_values[INCLUSION_CONTAINS_EMPTY])) |
||||
{ |
||||
column->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true); |
||||
PG_RETURN_BOOL(true); |
||||
} |
||||
|
||||
PG_RETURN_BOOL(false); |
||||
} |
||||
|
||||
if (new) |
||||
PG_RETURN_BOOL(true); |
||||
|
||||
/* Check if the new value is already contained. */ |
||||
finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_CONTAINS); |
||||
if (finfo != NULL && |
||||
DatumGetBool(FunctionCall2Coll(finfo, colloid, |
||||
column->bv_values[INCLUSION_UNION], |
||||
newval))) |
||||
PG_RETURN_BOOL(false); |
||||
|
||||
/*
|
||||
* Check if the new value is mergeable to the existing union. If it is |
||||
* not, mark the value as containing unmergeable elements and get out. |
||||
* |
||||
* Note: at this point we could remove the value from the union, since |
||||
* it's not going to be used any longer. However, the BRIN framework |
||||
* doesn't allow for the value not being present. Improve someday. |
||||
*/ |
||||
finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE); |
||||
if (finfo != NULL && |
||||
!DatumGetBool(FunctionCall2Coll(finfo, colloid, |
||||
column->bv_values[INCLUSION_UNION], |
||||
newval))) |
||||
{ |
||||
column->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true); |
||||
PG_RETURN_BOOL(true); |
||||
} |
||||
|
||||
/* Finally, merge the new value to the existing union. */ |
||||
finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE); |
||||
Assert(finfo != NULL); |
||||
result = FunctionCall2Coll(finfo, colloid, |
||||
column->bv_values[INCLUSION_UNION], newval); |
||||
if (!attr->attbyval) |
||||
pfree(DatumGetPointer(column->bv_values[INCLUSION_UNION])); |
||||
column->bv_values[INCLUSION_UNION] = result; |
||||
|
||||
PG_RETURN_BOOL(true); |
||||
} |
||||
|
||||
/*
|
||||
* BRIN inclusion consistent function |
||||
* |
||||
* All of the strategies are optional. |
||||
*/ |
||||
Datum |
||||
brin_inclusion_consistent(PG_FUNCTION_ARGS) |
||||
{ |
||||
BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); |
||||
BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1); |
||||
ScanKey key = (ScanKey) PG_GETARG_POINTER(2); |
||||
Oid colloid = PG_GET_COLLATION(), |
||||
subtype; |
||||
Datum unionval; |
||||
AttrNumber attno; |
||||
Datum query; |
||||
FmgrInfo *finfo; |
||||
Datum result; |
||||
|
||||
Assert(key->sk_attno == column->bv_attno); |
||||
|
||||
/* Handle IS NULL/IS NOT NULL tests. */ |
||||
if (key->sk_flags & SK_ISNULL) |
||||
{ |
||||
if (key->sk_flags & SK_SEARCHNULL) |
||||
{ |
||||
if (column->bv_allnulls || column->bv_hasnulls) |
||||
PG_RETURN_BOOL(true); |
||||
PG_RETURN_BOOL(false); |
||||
} |
||||
|
||||
/*
|
||||
* For IS NOT NULL, we can only skip ranges that are known to have |
||||
* only nulls. |
||||
*/ |
||||
Assert(key->sk_flags & SK_SEARCHNOTNULL); |
||||
PG_RETURN_BOOL(!column->bv_allnulls); |
||||
} |
||||
|
||||
/* If it is all nulls, it cannot possibly be consistent. */ |
||||
if (column->bv_allnulls) |
||||
PG_RETURN_BOOL(false); |
||||
|
||||
/* It has to be checked, if it contains elements that are not mergeable. */ |
||||
if (DatumGetBool(column->bv_values[INCLUSION_UNMERGEABLE])) |
||||
PG_RETURN_BOOL(true); |
||||
|
||||
attno = key->sk_attno; |
||||
subtype = key->sk_subtype; |
||||
query = key->sk_argument; |
||||
unionval = column->bv_values[INCLUSION_UNION]; |
||||
switch (key->sk_strategy) |
||||
{ |
||||
/*
|
||||
* Placement strategies |
||||
* |
||||
* These are implemented by logically negating the result of the |
||||
* converse placement operator; for this to work, the converse operator |
||||
* must be part of the opclass. An error will be thrown by |
||||
* inclusion_get_strategy_procinfo() if the required strategy is not |
||||
* part of the opclass. |
||||
* |
||||
* These all return false if either argument is empty, so there is |
||||
* no need to check for empty elements. |
||||
*/ |
||||
|
||||
case RTLeftStrategyNumber: |
||||
finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
||||
RTOverRightStrategyNumber); |
||||
result = FunctionCall2Coll(finfo, colloid, unionval, query); |
||||
PG_RETURN_BOOL(!DatumGetBool(result)); |
||||
|
||||
case RTOverLeftStrategyNumber: |
||||
finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
||||
RTRightStrategyNumber); |
||||
result = FunctionCall2Coll(finfo, colloid, unionval, query); |
||||
PG_RETURN_BOOL(!DatumGetBool(result)); |
||||
|
||||
case RTOverRightStrategyNumber: |
||||
finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
||||
RTLeftStrategyNumber); |
||||
result = FunctionCall2Coll(finfo, colloid, unionval, query); |
||||
PG_RETURN_BOOL(!DatumGetBool(result)); |
||||
|
||||
case RTRightStrategyNumber: |
||||
finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
||||
RTOverLeftStrategyNumber); |
||||
result = FunctionCall2Coll(finfo, colloid, unionval, query); |
||||
PG_RETURN_BOOL(!DatumGetBool(result)); |
||||
|
||||
case RTBelowStrategyNumber: |
||||
finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
||||
RTOverAboveStrategyNumber); |
||||
result = FunctionCall2Coll(finfo, colloid, unionval, query); |
||||
PG_RETURN_BOOL(!DatumGetBool(result)); |
||||
|
||||
case RTOverBelowStrategyNumber: |
||||
finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
||||
RTAboveStrategyNumber); |
||||
result = FunctionCall2Coll(finfo, colloid, unionval, query); |
||||
PG_RETURN_BOOL(!DatumGetBool(result)); |
||||
|
||||
case RTOverAboveStrategyNumber: |
||||
finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
||||
RTBelowStrategyNumber); |
||||
result = FunctionCall2Coll(finfo, colloid, unionval, query); |
||||
PG_RETURN_BOOL(!DatumGetBool(result)); |
||||
|
||||
case RTAboveStrategyNumber: |
||||
finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
||||
RTOverBelowStrategyNumber); |
||||
result = FunctionCall2Coll(finfo, colloid, unionval, query); |
||||
PG_RETURN_BOOL(!DatumGetBool(result)); |
||||
|
||||
/*
|
||||
* Overlap and contains strategies |
||||
* |
||||
* These strategies are simple enough that we can simply call the |
||||
* operator and return its result. Empty elements don't change |
||||
* the result. |
||||
*/ |
||||
|
||||
case RTOverlapStrategyNumber: |
||||
case RTContainsStrategyNumber: |
||||
case RTOldContainsStrategyNumber: |
||||
case RTContainsElemStrategyNumber: |
||||
case RTSubStrategyNumber: |
||||
case RTSubEqualStrategyNumber: |
||||
finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
||||
key->sk_strategy); |
||||
result = FunctionCall2Coll(finfo, colloid, unionval, query); |
||||
PG_RETURN_DATUM(result); |
||||
|
||||
/*
|
||||
* Contained by strategies |
||||
* |
||||
* We cannot just call the original operator for the contained by |
||||
* strategies because some elements can be contained even though |
||||
* the union is not; instead we use the overlap operator. |
||||
* |
||||
* We check for empty elements separately as they are not merged to |
||||
* the union but contained by everything. |
||||
*/ |
||||
|
||||
case RTContainedByStrategyNumber: |
||||
case RTOldContainedByStrategyNumber: |
||||
case RTSuperStrategyNumber: |
||||
case RTSuperEqualStrategyNumber: |
||||
finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
||||
RTOverlapStrategyNumber); |
||||
result = FunctionCall2Coll(finfo, colloid, unionval, query); |
||||
if (DatumGetBool(result)) |
||||
PG_RETURN_BOOL(true); |
||||
|
||||
PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); |
||||
|
||||
/*
|
||||
* Adjacent strategy |
||||
* |
||||
* We test for overlap first but to be safe we need to call |
||||
* the actual adjacent operator also. |
||||
* |
||||
* An empty element cannot be adjacent to any other, so there is |
||||
* no need to check for it. |
||||
*/ |
||||
|
||||
case RTAdjacentStrategyNumber: |
||||
finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
||||
RTOverlapStrategyNumber); |
||||
result = FunctionCall2Coll(finfo, colloid, unionval, query); |
||||
if (DatumGetBool(result)) |
||||
PG_RETURN_BOOL(true); |
||||
|
||||
finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
||||
RTAdjacentStrategyNumber); |
||||
result = FunctionCall2Coll(finfo, colloid, unionval, query); |
||||
PG_RETURN_DATUM(result); |
||||
|
||||
/*
|
||||
* Basic comparison strategies |
||||
* |
||||
* It is straightforward to support the equality strategies with |
||||
* the contains operator. Generally, inequality strategies do not |
||||
* make much sense for the types which will be used with the |
||||
* inclusion BRIN family of opclasses, but is is possible to |
||||
* implement them with logical negation of the left-of and right-of |
||||
* operators. |
||||
* |
||||
* NB: These strategies cannot be used with geometric datatypes |
||||
* that use comparison of areas! The only exception is the "same" |
||||
* strategy. |
||||
* |
||||
* Empty elements are considered to be less than the others. We |
||||
* cannot use the empty support function to check the query is an |
||||
* empty element, because the query can be another data type than |
||||
* the empty support function argument. So we will return true, |
||||
* if there is a possibility that empty elements will change the |
||||
* result. |
||||
*/ |
||||
|
||||
case RTLessStrategyNumber: |
||||
case RTLessEqualStrategyNumber: |
||||
finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
||||
RTRightStrategyNumber); |
||||
result = FunctionCall2Coll(finfo, colloid, unionval, query); |
||||
if (!DatumGetBool(result)) |
||||
PG_RETURN_BOOL(true); |
||||
|
||||
PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); |
||||
|
||||
case RTSameStrategyNumber: |
||||
case RTEqualStrategyNumber: |
||||
finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
||||
RTContainsStrategyNumber); |
||||
result = FunctionCall2Coll(finfo, colloid, unionval, query); |
||||
if (DatumGetBool(result)) |
||||
PG_RETURN_BOOL(true); |
||||
|
||||
PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); |
||||
|
||||
case RTGreaterEqualStrategyNumber: |
||||
finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
||||
RTLeftStrategyNumber); |
||||
result = FunctionCall2Coll(finfo, colloid, unionval, query); |
||||
if (!DatumGetBool(result)) |
||||
PG_RETURN_BOOL(true); |
||||
|
||||
PG_RETURN_DATUM(column->bv_values[INCLUSION_CONTAINS_EMPTY]); |
||||
|
||||
case RTGreaterStrategyNumber: |
||||
/* no need to check for empty elements */ |
||||
finfo = inclusion_get_strategy_procinfo(bdesc, attno, subtype, |
||||
RTLeftStrategyNumber); |
||||
result = FunctionCall2Coll(finfo, colloid, unionval, query); |
||||
PG_RETURN_BOOL(!DatumGetBool(result)); |
||||
|
||||
default: |
||||
/* shouldn't happen */ |
||||
elog(ERROR, "invalid strategy number %d", key->sk_strategy); |
||||
PG_RETURN_BOOL(false); |
||||
} |
||||
} |
||||
|
||||
/*
|
||||
* BRIN inclusion union function |
||||
* |
||||
* Given two BrinValues, update the first of them as a union of the summary |
||||
* values contained in both. The second one is untouched. |
||||
*/ |
||||
Datum |
||||
brin_inclusion_union(PG_FUNCTION_ARGS) |
||||
{ |
||||
BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0); |
||||
BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1); |
||||
BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2); |
||||
Oid colloid = PG_GET_COLLATION(); |
||||
AttrNumber attno; |
||||
Form_pg_attribute attr; |
||||
FmgrInfo *finfo; |
||||
Datum result; |
||||
|
||||
Assert(col_a->bv_attno == col_b->bv_attno); |
||||
|
||||
/* Adjust "hasnulls". */ |
||||
if (!col_a->bv_hasnulls && col_b->bv_hasnulls) |
||||
col_a->bv_hasnulls = true; |
||||
|
||||
/* If there are no values in B, there's nothing left to do. */ |
||||
if (col_b->bv_allnulls) |
||||
PG_RETURN_VOID(); |
||||
|
||||
attno = col_a->bv_attno; |
||||
attr = bdesc->bd_tupdesc->attrs[attno - 1]; |
||||
|
||||
/*
|
||||
* Adjust "allnulls". If A doesn't have values, just copy the values from |
||||
* B into A, and we're done. We cannot run the operators in this case, |
||||
* because values in A might contain garbage. Note we already established |
||||
* that B contains values. |
||||
*/ |
||||
if (col_a->bv_allnulls) |
||||
{ |
||||
col_a->bv_allnulls = false; |
||||
col_a->bv_values[INCLUSION_UNION] = |
||||
datumCopy(col_b->bv_values[INCLUSION_UNION], |
||||
attr->attbyval, attr->attlen); |
||||
col_a->bv_values[INCLUSION_UNMERGEABLE] = |
||||
col_b->bv_values[INCLUSION_UNMERGEABLE]; |
||||
col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = |
||||
col_b->bv_values[INCLUSION_CONTAINS_EMPTY]; |
||||
PG_RETURN_VOID(); |
||||
} |
||||
|
||||
/* If B includes empty elements, mark A similarly, if needed. */ |
||||
if (!DatumGetBool(col_a->bv_values[INCLUSION_CONTAINS_EMPTY]) && |
||||
DatumGetBool(col_b->bv_values[INCLUSION_CONTAINS_EMPTY])) |
||||
col_a->bv_values[INCLUSION_CONTAINS_EMPTY] = BoolGetDatum(true); |
||||
|
||||
/* Check if A includes elements that are not mergeable. */ |
||||
if (DatumGetBool(col_a->bv_values[INCLUSION_UNMERGEABLE])) |
||||
PG_RETURN_VOID(); |
||||
|
||||
/* If B includes elements that are not mergeable, mark A similarly. */ |
||||
if (DatumGetBool(col_b->bv_values[INCLUSION_UNMERGEABLE])) |
||||
{ |
||||
col_a->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true); |
||||
PG_RETURN_VOID(); |
||||
} |
||||
|
||||
/* Check if A and B are mergeable; if not, mark A unmergeable. */ |
||||
finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGEABLE); |
||||
if (finfo != NULL && |
||||
!DatumGetBool(FunctionCall2Coll(finfo, colloid, |
||||
col_a->bv_values[INCLUSION_UNION], |
||||
col_b->bv_values[INCLUSION_UNION]))) |
||||
{ |
||||
col_a->bv_values[INCLUSION_UNMERGEABLE] = BoolGetDatum(true); |
||||
PG_RETURN_VOID(); |
||||
} |
||||
|
||||
/* Finally, merge B to A. */ |
||||
finfo = inclusion_get_procinfo(bdesc, attno, PROCNUM_MERGE); |
||||
Assert(finfo != NULL); |
||||
result = FunctionCall2Coll(finfo, colloid, |
||||
col_a->bv_values[INCLUSION_UNION], |
||||
col_b->bv_values[INCLUSION_UNION]); |
||||
if (!attr->attbyval) |
||||
pfree(DatumGetPointer(col_a->bv_values[INCLUSION_UNION])); |
||||
col_a->bv_values[INCLUSION_UNION] = result; |
||||
|
||||
PG_RETURN_VOID(); |
||||
} |
||||
|
||||
/*
|
||||
* Cache and return inclusion opclass support procedure |
||||
* |
||||
* Return the procedure corresponding to the given function support number |
||||
* or null if it is not exists. |
||||
*/ |
||||
static FmgrInfo * |
||||
inclusion_get_procinfo(BrinDesc *bdesc, uint16 attno, uint16 procnum) |
||||
{ |
||||
InclusionOpaque *opaque; |
||||
uint16 basenum = procnum - PROCNUM_BASE; |
||||
|
||||
/*
|
||||
* We cache these in the opaque struct, to avoid repetitive syscache |
||||
* lookups. |
||||
*/ |
||||
opaque = (InclusionOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; |
||||
|
||||
/*
|
||||
* If we already searched for this proc and didn't find it, don't bother |
||||
* searching again. |
||||
*/ |
||||
if (opaque->extra_proc_missing[basenum]) |
||||
return NULL; |
||||
|
||||
if (opaque->extra_procinfos[basenum].fn_oid == InvalidOid) |
||||
{ |
||||
if (RegProcedureIsValid(index_getprocid(bdesc->bd_index, attno, |
||||
procnum))) |
||||
{ |
||||
fmgr_info_copy(&opaque->extra_procinfos[basenum], |
||||
index_getprocinfo(bdesc->bd_index, attno, procnum), |
||||
bdesc->bd_context); |
||||
} |
||||
else |
||||
{ |
||||
opaque->extra_proc_missing[basenum] = true; |
||||
return NULL; |
||||
} |
||||
} |
||||
|
||||
return &opaque->extra_procinfos[basenum]; |
||||
} |
||||
|
||||
/*
|
||||
* Cache and return the procedure of the given strategy |
||||
* |
||||
* Return the procedure corresponding to the given sub-type and strategy |
||||
* number. The data type of the index will be used as the left hand side of |
||||
* the operator and the given sub-type will be used as the right hand side. |
||||
* Throws an error if the pg_amop row does not exist, but that should not |
||||
* happen with a properly configured opclass. |
||||
* |
||||
* It always throws an error when the data type of the opclass is different |
||||
* from the data type of the column or the expression. That happens when the |
||||
* column data type has implicit cast to the opclass data type. We don't |
||||
* bother casting types, because this situation can easily be avoided by |
||||
* setting storage data type to that of the opclass. The same problem does not |
||||
* apply to the data type of the right hand side, because the type in the |
||||
* ScanKey always matches the opclass' one. |
||||
* |
||||
* Note: this function mirrors minmax_get_strategy_procinfo; if changes are |
||||
* made here, see that function too. |
||||
*/ |
||||
static FmgrInfo * |
||||
inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, |
||||
uint16 strategynum) |
||||
{ |
||||
InclusionOpaque *opaque; |
||||
|
||||
Assert(strategynum >= 1 && |
||||
strategynum <= RTMaxStrategyNumber); |
||||
|
||||
opaque = (InclusionOpaque *) bdesc->bd_info[attno - 1]->oi_opaque; |
||||
|
||||
/*
|
||||
* We cache the procedures for the last sub-type in the opaque struct, to |
||||
* avoid repetitive syscache lookups. If the sub-type is changed, |
||||
* invalidate all the cached entries. |
||||
*/ |
||||
if (opaque->cached_subtype != subtype) |
||||
{ |
||||
uint16 i; |
||||
|
||||
for (i = 1; i <= RTMaxStrategyNumber; i++) |
||||
opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid; |
||||
opaque->cached_subtype = subtype; |
||||
} |
||||
|
||||
if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid) |
||||
{ |
||||
Form_pg_attribute attr; |
||||
HeapTuple tuple; |
||||
Oid opfamily, |
||||
oprid; |
||||
bool isNull; |
||||
|
||||
opfamily = bdesc->bd_index->rd_opfamily[attno - 1]; |
||||
attr = bdesc->bd_tupdesc->attrs[attno - 1]; |
||||
tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily), |
||||
ObjectIdGetDatum(attr->atttypid), |
||||
ObjectIdGetDatum(subtype), |
||||
Int16GetDatum(strategynum)); |
||||
|
||||
if (!HeapTupleIsValid(tuple)) |
||||
elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", |
||||
strategynum, attr->atttypid, subtype, opfamily); |
||||
|
||||
oprid = DatumGetObjectId(SysCacheGetAttr(AMOPSTRATEGY, tuple, |
||||
Anum_pg_amop_amopopr, &isNull)); |
||||
ReleaseSysCache(tuple); |
||||
Assert(!isNull && RegProcedureIsValid(oprid)); |
||||
|
||||
fmgr_info_cxt(get_opcode(oprid), |
||||
&opaque->strategy_procinfos[strategynum - 1], |
||||
bdesc->bd_context); |
||||
} |
||||
|
||||
return &opaque->strategy_procinfos[strategynum - 1]; |
||||
} |
||||
Loading…
Reference in new issue