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