From 4d417948682dee5aa2ad154f28e803cb198f3d2c Mon Sep 17 00:00:00 2001 From: Maxim Muzafarov Date: Sun, 12 Jul 2026 18:56:28 +0200 Subject: [PATCH] Allow unreserved keywords as user and identity names in USER and IDENTITY statements patch by Maxim Muzafarov; reviewed by for CASSANDRA-21510 --- src/antlr/Parser.g | 2 ++ .../cassandra/cql3/ReservedKeywordsTest.java | 33 ++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/antlr/Parser.g b/src/antlr/Parser.g index 8794d00d02a9..c4597164064f 100644 --- a/src/antlr/Parser.g +++ b/src/antlr/Parser.g @@ -2363,12 +2363,14 @@ vector_type returns [CQL3Type.Raw vt] username : IDENT | STRING_LITERAL + | unreserved_keyword | QUOTED_NAME { addRecognitionError("Quoted strings are are not supported for user names and USER is deprecated, please use ROLE");} ; identity : IDENT | STRING_LITERAL + | unreserved_keyword | QUOTED_NAME { addRecognitionError("Quoted strings are are not supported for identity");} ; diff --git a/test/unit/org/apache/cassandra/cql3/ReservedKeywordsTest.java b/test/unit/org/apache/cassandra/cql3/ReservedKeywordsTest.java index ef7a4b7ec218..0278a1d11104 100644 --- a/test/unit/org/apache/cassandra/cql3/ReservedKeywordsTest.java +++ b/test/unit/org/apache/cassandra/cql3/ReservedKeywordsTest.java @@ -68,11 +68,42 @@ public void parserAndTextFileMatch() asserts.assertAll(); } + /** + * Legacy USER and IDENTITY statements must accept unreserved keywords as names, just like + * role statements do ({@code roleName} accepts {@code unreserved_keyword}). Otherwise adding + * a new keyword to the grammar breaks existing deployments with a user or identity of that name. + */ + @Test + public void testUnreservedKeywordsAsUserNameAndIdentity() + { + SoftAssertions asserts = new SoftAssertions(); + for (var f : Cql_Lexer.class.getDeclaredFields()) + { + if (!Modifier.isStatic(f.getModifiers())) continue; + if (!f.getName().startsWith("K_")) continue; + String keyword = f.getName().replaceFirst("K_", ""); + if (ReservedKeywords.isReserved(keyword)) continue; + + for (String statement : new String[]{ "DROP USER %s", "DROP IDENTITY %s" }) + { + asserts.assertThat(parses(String.format(statement, keyword))) + .describedAs(String.format(statement, keyword)) + .isTrue(); + } + } + asserts.assertAll(); + } + private static boolean isAllowed(String keyword) + { + return parses(String.format("ALTER TABLE ks.t ADD %s TEXT", keyword)); + } + + private static boolean parses(String cql) { try { - QueryProcessor.parseStatement(String.format("ALTER TABLE ks.t ADD %s TEXT", keyword)); + QueryProcessor.parseStatement(cql); return true; } catch (SyntaxException ignore)