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 "commands/vacuum.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 321 of file spgutils.c.

Referenced by SpGistGetBuffer(), and SpGistSetLastUsedPage().

Function Documentation

◆ allocNewBuffer()

static Buffer allocNewBuffer ( Relation  index,
int  flags 
)
static

Definition at line 344 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().

345 {
346  SpGistCache *cache = spgGetCache(index);
347  uint16 pageflags = 0;
348 
349  if (GBUF_REQ_LEAF(flags))
350  pageflags |= SPGIST_LEAF;
351  if (GBUF_REQ_NULLS(flags))
352  pageflags |= SPGIST_NULLS;
353 
354  for (;;)
355  {
356  Buffer buffer;
357 
358  buffer = SpGistNewBuffer(index);
359  SpGistInitBuffer(buffer, pageflags);
360 
361  if (pageflags & SPGIST_LEAF)
362  {
363  /* Leaf pages have no parity concerns, so just use it */
364  return buffer;
365  }
366  else
367  {
368  BlockNumber blkno = BufferGetBlockNumber(buffer);
369  int blkFlags = GBUF_INNER_PARITY(blkno);
370 
371  if ((flags & GBUF_PARITY_MASK) == blkFlags)
372  {
373  /* Page has right parity, use it */
374  return buffer;
375  }
376  else
377  {
378  /* Page has wrong parity, record it in cache and try again */
379  if (pageflags & SPGIST_NULLS)
380  blkFlags |= GBUF_NULLS;
381  cache->lastUsedPages.cachedPage[blkFlags].blkno = blkno;
382  cache->lastUsedPages.cachedPage[blkFlags].freeSpace =
384  UnlockReleaseBuffer(buffer);
385  }
386  }
387  }
388 }
SpGistCache * spgGetCache(Relation index)
Definition: spgutils.c:104
#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:366
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3506
#define GBUF_INNER_PARITY(x)
void SpGistInitBuffer(Buffer b, uint16 f)
Definition: spgutils.c:554
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
Buffer SpGistNewBuffer(Relation index)
Definition: spgutils.c:216
#define GBUF_PARITY_MASK
#define GBUF_NULLS
Size PageGetExactFreeSpace(Page page)
Definition: bufpage.c:625
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2633
#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 93 of file spgutils.c.

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

Referenced by spgGetCache().

94 {
95  desc->type = type;
96  get_typlenbyval(type, &desc->attlen, &desc->attbyval);
97 }
void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval)
Definition: lsyscache.c:2139

◆ initSpGistState()

void initSpGistState ( SpGistState state,
Relation  index 
)

Definition at line 186 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().

187 {
188  SpGistCache *cache;
189 
190  /* Get cached static information about index */
191  cache = spgGetCache(index);
192 
193  state->config = cache->config;
194  state->attType = cache->attType;
195  state->attLeafType = cache->attLeafType;
196  state->attPrefixType = cache->attPrefixType;
197  state->attLabelType = cache->attLabelType;
198 
199  /* Make workspace for constructing dead tuples */
201 
202  /* Set XID to use in redirection tuples */
203  state->myXid = GetTopTransactionIdIfAny();
204 
205  /* Assume we're not in an index build (spgbuild will override) */
206  state->isBuild = false;
207 }
SpGistTypeDesc attLeafType
SpGistTypeDesc attPrefixType
SpGistCache * spgGetCache(Relation index)
Definition: spgutils.c:104
#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 629 of file spgutils.c.

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

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

630 {
631  unsigned int size;
632 
633  if (att->attbyval)
634  {
635  memcpy(target, &datum, sizeof(Datum));
636  }
637  else
638  {
639  size = (att->attlen > 0) ? att->attlen : VARSIZE_ANY(datum);
640  memcpy(target, DatumGetPointer(datum), size);
641  }
642 }
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 840 of file spgutils.c.

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

Referenced by spgdoinsert(), and spgInitInnerConsistentIn().

