PostgreSQL Source Code  git master
spgutils.c File Reference
#include "postgres.h"
#include "access/amvalidate.h"
#include "access/htup_details.h"
#include "access/reloptions.h"
#include "access/spgist_private.h"
#include "access/toast_compression.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/pg_amop.h"
#include "commands/vacuum.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_coerce.h"
#include "storage/bufmgr.h"
#include "storage/indexfsm.h"
#include "utils/catcache.h"
#include "utils/fmgrprotos.h"
#include "utils/index_selfuncs.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
Include dependency graph for spgutils.c:

Go to the source code of this file.

Macros

#define GET_LUP(c, f)   (&(c)->lastUsedPages.cachedPage[((unsigned int) (f)) % SPGIST_CACHED_PAGES])
 

Functions

Datum spghandler (PG_FUNCTION_ARGS)
 
static Oid GetIndexInputType (Relation index, AttrNumber indexcol)
 
static void fillTypeDesc (SpGistTypeDesc *desc, Oid type)
 
SpGistCachespgGetCache (Relation index)
 
TupleDesc getSpGistTupleDesc (Relation index, SpGistTypeDesc *keyType)
 
void initSpGistState (SpGistState *state, Relation index)
 
Buffer SpGistNewBuffer (Relation index)
 
void SpGistUpdateMetaPage (Relation index)
 
static Buffer allocNewBuffer (Relation index, int flags)
 
Buffer SpGistGetBuffer (Relation index, int flags, int needSpace, bool *isNew)
 
void SpGistSetLastUsedPage (Relation index, Buffer buffer)
 
void SpGistInitPage (Page page, uint16 f)
 
void SpGistInitBuffer (Buffer b, uint16 f)
 
void SpGistInitMetapage (Page page)
 
byteaspgoptions (Datum reloptions, bool validate)
 
unsigned int SpGistGetInnerTypeSize (SpGistTypeDesc *att, Datum datum)
 
static void memcpyInnerDatum (void *target, SpGistTypeDesc *att, Datum datum)
 
Size SpGistGetLeafTupleSize (TupleDesc tupleDescriptor, const Datum *datums, const bool *isnulls)
 
SpGistLeafTuple spgFormLeafTuple (SpGistState *state, ItemPointer heapPtr, const Datum *datums, const bool *isnulls)
 
SpGistNodeTuple spgFormNodeTuple (SpGistState *state, Datum label, bool isnull)
 
SpGistInnerTuple spgFormInnerTuple (SpGistState *state, bool hasPrefix, Datum prefix, int nNodes, SpGistNodeTuple *nodes)
 
SpGistDeadTuple spgFormDeadTuple (SpGistState *state, int tupstate, BlockNumber blkno, OffsetNumber offnum)
 
void spgDeformLeafTuple (SpGistLeafTuple tup, TupleDesc tupleDescriptor, Datum *datums, bool *isnulls, bool keyColumnIsNull)
 
DatumspgExtractNodeLabels (SpGistState *state, SpGistInnerTuple innerTuple)
 
OffsetNumber SpGistPageAddNewItem (SpGistState *state, Page page, Item item, Size size, OffsetNumber *startOffset, bool errorOK)
 
bool spgproperty (Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull)
 

Macro Definition Documentation

◆ GET_LUP

#define GET_LUP (   c,
 
)    (&(c)->lastUsedPages.cachedPage[((unsigned int) (f)) % SPGIST_CACHED_PAGES])

Definition at line 482 of file spgutils.c.

Function Documentation

◆ allocNewBuffer()

static Buffer allocNewBuffer ( Relation  index,
int  flags 
)
static

Definition at line 505 of file spgutils.c.

506 {
507  SpGistCache *cache = spgGetCache(index);
508  uint16 pageflags = 0;
509 
510  if (GBUF_REQ_LEAF(flags))
511  pageflags |= SPGIST_LEAF;
512  if (GBUF_REQ_NULLS(flags))
513  pageflags |= SPGIST_NULLS;
514 
515  for (;;)
516  {
517  Buffer buffer;
518 
519  buffer = SpGistNewBuffer(index);
520  SpGistInitBuffer(buffer, pageflags);
521 
522  if (pageflags & SPGIST_LEAF)
523  {
524  /* Leaf pages have no parity concerns, so just use it */
525  return buffer;
526  }
527  else
528  {
529  BlockNumber blkno = BufferGetBlockNumber(buffer);
530  int blkFlags = GBUF_INNER_PARITY(blkno);
531 
532  if ((flags & GBUF_PARITY_MASK) == blkFlags)
533  {
534  /* Page has right parity, use it */
535  return buffer;
536  }
537  else
538  {
539  /* Page has wrong parity, record it in cache and try again */
540  if (pageflags & SPGIST_NULLS)
541  blkFlags |= GBUF_NULLS;
542  cache->lastUsedPages.cachedPage[blkFlags].blkno = blkno;
543  cache->lastUsedPages.cachedPage[blkFlags].freeSpace =
545  UnlockReleaseBuffer(buffer);
546  }
547  }
548  }
549 }
uint32 BlockNumber
Definition: block.h:31
int Buffer
Definition: buf.h:23
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:3736
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4953
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:400
Size PageGetExactFreeSpace(Page page)
Definition: bufpage.c:958
unsigned short uint16
Definition: c.h:505
#define GBUF_NULLS
#define GBUF_PARITY_MASK
#define SPGIST_NULLS
#define GBUF_REQ_NULLS(flags)
#define GBUF_INNER_PARITY(x)
#define GBUF_REQ_LEAF(flags)
#define SPGIST_LEAF
Buffer SpGistNewBuffer(Relation index)
Definition: spgutils.c:386
void SpGistInitBuffer(Buffer b, uint16 f)
Definition: spgutils.c:714
SpGistCache * spgGetCache(Relation index)
Definition: spgutils.c:182
SpGistLUPCache lastUsedPages
SpGistLastUsedPage cachedPage[SPGIST_CACHED_PAGES]
Definition: type.h:95

References SpGistLastUsedPage::blkno, BufferGetBlockNumber(), BufferGetPage(), SpGistLUPCache::cachedPage, SpGistLastUsedPage::freeSpace, GBUF_INNER_PARITY, GBUF_NULLS, GBUF_PARITY_MASK, GBUF_REQ_LEAF, GBUF_REQ_NULLS, SpGistCache::lastUsedPages, PageGetExactFreeSpace(), spgGetCache(), SPGIST_LEAF, SPGIST_NULLS, SpGistInitBuffer(), SpGistNewBuffer(), and UnlockReleaseBuffer().

Referenced by SpGistGetBuffer().

◆ fillTypeDesc()

static void fillTypeDesc ( SpGistTypeDesc desc,
Oid  type 
)
static

Definition at line 160 of file spgutils.c.

161 {
162  HeapTuple tp;
163  Form_pg_type typtup;
164 
165  desc->type = type;
166  tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type));
167  if (!HeapTupleIsValid(tp))
168  elog(ERROR, "cache lookup failed for type %u", type);
169  typtup = (Form_pg_type) GETSTRUCT(tp);
170  desc->attlen = typtup->typlen;
171  desc->attbyval = typtup->typbyval;
172  desc->attalign = typtup->typalign;
173  desc->attstorage = typtup->typstorage;
174  ReleaseSysCache(tp);
175 }
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
FormData_pg_type * Form_pg_type
Definition: pg_type.h:261
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:266
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:218
const char * type

References SpGistTypeDesc::attalign, SpGistTypeDesc::attbyval, SpGistTypeDesc::attlen, SpGistTypeDesc::attstorage, elog, ERROR, GETSTRUCT, HeapTupleIsValid, ObjectIdGetDatum(), ReleaseSysCache(), SearchSysCache1(), type, and SpGistTypeDesc::type.

Referenced by spgGetCache().

◆ GetIndexInputType()

static Oid GetIndexInputType ( Relation  index,
AttrNumber  indexcol 
)
static

Definition at line 115 of file spgutils.c.

