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