841 {
842  Datum *nodeLabels;
843  int i;
844  SpGistNodeTuple node;
845 
846  /* Either all the labels must be NULL, or none. */
847  node = SGITNODEPTR(innerTuple);
848  if (IndexTupleHasNulls(node))
849  {
850  SGITITERATE(innerTuple, i, node)
851  {
852  if (!IndexTupleHasNulls(node))
853  elog(ERROR, "some but not all node labels are null in SPGiST inner tuple");
854  }
855  /* They're all null, so just return NULL */
856  return NULL;
857  }
858  else
859  {
860  nodeLabels = (Datum *) palloc(sizeof(Datum) * innerTuple->nNodes);
861  SGITITERATE(innerTuple, i, node)
862  {
863  if (IndexTupleHasNulls(node))
864  elog(ERROR, "some but not all node labels are null in SPGiST inner tuple");
865  nodeLabels[i] = SGNTDATUM(node, state);
866  }
867  return nodeLabels;
868  }
869 }
#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:214
int i
#define SGNTDATUM(x, s)

◆ spgFormDeadTuple()

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

Definition at line 810 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().

812 {
814 
815  tuple->tupstate = tupstate;
816  tuple->size = SGDTSIZE;
818 
819  if (tupstate == SPGIST_REDIRECT)
820  {
821  ItemPointerSet(&tuple->pointer, blkno, offnum);
823  tuple->xid = state->myXid;
824  }
825  else
826  {
828  tuple->xid = InvalidTransactionId;
829  }
830 
831  return tuple;
832 }
#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:738
#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 727 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().

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

650 {
651  SpGistLeafTuple tup;
652  unsigned int size;
653 
654  /* compute space needed (note result is already maxaligned) */
655  size = SGLTHDRSZ;
656  if (!isnull)
657  size += SpGistGetTypeSize(&state->attLeafType, datum);
658 
659  /*
660  * Ensure that we can replace the tuple with a dead tuple later. This
661  * test is unnecessary when !isnull, but let's be safe.
662  */
663  if (size < SGDTSIZE)
664  size = SGDTSIZE;
665 
666  /* OK, form the tuple */
667  tup = (SpGistLeafTuple) palloc0(size);
668 
669  tup->size = size;
671  tup->heapPtr = *heapPtr;
672  if (!isnull)
673  memcpyDatum(SGLTDATAPTR(tup), &state->attLeafType, datum);
674 
675  return tup;
676 }
#define SGDTSIZE
SpGistTypeDesc attLeafType
#define SGLTDATAPTR(x)
#define SGLTHDRSZ
unsigned int SpGistGetTypeSize(SpGistTypeDesc *att, Datum datum)
Definition: spgutils.c:611
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:629
ItemPointerData heapPtr
SpGistLeafTupleData * SpGistLeafTuple

◆ spgFormNodeTuple()

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

Definition at line 685 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().

686 {
687  SpGistNodeTuple tup;
688  unsigned int size;
689  unsigned short infomask = 0;
690 
691  /* compute space needed (note result is already maxaligned) */
692  size = SGNTHDRSZ;
693  if (!isnull)
694  size += SpGistGetTypeSize(&state->attLabelType, label);
695 
696  /*
697  * Here we make sure that the size will fit in the field reserved for it
698  * in t_info.
699  */
700  if ((size & INDEX_SIZE_MASK) != size)
701  ereport(ERROR,
702  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
703  errmsg("index row requires %zu bytes, maximum size is %zu",
704  (Size) size, (Size) INDEX_SIZE_MASK)));
705 
706  tup = (SpGistNodeTuple) palloc0(size);
707 
708  if (isnull)
709  infomask |= INDEX_NULL_MASK;
710  /* we don't bother setting the INDEX_VAR_MASK bit */
711  infomask |= size;
712  tup->t_info = infomask;
713 
714  /* The TID field will be filled in later */
716 
717  if (!isnull)
718  memcpyDatum(SGNTDATAPTR(tup), &state->attLabelType, label);
719 
720  return tup;
721 }
ItemPointerData t_tid
Definition: itup.h:37
#define INDEX_SIZE_MASK
Definition: itup.h:65
int errcode(int sqlerrcode)
Definition: elog.c:610
SpGistTypeDesc attLabelType
#define SGNTHDRSZ
#define ERROR
Definition: elog.h:43
#define INDEX_NULL_MASK
Definition: itup.h:69
unsigned int SpGistGetTypeSize(SpGistTypeDesc *att, Datum datum)
Definition: spgutils.c:611
void * palloc0(Size size)
Definition: mcxt.c:980
static char * label
#define ereport(elevel,...)
Definition: elog.h:144
#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:629
int errmsg(const char *fmt,...)
Definition: elog.c:824
unsigned short t_info
Definition: itup.h:49
SpGistNodeTupleData * SpGistNodeTuple

