This is an automated email from the ASF dual-hosted git repository.
mihaibudiu 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 9acb3745c6 [CALCITE-7607] DML type coercion inserts implicit narrowing
casts for target-column assignments
9acb3745c6 is described below
commit 9acb3745c61db9bb01a7427df64cd5d2a0834a96
Author: zzwqqq <[email protected]>
AuthorDate: Tue Jun 23 17:05:50 2026 +0800
[CALCITE-7607] DML type coercion inserts implicit narrowing casts for
target-column assignments
---
.../org/apache/calcite/rel/core/TableModify.java | 30 ++++++++++++++
.../sql/validate/implicit/TypeCoercionImpl.java | 8 +++-
.../calcite/rel/rel2sql/RelToSqlConverterTest.java | 21 +++++-----
.../org/apache/calcite/test/JdbcAdapterTest.java | 8 ++--
.../java/org/apache/calcite/test/JdbcTest.java | 2 +-
.../org/apache/calcite/test/RelOptRulesTest.java | 2 +-
.../calcite/test/TypeCoercionConverterTest.java | 32 +++++++++++++++
.../org/apache/calcite/test/RelOptRulesTest.xml | 4 +-
.../apache/calcite/test/SqlToRelConverterTest.xml | 4 +-
.../calcite/test/TypeCoercionConverterTest.xml | 47 ++++++++++++++++++++--
server/src/test/resources/sql/materialized_view.iq | 26 ++++++------
server/src/test/resources/sql/table_as.iq | 28 ++++++-------
12 files changed, 160 insertions(+), 52 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 60b9814dfd..83dd810647 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
@@ -32,7 +32,9 @@
import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexUtil;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.type.SqlTypeUtil;
@@ -261,9 +263,37 @@ public boolean isMerge() {
null);
}
+ inputRowType = expectedInputRowTypeForAssignment(inputRowType);
return inputRowType;
}
+ private RelDataType expectedInputRowTypeForAssignment(
+ RelDataType expectedRowType) {
+ final RelDataType actualRowType = getInput().getRowType();
+ if (actualRowType.getFieldCount() != expectedRowType.getFieldCount()) {
+ return expectedRowType;
+ }
+ final RelDataTypeFactory typeFactory = getCluster().getTypeFactory();
+ final RelDataTypeFactory.Builder builder = typeFactory.builder();
+ boolean changed = false;
+ final List<RelDataTypeField> expectedFields =
expectedRowType.getFieldList();
+ final List<RelDataTypeField> actualFields = actualRowType.getFieldList();
+ for (int i = 0; i < expectedFields.size(); i++) {
+ final RelDataTypeField expectedField = expectedFields.get(i);
+ final RelDataType actualType = actualFields.get(i).getType();
+ final RelDataType expectedType = expectedField.getType();
+ if (!SqlTypeUtil.equalSansNullability(typeFactory, actualType,
expectedType)
+ && SqlTypeUtil.canAssignFrom(expectedType, actualType)
+ && !RexUtil.isLosslessCast(actualType, expectedType)) {
+ builder.add(expectedField.getName(), actualType);
+ changed = true;
+ } else {
+ builder.add(expectedField);
+ }
+ }
+ return changed ? builder.build() : expectedRowType;
+ }
+
@Override public RelWriter explainTerms(RelWriter pw) {
return super.explainTerms(pw)
.item("table", table.getQualifiedName())
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 26812be961..46dc3af5ed 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
@@ -19,6 +19,7 @@
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rex.RexUtil;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlCallBinding;
import org.apache.calcite.sql.SqlFunction;
@@ -830,8 +831,13 @@ && coerceOperandType(binding.getScope(),
binding.getCall(), i, implicitType)
}
boolean coerced = false;
for (int i = 0; i < sourceFields.size(); i++) {
+ RelDataType sourceType = sourceFields.get(i).getType();
RelDataType targetType = targetFields.get(i).getType();
- coerced = coerceSourceRowType(scope, query, i, targetType) || coerced;
+ if (!SqlTypeUtil.equalSansNullability(validator.getTypeFactory(),
sourceType, targetType)
+ && (RexUtil.isLosslessCast(sourceType, targetType)
+ || !SqlTypeUtil.canAssignFrom(targetType, sourceType))) {
+ coerced = coerceSourceRowType(scope, query, i, targetType) || coerced;
+ }
}
return coerced;
}
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 f056ee9d4f..2ac820e838 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
@@ -10105,7 +10105,7 @@ private void checkLiteral2(String expression, String
expected) {
+ "ON \"DEPT\".\"DEPTNO\" = \"DEPT0\".\"DEPTNO\"\n"
+ "WHEN MATCHED THEN UPDATE SET \"DNAME\" = \"DEPT\".\"DNAME\"\n"
+ "WHEN NOT MATCHED THEN INSERT (\"DEPTNO\", \"DNAME\", \"LOC\") "
- + "VALUES CAST(\"DEPT\".\"DEPTNO\" + 1 AS TINYINT),\n"
+ + "VALUES \"DEPT\".\"DEPTNO\" + 1,\n"
+ "LOWER(\"DEPT\".\"DNAME\"),\n"
+ "UPPER(\"DEPT\".\"LOC\")";
sql(sql1)
@@ -10139,9 +10139,9 @@ private void checkLiteral2(String expression, String
expected) {
+ "ON \"DEPT\".\"DEPTNO\" = \"DEPT0\".\"DEPTNO\"\n"
+ "WHEN MATCHED THEN UPDATE SET \"DNAME\" = \"DEPT\".\"DNAME\"\n"
+ "WHEN NOT MATCHED THEN INSERT (\"DEPTNO\", \"DNAME\", \"LOC\") "
- + "VALUES CAST(\"DEPT\".\"DEPTNO\" + 1 AS TINYINT),\n"
+ + "VALUES \"DEPT\".\"DEPTNO\" + 1,\n"
+ "'abc',\n"
- + "CAST(LOWER(\"DEPT\".\"DNAME\") AS VARCHAR(13) CHARACTER SET
\"ISO-8859-1\")";
+ + "LOWER(\"DEPT\".\"DNAME\")";
sql(sql3)
.schema(CalciteAssert.SchemaSpec.JDBC_SCOTT)
.ok(expected3);
@@ -10171,7 +10171,7 @@ private void checkLiteral2(String expression, String
expected) {
+ "USING \"SCOTT\".\"DEPT\"\n"
+ "ON \"DEPT\".\"DEPTNO\" = \"DEPT0\".\"DEPTNO\"\n"
+ "WHEN NOT MATCHED THEN INSERT (\"DEPTNO\", \"DNAME\", \"LOC\") "
- + "VALUES CAST(\"DEPT\".\"DEPTNO\" + 1 AS TINYINT),\n"
+ + "VALUES \"DEPT\".\"DEPTNO\" + 1,\n"
+ "LOWER(\"DEPT\".\"DNAME\"),\n"
+ "UPPER(\"DEPT\".\"LOC\")";
sql(sql5)
@@ -10191,7 +10191,7 @@ private void checkLiteral2(String expression, String
expected) {
+ "WHERE CAST(\"DEPTNO\" AS INTEGER) <> 5) AS \"t0\"\n"
+ "ON \"t0\".\"DEPTNO\" = \"DEPT0\".\"DEPTNO\"\n"
+ "WHEN NOT MATCHED THEN INSERT (\"DEPTNO\", \"DNAME\", \"LOC\") "
- + "VALUES CAST(\"t0\".\"DEPTNO\" + 1 AS TINYINT),\n"
+ + "VALUES \"t0\".\"DEPTNO\" + 1,\n"
+ "LOWER(\"t0\".\"DNAME\"),\n"
+ "UPPER(\"t0\".\"LOC\")";
sql(sql6)
@@ -10213,7 +10213,7 @@ private void checkLiteral2(String expression, String
expected) {
+ "ON \"t0\".\"EXPR$0\" = \"t1\".\"DEPTNO0\"\n"
+ "WHEN MATCHED THEN UPDATE SET \"DNAME\" = 'abc'\n"
+ "WHEN NOT MATCHED THEN INSERT (\"DEPTNO\", \"DNAME\", \"LOC\") "
- + "VALUES CAST(\"t0\".\"EXPR$0\" + 1 AS TINYINT),\n"
+ + "VALUES \"t0\".\"EXPR$0\" + 1,\n"
+ "CAST(LOWER(\"t0\".\"EXPR$1\") AS VARCHAR(14) CHARACTER SET
\"ISO-8859-1\"),\n"
+ "CAST(UPPER(\"t0\".\"EXPR$2\") AS VARCHAR(13) CHARACTER SET
\"ISO-8859-1\")";
sql(sql7)
@@ -10402,9 +10402,8 @@ private void checkLiteral2(String expression, String
expected) {
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";
+ final String expected0 = "UPDATE \"foodmart\".\"product\" SET
\"product_name\" = "
+ + "\"product_name\" || '_'\nWHERE \"product_id\" > 10";
sql(sql0).ok(expected0);
final String sql1 = "update \"foodmart\".\"product\""
@@ -10420,8 +10419,8 @@ private void checkLiteral2(String expression, String
expected) {
+ " \"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";
+ + " + CHAR_LENGTH(\"product_name\"), \"product_name\" =
\"product_name\" || '_'"
+ + "\nWHERE \"product_id\" > 10";
sql(sql2).ok(expected2);
final String sql3 = "update \"foodmart\".\"product\"\n"
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 7bed49bb10..5ecf63c5ba 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
@@ -1465,21 +1465,21 @@ private LockWrapper exclusiveCleanDb(Connection c)
throws SQLException {
+ " JdbcTableModify(table=[[foodmart, expense_fact]],
operation=[MERGE],"
+ " updateColumnList=[[amount]], flattened=[false])\n"
+ " JdbcProject(STORE_ID=[$0], $f1=[666], $f2=[1997-01-01
00:00:00], $f3=[666],"
- + " $f4=['666':VARCHAR(30)], $f5=[666], AMOUNT=[CAST($1):DECIMAL(10,
4) NOT NULL],"
+ + " $f4=['666':VARCHAR(30)], $f5=[666], AMOUNT=[$1],"
+ " store_id=[$2],"
+ " account_id=[$3], exp_date=[$4], time_id=[$5], category_id=[$6],
currency_id=[$7],"
- + " amount=[$8], AMOUNT0=[CAST($1):DECIMAL(10, 4) NOT NULL])\n"
+ + " amount=[$8], AMOUNT0=[$1])\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\" = CAST(\"t\".\"AMOUNT\" AS
DECIMAL(10, 4))\n"
+ + "WHEN MATCHED THEN UPDATE SET \"amount\" = \"t\".\"AMOUNT\"\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"
- + "CAST(\"t\".\"AMOUNT\" AS DECIMAL(10, 4))";
+ + "\"t\".\"AMOUNT\"";
final AssertThat that =
CalciteAssert.model(FoodmartSchema.FOODMART_MODEL)
.enable(CalciteAssert.DB == DatabaseInstance.HSQLDB);
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index 0c5ab19b2e..83ce5b1d78 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -390,7 +390,7 @@ static void forEachExpand(Runnable r) {
+ "expr#7=[null:JavaType(class java.lang.Integer)], "
+ "empid=[$t3], deptno=[$t4], name=[$t5], salary=[$t6], "
+ "commission=[$t7])\n"
- + " EnumerableValues(tuples=[[{ 'Fred', 56,
123.4000015258789E0 }]])\n";
+ + " EnumerableValues(tuples=[[{ 'Fred', 56, 123.4 }]])\n";
assertThat(resultSet.getString(1), isLinux(expected));
// With named columns
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
index d94ae46526..60a5c56cba 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -6053,7 +6053,7 @@ private void checkEmptyJoin(RelOptFixture f) {
@Test void testReduceCastsNullable() {
HepProgram program = new HepProgramBuilder()
- // Simulate the way INSERT will insert casts to the target types
+ // Simulate the way INSERT can insert casts to the target types.
.addRuleInstance(
CoerceInputsRule.Config.DEFAULT
.withCoerceNames(false)
diff --git
a/core/src/test/java/org/apache/calcite/test/TypeCoercionConverterTest.java
b/core/src/test/java/org/apache/calcite/test/TypeCoercionConverterTest.java
index b31e982671..a12a07d5e5 100644
--- a/core/src/test/java/org/apache/calcite/test/TypeCoercionConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/TypeCoercionConverterTest.java
@@ -236,4 +236,36 @@ public static void checkActualAndReferenceFiles() {
String sql = "select CAST(null AS INTEGER) union select '10'";
sql(sql).ok();
}
+
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-7607">[CALCITE-7607]
+ * Avoid implicit narrowing casts in DML assignment coercion</a>. */
+ @Test void testInsertVarcharNarrowingKeepsSourceType() {
+ final String sql = "insert into t1 select "
+ + "CAST('AVeryLongLongStringValue' AS VARCHAR(64)), "
+ + "t2_smallint, t2_int, t2_bigint,\n"
+ + "t2_real, t2_double, t2_decimal, t2_timestamp, "
+ + "t2_date, t2_binary, t2_boolean from t2";
+ sql(sql).ok();
+ }
+
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-7607">[CALCITE-7607]
+ * Avoid implicit narrowing casts in DML assignment coercion</a>. */
+ @Test void testUpdateVarcharNarrowingKeepsSourceType() {
+ final String sql = "update t1 set t1_varchar20 = "
+ + "CAST('AVeryLongLongStringValue' AS VARCHAR(64))";
+ sql(sql).ok();
+ }
+
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-7607">[CALCITE-7607]
+ * Avoid implicit narrowing casts in DML assignment coercion</a>. */
+ @Test void testMergeVarcharNarrowingKeepsSourceType() {
+ final String sql = "merge into t1\n"
+ + "using t2 on t1.t1_smallint = t2.t2_smallint\n"
+ + "when matched then update set t1_varchar20 = "
+ + "CAST('AVeryLongLongStringValue' AS VARCHAR(64))";
+ sql(sql).ok();
+ }
}
diff --git
a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
index 24d6a16f3f..d7c03e93f5 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -16542,14 +16542,14 @@ select empno, cast(job as varchar(128)) from
sales.empnullables]]>
<Resource name="planBefore">
<![CDATA[
LogicalTableModify(table=[[CATALOG, SALES, DEPT]], operation=[INSERT],
flattened=[false])
- LogicalProject(DEPTNO=[$0], NAME=[$2])
+ LogicalProject(DEPTNO=[$0], NAME=[CAST($2):VARCHAR(128)])
LogicalTableScan(table=[[CATALOG, SALES, EMPNULLABLES]])
]]>
</Resource>
<Resource name="planAfter">
<![CDATA[
LogicalTableModify(table=[[CATALOG, SALES, DEPT]], operation=[INSERT],
flattened=[false])
- LogicalCalc(expr#0..8=[{inputs}], expr#9=[CAST($t2):VARCHAR(10) NOT NULL],
DEPTNO=[$t0], NAME=[$t9])
+ LogicalCalc(expr#0..8=[{inputs}], expr#9=[CAST($t2):VARCHAR(128)],
DEPTNO=[$t0], NAME=[$t9])
LogicalTableScan(table=[[CATALOG, SALES, EMPNULLABLES]])
]]>
</Resource>
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 f2ab8fec37..c36f7ad59b 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -5597,7 +5597,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=[CAST(*($5, 0.1:DECIMAL(1, 1))):INTEGER NOT NULL])
+ LogicalProject(EMPNO=[$0], ENAME=[$1], $f2=[null:VARCHAR(10)],
$f3=[null:INTEGER], $f4=[null:TIMESTAMP(0)], $f5=[*($5, 0.15:DECIMAL(2, 2))],
$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))])
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)])
@@ -7257,7 +7257,7 @@ LogicalTableModify(table=[[CATALOG, SALES, DEPT]],
operation=[INSERT], flattened
LogicalAggregate(group=[{0, 1}])
LogicalProject(EMPNO=[$0], ENAME=[$1])
LogicalFilter(condition=[$2])
- LogicalProject(EMPNO=[$0], ENAME=[CAST($1):VARCHAR(10) NOT NULL],
QualifyExpression=[=(RANK() OVER (PARTITION BY $1 ORDER BY $8 DESC), 1)])
+ LogicalProject(EMPNO=[$0], ENAME=[$1], QualifyExpression=[=(RANK()
OVER (PARTITION BY $1 ORDER BY $8 DESC), 1)])
LogicalFilter(condition=[>($7, 5)])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
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 8e9d0207ca..15512cb615 100644
---
a/core/src/test/resources/org/apache/calcite/test/TypeCoercionConverterTest.xml
+++
b/core/src/test/resources/org/apache/calcite/test/TypeCoercionConverterTest.xml
@@ -140,7 +140,7 @@ t2_double, t2_decimal, t2_int, t2_date, t2_timestamp,
t2_varchar20, t2_int from
<Resource name="plan">
<![CDATA[
LogicalTableModify(table=[[CATALOG, SALES, T1]], operation=[INSERT],
flattened=[false])
- LogicalProject(t1_varchar20=[CAST($1):VARCHAR(20) NOT NULL],
t1_smallint=[CAST($2):SMALLINT NOT NULL], t1_int=[CAST($3):INTEGER NOT NULL],
t1_bigint=[CAST($4):BIGINT NOT NULL], t1_real=[CAST($5):REAL NOT NULL],
t1_double=[CAST($6):DOUBLE NOT NULL], t1_decimal=[CAST($2):DECIMAL(19, 0) NOT
NULL], t1_timestamp=[CAST($8):TIMESTAMP(0) NOT NULL], t1_date=[CAST($7):DATE
NOT NULL], t1_binary=[CAST($0):BINARY(1) NOT NULL], t1_boolean=[<>($2, 0)])
+ LogicalProject(t1_varchar20=[CAST($1):VARCHAR(20) NOT NULL],
t1_smallint=[$2], t1_int=[$3], t1_bigint=[$4], t1_real=[$5], t1_double=[$6],
t1_decimal=[CAST($2):DECIMAL(19, 0) NOT NULL],
t1_timestamp=[CAST($8):TIMESTAMP(0) NOT NULL], t1_date=[CAST($7):DATE NOT
NULL], t1_binary=[CAST($0):BINARY(1) NOT NULL], t1_boolean=[<>($2, 0)])
LogicalTableScan(table=[[CATALOG, SALES, T2]])
]]>
</Resource>
@@ -152,7 +152,7 @@ LogicalTableModify(table=[[CATALOG, SALES, T1]],
operation=[INSERT], flattened=[
<Resource name="plan">
<![CDATA[
LogicalTableModify(table=[[CATALOG, SALES, T1]], operation=[INSERT],
flattened=[false])
- LogicalValues(tuples=[[{ 'a', 1, 1, 0, 0.0E0, 0.0E0, 0, 2021-11-28 00:00:00,
2021-11-28, X'0a', false }, { 'b', 2, 2, 0, 0.0E0, 0.0E0, 0, 2021-11-28
00:00:00, 2021-11-28, X'0a', false }, { 'c', 3, 3, 0, 0.0E0, 0.0E0, 0,
2021-11-28 00:00:00, 2021-11-28, X'0a', false }, { 'd', 4, 4, 0, 0.0E0, 0.0E0,
0, 2021-11-28 00:00:00, 2021-11-28, X'0a', false }, { 'e', 5, 5, 0, 0.0E0,
0.0E0, 0, 2021-11-28 00:00:00, 2021-11-28, X'0a', false }]])
+ LogicalValues(tuples=[[{ 'a', 1, 1.0, 0, 0, 0.0E0, 0, 2021-11-28 00:00:00,
2021-11-28, X'0a', false }, { 'b', 2, 2.0, 0, 0, 0.0E0, 0, 2021-11-28 00:00:00,
2021-11-28, X'0a', false }, { 'c', 3, 3.0, 0, 0, 0.0E0, 0, 2021-11-28 00:00:00,
2021-11-28, X'0a', false }, { 'd', 4, 4.0, 0, 0, 0.0E0, 0, 2021-11-28 00:00:00,
2021-11-28, X'0a', false }, { 'e', 5, 5.0, 0, 0, 0.0E0, 0, 2021-11-28 00:00:00,
2021-11-28, X'0a', false }]])
]]>
</Resource>
</TestCase>
@@ -164,6 +164,19 @@ LogicalTableModify(table=[[CATALOG, SALES, T1]],
operation=[INSERT], flattened=[
<![CDATA[
LogicalTableModify(table=[[CATALOG, SALES, T1]], operation=[INSERT],
flattened=[false])
LogicalValues(tuples=[[{ 'a', 1, 1, 0, 0.0E0, 0.0E0, 0, 2021-11-28 00:00:00,
2021-11-28, X'0a', false }, { 'b', 2, 2, 0, 0.0E0, 0.0E0, 0, 2021-11-28
00:00:00, 2021-11-28, X'0a', false }, { 'c', 3, 3, 0, 0.0E0, 0.0E0, 0,
2021-11-28 00:00:00, 2021-11-28, X'0a', false }, { 'd', 4, 4, 0, 0.0E0, 0.0E0,
0, 2021-11-28 00:00:00, 2021-11-28, X'0a', false }, { 'e', 5, 5, 0, 0.0E0,
0.0E0, 0, 2021-11-28 00:00:00, 2021-11-28, X'0a', false }]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testInsertVarcharNarrowingKeepsSourceType">
+ <Resource name="sql">
+ <![CDATA[insert into t1 select CAST('AVeryLongLongStringValue' AS
VARCHAR(64)), t2_smallint, t2_int, t2_bigint,
+t2_real, t2_double, t2_decimal, t2_timestamp, t2_date, t2_binary, t2_boolean
from t2]]>
+ </Resource>
+ <Resource name="plan">
+ <![CDATA[
+LogicalTableModify(table=[[CATALOG, SALES, T1]], operation=[INSERT],
flattened=[false])
+ LogicalProject(t1_varchar20=['AVeryLongLongStringValue':VARCHAR(64)],
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])
+ LogicalTableScan(table=[[CATALOG, SALES, T2]])
]]>
</Resource>
</TestCase>
@@ -206,6 +219,22 @@ LogicalProject(X=[$0])
LogicalAggregate(group=[{0}])
LogicalProject(X=[$0])
LogicalValues(tuples=[[{ 3 }, { 4 }]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testMergeVarcharNarrowingKeepsSourceType">
+ <Resource name="sql">
+ <![CDATA[merge into t1
+using t2 on t1.t1_smallint = t2.t2_smallint
+when matched then update set t1_varchar20 = CAST('AVeryLongLongStringValue' AS
VARCHAR(64))]]>
+ </Resource>
+ <Resource name="plan">
+ <![CDATA[
+LogicalTableModify(table=[[CATALOG, SALES, T1]], operation=[MERGE],
updateColumnList=[[t1_varchar20]], flattened=[false])
+ LogicalProject(t1_varchar20=[$11], t1_smallint=[$12], t1_int=[$13],
t1_bigint=[$14], t1_real=[$15], t1_double=[$16], t1_decimal=[$17],
t1_timestamp=[$18], t1_date=[$19], t1_binary=[$20], t1_boolean=[$21],
$f11=['AVeryLongLongStringValue':VARCHAR(64)])
+ LogicalJoin(condition=[=($12, $1)], joinType=[inner])
+ LogicalTableScan(table=[[CATALOG, SALES, T2]])
+ LogicalTableScan(table=[[CATALOG, SALES, T1]])
]]>
</Resource>
</TestCase>
@@ -282,7 +311,19 @@ LogicalUnion(all=[false])
<Resource name="plan">
<![CDATA[
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])
+ 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.3:DECIMAL(3, 1)])
+ LogicalTableScan(table=[[CATALOG, SALES, T1]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testUpdateVarcharNarrowingKeepsSourceType">
+ <Resource name="sql">
+ <![CDATA[update t1 set t1_varchar20 = CAST('AVeryLongLongStringValue' AS
VARCHAR(64))]]>
+ </Resource>
+ <Resource name="plan">
+ <![CDATA[
+LogicalTableModify(table=[[CATALOG, SALES, T1]], operation=[UPDATE],
updateColumnList=[[t1_varchar20]], sourceExpressionList=[[$11]],
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=['AVeryLongLongStringValue':VARCHAR(64)])
LogicalTableScan(table=[[CATALOG, SALES, T1]])
]]>
</Resource>
diff --git a/server/src/test/resources/sql/materialized_view.iq
b/server/src/test/resources/sql/materialized_view.iq
index 8a8b12992f..ca18a8ccf6 100644
--- a/server/src/test/resources/sql/materialized_view.iq
+++ b/server/src/test/resources/sql/materialized_view.iq
@@ -19,7 +19,7 @@
!set outputformat mysql
# Create a source table
-create table dept (deptno int not null, name varchar(10));
+create table dept (deptno int not null, name varchar(20));
(0 rows modified)
!update
@@ -39,12 +39,12 @@ select * from dept where deptno > 10;
# Check contents
select * from v;
-+--------+------------+
-| DEPTNO | NAME |
-+--------+------------+
-| 20 | Marketing |
-| 30 | Engineerin |
-+--------+------------+
++--------+-------------+
+| DEPTNO | NAME |
++--------+-------------+
+| 20 | Marketing |
+| 30 | Engineering |
++--------+-------------+
(2 rows)
!ok
@@ -64,12 +64,12 @@ select * from dept where deptno < 30;
# Check contents are unchanged
select * from v;
-+--------+------------+
-| DEPTNO | NAME |
-+--------+------------+
-| 20 | Marketing |
-| 30 | Engineerin |
-+--------+------------+
++--------+-------------+
+| DEPTNO | NAME |
++--------+-------------+
+| 20 | Marketing |
+| 30 | Engineering |
++--------+-------------+
(2 rows)
!ok
diff --git a/server/src/test/resources/sql/table_as.iq
b/server/src/test/resources/sql/table_as.iq
index cf24509914..8e16f4c5a9 100644
--- a/server/src/test/resources/sql/table_as.iq
+++ b/server/src/test/resources/sql/table_as.iq
@@ -19,7 +19,7 @@
!set outputformat mysql
# Create a source table
-create table dept (deptno int not null, name varchar(10));
+create table dept (deptno int not null, name varchar(20));
(0 rows modified)
!update
@@ -37,14 +37,14 @@ select * from dept where deptno > 10;
!update
-# Check contents; "Engineering" is too long for varchar(10)
+# Check contents
select * from d;
-+--------+------------+
-| DEPTNO | NAME |
-+--------+------------+
-| 20 | Marketing |
-| 30 | Engineerin |
-+--------+------------+
++--------+-------------+
+| DEPTNO | NAME |
++--------+-------------+
+| 20 | Marketing |
+| 30 | Engineering |
++--------+-------------+
(2 rows)
!ok
@@ -64,12 +64,12 @@ select * from dept where deptno < 30;
# Check contents are unchanged
select * from d;
-+--------+------------+
-| DEPTNO | NAME |
-+--------+------------+
-| 20 | Marketing |
-| 30 | Engineerin |
-+--------+------------+
++--------+-------------+
+| DEPTNO | NAME |
++--------+-------------+
+| 20 | Marketing |
+| 30 | Engineering |
++--------+-------------+
(2 rows)
!ok