Skip to content

Commit edfeeff

Browse files
smonkewitzMarkus Nullmeier
authored andcommitted
Fix incorrect query results with spatial indexes [#1011453]
The bounding box computation for ellipses, polygons, and paths appeared to be broken. As these bounding boxes are used by the R-tree on-top-of GiST index implementation in pgsphere, the effect was that some queries returned different results depending on whether or not the optimizer chose to make use of an available spatial index or not. I have attached a patch which fixes these problems as best as I can tell. http://pgfoundry.org/tracker/index.php?func=detail&aid=1011453&group_id=1000240&atid=914
1 parent 97b5a21 commit edfeeff

File tree

4 files changed

+194
-33
lines changed

4 files changed

+194
-33
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ EXTENSION = pg_sphere
88
DATA_built = pg_sphere--1.0.sql
99
DOCS = README.pg_sphere COPYRIGHT.pg_sphere
1010
REGRESS = init tables points euler circle line ellipse poly path box index \
11-
contains_ops contains_ops_compat
11+
contains_ops contains_ops_compat bounding_box_gist
1212

1313
EXTRA_CLEAN = pg_sphere--1.0.sql $(PGS_SQL)
1414

expected/bounding_box_gist.out

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
CREATE TABLE bbox_ellipse (e sellipse not null);
2+
INSERT INTO bbox_ellipse VALUES ('<{10d, 0.1d}, (0d,0d), 0d>');
3+
SELECT spoint '(5d, 0d)' @ sellipse '<{10d, 0.1d}, (0d,0d), 0d>' AS inside;
4+
inside
5+
--------
6+
t
7+
(1 row)
8+
9+
SELECT COUNT(*) FROM bbox_ellipse WHERE spoint '(5d, 0d)' @ e;
10+
count
11+
-------
12+
1
13+
(1 row)
14+
15+
-- The ellipse has semi-major axis length of 10 degrees along the equator,
16+
-- so (lon,lat) = (5,0) should be inside.
17+
CREATE INDEX idx_bbox_ellipse ON bbox_ellipse USING gist (e);
18+
ANALYZE bbox_ellipse;
19+
SET enable_seqscan=false;
20+
SELECT COUNT(*) FROM bbox_ellipse WHERE spoint '(5d, 0d)' @ e;
21+
count
22+
-------
23+
1
24+
(1 row)
25+
26+
CREATE TABLE bbox_poly (p spoly not null);
27+
INSERT INTO bbox_poly VALUES ('{(40d,-40d), (0d,80d), (-40d,-40d)}');
28+
SELECT spoint '(0d, 0d)' @ spoly '{(40d,-40d), (0d,80d), (-40d,-40d)}' AS inside;
29+
inside
30+
--------
31+
t
32+
(1 row)
33+
34+
SELECT COUNT(*) FROM bbox_poly WHERE spoint '(0d, 0d)' @ p;
35+
count
36+
-------
37+
1
38+
(1 row)
39+
40+
CREATE INDEX idx_bbox_poly ON bbox_poly USING gist (p);
41+
ANALYZE bbox_poly;
42+
SET enable_seqscan=false;
43+
SELECT COUNT(*) FROM bbox_poly WHERE spoint '(0d, 0d)' @ p;
44+
count
45+
-------
46+
1
47+
(1 row)
48+
49+
CREATE TABLE bbox_path (p spath not null);
50+
INSERT INTO bbox_path VALUES ('{(-46d,0d), (-45d,80d), (-45d,0d), (80d,0d)}');
51+
SELECT sline(spoint '(0d, -10d)', spoint '(0d, 10d)') && spath '{(-46d,0d), (-45d,80d), (-45d,0d), (80d,0d)}' AS crossing;
52+
crossing
53+
----------
54+
t
55+
(1 row)
56+
57+
SELECT spoint '(0d, 0d)' @ spath '{(-46d,0d), (-45d,80d), (-45d,0d), (80d,0d)}' AS inside;
58+
inside
59+
--------
60+
t
61+
(1 row)
62+
63+
SELECT COUNT(*) FROM bbox_path WHERE sline(spoint '(0d, -10d)', spoint '(0d, 10d)') && p;
64+
count
65+
-------
66+
1
67+
(1 row)
68+
69+
SELECT COUNT(*) FROM bbox_path WHERE spoint '(0d, 0d)' @ p;
70+
count
71+
-------
72+
1
73+
(1 row)
74+
75+
CREATE INDEX idx_bbox_path ON bbox_path USING gist (p);
76+
ANALYZE bbox_path;
77+
SET enable_seqscan=false;
78+
SELECT COUNT(*) FROM bbox_path WHERE sline(spoint '(0d, -10d)', spoint '(0d, 10d)') && p;
79+
count
80+
-------
81+
1
82+
(1 row)
83+
84+
SELECT COUNT(*) FROM bbox_path WHERE spoint '(0d, 0d)' @ p;
85+
count
86+
-------
87+
1
88+
(1 row)
89+

key.c

Lines changed: 73 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ sphereellipse_gen_key(int32 *k, const SELLIPSE *e)
197197

198198
r[0] = sin(e->rad[0]);
199199
r[1] = sin(e->rad[1]);
200-
d = cos(e->rad[1]);
200+
d = cos(e->rad[0]);
201201

202202
v[0].x = d;
203203
v[0].y = -r[0];
@@ -341,21 +341,36 @@ sphereline_gen_key(int32 *k, const SLine *sl)
341341
for (i = 0; i < 4; i++)
342342
{
343343
euler_vector_trans(&vt, &v[i], &se);
344-
if (vt.x >= -1.0 && vt.x <= 1.0)
344+
if (vt.x < -1.0)
345345
{
346-
vr[0].x = Min(vr[0].x, vt.x);
347-
vr[1].x = Max(vr[1].x, vt.x);
346+
vt.x = -1.0;
348347
}
349-
if (vt.y >= -1.0 && vt.y <= 1.0)
348+
if (vt.y < -1.0)
350349
{
351-
vr[0].y = Min(vr[0].y, vt.y);
352-
vr[1].y = Max(vr[1].y, vt.y);
350+
vt.y = -1.0;
353351
}
354-
if (vt.z >= -1.0 && vt.z <= 1.0)
352+
if (vt.z < -1.0)
355353
{
356-
vr[0].z = Min(vr[0].z, vt.z);
357-
vr[1].z = Max(vr[1].z, vt.z);
354+
vt.z = -1.0;
358355
}
356+
if (vt.x > 1.0)
357+
{
358+
vt.x = 1.0;
359+
}
360+
if (vt.y > 1.0)
361+
{
362+
vt.y = 1.0;
363+
}
364+
if (vt.z > 1.0)
365+
{
366+
vt.z = 1.0;
367+
}
368+
vr[0].x = Min(vr[0].x, vt.x);
369+
vr[1].x = Max(vr[1].x, vt.x);
370+
vr[0].y = Min(vr[0].y, vt.y);
371+
vr[1].y = Max(vr[1].y, vt.y);
372+
vr[0].z = Min(vr[0].z, vt.z);
373+
vr[1].z = Max(vr[1].z, vt.z);
359374
}
360375

361376
k[0] = vr[0].x * ks;
@@ -375,6 +390,7 @@ spherepoly_gen_key(int32 *key, const SPOLY *sp)
375390
{
376391
int32 i;
377392
SLine l;
393+
SPoint p;
378394
int32 tk[6];
379395
bool start = true;
380396

@@ -396,40 +412,65 @@ spherepoly_gen_key(int32 *key, const SPOLY *sp)
396412
key[4] = Max(key[4], tk[4]);
397413
key[5] = Max(key[5], tk[5]);
398414
}
415+
p.lng = 0.0;
416+
p.lat = PIH;
417+
if (spoly_contains_point(sp, &p))
418+
{
419+
key[5] = MAXCVALUE;
420+
}
421+
p.lat = -PIH;
422+
if (spoly_contains_point(sp, &p))
423+
{
424+
key[2] = -MAXCVALUE;
425+
}
426+
p.lat = 0.0;
427+
if (spoly_contains_point(sp, &p))
428+
{
429+
key[3] = MAXCVALUE;
430+
}
431+
p.lng = PI;
432+
if (spoly_contains_point(sp, &p))
433+
{
434+
key[0] = -MAXCVALUE;
435+
}
436+
p.lng = PIH;
437+
if (spoly_contains_point(sp, &p))
438+
{
439+
key[4] = MAXCVALUE;
440+
}
441+
p.lng = PI + PIH;
442+
if (spoly_contains_point(sp, &p))
443+
{
444+
key[1] = -MAXCVALUE;
445+
}
399446
}
400447
}
401448

