PostgreSQL Source Code  git master
ginfast.c File Reference
#include "postgres.h"
#include "access/gin_private.h"
#include "access/ginxlog.h"
#include "access/xloginsert.h"
#include "access/xlog.h"
#include "commands/vacuum.h"
#include "catalog/pg_am.h"
#include "miscadmin.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/acl.h"
#include "postmaster/autovacuum.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
#include "utils/builtins.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 40 of file ginfast.c.

Referenced by ginHeapTupleFastInsert().

Typedef Documentation

◆ KeyArray

Function Documentation

◆ addDatum()

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

Definition at line 665 of file ginfast.c.

References KeyArray::categories, KeyArray::keys, KeyArray::maxvalues, KeyArray::nvalues, and repalloc().

Referenced by processPendingPage().

666 {
667  if (keys->nvalues >= keys->maxvalues)
668  {
669  keys->maxvalues *= 2;
670  keys->keys = (Datum *)
671  repalloc(keys->keys, sizeof(Datum) * keys->maxvalues);
672  keys->categories = (GinNullCategory *)
673  repalloc(keys->categories, sizeof(GinNullCategory) * keys->maxvalues);
674  }
675 
676  keys->keys[keys->nvalues] = datum;
677  keys->categories[keys->nvalues] = category;
678  keys->nvalues++;
679 }
signed char GinNullCategory
Definition: ginblock.h:200
int32 nvalues
Definition: ginfast.c:47
int32 maxvalues
Definition: ginfast.c:48
Datum * keys
Definition: ginfast.c:45
uintptr_t Datum
Definition: postgres.h:367
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1044
GinNullCategory * categories
Definition: ginfast.c:46

◆ gin_clean_pending_list()

Datum gin_clean_pending_list ( PG_FUNCTION_ARGS  )

Definition at line 1013 of file ginfast.c.

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

1014 {
1015  Oid indexoid = PG_GETARG_OID(0);
1016  Relation indexRel = index_open(indexoid, AccessShareLock);
1017  IndexBulkDeleteResult stats;
1018  GinState ginstate;
1019 
1020  if (RecoveryInProgress())
1021  ereport(ERROR,
1022  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1023  errmsg("recovery is in progress"),
1024  errhint("GIN pending list cannot be cleaned up during recovery.")));
1025 
1026  /* Must be a GIN index */
1027  if (indexRel->rd_rel->relkind != RELKIND_INDEX ||
1028  indexRel->rd_rel->relam != GIN_AM_OID)
1029  ereport(ERROR,
1030  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1031  errmsg("\"%s\" is not a GIN index",
1032  RelationGetRelationName(indexRel))));
1033 
1034  /*
1035  * Reject attempts to read non-local temporary relations; we would be
1036  * likely to get wrong data since we have no visibility into the owning
1037  * session's local buffers.
1038  */
1039  if (RELATION_IS_OTHER_TEMP(indexRel))
1040  ereport(ERROR,
1041  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1042  errmsg("cannot access temporary indexes of other sessions")));
1043 
1044  /* User must own the index (comparable to privileges needed for VACUUM) */
1045  if (!pg_class_ownercheck(indexoid, GetUserId()))
1047  RelationGetRelationName(indexRel));
1048 
1049  memset(&stats, 0, sizeof(stats));
1050  initGinState(&ginstate, indexRel);
1051  ginInsertCleanup(&ginstate, true, true, true, &stats);
1052 
1053  index_close(indexRel, AccessShareLock);
1054 
1055  PG_RETURN_INT64((int64) stats.pages_deleted);
1056 }
int errhint(const char *fmt,...)
Definition: elog.c:987
Oid GetUserId(void)
Definition: miscinit.c:379
#define PG_RETURN_INT64(x)
Definition: fmgr.h:332
#define AccessShareLock
Definition: lockdefs.h:36
int errcode(int sqlerrcode)
Definition: elog.c:575
Form_pg_class rd_rel
Definition: rel.h:84
unsigned int Oid
Definition: postgres_ext.h:31
bool RecoveryInProgress(void)
Definition: xlog.c:7949
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:3349
#define ERROR
Definition: elog.h:43
#define PG_GETARG_OID(n)
Definition: fmgr.h:245
void initGinState(GinState *state, Relation index)
Definition: ginutil.c:88
#define RelationGetRelationName(relation)
Definition: rel.h:441
BlockNumber pages_deleted
Definition: genam.h:78
#define ereport(elevel, rest)
Definition: elog.h:122
void ginInsertCleanup(GinState *ginstate, bool full_clean, bool fill_fsm, bool forceCleanup, IndexBulkDeleteResult *stats)
Definition: ginfast.c:762
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:538
bool pg_class_ownercheck(Oid class_oid, Oid roleid)
Definition: aclchk.c:4751
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:176
int errmsg(const char *fmt,...)
Definition: elog.c:797
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:150