◆ spgGetCache()

SpGistCache* spgGetCache ( Relation  index)

Definition at line 104 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().

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

◆ spghandler()

Datum spghandler ( PG_FUNCTION_ARGS  )

Definition at line 41 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::amoptsprocnum, IndexAmRoutine::amparallelrescan, IndexAmRoutine::amparallelvacuumoptions, IndexAmRoutine::ampredlocks, IndexAmRoutine::amproperty, IndexAmRoutine::amrescan, IndexAmRoutine::amrestrpos, IndexAmRoutine::amsearcharray, IndexAmRoutine::amsearchnulls, IndexAmRoutine::amstorage, IndexAmRoutine::amstrategies, IndexAmRoutine::amsupport, IndexAmRoutine::amusemaintenanceworkmem, IndexAmRoutine::amvacuumcleanup, IndexAmRoutine::amvalidate, InvalidOid, makeNode, PG_RETURN_POINTER, 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.

42 {
44 
45  amroutine->amstrategies = 0;
46  amroutine->amsupport = SPGISTNProc;
48  amroutine->amcanorder = false;
49  amroutine->amcanorderbyop = true;
50  amroutine->amcanbackward = false;
51  amroutine->amcanunique = false;
52  amroutine->amcanmulticol = false;
53  amroutine->amoptionalkey = true;
54  amroutine->amsearcharray = false;
55  amroutine->amsearchnulls = true;
56  amroutine->amstorage = false;
57  amroutine->amclusterable = false;
58  amroutine->ampredlocks = false;
59  amroutine->amcanparallel = false;
60  amroutine->amcaninclude = false;
61  amroutine->amusemaintenanceworkmem = false;
62  amroutine->amparallelvacuumoptions =
64  amroutine->amkeytype = InvalidOid;
65 
66  amroutine->ambuild = spgbuild;
67  amroutine->ambuildempty = spgbuildempty;
68  amroutine->aminsert = spginsert;
69  amroutine->ambulkdelete = spgbulkdelete;
70  amroutine->amvacuumcleanup = spgvacuumcleanup;
71  amroutine->amcanreturn = spgcanreturn;
72  amroutine->amcostestimate = spgcostestimate;
73  amroutine->amoptions = spgoptions;
74  amroutine->amproperty = spgproperty;
75  amroutine->ambuildphasename = NULL;
76  amroutine->amvalidate = spgvalidate;
77  amroutine->ambeginscan = spgbeginscan;
78  amroutine->amrescan = spgrescan;
79  amroutine->amgettuple = spggettuple;
80  amroutine->amgetbitmap = spggetbitmap;
81  amroutine->amendscan = spgendscan;
82  amroutine->ammarkpos = NULL;
83  amroutine->amrestrpos = NULL;
84  amroutine->amestimateparallelscan = NULL;
85  amroutine->aminitparallelscan = NULL;
86  amroutine->amparallelrescan = NULL;
87 
88  PG_RETURN_POINTER(amroutine);
89 }
ambeginscan_function ambeginscan
Definition: amapi.h:227
uint8 amparallelvacuumoptions
Definition: amapi.h:205
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:360
ambulkdelete_function ambulkdelete
Definition: amapi.h:219
bool amcanmulticol
Definition: amapi.h:185
uint16 amsupport
Definition: amapi.h:173
#define SPGIST_OPTIONS_PROC
Definition: spgist.h:29
amgettuple_function amgettuple
Definition: amapi.h:229
bool amcanorderbyop
Definition: amapi.h:179
amproperty_function amproperty
Definition: amapi.h:224
bool spgproperty(Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull)
Definition: spgutils.c:978
IndexBulkDeleteResult * spgvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
Definition: spgvacuum.c:932
amparallelrescan_function amparallelrescan
Definition: amapi.h:238
bool amstorage
Definition: amapi.h:193
void spgrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys, ScanKey orderbys, int norderbys)
Definition: spgscan.c:365
bool ampredlocks
Definition: amapi.h:197
aminsert_function aminsert
Definition: amapi.h:218
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:207
void spgendscan(IndexScanDesc scan)
Definition: spgscan.c:411
bool amoptionalkey
Definition: amapi.h:187
amvalidate_function amvalidate
Definition: amapi.h:226
bool spgcanreturn(Relation index, int attno)
Definition: spgscan.c:1018
amgetbitmap_function amgetbitmap
Definition: amapi.h:230
IndexBulkDeleteResult * spgbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition: spgvacuum.c:901
ambuild_function ambuild
Definition: amapi.h:216
amoptions_function amoptions
Definition: amapi.h:223
bool amcaninclude
Definition: amapi.h:201
amcostestimate_function amcostestimate
Definition: amapi.h:222
bool amcanunique
Definition: amapi.h:183
amvacuumcleanup_function amvacuumcleanup
Definition: amapi.h:220
amendscan_function amendscan
Definition: amapi.h:231
bool amcanbackward
Definition: amapi.h:181
int64 spggetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
Definition: spgscan.c:888
void spgbuildempty(Relation index)
Definition: spginsert.c:156
amrescan_function amrescan
Definition: amapi.h:228
bool amcanparallel
Definition: amapi.h:199
bytea * spgoptions(Datum reloptions, bool validate)
Definition: spgutils.c:591
bool amsearchnulls
Definition: amapi.h:191
bool amclusterable
Definition: amapi.h:195
bool amsearcharray
Definition: amapi.h:189
#define InvalidOid
Definition: postgres_ext.h:36
bool amusemaintenanceworkmem
Definition: amapi.h:203
void spgcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation, double *indexPages)
Definition: selfuncs.c:6641
#define makeNode(_type_)
Definition: nodes.h:577
#define VACUUM_OPTION_PARALLEL_COND_CLEANUP
Definition: vacuum.h:52
ammarkpos_function ammarkpos
Definition: amapi.h:232
bool amcanorder
Definition: amapi.h:177
ambuildphasename_function ambuildphasename
Definition: amapi.h:225
#define VACUUM_OPTION_PARALLEL_BULKDEL
Definition: vacuum.h:45
amestimateparallelscan_function amestimateparallelscan
Definition: amapi.h:236
IndexBuildResult * spgbuild(Relation heap, Relation index, IndexInfo *indexInfo)
Definition: spginsert.c:75
uint16 amstrategies
Definition: amapi.h:171
#define SPGISTNProc
Definition: spgist.h:31
bool spgvalidate(Oid opclassoid)
Definition: spgvalidate.c:39
uint16 amoptsprocnum
Definition: amapi.h:175
bool spggettuple(IndexScanDesc scan, ScanDirection dir)
Definition: spgscan.c:960
ambuildempty_function ambuildempty
Definition: amapi.h:217
IndexScanDesc spgbeginscan(Relation rel, int keysz, int orderbysz)
Definition: spgscan.c:295
amcanreturn_function amcanreturn
Definition: amapi.h:221
aminitparallelscan_function aminitparallelscan
Definition: amapi.h:237
amrestrpos_function amrestrpos
Definition: amapi.h:233

