diff --git a/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java b/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java index e0321331a4ac..9f6b4aefac1b 100644 --- a/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java +++ b/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java @@ -3270,14 +3270,14 @@ protected Scope visitUpdate(Update update, Optional scope) .collect(toImmutableMap(ColumnSchema::getName, Function.identity())); for (UpdateAssignment assignment : update.getAssignments()) { - String columnName = assignment.getName().getValue(); + String columnName = assignment.getName().getValue().toLowerCase(ENGLISH); if (!columns.containsKey(columnName)) { throw semanticException(COLUMN_NOT_FOUND, assignment.getName(), "The UPDATE SET target column %s doesn't exist", columnName); } } Set assignmentTargets = update.getAssignments().stream() - .map(assignment -> assignment.getName().getValue()) + .map(assignment -> assignment.getName().getValue().toLowerCase(ENGLISH)) .collect(toImmutableSet()); accessControl.checkCanUpdateTableColumns(session.toSecurityContext(), tableName, assignmentTargets); @@ -3325,7 +3325,7 @@ protected Scope visitUpdate(Update update, Optional scope) List expressionTypes = expressionTypesBuilder.build(); List tableTypes = update.getAssignments().stream() - .map(assignment -> requireNonNull(columns.get(assignment.getName().getValue()))) + .map(assignment -> requireNonNull(columns.get(assignment.getName().getValue().toLowerCase(ENGLISH)))) .map(ColumnSchema::getType) .collect(toImmutableList()); diff --git a/core/trino-main/src/main/java/io/trino/sql/planner/QueryPlanner.java b/core/trino-main/src/main/java/io/trino/sql/planner/QueryPlanner.java index d00f50e6dbb8..68be174117d0 100644 --- a/core/trino-main/src/main/java/io/trino/sql/planner/QueryPlanner.java +++ b/core/trino-main/src/main/java/io/trino/sql/planner/QueryPlanner.java @@ -177,6 +177,7 @@ import static io.trino.type.IntervalDayTimeType.INTERVAL_DAY_TIME; import static io.trino.type.IntervalYearMonthType.INTERVAL_YEAR_MONTH; import static java.lang.String.format; +import static java.util.Locale.ENGLISH; import static java.util.Objects.requireNonNull; class QueryPlanner @@ -600,7 +601,7 @@ public PlanNode plan(Update node) Expression[] orderedColumnValuesArray = new Expression[updatedColumnHandles.size()]; node.getAssignments().forEach(assignment -> { - String name = assignment.getName().getValue(); + String name = assignment.getName().getValue().toLowerCase(ENGLISH); ColumnHandle handle = nameToHandle.get(name); int index = updatedColumnHandles.indexOf(handle); if (index >= 0) { diff --git a/plugin/trino-hive/src/test/java/io/trino/plugin/hive/BaseHiveConnectorTest.java b/plugin/trino-hive/src/test/java/io/trino/plugin/hive/BaseHiveConnectorTest.java index 9e58658d6186..d54ffc30a115 100644 --- a/plugin/trino-hive/src/test/java/io/trino/plugin/hive/BaseHiveConnectorTest.java +++ b/plugin/trino-hive/src/test/java/io/trino/plugin/hive/BaseHiveConnectorTest.java @@ -342,6 +342,14 @@ public void testUpdate() .hasMessage(MODIFYING_NON_TRANSACTIONAL_TABLE_MESSAGE); } + @Override + public void testUpdateCaseSensitivity() + { + assertThatThrownBy(super::testUpdateCaseSensitivity) + .hasMessage(MODIFYING_NON_TRANSACTIONAL_TABLE_MESSAGE); + } + + @Test @Override public void testUpdateRowConcurrently() throws Exception diff --git a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/TestKuduConnectorTest.java b/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/TestKuduConnectorTest.java index 6fb5b5557a85..b0e41a9613cf 100644 --- a/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/TestKuduConnectorTest.java +++ b/plugin/trino-kudu/src/test/java/io/trino/plugin/kudu/TestKuduConnectorTest.java @@ -990,6 +990,15 @@ public void testUpdate() }); } + /** + * This test fails intermittently because Kudu doesn't have strong enough + * semantics to support writing from multiple threads. + */ + @Test(enabled = false) + @Override + public void testUpdateCaseSensitivity() {} + + @Test @Override public void testUpdateRowConcurrently() throws Exception diff --git a/testing/trino-testing/src/main/java/io/trino/testing/BaseConnectorTest.java b/testing/trino-testing/src/main/java/io/trino/testing/BaseConnectorTest.java index d566126a54aa..5f157c1ea578 100644 --- a/testing/trino-testing/src/main/java/io/trino/testing/BaseConnectorTest.java +++ b/testing/trino-testing/src/main/java/io/trino/testing/BaseConnectorTest.java @@ -4651,6 +4651,17 @@ public void testUpdate() } } + @Test + public void testUpdateCaseSensitivity() + { + skipTestUnless(hasBehavior(SUPPORTS_UPDATE)); + + try (TestTable table = new TestTable(getQueryRunner()::execute, "test_row_update", "AS SELECT * FROM nation")) { + assertUpdate("UPDATE " + table.getName() + " SET NATIONKEY = 100 WHERE REGIONKEY = 2", 5); + assertQuery("SELECT count(*) FROM " + table.getName() + " WHERE nationkey = 100", "VALUES 5"); + } + } + // Repeat test with invocationCount for better test coverage, since the tested aspect is inherently non-deterministic. @Test(timeOut = 60_000, invocationCount = 4) public void testUpdateRowConcurrently()