◆ ginHeapTupleFastCollect()

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

Definition at line 478 of file ginfast.c.

References KeyArray::categories, ginExtractEntries(), GinFormTuple(), i, IndexTupleSize, GinTupleCollector::lentuples, tupleDesc::natts, GinTupleCollector::ntuples, GinState::origTupdesc, palloc(), repalloc(), GinTupleCollector::sumsize, IndexTupleData::t_tid, and GinTupleCollector::tuples.

Referenced by gininsert().

482 {
483  Datum *entries;
484  GinNullCategory *categories;
485  int32 i,
486  nentries;
487 
488  /*
489  * Extract the key values that need to be inserted in the index
490  */
491  entries = ginExtractEntries(ginstate, attnum, value, isNull,
492  &nentries, &categories);
493 
494  /*
495  * Allocate/reallocate memory for storing collected tuples
496  */
497  if (collector->tuples == NULL)
498  {
499  collector->lentuples = nentries * ginstate->origTupdesc->natts;
500  collector->tuples = (IndexTuple *) palloc(sizeof(IndexTuple) * collector->lentuples);
501  }
502 
503  while (collector->ntuples + nentries > collector->lentuples)
504  {
505  collector->lentuples *= 2;
506  collector->tuples = (IndexTuple *) repalloc(collector->tuples,
507  sizeof(IndexTuple) * collector->lentuples);
508  }
509 
510  /*
511  * Build an index tuple for each key value, and add to array. In pending
512  * tuples we just stick the heap TID into t_tid.
513  */
514  for (i = 0; i < nentries; i++)
515  {
516  IndexTuple itup;
517 
518  itup = GinFormTuple(ginstate, attnum, entries[i], categories[i],
519  NULL, 0, 0, true);
520  itup->t_tid = *ht_ctid;
521  collector->tuples[collector->ntuples++] = itup;
522  collector->sumsize += IndexTupleSize(itup);
523  }
524 }
ItemPointerData t_tid
Definition: itup.h:37
int natts
Definition: tupdesc.h:82
signed int int32
Definition: c.h:313
signed char GinNullCategory
Definition: ginblock.h:200
IndexTuple * tuples
Definition: gin_private.h:428
uintptr_t Datum
Definition: postgres.h:367
static struct @131 value
int16 attnum
Definition: pg_attribute.h:79
IndexTuple GinFormTuple(GinState *ginstate, OffsetNumber attnum, Datum key, GinNullCategory category, Pointer data, Size dataSize, int nipd, bool errorTooBig)
Definition: ginentrypage.c:45
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1044
void * palloc(Size size)
Definition: mcxt.c:924
int i
TupleDesc origTupdesc
Definition: gin_private.h:67
Datum * ginExtractEntries(GinState *ginstate, OffsetNumber attnum, Datum value, bool isNull, int32 *nentries, GinNullCategory **categories)
Definition: ginutil.c:488
#define IndexTupleSize(itup)
Definition: itup.h:71

◆ ginHeapTupleFastInsert()

void ginHeapTupleFastInsert ( GinState ginstate,
GinTupleCollector collector 
)

Definition at line 223 of file ginfast.c.

References Assert, buffer, BufferGetPage, CheckForSerializableConflictIn(), 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(), ginxlogUpdateMeta::metadata, ginxlogUpdateMeta::newRightlink, ginxlogUpdateMeta::node, GinMetaPageData::nPendingHeapTuples, GinMetaPageData::nPendingPages, ginxlogUpdateMeta::ntuples, GinTupleCollector::ntuples, OffsetNumberNext, PageAddItem, PageGetExactFreeSpace(), PageGetMaxOffsetNumber, PageIsEmpty, PageSetLSN, palloc(), ginxlogUpdateMeta::prevTail, RelationData::rd_node, 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().

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

◆ ginInsertCleanup()

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

Definition at line 762 of file ginfast.c.

References BuildAccumulator::allocatedMemory, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert, attnum, autovacuum_work_mem, buffer, 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::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().

