in src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java [164:357]
public StatementRestrictions(ClientState state,
StatementType type,
TableMetadata table,
WhereClause whereClause,
VariableSpecifications boundNames,
List<Ordering> orderings,
boolean selectsOnlyStaticColumns,
boolean allowUseOfSecondaryIndices,
boolean allowFiltering,
boolean forView)
{
this(type, table, allowFiltering);
final IndexRegistry indexRegistry = type.allowUseOfSecondaryIndices() && allowUseOfSecondaryIndices
? IndexRegistry.obtain(table)
: null;
/*
* WHERE clause. For a given entity, rules are:
* - EQ relation conflicts with anything else (including a 2nd EQ)
* - Can't have more than one LT(E) relation (resp. GT(E) relation)
* - IN relation are restricted to row keys (for now) and conflicts with anything else (we could
* allow two IN for the same entity but that doesn't seem very useful)
* - The value_alias cannot be restricted in any way (we don't support wide rows with indexed value
* in CQL so far)
* - CONTAINS and CONTAINS_KEY cannot be used with UPDATE or DELETE
*/
for (Relation relation : whereClause.relations)
{
Operator operator = relation.operator();
if (operator.requiresFilteringOrIndexingFor(ColumnMetadata.Kind.CLUSTERING) && (type.isUpdate() || type.isDelete()))
{
throw invalidRequest("Cannot use %s with %s", type, operator);
}
if (operator == Operator.IS_NOT)
{
if (!forView)
throw new InvalidRequestException("Unsupported restriction: " + relation);
this.notNullColumns.addAll(relation.toRestriction(table, boundNames).columns());
}
else if (operator.requiresIndexing())
{
Restriction restriction = relation.toRestriction(table, boundNames);
if (!type.allowUseOfSecondaryIndices() || !restriction.hasSupportingIndex(indexRegistry))
throw invalidRequest("%s restriction is only supported on properly " +
"indexed columns. %s is not valid.", operator, relation);
addRestriction(restriction, indexRegistry);
}
else
{
addRestriction(relation.toRestriction(table, boundNames), indexRegistry);
}
}
// ORDER BY clause.
// Some indexes can be used for ordering.
nonPrimaryKeyRestrictions = addOrderingRestrictions(orderings, nonPrimaryKeyRestrictions);
hasRegularColumnsRestrictions = nonPrimaryKeyRestrictions.hasRestrictionFor(ColumnMetadata.Kind.REGULAR);
boolean hasQueriableClusteringColumnIndex = false;
boolean hasQueriableIndex = false;
if (allowUseOfSecondaryIndices)
{
if (whereClause.containsCustomExpressions())
processCustomIndexExpressions(whereClause.expressions, boundNames, indexRegistry);
hasQueriableClusteringColumnIndex = clusteringColumnsRestrictions.hasSupportingIndex(indexRegistry);
hasQueriableIndex = !filterRestrictions.getCustomIndexExpressions().isEmpty()
|| hasQueriableClusteringColumnIndex
|| partitionKeyRestrictions.hasSupportingIndex(indexRegistry)
|| nonPrimaryKeyRestrictions.hasSupportingIndex(indexRegistry);
}
// At this point, the select statement if fully constructed, but we still have a few things to validate
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.
if (usesSecondaryIndexing || partitionKeyRestrictions.needFiltering())
filterRestrictions.add(partitionKeyRestrictions);
if (selectsOnlyStaticColumns && hasClusteringColumnsRestrictions())
{
// If the only updated/deleted columns are static, then we don't need clustering columns.
// And in fact, unless it is an INSERT, we reject if clustering colums are provided as that
// suggest something unintended. For instance, given:
// CREATE TABLE t (k int, v int, s int static, PRIMARY KEY (k, v))
// it can make sense to do:
// INSERT INTO t(k, v, s) VALUES (0, 1, 2)
// but both
// UPDATE t SET s = 3 WHERE k = 0 AND v = 1
// DELETE v FROM t WHERE k = 0 AND v = 1
// sounds like you don't really understand what your are doing.
if (type.isDelete() || type.isUpdate())
throw invalidRequest("Invalid restrictions on clustering columns since the %s statement modifies only static columns",
type);
if (type.isSelect())
throw invalidRequest("Cannot restrict clustering columns when selecting only static columns");
}
processClusteringColumnsRestrictions(hasQueriableIndex,
selectsOnlyStaticColumns,
forView,
allowFiltering);
// Covers indexes on the first clustering column (among others).
if (isKeyRange && hasQueriableClusteringColumnIndex)
usesSecondaryIndexing = true;
if (usesSecondaryIndexing || clusteringColumnsRestrictions.needFiltering())
filterRestrictions.add(clusteringColumnsRestrictions);
// Even if usesSecondaryIndexing is false at this point, we'll still have to use one if
// there is restrictions not covered by the PK.
if (!nonPrimaryKeyRestrictions.isEmpty())
{
if (!type.allowNonPrimaryKeyInWhereClause())
{
Collection<ColumnIdentifier> nonPrimaryKeyColumns =
ColumnMetadata.toIdentifiers(nonPrimaryKeyRestrictions.columns());
throw invalidRequest("Non PRIMARY KEY columns found in where clause: %s ",
Joiner.on(", ").join(nonPrimaryKeyColumns));
}
Optional<SingleRestriction> annRestriction = Streams.stream(nonPrimaryKeyRestrictions)
.filter(SingleRestriction::isANN)
.findFirst();
if (annRestriction.isPresent())
{
// If there is an ANN restriction then it must be for a vector<float, n> column, and it must have an index
ColumnMetadata annColumn = annRestriction.get().firstColumn();
if (!annColumn.type.isVector() || !(((VectorType<?>)annColumn.type).elementType instanceof FloatType))
throw invalidRequest(ANN_ONLY_SUPPORTED_ON_VECTOR_MESSAGE);
if (indexRegistry == null || indexRegistry.listIndexes().stream().noneMatch(i -> i.dependsOn(annColumn)))
throw invalidRequest(ANN_REQUIRES_INDEX_MESSAGE);
// We do not allow ANN queries using partition key restrictions that need filtering
if (partitionKeyRestrictions.needFiltering())
throw invalidRequest(ANN_REQUIRES_INDEXED_FILTERING_MESSAGE);
// We do not allow ANN query filtering using non-indexed columns
List<ColumnMetadata> nonAnnColumns = Streams.stream(nonPrimaryKeyRestrictions)
.filter(r -> !r.isANN())
.map(SingleRestriction::firstColumn)
.collect(Collectors.toList());
List<ColumnMetadata> clusteringColumns = clusteringColumnsRestrictions.columns();
if (!nonAnnColumns.isEmpty() || !clusteringColumns.isEmpty())
{
List<ColumnMetadata> nonIndexedColumns = Stream.concat(nonAnnColumns.stream(), clusteringColumns.stream())
.filter(c -> indexRegistry.listIndexes().stream().noneMatch(i -> i.dependsOn(c)))
.collect(Collectors.toList());
if (!nonIndexedColumns.isEmpty())
{
// restrictions on non-clustering columns, or clusterings that still need filtering, are invalid
if (!clusteringColumns.containsAll(nonIndexedColumns)
|| partitionKeyRestrictions.hasUnrestrictedPartitionKeyComponents()
|| clusteringColumnsRestrictions.needFiltering())
throw invalidRequest(StatementRestrictions.ANN_REQUIRES_INDEXED_FILTERING_MESSAGE);
}
}
}
else
{
// We do not support indexed vector restrictions that are not part of an ANN ordering
Optional<ColumnMetadata> vectorColumn = nonPrimaryKeyRestrictions.columns()
.stream()
.filter(c -> c.type.isVector())
.findFirst();
if (vectorColumn.isPresent() && indexRegistry.listIndexes().stream().anyMatch(i -> i.dependsOn(vectorColumn.get())))
throw invalidRequest(StatementRestrictions.VECTOR_INDEXES_ANN_ONLY_MESSAGE);
}
if (hasQueriableIndex)
{
usesSecondaryIndexing = true;
}
else
{
if (!allowFiltering && requiresAllowFilteringIfNotSpecified(table))
throw invalidRequest(allowFilteringMessage(state));
}
filterRestrictions.add(nonPrimaryKeyRestrictions);
}
if (usesSecondaryIndexing)
validateSecondaryIndexSelections();
}