PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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, ItemPointer 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, Item item, Size size, OffsetNumber *startOffset, bool errorOK)
 
bool spgproperty (Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull)
 

Macro Definition Documentation

◆ GET_LUP

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

Definition at line 490 of file spgutils.c.

Function Documentation

◆ allocNewBuffer()

static Buffer allocNewBuffer ( Relation  index,
int  flags 
)
static

Definition at line 513 of file spgutils.c.

514{
515 SpGistCache *cache = spgGetCache(index);
516 uint16 pageflags = 0;
517
518 if (GBUF_REQ_LEAF(flags))
519 pageflags |= SPGIST_LEAF;
520 if (GBUF_REQ_NULLS(flags))
521 pageflags |= SPGIST_NULLS;
522
523 for (;;)
524 {
525 Buffer buffer;
526
527 buffer = SpGistNewBuffer(index);
528 SpGistInitBuffer(buffer, pageflags);
529
530 if (pageflags & SPGIST_LEAF)
531 {
532 /* Leaf pages have no parity concerns, so just use it */
533 return buffer;
534 }
535 else
536 {
537 BlockNumber blkno = BufferGetBlockNumber(buffer);
538 int blkFlags = GBUF_INNER_PARITY(blkno);
539
540 if ((flags & GBUF_PARITY_MASK) == blkFlags)
541 {
542 /* Page has right parity, use it */
543 return buffer;
544 }
545 else
546 {
547 /* Page has wrong parity, record it in cache and try again */
548 if (pageflags & SPGIST_NULLS)
549 blkFlags |= GBUF_NULLS;
550 cache->lastUsedPages.cachedPage[blkFlags].blkno = blkno;
551 cache->lastUsedPages.cachedPage[blkFlags].freeSpace =
553 UnlockReleaseBuffer(buffer);
554 }
555 }
556 }
557}
uint32 BlockNumber
Definition: block.h:31
int Buffer
Definition: buf.h:23
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:4231
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:5390
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:417
Size PageGetExactFreeSpace(const PageData *page)
Definition: bufpage.c:957
uint16_t uint16
Definition: c.h:501
#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:394
SpGistCache * spgGetCache(Relation index)
Definition: spgutils.c:188
void SpGistInitBuffer(Buffer b, uint16 f)
Definition: spgutils.c:722
SpGistLUPCache lastUsedPages
SpGistLastUsedPage cachedPage[SPGIST_CACHED_PAGES]
Definition: type.h:96

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

Referenced by SpGistGetBuffer().

◆ fillTypeDesc()

static void fillTypeDesc ( SpGistTypeDesc desc,
Oid  type 
)
static

Definition at line 166 of file spgutils.c.

167{
168 HeapTuple tp;
169 Form_pg_type typtup;
170
171 desc->type = type;
172 tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type));
173 if (!HeapTupleIsValid(tp))
174 elog(ERROR, "cache lookup failed for type %u", type);
175 typtup = (Form_pg_type) GETSTRUCT(tp);
176 desc->attlen = typtup->typlen;
177 desc->attbyval = typtup->typbyval;
178 desc->attalign = typtup->typalign;
179 desc->attstorage = typtup->typstorage;
180 ReleaseSysCache(tp);
181}
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
Definition: htup_details.h:728
FormData_pg_type * Form_pg_type
Definition: pg_type.h:261
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:257
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:221
const char * type

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

Referenced by spgGetCache().

◆ GetIndexInputType()

static Oid GetIndexInputType ( Relation  index,
AttrNumber  indexcol 
)
static

Definition at line 121 of file spgutils.c.

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

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

Referenced by spgGetCache().

◆ getSpGistTupleDesc()

TupleDesc getSpGistTupleDesc ( Relation  index,
SpGistTypeDesc keyType 
)

Definition at line 315 of file spgutils.c.

