PostgreSQL Source Code  git master
ginfast.c File Reference
#include "postgres.h"
#include "access/gin_private.h"
#include "access/ginxlog.h"
#include "access/xlog.h"
#include "access/xloginsert.h"
#include "catalog/pg_am.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
#include "port/pg_bitutils.h"
#include "postmaster/autovacuum.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/rel.h"
Include dependency graph for ginfast.c:

Go to the source code of this file.

Data Structures

struct  KeyArray
 

Macros

#define GIN_PAGE_FREESIZE    ( BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - MAXALIGN(sizeof(GinPageOpaqueData)) )
 

Typedefs

typedef struct KeyArray KeyArray
 

Functions

static int32 writeListPage (Relation index, Buffer buffer, IndexTuple *tuples, int32 ntuples, BlockNumber rightlink)
 
static void makeSublist (Relation index, IndexTuple *tuples, int32 ntuples, GinMetaPageData *res)
 
void ginHeapTupleFastInsert (GinState *ginstate, GinTupleCollector *collector)
 
void ginHeapTupleFastCollect (GinState *ginstate, GinTupleCollector *collector, OffsetNumber attnum, Datum value, bool isNull, ItemPointer ht_ctid)
 
static void shiftList (Relation index, Buffer metabuffer, BlockNumber newHead, bool fill_fsm, IndexBulkDeleteResult *stats)
 
static void initKeyArray (KeyArray *keys, int32 maxvalues)
 
static void addDatum (KeyArray *keys, Datum datum, GinNullCategory category)
 
static void processPendingPage (BuildAccumulator *accum, KeyArray *ka, Page page, OffsetNumber startoff)
 
void ginInsertCleanup (GinState *ginstate, bool full_clean, bool fill_fsm, bool forceCleanup, IndexBulkDeleteResult *stats)
 
Datum gin_clean_pending_list (PG_FUNCTION_ARGS)
 

Variables

int gin_pending_list_limit = 0
 

Macro Definition Documentation

◆ GIN_PAGE_FREESIZE

#define GIN_PAGE_FREESIZE    ( BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - MAXALIGN(sizeof(GinPageOpaqueData)) )

Definition at line 41 of file ginfast.c.

Typedef Documentation

◆ KeyArray

typedef struct KeyArray KeyArray

Function Documentation

◆ addDatum()

static void addDatum ( KeyArray keys,
Datum  datum,
GinNullCategory  category 
)
static

Definition at line 684 of file ginfast.c.

685 {
686  if (keys->nvalues >= keys->maxvalues)
687  {
688  keys->maxvalues *= 2;
689  keys->keys = repalloc_array(keys->keys, Datum, keys->maxvalues);
691  }
692 
693  keys->keys[keys->nvalues] = datum;
694  keys->categories[keys->nvalues] = category;
695  keys->nvalues++;
696 }
#define repalloc_array(pointer, type, count)
Definition: fe_memutils.h:66
signed char GinNullCategory
Definition: ginblock.h:206
uintptr_t Datum
Definition: postgres.h:64
Datum * keys
Definition: ginfast.c:46
GinNullCategory * categories
Definition: ginfast.c:47
int32 nvalues
Definition: ginfast.c:48
int32 maxvalues
Definition: ginfast.c:49

References KeyArray::categories, KeyArray::keys, KeyArray::maxvalues, KeyArray::nvalues, and repalloc_array.

Referenced by processPendingPage().

◆ gin_clean_pending_list()

Datum gin_clean_pending_list ( PG_FUNCTION_ARGS  )

Definition at line 1030 of file ginfast.c.

1031 {
1032  Oid indexoid = PG_GETARG_OID(0);
1033  Relation indexRel = index_open(indexoid, RowExclusiveLock);
1034  IndexBulkDeleteResult stats;
1035  GinState ginstate;
1036 
1037  if (RecoveryInProgress())
1038  ereport(ERROR,
1039  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1040  errmsg("recovery is in progress"),
1041  errhint("GIN pending list cannot be cleaned up during recovery.")));
1042 
1043  /* Must be a GIN index */
1044  if (indexRel->rd_rel->relkind != RELKIND_INDEX ||
1045  indexRel->rd_rel->relam != GIN_AM_OID)
1046  ereport(ERROR,
1047  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1048  errmsg("\"%s\" is not a GIN index",
1049  RelationGetRelationName(indexRel))));
1050 
1051  /*
1052  * Reject attempts to read non-local temporary relations; we would be
1053  * likely to get wrong data since we have no visibility into the owning
1054  * session's local buffers.
1055  */
1056  if (RELATION_IS_OTHER_TEMP(indexRel))
1057  ereport(ERROR,
1058  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1059  errmsg("cannot access temporary indexes of other sessions")));
1060 
1061  /* User must own the index (comparable to privileges needed for VACUUM) */
1062  if (!object_ownercheck(RelationRelationId, indexoid, GetUserId()))
1064  RelationGetRelationName(indexRel));
1065 
1066  memset(&stats, 0, sizeof(stats));
1067  initGinState(&ginstate, indexRel);
1068  ginInsertCleanup(&ginstate, true, true, true, &stats);
1069 
1070  index_close(indexRel, RowExclusiveLock);
1071 
1072  PG_RETURN_INT64((int64) stats.pages_deleted);
1073 }
@ ACLCHECK_NOT_OWNER
Definition: acl.h:184
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:2669
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:3961
int errhint(const char *fmt,...)
Definition: elog.c:1316
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define PG_RETURN_INT64(x)
Definition: fmgr.h:368
void ginInsertCleanup(GinState *ginstate, bool full_clean, bool fill_fsm, bool forceCleanup, IndexBulkDeleteResult *stats)
Definition: ginfast.c:779
void initGinState(GinState *state, Relation index)
Definition: ginutil.c:96
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:158
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:132
#define RowExclusiveLock
Definition: lockdefs.h:38
Oid GetUserId(void)
Definition: miscinit.c:509
@ OBJECT_INDEX
Definition: parsenodes.h:2140
unsigned int Oid
Definition: postgres_ext.h:31
#define RelationGetRelationName(relation)
Definition: rel.h:538
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:659
BlockNumber pages_deleted
Definition: genam.h:82
Form_pg_class rd_rel
Definition: rel.h:111
bool RecoveryInProgress(void)
Definition: xlog.c:5948

