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

Go to the source code of this file.

Macros

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

Functions

Datum spghandler (PG_FUNCTION_ARGS)
 
static Oid GetIndexInputType (Relation index, AttrNumber indexcol)
 
static void fillTypeDesc (SpGistTypeDesc *desc, Oid type)
 
SpGistCache * spgGetCache (Relation index)
 
TupleDesc getSpGistTupleDesc (Relation index, SpGistTypeDesc *keyType)
 
void initSpGistState (SpGistState *state, Relation index)
 
Buffer SpGistNewBuffer (Relation index)
 
void SpGistUpdateMetaPage (Relation index)
 
static Buffer allocNewBuffer (Relation index, int flags)
 
Buffer SpGistGetBuffer (Relation index, int flags, int needSpace, bool *isNew)
 
void SpGistSetLastUsedPage (Relation index, Buffer buffer)
 
void SpGistInitPage (Page page, uint16 f)
 
void SpGistInitBuffer (Buffer b, uint16 f)
 
void SpGistInitMetapage (Page page)
 
bytea * spgoptions (Datum reloptions, bool validate)
 
unsigned int SpGistGetInnerTypeSize (SpGistTypeDesc *att, Datum datum)
 
static void memcpyInnerDatum (void *target, SpGistTypeDesc *att, Datum datum)
 
Size SpGistGetLeafTupleSize (TupleDesc tupleDescriptor, const Datum *datums, const bool *isnulls)
 
SpGistLeafTuple spgFormLeafTuple (SpGistState *state, const ItemPointerData *heapPtr, const Datum *datums, const bool *isnulls)
 
SpGistNodeTuple spgFormNodeTuple (SpGistState *state, Datum label, bool isnull)
 
SpGistInnerTuple spgFormInnerTuple (SpGistState *state, bool hasPrefix, Datum prefix, int nNodes, SpGistNodeTuple *nodes)
 
SpGistDeadTuple spgFormDeadTuple (SpGistState *state, int tupstate, BlockNumber blkno, OffsetNumber offnum)
 
void spgDeformLeafTuple (SpGistLeafTuple tup, TupleDesc tupleDescriptor, Datum *datums, bool *isnulls, bool keyColumnIsNull)
 
Datum * spgExtractNodeLabels (SpGistState *state, SpGistInnerTuple innerTuple)
 
OffsetNumber SpGistPageAddNewItem (SpGistState *state, Page page, const void *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,
  f 
)    (&(c)->lastUsedPages.cachedPage[((unsigned int) (f)) % SPGIST_CACHED_PAGES])

Definition at line 489 of file spgutils.c.

Function Documentation

◆ allocNewBuffer()

static Buffer allocNewBuffer ( Relation  index,
int  flags 
)
static

Definition at line 512 of file spgutils.c.

513{
514 SpGistCache *cache = spgGetCache(index);
515 uint16 pageflags = 0;
516
517 if (GBUF_REQ_LEAF(flags))
519 if (GBUF_REQ_NULLS(flags))
521
522 for (;;)
523 {
524 Buffer buffer;
525
526 buffer = SpGistNewBuffer(index);
528
530 {
531 /* Leaf pages have no parity concerns, so just use it */
532 return buffer;
533 }
534 else
535 {
536 BlockNumber blkno = BufferGetBlockNumber(buffer);
537 int blkFlags = GBUF_INNER_PARITY(blkno);
538
539 if ((flags & GBUF_PARITY_MASK) == blkFlags)
540 {
541 /* Page has right parity, use it */
542 return buffer;
543 }
544 else
545 {
546 /* Page has wrong parity, record it in cache and try again */
549 cache->lastUsedPages.cachedPage[blkFlags].blkno = blkno;
552 UnlockReleaseBuffer(buffer);
553 }
554 }
555 }
556}
uint32 BlockNumber
Definition block.h:31
int Buffer
Definition buf.h:23
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition bufmgr.c:4469
void UnlockReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5626
static Page BufferGetPage(Buffer buffer)
Definition bufmgr.h:468
Size PageGetExactFreeSpace(const PageData *page)
Definition bufpage.c:967
uint16_t uint16
Definition c.h:682
static int fb(int x)
#define GBUF_NULLS
#define GBUF_PARITY_MASK
#define SPGIST_NULLS
#define GBUF_REQ_NULLS(flags)
#define GBUF_INNER_PARITY(x)
#define GBUF_REQ_LEAF(flags)
#define SPGIST_LEAF
Buffer SpGistNewBuffer(Relation index)
Definition spgutils.c:393
SpGistCache * spgGetCache(Relation index)
Definition spgutils.c:189
void SpGistInitBuffer(Buffer b, uint16 f)
Definition spgutils.c:721
SpGistLUPCache lastUsedPages
SpGistLastUsedPage cachedPage[SPGIST_CACHED_PAGES]
Definition type.h:97

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

