/*
 * 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.statements;

import java.nio.ByteBuffer;
import java.util.*;

import com.google.common.collect.Iterables;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.apache.cassandra.auth.Permission;
import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.ColumnDefinition;
import org.apache.cassandra.config.Schema;
import org.apache.cassandra.config.ColumnDefinition.Raw;
import org.apache.cassandra.config.ViewDefinition;
import org.apache.cassandra.cql3.*;
import org.apache.cassandra.cql3.functions.Function;
import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
import org.apache.cassandra.cql3.selection.Selection;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.filter.*;
import org.apache.cassandra.db.marshal.BooleanType;
import org.apache.cassandra.db.partitions.*;
import org.apache.cassandra.db.rows.RowIterator;
import org.apache.cassandra.db.view.View;
import org.apache.cassandra.exceptions.*;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.service.StorageProxy;
import org.apache.cassandra.service.paxos.Commit;
import org.apache.cassandra.thrift.ThriftValidation;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.triggers.TriggerExecutor;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.Pair;
import org.apache.cassandra.utils.UUIDGen;

import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
import static org.apache.cassandra.cql3.statements.RequestValidations.checkNull;

/*
 * Abstract parent class of individual modifications, i.e. INSERT, UPDATE and DELETE.
 */
public abstract class ModificationStatement implements CQLStatement
{
    protected static final Logger logger = LoggerFactory.getLogger(ModificationStatement.class);

    public static final String CUSTOM_EXPRESSIONS_NOT_ALLOWED =
        "Custom index expressions cannot be used in WHERE clauses for UPDATE or DELETE statements";

    private static final ColumnIdentifier CAS_RESULT_COLUMN = new ColumnIdentifier("[applied]", false);

    protected final StatementType type;

    private final int boundTerms;
    public final CFMetaData cfm;
    private final Attributes attrs;

    private final StatementRestrictions restrictions;

    private final Operations operations;

    private final PartitionColumns updatedColumns;

    private final Conditions conditions;

    private final PartitionColumns conditionColumns;

    private final PartitionColumns requiresRead;

    public ModificationStatement(StatementType type,
                                 int boundTerms,
                                 CFMetaData cfm,
                                 Operations operations,
                                 StatementRestrictions restrictions,
                                 Conditions conditions,
                                 Attributes attrs)
    {
        this.type = type;
        this.boundTerms = boundTerms;
        this.cfm = cfm;
        this.restrictions = restrictions;
        this.operations = operations;
        this.conditions = conditions;
        this.attrs = attrs;

        if (!conditions.isEmpty())
        {
            checkFalse(cfm.isCounter(), "Conditional updates are not supported on counter tables");
            checkFalse(attrs.isTimestampSet(), "Cannot provide custom timestamp for conditional updates");
        }

        PartitionColumns.Builder conditionColumnsBuilder = PartitionColumns.builder();
        Iterable<ColumnDefinition> columns = conditions.getColumns();
        if (columns != null)
            conditionColumnsBuilder.addAll(columns);

        PartitionColumns.Builder updatedColumnsBuilder = PartitionColumns.builder();
        PartitionColumns.Builder requiresReadBuilder = PartitionColumns.builder();
        for (Operation operation : operations)
        {
            updatedColumnsBuilder.add(operation.column);
            // If the operation requires a read-before-write and we're doing a conditional read, we want to read
            // the affected column as part of the read-for-conditions paxos phase (see #7499).
            if (operation.requiresRead())
            {
                conditionColumnsBuilder.add(operation.column);
                requiresReadBuilder.add(operation.column);
            }
        }

        PartitionColumns modifiedColumns = updatedColumnsBuilder.build();
        // Compact tables have not row marker. So if we don't actually update any particular column,
        // this means that we're only updating the PK, which we allow if only those were declared in
        // the definition. In that case however, we do went to write the compactValueColumn (since again
        // we can't use a "row marker") so add it automatically.
        if (cfm.isCompactTable() && modifiedColumns.isEmpty() && updatesRegularRows())
            modifiedColumns = cfm.partitionColumns();

        this.updatedColumns = modifiedColumns;
        this.conditionColumns = conditionColumnsBuilder.build();
        this.requiresRead = requiresReadBuilder.build();
    }