◆ SpGistGetBuffer()

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

Definition at line 400 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(), 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().

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

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

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

612 {
613  unsigned int size;
614 
615  if (att->attbyval)
616  size = sizeof(Datum);
617  else if (att->attlen > 0)
618  size = att->attlen;
619  else
620  size = VARSIZE_ANY(datum);
621 
622  return MAXALIGN(size);
623 }
uintptr_t Datum
Definition: postgres.h:367
#define VARSIZE_ANY(PTR)
Definition: postgres.h:335
#define MAXALIGN(LEN)
Definition: c.h:691

◆ SpGistInitBuffer()

void SpGistInitBuffer ( Buffer  b,
uint16  f 
)

Definition at line 554 of file spgutils.c.

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

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

555 {
556  Assert(BufferGetPageSize(b) == BLCKSZ);
558 }
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define BufferGetPageSize(buffer)
Definition: bufmgr.h:156
#define Assert(condition)
Definition: c.h:738
void SpGistInitPage(Page page, uint16 f)
Definition: spgutils.c:539

◆ SpGistInitMetapage()

void SpGistInitMetapage ( Page  page)

Definition at line 564 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().

565 {
566  SpGistMetaPageData *metadata;
567  int i;
568 
570  metadata = SpGistPageGetMeta(page);
571  memset(metadata, 0, sizeof(SpGistMetaPageData));
572  metadata->magicNumber = SPGIST_MAGIC_NUMBER;
573 
574  /* initialize last-used-page cache to empty */
575  for (i = 0; i < SPGIST_CACHED_PAGES; i++)
577 
578  /*
579  * Set pd_lower just past the end of the metadata. This is essential,
580  * because without doing so, metadata will be lost if xlog.c compresses
581  * the page.
582  */
583  ((PageHeader) page)->pd_lower =
584  ((char *) metadata + sizeof(SpGistMetaPageData)) - (char *) page;
585 }
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:539
int i

