mirror of https://github.com/postgres/postgres
parent
e7e1694295
commit
e6a8eba3f2
@ -1,16 +0,0 @@ |
||||
# $PostgreSQL: pgsql/contrib/rtree_gist/Makefile,v 1.5 2004/08/20 20:13:07 momjian Exp $
|
||||
|
||||
MODULES = rtree_gist
|
||||
DATA_built = rtree_gist.sql
|
||||
DOCS = README.rtree_gist
|
||||
REGRESS = rtree_gist
|
||||
|
||||
ifdef USE_PGXS |
||||
PGXS = $(shell pg_config --pgxs)
|
||||
include $(PGXS) |
||||
else |
||||
subdir = contrib/rtree_gist
|
||||
top_builddir = ../..
|
||||
include $(top_builddir)/src/Makefile.global |
||||
include $(top_srcdir)/contrib/contrib-global.mk |
||||
endif |
||||
@ -1,98 +0,0 @@ |
||||
This is a R-Tree implementation using GiST. |
||||
Code (for PG95) are taken from http://s2k-ftp.cs.berkeley.edu:8000/gist/pggist/ |
||||
and changed according to new version of GiST (7.1 and above) |
||||
|
||||
All work was done by Teodor Sigaev (teodor@stack.net) and Oleg Bartunov |
||||
(oleg@sai.msu.su). See http://www.sai.msu.su/~megera/postgres/gist |
||||
for additional information. |
||||
|
||||
CHANGES: |
||||
Oct 10 MSD 2001 |
||||
|
||||
1. Implemented new linear algorithm for picksplit |
||||
ref. ( 'New Linear Node Splitting Algorithm for R-tree', |
||||
C.H.Ang and T.C.Tan ) |
||||
|
||||
Tue May 29 17:04:16 MSD 2001 |
||||
|
||||
1. Small fixes in polygon code |
||||
Thanks to Dave Blasby <dblasby@refractions.net> |
||||
|
||||
Mon May 28 19:42:14 MSD 2001 |
||||
|
||||
1. Full implementation of R-tree using GiST - gist_box_ops,gist_poly_ops |
||||
2. gist_poly_ops is lossy |
||||
3. NULLs support |
||||
4. works with multi-key GiST |
||||
|
||||
NOTICE: |
||||
This version will only work with PostgreSQL version 7.1 and above |
||||
because of changes in the function call interface. |
||||
|
||||
INSTALLATION: |
||||
|
||||
gmake |
||||
gmake install |
||||
-- load functions |
||||
psql <database> < rtree_gist.sql |
||||
|
||||
REGRESSION TEST: |
||||
|
||||
gmake installcheck |
||||
|
||||
EXAMPLE USAGE: |
||||
|
||||
create table boxtmp (b box); |
||||
-- create index |
||||
create index bix on boxtmp using gist (b gist_box_ops); |
||||
-- query |
||||
select * from boxtmp where b && '(1000,1000,0,0)'::box; |
||||
|
||||
|
||||
BENCHMARKS: |
||||
|
||||
subdirectory bench contains benchmark suite. |
||||
Prerequisities: perl, DBI, DBD:Pg, Time::HiRes |
||||
|
||||
cd ./bench |
||||
1. createdb TEST |
||||
2. psql TEST < ../rtree_gist.sql |
||||
3. ./create_test.pl | psql TEST |
||||
-- change $NUM - number of rows in test dataset |
||||
4. ./bench.pl - perl script to benchmark queries. |
||||
Run script without arguments to see available options. |
||||
|
||||
a)test without GiST index, using built-in R-Tree |
||||
./bench.pl -d TEST |
||||
b)test R-Tree using GiST index |
||||
./bench.pl -d TEST -g |
||||
|
||||
|
||||
RESULTS: |
||||
|
||||
1. One interesting thing is that insertion time for built-in R-Tree is |
||||
about 8 times more than ones for GiST implementation of R-Tree !!! |
||||
2. Postmaster requires much more memory for built-in R-Tree |
||||
3. Search time depends on dataset. In our case we got: |
||||
+------------+-----------+--------------+ |
||||
|Number boxes|R-tree, sec|R-tree using | |
||||
| | | GiST, sec | |
||||
+------------+-----------+--------------+ |
||||
| 10| 0.002| 0.002| |
||||
+------------+-----------+--------------+ |
||||
| 100| 0.002| 0.002| |
||||
+------------+-----------+--------------+ |
||||
| 1000| 0.002| 0.002| |
||||
+------------+-----------+--------------+ |
||||
| 10000| 0.015| 0.025| |
||||
+------------+-----------+--------------+ |
||||
| 20000| 0.029| 0.048| |
||||
+------------+-----------+--------------+ |
||||
| 40000| 0.055| 0.092| |
||||
+------------+-----------+--------------+ |
||||
| 80000| 0.113| 0.178| |
||||
+------------+-----------+--------------+ |
||||
| 160000| 0.338| 0.337| |
||||
+------------+-----------+--------------+ |
||||
| 320000| 0.674| 0.673| |
||||
+------------+-----------+--------------+ |
||||
@ -1,74 +0,0 @@ |
||||
#!/usr/bin/perl -w |
||||
|
||||
use strict; |
||||
# make sure we are in a sane environment. |
||||
use DBI(); |
||||
use DBD::Pg(); |
||||
use Time::HiRes qw( usleep ualarm gettimeofday tv_interval ); |
||||
use Getopt::Std; |
||||
my %opt; |
||||
getopts('d:b:gv', \%opt); |
||||
|
||||
if ( !( scalar %opt ) ) { |
||||
print <<EOT; |
||||
Usage: |
||||
$0 -d DATABASE -b N [-v] [-g] |
||||
-d DATABASE - DATABASE name |
||||
-b N -number of cycles |
||||
-v - print sql |
||||
-g -use GiST index( default built-in R-tree ) |
||||
|
||||
EOT |
||||
exit; |
||||
} |
||||
|
||||
$opt{d} ||= 'TEST'; |
||||
my $dbi=DBI->connect('DBI:Pg:dbname='.$opt{d}) || die "Couldn't connect DB: $opt{d} !\n"; |
||||
|
||||
my $setsql = qq{ |
||||
SET search_path = public; |
||||
}; |
||||
|
||||
my $sth = $dbi->prepare($setsql); |
||||
$sth->execute(); |
||||
|
||||
my $sql; |
||||
my $notice; |
||||
my $sss = '(3000,3000,2990,2990)'; |
||||
if ( $opt{g} ) { |
||||
$notice = "Testing GiST implementation of R-Tree"; |
||||
$sql = "select count(*) from boxtmp where b && '$sss'::box;"; |
||||
} else { |
||||
$notice = "Testing built-in implementation of R-Tree"; |
||||
$sql = "select count(*) from boxtmp2 where b && '$sss'::box;"; |
||||
} |
||||
|
||||
my $t0 = [gettimeofday]; |
||||
my $count=0; |
||||
my $b=$opt{b}; |
||||
|
||||
$b ||=1; |
||||
foreach ( 1..$b ) { |
||||
my @a=exec_sql($dbi,$sql); |
||||
$count=$#a; |
||||
} |
||||
my $elapsed = tv_interval ( $t0, [gettimeofday]); |
||||
print "$notice:\n"; |
||||
print "$sql\n" if ( $opt{v} ); |
||||
print "Done\n"; |
||||
print sprintf("total: %.02f sec; number: %d; for one: %.03f sec; found %d docs\n", $elapsed, $b, $elapsed/$b, $count+1 ); |
||||
$dbi -> disconnect; |
||||
|
||||
sub exec_sql { |
||||
my ($dbi, $sql, @keys) = @_; |
||||
my $sth=$dbi->prepare($sql) || die; |
||||
$sth->execute( @keys ) || die; |
||||
my $r; |
||||
my @row; |
||||
while ( defined ( $r=$sth->fetchrow_hashref ) ) { |
||||
push @row, $r; |
||||
} |
||||
$sth->finish; |
||||
return @row; |
||||
} |
||||
|
||||
@ -1,50 +0,0 @@ |
||||
#!/usr/bin/perl |
||||
use strict; |
||||
|
||||
my $NUM = 20000; |
||||
print "DROP TABLE boxtmp;\n"; |
||||
print "DROP TABLE boxtmp2;\n"; |
||||
|
||||
print "CREATE TABLE boxtmp (b box);\n"; |
||||
print "CREATE TABLE boxtmp2 (b box);\n"; |
||||
|
||||
srand(1); |
||||
open(DAT,">bbb.dat") || die; |
||||
foreach ( 1..$NUM ) { |
||||
#print DAT '(',int( 500+500*rand() ),',',int( 500+500*rand() ),',',int( 500*rand() ),',',int( 500*rand() ),")\n"; |
||||
my ( $x1,$y1, $x2,$y2 ) = ( |
||||
10000*rand(), |
||||
10000*rand(), |
||||
10000*rand(), |
||||
10000*rand() |
||||
); |
||||
print DAT '(', |
||||
max($x1,$x2),',', |
||||
max($y1,$y2),',', |
||||
min($x1,$x2),',', |
||||
min($y1,$y2),")\n"; |
||||
} |
||||
close DAT; |
||||
|
||||
print "COPY boxtmp FROM stdin;\n"; |
||||
open(DAT,"bbb.dat") || die; |
||||
while(<DAT>) { print; } |
||||
close DAT; |
||||
print "\\.\n"; |
||||
|
||||
print "COPY boxtmp2 FROM stdin;\n"; |
||||
open(DAT,"bbb.dat") || die; |
||||
while(<DAT>) { print; } |
||||
close DAT; |
||||
print "\\.\n"; |
||||
|
||||
print "CREATE INDEX bix ON boxtmp USING gist (b gist_box_ops);\n"; |
||||
print "CREATE INDEX bix2 ON boxtmp2 USING rtree (b box_ops);\n"; |
||||
|
||||
|
||||
sub min { |
||||
return ( $_[0] < $_[1] ) ? $_[0] : $_[1]; |
||||
} |
||||
sub max { |
||||
return ( $_[0] > $_[1] ) ? $_[0] : $_[1]; |
||||
} |
||||
File diff suppressed because it is too large
Load Diff
@ -1,55 +0,0 @@ |
||||
-- |
||||
-- first, define the datatype. Turn off echoing so that expected file |
||||
-- does not depend on contents of seg.sql. |
||||
-- |
||||
\set ECHO none |
||||
CREATE TABLE boxtmp (b box); |
||||
\copy boxtmp from 'data/test_box.data' |
||||
SELECT count(*) |
||||
FROM boxtmp |
||||
WHERE b && '(1000,1000,0,0)'::box; |
||||
count |
||||
------- |
||||
2 |
||||
(1 row) |
||||
|
||||
CREATE INDEX bix ON boxtmp USING rtree (b); |
||||
SELECT count(*) |
||||
FROM boxtmp |
||||
WHERE b && '(1000,1000,0,0)'::box; |
||||
count |
||||
------- |
||||
2 |
||||
(1 row) |
||||
|
||||
DROP INDEX bix; |
||||
CREATE INDEX bix ON boxtmp USING gist (b); |
||||
SELECT count(*) |
||||
FROM boxtmp |
||||
WHERE b && '(1000,1000,0,0)'::box; |
||||
count |
||||
------- |
||||
2 |
||||
(1 row) |
||||
|
||||
CREATE TABLE polytmp (p polygon); |
||||
\copy polytmp from 'data/test_box.data' |
||||
CREATE INDEX pix ON polytmp USING rtree (p); |
||||
SELECT count(*) |
||||
FROM polytmp |
||||
WHERE p && '(1000,1000),(0,0)'::polygon; |
||||
count |
||||
------- |
||||
2 |
||||
(1 row) |
||||
|
||||
DROP INDEX pix; |
||||
CREATE INDEX pix ON polytmp USING gist (p); |
||||
SELECT count(*) |
||||
FROM polytmp |
||||
WHERE p && '(1000,1000),(0,0)'::polygon; |
||||
count |
||||
------- |
||||
2 |
||||
(1 row) |
||||
|
||||
@ -1,599 +0,0 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* rtree_gist.c |
||||
* pg_amproc entries for GiSTs over 2-D boxes and polygons. |
||||
* |
||||
* This gives R-tree behavior, with Guttman's poly-time split algorithm. |
||||
* |
||||
* |
||||
* IDENTIFICATION |
||||
* $PostgreSQL: pgsql/contrib/rtree_gist/rtree_gist.c,v 1.14 2005/06/24 20:53:29 tgl Exp $ |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#include "postgres.h" |
||||
|
||||
#include "access/gist.h" |
||||
#include "access/itup.h" |
||||
#include "access/rtree.h" |
||||
#include "utils/geo_decls.h" |
||||
|
||||
typedef Datum (*RDF) (PG_FUNCTION_ARGS); |
||||
typedef Datum (*BINARY_UNION) (Datum, Datum, int *); |
||||
typedef float (*SIZE_BOX) (Datum); |
||||
|
||||
/*
|
||||
* box ops |
||||
*/ |
||||
PG_FUNCTION_INFO_V1(gbox_compress); |
||||
PG_FUNCTION_INFO_V1(gbox_union); |
||||
PG_FUNCTION_INFO_V1(gbox_picksplit); |
||||
PG_FUNCTION_INFO_V1(gbox_consistent); |
||||
PG_FUNCTION_INFO_V1(gbox_penalty); |
||||
PG_FUNCTION_INFO_V1(gbox_same); |
||||
|
||||
Datum gbox_compress(PG_FUNCTION_ARGS); |
||||
Datum gbox_union(PG_FUNCTION_ARGS); |
||||
Datum gbox_picksplit(PG_FUNCTION_ARGS); |
||||
Datum gbox_consistent(PG_FUNCTION_ARGS); |
||||
Datum gbox_penalty(PG_FUNCTION_ARGS); |
||||
Datum gbox_same(PG_FUNCTION_ARGS); |
||||
|
||||
static bool gbox_leaf_consistent(BOX *key, BOX *query, StrategyNumber strategy); |
||||
static float size_box(Datum box); |
||||
|
||||
/*
|
||||
* Polygon ops |
||||
*/ |
||||
PG_FUNCTION_INFO_V1(gpoly_compress); |
||||
PG_FUNCTION_INFO_V1(gpoly_consistent); |
||||
|
||||
Datum gpoly_compress(PG_FUNCTION_ARGS); |
||||
Datum gpoly_consistent(PG_FUNCTION_ARGS); |
||||
|
||||
/*
|
||||
* Common rtree-function (for all ops) |
||||
*/ |
||||
static bool rtree_internal_consistent(BOX *key, BOX *query, StrategyNumber strategy); |
||||
|
||||
PG_FUNCTION_INFO_V1(rtree_decompress); |
||||
|
||||
Datum rtree_decompress(PG_FUNCTION_ARGS); |
||||
|
||||
/**************************************************
|
||||
* Box ops |
||||
**************************************************/ |
||||
|
||||
/*
|
||||
** The GiST Consistent method for boxes |
||||
** Should return false if for all data items x below entry, |
||||
** the predicate x op query == FALSE, where op is the oper |
||||
** corresponding to strategy in the pg_amop table. |
||||
*/ |
||||
Datum |
||||
gbox_consistent(PG_FUNCTION_ARGS) |
||||
{ |
||||
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); |
||||
BOX *query = (BOX *) PG_GETARG_POINTER(1); |
||||
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); |
||||
|
||||
/*
|
||||
* if entry is not leaf, use rtree_internal_consistent, else use |
||||
* gbox_leaf_consistent |
||||
*/ |
||||
if (!(DatumGetPointer(entry->key) != NULL && query)) |
||||
PG_RETURN_BOOL(FALSE); |
||||
|
||||
if (GIST_LEAF(entry)) |
||||
PG_RETURN_BOOL(gbox_leaf_consistent((BOX *) DatumGetPointer(entry->key), query, strategy)); |
||||
else |
||||
PG_RETURN_BOOL(rtree_internal_consistent((BOX *) DatumGetPointer(entry->key), query, strategy)); |
||||
} |
||||
|
||||
|
||||
/*
|
||||
** The GiST Union method for boxes |
||||
** returns the minimal bounding box that encloses all the entries in entryvec |
||||
*/ |
||||
Datum |
||||
gbox_union(PG_FUNCTION_ARGS) |
||||
{ |
||||
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); |
||||
int *sizep = (int *) PG_GETARG_POINTER(1); |
||||
int numranges, |
||||
i; |
||||
BOX *cur, |
||||
*pageunion; |
||||
|
||||
numranges = entryvec->n; |
||||
pageunion = (BOX *) palloc(sizeof(BOX)); |
||||
cur = DatumGetBoxP(entryvec->vector[0].key); |
||||
memcpy((void *) pageunion, (void *) cur, sizeof(BOX)); |
||||
|
||||
for (i = 1; i < numranges; i++) |
||||
{ |
||||
cur = DatumGetBoxP(entryvec->vector[i].key); |
||||
if (pageunion->high.x < cur->high.x) |
||||
pageunion->high.x = cur->high.x; |
||||
if (pageunion->low.x > cur->low.x) |
||||
pageunion->low.x = cur->low.x; |
||||
if (pageunion->high.y < cur->high.y) |
||||
pageunion->high.y = cur->high.y; |
||||
if (pageunion->low.y > cur->low.y) |
||||
pageunion->low.y = cur->low.y; |
||||
} |
||||
*sizep = sizeof(BOX); |
||||
|
||||
PG_RETURN_POINTER(pageunion); |
||||
} |
||||
|
||||
/*
|
||||
** GiST Compress methods for boxes |
||||
** do not do anything. |
||||
*/ |
||||
Datum |
||||
gbox_compress(PG_FUNCTION_ARGS) |
||||
{ |
||||
PG_RETURN_POINTER(PG_GETARG_POINTER(0)); |
||||
} |
||||
|
||||
/*
|
||||
** The GiST Penalty method for boxes |
||||
** As in the R-tree paper, we use change in area as our penalty metric |
||||
*/ |
||||
Datum |
||||
gbox_penalty(PG_FUNCTION_ARGS) |
||||
{ |
||||
GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); |
||||
GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1); |
||||
float *result = (float *) PG_GETARG_POINTER(2); |
||||
Datum ud; |
||||
float tmp1; |
||||
|
||||
ud = DirectFunctionCall2(rt_box_union, origentry->key, newentry->key); |
||||
tmp1 = size_box(ud); |
||||
*result = tmp1 - size_box(origentry->key); |
||||
PG_RETURN_POINTER(result); |
||||
} |
||||
|
||||
typedef struct |
||||
{ |
||||
BOX *key; |
||||
int pos; |
||||
} KBsort; |
||||
|
||||
static int |
||||
compare_KB(const void *a, const void *b) |
||||
{ |
||||
BOX *abox = ((KBsort *) a)->key; |
||||
BOX *bbox = ((KBsort *) b)->key; |
||||
float sa = (abox->high.x - abox->low.x) * (abox->high.y - abox->low.y); |
||||
float sb = (bbox->high.x - bbox->low.x) * (bbox->high.y - bbox->low.y); |
||||
|
||||
if (sa == sb) |
||||
return 0; |
||||
return (sa > sb) ? 1 : -1; |
||||
} |
||||
|
||||
/*
|
||||
** The GiST PickSplit method |
||||
** New linear algorithm, see 'New Linear Node Splitting Algorithm for R-tree', |
||||
** C.H.Ang and T.C.Tan |
||||
*/ |
||||
Datum |
||||
gbox_picksplit(PG_FUNCTION_ARGS) |
||||
{ |
||||
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); |
||||
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); |
||||
OffsetNumber i; |
||||
OffsetNumber *listL, |
||||
*listR, |
||||
*listB, |
||||
*listT; |
||||
BOX *unionL, |
||||
*unionR, |
||||
*unionB, |
||||
*unionT; |
||||
int posL, |
||||
posR, |
||||
posB, |
||||
posT; |
||||
BOX pageunion; |
||||
BOX *cur; |
||||
char direction = ' '; |
||||
bool allisequal = true; |
||||
OffsetNumber maxoff; |
||||
int nbytes; |
||||
|
||||
posL = posR = posB = posT = 0; |
||||
maxoff = entryvec->n - 1; |
||||
|
||||
cur = DatumGetBoxP(entryvec->vector[FirstOffsetNumber].key); |
||||
memcpy((void *) &pageunion, (void *) cur, sizeof(BOX)); |
||||
|
||||
/* find MBR */ |
||||
for (i = OffsetNumberNext(FirstOffsetNumber); i <= maxoff; i = OffsetNumberNext(i)) |
||||
{ |
||||
cur = DatumGetBoxP(entryvec->vector[i].key); |
||||
if (allisequal == true && ( |
||||
pageunion.high.x != cur->high.x || |
||||
pageunion.high.y != cur->high.y || |
||||
pageunion.low.x != cur->low.x || |
||||
pageunion.low.y != cur->low.y |
||||
)) |
||||
allisequal = false; |
||||
|
||||
if (pageunion.high.x < cur->high.x) |
||||
pageunion.high.x = cur->high.x; |
||||
if (pageunion.low.x > cur->low.x) |
||||
pageunion.low.x = cur->low.x; |
||||
if (pageunion.high.y < cur->high.y) |
||||
pageunion.high.y = cur->high.y; |
||||
if (pageunion.low.y > cur->low.y) |
||||
pageunion.low.y = cur->low.y; |
||||
} |
||||
|
||||
nbytes = (maxoff + 2) * sizeof(OffsetNumber); |
||||
listL = (OffsetNumber *) palloc(nbytes); |
||||
listR = (OffsetNumber *) palloc(nbytes); |
||||
unionL = (BOX *) palloc(sizeof(BOX)); |
||||
unionR = (BOX *) palloc(sizeof(BOX)); |
||||
if (allisequal) |
||||
{ |
||||
cur = DatumGetBoxP(entryvec->vector[OffsetNumberNext(FirstOffsetNumber)].key); |
||||
if (memcmp((void *) cur, (void *) &pageunion, sizeof(BOX)) == 0) |
||||
{ |
||||
v->spl_left = listL; |
||||
v->spl_right = listR; |
||||
v->spl_nleft = v->spl_nright = 0; |
||||
memcpy((void *) unionL, (void *) &pageunion, sizeof(BOX)); |
||||
memcpy((void *) unionR, (void *) &pageunion, sizeof(BOX)); |
||||
|
||||
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) |
||||
{ |
||||
if (i <= (maxoff - FirstOffsetNumber + 1) / 2) |
||||
{ |
||||
v->spl_left[v->spl_nleft] = i; |
||||
v->spl_nleft++; |
||||
} |
||||
else |
||||
{ |
||||
v->spl_right[v->spl_nright] = i; |
||||
v->spl_nright++; |
||||
} |
||||
} |
||||
v->spl_ldatum = BoxPGetDatum(unionL); |
||||
v->spl_rdatum = BoxPGetDatum(unionR); |
||||
|
||||
PG_RETURN_POINTER(v); |
||||
} |
||||
} |
||||
|
||||
listB = (OffsetNumber *) palloc(nbytes); |
||||
listT = (OffsetNumber *) palloc(nbytes); |
||||
unionB = (BOX *) palloc(sizeof(BOX)); |
||||
unionT = (BOX *) palloc(sizeof(BOX)); |
||||
|
||||
#define ADDLIST( list, unionD, pos, num ) do { \ |
||||
if ( pos ) { \
|
||||
if ( (unionD)->high.x < cur->high.x ) (unionD)->high.x = cur->high.x; \
|
||||
if ( (unionD)->low.x > cur->low.x ) (unionD)->low.x = cur->low.x; \
|
||||
if ( (unionD)->high.y < cur->high.y ) (unionD)->high.y = cur->high.y; \
|
||||
if ( (unionD)->low.y > cur->low.y ) (unionD)->low.y = cur->low.y; \
|
||||
} else { \
|
||||
memcpy( (void*)(unionD), (void*) cur, sizeof( BOX ) ); \
|
||||
} \
|
||||
(list)[pos] = num; \
|
||||
(pos)++; \
|
||||
} while(0) |
||||
|
||||
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) |
||||
{ |
||||
cur = DatumGetBoxP(entryvec->vector[i].key); |
||||
if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x) |
||||
ADDLIST(listL, unionL, posL, i); |
||||
else |
||||
ADDLIST(listR, unionR, posR, i); |
||||
if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y) |
||||
ADDLIST(listB, unionB, posB, i); |
||||
else |
||||
ADDLIST(listT, unionT, posT, i); |
||||
} |
||||
|
||||
/* bad disposition, sort by ascending and resplit */ |
||||
if ((posR == 0 || posL == 0) && (posT == 0 || posB == 0)) |
||||
{ |
||||
KBsort *arr = (KBsort *) palloc(sizeof(KBsort) * maxoff); |
||||
|
||||
posL = posR = posB = posT = 0; |
||||
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) |
||||
{ |
||||
arr[i - 1].key = DatumGetBoxP(entryvec->vector[i].key); |
||||
arr[i - 1].pos = i; |
||||
} |
||||
qsort(arr, maxoff, sizeof(KBsort), compare_KB); |
||||
for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) |
||||
{ |
||||
cur = arr[i - 1].key; |
||||
if (cur->low.x - pageunion.low.x < pageunion.high.x - cur->high.x) |
||||
ADDLIST(listL, unionL, posL, arr[i - 1].pos); |
||||
else if (cur->low.x - pageunion.low.x == pageunion.high.x - cur->high.x) |
||||
{ |
||||
if (posL > posR) |
||||
ADDLIST(listR, unionR, posR, arr[i - 1].pos); |
||||
else |
||||
ADDLIST(listL, unionL, posL, arr[i - 1].pos); |
||||
} |
||||
else |
||||
ADDLIST(listR, unionR, posR, arr[i - 1].pos); |
||||
|
||||
if (cur->low.y - pageunion.low.y < pageunion.high.y - cur->high.y) |
||||
ADDLIST(listB, unionB, posB, arr[i - 1].pos); |
||||
else if (cur->low.y - pageunion.low.y == pageunion.high.y - cur->high.y) |
||||
{ |
||||
if (posB > posT) |
||||
ADDLIST(listT, unionT, posT, arr[i - 1].pos); |
||||
else |
||||
ADDLIST(listB, unionB, posB, arr[i - 1].pos); |
||||
} |
||||
else |
||||
ADDLIST(listT, unionT, posT, arr[i - 1].pos); |
||||
} |
||||
} |
||||
|
||||
/* which split more optimal? */ |
||||
if (Max(posL, posR) < Max(posB, posT)) |
||||
direction = 'x'; |
||||
else if (Max(posL, posR) > Max(posB, posT)) |
||||
direction = 'y'; |
||||
else |
||||
{ |
||||
Datum interLR = DirectFunctionCall2(rt_box_inter, |
||||
BoxPGetDatum(unionL), |
||||
BoxPGetDatum(unionR)); |
||||
Datum interBT = DirectFunctionCall2(rt_box_inter, |
||||
BoxPGetDatum(unionB), |
||||
BoxPGetDatum(unionT)); |
||||
float sizeLR, |
||||
sizeBT; |
||||
|
||||
sizeLR = size_box(interLR); |
||||
sizeBT = size_box(interBT); |
||||
|
||||
if (sizeLR < sizeBT) |
||||
direction = 'x'; |
||||
else |
||||
direction = 'y'; |
||||
} |
||||
|
||||
if (direction == 'x') |
||||
{ |
||||
v->spl_left = listL; |
||||
v->spl_right = listR; |
||||
v->spl_nleft = posL; |
||||
v->spl_nright = posR; |
||||
v->spl_ldatum = BoxPGetDatum(unionL); |
||||
v->spl_rdatum = BoxPGetDatum(unionR); |
||||
} |
||||
else |
||||
{ |
||||
v->spl_left = listB; |
||||
v->spl_right = listT; |
||||
v->spl_nleft = posB; |
||||
v->spl_nright = posT; |
||||
v->spl_ldatum = BoxPGetDatum(unionB); |
||||
v->spl_rdatum = BoxPGetDatum(unionT); |
||||
} |
||||
|
||||
PG_RETURN_POINTER(v); |
||||
} |
||||
|
||||
/*
|
||||
** Equality method |
||||
*/ |
||||
Datum |
||||
gbox_same(PG_FUNCTION_ARGS) |
||||
{ |
||||
BOX *b1 = (BOX *) PG_GETARG_POINTER(0); |
||||
BOX *b2 = (BOX *) PG_GETARG_POINTER(1); |
||||
bool *result = (bool *) PG_GETARG_POINTER(2); |
||||
|
||||
if (b1 && b2) |
||||
*result = DatumGetBool(DirectFunctionCall2(box_same, PointerGetDatum(b1), PointerGetDatum(b2))); |
||||
else |
||||
*result = (b1 == NULL && b2 == NULL) ? TRUE : FALSE; |
||||
PG_RETURN_POINTER(result); |
||||
} |
||||
|
||||
/*
|
||||
** SUPPORT ROUTINES for boxes |
||||
*/ |
||||
static bool |
||||
gbox_leaf_consistent(BOX *key, |
||||
BOX *query, |
||||
StrategyNumber strategy) |
||||
{ |
||||
bool retval; |
||||
|
||||
switch (strategy) |
||||
{ |
||||
case RTLeftStrategyNumber: |
||||
retval = DatumGetBool(DirectFunctionCall2(box_left, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
case RTOverLeftStrategyNumber: |
||||
retval = DatumGetBool(DirectFunctionCall2(box_overleft, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
case RTOverlapStrategyNumber: |
||||
retval = DatumGetBool(DirectFunctionCall2(box_overlap, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
case RTOverRightStrategyNumber: |
||||
retval = DatumGetBool(DirectFunctionCall2(box_overright, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
case RTRightStrategyNumber: |
||||
retval = DatumGetBool(DirectFunctionCall2(box_right, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
case RTSameStrategyNumber: |
||||
retval = DatumGetBool(DirectFunctionCall2(box_same, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
case RTContainsStrategyNumber: |
||||
retval = DatumGetBool(DirectFunctionCall2(box_contain, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
case RTContainedByStrategyNumber: |
||||
retval = DatumGetBool(DirectFunctionCall2(box_contained, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
case RTOverBelowStrategyNumber: |
||||
retval = DatumGetBool(DirectFunctionCall2(box_overbelow, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
case RTBelowStrategyNumber: |
||||
retval = DatumGetBool(DirectFunctionCall2(box_below, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
case RTAboveStrategyNumber: |
||||
retval = DatumGetBool(DirectFunctionCall2(box_above, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
case RTOverAboveStrategyNumber: |
||||
retval = DatumGetBool(DirectFunctionCall2(box_overabove, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
default: |
||||
retval = FALSE; |
||||
} |
||||
return (retval); |
||||
} |
||||
|
||||
static float |
||||
size_box(Datum box) |
||||
{ |
||||
if (DatumGetPointer(box) != NULL) |
||||
{ |
||||
float size; |
||||
|
||||
DirectFunctionCall2(rt_box_size, |
||||
box, PointerGetDatum(&size)); |
||||
return size; |
||||
} |
||||
else |
||||
return 0.0; |
||||
} |
||||
|
||||
/**************************************************
|
||||
* Polygon ops |
||||
**************************************************/ |
||||
|
||||
Datum |
||||
gpoly_compress(PG_FUNCTION_ARGS) |
||||
{ |
||||
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); |
||||
GISTENTRY *retval; |
||||
|
||||
if (entry->leafkey) |
||||
{ |
||||
retval = palloc(sizeof(GISTENTRY)); |
||||
if (DatumGetPointer(entry->key) != NULL) |
||||
{ |
||||
POLYGON *in; |
||||
BOX *r; |
||||
|
||||
in = (POLYGON *) PG_DETOAST_DATUM(entry->key); |
||||
r = (BOX *) palloc(sizeof(BOX)); |
||||
memcpy((void *) r, (void *) &(in->boundbox), sizeof(BOX)); |
||||
gistentryinit(*retval, PointerGetDatum(r), |
||||
entry->rel, entry->page, |
||||
entry->offset, sizeof(BOX), FALSE); |
||||
|
||||
} |
||||
else |
||||
{ |
||||
gistentryinit(*retval, (Datum) 0, |
||||
entry->rel, entry->page, |
||||
entry->offset, 0, FALSE); |
||||
} |
||||
} |
||||
else |
||||
retval = entry; |
||||
PG_RETURN_POINTER(retval); |
||||
} |
||||
|
||||
Datum |
||||
gpoly_consistent(PG_FUNCTION_ARGS) |
||||
{ |
||||
GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); |
||||
POLYGON *query = (POLYGON *) PG_DETOAST_DATUM(PG_GETARG_POINTER(1)); |
||||
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); |
||||
bool result; |
||||
|
||||
/*
|
||||
* Since the operators are marked lossy anyway, we can just use |
||||
* rtree_internal_consistent even at leaf nodes. (This works |
||||
* in part because the index entries are bounding Boxes not polygons.) |
||||
*/ |
||||
if (!(DatumGetPointer(entry->key) != NULL && query)) |
||||
PG_RETURN_BOOL(FALSE); |
||||
|
||||
result = rtree_internal_consistent((BOX *) DatumGetPointer(entry->key), |
||||
&(query->boundbox), strategy); |
||||
|
||||
PG_FREE_IF_COPY(query, 1); |
||||
PG_RETURN_BOOL(result); |
||||
} |
||||
|
||||
/*****************************************
|
||||
* Common rtree-function (for all ops) |
||||
*****************************************/ |
||||
|
||||
static bool |
||||
rtree_internal_consistent(BOX *key, |
||||
BOX *query, |
||||
StrategyNumber strategy) |
||||
{ |
||||
bool retval; |
||||
|
||||
switch (strategy) |
||||
{ |
||||
case RTLeftStrategyNumber: |
||||
retval = !DatumGetBool(DirectFunctionCall2(box_overright, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
case RTOverLeftStrategyNumber: |
||||
retval = !DatumGetBool(DirectFunctionCall2(box_right, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
case RTOverlapStrategyNumber: |
||||
retval = DatumGetBool(DirectFunctionCall2(box_overlap, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
case RTOverRightStrategyNumber: |
||||
retval = !DatumGetBool(DirectFunctionCall2(box_left, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
case RTRightStrategyNumber: |
||||
retval = !DatumGetBool(DirectFunctionCall2(box_overleft, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
case RTSameStrategyNumber: |
||||
case RTContainsStrategyNumber: |
||||
retval = DatumGetBool(DirectFunctionCall2(box_contain, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
case RTContainedByStrategyNumber: |
||||
retval = DatumGetBool(DirectFunctionCall2(box_overlap, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
case RTOverBelowStrategyNumber: |
||||
retval = !DatumGetBool(DirectFunctionCall2(box_above, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
case RTBelowStrategyNumber: |
||||
retval = !DatumGetBool(DirectFunctionCall2(box_overabove, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
case RTAboveStrategyNumber: |
||||
retval = !DatumGetBool(DirectFunctionCall2(box_overbelow, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
case RTOverAboveStrategyNumber: |
||||
retval = !DatumGetBool(DirectFunctionCall2(box_below, PointerGetDatum(key), PointerGetDatum(query))); |
||||
break; |
||||
default: |
||||
retval = FALSE; |
||||
} |
||||
return (retval); |
||||
} |
||||
|
||||
/*
|
||||
** GiST DeCompress methods |
||||
** do not do anything. |
||||
*/ |
||||
Datum |
||||
rtree_decompress(PG_FUNCTION_ARGS) |
||||
{ |
||||
PG_RETURN_POINTER(PG_GETARG_POINTER(0)); |
||||
} |
||||
@ -1,113 +0,0 @@ |
||||
-- Adjust this setting to control where the objects get created. |
||||
SET search_path = public; |
||||
|
||||
-- |
||||
-- |
||||
-- |
||||
-- BOX ops |
||||
-- |
||||
-- |
||||
-- |
||||
-- define the GiST support methods |
||||
CREATE FUNCTION gbox_consistent(internal,box,int4) |
||||
RETURNS bool |
||||
AS 'MODULE_PATHNAME' |
||||
LANGUAGE 'C'; |
||||
|
||||
CREATE FUNCTION gbox_compress(internal) |
||||
RETURNS internal |
||||
AS 'MODULE_PATHNAME' |
||||
LANGUAGE 'C'; |
||||
|
||||
CREATE FUNCTION rtree_decompress(internal) |
||||
RETURNS internal |
||||
AS 'MODULE_PATHNAME' |
||||
LANGUAGE 'C'; |
||||
|
||||
CREATE FUNCTION gbox_penalty(internal,internal,internal) |
||||
RETURNS internal |
||||
AS 'MODULE_PATHNAME' |
||||
LANGUAGE 'C' STRICT; |
||||
|
||||
CREATE FUNCTION gbox_picksplit(internal, internal) |
||||
RETURNS internal |
||||
AS 'MODULE_PATHNAME' |
||||
LANGUAGE 'C'; |
||||
|
||||
CREATE FUNCTION gbox_union(internal, internal) |
||||
RETURNS box |
||||
AS 'MODULE_PATHNAME' |
||||
LANGUAGE 'C'; |
||||
|
||||
CREATE FUNCTION gbox_same(box, box, internal) |
||||
RETURNS internal |
||||
AS 'MODULE_PATHNAME' |
||||
LANGUAGE 'C'; |
||||
|
||||
-- create the operator class |
||||
CREATE OPERATOR CLASS gist_box_ops |
||||
DEFAULT FOR TYPE box USING gist |
||||
AS |
||||
OPERATOR 1 << , |
||||
OPERATOR 2 &< , |
||||
OPERATOR 3 && , |
||||
OPERATOR 4 &> , |
||||
OPERATOR 5 >> , |
||||
OPERATOR 6 ~= , |
||||
OPERATOR 7 ~ , |
||||
OPERATOR 8 @ , |
||||
OPERATOR 9 &<| , |
||||
OPERATOR 10 <<| , |
||||
OPERATOR 11 |>> , |
||||
OPERATOR 12 |&> , |
||||
FUNCTION 1 gbox_consistent (internal, box, int4), |
||||
FUNCTION 2 gbox_union (internal, internal), |
||||
FUNCTION 3 gbox_compress (internal), |
||||
FUNCTION 4 rtree_decompress (internal), |
||||
FUNCTION 5 gbox_penalty (internal, internal, internal), |
||||
FUNCTION 6 gbox_picksplit (internal, internal), |
||||
FUNCTION 7 gbox_same (box, box, internal); |
||||
|
||||
|
||||
-- |
||||
-- |
||||
-- |
||||
-- POLYGON ops |
||||
-- |
||||
-- |
||||
-- |
||||
-- define the GiST support methods |
||||
CREATE FUNCTION gpoly_consistent(internal,polygon,int4) |
||||
RETURNS bool |
||||
AS 'MODULE_PATHNAME' |
||||
LANGUAGE 'C'; |
||||
|
||||
CREATE FUNCTION gpoly_compress(internal) |
||||
RETURNS internal |
||||
AS 'MODULE_PATHNAME' |
||||
LANGUAGE 'C'; |
||||
|
||||
-- create the operator class |
||||
CREATE OPERATOR CLASS gist_poly_ops |
||||
DEFAULT FOR TYPE polygon USING gist |
||||
AS |
||||
OPERATOR 1 << RECHECK, |
||||
OPERATOR 2 &< RECHECK, |
||||
OPERATOR 3 && RECHECK, |
||||
OPERATOR 4 &> RECHECK, |
||||
OPERATOR 5 >> RECHECK, |
||||
OPERATOR 6 ~= RECHECK, |
||||
OPERATOR 7 ~ RECHECK, |
||||
OPERATOR 8 @ RECHECK, |
||||
OPERATOR 9 &<| RECHECK, |
||||
OPERATOR 10 <<| RECHECK, |
||||
OPERATOR 11 |>> RECHECK, |
||||
OPERATOR 12 |&> RECHECK, |
||||
FUNCTION 1 gpoly_consistent (internal, polygon, int4), |
||||
FUNCTION 2 gbox_union (internal, internal), |
||||
FUNCTION 3 gpoly_compress (internal), |
||||
FUNCTION 4 rtree_decompress (internal), |
||||
FUNCTION 5 gbox_penalty (internal, internal, internal), |
||||
FUNCTION 6 gbox_picksplit (internal, internal), |
||||
FUNCTION 7 gbox_same (box, box, internal), |
||||
STORAGE box; |
||||
@ -1,47 +0,0 @@ |
||||
-- |
||||
-- first, define the datatype. Turn off echoing so that expected file |
||||
-- does not depend on contents of seg.sql. |
||||
-- |
||||
\set ECHO none |
||||
\i rtree_gist.sql |
||||
\set ECHO all |
||||
|
||||
CREATE TABLE boxtmp (b box); |
||||
|
||||
\copy boxtmp from 'data/test_box.data' |
||||
|
||||
SELECT count(*) |
||||
FROM boxtmp |
||||
WHERE b && '(1000,1000,0,0)'::box; |
||||
|
||||
CREATE INDEX bix ON boxtmp USING rtree (b); |
||||
|
||||
SELECT count(*) |
||||
FROM boxtmp |
||||
WHERE b && '(1000,1000,0,0)'::box; |
||||
|
||||
DROP INDEX bix; |
||||
|
||||
CREATE INDEX bix ON boxtmp USING gist (b); |
||||
|
||||
SELECT count(*) |
||||
FROM boxtmp |
||||
WHERE b && '(1000,1000,0,0)'::box; |
||||
|
||||
CREATE TABLE polytmp (p polygon); |
||||
|
||||
\copy polytmp from 'data/test_box.data' |
||||
|
||||
CREATE INDEX pix ON polytmp USING rtree (p); |
||||
|
||||
SELECT count(*) |
||||
FROM polytmp |
||||
WHERE p && '(1000,1000),(0,0)'::polygon; |
||||
|
||||
DROP INDEX pix; |
||||
|
||||
CREATE INDEX pix ON polytmp USING gist (p); |
||||
|
||||
SELECT count(*) |
||||
FROM polytmp |
||||
WHERE p && '(1000,1000),(0,0)'::polygon; |
||||
Loading…
Reference in new issue