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

chunwei 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 52c9b7836b [CALCITE-5163] MysqlSqlDialect support to unparse LISTAGG 
aggregate function
52c9b7836b is described below

commit 52c9b7836bbed90038f28a933c0cb56d2d69cd28
Author: xurenhe <[email protected]>
AuthorDate: Fri May 20 16:37:04 2022 +0800

    [CALCITE-5163] MysqlSqlDialect support to unparse LISTAGG aggregate function
---
 .../calcite/sql/dialect/MysqlSqlDialect.java       | 52 ++++++++++++++++++++++
 .../calcite/rel/rel2sql/RelToSqlConverterTest.java | 24 ++++++++++
 2 files changed, 76 insertions(+)

diff --git 
a/core/src/main/java/org/apache/calcite/sql/dialect/MysqlSqlDialect.java 
b/core/src/main/java/org/apache/calcite/sql/dialect/MysqlSqlDialect.java
index be1c8ef501..195332db69 100644
--- a/core/src/main/java/org/apache/calcite/sql/dialect/MysqlSqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/dialect/MysqlSqlDialect.java
@@ -40,6 +40,8 @@ import org.apache.calcite.sql.SqlNodeList;
 import org.apache.calcite.sql.SqlSelect;
 import org.apache.calcite.sql.SqlWriter;
 import org.apache.calcite.sql.fun.SqlCase;
+import org.apache.calcite.sql.fun.SqlInternalOperators;
+import org.apache.calcite.sql.fun.SqlLibraryOperators;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.type.InferTypes;
@@ -47,8 +49,12 @@ import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
 import org.apache.calcite.sql.type.SqlTypeName;
 
+import com.google.common.collect.ImmutableList;
+
 import org.checkerframework.checker.nullness.qual.Nullable;
 
+import java.util.List;
+
 /**
  * A <code>SqlDialect</code> implementation for the MySQL database.
  */
@@ -223,11 +229,57 @@ public class MysqlSqlDialect extends SqlDialect {
       unparseFloor(writer, call);
       break;
 
+    case WITHIN_GROUP:
+      final List<SqlNode> operands = call.getOperandList();
+      if (operands.size() <= 0 || operands.get(0).getKind() != 
SqlKind.LISTAGG) {
+        super.unparseCall(writer, call, leftPrec, rightPrec);
+        return;
+      }
+      unparseListAggCall(writer, (SqlCall) operands.get(0),
+          operands.size() == 2 ? operands.get(1) : null, leftPrec, rightPrec);
+      break;
+
+    case LISTAGG:
+      unparseListAggCall(writer, call, null, leftPrec, rightPrec);
+      break;
+
     default:
       super.unparseCall(writer, call, leftPrec, rightPrec);
     }
   }
 
+  /**
+   * Unparses LISTAGG for MySQL. This call is translated to GROUP_CONCAT.<br/>
+   * For example:
+   * source: <code>LISTAGG(DISTINCT c1, ',') WITHIN GROUP (ORDER BY c2, 
c3)</code>
+   * target: <code>GROUP_CONCAT(DISTINCT c1 ORDER BY c2, c3 SEPARATOR 
',')</code>
+   *
+   * @param writer Writer
+   * @param listAggCall Call of LISTAGG
+   * @param orderItemNode Elems of WITHIN_GROUP, NULL means none elem in the 
WITHIN_GROUP
+   * @param leftPrec leftPrec
+   * @param rightPrec rightPrec
+   */
+  private void unparseListAggCall(SqlWriter writer, SqlCall listAggCall,
+      @Nullable SqlNode orderItemNode, int leftPrec, int rightPrec) {
+    final List<SqlNode> listAggCallOperands = listAggCall.getOperandList();
+    final boolean separatorExist = listAggCallOperands.size() == 2;
+
+    final ImmutableList.Builder<SqlNode> newOperandListBuilder = 
ImmutableList.<SqlNode>builder()
+        .add(listAggCallOperands.get(0));
+    if (orderItemNode != null) {
+      newOperandListBuilder.add(orderItemNode);
+    }
+    if (separatorExist) {
+      newOperandListBuilder.add(
+          SqlInternalOperators.SEPARATOR.createCall(
+          SqlParserPos.ZERO, listAggCallOperands.get(1)));
+    }
+    
SqlLibraryOperators.GROUP_CONCAT.createCall(listAggCall.getFunctionQuantifier(),
+        listAggCall.getParserPosition(), newOperandListBuilder.build())
+        .unparse(writer, leftPrec, rightPrec);
+  }
+
   /**
    * Unparses datetime floor for MySQL. There is no TRUNC function, so simulate
    * this using calls to DATE_FORMAT.
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 c2d235c132..4f6ee5d838 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
@@ -2457,6 +2457,30 @@ class RelToSqlConverterTest {
     sql(query).withMysql().ok(expected);
   }
 
+  @Test void testMySqlUnparseListAggCall() {
+    final String query = "select\n"
+        + "listagg(distinct \"product_name\", ',') within group(order by 
\"cases_per_pallet\"),\n"
+        + "listagg(\"product_name\", ',') within group(order by 
\"cases_per_pallet\"),\n"
+        + "listagg(distinct \"product_name\") within group(order by 
\"cases_per_pallet\" desc),\n"
+        + "listagg(distinct \"product_name\", ',') within group(order by 
\"cases_per_pallet\"),\n"
+        + "listagg(\"product_name\"),\n"
+        + "listagg(\"product_name\", ',')\n"
+        + "from \"product\"\n"
+        + "group by \"product_id\"\n";
+    final String expected = "SELECT GROUP_CONCAT(DISTINCT `product_name` "
+        + "ORDER BY `cases_per_pallet` IS NULL, `cases_per_pallet` SEPARATOR 
','), "
+        + "GROUP_CONCAT(`product_name` "
+        + "ORDER BY `cases_per_pallet` IS NULL, `cases_per_pallet` SEPARATOR 
','), "
+        + "GROUP_CONCAT(DISTINCT `product_name` "
+        + "ORDER BY `cases_per_pallet` IS NULL DESC, `cases_per_pallet` DESC), 
"
+        + "GROUP_CONCAT(DISTINCT `product_name` "
+        + "ORDER BY `cases_per_pallet` IS NULL, `cases_per_pallet` SEPARATOR 
','), "
+        + "GROUP_CONCAT(`product_name`), GROUP_CONCAT(`product_name` SEPARATOR 
',')\n"
+        + "FROM `foodmart`.`product`\n"
+        + "GROUP BY `product_id`";
+    sql(query).withMysql().ok(expected);
+  }
+
   @Test void 
testMySqlWithHighNullsSelectWithOrderByAscNullsLastAndNoEmulation() {
     final String query = "select \"product_id\" from \"product\"\n"
         + "order by \"product_id\" nulls last";

Reply via email to