cassandra-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From tylerho...@apache.org
Subject [1/3] git commit: Add support for DELETE ... IF EXISTS to CQL3
Date Tue, 18 Mar 2014 19:47:48 GMT
Repository: cassandra
Updated Branches:
  refs/heads/trunk a911893f6 -> 83f4acc82


Add support for DELETE ... IF EXISTS to CQL3

Patch by Tyler Hobbs, reviewed by Sylvain Lebresne for CASSANDRA-5708


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/5b3b52f5
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/5b3b52f5
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/5b3b52f5

Branch: refs/heads/trunk
Commit: 5b3b52f596eaa50a5eff7e31bec7f1243e34ade2
Parents: 1f13efe
Author: Tyler Hobbs <tyler@datastax.com>
Authored: Tue Mar 18 14:24:15 2014 -0500
Committer: Tyler Hobbs <tyler@datastax.com>
Committed: Tue Mar 18 14:24:15 2014 -0500

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 src/java/org/apache/cassandra/cql3/Cql.g        |  9 +++--
 .../cql3/statements/BatchStatement.java         |  2 +-
 .../cql3/statements/CQL3CasConditions.java      | 36 +++++++++++++++++++-
 .../cql3/statements/DeleteStatement.java        |  5 +--
 .../cql3/statements/ModificationStatement.java  | 33 +++++++++++++++---
 .../cql3/statements/UpdateStatement.java        |  4 +--
 7 files changed, 77 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/5b3b52f5/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 862ea3e..e738a2e 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -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)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5b3b52f5/src/java/org/apache/cassandra/cql3/Cql.g
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Cql.g b/src/java/org/apache/cassandra/cql3/Cql.g
index a11a818..ed482d0 100644
--- a/src/java/org/apache/cassandra/cql3/Cql.g
+++ b/src/java/org/apache/cassandra/cql3/Cql.g
@@ -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);
       }
     ;
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5b3b52f5/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
index 675ce7a..799d8ea 100644
--- a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
@@ -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());

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5b3b52f5/src/java/org/apache/cassandra/cql3/statements/CQL3CasConditions.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/CQL3CasConditions.java b/src/java/org/apache/cassandra/cql3/statements/CQL3CasConditions.java
index ee004bc..a7ec8d4 100644
--- a/src/java/org/apache/cassandra/cql3/statements/CQL3CasConditions.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CQL3CasConditions.java
@@ -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<>();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5b3b52f5/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java b/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java
index 6efe100..902add4 100644
--- a/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DeleteStatement.java
@@ -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;
         }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5b3b52f5/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
index 154c01c..d96ea9c 100644
--- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
@@ -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)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/5b3b52f5/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
index fc9bb66..8453a76 100644
--- a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
@@ -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;
         }


Mime
View raw message