765 {
766  Relation index = ginstate->index;
767  Buffer metabuffer,
768  buffer;
769  Page metapage,
770  page;
771  GinMetaPageData *metadata;
773  oldCtx;
774  BuildAccumulator accum;
775  KeyArray datums;
776  BlockNumber blkno,
777  blknoFinish;
778  bool cleanupFinish = false;
779  bool fsm_vac = false;
780  Size workMemory;
781 
782  /*
783  * We would like to prevent concurrent cleanup process. For that we will
784  * lock metapage in exclusive mode using LockPage() call. Nobody other
785  * will use that lock for metapage, so we keep possibility of concurrent
786  * insertion into pending list
787  */
788 
789  if (forceCleanup)
790  {
791  /*
792  * We are called from [auto]vacuum/analyze or gin_clean_pending_list()
793  * and we would like to wait concurrent cleanup to finish.
794  */
796  workMemory =
799  }
800  else
801  {
802  /*
803  * We are called from regular insert and if we see concurrent cleanup
804  * just exit in hope that concurrent process will clean up pending
805  * list.
806  */
808  return;
809  workMemory = work_mem;
810  }
811 
812  metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
813  LockBuffer(metabuffer, GIN_SHARE);
814  metapage = BufferGetPage(metabuffer);
815  metadata = GinPageGetMeta(metapage);
816 
817  if (metadata->head == InvalidBlockNumber)
818  {
819  /* Nothing to do */
820  UnlockReleaseBuffer(metabuffer);
822  return;
823  }
824 
825  /*
826  * Remember a tail page to prevent infinite cleanup if other backends add
827  * new tuples faster than we can cleanup.
828  */
829  blknoFinish = metadata->tail;
830 
831  /*
832  * Read and lock head of pending list
833  */
834  blkno = metadata->head;
835  buffer = ReadBuffer(index, blkno);
836  LockBuffer(buffer, GIN_SHARE);
837  page = BufferGetPage(buffer);
838 
839  LockBuffer(metabuffer, GIN_UNLOCK);
840 
841  /*
842  * Initialize. All temporary space will be in opCtx
843  */
845  "GIN insert cleanup temporary context",
847 
848  oldCtx = MemoryContextSwitchTo(opCtx);
849 
850  initKeyArray(&datums, 128);
851  ginInitBA(&accum);
852  accum.ginstate = ginstate;
853 
854  /*
855  * At the top of this loop, we have pin and lock on the current page of
856  * the pending list. However, we'll release that before exiting the loop.
857  * Note we also have pin but not lock on the metapage.
858  */
859  for (;;)
860  {
861  Assert(!GinPageIsDeleted(page));
862 
863  /*
864  * Are we walk through the page which as we remember was a tail when
865  * we start our cleanup? But if caller asks us to clean up whole
866  * pending list then ignore old tail, we will work until list becomes
867  * empty.
868  */
869  if (blkno == blknoFinish && full_clean == false)
870  cleanupFinish = true;
871 
872  /*
873  * read page's datums into accum
874  */
875  processPendingPage(&accum, &datums, page, FirstOffsetNumber);
876 
878 
879  /*
880  * Is it time to flush memory to disk? Flush if we are at the end of
881  * the pending list, or if we have a full row and memory is getting
882  * full.
883  */
884  if (GinPageGetOpaque(page)->rightlink == InvalidBlockNumber ||
885  (GinPageHasFullRow(page) &&
886  (accum.allocatedMemory >= workMemory * 1024L)))
887  {
889  uint32 nlist;
890  Datum key;
891  GinNullCategory category;
892  OffsetNumber maxoff,
893  attnum;
894 
895  /*
896  * Unlock current page to increase performance. Changes of page
897  * will be checked later by comparing maxoff after completion of
898  * memory flush.
899  */
900  maxoff = PageGetMaxOffsetNumber(page);
901  LockBuffer(buffer, GIN_UNLOCK);
902 
903  /*
904  * Moving collected data into regular structure can take
905  * significant amount of time - so, run it without locking pending
906  * list.
907  */
908  ginBeginBAScan(&accum);
909  while ((list = ginGetBAEntry(&accum,
910  &attnum, &key, &category, &nlist)) != NULL)
911  {
912  ginEntryInsert(ginstate, attnum, key, category,
913  list, nlist, NULL);
915  }
916 
917  /*
918  * Lock the whole list to remove pages
919  */
920  LockBuffer(metabuffer, GIN_EXCLUSIVE);
921  LockBuffer(buffer, GIN_SHARE);
922 
923  Assert(!GinPageIsDeleted(page));
924 
925  /*
926  * While we left the page unlocked, more stuff might have gotten
927  * added to it. If so, process those entries immediately. There
928  * shouldn't be very many, so we don't worry about the fact that
929  * we're doing this with exclusive lock. Insertion algorithm
930  * guarantees that inserted row(s) will not continue on next page.
931  * NOTE: intentionally no vacuum_delay_point in this loop.
932  */
933  if (PageGetMaxOffsetNumber(page) != maxoff)
934  {
935  ginInitBA(&accum);
936  processPendingPage(&accum, &datums, page, maxoff + 1);
937 
938  ginBeginBAScan(&accum);
939  while ((list = ginGetBAEntry(&accum,
940  &attnum, &key, &category, &nlist)) != NULL)
941  ginEntryInsert(ginstate, attnum, key, category,
942  list, nlist, NULL);
943  }
944 
945  /*
946  * Remember next page - it will become the new list head
947  */
948  blkno = GinPageGetOpaque(page)->rightlink;
949  UnlockReleaseBuffer(buffer); /* shiftList will do exclusive
950  * locking */
951 
952  /*
953  * remove read pages from pending list, at this point all content
954  * of read pages is in regular structure
955  */
956  shiftList(index, metabuffer, blkno, fill_fsm, stats);
957 
958  /* At this point, some pending pages have been freed up */
959  fsm_vac = true;
960 
961  Assert(blkno == metadata->head);
962  LockBuffer(metabuffer, GIN_UNLOCK);
963 
964  /*
965  * if we removed the whole pending list or we cleanup tail (which
966  * we remembered on start our cleanup process) then just exit
967  */
968  if (blkno == InvalidBlockNumber || cleanupFinish)
969  break;
970 
971  /*
972  * release memory used so far and reinit state
973  */
974  MemoryContextReset(opCtx);
975  initKeyArray(&datums, datums.maxvalues);
976  ginInitBA(&accum);
977  }
978  else
979  {
980  blkno = GinPageGetOpaque(page)->rightlink;
981  UnlockReleaseBuffer(buffer);
982  }
983 
984  /*
985  * Read next page in pending list
986  */
988  buffer = ReadBuffer(index, blkno);
989  LockBuffer(buffer, GIN_SHARE);
990  page = BufferGetPage(buffer);
991  }
992 
994  ReleaseBuffer(metabuffer);
995 
996  /*
997  * As pending list pages can have a high churn rate, it is desirable to
998  * recycle them immediately to the FreeSpace Map when ordinary backends
999  * clean the list.
1000  */
1001  if (fsm_vac && fill_fsm)
1002  IndexFreeSpaceMapVacuum(index);
1003 
1004  /* Clean up temporary space */
1005  MemoryContextSwitchTo(oldCtx);
1006  MemoryContextDelete(opCtx);
1007 }
#define GinPageHasFullRow(page)
Definition: ginblock.h:118
int autovacuum_work_mem
Definition: autovacuum.c:115
#define GIN_UNLOCK
Definition: gin_private.h:43
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
Relation index
Definition: gin_private.h:53
#define ExclusiveLock
Definition: lockdefs.h:44
static MemoryContext opCtx
Definition: ginxlog.c:22
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:136
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3309
#define GinPageGetOpaque(page)
Definition: ginblock.h:109
#define GIN_METAPAGE_BLKNO
Definition: ginblock.h:50
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:353
uint16 OffsetNumber
Definition: off.h:24
static void initKeyArray(KeyArray *keys, int32 maxvalues)
Definition: ginfast.c:654
Definition: type.h:89
void ginEntryInsert(GinState *ginstate, OffsetNumber attnum, Datum key, GinNullCategory category, ItemPointerData *items, uint32 nitem, GinStatsData *buildStats)
Definition: gininsert.c:179
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3332
signed char GinNullCategory
Definition: ginblock.h:200
BlockNumber head
Definition: ginblock.h:60
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:192
void LockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode)
Definition: lmgr.c:400
BlockNumber tail
Definition: ginblock.h:61
#define FirstOffsetNumber
Definition: off.h:27
unsigned int uint32
Definition: c.h:325
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
GinState * ginstate
Definition: gin_private.h:406
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
bool IsAutoVacuumWorkerProcess(void)
Definition: autovacuum.c:3295
#define GIN_SHARE
Definition: gin_private.h:44
int32 maxvalues
Definition: ginfast.c:48
#define AllocSetContextCreate(parent, name, allocparams)
Definition: memutils.h:170
static void processPendingPage(BuildAccumulator *accum, KeyArray *ka, Page page, OffsetNumber startoff)
Definition: ginfast.c:691
#define GinPageIsDeleted(page)
Definition: ginblock.h:123
uintptr_t Datum
Definition: postgres.h:367
#define GIN_EXCLUSIVE
Definition: gin_private.h:45
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3546
int work_mem
Definition: globals.c:120
int16 attnum
Definition: pg_attribute.h:79
int maintenance_work_mem
Definition: globals.c:121
#define Assert(condition)
Definition: c.h:699
static void shiftList(Relation index, Buffer metabuffer, BlockNumber newHead, bool fill_fsm, IndexBulkDeleteResult *stats)
Definition: ginfast.c:533
void IndexFreeSpaceMapVacuum(Relation rel)
Definition: indexfsm.c:71
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:215
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:594
size_t Size
Definition: c.h:433
#define InvalidBlockNumber
Definition: block.h:33
ItemPointerData * ginGetBAEntry(BuildAccumulator *accum, OffsetNumber *attnum, Datum *key, GinNullCategory *category, uint32 *n)
Definition: ginbulk.c:268
void UnlockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode)
Definition: lmgr.c:435
bool ConditionalLockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode)
Definition: lmgr.c:419
void ginBeginBAScan(BuildAccumulator *accum)
Definition: ginbulk.c:257
#define GinPageGetMeta(p)
Definition: ginblock.h:103
void vacuum_delay_point(void)
Definition: vacuum.c:1672
int Buffer
Definition: buf.h:23
Pointer Page
Definition: bufpage.h:74
void ginInitBA(BuildAccumulator *accum)
Definition: ginbulk.c:109

