This is an automated email from the ASF dual-hosted git repository.

adelapena pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 9f99e72aae Add reasons to guardrail messages and consider guardrails 
in the error message for needed ALLOW FILTERING
9f99e72aae is described below

commit 9f99e72aae812b86d277883601450bc0e7bb9463
Author: Andrés de la Peña <[email protected]>
AuthorDate: Fri Nov 4 12:39:21 2022 +0000

    Add reasons to guardrail messages and consider guardrails in the error 
message for needed ALLOW FILTERING
    
    * Add optional reason for guarding an operation to every guardrail
    
    * Add reason for disabling ALLOW FILTERING into cassandra.yaml
    
    * Consider allow_filtering_enabled guardrail in the error message for 
needed ALLOW FILTERING
    
    patch by Andrés de la Peña; reviewed by Berenguer Blasi and Josh McKenzie 
for CASSANDRA-17967
---
 CHANGES.txt                                        |  1 +
 conf/cassandra.yaml                                |  1 +
 .../cql3/restrictions/StatementRestrictions.java   | 37 +++++++++----
 .../cassandra/cql3/statements/BatchStatement.java  |  2 +-
 .../cassandra/cql3/statements/DeleteStatement.java |  7 ++-
 .../cql3/statements/ModificationStatement.java     | 14 +++--
 .../cassandra/cql3/statements/SelectStatement.java | 12 ++--
 .../cassandra/cql3/statements/UpdateStatement.java | 19 +++++--
 .../statements/schema/CreateViewStatement.java     |  3 +-
 .../apache/cassandra/db/guardrails/EnableFlag.java | 19 ++++++-
 .../apache/cassandra/db/guardrails/Guardrail.java  | 19 ++++++-
 .../apache/cassandra/db/guardrails/Guardrails.java | 34 +++++++++++-
 .../cassandra/db/guardrails/MaxThreshold.java      |  6 +-
 .../cassandra/db/guardrails/MinThreshold.java      |  6 +-
 .../db/guardrails/PercentageThreshold.java         |  6 +-
 .../apache/cassandra/db/guardrails/Predicates.java |  4 +-
 .../apache/cassandra/db/guardrails/Threshold.java  |  4 +-
 .../org/apache/cassandra/db/guardrails/Values.java |  4 +-
 src/java/org/apache/cassandra/db/view/View.java    |  3 +-
 .../distributed/fuzz/SSTableGenerator.java         |  3 +-
 .../db/guardrails/GuardrailAllowFilteringTest.java | 27 ++++++++-
 .../cassandra/db/guardrails/GuardrailTester.java   | 23 ++++++--
 .../guardrails/GuardrailsConfigProviderTest.java   |  1 +
 .../cassandra/db/guardrails/GuardrailsTest.java    | 64 +++++++++++++---------
 24 files changed, 240 insertions(+), 79 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 1728a8db8d..b1b3788dc4 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 4.2
+ * Add reasons to guardrail messages and consider guardrails in the error 
message for needed ALLOW FILTERING (CASSANDRA-17967)
  * Add support for CQL functions on collections, tuples and UDTs 
(CASSANDRA-17811)
  * Add flag to exclude nodes from local DC when running nodetool rebuild 
(CASSANDRA-17870)
  * Adding endpoint verification option to client_encryption_options 
(CASSANDRA-18034)
diff --git a/conf/cassandra.yaml b/conf/cassandra.yaml
index ff074bddd7..ab15c87ebc 100644
--- a/conf/cassandra.yaml
+++ b/conf/cassandra.yaml
@@ -1814,6 +1814,7 @@ drop_compact_storage_enabled: false
 # items_per_collection_fail_threshold: -1
 #
 # Guardrail to allow/disallow querying with ALLOW FILTERING. Defaults to true.
+# ALLOW FILTERING can potentially visit all the data in the table and have 
unpredictable performance.
 # allow_filtering_enabled: true
 #
 # Guardrail to allow/disallow setting SimpleStrategy via keyspace creation or 
alteration. Defaults to true.
diff --git 
a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java 
b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
index 8f8be94e1f..371bc589c3 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
@@ -28,6 +28,7 @@ import org.apache.cassandra.cql3.statements.Bound;
 import org.apache.cassandra.cql3.statements.StatementType;
 import org.apache.cassandra.db.*;
 import org.apache.cassandra.db.filter.RowFilter;
+import org.apache.cassandra.db.guardrails.Guardrails;
 import org.apache.cassandra.db.marshal.AbstractType;
 import org.apache.cassandra.dht.*;
 import org.apache.cassandra.exceptions.InvalidRequestException;
@@ -50,10 +51,15 @@ import static 
org.apache.cassandra.cql3.statements.RequestValidations.invalidReq
  */
 public final class StatementRestrictions
 {
-    public static final String REQUIRES_ALLOW_FILTERING_MESSAGE =
-            "Cannot execute this query as it might involve data filtering and 
" +
-            "thus may have unpredictable performance. If you want to execute " 
+
-            "this query despite the performance unpredictability, use ALLOW 
FILTERING";
+    private static final String ALLOW_FILTERING_MESSAGE =
+            "Cannot execute this query as it might involve data filtering and 
thus may have unpredictable performance. ";
+
+    public static final String REQUIRES_ALLOW_FILTERING_MESSAGE = 
ALLOW_FILTERING_MESSAGE +
+            "If you want to execute this query despite the performance 
unpredictability, use ALLOW FILTERING";
+
+    public static final String CANNOT_USE_ALLOW_FILTERING_MESSAGE = 
ALLOW_FILTERING_MESSAGE +
+            "Executing this query despite the performance unpredictability 
with ALLOW FILTERING has been disabled " +
+            "by the allow_filtering_enabled property in cassandra.yaml";
 
     /**
      * The type of statement
@@ -125,7 +131,8 @@ public final class StatementRestrictions
         this.notNullColumns = new HashSet<>();
     }
 
-    public StatementRestrictions(StatementType type,
+    public StatementRestrictions(ClientState state,
+                                 StatementType type,
                                  TableMetadata table,
                                  WhereClause whereClause,
                                  VariableSpecifications boundNames,
@@ -133,14 +140,15 @@ public final class StatementRestrictions
                                  boolean allowFiltering,
                                  boolean forView)
     {
-        this(type, table, whereClause, boundNames, selectsOnlyStaticColumns, 
type.allowUseOfSecondaryIndices(), allowFiltering, forView);
+        this(state, type, table, whereClause, boundNames, 
selectsOnlyStaticColumns, type.allowUseOfSecondaryIndices(), allowFiltering, 
forView);
     }
 
     /*
      * We want to override allowUseOfSecondaryIndices flag from the 
StatementType for MV statements
      * to avoid initing the Keyspace and SecondaryIndexManager.
      */
-    public StatementRestrictions(StatementType type,
+    public StatementRestrictions(ClientState state,
+                                 StatementType type,
                                  TableMetadata table,
                                  WhereClause whereClause,
                                  VariableSpecifications boundNames,
@@ -214,7 +222,7 @@ public final class StatementRestrictions
         }
 
         // At this point, the select statement if fully constructed, but we 
still have a few things to validate
-        processPartitionKeyRestrictions(hasQueriableIndex, allowFiltering, 
forView);
+        processPartitionKeyRestrictions(state, hasQueriableIndex, 
allowFiltering, forView);
 
         // Some but not all of the partition key columns have been specified;
         // hence we need turn these restrictions into a row filter.
@@ -267,7 +275,7 @@ public final class StatementRestrictions
             if (hasQueriableIndex)
                 usesSecondaryIndexing = true;
             else if (!allowFiltering)
-                throw 
invalidRequest(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE);
+                throw invalidRequest(allowFilteringMessage(state));
 
             filterRestrictions.add(nonPrimaryKeyRestrictions);
         }
@@ -408,7 +416,7 @@ public final class StatementRestrictions
         return this.usesSecondaryIndexing;
     }
 
