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 "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
#include "utils/index_selfuncs.h"
#include "utils/lsyscache.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, Datum *datums, bool *isnulls)
 
SpGistLeafTuple spgFormLeafTuple (SpGistState *state, ItemPointer heapPtr, Datum *datums, 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 462 of file spgutils.c.

Function Documentation

◆ allocNewBuffer()

static Buffer allocNewBuffer ( Relation  index,
int  flags 
)
static

Definition at line 485 of file spgutils.c.

486 {
487  SpGistCache *cache = spgGetCache(index);
488  uint16 pageflags = 0;
489 
490  if (GBUF_REQ_LEAF(flags))
491  pageflags |= SPGIST_LEAF;
492  if (GBUF_REQ_NULLS(flags))
493  pageflags |= SPGIST_NULLS;
494 
495  for (;;)
496  {
497  Buffer buffer;
498 
499  buffer = SpGistNewBuffer(index);
500  SpGistInitBuffer(buffer, pageflags);
501 
502  if (pageflags & SPGIST_LEAF)
503  {
504  /* Leaf pages have no parity concerns, so just use it */
505  return buffer;
506  }
507  else
508  {
509  BlockNumber blkno = BufferGetBlockNumber(buffer);
510  int blkFlags = GBUF_INNER_PARITY(blkno);
511 
512  if ((flags & GBUF_PARITY_MASK) == blkFlags)
513  {
514  /* Page has right parity, use it */
515  return buffer;
516  }
517  else
518  {
519  /* Page has wrong parity, record it in cache and try again */
520  if (pageflags & SPGIST_NULLS)
521  blkFlags |= GBUF_NULLS;
522  cache->lastUsedPages.cachedPage[blkFlags].blkno = blkno;
523  cache->lastUsedPages.cachedPage[blkFlags].freeSpace =
525  UnlockReleaseBuffer(buffer);
526  }
527  }
528  }
529 }
uint32 BlockNumber
Definition: block.h:31
int Buffer
Definition: buf.h:23
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:3290
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4497
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:350
Size PageGetExactFreeSpace(Page page)
Definition: bufpage.c:958
unsigned short uint16
Definition: c.h:494
#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:366
void SpGistInitBuffer(Buffer b, uint16 f)
Definition: spgutils.c:694
SpGistCache * spgGetCache(Relation index)
Definition: spgutils.c:180
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 158 of file spgutils.c.

159 {
160  HeapTuple tp;
161  Form_pg_type typtup;
162 
163  desc->type = type;
165  if (!HeapTupleIsValid(tp))
166  elog(ERROR, "cache lookup failed for type %u", type);
167  typtup = (Form_pg_type) GETSTRUCT(tp);
168  desc->attlen = typtup->typlen;
169  desc->attbyval = typtup->typbyval;
170  desc->attalign = typtup->typalign;
171  desc->attstorage = typtup->typstorage;
172  ReleaseSysCache(tp);
173 }
#define ERROR
Definition: elog.h:39
#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:868
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:820
@ TYPEOID
Definition: syscache.h:114
const char * type

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

Referenced by spgGetCache().

◆ GetIndexInputType()

static Oid GetIndexInputType ( Relation  index,
AttrNumber  indexcol 
)
static

Definition at line 113 of file spgutils.c.

114 {
115  Oid opcintype;
116  AttrNumber heapcol;
117  List *indexprs;
118  ListCell *indexpr_item;
119 
120  Assert(index->rd_index != NULL);
121  Assert(indexcol > 0 && indexcol <= index->rd_index->indnkeyatts);
122  opcintype = index->rd_opcintype[indexcol - 1];
123  if (!IsPolymorphicType(opcintype))
124  return opcintype;
125  heapcol = index->rd_index->indkey.values[indexcol - 1];
126  if (heapcol != 0) /* Simple index column? */
127  return getBaseType(get_atttype(index->rd_index->indrelid, heapcol));
128 
129  /*
130  * If the index expressions are already cached, skip calling
131  * RelationGetIndexExpressions, as it will make a copy which is overkill.
132  * We're not going to modify the trees, and we're not going to do anything
133  * that would invalidate the relcache entry before we're done.
134  */
135  if (index->rd_indexprs)
136  indexprs = index->rd_indexprs;
137  else
139  indexpr_item = list_head(indexprs);
140  for (int i = 1; i <= index->rd_index->indnkeyatts; i++)
141  {
142  if (index->rd_index->indkey.values[i - 1] == 0)
143  {
144  /* expression column */
145  if (indexpr_item == NULL)
146  elog(ERROR, "wrong number of index expressions");
147  if (i == indexcol)
148  return getBaseType(exprType((Node *) lfirst(indexpr_item)));
149  indexpr_item = lnext(indexprs, indexpr_item);
150  }
151  }
152  elog(ERROR, "wrong number of index expressions");
153  return InvalidOid; /* keep compiler quiet */
154 }
int16 AttrNumber
Definition: attnum.h:21
int i
Definition: isn.c:73
Assert(fmt[strlen(fmt) - 1] !='\n')
Oid getBaseType(Oid typid)
Definition: lsyscache.c:2503
Oid get_atttype(Oid relid, AttrNumber attnum)
Definition: lsyscache.c:939
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:43
#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:4996
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 300 of file spgutils.c.

301 {
302  TupleDesc outTupDesc;
303  Form_pg_attribute att;
304 
305  if (keyType->type ==
307  outTupDesc = RelationGetDescr(index);
308  else
309  {
311  att = TupleDescAttr(outTupDesc, spgKeyColumn);
312  /* It's sufficient to update the type-dependent fields of the column */
313  att->atttypid = keyType->type;
314  att->atttypmod = -1;
315  att->attlen = keyType->attlen;
316  att->attbyval = keyType->attbyval;
317  att->attalign = keyType->attalign;
318  att->attstorage = keyType->attstorage;
319  /* We shouldn't need to bother with making these valid: */
320  att->attcompression = InvalidCompressionMethod;
321  att->attcollation = InvalidOid;
322  /* In case we changed typlen, we'd better reset following offsets */
323  for (int i = spgFirstIncludeColumn; i < outTupDesc->natts; i++)
324  TupleDescAttr(outTupDesc, i)->attcacheoff = -1;
325  }
326  return outTupDesc;
327 }
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
#define RelationGetDescr(relation)
Definition: rel.h:530
#define spgFirstIncludeColumn
#define spgKeyColumn
#define InvalidCompressionMethod
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:111
#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 331 of file spgutils.c.

332 {
333  SpGistCache *cache;
334 
335  state->index = index;
336 
337  /* Get cached static information about index */
338  cache = spgGetCache(index);
339 
340  state->config = cache->config;
341  state->attType = cache->attType;
342  state->attLeafType = cache->attLeafType;
343  state->attPrefixType = cache->attPrefixType;
344  state->attLabelType = cache->attLabelType;
345 
346  /* Ensure we have a valid descriptor for leaf tuples */
347  state->leafTupDesc = getSpGistTupleDesc(state->index, &state->attLeafType);
348 
349  /* Make workspace for constructing dead tuples */
350  state->deadTupleStorage = palloc0(SGDTSIZE);
351 
352  /* Set XID to use in redirection tuples */
353  state->myXid = GetTopTransactionIdIfAny();
354 
355  /* Assume we're not in an index build (spgbuild will override) */
356  state->isBuild = false;
357 }
void * palloc0(Size size)
Definition: mcxt.c:1257
#define SGDTSIZE
TupleDesc getSpGistTupleDesc(Relation index, SpGistTypeDesc *keyType)
Definition: spgutils.c:300
SpGistTypeDesc attPrefixType
SpGistTypeDesc attLeafType
SpGistTypeDesc attType
spgConfigOut config
SpGistTypeDesc attLabelType
Definition: regguts.h:323
TransactionId GetTopTransactionIdIfAny(void)
Definition: xact.c:432

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 769 of file spgutils.c.

770 {
771  unsigned int size;
772 
773  if (att->attbyval)
774  {
775  memcpy(target, &datum, sizeof(Datum));
776  }
777  else
778  {
779  size = (att->attlen > 0) ? att->attlen : VARSIZE_ANY(datum);
780  memcpy(target, DatumGetPointer(datum), size);
781  }
782 }
uintptr_t Datum
Definition: postgres.h:64
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
#define VARSIZE_ANY(PTR)
Definition: varatt.h:311

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

Referenced by spgFormInnerTuple(), and spgFormNodeTuple().

◆ spgDeformLeafTuple()

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

Definition at line 1088 of file spgutils.c.

1090 {
1091  bool hasNullsMask = SGLT_GET_HASNULLMASK(tup);
1092  char *tp; /* ptr to tuple data */
1093  bits8 *bp; /* ptr to null bitmap in tuple */
1094 
1095  if (keyColumnIsNull && tupleDescriptor->natts == 1)
1096  {
1097  /*
1098  * Trivial case: there is only the key attribute and we're in a nulls
1099  * tree. The hasNullsMask bit in the tuple header should not be set
1100  * (and thus we can't use index_deform_tuple_internal), but
1101  * nonetheless the result is NULL.
1102  *
1103  * Note: currently this is dead code, because noplace calls this when
1104  * there is only the key attribute. But we should cover the case.
1105  */
1106  Assert(!hasNullsMask);
1107 
1108  datums[spgKeyColumn] = (Datum) 0;
1109  isnulls[spgKeyColumn] = true;
1110  return;
1111  }
1112 
1113  tp = (char *) tup + SGLTHDRSZ(hasNullsMask);
1114  bp = (bits8 *) ((char *) tup + sizeof(SpGistLeafTupleData));
1115 
1116  index_deform_tuple_internal(tupleDescriptor,
1117  datums, isnulls,
1118  tp, bp, hasNullsMask);
1119 
1120  /*
1121  * Key column isnull value from the tuple should be consistent with
1122  * keyColumnIsNull flag from the caller.
1123  */
1124  Assert(keyColumnIsNull == isnulls[spgKeyColumn]);
1125 }
uint8 bits8
Definition: c.h:502
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 1133 of file spgutils.c.

1134 {
1135  Datum *nodeLabels;
1136  int i;
1137  SpGistNodeTuple node;
1138 
1139  /* Either all the labels must be NULL, or none. */
1140  node = SGITNODEPTR(innerTuple);
1141  if (IndexTupleHasNulls(node))
1142  {
1143  SGITITERATE(innerTuple, i, node)
1144  {
1145  if (!IndexTupleHasNulls(node))
1146  elog(ERROR, "some but not all node labels are null in SPGiST inner tuple");
1147  }
1148  /* They're all null, so just return NULL */
1149  return NULL;
1150  }
1151  else
1152  {
1153  nodeLabels = (Datum *) palloc(sizeof(Datum) * innerTuple->nNodes);
1154  SGITITERATE(innerTuple, i, node)
1155  {
1156  if (IndexTupleHasNulls(node))
1157  elog(ERROR, "some but not all node labels are null in SPGiST inner tuple");
1158  nodeLabels[i] = SGNTDATUM(node, state);
1159  }
1160  return nodeLabels;
1161  }
1162 }
#define IndexTupleHasNulls(itup)
Definition: itup.h:71
void * palloc(Size size)
Definition: mcxt.c:1226
#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 1057 of file spgutils.c.

1059 {
1060  SpGistDeadTuple tuple = (SpGistDeadTuple) state->deadTupleStorage;
1061 
1062  tuple->tupstate = tupstate;
1063  tuple->size = SGDTSIZE;
1065 
1066  if (tupstate == SPGIST_REDIRECT)
1067  {
1068  ItemPointerSet(&tuple->pointer, blkno, offnum);
1070  tuple->xid = state->myXid;
1071  }
1072  else
1073  {
1074  ItemPointerSetInvalid(&tuple->pointer);
1075  tuple->xid = InvalidTransactionId;
1076  }
1077 
1078  return tuple;
1079 }
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
#define TransactionIdIsValid(xid)
Definition: transam.h:41

References Assert(), InvalidOffsetNumber, InvalidTransactionId, ItemPointerSet(), ItemPointerSetInvalid(), SpGistDeadTupleData::pointer, SGDTSIZE, SGLT_SET_NEXTOFFSET, SpGistDeadTupleData::size, SPGIST_REDIRECT, TransactionIdIsValid, 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 974 of file spgutils.c.

976 {
977  SpGistInnerTuple tup;
978  unsigned int size;
979  unsigned int prefixSize;
980  int i;
981  char *ptr;
982 
983  /* Compute size needed */
984  if (hasPrefix)
985  prefixSize = SpGistGetInnerTypeSize(&state->attPrefixType, prefix);
986  else
987  prefixSize = 0;
988 
989  size = SGITHDRSZ + prefixSize;
990 
991  /* Note: we rely on node tuple sizes to be maxaligned already */
992  for (i = 0; i < nNodes; i++)
993  size += IndexTupleSize(nodes[i]);
994 
995  /*
996  * Ensure that we can replace the tuple with a dead tuple later. This
997  * test is unnecessary given current tuple layouts, but let's be safe.
998  */
999  if (size < SGDTSIZE)
1000  size = SGDTSIZE;
1001 
1002  /*
1003  * Inner tuple should be small enough to fit on a page
1004  */
1005  if (size > SPGIST_PAGE_CAPACITY - sizeof(ItemIdData))
1006  ereport(ERROR,
1007  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1008  errmsg("SP-GiST inner tuple size %zu exceeds maximum %zu",
1009  (Size) size,
1010  SPGIST_PAGE_CAPACITY - sizeof(ItemIdData)),
1011  errhint("Values larger than a buffer page cannot be indexed.")));
1012 
1013  /*
1014  * Check for overflow of header fields --- probably can't fail if the
1015  * above succeeded, but let's be paranoid
1016  */
1017  if (size > SGITMAXSIZE ||
1018  prefixSize > SGITMAXPREFIXSIZE ||
1019  nNodes > SGITMAXNNODES)
1020  elog(ERROR, "SPGiST inner tuple header field is too small");
1021 
1022  /* OK, form the tuple */
1023  tup = (SpGistInnerTuple) palloc0(size);
1024 
1025  tup->nNodes = nNodes;
1026  tup->prefixSize = prefixSize;
1027  tup->size = size;
1028 
1029  if (hasPrefix)
1030  memcpyInnerDatum(SGITDATAPTR(tup), &state->attPrefixType, prefix);
1031 
1032  ptr = (char *) SGITNODEPTR(tup);
1033 
1034  for (i = 0; i < nNodes; i++)
1035  {
1036  SpGistNodeTuple node = nodes[i];
1037 
1038  memcpy(ptr, node, IndexTupleSize(node));
1039  ptr += IndexTupleSize(node);
1040  }
1041 
1042  return tup;
1043 }
size_t Size
Definition: c.h:594
int errhint(const char *fmt,...)
Definition: elog.c:1316
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#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:769
unsigned int SpGistGetInnerTypeSize(SpGistTypeDesc *att, Datum datum)
Definition: spgutils.c:751
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, SpGistInnerTupleData::size, SPGIST_PAGE_CAPACITY, and SpGistGetInnerTypeSize().

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

◆ spgFormLeafTuple()

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

Definition at line 843 of file spgutils.c.

845 {
846  SpGistLeafTuple tup;
847  TupleDesc tupleDescriptor = state->leafTupDesc;
848  Size size;
849  Size hoff;
850  Size data_size;
851  bool needs_null_mask = false;
852  int natts = tupleDescriptor->natts;
853  char *tp; /* ptr to tuple data */
854  uint16 tupmask = 0; /* unused heap_fill_tuple output */
855 
856  /*
857  * Decide whether we need a nulls bitmask.
858  *
859  * If there is only a key attribute (natts == 1), never use a bitmask, for
860  * compatibility with the pre-v14 layout of leaf tuples. Otherwise, we
861  * need one if any attribute is null.
862  */
863  if (natts > 1)
864  {
865  for (int i = 0; i < natts; i++)
866  {
867  if (isnulls[i])
868  {
869  needs_null_mask = true;
870  break;
871  }
872  }
873  }
874 
875  /*
876  * Calculate size of the data part; same as for heap tuples.
877  */
878  data_size = heap_compute_data_size(tupleDescriptor, datums, isnulls);
879 
880  /*
881  * Compute total size.
882  */
883  hoff = SGLTHDRSZ(needs_null_mask);
884  size = hoff + data_size;
885  size = MAXALIGN(size);
886 
887  /*
888  * Ensure that we can replace the tuple with a dead tuple later. This test
889  * is unnecessary when there are any non-null attributes, but be safe.
890  */
891  if (size < SGDTSIZE)
892  size = SGDTSIZE;
893 
894  /* OK, form the tuple */
895  tup = (SpGistLeafTuple) palloc0(size);
896 
897  tup->size = size;
899  tup->heapPtr = *heapPtr;
900 
901  tp = (char *) tup + hoff;
902 
903  if (needs_null_mask)
904  {
905  bits8 *bp; /* ptr to null bitmap in tuple */
906 
907  /* Set nullmask presence bit in SpGistLeafTuple header */
908  SGLT_SET_HASNULLMASK(tup, true);
909  /* Fill the data area and null mask */
910  bp = (bits8 *) ((char *) tup + sizeof(SpGistLeafTupleData));
911  heap_fill_tuple(tupleDescriptor, datums, isnulls, tp, data_size,
912  &tupmask, bp);
913  }
914  else if (natts > 1 || !isnulls[spgKeyColumn])
915  {
916  /* Fill data area only */
917  heap_fill_tuple(tupleDescriptor, datums, isnulls, tp, data_size,
918  &tupmask, (bits8 *) NULL);
919  }
920  /* otherwise we have no data, nor a bitmap, to fill */
921 
922  return tup;
923 }
#define MAXALIGN(LEN)
Definition: c.h:800
Size heap_compute_data_size(TupleDesc tupleDesc, Datum *values, bool *isnull)
Definition: heaptuple.c:207
void heap_fill_tuple(TupleDesc tupleDesc, Datum *values, bool *isnull, char *data, Size data_size, uint16 *infomask, bits8 *bit)
Definition: heaptuple.c:392
#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, SpGistLeafTupleData::size, and spgKeyColumn.

Referenced by doPickSplit(), and spgdoinsert().

◆ spgFormNodeTuple()

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

Definition at line 932 of file spgutils.c.

933 {
934  SpGistNodeTuple tup;
935  unsigned int size;
936  unsigned short infomask = 0;
937 
938  /* compute space needed (note result is already maxaligned) */
939  size = SGNTHDRSZ;
940  if (!isnull)
941  size += SpGistGetInnerTypeSize(&state->attLabelType, label);
942 
943  /*
944  * Here we make sure that the size will fit in the field reserved for it
945  * in t_info.
946  */
947  if ((size & INDEX_SIZE_MASK) != size)
948  ereport(ERROR,
949  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
950  errmsg("index row requires %zu bytes, maximum size is %zu",
951  (Size) size, (Size) INDEX_SIZE_MASK)));
952 
953  tup = (SpGistNodeTuple) palloc0(size);
954 
955  if (isnull)
956  infomask |= INDEX_NULL_MASK;
957  /* we don't bother setting the INDEX_VAR_MASK bit */
958  infomask |= size;
959  tup->t_info = infomask;
960 
961  /* The TID field will be filled in later */
963 
964  if (!isnull)
965  memcpyInnerDatum(SGNTDATAPTR(tup), &state->attLabelType, label);
966 
967  return tup;
968 }
#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, SpGistGetInnerTypeSize(), IndexTupleData::t_info, and IndexTupleData::t_tid.

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