Referenced by SpGistGetBuffer().

◆ fillTypeDesc()

static void fillTypeDesc ( SpGistTypeDesc *  desc,
Oid  type 
)
static

Definition at line 167 of file spgutils.c.

168{
169 HeapTuple tp;
171
172 desc->type = type;
174 if (!HeapTupleIsValid(tp))
175 elog(ERROR, "cache lookup failed for type %u", type);
177 desc->attlen = typtup->typlen;
178 desc->attbyval = typtup->typbyval;
179 desc->attalign = typtup->typalign;
180 desc->attstorage = typtup->typstorage;
181 ReleaseSysCache(tp);
182}
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define HeapTupleIsValid(tuple)
Definition htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
END_CATALOG_STRUCT typedef FormData_pg_type * Form_pg_type
Definition pg_type.h:265
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:265
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:221
const char * type

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

Referenced by spgGetCache().

◆ GetIndexInputType()

static Oid GetIndexInputType ( Relation  index,
AttrNumber  indexcol 
)
static

Definition at line 122 of file spgutils.c.

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

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

Referenced by spgGetCache().

◆ getSpGistTupleDesc()

TupleDesc getSpGistTupleDesc ( Relation  index,
SpGistTypeDesc *  keyType 
)

Definition at line 316 of file spgutils.c.

