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

rubenada 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 7173989162 [CALCITE-7905] AnsiSqlDialect does not check hint names and 
options for block-comment delimiters
7173989162 is described below

commit 71739891623e613f64898caabb50665ecdda33f2
Author: Ruben Quesada Lopez <[email protected]>
AuthorDate: Thu Oct 29 17:24:52 2026 +0000

    [CALCITE-7905] AnsiSqlDialect does not check hint names and options for 
block-comment delimiters
---
 .../calcite/rel/rel2sql/RelToSqlConverter.java     |  4 +--
 .../apache/calcite/sql/dialect/AnsiSqlDialect.java | 20 +++++++++++++
 .../calcite/rel/rel2sql/RelToSqlConverterTest.java | 33 ++++++++++++++++++++++
 3 files changed, 55 insertions(+), 2 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java 
b/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java
index 7806ab5e75..32d1a6c0f9 100644
--- a/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java
+++ b/core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java
@@ -1086,7 +1086,7 @@ public Result visit(TableScan e) {
   }
 
   private static SqlHint toSqlHint(RelHint hint, SqlParserPos pos) {
-    if (hint.kvOptions != null) {
+    if (!hint.kvOptions.isEmpty()) {
       return new SqlHint(pos, new SqlIdentifier(hint.hintName, pos),
           SqlNodeList.of(pos, hint.kvOptions.entrySet().stream()
               .flatMap(
@@ -1094,7 +1094,7 @@ private static SqlHint toSqlHint(RelHint hint, 
SqlParserPos pos) {
                   SqlLiteral.createCharString(e.getValue(), pos)))
               .collect(Collectors.toList())),
           SqlHint.HintOptionFormat.KV_LIST);
-    } else if (hint.listOptions != null) {
+    } else if (!hint.listOptions.isEmpty()) {
       return new SqlHint(pos, new SqlIdentifier(hint.hintName, pos),
           SqlNodeList.of(pos, hint.listOptions.stream()
               .map(e -> SqlLiteral.createCharString(e, pos))
diff --git 
a/core/src/main/java/org/apache/calcite/sql/dialect/AnsiSqlDialect.java 
b/core/src/main/java/org/apache/calcite/sql/dialect/AnsiSqlDialect.java
index 76501b451a..cc6d752720 100644
--- a/core/src/main/java/org/apache/calcite/sql/dialect/AnsiSqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/dialect/AnsiSqlDialect.java
@@ -17,9 +17,13 @@
 package org.apache.calcite.sql.dialect;
 
 import org.apache.calcite.sql.SqlDialect;
+import org.apache.calcite.sql.SqlHint;
+import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.SqlNodeList;
 import org.apache.calcite.sql.SqlWriter;
 
+import static com.google.common.base.Preconditions.checkArgument;
+
 /**
  * A <code>SqlDialect</code> implementation for an unknown ANSI compatible 
database.
  */
@@ -44,10 +48,26 @@ public AnsiSqlDialect(Context context) {
   /** Converts table scan hints.*/
   @Override public void unparseTableScanHints(SqlWriter writer,
       SqlNodeList hints, int leftPrec, int rightPrec) {
+    for (SqlNode node : hints) {
+      final SqlHint hint = (SqlHint) node;
+      checkHintText(hint.getName());
+      hint.getOptionList().forEach(AnsiSqlDialect::checkHintText);
+      hint.getOptionKVPairs().forEach((k, v) -> {
+        checkHintText(k);
+        checkHintText(v);
+      });
+    }
     writer.newlineAndIndent();
     writer.keyword("/*+");
     hints.unparse(writer, 0, 0);
     writer.keyword("*/");
   }
 
+  /** Throws if a hint name or option contains a block-comment delimiter;
+   * hints are written inside a block comment, which has no escape syntax. */
+  private static void checkHintText(String s) {
+    checkArgument(!s.contains("*/") && !s.contains("/*"),
+        "Hint name or option contains a block-comment delimiter: %s", s);
+  }
+
 }
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 ee7f9d5803..7d716d0947 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
@@ -8785,6 +8785,39 @@ private void checkLiteral2(String expression, String 
expected) {
         isLinux(expectedSql2));
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7905";>[CALCITE-7905]
+   * AnsiSqlDialect does not check hint names and options for block-comment 
delimiters</a>. */
+  @Test void testTableScanHintsWithCommentDelimiter() {
+    final RelBuilder builder = relBuilder();
+    builder.getCluster().setHintStrategies(HintStrategyTable.builder()
+        .hintStrategy("PLACEHOLDER", HintPredicates.TABLE_SCAN)
+        .build());
+    // Each entry maps the text expected in the error message to a hint that 
contains it
+    final Map<String, RelHint> hints = ImmutableMap.<String, RelHint>builder()
+        // hint name, no options
+        .put("PLACE*/HOLDER", RelHint.builder("PLACE*/HOLDER").build())
+        // key-value options, in the value and in the key
+        .put("f */ g", RelHint.builder("PLACEHOLDER").hintOption("a", "f */ 
g").build())
+        .put("h /* i", RelHint.builder("PLACEHOLDER").hintOption("h /* i", 
"b").build())
+        // list options
+        .put("b */ c", RelHint.builder("PLACEHOLDER").hintOption("b */ 
c").build())
+        .put("d /* e", 
RelHint.builder("PLACEHOLDER").hintOption("a").hintOption("d /* e").build())
+        .build();
+    for (Map.Entry<String, RelHint> entry : hints.entrySet()) {
+      final RelNode root = builder
+          .scan("orders")
+          .hints(entry.getValue())
+          .project(builder.field("PRODUCT"))
+          .build();
+
+      final IllegalArgumentException e =
+          assertThrows(IllegalArgumentException.class, () ->
+              toSql(root, new AnsiSqlDialect(SqlDialect.EMPTY_CONTEXT)));
+      assertThat(e.getMessage(), containsString(entry.getKey()));
+    }
+  }
+
   /** Test case for
    * <a 
href="https://issues.apache.org/jira/browse/CALCITE-2118";>[CALCITE-2118]
    * RelToSqlConverter should only generate "*" if field names match</a>. */

Reply via email to