316{
317 TupleDesc outTupDesc;
319
320 if (keyType->type ==
322 outTupDesc = RelationGetDescr(index);
323 else
324 {
326 att = TupleDescAttr(outTupDesc, spgKeyColumn);
327 /* It's sufficient to update the type-dependent fields of the column */
328 att->atttypid = keyType->type;
329 att->atttypmod = -1;
330 att->attlen = keyType->attlen;
331 att->attbyval = keyType->attbyval;
332 att->attalign = keyType->attalign;
333 att->attstorage = keyType->attstorage;
334 /* We shouldn't need to bother with making these valid: */
335 att->attcompression = InvalidCompressionMethod;
336 att->attcollation = InvalidOid;
337 /* In case we changed typlen, we'd better reset following offsets */
338 for (int i = spgFirstIncludeColumn; i < outTupDesc->natts; i++)
339 TupleDescCompactAttr(outTupDesc, i)->attcacheoff = -1;
340
342 }
343 return outTupDesc;
344}
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:202
#define RelationGetDescr(relation)
Definition: rel.h:542
#define spgFirstIncludeColumn
#define spgKeyColumn
int32 attcacheoff
Definition: tupdesc.h:70
#define InvalidCompressionMethod
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:245
void populate_compact_attribute(TupleDesc tupdesc, int attnum)
Definition: tupdesc.c:117
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:160
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:175

References SpGistTypeDesc::attalign, SpGistTypeDesc::attbyval, CompactAttribute::attcacheoff, SpGistTypeDesc::attlen, SpGistTypeDesc::attstorage, CreateTupleDescCopy(), i, InvalidCompressionMethod, InvalidOid, TupleDescData::natts, populate_compact_attribute(), RelationGetDescr, spgFirstIncludeColumn, spgKeyColumn, TupleDescAttr(), TupleDescCompactAttr(), and SpGistTypeDesc::type.

Referenced by initSpGistState(), and spgbeginscan().

◆ initSpGistState()

void initSpGistState ( SpGistState state,
Relation  index 
)

Definition at line 348 of file spgutils.c.

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

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

798{
799 unsigned int size;
800
801 if (att->attbyval)
802 {
803 memcpy(target, &datum, sizeof(Datum));
804 }
805 else
806 {
807 size = (att->attlen > 0) ? att->attlen : VARSIZE_ANY(datum);
808 memcpy(target, DatumGetPointer(datum), size);
809 }
810}
uintptr_t Datum
Definition: postgres.h:69
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:317
#define VARSIZE_ANY(PTR)
Definition: varatt.h:311

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

Referenced by spgFormInnerTuple(), and spgFormNodeTuple().

◆ spgDeformLeafTuple()

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

Definition at line 1115 of file spgutils.c.

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

References Assert(), index_deform_tuple_internal(), TupleDescData::natts, SGLT_GET_HASNULLMASK, SGLTHDRSZ, and spgKeyColumn.

Referenced by doPickSplit(), and storeGettuple().

◆ spgExtractNodeLabels()

Datum * spgExtractNodeLabels ( SpGistState state,
SpGistInnerTuple  innerTuple 
)

Definition at line 1160 of file spgutils.c.

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

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

Referenced by spgdoinsert(), and spgInitInnerConsistentIn().

◆ spgFormDeadTuple()

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

Definition at line 1085 of file spgutils.c.

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

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

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

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

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

◆ spgFormLeafTuple()

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

Definition at line 871 of file spgutils.c.

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

References heap_compute_data_size(), heap_fill_tuple(), SpGistLeafTupleData::heapPtr, i, InvalidOffsetNumber, MAXALIGN, TupleDescData::natts, palloc0(), SGDTSIZE, SGLT_SET_HASNULLMASK, SGLT_SET_NEXTOFFSET, SGLTHDRSZ, SpGistLeafTupleData::size, and spgKeyColumn.

Referenced by doPickSplit(), and spgdoinsert().

◆ spgFormNodeTuple()

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

Definition at line 960 of file spgutils.c.

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

References ereport, errcode(), errmsg(), ERROR, INDEX_NULL_MASK, INDEX_SIZE_MASK, ItemPointerSetInvalid(), label, memcpyInnerDatum(), palloc0(), SGNTDATAPTR, SGNTHDRSZ, SpGistGetInnerTypeSize(), IndexTupleData::t_info, and IndexTupleData::t_tid.

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

◆ spgGetCache()

SpGistCache * spgGetCache ( Relation  index)

Definition at line 188 of file spgutils.c.

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

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

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

◆ spghandler()

Datum spghandler ( PG_FUNCTION_ARGS  )

Definition at line 44 of file spgutils.c.

45{
47
48 amroutine->amstrategies = 0;
49 amroutine->amsupport = SPGISTNProc;
51 amroutine->amcanorder = false;
52 amroutine->amcanorderbyop = true;
53 amroutine->amcanhash = false;
54 amroutine->amconsistentequality = false;
55 amroutine->amconsistentordering = false;
56 amroutine->amcanbackward = false;
57 amroutine->amcanunique = false;
58 amroutine->amcanmulticol = false;
59 amroutine->amoptionalkey = true;
60 amroutine->amsearcharray = false;
61 amroutine->amsearchnulls = true;
62 amroutine->amstorage = true;
63 amroutine->amclusterable = false;
64 amroutine->ampredlocks = false;
65 amroutine->amcanparallel = false;
66 amroutine->amcanbuildparallel = false;
67 amroutine->amcaninclude = true;
68 amroutine->amusemaintenanceworkmem = false;
69 amroutine->amsummarizing = false;
70 amroutine->amparallelvacuumoptions =
72 amroutine->amkeytype = InvalidOid;
73
74 amroutine->ambuild = spgbuild;
75 amroutine->ambuildempty = spgbuildempty;
76 amroutine->aminsert = spginsert;
77 amroutine->aminsertcleanup = NULL;
78 amroutine->ambulkdelete = spgbulkdelete;
80 amroutine->amcanreturn = spgcanreturn;
82 amroutine->amgettreeheight = NULL;
83 amroutine->amoptions = spgoptions;
84 amroutine->amproperty = spgproperty;
85 amroutine->ambuildphasename = NULL;
86 amroutine->amvalidate = spgvalidate;
88 amroutine->ambeginscan = spgbeginscan;
89 amroutine->amrescan = spgrescan;
90 amroutine->amgettuple = spggettuple;
91 amroutine->amgetbitmap = spggetbitmap;
92 amroutine->amendscan = spgendscan;
93 amroutine->ammarkpos = NULL;
94 amroutine->amrestrpos = NULL;
95 amroutine->amestimateparallelscan = NULL;
96 amroutine->aminitparallelscan = NULL;
97 amroutine->amparallelrescan = NULL;
98 amroutine->amtranslatestrategy = NULL;
99 amroutine->amtranslatecmptype = NULL;
100
101 PG_RETURN_POINTER(amroutine);
102}
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:361
#define makeNode(_type_)
Definition: nodes.h:161
void spgcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation, double *indexPages)
Definition: selfuncs.c:7786
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:304
bool spgcanreturn(Relation index, int attno)
Definition: spgscan.c:1083
bool spggettuple(IndexScanDesc scan, ScanDirection dir)
Definition: spgscan.c:1026
void spgendscan(IndexScanDesc scan)
Definition: spgscan.c:429
void spgrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys, ScanKey orderbys, int norderbys)
Definition: spgscan.c:380
int64 spggetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
Definition: spgscan.c:942
bytea * spgoptions(Datum reloptions, bool validate)
Definition: spgutils.c:759
bool spgproperty(Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull)
Definition: spgutils.c:1298
IndexBulkDeleteResult * spgbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition: spgvacuum.c:949
IndexBulkDeleteResult * spgvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
Definition: spgvacuum.c:980
bool spgvalidate(Oid opclassoid)
Definition: spgvalidate.c:38
void spgadjustmembers(Oid opfamilyoid, Oid opclassoid, List *operators, List *functions)
Definition: spgvalidate.c:323
ambuildphasename_function ambuildphasename
Definition: amapi.h:304
ambuildempty_function ambuildempty
Definition: amapi.h:294
amvacuumcleanup_function amvacuumcleanup
Definition: amapi.h:298
bool amclusterable
Definition: amapi.h:268
amoptions_function amoptions
Definition: amapi.h:302
amestimateparallelscan_function amestimateparallelscan
Definition: amapi.h:316
amrestrpos_function amrestrpos
Definition: amapi.h:313
aminsert_function aminsert
Definition: amapi.h:295
amendscan_function amendscan
Definition: amapi.h:311
amtranslate_strategy_function amtranslatestrategy
Definition: amapi.h:321
uint16 amoptsprocnum
Definition: amapi.h:242
amparallelrescan_function amparallelrescan
Definition: amapi.h:318
Oid amkeytype
Definition: amapi.h:284
bool amconsistentordering
Definition: amapi.h:252
bool ampredlocks
Definition: amapi.h:270
uint16 amsupport
Definition: amapi.h:240
amtranslate_cmptype_function amtranslatecmptype
Definition: amapi.h:322
amcostestimate_function amcostestimate
Definition: amapi.h:300
bool amcanorderbyop
Definition: amapi.h:246
amadjustmembers_function amadjustmembers
Definition: amapi.h:306
ambuild_function ambuild
Definition: amapi.h:293
bool amstorage
Definition: amapi.h:266
uint16 amstrategies
Definition: amapi.h:238
bool amoptionalkey
Definition: amapi.h:260
amgettuple_function amgettuple
Definition: amapi.h:309
amcanreturn_function amcanreturn
Definition: amapi.h:299
bool amcanunique
Definition: amapi.h:256
amgetbitmap_function amgetbitmap
Definition: amapi.h:310
amproperty_function amproperty
Definition: amapi.h:303
ambulkdelete_function ambulkdelete
Definition: amapi.h:297
bool amsearcharray
Definition: amapi.h:262
bool amsummarizing
Definition: amapi.h:280
amvalidate_function amvalidate
Definition: amapi.h:305
ammarkpos_function ammarkpos
Definition: amapi.h:312
bool amcanmulticol
Definition: amapi.h:258
bool amusemaintenanceworkmem
Definition: amapi.h:278
ambeginscan_function ambeginscan
Definition: amapi.h:307
bool amcanparallel
Definition: amapi.h:272
amrescan_function amrescan
Definition: amapi.h:308
bool amcanorder
Definition: amapi.h:244
bool amcanbuildparallel
Definition: amapi.h:274
aminitparallelscan_function aminitparallelscan
Definition: amapi.h:317
uint8 amparallelvacuumoptions
Definition: amapi.h:282
aminsertcleanup_function aminsertcleanup
Definition: amapi.h:296
bool amcanbackward
Definition: amapi.h:254
amgettreeheight_function amgettreeheight
Definition: amapi.h:301
bool amcaninclude
Definition: amapi.h:276
bool amsearchnulls
Definition: amapi.h:264
bool amconsistentequality
Definition: amapi.h:250
bool amcanhash
Definition: amapi.h:248
#define VACUUM_OPTION_PARALLEL_BULKDEL
Definition: vacuum.h:48
#define VACUUM_OPTION_PARALLEL_COND_CLEANUP
Definition: vacuum.h:55