◆ SpGistInitPage()

void SpGistInitPage ( Page  page,
uint16  f 
)

Definition at line 539 of file spgutils.c.

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

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

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

◆ SpGistNewBuffer()

Buffer SpGistNewBuffer ( Relation  index)

Definition at line 216 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().

217 {
218  Buffer buffer;
219  bool needLock;
220 
221  /* First, try to get a page from FSM */
222  for (;;)
223  {
224  BlockNumber blkno = GetFreeIndexPage(index);
225 
226  if (blkno == InvalidBlockNumber)
227  break; /* nothing known to FSM */
228 
229  /*
230  * The fixed pages shouldn't ever be listed in FSM, but just in case
231  * one is, ignore it.
232  */
233  if (SpGistBlockIsFixed(blkno))
234  continue;
235 
236  buffer = ReadBuffer(index, blkno);
237 
238  /*
239  * We have to guard against the possibility that someone else already
240  * recycled this page; the buffer may be locked if so.
241  */
242  if (ConditionalLockBuffer(buffer))
243  {
244  Page page = BufferGetPage(buffer);
245 
246  if (PageIsNew(page))
247  return buffer; /* OK to use, if never initialized */
248 
249  if (SpGistPageIsDeleted(page) || PageIsEmpty(page))
250  return buffer; /* OK to use */
251 
253  }
254 
255  /* Can't use it, so release buffer and try again */
256  ReleaseBuffer(buffer);
257  }
258 
259  /* Must extend the file */
260  needLock = !RELATION_IS_LOCAL(index);
261  if (needLock)
263 
264  buffer = ReadBuffer(index, P_NEW);
266 
267  if (needLock)
269 
270  return buffer;
271 }
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
#define PageIsEmpty(page)
Definition: bufpage.h:222
#define ExclusiveLock
Definition: lockdefs.h:44
#define RELATION_IS_LOCAL(relation)
Definition: rel.h:583
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3483
#define P_NEW
Definition: bufmgr.h:91
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:98
#define SpGistBlockIsFixed(blkno)
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
bool ConditionalLockBuffer(Buffer buffer)
Definition: bufmgr.c:3748
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:3722
BlockNumber GetFreeIndexPage(Relation rel)
Definition: indexfsm.c:38
#define SpGistPageIsDeleted(page)
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:606
#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 883 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().

885 {
886  SpGistPageOpaque opaque = SpGistPageGetOpaque(page);
887  OffsetNumber i,
888  maxoff,
889  offnum;
890 
891  if (opaque->nPlaceholder > 0 &&
892  PageGetExactFreeSpace(page) + SGDTSIZE >= MAXALIGN(size))
893  {
894  /* Try to replace a placeholder */
895  maxoff = PageGetMaxOffsetNumber(page);
896  offnum = InvalidOffsetNumber;
897 
898  for (;;)
899  {
900  if (startOffset && *startOffset != InvalidOffsetNumber)
901  i = *startOffset;
902  else
903  i = FirstOffsetNumber;
904  for (; i <= maxoff; i++)
905  {
907  PageGetItemId(page, i));
908 
909  if (it->tupstate == SPGIST_PLACEHOLDER)
910  {
911  offnum = i;
912  break;
913  }
914  }
915 
916  /* Done if we found a placeholder */
917  if (offnum != InvalidOffsetNumber)
918  break;
919 
920  if (startOffset && *startOffset != InvalidOffsetNumber)
921  {
922  /* Hint was no good, re-search from beginning */
923  *startOffset = InvalidOffsetNumber;
924  continue;
925  }
926 
927  /* Hmm, no placeholder found? */
928  opaque->nPlaceholder = 0;
929  break;
930  }
931 
932  if (offnum != InvalidOffsetNumber)
933  {
934  /* Replace the placeholder tuple */
935  PageIndexTupleDelete(page, offnum);
936 
937  offnum = PageAddItem(page, item, size, offnum, false, false);
938 
939  /*
940  * We should not have failed given the size check at the top of
941  * the function, but test anyway. If we did fail, we must PANIC
942  * because we've already deleted the placeholder tuple, and
943  * there's no other way to keep the damage from getting to disk.
944  */
945  if (offnum != InvalidOffsetNumber)
946  {
947  Assert(opaque->nPlaceholder > 0);
948  opaque->nPlaceholder--;
949  if (startOffset)
950  *startOffset = offnum + 1;
951  }
952  else
953  elog(PANIC, "failed to add item of size %u to SPGiST index page",
954  (int) size);
955 
956  return offnum;
957  }
958  }
959 
960  /* No luck in replacing a placeholder, so just add it to the page */
961  offnum = PageAddItem(page, item, size,
962  InvalidOffsetNumber, false, false);
963 
964  if (offnum == InvalidOffsetNumber && !errorOK)
965  elog(ERROR, "failed to add item of size %u to SPGiST index page",
966  (int) size);
967 
968  return offnum;
969 }
#define SGDTSIZE
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
Definition: bufpage.c:719
#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:738
#define MAXALIGN(LEN)
Definition: c.h:691
Size PageGetExactFreeSpace(Page page)
Definition: bufpage.c:625
#define SpGistPageGetOpaque(page)
#define elog(elevel,...)
Definition: elog.h:214
int i
#define PageGetItem(page, itemId)
Definition: bufpage.h:340

