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/transam.h"
#include "access/xact.h"
#include "catalog/pg_amop.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 void fillTypeDesc (SpGistTypeDesc *desc, Oid type)
 
SpGistCachespgGetCache (Relation index)
 
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 SpGistGetTypeSize (SpGistTypeDesc *att, Datum datum)
 
static void memcpyDatum (void *target, SpGistTypeDesc *att, Datum datum)
 
SpGistLeafTuple spgFormLeafTuple (SpGistState *state, ItemPointer heapPtr, Datum datum, bool isnull)
 
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)
 
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 316 of file spgutils.c.

Referenced by SpGistGetBuffer(), and SpGistSetLastUsedPage().

Function Documentation

◆ allocNewBuffer()

static Buffer allocNewBuffer ( Relation  index,
int  flags 
)
static

Definition at line 339 of file spgutils.c.

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().

340 {
341  SpGistCache *cache = spgGetCache(index);
342  uint16 pageflags = 0;
343 
344  if (GBUF_REQ_LEAF(flags))
345  pageflags |= SPGIST_LEAF;
346  if (GBUF_REQ_NULLS(flags))
347  pageflags |= SPGIST_NULLS;
348 
349  for (;;)
350  {
351  Buffer buffer;
352 
353  buffer = SpGistNewBuffer(index);
354  SpGistInitBuffer(buffer, pageflags);
355 
356  if (pageflags & SPGIST_LEAF)
357  {
358  /* Leaf pages have no parity concerns, so just use it */
359  return buffer;
360  }
361  else
362  {
363  BlockNumber blkno = BufferGetBlockNumber(buffer);
364  int blkFlags = GBUF_INNER_PARITY(blkno);
365 
366  if ((flags & GBUF_PARITY_MASK) == blkFlags)
367  {
368  /* Page has right parity, use it */
369  return buffer;
370  }
371  else
372  {
373  /* Page has wrong parity, record it in cache and try again */
374  if (pageflags & SPGIST_NULLS)
375  blkFlags |= GBUF_NULLS;
376  cache->lastUsedPages.cachedPage[blkFlags].blkno = blkno;
377  cache->lastUsedPages.cachedPage[blkFlags].freeSpace =
379  UnlockReleaseBuffer(buffer);
380  }
381  }
382  }
383 }
SpGistCache * spgGetCache(Relation index)
Definition: spgutils.c:99
#define SPGIST_NULLS
uint32 BlockNumber
Definition: block.h:31
SpGistLUPCache lastUsedPages
SpGistLastUsedPage cachedPage[SPGIST_CACHED_PAGES]
#define GBUF_REQ_NULLS(flags)
unsigned short uint16
Definition: c.h:357
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3388
#define GBUF_INNER_PARITY(x)
void SpGistInitBuffer(Buffer b, uint16 f)
Definition: spgutils.c:550
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
Buffer SpGistNewBuffer(Relation index)
Definition: spgutils.c:211
#define GBUF_PARITY_MASK
#define GBUF_NULLS
Size PageGetExactFreeSpace(Page page)
Definition: bufpage.c:632
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2613
#define SPGIST_LEAF
int Buffer
Definition: buf.h:23
#define GBUF_REQ_LEAF(flags)

◆ fillTypeDesc()

static void fillTypeDesc ( SpGistTypeDesc desc,
Oid  type 
)
static

Definition at line 88 of file spgutils.c.

References SpGistTypeDesc::attbyval, SpGistTypeDesc::attlen, get_typlenbyval(), SpGistTypeDesc::type, and generate_unaccent_rules::type.

Referenced by spgGetCache().

89 {
90  desc->type = type;
91  get_typlenbyval(type, &desc->attlen, &desc->attbyval);
92 }
void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval)
Definition: lsyscache.c:2029

◆ initSpGistState()

void initSpGistState ( SpGistState state,
Relation  index 
)

Definition at line 181 of file spgutils.c.

References SpGistState::attLabelType, SpGistCache::attLabelType, SpGistState::attLeafType, SpGistCache::attLeafType, SpGistState::attPrefixType, SpGistCache::attPrefixType, SpGistState::attType, SpGistCache::attType, SpGistState::config, SpGistCache::config, SpGistState::deadTupleStorage, GetTopTransactionIdIfAny(), SpGistState::isBuild, SpGistState::myXid, palloc0(), SGDTSIZE, and spgGetCache().

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

182 {
183  SpGistCache *cache;
184 
185  /* Get cached static information about index */
186  cache = spgGetCache(index);
187 
188  state->config = cache->config;
189  state->attType = cache->attType;
190  state->attLeafType = cache->attLeafType;
191  state->attPrefixType = cache->attPrefixType;
192  state->attLabelType = cache->attLabelType;
193 
194  /* Make workspace for constructing dead tuples */
196 
197  /* Set XID to use in redirection tuples */
198  state->myXid = GetTopTransactionIdIfAny();
199 
200  /* Assume we're not in an index build (spgbuild will override) */
201  state->isBuild = false;
202 }
SpGistTypeDesc attLeafType
SpGistTypeDesc attPrefixType
SpGistCache * spgGetCache(Relation index)
Definition: spgutils.c:99
#define SGDTSIZE
SpGistTypeDesc attLeafType
SpGistTypeDesc attType
SpGistTypeDesc attLabelType
SpGistTypeDesc attType
spgConfigOut config
TransactionId myXid
TransactionId GetTopTransactionIdIfAny(void)
Definition: xact.c:410
spgConfigOut config
void * palloc0(Size size)
Definition: mcxt.c:980
char * deadTupleStorage
SpGistTypeDesc attLabelType
SpGistTypeDesc attPrefixType

◆ memcpyDatum()

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

Definition at line 617 of file spgutils.c.

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

Referenced by spgFormInnerTuple(), spgFormLeafTuple(), and spgFormNodeTuple().

618 {
619  unsigned int size;
620 
621  if (att->attbyval)
622  {
623  memcpy(target, &datum, sizeof(Datum));
624  }
625  else
626  {
627  size = (att->attlen > 0) ? att->attlen : VARSIZE_ANY(datum);
628  memcpy(target, DatumGetPointer(datum), size);
629  }
630 }
uintptr_t Datum
Definition: postgres.h:367
#define VARSIZE_ANY(PTR)
Definition: postgres.h:335
#define DatumGetPointer(X)
Definition: postgres.h:549

◆ spgExtractNodeLabels()

Datum* spgExtractNodeLabels ( SpGistState state,
SpGistInnerTuple  innerTuple 
)