References IndexAmRoutine::amadjustmembers, IndexAmRoutine::ambeginscan, IndexAmRoutine::ambuild, IndexAmRoutine::ambuildempty, IndexAmRoutine::ambuildphasename, IndexAmRoutine::ambulkdelete, IndexAmRoutine::amcanbackward, IndexAmRoutine::amcanbuildparallel, IndexAmRoutine::amcanhash, IndexAmRoutine::amcaninclude, IndexAmRoutine::amcanmulticol, IndexAmRoutine::amcanorder, IndexAmRoutine::amcanorderbyop, IndexAmRoutine::amcanparallel, IndexAmRoutine::amcanreturn, IndexAmRoutine::amcanunique, IndexAmRoutine::amclusterable, IndexAmRoutine::amconsistentequality, IndexAmRoutine::amconsistentordering, IndexAmRoutine::amcostestimate, IndexAmRoutine::amendscan, IndexAmRoutine::amestimateparallelscan, IndexAmRoutine::amgetbitmap, IndexAmRoutine::amgettreeheight, IndexAmRoutine::amgettuple, IndexAmRoutine::aminitparallelscan, IndexAmRoutine::aminsert, IndexAmRoutine::aminsertcleanup, IndexAmRoutine::amkeytype, IndexAmRoutine::ammarkpos, IndexAmRoutine::amoptionalkey, IndexAmRoutine::amoptions, IndexAmRoutine::amoptsprocnum, IndexAmRoutine::amparallelrescan, IndexAmRoutine::amparallelvacuumoptions, IndexAmRoutine::ampredlocks, IndexAmRoutine::amproperty, IndexAmRoutine::amrescan, IndexAmRoutine::amrestrpos, IndexAmRoutine::amsearcharray, IndexAmRoutine::amsearchnulls, IndexAmRoutine::amstorage, IndexAmRoutine::amstrategies, IndexAmRoutine::amsummarizing, IndexAmRoutine::amsupport, IndexAmRoutine::amtranslatecmptype, IndexAmRoutine::amtranslatestrategy, IndexAmRoutine::amusemaintenanceworkmem, IndexAmRoutine::amvacuumcleanup, IndexAmRoutine::amvalidate, InvalidOid, makeNode, PG_RETURN_POINTER, spgadjustmembers(), spgbeginscan(), spgbuild(), spgbuildempty(), spgbulkdelete(), spgcanreturn(), spgcostestimate(), spgendscan(), spggetbitmap(), spggettuple(), spginsert(), SPGIST_OPTIONS_PROC, SPGISTNProc, spgoptions(), spgproperty(), spgrescan(), spgvacuumcleanup(), spgvalidate(), VACUUM_OPTION_PARALLEL_BULKDEL, and VACUUM_OPTION_PARALLEL_COND_CLEANUP.

