~ubuntu-branches/debian/experimental/postgresql-11/experimental

« back to all changes in this revision

Viewing changes to src/backend/access/gin/gininsert.c

  • Committer: Package Import Robot
  • Author(s): Christoph Berg
  • Date: 2018-05-22 14:19:08 UTC
  • Revision ID: package-import@ubuntu.com-20180522141908-0oy9ujs1b5vrda74
Tags: upstream-11~beta1
ImportĀ upstreamĀ versionĀ 11~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-------------------------------------------------------------------------
 
2
 *
 
3
 * gininsert.c
 
4
 *        insert routines for the postgres inverted index access method.
 
5
 *
 
6
 *
 
7
 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
 
8
 * Portions Copyright (c) 1994, Regents of the University of California
 
9
 *
 
10
 * IDENTIFICATION
 
11
 *                      src/backend/access/gin/gininsert.c
 
12
 *-------------------------------------------------------------------------
 
13
 */
 
14
 
 
15
#include "postgres.h"
 
16
 
 
17
#include "access/gin_private.h"
 
18
#include "access/ginxlog.h"
 
19
#include "access/xloginsert.h"
 
20
#include "catalog/index.h"
 
21
#include "miscadmin.h"
 
22
#include "storage/bufmgr.h"
 
23
#include "storage/smgr.h"
 
24
#include "storage/indexfsm.h"
 
25
#include "storage/predicate.h"
 
26
#include "utils/memutils.h"
 
27
#include "utils/rel.h"
 
28
 
 
29
 
 
30
typedef struct
 
31
{
 
32
        GinState        ginstate;
 
33
        double          indtuples;
 
34
        GinStatsData buildStats;
 
35
        MemoryContext tmpCtx;
 
36
        MemoryContext funcCtx;
 
37
        BuildAccumulator accum;
 
38
} GinBuildState;
 
39
 
 
40
 
 
41
/*
 
42
 * Adds array of item pointers to tuple's posting list, or
 
43
 * creates posting tree and tuple pointing to tree in case
 
44
 * of not enough space.  Max size of tuple is defined in
 
45
 * GinFormTuple().  Returns a new, modified index tuple.
 
46
 * items[] must be in sorted order with no duplicates.
 
47
 */
 
48
static IndexTuple
 
49
addItemPointersToLeafTuple(GinState *ginstate,
 
50
                                                   IndexTuple old,
 
51
                                                   ItemPointerData *items, uint32 nitem,
 
52
                                                   GinStatsData *buildStats, Buffer buffer)
 
53
{
 
54
        OffsetNumber attnum;
 
55
        Datum           key;
 
56
        GinNullCategory category;
 
57
        IndexTuple      res;
 
58
        ItemPointerData *newItems,
 
59
                           *oldItems;
 
60
        int                     oldNPosting,
 
61
                                newNPosting;
 
62
        GinPostingList *compressedList;
 
63
 
 
64
        Assert(!GinIsPostingTree(old));
 
65
 
 
66
        attnum = gintuple_get_attrnum(ginstate, old);
 
67
        key = gintuple_get_key(ginstate, old, &category);
 
68
 
 
69
        /* merge the old and new posting lists */
 
70
        oldItems = ginReadTuple(ginstate, attnum, old, &oldNPosting);
 
71
 
 
72
        newItems = ginMergeItemPointers(items, nitem,
 
73
                                                                        oldItems, oldNPosting,
 
74
                                                                        &newNPosting);
 
75
 
 
76
        /* Compress the posting list, and try to a build tuple with room for it */
 
77
        res = NULL;
 
78
        compressedList = ginCompressPostingList(newItems, newNPosting, GinMaxItemSize,
 
79
                                                                                        NULL);
 
80
        pfree(newItems);
 
81
        if (compressedList)
 
82
        {
 
83
                res = GinFormTuple(ginstate, attnum, key, category,
 
84
                                                   (char *) compressedList,
 
85
                                                   SizeOfGinPostingList(compressedList),
 
86
                                                   newNPosting,
 
87
                                                   false);
 
88
                pfree(compressedList);
 
89
        }
 
90
        if (!res)
 
91
        {
 
92
                /* posting list would be too big, convert to posting tree */
 
93
                BlockNumber postingRoot;
 
94
 
 
95
                /*
 
96
                 * Initialize posting tree with the old tuple's posting list.  It's
 
97
                 * surely small enough to fit on one posting-tree page, and should
 
98
                 * already be in order with no duplicates.
 
99
                 */
 
100
                postingRoot = createPostingTree(ginstate->index,
 
101
                                                                                oldItems,
 
102
                                                                                oldNPosting,
 
103
                                                                                buildStats,
 
104
                                                                                buffer);
 
105
 
 
106
                /* Now insert the TIDs-to-be-added into the posting tree */
 
107
                ginInsertItemPointers(ginstate->index, postingRoot,
 
108
                                                          items, nitem,
 
109
                                                          buildStats);
 
110
 
 
111
                /* And build a new posting-tree-only result tuple */
 
112
                res = GinFormTuple(ginstate, attnum, key, category, NULL, 0, 0, true);
 
113
                GinSetPostingTree(res, postingRoot);
 
114
        }
 
115
        pfree(oldItems);
 
116
 
 
117
        return res;
 
118
}
 