References aclcheck_error(), ACLCHECK_NOT_OWNER, ereport, errcode(), errhint(), errmsg(), ERROR, GetUserId(), ginInsertCleanup(), index_close(), index_open(), initGinState(), OBJECT_INDEX, object_ownercheck(), IndexBulkDeleteResult::pages_deleted, PG_GETARG_OID, PG_RETURN_INT64, RelationData::rd_rel, RecoveryInProgress(), RELATION_IS_OTHER_TEMP, RelationGetRelationName, and RowExclusiveLock.

◆ ginHeapTupleFastCollect()

void ginHeapTupleFastCollect ( GinState ginstate,
GinTupleCollector collector,
OffsetNumber  attnum,
Datum  value,
bool  isNull,
ItemPointer  ht_ctid 
)

Definition at line 482 of file ginfast.c.

486 {
487  Datum *entries;
488  GinNullCategory *categories;
489  int32 i,
490  nentries;
491 
492  /*
493  * Extract the key values that need to be inserted in the index
494  */
495  entries = ginExtractEntries(ginstate, attnum, value, isNull,
496  &nentries, &categories);
497 
498  /*
499  * Protect against integer overflow in allocation calculations
500  */
501  if (nentries < 0 ||
502  collector->ntuples + nentries > MaxAllocSize / sizeof(IndexTuple))
503  elog(ERROR, "too many entries for GIN index");
504 
505  /*
506  * Allocate/reallocate memory for storing collected tuples
507  */
508  if (collector->tuples == NULL)
509  {
510  /*
511  * Determine the number of elements to allocate in the tuples array
512  * initially. Make it a power of 2 to avoid wasting memory when
513  * resizing (since palloc likes powers of 2).
514  */
515  collector->lentuples = pg_nextpower2_32(Max(16, nentries));
516  collector->tuples = palloc_array(IndexTuple, collector->lentuples);
517  }
518  else if (collector->lentuples < collector->ntuples + nentries)
519  {
520  /*
521  * Advance lentuples to the next suitable power of 2. This won't
522  * overflow, though we could get to a value that exceeds
523  * MaxAllocSize/sizeof(IndexTuple), causing an error in repalloc.
524  */
525  collector->lentuples = pg_nextpower2_32(collector->ntuples + nentries);
526  collector->tuples = repalloc_array(collector->tuples,
527  IndexTuple, collector->lentuples);
528  }
529 
530  /*
531  * Build an index tuple for each key value, and add to array. In pending
532  * tuples we just stick the heap TID into t_tid.
533  */
534  for (i = 0; i < nentries; i++)
535  {
536  IndexTuple itup;
537 
538  itup = GinFormTuple(ginstate, attnum, entries[i], categories[i],
539  NULL, 0, 0, true);
540  itup->t_tid = *ht_ctid;
541  collector->tuples[collector->ntuples++] = itup;
542  collector->sumsize += IndexTupleSize(itup);
543  }
544 }
signed int int32
Definition: c.h:483
#define Max(x, y)
Definition: c.h:987
#define palloc_array(type, count)
Definition: fe_memutils.h:64
IndexTuple GinFormTuple(GinState *ginstate, OffsetNumber attnum, Datum key, GinNullCategory category, Pointer data, Size dataSize, int nipd, bool errorTooBig)
Definition: ginentrypage.c:45
Datum * ginExtractEntries(GinState *ginstate, OffsetNumber attnum, Datum value, bool isNull, int32 *nentries, GinNullCategory **categories)
Definition: ginutil.c:482
static struct @148 value
int i
Definition: isn.c:73
#define IndexTupleSize(itup)
Definition: itup.h:70
#define MaxAllocSize
Definition: memutils.h:40
int16 attnum
Definition: pg_attribute.h:74
static uint32 pg_nextpower2_32(uint32 num)
Definition: pg_bitutils.h:189
IndexTuple * tuples
Definition: gin_private.h:454
ItemPointerData t_tid
Definition: itup.h:37

References attnum, elog(), ERROR, ginExtractEntries(), GinFormTuple(), i, IndexTupleSize, GinTupleCollector::lentuples, Max, MaxAllocSize, GinTupleCollector::ntuples, palloc_array, pg_nextpower2_32(), repalloc_array, GinTupleCollector::sumsize, IndexTupleData::t_tid, GinTupleCollector::tuples, and value.

Referenced by gininsert().

◆ ginHeapTupleFastInsert()

void ginHeapTupleFastInsert ( GinState ginstate,
GinTupleCollector collector 
)

Definition at line 219 of file ginfast.c.