◆ SpGistSetLastUsedPage()

void SpGistSetLastUsedPage ( Relation  index,
Buffer  buffer 
)

Definition at line 504 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().

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

◆ SpGistUpdateMetaPage()

void SpGistUpdateMetaPage ( Relation  index)

Definition at line 281 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().

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

◆ spgoptions()

bytea* spgoptions ( Datum  reloptions,
bool  validate 
)

Definition at line 591 of file spgutils.c.

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

Referenced by spghandler().

592 {
593  static const relopt_parse_elt tab[] = {
595  };
596 
597  return (bytea *) build_reloptions(reloptions, validate,
599  sizeof(SpGistOptions),
600  tab, lengthof(tab));
601 
602 }
#define lengthof(array)
Definition: c.h:668
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:1887
int fillfactor
Definition: pgbench.c:159
Definition: c.h:555
#define offsetof(type, field)
Definition: c.h:661

◆ spgproperty()

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

Definition at line 978 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().

981 {
982  Oid opclass,
983  opfamily,
984  opcintype;
985  CatCList *catlist;
986  int i;
987 
988  /* Only answer column-level inquiries */
989  if (attno == 0)
990  return false;
991 
992  switch (prop)
993  {
995  break;
996  default:
997  return false;
998  }
999 
1000  /*
1001  * Currently, SP-GiST distance-ordered scans require that there be a
1002  * distance operator in the opclass with the default types. So we assume
1003  * that if such a operator exists, then there's a reason for it.
1004  */
1005 
1006  /* First we need to know the column's opclass. */
1007  opclass = get_index_column_opclass(index_oid, attno);
1008  if (!OidIsValid(opclass))
1009  {
1010  *isnull = true;
1011  return true;
1012  }
1013 
1014  /* Now look up the opclass family and input datatype. */
1015  if (!get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
1016  {
1017  *isnull = true;
1018  return true;
1019  }
1020 
1021  /* And now we can check whether the operator is provided. */
1023  ObjectIdGetDatum(opfamily));
1024 
1025  *res = false;
1026 
1027  for (i = 0; i < catlist->n_members; i++)
1028  {
1029  HeapTuple amoptup = &catlist->members[i]->tuple;
1030  Form_pg_amop amopform = (Form_pg_amop) GETSTRUCT(amoptup);
1031 
1032  if (amopform->amoppurpose == AMOP_ORDER &&
1033  (amopform->amoplefttype == opcintype ||
1034  amopform->amoprighttype == opcintype) &&
1035  opfamily_can_sort_type(amopform->amopsortfamily,
1036  get_op_rettype(amopform->amopopr)))
1037  {
1038  *res = true;
1039  break;
1040  }
1041  }
1042 
1043  ReleaseSysCacheList(catlist);
1044 
1045  *isnull = false;
1046 
1047  return true;
1048 }
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:228
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:644
Oid get_op_rettype(Oid opno)
Definition: lsyscache.c:1250
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:1174
#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:3287
FormData_pg_amop * Form_pg_amop
Definition: pg_amop.h:88
int i
HeapTupleData tuple
Definition: catcache.h:121