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