From 7623a59b31102a127560b378cf6e93f745f49574 Mon Sep 17 00:00:00 2001 From: Benedict Elliott Smith Date: Fri, 21 Nov 2025 15:46:58 +0000 Subject: [PATCH] Accord: Deterministic time integration - now() functions must be deterministic (and derived from Accord timestamp) - tombstone GC and TTL evaluation must be deterministic and based on both Accord timestamp and Accord GC mechanisms (that guarantee completeness of execution) patch by Benedict; reviewed by Alex Petrov and Ariel Weisberg for CASSANDRA-21376 --- .../org/apache/cassandra/cql3/Attributes.java | 8 +- .../cassandra/cql3/ColumnsExpression.java | 4 +- .../cassandra/cql3/ElementExpression.java | 6 +- .../cassandra/cql3/FunctionContext.java | 96 ++++++++++++++++ src/java/org/apache/cassandra/cql3/Json.java | 6 +- .../org/apache/cassandra/cql3/Operation.java | 4 +- .../apache/cassandra/cql3/QueryOptions.java | 9 +- .../apache/cassandra/cql3/QueryProcessor.java | 2 +- ...eParameters.java => RowUpdateBuilder.java} | 56 ++++++---- .../cql3/conditions/ColumnCondition.java | 24 ++-- .../cql3/functions/AggregateFcts.java | 60 +++++----- .../cql3/functions/AggregateFunction.java | 6 +- .../cassandra/cql3/functions/Arguments.java | 3 + .../cql3/functions/BytesConversionFcts.java | 6 +- .../cassandra/cql3/functions/CastFcts.java | 6 +- .../cql3/functions/CollectionFcts.java | 14 +-- .../cassandra/cql3/functions/Function.java | 6 +- .../cql3/functions/FunctionArguments.java | 58 ++++------ .../cql3/functions/FunctionCall.java | 12 +- .../cassandra/cql3/functions/LengthFcts.java | 6 +- .../cql3/functions/NativeFunction.java | 7 +- .../cql3/functions/PartialScalarFunction.java | 2 +- .../PartiallyAppliedScalarFunction.java | 17 ++- .../functions/PreComputedScalarFunction.java | 7 +- .../cql3/functions/ScalarFunction.java | 3 +- .../cassandra/cql3/functions/TimeFcts.java | 2 +- .../cassandra/cql3/functions/ToJsonFct.java | 6 +- .../cassandra/cql3/functions/TokenFct.java | 6 +- .../cassandra/cql3/functions/UDAggregate.java | 10 +- .../cassandra/cql3/functions/UDFunction.java | 5 +- .../cassandra/cql3/functions/VectorFcts.java | 6 +- .../cql3/functions/masking/ColumnMask.java | 17 ++- .../masking/DefaultMaskingFunction.java | 6 +- .../masking/HashMaskingFunction.java | 6 +- .../masking/NullMaskingFunction.java | 6 +- .../masking/PartialMaskingFunction.java | 6 +- .../masking/ReplaceMaskingFunction.java | 6 +- .../ClusteringColumnRestrictions.java | 5 +- .../cql3/restrictions/MergedRestriction.java | 16 +-- .../cql3/restrictions/Restriction.java | 8 +- .../cql3/restrictions/RestrictionSet.java | 6 +- .../restrictions/RestrictionSetWrapper.java | 6 +- .../cql3/restrictions/SimpleRestriction.java | 52 ++++----- .../cql3/restrictions/SingleRestriction.java | 12 +- .../selection/AbstractFunctionSelector.java | 21 +++- .../selection/AggregateFunctionSelector.java | 17 ++- .../cql3/selection/ElementsSelector.java | 8 ++ .../cql3/selection/FieldSelector.java | 8 ++ .../cql3/selection/ListSelector.java | 9 ++ .../cassandra/cql3/selection/MapSelector.java | 13 ++- .../cql3/selection/ResultSetBuilder.java | 8 +- .../selection/ScalarFunctionSelector.java | 6 +- .../cassandra/cql3/selection/Selection.java | 10 ++ .../cassandra/cql3/selection/Selector.java | 3 + .../cassandra/cql3/selection/SetSelector.java | 9 ++ .../cql3/selection/SimpleSelector.java | 21 ++-- .../cql3/selection/TupleSelector.java | 9 ++ .../cql3/selection/UserTypeSelector.java | 9 ++ .../cql3/selection/VectorSelector.java | 9 ++ .../selection/WritetimeOrTTLSelector.java | 8 ++ .../cql3/statements/CQL3CasRequest.java | 38 ++++--- .../cql3/statements/DeleteStatement.java | 6 +- .../statements/ModificationStatement.java | 63 +++++------ .../cql3/statements/SelectStatement.java | 2 +- .../cql3/statements/TransactionStatement.java | 22 ++-- .../cql3/statements/UpdateStatement.java | 6 +- .../cassandra/cql3/terms/Constants.java | 64 +++++------ .../apache/cassandra/cql3/terms/InMarker.java | 14 +-- .../apache/cassandra/cql3/terms/Lists.java | 78 ++++++------- .../org/apache/cassandra/cql3/terms/Maps.java | 48 ++++---- .../apache/cassandra/cql3/terms/Marker.java | 20 ++-- .../cassandra/cql3/terms/MultiElements.java | 6 +- .../org/apache/cassandra/cql3/terms/Sets.java | 48 ++++---- .../org/apache/cassandra/cql3/terms/Term.java | 27 ++--- .../apache/cassandra/cql3/terms/Terms.java | 56 +++++----- .../apache/cassandra/cql3/terms/Tuples.java | 4 +- .../cassandra/cql3/terms/UserTypes.java | 30 ++--- .../apache/cassandra/cql3/terms/Vectors.java | 4 +- .../cql3/transactions/ReferenceValue.java | 12 +- .../cql3/transactions/RowDataReference.java | 12 +- .../cassandra/db/ColumnFamilyStore.java | 13 ++- .../org/apache/cassandra/db/ReadCommand.java | 3 + .../cassandra/db/aggregation/GroupMaker.java | 3 +- .../db/compaction/CompactionIterator.java | 56 +++++----- .../db/compaction/CompactionManager.java | 26 ++--- .../db/compaction/CompactionTask.java | 50 ++++++++- .../db/compaction/SSTableSplitter.java | 2 +- .../compaction/TimeWindowCompactionTask.java | 2 +- .../db/marshal/AbstractTimeUUIDType.java | 6 +- .../cassandra/db/marshal/TemporalType.java | 7 +- .../apache/cassandra/db/marshal/TimeType.java | 6 +- .../db/partitions/AbstractBTreePartition.java | 1 - .../repair/CassandraValidationIterator.java | 9 +- .../io/sstable/CQLSSTableWriter.java | 21 ++-- .../cassandra/repair/RepairSession.java | 1 + .../AccordExecutorAbstractLockLoop.java | 1 - .../service/accord/AccordObjectSizes.java | 5 +- .../service/accord/api/TokenKey.java | 5 + .../accord/journal/CommandChangeWriter.java | 4 +- .../accord/journal/CommandChanges.java | 9 +- .../accord/serializers/ApplySerializers.java | 3 +- .../serializers/CheckStatusSerializers.java | 3 +- .../serializers/ReadDataSerializer.java | 3 +- .../serializers/RecoverySerializers.java | 3 +- .../serializers/RemoteSuccessSerializers.java | 8 +- .../accord/serializers/ResultSerializers.java | 45 -------- .../accord/txn/AccordUpdateParameters.java | 16 ++- .../txn/RetryWithNewProtocolResult.java | 6 - .../cassandra/service/accord/txn/TxnData.java | 33 ++---- .../service/accord/txn/TxnDataResult.java | 103 ++++++++++++++++++ .../service/accord/txn/TxnQuery.java | 45 +++++--- .../accord/txn/TxnRangeReadResult.java | 27 +---- .../accord/txn/TxnReferenceOperation.java | 4 +- .../service/accord/txn/TxnResult.java | 9 +- .../accord/txn/TxnValidationRejection.java | 6 - .../service/accord/txn/TxnWrite.java | 6 +- .../cassandra/transport/ProtocolVersion.java | 10 +- .../org/apache/cassandra/utils/Clock.java | 17 ++- .../org/apache/cassandra/utils/TimeUUID.java | 2 +- .../utils/concurrent/SignalLock.java | 10 +- ...rdCommandStoreTryExecuteListeningTest.java | 4 +- .../AccordWriteInteroperabilityTest.java | 2 + .../journal/AccordJournalReplayTest.java | 4 +- .../accord/journal/AccordJournalBurnTest.java | 6 +- .../io/sstable/HarrySSTableWriter.java | 17 +-- .../cql3/functions/FunctionFactoryTest.java | 6 +- .../cql3/functions/TimeFctsTest.java | 4 +- .../cassandra/cql3/terms/ListsTest.java | 18 +-- .../miscellaneous/CrcCheckChanceTest.java | 2 +- .../validation/operations/CQLVectorTest.java | 10 +- .../db/compaction/CompactionsCQLTest.java | 2 +- .../cassandra/hints/HintsServiceTest.java | 4 +- .../accord/AccordCommandStoreTest.java | 4 +- .../CommandsForKeySerializerTest.java | 3 +- .../cassandra/utils/AccordGenerators.java | 4 +- .../io/sstable/StressCQLSSTableWriter.java | 19 ++-- 136 files changed, 1201 insertions(+), 840 deletions(-) create mode 100644 src/java/org/apache/cassandra/cql3/FunctionContext.java rename src/java/org/apache/cassandra/cql3/{UpdateParameters.java => RowUpdateBuilder.java} (91%) delete mode 100644 src/java/org/apache/cassandra/service/accord/serializers/ResultSerializers.java create mode 100644 src/java/org/apache/cassandra/service/accord/txn/TxnDataResult.java diff --git a/src/java/org/apache/cassandra/cql3/Attributes.java b/src/java/org/apache/cassandra/cql3/Attributes.java index 8ee7995790..26fd590ec2 100644 --- a/src/java/org/apache/cassandra/cql3/Attributes.java +++ b/src/java/org/apache/cassandra/cql3/Attributes.java @@ -82,12 +82,12 @@ public class Attributes return timeToLive != null; } - public long getTimestamp(long now, QueryOptions options) throws InvalidRequestException + public long getTimestamp(long now, FunctionContext context) throws InvalidRequestException { if (timestamp == null) return now; - ByteBuffer tval = timestamp.bindAndGet(options); + ByteBuffer tval = timestamp.bindAndGet(context); if (tval == null) throw new InvalidRequestException("Invalid null value of timestamp"); @@ -106,7 +106,7 @@ public class Attributes return LongType.instance.compose(tval); } - public int getTimeToLive(QueryOptions options, TableMetadata metadata) throws InvalidRequestException + public int getTimeToLive(FunctionContext context, TableMetadata metadata) throws InvalidRequestException { if (timeToLive == null) { @@ -114,7 +114,7 @@ public class Attributes return metadata.params.defaultTimeToLive; } - ByteBuffer tval = timeToLive.bindAndGet(options); + ByteBuffer tval = timeToLive.bindAndGet(context); if (tval == null) return 0; diff --git a/src/java/org/apache/cassandra/cql3/ColumnsExpression.java b/src/java/org/apache/cassandra/cql3/ColumnsExpression.java index 0db17261bf..c82834f71a 100644 --- a/src/java/org/apache/cassandra/cql3/ColumnsExpression.java +++ b/src/java/org/apache/cassandra/cql3/ColumnsExpression.java @@ -356,9 +356,9 @@ public final class ColumnsExpression * Returns the key, index or fieldname specifying the selected element. * @return the key, index or fieldname specifying the selected element. */ - public ByteBuffer element(QueryOptions options) + public ByteBuffer element(FunctionContext context) { - return element.bindAndGet(options); + return element.bindAndGet(context); } /** diff --git a/src/java/org/apache/cassandra/cql3/ElementExpression.java b/src/java/org/apache/cassandra/cql3/ElementExpression.java index 5d89c74427..87ddf37e90 100644 --- a/src/java/org/apache/cassandra/cql3/ElementExpression.java +++ b/src/java/org/apache/cassandra/cql3/ElementExpression.java @@ -158,12 +158,12 @@ public final class ElementExpression /** * Returns the ByteBuffer representation of the key or index. * - * @param options the query options + * @param context the query options * @return the ByteBuffer representation of the key or index. */ - public ByteBuffer bindAndGet(QueryOptions options) + public ByteBuffer bindAndGet(FunctionContext context) { - return keyOrIndex.bindAndGet(options); + return keyOrIndex.bindAndGet(context); } public String toCQLString() diff --git a/src/java/org/apache/cassandra/cql3/FunctionContext.java b/src/java/org/apache/cassandra/cql3/FunctionContext.java new file mode 100644 index 0000000000..c6e2f47a7b --- /dev/null +++ b/src/java/org/apache/cassandra/cql3/FunctionContext.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.cassandra.cql3; + +import java.time.Instant; + +import org.apache.cassandra.cql3.functions.Arguments; +import org.apache.cassandra.cql3.functions.FunctionArguments; +import org.apache.cassandra.db.marshal.TimeUUIDType; +import org.apache.cassandra.transport.ProtocolVersion; +import org.apache.cassandra.utils.Clock.Global; +import org.apache.cassandra.utils.TimeUUID; + +public interface FunctionContext +{ + NoTimeOrQueryFunctionContext NONE = new NoTimeOrQueryFunctionContext() {}; + + interface RealTimeFunctionContext extends FunctionContext + { + @Override default byte[] nextTimeUUIDAsBytes() { return TimeUUID.Generator.nextTimeUUIDAsBytes(); } + @Override default Instant now() { return Global.currentTime(); } + @Override default long nowMicros() { return Global.currentTimeMicros(); } + @Override default long nowMillis() { return Global.currentTimeMillis(); } + } + + interface NoTimeFunctionContext extends FunctionContext + { + @Override default byte[] nextTimeUUIDAsBytes() { throw new UnsupportedOperationException(); } + @Override default Instant now() { throw new UnsupportedOperationException(); } + @Override default long nowMicros() { throw new UnsupportedOperationException(); } + @Override default long nowMillis() { throw new UnsupportedOperationException(); } + } + + interface NoTimeOrQueryFunctionContext extends NoTimeFunctionContext + { + @Override default QueryOptions options() { throw new UnsupportedOperationException(); } + } + + interface PartialFunctionContext extends FunctionContext + { + default long nowMillis() { return nowMicros() / 1000; } + default Instant now() + { + long nowMicros = nowMicros(); + return Instant.ofEpochSecond(nowMicros / 1000_000, (nowMicros % 1000_000) * 1000); + } + } + + abstract class MicrosFunctionContext implements PartialFunctionContext + { + final long atMicros; + private long timeUuidNanos; + + public MicrosFunctionContext(long atMicros) + { + this.atMicros = atMicros; + } + + @Override public long nowMicros() { return atMicros; } + + @Override + public byte[] nextTimeUUIDAsBytes() + { + return TimeUUID.toBytes(TimeUUID.unixMicrosToMsb(atMicros), TimeUUIDType.signedBytesToNativeLong(timeUuidNanos++)); + } + } + + QueryOptions options(); + Instant now(); + long nowMillis(); + long nowMicros(); + byte[] nextTimeUUIDAsBytes(); + + default ProtocolVersion getProtocolVersion() { return options().getProtocolVersion(); } + + default Arguments noArguments() + { + return new FunctionArguments(this); + } +} diff --git a/src/java/org/apache/cassandra/cql3/Json.java b/src/java/org/apache/cassandra/cql3/Json.java index be044f0a1a..67409f0509 100644 --- a/src/java/org/apache/cassandra/cql3/Json.java +++ b/src/java/org/apache/cassandra/cql3/Json.java @@ -251,12 +251,12 @@ public final class Json } @Override - public Terminal bind(QueryOptions options) throws InvalidRequestException + public Terminal bind(FunctionContext context) throws InvalidRequestException { - Term term = options.getJsonColumnValue(marker.bindIndex, column.name, marker.columns); + Term term = context.options().getJsonColumnValue(marker.bindIndex, column.name, marker.columns); return term == null ? (defaultUnset ? Constants.UNSET_VALUE : null) - : term.bind(options); + : term.bind(context); } @Override diff --git a/src/java/org/apache/cassandra/cql3/Operation.java b/src/java/org/apache/cassandra/cql3/Operation.java index 3d8555849b..cca7bb0d2b 100644 --- a/src/java/org/apache/cassandra/cql3/Operation.java +++ b/src/java/org/apache/cassandra/cql3/Operation.java @@ -116,9 +116,9 @@ public abstract class Operation * Execute the operation. * * @param partitionKey partition key for the update. - * @param params parameters of the update. + * @param builder parameters of the update. */ - public abstract void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException; + public abstract void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException; /** * A parsed raw UPDATE operation. diff --git a/src/java/org/apache/cassandra/cql3/QueryOptions.java b/src/java/org/apache/cassandra/cql3/QueryOptions.java index 3244497914..afc5bacc38 100644 --- a/src/java/org/apache/cassandra/cql3/QueryOptions.java +++ b/src/java/org/apache/cassandra/cql3/QueryOptions.java @@ -31,6 +31,7 @@ import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.cassandra.config.DataStorageSpec; import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.cql3.FunctionContext.RealTimeFunctionContext; import org.apache.cassandra.cql3.terms.Term; import org.apache.cassandra.db.ConsistencyLevel; import org.apache.cassandra.db.marshal.UTF8Type; @@ -53,7 +54,7 @@ import static org.apache.cassandra.utils.ByteArrayUtil.convertToByteBufferValue; /** * Options for a query. */ -public abstract class QueryOptions +public abstract class QueryOptions implements RealTimeFunctionContext { public static final QueryOptions DEFAULT = new DefaultQueryOptions(ConsistencyLevel.ONE, Collections.emptyList(), @@ -378,6 +379,12 @@ public abstract class QueryOptions } } + @Override + public QueryOptions options() + { + return this; + } + static class DefaultQueryOptions extends QueryOptions { private final ConsistencyLevel consistency; diff --git a/src/java/org/apache/cassandra/cql3/QueryProcessor.java b/src/java/org/apache/cassandra/cql3/QueryProcessor.java index 606da47305..cf27f5e9ac 100644 --- a/src/java/org/apache/cassandra/cql3/QueryProcessor.java +++ b/src/java/org/apache/cassandra/cql3/QueryProcessor.java @@ -555,7 +555,7 @@ public class QueryProcessor implements QueryHandler .map(m -> MessagingService.instance().sendWithResult(m, address)) .collect(Collectors.toList())); - ResultSetBuilder result = new ResultSetBuilder(select.getResultMetadata(), select.getSelection().newSelectors(options), false); + ResultSetBuilder result = new ResultSetBuilder(select.getResultMetadata(), options, select.getSelection().newSelectors(options), false); return future.map(list -> { int i = 0; for (Message m : list) diff --git a/src/java/org/apache/cassandra/cql3/UpdateParameters.java b/src/java/org/apache/cassandra/cql3/RowUpdateBuilder.java similarity index 91% rename from src/java/org/apache/cassandra/cql3/UpdateParameters.java rename to src/java/org/apache/cassandra/cql3/RowUpdateBuilder.java index f7a5cf1e37..8881dc5bd8 100644 --- a/src/java/org/apache/cassandra/cql3/UpdateParameters.java +++ b/src/java/org/apache/cassandra/cql3/RowUpdateBuilder.java @@ -15,6 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.apache.cassandra.cql3; import java.nio.ByteBuffer; @@ -43,20 +44,32 @@ import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.schema.ColumnMetadata; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.service.ClientState; -import org.apache.cassandra.utils.TimeUUID; -/** - * Groups the parameters of an update query, and make building updates easier. - */ -public class UpdateParameters +public abstract class RowUpdateBuilder implements FunctionContext { - public final TableMetadata metadata; - public final ClientState clientState; - public final QueryOptions options; + public static class RegularRowUpdateBuilder extends RowUpdateBuilder implements RealTimeFunctionContext + { + public RegularRowUpdateBuilder(TableMetadata metadata, ClientState clientState, QueryOptions options, long timestamp, long nowInSec, int ttl, Map prefetchedRows) throws InvalidRequestException + { + super(metadata, clientState, options, timestamp, nowInSec, ttl, prefetchedRows); + } + } - private final long nowInSec; - protected final long timestamp; - private final int ttl; + public static class NoTimeRowUpdateBuilder extends RowUpdateBuilder implements NoTimeFunctionContext + { + public NoTimeRowUpdateBuilder(TableMetadata metadata, ClientState clientState, QueryOptions options, long timestamp, long nowInSec, int ttl, Map prefetchedRows) throws InvalidRequestException + { + super(metadata, clientState, options, timestamp, nowInSec, ttl, prefetchedRows); + } + } + + public final TableMetadata metadata; + private final QueryOptions options; + public final ClientState clientState; + + public final long nowInSec; + public final long timestamp; + public final int ttl; private DeletionTime deletionTime; @@ -66,7 +79,7 @@ public class UpdateParameters // The builder currently in use. Will alias either staticBuilder or regularBuilder, which are themselves built lazily. private Row.Builder builder; - public UpdateParameters(TableMetadata metadata, + protected RowUpdateBuilder(TableMetadata metadata, ClientState clientState, QueryOptions options, long timestamp, @@ -75,21 +88,26 @@ public class UpdateParameters Map prefetchedRows) throws InvalidRequestException { this.metadata = metadata; - this.clientState = clientState; this.options = options; - + this.clientState = clientState; this.nowInSec = nowInSec; this.timestamp = timestamp; this.ttl = ttl; + this.deletionTime = DeletionTime.build(timestamp, nowInSec); this.prefetchedRows = prefetchedRows; - // We use MIN_VALUE internally to mean the absence of of timestamp (in Selection, in sstable stats, ...), so exclude // it to avoid potential confusion. if (timestamp == Long.MIN_VALUE) throw new InvalidRequestException(String.format("Out of bound timestamp, must be in [%d, %d]", Long.MIN_VALUE + 1, Long.MAX_VALUE)); } + @Override + public QueryOptions options() + { + return options; + } + public void newRow(Clustering clustering) throws InvalidRequestException { if (metadata.isCompactTable()) @@ -208,7 +226,7 @@ public class UpdateParameters newRow(row.clustering()); addRowDeletion(row.deletion()); addPrimaryKeyLivenessInfo(row.primaryKeyLivenessInfo()); - row.iterator().forEachRemaining(cd -> { + row.forEach(cd -> { if (cd instanceof Cell) { builder.addCell((Cell) cd); @@ -297,11 +315,6 @@ public class UpdateParameters return new RangeTombstone(slice, deletionTime()); } - public byte[] nextTimeUUIDAsBytes() - { - return TimeUUID.Generator.nextTimeUUIDAsBytes(); - } - /** * Returns the prefetched row with the already performed modifications. *

If no modification have yet been performed this method will return the fetched row or {@code null} if @@ -332,4 +345,5 @@ public class UpdateParameters return Rows.merge(prefetchedRow, pendingMutations) .purge(DeletionPurger.PURGE_ALL, nowInSec, metadata.enforceStrictLiveness()); } + } diff --git a/src/java/org/apache/cassandra/cql3/conditions/ColumnCondition.java b/src/java/org/apache/cassandra/cql3/conditions/ColumnCondition.java index 892cdbaa91..cbe2607cfc 100644 --- a/src/java/org/apache/cassandra/cql3/conditions/ColumnCondition.java +++ b/src/java/org/apache/cassandra/cql3/conditions/ColumnCondition.java @@ -29,8 +29,8 @@ import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.cql3.ColumnSpecification; import org.apache.cassandra.cql3.ColumnsExpression; import org.apache.cassandra.cql3.FieldIdentifier; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.Operator; -import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.VariableSpecifications; import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.cql3.terms.Term; @@ -110,39 +110,39 @@ public final class ColumnCondition values.collectMarkerSpecification(boundNames, owner); } - public ColumnCondition.Bound bind(QueryOptions options) + public ColumnCondition.Bound bind(FunctionContext context) { switch (columnsExpression.kind()) { case SINGLE_COLUMN: - return bindSingleColumn(options); + return bindSingleColumn(context); case ELEMENT: - return bindElement(options); + return bindElement(context); default: throw new UnsupportedOperationException(); } } - private Bound bindSingleColumn(QueryOptions options) + private Bound bindSingleColumn(FunctionContext context) { ColumnMetadata column = columnsExpression.firstColumn(); TableMetadata table = columnsExpression.table(); if (column.type.isMultiCell()) - return new MultiCellBound(column, table, operator, toValue(column.type, bindAndGetTerms(options))); + return new MultiCellBound(column, table, operator, toValue(column.type, bindAndGetTerms(context))); - return new SimpleBound(column, table, operator, toValue(column.type, bindAndGetTerms(options))); + return new SimpleBound(column, table, operator, toValue(column.type, bindAndGetTerms(context))); } - private ColumnCondition.Bound bindElement(QueryOptions options) + private ColumnCondition.Bound bindElement(FunctionContext context) { ColumnMetadata column = columnsExpression.firstColumn(); TableMetadata table = columnsExpression.table(); - ByteBuffer keyOrIndex = columnsExpression.element(options); + ByteBuffer keyOrIndex = columnsExpression.element(context); if (column.type.isCollection()) { checkNotNull(keyOrIndex, "Invalid null value for %s element access", column.type instanceof MapType ? "map" : "list"); } - return new ElementOrFieldAccessBound(column, table, keyOrIndex, operator, toValue(columnsExpression.type(), bindAndGetTerms(options))); + return new ElementOrFieldAccessBound(column, table, keyOrIndex, operator, toValue(columnsExpression.type(), bindAndGetTerms(context))); } private ByteBuffer toValue(AbstractType type, List values) @@ -157,9 +157,9 @@ public final class ColumnCondition return value; } - private List bindAndGetTerms(QueryOptions options) + private List bindAndGetTerms(FunctionContext context) { - List buffers = values.bindAndGet(options); + List buffers = values.bindAndGet(context); checkFalse(buffers == null && operator.isIN(), "Invalid null list in IN condition"); checkFalse(buffers == Term.UNSET_LIST, "Invalid 'unset' value in condition"); return filterUnsetValuesIfNeeded(buffers, ByteBufferUtil.UNSET_BYTE_BUFFER); diff --git a/src/java/org/apache/cassandra/cql3/functions/AggregateFcts.java b/src/java/org/apache/cassandra/cql3/functions/AggregateFcts.java index 4cc19c0824..b32e268269 100644 --- a/src/java/org/apache/cassandra/cql3/functions/AggregateFcts.java +++ b/src/java/org/apache/cassandra/cql3/functions/AggregateFcts.java @@ -23,6 +23,7 @@ import java.math.RoundingMode; import java.nio.ByteBuffer; import java.util.List; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.ByteType; import org.apache.cassandra.db.marshal.BytesType; @@ -35,7 +36,6 @@ import org.apache.cassandra.db.marshal.IntegerType; import org.apache.cassandra.db.marshal.LongType; import org.apache.cassandra.db.marshal.ShortType; import org.apache.cassandra.exceptions.InvalidRequestException; -import org.apache.cassandra.transport.ProtocolVersion; /** * Factory methods for aggregate functions. @@ -119,7 +119,7 @@ public abstract class AggregateFcts count = 0; } - public ByteBuffer compute(ProtocolVersion protocolVersion) + public ByteBuffer compute(FunctionContext context) { return LongType.instance.decompose(count); } @@ -163,7 +163,7 @@ public abstract class AggregateFcts sum = BigDecimal.ZERO; } - public ByteBuffer compute(ProtocolVersion protocolVersion) + public ByteBuffer compute(FunctionContext context) { return ((DecimalType) returnType()).decompose(sum); } @@ -204,7 +204,7 @@ public abstract class AggregateFcts avg = BigDecimal.ZERO; } - public ByteBuffer compute(ProtocolVersion protocolVersion) + public ByteBuffer compute(FunctionContext context) { return DecimalType.instance.decompose(avg); } @@ -244,7 +244,7 @@ public abstract class AggregateFcts sum = BigInteger.ZERO; } - public ByteBuffer compute(ProtocolVersion protocolVersion) + public ByteBuffer compute(FunctionContext context) { return ((IntegerType) returnType()).decompose(sum); } @@ -286,7 +286,7 @@ public abstract class AggregateFcts sum = BigInteger.ZERO; } - public ByteBuffer compute(ProtocolVersion protocolVersion) + public ByteBuffer compute(FunctionContext context) { if (count == 0) return IntegerType.instance.decompose(BigInteger.ZERO); @@ -329,7 +329,7 @@ public abstract class AggregateFcts sum = 0; } - public ByteBuffer compute(ProtocolVersion protocolVersion) + public ByteBuffer compute(FunctionContext context) { return ((ByteType) returnType()).decompose(sum); } @@ -361,7 +361,7 @@ public abstract class AggregateFcts { return new AvgAggregate() { - public ByteBuffer compute(ProtocolVersion protocolVersion) throws InvalidRequestException + public ByteBuffer compute(FunctionContext context) throws InvalidRequestException { return ByteType.instance.decompose((byte) computeInternal()); } @@ -389,7 +389,7 @@ public abstract class AggregateFcts sum = 0; } - public ByteBuffer compute(ProtocolVersion protocolVersion) + public ByteBuffer compute(FunctionContext context) { return ((ShortType) returnType()).decompose(sum); } @@ -421,7 +421,7 @@ public abstract class AggregateFcts { return new AvgAggregate() { - public ByteBuffer compute(ProtocolVersion protocolVersion) + public ByteBuffer compute(FunctionContext context) { return ShortType.instance.decompose((short) computeInternal()); } @@ -449,7 +449,7 @@ public abstract class AggregateFcts sum = 0; } - public ByteBuffer compute(ProtocolVersion protocolVersion) + public ByteBuffer compute(FunctionContext context) { return ((Int32Type) returnType()).decompose(sum); } @@ -481,7 +481,7 @@ public abstract class AggregateFcts { return new AvgAggregate() { - public ByteBuffer compute(ProtocolVersion protocolVersion) + public ByteBuffer compute(FunctionContext context) { return Int32Type.instance.decompose((int) computeInternal()); } @@ -517,7 +517,7 @@ public abstract class AggregateFcts { return new AvgAggregate() { - public ByteBuffer compute(ProtocolVersion protocolVersion) + public ByteBuffer compute(FunctionContext context) { return LongType.instance.decompose(computeInternal()); } @@ -538,7 +538,7 @@ public abstract class AggregateFcts { return new FloatSumAggregate() { - public ByteBuffer compute(ProtocolVersion protocolVersion) throws InvalidRequestException + public ByteBuffer compute(FunctionContext context) throws InvalidRequestException { return FloatType.instance.decompose((float) computeInternal()); } @@ -558,7 +558,7 @@ public abstract class AggregateFcts { return new FloatAvgAggregate() { - public ByteBuffer compute(ProtocolVersion protocolVersion) throws InvalidRequestException + public ByteBuffer compute(FunctionContext context) throws InvalidRequestException { return FloatType.instance.decompose((float) computeInternal()); } @@ -579,7 +579,7 @@ public abstract class AggregateFcts { return new FloatSumAggregate() { - public ByteBuffer compute(ProtocolVersion protocolVersion) throws InvalidRequestException + public ByteBuffer compute(FunctionContext context) throws InvalidRequestException { return DoubleType.instance.decompose(computeInternal()); } @@ -730,7 +730,7 @@ public abstract class AggregateFcts { return new FloatAvgAggregate() { - public ByteBuffer compute(ProtocolVersion protocolVersion) throws InvalidRequestException + public ByteBuffer compute(FunctionContext context) throws InvalidRequestException { return DoubleType.instance.decompose(computeInternal()); } @@ -760,7 +760,7 @@ public abstract class AggregateFcts { return new AvgAggregate() { - public ByteBuffer compute(ProtocolVersion protocolVersion) throws InvalidRequestException + public ByteBuffer compute(FunctionContext context) throws InvalidRequestException { return CounterColumnType.instance.decompose(computeInternal()); } @@ -785,7 +785,7 @@ public abstract class AggregateFcts min = null; } - public ByteBuffer compute(ProtocolVersion protocolVersion) + public ByteBuffer compute(FunctionContext context) { return min != null ? LongType.instance.decompose(min) : null; } @@ -824,7 +824,7 @@ public abstract class AggregateFcts max = null; } - public ByteBuffer compute(ProtocolVersion protocolVersion) + public ByteBuffer compute(FunctionContext context) { return max != null ? LongType.instance.decompose(max) : null; } @@ -857,9 +857,9 @@ public abstract class AggregateFcts return new NativeAggregateFunction("max", inputType, inputType) { @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return FunctionArguments.newNoopInstance(version, 1); + return FunctionArguments.newNoopInstance(context, 1); } @Override @@ -874,7 +874,7 @@ public abstract class AggregateFcts max = null; } - public ByteBuffer compute(ProtocolVersion protocolVersion) + public ByteBuffer compute(FunctionContext context) { return max; } @@ -906,9 +906,9 @@ public abstract class AggregateFcts return new NativeAggregateFunction("min", inputType, inputType) { @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return FunctionArguments.newNoopInstance(version, 1); + return FunctionArguments.newNoopInstance(context, 1); } @Override @@ -923,7 +923,7 @@ public abstract class AggregateFcts min = null; } - public ByteBuffer compute(ProtocolVersion protocolVersion) + public ByteBuffer compute(FunctionContext context) { return min; } @@ -955,9 +955,9 @@ public abstract class AggregateFcts return new NativeAggregateFunction("count", LongType.instance, inputType) { @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return FunctionArguments.newNoopInstance(version, 1); + return FunctionArguments.newNoopInstance(context, 1); } @Override @@ -972,7 +972,7 @@ public abstract class AggregateFcts count = 0; } - public ByteBuffer compute(ProtocolVersion protocolVersion) + public ByteBuffer compute(FunctionContext context) { return ((LongType) returnType()).decompose(count); } @@ -999,7 +999,7 @@ public abstract class AggregateFcts sum = 0; } - public ByteBuffer compute(ProtocolVersion protocolVersion) + public ByteBuffer compute(FunctionContext context) { return LongType.instance.decompose(sum); } diff --git a/src/java/org/apache/cassandra/cql3/functions/AggregateFunction.java b/src/java/org/apache/cassandra/cql3/functions/AggregateFunction.java index 57a966f0a8..9ae5cc5efe 100644 --- a/src/java/org/apache/cassandra/cql3/functions/AggregateFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/AggregateFunction.java @@ -19,8 +19,8 @@ package org.apache.cassandra.cql3.functions; import java.nio.ByteBuffer; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.exceptions.InvalidRequestException; -import org.apache.cassandra.transport.ProtocolVersion; /** * Performs a calculation on a set of values and return a single value. @@ -49,10 +49,10 @@ public interface AggregateFunction extends Function /** * Computes and returns the aggregate current value. * - * @param protocolVersion native protocol version + * @param context * @return the aggregate current value. */ - public ByteBuffer compute(ProtocolVersion protocolVersion) throws InvalidRequestException; + public ByteBuffer compute(FunctionContext context) throws InvalidRequestException; /** * Reset this aggregate. diff --git a/src/java/org/apache/cassandra/cql3/functions/Arguments.java b/src/java/org/apache/cassandra/cql3/functions/Arguments.java index 18cace3648..af4b4f4fb8 100644 --- a/src/java/org/apache/cassandra/cql3/functions/Arguments.java +++ b/src/java/org/apache/cassandra/cql3/functions/Arguments.java @@ -21,6 +21,7 @@ package org.apache.cassandra.cql3.functions; import java.nio.ByteBuffer; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.transport.ProtocolVersion; /** @@ -29,6 +30,8 @@ import org.apache.cassandra.transport.ProtocolVersion; */ public interface Arguments { + FunctionContext context(); + /** * Sets the specified value to the arguments * diff --git a/src/java/org/apache/cassandra/cql3/functions/BytesConversionFcts.java b/src/java/org/apache/cassandra/cql3/functions/BytesConversionFcts.java index 6c032dbc91..9f7e5154b7 100644 --- a/src/java/org/apache/cassandra/cql3/functions/BytesConversionFcts.java +++ b/src/java/org/apache/cassandra/cql3/functions/BytesConversionFcts.java @@ -20,11 +20,11 @@ package org.apache.cassandra.cql3.functions; import java.nio.ByteBuffer; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.BytesType; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.serializers.MarshalException; -import org.apache.cassandra.transport.ProtocolVersion; import org.apache.cassandra.utils.ByteBufferUtil; public abstract class BytesConversionFcts @@ -49,9 +49,9 @@ public abstract class BytesConversionFcts } @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return FunctionArguments.newNoopInstance(version, 1); + return FunctionArguments.newNoopInstance(context, 1); } } diff --git a/src/java/org/apache/cassandra/cql3/functions/CastFcts.java b/src/java/org/apache/cassandra/cql3/functions/CastFcts.java index 4f4936c66d..bf818a7482 100644 --- a/src/java/org/apache/cassandra/cql3/functions/CastFcts.java +++ b/src/java/org/apache/cassandra/cql3/functions/CastFcts.java @@ -25,6 +25,7 @@ import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.AsciiType; import org.apache.cassandra.db.marshal.BooleanType; @@ -44,7 +45,6 @@ import org.apache.cassandra.db.marshal.TimeUUIDType; import org.apache.cassandra.db.marshal.TimestampType; import org.apache.cassandra.db.marshal.UTF8Type; import org.apache.cassandra.db.marshal.UUIDType; -import org.apache.cassandra.transport.ProtocolVersion; import static org.apache.cassandra.cql3.functions.TimeFcts.toDate; import static org.apache.cassandra.cql3.functions.TimeFcts.toTimestamp; @@ -388,9 +388,9 @@ public final class CastFcts } @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return new FunctionArguments(version, (protocolVersion, buffer) -> { + return new FunctionArguments(context, (protocolVersion, buffer) -> { AbstractType argType = argTypes.get(0); if (buffer == null || (!buffer.hasRemaining() && argType.isEmptyValueMeaningless())) return null; diff --git a/src/java/org/apache/cassandra/cql3/functions/CollectionFcts.java b/src/java/org/apache/cassandra/cql3/functions/CollectionFcts.java index a3bc4726f9..9597ee5dac 100644 --- a/src/java/org/apache/cassandra/cql3/functions/CollectionFcts.java +++ b/src/java/org/apache/cassandra/cql3/functions/CollectionFcts.java @@ -26,13 +26,13 @@ import java.util.Set; import com.google.common.collect.ImmutableList; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.CollectionType; import org.apache.cassandra.db.marshal.Int32Type; import org.apache.cassandra.db.marshal.ListType; import org.apache.cassandra.db.marshal.MapType; import org.apache.cassandra.db.marshal.SetType; -import org.apache.cassandra.transport.ProtocolVersion; /** * Native CQL functions for collections (sets, list and maps). @@ -179,9 +179,9 @@ public class CollectionFcts return new NativeScalarFunction(name, Int32Type.instance, inputType) { @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return FunctionArguments.newNoopInstance(version, 1); + return FunctionArguments.newNoopInstance(context, 1); } @Override @@ -359,9 +359,9 @@ public class CollectionFcts } @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return FunctionArguments.newNoopInstance(version, 1); + return FunctionArguments.newNoopInstance(context, 1); } @Override @@ -370,7 +370,7 @@ public class CollectionFcts if (arguments.containsNulls()) return null; - Arguments args = aggregateFunction.newArguments(arguments.getProtocolVersion()); + Arguments args = aggregateFunction.newArguments(arguments.context()); AggregateFunction.Aggregate aggregate = aggregateFunction.newAggregate(); inputType.forEach(arguments.get(0), element -> { @@ -378,7 +378,7 @@ public class CollectionFcts aggregate.addInput(args); }); - return aggregate.compute(arguments.getProtocolVersion()); + return aggregate.compute(arguments.context()); } } } diff --git a/src/java/org/apache/cassandra/cql3/functions/Function.java b/src/java/org/apache/cassandra/cql3/functions/Function.java index 70fd4b48bf..611d95e1b2 100644 --- a/src/java/org/apache/cassandra/cql3/functions/Function.java +++ b/src/java/org/apache/cassandra/cql3/functions/Function.java @@ -24,9 +24,9 @@ import java.util.Optional; import org.github.jamm.Unmetered; import org.apache.cassandra.cql3.AssignmentTestable; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.schema.Difference; -import org.apache.cassandra.transport.ProtocolVersion; @Unmetered public interface Function extends AssignmentTestable @@ -77,10 +77,10 @@ public interface Function extends AssignmentTestable /** * Creates some new input arguments for this function. * - * @param version the protocol version + * @param context * @return some new input arguments for this function */ - Arguments newArguments(ProtocolVersion version); + Arguments newArguments(FunctionContext context); public default Optional compare(Function other) { diff --git a/src/java/org/apache/cassandra/cql3/functions/FunctionArguments.java b/src/java/org/apache/cassandra/cql3/functions/FunctionArguments.java index 102fa5e7cc..8e2c7b91ee 100644 --- a/src/java/org/apache/cassandra/cql3/functions/FunctionArguments.java +++ b/src/java/org/apache/cassandra/cql3/functions/FunctionArguments.java @@ -22,6 +22,7 @@ import java.nio.ByteBuffer; import java.util.Arrays; import java.util.List; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.transport.ProtocolVersion; @@ -30,20 +31,14 @@ import org.apache.cassandra.transport.ProtocolVersion; */ public final class FunctionArguments implements Arguments { - /** - * An empty {@link FunctionArguments} for the current protocol. - */ - private static final FunctionArguments EMPTY = new FunctionArguments(ProtocolVersion.CURRENT); + public static final ArgumentDeserializer[] NO_DESERIALIZERS = new ArgumentDeserializer[0]; /** * The deserializer used to deserialize the columns. */ private final ArgumentDeserializer[] deserializers; - /** - * The protocol version. - */ - private final ProtocolVersion version; + private final FunctionContext context; /** * The deserialized arguments. @@ -53,93 +48,82 @@ public final class FunctionArguments implements Arguments /** * Creates a new {@link FunctionArguments} for the specified types. * - * @param version the protocol version * @param argTypes the argument types * @return a new {@link FunctionArguments} for the specified types. */ - public static FunctionArguments newInstanceForUdf(ProtocolVersion version, List argTypes) + public static Arguments newInstanceForUdf(FunctionContext context, List argTypes) { int size = argTypes.size(); if (size == 0) - return emptyInstance(version); + return context.noArguments(); ArgumentDeserializer[] deserializers = new ArgumentDeserializer[size]; for (int i = 0; i < size; i++) deserializers[i] = argTypes.get(i).getArgumentDeserializer(); - return new FunctionArguments(version, deserializers); + return new FunctionArguments(context, deserializers); } @Override public ProtocolVersion getProtocolVersion() { - return version; + return context.getProtocolVersion(); + } + + @Override + public FunctionContext context() + { + return context; } /** * Creates a new {@link FunctionArguments} that does not deserialize the arguments. * - * @param version the protocol version * @param numberOfArguments the number of argument * @return a new {@link FunctionArguments} for the specified types. */ - public static FunctionArguments newNoopInstance(ProtocolVersion version, int numberOfArguments) + public static FunctionArguments newNoopInstance(FunctionContext context, int numberOfArguments) { ArgumentDeserializer[] deserializers = new ArgumentDeserializer[numberOfArguments]; Arrays.fill(deserializers, ArgumentDeserializer.NOOP_DESERIALIZER); - return new FunctionArguments(version, deserializers); - } - - /** - * Creates an empty {@link FunctionArguments}. - * - * @param version the protocol version - * @return an empty {@link FunctionArguments} - */ - public static FunctionArguments emptyInstance(ProtocolVersion version) - { - if (version == ProtocolVersion.CURRENT) - return EMPTY; - - return new FunctionArguments(version); + return new FunctionArguments(context, deserializers); } /** * Creates a new {@link FunctionArguments} for a native function. *

Native functions can use different {@link ArgumentDeserializer} to avoid instanciating primitive wrappers.

* - * @param version the protocol version * @param argTypes the argument types * @return a new {@link FunctionArguments} for the specified types. */ - public static FunctionArguments newInstanceForNativeFunction(ProtocolVersion version, List> argTypes) + public static Arguments newInstanceForNativeFunction(FunctionContext context, List> argTypes) { int size = argTypes.size(); if (size == 0) - return emptyInstance(version); + return context.noArguments(); ArgumentDeserializer[] deserializers = new ArgumentDeserializer[size]; for (int i = 0; i < size; i++) deserializers[i] = argTypes.get(i).getArgumentDeserializer(); - return new FunctionArguments(version, deserializers); + return new FunctionArguments(context, deserializers); } - public FunctionArguments(ProtocolVersion version, ArgumentDeserializer... deserializers) + public FunctionArguments(FunctionContext context, ArgumentDeserializer... deserializers) { - this.version = version; + this.context = context; this.deserializers = deserializers; this.arguments = new Object[deserializers.length]; } public void set(int i, ByteBuffer buffer) { - arguments[i] = deserializers[i].deserialize(version, buffer); + arguments[i] = deserializers[i].deserialize(context.getProtocolVersion(), buffer); } @Override diff --git a/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java b/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java index fab0402e62..004f3608c4 100644 --- a/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java +++ b/src/java/org/apache/cassandra/cql3/functions/FunctionCall.java @@ -28,7 +28,7 @@ import org.apache.cassandra.cql3.AssignmentTestable; import org.apache.cassandra.cql3.CQL3Type; import org.apache.cassandra.cql3.ColumnSpecification; import org.apache.cassandra.cql3.CqlBuilder; -import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.VariableSpecifications; import org.apache.cassandra.cql3.statements.RequestValidations; import org.apache.cassandra.cql3.terms.Constants; @@ -68,19 +68,19 @@ public class FunctionCall extends Term.NonTerminal } @Override - public Term.Terminal bind(QueryOptions options) throws InvalidRequestException + public Term.Terminal bind(FunctionContext context) throws InvalidRequestException { - return makeTerminal(fun, bindAndGet(options)); + return makeTerminal(fun, bindAndGet(context)); } @Override - public ByteBuffer bindAndGet(QueryOptions options) throws InvalidRequestException + public ByteBuffer bindAndGet(FunctionContext context) throws InvalidRequestException { - Arguments arguments = fun.newArguments(options.getProtocolVersion()); + Arguments arguments = fun.newArguments(context); for (int i = 0, m = terms.size(); i < m; i++) { Term t = terms.get(i); - ByteBuffer argument = t.bindAndGet(options); + ByteBuffer argument = t.bindAndGet(context); RequestValidations.checkBindValueSet(argument, "Invalid unset value for argument in call to function %s", fun.name().name); arguments.set(i, argument); } diff --git a/src/java/org/apache/cassandra/cql3/functions/LengthFcts.java b/src/java/org/apache/cassandra/cql3/functions/LengthFcts.java index 64cb940606..ec2085a1ed 100644 --- a/src/java/org/apache/cassandra/cql3/functions/LengthFcts.java +++ b/src/java/org/apache/cassandra/cql3/functions/LengthFcts.java @@ -23,11 +23,11 @@ import java.util.HashSet; import java.util.Set; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.Int32Type; import org.apache.cassandra.db.marshal.UTF8Type; import org.apache.cassandra.exceptions.InvalidRequestException; -import org.apache.cassandra.transport.ProtocolVersion; import org.apache.cassandra.utils.ByteBufferUtil; /** @@ -60,9 +60,9 @@ public class LengthFcts { // Do not deserialize @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return FunctionArguments.newNoopInstance(version, 1); + return FunctionArguments.newNoopInstance(context, 1); } @Override diff --git a/src/java/org/apache/cassandra/cql3/functions/NativeFunction.java b/src/java/org/apache/cassandra/cql3/functions/NativeFunction.java index 3437a8d158..1647712ae8 100644 --- a/src/java/org/apache/cassandra/cql3/functions/NativeFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/NativeFunction.java @@ -21,8 +21,8 @@ import java.util.Arrays; import javax.annotation.Nullable; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.db.marshal.AbstractType; -import org.apache.cassandra.transport.ProtocolVersion; /** * Base class for our native/hardcoded functions. @@ -62,9 +62,8 @@ public abstract class NativeFunction extends AbstractFunction } @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return FunctionArguments.newInstanceForNativeFunction(version, argTypes); + return FunctionArguments.newInstanceForNativeFunction(context, argTypes); } - } diff --git a/src/java/org/apache/cassandra/cql3/functions/PartialScalarFunction.java b/src/java/org/apache/cassandra/cql3/functions/PartialScalarFunction.java index 4486a40978..f18ff5635c 100644 --- a/src/java/org/apache/cassandra/cql3/functions/PartialScalarFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/PartialScalarFunction.java @@ -25,7 +25,7 @@ import org.apache.cassandra.transport.ProtocolVersion; /** * A partial application of a function. * - * @see ScalarFunction#partialApplication(ProtocolVersion, List) + * @see ScalarFunction#partialApplication(ProtocolVersion, org.apache.cassandra.cql3.FunctionContext, List) */ public interface PartialScalarFunction extends ScalarFunction { diff --git a/src/java/org/apache/cassandra/cql3/functions/PartiallyAppliedScalarFunction.java b/src/java/org/apache/cassandra/cql3/functions/PartiallyAppliedScalarFunction.java index 7a5e5fb71f..5a7fef14c8 100644 --- a/src/java/org/apache/cassandra/cql3/functions/PartiallyAppliedScalarFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/PartiallyAppliedScalarFunction.java @@ -21,6 +21,7 @@ import java.nio.ByteBuffer; import java.util.List; import org.apache.cassandra.cql3.CqlBuilder; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.transport.ProtocolVersion; @@ -28,7 +29,7 @@ import org.apache.cassandra.transport.ProtocolVersion; /** * An internal function used to hold the partial application of another function to only some of its parameters. * - * @see ScalarFunction#partialApplication(ProtocolVersion, List) + * @see ScalarFunction#partialApplication(FunctionContext, List) */ final class PartiallyAppliedScalarFunction extends NativeScalarFunction implements PartialScalarFunction { @@ -63,9 +64,9 @@ final class PartiallyAppliedScalarFunction extends NativeScalarFunction implemen } @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return new PartialFunctionArguments(version, function, partialParameters, argTypes.size()); + return new PartialFunctionArguments(context, function, partialParameters, argTypes.size()); } @Override @@ -126,9 +127,9 @@ final class PartiallyAppliedScalarFunction extends NativeScalarFunction implemen */ private final int[] mapping; - public PartialFunctionArguments(ProtocolVersion version, ScalarFunction function, List partialArguments, int unresolvedCount) + public PartialFunctionArguments(FunctionContext context, ScalarFunction function, List partialArguments, int unresolvedCount) { - arguments = function.newArguments(version); + arguments = function.newArguments(context); mapping = new int[unresolvedCount]; int mappingIndex = 0; for (int i = 0, m = partialArguments.size(); i < m; i++) @@ -151,6 +152,12 @@ final class PartiallyAppliedScalarFunction extends NativeScalarFunction implemen return arguments.getProtocolVersion(); } + @Override + public FunctionContext context() + { + return arguments.context(); + } + @Override public void set(int i, ByteBuffer buffer) { diff --git a/src/java/org/apache/cassandra/cql3/functions/PreComputedScalarFunction.java b/src/java/org/apache/cassandra/cql3/functions/PreComputedScalarFunction.java index 0dd32ed0c4..2a30088de5 100644 --- a/src/java/org/apache/cassandra/cql3/functions/PreComputedScalarFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/PreComputedScalarFunction.java @@ -20,6 +20,7 @@ package org.apache.cassandra.cql3.functions; import java.nio.ByteBuffer; import java.util.List; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.transport.ProtocolVersion; @@ -27,7 +28,7 @@ import org.apache.cassandra.transport.ProtocolVersion; /** * Function used internally to hold the pre-computed result of another function. *

- * See {@link ScalarFunction#partialApplication(ProtocolVersion, List)} for why this is used. + * See {@link ScalarFunction#partialApplication(ProtocolVersion, FunctionContext, List)} for why this is used. *

* Note : the function is cautious in keeping the protocol version used for the pre-computed value and to * fallback to recomputation if the version we get when {@link #execute} is called. I don't think it's truly necessary @@ -74,7 +75,7 @@ class PreComputedScalarFunction extends NativeScalarFunction implements PartialS if (nothing.getProtocolVersion() == valueVersion) return value; - Arguments args = function.newArguments(nothing.getProtocolVersion()); + Arguments args = function.newArguments(nothing.context()); for (int i = 0, m = arguments.size() ; i < m; i++) { args.set(i, arguments.get(i));; @@ -83,7 +84,7 @@ class PreComputedScalarFunction extends NativeScalarFunction implements PartialS return function.execute(args); } - public ScalarFunction partialApplication(ProtocolVersion protocolVersion, List nothing) throws InvalidRequestException + public ScalarFunction partialApplication(ProtocolVersion protocolVersion, FunctionContext context, List nothing) throws InvalidRequestException { return this; } diff --git a/src/java/org/apache/cassandra/cql3/functions/ScalarFunction.java b/src/java/org/apache/cassandra/cql3/functions/ScalarFunction.java index 986242a413..9e7db43b35 100644 --- a/src/java/org/apache/cassandra/cql3/functions/ScalarFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/ScalarFunction.java @@ -65,7 +65,7 @@ public interface ScalarFunction extends Function * * and such that for any value of {@code b} and {@code d}, {@code bar(b, d) == foo(3, b, 'bar', d)}. * - * @param protocolVersion protocol version used for arguments + * @param protocolVersion protocol version used for arguments * @param partialArguments a list of input arguments for the function where some arguments can be {@link #UNRESOLVED}. * The input must be of size {@code this.argsType().size()}. For convenience, it is * allowed both to pass a list with all arguments being {@link #UNRESOLVED} (the function is @@ -88,6 +88,7 @@ public interface ScalarFunction extends Function if (isPure() && unresolvedCount == 0) { + // if isPure(), requires no FunctionContext Arguments arguments = newArguments(protocolVersion); for (int i = 0, m = partialArguments.size(); i < m; i++) { diff --git a/src/java/org/apache/cassandra/cql3/functions/TimeFcts.java b/src/java/org/apache/cassandra/cql3/functions/TimeFcts.java index 16b546afe2..dbcb0cdc49 100644 --- a/src/java/org/apache/cassandra/cql3/functions/TimeFcts.java +++ b/src/java/org/apache/cassandra/cql3/functions/TimeFcts.java @@ -80,7 +80,7 @@ public abstract class TimeFcts @Override public ByteBuffer execute(Arguments arguments) { - return type.now(); + return type.now(arguments.context()); } @Override diff --git a/src/java/org/apache/cassandra/cql3/functions/ToJsonFct.java b/src/java/org/apache/cassandra/cql3/functions/ToJsonFct.java index 0f0e917ba5..2ce0bc6e4c 100644 --- a/src/java/org/apache/cassandra/cql3/functions/ToJsonFct.java +++ b/src/java/org/apache/cassandra/cql3/functions/ToJsonFct.java @@ -22,10 +22,10 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.UTF8Type; import org.apache.cassandra.exceptions.InvalidRequestException; -import org.apache.cassandra.transport.ProtocolVersion; import org.apache.cassandra.utils.ByteBufferUtil; import static java.lang.String.format; @@ -55,9 +55,9 @@ public class ToJsonFct extends NativeScalarFunction } @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return new FunctionArguments(version, (protocolVersion, buffer) -> { + return new FunctionArguments(context, (protocolVersion, buffer) -> { AbstractType argType = argTypes.get(0); if (buffer == null || (!buffer.hasRemaining() && argType.isEmptyValueMeaningless())) diff --git a/src/java/org/apache/cassandra/cql3/functions/TokenFct.java b/src/java/org/apache/cassandra/cql3/functions/TokenFct.java index a83ad4f7d9..9dd80c69b6 100644 --- a/src/java/org/apache/cassandra/cql3/functions/TokenFct.java +++ b/src/java/org/apache/cassandra/cql3/functions/TokenFct.java @@ -22,13 +22,13 @@ import java.util.Arrays; import java.util.List; import org.apache.cassandra.cql3.AssignmentTestable; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.db.CBuilder; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.schema.ColumnMetadata; import org.apache.cassandra.schema.Schema; import org.apache.cassandra.schema.TableMetadata; -import org.apache.cassandra.transport.ProtocolVersion; public class TokenFct extends NativeScalarFunction { @@ -41,11 +41,11 @@ public class TokenFct extends NativeScalarFunction } @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { ArgumentDeserializer[] deserializers = new ArgumentDeserializer[argTypes.size()]; Arrays.fill(deserializers, ArgumentDeserializer.NOOP_DESERIALIZER); - return new FunctionArguments(version, deserializers); + return new FunctionArguments(context, deserializers); } private static AbstractType[] getKeyTypes(TableMetadata metadata) diff --git a/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java b/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java index ccc24725ce..97ba464bc0 100644 --- a/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java +++ b/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java @@ -32,6 +32,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.cassandra.cql3.CqlBuilder; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.UserType; import org.apache.cassandra.exceptions.ConfigurationException; @@ -120,9 +121,9 @@ public class UDAggregate extends UserFunction implements AggregateFunction } @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return FunctionArguments.newInstanceForUdf(version, argumentTypes); + return FunctionArguments.newInstanceForUdf(context, argumentTypes); } public boolean hasReferenceTo(Function function) @@ -230,8 +231,9 @@ public class UDAggregate extends UserFunction implements AggregateFunction } } - public ByteBuffer compute(ProtocolVersion protocolVersion) throws InvalidRequestException + public ByteBuffer compute(FunctionContext context) throws InvalidRequestException { + ProtocolVersion protocolVersion = context.getProtocolVersion(); maybeInit(protocolVersion); // final function is traced in UDFunction @@ -242,7 +244,7 @@ public class UDAggregate extends UserFunction implements AggregateFunction if (finalFunction instanceof UDFunction) { UDFunction udf = (UDFunction)finalFunction; - Object result = udf.executeForAggregate(state, FunctionArguments.emptyInstance(protocolVersion)); + Object result = udf.executeForAggregate(state, context.noArguments()); return resultType.decompose(protocolVersion, result); } throw new UnsupportedOperationException("UDAs only support UDFs"); diff --git a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java index 3e6d20f9a3..71d51c09c9 100644 --- a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java @@ -49,6 +49,7 @@ import org.apache.cassandra.config.Config; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.cql3.CqlBuilder; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.functions.types.TypeCodec; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.UserType; @@ -232,9 +233,9 @@ public abstract class UDFunction extends UserFunction implements ScalarFunction } @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return FunctionArguments.newInstanceForUdf(version, argumentTypes); + return FunctionArguments.newInstanceForUdf(context, argumentTypes); } public static UDFunction tryCreate(FunctionName name, diff --git a/src/java/org/apache/cassandra/cql3/functions/VectorFcts.java b/src/java/org/apache/cassandra/cql3/functions/VectorFcts.java index 08403b5f94..a224390ddb 100644 --- a/src/java/org/apache/cassandra/cql3/functions/VectorFcts.java +++ b/src/java/org/apache/cassandra/cql3/functions/VectorFcts.java @@ -22,11 +22,11 @@ import java.nio.ByteBuffer; import java.util.List; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.FloatType; import org.apache.cassandra.db.marshal.VectorType; import org.apache.cassandra.exceptions.InvalidRequestException; -import org.apache.cassandra.transport.ProtocolVersion; import io.github.jbellis.jvector.vector.VectorSimilarityFunction; @@ -69,9 +69,9 @@ public class VectorFcts return new NativeScalarFunction(name, FloatType.instance, type, type) { @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return new FunctionArguments(version, + return new FunctionArguments(context, (v, b) -> type.composeAsFloat(b), (v, b) -> type.composeAsFloat(b)); } diff --git a/src/java/org/apache/cassandra/cql3/functions/masking/ColumnMask.java b/src/java/org/apache/cassandra/cql3/functions/masking/ColumnMask.java index 642bd358ca..0302b4db2b 100644 --- a/src/java/org/apache/cassandra/cql3/functions/masking/ColumnMask.java +++ b/src/java/org/apache/cassandra/cql3/functions/masking/ColumnMask.java @@ -35,6 +35,7 @@ import org.apache.cassandra.cql3.AssignmentTestable; import org.apache.cassandra.cql3.CQL3Type; import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.cql3.CqlBuilder; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.functions.Arguments; import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.cql3.functions.FunctionName; @@ -135,20 +136,28 @@ public class ColumnMask * @param version the used version of the transport protocol * @return a masker instance that caches the terminal masking function arguments */ - public Masker masker(ProtocolVersion version) + public Masker masker(ProtocolVersion version, FunctionContext context) { - return new Masker(version, function, partialArgumentValues); + return new Masker(version, context, function, partialArgumentValues); } public static class Masker { + public static final Masker NOT_A_MASKER = new Masker(); + private final ScalarFunction function; private final Arguments arguments; - private Masker(ProtocolVersion version, ScalarFunction function, ByteBuffer[] partialArgumentValues) + private Masker() + { + function = null; + arguments = null; + } + + private Masker(ProtocolVersion version, FunctionContext context, ScalarFunction function, ByteBuffer[] partialArgumentValues) { this.function = function; - arguments = function.newArguments(version); + arguments = function.newArguments(context); for (int i = 0; i < partialArgumentValues.length; i++) arguments.set(i + 1, partialArgumentValues[i]); } diff --git a/src/java/org/apache/cassandra/cql3/functions/masking/DefaultMaskingFunction.java b/src/java/org/apache/cassandra/cql3/functions/masking/DefaultMaskingFunction.java index e5c0ee2e7d..cdf98052ac 100644 --- a/src/java/org/apache/cassandra/cql3/functions/masking/DefaultMaskingFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/masking/DefaultMaskingFunction.java @@ -21,6 +21,7 @@ package org.apache.cassandra.cql3.functions.masking; import java.nio.ByteBuffer; import java.util.List; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.functions.Arguments; import org.apache.cassandra.cql3.functions.FunctionArguments; import org.apache.cassandra.cql3.functions.FunctionFactory; @@ -29,7 +30,6 @@ import org.apache.cassandra.cql3.functions.FunctionParameter; import org.apache.cassandra.cql3.functions.NativeFunction; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.exceptions.InvalidRequestException; -import org.apache.cassandra.transport.ProtocolVersion; /** * A {@link MaskingFunction} that returns a fixed replacement value for the data type of its single argument. @@ -53,9 +53,9 @@ public class DefaultMaskingFunction extends MaskingFunction } @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return FunctionArguments.newNoopInstance(version, 1); + return FunctionArguments.newNoopInstance(context, 1); } @Override diff --git a/src/java/org/apache/cassandra/cql3/functions/masking/HashMaskingFunction.java b/src/java/org/apache/cassandra/cql3/functions/masking/HashMaskingFunction.java index 4bd0d74ecf..4af45f33a6 100644 --- a/src/java/org/apache/cassandra/cql3/functions/masking/HashMaskingFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/masking/HashMaskingFunction.java @@ -30,6 +30,7 @@ import javax.annotation.Nullable; import com.google.common.annotations.VisibleForTesting; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.functions.ArgumentDeserializer; import org.apache.cassandra.cql3.functions.Arguments; import org.apache.cassandra.cql3.functions.FunctionArguments; @@ -41,7 +42,6 @@ import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.BytesType; import org.apache.cassandra.db.marshal.UTF8Type; import org.apache.cassandra.exceptions.InvalidRequestException; -import org.apache.cassandra.transport.ProtocolVersion; import org.apache.cassandra.utils.ByteBufferUtil; /** @@ -71,9 +71,9 @@ public class HashMaskingFunction extends MaskingFunction } @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return new FunctionArguments(version, + return new FunctionArguments(context, ArgumentDeserializer.NOOP_DESERIALIZER, // the value to be masked (v, b) -> messageDigest(b)); // the algorithm, if any } diff --git a/src/java/org/apache/cassandra/cql3/functions/masking/NullMaskingFunction.java b/src/java/org/apache/cassandra/cql3/functions/masking/NullMaskingFunction.java index 830a1c3b54..8dac012331 100644 --- a/src/java/org/apache/cassandra/cql3/functions/masking/NullMaskingFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/masking/NullMaskingFunction.java @@ -21,6 +21,7 @@ package org.apache.cassandra.cql3.functions.masking; import java.nio.ByteBuffer; import java.util.List; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.functions.Arguments; import org.apache.cassandra.cql3.functions.FunctionArguments; import org.apache.cassandra.cql3.functions.FunctionFactory; @@ -29,7 +30,6 @@ import org.apache.cassandra.cql3.functions.FunctionParameter; import org.apache.cassandra.cql3.functions.NativeFunction; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.exceptions.InvalidRequestException; -import org.apache.cassandra.transport.ProtocolVersion; /** * A {@link MaskingFunction} that always returns a {@code null} column. The returned value is always an absent column, @@ -48,9 +48,9 @@ public class NullMaskingFunction extends MaskingFunction } @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return FunctionArguments.newNoopInstance(version, 1); + return FunctionArguments.newNoopInstance(context, 1); } @Override diff --git a/src/java/org/apache/cassandra/cql3/functions/masking/PartialMaskingFunction.java b/src/java/org/apache/cassandra/cql3/functions/masking/PartialMaskingFunction.java index 6fb2b9e797..d64682136b 100644 --- a/src/java/org/apache/cassandra/cql3/functions/masking/PartialMaskingFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/masking/PartialMaskingFunction.java @@ -29,6 +29,7 @@ import com.google.common.annotations.VisibleForTesting; import org.apache.commons.lang3.StringUtils; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.functions.ArgumentDeserializer; import org.apache.cassandra.cql3.functions.Arguments; import org.apache.cassandra.cql3.functions.FunctionArguments; @@ -40,7 +41,6 @@ import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.Int32Type; import org.apache.cassandra.db.marshal.UTF8Type; import org.apache.cassandra.exceptions.InvalidRequestException; -import org.apache.cassandra.transport.ProtocolVersion; import static java.lang.String.format; @@ -106,9 +106,9 @@ public class PartialMaskingFunction extends MaskingFunction } @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return new FunctionArguments(version, + return new FunctionArguments(context, inputType.getArgumentDeserializer(), Int32Type.instance.getArgumentDeserializer(), Int32Type.instance.getArgumentDeserializer(), diff --git a/src/java/org/apache/cassandra/cql3/functions/masking/ReplaceMaskingFunction.java b/src/java/org/apache/cassandra/cql3/functions/masking/ReplaceMaskingFunction.java index 373743daf4..22f6667dd9 100644 --- a/src/java/org/apache/cassandra/cql3/functions/masking/ReplaceMaskingFunction.java +++ b/src/java/org/apache/cassandra/cql3/functions/masking/ReplaceMaskingFunction.java @@ -21,6 +21,7 @@ package org.apache.cassandra.cql3.functions.masking; import java.nio.ByteBuffer; import java.util.List; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.functions.Arguments; import org.apache.cassandra.cql3.functions.FunctionArguments; import org.apache.cassandra.cql3.functions.FunctionFactory; @@ -29,7 +30,6 @@ import org.apache.cassandra.cql3.functions.FunctionParameter; import org.apache.cassandra.cql3.functions.NativeFunction; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.exceptions.InvalidRequestException; -import org.apache.cassandra.transport.ProtocolVersion; /** * A {@link MaskingFunction} that replaces the specified column value by a certain replacement value. @@ -48,9 +48,9 @@ public class ReplaceMaskingFunction extends MaskingFunction } @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return FunctionArguments.newNoopInstance(version, 2); + return FunctionArguments.newNoopInstance(context, 2); } @Override diff --git a/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java index a2455bf14f..6d2820cb62 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java @@ -24,6 +24,7 @@ import javax.annotation.Nullable; import com.google.common.collect.RangeSet; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.db.Clustering; import org.apache.cassandra.db.ClusteringComparator; @@ -199,7 +200,7 @@ final class ClusteringColumnRestrictions extends RestrictionSetWrapper @Override public void addToRowFilter(RowFilter filter, IndexRegistry indexRegistry, - QueryOptions options, + FunctionContext context, IndexHints indexHints) throws InvalidRequestException { int position = 0; @@ -209,7 +210,7 @@ final class ClusteringColumnRestrictions extends RestrictionSetWrapper // We ignore all the clustering columns that can be handled by slices. if (handleInFilter(restriction, position) || restriction.hasSupportingIndex(indexRegistry, indexHints)) { - restriction.addToRowFilter(filter, indexRegistry, options, indexHints); + restriction.addToRowFilter(filter, indexRegistry, context, indexHints); continue; } diff --git a/src/java/org/apache/cassandra/cql3/restrictions/MergedRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/MergedRestriction.java index 447a1ac6dc..2da9a6547e 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/MergedRestriction.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/MergedRestriction.java @@ -25,8 +25,8 @@ import java.util.Set; import com.google.common.collect.ImmutableList; import com.google.common.collect.RangeSet; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.Operator; -import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.Relation; import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.db.filter.IndexHints; @@ -332,31 +332,31 @@ public final class MergedRestriction implements SingleRestriction } @Override - public List values(QueryOptions options) + public List values(FunctionContext context) { - List values = restrictions.get(0).values(options); + List values = restrictions.get(0).values(context); for (int i = 1, m = restrictions.size(); i < m; i++) { - values.retainAll(restrictions.get(i).values(options)); + values.retainAll(restrictions.get(i).values(context)); } return values; } @Override - public void restrict(RangeSet rangeSet, QueryOptions options, IPartitioner partitioner) + public void restrict(RangeSet rangeSet, FunctionContext context, IPartitioner partitioner) { for (int i = 0, m = restrictions.size(); i < m; i++) { - restrictions.get(i).restrict(rangeSet, options, partitioner); + restrictions.get(i).restrict(rangeSet, context, partitioner); } } @Override - public void addToRowFilter(RowFilter filter, IndexRegistry indexRegistry, QueryOptions options, IndexHints indexHints) + public void addToRowFilter(RowFilter filter, IndexRegistry indexRegistry, FunctionContext context, IndexHints indexHints) { for (int i = 0, m = restrictions.size(); i < m; i++) { - restrictions.get(i).addToRowFilter(filter, indexRegistry, options, indexHints); + restrictions.get(i).addToRowFilter(filter, indexRegistry, context, indexHints); } } } diff --git a/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java b/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java index e5b576332d..ea10a020a7 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java @@ -19,7 +19,7 @@ package org.apache.cassandra.cql3.restrictions; import java.util.List; -import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.db.filter.IndexHints; import org.apache.cassandra.db.filter.RowFilter; @@ -109,12 +109,12 @@ public interface Restriction /** * Adds to the specified row filter the expressions corresponding to this Restriction. * - * @param filter the row filter to add expressions to + * @param filter the row filter to add expressions to * @param indexRegistry the index registry - * @param options the query options + * @param context the query options */ void addToRowFilter(RowFilter filter, IndexRegistry indexRegistry, - QueryOptions options, + FunctionContext context, IndexHints indexHints); } diff --git a/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java b/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java index d7b463b762..b973102b88 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java @@ -33,7 +33,7 @@ import com.google.common.collect.AbstractIterator; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.db.filter.IndexHints; import org.apache.cassandra.db.filter.RowFilter; @@ -113,10 +113,10 @@ final class RestrictionSet implements Restrictions, Iterable } @Override - public void addToRowFilter(RowFilter filter, IndexRegistry indexRegistry, QueryOptions options, IndexHints indexHints) throws InvalidRequestException + public void addToRowFilter(RowFilter filter, IndexRegistry indexRegistry, FunctionContext context, IndexHints indexHints) throws InvalidRequestException { for (Restriction restriction : this) - restriction.addToRowFilter(filter, indexRegistry, options, indexHints); + restriction.addToRowFilter(filter, indexRegistry, context, indexHints); } @Override diff --git a/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSetWrapper.java b/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSetWrapper.java index abe1f004a4..c88005d165 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSetWrapper.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSetWrapper.java @@ -22,7 +22,7 @@ import java.util.List; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.db.filter.IndexHints; import org.apache.cassandra.db.filter.RowFilter; @@ -48,10 +48,10 @@ class RestrictionSetWrapper implements Restrictions public void addToRowFilter(RowFilter filter, IndexRegistry indexRegistry, - QueryOptions options, + FunctionContext context, IndexHints indexHints) { - restrictions.addToRowFilter(filter, indexRegistry, options, indexHints); + restrictions.addToRowFilter(filter, indexRegistry, context, indexHints); } public List columns() diff --git a/src/java/org/apache/cassandra/cql3/restrictions/SimpleRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/SimpleRestriction.java index e8db9daa54..13bdaaceae 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/SimpleRestriction.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/SimpleRestriction.java @@ -28,8 +28,8 @@ import java.util.stream.Collectors; import com.google.common.collect.RangeSet; import org.apache.cassandra.cql3.ColumnsExpression; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.Operator; -import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.Relation; import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.cql3.terms.Term; @@ -248,44 +248,44 @@ public final class SimpleRestriction implements SingleRestriction } @Override - public List values(QueryOptions options) + public List values(FunctionContext context) { assert operator == Operator.EQ || operator == Operator.IN || operator == Operator.ANN : String.format("Unexpected operator: %s", operator); - return bindAndGetClusteringElements(options); + return bindAndGetClusteringElements(context); } @Override - public void restrict(RangeSet rangeSet, QueryOptions options, IPartitioner partitioner) + public void restrict(RangeSet rangeSet, FunctionContext context, IPartitioner partitioner) { assert operator.isSlice() || operator == Operator.EQ; - operator.restrict(rangeSet, bindAndGetClusteringElements(options), partitioner); + operator.restrict(rangeSet, bindAndGetClusteringElements(context), partitioner); } - private List bindAndGetClusteringElements(QueryOptions options) + private List bindAndGetClusteringElements(FunctionContext context) { switch (columnsExpression.kind()) { case SINGLE_COLUMN: case TOKEN: - return bindAndGetSingleTermClusteringElements(options); + return bindAndGetSingleTermClusteringElements(context); case MULTI_COLUMN: - return bindAndGetMultiTermClusteringElements(options); + return bindAndGetMultiTermClusteringElements(context); default: throw new UnsupportedOperationException(); } } - private List bindAndGetSingleTermClusteringElements(QueryOptions options) + private List bindAndGetSingleTermClusteringElements(FunctionContext context) { - if (values.isSingleTerm(options)) + if (values.isSingleTerm(context)) { - ByteBuffer value = bindAndGetSingle(options); + ByteBuffer value = bindAndGetSingle(context); return Collections.singletonList(ClusteringElements.of(columnsExpression.columnSpecification(), value, isOnToken())); } - List values = bindAndGet(options); + List values = bindAndGet(context); if (values.isEmpty()) return Collections.emptyList(); @@ -301,9 +301,9 @@ public final class SimpleRestriction implements SingleRestriction return elements; } - private List bindAndGetMultiTermClusteringElements(QueryOptions options) + private List bindAndGetMultiTermClusteringElements(FunctionContext context) { - List> values = bindAndGetElements(options); + List> values = bindAndGetElements(context); if (values.isEmpty()) return Collections.emptyList(); @@ -313,24 +313,24 @@ public final class SimpleRestriction implements SingleRestriction return elements; } - private List bindAndGet(QueryOptions options) + private List bindAndGet(FunctionContext context) { - List buffers = values.bindAndGet(options); + List buffers = values.bindAndGet(context); validate(buffers); buffers.forEach(this::validate); return buffers; } - private ByteBuffer bindAndGetSingle(QueryOptions options) + private ByteBuffer bindAndGetSingle(FunctionContext context) { - ByteBuffer buffer = values.bindAndGetSingleTermValue(options); + ByteBuffer buffer = values.bindAndGetSingleTermValue(context); validate(buffer); return buffer; } - private List> bindAndGetElements(QueryOptions options) + private List> bindAndGetElements(FunctionContext context) { - List> elementsList = values.bindAndGetElements(options); + List> elementsList = values.bindAndGetElements(context); validate(elementsList); elementsList.forEach(this::validateElements); return elementsList; @@ -371,7 +371,7 @@ public final class SimpleRestriction implements SingleRestriction } @Override - public void addToRowFilter(RowFilter filter, IndexRegistry indexRegistry, QueryOptions options, IndexHints indexHints) + public void addToRowFilter(RowFilter filter, IndexRegistry indexRegistry, FunctionContext context, IndexHints indexHints) { if (isOnToken()) throw new UnsupportedOperationException(); @@ -380,7 +380,7 @@ public final class SimpleRestriction implements SingleRestriction switch (columnsExpression.kind()) { case SINGLE_COLUMN: - List buffers = bindAndGet(options); + List buffers = bindAndGet(context); if (operator.kind() != Operator.Kind.BINARY) { if (operator == Operator.IN && !column.type.isCounter()) @@ -406,7 +406,7 @@ public final class SimpleRestriction implements SingleRestriction if (isEQ()) { - List elements = bindAndGetElements(options).get(0); + List elements = bindAndGetElements(context).get(0); for (int i = 0, m = columns().size(); i < m; i++) { @@ -420,7 +420,7 @@ public final class SimpleRestriction implements SingleRestriction // c IN (x, y, z) and we can perform filtering if (columns().size() == 1) { - List values = bindAndGetElements(options).stream() + List values = bindAndGetElements(context).stream() .map(elements -> elements.get(0)) .collect(Collectors.toList()); @@ -451,12 +451,12 @@ public final class SimpleRestriction implements SingleRestriction } } - ByteBuffer key = columnsExpression.element(options); + ByteBuffer key = columnsExpression.element(context); if (key == null) throw invalidRequest("Invalid null map key for column %s", column.name.toCQLString()); if (key == ByteBufferUtil.UNSET_BYTE_BUFFER) throw invalidRequest("Invalid unset map key for column %s", column.name.toCQLString()); - List values = bindAndGet(options); + List values = bindAndGet(context); filter.addMapEquality(column, key, operator, values.get(0)); } break; diff --git a/src/java/org/apache/cassandra/cql3/restrictions/SingleRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/SingleRestriction.java index 7720fb1bb6..c99097a3cf 100644 --- a/src/java/org/apache/cassandra/cql3/restrictions/SingleRestriction.java +++ b/src/java/org/apache/cassandra/cql3/restrictions/SingleRestriction.java @@ -21,7 +21,7 @@ import java.util.List; import com.google.common.collect.RangeSet; -import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.dht.IPartitioner; import org.apache.cassandra.index.Index; @@ -93,19 +93,19 @@ public interface SingleRestriction extends Restriction /** * Returns the values selected by this restriction (or by the intersection of merged restrictions) if the operator is an {@code EQ} or an {@code IN}. * - * @param options the query options + * @param context the query options * @return the values selected by this restriction (or by the intersection of merged restrictions) if the operator is an {@code EQ} or an {@code IN}. * @throws UnsupportedOperationException if the operator is not an {@code EQ} or an {@code IN}. */ - List values(QueryOptions options); + List values(FunctionContext context); /** * Removes the ranges of values not selected by this restriction from the specified {@code RangeSet} if the operator is an operator selecting ranges of data. * - * @param rangeSet the range set to add to - * @param options the query options + * @param rangeSet the range set to add to + * @param context the query options * @param partitioner the partitioner, used to identify MIN_TOKEN when using token restrictions * @throws UnsupportedOperationException if the operator is not an operator selecting ranges of data. */ - void restrict(RangeSet rangeSet, QueryOptions options, IPartitioner partitioner); + void restrict(RangeSet rangeSet, FunctionContext context, IPartitioner partitioner); } diff --git a/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java b/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java index cd64e4ce9a..b810f54028 100644 --- a/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java +++ b/src/java/org/apache/cassandra/cql3/selection/AbstractFunctionSelector.java @@ -28,6 +28,7 @@ import com.google.common.collect.Iterables; import org.apache.commons.lang3.text.StrBuilder; import org.apache.cassandra.cql3.ColumnSpecification; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.functions.Arguments; import org.apache.cassandra.cql3.functions.Function; @@ -133,9 +134,17 @@ abstract class AbstractFunctionSelector extends Selector * The list used to pass the function arguments is recycled to avoid the cost of instantiating a new list * with each function call. */ - private final Arguments args; + private Arguments args; protected final List argSelectors; + @Override + public void prepare(FunctionContext context) + { + args = fun.newArguments(context); + for (Selector selector : argSelectors) + selector.prepare(context); + } + public static Factory newFactory(final Function fun, final SelectorFactories factories) throws InvalidRequestException { if (fun.isAggregate()) @@ -179,7 +188,7 @@ abstract class AbstractFunctionSelector extends Selector public Selector newInstance(QueryOptions options) throws InvalidRequestException { - return fun.isAggregate() ? new AggregateFunctionSelector(options.getProtocolVersion(), fun, factories.newInstances(options)) + return fun.isAggregate() ? new AggregateFunctionSelector(fun, factories.newInstances(options)) : createScalarSelector(options, (ScalarFunction) fun, factories.newInstances(options)); } @@ -204,7 +213,7 @@ abstract class AbstractFunctionSelector extends Selector } if (terminalCount == 0) - return new ScalarFunctionSelector(version, fun, argSelectors); + return new ScalarFunctionSelector(fun, argSelectors); // We have some terminal arguments, do a partial application ScalarFunction partialFunction = function.partialApplication(version, terminalArgs); @@ -212,6 +221,7 @@ abstract class AbstractFunctionSelector extends Selector // If all the arguments are terminal and the function is pure we can reduce to a simple value. if (terminalCount == argSelectors.size() && fun.isPure()) { + // pure functions need no context Arguments arguments = partialFunction.newArguments(version); return new TermSelector(partialFunction.execute(arguments), partialFunction.returnType()); } @@ -222,7 +232,7 @@ abstract class AbstractFunctionSelector extends Selector if (!selector.isTerminal()) remainingSelectors.add(selector); } - return new ScalarFunctionSelector(version, partialFunction, remainingSelectors); + return new ScalarFunctionSelector(partialFunction, remainingSelectors); } public boolean isWritetimeSelectorFactory() @@ -255,12 +265,11 @@ abstract class AbstractFunctionSelector extends Selector }; } - protected AbstractFunctionSelector(Kind kind, ProtocolVersion version, T fun, List argSelectors) + protected AbstractFunctionSelector(Kind kind, T fun, List argSelectors) { super(kind); this.fun = fun; this.argSelectors = argSelectors; - this.args = fun.newArguments(version); } @Override diff --git a/src/java/org/apache/cassandra/cql3/selection/AggregateFunctionSelector.java b/src/java/org/apache/cassandra/cql3/selection/AggregateFunctionSelector.java index d91981b04d..a205bb6a1a 100644 --- a/src/java/org/apache/cassandra/cql3/selection/AggregateFunctionSelector.java +++ b/src/java/org/apache/cassandra/cql3/selection/AggregateFunctionSelector.java @@ -20,6 +20,7 @@ package org.apache.cassandra.cql3.selection; import java.nio.ByteBuffer; import java.util.List; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.functions.AggregateFunction; import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.exceptions.InvalidRequestException; @@ -32,10 +33,11 @@ final class AggregateFunctionSelector extends AbstractFunctionSelector argSelectors) { - return new AggregateFunctionSelector(version, function, argSelectors); + return new AggregateFunctionSelector(function, argSelectors); } }; + private FunctionContext context; private final AggregateFunction.Aggregate aggregate; public boolean isAggregate() @@ -43,6 +45,13 @@ final class AggregateFunctionSelector extends AbstractFunctionSelector argSelectors) throws InvalidRequestException + AggregateFunctionSelector(Function fun, List argSelectors) throws InvalidRequestException { - super(Kind.AGGREGATE_FUNCTION_SELECTOR, version, (AggregateFunction) fun, argSelectors); + super(Kind.AGGREGATE_FUNCTION_SELECTOR, (AggregateFunction) fun, argSelectors); this.aggregate = this.fun.newAggregate(); } diff --git a/src/java/org/apache/cassandra/cql3/selection/ElementsSelector.java b/src/java/org/apache/cassandra/cql3/selection/ElementsSelector.java index da75884bb1..41e58fd6da 100644 --- a/src/java/org/apache/cassandra/cql3/selection/ElementsSelector.java +++ b/src/java/org/apache/cassandra/cql3/selection/ElementsSelector.java @@ -24,6 +24,7 @@ import com.google.common.base.Objects; import com.google.common.collect.Range; import org.apache.cassandra.cql3.ColumnSpecification; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.selection.SimpleSelector.SimpleSelectorFactory; import org.apache.cassandra.cql3.terms.Term; @@ -63,6 +64,13 @@ abstract class ElementsSelector extends Selector this.type = getCollectionType(selected); } + @Override + public void prepare(FunctionContext context) + { + super.prepare(context); + selected.prepare(context); + } + private static boolean isUnset(ByteBuffer bb) { return bb == ByteBufferUtil.UNSET_BYTE_BUFFER; diff --git a/src/java/org/apache/cassandra/cql3/selection/FieldSelector.java b/src/java/org/apache/cassandra/cql3/selection/FieldSelector.java index 8c34586d1d..96b85de0b7 100644 --- a/src/java/org/apache/cassandra/cql3/selection/FieldSelector.java +++ b/src/java/org/apache/cassandra/cql3/selection/FieldSelector.java @@ -24,6 +24,7 @@ import java.util.List; import com.google.common.base.Objects; import org.apache.cassandra.cql3.ColumnSpecification; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.db.TypeSizes; import org.apache.cassandra.db.filter.ColumnFilter; @@ -159,6 +160,13 @@ final class FieldSelector extends Selector this.selected = selected; } + @Override + public void prepare(FunctionContext context) + { + super.prepare(context); + selected.prepare(context); + } + @Override public boolean equals(Object o) { diff --git a/src/java/org/apache/cassandra/cql3/selection/ListSelector.java b/src/java/org/apache/cassandra/cql3/selection/ListSelector.java index 3494b4b831..d478bbba05 100644 --- a/src/java/org/apache/cassandra/cql3/selection/ListSelector.java +++ b/src/java/org/apache/cassandra/cql3/selection/ListSelector.java @@ -24,6 +24,7 @@ import java.util.List; import com.google.common.base.Objects; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.terms.Lists; import org.apache.cassandra.db.TypeSizes; @@ -139,6 +140,14 @@ final class ListSelector extends Selector this.elements = elements; } + @Override + public void prepare(FunctionContext context) + { + super.prepare(context); + for (Selector selector : elements) + selector.prepare(context); + } + @Override public boolean equals(Object o) { diff --git a/src/java/org/apache/cassandra/cql3/selection/MapSelector.java b/src/java/org/apache/cassandra/cql3/selection/MapSelector.java index 0c6c6dd8e0..81f4df168a 100644 --- a/src/java/org/apache/cassandra/cql3/selection/MapSelector.java +++ b/src/java/org/apache/cassandra/cql3/selection/MapSelector.java @@ -28,6 +28,7 @@ import java.util.stream.Collectors; import com.google.common.base.Objects; import org.apache.cassandra.cql3.ColumnSpecification; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.cql3.terms.Maps; @@ -259,7 +260,17 @@ final class MapSelector extends Selector this.type = (MapType) type; this.elements = elements; } - + + @Override + public void prepare(FunctionContext context) + { + super.prepare(context); + for (Pair selector : elements) + { + selector.left.prepare(context); + selector.right.prepare(context); + } + } @Override public boolean equals(Object o) diff --git a/src/java/org/apache/cassandra/cql3/selection/ResultSetBuilder.java b/src/java/org/apache/cassandra/cql3/selection/ResultSetBuilder.java index 9ab5ca0370..2b90d91ffd 100644 --- a/src/java/org/apache/cassandra/cql3/selection/ResultSetBuilder.java +++ b/src/java/org/apache/cassandra/cql3/selection/ResultSetBuilder.java @@ -21,6 +21,7 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.ResultSet; import org.apache.cassandra.cql3.ResultSet.ResultMetadata; import org.apache.cassandra.cql3.selection.Selection.Selectors; @@ -60,17 +61,18 @@ public final class ResultSetBuilder private long size = 0; private boolean sizeWarningEmitted = false; - public ResultSetBuilder(ResultMetadata metadata, Selectors selectors, boolean unmask) + public ResultSetBuilder(ResultMetadata metadata, FunctionContext context, Selectors selectors, boolean unmask) { - this(metadata, selectors, unmask, null); + this(metadata, context, selectors, unmask, null); } - public ResultSetBuilder(ResultMetadata metadata, Selectors selectors, boolean unmask, GroupMaker groupMaker) + public ResultSetBuilder(ResultMetadata metadata, FunctionContext context, Selectors selectors, boolean unmask, GroupMaker groupMaker) { this.resultSet = new ResultSet(metadata.copy(), new ArrayList<>()); this.selectors = selectors; this.groupMaker = groupMaker; this.unmask = unmask; + selectors.prepare(context); } private void addSize(List row) diff --git a/src/java/org/apache/cassandra/cql3/selection/ScalarFunctionSelector.java b/src/java/org/apache/cassandra/cql3/selection/ScalarFunctionSelector.java index 6df2b85b08..2cc9c6dce9 100644 --- a/src/java/org/apache/cassandra/cql3/selection/ScalarFunctionSelector.java +++ b/src/java/org/apache/cassandra/cql3/selection/ScalarFunctionSelector.java @@ -33,7 +33,7 @@ final class ScalarFunctionSelector extends AbstractFunctionSelector argSelectors) { - return new ScalarFunctionSelector(version, function, argSelectors); + return new ScalarFunctionSelector(function, argSelectors); } }; @@ -69,8 +69,8 @@ final class ScalarFunctionSelector extends AbstractFunctionSelector argSelectors) + ScalarFunctionSelector(Function fun, List argSelectors) { - super(Kind.SCALAR_FUNCTION_SELECTOR, version, (ScalarFunction) fun, argSelectors); + super(Kind.SCALAR_FUNCTION_SELECTOR, (ScalarFunction) fun, argSelectors); } } diff --git a/src/java/org/apache/cassandra/cql3/selection/Selection.java b/src/java/org/apache/cassandra/cql3/selection/Selection.java index 9d08166720..f5f6b4276d 100644 --- a/src/java/org/apache/cassandra/cql3/selection/Selection.java +++ b/src/java/org/apache/cassandra/cql3/selection/Selection.java @@ -32,6 +32,7 @@ import com.google.common.collect.Iterators; import com.google.common.collect.Lists; import org.apache.cassandra.cql3.ColumnSpecification; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.Json; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.ResultSet; @@ -393,6 +394,8 @@ public abstract class Selection */ default boolean collectWritetimes() { return false; } + default void prepare(FunctionContext context) {} + /** * Adds the current row of the specified ResultSetBuilder. * @@ -601,6 +604,13 @@ public abstract class Selection return isJson ? rowToJson(outputRow, options.getProtocolVersion(), metadata, orderingColumns) : outputRow; } + @Override + public void prepare(FunctionContext context) + { + for (Selector selector : selectors) + selector.prepare(context); + } + public void addInputRow(InputRow input) { for (Selector selector : selectors) diff --git a/src/java/org/apache/cassandra/cql3/selection/Selector.java b/src/java/org/apache/cassandra/cql3/selection/Selector.java index fa22ea0bb5..d3dbbab8ef 100644 --- a/src/java/org/apache/cassandra/cql3/selection/Selector.java +++ b/src/java/org/apache/cassandra/cql3/selection/Selector.java @@ -24,6 +24,7 @@ import java.util.List; import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.cql3.ColumnSpecification; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.cql3.selection.ColumnTimestamps.TimestampsType; @@ -562,6 +563,8 @@ public abstract class Selector return false; } + public void prepare(FunctionContext context) {} + /** * Checks that this selector is valid for GROUP BY clause. */ diff --git a/src/java/org/apache/cassandra/cql3/selection/SetSelector.java b/src/java/org/apache/cassandra/cql3/selection/SetSelector.java index a4cc5009af..6b042733fd 100644 --- a/src/java/org/apache/cassandra/cql3/selection/SetSelector.java +++ b/src/java/org/apache/cassandra/cql3/selection/SetSelector.java @@ -26,6 +26,7 @@ import java.util.TreeSet; import com.google.common.base.Objects; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.terms.Sets; import org.apache.cassandra.db.TypeSizes; @@ -141,6 +142,14 @@ final class SetSelector extends Selector this.elements = elements; } + @Override + public void prepare(FunctionContext context) + { + super.prepare(context); + for (Selector selector : elements) + selector.prepare(context); + } + @Override public boolean equals(Object o) { diff --git a/src/java/org/apache/cassandra/cql3/selection/SimpleSelector.java b/src/java/org/apache/cassandra/cql3/selection/SimpleSelector.java index 965f3a49f9..d94339faf2 100644 --- a/src/java/org/apache/cassandra/cql3/selection/SimpleSelector.java +++ b/src/java/org/apache/cassandra/cql3/selection/SimpleSelector.java @@ -24,6 +24,7 @@ import com.google.common.base.Objects; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.cql3.ColumnSpecification; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.functions.masking.ColumnMask; import org.apache.cassandra.db.TypeSizes; @@ -38,6 +39,8 @@ import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.transport.ProtocolVersion; import org.apache.cassandra.utils.ByteBufferUtil; +import static org.apache.cassandra.cql3.functions.masking.ColumnMask.Masker.NOT_A_MASKER; + public final class SimpleSelector extends Selector { protected static final SelectorDeserializer deserializer = new SelectorDeserializer() @@ -47,7 +50,7 @@ public final class SimpleSelector extends Selector ByteBuffer columnName = ByteBufferUtil.readWithVIntLength(in); ColumnMetadata column = metadata.getColumn(columnName); int idx = in.readInt(); - return new SimpleSelector(column, idx, false, ProtocolVersion.CURRENT); + return new SimpleSelector(column, idx, false); } }; @@ -87,7 +90,7 @@ public final class SimpleSelector extends Selector @Override public Selector newInstance(QueryOptions options) { - return new SimpleSelector(column, idx, useForPostOrdering, options.getProtocolVersion()); + return new SimpleSelector(column, idx, useForPostOrdering); } @Override @@ -120,7 +123,7 @@ public final class SimpleSelector extends Selector public final ColumnMetadata column; private final int idx; - private final ColumnMask.Masker masker; + private ColumnMask.Masker masker; private ByteBuffer current; private ColumnTimestamps writetimes; private ColumnTimestamps ttls; @@ -197,7 +200,7 @@ public final class SimpleSelector extends Selector return column.name.toString(); } - private SimpleSelector(ColumnMetadata column, int idx, boolean useForPostOrdering, ProtocolVersion version) + private SimpleSelector(ColumnMetadata column, int idx, boolean useForPostOrdering) { super(Kind.SIMPLE_SELECTOR); this.column = column; @@ -207,9 +210,13 @@ public final class SimpleSelector extends Selector - The column doesn't have a mask - This selector is for a query with ORDER BY post-ordering */ - this.masker = useForPostOrdering || column.getMask() == null - ? null - : column.getMask().masker(version); + this.masker = useForPostOrdering || column.getMask() == null ? null : NOT_A_MASKER; + } + + public void prepare(FunctionContext context) + { + if (masker != null) + masker = column.getMask().masker(context.options().getProtocolVersion(), context); } @Override diff --git a/src/java/org/apache/cassandra/cql3/selection/TupleSelector.java b/src/java/org/apache/cassandra/cql3/selection/TupleSelector.java index fd3071b1cd..9b76252292 100644 --- a/src/java/org/apache/cassandra/cql3/selection/TupleSelector.java +++ b/src/java/org/apache/cassandra/cql3/selection/TupleSelector.java @@ -24,6 +24,7 @@ import java.util.List; import com.google.common.base.Objects; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.terms.Tuples; import org.apache.cassandra.db.TypeSizes; @@ -140,6 +141,14 @@ final class TupleSelector extends Selector this.elements = elements; } + @Override + public void prepare(FunctionContext context) + { + super.prepare(context); + for (Selector selector : elements) + selector.prepare(context); + } + @Override public boolean equals(Object o) { diff --git a/src/java/org/apache/cassandra/cql3/selection/UserTypeSelector.java b/src/java/org/apache/cassandra/cql3/selection/UserTypeSelector.java index 3f6449a2d1..42579aebc6 100644 --- a/src/java/org/apache/cassandra/cql3/selection/UserTypeSelector.java +++ b/src/java/org/apache/cassandra/cql3/selection/UserTypeSelector.java @@ -29,6 +29,7 @@ import com.google.common.base.Objects; import org.apache.cassandra.cql3.ColumnSpecification; import org.apache.cassandra.cql3.FieldIdentifier; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.cql3.terms.UserTypes; @@ -235,6 +236,14 @@ final class UserTypeSelector extends Selector this.fields = fields; } + @Override + public void prepare(FunctionContext context) + { + super.prepare(context); + for (Selector selector : fields.values()) + selector.prepare(context); + } + @Override public boolean equals(Object o) { diff --git a/src/java/org/apache/cassandra/cql3/selection/VectorSelector.java b/src/java/org/apache/cassandra/cql3/selection/VectorSelector.java index ceef630485..c63b5f02ba 100644 --- a/src/java/org/apache/cassandra/cql3/selection/VectorSelector.java +++ b/src/java/org/apache/cassandra/cql3/selection/VectorSelector.java @@ -26,6 +26,7 @@ import java.util.Objects; import com.google.common.base.Preconditions; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.terms.Lists; import org.apache.cassandra.db.filter.ColumnFilter; @@ -74,6 +75,14 @@ public class VectorSelector extends Selector this.elements = elements; } + @Override + public void prepare(FunctionContext context) + { + super.prepare(context); + for (Selector selector : elements) + selector.prepare(context); + } + public static Factory newFactory(final AbstractType type, final SelectorFactories factories) { assert type.isVector() : String.format("Unable to create vector selector from typs %s", type.asCQL3Type()); diff --git a/src/java/org/apache/cassandra/cql3/selection/WritetimeOrTTLSelector.java b/src/java/org/apache/cassandra/cql3/selection/WritetimeOrTTLSelector.java index 9772d242ef..6292f29d05 100644 --- a/src/java/org/apache/cassandra/cql3/selection/WritetimeOrTTLSelector.java +++ b/src/java/org/apache/cassandra/cql3/selection/WritetimeOrTTLSelector.java @@ -23,6 +23,7 @@ import java.nio.ByteBuffer; import com.google.common.base.Objects; import org.apache.cassandra.cql3.ColumnSpecification; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.db.TypeSizes; import org.apache.cassandra.db.filter.ColumnFilter; @@ -179,6 +180,13 @@ final class WritetimeOrTTLSelector extends Selector this.isMultiCell = isMultiCell; } + @Override + public void prepare(FunctionContext context) + { + super.prepare(context); + selected.prepare(context); + } + @Override public boolean equals(Object o) { diff --git a/src/java/org/apache/cassandra/cql3/statements/CQL3CasRequest.java b/src/java/org/apache/cassandra/cql3/statements/CQL3CasRequest.java index c2514d04ce..eba92a16e5 100644 --- a/src/java/org/apache/cassandra/cql3/statements/CQL3CasRequest.java +++ b/src/java/org/apache/cassandra/cql3/statements/CQL3CasRequest.java @@ -35,8 +35,11 @@ import accord.api.Update; import accord.primitives.Keys; import accord.primitives.Txn; +import org.apache.cassandra.cql3.FunctionContext; +import org.apache.cassandra.cql3.FunctionContext.PartialFunctionContext; import org.apache.cassandra.cql3.QueryOptions; -import org.apache.cassandra.cql3.UpdateParameters; +import org.apache.cassandra.cql3.RowUpdateBuilder; +import org.apache.cassandra.cql3.RowUpdateBuilder.NoTimeRowUpdateBuilder; import org.apache.cassandra.cql3.conditions.ColumnCondition; import org.apache.cassandra.db.Clustering; import org.apache.cassandra.db.Columns; @@ -309,12 +312,12 @@ public class CQL3CasRequest implements CASRequest return partitionUpdate; } - private static class CASUpdateParameters extends UpdateParameters + private static class CASUpdateBuilder extends RowUpdateBuilder implements PartialFunctionContext { final long timeUuidMsb; long timeUuidNanos; - public CASUpdateParameters(TableMetadata metadata, ClientState state, QueryOptions options, long timestamp, long nowInSec, int ttl, Map prefetchedRows, long timeUuidMsb, long timeUuidNanos) throws InvalidRequestException + public CASUpdateBuilder(TableMetadata metadata, ClientState state, QueryOptions options, long timestamp, long nowInSec, int ttl, Map prefetchedRows, long timeUuidMsb, long timeUuidNanos) throws InvalidRequestException { super(metadata, state, options, timestamp, nowInSec, ttl, prefetchedRows); this.timeUuidMsb = timeUuidMsb; @@ -325,6 +328,11 @@ public class CQL3CasRequest implements CASRequest { return TimeUUID.toBytes(timeUuidMsb, TimeUUIDType.signedBytesToNativeLong(timeUuidNanos++)); } + + public long nowMicros() + { + return timestamp; + } } /** @@ -353,8 +361,8 @@ public class CQL3CasRequest implements CASRequest long applyUpdates(FilteredPartition current, PartitionUpdate.Builder updateBuilder, ClientState state, long timeUuidMsb, long timeUuidNanos) { Map map = stmt.requiresRead() ? Collections.singletonMap(key, current) : null; - CASUpdateParameters params = - new CASUpdateParameters(metadata, state, options, timestamp, nowInSeconds, + CASUpdateBuilder params = + new CASUpdateBuilder(metadata, state, options, timestamp, nowInSeconds, stmt.getTimeToLive(options), map, timeUuidMsb, timeUuidNanos); stmt.addUpdateForKey(updateBuilder, clustering, params); return params.timeUuidNanos; @@ -382,14 +390,14 @@ public class CQL3CasRequest implements CASRequest { // No slice statements currently require a read, but this maintains consistency with RowUpdate, and future proofs us Map map = stmt.requiresRead() ? Collections.singletonMap(key, current) : null; - UpdateParameters params = - new UpdateParameters(metadata, - state, - options, - timestamp, - nowInSeconds, - stmt.getTimeToLive(options), - map); + RowUpdateBuilder params = + new NoTimeRowUpdateBuilder(metadata, + state, + options, + timestamp, + nowInSeconds, + stmt.getTimeToLive(options), + map); stmt.addUpdateForKey(updateBuilder, slice, params); } } @@ -472,11 +480,11 @@ public class CQL3CasRequest implements CASRequest super(clustering); } - public void addConditions(Collection conds, QueryOptions options) throws InvalidRequestException + public void addConditions(Collection conds, FunctionContext context) throws InvalidRequestException { for (ColumnCondition condition : conds) { - conditions.add(condition.bind(options)); + conditions.add(condition.bind(context)); } } diff --git a/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java b/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java index 157f1f7d8a..4d21b9f5b1 100644 --- a/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java @@ -28,8 +28,8 @@ import org.apache.cassandra.cql3.Attributes; import org.apache.cassandra.cql3.Operation; import org.apache.cassandra.cql3.Operations; import org.apache.cassandra.cql3.QualifiedName; +import org.apache.cassandra.cql3.RowUpdateBuilder; import org.apache.cassandra.cql3.StatementSource; -import org.apache.cassandra.cql3.UpdateParameters; import org.apache.cassandra.cql3.VariableSpecifications; import org.apache.cassandra.cql3.WhereClause; import org.apache.cassandra.cql3.conditions.ColumnCondition; @@ -69,7 +69,7 @@ public class DeleteStatement extends ModificationStatement } @Override - public void addUpdateForKey(PartitionUpdate.Builder updateBuilder, Clustering clustering, UpdateParameters params) + public void addUpdateForKey(PartitionUpdate.Builder updateBuilder, Clustering clustering, RowUpdateBuilder params) throws InvalidRequestException { TableMetadata metadata = metadata(); @@ -125,7 +125,7 @@ public class DeleteStatement extends ModificationStatement } @Override - public void addUpdateForKey(PartitionUpdate.Builder update, Slice slice, UpdateParameters params) + public void addUpdateForKey(PartitionUpdate.Builder update, Slice slice, RowUpdateBuilder params) { List regularDeletions = getRegularOperations(); List staticDeletions = getStaticOperations(); diff --git a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java index 7a6235b534..423e66939a 100644 --- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java @@ -48,14 +48,16 @@ import org.apache.cassandra.cql3.Attributes; import org.apache.cassandra.cql3.CQLStatement; import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.cql3.ColumnSpecification; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.Operation; import org.apache.cassandra.cql3.Operations; import org.apache.cassandra.cql3.QualifiedName; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.QueryProcessor; import org.apache.cassandra.cql3.ResultSet; +import org.apache.cassandra.cql3.RowUpdateBuilder; +import org.apache.cassandra.cql3.RowUpdateBuilder.RegularRowUpdateBuilder; import org.apache.cassandra.cql3.StatementSource; -import org.apache.cassandra.cql3.UpdateParameters; import org.apache.cassandra.cql3.Validation; import org.apache.cassandra.cql3.VariableSpecifications; import org.apache.cassandra.cql3.WhereClause; @@ -330,9 +332,9 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa return restrictions; } - public abstract void addUpdateForKey(PartitionUpdate.Builder updateBuilder, Clustering clustering, UpdateParameters params); + public abstract void addUpdateForKey(PartitionUpdate.Builder updateBuilder, Clustering clustering, RowUpdateBuilder builder); - public abstract void addUpdateForKey(PartitionUpdate.Builder updateBuilder, Slice slice, UpdateParameters params); + public abstract void addUpdateForKey(PartitionUpdate.Builder updateBuilder, Slice slice, RowUpdateBuilder builder); @Override public String keyspace() @@ -360,9 +362,9 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa return metadata().isVirtual(); } - public long getTimestamp(long now, QueryOptions options) throws InvalidRequestException + public long getTimestamp(long now, FunctionContext context) throws InvalidRequestException { - return attrs.getTimestamp(now, options); + return attrs.getTimestamp(now, context); } public boolean isTimestampSet() @@ -370,9 +372,9 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa return attrs.isTimestampSet(); } - public int getTimeToLive(QueryOptions options) throws InvalidRequestException + public int getTimeToLive(FunctionContext context) throws InvalidRequestException { - return attrs.getTimeToLive(options, metadata); + return attrs.getTimeToLive(context, metadata); } @Override @@ -454,12 +456,12 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa } } - public void validateTimestamp(QueryState queryState, QueryOptions options) + public void validateTimestamp(QueryState queryState, FunctionContext context) { if (!isTimestampSet()) return; - long ts = attrs.getTimestamp(options.getTimestamp(queryState), options); + long ts = attrs.getTimestamp(context.options().getTimestamp(queryState), context); Guardrails.maximumAllowableTimestamp.guard(ts, table(), false, queryState.getClientState()); Guardrails.minimumAllowableTimestamp.guard(ts, table(), false, queryState.getClientState()); } @@ -784,13 +786,13 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa assert left.size() == 1; int size = left.metadata.names.size() + right.metadata.names.size(); - List specs = new ArrayList(size); + List specs = new ArrayList<>(size); specs.addAll(left.metadata.names); specs.addAll(right.metadata.names); List> rows = new ArrayList<>(right.size()); for (int i = 0; i < right.size(); i++) { - List row = new ArrayList(size); + List row = new ArrayList<>(size); row.addAll(left.rows.get(0)); row.addAll(right.rows.get(i)); rows.add(row); @@ -821,11 +823,10 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa Iterables.addAll(defs, metadata.primaryKeyColumns()); Iterables.addAll(defs, columnsWithConditions); selection = Selection.forColumns(metadata, new ArrayList<>(defs), false); - } Selectors selectors = selection.newSelectors(options); - ResultSetBuilder builder = new ResultSetBuilder(selection.getResultMetadata(), selectors, false); + ResultSetBuilder builder = new ResultSetBuilder(selection.getResultMetadata(), options, selectors, false); SelectStatement.forSelection(metadata, selection) .processPartition(partition, options, builder, nowInSeconds); @@ -1087,7 +1088,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa if (slices.isEmpty()) return; - UpdateParameters params = makeUpdateParameters(keys, + RowUpdateBuilder params = makeUpdateParameters(keys, (slicesToFilter) -> new ClusteringIndexSliceFilter(slicesToFilter, false), slices, state, @@ -1120,7 +1121,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa if (restrictions.hasClusteringColumnsRestrictions() && clusterings.isEmpty()) return; - UpdateParameters params = makeUpdateParameters(keys, clusterings, state, options, local, timestamp, nowInSeconds, requestTime); + RowUpdateBuilder params = makeUpdateBuilder(keys, clusterings, state, options, local, timestamp, nowInSeconds, requestTime); for (ByteBuffer key : keys) { @@ -1171,14 +1172,14 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa return restrictions.getSlices(options); } - private UpdateParameters makeUpdateParameters(Collection keys, - NavigableSet> clusterings, - ClientState state, - QueryOptions options, - boolean local, - long timestamp, - long nowInSeconds, - Dispatcher.RequestTime requestTime) + private RowUpdateBuilder makeUpdateBuilder(Collection keys, + NavigableSet> clusterings, + ClientState state, + QueryOptions options, + boolean local, + long timestamp, + long nowInSeconds, + Dispatcher.RequestTime requestTime) { if (clusterings.contains(Clustering.STATIC_CLUSTERING)) return makeUpdateParameters(keys, @@ -1206,7 +1207,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa ); } - private UpdateParameters makeUpdateParameters(Collection keys, + private RowUpdateBuilder makeUpdateParameters(Collection keys, // filter is needed rarely, so we allocate it on demand java.util.function.Function filterBuilder, F filterArg, @@ -1229,13 +1230,13 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa nowInSeconds, requestTime); - return new UpdateParameters(metadata(), - state, - options, - getTimestamp(timestamp, options), - nowInSeconds, - getTimeToLive(options), - lists); + return new RegularRowUpdateBuilder(metadata(), + state, + options, + getTimestamp(timestamp, options), + nowInSeconds, + getTimeToLive(options), + lists); } public static abstract class Parsed extends QualifiedStatement diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java index a32dc8182d..1f82004bfa 100644 --- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java @@ -1081,7 +1081,7 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement, ClientState state) throws InvalidRequestException { GroupMaker groupMaker = aggregationSpec == null ? null : aggregationSpec.newGroupMaker(); - ResultSetBuilder result = new ResultSetBuilder(getResultMetadata(), selectors, unmask, groupMaker); + ResultSetBuilder result = new ResultSetBuilder(getResultMetadata(), options, selectors, unmask, groupMaker); while (partitions.hasNext()) { diff --git a/src/java/org/apache/cassandra/cql3/statements/TransactionStatement.java b/src/java/org/apache/cassandra/cql3/statements/TransactionStatement.java index 5d50e0e486..97a7fd32f2 100644 --- a/src/java/org/apache/cassandra/cql3/statements/TransactionStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/TransactionStatement.java @@ -51,6 +51,7 @@ import org.apache.cassandra.audit.AuditLogEntryType; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.cql3.CQLStatement; import org.apache.cassandra.cql3.ColumnSpecification; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.ResultSet; import org.apache.cassandra.cql3.VariableSpecifications; @@ -80,8 +81,8 @@ import org.apache.cassandra.service.accord.serializers.TableMetadatas; import org.apache.cassandra.service.accord.serializers.TableMetadatasAndKeys; import org.apache.cassandra.service.accord.txn.AccordUpdate; import org.apache.cassandra.service.accord.txn.TxnCondition; -import org.apache.cassandra.service.accord.txn.TxnData; import org.apache.cassandra.service.accord.txn.TxnDataKeyValue; +import org.apache.cassandra.service.accord.txn.TxnDataResult; import org.apache.cassandra.service.accord.txn.TxnNamedRead; import org.apache.cassandra.service.accord.txn.TxnQuery; import org.apache.cassandra.service.accord.txn.TxnRead; @@ -96,7 +97,6 @@ import org.apache.cassandra.tcm.ClusterMetadata; import org.apache.cassandra.tcm.Epoch; import org.apache.cassandra.transport.Dispatcher; import org.apache.cassandra.transport.messages.ResultMessage; -import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.NoSpamLogger; import static accord.primitives.Txn.Kind.Read; @@ -607,31 +607,35 @@ public class TransactionStatement implements CQLStatement.CompositeCQLStatement, if (txnResult.kind() == retry_new_protocol) throw new InvalidRequestException(UNSUPPORTED_MIGRATION); TxnValidationRejection.maybeThrow(txnResult); - - TxnData data = (TxnData)txnResult; + TxnDataResult data = (TxnDataResult)txnResult; if (returningSelect != null) { @SuppressWarnings("unchecked") SinglePartitionReadQuery.Group selectQuery = (SinglePartitionReadQuery.Group) returningSelect.select.getQuery(options, 0); Selection.Selectors selectors = returningSelect.select.getSelection().newSelectors(options); - ResultSetBuilder result = new ResultSetBuilder(resultMetadata, selectors, false); + long atMicros = data.atMicros; + FunctionContext context = new FunctionContext.MicrosFunctionContext(atMicros) + { + @Override public QueryOptions options() { return options; } + }; + ResultSetBuilder result = new ResultSetBuilder(resultMetadata, context, selectors, false); + long atSeconds = atMicros / 1000_000; if (selectQuery.queries.size() == 1) { TxnDataKeyValue partition = (TxnDataKeyValue)data.get(txnDataName(RETURNING)); boolean reversed = selectQuery.queries.get(0).isReversed(); if (partition != null) - returningSelect.select.processPartition(partition.rowIterator(reversed), options, result, FBUtilities.nowInSeconds()); + returningSelect.select.processPartition(partition.rowIterator(reversed), options, result, atSeconds); } else { - long nowInSec = FBUtilities.nowInSeconds(); for (int i = 0; i < selectQuery.queries.size(); i++) { TxnDataKeyValue partition = (TxnDataKeyValue)data.get(txnDataName(RETURNING, i)); boolean reversed = selectQuery.queries.get(i).isReversed(); if (partition != null) - returningSelect.select.processPartition(partition.rowIterator(reversed), options, result, nowInSec); + returningSelect.select.processPartition(partition.rowIterator(reversed), options, result, atSeconds); } } return new ResultMessage.Rows(result.build()); @@ -649,7 +653,7 @@ public class TransactionStatement implements CQLStatement.CompositeCQLStatement, columns.add(reference.column()); } - ResultSetBuilder result = new ResultSetBuilder(resultMetadata, Selection.noopSelector(), false); + ResultSetBuilder result = new ResultSetBuilder(resultMetadata, FunctionContext.NONE, Selection.noopSelector(), false); result.newRow(options.getProtocolVersion(), null, null, columns); for (int i = 0; i < returningReferences.size(); i++) diff --git a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java index f0fe6ce20f..11e60477c7 100644 --- a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java @@ -38,8 +38,8 @@ import org.apache.cassandra.cql3.Operations; import org.apache.cassandra.cql3.Operator; import org.apache.cassandra.cql3.QualifiedName; import org.apache.cassandra.cql3.Relation; +import org.apache.cassandra.cql3.RowUpdateBuilder; import org.apache.cassandra.cql3.StatementSource; -import org.apache.cassandra.cql3.UpdateParameters; import org.apache.cassandra.cql3.VariableSpecifications; import org.apache.cassandra.cql3.WhereClause; import org.apache.cassandra.cql3.conditions.ColumnCondition; @@ -96,7 +96,7 @@ public class UpdateStatement extends ModificationStatement } @Override - public void addUpdateForKey(PartitionUpdate.Builder updateBuilder, Clustering clustering, UpdateParameters params) + public void addUpdateForKey(PartitionUpdate.Builder updateBuilder, Clustering clustering, RowUpdateBuilder params) { if (updatesRegularRows()) { @@ -142,7 +142,7 @@ public class UpdateStatement extends ModificationStatement } @Override - public void addUpdateForKey(PartitionUpdate.Builder update, Slice slice, UpdateParameters params) + public void addUpdateForKey(PartitionUpdate.Builder update, Slice slice, RowUpdateBuilder builder) { throw new UnsupportedOperationException(); } diff --git a/src/java/org/apache/cassandra/cql3/terms/Constants.java b/src/java/org/apache/cassandra/cql3/terms/Constants.java index 49d9592567..6556082916 100644 --- a/src/java/org/apache/cassandra/cql3/terms/Constants.java +++ b/src/java/org/apache/cassandra/cql3/terms/Constants.java @@ -24,9 +24,9 @@ import java.nio.ByteBuffer; import org.apache.cassandra.cql3.AssignmentTestable; import org.apache.cassandra.cql3.CQL3Type; import org.apache.cassandra.cql3.ColumnSpecification; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.Operation; -import org.apache.cassandra.cql3.QueryOptions; -import org.apache.cassandra.cql3.UpdateParameters; +import org.apache.cassandra.cql3.RowUpdateBuilder; import org.apache.cassandra.db.Clustering; import org.apache.cassandra.db.DecoratedKey; import org.apache.cassandra.db.marshal.AbstractType; @@ -63,9 +63,9 @@ import static java.nio.charset.StandardCharsets.US_ASCII; */ public abstract class Constants { - private static ByteBuffer getCurrentCellBuffer(ColumnMetadata column, DecoratedKey key, UpdateParameters params) + private static ByteBuffer getCurrentCellBuffer(ColumnMetadata column, DecoratedKey key, RowUpdateBuilder builder) { - Row currentRow = params.getPrefetchedRow(key, column.isStatic() ? Clustering.STATIC_CLUSTERING : params.currentClustering()); + Row currentRow = builder.getPrefetchedRow(key, column.isStatic() ? Clustering.STATIC_CLUSTERING : builder.currentClustering()); Cell currentCell = currentRow == null ? null : currentRow.getCell(column); return currentCell == null ? null : currentCell.buffer(); } @@ -228,7 +228,7 @@ public abstract class Constants { // TODO: The bind overriding should be removed. A user does not have to call bind on a Terminal. @Override - public Terminal bind(QueryOptions options) + public Terminal bind(FunctionContext context) { // We return null because that makes life easier for collections return null; @@ -459,7 +459,7 @@ public abstract class Constants } @Override - public ByteBuffer bindAndGet(QueryOptions options) + public ByteBuffer bindAndGet(FunctionContext context) { return bytes; } @@ -478,23 +478,23 @@ public abstract class Constants super(column, t); } - public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException + public void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException { - if (t.isByteArrayGetSupported(params.options)) + if (t.isByteArrayGetSupported(builder)) { - byte[] value = t.bindAndGetByteArray(params.options); + byte[] value = t.bindAndGetByteArray(builder); if (value == null) - params.addTombstone(column); + builder.addTombstone(column); else if (value != ByteArrayUtil.UNSET_BYTE_ARRAY) // use reference equality and not object equality - params.addCell(column, value); + builder.addCell(column, value); } else { - ByteBuffer value = t.bindAndGet(params.options); + ByteBuffer value = t.bindAndGet(builder); if (value == null) - params.addTombstone(column); + builder.addTombstone(column); else if (value != ByteBufferUtil.UNSET_BYTE_BUFFER) // use reference equality and not object equality - params.addCell(column, value); + builder.addCell(column, value); } } } @@ -511,43 +511,43 @@ public abstract class Constants return !(column.type instanceof CounterColumnType); } - public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException + public void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException { if (column.type instanceof CounterColumnType) { - ByteBuffer bytes = t.bindAndGet(params.options); + ByteBuffer bytes = t.bindAndGet(builder); if (bytes == null) throw new InvalidRequestException("Invalid null value for counter increment"); if (bytes == ByteBufferUtil.UNSET_BYTE_BUFFER) return; long increment = ByteBufferUtil.toLong(bytes); - params.addCounter(column, increment); + builder.addCounter(column, increment); } else if (column.type instanceof NumberType) { @SuppressWarnings("unchecked") NumberType type = (NumberType) column.type; - ByteBuffer increment = type.sanitize(t.bindAndGet(params.options)); + ByteBuffer increment = type.sanitize(t.bindAndGet(builder)); if (increment == null) return; - ByteBuffer current = type.sanitize(getCurrentCellBuffer(column, partitionKey, params)); + ByteBuffer current = type.sanitize(getCurrentCellBuffer(column, partitionKey, builder)); if (current == null) return; ByteBuffer newValue = type.add(type.compose(current), type.compose(increment)); - params.addCell(column, newValue); + builder.addCell(column, newValue); } else if (column.type instanceof StringType) { - ByteBuffer append = t.bindAndGet(params.options); + ByteBuffer append = t.bindAndGet(builder); if (append == null) return; - ByteBuffer current = getCurrentCellBuffer(column, partitionKey, params); + ByteBuffer current = getCurrentCellBuffer(column, partitionKey, builder); if (current == null) return; ByteBuffer newValue = ByteBuffer.allocate(current.remaining() + append.remaining()); FastByteOperations.copy(current, current.position(), newValue, newValue.position(), current.remaining()); FastByteOperations.copy(append, append.position(), newValue, newValue.position() + current.remaining(), append.remaining()); - params.addCell(column, newValue); + builder.addCell(column, newValue); } } } @@ -565,11 +565,11 @@ public abstract class Constants return !column.type.isCounter(); } - public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException + public void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException { if (column.type instanceof CounterColumnType) { - ByteBuffer bytes = t.bindAndGet(params.options); + ByteBuffer bytes = t.bindAndGet(builder); if (bytes == null) throw new InvalidRequestException("Invalid null value for counter increment"); if (bytes == ByteBufferUtil.UNSET_BYTE_BUFFER) @@ -579,19 +579,19 @@ public abstract class Constants if (increment == Long.MIN_VALUE) throw new InvalidRequestException("The negation of " + increment + " overflows supported counter precision (signed 8 bytes integer)"); - params.addCounter(column, -increment); + builder.addCounter(column, -increment); } else if (column.type instanceof NumberType) { @SuppressWarnings("unchecked") NumberType type = (NumberType) column.type; - ByteBuffer increment = type.sanitize(t.bindAndGet(params.options)); + ByteBuffer increment = type.sanitize(t.bindAndGet(builder)); if (increment == null) return; - ByteBuffer current = type.sanitize(getCurrentCellBuffer(column, partitionKey, params)); + ByteBuffer current = type.sanitize(getCurrentCellBuffer(column, partitionKey, builder)); if (current == null) return; ByteBuffer newValue = type.substract(type.compose(current), type.compose(increment)); - params.addCell(column, newValue); + builder.addCell(column, newValue); } } } @@ -605,12 +605,12 @@ public abstract class Constants super(column, null); } - public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException + public void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException { if (column.type.isMultiCell()) - params.setComplexDeletionTime(column); + builder.setComplexDeletionTime(column); else - params.addTombstone(column); + builder.addTombstone(column); } } } diff --git a/src/java/org/apache/cassandra/cql3/terms/InMarker.java b/src/java/org/apache/cassandra/cql3/terms/InMarker.java index 857d9e0018..7941acbfde 100644 --- a/src/java/org/apache/cassandra/cql3/terms/InMarker.java +++ b/src/java/org/apache/cassandra/cql3/terms/InMarker.java @@ -25,7 +25,7 @@ import java.util.List; import org.apache.cassandra.cql3.AssignmentTestable; import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.cql3.ColumnSpecification; -import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.VariableSpecifications; import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.db.marshal.AbstractType; @@ -60,9 +60,9 @@ public final class InMarker extends Terms.NonTerminals } @Override - public Terminals bind(QueryOptions options) + public Terminals bind(FunctionContext context) { - ByteBuffer values = options.getValues().get(bindIndex); + ByteBuffer values = context.options().getValues().get(bindIndex); if (values == null) return null; @@ -95,16 +95,16 @@ public final class InMarker extends Terms.NonTerminals } @Override - public List bindAndGet(QueryOptions options) + public List bindAndGet(FunctionContext context) { - Terminals terminals = bind(options); + Terminals terminals = bind(context); return terminals == null ? null : terminals.get(); } @Override - public List> bindAndGetElements(QueryOptions options) + public List> bindAndGetElements(FunctionContext context) { - Terminals terminals = bind(options); + Terminals terminals = bind(context); return terminals == null ? null : terminals.getElements(); } diff --git a/src/java/org/apache/cassandra/cql3/terms/Lists.java b/src/java/org/apache/cassandra/cql3/terms/Lists.java index 501306df72..a55d7ea799 100644 --- a/src/java/org/apache/cassandra/cql3/terms/Lists.java +++ b/src/java/org/apache/cassandra/cql3/terms/Lists.java @@ -35,9 +35,9 @@ import org.slf4j.LoggerFactory; import org.apache.cassandra.cql3.AssignmentTestable; import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.cql3.ColumnSpecification; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.Operation; -import org.apache.cassandra.cql3.QueryOptions; -import org.apache.cassandra.cql3.UpdateParameters; +import org.apache.cassandra.cql3.RowUpdateBuilder; import org.apache.cassandra.cql3.VariableSpecifications; import org.apache.cassandra.db.DecoratedKey; import org.apache.cassandra.db.guardrails.Guardrails; @@ -180,7 +180,7 @@ public abstract class Lists values.add(t); } MultiElements.DelayedValue value = new MultiElements.DelayedValue((MultiElementType) receiver.type.unwrap(), values); - return allTerminal ? value.bind(QueryOptions.DEFAULT) : value; + return allTerminal ? value.bind(FunctionContext.NONE) : value; } private void validateAssignableTo(String keyspace, ColumnSpecification receiver) throws InvalidRequestException @@ -304,16 +304,16 @@ public abstract class Lists return column.type.isMultiCell(); } - public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException + public void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException { - Term.Terminal value = t.bind(params.options); + Term.Terminal value = t.bind(builder); if (value == UNSET_VALUE) return; // delete + append if (column.type.isMultiCell()) - params.setComplexDeletionTimeForOverwrite(column); - Appender.doAppend(value, column, params); + builder.setComplexDeletionTimeForOverwrite(column); + Appender.doAppend(value, column, builder); } } @@ -349,23 +349,23 @@ public abstract class Lists idx.collectMarkerSpecification(boundNames, owner); } - public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException + public void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException { // we should not get here for frozen lists assert column.type.isMultiCell() : "Attempted to set an individual element on a frozen list"; Guardrails.readBeforeWriteListOperationsEnabled - .ensureEnabled("Setting of list items by index requiring read before write", params.clientState); + .ensureEnabled("Setting of list items by index requiring read before write", builder.clientState); - ByteBuffer index = idx.bindAndGet(params.options); - ByteBuffer value = t.bindAndGet(params.options); + ByteBuffer index = idx.bindAndGet(builder); + ByteBuffer value = t.bindAndGet(builder); if (index == null) throw new InvalidRequestException("Invalid null value for list index"); if (index == ByteBufferUtil.UNSET_BYTE_BUFFER) throw new InvalidRequestException("Invalid unset value for list index"); - Row existingRow = params.getPrefetchedRow(partitionKey, params.currentClustering()); + Row existingRow = builder.getPrefetchedRow(partitionKey, builder.currentClustering()); int existingSize = existingSize(existingRow, column); int idx = ByteBufferUtil.toInt(index); if (existingSize == 0) @@ -375,9 +375,9 @@ public abstract class Lists CellPath elementPath = existingRow.getComplexColumnData(column).getCellByIndex(idx).path(); if (value == null) - params.addTombstone(column, elementPath); + builder.addTombstone(column, elementPath); else if (value != ByteBufferUtil.UNSET_BYTE_BUFFER) - params.addCell(column, elementPath, value); + builder.addCell(column, elementPath, value); } } @@ -388,11 +388,11 @@ public abstract class Lists super(column, t); } - public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException + public void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException { assert column.type.isMultiCell() : "Attempted to append to a frozen list"; - Term.Terminal value = t.bind(params.options); - doAppend(value, column, params); + Term.Terminal value = t.bind(builder); + doAppend(value, column, builder); } @Override @@ -401,7 +401,7 @@ public abstract class Lists return column.type.isMultiCell(); } - static void doAppend(Term.Terminal value, ColumnMetadata column, UpdateParameters params) throws InvalidRequestException + static void doAppend(Term.Terminal value, ColumnMetadata column, RowUpdateBuilder builder) throws InvalidRequestException { ListType type = (ListType) column.type; @@ -409,7 +409,7 @@ public abstract class Lists { // for frozen lists, we're overwriting the whole cell value if (!type.isMultiCell()) - params.addTombstone(column); + builder.addTombstone(column); // If we append null, do nothing. Note that for Setter, we've // already removed the previous value so we're good here too @@ -426,22 +426,22 @@ public abstract class Lists // Guardrails about collection size are only checked for the added elements without considering // already existent elements. This is done so to avoid read-before-write, having additional checks // during SSTable write. - Guardrails.itemsPerCollection.guard(type.collectionSize(elements), column.name.toString(), false, params.clientState); + Guardrails.itemsPerCollection.guard(type.collectionSize(elements), column.name.toString(), false, builder.clientState); int dataSize = 0; for (ByteBuffer buffer : elements) { - ByteBuffer cellPath = ByteBuffer.wrap(params.nextTimeUUIDAsBytes()); - Cell cell = params.addCell(column, CellPath.create(cellPath), buffer); + ByteBuffer cellPath = ByteBuffer.wrap(builder.nextTimeUUIDAsBytes()); + Cell cell = builder.addCell(column, CellPath.create(cellPath), buffer); dataSize += cell.dataSize(); } - Guardrails.collectionListSize.guard(dataSize, column.name.toString(), false, params.clientState); + Guardrails.collectionListSize.guard(dataSize, column.name.toString(), false, builder.clientState); } else { - Guardrails.itemsPerCollection.guard(type.collectionSize(elements), column.name.toString(), false, params.clientState); - Cell cell = params.addCell(column, value.get()); - Guardrails.collectionListSize.guard(cell.dataSize(), column.name.toString(), false, params.clientState); + Guardrails.itemsPerCollection.guard(type.collectionSize(elements), column.name.toString(), false, builder.clientState); + Cell cell = builder.addCell(column, value.get()); + Guardrails.collectionListSize.guard(cell.dataSize(), column.name.toString(), false, builder.clientState); } } } @@ -459,10 +459,10 @@ public abstract class Lists return column.type.isMultiCell(); } - public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException + public void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException { assert column.type.isMultiCell() : "Attempted to prepend to a frozen list"; - Term.Terminal value = t.bind(params.options); + Term.Terminal value = t.bind(builder); if (value == null || value == UNSET_VALUE) return; @@ -484,7 +484,7 @@ public abstract class Lists // TODO: is this safe as part of LWTs? ByteBuffer uuid = ByteBuffer.wrap(atUnixMillisAsBytes(pt.millis, (pt.nanos + remainingInBatch--))); - params.addCell(column, CellPath.create(uuid), toAdd.get(i)); + builder.addCell(column, CellPath.create(uuid), toAdd.get(i)); } } } @@ -502,17 +502,17 @@ public abstract class Lists return true; } - public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException + public void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException { assert column.type.isMultiCell() : "Attempted to delete from a frozen list"; Guardrails.readBeforeWriteListOperationsEnabled - .ensureEnabled("Removal of list items requiring read before write", params.clientState); + .ensureEnabled("Removal of list items requiring read before write", builder.clientState); // We want to call bind before possibly returning to reject queries where the value provided is not a list. - Term.Terminal value = t.bind(params.options); + Term.Terminal value = t.bind(builder); - Row existingRow = params.getPrefetchedRow(partitionKey, params.currentClustering()); + Row existingRow = builder.getPrefetchedRow(partitionKey, builder.currentClustering()); ComplexColumnData complexData = existingRow == null ? null : existingRow.getComplexColumnData(column); if (value == null || value == UNSET_VALUE || complexData == null) return; @@ -525,7 +525,7 @@ public abstract class Lists for (Cell cell : complexData) { if (toDiscard.contains(cell.buffer())) - params.addTombstone(column, cell.path()); + builder.addTombstone(column, cell.path()); } } } @@ -543,20 +543,20 @@ public abstract class Lists return true; } - public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException + public void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException { assert column.type.isMultiCell() : "Attempted to delete an item by index from a frozen list"; Guardrails.readBeforeWriteListOperationsEnabled - .ensureEnabled("Removal of list items by index requiring read before write", params.clientState); + .ensureEnabled("Removal of list items by index requiring read before write", builder.clientState); - Term.Terminal index = t.bind(params.options); + Term.Terminal index = t.bind(builder); if (index == null) throw new InvalidRequestException("Invalid null value for list index"); if (index == Constants.UNSET_VALUE) return; - Row existingRow = params.getPrefetchedRow(partitionKey, params.currentClustering()); + Row existingRow = builder.getPrefetchedRow(partitionKey, builder.currentClustering()); int existingSize = existingSize(existingRow, column); int idx = ByteBufferUtil.toInt(index.get()); if (existingSize == 0) @@ -564,7 +564,7 @@ public abstract class Lists if (idx < 0 || idx >= existingSize) throw new InvalidRequestException(String.format("List index %d out of bound, list has size %d", idx, existingSize)); - params.addTombstone(column, existingRow.getComplexColumnData(column).getCellByIndex(idx).path()); + builder.addTombstone(column, existingRow.getComplexColumnData(column).getCellByIndex(idx).path()); } } } diff --git a/src/java/org/apache/cassandra/cql3/terms/Maps.java b/src/java/org/apache/cassandra/cql3/terms/Maps.java index 0b2af97245..f62d3ed48b 100644 --- a/src/java/org/apache/cassandra/cql3/terms/Maps.java +++ b/src/java/org/apache/cassandra/cql3/terms/Maps.java @@ -30,7 +30,7 @@ import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.cql3.ColumnSpecification; import org.apache.cassandra.cql3.Operation; import org.apache.cassandra.cql3.QueryOptions; -import org.apache.cassandra.cql3.UpdateParameters; +import org.apache.cassandra.cql3.RowUpdateBuilder; import org.apache.cassandra.cql3.VariableSpecifications; import org.apache.cassandra.db.DecoratedKey; import org.apache.cassandra.db.guardrails.Guardrails; @@ -252,16 +252,16 @@ public final class Maps super(column, t); } - public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException + public void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException { - Term.Terminal value = t.bind(params.options); + Term.Terminal value = t.bind(builder); if (value == UNSET_VALUE) return; // delete + put if (column.type.isMultiCell()) - params.setComplexDeletionTimeForOverwrite(column); - Putter.doPut(value, column, params); + builder.setComplexDeletionTimeForOverwrite(column); + Putter.doPut(value, column, builder); } } @@ -282,11 +282,11 @@ public final class Maps k.collectMarkerSpecification(boundNames, owner); } - public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException + public void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException { assert column.type.isMultiCell() : "Attempted to set a value for a single key on a frozen map"; - ByteBuffer key = k.bindAndGet(params.options); - ByteBuffer value = t.bindAndGet(params.options); + ByteBuffer key = k.bindAndGet(builder); + ByteBuffer value = t.bindAndGet(builder); if (key == null) throw new InvalidRequestException("Invalid null map key"); if (key == ByteBufferUtil.UNSET_BYTE_BUFFER) @@ -296,11 +296,11 @@ public final class Maps if (value == null) { - params.addTombstone(column, path); + builder.addTombstone(column, path); } else if (value != ByteBufferUtil.UNSET_BYTE_BUFFER) { - params.addCell(column, path, value); + builder.addCell(column, path, value); } } } @@ -312,15 +312,15 @@ public final class Maps super(column, t); } - public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException + public void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException { assert column.type.isMultiCell() : "Attempted to add items to a frozen map"; - Term.Terminal value = t.bind(params.options); + Term.Terminal value = t.bind(builder); if (value != UNSET_VALUE) - doPut(value, column, params); + doPut(value, column, builder); } - static void doPut(Term.Terminal value, ColumnMetadata column, UpdateParameters params) throws InvalidRequestException + static void doPut(Term.Terminal value, ColumnMetadata column, RowUpdateBuilder builder) throws InvalidRequestException { MapType type = (MapType) column.type; @@ -328,7 +328,7 @@ public final class Maps { // for frozen maps, we're overwriting the whole cell if (!type.isMultiCell()) - params.addTombstone(column); + builder.addTombstone(column); return; } @@ -343,22 +343,22 @@ public final class Maps // Guardrails about collection size are only checked for the added elements without considering // already existent elements. This is done so to avoid read-before-write, having additional checks // during SSTable write. - Guardrails.itemsPerCollection.guard(type.collectionSize(elements), column.name.toString(), false, params.clientState); + Guardrails.itemsPerCollection.guard(type.collectionSize(elements), column.name.toString(), false, builder.clientState); int dataSize = 0; Iterator iter = elements.iterator(); while(iter.hasNext()) { - Cell cell = params.addCell(column, CellPath.create(iter.next()), iter.next()); + Cell cell = builder.addCell(column, CellPath.create(iter.next()), iter.next()); dataSize += cell.dataSize(); } - Guardrails.collectionMapSize.guard(dataSize, column.name.toString(), false, params.clientState); + Guardrails.collectionMapSize.guard(dataSize, column.name.toString(), false, builder.clientState); } else { - Guardrails.itemsPerCollection.guard(type.collectionSize(elements), column.name.toString(), false, params.clientState); - Cell cell = params.addCell(column, value.get()); - Guardrails.collectionMapSize.guard(cell.dataSize(), column.name.toString(), false, params.clientState); + Guardrails.itemsPerCollection.guard(type.collectionSize(elements), column.name.toString(), false, builder.clientState); + Cell cell = builder.addCell(column, value.get()); + Guardrails.collectionMapSize.guard(cell.dataSize(), column.name.toString(), false, builder.clientState); } } } @@ -370,16 +370,16 @@ public final class Maps super(column, k); } - public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException + public void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException { assert column.type.isMultiCell() : "Attempted to delete a single key in a frozen map"; - Term.Terminal key = t.bind(params.options); + Term.Terminal key = t.bind(builder); if (key == null) throw new InvalidRequestException("Invalid null map key"); if (key == Constants.UNSET_VALUE) throw new InvalidRequestException("Invalid unset map key"); - params.addTombstone(column, CellPath.create(key.get())); + builder.addTombstone(column, CellPath.create(key.get())); } } } diff --git a/src/java/org/apache/cassandra/cql3/terms/Marker.java b/src/java/org/apache/cassandra/cql3/terms/Marker.java index 12c95d6619..603085ba77 100644 --- a/src/java/org/apache/cassandra/cql3/terms/Marker.java +++ b/src/java/org/apache/cassandra/cql3/terms/Marker.java @@ -22,7 +22,7 @@ import java.util.List; import org.apache.cassandra.cql3.AssignmentTestable; import org.apache.cassandra.cql3.ColumnSpecification; -import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.VariableSpecifications; import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.db.marshal.AbstractType; @@ -42,7 +42,7 @@ public final class Marker extends Term.NonTerminal /** * The index of the bind variable within the query options. *

When a query is executed the value of this placeholder is retrieved from the query options values using - * the bindIndex. (see {@link Marker#bind(QueryOptions)}

+ * the bindIndex. (see {@link Term#bind(FunctionContext)}

*/ private final int bindIndex; @@ -72,11 +72,11 @@ public final class Marker extends Term.NonTerminal } @Override - public Term.Terminal bind(QueryOptions options) throws InvalidRequestException + public Term.Terminal bind(FunctionContext context) throws InvalidRequestException { try { - ByteBuffer bytes = options.getValue(bindIndex); + ByteBuffer bytes = context.options().getValues().get(bindIndex); if (bytes == null) return null; @@ -97,11 +97,11 @@ public final class Marker extends Term.NonTerminal // an optimized version without allocating interim Terminal objects @Override - public ByteBuffer bindAndGet(QueryOptions options) + public ByteBuffer bindAndGet(FunctionContext context) { try { - ByteBuffer bytes = options.getValue(bindIndex); + ByteBuffer bytes = context.options().getValue(bindIndex); if (bytes == null) return null; @@ -122,20 +122,20 @@ public final class Marker extends Term.NonTerminal } } - public boolean isByteArrayGetSupported(QueryOptions options) + public boolean isByteArrayGetSupported(FunctionContext context) { - return options.isByteArrayValuesGetSupported(); + return context.options().isByteArrayValuesGetSupported(); } /* Same logic as bind() but it returns byte[] instead of ByteBuffer and there is no Value wrapper usage */ @Override - public byte[] bindAndGetByteArray(QueryOptions options) throws InvalidRequestException + public byte[] bindAndGetByteArray(FunctionContext context) throws InvalidRequestException { try { - byte[] bytes = options.getByteArrayValues()[bindIndex]; + byte[] bytes = context.options().getByteArrayValues()[bindIndex]; if (bytes == null) return null; diff --git a/src/java/org/apache/cassandra/cql3/terms/MultiElements.java b/src/java/org/apache/cassandra/cql3/terms/MultiElements.java index 8fc8833b30..745525f87e 100644 --- a/src/java/org/apache/cassandra/cql3/terms/MultiElements.java +++ b/src/java/org/apache/cassandra/cql3/terms/MultiElements.java @@ -22,7 +22,7 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; -import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.VariableSpecifications; import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.db.marshal.MultiElementType; @@ -126,14 +126,14 @@ public final class MultiElements } @Override - public Terminal bind(QueryOptions options) + public Terminal bind(FunctionContext context) { try { List buffers = new ArrayList<>(elements.size()); for (Term t : elements) { - buffers.add(t.bindAndGet(options)); + buffers.add(t.bindAndGet(context)); } buffers = type.filterSortAndValidateElements(buffers); diff --git a/src/java/org/apache/cassandra/cql3/terms/Sets.java b/src/java/org/apache/cassandra/cql3/terms/Sets.java index 885bf28cdf..de4e7a6950 100644 --- a/src/java/org/apache/cassandra/cql3/terms/Sets.java +++ b/src/java/org/apache/cassandra/cql3/terms/Sets.java @@ -30,9 +30,9 @@ import java.util.stream.StreamSupport; import org.apache.cassandra.cql3.AssignmentTestable; import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.cql3.ColumnSpecification; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.Operation; -import org.apache.cassandra.cql3.QueryOptions; -import org.apache.cassandra.cql3.UpdateParameters; +import org.apache.cassandra.cql3.RowUpdateBuilder; import org.apache.cassandra.db.DecoratedKey; import org.apache.cassandra.db.guardrails.Guardrails; import org.apache.cassandra.db.marshal.AbstractType; @@ -172,7 +172,7 @@ public final class Sets values.add(t); } MultiElements.DelayedValue value = new MultiElements.DelayedValue((MultiElementType) receiver.type.unwrap(), values); - return allTerminal ? value.bind(QueryOptions.DEFAULT) : value; + return allTerminal ? value.bind(FunctionContext.NONE) : value; } private void validateAssignableTo(String keyspace, ColumnSpecification receiver) throws InvalidRequestException @@ -227,16 +227,16 @@ public final class Sets super(column, t); } - public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException + public void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException { - Term.Terminal value = t.bind(params.options); + Term.Terminal value = t.bind(builder); if (value == UNSET_VALUE) return; // delete + add if (column.type.isMultiCell()) - params.setComplexDeletionTimeForOverwrite(column); - Adder.doAdd(value, column, params); + builder.setComplexDeletionTimeForOverwrite(column); + Adder.doAdd(value, column, builder); } } @@ -247,15 +247,15 @@ public final class Sets super(column, t); } - public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException + public void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException { assert column.type.isMultiCell() : "Attempted to add items to a frozen set"; - Term.Terminal value = t.bind(params.options); + Term.Terminal value = t.bind(builder); if (value != UNSET_VALUE) - doAdd(value, column, params); + doAdd(value, column, builder); } - static void doAdd(Term.Terminal value, ColumnMetadata column, UpdateParameters params) throws InvalidRequestException + static void doAdd(Term.Terminal value, ColumnMetadata column, RowUpdateBuilder builder) throws InvalidRequestException { SetType type = (SetType) column.type; @@ -263,7 +263,7 @@ public final class Sets { // for frozen sets, we're overwriting the whole cell if (!type.isMultiCell()) - params.addTombstone(column); + builder.addTombstone(column); return; } @@ -278,7 +278,7 @@ public final class Sets // Guardrails about collection size are only checked for the added elements without considering // already existent elements. This is done so to avoid read-before-write, having additional checks // during SSTable write. - Guardrails.itemsPerCollection.guard(type.collectionSize(elements), column.name.toString(), false, params.clientState); + Guardrails.itemsPerCollection.guard(type.collectionSize(elements), column.name.toString(), false, builder.clientState); int dataSize = 0; for (ByteBuffer bb : elements) @@ -286,16 +286,16 @@ public final class Sets if (bb == ByteBufferUtil.UNSET_BYTE_BUFFER) continue; - Cell cell = params.addCell(column, CellPath.create(bb), ByteBufferUtil.EMPTY_BYTE_BUFFER); + Cell cell = builder.addCell(column, CellPath.create(bb), ByteBufferUtil.EMPTY_BYTE_BUFFER); dataSize += cell.dataSize(); } - Guardrails.collectionSetSize.guard(dataSize, column.name.toString(), false, params.clientState); + Guardrails.collectionSetSize.guard(dataSize, column.name.toString(), false, builder.clientState); } else { - Guardrails.itemsPerCollection.guard(type.collectionSize(elements), column.name.toString(), false, params.clientState); - Cell cell = params.addCell(column, value.get()); - Guardrails.collectionSetSize.guard(cell.dataSize(), column.name.toString(), false, params.clientState); + Guardrails.itemsPerCollection.guard(type.collectionSize(elements), column.name.toString(), false, builder.clientState); + Cell cell = builder.addCell(column, value.get()); + Guardrails.collectionSetSize.guard(cell.dataSize(), column.name.toString(), false, builder.clientState); } } } @@ -308,11 +308,11 @@ public final class Sets super(column, t); } - public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException + public void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException { assert column.type.isMultiCell() : "Attempted to remove items from a frozen set"; - Term.Terminal value = t.bind(params.options); + Term.Terminal value = t.bind(builder); if (value == null || value == UNSET_VALUE) return; @@ -320,7 +320,7 @@ public final class Sets List toDiscard = value.getElements(); for (ByteBuffer bb : toDiscard) - params.addTombstone(column, CellPath.create(bb)); + builder.addTombstone(column, CellPath.create(bb)); } } @@ -331,14 +331,14 @@ public final class Sets super(column, k); } - public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException + public void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException { assert column.type.isMultiCell() : "Attempted to delete a single element in a frozen set"; - Term.Terminal elt = t.bind(params.options); + Term.Terminal elt = t.bind(builder); if (elt == null) throw new InvalidRequestException("Invalid null set element"); - params.addTombstone(column, CellPath.create(elt.get())); + builder.addTombstone(column, CellPath.create(elt.get())); } } } diff --git a/src/java/org/apache/cassandra/cql3/terms/Term.java b/src/java/org/apache/cassandra/cql3/terms/Term.java index 64912fe0cd..979e8050fa 100644 --- a/src/java/org/apache/cassandra/cql3/terms/Term.java +++ b/src/java/org/apache/cassandra/cql3/terms/Term.java @@ -27,6 +27,7 @@ import org.apache.cassandra.cql3.CQLFragmentParser; import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.cql3.ColumnSpecification; import org.apache.cassandra.cql3.CqlParser; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.VariableSpecifications; import org.apache.cassandra.cql3.functions.Function; @@ -77,10 +78,10 @@ public interface Term * Bind the values in this term to the values contained in the {@code options}. * This is obviously a no-op if the term is Terminal. * - * @param options the values to bind markers to. + * @param context the values to bind markers to. * @return the {@code Terminal} resulting of binding the values contained in the {@code options}. */ - Terminal bind(QueryOptions options); + Terminal bind(FunctionContext context); /** * A shorter for {@code bind(options).get()}. @@ -88,13 +89,13 @@ public interface Term * object between the bind and the get (note that we still want to be able * to separate bind and get for collections). */ - ByteBuffer bindAndGet(QueryOptions options); + ByteBuffer bindAndGet(FunctionContext context); /** * return true if an optimized bindAndGetByteArray() method is implemented and can be used for this type of Term * to retrieve a value as byte[] instead of default ByteBuffer provided by bindAndGet() */ - default boolean isByteArrayGetSupported(QueryOptions options) + default boolean isByteArrayGetSupported(FunctionContext context) { return false; } @@ -102,7 +103,7 @@ public interface Term /** * an allocation-optimized version of bindAndGet() method */ - default byte[] bindAndGetByteArray(QueryOptions options) throws InvalidRequestException + default byte[] bindAndGetByteArray(FunctionContext context) throws InvalidRequestException { throw new IllegalStateException("bindAndGetByteArray() method is not implemented, " + "isByteArrayGetSupported() must be always checked before invoking this method"); @@ -113,7 +114,7 @@ public interface Term * We expose it mainly because for constants it can avoid allocating a temporary * object between the bind and the getElements. */ - List bindAndGetElements(QueryOptions options); + List bindAndGetElements(FunctionContext context); /** * Whether that term contains at least one bind marker. @@ -250,7 +251,7 @@ public interface Term public void collectMarkerSpecification(VariableSpecifications boundNames, Object owner) {} @Override - public Terminal bind(QueryOptions options) { return this; } + public Terminal bind(FunctionContext context) { return this; } @Override public void addFunctionsTo(List functions) @@ -291,13 +292,13 @@ public interface Term } @Override - public ByteBuffer bindAndGet(QueryOptions options) + public ByteBuffer bindAndGet(FunctionContext context) { return get(); } @Override - public List bindAndGetElements(QueryOptions options) + public List bindAndGetElements(FunctionContext context) { return getElements(); } @@ -318,16 +319,16 @@ public interface Term abstract class NonTerminal implements Term { @Override - public ByteBuffer bindAndGet(QueryOptions options) throws InvalidRequestException + public ByteBuffer bindAndGet(FunctionContext context) throws InvalidRequestException { - Terminal t = bind(options); + Terminal t = bind(context); return t == null ? null : t.get(); } @Override - public List bindAndGetElements(QueryOptions options) + public List bindAndGetElements(FunctionContext context) { - Terminal t = bind(options); + Terminal t = bind(context); return t == null ? Collections.emptyList() : t.getElements(); } } diff --git a/src/java/org/apache/cassandra/cql3/terms/Terms.java b/src/java/org/apache/cassandra/cql3/terms/Terms.java index 9936089d9b..8965e937a2 100644 --- a/src/java/org/apache/cassandra/cql3/terms/Terms.java +++ b/src/java/org/apache/cassandra/cql3/terms/Terms.java @@ -28,7 +28,7 @@ import com.google.common.collect.ImmutableList; import org.apache.cassandra.cql3.AssignmentTestable; import org.apache.cassandra.cql3.ColumnSpecification; -import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.VariableSpecifications; import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.cql3.terms.Term.NonTerminal; @@ -104,25 +104,26 @@ public interface Terms * Bind the values in these terms to the values contained in {@code options}. * This is obviously a no-op if this {@code Terms} are Terminals. * - * @param options the query options containing the values to bind markers to. + * @param context the query options containing the values to bind markers to. * @return the result of binding all the variables of these NonTerminals. */ - Terminals bind(QueryOptions options); + Terminals bind(FunctionContext context); /** * A shorter for {@code bind(options).get()}. * We expose it mainly because for constants it can avoid allocating a temporary * object between the bind and the get. - * @param options the query options containing the values to bind markers to. + * + * @param context the query options containing the values to bind markers to. */ - List bindAndGet(QueryOptions options); + List bindAndGet(FunctionContext context); - default boolean isSingleTerm(QueryOptions options) + default boolean isSingleTerm(FunctionContext context) { return false; } - default ByteBuffer bindAndGetSingleTermValue(QueryOptions options) + default ByteBuffer bindAndGetSingleTermValue(FunctionContext context) { throw new IllegalStateException("bindAndGetSingleTermValue() method is not implemented, " + "isSingleTerm() must be always checked before invoking this method"); @@ -132,9 +133,10 @@ public interface Terms * A shorter for {@code bind(options).getElements()}. * We expose it mainly because for constants it can avoid allocating a temporary * object between the {@code bind} and the {@code getElements}. - * @param options the query options containing the values to bind markers to. + * + * @param context the query options containing the values to bind markers to. */ - List> bindAndGetElements(QueryOptions options); + List> bindAndGetElements(FunctionContext context); /** * Creates a {@code Terms} containing a single {@code Term}. @@ -457,19 +459,19 @@ public interface Terms public void collectMarkerSpecification(VariableSpecifications boundNames, Object owner) {} @Override - public final Terminals bind(QueryOptions options) + public final Terminals bind(FunctionContext context) { return this; } @Override - public List bindAndGet(QueryOptions options) + public List bindAndGet(FunctionContext context) { return get(); } @Override - public List> bindAndGetElements(QueryOptions options) + public List> bindAndGetElements(FunctionContext context) { return getElements(); } @@ -628,34 +630,34 @@ public interface Terms } @Override - public Terminals bind(QueryOptions options) + public Terminals bind(FunctionContext context) { - return Terminals.of(term.bind(options)); + return Terminals.of(term.bind(context)); } @Override - public List bindAndGet(QueryOptions options) + public List bindAndGet(FunctionContext context) { - return Collections.singletonList(term.bindAndGet(options)); + return Collections.singletonList(term.bindAndGet(context)); } @Override - public boolean isSingleTerm(QueryOptions options) + public boolean isSingleTerm(FunctionContext context) { return true; } @Override - public ByteBuffer bindAndGetSingleTermValue(QueryOptions options) + public ByteBuffer bindAndGetSingleTermValue(FunctionContext context) { - return term.bindAndGet(options); + return term.bindAndGet(context); } @Override - public List> bindAndGetElements(QueryOptions options) + public List> bindAndGetElements(FunctionContext context) { - return Collections.singletonList(term.bindAndGetElements(options)); + return Collections.singletonList(term.bindAndGetElements(context)); } @Override @@ -698,40 +700,40 @@ public interface Terms } @Override - public Terminals bind(QueryOptions options) + public Terminals bind(FunctionContext context) { int size = terms.size(); List terminals = new ArrayList<>(size); for (int i = 0; i < size; i++) { Term term = terms.get(i); - terminals.add(term.bind(options)); + terminals.add(term.bind(context)); } return Terminals.of(terminals); } @Override - public List bindAndGet(QueryOptions options) + public List bindAndGet(FunctionContext context) { int size = terms.size(); List buffers = new ArrayList<>(size); for (int i = 0; i < size; i++) { Term term = terms.get(i); - buffers.add(term.bindAndGet(options)); + buffers.add(term.bindAndGet(context)); } return buffers; } @Override - public List> bindAndGetElements(QueryOptions options) + public List> bindAndGetElements(FunctionContext context) { int size = terms.size(); List> buffers = new ArrayList<>(size); for (int i = 0; i < size; i++) { Term term = terms.get(i); - buffers.add(term.bindAndGetElements(options)); + buffers.add(term.bindAndGetElements(context)); } return buffers; } diff --git a/src/java/org/apache/cassandra/cql3/terms/Tuples.java b/src/java/org/apache/cassandra/cql3/terms/Tuples.java index f9ba523567..c5e9b62e53 100644 --- a/src/java/org/apache/cassandra/cql3/terms/Tuples.java +++ b/src/java/org/apache/cassandra/cql3/terms/Tuples.java @@ -25,7 +25,7 @@ import java.util.stream.StreamSupport; import org.apache.cassandra.cql3.AssignmentTestable; import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.cql3.ColumnSpecification; -import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.ReversedType; import org.apache.cassandra.db.marshal.TupleType; @@ -88,7 +88,7 @@ public final class Tuples } MultiElements.DelayedValue value = new MultiElements.DelayedValue(tupleType, values); - return allTerminal ? value.bind(QueryOptions.DEFAULT) : value; + return allTerminal ? value.bind(FunctionContext.NONE) : value; } public TestResult testAssignment(String keyspace, ColumnSpecification receiver) diff --git a/src/java/org/apache/cassandra/cql3/terms/UserTypes.java b/src/java/org/apache/cassandra/cql3/terms/UserTypes.java index d43a000736..af2c28f132 100644 --- a/src/java/org/apache/cassandra/cql3/terms/UserTypes.java +++ b/src/java/org/apache/cassandra/cql3/terms/UserTypes.java @@ -28,9 +28,9 @@ import org.apache.cassandra.cql3.AssignmentTestable; import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.cql3.ColumnSpecification; import org.apache.cassandra.cql3.FieldIdentifier; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.Operation; -import org.apache.cassandra.cql3.QueryOptions; -import org.apache.cassandra.cql3.UpdateParameters; +import org.apache.cassandra.cql3.RowUpdateBuilder; import org.apache.cassandra.db.DecoratedKey; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.UserType; @@ -170,7 +170,7 @@ public final class UserTypes } MultiElements.DelayedValue value = new MultiElements.DelayedValue(((UserType)receiver.type.unwrap()), values); - return allTerminal ? value.bind(QueryOptions.DEFAULT) : value; + return allTerminal ? value.bind(FunctionContext.NONE) : value; } private void validateAssignableTo(String keyspace, ColumnSpecification receiver) throws InvalidRequestException @@ -220,9 +220,9 @@ public final class UserTypes super(column, t); } - public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException + public void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException { - Term.Terminal value = t.bind(params.options); + Term.Terminal value = t.bind(builder); if (value == UNSET_VALUE) return; @@ -230,7 +230,7 @@ public final class UserTypes if (type.isMultiCell()) { // setting a whole UDT at once means we overwrite all cells, so delete existing cells - params.setComplexDeletionTimeForOverwrite(column); + builder.setComplexDeletionTimeForOverwrite(column); if (value == null) return; @@ -243,16 +243,16 @@ public final class UserTypes continue; CellPath fieldPath = type.cellPathForField(fieldName); - params.addCell(column, fieldPath, buffer); + builder.addCell(column, fieldPath, buffer); } } else { // for frozen UDTs, we're overwriting the whole cell value if (value == null) - params.addTombstone(column); + builder.addTombstone(column); else - params.addCell(column, value.get()); + builder.addCell(column, value.get()); } } } @@ -267,20 +267,20 @@ public final class UserTypes this.field = field; } - public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException + public void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException { // we should not get here for frozen UDTs assert column.type.isMultiCell() : "Attempted to set an individual field on a frozen UDT"; - Term.Terminal value = t.bind(params.options); + Term.Terminal value = t.bind(builder); if (value == UNSET_VALUE) return; CellPath fieldPath = ((UserType) column.type).cellPathForField(field); if (value == null) - params.addTombstone(column, fieldPath); + builder.addTombstone(column, fieldPath); else - params.addCell(column, fieldPath, value.get()); + builder.addCell(column, fieldPath, value.get()); } } @@ -294,13 +294,13 @@ public final class UserTypes this.field = field; } - public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException + public void execute(DecoratedKey partitionKey, RowUpdateBuilder builder) throws InvalidRequestException { // we should not get here for frozen UDTs assert column.type.isMultiCell() : "Attempted to delete a single field from a frozen UDT"; CellPath fieldPath = ((UserType) column.type).cellPathForField(field); - params.addTombstone(column, fieldPath); + builder.addTombstone(column, fieldPath); } } } diff --git a/src/java/org/apache/cassandra/cql3/terms/Vectors.java b/src/java/org/apache/cassandra/cql3/terms/Vectors.java index 15ba9208b8..6419793efa 100644 --- a/src/java/org/apache/cassandra/cql3/terms/Vectors.java +++ b/src/java/org/apache/cassandra/cql3/terms/Vectors.java @@ -28,7 +28,7 @@ import java.util.stream.Collectors; import org.apache.cassandra.cql3.AssignmentTestable; import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.cql3.ColumnSpecification; -import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.VectorType; import org.apache.cassandra.exceptions.InvalidRequestException; @@ -139,7 +139,7 @@ public final class Vectors values.add(t); } MultiElements.DelayedValue value = new MultiElements.DelayedValue(type, values); - return allTerminal ? value.bind(QueryOptions.DEFAULT) : value; + return allTerminal ? value.bind(FunctionContext.NONE) : value; } @Override diff --git a/src/java/org/apache/cassandra/cql3/transactions/ReferenceValue.java b/src/java/org/apache/cassandra/cql3/transactions/ReferenceValue.java index 3f81b785d4..5d4fb64e97 100644 --- a/src/java/org/apache/cassandra/cql3/transactions/ReferenceValue.java +++ b/src/java/org/apache/cassandra/cql3/transactions/ReferenceValue.java @@ -19,7 +19,7 @@ package org.apache.cassandra.cql3.transactions; import org.apache.cassandra.cql3.ColumnSpecification; -import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.VariableSpecifications; import org.apache.cassandra.cql3.terms.Term; import org.apache.cassandra.db.marshal.AbstractType; @@ -31,7 +31,7 @@ import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue; public abstract class ReferenceValue { - public abstract TxnReferenceValue bindAndGet(QueryOptions options); + public abstract TxnReferenceValue bindAndGet(FunctionContext context); public static abstract class Raw extends Term.Raw { @@ -48,9 +48,9 @@ public abstract class ReferenceValue } @Override - public TxnReferenceValue bindAndGet(QueryOptions options) + public TxnReferenceValue bindAndGet(FunctionContext context) { - return new TxnReferenceValue.Constant(term.bindAndGet(options)); + return new TxnReferenceValue.Constant(term.bindAndGet(context)); } public static class Raw extends ReferenceValue.Raw @@ -104,9 +104,9 @@ public abstract class ReferenceValue } @Override - public TxnReferenceValue bindAndGet(QueryOptions options) + public TxnReferenceValue bindAndGet(FunctionContext context) { - return new TxnReferenceValue.Substitution(reference.toTxnReference(options).asColumn()); + return new TxnReferenceValue.Substitution(reference.toTxnReference(context).asColumn()); } public static class Raw extends ReferenceValue.Raw diff --git a/src/java/org/apache/cassandra/cql3/transactions/RowDataReference.java b/src/java/org/apache/cassandra/cql3/transactions/RowDataReference.java index 21d6c442eb..0d37643934 100644 --- a/src/java/org/apache/cassandra/cql3/transactions/RowDataReference.java +++ b/src/java/org/apache/cassandra/cql3/transactions/RowDataReference.java @@ -27,7 +27,7 @@ import org.apache.cassandra.cql3.AssignmentTestable; import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.cql3.ColumnSpecification; import org.apache.cassandra.cql3.FieldIdentifier; -import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.VariableSpecifications; import org.apache.cassandra.cql3.functions.Function; import org.apache.cassandra.cql3.functions.types.utils.Bytes; @@ -84,7 +84,7 @@ public class RowDataReference extends Term.NonTerminal } @Override - public Terminal bind(QueryOptions options) throws InvalidRequestException + public Terminal bind(FunctionContext context) throws InvalidRequestException { throw new UnsupportedOperationException(); } @@ -174,20 +174,20 @@ public class RowDataReference extends Term.NonTerminal return UserTypes.fieldSpecOf(column, field); } - private CellPath bindCellPath(QueryOptions options) + private CellPath bindCellPath(FunctionContext context) { if (fieldPath != null) return fieldPath; - return elementPath != null ? CellPath.create(elementPath.bindAndGet(options)) : null; + return elementPath != null ? CellPath.create(elementPath.bindAndGet(context)) : null; } - public TxnReference toTxnReference(QueryOptions options) + public TxnReference toTxnReference(FunctionContext context) { Preconditions.checkState(elementPath == null || column.isComplex() || column.type.isFrozenCollection()); Preconditions.checkState(fieldPath == null || column.isComplex() || column.type.isUDT()); - return TxnReference.columnOrRow(txnDataName, table, column, bindCellPath(options)); + return TxnReference.columnOrRow(txnDataName, table, column, bindCellPath(context)); } public ColumnIdentifier getFullyQualifiedName() diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java index 03fbf9020a..05a1163faf 100644 --- a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java +++ b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java @@ -177,6 +177,7 @@ import org.apache.cassandra.utils.concurrent.UncheckedInterruptedException; import static org.apache.cassandra.concurrent.ExecutorFactory.Global.executorFactory; import static org.apache.cassandra.config.DatabaseDescriptor.getFlushWriters; import static org.apache.cassandra.db.commitlog.CommitLogPosition.NONE; +import static org.apache.cassandra.db.compaction.CompactionManager.NO_GC; import static org.apache.cassandra.utils.Clock.Global.currentTimeMillis; import static org.apache.cassandra.utils.Clock.Global.nanoTime; import static org.apache.cassandra.utils.Throwables.maybeFail; @@ -2011,9 +2012,19 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean, Memtable.Owner return paxosRepairHistory.get().getBallotForToken(key.getToken()); } + public long getDefaultGcBefore(long nowInSec) + { + // 2ndary indexes have ExpiringColumns too, so we need to purge tombstones deleted before now. We do not need to + // add any GcGrace however since 2ndary indexes are local to a node. + return isIndex() ? nowInSec : gcBefore(nowInSec); + } + public long gcBefore(long nowInSec) { - return nowInSec - metadata().params.gcGraceSeconds; + TableMetadata metadata = metadata(); + if (metadata.isAccordEnabled()) + return NO_GC; + return nowInSec - metadata.params.gcGraceSeconds; } public RefViewFragment selectAndReference(Function> filter) diff --git a/src/java/org/apache/cassandra/db/ReadCommand.java b/src/java/org/apache/cassandra/db/ReadCommand.java index e88a6d1c27..c81f9ab57f 100644 --- a/src/java/org/apache/cassandra/db/ReadCommand.java +++ b/src/java/org/apache/cassandra/db/ReadCommand.java @@ -961,6 +961,9 @@ public abstract class ReadCommand extends AbstractReadQuery ColumnFamilyStore cfs, ReadExecutionController controller) { + if (nowInSec() == 0) + return iterator; + class WithoutPurgeableTombstones extends PurgeFunction { public WithoutPurgeableTombstones() diff --git a/src/java/org/apache/cassandra/db/aggregation/GroupMaker.java b/src/java/org/apache/cassandra/db/aggregation/GroupMaker.java index 21bb438bee..9701e35fdf 100644 --- a/src/java/org/apache/cassandra/db/aggregation/GroupMaker.java +++ b/src/java/org/apache/cassandra/db/aggregation/GroupMaker.java @@ -224,7 +224,8 @@ public abstract class GroupMaker { input.add(argument); - // For computing groups we do not need to use the client protocol version. + // For computing groups we do not need to use the specific client protocol version. + selector.prepare(ProtocolVersion.CURRENT); selector.addInput(input); ByteBuffer output = selector.getOutput(ProtocolVersion.CURRENT); selector.reset(); diff --git a/src/java/org/apache/cassandra/db/compaction/CompactionIterator.java b/src/java/org/apache/cassandra/db/compaction/CompactionIterator.java index 35739ec1bc..d0ad79cea3 100644 --- a/src/java/org/apache/cassandra/db/compaction/CompactionIterator.java +++ b/src/java/org/apache/cassandra/db/compaction/CompactionIterator.java @@ -195,7 +195,7 @@ public class CompactionIterator extends CompactionInfo.Holder implements Unfilte private static IAccordService accord(AbstractCompactionController controller) { IAccordService accord = AccordService.tryGetUnsafe(); - Invariants.require(accord != null || (!isAccordJournal(controller.cfs) && !isAccordCommandsForKey(controller.cfs))); + Invariants.require(accord != null || !isAccordSystemTable(controller.cfs.metadata())); return accord; } @@ -261,19 +261,28 @@ public class CompactionIterator extends CompactionInfo.Holder implements Unfilte private Transformation purger(ColumnFamilyStore cfs, Supplier compactionInfos, Supplier version) { - if (isPaxos(cfs) && paxosStatePurging() != legacy) + TableMetadata metadata = cfs.metadata.get(); + + if (isPaxos(metadata) && paxosStatePurging() != legacy) return new PaxosPurger(); // Topologies uses regular deletion so it can use a regular Purger - if (!requiresAccordSpecificPurger(cfs)) - return new Purger(controller, nowInSec); + if (isAccordSystemTable(metadata)) + { + if (isAccordJournal(metadata)) + return new AccordJournalPurger(compactionInfos.get(), version.get(), cfs); + if (isAccordCommandsForKey(metadata)) + return new AccordCommandsForKeyPurger(AccordKeyspace.CFKAccessor, compactionInfos); - if (isAccordJournal(cfs)) - return new AccordJournalPurger(compactionInfos.get(), version.get(), cfs); - if (isAccordCommandsForKey(cfs)) - return new AccordCommandsForKeyPurger(AccordKeyspace.CFKAccessor, compactionInfos); + // at time of writing there are no other accord system tables, + // but unless otherwise stated additional tables should be treated as normal + } - throw new IllegalArgumentException("Unhandled accord table: " + cfs.keyspace.getName() + '.' + cfs.name); + long nowInSec = this.nowInSec; + if (metadata.isAccordEnabled() || metadata.migratingFromAccord()) + nowInSec = controller.gcBefore; + + return new Purger(controller, nowInSec); } public TableMetadata metadata() @@ -907,7 +916,7 @@ public class CompactionIterator extends CompactionInfo.Holder implements Unfilte switch (key.type) { case COMMAND_DIFF: - compactor = new AccordCommandRowCompactor(infos, userVersion, nowInSec); + compactor = new AccordCommandRowCompactor(infos, userVersion); break; case TOPOLOGY_UPDATE: compactor = new TopologyCompactor(userVersion, infos.minEpoch); @@ -1112,20 +1121,18 @@ public class CompactionIterator extends CompactionInfo.Holder implements Unfilte final AccordCompactionInfos infos; final Version userVersion; final ColumnData userVersionCell; - final long nowInSec; final CommandChanges mainBuilder = new CommandChanges(); final List entries = new ArrayList<>(); final ArrayDeque reuseEntries = new ArrayDeque<>(); AccordCompactionInfo info; - AccordCommandRowCompactor(AccordCompactionInfos infos, Version userVersion, long nowInSec) + AccordCommandRowCompactor(AccordCompactionInfos infos, Version userVersion) { super((CommandChangeSerializer) JournalKey.Type.COMMAND_DIFF.serializer); this.infos = infos; this.userVersion = userVersion; this.userVersionCell = BufferCell.live(AccordKeyspace.JournalColumns.user_version, timestamp, Int32Type.instance.decompose(userVersion.version)); - this.nowInSec = nowInSec; } @Override @@ -1258,30 +1265,23 @@ public class CompactionIterator extends CompactionInfo.Holder implements Unfilte } } - private static boolean isPaxos(ColumnFamilyStore cfs) + private static boolean isPaxos(TableMetadata metadata) { - return cfs.name.equals(SystemKeyspace.PAXOS) && cfs.getKeyspaceName().equals(SchemaConstants.SYSTEM_KEYSPACE_NAME); + return metadata.name.equals(SystemKeyspace.PAXOS) && metadata.keyspace.equals(SchemaConstants.SYSTEM_KEYSPACE_NAME); } - private static boolean requiresAccordSpecificPurger(ColumnFamilyStore cfs) + private static boolean isAccordSystemTable(TableMetadata metadata) { - return cfs.getKeyspaceName().equals(SchemaConstants.ACCORD_KEYSPACE_NAME) && - (cfs.getTableName().contains(AccordKeyspace.JOURNAL) || - AccordKeyspace.COMMANDS_FOR_KEY.equals(cfs.getTableName())); + return metadata.keyspace.equals(SchemaConstants.ACCORD_KEYSPACE_NAME); } - private static boolean isAccordTable(ColumnFamilyStore cfs, String name) + private static boolean isAccordJournal(TableMetadata metadata) { - return cfs.name.equals(name) && cfs.getKeyspaceName().equals(SchemaConstants.ACCORD_KEYSPACE_NAME); + return metadata.name.startsWith(AccordKeyspace.JOURNAL) && metadata.keyspace.equals(SchemaConstants.ACCORD_KEYSPACE_NAME); } - private static boolean isAccordJournal(ColumnFamilyStore cfs) + private static boolean isAccordCommandsForKey(TableMetadata metadata) { - return cfs.getKeyspaceName().equals(SchemaConstants.ACCORD_KEYSPACE_NAME) && cfs.name.startsWith(AccordKeyspace.JOURNAL); - } - - private static boolean isAccordCommandsForKey(ColumnFamilyStore cfs) - { - return isAccordTable(cfs, AccordKeyspace.COMMANDS_FOR_KEY); + return metadata.name.equals(AccordKeyspace.COMMANDS_FOR_KEY) && metadata.keyspace.equals(SchemaConstants.ACCORD_KEYSPACE_NAME); } } diff --git a/src/java/org/apache/cassandra/db/compaction/CompactionManager.java b/src/java/org/apache/cassandra/db/compaction/CompactionManager.java index ea370b4601..84c1193517 100644 --- a/src/java/org/apache/cassandra/db/compaction/CompactionManager.java +++ b/src/java/org/apache/cassandra/db/compaction/CompactionManager.java @@ -162,7 +162,6 @@ public class CompactionManager implements CompactionManagerMBean, ICompactionMan public final AtomicInteger currentlyBackgroundUpgrading = new AtomicInteger(0); public static final int NO_GC = Integer.MIN_VALUE; - public static final int GC_ALL = Integer.MAX_VALUE; static { @@ -395,7 +394,7 @@ public class CompactionManager implements CompactionManagerMBean, ICompactionMan } CompactionStrategyManager strategy = cfs.getCompactionStrategyManager(); - tasks = strategy.getNextBackgroundTasks(getDefaultGcBefore(cfs, FBUtilities.nowInSeconds())); + tasks = strategy.getNextBackgroundTasks(cfs.getDefaultGcBefore(FBUtilities.nowInSeconds())); if (tasks == null || tasks.isEmpty()) { if (DatabaseDescriptor.automaticSSTableUpgrade()) @@ -885,10 +884,10 @@ public class CompactionManager implements CompactionManagerMBean, ICompactionMan public void execute(LifecycleTransaction txn) throws IOException { logger.debug("Garbage collecting {}", txn.originals()); - CompactionTask task = new CompactionTask(cfStore, txn, getDefaultGcBefore(cfStore, FBUtilities.nowInSeconds())) + CompactionTask task = new CompactionTask(cfStore, txn, cfStore.getDefaultGcBefore(FBUtilities.nowInSeconds())) { @Override - protected CompactionController getCompactionController(Set toCompact) + protected CompactionController getCompactionController(Set toCompact, long gcBefore) { return new CompactionController(cfStore, toCompact, gcBefore, null, tombstoneOption); } @@ -1153,12 +1152,12 @@ public class CompactionManager implements CompactionManagerMBean, ICompactionMan public void performMaximal(final ColumnFamilyStore cfStore, boolean splitOutput, int permittedParallelism) { - FBUtilities.waitOnFutures(submitMaximal(cfStore, getDefaultGcBefore(cfStore, FBUtilities.nowInSeconds()), splitOutput, permittedParallelism)); + FBUtilities.waitOnFutures(submitMaximal(cfStore, cfStore.getDefaultGcBefore(FBUtilities.nowInSeconds()), splitOutput, permittedParallelism)); } public List> submitMaximal(final ColumnFamilyStore cfStore, boolean splitOutput, int permittedParallelism) { - return submitMaximal(cfStore, getDefaultGcBefore(cfStore, FBUtilities.nowInSeconds()), splitOutput, permittedParallelism); + return submitMaximal(cfStore, cfStore.getDefaultGcBefore(FBUtilities.nowInSeconds()), splitOutput, permittedParallelism); } public List> submitMaximal(final ColumnFamilyStore cfStore, final long gcBefore, boolean splitOutput, int permittedParallelism) @@ -1218,7 +1217,7 @@ public class CompactionManager implements CompactionManagerMBean, ICompactionMan logger.debug("No sstables found for the provided token range"); return CompactionTasks.empty(); } - return cfStore.getCompactionStrategyManager().getUserDefinedTasks(sstables, getDefaultGcBefore(cfStore, FBUtilities.nowInSeconds())); + return cfStore.getCompactionStrategyManager().getUserDefinedTasks(sstables, cfStore.getDefaultGcBefore(FBUtilities.nowInSeconds())); }; try (CompactionTasks tasks = cfStore.runWithCompactionsDisabled(taskCreator, @@ -1354,7 +1353,7 @@ public class CompactionManager implements CompactionManagerMBean, ICompactionMan List> futures = new ArrayList<>(descriptors.size()); long nowInSec = FBUtilities.nowInSeconds(); for (ColumnFamilyStore cfs : descriptors.keySet()) - futures.add(submitUserDefined(cfs, descriptors.get(cfs), getDefaultGcBefore(cfs, nowInSec))); + futures.add(submitUserDefined(cfs, descriptors.get(cfs), cfs.getDefaultGcBefore(nowInSec))); FBUtilities.waitOnFutures(futures); } @@ -1645,7 +1644,7 @@ public class CompactionManager implements CompactionManagerMBean, ICompactionMan long nowInSec = FBUtilities.nowInSeconds(); try (SSTableRewriter writer = SSTableRewriter.construct(cfs, txn, false, sstable.maxDataAge); ISSTableScanner scanner = cleanupStrategy.getScanner(sstable); - CompactionController controller = new CompactionController(cfs, txn.originals(), getDefaultGcBefore(cfs, nowInSec)); + CompactionController controller = new CompactionController(cfs, txn.originals(), cfs.getDefaultGcBefore(nowInSec)); Refs refs = Refs.ref(Collections.singleton(sstable)); CompactionIterator ci = new CompactionIterator(OperationType.CLEANUP, Collections.singletonList(scanner), controller, nowInSec, nextTimeUUID(), active, null)) { @@ -1992,7 +1991,7 @@ public class CompactionManager implements CompactionManagerMBean, ICompactionMan SSTableRewriter unrepairedWriter = SSTableRewriter.constructWithoutEarlyOpening(sharedTxn, false, groupMaxDataAge); AbstractCompactionStrategy.ScannerList scanners = strategy.getScanners(txn.originals()); - CompactionController controller = new CompactionController(cfs, sstableAsSet, getDefaultGcBefore(cfs, nowInSec)); + CompactionController controller = new CompactionController(cfs, sstableAsSet, cfs.getDefaultGcBefore(nowInSec)); CompactionIterator ci = getAntiCompactionIterator(scanners.scanners, controller, nowInSec, nextTimeUUID(), active, isCancelled)) { int expectedBloomFilterSize = Math.max(cfs.metadata().params.minIndexInterval, (int)(SSTableReader.getApproximateKeyCount(sstableAsSet))); @@ -2178,13 +2177,6 @@ public class CompactionManager implements CompactionManagerMBean, ICompactionMan } } - public static long getDefaultGcBefore(ColumnFamilyStore cfs, long nowInSec) - { - // 2ndary indexes have ExpiringColumns too, so we need to purge tombstones deleted before now. We do not need to - // add any GcGrace however since 2ndary indexes are local to a node. - return cfs.isIndex() ? nowInSec : cfs.gcBefore(nowInSec); - } - public Future submitViewBuilder(final ViewBuilderTask task) { return submitViewBuilder(task, active); diff --git a/src/java/org/apache/cassandra/db/compaction/CompactionTask.java b/src/java/org/apache/cassandra/db/compaction/CompactionTask.java index bf62db23f5..c2e34d9b34 100644 --- a/src/java/org/apache/cassandra/db/compaction/CompactionTask.java +++ b/src/java/org/apache/cassandra/db/compaction/CompactionTask.java @@ -35,6 +35,12 @@ import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import accord.local.CommandStores; +import accord.local.CommandStores.StoreFinder; +import accord.local.Node; +import accord.primitives.Ranges; +import accord.utils.Invariants; + import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.Directories; @@ -55,7 +61,11 @@ import org.apache.cassandra.io.sstable.ISSTableScanner; import org.apache.cassandra.io.sstable.format.SSTableReader; import org.apache.cassandra.io.sstable.metadata.MetadataCollector; import org.apache.cassandra.io.util.File; +import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.service.ActiveRepairService; +import org.apache.cassandra.service.accord.AccordService; +import org.apache.cassandra.service.accord.TokenRange; +import org.apache.cassandra.service.accord.api.TokenKey; import org.apache.cassandra.service.snapshot.SnapshotManager; import org.apache.cassandra.service.snapshot.SnapshotOptions; import org.apache.cassandra.service.snapshot.SnapshotType; @@ -63,6 +73,8 @@ import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.TimeUUID; import org.apache.cassandra.utils.concurrent.Refs; +import static accord.local.RedundantStatus.Property.LOCALLY_APPLIED; +import static org.apache.cassandra.db.compaction.CompactionManager.NO_GC; import static org.apache.cassandra.utils.Clock.Global.currentTimeMillis; import static org.apache.cassandra.utils.Clock.Global.nanoTime; @@ -71,20 +83,20 @@ public class CompactionTask extends AbstractCompactionTask private static final int MEGABYTE = 1024 * 1024; protected static final Logger logger = LoggerFactory.getLogger(CompactionTask.class); - protected final long gcBefore; + protected final long gcBeforeSeconds; protected final boolean keepOriginals; protected static long totalBytesCompacted = 0; private ActiveCompactionsTracker activeCompactions; - public CompactionTask(ColumnFamilyStore cfs, ILifecycleTransaction txn, long gcBefore) + public CompactionTask(ColumnFamilyStore cfs, ILifecycleTransaction txn, long gcBeforeSeconds) { - this(cfs, txn, gcBefore, false); + this(cfs, txn, gcBeforeSeconds, false); } - public CompactionTask(ColumnFamilyStore cfs, ILifecycleTransaction txn, long gcBefore, boolean keepOriginals) + public CompactionTask(ColumnFamilyStore cfs, ILifecycleTransaction txn, long gcBeforeSeconds, boolean keepOriginals) { super(cfs, txn); - this.gcBefore = gcBefore; + this.gcBeforeSeconds = gcBeforeSeconds; this.keepOriginals = keepOriginals; } @@ -546,7 +558,33 @@ public class CompactionTask extends AbstractCompactionTask return 0; } - protected CompactionController getCompactionController(Set toCompact) + protected final CompactionController getCompactionController(Set toCompact) + { + long gcBeforeSeconds = this.gcBeforeSeconds; + TableMetadata metadata = cfs.metadata(); + if (metadata.isAccordEnabled()) + { + Invariants.expect(gcBeforeSeconds <= 0); + TokenRange[] rs = new TokenRange[toCompact.size()]; + int i = 0; + for (SSTableReader reader : toCompact) + rs[i++] = TokenRange.create(TokenKey.before(metadata.id, reader.getFirst().getToken()), TokenKey.after(metadata.id, reader.getLast().getToken())); + Ranges ranges = Ranges.of(rs); + Node node = AccordService.unsafeInstance().node(); + // TODO (expected): we shouldn't need to limit to universalBefore, but this is a safety margin esp. against bugs with repairing tombstones or incomplete information in CommandStore; + // we should impose stronger guarantees on incoming streams (that they don't contain tombstones), and validate our CommandStore-derived bounds + gcBeforeSeconds = node.durableBefore().foldlWithDefault(ranges, (e, v) -> e == null ? NO_GC : Math.min(e.universal.hlc(), v), null, Long.MAX_VALUE); + CommandStores.StoreSelector selector = StoreFinder.selector(ranges, Long.MIN_VALUE, Long.MAX_VALUE); + gcBeforeSeconds = node.commandStores().mapReduceUnsafe(selector, commandStore -> commandStore.unsafeGetRedundantBefore().foldl(ranges, (bs, v) -> bs.maxBound(LOCALLY_APPLIED).hlc(), Long.MAX_VALUE), Math::min, gcBeforeSeconds); + if (gcBeforeSeconds == Long.MAX_VALUE) + gcBeforeSeconds = NO_GC; + else + gcBeforeSeconds = TimeUnit.MICROSECONDS.toSeconds(gcBeforeSeconds); + } + return getCompactionController(toCompact, gcBeforeSeconds); + } + + protected CompactionController getCompactionController(Set toCompact, long gcBefore) { return new CompactionController(cfs, toCompact, gcBefore); } diff --git a/src/java/org/apache/cassandra/db/compaction/SSTableSplitter.java b/src/java/org/apache/cassandra/db/compaction/SSTableSplitter.java index d13b11f99b..47d947b776 100644 --- a/src/java/org/apache/cassandra/db/compaction/SSTableSplitter.java +++ b/src/java/org/apache/cassandra/db/compaction/SSTableSplitter.java @@ -56,7 +56,7 @@ public class SSTableSplitter } @Override - protected CompactionController getCompactionController(Set toCompact) + protected CompactionController getCompactionController(Set toCompact, long gcBefore) { return new SplitController(cfs); } diff --git a/src/java/org/apache/cassandra/db/compaction/TimeWindowCompactionTask.java b/src/java/org/apache/cassandra/db/compaction/TimeWindowCompactionTask.java index 33604e52e6..9da33d4ba6 100644 --- a/src/java/org/apache/cassandra/db/compaction/TimeWindowCompactionTask.java +++ b/src/java/org/apache/cassandra/db/compaction/TimeWindowCompactionTask.java @@ -35,7 +35,7 @@ public class TimeWindowCompactionTask extends CompactionTask } @Override - public CompactionController getCompactionController(Set toCompact) + public CompactionController getCompactionController(Set toCompact, long gcBefore) { return new TimeWindowCompactionController(cfs, toCompact, gcBefore, ignoreOverlaps); } diff --git a/src/java/org/apache/cassandra/db/marshal/AbstractTimeUUIDType.java b/src/java/org/apache/cassandra/db/marshal/AbstractTimeUUIDType.java index 04ca358bd6..ffa8fbc95b 100644 --- a/src/java/org/apache/cassandra/db/marshal/AbstractTimeUUIDType.java +++ b/src/java/org/apache/cassandra/db/marshal/AbstractTimeUUIDType.java @@ -22,6 +22,7 @@ import java.util.UUID; import org.apache.cassandra.cql3.CQL3Type; import org.apache.cassandra.cql3.Duration; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.terms.Constants; import org.apache.cassandra.cql3.terms.Term; import org.apache.cassandra.serializers.MarshalException; @@ -32,7 +33,6 @@ import org.apache.cassandra.utils.bytecomparable.ByteSource; import org.apache.cassandra.utils.bytecomparable.ByteSourceInverse; import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static org.apache.cassandra.utils.TimeUUID.Generator.nextTimeUUIDAsBytes; // Fully compatible with UUID, and indeed is interpreted as UUID for UDF public abstract class AbstractTimeUUIDType extends TemporalType @@ -218,9 +218,9 @@ public abstract class AbstractTimeUUIDType extends TemporalType } @Override - public ByteBuffer now() + public ByteBuffer now(FunctionContext context) { - return ByteBuffer.wrap(nextTimeUUIDAsBytes()); + return ByteBuffer.wrap(context.nextTimeUUIDAsBytes()); } @Override diff --git a/src/java/org/apache/cassandra/db/marshal/TemporalType.java b/src/java/org/apache/cassandra/db/marshal/TemporalType.java index 9b6b1ff81d..5f5c4564d5 100644 --- a/src/java/org/apache/cassandra/db/marshal/TemporalType.java +++ b/src/java/org/apache/cassandra/db/marshal/TemporalType.java @@ -22,11 +22,10 @@ import java.nio.ByteBuffer; import org.apache.commons.lang3.mutable.MutableLong; import org.apache.cassandra.cql3.Duration; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.functions.ArgumentDeserializer; import org.apache.cassandra.transport.ProtocolVersion; -import static org.apache.cassandra.utils.Clock.Global.currentTimeMillis; - /** * Base type for temporal types (timestamp, date ...). * @@ -42,9 +41,9 @@ public abstract class TemporalType extends AbstractType * Returns the current temporal value. * @return the current temporal value. */ - public ByteBuffer now() + public ByteBuffer now(FunctionContext context) { - return fromTimeInMillis(currentTimeMillis()); + return fromTimeInMillis(context.nowMillis()); } /** diff --git a/src/java/org/apache/cassandra/db/marshal/TimeType.java b/src/java/org/apache/cassandra/db/marshal/TimeType.java index 6239b6083e..1cef2c1a6d 100644 --- a/src/java/org/apache/cassandra/db/marshal/TimeType.java +++ b/src/java/org/apache/cassandra/db/marshal/TimeType.java @@ -22,6 +22,7 @@ import java.time.LocalTime; import java.time.ZoneOffset; import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.functions.ArgumentDeserializer; import org.apache.cassandra.cql3.terms.Constants; import org.apache.cassandra.cql3.terms.Term; @@ -110,9 +111,10 @@ public class TimeType extends TemporalType } @Override - public ByteBuffer now() + public ByteBuffer now(FunctionContext context) { - return decompose(LocalTime.now(ZoneOffset.UTC).toNanoOfDay()); + long nanoOfDay = LocalTime.ofInstant(context.now(), ZoneOffset.UTC).toNanoOfDay(); + return decompose(nanoOfDay); } @Override diff --git a/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java b/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java index f8f2a041e3..d95105a1b3 100644 --- a/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java +++ b/src/java/org/apache/cassandra/db/partitions/AbstractBTreePartition.java @@ -130,7 +130,6 @@ public abstract class AbstractBTreePartition implements Partition, Iterable if (rt != null && rt.deletionTime().supersedes(activeDeletion)) activeDeletion = rt.deletionTime(); - if (row == null) { // this means our partition level deletion supersedes all other deletions and we don't have to keep the row deletions diff --git a/src/java/org/apache/cassandra/db/repair/CassandraValidationIterator.java b/src/java/org/apache/cassandra/db/repair/CassandraValidationIterator.java index a46cbe58ca..926eb68c98 100644 --- a/src/java/org/apache/cassandra/db/repair/CassandraValidationIterator.java +++ b/src/java/org/apache/cassandra/db/repair/CassandraValidationIterator.java @@ -99,13 +99,6 @@ public class CassandraValidationIterator extends ValidationPartitionIterator } } - public static long getDefaultGcBefore(ColumnFamilyStore cfs, long nowInSec) - { - // 2ndary indexes have ExpiringColumns too, so we need to purge tombstones deleted before now. We do not need to - // add any GcGrace however since 2ndary indexes are local to a node. - return cfs.isIndex() ? nowInSec : cfs.gcBefore(nowInSec); - } - private static class ValidationCompactionIterator extends CompactionIterator { public ValidationCompactionIterator(List scanners, ValidationCompactionController controller, long nowInSec, ActiveCompactionsTracker activeCompactions, TopPartitionTracker.Collector topPartitionCollector) @@ -219,7 +212,7 @@ public class CassandraValidationIterator extends ValidationPartitionIterator cfs.getKeyspaceName(), cfs.getTableName()); - long gcBefore = dontPurgeTombstones ? Long.MIN_VALUE : getDefaultGcBefore(cfs, nowInSec); + long gcBefore = dontPurgeTombstones ? Long.MIN_VALUE : cfs.getDefaultGcBefore(nowInSec); controller = new ValidationCompactionController(cfs, gcBefore); scanners = cfs.getCompactionStrategyManager().getScanners(sstables, ranges); ci = new ValidationCompactionIterator(scanners.scanners, controller, nowInSec, CompactionManager.instance.active, topPartitionCollector); diff --git a/src/java/org/apache/cassandra/io/sstable/CQLSSTableWriter.java b/src/java/org/apache/cassandra/io/sstable/CQLSSTableWriter.java index 552da199ca..71f6efb161 100644 --- a/src/java/org/apache/cassandra/io/sstable/CQLSSTableWriter.java +++ b/src/java/org/apache/cassandra/io/sstable/CQLSSTableWriter.java @@ -46,7 +46,8 @@ import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.cql3.ColumnSpecification; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.QueryProcessor; -import org.apache.cassandra.cql3.UpdateParameters; +import org.apache.cassandra.cql3.RowUpdateBuilder; +import org.apache.cassandra.cql3.RowUpdateBuilder.RegularRowUpdateBuilder; import org.apache.cassandra.cql3.functions.types.TypeCodec; import org.apache.cassandra.cql3.functions.types.UserType; import org.apache.cassandra.cql3.statements.ModificationStatement; @@ -285,13 +286,13 @@ public class CQLSSTableWriter implements Closeable long now = currentTimeMillis(); // Note that we asks indexes to not validate values (the last 'false' arg below) because that triggers a 'Keyspace.open' // and that forces a lot of initialization that we don't want. - UpdateParameters params = new UpdateParameters(modificationStatement.metadata, - ClientState.forInternalCalls(), - options, - modificationStatement.getTimestamp(TimeUnit.MILLISECONDS.toMicros(now), options), - options.getNowInSec((int) TimeUnit.MILLISECONDS.toSeconds(now)), - modificationStatement.getTimeToLive(options), - Collections.emptyMap()); + RowUpdateBuilder builder = new RegularRowUpdateBuilder(modificationStatement.metadata, + ClientState.forInternalCalls(), + options, + modificationStatement.getTimestamp(TimeUnit.MILLISECONDS.toMicros(now), options), + options.getNowInSec((int) TimeUnit.MILLISECONDS.toSeconds(now)), + modificationStatement.getTimeToLive(options), + null); try { @@ -302,7 +303,7 @@ public class CQLSSTableWriter implements Closeable for (ByteBuffer key : keys) { for (Slice slice : slices) - modificationStatement.addUpdateForKey(writer.getUpdateFor(key), slice, params); + modificationStatement.addUpdateForKey(writer.getUpdateFor(key), slice, builder); } } else @@ -312,7 +313,7 @@ public class CQLSSTableWriter implements Closeable for (ByteBuffer key : keys) { for (Clustering clustering : clusterings) - modificationStatement.addUpdateForKey(writer.getUpdateFor(key), clustering, params); + modificationStatement.addUpdateForKey(writer.getUpdateFor(key), clustering, builder); } } return this; diff --git a/src/java/org/apache/cassandra/repair/RepairSession.java b/src/java/org/apache/cassandra/repair/RepairSession.java index 33248a09ee..ed74e81a6e 100644 --- a/src/java/org/apache/cassandra/repair/RepairSession.java +++ b/src/java/org/apache/cassandra/repair/RepairSession.java @@ -123,6 +123,7 @@ public class RepairSession extends AsyncFuture implements I public final boolean isIncremental; public final boolean allReplicas; public final PreviewKind previewKind; + // TODO (expected): introduce forceRepairData and ignore repairData for Accord tables public final boolean repairData; public final boolean repairPaxos; public final boolean repairAccord; diff --git a/src/java/org/apache/cassandra/service/accord/AccordExecutorAbstractLockLoop.java b/src/java/org/apache/cassandra/service/accord/AccordExecutorAbstractLockLoop.java index e3f442a620..5afeaf8a7b 100644 --- a/src/java/org/apache/cassandra/service/accord/AccordExecutorAbstractLockLoop.java +++ b/src/java/org/apache/cassandra/service/accord/AccordExecutorAbstractLockLoop.java @@ -200,7 +200,6 @@ abstract class AccordExecutorAbstractLockLoop extends AccordExecutorAbstractLoop @Override public void run() { - Thread self = Thread.currentThread(); int count = 0; Task task = null; while (true) diff --git a/src/java/org/apache/cassandra/service/accord/AccordObjectSizes.java b/src/java/org/apache/cassandra/service/accord/AccordObjectSizes.java index ddef3232f3..43d26bec27 100644 --- a/src/java/org/apache/cassandra/service/accord/AccordObjectSizes.java +++ b/src/java/org/apache/cassandra/service/accord/AccordObjectSizes.java @@ -65,9 +65,9 @@ import org.apache.cassandra.dht.Murmur3Partitioner; import org.apache.cassandra.schema.TableId; import org.apache.cassandra.service.accord.api.PartitionKey; import org.apache.cassandra.service.accord.api.TokenKey; -import org.apache.cassandra.service.accord.serializers.ResultSerializers; import org.apache.cassandra.service.accord.serializers.TableMetadatasAndKeys; import org.apache.cassandra.service.accord.txn.AccordUpdate; +import org.apache.cassandra.service.accord.txn.TxnDataResult; import org.apache.cassandra.service.accord.txn.TxnQuery; import org.apache.cassandra.service.accord.txn.TxnRead; import org.apache.cassandra.service.accord.txn.TxnWrite; @@ -318,7 +318,7 @@ public class AccordObjectSizes builder.waitingOn(WaitingOn.empty(Domain.Key)); if (saveStatus.known.is(Known.Outcome.Apply)) - builder.result(ResultSerializers.APPLIED); + builder.result(TxnDataResult.PERSISTABLE); return builder.build(saveStatus); } @@ -400,6 +400,7 @@ public class AccordObjectSizes size += sizeNullable(command.partialDeps(), AccordObjectSizes::dependencies); size += sizeNullable(command.acceptedOrCommitted(), AccordObjectSizes::timestamp); size += sizeNullable(command.writes(), AccordObjectSizes::writes); + // no need to measure command.results(), as should always be a sentinel value size += sizeNullable(command.waitingOn(), AccordObjectSizes::waitingOn); return size; } diff --git a/src/java/org/apache/cassandra/service/accord/api/TokenKey.java b/src/java/org/apache/cassandra/service/accord/api/TokenKey.java index 7666f70480..0b5bb356fb 100644 --- a/src/java/org/apache/cassandra/service/accord/api/TokenKey.java +++ b/src/java/org/apache/cassandra/service/accord/api/TokenKey.java @@ -261,6 +261,11 @@ public final class TokenKey extends AccordRoutableKey implements RoutingKey, Ran return new TokenKey(table, BEFORE_TOKEN_SENTINEL, token); } + public static TokenKey after(TableId table, Token token) + { + return new TokenKey(table, AFTER_TOKEN_SENTINEL, token); + } + public static final NoTableSerializer noTableSerializer = new NoTableSerializer(); diff --git a/src/java/org/apache/cassandra/service/accord/journal/CommandChangeWriter.java b/src/java/org/apache/cassandra/service/accord/journal/CommandChangeWriter.java index ec8fecfaf4..967618ca76 100644 --- a/src/java/org/apache/cassandra/service/accord/journal/CommandChangeWriter.java +++ b/src/java/org/apache/cassandra/service/accord/journal/CommandChangeWriter.java @@ -35,9 +35,9 @@ import org.apache.cassandra.io.util.DataOutputPlus; import org.apache.cassandra.journal.Journal; import org.apache.cassandra.service.accord.serializers.CommandSerializers; import org.apache.cassandra.service.accord.serializers.DepsSerializers; -import org.apache.cassandra.service.accord.serializers.ResultSerializers; import org.apache.cassandra.service.accord.serializers.Version; import org.apache.cassandra.service.accord.serializers.WaitingOnSerializer; +import org.apache.cassandra.service.accord.txn.TxnDataResult; import static accord.impl.CommandChange.anyFieldChanged; import static accord.impl.CommandChange.describeFlags; @@ -140,7 +140,7 @@ public class CommandChangeWriter implements Journal.Writer CommandSerializers.writes.serialize(command.writes(), out, userVersion); break; case RESULT: - ResultSerializers.result.serialize(command.result(), out); + TxnDataResult.persistable.serialize(command.result(), out); break; case CLEANUP: Cleanup cleanup; diff --git a/src/java/org/apache/cassandra/service/accord/journal/CommandChanges.java b/src/java/org/apache/cassandra/service/accord/journal/CommandChanges.java index c0246f25dd..13c88f2834 100644 --- a/src/java/org/apache/cassandra/service/accord/journal/CommandChanges.java +++ b/src/java/org/apache/cassandra/service/accord/journal/CommandChanges.java @@ -41,9 +41,9 @@ import org.apache.cassandra.io.util.DataOutputPlus; import org.apache.cassandra.service.accord.JournalKey; import org.apache.cassandra.service.accord.serializers.CommandSerializers; import org.apache.cassandra.service.accord.serializers.DepsSerializers; -import org.apache.cassandra.service.accord.serializers.ResultSerializers; import org.apache.cassandra.service.accord.serializers.Version; import org.apache.cassandra.service.accord.serializers.WaitingOnSerializer; +import org.apache.cassandra.service.accord.txn.TxnDataResult; import static accord.api.Journal.Load.ALL; import static accord.impl.CommandChange.Field.CLEANUP; @@ -205,7 +205,7 @@ public class CommandChanges extends CommandChange.Builder implements Merger break; case RESULT: Invariants.require(result != null, "%s", this); - ResultSerializers.result.serialize(result, out); + TxnDataResult.persistable.serialize(result, out); break; } } @@ -309,7 +309,7 @@ public class CommandChanges extends CommandChange.Builder implements Merger cleanup = newCleanup; break; case RESULT: - result = ResultSerializers.result.deserialize(in); + result = TxnDataResult.persistable.deserialize(in); break; } } @@ -355,8 +355,7 @@ public class CommandChanges extends CommandChange.Builder implements Merger CommandSerializers.writes.skip(in, userVersion); break; case RESULT: - // TODO (expected): skip - ResultSerializers.result.skip(in); + TxnDataResult.persistable.skip(in); break; } } diff --git a/src/java/org/apache/cassandra/service/accord/serializers/ApplySerializers.java b/src/java/org/apache/cassandra/service/accord/serializers/ApplySerializers.java index 291acace92..53244bfdeb 100644 --- a/src/java/org/apache/cassandra/service/accord/serializers/ApplySerializers.java +++ b/src/java/org/apache/cassandra/service/accord/serializers/ApplySerializers.java @@ -38,6 +38,7 @@ import org.apache.cassandra.db.TypeSizes; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; import org.apache.cassandra.service.accord.serializers.CommandSerializers.ExecuteAtSerializer; +import org.apache.cassandra.service.accord.txn.TxnDataResult; import static accord.primitives.Txn.Kind.Write; @@ -79,7 +80,7 @@ public class ApplySerializers CommandSerializers.nullablePartialTxn.deserialize(in, version), KeySerializers.nullableFullRoute.deserialize(in), (txnId.is(Write) ? CommandSerializers.writes.deserialize(in, version) : null), - ResultSerializers.APPLIED, + TxnDataResult.PERSISTABLE, ExecuteFlags.get(in.readUnsignedVInt32())); } diff --git a/src/java/org/apache/cassandra/service/accord/serializers/CheckStatusSerializers.java b/src/java/org/apache/cassandra/service/accord/serializers/CheckStatusSerializers.java index c3131bd4ba..2f18b716ed 100644 --- a/src/java/org/apache/cassandra/service/accord/serializers/CheckStatusSerializers.java +++ b/src/java/org/apache/cassandra/service/accord/serializers/CheckStatusSerializers.java @@ -48,6 +48,7 @@ import org.apache.cassandra.io.UnversionedSerializer; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; import org.apache.cassandra.service.accord.serializers.CommandSerializers.ExecuteAtSerializer; +import org.apache.cassandra.service.accord.txn.TxnDataResult; import static accord.messages.CheckStatus.SerializationSupport.createOk; import static org.apache.cassandra.service.accord.serializers.CommandSerializers.known; @@ -230,7 +231,7 @@ public class CheckStatusSerializers PersistableResult result = null; if (maxKnowledgeStatus.known.outcome().isOrWasApply()) - result = ResultSerializers.APPLIED; + result = TxnDataResult.PERSISTABLE; return createOk(map, maxKnowledgeStatus, maxStatus, maxPromised, maxAcceptedOrCommitted, acceptedOrCommitted, executeAt, isCoordinating, durability, route, homeKey, invalidIf, partialTxn, committedDeps, writes, result); diff --git a/src/java/org/apache/cassandra/service/accord/serializers/ReadDataSerializer.java b/src/java/org/apache/cassandra/service/accord/serializers/ReadDataSerializer.java index 5e496329df..ffa792b566 100644 --- a/src/java/org/apache/cassandra/service/accord/serializers/ReadDataSerializer.java +++ b/src/java/org/apache/cassandra/service/accord/serializers/ReadDataSerializer.java @@ -51,6 +51,7 @@ import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; import org.apache.cassandra.service.accord.serializers.CommandSerializers.ExecuteAtSerializer; import org.apache.cassandra.service.accord.txn.TxnData; +import org.apache.cassandra.service.accord.txn.TxnDataResult; import org.apache.cassandra.utils.vint.VIntCoding; import static org.apache.cassandra.utils.NullableSerializer.deserializeNullable; @@ -216,7 +217,7 @@ public class ReadDataSerializer implements IVersionedSerializer PartialDeps deps = DepsSerializers.partialDeps.deserialize(in); FullRoute route = KeySerializers.fullRoute.deserialize(in); Writes writes = hasWrites ? CommandSerializers.writes.deserialize(in, version) : null; - return ApplyThenWaitUntilApplied.SerializerSupport.create(txnId, scope, minEpoch, executeAt, route, txn, deps, writes, ResultSerializers.APPLIED); + return ApplyThenWaitUntilApplied.SerializerSupport.create(txnId, scope, minEpoch, executeAt, route, txn, deps, writes, TxnDataResult.PERSISTABLE); } } } diff --git a/src/java/org/apache/cassandra/service/accord/serializers/RecoverySerializers.java b/src/java/org/apache/cassandra/service/accord/serializers/RecoverySerializers.java index 56ad82df40..e11acd15dc 100644 --- a/src/java/org/apache/cassandra/service/accord/serializers/RecoverySerializers.java +++ b/src/java/org/apache/cassandra/service/accord/serializers/RecoverySerializers.java @@ -45,6 +45,7 @@ import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; import org.apache.cassandra.service.accord.serializers.CommandSerializers.ExecuteAtSerializer; import org.apache.cassandra.service.accord.serializers.TxnRequestSerializer.WithUnsyncedSerializer; +import org.apache.cassandra.service.accord.txn.TxnDataResult; import org.apache.cassandra.utils.vint.VIntCoding; import static accord.messages.BeginRecovery.RecoverReply.Kind.Ok; @@ -154,7 +155,7 @@ public class RecoverySerializers Result result = null; if (status == Status.PreApplied || status == Status.Applied || status == Status.Truncated) - result = ResultSerializers.APPLIED; + result = TxnDataResult.PERSISTABLE; return deserializeOk(id, status, diff --git a/src/java/org/apache/cassandra/service/accord/serializers/RemoteSuccessSerializers.java b/src/java/org/apache/cassandra/service/accord/serializers/RemoteSuccessSerializers.java index 8a66118023..6fdd8309cf 100644 --- a/src/java/org/apache/cassandra/service/accord/serializers/RemoteSuccessSerializers.java +++ b/src/java/org/apache/cassandra/service/accord/serializers/RemoteSuccessSerializers.java @@ -25,7 +25,7 @@ import accord.primitives.TxnId; import org.apache.cassandra.io.VersionedSerializer; import org.apache.cassandra.io.util.DataInputPlus; import org.apache.cassandra.io.util.DataOutputPlus; -import org.apache.cassandra.service.accord.txn.TxnData; +import org.apache.cassandra.service.accord.txn.TxnDataResult; public class RemoteSuccessSerializers { @@ -35,14 +35,14 @@ public class RemoteSuccessSerializers public void serialize(RemoteSuccess msg, DataOutputPlus out, Version version) throws IOException { CommandSerializers.txnId.serialize(msg.txnId, out); - TxnData.serializer.serialize((TxnData) msg.result, out, version); + TxnDataResult.serializer.serialize((TxnDataResult) msg.result, out, version); } @Override public RemoteSuccess deserialize(DataInputPlus in, Version version) throws IOException { TxnId txnId = CommandSerializers.txnId.deserialize(in); - TxnData data = TxnData.serializer.deserialize(in, version); + TxnDataResult data = TxnDataResult.serializer.deserialize(in, version); return new RemoteSuccess(txnId, data); } @@ -50,7 +50,7 @@ public class RemoteSuccessSerializers public long serializedSize(RemoteSuccess msg, Version version) { return CommandSerializers.txnId.serializedSize(msg.txnId) - + TxnData.serializer.serializedSize((TxnData) msg.result, version); + + TxnDataResult.serializer.serializedSize((TxnDataResult) msg.result, version); } }; } diff --git a/src/java/org/apache/cassandra/service/accord/serializers/ResultSerializers.java b/src/java/org/apache/cassandra/service/accord/serializers/ResultSerializers.java deleted file mode 100644 index 2a9e36b255..0000000000 --- a/src/java/org/apache/cassandra/service/accord/serializers/ResultSerializers.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.cassandra.service.accord.serializers; - -import accord.api.Result.PersistableResult; - -import org.apache.cassandra.io.UnversionedSerializer; -import org.apache.cassandra.io.util.DataInputPlus; -import org.apache.cassandra.io.util.DataOutputPlus; - -public class ResultSerializers -{ - // TODO (desired): this is meant to encode e.g. whether the transaction's condition met or not for clients to later query - public static final PersistableResult APPLIED = new PersistableResult(){}; - - public static final UnversionedSerializer result = new UnversionedSerializer<>() - { - public void serialize(PersistableResult t, DataOutputPlus out) { } - public PersistableResult deserialize(DataInputPlus in) - { - return APPLIED; - } - - public long serializedSize(PersistableResult t) - { - return 0; - } - }; -} diff --git a/src/java/org/apache/cassandra/service/accord/txn/AccordUpdateParameters.java b/src/java/org/apache/cassandra/service/accord/txn/AccordUpdateParameters.java index db369440d5..c8f0b6f744 100644 --- a/src/java/org/apache/cassandra/service/accord/txn/AccordUpdateParameters.java +++ b/src/java/org/apache/cassandra/service/accord/txn/AccordUpdateParameters.java @@ -23,8 +23,9 @@ import java.util.Map; import com.google.common.collect.ImmutableMap; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.QueryOptions; -import org.apache.cassandra.cql3.UpdateParameters; +import org.apache.cassandra.cql3.RowUpdateBuilder; import org.apache.cassandra.db.DecoratedKey; import org.apache.cassandra.db.marshal.TimeUUIDType; import org.apache.cassandra.db.partitions.Partition; @@ -50,11 +51,11 @@ public class AccordUpdateParameters this.timestamp = timestamp; } - static class RowUpdateParameters extends UpdateParameters + static class AccordUpdateBuilder extends RowUpdateBuilder implements FunctionContext.PartialFunctionContext { private long timeUuidNanos; - public RowUpdateParameters(TableMetadata metadata, ClientState clientState, QueryOptions options, long timestamp, long nowInSec, int ttl, Map prefetchedRows) throws InvalidRequestException + public AccordUpdateBuilder(TableMetadata metadata, ClientState clientState, QueryOptions options, long timestamp, long nowInSec, int ttl, Map prefetchedRows) throws InvalidRequestException { super(metadata, clientState, options, timestamp, nowInSec, ttl, prefetchedRows); } @@ -64,6 +65,11 @@ public class AccordUpdateParameters { return TimeUUID.toBytes(Ballot.unixMicrosToMsb(timestamp), TimeUUIDType.signedBytesToNativeLong(timeUuidNanos++)); } + + public long nowMicros() + { + return timestamp; + } } public TxnData getData() @@ -71,7 +77,7 @@ public class AccordUpdateParameters return data; } - public UpdateParameters updateParameters(TableMetadata metadata, DecoratedKey dk, int rowIndex, long overrideTimestamp) + public RowUpdateBuilder updateBuilder(TableMetadata metadata, DecoratedKey dk, int rowIndex, long overrideTimestamp) { // This is currently only used by Guardrails, but this logically have issues with Accord as drifts in config // values could cause unexpected issues in Accord. (ex. some nodes reject writes while others accept) @@ -79,7 +85,7 @@ public class AccordUpdateParameters ClientState disabledGuardrails = null; int ttl = metadata.params.defaultTimeToLive; - return new RowUpdateParameters(metadata, + return new AccordUpdateBuilder(metadata, disabledGuardrails, options, overrideTimestamp == TxnWrite.NO_TIMESTAMP ? timestamp : overrideTimestamp, diff --git a/src/java/org/apache/cassandra/service/accord/txn/RetryWithNewProtocolResult.java b/src/java/org/apache/cassandra/service/accord/txn/RetryWithNewProtocolResult.java index 5f80e304e4..8efc5a7ad4 100644 --- a/src/java/org/apache/cassandra/service/accord/txn/RetryWithNewProtocolResult.java +++ b/src/java/org/apache/cassandra/service/accord/txn/RetryWithNewProtocolResult.java @@ -35,10 +35,4 @@ public class RetryWithNewProtocolResult implements TxnResult { return Kind.retry_new_protocol; } - - @Override - public long estimatedSizeOnHeap() - { - return 0; - } } diff --git a/src/java/org/apache/cassandra/service/accord/txn/TxnData.java b/src/java/org/apache/cassandra/service/accord/txn/TxnData.java index 2791ecce30..8297c308da 100644 --- a/src/java/org/apache/cassandra/service/accord/txn/TxnData.java +++ b/src/java/org/apache/cassandra/service/accord/txn/TxnData.java @@ -30,7 +30,6 @@ import accord.primitives.TxnId; import org.apache.cassandra.db.EmptyIterators; import org.apache.cassandra.db.SinglePartitionReadCommand; -import org.apache.cassandra.db.TypeSizes; import org.apache.cassandra.db.partitions.PartitionIterators; import org.apache.cassandra.io.VersionedSerializer; import org.apache.cassandra.io.util.DataInputPlus; @@ -40,19 +39,15 @@ import org.apache.cassandra.service.accord.serializers.Version; import org.apache.cassandra.utils.CollectionSerializers; import org.apache.cassandra.utils.Int32Serializer; import org.apache.cassandra.utils.NullableSerializer; -import org.apache.cassandra.utils.ObjectSizes; import static accord.utils.Invariants.requireArgument; -import static org.apache.cassandra.service.accord.txn.TxnResult.Kind.txn_data; /** * Fairly generic holder for result values for Accord txns as well as data exchange during Accord txn execution * when read results are returned to the coordinator to compute query results and writes. */ -public class TxnData extends Int2ObjectHashMap implements TxnResult, Data +public class TxnData extends Int2ObjectHashMap implements Data { - private static final long EMPTY_SIZE = ObjectSizes.measure(new TxnData()); - private static final int TXN_DATA_NAME_INDEX_BITS = 32 - 6; private static final int TXN_DATA_NAME_INDEX_MASK = ~(~0 << TXN_DATA_NAME_INDEX_BITS); public static final int TXN_DATA_NAME_INDEX_MAX = ((1 << TXN_DATA_NAME_INDEX_BITS) - 1); @@ -114,7 +109,7 @@ public class TxnData extends Int2ObjectHashMap implements TxnResul public TxnData() {} - private TxnData(int size) + TxnData(int size) { super(size, 0.65f, false); } @@ -130,7 +125,12 @@ public class TxnData extends Int2ObjectHashMap implements TxnResul { requireArgument(size >= 0, "size can't be negative"); size = Math.max(4, size); - return new TxnData(size < 1073741824 ? (int)((float)size / 0.75F + 1.0F) : Integer.MAX_VALUE); + return new TxnData(capacityForExpectedSize(size)); + } + + public static int capacityForExpectedSize(int size) + { + return size < 1073741824 ? (int)((float)size / 0.65F + 1.0F) : Integer.MAX_VALUE; } @Override @@ -235,15 +235,6 @@ public class TxnData extends Int2ObjectHashMap implements TxnResul return true; } - @Override - public long estimatedSizeOnHeap() - { - long size = EMPTY_SIZE + ObjectSizes.sizeOfReferenceArray(capacity()) + (capacity() * TypeSizes.INT_SIZE); - for (TxnDataValue value : values()) - size += value.estimatedSizeOnHeap(); - return size; - } - public static TxnData emptyPartition(int name, SinglePartitionReadCommand command) { TxnData result = new TxnData(); @@ -252,13 +243,7 @@ public class TxnData extends Int2ObjectHashMap implements TxnResul return result; } - @Override - public Kind kind() - { - return txn_data; - } - - private static final IVersionedSerializer INT32_SERIALIZER = IVersionedSerializer.fromSerializer(Int32Serializer.serializer); + static final IVersionedSerializer INT32_SERIALIZER = IVersionedSerializer.fromSerializer(Int32Serializer.serializer); public static final IVersionedSerializer serializer = new IVersionedSerializer() { @Override diff --git a/src/java/org/apache/cassandra/service/accord/txn/TxnDataResult.java b/src/java/org/apache/cassandra/service/accord/txn/TxnDataResult.java new file mode 100644 index 0000000000..b9fed22843 --- /dev/null +++ b/src/java/org/apache/cassandra/service/accord/txn/TxnDataResult.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.cassandra.service.accord.txn; + +import java.io.IOException; + +import accord.utils.Invariants; + +import org.apache.cassandra.db.TypeSizes; +import org.apache.cassandra.io.UnversionedSerializer; +import org.apache.cassandra.io.util.DataInputPlus; +import org.apache.cassandra.io.util.DataOutputPlus; +import org.apache.cassandra.service.accord.serializers.IVersionedSerializer; +import org.apache.cassandra.service.accord.serializers.Version; +import org.apache.cassandra.utils.CollectionSerializers; + +import static org.apache.cassandra.service.accord.txn.TxnResult.Kind.txn_data; + +public class TxnDataResult extends TxnData implements TxnResult +{ + public final long atMicros; + + public TxnDataResult(long atMicros) + { + this.atMicros = atMicros; + } + + private TxnDataResult(long atMicros, int size) + { + super(capacityForExpectedSize(size)); + this.atMicros = atMicros; + } + + @Override + public Kind kind() + { + return txn_data; + } + + public static TxnDataResult of(long timestamp, int key, TxnDataValue value) + { + TxnDataResult result = new TxnDataResult(timestamp); + result.put(key, value); + return result; + } + + public static final IVersionedSerializer serializer = new IVersionedSerializer<>() + { + @Override + public void serialize(TxnDataResult data, DataOutputPlus out, Version version) throws IOException + { + out.writeUnsignedVInt(data.atMicros); + TxnData.serializer.serialize(data, out, version); + } + + @Override + public TxnDataResult deserialize(DataInputPlus in, Version version) throws IOException + { + long atMicros = in.readUnsignedVInt(); + return CollectionSerializers.deserializeMap(in, version, INT32_SERIALIZER, TxnDataValue.serializer, expectedSize -> new TxnDataResult(atMicros, expectedSize)); + } + + @Override + public long serializedSize(TxnDataResult data, Version version) + { + return TypeSizes.sizeofUnsignedVInt(data.atMicros) + TxnData.serializer.serializedSize(data, version); + } + }; + + public static final UnversionedSerializer persistable = new UnversionedSerializer<>() + { + public void serialize(PersistableResult t, DataOutputPlus out) + { + Invariants.require(t == PERSISTABLE); + } + + public PersistableResult deserialize(DataInputPlus in) + { + return PERSISTABLE; + } + + public long serializedSize(PersistableResult t) + { + return 0; + } + }; +} diff --git a/src/java/org/apache/cassandra/service/accord/txn/TxnQuery.java b/src/java/org/apache/cassandra/service/accord/txn/TxnQuery.java index 088cd684d9..9445864cd0 100644 --- a/src/java/org/apache/cassandra/service/accord/txn/TxnQuery.java +++ b/src/java/org/apache/cassandra/service/accord/txn/TxnQuery.java @@ -78,7 +78,10 @@ public abstract class TxnQuery implements Query @Override public Result doCompute(TxnId txnId, Timestamp executeAt, Seekables keys, @Nullable Data data, @Nullable Read read, @Nullable Update update) { - return data != null ? (TxnData) data : new TxnData(); + TxnDataResult result = new TxnDataResult(executeAt.uniqueHlc()); + if (data != null) + result.putAll((TxnData)data); + return result; } }; @@ -97,7 +100,7 @@ public abstract class TxnQuery implements Query @Override public Result doCompute(TxnId txnId, Timestamp executeAt, Seekables keys, @Nullable Data data, @Nullable Read read, @Nullable Update update) { - return new TxnData(); + return new TxnDataResult(executeAt.uniqueHlc()); } }; @@ -122,19 +125,26 @@ public abstract class TxnQuery implements Query AccordUpdate accordUpdate = (AccordUpdate)update; TxnData txnData = (TxnData)data; boolean conditionCheck = accordUpdate.checkAnyConditionMatch(data); - // If the condition applied an empty result indicates success - if (conditionCheck) - return new TxnData(); - else if (txnData.isEmpty()) + TxnDataResult result = new TxnDataResult(executeAt.uniqueHlc()); + + if (!conditionCheck) { - TxnRead txnKeyRead = (TxnRead)read; - SinglePartitionReadCommand command = (SinglePartitionReadCommand) txnKeyRead.deserialize(0); - // For CAS must return a non-empty result to indicate error even if there was no partition found - return TxnData.of(txnDataName(CAS_READ), new TxnDataKeyValue(EmptyIterators.row(command.metadata(), command.partitionKey(), command.isReversed()))); + if (txnData.isEmpty()) + { + TxnRead txnKeyRead = (TxnRead) read; + SinglePartitionReadCommand command = (SinglePartitionReadCommand) txnKeyRead.deserialize(0); + // For CAS must return a non-empty result to indicate error even if there was no partition found + result.put(txnDataName(CAS_READ), new TxnDataKeyValue(EmptyIterators.row(command.metadata(), command.partitionKey(), command.isReversed()))); + } + else + { + // If it failed to apply the partition contents are returned and it indicates failure + result.putAll(((TxnData) data)); + } } - else - // If it failed to apply the partition contents are returned and it indicates failure - return ((TxnData)data); + // (else): If the condition applied, an empty result indicates success + + return result; } }; @@ -161,7 +171,7 @@ public abstract class TxnQuery implements Query // Skip the migration checks in the base class for empty transactions, we don't // want/need the RetryWithNewProtocolResult - return new TxnData(); + return new TxnDataResult(executeAt.uniqueHlc()); } @Override @@ -182,10 +192,11 @@ public abstract class TxnQuery implements Query @Override public Result doCompute(TxnId txnId, Timestamp executeAt, Seekables keys, @Nullable Data data, @Nullable Read read, @Nullable Update update) { - return data != null ? concat((TxnData) data, read) : new TxnData(); + long timestamp = executeAt.uniqueHlc(); + return data != null ? concat(timestamp, (TxnData) data, read) : new TxnDataResult(timestamp); } - private Result concat(TxnData data, Read read) + private Result concat(long timestamp, TxnData data, Read read) { TxnRead txnRead = (TxnRead) read; PartitionRangeReadCommand command = (PartitionRangeReadCommand) txnRead.deserialize(0); @@ -197,7 +208,7 @@ public abstract class TxnQuery implements Query 0, command.selectsFullPartition(), command.metadata().enforceStrictLiveness()); - return new TxnRangeReadResult(sourceWithLimits); + return new TxnRangeReadResult(timestamp, sourceWithLimits); } }; diff --git a/src/java/org/apache/cassandra/service/accord/txn/TxnRangeReadResult.java b/src/java/org/apache/cassandra/service/accord/txn/TxnRangeReadResult.java index a70709cee9..44890bbc7f 100644 --- a/src/java/org/apache/cassandra/service/accord/txn/TxnRangeReadResult.java +++ b/src/java/org/apache/cassandra/service/accord/txn/TxnRangeReadResult.java @@ -21,18 +21,15 @@ package org.apache.cassandra.service.accord.txn; import java.util.function.Supplier; import org.apache.cassandra.db.partitions.PartitionIterator; -import org.apache.cassandra.db.rows.Row; -import org.apache.cassandra.db.rows.RowIterator; -import org.apache.cassandra.utils.ObjectSizes; public class TxnRangeReadResult implements TxnResult { - private static final long EMPTY_SIZE = ObjectSizes.measure(new TxnRangeReadResult(null)); - + public final long atMicros; public final Supplier partitions; - public TxnRangeReadResult(Supplier partitions) + public TxnRangeReadResult(long atMicros, Supplier partitions) { + this.atMicros = atMicros; this.partitions = partitions; } @@ -41,22 +38,4 @@ public class TxnRangeReadResult implements TxnResult { return Kind.range_read; } - - @Override - public long estimatedSizeOnHeap() - { - long size = EMPTY_SIZE; - PartitionIterator iterator = partitions.get(); - while (iterator.hasNext()) - { - RowIterator rowIterator = iterator.next(); - Row staticRow = rowIterator.staticRow(); - if (staticRow != null) - size += staticRow.unsharedHeapSize(); - while (rowIterator.hasNext()) - size += rowIterator.next().unsharedHeapSize(); - } - // TODO: Include the other parts of FilteredPartition after we rebase to pull in BTreePartitionData? - return size; - } } diff --git a/src/java/org/apache/cassandra/service/accord/txn/TxnReferenceOperation.java b/src/java/org/apache/cassandra/service/accord/txn/TxnReferenceOperation.java index a07784f2f3..7d47f8afd2 100644 --- a/src/java/org/apache/cassandra/service/accord/txn/TxnReferenceOperation.java +++ b/src/java/org/apache/cassandra/service/accord/txn/TxnReferenceOperation.java @@ -30,7 +30,7 @@ import com.google.common.annotations.VisibleForTesting; import org.apache.cassandra.cql3.FieldIdentifier; import org.apache.cassandra.cql3.Operation; -import org.apache.cassandra.cql3.UpdateParameters; +import org.apache.cassandra.cql3.RowUpdateBuilder; import org.apache.cassandra.cql3.terms.Constants; import org.apache.cassandra.cql3.terms.Lists; import org.apache.cassandra.cql3.terms.Maps; @@ -269,7 +269,7 @@ public class TxnReferenceOperation return receiver; } - public void apply(TxnData data, DecoratedKey key, UpdateParameters up) + public void apply(TxnData data, DecoratedKey key, RowUpdateBuilder up) { Operation operation = toOperation(data); operation.execute(key, up); diff --git a/src/java/org/apache/cassandra/service/accord/txn/TxnResult.java b/src/java/org/apache/cassandra/service/accord/txn/TxnResult.java index 10d97d6f22..2917a12dee 100644 --- a/src/java/org/apache/cassandra/service/accord/txn/TxnResult.java +++ b/src/java/org/apache/cassandra/service/accord/txn/TxnResult.java @@ -20,10 +20,10 @@ package org.apache.cassandra.service.accord.txn; import accord.api.Result; -import static org.apache.cassandra.service.accord.serializers.ResultSerializers.APPLIED; - public interface TxnResult extends Result { + PersistableResult PERSISTABLE = new PersistableResult(){}; + enum Kind { txn_data(0), @@ -40,12 +40,9 @@ public interface TxnResult extends Result } Kind kind(); - - long estimatedSizeOnHeap(); - default PersistableResult toPersistable() { // TODO (required): should we persist the Kind? - return APPLIED; + return PERSISTABLE; } } diff --git a/src/java/org/apache/cassandra/service/accord/txn/TxnValidationRejection.java b/src/java/org/apache/cassandra/service/accord/txn/TxnValidationRejection.java index aca6fbfda3..4d404a14af 100644 --- a/src/java/org/apache/cassandra/service/accord/txn/TxnValidationRejection.java +++ b/src/java/org/apache/cassandra/service/accord/txn/TxnValidationRejection.java @@ -44,10 +44,4 @@ public class TxnValidationRejection implements TxnResult { return Kind.validation_rejection; } - - @Override - public long estimatedSizeOnHeap() - { - return 0; - } } diff --git a/src/java/org/apache/cassandra/service/accord/txn/TxnWrite.java b/src/java/org/apache/cassandra/service/accord/txn/TxnWrite.java index ad157e6287..719eb0f590 100644 --- a/src/java/org/apache/cassandra/service/accord/txn/TxnWrite.java +++ b/src/java/org/apache/cassandra/service/accord/txn/TxnWrite.java @@ -47,7 +47,7 @@ import accord.utils.SimpleBitSets; import accord.utils.async.AsyncChain; import accord.utils.async.AsyncChains; -import org.apache.cassandra.cql3.UpdateParameters; +import org.apache.cassandra.cql3.RowUpdateBuilder; import org.apache.cassandra.db.Clustering; import org.apache.cassandra.db.Columns; import org.apache.cassandra.db.DecoratedKey; @@ -307,7 +307,7 @@ public class TxnWrite extends AbstractKeySorted implements Writ baseUpdate.rowCount(), baseUpdate.canHaveShadowedData()); - UpdateParameters up = parameters.updateParameters(baseUpdate.metadata(), key, index, timestamp); + RowUpdateBuilder up = parameters.updateBuilder(baseUpdate.metadata(), key, index, timestamp); TxnData data = parameters.getData(); Row staticRow = applyUpdates(baseUpdate.staticRow(), referenceOps.statics, key, Clustering.STATIC_CLUSTERING, up, data); @@ -354,7 +354,7 @@ public class TxnWrite extends AbstractKeySorted implements Writ columns(current.regulars, referenceOps.regulars)); } - private static Row applyUpdates(Row existing, List operations, DecoratedKey key, Clustering clustering, UpdateParameters up, TxnData data) + private static Row applyUpdates(Row existing, List operations, DecoratedKey key, Clustering clustering, RowUpdateBuilder up, TxnData data) { if (operations.isEmpty()) return existing; diff --git a/src/java/org/apache/cassandra/transport/ProtocolVersion.java b/src/java/org/apache/cassandra/transport/ProtocolVersion.java index 6078fb2a5f..1555e5f558 100644 --- a/src/java/org/apache/cassandra/transport/ProtocolVersion.java +++ b/src/java/org/apache/cassandra/transport/ProtocolVersion.java @@ -26,6 +26,8 @@ import java.util.Optional; import org.apache.commons.lang3.ArrayUtils; +import org.apache.cassandra.cql3.FunctionContext; + /** * The native (CQL binary) protocol version. * @@ -35,7 +37,7 @@ import org.apache.commons.lang3.ArrayUtils; * by the specs. * */ -public enum ProtocolVersion implements Comparable +public enum ProtocolVersion implements Comparable, FunctionContext.NoTimeOrQueryFunctionContext { // The order is important as it defines the chronological history of versions, which is used // to determine if a feature is supported or some serdes formats @@ -173,4 +175,10 @@ public enum ProtocolVersion implements Comparable { return num <= other.num; } + + @Override + public ProtocolVersion getProtocolVersion() + { + return this; + } } diff --git a/src/java/org/apache/cassandra/utils/Clock.java b/src/java/org/apache/cassandra/utils/Clock.java index ed65d9a1b1..1d7bd70368 100644 --- a/src/java/org/apache/cassandra/utils/Clock.java +++ b/src/java/org/apache/cassandra/utils/Clock.java @@ -120,6 +120,11 @@ public interface Clock return instance.currentTimeMicros(); } + public static Instant currentTime() + { + return instance.currentTime(); + } + /** * Semantically equivalent to {@link FBUtilities#nowInSeconds()} */ @@ -150,14 +155,19 @@ public interface Clock @Override public long currentTimeMicros() { - Instant now = Instant.now(); // checkstyle: permit this invocation + Instant now = currentTime(); long seconds = now.getEpochSecond(); long nanos = now.getNano(); Invariants.require(seconds >= 0); long micros = Math.multiplyExact(seconds, 1000_000); micros = Math.addExact(micros, nanos/1000); return micros; + } + @Override + public Instant currentTime() + { + return Instant.now(); // checkstyle: permit this invocation } } @@ -176,6 +186,11 @@ public interface Clock return currentTimeMillis() * 1000L; } + public default Instant currentTime() + { + return Instant.ofEpochMilli(currentTimeMillis()); + } + public default long nowInSeconds() { return currentTimeMillis() / 1000L; diff --git a/src/java/org/apache/cassandra/utils/TimeUUID.java b/src/java/org/apache/cassandra/utils/TimeUUID.java index a0b9698848..752f3d47e7 100644 --- a/src/java/org/apache/cassandra/utils/TimeUUID.java +++ b/src/java/org/apache/cassandra/utils/TimeUUID.java @@ -466,7 +466,7 @@ public class TimeUUID implements Serializable, Comparable // needs to return two different values for the same when. // we can generate at most 10k UUIDs per ms. - private static long nextUnixMicros() + public static long nextUnixMicros() { long newLastMicros; while (true) diff --git a/src/java/org/apache/cassandra/utils/concurrent/SignalLock.java b/src/java/org/apache/cassandra/utils/concurrent/SignalLock.java index bced18c198..9542d396bd 100644 --- a/src/java/org/apache/cassandra/utils/concurrent/SignalLock.java +++ b/src/java/org/apache/cassandra/utils/concurrent/SignalLock.java @@ -220,7 +220,7 @@ public final class SignalLock implements Lock long cur = state; if (hasLockWork(cur) && isLockAvailable(cur, thread, null)) { - if (tryTakeLockInAwaitLoop(cur, thread, self, hasPaused)) return true; + if (tryTakeLockInAwaitLoop(cur, thread, 0L, self, hasPaused)) return true; else continue; } else if (tryAcquireAsyncInAwaitLoop(cur, thread, 0L, hasPaused, burstSignalOnAcquire)) @@ -234,7 +234,7 @@ public final class SignalLock implements Lock hasPaused = pause(thread, hasPaused); int waitingEnabledThreadCount = waitingEnabledThreadCount(cur); - int multiplier = 1 + waitingEnabledThreadCount == 0 ? 0 : ThreadLocalRandom.current().nextInt(2 * waitingEnabledThreadCount(cur)); + int multiplier = 1 + (waitingEnabledThreadCount == 0 ? 0 : ThreadLocalRandom.current().nextInt(2 * waitingEnabledThreadCount(cur))); LockSupport.parkNanos(spinIntervalNanos * multiplier); } } @@ -254,7 +254,7 @@ public final class SignalLock implements Lock } else if (hasLockWork(cur) && isLockAvailable(cur, thread, null)) { - if (tryTakeLockInAwaitLoop(cur, thread, self, hasPaused)) return true; + if (tryTakeLockInAwaitLoop(cur, thread, threadBit, self, hasPaused)) return true; else continue; } else if (tryAcquireAsyncInAwaitLoop(cur, thread, threadBit, hasPaused, burstSignalOnAcquire)) @@ -271,9 +271,9 @@ public final class SignalLock implements Lock } } - private boolean tryTakeLockInAwaitLoop(long cur, int thread, Thread self, boolean hasPaused) + private boolean tryTakeLockInAwaitLoop(long cur, int thread, long threadBitIfWaiting, Thread self, boolean hasPaused) { - long upd = setLockThread(clearLockThread(cur), thread, LOCK_OWNED) ^ threadBit(thread); + long upd = setLockThread(clearLockThread(cur), thread, LOCK_OWNED) ^ threadBitIfWaiting; if (!stateUpdater.compareAndSet(this, cur, upd)) return false; diff --git a/test/distributed/org/apache/cassandra/distributed/test/accord/AccordCommandStoreTryExecuteListeningTest.java b/test/distributed/org/apache/cassandra/distributed/test/accord/AccordCommandStoreTryExecuteListeningTest.java index 1f853fd5f5..0277095da4 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/accord/AccordCommandStoreTryExecuteListeningTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/accord/AccordCommandStoreTryExecuteListeningTest.java @@ -64,7 +64,7 @@ import org.apache.cassandra.schema.TableId; import org.apache.cassandra.service.accord.AccordCommandStore; import org.apache.cassandra.service.accord.AccordService; import org.apache.cassandra.service.accord.api.PartitionKey; -import org.apache.cassandra.service.accord.serializers.ResultSerializers; +import org.apache.cassandra.service.accord.txn.TxnDataResult; import org.apache.cassandra.utils.ByteBufferUtil; import static org.apache.cassandra.distributed.api.Feature.GOSSIP; @@ -179,7 +179,7 @@ public class AccordCommandStoreTryExecuteListeningTest extends TestBaseImpl waitingOnBits.setRange(0, txnIds.length); waitingOn = new Command.WaitingOn(RoutingKeys.EMPTY, deps.rangeDeps, new ImmutableBitSet(waitingOnBits), new ImmutableBitSet(txnIds.length)); } - return Command.Executed.executed(txnId, saveStatus, Status.Durability.NotDurable, StoreParticipants.execute(commandStore.unsafeGetRangesForEpoch(), route, txnId, txnId.epoch()), Ballot.ZERO, txnId, txn.intersecting(route, true), deps.intersecting(route), Ballot.ZERO, waitingOn, null, ResultSerializers.APPLIED); + return Command.Executed.executed(txnId, saveStatus, Status.Durability.NotDurable, StoreParticipants.execute(commandStore.unsafeGetRangesForEpoch(), route, txnId, txnId.epoch()), Ballot.ZERO, txnId, txn.intersecting(route, true), deps.intersecting(route), Ballot.ZERO, waitingOn, null, TxnDataResult.PERSISTABLE); } private static PartitionKey keyN(int n, Node node) diff --git a/test/distributed/org/apache/cassandra/distributed/test/accord/AccordWriteInteroperabilityTest.java b/test/distributed/org/apache/cassandra/distributed/test/accord/AccordWriteInteroperabilityTest.java index dff97a0717..d6c7ccebc1 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/accord/AccordWriteInteroperabilityTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/accord/AccordWriteInteroperabilityTest.java @@ -32,6 +32,7 @@ import com.google.common.collect.ImmutableList; import org.junit.After; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -65,6 +66,7 @@ import static org.junit.Assert.fail; * mode at each step of migration as well as that when the apply response is sent that the memtable actually contains * the data that the apply should have applied */ +@Ignore("flaky") @RunWith(Parameterized.class) public class AccordWriteInteroperabilityTest extends AccordTestBase { diff --git a/test/distributed/org/apache/cassandra/distributed/test/accord/journal/AccordJournalReplayTest.java b/test/distributed/org/apache/cassandra/distributed/test/accord/journal/AccordJournalReplayTest.java index 2c95350789..44bcc0f306 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/accord/journal/AccordJournalReplayTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/accord/journal/AccordJournalReplayTest.java @@ -63,7 +63,7 @@ import org.apache.cassandra.service.accord.AccordCommandStore; import org.apache.cassandra.service.accord.AccordService; import org.apache.cassandra.service.accord.AccordTestUtils; import org.apache.cassandra.service.accord.api.PartitionKey; -import org.apache.cassandra.service.accord.serializers.ResultSerializers; +import org.apache.cassandra.service.accord.txn.TxnDataResult; import org.apache.cassandra.utils.ByteBufferUtil; import static org.apache.cassandra.Util.spinUntilTrue; @@ -128,7 +128,7 @@ public class AccordJournalReplayTest extends TestBaseImpl waitingOn = new Command.WaitingOn(RoutingKeys.EMPTY, deps.rangeDeps, new ImmutableBitSet(waitingOnBits), null); } Writes writes = new Writes(txnId, txnId, Keys.of(key), null); - Command command = Command.Executed.executed(txnId, SaveStatus.PreApplied, Status.Durability.NotDurable, StoreParticipants.execute(commandStore.unsafeGetRangesForEpoch(), route, txnId, txnId.epoch()), Ballot.ZERO, txnId, txn.intersecting(route, true), deps.intersecting(route), Ballot.ZERO, waitingOn, writes, ResultSerializers.APPLIED); + Command command = Command.Executed.executed(txnId, SaveStatus.PreApplied, Status.Durability.NotDurable, StoreParticipants.execute(commandStore.unsafeGetRangesForEpoch(), route, txnId, txnId.epoch()), Ballot.ZERO, txnId, txn.intersecting(route, true), deps.intersecting(route), Ballot.ZERO, waitingOn, writes, TxnDataResult.PERSISTABLE); commandStore.journal.saveCommand(commandStore.id(), new Journal.CommandUpdate(null, command), () -> {}); SyncPoint syncPoint = AccordService.getBlocking(CoordinateSyncPoint.exclusive(node, syncPointId, Ranges.of(key.asRange()))); diff --git a/test/distributed/org/apache/cassandra/service/accord/journal/AccordJournalBurnTest.java b/test/distributed/org/apache/cassandra/service/accord/journal/AccordJournalBurnTest.java index 1e078ede8b..ab4ee9b396 100644 --- a/test/distributed/org/apache/cassandra/service/accord/journal/AccordJournalBurnTest.java +++ b/test/distributed/org/apache/cassandra/service/accord/journal/AccordJournalBurnTest.java @@ -87,9 +87,9 @@ import org.apache.cassandra.service.accord.JournalKey; import org.apache.cassandra.service.accord.serializers.CommandSerializers; import org.apache.cassandra.service.accord.serializers.DepsSerializers; import org.apache.cassandra.service.accord.serializers.KeySerializers; -import org.apache.cassandra.service.accord.serializers.ResultSerializers; import org.apache.cassandra.service.accord.serializers.TopologySerializers; import org.apache.cassandra.service.accord.serializers.Version; +import org.apache.cassandra.service.accord.txn.TxnDataResult; import org.apache.cassandra.tools.FieldUtil; import org.apache.cassandra.utils.CloseableIterator; @@ -123,9 +123,9 @@ public class AccordJournalBurnTest extends BurnTestBase FieldUtil.transferFields(new DepsSerializers.Impl(BurnTestKeySerializers.range), DepsSerializers.class); - FieldUtil.setInstanceUnsafe(ResultSerializers.class, + FieldUtil.setInstanceUnsafe(TxnDataResult.class, BurnTestKeySerializers.result, - "result"); + "persistable"); FieldUtil.setInstanceUnsafe(TopologySerializers.class, new TopologySerializers.ShardSerializer(BurnTestKeySerializers.range), diff --git a/test/harry/main/org/apache/cassandra/io/sstable/HarrySSTableWriter.java b/test/harry/main/org/apache/cassandra/io/sstable/HarrySSTableWriter.java index 020c3ac6df..9036afc7ca 100644 --- a/test/harry/main/org/apache/cassandra/io/sstable/HarrySSTableWriter.java +++ b/test/harry/main/org/apache/cassandra/io/sstable/HarrySSTableWriter.java @@ -43,7 +43,8 @@ import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.cql3.ColumnSpecification; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.cql3.QueryProcessor; -import org.apache.cassandra.cql3.UpdateParameters; +import org.apache.cassandra.cql3.RowUpdateBuilder; +import org.apache.cassandra.cql3.RowUpdateBuilder.RegularRowUpdateBuilder; import org.apache.cassandra.cql3.functions.types.TypeCodec; import org.apache.cassandra.cql3.statements.ModificationStatement; import org.apache.cassandra.cql3.statements.schema.CreateIndexStatement; @@ -173,13 +174,13 @@ public class HarrySSTableWriter implements Closeable long now = currentTimeMillis(); // Note that we asks indexes to not validate values (the last 'false' arg below) because that triggers a 'Keyspace.open' // and that forces a lot of initialization that we don't want. - UpdateParameters params = new UpdateParameters(modificationStatement.metadata, - ClientState.forInternalCalls(), - options, - modificationStatement.getTimestamp(TimeUnit.MILLISECONDS.toMicros(now), options), - options.getNowInSec((int) TimeUnit.MILLISECONDS.toSeconds(now)), - modificationStatement.getTimeToLive(options), - Collections.emptyMap()); + RowUpdateBuilder params = new RegularRowUpdateBuilder(modificationStatement.metadata, + ClientState.forInternalCalls(), + options, + modificationStatement.getTimestamp(TimeUnit.MILLISECONDS.toMicros(now), options), + options.getNowInSec((int) TimeUnit.MILLISECONDS.toSeconds(now)), + modificationStatement.getTimeToLive(options), + Collections.emptyMap()); try { diff --git a/test/unit/org/apache/cassandra/cql3/functions/FunctionFactoryTest.java b/test/unit/org/apache/cassandra/cql3/functions/FunctionFactoryTest.java index 8db1c182ac..19575559ef 100644 --- a/test/unit/org/apache/cassandra/cql3/functions/FunctionFactoryTest.java +++ b/test/unit/org/apache/cassandra/cql3/functions/FunctionFactoryTest.java @@ -32,10 +32,10 @@ import org.junit.Test; import org.apache.cassandra.cql3.CQLTester; import org.apache.cassandra.cql3.Duration; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.UntypedResultSet; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.UTF8Type; -import org.apache.cassandra.transport.ProtocolVersion; import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.TimeUUID; @@ -53,9 +53,9 @@ public class FunctionFactoryTest extends CQLTester return new NativeScalarFunction(name.name, argTypes.get(0), argTypes.get(0)) { @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return FunctionArguments.newNoopInstance(version, 1); + return FunctionArguments.newNoopInstance(context, 1); } @Override diff --git a/test/unit/org/apache/cassandra/cql3/functions/TimeFctsTest.java b/test/unit/org/apache/cassandra/cql3/functions/TimeFctsTest.java index ac49673eb5..6ef536f19c 100644 --- a/test/unit/org/apache/cassandra/cql3/functions/TimeFctsTest.java +++ b/test/unit/org/apache/cassandra/cql3/functions/TimeFctsTest.java @@ -27,11 +27,11 @@ import java.util.Date; import org.junit.Test; +import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.db.marshal.LongType; import org.apache.cassandra.db.marshal.SimpleDateType; import org.apache.cassandra.db.marshal.TimeUUIDType; import org.apache.cassandra.db.marshal.TimestampType; -import org.apache.cassandra.transport.ProtocolVersion; import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.TimeUUID; @@ -188,7 +188,7 @@ public class TimeFctsTest private static ByteBuffer executeFunction(Function function, ByteBuffer input) { - Arguments arguments = function.newArguments(ProtocolVersion.CURRENT); + Arguments arguments = function.newArguments(QueryOptions.DEFAULT); arguments.set(0, input); return ((ScalarFunction) function).execute(arguments); } diff --git a/test/unit/org/apache/cassandra/cql3/terms/ListsTest.java b/test/unit/org/apache/cassandra/cql3/terms/ListsTest.java index 3e3180e6b8..77e7c6b7b1 100644 --- a/test/unit/org/apache/cassandra/cql3/terms/ListsTest.java +++ b/test/unit/org/apache/cassandra/cql3/terms/ListsTest.java @@ -20,7 +20,6 @@ package org.apache.cassandra.cql3.terms; import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.UUID; @@ -31,7 +30,8 @@ import org.junit.Test; import org.apache.cassandra.cql3.CQLTester; import org.apache.cassandra.cql3.QueryOptions; -import org.apache.cassandra.cql3.UpdateParameters; +import org.apache.cassandra.cql3.RowUpdateBuilder; +import org.apache.cassandra.cql3.RowUpdateBuilder.RegularRowUpdateBuilder; import org.apache.cassandra.cql3.terms.Lists.PrecisionTime; import org.apache.cassandra.db.Clustering; import org.apache.cassandra.db.DecoratedKey; @@ -147,13 +147,13 @@ public class ListsTest extends CQLTester ByteBuffer keyBuf = ByteBufferUtil.bytes("key"); DecoratedKey key = Murmur3Partitioner.instance.decorateKey(keyBuf); - UpdateParameters parameters = new UpdateParameters(metaData, - ClientState.forInternalCalls(), - QueryOptions.DEFAULT, - System.currentTimeMillis(), - FBUtilities.nowInSeconds(), - 1000, - Collections.emptyMap()); + RowUpdateBuilder parameters = new RegularRowUpdateBuilder(metaData, + ClientState.forInternalCalls(), + QueryOptions.DEFAULT, + System.currentTimeMillis(), + FBUtilities.nowInSeconds(), + 1000, + null); Clustering clustering = Clustering.make(ByteBufferUtil.bytes(1)); parameters.newRow(clustering); prepender.execute(key, parameters); diff --git a/test/unit/org/apache/cassandra/cql3/validation/miscellaneous/CrcCheckChanceTest.java b/test/unit/org/apache/cassandra/cql3/validation/miscellaneous/CrcCheckChanceTest.java index 385b777a10..42a2f73646 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/miscellaneous/CrcCheckChanceTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/miscellaneous/CrcCheckChanceTest.java @@ -188,7 +188,7 @@ public class CrcCheckChanceTest extends CQLTester } DatabaseDescriptor.setCompactionThroughputMebibytesPerSec(1); - List> futures = CompactionManager.instance.submitMaximal(cfs, CompactionManager.getDefaultGcBefore(cfs, FBUtilities.nowInSeconds()), false, 1); + List> futures = CompactionManager.instance.submitMaximal(cfs, cfs.getDefaultGcBefore(FBUtilities.nowInSeconds()), false, 1); execute("DROP TABLE %s"); try diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/CQLVectorTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/CQLVectorTest.java index da27781332..4d959b9f70 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/operations/CQLVectorTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/CQLVectorTest.java @@ -27,6 +27,7 @@ import org.junit.Assert; import org.junit.Test; import org.apache.cassandra.cql3.CQLTester; +import org.apache.cassandra.cql3.FunctionContext; import org.apache.cassandra.cql3.functions.Arguments; import org.apache.cassandra.cql3.functions.FunctionArguments; import org.apache.cassandra.cql3.functions.NativeFunctions; @@ -36,7 +37,6 @@ import org.apache.cassandra.db.marshal.Int32Type; import org.apache.cassandra.db.marshal.VectorType; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.schema.SchemaKeyspace; -import org.apache.cassandra.transport.ProtocolVersion; import org.apache.cassandra.utils.ByteBufferUtil; import static java.lang.String.format; @@ -337,9 +337,9 @@ public class CQLVectorTest extends CQLTester.InMemory } @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return FunctionArguments.newNoopInstance(version, 1); + return FunctionArguments.newNoopInstance(context, 1); } }); @@ -371,9 +371,9 @@ public class CQLVectorTest extends CQLTester.InMemory } @Override - public Arguments newArguments(ProtocolVersion version) + public Arguments newArguments(FunctionContext context) { - return new FunctionArguments(version, + return new FunctionArguments(context, (v, b) -> type.composeAsFloat(b), (v, b) -> type.composeAsFloat(b)); } diff --git a/test/unit/org/apache/cassandra/db/compaction/CompactionsCQLTest.java b/test/unit/org/apache/cassandra/db/compaction/CompactionsCQLTest.java index 3bd133ca8d..e3a4b3f9b2 100644 --- a/test/unit/org/apache/cassandra/db/compaction/CompactionsCQLTest.java +++ b/test/unit/org/apache/cassandra/db/compaction/CompactionsCQLTest.java @@ -622,7 +622,7 @@ public class CompactionsCQLTest extends CQLTester { public NotifyingCompactionTask(LeveledCompactionTask task) { - super(task.cfs, task.transaction, task.getLevel(), task.gcBefore, task.getLevel(), false); + super(task.cfs, task.transaction, task.getLevel(), task.gcBeforeSeconds, task.getLevel(), false); } @Override diff --git a/test/unit/org/apache/cassandra/hints/HintsServiceTest.java b/test/unit/org/apache/cassandra/hints/HintsServiceTest.java index 610b534082..51b1fd1aac 100644 --- a/test/unit/org/apache/cassandra/hints/HintsServiceTest.java +++ b/test/unit/org/apache/cassandra/hints/HintsServiceTest.java @@ -61,7 +61,7 @@ import org.apache.cassandra.service.accord.AccordResult; import org.apache.cassandra.service.accord.AccordTestUtils; import org.apache.cassandra.service.accord.IAccordService.IAccordResult; import org.apache.cassandra.service.accord.TimeOnlyRequestBookkeeping.LatencyRequestBookkeeping; -import org.apache.cassandra.service.accord.txn.TxnData; +import org.apache.cassandra.service.accord.txn.TxnDataResult; import org.apache.cassandra.service.accord.txn.TxnResult; import org.apache.cassandra.service.consensus.migration.ConsensusMigrationMutationHelper; import org.apache.cassandra.tcm.ClusterMetadata; @@ -270,7 +270,7 @@ public class HintsServiceTest accordTxnCount.incrementAndGet(); TxnId txnId = AccordTestUtils.txnId(42, 43, 44); AccordResult result = new AccordResult<>(txnId, Keys.EMPTY, new LatencyRequestBookkeeping(null), requestTime.startedAtNanos(), requestTime.startedAtNanos(), true, null); - result.accept(new TxnData(), null); + result.accept(new TxnDataResult(txnId.hlc()), null); return result; } }); diff --git a/test/unit/org/apache/cassandra/service/accord/AccordCommandStoreTest.java b/test/unit/org/apache/cassandra/service/accord/AccordCommandStoreTest.java index 102485011d..f3d94e7827 100644 --- a/test/unit/org/apache/cassandra/service/accord/AccordCommandStoreTest.java +++ b/test/unit/org/apache/cassandra/service/accord/AccordCommandStoreTest.java @@ -65,7 +65,7 @@ import org.apache.cassandra.service.accord.AccordKeyspace.CommandsForKeyAccessor import org.apache.cassandra.service.accord.api.PartitionKey; import org.apache.cassandra.service.accord.api.TokenKey; import org.apache.cassandra.service.accord.serializers.CommandsForKeySerializerTest.TestSafeCommandStore; -import org.apache.cassandra.service.accord.serializers.ResultSerializers; +import org.apache.cassandra.service.accord.txn.TxnDataResult; import org.apache.cassandra.service.accord.txn.TxnUpdate; import org.apache.cassandra.service.consensus.TransactionalMode; import org.apache.cassandra.utils.Pair; @@ -138,7 +138,7 @@ public class AccordCommandStoreTest Command expected = Command.Executed.executed(txnId, SaveStatus.Applied, AllQuorums, StoreParticipants.all(route), promised, executeAt, txn, dependencies, accepted, - waitingOn, result.left, ResultSerializers.APPLIED); + waitingOn, result.left, TxnDataResult.PERSISTABLE); AccordSafeCommand safeCommand = new AccordSafeCommand(loaded(txnId, null)); safeCommand.set(expected); // In practice we should never need to save it with the condition boolean set diff --git a/test/unit/org/apache/cassandra/service/accord/serializers/CommandsForKeySerializerTest.java b/test/unit/org/apache/cassandra/service/accord/serializers/CommandsForKeySerializerTest.java index ca1c1a4e30..016bde0fe9 100644 --- a/test/unit/org/apache/cassandra/service/accord/serializers/CommandsForKeySerializerTest.java +++ b/test/unit/org/apache/cassandra/service/accord/serializers/CommandsForKeySerializerTest.java @@ -120,6 +120,7 @@ import org.apache.cassandra.schema.TableId; import org.apache.cassandra.service.StorageService; import org.apache.cassandra.service.accord.api.AccordAgent; import org.apache.cassandra.service.accord.api.TokenKey; +import org.apache.cassandra.service.accord.txn.TxnResult; import org.apache.cassandra.service.accord.txn.TxnWrite; import org.apache.cassandra.simulator.RandomSource.Choices; import org.apache.cassandra.utils.AccordGenerators; @@ -221,7 +222,7 @@ public class CommandsForKeySerializerTest { if (txnId.is(Kind.Write)) builder.writes(new Writes(txnId, executeAt, txn.keys(), new TxnWrite(TableMetadatas.none(), Collections.emptyList(), SimpleBitSets.allSet(1)))); - builder.result(ResultSerializers.APPLIED); + builder.result(TxnResult.PERSISTABLE); } return builder; } diff --git a/test/unit/org/apache/cassandra/utils/AccordGenerators.java b/test/unit/org/apache/cassandra/utils/AccordGenerators.java index 7019e05c68..3b936ece0b 100644 --- a/test/unit/org/apache/cassandra/utils/AccordGenerators.java +++ b/test/unit/org/apache/cassandra/utils/AccordGenerators.java @@ -84,9 +84,9 @@ import org.apache.cassandra.service.accord.AccordTestUtils; import org.apache.cassandra.service.accord.TokenRange; import org.apache.cassandra.service.accord.api.PartitionKey; import org.apache.cassandra.service.accord.api.TokenKey; -import org.apache.cassandra.service.accord.serializers.ResultSerializers; import org.apache.cassandra.service.accord.serializers.TableMetadatas; import org.apache.cassandra.service.accord.topology.FetchTopologies; +import org.apache.cassandra.service.accord.txn.TxnResult; import org.apache.cassandra.service.accord.txn.TxnWrite; import static accord.local.CommandStores.RangesForEpoch; @@ -292,7 +292,7 @@ public class AccordGenerators { if (txnId.is(Write)) builder.writes(new Writes(txnId, executeAt, keysOrRanges, new TxnWrite(TableMetadatas.none(), Collections.emptyList(), SimpleBitSets.allSet(1)))); - builder.result(ResultSerializers.APPLIED); + builder.result(TxnResult.PERSISTABLE); } return builder.build(saveStatus); } diff --git a/tools/stress/src/org/apache/cassandra/io/sstable/StressCQLSSTableWriter.java b/tools/stress/src/org/apache/cassandra/io/sstable/StressCQLSSTableWriter.java index fc76b34548..c4996f57ee 100644 --- a/tools/stress/src/org/apache/cassandra/io/sstable/StressCQLSSTableWriter.java +++ b/tools/stress/src/org/apache/cassandra/io/sstable/StressCQLSSTableWriter.java @@ -40,7 +40,8 @@ import org.apache.cassandra.cql3.CQLStatement; import org.apache.cassandra.cql3.ColumnSpecification; import org.apache.cassandra.cql3.CqlParser; import org.apache.cassandra.cql3.QueryOptions; -import org.apache.cassandra.cql3.UpdateParameters; +import org.apache.cassandra.cql3.RowUpdateBuilder; +import org.apache.cassandra.cql3.RowUpdateBuilder.RegularRowUpdateBuilder; import org.apache.cassandra.cql3.functions.types.TypeCodec; import org.apache.cassandra.cql3.statements.UpdateStatement; import org.apache.cassandra.cql3.statements.schema.CreateTableStatement; @@ -265,20 +266,20 @@ public class StressCQLSSTableWriter implements Closeable long now = currentTimeMillis(); // Note that we asks indexes to not validate values (the last 'false' arg below) because that triggers a 'Keyspace.open' // and that forces a lot of initialization that we don't want. - UpdateParameters params = new UpdateParameters(insert.metadata(), - ClientState.forInternalCalls(), - options, - insert.getTimestamp(TimeUnit.MILLISECONDS.toMicros(now), options), - (int) TimeUnit.MILLISECONDS.toSeconds(now), - insert.getTimeToLive(options), - Collections.emptyMap()); + RowUpdateBuilder builder = new RegularRowUpdateBuilder(insert.metadata(), + ClientState.forInternalCalls(), + options, + insert.getTimestamp(TimeUnit.MILLISECONDS.toMicros(now), options), + (int) TimeUnit.MILLISECONDS.toSeconds(now), + insert.getTimeToLive(options), + null); try { for (ByteBuffer key : keys) { for (Clustering clustering : clusterings) - insert.addUpdateForKey(writer.getUpdateFor(key), clustering, params); + insert.addUpdateForKey(writer.getUpdateFor(key), clustering, builder); } return this; }