116 {
117  Oid opcintype;
118  AttrNumber heapcol;
119  List *indexprs;
120  ListCell *indexpr_item;
121 
122  Assert(index->rd_index != NULL);
123  Assert(indexcol > 0 && indexcol <= index->rd_index->indnkeyatts);
124  opcintype = index->rd_opcintype[indexcol - 1];
125  if (!IsPolymorphicType(opcintype))
126  return opcintype;
127  heapcol = index->rd_index->indkey.values[indexcol - 1];
128  if (heapcol != 0) /* Simple index column? */
129  return getBaseType(get_atttype(index->rd_index->indrelid, heapcol));
130 
131  /*
132  * If the index expressions are already cached, skip calling
133  * RelationGetIndexExpressions, as it will make a copy which is overkill.
134  * We're not going to modify the trees, and we're not going to do anything
135  * that would invalidate the relcache entry before we're done.
136  */
137  if (index->rd_indexprs)
138  indexprs = index->rd_indexprs;
139  else
141  indexpr_item = list_head(indexprs);
142  for (int i = 1; i <= index->rd_index->indnkeyatts; i++)
143  {
144  if (index->rd_index->indkey.values[i - 1] == 0)
145  {
146  /* expression column */
147  if (indexpr_item == NULL)
148  elog(ERROR, "wrong number of index expressions");
149  if (i == indexcol)
150  return getBaseType(exprType((Node *) lfirst(indexpr_item)));
151  indexpr_item = lnext(indexprs, indexpr_item);
152  }
153  }
154  elog(ERROR, "wrong number of index expressions");
155  return InvalidOid; /* keep compiler quiet */
156 }
int16 AttrNumber
Definition: attnum.h:21
#define Assert(condition)
Definition: c.h:858
int i
Definition: isn.c:73
Oid getBaseType(Oid typid)
Definition: lsyscache.c:2521
Oid get_atttype(Oid relid, AttrNumber attnum)
Definition: lsyscache.c:913
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
#define lfirst(lc)
Definition: pg_list.h:172
static ListCell * list_head(const List *l)
Definition: pg_list.h:128
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:343
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
List * RelationGetIndexExpressions(Relation relation)
Definition: relcache.c:5038
Definition: pg_list.h:54
Definition: nodes.h:129

References Assert, elog, ERROR, exprType(), get_atttype(), getBaseType(), i, InvalidOid, lfirst, list_head(), lnext(), and RelationGetIndexExpressions().

Referenced by spgGetCache().

◆ getSpGistTupleDesc()

TupleDesc getSpGistTupleDesc ( Relation  index,
SpGistTypeDesc keyType 
)

Definition at line 309 of file spgutils.c.

310 {
311  TupleDesc outTupDesc;
312  Form_pg_attribute att;
313 
314  if (keyType->type ==
316  outTupDesc = RelationGetDescr(index);
317  else
318  {
320  att = TupleDescAttr(outTupDesc, spgKeyColumn);
321  /* It's sufficient to update the type-dependent fields of the column */
322  att->atttypid = keyType->type;
323  att->atttypmod = -1;
324  att->attlen = keyType->attlen;
325  att->attbyval = keyType->attbyval;
326  att->attalign = keyType->attalign;
327  att->attstorage = keyType->attstorage;
328  /* We shouldn't need to bother with making these valid: */
329  att->attcompression = InvalidCompressionMethod;
330  att->attcollation = InvalidOid;
331  /* In case we changed typlen, we'd better reset following offsets */
332  for (int i = spgFirstIncludeColumn; i < outTupDesc->natts; i++)
333  TupleDescAttr(outTupDesc, i)->attcacheoff = -1;
334  }
335  return outTupDesc;
336 }
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
#define RelationGetDescr(relation)
Definition: rel.h:531
#define spgFirstIncludeColumn
#define spgKeyColumn
#define InvalidCompressionMethod
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:133
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92

References SpGistTypeDesc::attalign, SpGistTypeDesc::attbyval, SpGistTypeDesc::attlen, SpGistTypeDesc::attstorage, CreateTupleDescCopy(), i, InvalidCompressionMethod, InvalidOid, TupleDescData::natts, RelationGetDescr, spgFirstIncludeColumn, spgKeyColumn, TupleDescAttr, and SpGistTypeDesc::type.

Referenced by initSpGistState(), and spgbeginscan().

◆ initSpGistState()

void initSpGistState ( SpGistState state,
Relation  index 
)

Definition at line 340 of file spgutils.c.

341 {
342  SpGistCache *cache;
343 
344  state->index = index;
345 
346  /* Get cached static information about index */
347  cache = spgGetCache(index);
348 
349  state->config = cache->config;
350  state->attType = cache->attType;
351  state->attLeafType = cache->attLeafType;
352  state->attPrefixType = cache->attPrefixType;
353  state->attLabelType = cache->attLabelType;
354 
355  /* Ensure we have a valid descriptor for leaf tuples */
356  state->leafTupDesc = getSpGistTupleDesc(state->index, &state->attLeafType);
357 
358  /* Make workspace for constructing dead tuples */
359  state->deadTupleStorage = palloc0(SGDTSIZE);
360 
361  /*
362  * Set horizon XID to use in redirection tuples. Use our own XID if we
363  * have one, else use InvalidTransactionId. The latter case can happen in
364  * VACUUM or REINDEX CONCURRENTLY, and in neither case would it be okay to
365  * force an XID to be assigned. VACUUM won't create any redirection
366  * tuples anyway, but REINDEX CONCURRENTLY can. Fortunately, REINDEX
367  * CONCURRENTLY doesn't mark the index valid until the end, so there could
368  * never be any concurrent scans "in flight" to a redirection tuple it has
369  * inserted. And it locks out VACUUM until the end, too. So it's okay
370  * for VACUUM to immediately expire a redirection tuple that contains an
371  * invalid xid.
372  */
373  state->redirectXid = GetTopTransactionIdIfAny();
374 
375  /* Assume we're not in an index build (spgbuild will override) */
376  state->isBuild = false;
377 }
void * palloc0(Size size)
Definition: mcxt.c:1347
#define SGDTSIZE
TupleDesc getSpGistTupleDesc(Relation index, SpGistTypeDesc *keyType)
Definition: spgutils.c:309
SpGistTypeDesc attPrefixType
SpGistTypeDesc attLeafType
SpGistTypeDesc attType
spgConfigOut config
SpGistTypeDesc attLabelType
Definition: regguts.h:323
TransactionId GetTopTransactionIdIfAny(void)
Definition: xact.c:439

References SpGistCache::attLabelType, SpGistCache::attLeafType, SpGistCache::attPrefixType, SpGistCache::attType, SpGistCache::config, getSpGistTupleDesc(), GetTopTransactionIdIfAny(), palloc0(), SGDTSIZE, and spgGetCache().

Referenced by spgbeginscan(), spgbuild(), spginsert(), and spgvacuumscan().

◆ memcpyInnerDatum()

static void memcpyInnerDatum ( void *  target,
SpGistTypeDesc att,
Datum  datum 
)
static

Definition at line 789 of file spgutils.c.

790 {
791  unsigned int size;
792 
793  if (att->attbyval)
794  {
795  memcpy(target, &datum, sizeof(Datum));
796  }
797  else
798  {
799  size = (att->attlen > 0) ? att->attlen : VARSIZE_ANY(datum);
800  memcpy(target, DatumGetPointer(datum), size);
801  }
802 }
uintptr_t Datum
Definition: postgres.h:64
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
static pg_noinline void Size size
Definition: slab.c:607
#define VARSIZE_ANY(PTR)
Definition: varatt.h:311

References SpGistTypeDesc::attbyval, SpGistTypeDesc::attlen, DatumGetPointer(), size, and VARSIZE_ANY.

Referenced by spgFormInnerTuple(), and spgFormNodeTuple().

◆ spgDeformLeafTuple()

void spgDeformLeafTuple ( SpGistLeafTuple  tup,
TupleDesc  tupleDescriptor,
Datum datums,
bool isnulls,
bool  keyColumnIsNull 
)

Definition at line 1107 of file spgutils.c.

1109 {
1110  bool hasNullsMask = SGLT_GET_HASNULLMASK(tup);
1111  char *tp; /* ptr to tuple data */
1112  bits8 *bp; /* ptr to null bitmap in tuple */
1113 
1114  if (keyColumnIsNull && tupleDescriptor->natts == 1)
1115  {
1116  /*
1117  * Trivial case: there is only the key attribute and we're in a nulls
1118  * tree. The hasNullsMask bit in the tuple header should not be set
1119  * (and thus we can't use index_deform_tuple_internal), but
1120  * nonetheless the result is NULL.
1121  *
1122  * Note: currently this is dead code, because noplace calls this when
1123  * there is only the key attribute. But we should cover the case.
1124  */
1125  Assert(!hasNullsMask);
1126 
1127  datums[spgKeyColumn] = (Datum) 0;
1128  isnulls[spgKeyColumn] = true;
1129  return;
1130  }
1131 
1132  tp = (char *) tup + SGLTHDRSZ(hasNullsMask);
1133  bp = (bits8 *) ((char *) tup + sizeof(SpGistLeafTupleData));
1134 
1135  index_deform_tuple_internal(tupleDescriptor,
1136  datums, isnulls,
1137  tp, bp, hasNullsMask);
1138 
1139  /*
1140  * Key column isnull value from the tuple should be consistent with
1141  * keyColumnIsNull flag from the caller.
1142  */
1143  Assert(keyColumnIsNull == isnulls[spgKeyColumn]);
1144 }
uint8 bits8
Definition: c.h:513
void index_deform_tuple_internal(TupleDesc tupleDescriptor, Datum *values, bool *isnull, char *tp, bits8 *bp, int hasnulls)
Definition: indextuple.c:479
#define SGLT_GET_HASNULLMASK(spgLeafTuple)
#define SGLTHDRSZ(hasnulls)
struct SpGistLeafTupleData SpGistLeafTupleData

References Assert, index_deform_tuple_internal(), TupleDescData::natts, SGLT_GET_HASNULLMASK, SGLTHDRSZ, and spgKeyColumn.

Referenced by doPickSplit(), and storeGettuple().

◆ spgExtractNodeLabels()

Datum* spgExtractNodeLabels ( SpGistState state,
SpGistInnerTuple  innerTuple 
)

Definition at line 1152 of file spgutils.c.

1153 {
1154  Datum *nodeLabels;
1155  int i;
1156  SpGistNodeTuple node;
1157 
1158  /* Either all the labels must be NULL, or none. */
1159  node = SGITNODEPTR(innerTuple);
1160  if (IndexTupleHasNulls(node))
1161  {
1162  SGITITERATE(innerTuple, i, node)
1163  {
1164  if (!IndexTupleHasNulls(node))
1165  elog(ERROR, "some but not all node labels are null in SPGiST inner tuple");
1166  }
1167  /* They're all null, so just return NULL */
1168  return NULL;
1169  }
1170  else
1171  {
1172  nodeLabels = (Datum *) palloc(sizeof(Datum) * innerTuple->nNodes);
1173  SGITITERATE(innerTuple, i, node)
1174  {
1175  if (IndexTupleHasNulls(node))
1176  elog(ERROR, "some but not all node labels are null in SPGiST inner tuple");
1177  nodeLabels[i] = SGNTDATUM(node, state);
1178  }
1179  return nodeLabels;
1180  }
1181 }
#define IndexTupleHasNulls(itup)
Definition: itup.h:71
void * palloc(Size size)
Definition: mcxt.c:1317
#define SGITITERATE(x, i, nt)
#define SGNTDATUM(x, s)
#define SGITNODEPTR(x)

References elog, ERROR, i, IndexTupleHasNulls, SpGistInnerTupleData::nNodes, palloc(), SGITITERATE, SGITNODEPTR, and SGNTDATUM.

Referenced by spgdoinsert(), and spgInitInnerConsistentIn().

◆ spgFormDeadTuple()

SpGistDeadTuple spgFormDeadTuple ( SpGistState state,
int  tupstate,
BlockNumber  blkno,
OffsetNumber  offnum 
)

Definition at line 1077 of file spgutils.c.

1079 {
1080  SpGistDeadTuple tuple = (SpGistDeadTuple) state->deadTupleStorage;
1081 
1082  tuple->tupstate = tupstate;
1083  tuple->size = SGDTSIZE;
1085 
1086  if (tupstate == SPGIST_REDIRECT)
1087  {
1088  ItemPointerSet(&tuple->pointer, blkno, offnum);
1089  tuple->xid = state->redirectXid;
1090  }
1091  else
1092  {
1093  ItemPointerSetInvalid(&tuple->pointer);
1094  tuple->xid = InvalidTransactionId;
1095  }
1096 
1097  return tuple;
1098 }
static void ItemPointerSet(ItemPointerData *pointer, BlockNumber blockNumber, OffsetNumber offNum)
Definition: itemptr.h:135
static void ItemPointerSetInvalid(ItemPointerData *pointer)
Definition: itemptr.h:184
#define InvalidOffsetNumber
Definition: off.h:26
SpGistDeadTupleData * SpGistDeadTuple
#define SPGIST_REDIRECT
#define SGLT_SET_NEXTOFFSET(spgLeafTuple, offsetNumber)
unsigned int tupstate
ItemPointerData pointer
#define InvalidTransactionId
Definition: transam.h:31

References InvalidOffsetNumber, InvalidTransactionId, ItemPointerSet(), ItemPointerSetInvalid(), SpGistDeadTupleData::pointer, SGDTSIZE, SGLT_SET_NEXTOFFSET, SpGistDeadTupleData::size, SPGIST_REDIRECT, SpGistDeadTupleData::tupstate, and SpGistDeadTupleData::xid.

Referenced by spgAddNodeAction(), spgPageIndexMultiDelete(), and spgRedoAddNode().

◆ spgFormInnerTuple()

SpGistInnerTuple spgFormInnerTuple ( SpGistState state,
bool  hasPrefix,
Datum  prefix,
int  nNodes,
SpGistNodeTuple nodes 
)

Definition at line 994 of file spgutils.c.

996 {
997  SpGistInnerTuple tup;
998  unsigned int size;
999  unsigned int prefixSize;
1000  int i;
1001  char *ptr;
1002 
1003  /* Compute size needed */
1004  if (hasPrefix)
1005  prefixSize = SpGistGetInnerTypeSize(&state->attPrefixType, prefix);
1006  else
1007  prefixSize = 0;
1008 
1009  size = SGITHDRSZ + prefixSize;
1010 
1011  /* Note: we rely on node tuple sizes to be maxaligned already */
1012  for (i = 0; i < nNodes; i++)
1013  size += IndexTupleSize(nodes[i]);
1014 
1015  /*
1016  * Ensure that we can replace the tuple with a dead tuple later. This
1017  * test is unnecessary given current tuple layouts, but let's be safe.
1018  */
1019  if (size < SGDTSIZE)
1020  size = SGDTSIZE;
1021 
1022  /*
1023  * Inner tuple should be small enough to fit on a page
1024  */
1025  if (size > SPGIST_PAGE_CAPACITY - sizeof(ItemIdData))
1026  ereport(ERROR,
1027  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1028  errmsg("SP-GiST inner tuple size %zu exceeds maximum %zu",
1029  (Size) size,
1030  SPGIST_PAGE_CAPACITY - sizeof(ItemIdData)),
1031  errhint("Values larger than a buffer page cannot be indexed.")));
1032 
1033  /*
1034  * Check for overflow of header fields --- probably can't fail if the
1035  * above succeeded, but let's be paranoid
1036  */
1037  if (size > SGITMAXSIZE ||
1038  prefixSize > SGITMAXPREFIXSIZE ||
1039  nNodes > SGITMAXNNODES)
1040  elog(ERROR, "SPGiST inner tuple header field is too small");
1041 
1042  /* OK, form the tuple */
1043  tup = (SpGistInnerTuple) palloc0(size);
1044 
1045  tup->nNodes = nNodes;
1046  tup->prefixSize = prefixSize;
1047  tup->size = size;
1048 
1049  if (hasPrefix)
1050  memcpyInnerDatum(SGITDATAPTR(tup), &state->attPrefixType, prefix);
1051 
1052  ptr = (char *) SGITNODEPTR(tup);
1053 
1054  for (i = 0; i < nNodes; i++)
1055  {
1056  SpGistNodeTuple node = nodes[i];
1057 
1058  memcpy(ptr, node, IndexTupleSize(node));
1059  ptr += IndexTupleSize(node);
1060  }
1061 
1062  return tup;
1063 }
size_t Size
Definition: c.h:605
int errhint(const char *fmt,...)
Definition: elog.c:1317
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ereport(elevel,...)
Definition: elog.h:149
#define IndexTupleSize(itup)
Definition: itup.h:70
SpGistInnerTupleData * SpGistInnerTuple
#define SGITDATAPTR(x)
#define SGITMAXSIZE
#define SGITMAXPREFIXSIZE
#define SGITHDRSZ
#define SPGIST_PAGE_CAPACITY
#define SGITMAXNNODES
static void memcpyInnerDatum(void *target, SpGistTypeDesc *att, Datum datum)
Definition: spgutils.c:789
unsigned int SpGistGetInnerTypeSize(SpGistTypeDesc *att, Datum datum)
Definition: spgutils.c:771
unsigned int prefixSize

