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

dmsysolyatin 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 95350ed1a4 [CALCITE-7220] RelToSqlConverter throws exception for 
UPDATE with self-referencing column in SET
95350ed1a4 is described below

commit 95350ed1a449bbb2f008fcf2b704544e7d95c410
Author: Dmitry Sysolyatin <[email protected]>
AuthorDate: Wed Oct 8 23:06:37 2025 +0300

    [CALCITE-7220] RelToSqlConverter throws exception for UPDATE with 
self-referencing column in SET
---
 .../org/apache/calcite/rel/core/TableModify.java   |  13 +-
 .../calcite/rel/rel2sql/RelToSqlConverter.java     |  12 +-
 .../apache/calcite/rel/rel2sql/SqlImplementor.java | 143 ++++++++++++---------
 .../java/org/apache/calcite/sql/SqlDelete.java     |  10 +-
 .../main/java/org/apache/calcite/sql/SqlMerge.java |  13 +-
 .../java/org/apache/calcite/sql/SqlUpdate.java     |  10 +-
 .../calcite/sql/validate/SqlValidatorImpl.java     |  17 ++-
 .../sql/validate/implicit/TypeCoercionImpl.java    |  15 +--
 .../apache/calcite/sql2rel/SqlToRelConverter.java  |  25 ++--
 .../calcite/rel/rel2sql/RelToSqlConverterTest.java |  96 ++++++++++++++
 .../org/apache/calcite/test/JdbcAdapterTest.java   |   6 +-
 .../apache/calcite/test/SqlToRelConverterTest.xml  |  44 +++----
 .../calcite/test/TypeCoercionConverterTest.xml     |   4 +-
 13 files changed, 272 insertions(+), 136 deletions(-)

diff --git a/core/src/main/java/org/apache/calcite/rel/core/TableModify.java 
b/core/src/main/java/org/apache/calcite/rel/core/TableModify.java
index d42020b665..60b9814dfd 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/TableModify.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/TableModify.java
@@ -55,7 +55,18 @@
  * <ul>
  * <li>For {@code INSERT}, those rows are the new values;
  * <li>for {@code DELETE}, the old values;
