Make virtual tables decide if they implicitly enable ALLOW FILTERING

patch by Stefan Miklosovic; reviewed by Aleksey Yeschenko for CASSANDRA-18238
This commit is contained in:
Stefan Miklosovic 2023-02-09 14:18:50 +01:00
parent 5f8270eb86
commit 452ad8ce70
No known key found for this signature in database
GPG Key ID: 32F35CB2F546D93E
8 changed files with 90 additions and 4 deletions

View File

@ -1,4 +1,5 @@
4.2
* Make virtual tables decide if they implicitly enable ALLOW FILTERING (CASSANDRA-18238)
* Add row, tombstone, and sstable count to nodetool profileload (CASSANDRA-18022)
* Coordinator level metrics for read response and mutation row and column counts (CASSANDRA-18155)
* Add CQL functions for dynamic data masking (CASSANDRA-17941)

View File

@ -122,6 +122,8 @@ New features
- `mask_inner` replaces every character but the first and last ones by a fixed character.
- `mask_outer` replaces the first and last characters by a fixed character.
- `mask_hash` replaces the data by its hash, according to the specified algorithm.
- On virtual tables, it is not strictly necessary to specify `ALLOW FILTERING` for select statements which would
normally require it, except `system_views.system_logs`.
Upgrading
---------

View File

@ -32,6 +32,7 @@ CQL does *not* execute joins or sub-queries and a select statement only apply to
A select statement can also have a xref:cql/dml.adoc#where-clause[where clause] that can further narrow the query results.
Additional clauses can xref:cql/dml.adoc#ordering-clause[order] or xref:cql/dml.adoc#limit-clause[limit] the results.
Lastly, xref:cql/dml.adoc#allow-filtering[queries that require full cluster filtering] can append `ALLOW FILTERING` to any query.
For virtual tables, from https://issues.apache.org/jira/browse/CASSANDRA-18238[CASSANDRA-18238], it is not necessary to specify `ALLOW FILTERING` when a query would normally require that. Please consult the documentation for virtual tables to know more.
[[selection-clause]]
=== Selection clause

View File

@ -20,7 +20,8 @@ Virtual keyspaces and tables are quite different from regular tables and keyspac
* Consistency level of the queries sent to virtual tables are ignored.
* All existing virtual tables use `LocalPartitioner`.
Since a virtual table is not replicated the partitioner sorts in order of partition keys instead of by their hash.
* Making advanced queries using `ALLOW FILTERING` and aggregation functions can be executed in virtual tables, even though in normal tables we dont recommend it.
* Making advanced queries using `ALLOW FILTERING` and aggregation functions can be executed in virtual tables, even though in normal tables we do not recommend it.
From https://issues.apache.org/jira/browse/CASSANDRA-18238[CASSANDRA-18238], it is not necessary to specify `ALLOW FILTERING` when a query would normally require that, except when querying the table `system_views.system_logs`.
== Virtual Keyspaces
@ -94,6 +95,8 @@ recent_hit_rate_per_second, recent_request_rate_per_second, request_count, and s
|sstable_tasks |Lists currently running tasks and progress on SSTables, for operations like compaction and upgrade.
|system_logs |Displays Cassandra logs if logged via CQLLOG appender in logback.xml
|system_properties |Displays environmental system properties set on the node.
|thread_pools |Lists metrics for each thread pool.
@ -101,6 +104,9 @@ recent_hit_rate_per_second, recent_request_rate_per_second, request_count, and s
|tombstones_per_read |Records counts, keyspace_name, tablek_name, max, and median for tombstones.
|===
For improved usability, from https://issues.apache.org/jira/browse/CASSANDRA-18238[CASSANDRA-18238],
all tables except `system_logs` have `ALLOW FILTERING` implicitly added to a query when required by CQL specification.
We shall discuss some of the virtual tables in more detail next.
=== Clients Virtual Table

View File