    public Iterable<Function> getFunctions()
    {
        List<Function> functions = new ArrayList<>();
        addFunctionsTo(functions);
        return functions;
    }

    public void addFunctionsTo(List<Function> functions)
    {
        attrs.addFunctionsTo(functions);
        restrictions.addFunctionsTo(functions);
        operations.addFunctionsTo(functions);
        conditions.addFunctionsTo(functions);
    }

    /*
     * May be used by QueryHandler implementations
     */
    public StatementRestrictions getRestrictions()
    {
        return restrictions;
    }

    public abstract void addUpdateForKey(PartitionUpdate update, Clustering clustering, UpdateParameters params);

    public abstract void addUpdateForKey(PartitionUpdate update, Slice slice, UpdateParameters params);

    public int getBoundTerms()
    {
        return boundTerms;
    }

    public String keyspace()
    {
        return cfm.ksName;
    }

    public String columnFamily()
    {
        return cfm.cfName;
    }

    public boolean isCounter()
    {
        return cfm.isCounter();
    }

    public boolean isView()
    {
        return cfm.isView();
    }

    public long getTimestamp(long now, QueryOptions options) throws InvalidRequestException
    {
        return attrs.getTimestamp(now, options);
    }

    public boolean isTimestampSet()
    {
        return attrs.isTimestampSet();
    }

    public int getTimeToLive(QueryOptions options) throws InvalidRequestException
    {
        return attrs.getTimeToLive(options, cfm);
    }

    public void checkAccess(ClientState state) throws InvalidRequestException, UnauthorizedException
    {
        state.hasColumnFamilyAccess(cfm, Permission.MODIFY);

        // CAS updates can be used to simulate a SELECT query, so should require Permission.SELECT as well.
        if (hasConditions())
            state.hasColumnFamilyAccess(cfm, Permission.SELECT);

        // MV updates need to get the current state from the table, and might update the views
        // Require Permission.SELECT on the base table, and Permission.MODIFY on the views
        Iterator<ViewDefinition> views = View.findAll(keyspace(), columnFamily()).iterator();
        if (views.hasNext())
        {
            state.hasColumnFamilyAccess(cfm, Permission.SELECT);
            do
            {
                state.hasColumnFamilyAccess(views.next().metadata, Permission.MODIFY);
            } while (views.hasNext());
        }

        for (Function function : getFunctions())
            state.ensureHasPermission(Permission.EXECUTE, function);
    }

    public void validate(ClientState state) throws InvalidRequestException
    {
        checkFalse(hasConditions() && attrs.isTimestampSet(), "Cannot provide custom timestamp for conditional updates");
        checkFalse(isCounter() && attrs.isTimestampSet(), "Cannot provide custom timestamp for counter updates");
        checkFalse(isCounter() && attrs.isTimeToLiveSet(), "Cannot provide custom TTL for counter updates");
        checkFalse(isView(), "Cannot directly modify a materialized view");
    }

    public PartitionColumns updatedColumns()
    {
        return updatedColumns;
    }

    public PartitionColumns conditionColumns()
    {
        return conditionColumns;
    }

    public boolean updatesRegularRows()
    {
        // We're updating regular rows if all the clustering columns are provided.
        // Note that the only case where we're allowed not to provide clustering
        // columns is if we set some static columns, and in that case no clustering
        // columns should be given. So in practice, it's enough to check if we have
        // either the table has no clustering or if it has at least one of them set.
        return cfm.clusteringColumns().isEmpty() || restrictions.hasClusteringColumnsRestrictions();
    }

    public boolean updatesStaticRow()
    {
        return operations.appliesToStaticColumns();
    }

    public List<Operation> getRegularOperations()
    {
        return operations.regularOperations();
    }

    public List<Operation> getStaticOperations()
    {
        return operations.staticOperations();
    }

    public Iterable<Operation> allOperations()
    {
        return operations;
    }

    public Iterable<ColumnDefinition> getColumnsWithConditions()
    {
         return conditions.getColumns();
    }

    public boolean hasIfNotExistCondition()
    {
        return conditions.isIfNotExists();
    }

    public boolean hasIfExistCondition()
    {
        return conditions.isIfExists();
    }