119
 
 
120
/*
 
121
 * Build a fresh leaf tuple, either posting-list or posting-tree format
 
122
 * depending on whether the given items list will fit.
 
123
 * items[] must be in sorted order with no duplicates.
 
124
 *
 
125
 * This is basically the same logic as in addItemPointersToLeafTuple,
 
126
 * but working from slightly different input.
 
127
 */
 
128
static IndexTuple
 
129
buildFreshLeafTuple(GinState *ginstate,
 
130
                                        OffsetNumber attnum, Datum key, GinNullCategory category,
 
131
                                        ItemPointerData *items, uint32 nitem,
 
132
                                        GinStatsData *buildStats, Buffer buffer)
 
133
{
 
134
        IndexTuple      res = NULL;
 
135
        GinPostingList *compressedList;
 
136
 
 
137
        /* try to build a posting list tuple with all the items */
 
138
        compressedList = ginCompressPostingList(items, nitem, GinMaxItemSize, NULL);
 
139
        if (compressedList)
 
140
        {
 
141
                res = GinFormTuple(ginstate, attnum, key, category,
 
142
                                                   (char *) compressedList,
 
143
                                                   SizeOfGinPostingList(compressedList),
 
144
                                                   nitem, false);
 
145
                pfree(compressedList);
 
146
        }
 
147
        if (!res)
 
148
        {
 
149
                /* posting list would be too big, build posting tree */
 
150
                BlockNumber postingRoot;
 
151
 
 
152
                /*
 
153
                 * Build posting-tree-only result tuple.  We do this first so as to
 
154
                 * fail quickly if the key is too big.
 
155
                 */
 
156
                res = GinFormTuple(ginstate, attnum, key, category, NULL, 0, 0, true);
 
157
 
 
158
                /*
 
159
                 * Initialize a new posting tree with the TIDs.
 
160
                 */
 
161
                postingRoot = createPostingTree(ginstate->index, items, nitem,
 
162
                                                                                buildStats, buffer);
 
163
 
 
164
                /* And save the root link in the result tuple */
 
165
                GinSetPostingTree(res, postingRoot);
 
166
        }
 
167
 
 
168
        return res;
 
169
}
 
170
 
 
171
/*
 
172
 * Insert one or more heap TIDs associated with the given key value.
 
173
 * This will either add a single key entry, or enlarge a pre-existing entry.
 
174
 *
 
175
 * During an index build, buildStats is non-null and the counters
 
176
 * it contains should be incremented as needed.
 
177
 */
 
178
void
 
179
ginEntryInsert(GinState *ginstate,
 
180
                           OffsetNumber attnum, Datum key, GinNullCategory category,
 
181
                           ItemPointerData *items, uint32 nitem,
 
182
                           GinStatsData *buildStats)
 
