Fix xslt_process() to ensure that it inserts a NULL terminator after the
[PostgreSQL.git] / src / backend / access / gist / gistget.c
blobd1e4d5e0579ca504a02b0bc0f467f156e0957694
1 /*-------------------------------------------------------------------------
3 * gistget.c
4 * fetch tuples from a GiST scan.
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$
13 *-------------------------------------------------------------------------
15 #include "postgres.h"
17 #include "access/gist_private.h"
18 #include "access/relscan.h"
19 #include "executor/execdebug.h"
20 #include "miscadmin.h"
21 #include "pgstat.h"
22 #include "storage/bufmgr.h"
23 #include "utils/memutils.h"
26 static OffsetNumber gistfindnext(IndexScanDesc scan, OffsetNumber n);
27 static int64 gistnext(IndexScanDesc scan, TIDBitmap *tbm);
28 static bool gistindex_keytest(IndexTuple tuple, IndexScanDesc scan,
29 OffsetNumber offset);
31 static void
32 killtuple(Relation r, GISTScanOpaque so, ItemPointer iptr)
34 Page p;
35 OffsetNumber offset;
37 LockBuffer(so->curbuf, GIST_SHARE);
38 gistcheckpage(r, so->curbuf);
39 p = (Page) BufferGetPage(so->curbuf);
41 if (XLByteEQ(so->stack->lsn, PageGetLSN(p)))
43 /* page unchanged, so all is simple */
44 offset = ItemPointerGetOffsetNumber(iptr);
45 ItemIdMarkDead(PageGetItemId(p, offset));
46 SetBufferCommitInfoNeedsSave(so->curbuf);
48 else
50 OffsetNumber maxoff = PageGetMaxOffsetNumber(p);
52 for (offset = FirstOffsetNumber; offset <= maxoff; offset = OffsetNumberNext(offset))
54 IndexTuple ituple = (IndexTuple) PageGetItem(p, PageGetItemId(p, offset));
56 if (ItemPointerEquals(&(ituple->t_tid), iptr))
58 /* found */
59 ItemIdMarkDead(PageGetItemId(p, offset));
60 SetBufferCommitInfoNeedsSave(so->curbuf);
61 break;
66 LockBuffer(so->curbuf, GIST_UNLOCK);
70 * gistgettuple() -- Get the next tuple in the scan
72 Datum
73 gistgettuple(PG_FUNCTION_ARGS)
75 IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
76 ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1);
77 GISTScanOpaque so;
78 bool res;
80 so = (GISTScanOpaque) scan->opaque;
82 if (dir != ForwardScanDirection)
83 elog(ERROR, "GiST doesn't support other scan directions than forward");
86 * If we have produced an index tuple in the past and the executor has
87 * informed us we need to mark it as "killed", do so now.
89 if (scan->kill_prior_tuple && ItemPointerIsValid(&(so->curpos)))
90 killtuple(scan->indexRelation, so, &(so->curpos));
93 * Get the next tuple that matches the search key.
95 res = (gistnext(scan, NULL) > 0);
97 PG_RETURN_BOOL(res);
100 Datum
101 gistgetbitmap(PG_FUNCTION_ARGS)
103 IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
104 TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
105 int64 ntids;
107 ntids = gistnext(scan, tbm);
109 PG_RETURN_INT64(ntids);
113 * Fetch tuple(s) that match the search key; this can be invoked
114 * either to fetch the first such tuple or subsequent matching tuples.
116 * This function is used by both gistgettuple and gistgetbitmap. When
117 * invoked from gistgettuple, tbm is null and the next matching tuple
118 * is returned in scan->xs_ctup.t_self. When invoked from getbitmap,
119 * tbm is non-null and all matching tuples are added to tbm before
120 * returning. In both cases, the function result is the number of
121 * returned tuples.
123 * If scan specifies to skip killed tuples, continue looping until we find a
124 * non-killed tuple that matches the search key.
126 static int64
127 gistnext(IndexScanDesc scan, TIDBitmap *tbm)
129 Page p;
130 OffsetNumber n;
131 GISTScanOpaque so;
132 GISTSearchStack *stk;
133 IndexTuple it;
134 GISTPageOpaque opaque;
135 int64 ntids = 0;
137 so = (GISTScanOpaque) scan->opaque;
139 if (so->qual_ok == false)
140 return 0;
142 if (so->curbuf == InvalidBuffer)
144 if (ItemPointerIsValid(&so->curpos) == false)
146 /* Being asked to fetch the first entry, so start at the root */
147 Assert(so->curbuf == InvalidBuffer);
148 Assert(so->stack == NULL);
150 so->curbuf = ReadBuffer(scan->indexRelation, GIST_ROOT_BLKNO);
152 stk = so->stack = (GISTSearchStack *) palloc0(sizeof(GISTSearchStack));
154 stk->next = NULL;
155 stk->block = GIST_ROOT_BLKNO;
157 pgstat_count_index_scan(scan->indexRelation);
159 else
161 /* scan is finished */
162 return 0;
167 * check stored pointers from last visit
169 if (so->nPageData > 0)
172 * gistgetmulti never should go here
174 Assert(tbm == NULL);
176 if (so->curPageData < so->nPageData)
178 scan->xs_ctup.t_self = so->pageData[so->curPageData].heapPtr;
179 scan->xs_recheck = so->pageData[so->curPageData].recheck;
181 ItemPointerSet(&so->curpos,
182 BufferGetBlockNumber(so->curbuf),
183 so->pageData[so->curPageData].pageOffset);
185 so->curPageData++;
187 return 1;
189 else
192 * Go to the next page
194 stk = so->stack->next;
195 pfree(so->stack);
196 so->stack = stk;
198 /* If we're out of stack entries, we're done */
199 if (so->stack == NULL)
201 ReleaseBuffer(so->curbuf);
202 so->curbuf = InvalidBuffer;
203 return 0;
206 so->curbuf = ReleaseAndReadBuffer(so->curbuf,
207 scan->indexRelation,
208 stk->block);
212 for (;;)
214 CHECK_FOR_INTERRUPTS();
216 /* First of all, we need lock buffer */
217 Assert(so->curbuf != InvalidBuffer);
218 LockBuffer(so->curbuf, GIST_SHARE);
219 gistcheckpage(scan->indexRelation, so->curbuf);
220 p = BufferGetPage(so->curbuf);
221 opaque = GistPageGetOpaque(p);
223 /* remember lsn to identify page changed for tuple's killing */
224 so->stack->lsn = PageGetLSN(p);
226 /* check page split, occured since visit to parent */
227 if (!XLogRecPtrIsInvalid(so->stack->parentlsn) &&
228 XLByteLT(so->stack->parentlsn, opaque->nsn) &&
229 opaque->rightlink != InvalidBlockNumber /* sanity check */ &&
230 (so->stack->next == NULL || so->stack->next->block != opaque->rightlink) /* check if already
231 added */ )
233 /* detect page split, follow right link to add pages */
235 stk = (GISTSearchStack *) palloc(sizeof(GISTSearchStack));
236 stk->next = so->stack->next;
237 stk->block = opaque->rightlink;
238 stk->parentlsn = so->stack->parentlsn;
239 memset(&(stk->lsn), 0, sizeof(GistNSN));
240 so->stack->next = stk;
243 /* if page is empty, then just skip it */
244 if (PageIsEmpty(p))
246 LockBuffer(so->curbuf, GIST_UNLOCK);
247 stk = so->stack->next;
248 pfree(so->stack);
249 so->stack = stk;
251 if (so->stack == NULL)
253 ReleaseBuffer(so->curbuf);
254 so->curbuf = InvalidBuffer;
255 return ntids;
258 so->curbuf = ReleaseAndReadBuffer(so->curbuf, scan->indexRelation,
259 stk->block);
260 continue;
263 n = FirstOffsetNumber;
265 /* wonderful, we can look at page */
266 so->nPageData = so->curPageData = 0;
268 for (;;)
270 n = gistfindnext(scan, n);
272 if (!OffsetNumberIsValid(n))
275 * If we was called from gistgettuple and current buffer
276 * contains something matched then make a recursive call - it
277 * will return ItemPointer from so->pageData. But we save
278 * buffer pinned to support tuple's killing
280 if (!tbm && so->nPageData > 0)
282 LockBuffer(so->curbuf, GIST_UNLOCK);
283 return gistnext(scan, NULL);
287 * We ran out of matching index entries on the current page,
288 * so pop the top stack entry and use it to continue the
289 * search.
291 LockBuffer(so->curbuf, GIST_UNLOCK);
292 stk = so->stack->next;
293 pfree(so->stack);
294 so->stack = stk;
296 /* If we're out of stack entries, we're done */
298 if (so->stack == NULL)
300 ReleaseBuffer(so->curbuf);
301 so->curbuf = InvalidBuffer;
302 return ntids;
305 so->curbuf = ReleaseAndReadBuffer(so->curbuf,
306 scan->indexRelation,
307 stk->block);
308 /* XXX go up */
309 break;
312 if (GistPageIsLeaf(p))
315 * We've found a matching index entry in a leaf page, so
316 * return success. Note that we keep "curbuf" pinned so that
317 * we can efficiently resume the index scan later.
320 if (!(scan->ignore_killed_tuples &&
321 ItemIdIsDead(PageGetItemId(p, n))))
323 it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
324 ntids++;
325 if (tbm != NULL)
326 tbm_add_tuples(tbm, &it->t_tid, 1, scan->xs_recheck);
327 else
329 so->pageData[so->nPageData].heapPtr = it->t_tid;
330 so->pageData[so->nPageData].pageOffset = n;
331 so->pageData[so->nPageData].recheck = scan->xs_recheck;
332 so->nPageData++;
336 else
339 * We've found an entry in an internal node whose key is
340 * consistent with the search key, so push it to stack
342 stk = (GISTSearchStack *) palloc(sizeof(GISTSearchStack));
344 it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
345 stk->block = ItemPointerGetBlockNumber(&(it->t_tid));
346 memset(&(stk->lsn), 0, sizeof(GistNSN));
347 stk->parentlsn = so->stack->lsn;
349 stk->next = so->stack->next;
350 so->stack->next = stk;
353 n = OffsetNumberNext(n);
357 return ntids;
361 * gistindex_keytest() -- does this index tuple satisfy the scan key(s)?
363 * On success return for a leaf tuple, scan->xs_recheck is set to indicate
364 * whether recheck is needed. We recheck if any of the consistent() functions
365 * request it.
367 * We must decompress the key in the IndexTuple before passing it to the
368 * sk_func (and we have previously overwritten the sk_func to use the
369 * user-defined Consistent method, so we actually are invoking that).
371 * Note that this function is always invoked in a short-lived memory context,
372 * so we don't need to worry about cleaning up allocated memory, either here
373 * or in the implementation of any Consistent methods.
375 static bool
376 gistindex_keytest(IndexTuple tuple,
377 IndexScanDesc scan,
378 OffsetNumber offset)
380 int keySize = scan->numberOfKeys;
381 ScanKey key = scan->keyData;
382 Relation r = scan->indexRelation;
383 GISTScanOpaque so;
384 Page p;
385 GISTSTATE *giststate;
387 so = (GISTScanOpaque) scan->opaque;
388 giststate = so->giststate;
389 p = BufferGetPage(so->curbuf);
391 IncrIndexProcessed();
393 scan->xs_recheck = false;
396 * Tuple doesn't restore after crash recovery because of incomplete insert
398 if (!GistPageIsLeaf(p) && GistTupleIsInvalid(tuple))
399 return true;
401 while (keySize > 0)
403 Datum datum;
404 bool isNull;
405 Datum test;
406 bool recheck;
407 GISTENTRY de;
409 datum = index_getattr(tuple,
410 key->sk_attno,
411 giststate->tupdesc,
412 &isNull);
414 if (key->sk_flags & SK_ISNULL)
417 * On non-leaf page we can't conclude that child hasn't NULL
418 * values because of assumption in GiST: uinon (VAL, NULL) is VAL
419 * But if on non-leaf page key IS NULL then all childs has NULL.
422 Assert(key->sk_flags & SK_SEARCHNULL);
424 if (GistPageIsLeaf(p) && !isNull)
425 return false;
427 else if (isNull)
429 return false;
431 else
433 gistdentryinit(giststate, key->sk_attno - 1, &de,
434 datum, r, p, offset,
435 FALSE, isNull);
438 * Call the Consistent function to evaluate the test. The
439 * arguments are the index datum (as a GISTENTRY*), the comparison
440 * datum, the comparison operator's strategy number and subtype
441 * from pg_amop, and the recheck flag.
443 * (Presently there's no need to pass the subtype since it'll
444 * always be zero, but might as well pass it for possible future
445 * use.)
447 * We initialize the recheck flag to true (the safest assumption)
448 * in case the Consistent function forgets to set it.
450 recheck = true;
452 test = FunctionCall5(&key->sk_func,
453 PointerGetDatum(&de),
454 key->sk_argument,
455 Int32GetDatum(key->sk_strategy),
456 ObjectIdGetDatum(key->sk_subtype),
457 PointerGetDatum(&recheck));
459 if (!DatumGetBool(test))
460 return false;
461 scan->xs_recheck |= recheck;
464 keySize--;
465 key++;
468 return true;
472 * Return the offset of the first index entry that is consistent with
473 * the search key after offset 'n' in the current page. If there are
474 * no more consistent entries, return InvalidOffsetNumber.
475 * On success, scan->xs_recheck is set correctly, too.
476 * Page should be locked....
478 static OffsetNumber
479 gistfindnext(IndexScanDesc scan, OffsetNumber n)
481 OffsetNumber maxoff;
482 IndexTuple it;
483 GISTScanOpaque so;
484 MemoryContext oldcxt;
485 Page p;
487 so = (GISTScanOpaque) scan->opaque;
488 p = BufferGetPage(so->curbuf);
489 maxoff = PageGetMaxOffsetNumber(p);
492 * Make sure we're in a short-lived memory context when we invoke a
493 * user-supplied GiST method in gistindex_keytest(), so we don't leak
494 * memory
496 oldcxt = MemoryContextSwitchTo(so->tempCxt);
498 while (n >= FirstOffsetNumber && n <= maxoff)
500 it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
501 if (gistindex_keytest(it, scan, n))
502 break;
504 n = OffsetNumberNext(n);
507 MemoryContextSwitchTo(oldcxt);
508 MemoryContextReset(so->tempCxt);
511 * If we found a matching entry, return its offset; otherwise return
512 * InvalidOffsetNumber to inform the caller to go to the next page.
514 if (n >= FirstOffsetNumber && n <= maxoff)
515 return n;
516 else
517 return InvalidOffsetNumber;