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]

Reply via email to