183
{
 
184
        GinBtreeData btree;
 
185
        GinBtreeEntryInsertData insertdata;
 
186
        GinBtreeStack *stack;
 
187
        IndexTuple      itup;
 
188
        Page            page;
 
189
 
 
190
        insertdata.isDelete = false;
 
191
 
 
192
        /* During index build, count the to-be-inserted entry */
 
193
        if (buildStats)
 
194
                buildStats->nEntries++;
 
195
 
 
196
        ginPrepareEntryScan(&btree, attnum, key, category, ginstate);
 
197
 
 
198
        stack = ginFindLeafPage(&btree, false, NULL);
 
199
        page = BufferGetPage(stack->buffer);
 
200
 
 
201
        if (btree.findItem(&btree, stack))
 
202
        {
 
203
                /* found pre-existing entry */
 
204
                itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stack->off));
 
205
 
 
206
                if (GinIsPostingTree(itup))
 
207
                {
 
208
                        /* add entries to existing posting tree */
 
209
                        BlockNumber rootPostingTree = GinGetPostingTree(itup);
 
210
 
 
211
                        /* release all stack */
 
212
                        LockBuffer(stack->buffer, GIN_UNLOCK);
 
213
                        freeGinBtreeStack(stack);
 
214
 
 
215
                        /* insert into posting tree */
 
216
                        ginInsertItemPointers(ginstate->index, rootPostingTree,
 
217
                                                                  items, nitem,
 
218
                                                                  buildStats);
 
219
                        return;
 
220
                }
 
221
 
 
222
                CheckForSerializableConflictIn(ginstate->index, NULL, stack->buffer);
 
223
                /* modify an existing leaf entry */
 
224
                itup = addItemPointersToLeafTuple(ginstate, itup,
 
225
                                                                                  items, nitem, buildStats, stack->buffer);
 
226
 
 
227
                insertdata.isDelete = true;
 
228
        }
 
229
        else
 
230
        {
 
231
                CheckForSerializableConflictIn(ginstate->index, NULL, stack->buffer);
 
232
                /* no match, so construct a new leaf entry */
 
233
                itup = buildFreshLeafTuple(ginstate, attnum, key, category,
 
234
                                                                   items, nitem, buildStats, stack->buffer);
 
235
        }
 
236
 
 
237
        /* Insert the new or modified leaf tuple */
 
238
        insertdata.entry = itup;
 
239
        ginInsertValue(&btree, stack, &insertdata, buildStats);
 
240
        pfree(itup);
 
241
}
 
242
 
 
243
/*
 
244
 * Extract index entries for a single indexable item, and add them to the
 
245
 * BuildAccumulator's state.
 
246
 *
 
247
 * This function is used only during initial index creation.
 
248
 */
 
249
static void
 
250
ginHeapTupleBulkInsert(GinBuildState *buildstate, OffsetNumber attnum,
 
251
                                           Datum value, bool isNull,
 
252
                                           ItemPointer heapptr)
 
253
{
 
254
        Datum      *entries;
 
255
        GinNullCategory *categories;
 
256
        int32           nentries;
 
257
        MemoryContext oldCtx;
 
258
 
 
259
        oldCtx = MemoryContextSwitchTo(buildstate->funcCtx);
 
260
        entries = ginExtractEntries(buildstate->accum.ginstate, attnum,
 
261
                                                                value, isNull,
 
262
                                                                &nentries, &categories);
 
263
        MemoryContextSwitchTo(oldCtx);
 
264
 
 
265
        ginInsertBAEntries(&buildstate->accum, heapptr, attnum,
 
266
                                           entries, categories, nentries);
 
267
 
 
268
        buildstate->indtuples += nentries;
 
269
 
 
270
        MemoryContextReset(buildstate->funcCtx);
 
271
}
 
272
 
 
273
static void
 
274
ginBuildCallback(Relation index, HeapTuple htup, Datum *values,
 
275
                                 bool *isnull, bool tupleIsAlive, void *state)
 
