1 /*------------------------------------------------------------------------- 2 * 3 * spgist_private.h 4 * Private declarations for SP-GiST access method. 5 * 6 * 7 * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group 8 * Portions Copyright (c) 1994, Regents of the University of California 9 * 10 * src/include/access/spgist_private.h 11 * 12 *------------------------------------------------------------------------- 13 */ 14 #ifndef SPGIST_PRIVATE_H 15 #define SPGIST_PRIVATE_H 16 17 #include "access/itup.h" 18 #include "access/spgist.h" 19 #include "catalog/pg_am_d.h" 20 #include "nodes/tidbitmap.h" 21 #include "storage/buf.h" 22 #include "utils/geo_decls.h" 23 #include "utils/relcache.h" 24 25 26 typedef struct SpGistOptions 27 { 28 int32 varlena_header_; /* varlena header (do not touch directly!) */ 29 int fillfactor; /* page fill factor in percent (0..100) */ 30 } SpGistOptions; 31 32 #define SpGistGetFillFactor(relation) \ 33 (AssertMacro(relation->rd_rel->relkind == RELKIND_INDEX && \ 34 relation->rd_rel->relam == SPGIST_AM_OID), \ 35 (relation)->rd_options ? \ 36 ((SpGistOptions *) (relation)->rd_options)->fillfactor : \ 37 SPGIST_DEFAULT_FILLFACTOR) 38 #define SpGistGetTargetPageFreeSpace(relation) \ 39 (BLCKSZ * (100 - SpGistGetFillFactor(relation)) / 100) 40 41 42 /* SPGiST leaf tuples have one key column, optionally have included columns */ 43 #define spgKeyColumn 0 44 #define spgFirstIncludeColumn 1 45 46 /* Page numbers of fixed-location pages */ 47 #define SPGIST_METAPAGE_BLKNO (0) /* metapage */ 48 #define SPGIST_ROOT_BLKNO (1) /* root for normal entries */ 49 #define SPGIST_NULL_BLKNO (2) /* root for null-value entries */ 50 #define SPGIST_LAST_FIXED_BLKNO SPGIST_NULL_BLKNO 51 52 #define SpGistBlockIsRoot(blkno) \ 53 ((blkno) == SPGIST_ROOT_BLKNO || (blkno) == SPGIST_NULL_BLKNO) 54 #define SpGistBlockIsFixed(blkno) \ 55 ((BlockNumber) (blkno) <= (BlockNumber) SPGIST_LAST_FIXED_BLKNO) 56 57 /* 58 * Contents of page special space on SPGiST index pages 59 */ 60 typedef struct SpGistPageOpaqueData 61 { 62 uint16 flags; /* see bit definitions below */ 63 uint16 nRedirection; /* number of redirection tuples on page */ 64 uint16 nPlaceholder; /* number of placeholder tuples on page */ 65 /* note there's no count of either LIVE or DEAD tuples ... */ 66 uint16 spgist_page_id; /* for identification of SP-GiST indexes */ 67 } SpGistPageOpaqueData; 68 69 typedef SpGistPageOpaqueData *SpGistPageOpaque; 70 71 /* Flag bits in page special space */ 72 #define SPGIST_META (1<<0) 73 #define SPGIST_DELETED (1<<1) /* never set, but keep for backwards 74 * compatibility */ 75 #define SPGIST_LEAF (1<<2) 76 #define SPGIST_NULLS (1<<3) 77 78 #define SpGistPageGetOpaque(page) ((SpGistPageOpaque) PageGetSpecialPointer(page)) 79 #define SpGistPageIsMeta(page) (SpGistPageGetOpaque(page)->flags & SPGIST_META) 80 #define SpGistPageIsDeleted(page) (SpGistPageGetOpaque(page)->flags & SPGIST_DELETED) 81 #define SpGistPageIsLeaf(page) (SpGistPageGetOpaque(page)->flags & SPGIST_LEAF) 82 #define SpGistPageStoresNulls(page) (SpGistPageGetOpaque(page)->flags & SPGIST_NULLS) 83 84 /* 85 * The page ID is for the convenience of pg_filedump and similar utilities, 86 * which otherwise would have a hard time telling pages of different index 87 * types apart. It should be the last 2 bytes on the page. This is more or 88 * less "free" due to alignment considerations. 89 * 90 * See comments above GinPageOpaqueData. 91 */ 92 #define SPGIST_PAGE_ID 0xFF82 93 94 /* 95 * Each backend keeps a cache of last-used page info in its index->rd_amcache 96 * area. This is initialized from, and occasionally written back to, 97 * shared storage in the index metapage. 98 */ 99 typedef struct SpGistLastUsedPage 100 { 101 BlockNumber blkno; /* block number, or InvalidBlockNumber */ 102 int freeSpace; /* page's free space (could be obsolete!) */ 103 } SpGistLastUsedPage; 104 105 /* Note: indexes in cachedPage[] match flag assignments for SpGistGetBuffer */ 106 #define SPGIST_CACHED_PAGES 8 107 108 typedef struct SpGistLUPCache 109 { 110 SpGistLastUsedPage cachedPage[SPGIST_CACHED_PAGES]; 111 } SpGistLUPCache; 112 113 /* 114 * metapage 115 */ 116 typedef struct SpGistMetaPageData 117 { 118 uint32 magicNumber; /* for identity cross-check */ 119 SpGistLUPCache lastUsedPages; /* shared storage of last-used info */ 120 } SpGistMetaPageData; 121 122 #define SPGIST_MAGIC_NUMBER (0xBA0BABEE) 123 124 #define SpGistPageGetMeta(p) \ 125 ((SpGistMetaPageData *) PageGetContents(p)) 126 127 /* 128 * Private state of index AM. SpGistState is common to both insert and 129 * search code; SpGistScanOpaque is for searches only. 130 */ 131 132 typedef struct SpGistLeafTupleData *SpGistLeafTuple; /* forward reference */ 133 134 /* Per-datatype info needed in SpGistState */ 135 typedef struct SpGistTypeDesc 136 { 137 Oid type; 138 int16 attlen; 139 bool attbyval; 140 char attalign; 141 char attstorage; 142 } SpGistTypeDesc; 143 144 typedef struct SpGistState 145 { 146 Relation index; /* index we're working with */ 147 148 spgConfigOut config; /* filled in by opclass config method */ 149 150 SpGistTypeDesc attType; /* type of values to be indexed/restored */ 151 SpGistTypeDesc attLeafType; /* type of leaf-tuple values */ 152 SpGistTypeDesc attPrefixType; /* type of inner-tuple prefix values */ 153 SpGistTypeDesc attLabelType; /* type of node label values */ 154 155 /* leafTupDesc typically points to index's tupdesc, but not always */ 156 TupleDesc leafTupDesc; /* descriptor for leaf-level tuples */ 157 158 char *deadTupleStorage; /* workspace for spgFormDeadTuple */ 159 160 TransactionId myXid; /* XID to use when creating a redirect tuple */ 161 bool isBuild; /* true if doing index build */ 162 } SpGistState; 163 164 /* Item to be re-examined later during a search */ 165 typedef struct SpGistSearchItem 166 { 167 pairingheap_node phNode; /* pairing heap node */ 168 Datum value; /* value reconstructed from parent, or 169 * leafValue if isLeaf */ 170 SpGistLeafTuple leafTuple; /* whole leaf tuple, if needed */ 171 void *traversalValue; /* opclass-specific traverse value */ 172 int level; /* level of items on this page */ 173 ItemPointerData heapPtr; /* heap info, if heap tuple */ 174 bool isNull; /* SearchItem is NULL item */ 175 bool isLeaf; /* SearchItem is heap item */ 176 bool recheck; /* qual recheck is needed */ 177 bool recheckDistances; /* distance recheck is needed */ 178 179 /* array with numberOfOrderBys entries */ 180 double distances[FLEXIBLE_ARRAY_MEMBER]; 181 } SpGistSearchItem; 182 183 #define SizeOfSpGistSearchItem(n_distances) \ 184 (offsetof(SpGistSearchItem, distances) + sizeof(double) * (n_distances)) 185 186 /* 187 * Private state of an index scan 188 */ 189 typedef struct SpGistScanOpaqueData 190 { 191 SpGistState state; /* see above */ 192 pairingheap *scanQueue; /* queue of to be visited items */ 193 MemoryContext tempCxt; /* short-lived memory context */ 194 MemoryContext traversalCxt; /* single scan lifetime memory context */ 195 196 /* Control flags showing whether to search nulls and/or non-nulls */ 197 bool searchNulls; /* scan matches (all) null entries */ 198 bool searchNonNulls; /* scan matches (some) non-null entries */ 199 200 /* Index quals to be passed to opclass (null-related quals removed) */ 201 int numberOfKeys; /* number of index qualifier conditions */ 202 ScanKey keyData; /* array of index qualifier descriptors */ 203 int numberOfOrderBys; /* number of ordering operators */ 204 int numberOfNonNullOrderBys; /* number of ordering operators 205 * with non-NULL arguments */ 206 ScanKey orderByData; /* array of ordering op descriptors */ 207 Oid *orderByTypes; /* array of ordering op return types */ 208 int *nonNullOrderByOffsets; /* array of offset of non-NULL 209 * ordering keys in the original array */ 210 Oid indexCollation; /* collation of index column */ 211 212 /* Opclass defined functions: */ 213 FmgrInfo innerConsistentFn; 214 FmgrInfo leafConsistentFn; 215 216 /* Pre-allocated workspace arrays: */ 217 double *zeroDistances; 218 double *infDistances; 219 220 /* These fields are only used in amgetbitmap scans: */ 221 TIDBitmap *tbm; /* bitmap being filled */ 222 int64 ntids; /* number of TIDs passed to bitmap */ 223 224 /* These fields are only used in amgettuple scans: */ 225 bool want_itup; /* are we reconstructing tuples? */ 226 TupleDesc reconTupDesc; /* if so, descriptor for reconstructed tuples */ 227 int nPtrs; /* number of TIDs found on current page */ 228 int iPtr; /* index for scanning through same */ 229 ItemPointerData heapPtrs[MaxIndexTuplesPerPage]; /* TIDs from cur page */ 230 bool recheck[MaxIndexTuplesPerPage]; /* their recheck flags */ 231 bool recheckDistances[MaxIndexTuplesPerPage]; /* distance recheck 232 * flags */ 233 HeapTuple reconTups[MaxIndexTuplesPerPage]; /* reconstructed tuples */ 234 235 /* distances (for recheck) */ 236 IndexOrderByDistance *distances[MaxIndexTuplesPerPage]; 237 238 /* 239 * Note: using MaxIndexTuplesPerPage above is a bit hokey since 240 * SpGistLeafTuples aren't exactly IndexTuples; however, they are larger, 241 * so this is safe. 242 */ 243 } SpGistScanOpaqueData; 244 245 typedef SpGistScanOpaqueData *SpGistScanOpaque; 246 247 /* 248 * This struct is what we actually keep in index->rd_amcache. It includes 249 * static configuration information as well as the lastUsedPages cache. 250 */ 251 typedef struct SpGistCache 252 { 253 spgConfigOut config; /* filled in by opclass config method */ 254 255 SpGistTypeDesc attType; /* type of values to be indexed/restored */ 256 SpGistTypeDesc attLeafType; /* type of leaf-tuple values */ 257 SpGistTypeDesc attPrefixType; /* type of inner-tuple prefix values */ 258 SpGistTypeDesc attLabelType; /* type of node label values */ 259 260 SpGistLUPCache lastUsedPages; /* local storage of last-used info */ 261 } SpGistCache; 262 263 264 /* 265 * SPGiST tuple types. Note: inner, leaf, and dead tuple structs 266 * must have the same tupstate field in the same position! Real inner and 267 * leaf tuples always have tupstate = LIVE; if the state is something else, 268 * use the SpGistDeadTuple struct to inspect the tuple. 269 */ 270 271 /* values of tupstate (see README for more info) */ 272 #define SPGIST_LIVE 0 /* normal live tuple (either inner or leaf) */ 273 #define SPGIST_REDIRECT 1 /* temporary redirection placeholder */ 274 #define SPGIST_DEAD 2 /* dead, cannot be removed because of links */ 275 #define SPGIST_PLACEHOLDER 3 /* placeholder, used to preserve offsets */ 276 277 /* 278 * SPGiST inner tuple: list of "nodes" that subdivide a set of tuples 279 * 280 * Inner tuple layout: 281 * header/optional prefix/array of nodes, which are SpGistNodeTuples 282 * 283 * size and prefixSize must be multiples of MAXALIGN 284 * 285 * If the prefix datum is of a pass-by-value type, it is stored in its 286 * Datum representation, that is its on-disk representation is of length 287 * sizeof(Datum). This is a fairly unfortunate choice, because in no other 288 * place does Postgres use Datum as an on-disk representation; it creates 289 * an unnecessary incompatibility between 32-bit and 64-bit builds. But the 290 * compatibility loss is mostly theoretical since MAXIMUM_ALIGNOF typically 291 * differs between such builds, too. Anyway we're stuck with it now. 292 */ 293 typedef struct SpGistInnerTupleData 294 { 295 unsigned int tupstate:2, /* LIVE/REDIRECT/DEAD/PLACEHOLDER */ 296 allTheSame:1, /* all nodes in tuple are equivalent */ 297 nNodes:13, /* number of nodes within inner tuple */ 298 prefixSize:16; /* size of prefix, or 0 if none */ 299 uint16 size; /* total size of inner tuple */ 300 /* On most machines there will be a couple of wasted bytes here */ 301 /* prefix datum follows, then nodes */ 302 } SpGistInnerTupleData; 303 304 typedef SpGistInnerTupleData *SpGistInnerTuple; 305 306 /* these must match largest values that fit in bit fields declared above */ 307 #define SGITMAXNNODES 0x1FFF 308 #define SGITMAXPREFIXSIZE 0xFFFF 309 #define SGITMAXSIZE 0xFFFF 310 311 #define SGITHDRSZ MAXALIGN(sizeof(SpGistInnerTupleData)) 312 #define _SGITDATA(x) (((char *) (x)) + SGITHDRSZ) 313 #define SGITDATAPTR(x) ((x)->prefixSize ? _SGITDATA(x) : NULL) 314 #define SGITDATUM(x, s) ((x)->prefixSize ? \ 315 ((s)->attPrefixType.attbyval ? \ 316 *(Datum *) _SGITDATA(x) : \ 317 PointerGetDatum(_SGITDATA(x))) \ 318 : (Datum) 0) 319 #define SGITNODEPTR(x) ((SpGistNodeTuple) (_SGITDATA(x) + (x)->prefixSize)) 320 321 /* Macro for iterating through the nodes of an inner tuple */ 322 #define SGITITERATE(x, i, nt) \ 323 for ((i) = 0, (nt) = SGITNODEPTR(x); \ 324 (i) < (x)->nNodes; \ 325 (i)++, (nt) = (SpGistNodeTuple) (((char *) (nt)) + IndexTupleSize(nt))) 326 327 /* 328 * SPGiST node tuple: one node within an inner tuple 329 * 330 * Node tuples use the same header as ordinary Postgres IndexTuples, but 331 * we do not use a null bitmap, because we know there is only one column 332 * so the INDEX_NULL_MASK bit suffices. Also, pass-by-value datums are 333 * stored in Datum form, the same convention as for inner tuple prefixes. 334 */ 335 336 typedef IndexTupleData SpGistNodeTupleData; 337 338 typedef SpGistNodeTupleData *SpGistNodeTuple; 339 340 #define SGNTHDRSZ MAXALIGN(sizeof(SpGistNodeTupleData)) 341 #define SGNTDATAPTR(x) (((char *) (x)) + SGNTHDRSZ) 342 #define SGNTDATUM(x, s) ((s)->attLabelType.attbyval ? \ 343 *(Datum *) SGNTDATAPTR(x) : \ 344 PointerGetDatum(SGNTDATAPTR(x))) 345 346 /* 347 * SPGiST leaf tuple: carries a leaf datum and a heap tuple TID, 348 * and optionally some "included" columns. 349 * 350 * In the simplest case, the leaf datum is the same as the indexed value; 351 * but it could also be a suffix or some other sort of delta that permits 352 * reconstruction given knowledge of the prefix path traversed to get here. 353 * Any included columns are stored without modification. 354 * 355 * A nulls bitmap is present if there are included columns AND any of the 356 * datums are NULL. We do not need a nulls bitmap for the case of a null 357 * leaf datum without included columns, as we can infer whether the leaf 358 * datum is null from whether the tuple is stored on a nulls page. (This 359 * provision is mostly for backwards compatibility, but it does save space 360 * on 32-bit machines.) As with other PG index tuple designs, if the nulls 361 * bitmap exists then it's of size INDEX_MAX_KEYS bits regardless of the 362 * actual number of attributes. For the usual choice of INDEX_MAX_KEYS, 363 * this costs nothing because of alignment considerations. 364 * 365 * The size field is wider than could possibly be needed for an on-disk leaf 366 * tuple, but this allows us to form leaf tuples even when the datum is too 367 * wide to be stored immediately, and it costs nothing because of alignment 368 * considerations. 369 * 370 * t_info holds the nextOffset field (14 bits wide, enough for supported 371 * page sizes) plus the has-nulls-bitmap flag bit; another flag bit is free. 372 * 373 * Normally, nextOffset links to the next tuple belonging to the same parent 374 * node (which must be on the same page), or it's 0 if there is no next tuple. 375 * But when the root page is a leaf page, we don't chain its tuples, 376 * so nextOffset is always 0 on the root. 377 * 378 * size must be a multiple of MAXALIGN; also, it must be at least SGDTSIZE 379 * so that the tuple can be converted to REDIRECT status later. (This 380 * restriction only adds bytes for a NULL leaf datum stored on a 32-bit 381 * machine; otherwise alignment restrictions force it anyway.) 382 */ 383 typedef struct SpGistLeafTupleData 384 { 385 unsigned int tupstate:2, /* LIVE/REDIRECT/DEAD/PLACEHOLDER */ 386 size:30; /* large enough for any palloc'able value */ 387 uint16 t_info; /* nextOffset, which links to the next tuple 388 * in chain, plus two flag bits */ 389 ItemPointerData heapPtr; /* TID of represented heap tuple */ 390 /* nulls bitmap follows if the flag bit for it is set */ 391 /* leaf datum, then any included datums, follows on a MAXALIGN boundary */ 392 } SpGistLeafTupleData; 393 394 /* Macros to access nextOffset and bit fields inside t_info */ 395 #define SGLT_GET_NEXTOFFSET(spgLeafTuple) \ 396 ((spgLeafTuple)->t_info & 0x3FFF) 397 #define SGLT_GET_HASNULLMASK(spgLeafTuple) \ 398 (((spgLeafTuple)->t_info & 0x8000) ? true : false) 399 #define SGLT_SET_NEXTOFFSET(spgLeafTuple, offsetNumber) \ 400 ((spgLeafTuple)->t_info = \ 401 ((spgLeafTuple)->t_info & 0xC000) | ((offsetNumber) & 0x3FFF)) 402 #define SGLT_SET_HASNULLMASK(spgLeafTuple, hasnulls) \ 403 ((spgLeafTuple)->t_info = \ 404 ((spgLeafTuple)->t_info & 0x7FFF) | ((hasnulls) ? 0x8000 : 0)) 405 406 #define SGLTHDRSZ(hasnulls) \ 407 ((hasnulls) ? MAXALIGN(sizeof(SpGistLeafTupleData) + \ 408 sizeof(IndexAttributeBitMapData)) : \ 409 MAXALIGN(sizeof(SpGistLeafTupleData))) 410 #define SGLTDATAPTR(x) (((char *) (x)) + SGLTHDRSZ(SGLT_GET_HASNULLMASK(x))) 411 #define SGLTDATUM(x, s) fetch_att(SGLTDATAPTR(x), \ 412 (s)->attLeafType.attbyval, \ 413 (s)->attLeafType.attlen) 414 415 /* 416 * SPGiST dead tuple: declaration for examining non-live tuples 417 * 418 * The tupstate field of this struct must match those of regular inner and 419 * leaf tuples, and its size field must match a leaf tuple's. 420 * Also, the pointer field must be in the same place as a leaf tuple's heapPtr 421 * field, to satisfy some Asserts that we make when replacing a leaf tuple 422 * with a dead tuple. 423 * We don't use t_info, but it's needed to align the pointer field. 424 * pointer and xid are only valid when tupstate = REDIRECT. 425 */ 426 typedef struct SpGistDeadTupleData 427 { 428 unsigned int tupstate:2, /* LIVE/REDIRECT/DEAD/PLACEHOLDER */ 429 size:30; 430 uint16 t_info; /* not used in dead tuples */ 431 ItemPointerData pointer; /* redirection inside index */ 432 TransactionId xid; /* ID of xact that inserted this tuple */ 433 } SpGistDeadTupleData; 434 435 typedef SpGistDeadTupleData *SpGistDeadTuple; 436 437 #define SGDTSIZE MAXALIGN(sizeof(SpGistDeadTupleData)) 438 439 /* 440 * Macros for doing free-space calculations. Note that when adding up the 441 * space needed for tuples, we always consider each tuple to need the tuple's 442 * size plus sizeof(ItemIdData) (for the line pointer). This works correctly 443 * so long as tuple sizes are always maxaligned. 444 */ 445 446 /* Page capacity after allowing for fixed header and special space */ 447 #define SPGIST_PAGE_CAPACITY \ 448 MAXALIGN_DOWN(BLCKSZ - \ 449 SizeOfPageHeaderData - \ 450 MAXALIGN(sizeof(SpGistPageOpaqueData))) 451 452 /* 453 * Compute free space on page, assuming that up to n placeholders can be 454 * recycled if present (n should be the number of tuples to be inserted) 455 */ 456 #define SpGistPageGetFreeSpace(p, n) \ 457 (PageGetExactFreeSpace(p) + \ 458 Min(SpGistPageGetOpaque(p)->nPlaceholder, n) * \ 459 (SGDTSIZE + sizeof(ItemIdData))) 460 461 /* 462 * XLOG stuff 463 */ 464 465 #define STORE_STATE(s, d) \ 466 do { \ 467 (d).myXid = (s)->myXid; \ 468 (d).isBuild = (s)->isBuild; \ 469 } while(0) 470 471 /* 472 * The "flags" argument for SpGistGetBuffer should be either GBUF_LEAF to 473 * get a leaf page, or GBUF_INNER_PARITY(blockNumber) to get an inner 474 * page in the same triple-parity group as the specified block number. 475 * (Typically, this should be GBUF_INNER_PARITY(parentBlockNumber + 1) 476 * to follow the rule described in spgist/README.) 477 * In addition, GBUF_NULLS can be OR'd in to get a page for storage of 478 * null-valued tuples. 479 * 480 * Note: these flag values are used as indexes into lastUsedPages. 481 */ 482 #define GBUF_LEAF 0x03 483 #define GBUF_INNER_PARITY(x) ((x) % 3) 484 #define GBUF_NULLS 0x04 485 486 #define GBUF_PARITY_MASK 0x03 487 #define GBUF_REQ_LEAF(flags) (((flags) & GBUF_PARITY_MASK) == GBUF_LEAF) 488 #define GBUF_REQ_NULLS(flags) ((flags) & GBUF_NULLS) 489 490 /* spgutils.c */ 491 492 /* reloption parameters */ 493 #define SPGIST_MIN_FILLFACTOR 10 494 #define SPGIST_DEFAULT_FILLFACTOR 80 495 496 extern SpGistCache *spgGetCache(Relation index); 497 extern TupleDesc getSpGistTupleDesc(Relation index, SpGistTypeDesc *keyType); 498 extern void initSpGistState(SpGistState *state, Relation index); 499 extern Buffer SpGistNewBuffer(Relation index); 500 extern void SpGistUpdateMetaPage(Relation index); 501 extern Buffer SpGistGetBuffer(Relation index, int flags, 502 int needSpace, bool *isNew); 503 extern void SpGistSetLastUsedPage(Relation index, Buffer buffer); 504 extern void SpGistInitPage(Page page, uint16 f); 505 extern void SpGistInitBuffer(Buffer b, uint16 f); 506 extern void SpGistInitMetapage(Page page); 507 extern unsigned int SpGistGetInnerTypeSize(SpGistTypeDesc *att, Datum datum); 508 extern Size SpGistGetLeafTupleSize(TupleDesc tupleDescriptor, 509 Datum *datums, bool *isnulls); 510 extern SpGistLeafTuple spgFormLeafTuple(SpGistState *state, 511 ItemPointer heapPtr, 512 Datum *datums, bool *isnulls); 513 extern SpGistNodeTuple spgFormNodeTuple(SpGistState *state, 514 Datum label, bool isnull); 515 extern SpGistInnerTuple spgFormInnerTuple(SpGistState *state, 516 bool hasPrefix, Datum prefix, 517 int nNodes, SpGistNodeTuple *nodes); 518 extern SpGistDeadTuple spgFormDeadTuple(SpGistState *state, int tupstate, 519 BlockNumber blkno, OffsetNumber offnum); 520 extern void spgDeformLeafTuple(SpGistLeafTuple tup, TupleDesc tupleDescriptor, 521 Datum *datums, bool *isnulls, 522 bool keyColumnIsNull); 523 extern Datum *spgExtractNodeLabels(SpGistState *state, 524 SpGistInnerTuple innerTuple); 525 extern OffsetNumber SpGistPageAddNewItem(SpGistState *state, Page page, 526 Item item, Size size, 527 OffsetNumber *startOffset, 528 bool errorOK); 529 extern bool spgproperty(Oid index_oid, int attno, 530 IndexAMProperty prop, const char *propname, 531 bool *res, bool *isnull); 532 533 /* spgdoinsert.c */ 534 extern void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN, 535 BlockNumber blkno, OffsetNumber offset); 536 extern void spgPageIndexMultiDelete(SpGistState *state, Page page, 537 OffsetNumber *itemnos, int nitems, 538 int firststate, int reststate, 539 BlockNumber blkno, OffsetNumber offnum); 540 extern bool spgdoinsert(Relation index, SpGistState *state, 541 ItemPointer heapPtr, Datum *datums, bool *isnulls); 542 543 /* spgproc.c */ 544 extern double *spg_key_orderbys_distances(Datum key, bool isLeaf, 545 ScanKey orderbys, int norderbys); 546 extern BOX *box_copy(BOX *orig); 547 548 #endif /* SPGIST_PRIVATE_H */ 549