◆ initKeyArray()

static void initKeyArray ( KeyArray keys,
int32  maxvalues 
)
static

Definition at line 654 of file ginfast.c.

References KeyArray::categories, KeyArray::keys, KeyArray::maxvalues, KeyArray::nvalues, and palloc().

Referenced by ginInsertCleanup().

655 {
656  keys->keys = (Datum *) palloc(sizeof(Datum) * maxvalues);
657  keys->categories = (GinNullCategory *)
658  palloc(sizeof(GinNullCategory) * maxvalues);
659  keys->nvalues = 0;
660  keys->maxvalues = maxvalues;
661 }
signed char GinNullCategory
Definition: ginblock.h:200
int32 nvalues
Definition: ginfast.c:47
int32 maxvalues
Definition: ginfast.c:48
Datum * keys
Definition: ginfast.c:45
uintptr_t Datum
Definition: postgres.h:367
void * palloc(Size size)
Definition: mcxt.c:924
GinNullCategory * categories
Definition: ginfast.c:46

◆ makeSublist()

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

Definition at line 149 of file ginfast.c.

References Assert, BufferGetBlockNumber(), GinListPageSize, GinNewBuffer(), GinMetaPageData::head, i, IndexTupleSize, InvalidBlockNumber, InvalidBuffer, MAXALIGN, GinMetaPageData::nPendingHeapTuples, GinMetaPageData::nPendingPages, GinMetaPageData::tail, GinMetaPageData::tailFreeSize, and writeListPage().