◆ spgGetCache()

SpGistCache* spgGetCache ( Relation  index)

Definition at line 180 of file spgutils.c.

181 {
182  SpGistCache *cache;
183 
184  if (index->rd_amcache == NULL)
185  {
186  Oid atttype;
187  spgConfigIn in;
188  FmgrInfo *procinfo;
189  Buffer metabuffer;
190  SpGistMetaPageData *metadata;
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  /* Last, get the lastUsedPages data from the metapage */
259  metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO);
260  LockBuffer(metabuffer, BUFFER_LOCK_SHARE);
261 
262  metadata = SpGistPageGetMeta(BufferGetPage(metabuffer));
263 
264  if (metadata->magicNumber != SPGIST_MAGIC_NUMBER)
265  elog(ERROR, "index \"%s\" is not an SP-GiST index",
267 
268  cache->lastUsedPages = metadata->lastUsedPages;
269 
270  UnlockReleaseBuffer(metabuffer);
271 
272  index->rd_amcache = (void *) cache;
273  }
274  else
275  {
276  /* assume it's up to date */
277  cache = (SpGistCache *) index->rd_amcache;
278  }
279 
280  return cache;
281 }
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4715
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:708
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:158
#define OidIsValid(objectId)
Definition: c.h:764
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1132
FmgrInfo * index_getprocinfo(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:811
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:777
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1064
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:538
#define IndexRelationGetNumberOfAttributes(relation)
Definition: rel.h:516
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:523
#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:113
static void fillTypeDesc(SpGistTypeDesc *desc, Oid type)
Definition: spgutils.c:158
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->amcaninclude = true;
64  amroutine->amusemaintenanceworkmem = false;
65  amroutine->amsummarizing = false;
66  amroutine->amparallelvacuumoptions =
68  amroutine->amkeytype = InvalidOid;
69 
70  amroutine->ambuild = spgbuild;
71  amroutine->ambuildempty = spgbuildempty;
72  amroutine->aminsert = spginsert;
73  amroutine->ambulkdelete = spgbulkdelete;
74  amroutine->amvacuumcleanup = spgvacuumcleanup;
75  amroutine->amcanreturn = spgcanreturn;
76  amroutine->amcostestimate = spgcostestimate;
77  amroutine->amoptions = spgoptions;
78  amroutine->amproperty = spgproperty;
79  amroutine->ambuildphasename = NULL;
80  amroutine->amvalidate = spgvalidate;
81  amroutine->amadjustmembers = spgadjustmembers;
82  amroutine->ambeginscan = spgbeginscan;
83  amroutine->amrescan = spgrescan;
84  amroutine->amgettuple = spggettuple;
85  amroutine->amgetbitmap = spggetbitmap;
86  amroutine->amendscan = spgendscan;
87  amroutine->ammarkpos = NULL;
88  amroutine->amrestrpos = NULL;
89  amroutine->amestimateparallelscan = NULL;
90  amroutine->aminitparallelscan = NULL;
91  amroutine->amparallelrescan = NULL;
92 
93  PG_RETURN_POINTER(amroutine);
94 }
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:361
#define makeNode(_type_)
Definition: nodes.h:176
void spgcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation, double *indexPages)
Definition: selfuncs.c:7070
bool spginsert(Relation index, Datum *values, bool *isnull, ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique, bool indexUnchanged, IndexInfo *indexInfo)
Definition: spginsert.c:200
void spgbuildempty(Relation index)
Definition: spginsert.c:156
IndexBuildResult * spgbuild(Relation heap, Relation index, IndexInfo *indexInfo)
Definition: spginsert.c:75
#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:731
bool spgproperty(Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull)
Definition: spgutils.c:1271
IndexBulkDeleteResult * spgvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
Definition: spgvacuum.c:937
IndexBulkDeleteResult * spgbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition: spgvacuum.c:906
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:270
ambuildempty_function ambuildempty
Definition: amapi.h:262
amvacuumcleanup_function amvacuumcleanup
Definition: amapi.h:265
bool amclusterable
Definition: amapi.h:238
amoptions_function amoptions
Definition: amapi.h:268
amestimateparallelscan_function amestimateparallelscan
Definition: amapi.h:282
amrestrpos_function amrestrpos
Definition: amapi.h:279
aminsert_function aminsert
Definition: amapi.h:263
amendscan_function amendscan
Definition: amapi.h:277
uint16 amoptsprocnum
Definition: amapi.h:218
amparallelrescan_function amparallelrescan
Definition: amapi.h:284
Oid amkeytype
Definition: amapi.h:252
bool ampredlocks
Definition: amapi.h:240
uint16 amsupport
Definition: amapi.h:216
amcostestimate_function amcostestimate
Definition: amapi.h:267
bool amcanorderbyop
Definition: amapi.h:222
amadjustmembers_function amadjustmembers
Definition: amapi.h:272
ambuild_function ambuild
Definition: amapi.h:261
bool amstorage
Definition: amapi.h:236
uint16 amstrategies
Definition: amapi.h:214
bool amoptionalkey
Definition: amapi.h:230
amgettuple_function amgettuple
Definition: amapi.h:275
amcanreturn_function amcanreturn
Definition: amapi.h:266
bool amcanunique
Definition: amapi.h:226
amgetbitmap_function amgetbitmap
Definition: amapi.h:276
amproperty_function amproperty
Definition: amapi.h:269
ambulkdelete_function ambulkdelete
Definition: amapi.h:264
bool amsearcharray
Definition: amapi.h:232
bool amsummarizing
Definition: amapi.h:248
amvalidate_function amvalidate
Definition: amapi.h:271
ammarkpos_function ammarkpos
Definition: amapi.h:278
bool amcanmulticol
Definition: amapi.h:228
bool amusemaintenanceworkmem
Definition: amapi.h:246
ambeginscan_function ambeginscan
Definition: amapi.h:273
bool amcanparallel
Definition: amapi.h:242
amrescan_function amrescan
Definition: amapi.h:274
bool amcanorder
Definition: amapi.h:220
aminitparallelscan_function aminitparallelscan
Definition: amapi.h:283
uint8 amparallelvacuumoptions
Definition: amapi.h:250
bool amcanbackward
Definition: amapi.h:224
bool amcaninclude
Definition: amapi.h:244
bool amsearchnulls
Definition: amapi.h:234
#define VACUUM_OPTION_PARALLEL_BULKDEL
Definition: vacuum.h:47
#define VACUUM_OPTION_PARALLEL_COND_CLEANUP
Definition: vacuum.h:54

