Disallow empty passwords in LDAP authentication, the same way
[PostgreSQL.git] / src / backend / access / gin / gininsert.c
blob5120e8ee0a57f0c74d68efb352bc400c23ac74ad
1 /*-------------------------------------------------------------------------
3 * gininsert.c
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
10 * IDENTIFICATION
11 * $PostgreSQL$
12 *-------------------------------------------------------------------------
15 #include "postgres.h"
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"
26 typedef struct
28 GinState ginstate;
29 double indtuples;
30 MemoryContext tmpCtx;
31 MemoryContext funcCtx;
32 BuildAccumulator accum;
33 } GinBuildState;
36 * Creates posting tree with one page. Function
37 * suppose that items[] fits to page
39 static BlockNumber
40 createPostingTree(Relation index, ItemPointerData *items, uint32 nitems)
42 BlockNumber blkno;
43 Buffer buffer = GinNewBuffer(index);
44 Page page;
46 START_CRIT_SECTION();
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)
59 XLogRecPtr recptr;
60 XLogRecData rdata[2];
61 ginxlogCreatePostingTree data;
63 data.node = index->rd_node;
64 data.blkno = blkno;
65 data.nitem = nitems;
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;
75 rdata[1].next = NULL;
79 recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_CREATE_PTREE, rdata);
80 PageSetLSN(page, recptr);
81 PageSetTLI(page, ThisTimeLineID);
85 UnlockReleaseBuffer(buffer);
87 END_CRIT_SECTION();
89 return blkno;
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
97 * GinFormTuple().
99 static IndexTuple
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));
108 if (res)
110 /* good, small enough */
111 uint32 newnitem;
113 newnitem = MergeItemPointers(GinGetPosting(res),
114 GinGetPosting(old), GinGetNPosting(old),
115 items, nitem);
116 /* merge might have eliminated some duplicate items */
117 GinShortenTuple(res, newnitem);
119 else
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);
134 pfree(gdi);
137 return res;
141 * Inserts only one entry to the index, but it can add more than 1 ItemPointer.
143 void
144 ginEntryInsert(Relation index, GinState *ginstate,
145 OffsetNumber attnum, Datum value,
146 ItemPointerData *items, uint32 nitem,
147 bool isBuild)
149 GinBtreeData btree;
150 GinBtreeStack *stack;
151 IndexTuple itup;
152 Page page;
154 prepareEntryScan(&btree, index, attnum, value, ginstate);
156 stack = ginFindLeafPage(&btree, NULL);
157 page = BufferGetPage(stack->buffer);
159 if (btree.findItem(&btree, stack))
161 /* found entry */
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);
179 return;
182 itup = addItemPointersToTuple(index, ginstate, stack, itup, items, nitem, isBuild);
184 btree.isDelete = TRUE;
186 else
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");
193 if (nitem > 1)
195 IndexTuple previtup = itup;
197 itup = addItemPointersToTuple(index, ginstate, stack, previtup, items + 1, nitem - 1, isBuild);
198 pfree(previtup);
202 btree.entry = itup;
203 ginInsertValue(&btree, stack);
204 pfree(itup);
208 * Saves indexed value in memory accumulator during index creation
209 * Function isn't used during normal insert
211 static uint32
212 ginHeapTupleBulkInsert(GinBuildState *buildstate, OffsetNumber attnum, Datum value, ItemPointer heapptr)
214 Datum *entries;
215 int32 nentries;
216 MemoryContext oldCtx;
218 oldCtx = MemoryContextSwitchTo(buildstate->funcCtx);
219 entries = extractEntriesSU(buildstate->accum.ginstate, attnum, value, &nentries);
220 MemoryContextSwitchTo(oldCtx);
222 if (nentries == 0)
223 /* nothing to insert */
224 return 0;
226 ginInsertRecordBA(&buildstate->accum, heapptr, attnum, entries, nentries);
228 MemoryContextReset(buildstate->funcCtx);
230 return nentries;
233 static void
234 ginBuildCallback(Relation index, HeapTuple htup, Datum *values,
235 bool *isnull, bool tupleIsAlive, void *state)
237 GinBuildState *buildstate = (GinBuildState *) state;
238 MemoryContext oldCtx;
239 int i;
241 oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
243 for (i = 0; i < buildstate->ginstate.origTupdesc->natts; i++)
244 if (!isnull[i])
245 buildstate->indtuples += ginHeapTupleBulkInsert(buildstate,
246 (OffsetNumber) (i + 1), values[i],
247 &htup->t_self);
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;
255 Datum entry;
256 uint32 nlist;
257 OffsetNumber attnum;
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);
273 Datum
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;
280 double reltuples;
281 GinBuildState buildstate;
282 Buffer RootBuffer,
283 MetaBuffer;
284 ItemPointerData *list;
285 Datum entry;
286 uint32 nlist;
287 MemoryContext oldCtx;
288 OffsetNumber attnum;
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)
310 XLogRecPtr recptr;
311 XLogRecData rdata;
312 Page page;
314 rdata.buffer = InvalidBuffer;
315 rdata.data = (char *) &(index->rd_node);
316 rdata.len = sizeof(RelFileNode);
317 rdata.next = NULL;
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);
332 END_CRIT_SECTION();
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);
376 * Return statistics
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
389 static uint32
390 ginHeapTupleInsert(Relation index, GinState *ginstate, OffsetNumber attnum, Datum value, ItemPointer item)
392 Datum *entries;
393 int32 i,
394 nentries;
396 entries = extractEntriesSU(ginstate, attnum, value, &nentries);
398 if (nentries == 0)
399 /* nothing to insert */
400 return 0;
402 for (i = 0; i < nentries; i++)
403 ginEntryInsert(index, ginstate, attnum, entries[i], item, 1, FALSE);
405 return nentries;
408 Datum
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);
416 #ifdef NOT_USED
417 Relation heapRel = (Relation) PG_GETARG_POINTER(4);
418 bool checkUnique = PG_GETARG_BOOL(5);
419 #endif
420 GinState ginstate;
421 MemoryContext oldCtx;
422 MemoryContext insertCtx;
423 uint32 res = 0;
424 int i;
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++)
442 if (!isnull[i])
443 res += ginHeapTupleFastCollect(index, &ginstate, &collector,
444 (OffsetNumber) (i + 1), values[i], ht_ctid);
446 ginHeapTupleFastInsert(index, &ginstate, &collector);
448 else
450 for (i = 0; i < ginstate.origTupdesc->natts; i++)
451 if (!isnull[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);