Referenced by ginHeapTupleFastInsert().

151 {
152  Buffer curBuffer = InvalidBuffer;
153  Buffer prevBuffer = InvalidBuffer;
154  int i,
155  size = 0,
156  tupsize;
157  int startTuple = 0;
158 
159  Assert(ntuples > 0);
160 
161  /*
162  * Split tuples into pages
163  */
164  for (i = 0; i < ntuples; i++)
165  {
166  if (curBuffer == InvalidBuffer)
167  {
168  curBuffer = GinNewBuffer(index);
169 
170  if (prevBuffer != InvalidBuffer)
171  {
172  res->nPendingPages++;
173  writeListPage(index, prevBuffer,
174  tuples + startTuple,
175  i - startTuple,
176  BufferGetBlockNumber(curBuffer));
177  }
178  else
179  {
180  res->head = BufferGetBlockNumber(curBuffer);
181  }
182 
183  prevBuffer = curBuffer;
184  startTuple = i;
185  size = 0;
186  }
187 
188  tupsize = MAXALIGN(IndexTupleSize(tuples[i])) + sizeof(ItemIdData);
189 
190  if (size + tupsize > GinListPageSize)
191  {
192  /* won't fit, force a new page and reprocess */
193  i--;
194  curBuffer = InvalidBuffer;
195  }
196  else
197  {
198  size += tupsize;
199  }
200  }
201 
202  /*
203  * Write last page
204  */
205  res->tail = BufferGetBlockNumber(curBuffer);
206  res->tailFreeSize = writeListPage(index, curBuffer,
207  tuples + startTuple,
208  ntuples - startTuple,
210  res->nPendingPages++;
211  /* that was only one heap tuple */
212  res->nPendingHeapTuples = 1;
213 }
Buffer GinNewBuffer(Relation index)
Definition: ginutil.c:291
#define GinListPageSize
Definition: ginblock.h:321
#define InvalidBuffer
Definition: buf.h:25
static int32 writeListPage(Relation index, Buffer buffer, IndexTuple *tuples, int32 ntuples, BlockNumber rightlink)
Definition: ginfast.c:58
int64 nPendingHeapTuples
Definition: ginblock.h:73
BlockNumber head
Definition: ginblock.h:60
BlockNumber tail
Definition: ginblock.h:61
struct ItemIdData ItemIdData
uint32 tailFreeSize
Definition: ginblock.h:66
#define Assert(condition)
Definition: c.h:699
#define InvalidBlockNumber
Definition: block.h:33
#define MAXALIGN(LEN)
Definition: c.h:652
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2605
int i
int Buffer
Definition: buf.h:23
#define IndexTupleSize(itup)
Definition: itup.h:71
BlockNumber nPendingPages
Definition: ginblock.h:72

◆ processPendingPage()

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

Definition at line 691 of file ginfast.c.

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().

693 {
694  ItemPointerData heapptr;
695  OffsetNumber i,
696  maxoff;
697  OffsetNumber attrnum;
698 
699  /* reset *ka to empty */
700  ka->nvalues = 0;
701 
702  maxoff = PageGetMaxOffsetNumber(page);
703  Assert(maxoff >= FirstOffsetNumber);
704  ItemPointerSetInvalid(&heapptr);
705  attrnum = 0;
706 
707  for (i = startoff; i <= maxoff; i = OffsetNumberNext(i))
708  {
709  IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
710  OffsetNumber curattnum;
711  Datum curkey;
712  GinNullCategory curcategory;
713 
714  /* Check for change of heap TID or attnum */
715  curattnum = gintuple_get_attrnum(accum->ginstate, itup);
716 
717  if (!ItemPointerIsValid(&heapptr))
718  {
719  heapptr = itup->t_tid;
720  attrnum = curattnum;
721  }
722  else if (!(ItemPointerEquals(&heapptr, &itup->t_tid) &&
723  curattnum == attrnum))
724  {
725  /*
726  * ginInsertBAEntries can insert several datums per call, but only
727  * for one heap tuple and one column. So call it at a boundary,
728  * and reset ka.
729  */
730  ginInsertBAEntries(accum, &heapptr, attrnum,
731  ka->keys, ka->categories, ka->nvalues);
732  ka->nvalues = 0;
733  heapptr = itup->t_tid;
734  attrnum = curattnum;
735  }
736 
737  /* Add key to KeyArray */
738  curkey = gintuple_get_key(accum->ginstate, itup, &curcategory);
739  addDatum(ka, curkey, curcategory);
740  }
741 
742  /* Dump out all remaining keys */
743  ginInsertBAEntries(accum, &heapptr, attrnum,
744  ka->keys, ka->categories, ka->nvalues);
745 }
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:82
ItemPointerData t_tid
Definition: itup.h:37
static void addDatum(KeyArray *keys, Datum datum, GinNullCategory category)
Definition: ginfast.c:665
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:353
uint16 OffsetNumber
Definition: off.h:24
signed char GinNullCategory
Definition: ginblock.h:200
OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple)
Definition: ginutil.c:217
#define FirstOffsetNumber
Definition: off.h:27
IndexTupleData * IndexTuple
Definition: itup.h:53
int32 nvalues
Definition: ginfast.c:47
GinState * ginstate
Definition: gin_private.h:406
Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple, GinNullCategory *category)
Definition: ginutil.c:250
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:231
Datum * keys
Definition: ginfast.c:45
uintptr_t Datum
Definition: postgres.h:367
void ginInsertBAEntries(BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber attnum, Datum *entries, GinNullCategory *categories, int32 nentries)
Definition: ginbulk.c:210
#define Assert(condition)
Definition: c.h:699
#define OffsetNumberNext(offsetNumber)
Definition: off.h:53
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:29
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:172
int i
GinNullCategory * categories
Definition: ginfast.c:46
#define PageGetItem(page, itemId)
Definition: bufpage.h:336