References elog, ereport, errcode(), errhint(), errmsg(), ERROR, i, IndexTupleSize, memcpyInnerDatum(), SpGistInnerTupleData::nNodes, palloc0(), SpGistInnerTupleData::prefixSize, SGDTSIZE, SGITDATAPTR, SGITHDRSZ, SGITMAXNNODES, SGITMAXPREFIXSIZE, SGITMAXSIZE, SGITNODEPTR, size, SpGistInnerTupleData::size, SPGIST_PAGE_CAPACITY, and SpGistGetInnerTypeSize().

Referenced by addNode(), doPickSplit(), and spgSplitNodeAction().

◆ spgFormLeafTuple()

SpGistLeafTuple spgFormLeafTuple ( SpGistState state,
ItemPointer  heapPtr,
const Datum datums,
const bool isnulls 
)

Definition at line 863 of file spgutils.c.

865 {
866  SpGistLeafTuple tup;
867  TupleDesc tupleDescriptor = state->leafTupDesc;
868  Size size;
869  Size hoff;
870  Size data_size;
871  bool needs_null_mask = false;
872  int natts = tupleDescriptor->natts;
873  char *tp; /* ptr to tuple data */
874  uint16 tupmask = 0; /* unused heap_fill_tuple output */
875 
876  /*
877  * Decide whether we need a nulls bitmask.
878  *
879  * If there is only a key attribute (natts == 1), never use a bitmask, for
880  * compatibility with the pre-v14 layout of leaf tuples. Otherwise, we
881  * need one if any attribute is null.
882  */
883  if (natts > 1)
884  {
885  for (int i = 0; i < natts; i++)
886  {
887  if (isnulls[i])
888  {
889  needs_null_mask = true;
890  break;
891  }
892  }
893  }
894 
895  /*
896  * Calculate size of the data part; same as for heap tuples.
897  */
898  data_size = heap_compute_data_size(tupleDescriptor, datums, isnulls);
899 
900  /*
901  * Compute total size.
902  */
903  hoff = SGLTHDRSZ(needs_null_mask);
904  size = hoff + data_size;
905  size = MAXALIGN(size);
906 
907  /*
908  * Ensure that we can replace the tuple with a dead tuple later. This test
909  * is unnecessary when there are any non-null attributes, but be safe.
910  */
911  if (size < SGDTSIZE)
912  size = SGDTSIZE;
913 
914  /* OK, form the tuple */
915  tup = (SpGistLeafTuple) palloc0(size);
916 
917  tup->size = size;
919  tup->heapPtr = *heapPtr;
920 
921  tp = (char *) tup + hoff;
922 
923  if (needs_null_mask)
924  {
925  bits8 *bp; /* ptr to null bitmap in tuple */
926 
927  /* Set nullmask presence bit in SpGistLeafTuple header */
928  SGLT_SET_HASNULLMASK(tup, true);
929  /* Fill the data area and null mask */
930  bp = (bits8 *) ((char *) tup + sizeof(SpGistLeafTupleData));
931  heap_fill_tuple(tupleDescriptor, datums, isnulls, tp, data_size,
932  &tupmask, bp);
933  }
934  else if (natts > 1 || !isnulls[spgKeyColumn])
935  {
936  /* Fill data area only */
937  heap_fill_tuple(tupleDescriptor, datums, isnulls, tp, data_size,
938  &tupmask, (bits8 *) NULL);
939  }
940  /* otherwise we have no data, nor a bitmap, to fill */
941 
942  return tup;
943 }
#define MAXALIGN(LEN)
Definition: c.h:811
Size heap_compute_data_size(TupleDesc tupleDesc, const Datum *values, const bool *isnull)
Definition: heaptuple.c:215
void heap_fill_tuple(TupleDesc tupleDesc, const Datum *values, const bool *isnull, char *data, Size data_size, uint16 *infomask, bits8 *bit)
Definition: heaptuple.c:400
#define SGLT_SET_HASNULLMASK(spgLeafTuple, hasnulls)
struct SpGistLeafTupleData * SpGistLeafTuple
ItemPointerData heapPtr