References IndexAmRoutine::amadjustmembers, IndexAmRoutine::ambeginscan, IndexAmRoutine::ambuild, IndexAmRoutine::ambuildempty, IndexAmRoutine::ambuildphasename, IndexAmRoutine::ambulkdelete, IndexAmRoutine::amcanbackward, 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::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 541 of file spgutils.c.

542 {
543  SpGistCache *cache = spgGetCache(index);
544  SpGistLastUsedPage *lup;
545 
546  /* Bail out if even an empty page wouldn't meet the demand */
547  if (needSpace > SPGIST_PAGE_CAPACITY)
548  elog(ERROR, "desired SPGiST tuple size is too big");
549 
550  /*
551  * If possible, increase the space request to include relation's
552  * fillfactor. This ensures that when we add unrelated tuples to a page,
553  * we try to keep 100-fillfactor% available for adding tuples that are
554  * related to the ones already on it. But fillfactor mustn't cause an
555  * error for requests that would otherwise be legal.
556  */
557  needSpace += SpGistGetTargetPageFreeSpace(index);
558  needSpace = Min(needSpace, SPGIST_PAGE_CAPACITY);
559 
560  /* Get the cache entry for this flags setting */
561  lup = GET_LUP(cache, flags);
562 
563  /* If we have nothing cached, just turn it over to allocNewBuffer */
564  if (lup->blkno == InvalidBlockNumber)
565  {
566  *isNew = true;
567  return allocNewBuffer(index, flags);
568  }
569 
570  /* fixed pages should never be in cache */
572 
573  /* If cached freeSpace isn't enough, don't bother looking at the page */
574  if (lup->freeSpace >= needSpace)
575  {
576  Buffer buffer;
577  Page page;
578 
579  buffer = ReadBuffer(index, lup->blkno);
580 
581  if (!ConditionalLockBuffer(buffer))
582  {
583  /*
584  * buffer is locked by another process, so return a new buffer
585  */
586  ReleaseBuffer(buffer);
587  *isNew = true;
588  return allocNewBuffer(index, flags);
589  }
590 
591  page = BufferGetPage(buffer);
592 
593  if (PageIsNew(page) || SpGistPageIsDeleted(page) || PageIsEmpty(page))
594  {
595  /* OK to initialize the page */
596  uint16 pageflags = 0;
597 
598  if (GBUF_REQ_LEAF(flags))
599  pageflags |= SPGIST_LEAF;
600  if (GBUF_REQ_NULLS(flags))
601  pageflags |= SPGIST_NULLS;
602  SpGistInitBuffer(buffer, pageflags);
603  lup->freeSpace = PageGetExactFreeSpace(page) - needSpace;
604  *isNew = true;
605  return buffer;
606  }
607 
608  /*
609  * Check that page is of right type and has enough space. We must
610  * recheck this since our cache isn't necessarily up to date.
611  */
612  if ((GBUF_REQ_LEAF(flags) ? SpGistPageIsLeaf(page) : !SpGistPageIsLeaf(page)) &&
614  {
615  int freeSpace = PageGetExactFreeSpace(page);
616 
617  if (freeSpace >= needSpace)
618  {
619  /* Success, update freespace info and return the buffer */
620  lup->freeSpace = freeSpace - needSpace;
621  *isNew = false;
622  return buffer;
623  }
624  }
625 
626  /*
627  * fallback to allocation of new buffer
628  */
629  UnlockReleaseBuffer(buffer);
630  }
631 
632  /* No success with cache, so return a new buffer */
633  *isNew = true;
634  return allocNewBuffer(index, flags);
635 }
#define InvalidBlockNumber
Definition: block.h:33
bool ConditionalLockBuffer(Buffer buffer)
Definition: bufmgr.c:4741
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4480
static bool PageIsEmpty(Page page)
Definition: bufpage.h:220
Pointer Page
Definition: bufpage.h:78
static bool PageIsNew(Page page)
Definition: bufpage.h:230
#define Min(x, y)
Definition: c.h:993
#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:485
#define GET_LUP(c, f)
Definition: spgutils.c:462

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 751 of file spgutils.c.

752 {
753  unsigned int size;
754 
755  if (att->attbyval)
756  size = sizeof(Datum);
757  else if (att->attlen > 0)
758  size = att->attlen;
759  else
760  size = VARSIZE_ANY(datum);
761 
762  return MAXALIGN(size);
763 }

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

Referenced by spgFormInnerTuple(), and spgFormNodeTuple().

◆ SpGistGetLeafTupleSize()

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

Definition at line 790 of file spgutils.c.

792 {
793  Size size;
794  Size data_size;
795  bool needs_null_mask = false;
796  int natts = tupleDescriptor->natts;
797 
798  /*
799  * Decide whether we need a nulls bitmask.
800  *
801  * If there is only a key attribute (natts == 1), never use a bitmask, for
802  * compatibility with the pre-v14 layout of leaf tuples. Otherwise, we
803  * need one if any attribute is null.
804  */
805  if (natts > 1)
806  {
807  for (int i = 0; i < natts; i++)
808  {
809  if (isnulls[i])
810  {
811  needs_null_mask = true;
812  break;
813  }
814  }
815  }
816 
817  /*
818  * Calculate size of the data part; same as for heap tuples.
819  */
820  data_size = heap_compute_data_size(tupleDescriptor, datums, isnulls);
821 
822  /*
823  * Compute total size.
824  */
825  size = SGLTHDRSZ(needs_null_mask);
826  size += data_size;
827  size = MAXALIGN(size);
828 
829  /*
830  * Ensure that we can replace the tuple with a dead tuple later. This test
831  * is unnecessary when there are any non-null attributes, but be safe.
832  */
833  if (size < SGDTSIZE)
834  size = SGDTSIZE;
835 
836  return size;
837 }

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

Referenced by spgdoinsert().

◆ SpGistInitBuffer()

void SpGistInitBuffer ( Buffer  b,
uint16  f 
)

Definition at line 694 of file spgutils.c.

695 {
696  Assert(BufferGetPageSize(b) == BLCKSZ);
698 }
static Size BufferGetPageSize(Buffer buffer)
Definition: bufmgr.h:339
int b
Definition: isn.c:70
void SpGistInitPage(Page page, uint16 f)
Definition: spgutils.c:680

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

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

◆ SpGistInitMetapage()

void SpGistInitMetapage ( Page  page)

Definition at line 704 of file spgutils.c.

705 {
706  SpGistMetaPageData *metadata;
707  int i;
708 
710  metadata = SpGistPageGetMeta(page);
711  memset(metadata, 0, sizeof(SpGistMetaPageData));
712  metadata->magicNumber = SPGIST_MAGIC_NUMBER;
713 
714  /* initialize last-used-page cache to empty */
715  for (i = 0; i < SPGIST_CACHED_PAGES; i++)
717 
718  /*
719  * Set pd_lower just past the end of the metadata. This is essential,
720  * because without doing so, metadata will be lost if xlog.c compresses
721  * the page.
722  */
723  ((PageHeader) page)->pd_lower =
724  ((char *) metadata + sizeof(SpGistMetaPageData)) - (char *) page;
725 }
#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 680 of file spgutils.c.

681 {
682  SpGistPageOpaque opaque;
683 
684  PageInit(page, BLCKSZ, sizeof(SpGistPageOpaqueData));
685  opaque = SpGistPageGetOpaque(page);
686  opaque->flags = f;
687  opaque->spgist_page_id = SPGIST_PAGE_ID;
688 }
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 SpGistInitBuffer(), and SpGistInitMetapage().

◆ SpGistNewBuffer()

Buffer SpGistNewBuffer ( Relation  index)

Definition at line 366 of file spgutils.c.

367 {
368  Buffer buffer;
369 
370  /* First, try to get a page from FSM */
371  for (;;)
372  {
374 
375  if (blkno == InvalidBlockNumber)
376  break; /* nothing known to FSM */
377 
378  /*
379  * The fixed pages shouldn't ever be listed in FSM, but just in case
380  * one is, ignore it.
381  */
382  if (SpGistBlockIsFixed(blkno))
383  continue;
384 
385  buffer = ReadBuffer(index, blkno);
386 
387  /*
388  * We have to guard against the possibility that someone else already
389  * recycled this page; the buffer may be locked if so.
390  */
391  if (ConditionalLockBuffer(buffer))
392  {
393  Page page = BufferGetPage(buffer);
394 
395  if (PageIsNew(page))
396  return buffer; /* OK to use, if never initialized */
397 
398  if (SpGistPageIsDeleted(page) || PageIsEmpty(page))
399  return buffer; /* OK to use */
400 
402  }
403 
404  /* Can't use it, so release buffer and try again */
405  ReleaseBuffer(buffer);
406  }
407 
408  buffer = ExtendBufferedRel(BMR_REL(index), MAIN_FORKNUM, NULL,
409  EB_LOCK_FIRST);
410 
411  return buffer;
412 }
Buffer ExtendBufferedRel(BufferManagerRelation bmr, ForkNumber forkNum, BufferAccessStrategy strategy, uint32 flags)
Definition: bufmgr.c:812
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:157
@ EB_LOCK_FIRST
Definition: bufmgr.h:85
#define BMR_REL(p_rel)
Definition: bufmgr.h:106
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 1176 of file spgutils.c.

1178 {
1179  SpGistPageOpaque opaque = SpGistPageGetOpaque(page);
1180  OffsetNumber i,
1181  maxoff,
1182  offnum;
1183 
1184  if (opaque->nPlaceholder > 0 &&
1185  PageGetExactFreeSpace(page) + SGDTSIZE >= MAXALIGN(size))
1186  {
1187  /* Try to replace a placeholder */
1188  maxoff = PageGetMaxOffsetNumber(page);
1189  offnum = InvalidOffsetNumber;
1190 
1191  for (;;)
1192  {
1193  if (startOffset && *startOffset != InvalidOffsetNumber)
1194  i = *startOffset;
1195  else
1196  i = FirstOffsetNumber;
1197  for (; i <= maxoff; i++)
1198  {
1200  PageGetItemId(page, i));
1201 
1202  if (it->tupstate == SPGIST_PLACEHOLDER)
1203  {
1204  offnum = i;
1205  break;
1206  }
1207  }
1208 
1209  /* Done if we found a placeholder */
1210  if (offnum != InvalidOffsetNumber)
1211  break;
1212 
1213  if (startOffset && *startOffset != InvalidOffsetNumber)
1214  {
1215  /* Hint was no good, re-search from beginning */
1216  *startOffset = InvalidOffsetNumber;
1217  continue;
1218  }
1219 
1220  /* Hmm, no placeholder found? */
1221  opaque->nPlaceholder = 0;
1222  break;
1223  }
1224 
1225  if (offnum != InvalidOffsetNumber)
1226  {
1227  /* Replace the placeholder tuple */
1228  PageIndexTupleDelete(page, offnum);
1229 
1230  offnum = PageAddItem(page, item, size, offnum, false, false);
1231 
1232  /*
1233  * We should not have failed given the size check at the top of
1234  * the function, but test anyway. If we did fail, we must PANIC
1235  * because we've already deleted the placeholder tuple, and
1236  * there's no other way to keep the damage from getting to disk.
1237  */
1238  if (offnum != InvalidOffsetNumber)
1239  {
1240  Assert(opaque->nPlaceholder > 0);
1241  opaque->nPlaceholder--;
1242  if (startOffset)
1243  *startOffset = offnum + 1;
1244  }
1245  else
1246  elog(PANIC, "failed to add item of size %zu to SPGiST index page",
1247  size);
1248 
1249  return offnum;
1250  }
1251  }
1252 
1253  /* No luck in replacing a placeholder, so just add it to the page */
1254  offnum = PageAddItem(page, item, size,
1255  InvalidOffsetNumber, false, false);
1256 
1257  if (offnum == InvalidOffsetNumber && !errorOK)
1258  elog(ERROR, "failed to add item of size %zu to SPGiST index page",
1259  size);
1260 
1261  return offnum;
1262 }
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
Definition: bufpage.c:1052
static Item PageGetItem(Page page, ItemId itemId)
Definition: bufpage.h:351
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:240
static OffsetNumber PageGetMaxOffsetNumber(Page page)
Definition: bufpage.h:369
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:468
#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, SPGIST_PLACEHOLDER, SpGistPageGetOpaque, and SpGistDeadTupleData::tupstate.

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