402449
void
403450
spherepath_gen_key(int32 *key, const SPATH *sp)
404451
{
405-
int32 i,
406-
k,
407-
r;
452+
int32 i;
408453
SLine l;
409454
int32 tk[6];
410455
bool start = true;
411456

412-
for (i = 0; i < sp->npts; i++)
457+
for (i=0; i < sp->npts - 1; i++)
413458
{
414-
for (k = i + 1; i < sp->npts; i++)
459+
sline_from_points(&l, &sp->p[i], &sp->p[i + 1]);
460+
sphereline_gen_key(&tk[0], &l);
461+
if (start)
415462
{
416-
r = ((k == sp->npts) ? (0) : (k));
417-
sline_from_points(&l, &sp->p[i], &sp->p[r]);
418-
sphereline_gen_key(&tk[0], &l);
419-
if (start)
420-
{
421-
start = false;
422-
memcpy((void *) key, (void *) &tk[0], KEYSIZE);
423-
}
424-
else
425-
{
426-
key[0] = Min(key[0], tk[0]);
427-
key[1] = Min(key[1], tk[1]);
428-
key[2] = Min(key[2], tk[2]);
429-
key[3] = Max(key[3], tk[3]);
430-
key[4] = Max(key[4], tk[4]);
431-
key[5] = Max(key[5], tk[5]);
432-
}
463+
start = false;
464+
memcpy((void *) key, (void *) &tk[0], KEYSIZE);
465+
}
466+
else
467+
{
468+
key[0] = Min(key[0], tk[0]);
469+
key[1] = Min(key[1], tk[1]);
470+
key[2] = Min(key[2], tk[2]);
471+
key[3] = Max(key[3], tk[3]);
472+
key[4] = Max(key[4], tk[4]);
473+
key[5] = Max(key[5], tk[5]);
433474
}
434475
}
435476
}

