Remove contrib version of rtree_gist --- now in core system.
This commit is contained in:
parent
e7e1694295
commit
e6a8eba3f2
@ -1,4 +1,4 @@
|
||||
# $PostgreSQL: pgsql/contrib/Makefile,v 1.56 2005/06/23 02:33:25 tgl Exp $
|
||||
# $PostgreSQL: pgsql/contrib/Makefile,v 1.57 2005/07/01 19:23:04 tgl Exp $
|
||||
|
||||
subdir = contrib
|
||||
top_builddir = ..
|
||||
@ -27,7 +27,6 @@ WANTED_DIRS = \
|
||||
pgbench \
|
||||
pgcrypto \
|
||||
pgstattuple \
|
||||
rtree_gist \
|
||||
seg \
|
||||
spi \
|
||||
tablefunc \
|
||||
|
@ -136,10 +136,6 @@ reindexdb -
|
||||
Reindexes a database
|
||||
by Shaun Thomas <sthomas@townnews.com>
|
||||
|
||||
rtree_gist -
|
||||
Support for emulating RTREE indexing in GiST
|
||||
by Oleg Bartunov <oleg@sai.msu.su> and Teodor Sigaev <teodor@sigaev.ru>
|
||||
|
||||
seg -
|
||||
Confidence-interval datatype (GiST indexing example)
|
||||
by Gene Selkov, Jr. <selkovjr@mcs.anl.gov>
|
||||
|
@ -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…
x
Reference in New Issue
Block a user