PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gininsert.c File Reference
#include "postgres.h"
#include "access/gin_private.h"
#include "access/gin_tuple.h"
#include "access/parallel.h"
#include "access/table.h"
#include "access/tableam.h"
#include "access/xloginsert.h"
#include "catalog/index.h"
#include "catalog/pg_collation.h"
#include "commands/progress.h"
#include "miscadmin.h"
#include "nodes/execnodes.h"
#include "pgstat.h"
#include "storage/bufmgr.h"
#include "storage/predicate.h"
#include "tcop/tcopprot.h"
#include "utils/datum.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/builtins.h"
Include dependency graph for gininsert.c:

Go to the source code of this file.

Data Structures

struct  GinBuildShared
 
struct  GinLeader
 
struct  GinBuildState
 
struct  GinBuffer
 
struct  GinSegmentInfo
 

Macros

#define PARALLEL_KEY_GIN_SHARED   UINT64CONST(0xB000000000000001)
 
#define PARALLEL_KEY_TUPLESORT   UINT64CONST(0xB000000000000002)
 
#define PARALLEL_KEY_QUERY_TEXT   UINT64CONST(0xB000000000000003)
 
#define PARALLEL_KEY_WAL_USAGE   UINT64CONST(0xB000000000000004)
 
#define PARALLEL_KEY_BUFFER_USAGE   UINT64CONST(0xB000000000000005)
 
#define ParallelTableScanFromGinBuildShared(shared)    (ParallelTableScanDesc) ((char *) (shared) + BUFFERALIGN(sizeof(GinBuildShared)))
 

Typedefs

typedef struct GinBuildShared GinBuildShared
 
typedef struct GinLeader GinLeader
 
typedef struct GinBuffer GinBuffer
 

Functions

static void _gin_begin_parallel (GinBuildState *buildstate, Relation heap, Relation index, bool isconcurrent, int request)
 
static void _gin_end_parallel (GinLeader *ginleader, GinBuildState *state)
 
static Size _gin_parallel_estimate_shared (Relation heap, Snapshot snapshot)
 
static double _gin_parallel_heapscan (GinBuildState *state)
 
static double _gin_parallel_merge (GinBuildState *state)
 
static void _gin_leader_participate_as_worker (GinBuildState *buildstate, Relation heap, Relation index)
 
static void _gin_parallel_scan_and_build (GinBuildState *state, GinBuildShared *ginshared, Sharedsort *sharedsort, Relation heap, Relation index, int sortmem, bool progress)
 
static ItemPointer _gin_parse_tuple_items (GinTuple *a)
 
static Datum _gin_parse_tuple_key (GinTuple *a)
 
static GinTuple_gin_build_tuple (OffsetNumber attrnum, unsigned char category, Datum key, int16 typlen, bool typbyval, ItemPointerData *items, uint32 nitems, Size *len)
 
static IndexTuple addItemPointersToLeafTuple (GinState *ginstate, IndexTuple old, ItemPointerData *items, uint32 nitem, GinStatsData *buildStats, Buffer buffer)
 
static IndexTuple buildFreshLeafTuple (GinState *ginstate, OffsetNumber attnum, Datum key, GinNullCategory category, ItemPointerData *items, uint32 nitem, GinStatsData *buildStats, Buffer buffer)
 
void ginEntryInsert (GinState *ginstate, OffsetNumber attnum, Datum key, GinNullCategory category, ItemPointerData *items, uint32 nitem, GinStatsData *buildStats)
 
static void ginHeapTupleBulkInsert (GinBuildState *buildstate, OffsetNumber attnum, Datum value, bool isNull, ItemPointer heapptr)
 
static void ginBuildCallback (Relation index, ItemPointer tid, Datum *values, bool *isnull, bool tupleIsAlive, void *state)
 
static void ginFlushBuildState (GinBuildState *buildstate, Relation index)
 
static void ginBuildCallbackParallel (Relation index, ItemPointer tid, Datum *values, bool *isnull, bool tupleIsAlive, void *state)
 
IndexBuildResultginbuild (Relation heap, Relation index, IndexInfo *indexInfo)
 
void ginbuildempty (Relation index)
 
static void ginHeapTupleInsert (GinState *ginstate, OffsetNumber attnum, Datum value, bool isNull, ItemPointer item)
 
bool gininsert (Relation index, Datum *values, bool *isnull, ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique, bool indexUnchanged, IndexInfo *indexInfo)
 
static void AssertCheckItemPointers (GinBuffer *buffer)
 
static void AssertCheckGinBuffer (GinBuffer *buffer)
 
static GinBufferGinBufferInit (Relation index)
 
static bool GinBufferIsEmpty (GinBuffer *buffer)
 
static bool GinBufferKeyEquals (GinBuffer *buffer, GinTuple *tup)
 
static bool GinBufferShouldTrim (GinBuffer *buffer, GinTuple *tup)
 
static void GinBufferStoreTuple (GinBuffer *buffer, GinTuple *tup)
 
static void GinBufferReset (GinBuffer *buffer)
 
static void GinBufferTrim (GinBuffer *buffer)
 
static void GinBufferFree (GinBuffer *buffer)
 
static bool GinBufferCanAddKey (GinBuffer *buffer, GinTuple *tup)
 
static void _gin_process_worker_data (GinBuildState *state, Tuplesortstate *worker_sort, bool progress)
 
void _gin_parallel_build_main (dsm_segment *seg, shm_toc *toc)
 
int _gin_compare_tuples (GinTuple *a, GinTuple *b, SortSupport ssup)
 

Macro Definition Documentation

◆ PARALLEL_KEY_BUFFER_USAGE

#define PARALLEL_KEY_BUFFER_USAGE   UINT64CONST(0xB000000000000005)

Definition at line 43 of file gininsert.c.

◆ PARALLEL_KEY_GIN_SHARED

#define PARALLEL_KEY_GIN_SHARED   UINT64CONST(0xB000000000000001)

Definition at line 39 of file gininsert.c.

◆ PARALLEL_KEY_QUERY_TEXT

#define PARALLEL_KEY_QUERY_TEXT   UINT64CONST(0xB000000000000003)

Definition at line 41 of file gininsert.c.

◆ PARALLEL_KEY_TUPLESORT

#define PARALLEL_KEY_TUPLESORT   UINT64CONST(0xB000000000000002)

Definition at line 40 of file gininsert.c.

◆ PARALLEL_KEY_WAL_USAGE

#define PARALLEL_KEY_WAL_USAGE   UINT64CONST(0xB000000000000004)

Definition at line 42 of file gininsert.c.

◆ ParallelTableScanFromGinBuildShared

#define ParallelTableScanFromGinBuildShared (   shared)     (ParallelTableScanDesc) ((char *) (shared) + BUFFERALIGN(sizeof(GinBuildShared)))

Definition at line 104 of file gininsert.c.

Typedef Documentation

◆ GinBuffer

typedef struct GinBuffer GinBuffer

◆ GinBuildShared

◆ GinLeader

typedef struct GinLeader GinLeader

Function Documentation

◆ _gin_begin_parallel()

static void _gin_begin_parallel ( GinBuildState buildstate,
Relation  heap,
Relation  index,
bool  isconcurrent,
int  request 
)
static

Definition at line 900 of file gininsert.c.

902{
903 ParallelContext *pcxt;
904 int scantuplesortstates;
905 Snapshot snapshot;
906 Size estginshared;
907 Size estsort;
908 GinBuildShared *ginshared;
909 Sharedsort *sharedsort;
910 GinLeader *ginleader = (GinLeader *) palloc0(sizeof(GinLeader));
911 WalUsage *walusage;
912 BufferUsage *bufferusage;
913 bool leaderparticipates = true;
914 int querylen;
915
916#ifdef DISABLE_LEADER_PARTICIPATION
917 leaderparticipates = false;
918#endif
919
920 /*
921 * Enter parallel mode, and create context for parallel build of gin index
922 */
924 Assert(request > 0);
925 pcxt = CreateParallelContext("postgres", "_gin_parallel_build_main",
926 request);
927
928 scantuplesortstates = leaderparticipates ? request + 1 : request;
929
930 /*
931 * Prepare for scan of the base relation. In a normal index build, we use
932 * SnapshotAny because we must retrieve all tuples and do our own time
933 * qual checks (because we have to index RECENTLY_DEAD tuples). In a
934 * concurrent build, we take a regular MVCC snapshot and index whatever's
935 * live according to that.
936 */
937 if (!isconcurrent)
938 snapshot = SnapshotAny;
939 else
941
942 /*
943 * Estimate size for our own PARALLEL_KEY_GIN_SHARED workspace.
944 */
945 estginshared = _gin_parallel_estimate_shared(heap, snapshot);
946 shm_toc_estimate_chunk(&pcxt->estimator, estginshared);
947 estsort = tuplesort_estimate_shared(scantuplesortstates);
948 shm_toc_estimate_chunk(&pcxt->estimator, estsort);
949
951
952 /*
953 * Estimate space for WalUsage and BufferUsage -- PARALLEL_KEY_WAL_USAGE
954 * and PARALLEL_KEY_BUFFER_USAGE.
955 *
956 * If there are no extensions loaded that care, we could skip this. We
957 * have no way of knowing whether anyone's looking at pgWalUsage or
958 * pgBufferUsage, so do it unconditionally.
959 */
961 mul_size(sizeof(WalUsage), pcxt->nworkers));
964 mul_size(sizeof(BufferUsage), pcxt->nworkers));
966
967 /* Finally, estimate PARALLEL_KEY_QUERY_TEXT space */
969 {
970 querylen = strlen(debug_query_string);
971 shm_toc_estimate_chunk(&pcxt->estimator, querylen + 1);
973 }
974 else
975 querylen = 0; /* keep compiler quiet */
976
977 /* Everyone's had a chance to ask for space, so now create the DSM */
979
980 /* If no DSM segment was available, back out (do serial build) */
981 if (pcxt->seg == NULL)
982 {
983 if (IsMVCCSnapshot(snapshot))
984 UnregisterSnapshot(snapshot);
987 return;
988 }
989
990 /* Store shared build state, for which we reserved space */
991 ginshared = (GinBuildShared *) shm_toc_allocate(pcxt->toc, estginshared);
992 /* Initialize immutable state */
993 ginshared->heaprelid = RelationGetRelid(heap);
994 ginshared->indexrelid = RelationGetRelid(index);
995 ginshared->isconcurrent = isconcurrent;
996 ginshared->scantuplesortstates = scantuplesortstates;
997
999 SpinLockInit(&ginshared->mutex);
1000
1001 /* Initialize mutable state */
1002 ginshared->nparticipantsdone = 0;
1003 ginshared->reltuples = 0.0;
1004 ginshared->indtuples = 0.0;
1005
1008 snapshot);
1009
1010 /*
1011 * Store shared tuplesort-private state, for which we reserved space.
1012 * Then, initialize opaque state using tuplesort routine.
1013 */
1014 sharedsort = (Sharedsort *) shm_toc_allocate(pcxt->toc, estsort);
1015 tuplesort_initialize_shared(sharedsort, scantuplesortstates,
1016 pcxt->seg);
1017
1018 shm_toc_insert(pcxt->toc, PARALLEL_KEY_GIN_SHARED, ginshared);
1019 shm_toc_insert(pcxt->toc, PARALLEL_KEY_TUPLESORT, sharedsort);
1020
1021 /* Store query string for workers */
1023 {
1024 char *sharedquery;
1025
1026 sharedquery = (char *) shm_toc_allocate(pcxt->toc, querylen + 1);
1027 memcpy(sharedquery, debug_query_string, querylen + 1);
1028 shm_toc_insert(pcxt->toc, PARALLEL_KEY_QUERY_TEXT, sharedquery);
1029 }
1030
1031 /*
1032 * Allocate space for each worker's WalUsage and BufferUsage; no need to
1033 * initialize.
1034 */
1035 walusage = shm_toc_allocate(pcxt->toc,
1036 mul_size(sizeof(WalUsage), pcxt->nworkers));
1037 shm_toc_insert(pcxt->toc, PARALLEL_KEY_WAL_USAGE, walusage);
1038 bufferusage = shm_toc_allocate(pcxt->toc,
1039 mul_size(sizeof(BufferUsage), pcxt->nworkers));
1040 shm_toc_insert(pcxt->toc, PARALLEL_KEY_BUFFER_USAGE, bufferusage);
1041
1042 /* Launch workers, saving status for leader/caller */
1044 ginleader->pcxt = pcxt;
1045 ginleader->nparticipanttuplesorts = pcxt->nworkers_launched;
1046 if (leaderparticipates)
1047 ginleader->nparticipanttuplesorts++;
1048 ginleader->ginshared = ginshared;
1049 ginleader->sharedsort = sharedsort;
1050 ginleader->snapshot = snapshot;
1051 ginleader->walusage = walusage;
1052 ginleader->bufferusage = bufferusage;
1053
1054 /* If no workers were successfully launched, back out (do serial build) */
1055 if (pcxt->nworkers_launched == 0)
1056 {
1057 _gin_end_parallel(ginleader, NULL);
1058 return;
1059 }
1060
1061 /* Save leader state now that it's clear build will be parallel */
1062 buildstate->bs_leader = ginleader;
1063
1064 /* Join heap scan ourselves */
1065 if (leaderparticipates)
1066 _gin_leader_participate_as_worker(buildstate, heap, index);
1067
1068 /*
1069 * Caller needs to wait for all launched workers when we return. Make
1070 * sure that the failure-to-start case will not hang forever.
1071 */
1073}
void InitializeParallelDSM(ParallelContext *pcxt)
Definition: parallel.c:211
void LaunchParallelWorkers(ParallelContext *pcxt)
Definition: parallel.c:573
void DestroyParallelContext(ParallelContext *pcxt)
Definition: parallel.c:950
ParallelContext * CreateParallelContext(const char *library_name, const char *function_name, int nworkers)
Definition: parallel.c:173
void WaitForParallelWorkersToAttach(ParallelContext *pcxt)
Definition: parallel.c:693
size_t Size
Definition: c.h:610
void ConditionVariableInit(ConditionVariable *cv)
#define PARALLEL_KEY_BUFFER_USAGE
Definition: gininsert.c:43
#define PARALLEL_KEY_GIN_SHARED
Definition: gininsert.c:39
static Size _gin_parallel_estimate_shared(Relation heap, Snapshot snapshot)
Definition: gininsert.c:1779
static void _gin_end_parallel(GinLeader *ginleader, GinBuildState *state)
Definition: gininsert.c:1079
static void _gin_leader_participate_as_worker(GinBuildState *buildstate, Relation heap, Relation index)
Definition: gininsert.c:1790
#define PARALLEL_KEY_TUPLESORT
Definition: gininsert.c:40
#define PARALLEL_KEY_QUERY_TEXT
Definition: gininsert.c:41
#define ParallelTableScanFromGinBuildShared(shared)
Definition: gininsert.c:104
#define PARALLEL_KEY_WAL_USAGE
Definition: gininsert.c:42
Assert(PointerIsAligned(start, uint64))
void * palloc0(Size size)
Definition: mcxt.c:1395
const char * debug_query_string
Definition: postgres.c:88
#define RelationGetRelid(relation)
Definition: rel.h:514
void * shm_toc_allocate(shm_toc *toc, Size nbytes)
Definition: shm_toc.c:88
void shm_toc_insert(shm_toc *toc, uint64 key, void *address)
Definition: shm_toc.c:171
#define shm_toc_estimate_chunk(e, sz)
Definition: shm_toc.h:51
#define shm_toc_estimate_keys(e, cnt)
Definition: shm_toc.h:53
Size mul_size(Size s1, Size s2)
Definition: shmem.c:510
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:271
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:864
Snapshot RegisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:822
#define SnapshotAny
Definition: snapmgr.h:33
#define IsMVCCSnapshot(snapshot)
Definition: snapmgr.h:55
#define SpinLockInit(lock)
Definition: spin.h:57
double reltuples
Definition: gininsert.c:88
int scantuplesortstates
Definition: gininsert.c:59
double indtuples
Definition: gininsert.c:89
bool isconcurrent
Definition: gininsert.c:58
slock_t mutex
Definition: gininsert.c:75
ConditionVariable workersdonecv
Definition: gininsert.c:67
int nparticipantsdone
Definition: gininsert.c:87
GinLeader * bs_leader
Definition: gininsert.c:154
Sharedsort * sharedsort
Definition: gininsert.c:133
int nparticipanttuplesorts
Definition: gininsert.c:121
ParallelContext * pcxt
Definition: gininsert.c:113
BufferUsage * bufferusage
Definition: gininsert.c:136
Snapshot snapshot
Definition: gininsert.c:134
GinBuildShared * ginshared
Definition: gininsert.c:132
WalUsage * walusage
Definition: gininsert.c:135
dsm_segment * seg
Definition: parallel.h:42
shm_toc_estimator estimator
Definition: parallel.h:41
shm_toc * toc
Definition: parallel.h:44
int nworkers_launched
Definition: parallel.h:37
Definition: type.h:96
void table_parallelscan_initialize(Relation rel, ParallelTableScanDesc pscan, Snapshot snapshot)
Definition: tableam.c:146
void tuplesort_initialize_shared(Sharedsort *shared, int nWorkers, dsm_segment *seg)
Definition: tuplesort.c:2932
Size tuplesort_estimate_shared(int nWorkers)
Definition: tuplesort.c:2911
void ExitParallelMode(void)
Definition: xact.c:1064
void EnterParallelMode(void)
Definition: xact.c:1051