220 {
221  Relation index = ginstate->index;
222  Buffer metabuffer;
223  Page metapage;
224  GinMetaPageData *metadata = NULL;
225  Buffer buffer = InvalidBuffer;
226  Page page = NULL;
228  bool separateList = false;
229  bool needCleanup = false;
230  int cleanupSize;
231  bool needWal;
232 
233  if (collector->ntuples == 0)
234  return;
235 
236  needWal = RelationNeedsWAL(index);
237 
238  data.locator = index->rd_locator;
239  data.ntuples = 0;
240  data.newRightlink = data.prevTail = InvalidBlockNumber;
241 
242  metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
243  metapage = BufferGetPage(metabuffer);
244 
245  /*
246  * An insertion to the pending list could logically belong anywhere in the
247  * tree, so it conflicts with all serializable scans. All scans acquire a
248  * predicate lock on the metabuffer to represent that. Therefore we'll
249  * check for conflicts in, but not until we have the page locked and are
250  * ready to modify the page.
251  */
252 
253  if (collector->sumsize + collector->ntuples * sizeof(ItemIdData) > GinListPageSize)
254  {
255  /*
256  * Total size is greater than one page => make sublist
257  */
258  separateList = true;
259  }
260  else
261  {
262  LockBuffer(metabuffer, GIN_EXCLUSIVE);
263  metadata = GinPageGetMeta(metapage);
264 
265  if (metadata->head == InvalidBlockNumber ||
266  collector->sumsize + collector->ntuples * sizeof(ItemIdData) > metadata->tailFreeSize)
267  {
268  /*
269  * Pending list is empty or total size is greater than freespace
270  * on tail page => make sublist
271  *
272  * We unlock metabuffer to keep high concurrency
273  */
274  separateList = true;
275  LockBuffer(metabuffer, GIN_UNLOCK);
276  }
277  }
278 
279  if (separateList)
280  {
281  /*
282  * We should make sublist separately and append it to the tail
283  */
284  GinMetaPageData sublist;
285 
286  memset(&sublist, 0, sizeof(GinMetaPageData));
287  makeSublist(index, collector->tuples, collector->ntuples, &sublist);
288 
289  /*
290  * metapage was unlocked, see above
291  */
292  LockBuffer(metabuffer, GIN_EXCLUSIVE);
293  metadata = GinPageGetMeta(metapage);
294 
296 
297  if (metadata->head == InvalidBlockNumber)
298  {
299  /*
300  * Main list is empty, so just insert sublist as main list
301  */
303 
304  metadata->head = sublist.head;
305  metadata->tail = sublist.tail;
306  metadata->tailFreeSize = sublist.tailFreeSize;
307 
308  metadata->nPendingPages = sublist.nPendingPages;
309  metadata->nPendingHeapTuples = sublist.nPendingHeapTuples;
310 
311  if (needWal)
312  XLogBeginInsert();
313  }
314  else
315  {
316  /*
317  * Merge lists
318  */
319  data.prevTail = metadata->tail;
320  data.newRightlink = sublist.head;
321 
322  buffer = ReadBuffer(index, metadata->tail);
323  LockBuffer(buffer, GIN_EXCLUSIVE);
324  page = BufferGetPage(buffer);
325 
326  Assert(GinPageGetOpaque(page)->rightlink == InvalidBlockNumber);
327 
329 
330  GinPageGetOpaque(page)->rightlink = sublist.head;
331 
332  MarkBufferDirty(buffer);
333 
334  metadata->tail = sublist.tail;
335  metadata->tailFreeSize = sublist.tailFreeSize;
336 
337  metadata->nPendingPages += sublist.nPendingPages;
338  metadata->nPendingHeapTuples += sublist.nPendingHeapTuples;
339 
340  if (needWal)
341  {
342  XLogBeginInsert();
344  }
345  }
346  }
347  else
348  {
349  /*
350  * Insert into tail page. Metapage is already locked
351  */
352  OffsetNumber l,
353  off;
354  int i,
355  tupsize;
356  char *ptr;
357  char *collectordata;
358 
360 
361  buffer = ReadBuffer(index, metadata->tail);
362  LockBuffer(buffer, GIN_EXCLUSIVE);
363  page = BufferGetPage(buffer);
364 
365  off = (PageIsEmpty(page)) ? FirstOffsetNumber :
367 
368  collectordata = ptr = (char *) palloc(collector->sumsize);
369 
370  data.ntuples = collector->ntuples;
371 
373 
374  if (needWal)
375  XLogBeginInsert();
376 
377  /*
378  * Increase counter of heap tuples
379  */
380  Assert(GinPageGetOpaque(page)->maxoff <= metadata->nPendingHeapTuples);
381  GinPageGetOpaque(page)->maxoff++;
382  metadata->nPendingHeapTuples++;
383 
384  for (i = 0; i < collector->ntuples; i++)
385  {
386  tupsize = IndexTupleSize(collector->tuples[i]);
387  l = PageAddItem(page, (Item) collector->tuples[i], tupsize, off, false, false);
388 
389  if (l == InvalidOffsetNumber)
390  elog(ERROR, "failed to add item to index page in \"%s\"",
392 
393  memcpy(ptr, collector->tuples[i], tupsize);
394  ptr += tupsize;
395 
396  off++;
397  }
398 
399  Assert((ptr - collectordata) <= collector->sumsize);
400  if (needWal)
401  {
403  XLogRegisterBufData(1, collectordata, collector->sumsize);
404  }
405 
406  metadata->tailFreeSize = PageGetExactFreeSpace(page);
407 
408  MarkBufferDirty(buffer);
409  }
410 
411  /*
412  * Set pd_lower just past the end of the metadata. This is essential,
413  * because without doing so, metadata will be lost if xlog.c compresses
414  * the page. (We must do this here because pre-v11 versions of PG did not
415  * set the metapage's pd_lower correctly, so a pg_upgraded index might
416  * contain the wrong value.)
417  */
418  ((PageHeader) metapage)->pd_lower =
419  ((char *) metadata + sizeof(GinMetaPageData)) - (char *) metapage;
420 
421  /*
422  * Write metabuffer, make xlog entry
423  */
424  MarkBufferDirty(metabuffer);
425 
426  if (needWal)
427  {
428  XLogRecPtr recptr;
429 
430  memcpy(&data.metadata, metadata, sizeof(GinMetaPageData));
431 
433  XLogRegisterData((char *) &data, sizeof(ginxlogUpdateMeta));
434 
435  recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_UPDATE_META_PAGE);
436  PageSetLSN(metapage, recptr);
437 
438  if (buffer != InvalidBuffer)
439  {
440  PageSetLSN(page, recptr);
441  }
442  }
443 
444  if (buffer != InvalidBuffer)
445  UnlockReleaseBuffer(buffer);
446 
447  /*
448  * Force pending list cleanup when it becomes too long. And,
449  * ginInsertCleanup could take significant amount of time, so we prefer to
450  * call it when it can do all the work in a single collection cycle. In
451  * non-vacuum mode, it shouldn't require maintenance_work_mem, so fire it
452  * while pending list is still small enough to fit into
453  * gin_pending_list_limit.
454  *
455  * ginInsertCleanup() should not be called inside our CRIT_SECTION.
456  */
457  cleanupSize = GinGetPendingListCleanupSize(index);
458  if (metadata->nPendingPages * GIN_PAGE_FREESIZE > cleanupSize * 1024L)
459  needCleanup = true;
460 
461  UnlockReleaseBuffer(metabuffer);
462 
464 
465  /*
466  * Since it could contend with concurrent cleanup process we cleanup
467  * pending list not forcibly.
468  */
469  if (needCleanup)
470  ginInsertCleanup(ginstate, false, true, false, NULL);
471 }
#define InvalidBlockNumber
Definition: block.h:33
int Buffer
Definition: buf.h:23
#define InvalidBuffer
Definition: buf.h:25
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4497
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:2111
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4715
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:708
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:350
Size PageGetExactFreeSpace(Page page)
Definition: bufpage.c:958
PageHeaderData * PageHeader
Definition: bufpage.h:170
static bool PageIsEmpty(Page page)
Definition: bufpage.h:220
Pointer Page
Definition: bufpage.h:78
static void PageSetLSN(Page page, XLogRecPtr lsn)
Definition: bufpage.h:388
static OffsetNumber PageGetMaxOffsetNumber(Page page)
Definition: bufpage.h:369
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:468
#define GinGetPendingListCleanupSize(relation)
Definition: gin_private.h:38
#define GIN_UNLOCK
Definition: gin_private.h:48
#define GIN_EXCLUSIVE
Definition: gin_private.h:50
#define GinListPageSize
Definition: ginblock.h:327
#define GIN_METAPAGE_BLKNO
Definition: ginblock.h:51
#define GinPageGetOpaque(page)
Definition: ginblock.h:110
#define GinPageGetMeta(p)
Definition: ginblock.h:104
#define GIN_PAGE_FREESIZE
Definition: ginfast.c:41
static void makeSublist(Relation index, IndexTuple *tuples, int32 ntuples, GinMetaPageData *res)
Definition: ginfast.c:145
#define XLOG_GIN_UPDATE_META_PAGE
Definition: ginxlog.h:162
Pointer Item
Definition: item.h:17
Assert(fmt[strlen(fmt) - 1] !='\n')
void * palloc(Size size)
Definition: mcxt.c:1226
#define START_CRIT_SECTION()
Definition: miscadmin.h:148
#define END_CRIT_SECTION()
Definition: miscadmin.h:150
#define InvalidOffsetNumber
Definition: off.h:26
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
uint16 OffsetNumber
Definition: off.h:24
#define FirstOffsetNumber
Definition: off.h:27
const void * data
void CheckForSerializableConflictIn(Relation relation, ItemPointer tid, BlockNumber blkno)
Definition: predicate.c:4270
#define RelationNeedsWAL(relation)
Definition: rel.h:629
BlockNumber tail
Definition: ginblock.h:62
uint32 tailFreeSize
Definition: ginblock.h:67
BlockNumber nPendingPages
Definition: ginblock.h:73
int64 nPendingHeapTuples
Definition: ginblock.h:74
BlockNumber head
Definition: ginblock.h:61
Relation index
Definition: gin_private.h:58
Definition: type.h:95
uint64 XLogRecPtr
Definition: xlogdefs.h:21
void XLogRegisterData(char *data, uint32 len)
Definition: xloginsert.c:351
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:461
void XLogRegisterBufData(uint8 block_id, char *data, uint32 len)
Definition: xloginsert.c:392
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:243
void XLogBeginInsert(void)
Definition: xloginsert.c:150
#define REGBUF_STANDARD
Definition: xloginsert.h:34
#define REGBUF_WILL_INIT
Definition: xloginsert.h:33