Definition at line 828 of file spgutils.c.

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

Referenced by spgdoinsert(), and spgInitInnerConsistentIn().

829 {
830  Datum *nodeLabels;
831  int i;
832  SpGistNodeTuple node;
833 
834  /* Either all the labels must be NULL, or none. */
835  node = SGITNODEPTR(innerTuple);
836  if (IndexTupleHasNulls(node))
837  {
838  SGITITERATE(innerTuple, i, node)
839  {
840  if (!IndexTupleHasNulls(node))
841  elog(ERROR, "some but not all node labels are null in SPGiST inner tuple");
842  }
843  /* They're all null, so just return NULL */
844  return NULL;
845  }
846  else
847  {
848  nodeLabels = (Datum *) palloc(sizeof(Datum) * innerTuple->nNodes);
849  SGITITERATE(innerTuple, i, node)
850  {
851  if (IndexTupleHasNulls(node))
852  elog(ERROR, "some but not all node labels are null in SPGiST inner tuple");
853  nodeLabels[i] = SGNTDATUM(node, state);
854  }
855  return nodeLabels;
856  }
857 }
#define SGITNODEPTR(x)
#define SGITITERATE(x, i, nt)
#define IndexTupleHasNulls(itup)
Definition: itup.h:72
#define ERROR
Definition: elog.h:43
uintptr_t Datum
Definition: postgres.h:367
void * palloc(Size size)
Definition: mcxt.c:949
#define elog(elevel,...)
Definition: elog.h:226
int i
#define SGNTDATUM(x, s)

◆ spgFormDeadTuple()

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

Definition at line 798 of file spgutils.c.

References Assert, SpGistState::deadTupleStorage, InvalidOffsetNumber, InvalidTransactionId, ItemPointerSet, ItemPointerSetInvalid, SpGistState::myXid, SpGistDeadTupleData::nextOffset, SpGistDeadTupleData::pointer, SGDTSIZE, SpGistDeadTupleData::size, SPGIST_REDIRECT, TransactionIdIsValid, SpGistDeadTupleData::tupstate, and SpGistDeadTupleData::xid.

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

800 {
802 
803  tuple->tupstate = tupstate;
804  tuple->size = SGDTSIZE;
806 
807  if (tupstate == SPGIST_REDIRECT)
808  {
809  ItemPointerSet(&tuple->pointer, blkno, offnum);
811  tuple->xid = state->myXid;
812  }
813  else
814  {
816  tuple->xid = InvalidTransactionId;
817  }
818 
819  return tuple;
820 }
#define SGDTSIZE
#define SPGIST_REDIRECT
ItemPointerData pointer
SpGistDeadTupleData * SpGistDeadTuple
#define InvalidTransactionId
Definition: transam.h:31
TransactionId myXid
unsigned int tupstate
char * deadTupleStorage
OffsetNumber nextOffset
#define InvalidOffsetNumber
Definition: off.h:26
#define Assert(condition)
Definition: c.h:732
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:172
#define TransactionIdIsValid(xid)
Definition: transam.h:41
#define ItemPointerSet(pointer, blockNumber, offNum)
Definition: itemptr.h:127

◆ spgFormInnerTuple()

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

Definition at line 715 of file spgutils.c.

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

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

717 {
718  SpGistInnerTuple tup;
719  unsigned int size;
720  unsigned int prefixSize;
721  int i;
722  char *ptr;
723 
724  /* Compute size needed */
725  if (hasPrefix)
726  prefixSize = SpGistGetTypeSize(&state->attPrefixType, prefix);
727  else
728  prefixSize = 0;
729 
730  size = SGITHDRSZ + prefixSize;
731 
732  /* Note: we rely on node tuple sizes to be maxaligned already */
733  for (i = 0; i < nNodes; i++)
734  size += IndexTupleSize(nodes[i]);
735 
736  /*
737  * Ensure that we can replace the tuple with a dead tuple later. This
738  * test is unnecessary given current tuple layouts, but let's be safe.
739  */
740  if (size < SGDTSIZE)
741  size = SGDTSIZE;
742 
743  /*
744  * Inner tuple should be small enough to fit on a page
745  */
746  if (size > SPGIST_PAGE_CAPACITY - sizeof(ItemIdData))
747  ereport(ERROR,
748  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
749  errmsg("SP-GiST inner tuple size %zu exceeds maximum %zu",
750  (Size) size,
751  SPGIST_PAGE_CAPACITY - sizeof(ItemIdData)),
752  errhint("Values larger than a buffer page cannot be indexed.")));
753 
754  /*
755  * Check for overflow of header fields --- probably can't fail if the
756  * above succeeded, but let's be paranoid
757  */
758  if (size > SGITMAXSIZE ||
759  prefixSize > SGITMAXPREFIXSIZE ||
760  nNodes > SGITMAXNNODES)
761  elog(ERROR, "SPGiST inner tuple header field is too small");
762 
763  /* OK, form the tuple */
764  tup = (SpGistInnerTuple) palloc0(size);
765 
766  tup->nNodes = nNodes;
767  tup->prefixSize = prefixSize;
768  tup->size = size;
769 
770  if (hasPrefix)
771  memcpyDatum(SGITDATAPTR(tup), &state->attPrefixType, prefix);
772 
773  ptr = (char *) SGITNODEPTR(tup);
774 
775  for (i = 0; i < nNodes; i++)
776  {
777  SpGistNodeTuple node = nodes[i];
778 
779  memcpy(ptr, node, IndexTupleSize(node));
780  ptr += IndexTupleSize(node);
781  }
782 
783  return tup;
784 }
#define SGITNODEPTR(x)
SpGistTypeDesc attPrefixType
SpGistInnerTupleData * SpGistInnerTuple
int errhint(const char *fmt,...)
Definition: elog.c:974
#define SGITMAXPREFIXSIZE
#define SGDTSIZE
int errcode(int sqlerrcode)
Definition: elog.c:570
#define ERROR
Definition: elog.h:43
unsigned int prefixSize
#define SGITMAXSIZE
#define SGITDATAPTR(x)
#define SGITMAXNNODES
#define ereport(elevel, rest)
Definition: elog.h:141
unsigned int SpGistGetTypeSize(SpGistTypeDesc *att, Datum datum)
Definition: spgutils.c:599
void * palloc0(Size size)
Definition: mcxt.c:980
#define SPGIST_PAGE_CAPACITY
size_t Size
Definition: c.h:466
static void memcpyDatum(void *target, SpGistTypeDesc *att, Datum datum)
Definition: spgutils.c:617
int errmsg(const char *fmt,...)
Definition: elog.c:784
#define elog(elevel,...)
Definition: elog.h:226
int i
#define SGITHDRSZ
#define IndexTupleSize(itup)
Definition: itup.h:71