276
{
 
277
        GinBuildState *buildstate = (GinBuildState *) state;
 
278
        MemoryContext oldCtx;
 
279
        int                     i;
 
280
 
 
281
        oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
 
282
 
 
283
        for (i = 0; i < buildstate->ginstate.origTupdesc->natts; i++)
 
284
                ginHeapTupleBulkInsert(buildstate, (OffsetNumber) (i + 1),
 
285
                                                           values[i], isnull[i],
 
286
                                                           &htup->t_self);
 
287
 
 
288
        /* If we've maxed out our available memory, dump everything to the index */
 
289
        if (buildstate->accum.allocatedMemory >= (Size) maintenance_work_mem * 1024L)
 
290
        {
 
291
                ItemPointerData *list;
 
292
                Datum           key;
 
293
                GinNullCategory category;
 
294
                uint32          nlist;
 
295
                OffsetNumber attnum;
 
296
 
 
297
                ginBeginBAScan(&buildstate->accum);
 
298
                while ((list = ginGetBAEntry(&buildstate->accum,
 
299
                                                                         &attnum, &key, &category, &nlist)) != NULL)
 
300
                {
 
301
                        /* there could be many entries, so be willing to abort here */
 
302
                        CHECK_FOR_INTERRUPTS();
 
303
                        ginEntryInsert(&buildstate->ginstate, attnum, key, category,
 
304
                                                   list, nlist, &buildstate->buildStats);
 
305
                }
 
306
 
 
307
                MemoryContextReset(buildstate->tmpCtx);
 
308
                ginInitBA(&buildstate->accum);
 
309
        }
 
310
 
 
311
        MemoryContextSwitchTo(oldCtx);
 
312
}
 
313
 
 
314
IndexBuildResult *
 
315
ginbuild(Relation heap, Relation index, IndexInfo *indexInfo)
 
316
{
 
317
        IndexBuildResult *result;
 
318
        double          reltuples;
 
319
        GinBuildState buildstate;
 
320
        Buffer          RootBuffer,
 
321
                                MetaBuffer;
 
322
        ItemPointerData *list;
 
323
        Datum           key;
 
324
        GinNullCategory category;
 
325
        uint32          nlist;
 
326
        MemoryContext oldCtx;
 
327
        OffsetNumber attnum;
 
328
 
 
329
        if (RelationGetNumberOfBlocks(index) != 0)
 
330
                elog(ERROR, "index \"%s\" already contains data",
 
331
                         RelationGetRelationName(index));
 
332
 
 
333
        initGinState(&buildstate.ginstate, index);
 
334
        buildstate.indtuples = 0;
 
335
        memset(&buildstate.buildStats, 0, sizeof(GinStatsData));
 
336
 
 
337
        /* initialize the meta page */
 
338
        MetaBuffer = GinNewBuffer(index);
 
339
 
 
340
        /* initialize the root page */
 
341
        RootBuffer = GinNewBuffer(index);
 
342
 
 
343
        START_CRIT_SECTION();
 
344
        GinInitMetabuffer(MetaBuffer);
 
345
        MarkBufferDirty(MetaBuffer);
 
346
        GinInitBuffer(RootBuffer, GIN_LEAF);
 
347
        MarkBufferDirty(RootBuffer);
 
348
 
 
349
        if (RelationNeedsWAL(index))
 
350
        {
 
351
                XLogRecPtr      recptr;
 
352
                Page            page;
 
353
 
 
354
                XLogBeginInsert();
 
355
                XLogRegisterBuffer(0, MetaBuffer, REGBUF_WILL_INIT | REGBUF_STANDARD);
 
356
                XLogRegisterBuffer(1, RootBuffer, REGBUF_WILL_INIT);
 
357
 
 
358
                recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_CREATE_INDEX);
 
359
 
 
360
                page = BufferGetPage(RootBuffer);
 
361
                PageSetLSN(page, recptr);
 
362
 
 
363
                page = BufferGetPage(MetaBuffer);
 
364
                PageSetLSN(page, recptr);
 
365
        }
 