@ -30,6 +30,8 @@ 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.db.virtual.VirtualKeyspaceRegistry;
import org.apache.cassandra.db.virtual.VirtualTable;
import org.apache.cassandra.dht.*;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.index.Index;
@ -274,7 +276,7 @@ public final class StatementRestrictions
}
if (hasQueriableIndex)
usesSecondaryIndexing = true;
else if (!allowFiltering)
else if (!allowFiltering && requiresAllowFilteringIfNotSpecified())
throw invalidRequest(allowFilteringMessage(state));
filterRestrictions.add(nonPrimaryKeyRestrictions);
@ -284,6 +286,16 @@ public final class StatementRestrictions
validateSecondaryIndexSelections();
}
private boolean requiresAllowFilteringIfNotSpecified()
{
if (!table.isVirtual())
return true;
VirtualTable tableNullable = VirtualKeyspaceRegistry.instance.getTableNullable(table.id);
assert tableNullable != null;
return !tableNullable.allowFilteringImplicitly();
}
private void addRestriction(Restriction restriction)
{
ColumnMetadata def = restriction.getFirstColumn();
@ -500,7 +512,7 @@ public final class StatementRestrictions
// components must have a EQ. Only the last partition key component can be in IN relation.
if (partitionKeyRestrictions.needFiltering(table))
{
if (!allowFiltering && !forView && !hasQueriableIndex)
if (!allowFiltering && !forView && !hasQueriableIndex && requiresAllowFilteringIfNotSpecified())
throw new InvalidRequestException(allowFilteringMessage(state));
isKeyRange = true;

View File

@ -44,7 +44,10 @@ import org.apache.cassandra.schema.TableMetadata;
* the oldest one is removed.
* <p>
* This virtual table can be truncated.
* <p>
* This table does not enable {@code ALLOW FILTERING} implicitly.
*
* @see <a href="https://issues.apache.org/jira/browse/CASSANDRA-18238">CASSANDRA-18238</a>
* @see org.apache.cassandra.utils.logging.VirtualTableAppender
*/
public final class LogMessagesTable extends AbstractMutableVirtualTable
@ -134,6 +137,12 @@ public final class LogMessagesTable extends AbstractMutableVirtualTable
buffer.clear();
}
@Override
public boolean allowFilteringImplicitly()
{
return false;
}
@VisibleForTesting
static int resolveBufferSize()
{

View File

@ -76,4 +76,15 @@ public interface VirtualTable
* Truncates data from the underlying source, if supported.
*/
void truncate();
/**
* Tells whether {@code ALLOW FILTERING} is implicitly added to select statement
* which requires it. Defaults to true.
*
* @return true if {@code ALLOW FILTERING} is implicitly added to select statements when required, false otherwise.
*/
default boolean allowFilteringImplicitly()
{
return true;
}
}

View File

@ -38,6 +38,7 @@ import org.apache.commons.lang3.tuple.Pair;
import org.junit.BeforeClass;
import org.junit.Test;
import com.datastax.driver.core.exceptions.InvalidQueryException;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.db.Mutation;
import org.apache.cassandra.db.marshal.Int32Type;
@ -56,7 +57,9 @@ import org.apache.cassandra.service.StorageServiceMBean;
import org.apache.cassandra.triggers.ITrigger;
import static java.lang.String.format;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class VirtualTableTest extends CQLTester
@ -66,6 +69,7 @@ public class VirtualTableTest extends CQLTester
private static final String VT2_NAME = "vt2";
private static final String VT3_NAME = "vt3";
private static final String VT4_NAME = "vt4";
private static final String VT5_NAME = "vt5";
// As long as we execute test queries using execute (and not executeNet) the virtual tables implementation
// do not need to be thread-safe. We choose to do it to avoid issues if the test framework was changed or somebody
@ -343,7 +347,27 @@ public class VirtualTableTest extends CQLTester
};
VirtualKeyspaceRegistry.instance.register(new VirtualKeyspace(KS_NAME, ImmutableList.of(vt1, vt2, vt3, vt4)));
VirtualTable vt5 = new AbstractVirtualTable(TableMetadata.builder(KS_NAME, VT5_NAME)
.kind(TableMetadata.Kind.VIRTUAL)
.addPartitionKeyColumn("pk", UTF8Type.instance)
.addClusteringColumn("c", UTF8Type.instance)
.addRegularColumn("v1", Int32Type.instance)
.addRegularColumn("v2", LongType.instance)
.build())
{
public DataSet data()
{
return new SimpleDataSet(metadata());
}
@Override
public boolean allowFilteringImplicitly()
{
return false;
}
};
VirtualKeyspaceRegistry.instance.register(new VirtualKeyspace(KS_NAME, ImmutableList.of(vt1, vt2, vt3, vt4, vt5)));
CQLTester.setUpClass();
}
@ -1025,6 +1049,26 @@ public class VirtualTableTest extends CQLTester
assertJMXFails(() -> mbean.getAutoCompactionStatus(KS_NAME, VT1_NAME));
}
@Test
public void testDisallowedFilteringOnTable() throws Throwable
{
try
{
executeNet(format("SELECT * FROM %s.%s WHERE v2 = 5", KS_NAME, VT5_NAME));
fail(format("should fail as %s.%s is not allowed to be filtered on implicitly.", KS_NAME, VT5_NAME));
}
catch (InvalidQueryException ex)
{
assertTrue(ex.getMessage().contains("Cannot execute this query as it might involve data filtering and thus may have unpredictable performance"));
}
}
@Test
public void testAllowedFilteringOnTable() throws Throwable
{
executeNet(format("SELECT * FROM %s.%s WHERE v2 = 5", KS_NAME, VT1_NAME));
}
@FunctionalInterface
private static interface ThrowingRunnable
{