References _gin_end_parallel(), _gin_leader_participate_as_worker(), _gin_parallel_estimate_shared(), Assert(), GinBuildState::bs_leader, GinLeader::bufferusage, ConditionVariableInit(), CreateParallelContext(), debug_query_string, DestroyParallelContext(), EnterParallelMode(), ParallelContext::estimator, ExitParallelMode(), GetTransactionSnapshot(), GinLeader::ginshared, GinBuildShared::heaprelid, GinBuildShared::indexrelid, GinBuildShared::indtuples, InitializeParallelDSM(), GinBuildShared::isconcurrent, IsMVCCSnapshot, LaunchParallelWorkers(), mul_size(), GinBuildShared::mutex, GinBuildShared::nparticipantsdone, GinLeader::nparticipanttuplesorts, ParallelContext::nworkers, ParallelContext::nworkers_launched, palloc0(), PARALLEL_KEY_BUFFER_USAGE, PARALLEL_KEY_GIN_SHARED, PARALLEL_KEY_QUERY_TEXT, PARALLEL_KEY_TUPLESORT, PARALLEL_KEY_WAL_USAGE, ParallelTableScanFromGinBuildShared, GinLeader::pcxt, RegisterSnapshot(), RelationGetRelid, GinBuildShared::reltuples, GinBuildShared::scantuplesortstates, ParallelContext::seg, GinLeader::sharedsort, shm_toc_allocate(), shm_toc_estimate_chunk, shm_toc_estimate_keys, shm_toc_insert(), GinLeader::snapshot, SnapshotAny, SpinLockInit, table_parallelscan_initialize(), ParallelContext::toc, tuplesort_estimate_shared(), tuplesort_initialize_shared(), UnregisterSnapshot(), WaitForParallelWorkersToAttach(), GinLeader::walusage, and GinBuildShared::workersdonecv.

Referenced by ginbuild().

◆ _gin_build_tuple()

static GinTuple * _gin_build_tuple ( OffsetNumber  attrnum,
unsigned char  category,
Datum  key,
int16  typlen,
bool  typbyval,
ItemPointerData items,
uint32  nitems,
Size len 
)
static

Definition at line 2207 of file gininsert.c.

2211{
2212 GinTuple *tuple;
2213 char *ptr;
2214
2215 Size tuplen;
2216 int keylen;
2217
2218 dlist_mutable_iter iter;
2219 dlist_head segments;
2220 int ncompressed;
2221 Size compresslen;
2222
2223 /*
2224 * Calculate how long is the key value. Only keys with GIN_CAT_NORM_KEY
2225 * have actual non-empty key. We include varlena headers and \0 bytes for
2226 * strings, to make it easier to access the data in-line.
2227 *
2228 * For byval types we simply copy the whole Datum. We could store just the
2229 * necessary bytes, but this is simpler to work with and not worth the
2230 * extra complexity. Moreover we still need to do the MAXALIGN to allow
2231 * direct access to items pointers.
2232 *
2233 * XXX Note that for byval types we store the whole datum, no matter what
2234 * the typlen value is.
2235 */
2236 if (category != GIN_CAT_NORM_KEY)
2237 keylen = 0;
2238 else if (typbyval)
2239 keylen = sizeof(Datum);
2240 else if (typlen > 0)
2241 keylen = typlen;
2242 else if (typlen == -1)
2243 keylen = VARSIZE_ANY(DatumGetPointer(key));
2244 else if (typlen == -2)
2245 keylen = strlen(DatumGetPointer(key)) + 1;
2246 else
2247 elog(ERROR, "unexpected typlen value (%d)", typlen);
2248
2249 /* compress the item pointers */
2250 ncompressed = 0;
2251 compresslen = 0;
2252 dlist_init(&segments);
2253
2254 /* generate compressed segments of TID list chunks */
2255 while (ncompressed < nitems)
2256 {
2257 int cnt;
2258 GinSegmentInfo *seginfo = palloc(sizeof(GinSegmentInfo));
2259
2260 seginfo->seg = ginCompressPostingList(&items[ncompressed],
2261 (nitems - ncompressed),
2262 UINT16_MAX,
2263 &cnt);
2264
2265 ncompressed += cnt;
2266 compresslen += SizeOfGinPostingList(seginfo->seg);
2267
2268 dlist_push_tail(&segments, &seginfo->node);
2269 }
2270
2271 /*
2272 * Determine GIN tuple length with all the data included. Be careful about
2273 * alignment, to allow direct access to compressed segments (those require
2274 * only SHORTALIGN).
2275 */
2276 tuplen = SHORTALIGN(offsetof(GinTuple, data) + keylen) + compresslen;
2277
2278 *len = tuplen;
2279
2280 /*
2281 * Allocate space for the whole GIN tuple.
2282 *
2283 * The palloc0 is needed - writetup_index_gin will write the whole tuple
2284 * to disk, so we need to make sure the padding bytes are defined
2285 * (otherwise valgrind would report this).
2286 */
2287 tuple = palloc0(tuplen);
2288
2289 tuple->tuplen = tuplen;
2290 tuple->attrnum = attrnum;
2291 tuple->category = category;
2292 tuple->keylen = keylen;
2293 tuple->nitems = nitems;
2294
2295 /* key type info */
2296 tuple->typlen = typlen;
2297 tuple->typbyval = typbyval;
2298
2299 /*
2300 * Copy the key and items into the tuple. First the key value, which we
2301 * can simply copy right at the beginning of the data array.
2302 */
2303 if (category == GIN_CAT_NORM_KEY)
2304 {
2305 if (typbyval)
2306 {
2307 memcpy(tuple->data, &key, sizeof(Datum));
2308 }
2309 else if (typlen > 0) /* byref, fixed length */
2310 {
2311 memcpy(tuple->data, DatumGetPointer(key), typlen);
2312 }
2313 else if (typlen == -1)
2314 {
2315 memcpy(tuple->data, DatumGetPointer(key), keylen);
2316 }
2317 else if (typlen == -2)
2318 {
2319 memcpy(tuple->data, DatumGetPointer(key), keylen);
2320 }
2321 }
2322
2323 /* finally, copy the TIDs into the array */
2324 ptr = (char *) tuple + SHORTALIGN(offsetof(GinTuple, data) + keylen);
2325
2326 /* copy in the compressed data, and free the segments */
2327 dlist_foreach_modify(iter, &segments)
2328 {
2329 GinSegmentInfo *seginfo = dlist_container(GinSegmentInfo, node, iter.cur);
2330
2331 memcpy(ptr, seginfo->seg, SizeOfGinPostingList(seginfo->seg));
2332
2333 ptr += SizeOfGinPostingList(seginfo->seg);
2334
2335 dlist_delete(&seginfo->node);
2336
2337 pfree(seginfo->seg);
2338 pfree(seginfo);
2339 }
2340
2341 return tuple;
2342}
#define SHORTALIGN(LEN)
Definition: c.h:806
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
#define GIN_CAT_NORM_KEY
Definition: ginblock.h:208
#define SizeOfGinPostingList(plist)
Definition: ginblock.h:342
GinPostingList * ginCompressPostingList(const ItemPointer ipd, int nipd, int maxsize, int *nwritten)
static void dlist_init(dlist_head *head)
Definition: ilist.h:314
static void dlist_delete(dlist_node *node)
Definition: ilist.h:405
#define dlist_foreach_modify(iter, lhead)
Definition: ilist.h:640
static void dlist_push_tail(dlist_head *head, dlist_node *node)
Definition: ilist.h:364
#define dlist_container(type, membername, ptr)
Definition: ilist.h:593
#define nitems(x)
Definition: indent.h:31
void pfree(void *pointer)
Definition: mcxt.c:1594
void * palloc(Size size)
Definition: mcxt.c:1365
const void size_t len
const void * data
uint64_t Datum
Definition: postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:322
dlist_node node
Definition: gininsert.c:2181
GinPostingList * seg
Definition: gininsert.c:2182
char data[FLEXIBLE_ARRAY_MEMBER]
Definition: gin_tuple.h:31
int nitems
Definition: gin_tuple.h:30
int16 typlen
Definition: gin_tuple.h:27
bool typbyval
Definition: gin_tuple.h:28
signed char category
Definition: gin_tuple.h:29
int tuplen
Definition: gin_tuple.h:24
uint16 keylen
Definition: gin_tuple.h:26
OffsetNumber attrnum
Definition: gin_tuple.h:25
dlist_node * cur
Definition: ilist.h:200
static ItemArray items
Definition: test_tidstore.c:48
static Size VARSIZE_ANY(const void *PTR)
Definition: varatt.h:460

References GinTuple::attrnum, GinTuple::category, dlist_mutable_iter::cur, GinTuple::data, data, DatumGetPointer(), dlist_container, dlist_delete(), dlist_foreach_modify, dlist_init(), dlist_push_tail(), elog, ERROR, GIN_CAT_NORM_KEY, ginCompressPostingList(), items, sort-test::key, GinTuple::keylen, len, GinTuple::nitems, nitems, GinSegmentInfo::node, palloc(), palloc0(), pfree(), GinSegmentInfo::seg, SHORTALIGN, SizeOfGinPostingList, GinTuple::tuplen, GinTuple::typbyval, GinTuple::typlen, and VARSIZE_ANY().

Referenced by _gin_process_worker_data(), and ginFlushBuildState().

◆ _gin_compare_tuples()

int _gin_compare_tuples ( GinTuple a,
GinTuple b,
SortSupport  ssup 
)

Definition at line 2409 of file gininsert.c.

