Add support for DELETE ... IF EXISTS to CQL3

Patch by Tyler Hobbs, reviewed by Sylvain Lebresne for CASSANDRA-5708
This commit is contained in:
Tyler Hobbs 2014-03-18 14:24:15 -05:00
parent 1f13efefd3
commit 5b3b52f596
7 changed files with 77 additions and 13 deletions

View File

@ -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)

View File

@ -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);
}
;

View File

@ -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());

View File

@ -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<>();

View File

@ -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;
}

View File

@ -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)

View File

@ -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;
}