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)
 
SpGistCachespgGetCache (Relation index)
 
TupleDesc getSpGistTupleDesc (Relation index, SpGistTypeDesc *keyType)
 
void initSpGistState (SpGistState *state, Relation index)
 
Buffer SpGistNewBuffer (Relation index)
 
void SpGistUpdateMetaPage (Relation index)
 
static Buffer allocNewBuffer (Relation index, int flags)
 
Buffer SpGistGetBuffer (Relation index, int flags, int needSpace, bool *isNew)
 
void SpGistSetLastUsedPage (Relation index, Buffer buffer)
 
void SpGistInitPage (Page page, uint16 f)
 
void SpGistInitBuffer (Buffer b, uint16 f)
 
void SpGistInitMetapage (Page page)
 
byteaspgoptions (Datum reloptions, bool validate)
 
unsigned int SpGistGetInnerTypeSize (SpGistTypeDesc *att, Datum datum)
 
static void memcpyInnerDatum (void *target, SpGistTypeDesc *att, Datum datum)
 
Size SpGistGetLeafTupleSize (TupleDesc tupleDescriptor, const Datum *datums, const bool *isnulls)
 
SpGistLeafTuple spgFormLeafTuple (SpGistState *state, 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)
 
DatumspgExtractNodeLabels (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,
 
)    (&(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:4426
void UnlockReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5583
static Page BufferGetPage(Buffer buffer)
Definition bufmgr.h:472
Size PageGetExactFreeSpace(const PageData *page)
Definition bufpage.c:957
uint16_t uint16
Definition c.h:617
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:96

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:39
#define elog(elevel,...)
Definition elog.h:226
#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:945
int i
Definition isn.c:77
Oid getBaseType(Oid typid)
Definition lsyscache.c:2743
Oid get_atttype(Oid relid, AttrNumber attnum)
Definition lsyscache.c:1032
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:5088
Definition pg_list.h:54
Definition nodes.h:135

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:540
#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:1417
#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}
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 DatumGetPointer(), fb(), 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 bits8 *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 = (bits8 *) ((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 bits8
Definition c.h:625
void index_deform_tuple_internal(TupleDesc tupleDescriptor, Datum *values, bool *isnull, char *tp, bits8 *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:76
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:691
int errcode(int sqlerrcode)
Definition elog.c:874
int errhint(const char *fmt,...) pg_attribute_printf(1
#define ereport(elevel,...)
Definition elog.h:150
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(), 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 bits8 *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 = (bits8 *) ((char *) tup + sizeof(SpGistLeafTupleData));
939 &tupmask, bp);
940 }
941 else if (natts > 1 || !isnulls[spgKeyColumn])
942 {
943 /* Fill data area only */
945 &tupmask, (bits8 *) NULL);
946 }
947 /* otherwise we have no data, nor a bitmap, to fill */
948
949 return tup;
950}
#define MAXALIGN(LEN)
Definition c.h:898
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, bits8 *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:860
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:918
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
Definition indexam.c:884
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition mcxt.c:1266
bool IsBinaryCoercible(Oid srctype, Oid targettype)
#define INDEX_MAX_KEYS
static Datum PointerGetDatum(const void *X)
Definition postgres.h:342
#define RelationGetRelationName(relation)
Definition rel.h:548
#define IndexRelationGetNumberOfAttributes(relation)
Definition rel.h:526
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition rel.h:533
#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:8264
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:234
#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:6597
void ReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5566
static bool PageIsEmpty(const PageData *page)
Definition bufpage.h:249
static bool PageIsNew(const PageData *page)
Definition bufpage.h:259
PageData * Page
Definition bufpage.h:81
#define Min(x, y)
Definition c.h:1093
#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 DatumGetPointer(), fb(), 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:461
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:979
@ 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:1051
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition bufpage.h:269
static void * PageGetItem(PageData *page, const ItemIdData *itemId)
Definition bufpage.h:379
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition bufpage.h:504
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
Definition bufpage.h:397
#define PANIC
Definition elog.h:42
uint16 OffsetNumber
Definition off.h:24
#define FirstOffsetNumber
Definition off.h:27
#define SPGIST_PLACEHOLDER

References Assert, elog, ERROR, 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:3132

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)
Definition auth-oauth.c:638
#define lengthof(array)
Definition c.h:875
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:778

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:1407
Oid get_op_rettype(Oid opno)
Definition lsyscache.c:1553
Oid get_index_column_opclass(Oid index_oid, int attno)
Definition lsyscache.c:3734
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().