◆ shiftList()

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

Definition at line 533 of file ginfast.c.

References Assert, BufferGetPage, END_CRIT_SECTION, GIN_DELETED, GIN_EXCLUSIVE, GIN_NDELETE_AT_ONCE, GinPageGetMeta, GinPageGetOpaque, GinPageIsDeleted, GinMetaPageData::head, i, InvalidBlockNumber, LockBuffer(), MarkBufferDirty(), ginxlogDeleteListPages::metadata, ginxlogDeleteListPages::ndeleted, 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().

535 {
536  Page metapage;
537  GinMetaPageData *metadata;
538  BlockNumber blknoToDelete;
539 
540  metapage = BufferGetPage(metabuffer);
541  metadata = GinPageGetMeta(metapage);
542  blknoToDelete = metadata->head;
543 
544  do
545  {
546  Page page;
547  int i;
548  int64 nDeletedHeapTuples = 0;
550  Buffer buffers[GIN_NDELETE_AT_ONCE];
551  BlockNumber freespace[GIN_NDELETE_AT_ONCE];
552 
553  data.ndeleted = 0;
554  while (data.ndeleted < GIN_NDELETE_AT_ONCE && blknoToDelete != newHead)
555  {
556  freespace[data.ndeleted] = blknoToDelete;
557  buffers[data.ndeleted] = ReadBuffer(index, blknoToDelete);
558  LockBuffer(buffers[data.ndeleted], GIN_EXCLUSIVE);
559  page = BufferGetPage(buffers[data.ndeleted]);
560 
561  data.ndeleted++;
562 
563  Assert(!GinPageIsDeleted(page));
564 
565  nDeletedHeapTuples += GinPageGetOpaque(page)->maxoff;
566  blknoToDelete = GinPageGetOpaque(page)->rightlink;
567  }
568 
569  if (stats)
570  stats->pages_deleted += data.ndeleted;
571 
572  /*
573  * This operation touches an unusually large number of pages, so
574  * prepare the XLogInsert machinery for that before entering the
575  * critical section.
576  */
577  if (RelationNeedsWAL(index))
579 
581 
582  metadata->head = blknoToDelete;
583 
584  Assert(metadata->nPendingPages >= data.ndeleted);
585  metadata->nPendingPages -= data.ndeleted;
586  Assert(metadata->nPendingHeapTuples >= nDeletedHeapTuples);
587  metadata->nPendingHeapTuples -= nDeletedHeapTuples;
588 
589  if (blknoToDelete == InvalidBlockNumber)
590  {
591  metadata->tail = InvalidBlockNumber;
592  metadata->tailFreeSize = 0;
593  metadata->nPendingPages = 0;
594  metadata->nPendingHeapTuples = 0;
595  }
596 
597  /*
598  * Set pd_lower just past the end of the metadata. This is essential,
599  * because without doing so, metadata will be lost if xlog.c
600  * compresses the page. (We must do this here because pre-v11
601  * versions of PG did not set the metapage's pd_lower correctly, so a
602  * pg_upgraded index might contain the wrong value.)
603  */
604  ((PageHeader) metapage)->pd_lower =
605  ((char *) metadata + sizeof(GinMetaPageData)) - (char *) metapage;
606 
607  MarkBufferDirty(metabuffer);
608 
609  for (i = 0; i < data.ndeleted; i++)
610  {
611  page = BufferGetPage(buffers[i]);
612  GinPageGetOpaque(page)->flags = GIN_DELETED;
613  MarkBufferDirty(buffers[i]);
614  }
615 
616  if (RelationNeedsWAL(index))
617  {
618  XLogRecPtr recptr;
619 
620  XLogBeginInsert();
621  XLogRegisterBuffer(0, metabuffer,
623  for (i = 0; i < data.ndeleted; i++)
624  XLogRegisterBuffer(i + 1, buffers[i], REGBUF_WILL_INIT);
625 
626  memcpy(&data.metadata, metadata, sizeof(GinMetaPageData));
627 
628  XLogRegisterData((char *) &data,
629  sizeof(ginxlogDeleteListPages));
630 
631  recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_DELETE_LISTPAGE);
632  PageSetLSN(metapage, recptr);
633 
634  for (i = 0; i < data.ndeleted; i++)
635  {
636  page = BufferGetPage(buffers[i]);
637  PageSetLSN(page, recptr);
638  }
639  }
640 
641  for (i = 0; i < data.ndeleted; i++)
642  UnlockReleaseBuffer(buffers[i]);
643 
645 
646  for (i = 0; fill_fsm && i < data.ndeleted; i++)
647  RecordFreeIndexPage(index, freespace[i]);
648 
649  } while (blknoToDelete != newHead);
650 }
#define GIN_DELETED
Definition: ginblock.h:41
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1450
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:213
#define END_CRIT_SECTION()
Definition: miscadmin.h:133
#define REGBUF_WILL_INIT
Definition: xloginsert.h:32
#define START_CRIT_SECTION()
Definition: miscadmin.h:131
#define GIN_NDELETE_AT_ONCE
Definition: ginxlog.h:203
uint32 BlockNumber
Definition: block.h:31
#define GinPageGetOpaque(page)
Definition: ginblock.h:109
int64 nPendingHeapTuples
Definition: ginblock.h:73
GinMetaPageData metadata
Definition: ginxlog.h:206
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3332
BlockNumber head
Definition: ginblock.h:60
BlockNumber tail
Definition: ginblock.h:61
#define REGBUF_STANDARD
Definition: xloginsert.h:34
BlockNumber pages_deleted
Definition: genam.h:78
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:323
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:415
#define GinPageIsDeleted(page)
Definition: ginblock.h:123
#define GIN_EXCLUSIVE
Definition: gin_private.h:45
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3546
PageHeaderData * PageHeader
Definition: bufpage.h:162
uint32 tailFreeSize
Definition: ginblock.h:66
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:699
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:594
#define InvalidBlockNumber
Definition: block.h:33
void XLogEnsureRecordSpace(int max_block_id, int ndatas)
Definition: xloginsert.c:146
#define RelationNeedsWAL(relation)
Definition: rel.h:510
int i
#define GinPageGetMeta(p)
Definition: ginblock.h:103
void XLogBeginInsert(void)
Definition: xloginsert.c:120
void RecordFreeIndexPage(Relation rel, BlockNumber freeBlock)
Definition: indexfsm.c:52
#define PageSetLSN(page, lsn)
Definition: bufpage.h:364
int Buffer
Definition: buf.h:23
Pointer Page
Definition: bufpage.h:74
BlockNumber nPendingPages
Definition: ginblock.h:72
#define XLOG_GIN_DELETE_LISTPAGE
Definition: ginxlog.h:195