- * <li>for {@code UPDATE}, all old values plus updated new values.
+ * <li>for {@code UPDATE}, all old values plus updated new values;
+ * <li>for {@code MERGE}, the rows may contain fields for both {@code INSERT} 
and {@code UPDATE}
+ *     operations, depending on the clauses specified:
+ *     <ul>
+ *       <li>If only `WHEN MATCHED THEN UPDATE` is specified, the row contains 
all old values plus
+ *       updated new values (like {@code UPDATE}).</li>
+ *       <li>If only `WHEN NOT MATCHED THEN INSERT` is specified, the row 
contains new values to be
+ *       inserted (like {@code INSERT}).</li>
+ *       <li>If both `WHEN MATCHED THEN UPDATE` and `WHEN NOT MATCHED THEN 
INSERT` are specified,
+ *       the row contains: new values to be inserted, all old values, updated 
new values.
+ *     </ul>
+ *   </li>
  * </ul>
  */
 public abstract class TableModify extends SingleRel {
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 67f3948c8c..84fb8329ec 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
@@ -1121,9 +1121,6 @@ private static SqlIdentifier getSqlTargetTable(RelNode e) 
{
 
   /** Visits a TableModify; called by {@link #dispatch} via reflection. */
   public Result visit(TableModify modify) {
-    final Map<String, RelDataType> pairs = ImmutableMap.of();
-    final Context context = aliasContext(pairs, false);
-
     // Target Table Name
     final SqlIdentifier sqlTargetTable = getSqlTargetTable(modify);
 
@@ -1142,6 +1139,10 @@ public Result visit(TableModify modify) {
     }
     case UPDATE: {
       final Result input = visitInput(modify, 0);
+      // SELECT statement has two parts: columns from the target table (old 
values) and
+      // expressions for the UPDATE. See the TableModify documentation for 
details.
+      final SqlSelect select = input.asSelect();
+      final Context context = selectListContext(select.getSelectList(), false);
 
       final SqlUpdate sqlUpdate =
           new SqlUpdate(POS, sqlTargetTable,
@@ -1151,8 +1152,7 @@ public Result visit(TableModify modify) {
               exprList(context,
                   requireNonNull(modify.getSourceExpressionList(),
                       () -> "modify.getSourceExpressionList() is null for " + 
modify)),
-              ((SqlSelect) input.node).getWhere(), input.asSelect(),
-              null);
+              select.getWhere(), select, null);
 
       return result(sqlUpdate, input.clauses, modify, null);
     }
@@ -1172,7 +1172,7 @@ public Result visit(TableModify modify) {
       // `WHEN NOT MATCHED THEN INSERT` clauses, the selectList consists of 
three parts:
       // the insert expression, the target table reference, and the update 
expression.
       // When querying with the `WHEN MATCHED THEN UPDATE` clause, the 
selectList will not
-      // include the update expression.
+      // include the insert expression.
       // However, when querying with the `WHEN NOT MATCHED THEN INSERT` clause,
       // the expression list will only contain the insert expression.
       final SqlNodeList selectList = 
SqlUtil.stripListAs(select.getSelectList());
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java 
b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
index 5b39bf0b6f..8952736563 100644
--- a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
+++ b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
@@ -1633,6 +1633,10 @@ private static int computeFieldCount(
     return x;
   }
 
+  public Context selectListContext(SqlNodeList sqlNodeList, boolean aliasRef) {
+    return new SelectListContext(dialect, sqlNodeList.size(), aliasRef, 
sqlNodeList);
+  }
+
   public Context aliasContext(Map<String, RelDataType> aliases,
       boolean qualified) {
     return new AliasContext(dialect, aliases, qualified);
@@ -1810,6 +1814,80 @@ private boolean isWindowTableFunctionRex(RexNode rex) {
     };
   }
 
+  /**
+   * A context that uses a select list.
+   */
+  final class SelectListContext extends BaseContext {
+    private final boolean aliasRef;
+    private final SqlNodeList selectList;
+
+    /**
+     * Creates a SelectListContext.
+     *
+     * @param dialect    SQL dialect.
+     * @param fieldCount Number of fields.
+     * @param aliasRef   If true, and a select-list item at a given ordinal 
has an alias
+     *                   (e.g. {@code SUM(x) AS sx}), then a call to {@code 
field(ordinal)}
+     *                   will return the alias ({@code sx}). If false, it will 
return the
+     *                   full expression ({@code SUM(x)}).
+     * @param selectList The list of expressions in a SELECT clause.
+     */
+    SelectListContext(
+        SqlDialect dialect, int fieldCount, boolean aliasRef, SqlNodeList 
selectList) {
+      super(dialect, fieldCount);
+      this.aliasRef = aliasRef;
+      this.selectList = selectList;
+    }
+
+    @Override public SqlNode field(int ordinal) {
+      final SqlNode selectItem = selectList.get(ordinal);
+      switch (selectItem.getKind()) {
+      case AS:
+        final SqlCall asCall = (SqlCall) selectItem;
+        SqlNode alias = asCall.operand(1);
+        if (aliasRef && !SqlUtil.isGeneratedAlias(((SqlIdentifier) 
alias).getSimple())) {
+          // For BigQuery, given the query
+          //   SELECT SUM(x) AS x FROM t HAVING(SUM(t.x) > 0)
+          // we can generate
+          //   SELECT SUM(x) AS x FROM t HAVING(x > 0)
+          // because 'x' in HAVING resolves to the 'AS x' not 't.x'.
+          return alias;
+        }
+        return asCall.operand(0);
+      default:
+        break;
+      }
+      return selectItem;
+    }
+
+    @Override public SqlNode orderField(int ordinal) {
+      // If the field expression is an unqualified column identifier
+      // and matches a different alias, use an ordinal.
+      // For example, given
+      //    SELECT deptno AS empno, empno AS x FROM emp ORDER BY emp.empno
+      // we generate
+      //    SELECT deptno AS empno, empno AS x FROM emp ORDER BY 2
+      // "ORDER BY empno" would give incorrect result;
+      // "ORDER BY x" is acceptable but is not preferred.
+      final SqlNode node = super.orderField(ordinal);
+      if (node instanceof SqlIdentifier
+          && ((SqlIdentifier) node).isSimple()) {
+        final String name = ((SqlIdentifier) node).getSimple();
+        for (Ord<SqlNode> selectItem : Ord.zip(selectList)) {
+          if (selectItem.i != ordinal) {
+            final @Nullable String alias =
+                SqlValidatorUtil.alias(selectItem.e);
+            if (name.equalsIgnoreCase(alias) && 
dialect.getConformance().isSortByAlias()) {
+              return SqlLiteral.createExactNumeric(
+                  Integer.toString(ordinal + 1), SqlParserPos.ZERO);
+            }
+          }
+        }
+      }
+      return node;
+    }
+  }
+
   /** Result of implementing a node. */
   public class Result {
     final SqlNode node;
@@ -1907,7 +1985,7 @@ private Builder builder(RelNode rel, Set<Clause> clauses) 
{
       if (!selectList.equals(SqlNodeList.SINGLETON_STAR)) {
         final boolean aliasRef = expectedClauses.contains(Clause.HAVING)
             && dialect.getConformance().isHavingAlias();
-        newContext = new SelectListContext(dialect, selectList.size(), 
aliasRef, selectList);
+        newContext = selectListContext(selectList, aliasRef);
       } else {
         boolean qualified =
             !dialect.hasImplicitTableAlias() || aliases.size() > 1;
@@ -2295,69 +2373,6 @@ Result withExpectedClauses(boolean ignoreClauses,
           : new Result(node, clauses, neededAlias, neededType, aliases, anon,
               ignoreClauses, ImmutableSet.copyOf(expectedClauses), 
expectedRel);
     }
-
-    /**
-     * A context that uses a select list.
-     */
-    private final class SelectListContext extends BaseContext {
-      private final boolean aliasRef;
-      private final SqlNodeList selectList;
-
-      private SelectListContext(
-          SqlDialect dialect, int fieldCount, boolean aliasRef, SqlNodeList 
selectList) {
-        super(dialect, fieldCount);
-        this.aliasRef = aliasRef;
-        this.selectList = selectList;
-      }
-
-      @Override public SqlNode field(int ordinal) {
-        final SqlNode selectItem = selectList.get(ordinal);
-        switch (selectItem.getKind()) {
-        case AS:
-          final SqlCall asCall = (SqlCall) selectItem;
-          SqlNode alias = asCall.operand(1);
-          if (aliasRef && !SqlUtil.isGeneratedAlias(((SqlIdentifier) 
alias).getSimple())) {
-            // For BigQuery, given the query
-            //   SELECT SUM(x) AS x FROM t HAVING(SUM(t.x) > 0)
-            // we can generate
-            //   SELECT SUM(x) AS x FROM t HAVING(x > 0)
-            // because 'x' in HAVING resolves to the 'AS x' not 't.x'.
-            return alias;
-          }
-          return asCall.operand(0);
-        default:
-          break;
-        }
-        return selectItem;
-      }
-
-      @Override public SqlNode orderField(int ordinal) {
-        // If the field expression is an unqualified column identifier
-        // and matches a different alias, use an ordinal.
-        // For example, given
-        //    SELECT deptno AS empno, empno AS x FROM emp ORDER BY emp.empno
-        // we generate
-        //    SELECT deptno AS empno, empno AS x FROM emp ORDER BY 2
-        // "ORDER BY empno" would give incorrect result;
-        // "ORDER BY x" is acceptable but is not preferred.
-        final SqlNode node = super.orderField(ordinal);
-        if (node instanceof SqlIdentifier
-            && ((SqlIdentifier) node).isSimple()) {
-          final String name = ((SqlIdentifier) node).getSimple();
-          for (Ord<SqlNode> selectItem : Ord.zip(selectList)) {
-            if (selectItem.i != ordinal) {
-              final @Nullable String alias =
-                  SqlValidatorUtil.alias(selectItem.e);
-              if (name.equalsIgnoreCase(alias) && 
dialect.getConformance().isSortByAlias()) {
-                return SqlLiteral.createExactNumeric(
-                    Integer.toString(ordinal + 1), SqlParserPos.ZERO);
-              }
-            }
-          }
-        }
-        return node;
-      }
-    }
   }
 
   /** Builder. */
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlDelete.java 
b/core/src/main/java/org/apache/calcite/sql/SqlDelete.java
index 9bf937a7cd..9e5ff8c87d 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlDelete.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlDelete.java
@@ -127,11 +127,13 @@ public SqlNode getTargetTable() {
   }
 
   /**
-   * Gets the source SELECT expression for the data to be deleted. This
-   * returns null before the condition has been expanded by
-   * {@link SqlValidatorImpl#performUnconditionalRewrites(SqlNode, boolean)}.
+   * Gets the source SELECT expression for the data to be deleted. The SELECT 
contains target
+   * table columns, for example <code>SELECT * FROM target</code>.
+   * Returns null before the statement has been expanded by
+   * {@link SqlValidatorImpl#performUnconditionalRewrites(SqlNode, boolean)} or
+   * {@link SqlValidatorImpl#createSourceSelectForDelete(SqlDelete)}.
    *
-   * @return the source SELECT for the data to be inserted
+   * @return the source SELECT for the data to be deleted.
    */
   public @Nullable SqlSelect getSourceSelect() {
     return sourceSelect;
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlMerge.java 
b/core/src/main/java/org/apache/calcite/sql/SqlMerge.java
index ba70a9e52e..cbed8e40a8 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlMerge.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlMerge.java
@@ -148,10 +148,19 @@ public SqlNode getCondition() {
 
   /**
    * Gets the source SELECT expression for the data to be updated/inserted.
+   *
+   * <p>The source SELECT column order:
+   * <ul>
+   *   <li>`WHEN NOT MATCHED THEN INSERT` only: [new values...]</li>
+   *   <li>`WHEN MATCHED THEN UPDATE` only: [old table columns..., updated new 
values...]</li>
+   *   <li>Both `NOT MATCHED THEN INSERT` and `WHEN MATCHED THEN UPDATE`: 
[insert new values...,
+   *   old table columns..., updated new values...]</li>
+   * </ul>
    * Returns null before the statement has been expanded by
-   * {@link SqlValidatorImpl#performUnconditionalRewrites(SqlNode, boolean)}.
+   * {@link SqlValidatorImpl#performUnconditionalRewrites(SqlNode, boolean)} 
and
+   * {@link SqlValidatorImpl#rewriteMerge(SqlMerge)}.
    *
-   * @return the source SELECT for the data to be updated
+   * @return the source SELECT for the data to be updated/inserted
    */
   public @Nullable SqlSelect getSourceSelect() {
     return sourceSelect;
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlUpdate.java 
b/core/src/main/java/org/apache/calcite/sql/SqlUpdate.java
index 9c4d2a35b3..3d1ad09c52 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlUpdate.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlUpdate.java
@@ -159,9 +159,13 @@ public SqlNodeList getSourceExpressionList() {
   }
 
   /**
-   * Gets the source SELECT expression for the data to be updated. Returns
-   * null before the statement has been expanded by
-   * {@link SqlValidatorImpl#performUnconditionalRewrites(SqlNode, boolean)}.
+   * Gets the source SELECT expression for the data to be updated. The SELECT 
contains the target
+   * table columns followed by the source expressions. For example, for 
<code>UPDATE target SET
+   * column1 = source_expr1, column5 = source_expr2</code> the source select is
+   * <code>SELECT *, source_expr1, source_expr2</code>.
+   * Returns null before the statement has been expanded by
+   * {@link SqlValidatorImpl#performUnconditionalRewrites(SqlNode, boolean)} 
and
+   * {@link SqlValidatorImpl#createSourceSelectForUpdate(SqlUpdate)}.
    *
    * @return the source SELECT for the data to be updated
    */
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 71e76be78a..03e8978efb 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
@@ -5622,15 +5622,6 @@ protected void checkTypeAssignment(
     // matched
     boolean isUpdateModifiableViewTable = false;
     if (query instanceof SqlUpdate) {
-      final SqlNodeList targetColumnList =
-          requireNonNull(((SqlUpdate) query).getTargetColumnList());
-      final int targetColumnCount = targetColumnList.size();
-      targetRowType =
-          SqlTypeUtil.extractLastNFields(typeFactory, targetRowType,
-              targetColumnCount);
-      sourceRowType =
-          SqlTypeUtil.extractLastNFields(typeFactory, sourceRowType,
-              targetColumnCount);
       isUpdateModifiableViewTable =
           table.unwrap(ModifiableViewTable.class) != null;
     }
@@ -5753,6 +5744,14 @@ private static SqlNode getNthExpr(SqlNode query, int 
ordinal, int sourceCount) {
     checkTypeAssignment(scopes.get(select), table, sourceRowType, 
targetRowType,
         call);
 
+    // Set validated sourceExpressionList from the source select.
+    // The last elements of sourceSelect are the expression list.
+    List<SqlNode> sourceExpressionList =
+        Util.last(select.getSelectList(), 
call.getSourceExpressionList().size());
+    call.setOperand(
+        2, SqlUtil.stripListAs(
+        new SqlNodeList(sourceExpressionList,
+            call.getSourceExpressionList().getParserPosition())));
     checkConstraint(table, call, targetRowType);
 
     validateAccess(call.getTargetTable(), table, SqlAccessEnum.UPDATE);
diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java
 
b/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java
index 762d0498ff..f3f5c53469 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java
@@ -51,7 +51,6 @@
 import java.util.List;
 import java.util.stream.Collectors;
 
-import static org.apache.calcite.linq4j.Nullness.castNonNull;
 import static org.apache.calcite.sql.validate.SqlNonNullableAccessors.getScope;
 import static 
org.apache.calcite.sql.validate.SqlNonNullableAccessors.getSelectList;
 
@@ -787,16 +786,10 @@ private boolean coerceSourceRowType(
           targetType);
     case UPDATE:
       SqlUpdate update = (SqlUpdate) query;
-      final SqlNodeList sourceExpressionList = 
update.getSourceExpressionList();
-      if (sourceExpressionList != null) {
-        return coerceColumnType(sourceScope, sourceExpressionList, 
columnIndex, targetType);
-      } else {
-        // Note: this is dead code since sourceExpressionList is always 
non-null
-        return coerceSourceRowType(sourceScope,
-            castNonNull(update.getSourceSelect()),
-            columnIndex,
-            targetType);
-      }
+      return coerceSourceRowType(sourceScope,
+          requireNonNull(update.getSourceSelect()),
+          columnIndex,
+          targetType);
     default:
       return rowTypeCoercion(sourceScope, query, columnIndex, targetType);
     }
diff --git 
a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java 
b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index 98da6975ad..1fddc3f6ea 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -4466,19 +4466,26 @@ private RelNode convertUpdate(SqlUpdate call) {
       targetColumnNameList.add(field.getName());
     }
 
-    RelNode sourceRel = convertSelect(sourceSelect, false);
+    // `sourceSelect` should contain target columns values plus source 
expressions
+    if (sourceSelect.getSelectList().size()
+        != targetTable.getRowType().getFieldCount() + 
call.getSourceExpressionList().size()) {
+      throw new AssertionError(
+          "Unexpected select list size. Select list should contain both target 
table columns and "
+              + "set expressions");
+    }
 
+    RelNode sourceRel = convertSelect(sourceSelect, false);
     bb.setRoot(sourceRel, false);
-    ImmutableList.Builder<RexNode> rexNodeSourceExpressionListBuilder =
-        ImmutableList.builder();
-    for (SqlNode n : call.getSourceExpressionList()) {
-      RexNode rn = bb.convertExpression(n);
-      rexNodeSourceExpressionListBuilder.add(rn);
-    }
+
+    // sourceRel already contains all source expressions. Only create 
references to those fields.
+    List<RexNode> rexExpressionList =
+        Util.transform(
+            Util.last(sourceRel.getRowType().getFieldList(), 
targetColumnNameList.size()),
+            expressionField -> new RexInputRef(expressionField.getIndex(),
+                expressionField.getType()));
 
     return LogicalTableModify.create(targetTable, catalogReader, sourceRel,
-        LogicalTableModify.Operation.UPDATE, targetColumnNameList,
-        rexNodeSourceExpressionListBuilder.build(), false);
+        LogicalTableModify.Operation.UPDATE, targetColumnNameList, 
rexExpressionList, false);
   }
 
   private RelNode convertMerge(SqlMerge call) {
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 ebd8835ca3..fd13824c87 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
@@ -9566,6 +9566,102 @@ private void checkLiteral2(String expression, String 
expected) {
     sql(sql).ok(expected);
   }
 
+  @Test void testUpdate() {
+    final String sql0 = "update \"foodmart\".\"product\"\n"
+        + "set \"product_name\" = 'calcite'";
+    final String expected0 = "UPDATE \"foodmart\".\"product\" SET 
\"product_name\" = "
+        + "'calcite'";
+    sql(sql0).ok(expected0);
+
+    final String sql1 = "update \"foodmart\".\"product\"\n"
+        + "set \"product_name\" = 'calcite'\n"
+        + "where \"product_id\" = 1";
+    final String expected1 = "UPDATE \"foodmart\".\"product\" SET 
\"product_name\" = "
+        + "'calcite'\nWHERE \"product_id\" = 1";
+    sql(sql1).ok(expected1);
+
+    final String sql2 = "update \"foodmart\".\"product\"\n"
+        + "set \"product_name\" = 'calcite', \"product_id\" = 10\n"
+        + "where \"product_id\" = 1";
+    final String expected2 = "UPDATE \"foodmart\".\"product\" SET 
\"product_name\" = 'calcite', "
+        + "\"product_id\" = 10\nWHERE \"product_id\" = 1";
+    sql(sql2).ok(expected2);
+  }
+
+  /**
+   * Test case for <a 
href="https://issues.apache.org/jira/browse/CALCITE-7220";>[CALCITE-7220]
+   * RelToSqlConverter throws exception for UPDATE with self-referencing 
column in SET</a>.
+   */
+  @Test void testUpdateSetWithColumnReference() {
+    final String sql0 = "update \"foodmart\".\"product\" "
+        + "set \"product_name\" = \"product_name\" || '_'\n"
+        + "where \"product_id\" > 10";
+    final String expected0 = "UPDATE \"foodmart\".\"product\" SET 
\"product_name\" = CAST"
+        + "(\"product_name\" || '_' AS VARCHAR(60) CHARACTER SET 
\"ISO-8859-1\")\nWHERE "
+        + "\"product_id\" > 10";
+    sql(sql0).ok(expected0);
+
+    final String sql1 = "update \"foodmart\".\"product\""
+        + "set \"product_id\" = \"product_id\" + char_length(\"product_name\")"
+        + "where \"product_id\" > 10";
+    final String expected1 = "UPDATE \"foodmart\".\"product\" SET 
\"product_id\" = \"product_id\""
+        + " + CHAR_LENGTH(\"product_name\")\nWHERE \"product_id\" > 10";
+    sql(sql1).ok(expected1);
+
+    final String sql2 = "update \"foodmart\".\"product\""
+        + "set\n"
+        + "   \"product_id\" = \"product_id\" + 
char_length(\"product_name\"),\n"
+        + "   \"product_name\" = \"product_name\" || '_' \n"
+        + "where \"product_id\" > 10";
+    final String expected2 = "UPDATE \"foodmart\".\"product\" SET 
\"product_id\" = \"product_id\""
+        + " + CHAR_LENGTH(\"product_name\"), \"product_name\" = 
CAST(\"product_name\" || '_' AS "
+        + "VARCHAR(60) CHARACTER SET \"ISO-8859-1\")\nWHERE \"product_id\" > 
10";
+    sql(sql2).ok(expected2);
+
+    final String sql3 = "update \"foodmart\".\"product\"\n"
+        + "set \"product_name\" = 'calcite'\n"
+        + "where \"product_id\" in (\n"
+        + "   select \"product_id\" from \"foodmart\".\"product\" where 
\"product_id\" < 10"
+        + ")";
+    final String expected3 = "UPDATE \"foodmart\".\"product\" SET 
\"product_name\" = "
+        + "'calcite'\nWHERE \"product_id\" IN (SELECT \"product_id\"\nFROM 
\"foodmart\""
+        + ".\"product\"\nWHERE \"product_id\" < 10)";
+    sql(sql3).ok(expected3);
+  }
+
+  /**
+   * Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-5122";>[CALCITE-5122]
+   * Update query with correlated throws AssertionError "field ordinal 0 out 
of range"</a>.
+   */
+  @Test void testUpdateWithCorrelatedInSet() {
+    final String sql0 = "update \"foodmart\".\"product\" a set \"product_id\" 
= "
+        + "(select \"product_class_id\" from \"foodmart\".\"product_class\" b "
+        + "where a.\"product_class_id\" = b.\"product_class_id\")";
+    final String expected0 = "UPDATE \"foodmart\".\"product\" SET 
\"product_id\" = (((SELECT "
+        + "\"product_class_id\"\nFROM \"foodmart\".\"product_class\"\nWHERE 
\"product\""
+        + ".\"product_class_id\" = \"product_class_id\")))";
+    sql(sql0).ok(expected0);
+
+    final String sql1 = "update \"foodmart\".\"product\" a set \"brand_name\" 
= "
+        + "(select cast(\"product_category\" as varchar(60)) from 
\"foodmart\".\"product_class\" b "
+        + "where a.\"product_class_id\" = b.\"product_class_id\")";
+    final String expected1 = "UPDATE \"foodmart\".\"product\" SET 
\"brand_name\" = (((SELECT CAST"
+        + "(\"product_category\" AS VARCHAR(60) CHARACTER SET 
\"ISO-8859-1\")\nFROM \"foodmart\""
+        + ".\"product_class\"\nWHERE \"product\".\"product_class_id\" = 
\"product_class_id\")))";
+    sql(sql1).ok(expected1);
+
+    final String sql2 = "update \"foodmart\".\"product\"\n"
+        + "set \"product_name\" = 'calcite'\n"
+        + "where \"product_id\" in (\n"
+        + "   select \"product_id\" from \"foodmart\".\"product\" where 
\"product_id\" < 10"
+        + ")";
+    final String expected2 = "UPDATE \"foodmart\".\"product\" SET 
\"product_name\" = "
+        + "'calcite'\nWHERE \"product_id\" IN (SELECT \"product_id\"\nFROM 
\"foodmart\""
+        + ".\"product\"\nWHERE \"product_id\" < 10)";
+    sql(sql2).ok(expected2);
+  }
+
   /** Test case for
    * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6116";>[CALCITE-6116]
    * Add EXISTS function (enabled in Spark library)</a>. */
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java 
b/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
index 7749bc8831..51e2aca144 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
@@ -1292,7 +1292,7 @@ private LockWrapper exclusiveCleanDb(Connection c) throws 
SQLException {
             + " SET \"account_id\"=888\n"
             + " WHERE \"store_id\"=666\n";
         final String explain = "PLAN=JdbcToEnumerableConverter\n"
-            + "  JdbcTableModify(table=[[foodmart, expense_fact]], 
operation=[UPDATE], updateColumnList=[[account_id]], 
sourceExpressionList=[[888]], flattened=[false])\n"
+            + "  JdbcTableModify(table=[[foodmart, expense_fact]], 
operation=[UPDATE], updateColumnList=[[account_id]], 
sourceExpressionList=[[$7]], flattened=[false])\n"
             + "    JdbcProject(store_id=[$0], account_id=[$1], exp_date=[$2], 
time_id=[$3], category_id=[$4], currency_id=[$5], amount=[$6], EXPR$0=[888])\n"
             + "      JdbcFilter(condition=[=($0, 666)])\n"
             + "        JdbcTableScan(table=[[foodmart, expense_fact]])";
@@ -1428,14 +1428,14 @@ private LockWrapper exclusiveCleanDb(Connection c) 
throws SQLException {
         + " $f4=['666':VARCHAR(30)], $f5=[666], AMOUNT=[CAST($1):DECIMAL(10, 
4) NOT NULL],"
         + " store_id=[$2],"
         + " account_id=[$3], exp_date=[$4], time_id=[$5], category_id=[$6], 
currency_id=[$7],"
-        + " amount=[$8], AMOUNT0=[$1])\n"
+        + " amount=[$8], AMOUNT0=[CAST($1):DECIMAL(10, 4) NOT NULL])\n"
         + "      JdbcJoin(condition=[=($2, $0)], joinType=[left])\n"
         + "        JdbcValues(tuples=[[{ 666, 42 }]])\n"
         + "        JdbcTableScan(table=[[foodmart, expense_fact]])\n";
     final String jdbcSql = "MERGE INTO \"foodmart\".\"expense_fact\"\n"
         + "USING (VALUES (666, 42)) AS \"t\" (\"STORE_ID\", \"AMOUNT\")\n"
         + "ON \"t\".\"STORE_ID\" = \"expense_fact\".\"store_id\"\n"
-        + "WHEN MATCHED THEN UPDATE SET \"amount\" = \"t\".\"AMOUNT\"\n"
+        + "WHEN MATCHED THEN UPDATE SET \"amount\" = CAST(\"t\".\"AMOUNT\" AS 
DECIMAL(10, 4))\n"
         + "WHEN NOT MATCHED THEN INSERT (\"store_id\", \"account_id\", 
\"exp_date\", \"time_id\", "
         + "\"category_id\", \"currency_id\", \"amount\") VALUES 
\"t\".\"STORE_ID\",\n"
         + "666,\nTIMESTAMP '1997-01-01 00:00:00',\n666,\n'666',\n666,\n"
diff --git 
a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml 
b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index 2b70fa7381..462dab0a57 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -5163,7 +5163,7 @@ values(t.empno, t.ename, 10, t.sal * .15)]]>
     <Resource name="plan">
       <![CDATA[
 LogicalTableModify(table=[[CATALOG, SALES, EMPNULLABLES]], operation=[MERGE], 
updateColumnList=[[ENAME, DEPTNO, SAL]], flattened=[true])
-  LogicalProject(EMPNO=[$0], ENAME=[$1], $f2=[null:VARCHAR(10)], 
$f3=[null:INTEGER], $f4=[null:TIMESTAMP(0)], $f5=[CAST(*($5, 0.15:DECIMAL(2, 
2))):INTEGER NOT NULL], $f6=[null:INTEGER], $f7=[10], $f8=[null:BOOLEAN], 
EMPNO0=[$9], ENAME0=[$10], JOB0=[$11], MGR0=[$12], HIREDATE0=[$13], SAL0=[$14], 
COMM0=[$15], DEPTNO0=[$16], SLACKER0=[$17], ENAME1=[$1], DEPTNO=[$7], 
$f20=[*($5, 0.1:DECIMAL(1, 1))])
+  LogicalProject(EMPNO=[$0], ENAME=[$1], $f2=[null:VARCHAR(10)], 
$f3=[null:INTEGER], $f4=[null:TIMESTAMP(0)], $f5=[CAST(*($5, 0.15:DECIMAL(2, 
2))):INTEGER NOT NULL], $f6=[null:INTEGER], $f7=[10], $f8=[null:BOOLEAN], 
EMPNO0=[$9], ENAME0=[$10], JOB0=[$11], MGR0=[$12], HIREDATE0=[$13], SAL0=[$14], 
COMM0=[$15], DEPTNO0=[$16], SLACKER0=[$17], ENAME1=[$1], DEPTNO=[$7], 
$f20=[CAST(*($5, 0.1:DECIMAL(1, 1))):INTEGER NOT NULL])
     LogicalJoin(condition=[=($9, $0)], joinType=[left])
       LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], 
HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
         LogicalFilter(condition=[IS NULL($7)])
@@ -9419,7 +9419,7 @@ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], 
MGR=[$3], HIREDATE=[$4], DEPTNO
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[UPDATE], 
updateColumnList=[[EMPNO]], sourceExpressionList=[[+($0, 1)]], flattened=[true])
+LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[UPDATE], 
updateColumnList=[[EMPNO]], sourceExpressionList=[[$9]], flattened=[true])
   LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], EXPR$0=[+($0, 1)])
     LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