sql/bounding_box_gist.sql

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
CREATE TABLE bbox_ellipse (e sellipse not null);
2+
INSERT INTO bbox_ellipse VALUES ('<{10d, 0.1d}, (0d,0d), 0d>');
3+
SELECT spoint '(5d, 0d)' @ sellipse '<{10d, 0.1d}, (0d,0d), 0d>' AS inside;
4+
SELECT COUNT(*) FROM bbox_ellipse WHERE spoint '(5d, 0d)' @ e;
5+
-- The ellipse has semi-major axis length of 10 degrees along the equator,
6+
-- so (lon,lat) = (5,0) should be inside.
7+
CREATE INDEX idx_bbox_ellipse ON bbox_ellipse USING gist (e);
8+
ANALYZE bbox_ellipse;
9+
SET enable_seqscan=false;
10+
SELECT COUNT(*) FROM bbox_ellipse WHERE spoint '(5d, 0d)' @ e;
11+
12+
CREATE TABLE bbox_poly (p spoly not null);
13+
INSERT INTO bbox_poly VALUES ('{(40d,-40d), (0d,80d), (-40d,-40d)}');
14+
SELECT spoint '(0d, 0d)' @ spoly '{(40d,-40d), (0d,80d), (-40d,-40d)}' AS inside;
15+
SELECT COUNT(*) FROM bbox_poly WHERE spoint '(0d, 0d)' @ p;
16+
CREATE INDEX idx_bbox_poly ON bbox_poly USING gist (p);
17+
ANALYZE bbox_poly;
18+
SET enable_seqscan=false;
19+
SELECT COUNT(*) FROM bbox_poly WHERE spoint '(0d, 0d)' @ p;
20+
21+
CREATE TABLE bbox_path (p spath not null);
22+
INSERT INTO bbox_path VALUES ('{(-46d,0d), (-45d,80d), (-45d,0d), (80d,0d)}');
23+
SELECT sline(spoint '(0d, -10d)', spoint '(0d, 10d)') && spath '{(-46d,0d), (-45d,80d), (-45d,0d), (80d,0d)}' AS crossing;
24+
SELECT spoint '(0d, 0d)' @ spath '{(-46d,0d), (-45d,80d), (-45d,0d), (80d,0d)}' AS inside;
25+
SELECT COUNT(*) FROM bbox_path WHERE sline(spoint '(0d, -10d)', spoint '(0d, 10d)') && p;
26+
SELECT COUNT(*) FROM bbox_path WHERE spoint '(0d, 0d)' @ p;
27+
CREATE INDEX idx_bbox_path ON bbox_path USING gist (p);
28+
ANALYZE bbox_path;
29+
SET enable_seqscan=false;
30+
SELECT COUNT(*) FROM bbox_path WHERE sline(spoint '(0d, -10d)', spoint '(0d, 10d)') && p;
31+
SELECT COUNT(*) FROM bbox_path WHERE spoint '(0d, 0d)' @ p;

0 commit comments

Comments
 (0)