PostgreSQL Source Code  git master
spgutils.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * spgutils.c
4  * various support functions for SP-GiST
5  *
6  *
7  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  * IDENTIFICATION
11  * src/backend/access/spgist/spgutils.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 
16 #include "postgres.h"
17 
18 #include "access/amvalidate.h"
19 #include "access/htup_details.h"
20 #include "access/reloptions.h"
21 #include "access/spgist_private.h"
23 #include "access/transam.h"
24 #include "access/xact.h"
25 #include "catalog/pg_amop.h"
26 #include "commands/vacuum.h"
27 #include "nodes/nodeFuncs.h"
28 #include "parser/parse_coerce.h"
29 #include "storage/bufmgr.h"
30 #include "storage/indexfsm.h"
31 #include "utils/catcache.h"
32 #include "utils/fmgrprotos.h"
33 #include "utils/index_selfuncs.h"
34 #include "utils/lsyscache.h"
35 #include "utils/rel.h"
36 #include "utils/syscache.h"
37 
38 
39 /*
40  * SP-GiST handler function: return IndexAmRoutine with access method parameters
41  * and callbacks.
42  */
43 Datum
45 {
47 
48  amroutine->amstrategies = 0;
49  amroutine->amsupport = SPGISTNProc;
51  amroutine->amcanorder = false;
52  amroutine->amcanorderbyop = true;
53  amroutine->amcanbackward = false;
54  amroutine->amcanunique = false;
55  amroutine->amcanmulticol = false;
56  amroutine->amoptionalkey = true;
57  amroutine->amsearcharray = false;
58  amroutine->amsearchnulls = true;
59  amroutine->amstorage = true;
60  amroutine->amclusterable = false;
61  amroutine->ampredlocks = false;
62  amroutine->amcanparallel = false;
63  amroutine->amcanbuildparallel = false;
64  amroutine->amcaninclude = true;
65  amroutine->amusemaintenanceworkmem = false;
66  amroutine->amsummarizing = false;
67  amroutine->amparallelvacuumoptions =
69  amroutine->amkeytype = InvalidOid;
70 
71  amroutine->ambuild = spgbuild;
72  amroutine->ambuildempty = spgbuildempty;
73  amroutine->aminsert = spginsert;
74  amroutine->aminsertcleanup = NULL;
75  amroutine->ambulkdelete = spgbulkdelete;
76  amroutine->amvacuumcleanup = spgvacuumcleanup;
77  amroutine->amcanreturn = spgcanreturn;
78  amroutine->amcostestimate = spgcostestimate;
79  amroutine->amgettreeheight = NULL;
80  amroutine->amoptions = spgoptions;
81  amroutine->amproperty = spgproperty;
82  amroutine->ambuildphasename = NULL;
83  amroutine->amvalidate = spgvalidate;
84  amroutine->amadjustmembers = spgadjustmembers;
85  amroutine->ambeginscan = spgbeginscan;
86  amroutine->amrescan = spgrescan;
87  amroutine->amgettuple = spggettuple;
88  amroutine->amgetbitmap = spggetbitmap;
89  amroutine->amendscan = spgendscan;
90  amroutine->ammarkpos = NULL;
91  amroutine->amrestrpos = NULL;
92  amroutine->amestimateparallelscan = NULL;
93  amroutine->aminitparallelscan = NULL;
94  amroutine->amparallelrescan = NULL;
95 
96  PG_RETURN_POINTER(amroutine);
97 }
98 
99 /*
100  * GetIndexInputType
101  * Determine the nominal input data type for an index column
102  *
103  * We define the "nominal" input type as the associated opclass's opcintype,
104  * or if that is a polymorphic type, the base type of the heap column or
105  * expression that is the index's input. The reason for preferring the
106  * opcintype is that non-polymorphic opclasses probably don't want to hear
107  * about binary-compatible input types. For instance, if a text opclass
108  * is being used with a varchar heap column, we want to report "text" not
109  * "varchar". Likewise, opclasses don't want to hear about domain types,
110  * so if we do consult the actual input type, we make sure to flatten domains.
111  *
112  * At some point maybe this should go somewhere else, but it's not clear
113  * if any other index AMs have a use for it.
114  */
115 static Oid
117 {
118  Oid opcintype;
119  AttrNumber heapcol;
120  List *indexprs;
121  ListCell *indexpr_item;
122 
123  Assert(index->rd_index != NULL);
124  Assert(indexcol > 0 && indexcol <= index->rd_index->indnkeyatts);
125  opcintype = index->rd_opcintype[indexcol - 1];
126  if (!IsPolymorphicType(opcintype))
127  return opcintype;
128  heapcol = index->rd_index->indkey.values[indexcol - 1];
129  if (heapcol != 0) /* Simple index column? */
130  return getBaseType(get_atttype(index->rd_index->indrelid, heapcol));
131 
132  /*
133  * If the index expressions are already cached, skip calling
134  * RelationGetIndexExpressions, as it will make a copy which is overkill.
135  * We're not going to modify the trees, and we're not going to do anything
136  * that would invalidate the relcache entry before we're done.
137  */
138  if (index->rd_indexprs)
139  indexprs = index->rd_indexprs;
140  else
142  indexpr_item = list_head(indexprs);
143  for (int i = 1; i <= index->rd_index->indnkeyatts; i++)
144  {
145  if (index->rd_index->indkey.values[i - 1] == 0)
146  {
147  /* expression column */
148  if (indexpr_item == NULL)
149  elog(ERROR, "wrong number of index expressions");
150  if (i == indexcol)
151  return getBaseType(exprType((Node *) lfirst(indexpr_item)));
152  indexpr_item = lnext(indexprs, indexpr_item);
153  }
154  }
155  elog(ERROR, "wrong number of index expressions");
156  return InvalidOid; /* keep compiler quiet */
157 }
158 
159 /* Fill in a SpGistTypeDesc struct with info about the specified data type */
160 static void
162 {
163  HeapTuple tp;
164  Form_pg_type typtup;
165 
166  desc->type = type;
167  tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type));
168  if (!HeapTupleIsValid(tp))
169  elog(ERROR, "cache lookup failed for type %u", type);
170  typtup = (Form_pg_type) GETSTRUCT(tp);
171  desc->attlen = typtup->typlen;
172  desc->attbyval = typtup->typbyval;
173  desc->attalign = typtup->typalign;
174  desc->attstorage = typtup->typstorage;
175  ReleaseSysCache(tp);
176 }
177 
178 /*
179  * Fetch local cache of AM-specific info about the index, initializing it
180  * if necessary
181  */
182 SpGistCache *
184 {
185  SpGistCache *cache;
186 
187  if (index->rd_amcache == NULL)
188  {
189  Oid atttype;
190  spgConfigIn in;
191  FmgrInfo *procinfo;
192 
193  cache = MemoryContextAllocZero(index->rd_indexcxt,
194  sizeof(SpGistCache));
195 
196  /* SPGiST must have one key column and can also have INCLUDE columns */
199 
200  /*
201  * Get the actual (well, nominal) data type of the key column. We
202  * pass this to the opclass config function so that polymorphic
203  * opclasses are possible.
204  */
205  atttype = GetIndexInputType(index, spgKeyColumn + 1);
206 
207  /* Call the config function to get config info for the opclass */
208  in.attType = atttype;
209 
211  FunctionCall2Coll(procinfo,
212  index->rd_indcollation[spgKeyColumn],
213  PointerGetDatum(&in),
214  PointerGetDatum(&cache->config));
215 
216  /*
217  * If leafType isn't specified, use the declared index column type,
218  * which index.c will have derived from the opclass's opcintype.
219  * (Although we now make spgvalidate.c warn if these aren't the same,
220  * old user-defined opclasses may not set the STORAGE parameter
221  * correctly, so believe leafType if it's given.)
222  */
223  if (!OidIsValid(cache->config.leafType))
224  {
225  cache->config.leafType =
227 
228  /*
229  * If index column type is binary-coercible to atttype (for
230  * example, it's a domain over atttype), treat it as plain atttype
231  * to avoid thinking we need to compress.
232  */
233  if (cache->config.leafType != atttype &&
234  IsBinaryCoercible(cache->config.leafType, atttype))
235  cache->config.leafType = atttype;
236  }
237 
238  /* Get the information we need about each relevant datatype */
239  fillTypeDesc(&cache->attType, atttype);
240 
241  if (cache->config.leafType != atttype)
242  {
244  ereport(ERROR,
245  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
246  errmsg("compress method must be defined when leaf type is different from input type")));
247 
248  fillTypeDesc(&cache->attLeafType, cache->config.leafType);
249  }
250  else
251  {
252  /* Save lookups in this common case */
253  cache->attLeafType = cache->attType;
254  }
255 
256  fillTypeDesc(&cache->attPrefixType, cache->config.prefixType);
257  fillTypeDesc(&cache->attLabelType, cache->config.labelType);
258 
259  /*
260  * Finally, if it's a real index (not a partitioned one), get the
261  * lastUsedPages data from the metapage
262  */
263  if (index->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
264  {
265  Buffer metabuffer;
266  SpGistMetaPageData *metadata;
267 
268  metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO);
269  LockBuffer(metabuffer, BUFFER_LOCK_SHARE);
270 
271  metadata = SpGistPageGetMeta(BufferGetPage(metabuffer));
272 
273  if (metadata->magicNumber != SPGIST_MAGIC_NUMBER)
274  elog(ERROR, "index \"%s\" is not an SP-GiST index",
276 
277  cache->lastUsedPages = metadata->lastUsedPages;
278 
279  UnlockReleaseBuffer(metabuffer);
280  }
281 
282  index->rd_amcache = cache;
283  }
284  else
285  {
286  /* assume it's up to date */
287  cache = (SpGistCache *) index->rd_amcache;
288  }
289 
290  return cache;
291 }
292 
293 /*
294  * Compute a tuple descriptor for leaf tuples or index-only-scan result tuples.
295  *
296  * We can use the relcache's tupdesc as-is in many cases, and it's always
297  * OK so far as any INCLUDE columns are concerned. However, the entry for
298  * the key column has to match leafType in the first case or attType in the
299  * second case. While the relcache's tupdesc *should* show leafType, this
300  * might not hold for legacy user-defined opclasses, since before v14 they
301  * were not allowed to declare their true storage type in CREATE OPCLASS.
302  * Also, attType can be different from what is in the relcache.
303  *
304  * This function gives back either a pointer to the relcache's tupdesc
305  * if that is suitable, or a palloc'd copy that's been adjusted to match
306  * the specified key column type. We can avoid doing any catalog lookups
307  * here by insisting that the caller pass an SpGistTypeDesc not just an OID.
308  */
309 TupleDesc
311 {
312  TupleDesc outTupDesc;
313  Form_pg_attribute att;
314 
315  if (keyType->type ==
317  outTupDesc = RelationGetDescr(index);
318  else
319  {
321  att = TupleDescAttr(outTupDesc, spgKeyColumn);
322  /* It's sufficient to update the type-dependent fields of the column */
323  att->atttypid = keyType->type;
324  att->atttypmod = -1;
325  att->attlen = keyType->attlen;
326  att->attbyval = keyType->attbyval;
327  att->attalign = keyType->attalign;
328  att->attstorage = keyType->attstorage;
329  /* We shouldn't need to bother with making these valid: */
330  att->attcompression = InvalidCompressionMethod;
331  att->attcollation = InvalidOid;
332  /* In case we changed typlen, we'd better reset following offsets */
333  for (int i = spgFirstIncludeColumn; i < outTupDesc->natts; i++)
334  TupleDescAttr(outTupDesc, i)->attcacheoff = -1;
335  }
336  return outTupDesc;
337 }
338 
339 /* Initialize SpGistState for working with the given index */
340 void
342 {
343  SpGistCache *cache;
344 
345  state->index = index;
346 
347  /* Get cached static information about index */
348  cache = spgGetCache(index);
349 
350  state->config = cache->config;
351  state->attType = cache->attType;
352  state->attLeafType = cache->attLeafType;
353  state->attPrefixType = cache->attPrefixType;
354  state->attLabelType = cache->attLabelType;
355 
356  /* Ensure we have a valid descriptor for leaf tuples */
357  state->leafTupDesc = getSpGistTupleDesc(state->index, &state->attLeafType);
358 
359  /* Make workspace for constructing dead tuples */
360  state->deadTupleStorage = palloc0(SGDTSIZE);
361 
362  /*
363  * Set horizon XID to use in redirection tuples. Use our own XID if we
364  * have one, else use InvalidTransactionId. The latter case can happen in
365  * VACUUM or REINDEX CONCURRENTLY, and in neither case would it be okay to
366  * force an XID to be assigned. VACUUM won't create any redirection
367  * tuples anyway, but REINDEX CONCURRENTLY can. Fortunately, REINDEX
368  * CONCURRENTLY doesn't mark the index valid until the end, so there could
369  * never be any concurrent scans "in flight" to a redirection tuple it has
370  * inserted. And it locks out VACUUM until the end, too. So it's okay
371  * for VACUUM to immediately expire a redirection tuple that contains an
372  * invalid xid.
373  */
374  state->redirectXid = GetTopTransactionIdIfAny();
375 
376  /* Assume we're not in an index build (spgbuild will override) */
377  state->isBuild = false;
378 }
379 
380 /*
381  * Allocate a new page (either by recycling, or by extending the index file).
382  *
383  * The returned buffer is already pinned and exclusive-locked.
384  * Caller is responsible for initializing the page by calling SpGistInitBuffer.
385  */
386 Buffer
388 {
389  Buffer buffer;
390 
391  /* First, try to get a page from FSM */
392  for (;;)
393  {
395 
396  if (blkno == InvalidBlockNumber)
397  break; /* nothing known to FSM */
398 
399  /*
400  * The fixed pages shouldn't ever be listed in FSM, but just in case
401  * one is, ignore it.
402  */
403  if (SpGistBlockIsFixed(blkno))
404  continue;
405 
406  buffer = ReadBuffer(index, blkno);
407 
408  /*
409  * We have to guard against the possibility that someone else already
410  * recycled this page; the buffer may be locked if so.
411  */
412  if (ConditionalLockBuffer(buffer))
413  {
414  Page page = BufferGetPage(buffer);
415 
416  if (PageIsNew(page))
417  return buffer; /* OK to use, if never initialized */
418 
419  if (SpGistPageIsDeleted(page) || PageIsEmpty(page))
420  return buffer; /* OK to use */
421 
423  }
424 
425  /* Can't use it, so release buffer and try again */
426  ReleaseBuffer(buffer);
427  }
428 
429  buffer = ExtendBufferedRel(BMR_REL(index), MAIN_FORKNUM, NULL,
430  EB_LOCK_FIRST);
431 
432  return buffer;
433 }
434 
435 /*
436  * Update index metapage's lastUsedPages info from local cache, if possible
437  *
438  * Updating meta page isn't critical for index working, so
439  * 1 use ConditionalLockBuffer to improve concurrency
440  * 2 don't WAL-log metabuffer changes to decrease WAL traffic
441  */
442 void
444 {
445  SpGistCache *cache = (SpGistCache *) index->rd_amcache;
446 
447  if (cache != NULL)
448  {
449  Buffer metabuffer;
450 
451  metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO);
452 
453  if (ConditionalLockBuffer(metabuffer))
454  {
455  Page metapage = BufferGetPage(metabuffer);
456  SpGistMetaPageData *metadata = SpGistPageGetMeta(metapage);
457 
458  metadata->lastUsedPages = cache->lastUsedPages;
459 
460  /*
461  * Set pd_lower just past the end of the metadata. This is
462  * essential, because without doing so, metadata will be lost if
463  * xlog.c compresses the page. (We must do this here because
464  * pre-v11 versions of PG did not set the metapage's pd_lower
465  * correctly, so a pg_upgraded index might contain the wrong
466  * value.)
467  */
468  ((PageHeader) metapage)->pd_lower =
469  ((char *) metadata + sizeof(SpGistMetaPageData)) - (char *) metapage;
470 
471  MarkBufferDirty(metabuffer);
472  UnlockReleaseBuffer(metabuffer);
473  }
474  else
475  {
476  ReleaseBuffer(metabuffer);
477  }
478  }
479 }
480 
481 /* Macro to select proper element of lastUsedPages cache depending on flags */
482 /* Masking flags with SPGIST_CACHED_PAGES is just for paranoia's sake */
483 #define GET_LUP(c, f) (&(c)->lastUsedPages.cachedPage[((unsigned int) (f)) % SPGIST_CACHED_PAGES])
484 
485 /*
486  * Allocate and initialize a new buffer of the type and parity specified by
487  * flags. The returned buffer is already pinned and exclusive-locked.
488  *
489  * When requesting an inner page, if we get one with the wrong parity,
490  * we just release the buffer and try again. We will get a different page
491  * because GetFreeIndexPage will have marked the page used in FSM. The page
492  * is entered in our local lastUsedPages cache, so there's some hope of
493  * making use of it later in this session, but otherwise we rely on VACUUM
494  * to eventually re-enter the page in FSM, making it available for recycling.
495  * Note that such a page does not get marked dirty here, so unless it's used
496  * fairly soon, the buffer will just get discarded and the page will remain
497  * as it was on disk.
498  *
499  * When we return a buffer to the caller, the page is *not* entered into
500  * the lastUsedPages cache; we expect the caller will do so after it's taken
501  * whatever space it will use. This is because after the caller has used up
502  * some space, the page might have less space than whatever was cached already
503  * so we'd rather not trash the old cache entry.
504  */
505 static Buffer
507 {
508  SpGistCache *cache = spgGetCache(index);
509  uint16 pageflags = 0;
510 
511  if (GBUF_REQ_LEAF(flags))
512  pageflags |= SPGIST_LEAF;
513  if (GBUF_REQ_NULLS(flags))
514  pageflags |= SPGIST_NULLS;
515 
516  for (;;)
517  {
518  Buffer buffer;
519 
520  buffer = SpGistNewBuffer(index);
521  SpGistInitBuffer(buffer, pageflags);
522 
523  if (pageflags & SPGIST_LEAF)
524  {
525  /* Leaf pages have no parity concerns, so just use it */
526  return buffer;
527  }
528  else
529  {
530  BlockNumber blkno = BufferGetBlockNumber(buffer);
531  int blkFlags = GBUF_INNER_PARITY(blkno);
532 
533  if ((flags & GBUF_PARITY_MASK) == blkFlags)
534  {
535  /* Page has right parity, use it */
536  return buffer;
537  }
538  else
539  {
540  /* Page has wrong parity, record it in cache and try again */
541  if (pageflags & SPGIST_NULLS)
542  blkFlags |= GBUF_NULLS;
543  cache->lastUsedPages.cachedPage[blkFlags].blkno = blkno;
544  cache->lastUsedPages.cachedPage[blkFlags].freeSpace =
546  UnlockReleaseBuffer(buffer);
547  }
548  }
549  }
550 }
551 
552 /*
553  * Get a buffer of the type and parity specified by flags, having at least
554  * as much free space as indicated by needSpace. We use the lastUsedPages
555  * cache to assign the same buffer previously requested when possible.
556  * The returned buffer is already pinned and exclusive-locked.
557  *
558  * *isNew is set true if the page was initialized here, false if it was
559  * already valid.
560  */
561 Buffer
562 SpGistGetBuffer(Relation index, int flags, int needSpace, bool *isNew)
563 {
564  SpGistCache *cache = spgGetCache(index);
565  SpGistLastUsedPage *lup;
566 
567  /* Bail out if even an empty page wouldn't meet the demand */
568  if (needSpace > SPGIST_PAGE_CAPACITY)
569  elog(ERROR, "desired SPGiST tuple size is too big");
570 
571  /*
572  * If possible, increase the space request to include relation's
573  * fillfactor. This ensures that when we add unrelated tuples to a page,
574  * we try to keep 100-fillfactor% available for adding tuples that are
575  * related to the ones already on it. But fillfactor mustn't cause an
576  * error for requests that would otherwise be legal.
577  */
578  needSpace += SpGistGetTargetPageFreeSpace(index);
579  needSpace = Min(needSpace, SPGIST_PAGE_CAPACITY);
580 
581  /* Get the cache entry for this flags setting */
582  lup = GET_LUP(cache, flags);
583 
584  /* If we have nothing cached, just turn it over to allocNewBuffer */
585  if (lup->blkno == InvalidBlockNumber)
586  {
587  *isNew = true;
588  return allocNewBuffer(index, flags);
589  }
590 
591  /* fixed pages should never be in cache */
593 
594  /* If cached freeSpace isn't enough, don't bother looking at the page */
595  if (lup->freeSpace >= needSpace)
596  {
597  Buffer buffer;
598  Page page;
599 
600  buffer = ReadBuffer(index, lup->blkno);
601 
602  if (!ConditionalLockBuffer(buffer))
603  {
604  /*
605  * buffer is locked by another process, so return a new buffer
606  */
607  ReleaseBuffer(buffer);
608  *isNew = true;
609  return allocNewBuffer(index, flags);
610  }
611 
612  page = BufferGetPage(buffer);
613 
614  if (PageIsNew(page) || SpGistPageIsDeleted(page) || PageIsEmpty(page))
615  {
616  /* OK to initialize the page */
617  uint16 pageflags = 0;
618 
619  if (GBUF_REQ_LEAF(flags))
620  pageflags |= SPGIST_LEAF;
621  if (GBUF_REQ_NULLS(flags))
622  pageflags |= SPGIST_NULLS;
623  SpGistInitBuffer(buffer, pageflags);
624  lup->freeSpace = PageGetExactFreeSpace(page) - needSpace;
625  *isNew = true;
626  return buffer;
627  }
628 
629  /*
630  * Check that page is of right type and has enough space. We must
631  * recheck this since our cache isn't necessarily up to date.
632  */
633  if ((GBUF_REQ_LEAF(flags) ? SpGistPageIsLeaf(page) : !SpGistPageIsLeaf(page)) &&
635  {
636  int freeSpace = PageGetExactFreeSpace(page);
637 
638  if (freeSpace >= needSpace)
639  {
640  /* Success, update freespace info and return the buffer */
641  lup->freeSpace = freeSpace - needSpace;
642  *isNew = false;
643  return buffer;
644  }
645  }
646 
647  /*
648  * fallback to allocation of new buffer
649  */
650  UnlockReleaseBuffer(buffer);
651  }
652 
653  /* No success with cache, so return a new buffer */
654  *isNew = true;
655  return allocNewBuffer(index, flags);
656 }
657 
658 /*
659  * Update lastUsedPages cache when done modifying a page.
660  *
661  * We update the appropriate cache entry if it already contained this page
662  * (its freeSpace is likely obsolete), or if this page has more space than
663  * whatever we had cached.
664  */
665 void
667 {
668  SpGistCache *cache = spgGetCache(index);
669  SpGistLastUsedPage *lup;
670  int freeSpace;
671  Page page = BufferGetPage(buffer);
672  BlockNumber blkno = BufferGetBlockNumber(buffer);
673  int flags;
674 
675  /* Never enter fixed pages (root pages) in cache, though */
676  if (SpGistBlockIsFixed(blkno))
677  return;
678 
679  if (SpGistPageIsLeaf(page))
680  flags = GBUF_LEAF;
681  else
682  flags = GBUF_INNER_PARITY(blkno);
683  if (SpGistPageStoresNulls(page))
684  flags |= GBUF_NULLS;
685 
686  lup = GET_LUP(cache, flags);
687 
688  freeSpace = PageGetExactFreeSpace(page);
689  if (lup->blkno == InvalidBlockNumber || lup->blkno == blkno ||
690  lup->freeSpace < freeSpace)
691  {
692  lup->blkno = blkno;
693  lup->freeSpace = freeSpace;
694  }
695 }
696 
697 /*
698  * Initialize an SPGiST page to empty, with specified flags
699  */
700 void
702 {
703  SpGistPageOpaque opaque;
704 
705  PageInit(page, BLCKSZ, sizeof(SpGistPageOpaqueData));
706  opaque = SpGistPageGetOpaque(page);
707  opaque->flags = f;
708  opaque->spgist_page_id = SPGIST_PAGE_ID;
709 }
710 
711 /*
712  * Initialize a buffer's page to empty, with specified flags
713  */
714 void
716 {
717  Assert(BufferGetPageSize(b) == BLCKSZ);
719 }
720 
721 /*
722  * Initialize metadata page
723  */
724 void
726 {
727  SpGistMetaPageData *metadata;
728  int i;
729 
731  metadata = SpGistPageGetMeta(page);
732  memset(metadata, 0, sizeof(SpGistMetaPageData));
733  metadata->magicNumber = SPGIST_MAGIC_NUMBER;
734 
735  /* initialize last-used-page cache to empty */
736  for (i = 0; i < SPGIST_CACHED_PAGES; i++)
738 
739  /*
740  * Set pd_lower just past the end of the metadata. This is essential,
741  * because without doing so, metadata will be lost if xlog.c compresses
742  * the page.
743  */
744  ((PageHeader) page)->pd_lower =
745  ((char *) metadata + sizeof(SpGistMetaPageData)) - (char *) page;
746 }
747 
748 /*
749  * reloptions processing for SPGiST
750  */
751 bytea *
752 spgoptions(Datum reloptions, bool validate)
753 {
754  static const relopt_parse_elt tab[] = {
755  {"fillfactor", RELOPT_TYPE_INT, offsetof(SpGistOptions, fillfactor)},
756  };
757 
758  return (bytea *) build_reloptions(reloptions, validate,
760  sizeof(SpGistOptions),
761  tab, lengthof(tab));
762 }
763 
764 /*
765  * Get the space needed to store a non-null datum of the indicated type
766  * in an inner tuple (that is, as a prefix or node label).
767  * Note the result is already rounded up to a MAXALIGN boundary.
768  * Here we follow the convention that pass-by-val types are just stored
769  * in their Datum representation (compare memcpyInnerDatum).
770  */
771 unsigned int
773 {
774  unsigned int size;
775 
776  if (att->attbyval)
777  size = sizeof(Datum);
778  else if (att->attlen > 0)
779  size = att->attlen;
780  else
781  size = VARSIZE_ANY(datum);
782 
783  return MAXALIGN(size);
784 }
785 
786 /*
787  * Copy the given non-null datum to *target, in the inner-tuple case
788  */
789 static void
790 memcpyInnerDatum(void *target, SpGistTypeDesc *att, Datum datum)
791 {
792  unsigned int size;
793 
794  if (att->attbyval)
795  {
796  memcpy(target, &datum, sizeof(Datum));
797  }
798  else
799  {
800  size = (att->attlen > 0) ? att->attlen : VARSIZE_ANY(datum);
801  memcpy(target, DatumGetPointer(datum), size);
802  }
803 }
804 
805 /*
806  * Compute space required for a leaf tuple holding the given data.
807  *
808  * This must match the size-calculation portion of spgFormLeafTuple.
809  */
810 Size
812  const Datum *datums, const bool *isnulls)
813 {
814  Size size;
815  Size data_size;
816  bool needs_null_mask = false;
817  int natts = tupleDescriptor->natts;
818 
819  /*
820  * Decide whether we need a nulls bitmask.
821  *
822  * If there is only a key attribute (natts == 1), never use a bitmask, for
823  * compatibility with the pre-v14 layout of leaf tuples. Otherwise, we
824  * need one if any attribute is null.
825  */
826  if (natts > 1)
827  {
828  for (int i = 0; i < natts; i++)
829  {
830  if (isnulls[i])
831  {
832  needs_null_mask = true;
833  break;
834  }
835  }
836  }
837 
838  /*
839  * Calculate size of the data part; same as for heap tuples.
840  */
841  data_size = heap_compute_data_size(tupleDescriptor, datums, isnulls);
842 
843  /*
844  * Compute total size.
845  */
846  size = SGLTHDRSZ(needs_null_mask);
847  size += data_size;
848  size = MAXALIGN(size);
849 
850  /*
851  * Ensure that we can replace the tuple with a dead tuple later. This test
852  * is unnecessary when there are any non-null attributes, but be safe.
853  */
854  if (size < SGDTSIZE)
855  size = SGDTSIZE;
856 
857  return size;
858 }
859 
860 /*
861  * Construct a leaf tuple containing the given heap TID and datum values
862  */
865  const Datum *datums, const bool *isnulls)
866 {
867  SpGistLeafTuple tup;
868  TupleDesc tupleDescriptor = state->leafTupDesc;
869  Size size;
870  Size hoff;
871  Size data_size;
872  bool needs_null_mask = false;
873  int natts = tupleDescriptor->natts;
874  char *tp; /* ptr to tuple data */
875  uint16 tupmask = 0; /* unused heap_fill_tuple output */
876 
877  /*
878  * Decide whether we need a nulls bitmask.
879  *
880  * If there is only a key attribute (natts == 1), never use a bitmask, for
881  * compatibility with the pre-v14 layout of leaf tuples. Otherwise, we
882  * need one if any attribute is null.
883  */
884  if (natts > 1)
885  {
886  for (int i = 0; i < natts; i++)
887  {
888  if (isnulls[i])
889  {
890  needs_null_mask = true;
891  break;
892  }
893  }
894  }
895 
896  /*
897  * Calculate size of the data part; same as for heap tuples.
898  */
899  data_size = heap_compute_data_size(tupleDescriptor, datums, isnulls);
900 
901  /*
902  * Compute total size.
903  */
904  hoff = SGLTHDRSZ(needs_null_mask);
905  size = hoff + data_size;
906  size = MAXALIGN(size);
907 
908  /*
909  * Ensure that we can replace the tuple with a dead tuple later. This test
910  * is unnecessary when there are any non-null attributes, but be safe.
911  */
912  if (size < SGDTSIZE)
913  size = SGDTSIZE;
914 
915  /* OK, form the tuple */
916  tup = (SpGistLeafTuple) palloc0(size);
917 
918  tup->size = size;
920  tup->heapPtr = *heapPtr;
921 
922  tp = (char *) tup + hoff;
923 
924  if (needs_null_mask)
925  {
926  bits8 *bp; /* ptr to null bitmap in tuple */
927 
928  /* Set nullmask presence bit in SpGistLeafTuple header */
929  SGLT_SET_HASNULLMASK(tup, true);
930  /* Fill the data area and null mask */
931  bp = (bits8 *) ((char *) tup + sizeof(SpGistLeafTupleData));
932  heap_fill_tuple(tupleDescriptor, datums, isnulls, tp, data_size,
933  &tupmask, bp);
934  }
935  else if (natts > 1 || !isnulls[spgKeyColumn])
936  {
937  /* Fill data area only */
938  heap_fill_tuple(tupleDescriptor, datums, isnulls, tp, data_size,
939  &tupmask, (bits8 *) NULL);
940  }
941  /* otherwise we have no data, nor a bitmap, to fill */
942 
943  return tup;
944 }
945 
946 /*
947  * Construct a node (to go into an inner tuple) containing the given label
948  *
949  * Note that the node's downlink is just set invalid here. Caller will fill
950  * it in later.
951  */
954 {
955  SpGistNodeTuple tup;
956  unsigned int size;
957  unsigned short infomask = 0;
958 
959  /* compute space needed (note result is already maxaligned) */
960  size = SGNTHDRSZ;
961  if (!isnull)
962  size += SpGistGetInnerTypeSize(&state->attLabelType, label);
963 
964  /*
965  * Here we make sure that the size will fit in the field reserved for it
966  * in t_info.
967  */
968  if ((size & INDEX_SIZE_MASK) != size)
969  ereport(ERROR,
970  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
971  errmsg("index row requires %zu bytes, maximum size is %zu",
972  (Size) size, (Size) INDEX_SIZE_MASK)));
973 
974  tup = (SpGistNodeTuple) palloc0(size);
975 
976  if (isnull)
977  infomask |= INDEX_NULL_MASK;
978  /* we don't bother setting the INDEX_VAR_MASK bit */
979  infomask |= size;
980  tup->t_info = infomask;
981 
982  /* The TID field will be filled in later */
984 
985  if (!isnull)
986  memcpyInnerDatum(SGNTDATAPTR(tup), &state->attLabelType, label);
987 
988  return tup;
989 }
990 
991 /*
992  * Construct an inner tuple containing the given prefix and node array
993  */
995 spgFormInnerTuple(SpGistState *state, bool hasPrefix, Datum prefix,
996  int nNodes, SpGistNodeTuple *nodes)
997 {
998  SpGistInnerTuple tup;
999  unsigned int size;
1000  unsigned int prefixSize;
1001  int i;
1002  char *ptr;
1003 
1004  /* Compute size needed */
1005  if (hasPrefix)
1006  prefixSize = SpGistGetInnerTypeSize(&state->attPrefixType, prefix);
1007  else
1008  prefixSize = 0;
1009 
1010  size = SGITHDRSZ + prefixSize;
1011 
1012  /* Note: we rely on node tuple sizes to be maxaligned already */
1013  for (i = 0; i < nNodes; i++)
1014  size += IndexTupleSize(nodes[i]);
1015 
1016  /*
1017  * Ensure that we can replace the tuple with a dead tuple later. This
1018  * test is unnecessary given current tuple layouts, but let's be safe.
1019  */
1020  if (size < SGDTSIZE)
1021  size = SGDTSIZE;
1022 
1023  /*
1024  * Inner tuple should be small enough to fit on a page
1025  */
1026  if (size > SPGIST_PAGE_CAPACITY - sizeof(ItemIdData))
1027  ereport(ERROR,
1028  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1029  errmsg("SP-GiST inner tuple size %zu exceeds maximum %zu",
1030  (Size) size,
1031  SPGIST_PAGE_CAPACITY - sizeof(ItemIdData)),
1032  errhint("Values larger than a buffer page cannot be indexed.")));
1033 
1034  /*
1035  * Check for overflow of header fields --- probably can't fail if the
1036  * above succeeded, but let's be paranoid
1037  */
1038  if (size > SGITMAXSIZE ||
1039  prefixSize > SGITMAXPREFIXSIZE ||
1040  nNodes > SGITMAXNNODES)
1041  elog(ERROR, "SPGiST inner tuple header field is too small");
1042 
1043  /* OK, form the tuple */
1044  tup = (SpGistInnerTuple) palloc0(size);
1045 
1046  tup->nNodes = nNodes;
1047  tup->prefixSize = prefixSize;
1048  tup->size = size;
1049 
1050  if (hasPrefix)
1051  memcpyInnerDatum(SGITDATAPTR(tup), &state->attPrefixType, prefix);
1052 
1053  ptr = (char *) SGITNODEPTR(tup);
1054 
1055  for (i = 0; i < nNodes; i++)
1056  {
1057  SpGistNodeTuple node = nodes[i];
1058 
1059  memcpy(ptr, node, IndexTupleSize(node));
1060  ptr += IndexTupleSize(node);
1061  }
1062 
1063  return tup;
1064 }
1065 
1066 /*
1067  * Construct a "dead" tuple to replace a tuple being deleted.
1068  *
1069  * The state can be SPGIST_REDIRECT, SPGIST_DEAD, or SPGIST_PLACEHOLDER.
1070  * For a REDIRECT tuple, a pointer (blkno+offset) must be supplied, and
1071  * the xid field is filled in automatically.
1072  *
1073  * This is called in critical sections, so we don't use palloc; the tuple
1074  * is built in preallocated storage. It should be copied before another
1075  * call with different parameters can occur.
1076  */
1079  BlockNumber blkno, OffsetNumber offnum)
1080 {
1081  SpGistDeadTuple tuple = (SpGistDeadTuple) state->deadTupleStorage;
1082 
1083  tuple->tupstate = tupstate;
1084  tuple->size = SGDTSIZE;
1086 
1087  if (tupstate == SPGIST_REDIRECT)
1088  {
1089  ItemPointerSet(&tuple->pointer, blkno, offnum);
1090  tuple->xid = state->redirectXid;
1091  }
1092  else
1093  {
1094  ItemPointerSetInvalid(&tuple->pointer);
1095  tuple->xid = InvalidTransactionId;
1096  }
1097 
1098  return tuple;
1099 }
1100 
1101 /*
1102  * Convert an SPGiST leaf tuple into Datum/isnull arrays.
1103  *
1104  * The caller must allocate sufficient storage for the output arrays.
1105  * (INDEX_MAX_KEYS entries should be enough.)
1106  */
1107 void
1109  Datum *datums, bool *isnulls, bool keyColumnIsNull)
1110 {
1111  bool hasNullsMask = SGLT_GET_HASNULLMASK(tup);
1112  char *tp; /* ptr to tuple data */
1113  bits8 *bp; /* ptr to null bitmap in tuple */
1114 
1115  if (keyColumnIsNull && tupleDescriptor->natts == 1)
1116  {
1117  /*
1118  * Trivial case: there is only the key attribute and we're in a nulls
1119  * tree. The hasNullsMask bit in the tuple header should not be set
1120  * (and thus we can't use index_deform_tuple_internal), but
1121  * nonetheless the result is NULL.
1122  *
1123  * Note: currently this is dead code, because noplace calls this when
1124  * there is only the key attribute. But we should cover the case.
1125  */
1126  Assert(!hasNullsMask);
1127 
1128  datums[spgKeyColumn] = (Datum) 0;
1129  isnulls[spgKeyColumn] = true;
1130  return;
1131  }
1132 
1133  tp = (char *) tup + SGLTHDRSZ(hasNullsMask);
1134  bp = (bits8 *) ((char *) tup + sizeof(SpGistLeafTupleData));
1135 
1136  index_deform_tuple_internal(tupleDescriptor,
1137  datums, isnulls,
1138  tp, bp, hasNullsMask);
1139 
1140  /*
1141  * Key column isnull value from the tuple should be consistent with
1142  * keyColumnIsNull flag from the caller.
1143  */
1144  Assert(keyColumnIsNull == isnulls[spgKeyColumn]);
1145 }
1146 
1147 /*
1148  * Extract the label datums of the nodes within innerTuple
1149  *
1150  * Returns NULL if label datums are NULLs
1151  */
1152 Datum *
1154 {
1155  Datum *nodeLabels;
1156  int i;
1157  SpGistNodeTuple node;
1158 
1159  /* Either all the labels must be NULL, or none. */
1160  node = SGITNODEPTR(innerTuple);
1161  if (IndexTupleHasNulls(node))
1162  {
1163  SGITITERATE(innerTuple, i, node)
1164  {
1165  if (!IndexTupleHasNulls(node))
1166  elog(ERROR, "some but not all node labels are null in SPGiST inner tuple");
1167  }
1168  /* They're all null, so just return NULL */
1169  return NULL;
1170  }
1171  else
1172  {
1173  nodeLabels = (Datum *) palloc(sizeof(Datum) * innerTuple->nNodes);
1174  SGITITERATE(innerTuple, i, node)
1175  {
1176  if (IndexTupleHasNulls(node))
1177  elog(ERROR, "some but not all node labels are null in SPGiST inner tuple");
1178  nodeLabels[i] = SGNTDATUM(node, state);
1179  }
1180  return nodeLabels;
1181  }
1182 }
1183 
1184 /*
1185  * Add a new item to the page, replacing a PLACEHOLDER item if possible.
1186  * Return the location it's inserted at, or InvalidOffsetNumber on failure.
1187  *
1188  * If startOffset isn't NULL, we start searching for placeholders at
1189  * *startOffset, and update that to the next place to search. This is just
1190  * an optimization for repeated insertions.
1191  *
1192  * If errorOK is false, we throw error when there's not enough room,
1193  * rather than returning InvalidOffsetNumber.
1194  */
1197  OffsetNumber *startOffset, bool errorOK)
1198 {
1199  SpGistPageOpaque opaque = SpGistPageGetOpaque(page);
1200  OffsetNumber i,
1201  maxoff,
1202  offnum;
1203 
1204  if (opaque->nPlaceholder > 0 &&
1206  {
1207  /* Try to replace a placeholder */
1208  maxoff = PageGetMaxOffsetNumber(page);
1209  offnum = InvalidOffsetNumber;
1210 
1211  for (;;)
1212  {
1213  if (startOffset && *startOffset != InvalidOffsetNumber)
1214  i = *startOffset;
1215  else
1216  i = FirstOffsetNumber;
1217  for (; i <= maxoff; i++)
1218  {
1220  PageGetItemId(page, i));
1221 
1222  if (it->tupstate == SPGIST_PLACEHOLDER)
1223  {
1224  offnum = i;
1225  break;
1226  }
1227  }
1228 
1229  /* Done if we found a placeholder */
1230  if (offnum != InvalidOffsetNumber)
1231  break;
1232 
1233  if (startOffset && *startOffset != InvalidOffsetNumber)
1234  {
1235  /* Hint was no good, re-search from beginning */
1236  *startOffset = InvalidOffsetNumber;
1237  continue;
1238  }
1239 
1240  /* Hmm, no placeholder found? */
1241  opaque->nPlaceholder = 0;
1242  break;
1243  }
1244 
1245  if (offnum != InvalidOffsetNumber)
1246  {
1247  /* Replace the placeholder tuple */
1248  PageIndexTupleDelete(page, offnum);
1249 
1250  offnum = PageAddItem(page, item, size, offnum, false, false);
1251 
1252  /*
1253  * We should not have failed given the size check at the top of
1254  * the function, but test anyway. If we did fail, we must PANIC
1255  * because we've already deleted the placeholder tuple, and
1256  * there's no other way to keep the damage from getting to disk.
1257  */
1258  if (offnum != InvalidOffsetNumber)
1259  {
1260  Assert(opaque->nPlaceholder > 0);
1261  opaque->nPlaceholder--;
1262  if (startOffset)
1263  *startOffset = offnum + 1;
1264  }
1265  else
1266  elog(PANIC, "failed to add item of size %zu to SPGiST index page",
1267  size);
1268 
1269  return offnum;
1270  }
1271  }
1272 
1273  /* No luck in replacing a placeholder, so just add it to the page */
1274  offnum = PageAddItem(page, item, size,
1275  InvalidOffsetNumber, false, false);
1276 
1277  if (offnum == InvalidOffsetNumber && !errorOK)
1278  elog(ERROR, "failed to add item of size %zu to SPGiST index page",
1279  size);
1280 
1281  return offnum;
1282 }
1283 
1284 /*
1285  * spgproperty() -- Check boolean properties of indexes.
1286  *
1287  * This is optional for most AMs, but is required for SP-GiST because the core
1288  * property code doesn't support AMPROP_DISTANCE_ORDERABLE.
1289  */
1290 bool
1291 spgproperty(Oid index_oid, int attno,
1292  IndexAMProperty prop, const char *propname,
1293  bool *res, bool *isnull)
1294 {
1295  Oid opclass,
1296  opfamily,
1297  opcintype;
1298  CatCList *catlist;
1299  int i;
1300 
1301  /* Only answer column-level inquiries */
1302  if (attno == 0)
1303  return false;
1304 
1305  switch (prop)
1306  {
1308  break;
1309  default:
1310  return false;
1311  }
1312 
1313  /*
1314  * Currently, SP-GiST distance-ordered scans require that there be a
1315  * distance operator in the opclass with the default types. So we assume
1316  * that if such an operator exists, then there's a reason for it.
1317  */
1318 
1319  /* First we need to know the column's opclass. */
1320  opclass = get_index_column_opclass(index_oid, attno);
1321  if (!OidIsValid(opclass))
1322  {
1323  *isnull = true;
1324  return true;
1325  }
1326 
1327  /* Now look up the opclass family and input datatype. */
1328  if (!get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
1329  {
1330  *isnull = true;
1331  return true;
1332  }
1333 
1334  /* And now we can check whether the operator is provided. */
1335  catlist = SearchSysCacheList1(AMOPSTRATEGY,
1336  ObjectIdGetDatum(opfamily));
1337 
1338  *res = false;
1339 
1340  for (i = 0; i < catlist->n_members; i++)
1341  {
1342  HeapTuple amoptup = &catlist->members[i]->tuple;
1343  Form_pg_amop amopform = (Form_pg_amop) GETSTRUCT(amoptup);
1344 
1345  if (amopform->amoppurpose == AMOP_ORDER &&
1346  (amopform->amoplefttype == opcintype ||
1347  amopform->amoprighttype == opcintype) &&
1348  opfamily_can_sort_type(amopform->amopsortfamily,
1349  get_op_rettype(amopform->amopopr)))
1350  {
1351  *res = true;
1352  break;
1353  }
1354  }
1355 
1356  ReleaseSysCacheList(catlist);
1357 
1358  *isnull = false;
1359 
1360  return true;
1361 }
IndexAMProperty
Definition: amapi.h:35
@ AMPROP_DISTANCE_ORDERABLE
Definition: amapi.h:42
bool opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid)
Definition: amvalidate.c:271
int16 AttrNumber
Definition: attnum.h:21
uint32 BlockNumber
Definition: block.h:31
#define InvalidBlockNumber
Definition: block.h:33
int Buffer
Definition: buf.h:23
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:3724
Buffer ExtendBufferedRel(BufferManagerRelation bmr, ForkNumber forkNum, BufferAccessStrategy strategy, uint32 flags)
Definition: bufmgr.c:846
bool ConditionalLockBuffer(Buffer buffer)
Definition: bufmgr.c:5184
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4924
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4941
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:2532
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:5158
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:746
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:189
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:190
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:400
static Size BufferGetPageSize(Buffer buffer)
Definition: bufmgr.h:389
@ EB_LOCK_FIRST
Definition: bufmgr.h:86
#define BMR_REL(p_rel)
Definition: bufmgr.h:107
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
Definition: bufpage.c:1041
void PageInit(Page page, Size pageSize, Size specialSize)
Definition: bufpage.c:42
Size PageGetExactFreeSpace(Page page)
Definition: bufpage.c:947
PageHeaderData * PageHeader
Definition: bufpage.h:173
static bool PageIsEmpty(Page page)
Definition: bufpage.h:223
Pointer Page
Definition: bufpage.h:81
static Item PageGetItem(Page page, ItemId itemId)
Definition: bufpage.h:354
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:243
static bool PageIsNew(Page page)
Definition: bufpage.h:233
static OffsetNumber PageGetMaxOffsetNumber(Page page)
Definition: bufpage.h:372
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:471
#define Min(x, y)
Definition: c.h:958
#define MAXALIGN(LEN)
Definition: c.h:765
#define Assert(condition)
Definition: c.h:812
uint8 bits8
Definition: c.h:492
uint16_t uint16
Definition: c.h:484
#define lengthof(array)
Definition: c.h:742
#define OidIsValid(objectId)
Definition: c.h:729
size_t Size
Definition: c.h:559
int errhint(const char *fmt,...)
Definition: elog.c:1317
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define PANIC
Definition: elog.h:42
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define ereport(elevel,...)
Definition: elog.h:149
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1149
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:361
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
Size heap_compute_data_size(TupleDesc tupleDesc, const Datum *values, const bool *isnull)
Definition: heaptuple.c:215
void heap_fill_tuple(TupleDesc tupleDesc, const Datum *values, const bool *isnull, char *data, Size data_size, uint16 *infomask, bits8 *bit)
Definition: heaptuple.c:400
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
FmgrInfo * index_getprocinfo(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:862
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:828
BlockNumber GetFreeIndexPage(Relation rel)
Definition: indexfsm.c:38
void index_deform_tuple_internal(TupleDesc tupleDescriptor, Datum *values, bool *isnull, char *tp, bits8 *bp, int hasnulls)
Definition: indextuple.c:479
int b
Definition: isn.c:69
int i
Definition: isn.c:72
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:76
Pointer Item
Definition: item.h:17
static void ItemPointerSet(ItemPointerData *pointer, BlockNumber blockNumber, OffsetNumber offNum)
Definition: itemptr.h:135
static void ItemPointerSetInvalid(ItemPointerData *pointer)
Definition: itemptr.h:184
#define IndexTupleHasNulls(itup)
Definition: itup.h:71
#define INDEX_NULL_MASK
Definition: itup.h:68
#define IndexTupleSize(itup)
Definition: itup.h:70
#define INDEX_SIZE_MASK
Definition: itup.h:65
bool get_opclass_opfamily_and_input_type(Oid opclass, Oid *opfamily, Oid *opcintype)
Definition: lsyscache.c:1235
Oid get_op_rettype(Oid opno)
Definition: lsyscache.c:1333
Oid get_index_column_opclass(Oid index_oid, int attno)
Definition: lsyscache.c:3512
Oid getBaseType(Oid typid)
Definition: lsyscache.c:2521
Oid get_atttype(Oid relid, AttrNumber attnum)
Definition: lsyscache.c:913
void * palloc0(Size size)
Definition: mcxt.c:1347
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1215
void * palloc(Size size)
Definition: mcxt.c:1317
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
#define makeNode(_type_)
Definition: nodes.h:155
#define InvalidOffsetNumber
Definition: off.h:26
uint16 OffsetNumber
Definition: off.h:24
#define FirstOffsetNumber
Definition: off.h:27
bool IsBinaryCoercible(Oid srctype, Oid targettype)
FormData_pg_amop * Form_pg_amop
Definition: pg_amop.h:88
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
static char * label
#define INDEX_MAX_KEYS
#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
FormData_pg_type * Form_pg_type
Definition: pg_type.h:261
static int fillfactor
Definition: pgbench.c:187
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
uintptr_t Datum
Definition: postgres.h:64
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
#define RelationGetDescr(relation)
Definition: rel.h:531
#define RelationGetRelationName(relation)
Definition: rel.h:539
#define IndexRelationGetNumberOfAttributes(relation)
Definition: rel.h:517
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:524
List * RelationGetIndexExpressions(Relation relation)
Definition: relcache.c:5027
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:1908
@ RELOPT_KIND_SPGIST
Definition: reloptions.h:50
@ RELOPT_TYPE_INT
Definition: reloptions.h:32
@ MAIN_FORKNUM
Definition: relpath.h:58
void spgcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation, double *indexPages)
Definition: selfuncs.c:7239
static pg_noinline void Size size
Definition: slab.c:607
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
IndexBuildResult * spgbuild(Relation heap, Relation index, IndexInfo *indexInfo)
Definition: spginsert.c:73
#define SPGIST_OPTIONS_PROC
Definition: spgist.h:29
#define SPGIST_COMPRESS_PROC
Definition: spgist.h:28
#define SPGISTNProc
Definition: spgist.h:31
#define SPGIST_CONFIG_PROC
Definition: spgist.h:23
#define SGNTDATAPTR(x)
SpGistDeadTupleData * SpGistDeadTuple
#define SGLT_GET_HASNULLMASK(spgLeafTuple)
#define GBUF_NULLS
#define SPGIST_REDIRECT
SpGistInnerTupleData * SpGistInnerTuple
#define SGDTSIZE
#define SpGistPageStoresNulls(page)
#define SPGIST_PLACEHOLDER
#define SGITDATAPTR(x)
#define SGLT_SET_HASNULLMASK(spgLeafTuple, hasnulls)
#define SGITITERATE(x, i, nt)
#define SpGistGetTargetPageFreeSpace(relation)
#define GBUF_PARITY_MASK
#define GBUF_LEAF
#define spgFirstIncludeColumn
#define SGITMAXSIZE
#define SpGistPageGetMeta(p)
SpGistNodeTupleData * SpGistNodeTuple
#define SGITMAXPREFIXSIZE
#define SpGistPageIsLeaf(page)
#define SPGIST_METAPAGE_BLKNO
#define SpGistPageIsDeleted(page)
#define SGLTHDRSZ(hasnulls)
#define SGLT_SET_NEXTOFFSET(spgLeafTuple, offsetNumber)
#define SGITHDRSZ
#define SPGIST_META
struct SpGistLeafTupleData * SpGistLeafTuple
#define SPGIST_MAGIC_NUMBER
#define SPGIST_CACHED_PAGES
#define SPGIST_NULLS
#define GBUF_REQ_NULLS(flags)
#define SPGIST_PAGE_CAPACITY
#define SGITMAXNNODES
#define SGNTDATUM(x, s)
#define SpGistPageGetOpaque(page)
#define SPGIST_PAGE_ID
#define SGNTHDRSZ
#define GBUF_INNER_PARITY(x)
#define SGITNODEPTR(x)
#define SpGistBlockIsFixed(blkno)
#define GBUF_REQ_LEAF(flags)
struct SpGistLeafTupleData SpGistLeafTupleData
#define SPGIST_LEAF
#define spgKeyColumn
IndexScanDesc spgbeginscan(Relation rel, int keysz, int orderbysz)
Definition: spgscan.c:304
bool spgcanreturn(Relation index, int attno)
Definition: spgscan.c:1081
bool spggettuple(IndexScanDesc scan, ScanDirection dir)
Definition: spgscan.c:1024
void spgendscan(IndexScanDesc scan)
Definition: spgscan.c:427
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:940
bytea * spgoptions(Datum reloptions, bool validate)
Definition: spgutils.c:752
Datum * spgExtractNodeLabels(SpGistState *state, SpGistInnerTuple innerTuple)
Definition: spgutils.c:1153
void initSpGistState(SpGistState *state, Relation index)
Definition: spgutils.c:341
void SpGistUpdateMetaPage(Relation index)
Definition: spgutils.c:443
TupleDesc getSpGistTupleDesc(Relation index, SpGistTypeDesc *keyType)
Definition: spgutils.c:310
Buffer SpGistNewBuffer(Relation index)
Definition: spgutils.c:387
SpGistInnerTuple spgFormInnerTuple(SpGistState *state, bool hasPrefix, Datum prefix, int nNodes, SpGistNodeTuple *nodes)
Definition: spgutils.c:995
SpGistLeafTuple spgFormLeafTuple(SpGistState *state, ItemPointer heapPtr, const Datum *datums, const bool *isnulls)
Definition: spgutils.c:864
static void memcpyInnerDatum(void *target, SpGistTypeDesc *att, Datum datum)
Definition: spgutils.c:790
void spgDeformLeafTuple(SpGistLeafTuple tup, TupleDesc tupleDescriptor, Datum *datums, bool *isnulls, bool keyColumnIsNull)
Definition: spgutils.c:1108
SpGistDeadTuple spgFormDeadTuple(SpGistState *state, int tupstate, BlockNumber blkno, OffsetNumber offnum)
Definition: spgutils.c:1078
unsigned int SpGistGetInnerTypeSize(SpGistTypeDesc *att, Datum datum)
Definition: spgutils.c:772
static Oid GetIndexInputType(Relation index, AttrNumber indexcol)
Definition: spgutils.c:116
void SpGistInitBuffer(Buffer b, uint16 f)
Definition: spgutils.c:715
Buffer SpGistGetBuffer(Relation index, int flags, int needSpace, bool *isNew)
Definition: spgutils.c:562
static Buffer allocNewBuffer(Relation index, int flags)
Definition: spgutils.c:506
bool spgproperty(Oid index_oid, int attno, IndexAMProperty prop, const char *propname, bool *res, bool *isnull)
Definition: spgutils.c:1291
void SpGistSetLastUsedPage(Relation index, Buffer buffer)
Definition: spgutils.c:666
static void fillTypeDesc(SpGistTypeDesc *desc, Oid type)
Definition: spgutils.c:161
SpGistCache * spgGetCache(Relation index)
Definition: spgutils.c:183
OffsetNumber SpGistPageAddNewItem(SpGistState *state, Page page, Item item, Size size, OffsetNumber *startOffset, bool errorOK)
Definition: spgutils.c:1196
Datum spghandler(PG_FUNCTION_ARGS)
Definition: spgutils.c:44
Size SpGistGetLeafTupleSize(TupleDesc tupleDescriptor, const Datum *datums, const bool *isnulls)
Definition: spgutils.c:811
void SpGistInitPage(Page page, uint16 f)
Definition: spgutils.c:701
#define GET_LUP(c, f)
Definition: spgutils.c:483
SpGistNodeTuple spgFormNodeTuple(SpGistState *state, Datum label, bool isnull)
Definition: spgutils.c:953
void SpGistInitMetapage(Page page)
Definition: spgutils.c:725
IndexBulkDeleteResult * spgvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
Definition: spgvacuum.c:947
IndexBulkDeleteResult * spgbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state)
Definition: spgvacuum.c:916
bool spgvalidate(Oid opclassoid)
Definition: spgvalidate.c:39
void spgadjustmembers(Oid opfamilyoid, Oid opclassoid, List *operators, List *functions)
Definition: spgvalidate.c:332
Definition: fmgr.h:57
ambuildphasename_function ambuildphasename
Definition: amapi.h:289
ambuildempty_function ambuildempty
Definition: amapi.h:279
amvacuumcleanup_function amvacuumcleanup
Definition: amapi.h:283
bool amclusterable
Definition: amapi.h:253
amoptions_function amoptions
Definition: amapi.h:287
amestimateparallelscan_function amestimateparallelscan
Definition: amapi.h:301
amrestrpos_function amrestrpos
Definition: amapi.h:298
aminsert_function aminsert
Definition: amapi.h:280
amendscan_function amendscan
Definition: amapi.h:296
uint16 amoptsprocnum
Definition: amapi.h:233
amparallelrescan_function amparallelrescan
Definition: amapi.h:303
Oid amkeytype
Definition: amapi.h:269
bool ampredlocks
Definition: amapi.h:255
uint16 amsupport
Definition: amapi.h:231
amcostestimate_function amcostestimate
Definition: amapi.h:285
bool amcanorderbyop
Definition: amapi.h:237
amadjustmembers_function amadjustmembers
Definition: amapi.h:291
ambuild_function ambuild
Definition: amapi.h:278
bool amstorage
Definition: amapi.h:251
uint16 amstrategies
Definition: amapi.h:229
bool amoptionalkey
Definition: amapi.h:245
amgettuple_function amgettuple
Definition: amapi.h:294
amcanreturn_function amcanreturn
Definition: amapi.h:284
bool amcanunique
Definition: amapi.h:241
amgetbitmap_function amgetbitmap
Definition: amapi.h:295
amproperty_function amproperty
Definition: amapi.h:288
ambulkdelete_function ambulkdelete
Definition: amapi.h:282
bool amsearcharray
Definition: amapi.h:247
bool amsummarizing
Definition: amapi.h:265
amvalidate_function amvalidate
Definition: amapi.h:290
ammarkpos_function ammarkpos
Definition: amapi.h:297
bool amcanmulticol
Definition: amapi.h:243
bool amusemaintenanceworkmem
Definition: amapi.h:263
ambeginscan_function ambeginscan
Definition: amapi.h:292
bool amcanparallel
Definition: amapi.h:257
amrescan_function amrescan
Definition: amapi.h:293
bool amcanorder
Definition: amapi.h:235
bool amcanbuildparallel
Definition: amapi.h:259
aminitparallelscan_function aminitparallelscan
Definition: amapi.h:302
uint8 amparallelvacuumoptions
Definition: amapi.h:267
aminsertcleanup_function aminsertcleanup
Definition: amapi.h:281
bool amcanbackward
Definition: amapi.h:239
amgettreeheight_function amgettreeheight
Definition: amapi.h:286
bool amcaninclude
Definition: amapi.h:261
bool amsearchnulls
Definition: amapi.h:249
ItemPointerData t_tid
Definition: itup.h:37
unsigned short t_info
Definition: itup.h:49
Definition: pg_list.h:54
Definition: nodes.h:129
SpGistTypeDesc attPrefixType
SpGistTypeDesc attLeafType
SpGistTypeDesc attType
SpGistLUPCache lastUsedPages
spgConfigOut config
SpGistTypeDesc attLabelType
unsigned int tupstate
ItemPointerData pointer
unsigned int prefixSize
SpGistLastUsedPage cachedPage[SPGIST_CACHED_PAGES]
ItemPointerData heapPtr
SpGistLUPCache lastUsedPages
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:180
int n_members
Definition: catcache.h:178
HeapTupleData tuple
Definition: catcache.h:123
Definition: type.h:96
Oid attType
Definition: spgist.h:38
Oid leafType
Definition: spgist.h:45
Oid labelType
Definition: spgist.h:44
Oid prefixType
Definition: spgist.h:43
Definition: regguts.h:323
Definition: c.h:641
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:221
#define ReleaseSysCacheList(x)
Definition: syscache.h:134
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:127
#define InvalidCompressionMethod
#define InvalidTransactionId
Definition: transam.h:31
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:133
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
#define VACUUM_OPTION_PARALLEL_BULKDEL
Definition: vacuum.h:48
#define VACUUM_OPTION_PARALLEL_COND_CLEANUP
Definition: vacuum.h:55
#define VARSIZE_ANY(PTR)
Definition: varatt.h:311
const char * type
TransactionId GetTopTransactionIdIfAny(void)
Definition: xact.c:440