1 /*-------------------------------------------------------------------------
4 * routines to manage scans on index relations
8 * /usr/local/devel/pglite/cvs/src/backend/access/gist/gistscan.c,v 1.7 1995/06/14 00:10:05 jolly Exp
10 *-------------------------------------------------------------------------
15 #include "access/genam.h"
16 #include "access/gist.h"
17 #include "access/gistscan.h"
20 /* routines defined and used here */
21 static void gistregscan(IndexScanDesc s);
22 static void gistdropscan(IndexScanDesc s);
23 static void gistadjone(IndexScanDesc s, int op, BlockNumber blkno,
25 static void adjuststack(GISTSTACK *stk, BlockNumber blkno,
27 static void adjustiptr(IndexScanDesc s, ItemPointer iptr,
28 int op, BlockNumber blkno, OffsetNumber offnum);
31 * Whenever we start a GiST scan in a backend, we register it in private
32 * space. Then if the GiST index gets updated, we check all registered
33 * scans and adjust them if the tuple they point at got moved by the
34 * update. We only need to do this in private space, because when we update
35 * an GiST we have a write lock on the tree, so no other process can have
36 * any locks at all on it. A single transaction can have write and read
37 * locks on the same object, so that's why we need to handle this case.
40 typedef struct GISTScanListData
42 IndexScanDesc gsl_scan;
43 struct GISTScanListData *gsl_next;
46 typedef GISTScanListData *GISTScanList;
48 /* pointer to list of local scans on GiSTs */
49 static GISTScanList GISTScans = (GISTScanList) NULL;
52 gistbeginscan(PG_FUNCTION_ARGS)
54 Relation r = (Relation) PG_GETARG_POINTER(0);
55 bool fromEnd = PG_GETARG_BOOL(1);
56 uint16 nkeys = PG_GETARG_UINT16(2);
57 ScanKey key = (ScanKey) PG_GETARG_POINTER(3);
61 * Let index_beginscan does its work...
63 * RelationSetLockForRead(r);
66 s = RelationGetIndexScan(r, fromEnd, nkeys, key);
73 gistrescan(PG_FUNCTION_ARGS)
75 IndexScanDesc s = (IndexScanDesc) PG_GETARG_POINTER(0);
76 bool fromEnd = PG_GETARG_BOOL(1);
77 ScanKey key = (ScanKey) PG_GETARG_POINTER(2);
82 * Clear all the pointers.
85 ItemPointerSetInvalid(&s->previousItemData);
86 ItemPointerSetInvalid(&s->currentItemData);
87 ItemPointerSetInvalid(&s->nextItemData);
88 ItemPointerSetInvalid(&s->previousMarkData);
89 ItemPointerSetInvalid(&s->currentMarkData);
90 ItemPointerSetInvalid(&s->nextMarkData);
95 if (RelationGetNumberOfBlocks(s->relation) == 0)
96 s->flags = ScanUnmarked;
98 s->flags = ScanUnmarked | ScanUncheckedPrevious;
100 s->flags = ScanUnmarked | ScanUncheckedNext;
102 s->scanFromEnd = fromEnd;
104 if (s->numberOfKeys > 0)
108 s->numberOfKeys * sizeof(ScanKeyData));
111 p = (GISTScanOpaque) s->opaque;
112 if (p != (GISTScanOpaque) NULL)
114 gistfreestack(p->s_stack);
115 gistfreestack(p->s_markstk);
116 p->s_stack = p->s_markstk = (GISTSTACK *) NULL;
118 for (i = 0; i < s->numberOfKeys; i++)
120 s->keyData[i].sk_procedure
121 = RelationGetGISTStrategy(s->relation, s->keyData[i].sk_attno,
122 s->keyData[i].sk_procedure);
123 s->keyData[i].sk_func = p->giststate->consistentFn;
128 /* initialize opaque data */
129 p = (GISTScanOpaque) palloc(sizeof(GISTScanOpaqueData));
130 p->s_stack = p->s_markstk = (GISTSTACK *) NULL;
133 p->giststate = (GISTSTATE *) palloc(sizeof(GISTSTATE));
134 initGISTstate(p->giststate, s->relation);
135 if (s->numberOfKeys > 0)
138 * * Play games here with the scan key to use the Consistent *
139 * function for all comparisons: * 1) the sk_procedure field
140 * will now be used to hold the * strategy number * 2) the
141 * sk_func field will point to the Consistent function
143 for (i = 0; i < s->numberOfKeys; i++)
147 * s->keyData[i].sk_procedure =
148 * index_getprocid(s->relation, 1, GIST_CONSISTENT_PROC);
150 s->keyData[i].sk_procedure
151 = RelationGetGISTStrategy(s->relation, s->keyData[i].sk_attno,
152 s->keyData[i].sk_procedure);
153 s->keyData[i].sk_func = p->giststate->consistentFn;
161 gistmarkpos(PG_FUNCTION_ARGS)
163 IndexScanDesc s = (IndexScanDesc) PG_GETARG_POINTER(0);
169 s->currentMarkData = s->currentItemData;
170 p = (GISTScanOpaque) s->opaque;
171 if (p->s_flags & GS_CURBEFORE)
172 p->s_flags |= GS_MRKBEFORE;
174 p->s_flags &= ~GS_MRKBEFORE;
176 o = (GISTSTACK *) NULL;
179 /* copy the parent stack from the current item data */
180 while (n != (GISTSTACK *) NULL)
182 tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK));
183 tmp->gs_child = n->gs_child;
184 tmp->gs_blk = n->gs_blk;
190 gistfreestack(p->s_markstk);
197 gistrestrpos(PG_FUNCTION_ARGS)
199 IndexScanDesc s = (IndexScanDesc) PG_GETARG_POINTER(0);
205 s->currentItemData = s->currentMarkData;
206 p = (GISTScanOpaque) s->opaque;
207 if (p->s_flags & GS_MRKBEFORE)
208 p->s_flags |= GS_CURBEFORE;
210 p->s_flags &= ~GS_CURBEFORE;
212 o = (GISTSTACK *) NULL;
215 /* copy the parent stack from the current item data */
216 while (n != (GISTSTACK *) NULL)
218 tmp = (GISTSTACK *) palloc(sizeof(GISTSTACK));
219 tmp->gs_child = n->gs_child;
220 tmp->gs_blk = n->gs_blk;
226 gistfreestack(p->s_stack);
233 gistendscan(PG_FUNCTION_ARGS)
235 IndexScanDesc s = (IndexScanDesc) PG_GETARG_POINTER(0);
238 p = (GISTScanOpaque) s->opaque;
240 if (p != (GISTScanOpaque) NULL)
242 gistfreestack(p->s_stack);
243 gistfreestack(p->s_markstk);
248 /* XXX don't unset read lock -- two-phase locking */
254 gistregscan(IndexScanDesc s)
258 l = (GISTScanList) palloc(sizeof(GISTScanListData));
260 l->gsl_next = GISTScans;
265 gistdropscan(IndexScanDesc s)
270 prev = (GISTScanList) NULL;
273 l != (GISTScanList) NULL && l->gsl_scan != s;
277 if (l == (GISTScanList) NULL)
278 elog(ERROR, "GiST scan list corrupted -- cannot find 0x%p", (void *) s);
280 if (prev == (GISTScanList) NULL)
281 GISTScans = l->gsl_next;
283 prev->gsl_next = l->gsl_next;
289 gistadjscans(Relation rel, int op, BlockNumber blkno, OffsetNumber offnum)
294 relid = RelationGetRelid(rel);
295 for (l = GISTScans; l != (GISTScanList) NULL; l = l->gsl_next)
297 if (l->gsl_scan->relation->rd_id == relid)
298 gistadjone(l->gsl_scan, op, blkno, offnum);
303 * gistadjone() -- adjust one scan for update.
305 * By here, the scan passed in is on a modified relation. Op tells
306 * us what the modification is, and blkno and offind tell us what
307 * block and offset index were affected. This routine checks the
308 * current and marked positions, and the current and marked stacks,
309 * to see if any stored location needs to be changed because of the
310 * update. If so, we make the change here.
313 gistadjone(IndexScanDesc s,
320 adjustiptr(s, &(s->currentItemData), op, blkno, offnum);
321 adjustiptr(s, &(s->currentMarkData), op, blkno, offnum);
323 so = (GISTScanOpaque) s->opaque;
325 if (op == GISTOP_SPLIT)
327 adjuststack(so->s_stack, blkno, offnum);
328 adjuststack(so->s_markstk, blkno, offnum);
333 * adjustiptr() -- adjust current and marked item pointers in the scan
335 * Depending on the type of update and the place it happened, we
336 * need to do nothing, to back up one record, or to start over on
340 adjustiptr(IndexScanDesc s,
349 if (ItemPointerIsValid(iptr))
351 if (ItemPointerGetBlockNumber(iptr) == blkno)
353 curoff = ItemPointerGetOffsetNumber(iptr);
354 so = (GISTScanOpaque) s->opaque;
359 /* back up one if we need to */
360 if (curoff >= offnum)
363 if (curoff > FirstOffsetNumber)
365 /* just adjust the item pointer */
366 ItemPointerSet(iptr, blkno, OffsetNumberPrev(curoff));
372 * remember that we're before the current
375 ItemPointerSet(iptr, blkno, FirstOffsetNumber);
376 if (iptr == &(s->currentItemData))
377 so->s_flags |= GS_CURBEFORE;
379 so->s_flags |= GS_MRKBEFORE;
385 /* back to start of page on split */
386 ItemPointerSet(iptr, blkno, FirstOffsetNumber);
387 if (iptr == &(s->currentItemData))
388 so->s_flags &= ~GS_CURBEFORE;
390 so->s_flags &= ~GS_MRKBEFORE;
394 elog(ERROR, "Bad operation in GiST scan adjust: %d", op);
401 * adjuststack() -- adjust the supplied stack for a split on a page in
402 * the index we're scanning.
404 * If a page on our parent stack has split, we need to back up to the
405 * beginning of the page and rescan it. The reason for this is that
406 * the split algorithm for GiSTs doesn't order tuples in any useful
407 * way on a single page. This means on that a split, we may wind up
408 * looking at some heap tuples more than once. This is handled in the
409 * access method update code for heaps; if we've modified the tuple we
410 * are looking at already in this transaction, we ignore the update
415 adjuststack(GISTSTACK *stk,
419 while (stk != (GISTSTACK *) NULL)
421 if (stk->gs_blk == blkno)
422 stk->gs_child = FirstOffsetNumber;
424 stk = stk->gs_parent;