References heap_compute_data_size(), heap_fill_tuple(), SpGistLeafTupleData::heapPtr, i, InvalidOffsetNumber, MAXALIGN, TupleDescData::natts, palloc0(), SGDTSIZE, SGLT_SET_HASNULLMASK, SGLT_SET_NEXTOFFSET, SGLTHDRSZ, size, SpGistLeafTupleData::size, and spgKeyColumn.

Referenced by doPickSplit(), and spgdoinsert().

◆ spgFormNodeTuple()

SpGistNodeTuple spgFormNodeTuple ( SpGistState state,
Datum  label,
bool  isnull 
)

Definition at line 952 of file spgutils.c.

953 {
954  SpGistNodeTuple tup;
955  unsigned int size;
956  unsigned short infomask = 0;
957 
958  /* compute space needed (note result is already maxaligned) */
959  size = SGNTHDRSZ;
960  if (!isnull)
961  size += SpGistGetInnerTypeSize(&state->attLabelType, label);
962 
963  /*
964  * Here we make sure that the size will fit in the field reserved for it
965  * in t_info.
966  */
967  if ((size & INDEX_SIZE_MASK) != size)
968  ereport(ERROR,
969  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
970  errmsg("index row requires %zu bytes, maximum size is %zu",
971  (Size) size, (Size) INDEX_SIZE_MASK)));
972 
973  tup = (SpGistNodeTuple) palloc0(size);
974 
975  if (isnull)
976  infomask |= INDEX_NULL_MASK;
977  /* we don't bother setting the INDEX_VAR_MASK bit */
978  infomask |= size;
979  tup->t_info = infomask;
980 
981  /* The TID field will be filled in later */
983 
984  if (!isnull)
985  memcpyInnerDatum(SGNTDATAPTR(tup), &state->attLabelType, label);
986 
987  return tup;
988 }
#define INDEX_NULL_MASK
Definition: itup.h:68
#define INDEX_SIZE_MASK
Definition: itup.h:65
static char * label
#define SGNTDATAPTR(x)
SpGistNodeTupleData * SpGistNodeTuple
#define SGNTHDRSZ
ItemPointerData t_tid
Definition: itup.h:37
unsigned short t_info
Definition: itup.h:49

References ereport, errcode(), errmsg(), ERROR, INDEX_NULL_MASK, INDEX_SIZE_MASK, ItemPointerSetInvalid(), label, memcpyInnerDatum(), palloc0(), SGNTDATAPTR, SGNTHDRSZ, size, SpGistGetInnerTypeSize(), IndexTupleData::t_info, and IndexTupleData::t_tid.

Referenced by addNode(), doPickSplit(), and spgSplitNodeAction().

◆ spgGetCache()

SpGistCache* spgGetCache ( Relation  index)

Definition at line 182 of file spgutils.c.