◆ spgFormLeafTuple()

SpGistLeafTuple spgFormLeafTuple ( SpGistState state,
ItemPointer  heapPtr,
Datum  datum,
bool  isnull 
)

Definition at line 636 of file spgutils.c.

References SpGistState::attLeafType, SpGistLeafTupleData::heapPtr, InvalidOffsetNumber, memcpyDatum(), SpGistLeafTupleData::nextOffset, palloc0(), SGDTSIZE, SGLTDATAPTR, SGLTHDRSZ, SpGistLeafTupleData::size, and SpGistGetTypeSize().

Referenced by doPickSplit(), and spgdoinsert().

638 {
639  SpGistLeafTuple tup;
640  unsigned int size;
641 
642  /* compute space needed (note result is already maxaligned) */
643  size = SGLTHDRSZ;
644  if (!isnull)
645  size += SpGistGetTypeSize(&state->attLeafType, datum);
646 
647  /*
648  * Ensure that we can replace the tuple with a dead tuple later. This
649  * test is unnecessary when !isnull, but let's be safe.
650  */
651  if (size < SGDTSIZE)
652  size = SGDTSIZE;
653 
654  /* OK, form the tuple */
655  tup = (SpGistLeafTuple) palloc0(size);
656 
657  tup->size = size;
659  tup->heapPtr = *heapPtr;
660  if (!isnull)
661  memcpyDatum(SGLTDATAPTR(tup), &state->attLeafType, datum);
662 
663  return tup;
664 }
#define SGDTSIZE
SpGistTypeDesc attLeafType
#define SGLTDATAPTR(x)
#define SGLTHDRSZ
unsigned int SpGistGetTypeSize(SpGistTypeDesc *att, Datum datum)
Definition: spgutils.c:599
void * palloc0(Size size)
Definition: mcxt.c:980
#define InvalidOffsetNumber
Definition: off.h:26
OffsetNumber nextOffset
static void memcpyDatum(void *target, SpGistTypeDesc *att, Datum datum)
Definition: spgutils.c:617
ItemPointerData heapPtr
SpGistLeafTupleData * SpGistLeafTuple

◆ spgFormNodeTuple()

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

Definition at line 673 of file spgutils.c.

References SpGistState::attLabelType, ereport, errcode(), errmsg(), ERROR, INDEX_NULL_MASK, INDEX_SIZE_MASK, ItemPointerSetInvalid, memcpyDatum(), palloc0(), SGNTDATAPTR, SGNTHDRSZ, SpGistGetTypeSize(), IndexTupleData::t_info, and IndexTupleData::t_tid.

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

674 {
675  SpGistNodeTuple tup;
676  unsigned int size;
677  unsigned short infomask = 0;
678 
679  /* compute space needed (note result is already maxaligned) */
680  size = SGNTHDRSZ;
681  if (!isnull)
682  size += SpGistGetTypeSize(&state->attLabelType, label);
683 
684  /*
685  * Here we make sure that the size will fit in the field reserved for it
686  * in t_info.
687  */
688  if ((size & INDEX_SIZE_MASK) != size)
689  ereport(ERROR,
690  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
691  errmsg("index row requires %zu bytes, maximum size is %zu",
692  (Size) size, (Size) INDEX_SIZE_MASK)));
693 
694  tup = (SpGistNodeTuple) palloc0(size);
695 
696  if (isnull)
697  infomask |= INDEX_NULL_MASK;
698  /* we don't bother setting the INDEX_VAR_MASK bit */
699  infomask |= size;
700  tup->t_info = infomask;
701 
702  /* The TID field will be filled in later */
704 
705  if (!isnull)
706  memcpyDatum(SGNTDATAPTR(tup), &state->attLabelType, label);
707 
708  return tup;
709 }
ItemPointerData t_tid
Definition: itup.h:37
#define INDEX_SIZE_MASK
Definition: itup.h:65
int errcode(int sqlerrcode)
Definition: elog.c:570
SpGistTypeDesc attLabelType
#define SGNTHDRSZ
#define ERROR
Definition: elog.h:43
#define INDEX_NULL_MASK
Definition: itup.h:69
#define ereport(elevel, rest)
Definition: elog.h:141
unsigned int SpGistGetTypeSize(SpGistTypeDesc *att, Datum datum)
Definition: spgutils.c:599
void * palloc0(Size size)
Definition: mcxt.c:980
static char * label
Definition: pg_basebackup.c:86
#define SGNTDATAPTR(x)
size_t Size
Definition: c.h:466
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:172
static void memcpyDatum(void *target, SpGistTypeDesc *att, Datum datum)
Definition: spgutils.c:617
int errmsg(const char *fmt,...)
Definition: elog.c:784
unsigned short t_info
Definition: itup.h:49
SpGistNodeTupleData * SpGistNodeTuple

◆ spgGetCache()

SpGistCache* spgGetCache ( Relation  index)

Definition at line 99 of file spgutils.c.

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(), index_getprocid(), index_getprocinfo(), spgConfigOut::labelType, SpGistMetaPageData::lastUsedPages, SpGistCache::lastUsedPages, spgConfigOut::leafType, LockBuffer(), SpGistMetaPageData::magicNumber, MemoryContextAllocZero(), TupleDescData::natts, OidIsValid, PointerGetDatum, spgConfigOut::prefixType, RelationData::rd_amcache, RelationData::rd_att, RelationData::rd_indcollation, RelationData::rd_indexcxt, ReadBuffer(), RelationGetRelationName, SPGIST_COMPRESS_PROC, SPGIST_CONFIG_PROC, SPGIST_MAGIC_NUMBER, SPGIST_METAPAGE_BLKNO, SpGistPageGetMeta, TupleDescAttr, and UnlockReleaseBuffer().

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