317{
320
321 if (keyType->type ==
324 else
325 {
328 /* It's sufficient to update the type-dependent fields of the column */
329 att->atttypid = keyType->type;
330 att->atttypmod = -1;
331 att->attlen = keyType->attlen;
332 att->attbyval = keyType->attbyval;
333 att->attalign = keyType->attalign;
334 att->attstorage = keyType->attstorage;
335 /* We shouldn't need to bother with making these valid: */
336 att->attcompression = InvalidCompressionMethod;
337 att->attcollation = InvalidOid;
338
341 }
342 return outTupDesc;
343}
FormData_pg_attribute * Form_pg_attribute
#define RelationGetDescr(relation)
Definition rel.h:542
#define spgKeyColumn
#define InvalidCompressionMethod
void TupleDescFinalize(TupleDesc tupdesc)
Definition tupdesc.c:511
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition tupdesc.c:242
void populate_compact_attribute(TupleDesc tupdesc, int attnum)
Definition tupdesc.c:100
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:178

References CreateTupleDescCopy(), fb(), InvalidCompressionMethod, InvalidOid, populate_compact_attribute(), RelationGetDescr, spgKeyColumn, TupleDescAttr(), and TupleDescFinalize().

Referenced by initSpGistState(), and spgbeginscan().

◆ initSpGistState()

void initSpGistState ( SpGistState *  state,
Relation  index 
)

Definition at line 347 of file spgutils.c.

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

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

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

◆ memcpyInnerDatum()

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

Definition at line 796 of file spgutils.c.

797{
798 unsigned int size;
799
800 if (att->attbyval)
801 {
802 memcpy(target, &datum, sizeof(Datum));
803 }
804 else
805 {
806 size = (att->attlen > 0) ? att->attlen : VARSIZE_ANY(DatumGetPointer(datum));
807 memcpy(target, DatumGetPointer(datum), size);
808 }
809}
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
uint64_t Datum
Definition postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
static Size VARSIZE_ANY(const void *PTR)
Definition varatt.h:460

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

Referenced by spgFormInnerTuple(), and spgFormNodeTuple().

◆ spgDeformLeafTuple()

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

Definition at line 1114 of file spgutils.c.

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

References Assert, fb(), index_deform_tuple_internal(), SGLT_GET_HASNULLMASK, SGLTHDRSZ, and spgKeyColumn.

Referenced by doPickSplit(), and storeGettuple().

◆ spgExtractNodeLabels()

Datum * spgExtractNodeLabels ( SpGistState *  state,
SpGistInnerTuple  innerTuple 
)

Definition at line 1159 of file spgutils.c.

1160{
1161 Datum *nodeLabels;
1162 int i;
1163 SpGistNodeTuple node;
1164
1165 /* Either all the labels must be NULL, or none. */
1166 node = SGITNODEPTR(innerTuple);
1167 if (IndexTupleHasNulls(node))
1168 {
1169 SGITITERATE(innerTuple, i, node)
1170 {
1171 if (!IndexTupleHasNulls(node))
1172 elog(ERROR, "some but not all node labels are null in SPGiST inner tuple");
1173 }
1174 /* They're all null, so just return NULL */
1175 return NULL;
1176 }
1177 else
1178 {
1179 nodeLabels = palloc_array(Datum, innerTuple->nNodes);
1180 SGITITERATE(innerTuple, i, node)
1181 {
1182 if (IndexTupleHasNulls(node))
1183 elog(ERROR, "some but not all node labels are null in SPGiST inner tuple");
1184 nodeLabels[i] = SGNTDATUM(node, state);
1185 }
1186 return nodeLabels;
1187 }
1188}
#define palloc_array(type, count)
Definition fe_memutils.h:91
static bool IndexTupleHasNulls(const IndexTupleData *itup)
Definition itup.h:77
#define SGITITERATE(x, i, nt)
#define SGNTDATUM(x, s)
#define SGITNODEPTR(x)

References elog, ERROR, fb(), i, IndexTupleHasNulls(), palloc_array, SGITITERATE, SGITNODEPTR, and SGNTDATUM.

Referenced by spgdoinsert(), and spgInitInnerConsistentIn().

◆ spgFormDeadTuple()

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

Definition at line 1084 of file spgutils.c.

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

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

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

◆ spgFormInnerTuple()

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

Definition at line 1001 of file spgutils.c.

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

References elog, ereport, errcode(), errhint(), errmsg, ERROR, fb(), i, IndexTupleSize(), memcpy(), memcpyInnerDatum(), palloc0(), SGDTSIZE, SGITDATAPTR, SGITHDRSZ, SGITMAXNNODES, SGITMAXPREFIXSIZE, SGITMAXSIZE, SGITNODEPTR, SPGIST_PAGE_CAPACITY, and SpGistGetInnerTypeSize().

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

◆ spgFormLeafTuple()

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

Definition at line 870 of file spgutils.c.

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

References fb(), heap_compute_data_size(), heap_fill_tuple(), i, InvalidOffsetNumber, MAXALIGN, palloc0(), SGDTSIZE, SGLT_SET_HASNULLMASK, SGLT_SET_NEXTOFFSET, SGLTHDRSZ, and spgKeyColumn.

Referenced by doPickSplit(), and spgdoinsert().

◆ spgFormNodeTuple()

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

Definition at line 959 of file spgutils.c.

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

References ereport, errcode(), errmsg, ERROR, fb(), INDEX_NULL_MASK, INDEX_SIZE_MASK, ItemPointerSetInvalid(), label, memcpyInnerDatum(), palloc0(), SGNTDATAPTR, SGNTHDRSZ, and SpGistGetInnerTypeSize().

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

◆ spgGetCache()

SpGistCache * spgGetCache ( Relation  index)

Definition at line 189 of file spgutils.c.

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

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

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

◆ spghandler()

Datum spghandler ( PG_FUNCTION_ARGS  )

Definition at line 44 of file spgutils.c.

45{
46 static const IndexAmRoutine amroutine = {
48 .amstrategies = 0,
49 .amsupport = SPGISTNProc,
50 .amoptsprocnum = SPGIST_OPTIONS_PROC,
51 .amcanorder = false,
52 .amcanorderbyop = true,
53 .amcanhash = false,
54 .amconsistentequality = false,
55 .amconsistentordering = false,
56 .amcanbackward = false,
57 .amcanunique = false,
58 .amcanmulticol = false,
59 .amoptionalkey = true,
60 .amsearcharray = false,
61 .amsearchnulls = true,
62 .amstorage = true,
63 .amclusterable = false,
64 .ampredlocks = false,
65 .amcanparallel = false,
66 .amcanbuildparallel = false,
67 .amcaninclude = true,
68 .amusemaintenanceworkmem = false,
69 .amsummarizing = false,
70 .amparallelvacuumoptions =
72 .amkeytype = InvalidOid,
73
74 .ambuild = spgbuild,
75 .ambuildempty = spgbuildempty,
76 .aminsert = spginsert,
77 .aminsertcleanup = NULL,
78 .ambulkdelete = spgbulkdelete,
79 .amvacuumcleanup = spgvacuumcleanup,
80 .amcanreturn = spgcanreturn,
81 .amcostestimate = spgcostestimate,
82 .amgettreeheight = NULL,
83 .amoptions = spgoptions,
84 .amproperty = spgproperty,
85 .ambuildphasename = NULL,
86 .amvalidate = spgvalidate,
87 .amadjustmembers = spgadjustmembers,
88 .ambeginscan = spgbeginscan,
89 .amrescan = spgrescan,
90 .amgettuple = spggettuple,
91 .amgetbitmap = spggetbitmap,
92 .amendscan = spgendscan,
93 .ammarkpos = NULL,
94 .amrestrpos = NULL,
95 .amestimateparallelscan = NULL,
96 .aminitparallelscan = NULL,
97 .amparallelrescan = NULL,
98 .amtranslatestrategy = NULL,
99 .amtranslatecmptype = NULL,
100 };
101
103}
#define PG_RETURN_POINTER(x)
Definition fmgr.h:363
void spgcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation, double *indexPages)
Definition selfuncs.c:8379
IndexBuildResult * spgbuild(Relation heap, Relation index, IndexInfo *indexInfo)
Definition spginsert.c:73
bool spginsert(Relation index, Datum *values, bool *isnull, ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique, bool indexUnchanged, IndexInfo *indexInfo)
Definition spginsert.c:183
void spgbuildempty(Relation index)
Definition spginsert.c:154
#define SPGIST_OPTIONS_PROC
Definition spgist.h:29
#define SPGISTNProc
Definition spgist.h:31
IndexScanDesc spgbeginscan(Relation rel, int keysz, int orderbysz)
Definition spgscan.c:305
bool spgcanreturn(Relation index, int attno)
Definition spgscan.c:1078
bool spggettuple(IndexScanDesc scan, ScanDirection dir)
Definition spgscan.c:1021
void spgendscan(IndexScanDesc scan)
Definition spgscan.c:424
void spgrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys, ScanKey orderbys, int norderbys)
Definition spgscan.c:375
int64 spggetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
Definition spgscan.c:937
bytea * spgoptions(Datum reloptions, bool validate)
Definition spgutils.c:758
bool spgproperty(Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull)
Definition spgutils.c:1297
IndexBulkDeleteResult * spgbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition spgvacuum.c:950
IndexBulkDeleteResult * spgvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
Definition spgvacuum.c:981
bool spgvalidate(Oid opclassoid)
Definition spgvalidate.c:38
void spgadjustmembers(Oid opfamilyoid, Oid opclassoid, List *operators, List *functions)
NodeTag type
Definition amapi.h:235
#define VACUUM_OPTION_PARALLEL_BULKDEL
Definition vacuum.h:47
#define VACUUM_OPTION_PARALLEL_COND_CLEANUP
Definition vacuum.h:54