183 {
184  SpGistCache *cache;
185 
186  if (index->rd_amcache == NULL)
187  {
188  Oid atttype;
189  spgConfigIn in;
190  FmgrInfo *procinfo;
191 
192  cache = MemoryContextAllocZero(index->rd_indexcxt,
193  sizeof(SpGistCache));
194 
195  /* SPGiST must have one key column and can also have INCLUDE columns */
198 
199  /*
200  * Get the actual (well, nominal) data type of the key column. We
201  * pass this to the opclass config function so that polymorphic
202  * opclasses are possible.
203  */
204  atttype = GetIndexInputType(index, spgKeyColumn + 1);
205 
206  /* Call the config function to get config info for the opclass */
207  in.attType = atttype;
208 
210  FunctionCall2Coll(procinfo,
211  index->rd_indcollation[spgKeyColumn],
212  PointerGetDatum(&in),
213  PointerGetDatum(&cache->config));
214 
215  /*
216  * If leafType isn't specified, use the declared index column type,
217  * which index.c will have derived from the opclass's opcintype.
218  * (Although we now make spgvalidate.c warn if these aren't the same,
219  * old user-defined opclasses may not set the STORAGE parameter
220  * correctly, so believe leafType if it's given.)
221  */
222  if (!OidIsValid(cache->config.leafType))
223  {
224  cache->config.leafType =
226 
227  /*
228  * If index column type is binary-coercible to atttype (for
229  * example, it's a domain over atttype), treat it as plain atttype
230  * to avoid thinking we need to compress.
231  */
232  if (cache->config.leafType != atttype &&
233  IsBinaryCoercible(cache->config.leafType, atttype))
234  cache->config.leafType = atttype;
235  }
236 
237  /* Get the information we need about each relevant datatype */
238  fillTypeDesc(&cache->attType, atttype);
239 
240  if (cache->config.leafType != atttype)
241  {
243  ereport(ERROR,
244  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
245  errmsg("compress method must be defined when leaf type is different from input type")));
246 
247  fillTypeDesc(&cache->attLeafType, cache->config.leafType);
248  }
249  else
250  {
251  /* Save lookups in this common case */
252  cache->attLeafType = cache->attType;
253  }
254 
255  fillTypeDesc(&cache->attPrefixType, cache->config.prefixType);
256  fillTypeDesc(&cache->attLabelType, cache->config.labelType);
257 
258  /*
259  * Finally, if it's a real index (not a partitioned one), get the
260  * lastUsedPages data from the metapage
261  */
262  if (index->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
263  {
264  Buffer metabuffer;
265  SpGistMetaPageData *metadata;
266 
267  metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO);
268  LockBuffer(metabuffer, BUFFER_LOCK_SHARE);
269 
270  metadata = SpGistPageGetMeta(BufferGetPage(metabuffer));
271 
272  if (metadata->magicNumber != SPGIST_MAGIC_NUMBER)
273  elog(ERROR, "index \"%s\" is not an SP-GiST index",
275 
276  cache->lastUsedPages = metadata->lastUsedPages;
277 
278  UnlockReleaseBuffer(metabuffer);
279  }
280 
281  index->rd_amcache = (void *) cache;
282  }
283  else
284  {
285  /* assume it's up to date */
286  cache = (SpGistCache *) index->rd_amcache;
287  }
288 
289  return cache;
290 }
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:5171
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:773
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:190
#define OidIsValid(objectId)
Definition: c.h:775
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1149
FmgrInfo * index_getprocinfo(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:860
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:826
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1215
bool IsBinaryCoercible(Oid srctype, Oid targettype)
#define INDEX_MAX_KEYS
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
#define RelationGetRelationName(relation)
Definition: rel.h:539
#define IndexRelationGetNumberOfAttributes(relation)
Definition: rel.h:517
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:524
#define SPGIST_COMPRESS_PROC
Definition: spgist.h:28
#define SPGIST_CONFIG_PROC
Definition: spgist.h:23
#define SpGistPageGetMeta(p)
#define SPGIST_METAPAGE_BLKNO
#define SPGIST_MAGIC_NUMBER
static Oid GetIndexInputType(Relation index, AttrNumber indexcol)
Definition: spgutils.c:115
static void fillTypeDesc(SpGistTypeDesc *desc, Oid type)
Definition: spgutils.c:160
Definition: fmgr.h:57
SpGistLUPCache lastUsedPages
Oid attType
Definition: spgist.h:38
Oid leafType
Definition: spgist.h:45
Oid labelType
Definition: spgist.h:44
Oid prefixType
Definition: spgist.h:43

References Assert, SpGistCache::attLabelType, SpGistCache::attLeafType, SpGistCache::attPrefixType, spgConfigIn::attType, SpGistCache::attType, BUFFER_LOCK_SHARE, BufferGetPage(), SpGistCache::config, elog, ereport, errcode(), errmsg(), ERROR, fillTypeDesc(), FunctionCall2Coll(), GetIndexInputType(), index_getprocid(), index_getprocinfo(), INDEX_MAX_KEYS, IndexRelationGetNumberOfAttributes, IndexRelationGetNumberOfKeyAttributes, IsBinaryCoercible(), spgConfigOut::labelType, SpGistMetaPageData::lastUsedPages, SpGistCache::lastUsedPages, spgConfigOut::leafType, LockBuffer(), SpGistMetaPageData::magicNumber, MemoryContextAllocZero(), OidIsValid, PointerGetDatum(), spgConfigOut::prefixType, ReadBuffer(), RelationGetDescr, RelationGetRelationName, SPGIST_COMPRESS_PROC, SPGIST_CONFIG_PROC, SPGIST_MAGIC_NUMBER, SPGIST_METAPAGE_BLKNO, SpGistPageGetMeta, spgKeyColumn, TupleDescAttr, and UnlockReleaseBuffer().

Referenced by allocNewBuffer(), initSpGistState(), spgcanreturn(), SpGistGetBuffer(), and SpGistSetLastUsedPage().

◆ spghandler()

Datum spghandler ( PG_FUNCTION_ARGS  )

Definition at line 44 of file spgutils.c.

45 {
47 
48  amroutine->amstrategies = 0;
49  amroutine->amsupport = SPGISTNProc;
51  amroutine->amcanorder = false;
52  amroutine->amcanorderbyop = true;
53  amroutine->amcanbackward = false;
54  amroutine->amcanunique = false;
55  amroutine->amcanmulticol = false;
56  amroutine->amoptionalkey = true;
57  amroutine->amsearcharray = false;
58  amroutine->amsearchnulls = true;
59  amroutine->amstorage = true;
60  amroutine->amclusterable = false;
61  amroutine->ampredlocks = false;
62  amroutine->amcanparallel = false;
63  amroutine->amcanbuildparallel = false;
64  amroutine->amcaninclude = true;
65  amroutine->amusemaintenanceworkmem = false;
66  amroutine->amsummarizing = false;
67  amroutine->amparallelvacuumoptions =
69  amroutine->amkeytype = InvalidOid;
70 
71  amroutine->ambuild = spgbuild;
72  amroutine->ambuildempty = spgbuildempty;
73  amroutine->aminsert = spginsert;
74  amroutine->aminsertcleanup = NULL;
75  amroutine->ambulkdelete = spgbulkdelete;
76  amroutine->amvacuumcleanup = spgvacuumcleanup;
77  amroutine->amcanreturn = spgcanreturn;
78  amroutine->amcostestimate = spgcostestimate;
79  amroutine->amoptions = spgoptions;
80  amroutine->amproperty = spgproperty;
81  amroutine->ambuildphasename = NULL;
82  amroutine->amvalidate = spgvalidate;
83  amroutine->amadjustmembers = spgadjustmembers;
84  amroutine->ambeginscan = spgbeginscan;
85  amroutine->amrescan = spgrescan;
86  amroutine->amgettuple = spggettuple;
87  amroutine->amgetbitmap = spggetbitmap;
88  amroutine->amendscan = spgendscan;
89  amroutine->ammarkpos = NULL;
90  amroutine->amrestrpos = NULL;
91  amroutine->amestimateparallelscan = NULL;
92  amroutine->aminitparallelscan = NULL;
93  amroutine->amparallelrescan = NULL;
94 
95  PG_RETURN_POINTER(amroutine);
96 }
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:361
#define makeNode(_type_)
Definition: nodes.h:155
void spgcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation, double *indexPages)
Definition: selfuncs.c:7228
bool spginsert(Relation index, Datum *values, bool *isnull, ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique, bool indexUnchanged, IndexInfo *indexInfo)
Definition: spginsert.c:183
void spgbuildempty(Relation index)
Definition: spginsert.c:154
IndexBuildResult * spgbuild(Relation heap, Relation index, IndexInfo *indexInfo)
Definition: spginsert.c:73
#define SPGIST_OPTIONS_PROC
Definition: spgist.h:29
#define SPGISTNProc
Definition: spgist.h:31
IndexScanDesc spgbeginscan(Relation rel, int keysz, int orderbysz)
Definition: spgscan.c:304
bool spgcanreturn(Relation index, int attno)
Definition: spgscan.c:1083
bool spggettuple(IndexScanDesc scan, ScanDirection dir)
Definition: spgscan.c:1026
void spgendscan(IndexScanDesc scan)
Definition: spgscan.c:429
void spgrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys, ScanKey orderbys, int norderbys)
Definition: spgscan.c:380
int64 spggetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
Definition: spgscan.c:942
bytea * spgoptions(Datum reloptions, bool validate)
Definition: spgutils.c:751
bool spgproperty(Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull)
Definition: spgutils.c:1290
IndexBulkDeleteResult * spgvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
Definition: spgvacuum.c:947
IndexBulkDeleteResult * spgbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition: spgvacuum.c:916
bool spgvalidate(Oid opclassoid)
Definition: spgvalidate.c:39
void spgadjustmembers(Oid opfamilyoid, Oid opclassoid, List *operators, List *functions)
Definition: spgvalidate.c:332
ambuildphasename_function ambuildphasename
Definition: amapi.h:277
ambuildempty_function ambuildempty
Definition: amapi.h:268
amvacuumcleanup_function amvacuumcleanup
Definition: amapi.h:272
bool amclusterable
Definition: amapi.h:242
amoptions_function amoptions
Definition: amapi.h:275
amestimateparallelscan_function amestimateparallelscan
Definition: amapi.h:289
amrestrpos_function amrestrpos
Definition: amapi.h:286
aminsert_function aminsert
Definition: amapi.h:269
amendscan_function amendscan
Definition: amapi.h:284
uint16 amoptsprocnum
Definition: amapi.h:222
amparallelrescan_function amparallelrescan
Definition: amapi.h:291
Oid amkeytype
Definition: amapi.h:258
bool ampredlocks
Definition: amapi.h:244
uint16 amsupport
Definition: amapi.h:220
amcostestimate_function amcostestimate
Definition: amapi.h:274
bool amcanorderbyop
Definition: amapi.h:226
amadjustmembers_function amadjustmembers
Definition: amapi.h:279
ambuild_function ambuild
Definition: amapi.h:267
bool amstorage
Definition: amapi.h:240
uint16 amstrategies
Definition: amapi.h:218
bool amoptionalkey
Definition: amapi.h:234
amgettuple_function amgettuple
Definition: amapi.h:282
amcanreturn_function amcanreturn
Definition: amapi.h:273
bool amcanunique
Definition: amapi.h:230
amgetbitmap_function amgetbitmap
Definition: amapi.h:283
amproperty_function amproperty
Definition: amapi.h:276
ambulkdelete_function ambulkdelete
Definition: amapi.h:271
bool amsearcharray
Definition: amapi.h:236
bool amsummarizing
Definition: amapi.h:254
amvalidate_function amvalidate
Definition: amapi.h:278
ammarkpos_function ammarkpos
Definition: amapi.h:285
bool amcanmulticol
Definition: amapi.h:232
bool amusemaintenanceworkmem
Definition: amapi.h:252
ambeginscan_function ambeginscan
Definition: amapi.h:280
bool amcanparallel
Definition: amapi.h:246
amrescan_function amrescan
Definition: amapi.h:281
bool amcanorder
Definition: amapi.h:224
bool amcanbuildparallel
Definition: amapi.h:248
aminitparallelscan_function aminitparallelscan
Definition: amapi.h:290
uint8 amparallelvacuumoptions
Definition: amapi.h:256
aminsertcleanup_function aminsertcleanup
Definition: amapi.h:270
bool amcanbackward
Definition: amapi.h:228
bool amcaninclude
Definition: amapi.h:250
bool amsearchnulls
Definition: amapi.h:238
#define VACUUM_OPTION_PARALLEL_BULKDEL
Definition: vacuum.h:48
#define VACUUM_OPTION_PARALLEL_COND_CLEANUP
Definition: vacuum.h:55