◆ SpGistGetBuffer()

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

Definition at line 569 of file spgutils.c.

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

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

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

◆ SpGistGetInnerTypeSize()

unsigned int SpGistGetInnerTypeSize ( SpGistTypeDesc att,
Datum  datum 
)

Definition at line 779 of file spgutils.c.

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

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

Referenced by spgFormInnerTuple(), and spgFormNodeTuple().

◆ SpGistGetLeafTupleSize()

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

Definition at line 818 of file spgutils.c.

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

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

Referenced by spgdoinsert().

◆ SpGistInitBuffer()

void SpGistInitBuffer ( Buffer  b,
uint16  f 
)

Definition at line 722 of file spgutils.c.

723{
724 Assert(BufferGetPageSize(b) == BLCKSZ);
726}
static Size BufferGetPageSize(Buffer buffer)
Definition: bufmgr.h:406
int b
Definition: isn.c:74
void SpGistInitPage(Page page, uint16 f)
Definition: spgutils.c:708

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

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

◆ SpGistInitMetapage()

void SpGistInitMetapage ( Page  page)

Definition at line 732 of file spgutils.c.

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

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

Referenced by spgbuild(), and spgbuildempty().

◆ SpGistInitPage()

void SpGistInitPage ( Page  page,
uint16  f 
)

