mirror of https://github.com/postgres/postgres
parent
645adf5de8
commit
2a8d3d83ef
@ -1,31 +0,0 @@ |
|||||||
#-------------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
# Makefile--
|
|
||||||
# Makefile for access/rtree
|
|
||||||
#
|
|
||||||
# IDENTIFICATION
|
|
||||||
# $PostgreSQL: pgsql/src/backend/access/rtree/Makefile,v 1.11 2003/11/29 19:51:40 pgsql Exp $
|
|
||||||
#
|
|
||||||
#-------------------------------------------------------------------------
|
|
||||||
|
|
||||||
subdir = src/backend/access/rtree
|
|
||||||
top_builddir = ../../../..
|
|
||||||
include $(top_builddir)/src/Makefile.global |
|
||||||
|
|
||||||
OBJS = rtget.o rtproc.o rtree.o rtscan.o rtstrat.o
|
|
||||||
|
|
||||||
all: SUBSYS.o |
|
||||||
|
|
||||||
SUBSYS.o: $(OBJS) |
|
||||||
$(LD) $(LDREL) $(LDOUT) SUBSYS.o $(OBJS)
|
|
||||||
|
|
||||||
depend dep: |
|
||||||
$(CC) -MM $(CFLAGS) *.c >depend
|
|
||||||
|
|
||||||
clean: |
|
||||||
rm -f SUBSYS.o $(OBJS)
|
|
||||||
|
|
||||||
ifeq (depend,$(wildcard depend)) |
|
||||||
include depend |
|
||||||
endif |
|
||||||
|
|
@ -1,281 +0,0 @@ |
|||||||
/*-------------------------------------------------------------------------
|
|
||||||
* |
|
||||||
* rtget.c |
|
||||||
* fetch tuples from an rtree scan. |
|
||||||
* |
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group |
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California |
|
||||||
* |
|
||||||
* |
|
||||||
* IDENTIFICATION |
|
||||||
* $PostgreSQL: pgsql/src/backend/access/rtree/rtget.c,v 1.37 2005/10/15 02:49:09 momjian Exp $ |
|
||||||
* |
|
||||||
*------------------------------------------------------------------------- |
|
||||||
*/ |
|
||||||
|
|
||||||
#include "postgres.h" |
|
||||||
|
|
||||||
#include "access/iqual.h" |
|
||||||
#include "access/relscan.h" |
|
||||||
#include "access/rtree.h" |
|
||||||
#include "pgstat.h" |
|
||||||
|
|
||||||
|
|
||||||
static OffsetNumber findnext(IndexScanDesc s, OffsetNumber n, |
|
||||||
ScanDirection dir); |
|
||||||
static bool rtnext(IndexScanDesc s, ScanDirection dir); |
|
||||||
|
|
||||||
|
|
||||||
Datum |
|
||||||
rtgettuple(PG_FUNCTION_ARGS) |
|
||||||
{ |
|
||||||
IndexScanDesc s = (IndexScanDesc) PG_GETARG_POINTER(0); |
|
||||||
ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1); |
|
||||||
RTreeScanOpaque so = (RTreeScanOpaque) s->opaque; |
|
||||||
Page page; |
|
||||||
OffsetNumber offnum; |
|
||||||
|
|
||||||
/*
|
|
||||||
* If we've already produced a tuple and the executor has informed us that |
|
||||||
* it should be marked "killed", do so now. |
|
||||||
*/ |
|
||||||
if (s->kill_prior_tuple && ItemPointerIsValid(&(s->currentItemData))) |
|
||||||
{ |
|
||||||
offnum = ItemPointerGetOffsetNumber(&(s->currentItemData)); |
|
||||||
page = BufferGetPage(so->curbuf); |
|
||||||
PageGetItemId(page, offnum)->lp_flags |= LP_DELETE; |
|
||||||
SetBufferCommitInfoNeedsSave(so->curbuf); |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
* Get the next tuple that matches the search key; if asked to skip killed |
|
||||||
* tuples, find the first non-killed tuple that matches. Return as soon as |
|
||||||
* we've run out of matches or we've found an acceptable match. |
|
||||||
*/ |
|
||||||
for (;;) |
|
||||||
{ |
|
||||||
bool res = rtnext(s, dir); |
|
||||||
|
|
||||||
if (res && s->ignore_killed_tuples) |
|
||||||
{ |
|
||||||
offnum = ItemPointerGetOffsetNumber(&(s->currentItemData)); |
|
||||||
page = BufferGetPage(so->curbuf); |
|
||||||
if (ItemIdDeleted(PageGetItemId(page, offnum))) |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
PG_RETURN_BOOL(res); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
Datum |
|
||||||
rtgetmulti(PG_FUNCTION_ARGS) |
|
||||||
{ |
|
||||||
IndexScanDesc s = (IndexScanDesc) PG_GETARG_POINTER(0); |
|
||||||
ItemPointer tids = (ItemPointer) PG_GETARG_POINTER(1); |
|
||||||
int32 max_tids = PG_GETARG_INT32(2); |
|
||||||
int32 *returned_tids = (int32 *) PG_GETARG_POINTER(3); |
|
||||||
RTreeScanOpaque so = (RTreeScanOpaque) s->opaque; |
|
||||||
bool res = true; |
|
||||||
int32 ntids = 0; |
|
||||||
|
|
||||||
/* XXX generic implementation: loop around guts of rtgettuple */ |
|
||||||
while (ntids < max_tids) |
|
||||||
{ |
|
||||||
res = rtnext(s, ForwardScanDirection); |
|
||||||
if (res && s->ignore_killed_tuples) |
|
||||||
{ |
|
||||||
Page page; |
|
||||||
OffsetNumber offnum; |
|
||||||
|
|
||||||
offnum = ItemPointerGetOffsetNumber(&(s->currentItemData)); |
|
||||||
page = BufferGetPage(so->curbuf); |
|
||||||
if (ItemIdDeleted(PageGetItemId(page, offnum))) |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
if (!res) |
|
||||||
break; |
|
||||||
tids[ntids] = s->xs_ctup.t_self; |
|
||||||
ntids++; |
|
||||||
} |
|
||||||
|
|
||||||
*returned_tids = ntids; |
|
||||||
PG_RETURN_BOOL(res); |
|
||||||
} |
|
||||||
|
|
||||||
static bool |
|
||||||
rtnext(IndexScanDesc s, ScanDirection dir) |
|
||||||
{ |
|
||||||
Page p; |
|
||||||
OffsetNumber n; |
|
||||||
RTreePageOpaque po; |
|
||||||
RTreeScanOpaque so; |
|
||||||
|
|
||||||
so = (RTreeScanOpaque) s->opaque; |
|
||||||
|
|
||||||
if (!ItemPointerIsValid(&(s->currentItemData))) |
|
||||||
{ |
|
||||||
/* first call: start at the root */ |
|
||||||
Assert(BufferIsValid(so->curbuf) == false); |
|
||||||
so->curbuf = ReadBuffer(s->indexRelation, P_ROOT); |
|
||||||
pgstat_count_index_scan(&s->xs_pgstat_info); |
|
||||||
} |
|
||||||
|
|
||||||
p = BufferGetPage(so->curbuf); |
|
||||||
po = (RTreePageOpaque) PageGetSpecialPointer(p); |
|
||||||
|
|
||||||
if (!ItemPointerIsValid(&(s->currentItemData))) |
|
||||||
{ |
|
||||||
/* first call: start at first/last offset */ |
|
||||||
if (ScanDirectionIsForward(dir)) |
|
||||||
n = FirstOffsetNumber; |
|
||||||
else |
|
||||||
n = PageGetMaxOffsetNumber(p); |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
/* go on to the next offset */ |
|
||||||
n = ItemPointerGetOffsetNumber(&(s->currentItemData)); |
|
||||||
if (ScanDirectionIsForward(dir)) |
|
||||||
n = OffsetNumberNext(n); |
|
||||||
else |
|
||||||
n = OffsetNumberPrev(n); |
|
||||||
} |
|
||||||
|
|
||||||
for (;;) |
|
||||||
{ |
|
||||||
IndexTuple it; |
|
||||||
RTSTACK *stk; |
|
||||||
|
|
||||||
n = findnext(s, n, dir); |
|
||||||
|
|
||||||
/* no match on this page, so read in the next stack entry */ |
|
||||||
if (n == InvalidOffsetNumber) |
|
||||||
{ |
|
||||||
/* if out of stack entries, we're done */ |
|
||||||
if (so->s_stack == NULL) |
|
||||||
{ |
|
||||||
ReleaseBuffer(so->curbuf); |
|
||||||
so->curbuf = InvalidBuffer; |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
stk = so->s_stack; |
|
||||||
so->curbuf = ReleaseAndReadBuffer(so->curbuf, s->indexRelation, |
|
||||||
stk->rts_blk); |
|
||||||
p = BufferGetPage(so->curbuf); |
|
||||||
po = (RTreePageOpaque) PageGetSpecialPointer(p); |
|
||||||
|
|
||||||
if (ScanDirectionIsBackward(dir)) |
|
||||||
n = OffsetNumberPrev(stk->rts_child); |
|
||||||
else |
|
||||||
n = OffsetNumberNext(stk->rts_child); |
|
||||||
so->s_stack = stk->rts_parent; |
|
||||||
pfree(stk); |
|
||||||
|
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
if (po->flags & F_LEAF) |
|
||||||
{ |
|
||||||
ItemPointerSet(&(s->currentItemData), |
|
||||||
BufferGetBlockNumber(so->curbuf), |
|
||||||
n); |
|
||||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); |
|
||||||
s->xs_ctup.t_self = it->t_tid; |
|
||||||
return true; |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
BlockNumber blk; |
|
||||||
|
|
||||||
stk = (RTSTACK *) palloc(sizeof(RTSTACK)); |
|
||||||
stk->rts_child = n; |
|
||||||
stk->rts_blk = BufferGetBlockNumber(so->curbuf); |
|
||||||
stk->rts_parent = so->s_stack; |
|
||||||
so->s_stack = stk; |
|
||||||
|
|
||||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); |
|
||||||
blk = ItemPointerGetBlockNumber(&(it->t_tid)); |
|
||||||
|
|
||||||
/*
|
|
||||||
* Note that we release the pin on the page as we descend down the |
|
||||||
* tree, even though there's a good chance we'll eventually need |
|
||||||
* to re-read the buffer later in this scan. This may or may not |
|
||||||
* be optimal, but it doesn't seem likely to make a huge |
|
||||||
* performance difference either way. |
|
||||||
*/ |
|
||||||
so->curbuf = ReleaseAndReadBuffer(so->curbuf, s->indexRelation, blk); |
|
||||||
p = BufferGetPage(so->curbuf); |
|
||||||
po = (RTreePageOpaque) PageGetSpecialPointer(p); |
|
||||||
|
|
||||||
if (ScanDirectionIsBackward(dir)) |
|
||||||
n = PageGetMaxOffsetNumber(p); |
|
||||||
else |
|
||||||
n = FirstOffsetNumber; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
* Return the offset of the next matching index entry. We begin the |
|
||||||
* search at offset "n" and search for matches in the direction |
|
||||||
* "dir". If no more matching entries are found on the page, |
|
||||||
* InvalidOffsetNumber is returned. |
|
||||||
*/ |
|
||||||
static OffsetNumber |
|
||||||
findnext(IndexScanDesc s, OffsetNumber n, ScanDirection dir) |
|
||||||
{ |
|
||||||
OffsetNumber maxoff; |
|
||||||
IndexTuple it; |
|
||||||
RTreePageOpaque po; |
|
||||||
RTreeScanOpaque so; |
|
||||||
Page p; |
|
||||||
|
|
||||||
so = (RTreeScanOpaque) s->opaque; |
|
||||||
p = BufferGetPage(so->curbuf); |
|
||||||
|
|
||||||
maxoff = PageGetMaxOffsetNumber(p); |
|
||||||
po = (RTreePageOpaque) PageGetSpecialPointer(p); |
|
||||||
|
|
||||||
/*
|
|
||||||
* If we modified the index during the scan, we may have a pointer to a |
|
||||||
* ghost tuple, before the scan. If this is the case, back up one. |
|
||||||
*/ |
|
||||||
|
|
||||||
if (so->s_flags & RTS_CURBEFORE) |
|
||||||
{ |
|
||||||
so->s_flags &= ~RTS_CURBEFORE; |
|
||||||
n = OffsetNumberPrev(n); |
|
||||||
} |
|
||||||
|
|
||||||
while (n >= FirstOffsetNumber && n <= maxoff) |
|
||||||
{ |
|
||||||
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); |
|
||||||
if (po->flags & F_LEAF) |
|
||||||
{ |
|
||||||
if (index_keytest(it, |
|
||||||
RelationGetDescr(s->indexRelation), |
|
||||||
s->numberOfKeys, s->keyData)) |
|
||||||
break; |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
if (index_keytest(it, |
|
||||||
RelationGetDescr(s->indexRelation), |
|
||||||
so->s_internalNKey, so->s_internalKey)) |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
if (ScanDirectionIsBackward(dir)) |
|
||||||
n = OffsetNumberPrev(n); |
|
||||||
else |
|
||||||
n = OffsetNumberNext(n); |
|
||||||
} |
|
||||||
|
|
||||||
if (n >= FirstOffsetNumber && n <= maxoff) |
|
||||||
return n; /* found a match on this page */ |
|
||||||
else |
|
||||||
return InvalidOffsetNumber; /* no match, go to next page */ |
|
||||||
} |
|
@ -1,175 +0,0 @@ |
|||||||
/*-------------------------------------------------------------------------
|
|
||||||
* |
|
||||||
* rtproc.c |
|
||||||
* pg_amproc entries for rtrees. |
|
||||||
* |
|
||||||
* NOTE: for largely-historical reasons, the intersection functions should |
|
||||||
* return a NULL pointer (*not* an SQL null value) to indicate "no |
|
||||||
* intersection". The size functions must be prepared to accept such |
|
||||||
* a pointer and return 0. This convention means that only pass-by-reference |
|
||||||
* data types can be used as the output of the union and intersection |
|
||||||
* routines, but that's not a big problem. |
|
||||||
* |
|
||||||
* |
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group |
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California |
|
||||||
* |
|
||||||
* IDENTIFICATION |
|
||||||
* $PostgreSQL: pgsql/src/backend/access/rtree/rtproc.c,v 1.43 2005/10/15 02:49:09 momjian Exp $ |
|
||||||
* |
|
||||||
*------------------------------------------------------------------------- |
|
||||||
*/ |
|
||||||
|
|
||||||
#include "postgres.h" |
|
||||||
|
|
||||||
#include "utils/geo_decls.h" |
|
||||||
|
|
||||||
|
|
||||||
Datum |
|
||||||
rt_box_union(PG_FUNCTION_ARGS) |
|
||||||
{ |
|
||||||
BOX *a = PG_GETARG_BOX_P(0); |
|
||||||
BOX *b = PG_GETARG_BOX_P(1); |
|
||||||
BOX *n; |
|
||||||
|
|
||||||
n = (BOX *) palloc(sizeof(BOX)); |
|
||||||
|
|
||||||
n->high.x = Max(a->high.x, b->high.x); |
|
||||||
n->high.y = Max(a->high.y, b->high.y); |
|
||||||
n->low.x = Min(a->low.x, b->low.x); |
|
||||||
n->low.y = Min(a->low.y, b->low.y); |
|
||||||
|
|
||||||
PG_RETURN_BOX_P(n); |
|
||||||
} |
|
||||||
|
|
||||||
Datum |
|
||||||
rt_box_inter(PG_FUNCTION_ARGS) |
|
||||||
{ |
|
||||||
BOX *a = PG_GETARG_BOX_P(0); |
|
||||||
BOX *b = PG_GETARG_BOX_P(1); |
|
||||||
BOX *n; |
|
||||||
|
|
||||||
n = (BOX *) palloc(sizeof(BOX)); |
|
||||||
|
|
||||||
n->high.x = Min(a->high.x, b->high.x); |
|
||||||
n->high.y = Min(a->high.y, b->high.y); |
|
||||||
n->low.x = Max(a->low.x, b->low.x); |
|
||||||
n->low.y = Max(a->low.y, b->low.y); |
|
||||||
|
|
||||||
if (n->high.x < n->low.x || n->high.y < n->low.y) |
|
||||||
{ |
|
||||||
pfree(n); |
|
||||||
/* Indicate "no intersection" by returning NULL pointer */ |
|
||||||
n = NULL; |
|
||||||
} |
|
||||||
|
|
||||||
PG_RETURN_BOX_P(n); |
|
||||||
} |
|
||||||
|
|
||||||
Datum |
|
||||||
rt_box_size(PG_FUNCTION_ARGS) |
|
||||||
{ |
|
||||||
BOX *a = PG_GETARG_BOX_P(0); |
|
||||||
|
|
||||||
/* NB: size is an output argument */ |
|
||||||
float *size = (float *) PG_GETARG_POINTER(1); |
|
||||||
|
|
||||||
if (a == NULL || a->high.x <= a->low.x || a->high.y <= a->low.y) |
|
||||||
*size = 0.0; |
|
||||||
else |
|
||||||
*size = (float) ((a->high.x - a->low.x) * (a->high.y - a->low.y)); |
|
||||||
|
|
||||||
PG_RETURN_VOID(); |
|
||||||
} |
|
||||||
|
|
||||||
Datum |
|
||||||
rt_poly_union(PG_FUNCTION_ARGS) |
|
||||||
{ |
|
||||||
POLYGON *a = PG_GETARG_POLYGON_P(0); |
|
||||||
POLYGON *b = PG_GETARG_POLYGON_P(1); |
|
||||||
POLYGON *p; |
|
||||||
|
|
||||||
p = (POLYGON *) palloc0(sizeof(POLYGON)); /* zero any holes */ |
|
||||||
p->size = sizeof(POLYGON); |
|
||||||
p->npts = 0; |
|
||||||
p->boundbox.high.x = Max(a->boundbox.high.x, b->boundbox.high.x); |
|
||||||
p->boundbox.high.y = Max(a->boundbox.high.y, b->boundbox.high.y); |
|
||||||
p->boundbox.low.x = Min(a->boundbox.low.x, b->boundbox.low.x); |
|
||||||
p->boundbox.low.y = Min(a->boundbox.low.y, b->boundbox.low.y); |
|
||||||
|
|
||||||
/* Avoid leaking memory when handed toasted input. */ |
|
||||||
PG_FREE_IF_COPY(a, 0); |
|
||||||
PG_FREE_IF_COPY(b, 1); |
|
||||||
|
|
||||||
PG_RETURN_POLYGON_P(p); |
|
||||||
} |
|
||||||
|
|
||||||
Datum |
|
||||||
rt_poly_inter(PG_FUNCTION_ARGS) |
|
||||||
{ |
|
||||||
POLYGON *a = PG_GETARG_POLYGON_P(0); |
|
||||||
POLYGON *b = PG_GETARG_POLYGON_P(1); |
|
||||||
POLYGON *p; |
|
||||||
|
|
||||||
p = (POLYGON *) palloc0(sizeof(POLYGON)); /* zero any holes */ |
|
||||||
p->size = sizeof(POLYGON); |
|
||||||
p->npts = 0; |
|
||||||
p->boundbox.high.x = Min(a->boundbox.high.x, b->boundbox.high.x); |
|
||||||
p->boundbox.high.y = Min(a->boundbox.high.y, b->boundbox.high.y); |
|
||||||
p->boundbox.low.x = Max(a->boundbox.low.x, b->boundbox.low.x); |
|
||||||
p->boundbox.low.y = Max(a->boundbox.low.y, b->boundbox.low.y); |
|
||||||
|
|
||||||
if (p->boundbox.high.x < p->boundbox.low.x || |
|
||||||
p->boundbox.high.y < p->boundbox.low.y) |
|
||||||
{ |
|
||||||
pfree(p); |
|
||||||
/* Indicate "no intersection" by returning NULL pointer */ |
|
||||||
p = NULL; |
|
||||||
} |
|
||||||
|
|
||||||
/* Avoid leaking memory when handed toasted input. */ |
|
||||||
PG_FREE_IF_COPY(a, 0); |
|
||||||
PG_FREE_IF_COPY(b, 1); |
|
||||||
|
|
||||||
PG_RETURN_POLYGON_P(p); |
|
||||||
} |
|
||||||
|
|
||||||
Datum |
|
||||||
rt_poly_size(PG_FUNCTION_ARGS) |
|
||||||
{ |
|
||||||
Pointer aptr = PG_GETARG_POINTER(0); |
|
||||||
|
|
||||||
/* NB: size is an output argument */ |
|
||||||
float *size = (float *) PG_GETARG_POINTER(1); |
|
||||||
POLYGON *a; |
|
||||||
double xdim, |
|
||||||
ydim; |
|
||||||
|
|
||||||
/*
|
|
||||||
* Can't just use GETARG because of possibility that input is NULL; since |
|
||||||
* POLYGON is toastable, GETARG will try to inspect its value |
|
||||||
*/ |
|
||||||
if (aptr == NULL) |
|
||||||
{ |
|
||||||
*size = 0.0; |
|
||||||
PG_RETURN_VOID(); |
|
||||||
} |
|
||||||
/* Now safe to apply GETARG */ |
|
||||||
a = PG_GETARG_POLYGON_P(0); |
|
||||||
|
|
||||||
if (a->boundbox.high.x <= a->boundbox.low.x || |
|
||||||
a->boundbox.high.y <= a->boundbox.low.y) |
|
||||||
*size = 0.0; |
|
||||||
else |
|
||||||
{ |
|
||||||
xdim = (a->boundbox.high.x - a->boundbox.low.x); |
|
||||||
ydim = (a->boundbox.high.y - a->boundbox.low.y); |
|
||||||
|
|
||||||
*size = (float) (xdim * ydim); |
|
||||||
} |
|
||||||
|
|
||||||
/* Avoid leaking memory when handed toasted input. */ |
|
||||||
PG_FREE_IF_COPY(a, 0); |
|
||||||
|
|
||||||
PG_RETURN_VOID(); |
|
||||||
} |
|
File diff suppressed because it is too large
Load Diff
@ -1,493 +0,0 @@ |
|||||||
/*-------------------------------------------------------------------------
|
|
||||||
* |
|
||||||
* rtscan.c |
|
||||||
* routines to manage scans on index relations |
|
||||||
* |
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group |
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California |
|
||||||
* |
|
||||||
* |
|
||||||
* IDENTIFICATION |
|
||||||
* $PostgreSQL: pgsql/src/backend/access/rtree/rtscan.c,v 1.60 2005/10/15 02:49:09 momjian Exp $ |
|
||||||
* |
|
||||||
*------------------------------------------------------------------------- |
|
||||||
*/ |
|
||||||
|
|
||||||
#include "postgres.h" |
|
||||||
|
|
||||||
#include "access/genam.h" |
|
||||||
#include "access/rtree.h" |
|
||||||
#include "utils/lsyscache.h" |
|
||||||
#include "utils/resowner.h" |
|
||||||
|
|
||||||
|
|
||||||
/* routines defined and used here */ |
|
||||||
static void rtregscan(IndexScanDesc s); |
|
||||||
static void rtdropscan(IndexScanDesc s); |
|
||||||
static void rtadjone(IndexScanDesc s, int op, BlockNumber blkno, |
|
||||||
OffsetNumber offnum); |
|
||||||
static void adjuststack(RTSTACK *stk, BlockNumber blkno); |
|
||||||
static void adjustiptr(IndexScanDesc s, ItemPointer iptr, |
|
||||||
int op, BlockNumber blkno, OffsetNumber offnum); |
|
||||||
|
|
||||||
/*
|
|
||||||
* Whenever we start an rtree scan in a backend, we register it in private |
|
||||||
* space. Then if the rtree index gets updated, we check all registered |
|
||||||
* scans and adjust them if the tuple they point at got moved by the |
|
||||||
* update. We only need to do this in private space, because when we update |
|
||||||
* an rtree we have a write lock on the tree, so no other process can have |
|
||||||
* any locks at all on it. A single transaction can have write and read |
|
||||||
* locks on the same object, so that's why we need to handle this case. |
|
||||||
*/ |
|
||||||
|
|
||||||
typedef struct RTScanListData |
|
||||||
{ |
|
||||||
IndexScanDesc rtsl_scan; |
|
||||||
ResourceOwner rtsl_owner; |
|
||||||
struct RTScanListData *rtsl_next; |
|
||||||
} RTScanListData; |
|
||||||
|
|
||||||
typedef RTScanListData *RTScanList; |
|
||||||
|
|
||||||
/* pointer to list of local scans on rtrees */ |
|
||||||
static RTScanList RTScans = NULL; |
|
||||||
|
|
||||||
Datum |
|
||||||
rtbeginscan(PG_FUNCTION_ARGS) |
|
||||||
{ |
|
||||||
Relation r = (Relation) PG_GETARG_POINTER(0); |
|
||||||
int nkeys = PG_GETARG_INT32(1); |
|
||||||
ScanKey key = (ScanKey) PG_GETARG_POINTER(2); |
|
||||||
IndexScanDesc s; |
|
||||||
|
|
||||||
s = RelationGetIndexScan(r, nkeys, key); |
|
||||||
|
|
||||||
rtregscan(s); |
|
||||||
|
|
||||||
PG_RETURN_POINTER(s); |
|
||||||
} |
|
||||||
|
|
||||||
Datum |
|
||||||
rtrescan(PG_FUNCTION_ARGS) |
|
||||||
{ |
|
||||||
IndexScanDesc s = (IndexScanDesc) PG_GETARG_POINTER(0); |
|
||||||
ScanKey key = (ScanKey) PG_GETARG_POINTER(1); |
|
||||||
RTreeScanOpaque p; |
|
||||||
int i; |
|
||||||
|
|
||||||
/*
|
|
||||||
* Clear all the pointers. |
|
||||||
*/ |
|
||||||
ItemPointerSetInvalid(&s->currentItemData); |
|
||||||
ItemPointerSetInvalid(&s->currentMarkData); |
|
||||||
|
|
||||||
p = (RTreeScanOpaque) s->opaque; |
|
||||||
if (p != NULL) |
|
||||||
{ |
|
||||||
/* rescan an existing indexscan --- reset state */ |
|
||||||
freestack(p->s_stack); |
|
||||||
freestack(p->s_markstk); |
|
||||||
p->s_stack = p->s_markstk = NULL; |
|
||||||
p->s_flags = 0x0; |
|
||||||
/* drop pins on buffers -- no locks held */ |
|
||||||
if (BufferIsValid(p->curbuf)) |
|
||||||
{ |
|
||||||
ReleaseBuffer(p->curbuf); |
|
||||||
p->curbuf = InvalidBuffer; |
|
||||||
} |
|
||||||
if (BufferIsValid(p->markbuf)) |
|
||||||
{ |
|
||||||
ReleaseBuffer(p->markbuf); |
|
||||||
p->markbuf = InvalidBuffer; |
|
||||||
} |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
/* initialize opaque data */ |
|
||||||
p = (RTreeScanOpaque) palloc(sizeof(RTreeScanOpaqueData)); |
|
||||||
p->s_stack = p->s_markstk = NULL; |
|
||||||
p->curbuf = p->markbuf = InvalidBuffer; |
|
||||||
p->s_internalNKey = s->numberOfKeys; |
|
||||||
p->s_flags = 0x0; |
|
||||||
s->opaque = p; |
|
||||||
if (s->numberOfKeys > 0) |
|
||||||
p->s_internalKey = (ScanKey) palloc(sizeof(ScanKeyData) * s->numberOfKeys); |
|
||||||
} |
|
||||||
|
|
||||||
/* Update scan key, if a new one is given */ |
|
||||||
if (key && s->numberOfKeys > 0) |
|
||||||
{ |
|
||||||
memmove(s->keyData, |
|
||||||
key, |
|
||||||
s->numberOfKeys * sizeof(ScanKeyData)); |
|
||||||
|
|
||||||
/*
|
|
||||||
* Scans on internal pages use different operators than they do on |
|
||||||
* leaf pages. For example, if the user wants all boxes that exactly |
|
||||||
* match (x1,y1,x2,y2), then on internal pages we need to find all |
|
||||||
* boxes that contain (x1,y1,x2,y2). rtstrat.c knows how to pick the |
|
||||||
* opclass member to use for internal pages. In some cases we need to |
|
||||||
* negate the result of the opclass member. |
|
||||||
*/ |
|
||||||
for (i = 0; i < s->numberOfKeys; i++) |
|
||||||
{ |
|
||||||
AttrNumber attno = s->keyData[i].sk_attno; |
|
||||||
Oid opclass; |
|
||||||
Oid subtype; |
|
||||||
StrategyNumber orig_strategy; |
|
||||||
StrategyNumber int_strategy; |
|
||||||
Oid int_oper; |
|
||||||
RegProcedure int_proc; |
|
||||||
int int_flags; |
|
||||||
|
|
||||||
opclass = s->indexRelation->rd_indclass->values[attno - 1]; |
|
||||||
subtype = s->keyData[i].sk_subtype; |
|
||||||
orig_strategy = s->keyData[i].sk_strategy; |
|
||||||
int_strategy = RTMapToInternalOperator(orig_strategy); |
|
||||||
int_oper = get_opclass_member(opclass, subtype, int_strategy); |
|
||||||
Assert(OidIsValid(int_oper)); |
|
||||||
int_proc = get_opcode(int_oper); |
|
||||||
int_flags = s->keyData[i].sk_flags; |
|
||||||
if (RTMapToInternalNegate(orig_strategy)) |
|
||||||
int_flags |= SK_NEGATE; |
|
||||||
ScanKeyEntryInitialize(&(p->s_internalKey[i]), |
|
||||||
int_flags, |
|
||||||
attno, |
|
||||||
int_strategy, |
|
||||||
subtype, |
|
||||||
int_proc, |
|
||||||
s->keyData[i].sk_argument); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
PG_RETURN_VOID(); |
|
||||||
} |
|
||||||
|
|
||||||
Datum |
|
||||||
rtmarkpos(PG_FUNCTION_ARGS) |
|
||||||
{ |
|
||||||
IndexScanDesc s = (IndexScanDesc) PG_GETARG_POINTER(0); |
|
||||||
RTreeScanOpaque p; |
|
||||||
RTSTACK *o, |
|
||||||
*n, |
|
||||||
*tmp; |
|
||||||
|
|
||||||
s->currentMarkData = s->currentItemData; |
|
||||||
p = (RTreeScanOpaque) s->opaque; |
|
||||||
if (p->s_flags & RTS_CURBEFORE) |
|
||||||
p->s_flags |= RTS_MRKBEFORE; |
|
||||||
else |
|
||||||
p->s_flags &= ~RTS_MRKBEFORE; |
|
||||||
|
|
||||||
o = NULL; |
|
||||||
n = p->s_stack; |
|
||||||
|
|
||||||
/* copy the parent stack from the current item data */ |
|
||||||
while (n != NULL) |
|
||||||
{ |
|
||||||
tmp = (RTSTACK *) palloc(sizeof(RTSTACK)); |
|
||||||
tmp->rts_child = n->rts_child; |
|
||||||
tmp->rts_blk = n->rts_blk; |
|
||||||
tmp->rts_parent = o; |
|
||||||
o = tmp; |
|
||||||
n = n->rts_parent; |
|
||||||
} |
|
||||||
|
|
||||||
freestack(p->s_markstk); |
|
||||||
p->s_markstk = o; |
|
||||||
|
|
||||||
/* Update markbuf: make sure to bump ref count on curbuf */ |
|
||||||
if (BufferIsValid(p->markbuf)) |
|
||||||
{ |
|
||||||
ReleaseBuffer(p->markbuf); |
|
||||||
p->markbuf = InvalidBuffer; |
|
||||||
} |
|
||||||
if (BufferIsValid(p->curbuf)) |
|
||||||
{ |
|
||||||
IncrBufferRefCount(p->curbuf); |
|
||||||
p->markbuf = p->curbuf; |
|
||||||
} |
|
||||||
|
|
||||||
PG_RETURN_VOID(); |
|
||||||
} |
|
||||||
|
|
||||||
Datum |
|
||||||
rtrestrpos(PG_FUNCTION_ARGS) |
|
||||||
{ |
|
||||||
IndexScanDesc s = (IndexScanDesc) PG_GETARG_POINTER(0); |
|
||||||
RTreeScanOpaque p; |
|
||||||
RTSTACK *o, |
|
||||||
*n, |
|
||||||
*tmp; |
|
||||||
|
|
||||||
s->currentItemData = s->currentMarkData; |
|
||||||
p = (RTreeScanOpaque) s->opaque; |
|
||||||
if (p->s_flags & RTS_MRKBEFORE) |
|
||||||
p->s_flags |= RTS_CURBEFORE; |
|
||||||
else |
|
||||||
p->s_flags &= ~RTS_CURBEFORE; |
|
||||||
|
|
||||||
o = NULL; |
|
||||||
n = p->s_markstk; |
|
||||||
|
|
||||||
/* copy the parent stack from the current item data */ |
|
||||||
while (n != NULL) |
|
||||||
{ |
|
||||||
tmp = (RTSTACK *) palloc(sizeof(RTSTACK)); |
|
||||||
tmp->rts_child = n->rts_child; |
|
||||||
tmp->rts_blk = n->rts_blk; |
|
||||||
tmp->rts_parent = o; |
|
||||||
o = tmp; |
|
||||||
n = n->rts_parent; |
|
||||||
} |
|
||||||
|
|
||||||
freestack(p->s_stack); |
|
||||||
p->s_stack = o; |
|
||||||
|
|
||||||
/* Update curbuf; be sure to bump ref count on markbuf */ |
|
||||||
if (BufferIsValid(p->curbuf)) |
|
||||||
{ |
|
||||||
ReleaseBuffer(p->curbuf); |
|
||||||
p->curbuf = InvalidBuffer; |
|
||||||
} |
|
||||||
if (BufferIsValid(p->markbuf)) |
|
||||||
{ |
|
||||||
IncrBufferRefCount(p->markbuf); |
|
||||||
p->curbuf = p->markbuf; |
|
||||||
} |
|
||||||
|
|
||||||
PG_RETURN_VOID(); |
|
||||||
} |
|
||||||
|
|
||||||
Datum |
|
||||||
rtendscan(PG_FUNCTION_ARGS) |
|
||||||
{ |
|
||||||
IndexScanDesc s = (IndexScanDesc) PG_GETARG_POINTER(0); |
|
||||||
RTreeScanOpaque p; |
|
||||||
|
|
||||||
p = (RTreeScanOpaque) s->opaque; |
|
||||||
|
|
||||||
if (p != NULL) |
|
||||||
{ |
|
||||||
freestack(p->s_stack); |
|
||||||
freestack(p->s_markstk); |
|
||||||
if (BufferIsValid(p->curbuf)) |
|
||||||
ReleaseBuffer(p->curbuf); |
|
||||||
if (BufferIsValid(p->markbuf)) |
|
||||||
ReleaseBuffer(p->markbuf); |
|
||||||
pfree(s->opaque); |
|
||||||
} |
|
||||||
|
|
||||||
rtdropscan(s); |
|
||||||
|
|
||||||
PG_RETURN_VOID(); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
rtregscan(IndexScanDesc s) |
|
||||||
{ |
|
||||||
RTScanList l; |
|
||||||
|
|
||||||
l = (RTScanList) palloc(sizeof(RTScanListData)); |
|
||||||
l->rtsl_scan = s; |
|
||||||
l->rtsl_owner = CurrentResourceOwner; |
|
||||||
l->rtsl_next = RTScans; |
|
||||||
RTScans = l; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
rtdropscan(IndexScanDesc s) |
|
||||||
{ |
|
||||||
RTScanList l; |
|
||||||
RTScanList prev; |
|
||||||
|
|
||||||
prev = NULL; |
|
||||||
|
|
||||||
for (l = RTScans; |
|
||||||
l != NULL && l->rtsl_scan != s; |
|
||||||
l = l->rtsl_next) |
|
||||||
prev = l; |
|
||||||
|
|
||||||
if (l == NULL) |
|
||||||
elog(ERROR, "rtree scan list corrupted -- could not find 0x%p", |
|
||||||
(void *) s); |
|
||||||
|
|
||||||
if (prev == NULL) |
|
||||||
RTScans = l->rtsl_next; |
|
||||||
else |
|
||||||
prev->rtsl_next = l->rtsl_next; |
|
||||||
|
|
||||||
pfree(l); |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
* ReleaseResources_rtree() --- clean up rtree subsystem resources. |
|
||||||
* |
|
||||||
* This is here because it needs to touch this module's static var RTScans. |
|
||||||
*/ |
|
||||||
void |
|
||||||
ReleaseResources_rtree(void) |
|
||||||
{ |
|
||||||
RTScanList l; |
|
||||||
RTScanList prev; |
|
||||||
RTScanList next; |
|
||||||
|
|
||||||
/*
|
|
||||||
* Note: this should be a no-op during normal query shutdown. However, in |
|
||||||
* an abort situation ExecutorEnd is not called and so there may be open |
|
||||||
* index scans to clean up. |
|
||||||
*/ |
|
||||||
prev = NULL; |
|
||||||
|
|
||||||
for (l = RTScans; l != NULL; l = next) |
|
||||||
{ |
|
||||||
next = l->rtsl_next; |
|
||||||
if (l->rtsl_owner == CurrentResourceOwner) |
|
||||||
{ |
|
||||||
if (prev == NULL) |
|
||||||
RTScans = next; |
|
||||||
else |
|
||||||
prev->rtsl_next = next; |
|
||||||
|
|
||||||
pfree(l); |
|
||||||
/* prev does not change */ |
|
||||||
} |
|
||||||
else |
|
||||||
prev = l; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
rtadjscans(Relation r, int op, BlockNumber blkno, OffsetNumber offnum) |
|
||||||
{ |
|
||||||
RTScanList l; |
|
||||||
Oid relid; |
|
||||||
|
|
||||||
relid = RelationGetRelid(r); |
|
||||||
for (l = RTScans; l != NULL; l = l->rtsl_next) |
|
||||||
{ |
|
||||||
if (RelationGetRelid(l->rtsl_scan->indexRelation) == relid) |
|
||||||
rtadjone(l->rtsl_scan, op, blkno, offnum); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
* rtadjone() -- adjust one scan for update. |
|
||||||
* |
|
||||||
* By here, the scan passed in is on a modified relation. Op tells |
|
||||||
* us what the modification is, and blkno and offind tell us what |
|
||||||
* block and offset index were affected. This routine checks the |
|
||||||
* current and marked positions, and the current and marked stacks, |
|
||||||
* to see if any stored location needs to be changed because of the |
|
||||||
* update. If so, we make the change here. |
|
||||||
*/ |
|
||||||
static void |
|
||||||
rtadjone(IndexScanDesc s, |
|
||||||
int op, |
|
||||||
BlockNumber blkno, |
|
||||||
OffsetNumber offnum) |
|
||||||
{ |
|
||||||
RTreeScanOpaque so; |
|
||||||
|
|
||||||
adjustiptr(s, &(s->currentItemData), op, blkno, offnum); |
|
||||||
adjustiptr(s, &(s->currentMarkData), op, blkno, offnum); |
|
||||||
|
|
||||||
so = (RTreeScanOpaque) s->opaque; |
|
||||||
|
|
||||||
if (op == RTOP_SPLIT) |
|
||||||
{ |
|
||||||
adjuststack(so->s_stack, blkno); |
|
||||||
adjuststack(so->s_markstk, blkno); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
* adjustiptr() -- adjust current and marked item pointers in the scan |
|
||||||
* |
|
||||||
* Depending on the type of update and the place it happened, we |
|
||||||
* need to do nothing, to back up one record, or to start over on |
|
||||||
* the same page. |
|
||||||
*/ |
|
||||||
static void |
|
||||||
adjustiptr(IndexScanDesc s, |
|
||||||
ItemPointer iptr, |
|
||||||
int op, |
|
||||||
BlockNumber blkno, |
|
||||||
OffsetNumber offnum) |
|
||||||
{ |
|
||||||
OffsetNumber curoff; |
|
||||||
RTreeScanOpaque so; |
|
||||||
|
|
||||||
if (ItemPointerIsValid(iptr)) |
|
||||||
{ |
|
||||||
if (ItemPointerGetBlockNumber(iptr) == blkno) |
|
||||||
{ |
|
||||||
curoff = ItemPointerGetOffsetNumber(iptr); |
|
||||||
so = (RTreeScanOpaque) s->opaque; |
|
||||||
|
|
||||||
switch (op) |
|
||||||
{ |
|
||||||
case RTOP_DEL: |
|
||||||
/* back up one if we need to */ |
|
||||||
if (curoff >= offnum) |
|
||||||
{ |
|
||||||
|
|
||||||
if (curoff > FirstOffsetNumber) |
|
||||||
{ |
|
||||||
/* just adjust the item pointer */ |
|
||||||
ItemPointerSet(iptr, blkno, OffsetNumberPrev(curoff)); |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
/*
|
|
||||||
* remember that we're before the current tuple |
|
||||||
*/ |
|
||||||
ItemPointerSet(iptr, blkno, FirstOffsetNumber); |
|
||||||
if (iptr == &(s->currentItemData)) |
|
||||||
so->s_flags |= RTS_CURBEFORE; |
|
||||||
else |
|
||||||
so->s_flags |= RTS_MRKBEFORE; |
|
||||||
} |
|
||||||
} |
|
||||||
break; |
|
||||||
|
|
||||||
case RTOP_SPLIT: |
|
||||||
/* back to start of page on split */ |
|
||||||
ItemPointerSet(iptr, blkno, FirstOffsetNumber); |
|
||||||
if (iptr == &(s->currentItemData)) |
|
||||||
so->s_flags &= ~RTS_CURBEFORE; |
|
||||||
else |
|
||||||
so->s_flags &= ~RTS_MRKBEFORE; |
|
||||||
break; |
|
||||||
|
|
||||||
default: |
|
||||||
elog(ERROR, "unrecognized operation in rtree scan adjust: %d", op); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
* adjuststack() -- adjust the supplied stack for a split on a page in |
|
||||||
* the index we're scanning. |
|
||||||
* |
|
||||||
* If a page on our parent stack has split, we need to back up to the |
|
||||||
* beginning of the page and rescan it. The reason for this is that |
|
||||||
* the split algorithm for rtrees doesn't order tuples in any useful |
|
||||||
* way on a single page. This means on that a split, we may wind up |
|
||||||
* looking at some heap tuples more than once. This is handled in the |
|
||||||
* access method update code for heaps; if we've modified the tuple we |
|
||||||
* are looking at already in this transaction, we ignore the update |
|
||||||
* request. |
|
||||||
*/ |
|
||||||
static void |
|
||||||
adjuststack(RTSTACK *stk, BlockNumber blkno) |
|
||||||
{ |
|
||||||
while (stk != NULL) |
|
||||||
{ |
|
||||||
if (stk->rts_blk == blkno) |
|
||||||
stk->rts_child = FirstOffsetNumber; |
|
||||||
|
|
||||||
stk = stk->rts_parent; |
|
||||||
} |
|
||||||
} |
|
@ -1,79 +0,0 @@ |
|||||||
/*-------------------------------------------------------------------------
|
|
||||||
* |
|
||||||
* rtstrat.c |
|
||||||
* strategy map data for rtrees. |
|
||||||
* |
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group |
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California |
|
||||||
* |
|
||||||
* |
|
||||||
* IDENTIFICATION |
|
||||||
* $PostgreSQL: pgsql/src/backend/access/rtree/rtstrat.c,v 1.27 2005/06/24 20:53:30 tgl Exp $ |
|
||||||
* |
|
||||||
*------------------------------------------------------------------------- |
|
||||||
*/ |
|
||||||
|
|
||||||
#include "postgres.h" |
|
||||||
|
|
||||||
#include "access/rtree.h" |
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Here's something peculiar to rtrees that doesn't apply to most other |
|
||||||
* indexing structures: When we're searching a tree for a given value, we |
|
||||||
* can't do the same sorts of comparisons on internal node entries as we |
|
||||||
* do at leaves. The reason is that if we're looking for (say) all boxes |
|
||||||
* that are the same as (0,0,10,10), then we need to find all leaf pages |
|
||||||
* that overlap that region. So internally we search for overlap, and at |
|
||||||
* the leaf we search for equality. |
|
||||||
* |
|
||||||
* This array maps leaf search operators to the internal search operators. |
|
||||||
*/ |
|
||||||
static const StrategyNumber RTOperMap[RTNStrategies] = { |
|
||||||
RTOverRightStrategyNumber, /* left */ |
|
||||||
RTRightStrategyNumber, /* overleft */ |
|
||||||
RTOverlapStrategyNumber, /* overlap */ |
|
||||||
RTLeftStrategyNumber, /* overright */ |
|
||||||
RTOverLeftStrategyNumber, /* right */ |
|
||||||
RTContainsStrategyNumber, /* same */ |
|
||||||
RTContainsStrategyNumber, /* contains */ |
|
||||||
RTOverlapStrategyNumber, /* contained-by */ |
|
||||||
RTAboveStrategyNumber, /* overbelow */ |
|
||||||
RTOverAboveStrategyNumber, /* below */ |
|
||||||
RTOverBelowStrategyNumber, /* above */ |
|
||||||
RTBelowStrategyNumber /* overabove */ |
|
||||||
}; |
|
||||||
|
|
||||||
/*
|
|
||||||
* We may need to negate the result of the selected operator. (This could |
|
||||||
* be avoided by expanding the set of operators required for an opclass.) |
|
||||||
*/ |
|
||||||
static const bool RTNegateMap[RTNStrategies] = { |
|
||||||
true, /* left */ |
|
||||||
true, /* overleft */ |
|
||||||
false, /* overlap */ |
|
||||||
true, /* overright */ |
|
||||||
true, /* right */ |
|
||||||
false, /* same */ |
|
||||||
false, /* contains */ |
|
||||||
false, /* contained-by */ |
|
||||||
true, /* overbelow */ |
|
||||||
true, /* below */ |
|
||||||
true, /* above */ |
|
||||||
true /* overabove */ |
|
||||||
}; |
|
||||||
|
|
||||||
|
|
||||||
StrategyNumber |
|
||||||
RTMapToInternalOperator(StrategyNumber strat) |
|
||||||
{ |
|
||||||
Assert(strat > 0 && strat <= RTNStrategies); |
|
||||||
return RTOperMap[strat - 1]; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
RTMapToInternalNegate(StrategyNumber strat) |
|
||||||
{ |
|
||||||
Assert(strat > 0 && strat <= RTNStrategies); |
|
||||||
return RTNegateMap[strat - 1]; |
|
||||||
} |
|
@ -1,145 +0,0 @@ |
|||||||
/*-------------------------------------------------------------------------
|
|
||||||
* |
|
||||||
* rtree.h |
|
||||||
* common declarations for the rtree access method code. |
|
||||||
* |
|
||||||
* |
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group |
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California |
|
||||||
* |
|
||||||
* $PostgreSQL: pgsql/src/include/access/rtree.h,v 1.41 2005/06/24 20:53:31 tgl Exp $ |
|
||||||
* |
|
||||||
*------------------------------------------------------------------------- |
|
||||||
*/ |
|
||||||
#ifndef RTREE_H |
|
||||||
#define RTREE_H |
|
||||||
|
|
||||||
#include "access/itup.h" |
|
||||||
#include "access/sdir.h" |
|
||||||
#include "access/skey.h" |
|
||||||
#include "access/xlog.h" |
|
||||||
#include "utils/rel.h" |
|
||||||
|
|
||||||
/* see rtstrat.c for what all this is about */ |
|
||||||
#define RTNStrategies 12 |
|
||||||
#define RTLeftStrategyNumber 1 |
|
||||||
#define RTOverLeftStrategyNumber 2 |
|
||||||
#define RTOverlapStrategyNumber 3 |
|
||||||
#define RTOverRightStrategyNumber 4 |
|
||||||
#define RTRightStrategyNumber 5 |
|
||||||
#define RTSameStrategyNumber 6 |
|
||||||
#define RTContainsStrategyNumber 7 |
|
||||||
#define RTContainedByStrategyNumber 8 |
|
||||||
#define RTOverBelowStrategyNumber 9 |
|
||||||
#define RTBelowStrategyNumber 10 |
|
||||||
#define RTAboveStrategyNumber 11 |
|
||||||
#define RTOverAboveStrategyNumber 12 |
|
||||||
|
|
||||||
#define RTNProcs 3 |
|
||||||
#define RT_UNION_PROC 1 |
|
||||||
#define RT_INTER_PROC 2 |
|
||||||
#define RT_SIZE_PROC 3 |
|
||||||
|
|
||||||
#define F_LEAF (1 << 0) |
|
||||||
|
|
||||||
typedef struct RTreePageOpaqueData |
|
||||||
{ |
|
||||||
uint32 flags; |
|
||||||
} RTreePageOpaqueData; |
|
||||||
|
|
||||||
typedef RTreePageOpaqueData *RTreePageOpaque; |
|
||||||
|
|
||||||
/*
|
|
||||||
* When we descend a tree, we keep a stack of parent pointers. |
|
||||||
*/ |
|
||||||
|
|
||||||
typedef struct RTSTACK |
|
||||||
{ |
|
||||||
struct RTSTACK *rts_parent; |
|
||||||
OffsetNumber rts_child; |
|
||||||
BlockNumber rts_blk; |
|
||||||
} RTSTACK; |
|
||||||
|
|
||||||
/*
|
|
||||||
* When we're doing a scan, we need to keep track of the parent stack |
|
||||||
* for the marked and current items. Also, rtrees have the following |
|
||||||
* property: if you're looking for the box (1,1,2,2), on the internal |
|
||||||
* nodes you have to search for all boxes that *contain* (1,1,2,2), |
|
||||||
* and not the ones that match it. We have a private scan key for |
|
||||||
* internal nodes in the opaque structure for rtrees for this reason. |
|
||||||
* See access/index-rtree/rtscan.c and rtstrat.c for how it gets |
|
||||||
* initialized. We also keep pins on the scan's current buffer and |
|
||||||
* marked buffer, if any: this avoids the need to invoke ReadBuffer() |
|
||||||
* for each tuple produced by the index scan. |
|
||||||
*/ |
|
||||||
|
|
||||||
typedef struct RTreeScanOpaqueData |
|
||||||
{ |
|
||||||
struct RTSTACK *s_stack; |
|
||||||
struct RTSTACK *s_markstk; |
|
||||||
uint16 s_flags; |
|
||||||
int s_internalNKey; |
|
||||||
ScanKey s_internalKey; |
|
||||||
Buffer curbuf; |
|
||||||
Buffer markbuf; |
|
||||||
} RTreeScanOpaqueData; |
|
||||||
|
|
||||||
typedef RTreeScanOpaqueData *RTreeScanOpaque; |
|
||||||
|
|
||||||
/*
|
|
||||||
* When we're doing a scan and updating a tree at the same time, the |
|
||||||
* updates may affect the scan. We use the flags entry of the scan's |
|
||||||
* opaque space to record our actual position in response to updates |
|
||||||
* that we can't handle simply by adjusting pointers. |
|
||||||
*/ |
|
||||||
|
|
||||||
#define RTS_CURBEFORE ((uint16) (1 << 0)) |
|
||||||
#define RTS_MRKBEFORE ((uint16) (1 << 1)) |
|
||||||
|
|
||||||
/* root page of an rtree */ |
|
||||||
#define P_ROOT 0 |
|
||||||
|
|
||||||
/*
|
|
||||||
* When we update a relation on which we're doing a scan, we need to |
|
||||||
* check the scan and fix it if the update affected any of the pages it |
|
||||||
* touches. Otherwise, we can miss records that we should see. The only |
|
||||||
* times we need to do this are for deletions and splits. See the code in |
|
||||||
* rtscan.c for how the scan is fixed. These two contants tell us what sort |
|
||||||
* of operation changed the index. |
|
||||||
*/ |
|
||||||
|
|
||||||
#define RTOP_DEL 0 |
|
||||||
#define RTOP_SPLIT 1 |
|
||||||
|
|
||||||
/* defined in rtree.c */ |
|
||||||
extern void freestack(RTSTACK *s); |
|
||||||
|
|
||||||
/*
|
|
||||||
* RTree code. |
|
||||||
* Defined in access/rtree/ |
|
||||||
*/ |
|
||||||
extern Datum rtinsert(PG_FUNCTION_ARGS); |
|
||||||
extern Datum rtbulkdelete(PG_FUNCTION_ARGS); |
|
||||||
extern Datum rtbeginscan(PG_FUNCTION_ARGS); |
|
||||||
extern Datum rtgettuple(PG_FUNCTION_ARGS); |
|
||||||
extern Datum rtgetmulti(PG_FUNCTION_ARGS); |
|
||||||
extern Datum rtendscan(PG_FUNCTION_ARGS); |
|
||||||
extern Datum rtmarkpos(PG_FUNCTION_ARGS); |
|
||||||
extern Datum rtrestrpos(PG_FUNCTION_ARGS); |
|
||||||
extern Datum rtrescan(PG_FUNCTION_ARGS); |
|
||||||
extern Datum rtbuild(PG_FUNCTION_ARGS); |
|
||||||
extern void _rtdump(Relation r); |
|
||||||
|
|
||||||
extern void rtree_redo(XLogRecPtr lsn, XLogRecord *record); |
|
||||||
extern void rtree_desc(char *buf, uint8 xl_info, char *rec); |
|
||||||
|
|
||||||
/* rtscan.c */ |
|
||||||
extern void rtadjscans(Relation r, int op, BlockNumber blkno, |
|
||||||
OffsetNumber offnum); |
|
||||||
extern void ReleaseResources_rtree(void); |
|
||||||
|
|
||||||
/* rtstrat.c */ |
|
||||||
extern StrategyNumber RTMapToInternalOperator(StrategyNumber strat); |
|
||||||
extern bool RTMapToInternalNegate(StrategyNumber strat); |
|
||||||
|
|
||||||
#endif /* RTREE_H */ |
|
@ -1,23 +0,0 @@ |
|||||||
/*-------------------------------------------------------------------------
|
|
||||||
* |
|
||||||
* rtscan.h |
|
||||||
* routines defined in access/rtree/rtscan.c |
|
||||||
* |
|
||||||
* |
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group |
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California |
|
||||||
* |
|
||||||
* $PostgreSQL: pgsql/src/include/access/rtscan.h,v 1.18 2004/12/31 22:03:21 pgsql Exp $ |
|
||||||
* |
|
||||||
*------------------------------------------------------------------------- |
|
||||||
*/ |
|
||||||
#ifndef RTSCAN_H |
|
||||||
#define RTSCAN_H |
|
||||||
|
|
||||||
#include "storage/block.h" |
|
||||||
#include "storage/off.h" |
|
||||||
#include "utils/rel.h" |
|
||||||
|
|
||||||
void rtadjscans(Relation r, int op, BlockNumber blkno, OffsetNumber offnum); |
|
||||||
|
|
||||||
#endif /* RTSCAN_H */ |
|
Loading…
Reference in new issue