-    private void processPartitionKeyRestrictions(boolean hasQueriableIndex, 
boolean allowFiltering, boolean forView)
+    private void processPartitionKeyRestrictions(ClientState state, boolean 
hasQueriableIndex, boolean allowFiltering, boolean forView)
     {
         if (!type.allowPartitionKeyRanges())
         {
@@ -445,7 +453,7 @@ public final class StatementRestrictions
             if (partitionKeyRestrictions.needFiltering(table))
             {
                 if (!allowFiltering && !forView && !hasQueriableIndex)
-                    throw new 
InvalidRequestException(REQUIRES_ALLOW_FILTERING_MESSAGE);
+                    throw new 
InvalidRequestException(allowFilteringMessage(state));
 
                 isKeyRange = true;
                 usesSecondaryIndexing = hasQueriableIndex;
@@ -874,4 +882,11 @@ public final class StatementRestrictions
     {
         return ToStringBuilder.reflectionToString(this, 
ToStringStyle.SHORT_PREFIX_STYLE);
     }
+
+    private static String allowFilteringMessage(ClientState state)
+    {
+        return Guardrails.allowFilteringEnabled.isEnabled(state)
+               ? REQUIRES_ALLOW_FILTERING_MESSAGE
+               : CANNOT_USE_ALLOW_FILTERING_MESSAGE;
+    }
 }
diff --git a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java 
b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
index 61e4934864..ab761ad9c1 100644
--- a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
@@ -647,7 +647,7 @@ public class BatchStatement implements CQLStatement
         public BatchStatement prepare(ClientState state)
         {
             List<ModificationStatement> statements = new 
ArrayList<>(parsedStatements.size());
-            parsedStatements.forEach(s -> 
statements.add(s.prepare(bindVariables)));
+            parsedStatements.forEach(s -> statements.add(s.prepare(state, 
bindVariables)));
 
             Attributes prepAttrs = attrs.prepare("[batch]", "[batch]");
             prepAttrs.collectMarkerSpecification(bindVariables);
diff --git a/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java 
b/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java
index be01481a45..fff6dd33df 100644
--- a/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java
@@ -31,6 +31,7 @@ import org.apache.cassandra.db.partitions.PartitionUpdate;
 import org.apache.cassandra.exceptions.InvalidRequestException;
 import org.apache.cassandra.schema.ColumnMetadata;
 import org.apache.cassandra.schema.TableMetadata;
+import org.apache.cassandra.service.ClientState;
 import org.apache.cassandra.utils.Pair;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
@@ -140,7 +141,8 @@ public class DeleteStatement extends ModificationStatement
 
 
         @Override
-        protected ModificationStatement prepareInternal(TableMetadata metadata,
+        protected ModificationStatement prepareInternal(ClientState state,
+                                                        TableMetadata metadata,
                                                         VariableSpecifications 
bindVariables,
                                                         Conditions conditions,
                                                         Attributes attrs)
@@ -160,7 +162,8 @@ public class DeleteStatement extends ModificationStatement
                 operations.add(op);
             }
 
-            StatementRestrictions restrictions = newRestrictions(metadata,
+            StatementRestrictions restrictions = newRestrictions(state,
+                                                                 metadata,
                                                                  bindVariables,
                                                                  operations,
                                                                  whereClause,
diff --git 
a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java 
b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
index ab36ec971b..c69cf4945a 100644
--- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
@@ -940,10 +940,10 @@ public abstract class ModificationStatement implements 
CQLStatement.SingleKeyspa
 
         public ModificationStatement prepare(ClientState state)
         {
-            return prepare(bindVariables);
+            return prepare(state, bindVariables);
         }
 
-        public ModificationStatement prepare(VariableSpecifications 
bindVariables)
+        public ModificationStatement prepare(ClientState state, 
VariableSpecifications bindVariables)
         {
             TableMetadata metadata = Schema.instance.validateTable(keyspace(), 
name());
 
@@ -952,7 +952,7 @@ public abstract class ModificationStatement implements 
CQLStatement.SingleKeyspa
 
             Conditions preparedConditions = prepareConditions(metadata, 
bindVariables);
 
-            return prepareInternal(metadata, bindVariables, 
preparedConditions, preparedAttributes);
+            return prepareInternal(state, metadata, bindVariables, 
preparedConditions, preparedAttributes);
         }
 
         /**
@@ -1011,7 +1011,8 @@ public abstract class ModificationStatement implements 
CQLStatement.SingleKeyspa
             return builder.build();
         }
 
-        protected abstract ModificationStatement prepareInternal(TableMetadata 
metadata,
+        protected abstract ModificationStatement prepareInternal(ClientState 
state,
+                                                                 TableMetadata 
metadata,
                                                                  
VariableSpecifications bindVariables,
                                                                  Conditions 
conditions,
                                                                  Attributes 
attrs);
@@ -1026,7 +1027,8 @@ public abstract class ModificationStatement implements 
CQLStatement.SingleKeyspa
          * @param conditions the conditions
          * @return the restrictions
          */
-        protected StatementRestrictions newRestrictions(TableMetadata metadata,
+        protected StatementRestrictions newRestrictions(ClientState state,
+                                                        TableMetadata metadata,
                                                         VariableSpecifications 
boundNames,
                                                         Operations operations,
                                                         WhereClause where,
@@ -1036,7 +1038,7 @@ public abstract class ModificationStatement implements 
CQLStatement.SingleKeyspa
                 throw new 
InvalidRequestException(CUSTOM_EXPRESSIONS_NOT_ALLOWED);
 
             boolean applyOnlyToStaticColumns = 
appliesOnlyToStaticColumns(operations, conditions);
-            return new StatementRestrictions(type, metadata, where, 
boundNames, applyOnlyToStaticColumns, false, false);
+            return new StatementRestrictions(state, type, metadata, where, 
boundNames, applyOnlyToStaticColumns, false, false);
         }
 
         public List<Pair<ColumnIdentifier, ColumnCondition.Raw>> 
getConditions()
diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java 
b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index 1b2e937189..75c12f065e 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -1075,17 +1075,17 @@ public class SelectStatement implements 
CQLStatement.SingleKeyspaceCqlStatement
         {
             // Cache locally for use by Guardrails
             this.state = state;
-            return prepare(false);
+            return prepare(state, false);
         }
 
-        public SelectStatement prepare(boolean forView) throws 
InvalidRequestException
+        public SelectStatement prepare(ClientState state, boolean forView) 
throws InvalidRequestException
         {
             TableMetadata table = Schema.instance.validateTable(keyspace(), 
name());
 
             List<Selectable> selectables = 
RawSelector.toSelectables(selectClause, table);
             boolean containsOnlyStaticColumns = selectOnlyStaticColumns(table, 
selectables);
 
-            StatementRestrictions restrictions = prepareRestrictions(table, 
bindVariables, containsOnlyStaticColumns, forView);
+            StatementRestrictions restrictions = prepareRestrictions(state, 
table, bindVariables, containsOnlyStaticColumns, forView);
 
             // If we order post-query, the sorted column needs to be in the 
ResultSet for sorting,
             // even if we don't ultimately ship them to the client 
(CASSANDRA-4911).
@@ -1215,12 +1215,14 @@ public class SelectStatement implements 
CQLStatement.SingleKeyspaceCqlStatement
          * @return the restrictions
          * @throws InvalidRequestException if a problem occurs while building 
the restrictions
          */
-        private StatementRestrictions prepareRestrictions(TableMetadata 
metadata,
+        private StatementRestrictions prepareRestrictions(ClientState state,
+                                                          TableMetadata 
metadata,
                                                           
VariableSpecifications boundNames,
                                                           boolean 
selectsOnlyStaticColumns,
                                                           boolean forView) 
throws InvalidRequestException
         {
-            return new StatementRestrictions(StatementType.SELECT,
+            return new StatementRestrictions(state,
+                                             StatementType.SELECT,
                                              metadata,
                                              whereClause,
                                              boundNames,
diff --git a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java 
b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
index 20df151849..258fbf354e 100644
--- a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
@@ -32,6 +32,7 @@ import org.apache.cassandra.db.Slice;
 import org.apache.cassandra.db.partitions.PartitionUpdate;
 import org.apache.cassandra.schema.ColumnMetadata;
 import org.apache.cassandra.schema.TableMetadata;
+import org.apache.cassandra.service.ClientState;
 import org.apache.cassandra.utils.ByteBufferUtil;
 import org.apache.cassandra.utils.Pair;
 import org.apache.commons.lang3.builder.ToStringBuilder;
@@ -135,7 +136,8 @@ public class UpdateStatement extends ModificationStatement
         }
 
         @Override
-        protected ModificationStatement prepareInternal(TableMetadata metadata,
+        protected ModificationStatement prepareInternal(ClientState state,
+                                                        TableMetadata metadata,
                                                         VariableSpecifications 
bindVariables,
                                                         Conditions conditions,
                                                         Attributes attrs)
@@ -176,7 +178,8 @@ public class UpdateStatement extends ModificationStatement
 
             boolean applyOnlyToStaticColumns = !hasClusteringColumnsSet && 
appliesOnlyToStaticColumns(operations, conditions);
 
-            StatementRestrictions restrictions = new 
StatementRestrictions(type,
+            StatementRestrictions restrictions = new 
StatementRestrictions(state,
+                                                                           
type,
                                                                            
metadata,
                                                                            
whereClause.build(),
                                                                            
bindVariables,
@@ -210,7 +213,8 @@ public class UpdateStatement extends ModificationStatement
         }
 
         @Override
-        protected ModificationStatement prepareInternal(TableMetadata metadata,
+        protected ModificationStatement prepareInternal(ClientState state,
+                                                        TableMetadata metadata,
                                                         VariableSpecifications 
bindVariables,
                                                         Conditions conditions,
                                                         Attributes attrs)
@@ -244,7 +248,8 @@ public class UpdateStatement extends ModificationStatement
 
             boolean applyOnlyToStaticColumns = !hasClusteringColumnsSet && 
appliesOnlyToStaticColumns(operations, conditions);
 
-            StatementRestrictions restrictions = new 
StatementRestrictions(type,
+            StatementRestrictions restrictions = new 
StatementRestrictions(state,
+                                                                           
type,
                                                                            
metadata,
                                                                            
whereClause.build(),
                                                                            
bindVariables,
@@ -291,7 +296,8 @@ public class UpdateStatement extends ModificationStatement
         }
 
         @Override
-        protected ModificationStatement prepareInternal(TableMetadata metadata,
+        protected ModificationStatement prepareInternal(ClientState state,
+                                                        TableMetadata metadata,
                                                         VariableSpecifications 
bindVariables,
                                                         Conditions conditions,
                                                         Attributes attrs)
@@ -309,7 +315,8 @@ public class UpdateStatement extends ModificationStatement
                 operations.add(operation);
             }
 
-            StatementRestrictions restrictions = newRestrictions(metadata,
+            StatementRestrictions restrictions = newRestrictions(state,
+                                                                 metadata,
                                                                  bindVariables,
                                                                  operations,
                                                                  whereClause,
diff --git 
a/src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java 
b/src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java
index 145c8fc838..dce054faa4 100644
--- 
a/src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java
+++ 
b/src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java
@@ -272,7 +272,8 @@ public final class CreateViewStatement extends 
AlterSchemaStatement
             throw ire("WHERE clause for materialized view '%s' cannot contain 
custom index expressions", viewName);
 
         StatementRestrictions restrictions =
-            new StatementRestrictions(StatementType.SELECT,
+            new StatementRestrictions(state,
+                                      StatementType.SELECT,
                                       table,
                                       whereClause,
                                       VariableSpecifications.empty(),
diff --git a/src/java/org/apache/cassandra/db/guardrails/EnableFlag.java 
b/src/java/org/apache/cassandra/db/guardrails/EnableFlag.java
index aba013a65f..7b5a63eb99 100644
--- a/src/java/org/apache/cassandra/db/guardrails/EnableFlag.java
+++ b/src/java/org/apache/cassandra/db/guardrails/EnableFlag.java
@@ -39,18 +39,31 @@ public class EnableFlag extends Guardrail
      * Creates a new {@link EnableFlag} guardrail.
      *
      * @param name        the identifying name of the guardrail
+     * @param reason      the optional description of the reason for guarding 
the operation
      * @param enabled     a {@link ClientState}-based supplier of boolean 
indicating whether the feature guarded by this
      *                    guardrail is enabled.
      * @param featureName The feature that is guarded by this guardrail (for 
reporting in error messages), {@link
      *                    EnableFlag#ensureEnabled(String, ClientState)} can 
specify a different {@code featureName}.
      */
-    public EnableFlag(String name, Predicate<ClientState> enabled, String 
featureName)
+    public EnableFlag(String name, @Nullable String reason, 
Predicate<ClientState> enabled, String featureName)
     {
-        super(name);
+        super(name, reason);
         this.enabled = enabled;
         this.featureName = featureName;
     }
 
+    /**
+     * Returns whether the guarded feature is enabled or not.
+     *
+     * @param state The client state, used to skip the check if the query is 
internal or is done by a superuser.
+     *              A {@code null} value means that the check should be done 
regardless of the query.
+     * @return {@code true} is the feature is enabled, {@code false} otherwise.
+     */
+    public boolean isEnabled(@Nullable ClientState state)
+    {
+        return !enabled(state) || enabled.test(state);
+    }
+
     /**
      * Aborts the operation if this guardrail is not enabled.
      *
@@ -80,7 +93,7 @@ public class EnableFlag extends Guardrail
      */
     public void ensureEnabled(String featureName, @Nullable ClientState state)
     {
-        if (enabled(state) && !enabled.test(state))
+        if (!isEnabled(state))
             fail(featureName + " is not allowed", state);
     }
 }
diff --git a/src/java/org/apache/cassandra/db/guardrails/Guardrail.java 
b/src/java/org/apache/cassandra/db/guardrails/Guardrail.java
index c058f10784..fbd1a5b880 100644
--- a/src/java/org/apache/cassandra/db/guardrails/Guardrail.java
+++ b/src/java/org/apache/cassandra/db/guardrails/Guardrail.java
@@ -51,6 +51,10 @@ public abstract class Guardrail
     /** A name identifying the guardrail (mainly for shipping with diagnostic 
events). */
     public final String name;
 
+    /** An optional description of the reason for guarding the operation. */
+    @Nullable
+    public final String reason;
+
     /** Minimum logging and triggering interval to avoid spamming downstream. 
*/
     private long minNotifyIntervalInMs = 0;
 
@@ -60,9 +64,10 @@ public abstract class Guardrail
     /** Time of last failure in milliseconds. */
     private volatile long lastFailInMs = 0;
 
-    Guardrail(String name)
+    Guardrail(String name, @Nullable String reason)
     {
         this.name = name;
+        this.reason = reason;
     }
 
     /**
@@ -138,8 +143,16 @@ public abstract class Guardrail
     @VisibleForTesting
     String decorateMessage(String message)
     {
-        // Add a prefix to error message so user knows what threw the warning 
or cause the failure
-        return String.format("Guardrail %s violated: %s", name, message);
+        // Add a prefix to error message so user knows what threw the warning 
or cause the failure.
+        String decoratedMessage = String.format("Guardrail %s violated: %s", 
name, message);
+
+        // Add the reason for the guardrail triggering, if there is any.
+        if (reason != null)
+        {
+            decoratedMessage += (message.endsWith(".") ? ' ' : ". ") + reason;
+        }
+
+        return decoratedMessage;
     }
 
     /**
diff --git a/src/java/org/apache/cassandra/db/guardrails/Guardrails.java 
b/src/java/org/apache/cassandra/db/guardrails/Guardrails.java
index 633f396ce5..4ea6312b4f 100644
--- a/src/java/org/apache/cassandra/db/guardrails/Guardrails.java
+++ b/src/java/org/apache/cassandra/db/guardrails/Guardrails.java
@@ -57,6 +57,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final MaxThreshold keyspaces =
     new MaxThreshold("keyspaces",
+                     null,
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getKeyspacesWarnThreshold(),
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getKeyspacesFailThreshold(),
                      (isWarning, what, value, threshold) ->
@@ -70,6 +71,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final MaxThreshold tables =
     new MaxThreshold("tables",
+                     null,
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getTablesWarnThreshold(),
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getTablesFailThreshold(),
                      (isWarning, what, value, threshold) ->
@@ -83,6 +85,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final MaxThreshold columnsPerTable =
     new MaxThreshold("columns_per_table",
+                     null,
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getColumnsPerTableWarnThreshold(),
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getColumnsPerTableFailThreshold(),
                      (isWarning, what, value, threshold) ->
@@ -93,6 +96,7 @@ public final class Guardrails implements GuardrailsMBean
 
     public static final MaxThreshold secondaryIndexesPerTable =
     new MaxThreshold("secondary_indexes_per_table",
+                     null,
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getSecondaryIndexesPerTableWarnThreshold(),
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getSecondaryIndexesPerTableFailThreshold(),
                      (isWarning, what, value, threshold) ->
@@ -106,6 +110,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final EnableFlag createSecondaryIndexesEnabled =
     new EnableFlag("secondary_indexes",
+                   null,
                    state -> 
CONFIG_PROVIDER.getOrCreate(state).getSecondaryIndexesEnabled(),
                    "User creation of secondary indexes");
 
@@ -114,6 +119,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final MaxThreshold materializedViewsPerTable =
     new MaxThreshold("materialized_views_per_table",
+                     null,
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getMaterializedViewsPerTableWarnThreshold(),
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getMaterializedViewsPerTableFailThreshold(),
                      (isWarning, what, value, threshold) ->
@@ -127,6 +133,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final Values<String> tableProperties =
     new Values<>("table_properties",
+                 null,
                  state -> 
CONFIG_PROVIDER.getOrCreate(state).getTablePropertiesWarned(),
                  state -> 
CONFIG_PROVIDER.getOrCreate(state).getTablePropertiesIgnored(),
                  state -> 
CONFIG_PROVIDER.getOrCreate(state).getTablePropertiesDisallowed(),
@@ -137,11 +144,13 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final EnableFlag userTimestampsEnabled =
     new EnableFlag("user_timestamps",
+                   null,
                    state -> 
CONFIG_PROVIDER.getOrCreate(state).getUserTimestampsEnabled(),
                    "User provided timestamps (USING TIMESTAMP)");
 
     public static final EnableFlag groupByEnabled =
     new EnableFlag("group_by",
+                   null,
                    state -> 
CONFIG_PROVIDER.getOrCreate(state).getGroupByEnabled(),
                    "GROUP BY functionality");
 
@@ -150,14 +159,16 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final EnableFlag alterTableEnabled =
     new EnableFlag("alter_table",
-                    state -> 
CONFIG_PROVIDER.getOrCreate(state).getAlterTableEnabled(),
-                    "User access to ALTER TABLE statement for column 
mutation");
+                   null,
+                   state -> 
CONFIG_PROVIDER.getOrCreate(state).getAlterTableEnabled(),
+                   "User access to ALTER TABLE statement for column mutation");
 
     /**
      * Guardrail disabling DROP / TRUNCATE TABLE behavior
      */
     public static final EnableFlag dropTruncateTableEnabled =
     new EnableFlag("drop_truncate_table_enabled",
+                   null,
                    state -> 
CONFIG_PROVIDER.getOrCreate(state).getDropTruncateTableEnabled(),
                    "DROP and TRUNCATE TABLE functionality");
 
@@ -166,6 +177,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final EnableFlag dropKeyspaceEnabled =
     new EnableFlag("drop_keyspace_enabled",
+                    null,
                     state -> 
CONFIG_PROVIDER.getOrCreate(state).getDropKeyspaceEnabled(),
                     "DROP KEYSPACE functionality");
 
@@ -174,6 +186,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final EnableFlag uncompressedTablesEnabled =
     new EnableFlag("uncompressed_tables_enabled",
+                   null,
                    state -> 
CONFIG_PROVIDER.getOrCreate(state).getUncompressedTablesEnabled(),
                    "Uncompressed table");
 
@@ -182,6 +195,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final EnableFlag compactTablesEnabled =
     new EnableFlag("compact_tables",
+                   null,
                    state -> 
CONFIG_PROVIDER.getOrCreate(state).getCompactTablesEnabled(),
                    "Creation of new COMPACT STORAGE tables");
 
@@ -190,6 +204,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final MaxThreshold pageSize =
     new MaxThreshold("page_size",
+                     null,
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getPageSizeWarnThreshold(),
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getPageSizeFailThreshold(),
                      (isWarning, what, value, threshold) ->
@@ -203,6 +218,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final MaxThreshold partitionKeysInSelect =
     new MaxThreshold("partition_keys_in_select",
+                     null,
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getPartitionKeysInSelectWarnThreshold(),
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getPartitionKeysInSelectFailThreshold(),
                      (isWarning, what, value, threshold) ->
@@ -218,6 +234,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final EnableFlag readBeforeWriteListOperationsEnabled =
     new EnableFlag("read_before_write_list_operations",
+                   null,
                    state -> 
CONFIG_PROVIDER.getOrCreate(state).getReadBeforeWriteListOperationsEnabled(),
                    "List operation requiring read before write");
 
@@ -226,6 +243,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final EnableFlag allowFilteringEnabled =
     new EnableFlag("allow_filtering",
+                   "ALLOW FILTERING can potentially visit all the data in the 
table and have unpredictable performance.",
                    state -> 
CONFIG_PROVIDER.getOrCreate(state).getAllowFilteringEnabled(),
                    "Querying with ALLOW FILTERING");
 
@@ -234,6 +252,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final EnableFlag simpleStrategyEnabled =
     new EnableFlag("simplestrategy",
+                   null,
                    state -> 
CONFIG_PROVIDER.getOrCreate(state).getSimpleStrategyEnabled(),
                    "SimpleStrategy");
 
@@ -242,6 +261,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final MaxThreshold inSelectCartesianProduct =
     new MaxThreshold("in_select_cartesian_product",
+                     null,
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getInSelectCartesianProductWarnThreshold(),
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getInSelectCartesianProductFailThreshold(),
                      (isWarning, what, value, threshold) ->
@@ -257,6 +277,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final Values<ConsistencyLevel> readConsistencyLevels =
     new Values<>("read_consistency_levels",
+                 null,
                  state -> 
CONFIG_PROVIDER.getOrCreate(state).getReadConsistencyLevelsWarned(),
                  state -> Collections.emptySet(),
                  state -> 
CONFIG_PROVIDER.getOrCreate(state).getReadConsistencyLevelsDisallowed(),
@@ -267,6 +288,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final Values<ConsistencyLevel> writeConsistencyLevels =
     new Values<>("write_consistency_levels",
+                 null,
                  state -> 
CONFIG_PROVIDER.getOrCreate(state).getWriteConsistencyLevelsWarned(),
                  state -> Collections.emptySet(),
                  state -> 
CONFIG_PROVIDER.getOrCreate(state).getWriteConsistencyLevelsDisallowed(),
@@ -277,6 +299,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final MaxThreshold columnValueSize =
     new MaxThreshold("column_value_size",
+                     null,
                      state -> 
sizeToBytes(CONFIG_PROVIDER.getOrCreate(state).getColumnValueSizeWarnThreshold()),
                      state -> 
sizeToBytes(CONFIG_PROVIDER.getOrCreate(state).getColumnValueSizeFailThreshold()),
                      (isWarning, what, value, threshold) ->
@@ -288,6 +311,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final MaxThreshold collectionSize =
     new MaxThreshold("collection_size",
+                     null,
                      state -> 
sizeToBytes(CONFIG_PROVIDER.getOrCreate(state).getCollectionSizeWarnThreshold()),
                      state -> 
sizeToBytes(CONFIG_PROVIDER.getOrCreate(state).getCollectionSizeFailThreshold()),
                      (isWarning, what, value, threshold) ->
@@ -299,6 +323,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final MaxThreshold itemsPerCollection =
     new MaxThreshold("items_per_collection",
+                     null,
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getItemsPerCollectionWarnThreshold(),
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getItemsPerCollectionFailThreshold(),
                      (isWarning, what, value, threshold) ->
@@ -310,6 +335,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final MaxThreshold fieldsPerUDT =
     new MaxThreshold("fields_per_udt",
+                     null,
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getFieldsPerUDTWarnThreshold(),
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getFieldsPerUDTFailThreshold(),
                      (isWarning, what, value, threshold) ->
@@ -324,6 +350,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final PercentageThreshold localDataDiskUsage =
     new PercentageThreshold("local_data_disk_usage",
+                            null,
                             state -> 
CONFIG_PROVIDER.getOrCreate(state).getDataDiskUsagePercentageWarnThreshold(),
                             state -> 
CONFIG_PROVIDER.getOrCreate(state).getDataDiskUsagePercentageFailThreshold(),
                             (isWarning, what, value, threshold) ->
@@ -339,6 +366,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final Predicates<InetAddressAndPort> replicaDiskUsage =
     new Predicates<>("replica_disk_usage",
+                     null,
                      state -> DiskUsageBroadcaster.instance::isStuffed,
                      state -> DiskUsageBroadcaster.instance::isFull,
                      // not using `value` because it represents replica 
address which should be hidden from client.
@@ -359,6 +387,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final MinThreshold minimumReplicationFactor =
     new MinThreshold("minimum_replication_factor",
+                     null,
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getMinimumReplicationFactorWarnThreshold(),
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getMinimumReplicationFactorFailThreshold(),
                      (isWarning, what, value, threshold) ->
@@ -370,6 +399,7 @@ public final class Guardrails implements GuardrailsMBean
      */
     public static final MaxThreshold maximumReplicationFactor =
     new MaxThreshold("maximum_replication_factor",
+                     null,
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getMaximumReplicationFactorWarnThreshold(),
                      state -> 
CONFIG_PROVIDER.getOrCreate(state).getMaximumReplicationFactorFailThreshold(),
                      (isWarning, what, value, threshold) ->
diff --git a/src/java/org/apache/cassandra/db/guardrails/MaxThreshold.java 
b/src/java/org/apache/cassandra/db/guardrails/MaxThreshold.java
index badaff784b..6203ee2bf5 100644
--- a/src/java/org/apache/cassandra/db/guardrails/MaxThreshold.java
+++ b/src/java/org/apache/cassandra/db/guardrails/MaxThreshold.java
@@ -19,6 +19,8 @@
 package org.apache.cassandra.db.guardrails;
 
 import java.util.function.ToLongFunction;
+import javax.annotation.Nullable;
+
 import org.apache.cassandra.service.ClientState;
 
 /**
@@ -30,16 +32,18 @@ public class MaxThreshold extends Threshold
      * Creates a new threshold guardrail.
      *
      * @param name            the identifying name of the guardrail
+     * @param reason          the optional description of the reason for 
guarding the operation
      * @param warnThreshold   a {@link ClientState}-based provider of the 
value above which a warning should be triggered.
      * @param failThreshold   a {@link ClientState}-based provider of the 
value above which the operation should be aborted.
      * @param messageProvider a function to generate the warning or error 
message if the guardrail is triggered
      */
     public MaxThreshold(String name,
+                        @Nullable String reason,
                         ToLongFunction<ClientState> warnThreshold,
                         ToLongFunction<ClientState> failThreshold,
                         Threshold.ErrorMessageProvider messageProvider)
     {
-        super(name, warnThreshold, failThreshold, messageProvider);
+        super(name, reason, warnThreshold, failThreshold, messageProvider);
     }
 
     @Override
diff --git a/src/java/org/apache/cassandra/db/guardrails/MinThreshold.java 
b/src/java/org/apache/cassandra/db/guardrails/MinThreshold.java
index 427f2777e8..72704cc51c 100644
--- a/src/java/org/apache/cassandra/db/guardrails/MinThreshold.java
+++ b/src/java/org/apache/cassandra/db/guardrails/MinThreshold.java
@@ -19,6 +19,8 @@
 package org.apache.cassandra.db.guardrails;
 
 import java.util.function.ToLongFunction;
+import javax.annotation.Nullable;
+
 import org.apache.cassandra.service.ClientState;
 
 /**
@@ -30,16 +32,18 @@ public class MinThreshold extends Threshold
      * Creates a new minimum threshold guardrail.
      *
      * @param name            the identifying name of the guardrail
+     * @param reason          the optional description of the reason for 
guarding the operation
      * @param warnThreshold   a {@link ClientState}-based provider of the 
value above which a warning should be triggered.
      * @param failThreshold   a {@link ClientState}-based provider of the 
value above which the operation should be aborted.
      * @param messageProvider a function to generate the warning or error 
message if the guardrail is triggered
      */
     public MinThreshold(String name,
+                        @Nullable String reason,
                         ToLongFunction<ClientState> warnThreshold,
                         ToLongFunction<ClientState> failThreshold,
                         Threshold.ErrorMessageProvider messageProvider)
     {
-        super(name, warnThreshold, failThreshold, messageProvider);
+        super(name, reason, warnThreshold, failThreshold, messageProvider);
     }
 
     @Override
diff --git 
a/src/java/org/apache/cassandra/db/guardrails/PercentageThreshold.java 
b/src/java/org/apache/cassandra/db/guardrails/PercentageThreshold.java
index 6f866c6ec0..c316276596 100644
--- a/src/java/org/apache/cassandra/db/guardrails/PercentageThreshold.java
+++ b/src/java/org/apache/cassandra/db/guardrails/PercentageThreshold.java
@@ -20,6 +20,8 @@ package org.apache.cassandra.db.guardrails;
 
 import java.util.function.ToLongFunction;
 
+import javax.annotation.Nullable;
+
 import org.apache.cassandra.service.ClientState;
 
 /**
@@ -33,16 +35,18 @@ public class PercentageThreshold extends MaxThreshold
      * Creates a new threshold guardrail.
      *
      * @param name            the identifying name of the guardrail
+     * @param reason          the optional description of the reason for 
guarding the operation
      * @param warnThreshold   a {@link ClientState}-based provider of the 
value above which a warning should be triggered.
      * @param failThreshold   a {@link ClientState}-based provider of the 
value above which the operation should be aborted.
      * @param messageProvider a function to generate the warning or error 
message if the guardrail is triggered
      */
     public PercentageThreshold(String name,
+                               @Nullable String reason,
                                ToLongFunction<ClientState> warnThreshold,
                                ToLongFunction<ClientState> failThreshold,
                                ErrorMessageProvider messageProvider)
     {
-        super(name, warnThreshold, failThreshold, messageProvider);
+        super(name, reason, warnThreshold, failThreshold, messageProvider);
     }
 
     @Override
diff --git a/src/java/org/apache/cassandra/db/guardrails/Predicates.java 
b/src/java/org/apache/cassandra/db/guardrails/Predicates.java
index 13be9e9302..ab08560218 100644
--- a/src/java/org/apache/cassandra/db/guardrails/Predicates.java
+++ b/src/java/org/apache/cassandra/db/guardrails/Predicates.java
@@ -56,16 +56,18 @@ public class Predicates<T> extends Guardrail
      * Creates a new {@link Predicates} guardrail.
      *
      * @param name             the identifying name of the guardrail
+     * @param reason           the optional description of the reason for 
guarding the operation
      * @param warnPredicate    a {@link ClientState}-based predicate provider 
that is used to check if given value should trigger a warning.
      * @param failurePredicate a {@link ClientState}-based predicate provider 
that is used to check if given value should trigger a failure.
      * @param messageProvider  a function to generate the warning or error 
message if the guardrail is triggered
      */
     Predicates(String name,
+               @Nullable String reason,
                Function<ClientState, Predicate<T>> warnPredicate,
                Function<ClientState, Predicate<T>> failurePredicate,
                MessageProvider<T> messageProvider)
     {
-        super(name);
+        super(name, reason);
         this.warnPredicate = warnPredicate;
         this.failurePredicate = failurePredicate;
         this.messageProvider = messageProvider;
diff --git a/src/java/org/apache/cassandra/db/guardrails/Threshold.java 
b/src/java/org/apache/cassandra/db/guardrails/Threshold.java
index b671907c62..257ab013b7 100644
--- a/src/java/org/apache/cassandra/db/guardrails/Threshold.java
+++ b/src/java/org/apache/cassandra/db/guardrails/Threshold.java
@@ -41,16 +41,18 @@ public abstract class Threshold extends Guardrail
      * Creates a new threshold guardrail.
      *
      * @param name            the identifying name of the guardrail
+     * @param reason          the optional description of the reason for 
guarding the operation
      * @param warnThreshold   a {@link ClientState}-based provider of the 
value above which a warning should be triggered.
      * @param failThreshold   a {@link ClientState}-based provider of the 
value above which the operation should be aborted.
      * @param messageProvider a function to generate the warning or error 
message if the guardrail is triggered
      */
     public Threshold(String name,
+                     @Nullable String reason,
                      ToLongFunction<ClientState> warnThreshold,
                      ToLongFunction<ClientState> failThreshold,
                      ErrorMessageProvider messageProvider)
     {
-        super(name);
+        super(name, reason);
         this.warnThreshold = warnThreshold;
         this.failThreshold = failThreshold;
         this.messageProvider = messageProvider;
diff --git a/src/java/org/apache/cassandra/db/guardrails/Values.java 
b/src/java/org/apache/cassandra/db/guardrails/Values.java
index f46e3af035..9504a3d63b 100644
--- a/src/java/org/apache/cassandra/db/guardrails/Values.java
+++ b/src/java/org/apache/cassandra/db/guardrails/Values.java
@@ -47,18 +47,20 @@ public class Values<T> extends Guardrail
      * Creates a new values guardrail.
      *
      * @param name             the identifying name of the guardrail
+     * @param reason           the optional description of the reason for 
guarding the operation
      * @param warnedValues     a {@link ClientState}-based provider of the 
values for which a warning is triggered.
      * @param ignoredValues    a {@link ClientState}-based provider of the 
values that are ignored.
      * @param disallowedValues a {@link ClientState}-based provider of the 
values that are disallowed.
      * @param what             The feature that is guarded by this guardrail 
(for reporting in error messages).
      */
     public Values(String name,
+                  @Nullable String reason,
                   Function<ClientState, Set<T>> warnedValues,
                   Function<ClientState, Set<T>> ignoredValues,
                   Function<ClientState, Set<T>> disallowedValues,
                   String what)
     {
-        super(name);
+        super(name, reason);
         this.warnedValues = warnedValues;
         this.ignoredValues = ignoredValues;
         this.disallowedValues = disallowedValues;
diff --git a/src/java/org/apache/cassandra/db/view/View.java 
b/src/java/org/apache/cassandra/db/view/View.java
index d813d0e668..a3ecc33d79 100644
--- a/src/java/org/apache/cassandra/db/view/View.java
+++ b/src/java/org/apache/cassandra/db/view/View.java
@@ -35,6 +35,7 @@ import org.apache.cassandra.schema.KeyspaceMetadata;
 import org.apache.cassandra.schema.Schema;
 import org.apache.cassandra.schema.TableMetadataRef;
 import org.apache.cassandra.schema.ViewMetadata;
+import org.apache.cassandra.service.ClientState;
 import org.apache.cassandra.utils.FBUtilities;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -177,7 +178,7 @@ public class View
 
             rawSelect.setBindVariables(Collections.emptyList());
 
-            select = rawSelect.prepare(true);
+            select = rawSelect.prepare(ClientState.forInternalCalls(), true);
         }
 
         return select;
diff --git 
a/test/distributed/org/apache/cassandra/distributed/fuzz/SSTableGenerator.java 
b/test/distributed/org/apache/cassandra/distributed/fuzz/SSTableGenerator.java
index 627d3606da..c7d0fbbc96 100644
--- 
a/test/distributed/org/apache/cassandra/distributed/fuzz/SSTableGenerator.java
+++ 
b/test/distributed/org/apache/cassandra/distributed/fuzz/SSTableGenerator.java
@@ -306,7 +306,8 @@ public class SSTableGenerator
                                                  new 
AbstractMarker.Raw(values.size() - 1)));
         }
 
-        StatementRestrictions restrictions = new 
StatementRestrictions(StatementType.DELETE,
+        StatementRestrictions restrictions = new StatementRestrictions(null,
+                                                                       
StatementType.DELETE,
                                                                        
metadata,
                                                                        
builder.build(),
                                                                        new 
VariableSpecifications(variableNames),
diff --git 
a/test/unit/org/apache/cassandra/db/guardrails/GuardrailAllowFilteringTest.java 
b/test/unit/org/apache/cassandra/db/guardrails/GuardrailAllowFilteringTest.java
index c46498c01f..5eca8393a9 100644
--- 
a/test/unit/org/apache/cassandra/db/guardrails/GuardrailAllowFilteringTest.java
+++ 
b/test/unit/org/apache/cassandra/db/guardrails/GuardrailAllowFilteringTest.java
@@ -22,8 +22,11 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
+import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
+import org.apache.cassandra.exceptions.InvalidRequestException;
 import org.apache.cassandra.schema.SchemaConstants;
 import org.apache.cassandra.schema.SchemaKeyspaceTables;
+import org.apache.cassandra.service.ClientState;
 
 public class GuardrailAllowFilteringTest extends GuardrailTester
 {
@@ -33,7 +36,7 @@ public class GuardrailAllowFilteringTest extends 
GuardrailTester
     public void setupTest()
     {
         createTable("CREATE TABLE %s (k int PRIMARY KEY, a int, b int)");
-        enableState = getGuardrial();
+        enableState = getGuardrail();
     }
 
     @After
@@ -47,7 +50,7 @@ public class GuardrailAllowFilteringTest extends 
GuardrailTester
         guardrails().setAllowFilteringEnabled(allowFilteringEnabled);
     }
 
-    private boolean getGuardrial()
+    private boolean getGuardrail()
     {
         return guardrails().getAllowFilteringEnabled();
     }
@@ -91,4 +94,24 @@ public class GuardrailAllowFilteringTest extends 
GuardrailTester
                                   SchemaKeyspaceTables.TABLES,
                                   currentTable()));
     }
+
+    @Test
+    public void testRequiredAllowFiltering()
+    {
+        setGuardrail(true);
+        assertRequiredAllowFilteringThrows(systemClientState, 
StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE);
+        assertRequiredAllowFilteringThrows(superClientState, 
StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE);
+        assertRequiredAllowFilteringThrows(userClientState, 
StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE);
+
+        setGuardrail(false);
+        assertRequiredAllowFilteringThrows(systemClientState, 
StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE);
+        assertRequiredAllowFilteringThrows(superClientState, 
StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE);
+        assertRequiredAllowFilteringThrows(userClientState, 
StatementRestrictions.CANNOT_USE_ALLOW_FILTERING_MESSAGE);
+    }
+
+    private void assertRequiredAllowFilteringThrows(ClientState state, String 
message)
+    {
+        String query = "SELECT * FROM %s WHERE a = 5";
+        assertThrows(() -> execute(state, query), 
InvalidRequestException.class, message);
+    }
 }
diff --git a/test/unit/org/apache/cassandra/db/guardrails/GuardrailTester.java 
b/test/unit/org/apache/cassandra/db/guardrails/GuardrailTester.java
index 54523743b7..5e0cae224d 100644
--- a/test/unit/org/apache/cassandra/db/guardrails/GuardrailTester.java
+++ b/test/unit/org/apache/cassandra/db/guardrails/GuardrailTester.java
@@ -353,9 +353,17 @@ public abstract class GuardrailTester extends CQLTester
 
             if (guardrail != null)
             {
-                String prefix = guardrail.decorateMessage("");
-                assertTrue(format("Full error message '%s' doesn't start with 
the prefix '%s'", e.getMessage(), prefix),
-                           e.getMessage().startsWith(prefix));
+                String message = e.getMessage();
+                String prefix = guardrail.decorateMessage("").replace(". " + 
guardrail.reason, "");
+                assertTrue(format("Full error message '%s' doesn't start with 
the prefix '%s'", message, prefix),
+                           message.startsWith(prefix));
+
+                String reason = guardrail.reason;
+                if (reason != null)
+                {
+                    assertTrue(format("Full error message '%s' doesn't end 
with the reason '%s'", message, reason),
+                               message.endsWith(reason));
+                }
             }
 
             assertTrue(format("Full error message '%s' does not contain 
expected message '%s'", e.getMessage(), failMessage),
@@ -412,9 +420,16 @@ public abstract class GuardrailTester extends CQLTester
             String warning = warnings.get(i);
             if (guardrail != null)
             {
-                String prefix = guardrail.decorateMessage("");
+                String prefix = guardrail.decorateMessage("").replace(". " + 
guardrail.reason, "");
                 assertTrue(format("Warning log message '%s' doesn't start with 
the prefix '%s'", warning, prefix),
                            warning.startsWith(prefix));
+
+                String reason = guardrail.reason;
+                if (reason != null)
+                {
+                    assertTrue(format("Warning log message '%s' doesn't end 
with the reason '%s'", warning, reason),
+                               warning.endsWith(reason));
+                }
             }
 
             assertTrue(format("Warning log message '%s' does not contain 
expected message '%s'", warning, message),
diff --git 
a/test/unit/org/apache/cassandra/db/guardrails/GuardrailsConfigProviderTest.java
 
b/test/unit/org/apache/cassandra/db/guardrails/GuardrailsConfigProviderTest.java
index e99b736b03..36fbc7cd08 100644
--- 
a/test/unit/org/apache/cassandra/db/guardrails/GuardrailsConfigProviderTest.java
+++ 
b/test/unit/org/apache/cassandra/db/guardrails/GuardrailsConfigProviderTest.java
@@ -37,6 +37,7 @@ public class GuardrailsConfigProviderTest extends 
GuardrailTester
         String name = getClass().getCanonicalName() + '$' + 
CustomProvider.class.getSimpleName();
         GuardrailsConfigProvider provider = 
GuardrailsConfigProvider.build(name);
         MaxThreshold guard = new MaxThreshold("test_guardrail",
+                                        "Some reason",
                                         state -> 
provider.getOrCreate(state).getTablesWarnThreshold(),
                                         state -> 
provider.getOrCreate(state).getTablesFailThreshold(),
                                         (isWarn, what, v, t) -> format("%s: 
for %s, %s > %s",
diff --git a/test/unit/org/apache/cassandra/db/guardrails/GuardrailsTest.java 
b/test/unit/org/apache/cassandra/db/guardrails/GuardrailsTest.java
index 5c7e724abb..9cea89b4b5 100644
--- a/test/unit/org/apache/cassandra/db/guardrails/GuardrailsTest.java
+++ b/test/unit/org/apache/cassandra/db/guardrails/GuardrailsTest.java
@@ -38,7 +38,7 @@ import static org.junit.Assert.assertTrue;
 public class GuardrailsTest extends GuardrailTester
 {
     public static final int DISABLED = -1;
-
+    public static final String REASON = "Testing";
 
     private void testDisabledThreshold(Threshold guard) throws Throwable
     {
@@ -58,17 +58,18 @@ public class GuardrailsTest extends GuardrailTester
     public void testDisabledMaxThreshold() throws Throwable
     {
         Threshold.ErrorMessageProvider errorMessageProvider = (isWarn, what, 
v, t) -> "Should never trigger";
-        testDisabledThreshold(new MaxThreshold("x", state -> DISABLED, state 
-> DISABLED, errorMessageProvider));
+        testDisabledThreshold(new MaxThreshold("x", REASON, state -> DISABLED, 
state -> DISABLED, errorMessageProvider));
     }
 
     @Test
     public void testMaxThreshold() throws Throwable
     {
         MaxThreshold guard = new MaxThreshold("x",
-                                        state -> 10,
-                                        state -> 100,
-                                        (isWarn, featureName, v, t) -> 
format("%s: for %s, %s > %s",
-                                                                       isWarn 
? "Warning" : "Aborting", featureName, v, t));
+                                              REASON,
+                                              state -> 10,
+                                              state -> 100,
+                                              (isWarn, featureName, v, t) -> 
format("%s: for %s, %s > %s",
+                                                                             
isWarn ? "Warning" : "Aborting", featureName, v, t));
 
         assertTrue(guard.enabled(userClientState));
 
@@ -91,10 +92,11 @@ public class GuardrailsTest extends GuardrailTester
     public void testWarnOnlyMaxThreshold() throws Throwable
     {
         MaxThreshold guard = new MaxThreshold("x",
-                                        state -> 10,
-                                        state -> DISABLED,
-                                        (isWarn, featureName, v, t) -> 
format("%s: for %s, %s > %s",
-                                                                       isWarn 
? "Warning" : "Aborting", featureName, v, t));
+                                              REASON,
+                                              state -> 10,
+                                              state -> DISABLED,
+                                              (isWarn, featureName, v, t) -> 
format("%s: for %s, %s > %s",
+                                                                             
isWarn ? "Warning" : "Aborting", featureName, v, t));
 
         assertTrue(guard.enabled(userClientState));
 
@@ -109,10 +111,11 @@ public class GuardrailsTest extends GuardrailTester
     public void testFailOnlyMaxThreshold() throws Throwable
     {
         MaxThreshold guard = new MaxThreshold("x",
-                                        state -> DISABLED,
-                                        state -> 10,
-                                        (isWarn, featureName, v, t) -> 
format("%s: for %s, %s > %s",
-                                                                       isWarn 
? "Warning" : "Aborting", featureName, v, t));
+                                              REASON,
+                                              state -> DISABLED,
+                                              state -> 10,
+                                              (isWarn, featureName, v, t) -> 
format("%s: for %s, %s > %s",
+                                                                             
isWarn ? "Warning" : "Aborting", featureName, v, t));
 
         assertTrue(guard.enabled(userClientState));
 
@@ -127,10 +130,11 @@ public class GuardrailsTest extends GuardrailTester
     public void testMaxThresholdUsers() throws Throwable
     {
         MaxThreshold guard = new MaxThreshold("x",
-                                        state -> 10,
-                                        state -> 100,
-                                        (isWarn, featureName, v, t) -> 
format("%s: for %s, %s > %s",
-                                                                       isWarn 
? "Warning" : "Failure", featureName, v, t));
+                                              REASON,
+                                              state -> 10,
+                                              state -> 100,
+                                              (isWarn, featureName, v, t) -> 
format("%s: for %s, %s > %s",
+                                                                             
isWarn ? "Warning" : "Failure", featureName, v, t));
 
         // value under both thresholds
         assertValid(() -> guard.guard(5, "x", false, null));
@@ -156,13 +160,14 @@ public class GuardrailsTest extends GuardrailTester
     public void testDisabledMinThreshold() throws Throwable
     {
         Threshold.ErrorMessageProvider errorMessageProvider = (isWarn, what, 
v, t) -> "Should never trigger";
-        testDisabledThreshold(new MinThreshold("x", state -> DISABLED, state 
-> DISABLED, errorMessageProvider));
+        testDisabledThreshold(new MinThreshold("x", REASON, state -> DISABLED, 
state -> DISABLED, errorMessageProvider));
     }
 
     @Test
     public void testMinThreshold() throws Throwable
     {
         MinThreshold guard = new MinThreshold("x",
+                                              REASON,
                                               state -> 100,
                                               state -> 10,
                                               (isWarn, what, v, t) -> 
format("%s: for %s, %s < %s",
@@ -189,6 +194,7 @@ public class GuardrailsTest extends GuardrailTester
     public void testWarnOnlyMinThreshold() throws Throwable
     {
         MinThreshold guard = new MinThreshold("x",
+                                              REASON,
                                               state -> 10,
                                               state -> DISABLED,
                                               (isWarn, what, v, t) -> 
format("%s: for %s, %s < %s",
@@ -207,6 +213,7 @@ public class GuardrailsTest extends GuardrailTester
     public void testFailOnlyMinThreshold() throws Throwable
     {
         MinThreshold guard = new MinThreshold("x",
+                                              REASON,
                                               state -> DISABLED,
                                               state -> 10,
                                               (isWarn, what, v, t) -> 
format("%s: for %s, %s < %s",
@@ -225,6 +232,7 @@ public class GuardrailsTest extends GuardrailTester
     public void testMinThresholdUsers() throws Throwable
     {
         MinThreshold guard = new MinThreshold("x",
+                                              REASON,
                                               state -> 100,
                                               state -> 10,
                                               (isWarn, what, v, t) -> 
format("%s: for %s, %s < %s",
@@ -253,23 +261,23 @@ public class GuardrailsTest extends GuardrailTester
     @Test
     public void testEnableFlag() throws Throwable
     {
-        assertFails(() -> new EnableFlag("x", state -> false, 
"X").ensureEnabled(userClientState), "X is not allowed");
-        assertValid(() -> new EnableFlag("x", state -> true, 
"X").ensureEnabled(userClientState));
+        assertFails(() -> new EnableFlag("x", REASON, state -> false, 
"X").ensureEnabled(userClientState), "X is not allowed");
+        assertValid(() -> new EnableFlag("x", REASON, state -> true, 
"X").ensureEnabled(userClientState));
 
-        assertFails(() -> new EnableFlag("x", state -> false, 
"X").ensureEnabled("Y", userClientState), "Y is not allowed");
-        assertValid(() -> new EnableFlag("x", state -> true, 
"X").ensureEnabled("Y", userClientState));
+        assertFails(() -> new EnableFlag("x", REASON, state -> false, 
"X").ensureEnabled("Y", userClientState), "Y is not allowed");
+        assertValid(() -> new EnableFlag("x", REASON, state -> true, 
"X").ensureEnabled("Y", userClientState));
     }
 
     @Test
     public void testEnableFlagUsers() throws Throwable
     {
-        EnableFlag enabled = new EnableFlag("x", state -> true, "X");
+        EnableFlag enabled = new EnableFlag("x", REASON, state -> true, "X");
         assertValid(() -> enabled.ensureEnabled(null));
         assertValid(() -> enabled.ensureEnabled(userClientState));
         assertValid(() -> enabled.ensureEnabled(systemClientState));
         assertValid(() -> enabled.ensureEnabled(superClientState));
 
-        EnableFlag disabled = new EnableFlag("x", state -> false, "X");
+        EnableFlag disabled = new EnableFlag("x", REASON, state -> false, "X");
         assertFails(() -> disabled.ensureEnabled(userClientState), "X is not 
allowed");
         assertValid(() -> disabled.ensureEnabled(systemClientState));
         assertValid(() -> disabled.ensureEnabled(superClientState));
@@ -280,6 +288,7 @@ public class GuardrailsTest extends GuardrailTester
     {
         // Using a sorted set below to ensure the order in the warning message 
checked below is not random
         Values<Integer> warned = new Values<>("x",
+                                              REASON,
                                               state -> insertionOrderedSet(4, 
6, 20),
                                               state -> Collections.emptySet(),
                                               state -> Collections.emptySet(),
@@ -300,6 +309,7 @@ public class GuardrailsTest extends GuardrailTester
     {
         // Using a sorted set below to ensure the order in the error message 
checked below are not random
         Values<Integer> ignored = new Values<>("x",
+                                               REASON,
                                                state -> Collections.emptySet(),
                                                state -> insertionOrderedSet(4, 
6, 20),
                                                state -> Collections.emptySet(),
@@ -334,6 +344,7 @@ public class GuardrailsTest extends GuardrailTester
     {
         // Using a sorted set below to ensure the order in the error message 
checked below are not random
         Values<Integer> disallowed = new Values<>("x",
+                                                  REASON,
                                                   state -> 
Collections.emptySet(),
                                                   state -> 
Collections.emptySet(),
                                                   state -> 
insertionOrderedSet(4, 6, 20),
@@ -359,6 +370,7 @@ public class GuardrailsTest extends GuardrailTester
     public void testValuesUsers() throws Throwable
     {
         Values<Integer> disallowed = new Values<>("x",
+                                                  REASON,
                                                   state -> 
Collections.singleton(2),
                                                   state -> 
Collections.singleton(3),
                                                   state -> 
Collections.singleton(4),
@@ -396,6 +408,7 @@ public class GuardrailsTest extends GuardrailTester
     public void testPredicates() throws Throwable
     {
         Predicates<Integer> guard = new Predicates<>("x",
+                                                     REASON,
                                                      state -> x -> x > 10,
                                                      state -> x -> x > 100,
                                                      (isWarn, value) -> 
format("%s: %s", isWarn ? "Warning" : "Aborting", value));
@@ -412,6 +425,7 @@ public class GuardrailsTest extends GuardrailTester
     public void testPredicatesUsers() throws Throwable
     {
         Predicates<Integer> guard = new Predicates<>("x",
+                                                     REASON,
                                                      state -> x -> x > 10,
                                                      state -> x -> x > 100,
                                                      (isWarn, value) -> 
format("%s: %s", isWarn ? "Warning" : "Aborting", value));


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to