提交 f676af90 authored 作者: Thomas Mueller's avatar Thomas Mueller

Issue 609: the spatial index did not support NULL.

上级 c7233861
......@@ -20,7 +20,8 @@ Change Log
<h1>Change Log</h1>
<h2>Next Version (unreleased)</h2>
<ul><li>Granting a schema is now supported.
<ul><li>Issue 609: the spatial index did not support NULL.
</li><li>Granting a schema is now supported.
</li><li>Linked tables did not work when a function-based index is present (Oracle).
</li><li>Creating a user with a null password, salt, or hash threw a NullPointerException.
</li><li>Foreign key: don't add a single column index if column
......
......@@ -10,6 +10,7 @@ import java.util.ArrayList;
import org.h2.mvstore.DataUtils;
import org.h2.mvstore.WriteBuffer;
import org.h2.mvstore.type.DataType;
import org.h2.util.New;
/**
* A spatial data type. This class supports up to 31 dimensions. Each dimension
......@@ -138,6 +139,9 @@ public class SpatialDataType implements DataType {
public boolean isOverlap(Object objA, Object objB) {
SpatialKey a = (SpatialKey) objA;
SpatialKey b = (SpatialKey) objB;
if (a.isNull() || b.isNull()) {
return false;
}
for (int i = 0; i < dimensions; i++) {
if (a.max(i) < b.min(i) || a.min(i) > b.max(i)) {
return false;
......@@ -153,8 +157,11 @@ public class SpatialDataType implements DataType {
* @param add the value
*/
public void increaseBounds(Object bounds, Object add) {
SpatialKey b = (SpatialKey) bounds;
SpatialKey a = (SpatialKey) add;
SpatialKey b = (SpatialKey) bounds;
if (a.isNull() || b.isNull()) {
return;
}
for (int i = 0; i < dimensions; i++) {
b.setMin(i, Math.min(b.min(i), a.min(i)));
b.setMax(i, Math.max(b.max(i), a.max(i)));
......@@ -169,8 +176,11 @@ public class SpatialDataType implements DataType {
* @return the area
*/
public float getAreaIncrease(Object objA, Object objB) {
SpatialKey a = (SpatialKey) objA;
SpatialKey b = (SpatialKey) objB;
SpatialKey a = (SpatialKey) objA;
if (a.isNull() || b.isNull()) {
return 0;
}
float min = a.min(0);
float max = a.max(0);
float areaOld = max - min;
......@@ -198,6 +208,11 @@ public class SpatialDataType implements DataType {
float getCombinedArea(Object objA, Object objB) {
SpatialKey a = (SpatialKey) objA;
SpatialKey b = (SpatialKey) objB;
if (a.isNull()) {
return getArea(b);
} else if (b.isNull()) {
return getArea(a);
}
float area = 1;
for (int i = 0; i < dimensions; i++) {
float min = Math.min(a.min(i), b.min(i));
......@@ -207,6 +222,17 @@ public class SpatialDataType implements DataType {
return area;
}
private float getArea(SpatialKey a) {
if (a.isNull()) {
return 0;
}
float area = 1;
for (int i = 0; i < dimensions; i++) {
area *= a.max(i) - a.min(i);
}
return area;
}
/**
* Check whether a contains b.
*
......@@ -217,6 +243,9 @@ public class SpatialDataType implements DataType {
public boolean contains(Object objA, Object objB) {
SpatialKey a = (SpatialKey) objA;
SpatialKey b = (SpatialKey) objB;
if (a.isNull() || b.isNull()) {
return false;
}
for (int i = 0; i < dimensions; i++) {
if (a.min(i) > b.min(i) || a.max(i) < b.max(i)) {
return false;
......@@ -236,6 +265,9 @@ public class SpatialDataType implements DataType {
public boolean isInside(Object objA, Object objB) {
SpatialKey a = (SpatialKey) objA;
SpatialKey b = (SpatialKey) objB;
if (a.isNull() || b.isNull()) {
return false;
}
for (int i = 0; i < dimensions; i++) {
if (a.min(i) <= b.min(i) || a.max(i) >= b.max(i)) {
return false;
......@@ -251,8 +283,11 @@ public class SpatialDataType implements DataType {
* @return the bounding box
*/
Object createBoundingBox(Object objA) {
float[] minMax = new float[dimensions * 2];
SpatialKey a = (SpatialKey) objA;
if (a.isNull()) {
return a;
}
float[] minMax = new float[dimensions * 2];
for (int i = 0; i < dimensions; i++) {
minMax[i + i] = a.min(i);
minMax[i + i + 1] = a.max(i);
......@@ -269,6 +304,10 @@ public class SpatialDataType implements DataType {
* @return the indexes of the extremes
*/
public int[] getExtremes(ArrayList<Object> list) {
list = getNotNull(list);
if (list.size() == 0) {
return null;
}
SpatialKey bounds = (SpatialKey) createBoundingBox(list.get(0));
SpatialKey boundsInner = (SpatialKey) createBoundingBox(bounds);
for (int i = 0; i < dimensions; i++) {
......@@ -313,6 +352,27 @@ public class SpatialDataType implements DataType {
return new int[] { firstIndex, lastIndex };
}
ArrayList<Object> getNotNull(ArrayList<Object> list) {
ArrayList<Object> result = null;
for (Object o : list) {
SpatialKey a = (SpatialKey) o;
if (a.isNull()) {
result = New.arrayList();
break;
}
}
if (result == null) {
return list;
}
for (Object o : list) {
SpatialKey a = (SpatialKey) o;
if (!a.isNull()) {
result.add(a);
}
}
return result;
}
private void increaseMaxInnerBounds(Object bounds, Object add) {
SpatialKey b = (SpatialKey) bounds;
SpatialKey a = (SpatialKey) add;
......
......@@ -91,6 +91,7 @@ public class TestSpatial extends TestBase {
testExplainSpatialIndexWithPk();
testNullableGeometry();
testNullableGeometryDelete();
testNullableGeometryInsert();
testNullableGeometryUpdate();
}
......@@ -204,9 +205,11 @@ public class TestSpatial extends TestBase {
"(id int primary key, poly geometry)");
stat.execute("insert into test values(1, " +
"'POLYGON ((1 1, 1 2, 2 2, 1 1))')");
stat.execute("insert into test values(2, " +
"'POLYGON ((3 1, 3 2, 4 2, 3 1))')");
stat.execute("insert into test values(2,null)");
stat.execute("insert into test values(3, " +
"'POLYGON ((3 1, 3 2, 4 2, 3 1))')");
stat.execute("insert into test values(4,null)");
stat.execute("insert into test values(5, " +
"'POLYGON ((1 3, 1 4, 2 4, 1 3))')");
stat.execute("create spatial index on test(poly)");
......@@ -260,18 +263,20 @@ public class TestSpatial extends TestBase {
"(id int primary key, poly geometry)");
stat.execute("insert into test values(1, " +
"'POLYGON ((1 1, 1 2, 2 2, 1 1))')");
stat.execute("insert into test values(2, " +
"'POLYGON ((3 1, 3 2, 4 2, 3 1))')");
stat.execute("insert into test values(2,null)");
stat.execute("insert into test values(3, " +
"'POLYGON ((3 1, 3 2, 4 2, 3 1))')");
stat.execute("insert into test values(4,null)");
stat.execute("insert into test values(5, " +
"'POLYGON ((1 3, 1 4, 2 4, 1 3))')");
ResultSet rs = stat.executeQuery(
"select * from test " +
"where NOT poly && 'POINT (1.5 1.5)'::Geometry");
assertTrue(rs.next());
assertEquals(2, rs.getInt("id"));
assertTrue(rs.next());
assertEquals(3, rs.getInt("id"));
assertTrue(rs.next());
assertEquals(5, rs.getInt("id"));
assertFalse(rs.next());
stat.execute("drop table test");
} finally {
......@@ -294,6 +299,10 @@ public class TestSpatial extends TestBase {
"'POLYGON ((90 9, 190 9, 190 -91, 90 -91, 90 9))')");
stat.execute("insert into area values(6, " +
"'POLYGON ((190 9, 290 9, 290 -91, 190 -91, 190 9))')");
stat.execute("insert into area values(7,null)");
stat.execute("insert into area values(8,null)");
stat.execute("create table roads(idRoad int primary key, the_geom geometry)");
stat.execute("create spatial index on roads(the_geom)");
stat.execute("insert into roads values(1, " +
......@@ -317,6 +326,9 @@ public class TestSpatial extends TestBase {
stat.execute("insert into roads values(7, " +
"'LINESTRING (60.321361058601155 -13.099243856332663, " +
"149.24385633270325 5.955576559546344)')");
stat.execute("insert into roads values(8, null)");
stat.execute("insert into roads values(9, null)");
}
private void testSpatialIndexQueryMultipleTable() throws SQLException {
......@@ -369,6 +381,7 @@ public class TestSpatial extends TestBase {
createTestTable(stat);
Savepoint sp = conn.setSavepoint();
// Remove a row but do not commit
stat.execute("delete from roads where idRoad=9");
stat.execute("delete from roads where idRoad=7");
// Check if index is updated
ResultSet rs = stat.executeQuery(
......@@ -416,6 +429,7 @@ public class TestSpatial extends TestBase {
stat.execute("create memory table test(id int primary key, polygon geometry)");
stat.execute("create spatial index idx_test_polygon on test(polygon)");
stat.execute("insert into test values(1, 'POLYGON ((1 1, 1 2, 2 2, 1 1))')");
stat.execute("insert into test values(2, null)");
ResultSet rs;
// an query that can not possibly return a result
......@@ -707,7 +721,7 @@ public class TestSpatial extends TestBase {
st.execute("CREATE AGGREGATE TABLE_ENVELOPE FOR \""+
TableEnvelope.class.getName()+"\"");
st.execute("CREATE TABLE test(the_geom GEOMETRY)");
st.execute("INSERT INTO test VALUES ('POINT(1 1)'), ('POINT(10 5)')");
st.execute("INSERT INTO test VALUES ('POINT(1 1)'), (null), (null), ('POINT(10 5)')");
ResultSet rs = st.executeQuery("select TABLE_ENVELOPE(the_geom) from test");
assertEquals("geometry", rs.getMetaData().
getColumnTypeName(1).toLowerCase());
......@@ -769,6 +783,7 @@ public class TestSpatial extends TestBase {
stat.execute("drop view if exists test_view");
stat.execute("create table test(id int primary key, poly geometry)");
stat.execute("insert into test values(1, 'POLYGON ((1 1, 1 2, 2 2, 1 1))')");
stat.execute("insert into test values(4, null)");
stat.execute("insert into test values(2, 'POLYGON ((3 1, 3 2, 4 2, 3 1))')");
stat.execute("insert into test values(3, 'POLYGON ((1 3, 1 4, 2 4, 1 3))')");
stat.execute("create view test_view as select * from test");
......@@ -925,10 +940,45 @@ public class TestSpatial extends TestBase {
stat.execute("insert into test values(2, null)");
stat.execute("delete from test where the_geom is null");
stat.execute("insert into test values(1, null)");
stat.execute("insert into test values(2, null)");
stat.execute("insert into test values(3, 'POLYGON ((1000 2000, 1000 3000, 2000 3000, 1000 2000))')");
stat.execute("insert into test values(4, null)");
stat.execute("insert into test values(5, null)");
stat.execute("insert into test values(6, 'POLYGON ((1000 3000, 1000 4000, 2000 4000, 1000 3000))')");
ResultSet rs = stat.executeQuery("select * from test");
assertTrue(rs.next());
assertEquals(1, rs.getInt(1));
assertNull(rs.getObject(2));
int count = 0;
while (rs.next()) {
count++;
int id = rs.getInt(1);
if (id == 3 || id == 6) {
assertTrue(rs.getObject(2) != null);
} else {
assertNull(rs.getObject(2));
}
}
assertEquals(6, count);
rs = stat.executeQuery("select * from test where the_geom is null");
count = 0;
while (rs.next()) {
count++;
assertNull(rs.getObject(2));
}
assertEquals(4, count);
rs = stat.executeQuery("select * from test where the_geom is not null");
count = 0;
while (rs.next()) {
count++;
assertTrue(rs.getObject(2) != null);
}
assertEquals(2, count);
rs = stat.executeQuery(
"select * from test " +
"where intersects(the_geom, 'POLYGON ((1000 1000, 1000 2000, 2000 2000, 1000 1000))')");
conn.close();
if (!config.memory) {
conn = getConnection(url);
......@@ -952,10 +1002,10 @@ public class TestSpatial extends TestBase {
stat.execute("insert into test values(1, null)");
stat.execute("insert into test values(2, null)");
stat.execute("insert into test values(3, null)");
ResultSet rs = stat.executeQuery("select * from test");
assertTrue(rs.next());
assertEquals(1, rs.getInt(1));
assertNull(rs.getObject(2));
ResultSet rs = stat.executeQuery("select * from test order by id");
while (rs.next()) {
assertNull(rs.getObject(2));
}
stat.execute("delete from test where id = 1");
stat.execute("delete from test where id = 2");
stat.execute("delete from test where id = 3");
......@@ -969,6 +1019,24 @@ public class TestSpatial extends TestBase {
deleteDb("spatial");
}
private void testNullableGeometryInsert() throws SQLException {
deleteDb("spatial");
Connection conn = getConnection(url);
Statement stat = conn.createStatement();
stat.execute("create memory table test"
+ "(id identity, the_geom geometry)");
stat.execute("create spatial index on test(the_geom)");
for (int i = 0; i < 1000; i++) {
stat.execute("insert into test values(null, null)");
}
ResultSet rs = stat.executeQuery("select * from test");
while (rs.next()) {
assertNull(rs.getObject(2));
}
conn.close();
deleteDb("spatial");
}
private void testNullableGeometryUpdate() throws SQLException {
deleteDb("spatial");
Connection conn = getConnection(url);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论