◆ SpGistSetLastUsedPage()

void SpGistSetLastUsedPage ( Relation  index,
Buffer  buffer 
)

Definition at line 645 of file spgutils.c.

646 {
647  SpGistCache *cache = spgGetCache(index);
648  SpGistLastUsedPage *lup;
649  int freeSpace;
650  Page page = BufferGetPage(buffer);
651  BlockNumber blkno = BufferGetBlockNumber(buffer);
652  int flags;
653 
654  /* Never enter fixed pages (root pages) in cache, though */
655  if (SpGistBlockIsFixed(blkno))
656  return;
657 
658  if (SpGistPageIsLeaf(page))
659  flags = GBUF_LEAF;
660  else
661  flags = GBUF_INNER_PARITY(blkno);
662  if (SpGistPageStoresNulls(page))
663  flags |= GBUF_NULLS;
664 
665  lup = GET_LUP(cache, flags);
666 
667  freeSpace = PageGetExactFreeSpace(page);
668  if (lup->blkno == InvalidBlockNumber || lup->blkno == blkno ||
669  lup->freeSpace < freeSpace)
670  {
671  lup->blkno = blkno;
672  lup->freeSpace = freeSpace;
673  }
674 }
#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 422 of file spgutils.c.

423 {
424  SpGistCache *cache = (SpGistCache *) index->rd_amcache;
425 
426  if (cache != NULL)
427  {
428  Buffer metabuffer;
429 
430  metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO);
431 
432  if (ConditionalLockBuffer(metabuffer))
433  {
434  Page metapage = BufferGetPage(metabuffer);
435  SpGistMetaPageData *metadata = SpGistPageGetMeta(metapage);
436 
437  metadata->lastUsedPages = cache->lastUsedPages;
438 
439  /*
440  * Set pd_lower just past the end of the metadata. This is
441  * essential, because without doing so, metadata will be lost if
442  * xlog.c compresses the page. (We must do this here because
443  * pre-v11 versions of PG did not set the metapage's pd_lower
444  * correctly, so a pg_upgraded index might contain the wrong
445  * value.)
446  */
447  ((PageHeader) metapage)->pd_lower =
448  ((char *) metadata + sizeof(SpGistMetaPageData)) - (char *) metapage;
449 
450  MarkBufferDirty(metabuffer);
451  UnlockReleaseBuffer(metabuffer);
452  }
453  else
454  {
455  ReleaseBuffer(metabuffer);
456  }
457  }
458 }
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:2111
PageHeaderData * PageHeader
Definition: bufpage.h:170
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 731 of file spgutils.c.