@@ -9431,7 +9431,7 @@ LogicalTableModify(table=[[CATALOG, SALES, EMP]], 
operation=[UPDATE], updateColu
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[UPDATE], 
updateColumnList=[[SAL]], sourceExpressionList=[[+($5, ?0)]], flattened=[true])
+LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[UPDATE], 
updateColumnList=[[SAL]], sourceExpressionList=[[$9]], flattened=[true])
   LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], EXPR$0=[+($5, ?0)])
     LogicalFilter(condition=[=($8, false)])
       LogicalTableScan(table=[[CATALOG, SALES, EMP]])
@@ -9444,7 +9444,7 @@ LogicalTableModify(table=[[CATALOG, SALES, EMP]], 
operation=[UPDATE], updateColu
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[UPDATE], 
updateColumnList=[[SAL]], sourceExpressionList=[[?0]], flattened=[true])
+LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[UPDATE], 
updateColumnList=[[SAL]], sourceExpressionList=[[$9]], flattened=[true])
   LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], EXPR$0=[?0])
     LogicalFilter(condition=[=($8, false)])
       LogicalTableScan(table=[[CATALOG, SALES, EMP]])
@@ -9457,8 +9457,8 @@ LogicalTableModify(table=[[CATALOG, SALES, EMP]], 
operation=[UPDATE], updateColu
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalTableModify(table=[[CATALOG, SALES, EMPDEFAULTS]], operation=[UPDATE], 
updateColumnList=[[DEPTNO, UPDATED, EMPNO, ENAME]], sourceExpressionList=[[1, 
2017-03-12 13:03:05, 20, 'Bob':VARCHAR(20)]], flattened=[true])
-  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], UPDATED=[$9], EXPR$0=[1], 
EXPR$1=[2017-03-12 13:03:05], EXPR$2=[20], EXPR$3=['Bob'])
+LogicalTableModify(table=[[CATALOG, SALES, EMPDEFAULTS]], operation=[UPDATE], 
updateColumnList=[[DEPTNO, UPDATED, EMPNO, ENAME]], sourceExpressionList=[[$10, 
$11, $12, $13]], flattened=[true])
+  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], UPDATED=[$9], EXPR$0=[1], 
EXPR$1=[2017-03-12 13:03:05], EXPR$2=[20], EXPR$3=['Bob':VARCHAR(20)])
     LogicalFilter(condition=[=($7, 10)])
       LogicalTableScan(table=[[CATALOG, SALES, EMPDEFAULTS]])
 ]]>