References Assert(), BufferGetPage(), CheckForSerializableConflictIn(), data, elog(), END_CRIT_SECTION, ERROR, FirstOffsetNumber, GIN_EXCLUSIVE, GIN_METAPAGE_BLKNO, GIN_PAGE_FREESIZE, GIN_UNLOCK, GinGetPendingListCleanupSize, ginInsertCleanup(), GinListPageSize, GinPageGetMeta, GinPageGetOpaque, GinMetaPageData::head, i, GinState::index, IndexTupleSize, InvalidBlockNumber, InvalidBuffer, InvalidOffsetNumber, LockBuffer(), makeSublist(), MarkBufferDirty(), GinMetaPageData::nPendingHeapTuples, GinMetaPageData::nPendingPages, GinTupleCollector::ntuples, OffsetNumberNext, PageAddItem, PageGetExactFreeSpace(), PageGetMaxOffsetNumber(), PageIsEmpty(), PageSetLSN(), palloc(), ReadBuffer(), REGBUF_STANDARD, REGBUF_WILL_INIT, RelationGetRelationName, RelationNeedsWAL, START_CRIT_SECTION, GinTupleCollector::sumsize, GinMetaPageData::tail, GinMetaPageData::tailFreeSize, GinTupleCollector::tuples, UnlockReleaseBuffer(), XLOG_GIN_UPDATE_META_PAGE, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by gininsert().

◆ ginInsertCleanup()

void ginInsertCleanup ( GinState ginstate,
bool  full_clean,
bool  fill_fsm,
bool  forceCleanup,
IndexBulkDeleteResult stats 
)

Definition at line 779 of file ginfast.c.

