Skip to content
Permalink
Browse files
Add guardrail for ALTER TABLE ADD / DROP / REMOVE column operations
Patch by Josh McKenzie; reviewed by Jon Meredith for CASSANDRA-17495
  • Loading branch information
josh-mckenzie committed May 9, 2022
1 parent 11bdf1b commit ce515a3d77c2042575827e8bdb1da639fc957491
Showing 11 changed files with 232 additions and 2 deletions.
@@ -1,4 +1,5 @@
4.2
* Add guardrail for ALTER TABLE ADD / DROP / REMOVE column operations (CASSANDRA-17495)
* Rename DisableFlag class to EnableFlag on guardrails (CASSANDRA-17544)
Merged from 4.1:
Merged from 4.0:
@@ -57,6 +57,8 @@ using the provided 'sstableupgrade' tool.

New features
------------
- New Guardrails added:
- Whether ALTER TABLE commands are allowed to mutate columns

Upgrading
---------
@@ -1601,58 +1601,75 @@ drop_compact_storage_enabled: false
# The two thresholds default to -1 to disable.
# keyspaces_warn_threshold: -1
# keyspaces_fail_threshold: -1
#
# Guardrail to warn or fail when creating more user tables than threshold.
# The two thresholds default to -1 to disable.
# tables_warn_threshold: -1
# tables_fail_threshold: -1
#
# Guardrail to enable or disable the ability to create uncompressed tables
# uncompressed_tables_enabled: true
#
# Guardrail to warn or fail when creating/altering a table with more columns per table than threshold.
# The two thresholds default to -1 to disable.
# columns_per_table_warn_threshold: -1
# columns_per_table_fail_threshold: -1
#
# Guardrail to warn or fail when creating more secondary indexes per table than threshold.
# The two thresholds default to -1 to disable.
# secondary_indexes_per_table_warn_threshold: -1
# secondary_indexes_per_table_fail_threshold: -1
#
# Guardrail to enable or disable the creation of secondary indexes
# secondary_indexes_enabled: true
#
# Guardrail to warn or fail when creating more materialized views per table than threshold.
# The two thresholds default to -1 to disable.
# materialized_views_per_table_warn_threshold: -1
# materialized_views_per_table_fail_threshold: -1
#
# Guardrail to warn about, ignore or reject properties when creating tables. By default all properties are allowed.
# table_properties_warned: []
# table_properties_ignored: []
# table_properties_disallowed: []
#
# Guardrail to allow/disallow user-provided timestamps. Defaults to true.
# user_timestamps_enabled: true
#
# Guardrail to allow/disallow GROUP BY functionality.
# group_by_enabled: true
#
# Guardrail to allow/disallow TRUNCATE and DROP TABLE statements
# drop_truncate_table_enabled: true
#
# Guardrail to warn or fail when using a page size greater than threshold.
# The two thresholds default to -1 to disable.
# page_size_warn_threshold: -1
# page_size_fail_threshold: -1
#
# Guardrail to allow/disallow list operations that require read before write, i.e. setting list element by index and
# removing list elements by either index or value. Defaults to true.
# read_before_write_list_operations_enabled: true
#
# Guardrail to warn or fail when querying with an IN restriction selecting more partition keys than threshold.
# The two thresholds default to -1 to disable.
# partition_keys_in_select_warn_threshold: -1
# partition_keys_in_select_fail_threshold: -1
#
# Guardrail to warn or fail when an IN query creates a cartesian product with a size exceeding threshold,
# eg. "a in (1,2,...10) and b in (1,2...10)" results in cartesian product of 100.
# The two thresholds default to -1 to disable.
# in_select_cartesian_product_warn_threshold: -1
# in_select_cartesian_product_fail_threshold: -1
#
# Guardrail to warn about or reject read consistency levels. By default, all consistency levels are allowed.
# read_consistency_levels_warned: []
# read_consistency_levels_disallowed: []
#
# Guardrail to warn about or reject write consistency levels. By default, all consistency levels are allowed.
# write_consistency_levels_warned: []
# write_consistency_levels_disallowed: []
#
# Guardrail to warn or fail when encountering larger size of collection data than threshold.
# At query time this guardrail is applied only to the collection fragment that is being writen, even though in the case
# of non-frozen collections there could be unaccounted parts of the collection on the sstables. This is done this way to
@@ -1661,6 +1678,7 @@ drop_compact_storage_enabled: false
# The two thresholds default to null to disable.
# collection_size_warn_threshold:
# collection_size_fail_threshold:
#
# Guardrail to warn or fail when encountering more elements in collection than threshold.
# At query time this guardrail is applied only to the collection fragment that is being writen, even though in the case
# of non-frozen collections there could be unaccounted parts of the collection on the sstables. This is done this way to
@@ -1669,12 +1687,18 @@ drop_compact_storage_enabled: false
# The two thresholds default to -1 to disable.
# items_per_collection_warn_threshold: -1
# items_per_collection_fail_threshold: -1
#
# Guardrail to allow/disallow querying with ALLOW FILTERING. Defaults to true.
# allow_filtering_enabled: true
#
# Guardrail to warn or fail when creating a user-defined-type with more fields in than threshold.
# Default -1 to disable.
# fields_per_udt_warn_threshold: -1
# fields_per_udt_fail_threshold: -1
#
# Guardrail to indicate whether or not users are allowed to use ALTER TABLE commands to make column changes to tables
# alter_table_enabled: true
#
# Guardrail to warn or fail when local data disk usage percentage exceeds threshold. Valid values are in [1, 100].
# This is only used for the disks storing data directories, so it won't count any separate disks used for storing
# the commitlog, hints nor saved caches. The disk usage is the ratio between the amount of space used by the data
@@ -1686,13 +1710,15 @@ drop_compact_storage_enabled: false
# The two thresholds default to -1 to disable.
# data_disk_usage_percentage_warn_threshold: -1
# data_disk_usage_percentage_fail_threshold: -1
# Allows defining the max disk size of the data directories when calculating thresholds for
#
# Guardrail that allows users to define the max disk size of the data directories when calculating thresholds for
# disk_usage_percentage_warn_threshold and disk_usage_percentage_fail_threshold, so if this is greater than zero they
# become percentages of a fixed size on disk instead of percentages of the physically available disk size. This should
# be useful when we have a large disk and we only want to use a part of it for Cassandra's data directories.
# Valid values are in [1, max available disk size of all data directories].
# Defaults to null to disable and use the physically available disk size of data directories during calculations.
# data_disk_usage_max_disk_size:
#
# Guardrail to warn or fail when the minimum replication factor is lesser than threshold.
# This would also apply to system keyspaces.
# Suggested value for use in production: 2 or higher
@@ -825,6 +825,7 @@ public static void setClientMode(boolean clientMode)
public volatile Set<ConsistencyLevel> write_consistency_levels_warned = Collections.emptySet();
public volatile Set<ConsistencyLevel> write_consistency_levels_disallowed = Collections.emptySet();
public volatile boolean user_timestamps_enabled = true;
public volatile boolean alter_table_enabled = true;
public volatile boolean group_by_enabled = true;
public volatile boolean drop_truncate_table_enabled = true;
public volatile boolean secondary_indexes_enabled = true;
@@ -385,6 +385,20 @@ public void setCompactTablesEnabled(boolean enabled)
x -> config.compact_tables_enabled = x);
}