References IndexAmRoutine::amadjustmembers, IndexAmRoutine::ambeginscan, IndexAmRoutine::ambuild, IndexAmRoutine::ambuildempty, IndexAmRoutine::ambuildphasename, IndexAmRoutine::ambulkdelete, IndexAmRoutine::amcanbackward, IndexAmRoutine::amcanbuildparallel, IndexAmRoutine::amcaninclude, IndexAmRoutine::amcanmulticol, IndexAmRoutine::amcanorder, IndexAmRoutine::amcanorderbyop, IndexAmRoutine::amcanparallel, IndexAmRoutine::amcanreturn, IndexAmRoutine::amcanunique, IndexAmRoutine::amclusterable, IndexAmRoutine::amcostestimate, IndexAmRoutine::amendscan, IndexAmRoutine::amestimateparallelscan, IndexAmRoutine::amgetbitmap, IndexAmRoutine::amgettuple, IndexAmRoutine::aminitparallelscan, IndexAmRoutine::aminsert, IndexAmRoutine::aminsertcleanup, IndexAmRoutine::amkeytype, IndexAmRoutine::ammarkpos, IndexAmRoutine::amoptionalkey, IndexAmRoutine::amoptions, IndexAmRoutine::amoptsprocnum, IndexAmRoutine::amparallelrescan, IndexAmRoutine::amparallelvacuumoptions, IndexAmRoutine::ampredlocks, IndexAmRoutine::amproperty, IndexAmRoutine::amrescan, IndexAmRoutine::amrestrpos, IndexAmRoutine::amsearcharray, IndexAmRoutine::amsearchnulls, IndexAmRoutine::amstorage, IndexAmRoutine::amstrategies, IndexAmRoutine::amsummarizing, IndexAmRoutine::amsupport, IndexAmRoutine::amusemaintenanceworkmem, IndexAmRoutine::amvacuumcleanup, IndexAmRoutine::amvalidate, InvalidOid, makeNode, PG_RETURN_POINTER, spgadjustmembers(), spgbeginscan(), spgbuild(), spgbuildempty(), spgbulkdelete(), spgcanreturn(), spgcostestimate(), spgendscan(), spggetbitmap(), spggettuple(), spginsert(), SPGIST_OPTIONS_PROC, SPGISTNProc, spgoptions(), spgproperty(), spgrescan(), spgvacuumcleanup(), spgvalidate(), VACUUM_OPTION_PARALLEL_BULKDEL, and VACUUM_OPTION_PARALLEL_COND_CLEANUP.

◆ SpGistGetBuffer()

Buffer SpGistGetBuffer ( Relation  index,
int  flags,
int  needSpace,
bool isNew 
)

Definition at line 561 of file spgutils.c.