782 {
783  Relation index = ginstate->index;
784  Buffer metabuffer,
785  buffer;
786  Page metapage,
787  page;
788  GinMetaPageData *metadata;
790  oldCtx;
791  BuildAccumulator accum;
792  KeyArray datums;
793  BlockNumber blkno,
794  blknoFinish;
795  bool cleanupFinish = false;
796  bool fsm_vac = false;
797  Size workMemory;
798 
799  /*
800  * We would like to prevent concurrent cleanup process. For that we will
801  * lock metapage in exclusive mode using LockPage() call. Nobody other
802  * will use that lock for metapage, so we keep possibility of concurrent
803  * insertion into pending list
804  */
805 
806  if (forceCleanup)
807  {
808  /*
809  * We are called from [auto]vacuum/analyze or gin_clean_pending_list()
810  * and we would like to wait concurrent cleanup to finish.
811  */
813  workMemory =
816  }
817  else
818  {
819  /*
820  * We are called from regular insert and if we see concurrent cleanup
821  * just exit in hope that concurrent process will clean up pending
822  * list.
823  */
825  return;
826  workMemory = work_mem;
827  }
828 
829  metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
830  LockBuffer(metabuffer, GIN_SHARE);
831  metapage = BufferGetPage(metabuffer);
832  metadata = GinPageGetMeta(metapage);
833 
834  if (metadata->head == InvalidBlockNumber)
835  {
836  /* Nothing to do */
837  UnlockReleaseBuffer(metabuffer);
839  return;
840  }
841 
842  /*
843  * Remember a tail page to prevent infinite cleanup if other backends add
844  * new tuples faster than we can cleanup.
845  */
846  blknoFinish = metadata->tail;
847 
848  /*
849  * Read and lock head of pending list
850  */
851  blkno = metadata->head;
852  buffer = ReadBuffer(index, blkno);
853  LockBuffer(buffer, GIN_SHARE);
854  page = BufferGetPage(buffer);
855 
856  LockBuffer(metabuffer, GIN_UNLOCK);
857 
858  /*
859  * Initialize. All temporary space will be in opCtx
860  */
862  "GIN insert cleanup temporary context",
864 
865  oldCtx = MemoryContextSwitchTo(opCtx);
866 
867  initKeyArray(&datums, 128);
868  ginInitBA(&accum);
869  accum.ginstate = ginstate;
870 
871  /*
872  * At the top of this loop, we have pin and lock on the current page of
873  * the pending list. However, we'll release that before exiting the loop.
874  * Note we also have pin but not lock on the metapage.
875  */
876  for (;;)
877  {
878  Assert(!GinPageIsDeleted(page));
879 
880  /*
881  * Are we walk through the page which as we remember was a tail when
882  * we start our cleanup? But if caller asks us to clean up whole
883  * pending list then ignore old tail, we will work until list becomes
884  * empty.
885  */
886  if (blkno == blknoFinish && full_clean == false)
887  cleanupFinish = true;
888 
889  /*
890  * read page's datums into accum
891  */
892  processPendingPage(&accum, &datums, page, FirstOffsetNumber);
893 
895 
896  /*
897  * Is it time to flush memory to disk? Flush if we are at the end of
898  * the pending list, or if we have a full row and memory is getting
899  * full.
900  */
901  if (GinPageGetOpaque(page)->rightlink == InvalidBlockNumber ||
902  (GinPageHasFullRow(page) &&
903  (accum.allocatedMemory >= workMemory * 1024L)))
904  {
906  uint32 nlist;
907  Datum key;
908  GinNullCategory category;
909  OffsetNumber maxoff,
910  attnum;
911 
912  /*
913  * Unlock current page to increase performance. Changes of page
914  * will be checked later by comparing maxoff after completion of
915  * memory flush.
916  */
917  maxoff = PageGetMaxOffsetNumber(page);
918  LockBuffer(buffer, GIN_UNLOCK);
919 
920  /*
921  * Moving collected data into regular structure can take
922  * significant amount of time - so, run it without locking pending
923  * list.
924  */
925  ginBeginBAScan(&accum);
926  while ((list = ginGetBAEntry(&accum,
927  &attnum, &key, &category, &nlist)) != NULL)
928  {
929  ginEntryInsert(ginstate, attnum, key, category,
930  list, nlist, NULL);
932  }
933 
934  /*
935  * Lock the whole list to remove pages
936  */
937  LockBuffer(metabuffer, GIN_EXCLUSIVE);
938  LockBuffer(buffer, GIN_SHARE);
939 
940  Assert(!GinPageIsDeleted(page));
941 
942  /*
943  * While we left the page unlocked, more stuff might have gotten
944  * added to it. If so, process those entries immediately. There
945  * shouldn't be very many, so we don't worry about the fact that
946  * we're doing this with exclusive lock. Insertion algorithm
947  * guarantees that inserted row(s) will not continue on next page.
948  * NOTE: intentionally no vacuum_delay_point in this loop.
949  */
950  if (PageGetMaxOffsetNumber(page) != maxoff)
951  {
952  ginInitBA(&accum);
953  processPendingPage(&accum, &datums, page, maxoff + 1);
954 
955  ginBeginBAScan(&accum);
956  while ((list = ginGetBAEntry(&accum,
957  &attnum, &key, &category, &nlist)) != NULL)
958  ginEntryInsert(ginstate, attnum, key, category,
959  list, nlist, NULL);
960  }
961 
962  /*
963  * Remember next page - it will become the new list head
964  */
965  blkno = GinPageGetOpaque(page)->rightlink;
966  UnlockReleaseBuffer(buffer); /* shiftList will do exclusive
967  * locking */
968 
969  /*
970  * remove read pages from pending list, at this point all content
971  * of read pages is in regular structure
972  */
973  shiftList(index, metabuffer, blkno, fill_fsm, stats);
974 
975  /* At this point, some pending pages have been freed up */
976  fsm_vac = true;
977 
978  Assert(blkno == metadata->head);
979  LockBuffer(metabuffer, GIN_UNLOCK);
980 
981  /*
982  * if we removed the whole pending list or we cleanup tail (which
983  * we remembered on start our cleanup process) then just exit
984  */
985  if (blkno == InvalidBlockNumber || cleanupFinish)
986  break;
987 
988  /*
989  * release memory used so far and reinit state
990  */
992  initKeyArray(&datums, datums.maxvalues);
993  ginInitBA(&accum);
994  }
995  else
996  {
997  blkno = GinPageGetOpaque(page)->rightlink;
998  UnlockReleaseBuffer(buffer);
999  }
1000 
1001  /*
1002  * Read next page in pending list
1003  */
1005  buffer = ReadBuffer(index, blkno);
1006  LockBuffer(buffer, GIN_SHARE);
1007  page = BufferGetPage(buffer);
1008  }
1009 
1011  ReleaseBuffer(metabuffer);
1012 
1013  /*
1014  * As pending list pages can have a high churn rate, it is desirable to
1015  * recycle them immediately to the FreeSpaceMap when ordinary backends
1016  * clean the list.
1017  */
1018  if (fsm_vac && fill_fsm)
1020 
1021  /* Clean up temporary space */
1022  MemoryContextSwitchTo(oldCtx);
1024 }
int autovacuum_work_mem
Definition: autovacuum.c:118
bool IsAutoVacuumWorkerProcess(void)
Definition: autovacuum.c:3397
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4480
unsigned int uint32
Definition: c.h:495
size_t Size
Definition: c.h:594
#define GIN_SHARE
Definition: gin_private.h:49
#define GinPageHasFullRow(page)
Definition: ginblock.h:119
#define GinPageIsDeleted(page)
Definition: ginblock.h:124
void ginBeginBAScan(BuildAccumulator *accum)
Definition: ginbulk.c:257
void ginInitBA(BuildAccumulator *accum)
Definition: ginbulk.c:109
ItemPointerData * ginGetBAEntry(BuildAccumulator *accum, OffsetNumber *attnum, Datum *key, GinNullCategory *category, uint32 *n)
Definition: ginbulk.c:268
static void processPendingPage(BuildAccumulator *accum, KeyArray *ka, Page page, OffsetNumber startoff)
Definition: ginfast.c:708
static void initKeyArray(KeyArray *keys, int32 maxvalues)
Definition: ginfast.c:674
static void shiftList(Relation index, Buffer metabuffer, BlockNumber newHead, bool fill_fsm, IndexBulkDeleteResult *stats)
Definition: ginfast.c:553
void ginEntryInsert(GinState *ginstate, OffsetNumber attnum, Datum key, GinNullCategory category, ItemPointerData *items, uint32 nitem, GinStatsData *buildStats)
Definition: gininsert.c:179
static MemoryContext opCtx
Definition: ginxlog.c:22
int maintenance_work_mem
Definition: globals.c:127
int work_mem
Definition: globals.c:125
void IndexFreeSpaceMapVacuum(Relation rel)
Definition: indexfsm.c:71
bool ConditionalLockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode)
Definition: lmgr.c:533
void LockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode)
Definition: lmgr.c:514
void UnlockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode)
Definition: lmgr.c:549
#define ExclusiveLock
Definition: lockdefs.h:42
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:330
MemoryContext CurrentMemoryContext
Definition: mcxt.c:135
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:403
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:153
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:138
GinState * ginstate
Definition: gin_private.h:432
void vacuum_delay_point(void)
Definition: vacuum.c:2322

