PostgreSQL Source Code  git master
brin_minmax.c
Go to the documentation of this file.
1 /*
2  * brin_minmax.c
3  * Implementation of Min/Max opclass for BRIN
4  *
5  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
6  * Portions Copyright (c) 1994, Regents of the University of California
7  *
8  * IDENTIFICATION
9  * src/backend/access/brin/brin_minmax.c
10  */
11 #include "postgres.h"
12 
13 #include "access/brin_internal.h"
14 #include "access/brin_tuple.h"
15 #include "access/stratnum.h"
16 #include "catalog/pg_amop.h"
17 #include "utils/datum.h"
18 #include "utils/fmgrprotos.h"
19 #include "utils/lsyscache.h"
20 #include "utils/rel.h"
21 #include "utils/syscache.h"
22 
23 typedef struct MinmaxOpaque
24 {
28 
30  Oid subtype, uint16 strategynum);
31 
32 
33 Datum
35 {
36  Oid typoid = PG_GETARG_OID(0);
37  BrinOpcInfo *result;
38 
39  /*
40  * opaque->strategy_procinfos is initialized lazily; here it is set to
41  * all-uninitialized by palloc0 which sets fn_oid to InvalidOid.
42  */
43 
44  result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) +
45  sizeof(MinmaxOpaque));
46  result->oi_nstored = 2;
47  result->oi_regular_nulls = true;
48  result->oi_opaque = (MinmaxOpaque *)
49  MAXALIGN((char *) result + SizeofBrinOpcInfo(2));
50  result->oi_typcache[0] = result->oi_typcache[1] =
51  lookup_type_cache(typoid, 0);
52 
53  PG_RETURN_POINTER(result);
54 }
55 
56 /*
57  * Examine the given index tuple (which contains partial status of a certain
58  * page range) by comparing it to the given value that comes from another heap
59  * tuple. If the new value is outside the min/max range specified by the
60  * existing tuple values, update the index tuple and return true. Otherwise,
61  * return false and do not modify in this case.
62  */
63 Datum
65 {
66  BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
67  BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
70  Oid colloid = PG_GET_COLLATION();
71  FmgrInfo *cmpFn;
72  Datum compar;
73  bool updated = false;
74  Form_pg_attribute attr;
75  AttrNumber attno;
76 
77  Assert(!isnull);
78 
79  attno = column->bv_attno;
80  attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
81 
82  /*
83  * If the recorded value is null, store the new value (which we know to be
84  * not null) as both minimum and maximum, and we're done.
85  */
86  if (column->bv_allnulls)
87  {
88  column->bv_values[0] = datumCopy(newval, attr->attbyval, attr->attlen);
89  column->bv_values[1] = datumCopy(newval, attr->attbyval, attr->attlen);
90  column->bv_allnulls = false;
91  PG_RETURN_BOOL(true);
92  }
93 
94  /*
95  * Otherwise, need to compare the new value with the existing boundaries
96  * and update them accordingly. First check if it's less than the
97  * existing minimum.
98  */
99  cmpFn = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
101  compar = FunctionCall2Coll(cmpFn, colloid, newval, column->bv_values[0]);
102  if (DatumGetBool(compar))
103  {
104  if (!attr->attbyval)
105  pfree(DatumGetPointer(column->bv_values[0]));
106  column->bv_values[0] = datumCopy(newval, attr->attbyval, attr->attlen);
107  updated = true;
108  }
109 
110  /*
111  * And now compare it to the existing maximum.
112  */
113  cmpFn = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
115  compar = FunctionCall2Coll(cmpFn, colloid, newval, column->bv_values[1]);
116  if (DatumGetBool(compar))
117  {
118  if (!attr->attbyval)
119  pfree(DatumGetPointer(column->bv_values[1]));
120  column->bv_values[1] = datumCopy(newval, attr->attbyval, attr->attlen);
121  updated = true;
122  }
123 
124  PG_RETURN_BOOL(updated);
125 }
126 
127 /*
128  * Given an index tuple corresponding to a certain page range and a scan key,
129  * return whether the scan key is consistent with the index tuple's min/max
130  * values. Return true if so, false otherwise.
131  *
132  * We're no longer dealing with NULL keys in the consistent function, that is
133  * now handled by the AM code. That means we should not get any all-NULL ranges
134  * either, because those can't be consistent with regular (not [IS] NULL) keys.
135  */
136 Datum
138 {
139  BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
140  BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
142  Oid colloid = PG_GET_COLLATION(),
143  subtype;
144  AttrNumber attno;
145  Datum value;
146  Datum matches;
147  FmgrInfo *finfo;
148 
149  /* This opclass uses the old signature with only three arguments. */
150  Assert(PG_NARGS() == 3);
151 
152  /* Should not be dealing with all-NULL ranges. */
153  Assert(!column->bv_allnulls);
154 
155  attno = key->sk_attno;
156  subtype = key->sk_subtype;
157  value = key->sk_argument;
158  switch (key->sk_strategy)
159  {
162  finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
163  key->sk_strategy);
164  matches = FunctionCall2Coll(finfo, colloid, column->bv_values[0],
165  value);
166  break;
168 
169  /*
170  * In the equality case (WHERE col = someval), we want to return
171  * the current page range if the minimum value in the range <=
172  * scan key, and the maximum value >= scan key.
173  */
174  finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
176  matches = FunctionCall2Coll(finfo, colloid, column->bv_values[0],
177  value);
178  if (!DatumGetBool(matches))
179  break;
180  /* max() >= scankey */
181  finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
183  matches = FunctionCall2Coll(finfo, colloid, column->bv_values[1],
184  value);
185  break;
188  finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
189  key->sk_strategy);
190  matches = FunctionCall2Coll(finfo, colloid, column->bv_values[1],
191  value);
192  break;
193  default:
194  /* shouldn't happen */
195  elog(ERROR, "invalid strategy number %d", key->sk_strategy);
196  matches = 0;
197  break;
198  }
199 
200  PG_RETURN_DATUM(matches);
201 }
202 
203 /*
204  * Given two BrinValues, update the first of them as a union of the summary
205  * values contained in both. The second one is untouched.
206  */
207 Datum
209 {
210  BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
211  BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1);
212  BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
213  Oid colloid = PG_GET_COLLATION();
214  AttrNumber attno;
215  Form_pg_attribute attr;
216  FmgrInfo *finfo;
217  bool needsadj;
218 
219  Assert(col_a->bv_attno == col_b->bv_attno);
220  Assert(!col_a->bv_allnulls && !col_b->bv_allnulls);
221 
222  attno = col_a->bv_attno;
223  attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
224 
225  /* Adjust minimum, if B's min is less than A's min */
226  finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
228  needsadj = FunctionCall2Coll(finfo, colloid, col_b->bv_values[0],
229  col_a->bv_values[0]);
230  if (needsadj)
231  {
232  if (!attr->attbyval)
233  pfree(DatumGetPointer(col_a->bv_values[0]));
234  col_a->bv_values[0] = datumCopy(col_b->bv_values[0],
235  attr->attbyval, attr->attlen);
236  }
237 
238  /* Adjust maximum, if B's max is greater than A's max */
239  finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
241  needsadj = FunctionCall2Coll(finfo, colloid, col_b->bv_values[1],
242  col_a->bv_values[1]);
243  if (needsadj)
244  {
245  if (!attr->attbyval)
246  pfree(DatumGetPointer(col_a->bv_values[1]));
247  col_a->bv_values[1] = datumCopy(col_b->bv_values[1],
248  attr->attbyval, attr->attlen);
249  }
250 
251  PG_RETURN_VOID();
252 }
253 
254 /*
255  * Cache and return the procedure for the given strategy.
256  *
257  * Note: this function mirrors inclusion_get_strategy_procinfo; see notes
258  * there. If changes are made here, see that function too.
259  */
260 static FmgrInfo *
262  uint16 strategynum)
263 {
264  MinmaxOpaque *opaque;
265 
266  Assert(strategynum >= 1 &&
267  strategynum <= BTMaxStrategyNumber);
268 
269  opaque = (MinmaxOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
270 
271  /*
272  * We cache the procedures for the previous subtype in the opaque struct,
273  * to avoid repetitive syscache lookups. If the subtype changed,
274  * invalidate all the cached entries.
275  */
276  if (opaque->cached_subtype != subtype)
277  {
278  uint16 i;
279 
280  for (i = 1; i <= BTMaxStrategyNumber; i++)
281  opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid;
282  opaque->cached_subtype = subtype;
283  }
284 
285  if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid)
286  {
287  Form_pg_attribute attr;
288  HeapTuple tuple;
289  Oid opfamily,
290  oprid;
291 
292  opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
293  attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
294  tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
295  ObjectIdGetDatum(attr->atttypid),
296  ObjectIdGetDatum(subtype),
297  Int16GetDatum(strategynum));
298 
299  if (!HeapTupleIsValid(tuple))
300  elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
301  strategynum, attr->atttypid, subtype, opfamily);
302 
303  oprid = DatumGetObjectId(SysCacheGetAttrNotNull(AMOPSTRATEGY, tuple,
304  Anum_pg_amop_amopopr));
305  ReleaseSysCache(tuple);
307 
309  &opaque->strategy_procinfos[strategynum - 1],
310  bdesc->bd_context);
311  }
312 
313  return &opaque->strategy_procinfos[strategynum - 1];
314 }
int16 AttrNumber
Definition: attnum.h:21
#define SizeofBrinOpcInfo(ncols)
Definition: brin_internal.h:41
static FmgrInfo * minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum)
Definition: brin_minmax.c:261
Datum brin_minmax_consistent(PG_FUNCTION_ARGS)
Definition: brin_minmax.c:137
Datum brin_minmax_union(PG_FUNCTION_ARGS)
Definition: brin_minmax.c:208
Datum brin_minmax_add_value(PG_FUNCTION_ARGS)
Definition: brin_minmax.c:64
Datum brin_minmax_opcinfo(PG_FUNCTION_ARGS)
Definition: brin_minmax.c:34
struct MinmaxOpaque MinmaxOpaque
unsigned short uint16
Definition: c.h:505
#define RegProcedureIsValid(p)
Definition: c.h:777
#define MAXALIGN(LEN)
Definition: c.h:811
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:182
#define Assert(condition)
Definition: c.h:858
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:132
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1149
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:137
#define PG_RETURN_VOID()
Definition: fmgr.h:349
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define PG_GETARG_POINTER(n)
Definition: fmgr.h:276
#define PG_GETARG_DATUM(n)
Definition: fmgr.h:268
#define PG_NARGS()
Definition: fmgr.h:203
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:353
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:361
#define PG_GET_COLLATION()
Definition: fmgr.h:198
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359
#define newval
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static struct @155 value
int i
Definition: isn.c:73
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1285
void pfree(void *pointer)
Definition: mcxt.c:1520
void * palloc0(Size size)
Definition: mcxt.c:1346
Oid oprid(Operator op)
Definition: parse_oper.c:238
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
static bool DatumGetBool(Datum X)
Definition: postgres.h:90
uintptr_t Datum
Definition: postgres.h:64
static Oid DatumGetObjectId(Datum X)
Definition: postgres.h:242
static Datum Int16GetDatum(int16 X)
Definition: postgres.h:172
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
ScanKeyData * ScanKey
Definition: skey.h:75
#define BTGreaterStrategyNumber
Definition: stratnum.h:33
#define BTMaxStrategyNumber
Definition: stratnum.h:35
#define BTLessStrategyNumber
Definition: stratnum.h:29
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define BTLessEqualStrategyNumber
Definition: stratnum.h:30
#define BTGreaterEqualStrategyNumber
Definition: stratnum.h:32
TupleDesc bd_tupdesc
Definition: brin_internal.h:53
BrinOpcInfo * bd_info[FLEXIBLE_ARRAY_MEMBER]
Definition: brin_internal.h:62
Relation bd_index
Definition: brin_internal.h:50
MemoryContext bd_context
Definition: brin_internal.h:47
TypeCacheEntry * oi_typcache[FLEXIBLE_ARRAY_MEMBER]
Definition: brin_internal.h:37
uint16 oi_nstored
Definition: brin_internal.h:28
bool oi_regular_nulls
Definition: brin_internal.h:31
void * oi_opaque
Definition: brin_internal.h:34
Datum * bv_values
Definition: brin_tuple.h:34
AttrNumber bv_attno
Definition: brin_tuple.h:31
bool bv_allnulls
Definition: brin_tuple.h:33
Definition: fmgr.h:57
Oid fn_oid
Definition: fmgr.h:59
FmgrInfo strategy_procinfos[BTMaxStrategyNumber]
Definition: brin_minmax.c:26
Oid cached_subtype
Definition: brin_minmax.c:25
Oid * rd_opfamily
Definition: rel.h:207
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:266
HeapTuple SearchSysCache4(int cacheId, Datum key1, Datum key2, Datum key3, Datum key4)
Definition: syscache.c:251
Datum SysCacheGetAttrNotNull(int cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition: syscache.c:510
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:346