    public List<ByteBuffer> buildPartitionKeyNames(QueryOptions options)
    throws InvalidRequestException
    {
        List<ByteBuffer> partitionKeys = restrictions.getPartitionKeys(options);
        for (ByteBuffer key : partitionKeys)
            QueryProcessor.validateKey(key);

        return partitionKeys;
    }

    public NavigableSet<Clustering> createClustering(QueryOptions options)
    throws InvalidRequestException
    {
        if (appliesOnlyToStaticColumns() && !restrictions.hasClusteringColumnsRestrictions())
            return FBUtilities.singleton(CBuilder.STATIC_BUILDER.build(), cfm.comparator);

        return restrictions.getClusteringColumns(options);
    }

    
Checks that the modification only apply to static columns.
Returns:true if the modification only apply to static columns, false otherwise.
/** * Checks that the modification only apply to static columns. * @return <code>true</code> if the modification only apply to static columns, <code>false</code> otherwise. */
private boolean appliesOnlyToStaticColumns() { return appliesOnlyToStaticColumns(operations, conditions); }
Checks that the specified operations and conditions only apply to static columns.
Returns:true if the specified operations and conditions only apply to static columns, false otherwise.
/** * Checks that the specified operations and conditions only apply to static columns. * @return <code>true</code> if the specified operations and conditions only apply to static columns, * <code>false</code> otherwise. */
public static boolean appliesOnlyToStaticColumns(Operations operation, Conditions conditions) { return !operation.appliesToRegularColumns() && !conditions.appliesToRegularColumns() && (operation.appliesToStaticColumns() || conditions.appliesToStaticColumns()); } public boolean requiresRead() { // Lists SET operation incurs a read. for (Operation op : allOperations()) if (op.requiresRead()) return true; return false; } private Map<DecoratedKey, Partition> readRequiredLists(Collection<ByteBuffer> partitionKeys, ClusteringIndexFilter filter, DataLimits limits, boolean local, ConsistencyLevel cl, long queryStartNanoTime) { if (!requiresRead()) return null; try { cl.validateForRead(keyspace()); } catch (InvalidRequestException e) { throw new InvalidRequestException(String.format("Write operation require a read but consistency %s is not supported on reads", cl)); } List<SinglePartitionReadCommand> commands = new ArrayList<>(partitionKeys.size()); int nowInSec = FBUtilities.nowInSeconds(); for (ByteBuffer key : partitionKeys) commands.add(SinglePartitionReadCommand.create(cfm, nowInSec, ColumnFilter.selection(this.requiresRead), RowFilter.NONE, limits, cfm.decorateKey(key), filter)); SinglePartitionReadCommand.Group group = new SinglePartitionReadCommand.Group(commands, DataLimits.NONE); if (local) { try (ReadExecutionController executionController = group.executionController(); PartitionIterator iter = group.executeInternal(executionController)) { return asMaterializedMap(iter); } } try (PartitionIterator iter = group.execute(cl, null, queryStartNanoTime)) { return asMaterializedMap(iter); } } private Map<DecoratedKey, Partition> asMaterializedMap(PartitionIterator iterator) { Map<DecoratedKey, Partition> map = new HashMap<>(); while (iterator.hasNext()) { try (RowIterator partition = iterator.next()) { map.put(partition.partitionKey(), FilteredPartition.create(partition)); } } return map; } public boolean hasConditions() { return !conditions.isEmpty(); } public boolean hasSlices() { return type.allowClusteringColumnSlices() && getRestrictions().hasClusteringColumnsRestrictions() && getRestrictions().isColumnRange(); } public ResultMessage execute(QueryState queryState, QueryOptions options, long queryStartNanoTime) throws RequestExecutionException, RequestValidationException { if (options.getConsistency() == null) throw new InvalidRequestException("Invalid empty consistency level"); return hasConditions() ? executeWithCondition(queryState, options, queryStartNanoTime) : executeWithoutCondition(queryState, options, queryStartNanoTime); } private ResultMessage executeWithoutCondition(QueryState queryState, QueryOptions options, long queryStartNanoTime) throws RequestExecutionException, RequestValidationException { ConsistencyLevel cl = options.getConsistency(); if (isCounter()) cl.validateCounterForWrite(cfm); else cl.validateForWrite(cfm.ksName); Collection<? extends IMutation> mutations = getMutations(options, false, options.getTimestamp(queryState), queryStartNanoTime); if (!mutations.isEmpty()) StorageProxy.mutateWithTriggers(mutations, cl, false, queryStartNanoTime); return null; } public ResultMessage executeWithCondition(QueryState queryState, QueryOptions options, long queryStartNanoTime) throws RequestExecutionException, RequestValidationException { CQL3CasRequest request = makeCasRequest(queryState, options); try (RowIterator result = StorageProxy.cas(keyspace(), columnFamily(), request.key, request, options.getSerialConsistency(), options.getConsistency(), queryState.getClientState(), queryStartNanoTime)) { return new ResultMessage.Rows(buildCasResultSet(result, options)); } } private CQL3CasRequest makeCasRequest(QueryState queryState, QueryOptions options) { List<ByteBuffer> keys = buildPartitionKeyNames(options); // We don't support IN for CAS operation so far checkFalse(restrictions.keyIsInRelation(), "IN on the partition key is not supported with conditional %s", type.isUpdate()? "updates" : "deletions"); DecoratedKey key = cfm.decorateKey(keys.get(0)); long now = options.getTimestamp(queryState); checkFalse(restrictions.clusteringKeyRestrictionsHasIN(), "IN on the clustering key columns is not supported with conditional %s", type.isUpdate()? "updates" : "deletions"); Clustering clustering = Iterables.getOnlyElement(createClustering(options)); CQL3CasRequest request = new CQL3CasRequest(cfm, key, false, conditionColumns(), updatesRegularRows(), updatesStaticRow()); addConditions(clustering, request, options); request.addRowUpdate(clustering, this, options, now); return request; } public void addConditions(Clustering clustering, CQL3CasRequest request, QueryOptions options) throws InvalidRequestException { conditions.addConditionsTo(request, clustering, options); } private ResultSet buildCasResultSet(RowIterator partition, QueryOptions options) throws InvalidRequestException { return buildCasResultSet(keyspace(), columnFamily(), partition, getColumnsWithConditions(), false, options); } public static ResultSet buildCasResultSet(String ksName, String tableName, RowIterator partition, Iterable<ColumnDefinition> columnsWithConditions, boolean isBatch, QueryOptions options) throws InvalidRequestException { boolean success = partition == null; ColumnSpecification spec = new ColumnSpecification(ksName, tableName, CAS_RESULT_COLUMN, BooleanType.instance); ResultSet.ResultMetadata metadata = new ResultSet.ResultMetadata(Collections.singletonList(spec)); List<List<ByteBuffer>> rows = Collections.singletonList(Collections.singletonList(BooleanType.instance.decompose(success))); ResultSet rs = new ResultSet(metadata, rows); return success ? rs : merge(rs, buildCasFailureResultSet(partition, columnsWithConditions, isBatch, options)); } private static ResultSet merge(ResultSet left, ResultSet right) { if (left.size() == 0) return right; else if (right.size() == 0) return left; assert left.size() == 1; int size = left.metadata.names.size() + right.metadata.names.size(); List<ColumnSpecification> specs = new ArrayList<ColumnSpecification>(size); specs.addAll(left.metadata.names); specs.addAll(right.metadata.names); List<List<ByteBuffer>> rows = new ArrayList<>(right.size()); for (int i = 0; i < right.size(); i++) { List<ByteBuffer> row = new ArrayList<ByteBuffer>(size); row.addAll(left.rows.get(0)); row.addAll(right.rows.get(i)); rows.add(row); } return new ResultSet(new ResultSet.ResultMetadata(specs), rows); } private static ResultSet buildCasFailureResultSet(RowIterator partition, Iterable<ColumnDefinition> columnsWithConditions, boolean isBatch, QueryOptions options) throws InvalidRequestException { CFMetaData cfm = partition.metadata(); Selection selection; if (columnsWithConditions == null) { selection = Selection.wildcard(cfm); } else { // We can have multiple conditions on the same columns (for collections) so use a set // to avoid duplicate, but preserve the order just to it follows the order of IF in the query in general Set<ColumnDefinition> defs = new LinkedHashSet<>(); // Adding the partition key for batches to disambiguate if the conditions span multipe rows (we don't add them outside // of batches for compatibility sakes). if (isBatch) { defs.addAll(cfm.partitionKeyColumns()); defs.addAll(cfm.clusteringColumns()); } if (cfm.isSuper() && cfm.isDense()) { defs.add(cfm.superColumnValueColumn()); } else { for (ColumnDefinition def : columnsWithConditions) defs.add(def); } selection = Selection.forColumns(cfm, new ArrayList<>(defs)); } Selection.ResultSetBuilder builder = selection.resultSetBuilder(options, false); SelectStatement.forSelection(cfm, selection).processPartition(partition, options, builder, FBUtilities.nowInSeconds()); return builder.build(); } public ResultMessage executeInternal(QueryState queryState, QueryOptions options) throws RequestValidationException, RequestExecutionException { return hasConditions() ? executeInternalWithCondition(queryState, options) : executeInternalWithoutCondition(queryState, options, System.nanoTime()); } public ResultMessage executeInternalWithoutCondition(QueryState queryState, QueryOptions options, long queryStartNanoTime) throws RequestValidationException, RequestExecutionException { for (IMutation mutation : getMutations(options, true, queryState.getTimestamp(), queryStartNanoTime)) mutation.apply(); return null; } public ResultMessage executeInternalWithCondition(QueryState state, QueryOptions options) throws RequestValidationException, RequestExecutionException { CQL3CasRequest request = makeCasRequest(state, options); try (RowIterator result = casInternal(request, state)) { return new ResultMessage.Rows(buildCasResultSet(result, options)); } } static RowIterator casInternal(CQL3CasRequest request, QueryState state) { UUID ballot = UUIDGen.getTimeUUIDFromMicros(state.getTimestamp()); SinglePartitionReadCommand readCommand = request.readCommand(FBUtilities.nowInSeconds()); FilteredPartition current; try (ReadExecutionController executionController = readCommand.executionController(); PartitionIterator iter = readCommand.executeInternal(executionController)) { current = FilteredPartition.create(PartitionIterators.getOnlyElement(iter, readCommand)); } if (!request.appliesTo(current)) return current.rowIterator(); PartitionUpdate updates = request.makeUpdates(current); updates = TriggerExecutor.instance.execute(updates); Commit proposal = Commit.newProposal(ballot, updates); proposal.makeMutation().apply(); return null; }
Convert statement into a list of mutations to apply on the server
Params:
  • options – value for prepared statement markers
  • local – if true, any requests (for collections) performed by getMutation should be done locally only.
  • now – the current timestamp in microseconds to use if no timestamp is user provided.
Returns:list of the mutations
/** * Convert statement into a list of mutations to apply on the server * * @param options value for prepared statement markers * @param local if true, any requests (for collections) performed by getMutation should be done locally only. * @param now the current timestamp in microseconds to use if no timestamp is user provided. * * @return list of the mutations */
private Collection<? extends IMutation> getMutations(QueryOptions options, boolean local, long now, long queryStartNanoTime) { UpdatesCollector collector = new UpdatesCollector(Collections.singletonMap(cfm.cfId, updatedColumns), 1); addUpdates(collector, options, local, now, queryStartNanoTime); collector.validateIndexedColumns(); return collector.toMutations(); } final void addUpdates(UpdatesCollector collector, QueryOptions options, boolean local, long now, long queryStartNanoTime) { List<ByteBuffer> keys = buildPartitionKeyNames(options); if (hasSlices()) { Slices slices = createSlices(options); // If all the ranges were invalid we do not need to do anything. if (slices.isEmpty()) return; UpdateParameters params = makeUpdateParameters(keys, new ClusteringIndexSliceFilter(slices, false), options, DataLimits.NONE, local, now, queryStartNanoTime); for (ByteBuffer key : keys) { ThriftValidation.validateKey(cfm, key); DecoratedKey dk = cfm.decorateKey(key); PartitionUpdate upd = collector.getPartitionUpdate(cfm, dk, options.getConsistency()); for (Slice slice : slices) addUpdateForKey(upd, slice, params); } } else { NavigableSet<Clustering> clusterings = createClustering(options); // If some of the restrictions were unspecified (e.g. empty IN restrictions) we do not need to do anything. if (restrictions.hasClusteringColumnsRestrictions() && clusterings.isEmpty()) return; UpdateParameters params = makeUpdateParameters(keys, clusterings, options, local, now, queryStartNanoTime); for (ByteBuffer key : keys) { ThriftValidation.validateKey(cfm, key); DecoratedKey dk = cfm.decorateKey(key); PartitionUpdate upd = collector.getPartitionUpdate(cfm, dk, options.getConsistency()); if (!restrictions.hasClusteringColumnsRestrictions()) { addUpdateForKey(upd, Clustering.EMPTY, params); } else { for (Clustering clustering : clusterings) { for (ByteBuffer c : clustering.getRawValues()) { if (c != null && c.remaining() > FBUtilities.MAX_UNSIGNED_SHORT) throw new InvalidRequestException(String.format("Key length of %d is longer than maximum of %d", clustering.dataSize(), FBUtilities.MAX_UNSIGNED_SHORT)); } addUpdateForKey(upd, clustering, params); } } } } } Slices createSlices(QueryOptions options) { SortedSet<ClusteringBound> startBounds = restrictions.getClusteringColumnsBounds(Bound.START, options); SortedSet<ClusteringBound> endBounds = restrictions.getClusteringColumnsBounds(Bound.END, options); return toSlices(startBounds, endBounds); } private UpdateParameters makeUpdateParameters(Collection<ByteBuffer> keys, NavigableSet<Clustering> clusterings, QueryOptions options, boolean local, long now, long queryStartNanoTime) { if (clusterings.contains(Clustering.STATIC_CLUSTERING)) return makeUpdateParameters(keys, new ClusteringIndexSliceFilter(Slices.ALL, false), options, DataLimits.cqlLimits(1), local, now, queryStartNanoTime); return makeUpdateParameters(keys, new ClusteringIndexNamesFilter(clusterings, false), options, DataLimits.NONE, local, now, queryStartNanoTime); } private UpdateParameters makeUpdateParameters(Collection<ByteBuffer> keys, ClusteringIndexFilter filter, QueryOptions options, DataLimits limits, boolean local, long now, long queryStartNanoTime) { // Some lists operation requires reading Map<DecoratedKey, Partition> lists = readRequiredLists(keys, filter, limits, local, options.getConsistency(), queryStartNanoTime); return new UpdateParameters(cfm, updatedColumns(), options, getTimestamp(now, options), getTimeToLive(options), lists); } private Slices toSlices(SortedSet<ClusteringBound> startBounds, SortedSet<ClusteringBound> endBounds) { assert startBounds.size() == endBounds.size(); Slices.Builder builder = new Slices.Builder(cfm.comparator); Iterator<ClusteringBound> starts = startBounds.iterator(); Iterator<ClusteringBound> ends = endBounds.iterator(); while (starts.hasNext()) { Slice slice = Slice.make(starts.next(), ends.next()); if (!slice.isEmpty(cfm.comparator)) { builder.add(slice); } } return builder.build(); } public static abstract class Parsed extends CFStatement { protected final StatementType type; private final Attributes.Raw attrs; private final List<Pair<ColumnDefinition.Raw, ColumnCondition.Raw>> conditions; private final boolean ifNotExists; private final boolean ifExists; protected Parsed(CFName name, StatementType type, Attributes.Raw attrs, List<Pair<ColumnDefinition.Raw, ColumnCondition.Raw>> conditions, boolean ifNotExists, boolean ifExists) { super(name); this.type = type; this.attrs = attrs; this.conditions = conditions == null ? Collections.<Pair<ColumnDefinition.Raw, ColumnCondition.Raw>>emptyList() : conditions; this.ifNotExists = ifNotExists; this.ifExists = ifExists; } public ParsedStatement.Prepared prepare(ClientState clientState) { VariableSpecifications boundNames = getBoundVariables(); ModificationStatement statement = prepare(boundNames, clientState); return new ParsedStatement.Prepared(statement, boundNames, boundNames.getPartitionKeyBindIndexes(statement.cfm)); } public ModificationStatement prepare(VariableSpecifications boundNames, ClientState clientState) { CFMetaData metadata = ThriftValidation.validateColumnFamilyWithCompactMode(keyspace(), columnFamily(), clientState.isNoCompactMode()); Attributes preparedAttributes = attrs.prepare(keyspace(), columnFamily()); preparedAttributes.collectMarkerSpecification(boundNames); Conditions preparedConditions = prepareConditions(metadata, boundNames); return prepareInternal(metadata, boundNames, preparedConditions, preparedAttributes); }
Returns the column conditions.
Params:
  • metadata – the column family meta data
  • boundNames – the bound names
Returns:the column conditions.
/** * Returns the column conditions. * * @param metadata the column family meta data * @param boundNames the bound names * @return the column conditions. */
private Conditions prepareConditions(CFMetaData metadata, VariableSpecifications boundNames) { // To have both 'IF EXISTS'/'IF NOT EXISTS' and some other conditions doesn't make sense. // So far this is enforced by the parser, but let's assert it for sanity if ever the parse changes. if (ifExists) { assert conditions.isEmpty(); assert !ifNotExists; return Conditions.IF_EXISTS_CONDITION; } if (ifNotExists) { assert conditions.isEmpty(); assert !ifExists; return Conditions.IF_NOT_EXISTS_CONDITION; } if (conditions.isEmpty()) return Conditions.EMPTY_CONDITION; return prepareColumnConditions(metadata, boundNames); }
Returns the column conditions.
Params:
  • metadata – the column family meta data
  • boundNames – the bound names
Returns:the column conditions.
/** * Returns the column conditions. * * @param metadata the column family meta data * @param boundNames the bound names * @return the column conditions. */
private ColumnConditions prepareColumnConditions(CFMetaData metadata, VariableSpecifications boundNames) { checkNull(attrs.timestamp, "Cannot provide custom timestamp for conditional updates"); ColumnConditions.Builder builder = ColumnConditions.newBuilder(); for (Pair<ColumnDefinition.Raw, ColumnCondition.Raw> entry : conditions) { ColumnDefinition def = entry.left.prepare(metadata); ColumnCondition condition = entry.right.prepare(keyspace(), def, metadata); condition.collectMarkerSpecification(boundNames); checkFalse(def.isPrimaryKeyColumn(), "PRIMARY KEY column '%s' cannot have IF conditions", def.name); builder.add(condition); } return builder.build(); } protected abstract ModificationStatement prepareInternal(CFMetaData cfm, VariableSpecifications boundNames, Conditions conditions, Attributes attrs);
Creates the restrictions.
Params:
  • cfm – the column family meta data
  • boundNames – the bound names
  • operations – the column operations
  • where – the where clause
  • conditions – the conditions
Returns:the restrictions
/** * Creates the restrictions. * * @param cfm the column family meta data * @param boundNames the bound names * @param operations the column operations * @param where the where clause * @param conditions the conditions * @return the restrictions */
protected StatementRestrictions newRestrictions(CFMetaData cfm, VariableSpecifications boundNames, Operations operations, WhereClause where, Conditions conditions) { if (where.containsCustomExpressions()) throw new InvalidRequestException(CUSTOM_EXPRESSIONS_NOT_ALLOWED); boolean applyOnlyToStaticColumns = appliesOnlyToStaticColumns(operations, conditions); return new StatementRestrictions(type, cfm, where, boundNames, applyOnlyToStaticColumns, false, false, false); }
Retrieves the ColumnDefinition corresponding to the specified raw ColumnIdentifier.
Params:
  • cfm – the column family meta data
  • rawId – the raw ColumnIdentifier
Returns:the ColumnDefinition corresponding to the specified raw ColumnIdentifier
/** * Retrieves the <code>ColumnDefinition</code> corresponding to the specified raw <code>ColumnIdentifier</code>. * * @param cfm the column family meta data * @param rawId the raw <code>ColumnIdentifier</code> * @return the <code>ColumnDefinition</code> corresponding to the specified raw <code>ColumnIdentifier</code> */
protected static ColumnDefinition getColumnDefinition(CFMetaData cfm, Raw rawId) { return rawId.prepare(cfm); } } }