References BuildAccumulator::allocatedMemory, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert(), attnum, autovacuum_work_mem, BufferGetPage(), ConditionalLockPage(), CurrentMemoryContext, ExclusiveLock, FirstOffsetNumber, GIN_EXCLUSIVE, GIN_METAPAGE_BLKNO, GIN_SHARE, GIN_UNLOCK, ginBeginBAScan(), ginEntryInsert(), ginGetBAEntry(), ginInitBA(), GinPageGetMeta, GinPageGetOpaque, GinPageHasFullRow, GinPageIsDeleted, BuildAccumulator::ginstate, GinMetaPageData::head, GinState::index, IndexFreeSpaceMapVacuum(), initKeyArray(), InvalidBlockNumber, IsAutoVacuumWorkerProcess(), sort-test::key, sort-test::list, LockBuffer(), LockPage(), maintenance_work_mem, KeyArray::maxvalues, MemoryContextDelete(), MemoryContextReset(), MemoryContextSwitchTo(), opCtx, PageGetMaxOffsetNumber(), processPendingPage(), ReadBuffer(), ReleaseBuffer(), shiftList(), GinMetaPageData::tail, UnlockPage(), UnlockReleaseBuffer(), vacuum_delay_point(), and work_mem.

Referenced by gin_clean_pending_list(), ginbulkdelete(), ginHeapTupleFastInsert(), and ginvacuumcleanup().

◆ initKeyArray()

static void initKeyArray ( KeyArray keys,
int32  maxvalues 
)
static

Definition at line 674 of file ginfast.c.

675 {
676  keys->keys = palloc_array(Datum, maxvalues);
677  keys->categories = palloc_array(GinNullCategory, maxvalues);
678  keys->nvalues = 0;
679  keys->maxvalues = maxvalues;
680 }

References KeyArray::categories, KeyArray::keys, KeyArray::maxvalues, KeyArray::nvalues, and palloc_array.

Referenced by ginInsertCleanup().

◆ makeSublist()

static void makeSublist ( Relation  index,
IndexTuple tuples,
int32  ntuples,
GinMetaPageData res 
)
static

Definition at line 145 of file ginfast.c.

147 {
148  Buffer curBuffer = InvalidBuffer;
149  Buffer prevBuffer = InvalidBuffer;
150  int i,
151  size = 0,
152  tupsize;
153  int startTuple = 0;
154 
155  Assert(ntuples > 0);
156 
157  /*
158  * Split tuples into pages
159  */
160  for (i = 0; i < ntuples; i++)
161  {
162  if (curBuffer == InvalidBuffer)
163  {
164  curBuffer = GinNewBuffer(index);
165 
166  if (prevBuffer != InvalidBuffer)
167  {
168  res->nPendingPages++;
169  writeListPage(index, prevBuffer,
170  tuples + startTuple,
171  i - startTuple,
172  BufferGetBlockNumber(curBuffer));
173  }
174  else
175  {
176  res->head = BufferGetBlockNumber(curBuffer);
177  }
178 
179  prevBuffer = curBuffer;
180  startTuple = i;
181  size = 0;
182  }
183 
184  tupsize = MAXALIGN(IndexTupleSize(tuples[i])) + sizeof(ItemIdData);
185 
186  if (size + tupsize > GinListPageSize)
187  {
188  /* won't fit, force a new page and reprocess */
189  i--;
190  curBuffer = InvalidBuffer;
191  }
192  else
193  {
194  size += tupsize;
195  }
196  }
197 
198  /*
199  * Write last page
200  */
201  res->tail = BufferGetBlockNumber(curBuffer);
202  res->tailFreeSize = writeListPage(index, curBuffer,
203  tuples + startTuple,
204  ntuples - startTuple,
206  res->nPendingPages++;
207  /* that was only one heap tuple */
208  res->nPendingHeapTuples = 1;
209 }
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:3290
#define MAXALIGN(LEN)
Definition: c.h:800
static int32 writeListPage(Relation index, Buffer buffer, IndexTuple *tuples, int32 ntuples, BlockNumber rightlink)
Definition: ginfast.c:59
Buffer GinNewBuffer(Relation index)
Definition: ginutil.c:299
struct ItemIdData ItemIdData

References Assert(), BufferGetBlockNumber(), GinListPageSize, GinNewBuffer(), i, IndexTupleSize, InvalidBlockNumber, InvalidBuffer, MAXALIGN, res, and writeListPage().

Referenced by ginHeapTupleFastInsert().

◆ processPendingPage()

static void processPendingPage ( BuildAccumulator accum,
KeyArray ka,
Page  page,
OffsetNumber  startoff 
)
static

Definition at line 708 of file ginfast.c.

710 {
711  ItemPointerData heapptr;
712  OffsetNumber i,
713  maxoff;
714  OffsetNumber attrnum;
715 
716  /* reset *ka to empty */
717  ka->nvalues = 0;
718 
719  maxoff = PageGetMaxOffsetNumber(page);
720  Assert(maxoff >= FirstOffsetNumber);
721  ItemPointerSetInvalid(&heapptr);
722  attrnum = 0;
723 
724  for (i = startoff; i <= maxoff; i = OffsetNumberNext(i))
725  {
726  IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
727  OffsetNumber curattnum;
728  Datum curkey;
729  GinNullCategory curcategory;
730 
731  /* Check for change of heap TID or attnum */
732  curattnum = gintuple_get_attrnum(accum->ginstate, itup);
733 
734  if (!ItemPointerIsValid(&heapptr))
735  {
736  heapptr = itup->t_tid;
737  attrnum = curattnum;
738  }
739  else if (!(ItemPointerEquals(&heapptr, &itup->t_tid) &&
740  curattnum == attrnum))
741  {
742  /*
743  * ginInsertBAEntries can insert several datums per call, but only
744  * for one heap tuple and one column. So call it at a boundary,
745  * and reset ka.
746  */
747  ginInsertBAEntries(accum, &heapptr, attrnum,
748  ka->keys, ka->categories, ka->nvalues);
749  ka->nvalues = 0;
750  heapptr = itup->t_tid;
751  attrnum = curattnum;
752  }
753 
754  /* Add key to KeyArray */
755  curkey = gintuple_get_key(accum->ginstate, itup, &curcategory);
756  addDatum(ka, curkey, curcategory);
757  }
758 
759  /* Dump out all remaining keys */
760  ginInsertBAEntries(accum, &heapptr, attrnum,
761  ka->keys, ka->categories, ka->nvalues);
762 }
static Item PageGetItem(Page page, ItemId itemId)
Definition: bufpage.h:351
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:240
void ginInsertBAEntries(BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber attnum, Datum *entries, GinNullCategory *categories, int32 nentries)
Definition: ginbulk.c:210
static void addDatum(KeyArray *keys, Datum datum, GinNullCategory category)
Definition: ginfast.c:684
OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple)
Definition: ginutil.c:225
Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple, GinNullCategory *category)
Definition: ginutil.c:258
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:35
static void ItemPointerSetInvalid(ItemPointerData *pointer)
Definition: itemptr.h:184
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition: itemptr.h:83
IndexTupleData * IndexTuple
Definition: itup.h:53