@@ -9470,7 +9470,7 @@ LogicalTableModify(table=[[CATALOG, SALES, EMPDEFAULTS]], 
operation=[UPDATE], up
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalTableModify(table=[[CATALOG, SALES, EMPDEFAULTS]], operation=[UPDATE], 
updateColumnList=[[DEPTNO, slacker]], sourceExpressionList=[[1, 100]], 
flattened=[true])
+LogicalTableModify(table=[[CATALOG, SALES, EMPDEFAULTS]], operation=[UPDATE], 
updateColumnList=[[DEPTNO, slacker]], sourceExpressionList=[[$10, $11]], 
flattened=[true])
   LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], slacker=[$9], EXPR$0=[1], 
EXPR$1=[100])
     LogicalFilter(condition=[=($1, 'Bob')])
       LogicalTableScan(table=[[CATALOG, SALES, EMPDEFAULTS]])
@@ -9483,8 +9483,8 @@ LogicalTableModify(table=[[CATALOG, SALES, EMPDEFAULTS]], 
operation=[UPDATE], up
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalTableModify(table=[[CATALOG, SALES, EMPDEFAULTS]], operation=[UPDATE], 
updateColumnList=[[DEPTNO, EMPNO, ENAME]], sourceExpressionList=[[1, 20, 
'Bob':VARCHAR(20)]], flattened=[true])
-  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], EXPR$0=[1], EXPR$1=[20], 
EXPR$2=['Bob'])
+LogicalTableModify(table=[[CATALOG, SALES, EMPDEFAULTS]], operation=[UPDATE], 
updateColumnList=[[DEPTNO, EMPNO, ENAME]], sourceExpressionList=[[$9, $10, 
$11]], flattened=[true])
+  LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], EXPR$0=[1], EXPR$1=[20], 
EXPR$2=['Bob':VARCHAR(20)])
     LogicalFilter(condition=[=($7, 10)])
       LogicalTableScan(table=[[CATALOG, SALES, EMPDEFAULTS]])
 ]]>