References fb(), InvalidOid, PG_RETURN_POINTER, spgadjustmembers(), spgbeginscan(), spgbuild(), spgbuildempty(), spgbulkdelete(), spgcanreturn(), spgcostestimate(), spgendscan(), spggetbitmap(), spggettuple(), spginsert(), SPGIST_OPTIONS_PROC, SPGISTNProc, spgoptions(), spgproperty(), spgrescan(), spgvacuumcleanup(), spgvalidate(), IndexAmRoutine::type, VACUUM_OPTION_PARALLEL_BULKDEL, and VACUUM_OPTION_PARALLEL_COND_CLEANUP.

◆ SpGistGetBuffer()

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

Definition at line 568 of file spgutils.c.

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

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

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

◆ SpGistGetInnerTypeSize()

unsigned int SpGistGetInnerTypeSize ( SpGistTypeDesc *  att,
Datum  datum 
)

Definition at line 778 of file spgutils.c.

779{
780 unsigned int size;
781
782 if (att->attbyval)
783 size = sizeof(Datum);
784 else if (att->attlen > 0)
785 size = att->attlen;
786 else
787 size = VARSIZE_ANY(DatumGetPointer(datum));
788
789 return MAXALIGN(size);
790}

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

Referenced by spgFormInnerTuple(), and spgFormNodeTuple().

◆ SpGistGetLeafTupleSize()

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

Definition at line 817 of file spgutils.c.

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

References fb(), heap_compute_data_size(), i, MAXALIGN, SGDTSIZE, and SGLTHDRSZ.

Referenced by spgdoinsert().

◆ SpGistInitBuffer()

void SpGistInitBuffer ( Buffer  b,
uint16  f 
)

Definition at line 721 of file spgutils.c.

722{
725}
static Size BufferGetPageSize(Buffer buffer)
Definition bufmgr.h:457
int b
Definition isn.c:74
void SpGistInitPage(Page page, uint16 f)
Definition spgutils.c:707

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

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

◆ SpGistInitMetapage()

void SpGistInitMetapage ( Page  page)

Definition at line 731 of file spgutils.c.