References addDatum(), Assert(), KeyArray::categories, FirstOffsetNumber, ginInsertBAEntries(), BuildAccumulator::ginstate, gintuple_get_attrnum(), gintuple_get_key(), i, ItemPointerEquals(), ItemPointerIsValid(), ItemPointerSetInvalid(), KeyArray::keys, KeyArray::nvalues, OffsetNumberNext, PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), and IndexTupleData::t_tid.

Referenced by ginInsertCleanup().

◆ shiftList()

static void shiftList ( Relation  index,
Buffer  metabuffer,
BlockNumber  newHead,
bool  fill_fsm,
IndexBulkDeleteResult stats 
)
static

Definition at line 553 of file ginfast.c.

555 {
556  Page metapage;
557  GinMetaPageData *metadata;
558  BlockNumber blknoToDelete;
559 
560  metapage = BufferGetPage(metabuffer);
561  metadata = GinPageGetMeta(metapage);
562  blknoToDelete = metadata->head;
563 
564  do
565  {
566  Page page;
567  int i;
568  int64 nDeletedHeapTuples = 0;
570  Buffer buffers[GIN_NDELETE_AT_ONCE];
571  BlockNumber freespace[GIN_NDELETE_AT_ONCE];
572 
573  data.ndeleted = 0;
574  while (data.ndeleted < GIN_NDELETE_AT_ONCE && blknoToDelete != newHead)
575  {
576  freespace[data.ndeleted] = blknoToDelete;
577  buffers[data.ndeleted] = ReadBuffer(index, blknoToDelete);
578  LockBuffer(buffers[data.ndeleted], GIN_EXCLUSIVE);
579  page = BufferGetPage(buffers[data.ndeleted]);
580 
581  data.ndeleted++;
582 
583  Assert(!GinPageIsDeleted(page));
584 
585  nDeletedHeapTuples += GinPageGetOpaque(page)->maxoff;
586  blknoToDelete = GinPageGetOpaque(page)->rightlink;
587  }
588 
589  if (stats)
590  stats->pages_deleted += data.ndeleted;
591 
592  /*
593  * This operation touches an unusually large number of pages, so
594  * prepare the XLogInsert machinery for that before entering the
595  * critical section.
596  */
597  if (RelationNeedsWAL(index))
598  XLogEnsureRecordSpace(data.ndeleted, 0);
599 
601 
602  metadata->head = blknoToDelete;
603 
604  Assert(metadata->nPendingPages >= data.ndeleted);
605  metadata->nPendingPages -= data.ndeleted;
606  Assert(metadata->nPendingHeapTuples >= nDeletedHeapTuples);
607  metadata->nPendingHeapTuples -= nDeletedHeapTuples;
608 
609  if (blknoToDelete == InvalidBlockNumber)
610  {
611  metadata->tail = InvalidBlockNumber;
612  metadata->tailFreeSize = 0;
613  metadata->nPendingPages = 0;
614  metadata->nPendingHeapTuples = 0;
615  }
616 
617  /*
618  * Set pd_lower just past the end of the metadata. This is essential,
619  * because without doing so, metadata will be lost if xlog.c
620  * compresses the page. (We must do this here because pre-v11
621  * versions of PG did not set the metapage's pd_lower correctly, so a
622  * pg_upgraded index might contain the wrong value.)
623  */
624  ((PageHeader) metapage)->pd_lower =
625  ((char *) metadata + sizeof(GinMetaPageData)) - (char *) metapage;
626 
627  MarkBufferDirty(metabuffer);
628 
629  for (i = 0; i < data.ndeleted; i++)
630  {
631  page = BufferGetPage(buffers[i]);
632  GinPageGetOpaque(page)->flags = GIN_DELETED;
633  MarkBufferDirty(buffers[i]);
634  }
635 
636  if (RelationNeedsWAL(index))
637  {
638  XLogRecPtr recptr;
639 
640  XLogBeginInsert();
641  XLogRegisterBuffer(0, metabuffer,
643  for (i = 0; i < data.ndeleted; i++)
644  XLogRegisterBuffer(i + 1, buffers[i], REGBUF_WILL_INIT);
645 
646  memcpy(&data.metadata, metadata, sizeof(GinMetaPageData));
647 
648  XLogRegisterData((char *) &data,
649  sizeof(ginxlogDeleteListPages));
650 
651  recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_DELETE_LISTPAGE);
652  PageSetLSN(metapage, recptr);
653 
654  for (i = 0; i < data.ndeleted; i++)
655  {
656  page = BufferGetPage(buffers[i]);
657  PageSetLSN(page, recptr);
658  }
659  }
660 
661  for (i = 0; i < data.ndeleted; i++)
662  UnlockReleaseBuffer(buffers[i]);
663 
665 
666  for (i = 0; fill_fsm && i < data.ndeleted; i++)
667  RecordFreeIndexPage(index, freespace[i]);
668 
669  } while (blknoToDelete != newHead);
670 }
#define GIN_DELETED
Definition: ginblock.h:43
#define GIN_NDELETE_AT_ONCE
Definition: ginxlog.h:202
#define XLOG_GIN_DELETE_LISTPAGE
Definition: ginxlog.h:194
void RecordFreeIndexPage(Relation rel, BlockNumber freeBlock)
Definition: indexfsm.c:52
void XLogEnsureRecordSpace(int max_block_id, int ndatas)
Definition: xloginsert.c:176

