This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.1 by this push:
new 85818813400 branch-4.1: [fix](nereids) Make role-mapping keywords
RULE/CEL/MAPPING non-reserved (#64105)
85818813400 is described below
commit 8581881340069c91a40adcafe5953054839b75c4
Author: Calvin Kirs <[email protected]>
AuthorDate: Fri Jun 5 10:57:06 2026 +0800
branch-4.1: [fix](nereids) Make role-mapping keywords RULE/CEL/MAPPING
non-reserved (#64105)
#64104
---
.../antlr4/org/apache/doris/nereids/DorisParser.g4 | 3 +
.../nereids/parser/RoleMappingParserTest.java | 67 +++++++++++++++++++
.../keyword/test_role_mapping_keyword.groovy | 75 ++++++++++++++++++++++
3 files changed, 145 insertions(+)
diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
index e66d06c7475..b6459387c1b 100644
--- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
+++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
@@ -2030,6 +2030,7 @@ nonReserved
| CAST
| CATALOG
| CATALOGS
+ | CEL
| CHAIN
| CIPHER
| CHAR
@@ -2187,6 +2188,7 @@ nonReserved
| LOGICAL
| MANUAL
| MAP
+ | MAPPING
| MATCHED
| MATCH_ALL
| MATCH_ANY
@@ -2294,6 +2296,7 @@ nonReserved
| ROOT
| ROTATE
| ROUTINE
+ | RULE
| S3
| SAMPLE
| SAN
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/RoleMappingParserTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/RoleMappingParserTest.java
index c90c713080c..d6d4aeb0905 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/RoleMappingParserTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/RoleMappingParserTest.java
@@ -18,18 +18,38 @@
package org.apache.doris.nereids.parser;
import org.apache.doris.nereids.exceptions.ParseException;
+import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.commands.CreateRoleMappingCommand;
import org.apache.doris.nereids.trees.plans.commands.DropRoleMappingCommand;
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.qe.ConnectContext;
import com.google.common.collect.ImmutableSet;
+import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import java.util.List;
+
public class RoleMappingParserTest {
private final NereidsParser parser = new NereidsParser();
+ @BeforeEach
+ public void setUp() {
+ // parsing some statements (qualified column refs, INSERT target)
reads session state
+ ConnectContext ctx = new ConnectContext();
+ ctx.setDatabase("test");
+ ctx.setThreadLocalInfo();
+ }
+
+ @AfterEach
+ public void tearDown() {
+ ConnectContext.remove();
+ }
+
@Test
public void testCreateRoleMappingParse() {
LogicalPlan plan = parser.parseSingle("CREATE ROLE MAPPING IF NOT
EXISTS corp_mapping "
@@ -74,4 +94,51 @@ public class RoleMappingParserTest {
DropRoleMappingCommand drop2 = (DropRoleMappingCommand) plan2;
Assertions.assertTrue(drop2.isIfExists());
}
+
+ /**
+ * RULE/CEL/MAPPING are keywords introduced by the role-mapping DDL. They
are non-reserved,
+ * so they must still be usable as ordinary identifiers (column names,
etc.). Otherwise legacy
+ * SQL such as `INSERT INTO t(..., RULE, ...)` breaks with "mismatched
input 'RULE'".
+ */
+ @Test
+ public void testRoleMappingKeywordsAsIdentifier() {
+ LogicalPlan plan = parser.parseSingle("SELECT rule, cel, mapping FROM
t");
+ // the top plan is an UnboundResultSink wrapping the project; descend
to the project
+ Plan node = plan;
+ while (!(node instanceof LogicalProject) &&
!node.children().isEmpty()) {
+ node = node.child(0);
+ }
+ Assertions.assertInstanceOf(LogicalProject.class, node);
+ List<String> names = ((LogicalProject<?>) node).getProjects().stream()
+ .map(p ->
p.getName()).collect(java.util.stream.Collectors.toList());
+ Assertions.assertEquals(3, names.size());
+ Assertions.assertTrue(names.get(0).equalsIgnoreCase("rule"),
names.toString());
+ Assertions.assertTrue(names.get(1).equalsIgnoreCase("cel"),
names.toString());
+ Assertions.assertTrue(names.get(2).equalsIgnoreCase("mapping"),
names.toString());
+
+ // the keywords must also work as a table name / alias
+ Assertions.assertDoesNotThrow(() ->
+ parser.parseSingle("SELECT rule.cel FROM mapping AS rule"));
+
+ // the original failing case: keywords appearing in an INSERT column
list
+ Assertions.assertDoesNotThrow(() -> parser.parseSingle(
+ "INSERT INTO fnd_rnk_info(query_level, rule, mapping) "
+ + "SELECT query_level, rule, mapping FROM t"));
+ }
+
+ /**
+ * Making RULE/MAPPING non-reserved must not regress the role-mapping DDL:
the parser still
+ * has to route `CREATE/DROP ROLE MAPPING ...` to the role-mapping command
rather than treating
+ * MAPPING as a role name.
+ */
+ @Test
+ public void testRoleMappingDdlNotAmbiguous() {
+ Assertions.assertInstanceOf(CreateRoleMappingCommand.class,
parser.parseSingle(
+ "CREATE ROLE MAPPING corp_mapping ON AUTHENTICATION
INTEGRATION corp_oidc "
+ + "RULE ( USING CEL 'true' GRANT ROLE analyst )"));
+ Assertions.assertInstanceOf(DropRoleMappingCommand.class,
+ parser.parseSingle("DROP ROLE MAPPING corp_mapping"));
+ // bare `CREATE ROLE mapping` now creates a role literally named
"mapping"
+ Assertions.assertFalse(parser.parseSingle("CREATE ROLE mapping")
instanceof CreateRoleMappingCommand);
+ }
}
diff --git
a/regression-test/suites/query_p0/keyword/test_role_mapping_keyword.groovy
b/regression-test/suites/query_p0/keyword/test_role_mapping_keyword.groovy
new file mode 100644
index 00000000000..9cc0b27259d
--- /dev/null
+++ b/regression-test/suites/query_p0/keyword/test_role_mapping_keyword.groovy
@@ -0,0 +1,75 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// RULE / CEL / MAPPING are keywords introduced by the role-mapping DDL. They
are non-reserved,
+// so they must keep working as ordinary column / table identifiers. This
guards against the
+// regression where `INSERT INTO t(..., RULE, ...)` failed with "mismatched
input 'RULE'".
+suite("test_role_mapping_keyword", "query,p0") {
+ def tbl = "test_role_mapping_keyword_tbl"
+ sql "drop table if exists ${tbl}"
+ sql """
+ create table ${tbl}(
+ `query_level` int NOT NULL,
+ `rule` varchar(64),
+ `mapping` varchar(64),
+ `cel` varchar(64)
+ ) ENGINE=OLAP
+ DUPLICATE KEY(`query_level`)
+ DISTRIBUTED BY HASH(`query_level`) BUCKETS 1
+ PROPERTIES ("replication_allocation" = "tag.location.default: 1");
+ """
+
+ // keywords in an INSERT column list -- the original failing shape
+ sql """insert into ${tbl}(query_level, rule, mapping, cel) values (1,
'r1', 'm1', 'c1')"""
+
+ // Assert inline instead of using qt_/order_qt_ golden files: this test
only needs to prove the
+ // keywords parse as identifiers and round-trip correctly, so explicit
asserts keep it self-contained
+ // (no .out file to generate/commit).
+
+ // keywords as projection items
+ def r1 = sql "select rule, mapping, cel from ${tbl} order by query_level"
+ assertEquals(1, r1.size())
+ assertEquals("r1", r1[0][0])
+ assertEquals("m1", r1[0][1])
+ assertEquals("c1", r1[0][2])
+
+ // keyword as table alias and qualified column reference
+ def r2 = sql "select rule.rule from ${tbl} as rule"
+ assertEquals(1, r2.size())
+ assertEquals("r1", r2[0][0])
+
+ // keywords in a WHERE predicate
+ def r3 = sql "select query_level from ${tbl} where rule = 'r1' and cel =
'c1' order by query_level"
+ assertEquals(1, r3.size())
+ assertEquals(1, r3[0][0])
+
+ // insert ... select carrying the keywords through both the target and
source column lists
+ sql """insert into ${tbl}(query_level, rule, mapping, cel)
+ select 2, rule, mapping, cel from ${tbl}"""
+ def r4 = sql "select query_level, rule, mapping, cel from ${tbl} order by
query_level"
+ assertEquals(2, r4.size())
+ assertEquals(1, r4[0][0])
+ assertEquals("r1", r4[0][1])
+ assertEquals("m1", r4[0][2])
+ assertEquals("c1", r4[0][3])
+ assertEquals(2, r4[1][0])
+ assertEquals("r1", r4[1][1])
+ assertEquals("m1", r4[1][2])
+ assertEquals("c1", r4[1][3])
+
+ sql "drop table if exists ${tbl}"
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]