@@ -9498,7 +9498,7 @@ where slacker = false]]>
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalTableModify(table=[[CATALOG, SALES, EMP_MODIFIABLEVIEW2]], 
operation=[UPDATE], updateColumnList=[[UPDATED, SAL]], 
sourceExpressionList=[[2017-03-12 13:03:05, +($5, 5000)]], flattened=[true])
+LogicalTableModify(table=[[CATALOG, SALES, EMP_MODIFIABLEVIEW2]], 
operation=[UPDATE], updateColumnList=[[UPDATED, SAL]], 
sourceExpressionList=[[$11, $12]], flattened=[true])
   LogicalProject(ENAME=[$0], EMPNO=[$1], JOB=[$2], DEPTNO=[$3], SLACKER=[$4], 
SAL=[$5], EXTRA=[$6], HIREDATE=[$7], MGR=[$8], COMM=[$9], UPDATED=[$10], 
EXPR$0=[2017-03-12 13:03:05], EXPR$1=[+($5, 5000)])
     LogicalFilter(condition=[=($4, false)])
       LogicalTableScan(table=[[CATALOG, SALES, EMP_MODIFIABLEVIEW2]])
@@ -9511,7 +9511,7 @@ LogicalTableModify(table=[[CATALOG, SALES, 
EMP_MODIFIABLEVIEW2]], operation=[UPD
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalTableModify(table=[[CATALOG, SALES, EMP_MODIFIABLEVIEW2]], 
operation=[UPDATE], updateColumnList=[[DEPTNO, slacker]], 
sourceExpressionList=[[20, 100]], flattened=[true])
+LogicalTableModify(table=[[CATALOG, SALES, EMP_MODIFIABLEVIEW2]], 
operation=[UPDATE], updateColumnList=[[DEPTNO, slacker]], 
sourceExpressionList=[[$11, $12]], flattened=[true])
   LogicalProject(ENAME=[$0], EMPNO=[$1], JOB=[$2], DEPTNO=[$3], SLACKER=[$4], 
SAL=[$5], EXTRA=[$6], HIREDATE=[$7], MGR=[$8], COMM=[$9], slacker=[$10], 
EXPR$0=[20], EXPR$1=[100])
     LogicalFilter(condition=[=($0, 'Bob')])
       LogicalTableScan(table=[[CATALOG, SALES, EMP_MODIFIABLEVIEW2]])
@@ -9524,7 +9524,7 @@ LogicalTableModify(table=[[CATALOG, SALES, 
EMP_MODIFIABLEVIEW2]], operation=[UPD
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalTableModify(table=[[CATALOG, SALES, EMP_MODIFIABLEVIEW3]], 
operation=[UPDATE], updateColumnList=[[DEPTNO, EMPNO, ENAME]], 
sourceExpressionList=[[20, 20, 'Bob']], flattened=[true])
+LogicalTableModify(table=[[CATALOG, SALES, EMP_MODIFIABLEVIEW3]], 
operation=[UPDATE], updateColumnList=[[DEPTNO, EMPNO, ENAME]], 
sourceExpressionList=[[$8, $9, $10]], flattened=[true])
   LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], SLACKER=[$6], DEPTNO=[$7], EXPR$0=[20], EXPR$1=[20], EXPR$2=['Bob'])
     LogicalFilter(condition=[=($0, 10)])
       LogicalTableScan(table=[[CATALOG, SALES, EMP_MODIFIABLEVIEW3]])
@@ -9537,7 +9537,7 @@ LogicalTableModify(table=[[CATALOG, SALES, 
EMP_MODIFIABLEVIEW3]], operation=[UPD
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalTableModify(table=[[CATALOG, SALES, EMP_MODIFIABLEVIEW2]], 
operation=[UPDATE], updateColumnList=[[DEPTNO, extra, EXTRA]], 
sourceExpressionList=[[20, 100, true]], flattened=[true])
+LogicalTableModify(table=[[CATALOG, SALES, EMP_MODIFIABLEVIEW2]], 
operation=[UPDATE], updateColumnList=[[DEPTNO, extra, EXTRA]], 
sourceExpressionList=[[$11, $12, $13]], flattened=[true])
   LogicalProject(ENAME=[$0], EMPNO=[$1], JOB=[$2], DEPTNO=[$3], SLACKER=[$4], 
SAL=[$5], EXTRA=[$6], HIREDATE=[$7], MGR=[$8], COMM=[$9], extra=[$10], 
EXPR$0=[20], EXPR$1=[100], EXPR$2=[true])
     LogicalFilter(condition=[=($0, 'Bob')])
       LogicalTableScan(table=[[CATALOG, SALES, EMP_MODIFIABLEVIEW2]])
@@ -9550,7 +9550,7 @@ LogicalTableModify(table=[[CATALOG, SALES, 
EMP_MODIFIABLEVIEW2]], operation=[UPD
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalTableModify(table=[[CATALOG, SALES, EMP_MODIFIABLEVIEW2]], 
operation=[UPDATE], updateColumnList=[[DEPTNO, slacker, EXTRA]], 
sourceExpressionList=[[20, 100, true]], flattened=[true])
+LogicalTableModify(table=[[CATALOG, SALES, EMP_MODIFIABLEVIEW2]], 
operation=[UPDATE], updateColumnList=[[DEPTNO, slacker, EXTRA]], 
sourceExpressionList=[[$11, $12, $13]], flattened=[true])
   LogicalProject(ENAME=[$0], EMPNO=[$1], JOB=[$2], DEPTNO=[$3], SLACKER=[$4], 
SAL=[$5], EXTRA=[$6], HIREDATE=[$7], MGR=[$8], COMM=[$9], slacker=[$10], 
EXPR$0=[20], EXPR$1=[100], EXPR$2=[true])
     LogicalFilter(condition=[=($0, 'Bob')])
       LogicalTableScan(table=[[CATALOG, SALES, EMP_MODIFIABLEVIEW2]])
@@ -9563,7 +9563,7 @@ LogicalTableModify(table=[[CATALOG, SALES, 
EMP_MODIFIABLEVIEW2]], operation=[UPD
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalTableModify(table=[[CATALOG, SALES, EMP_MODIFIABLEVIEW3]], 
operation=[UPDATE], updateColumnList=[[EMPNO, COMM, EXTRA]], 
sourceExpressionList=[[20, 123, true]], flattened=[true])
+LogicalTableModify(table=[[CATALOG, SALES, EMP_MODIFIABLEVIEW3]], 
operation=[UPDATE], updateColumnList=[[EMPNO, COMM, EXTRA]], 
sourceExpressionList=[[$9, $10, $11]], flattened=[true])
   LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], SLACKER=[$6], EXTRA=[$7], COMM=[$8], EXPR$0=[20], EXPR$1=[123], 
EXPR$2=[true])
     LogicalFilter(condition=[=($1, 'Bob')])
       LogicalTableScan(table=[[CATALOG, SALES, EMP_MODIFIABLEVIEW3]])
@@ -9577,7 +9577,7 @@ set sal = sal + 5000 where slacker = false]]>
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalTableModify(table=[[CATALOG, SALES, EMP_MODIFIABLEVIEW2]], 
operation=[UPDATE], updateColumnList=[[SAL]], sourceExpressionList=[[+($5, 
5000)]], flattened=[true])
+LogicalTableModify(table=[[CATALOG, SALES, EMP_MODIFIABLEVIEW2]], 
operation=[UPDATE], updateColumnList=[[SAL]], sourceExpressionList=[[$10]], 
flattened=[true])
   LogicalProject(ENAME=[$0], EMPNO=[$1], JOB=[$2], DEPTNO=[$3], SLACKER=[$4], 
SAL=[$5], EXTRA=[$6], HIREDATE=[$7], MGR=[$8], COMM=[$9], EXPR$0=[+($5, 5000)])
     LogicalFilter(condition=[=($4, false)])
       LogicalTableScan(table=[[CATALOG, SALES, EMP_MODIFIABLEVIEW2]])
@@ -9592,7 +9592,7 @@ set empno = (
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[UPDATE], 
updateColumnList=[[EMPNO]], sourceExpressionList=[[$0]], flattened=[true])
+LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[UPDATE], 
updateColumnList=[[EMPNO]], sourceExpressionList=[[$9]], flattened=[true])
   LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], EXPR$0=[$10])
     LogicalJoin(condition=[=($7, $9)], joinType=[left])
       LogicalTableScan(table=[[CATALOG, SALES, EMP]])
@@ -9610,7 +9610,7 @@ set empno = 1 where empno in (
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[UPDATE], 
updateColumnList=[[EMPNO]], sourceExpressionList=[[1]], flattened=[true])
+LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[UPDATE], 
updateColumnList=[[EMPNO]], sourceExpressionList=[[$9]], flattened=[true])
   LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], EXPR$0=[1])
     LogicalJoin(condition=[=($0, $9)], joinType=[inner])
       LogicalTableScan(table=[[CATALOG, SALES, EMP]])