562 {
563  SpGistCache *cache = spgGetCache(index);
564  SpGistLastUsedPage *lup;
565 
566  /* Bail out if even an empty page wouldn't meet the demand */
567  if (needSpace > SPGIST_PAGE_CAPACITY)
568  elog(ERROR, "desired SPGiST tuple size is too big");
569 
570  /*
571  * If possible, increase the space request to include relation's
572  * fillfactor. This ensures that when we add unrelated tuples to a page,
573  * we try to keep 100-fillfactor% available for adding tuples that are
574  * related to the ones already on it. But fillfactor mustn't cause an
575  * error for requests that would otherwise be legal.
576  */
577  needSpace += SpGistGetTargetPageFreeSpace(index);
578  needSpace = Min(needSpace, SPGIST_PAGE_CAPACITY);
579 
580  /* Get the cache entry for this flags setting */
581  lup = GET_LUP(cache, flags);
582 
583  /* If we have nothing cached, just turn it over to allocNewBuffer */
584  if (lup->blkno == InvalidBlockNumber)
585  {
586  *isNew = true;
587  return allocNewBuffer(index, flags);
588  }
589 
590  /* fixed pages should never be in cache */
592 
593  /* If cached freeSpace isn't enough, don't bother looking at the page */
594  if (lup->freeSpace >= needSpace)
595  {
596  Buffer buffer;
597  Page page;
598 
599  buffer = ReadBuffer(index, lup->blkno);
600 
601  if (!ConditionalLockBuffer(buffer))
602  {
603  /*
604  * buffer is locked by another process, so return a new buffer
605  */
606  ReleaseBuffer(buffer);
607  *isNew = true;
608  return allocNewBuffer(index, flags);
609  }
610 
611  page = BufferGetPage(buffer);
612 
613  if (PageIsNew(page) || SpGistPageIsDeleted(page) || PageIsEmpty(page))
614  {
615  /* OK to initialize the page */
616  uint16 pageflags = 0;
617 
618  if (GBUF_REQ_LEAF(flags))
619  pageflags |= SPGIST_LEAF;
620  if (GBUF_REQ_NULLS(flags))
621  pageflags |= SPGIST_NULLS;
622  SpGistInitBuffer(buffer, pageflags);
623  lup->freeSpace = PageGetExactFreeSpace(page) - needSpace;
624  *isNew = true;
625  return buffer;
626  }
627 
628  /*
629  * Check that page is of right type and has enough space. We must
630  * recheck this since our cache isn't necessarily up to date.
631  */
632  if ((GBUF_REQ_LEAF(flags) ? SpGistPageIsLeaf(page) : !SpGistPageIsLeaf(page)) &&
634  {
635  int freeSpace = PageGetExactFreeSpace(page);
636 
637  if (freeSpace >= needSpace)
638  {
639  /* Success, update freespace info and return the buffer */
640  lup->freeSpace = freeSpace - needSpace;
641  *isNew = false;
642  return buffer;
643  }
644  }
645 
646  /*
647  * fallback to allocation of new buffer
648  */
649  UnlockReleaseBuffer(buffer);
650  }
651 
652  /* No success with cache, so return a new buffer */
653  *isNew = true;
654  return allocNewBuffer(index, flags);
655 }
#define InvalidBlockNumber
Definition: block.h:33
bool ConditionalLockBuffer(Buffer buffer)
Definition: bufmgr.c:5197
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4936
static bool PageIsEmpty(Page page)
Definition: bufpage.h:223
Pointer Page
Definition: bufpage.h:81
static bool PageIsNew(Page page)
Definition: bufpage.h:233
#define Min(x, y)
Definition: c.h:1004
#define SpGistPageStoresNulls(page)
#define SpGistGetTargetPageFreeSpace(relation)
#define SpGistPageIsLeaf(page)
#define SpGistPageIsDeleted(page)
#define SpGistBlockIsFixed(blkno)
static Buffer allocNewBuffer(Relation index, int flags)
Definition: spgutils.c:505
#define GET_LUP(c, f)
Definition: spgutils.c:482

References allocNewBuffer(), Assert, SpGistLastUsedPage::blkno, BufferGetPage(), ConditionalLockBuffer(), elog, ERROR, SpGistLastUsedPage::freeSpace, GBUF_REQ_LEAF, GBUF_REQ_NULLS, GET_LUP, InvalidBlockNumber, Min, PageGetExactFreeSpace(), PageIsEmpty(), PageIsNew(), ReadBuffer(), ReleaseBuffer(), spgGetCache(), SPGIST_LEAF, SPGIST_NULLS, SPGIST_PAGE_CAPACITY, SpGistBlockIsFixed, SpGistGetTargetPageFreeSpace, SpGistInitBuffer(), SpGistPageIsDeleted, SpGistPageIsLeaf, SpGistPageStoresNulls, and UnlockReleaseBuffer().

Referenced by doPickSplit(), moveLeafs(), spgAddNodeAction(), spgdoinsert(), and spgSplitNodeAction().

◆ SpGistGetInnerTypeSize()

unsigned int SpGistGetInnerTypeSize ( SpGistTypeDesc att,
Datum  datum 
)

Definition at line 771 of file spgutils.c.

772 {
773  unsigned int size;
774 
775  if (att->attbyval)
776  size = sizeof(Datum);
777  else if (att->attlen > 0)
778  size = att->attlen;
779  else
780  size = VARSIZE_ANY(datum);
781 
782  return MAXALIGN(size);
783 }

References SpGistTypeDesc::attbyval, SpGistTypeDesc::attlen, MAXALIGN, size, and VARSIZE_ANY.

Referenced by spgFormInnerTuple(), and spgFormNodeTuple().

◆ SpGistGetLeafTupleSize()

Size SpGistGetLeafTupleSize ( TupleDesc  tupleDescriptor,
const Datum datums,
const bool isnulls 
)

Definition at line 810 of file spgutils.c.

812 {
813  Size size;
814  Size data_size;
815  bool needs_null_mask = false;
816  int natts = tupleDescriptor->natts;
817 
818  /*
819  * Decide whether we need a nulls bitmask.
820  *
821  * If there is only a key attribute (natts == 1), never use a bitmask, for
822  * compatibility with the pre-v14 layout of leaf tuples. Otherwise, we
823  * need one if any attribute is null.
824  */
825  if (natts > 1)
826  {
827  for (int i = 0; i < natts; i++)
828  {
829  if (isnulls[i])
830  {
831  needs_null_mask = true;
832  break;
833  }
834  }
835  }
836 
837  /*
838  * Calculate size of the data part; same as for heap tuples.
839  */
840  data_size = heap_compute_data_size(tupleDescriptor, datums, isnulls);
841 
842  /*
843  * Compute total size.
844  */
845  size = SGLTHDRSZ(needs_null_mask);
846  size += data_size;
847  size = MAXALIGN(size);
848 
849  /*
850  * Ensure that we can replace the tuple with a dead tuple later. This test
851  * is unnecessary when there are any non-null attributes, but be safe.
852  */
853  if (size < SGDTSIZE)
854  size = SGDTSIZE;
855 
856  return size;
857 }

References heap_compute_data_size(), i, MAXALIGN, TupleDescData::natts, SGDTSIZE, SGLTHDRSZ, and size.

Referenced by spgdoinsert().

◆ SpGistInitBuffer()

void SpGistInitBuffer ( Buffer  b,
uint16  f 
)

Definition at line 714 of file spgutils.c.

715 {
716  Assert(BufferGetPageSize(b) == BLCKSZ);
718 }
static Size BufferGetPageSize(Buffer buffer)
Definition: bufmgr.h:389
int b
Definition: isn.c:70
void SpGistInitPage(Page page, uint16 f)
Definition: spgutils.c:700

References Assert, b, BufferGetPage(), BufferGetPageSize(), and SpGistInitPage().

Referenced by allocNewBuffer(), doPickSplit(), spgbuild(), SpGistGetBuffer(), spgRedoAddLeaf(), spgRedoAddNode(), spgRedoMoveLeafs(), spgRedoPickSplit(), and spgRedoSplitTuple().

◆ SpGistInitMetapage()

void SpGistInitMetapage ( Page  page)

Definition at line 724 of file spgutils.c.

725 {
726  SpGistMetaPageData *metadata;
727  int i;
728 
730  metadata = SpGistPageGetMeta(page);
731  memset(metadata, 0, sizeof(SpGistMetaPageData));
732  metadata->magicNumber = SPGIST_MAGIC_NUMBER;
733 
734  /* initialize last-used-page cache to empty */
735  for (i = 0; i < SPGIST_CACHED_PAGES; i++)
737 
738  /*
739  * Set pd_lower just past the end of the metadata. This is essential,
740  * because without doing so, metadata will be lost if xlog.c compresses
741  * the page.
742  */
743  ((PageHeader) page)->pd_lower =
744  ((char *) metadata + sizeof(SpGistMetaPageData)) - (char *) page;
745 }
#define SPGIST_META
#define SPGIST_CACHED_PAGES

References SpGistLastUsedPage::blkno, SpGistLUPCache::cachedPage, i, InvalidBlockNumber, SpGistMetaPageData::lastUsedPages, SpGistMetaPageData::magicNumber, SPGIST_CACHED_PAGES, SPGIST_MAGIC_NUMBER, SPGIST_META, SpGistInitPage(), and SpGistPageGetMeta.

Referenced by spgbuild(), and spgbuildempty().

◆ SpGistInitPage()

void SpGistInitPage ( Page  page,
uint16  f 
)

Definition at line 700 of file spgutils.c.

701 {
702  SpGistPageOpaque opaque;
703 
704  PageInit(page, BLCKSZ, sizeof(SpGistPageOpaqueData));
705  opaque = SpGistPageGetOpaque(page);
706  opaque->flags = f;
707  opaque->spgist_page_id = SPGIST_PAGE_ID;
708 }
void PageInit(Page page, Size pageSize, Size specialSize)
Definition: bufpage.c:42
#define SpGistPageGetOpaque(page)
#define SPGIST_PAGE_ID

References SpGistPageOpaqueData::flags, PageInit(), SpGistPageOpaqueData::spgist_page_id, SPGIST_PAGE_ID, and SpGistPageGetOpaque.

Referenced by spgbuildempty(), SpGistInitBuffer(), and SpGistInitMetapage().

◆ SpGistNewBuffer()

Buffer SpGistNewBuffer ( Relation  index)

Definition at line 386 of file spgutils.c.

387 {
388  Buffer buffer;
389 
390  /* First, try to get a page from FSM */
391  for (;;)
392  {
394 
395  if (blkno == InvalidBlockNumber)
396  break; /* nothing known to FSM */
397 
398  /*
399  * The fixed pages shouldn't ever be listed in FSM, but just in case
400  * one is, ignore it.
401  */
402  if (SpGistBlockIsFixed(blkno))
403  continue;
404 
405  buffer = ReadBuffer(index, blkno);
406 
407  /*
408  * We have to guard against the possibility that someone else already
409  * recycled this page; the buffer may be locked if so.
410  */
411  if (ConditionalLockBuffer(buffer))
412  {
413  Page page = BufferGetPage(buffer);
414 
415  if (PageIsNew(page))
416  return buffer; /* OK to use, if never initialized */
417 
418  if (SpGistPageIsDeleted(page) || PageIsEmpty(page))
419  return buffer; /* OK to use */
420 
422  }
423 
424  /* Can't use it, so release buffer and try again */
425  ReleaseBuffer(buffer);
426  }
427 
428  buffer = ExtendBufferedRel(BMR_REL(index), MAIN_FORKNUM, NULL,
429  EB_LOCK_FIRST);
430 
431  return buffer;
432 }
Buffer ExtendBufferedRel(BufferManagerRelation bmr, ForkNumber forkNum, BufferAccessStrategy strategy, uint32 flags)
Definition: bufmgr.c:873
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:189
@ EB_LOCK_FIRST
Definition: bufmgr.h:86
#define BMR_REL(p_rel)
Definition: bufmgr.h:107
BlockNumber GetFreeIndexPage(Relation rel)
Definition: indexfsm.c:38
@ MAIN_FORKNUM
Definition: relpath.h:50

References BMR_REL, BUFFER_LOCK_UNLOCK, BufferGetPage(), ConditionalLockBuffer(), EB_LOCK_FIRST, ExtendBufferedRel(), GetFreeIndexPage(), InvalidBlockNumber, LockBuffer(), MAIN_FORKNUM, PageIsEmpty(), PageIsNew(), ReadBuffer(), ReleaseBuffer(), SpGistBlockIsFixed, and SpGistPageIsDeleted.

Referenced by allocNewBuffer(), and spgbuild().

◆ SpGistPageAddNewItem()

OffsetNumber SpGistPageAddNewItem ( SpGistState state,
Page  page,
Item  item,
Size  size,
OffsetNumber startOffset,
bool  errorOK 
)

Definition at line 1195 of file spgutils.c.

1197 {
1198  SpGistPageOpaque opaque = SpGistPageGetOpaque(page);
1199  OffsetNumber i,
1200  maxoff,
1201  offnum;
1202 
1203  if (opaque->nPlaceholder > 0 &&
1205  {
1206  /* Try to replace a placeholder */
1207  maxoff = PageGetMaxOffsetNumber(page);
1208  offnum = InvalidOffsetNumber;
1209 
1210  for (;;)
1211  {
1212  if (startOffset && *startOffset != InvalidOffsetNumber)
1213  i = *startOffset;
1214  else
1215  i = FirstOffsetNumber;
1216  for (; i <= maxoff; i++)
1217  {
1219  PageGetItemId(page, i));
1220 
1221  if (it->tupstate == SPGIST_PLACEHOLDER)
1222  {
1223  offnum = i;
1224  break;
1225  }
1226  }
1227 
1228  /* Done if we found a placeholder */
1229  if (offnum != InvalidOffsetNumber)
1230  break;
1231 
1232  if (startOffset && *startOffset != InvalidOffsetNumber)
1233  {
1234  /* Hint was no good, re-search from beginning */
1235  *startOffset = InvalidOffsetNumber;
1236  continue;
1237  }
1238 
1239  /* Hmm, no placeholder found? */
1240  opaque->nPlaceholder = 0;
1241  break;
1242  }
1243 
1244  if (offnum != InvalidOffsetNumber)
1245  {
1246  /* Replace the placeholder tuple */
1247  PageIndexTupleDelete(page, offnum);
1248 
1249  offnum = PageAddItem(page, item, size, offnum, false, false);
1250 
1251  /*
1252  * We should not have failed given the size check at the top of
1253  * the function, but test anyway. If we did fail, we must PANIC
1254  * because we've already deleted the placeholder tuple, and
1255  * there's no other way to keep the damage from getting to disk.
1256  */
1257  if (offnum != InvalidOffsetNumber)
1258  {
1259  Assert(opaque->nPlaceholder > 0);
1260  opaque->nPlaceholder--;
1261  if (startOffset)
1262  *startOffset = offnum + 1;
1263  }
1264  else
1265  elog(PANIC, "failed to add item of size %zu to SPGiST index page",
1266  size);
1267 
1268  return offnum;
1269  }
1270  }
1271 
1272  /* No luck in replacing a placeholder, so just add it to the page */
1273  offnum = PageAddItem(page, item, size,
1274  InvalidOffsetNumber, false, false);
1275 
1276  if (offnum == InvalidOffsetNumber && !errorOK)
1277  elog(ERROR, "failed to add item of size %zu to SPGiST index page",
1278  size);
1279 
1280  return offnum;
1281 }
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
Definition: bufpage.c:1052
static Item PageGetItem(Page page, ItemId itemId)
Definition: bufpage.h:354
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:243
static OffsetNumber PageGetMaxOffsetNumber(Page page)
Definition: bufpage.h:372
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:471
#define PANIC
Definition: elog.h:42
uint16 OffsetNumber
Definition: off.h:24
#define FirstOffsetNumber
Definition: off.h:27
#define SPGIST_PLACEHOLDER

References Assert, elog, ERROR, FirstOffsetNumber, i, InvalidOffsetNumber, MAXALIGN, SpGistPageOpaqueData::nPlaceholder, PageAddItem, PageGetExactFreeSpace(), PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), PageIndexTupleDelete(), PANIC, SGDTSIZE, size, SPGIST_PLACEHOLDER, SpGistPageGetOpaque, and SpGistDeadTupleData::tupstate.