732 {
733  static const relopt_parse_elt tab[] = {
734  {"fillfactor", RELOPT_TYPE_INT, offsetof(SpGistOptions, fillfactor)},
735  };
736 
737  return (bytea *) build_reloptions(reloptions, validate,
739  sizeof(SpGistOptions),
740  tab, lengthof(tab));
741 }
#define lengthof(array)
Definition: c.h:777
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:1910
@ RELOPT_KIND_SPGIST
Definition: reloptions.h:50
@ RELOPT_TYPE_INT
Definition: reloptions.h:32
Definition: c.h:676

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 1271 of file spgutils.c.

1274 {
1275  Oid opclass,
1276  opfamily,
1277  opcintype;
1278  CatCList *catlist;
1279  int i;
1280 
1281  /* Only answer column-level inquiries */
1282  if (attno == 0)
1283  return false;
1284 
1285  switch (prop)
1286  {
1288  break;
1289  default:
1290  return false;
1291  }
1292 
1293  /*
1294  * Currently, SP-GiST distance-ordered scans require that there be a
1295  * distance operator in the opclass with the default types. So we assume
1296  * that if such an operator exists, then there's a reason for it.
1297  */
1298 
1299  /* First we need to know the column's opclass. */
1300  opclass = get_index_column_opclass(index_oid, attno);
1301  if (!OidIsValid(opclass))
1302  {
1303  *isnull = true;
1304  return true;
1305  }
1306 
1307  /* Now look up the opclass family and input datatype. */
1308  if (!get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
1309  {
1310  *isnull = true;
1311  return true;
1312  }
1313 
1314  /* And now we can check whether the operator is provided. */
1316  ObjectIdGetDatum(opfamily));
1317 
1318  *res = false;
1319 
1320  for (i = 0; i < catlist->n_members; i++)
1321  {
1322  HeapTuple amoptup = &catlist->members[i]->tuple;
1323  Form_pg_amop amopform = (Form_pg_amop) GETSTRUCT(amoptup);
1324 
1325  if (amopform->amoppurpose == AMOP_ORDER &&
1326  (amopform->amoplefttype == opcintype ||
1327  amopform->amoprighttype == opcintype) &&
1328  opfamily_can_sort_type(amopform->amopsortfamily,
1329  get_op_rettype(amopform->amopopr)))
1330  {
1331  *res = true;
1332  break;
1333  }
1334  }
1335 
1336  ReleaseSysCacheList(catlist);
1337 
1338  *isnull = false;
1339 
1340  return true;
1341 }
@ 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:1239
Oid get_op_rettype(Oid opno)
Definition: lsyscache.c:1337
Oid get_index_column_opclass(Oid index_oid, int attno)
Definition: lsyscache.c:3494
FormData_pg_amop * Form_pg_amop
Definition: pg_amop.h:88
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:178
int n_members
Definition: catcache.h:176
HeapTupleData tuple
Definition: catcache.h:121
@ AMOPSTRATEGY
Definition: syscache.h:38
#define ReleaseSysCacheList(x)
Definition: syscache.h:225
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:218

References AMOPSTRATEGY, 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().