2410{
2411 int r;
2412 Datum keya,
2413 keyb;
2414
2415 if (a->attrnum < b->attrnum)
2416 return -1;
2417
2418 if (a->attrnum > b->attrnum)
2419 return 1;
2420
2421 if (a->category < b->category)
2422 return -1;
2423
2424 if (a->category > b->category)
2425 return 1;
2426
2427 if (a->category == GIN_CAT_NORM_KEY)
2428 {
2429 keya = _gin_parse_tuple_key(a);
2430 keyb = _gin_parse_tuple_key(b);
2431
2432 r = ApplySortComparator(keya, false,
2433 keyb, false,
2434 &ssup[a->attrnum - 1]);
2435
2436 /* if the key is the same, consider the first TID in the array */
2437 return (r != 0) ? r : ItemPointerCompare(GinTupleGetFirst(a),
2439 }
2440
2443}
static ItemPointer GinTupleGetFirst(GinTuple *tup)
Definition: gin_tuple.h:35
static Datum _gin_parse_tuple_key(GinTuple *a)
Definition: gininsert.c:2356
int b
Definition: isn.c:74
int a
Definition: isn.c:73
int32 ItemPointerCompare(ItemPointer arg1, ItemPointer arg2)
Definition: itemptr.c:51
static int ApplySortComparator(Datum datum1, bool isNull1, Datum datum2, bool isNull2, SortSupport ssup)
Definition: sortsupport.h:200

References _gin_parse_tuple_key(), a, ApplySortComparator(), b, GIN_CAT_NORM_KEY, GinTupleGetFirst(), and ItemPointerCompare().

Referenced by comparetup_index_gin().

◆ _gin_end_parallel()

static void _gin_end_parallel ( GinLeader ginleader,
GinBuildState state 
)
static

Definition at line 1079 of file gininsert.c.

1080{
1081 int i;
1082
1083 /* Shutdown worker processes */
1085
1086 /*
1087 * Next, accumulate WAL usage. (This must wait for the workers to finish,
1088 * or we might get incomplete data.)
1089 */
1090 for (i = 0; i < ginleader->pcxt->nworkers_launched; i++)
1091 InstrAccumParallelQuery(&ginleader->bufferusage[i], &ginleader->walusage[i]);
1092
1093 /* Free last reference to MVCC snapshot, if one was used */
1094 if (IsMVCCSnapshot(ginleader->snapshot))
1095 UnregisterSnapshot(ginleader->snapshot);
1096 DestroyParallelContext(ginleader->pcxt);
1098}
void WaitForParallelWorkersToFinish(ParallelContext *pcxt)
Definition: parallel.c:796
void InstrAccumParallelQuery(BufferUsage *bufusage, WalUsage *walusage)
Definition: instrument.c:218
int i
Definition: isn.c:77

References GinLeader::bufferusage, DestroyParallelContext(), ExitParallelMode(), i, InstrAccumParallelQuery(), IsMVCCSnapshot, ParallelContext::nworkers_launched, GinLeader::pcxt, GinLeader::snapshot, UnregisterSnapshot(), WaitForParallelWorkersToFinish(), and GinLeader::walusage.

Referenced by _gin_begin_parallel(), and ginbuild().

◆ _gin_leader_participate_as_worker()

static void _gin_leader_participate_as_worker ( GinBuildState buildstate,
Relation  heap,
Relation  index 
)
static

Definition at line 1790 of file gininsert.c.

1791{
1792 GinLeader *ginleader = buildstate->bs_leader;
1793 int sortmem;
1794
1795 /*
1796 * Might as well use reliable figure when doling out maintenance_work_mem
1797 * (when requested number of workers were not launched, this will be
1798 * somewhat higher than it is for other workers).
1799 */
1800 sortmem = maintenance_work_mem / ginleader->nparticipanttuplesorts;
1801
1802 /* Perform work common to all participants */
1803 _gin_parallel_scan_and_build(buildstate, ginleader->ginshared,
1804 ginleader->sharedsort, heap, index,
1805 sortmem, true);
1806}
static void _gin_parallel_scan_and_build(GinBuildState *state, GinBuildShared *ginshared, Sharedsort *sharedsort, Relation heap, Relation index, int sortmem, bool progress)
Definition: gininsert.c:2001
int maintenance_work_mem
Definition: globals.c:133

References _gin_parallel_scan_and_build(), GinBuildState::bs_leader, GinLeader::ginshared, maintenance_work_mem, GinLeader::nparticipanttuplesorts, and GinLeader::sharedsort.

Referenced by _gin_begin_parallel().

◆ _gin_parallel_build_main()

void _gin_parallel_build_main ( dsm_segment seg,
shm_toc toc 
)

Definition at line 2076 of file gininsert.c.

2077{
2078 char *sharedquery;
2079 GinBuildShared *ginshared;
2080 Sharedsort *sharedsort;
2081 GinBuildState buildstate;
2082 Relation heapRel;
2083 Relation indexRel;
2084 LOCKMODE heapLockmode;
2085 LOCKMODE indexLockmode;
2086 WalUsage *walusage;
2087 BufferUsage *bufferusage;
2088 int sortmem;
2089
2090 /*
2091 * The only possible status flag that can be set to the parallel worker is
2092 * PROC_IN_SAFE_IC.
2093 */
2094 Assert((MyProc->statusFlags == 0) ||
2096
2097 /* Set debug_query_string for individual workers first */
2098 sharedquery = shm_toc_lookup(toc, PARALLEL_KEY_QUERY_TEXT, true);
2099 debug_query_string = sharedquery;
2100
2101 /* Report the query string from leader */
2103
2104 /* Look up gin shared state */
2105 ginshared = shm_toc_lookup(toc, PARALLEL_KEY_GIN_SHARED, false);
2106
2107 /* Open relations using lock modes known to be obtained by index.c */
2108 if (!ginshared->isconcurrent)
2109 {
2110 heapLockmode = ShareLock;
2111 indexLockmode = AccessExclusiveLock;
2112 }
2113 else
2114 {
2115 heapLockmode = ShareUpdateExclusiveLock;
2116 indexLockmode = RowExclusiveLock;
2117 }
2118
2119 /* Open relations within worker */
2120 heapRel = table_open(ginshared->heaprelid, heapLockmode);
2121 indexRel = index_open(ginshared->indexrelid, indexLockmode);
2122
2123 /* initialize the GIN build state */
2124 initGinState(&buildstate.ginstate, indexRel);
2125 buildstate.indtuples = 0;
2126 memset(&buildstate.buildStats, 0, sizeof(GinStatsData));
2127 memset(&buildstate.tid, 0, sizeof(ItemPointerData));
2128
2129 /*
2130 * create a temporary memory context that is used to hold data not yet
2131 * dumped out to the index
2132 */
2134 "Gin build temporary context",
2136
2137 /*
2138 * create a temporary memory context that is used for calling
2139 * ginExtractEntries(), and can be reset after each tuple
2140 */
2142 "Gin build temporary context for user-defined function",
2144
2145 buildstate.accum.ginstate = &buildstate.ginstate;
2146 ginInitBA(&buildstate.accum);
2147
2148
2149 /* Look up shared state private to tuplesort.c */
2150 sharedsort = shm_toc_lookup(toc, PARALLEL_KEY_TUPLESORT, false);
2151 tuplesort_attach_shared(sharedsort, seg);
2152
2153 /* Prepare to track buffer usage during parallel execution */
2155
2156 /*
2157 * Might as well use reliable figure when doling out maintenance_work_mem
2158 * (when requested number of workers were not launched, this will be
2159 * somewhat higher than it is for other workers).
2160 */
2161 sortmem = maintenance_work_mem / ginshared->scantuplesortstates;
2162
2163 _gin_parallel_scan_and_build(&buildstate, ginshared, sharedsort,
2164 heapRel, indexRel, sortmem, false);
2165
2166 /* Report WAL/buffer usage during parallel execution */
2167 bufferusage = shm_toc_lookup(toc, PARALLEL_KEY_BUFFER_USAGE, false);
2168 walusage = shm_toc_lookup(toc, PARALLEL_KEY_WAL_USAGE, false);
2170 &walusage[ParallelWorkerNumber]);
2171
2172 index_close(indexRel, indexLockmode);
2173 table_close(heapRel, heapLockmode);
2174}
int ParallelWorkerNumber
Definition: parallel.c:115
void pgstat_report_activity(BackendState state, const char *cmd_str)
@ STATE_RUNNING
void ginInitBA(BuildAccumulator *accum)
Definition: ginbulk.c:109
void initGinState(GinState *state, Relation index)
Definition: ginutil.c:102
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:177
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
void InstrEndParallelQuery(BufferUsage *bufusage, WalUsage *walusage)
Definition: instrument.c:208
void InstrStartParallelQuery(void)
Definition: instrument.c:200
int LOCKMODE
Definition: lockdefs.h:26
#define AccessExclusiveLock
Definition: lockdefs.h:43
#define ShareUpdateExclusiveLock
Definition: lockdefs.h:39
#define ShareLock
Definition: lockdefs.h:40
#define RowExclusiveLock
Definition: lockdefs.h:38
MemoryContext CurrentMemoryContext
Definition: mcxt.c:160
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
#define PROC_IN_SAFE_IC
Definition: proc.h:59
void * shm_toc_lookup(shm_toc *toc, uint64 key, bool noError)
Definition: shm_toc.c:232
PGPROC * MyProc
Definition: proc.c:66
GinState * ginstate
Definition: gin_private.h:442
double indtuples
Definition: gininsert.c:142
GinState ginstate
Definition: gininsert.c:141
MemoryContext tmpCtx
Definition: gininsert.c:144
GinStatsData buildStats
Definition: gininsert.c:143
ItemPointerData tid
Definition: gininsert.c:147
MemoryContext funcCtx
Definition: gininsert.c:145
BuildAccumulator accum
Definition: gininsert.c:146
uint8 statusFlags
Definition: proc.h:259
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
void tuplesort_attach_shared(Sharedsort *shared, dsm_segment *seg)
Definition: tuplesort.c:2955

References _gin_parallel_scan_and_build(), AccessExclusiveLock, GinBuildState::accum, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert(), GinBuildState::buildStats, CurrentMemoryContext, debug_query_string, GinBuildState::funcCtx, ginInitBA(), GinBuildState::ginstate, BuildAccumulator::ginstate, GinBuildShared::heaprelid, index_close(), index_open(), GinBuildShared::indexrelid, GinBuildState::indtuples, initGinState(), InstrEndParallelQuery(), InstrStartParallelQuery(), GinBuildShared::isconcurrent, maintenance_work_mem, MyProc, PARALLEL_KEY_BUFFER_USAGE, PARALLEL_KEY_GIN_SHARED, PARALLEL_KEY_QUERY_TEXT, PARALLEL_KEY_TUPLESORT, PARALLEL_KEY_WAL_USAGE, ParallelWorkerNumber, pgstat_report_activity(), PROC_IN_SAFE_IC, RowExclusiveLock, GinBuildShared::scantuplesortstates, ShareLock, ShareUpdateExclusiveLock, shm_toc_lookup(), STATE_RUNNING, PGPROC::statusFlags, table_close(), table_open(), GinBuildState::tid, GinBuildState::tmpCtx, and tuplesort_attach_shared().

◆ _gin_parallel_estimate_shared()

static Size _gin_parallel_estimate_shared ( Relation  heap,
Snapshot  snapshot 
)
static

Definition at line 1779 of file gininsert.c.

1780{
1781 /* c.f. shm_toc_allocate as to why BUFFERALIGN is used */
1782 return add_size(BUFFERALIGN(sizeof(GinBuildShared)),
1783 table_parallelscan_estimate(heap, snapshot));
1784}
#define BUFFERALIGN(LEN)
Definition: c.h:812
Size add_size(Size s1, Size s2)
Definition: shmem.c:493
Size table_parallelscan_estimate(Relation rel, Snapshot snapshot)
Definition: tableam.c:131

References add_size(), BUFFERALIGN, and table_parallelscan_estimate().

Referenced by _gin_begin_parallel().

◆ _gin_parallel_heapscan()

static double _gin_parallel_heapscan ( GinBuildState state)
static

Definition at line 1110 of file gininsert.c.

1111{
1112 GinBuildShared *ginshared = state->bs_leader->ginshared;
1113 int nparticipanttuplesorts;
1114
1115 nparticipanttuplesorts = state->bs_leader->nparticipanttuplesorts;
1116 for (;;)
1117 {
1118 SpinLockAcquire(&ginshared->mutex);
1119 if (ginshared->nparticipantsdone == nparticipanttuplesorts)
1120 {
1121 /* copy the data into leader state */
1122 state->bs_reltuples = ginshared->reltuples;
1123 state->bs_numtuples = ginshared->indtuples;
1124
1125 SpinLockRelease(&ginshared->mutex);
1126 break;
1127 }
1128 SpinLockRelease(&ginshared->mutex);
1129
1131 WAIT_EVENT_PARALLEL_CREATE_INDEX_SCAN);
1132 }
1133
1135
1136 return state->bs_reltuples;
1137}
bool ConditionVariableCancelSleep(void)
void ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info)
#define SpinLockRelease(lock)
Definition: spin.h:61
#define SpinLockAcquire(lock)
Definition: spin.h:59
Definition: regguts.h:323

References ConditionVariableCancelSleep(), ConditionVariableSleep(), GinBuildShared::indtuples, GinBuildShared::mutex, GinBuildShared::nparticipantsdone, GinBuildShared::reltuples, SpinLockAcquire, SpinLockRelease, and GinBuildShared::workersdonecv.

Referenced by _gin_parallel_merge().

◆ _gin_parallel_merge()

static double _gin_parallel_merge ( GinBuildState state)
static

Definition at line 1617 of file gininsert.c.