100 {
101  SpGistCache *cache;
102 
103  if (index->rd_amcache == NULL)
104  {
105  Oid atttype;
106  spgConfigIn in;
107  FmgrInfo *procinfo;
108  Buffer metabuffer;
109  SpGistMetaPageData *metadata;
110 
111  cache = MemoryContextAllocZero(index->rd_indexcxt,
112  sizeof(SpGistCache));
113 
114  /* SPGiST doesn't support multi-column indexes */
115  Assert(index->rd_att->natts == 1);
116 
117  /*
118  * Get the actual data type of the indexed column from the index
119  * tupdesc. We pass this to the opclass config function so that
120  * polymorphic opclasses are possible.
121  */
122  atttype = TupleDescAttr(index->rd_att, 0)->atttypid;
123 
124  /* Call the config function to get config info for the opclass */
125  in.attType = atttype;
126 
127  procinfo = index_getprocinfo(index, 1, SPGIST_CONFIG_PROC);
128  FunctionCall2Coll(procinfo,
129  index->rd_indcollation[0],
130  PointerGetDatum(&in),
131  PointerGetDatum(&cache->config));
132 
133  /* Get the information we need about each relevant datatype */
134  fillTypeDesc(&cache->attType, atttype);
135 
136  if (OidIsValid(cache->config.leafType) &&
137  cache->config.leafType != atttype)
138  {
140  ereport(ERROR,
141  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
142  errmsg("compress method must be defined when leaf type is different from input type")));
143 
144  fillTypeDesc(&cache->attLeafType, cache->config.leafType);
145  }
146  else
147  {
148  cache->attLeafType = cache->attType;
149  }
150 
151  fillTypeDesc(&cache->attPrefixType, cache->config.prefixType);
152  fillTypeDesc(&cache->attLabelType, cache->config.labelType);
153 
154  /* Last, get the lastUsedPages data from the metapage */
155  metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO);
156  LockBuffer(metabuffer, BUFFER_LOCK_SHARE);
157 
158  metadata = SpGistPageGetMeta(BufferGetPage(metabuffer));
159 
160  if (metadata->magicNumber != SPGIST_MAGIC_NUMBER)
161  elog(ERROR, "index \"%s\" is not an SP-GiST index",
162  RelationGetRelationName(index));
163 
164  cache->lastUsedPages = metadata->lastUsedPages;
165 
166  UnlockReleaseBuffer(metabuffer);
167 
168  index->rd_amcache = (void *) cache;
169  }
170  else
171  {
172  /* assume it's up to date */
173  cache = (SpGistCache *) index->rd_amcache;
174  }
175 
176  return cache;
177 }
SpGistTypeDesc attLeafType
Definition: fmgr.h:56
FmgrInfo * index_getprocinfo(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:794
#define PointerGetDatum(X)
Definition: postgres.h:556
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
int errcode(int sqlerrcode)
Definition: elog.c:570
Oid attType
Definition: spgist.h:41
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1150
unsigned int Oid
Definition: postgres_ext.h:31
SpGistTypeDesc attType
#define OidIsValid(objectId)
Definition: c.h:638
SpGistLUPCache lastUsedPages
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3388
Oid * rd_indcollation
Definition: rel.h:168
#define ERROR
Definition: elog.h:43
#define SPGIST_METAPAGE_BLKNO
#define RelationGetRelationName(relation)
Definition: rel.h:453
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
#define ereport(elevel, rest)
Definition: elog.h:141
#define SPGIST_MAGIC_NUMBER
spgConfigOut config
SpGistLUPCache lastUsedPages
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3602
Oid prefixType
Definition: spgist.h:46
TupleDesc rd_att
Definition: rel.h:84
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:839
#define Assert(condition)
Definition: c.h:732
#define SPGIST_COMPRESS_PROC
Definition: spgist.h:32
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:596
#define SPGIST_CONFIG_PROC
Definition: spgist.h:27
#define SpGistPageGetMeta(p)
SpGistTypeDesc attLabelType
int errmsg(const char *fmt,...)
Definition: elog.c:784
#define elog(elevel,...)
Definition: elog.h:226
MemoryContext rd_indexcxt
Definition: rel.h:155
Oid labelType
Definition: spgist.h:47
Oid leafType
Definition: spgist.h:48
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:87
SpGistTypeDesc attPrefixType
static void fillTypeDesc(SpGistTypeDesc *desc, Oid type)
Definition: spgutils.c:88
void * rd_amcache
Definition: rel.h:179
int Buffer
Definition: buf.h:23
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:760

◆ spghandler()

Datum spghandler ( PG_FUNCTION_ARGS  )

Definition at line 40 of file spgutils.c.

References 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::amparallelrescan, IndexAmRoutine::ampredlocks, IndexAmRoutine::amproperty, IndexAmRoutine::amrescan, IndexAmRoutine::amrestrpos, IndexAmRoutine::amsearcharray, IndexAmRoutine::amsearchnulls, IndexAmRoutine::amstorage, IndexAmRoutine::amstrategies, IndexAmRoutine::amsupport, IndexAmRoutine::amvacuumcleanup, IndexAmRoutine::amvalidate, InvalidOid, makeNode, PG_RETURN_POINTER, spgbeginscan(), spgbuild(), spgbuildempty(), spgbulkdelete(), spgcanreturn(), spgcostestimate(), spgendscan(), spggetbitmap(), spggettuple(), spginsert(), SPGISTNProc, spgoptions(), spgproperty(), spgrescan(), spgvacuumcleanup(), and spgvalidate().

41 {
43 
44  amroutine->amstrategies = 0;
45  amroutine->amsupport = SPGISTNProc;
46  amroutine->amcanorder = false;
47  amroutine->amcanorderbyop = true;
48  amroutine->amcanbackward = false;
49  amroutine->amcanunique = false;
50  amroutine->amcanmulticol = false;
51  amroutine->amoptionalkey = true;
52  amroutine->amsearcharray = false;
53  amroutine->amsearchnulls = true;
54  amroutine->amstorage = false;
55  amroutine->amclusterable = false;
56  amroutine->ampredlocks = false;
57  amroutine->amcanparallel = false;
58  amroutine->amcaninclude = false;
59  amroutine->amkeytype = InvalidOid;
60 
61  amroutine->ambuild = spgbuild;
62  amroutine->ambuildempty = spgbuildempty;
63  amroutine->aminsert = spginsert;
64  amroutine->ambulkdelete = spgbulkdelete;
65  amroutine->amvacuumcleanup = spgvacuumcleanup;
66  amroutine->amcanreturn = spgcanreturn;
67  amroutine->amcostestimate = spgcostestimate;
68  amroutine->amoptions = spgoptions;
69  amroutine->amproperty = spgproperty;
70  amroutine->ambuildphasename = NULL;
71  amroutine->amvalidate = spgvalidate;
72  amroutine->ambeginscan = spgbeginscan;
73  amroutine->amrescan = spgrescan;
74  amroutine->amgettuple = spggettuple;
75  amroutine->amgetbitmap = spggetbitmap;
76  amroutine->amendscan = spgendscan;
77  amroutine->ammarkpos = NULL;
78  amroutine->amrestrpos = NULL;
79  amroutine->amestimateparallelscan = NULL;
80  amroutine->aminitparallelscan = NULL;
81  amroutine->amparallelrescan = NULL;
82 
83  PG_RETURN_POINTER(amroutine);
84 }
ambeginscan_function ambeginscan
Definition: amapi.h:221
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:351
ambulkdelete_function ambulkdelete
Definition: amapi.h:213
bool amcanmulticol
Definition: amapi.h:183
uint16 amsupport
Definition: amapi.h:173
amgettuple_function amgettuple
Definition: amapi.h:223
bool amcanorderbyop
Definition: amapi.h:177
amproperty_function amproperty
Definition: amapi.h:218
bool spgproperty(Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull)
Definition: spgutils.c:966
IndexBulkDeleteResult * spgvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
Definition: spgvacuum.c:932
amparallelrescan_function amparallelrescan
Definition: amapi.h:232
bool amstorage
Definition: amapi.h:191
void spgrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys, ScanKey orderbys, int norderbys)
Definition: spgscan.c:365
bool ampredlocks
Definition: amapi.h:195
aminsert_function aminsert
Definition: amapi.h:212
bool spginsert(Relation index, Datum *values, bool *isnull, ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique, IndexInfo *indexInfo)
Definition: spginsert.c:207
Oid amkeytype
Definition: amapi.h:201
void spgendscan(IndexScanDesc scan)
Definition: spgscan.c:411
bool amoptionalkey
Definition: amapi.h:185
amvalidate_function amvalidate
Definition: amapi.h:220
bool spgcanreturn(Relation index, int attno)
Definition: spgscan.c:1019
amgetbitmap_function amgetbitmap
Definition: amapi.h:224
IndexBulkDeleteResult * spgbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition: spgvacuum.c:901
ambuild_function ambuild
Definition: amapi.h:210
amoptions_function amoptions
Definition: amapi.h:217
bool amcaninclude
Definition: amapi.h:199
amcostestimate_function amcostestimate
Definition: amapi.h:216
bool amcanunique
Definition: amapi.h:181
amvacuumcleanup_function amvacuumcleanup
Definition: amapi.h:214
amendscan_function amendscan
Definition: amapi.h:225
bool amcanbackward
Definition: amapi.h:179
int64 spggetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
Definition: spgscan.c:889
void spgbuildempty(Relation index)
Definition: spginsert.c:156
amrescan_function amrescan
Definition: amapi.h:222
bool amcanparallel
Definition: amapi.h:197
bytea * spgoptions(Datum reloptions, bool validate)
Definition: spgutils.c:587
bool amsearchnulls
Definition: amapi.h:189
bool amclusterable
Definition: amapi.h:193
bool amsearcharray
Definition: amapi.h:187
#define InvalidOid
Definition: postgres_ext.h:36
void spgcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation, double *indexPages)
Definition: selfuncs.c:6170
#define makeNode(_type_)
Definition: nodes.h:573
ammarkpos_function ammarkpos
Definition: amapi.h:226
bool amcanorder
Definition: amapi.h:175
ambuildphasename_function ambuildphasename
Definition: amapi.h:219
amestimateparallelscan_function amestimateparallelscan
Definition: amapi.h:230
IndexBuildResult * spgbuild(Relation heap, Relation index, IndexInfo *indexInfo)
Definition: spginsert.c:75
uint16 amstrategies
Definition: amapi.h:171
#define SPGISTNProc
Definition: spgist.h:34
bool spgvalidate(Oid opclassoid)
Definition: spgvalidate.c:39
bool spggettuple(IndexScanDesc scan, ScanDirection dir)
Definition: spgscan.c:961
ambuildempty_function ambuildempty
Definition: amapi.h:211
IndexScanDesc spgbeginscan(Relation rel, int keysz, int orderbysz)
Definition: spgscan.c:295
amcanreturn_function amcanreturn
Definition: amapi.h:215
aminitparallelscan_function aminitparallelscan
Definition: amapi.h:231
amrestrpos_function amrestrpos
Definition: amapi.h:227

