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-2019, 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/genam.h"
16 #include "access/stratnum.h"
17 #include "catalog/pg_amop.h"
18 #include "catalog/pg_type.h"
19 #include "utils/builtins.h"
20 #include "utils/datum.h"
21 #include "utils/lsyscache.h"
22 #include "utils/rel.h"
23 #include "utils/syscache.h"
24 
25 typedef struct MinmaxOpaque
26 {
29 } MinmaxOpaque;
30 
32  Oid subtype, uint16 strategynum);
33 
34 
35 Datum
37 {
38  Oid typoid = PG_GETARG_OID(0);
39  BrinOpcInfo *result;
40 
41  /*
42  * opaque->strategy_procinfos is initialized lazily; here it is set to
43  * all-uninitialized by palloc0 which sets fn_oid to InvalidOid.
44  */
45 
46  result = palloc0(MAXALIGN(SizeofBrinOpcInfo(2)) +
47  sizeof(MinmaxOpaque));
48  result->oi_nstored = 2;
49  result->oi_opaque = (MinmaxOpaque *)
50  MAXALIGN((char *) result + SizeofBrinOpcInfo(2));
51  result->oi_typcache[0] = result->oi_typcache[1] =
52  lookup_type_cache(typoid, 0);
53 
54  PG_RETURN_POINTER(result);
55 }
56 
57 /*
58  * Examine the given index tuple (which contains partial status of a certain
59  * page range) by comparing it to the given value that comes from another heap
60  * tuple. If the new value is outside the min/max range specified by the
61  * existing tuple values, update the index tuple and return true. Otherwise,
62  * return false and do not modify in this case.
63  */
64 Datum
66 {
67  BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
68  BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
70  bool isnull = PG_GETARG_DATUM(3);
71  Oid colloid = PG_GET_COLLATION();
72  FmgrInfo *cmpFn;
73  Datum compar;
74  bool updated = false;
75  Form_pg_attribute attr;
76  AttrNumber attno;
77 
78  /*
79  * If the new value is null, we record that we saw it if it's the first
80  * one; otherwise, there's nothing to do.
81  */
82  if (isnull)
83  {
84  if (column->bv_hasnulls)
85  PG_RETURN_BOOL(false);
86 
87  column->bv_hasnulls = true;
88  PG_RETURN_BOOL(true);
89  }
90 
91  attno = column->bv_attno;
92  attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
93 
94  /*
95  * If the recorded value is null, store the new value (which we know to be
96  * not null) as both minimum and maximum, and we're done.
97  */
98  if (column->bv_allnulls)
99  {
100  column->bv_values[0] = datumCopy(newval, attr->attbyval, attr->attlen);
101  column->bv_values[1] = datumCopy(newval, attr->attbyval, attr->attlen);
102  column->bv_allnulls = false;
103  PG_RETURN_BOOL(true);
104  }
105 
106  /*
107  * Otherwise, need to compare the new value with the existing boundaries
108  * and update them accordingly. First check if it's less than the
109  * existing minimum.
110  */
111  cmpFn = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
113  compar = FunctionCall2Coll(cmpFn, colloid, newval, column->bv_values[0]);
114  if (DatumGetBool(compar))
115  {
116  if (!attr->attbyval)
117  pfree(DatumGetPointer(column->bv_values[0]));
118  column->bv_values[0] = datumCopy(newval, attr->attbyval, attr->attlen);
119  updated = true;
120  }
121 
122  /*
123  * And now compare it to the existing maximum.
124  */
125  cmpFn = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
127  compar = FunctionCall2Coll(cmpFn, colloid, newval, column->bv_values[1]);
128  if (DatumGetBool(compar))
129  {
130  if (!attr->attbyval)
131  pfree(DatumGetPointer(column->bv_values[1]));
132  column->bv_values[1] = datumCopy(newval, attr->attbyval, attr->attlen);
133  updated = true;
134  }
135 
136  PG_RETURN_BOOL(updated);
137 }
138 
139 /*
140  * Given an index tuple corresponding to a certain page range and a scan key,
141  * return whether the scan key is consistent with the index tuple's min/max
142  * values. Return true if so, false otherwise.
143  */
144 Datum
146 {
147  BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
148  BrinValues *column = (BrinValues *) PG_GETARG_POINTER(1);
150  Oid colloid = PG_GET_COLLATION(),
151  subtype;
152  AttrNumber attno;
153  Datum value;
154  Datum matches;
155  FmgrInfo *finfo;
156 
157  Assert(key->sk_attno == column->bv_attno);
158 
159  /* handle IS NULL/IS NOT NULL tests */
160  if (key->sk_flags & SK_ISNULL)
161  {
162  if (key->sk_flags & SK_SEARCHNULL)
163  {
164  if (column->bv_allnulls || column->bv_hasnulls)
165  PG_RETURN_BOOL(true);
166  PG_RETURN_BOOL(false);
167  }
168 
169  /*
170  * For IS NOT NULL, we can only skip ranges that are known to have
171  * only nulls.
172  */
173  if (key->sk_flags & SK_SEARCHNOTNULL)
174  PG_RETURN_BOOL(!column->bv_allnulls);
175 
176  /*
177  * Neither IS NULL nor IS NOT NULL was used; assume all indexable
178  * operators are strict and return false.
179  */
180  PG_RETURN_BOOL(false);
181  }
182 
183  /* if the range is all empty, it cannot possibly be consistent */
184  if (column->bv_allnulls)
185  PG_RETURN_BOOL(false);
186 
187  attno = key->sk_attno;
188  subtype = key->sk_subtype;
189  value = key->sk_argument;
190  switch (key->sk_strategy)
191  {
194  finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
195  key->sk_strategy);
196  matches = FunctionCall2Coll(finfo, colloid, column->bv_values[0],
197  value);
198  break;
200 
201  /*
202  * In the equality case (WHERE col = someval), we want to return
203  * the current page range if the minimum value in the range <=
204  * scan key, and the maximum value >= scan key.
205  */
206  finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
208  matches = FunctionCall2Coll(finfo, colloid, column->bv_values[0],
209  value);
210  if (!DatumGetBool(matches))
211  break;
212  /* max() >= scankey */
213  finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
215  matches = FunctionCall2Coll(finfo, colloid, column->bv_values[1],
216  value);
217  break;
220  finfo = minmax_get_strategy_procinfo(bdesc, attno, subtype,
221  key->sk_strategy);
222  matches = FunctionCall2Coll(finfo, colloid, column->bv_values[1],
223  value);
224  break;
225  default:
226  /* shouldn't happen */
227  elog(ERROR, "invalid strategy number %d", key->sk_strategy);
228  matches = 0;
229  break;
230  }
231 
232  PG_RETURN_DATUM(matches);
233 }
234 
235 /*
236  * Given two BrinValues, update the first of them as a union of the summary
237  * values contained in both. The second one is untouched.
238  */
239 Datum
241 {
242  BrinDesc *bdesc = (BrinDesc *) PG_GETARG_POINTER(0);
243  BrinValues *col_a = (BrinValues *) PG_GETARG_POINTER(1);
244  BrinValues *col_b = (BrinValues *) PG_GETARG_POINTER(2);
245  Oid colloid = PG_GET_COLLATION();
246  AttrNumber attno;
247  Form_pg_attribute attr;
248  FmgrInfo *finfo;
249  bool needsadj;
250 
251  Assert(col_a->bv_attno == col_b->bv_attno);
252 
253  /* Adjust "hasnulls" */
254  if (!col_a->bv_hasnulls && col_b->bv_hasnulls)
255  col_a->bv_hasnulls = true;
256 
257  /* If there are no values in B, there's nothing left to do */
258  if (col_b->bv_allnulls)
259  PG_RETURN_VOID();
260 
261  attno = col_a->bv_attno;
262  attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
263 
264  /*
265  * Adjust "allnulls". If A doesn't have values, just copy the values from
266  * B into A, and we're done. We cannot run the operators in this case,
267  * because values in A might contain garbage. Note we already established
268  * that B contains values.
269  */
270  if (col_a->bv_allnulls)
271  {
272  col_a->bv_allnulls = false;
273  col_a->bv_values[0] = datumCopy(col_b->bv_values[0],
274  attr->attbyval, attr->attlen);
275  col_a->bv_values[1] = datumCopy(col_b->bv_values[1],
276  attr->attbyval, attr->attlen);
277  PG_RETURN_VOID();
278  }
279 
280  /* Adjust minimum, if B's min is less than A's min */
281  finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
283  needsadj = FunctionCall2Coll(finfo, colloid, col_b->bv_values[0],
284  col_a->bv_values[0]);
285  if (needsadj)
286  {
287  if (!attr->attbyval)
288  pfree(DatumGetPointer(col_a->bv_values[0]));
289  col_a->bv_values[0] = datumCopy(col_b->bv_values[0],
290  attr->attbyval, attr->attlen);
291  }
292 
293  /* Adjust maximum, if B's max is greater than A's max */
294  finfo = minmax_get_strategy_procinfo(bdesc, attno, attr->atttypid,
296  needsadj = FunctionCall2Coll(finfo, colloid, col_b->bv_values[1],
297  col_a->bv_values[1]);
298  if (needsadj)
299  {
300  if (!attr->attbyval)
301  pfree(DatumGetPointer(col_a->bv_values[1]));
302  col_a->bv_values[1] = datumCopy(col_b->bv_values[1],
303  attr->attbyval, attr->attlen);
304  }
305 
306  PG_RETURN_VOID();
307 }
308 
309 /*
310  * Cache and return the procedure for the given strategy.
311  *
312  * Note: this function mirrors inclusion_get_strategy_procinfo; see notes
313  * there. If changes are made here, see that function too.
314  */
315 static FmgrInfo *
317  uint16 strategynum)
318 {
319  MinmaxOpaque *opaque;
320 
321  Assert(strategynum >= 1 &&
322  strategynum <= BTMaxStrategyNumber);
323 
324  opaque = (MinmaxOpaque *) bdesc->bd_info[attno - 1]->oi_opaque;
325 
326  /*
327  * We cache the procedures for the previous subtype in the opaque struct,
328  * to avoid repetitive syscache lookups. If the subtype changed,
329  * invalidate all the cached entries.
330  */
331  if (opaque->cached_subtype != subtype)
332  {
333  uint16 i;
334 
335  for (i = 1; i <= BTMaxStrategyNumber; i++)
336  opaque->strategy_procinfos[i - 1].fn_oid = InvalidOid;
337  opaque->cached_subtype = subtype;
338  }
339 
340  if (opaque->strategy_procinfos[strategynum - 1].fn_oid == InvalidOid)
341  {
342  Form_pg_attribute attr;
343  HeapTuple tuple;
344  Oid opfamily,
345  oprid;
346  bool isNull;
347 
348  opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
349  attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
350  tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
351  ObjectIdGetDatum(attr->atttypid),
352  ObjectIdGetDatum(subtype),
353  Int16GetDatum(strategynum));
354 
355  if (!HeapTupleIsValid(tuple))
356  elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
357  strategynum, attr->atttypid, subtype, opfamily);
358 
360  Anum_pg_amop_amopopr, &isNull));
361  ReleaseSysCache(tuple);
362  Assert(!isNull && RegProcedureIsValid(oprid));
363 
364  fmgr_info_cxt(get_opcode(oprid),
365  &opaque->strategy_procinfos[strategynum - 1],
366  bdesc->bd_context);
367  }
368 
369  return &opaque->strategy_procinfos[strategynum - 1];
370 }
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:351
Oid sk_subtype
Definition: skey.h:69
Definition: fmgr.h:56
#define BTGreaterStrategyNumber
Definition: stratnum.h:33
Oid oprid(Operator op)
Definition: parse_oper.c:245
#define PG_GETARG_DATUM(n)
Definition: fmgr.h:263
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
#define DatumGetObjectId(X)
Definition: postgres.h:500
#define SizeofBrinOpcInfo(ncols)
Definition: brin_internal.h:38
FmgrInfo strategy_procinfos[BTMaxStrategyNumber]
Definition: brin_minmax.c:28
bool bv_allnulls
Definition: brin_tuple.h:28
#define Int16GetDatum(X)
Definition: postgres.h:451
Datum brin_minmax_union(PG_FUNCTION_ARGS)
Definition: brin_minmax.c:240
static struct @145 value
#define PG_GETARG_POINTER(n)
Definition: fmgr.h:271
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1150
unsigned int Oid
Definition: postgres_ext.h:31
#define PG_GET_COLLATION()
Definition: fmgr.h:193
#define BTLessEqualStrategyNumber
Definition: stratnum.h:30
unsigned short uint16
Definition: c.h:358
void pfree(void *pointer)
Definition: mcxt.c:1056
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
Relation bd_index
Definition: brin_internal.h:47
bool bv_hasnulls
Definition: brin_tuple.h:27
TypeCacheEntry * oi_typcache[FLEXIBLE_ARRAY_MEMBER]
Definition: brin_internal.h:34
StrategyNumber sk_strategy
Definition: skey.h:68
AttrNumber bv_attno
Definition: brin_tuple.h:26
uint16 oi_nstored
Definition: brin_internal.h:28
#define PG_GETARG_OID(n)
Definition: fmgr.h:270
ScanKeyData * ScanKey
Definition: skey.h:75
#define RegProcedureIsValid(p)
Definition: c.h:641
#define DatumGetBool(X)
Definition: postgres.h:393
TupleDesc bd_tupdesc
Definition: brin_internal.h:50
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:200
#define SK_SEARCHNOTNULL
Definition: skey.h:122
Oid * rd_opfamily
Definition: rel.h:158
HeapTuple SearchSysCache4(int cacheId, Datum key1, Datum key2, Datum key3, Datum key4)
Definition: syscache.c:1149
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:134
#define SK_ISNULL
Definition: skey.h:115
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:130
void * palloc0(Size size)
Definition: mcxt.c:980
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:349
uintptr_t Datum
Definition: postgres.h:367
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1164
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:343
void * oi_opaque
Definition: brin_internal.h:31
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1377
Datum brin_minmax_opcinfo(PG_FUNCTION_ARGS)
Definition: brin_minmax.c:36
Oid cached_subtype
Definition: brin_minmax.c:27
BrinOpcInfo * bd_info[FLEXIBLE_ARRAY_MEMBER]
Definition: brin_internal.h:59
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:322
#define InvalidOid
Definition: postgres_ext.h:36
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1092
Oid fn_oid
Definition: fmgr.h:59
#define PG_RETURN_VOID()
Definition: fmgr.h:339
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
int sk_flags
Definition: skey.h:66
#define Assert(condition)
Definition: c.h:733
#define newval
#define MAXALIGN(LEN)
Definition: c.h:686
#define DatumGetPointer(X)
Definition: postgres.h:549
MemoryContext bd_context
Definition: brin_internal.h:44
static FmgrInfo * minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype, uint16 strategynum)
Definition: brin_minmax.c:316
#define elog(elevel,...)
Definition: elog.h:228
int i
Datum brin_minmax_add_value(PG_FUNCTION_ARGS)
Definition: brin_minmax.c:65
#define PG_FUNCTION_ARGS
Definition: fmgr.h:188
#define BTMaxStrategyNumber
Definition: stratnum.h:35
Datum brin_minmax_consistent(PG_FUNCTION_ARGS)
Definition: brin_minmax.c:145
#define BTLessStrategyNumber
Definition: stratnum.h:29
Datum * bv_values
Definition: brin_tuple.h:29
struct MinmaxOpaque MinmaxOpaque
Datum sk_argument
Definition: skey.h:72
int16 AttrNumber
Definition: attnum.h:21
#define SK_SEARCHNULL
Definition: skey.h:121
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define BTGreaterEqualStrategyNumber
Definition: stratnum.h:32
AttrNumber sk_attno
Definition: skey.h:67