References Assert(), BufferGetPage(), data, END_CRIT_SECTION, GIN_DELETED, GIN_EXCLUSIVE, GIN_NDELETE_AT_ONCE, GinPageGetMeta, GinPageGetOpaque, GinPageIsDeleted, GinMetaPageData::head, i, InvalidBlockNumber, LockBuffer(), MarkBufferDirty(), GinMetaPageData::nPendingHeapTuples, GinMetaPageData::nPendingPages, IndexBulkDeleteResult::pages_deleted, PageSetLSN(), ReadBuffer(), RecordFreeIndexPage(), REGBUF_STANDARD, REGBUF_WILL_INIT, RelationNeedsWAL, START_CRIT_SECTION, GinMetaPageData::tail, GinMetaPageData::tailFreeSize, UnlockReleaseBuffer(), XLOG_GIN_DELETE_LISTPAGE, XLogBeginInsert(), XLogEnsureRecordSpace(), XLogInsert(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by ginInsertCleanup().

◆ writeListPage()

static int32 writeListPage ( Relation  index,
Buffer  buffer,
IndexTuple tuples,
int32  ntuples,
BlockNumber  rightlink 
)
static

Definition at line 59 of file ginfast.c.

61 {
62  Page page = BufferGetPage(buffer);
63  int32 i,
64  freesize,
65  size = 0;
66  OffsetNumber l,
67  off;
68  PGAlignedBlock workspace;
69  char *ptr;
70 
72 
73  GinInitBuffer(buffer, GIN_LIST);
74 
75  off = FirstOffsetNumber;
76  ptr = workspace.data;
77 
78  for (i = 0; i < ntuples; i++)
79  {
80  int this_size = IndexTupleSize(tuples[i]);
81 
82  memcpy(ptr, tuples[i], this_size);
83  ptr += this_size;
84  size += this_size;
85 
86  l = PageAddItem(page, (Item) tuples[i], this_size, off, false, false);
87 
88  if (l == InvalidOffsetNumber)
89  elog(ERROR, "failed to add item to index page in \"%s\"",
91 
92  off++;
93  }
94 
95  Assert(size <= BLCKSZ); /* else we overran workspace */
96 
97  GinPageGetOpaque(page)->rightlink = rightlink;
98 
99  /*
100  * tail page may contain only whole row(s) or final part of row placed on
101  * previous pages (a "row" here meaning all the index tuples generated for
102  * one heap tuple)
103  */
104  if (rightlink == InvalidBlockNumber)
105  {
106  GinPageSetFullRow(page);
107  GinPageGetOpaque(page)->maxoff = 1;
108  }
109  else
110  {
111  GinPageGetOpaque(page)->maxoff = 0;
112  }
113 
114  MarkBufferDirty(buffer);
115 
116  if (RelationNeedsWAL(index))
117  {
119  XLogRecPtr recptr;
120 
121  data.rightlink = rightlink;
122  data.ntuples = ntuples;
123 
124  XLogBeginInsert();
125  XLogRegisterData((char *) &data, sizeof(ginxlogInsertListPage));
126 
128  XLogRegisterBufData(0, workspace.data, size);
129 
130  recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_INSERT_LISTPAGE);
131  PageSetLSN(page, recptr);
132  }
133 
134  /* get free space before releasing buffer */
135  freesize = PageGetExactFreeSpace(page);
136 
137  UnlockReleaseBuffer(buffer);
138 
140 
141  return freesize;
142 }
#define GIN_LIST
Definition: ginblock.h:45
#define GinPageSetFullRow(page)
Definition: ginblock.h:120
void GinInitBuffer(Buffer b, uint32 f)
Definition: ginutil.c:349
#define XLOG_GIN_INSERT_LISTPAGE
Definition: ginxlog.h:180
char data[BLCKSZ]
Definition: c.h:1132

References Assert(), BufferGetPage(), PGAlignedBlock::data, data, elog(), END_CRIT_SECTION, ERROR, FirstOffsetNumber, GIN_LIST, GinInitBuffer(), GinPageGetOpaque, GinPageSetFullRow, i, IndexTupleSize, InvalidBlockNumber, InvalidOffsetNumber, MarkBufferDirty(), PageAddItem, PageGetExactFreeSpace(), PageSetLSN(), REGBUF_WILL_INIT, RelationGetRelationName, RelationNeedsWAL, START_CRIT_SECTION, UnlockReleaseBuffer(), XLOG_GIN_INSERT_LISTPAGE, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by makeSublist().

Variable Documentation

◆ gin_pending_list_limit

int gin_pending_list_limit = 0

Definition at line 39 of file ginfast.c.