Unverified 提交 5f682ece authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov 提交者: GitHub

Merge pull request #1422 from katzyn/st_extent

Add ENVELOPE aggregate function
......@@ -1839,7 +1839,7 @@ but only for at most the time defined by the database setting ""h2.maxCompactTim
SHUTDOWN IMMEDIATELY closes the database files without any cleanup and without compacting.
SHUTDOWN DEFRAG re-orders the pages when closing the database so that table scans are faster. In case of MVStore it is currently equvalent to COMPACT.
SHUTDOWN DEFRAG re-orders the pages when closing the database so that table scans are faster. In case of MVStore it is currently equivalent to COMPACT.
Admin rights are required to execute this command.
","
......@@ -3437,6 +3437,18 @@ MODE(X ORDER BY X)
MODE() WITHIN GROUP(ORDER BY X)
"
"Functions (Aggregate)","ST_EXTENT","
ENVELOPE( 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.
","
ENVELOPE(X)
"
"Functions (Numeric)","ABS","
ABS ( { numeric } )
","
......@@ -4724,6 +4736,23 @@ Throw an SQLException with the passed SQLState and reason.
CALL SIGNAL('23505', 'Duplicate user ID: ' || user_id);
"
"Functions (System)","ESTIMATED_ENVELOPE","
ESTIMATED_ENVELOPE(tableNameString, columnNameString)
","
Returns the estimated minimum bounding box that encloses all specified GEOMETRY values.
Only 2D coordinate plane is supported.
NULL values are ignored.
This function is only supported by MVStore engine.
Column must have a spatial index.
This function is fast, but estimation may include uncommitted data (including data from other transactions),
may return approximate bounds, or be different with actual value due to other reasons.
Use with caution.
If estimation is not available this function returns NULL.
For accurate and reliable result use ESTIMATE aggregate function instead.
","
CALL ESTIMATED_ENVELOPE('MY_TABLE', 'GEOMETRY_COLUMN');
"
"Functions (System)","FILE_READ","
FILE_READ(fileNameString [,encodingString])
","
......
......@@ -31,7 +31,7 @@
42S12=Index {0} not found
42S21=Duplicate column name {0}
42S22=Column {0} not found
42S32=Setting {0} not found
42S31=Identical expressions should be used; expected {0}, found {1}
57014=Statement was canceled or the session timed out
90000=Function {0} must return a result set
90001=Method is not allowed for a query. Use execute or executeQuery instead of executeUpdate
......
......@@ -403,7 +403,7 @@ public class ErrorCode {
public static final int COLUMN_NOT_FOUND_1 = 42122;
/**
* The error with code <code>42123</code> is thrown when
* The error with code <code>42131</code> is thrown when
* identical expressions should be used, but different
* exceptions were found.
* Example:
......
......@@ -27,9 +27,11 @@ import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Mode;
import org.h2.engine.Session;
import org.h2.index.Index;
import org.h2.message.DbException;
import org.h2.mode.FunctionsMSSQLServer;
import org.h2.mode.FunctionsMySQL;
import org.h2.mvstore.db.MVSpatialIndex;
import org.h2.schema.Schema;
import org.h2.schema.Sequence;
import org.h2.security.BlockCipher;
......@@ -116,7 +118,7 @@ public class Function extends Expression implements FunctionCall {
public static final int DATABASE = 150, USER = 151, CURRENT_USER = 152,
IDENTITY = 153, SCOPE_IDENTITY = 154, AUTOCOMMIT = 155,
READONLY = 156, DATABASE_PATH = 157, LOCK_TIMEOUT = 158,
DISK_SPACE_USED = 159, SIGNAL = 160;
DISK_SPACE_USED = 159, SIGNAL = 160, ESTIMATED_ENVELOPE = 161;
private static final Pattern SIGNAL_PATTERN = Pattern.compile("[0-9A-Z]{5}");
......@@ -456,6 +458,7 @@ public class Function extends Expression implements FunctionCall {
addFunctionNotDeterministic("DISK_SPACE_USED", DISK_SPACE_USED,
1, Value.LONG);
addFunctionWithNull("SIGNAL", SIGNAL, 2, Value.NULL);
addFunctionNotDeterministic("ESTIMATED_ENVELOPE", ESTIMATED_ENVELOPE, 2, Value.LONG);
addFunction("H2VERSION", H2VERSION, 0, Value.STRING);
// TableFunction
......@@ -883,6 +886,9 @@ public class Function extends Expression implements FunctionCall {
case DISK_SPACE_USED:
result = ValueLong.get(getDiskSpaceUsed(session, v0));
break;
case ESTIMATED_ENVELOPE:
result = getEstimatedEnvelope(session, v0, values[1]);
break;
case CAST:
case CONVERT: {
Mode mode = database.getMode();
......@@ -1096,11 +1102,27 @@ public class Function extends Expression implements FunctionCall {
return false;
}
private static long getDiskSpaceUsed(Session session, Value v0) {
Parser p = new Parser(session);
String sql = v0.getString();
Table table = p.parseTableName(sql);
return table.getDiskSpaceUsed();
private static long getDiskSpaceUsed(Session session, Value tableName) {
return getTable(session, tableName).getDiskSpaceUsed();
}
private static Value getEstimatedEnvelope(Session session, Value tableName, Value columnName) {
Table table = getTable(session, tableName);
Column column = table.getColumn(columnName.getString());
ArrayList<Index> indexes = table.getIndexes();
if (indexes != null) {
for (int i = 1, size = indexes.size(); i < size; i++) {
Index index = indexes.get(i);
if (index instanceof MVSpatialIndex && index.isFirstColumn(column)) {
return ((MVSpatialIndex) index).getEstimatedBounds(session);
}
}
}
return ValueNull.INSTANCE;
}
private static Table getTable(Session session, Value tableName) {
return new Parser(session).parseTableName(tableName.getString());
}
protected static Value getNullOrValue(Session session, Expression[] args,
......
......@@ -19,6 +19,7 @@ import org.h2.expression.ExpressionVisitor;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.message.DbException;
import org.h2.mvstore.db.MVSpatialIndex;
import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
import org.h2.table.Column;
......@@ -142,6 +143,11 @@ public class Aggregate extends Expression {
* The aggregate type for MODE(expression).
*/
MODE,
/**
* The aggregate type for ENVELOPE(expression).
*/
ENVELOPE,
}
private static final HashMap<String, AggregateType> AGGREGATES = new HashMap<>(64);
......@@ -212,6 +218,7 @@ public class Aggregate extends Expression {
addAggregate("MODE", AggregateType.MODE);
// Oracle compatibility
addAggregate("STATS_MODE", AggregateType.MODE);
addAggregate("ENVELOPE", AggregateType.ENVELOPE);
}
private static void addAggregate(String name, AggregateType type) {
......@@ -369,9 +376,10 @@ public class Aggregate extends Expression {
}
return v;
}
case MEDIAN: {
case MEDIAN:
return AggregateDataMedian.getResultFromIndex(session, on, dataType);
}
case ENVELOPE:
return ((MVSpatialIndex) AggregateDataEnvelope.getGeometryColumnIndex(on)).getBounds(session);
default:
DbException.throwInternalError("type=" + type);
}
......@@ -554,6 +562,11 @@ public class Aggregate extends Expression {
scale = 0;
precision = displaySize = Integer.MAX_VALUE;
break;
case ENVELOPE:
dataType = Value.GEOMETRY;
scale = 0;
precision = displaySize = Integer.MAX_VALUE;
break;
default:
DbException.throwInternalError("type=" + type);
}
......@@ -699,6 +712,9 @@ public class Aggregate extends Expression {
case MODE:
text = "MODE";
break;
case ENVELOPE:
text = "ENVELOPE";
break;
default:
throw DbException.throwInternalError("type=" + type);
}
......@@ -749,6 +765,8 @@ public class Aggregate extends Expression {
return false;
}
return AggregateDataMedian.getMedianColumnIndex(on) != null;
case ENVELOPE:
return AggregateDataEnvelope.getGeometryColumnIndex(on) != null;
default:
return false;
}
......
......@@ -37,6 +37,8 @@ abstract class AggregateData {
return new AggregateDataMedian();
case MODE:
return new AggregateDataMode();
case ENVELOPE:
return new AggregateDataEnvelope();
default:
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.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.index.Index;
import org.h2.mvstore.db.MVSpatialIndex;
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 AggregateDataEnvelope 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 && index.isFirstColumn(column)) {
return index;
}
}
}
}
}
}
return null;
}
@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));
}
}
......@@ -24,7 +24,6 @@ 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.Geometry;
/**
* This is an index based on a MVR-TreeMap.
......@@ -134,8 +133,7 @@ public class SpatialTreeIndex extends BaseIndex implements SpatialIndex {
if (v == ValueNull.INSTANCE) {
return new SpatialKey(row.getKey());
}
Geometry g = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getGeometryNoCopy();
Envelope env = g.getEnvelopeInternal();
Envelope env = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getEnvelopeNoCopy();
return new SpatialKey(row.getKey(),
(float) env.getMinX(), (float) env.getMaxX(),
(float) env.getMinY(), (float) env.getMaxY());
......
......@@ -17,6 +17,7 @@ import org.h2.index.IndexType;
import org.h2.index.SpatialIndex;
import org.h2.index.SpatialTreeIndex;
import org.h2.message.DbException;
import org.h2.mvstore.Page;
import org.h2.mvstore.rtree.MVRTreeMap;
import org.h2.mvstore.rtree.MVRTreeMap.RTreeCursor;
import org.h2.mvstore.rtree.SpatialKey;
......@@ -33,7 +34,7 @@ import org.h2.value.ValueGeometry;
import org.h2.value.ValueLong;
import org.h2.value.ValueNull;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
/**
* This is an index based on a MVRTreeMap.
......@@ -218,13 +219,63 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
return new MVStoreCursor(session, it, mvTable);
}
/**
* Returns the minimum bounding box that encloses all keys.
*
* @param session the session
* @return the minimum bounding box that encloses all keys, or null
*/
public Value getBounds(Session session) {
FindBoundsCursor cursor = new FindBoundsCursor(spatialMap.getRootPage(), new SpatialKey(0), session,
getMap(session), columnIds[0]);
while (cursor.hasNext()) {
cursor.next();
}
return cursor.getBounds();
}
/**
* Returns the estimated minimum bounding box that encloses all keys.
*
* The returned value may be incorrect.
*
* @param session the session
* @return the estimated minimum bounding box that encloses all keys, or null
*/
public Value getEstimatedBounds(Session session) {
Page p = spatialMap.getRootPage();
int count = p.getKeyCount();
if (count > 0) {
SpatialKey key = (SpatialKey) p.getKey(0);
float bminxf = key.min(0), bmaxxf = key.max(0), bminyf = key.min(1), bmaxyf = key.max(1);
for (int i = 1; i < count; i++) {
key = (SpatialKey) p.getKey(i);
float minxf = key.min(0), maxxf = key.max(0), minyf = key.min(1), maxyf = key.max(1);
if (minxf < bminxf) {
bminxf = minxf;
}
if (maxxf > bmaxxf) {
bmaxxf = maxxf;
}
if (minyf < bminyf) {
bminyf = minyf;
}
if (maxyf > bmaxyf) {
bmaxyf = maxyf;
}
}
return ValueGeometry.getFromGeometry(new GeometryFactory().toGeometry(
new Envelope(bminxf, bmaxxf, bminyf, bmaxyf)));
}
return ValueNull.INSTANCE;
}
private SpatialKey getKey(SearchRow row) {
Value v = row.getValue(columnIds[0]);
if (v == ValueNull.INSTANCE) {
return new SpatialKey(row.getKey());
}
Geometry g = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getGeometryNoCopy();
Envelope env = g.getEnvelopeInternal();
Envelope env = ((ValueGeometry) v.convertTo(Value.GEOMETRY)).getEnvelopeNoCopy();
return new SpatialKey(row.getKey(),
(float) env.getMinX(), (float) env.getMaxX(),
(float) env.getMinY(), (float) env.getMaxY());
......@@ -323,7 +374,7 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
/**
* A cursor.
*/
private static class MVStoreCursor implements Cursor {
public static class MVStoreCursor implements Cursor {
private final Session session;
private final Iterator<SpatialKey> it;
......@@ -360,6 +411,15 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
return searchRow;
}
/**
* Returns the current key.
*
* @return the current key
*/
public SpatialKey getKey() {
return current;
}
@Override
public boolean next() {
current = it.hasNext() ? it.next() : null;
......@@ -374,5 +434,89 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
}
}
/**
* A cursor for getBounds() method.
*/
private final class FindBoundsCursor extends RTreeCursor {
private final Session session;
private final TransactionMap<SpatialKey, Value> map;
private final int columnId;
private boolean hasBounds;
private float bminxf, bmaxxf, bminyf, bmaxyf;
private double bminxd, bmaxxd, bminyd, bmaxyd;
FindBoundsCursor(Page root, SpatialKey filter, Session session, TransactionMap<SpatialKey, Value> map,
int columnId) {
super(root, filter);
this.session = session;
this.map = map;
this.columnId = columnId;
}
@Override
protected boolean check(boolean leaf, SpatialKey key, SpatialKey test) {
float minxf = key.min(0), maxxf = key.max(0), minyf = key.min(1), maxyf = key.max(1);
if (leaf) {
if (hasBounds) {
if ((minxf <= bminxf || maxxf >= bmaxxf || minyf <= bminyf || maxyf >= bmaxyf)
&& map.containsKey(key)) {
Envelope env = ((ValueGeometry) mvTable.getRow(session, key.getId()).getValue(columnId))
.getEnvelopeNoCopy();
double minxd = env.getMinX(), maxxd = env.getMaxX(), minyd = env.getMinY(),
maxyd = env.getMaxY();
if (minxd < bminxd) {
bminxf = minxf;
bminxd = minxd;
}
if (maxxd > bmaxxd) {
bmaxxf = maxxf;
bmaxxd = maxxd;
}
if (minyd < bminyd) {
bminyf = minyf;
bminyd = minyd;
}
if (maxyd > bmaxyd) {
bmaxyf = maxyf;
bmaxyd = maxyd;
}
}
} else if (map.containsKey(key)) {
hasBounds = true;
Envelope env = ((ValueGeometry) mvTable.getRow(session, key.getId()).getValue(columnId))
.getEnvelopeNoCopy();
bminxf = minxf;
bminxd = env.getMinX();
bmaxxf = maxxf;
bmaxxd = env.getMaxX();
bminyf = minyf;
bminyd = env.getMinY();
bmaxyf = maxyf;
bmaxyd = env.getMaxY();
}
} else if (hasBounds) {
if (minxf <= bminxf || maxxf >= bmaxxf || minyf <= bminyf || maxyf >= bmaxyf) {
return true;
}
} else {
return true;
}
return false;
}
Value getBounds() {
return hasBounds ? ValueGeometry.getFromGeometry(new GeometryFactory().toGeometry(
new Envelope(bminxd, bmaxxd, bminyd, bmaxyd))) : ValueNull.INSTANCE;
}
}
}
......@@ -52,6 +52,11 @@ public class ValueGeometry extends Value {
*/
private Geometry geometry;
/**
* The envelope of the value. Calculated only on request.
*/
private Envelope envelope;
/**
* Create a new geometry objects.
*
......@@ -205,6 +210,18 @@ public class ValueGeometry extends Value {
return 0;
}
/**
* Return an envelope of this geometry. Do not modify the returned value.
*
* @return envelope of this geometry
*/
public Envelope getEnvelopeNoCopy() {
if (envelope == null) {
envelope = getGeometryNoCopy().getEnvelopeInternal();
}
return envelope;
}
/**
* Test if this geometry envelope intersects with the other geometry
* envelope.
......@@ -213,9 +230,7 @@ public class ValueGeometry extends Value {
* @return true if the two overlap
*/
public boolean intersectsBoundingBox(ValueGeometry r) {
// the Geometry object caches the envelope
return getGeometryNoCopy().getEnvelopeInternal().intersects(
r.getGeometryNoCopy().getEnvelopeInternal());
return getEnvelopeNoCopy().intersects(r.getEnvelopeNoCopy());
}
/**
......@@ -226,8 +241,8 @@ public class ValueGeometry extends Value {
*/
public Value getEnvelopeUnion(ValueGeometry r) {
GeometryFactory gf = new GeometryFactory();
Envelope mergedEnvelope = new Envelope(getGeometryNoCopy().getEnvelopeInternal());
mergedEnvelope.expandToInclude(r.getGeometryNoCopy().getEnvelopeInternal());
Envelope mergedEnvelope = new Envelope(getEnvelopeNoCopy());
mergedEnvelope.expandToInclude(r.getEnvelopeNoCopy());
return get(gf.toGeometry(mergedEnvelope));
}
......
......@@ -136,7 +136,7 @@ public class TestScript extends TestDb {
for (String s : new String[] { "help" }) {
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", "envelope",
"group-concat", "max", "median", "min", "mode", "selectivity", "stddev-pop",
"stddev-samp", "sum", "var-pop", "var-samp", "array-agg" }) {
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 ENVELOPE(V) FROM TEST;
>> null
INSERT INTO TEST VALUES ('POINT(1 1)');
> update count: 1
SELECT ENVELOPE(V) FROM TEST;
>> POINT (1 1)
INSERT INTO TEST VALUES ('POINT(1 2)'), (NULL), ('POINT(3 1)');
> update count: 3
SELECT ENVELOPE(V), ENVELOPE(V) FILTER (WHERE V <> 'POINT(3 1)') FILTERED1,
ENVELOPE(V) FILTER (WHERE V <> 'POINT(1 2)') FILTERED2 FROM TEST;
> ENVELOPE(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
-- Without index
SELECT ENVELOPE(N) FROM (SELECT V AS N FROM TEST);
>> POLYGON ((1 1, 1 2, 3 2, 3 1, 1 1))
-- With index
SELECT ENVELOPE(V) FROM TEST;
>> POLYGON ((1 1, 1 2, 3 2, 3 1, 1 1))
-- Without index
SELECT ENVELOPE(V) FILTER (WHERE V <> 'POINT(3 1)') FILTERED FROM TEST;
>> LINESTRING (1 1, 1 2)
-- Without index
SELECT ENVELOPE(V) FROM TEST WHERE V <> 'POINT(3 1)';
>> LINESTRING (1 1, 1 2)
INSERT INTO TEST VALUES ('POINT(-1.0000000001 1)');
> update count: 1
-- Without index
SELECT ENVELOPE(N) FROM (SELECT V AS N FROM TEST);
>> POLYGON ((-1.0000000001 1, -1.0000000001 2, 3 2, 3 1, -1.0000000001 1))
-- With index
SELECT ENVELOPE(V) FROM TEST;
>> POLYGON ((-1.0000000001 1, -1.0000000001 2, 3 2, 3 1, -1.0000000001 1))
TRUNCATE TABLE TEST;
> ok
-- Without index
SELECT ENVELOPE(N) FROM (SELECT V AS N FROM TEST);
>> null
-- With index
SELECT ENVELOPE(V) FROM TEST;
>> null
SELECT ESTIMATED_ENVELOPE('TEST', 'V');
>> null
SELECT RAND(1000) * 0;
>> 0.0
INSERT INTO TEST SELECT CAST('POINT(' || CAST(RAND() * 100000 AS INT) || ' ' || CAST(RAND() * 100000 AS INT) || ')' AS GEOMETRY) FROM SYSTEM_RANGE(1, 1000);
> update count: 1000
-- Without index
SELECT ENVELOPE(N) FROM (SELECT V AS N FROM TEST);
>> POLYGON ((68 78, 68 99951, 99903 99951, 99903 78, 68 78))
-- With index
SELECT ENVELOPE(V) FROM TEST;
>> POLYGON ((68 78, 68 99951, 99903 99951, 99903 78, 68 78))
SELECT ESTIMATED_ENVELOPE('TEST', 'V');
#+mvStore#>> POLYGON ((68 78, 68 99951, 99903 99951, 99903 78, 68 78))
#-mvStore#>> null
TRUNCATE TABLE TEST;
> ok
SELECT RAND(1000) * 0;
>> 0.0
INSERT INTO TEST SELECT CAST('POINT(' || (CAST(RAND() * 100000 AS INT) * 0.000000001 + 1) || ' '
|| (CAST(RAND() * 100000 AS INT) * 0.000000001 + 1) || ')' AS GEOMETRY) FROM SYSTEM_RANGE(1, 1000);
> update count: 1000
-- Without index
SELECT ENVELOPE(N) FROM (SELECT V AS N FROM TEST);
>> POLYGON ((1.000000068 1.000000078, 1.000000068 1.000099951, 1.000099903 1.000099951, 1.000099903 1.000000078, 1.000000068 1.000000078))
-- With index
SELECT ENVELOPE(V) FROM TEST;
>> POLYGON ((1.000000068 1.000000078, 1.000000068 1.000099951, 1.000099903 1.000099951, 1.000099903 1.000000078, 1.000000068 1.000000078))
DROP TABLE TEST;
> ok
-- Test for index selection
CREATE TABLE TEST(G1 GEOMETRY, G2 GEOMETRY) AS (SELECT NULL, 'POINT (1 1)'::GEOMETRY);
> ok
CREATE SPATIAL INDEX G1IDX ON TEST(G1);
> ok
CREATE SPATIAL INDEX G2IDX ON TEST(G2);
> ok
SELECT ENVELOPE(G2) FROM TEST;
>> POINT (1 1)
DROP TABLE TEST;
> ok
......@@ -1435,7 +1435,8 @@ public class TestMVTableEngine extends TestDb {
reverse += testReverseDeletePerformance(true);
direct += testReverseDeletePerformance(false);
}
assertTrue("direct: " + direct + ", reverse: " + reverse, 3 * Math.abs(reverse - direct) < 2 * (reverse + direct));
assertTrue("direct: " + direct + ", reverse: " + reverse,
3 * Math.abs(reverse - direct) < 2 * (reverse + direct));
}
private long testReverseDeletePerformance(boolean reverse) throws Exception {
......
......@@ -789,4 +789,6 @@ inconsistencies discover eliminated violates tweaks postpone leftovers
tied ties
launched unavailable smallmoney erroneously multiplier newid pan streamline unmap preview unexpectedly presumably
converging smth rng curs casts unmapping unmapper
immediate hhmmss scheduled hhmm prematurely postponed arranges subexpression subexpressions
immediate hhmmss scheduled hhmm prematurely postponed arranges subexpression subexpressions encloses plane caution
minxf maxxf minyf maxyf bminxf bmaxxf bminyf bmaxyf
minxd maxxd minyd maxyd bminxd bmaxxd bminyd bmaxyd
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论