◆ SpGistGetBuffer()

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

Definition at line 395 of file spgutils.c.

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(), RelationGetTargetPageFreeSpace, ReleaseBuffer(), spgGetCache(), SPGIST_DEFAULT_FILLFACTOR, SPGIST_LEAF, SPGIST_NULLS, SPGIST_PAGE_CAPACITY, SpGistBlockIsFixed, SpGistInitBuffer(), SpGistPageIsDeleted, SpGistPageIsLeaf, SpGistPageStoresNulls, and UnlockReleaseBuffer().

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

396 {
397  SpGistCache *cache = spgGetCache(index);
398  SpGistLastUsedPage *lup;
399 
400  /* Bail out if even an empty page wouldn't meet the demand */
401  if (needSpace > SPGIST_PAGE_CAPACITY)
402  elog(ERROR, "desired SPGiST tuple size is too big");
403 
404  /*
405  * If possible, increase the space request to include relation's
406  * fillfactor. This ensures that when we add unrelated tuples to a page,
407  * we try to keep 100-fillfactor% available for adding tuples that are
408  * related to the ones already on it. But fillfactor mustn't cause an
409  * error for requests that would otherwise be legal.
410  */
411  needSpace += RelationGetTargetPageFreeSpace(index,
413  needSpace = Min(needSpace, SPGIST_PAGE_CAPACITY);
414 
415  /* Get the cache entry for this flags setting */
416  lup = GET_LUP(cache, flags);
417 
418  /* If we have nothing cached, just turn it over to allocNewBuffer */
419  if (lup->blkno == InvalidBlockNumber)
420  {
421  *isNew = true;
422  return allocNewBuffer(index, flags);
423  }
424 
425  /* fixed pages should never be in cache */
427 
428  /* If cached freeSpace isn't enough, don't bother looking at the page */
429  if (lup->freeSpace >= needSpace)
430  {
431  Buffer buffer;
432  Page page;
433 
434  buffer = ReadBuffer(index, lup->blkno);
435 
436  if (!ConditionalLockBuffer(buffer))
437  {
438  /*
439  * buffer is locked by another process, so return a new buffer
440  */
441  ReleaseBuffer(buffer);
442  *isNew = true;
443  return allocNewBuffer(index, flags);
444  }
445 
446  page = BufferGetPage(buffer);
447 
448  if (PageIsNew(page) || SpGistPageIsDeleted(page) || PageIsEmpty(page))
449  {
450  /* OK to initialize the page */
451  uint16 pageflags = 0;
452 
453  if (GBUF_REQ_LEAF(flags))
454  pageflags |= SPGIST_LEAF;
455  if (GBUF_REQ_NULLS(flags))
456  pageflags |= SPGIST_NULLS;
457  SpGistInitBuffer(buffer, pageflags);
458  lup->freeSpace = PageGetExactFreeSpace(page) - needSpace;
459  *isNew = true;
460  return buffer;
461  }
462 
463  /*
464  * Check that page is of right type and has enough space. We must
465  * recheck this since our cache isn't necessarily up to date.
466  */
467  if ((GBUF_REQ_LEAF(flags) ? SpGistPageIsLeaf(page) : !SpGistPageIsLeaf(page)) &&
469  {
470  int freeSpace = PageGetExactFreeSpace(page);
471 
472  if (freeSpace >= needSpace)
473  {
474  /* Success, update freespace info and return the buffer */
475  lup->freeSpace = freeSpace - needSpace;
476  *isNew = false;
477  return buffer;
478  }
479  }
480 
481  /*
482  * fallback to allocation of new buffer
483  */
484  UnlockReleaseBuffer(buffer);
485  }
486 
487  /* No success with cache, so return a new buffer */
488  *isNew = true;
489  return allocNewBuffer(index, flags);
490 }
#define PageIsEmpty(page)
Definition: bufpage.h:222
#define SpGistPageIsLeaf(page)
SpGistCache * spgGetCache(Relation index)
Definition: spgutils.c:99
#define GET_LUP(c, f)
Definition: spgutils.c:316
#define Min(x, y)
Definition: c.h:904
#define SPGIST_NULLS
static Buffer allocNewBuffer(Relation index, int flags)
Definition: spgutils.c:339
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3365
#define SPGIST_DEFAULT_FILLFACTOR
Definition: spgist.h:24
#define GBUF_REQ_NULLS(flags)
unsigned short uint16
Definition: c.h:357
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3388
#define ERROR
Definition: elog.h:43
#define SpGistBlockIsFixed(blkno)
void SpGistInitBuffer(Buffer b, uint16 f)
Definition: spgutils.c:550
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
bool ConditionalLockBuffer(Buffer buffer)
Definition: bufmgr.c:3628
#define RelationGetTargetPageFreeSpace(relation, defaultff)
Definition: rel.h:307
#define SPGIST_PAGE_CAPACITY
#define SpGistPageStoresNulls(page)
#define Assert(condition)
Definition: c.h:732
#define SpGistPageIsDeleted(page)
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:596
#define InvalidBlockNumber
Definition: block.h:33
Size PageGetExactFreeSpace(Page page)
Definition: bufpage.c:632
#define PageIsNew(page)
Definition: bufpage.h:229
#define elog(elevel,...)
Definition: elog.h:226
#define SPGIST_LEAF
int Buffer
Definition: buf.h:23
Pointer Page
Definition: bufpage.h:78
#define GBUF_REQ_LEAF(flags)

◆ SpGistGetTypeSize()

unsigned int SpGistGetTypeSize ( SpGistTypeDesc att,
Datum  datum 
)

Definition at line 599 of file spgutils.c.

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

Referenced by spgdoinsert(), spgFormInnerTuple(), spgFormLeafTuple(), and spgFormNodeTuple().

600 {
601  unsigned int size;
602 
603  if (att->attbyval)
604  size = sizeof(Datum);
605  else if (att->attlen > 0)
606  size = att->attlen;
607  else
608  size = VARSIZE_ANY(datum);
609 
610  return MAXALIGN(size);
611 }
uintptr_t Datum
Definition: postgres.h:367
#define VARSIZE_ANY(PTR)
Definition: postgres.h:335
#define MAXALIGN(LEN)
Definition: c.h:685

◆ SpGistInitBuffer()

void SpGistInitBuffer ( Buffer  b,
uint16  f 
)

Definition at line 550 of file spgutils.c.

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

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

551 {
552  Assert(BufferGetPageSize(b) == BLCKSZ);
554 }
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
#define BufferGetPageSize(buffer)
Definition: bufmgr.h:146
#define Assert(condition)
Definition: c.h:732
void SpGistInitPage(Page page, uint16 f)
Definition: spgutils.c:535

◆ SpGistInitMetapage()

void SpGistInitMetapage ( Page  page)

Definition at line 560 of file spgutils.c.

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().

561 {
562  SpGistMetaPageData *metadata;
563  int i;
564 
566  metadata = SpGistPageGetMeta(page);
567  memset(metadata, 0, sizeof(SpGistMetaPageData));
568  metadata->magicNumber = SPGIST_MAGIC_NUMBER;
569 
570  /* initialize last-used-page cache to empty */
571  for (i = 0; i < SPGIST_CACHED_PAGES; i++)
573 
574  /*
575  * Set pd_lower just past the end of the metadata. This is essential,
576  * because without doing so, metadata will be lost if xlog.c compresses
577  * the page.
578  */
579  ((PageHeader) page)->pd_lower =
580  ((char *) metadata + sizeof(SpGistMetaPageData)) - (char *) page;
581 }
SpGistLastUsedPage cachedPage[SPGIST_CACHED_PAGES]
#define SPGIST_META
#define SPGIST_MAGIC_NUMBER
SpGistLUPCache lastUsedPages
#define InvalidBlockNumber
Definition: block.h:33
#define SPGIST_CACHED_PAGES
#define SpGistPageGetMeta(p)
void SpGistInitPage(Page page, uint16 f)
Definition: spgutils.c:535
int i

◆ SpGistInitPage()

void SpGistInitPage ( Page  page,
uint16  f 
)

Definition at line 535 of file spgutils.c.

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

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

536 {
537  SpGistPageOpaque opaque;
538 
539  PageInit(page, BLCKSZ, MAXALIGN(sizeof(SpGistPageOpaqueData)));
540  opaque = SpGistPageGetOpaque(page);
541  memset(opaque, 0, sizeof(SpGistPageOpaqueData));
542  opaque->flags = f;
543  opaque->spgist_page_id = SPGIST_PAGE_ID;
544 }
#define SPGIST_PAGE_ID
#define MAXALIGN(LEN)
Definition: c.h:685
#define SpGistPageGetOpaque(page)
void PageInit(Page page, Size pageSize, Size specialSize)
Definition: bufpage.c:42

◆ SpGistNewBuffer()

Buffer SpGistNewBuffer ( Relation  index)

Definition at line 211 of file spgutils.c.

References BUFFER_LOCK_EXCLUSIVE, BUFFER_LOCK_UNLOCK, BufferGetPage, ConditionalLockBuffer(), ExclusiveLock, GetFreeIndexPage(), InvalidBlockNumber, LockBuffer(), LockRelationForExtension(), P_NEW, PageIsEmpty, PageIsNew, ReadBuffer(), RELATION_IS_LOCAL, ReleaseBuffer(), SpGistBlockIsFixed, SpGistPageIsDeleted, and UnlockRelationForExtension().

Referenced by allocNewBuffer(), and spgbuild().

212 {
213  Buffer buffer;
214  bool needLock;
215 
216  /* First, try to get a page from FSM */
217  for (;;)
218  {
219  BlockNumber blkno = GetFreeIndexPage(index);
220 
221  if (blkno == InvalidBlockNumber)
222  break; /* nothing known to FSM */
223 
224  /*
225  * The fixed pages shouldn't ever be listed in FSM, but just in case
226  * one is, ignore it.
227  */
228  if (SpGistBlockIsFixed(blkno))
229  continue;
230 
231  buffer = ReadBuffer(index, blkno);
232 
233  /*
234  * We have to guard against the possibility that someone else already
235  * recycled this page; the buffer may be locked if so.
236  */
237  if (ConditionalLockBuffer(buffer))
238  {
239  Page page = BufferGetPage(buffer);
240 
241  if (PageIsNew(page))
242  return buffer; /* OK to use, if never initialized */
243 
244  if (SpGistPageIsDeleted(page) || PageIsEmpty(page))
245  return buffer; /* OK to use */
246 
248  }
249 
250  /* Can't use it, so release buffer and try again */
251  ReleaseBuffer(buffer);
252  }
253 
254  /* Must extend the file */
255  needLock = !RELATION_IS_LOCAL(index);
256  if (needLock)
258 
259  buffer = ReadBuffer(index, P_NEW);
261 
262  if (needLock)
264 
265  return buffer;
266 }
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:86
#define PageIsEmpty(page)
Definition: bufpage.h:222
#define ExclusiveLock
Definition: lockdefs.h:44
#define RELATION_IS_LOCAL(relation)
Definition: rel.h:539
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3365
#define P_NEW
Definition: bufmgr.h:81
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:88
#define SpGistBlockIsFixed(blkno)
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
bool ConditionalLockBuffer(Buffer buffer)
Definition: bufmgr.c:3628
void LockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:402
void UnlockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:452
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3602
BlockNumber GetFreeIndexPage(Relation rel)
Definition: indexfsm.c:38
#define SpGistPageIsDeleted(page)
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:596
#define InvalidBlockNumber
Definition: block.h:33
#define PageIsNew(page)
Definition: bufpage.h:229
int Buffer
Definition: buf.h:23
Pointer Page
Definition: bufpage.h:78

◆ SpGistPageAddNewItem()

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

Definition at line 871 of file spgutils.c.

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().

873 {
874  SpGistPageOpaque opaque = SpGistPageGetOpaque(page);
875  OffsetNumber i,
876  maxoff,
877  offnum;
878 
879  if (opaque->nPlaceholder > 0 &&
880  PageGetExactFreeSpace(page) + SGDTSIZE >= MAXALIGN(size))
881  {
882  /* Try to replace a placeholder */
883  maxoff = PageGetMaxOffsetNumber(page);
884  offnum = InvalidOffsetNumber;
885 
886  for (;;)
887  {
888  if (startOffset && *startOffset != InvalidOffsetNumber)
889  i = *startOffset;
890  else
891  i = FirstOffsetNumber;
892  for (; i <= maxoff; i++)
893  {
895  PageGetItemId(page, i));
896 
897  if (it->tupstate == SPGIST_PLACEHOLDER)
898  {
899  offnum = i;
900  break;
901  }
902  }
903 
904  /* Done if we found a placeholder */
905  if (offnum != InvalidOffsetNumber)
906  break;
907 
908  if (startOffset && *startOffset != InvalidOffsetNumber)
909  {
910  /* Hint was no good, re-search from beginning */
911  *startOffset = InvalidOffsetNumber;
912  continue;
913  }
914 
915  /* Hmm, no placeholder found? */
916  opaque->nPlaceholder = 0;
917  break;
918  }
919 
920  if (offnum != InvalidOffsetNumber)
921  {
922  /* Replace the placeholder tuple */
923  PageIndexTupleDelete(page, offnum);
924 
925  offnum = PageAddItem(page, item, size, offnum, false, false);
926 
927  /*
928  * We should not have failed given the size check at the top of
929  * the function, but test anyway. If we did fail, we must PANIC
930  * because we've already deleted the placeholder tuple, and
931  * there's no other way to keep the damage from getting to disk.
932  */
933  if (offnum != InvalidOffsetNumber)
934  {
935  Assert(opaque->nPlaceholder > 0);
936  opaque->nPlaceholder--;
937  if (startOffset)
938  *startOffset = offnum + 1;
939  }
940  else
941  elog(PANIC, "failed to add item of size %u to SPGiST index page",
942  (int) size);
943 
944  return offnum;
945  }
946  }
947 
948  /* No luck in replacing a placeholder, so just add it to the page */
949  offnum = PageAddItem(page, item, size,
950  InvalidOffsetNumber, false, false);
951 
952  if (offnum == InvalidOffsetNumber && !errorOK)
953  elog(ERROR, "failed to add item of size %u to SPGiST index page",
954  (int) size);
955 
956  return offnum;
957 }
#define SGDTSIZE
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
Definition: bufpage.c:726
#define SPGIST_PLACEHOLDER
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:416
#define PANIC
Definition: elog.h:53
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
uint16 OffsetNumber
Definition: off.h:24
#define ERROR
Definition: elog.h:43
#define FirstOffsetNumber
Definition: off.h:27
SpGistDeadTupleData * SpGistDeadTuple
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
unsigned int tupstate
#define InvalidOffsetNumber
Definition: off.h:26
#define Assert(condition)
Definition: c.h:732
#define MAXALIGN(LEN)
Definition: c.h:685
Size PageGetExactFreeSpace(Page page)
Definition: bufpage.c:632
#define SpGistPageGetOpaque(page)
#define elog(elevel,...)
Definition: elog.h:226
int i
#define PageGetItem(page, itemId)
Definition: bufpage.h:340

◆ SpGistSetLastUsedPage()

void SpGistSetLastUsedPage ( Relation  index,
Buffer  buffer 
)

Definition at line 500 of file spgutils.c.

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().

501 {
502  SpGistCache *cache = spgGetCache(index);
503  SpGistLastUsedPage *lup;
504  int freeSpace;
505  Page page = BufferGetPage(buffer);
506  BlockNumber blkno = BufferGetBlockNumber(buffer);
507  int flags;
508 
509  /* Never enter fixed pages (root pages) in cache, though */
510  if (SpGistBlockIsFixed(blkno))
511  return;
512 
513  if (SpGistPageIsLeaf(page))
514  flags = GBUF_LEAF;
515  else
516  flags = GBUF_INNER_PARITY(blkno);
517  if (SpGistPageStoresNulls(page))
518  flags |= GBUF_NULLS;
519 
520  lup = GET_LUP(cache, flags);
521 
522  freeSpace = PageGetExactFreeSpace(page);
523  if (lup->blkno == InvalidBlockNumber || lup->blkno == blkno ||
524  lup->freeSpace < freeSpace)
525  {
526  lup->blkno = blkno;
527  lup->freeSpace = freeSpace;
528  }
529 }
#define SpGistPageIsLeaf(page)
SpGistCache * spgGetCache(Relation index)
Definition: spgutils.c:99
#define GET_LUP(c, f)
Definition: spgutils.c:316
uint32 BlockNumber
Definition: block.h:31
#define GBUF_INNER_PARITY(x)
#define SpGistBlockIsFixed(blkno)
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
#define GBUF_LEAF
#define SpGistPageStoresNulls(page)
#define InvalidBlockNumber
Definition: block.h:33
#define GBUF_NULLS
Size PageGetExactFreeSpace(Page page)
Definition: bufpage.c:632
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2613
Pointer Page
Definition: bufpage.h:78

◆ SpGistUpdateMetaPage()

void SpGistUpdateMetaPage ( Relation  index)

Definition at line 276 of file spgutils.c.

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

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

277 {
278  SpGistCache *cache = (SpGistCache *) index->rd_amcache;
279 
280  if (cache != NULL)
281  {
282  Buffer metabuffer;
283 
284  metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO);
285 
286  if (ConditionalLockBuffer(metabuffer))
287  {
288  Page metapage = BufferGetPage(metabuffer);
289  SpGistMetaPageData *metadata = SpGistPageGetMeta(metapage);
290 
291  metadata->lastUsedPages = cache->lastUsedPages;
292 
293  /*
294  * Set pd_lower just past the end of the metadata. This is
295  * essential, because without doing so, metadata will be lost if
296  * xlog.c compresses the page. (We must do this here because
297  * pre-v11 versions of PG did not set the metapage's pd_lower
298  * correctly, so a pg_upgraded index might contain the wrong
299  * value.)
300  */
301  ((PageHeader) metapage)->pd_lower =
302  ((char *) metadata + sizeof(SpGistMetaPageData)) - (char *) metapage;
303 
304  MarkBufferDirty(metabuffer);
305  UnlockReleaseBuffer(metabuffer);
306  }
307  else
308  {
309  ReleaseBuffer(metabuffer);
310  }
311  }
312 }
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1458
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3365
SpGistLUPCache lastUsedPages
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3388
#define SPGIST_METAPAGE_BLKNO
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
bool ConditionalLockBuffer(Buffer buffer)
Definition: bufmgr.c:3628
SpGistLUPCache lastUsedPages
PageHeaderData * PageHeader
Definition: bufpage.h:166
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:596
#define SpGistPageGetMeta(p)
void * rd_amcache
Definition: rel.h:179
int Buffer
Definition: buf.h:23
Pointer Page
Definition: bufpage.h:78