366
 
 
367
        UnlockReleaseBuffer(MetaBuffer);
 
368
        UnlockReleaseBuffer(RootBuffer);
 
369
        END_CRIT_SECTION();
 
370
 
 
371
        /* count the root as first entry page */
 
372
        buildstate.buildStats.nEntryPages++;
 
373
 
 
374
        /*
 
375
         * create a temporary memory context that is used to hold data not yet
 
376
         * dumped out to the index
 
377
         */
 
378
        buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
 
379
                                                                                          "Gin build temporary context",
 
380
                                                                                          ALLOCSET_DEFAULT_SIZES);
 
381
 
 
382
        /*
 
383
         * create a temporary memory context that is used for calling
 
384
         * ginExtractEntries(), and can be reset after each tuple
 
385
         */
 
386
        buildstate.funcCtx = AllocSetContextCreate(CurrentMemoryContext,
 
387
                                                                                           "Gin build temporary context for user-defined function",
 
388
                                                                                           ALLOCSET_DEFAULT_SIZES);
 
389
 
 
390
        buildstate.accum.ginstate = &buildstate.ginstate;
 
391
        ginInitBA(&buildstate.accum);
 
392
 
 
393
        /*
 
394
         * Do the heap scan.  We disallow sync scan here because dataPlaceToPage
 
395
         * prefers to receive tuples in TID order.
 
396
         */
 
397
        reltuples = IndexBuildHeapScan(heap, index, indexInfo, false,
 
398
                                                                   ginBuildCallback, (void *) &buildstate, NULL);
 
399
 
 
400
        /* dump remaining entries to the index */
 
401
        oldCtx = MemoryContextSwitchTo(buildstate.tmpCtx);
 
402
        ginBeginBAScan(&buildstate.accum);
 
403
        while ((list = ginGetBAEntry(&buildstate.accum,
 
404
                                                                 &attnum, &key, &category, &nlist)) != NULL)
 
405
        {
 
406
                /* there could be many entries, so be willing to abort here */
 
407
                CHECK_FOR_INTERRUPTS();
 
408
                ginEntryInsert(&buildstate.ginstate, attnum, key, category,
 
409
                                           list, nlist, &buildstate.buildStats);
 
410
        }
 
411
        MemoryContextSwitchTo(oldCtx);
 
412
 
 
413
        MemoryContextDelete(buildstate.funcCtx);
 
414
        MemoryContextDelete(buildstate.tmpCtx);
 
415
 
 
416
        /*
 
417
         * Update metapage stats
 
418
         */
 
419
        buildstate.buildStats.nTotalPages = RelationGetNumberOfBlocks(index);
 
420
        ginUpdateStats(index, &buildstate.buildStats);
 
421
 
 
422
        /*
 
423
         * Return statistics
 
424
         */
 
425
        result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
 
426
 
 
427
        result->heap_tuples = reltuples;
 
428
        result->index_tuples = buildstate.indtuples;
 
429
 
 
430
        return result;
 
431
}
 
432
 
 
433
/*
 
434
 *      ginbuildempty() -- build an empty gin index in the initialization fork
 
435
 */
 
436
void
 
437
ginbuildempty(Relation index)
 
438
{
 
439
        Buffer          RootBuffer,
 
440
                                MetaBuffer;
 
441
 
 
442
        /* An empty GIN index has two pages. */
 
443
        MetaBuffer =
 
444
                ReadBufferExtended(index, INIT_FORKNUM, P_NEW, RBM_NORMAL, NULL);
 
445
        LockBuffer(MetaBuffer, BUFFER_LOCK_EXCLUSIVE);
 
446
        RootBuffer =
 
447
                ReadBufferExtended(index, INIT_FORKNUM, P_NEW, RBM_NORMAL, NULL);
 
448
        LockBuffer(RootBuffer, BUFFER_LOCK_EXCLUSIVE);
 
449
 
 
450
        /* Initialize and xlog metabuffer and root buffer. */
 
451
        START_CRIT_SECTION();
 
452
        GinInitMetabuffer(MetaBuffer);
 
453
        MarkBufferDirty(MetaBuffer);
 
454
        log_newpage_buffer(MetaBuffer, true);
 
455
        GinInitBuffer(RootBuffer, GIN_LEAF);
 
456
        MarkBufferDirty(RootBuffer);
 
457
        log_newpage_buffer(RootBuffer, false);
 
458
        END_CRIT_SECTION();
 
459
 
 
460
        /* Unlock and release the buffers. */
 
461
        UnlockReleaseBuffer(MetaBuffer);
 
462
        UnlockReleaseBuffer(RootBuffer);
 
463
}
 
