PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
gistutil.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * gistutil.c
4  * utilities routines for the postgres GiST index access method.
5  *
6  *
7  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  * IDENTIFICATION
11  * src/backend/access/gist/gistutil.c
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15 
16 #include <math.h>
17 
18 #include "access/gist_private.h"
19 #include "access/reloptions.h"
20 #include "storage/indexfsm.h"
21 #include "storage/lmgr.h"
22 #include "utils/builtins.h"
23 
24 
25 /*
26  * Write itup vector to page, has no control of free space.
27  */
28 void
29 gistfillbuffer(Page page, IndexTuple *itup, int len, OffsetNumber off)
30 {
32  int i;
33 
34  if (off == InvalidOffsetNumber)
35  off = (PageIsEmpty(page)) ? FirstOffsetNumber :
37 
38  for (i = 0; i < len; i++)
39  {
40  Size sz = IndexTupleSize(itup[i]);
41 
42  l = PageAddItem(page, (Item) itup[i], sz, off, false, false);
43  if (l == InvalidOffsetNumber)
44  elog(ERROR, "failed to add item to GiST index page, item %d out of %d, size %d bytes",
45  i, len, (int) sz);
46  off++;
47  }
48 }
49 
50 /*
51  * Check space for itup vector on page
52  */
53 bool
54 gistnospace(Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace)
55 {
56  unsigned int size = freespace,
57  deleted = 0;
58  int i;
59 
60  for (i = 0; i < len; i++)
61  size += IndexTupleSize(itvec[i]) + sizeof(ItemIdData);
62 
63  if (todelete != InvalidOffsetNumber)
64  {
65  IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, todelete));
66 
67  deleted = IndexTupleSize(itup) + sizeof(ItemIdData);
68  }
69 
70  return (PageGetFreeSpace(page) + deleted < size);
71 }
72 
73 bool
74 gistfitpage(IndexTuple *itvec, int len)
75 {
76  int i;
77  Size size = 0;
78 
79  for (i = 0; i < len; i++)
80  size += IndexTupleSize(itvec[i]) + sizeof(ItemIdData);
81 
82  /* TODO: Consider fillfactor */
83  return (size <= GiSTPageSize);
84 }
85 
86 /*
87  * Read buffer into itup vector
88  */
89 IndexTuple *
90 gistextractpage(Page page, int *len /* out */ )
91 {
93  maxoff;
94  IndexTuple *itvec;
95 
96  maxoff = PageGetMaxOffsetNumber(page);
97  *len = maxoff;
98  itvec = palloc(sizeof(IndexTuple) * maxoff);
99  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
100  itvec[i - FirstOffsetNumber] = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
101 
102  return itvec;
103 }
104 
105 /*
106  * join two vectors into one
107  */
108 IndexTuple *
109 gistjoinvector(IndexTuple *itvec, int *len, IndexTuple *additvec, int addlen)
110 {
111  itvec = (IndexTuple *) repalloc((void *) itvec, sizeof(IndexTuple) * ((*len) + addlen));
112  memmove(&itvec[*len], additvec, sizeof(IndexTuple) * addlen);
113  *len += addlen;
114  return itvec;
115 }
116 
117 /*
118  * make plain IndexTupleVector
119  */
120 
122 gistfillitupvec(IndexTuple *vec, int veclen, int *memlen)
123 {
124  char *ptr,
125  *ret;
126  int i;
127 
128  *memlen = 0;
129 
130  for (i = 0; i < veclen; i++)
131  *memlen += IndexTupleSize(vec[i]);
132 
133  ptr = ret = palloc(*memlen);
134 
135  for (i = 0; i < veclen; i++)
136  {
137  memcpy(ptr, vec[i], IndexTupleSize(vec[i]));
138  ptr += IndexTupleSize(vec[i]);
139  }
140 
141  return (IndexTupleData *) ret;
142 }
143 
144 /*
145  * Make unions of keys in IndexTuple vector (one union datum per index column).
146  * Union Datums are returned into the attr/isnull arrays.
147  * Resulting Datums aren't compressed.
148  */
149 void
150 gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len,
151  Datum *attr, bool *isnull)
152 {
153  int i;
154  GistEntryVector *evec;
155  int attrsize;
156 
157  evec = (GistEntryVector *) palloc((len + 2) * sizeof(GISTENTRY) + GEVHDRSZ);
158 
159  for (i = 0; i < giststate->tupdesc->natts; i++)
160  {
161  int j;
162 
163  /* Collect non-null datums for this column */
164  evec->n = 0;
165  for (j = 0; j < len; j++)
166  {
167  Datum datum;
168  bool IsNull;
169 
170  datum = index_getattr(itvec[j], i + 1, giststate->tupdesc, &IsNull);
171  if (IsNull)
172  continue;
173 
174  gistdentryinit(giststate, i,
175  evec->vector + evec->n,
176  datum,
177  NULL, NULL, (OffsetNumber) 0,
178  FALSE, IsNull);
179  evec->n++;
180  }
181 
182  /* If this column was all NULLs, the union is NULL */
183  if (evec->n == 0)
184  {
185  attr[i] = (Datum) 0;
186  isnull[i] = TRUE;
187  }
188  else
189  {
190  if (evec->n == 1)
191  {
192  /* unionFn may expect at least two inputs */
193  evec->n = 2;
194  evec->vector[1] = evec->vector[0];
195  }
196 
197  /* Make union and store in attr array */
198  attr[i] = FunctionCall2Coll(&giststate->unionFn[i],
199  giststate->supportCollation[i],
200  PointerGetDatum(evec),
201  PointerGetDatum(&attrsize));
202 
203  isnull[i] = FALSE;
204  }
205  }
206 }
207 
208 /*
209  * Return an IndexTuple containing the result of applying the "union"
210  * method to the specified IndexTuple vector.
211  */
213 gistunion(Relation r, IndexTuple *itvec, int len, GISTSTATE *giststate)
214 {
215  Datum attr[INDEX_MAX_KEYS];
216  bool isnull[INDEX_MAX_KEYS];
217 
218  gistMakeUnionItVec(giststate, itvec, len, attr, isnull);
219 
220  return gistFormTuple(giststate, r, attr, isnull, false);
221 }
222 
223 /*
224  * makes union of two key
225  */
226 void
227 gistMakeUnionKey(GISTSTATE *giststate, int attno,
228  GISTENTRY *entry1, bool isnull1,
229  GISTENTRY *entry2, bool isnull2,
230  Datum *dst, bool *dstisnull)
231 {
232  /* we need a GistEntryVector with room for exactly 2 elements */
233  union
234  {
235  GistEntryVector gev;
236  char padding[2 * sizeof(GISTENTRY) + GEVHDRSZ];
237  } storage;
238  GistEntryVector *evec = &storage.gev;
239  int dstsize;
240 
241  evec->n = 2;
242 
243  if (isnull1 && isnull2)
244  {
245  *dstisnull = TRUE;
246  *dst = (Datum) 0;
247  }
248  else
249  {
250  if (isnull1 == FALSE && isnull2 == FALSE)
251  {
252  evec->vector[0] = *entry1;
253  evec->vector[1] = *entry2;
254  }
255  else if (isnull1 == FALSE)
256  {
257  evec->vector[0] = *entry1;
258  evec->vector[1] = *entry1;
259  }
260  else
261  {
262  evec->vector[0] = *entry2;
263  evec->vector[1] = *entry2;
264  }
265 
266  *dstisnull = FALSE;
267  *dst = FunctionCall2Coll(&giststate->unionFn[attno],
268  giststate->supportCollation[attno],
269  PointerGetDatum(evec),
270  PointerGetDatum(&dstsize));
271  }
272 }
273 
274 bool
275 gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
276 {
277  bool result;
278 
279  FunctionCall3Coll(&giststate->equalFn[attno],
280  giststate->supportCollation[attno],
281  a, b,
282  PointerGetDatum(&result));
283  return result;
284 }
285 
286 /*
287  * Decompress all keys in tuple
288  */
289 void
291  OffsetNumber o, GISTENTRY *attdata, bool *isnull)
292 {
293  int i;
294 
295  for (i = 0; i < r->rd_att->natts; i++)
296  {
297  Datum datum;
298 
299  datum = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]);
300  gistdentryinit(giststate, i, &attdata[i],
301  datum, r, p, o,
302  FALSE, isnull[i]);
303  }
304 }
305 
306 /*
307  * Forms union of oldtup and addtup, if union == oldtup then return NULL
308  */
311 {
312  bool neednew = FALSE;
313  GISTENTRY oldentries[INDEX_MAX_KEYS],
314  addentries[INDEX_MAX_KEYS];
315  bool oldisnull[INDEX_MAX_KEYS],
316  addisnull[INDEX_MAX_KEYS];
317  Datum attr[INDEX_MAX_KEYS];
318  bool isnull[INDEX_MAX_KEYS];
319  IndexTuple newtup = NULL;
320  int i;
321 
322  gistDeCompressAtt(giststate, r, oldtup, NULL,
323  (OffsetNumber) 0, oldentries, oldisnull);
324 
325  gistDeCompressAtt(giststate, r, addtup, NULL,
326  (OffsetNumber) 0, addentries, addisnull);
327 
328  for (i = 0; i < r->rd_att->natts; i++)
329  {
330  gistMakeUnionKey(giststate, i,
331  oldentries + i, oldisnull[i],
332  addentries + i, addisnull[i],
333  attr + i, isnull + i);
334 
335  if (neednew)
336  /* we already need new key, so we can skip check */
337  continue;
338 
339  if (isnull[i])
340  /* union of key may be NULL if and only if both keys are NULL */
341  continue;
342 
343  if (!addisnull[i])
344  {
345  if (oldisnull[i] ||
346  !gistKeyIsEQ(giststate, i, oldentries[i].key, attr[i]))
347  neednew = true;
348  }
349  }
350 
351  if (neednew)
352  {
353  /* need to update key */
354  newtup = gistFormTuple(giststate, r, attr, isnull, false);
355  newtup->t_tid = oldtup->t_tid;
356  }
357 
358  return newtup;
359 }
360 
361 /*
362  * Search an upper index page for the entry with lowest penalty for insertion
363  * of the new index key contained in "it".
364  *
365  * Returns the index of the page entry to insert into.
366  */
368 gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */
369  GISTSTATE *giststate)
370 {
371  OffsetNumber result;
372  OffsetNumber maxoff;
373  OffsetNumber i;
374  float best_penalty[INDEX_MAX_KEYS];
375  GISTENTRY entry,
376  identry[INDEX_MAX_KEYS];
377  bool isnull[INDEX_MAX_KEYS];
378  int keep_current_best;
379 
380  Assert(!GistPageIsLeaf(p));
381 
382  gistDeCompressAtt(giststate, r,
383  it, NULL, (OffsetNumber) 0,
384  identry, isnull);
385 
386  /* we'll return FirstOffsetNumber if page is empty (shouldn't happen) */
387  result = FirstOffsetNumber;
388 
389  /*
390  * The index may have multiple columns, and there's a penalty value for
391  * each column. The penalty associated with a column that appears earlier
392  * in the index definition is strictly more important than the penalty of
393  * a column that appears later in the index definition.
394  *
395  * best_penalty[j] is the best penalty we have seen so far for column j,
396  * or -1 when we haven't yet examined column j. Array entries to the
397  * right of the first -1 are undefined.
398  */
399  best_penalty[0] = -1;
400 
401  /*
402  * If we find a tuple that's exactly as good as the currently best one, we
403  * could use either one. When inserting a lot of tuples with the same or
404  * similar keys, it's preferable to descend down the same path when
405  * possible, as that's more cache-friendly. On the other hand, if all
406  * inserts land on the same leaf page after a split, we're never going to
407  * insert anything to the other half of the split, and will end up using
408  * only 50% of the available space. Distributing the inserts evenly would
409  * lead to better space usage, but that hurts cache-locality during
410  * insertion. To get the best of both worlds, when we find a tuple that's
411  * exactly as good as the previous best, choose randomly whether to stick
412  * to the old best, or use the new one. Once we decide to stick to the
413  * old best, we keep sticking to it for any subsequent equally good tuples
414  * we might find. This favors tuples with low offsets, but still allows
415  * some inserts to go to other equally-good subtrees.
416  *
417  * keep_current_best is -1 if we haven't yet had to make a random choice
418  * whether to keep the current best tuple. If we have done so, and
419  * decided to keep it, keep_current_best is 1; if we've decided to
420  * replace, keep_current_best is 0. (This state will be reset to -1 as
421  * soon as we've made the replacement, but sometimes we make the choice in
422  * advance of actually finding a replacement best tuple.)
423  */
424  keep_current_best = -1;
425 
426  /*
427  * Loop over tuples on page.
428  */
429  maxoff = PageGetMaxOffsetNumber(p);
430  Assert(maxoff >= FirstOffsetNumber);
431 
432  for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
433  {
434  IndexTuple itup = (IndexTuple) PageGetItem(p, PageGetItemId(p, i));
435  bool zero_penalty;
436  int j;
437 
438  zero_penalty = true;
439 
440  /* Loop over index attributes. */
441  for (j = 0; j < r->rd_att->natts; j++)
442  {
443  Datum datum;
444  float usize;
445  bool IsNull;
446 
447  /* Compute penalty for this column. */
448  datum = index_getattr(itup, j + 1, giststate->tupdesc, &IsNull);
449  gistdentryinit(giststate, j, &entry, datum, r, p, i,
450  FALSE, IsNull);
451  usize = gistpenalty(giststate, j, &entry, IsNull,
452  &identry[j], isnull[j]);
453  if (usize > 0)
454  zero_penalty = false;
455 
456  if (best_penalty[j] < 0 || usize < best_penalty[j])
457  {
458  /*
459  * New best penalty for column. Tentatively select this tuple
460  * as the target, and record the best penalty. Then reset the
461  * next column's penalty to "unknown" (and indirectly, the
462  * same for all the ones to its right). This will force us to
463  * adopt this tuple's penalty values as the best for all the
464  * remaining columns during subsequent loop iterations.
465  */
466  result = i;
467  best_penalty[j] = usize;
468 
469  if (j < r->rd_att->natts - 1)
470  best_penalty[j + 1] = -1;
471 
472  /* we have new best, so reset keep-it decision */
473  keep_current_best = -1;
474  }
475  else if (best_penalty[j] == usize)
476  {
477  /*
478  * The current tuple is exactly as good for this column as the
479  * best tuple seen so far. The next iteration of this loop
480  * will compare the next column.
481  */
482  }
483  else
484  {
485  /*
486  * The current tuple is worse for this column than the best
487  * tuple seen so far. Skip the remaining columns and move on
488  * to the next tuple, if any.
489  */
490  zero_penalty = false; /* so outer loop won't exit */
491  break;
492  }
493  }
494 
495  /*
496  * If we looped past the last column, and did not update "result",
497  * then this tuple is exactly as good as the prior best tuple.
498  */
499  if (j == r->rd_att->natts && result != i)
500  {
501  if (keep_current_best == -1)
502  {
503  /* we didn't make the random choice yet for this old best */
504  keep_current_best = (random() <= (MAX_RANDOM_VALUE / 2)) ? 1 : 0;
505  }
506  if (keep_current_best == 0)
507  {
508  /* we choose to use the new tuple */
509  result = i;
510  /* choose again if there are even more exactly-as-good ones */
511  keep_current_best = -1;
512  }
513  }
514 
515  /*
516  * If we find a tuple with zero penalty for all columns, and we've
517  * decided we don't want to search for another tuple with equal
518  * penalty, there's no need to examine remaining tuples; just break
519  * out of the loop and return it.
520  */
521  if (zero_penalty)
522  {
523  if (keep_current_best == -1)
524  {
525  /* we didn't make the random choice yet for this old best */
526  keep_current_best = (random() <= (MAX_RANDOM_VALUE / 2)) ? 1 : 0;
527  }
528  if (keep_current_best == 1)
529  break;
530  }
531  }
532 
533  return result;
534 }
535 
536 /*
537  * initialize a GiST entry with a decompressed version of key
538  */
539 void
540 gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
541  Datum k, Relation r, Page pg, OffsetNumber o,
542  bool l, bool isNull)
543 {
544  if (!isNull)
545  {
546  GISTENTRY *dep;
547 
548  gistentryinit(*e, k, r, pg, o, l);
549  dep = (GISTENTRY *)
551  giststate->supportCollation[nkey],
552  PointerGetDatum(e)));
553  /* decompressFn may just return the given pointer */
554  if (dep != e)
555  gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
556  dep->leafkey);
557  }
558  else
559  gistentryinit(*e, (Datum) 0, r, pg, o, l);
560 }
561 
564  Datum attdata[], bool isnull[], bool isleaf)
565 {
566  Datum compatt[INDEX_MAX_KEYS];
567  int i;
568  IndexTuple res;
569 
570  /*
571  * Call the compress method on each attribute.
572  */
573  for (i = 0; i < r->rd_att->natts; i++)
574  {
575  if (isnull[i])
576  compatt[i] = (Datum) 0;
577  else
578  {
579  GISTENTRY centry;
580  GISTENTRY *cep;
581 
582  gistentryinit(centry, attdata[i], r, NULL, (OffsetNumber) 0,
583  isleaf);
584  cep = (GISTENTRY *)
586  giststate->supportCollation[i],
587  PointerGetDatum(&centry)));
588  compatt[i] = cep->key;
589  }
590  }
591 
592  res = index_form_tuple(giststate->tupdesc, compatt, isnull);
593 
594  /*
595  * The offset number on tuples on internal pages is unused. For historical
596  * reasons, it is set to 0xffff.
597  */
598  ItemPointerSetOffsetNumber(&(res->t_tid), 0xffff);
599  return res;
600 }
601 
602 /*
603  * initialize a GiST entry with fetched value in key field
604  */
605 static Datum
606 gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r)
607 {
608  GISTENTRY fentry;
609  GISTENTRY *fep;
610 
611  gistentryinit(fentry, k, r, NULL, (OffsetNumber) 0, false);
612 
613  fep = (GISTENTRY *)
614  DatumGetPointer(FunctionCall1Coll(&giststate->fetchFn[nkey],
615  giststate->supportCollation[nkey],
616  PointerGetDatum(&fentry)));
617 
618  /* fetchFn set 'key', return it to the caller */
619  return fep->key;
620 }
621 
622 /*
623  * Fetch all keys in tuple.
624  * returns new IndexTuple that contains GISTENTRY with fetched data
625  */
628 {
629  MemoryContext oldcxt = MemoryContextSwitchTo(giststate->tempCxt);
631  bool isnull[INDEX_MAX_KEYS];
632  int i;
633 
634  for (i = 0; i < r->rd_att->natts; i++)
635  {
636  Datum datum;
637 
638  datum = index_getattr(tuple, i + 1, giststate->tupdesc, &isnull[i]);
639 
640  if (giststate->fetchFn[i].fn_oid != InvalidOid)
641  {
642  if (!isnull[i])
643  fetchatt[i] = gistFetchAtt(giststate, i, datum, r);
644  else
645  fetchatt[i] = (Datum) 0;
646  }
647  else
648  {
649  /*
650  * Index-only scans not supported for this column. Since the
651  * planner chose an index-only scan anyway, it is not interested
652  * in this column, and we can replace it with a NULL.
653  */
654  isnull[i] = true;
655  fetchatt[i] = (Datum) 0;
656  }
657  }
658  MemoryContextSwitchTo(oldcxt);
659 
660  return index_form_tuple(giststate->fetchTupdesc, fetchatt, isnull);
661 }
662 
663 float
664 gistpenalty(GISTSTATE *giststate, int attno,
665  GISTENTRY *orig, bool isNullOrig,
666  GISTENTRY *add, bool isNullAdd)
667 {
668  float penalty = 0.0;
669 
670  if (giststate->penaltyFn[attno].fn_strict == FALSE ||
671  (isNullOrig == FALSE && isNullAdd == FALSE))
672  {
673  FunctionCall3Coll(&giststate->penaltyFn[attno],
674  giststate->supportCollation[attno],
675  PointerGetDatum(orig),
676  PointerGetDatum(add),
677  PointerGetDatum(&penalty));
678  /* disallow negative or NaN penalty */
679  if (isnan(penalty) || penalty < 0.0)
680  penalty = 0.0;
681  }
682  else if (isNullOrig && isNullAdd)
683  penalty = 0.0;
684  else
685  {
686  /* try to prevent mixing null and non-null values */
687  penalty = get_float4_infinity();
688  }
689 
690  return penalty;
691 }
692 
693 /*
694  * Initialize a new index page
695  */
696 void
698 {
699  GISTPageOpaque opaque;
700  Page page;
701  Size pageSize;
702 
703  pageSize = BufferGetPageSize(b);
704  page = BufferGetPage(b);
705  PageInit(page, pageSize, sizeof(GISTPageOpaqueData));
706 
707  opaque = GistPageGetOpaque(page);
708  /* page was already zeroed by PageInit, so this is not needed: */
709  /* memset(&(opaque->nsn), 0, sizeof(GistNSN)); */
710  opaque->rightlink = InvalidBlockNumber;
711  opaque->flags = f;
712  opaque->gist_page_id = GIST_PAGE_ID;
713 }
714 
715 /*
716  * Verify that a freshly-read page looks sane.
717  */
718 void
720 {
721  Page page = BufferGetPage(buf);
722 
723  /*
724  * ReadBuffer verifies that every newly-read page passes
725  * PageHeaderIsValid, which means it either contains a reasonably sane
726  * page header or is all-zero. We have to defend against the all-zero
727  * case, however.
728  */
729  if (PageIsNew(page))
730  ereport(ERROR,
731  (errcode(ERRCODE_INDEX_CORRUPTED),
732  errmsg("index \"%s\" contains unexpected zero page at block %u",
734  BufferGetBlockNumber(buf)),
735  errhint("Please REINDEX it.")));
736 
737  /*
738  * Additionally check that the special area looks sane.
739  */
740  if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GISTPageOpaqueData)))
741  ereport(ERROR,
742  (errcode(ERRCODE_INDEX_CORRUPTED),
743  errmsg("index \"%s\" contains corrupted page at block %u",
745  BufferGetBlockNumber(buf)),
746  errhint("Please REINDEX it.")));
747 }
748 
749 
750 /*
751  * Allocate a new page (either by recycling, or by extending the index file)
752  *
753  * The returned buffer is already pinned and exclusive-locked
754  *
755  * Caller is responsible for initializing the page by calling GISTInitBuffer
756  */
757 Buffer
759 {
760  Buffer buffer;
761  bool needLock;
762 
763  /* First, try to get a page from FSM */
764  for (;;)
765  {
767 
768  if (blkno == InvalidBlockNumber)
769  break; /* nothing left in FSM */
770 
771  buffer = ReadBuffer(r, blkno);
772 
773  /*
774  * We have to guard against the possibility that someone else already
775  * recycled this page; the buffer may be locked if so.
776  */
777  if (ConditionalLockBuffer(buffer))
778  {
779  Page page = BufferGetPage(buffer);
780 
781  if (PageIsNew(page))
782  return buffer; /* OK to use, if never initialized */
783 
784  gistcheckpage(r, buffer);
785 
786  if (GistPageIsDeleted(page))
787  return buffer; /* OK to use */
788 
789  LockBuffer(buffer, GIST_UNLOCK);
790  }
791 
792  /* Can't use it, so release buffer and try again */
793  ReleaseBuffer(buffer);
794  }
795 
796  /* Must extend the file */
797  needLock = !RELATION_IS_LOCAL(r);
798 
799  if (needLock)
801 
802  buffer = ReadBuffer(r, P_NEW);
803  LockBuffer(buffer, GIST_EXCLUSIVE);
804 
805  if (needLock)
807 
808  return buffer;
809 }
810 
811 bytea *
812 gistoptions(Datum reloptions, bool validate)
813 {
815  GiSTOptions *rdopts;
816  int numoptions;
817  static const relopt_parse_elt tab[] = {
818  {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},
819  {"buffering", RELOPT_TYPE_STRING, offsetof(GiSTOptions, bufferingModeOffset)}
820  };
821 
822  options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIST,
823  &numoptions);
824 
825  /* if none set, we're done */
826  if (numoptions == 0)
827  return NULL;
828 
829  rdopts = allocateReloptStruct(sizeof(GiSTOptions), options, numoptions);
830 
831  fillRelOptions((void *) rdopts, sizeof(GiSTOptions), options, numoptions,
832  validate, tab, lengthof(tab));
833 
834  pfree(options);
835 
836  return (bytea *) rdopts;
837 }
838 
839 /*
840  * Temporary and unlogged GiST indexes are not WAL-logged, but we need LSNs
841  * to detect concurrent page splits anyway. This function provides a fake
842  * sequence of LSNs for that purpose.
843  */
846 {
847  static XLogRecPtr counter = 1;
848 
849  if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
850  {
851  /*
852  * Temporary relations are only accessible in our session, so a simple
853  * backend-local counter will do.
854  */
855  return counter++;
856  }
857  else
858  {
859  /*
860  * Unlogged relations are accessible from other backends, and survive
861  * (clean) restarts. GetFakeLSNForUnloggedRel() handles that for us.
862  */
863  Assert(rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED);
864  return GetFakeLSNForUnloggedRel();
865  }
866 }
Relation rel
Definition: gist.h:124
TupleDesc tupdesc
Definition: gist_private.h:82
static Datum gistFetchAtt(GISTSTATE *giststate, int nkey, Datum k, Relation r)
Definition: gistutil.c:606
#define GistPageIsDeleted(page)
Definition: gist.h:135
#define PageIsEmpty(page)
Definition: bufpage.h:219
int errhint(const char *fmt,...)
Definition: elog.c:987
OffsetNumber PageAddItem(Page page, Item item, Size size, OffsetNumber offsetNumber, bool overwrite, bool is_heap)
Definition: bufpage.c:176
#define RELPERSISTENCE_UNLOGGED
Definition: pg_class.h:165
FmgrInfo fetchFn[INDEX_MAX_KEYS]
Definition: gist_private.h:94
#define ExclusiveLock
Definition: lockdefs.h:44
Oid supportCollation[INDEX_MAX_KEYS]
Definition: gist_private.h:97
#define PointerGetDatum(X)
Definition: postgres.h:564
long random(void)
Definition: random.c:22
FmgrInfo compressFn[INDEX_MAX_KEYS]
Definition: gist_private.h:88
void gistDeCompressAtt(GISTSTATE *giststate, Relation r, IndexTuple tuple, Page p, OffsetNumber o, GISTENTRY *attdata, bool *isnull)
Definition: gistutil.c:290
FmgrInfo equalFn[INDEX_MAX_KEYS]
Definition: gist_private.h:92
#define RELATION_IS_LOCAL(relation)
Definition: rel.h:448
ItemPointerData t_tid
Definition: itup.h:37
void gistfillbuffer(Page page, IndexTuple *itup, int len, OffsetNumber off)
Definition: gistutil.c:29
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
Pointer Item
Definition: item.h:17
IndexTuple gistFetchTuple(GISTSTATE *giststate, Relation r, IndexTuple tuple)
Definition: gistutil.c:627
int32 n
Definition: gist.h:160
int errcode(int sqlerrcode)
Definition: elog.c:575
#define GIST_UNLOCK
Definition: gist_private.h:46
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3292
#define P_NEW
Definition: bufmgr.h:96
#define lengthof(array)
Definition: c.h:554
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1307
Form_pg_class rd_rel
Definition: rel.h:83
bytea * gistoptions(Datum reloptions, bool validate)
Definition: gistutil.c:812
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:354
IndexTuple gistgetadjusted(Relation r, IndexTuple oldtup, IndexTuple addtup, GISTSTATE *giststate)
Definition: gistutil.c:310
int natts
Definition: tupdesc.h:73
#define fetchatt(A, T)
Definition: tupmacs.h:37
XLogRecPtr gistGetFakeLSN(Relation rel)
Definition: gistutil.c:845
Size PageGetFreeSpace(Page page)
Definition: bufpage.c:567
bool gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
Definition: gistutil.c:275
uint16 OffsetNumber
Definition: off.h:24
Page page
Definition: gist.h:125
float get_float4_infinity(void)
Definition: float.c:148
GISTENTRY vector[FLEXIBLE_ARRAY_MEMBER]
Definition: gist.h:161
IndexTuple index_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: indextuple.c:37
IndexTuple * gistextractpage(Page page, int *len)
Definition: gistutil.c:90
uint16 gist_page_id
Definition: gist.h:63
void pfree(void *pointer)
Definition: mcxt.c:995
#define GEVHDRSZ
Definition: gist.h:164
float gistpenalty(GISTSTATE *giststate, int attno, GISTENTRY *orig, bool isNullOrig, GISTENTRY *add, bool isNullAdd)
Definition: gistutil.c:664
void gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e, Datum k, Relation r, Page pg, OffsetNumber o, bool l, bool isNull)
Definition: gistutil.c:540
struct GISTENTRY GISTENTRY
#define ERROR
Definition: elog.h:43
bool fn_strict
Definition: fmgr.h:58
int fillfactor
Definition: pgbench.c:111
#define FALSE
Definition: c.h:218
XLogRecPtr GetFakeLSNForUnloggedRel(void)
Definition: xlog.c:4534
MemoryContext tempCxt
Definition: gist_private.h:80
BlockNumber blkno
Definition: ginvacuum.c:290
Datum key
Definition: gist.h:123
void * allocateReloptStruct(Size base, relopt_value *options, int numoptions)
Definition: reloptions.c:1166
static char * buf
Definition: pg_test_fsync.c:65
#define memmove(d, s, c)
Definition: c.h:1038
#define FirstOffsetNumber
Definition: off.h:27
IndexTupleData * IndexTuple
Definition: itup.h:53
#define MAX_RANDOM_VALUE
bool gistnospace(Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace)
Definition: gistutil.c:54
#define RelationGetRelationName(relation)
Definition: rel.h:361
FmgrInfo penaltyFn[INDEX_MAX_KEYS]
Definition: gist_private.h:90
unsigned int uint32
Definition: c.h:265
struct ItemIdData ItemIdData
uint16 flags
Definition: gist.h:62
#define BufferGetPage(buffer)
Definition: bufmgr.h:174
#define ereport(elevel, rest)
Definition: elog.h:122
FmgrInfo decompressFn[INDEX_MAX_KEYS]
Definition: gist_private.h:89
bool ConditionalLockBuffer(Buffer buffer)
Definition: bufmgr.c:3555
bool leafkey
Definition: gist.h:127
OffsetNumber gistchoose(Relation r, Page p, IndexTuple it, GISTSTATE *giststate)
Definition: gistutil.c:368
#define GistPageIsLeaf(page)
Definition: gist.h:132
static char ** options
void fillRelOptions(void *rdopts, Size basesize, relopt_value *options, int numoptions, bool validate, const relopt_parse_elt *elems, int numelems)
Definition: reloptions.c:1190
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:232
void LockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:332
#define GiSTPageSize
Definition: gist_private.h:489
IndexTuple * gistjoinvector(IndexTuple *itvec, int *len, IndexTuple *additvec, int addlen)
Definition: gistutil.c:109
void UnlockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:382
uintptr_t Datum
Definition: postgres.h:374
Datum FunctionCall1Coll(FmgrInfo *flinfo, Oid collation, Datum arg1)
Definition: fmgr.c:1287
#define BufferGetPageSize(buffer)
Definition: bufmgr.h:161
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3529
TupleDesc rd_att
Definition: rel.h:84
#define InvalidOffsetNumber
Definition: off.h:26
#define InvalidOid
Definition: postgres_ext.h:36
Oid fn_oid
Definition: fmgr.h:56
Datum FunctionCall3Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, Datum arg3)
Definition: fmgr.c:1333
BlockNumber GetFreeIndexPage(Relation rel)
Definition: indexfsm.c:38
bool gistfitpage(IndexTuple *itvec, int len)
Definition: gistutil.c:74
#define GistPageGetOpaque(page)
Definition: gist.h:130
void gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len, Datum *attr, bool *isnull)
Definition: gistutil.c:150
#define NULL
Definition: c.h:226
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:667
#define gistentryinit(e, k, r, pg, o, l)
Definition: gist.h:169
TupleDesc fetchTupdesc
Definition: gist_private.h:83
void gistcheckpage(Relation rel, Buffer buf)
Definition: gistutil.c:719
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:594
void gistMakeUnionKey(GISTSTATE *giststate, int attno, GISTENTRY *entry1, bool isnull1, GISTENTRY *entry2, bool isnull2, Datum *dst, bool *dstisnull)
Definition: gistutil.c:227
#define INDEX_MAX_KEYS
#define OffsetNumberNext(offsetNumber)
Definition: off.h:53
size_t Size
Definition: c.h:352
IndexTupleData * gistfillitupvec(IndexTuple *vec, int veclen, int *memlen)
Definition: gistutil.c:122
#define InvalidBlockNumber
Definition: block.h:33
#define MAXALIGN(LEN)
Definition: c.h:580
#define index_getattr(tup, attnum, tupleDesc, isnull)
Definition: itup.h:100
#define PageGetSpecialSize(page)
Definition: bufpage.h:297
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1024
#define DatumGetPointer(X)
Definition: postgres.h:557
#define ItemPointerSetOffsetNumber(pointer, offsetNumber)
Definition: itemptr.h:111
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2588
e
Definition: preproc-init.c:82
IndexTuple gistunion(Relation r, IndexTuple *itvec, int len, GISTSTATE *giststate)
Definition: gistutil.c:213
#define PageIsNew(page)
Definition: bufpage.h:226
void * palloc(Size size)
Definition: mcxt.c:894
int errmsg(const char *fmt,...)
Definition: elog.c:797
int i
void GISTInitBuffer(Buffer b, uint32 f)
Definition: gistutil.c:697
IndexTuple gistFormTuple(GISTSTATE *giststate, Relation r, Datum attdata[], bool isnull[], bool isleaf)
Definition: gistutil.c:563
BlockNumber rightlink
Definition: gist.h:61
Definition: c.h:434
#define TRUE
Definition: c.h:214
relopt_value * parseRelOptions(Datum options, bool validate, relopt_kind kind, int *numrelopts)
Definition: reloptions.c:967
#define elog
Definition: elog.h:218
#define RELPERSISTENCE_TEMP
Definition: pg_class.h:166
FmgrInfo unionFn[INDEX_MAX_KEYS]
Definition: gist_private.h:87
#define GIST_EXCLUSIVE
Definition: gist_private.h:45
OffsetNumber offset
Definition: gist.h:126
int Buffer
Definition: buf.h:23
#define GIST_PAGE_ID
Definition: gist.h:74
Buffer gistNewBuffer(Relation r)
Definition: gistutil.c:758
#define offsetof(type, field)
Definition: c.h:547
#define PageGetItem(page, itemId)
Definition: bufpage.h:337
Pointer Page
Definition: bufpage.h:74
#define IndexTupleSize(itup)
Definition: itup.h:70
void PageInit(Page page, Size pageSize, Size specialSize)
Definition: bufpage.c:41