mirror of https://github.com/apache/cassandra
Runtime-configurable YAML option to prohibit USE statements
patch by Caleb Rackliffe; reviewed by David Capwell for CASSANDRA-17318
This commit is contained in:
parent
df16b3750d
commit
28eea6e8cd
|
|
@ -1,4 +1,5 @@
|
|||
4.1
|
||||
* Runtime-configurable YAML option to prohibit USE statements (CASSANDRA-17318)
|
||||
* When streaming sees a ClosedChannelException this triggers the disk failure policy (CASSANDRA-17116)
|
||||
* Add a virtual table for exposing prepared statements metrics (CASSANDRA-17224)
|
||||
* Remove python 2.x support from cqlsh (CASSANDRA-17242)
|
||||
|
|
|
|||
|
|
@ -1550,6 +1550,9 @@ enable_transient_replication: false
|
|||
# 'ALTER ... DROP COMPACT STORAGE' is considered experimental and is not recommended for production use.
|
||||
enable_drop_compact_storage: false
|
||||
|
||||
# Whether or not USE <keyspace> is allowed. This is enabled by default to avoid failure on upgrade.
|
||||
#use_statements_enabled: true
|
||||
|
||||
# When the client triggers a protocol exception or unknown issue (Cassandra bug) we increment
|
||||
# a client metric showing this; this logic will exclude specific subnets from updating these
|
||||
# metrics
|
||||
|
|
|
|||
|
|
@ -411,6 +411,8 @@ public class Config
|
|||
|
||||
public volatile boolean enable_drop_compact_storage = false;
|
||||
|
||||
public volatile boolean use_statements_enabled = true;
|
||||
|
||||
/**
|
||||
* Optionally disable asynchronous UDF execution.
|
||||
* Disabling asynchronous UDF execution also implicitly disables the security-manager!
|
||||
|
|
|
|||
|
|
@ -70,7 +70,6 @@ import org.apache.cassandra.security.EncryptionContext;
|
|||
import org.apache.cassandra.security.SSLFactory;
|
||||
import org.apache.cassandra.service.CacheService.CacheType;
|
||||
import org.apache.cassandra.service.paxos.Paxos;
|
||||
import org.apache.cassandra.utils.Clock;
|
||||
import org.apache.cassandra.utils.FBUtilities;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
|
@ -3810,4 +3809,18 @@ public class DatabaseDescriptor
|
|||
|
||||
conf.minimum_keyspace_rf = value;
|
||||
}
|
||||
|
||||
public static boolean getUseStatementsEnabled()
|
||||
{
|
||||
return conf.use_statements_enabled;
|
||||
}
|
||||
|
||||
public static void setUseStatementsEnabled(boolean enabled)
|
||||
{
|
||||
if (enabled != conf.use_statements_enabled)
|
||||
{
|
||||
logger.info("Setting use_statements_enabled to {}", enabled);
|
||||
conf.use_statements_enabled = enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,10 @@ package org.apache.cassandra.cql3.statements;
|
|||
|
||||
import org.apache.cassandra.audit.AuditLogContext;
|
||||
import org.apache.cassandra.audit.AuditLogEntryType;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.cql3.CQLStatement;
|
||||
import org.apache.cassandra.cql3.QueryOptions;
|
||||
import org.apache.cassandra.cql3.QueryProcessor;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.exceptions.UnauthorizedException;
|
||||
import org.apache.cassandra.transport.messages.ResultMessage;
|
||||
|
|
@ -29,6 +31,7 @@ import org.apache.cassandra.service.QueryState;
|
|||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
|
||||
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
|
||||
|
||||
public class UseStatement extends CQLStatement.Raw implements CQLStatement
|
||||
|
|
@ -50,12 +53,15 @@ public class UseStatement extends CQLStatement.Raw implements CQLStatement
|
|||
state.validateLogin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(ClientState state) throws InvalidRequestException
|
||||
{
|
||||
checkTrue(DatabaseDescriptor.getUseStatementsEnabled(), "USE statements prohibited. (see use_statements_enabled in cassandra.yaml)");
|
||||
}
|
||||
|
||||
public ResultMessage execute(QueryState state, QueryOptions options, long queryStartNanoTime) throws InvalidRequestException
|
||||
{
|
||||
QueryProcessor.metrics.useStatementsExecuted.inc();
|
||||
state.getClientState().setKeyspace(keyspace);
|
||||
return new ResultMessage.SetKeyspace(keyspace);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ public class CQLMetrics
|
|||
public final Counter preparedStatementsExecuted;
|
||||
public final Counter preparedStatementsEvicted;
|
||||
|
||||
public final Counter useStatementsExecuted;
|
||||
|
||||
public final Gauge<Integer> preparedStatementsCount;
|
||||
public final Gauge<Double> preparedStatementsRatio;
|
||||
|
||||
|
|
@ -41,13 +43,9 @@ public class CQLMetrics
|
|||
preparedStatementsExecuted = Metrics.counter(factory.createMetricName("PreparedStatementsExecuted"));
|
||||
preparedStatementsEvicted = Metrics.counter(factory.createMetricName("PreparedStatementsEvicted"));
|
||||
|
||||
preparedStatementsCount = Metrics.register(factory.createMetricName("PreparedStatementsCount"), new Gauge<Integer>()
|
||||
{
|
||||
public Integer getValue()
|
||||
{
|
||||
return QueryProcessor.preparedStatementsCount();
|
||||
}
|
||||
});
|
||||
useStatementsExecuted = Metrics.counter(factory.createMetricName("UseStatementsExecuted"));
|
||||
|
||||
preparedStatementsCount = Metrics.register(factory.createMetricName("PreparedStatementsCount"), QueryProcessor::preparedStatementsCount);
|
||||
preparedStatementsRatio = Metrics.register(factory.createMetricName("PreparedStatementsRatio"), new RatioGauge()
|
||||
{
|
||||
public Ratio getRatio()
|
||||
|
|
|
|||
|
|
@ -416,7 +416,6 @@ public class StorageProxy implements StorageProxyMBean
|
|||
* {@link ConsistencyLevel#LOCAL_SERIAL}).
|
||||
* @param consistencyForReplayCommits the consistency for the commit phase of "replayed" in-progress operations.
|
||||
* @param consistencyForCommit the consistency for the commit phase of _this_ operation update.
|
||||
* @param state the client state.
|
||||
* @param queryStartNanoTime the nano time for the start of the query this is part of. This is the base time for
|
||||
* timeouts.
|
||||
* @param casMetrics the metrics to update for this operation.
|
||||
|
|
@ -2981,4 +2980,16 @@ public class StorageProxy implements StorageProxyMBean
|
|||
{
|
||||
return Paxos.getPaxosVariant().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getUseStatementsEnabled()
|
||||
{
|
||||
return DatabaseDescriptor.getUseStatementsEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUseStatementsEnabled(boolean enabled)
|
||||
{
|
||||
DatabaseDescriptor.setUseStatementsEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,4 +121,7 @@ public interface StorageProxyMBean
|
|||
|
||||
void setPaxosVariant(String variant);
|
||||
String getPaxosVariant();
|
||||
|
||||
boolean getUseStatementsEnabled();
|
||||
void setUseStatementsEnabled(boolean enabled);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,15 +17,41 @@
|
|||
*/
|
||||
package org.apache.cassandra.cql3.validation.operations;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.cql3.CQLTester;
|
||||
import org.apache.cassandra.cql3.QueryProcessor;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class UseTest extends CQLTester
|
||||
{
|
||||
@Test
|
||||
public void testUseStatementWithBindVariable() throws Throwable
|
||||
{
|
||||
DatabaseDescriptor.setUseStatementsEnabled(true);
|
||||
assertInvalidSyntaxMessage("Bind variables cannot be used for keyspace names", "USE ?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRejectUseStatementWhenProhibited() throws Throwable
|
||||
{
|
||||
long useCountBefore = QueryProcessor.metrics.useStatementsExecuted.getCount();
|
||||
|
||||
try
|
||||
{
|
||||
DatabaseDescriptor.setUseStatementsEnabled(false);
|
||||
execute("USE cql_test_keyspace");
|
||||
fail("expected USE statement to fail with use_statements_enabled = false");
|
||||
}
|
||||
catch (InvalidRequestException e)
|
||||
{
|
||||
assertEquals(useCountBefore, QueryProcessor.metrics.useStatementsExecuted.getCount());
|
||||
Assertions.assertThat(e).hasMessageContaining("USE statements prohibited");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,12 +20,14 @@ package org.apache.cassandra.metrics;
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.datastax.driver.core.Cluster;
|
||||
import com.datastax.driver.core.PreparedStatement;
|
||||
import com.datastax.driver.core.Session;
|
||||
import com.datastax.driver.core.exceptions.InvalidQueryException;
|
||||
import org.apache.cassandra.SchemaLoader;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.schema.Schema;
|
||||
|
|
@ -33,12 +35,12 @@ import org.apache.cassandra.cql3.QueryProcessor;
|
|||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
import org.apache.cassandra.service.EmbeddedCassandraService;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class CQLMetricsTest extends SchemaLoader
|
||||
{
|
||||
private static EmbeddedCassandraService cassandra;
|
||||
|
||||
private static Cluster cluster;
|
||||
private static Session session;
|
||||
|
||||
|
|
@ -47,7 +49,7 @@ public class CQLMetricsTest extends SchemaLoader
|
|||
{
|
||||
Schema.instance.clear();
|
||||
|
||||
cassandra = new EmbeddedCassandraService();
|
||||
EmbeddedCassandraService cassandra = new EmbeddedCassandraService();
|
||||
cassandra.start();
|
||||
|
||||
cluster = Cluster.builder().addContactPoint("127.0.0.1").withPort(DatabaseDescriptor.getNativeTransportPort()).build();
|
||||
|
|
@ -57,11 +59,34 @@ public class CQLMetricsTest extends SchemaLoader
|
|||
session.execute("CREATE TABLE IF NOT EXISTS junit.metricstest (id int PRIMARY KEY, val text);");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnectionWithUseDisabled()
|
||||
{
|
||||
long useCountBefore = QueryProcessor.metrics.useStatementsExecuted.getCount();
|
||||
DatabaseDescriptor.setUseStatementsEnabled(false);
|
||||
|
||||
try (Session ignored = cluster.connect("junit"))
|
||||
{
|
||||
fail("expected USE statement to fail with use_statements_enabled = false");
|
||||
}
|
||||
catch (InvalidQueryException e)
|
||||
{
|
||||
Assert.assertEquals(useCountBefore, QueryProcessor.metrics.useStatementsExecuted.getCount());
|
||||
assertTrue(e.getMessage().contains("USE statements prohibited"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
DatabaseDescriptor.setUseStatementsEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreparedStatementsCount()
|
||||
{
|
||||
int n = QueryProcessor.metrics.preparedStatementsCount.getValue();
|
||||
long useCountBefore = QueryProcessor.metrics.useStatementsExecuted.getCount();
|
||||
session.execute("use junit");
|
||||
Assert.assertEquals(useCountBefore + 1, QueryProcessor.metrics.useStatementsExecuted.getCount());
|
||||
session.prepare("SELECT * FROM junit.metricstest WHERE id = ?");
|
||||
assertEquals(n+2, (int) QueryProcessor.metrics.preparedStatementsCount.getValue());
|
||||
}
|
||||
|
|
@ -104,15 +129,15 @@ public class CQLMetricsTest extends SchemaLoader
|
|||
clearMetrics();
|
||||
PreparedStatement metricsStatement = session.prepare("INSERT INTO junit.metricstest (id, val) VALUES (?, ?)");
|
||||
|
||||
assertEquals(Double.NaN, QueryProcessor.metrics.preparedStatementsRatio.getValue());
|
||||
assertEquals(Double.NaN, QueryProcessor.metrics.preparedStatementsRatio.getValue(), 0.0);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
session.execute(metricsStatement.bind(i, "val" + i));
|
||||
assertEquals(1.0, QueryProcessor.metrics.preparedStatementsRatio.getValue());
|
||||
assertEquals(1.0, QueryProcessor.metrics.preparedStatementsRatio.getValue(), 0.0);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
session.execute(String.format("INSERT INTO junit.metricstest (id, val) VALUES (%d, '%s')", i, "val" + i));
|
||||
assertEquals(0.5, QueryProcessor.metrics.preparedStatementsRatio.getValue());
|
||||
assertEquals(0.5, QueryProcessor.metrics.preparedStatementsRatio.getValue(), 0.0);
|
||||
}
|
||||
|
||||
private void clearMetrics()
|
||||
|
|
|
|||
Loading…
Reference in New Issue