提交 7b3d94bd authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Add ST_EXTENT aggregate function

上级 fe25a8f0
...@@ -3437,6 +3437,18 @@ MODE(X ORDER BY X) ...@@ -3437,6 +3437,18 @@ MODE(X ORDER BY X)
MODE() WITHIN GROUP(ORDER BY X) MODE() WITHIN GROUP(ORDER BY X)
" "
"Functions (Aggregate)","ST_EXTENT","
{ ST_EXTENT( value ) [ FILTER ( WHERE expression ) ]
","
Returns the minimum bounding box that encloses all specified GEOMETRY values.
Only 2D coordinate plane is supported.
NULL values are ignored in the calculation.
If no rows are selected, the result is NULL.
Aggregates are only allowed in select statements.
","
ST_EXTENT(X)
"
"Functions (Numeric)","ABS"," "Functions (Numeric)","ABS","
ABS ( { numeric } ) ABS ( { numeric } )
"," ","
......
...@@ -142,6 +142,11 @@ public class Aggregate extends Expression { ...@@ -142,6 +142,11 @@ public class Aggregate extends Expression {
* The aggregate type for MODE(expression). * The aggregate type for MODE(expression).
*/ */
MODE, MODE,
/**
* The aggregate type for ST_EXTENT(expression).
*/
ST_EXTENT,
} }
private static final HashMap<String, AggregateType> AGGREGATES = new HashMap<>(64); private static final HashMap<String, AggregateType> AGGREGATES = new HashMap<>(64);
...@@ -212,6 +217,7 @@ public class Aggregate extends Expression { ...@@ -212,6 +217,7 @@ public class Aggregate extends Expression {
addAggregate("MODE", AggregateType.MODE); addAggregate("MODE", AggregateType.MODE);
// Oracle compatibility // Oracle compatibility
addAggregate("STATS_MODE", AggregateType.MODE); addAggregate("STATS_MODE", AggregateType.MODE);
addAggregate("ST_EXTENT", AggregateType.ST_EXTENT);
} }
private static void addAggregate(String name, AggregateType type) { private static void addAggregate(String name, AggregateType type) {
...@@ -369,9 +375,10 @@ public class Aggregate extends Expression { ...@@ -369,9 +375,10 @@ public class Aggregate extends Expression {
} }
return v; return v;
} }
case MEDIAN: { case MEDIAN:
return AggregateDataMedian.getResultFromIndex(session, on, dataType); return AggregateDataMedian.getResultFromIndex(session, on, dataType);
} case ST_EXTENT:
return AggregateDataST_Extent.getResultFromIndex(session, on);
default: default:
DbException.throwInternalError("type=" + type); DbException.throwInternalError("type=" + type);
} }
...@@ -554,6 +561,11 @@ public class Aggregate extends Expression { ...@@ -554,6 +561,11 @@ public class Aggregate extends Expression {
scale = 0; scale = 0;
precision = displaySize = Integer.MAX_VALUE; precision = displaySize = Integer.MAX_VALUE;
break; break;
case ST_EXTENT:
dataType = Value.GEOMETRY;
scale = 0;
precision = displaySize = Integer.MAX_VALUE;
break;
default: default:
DbException.throwInternalError("type=" + type); DbException.throwInternalError("type=" + type);
} }
...@@ -699,6 +711,9 @@ public class Aggregate extends Expression { ...@@ -699,6 +711,9 @@ public class Aggregate extends Expression {
case MODE: case MODE:
text = "MODE"; text = "MODE";
break; break;
case ST_EXTENT:
text = "ST_EXTENT";
break;
default: default:
throw DbException.throwInternalError("type=" + type); throw DbException.throwInternalError("type=" + type);
} }
...@@ -749,6 +764,8 @@ public class Aggregate extends Expression { ...@@ -749,6 +764,8 @@ public class Aggregate extends Expression {
return false; return false;
} }
return AggregateDataMedian.getMedianColumnIndex(on) != null; return AggregateDataMedian.getMedianColumnIndex(on) != null;
case ST_EXTENT:
return AggregateDataST_Extent.getGeometryColumnIndex(on) != null;
default: default:
return false; return false;
} }
......
...@@ -37,6 +37,8 @@ abstract class AggregateData { ...@@ -37,6 +37,8 @@ abstract class AggregateData {
return new AggregateDataMedian(); return new AggregateDataMedian();
case MODE: case MODE:
return new AggregateDataMode(); return new AggregateDataMode();
case ST_EXTENT:
return new AggregateDataST_Extent();
default: default:
return new AggregateDataDefault(aggregateType); return new AggregateDataDefault(aggregateType);
} }
......
/*
* Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression.aggregate;
import java.util.ArrayList;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.index.Index;
import org.h2.mvstore.db.MVSpatialIndex;
import org.h2.mvstore.rtree.SpatialKey;
import org.h2.table.Column;
import org.h2.table.TableFilter;
import org.h2.value.Value;
import org.h2.value.ValueGeometry;
import org.h2.value.ValueNull;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.GeometryFactory;
/**
* Data stored while calculating an aggregate.
*/
class AggregateDataST_Extent extends AggregateData {
private Envelope envelope;
/**
* Get the index (if any) for the column specified in the geometry
* aggregate.
*
* @param on
* the expression (usually a column expression)
* @return the index, or null
*/
static Index getGeometryColumnIndex(Expression on) {
if (on instanceof ExpressionColumn) {
ExpressionColumn col = (ExpressionColumn) on;
Column column = col.getColumn();
if (column.getType() == Value.GEOMETRY) {
TableFilter filter = col.getTableFilter();
if (filter != null) {
ArrayList<Index> indexes = filter.getTable().getIndexes();
if (indexes != null) {
for (int i = 1, size = indexes.size(); i < size; i++) {
Index index = indexes.get(i);
if (index instanceof MVSpatialIndex) {
return index;
}
}
}
}
}
}
return null;
}
/**
* Get the result from the index.
*
* @param session
* the session
* @param on
* the expression
* @return the result
*/
static Value getResultFromIndex(Session session, Expression on) {
MVSpatialIndex index = (MVSpatialIndex) getGeometryColumnIndex(on);
MVSpatialIndex.MVStoreCursor cursor = (MVSpatialIndex.MVStoreCursor) index.find(session, null, null);
Envelope envelope = new Envelope();
while (cursor.next()) {
SpatialKey key = cursor.getKey();
if (!key.isNull()) {
envelope.expandToInclude(key.min(0), key.min(1));
envelope.expandToInclude(key.max(0), key.max(1));
}
}
if (envelope.isNull()) {
return ValueNull.INSTANCE;
}
return ValueGeometry.getFromGeometry(new GeometryFactory().toGeometry(envelope));
}
@Override
void add(Database database, int dataType, boolean distinct, Value v) {
if (v == ValueNull.INSTANCE) {
return;
}
if (envelope == null) {
envelope = new Envelope();
}
envelope.expandToInclude(((ValueGeometry) v.convertTo(Value.GEOMETRY)).getEnvelopeNoCopy());
}
@Override
Value getValue(Database database, int dataType, boolean distinct) {
if (envelope == null || envelope.isNull()) {
return ValueNull.INSTANCE;
}
return ValueGeometry.getFromGeometry(new GeometryFactory().toGeometry(envelope));
}
}
...@@ -321,7 +321,7 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex { ...@@ -321,7 +321,7 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
/** /**
* A cursor. * A cursor.
*/ */
private static class MVStoreCursor implements Cursor { public static class MVStoreCursor implements Cursor {
private final Session session; private final Session session;
private final Iterator<SpatialKey> it; private final Iterator<SpatialKey> it;
...@@ -358,6 +358,15 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex { ...@@ -358,6 +358,15 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
return searchRow; return searchRow;
} }
/**
* Returns the current key.
*
* @return the current key
*/
public SpatialKey getKey() {
return current;
}
@Override @Override
public boolean next() { public boolean next() {
current = it.hasNext() ? it.next() : null; current = it.hasNext() ? it.next() : null;
......
...@@ -137,7 +137,7 @@ public class TestScript extends TestDb { ...@@ -137,7 +137,7 @@ public class TestScript extends TestDb {
testScript("other/" + s + ".sql"); testScript("other/" + s + ".sql");
} }
for (String s : new String[] { "avg", "bit-and", "bit-or", "count", for (String s : new String[] { "avg", "bit-and", "bit-or", "count",
"group-concat", "max", "median", "min", "mode", "selectivity", "stddev-pop", "group-concat", "max", "median", "min", "mode", "selectivity", "st_extent", "stddev-pop",
"stddev-samp", "sum", "var-pop", "var-samp", "array-agg" }) { "stddev-samp", "sum", "var-pop", "var-samp", "array-agg" }) {
testScript("functions/aggregate/" + s + ".sql"); testScript("functions/aggregate/" + s + ".sql");
} }
......
-- Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
-- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--
CREATE TABLE TEST(V GEOMETRY);
> ok
SELECT ST_EXTENT(V) FROM TEST;
>> null
INSERT INTO TEST VALUES ('POINT(1 1)');
> update count: 1
SELECT ST_EXTENT(V) FROM TEST;
>> POINT (1 1)
INSERT INTO TEST VALUES ('POINT(1 2)'), (NULL), ('POINT(3 1)');
> update count: 3
SELECT ST_EXTENT(V), ST_EXTENT(V) FILTER (WHERE V <> 'POINT(3 1)') FILTERED1,
ST_EXTENT(V) FILTER (WHERE V <> 'POINT(1 2)') FILTERED2 FROM TEST;
> ST_EXTENT(V) FILTERED1 FILTERED2
> ----------------------------------- --------------------- ---------------------
> POLYGON ((1 1, 1 2, 3 2, 3 1, 1 1)) LINESTRING (1 1, 1 2) LINESTRING (1 1, 3 1)
> rows: 1
CREATE SPATIAL INDEX IDX ON TEST(V);
> ok
-- With index
SELECT ST_EXTENT(V) FROM TEST;
>> POLYGON ((1 1, 1 2, 3 2, 3 1, 1 1))
SELECT ST_EXTENT(V) FILTER (WHERE V <> 'POINT(3 1)') FILTERED FROM TEST;
>> LINESTRING (1 1, 1 2)
SELECT ST_EXTENT(V) FROM TEST WHERE V <> 'POINT(3 1)';
>> LINESTRING (1 1, 1 2)
TRUNCATE TABLE TEST;
> ok
-- With index
SELECT ST_EXTENT(V) FROM TEST;
>> null
DROP TABLE TEST;
> ok
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论