1618{
1619 GinTuple *tup;
1620 Size tuplen;
1621 double reltuples = 0;
1622 GinBuffer *buffer;
1623
1624 /* GIN tuples from workers, merged by leader */
1625 double numtuples = 0;
1626
1627 /* wait for workers to scan table and produce partial results */
1628 reltuples = _gin_parallel_heapscan(state);
1629
1630 /* Execute the sort */
1633
1634 /* do the actual sort in the leader */
1635 tuplesort_performsort(state->bs_sortstate);
1636
1637 /*
1638 * Initialize buffer to combine entries for the same key.
1639 *
1640 * The leader is allowed to use the whole maintenance_work_mem buffer to
1641 * combine data. The parallel workers already completed.
1642 */
1643 buffer = GinBufferInit(state->ginstate.index);
1644
1645 /*
1646 * Set the progress target for the next phase. Reset the block number
1647 * values set by table_index_build_scan
1648 */
1649 {
1650 const int progress_index[] = {
1655 };
1656 const int64 progress_vals[] = {
1658 state->bs_numtuples,
1659 0, 0
1660 };
1661
1662 pgstat_progress_update_multi_param(4, progress_index, progress_vals);
1663 }
1664
1665 /*
1666 * Read the GIN tuples from the shared tuplesort, sorted by category and
1667 * key. That probably gives us order matching how data is organized in the
1668 * index.
1669 *
1670 * We don't insert the GIN tuples right away, but instead accumulate as
1671 * many TIDs for the same key as possible, and then insert that at once.
1672 * This way we don't need to decompress/recompress the posting lists, etc.
1673 */
1674 while ((tup = tuplesort_getgintuple(state->bs_sortstate, &tuplen, true)) != NULL)
1675 {
1676 MemoryContext oldCtx;
1677
1679
1680 /*
1681 * If the buffer can accept the new GIN tuple, just store it there and
1682 * we're done. If it's a different key (or maybe too much data) flush
1683 * the current contents into the index first.
1684 */
1685 if (!GinBufferCanAddKey(buffer, tup))
1686 {
1687 /*
1688 * Buffer is not empty and it's storing a different key - flush
1689 * the data into the insert, and start a new entry for current
1690 * GinTuple.
1691 */
1693
1694 oldCtx = MemoryContextSwitchTo(state->tmpCtx);
1695
1696 ginEntryInsert(&state->ginstate,
1697 buffer->attnum, buffer->key, buffer->category,
1698 buffer->items, buffer->nitems, &state->buildStats);
1699
1700 MemoryContextSwitchTo(oldCtx);
1701 MemoryContextReset(state->tmpCtx);
1702
1703 /* discard the existing data */
1704 GinBufferReset(buffer);
1705 }
1706
1707 /*
1708 * We're about to add a GIN tuple to the buffer - check the memory
1709 * limit first, and maybe write out some of the data into the index
1710 * first, if needed (and possible). We only flush the part of the TID
1711 * list that we know won't change, and only if there's enough data for
1712 * compression to work well.
1713 */
1714 if (GinBufferShouldTrim(buffer, tup))
1715 {
1716 Assert(buffer->nfrozen > 0);
1717
1718 /*
1719 * Buffer is not empty and it's storing a different key - flush
1720 * the data into the insert, and start a new entry for current
1721 * GinTuple.
1722 */
1724
1725 oldCtx = MemoryContextSwitchTo(state->tmpCtx);
1726
1727 ginEntryInsert(&state->ginstate,
1728 buffer->attnum, buffer->key, buffer->category,
1729 buffer->items, buffer->nfrozen, &state->buildStats);
1730
1731 MemoryContextSwitchTo(oldCtx);
1732 MemoryContextReset(state->tmpCtx);
1733
1734 /* truncate the data we've just discarded */
1735 GinBufferTrim(buffer);
1736 }
1737
1738 /*
1739 * Remember data for the current tuple (either remember the new key,
1740 * or append if to the existing data).
1741 */
1742 GinBufferStoreTuple(buffer, tup);
1743
1744 /* Report progress */
1746 ++numtuples);
1747 }
1748
1749 /* flush data remaining in the buffer (for the last key) */
1750 if (!GinBufferIsEmpty(buffer))
1751 {
1753
1754 ginEntryInsert(&state->ginstate,
1755 buffer->attnum, buffer->key, buffer->category,
1756 buffer->items, buffer->nitems, &state->buildStats);
1757
1758 /* discard the existing data */
1759 GinBufferReset(buffer);
1760
1761 /* Report progress */
1763 ++numtuples);
1764 }
1765
1766 /* relase all the memory */
1767 GinBufferFree(buffer);
1768
1769 tuplesort_end(state->bs_sortstate);
1770
1771 return reltuples;
1772}
void pgstat_progress_update_param(int index, int64 val)
void pgstat_progress_update_multi_param(int nparam, const int *index, const int64 *val)
int64_t int64
Definition: c.h:535
#define PROGRESS_GIN_PHASE_PERFORMSORT_2
Definition: gin.h:49
#define PROGRESS_GIN_PHASE_MERGE_2
Definition: gin.h:50
static void AssertCheckItemPointers(GinBuffer *buffer)
Definition: gininsert.c:1177
static bool GinBufferIsEmpty(GinBuffer *buffer)
Definition: gininsert.c:1301
static GinBuffer * GinBufferInit(Relation index)
Definition: gininsert.c:1233
static void GinBufferReset(GinBuffer *buffer)
Definition: gininsert.c:1529
void ginEntryInsert(GinState *ginstate, OffsetNumber attnum, Datum key, GinNullCategory category, ItemPointerData *items, uint32 nitem, GinStatsData *buildStats)
Definition: gininsert.c:343
static void GinBufferTrim(GinBuffer *buffer)
Definition: gininsert.c:1559
static bool GinBufferCanAddKey(GinBuffer *buffer, GinTuple *tup)
Definition: gininsert.c:1595
static bool GinBufferShouldTrim(GinBuffer *buffer, GinTuple *tup)
Definition: gininsert.c:1379
static void GinBufferFree(GinBuffer *buffer)
Definition: gininsert.c:1575
static void GinBufferStoreTuple(GinBuffer *buffer, GinTuple *tup)
Definition: gininsert.c:1420
static double _gin_parallel_heapscan(GinBuildState *state)
Definition: gininsert.c:1110
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:400
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
#define PROGRESS_CREATEIDX_TUPLES_TOTAL
Definition: progress.h:89
#define PROGRESS_SCAN_BLOCKS_DONE
Definition: progress.h:125
#define PROGRESS_CREATEIDX_TUPLES_DONE
Definition: progress.h:90
#define PROGRESS_CREATEIDX_SUBPHASE
Definition: progress.h:88
#define PROGRESS_SCAN_BLOCKS_TOTAL
Definition: progress.h:124
int nfrozen
Definition: gininsert.c:1167
int nitems
Definition: gininsert.c:1166
GinNullCategory category
Definition: gininsert.c:1154
OffsetNumber attnum
Definition: gininsert.c:1153
Datum key
Definition: gininsert.c:1155
ItemPointerData * items
Definition: gininsert.c:1169
void tuplesort_performsort(Tuplesortstate *state)
Definition: tuplesort.c:1359
void tuplesort_end(Tuplesortstate *state)
Definition: tuplesort.c:947
GinTuple * tuplesort_getgintuple(Tuplesortstate *state, Size *len, bool forward)

References _gin_parallel_heapscan(), Assert(), AssertCheckItemPointers(), GinBuffer::attnum, GinBuffer::category, CHECK_FOR_INTERRUPTS, GinBufferCanAddKey(), GinBufferFree(), GinBufferInit(), GinBufferIsEmpty(), GinBufferReset(), GinBufferShouldTrim(), GinBufferStoreTuple(), GinBufferTrim(), ginEntryInsert(), GinBuffer::items, GinBuffer::key, MemoryContextReset(), MemoryContextSwitchTo(), GinBuffer::nfrozen, GinBuffer::nitems, pgstat_progress_update_multi_param(), pgstat_progress_update_param(), PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_CREATEIDX_TUPLES_DONE, PROGRESS_CREATEIDX_TUPLES_TOTAL, PROGRESS_GIN_PHASE_MERGE_2, PROGRESS_GIN_PHASE_PERFORMSORT_2, PROGRESS_SCAN_BLOCKS_DONE, PROGRESS_SCAN_BLOCKS_TOTAL, tuplesort_end(), tuplesort_getgintuple(), and tuplesort_performsort().

Referenced by ginbuild().

◆ _gin_parallel_scan_and_build()

static void _gin_parallel_scan_and_build ( GinBuildState state,
GinBuildShared ginshared,
Sharedsort sharedsort,
Relation  heap,
Relation  index,
int  sortmem,
bool  progress 
)
static

Definition at line 2001 of file gininsert.c.

