This is an automated email from the ASF dual-hosted git repository.

guohongyu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/main by this push:
     new 4369e88b15 [CALCITE-6258] Map value constructor is unparsed 
incorrectly for PrestoSqlDialect
4369e88b15 is described below

commit 4369e88b15a59333c05abeda38cf8799d61a8842
Author: YiwenWu <[email protected]>
AuthorDate: Sun Feb 11 12:59:33 2024 +0800

    [CALCITE-6258] Map value constructor is unparsed incorrectly for 
PrestoSqlDialect
---
 .../calcite/sql/dialect/PrestoSqlDialect.java      | 60 +++++++++++++++++++++-
 .../calcite/rel/rel2sql/RelToSqlConverterTest.java | 17 ++++++
 2 files changed, 75 insertions(+), 2 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/sql/dialect/PrestoSqlDialect.java 
b/core/src/main/java/org/apache/calcite/sql/dialect/PrestoSqlDialect.java
index de80351c3d..ddf2a10f6f 100644
--- a/core/src/main/java/org/apache/calcite/sql/dialect/PrestoSqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/dialect/PrestoSqlDialect.java
@@ -20,19 +20,28 @@ import org.apache.calcite.avatica.util.Casing;
 import org.apache.calcite.config.NullCollation;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeSystem;
+import org.apache.calcite.sql.SqlBasicCall;
 import org.apache.calcite.sql.SqlCall;
 import org.apache.calcite.sql.SqlDialect;
 import org.apache.calcite.sql.SqlIntervalQualifier;
 import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlLiteral;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.fun.SqlArrayValueConstructor;
+import org.apache.calcite.sql.fun.SqlMapValueConstructor;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.util.RelToSqlConverterUtil;
 
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
 
 import org.checkerframework.checker.nullness.qual.Nullable;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * A <code>SqlDialect</code> implementation for the Presto database.
  */
@@ -131,8 +140,14 @@ public class PrestoSqlDialect extends SqlDialect {
       RelToSqlConverterUtil.specialOperatorByName("APPROX_DISTINCT")
           .unparse(writer, call, 0, 0);
     } else {
-      // Current impl is same with Postgresql.
-      PostgresqlSqlDialect.DEFAULT.unparseCall(writer, call, leftPrec, 
rightPrec);
+      switch (call.getKind()) {
+      case MAP_VALUE_CONSTRUCTOR:
+        unparseMapValue(writer, call, leftPrec, rightPrec);
+        break;
+      default:
+        // Current impl is same with Postgresql.
+        PostgresqlSqlDialect.DEFAULT.unparseCall(writer, call, leftPrec, 
rightPrec);
+      }
     }
   }
 
@@ -141,4 +156,45 @@ public class PrestoSqlDialect extends SqlDialect {
     // Current impl is same with MySQL.
     MysqlSqlDialect.DEFAULT.unparseSqlIntervalQualifier(writer, qualifier, 
typeSystem);
   }
+
+  /**
+   * change map open/close symbol from default [] to ().
+   */
+  private void unparseMapValue(SqlWriter writer, SqlCall call,
+      int leftPrec, int rightPrec) {
+    call = convertMapValueCall(call);
+    writer.keyword(call.getOperator().getName());
+    final SqlWriter.Frame frame = writer.startList("(", ")");
+    for (SqlNode operand : call.getOperandList()) {
+      writer.sep(",");
+      operand.unparse(writer, leftPrec, rightPrec);
+    }
+    writer.endList(frame);
+  }
+
+  /**
+   * Convert Presto MapValue call
+   * From MAP['k1','v1','k2','v2'] to MAP[ARRAY['k1', 'k2'],ARRAY['v1', 'v2']].
+   */
+  private SqlCall convertMapValueCall(SqlCall call) {
+    boolean unnestMap = call.operandCount() > 0
+        && call.getOperandList().stream().allMatch(operand -> operand 
instanceof SqlLiteral);
+    if (!unnestMap) {
+      return call;
+    }
+    List<SqlNode> keys = new ArrayList<>();
+    List<SqlNode> values = new ArrayList<>();
+    for (int i = 0; i < call.operandCount(); i++) {
+      if (i % 2 == 0) {
+        keys.add(call.operand(i));
+      } else {
+        values.add(call.operand(i));
+      }
+    }
+    SqlParserPos pos = call.getParserPosition();
+    return new SqlBasicCall(
+        new SqlMapValueConstructor(), ImmutableList.of(
+        new SqlBasicCall(new SqlArrayValueConstructor(), keys, pos),
+        new SqlBasicCall(new SqlArrayValueConstructor(), values, pos)), pos);
+  }
 }
diff --git 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index 739153345b..394aac0780 100644
--- 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++ 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -7876,6 +7876,23 @@ class RelToSqlConverterTest {
         .withSpark().ok(sparkExpected);
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6258";>[CALCITE-6258]
+   * Map value constructor is unparsed incorrectly for PrestoSqlDialect</a>.*/
+  @Test void testMapValueConstructor() {
+    final String query = "SELECT MAP['k1', 'v1', 'k2', 'v2']";
+    final String expectedPresto = "SELECT MAP (ARRAY['k1', 'k2'], ARRAY['v1', 
'v2'])\n"
+        + "FROM (VALUES (0)) AS \"t\" (\"ZERO\")";
+    sql(query).withPresto().ok(expectedPresto);
+  }
+
+  @Test void testMapValueConstructorWithArray() {
+    final String query = "SELECT MAP[ARRAY['k1', 'k2'], ARRAY['v1', 'v2']]";
+    final String expectedPresto = "SELECT MAP (ARRAY['k1', 'k2'], ARRAY['v1', 
'v2'])\n"
+        + "FROM (VALUES (0)) AS \"t\" (\"ZERO\")";
+    sql(query).withPresto().ok(expectedPresto);
+  }
+
   /** Fluid interface to run tests. */
   static class Sql {
     private final CalciteAssert.SchemaSpec schemaSpec;

Reply via email to