org.apache.cassandra.cql3.statements.UpdateStatement Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cassandra-all Show documentation
Show all versions of cassandra-all Show documentation
The Apache Cassandra Project develops a highly scalable second-generation distributed database, bringing together Dynamo's fully distributed design and Bigtable's ColumnFamily-based data model.
/*
* 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.util.Collection;
import java.util.Collections;
import java.util.List;
import org.apache.cassandra.audit.AuditLogContext;
import org.apache.cassandra.audit.AuditLogEntryType;
import org.apache.cassandra.cql3.*;
import org.apache.cassandra.cql3.conditions.ColumnCondition;
import org.apache.cassandra.cql3.conditions.Conditions;
import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
import org.apache.cassandra.db.Clustering;
import org.apache.cassandra.db.Slice;
import org.apache.cassandra.db.partitions.PartitionUpdate;
import org.apache.cassandra.schema.ColumnMetadata;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.Pair;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import static org.apache.cassandra.cql3.statements.RequestValidations.checkContainsNoDuplicates;
import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
/**
* An UPDATE
statement parsed from a CQL query statement.
*
*/
public class UpdateStatement extends ModificationStatement
{
private static final Constants.Value EMPTY = new Constants.Value(ByteBufferUtil.EMPTY_BYTE_BUFFER);
private UpdateStatement(StatementType type,
VariableSpecifications bindVariables,
TableMetadata metadata,
Operations operations,
StatementRestrictions restrictions,
Conditions conditions,
Attributes attrs)
{
super(type, bindVariables, metadata, operations, restrictions, conditions, attrs);
}
@Override
public void addUpdateForKey(PartitionUpdate.Builder updateBuilder, Clustering> clustering, UpdateParameters params)
{
if (updatesRegularRows())
{
params.newRow(clustering);
// We update the row timestamp only on INSERT (#6782)
// Further, COMPACT tables semantic differs from "CQL3" ones in that a row exists only if it has
// a non-null column, so we don't want to set the row timestamp for them.
if (type.isInsert() && !metadata.isCompactTable())
params.addPrimaryKeyLivenessInfo();
List updates = getRegularOperations();
// For compact table, we don't accept an insert/update that only sets the PK unless the is no
// declared non-PK columns (which we recognize because in that case
// the compact value is of type "EmptyType").
if (metadata().isCompactTable() && updates.isEmpty())
{
TableMetadata.CompactTableMetadata metadata = (TableMetadata.CompactTableMetadata) metadata();
RequestValidations.checkTrue(metadata.hasEmptyCompactValue(),
"Column %s is mandatory for this COMPACT STORAGE table",
metadata.compactValueColumn);
updates = Collections.singletonList(new Constants.Setter(metadata.compactValueColumn, EMPTY));
}
for (int i = 0, isize = updates.size(); i < isize; i++)
updates.get(i).execute(updateBuilder.partitionKey(), params);
updateBuilder.add(params.buildRow());
}
if (updatesStaticRow())
{
params.newRow(Clustering.STATIC_CLUSTERING);
List staticOps = getStaticOperations();
for (int i = 0, isize = staticOps.size(); i < isize; i++)
staticOps.get(i).execute(updateBuilder.partitionKey(), params);
updateBuilder.add(params.buildRow());
}
}
@Override
public void addUpdateForKey(PartitionUpdate.Builder update, Slice slice, UpdateParameters params)
{
throw new UnsupportedOperationException();
}
public static class ParsedInsert extends ModificationStatement.Parsed
{
private final List columnNames;
private final List columnValues;
/**
* A parsed INSERT
statement.
*
* @param name column family being operated on
* @param attrs additional attributes for statement (CL, timestamp, timeToLive)
* @param columnNames list of column names
* @param columnValues list of column values (corresponds to names)
* @param ifNotExists true if an IF NOT EXISTS condition was specified, false otherwise
*/
public ParsedInsert(QualifiedName name,
Attributes.Raw attrs,
List columnNames,
List columnValues,
boolean ifNotExists)
{
super(name, StatementType.INSERT, attrs, null, ifNotExists, false);
this.columnNames = columnNames;
this.columnValues = columnValues;
}
@Override
protected ModificationStatement prepareInternal(TableMetadata metadata,
VariableSpecifications bindVariables,
Conditions conditions,
Attributes attrs)
{
// Created from an INSERT
checkFalse(metadata.isCounter(), "INSERT statements are not allowed on counter tables, use UPDATE instead");
checkFalse(columnNames == null, "Column names for INSERT must be provided when using VALUES");
checkFalse(columnNames.isEmpty(), "No columns provided to INSERT");
checkFalse(columnNames.size() != columnValues.size(), "Unmatched column names/values");
checkContainsNoDuplicates(columnNames, "The column names contains duplicates");
WhereClause.Builder whereClause = new WhereClause.Builder();
Operations operations = new Operations(type);
boolean hasClusteringColumnsSet = false;
for (int i = 0; i < columnNames.size(); i++)
{
ColumnMetadata def = metadata.getExistingColumn(columnNames.get(i));
if (def.isClusteringColumn())
hasClusteringColumnsSet = true;
Term.Raw value = columnValues.get(i);
if (def.isPrimaryKeyColumn())
{
whereClause.add(new SingleColumnRelation(columnNames.get(i), Operator.EQ, value));
}
else
{
Operation operation = new Operation.SetValue(value).prepare(metadata, def, !conditions.isEmpty());
operation.collectMarkerSpecification(bindVariables);
operations.add(operation);
}
}
boolean applyOnlyToStaticColumns = !hasClusteringColumnsSet && appliesOnlyToStaticColumns(operations, conditions);
StatementRestrictions restrictions = new StatementRestrictions(type,
metadata,
whereClause.build(),
bindVariables,
applyOnlyToStaticColumns,
false,
false);
return new UpdateStatement(type,
bindVariables,
metadata,
operations,
restrictions,
conditions,
attrs);
}
}
/**
* A parsed INSERT JSON statement.
*/
public static class ParsedInsertJson extends ModificationStatement.Parsed
{
private final Json.Raw jsonValue;
private final boolean defaultUnset;
public ParsedInsertJson(QualifiedName name, Attributes.Raw attrs, Json.Raw jsonValue, boolean defaultUnset, boolean ifNotExists)
{
super(name, StatementType.INSERT, attrs, null, ifNotExists, false);
this.jsonValue = jsonValue;
this.defaultUnset = defaultUnset;
}
@Override
protected ModificationStatement prepareInternal(TableMetadata metadata,
VariableSpecifications bindVariables,
Conditions conditions,
Attributes attrs)
{
checkFalse(metadata.isCounter(), "INSERT statements are not allowed on counter tables, use UPDATE instead");
Collection defs = metadata.columns();
Json.Prepared prepared = jsonValue.prepareAndCollectMarkers(metadata, defs, bindVariables);
WhereClause.Builder whereClause = new WhereClause.Builder();
Operations operations = new Operations(type);
boolean hasClusteringColumnsSet = false;
for (ColumnMetadata def : defs)
{
if (def.isClusteringColumn())
hasClusteringColumnsSet = true;
Term.Raw raw = prepared.getRawTermForColumn(def, defaultUnset);
if (def.isPrimaryKeyColumn())
{
whereClause.add(new SingleColumnRelation(def.name, Operator.EQ, raw));
}
else
{
Operation operation = new Operation.SetValue(raw).prepare(metadata, def, !conditions.isEmpty());
operation.collectMarkerSpecification(bindVariables);
operations.add(operation);
}
}
boolean applyOnlyToStaticColumns = !hasClusteringColumnsSet && appliesOnlyToStaticColumns(operations, conditions);
StatementRestrictions restrictions = new StatementRestrictions(type,
metadata,
whereClause.build(),
bindVariables,
applyOnlyToStaticColumns,
false,
false);
return new UpdateStatement(type,
bindVariables,
metadata,
operations,
restrictions,
conditions,
attrs);
}
}
public static class ParsedUpdate extends ModificationStatement.Parsed
{
// Provided for an UPDATE
private final List> updates;
private final WhereClause whereClause;
/**
* Creates a new UpdateStatement from a column family name, columns map, consistency
* level, and key term.
*
* @param name column family being operated on
* @param attrs additional attributes for statement (timestamp, timeToLive)
* @param updates a map of column operations to perform
* @param whereClause the where clause
* @param ifExists flag to check if row exists
* */
public ParsedUpdate(QualifiedName name,
Attributes.Raw attrs,
List> updates,
WhereClause whereClause,
List> conditions,
boolean ifExists)
{
super(name, StatementType.UPDATE, attrs, conditions, false, ifExists);
this.updates = updates;
this.whereClause = whereClause;
}
@Override
protected ModificationStatement prepareInternal(TableMetadata metadata,
VariableSpecifications bindVariables,
Conditions conditions,
Attributes attrs)
{
Operations operations = new Operations(type);
for (Pair entry : updates)
{
ColumnMetadata def = metadata.getExistingColumn(entry.left);
checkFalse(def.isPrimaryKeyColumn(), "PRIMARY KEY part %s found in SET part", def.name);
Operation operation = entry.right.prepare(metadata, def, !conditions.isEmpty());
operation.collectMarkerSpecification(bindVariables);
operations.add(operation);
}
StatementRestrictions restrictions = newRestrictions(metadata,
bindVariables,
operations,
whereClause,
conditions);
return new UpdateStatement(type,
bindVariables,
metadata,
operations,
restrictions,
conditions,
attrs);
}
}
@Override
public String toString()
{
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
@Override
public AuditLogContext getAuditLogContext()
{
return new AuditLogContext(AuditLogEntryType.UPDATE, keyspace(), table());
}
}