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

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

commit 4ab8d8058197a6515a016fa91009754d5e2205e1
Author: Niels Pardon <[email protected]>
AuthorDate: Thu Sep 19 15:36:48 2024 +0200

    [CALCITE-6584] Validate prefixed column identifiers in SET clause of UPDATE 
statement
    
    Signed-off-by: Niels Pardon <[email protected]>
    
    Close apache/calcite#3972
---
 .../calcite/sql/validate/SqlValidatorImpl.java     | 25 ++++++++++++++++----
 .../calcite/sql/validate/SqlValidatorUtil.java     |  2 +-
 .../org/apache/calcite/test/SqlValidatorTest.java  | 27 +++++++++++++++++++++-
 3 files changed, 47 insertions(+), 7 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
index 7b3c050554..c7a93359c7 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
@@ -5133,12 +5133,14 @@ public class SqlValidatorImpl implements 
SqlValidatorWithHints {
    * @param targetColumnList List of target columns, or null if not specified
    * @param append           Whether to append fields to those in <code>
    *                         baseRowType</code>
+   * @param targetTableAlias Target table alias, or null if not specified
    * @return Rowtype
    */
   protected RelDataType createTargetRowType(
       SqlValidatorTable table,
       @Nullable SqlNodeList targetColumnList,
-      boolean append) {
+      boolean append,
+      @Nullable SqlIdentifier targetTableAlias) {
     RelDataType baseRowType = table.getRowType();
     if (targetColumnList == null) {
       return baseRowType;
@@ -5156,6 +5158,15 @@ public class SqlValidatorImpl implements 
SqlValidatorWithHints {
         ? ((RelOptTable) table) : null;
     for (SqlNode node : targetColumnList) {
       SqlIdentifier id = (SqlIdentifier) node;
+      if (!id.isSimple() && targetTableAlias != null) {
+        // checks that target column identifiers are prefixed with the target
+        // table alias
+        SqlIdentifier prefixId = id.skipLast(1);
+        if (!prefixId.toString().equals(targetTableAlias.toString())) {
+          throw newValidationError(prefixId,
+              RESOURCE.unknownIdentifier(prefixId.toString()));
+        }
+      }
       RelDataTypeField targetField =
           SqlValidatorUtil.getTargetField(
               baseRowType, typeFactory, id, catalogReader, relOptTable);
@@ -5189,7 +5200,8 @@ public class SqlValidatorImpl implements 
SqlValidatorWithHints {
         createTargetRowType(
             table,
             insert.getTargetColumnList(),
-            false);
+            false,
+            null);
 
     final SqlNode source = insert.getSource();
     if (source instanceof SqlSelect) {
@@ -5610,7 +5622,8 @@ public class SqlValidatorImpl implements 
SqlValidatorWithHints {
         : relOptTable.unwrapOrThrow(SqlValidatorTable.class);
 
     final RelDataType targetRowType =
-        createTargetRowType(table, call.getTargetColumnList(), true);
+        createTargetRowType(table, call.getTargetColumnList(), true,
+            call.getAlias());
 
     final SqlSelect select = SqlNonNullableAccessors.getSourceSelect(call);
     validateSelect(select, targetRowType);
@@ -5651,13 +5664,15 @@ public class SqlValidatorImpl implements 
SqlValidatorWithHints {
     if (updateCall != null) {
       requireNonNull(table, () -> "ns.getTable() for " + targetNamespace);
       targetRowType =
-          createTargetRowType(table, updateCall.getTargetColumnList(), true);
+          createTargetRowType(table, updateCall.getTargetColumnList(), true,
+              call.getAlias());
     }
     SqlInsert insertCall = call.getInsertCall();
     if (insertCall != null) {
       requireNonNull(table, () -> "ns.getTable() for " + targetNamespace);
       targetRowType =
-          createTargetRowType(table, insertCall.getTargetColumnList(), false);
+          createTargetRowType(table, insertCall.getTargetColumnList(), false,
+              null);
     }
 
     validateSelect(sqlSelect, targetRowType);
diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java
index ac038e3dae..490debb07a 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java
@@ -653,7 +653,7 @@ public class SqlValidatorUtil {
     final Table t = table == null ? null : table.unwrap(Table.class);
     if (!(t instanceof CustomColumnResolvingTable)) {
       final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
-      return nameMatcher.field(rowType, id.getSimple());
+      return nameMatcher.field(rowType, Util.last(id.names));
     }
 
     final List<Pair<RelDataTypeField, List<String>>> entries =
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 c27e0124ab..03b1a7e932 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -4531,6 +4531,30 @@ public class SqlValidatorTest extends 
SqlValidatorTestCase {
         .fails("Table 'SALES.BAD' not found");
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6584";>[CALCITE-6584]
+   * Validate prefixed column identifiers in SET clause of UPDATE
+   * statement</a>. */
+  @Test void testAliasInSetClauseOfUpdate() {
+    // good examples
+    // (Postgres does not consider these valid, but Calcite in this case
+    // is more lenient than Postgres.)
+    sql("UPDATE sales.emp AS e SET e.deptno = 10").ok();
+    sql("UPDATE emp AS e SET e.deptno = 10").ok();
+
+    // bad examples
+    sql("UPDATE sales.emp AS emp SET ^sales.emp^.deptno = 10")
+        .fails("Unknown identifier 'SALES.EMP'");
+    sql("UPDATE sales.emp AS e SET ^emp^.deptno = 10")
+        .fails("Unknown identifier 'EMP'");
+    sql("UPDATE emp AS e SET ^emp^.deptno = 10")
+        .fails("Unknown identifier 'EMP'");
+    sql("UPDATE emp AS e SET ^a.b.c.d^.deptno = 10")
+        .fails("Unknown identifier 'A.B.C.D'");
+    sql("UPDATE emp AS e SET ^dept^.deptno = 10")
+        .fails("Unknown identifier 'DEPT'");
+  }
+
   /** Test case for
    * <a href="https://issues.apache.org/jira/browse/CALCITE-881";>[CALCITE-881]
    * Allow schema.table.column references in GROUP BY</a>. */
@@ -4564,7 +4588,8 @@ public class SqlValidatorTest extends 
SqlValidatorTestCase {
   }
 
   /**
-   * Test case for <a 
href="https://issues.apache.org/jira/browse/CALCITE-3003";>[CALCITE-3003]
+   * Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-3003";>[CALCITE-3003]
    * AssertionError when GROUP BY nested field</a>.
    *
    * <p>Make sure table name of GROUP BY item with nested field could be

Reply via email to