@Override
public boolean getAlterTableEnabled()
{
return config.alter_table_enabled;
}

public void setAlterTableEnabled(boolean enabled)
{
updatePropertyWithLogging("alter_table_enabled",
enabled,
() -> config.alter_table_enabled,
x -> config.alter_table_enabled = x);
}

@Override
public boolean getReadBeforeWriteListOperationsEnabled()
{
@@ -79,6 +79,7 @@ public abstract class AlterTableStatement extends AlterSchemaStatement
{
protected final String tableName;
private final boolean ifExists;
protected ClientState state;

public AlterTableStatement(String keyspaceName, String tableName, boolean ifExists)
{
@@ -87,6 +88,15 @@ public AlterTableStatement(String keyspaceName, String tableName, boolean ifExis
this.ifExists = ifExists;
}

@Override
public void validate(ClientState state)
{
super.validate(state);

// save the query state to use it for guardrails validation in #apply
this.state = state;
}

public Keyspaces apply(Keyspaces schema)
{
KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
@@ -187,6 +197,7 @@ public void validate(ClientState state)

public KeyspaceMetadata apply(KeyspaceMetadata keyspace, TableMetadata table)
{
Guardrails.alterTableEnabled.ensureEnabled("ALTER TABLE changing columns", state);
TableMetadata.Builder tableBuilder = table.unbuild();
Views.Builder viewsBuilder = keyspace.views.unbuild();
newColumns.forEach(c -> addColumn(keyspace, table, c, ifColumnNotExists, tableBuilder, viewsBuilder));
@@ -289,6 +300,7 @@ private DropColumns(String keyspaceName, String tableName, Set<ColumnIdentifier>

public KeyspaceMetadata apply(KeyspaceMetadata keyspace, TableMetadata table)
{
Guardrails.alterTableEnabled.ensureEnabled("ALTER TABLE changing columns", state);
TableMetadata.Builder builder = table.unbuild();
removedColumns.forEach(c -> dropColumn(keyspace, table, c, ifColumnExists, builder));
return keyspace.withSwapped(keyspace.tables.withSwapped(builder.build()));
@@ -356,6 +368,7 @@ private RenameColumns(String keyspaceName, String tableName, Map<ColumnIdentifie

public KeyspaceMetadata apply(KeyspaceMetadata keyspace, TableMetadata table)
{
Guardrails.alterTableEnabled.ensureEnabled("ALTER TABLE changing columns", state);
TableMetadata.Builder tableBuilder = table.unbuild();
Views.Builder viewsBuilder = keyspace.views.unbuild();
renamedColumns.forEach((o, n) -> renameColumn(keyspace, table, o, n, ifColumnsExists, tableBuilder, viewsBuilder));
@@ -145,6 +145,14 @@ public final class Guardrails implements GuardrailsMBean
state -> CONFIG_PROVIDER.getOrCreate(state).getGroupByEnabled(),
"GROUP BY functionality");

/**
* Guardrail disabling ALTER TABLE column mutation access.
*/
public static final EnableFlag alterTableEnabled =
new EnableFlag("alter_table",
state -> CONFIG_PROVIDER.getOrCreate(state).getAlterTableEnabled(),
"User access to ALTER TABLE statement for column mutation");

public static final EnableFlag dropTruncateTableEnabled =
new EnableFlag("drop_truncate_table_enabled",
state -> CONFIG_PROVIDER.getOrCreate(state).getDropTruncateTableEnabled(),
@@ -539,6 +547,18 @@ public void setUserTimestampsEnabled(boolean enabled)
DEFAULT_CONFIG.setUserTimestampsEnabled(enabled);
}

@Override
public boolean getAlterTableEnabled()
{
return DEFAULT_CONFIG.getAlterTableEnabled();
}

@Override
public void setAlterTableEnabled(boolean enabled)
{
DEFAULT_CONFIG.setAlterTableEnabled(enabled);
}

@Override
public boolean getAllowFilteringEnabled()
{
@@ -132,6 +132,13 @@
*/
boolean getUserTimestampsEnabled();

/**
* Returns whether users are allowed access to the ALTER TABLE statement to mutate columns or not
*
* @return {@code true} if ALTER TABLE ADD/REMOVE/RENAME is allowed, {@code false} otherwise.
*/
boolean getAlterTableEnabled();

/**
* Returns whether tables can be uncompressed
*
@@ -250,6 +250,20 @@
*/
void setCompactTablesEnabled(boolean enabled);

/**
* Gets whether users can use the ALTER TABLE statement to change columns
*
* @return {@code true} if ALTER TABLE is allowed, {@code false} otherwise.
*/
boolean getAlterTableEnabled();

/**
* Sets whether users can use the ALTER TABLE statement to change columns
*
* @param enabled {@code true} if changing columns is allowed, {@code false} otherwise.
*/
void setAlterTableEnabled(boolean enabled);

/**
* Returns whether GROUP BY queries are allowed.
*
@@ -17,7 +17,6 @@
*/
package org.apache.cassandra.service;


import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOError;

0 comments on commit ce515a3

Please sign in to comment.