Unverified 提交 92d7f561 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov 提交者: GitHub

Merge pull request #1441 from katzyn/ExtTypeInfo

Add GEOMETRY type subtypes with type and SRID constraints
......@@ -3037,20 +3037,34 @@ ENUM('clubs', 'diamonds', 'hearts', 'spades')
"
"Data Types","GEOMETRY Type","
GEOMETRY
[({ sridInt | { {POINT|LINESTRING|POLYGON|MULTIPOINT|MULTILINESTRING
|MULTIPOLYGON|GEOMETRYCOLLECTION} [Z|M|ZM] [, sridInt]} })]
","
A spatial geometry type.
Mapped to ""org.locationtech.jts.geom.Geometry"" if JTS library is in classpath.
If additional constraints are not specified this type accepts all supported types of geometries.
A constraint with required geometry type and dimension system can be set by specifying name of the type and
dimension system. A whitespace between them is optional.
2D dimension system does not have a name and assumed if only a geometry type name is specified.
POINT means 2D point, POINT Z or POINTZ means 3D point.
A constraint with required spatial reference system identifier (SRID) can be set by specifying this identifier.
Both constraints for type and SRID can be specified together.
Mapped to ""org.locationtech.jts.geom.Geometry"" if JTS library is in classpath and to ""java.lang.String"" otherwise.
May be represented in textual format using the WKT (well-known text) or EWKT (extended well-known text) format.
Values are stored internally in EWKB (extended well-known binary) format.
Only a subset of EWKB and EWKT features is supported.
Supported objects are POINT, LINESTRING, POLYGON, MULTIPOINT, MULTILINESTRING, MULTIPOLYGON, and GEOMETRYCOLLECTION.
Supported dimension systems are XY (2D), XYZ, XYM, and XYZM.
Supported dimension systems are 2D (XY), Z (XYZ), M (XYM), and ZM (XYZM).
SRID (spatial reference system identifier) is supported.
Use a quoted string containing a WKT/EWKT formatted string or ""PreparedStatement.setObject()"" to store values,
and ""ResultSet.getObject(..)"" or ""ResultSet.getString(..)"" to retrieve the values.
","
GEOMETRY
GEOMETRY(POINT)
GEOMETRY(POINT Z)
GEOMETRY(POINT Z, 4326)
GEOMETRY(4326)
"
"Data Types","INTERVAL Type","
......
......@@ -21,6 +21,18 @@ Change Log
<h2>Next Version (unreleased)</h2>
<ul>
<li>PR #1441: Add GEOMETRY type subtypes with type and SRID constraints
</li>
<li>PR #1434: Add support for ENUM in CAST and other changes
</li>
<li>PR #1431: Fix some inconsistencies in documentation and improve mvn build
</li>
<li>PR #1428: Add support for M and ZM dimensions to GEOMETRY data type
</li>
<li>Issue #1405: Introduce LocalResult factory
</li>
<li>PR #1422: Add ENVELOPE aggregate function
</li>
<li>Issue #1421: Remove old-style outer join
</li>
<li>PR #1419: Assorted minor changes
......
......@@ -194,14 +194,17 @@ import org.h2.util.ParserUtil;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils;
import org.h2.util.Utils;
import org.h2.util.geometry.EWKTUtils;
import org.h2.value.CompareMode;
import org.h2.value.DataType;
import org.h2.value.ExtTypeInfo;
import org.h2.value.ExtTypeInfoEnum;
import org.h2.value.ExtTypeInfoGeometry;
import org.h2.value.Value;
import org.h2.value.ValueBoolean;
import org.h2.value.ValueBytes;
import org.h2.value.ValueDate;
import org.h2.value.ValueDecimal;
import org.h2.value.ValueEnum;
import org.h2.value.ValueInt;
import org.h2.value.ValueInterval;
import org.h2.value.ValueLong;
......@@ -4862,7 +4865,7 @@ public class Parser {
}
long precision = -1;
int displaySize = -1;
String[] enumerators = null;
ExtTypeInfo extTypeInfo = null;
int scale = -1;
String comment = null;
Column templateColumn = null;
......@@ -4879,7 +4882,7 @@ public class Parser {
precision = templateColumn.getPrecision();
displaySize = templateColumn.getDisplaySize();
scale = templateColumn.getScale();
enumerators = templateColumn.getEnumerators();
extTypeInfo = templateColumn.getExtTypeInfo();
} else {
Mode mode = database.getMode();
dataType = DataType.getTypeByName(original, mode);
......@@ -5007,25 +5010,49 @@ public class Parser {
original = original + '(' + p + ')';
}
} else if (dataType.type == Value.ENUM) {
if (readIf(OPEN_PAREN)) {
java.util.List<String> enumeratorList = new ArrayList<>();
original += '(';
String enumerator0 = readString();
enumeratorList.add(enumerator0);
original += "'" + enumerator0 + "'";
while (readIfMore(true)) {
original += ',';
String enumeratorN = readString();
original += "'" + enumeratorN + "'";
enumeratorList.add(enumeratorN);
}
original += ')';
enumerators = enumeratorList.toArray(new String[0]);
if (extTypeInfo == null) {
String[] enumerators = null;
if (readIf(OPEN_PAREN)) {
java.util.List<String> enumeratorList = new ArrayList<>();
String enumerator0 = readString();
enumeratorList.add(enumerator0);
while (readIfMore(true)) {
String enumeratorN = readString();
enumeratorList.add(enumeratorN);
}
enumerators = enumeratorList.toArray(new String[0]);
}
try {
extTypeInfo = new ExtTypeInfoEnum(enumerators);
} catch (DbException e) {
throw e.addSQL(original);
}
original += extTypeInfo.getCreateSQL();
}
try {
ValueEnum.check(enumerators);
} catch (DbException e) {
throw e.addSQL(original);
} else if (dataType.type == Value.GEOMETRY) {
if (extTypeInfo == null) {
if (readIf(OPEN_PAREN)) {
int type = 0;
if (currentTokenType == IDENTIFIER && !currentTokenQuoted) {
try {
type = EWKTUtils.parseGeometryType(currentToken);
read();
if (type / 1_000 == 0 && currentTokenType == IDENTIFIER && !currentTokenQuoted) {
type += EWKTUtils.parseDimensionSystem(currentToken) * 1_000;
read();
}
} catch (IllegalArgumentException ex) {
throw getSyntaxError();
}
}
Integer srid = null;
if (type == 0 || readIf(COMMA)) {
srid = readInt();
}
read(CLOSE_PAREN);
extTypeInfo = new ExtTypeInfoGeometry(type, srid);
original += extTypeInfo.getCreateSQL();
}
}
} else if (readIf(OPEN_PAREN)) {
// Support for MySQL: INT(11), MEDIUMINT(8) and so on.
......@@ -5049,7 +5076,7 @@ public class Parser {
}
Column column = new Column(columnName, type, precision, scale,
displaySize, enumerators);
displaySize, extTypeInfo);
if (templateColumn != null) {
column.setNullable(templateColumn.isNullable());
column.setDefaultExpression(session,
......
......@@ -23,6 +23,7 @@ import org.h2.table.Column;
import org.h2.table.Table;
import org.h2.util.ColumnNamer;
import org.h2.value.DataType;
import org.h2.value.ExtTypeInfo;
import org.h2.value.Value;
/**
......@@ -197,18 +198,20 @@ public class CreateTable extends CommandWithColumns {
if (scale > precision) {
precision = scale;
}
String[] enumerators = null;
if (dt.type == Value.ENUM) {
/**
* Only columns of tables may be enumerated.
*/
if(!(expr instanceof ExpressionColumn)) {
ExtTypeInfo extTypeInfo = null;
int t = dt.type;
if (DataType.isExtInfoType(t)) {
if (expr instanceof ExpressionColumn) {
extTypeInfo = ((ExpressionColumn) expr).getColumn().getExtTypeInfo();
} else if (t == Value.ENUM) {
/*
* Only columns of tables may be enumerated.
*/
throw DbException.get(ErrorCode.GENERAL_ERROR_1,
"Unable to resolve enumerators of expression");
}
enumerators = ((ExpressionColumn)expr).getColumn().getEnumerators();
}
Column col = new Column(name, type, precision, scale, displaySize, enumerators);
Column col = new Column(name, type, precision, scale, displaySize, extTypeInfo);
addColumn(col);
}
}
......
......@@ -210,7 +210,7 @@ public class Comparison extends Condition {
Column column = ((ExpressionColumn) left).getColumn();
right = ValueExpression.get(r.convertTo(resType,
MathUtils.convertLongToInt(left.getPrecision()),
session.getDatabase().getMode(), column, column.getEnumerators()));
session.getDatabase().getMode(), column, column.getExtTypeInfo()));
}
} else if (right instanceof Parameter) {
((Parameter) right).setColumn(
......
......@@ -16,6 +16,7 @@ import org.h2.message.DbException;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.util.StatementBuilder;
import org.h2.value.ExtTypeInfo;
import org.h2.value.Value;
import org.h2.value.ValueBoolean;
import org.h2.value.ValueNull;
......@@ -34,7 +35,7 @@ public class ConditionInConstantSet extends Condition {
private final ArrayList<Expression> valueList;
private final TreeSet<Value> valueSet;
private final int type;
private String[] enumerators;
private ExtTypeInfo extTypeInfo;
/**
* Create a new IN(..) condition.
......@@ -54,9 +55,9 @@ public class ConditionInConstantSet extends Condition {
type = left.getType();
Mode mode = database.getMode();
if (type == Value.ENUM) {
enumerators = ((ExpressionColumn) left).getColumn().getEnumerators();
extTypeInfo = ((ExpressionColumn) left).getColumn().getExtTypeInfo();
for (Expression expression : valueList) {
valueSet.add(expression.getValue(session).convertToEnum(enumerators));
valueSet.add(extTypeInfo.cast(expression.getValue(session)));
}
} else {
for (Expression expression : valueList) {
......@@ -170,7 +171,7 @@ public class ConditionInConstantSet extends Condition {
if (add.isConstant()) {
valueList.add(add);
if (type == Value.ENUM) {
valueSet.add(add.getValue(session).convertToEnum(enumerators));
valueSet.add(add.getValue(session).convertToEnum(extTypeInfo));
} else {
valueSet.add(add.getValue(session).convertTo(type, session.getDatabase().getMode()));
}
......
......@@ -19,9 +19,9 @@ import org.h2.table.Column;
import org.h2.table.ColumnResolver;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.value.ExtTypeInfo;
import org.h2.value.Value;
import org.h2.value.ValueBoolean;
import org.h2.value.ValueEnum;
import org.h2.value.ValueNull;
/**
......@@ -191,8 +191,11 @@ public class ExpressionColumn extends Expression {
throw DbException.get(ErrorCode.MUST_GROUP_BY_COLUMN_1, getSQL());
}
}
if (column.getEnumerators() != null && value != ValueNull.INSTANCE) {
return ValueEnum.get(column.getEnumerators(), value.getInt());
if (value != ValueNull.INSTANCE) {
ExtTypeInfo extTypeInfo = column.getExtTypeInfo();
if (extTypeInfo != null) {
return extTypeInfo.cast(value);
}
}
return value;
}
......
......@@ -56,6 +56,7 @@ import org.h2.util.ToChar;
import org.h2.util.ToDateParser;
import org.h2.util.Utils;
import org.h2.value.DataType;
import org.h2.value.ExtTypeInfo;
import org.h2.value.Value;
import org.h2.value.ValueArray;
import org.h2.value.ValueBoolean;
......@@ -162,7 +163,7 @@ public class Function extends Expression implements FunctionCall {
protected int scale;
protected long precision = PRECISION_UNKNOWN;
protected int displaySize;
protected String[] enumerators;
protected ExtTypeInfo extTypeInfo;
private final Database database;
......@@ -894,7 +895,7 @@ public class Function extends Expression implements FunctionCall {
case CAST:
case CONVERT: {
Mode mode = database.getMode();
v0 = v0.convertTo(dataType, MathUtils.convertLongToInt(precision), mode, null, enumerators);
v0 = v0.convertTo(dataType, MathUtils.convertLongToInt(precision), mode, null, extTypeInfo);
v0 = v0.convertScale(mode.convertOnlyToSmallerScale, scale);
v0 = v0.convertPrecision(getPrecision(), false);
result = v0;
......@@ -2200,7 +2201,7 @@ public class Function extends Expression implements FunctionCall {
precision = col.getPrecision();
displaySize = col.getDisplaySize();
scale = col.getScale();
enumerators = col.getEnumerators();
extTypeInfo = col.getExtTypeInfo();
}
@Override
......@@ -2601,7 +2602,7 @@ public class Function extends Expression implements FunctionCall {
case CAST: {
buff.append(args[0].getSQL()).append(" AS ").
append(new Column(null, dataType, precision,
scale, displaySize, enumerators).getCreateSQL());
scale, displaySize, extTypeInfo).getCreateSQL());
break;
}
case CONVERT: {
......
......@@ -288,7 +288,7 @@ public final class MVSecondaryIndex extends BaseIndex implements MVIndex {
int idx = c.getColumnId();
Value v = r.getValue(idx);
if (v != null) {
array[i] = v.convertTo(c.getType(), -1, database.getMode(), null, c.getEnumerators());
array[i] = v.convertTo(c.getType(), -1, database.getMode(), null, c.getExtTypeInfo());
}
}
array[keyColumns - 1] = key != null ? key : ValueLong.get(r.getKey());
......
......@@ -6,7 +6,8 @@
package org.h2.table;
import java.sql.ResultSetMetaData;
import java.util.Arrays;
import java.util.Objects;
import org.h2.api.ErrorCode;
import org.h2.command.Parser;
import org.h2.engine.Constants;
......@@ -25,8 +26,8 @@ import org.h2.schema.Sequence;
import org.h2.util.MathUtils;
import org.h2.util.StringUtils;
import org.h2.value.DataType;
import org.h2.value.ExtTypeInfo;
import org.h2.value.Value;
import org.h2.value.ValueEnum;
import org.h2.value.ValueInt;
import org.h2.value.ValueLong;
import org.h2.value.ValueNull;
......@@ -65,7 +66,7 @@ public class Column {
private final int type;
private long precision;
private int scale;
private String[] enumerators;
private ExtTypeInfo extTypeInfo;
private int displaySize;
private Table table;
private String name;
......@@ -99,8 +100,7 @@ public class Column {
this(name, type, precision, scale, displaySize, null);
}
public Column(String name, int type, long precision, int scale,
int displaySize, String[] enumerators) {
public Column(String name, int type, long precision, int scale, int displaySize, ExtTypeInfo extTypeInfo) {
this.name = name;
this.type = type;
if (precision == -1 && scale == -1 && displaySize == -1 && type != Value.UNKNOWN) {
......@@ -112,7 +112,7 @@ public class Column {
this.precision = precision;
this.scale = scale;
this.displaySize = displaySize;
this.enumerators = enumerators;
this.extTypeInfo = extTypeInfo;
}
@Override
......@@ -141,12 +141,8 @@ public class Column {
return table.getId() ^ name.hashCode();
}
public boolean isEnumerated() {
return type == Value.ENUM;
}
public Column getClone() {
Column newColumn = new Column(name, type, precision, scale, displaySize, enumerators);
Column newColumn = new Column(name, type, precision, scale, displaySize, extTypeInfo);
newColumn.copy(this);
return newColumn;
}
......@@ -172,7 +168,7 @@ public class Column {
*/
public Value convert(Value v, Mode mode) {
try {
return v.convertTo(type, MathUtils.convertLongToInt(precision), mode, this, getEnumerators());
return v.convertTo(type, MathUtils.convertLongToInt(precision), mode, this, extTypeInfo);
} catch (DbException e) {
if (e.getErrorCode() == ErrorCode.DATA_CONVERSION_ERROR_1) {
String target = (table == null ? "" : table.getName() + ": ") +
......@@ -300,12 +296,12 @@ public class Column {
nullable = b;
}
public String[] getEnumerators() {
return enumerators;
public ExtTypeInfo getExtTypeInfo() {
return extTypeInfo;
}
public void setEnumerators(String[] enumerators) {
this.enumerators = enumerators;
public void setExtTypeInfo(ExtTypeInfo extTypeInfo) {
this.extTypeInfo = extTypeInfo;
}
public boolean getVisible() {
......@@ -406,17 +402,8 @@ public class Column {
getCreateSQL(), s + " (" + value.getPrecision() + ")");
}
}
if (isEnumerated() && value != ValueNull.INSTANCE) {
if (!ValueEnum.isValid(enumerators, value)) {
String s = value.getTraceSQL();
if (s.length() > 127) {
s = s.substring(0, 128) + "...";
}
throw DbException.get(ErrorCode.ENUM_VALUE_NOT_PERMITTED,
getCreateSQL(), s);
}
value = ValueEnum.get(enumerators, value.getInt());
if (value != ValueNull.INSTANCE && DataType.isExtInfoType(type) && extTypeInfo != null) {
value = extTypeInfo.cast(value);
}
updateSequenceIfRequired(session, value);
return value;
......@@ -522,15 +509,13 @@ public class Column {
case Value.DECIMAL:
buff.append('(').append(precision).append(", ").append(scale).append(')');
break;
case Value.ENUM:
buff.append('(');
for (int i = 0; i < enumerators.length; i++) {
buff.append('\'').append(enumerators[i]).append('\'');
if(i < enumerators.length - 1) {
buff.append(',');
}
case Value.GEOMETRY:
if (extTypeInfo == null) {
break;
}
buff.append(')');
//$FALL-THROUGH$
case Value.ENUM:
buff.append(extTypeInfo.getCreateSQL());
break;
case Value.BYTES:
case Value.STRING:
......@@ -862,6 +847,9 @@ public class Column {
if (onUpdateExpression != null || newColumn.onUpdateExpression != null) {
return false;
}
if (!Objects.equals(extTypeInfo, newColumn.extTypeInfo)) {
return false;
}
return true;
}
......@@ -876,8 +864,7 @@ public class Column {
displaySize = source.displaySize;
name = source.name;
precision = source.precision;
enumerators = source.enumerators == null ? null :
Arrays.copyOf(source.enumerators, source.enumerators.length);
extTypeInfo = source.extTypeInfo;
scale = source.scale;
// table is not set
// columnId is not set
......
......@@ -38,6 +38,8 @@ import org.h2.util.ColumnNamer;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils;
import org.h2.util.Utils;
import org.h2.value.DataType;
import org.h2.value.ExtTypeInfo;
import org.h2.value.Value;
/**
......@@ -194,13 +196,13 @@ public class TableView extends Table {
long precision = expr.getPrecision();
int scale = expr.getScale();
int displaySize = expr.getDisplaySize();
String[] enumerators = null;
if (type == Value.ENUM) {
ExtTypeInfo extTypeInfo = null;
if (DataType.isExtInfoType(type)) {
if (expr instanceof ExpressionColumn) {
enumerators = ((ExpressionColumn) expr).getColumn().getEnumerators();
extTypeInfo = ((ExpressionColumn) expr).getColumn().getExtTypeInfo();
}
}
Column col = new Column(name, type, precision, scale, displaySize, enumerators);
Column col = new Column(name, type, precision, scale, displaySize, extTypeInfo);
col.setTable(this, i);
// Fetch check constraint from view column source
ExpressionColumn fromColumn = null;
......
......@@ -233,17 +233,17 @@ public final class EWKBUtils {
/**
* Geometry type mask that indicates presence of dimension Z.
*/
private static final int EWKB_Z = 0x8000_0000;
public static final int EWKB_Z = 0x8000_0000;
/**
* Geometry type mask that indicates presence of dimension M.
*/
private static final int EWKB_M = 0x4000_0000;
public static final int EWKB_M = 0x4000_0000;
/**
* Geometry type mask that indicates presence of SRID.
*/
private static final int EWKB_SRID = 0x2000_0000;
public static final int EWKB_SRID = 0x2000_0000;
/**
* Converts any supported EWKB to EWKB representation that is used by this
......@@ -296,6 +296,32 @@ public final class EWKBUtils {
}
}
/**
* Converts geometry type with flags to a dimension system.
*
* @param type
* geometry type with flags
* @return dimension system
*/
public static int type2dimensionSystem(int type) {
// PostGIS extensions
boolean useZ = (type & EWKB_Z) != 0;
boolean useM = (type & EWKB_M) != 0;
// OGC 06-103r4
type &= 0xffff;
switch (type / 1_000) {
case DIMENSION_SYSTEM_XYZ:
useZ = true;
break;
case DIMENSION_SYSTEM_XYZM:
useZ = true;
//$FALL-THROUGH$
case DIMENSION_SYSTEM_XYM:
useM = true;
}
return (useZ ? DIMENSION_SYSTEM_XYZ : 0) | (useM ? DIMENSION_SYSTEM_XYM : 0);
}
/**
* Parses a EWKB.
*
......@@ -340,6 +366,7 @@ public final class EWKBUtils {
case DIMENSION_SYSTEM_XYM:
useM = true;
}
target.dimensionSystem((useZ ? DIMENSION_SYSTEM_XYZ : 0) | (useM ? DIMENSION_SYSTEM_XYM : 0));
type %= 1_000;
switch (type) {
case POINT:
......
......@@ -41,6 +41,30 @@ import org.h2.util.geometry.GeometryUtils.Target;
*/
public final class EWKTUtils {
/**
* 0-based type names of geometries, subtract 1 from type code to get index
* in this array.
*/
private static final String[] TYPES = { //
"POINT", //
"LINESTRING", //
"POLYGON", //
"MULTIPOINT", //
"MULTILINESTRING", //
"MULTIPOLYGON", //
"GEOMETRYCOLLECTION", //
};
/**
* Names of dimension systems.
*/
private static final String[] DIMENSION_SYSTEMS = { //
"XY", //
"Z", //
"M", //
"ZM", //
};
/**
* Converter output target that writes a EWKT.
*/
......@@ -249,10 +273,19 @@ public final class EWKTUtils {
}
int readSRID() {
skipWS();
int srid;
if (ewkt.startsWith("SRID=")) {
if (ewkt.regionMatches(true, offset, "SRID=", 0, 5)) {
offset += 5;
int idx = ewkt.indexOf(';', 5);
srid = Integer.parseInt(ewkt.substring(5, idx));
if (idx < 0) {
throw new IllegalArgumentException();
}
int end = idx;
while (ewkt.charAt(end - 1) <= ' ') {
end--;
}
srid = Integer.parseInt(ewkt.substring(offset, end).trim());
offset = idx + 1;
} else {
srid = 0;
......@@ -319,7 +352,7 @@ public final class EWKTUtils {
int o = offset;
skipWS();
int len = ewkt.length();
if (offset > len - 2) {
if (offset >= len) {
throw new IllegalArgumentException();
}
int result;
......@@ -333,12 +366,16 @@ public final class EWKTUtils {
case 'Z':
case 'z':
offset++;
ch = ewkt.charAt(offset);
if (ch == 'M' || ch == 'm') {
offset++;
result = DIMENSION_SYSTEM_XYZM;
} else {
if (offset >= len) {
result = DIMENSION_SYSTEM_XYZ;
} else {
ch = ewkt.charAt(offset);
if (ch == 'M' || ch == 'm') {
offset++;
result = DIMENSION_SYSTEM_XYZM;
} else {
result = DIMENSION_SYSTEM_XYZ;
}
}
break;
default:
......@@ -580,6 +617,71 @@ public final class EWKTUtils {
parseEWKT(new EWKTSource(ewkt), target, 0, 0);
}
/**
* Parses geometry type and dimension system from the given string.
*
* @param s
* string to parse
* @return geometry type and dimension system in OGC geometry code format
* (type + dimensionSystem * 1000)
* @throws IllegalArgumentException
* if input is not valid
*/
public static int parseGeometryType(String s) {
EWKTSource source = new EWKTSource(s);
int type = source.readType();
int dimensionSystem = 0;
if (source.hasData()) {
dimensionSystem = source.readDimensionSystem();
if (source.hasData()) {
throw new IllegalArgumentException();
}
}
return dimensionSystem * 1_000 + type;
}
/**
* Parses a dimension system from the given string.
*
* @param s
* string to parse
* @return dimension system, one of XYZ, XYM, or XYZM
* @see GeometryUtils#DIMENSION_SYSTEM_XYZ
* @see GeometryUtils#DIMENSION_SYSTEM_XYM
* @see GeometryUtils#DIMENSION_SYSTEM_XYZM
* @throws IllegalArgumentException
* if input is not valid
*/
public static int parseDimensionSystem(String s) {
EWKTSource source = new EWKTSource(s);
int dimensionSystem = source.readDimensionSystem();
if (source.hasData() || dimensionSystem == DIMENSION_SYSTEM_XY) {
throw new IllegalArgumentException();
}
return dimensionSystem;
}
/**
* Formats type and dimension system as a string.
*
* @param type
* OGC geometry code format (type + dimensionSystem * 1000)
* @return formatted string
* @throws IllegalArgumentException
* if type is not valid
*/
public static String formatGeometryTypeAndDimensionSystem(int type) {
int t = type % 1_000, d = type / 1_000;
if (t < POINT || t > GEOMETRY_COLLECTION || d < DIMENSION_SYSTEM_XY || d > DIMENSION_SYSTEM_XYZM) {
throw new IllegalArgumentException();
}
String result = TYPES[t - 1];
if (d != DIMENSION_SYSTEM_XY) {
result = result + ' ' + DIMENSION_SYSTEMS[d];
}
return result;
}
/**
* Parses a EWKB.
*
......@@ -613,6 +715,7 @@ public final class EWKTUtils {
type = POLYGON;
break;
}
target.dimensionSystem(dimensionSystem);
switch (type) {
case POINT: {
if (parentType != 0 && parentType != MULTI_POINT && parentType != GEOMETRY_COLLECTION) {
......
......@@ -27,6 +27,15 @@ public final class GeometryUtils {
protected void init(int srid) {
}
/**
* Invoked to add dimension system requirement.
*
* @param dimensionSystem
* dimension system
*/
protected void dimensionSystem(int dimensionSystem) {
}
/**
* Invoked before writing a POINT.
*/
......@@ -238,6 +247,16 @@ public final class GeometryUtils {
public DimensionSystemTarget() {
}
@Override
protected void dimensionSystem(int dimensionSystem) {
if ((dimensionSystem & DIMENSION_SYSTEM_XYZ) != 0) {
hasZ = true;
}
if ((dimensionSystem & DIMENSION_SYSTEM_XYM) != 0) {
hasM = true;
}
}
@Override
protected void addCoordinate(double x, double y, double z, double m, int index, int total) {
if (!hasZ && !Double.isNaN(z)) {
......@@ -288,6 +307,16 @@ public final class GeometryUtils {
public EnvelopeAndDimensionSystemTarget() {
}
@Override
protected void dimensionSystem(int dimensionSystem) {
if ((dimensionSystem & DIMENSION_SYSTEM_XYZ) != 0) {
hasZ = true;
}
if ((dimensionSystem & DIMENSION_SYSTEM_XYM) != 0) {
hasM = true;
}
}
@Override
protected void startPoint() {
enabled = true;
......@@ -362,37 +391,37 @@ public final class GeometryUtils {
/**
* POINT geometry type.
*/
static final int POINT = 1;
public static final int POINT = 1;
/**
* LINESTRING geometry type.
*/
static final int LINE_STRING = 2;
public static final int LINE_STRING = 2;
/**
* POLYGON geometry type.
*/
static final int POLYGON = 3;
public static final int POLYGON = 3;
/**
* MULTIPOINT geometry type.
*/
static final int MULTI_POINT = 4;
public static final int MULTI_POINT = 4;
/**
* MULTILINESTRING geometry type.
*/
static final int MULTI_LINE_STRING = 5;
public static final int MULTI_LINE_STRING = 5;
/**
* MULTIPOLYGON geometry type.
*/
static final int MULTI_POLYGON = 6;
public static final int MULTI_POLYGON = 6;
/**
* GEOMETRYCOLLECTION geometry type.
*/
static final int GEOMETRY_COLLECTION = 7;
public static final int GEOMETRY_COLLECTION = 7;
/**
* Number of X coordinate.
......
......@@ -358,7 +358,7 @@ public class DataType {
104
);
add(Value.GEOMETRY, Types.OTHER,
createString(false),
createGeometry(),
new String[]{"GEOMETRY"},
32
);
......@@ -548,6 +548,17 @@ public class DataType {
return t;
}
private static DataType createGeometry() {
DataType dataType = new DataType();
dataType.prefix = "'";
dataType.suffix = "'";
dataType.params = "TYPE,SRID";
dataType.maxPrecision = Integer.MAX_VALUE;
dataType.defaultPrecision = Integer.MAX_VALUE;
dataType.defaultDisplaySize = Integer.MAX_VALUE;
return dataType;
}
/**
* Get the list of data types.
*
......@@ -1403,6 +1414,16 @@ public class DataType {
return type == Value.STRING || type == Value.STRING_FIXED || type == Value.STRING_IGNORECASE;
}
/**
* Check if the given type may have extended type information.
*
* @param type the value type
* @return true if the value type may have extended type information
*/
public static boolean isExtInfoType(int type) {
return type == Value.GEOMETRY || type == Value.ENUM;
}
/**
* Check if the given value type supports the add operation.
*
......
/*
* 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.value;
/**
* Extended parameters of a data type.
*/
public abstract class ExtTypeInfo {
/**
* Casts a specified value to this data type.
*
* @param value
* value to cast
* @return casted value
*/
public abstract Value cast(Value value);
/**
* Returns SQL including parentheses that should be appended to a type name.
*
* @return SQL including parentheses that should be appended to a type name
*/
public abstract String getCreateSQL();
@Override
public String toString() {
return getCreateSQL();
}
}
/*
* 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.value;
import java.util.Arrays;
import java.util.Locale;
import org.h2.api.ErrorCode;
import org.h2.message.DbException;
/**
* Extended parameters of the ENUM data type.
*/
public final class ExtTypeInfoEnum extends ExtTypeInfo {
private final String[] enumerators, cleaned;
/**
* Returns enumerators for the two specified values for a binary operation.
*
* @param left
* left (first) operand
* @param right
* right (second) operand
* @return enumerators from the left or the right value, or an empty array
* if both values do not have enumerators
*/
public static ExtTypeInfoEnum getEnumeratorsForBinaryOperation(Value left, Value right) {
if (left.getType() == Value.ENUM) {
return ((ValueEnum) left).getEnumerators();
} else if (right.getType() == Value.ENUM) {
return ((ValueEnum) right).getEnumerators();
} else {
throw DbException.get(ErrorCode.UNKNOWN_DATA_TYPE_1,
"type1=" + left.getType() + ", type2=" + right.getType());
}
}
private static String sanitize(String label) {
return label == null ? null : label.trim().toUpperCase(Locale.ENGLISH);
}
private static String toSQL(String[] enumerators) {
StringBuilder result = new StringBuilder();
result.append('(');
for (int i = 0; i < enumerators.length; i++) {
if (i != 0) {
result.append(", ");
}
result.append('\'');
String s = enumerators[i];
for (int j = 0; j < s.length(); j++) {
char c = s.charAt(j);
if (c == '\'') {
result.append('\'');
}
result.append(c);
}
result.append('\'');
}
result.append(')');
return result.toString();
}
/**
* Creates new instance of extended parameters of the ENUM data type.
*
* @param enumerators
* the enumerators. May not be modified by caller or this class.
*/
public ExtTypeInfoEnum(String[] enumerators) {
if (enumerators == null || enumerators.length == 0) {
throw DbException.get(ErrorCode.ENUM_EMPTY);
}
final String[] cleaned = new String[enumerators.length];
for (int i = 0; i < enumerators.length; i++) {
String l = sanitize(enumerators[i]);
if (l == null || l.isEmpty()) {
throw DbException.get(ErrorCode.ENUM_EMPTY);
}
for (int j = 0; j < i; j++) {
if (l.equals(cleaned[j])) {
throw DbException.get(ErrorCode.ENUM_DUPLICATE, toSQL(enumerators));
}
}
cleaned[i] = l;
}
this.enumerators = enumerators;
this.cleaned = Arrays.equals(cleaned, enumerators) ? enumerators : cleaned;
}
@Override
public Value cast(Value value) {
switch (value.getType()) {
case Value.ENUM:
if (value instanceof ValueEnum && ((ValueEnum) value).getEnumerators().equals(this)) {
return value;
}
//$FALL-THROUGH$
case Value.STRING:
case Value.STRING_FIXED:
case Value.STRING_IGNORECASE:
ValueEnum v = getValueOrNull(value.getString());
if (v != null) {
return v;
}
break;
default:
int ordinal = value.getInt();
if (ordinal >= 0 && ordinal < enumerators.length) {
return new ValueEnum(this, enumerators[ordinal], ordinal);
}
}
String s = value.getTraceSQL();
if (s.length() > 127) {
s = s.substring(0, 128) + "...";
}
throw DbException.get(ErrorCode.ENUM_VALUE_NOT_PERMITTED, toString(), s);
}
/**
* Returns an enumerator with specified 0-based ordinal value.
*
* @param ordinal
* ordinal value of an enumerator
* @return the enumerator with specified ordinal value
*/
public String getEnumerator(int ordinal) {
return enumerators[ordinal];
}
public ValueEnum getValue(int ordinal) {
if (ordinal < 0 || ordinal >= enumerators.length) {
throw DbException.get(ErrorCode.ENUM_VALUE_NOT_PERMITTED, enumerators.toString(),
Integer.toString(ordinal));
}
return new ValueEnum(this, enumerators[ordinal], ordinal);
}
public ValueEnum getValue(String label) {
ValueEnum value = getValueOrNull(label);
if (value == null) {
throw DbException.get(ErrorCode.ENUM_VALUE_NOT_PERMITTED, toString(), label);
}
return value;
}
private ValueEnum getValueOrNull(String label) {
String l = sanitize(label);
if (l != null) {
for (int ordinal = 0; ordinal < cleaned.length; ordinal++) {
if (l.equals(cleaned[ordinal])) {
return new ValueEnum(this, enumerators[ordinal], ordinal);
}
}
}
return null;
}
@Override
public int hashCode() {
return Arrays.hashCode(enumerators) + 203_117;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || obj.getClass() != ExtTypeInfoEnum.class) {
return false;
}
return Arrays.equals(enumerators, ((ExtTypeInfoEnum) obj).enumerators);
}
@Override
public String getCreateSQL() {
return toSQL(enumerators);
}
}
/*
* 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.value;
import org.h2.api.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.geometry.EWKTUtils;
/**
* Extended parameters of the GEOMETRY data type.
*/
public final class ExtTypeInfoGeometry extends ExtTypeInfo {
private final int type;
private final Integer srid;
private static String toSQL(int type, Integer srid) {
if (type == 0 && srid == null) {
return "";
}
StringBuilder builder = new StringBuilder();
builder.append('(');
if (type != 0) {
builder.append(EWKTUtils.formatGeometryTypeAndDimensionSystem(type));
}
if (srid != null) {
if (type != 0) {
builder.append(", ");
}
builder.append((int) srid);
}
return builder.append(')').toString();
}
/**
* Creates new instance of extended parameters of the GEOMETRY data type.
*
* @param type
* the type and dimension system of geometries, or 0 if not
* constrained
* @param srid
* the SRID of geometries, or {@code null} if not constrained
*/
public ExtTypeInfoGeometry(int type, Integer srid) {
this.type = type;
this.srid = srid;
}
@Override
public Value cast(Value value) {
if (value.getType() != Value.GEOMETRY) {
value = value.convertTo(Value.GEOMETRY);
}
ValueGeometry g = (ValueGeometry) value;
if (type != 0 && g.getTypeAndDimensionSystem() != type || srid != null && g.getSRID() != srid) {
throw DbException.get(ErrorCode.CHECK_CONSTRAINT_VIOLATED_1,
toSQL(g.getTypeAndDimensionSystem(), g.getSRID()) + " <> " + toString());
}
return g;
}
@Override
public String getCreateSQL() {
return toSQL(type, srid);
}
}
......@@ -677,10 +677,10 @@ public abstract class Value {
/**
* Convert value to ENUM value
* @param enumerators allowed values for the ENUM to which the value is converted
* @param enumerators the extended type information for the ENUM data type
* @return value represented as ENUM
*/
public final Value convertToEnum(String[] enumerators) {
public final Value convertToEnum(ExtTypeInfo enumerators) {
// Use -1 to indicate "default behaviour" where value conversion should not
// depend on any datatype precision.
return convertTo(ENUM, -1, null, null, enumerators);
......@@ -706,11 +706,10 @@ public abstract class Value {
* the precision plays no role when converting the value
* @param mode the conversion mode
* @param column the column (if any), used for to improve the error message if conversion fails
* @param enumerators the ENUM datatype enumerators (if any),
* for dealing with ENUM conversions
* @param extTypeInfo the extended data type information, or null
* @return the converted value
*/
public Value convertTo(int targetType, int precision, Mode mode, Object column, String[] enumerators) {
public Value convertTo(int targetType, int precision, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
// converting NULL is done in ValueNull
// converting BLOB to CLOB and vice versa is done in ValueLob
if (getType() == targetType) {
......@@ -755,7 +754,7 @@ public abstract class Value {
case JAVA_OBJECT:
return convertToJavaObject();
case ENUM:
return convertToEnumInternal(enumerators);
return convertToEnumInternal((ExtTypeInfoEnum) extTypeInfo);
case BLOB:
return convertToBlob();
case CLOB:
......@@ -763,7 +762,7 @@ public abstract class Value {
case UUID:
return convertToUuid();
case GEOMETRY:
return convertToGeometry();
return convertToGeometry((ExtTypeInfoGeometry) extTypeInfo);
case Value.INTERVAL_YEAR:
case Value.INTERVAL_MONTH:
case Value.INTERVAL_YEAR_TO_MONTH:
......@@ -1158,24 +1157,24 @@ public abstract class Value {
return ValueJavaObject.getNoCopy(null, StringUtils.convertHexToBytes(getString().trim()), getDataHandler());
}
private ValueEnum convertToEnumInternal(String[] enumerators) {
private ValueEnum convertToEnumInternal(ExtTypeInfoEnum extTypeInfo) {
switch (getType()) {
case BYTE:
case SHORT:
case INT:
case LONG:
case DECIMAL:
return ValueEnum.get(enumerators, getInt());
return extTypeInfo.getValue(getInt());
case STRING:
case STRING_IGNORECASE:
case STRING_FIXED:
return ValueEnum.get(enumerators, getString());
return extTypeInfo.getValue(getString());
case JAVA_OBJECT:
Object object = JdbcUtils.deserialize(getBytesNoCopy(), getDataHandler());
if (object instanceof String) {
return ValueEnum.get(enumerators, (String) object);
return extTypeInfo.getValue((String) object);
} else if (object instanceof Integer) {
return ValueEnum.get(enumerators, (int) object);
return extTypeInfo.getValue((int) object);
}
//$FALL-THROUGH$
}
......@@ -1212,20 +1211,25 @@ public abstract class Value {
return ValueUuid.get(getString());
}
private ValueGeometry convertToGeometry() {
private Value convertToGeometry(ExtTypeInfoGeometry extTypeInfo) {
ValueGeometry result;
switch (getType()) {
case BYTES:
return ValueGeometry.getFromEWKB(getBytesNoCopy());
result = ValueGeometry.getFromEWKB(getBytesNoCopy());
break;
case JAVA_OBJECT:
Object object = JdbcUtils.deserialize(getBytesNoCopy(), getDataHandler());
if (DataType.isGeometry(object)) {
return ValueGeometry.getFromGeometry(object);
result = ValueGeometry.getFromGeometry(object);
break;
}
//$FALL-THROUGH$
case TIMESTAMP_TZ:
throw getDataConversionError(GEOMETRY);
default:
result = ValueGeometry.get(getString());
}
return ValueGeometry.get(getString());
return extTypeInfo != null ? extTypeInfo.cast(result) : result;
}
private ValueInterval convertToIntervalYearMonth(int targetType) {
......@@ -1337,7 +1341,7 @@ public abstract class Value {
if (leftType != rightType || leftType == Value.ENUM) {
int dataType = Value.getHigherOrder(leftType, rightType);
if (dataType == Value.ENUM) {
String[] enumerators = ValueEnum.getEnumeratorsForBinaryOperation(l, v);
ExtTypeInfoEnum enumerators = ExtTypeInfoEnum.getEnumeratorsForBinaryOperation(l, v);
l = l.convertToEnum(enumerators);
v = v.convertToEnum(enumerators);
} else {
......
......@@ -5,196 +5,20 @@
*/
package org.h2.value;
import java.util.Locale;
import org.h2.api.ErrorCode;
import org.h2.message.DbException;
/**
* ENUM value.
*/
public class ValueEnum extends ValueEnumBase {
private enum Validation {
DUPLICATE,
EMPTY,
INVALID,
VALID
}
private final String[] enumerators;
private final ExtTypeInfoEnum enumerators;
private ValueEnum(final String[] enumerators, final int ordinal) {
super(enumerators[ordinal], ordinal);
ValueEnum(ExtTypeInfoEnum enumerators, String label, int ordinal) {
super(label, ordinal);
this.enumerators = enumerators;
}
/**
* Check for any violations, such as empty
* values, duplicate values.
*
* @param enumerators the enumerators
*/
public static void check(final String[] enumerators) {
switch (validate(enumerators)) {
case VALID:
return;
case EMPTY:
throw DbException.get(ErrorCode.ENUM_EMPTY);
case DUPLICATE:
throw DbException.get(ErrorCode.ENUM_DUPLICATE,
toString(enumerators));
default:
throw DbException.get(ErrorCode.INVALID_VALUE_2,
toString(enumerators));
}
}
private static void check(final String[] enumerators, final Value value) {
check(enumerators);
if (validate(enumerators, value) != Validation.VALID) {
throw DbException.get(ErrorCode.ENUM_VALUE_NOT_PERMITTED,
toString(enumerators), value.toString());
}
}
/**
* Create an ENUM value from the provided enumerators
* and value.
*
* @param enumerators the enumerators
* @param value a value
* @return the ENUM value
*/
public static ValueEnum get(final String[] enumerators, int value) {
check(enumerators, ValueInt.get(value));
return new ValueEnum(enumerators, value);
}
public static ValueEnum get(final String[] enumerators, String value) {
check(enumerators, ValueString.get(value));
final String cleanLabel = sanitize(value);
for (int i = 0; i < enumerators.length; i++) {
if (cleanLabel.equals(sanitize(enumerators[i]))) {
return new ValueEnum(enumerators, i);
}
}
throw DbException.get(ErrorCode.GENERAL_ERROR_1, "Unexpected error");
}
/**
* Returns enumerators for the two specified values for a binary operation.
*
* @param left
* left (first) operand
* @param right
* right (second) operand
* @return enumerators from the left or the right value, or an empty array if
* both values do not have enumerators
*/
public static String[] getEnumeratorsForBinaryOperation(Value left, Value right) {
if (left.getType() == Value.ENUM) {
return ((ValueEnum) left).getEnumerators();
} else if (right.getType() == Value.ENUM) {
return ((ValueEnum) right).getEnumerators();
} else {
return new String[0];
}
}
public String[] getEnumerators() {
public ExtTypeInfoEnum getEnumerators() {
return enumerators;
}
/**
* Evaluates whether a valid ENUM can be constructed
* from the provided enumerators and value.
*
* @param enumerators the enumerators
* @param value the value
* @return whether a valid ENUM can be constructed from the provided values
*/
public static boolean isValid(final String enumerators[], final Value value) {
return validate(enumerators, value).equals(Validation.VALID);
}
private static String sanitize(final String label) {
return label == null ? null : label.trim().toUpperCase(Locale.ENGLISH);
}
private static String[] sanitize(final String[] enumerators) {
if (enumerators == null || enumerators.length == 0) {
return null;
}
final String[] clean = new String[enumerators.length];
for (int i = 0; i < enumerators.length; i++) {
clean[i] = sanitize(enumerators[i]);
}
return clean;
}
private static String toString(final String[] enumerators) {
String result = "(";
for (int i = 0; i < enumerators.length; i++) {
result += "'" + enumerators[i] + "'";
if (i < enumerators.length - 1) {
result += ", ";
}
}
result += ")";
return result;
}
private static Validation validate(final String[] enumerators) {
final String[] cleaned = sanitize(enumerators);
if (cleaned == null || cleaned.length == 0) {
return Validation.EMPTY;
}
for (int i = 0; i < cleaned.length; i++) {
if (cleaned[i] == null || cleaned[i].equals("")) {
return Validation.EMPTY;
}
if (i < cleaned.length - 1) {
for (int j = i + 1; j < cleaned.length; j++) {
if (cleaned[i].equals(cleaned[j])) {
return Validation.DUPLICATE;
}
}
}
}
return Validation.VALID;
}
private static Validation validate(final String[] enumerators, final Value value) {
final Validation validation = validate(enumerators);
if (!validation.equals(Validation.VALID)) {
return validation;
}
if (DataType.isStringType(value.getType())) {
final String cleanLabel = sanitize(value.getString());
for (String enumerator : enumerators) {
if (cleanLabel.equals(sanitize(enumerator))) {
return Validation.VALID;
}
}
return Validation.INVALID;
} else {
final int ordinal = value.getInt();
if (ordinal < 0 || ordinal >= enumerators.length) {
return Validation.INVALID;
}
return Validation.VALID;
}
}
}
......@@ -8,6 +8,8 @@ package org.h2.value;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.h2.engine.Mode;
/**
* Base implementation of the ENUM data type.
*
......@@ -137,4 +139,13 @@ public class ValueEnumBase extends Value {
final Value iv = v.convertTo(Value.INT);
return convertTo(Value.INT).subtract(iv);
}
@Override
public Value convertTo(int targetType, int precision, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
if (targetType == Value.ENUM) {
return extTypeInfo.cast(this);
}
return super.convertTo(targetType, precision, mode, column, extTypeInfo);
}
}
......@@ -5,6 +5,7 @@
*/
package org.h2.value;
import static org.h2.util.geometry.EWKBUtils.EWKB_SRID;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Arrays;
......@@ -18,6 +19,7 @@ import org.h2.util.geometry.EWKBUtils;
import org.h2.util.geometry.EWKTUtils;
import org.h2.util.geometry.GeometryUtils;
import org.h2.util.geometry.GeometryUtils.EnvelopeAndDimensionSystemTarget;
import org.h2.util.geometry.GeometryUtils.EnvelopeTarget;
import org.h2.util.geometry.JTSUtils;
import org.locationtech.jts.geom.Geometry;
......@@ -30,6 +32,8 @@ import org.locationtech.jts.geom.Geometry;
*/
public class ValueGeometry extends Value {
private static final double[] UNKNOWN_ENVELOPE = new double[0];
/**
* As conversion from/to WKB cost a significant amount of CPU cycles, WKB
* are kept in ValueGeometry instance.
......@@ -43,9 +47,15 @@ public class ValueGeometry extends Value {
private final int hashCode;
/**
* Dimension system. -1 if not known yet.
* Geometry type and dimension system in OGC geometry code format (type +
* dimensionSystem * 1000).
*/
private final int typeAndDimensionSystem;
/**
* Spatial reference system identifier.
*/
private int dimensionSystem;
private final int srid;
/**
* The envelope of the value. Calculated only on request.
......@@ -62,14 +72,18 @@ public class ValueGeometry extends Value {
* Create a new geometry object.
*
* @param bytes the EWKB bytes
* @param dimensionSystem dimension system
* @param envelope the envelope
*/
private ValueGeometry(byte[] bytes, int dimensionSystem, double[] envelope) {
private ValueGeometry(byte[] bytes, double[] envelope) {
if (bytes.length < 9 || bytes[0] != 0) {
throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, StringUtils.convertBytesToHex(bytes));
}
this.bytes = bytes;
this.hashCode = Arrays.hashCode(bytes);
this.dimensionSystem = dimensionSystem;
this.envelope = envelope;
int t = Bits.readInt(bytes, 1);
srid = (t & EWKB_SRID) != 0 ? Bits.readInt(bytes, 5) : 0;
typeAndDimensionSystem = (t & 0xffff) % 1_000 + EWKBUtils.type2dimensionSystem(t) * 1_000;
hashCode = Arrays.hashCode(bytes);
}
/**
......@@ -84,9 +98,8 @@ public class ValueGeometry extends Value {
EnvelopeAndDimensionSystemTarget target = new EnvelopeAndDimensionSystemTarget();
Geometry g = (Geometry) o;
JTSUtils.parseGeometry(g, target);
int dimensionSystem = target.getDimensionSystem();
return (ValueGeometry) Value.cache(new ValueGeometry(JTSUtils.geometry2ewkb(g, dimensionSystem),
dimensionSystem, target.getEnvelope()));
return (ValueGeometry) Value.cache(new ValueGeometry( //
JTSUtils.geometry2ewkb(g, target.getDimensionSystem()), target.getEnvelope()));
} catch (RuntimeException ex) {
throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, String.valueOf(o));
}
......@@ -102,9 +115,8 @@ public class ValueGeometry extends Value {
try {
EnvelopeAndDimensionSystemTarget target = new EnvelopeAndDimensionSystemTarget();
EWKTUtils.parseEWKT(s, target);
int dimensionSystem = target.getDimensionSystem();
return (ValueGeometry) Value.cache(new ValueGeometry(EWKTUtils.ewkt2ewkb(s, dimensionSystem),
dimensionSystem, target.getEnvelope()));
return (ValueGeometry) Value.cache(new ValueGeometry( //
EWKTUtils.ewkt2ewkb(s, target.getDimensionSystem()), target.getEnvelope()));
} catch (RuntimeException ex) {
throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, s);
}
......@@ -129,7 +141,7 @@ public class ValueGeometry extends Value {
* @return the value
*/
public static ValueGeometry get(byte[] bytes) {
return (ValueGeometry) Value.cache(new ValueGeometry(bytes, -1, null));
return (ValueGeometry) Value.cache(new ValueGeometry(bytes, UNKNOWN_ENVELOPE));
}
/**
......@@ -142,9 +154,8 @@ public class ValueGeometry extends Value {
try {
EnvelopeAndDimensionSystemTarget target = new EnvelopeAndDimensionSystemTarget();
EWKBUtils.parseEWKB(bytes, target);
int dimensionSystem = target.getDimensionSystem();
return (ValueGeometry) Value.cache(new ValueGeometry(EWKBUtils.ewkb2ewkb(bytes, dimensionSystem),
dimensionSystem, target.getEnvelope()));
return (ValueGeometry) Value.cache(new ValueGeometry( //
EWKBUtils.ewkb2ewkb(bytes, target.getDimensionSystem()), target.getEnvelope()));
} catch (RuntimeException ex) {
throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, StringUtils.convertBytesToHex(bytes));
}
......@@ -158,8 +169,7 @@ public class ValueGeometry extends Value {
*/
public static Value fromEnvelope(double[] envelope) {
return envelope != null
? Value.cache(new ValueGeometry(EWKBUtils.envelope2wkb(envelope), GeometryUtils.DIMENSION_SYSTEM_XY,
envelope))
? Value.cache(new ValueGeometry(EWKBUtils.envelope2wkb(envelope), envelope))
: ValueNull.INSTANCE;
}
......@@ -180,13 +190,23 @@ public class ValueGeometry extends Value {
return ((Geometry) geometry).copy();
}
private void calculateInfo() {
if (dimensionSystem < 0) {
EnvelopeAndDimensionSystemTarget target = new EnvelopeAndDimensionSystemTarget();
EWKBUtils.parseEWKB(bytes, target);
envelope = target.getEnvelope();
dimensionSystem = target.getDimensionSystem();
}
/**
* Returns geometry type and dimension system in OGC geometry code format
* (type + dimensionSystem * 1000).
*
* @return geometry type and dimension system
*/
public int getTypeAndDimensionSystem() {
return typeAndDimensionSystem;
}
/**
* Returns geometry type.
*
* @return geometry type and dimension system
*/
public int getGeometryType() {
return typeAndDimensionSystem % 1_000;
}
/**
......@@ -195,8 +215,16 @@ public class ValueGeometry extends Value {
* @return dimension system
*/
public int getDimensionSystem() {
calculateInfo();
return dimensionSystem;
return typeAndDimensionSystem / 1_000;
}
/**
* Return a spatial reference system identifier.
*
* @return spatial reference system identifier
*/
public int getSRID() {
return srid;
}
/**
......@@ -205,7 +233,11 @@ public class ValueGeometry extends Value {
* @return envelope of this geometry
*/
public double[] getEnvelopeNoCopy() {
calculateInfo();
if (envelope == UNKNOWN_ENVELOPE) {
EnvelopeTarget target = new EnvelopeTarget();
EWKBUtils.parseEWKB(bytes, target);
envelope = target.getEnvelope();
}
return envelope;
}
......@@ -318,8 +350,10 @@ public class ValueGeometry extends Value {
}
@Override
public Value convertTo(int targetType, int precision, Mode mode, Object column, String[] enumerators) {
if (targetType == Value.JAVA_OBJECT) {
public Value convertTo(int targetType, int precision, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
if (targetType == Value.GEOMETRY) {
return extTypeInfo != null ? extTypeInfo.cast(this) : this;
} else if (targetType == Value.JAVA_OBJECT) {
return this;
}
return super.convertTo(targetType, precision, mode, column, null);
......
......@@ -371,12 +371,11 @@ public class ValueLob extends Value {
* the precision plays no role when converting the value
* @param mode the database mode
* @param column the column (if any), used for to improve the error message if conversion fails
* @param enumerators the ENUM datatype enumerators (if any),
* for dealing with ENUM conversions
* @param extTypeInfo the extended data type information, or null
* @return the converted value
*/
@Override
public Value convertTo(int t, int precision, Mode mode, Object column, String[] enumerators) {
public Value convertTo(int t, int precision, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
if (t == valueType) {
return this;
} else if (t == Value.CLOB) {
......
......@@ -206,12 +206,11 @@ public class ValueLobDb extends Value {
* @param precision the precision
* @param mode the mode
* @param column the column (if any), used for to improve the error message if conversion fails
* @param enumerators the ENUM datatype enumerators (if any),
* for dealing with ENUM conversions
* @param extTypeInfo the extended data type information, or null
* @return the converted value
*/
@Override
public Value convertTo(int t, int precision, Mode mode, Object column, String[] enumerators) {
public Value convertTo(int t, int precision, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
if (t == valueType) {
return this;
} else if (t == Value.CLOB) {
......
......@@ -134,7 +134,7 @@ public class ValueNull extends Value {
}
@Override
public Value convertTo(int type, int precision, Mode mode, Object column, String[] enumerators) {
public Value convertTo(int type, int precision, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
return this;
}
......
......@@ -641,7 +641,7 @@ public class TestSpatial extends TestDb {
}
private void checkSRID(byte[] bytes, int srid) {
Point point = (Point) ValueGeometry.get(bytes).getGeometry();
Point point = (Point) ValueGeometry.getFromEWKB(bytes).getGeometry();
assertEquals(1.1, point.getX());
assertEquals(1.2, point.getY());
assertEquals(srid, point.getSRID());
......
......@@ -25,6 +25,7 @@ import org.h2.util.JdbcUtils;
import org.h2.util.StringUtils;
import org.h2.value.CompareMode;
import org.h2.value.DataType;
import org.h2.value.ExtTypeInfo;
import org.h2.value.Value;
import org.h2.value.ValueBytes;
import org.h2.value.ValueDouble;
......@@ -394,7 +395,7 @@ public class TestCustomDataTypesHandler extends TestDb {
}
@Override
public Value convertTo(int targetType, int precision, Mode mode, Object column, String[] enumerators) {
public Value convertTo(int targetType, int precision, Mode mode, Object column, ExtTypeInfo extTypeInfo) {
if (getType() == targetType) {
return this;
}
......
......@@ -26,6 +26,12 @@ select * from card;
> 4 null
> rows: 3
alter table card alter column suit enum('a', 'b', 'c', 'd');
> exception ENUM_VALUE_NOT_PERMITTED
alter table card alter column suit enum('''none''', 'hearts', 'clubs', 'spades', 'diamonds');
> ok
select * from card order by suit;
> RANK SUIT
> ---- ------
......@@ -50,7 +56,10 @@ select rank from card where suit = 'diamonds';
>> 8
select column_type from information_schema.columns where COLUMN_NAME = 'SUIT';
>> ENUM('hearts','clubs','spades','diamonds')
>> ENUM('''none''', 'hearts', 'clubs', 'spades', 'diamonds')
alter table card alter column suit enum('hearts', 'clubs', 'spades', 'diamonds');
> ok
--- ENUM integer-based operations
......@@ -242,13 +251,13 @@ SELECT * FROM V3;
>> -1
SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME = 'E' ORDER BY TABLE_NAME;
> TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION COLUMN_DEFAULT IS_NULLABLE DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_PRECISION_RADIX NUMERIC_SCALE DATETIME_PRECISION INTERVAL_TYPE INTERVAL_PRECISION CHARACTER_SET_NAME COLLATION_NAME TYPE_NAME NULLABLE IS_COMPUTED SELECTIVITY CHECK_CONSTRAINT SEQUENCE_NAME REMARKS SOURCE_DATA_TYPE COLUMN_TYPE COLUMN_ON_UPDATE IS_VISIBLE
> ------------- ------------ ---------- ----------- ---------------- -------------- ----------- --------- ------------------------ ---------------------- ----------------- ----------------------- ------------- ------------------ ------------- ------------------ ------------------ -------------- --------- -------- ----------- ----------- ---------------- ------------- ------- ---------------- ------------- ---------------- ----------
> SCRIPT PUBLIC TEST E 1 null YES 1111 2147483647 2147483647 2147483647 10 0 null null null Unicode OFF ENUM 1 FALSE 50 null null ENUM('A','B') null TRUE
> SCRIPT PUBLIC V E 1 null YES 1111 2147483647 2147483647 2147483647 10 0 null null null Unicode OFF ENUM 1 FALSE 50 null null ENUM('A','B') null TRUE
> SCRIPT PUBLIC V1 E 1 null YES 4 2147483647 2147483647 2147483647 10 0 null null null Unicode OFF INTEGER 1 FALSE 50 null null INTEGER null TRUE
> SCRIPT PUBLIC V2 E 1 null YES 4 2147483647 2147483647 2147483647 10 0 null null null Unicode OFF INTEGER 1 FALSE 50 null null INTEGER null TRUE
> SCRIPT PUBLIC V3 E 1 null YES 4 2147483647 2147483647 2147483647 10 0 null null null Unicode OFF INTEGER 1 FALSE 50 null null INTEGER null TRUE
> TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME ORDINAL_POSITION COLUMN_DEFAULT IS_NULLABLE DATA_TYPE CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH NUMERIC_PRECISION NUMERIC_PRECISION_RADIX NUMERIC_SCALE DATETIME_PRECISION INTERVAL_TYPE INTERVAL_PRECISION CHARACTER_SET_NAME COLLATION_NAME TYPE_NAME NULLABLE IS_COMPUTED SELECTIVITY CHECK_CONSTRAINT SEQUENCE_NAME REMARKS SOURCE_DATA_TYPE COLUMN_TYPE COLUMN_ON_UPDATE IS_VISIBLE
> ------------- ------------ ---------- ----------- ---------------- -------------- ----------- --------- ------------------------ ---------------------- ----------------- ----------------------- ------------- ------------------ ------------- ------------------ ------------------ -------------- --------- -------- ----------- ----------- ---------------- ------------- ------- ---------------- -------------- ---------------- ----------
> SCRIPT PUBLIC TEST E 1 null YES 1111 2147483647 2147483647 2147483647 10 0 null null null Unicode OFF ENUM 1 FALSE 50 null null ENUM('A', 'B') null TRUE
> SCRIPT PUBLIC V E 1 null YES 1111 2147483647 2147483647 2147483647 10 0 null null null Unicode OFF ENUM 1 FALSE 50 null null ENUM('A', 'B') null TRUE
> SCRIPT PUBLIC V1 E 1 null YES 4 2147483647 2147483647 2147483647 10 0 null null null Unicode OFF INTEGER 1 FALSE 50 null null INTEGER null TRUE
> SCRIPT PUBLIC V2 E 1 null YES 4 2147483647 2147483647 2147483647 10 0 null null null Unicode OFF INTEGER 1 FALSE 50 null null INTEGER null TRUE
> SCRIPT PUBLIC V3 E 1 null YES 4 2147483647 2147483647 2147483647 10 0 null null null Unicode OFF INTEGER 1 FALSE 50 null null INTEGER null TRUE
> rows (ordered): 5
DROP VIEW V;
......
......@@ -2,3 +2,99 @@
-- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--
CREATE TABLE TEST(G GEOMETRY, G_S GEOMETRY(1), P GEOMETRY(POINT), P_S GEOMETRY(POINT, 1),
PZ1 GEOMETRY(POINT Z), PZ2 GEOMETRY(POINTZ), PZ1_S GEOMETRY(POINT Z, 1), PZ2_S GEOMETRY(POINTZ, 1),
PM GEOMETRY(POINT M), PZM GEOMETRY(POINT ZM), PZM_S GEOMETRY(POINT ZM, -100),
LS GEOMETRY(LINESTRING), PG GEOMETRY(POLYGON),
MP GEOMETRY(MULTIPOINT), MLS GEOMETRY(MULTILINESTRING), MPG GEOMETRY(MULTIPOLYGON),
GC GEOMETRY(GEOMETRYCOLLECTION));
> ok
INSERT INTO TEST VALUES ('POINT EMPTY', 'SRID=1;POINT EMPTY', 'POINT EMPTY', 'SRID=1;POINT EMPTY',
'POINT Z EMPTY', 'POINT Z EMPTY', 'SRID=1;POINT Z EMPTY', 'SRID=1;POINTZ EMPTY',
'POINT M EMPTY', 'POINT ZM EMPTY', 'SRID=-100;POINT ZM EMPTY',
'LINESTRING EMPTY', 'POLYGON EMPTY',
'MULTIPOINT EMPTY', 'MULTILINESTRING EMPTY', 'MULTIPOLYGON EMPTY',
'GEOMETRYCOLLECTION EMPTY');
> update count: 1
SELECT COLUMN_NAME, TYPE_NAME, COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'TEST' ORDER BY ORDINAL_POSITION;
> COLUMN_NAME TYPE_NAME COLUMN_TYPE
> ----------- --------- ----------------------------
> G GEOMETRY GEOMETRY
> G_S GEOMETRY GEOMETRY(1)
> P GEOMETRY GEOMETRY(POINT)
> P_S GEOMETRY GEOMETRY(POINT, 1)
> PZ1 GEOMETRY GEOMETRY(POINT Z)
> PZ2 GEOMETRY GEOMETRY(POINT Z)
> PZ1_S GEOMETRY GEOMETRY(POINT Z, 1)
> PZ2_S GEOMETRY GEOMETRY(POINT Z, 1)
> PM GEOMETRY GEOMETRY(POINT M)
> PZM GEOMETRY GEOMETRY(POINT ZM)
> PZM_S GEOMETRY GEOMETRY(POINT ZM, -100)
> LS GEOMETRY GEOMETRY(LINESTRING)
> PG GEOMETRY GEOMETRY(POLYGON)
> MP GEOMETRY GEOMETRY(MULTIPOINT)
> MLS GEOMETRY GEOMETRY(MULTILINESTRING)
> MPG GEOMETRY GEOMETRY(MULTIPOLYGON)
> GC GEOMETRY GEOMETRY(GEOMETRYCOLLECTION)
> rows (ordered): 17
UPDATE TEST SET G = 'SRID=10;LINESTRING EMPTY';
> update count: 1
UPDATE TEST SET GC = 'SRID=8;GEOMETRYCOLLECTION(POINT (1 1))';
> update count: 1
UPDATE TEST SET G_S = 'POINT (1 1)';
> exception CHECK_CONSTRAINT_VIOLATED_1
UPDATE TEST SET P = 'POINT Z EMPTY';
> exception CHECK_CONSTRAINT_VIOLATED_1
UPDATE TEST SET P = 'POLYGON EMPTY';
> exception CHECK_CONSTRAINT_VIOLATED_1
UPDATE TEST SET PZ1 = 'POINT EMPTY';
> exception CHECK_CONSTRAINT_VIOLATED_1
SELECT * FROM TEST;
> G G_S P P_S PZ1 PZ2 PZ1_S PZ2_S PM PZM PZM_S LS PG MP MLS MPG GC
> ------------------------ ------------------ ----------- ------------------ ------------- ------------- -------------------- -------------------- ------------- -------------- ------------------------ ---------------- ------------- ---------------- --------------------- ------------------ ---------------------------------------
> SRID=10;LINESTRING EMPTY SRID=1;POINT EMPTY POINT EMPTY SRID=1;POINT EMPTY POINT Z EMPTY POINT Z EMPTY SRID=1;POINT Z EMPTY SRID=1;POINT Z EMPTY POINT M EMPTY POINT ZM EMPTY SRID=-100;POINT ZM EMPTY LINESTRING EMPTY POLYGON EMPTY MULTIPOINT EMPTY MULTILINESTRING EMPTY MULTIPOLYGON EMPTY SRID=8;GEOMETRYCOLLECTION (POINT (1 1))
> rows: 1
SELECT G FROM TEST WHERE P_S = 'SRID=1;POINT EMPTY';
>> SRID=10;LINESTRING EMPTY
SELECT G FROM TEST WHERE P_S = 'GEOMETRYCOLLECTION Z EMPTY';
> exception CHECK_CONSTRAINT_VIOLATED_1
CREATE SPATIAL INDEX IDX ON TEST(GC);
> ok
SELECT P FROM TEST WHERE GC = 'SRID=8;GEOMETRYCOLLECTION (POINT (1 1))';
>> POINT EMPTY
SELECT P FROM TEST WHERE GC = 'SRID=8;GEOMETRYCOLLECTION Z (POINT (1 1 1))';
> exception CHECK_CONSTRAINT_VIOLATED_1
SELECT CAST('POINT EMPTY' AS GEOMETRY(POINT));
>> POINT EMPTY
SELECT CAST('POINT EMPTY' AS GEOMETRY(POINT Z));
> exception CHECK_CONSTRAINT_VIOLATED_1
SELECT CAST('POINT EMPTY' AS GEOMETRY(POINT, 0));
>> POINT EMPTY
SELECT CAST('POINT EMPTY' AS GEOMETRY(POINT, 1));
> exception CHECK_CONSTRAINT_VIOLATED_1
SELECT CAST('POINT EMPTY' AS GEOMETRY(POLYGON));
> exception CHECK_CONSTRAINT_VIOLATED_1
DROP TABLE TEST;
> ok
......@@ -36,12 +36,12 @@ DROP DOMAIN E_NN CASCADE;
SELECT COLUMN_NAME, NULLABLE, COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'TEST' ORDER BY ORDINAL_POSITION;
> COLUMN_NAME NULLABLE COLUMN_TYPE
> ----------- -------- ----------------------
> ----------- -------- -----------------------
> I 0 INT NOT NULL
> E1 1 ENUM('A','B')
> E2 0 ENUM('A','B') NOT NULL
> E3 0 ENUM('A','B') NOT NULL
> E4 1 ENUM('A','B')
> E1 1 ENUM('A', 'B')
> E2 0 ENUM('A', 'B') NOT NULL
> E3 0 ENUM('A', 'B') NOT NULL
> E4 1 ENUM('A', 'B')
> rows (ordered): 5
DROP TABLE TEST;
......
......@@ -8,6 +8,8 @@ package org.h2.test.unit;
import static org.h2.util.geometry.GeometryUtils.DIMENSION_SYSTEM_XY;
import static org.h2.util.geometry.GeometryUtils.DIMENSION_SYSTEM_XYM;
import static org.h2.util.geometry.GeometryUtils.DIMENSION_SYSTEM_XYZ;
import static org.h2.util.geometry.GeometryUtils.DIMENSION_SYSTEM_XYZM;
import static org.h2.util.geometry.GeometryUtils.GEOMETRY_COLLECTION;
import static org.h2.util.geometry.GeometryUtils.M;
import static org.h2.util.geometry.GeometryUtils.MAX_X;
import static org.h2.util.geometry.GeometryUtils.MAX_Y;
......@@ -33,6 +35,7 @@ import org.h2.util.geometry.GeometryUtils.EnvelopeAndDimensionSystemTarget;
import org.h2.util.geometry.GeometryUtils.Target;
import org.h2.util.geometry.JTSUtils;
import org.h2.util.geometry.JTSUtils.GeometryTarget;
import org.h2.value.ValueGeometry;
import org.locationtech.jts.geom.CoordinateSequence;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Geometry;
......@@ -234,8 +237,11 @@ public class TestGeometryUtils extends TestBase {
testEnvelope(envelopeFromJTS, target.getEnvelope());
// Test dimensions
testDimensions(numOfDimensions > 2 ? GeometryUtils.DIMENSION_SYSTEM_XYZ : GeometryUtils.DIMENSION_SYSTEM_XY,
wkbFromJTS);
int expectedDimensionSystem = numOfDimensions > 2 ? GeometryUtils.DIMENSION_SYSTEM_XYZ
: GeometryUtils.DIMENSION_SYSTEM_XY;
testDimensions(expectedDimensionSystem, wkbFromJTS);
testValueGeometryProperties(wkbFromJTS);
}
private void testEnvelope(Envelope envelopeFromJTS, double[] envelopeFromH2) {
......@@ -274,6 +280,7 @@ public class TestGeometryUtils extends TestBase {
testDimensionMCheckPoint(cs);
assertEquals(ewkb, JTSUtils.geometry2ewkb(p));
testDimensions(GeometryUtils.DIMENSION_SYSTEM_XYM, ewkb);
testValueGeometryProperties(ewkb);
if (JTSUtils.M_IS_SUPPORTED) {
p = (Point) new WKTReader().read("POINT M (1 2 3)");
......@@ -310,6 +317,7 @@ public class TestGeometryUtils extends TestBase {
testDimensionZMCheckPoint(cs);
assertEquals(ewkb, JTSUtils.geometry2ewkb(p));
testDimensions(GeometryUtils.DIMENSION_SYSTEM_XYZM, ewkb);
testValueGeometryProperties(ewkb);
if (JTSUtils.M_IS_SUPPORTED) {
p = (Point) new WKTReader().read("POINT ZM (1 2 3 4)");
......@@ -333,6 +341,32 @@ public class TestGeometryUtils extends TestBase {
assertEquals(4, cs.getOrdinate(0, M));
}
private void testValueGeometryProperties(byte[] ewkb) {
ValueGeometry vg = ValueGeometry.getFromEWKB(ewkb);
DimensionSystemTarget target = new DimensionSystemTarget();
EWKBUtils.parseEWKB(ewkb, target);
int dimensionSystem = target.getDimensionSystem();
assertEquals(dimensionSystem, vg.getDimensionSystem());
String formattedType = EWKTUtils.formatGeometryTypeAndDimensionSystem(vg.getTypeAndDimensionSystem());
assertTrue(EWKTUtils.ewkb2ewkt(ewkb).startsWith(formattedType));
switch (dimensionSystem) {
case DIMENSION_SYSTEM_XY:
assertTrue(formattedType.indexOf(' ') < 0);
break;
case DIMENSION_SYSTEM_XYZ:
assertTrue(formattedType.endsWith(" Z"));
break;
case DIMENSION_SYSTEM_XYM:
assertTrue(formattedType.endsWith(" M"));
break;
case DIMENSION_SYSTEM_XYZM:
assertTrue(formattedType.endsWith(" ZM"));
break;
}
assertEquals(vg.getTypeAndDimensionSystem(), vg.getGeometryType() + vg.getDimensionSystem() * 1_000);
assertEquals(0, vg.getSRID());
}
private void testFiniteOnly() {
for (int i = 0; i < NON_FINITE.length; i++) {
testFiniteOnly(NON_FINITE[i], new EWKBTarget(new ByteArrayOutputStream(), NON_FINITE_DIMENSIONS[i]));
......@@ -381,6 +415,10 @@ public class TestGeometryUtils extends TestBase {
assertEquals(10, gc.getSRID());
assertEquals(10, gc.getGeometryN(0).getSRID());
assertEquals(ewkb, JTSUtils.geometry2ewkb(gc));
ValueGeometry vg = ValueGeometry.getFromEWKB(ewkb);
assertEquals(10, vg.getSRID());
assertEquals(GEOMETRY_COLLECTION, vg.getTypeAndDimensionSystem());
assertEquals("SRID=-1;POINT EMPTY", EWKTUtils.ewkb2ewkt(EWKTUtils.ewkt2ewkb(" srid=-1 ; POINT EMPTY ")));
}
private void testDimensions(int expected, byte[] ewkb) {
......
......@@ -795,3 +795,4 @@ minxd maxxd minyd maxyd bminxd bmaxxd bminyd bmaxyd
interior envelopes multilinestring multipoint packed exterior normalization awkward determination subgeometries
xym normalizes coord setz xyzm geometrycollection multipolygon mixup rings polygons rejection finite
pointzm pointz pointm dimensionality redefine forum measures
mpg casted pzm mls constrained
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论