732{
733 SpGistMetaPageData *metadata;
734 int i;
735
737 metadata = SpGistPageGetMeta(page);
738 memset(metadata, 0, sizeof(SpGistMetaPageData));
740
741 /* initialize last-used-page cache to empty */
742 for (i = 0; i < SPGIST_CACHED_PAGES; i++)
744
745 /*
746 * Set pd_lower just past the end of the metadata. This is essential,
747 * because without doing so, metadata will be lost if xlog.c compresses
748 * the page.
749 */
750 ((PageHeader) page)->pd_lower =
751 ((char *) metadata + sizeof(SpGistMetaPageData)) - (char *) page;
752}
PageHeaderData * PageHeader
Definition bufpage.h:199
#define SPGIST_META
#define SPGIST_CACHED_PAGES

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

Referenced by spgbuild(), and spgbuildempty().

◆ SpGistInitPage()

void SpGistInitPage ( Page  page,
uint16  f 
)

Definition at line 707 of file spgutils.c.

708{
709 SpGistPageOpaque opaque;
710
711 PageInit(page, BLCKSZ, sizeof(SpGistPageOpaqueData));
712 opaque = SpGistPageGetOpaque(page);
713 opaque->flags = f;
715}
void PageInit(Page page, Size pageSize, Size specialSize)
Definition bufpage.c:42
#define SpGistPageGetOpaque(page)
#define SPGIST_PAGE_ID

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

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

◆ SpGistNewBuffer()

Buffer SpGistNewBuffer ( Relation  index)

Definition at line 393 of file spgutils.c.

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

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

Referenced by allocNewBuffer(), and spgbuild().

◆ SpGistPageAddNewItem()

OffsetNumber SpGistPageAddNewItem ( SpGistState *  state,
Page  page,
const void *  item,
Size  size,
OffsetNumber *  startOffset,
bool  errorOK 
)

Definition at line 1202 of file spgutils.c.

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

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

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

◆ SpGistSetLastUsedPage()

void SpGistSetLastUsedPage ( Relation  index,
Buffer  buffer 
)

Definition at line 672 of file spgutils.c.

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

References BufferGetBlockNumber(), BufferGetPage(), fb(), GBUF_INNER_PARITY, GBUF_LEAF, GBUF_NULLS, GET_LUP, InvalidBlockNumber, PageGetExactFreeSpace(), spgGetCache(), SpGistBlockIsFixed, SpGistPageIsLeaf, and SpGistPageStoresNulls.

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

◆ SpGistUpdateMetaPage()

void SpGistUpdateMetaPage ( Relation  index)

Definition at line 449 of file spgutils.c.

450{
451 SpGistCache *cache = (SpGistCache *) index->rd_amcache;
452
453 if (cache != NULL)
454 {
456
458
460 {
463
464 metadata->lastUsedPages = cache->lastUsedPages;
465
466 /*
467 * Set pd_lower just past the end of the metadata. This is
468 * essential, because without doing so, metadata will be lost if
469 * xlog.c compresses the page. (We must do this here because
470 * pre-v11 versions of PG did not set the metapage's pd_lower
471 * correctly, so a pg_upgraded index might contain the wrong
472 * value.)
473 */
474 ((PageHeader) metapage)->pd_lower =
475 ((char *) metadata + sizeof(SpGistMetaPageData)) - (char *) metapage;
476
479 }
480 else
481 {
483 }
484 }
485}
void MarkBufferDirty(Buffer buffer)
Definition bufmgr.c:3170

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

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

◆ spgoptions()

bytea * spgoptions ( Datum  reloptions,
bool  validate 
)

Definition at line 758 of file spgutils.c.

759{
760 static const relopt_parse_elt tab[] = {
762 };
763
764 return (bytea *) build_reloptions(reloptions, validate,
766 sizeof(SpGistOptions),
767 tab, lengthof(tab));
768}
static bool validate(Port *port, const char *auth, const char **logdetail)
Definition auth-oauth.c:672
#define lengthof(array)
Definition c.h:932
static int fillfactor
Definition pgbench.c:188
void * build_reloptions(Datum reloptions, bool validate, relopt_kind kind, Size relopt_struct_size, const relopt_parse_elt *relopt_elems, int num_relopt_elems)
@ RELOPT_KIND_SPGIST
Definition reloptions.h:50
@ RELOPT_TYPE_INT
Definition reloptions.h:32
Definition c.h:835

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

Referenced by spghandler().

◆ spgproperty()

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

Definition at line 1297 of file spgutils.c.

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

References AMPROP_DISTANCE_ORDERABLE, fb(), Form_pg_amop, get_index_column_opclass(), get_op_rettype(), get_opclass_opfamily_and_input_type(), GETSTRUCT(), i, ObjectIdGetDatum(), OidIsValid, opfamily_can_sort_type(), ReleaseSysCacheList, and SearchSysCacheList1.

Referenced by spghandler().