[CALCITE-1968] OpenGIS Simple Feature Access SQL 1.2.1: add GEOMETRY data type and first 35 functions
Add Spatial page, document GIS functions in SQL reference (indicating which ones are implemented), and add "countries" data set for testing. Functions: ST_AsText, ST_AsWKT, ST_Boundary, ST_Buffer, ST_Contains, ST_ContainsProperly, ST_Crosses, ST_Disjoint, ST_Distance, ST_DWithin, ST_Envelope, ST_EnvelopesIntersect, ST_Equals, ST_GeometryType, ST_GeometryTypeCode, ST_GeomFromText, ST_Intersects, ST_Is3D, ST_LineFromText, ST_MakeLine, ST_MakePoint, ST_MLineFromText, ST_MPointFromText, ST_MPolyFromText, ST_Overlaps, ST_Point, ST_PointFromText, ST_PolyFromText, ST_SetSRID, ST_Touches, ST_Transform, ST_Union, ST_Within, ST_Z. Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/cc20ca13 Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/cc20ca13 Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/cc20ca13 Branch: refs/heads/branch-1.14 Commit: cc20ca13db4d506d9d4d1b861dd1c7ac3944e56e Parents: b2bf1ca Author: Julian Hyde <[email protected]> Authored: Thu Aug 24 03:27:35 2017 -0700 Committer: Julian Hyde <[email protected]> Committed: Tue Aug 29 13:24:42 2017 -0700 ---------------------------------------------------------------------- core/pom.xml | 4 + core/src/main/codegen/templates/Parser.jj | 11 +- .../calcite/jdbc/JavaTypeFactoryImpl.java | 3 + .../org/apache/calcite/model/ModelHandler.java | 39 +- .../calcite/prepare/CalcitePrepareImpl.java | 6 +- .../apache/calcite/runtime/CalciteResource.java | 3 + .../apache/calcite/runtime/GeoFunctions.java | 651 ++++++++++ .../apache/calcite/sql/type/ExtraSqlTypes.java | 3 + .../sql/type/JavaToSqlTypeConversionRules.java | 3 + .../sql/type/SqlTypeAssignmentRules.java | 3 + .../apache/calcite/sql/type/SqlTypeFamily.java | 5 +- .../apache/calcite/sql/type/SqlTypeName.java | 3 +- .../sql/validate/SqlAbstractConformance.java | 4 + .../calcite/sql/validate/SqlConformance.java | 12 +- .../sql/validate/SqlConformanceEnum.java | 11 + .../calcite/runtime/CalciteResource.properties | 1 + .../calcite/jdbc/CalciteRemoteDriverTest.java | 2 +- .../calcite/sql/parser/SqlParserTest.java | 7 + .../org/apache/calcite/test/CalciteAssert.java | 23 + .../calcite/test/CountriesTableFunction.java | 327 +++++ .../org/apache/calcite/test/QuidemTest.java | 4 + .../apache/calcite/test/SqlValidatorTest.java | 18 + core/src/test/resources/sql/spatial.iq | 1114 ++++++++++++++++++ file/src/test/resources/geo/countries.csv | 246 ++++ pom.xml | 6 + site/_data/docs.yml | 1 + site/_docs/reference.md | 342 +++++- site/_docs/spatial.md | 63 + src/main/config/checkstyle/suppressions.xml | 3 + 29 files changed, 2908 insertions(+), 10 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/pom.xml ---------------------------------------------------------------------- diff --git a/core/pom.xml b/core/pom.xml index 6e8428a..de6e24c 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -60,6 +60,10 @@ limitations under the License. <artifactId>commons-lang3</artifactId> </dependency> <dependency> + <groupId>com.esri.geometry</groupId> + <artifactId>esri-geometry-api</artifactId> + </dependency> + <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> </dependency> http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/src/main/codegen/templates/Parser.jj ---------------------------------------------------------------------- diff --git a/core/src/main/codegen/templates/Parser.jj b/core/src/main/codegen/templates/Parser.jj index fae4fd1..0aef85b 100644 --- a/core/src/main/codegen/templates/Parser.jj +++ b/core/src/main/codegen/templates/Parser.jj @@ -4363,7 +4363,7 @@ SqlIdentifier TypeName() : } } -// Types used for for JDBC and ODBC scalar conversion function +// Types used for JDBC and ODBC scalar conversion function SqlTypeName SqlTypeName(Span s) : { } @@ -4383,6 +4383,13 @@ SqlTypeName SqlTypeName(Span s) : | <TIMESTAMP> { return SqlTypeName.TIMESTAMP; } | + <GEOMETRY> { + if (!this.conformance.allowGeometry()) { + throw new ParseException(RESOURCE.geometryDisabled().str()); + } + return SqlTypeName.GEOMETRY; + } +| (<DECIMAL> | <DEC> | <NUMERIC>) { return SqlTypeName.DECIMAL; } | <BOOLEAN> { return SqlTypeName.BOOLEAN; } @@ -5564,6 +5571,7 @@ SqlPostfixOperator PostfixRowOperator() : | < G: "G" > | < GENERAL: "GENERAL" > | < GENERATED: "GENERATED" > +| < GEOMETRY: "GEOMETRY" > | < GET: "GET" > | < GLOBAL: "GLOBAL" > | < GO: "GO" > @@ -6105,6 +6113,7 @@ String CommonNonReservedKeyWord() : | <G> | <GENERAL> | <GENERATED> + | <GEOMETRY> | <GO> | <GOTO> | <GRANTED> http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java b/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java index 083d362..2e0ded2 100644 --- a/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java +++ b/core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java @@ -27,6 +27,7 @@ import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.rel.type.RelDataTypeFieldImpl; import org.apache.calcite.rel.type.RelDataTypeSystem; import org.apache.calcite.rel.type.RelRecordType; +import org.apache.calcite.runtime.GeoFunctions; import org.apache.calcite.runtime.Unit; import org.apache.calcite.sql.type.BasicSqlType; import org.apache.calcite.sql.type.IntervalSqlType; @@ -209,6 +210,8 @@ public class JavaTypeFactoryImpl case BINARY: case VARBINARY: return ByteString.class; + case GEOMETRY: + return GeoFunctions.Geom.class; case ANY: return Object.class; } http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/src/main/java/org/apache/calcite/model/ModelHandler.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/model/ModelHandler.java b/core/src/main/java/org/apache/calcite/model/ModelHandler.java index ee6373e..6af4395 100644 --- a/core/src/main/java/org/apache/calcite/model/ModelHandler.java +++ b/core/src/main/java/org/apache/calcite/model/ModelHandler.java @@ -54,6 +54,7 @@ import java.util.ArrayDeque; import java.util.Collections; import java.util.Deque; import java.util.List; +import java.util.Locale; import java.util.Map; import javax.sql.DataSource; @@ -86,9 +87,29 @@ public class ModelHandler { visit(root); } - /** Creates and validates a ScalarFunctionImpl. */ + /** @deprecated Use {@link #addFunctions}. */ + @Deprecated public static void create(SchemaPlus schema, String functionName, List<String> path, String className, String methodName) { + addFunctions(schema, functionName, path, className, methodName, false); + } + + /** Creates and validates a {@link ScalarFunctionImpl}, and adds it to a + * schema. If {@code methodName} is "*", may add more than one function. + * + * @param schema Schema to add to + * @param functionName Name of function; null to derived from method name + * @param path Path to look for functions + * @param className Class to inspect for methods that may be user-defined + * functions + * @param methodName Method name; + * null means use the class as a UDF; + * "*" means add all methods + * @param upCase Whether to convert method names to upper case, so that they + * can be called without using quotes + */ + public static void addFunctions(SchemaPlus schema, String functionName, + List<String> path, String className, String methodName, boolean upCase) { final Class<?> clazz; try { clazz = Class.forName(className); @@ -112,14 +133,26 @@ public class ModelHandler { if (methodName != null && methodName.equals("*")) { for (Map.Entry<String, ScalarFunction> entry : ScalarFunctionImpl.createAll(clazz).entries()) { - schema.add(entry.getKey(), entry.getValue()); + String name = entry.getKey(); + if (upCase) { + name = name.toUpperCase(Locale.ROOT); + } + schema.add(name, entry.getValue()); } return; } else { final ScalarFunction function = ScalarFunctionImpl.create(clazz, Util.first(methodName, "eval")); if (function != null) { - schema.add(Util.first(functionName, methodName), function); + final String name; + if (functionName != null) { + name = functionName; + } else if (upCase) { + name = methodName.toUpperCase(Locale.ROOT); + } else { + name = methodName; + } + schema.add(name, function); return; } } http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java index 17b6e3a..ec57a8d 100644 --- a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java +++ b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java @@ -124,6 +124,7 @@ import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.parser.SqlParseException; import org.apache.calcite.sql.parser.SqlParser; import org.apache.calcite.sql.parser.SqlParserImplFactory; +import org.apache.calcite.sql.type.ExtraSqlTypes; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.util.ChainedSqlOperatorTable; import org.apache.calcite.sql.validate.SqlConformance; @@ -922,7 +923,7 @@ public class CalcitePrepareImpl implements CalcitePrepare { assert rep != null; return ColumnMetaData.array(componentType, typeName, rep); } else { - final int typeOrdinal = getTypeOrdinal(type); + int typeOrdinal = getTypeOrdinal(type); switch (typeOrdinal) { case Types.STRUCT: final List<ColumnMetaData> columns = new ArrayList<>(); @@ -932,6 +933,9 @@ public class CalcitePrepareImpl implements CalcitePrepare { field.getType(), null, null)); } return ColumnMetaData.struct(columns); + case ExtraSqlTypes.GEOMETRY: + typeOrdinal = Types.VARCHAR; + // fall through default: final Type clazz = typeFactory.getJavaClass(Util.first(fieldType, type)); http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java index 61956cf..b42b1a0 100644 --- a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java +++ b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java @@ -55,6 +55,9 @@ public interface CalciteResource { @BaseMessage("BETWEEN operator has no terminating AND") ExInst<SqlValidatorException> betweenWithoutAnd(); + @BaseMessage("Geo-spatial extensions and the GEOMETRY data type are not enabled") + ExInst<SqlValidatorException> geometryDisabled(); + @BaseMessage("Illegal INTERVAL literal {0}; at {1}") @Property(name = "SQLSTATE", value = "42000") ExInst<CalciteException> illegalIntervalLiteral(String a0, String a1); http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/src/main/java/org/apache/calcite/runtime/GeoFunctions.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/runtime/GeoFunctions.java b/core/src/main/java/org/apache/calcite/runtime/GeoFunctions.java new file mode 100644 index 0000000..6ddcc8a --- /dev/null +++ b/core/src/main/java/org/apache/calcite/runtime/GeoFunctions.java @@ -0,0 +1,651 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.runtime; + +import org.apache.calcite.linq4j.function.Deterministic; +import org.apache.calcite.linq4j.function.Experimental; +import org.apache.calcite.linq4j.function.SemiStrict; +import org.apache.calcite.linq4j.function.Strict; +import org.apache.calcite.util.Util; + +import com.esri.core.geometry.Envelope; +import com.esri.core.geometry.Geometry; +import com.esri.core.geometry.GeometryEngine; +import com.esri.core.geometry.Line; +import com.esri.core.geometry.MapGeometry; +import com.esri.core.geometry.Operator; +import com.esri.core.geometry.OperatorBoundary; +import com.esri.core.geometry.OperatorFactoryLocal; +import com.esri.core.geometry.OperatorIntersects; +import com.esri.core.geometry.Point; +import com.esri.core.geometry.Polygon; +import com.esri.core.geometry.Polyline; +import com.esri.core.geometry.SpatialReference; +import com.esri.core.geometry.WktExportFlags; +import com.esri.core.geometry.WktImportFlags; + +import com.google.common.base.Preconditions; + +import java.math.BigDecimal; + +/** + * Helper methods to implement Geo-spatial functions in generated code. + * + * <p>Remaining tasks: + * + * <ul> + * <li>Determine type code for + * {@link org.apache.calcite.sql.type.ExtraSqlTypes#GEOMETRY} + * <li>Should we create aliases for functions in upper-case? + * Without ST_ prefix? + * <li>Consider adding spatial literals, e.g. `GEOMETRY 'POINT (30 10)'` + * <li>Integer arguments, e.g. SELECT ST_MakePoint(1, 2, 1.5), + * ST_MakePoint(1, 2) + * <li>Are GEOMETRY values comparable? If so add ORDER BY test + * <li>We have to add 'Z' to create 3D objects. This is inconsistent with + * PostGIS. Who is right? At least document the difference. + * <li>Should add GeometryEngine.intersects; similar to disjoint etc. + * <li>Make {@link #ST_MakeLine(Geom, Geom)} varargs</li> + * </ul> + */ +@SuppressWarnings({"UnnecessaryUnboxing", "WeakerAccess", "unused"}) +@Deterministic +@Strict +@Experimental +public class GeoFunctions { + private static final int NO_SRID = 0; + private static final SpatialReference SPATIAL_REFERENCE = + SpatialReference.create(4326); + + private GeoFunctions() {} + + private static UnsupportedOperationException todo() { + return new UnsupportedOperationException(); + } + + protected static Geom bind(Geometry geometry, int srid) { + if (geometry == null) { + return null; + } + if (srid == NO_SRID) { + return new SimpleGeom(geometry); + } + return bind(geometry, SpatialReference.create(srid)); + } + + private static MapGeom bind(Geometry geometry, SpatialReference sr) { + return new MapGeom(new MapGeometry(geometry, sr)); + } + + // Geometry conversion functions (2D and 3D) ================================ + + public static String ST_AsText(Geom g) { + return ST_AsWKT(g); + } + + public static String ST_AsWKT(Geom g) { + return GeometryEngine.geometryToWkt(g.g(), + WktExportFlags.wktExportDefaults); + } + + public static Geom ST_GeomFromText(String s) { + return ST_GeomFromText(s, NO_SRID); + } + + public static Geom ST_GeomFromText(String s, int srid) { + final Geometry g = GeometryEngine.geometryFromWkt(s, + WktImportFlags.wktImportDefaults, Geometry.Type.Unknown); + return bind(g, srid); + } + + public static Geom ST_LineFromText(String s) { + return ST_GeomFromText(s, NO_SRID); + } + + public static Geom ST_LineFromText(String wkt, int srid) { + final Geometry g = GeometryEngine.geometryFromWkt(wkt, + WktImportFlags.wktImportDefaults, + Geometry.Type.Line); + return bind(g, srid); + } + + public static Geom ST_MPointFromText(String s) { + return ST_GeomFromText(s, NO_SRID); + } + + public static Geom ST_MPointFromText(String wkt, int srid) { + final Geometry g = GeometryEngine.geometryFromWkt(wkt, + WktImportFlags.wktImportDefaults, + Geometry.Type.MultiPoint); + return bind(g, srid); + } + + public static Geom ST_PointFromText(String s) { + return ST_GeomFromText(s, NO_SRID); + } + + public static Geom ST_PointFromText(String wkt, int srid) { + final Geometry g = GeometryEngine.geometryFromWkt(wkt, + WktImportFlags.wktImportDefaults, + Geometry.Type.Point); + return bind(g, srid); + } + + public static Geom ST_PolyFromText(String s) { + return ST_GeomFromText(s, NO_SRID); + } + + public static Geom ST_PolyFromText(String wkt, int srid) { + final Geometry g = GeometryEngine.geometryFromWkt(wkt, + WktImportFlags.wktImportDefaults, + Geometry.Type.Polygon); + return bind(g, srid); + } + + public static Geom ST_MLineFromText(String s) { + return ST_GeomFromText(s, NO_SRID); + } + + public static Geom ST_MLineFromText(String wkt, int srid) { + final Geometry g = GeometryEngine.geometryFromWkt(wkt, + WktImportFlags.wktImportDefaults, + Geometry.Type.Unknown); // NOTE: there is no Geometry.Type.MultiLine + return bind(g, srid); + } + + public static Geom ST_MPolyFromText(String s) { + return ST_GeomFromText(s, NO_SRID); + } + + public static Geom ST_MPolyFromText(String wkt, int srid) { + final Geometry g = GeometryEngine.geometryFromWkt(wkt, + WktImportFlags.wktImportDefaults, + Geometry.Type.Unknown); // NOTE: there is no Geometry.Type.MultiPolygon + return bind(g, srid); + } + + // Geometry creation functions ============================================== + + /** Creates a line-string from the given POINTs (or MULTIPOINTs). */ + public static Geom ST_MakeLine(Geom geom1, Geom geom2) { + return makeLine(geom1, geom2); + } + + public static Geom ST_MakeLine(Geom geom1, Geom geom2, Geom geom3) { + return makeLine(geom1, geom2, geom3); + } + + public static Geom ST_MakeLine(Geom geom1, Geom geom2, Geom geom3, + Geom geom4) { + return makeLine(geom1, geom2, geom3, geom4); + } + + public static Geom ST_MakeLine(Geom geom1, Geom geom2, Geom geom3, + Geom geom4, Geom geom5) { + return makeLine(geom1, geom2, geom3, geom4, geom5); + } + + public static Geom ST_MakeLine(Geom geom1, Geom geom2, Geom geom3, + Geom geom4, Geom geom5, Geom geom6) { + return makeLine(geom1, geom2, geom3, geom4, geom5, geom6); + } + + private static Geom makeLine(Geom... geoms) { + final Polyline g = new Polyline(); + Point p = null; + for (Geom geom : geoms) { + if (geom.g() instanceof Point) { + final Point prev = p; + p = (Point) geom.g(); + if (prev != null) { + final Line line = new Line(); + line.setStart(prev); + line.setEnd(p); + g.addSegment(line, false); + } + } + } + return new SimpleGeom(g); + } + + /** Alias for {@link #ST_Point(BigDecimal, BigDecimal)}. */ + public static Geom ST_MakePoint(BigDecimal x, BigDecimal y) { + return ST_Point(x, y); + } + + /** Alias for {@link #ST_Point(BigDecimal, BigDecimal, BigDecimal)}. */ + public static Geom ST_MakePoint(BigDecimal x, BigDecimal y, BigDecimal z) { + return ST_Point(x, y, z); + } + + /** Constructs a 2D point from coordinates. */ + public static Geom ST_Point(BigDecimal x, BigDecimal y) { + // NOTE: Combine the double and BigDecimal variants of this function + return point(x.doubleValue(), y.doubleValue()); + } + + /** Constructs a 3D point from coordinates. */ + public static Geom ST_Point(BigDecimal x, BigDecimal y, BigDecimal z) { + final Geometry g = new Point(x.doubleValue(), y.doubleValue(), + z.doubleValue()); + return new SimpleGeom(g); + } + + private static Geom point(double x, double y) { + final Geometry g = new Point(x, y); + return new SimpleGeom(g); + } + + // Geometry properties (2D and 3D) ========================================== + + /** Returns whether {@code geom} has at least one z-coordinate. */ + public static boolean ST_Is3D(Geom geom) { + return geom.g().hasZ(); + } + + /** Returns the z-value of the first coordinate of {@code geom}. */ + public static Double ST_Z(Geom geom) { + return geom.g().getDescription().hasZ() && geom.g() instanceof Point + ? ((Point) geom.g()).getZ() : null; + } + + /** Returns the boundary of {@code geom}. */ + public static Geom ST_Boundary(Geom geom) { + OperatorBoundary op = OperatorBoundary.local(); + Geometry result = op.execute(geom.g(), null); + return geom.wrap(result); + } + + /** Returns the distance between {@code geom1} and {@code geom2}. */ + public static double ST_Distance(Geom geom1, Geom geom2) { + return GeometryEngine.distance(geom1.g(), geom2.g(), geom1.sr()); + } + + /** Returns the type of {@code geom}. */ + public static String ST_GeometryType(Geom geom) { + return type(geom.g()).name(); + } + + /** Returns the OGC SFS type code of {@code geom}. */ + public static int ST_GeometryTypeCode(Geom geom) { + return type(geom.g()).code; + } + + /** Returns the OGC type of a geometry. */ + private static Type type(Geometry g) { + switch (g.getType()) { + case Point: + return Type.POINT; + case Polyline: + return Type.LINESTRING; + case Polygon: + return Type.POLYGON; + case MultiPoint: + return Type.MULTIPOINT; + case Envelope: + return Type.POLYGON; + case Line: + return Type.LINESTRING; + case Unknown: + return Type.Geometry; + default: + throw new AssertionError(g); + } + } + + /** Returns the minimum bounding box of {@code geom} (which may be a + * GEOMETRYCOLLECTION). */ + public static Geom ST_Envelope(Geom geom) { + final Envelope env = envelope(geom.g()); + return geom.wrap(env); + } + + private static Envelope envelope(Geometry g) { + final Envelope env = new Envelope(); + g.queryEnvelope(env); + return env; + } + + // Geometry predicates ====================================================== + + /** Returns whether {@code geom1} contains {@code geom2}. */ + public static boolean ST_Contains(Geom geom1, Geom geom2) { + return GeometryEngine.contains(geom1.g(), geom2.g(), geom1.sr()); + } + + /** Returns whether {@code geom1} contains {@code geom2} but does not + * intersect its boundary. */ + public static boolean ST_ContainsProperly(Geom geom1, Geom geom2) { + return GeometryEngine.contains(geom1.g(), geom2.g(), geom1.sr()) + && !GeometryEngine.crosses(geom1.g(), geom2.g(), geom1.sr()); + } + + /** Returns whether no point in {@code geom2} is outside {@code geom1}. */ + private static boolean ST_Covers(Geom geom1, Geom geom2) { + throw todo(); + } + + /** Returns whether {@code geom1} crosses {@code geom2}. */ + public static boolean ST_Crosses(Geom geom1, Geom geom2) { + return GeometryEngine.crosses(geom1.g(), geom2.g(), geom1.sr()); + } + + /** Returns whether {@code geom1} and {@code geom2} are disjoint. */ + public static boolean ST_Disjoint(Geom geom1, Geom geom2) { + return GeometryEngine.disjoint(geom1.g(), geom2.g(), geom1.sr()); + } + + /** Returns whether the envelope of {@code geom1} intersects the envelope of + * {@code geom2}. */ + public static boolean ST_EnvelopesIntersect(Geom geom1, Geom geom2) { + final Geometry e1 = envelope(geom1.g()); + final Geometry e2 = envelope(geom2.g()); + return intersects(e1, e2, geom1.sr()); + } + + /** Returns whether {@code geom1} equals {@code geom2}. */ + public static boolean ST_Equals(Geom geom1, Geom geom2) { + return GeometryEngine.equals(geom1.g(), geom2.g(), geom1.sr()); + } + + /** Returns whether {@code geom1} intersects {@code geom2}. */ + public static boolean ST_Intersects(Geom geom1, Geom geom2) { + final Geometry g1 = geom1.g(); + final Geometry g2 = geom2.g(); + final SpatialReference sr = geom1.sr(); + return intersects(g1, g2, sr); + } + + private static boolean intersects(Geometry g1, Geometry g2, + SpatialReference sr) { + final OperatorIntersects op = (OperatorIntersects) OperatorFactoryLocal + .getInstance().getOperator(Operator.Type.Intersects); + return op.execute(g1, g2, sr, null); + } + + /** Returns whether {@code geom1} equals {@code geom2} and their coordinates + * and component Geometries are listed in the same order. */ + public static boolean ST_OrderingEquals(Geom geom1, Geom geom2) { + return GeometryEngine.equals(geom1.g(), geom2.g(), geom1.sr()); + } + + /** Returns {@code geom1} overlaps {@code geom2}. */ + public static boolean ST_Overlaps(Geom geom1, Geom geom2) { + return GeometryEngine.overlaps(geom1.g(), geom2.g(), geom1.sr()); + } + + /** Returns whether {@code geom1} touches {@code geom2}. */ + public static boolean ST_Touches(Geom geom1, Geom geom2) { + return GeometryEngine.touches(geom1.g(), geom2.g(), geom1.sr()); + } + + /** Returns whether {@code geom1} is within {@code geom2}. */ + public static boolean ST_Within(Geom geom1, Geom geom2) { + return GeometryEngine.within(geom1.g(), geom2.g(), geom1.sr()); + } + + /** Returns whether {@code geom1} and {@code geom2} are within + * {@code distance} of each other. */ + public static boolean ST_DWithin(Geom geom1, Geom geom2, double distance) { + final double distance1 = + GeometryEngine.distance(geom1.g(), geom2.g(), geom1.sr()); + return distance1 <= distance; + } + + // Geometry operators (2D and 3D) =========================================== + + /** Computes a buffer around {@code geom}. */ + public static Geom ST_Buffer(Geom geom, double distance) { + final Polygon g = GeometryEngine.buffer(geom.g(), geom.sr(), distance); + return geom.wrap(g); + } + + /** Computes a buffer around {@code geom} with . */ + public static Geom ST_Buffer(Geom geom, double distance, int quadSegs) { + throw todo(); + } + + /** Computes a buffer around {@code geom}. */ + public static Geom ST_Buffer(Geom geom, double bufferSize, String style) { + int quadSegCount = 8; + CapStyle endCapStyle = CapStyle.ROUND; + JoinStyle joinStyle = JoinStyle.ROUND; + float mitreLimit = 5f; + int i = 0; + parse: + for (;;) { + int equals = style.indexOf('=', i); + if (equals < 0) { + break; + } + int space = style.indexOf(' ', equals); + if (space < 0) { + space = style.length(); + } + String name = style.substring(i, equals); + String value = style.substring(equals + 1, space); + switch (name) { + case "quad_segs": + quadSegCount = Integer.valueOf(value); + break; + case "endcap": + endCapStyle = CapStyle.of(value); + break; + case "join": + joinStyle = JoinStyle.of(value); + break; + case "mitre_limit": + case "miter_limit": + mitreLimit = Float.parseFloat(value); + break; + default: + // ignore the value + } + i = space; + for (;;) { + if (i >= style.length()) { + break parse; + } + if (style.charAt(i) != ' ') { + break; + } + ++i; + } + } + return buffer(geom, bufferSize, quadSegCount, endCapStyle, joinStyle, + mitreLimit); + } + + private static Geom buffer(Geom geom, double bufferSize, + int quadSegCount, CapStyle endCapStyle, JoinStyle joinStyle, + float mitreLimit) { + Util.discard(endCapStyle + ":" + joinStyle + ":" + mitreLimit + + ":" + quadSegCount); + throw todo(); + } + + /** Computes the union of {@code geom1} and {@code geom2}. */ + public static Geom ST_Union(Geom geom1, Geom geom2) { + SpatialReference sr = geom1.sr(); + final Geometry g = + GeometryEngine.union(new Geometry[]{geom1.g(), geom2.g()}, sr); + return bind(g, sr); + } + + /** Computes the union of the geometries in {@code geomCollection}. */ + @SemiStrict public static Geom ST_Union(Geom geomCollection) { + SpatialReference sr = geomCollection.sr(); + final Geometry g = + GeometryEngine.union(new Geometry[] {geomCollection.g()}, sr); + return bind(g, sr); + } + + // Geometry projection functions ============================================ + + /** Transforms {@code geom} from one coordinate reference + * system (CRS) to the CRS specified by {@code srid}. */ + public static Geom ST_Transform(Geom geom, int srid) { + return geom.transform(srid); + } + + /** Returns a copy of {@code geom} with a new SRID. */ + public static Geom ST_SetSRID(Geom geom, int srid) { + return geom.transform(srid); + } + + // Inner classes ============================================================ + + /** How the "buffer" command terminates the end of a line. */ + enum CapStyle { + ROUND, FLAT, SQUARE; + + static CapStyle of(String value) { + switch (value) { + case "round": + return ROUND; + case "flat": + case "butt": + return FLAT; + case "square": + return SQUARE; + default: + throw new IllegalArgumentException("unknown endcap value: " + value); + } + } + } + + /** How the "buffer" command decorates junctions between line segments. */ + enum JoinStyle { + ROUND, MITRE, BEVEL; + + static JoinStyle of(String value) { + switch (value) { + case "round": + return ROUND; + case "mitre": + case "miter": + return MITRE; + case "bevel": + return BEVEL; + default: + throw new IllegalArgumentException("unknown join value: " + value); + } + } + } + + /** Geometry. It may or may not have a spatial reference + * associated with it. */ + public interface Geom { + Geometry g(); + + SpatialReference sr(); + + Geom transform(int srid); + + Geom wrap(Geometry g); + } + + /** Sub-class of geometry that has no spatial reference. */ + static class SimpleGeom implements Geom { + final Geometry g; + + SimpleGeom(Geometry g) { + this.g = Preconditions.checkNotNull(g); + } + + @Override public String toString() { + return g.toString(); + } + + public Geometry g() { + return g; + } + + public SpatialReference sr() { + return SPATIAL_REFERENCE; + } + + public Geom transform(int srid) { + if (srid == SPATIAL_REFERENCE.getID()) { + return this; + } + return bind(g, srid); + } + + public Geom wrap(Geometry g) { + return new SimpleGeom(g); + } + } + + /** Sub-class of geometry that has a spatial reference. */ + static class MapGeom implements Geom { + final MapGeometry mg; + + MapGeom(MapGeometry mg) { + this.mg = Preconditions.checkNotNull(mg); + } + + @Override public String toString() { + return mg.toString(); + } + + public Geometry g() { + return mg.getGeometry(); + } + + public SpatialReference sr() { + return mg.getSpatialReference(); + } + + public Geom transform(int srid) { + if (srid == NO_SRID) { + return new SimpleGeom(mg.getGeometry()); + } + if (srid == mg.getSpatialReference().getID()) { + return this; + } + return bind(mg.getGeometry(), srid); + } + + public Geom wrap(Geometry g) { + return bind(g, this.mg.getSpatialReference()); + } + } + + /** Geometry types, with the names and codes assigned by OGC. */ + enum Type { + Geometry(0), + POINT(1), + LINESTRING(2), + POLYGON(3), + MULTIPOINT(4), + MULTILINESTRING(5), + MULTIPOLYGON(6), + GEOMCOLLECTION(7), + CURVE(13), + SURFACE(14), + POLYHEDRALSURFACE(15); + + final int code; + + Type(int code) { + this.code = code; + } + } +} + +// End GeoFunctions.java http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/src/main/java/org/apache/calcite/sql/type/ExtraSqlTypes.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/ExtraSqlTypes.java b/core/src/main/java/org/apache/calcite/sql/type/ExtraSqlTypes.java index c9d775d..e883115 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/ExtraSqlTypes.java +++ b/core/src/main/java/org/apache/calcite/sql/type/ExtraSqlTypes.java @@ -42,6 +42,9 @@ public interface ExtraSqlTypes { int REF_CURSOR = 2012; int TIME_WITH_TIMEZONE = 2013; int TIMESTAMP_WITH_TIMEZONE = 2014; + + // From OpenGIS + int GEOMETRY = 2015; // TODO: confirm } // End ExtraSqlTypes.java http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/src/main/java/org/apache/calcite/sql/type/JavaToSqlTypeConversionRules.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/JavaToSqlTypeConversionRules.java b/core/src/main/java/org/apache/calcite/sql/type/JavaToSqlTypeConversionRules.java index c096dae..f2f3c80 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/JavaToSqlTypeConversionRules.java +++ b/core/src/main/java/org/apache/calcite/sql/type/JavaToSqlTypeConversionRules.java @@ -17,6 +17,7 @@ package org.apache.calcite.sql.type; import org.apache.calcite.avatica.util.ArrayImpl; +import org.apache.calcite.runtime.GeoFunctions; import com.google.common.collect.ImmutableMap; @@ -70,6 +71,8 @@ public class JavaToSqlTypeConversionRules { .put(Time.class, SqlTypeName.TIME) .put(BigDecimal.class, SqlTypeName.DECIMAL) + .put(GeoFunctions.Geom.class, SqlTypeName.GEOMETRY) + .put(ResultSet.class, SqlTypeName.CURSOR) .put(ColumnList.class, SqlTypeName.COLUMN_LIST) .put(ArrayImpl.class, SqlTypeName.ARRAY) http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRules.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRules.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRules.java index 9753f46..f77d639 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRules.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeAssignmentRules.java @@ -161,6 +161,9 @@ public class SqlTypeAssignmentRules { // Timestamp is assignable from ... rules.put(SqlTypeName.TIMESTAMP, EnumSet.of(SqlTypeName.TIMESTAMP)); + // Geometry is assignable from ... + rules.put(SqlTypeName.GEOMETRY, EnumSet.of(SqlTypeName.GEOMETRY)); + // Array is assignable from ... rules.put(SqlTypeName.ARRAY, EnumSet.of(SqlTypeName.ARRAY)); http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java index 4543b00..e88f96a 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java @@ -66,7 +66,8 @@ public enum SqlTypeFamily implements RelDataTypeFamily { NULL, ANY, CURSOR, - COLUMN_LIST; + COLUMN_LIST, + GEO; private static final Map<Integer, SqlTypeFamily> JDBC_TYPE_TO_FAMILY = ImmutableMap.<Integer, SqlTypeFamily>builder() @@ -150,6 +151,8 @@ public enum SqlTypeFamily implements RelDataTypeFamily { return SqlTypeName.DATETIME_TYPES; case DATETIME_INTERVAL: return SqlTypeName.INTERVAL_TYPES; + case GEO: + return ImmutableList.of(SqlTypeName.GEOMETRY); case MULTISET: return ImmutableList.of(SqlTypeName.MULTISET); case ARRAY: http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java index f1b4e6a..fcae1ec 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java +++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java @@ -115,7 +115,8 @@ public enum SqlTypeName { COLUMN_LIST(PrecScale.NO_NO, false, Types.OTHER + 2, SqlTypeFamily.COLUMN_LIST), DYNAMIC_STAR(PrecScale.NO_NO | PrecScale.YES_NO | PrecScale.YES_YES, true, - Types.JAVA_OBJECT, SqlTypeFamily.ANY); + Types.JAVA_OBJECT, SqlTypeFamily.ANY), + GEOMETRY(PrecScale.NO_NO, true, ExtraSqlTypes.GEOMETRY, SqlTypeFamily.GEO); public static final int MAX_DATETIME_PRECISION = 3; http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java index 6fa2483..dbf8b6b 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlAbstractConformance.java @@ -82,6 +82,10 @@ public abstract class SqlAbstractConformance implements SqlConformance { public boolean isPercentRemainderAllowed() { return SqlConformanceEnum.DEFAULT.isPercentRemainderAllowed(); } + + public boolean allowGeometry() { + return SqlConformanceEnum.DEFAULT.allowGeometry(); + } } // End SqlAbstractConformance.java http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java index 2b37026..76b6da9 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformance.java @@ -294,8 +294,18 @@ public interface SqlConformance { * {@link SqlConformanceEnum#MYSQL_5}; * false otherwise. */ - boolean isLimitStartCountAllowed(); + + /** + * Whether to allow geo-spatial extensions, including the GEOMETRY type. + * + * <p>Among the built-in conformance levels, true in + * {@link SqlConformanceEnum#LENIENT}, + * {@link SqlConformanceEnum#MYSQL_5}, + * {@link SqlConformanceEnum#SQL_SERVER_2008}; + * false otherwise. + */ + boolean allowGeometry(); } // End SqlConformance.java http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java index 9f06c18..1a5752f 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlConformanceEnum.java @@ -228,6 +228,17 @@ public enum SqlConformanceEnum implements SqlConformance { return false; } } + + public boolean allowGeometry() { + switch (this) { + case LENIENT: + case MYSQL_5: + case SQL_SERVER_2008: + return true; + default: + return false; + } + } } // End SqlConformanceEnum.java http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties ---------------------------------------------------------------------- diff --git a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties index 52168b0..1f52475 100644 --- a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties +++ b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties @@ -25,6 +25,7 @@ IllegalLiteral=Illegal {0} literal {1}: {2} IdentifierTooLong=Length of identifier ''{0}'' must be less than or equal to {1,number,#} characters BadFormat=not in format ''{0}'' BetweenWithoutAnd=BETWEEN operator has no terminating AND +GeometryDisabled=Geo-spatial extensions and the GEOMETRY data type are not enabled IllegalIntervalLiteral=Illegal INTERVAL literal {0}; at {1} IllegalMinusDate=Illegal expression. Was expecting "(DATETIME - DATETIME) INTERVALQUALIFIER" IllegalOverlaps=Illegal overlaps expression. Was expecting expression on the form "(DATETIME, EXPRESSION) OVERLAPS (DATETIME, EXPRESSION)" http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java b/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java index ab68285..9dd3019 100644 --- a/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java +++ b/core/src/test/java/org/apache/calcite/jdbc/CalciteRemoteDriverTest.java @@ -274,7 +274,7 @@ public class CalciteRemoteDriverTest { @Test public void testRemoteTypeInfo() throws Exception { CalciteAssert.hr().with(REMOTE_CONNECTION_FACTORY) .metaData(GET_TYPEINFO) - .returns(CalciteAssert.checkResultCount(is(42))); + .returns(CalciteAssert.checkResultCount(is(43))); } @Test public void testRemoteTableTypes() throws Exception { http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java index 435e906..e91a76c 100644 --- a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java +++ b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java @@ -6710,6 +6710,13 @@ public class SqlParserTest { "(?s)Encountered \"to\".*"); } + @Test public void testGeometry() { + checkExpFails("cast(null as geometry)", + "Geo-spatial extensions and the GEOMETRY data type are not enabled"); + conformance = SqlConformanceEnum.LENIENT; + checkExp("cast(null as geometry)", "CAST(NULL AS GEOMETRY)"); + } + @Test public void testIntervalArithmetics() { checkExp( "TIME '23:59:59' - interval '1' hour ", http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/src/test/java/org/apache/calcite/test/CalciteAssert.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java index 490c7e5..6db9435 100644 --- a/core/src/test/java/org/apache/calcite/test/CalciteAssert.java +++ b/core/src/test/java/org/apache/calcite/test/CalciteAssert.java @@ -28,15 +28,19 @@ import org.apache.calcite.jdbc.CalciteMetaImpl; import org.apache.calcite.jdbc.CalcitePrepare; import org.apache.calcite.jdbc.CalciteSchema; import org.apache.calcite.materialize.Lattice; +import org.apache.calcite.model.ModelHandler; import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.rel.RelNode; import org.apache.calcite.runtime.CalciteException; import org.apache.calcite.runtime.FlatLists; +import org.apache.calcite.runtime.GeoFunctions; import org.apache.calcite.runtime.Hook; import org.apache.calcite.schema.Schema; import org.apache.calcite.schema.SchemaPlus; import org.apache.calcite.schema.impl.AbstractSchema; import org.apache.calcite.schema.impl.ViewTable; +import org.apache.calcite.schema.impl.ViewTableMacro; +import org.apache.calcite.sql.validate.SqlConformanceEnum; import org.apache.calcite.sql.validate.SqlValidatorException; import org.apache.calcite.tools.FrameworkConfig; import org.apache.calcite.tools.RelBuilder; @@ -753,6 +757,17 @@ public class CalciteAssert { CalciteAssert.addSchema(rootSchema, SchemaSpec.JDBC_FOODMART); } return rootSchema.add("foodmart2", new CloneSchema(foodmart)); + case GEO: + ModelHandler.addFunctions(rootSchema, null, ImmutableList.<String>of(), + GeoFunctions.class.getName(), "*", true); + final SchemaPlus s = rootSchema.add("GEO", new AbstractSchema()); + ModelHandler.addFunctions(s, "countries", ImmutableList.<String>of(), + CountriesTableFunction.class.getName(), null, false); + final String sql = "select * from table(\"countries\"(true))"; + final ViewTableMacro viewMacro = ViewTable.viewMacro(rootSchema, sql, + ImmutableList.of("GEO"), ImmutableList.<String>of(), false); + s.add("countries", viewMacro); + return s; case HR: return rootSchema.add("hr", new ReflectiveSchema(new JdbcTest.HrSchema())); @@ -863,6 +878,10 @@ public class CalciteAssert { SchemaSpec.POST); case REGULAR_PLUS_METADATA: return with(SchemaSpec.HR, SchemaSpec.REFLECTIVE_FOODMART); + case GEO: + return with(SchemaSpec.GEO) + .with(CalciteConnectionProperty.CONFORMANCE.camelName(), + SqlConformanceEnum.LENIENT); case LINGUAL: return with(SchemaSpec.LINGUAL); case JDBC_FOODMART: @@ -1636,6 +1655,9 @@ public class CalciteAssert { * database. */ FOODMART_CLONE, + /** Configuration that contains geo-spatial functions. */ + GEO, + /** Configuration that contains an in-memory clone of the FoodMart * database, plus a lattice to enable on-the-fly materializations. */ JDBC_FOODMART_WITH_LATTICE, @@ -1761,6 +1783,7 @@ public class CalciteAssert { JDBC_FOODMART, CLONE_FOODMART, JDBC_FOODMART_WITH_LATTICE, + GEO, HR, JDBC_SCOTT, SCOTT, http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/src/test/java/org/apache/calcite/test/CountriesTableFunction.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/CountriesTableFunction.java b/core/src/test/java/org/apache/calcite/test/CountriesTableFunction.java new file mode 100644 index 0000000..b909241 --- /dev/null +++ b/core/src/test/java/org/apache/calcite/test/CountriesTableFunction.java @@ -0,0 +1,327 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.test; + +import org.apache.calcite.DataContext; +import org.apache.calcite.config.CalciteConnectionConfig; +import org.apache.calcite.linq4j.Enumerable; +import org.apache.calcite.linq4j.Linq4j; +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.rel.type.RelDataTypeFactory; +import org.apache.calcite.schema.ScannableTable; +import org.apache.calcite.schema.Schema; +import org.apache.calcite.schema.Statistic; +import org.apache.calcite.schema.Statistics; +import org.apache.calcite.sql.SqlCall; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.util.ImmutableBitSet; + +import com.google.common.collect.ImmutableList; + +/** A table function that returns all countries in the world. + * + * <p>Has same content as + * <code>file/src/test/resources/geo/countries.csv</code>. */ +public class CountriesTableFunction { + private CountriesTableFunction() {} + + private static final Object[][] ROWS = { + {"AD", 42.546245, 1.601554, "Andorra"}, + {"AE", 23.424076, 53.847818, "United Arab Emirates"}, + {"AF", 33.93911, 67.709953, "Afghanistan"}, + {"AG", 17.060816, -61.796428, "Antigua and Barbuda"}, + {"AI", 18.220554, -63.068615, "Anguilla"}, + {"AL", 41.153332, 20.168331, "Albania"}, + {"AM", 40.069099, 45.038189, "Armenia"}, + {"AN", 12.226079, -69.060087, "Netherlands Antilles"}, + {"AO", -11.202692, 17.873887, "Angola"}, + {"AQ", -75.250973, -0.071389, "Antarctica"}, + {"AR", -38.416097, -63.616672, "Argentina"}, + {"AS", -14.270972, -170.132217, "American Samoa"}, + {"AT", 47.516231, 14.550072, "Austria"}, + {"AU", -25.274398, 133.775136, "Australia"}, + {"AW", 12.52111, -69.968338, "Aruba"}, + {"AZ", 40.143105, 47.576927, "Azerbaijan"}, + {"BA", 43.915886, 17.679076, "Bosnia and Herzegovina"}, + {"BB", 13.193887, -59.543198, "Barbados"}, + {"BD", 23.684994, 90.356331, "Bangladesh"}, + {"BE", 50.503887, 4.469936, "Belgium"}, + {"BF", 12.238333, -1.561593, "Burkina Faso"}, + {"BG", 42.733883, 25.48583, "Bulgaria"}, + {"BH", 25.930414, 50.637772, "Bahrain"}, + {"BI", -3.373056, 29.918886, "Burundi"}, + {"BJ", 9.30769, 2.315834, "Benin"}, + {"BM", 32.321384, -64.75737, "Bermuda"}, + {"BN", 4.535277, 114.727669, "Brunei"}, + {"BO", -16.290154, -63.588653, "Bolivia"}, + {"BR", -14.235004, -51.92528, "Brazil"}, + {"BS", 25.03428, -77.39628, "Bahamas"}, + {"BT", 27.514162, 90.433601, "Bhutan"}, + {"BV", -54.423199, 3.413194, "Bouvet Island"}, + {"BW", -22.328474, 24.684866, "Botswana"}, + {"BY", 53.709807, 27.953389, "Belarus"}, + {"BZ", 17.189877, -88.49765, "Belize"}, + {"CA", 56.130366, -106.346771, "Canada"}, + {"CC", -12.164165, 96.870956, "Cocos [Keeling] Islands"}, + {"CD", -4.038333, 21.758664, "Congo [DRC]"}, + {"CF", 6.611111, 20.939444, "Central African Republic"}, + {"CG", -0.228021, 15.827659, "Congo [Republic]"}, + {"CH", 46.818188, 8.227512, "Switzerland"}, + {"CI", 7.539989, -5.54708, "Côte d'Ivoire"}, + {"CK", -21.236736, -159.777671, "Cook Islands"}, + {"CL", -35.675147, -71.542969, "Chile"}, + {"CM", 7.369722, 12.354722, "Cameroon"}, + {"CN", 35.86166, 104.195397, "China"}, + {"CO", 4.570868, -74.297333, "Colombia"}, + {"CR", 9.748917, -83.753428, "Costa Rica"}, + {"CU", 21.521757, -77.781167, "Cuba"}, + {"CV", 16.002082, -24.013197, "Cape Verde"}, + {"CX", -10.447525, 105.690449, "Christmas Island"}, + {"CY", 35.126413, 33.429859, "Cyprus"}, + {"CZ", 49.817492, 15.472962, "Czech Republic"}, + {"DE", 51.165691, 10.451526, "Germany"}, + {"DJ", 11.825138, 42.590275, "Djibouti"}, + {"DK", 56.26392, 9.501785, "Denmark"}, + {"DM", 15.414999, -61.370976, "Dominica"}, + {"DO", 18.735693, -70.162651, "Dominican Republic"}, + {"DZ", 28.033886, 1.659626, "Algeria"}, + {"EC", -1.831239, -78.183406, "Ecuador"}, + {"EE", 58.595272, 25.013607, "Estonia"}, + {"EG", 26.820553, 30.802498, "Egypt"}, + {"EH", 24.215527, -12.885834, "Western Sahara"}, + {"ER", 15.179384, 39.782334, "Eritrea"}, + {"ES", 40.463667, -3.74922, "Spain"}, + {"ET", 9.145, 40.489673, "Ethiopia"}, + {"FI", 61.92411, 25.748151, "Finland"}, + {"FJ", -16.578193, 179.414413, "Fiji"}, + {"FK", -51.796253, -59.523613, "Falkland Islands [Islas Malvinas]"}, + {"FM", 7.425554, 150.550812, "Micronesia"}, + {"FO", 61.892635, -6.911806, "Faroe Islands"}, + {"FR", 46.227638, 2.213749, "France"}, + {"GA", -0.803689, 11.609444, "Gabon"}, + {"GB", 55.378051, -3.435973, "United Kingdom"}, + {"GD", 12.262776, -61.604171, "Grenada"}, + {"GE", 42.315407, 43.356892, "Georgia"}, + {"GF", 3.933889, -53.125782, "French Guiana"}, + {"GG", 49.465691, -2.585278, "Guernsey"}, + {"GH", 7.946527, -1.023194, "Ghana"}, + {"GI", 36.137741, -5.345374, "Gibraltar"}, + {"GL", 71.706936, -42.604303, "Greenland"}, + {"GM", 13.443182, -15.310139, "Gambia"}, + {"GN", 9.945587, -9.696645, "Guinea"}, + {"GP", 16.995971, -62.067641, "Guadeloupe"}, + {"GQ", 1.650801, 10.267895, "Equatorial Guinea"}, + {"GR", 39.074208, 21.824312, "Greece"}, + {"GS", -54.429579, -36.587909, "South Georgia and the South Sandwich Islands"}, + {"GT", 15.783471, -90.230759, "Guatemala"}, + {"GU", 13.444304, 144.793731, "Guam"}, + {"GW", 11.803749, -15.180413, "Guinea-Bissau"}, + {"GY", 4.860416, -58.93018, "Guyana"}, + {"GZ", 31.354676, 34.308825, "Gaza Strip"}, + {"HK", 22.396428, 114.109497, "Hong Kong"}, + {"HM", -53.08181, 73.504158, "Heard Island and McDonald Islands"}, + {"HN", 15.199999, -86.241905, "Honduras"}, + {"HR", 45.1, 15.2, "Croatia"}, + {"HT", 18.971187, -72.285215, "Haiti"}, + {"HU", 47.162494, 19.503304, "Hungary"}, + {"ID", -0.789275, 113.921327, "Indonesia"}, + {"IE", 53.41291, -8.24389, "Ireland"}, + {"IL", 31.046051, 34.851612, "Israel"}, + {"IM", 54.236107, -4.548056, "Isle of Man"}, + {"IN", 20.593684, 78.96288, "India"}, + {"IO", -6.343194, 71.876519, "British Indian Ocean Territory"}, + {"IQ", 33.223191, 43.679291, "Iraq"}, + {"IR", 32.427908, 53.688046, "Iran"}, + {"IS", 64.963051, -19.020835, "Iceland"}, + {"IT", 41.87194, 12.56738, "Italy"}, + {"JE", 49.214439, -2.13125, "Jersey"}, + {"JM", 18.109581, -77.297508, "Jamaica"}, + {"JO", 30.585164, 36.238414, "Jordan"}, + {"JP", 36.204824, 138.252924, "Japan"}, + {"KE", -0.023559, 37.906193, "Kenya"}, + {"KG", 41.20438, 74.766098, "Kyrgyzstan"}, + {"KH", 12.565679, 104.990963, "Cambodia"}, + {"KI", -3.370417, -168.734039, "Kiribati"}, + {"KM", -11.875001, 43.872219, "Comoros"}, + {"KN", 17.357822, -62.782998, "Saint Kitts and Nevis"}, + {"KP", 40.339852, 127.510093, "North Korea"}, + {"KR", 35.907757, 127.766922, "South Korea"}, + {"KW", 29.31166, 47.481766, "Kuwait"}, + {"KY", 19.513469, -80.566956, "Cayman Islands"}, + {"KZ", 48.019573, 66.923684, "Kazakhstan"}, + {"LA", 19.85627, 102.495496, "Laos"}, + {"LB", 33.854721, 35.862285, "Lebanon"}, + {"LC", 13.909444, -60.978893, "Saint Lucia"}, + {"LI", 47.166, 9.555373, "Liechtenstein"}, + {"LK", 7.873054, 80.771797, "Sri Lanka"}, + {"LR", 6.428055, -9.429499, "Liberia"}, + {"LS", -29.609988, 28.233608, "Lesotho"}, + {"LT", 55.169438, 23.881275, "Lithuania"}, + {"LU", 49.815273, 6.129583, "Luxembourg"}, + {"LV", 56.879635, 24.603189, "Latvia"}, + {"LY", 26.3351, 17.228331, "Libya"}, + {"MA", 31.791702, -7.09262, "Morocco"}, + {"MC", 43.750298, 7.412841, "Monaco"}, + {"MD", 47.411631, 28.369885, "Moldova"}, + {"ME", 42.708678, 19.37439, "Montenegro"}, + {"MG", -18.766947, 46.869107, "Madagascar"}, + {"MH", 7.131474, 171.184478, "Marshall Islands"}, + {"MK", 41.608635, 21.745275, "Macedonia [FYROM]"}, + {"ML", 17.570692, -3.996166, "Mali"}, + {"MM", 21.913965, 95.956223, "Myanmar [Burma]"}, + {"MN", 46.862496, 103.846656, "Mongolia"}, + {"MO", 22.198745, 113.543873, "Macau"}, + {"MP", 17.33083, 145.38469, "Northern Mariana Islands"}, + {"MQ", 14.641528, -61.024174, "Martinique"}, + {"MR", 21.00789, -10.940835, "Mauritania"}, + {"MS", 16.742498, -62.187366, "Montserrat"}, + {"MT", 35.937496, 14.375416, "Malta"}, + {"MU", -20.348404, 57.552152, "Mauritius"}, + {"MV", 3.202778, 73.22068, "Maldives"}, + {"MW", -13.254308, 34.301525, "Malawi"}, + {"MX", 23.634501, -102.552784, "Mexico"}, + {"MY", 4.210484, 101.975766, "Malaysia"}, + {"MZ", -18.665695, 35.529562, "Mozambique"}, + {"NA", -22.95764, 18.49041, "Namibia"}, + {"NC", -20.904305, 165.618042, "New Caledonia"}, + {"NE", 17.607789, 8.081666, "Niger"}, + {"NF", -29.040835, 167.954712, "Norfolk Island"}, + {"NG", 9.081999, 8.675277, "Nigeria"}, + {"NI", 12.865416, -85.207229, "Nicaragua"}, + {"NL", 52.132633, 5.291266, "Netherlands"}, + {"NO", 60.472024, 8.468946, "Norway"}, + {"NP", 28.394857, 84.124008, "Nepal"}, + {"NR", -0.522778, 166.931503, "Nauru"}, + {"NU", -19.054445, -169.867233, "Niue"}, + {"NZ", -40.900557, 174.885971, "New Zealand"}, + {"OM", 21.512583, 55.923255, "Oman"}, + {"PA", 8.537981, -80.782127, "Panama"}, + {"PE", -9.189967, -75.015152, "Peru"}, + {"PF", -17.679742, -149.406843, "French Polynesia"}, + {"PG", -6.314993, 143.95555, "Papua New Guinea"}, + {"PH", 12.879721, 121.774017, "Philippines"}, + {"PK", 30.375321, 69.345116, "Pakistan"}, + {"PL", 51.919438, 19.145136, "Poland"}, + {"PM", 46.941936, -56.27111, "Saint Pierre and Miquelon"}, + {"PN", -24.703615, -127.439308, "Pitcairn Islands"}, + {"PR", 18.220833, -66.590149, "Puerto Rico"}, + {"PS", 31.952162, 35.233154, "Palestinian Territories"}, + {"PT", 39.399872, -8.224454, "Portugal"}, + {"PW", 7.51498, 134.58252, "Palau"}, + {"PY", -23.442503, -58.443832, "Paraguay"}, + {"QA", 25.354826, 51.183884, "Qatar"}, + {"RE", -21.115141, 55.536384, "Réunion"}, + {"RO", 45.943161, 24.96676, "Romania"}, + {"RS", 44.016521, 21.005859, "Serbia"}, + {"RU", 61.52401, 105.318756, "Russia"}, + {"RW", -1.940278, 29.873888, "Rwanda"}, + {"SA", 23.885942, 45.079162, "Saudi Arabia"}, + {"SB", -9.64571, 160.156194, "Solomon Islands"}, + {"SC", -4.679574, 55.491977, "Seychelles"}, + {"SD", 12.862807, 30.217636, "Sudan"}, + {"SE", 60.128161, 18.643501, "Sweden"}, + {"SG", 1.352083, 103.819836, "Singapore"}, + {"SH", -24.143474, -10.030696, "Saint Helena"}, + {"SI", 46.151241, 14.995463, "Slovenia"}, + {"SJ", 77.553604, 23.670272, "Svalbard and Jan Mayen"}, + {"SK", 48.669026, 19.699024, "Slovakia"}, + {"SL", 8.460555, -11.779889, "Sierra Leone"}, + {"SM", 43.94236, 12.457777, "San Marino"}, + {"SN", 14.497401, -14.452362, "Senegal"}, + {"SO", 5.152149, 46.199616, "Somalia"}, + {"SR", 3.919305, -56.027783, "Suriname"}, + {"ST", 0.18636, 6.613081, "São Tomé and PrÃncipe"}, + {"SV", 13.794185, -88.89653, "El Salvador"}, + {"SY", 34.802075, 38.996815, "Syria"}, + {"SZ", -26.522503, 31.465866, "Swaziland"}, + {"TC", 21.694025, -71.797928, "Turks and Caicos Islands"}, + {"TD", 15.454166, 18.732207, "Chad"}, + {"TF", -49.280366, 69.348557, "French Southern Territories"}, + {"TG", 8.619543, 0.824782, "Togo"}, + {"TH", 15.870032, 100.992541, "Thailand"}, + {"TJ", 38.861034, 71.276093, "Tajikistan"}, + {"TK", -8.967363, -171.855881, "Tokelau"}, + {"TL", -8.874217, 125.727539, "Timor-Leste"}, + {"TM", 38.969719, 59.556278, "Turkmenistan"}, + {"TN", 33.886917, 9.537499, "Tunisia"}, + {"TO", -21.178986, -175.198242, "Tonga"}, + {"TR", 38.963745, 35.243322, "Turkey"}, + {"TT", 10.691803, -61.222503, "Trinidad and Tobago"}, + {"TV", -7.109535, 177.64933, "Tuvalu"}, + {"TW", 23.69781, 120.960515, "Taiwan"}, + {"TZ", -6.369028, 34.888822, "Tanzania"}, + {"UA", 48.379433, 31.16558, "Ukraine"}, + {"UG", 1.373333, 32.290275, "Uganda"}, + {"UM", null, null, "U.S.Minor Outlying Islands"}, + {"US", 37.09024, -95.712891, "United States"}, + {"UY", -32.522779, -55.765835, "Uruguay"}, + {"UZ", 41.377491, 64.585262, "Uzbekistan"}, + {"VA", 41.902916, 12.453389, "Vatican City"}, + {"VC", 12.984305, -61.287228, "Saint Vincent and the Grenadines"}, + {"VE", 6.42375, -66.58973, "Venezuela"}, + {"VG", 18.420695, -64.639968, "British Virgin Islands"}, + {"VI", 18.335765, -64.896335, "U.S. Virgin Islands"}, + {"VN", 14.058324, 108.277199, "Vietnam"}, + {"VU", -15.376706, 166.959158, "Vanuatu"}, + {"WF", -13.768752, -177.156097, "Wallis and Futuna"}, + {"WS", -13.759029, -172.104629, "Samoa"}, + {"XK", 42.602636, 20.902977, "Kosovo"}, + {"YE", 15.552727, 48.516388, "Yemen"}, + {"YT", -12.8275, 45.166244, "Mayotte"}, + {"ZA", -30.559482, 22.937506, "South Africa"}, + {"ZM", -13.133897, 27.849332, "Zambia"}, + {"ZW", -19.015438, 29.154857, "Zimbabwe"}, + }; + + public static ScannableTable eval(boolean b) { + return new ScannableTable() { + public Enumerable<Object[]> scan(DataContext root) { + return Linq4j.asEnumerable(ROWS); + }; + + public RelDataType getRowType(RelDataTypeFactory typeFactory) { + return typeFactory.builder() + .add("country", SqlTypeName.VARCHAR) + .add("latitude", SqlTypeName.DECIMAL).nullable(true) + .add("longitude", SqlTypeName.DECIMAL).nullable(true) + .add("name", SqlTypeName.VARCHAR) + .build(); + } + + public Statistic getStatistic() { + return Statistics.of(246D, + ImmutableList.of(ImmutableBitSet.of(0), ImmutableBitSet.of(3))); + } + + public Schema.TableType getJdbcTableType() { + return Schema.TableType.TABLE; + } + + public boolean isRolledUp(String column) { + return false; + } + + public boolean rolledUpColumnValidInsideAgg(String column, SqlCall call, + SqlNode parent, CalciteConnectionConfig config) { + return false; + } + }; + } +} + +// End CountriesTableFunction.java http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/src/test/java/org/apache/calcite/test/QuidemTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/QuidemTest.java b/core/src/test/java/org/apache/calcite/test/QuidemTest.java index 28994a2..f30ebc8 100644 --- a/core/src/test/java/org/apache/calcite/test/QuidemTest.java +++ b/core/src/test/java/org/apache/calcite/test/QuidemTest.java @@ -279,6 +279,10 @@ public class QuidemTest { return CalciteAssert.that() .with(CalciteAssert.Config.FOODMART_CLONE) .connect(); + case "geo": + return CalciteAssert.that() + .with(CalciteAssert.Config.GEO) + .connect(); case "scott": return CalciteAssert.that() .with(CalciteAssert.Config.SCOTT) http://git-wip-us.apache.org/repos/asf/calcite/blob/cc20ca13/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java index f8c644b..44e510e 100644 --- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java +++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java @@ -992,6 +992,24 @@ public class SqlValidatorTest extends SqlValidatorTestCase { "Binary literal string must contain an even number of hexits"); } + /** + * Tests whether the GEOMETRY data type is allowed. + * + * @see SqlConformance#allowGeometry() + */ + @Test public void testGeometry() { + final SqlTester lenient = + tester.withConformance(SqlConformanceEnum.LENIENT); + final SqlTester strict = + tester.withConformance(SqlConformanceEnum.STRICT_2003); + + final String err = + "Geo-spatial extensions and the GEOMETRY data type are not enabled"; + sql("select cast(null as geometry) as g from emp") + .tester(strict).fails(err) + .tester(lenient).sansCarets().ok(); + } + @Test public void testDateTime() { // LOCAL_TIME checkExp("LOCALTIME(3)");