Definition at line 708 of file spgutils.c.

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

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

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

◆ SpGistNewBuffer()

Buffer SpGistNewBuffer ( Relation  index)

Definition at line 394 of file spgutils.c.

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

Referenced by allocNewBuffer(), and spgbuild().

◆ SpGistPageAddNewItem()

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

Definition at line 1203 of file spgutils.c.

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

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

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

◆ SpGistSetLastUsedPage()

void SpGistSetLastUsedPage ( Relation  index,
Buffer  buffer 
)

Definition at line 673 of file spgutils.c.

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

References SpGistLastUsedPage::blkno, BufferGetBlockNumber(), BufferGetPage(), SpGistLastUsedPage::freeSpace, GBUF_INNER_PARITY, GBUF_LEAF, GBUF_NULLS, GET_LUP, InvalidBlockNumber, PageGetExactFreeSpace(), spgGetCache(), SpGistBlockIsFixed, SpGistPageIsLeaf, and SpGistPageStoresNulls.

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

◆ SpGistUpdateMetaPage()

void SpGistUpdateMetaPage ( Relation  index)

Definition at line 450 of file spgutils.c.

451{
452 SpGistCache *cache = (SpGistCache *) index->rd_amcache;
453
454 if (cache != NULL)
455 {
456 Buffer metabuffer;
457
459
460 if (ConditionalLockBuffer(metabuffer))
461 {
462 Page metapage = BufferGetPage(metabuffer);
463 SpGistMetaPageData *metadata = SpGistPageGetMeta(metapage);
464
465 metadata->lastUsedPages = cache->lastUsedPages;
466
467 /*
468 * Set pd_lower just past the end of the metadata. This is
469 * essential, because without doing so, metadata will be lost if
470 * xlog.c compresses the page. (We must do this here because
471 * pre-v11 versions of PG did not set the metapage's pd_lower
472 * correctly, so a pg_upgraded index might contain the wrong
473 * value.)
474 */
475 ((PageHeader) metapage)->pd_lower =
476 ((char *) metadata + sizeof(SpGistMetaPageData)) - (char *) metapage;
477
478 MarkBufferDirty(metabuffer);
479 UnlockReleaseBuffer(metabuffer);
480 }
481 else
482 {
483 ReleaseBuffer(metabuffer);
484 }
485 }
486}
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:2952
PageHeaderData * PageHeader
Definition: bufpage.h:174
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81

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

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

◆ spgoptions()

bytea * spgoptions ( Datum  reloptions,
bool  validate 
)

Definition at line 759 of file spgutils.c.

760{
761 static const relopt_parse_elt tab[] = {
762 {"fillfactor", RELOPT_TYPE_INT, offsetof(SpGistOptions, fillfactor)},
763 };
764
765 return (bytea *) build_reloptions(reloptions, validate,
767 sizeof(SpGistOptions),
768 tab, lengthof(tab));
769}
static bool validate(Port *port, const char *auth)
Definition: auth-oauth.c:638
#define lengthof(array)
Definition: c.h:759
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)
Definition: reloptions.c:1934
@ RELOPT_KIND_SPGIST
Definition: reloptions.h:50
@ RELOPT_TYPE_INT
Definition: reloptions.h:32
Definition: c.h:658

References build_reloptions(), 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 1298 of file spgutils.c.

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

References AMPROP_DISTANCE_ORDERABLE, get_index_column_opclass(), get_op_rettype(), get_opclass_opfamily_and_input_type(), GETSTRUCT(), i, catclist::members, catclist::n_members, ObjectIdGetDatum(), OidIsValid, opfamily_can_sort_type(), ReleaseSysCacheList, SearchSysCacheList1, and catctup::tuple.

Referenced by spghandler().