◆ spgoptions()

bytea* spgoptions ( Datum  reloptions,
bool  validate 
)

Definition at line 587 of file spgutils.c.

References default_reloptions(), and RELOPT_KIND_SPGIST.

Referenced by spghandler().

588 {
589  return default_reloptions(reloptions, validate, RELOPT_KIND_SPGIST);
590 }
bytea * default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
Definition: reloptions.c:1475

◆ spgproperty()

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

Definition at line 966 of file spgutils.c.

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, SearchSysCacheList1, and catctup::tuple.

Referenced by spghandler().

969 {
970  Oid opclass,
971  opfamily,
972  opcintype;
973  CatCList *catlist;
974  int i;
975 
976  /* Only answer column-level inquiries */
977  if (attno == 0)
978  return false;
979 
980  switch (prop)
981  {
983  break;
984  default:
985  return false;
986  }
987 
988  /*
989  * Currently, SP-GiST distance-ordered scans require that there be a
990  * distance operator in the opclass with the default types. So we assume
991  * that if such a operator exists, then there's a reason for it.
992  */
993 
994  /* First we need to know the column's opclass. */
995  opclass = get_index_column_opclass(index_oid, attno);
996  if (!OidIsValid(opclass))
997  {
998  *isnull = true;
999  return true;
1000  }
1001 
1002  /* Now look up the opclass family and input datatype. */
1003  if (!get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
1004  {
1005  *isnull = true;
1006  return true;
1007  }
1008 
1009  /* And now we can check whether the operator is provided. */
1011  ObjectIdGetDatum(opfamily));
1012 
1013  *res = false;
1014 
1015  for (i = 0; i < catlist->n_members; i++)
1016  {
1017  HeapTuple amoptup = &catlist->members[i]->tuple;
1018  Form_pg_amop amopform = (Form_pg_amop) GETSTRUCT(amoptup);
1019 
1020  if (amopform->amoppurpose == AMOP_ORDER &&
1021  (amopform->amoplefttype == opcintype ||
1022  amopform->amoprighttype == opcintype) &&
1023  opfamily_can_sort_type(amopform->amopsortfamily,
1024  get_op_rettype(amopform->amopopr)))
1025  {
1026  *res = true;
1027  break;
1028  }
1029  }
1030 
1031  ReleaseSysCacheList(catlist);
1032 
1033  *isnull = false;
1034 
1035  return true;
1036 }
int n_members
Definition: catcache.h:176
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
bool opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid)
Definition: amvalidate.c:217
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:638
Oid get_op_rettype(Oid opno)
Definition: lsyscache.c:1140
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:178
bool get_opclass_opfamily_and_input_type(Oid opclass, Oid *opfamily, Oid *opcintype)
Definition: lsyscache.c:1064
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:210
#define ReleaseSysCacheList(x)
Definition: syscache.h:217
Oid get_index_column_opclass(Oid index_oid, int attno)
Definition: lsyscache.c:3164
FormData_pg_amop * Form_pg_amop
Definition: pg_amop.h:88
int i
HeapTupleData tuple
Definition: catcache.h:121