jyothsnakonisa commented on code in PR #4411:
URL: https://github.com/apache/cassandra/pull/4411#discussion_r2488025037
##########
test/unit/org/apache/cassandra/cql3/statements/DescribeStatementTest.java:
##########
@@ -1050,6 +1051,201 @@ public void
testDescFunctionAndAggregateShouldNotOmitQuotations() throws Throwab
}
}
+ @Test
+ public void testDescribeKeyspaceWithCommentsAndSecurityLabels() throws
Throwable
+ {
+ try
+ {
+ // Create keyspace with comment and security label
+ execute("CREATE KEYSPACE test_comments WITH REPLICATION = {'class'
: 'SimpleStrategy', 'replication_factor' : 1}");
+ execute("COMMENT ON KEYSPACE test_comments IS 'This is a test
keyspace with comments'");
+ execute("SECURITY LABEL ON KEYSPACE test_comments IS
'confidential'");
+
+ String expectedKeyspaceOutput = "CREATE KEYSPACE test_comments
WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'}" +
+ " AND durable_writes = true" +
+ " AND fast_path = 'simple';\n" +
+ "COMMENT ON KEYSPACE test_comments
IS 'This is a test keyspace with comments';\n" +
+ "SECURITY LABEL ON KEYSPACE
test_comments IS 'confidential';";
+
+ for (String describeKeyword : new String[]{ "DESCRIBE", "DESC" })
+ {
+ assertRowsNet(executeDescribeNet(describeKeyword + " ONLY
KEYSPACE test_comments"),
+ row("test_comments", "keyspace",
"test_comments", expectedKeyspaceOutput));
+ }
+ }
+ finally
+ {
+ execute("DROP KEYSPACE IF EXISTS test_comments");
+ }
+ }
+
+ @Test
+ public void testDescribeTableWithCommentsAndSecurityLabels() throws
Throwable
+ {
+ try
+ {
+ // Create keyspace and table with comments and security labels
+ execute("CREATE KEYSPACE test_table_comments WITH REPLICATION =
{'class' : 'SimpleStrategy', 'replication_factor' : 1}");
+ execute("CREATE TABLE test_table_comments.users (id int PRIMARY
KEY, name text, email text)");
+
+ // Add comments and security labels to table and columns
+ execute("COMMENT ON TABLE test_table_comments.users IS 'User
information table'");
+ execute("SECURITY LABEL ON TABLE test_table_comments.users IS
'restricted'");
+ execute("COMMENT ON COLUMN test_table_comments.users.name IS 'User
full name'");
+ execute("SECURITY LABEL ON COLUMN test_table_comments.users.name
IS 'pii'");
+
+ String expectedTableOutput = "CREATE TABLE
test_table_comments.users (\n" +
+ " id int PRIMARY KEY,\n" +
+ " email text,\n" +
+ " name text\n" +
+ ") WITH " + tableParametersCql("User
information table") + "\n" +
+ "COMMENT ON TABLE
test_table_comments.users IS 'User information table';\n" +
+ "SECURITY LABEL ON TABLE
test_table_comments.users IS 'restricted';\n" +
+ "COMMENT ON COLUMN
test_table_comments.users.name IS 'User full name';\n" +
+ "SECURITY LABEL ON COLUMN
test_table_comments.users.name IS 'pii';";
+ for (String describeKeyword : new String[]{ "DESCRIBE", "DESC" })
+ {
+ assertRowsNet(executeDescribeNet("test_table_comments",
describeKeyword + " TABLE users"),
+ row("test_table_comments", "table", "users",
expectedTableOutput));
+ }
+ }
+ finally
+ {
+ execute("DROP KEYSPACE IF EXISTS test_table_comments");
+ }
+ }
+
+ @Test
+ public void testDescribeUserTypeWithCommentsAndSecurityLabels() throws
Throwable
+ {
+ try
+ {
+ // Create a user-defined type with comments and security labels
+ String type = createType(KEYSPACE_PER_TEST, "CREATE TYPE %s (name
text, age int, address text)");
+
+ execute("COMMENT ON TYPE " + KEYSPACE_PER_TEST + "." + type + " IS
'Person information type'");
+ execute("SECURITY LABEL ON TYPE " + KEYSPACE_PER_TEST + "." + type
+ " IS 'personal_data'");
+
+ String expectedTypeOutput = "CREATE TYPE " + KEYSPACE_PER_TEST +
"." + type + " (\n" +
+ " name text,\n" +
+ " age int,\n" +
+ " address text\n" +
+ ");\n" +
+ "COMMENT ON TYPE " + KEYSPACE_PER_TEST
+ "." + type + " IS 'Person information type';\n" +
+ "SECURITY LABEL ON TYPE " +
KEYSPACE_PER_TEST + "." + type + " IS 'personal_data';";
+
+ for (String describeKeyword : new String[]{ "DESCRIBE", "DESC" })
+ {
+ assertRowsNet(executeDescribeNet(KEYSPACE_PER_TEST,
describeKeyword + " TYPE " + type),
+ row(KEYSPACE_PER_TEST, "type", type,
expectedTypeOutput));
+ }
+ }
+ finally
+ {
+ // Cleanup is handled by the base test class
+ }
+ }
+
+ @Test
+ public void testDescribeUserTypeWithFieldCommentsAndSecurityLabels()
throws Throwable
+ {
+ try
+ {
+ // Create a user-defined type with field-level comments and
security labels
+ String type = createType(KEYSPACE_PER_TEST, "CREATE TYPE %s
(latitude double, longitude double)");
+
+ execute("COMMENT ON TYPE " + KEYSPACE_PER_TEST + "." + type + " IS
'Geographic location'");
+ execute("SECURITY LABEL ON TYPE " + KEYSPACE_PER_TEST + "." + type
+ " IS 'GEODATA'");
+ execute("COMMENT ON FIELD " + KEYSPACE_PER_TEST + "." + type +
".latitude IS 'Latitude in decimal degrees'");
+ execute("SECURITY LABEL ON FIELD " + KEYSPACE_PER_TEST + "." +
type + ".latitude IS 'SENSITIVE'");
+ execute("COMMENT ON FIELD " + KEYSPACE_PER_TEST + "." + type +
".longitude IS 'Longitude in decimal degrees'");
+ execute("SECURITY LABEL ON FIELD " + KEYSPACE_PER_TEST + "." +
type + ".longitude IS 'SENSITIVE'");
+
+ String expectedTypeOutput = "CREATE TYPE " + KEYSPACE_PER_TEST +
"." + type + " (\n" +
+ " latitude double,\n" +
+ " longitude double\n" +
+ ");\n" +
+ "COMMENT ON TYPE " + KEYSPACE_PER_TEST
+ "." + type + " IS 'Geographic location';\n" +
+ "COMMENT ON FIELD " + KEYSPACE_PER_TEST
+ "." + type + ".latitude IS 'Latitude in decimal degrees';\n" +
+ "COMMENT ON FIELD " + KEYSPACE_PER_TEST
+ "." + type + ".longitude IS 'Longitude in decimal degrees';\n" +
+ "SECURITY LABEL ON TYPE " +
KEYSPACE_PER_TEST + "." + type + " IS 'GEODATA';\n" +
+ "SECURITY LABEL ON FIELD " +
KEYSPACE_PER_TEST + "." + type + ".latitude IS 'SENSITIVE';\n" +
+ "SECURITY LABEL ON FIELD " +
KEYSPACE_PER_TEST + "." + type + ".longitude IS 'SENSITIVE';";
+
+ for (String describeKeyword : new String[]{ "DESCRIBE", "DESC" })
+ {
+ assertRowsNet(executeDescribeNet(KEYSPACE_PER_TEST,
describeKeyword + " TYPE " + type),
+ row(KEYSPACE_PER_TEST, "type", type,
expectedTypeOutput));
+ }
+ }
+ finally
+ {
+ // Cleanup is handled by the base test class
+ }
+ }
+
+ @Test
+ public void testDescribeSchemaWithCommentsAndSecurityLabels() throws
Throwable
+ {
+ try
+ {
+ // Create a comprehensive schema with comments and security labels
+ execute("CREATE KEYSPACE test_schema WITH REPLICATION = {'class' :
'SimpleStrategy', 'replication_factor' : 1}");
+ execute("COMMENT ON KEYSPACE test_schema IS 'Test schema for
comments and security labels'");
+ execute("SECURITY LABEL ON KEYSPACE test_schema IS
'test_environment'");
+
+ execute("CREATE TYPE test_schema.address_type (street text, city
text, zip text)");
+ String type = "address_type";
+ execute("COMMENT ON TYPE test_schema." + type + " IS 'Address
information'");
+ execute("SECURITY LABEL ON TYPE test_schema." + type + " IS
'location_data'");
+
+ execute("CREATE TABLE test_schema.customers (id int PRIMARY KEY,
name text, addr " + type + ")");
+ execute("COMMENT ON TABLE test_schema.customers IS 'Customer
information'");
+ execute("SECURITY LABEL ON TABLE test_schema.customers IS
'customer_data'");
+ execute("COMMENT ON COLUMN test_schema.customers.name IS 'Customer
name'");
+ execute("SECURITY LABEL ON COLUMN test_schema.customers.name IS
'pii'");
+
+ ResultSet result = executeDescribeNet("DESCRIBE SCHEMA");
+ boolean foundKeyspaceWithComment = false;
+ boolean foundTypeWithComment = false;
+ boolean foundTableWithComment = false;
+
+ for (Row row : result.all())
+ {
+ String createStatement = row.getString("create_statement");
+
+ if (row.getString("type").equals("keyspace") &&
row.getString("name").equals("test_schema"))
+ {
+ assertTrue("Keyspace should include comment",
createStatement.contains("COMMENT ON KEYSPACE test_schema IS 'Test schema for
comments and security labels'"));
+ assertTrue("Keyspace should include security label",
createStatement.contains("SECURITY LABEL ON KEYSPACE test_schema IS
'test_environment'"));
+ foundKeyspaceWithComment = true;
+ }
+ else if (row.getString("type").equals("type") &&
row.getString("name").equals(type))
+ {
+ assertTrue("Type should include comment",
createStatement.contains("COMMENT ON TYPE test_schema." + type + " IS 'Address
information'"));
+ assertTrue("Type should include security label",
createStatement.contains("SECURITY LABEL ON TYPE test_schema." + type + " IS
'location_data'"));
+ foundTypeWithComment = true;
+ }
+ else if (row.getString("type").equals("table") &&
row.getString("name").equals("customers"))
+ {
+ assertTrue("Table should include comment",
createStatement.contains("COMMENT ON TABLE test_schema.customers IS 'Customer
information';"));
+ assertTrue("Table should include security label",
createStatement.contains("SECURITY LABEL ON TABLE test_schema.customers IS
'customer_data'"));
+ assertTrue("Table should include column comment",
createStatement.contains("COMMENT ON COLUMN test_schema.customers.name IS
'Customer name'"));
+ assertTrue("Table should include column security label",
createStatement.contains("SECURITY LABEL ON COLUMN test_schema.customers.name
IS 'pii'"));
+ foundTableWithComment = true;
+ }
+ }
+
+ assertTrue("Should find keyspace with comment in schema",
foundKeyspaceWithComment);
+ assertTrue("Should find type with comment in schema",
foundTypeWithComment);
+ assertTrue("Should find table with comment in schema",
foundTableWithComment);
+ }
+ finally
+ {
+ execute("DROP KEYSPACE IF EXISTS test_schema");
+ }
+ }
+
Review Comment:
Added `testDescribeOnSchemaElement` that tests all those scenarios, that you
mentioned
##########
src/java/org/apache/cassandra/cql3/statements/schema/SecurityLabelOnColumnStatement.java:
##########
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+package org.apache.cassandra.cql3.statements.schema;
+
+import org.apache.cassandra.audit.AuditLogContext;
+import org.apache.cassandra.audit.AuditLogEntryType;
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.cql3.ColumnIdentifier;
+import org.apache.cassandra.cql3.QualifiedName;
+import org.apache.cassandra.cql3.statements.SchemaDescriptionsUtil;
+import org.apache.cassandra.schema.ColumnMetadata;
+import org.apache.cassandra.schema.KeyspaceMetadata;
+import org.apache.cassandra.schema.Keyspaces;
+import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
+import org.apache.cassandra.schema.TableMetadata;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.service.ClientWarn;
+import org.apache.cassandra.tcm.ClusterMetadata;
+import org.apache.cassandra.transport.Event.SchemaChange;
+import org.apache.cassandra.transport.Event.SchemaChange.Change;
+import org.apache.cassandra.transport.Event.SchemaChange.Target;
+
+/**
+ * Handles the execution of SECURITY LABEL ON COLUMN CQL statements.
+ * <p>
+ * This statement allows users to add security classification labels to
individual columns.
+ * Security labels are stored in the column metadata and displayed when using
DESCRIBE TABLE.
+ * </p>
+ * <p>
+ * Syntax: {@code SECURITY LABEL ON COLUMN
[keyspace_name.]table_name.column_name IS 'label';}
+ * </p>
+ * <p>
+ * Security labels can be used to mark data sensitivity levels or compliance
requirements.
+ * Labels can be removed by setting them to an empty string or null.
+ * </p>
+ *
+ * @see SecurityLabelOnKeyspaceStatement
+ * @see SecurityLabelOnTableStatement
+ * @see SecurityLabelOnTypeStatement
+ */
+public final class SecurityLabelOnColumnStatement extends AlterSchemaStatement
+{
+ private final String tableName;
+ private final ColumnIdentifier columnName;
+ private final String securityLabel;
+ private final String provider;
Review Comment:
It is added in the documentation of the class
##########
src/java/org/apache/cassandra/cql3/statements/SchemaDescriptionsUtil.java:
##########
@@ -0,0 +1,292 @@
+/*
+ * 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.
+ */
+
+package org.apache.cassandra.cql3.statements;
+
+import org.apache.commons.lang3.StringUtils;
+
+import org.apache.cassandra.db.marshal.UserType;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.schema.ColumnMetadata;
+import org.apache.cassandra.schema.KeyspaceMetadata;
+import org.apache.cassandra.schema.TableMetadata;
+
+/**
+ * Utility class for appending COMMENT and SECURITY LABEL statements to schema
element descriptions.
+ * <p>
+ * This class provides methods to append CQL statements for comments and
security labels on various
+ * schema elements (keyspaces, tables, columns, and types) to their CREATE
statement output.
+ * These are typically used by DESCRIBE statements to show the complete schema
definition including
+ * any associated metadata.
+ * </p>
+ */
+public class SchemaDescriptionsUtil
+{
+ private static final String COMMENT_DESCRIPTION_TYPE = "COMMENT";
+ private static final String SECURITY_LABEL_DESCRIPTION_TYPE = "SECURITY
LABEL";
+ private static final String KEYSPACE_SCHEMA_ELEMENT = "KEYSPACE";
+ private static final String TABLE_SCHEMA_ELEMENT = "TABLE";
+ private static final String COLUMN_SCHEMA_ELEMENT = "COLUMN";
+ private static final String TYPE_SCHEMA_ELEMENT = "TYPE";
+ private static final String FIELD_SCHEMA_ELEMENT = "FIELD";
+
+ /**
+ * Maximum length for comments and security labels.
+ * This limit helps prevent excessive memory usage and storage overhead.
+ */
+ public static final int MAX_METADATA_LENGTH = 1024;
+
+ private SchemaDescriptionsUtil()
+ {
+ }
+
+ /**
+ * Validates that a comment does not exceed the maximum allowed length.
+ *
+ * @param comment the comment to validate (can be null)
+ * @throws InvalidRequestException if the comment exceeds the maximum
length
+ */
+ public static void validateComment(String comment)
+ {
+ if (comment != null && comment.length() > MAX_METADATA_LENGTH)
+ {
+ String msg = String.format("Comment length (%d) exceeds maximum
allowed length (%d)",
+ comment.length(),
+ MAX_METADATA_LENGTH);
+ throw new InvalidRequestException(msg);
+ }
+ }
+
+ /**
+ * Validates that a security label does not exceed the maximum allowed
length.
+ *
+ * @param securityLabel the security label to validate (can be null)
+ * @throws InvalidRequestException if the security label exceeds the
maximum length
+ */
+ public static void validateSecurityLabel(String securityLabel)
+ {
+ if (securityLabel != null && securityLabel.length() >
MAX_METADATA_LENGTH)
+ {
+ throw new InvalidRequestException(String.format("Security label
length (%d) exceeds maximum allowed length (%d)",
+
securityLabel.length(),
+
MAX_METADATA_LENGTH));
+ }
+ }
+
+ /**
+ * Appends a COMMENT ON KEYSPACE statement to the builder if the keyspace
has a comment.
+ *
+ * @param builder the StringBuilder to append to
+ * @param keyspaceMetadata the keyspace metadata containing the comment
+ */
+ public static void appendCommentOnKeyspace(StringBuilder builder,
KeyspaceMetadata keyspaceMetadata)
+ {
+ addDescription(builder, COMMENT_DESCRIPTION_TYPE,
KEYSPACE_SCHEMA_ELEMENT, keyspaceMetadata.name,
keyspaceMetadata.params.comment);
+ }
+
+ /**
+ * Appends a SECURITY LABEL ON KEYSPACE statement to the builder if the
keyspace has a security label.
+ *
+ * @param builder the StringBuilder to append to
+ * @param keyspaceMetadata the keyspace metadata containing the security
label
+ */
+ public static void appendSecurityLabelOnKeyspace(StringBuilder builder,
KeyspaceMetadata keyspaceMetadata)
+ {
+ addDescription(builder, SECURITY_LABEL_DESCRIPTION_TYPE,
KEYSPACE_SCHEMA_ELEMENT, keyspaceMetadata.name,
keyspaceMetadata.params.securityLabel);
+ }
+
+ /**
+ * Appends a COMMENT ON TABLE statement to the builder if the table has a
comment.
+ *
+ * @param builder the StringBuilder to append to
+ * @param tableMetadata the table metadata containing the comment
+ */
+ public static void appendCommentOnTable(StringBuilder builder,
TableMetadata tableMetadata)
+ {
+ String schemaElement = String.format("%s.%s", tableMetadata.keyspace,
tableMetadata.name);
+ addDescription(builder, COMMENT_DESCRIPTION_TYPE,
TABLE_SCHEMA_ELEMENT, schemaElement, tableMetadata.params.comment);
+ }
+
+ /**
+ * Appends SECURITY LABEL ON TABLE statement and COMMENT/SECURITY LABEL ON
COLUMN statements
+ * to the builder if the table or any of its columns have security labels
or comments.
+ *
+ * @param builder the StringBuilder to append to
+ * @param tableMetadata the table metadata containing the security label
and columns
+ */
+ public static void appendSecurityLabelOnTable(StringBuilder builder,
TableMetadata tableMetadata)
+ {
+ String schemaElement = String.format("%s.%s", tableMetadata.keyspace,
tableMetadata.name);
+ addDescription(builder, SECURITY_LABEL_DESCRIPTION_TYPE,
TABLE_SCHEMA_ELEMENT, schemaElement, tableMetadata.params.securityLabel);
Review Comment:
I have moved the logic of appending comments to schema elements, however
there will be code redundancy between all schema elements in terms of how we
create CQL statements for comments and security labels. This class avoids that.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]