1 /*-------------------------------------------------------------------------
4 * insert routines for the postgres inverted index access method.
7 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
12 *-------------------------------------------------------------------------
17 #include "access/genam.h"
18 #include "access/gin.h"
19 #include "catalog/index.h"
20 #include "miscadmin.h"
21 #include "storage/bufmgr.h"
22 #include "storage/indexfsm.h"
23 #include "utils/memutils.h"
31 MemoryContext funcCtx
;
32 BuildAccumulator accum
;
36 * Creates posting tree with one page. Function
37 * suppose that items[] fits to page
40 createPostingTree(Relation index
, ItemPointerData
*items
, uint32 nitems
)
43 Buffer buffer
= GinNewBuffer(index
);
48 GinInitBuffer(buffer
, GIN_DATA
| GIN_LEAF
);
49 page
= BufferGetPage(buffer
);
50 blkno
= BufferGetBlockNumber(buffer
);
52 memcpy(GinDataPageGetData(page
), items
, sizeof(ItemPointerData
) * nitems
);
53 GinPageGetOpaque(page
)->maxoff
= nitems
;
55 MarkBufferDirty(buffer
);
57 if (!index
->rd_istemp
)
61 ginxlogCreatePostingTree data
;
63 data
.node
= index
->rd_node
;
67 rdata
[0].buffer
= InvalidBuffer
;
68 rdata
[0].data
= (char *) &data
;
69 rdata
[0].len
= sizeof(ginxlogCreatePostingTree
);
70 rdata
[0].next
= &rdata
[1];
72 rdata
[1].buffer
= InvalidBuffer
;
73 rdata
[1].data
= (char *) items
;
74 rdata
[1].len
= sizeof(ItemPointerData
) * nitems
;
79 recptr
= XLogInsert(RM_GIN_ID
, XLOG_GIN_CREATE_PTREE
, rdata
);
80 PageSetLSN(page
, recptr
);
81 PageSetTLI(page
, ThisTimeLineID
);
85 UnlockReleaseBuffer(buffer
);
94 * Adds array of item pointers to tuple's posting list or
95 * creates posting tree and tuple pointed to tree in a case
96 * of not enough space. Max size of tuple is defined in
100 addItemPointersToTuple(Relation index
, GinState
*ginstate
, GinBtreeStack
*stack
,
101 IndexTuple old
, ItemPointerData
*items
, uint32 nitem
, bool isBuild
)
103 Datum key
= gin_index_getattr(ginstate
, old
);
104 OffsetNumber attnum
= gintuple_get_attrnum(ginstate
, old
);
105 IndexTuple res
= GinFormTuple(ginstate
, attnum
, key
,
106 NULL
, nitem
+ GinGetNPosting(old
));
110 /* good, small enough */
113 newnitem
= MergeItemPointers(GinGetPosting(res
),
114 GinGetPosting(old
), GinGetNPosting(old
),
116 /* merge might have eliminated some duplicate items */
117 GinShortenTuple(res
, newnitem
);
121 BlockNumber postingRoot
;
122 GinPostingTreeScan
*gdi
;
124 /* posting list becomes big, so we need to make posting's tree */
125 res
= GinFormTuple(ginstate
, attnum
, key
, NULL
, 0);
126 postingRoot
= createPostingTree(index
, GinGetPosting(old
), GinGetNPosting(old
));
127 GinSetPostingTree(res
, postingRoot
);
129 gdi
= prepareScanPostingTree(index
, postingRoot
, FALSE
);
130 gdi
->btree
.isBuild
= isBuild
;
132 insertItemPointer(gdi
, items
, nitem
);
141 * Inserts only one entry to the index, but it can add more than 1 ItemPointer.
144 ginEntryInsert(Relation index
, GinState
*ginstate
,
145 OffsetNumber attnum
, Datum value
,
146 ItemPointerData
*items
, uint32 nitem
,
150 GinBtreeStack
*stack
;
154 prepareEntryScan(&btree
, index
, attnum
, value
, ginstate
);
156 stack
= ginFindLeafPage(&btree
, NULL
);
157 page
= BufferGetPage(stack
->buffer
);
159 if (btree
.findItem(&btree
, stack
))
162 itup
= (IndexTuple
) PageGetItem(page
, PageGetItemId(page
, stack
->off
));
164 if (GinIsPostingTree(itup
))
166 /* lock root of posting tree */
167 GinPostingTreeScan
*gdi
;
168 BlockNumber rootPostingTree
= GinGetPostingTree(itup
);
170 /* release all stack */
171 LockBuffer(stack
->buffer
, GIN_UNLOCK
);
172 freeGinBtreeStack(stack
);
174 /* insert into posting tree */
175 gdi
= prepareScanPostingTree(index
, rootPostingTree
, FALSE
);
176 gdi
->btree
.isBuild
= isBuild
;
177 insertItemPointer(gdi
, items
, nitem
);
182 itup
= addItemPointersToTuple(index
, ginstate
, stack
, itup
, items
, nitem
, isBuild
);
184 btree
.isDelete
= TRUE
;
188 /* We suppose, that tuple can store at list one itempointer */
189 itup
= GinFormTuple(ginstate
, attnum
, value
, items
, 1);
190 if (itup
== NULL
|| IndexTupleSize(itup
) >= GinMaxItemSize
)
191 elog(ERROR
, "huge tuple");
195 IndexTuple previtup
= itup
;
197 itup
= addItemPointersToTuple(index
, ginstate
, stack
, previtup
, items
+ 1, nitem
- 1, isBuild
);
203 ginInsertValue(&btree
, stack
);
208 * Saves indexed value in memory accumulator during index creation
209 * Function isn't used during normal insert
212 ginHeapTupleBulkInsert(GinBuildState
*buildstate
, OffsetNumber attnum
, Datum value
, ItemPointer heapptr
)
216 MemoryContext oldCtx
;
218 oldCtx
= MemoryContextSwitchTo(buildstate
->funcCtx
);
219 entries
= extractEntriesSU(buildstate
->accum
.ginstate
, attnum
, value
, &nentries
);
220 MemoryContextSwitchTo(oldCtx
);
223 /* nothing to insert */
226 ginInsertRecordBA(&buildstate
->accum
, heapptr
, attnum
, entries
, nentries
);
228 MemoryContextReset(buildstate
->funcCtx
);
234 ginBuildCallback(Relation index
, HeapTuple htup
, Datum
*values
,
235 bool *isnull
, bool tupleIsAlive
, void *state
)
237 GinBuildState
*buildstate
= (GinBuildState
*) state
;
238 MemoryContext oldCtx
;
241 oldCtx
= MemoryContextSwitchTo(buildstate
->tmpCtx
);
243 for (i
= 0; i
< buildstate
->ginstate
.origTupdesc
->natts
; i
++)
245 buildstate
->indtuples
+= ginHeapTupleBulkInsert(buildstate
,
246 (OffsetNumber
) (i
+ 1), values
[i
],
249 /* If we've maxed out our available memory, dump everything to the index */
250 /* Also dump if the tree seems to be getting too unbalanced */
251 if (buildstate
->accum
.allocatedMemory
>= maintenance_work_mem
* 1024L ||
252 buildstate
->accum
.maxdepth
> GIN_MAX_TREE_DEPTH
)
254 ItemPointerData
*list
;
259 while ((list
= ginGetEntry(&buildstate
->accum
, &attnum
, &entry
, &nlist
)) != NULL
)
261 /* there could be many entries, so be willing to abort here */
262 CHECK_FOR_INTERRUPTS();
263 ginEntryInsert(index
, &buildstate
->ginstate
, attnum
, entry
, list
, nlist
, TRUE
);
266 MemoryContextReset(buildstate
->tmpCtx
);
267 ginInitBA(&buildstate
->accum
);
270 MemoryContextSwitchTo(oldCtx
);
274 ginbuild(PG_FUNCTION_ARGS
)
276 Relation heap
= (Relation
) PG_GETARG_POINTER(0);
277 Relation index
= (Relation
) PG_GETARG_POINTER(1);
278 IndexInfo
*indexInfo
= (IndexInfo
*) PG_GETARG_POINTER(2);
279 IndexBuildResult
*result
;
281 GinBuildState buildstate
;
284 ItemPointerData
*list
;
287 MemoryContext oldCtx
;
290 if (RelationGetNumberOfBlocks(index
) != 0)
291 elog(ERROR
, "index \"%s\" already contains data",
292 RelationGetRelationName(index
));
294 initGinState(&buildstate
.ginstate
, index
);
296 /* initialize the meta page */
297 MetaBuffer
= GinNewBuffer(index
);
299 /* initialize the root page */
300 RootBuffer
= GinNewBuffer(index
);
302 START_CRIT_SECTION();
303 GinInitMetabuffer(MetaBuffer
);
304 MarkBufferDirty(MetaBuffer
);
305 GinInitBuffer(RootBuffer
, GIN_LEAF
);
306 MarkBufferDirty(RootBuffer
);
308 if (!index
->rd_istemp
)
314 rdata
.buffer
= InvalidBuffer
;
315 rdata
.data
= (char *) &(index
->rd_node
);
316 rdata
.len
= sizeof(RelFileNode
);
319 recptr
= XLogInsert(RM_GIN_ID
, XLOG_GIN_CREATE_INDEX
, &rdata
);
321 page
= BufferGetPage(RootBuffer
);
322 PageSetLSN(page
, recptr
);
323 PageSetTLI(page
, ThisTimeLineID
);
325 page
= BufferGetPage(MetaBuffer
);
326 PageSetLSN(page
, recptr
);
327 PageSetTLI(page
, ThisTimeLineID
);
330 UnlockReleaseBuffer(MetaBuffer
);
331 UnlockReleaseBuffer(RootBuffer
);
334 /* build the index */
335 buildstate
.indtuples
= 0;
338 * create a temporary memory context that is reset once for each tuple
339 * inserted into the index
341 buildstate
.tmpCtx
= AllocSetContextCreate(CurrentMemoryContext
,
342 "Gin build temporary context",
343 ALLOCSET_DEFAULT_MINSIZE
,
344 ALLOCSET_DEFAULT_INITSIZE
,
345 ALLOCSET_DEFAULT_MAXSIZE
);
347 buildstate
.funcCtx
= AllocSetContextCreate(buildstate
.tmpCtx
,
348 "Gin build temporary context for user-defined function",
349 ALLOCSET_DEFAULT_MINSIZE
,
350 ALLOCSET_DEFAULT_INITSIZE
,
351 ALLOCSET_DEFAULT_MAXSIZE
);
353 buildstate
.accum
.ginstate
= &buildstate
.ginstate
;
354 ginInitBA(&buildstate
.accum
);
357 * Do the heap scan. We disallow sync scan here because dataPlaceToPage
358 * prefers to receive tuples in TID order.
360 reltuples
= IndexBuildHeapScan(heap
, index
, indexInfo
, false,
361 ginBuildCallback
, (void *) &buildstate
);
363 /* dump remaining entries to the index */
364 oldCtx
= MemoryContextSwitchTo(buildstate
.tmpCtx
);
365 while ((list
= ginGetEntry(&buildstate
.accum
, &attnum
, &entry
, &nlist
)) != NULL
)
367 /* there could be many entries, so be willing to abort here */
368 CHECK_FOR_INTERRUPTS();
369 ginEntryInsert(index
, &buildstate
.ginstate
, attnum
, entry
, list
, nlist
, TRUE
);
371 MemoryContextSwitchTo(oldCtx
);
373 MemoryContextDelete(buildstate
.tmpCtx
);
378 result
= (IndexBuildResult
*) palloc(sizeof(IndexBuildResult
));
380 result
->heap_tuples
= reltuples
;
381 result
->index_tuples
= buildstate
.indtuples
;
383 PG_RETURN_POINTER(result
);
387 * Inserts value during normal insertion
390 ginHeapTupleInsert(Relation index
, GinState
*ginstate
, OffsetNumber attnum
, Datum value
, ItemPointer item
)
396 entries
= extractEntriesSU(ginstate
, attnum
, value
, &nentries
);
399 /* nothing to insert */
402 for (i
= 0; i
< nentries
; i
++)
403 ginEntryInsert(index
, ginstate
, attnum
, entries
[i
], item
, 1, FALSE
);
409 gininsert(PG_FUNCTION_ARGS
)
411 Relation index
= (Relation
) PG_GETARG_POINTER(0);
412 Datum
*values
= (Datum
*) PG_GETARG_POINTER(1);
413 bool *isnull
= (bool *) PG_GETARG_POINTER(2);
414 ItemPointer ht_ctid
= (ItemPointer
) PG_GETARG_POINTER(3);
417 Relation heapRel
= (Relation
) PG_GETARG_POINTER(4);
418 bool checkUnique
= PG_GETARG_BOOL(5);
421 MemoryContext oldCtx
;
422 MemoryContext insertCtx
;
426 insertCtx
= AllocSetContextCreate(CurrentMemoryContext
,
427 "Gin insert temporary context",
428 ALLOCSET_DEFAULT_MINSIZE
,
429 ALLOCSET_DEFAULT_INITSIZE
,
430 ALLOCSET_DEFAULT_MAXSIZE
);
432 oldCtx
= MemoryContextSwitchTo(insertCtx
);
434 initGinState(&ginstate
, index
);
436 if (GinGetUseFastUpdate(index
))
438 GinTupleCollector collector
;
440 memset(&collector
, 0, sizeof(GinTupleCollector
));
441 for (i
= 0; i
< ginstate
.origTupdesc
->natts
; i
++)
443 res
+= ginHeapTupleFastCollect(index
, &ginstate
, &collector
,
444 (OffsetNumber
) (i
+ 1), values
[i
], ht_ctid
);
446 ginHeapTupleFastInsert(index
, &ginstate
, &collector
);
450 for (i
= 0; i
< ginstate
.origTupdesc
->natts
; i
++)
452 res
+= ginHeapTupleInsert(index
, &ginstate
,
453 (OffsetNumber
) (i
+ 1), values
[i
], ht_ctid
);
457 MemoryContextSwitchTo(oldCtx
);
458 MemoryContextDelete(insertCtx
);
460 PG_RETURN_BOOL(res
> 0);