Referenced by addLeafTuple(), doPickSplit(), moveLeafs(), spgAddNodeAction(), and spgSplitNodeAction().

◆ SpGistSetLastUsedPage()

void SpGistSetLastUsedPage ( Relation  index,
Buffer  buffer 
)

Definition at line 665 of file spgutils.c.

666 {
667  SpGistCache *cache = spgGetCache(index);
668  SpGistLastUsedPage *lup;
669  int freeSpace;
670  Page page = BufferGetPage(buffer);
671  BlockNumber blkno = BufferGetBlockNumber(buffer);
672  int flags;
673 
674  /* Never enter fixed pages (root pages) in cache, though */
675  if (SpGistBlockIsFixed(blkno))
676  return;
677 
678  if (SpGistPageIsLeaf(page))
679  flags = GBUF_LEAF;
680  else
681  flags = GBUF_INNER_PARITY(blkno);
682  if (SpGistPageStoresNulls(page))
683  flags |= GBUF_NULLS;
684 
685  lup = GET_LUP(cache, flags);
686 
687  freeSpace = PageGetExactFreeSpace(page);
688  if (lup->blkno == InvalidBlockNumber || lup->blkno == blkno ||
689  lup->freeSpace < freeSpace)
690  {
691  lup->blkno = blkno;
692  lup->freeSpace = freeSpace;
693  }
694 }
#define GBUF_LEAF

References SpGistLastUsedPage::blkno, BufferGetBlockNumber(), BufferGetPage(), SpGistLastUsedPage::freeSpace, GBUF_INNER_PARITY, GBUF_LEAF, GBUF_NULLS, GET_LUP, InvalidBlockNumber, PageGetExactFreeSpace(), spgGetCache(), SpGistBlockIsFixed, SpGistPageIsLeaf, and SpGistPageStoresNulls.

Referenced by doPickSplit(), moveLeafs(), spgAddNodeAction(), spgdoinsert(), spgMatchNodeAction(), spgprocesspending(), spgSplitNodeAction(), and spgvacuumpage().

◆ SpGistUpdateMetaPage()

void SpGistUpdateMetaPage ( Relation  index)

Definition at line 442 of file spgutils.c.

443 {
444  SpGistCache *cache = (SpGistCache *) index->rd_amcache;
445 
446  if (cache != NULL)
447  {
448  Buffer metabuffer;
449 
450  metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO);
451 
452  if (ConditionalLockBuffer(metabuffer))
453  {
454  Page metapage = BufferGetPage(metabuffer);
455  SpGistMetaPageData *metadata = SpGistPageGetMeta(metapage);
456 
457  metadata->lastUsedPages = cache->lastUsedPages;
458 
459  /*
460  * Set pd_lower just past the end of the metadata. This is
461  * essential, because without doing so, metadata will be lost if
462  * xlog.c compresses the page. (We must do this here because
463  * pre-v11 versions of PG did not set the metapage's pd_lower
464  * correctly, so a pg_upgraded index might contain the wrong
465  * value.)
466  */
467  ((PageHeader) metapage)->pd_lower =
468  ((char *) metadata + sizeof(SpGistMetaPageData)) - (char *) metapage;
469 
470  MarkBufferDirty(metabuffer);
471  UnlockReleaseBuffer(metabuffer);
472  }
473  else
474  {
475  ReleaseBuffer(metabuffer);
476  }
477  }
478 }
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:2543
PageHeaderData * PageHeader
Definition: bufpage.h:173
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77

References BufferGetPage(), ConditionalLockBuffer(), if(), SpGistMetaPageData::lastUsedPages, SpGistCache::lastUsedPages, MarkBufferDirty(), ReadBuffer(), ReleaseBuffer(), SPGIST_METAPAGE_BLKNO, SpGistPageGetMeta, and UnlockReleaseBuffer().

Referenced by spgbuild(), spginsert(), and spgvacuumscan().

◆ spgoptions()

bytea* spgoptions ( Datum  reloptions,
bool  validate 
)

Definition at line 751 of file spgutils.c.

752 {
753  static const relopt_parse_elt tab[] = {
754  {"fillfactor", RELOPT_TYPE_INT, offsetof(SpGistOptions, fillfactor)},
755  };
756 
757  return (bytea *) build_reloptions(reloptions, validate,
759  sizeof(SpGistOptions),
760  tab, lengthof(tab));
761 }
#define lengthof(array)
Definition: c.h:788
static int fillfactor
Definition: pgbench.c:187
void * build_reloptions(Datum reloptions, bool validate, relopt_kind kind, Size relopt_struct_size, const relopt_parse_elt *relopt_elems, int num_relopt_elems)
Definition: reloptions.c:1908
@ RELOPT_KIND_SPGIST
Definition: reloptions.h:50
@ RELOPT_TYPE_INT
Definition: reloptions.h:32
Definition: c.h:687

References build_reloptions(), fillfactor, lengthof, RELOPT_KIND_SPGIST, and RELOPT_TYPE_INT.

Referenced by spghandler().

◆ spgproperty()

bool spgproperty ( Oid  index_oid,
int  attno,
IndexAMProperty  prop,
const char *  propname,
bool res,
bool isnull 
)

Definition at line 1290 of file spgutils.c.

1293 {
1294  Oid opclass,
1295  opfamily,
1296  opcintype;
1297  CatCList *catlist;
1298  int i;
1299 
1300  /* Only answer column-level inquiries */
1301  if (attno == 0)
1302  return false;
1303 
1304  switch (prop)
1305  {
1307  break;
1308  default:
1309  return false;
1310  }
1311 
1312  /*
1313  * Currently, SP-GiST distance-ordered scans require that there be a
1314  * distance operator in the opclass with the default types. So we assume
1315  * that if such an operator exists, then there's a reason for it.
1316  */
1317 
1318  /* First we need to know the column's opclass. */
1319  opclass = get_index_column_opclass(index_oid, attno);
1320  if (!OidIsValid(opclass))
1321  {
1322  *isnull = true;
1323  return true;
1324  }
1325 
1326  /* Now look up the opclass family and input datatype. */
1327  if (!get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
1328  {
1329  *isnull = true;
1330  return true;
1331  }
1332 
1333  /* And now we can check whether the operator is provided. */
1334  catlist = SearchSysCacheList1(AMOPSTRATEGY,
1335  ObjectIdGetDatum(opfamily));
1336 
1337  *res = false;
1338 
1339  for (i = 0; i < catlist->n_members; i++)
1340  {
1341  HeapTuple amoptup = &catlist->members[i]->tuple;
1342  Form_pg_amop amopform = (Form_pg_amop) GETSTRUCT(amoptup);
1343 
1344  if (amopform->amoppurpose == AMOP_ORDER &&
1345  (amopform->amoplefttype == opcintype ||
1346  amopform->amoprighttype == opcintype) &&
1347  opfamily_can_sort_type(amopform->amopsortfamily,
1348  get_op_rettype(amopform->amopopr)))
1349  {
1350  *res = true;
1351  break;
1352  }
1353  }
1354 
1355  ReleaseSysCacheList(catlist);
1356 
1357  *isnull = false;
1358 
1359  return true;
1360 }
@ AMPROP_DISTANCE_ORDERABLE
Definition: amapi.h:42
bool opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid)
Definition: amvalidate.c:271
bool get_opclass_opfamily_and_input_type(Oid opclass, Oid *opfamily, Oid *opcintype)
Definition: lsyscache.c:1235
Oid get_op_rettype(Oid opno)
Definition: lsyscache.c:1333
Oid get_index_column_opclass(Oid index_oid, int attno)
Definition: lsyscache.c:3512
FormData_pg_amop * Form_pg_amop
Definition: pg_amop.h:88
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:180
int n_members
Definition: catcache.h:178
HeapTupleData tuple
Definition: catcache.h:123
#define ReleaseSysCacheList(x)
Definition: syscache.h:129
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:122

References AMPROP_DISTANCE_ORDERABLE, get_index_column_opclass(), get_op_rettype(), get_opclass_opfamily_and_input_type(), GETSTRUCT, i, catclist::members, catclist::n_members, ObjectIdGetDatum(), OidIsValid, opfamily_can_sort_type(), ReleaseSysCacheList, res, SearchSysCacheList1, and catctup::tuple.

Referenced by spghandler().