http://git-wip-us.apache.org/repos/asf/cassandra/blob/ce8c9b55/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 6296b97..5985962 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java @@ -118,9 +118,9 @@ public abstract class SingleColumnRestriction extends AbstractRestriction */ protected abstract boolean isSupportedBy(Index index); - public static final class EQRestriction extends SingleColumnRestriction + public static class EQRestriction extends SingleColumnRestriction { - private final Term value; + public final Term value; public EQRestriction(ColumnDefinition columnDef, Term value) { @@ -308,7 +308,7 @@ public abstract class SingleColumnRestriction extends AbstractRestriction public static class SliceRestriction extends SingleColumnRestriction { - private final TermSlice slice; + public final TermSlice slice; public SliceRestriction(ColumnDefinition columnDef, Bound bound, boolean inclusive, Term term) { @@ -404,7 +404,7 @@ public abstract class SingleColumnRestriction extends AbstractRestriction return String.format("SLICE%s", slice); } - private SliceRestriction(ColumnDefinition columnDef, TermSlice slice) + SliceRestriction(ColumnDefinition columnDef, TermSlice slice) { super(columnDef); this.slice = slice; @@ -646,4 +646,202 @@ public abstract class SingleColumnRestriction extends AbstractRestriction return index.supportsExpression(columnDef, Operator.IS_NOT); } } + + /** + * Super Column Compatibiltiy + */ + + public static class SuperColumnMultiEQRestriction extends EQRestriction + { + public ByteBuffer firstValue; + public ByteBuffer secondValue; + + public SuperColumnMultiEQRestriction(ColumnDefinition columnDef, Term value) + { + super(columnDef, value); + } + + @Override + public MultiCBuilder appendTo(MultiCBuilder builder, QueryOptions options) + { + Term term = value.bind(options); + + assert (term instanceof Tuples.Value); + firstValue = ((Tuples.Value)term).getElements().get(0); + secondValue = ((Tuples.Value)term).getElements().get(1); + + builder.addElementToAll(firstValue); + checkFalse(builder.containsNull(), "Invalid null value in condition for column %s", columnDef.name); + checkFalse(builder.containsUnset(), "Invalid unset value for column %s", columnDef.name); + return builder; + } + } + + public static class SuperColumnMultiSliceRestriction extends SliceRestriction + { + public ByteBuffer firstValue; + public ByteBuffer secondValue; + + // These are here to avoid polluting SliceRestriction + public final Bound bound; + public final boolean trueInclusive; + public SuperColumnMultiSliceRestriction(ColumnDefinition columnDef, Bound bound, boolean inclusive, Term term) + { + super(columnDef, bound, true, term); + this.bound = bound; + this.trueInclusive = inclusive; + + } + + @Override + public MultiCBuilder appendBoundTo(MultiCBuilder builder, Bound bound, QueryOptions options) + { + Bound b = reverseBoundIfNeeded(getFirstColumn(), bound); + + if (!hasBound(b)) + return builder; + + Term term = slice.bound(b); + + assert (term instanceof Tuples.Value); + firstValue = ((Tuples.Value)term).getElements().get(0); + secondValue = ((Tuples.Value)term).getElements().get(1); + + checkBindValueSet(firstValue, "Invalid unset value for column %s", columnDef.name); + checkBindValueSet(secondValue, "Invalid unset value for column %s", columnDef.name); + return builder.addElementToAll(firstValue); + + } + } + + public static final class SuperColumnKeyEQRestriction extends EQRestriction + { + public SuperColumnKeyEQRestriction(ColumnDefinition columnDef, Term value) + { + super(columnDef, value); + } + + public ByteBuffer bindValue(QueryOptions options) + { + return value.bindAndGet(options); + } + + @Override + public MultiCBuilder appendBoundTo(MultiCBuilder builder, Bound bound, QueryOptions options) + { + // no-op + return builder; + } + + @Override + public void addRowFilterTo(RowFilter filter, SecondaryIndexManager indexManager, QueryOptions options) throws InvalidRequestException + { + // no-op + } + } + + public static abstract class SuperColumnKeyINRestriction extends INRestriction + { + public SuperColumnKeyINRestriction(ColumnDefinition columnDef) + { + super(columnDef); + } + + @Override + public MultiCBuilder appendTo(MultiCBuilder builder, QueryOptions options) + { + // no-op + return builder; + } + + @Override + public void addRowFilterTo(RowFilter filter, + SecondaryIndexManager indexManager, + QueryOptions options) throws InvalidRequestException + { + // no-op + } + + public void addFunctionsTo(List<Function> functions) + { + // no-op + } + + MultiColumnRestriction toMultiColumnRestriction() + { + // no-op + return null; + } + + public abstract List<ByteBuffer> getValues(QueryOptions options) throws InvalidRequestException; + } + + public static class SuperColumnKeyINRestrictionWithMarkers extends SuperColumnKeyINRestriction + { + protected final AbstractMarker marker; + + public SuperColumnKeyINRestrictionWithMarkers(ColumnDefinition columnDef, AbstractMarker marker) + { + super(columnDef); + this.marker = marker; + } + + public List<ByteBuffer> getValues(QueryOptions options) throws InvalidRequestException + { + Terminal term = marker.bind(options); + checkNotNull(term, "Invalid null value for column %s", columnDef.name); + checkFalse(term == Constants.UNSET_VALUE, "Invalid unset value for column %s", columnDef.name); + Term.MultiItemTerminal lval = (Term.MultiItemTerminal) term; + return lval.getElements(); + } + } + + public static class SuperColumnKeyINRestrictionWithValues extends SuperColumnKeyINRestriction + { + private final List<Term> values; + + public SuperColumnKeyINRestrictionWithValues(ColumnDefinition columnDef, List<Term> values) + { + super(columnDef); + this.values = values; + } + + public List<ByteBuffer> getValues(QueryOptions options) throws InvalidRequestException + { + List<ByteBuffer> buffers = new ArrayList<>(values.size()); + for (Term value : values) + buffers.add(value.bindAndGet(options)); + return buffers; + } + } + + public static class SuperColumnKeySliceRestriction extends SliceRestriction + { + // These are here to avoid polluting SliceRestriction + private Term term; + + public SuperColumnKeySliceRestriction(ColumnDefinition columnDef, Bound bound, boolean inclusive, Term term) + { + super(columnDef, bound, inclusive, term); + this.term = term; + } + + public ByteBuffer bindValue(QueryOptions options) + { + return term.bindAndGet(options); + } + + @Override + public MultiCBuilder appendBoundTo(MultiCBuilder builder, Bound bound, QueryOptions options) + { + // no-op + return builder; + } + + @Override + public void addRowFilterTo(RowFilter filter, SecondaryIndexManager indexManager, QueryOptions options) throws InvalidRequestException + { + // no-op + } + } }
http://git-wip-us.apache.org/repos/asf/cassandra/blob/ce8c9b55/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 d025d8a..84c6958 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java @@ -21,6 +21,7 @@ import java.nio.ByteBuffer; import java.util.*; import com.google.common.base.Joiner; +import com.google.common.collect.Iterators; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.ColumnDefinition; @@ -160,7 +161,10 @@ public final class StatementRestrictions } else { - addRestriction(relation.toRestriction(cfm, boundNames)); + if (cfm.isSuper() && cfm.isDense() && !relation.onToken()) + addRestriction(relation.toSuperColumnAdapter().toRestriction(cfm, boundNames)); + else + addRestriction(relation.toRestriction(cfm, boundNames)); } } @@ -233,9 +237,16 @@ public final class StatementRestrictions Joiner.on(", ").join(nonPrimaryKeyColumns)); } if (hasQueriableIndex) + { usesSecondaryIndexing = true; - else if (!allowFiltering) + } + else if (!allowFiltering && !cfm.isSuper()) + { throw invalidRequest(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE); + } + + checkFalse(clusteringColumnsRestrictions.isEmpty() && cfm.isSuper(), + "Filtering is not supported on SuperColumn tables"); indexRestrictions.add(nonPrimaryKeyRestrictions); } @@ -847,4 +858,15 @@ public final class StatementRestrictions && (clusteringColumnsRestrictions.isEQ() || clusteringColumnsRestrictions.isIN()); } + + private SuperColumnCompatibility.SuperColumnRestrictions cached; + public SuperColumnCompatibility.SuperColumnRestrictions getSuperColumnRestrictions() + { + assert cfm.isSuper() && cfm.isDense(); + + if (cached == null) + cached = new SuperColumnCompatibility.SuperColumnRestrictions(Iterators.concat(((PrimaryKeyRestrictionSet) clusteringColumnsRestrictions).iterator(), + nonPrimaryKeyRestrictions.iterator())); + return cached; + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/ce8c9b55/src/java/org/apache/cassandra/cql3/restrictions/TermSlice.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/restrictions/TermSlice.java b/src/java/org/apache/cassandra/cql3/restrictions/TermSlice.java index ac66b96..4b13877 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/TermSlice.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/TermSlice.java @@ -26,7 +26,7 @@ import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.cql3.statements.Bound; import org.apache.cassandra.index.Index; -final class TermSlice +public final class TermSlice { /** * The slice boundaries. http://git-wip-us.apache.org/repos/asf/cassandra/blob/ce8c9b55/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 708d551..47304b6 100644 --- a/src/java/org/apache/cassandra/cql3/statements/CreateViewStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/CreateViewStatement.java @@ -137,6 +137,8 @@ public class CreateViewStatement extends SchemaAlteringStatement if (cfm.isCounter()) throw new InvalidRequestException("Materialized views are not supported on counter tables"); + if (cfm.isSuper()) + throw new InvalidRequestException("Materialized views are not supported on SuperColumn tables"); if (cfm.isView()) throw new InvalidRequestException("Materialized views cannot be created against other materialized views"); http://git-wip-us.apache.org/repos/asf/cassandra/blob/ce8c9b55/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java b/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java index 4888b43..a0919d7 100644 --- a/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java @@ -116,7 +116,7 @@ public class DeleteStatement extends ModificationStatement public static class Parsed extends ModificationStatement.Parsed { private final List<Operation.RawDeletion> deletions; - private final WhereClause whereClause; + private WhereClause whereClause; public Parsed(CFName name, Attributes.Raw attrs, @@ -139,17 +139,25 @@ public class DeleteStatement extends ModificationStatement { Operations operations = new Operations(type); - for (Operation.RawDeletion deletion : deletions) + if (cfm.isSuper() && cfm.isDense()) { - ColumnDefinition def = getColumnDefinition(cfm, deletion.affectedColumn()); - - // For compact, we only have one value except the key, so the only form of DELETE that make sense is without a column - // list. However, we support having the value name for coherence with the static/sparse case - checkFalse(def.isPrimaryKeyColumn(), "Invalid identifier %s for deletion (should not be a PRIMARY KEY part)", def.name); - - Operation op = deletion.prepare(cfm.ksName, def); - op.collectMarkerSpecification(boundNames); - operations.add(op); + conditions = SuperColumnCompatibility.rebuildLWTColumnConditions(conditions, cfm, whereClause); + whereClause = SuperColumnCompatibility.prepareDeleteOperations(cfm, whereClause, boundNames, operations); + } + else + { + for (Operation.RawDeletion deletion : deletions) + { + ColumnDefinition def = getColumnDefinition(cfm, deletion.affectedColumn()); + + // For compact, we only have one value except the key, so the only form of DELETE that make sense is without a column + // list. However, we support having the value name for coherence with the static/sparse case + checkFalse(def.isPrimaryKeyColumn(), "Invalid identifier %s for deletion (should not be a PRIMARY KEY part)", def.name); + + Operation op = deletion.prepare(cfm.ksName, def); + op.collectMarkerSpecification(boundNames); + operations.add(op); + } } StatementRestrictions restrictions = newRestrictions(cfm, http://git-wip-us.apache.org/repos/asf/cassandra/blob/ce8c9b55/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 0afd34d..28fc90f 100644 --- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java @@ -544,10 +544,19 @@ public abstract class ModificationStatement implements CQLStatement defs.addAll(cfm.partitionKeyColumns()); defs.addAll(cfm.clusteringColumns()); } - for (ColumnDefinition def : columnsWithConditions) - defs.add(def); - selection = Selection.forColumns(cfm, new ArrayList<>(defs)); + + if (cfm.isSuper() && cfm.isDense()) + { + defs.add(cfm.superColumnValueColumn()); + } + else + { + for (ColumnDefinition def : columnsWithConditions) + defs.add(def); + } + + selection = Selection.forColumns(cfm, new ArrayList<>(defs)); } Selection.ResultSetBuilder builder = selection.resultSetBuilder(false); http://git-wip-us.apache.org/repos/asf/cassandra/blob/ce8c9b55/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 3882a23..2e090fa 100644 --- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java @@ -18,7 +18,16 @@ package org.apache.cassandra.cql3.statements; import java.nio.ByteBuffer; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NavigableSet; +import java.util.SortedSet; import com.google.common.base.Objects; import com.google.common.base.Predicate; @@ -29,13 +38,40 @@ import org.slf4j.LoggerFactory; import org.apache.cassandra.auth.Permission; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.ColumnDefinition; -import org.apache.cassandra.cql3.*; +import org.apache.cassandra.cql3.CFName; +import org.apache.cassandra.cql3.CQLStatement; +import org.apache.cassandra.cql3.ColumnIdentifier; +import org.apache.cassandra.cql3.ColumnSpecification; +import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.QueryProcessor; +import org.apache.cassandra.cql3.ResultSet; +import org.apache.cassandra.cql3.SuperColumnCompatibility; +import org.apache.cassandra.cql3.Term; +import org.apache.cassandra.cql3.VariableSpecifications; +import org.apache.cassandra.cql3.WhereClause; import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.cql3.restrictions.StatementRestrictions; import org.apache.cassandra.cql3.selection.RawSelector; import org.apache.cassandra.cql3.selection.Selection; -import org.apache.cassandra.db.*; -import org.apache.cassandra.db.filter.*; +import org.apache.cassandra.db.Clustering; +import org.apache.cassandra.db.ColumnFamilyStore; +import org.apache.cassandra.db.ConsistencyLevel; +import org.apache.cassandra.db.DataRange; +import org.apache.cassandra.db.DecoratedKey; +import org.apache.cassandra.db.Keyspace; +import org.apache.cassandra.db.PartitionPosition; +import org.apache.cassandra.db.PartitionRangeReadCommand; +import org.apache.cassandra.db.ReadOrderGroup; +import org.apache.cassandra.db.ReadQuery; +import org.apache.cassandra.db.SinglePartitionReadCommand; +import org.apache.cassandra.db.Slice; +import org.apache.cassandra.db.Slices; +import org.apache.cassandra.db.filter.ClusteringIndexFilter; +import org.apache.cassandra.db.filter.ClusteringIndexNamesFilter; +import org.apache.cassandra.db.filter.ClusteringIndexSliceFilter; +import org.apache.cassandra.db.filter.ColumnFilter; +import org.apache.cassandra.db.filter.DataLimits; +import org.apache.cassandra.db.filter.RowFilter; import org.apache.cassandra.db.marshal.CollectionType; import org.apache.cassandra.db.marshal.CompositeType; import org.apache.cassandra.db.marshal.Int32Type; @@ -45,7 +81,11 @@ import org.apache.cassandra.db.rows.Row; import org.apache.cassandra.db.rows.RowIterator; import org.apache.cassandra.db.view.View; import org.apache.cassandra.dht.AbstractBounds; -import org.apache.cassandra.exceptions.*; +import org.apache.cassandra.exceptions.InvalidRequestException; +import org.apache.cassandra.exceptions.RequestExecutionException; +import org.apache.cassandra.exceptions.RequestValidationException; +import org.apache.cassandra.exceptions.UnauthorizedException; +import org.apache.cassandra.exceptions.UnrecognizedEntityException; import org.apache.cassandra.index.SecondaryIndexManager; import org.apache.cassandra.serializers.MarshalException; import org.apache.cassandra.service.ClientState; @@ -479,7 +519,8 @@ public class SelectStatement implements CQLStatement { QueryProcessor.validateKey(key); DecoratedKey dk = cfm.decorateKey(ByteBufferUtil.clone(key)); - commands.add(SinglePartitionReadCommand.create(cfm, nowInSec, queriedColumns, rowFilter, limit, dk, filter)); + ColumnFilter cf = (cfm.isSuper() && cfm.isDense()) ? SuperColumnCompatibility.getColumnFilter(cfm, options, restrictions.getSuperColumnRestrictions()) : queriedColumns; + commands.add(SinglePartitionReadCommand.create(cfm, nowInSec, cf, rowFilter, limit, dk, filter)); } return new SinglePartitionReadCommand.Group(commands, limit); @@ -730,6 +771,12 @@ public class SelectStatement implements CQLStatement void processPartition(RowIterator partition, QueryOptions options, Selection.ResultSetBuilder result, int nowInSec) throws InvalidRequestException { + if (cfm.isSuper() && cfm.isDense()) + { + SuperColumnCompatibility.processPartition(cfm, selection, partition, result, options.getProtocolVersion(), restrictions.getSuperColumnRestrictions(), options); + return; + } + int protocolVersion = options.getProtocolVersion(); ByteBuffer[] keyComponents = getComponents(cfm, partition.partitionKey()); http://git-wip-us.apache.org/repos/asf/cassandra/blob/ce8c9b55/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 6f872d4..641b6bb 100644 --- a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java @@ -17,7 +17,6 @@ */ package org.apache.cassandra.cql3.statements; -import java.util.Collection; import java.util.Collections; import java.util.List; @@ -32,6 +31,7 @@ import org.apache.cassandra.db.partitions.PartitionUpdate; import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.Pair; +import static com.google.common.collect.Lists.newArrayList; import static org.apache.cassandra.cql3.statements.RequestValidations.checkContainsNoDuplicates; import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse; import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue; @@ -81,7 +81,7 @@ public class UpdateStatement extends ModificationStatement // For a dense layout, when we translate it to thrift, we don't have a row marker. So we don't accept an insert/update // that only sets the PK unless the is no declared non-PK columns (which we recognize because in that case the compact // value is of type "EmptyType"). - if (cfm.isCompactTable() && updates.isEmpty()) + if ((cfm.isCompactTable() && !cfm.isSuper()) && updates.isEmpty()) { checkTrue(CompactTables.hasEmptyCompactValue(cfm), "Column %s is mandatory for this COMPACT STORAGE table", @@ -155,24 +155,33 @@ public class UpdateStatement extends ModificationStatement Operations operations = new Operations(type); boolean hasClusteringColumnsSet = false; - for (int i = 0; i < columnNames.size(); i++) + if (cfm.isSuper() && cfm.isDense()) { - ColumnDefinition def = getColumnDefinition(cfm, columnNames.get(i)); - - if (def.isClusteringColumn()) - hasClusteringColumnsSet = true; - - Term.Raw value = columnValues.get(i); - - if (def.isPrimaryKeyColumn()) - { - whereClause.add(new SingleColumnRelation(columnNames.get(i), Operator.EQ, value)); - } - else + // SuperColumn familiy updates are always row-level + hasClusteringColumnsSet = true; + SuperColumnCompatibility.prepareInsertOperations(cfm, columnNames, whereClause, columnValues, boundNames, operations); + } + else + { + for (int i = 0; i < columnNames.size(); i++) { - Operation operation = new Operation.SetValue(value).prepare(keyspace(), def); - operation.collectMarkerSpecification(boundNames); - operations.add(operation); + ColumnDefinition def = getColumnDefinition(cfm, columnNames.get(i)); + + if (def.isClusteringColumn()) + hasClusteringColumnsSet = true; + + Term.Raw value = columnValues.get(i); + + if (def.isPrimaryKeyColumn()) + { + whereClause.add(new SingleColumnRelation(columnNames.get(i), Operator.EQ, value)); + } + else + { + Operation operation = new Operation.SetValue(value).prepare(cfm.ksName, def); + operation.collectMarkerSpecification(boundNames); + operations.add(operation); + } } } @@ -218,30 +227,36 @@ public class UpdateStatement extends ModificationStatement { checkFalse(cfm.isCounter(), "INSERT statements are not allowed on counter tables, use UPDATE instead"); - Collection<ColumnDefinition> defs = cfm.allColumns(); + List<ColumnDefinition> defs = newArrayList(cfm.allColumnsInSelectOrder()); Json.Prepared prepared = jsonValue.prepareAndCollectMarkers(cfm, defs, boundNames); WhereClause.Builder whereClause = new WhereClause.Builder(); Operations operations = new Operations(type); boolean hasClusteringColumnsSet = false; - for (ColumnDefinition def : defs) + if (cfm.isSuper() && cfm.isDense()) { - if (def.isClusteringColumn()) - hasClusteringColumnsSet = true; - - Term.Raw raw = prepared.getRawTermForColumn(def); - if (def.isPrimaryKeyColumn()) - { - whereClause.add(new SingleColumnRelation(new ColumnIdentifier.ColumnIdentifierValue(def.name), - Operator.EQ, - raw)); - } - else + hasClusteringColumnsSet = true; + SuperColumnCompatibility.prepareInsertJSONOperations(cfm, defs, boundNames, prepared, whereClause, operations); + } + else + { + for (ColumnDefinition def : defs) { - Operation operation = new Operation.SetValue(raw).prepare(keyspace(), def); - operation.collectMarkerSpecification(boundNames); - operations.add(operation); + if (def.isClusteringColumn()) + hasClusteringColumnsSet = true; + + Term.Raw raw = prepared.getRawTermForColumn(def); + if (def.isPrimaryKeyColumn()) + { + whereClause.add(new SingleColumnRelation(new ColumnIdentifier.ColumnIdentifierValue(def.name), Operator.EQ, raw)); + } + else + { + Operation operation = new Operation.SetValue(raw).prepare(cfm.ksName, def); + operation.collectMarkerSpecification(boundNames); + operations.add(operation); + } } } @@ -270,7 +285,7 @@ public class UpdateStatement extends ModificationStatement { // Provided for an UPDATE private final List<Pair<ColumnIdentifier.Raw, Operation.RawUpdate>> updates; - private final WhereClause whereClause; + private WhereClause whereClause; /** * Creates a new UpdateStatement from a column family name, columns map, consistency @@ -302,17 +317,25 @@ public class UpdateStatement extends ModificationStatement { Operations operations = new Operations(type); - for (Pair<ColumnIdentifier.Raw, Operation.RawUpdate> entry : updates) + if (cfm.isSuper() && cfm.isDense()) + { + conditions = SuperColumnCompatibility.rebuildLWTColumnConditions(conditions, cfm, whereClause); + whereClause = SuperColumnCompatibility.prepareUpdateOperations(cfm, whereClause, updates, boundNames, operations); + } + else { - ColumnDefinition def = getColumnDefinition(cfm, entry.left); + for (Pair<ColumnIdentifier.Raw, Operation.RawUpdate> entry : updates) + { + ColumnDefinition def = getColumnDefinition(cfm, entry.left); - checkFalse(def.isPrimaryKeyColumn(), "PRIMARY KEY part %s found in SET part", def.name); + checkFalse(def.isPrimaryKeyColumn(), "PRIMARY KEY part %s found in SET part", def.name); - Operation operation = entry.right.prepare(keyspace(), def); - operation.collectMarkerSpecification(boundNames); - operations.add(operation); + Operation operation = entry.right.prepare(cfm.ksName, def); + operation.collectMarkerSpecification(boundNames); + operations.add(operation); + } } - + StatementRestrictions restrictions = newRestrictions(cfm, boundNames, operations, http://git-wip-us.apache.org/repos/asf/cassandra/blob/ce8c9b55/src/java/org/apache/cassandra/db/Columns.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/Columns.java b/src/java/org/apache/cassandra/db/Columns.java index cad295c..eb4f761 100644 --- a/src/java/org/apache/cassandra/db/Columns.java +++ b/src/java/org/apache/cassandra/db/Columns.java @@ -422,6 +422,7 @@ public class Columns extends AbstractCollection<ColumnDefinition> implements Col { ByteBuffer name = ByteBufferUtil.readWithVIntLength(in); ColumnDefinition column = metadata.getColumnDefinition(name); + if (column == null) { // If we don't find the definition, it could be we have data for a dropped column, and we shouldn't http://git-wip-us.apache.org/repos/asf/cassandra/blob/ce8c9b55/src/java/org/apache/cassandra/db/CompactTables.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/CompactTables.java b/src/java/org/apache/cassandra/db/CompactTables.java index e31fda3..9da4d94 100644 --- a/src/java/org/apache/cassandra/db/CompactTables.java +++ b/src/java/org/apache/cassandra/db/CompactTables.java @@ -17,13 +17,15 @@ */ package org.apache.cassandra.db; -import java.nio.ByteBuffer; -import java.util.*; +import java.util.HashSet; +import java.util.Set; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.ColumnDefinition; -import org.apache.cassandra.db.marshal.*; -import org.apache.cassandra.utils.ByteBufferUtil; +import org.apache.cassandra.cql3.SuperColumnCompatibility; +import org.apache.cassandra.db.marshal.AbstractType; +import org.apache.cassandra.db.marshal.EmptyType; +import org.apache.cassandra.db.marshal.UTF8Type; /** * Small utility methods pertaining to the encoding of COMPACT STORAGE tables. @@ -54,39 +56,14 @@ import org.apache.cassandra.utils.ByteBufferUtil; * On variation is that if the table comparator is a CompositeType, then the underlying table will have one clustering column by * element of the CompositeType, but the rest of the layout is as above. * - * As far as thrift is concerned, one exception to this is super column families, which have a different layout. Namely, a super - * column families is encoded with: - * CREATE TABLE super ( - * key [key_validation_class], - * super_column_name [comparator], - * [column_metadata_1] [type1], - * ..., - * [column_metadata_n] [type1], - * "" map<[sub_comparator], [default_validation_class]> - * PRIMARY KEY (key, super_column_name) - * ) - * In other words, every super column is encoded by a row. That row has one column for each defined "column_metadata", but it also - * has a special map column (whose name is the empty string as this is guaranteed to never conflict with a user-defined - * "column_metadata") which stores the super column "dynamic" sub-columns. + * SuperColumn families handling and detailed format description can be found in {@code SuperColumnCompatibility}. */ public abstract class CompactTables { - // We use an empty value for the 1) this can't conflict with a user-defined column and 2) this actually - // validate with any comparator which makes it convenient for columnDefinitionComparator(). - public static final ByteBuffer SUPER_COLUMN_MAP_COLUMN = ByteBufferUtil.EMPTY_BYTE_BUFFER; - public static final String SUPER_COLUMN_MAP_COLUMN_STR = UTF8Type.instance.compose(SUPER_COLUMN_MAP_COLUMN); - private CompactTables() {} - public static ColumnDefinition getCompactValueColumn(PartitionColumns columns, boolean isSuper) + public static ColumnDefinition getCompactValueColumn(PartitionColumns columns) { - if (isSuper) - { - for (ColumnDefinition column : columns.regulars) - if (column.name.bytes.equals(SUPER_COLUMN_MAP_COLUMN)) - return column; - throw new AssertionError("Invalid super column table definition, no 'dynamic' map column"); - } assert columns.regulars.simpleColumnCount() == 1 && columns.regulars.complexColumnCount() == 0; return columns.regulars.getSimple(0); } @@ -104,11 +81,6 @@ public abstract class CompactTables return metadata.compactValueColumn().type instanceof EmptyType; } - public static boolean isSuperColumnMapColumn(ColumnDefinition column) - { - return column.kind == ColumnDefinition.Kind.REGULAR && column.name.bytes.equals(SUPER_COLUMN_MAP_COLUMN); - } - public static DefaultNames defaultNameGenerator(Set<String> usedNames) { return new DefaultNames(new HashSet<String>(usedNames)); http://git-wip-us.apache.org/repos/asf/cassandra/blob/ce8c9b55/src/java/org/apache/cassandra/db/LegacyLayout.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/LegacyLayout.java b/src/java/org/apache/cassandra/db/LegacyLayout.java index 4f7bc22..40b9fd3 100644 --- a/src/java/org/apache/cassandra/db/LegacyLayout.java +++ b/src/java/org/apache/cassandra/db/LegacyLayout.java @@ -24,6 +24,7 @@ import java.nio.ByteBuffer; import java.security.MessageDigest; import java.util.*; +import org.apache.cassandra.cql3.SuperColumnCompatibility; import org.apache.cassandra.utils.AbstractIterator; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; @@ -298,7 +299,7 @@ public abstract class LegacyLayout // What it is depends if this a cell for a declared "static" column or a "dynamic" column part of the // super-column internal map. assert columnName != null; // This should never be null for supercolumns, see decodeForSuperColumn() above - values[clusteringSize] = columnName.equals(CompactTables.SUPER_COLUMN_MAP_COLUMN) + values[clusteringSize] = columnName.equals(SuperColumnCompatibility.SUPER_COLUMN_MAP_COLUMN) ? collectionElement : columnName; } http://git-wip-us.apache.org/repos/asf/cassandra/blob/ce8c9b55/src/java/org/apache/cassandra/db/SerializationHeader.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/SerializationHeader.java b/src/java/org/apache/cassandra/db/SerializationHeader.java index 494c2a3..b2ed26e 100644 --- a/src/java/org/apache/cassandra/db/SerializationHeader.java +++ b/src/java/org/apache/cassandra/db/SerializationHeader.java @@ -332,6 +332,7 @@ public class SerializationHeader for (ByteBuffer name : typeMap.keySet()) { ColumnDefinition column = metadata.getColumnDefinition(name); + if (column == null) { // TODO: this imply we don't read data for a column we don't yet know about, which imply this is theoretically http://git-wip-us.apache.org/repos/asf/cassandra/blob/ce8c9b55/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java b/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java index ac9cfd9..01c5e3e 100644 --- a/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java +++ b/src/java/org/apache/cassandra/schema/LegacySchemaMigrator.java @@ -30,6 +30,7 @@ import org.slf4j.LoggerFactory; import org.apache.cassandra.config.*; import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.cql3.QueryProcessor; +import org.apache.cassandra.cql3.SuperColumnCompatibility; import org.apache.cassandra.cql3.UntypedResultSet; import org.apache.cassandra.cql3.functions.FunctionName; import org.apache.cassandra.cql3.functions.UDAggregate; @@ -271,10 +272,11 @@ public final class LegacySchemaMigrator SystemKeyspace.LEGACY_TRIGGERS); UntypedResultSet triggerRows = query(triggersQuery, keyspaceName, tableName); - return decodeTableMetadata(tableRow, columnRows, triggerRows); + return decodeTableMetadata(tableName, tableRow, columnRows, triggerRows); } - private static CFMetaData decodeTableMetadata(UntypedResultSet.Row tableRow, + private static CFMetaData decodeTableMetadata(String tableName, + UntypedResultSet.Row tableRow, UntypedResultSet columnRows, UntypedResultSet triggerRows) { @@ -297,7 +299,7 @@ public final class LegacySchemaMigrator if (rawIsDense != null && !rawIsDense) isDense = false; else - isDense = calculateIsDense(rawComparator, columnRows); + isDense = calculateIsDense(rawComparator, columnRows, isSuper); // now, if switched to sparse, remove redundant compact_value column and the last clustering column, // directly copying CASSANDRA-11502 logic. See CASSANDRA-11315. @@ -389,7 +391,7 @@ public final class LegacySchemaMigrator * information for table just created through thrift, nor for table prior to CASSANDRA-7744, so this * method does its best to infer whether the table is dense or not based on other elements. */ - private static boolean calculateIsDense(AbstractType<?> comparator, UntypedResultSet columnRows) + private static boolean calculateIsDense(AbstractType<?> comparator, UntypedResultSet columnRows, boolean isSuper) { /* * As said above, this method is only here because we need to deal with thrift upgrades. @@ -411,8 +413,15 @@ public final class LegacySchemaMigrator * in the latter case only if the comparator is exactly CompositeType(UTF8Type). */ for (UntypedResultSet.Row columnRow : columnRows) + { if ("regular".equals(columnRow.getString("type"))) return false; + } + + // If we've checked the columns for supercf and found no regulars, it's dense. Relying on the emptiness + // of the value column is not enough due to index calculation. + if (isSuper) + return true; int maxClusteringIdx = -1; for (UntypedResultSet.Row columnRow : columnRows) @@ -431,18 +440,11 @@ public final class LegacySchemaMigrator { String kind = columnRow.getString("type"); - if ("compact_value".equals(kind)) + if (!isSuper && "compact_value".equals(kind)) continue; - if ("clustering_key".equals(kind)) - { - int position = columnRow.has("component_index") ? columnRow.getInt("component_index") : 0; - if (isSuper && position != 0) - continue; - - if (!isSuper && !isCompound) - continue; - } + if ("clustering_key".equals(kind) && !isSuper && !isCompound) + continue; filteredRows.add(columnRow); } @@ -569,14 +571,9 @@ public final class LegacySchemaMigrator // Should only be called on compact tables private static boolean checkNeedsUpgrade(Iterable<UntypedResultSet.Row> defs, boolean isSuper, boolean isStaticCompactTable) { + // For SuperColumn tables, re-create a compact value column if (isSuper) - { - // Check if we've added the "supercolumn map" column yet or not - for (UntypedResultSet.Row row : defs) - if (row.getString("column_name").isEmpty()) - return false; return true; - } // For static compact tables, we need to upgrade if the regular definitions haven't been converted to static yet, // i.e. if we don't have a static definition yet. @@ -626,7 +623,7 @@ public final class LegacySchemaMigrator if (isSuper) { - defs.add(ColumnDefinition.regularDef(ksName, cfName, CompactTables.SUPER_COLUMN_MAP_COLUMN_STR, MapType.getInstance(subComparator, defaultValidator, true))); + defs.add(ColumnDefinition.regularDef(ksName, cfName, SuperColumnCompatibility.SUPER_COLUMN_MAP_COLUMN_STR, MapType.getInstance(subComparator, defaultValidator, true))); } else if (isStaticCompactTable) { http://git-wip-us.apache.org/repos/asf/cassandra/blob/ce8c9b55/src/java/org/apache/cassandra/thrift/CassandraServer.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/thrift/CassandraServer.java b/src/java/org/apache/cassandra/thrift/CassandraServer.java index cb74b15..256f651 100644 --- a/src/java/org/apache/cassandra/thrift/CassandraServer.java +++ b/src/java/org/apache/cassandra/thrift/CassandraServer.java @@ -36,6 +36,7 @@ import org.slf4j.LoggerFactory; import org.apache.cassandra.auth.Permission; import org.apache.cassandra.config.*; import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.SuperColumnCompatibility; import org.apache.cassandra.cql3.statements.ParsedStatement; import org.apache.cassandra.db.*; import org.apache.cassandra.db.context.CounterContext; @@ -432,11 +433,15 @@ public class CassandraServer implements Cassandra.Iface ByteBuffer finish = range.reversed ? range.start : range.finish; builder.slice(def, start.hasRemaining() ? CellPath.create(start) : CellPath.BOTTOM, finish.hasRemaining() ? CellPath.create(finish) : CellPath.TOP); + if (metadata.isDense()) + return builder.build(); + // We also want to add any staticly defined column if it's within the range AbstractType<?> cmp = metadata.thriftColumnNameType(); + for (ColumnDefinition column : metadata.partitionColumns()) { - if (CompactTables.isSuperColumnMapColumn(column)) + if (SuperColumnCompatibility.isSuperColumnMapColumn(column)) continue; ByteBuffer name = column.name.bytes; http://git-wip-us.apache.org/repos/asf/cassandra/blob/ce8c9b55/src/java/org/apache/cassandra/thrift/ThriftConversion.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/thrift/ThriftConversion.java b/src/java/org/apache/cassandra/thrift/ThriftConversion.java index bccfd8a..e8256a8 100644 --- a/src/java/org/apache/cassandra/thrift/ThriftConversion.java +++ b/src/java/org/apache/cassandra/thrift/ThriftConversion.java @@ -18,7 +18,6 @@ package org.apache.cassandra.thrift; import java.util.*; -import java.util.regex.Matcher; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; @@ -27,6 +26,7 @@ import com.google.common.collect.Maps; import org.apache.cassandra.config.*; import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.cql3.Operator; +import org.apache.cassandra.cql3.SuperColumnCompatibility; import org.apache.cassandra.cql3.statements.IndexTarget; import org.apache.cassandra.db.CompactTables; import org.apache.cassandra.db.LegacyLayout; @@ -273,7 +273,8 @@ public class ThriftConversion hasKeyAlias ? null : keyValidator, rawComparator, subComparator, - defaultValidator); + defaultValidator, + isDense); } // We do not allow Thrift views, so we always set it to false @@ -368,7 +369,8 @@ public class ThriftConversion AbstractType<?> keyValidator, AbstractType<?> comparator, AbstractType<?> subComparator, - AbstractType<?> defaultValidator) + AbstractType<?> defaultValidator, + boolean isDense) { CompactTables.DefaultNames names = CompactTables.defaultNameGenerator(defs); if (keyValidator != null) @@ -389,7 +391,12 @@ public class ThriftConversion { // SuperColumn tables: we use a special map to hold dynamic values within a given super column defs.add(ColumnDefinition.clusteringDef(ks, cf, names.defaultClusteringName(), comparator, 0)); - defs.add(ColumnDefinition.regularDef(ks, cf, CompactTables.SUPER_COLUMN_MAP_COLUMN_STR, MapType.getInstance(subComparator, defaultValidator, true))); + defs.add(ColumnDefinition.regularDef(ks, cf, SuperColumnCompatibility.SUPER_COLUMN_MAP_COLUMN_STR, MapType.getInstance(subComparator, defaultValidator, true))); + if (isDense) + { + defs.add(ColumnDefinition.clusteringDef(ks, cf, names.defaultClusteringName(), subComparator, 1)); + defs.add(ColumnDefinition.regularDef(ks, cf, names.defaultCompactValueName(), defaultValidator)); + } } else { http://git-wip-us.apache.org/repos/asf/cassandra/blob/ce8c9b55/test/unit/org/apache/cassandra/SchemaLoader.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/SchemaLoader.java b/test/unit/org/apache/cassandra/SchemaLoader.java index 87e8e1e..1686973 100644 --- a/test/unit/org/apache/cassandra/SchemaLoader.java +++ b/test/unit/org/apache/cassandra/SchemaLoader.java @@ -387,25 +387,18 @@ public class SchemaLoader .compression(getCompressionParameters()); } - // TODO: Fix superCFMD failing on legacy table creation. Seems to be applying composite comparator to partition key public static CFMetaData superCFMD(String ksName, String cfName, AbstractType subcc) { return superCFMD(ksName, cfName, BytesType.instance, subcc); } + public static CFMetaData superCFMD(String ksName, String cfName, AbstractType cc, AbstractType subcc) { - return superCFMD(ksName, cfName, "cols", cc, subcc); - } - public static CFMetaData superCFMD(String ksName, String cfName, String ccName, AbstractType cc, AbstractType subcc) - { - //This is busted -// return CFMetaData.Builder.createSuper(ksName, cfName, false) -// .addPartitionKey("0", BytesType.instance) -// .addClusteringColumn("1", cc) -// .addClusteringColumn("2", subcc) -// .addRegularColumn("3", AsciiType.instance) -// .build(); - return standardCFMD(ksName, cfName); + return CFMetaData.Builder.createSuper(ksName, cfName, false) + .addPartitionKey("key", BytesType.instance) + .addClusteringColumn("column1", cc) + .addRegularColumn("", MapType.getInstance(AsciiType.instance, subcc, true)) + .build(); } public static CFMetaData compositeIndexCFMD(String ksName, String cfName, boolean withIndex) throws ConfigurationException http://git-wip-us.apache.org/repos/asf/cassandra/blob/ce8c9b55/test/unit/org/apache/cassandra/cql3/ViewTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/cql3/ViewTest.java b/test/unit/org/apache/cassandra/cql3/ViewTest.java index 1107a64..4a4fe1a 100644 --- a/test/unit/org/apache/cassandra/cql3/ViewTest.java +++ b/test/unit/org/apache/cassandra/cql3/ViewTest.java @@ -23,6 +23,7 @@ import static org.junit.Assert.*; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.UUID; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import com.google.common.util.concurrent.Uninterruptibles; @@ -36,6 +37,7 @@ import org.junit.Test; import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.Row; import com.datastax.driver.core.exceptions.InvalidQueryException; +import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.concurrent.SEPExecutor; import org.apache.cassandra.concurrent.Stage; import org.apache.cassandra.concurrent.StageManager; @@ -45,8 +47,12 @@ import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.db.SystemKeyspace; import org.apache.cassandra.db.compaction.CompactionManager; +import org.apache.cassandra.db.marshal.AsciiType; +import org.apache.cassandra.schema.KeyspaceParams; import org.apache.cassandra.utils.FBUtilities; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class ViewTest extends CQLTester { @@ -389,7 +395,30 @@ public class ViewTest extends CQLTester } @Test - public void testCreateMvWithTTL() throws Throwable + public void testSuperCoumn() throws Throwable + { + String keyspace = createKeyspaceName(); + String table = createTableName(); + SchemaLoader.createKeyspace(keyspace, + KeyspaceParams.simple(1), + SchemaLoader.superCFMD(keyspace, table, AsciiType.instance, AsciiType.instance)); + + execute("USE " + keyspace); + executeNet(protocolVersion, "USE " + keyspace); + + try + { + createView("mv_super_column", "CREATE MATERIALIZED VIEW %s AS SELECT * FROM " + keyspace + "." + table + " WHERE key IS NOT NULL AND column1 IS NOT NULL PRIMARY KEY (key,column1)"); + Assert.fail("MV on SuperColumn table should fail"); + } + catch (InvalidQueryException e) + { + assertEquals("Materialized views are not supported on SuperColumn tables", e.getMessage()); + } + } + + @Test + public void testDurationsTable() throws Throwable { createTable("CREATE TABLE %s (" + "k int PRIMARY KEY, " + --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org For additional commands, e-mail: commits-h...@cassandra.apache.org