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>