464
 
 
465
/*
 
466
 * Insert index entries for a single indexable item during "normal"
 
467
 * (non-fast-update) insertion
 
468
 */
 
469
static void
 
470
ginHeapTupleInsert(GinState *ginstate, OffsetNumber attnum,
 
471
                                   Datum value, bool isNull,
 
472
                                   ItemPointer item)
 
473
{
 
474
        Datum      *entries;
 
475
        GinNullCategory *categories;
 
476
        int32           i,
 
477
                                nentries;
 
478
 
 
479
        entries = ginExtractEntries(ginstate, attnum, value, isNull,
 
480
                                                                &nentries, &categories);
 
481
 
 
482
        for (i = 0; i < nentries; i++)
 
483
                ginEntryInsert(ginstate, attnum, entries[i], categories[i],
 
484
                                           item, 1, NULL);
 
485
}
 
486
 
 
487
bool
 
488
gininsert(Relation index, Datum *values, bool *isnull,
 
489
                  ItemPointer ht_ctid, Relation heapRel,
 
490
                  IndexUniqueCheck checkUnique,
 
491
                  IndexInfo *indexInfo)
 
492
{
 
493
        GinState   *ginstate = (GinState *) indexInfo->ii_AmCache;
 
494
        MemoryContext oldCtx;
 
495
        MemoryContext insertCtx;
 
496
        int                     i;
 
497
 
 
498
        /* Initialize GinState cache if first call in this statement */
 
499
        if (ginstate == NULL)
 
500
        {
 
501
                oldCtx = MemoryContextSwitchTo(indexInfo->ii_Context);
 
502
                ginstate = (GinState *) palloc(sizeof(GinState));
 
503
                initGinState(ginstate, index);
 
504
                indexInfo->ii_AmCache = (void *) ginstate;
 
505
                MemoryContextSwitchTo(oldCtx);
 
506
        }
 
507
 
 
508
        insertCtx = AllocSetContextCreate(CurrentMemoryContext,
 
509
                                                                          "Gin insert temporary context",
 
510
                                                                          ALLOCSET_DEFAULT_SIZES);
 
511
 
 
512
        oldCtx = MemoryContextSwitchTo(insertCtx);
 
513
 
 
514
        if (GinGetUseFastUpdate(index))
 
515
        {
 
516
                GinTupleCollector collector;
 
517
 
 
518
                memset(&collector, 0, sizeof(GinTupleCollector));
 
519
 
 
520
                for (i = 0; i < ginstate->origTupdesc->natts; i++)
 
521
                        ginHeapTupleFastCollect(ginstate, &collector,
 
522
                                                                        (OffsetNumber) (i + 1),
 
523
                                                                        values[i], isnull[i],
 
524
                                                                        ht_ctid);
 
525
 
 
526
                ginHeapTupleFastInsert(ginstate, &collector);
 
527
        }
 
528
        else
 
529
        {
 
530
                for (i = 0; i < ginstate->origTupdesc->natts; i++)
 
531
                        ginHeapTupleInsert(ginstate, (OffsetNumber) (i + 1),
 
532
                                                           values[i], isnull[i],
 
533
                                                           ht_ctid);
 
534
        }
 
535
 
 
536
        MemoryContextSwitchTo(oldCtx);
 
537
        MemoryContextDelete(insertCtx);
 
538
 
 
539
        return false;
 
540
}