mirror of https://github.com/apache/cassandra
Add support for DELETE ... IF EXISTS to CQL3
Patch by Tyler Hobbs, reviewed by Sylvain Lebresne for CASSANDRA-5708
This commit is contained in:
parent
1f13efefd3
commit
5b3b52f596
|
|
@ -1,4 +1,5 @@
|
|||
2.0.7
|
||||
* Add support for DELETE ... IF EXISTS to CQL3 (CASSANDRA-5708)
|
||||
* Update hadoop_cql3_word_count example (CASSANDRA-6793)
|
||||
* Fix handling of RejectedExecution in sync Thrift server (CASSANDRA-6788)
|
||||
* Log more information when exceeding tombstone_warn_threshold (CASSANDRA-6865)
|
||||
|
|
|
|||
|
|
@ -371,24 +371,27 @@ updateConditions returns [List<Pair<ColumnIdentifier, ColumnCondition.Raw>> cond
|
|||
* DELETE name1, name2
|
||||
* FROM <CF>
|
||||
* USING TIMESTAMP <long>
|
||||
* WHERE KEY = keyname;
|
||||
* WHERE KEY = keyname
|
||||
[IF (EXISTS | name = value, ...)];
|
||||
*/
|
||||
deleteStatement returns [DeleteStatement.Parsed expr]
|
||||
@init {
|
||||
Attributes.Raw attrs = new Attributes.Raw();
|
||||
List<Operation.RawDeletion> columnDeletions = Collections.emptyList();
|
||||
boolean ifExists = false;
|
||||
}
|
||||
: K_DELETE ( dels=deleteSelection { columnDeletions = dels; } )?
|
||||
K_FROM cf=columnFamilyName
|
||||
( usingClauseDelete[attrs] )?
|
||||
K_WHERE wclause=whereClause
|
||||
( K_IF conditions=updateConditions )?
|
||||
( K_IF ( K_EXISTS { ifExists = true; } | conditions=updateConditions ))?
|
||||
{
|
||||
return new DeleteStatement.Parsed(cf,
|
||||
attrs,
|
||||
columnDeletions,
|
||||
wclause,
|
||||
conditions == null ? Collections.<Pair<ColumnIdentifier, ColumnCondition.Raw>>emptyList() : conditions);
|
||||
conditions == null ? Collections.<Pair<ColumnIdentifier, ColumnCondition.Raw>>emptyList() : conditions,
|
||||
ifExists);
|
||||
}
|
||||
;
|
||||
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ public class BatchStatement implements CQLStatement, MeasurableForPreparedCache
|
|||
{
|
||||
statement.addUpdatesAndConditions(key, clusteringPrefix, updates, conditions, statementVariables, timestamp);
|
||||
// As soon as we have a ifNotExists, we set columnsWithConditions to null so that everything is in the resultSet
|
||||
if (statement.hasIfNotExistCondition())
|
||||
if (statement.hasIfNotExistCondition() || statement.hasIfExistCondition())
|
||||
columnsWithConditions = null;
|
||||
else if (columnsWithConditions != null)
|
||||
Iterables.addAll(columnsWithConditions, statement.getColumnsWithConditions());
|
||||
|
|
|
|||
|
|
@ -52,7 +52,21 @@ public class CQL3CasConditions implements CASConditions
|
|||
{
|
||||
RowCondition previous = conditions.put(prefix.build(), new NotExistCondition(prefix, now));
|
||||
if (previous != null && !(previous instanceof NotExistCondition))
|
||||
throw new InvalidRequestException("Cannot mix IF conditions and IF NOT EXISTS for the same row");
|
||||
{
|
||||
// these should be prevented by the parser, but it doesn't hurt to check
|
||||
if (previous instanceof ExistCondition)
|
||||
throw new InvalidRequestException("Cannot mix IF EXISTS and IF NOT EXISTS conditions for the same row");
|
||||
else
|
||||
throw new InvalidRequestException("Cannot mix IF conditions and IF NOT EXISTS for the same row");
|
||||
}
|
||||
}
|
||||
|
||||
public void addExist(ColumnNameBuilder prefix) throws InvalidRequestException
|
||||
{
|
||||
RowCondition previous = conditions.put(prefix.build(), new ExistCondition(prefix, now));
|
||||
// this should be prevented by the parser, but it doesn't hurt to check
|
||||
if (previous != null && previous instanceof NotExistCondition)
|
||||
throw new InvalidRequestException("Cannot mix IF EXISTS and IF NOT EXISTS conditions for the same row");
|
||||
}
|
||||
|
||||
public void addConditions(ColumnNameBuilder prefix, Collection<ColumnCondition> conds, List<ByteBuffer> variables) throws InvalidRequestException
|
||||
|
|
@ -130,6 +144,26 @@ public class CQL3CasConditions implements CASConditions
|
|||
}
|
||||
}
|
||||
|
||||
private static class ExistCondition extends RowCondition
|
||||
{
|
||||
private ExistCondition(ColumnNameBuilder rowPrefix, long now)
|
||||
{
|
||||
super (rowPrefix, now);
|
||||
}
|
||||
|
||||
public boolean appliesTo(ColumnFamily current)
|
||||
{
|
||||
if (current == null)
|
||||
return false;
|
||||
|
||||
Iterator<Column> iter = current.iterator(new ColumnSlice[]{ new ColumnSlice(rowPrefix.build(), rowPrefix.buildAsEndOfRange())});
|
||||
while (iter.hasNext())
|
||||
if (iter.next().isLive(now))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ColumnsConditions extends RowCondition
|
||||
{
|
||||
private final Map<ColumnIdentifier, ColumnCondition> conditions = new HashMap<>();
|
||||
|
|
|
|||
|
|
@ -101,9 +101,10 @@ public class DeleteStatement extends ModificationStatement
|
|||
Attributes.Raw attrs,
|
||||
List<Operation.RawDeletion> deletions,
|
||||
List<Relation> whereClause,
|
||||
List<Pair<ColumnIdentifier, ColumnCondition.Raw>> conditions)
|
||||
List<Pair<ColumnIdentifier, ColumnCondition.Raw>> conditions,
|
||||
boolean ifExists)
|
||||
{
|
||||
super(name, attrs, conditions, false);
|
||||
super(name, attrs, conditions, false, ifExists);
|
||||
this.deletions = deletions;
|
||||
this.whereClause = whereClause;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ public abstract class ModificationStatement implements CQLStatement, MeasurableF
|
|||
private List<ColumnCondition> columnConditions;
|
||||
private List<ColumnCondition> staticConditions;
|
||||
private boolean ifNotExists;
|
||||
private boolean ifExists;
|
||||
|
||||
private boolean hasNoClusteringColumns = true;
|
||||
private boolean setsOnlyStaticColumns;
|
||||
|
|
@ -195,7 +196,7 @@ public abstract class ModificationStatement implements CQLStatement, MeasurableF
|
|||
|
||||
public Iterable<ColumnIdentifier> getColumnsWithConditions()
|
||||
{
|
||||
if (ifNotExists)
|
||||
if (ifNotExists || ifExists)
|
||||
return null;
|
||||
|
||||
return Iterables.concat(columnConditions == null ? Collections.<ColumnIdentifier>emptyList() : Iterables.transform(columnConditions, getColumnForCondition),
|
||||
|
|
@ -230,6 +231,16 @@ public abstract class ModificationStatement implements CQLStatement, MeasurableF
|
|||
return ifNotExists;
|
||||
}
|
||||
|
||||
public void setIfExistCondition()
|
||||
{
|
||||
ifExists = true;
|
||||
}
|
||||
|
||||
public boolean hasIfExistCondition()
|
||||
{
|
||||
return ifExists;
|
||||
}
|
||||
|
||||
private void addKeyValues(CFDefinition.Name name, Restriction values) throws InvalidRequestException
|
||||
{
|
||||
if (name.kind == CFDefinition.Name.Kind.COLUMN_ALIAS)
|
||||
|
|
@ -489,6 +500,7 @@ public abstract class ModificationStatement implements CQLStatement, MeasurableF
|
|||
public boolean hasConditions()
|
||||
{
|
||||
return ifNotExists
|
||||
|| ifExists
|
||||
|| (columnConditions != null && !columnConditions.isEmpty())
|
||||
|| (staticConditions != null && !staticConditions.isEmpty());
|
||||
}
|
||||
|
|
@ -562,6 +574,10 @@ public abstract class ModificationStatement implements CQLStatement, MeasurableF
|
|||
// of any static columns and we should use the prefix for the "static part" of the partition.
|
||||
conditions.addNotExist(setsOnlyStaticColumns ? cfm.getStaticColumnNameBuilder() : clusteringPrefix);
|
||||
}
|
||||
else if (ifExists)
|
||||
{
|
||||
conditions.addExist(clusteringPrefix);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (columnConditions != null)
|
||||
|
|
@ -701,15 +717,17 @@ public abstract class ModificationStatement implements CQLStatement, MeasurableF
|
|||
public static abstract class Parsed extends CFStatement
|
||||
{
|
||||
protected final Attributes.Raw attrs;
|
||||
private final List<Pair<ColumnIdentifier, ColumnCondition.Raw>> conditions;
|
||||
protected final List<Pair<ColumnIdentifier, ColumnCondition.Raw>> conditions;
|
||||
private final boolean ifNotExists;
|
||||
private final boolean ifExists;
|
||||
|
||||
protected Parsed(CFName name, Attributes.Raw attrs, List<Pair<ColumnIdentifier, ColumnCondition.Raw>> conditions, boolean ifNotExists)
|
||||
protected Parsed(CFName name, Attributes.Raw attrs, List<Pair<ColumnIdentifier, ColumnCondition.Raw>> conditions, boolean ifNotExists, boolean ifExists)
|
||||
{
|
||||
super(name);
|
||||
this.attrs = attrs;
|
||||
this.conditions = conditions == null ? Collections.<Pair<ColumnIdentifier, ColumnCondition.Raw>>emptyList() : conditions;
|
||||
this.ifNotExists = ifNotExists;
|
||||
this.ifExists = ifExists;
|
||||
}
|
||||
|
||||
public ParsedStatement.Prepared prepare() throws InvalidRequestException
|
||||
|
|
@ -733,7 +751,7 @@ public abstract class ModificationStatement implements CQLStatement, MeasurableF
|
|||
|
||||
ModificationStatement stmt = prepareInternal(cfDef, boundNames, preparedAttributes);
|
||||
|
||||
if (ifNotExists || !conditions.isEmpty())
|
||||
if (ifNotExists || ifExists || !conditions.isEmpty())
|
||||
{
|
||||
if (stmt.isCounter())
|
||||
throw new InvalidRequestException("Conditional updates are not supported on counter tables");
|
||||
|
|
@ -746,8 +764,15 @@ public abstract class ModificationStatement implements CQLStatement, MeasurableF
|
|||
// To have both '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.
|
||||
assert conditions.isEmpty();
|
||||
assert !ifExists;
|
||||
stmt.setIfNotExistCondition();
|
||||
}
|
||||
else if (ifExists)
|
||||
{
|
||||
assert conditions.isEmpty();
|
||||
assert !ifNotExists;
|
||||
stmt.setIfExistCondition();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (Pair<ColumnIdentifier, ColumnCondition.Raw> entry : conditions)
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ public class UpdateStatement extends ModificationStatement
|
|||
List<ColumnIdentifier> columnNames, List<Term.Raw> columnValues,
|
||||
boolean ifNotExists)
|
||||
{
|
||||
super(name, attrs, null, ifNotExists);
|
||||
super(name, attrs, null, ifNotExists, false);
|
||||
this.columnNames = columnNames;
|
||||
this.columnValues = columnValues;
|
||||
}
|
||||
|
|
@ -189,7 +189,7 @@ public class UpdateStatement extends ModificationStatement
|
|||
List<Relation> whereClause,
|
||||
List<Pair<ColumnIdentifier, ColumnCondition.Raw>> conditions)
|
||||
{
|
||||
super(name, attrs, conditions, false);
|
||||
super(name, attrs, conditions, false, false);
|
||||
this.updates = updates;
|
||||
this.whereClause = whereClause;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue