bchapuis commented on code in PR #3668:
URL: https://github.com/apache/calcite/pull/3668#discussion_r2143309805


##########
core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java:
##########
@@ -1511,6 +1513,21 @@ public static SqlNode toSql(RexLiteral literal) {
       // Create a string without specifying a charset
       return SqlLiteral.createCharString((String) 
castNonNull(literal.getValue2()), POS);
     }
+    case GEOMETRY: {
+      Geometry geom = castNonNull(literal.getValueAs(Geometry.class));
+      switch (typeName) {
+      case CHAR:
+      case VARCHAR:
+        return SqlLiteral.createCharString(
+            SpatialTypeUtils.asEwkt(geom), POS);
+      case BINARY:
+      case VARBINARY:
+        return SqlLiteral.createBinaryString(
+            SpatialTypeUtils.asWkbArray(geom), POS);
+      default:
+        return SqlLiteral.createNull(POS);

Review Comment:
   sorry, I wasn't sure what to return in that case. Would the following be 
better?
   
   ```java
   SqlLiteral.createUnknown(POS);
   ```



##########
core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactoryImpl.java:
##########
@@ -644,15 +658,27 @@ public class JavaType extends RelDataTypeImpl {
     private final boolean nullable;
     private final @Nullable SqlCollation collation;
     private final @Nullable Charset charset;
+    private final @Nullable RelDataTypeFamily family;

Review Comment:
   The JavaType(Class clazz) constructor is called in many places, and I was 
worried to break things by specifying a RelDataTypeFamily everywhere. Is this 
something I should do?



##########
core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java:
##########
@@ -116,6 +117,7 @@
 
 import org.checkerframework.checker.initialization.qual.UnknownInitialization;
 import org.checkerframework.checker.nullness.qual.Nullable;
+import org.locationtech.jts.geom.Geometry;

Review Comment:
   I'm not sure how to do it as well. We can probably try to abstract JTS, but 
I'm not sure the effort is worth it as JTS almost became a defacto standard in 
Java. Additionaly,  I think this coupling is internal as the WKT strings and 
WKB binaries outputed here can be read by other libraries if needed.



##########
core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java:
##########
@@ -312,7 +312,9 @@ private Expression getConvertExpression(
       case CHAR:
       case VARCHAR:
         return Expressions.call(BuiltInMethod.ST_GEOM_FROM_EWKT.method, 
operand);
-
+      case BINARY:
+      case VARBINARY:
+        return Expressions.call(BuiltInMethod.ST_GEOM_FROM_EWKB.method, 
operand);

Review Comment:
   I logged a jira issue for the introduction of this functions:
   https://issues.apache.org/jira/browse/CALCITE-6264



##########
core/src/main/java/org/apache/calcite/sql/dialect/PostgisSqlDialect.java:
##########
@@ -0,0 +1,53 @@
+/*
+ * 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.sql.dialect;
+
+import org.apache.calcite.avatica.util.Casing;
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlDialect;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.validate.SqlConformanceEnum;
+import org.apache.calcite.util.RelToSqlConverterUtil;
+
+/**
+ * A <code>SqlDialect</code> implementation for the PostgreSQL database.
+ */
+public class PostgisSqlDialect extends PostgresqlSqlDialect {
+
+  public static final SqlDialect.Context DEFAULT_CONTEXT = 
SqlDialect.EMPTY_CONTEXT
+      .withDatabaseProduct(DatabaseProduct.POSTGIS)
+      .withConformance(SqlConformanceEnum.LENIENT)
+      .withIdentifierQuoteString("\"")
+      .withUnquotedCasing(Casing.TO_LOWER)
+      .withDataTypeSystem(POSTGRESQL_TYPE_SYSTEM);
+
+  public static final SqlDialect DEFAULT = new 
PostgisSqlDialect(DEFAULT_CONTEXT);
+
+  /** Creates a PostgresqlSqlDialect. */
+  public PostgisSqlDialect(Context context) {
+    super(context);
+  }
+
+  @Override public void unparseCall(SqlWriter writer, SqlCall call, int 
leftPrec, int rightPrec) {
+    if (call.getOperator().getName().equals("ST_UNARYUNION")) {
+      RelToSqlConverterUtil.specialOperatorByName("ST_UNION")

Review Comment:
   Thanks, I fixed it.



##########
core/src/main/java/org/apache/calcite/jdbc/JavaTypeFactoryImpl.java:
##########
@@ -440,4 +442,17 @@ private static class RecordFieldImpl implements 
Types.RecordField {
       return syntheticType;
     }
   }
+
+  @Override public RelDataType createJavaType(Class clazz) {
+    return createJavaType(clazz, null);
+  }
+
+  @Override public RelDataType createJavaType(
+      Class clazz,
+      @Nullable RelDataTypeFamily family) {
+    if (Geometry.class.isAssignableFrom(clazz)) {
+      return canonize(new JavaType(clazz, SqlTypeFamily.GEOMETRY));
+    }
+    return super.createJavaType(clazz, family);
+  }

Review Comment:
   @zabetak This relates to my interpretation of the architectural comments 
made in [CALCITE-6263](https://issues.apache.org/jira/browse/CALCITE-6263): 
`RelDataTypeFactoryImpl` is a core class, and the coupling with JTS should 
preferably occur in a child class. `JavaTypeFactoryImpl` looked to me like a 
good place to check if a class is a JTS Geometry.



##########
core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java:
##########
@@ -837,7 +837,7 @@ private static ColumnMetaData.AvaticaType 
avaticaType(JavaTypeFactory typeFactor
         }
         return ColumnMetaData.struct(columns);
       case ExtraSqlTypes.GEOMETRY:
-        typeOrdinal = Types.VARCHAR;
+        typeOrdinal = Types.OTHER;

Review Comment:
   Yes, this is probably a breaking change, but I don't think VARCHAR is 
appropriate here.
   
   I think GEOMETRY is clearly database specific as indicated here:
   
https://docs.oracle.com/en/java/javase/11/docs/api/java.sql/java/sql/Types.html#OTHER
   
   This choice also correspond to what is implemented in the postgresql jdbc 
driver.



##########
core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactory.java:
##########
@@ -65,6 +65,15 @@ public interface RelDataTypeFactory {
    */
   RelDataType createJavaType(Class clazz);
 
+  /**
+   * Creates a type that corresponds to a Java class, with a given family.
+   *
+   * @param clazz the Java class used to define the type
+   * @param family The family of the type, or null to infer
+   * @return canonical Java type descriptor
+   */
+  RelDataType createJavaType(Class clazz, @Nullable RelDataTypeFamily family);

Review Comment:
   This modification was suggested in the following commit and seemed necessary 
decouple `RelDataTypeFactoryImpl` from JTS:
   
   
https://github.com/apache/calcite/pull/3668/commits/da8494213c3810584a75064d5fadf52ac1f95d12
   
   > A related change is to add a 'family' parameter to
   the `TypeFactory.createJavaType` method. It is nullable, and
   if null, the type becomes its own family. In JavaType, we
   should add a `family` field. 
   
   > When this change is complete we should be able to remove JTS
   Geometry from RelDataTypeFactoryImpl.CLASS_FAMILIES.



##########
core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java:
##########
@@ -1445,6 +1447,21 @@ public static SqlNode toSql(RexLiteral literal) {
       // Create a string without specifying a charset
       return SqlLiteral.createCharString((String) 
castNonNull(literal.getValue2()), POS);
     }
+    case GEO: {
+      Geometry geom = castNonNull(literal.getValueAs(Geometry.class));
+      switch (typeName) {

Review Comment:
   I believe this is necessary when geometries are represented as string in the 
query with WKT.



##########
core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java:
##########
@@ -433,6 +433,17 @@ private static RelDataType sqlType(RelDataTypeFactory 
typeFactory, int dataType,
                 typeFactory.createSqlType(SqlTypeName.ANY), true);
       }
       return typeFactory.createArrayType(component, -1);
+    case GEOMETRY:

Review Comment:
   I'm not sure about this. There is no type name for geometry in 
java.sql.Types, but there is an `ExtraSqlTypes.GEOMETRY = 2015` defined in 
Calcite. I have now registered it in `SqlTypeName#JDBC_TYPE_TO_NAME`.



##########
core/src/main/java/org/apache/calcite/sql/type/SqlTypeFamily.java:
##########
@@ -76,7 +76,7 @@ public enum SqlTypeFamily implements RelDataTypeFamily {
   ANY,
   CURSOR,
   COLUMN_LIST,
-  GEO,
+  GEOMETRY,

Review Comment:
   Maybe, I can revert it.



##########
core/src/main/java/org/apache/calcite/sql/dialect/PostgisSqlDialect.java:
##########
@@ -0,0 +1,58 @@
+/*
+ * 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.sql.dialect;
+
+import org.apache.calcite.avatica.util.Casing;
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlDialect;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.validate.SqlConformanceEnum;
+import org.apache.calcite.util.RelToSqlConverterUtil;
+
+/**
+ * A <code>SqlDialect</code> implementation for the PostGIS database that 
extends
+ * the PostgreSQL dialect. As PostGIS is an extension of PostgreSQL that must 
be
+ * installed separately, it makes sense to extend the PostgreSQL dialect. 
Having
+ * a separate dialect allows for the possibility of adding PostGIS-specific 
features
+ * in the future. It also isolates PostGIS-specific behavior from the 
PostgreSQL dialect.
+ */
+public class PostgisSqlDialect extends PostgresqlSqlDialect {
+
+  public static final SqlDialect.Context DEFAULT_CONTEXT = 
SqlDialect.EMPTY_CONTEXT
+      .withDatabaseProduct(DatabaseProduct.POSTGIS)
+      .withConformance(SqlConformanceEnum.LENIENT)
+      .withIdentifierQuoteString("\"")
+      .withUnquotedCasing(Casing.TO_LOWER)
+      .withDataTypeSystem(POSTGRESQL_TYPE_SYSTEM);
+
+  public static final SqlDialect DEFAULT = new 
PostgisSqlDialect(DEFAULT_CONTEXT);
+
+  /** Creates a PostgisSqlDialect. */
+  public PostgisSqlDialect(Context context) {
+    super(context);
+  }
+
+  @Override public void unparseCall(SqlWriter writer, SqlCall call, int 
leftPrec, int rightPrec) {
+    if (call.getOperator().getName().equals(SqlKind.ST_UNARYUNION.name())) {
+      RelToSqlConverterUtil.specialOperatorByName(SqlKind.ST_UNION.name())
+          .unparse(writer, call, 0, 0);

Review Comment:
   I think this is somewhat related to 
https://issues.apache.org/jira/browse/CALCITE-5280



##########
linq4j/src/main/java/org/apache/calcite/linq4j/function/Parameter.java:
##########
@@ -81,4 +82,10 @@
    * is NULL, and therefore optional parameters must be nullable.
    */
   boolean optional() default false;
+
+  /** Returns the SQL type code.
+   *
+   * <p>Values are typically from {@link java.sql.Types}, for example
+   * {@link java.sql.Types#INTEGER}. */
+  int sqlType() default Types.OTHER;

Review Comment:
   This was introduced in the following commit:
   
https://github.com/apache/calcite/pull/3668/commits/da8494213c3810584a75064d5fadf52ac1f95d12
   
   > Add annotation Parameter.sqlType
   > The annotation allows you to define the SQL type of a
   parameter, for cases where it cannot be deduced automatically
   from the Java type. This is especially useful for GEOMETRY
   values, for which Calcite has no built-in Java type. (JTS
   Geometry is widely used in Calcite, but it is an
   implementation, and we do not want it to become the
   specification.)
   



##########
core/src/main/java/org/apache/calcite/rel/type/RelDataTypeFactoryImpl.java:
##########
@@ -680,6 +717,9 @@ public Class getJavaClass() {
     }
 
     @Override public RelDataTypeFamily getFamily() {
+      if (this.family != null) {
+        return this.family;
+      }
       RelDataTypeFamily family = CLASS_FAMILIES.get(clazz);
       return family != null ? family : this;

Review Comment:
   Same question as before, isn't it a risky breaking change.



##########
core/src/main/java/org/apache/calcite/config/Lex.java:
##########
@@ -63,6 +63,13 @@ public enum Lex {
   MYSQL_ANSI(Quoting.DOUBLE_QUOTE, Casing.UNCHANGED, Casing.UNCHANGED, false,
       CharLiteralStyle.STANDARD),
 
+  /** Lexical policy similar to PostgreSQL. Quoted identifiers are preserved;
+   * Unquoted identifiers are converted to lower-case; after which, identifiers
+   * are matched case-sensitively.
+   */
+  POSTGRESQL(Quoting.DOUBLE_QUOTE, Casing.TO_LOWER, Casing.UNCHANGED, false,

Review Comment:
   I will look into this. I believe it is covered by the integration tests I 
implemented with testcontainers in a separate repository. I'm not sure how to 
test it without starting a postgis container.



##########
core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcToEnumerableConverter.java:
##########
@@ -323,6 +323,14 @@ private static void generateGet(EnumerableRelImplementor 
implementor,
               java.sql.Array.class);
       source = Expressions.call(BuiltInMethod.JDBC_ARRAY_TO_LIST.method, x);
       break;
+    case GEOMETRY:

Review Comment:
   I will look into this. I believe it is covered by the integration tests I 
implemented with testcontainers in a separate repository.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to