Allow MV's SELECT to restrict PK columns Patch by Tyler Hobbs; reviewed by Sylvain Lebresne for CASSANDRA-9664
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/5a4253b6 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/5a4253b6 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/5a4253b6 Branch: refs/heads/cassandra-3.0 Commit: 5a4253b6a17de9810fbc4e1c3b8d4980e26adcca Parents: 41731b8 Author: Tyler Hobbs <[email protected]> Authored: Sat Sep 19 10:06:45 2015 -0500 Committer: Tyler Hobbs <[email protected]> Committed: Sat Sep 19 10:06:45 2015 -0500 ---------------------------------------------------------------------- CHANGES.txt | 2 + .../apache/cassandra/config/ViewDefinition.java | 74 +- .../apache/cassandra/cql3/AbstractMarker.java | 31 +- .../apache/cassandra/cql3/ColumnIdentifier.java | 35 + .../org/apache/cassandra/cql3/Constants.java | 26 +- src/java/org/apache/cassandra/cql3/Cql.g | 12 +- src/java/org/apache/cassandra/cql3/Json.java | 42 +- src/java/org/apache/cassandra/cql3/Lists.java | 8 +- src/java/org/apache/cassandra/cql3/Maps.java | 18 +- .../cassandra/cql3/MultiColumnRelation.java | 28 +- .../org/apache/cassandra/cql3/Operator.java | 58 +- .../org/apache/cassandra/cql3/Relation.java | 23 + src/java/org/apache/cassandra/cql3/Sets.java | 8 +- .../cassandra/cql3/SingleColumnRelation.java | 30 + src/java/org/apache/cassandra/cql3/Term.java | 19 +- .../apache/cassandra/cql3/TokenRelation.java | 26 + src/java/org/apache/cassandra/cql3/Tuples.java | 24 +- .../org/apache/cassandra/cql3/TypeCast.java | 5 +- .../org/apache/cassandra/cql3/UserTypes.java | 7 +- .../cassandra/cql3/functions/FunctionCall.java | 16 +- .../cql3/restrictions/AbstractRestriction.java | 14 +- .../ForwardingPrimaryKeyRestrictions.java | 6 + .../restrictions/MultiColumnRestriction.java | 55 + .../cql3/restrictions/Restriction.java | 1 + .../restrictions/SingleColumnRestriction.java | 58 + .../restrictions/StatementRestrictions.java | 69 +- .../cql3/statements/AlterTableStatement.java | 2 +- .../cql3/statements/CreateViewStatement.java | 74 +- .../cassandra/cql3/statements/IndexTarget.java | 14 +- .../cql3/statements/ModificationStatement.java | 2 +- .../cql3/statements/ParsedStatement.java | 5 + .../cql3/statements/SelectStatement.java | 27 +- .../cql3/statements/UpdateStatement.java | 2 + .../cassandra/db/PartitionRangeReadCommand.java | 13 +- .../org/apache/cassandra/db/ReadCommand.java | 12 - src/java/org/apache/cassandra/db/ReadQuery.java | 24 +- .../db/SinglePartitionReadCommand.java | 25 +- .../apache/cassandra/db/filter/RowFilter.java | 39 + .../apache/cassandra/db/view/TemporalRow.java | 17 +- src/java/org/apache/cassandra/db/view/View.java | 156 ++- .../apache/cassandra/db/view/ViewBuilder.java | 15 +- .../internal/composites/CompositesSearcher.java | 2 +- .../apache/cassandra/schema/SchemaKeyspace.java | 16 +- .../org/apache/cassandra/cql3/CQLTester.java | 78 ++ .../cassandra/cql3/ViewFilteringTest.java | 1292 ++++++++++++++++++ .../org/apache/cassandra/cql3/ViewTest.java | 4 +- .../SelectSingleColumnRelationTest.java | 4 + 47 files changed, 2270 insertions(+), 248 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index e55fd0a..e589626 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,6 @@ 3.0.0-rc1 + * Allow MATERIALIZED VIEW's SELECT statement to restrict primary key + columns (CASSANDRA-9664) * Move crc_check_chance out of compression options (CASSANDRA-9839) * Fix descending iteration past end of BTreeSearchIterator (CASSANDRA-10301) * Transfer hints to a different node on decommission (CASSANDRA-10198) http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/config/ViewDefinition.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/config/ViewDefinition.java b/src/java/org/apache/cassandra/config/ViewDefinition.java index 39695b9..02acc68 100644 --- a/src/java/org/apache/cassandra/config/ViewDefinition.java +++ b/src/java/org/apache/cassandra/config/ViewDefinition.java @@ -17,26 +17,35 @@ */ package org.apache.cassandra.config; +import java.util.List; import java.util.Objects; import java.util.UUID; +import java.util.stream.Collectors; +import org.antlr.runtime.*; +import org.apache.cassandra.cql3.*; +import org.apache.cassandra.cql3.statements.SelectStatement; +import org.apache.cassandra.db.view.View; +import org.apache.cassandra.exceptions.SyntaxException; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.cassandra.cql3.ColumnIdentifier; - public class ViewDefinition { public final String ksName; public final String viewName; public final UUID baseTableId; + public final String baseTableName; public final boolean includeAllColumns; // The order of partititon columns and clustering columns is important, so we cannot switch these two to sets public final CFMetaData metadata; + public SelectStatement.RawStatement select; + public String whereClause; + public ViewDefinition(ViewDefinition def) { - this(def.ksName, def.viewName, def.baseTableId, def.includeAllColumns, def.metadata); + this(def.ksName, def.viewName, def.baseTableId, def.baseTableName, def.includeAllColumns, def.select, def.whereClause, def.metadata); } /** @@ -44,12 +53,15 @@ public class ViewDefinition * @param baseTableId Internal ID of the table which this view is based off of * @param includeAllColumns Whether to include all columns or not */ - public ViewDefinition(String ksName, String viewName, UUID baseTableId, boolean includeAllColumns, CFMetaData metadata) + public ViewDefinition(String ksName, String viewName, UUID baseTableId, String baseTableName, boolean includeAllColumns, SelectStatement.RawStatement select, String whereClause, CFMetaData metadata) { this.ksName = ksName; this.viewName = viewName; this.baseTableId = baseTableId; + this.baseTableName = baseTableName; this.includeAllColumns = includeAllColumns; + this.select = select; + this.whereClause = whereClause; this.metadata = metadata; } @@ -63,7 +75,7 @@ public class ViewDefinition public ViewDefinition copy() { - return new ViewDefinition(ksName, viewName, baseTableId, includeAllColumns, metadata.copy()); + return new ViewDefinition(ksName, viewName, baseTableId, baseTableName, includeAllColumns, select, whereClause, metadata.copy()); } public CFMetaData baseTableMetadata() @@ -85,6 +97,7 @@ public class ViewDefinition && Objects.equals(viewName, other.viewName) && Objects.equals(baseTableId, other.baseTableId) && Objects.equals(includeAllColumns, other.includeAllColumns) + && Objects.equals(whereClause, other.whereClause) && Objects.equals(metadata, other.metadata); } @@ -96,6 +109,7 @@ public class ViewDefinition .append(viewName) .append(baseTableId) .append(includeAllColumns) + .append(whereClause) .append(metadata) .toHashCode(); } @@ -107,8 +121,58 @@ public class ViewDefinition .append("ksName", ksName) .append("viewName", viewName) .append("baseTableId", baseTableId) + .append("baseTableName", baseTableName) .append("includeAllColumns", includeAllColumns) + .append("whereClause", whereClause) .append("metadata", metadata) .toString(); } + + /** + * Replace the column {@param from} with {@param to} in this materialized view definition's partition, + * clustering, or included columns. + */ + public void renameColumn(ColumnIdentifier from, ColumnIdentifier to) + { + metadata.renameColumn(from, to); + + // convert whereClause to Relations, rename ids in Relations, then convert back to whereClause + List<Relation> relations = whereClauseToRelations(whereClause); + ColumnIdentifier.Raw fromRaw = new ColumnIdentifier.Literal(from.toString(), true); + ColumnIdentifier.Raw toRaw = new ColumnIdentifier.Literal(to.toString(), true); + List<Relation> newRelations = relations.stream() + .map(r -> r.renameIdentifier(fromRaw, toRaw)) + .collect(Collectors.toList()); + + this.whereClause = View.relationsToWhereClause(newRelations); + String rawSelect = View.buildSelectStatement(baseTableName, metadata.allColumns(), whereClause); + this.select = (SelectStatement.RawStatement) QueryProcessor.parseStatement(rawSelect); + } + + private static List<Relation> whereClauseToRelations(String whereClause) + { + ErrorCollector errorCollector = new ErrorCollector(whereClause); + CharStream stream = new ANTLRStringStream(whereClause); + CqlLexer lexer = new CqlLexer(stream); + lexer.addErrorListener(errorCollector); + + TokenStream tokenStream = new CommonTokenStream(lexer); + CqlParser parser = new CqlParser(tokenStream); + parser.addErrorListener(errorCollector); + + try + { + List<Relation> relations = parser.whereClause().build().relations; + + // The errorCollector has queued up any errors that the lexer and parser may have encountered + // along the way, if necessary, we turn the last error into exceptions here. + errorCollector.throwFirstSyntaxError(); + + return relations; + } + catch (RecognitionException | SyntaxException exc) + { + throw new RuntimeException("Unexpected error parsing materialized view's where clause while handling column rename: ", exc); + } + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/AbstractMarker.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/AbstractMarker.java b/src/java/org/apache/cassandra/cql3/AbstractMarker.java index d11b8e2..d2bc022 100644 --- a/src/java/org/apache/cassandra/cql3/AbstractMarker.java +++ b/src/java/org/apache/cassandra/cql3/AbstractMarker.java @@ -56,7 +56,7 @@ public abstract class AbstractMarker extends Term.NonTerminal /** * A parsed, but non prepared, bind marker. */ - public static class Raw implements Term.Raw + public static class Raw extends Term.Raw { protected final int bindIndex; @@ -85,7 +85,34 @@ public abstract class AbstractMarker extends Term.NonTerminal } @Override - public String toString() + public String getText() + { + return "?"; + } + } + + /** A MultiColumnRaw version of AbstractMarker.Raw */ + public static abstract class MultiColumnRaw extends Term.MultiColumnRaw + { + protected final int bindIndex; + + public MultiColumnRaw(int bindIndex) + { + this.bindIndex = bindIndex; + } + + public NonTerminal prepare(String keyspace, ColumnSpecification receiver) throws InvalidRequestException + { + throw new AssertionError("MultiColumnRaw..prepare() requires a list of receivers"); + } + + public AssignmentTestable.TestResult testAssignment(String keyspace, ColumnSpecification receiver) + { + return AssignmentTestable.TestResult.WEAKLY_ASSIGNABLE; + } + + @Override + public String getText() { return "?"; } http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java b/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java index 4880c60..eb16f93 100644 --- a/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java +++ b/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Locale; import java.nio.ByteBuffer; import java.util.concurrent.ConcurrentMap; +import java.util.regex.Pattern; import com.google.common.collect.MapMaker; @@ -55,6 +56,8 @@ public class ColumnIdentifier extends org.apache.cassandra.cql3.selection.Select public final long prefixComparison; private final boolean interned; + private static final Pattern UNQUOTED_IDENTIFIER = Pattern.compile("[a-z][a-z0-9_]*"); + private static final long EMPTY_SIZE = ObjectSizes.measure(new ColumnIdentifier(ByteBufferUtil.EMPTY_BYTE_BUFFER, "", false)); private static final ConcurrentMap<ByteBuffer, ColumnIdentifier> internedInstances = new MapMaker().weakValues().makeMap(); @@ -150,6 +153,15 @@ public class ColumnIdentifier extends org.apache.cassandra.cql3.selection.Select return text; } + /** + * Returns a string representation of the identifier that is safe to use directly in CQL queries. + * In necessary, the string will be double-quoted, and any quotes inside the string will be escaped. + */ + public String toCQLString() + { + return maybeQuote(text); + } + public long unsharedHeapSize() { return EMPTY_SIZE @@ -198,6 +210,12 @@ public class ColumnIdentifier extends org.apache.cassandra.cql3.selection.Select { public ColumnIdentifier prepare(CFMetaData cfm); + + /** + * Returns a string representation of the identifier that is safe to use directly in CQL queries. + * In necessary, the string will be double-quoted, and any quotes inside the string will be escaped. + */ + public String toCQLString(); } public static class Literal implements Raw @@ -257,6 +275,11 @@ public class ColumnIdentifier extends org.apache.cassandra.cql3.selection.Select { return text; } + + public String toCQLString() + { + return maybeQuote(text); + } } public static class ColumnIdentifierValue implements Raw @@ -298,5 +321,17 @@ public class ColumnIdentifier extends org.apache.cassandra.cql3.selection.Select { return identifier.toString(); } + + public String toCQLString() + { + return maybeQuote(identifier.text); + } + } + + private static String maybeQuote(String text) + { + if (UNQUOTED_IDENTIFIER.matcher(text).matches()) + return text; + return "\"" + text.replace("\"", "\"\"") + "\""; } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/Constants.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Constants.java b/src/java/org/apache/cassandra/cql3/Constants.java index f10484d..425dd85 100644 --- a/src/java/org/apache/cassandra/cql3/Constants.java +++ b/src/java/org/apache/cassandra/cql3/Constants.java @@ -46,7 +46,7 @@ public abstract class Constants public static final Value UNSET_VALUE = new Value(ByteBufferUtil.UNSET_BYTE_BUFFER); - public static final Term.Raw NULL_LITERAL = new Term.Raw() + private static class NullLiteral extends Term.Raw { public Term prepare(String keyspace, ColumnSpecification receiver) throws InvalidRequestException { @@ -63,12 +63,13 @@ public abstract class Constants : AssignmentTestable.TestResult.WEAKLY_ASSIGNABLE; } - @Override - public String toString() + public String getText() { - return "null"; + return "NULL"; } - }; + } + + public static final NullLiteral NULL_LITERAL = new NullLiteral(); public static final Term.Terminal NULL_VALUE = new Value(null) { @@ -86,7 +87,7 @@ public abstract class Constants } }; - public static class Literal implements Term.Raw + public static class Literal extends Term.Raw { private final Type type; private final String text; @@ -155,11 +156,6 @@ public abstract class Constants } } - public String getRawText() - { - return text; - } - public AssignmentTestable.TestResult testAssignment(String keyspace, ColumnSpecification receiver) { CQL3Type receiverType = receiver.type.asCQL3Type(); @@ -238,8 +234,12 @@ public abstract class Constants return AssignmentTestable.TestResult.NOT_ASSIGNABLE; } - @Override - public String toString() + public String getRawText() + { + return text; + } + + public String getText() { return type == Type.STRING ? String.format("'%s'", text) : text; } http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/Cql.g ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Cql.g b/src/java/org/apache/cassandra/cql3/Cql.g index cd52c1c..932ecd6 100644 --- a/src/java/org/apache/cassandra/cql3/Cql.g +++ b/src/java/org/apache/cassandra/cql3/Cql.g @@ -762,19 +762,18 @@ createMaterializedViewStatement returns [CreateViewStatement expr] } : K_CREATE K_MATERIALIZED K_VIEW (K_IF K_NOT K_EXISTS { ifNotExists = true; })? cf=columnFamilyName K_AS K_SELECT sclause=selectClause K_FROM basecf=columnFamilyName - (K_WHERE wclause=mvWhereClause)? + (K_WHERE wclause=whereClause)? K_PRIMARY K_KEY ( '(' '(' k1=cident { partitionKeys.add(k1); } ( ',' kn=cident { partitionKeys.add(kn); } )* ')' ( ',' c1=cident { compositeKeys.add(c1); } )* ')' | '(' k1=cident { partitionKeys.add(k1); } ( ',' cn=cident { compositeKeys.add(cn); } )* ')' ) - { $expr = new CreateViewStatement(cf, basecf, sclause, wclause, partitionKeys, compositeKeys, ifNotExists); } + { + WhereClause where = wclause == null ? WhereClause.empty() : wclause.build(); + $expr = new CreateViewStatement(cf, basecf, sclause, where, partitionKeys, compositeKeys, ifNotExists); + } ( K_WITH cfamProperty[expr.properties] ( K_AND cfamProperty[expr.properties] )*)? ; -mvWhereClause returns [List<ColumnIdentifier.Raw> expr] - : t1=cident { $expr = new ArrayList<ColumnIdentifier.Raw>(); $expr.add(t1); } K_IS K_NOT K_NULL (K_AND tN=cident { $expr.add(tN); } K_IS K_NOT K_NULL)* - ; - /** * CREATE TRIGGER triggerName ON columnFamily USING 'triggerClass'; */ @@ -1423,6 +1422,7 @@ relationType returns [Operator op] relation[WhereClause.Builder clauses] : name=cident type=relationType t=term { $clauses.add(new SingleColumnRelation(name, type, t)); } + | name=cident K_IS K_NOT K_NULL { $clauses.add(new SingleColumnRelation(name, Operator.IS_NOT, Constants.NULL_LITERAL)); } | K_TOKEN l=tupleOfIdentifiers type=relationType t=term { $clauses.add(new TokenRelation(l, type, t)); } | name=cident K_IN marker=inMarker http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/Json.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Json.java b/src/java/org/apache/cassandra/cql3/Json.java index 35c69ed..df2d9ab 100644 --- a/src/java/org/apache/cassandra/cql3/Json.java +++ b/src/java/org/apache/cassandra/cql3/Json.java @@ -143,9 +143,9 @@ public class Json this.columns = columns; } - public DelayedColumnValue getRawTermForColumn(ColumnDefinition def) + public RawDelayedColumnValue getRawTermForColumn(ColumnDefinition def) { - return new DelayedColumnValue(this, def); + return new RawDelayedColumnValue(this, def); } public void bind(QueryOptions options) throws InvalidRequestException @@ -173,7 +173,7 @@ public class Json * Note that this is intrinsically an already prepared term, but this still implements Term.Raw so that we can * easily use it to create raw operations. */ - private static class ColumnValue implements Term.Raw + private static class ColumnValue extends Term.Raw { private final Term term; @@ -193,19 +193,22 @@ public class Json { return TestResult.NOT_ASSIGNABLE; } + + public String getText() + { + return term.toString(); + } } /** - * A NonTerminal for a single column. - * - * As with {@code ColumnValue}, this is intrinsically a prepared term but implements Terms.Raw for convenience. + * A Raw term for a single column. Like ColumnValue, this is intrinsically already prepared. */ - private static class DelayedColumnValue extends Term.NonTerminal implements Term.Raw + private static class RawDelayedColumnValue extends Term.Raw { private final PreparedMarker marker; private final ColumnDefinition column; - public DelayedColumnValue(PreparedMarker prepared, ColumnDefinition column) + public RawDelayedColumnValue(PreparedMarker prepared, ColumnDefinition column) { this.marker = prepared; this.column = column; @@ -214,7 +217,7 @@ public class Json @Override public Term prepare(String keyspace, ColumnSpecification receiver) throws InvalidRequestException { - return this; + return new DelayedColumnValue(marker, column); } @Override @@ -223,6 +226,26 @@ public class Json return TestResult.WEAKLY_ASSIGNABLE; } + public String getText() + { + return marker.toString(); + } + } + + /** + * A NonTerminal for a single column. As with {@code ColumnValue}, this is intrinsically a prepared. + */ + private static class DelayedColumnValue extends Term.NonTerminal + { + private final PreparedMarker marker; + private final ColumnDefinition column; + + public DelayedColumnValue(PreparedMarker prepared, ColumnDefinition column) + { + this.marker = prepared; + this.column = column; + } + @Override public void collectMarkerSpecification(VariableSpecifications boundNames) { @@ -248,6 +271,7 @@ public class Json { return Collections.emptyList(); } + } /** http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/Lists.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Lists.java b/src/java/org/apache/cassandra/cql3/Lists.java index d9dac22..830561e 100644 --- a/src/java/org/apache/cassandra/cql3/Lists.java +++ b/src/java/org/apache/cassandra/cql3/Lists.java @@ -23,6 +23,7 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; import org.apache.cassandra.config.ColumnDefinition; import org.apache.cassandra.cql3.functions.Function; @@ -55,7 +56,7 @@ public abstract class Lists return new ColumnSpecification(column.ksName, column.cfName, new ColumnIdentifier("value(" + column.name + ")", true), ((ListType)column.type).getElementsType()); } - public static class Literal implements Term.Raw + public static class Literal extends Term.Raw { private final List<Term.Raw> elements; @@ -113,10 +114,9 @@ public abstract class Lists return AssignmentTestable.TestResult.testAll(keyspace, valueSpec, elements); } - @Override - public String toString() + public String getText() { - return elements.toString(); + return elements.stream().map(Term.Raw::getText).collect(Collectors.joining(", ", "[", "]")); } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/Maps.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Maps.java b/src/java/org/apache/cassandra/cql3/Maps.java index 0f0672f..d5df279 100644 --- a/src/java/org/apache/cassandra/cql3/Maps.java +++ b/src/java/org/apache/cassandra/cql3/Maps.java @@ -21,6 +21,7 @@ import static org.apache.cassandra.cql3.Constants.UNSET_VALUE; import java.nio.ByteBuffer; import java.util.*; +import java.util.stream.Collectors; import com.google.common.collect.Iterables; @@ -54,7 +55,7 @@ public abstract class Maps return new ColumnSpecification(column.ksName, column.cfName, new ColumnIdentifier("value(" + column.name + ")", true), ((MapType)column.type).getValuesType()); } - public static class Literal implements Term.Raw + public static class Literal extends Term.Raw { public final List<Pair<Term.Raw, Term.Raw>> entries; @@ -129,18 +130,11 @@ public abstract class Maps return res; } - @Override - public String toString() + public String getText() { - StringBuilder sb = new StringBuilder(); - sb.append("{"); - for (int i = 0; i < entries.size(); i++) - { - if (i > 0) sb.append(", "); - sb.append(entries.get(i).left).append(":").append(entries.get(i).right); - } - sb.append("}"); - return sb.toString(); + return entries.stream() + .map(entry -> String.format("%s: %s", entry.left.getText(), entry.right.getText())) + .collect(Collectors.joining(", ", "{", "}")); } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/MultiColumnRelation.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/MultiColumnRelation.java b/src/java/org/apache/cassandra/cql3/MultiColumnRelation.java index 7735c57..143106d 100644 --- a/src/java/org/apache/cassandra/cql3/MultiColumnRelation.java +++ b/src/java/org/apache/cassandra/cql3/MultiColumnRelation.java @@ -19,6 +19,7 @@ package org.apache.cassandra.cql3; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.ColumnDefinition; @@ -114,11 +115,17 @@ public class MultiColumnRelation extends Relation * For non-IN relations, returns the Tuples.Literal or Tuples.Raw marker for a single tuple. * @return a Tuples.Literal for non-IN relations or Tuples.Raw marker for a single tuple. */ - private Term.MultiColumnRaw getValue() + public Term.MultiColumnRaw getValue() { return relationType == Operator.IN ? inMarker : valuesOrMarker; } + public List<? extends Term.Raw> getInValues() + { + assert relationType == Operator.IN; + return inValues; + } + @Override public boolean isMultiColumn() { @@ -164,7 +171,15 @@ public class MultiColumnRelation extends Relation VariableSpecifications boundNames, boolean isKey) throws InvalidRequestException { - throw invalidRequest("%s cannot be used for Multi-column relations", operator()); + throw invalidRequest("%s cannot be used for multi-column relations", operator()); + } + + @Override + protected Restriction newIsNotRestriction(CFMetaData cfm, + VariableSpecifications boundNames) throws InvalidRequestException + { + // this is currently disallowed by the grammar + throw new AssertionError(String.format("%s cannot be used for multi-column relations", operator())); } @Override @@ -198,6 +213,15 @@ public class MultiColumnRelation extends Relation return names; } + public Relation renameIdentifier(ColumnIdentifier.Raw from, ColumnIdentifier.Raw to) + { + if (!entities.contains(from)) + return this; + + List<ColumnIdentifier.Raw> newEntities = entities.stream().map(e -> e.equals(from) ? to : e).collect(Collectors.toList()); + return new MultiColumnRelation(newEntities, operator(), valuesOrMarker, inValues, inMarker); + } + @Override public String toString() { http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/Operator.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Operator.java b/src/java/org/apache/cassandra/cql3/Operator.java index 5ae9885..7b28a30 100644 --- a/src/java/org/apache/cassandra/cql3/Operator.java +++ b/src/java/org/apache/cassandra/cql3/Operator.java @@ -21,8 +21,14 @@ import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.nio.ByteBuffer; +import java.util.List; +import java.util.Map; +import java.util.Set; import org.apache.cassandra.db.marshal.AbstractType; +import org.apache.cassandra.db.marshal.ListType; +import org.apache.cassandra.db.marshal.MapType; +import org.apache.cassandra.db.marshal.SetType; public enum Operator { @@ -87,6 +93,14 @@ public enum Operator { return "!="; } + }, + IS_NOT(9) + { + @Override + public String toString() + { + return "IS NOT"; + } }; /** @@ -114,6 +128,11 @@ public enum Operator output.writeInt(b); } + public int getValue() + { + return b; + } + /** * Deserializes a <code>Operator</code> instance from the specified input. * @@ -134,27 +153,48 @@ public enum Operator /** * Whether 2 values satisfy this operator (given the type they should be compared with). * - * @throws AssertionError for IN, CONTAINS and CONTAINS_KEY as this doesn't make sense for this function. + * @throws AssertionError for CONTAINS and CONTAINS_KEY as this doesn't support those operators yet */ public boolean isSatisfiedBy(AbstractType<?> type, ByteBuffer leftOperand, ByteBuffer rightOperand) { - int comparison = type.compareForCQL(leftOperand, rightOperand); switch (this) { case EQ: - return comparison == 0; + return type.compareForCQL(leftOperand, rightOperand) == 0; case LT: - return comparison < 0; + return type.compareForCQL(leftOperand, rightOperand) < 0; case LTE: - return comparison <= 0; + return type.compareForCQL(leftOperand, rightOperand) <= 0; case GT: - return comparison > 0; + return type.compareForCQL(leftOperand, rightOperand) > 0; case GTE: - return comparison >= 0; + return type.compareForCQL(leftOperand, rightOperand) >= 0; case NEQ: - return comparison != 0; + return type.compareForCQL(leftOperand, rightOperand) != 0; + case IN: + List inValues = ((List) ListType.getInstance(type, false).getSerializer().deserialize(rightOperand)); + return inValues.contains(type.getSerializer().deserialize(leftOperand)); + case CONTAINS: + if (type instanceof ListType) + { + List list = (List) type.getSerializer().deserialize(leftOperand); + return list.contains(((ListType) type).getElementsType().getSerializer().deserialize(rightOperand)); + } + else if (type instanceof SetType) + { + Set set = (Set) type.getSerializer().deserialize(leftOperand); + return set.contains(((SetType) type).getElementsType().getSerializer().deserialize(rightOperand)); + } + else // MapType + { + Map map = (Map) type.getSerializer().deserialize(leftOperand); + return map.containsValue(((MapType) type).getValuesType().getSerializer().deserialize(rightOperand)); + } + case CONTAINS_KEY: + Map map = (Map) type.getSerializer().deserialize(leftOperand); + return map.containsKey(((MapType) type).getKeysType().getSerializer().deserialize(rightOperand)); default: - // we shouldn't get IN, CONTAINS, or CONTAINS KEY here + // we shouldn't get CONTAINS, CONTAINS KEY, or IS NOT here throw new AssertionError(); } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/Relation.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Relation.java b/src/java/org/apache/cassandra/cql3/Relation.java index 1337096..334464f 100644 --- a/src/java/org/apache/cassandra/cql3/Relation.java +++ b/src/java/org/apache/cassandra/cql3/Relation.java @@ -39,6 +39,16 @@ public abstract class Relation { } /** + * Returns the raw value for this relation, or null if this is an IN relation. + */ + public abstract Term.Raw getValue(); + + /** + * Returns the list of raw IN values for this relation, or null if this is not an IN relation. + */ + public abstract List<? extends Term.Raw> getInValues(); + + /** * Checks if this relation apply to multiple columns. * * @return <code>true</code> if this relation apply to multiple columns, <code>false</code> otherwise. @@ -132,6 +142,7 @@ public abstract class Relation { case IN: return newINRestriction(cfm, boundNames); case CONTAINS: return newContainsRestriction(cfm, boundNames, false); case CONTAINS_KEY: return newContainsRestriction(cfm, boundNames, true); + case IS_NOT: return newIsNotRestriction(cfm, boundNames); default: throw invalidRequest("Unsupported \"!=\" relation: %s", this); } } @@ -186,6 +197,9 @@ public abstract class Relation { VariableSpecifications boundNames, boolean isKey) throws InvalidRequestException; + protected abstract Restriction newIsNotRestriction(CFMetaData cfm, + VariableSpecifications boundNames) throws InvalidRequestException; + /** * Converts the specified <code>Raw</code> into a <code>Term</code>. * @param receivers the columns to which the values must be associated at @@ -246,4 +260,13 @@ public abstract class Relation { return def; } + + /** + * Renames an identifier in this Relation, if applicable. + * @param from the old identifier + * @param to the new identifier + * @return this object, if the old identifier is not in the set of entities that this relation covers; otherwise + * a new Relation with "from" replaced by "to" is returned. + */ + public abstract Relation renameIdentifier(ColumnIdentifier.Raw from, ColumnIdentifier.Raw to); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/Sets.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Sets.java b/src/java/org/apache/cassandra/cql3/Sets.java index 7ff3815..010abaa 100644 --- a/src/java/org/apache/cassandra/cql3/Sets.java +++ b/src/java/org/apache/cassandra/cql3/Sets.java @@ -21,6 +21,7 @@ import static org.apache.cassandra.cql3.Constants.UNSET_VALUE; import java.nio.ByteBuffer; import java.util.*; +import java.util.stream.Collectors; import com.google.common.base.Joiner; @@ -48,7 +49,7 @@ public abstract class Sets return new ColumnSpecification(column.ksName, column.cfName, new ColumnIdentifier("value(" + column.name + ")", true), ((SetType)column.type).getElementsType()); } - public static class Literal implements Term.Raw + public static class Literal extends Term.Raw { private final List<Term.Raw> elements; @@ -124,10 +125,9 @@ public abstract class Sets return AssignmentTestable.TestResult.testAll(keyspace, valueSpec, elements); } - @Override - public String toString() + public String getText() { - return "{" + Joiner.on(", ").join(elements) + "}"; + return elements.stream().map(Term.Raw::getText).collect(Collectors.joining(", ", "{", "}")); } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java b/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java index 84e6274..e2c0b79 100644 --- a/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java +++ b/src/java/org/apache/cassandra/cql3/SingleColumnRelation.java @@ -54,6 +54,9 @@ public final class SingleColumnRelation extends Relation this.relationType = type; this.value = value; this.inValues = inValues; + + if (type == Operator.IS_NOT) + assert value == Constants.NULL_LITERAL; } /** @@ -81,6 +84,16 @@ public final class SingleColumnRelation extends Relation this(entity, null, type, value); } + public Term.Raw getValue() + { + return value; + } + + public List<? extends Term.Raw> getInValues() + { + return inValues; + } + public static SingleColumnRelation createInRelation(ColumnIdentifier.Raw entity, List<Term.Raw> inValues) { return new SingleColumnRelation(entity, null, Operator.IN, null, inValues); @@ -120,6 +133,13 @@ public final class SingleColumnRelation extends Relation } } + public Relation renameIdentifier(ColumnIdentifier.Raw from, ColumnIdentifier.Raw to) + { + return entity.equals(from) + ? new SingleColumnRelation(to, mapKey, operator(), value, inValues) + : this; + } + @Override public String toString() { @@ -185,6 +205,16 @@ public final class SingleColumnRelation extends Relation return new SingleColumnRestriction.ContainsRestriction(columnDef, term, isKey); } + @Override + protected Restriction newIsNotRestriction(CFMetaData cfm, + VariableSpecifications boundNames) throws InvalidRequestException + { + ColumnDefinition columnDef = toColumnDefinition(cfm, entity); + // currently enforced by the grammar + assert value == Constants.NULL_LITERAL : "Expected null literal for IS NOT relation: " + this.toString(); + return new SingleColumnRestriction.IsNotNullRestriction(columnDef); + } + /** * Returns the receivers for this relation. * @param columnDef the column definition http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/Term.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Term.java b/src/java/org/apache/cassandra/cql3/Term.java index 6fa0c76..1f6bc62 100644 --- a/src/java/org/apache/cassandra/cql3/Term.java +++ b/src/java/org/apache/cassandra/cql3/Term.java @@ -81,7 +81,7 @@ public interface Term * - a function call * - a marker */ - public interface Raw extends AssignmentTestable + public abstract class Raw implements AssignmentTestable { /** * This method validates this RawTerm is valid for provided column @@ -93,12 +93,23 @@ public interface Term * case this RawTerm describe a list index or a map key, etc... * @return the prepared term. */ - public Term prepare(String keyspace, ColumnSpecification receiver) throws InvalidRequestException; + public abstract Term prepare(String keyspace, ColumnSpecification receiver) throws InvalidRequestException; + + /** + * @return a String representation of the raw term that can be used when reconstructing a CQL query string. + */ + public abstract String getText(); + + @Override + public String toString() + { + return getText(); + } } - public interface MultiColumnRaw extends Raw + public abstract class MultiColumnRaw extends Term.Raw { - public Term prepare(String keyspace, List<? extends ColumnSpecification> receiver) throws InvalidRequestException; + public abstract Term prepare(String keyspace, List<? extends ColumnSpecification> receiver) throws InvalidRequestException; } /** http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/TokenRelation.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/TokenRelation.java b/src/java/org/apache/cassandra/cql3/TokenRelation.java index 6b487ef..2c13b19 100644 --- a/src/java/org/apache/cassandra/cql3/TokenRelation.java +++ b/src/java/org/apache/cassandra/cql3/TokenRelation.java @@ -20,6 +20,7 @@ package org.apache.cassandra.cql3; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import com.google.common.base.Joiner; @@ -63,6 +64,16 @@ public final class TokenRelation extends Relation return true; } + public Term.Raw getValue() + { + return value; + } + + public List<? extends Term.Raw> getInValues() + { + return null; + } + @Override protected Restriction newEQRestriction(CFMetaData cfm, VariableSpecifications boundNames) throws InvalidRequestException { @@ -95,6 +106,12 @@ public final class TokenRelation extends Relation } @Override + protected Restriction newIsNotRestriction(CFMetaData cfm, VariableSpecifications boundNames) throws InvalidRequestException + { + throw invalidRequest("%s cannot be used with the token function", operator()); + } + + @Override protected Term toTerm(List<? extends ColumnSpecification> receivers, Raw raw, String keyspace, @@ -105,6 +122,15 @@ public final class TokenRelation extends Relation return term; } + public Relation renameIdentifier(ColumnIdentifier.Raw from, ColumnIdentifier.Raw to) + { + if (!entities.contains(from)) + return this; + + List<ColumnIdentifier.Raw> newEntities = entities.stream().map(e -> e.equals(from) ? to : e).collect(Collectors.toList()); + return new TokenRelation(newEntities, operator(), value); + } + @Override public String toString() { http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/Tuples.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Tuples.java b/src/java/org/apache/cassandra/cql3/Tuples.java index 933088f..6c7df47 100644 --- a/src/java/org/apache/cassandra/cql3/Tuples.java +++ b/src/java/org/apache/cassandra/cql3/Tuples.java @@ -21,6 +21,7 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -53,7 +54,7 @@ public class Tuples * A raw, literal tuple. When prepared, this will become a Tuples.Value or Tuples.DelayedValue, depending * on whether the tuple holds NonTerminals. */ - public static class Literal implements Term.MultiColumnRaw + public static class Literal extends Term.MultiColumnRaw { private final List<Term.Raw> elements; @@ -133,10 +134,9 @@ public class Tuples } } - @Override - public String toString() + public String getText() { - return tupleToString(elements); + return elements.stream().map(Term.Raw::getText).collect(Collectors.joining(", ", "(", ")")); } } @@ -287,7 +287,7 @@ public class Tuples * For example, "SELECT ... WHERE (col1, col2) > ?". * } */ - public static class Raw extends AbstractMarker.Raw implements Term.MultiColumnRaw + public static class Raw extends AbstractMarker.MultiColumnRaw { public Raw(int bindIndex) { @@ -317,18 +317,12 @@ public class Tuples { return new Tuples.Marker(bindIndex, makeReceiver(receivers)); } - - @Override - public AbstractMarker prepare(String keyspace, ColumnSpecification receiver) - { - throw new AssertionError("Tuples.Raw.prepare() requires a list of receivers"); - } } /** * A raw marker for an IN list of tuples, like "SELECT ... WHERE (a, b, c) IN ?" */ - public static class INRaw extends AbstractMarker.Raw implements MultiColumnRaw + public static class INRaw extends AbstractMarker.MultiColumnRaw { public INRaw(int bindIndex) { @@ -362,12 +356,6 @@ public class Tuples { return new InMarker(bindIndex, makeInReceiver(receivers)); } - - @Override - public AbstractMarker prepare(String keyspace, ColumnSpecification receiver) - { - throw new AssertionError("Tuples.INRaw.prepare() requires a list of receivers"); - } } /** http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/TypeCast.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/TypeCast.java b/src/java/org/apache/cassandra/cql3/TypeCast.java index 561a158..890b34f 100644 --- a/src/java/org/apache/cassandra/cql3/TypeCast.java +++ b/src/java/org/apache/cassandra/cql3/TypeCast.java @@ -20,7 +20,7 @@ package org.apache.cassandra.cql3; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.exceptions.InvalidRequestException; -public class TypeCast implements Term.Raw +public class TypeCast extends Term.Raw { private final CQL3Type.Raw type; private final Term.Raw term; @@ -58,8 +58,7 @@ public class TypeCast implements Term.Raw return AssignmentTestable.TestResult.NOT_ASSIGNABLE; } - @Override - public String toString() + public String getText() { return "(" + type + ")" + term; } http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/UserTypes.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/UserTypes.java b/src/java/org/apache/cassandra/cql3/UserTypes.java index 22c7987..0beff06 100644 --- a/src/java/org/apache/cassandra/cql3/UserTypes.java +++ b/src/java/org/apache/cassandra/cql3/UserTypes.java @@ -42,7 +42,7 @@ public abstract class UserTypes ut.fieldType(field)); } - public static class Literal implements Term.Raw + public static class Literal extends Term.Raw { public final Map<ColumnIdentifier, Term.Raw> entries; @@ -118,8 +118,7 @@ public abstract class UserTypes } } - @Override - public String toString() + public String getText() { StringBuilder sb = new StringBuilder(); sb.append("{"); @@ -127,7 +126,7 @@ public abstract class UserTypes while (iter.hasNext()) { Map.Entry<ColumnIdentifier, Term.Raw> entry = iter.next(); - sb.append(entry.getKey()).append(":").append(entry.getValue()); + sb.append(entry.getKey()).append(": ").append(entry.getValue().getText()); if (iter.hasNext()) sb.append(", "); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java b/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java index b25d079..1766a79 100644 --- a/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java +++ b/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java @@ -20,6 +20,7 @@ package org.apache.cassandra.cql3.functions; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import com.google.common.collect.Iterables; @@ -110,7 +111,7 @@ public class FunctionCall extends Term.NonTerminal throw new AssertionError(); } - public static class Raw implements Term.Raw + public static class Raw extends Term.Raw { private FunctionName name; private final List<Term.Raw> terms; @@ -181,18 +182,9 @@ public class FunctionCall extends Term.NonTerminal } } - @Override - public String toString() + public String getText() { - StringBuilder sb = new StringBuilder(); - sb.append(name).append("("); - for (int i = 0; i < terms.size(); i++) - { - if (i > 0) - sb.append(", "); - sb.append(terms.get(i)); - } - return sb.append(")").toString(); + return name + terms.stream().map(Term.Raw::getText).collect(Collectors.joining(", ", "(", ")")); } } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java index 0f56fd9..023c2ac 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java @@ -17,17 +17,9 @@ */ package org.apache.cassandra.cql3.restrictions; -import java.nio.ByteBuffer; - -import org.apache.cassandra.cql3.ColumnSpecification; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.statements.Bound; import org.apache.cassandra.db.MultiCBuilder; -import org.apache.cassandra.exceptions.InvalidRequestException; - -import static org.apache.cassandra.cql3.statements.RequestValidations.checkBindValueSet; -import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse; -import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull; /** * Base class for <code>Restriction</code>s @@ -71,6 +63,12 @@ abstract class AbstractRestriction implements Restriction } @Override + public boolean isNotNull() + { + return false; + } + + @Override public boolean hasBound(Bound b) { return true; http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java index f82cb11..18e7105 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java @@ -167,6 +167,12 @@ abstract class ForwardingPrimaryKeyRestrictions implements PrimaryKeyRestriction } @Override + public boolean isNotNull() + { + return getDelegate().isNotNull(); + } + + @Override public boolean isMultiColumn() { return getDelegate().isMultiColumn(); http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java index b60930e..069a01b 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java @@ -459,4 +459,59 @@ public abstract class MultiColumnRestriction extends AbstractRestriction return Collections.singletonList(terminal.get(options.getProtocolVersion())); } } + + public static class NotNullRestriction extends MultiColumnRestriction + { + public NotNullRestriction(List<ColumnDefinition> columnDefs) + { + super(columnDefs); + assert columnDefs.size() == 1; + } + + @Override + public Iterable<Function> getFunctions() + { + return Collections.emptyList(); + } + + @Override + public boolean isNotNull() + { + return true; + } + + @Override + public String toString() + { + return "IS NOT NULL"; + } + + @Override + public Restriction doMergeWith(Restriction otherRestriction) throws InvalidRequestException + { + throw invalidRequest("%s cannot be restricted by a relation if it includes an IS NOT NULL clause", + getColumnsInCommons(otherRestriction)); + } + + @Override + protected boolean isSupportedBy(Index index) + { + for(ColumnDefinition column : columnDefs) + if (index.supportsExpression(column, Operator.IS_NOT)) + return true; + return false; + } + + @Override + public MultiCBuilder appendTo(MultiCBuilder builder, QueryOptions options) + { + throw new UnsupportedOperationException("Cannot use IS NOT NULL restriction for slicing"); + } + + @Override + public final void addRowFilterTo(RowFilter filter, SecondaryIndexManager indexMananger, QueryOptions options) throws InvalidRequestException + { + throw new UnsupportedOperationException("Secondary indexes do not support IS NOT NULL restrictions"); + } + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java b/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java index a21087e..a84ebc4 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java @@ -41,6 +41,7 @@ public interface Restriction public boolean isEQ(); public boolean isIN(); public boolean isContains(); + public boolean isNotNull(); public boolean isMultiColumn(); /** http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java index 25146e5..d851253 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java @@ -587,4 +587,62 @@ public abstract class SingleColumnRestriction extends AbstractRestriction super(columnDef); } } + + public static final class IsNotNullRestriction extends SingleColumnRestriction + { + public IsNotNullRestriction(ColumnDefinition columnDef) + { + super(columnDef); + } + + @Override + public Iterable<Function> getFunctions() + { + return Collections.emptyList(); + } + + @Override + public boolean isNotNull() + { + return true; + } + + @Override + MultiColumnRestriction toMultiColumnRestriction() + { + return new MultiColumnRestriction.NotNullRestriction(Collections.singletonList(columnDef)); + } + + @Override + public void addRowFilterTo(RowFilter filter, + SecondaryIndexManager indexManager, + QueryOptions options) + { + throw new UnsupportedOperationException("Secondary indexes do not support IS NOT NULL restrictions"); + } + + @Override + public MultiCBuilder appendTo(MultiCBuilder builder, QueryOptions options) + { + throw new UnsupportedOperationException("Cannot use IS NOT NULL restriction for slicing"); + } + + @Override + public String toString() + { + return "IS NOT NULL"; + } + + @Override + public Restriction doMergeWith(Restriction otherRestriction) throws InvalidRequestException + { + throw invalidRequest("%s cannot be restricted by a relation if it includes an IS NOT NULL", columnDef.name); + } + + @Override + protected boolean isSupportedBy(Index index) + { + return index.supportsExpression(columnDef, Operator.IS_NOT); + } + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java index b1c7aff..1bd4218 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java @@ -77,6 +77,8 @@ public final class StatementRestrictions */ private RestrictionSet nonPrimaryKeyRestrictions; + private Set<ColumnDefinition> notNullColumns; + /** * The restrictions used to build the row filter */ @@ -111,6 +113,7 @@ public final class StatementRestrictions this.partitionKeyRestrictions = new PrimaryKeyRestrictionSet(cfm.getKeyValidatorAsClusteringComparator(), true); this.clusteringColumnsRestrictions = new PrimaryKeyRestrictionSet(cfm.comparator, false); this.nonPrimaryKeyRestrictions = new RestrictionSet(); + this.notNullColumns = new HashSet<>(); } public StatementRestrictions(StatementType type, @@ -119,7 +122,8 @@ public final class StatementRestrictions VariableSpecifications boundNames, boolean selectsOnlyStaticColumns, boolean selectACollection, - boolean useFiltering) + boolean useFiltering, + boolean forView) throws InvalidRequestException { this(type, cfm); @@ -133,7 +137,20 @@ public final class StatementRestrictions * in CQL so far) */ for (Relation relation : whereClause.relations) - addRestriction(relation.toRestriction(cfm, boundNames)); + { + if (relation.operator() == Operator.IS_NOT) + { + if (!forView) + throw new InvalidRequestException("Unsupported restriction: " + relation); + + for (ColumnDefinition def : relation.toRestriction(cfm, boundNames).getColumnDefs()) + this.notNullColumns.add(def); + } + else + { + addRestriction(relation.toRestriction(cfm, boundNames)); + } + } boolean hasQueriableClusteringColumnIndex = false; boolean hasQueriableIndex = false; @@ -180,7 +197,7 @@ public final class StatementRestrictions throw invalidRequest("Cannot restrict clustering columns when selecting only static columns"); } - processClusteringColumnsRestrictions(hasQueriableIndex, selectsOnlyStaticColumns, selectACollection); + processClusteringColumnsRestrictions(hasQueriableIndex, selectsOnlyStaticColumns, selectACollection, forView); // Covers indexes on the first clustering column (among others). if (isKeyRange && hasQueriableClusteringColumnIndex) @@ -244,19 +261,56 @@ public final class StatementRestrictions } /** - * Returns the non-PK column that are restricted. + * Returns the non-PK column that are restricted. If includeNotNullRestrictions is true, columns that are restricted + * by an IS NOT NULL restriction will be included, otherwise they will not be included (unless another restriction + * applies to them). */ - public Set<ColumnDefinition> nonPKRestrictedColumns() + public Set<ColumnDefinition> nonPKRestrictedColumns(boolean includeNotNullRestrictions) { Set<ColumnDefinition> columns = new HashSet<>(); for (Restrictions r : indexRestrictions.getRestrictions()) + { for (ColumnDefinition def : r.getColumnDefs()) if (!def.isPrimaryKeyColumn()) columns.add(def); + } + + if (includeNotNullRestrictions) + { + for (ColumnDefinition def : notNullColumns) + { + if (!def.isPrimaryKeyColumn()) + columns.add(def); + } + } + return columns; } /** + * @return the set of columns that have an IS NOT NULL restriction on them + */ + public Set<ColumnDefinition> notNullColumns() + { + return notNullColumns; + } + + /** + * @return true if column is restricted by some restriction, false otherwise + */ + public boolean isRestricted(ColumnDefinition column) + { + if (notNullColumns.contains(column)) + return true; + else if (column.isPartitionKey()) + return partitionKeyRestrictions.getColumnDefs().contains(column); + else if (column.isClusteringColumn()) + return clusteringColumnsRestrictions.getColumnDefs().contains(column); + else + return nonPrimaryKeyRestrictions.getColumnDefs().contains(column); + } + + /** * Checks if the restrictions on the partition key is an IN restriction. * * @return <code>true</code> the restrictions on the partition key is an IN restriction, <code>false</code> @@ -370,7 +424,8 @@ public final class StatementRestrictions */ private void processClusteringColumnsRestrictions(boolean hasQueriableIndex, boolean selectsOnlyStaticColumns, - boolean selectACollection) + boolean selectACollection, + boolean forView) throws InvalidRequestException { checkFalse(!type.allowClusteringColumnSlices() && clusteringColumnsRestrictions.isSlice(), "Slice restrictions are not supported on the clustering columns in %s statements", type); @@ -401,7 +456,7 @@ public final class StatementRestrictions if (!clusteringColumn.equals(restrictedColumn)) { - checkTrue(hasQueriableIndex, + checkTrue(hasQueriableIndex || forView, "PRIMARY KEY column \"%s\" cannot be restricted as preceding column \"%s\" is not restricted", restrictedColumn.name, clusteringColumn.name); http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java index 0d2011b..c410f10 100644 --- a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java @@ -341,7 +341,7 @@ public class AlterTableStatement extends SchemaAlteringStatement ViewDefinition viewCopy = view.copy(); ColumnIdentifier viewFrom = entry.getKey().prepare(viewCopy.metadata); ColumnIdentifier viewTo = entry.getValue().prepare(viewCopy.metadata); - viewCopy.metadata.renameColumn(viewFrom, viewTo); + viewCopy.renameColumn(viewFrom, viewTo); if (viewUpdates == null) viewUpdates = new ArrayList<>(); http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/statements/CreateViewStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateViewStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateViewStatement.java index 1a020ce..586b09b 100644 --- a/src/java/org/apache/cassandra/cql3/statements/CreateViewStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/CreateViewStatement.java @@ -18,10 +18,8 @@ package org.apache.cassandra.cql3.statements; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; +import java.util.stream.Collectors; import com.google.common.collect.Iterables; @@ -30,8 +28,8 @@ import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.ColumnDefinition; import org.apache.cassandra.config.Schema; import org.apache.cassandra.config.ViewDefinition; -import org.apache.cassandra.cql3.CFName; -import org.apache.cassandra.cql3.ColumnIdentifier; +import org.apache.cassandra.cql3.*; +import org.apache.cassandra.cql3.restrictions.StatementRestrictions; import org.apache.cassandra.cql3.selection.RawSelector; import org.apache.cassandra.cql3.selection.Selectable; import org.apache.cassandra.db.marshal.AbstractType; @@ -52,7 +50,7 @@ public class CreateViewStatement extends SchemaAlteringStatement { private final CFName baseName; private final List<RawSelector> selectClause; - private final List<ColumnIdentifier.Raw> notNullWhereClause; + private final WhereClause whereClause; private final List<ColumnIdentifier.Raw> partitionKeys; private final List<ColumnIdentifier.Raw> clusteringKeys; public final CFProperties properties = new CFProperties(); @@ -61,7 +59,7 @@ public class CreateViewStatement extends SchemaAlteringStatement public CreateViewStatement(CFName viewName, CFName baseName, List<RawSelector> selectClause, - List<ColumnIdentifier.Raw> notNullWhereClause, + WhereClause whereClause, List<ColumnIdentifier.Raw> partitionKeys, List<ColumnIdentifier.Raw> clusteringKeys, boolean ifNotExists) @@ -69,7 +67,7 @@ public class CreateViewStatement extends SchemaAlteringStatement super(viewName); this.baseName = baseName; this.selectClause = selectClause; - this.notNullWhereClause = notNullWhereClause; + this.whereClause = whereClause; this.partitionKeys = partitionKeys; this.clusteringKeys = clusteringKeys; this.ifNotExists = ifNotExists; @@ -194,34 +192,50 @@ public class CreateViewStatement extends SchemaAlteringStatement throw new InvalidRequestException(String.format("Cannot use Static column '%s' in PRIMARY KEY of materialized view", identifier)); } + // build the select statement + Map<ColumnIdentifier.Raw, Boolean> orderings = Collections.emptyMap(); + SelectStatement.Parameters parameters = new SelectStatement.Parameters(orderings, false, true, false); + SelectStatement.RawStatement rawSelect = new SelectStatement.RawStatement(baseName, parameters, selectClause, whereClause, null); + + ClientState state = ClientState.forInternalCalls(); + state.setKeyspace(keyspace()); + + rawSelect.prepareKeyspace(state); + rawSelect.setBoundVariables(getBoundVariables()); + + ParsedStatement.Prepared prepared = rawSelect.prepare(true); + SelectStatement select = (SelectStatement) prepared.statement; + StatementRestrictions restrictions = select.getRestrictions(); + + if (!prepared.boundNames.isEmpty()) + throw new InvalidRequestException("Cannot use query parameters in CREATE MATERIALIZED VIEW statements"); + + if (!restrictions.nonPKRestrictedColumns(false).isEmpty()) + { + throw new InvalidRequestException(String.format( + "Non-primary key columns cannot be restricted in the SELECT statement used for materialized view " + + "creation (got restrictions on: %s)", + restrictions.nonPKRestrictedColumns(false).stream().map(def -> def.name.toString()).collect(Collectors.joining(", ")))); + } + + String whereClauseText = View.relationsToWhereClause(whereClause.relations); + Set<ColumnIdentifier> basePrimaryKeyCols = new HashSet<>(); for (ColumnDefinition definition : Iterables.concat(cfm.partitionKeyColumns(), cfm.clusteringColumns())) basePrimaryKeyCols.add(definition.name); List<ColumnIdentifier> targetClusteringColumns = new ArrayList<>(); List<ColumnIdentifier> targetPartitionKeys = new ArrayList<>(); - Set<ColumnIdentifier> notNullColumns = new HashSet<>(); - if (notNullWhereClause != null) - { - for (ColumnIdentifier.Raw raw : notNullWhereClause) - { - notNullColumns.add(raw.prepare(cfm)); - } - } // This is only used as an intermediate state; this is to catch whether multiple non-PK columns are used boolean hasNonPKColumn = false; for (ColumnIdentifier.Raw raw : partitionKeys) - { - hasNonPKColumn = getColumnIdentifier(cfm, basePrimaryKeyCols, hasNonPKColumn, raw, targetPartitionKeys, notNullColumns); - } + hasNonPKColumn = getColumnIdentifier(cfm, basePrimaryKeyCols, hasNonPKColumn, raw, targetPartitionKeys, restrictions); for (ColumnIdentifier.Raw raw : clusteringKeys) - { - hasNonPKColumn = getColumnIdentifier(cfm, basePrimaryKeyCols, hasNonPKColumn, raw, targetClusteringColumns, notNullColumns); - } + hasNonPKColumn = getColumnIdentifier(cfm, basePrimaryKeyCols, hasNonPKColumn, raw, targetClusteringColumns, restrictions); - // We need to include all of the primary key colums from the base table in order to make sure that we do not + // We need to include all of the primary key columns from the base table in order to make sure that we do not // overwrite values in the view. We cannot support "collapsing" the base table into a smaller number of rows in // the view because if we need to generate a tombstone, we have no way of knowing which value is currently being // used in the view and whether or not to generate a tombstone. In order to not surprise our users, we require @@ -269,7 +283,10 @@ public class CreateViewStatement extends SchemaAlteringStatement ViewDefinition definition = new ViewDefinition(keyspace(), columnFamily(), Schema.instance.getId(keyspace(), baseName.getColumnFamily()), + baseName.getColumnFamily(), included.isEmpty(), + rawSelect, + whereClauseText, viewCfm); try @@ -291,24 +308,21 @@ public class CreateViewStatement extends SchemaAlteringStatement boolean hasNonPKColumn, ColumnIdentifier.Raw raw, List<ColumnIdentifier> columns, - Set<ColumnIdentifier> allowedPKColumns) + StatementRestrictions restrictions) { ColumnIdentifier identifier = raw.prepare(cfm); + ColumnDefinition def = cfm.getColumnDefinition(identifier); boolean isPk = basePK.contains(identifier); if (!isPk && hasNonPKColumn) - { throw new InvalidRequestException(String.format("Cannot include more than one non-primary key column '%s' in materialized view partition key", identifier)); - } // We don't need to include the "IS NOT NULL" filter on a non-composite partition key // because we will never allow a single partition key to be NULL boolean isSinglePartitionKey = cfm.getColumnDefinition(identifier).isPartitionKey() && cfm.partitionKeyColumns().size() == 1; - if (!allowedPKColumns.remove(identifier) && !isSinglePartitionKey) - { + if (!isSinglePartitionKey && !restrictions.isRestricted(def)) throw new InvalidRequestException(String.format("Primary key column '%s' is required to be filtered by 'IS NOT NULL'", identifier)); - } columns.add(identifier); return !isPk; http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/statements/IndexTarget.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/IndexTarget.java b/src/java/org/apache/cassandra/cql3/statements/IndexTarget.java index 6210a86..8cdf2c8 100644 --- a/src/java/org/apache/cassandra/cql3/statements/IndexTarget.java +++ b/src/java/org/apache/cassandra/cql3/statements/IndexTarget.java @@ -60,18 +60,10 @@ public class IndexTarget public String asCqlString(CFMetaData cfm) { - if (! cfm.getColumnDefinition(column).type.isCollection()) - return maybeEscapeQuotedName(column.toString()); + if (!cfm.getColumnDefinition(column).type.isCollection()) + return column.toCQLString(); - return String.format("%s(%s)", type.toString(), maybeEscapeQuotedName(column.toString())); - } - - // Quoted column names may themselves contain quotes, these need - // to be escaped with a preceding quote when written out as cql. - // Of course, the escaped name also needs to be wrapped in quotes. - private String maybeEscapeQuotedName(String name) - { - return quoteName ? '\"' + name.replace("\"", "\"\"") + '\"' : name; + return String.format("%s(%s)", type.toString(), column.toCQLString()); } public static class Raw http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java index 54a4f28..23a26d0 100644 --- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java @@ -855,7 +855,7 @@ public abstract class ModificationStatement implements CQLStatement throw new InvalidRequestException(CUSTOM_EXPRESSIONS_NOT_ALLOWED); boolean applyOnlyToStaticColumns = appliesOnlyToStaticColumns(operations, conditions); - return new StatementRestrictions(type, cfm, where, boundNames, applyOnlyToStaticColumns, false, false); + return new StatementRestrictions(type, cfm, where, boundNames, applyOnlyToStaticColumns, false, false, false); } /** http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/statements/ParsedStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/ParsedStatement.java b/src/java/org/apache/cassandra/cql3/statements/ParsedStatement.java index 539a957..4c3f8a9 100644 --- a/src/java/org/apache/cassandra/cql3/statements/ParsedStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/ParsedStatement.java @@ -39,6 +39,11 @@ public abstract class ParsedStatement this.variables = new VariableSpecifications(boundNames); } + public void setBoundVariables(VariableSpecifications variables) + { + this.variables = variables; + } + public abstract Prepared prepare() throws RequestValidationException; public static class Prepared http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java index 170bfdf..7848556 100644 --- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java @@ -143,7 +143,7 @@ public class SelectStatement implements CQLStatement if (!def.isPrimaryKeyColumn()) builder.add(def); // as well as any restricted column (so we can actually apply the restriction) - builder.addAll(restrictions.nonPKRestrictedColumns()); + builder.addAll(restrictions.nonPKRestrictedColumns(true)); return builder.build(); } @@ -451,6 +451,17 @@ public class SelectStatement implements CQLStatement return new SinglePartitionReadCommand.Group(commands, limit); } + /** + * Returns a read command that can be used internally to filter individual rows for materialized views. + */ + public SinglePartitionReadCommand internalReadForView(DecoratedKey key, int nowInSec) + { + QueryOptions options = QueryOptions.forInternalCalls(Collections.emptyList()); + ClusteringIndexFilter filter = makeClusteringIndexFilter(options); + RowFilter rowFilter = getRowFilter(options); + return SinglePartitionReadCommand.create(cfm, nowInSec, queriedColumns, rowFilter, DataLimits.NONE, key, filter); + } + private ReadQuery getRangeCommand(QueryOptions options, DataLimits limit, int nowInSec) throws RequestValidationException { ClusteringIndexFilter clusteringIndexFilter = makeClusteringIndexFilter(options); @@ -738,6 +749,11 @@ public class SelectStatement implements CQLStatement public ParsedStatement.Prepared prepare() throws InvalidRequestException { + return prepare(false); + } + + public ParsedStatement.Prepared prepare(boolean forView) throws InvalidRequestException + { CFMetaData cfm = ThriftValidation.validateColumnFamily(keyspace(), columnFamily()); VariableSpecifications boundNames = getBoundVariables(); @@ -745,7 +761,7 @@ public class SelectStatement implements CQLStatement ? Selection.wildcard(cfm) : Selection.fromSelectors(cfm, selectClause); - StatementRestrictions restrictions = prepareRestrictions(cfm, boundNames, selection); + StatementRestrictions restrictions = prepareRestrictions(cfm, boundNames, selection, forView); if (parameters.isDistinct) validateDistinctSelection(cfm, selection, restrictions); @@ -755,6 +771,7 @@ public class SelectStatement implements CQLStatement if (!parameters.orderings.isEmpty()) { + assert !forView; verifyOrderingIsAllowed(restrictions); orderingComparator = getOrderingComparator(cfm, selection, restrictions); isReversed = isReversed(cfm); @@ -787,7 +804,8 @@ public class SelectStatement implements CQLStatement */ private StatementRestrictions prepareRestrictions(CFMetaData cfm, VariableSpecifications boundNames, - Selection selection) throws InvalidRequestException + Selection selection, + boolean forView) throws InvalidRequestException { try { @@ -797,7 +815,8 @@ public class SelectStatement implements CQLStatement boundNames, selection.containsOnlyStaticColumns(), selection.containsACollection(), - parameters.allowFiltering); + parameters.allowFiltering, + forView); } catch (UnrecognizedEntityException e) { http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java index f8435eb..ce9aaee 100644 --- a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java @@ -186,6 +186,7 @@ public class UpdateStatement extends ModificationStatement boundNames, applyOnlyToStaticColumns, false, + false, false); return new UpdateStatement(StatementType.INSERT, @@ -254,6 +255,7 @@ public class UpdateStatement extends ModificationStatement boundNames, applyOnlyToStaticColumns, false, + false, false); return new UpdateStatement(StatementType.INSERT, http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/db/PartitionRangeReadCommand.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/PartitionRangeReadCommand.java b/src/java/org/apache/cassandra/db/PartitionRangeReadCommand.java index 4e96d81..f17f3e3 100644 --- a/src/java/org/apache/cassandra/db/PartitionRangeReadCommand.java +++ b/src/java/org/apache/cassandra/db/PartitionRangeReadCommand.java @@ -139,15 +139,22 @@ public class PartitionRangeReadCommand extends ReadCommand return DatabaseDescriptor.getRangeRpcTimeout(); } - public boolean selects(DecoratedKey partitionKey, Clustering clustering) + public boolean selectsKey(DecoratedKey key) { - if (!dataRange().contains(partitionKey)) + if (!dataRange().contains(key)) return false; + return rowFilter().partitionKeyRestrictionsAreSatisfiedBy(key, metadata().getKeyValidator()); + } + + public boolean selectsClustering(DecoratedKey key, Clustering clustering) + { if (clustering == Clustering.STATIC_CLUSTERING) return !columnFilter().fetchedColumns().statics.isEmpty(); - return dataRange().clusteringIndexFilter(partitionKey).selects(clustering); + if (!dataRange().clusteringIndexFilter(key).selects(clustering)) + return false; + return rowFilter().clusteringKeyRestrictionsAreSatisfiedBy(clustering); } public PartitionIterator execute(ConsistencyLevel consistency, ClientState clientState) throws RequestExecutionException http://git-wip-us.apache.org/repos/asf/cassandra/blob/5a4253b6/src/java/org/apache/cassandra/db/ReadCommand.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/ReadCommand.java b/src/java/org/apache/cassandra/db/ReadCommand.java index 3d08f17..1c1da42 100644 --- a/src/java/org/apache/cassandra/db/ReadCommand.java +++ b/src/java/org/apache/cassandra/db/ReadCommand.java @@ -276,18 +276,6 @@ public abstract class ReadCommand implements ReadQuery */ public abstract ReadCommand copy(); - /** - * Whether the provided row, identified by its primary key components, is selected by - * this read command. - * - * @param partitionKey the partition key for the row to test. - * @param clustering the clustering for the row to test. - * - * @return whether the row of partition key {@code partitionKey} and clustering - * {@code clustering} is selected by this command. - */ - public abstract boolean selects(DecoratedKey partitionKey, Clustering clustering); - protected abstract UnfilteredPartitionIterator queryStorage(ColumnFamilyStore cfs, ReadOrderGroup orderGroup); protected abstract int oldestUnrepairedTombstone();