@@ -9629,7 +9629,7 @@ set empno = 1 where emp.empno in (
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[UPDATE], 
updateColumnList=[[EMPNO]], sourceExpressionList=[[1]], flattened=[true])
+LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[UPDATE], 
updateColumnList=[[EMPNO]], sourceExpressionList=[[$9]], flattened=[true])
   LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], EXPR$0=[1])
     LogicalJoin(condition=[=($0, $9)], joinType=[inner])
       LogicalTableScan(table=[[CATALOG, SALES, EMP]])
@@ -9648,7 +9648,7 @@ set empno = 1 where empno not in (
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[UPDATE], 
updateColumnList=[[EMPNO]], sourceExpressionList=[[1]], flattened=[true])
+LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[UPDATE], 
updateColumnList=[[EMPNO]], sourceExpressionList=[[$9]], flattened=[true])
   LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], EXPR$0=[1])
     LogicalFilter(condition=[NOT(AND(IS TRUE($11), IS NOT NULL($9)))])
       LogicalJoin(condition=[=($9, $10)], joinType=[left])
@@ -9667,7 +9667,7 @@ LogicalTableModify(table=[[CATALOG, SALES, EMP]], 
operation=[UPDATE], updateColu
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[UPDATE], 
updateColumnList=[[EMPNO]], sourceExpressionList=[[+($0, 1)]], flattened=[true])
+LogicalTableModify(table=[[CATALOG, SALES, EMP]], operation=[UPDATE], 
updateColumnList=[[EMPNO]], sourceExpressionList=[[$9]], flattened=[true])
   LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], 
SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8], EXPR$0=[+($0, 1)])
     LogicalFilter(condition=[=($7, 10)])
       LogicalTableScan(table=[[CATALOG, SALES, EMP]])
@@ -9680,7 +9680,7 @@ LogicalTableModify(table=[[CATALOG, SALES, EMP]], 
operation=[UPDATE], updateColu
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalTableModify(table=[[CATALOG, STRUCT, T]], operation=[UPDATE], 
updateColumnList=[["F0"."C0"]], sourceExpressionList=[[+($4, 1)]], 
flattened=[true])
+LogicalTableModify(table=[[CATALOG, STRUCT, T]], operation=[UPDATE], 
updateColumnList=[["F0"."C0"]], sourceExpressionList=[[$9]], flattened=[true])
   LogicalProject("K0"=[$0], "C1"=[$1], "F1"."A0"=[$2], "F2"."A0"=[$3], 
"F0"."C0"=[$4], "F1"."C0"=[$5], "F0"."C1"=[$6], "F1"."C2"=[$7], "F2"."C3"=[$8], 
EXPR$0=[+($4, 1)])
     LogicalTableScan(table=[[CATALOG, STRUCT, T]])
 ]]>
diff --git 
a/core/src/test/resources/org/apache/calcite/test/TypeCoercionConverterTest.xml 
b/core/src/test/resources/org/apache/calcite/test/TypeCoercionConverterTest.xml
index ee08be5b56..8e9d0207ca 100644
--- 
a/core/src/test/resources/org/apache/calcite/test/TypeCoercionConverterTest.xml
+++ 
b/core/src/test/resources/org/apache/calcite/test/TypeCoercionConverterTest.xml
@@ -281,8 +281,8 @@ LogicalUnion(all=[false])
     </Resource>
     <Resource name="plan">
       <![CDATA[
-LogicalTableModify(table=[[CATALOG, SALES, T1]], operation=[UPDATE], 
updateColumnList=[[t1_varchar20, t1_date, t1_int]], 
sourceExpressionList=[[CAST(123):VARCHAR(20) NOT NULL, CAST(2020-01-03 
10:14:34):DATE NOT NULL, CAST(12.3:DECIMAL(3, 1)):INTEGER NOT NULL]], 
flattened=[false])
-  LogicalProject(t1_varchar20=[$0], t1_smallint=[$1], t1_int=[$2], 
t1_bigint=[$3], t1_real=[$4], t1_double=[$5], t1_decimal=[$6], 
t1_timestamp=[$7], t1_date=[$8], t1_binary=[$9], t1_boolean=[$10], 
EXPR$0=[123], EXPR$1=[2020-01-03 10:14:34], EXPR$2=[12.3:DECIMAL(3, 1)])
+LogicalTableModify(table=[[CATALOG, SALES, T1]], operation=[UPDATE], 
updateColumnList=[[t1_varchar20, t1_date, t1_int]], sourceExpressionList=[[$11, 
$12, $13]], flattened=[false])
+  LogicalProject(t1_varchar20=[$0], t1_smallint=[$1], t1_int=[$2], 
t1_bigint=[$3], t1_real=[$4], t1_double=[$5], t1_decimal=[$6], 
t1_timestamp=[$7], t1_date=[$8], t1_binary=[$9], t1_boolean=[$10], 
EXPR$0=['123':VARCHAR(20)], EXPR$1=[2020-01-03], EXPR$2=[12])
     LogicalTableScan(table=[[CATALOG, SALES, T1]])
 ]]>
     </Resource>


Reply via email to