◆ writeListPage()

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

Definition at line 58 of file ginfast.c.

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

Referenced by makeSublist().

60 {
61  Page page = BufferGetPage(buffer);
62  int32 i,
63  freesize,
64  size = 0;
65  OffsetNumber l,
66  off;
67  char *workspace;
68  char *ptr;
69 
70  /* workspace could be a local array; we use palloc for alignment */
71  workspace = palloc(BLCKSZ);
72 
74 
76 
77  off = FirstOffsetNumber;
78  ptr = workspace;
79 
80  for (i = 0; i < ntuples; i++)
81  {
82  int this_size = IndexTupleSize(tuples[i]);
83 
84  memcpy(ptr, tuples[i], this_size);
85  ptr += this_size;
86  size += this_size;
87 
88  l = PageAddItem(page, (Item) tuples[i], this_size, off, false, false);
89 
90  if (l == InvalidOffsetNumber)
91  elog(ERROR, "failed to add item to index page in \"%s\"",
93 
94  off++;
95  }
96 
97  Assert(size <= BLCKSZ); /* else we overran workspace */
98 
99  GinPageGetOpaque(page)->rightlink = rightlink;
100 
101  /*
102  * tail page may contain only whole row(s) or final part of row placed on
103  * previous pages (a "row" here meaning all the index tuples generated for
104  * one heap tuple)
105  */
106  if (rightlink == InvalidBlockNumber)
107  {
108  GinPageSetFullRow(page);
109  GinPageGetOpaque(page)->maxoff = 1;
110  }
111  else
112  {
113  GinPageGetOpaque(page)->maxoff = 0;
114  }
115 
117 
118  if (RelationNeedsWAL(index))
119  {
121  XLogRecPtr recptr;
122 
123  data.rightlink = rightlink;
124  data.ntuples = ntuples;
125 
126  XLogBeginInsert();
127  XLogRegisterData((char *) &data, sizeof(ginxlogInsertListPage));
128 
130  XLogRegisterBufData(0, workspace, size);
131 
132  recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_INSERT_LISTPAGE);
133  PageSetLSN(page, recptr);
134  }
135 
136  /* get free space before releasing buffer */
137  freesize = PageGetExactFreeSpace(page);
138 
140 
142 
143  pfree(workspace);
144 
145  return freesize;
146 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:361
BlockNumber rightlink
Definition: ginxlog.h:185
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1450
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:213
#define END_CRIT_SECTION()
Definition: miscadmin.h:133
Pointer Item
Definition: item.h:17
#define REGBUF_WILL_INIT
Definition: xloginsert.h:32
#define START_CRIT_SECTION()
Definition: miscadmin.h:131
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:412
#define GinPageGetOpaque(page)
Definition: ginblock.h:109
signed int int32
Definition: c.h:313
uint16 OffsetNumber
Definition: off.h:24
#define XLOG_GIN_INSERT_LISTPAGE
Definition: ginxlog.h:181
void pfree(void *pointer)
Definition: mcxt.c:1031
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3332
#define ERROR
Definition: elog.h:43
#define GIN_LIST
Definition: ginblock.h:43
#define FirstOffsetNumber
Definition: off.h:27
#define RelationGetRelationName(relation)
Definition: rel.h:441
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
#define GinPageSetFullRow(page)
Definition: ginblock.h:119
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:323
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:415
#define InvalidOffsetNumber
Definition: off.h:26
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:699
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:215
#define InvalidBlockNumber
Definition: block.h:33
void GinInitBuffer(Buffer b, uint32 f)
Definition: ginutil.c:355
#define RelationNeedsWAL(relation)
Definition: rel.h:510
Size PageGetExactFreeSpace(Page page)
Definition: bufpage.c:629
void * palloc(Size size)
Definition: mcxt.c:924
int i
#define elog
Definition: elog.h:219
void XLogBeginInsert(void)
Definition: xloginsert.c:120
#define PageSetLSN(page, lsn)
Definition: bufpage.h:364
Pointer Page
Definition: bufpage.h:74
#define IndexTupleSize(itup)
Definition: itup.h:71

Variable Documentation

◆ gin_pending_list_limit

int gin_pending_list_limit = 0

Definition at line 38 of file ginfast.c.