2005{
2006 SortCoordinate coordinate;
2007 TableScanDesc scan;
2008 double reltuples;
2009 IndexInfo *indexInfo;
2010
2011 /* Initialize local tuplesort coordination state */
2012 coordinate = palloc0(sizeof(SortCoordinateData));
2013 coordinate->isWorker = true;
2014 coordinate->nParticipants = -1;
2015 coordinate->sharedsort = sharedsort;
2016
2017 /* remember how much space is allowed for the accumulated entries */
2018 state->work_mem = (sortmem / 2);
2019
2020 /* Begin "partial" tuplesort */
2021 state->bs_sortstate = tuplesort_begin_index_gin(heap, index,
2022 state->work_mem,
2023 coordinate,
2025
2026 /* Local per-worker sort of raw-data */
2027 state->bs_worker_sort = tuplesort_begin_index_gin(heap, index,
2028 state->work_mem,
2029 NULL,
2031
2032 /* Join parallel scan */
2033 indexInfo = BuildIndexInfo(index);
2034 indexInfo->ii_Concurrent = ginshared->isconcurrent;
2035
2036 scan = table_beginscan_parallel(heap,
2038
2039 reltuples = table_index_build_scan(heap, index, indexInfo, true, progress,
2041
2042 /* write remaining accumulated entries */
2044
2045 /*
2046 * Do the first phase of in-worker processing - sort the data produced by
2047 * the callback, and combine them into much larger chunks and place that
2048 * into the shared tuplestore for leader to process.
2049 */
2050 _gin_process_worker_data(state, state->bs_worker_sort, progress);
2051
2052 /* sort the GIN tuples built by this worker */
2053 tuplesort_performsort(state->bs_sortstate);
2054
2055 state->bs_reltuples += reltuples;
2056
2057 /*
2058 * Done. Record ambuild statistics.
2059 */
2060 SpinLockAcquire(&ginshared->mutex);
2061 ginshared->nparticipantsdone++;
2062 ginshared->reltuples += state->bs_reltuples;
2063 ginshared->indtuples += state->bs_numtuples;
2064 SpinLockRelease(&ginshared->mutex);
2065
2066 /* Notify leader */
2068
2069 tuplesort_end(state->bs_sortstate);
2070}
void ConditionVariableSignal(ConditionVariable *cv)
static void ginBuildCallbackParallel(Relation index, ItemPointer tid, Datum *values, bool *isnull, bool tupleIsAlive, void *state)
Definition: gininsert.c:537
static void ginFlushBuildState(GinBuildState *buildstate, Relation index)
Definition: gininsert.c:488
static void _gin_process_worker_data(GinBuildState *state, Tuplesortstate *worker_sort, bool progress)
Definition: gininsert.c:1823
IndexInfo * BuildIndexInfo(Relation index)
Definition: index.c:2428
static int progress
Definition: pgbench.c:262
bool ii_Concurrent
Definition: execnodes.h:210
Sharedsort * sharedsort
Definition: tuplesort.h:59
TableScanDesc table_beginscan_parallel(Relation relation, ParallelTableScanDesc pscan)
Definition: tableam.c:166
static double table_index_build_scan(Relation table_rel, Relation index_rel, IndexInfo *index_info, bool allow_sync, bool progress, IndexBuildCallback callback, void *callback_state, TableScanDesc scan)
Definition: tableam.h:1744
#define TUPLESORT_NONE
Definition: tuplesort.h:94
Tuplesortstate * tuplesort_begin_index_gin(Relation heapRel, Relation indexRel, int workMem, SortCoordinate coordinate, int sortopt)

References _gin_process_worker_data(), BuildIndexInfo(), ConditionVariableSignal(), ginBuildCallbackParallel(), ginFlushBuildState(), IndexInfo::ii_Concurrent, GinBuildShared::indtuples, GinBuildShared::isconcurrent, SortCoordinateData::isWorker, GinBuildShared::mutex, SortCoordinateData::nParticipants, GinBuildShared::nparticipantsdone, palloc0(), ParallelTableScanFromGinBuildShared, progress, GinBuildShared::reltuples, SortCoordinateData::sharedsort, SpinLockAcquire, SpinLockRelease, table_beginscan_parallel(), table_index_build_scan(), tuplesort_begin_index_gin(), tuplesort_end(), TUPLESORT_NONE, tuplesort_performsort(), and GinBuildShared::workersdonecv.

Referenced by _gin_leader_participate_as_worker(), and _gin_parallel_build_main().

◆ _gin_parse_tuple_items()

static ItemPointer _gin_parse_tuple_items ( GinTuple a)
static

Definition at line 2377 of file gininsert.c.

2378{
2379 int len;
2380 char *ptr;
2381 int ndecoded;
2383
2384 len = a->tuplen - SHORTALIGN(offsetof(GinTuple, data) + a->keylen);
2385 ptr = (char *) a + SHORTALIGN(offsetof(GinTuple, data) + a->keylen);
2386
2388
2389 Assert(ndecoded == a->nitems);
2390
2391 return (ItemPointer) items;
2392}
ItemPointer ginPostingListDecodeAllSegments(GinPostingList *segment, int len, int *ndecoded_out)

References a, Assert(), data, ginPostingListDecodeAllSegments(), items, len, and SHORTALIGN.

Referenced by GinBufferStoreTuple().

◆ _gin_parse_tuple_key()

static Datum _gin_parse_tuple_key ( GinTuple a)
static

Definition at line 2356 of file gininsert.c.

2357{
2358 Datum key;
2359
2360 if (a->category != GIN_CAT_NORM_KEY)
2361 return (Datum) 0;
2362
2363 if (a->typbyval)
2364 {
2365 memcpy(&key, a->data, a->keylen);
2366 return key;
2367 }
2368
2369 return PointerGetDatum(a->data);
2370}
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:332

References a, GIN_CAT_NORM_KEY, sort-test::key, and PointerGetDatum().

Referenced by _gin_compare_tuples(), and GinBufferStoreTuple().

◆ _gin_process_worker_data()

static void _gin_process_worker_data ( GinBuildState state,
Tuplesortstate worker_sort,
bool  progress 
)
static

Definition at line 1823 of file gininsert.c.

1825{
1826 GinTuple *tup;
1827 Size tuplen;
1828
1829 GinBuffer *buffer;
1830
1831 /*
1832 * Initialize buffer to combine entries for the same key.
1833 *
1834 * The workers are limited to the same amount of memory as during the sort
1835 * in ginBuildCallbackParallel. But this probably should be the 32MB used
1836 * during planning, just like there.
1837 */
1838 buffer = GinBufferInit(state->ginstate.index);
1839
1840 /* sort the raw per-worker data */
1841 if (progress)
1844
1845 tuplesort_performsort(state->bs_worker_sort);
1846
1847 /* reset the number of GIN tuples produced by this worker */
1848 state->bs_numtuples = 0;
1849
1850 if (progress)
1853
1854 /*
1855 * Read the GIN tuples from the shared tuplesort, sorted by the key, and
1856 * merge them into larger chunks for the leader to combine.
1857 */
1858 while ((tup = tuplesort_getgintuple(worker_sort, &tuplen, true)) != NULL)
1859 {
1860
1862
1863 /*
1864 * If the buffer can accept the new GIN tuple, just store it there and
1865 * we're done. If it's a different key (or maybe too much data) flush
1866 * the current contents into the index first.
1867 */
1868 if (!GinBufferCanAddKey(buffer, tup))
1869 {
1870 GinTuple *ntup;
1871 Size ntuplen;
1872
1873 /*
1874 * Buffer is not empty and it's storing a different key - flush
1875 * the data into the insert, and start a new entry for current
1876 * GinTuple.
1877 */
1879
1880 ntup = _gin_build_tuple(buffer->attnum, buffer->category,
1881 buffer->key, buffer->typlen, buffer->typbyval,
1882 buffer->items, buffer->nitems, &ntuplen);
1883
1884 tuplesort_putgintuple(state->bs_sortstate, ntup, ntuplen);
1885 state->bs_numtuples++;
1886
1887 pfree(ntup);
1888
1889 /* discard the existing data */
1890 GinBufferReset(buffer);
1891 }
1892
1893 /*
1894 * We're about to add a GIN tuple to the buffer - check the memory
1895 * limit first, and maybe write out some of the data into the index
1896 * first, if needed (and possible). We only flush the part of the TID
1897 * list that we know won't change, and only if there's enough data for
1898 * compression to work well.
1899 */
1900 if (GinBufferShouldTrim(buffer, tup))
1901 {
1902 GinTuple *ntup;
1903 Size ntuplen;
1904
1905 Assert(buffer->nfrozen > 0);
1906
1907 /*
1908 * Buffer is not empty and it's storing a different key - flush
1909 * the data into the insert, and start a new entry for current
1910 * GinTuple.
1911 */
1913
1914 ntup = _gin_build_tuple(buffer->attnum, buffer->category,
1915 buffer->key, buffer->typlen, buffer->typbyval,
1916 buffer->items, buffer->nfrozen, &ntuplen);
1917
1918 tuplesort_putgintuple(state->bs_sortstate, ntup, ntuplen);
1919
1920 pfree(ntup);
1921
1922 /* truncate the data we've just discarded */
1923 GinBufferTrim(buffer);
1924 }
1925
1926 /*
1927 * Remember data for the current tuple (either remember the new key,
1928 * or append if to the existing data).
1929 */
1930 GinBufferStoreTuple(buffer, tup);
1931 }
1932
1933 /* flush data remaining in the buffer (for the last key) */
1934 if (!GinBufferIsEmpty(buffer))
1935 {
1936 GinTuple *ntup;
1937 Size ntuplen;
1938
1940
1941 ntup = _gin_build_tuple(buffer->attnum, buffer->category,
1942 buffer->key, buffer->typlen, buffer->typbyval,
1943 buffer->items, buffer->nitems, &ntuplen);
1944
1945 tuplesort_putgintuple(state->bs_sortstate, ntup, ntuplen);
1946 state->bs_numtuples++;
1947
1948 pfree(ntup);
1949
1950 /* discard the existing data */
1951 GinBufferReset(buffer);
1952 }
1953
1954 /* relase all the memory */
1955 GinBufferFree(buffer);
1956
1957 tuplesort_end(worker_sort);
1958}
#define PROGRESS_GIN_PHASE_MERGE_1
Definition: gin.h:48
#define PROGRESS_GIN_PHASE_PERFORMSORT_1
Definition: gin.h:47
static GinTuple * _gin_build_tuple(OffsetNumber attrnum, unsigned char category, Datum key, int16 typlen, bool typbyval, ItemPointerData *items, uint32 nitems, Size *len)
Definition: gininsert.c:2207
int16 typlen
Definition: gininsert.c:1159
bool typbyval
Definition: gininsert.c:1160
void tuplesort_putgintuple(Tuplesortstate *state, GinTuple *tuple, Size size)

References _gin_build_tuple(), Assert(), AssertCheckItemPointers(), GinBuffer::attnum, GinBuffer::category, CHECK_FOR_INTERRUPTS, GinBufferCanAddKey(), GinBufferFree(), GinBufferInit(), GinBufferIsEmpty(), GinBufferReset(), GinBufferShouldTrim(), GinBufferStoreTuple(), GinBufferTrim(), GinBuffer::items, GinBuffer::key, GinBuffer::nfrozen, GinBuffer::nitems, pfree(), pgstat_progress_update_param(), progress, PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_GIN_PHASE_MERGE_1, PROGRESS_GIN_PHASE_PERFORMSORT_1, tuplesort_end(), tuplesort_getgintuple(), tuplesort_performsort(), tuplesort_putgintuple(), GinBuffer::typbyval, and GinBuffer::typlen.

Referenced by _gin_parallel_scan_and_build().

◆ addItemPointersToLeafTuple()

static IndexTuple addItemPointersToLeafTuple ( GinState ginstate,
IndexTuple  old,
ItemPointerData items,
uint32  nitem,
GinStatsData buildStats,
Buffer  buffer 
)
static

Definition at line 209 of file gininsert.c.

213{
215 Datum key;
216 GinNullCategory category;
217 IndexTuple res;
218 ItemPointerData *newItems,
219 *oldItems;
220 int oldNPosting,
221 newNPosting,
222 nwritten;
223 GinPostingList *compressedList;
224
226
227 attnum = gintuple_get_attrnum(ginstate, old);
228 key = gintuple_get_key(ginstate, old, &category);
229
230 /* merge the old and new posting lists */
231 oldItems = ginReadTuple(ginstate, attnum, old, &oldNPosting);
232
233 newItems = ginMergeItemPointers(items, nitem,
234 oldItems, oldNPosting,
235 &newNPosting);
236
237 /* Compress the posting list, and try to a build tuple with room for it */
238 res = NULL;
239 compressedList = ginCompressPostingList(newItems, newNPosting, GinMaxItemSize, &nwritten);
240 if (nwritten == newNPosting)
241 {
242 res = GinFormTuple(ginstate, attnum, key, category,
243 (char *) compressedList,
244 SizeOfGinPostingList(compressedList),
245 newNPosting,
246 false);
247 }
248
249 pfree(newItems);
250 pfree(compressedList);
251
252 if (!res)
253 {
254 /* posting list would be too big, convert to posting tree */
255 BlockNumber postingRoot;
256
257 /*
258 * Initialize posting tree with the old tuple's posting list. It's
259 * surely small enough to fit on one posting-tree page, and should
260 * already be in order with no duplicates.
261 */
262 postingRoot = createPostingTree(ginstate->index,
263 oldItems,
264 oldNPosting,
265 buildStats,
266 buffer);
267
268 /* Now insert the TIDs-to-be-added into the posting tree */
269 ginInsertItemPointers(ginstate->index, postingRoot,
270 items, nitem,
271 buildStats);
272
273 /* And build a new posting-tree-only result tuple */
274 res = GinFormTuple(ginstate, attnum, key, category, NULL, 0, 0, true);
275 GinSetPostingTree(res, postingRoot);
276 }
277 pfree(oldItems);
278
279 return res;
280}
uint32 BlockNumber
Definition: block.h:31
#define GinIsPostingTree(itup)
Definition: ginblock.h:231
signed char GinNullCategory
Definition: ginblock.h:206
#define GinSetPostingTree(itup, blkno)
Definition: ginblock.h:232
#define GinMaxItemSize
Definition: ginblock.h:248
BlockNumber createPostingTree(Relation index, ItemPointerData *items, uint32 nitems, GinStatsData *buildStats, Buffer entrybuffer)
Definition: gindatapage.c:1775
void ginInsertItemPointers(Relation index, BlockNumber rootBlkno, ItemPointerData *items, uint32 nitem, GinStatsData *buildStats)
Definition: gindatapage.c:1908
ItemPointer ginReadTuple(GinState *ginstate, OffsetNumber attnum, IndexTuple itup, int *nitems)
Definition: ginentrypage.c:162
IndexTuple GinFormTuple(GinState *ginstate, OffsetNumber attnum, Datum key, GinNullCategory category, Pointer data, Size dataSize, int nipd, bool errorTooBig)
Definition: ginentrypage.c:44
ItemPointer ginMergeItemPointers(ItemPointerData *a, uint32 na, ItemPointerData *b, uint32 nb, int *nmerged)
OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple)
Definition: ginutil.c:231
Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple, GinNullCategory *category)
Definition: ginutil.c:264
uint16 OffsetNumber
Definition: off.h:24
int16 attnum
Definition: pg_attribute.h:74
Relation index
Definition: gin_private.h:60

References Assert(), attnum, createPostingTree(), ginCompressPostingList(), GinFormTuple(), ginInsertItemPointers(), GinIsPostingTree, GinMaxItemSize, ginMergeItemPointers(), ginReadTuple(), GinSetPostingTree, gintuple_get_attrnum(), gintuple_get_key(), GinState::index, items, sort-test::key, pfree(), and SizeOfGinPostingList.

Referenced by ginEntryInsert().

◆ AssertCheckGinBuffer()

static void AssertCheckGinBuffer ( GinBuffer buffer)
static

Definition at line 1204 of file gininsert.c.

1205{
1206#ifdef USE_ASSERT_CHECKING
1207 /* if we have any items, the array must exist */
1208 Assert(!((buffer->nitems > 0) && (buffer->items == NULL)));
1209
1210 /*
1211 * The buffer may be empty, in which case we must not call the check of
1212 * item pointers, because that assumes non-emptiness.
1213 */
1214 if (buffer->nitems == 0)
1215 return;
1216
1217 /* Make sure the item pointers are valid and sorted. */
1219#endif
1220}

References Assert(), AssertCheckItemPointers(), GinBuffer::items, and GinBuffer::nitems.

Referenced by GinBufferKeyEquals(), and GinBufferStoreTuple().

◆ AssertCheckItemPointers()

static void AssertCheckItemPointers ( GinBuffer buffer)
static

Definition at line 1177 of file gininsert.c.

1178{
1179#ifdef USE_ASSERT_CHECKING
1180 /* we should not have a buffer with no TIDs to sort */
1181 Assert(buffer->items != NULL);
1182 Assert(buffer->nitems > 0);
1183
1184 for (int i = 0; i < buffer->nitems; i++)
1185 {
1186 Assert(ItemPointerIsValid(&buffer->items[i]));
1187
1188 /* don't check ordering for the first TID item */
1189 if (i == 0)
1190 continue;
1191
1192 Assert(ItemPointerCompare(&buffer->items[i - 1], &buffer->items[i]) < 0);
1193 }
1194#endif
1195}
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition: itemptr.h:83

References Assert(), i, ItemPointerCompare(), ItemPointerIsValid(), GinBuffer::items, and GinBuffer::nitems.

Referenced by _gin_parallel_merge(), _gin_process_worker_data(), AssertCheckGinBuffer(), and GinBufferStoreTuple().

◆ buildFreshLeafTuple()

static IndexTuple buildFreshLeafTuple ( GinState ginstate,
OffsetNumber  attnum,
Datum  key,
GinNullCategory  category,
ItemPointerData items,
uint32  nitem,
GinStatsData buildStats,
Buffer  buffer 
)
static

Definition at line 291 of file gininsert.c.

295{
296 IndexTuple res = NULL;
297 GinPostingList *compressedList;
298 int nwritten;
299
300 /* try to build a posting list tuple with all the items */
301 compressedList = ginCompressPostingList(items, nitem, GinMaxItemSize, &nwritten);
302 if (nwritten == nitem)
303 {
304 res = GinFormTuple(ginstate, attnum, key, category,
305 (char *) compressedList,
306 SizeOfGinPostingList(compressedList),
307 nitem, false);
308 }
309 pfree(compressedList);
310
311 if (!res)
312 {
313 /* posting list would be too big, build posting tree */
314 BlockNumber postingRoot;
315
316 /*
317 * Build posting-tree-only result tuple. We do this first so as to
318 * fail quickly if the key is too big.
319 */
320 res = GinFormTuple(ginstate, attnum, key, category, NULL, 0, 0, true);
321
322 /*
323 * Initialize a new posting tree with the TIDs.
324 */
325 postingRoot = createPostingTree(ginstate->index, items, nitem,
326 buildStats, buffer);
327
328 /* And save the root link in the result tuple */
329 GinSetPostingTree(res, postingRoot);
330 }
331
332 return res;
333}

References attnum, createPostingTree(), ginCompressPostingList(), GinFormTuple(), GinMaxItemSize, GinSetPostingTree, GinState::index, items, sort-test::key, pfree(), and SizeOfGinPostingList.

Referenced by ginEntryInsert().

◆ GinBufferCanAddKey()

static bool GinBufferCanAddKey ( GinBuffer buffer,
GinTuple tup 
)
static

Definition at line 1595 of file gininsert.c.

1596{
1597 /* empty buffer can accept data for any key */
1598 if (GinBufferIsEmpty(buffer))
1599 return true;
1600
1601 /* otherwise just data for the same key */
1602 return GinBufferKeyEquals(buffer, tup);
1603}
static bool GinBufferKeyEquals(GinBuffer *buffer, GinTuple *tup)
Definition: gininsert.c:1317

References GinBufferIsEmpty(), and GinBufferKeyEquals().

Referenced by _gin_parallel_merge(), and _gin_process_worker_data().

◆ GinBufferFree()

static void GinBufferFree ( GinBuffer buffer)
static

Definition at line 1575 of file gininsert.c.

1576{
1577 if (buffer->items)
1578 pfree(buffer->items);
1579
1580 /* release byref values, do nothing for by-val ones */
1581 if (!GinBufferIsEmpty(buffer) &&
1582 (buffer->category == GIN_CAT_NORM_KEY) && !buffer->typbyval)
1583 pfree(DatumGetPointer(buffer->key));
1584
1585 pfree(buffer);
1586}

References GinBuffer::category, DatumGetPointer(), GIN_CAT_NORM_KEY, GinBufferIsEmpty(), GinBuffer::items, GinBuffer::key, pfree(), and GinBuffer::typbyval.

Referenced by _gin_parallel_merge(), and _gin_process_worker_data().

◆ GinBufferInit()

static GinBuffer * GinBufferInit ( Relation  index)
static

Definition at line 1233 of file gininsert.c.

1234{
1235 GinBuffer *buffer = palloc0(sizeof(GinBuffer));
1236 int i,
1237 nKeys;
1239
1240 /*
1241 * How many items can we fit into the memory limit? We don't want to end
1242 * with too many TIDs. and 64kB seems more than enough. But maybe this
1243 * should be tied to maintenance_work_mem or something like that?
1244 */
1245 buffer->maxitems = (64 * 1024L) / sizeof(ItemPointerData);
1246
1248
1249 buffer->ssup = palloc0(sizeof(SortSupportData) * nKeys);
1250
1251 /*
1252 * Lookup ordering operator for the index key data type, and initialize
1253 * the sort support function.
1254 */
1255 for (i = 0; i < nKeys; i++)
1256 {
1257 Oid cmpFunc;
1258 SortSupport sortKey = &buffer->ssup[i];
1259 Form_pg_attribute att = TupleDescAttr(desc, i);
1260
1261 sortKey->ssup_cxt = CurrentMemoryContext;
1262 sortKey->ssup_collation = index->rd_indcollation[i];
1263
1264 if (!OidIsValid(sortKey->ssup_collation))
1265 sortKey->ssup_collation = DEFAULT_COLLATION_OID;
1266
1267 sortKey->ssup_nulls_first = false;
1268 sortKey->ssup_attno = i + 1;
1269 sortKey->abbreviate = false;
1270
1271 Assert(sortKey->ssup_attno != 0);
1272
1273 /*
1274 * If the compare proc isn't specified in the opclass definition, look
1275 * up the index key type's default btree comparator.
1276 */
1277 cmpFunc = index_getprocid(index, i + 1, GIN_COMPARE_PROC);
1278 if (cmpFunc == InvalidOid)
1279 {
1280 TypeCacheEntry *typentry;
1281
1282 typentry = lookup_type_cache(att->atttypid,
1284 if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
1285 ereport(ERROR,
1286 (errcode(ERRCODE_UNDEFINED_FUNCTION),
1287 errmsg("could not identify a comparison function for type %s",
1288 format_type_be(att->atttypid))));
1289
1290 cmpFunc = typentry->cmp_proc_finfo.fn_oid;
1291 }
1292
1293 PrepareSortSupportComparisonShim(cmpFunc, sortKey);
1294 }
1295
1296 return buffer;
1297}
#define OidIsValid(objectId)
Definition: c.h:774
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define ereport(elevel,...)
Definition: elog.h:150
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
#define GIN_COMPARE_PROC
Definition: gin.h:24
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
Definition: indexam.c:883
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:202
#define InvalidOid
Definition: postgres_ext.h:37
unsigned int Oid
Definition: postgres_ext.h:32
#define RelationGetDescr(relation)
Definition: rel.h:540
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:533
void PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup)
Definition: sortsupport.c:68
Oid fn_oid
Definition: fmgr.h:59
int maxitems
Definition: gininsert.c:1163
SortSupport ssup
Definition: gininsert.c:1168
AttrNumber ssup_attno
Definition: sortsupport.h:81
bool ssup_nulls_first
Definition: sortsupport.h:75
MemoryContext ssup_cxt
Definition: sortsupport.h:66
FmgrInfo cmp_proc_finfo
Definition: typcache.h:77
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:160
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:386
#define TYPECACHE_CMP_PROC_FINFO
Definition: typcache.h:144

References SortSupportData::abbreviate, Assert(), TypeCacheEntry::cmp_proc_finfo, CurrentMemoryContext, ereport, errcode(), errmsg(), ERROR, FmgrInfo::fn_oid, format_type_be(), GIN_COMPARE_PROC, i, index_getprocid(), IndexRelationGetNumberOfKeyAttributes, InvalidOid, lookup_type_cache(), GinBuffer::maxitems, OidIsValid, palloc0(), PrepareSortSupportComparisonShim(), RelationGetDescr, GinBuffer::ssup, SortSupportData::ssup_attno, SortSupportData::ssup_collation, SortSupportData::ssup_cxt, SortSupportData::ssup_nulls_first, TupleDescAttr(), and TYPECACHE_CMP_PROC_FINFO.

Referenced by _gin_parallel_merge(), and _gin_process_worker_data().

◆ GinBufferIsEmpty()

static bool GinBufferIsEmpty ( GinBuffer buffer)
static

Definition at line 1301 of file gininsert.c.

1302{
1303 return (buffer->nitems == 0);
1304}

References GinBuffer::nitems.

Referenced by _gin_parallel_merge(), _gin_process_worker_data(), GinBufferCanAddKey(), GinBufferFree(), GinBufferReset(), and GinBufferStoreTuple().

◆ GinBufferKeyEquals()

static bool GinBufferKeyEquals ( GinBuffer buffer,
GinTuple tup 
)
static

Definition at line 1317 of file gininsert.c.

1318{
1319 int r;
1320 Datum tupkey;
1321
1322 AssertCheckGinBuffer(buffer);
1323
1324 if (tup->attrnum != buffer->attnum)
1325 return false;
1326
1327 /* same attribute should have the same type info */
1328 Assert(tup->typbyval == buffer->typbyval);
1329 Assert(tup->typlen == buffer->typlen);
1330
1331 if (tup->category != buffer->category)
1332 return false;
1333
1334 /*
1335 * For NULL/empty keys, this means equality, for normal keys we need to
1336 * compare the actual key value.
1337 */
1338 if (buffer->category != GIN_CAT_NORM_KEY)
1339 return true;
1340
1341 /*
1342 * For the tuple, get either the first sizeof(Datum) bytes for byval
1343 * types, or a pointer to the beginning of the data array.
1344 */
1345 tupkey = (buffer->typbyval) ? *(Datum *) tup->data : PointerGetDatum(tup->data);
1346
1347 r = ApplySortComparator(buffer->key, false,
1348 tupkey, false,
1349 &buffer->ssup[buffer->attnum - 1]);
1350
1351 return (r == 0);
1352}
static void AssertCheckGinBuffer(GinBuffer *buffer)
Definition: gininsert.c:1204

References ApplySortComparator(), Assert(), AssertCheckGinBuffer(), GinBuffer::attnum, GinTuple::attrnum, GinBuffer::category, GinTuple::category, GinTuple::data, GIN_CAT_NORM_KEY, GinBuffer::key, PointerGetDatum(), GinBuffer::ssup, GinBuffer::typbyval, GinTuple::typbyval, GinBuffer::typlen, and GinTuple::typlen.

Referenced by GinBufferCanAddKey().

◆ GinBufferReset()

static void GinBufferReset ( GinBuffer buffer)
static

Definition at line 1529 of file gininsert.c.

1530{
1531 Assert(!GinBufferIsEmpty(buffer));
1532
1533 /* release byref values, do nothing for by-val ones */
1534 if ((buffer->category == GIN_CAT_NORM_KEY) && !buffer->typbyval)
1535 pfree(DatumGetPointer(buffer->key));
1536
1537 /*
1538 * Not required, but makes it more likely to trigger NULL dereference if
1539 * using the value incorrectly, etc.
1540 */
1541 buffer->key = (Datum) 0;
1542
1543 buffer->attnum = 0;
1544 buffer->category = 0;
1545 buffer->keylen = 0;
1546 buffer->nitems = 0;
1547 buffer->nfrozen = 0;
1548
1549 buffer->typlen = 0;
1550 buffer->typbyval = 0;
1551}
Size keylen
Definition: gininsert.c:1156

References Assert(), GinBuffer::attnum, GinBuffer::category, DatumGetPointer(), GIN_CAT_NORM_KEY, GinBufferIsEmpty(), GinBuffer::key, GinBuffer::keylen, GinBuffer::nfrozen, GinBuffer::nitems, pfree(), GinBuffer::typbyval, and GinBuffer::typlen.

Referenced by _gin_parallel_merge(), and _gin_process_worker_data().

◆ GinBufferShouldTrim()

static bool GinBufferShouldTrim ( GinBuffer buffer,
GinTuple tup 
)
static

Definition at line 1379 of file gininsert.c.

1380{
1381 /* not enough TIDs to trim (1024 is somewhat arbitrary number) */
1382 if (buffer->nfrozen < 1024)
1383 return false;
1384
1385 /* no need to trim if we have not hit the memory limit yet */
1386 if ((buffer->nitems + tup->nitems) < buffer->maxitems)
1387 return false;
1388
1389 /*
1390 * OK, we have enough frozen TIDs to flush, and we have hit the memory
1391 * limit, so it's time to write it out.
1392 */
1393 return true;
1394}

References GinBuffer::maxitems, GinBuffer::nfrozen, GinBuffer::nitems, and GinTuple::nitems.

Referenced by _gin_parallel_merge(), and _gin_process_worker_data().

◆ GinBufferStoreTuple()

static void GinBufferStoreTuple ( GinBuffer buffer,
GinTuple tup 
)
static

Definition at line 1420 of file gininsert.c.

1421{
1423 Datum key;
1424
1425 AssertCheckGinBuffer(buffer);
1426
1429
1430 /* if the buffer is empty, set the fields (and copy the key) */
1431 if (GinBufferIsEmpty(buffer))
1432 {
1433 buffer->category = tup->category;
1434 buffer->keylen = tup->keylen;
1435 buffer->attnum = tup->attrnum;
1436
1437 buffer->typlen = tup->typlen;
1438 buffer->typbyval = tup->typbyval;
1439
1440 if (tup->category == GIN_CAT_NORM_KEY)
1441 buffer->key = datumCopy(key, buffer->typbyval, buffer->typlen);
1442 else
1443 buffer->key = (Datum) 0;
1444 }
1445
1446 /*
1447 * Try freeze TIDs at the beginning of the list, i.e. exclude them from
1448 * the mergesort. We can do that with TIDs before the first TID in the new
1449 * tuple we're about to add into the buffer.
1450 *
1451 * We do this incrementally when adding data into the in-memory buffer,
1452 * and not later (e.g. when hitting a memory limit), because it allows us
1453 * to skip the frozen data during the mergesort, making it cheaper.
1454 */
1455
1456 /*
1457 * Check if the last TID in the current list is frozen. This is the case
1458 * when merging non-overlapping lists, e.g. in each parallel worker.
1459 */
1460 if ((buffer->nitems > 0) &&
1461 (ItemPointerCompare(&buffer->items[buffer->nitems - 1],
1462 GinTupleGetFirst(tup)) == 0))
1463 buffer->nfrozen = buffer->nitems;
1464
1465 /*
1466 * Now find the last TID we know to be frozen, i.e. the last TID right
1467 * before the new GIN tuple.
1468 *
1469 * Start with the first not-yet-frozen tuple, and walk until we find the
1470 * first TID that's higher. If we already know the whole list is frozen
1471 * (i.e. nfrozen == nitems), this does nothing.
1472 *
1473 * XXX This might do a binary search for sufficiently long lists, but it
1474 * does not seem worth the complexity. Overlapping lists should be rare
1475 * common, TID comparisons are cheap, and we should quickly freeze most of
1476 * the list.
1477 */
1478 for (int i = buffer->nfrozen; i < buffer->nitems; i++)
1479 {
1480 /* Is the TID after the first TID of the new tuple? Can't freeze. */
1481 if (ItemPointerCompare(&buffer->items[i],
1482 GinTupleGetFirst(tup)) > 0)
1483 break;
1484
1485 buffer->nfrozen++;
1486 }
1487
1488 /* add the new TIDs into the buffer, combine using merge-sort */
1489 {
1490 int nnew;
1491 ItemPointer new;
1492
1493 /*
1494 * Resize the array - we do this first, because we'll dereference the
1495 * first unfrozen TID, which would fail if the array is NULL. We'll
1496 * still pass 0 as number of elements in that array though.
1497 */
1498 if (buffer->items == NULL)
1499 buffer->items = palloc((buffer->nitems + tup->nitems) * sizeof(ItemPointerData));
1500 else
1501 buffer->items = repalloc(buffer->items,
1502 (buffer->nitems + tup->nitems) * sizeof(ItemPointerData));
1503
1504 new = ginMergeItemPointers(&buffer->items[buffer->nfrozen], /* first unfrozen */
1505 (buffer->nitems - buffer->nfrozen), /* num of unfrozen */
1506 items, tup->nitems, &nnew);
1507
1508 Assert(nnew == (tup->nitems + (buffer->nitems - buffer->nfrozen)));
1509
1510 memcpy(&buffer->items[buffer->nfrozen], new,
1511 nnew * sizeof(ItemPointerData));
1512
1513 pfree(new);
1514
1515 buffer->nitems += tup->nitems;
1516
1518 }
1519
1520 /* free the decompressed TID list */
1521 pfree(items);
1522}
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:132
static ItemPointer _gin_parse_tuple_items(GinTuple *a)
Definition: gininsert.c:2377
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1610

References _gin_parse_tuple_items(), _gin_parse_tuple_key(), Assert(), AssertCheckGinBuffer(), AssertCheckItemPointers(), GinBuffer::attnum, GinTuple::attrnum, GinBuffer::category, GinTuple::category, datumCopy(), GIN_CAT_NORM_KEY, GinBufferIsEmpty(), ginMergeItemPointers(), GinTupleGetFirst(), i, ItemPointerCompare(), GinBuffer::items, items, GinBuffer::key, sort-test::key, GinBuffer::keylen, GinTuple::keylen, GinBuffer::nfrozen, GinBuffer::nitems, GinTuple::nitems, nitems, palloc(), pfree(), repalloc(), GinBuffer::typbyval, GinTuple::typbyval, GinBuffer::typlen, and GinTuple::typlen.

Referenced by _gin_parallel_merge(), and _gin_process_worker_data().

◆ GinBufferTrim()

static void GinBufferTrim ( GinBuffer buffer)
static

Definition at line 1559 of file gininsert.c.

1560{
1561 Assert((buffer->nfrozen > 0) && (buffer->nfrozen <= buffer->nitems));
1562
1563 memmove(&buffer->items[0], &buffer->items[buffer->nfrozen],
1564 sizeof(ItemPointerData) * (buffer->nitems - buffer->nfrozen));
1565
1566 buffer->nitems -= buffer->nfrozen;
1567 buffer->nfrozen = 0;
1568}

References Assert(), GinBuffer::items, GinBuffer::nfrozen, and GinBuffer::nitems.

Referenced by _gin_parallel_merge(), and _gin_process_worker_data().

◆ ginbuild()

IndexBuildResult * ginbuild ( Relation  heap,
Relation  index,
IndexInfo indexInfo 
)

Definition at line 585 of file gininsert.c.

586{
587 IndexBuildResult *result;
588 double reltuples;
589 GinBuildState buildstate;
590 GinBuildState *state = &buildstate;
591 Buffer RootBuffer,
592 MetaBuffer;
594 Datum key;
595 GinNullCategory category;
596 uint32 nlist;
597 MemoryContext oldCtx;
599
601 elog(ERROR, "index \"%s\" already contains data",
603
604 initGinState(&buildstate.ginstate, index);
605 buildstate.indtuples = 0;
606 memset(&buildstate.buildStats, 0, sizeof(GinStatsData));
607
608 /* Initialize fields for parallel build too. */
609 buildstate.bs_numtuples = 0;
610 buildstate.bs_reltuples = 0;
611 buildstate.bs_leader = NULL;
612 memset(&buildstate.tid, 0, sizeof(ItemPointerData));
613
614 /* initialize the meta page */
615 MetaBuffer = GinNewBuffer(index);
616
617 /* initialize the root page */
618 RootBuffer = GinNewBuffer(index);
619
621 GinInitMetabuffer(MetaBuffer);
622 MarkBufferDirty(MetaBuffer);
623 GinInitBuffer(RootBuffer, GIN_LEAF);
624 MarkBufferDirty(RootBuffer);
625
626
627 UnlockReleaseBuffer(MetaBuffer);
628 UnlockReleaseBuffer(RootBuffer);
630
631 /* count the root as first entry page */
632 buildstate.buildStats.nEntryPages++;
633
634 /*
635 * create a temporary memory context that is used to hold data not yet
636 * dumped out to the index
637 */
639 "Gin build temporary context",
641
642 /*
643 * create a temporary memory context that is used for calling
644 * ginExtractEntries(), and can be reset after each tuple
645 */
647 "Gin build temporary context for user-defined function",
649
650 buildstate.accum.ginstate = &buildstate.ginstate;
651 ginInitBA(&buildstate.accum);
652
653 /* Report table scan phase started */
656
657 /*
658 * Attempt to launch parallel worker scan when required
659 *
660 * XXX plan_create_index_workers makes the number of workers dependent on
661 * maintenance_work_mem, requiring 32MB for each worker. For GIN that's
662 * reasonable too, because we sort the data just like btree. It does
663 * ignore the memory used to accumulate data in memory (set by work_mem),
664 * but there is no way to communicate that to plan_create_index_workers.
665 */
666 if (indexInfo->ii_ParallelWorkers > 0)
667 _gin_begin_parallel(state, heap, index, indexInfo->ii_Concurrent,
668 indexInfo->ii_ParallelWorkers);
669
670 /*
671 * If parallel build requested and at least one worker process was
672 * successfully launched, set up coordination state, wait for workers to
673 * complete. Then read all tuples from the shared tuplesort and insert
674 * them into the index.
675 *
676 * In serial mode, simply scan the table and build the index one index
677 * tuple at a time.
678 */
679 if (state->bs_leader)
680 {
681 SortCoordinate coordinate;
682
683 coordinate = (SortCoordinate) palloc0(sizeof(SortCoordinateData));
684 coordinate->isWorker = false;
685 coordinate->nParticipants =
686 state->bs_leader->nparticipanttuplesorts;
687 coordinate->sharedsort = state->bs_leader->sharedsort;
688
689 /*
690 * Begin leader tuplesort.
691 *
692 * In cases where parallelism is involved, the leader receives the
693 * same share of maintenance_work_mem as a serial sort (it is
694 * generally treated in the same way as a serial sort once we return).
695 * Parallel worker Tuplesortstates will have received only a fraction
696 * of maintenance_work_mem, though.
697 *
698 * We rely on the lifetime of the Leader Tuplesortstate almost not
699 * overlapping with any worker Tuplesortstate's lifetime. There may
700 * be some small overlap, but that's okay because we rely on leader
701 * Tuplesortstate only allocating a small, fixed amount of memory
702 * here. When its tuplesort_performsort() is called (by our caller),
703 * and significant amounts of memory are likely to be used, all
704 * workers must have already freed almost all memory held by their
705 * Tuplesortstates (they are about to go away completely, too). The
706 * overall effect is that maintenance_work_mem always represents an
707 * absolute high watermark on the amount of memory used by a CREATE
708 * INDEX operation, regardless of the use of parallelism or any other
709 * factor.
710 */
711 state->bs_sortstate =
713 maintenance_work_mem, coordinate,
715
716 /* scan the relation in parallel and merge per-worker results */
717 reltuples = _gin_parallel_merge(state);
718
719 _gin_end_parallel(state->bs_leader, state);
720 }
721 else /* no parallel index build */
722 {
723 /*
724 * Do the heap scan. We disallow sync scan here because
725 * dataPlaceToPage prefers to receive tuples in TID order.
726 */
727 reltuples = table_index_build_scan(heap, index, indexInfo, false, true,
728 ginBuildCallback, &buildstate, NULL);
729
730 /* dump remaining entries to the index */
731 oldCtx = MemoryContextSwitchTo(buildstate.tmpCtx);
732 ginBeginBAScan(&buildstate.accum);
733 while ((list = ginGetBAEntry(&buildstate.accum,
734 &attnum, &key, &category, &nlist)) != NULL)
735 {
736 /* there could be many entries, so be willing to abort here */
738 ginEntryInsert(&buildstate.ginstate, attnum, key, category,
739 list, nlist, &buildstate.buildStats);
740 }
741 MemoryContextSwitchTo(oldCtx);
742 }
743
744 MemoryContextDelete(buildstate.funcCtx);
745 MemoryContextDelete(buildstate.tmpCtx);
746
747 /*
748 * Update metapage stats
749 */
751 ginUpdateStats(index, &buildstate.buildStats, true);
752
753 /*
754 * We didn't write WAL records as we built the index, so if WAL-logging is
755 * required, write all pages to the WAL now.
756 */
758 {
761 true);
762 }
763
764 /*
765 * Return statistics
766 */
767 result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
768
769 result->heap_tuples = reltuples;
770 result->index_tuples = buildstate.indtuples;
771
772 return result;
773}
int Buffer
Definition: buf.h:23
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:5355
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:2921
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:283
uint32_t uint32
Definition: c.h:538
#define PROGRESS_GIN_PHASE_INDEXBUILD_TABLESCAN
Definition: gin.h:46
#define GIN_LEAF
Definition: ginblock.h:42
void ginBeginBAScan(BuildAccumulator *accum)
Definition: ginbulk.c:257
ItemPointerData * ginGetBAEntry(BuildAccumulator *accum, OffsetNumber *attnum, Datum *key, GinNullCategory *category, uint32 *n)
Definition: ginbulk.c:268
static void _gin_begin_parallel(GinBuildState *buildstate, Relation heap, Relation index, bool isconcurrent, int request)
Definition: gininsert.c:900
static double _gin_parallel_merge(GinBuildState *state)
Definition: gininsert.c:1617
static void ginBuildCallback(Relation index, ItemPointer tid, Datum *values, bool *isnull, bool tupleIsAlive, void *state)
Definition: gininsert.c:444
Buffer GinNewBuffer(Relation index)
Definition: ginutil.c:305
void GinInitBuffer(Buffer b, uint32 f)
Definition: ginutil.c:355
void GinInitMetabuffer(Buffer b)
Definition: ginutil.c:361
void ginUpdateStats(Relation index, const GinStatsData *stats, bool is_build)
Definition: ginutil.c:655
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:469
#define START_CRIT_SECTION()
Definition: miscadmin.h:149
#define END_CRIT_SECTION()
Definition: miscadmin.h:151
#define RelationGetRelationName(relation)
Definition: rel.h:548
#define RelationNeedsWAL(relation)
Definition: rel.h:637
@ MAIN_FORKNUM
Definition: relpath.h:58
double bs_reltuples
Definition: gininsert.c:159
double bs_numtuples
Definition: gininsert.c:158
BlockNumber nEntryPages
Definition: gin.h:59
BlockNumber nTotalPages
Definition: gin.h:58
double heap_tuples
Definition: genam.h:59
double index_tuples
Definition: genam.h:60
int ii_ParallelWorkers
Definition: execnodes.h:218
struct SortCoordinateData * SortCoordinate
Definition: tuplesort.h:62
void log_newpage_range(Relation rel, ForkNumber forknum, BlockNumber startblk, BlockNumber endblk, bool page_std)
Definition: xloginsert.c:1282

References _gin_begin_parallel(), _gin_end_parallel(), _gin_parallel_merge(), GinBuildState::accum, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, attnum, GinBuildState::bs_leader, GinBuildState::bs_numtuples, GinBuildState::bs_reltuples, GinBuildState::buildStats, CHECK_FOR_INTERRUPTS, CurrentMemoryContext, elog, END_CRIT_SECTION, ERROR, GinBuildState::funcCtx, GIN_LEAF, ginBeginBAScan(), ginBuildCallback(), ginEntryInsert(), ginGetBAEntry(), ginInitBA(), GinInitBuffer(), GinInitMetabuffer(), GinNewBuffer(), GinBuildState::ginstate, BuildAccumulator::ginstate, ginUpdateStats(), IndexBuildResult::heap_tuples, IndexInfo::ii_Concurrent, IndexInfo::ii_ParallelWorkers, IndexBuildResult::index_tuples, GinBuildState::indtuples, initGinState(), SortCoordinateData::isWorker, sort-test::key, sort-test::list, log_newpage_range(), MAIN_FORKNUM, maintenance_work_mem, MarkBufferDirty(), MemoryContextDelete(), MemoryContextSwitchTo(), GinStatsData::nEntryPages, SortCoordinateData::nParticipants, GinStatsData::nTotalPages, palloc(), palloc0(), pgstat_progress_update_param(), PROGRESS_CREATEIDX_SUBPHASE, PROGRESS_GIN_PHASE_INDEXBUILD_TABLESCAN, RelationGetNumberOfBlocks, RelationGetRelationName, RelationNeedsWAL, SortCoordinateData::sharedsort, START_CRIT_SECTION, table_index_build_scan(), GinBuildState::tid, GinBuildState::tmpCtx, tuplesort_begin_index_gin(), TUPLESORT_NONE, and UnlockReleaseBuffer().

Referenced by ginhandler().

◆ ginBuildCallback()

static void ginBuildCallback ( Relation  index,
ItemPointer  tid,
Datum values,
bool *  isnull,
bool  tupleIsAlive,
void *  state 
)
static

Definition at line 444 of file gininsert.c.

446{
447 GinBuildState *buildstate = (GinBuildState *) state;
448 MemoryContext oldCtx;
449 int i;
450
451 oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
452
453 for (i = 0; i < buildstate->ginstate.origTupdesc->natts; i++)
454 ginHeapTupleBulkInsert(buildstate, (OffsetNumber) (i + 1),
455 values[i], isnull[i], tid);
456
457 /* If we've maxed out our available memory, dump everything to the index */
458 if (buildstate->accum.allocatedMemory >= maintenance_work_mem * (Size) 1024)
459 {
461 Datum key;
462 GinNullCategory category;
463 uint32 nlist;
465
466 ginBeginBAScan(&buildstate->accum);
467 while ((list = ginGetBAEntry(&buildstate->accum,
468 &attnum, &key, &category, &nlist)) != NULL)
469 {
470 /* there could be many entries, so be willing to abort here */
472 ginEntryInsert(&buildstate->ginstate, attnum, key, category,
473 list, nlist, &buildstate->buildStats);
474 }
475
476 MemoryContextReset(buildstate->tmpCtx);
477 ginInitBA(&buildstate->accum);
478 }
479
480 MemoryContextSwitchTo(oldCtx);
481}
static Datum values[MAXATTR]
Definition: bootstrap.c:153
static void ginHeapTupleBulkInsert(GinBuildState *buildstate, OffsetNumber attnum, Datum value, bool isNull, ItemPointer heapptr)
Definition: gininsert.c:420
TupleDesc origTupdesc
Definition: gin_private.h:74

References GinBuildState::accum, BuildAccumulator::allocatedMemory, attnum, GinBuildState::buildStats, CHECK_FOR_INTERRUPTS, ginBeginBAScan(), ginEntryInsert(), ginGetBAEntry(), ginHeapTupleBulkInsert(), ginInitBA(), GinBuildState::ginstate, i, sort-test::key, sort-test::list, maintenance_work_mem, MemoryContextReset(), MemoryContextSwitchTo(), TupleDescData::natts, GinState::origTupdesc, GinBuildState::tmpCtx, and values.

Referenced by ginbuild().

◆ ginBuildCallbackParallel()

static void ginBuildCallbackParallel ( Relation  index,
ItemPointer  tid,
Datum values,
bool *  isnull,
bool  tupleIsAlive,
void *  state 
)
static

Definition at line 537 of file gininsert.c.

539{
540 GinBuildState *buildstate = (GinBuildState *) state;
541 MemoryContext oldCtx;
542 int i;
543
544 oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
545
546 /*
547 * if scan wrapped around - flush accumulated entries and start anew
548 *
549 * With parallel scans, we don't have a guarantee the scan does not start
550 * half-way through the relation (serial builds disable sync scans and
551 * always start from block 0, parallel scans require allow_sync=true).
552 *
553 * Building the posting lists assumes the TIDs are monotonic and never go
554 * back, and the wrap around would break that. We handle that by detecting
555 * the wraparound, and flushing all entries. This means we'll later see
556 * two separate entries with non-overlapping TID lists (which can be
557 * combined by merge sort).
558 *
559 * To detect a wraparound, we remember the last TID seen by each worker
560 * (for any key). If the next TID seen by the worker is lower, the scan
561 * must have wrapped around.
562 */
563 if (ItemPointerCompare(tid, &buildstate->tid) < 0)
564 ginFlushBuildState(buildstate, index);
565
566 /* remember the TID we're about to process */
567 buildstate->tid = *tid;
568
569 for (i = 0; i < buildstate->ginstate.origTupdesc->natts; i++)
570 ginHeapTupleBulkInsert(buildstate, (OffsetNumber) (i + 1),
571 values[i], isnull[i], tid);
572
573 /*
574 * If we've maxed out our available memory, dump everything to the
575 * tuplesort. We use half the per-worker fraction of maintenance_work_mem,
576 * the other half is used for the tuplesort.
577 */
578 if (buildstate->accum.allocatedMemory >= buildstate->work_mem * (Size) 1024)
579 ginFlushBuildState(buildstate, index);
580
581 MemoryContextSwitchTo(oldCtx);
582}

References GinBuildState::accum, BuildAccumulator::allocatedMemory, ginFlushBuildState(), ginHeapTupleBulkInsert(), GinBuildState::ginstate, i, ItemPointerCompare(), MemoryContextSwitchTo(), TupleDescData::natts, GinState::origTupdesc, GinBuildState::tid, GinBuildState::tmpCtx, values, and GinBuildState::work_mem.

Referenced by _gin_parallel_scan_and_build().

◆ ginbuildempty()

void ginbuildempty ( Relation  index)

Definition at line 779 of file gininsert.c.

780{
781 Buffer RootBuffer,
782 MetaBuffer;
783
784 /* An empty GIN index has two pages. */
785 MetaBuffer = ExtendBufferedRel(BMR_REL(index), INIT_FORKNUM, NULL,
787 RootBuffer = ExtendBufferedRel(BMR_REL(index), INIT_FORKNUM, NULL,
789
790 /* Initialize and xlog metabuffer and root buffer. */
792 GinInitMetabuffer(MetaBuffer);
793 MarkBufferDirty(MetaBuffer);
794 log_newpage_buffer(MetaBuffer, true);
795 GinInitBuffer(RootBuffer, GIN_LEAF);
796 MarkBufferDirty(RootBuffer);
797 log_newpage_buffer(RootBuffer, false);
799
800 /* Unlock and release the buffers. */
801 UnlockReleaseBuffer(MetaBuffer);
802 UnlockReleaseBuffer(RootBuffer);
803}
Buffer ExtendBufferedRel(BufferManagerRelation bmr, ForkNumber forkNum, BufferAccessStrategy strategy, uint32 flags)
Definition: bufmgr.c:858
@ EB_SKIP_EXTENSION_LOCK
Definition: bufmgr.h:75
@ EB_LOCK_FIRST
Definition: bufmgr.h:87
#define BMR_REL(p_rel)
Definition: bufmgr.h:111
@ INIT_FORKNUM
Definition: relpath.h:61
XLogRecPtr log_newpage_buffer(Buffer buffer, bool page_std)
Definition: xloginsert.c:1249

References BMR_REL, EB_LOCK_FIRST, EB_SKIP_EXTENSION_LOCK, END_CRIT_SECTION, ExtendBufferedRel(), GIN_LEAF, GinInitBuffer(), GinInitMetabuffer(), INIT_FORKNUM, log_newpage_buffer(), MarkBufferDirty(), START_CRIT_SECTION, and UnlockReleaseBuffer().

Referenced by ginhandler().

◆ ginEntryInsert()

void ginEntryInsert ( GinState ginstate,
OffsetNumber  attnum,
Datum  key,
GinNullCategory  category,
ItemPointerData items,
uint32  nitem,
GinStatsData buildStats 
)

Definition at line 343 of file gininsert.c.

347{
348 GinBtreeData btree;
349 GinBtreeEntryInsertData insertdata;
350 GinBtreeStack *stack;
351 IndexTuple itup;
352 Page page;
353
354 insertdata.isDelete = false;
355
356 ginPrepareEntryScan(&btree, attnum, key, category, ginstate);
357 btree.isBuild = (buildStats != NULL);
358
359 stack = ginFindLeafPage(&btree, false, false);
360 page = BufferGetPage(stack->buffer);
361
362 if (btree.findItem(&btree, stack))
363 {
364 /* found pre-existing entry */
365 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stack->off));
366
367 if (GinIsPostingTree(itup))
368 {
369 /* add entries to existing posting tree */
370 BlockNumber rootPostingTree = GinGetPostingTree(itup);
371
372 /* release all stack */
374 freeGinBtreeStack(stack);
375
376 /* insert into posting tree */
377 ginInsertItemPointers(ginstate->index, rootPostingTree,
378 items, nitem,
379 buildStats);
380 return;
381 }
382
385 /* modify an existing leaf entry */
386 itup = addItemPointersToLeafTuple(ginstate, itup,
387 items, nitem, buildStats, stack->buffer);
388
389 insertdata.isDelete = true;
390 }
391 else
392 {
395 /* no match, so construct a new leaf entry */
396 itup = buildFreshLeafTuple(ginstate, attnum, key, category,
397 items, nitem, buildStats, stack->buffer);
398
399 /*
400 * nEntries counts leaf tuples, so increment it only when we make a
401 * new one.
402 */
403 if (buildStats)
404 buildStats->nEntries++;
405 }
406
407 /* Insert the new or modified leaf tuple */
408 insertdata.entry = itup;
409 ginInsertValue(&btree, stack, &insertdata, buildStats);
410 pfree(itup);
411}
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:4198
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:5572
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:417
static Item PageGetItem(const PageData *page, const ItemIdData *itemId)
Definition: bufpage.h:354
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:244
PageData * Page
Definition: bufpage.h:82
#define GIN_UNLOCK
Definition: gin_private.h:50
#define GinGetPostingTree(itup)
Definition: ginblock.h:233
void freeGinBtreeStack(GinBtreeStack *stack)
Definition: ginbtree.c:198
void ginInsertValue(GinBtree btree, GinBtreeStack *stack, void *insertdata, GinStatsData *buildStats)
Definition: ginbtree.c:816
GinBtreeStack * ginFindLeafPage(GinBtree btree, bool searchMode, bool rootConflictCheck)
Definition: ginbtree.c:83
void ginPrepareEntryScan(GinBtree btree, OffsetNumber attnum, Datum key, GinNullCategory category, GinState *ginstate)
Definition: ginentrypage.c:747
static IndexTuple addItemPointersToLeafTuple(GinState *ginstate, IndexTuple old, ItemPointerData *items, uint32 nitem, GinStatsData *buildStats, Buffer buffer)
Definition: gininsert.c:209
static IndexTuple buildFreshLeafTuple(GinState *ginstate, OffsetNumber attnum, Datum key, GinNullCategory category, ItemPointerData *items, uint32 nitem, GinStatsData *buildStats, Buffer buffer)
Definition: gininsert.c:291
IndexTupleData * IndexTuple
Definition: itup.h:53
void CheckForSerializableConflictIn(Relation relation, ItemPointer tid, BlockNumber blkno)
Definition: predicate.c:4336
bool(* findItem)(GinBtree, GinBtreeStack *)
Definition: gin_private.h:158
OffsetNumber off
Definition: gin_private.h:135
int64 nEntries
Definition: gin.h:61

References addItemPointersToLeafTuple(), attnum, GinBtreeStack::buffer, BufferGetBlockNumber(), BufferGetPage(), buildFreshLeafTuple(), CheckForSerializableConflictIn(), GinBtreeEntryInsertData::entry, GinBtreeData::findItem, freeGinBtreeStack(), GIN_UNLOCK, ginFindLeafPage(), GinGetPostingTree, ginInsertItemPointers(), ginInsertValue(), GinIsPostingTree, ginPrepareEntryScan(), GinState::index, GinBtreeData::isBuild, GinBtreeEntryInsertData::isDelete, items, sort-test::key, LockBuffer(), GinStatsData::nEntries, GinBtreeStack::off, PageGetItem(), PageGetItemId(), and pfree().

Referenced by _gin_parallel_merge(), ginbuild(), ginBuildCallback(), ginHeapTupleInsert(), and ginInsertCleanup().

◆ ginFlushBuildState()

static void ginFlushBuildState ( GinBuildState buildstate,
Relation  index 
)
static

Definition at line 488 of file gininsert.c.

489{
491 Datum key;
492 GinNullCategory category;
493 uint32 nlist;
496
497 ginBeginBAScan(&buildstate->accum);
498 while ((list = ginGetBAEntry(&buildstate->accum,
499 &attnum, &key, &category, &nlist)) != NULL)
500 {
501 /* information about the key */
502 Form_pg_attribute attr = TupleDescAttr(tdesc, (attnum - 1));
503
504 /* GIN tuple and tuple length */
505 GinTuple *tup;
506 Size tuplen;
507
508 /* there could be many entries, so be willing to abort here */
510
511 tup = _gin_build_tuple(attnum, category,
512 key, attr->attlen, attr->attbyval,
513 list, nlist, &tuplen);
514
515 tuplesort_putgintuple(buildstate->bs_worker_sort, tup, tuplen);
516
517 pfree(tup);
518 }
519
520 MemoryContextReset(buildstate->tmpCtx);
521 ginInitBA(&buildstate->accum);
522}
Tuplesortstate * bs_worker_sort
Definition: gininsert.c:174

References _gin_build_tuple(), GinBuildState::accum, attnum, GinBuildState::bs_worker_sort, CHECK_FOR_INTERRUPTS, ginBeginBAScan(), ginGetBAEntry(), ginInitBA(), sort-test::key, sort-test::list, MemoryContextReset(), pfree(), RelationGetDescr, GinBuildState::tmpCtx, TupleDescAttr(), and tuplesort_putgintuple().

Referenced by _gin_parallel_scan_and_build(), and ginBuildCallbackParallel().

◆ ginHeapTupleBulkInsert()

static void ginHeapTupleBulkInsert ( GinBuildState buildstate,
OffsetNumber  attnum,
Datum  value,
bool  isNull,
ItemPointer  heapptr 
)
static

Definition at line 420 of file gininsert.c.

423{
424 Datum *entries;
425 GinNullCategory *categories;
426 int32 nentries;
427 MemoryContext oldCtx;
428
429 oldCtx = MemoryContextSwitchTo(buildstate->funcCtx);
430 entries = ginExtractEntries(buildstate->accum.ginstate, attnum,
431 value, isNull,
432 &nentries, &categories);
433 MemoryContextSwitchTo(oldCtx);
434
435 ginInsertBAEntries(&buildstate->accum, heapptr, attnum,
436 entries, categories, nentries);
437
438 buildstate->indtuples += nentries;
439
440 MemoryContextReset(buildstate->funcCtx);
441}
int32_t int32
Definition: c.h:534
void ginInsertBAEntries(BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber attnum, Datum *entries, GinNullCategory *categories, int32 nentries)
Definition: ginbulk.c:210
Datum * ginExtractEntries(GinState *ginstate, OffsetNumber attnum, Datum value, bool isNull, int32 *nentries, GinNullCategory **categories)
Definition: ginutil.c:488
static struct @169 value

References GinBuildState::accum, attnum, GinBuildState::funcCtx, ginExtractEntries(), ginInsertBAEntries(), BuildAccumulator::ginstate, GinBuildState::indtuples, MemoryContextReset(), MemoryContextSwitchTo(), and value.

Referenced by ginBuildCallback(), and ginBuildCallbackParallel().

◆ ginHeapTupleInsert()

static void ginHeapTupleInsert ( GinState ginstate,
OffsetNumber  attnum,
Datum  value,
bool  isNull,
ItemPointer  item 
)
static

Definition at line 810 of file gininsert.c.

813{
814 Datum *entries;
815 GinNullCategory *categories;
816 int32 i,
817 nentries;
818
819 entries = ginExtractEntries(ginstate, attnum, value, isNull,
820 &nentries, &categories);
821
822 for (i = 0; i < nentries; i++)
823 ginEntryInsert(ginstate, attnum, entries[i], categories[i],
824 item, 1, NULL);
825}

References attnum, ginEntryInsert(), ginExtractEntries(), i, and value.

Referenced by gininsert().

◆ gininsert()

bool gininsert ( Relation  index,
Datum values,
bool *  isnull,
ItemPointer  ht_ctid,
Relation  heapRel,
IndexUniqueCheck  checkUnique,
bool  indexUnchanged,
IndexInfo indexInfo 
)

Definition at line 828 of file gininsert.c.

833{
834 GinState *ginstate = (GinState *) indexInfo->ii_AmCache;
835 MemoryContext oldCtx;
836 MemoryContext insertCtx;
837 int i;
838
839 /* Initialize GinState cache if first call in this statement */
840 if (ginstate == NULL)
841 {
842 oldCtx = MemoryContextSwitchTo(indexInfo->ii_Context);
843 ginstate = (GinState *) palloc(sizeof(GinState));
844 initGinState(ginstate, index);
845 indexInfo->ii_AmCache = ginstate;
846 MemoryContextSwitchTo(oldCtx);
847 }
848
850 "Gin insert temporary context",
852
853 oldCtx = MemoryContextSwitchTo(insertCtx);
854
856 {
857 GinTupleCollector collector;
858
859 memset(&collector, 0, sizeof(GinTupleCollector));
860
861 for (i = 0; i < ginstate->origTupdesc->natts; i++)
862 ginHeapTupleFastCollect(ginstate, &collector,
863 (OffsetNumber) (i + 1),
864 values[i], isnull[i],
865 ht_ctid);
866
867 ginHeapTupleFastInsert(ginstate, &collector);
868 }
869 else
870 {
871 for (i = 0; i < ginstate->origTupdesc->natts; i++)
872 ginHeapTupleInsert(ginstate, (OffsetNumber) (i + 1),
873 values[i], isnull[i],
874 ht_ctid);
875 }
876
877 MemoryContextSwitchTo(oldCtx);
878 MemoryContextDelete(insertCtx);
879
880 return false;
881}
#define GinGetUseFastUpdate(relation)
Definition: gin_private.h:35
void ginHeapTupleFastCollect(GinState *ginstate, GinTupleCollector *collector, OffsetNumber attnum, Datum value, bool isNull, ItemPointer ht_ctid)
Definition: ginfast.c:483
void ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
Definition: ginfast.c:219
static void ginHeapTupleInsert(GinState *ginstate, OffsetNumber attnum, Datum value, bool isNull, ItemPointer item)
Definition: gininsert.c:810
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
void * ii_AmCache
Definition: execnodes.h:223
MemoryContext ii_Context
Definition: execnodes.h:226

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, CurrentMemoryContext, GinGetUseFastUpdate, ginHeapTupleFastCollect(), ginHeapTupleFastInsert(), ginHeapTupleInsert(), i, if(), IndexInfo::ii_AmCache, IndexInfo::ii_Context, initGinState(), MemoryContextDelete(), MemoryContextSwitchTo(), TupleDescData::natts, GinState::origTupdesc, palloc(), and values.

Referenced by ginhandler().