cassandra-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From slebre...@apache.org
Subject [2/2] git commit: Correctly handle prepared operation on collections
Date Wed, 21 Nov 2012 08:14:13 GMT
Correctly handle prepared operation on collections

patch by slebresne; reviewed by jbellis for CASSANDRA-4945


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

Branch: refs/heads/cassandra-1.2
Commit: b4f2f2014e088b293fc7305f9c3e6409ee428495
Parents: 553b9e8
Author: Sylvain Lebresne <sylvain@datastax.com>
Authored: Wed Nov 21 09:13:07 2012 +0100
Committer: Sylvain Lebresne <sylvain@datastax.com>
Committed: Wed Nov 21 09:13:07 2012 +0100

----------------------------------------------------------------------
 CHANGES.txt                                        |    1 +
 src/java/org/apache/cassandra/cql3/Cql.g           |   39 +++--
 .../cassandra/cql3/operations/ColumnOperation.java |   27 +---
 .../cassandra/cql3/operations/ListOperation.java   |   83 +++++++--
 .../cassandra/cql3/operations/MapOperation.java    |   24 ++-
 .../cassandra/cql3/operations/Operation.java       |    9 +-
 .../cql3/operations/PreparedOperation.java         |  143 +++++++++++++++
 .../cassandra/cql3/operations/SetOperation.java    |   44 ++++-
 .../cassandra/cql3/statements/UpdateStatement.java |   25 +--
 9 files changed, 298 insertions(+), 97 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4f2f201/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index e63350b..cdd651a 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -17,6 +17,7 @@
  * Use Stats.db when bulk loading if present (CASSANDRA-4957)
  * Skip repair on system_trace and keyspaces with RF=1 (CASSANDRA-4956)
  * Remove select arbitrary limit (CASSANDRA-4918)
+ * Correctly handle prepared operation on collections (CASSANDRA-4945)
 Merged from 1.1:
  * add basic authentication support for Pig CassandraStorage (CASSANDRA-3042)
  * fix CQL2 ALTER TABLE compaction_strategy_class altering (CASSANDRA-4965)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4f2f201/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 ce08e66..9c3f77b 100644
--- a/src/java/org/apache/cassandra/cql3/Cql.g
+++ b/src/java/org/apache/cassandra/cql3/Cql.g
@@ -74,7 +74,7 @@ options {
     // used by UPDATE of the counter columns to validate if '-' was supplied by user
     public void validateMinusSupplied(Object op, final Term value, IntStream stream) throws
MissingTokenException
     {
-        if (op == null && (value.isBindMarker() || Long.parseLong(value.getText())
> 0))
+        if (op == null && Long.parseLong(value.getText()) > 0)
             throw new MissingTokenException(102, stream, value);
     }
 
@@ -561,7 +561,8 @@ cfOrKsName[CFName name, boolean isKs]
     ;
 
 set_operation returns [Operation op]
-    : t=term         { $op = ColumnOperation.Set(t); }
+    : t=finalTerm    { $op = ColumnOperation.Set(t); }
+    | mk=QMARK       { $op = new PreparedOperation(new Term($mk.text, $mk.type, ++currentBindMarkerIdx),
PreparedOperation.Kind.SET); }
     | m=map_literal  { $op = MapOperation.Set(m);  }
     | l=list_literal { $op = ListOperation.Set(l); }
     | s=set_literal  { $op = SetOperation.Set(s);  }
@@ -591,14 +592,9 @@ term returns [Term term]
     | t=QMARK      { $term = new Term($t.text, $t.type, ++currentBindMarkerIdx); }
     ;
 
-intTerm returns [Term integer]
-    : t=INTEGER { $integer = new Term($t.text, $t.type); }
-    | t=QMARK   { $integer = new Term($t.text, $t.type, ++currentBindMarkerIdx); }
-    ;
-
 termPairWithOperation[List<Pair<ColumnIdentifier, Operation>> columns]
     : key=cident '='
-        (set_op = set_operation { columns.add(Pair.<ColumnIdentifier, Operation>create(key,
set_op)); }
+        ( set_op=set_operation { columns.add(Pair.<ColumnIdentifier, Operation>create(key,
set_op)); }
         | c=cident op=operation
           {
               if (!key.equals(c))
@@ -611,6 +607,13 @@ termPairWithOperation[List<Pair<ColumnIdentifier, Operation>>
columns]
                   addRecognitionError("Only expressions like X = <value> + X are supported.");
               columns.add(Pair.<ColumnIdentifier, Operation>create(key, ListOperation.Prepend(ll)));
           }
+        | mk=QMARK '+' c=cident
+          {
+              if (!key.equals(c))
+                  addRecognitionError("Only expressions like X = <value> + X are supported.");
+              PreparedOperation pop = new PreparedOperation(new Term($mk.text, $mk.type,
++currentBindMarkerIdx), PreparedOperation.Kind.PREPARED_PLUS);
+              columns.add(Pair.<ColumnIdentifier, Operation>create(key, pop));
+          }
         )
     | key=cident '[' t=term ']' '=' vv=term
       {
@@ -622,15 +625,25 @@ termPairWithOperation[List<Pair<ColumnIdentifier, Operation>>
columns]
       }
     ;
 
+intTerm returns [Term integer]
+    : t=INTEGER { $integer = new Term($t.text, $t.type); }
+    | t=QMARK   { $integer = new Term($t.text, $t.type, ++currentBindMarkerIdx); }
+    ;
+
+
 operation returns [Operation op]
-    : '+' v=intTerm { $op = ColumnOperation.CounterInc(v); }
-    | sign='-'? v=intTerm
+    : '+' i=INTEGER { $op = ColumnOperation.CounterInc(new Term($i.text, $i.type)); }
+    | sign='-'? i=INTEGER
       {
-          validateMinusSupplied(sign, v, input);
+          Term t = new Term($i.text, $i.type);
+          validateMinusSupplied(sign, t, input);
           if (sign == null)
-              v = new Term(-(Long.valueOf(v.getText())), v.getType());
-          $op = ColumnOperation.CounterDec(v);
+              t = new Term(-(Long.valueOf(t.getText())), t.getType());
+          $op = ColumnOperation.CounterDec(t);
       }
+    | '+' mk=QMARK { $op = new PreparedOperation(new Term($mk.text, $mk.type, ++currentBindMarkerIdx),
PreparedOperation.Kind.PLUS_PREPARED); }
+    | '-' mk=QMARK { $op = new PreparedOperation(new Term($mk.text, $mk.type, ++currentBindMarkerIdx),
PreparedOperation.Kind.MINUS_PREPARED); }
+
     | '+' ll=list_literal { $op = ListOperation.Append(ll); }
     | '-' ll=list_literal { $op = ListOperation.Discard(ll); }
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4f2f201/src/java/org/apache/cassandra/cql3/operations/ColumnOperation.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/operations/ColumnOperation.java b/src/java/org/apache/cassandra/cql3/operations/ColumnOperation.java
index 0f4c1fc..bfdec8c 100644
--- a/src/java/org/apache/cassandra/cql3/operations/ColumnOperation.java
+++ b/src/java/org/apache/cassandra/cql3/operations/ColumnOperation.java
@@ -62,7 +62,8 @@ public class ColumnOperation implements Operation
     public void execute(ColumnFamily cf,
                         ColumnNameBuilder builder,
                         AbstractType<?> validator,
-                        UpdateParameters params) throws InvalidRequestException
+                        UpdateParameters params,
+                        List<Pair<ByteBuffer, IColumn>> list) throws InvalidRequestException
     {
         switch (kind)
         {
@@ -78,28 +79,6 @@ public class ColumnOperation implements Operation
         }
     }
 
-    public void execute(ColumnFamily cf, ColumnNameBuilder builder, CollectionType validator,
UpdateParameters params, List<Pair<ByteBuffer, IColumn>> list) throws InvalidRequestException
-    {
-        throw new InvalidRequestException("Column operations are only supported on simple
types, but " + validator + " given.");
-    }
-
-    public void executePreparedCollection(ColumnFamily cf, ColumnNameBuilder builder, CollectionType
validator, UpdateParameters params) throws InvalidRequestException
-    {
-
-        switch (validator.kind)
-        {
-            case LIST:
-                ListOperation.doInsertFromPrepared(cf, builder, (ListType)validator, value,
params);
-                break;
-            case SET:
-                SetOperation.doInsertFromPrepared(cf, builder, (SetType)validator, value,
params);
-                break;
-            case MAP:
-                MapOperation.doInsertFromPrepared(cf, builder, (MapType)validator, value,
params);
-                break;
-        }
-    }
-
     protected void doSet(ColumnFamily cf, ColumnNameBuilder builder, AbstractType<?>
validator, UpdateParameters params) throws InvalidRequestException
     {
         ByteBuffer colName = builder.build();
@@ -138,7 +117,7 @@ public class ColumnOperation implements Operation
         return Collections.singletonList(value);
     }
 
-    public boolean requiresRead()
+    public boolean requiresRead(AbstractType<?> validator)
     {
         return false;
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4f2f201/src/java/org/apache/cassandra/cql3/operations/ListOperation.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/operations/ListOperation.java b/src/java/org/apache/cassandra/cql3/operations/ListOperation.java
index 44d997e..25c616d 100644
--- a/src/java/org/apache/cassandra/cql3/operations/ListOperation.java
+++ b/src/java/org/apache/cassandra/cql3/operations/ListOperation.java
@@ -93,30 +93,30 @@ public class ListOperation implements Operation
 
     public void execute(ColumnFamily cf,
                         ColumnNameBuilder builder,
-                        CollectionType validator,
+                        AbstractType<?> validator,
                         UpdateParameters params,
                         List<Pair<ByteBuffer, IColumn>> list) throws InvalidRequestException
     {
-        if (validator.kind != CollectionType.Kind.LIST)
+        if (!(validator instanceof ListType))
             throw new InvalidRequestException("List operations are only supported on List
typed columns, but " + validator + " given.");
 
         switch (kind)
         {
             case SET:
                 cf.addAtom(params.makeTombstoneForOverwrite(builder.copy().build(), builder.copy().buildAsEndOfRange()));
-                doAppend(cf, builder, validator, params);
+                doAppend(cf, builder, (CollectionType)validator, params);
                 break;
             case SET_IDX:
-                doSet(cf, builder, params, validator, list);
+                doSet(cf, builder, params, (CollectionType)validator, list);
                 break;
             case APPEND:
-                doAppend(cf, builder, validator, params);
+                doAppend(cf, builder, (CollectionType)validator, params);
                 break;
             case PREPEND:
-                doPrepend(cf, builder, validator, params);
+                doPrepend(cf, builder, (CollectionType)validator, params);
                 break;
             case DISCARD:
-                doDiscard(cf, validator, params, list);
+                doDiscard(cf, (CollectionType)validator, params, list);
                 break;
             case DISCARD_IDX:
                 doDiscardIdx(cf, params, list);
@@ -126,18 +126,20 @@ public class ListOperation implements Operation
         }
     }
 
-    public void execute(ColumnFamily cf, ColumnNameBuilder builder, AbstractType<?>
validator, UpdateParameters params) throws InvalidRequestException
+    public static void doSetFromPrepared(ColumnFamily cf, ColumnNameBuilder builder, ListType
validator, Term values, UpdateParameters params) throws InvalidRequestException
     {
-        throw new InvalidRequestException("List operations are only supported on List typed
columns, but " + validator + " given.");
+        if (!values.isBindMarker())
+            throw new InvalidRequestException("Can't apply operation on column with " + validator
+ " type.");
+
+        cf.addAtom(params.makeTombstoneForOverwrite(builder.copy().build(), builder.copy().buildAsEndOfRange()));
+        doAppendFromPrepared(cf, builder, validator, values, params);
     }
 
-    public static void doInsertFromPrepared(ColumnFamily cf, ColumnNameBuilder builder, ListType
validator, Term values, UpdateParameters params) throws InvalidRequestException
+    public static void doAppendFromPrepared(ColumnFamily cf, ColumnNameBuilder builder, ListType
validator, Term values, UpdateParameters params) throws InvalidRequestException
     {
         if (!values.isBindMarker())
             throw new InvalidRequestException("Can't apply operation on column with " + validator
+ " type.");
 
-        cf.addAtom(params.makeTombstoneForOverwrite(builder.copy().build(), builder.copy().buildAsEndOfRange()));
-
         try
         {
             List<?> l = validator.compose(params.variables.get(values.bindIndex));
@@ -156,6 +158,61 @@ public class ListOperation implements Operation
         }
     }
 
+    public static void doPrependFromPrepared(ColumnFamily cf, ColumnNameBuilder builder,
ListType validator, Term values, UpdateParameters params) throws InvalidRequestException
+    {
+        if (!values.isBindMarker())
+            throw new InvalidRequestException("Can't apply operation on column with " + validator
+ " type.");
+
+        long time = REFERENCE_TIME - (System.currentTimeMillis() - REFERENCE_TIME);
+
+        try
+        {
+            List<?> l = validator.compose(params.variables.get(values.bindIndex));
+
+            for (int i = 0; i < l.size(); i++)
+            {
+                ColumnNameBuilder b = i == l.size() - 1 ? builder : builder.copy();
+                PrecisionTime pt = getNextTime(time);
+                ByteBuffer uuid = ByteBuffer.wrap(UUIDGen.getTimeUUIDBytes(pt.millis, pt.nanos));
+                ByteBuffer name = b.add(uuid).build();
+                cf.addColumn(params.makeColumn(name, validator.valueComparator().decompose(l.get(i))));
+            }
+        }
+        catch (MarshalException e)
+        {
+            throw new InvalidRequestException(e.getMessage());
+        }
+    }
+
+    public static void doDiscardFromPrepared(ColumnFamily cf, ColumnNameBuilder builder,
ListType validator, Term values, UpdateParameters params, List<Pair<ByteBuffer, IColumn>>
list) throws InvalidRequestException
+    {
+        if (!values.isBindMarker())
+            throw new InvalidRequestException("Can't apply operation on column with " + validator
+ " type.");
+
+        if (list == null)
+            return;
+
+        try
+        {
+            List<?> l = validator.compose(params.variables.get(values.bindIndex));
+
+            Set<ByteBuffer> toDiscard = new HashSet<ByteBuffer>();
+            for (Object elt : l)
+                toDiscard.add(validator.valueComparator().decompose(elt));
+
+            for (Pair<ByteBuffer, IColumn> p : list)
+            {
+                IColumn c = p.right;
+                if (toDiscard.contains(c.value()))
+                    cf.addColumn(params.makeTombstone(c.name()));
+            }
+        }
+        catch (MarshalException e)
+        {
+            throw new InvalidRequestException(e.getMessage());
+        }
+    }
+
     private void doSet(ColumnFamily cf, ColumnNameBuilder builder, UpdateParameters params,
CollectionType validator, List<Pair<ByteBuffer, IColumn>> list) throws InvalidRequestException
     {
         int idx = validateListIdx(values.get(0), list);
@@ -219,7 +276,7 @@ public class ListOperation implements Operation
         return values;
     }
 
-    public boolean requiresRead()
+    public boolean requiresRead(AbstractType<?> validator)
     {
         return kind == Kind.DISCARD || kind == Kind.DISCARD_IDX || kind == Kind.SET_IDX;
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4f2f201/src/java/org/apache/cassandra/cql3/operations/MapOperation.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/operations/MapOperation.java b/src/java/org/apache/cassandra/cql3/operations/MapOperation.java
index 39a5413..5f844a7 100644
--- a/src/java/org/apache/cassandra/cql3/operations/MapOperation.java
+++ b/src/java/org/apache/cassandra/cql3/operations/MapOperation.java
@@ -57,18 +57,13 @@ public class MapOperation implements Operation
         this.kind = Kind.DISCARD;
     }
 
-    public void execute(ColumnFamily cf, ColumnNameBuilder builder, AbstractType<?>
validator, UpdateParameters params) throws InvalidRequestException
-    {
-        throw new InvalidRequestException("Map operations are only supported on Map typed
columns, but " + validator + " given.");
-    }
-
     public void execute(ColumnFamily cf,
                         ColumnNameBuilder builder,
-                        CollectionType validator,
+                        AbstractType<?> validator,
                         UpdateParameters params,
                         List<Pair<ByteBuffer, IColumn>> list) throws InvalidRequestException
     {
-        if (validator.kind != CollectionType.Kind.MAP)
+        if (!(validator instanceof MapType))
             throw new InvalidRequestException("Map operations are only supported on Map typed
columns, but " + validator + " given.");
 
         switch (kind)
@@ -76,22 +71,29 @@ public class MapOperation implements Operation
             case SET: // fallthrough on purpose; remove previous Map before setting (PUT)
the new one
                 cf.addAtom(params.makeTombstoneForOverwrite(builder.copy().build(), builder.copy().buildAsEndOfRange()));
             case PUT:
-                doPut(cf, builder, validator, params);
+                doPut(cf, builder, (CollectionType)validator, params);
                 break;
             case DISCARD:
-                doDiscard(cf, builder, validator, params);
+                doDiscard(cf, builder, (CollectionType)validator, params);
                 break;
             default:
                 throw new AssertionError("Unsupported Map operation: " + kind);
         }
     }
 
-    public static void doInsertFromPrepared(ColumnFamily cf, ColumnNameBuilder builder, MapType
validator, Term values, UpdateParameters params) throws InvalidRequestException
+    public static void doSetFromPrepared(ColumnFamily cf, ColumnNameBuilder builder, MapType
validator, Term values, UpdateParameters params) throws InvalidRequestException
     {
         if (!values.isBindMarker())
             throw new InvalidRequestException("Can't apply operation on column with " + validator
+ " type.");
 
         cf.addAtom(params.makeTombstoneForOverwrite(builder.copy().build(), builder.copy().buildAsEndOfRange()));
+        doPutFromPrepared(cf, builder, validator, values, params);
+    }
+
+    public static void doPutFromPrepared(ColumnFamily cf, ColumnNameBuilder builder, MapType
validator, Term values, UpdateParameters params) throws InvalidRequestException
+    {
+        if (!values.isBindMarker())
+            throw new InvalidRequestException("Can't apply operation on column with " + validator
+ " type.");
 
         try
         {
@@ -136,7 +138,7 @@ public class MapOperation implements Operation
         return l;
     }
 
-    public boolean requiresRead()
+    public boolean requiresRead(AbstractType<?> validator)
     {
         return kind == Kind.SET || kind == Kind.DISCARD;
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4f2f201/src/java/org/apache/cassandra/cql3/operations/Operation.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/operations/Operation.java b/src/java/org/apache/cassandra/cql3/operations/Operation.java
index 5261677..28caea7 100644
--- a/src/java/org/apache/cassandra/cql3/operations/Operation.java
+++ b/src/java/org/apache/cassandra/cql3/operations/Operation.java
@@ -32,22 +32,17 @@ import org.apache.cassandra.utils.Pair;
 
 public interface Operation
 {
-    public static enum Type { COLUMN, COUNTER, LIST, SET, MAP }
+    public static enum Type { COLUMN, COUNTER, LIST, SET, MAP, PREPARED }
 
     public void execute(ColumnFamily cf,
                         ColumnNameBuilder builder,
                         AbstractType<?> validator,
-                        UpdateParameters params) throws InvalidRequestException;
-
-    public void execute(ColumnFamily cf,
-                        ColumnNameBuilder builder,
-                        CollectionType validator,
                         UpdateParameters params,
                         List<Pair<ByteBuffer, IColumn>> list) throws InvalidRequestException;
 
     public List<Term> getValues();
 
-    public boolean requiresRead();
+    public boolean requiresRead(AbstractType<?> validator);
 
     public Type getType();
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4f2f201/src/java/org/apache/cassandra/cql3/operations/PreparedOperation.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/operations/PreparedOperation.java b/src/java/org/apache/cassandra/cql3/operations/PreparedOperation.java
new file mode 100644
index 0000000..a54b065
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/operations/PreparedOperation.java
@@ -0,0 +1,143 @@
+/**
+ * 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.operations;
+
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.db.ColumnFamily;
+import org.apache.cassandra.db.IColumn;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.marshal.CollectionType;
+import org.apache.cassandra.db.marshal.ListType;
+import org.apache.cassandra.db.marshal.MapType;
+import org.apache.cassandra.db.marshal.SetType;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.utils.Pair;
+
+public class PreparedOperation implements Operation
+{
+    public enum Kind { SET, PREPARED_PLUS, PLUS_PREPARED, MINUS_PREPARED }
+
+    private final Term preparedValue;
+    private final Kind kind;
+
+    public PreparedOperation(Term value, Kind kind)
+    {
+        assert value.isBindMarker();
+        this.preparedValue = value;
+        this.kind = kind;
+    }
+
+    public void execute(ColumnFamily cf,
+                        ColumnNameBuilder builder,
+                        AbstractType<?> validator,
+                        UpdateParameters params,
+                        List<Pair<ByteBuffer, IColumn>> list) throws InvalidRequestException
+    {
+        if (validator instanceof CollectionType)
+        {
+            switch (((CollectionType)validator).kind)
+            {
+                case LIST:
+                    switch (kind)
+                    {
+                        case SET:
+                            ListOperation.doSetFromPrepared(cf, builder, (ListType)validator,
preparedValue, params);
+                            break;
+                        case PREPARED_PLUS:
+                            ListOperation.doPrependFromPrepared(cf, builder, (ListType)validator,
preparedValue, params);
+                            break;
+                        case PLUS_PREPARED:
+                            ListOperation.doAppendFromPrepared(cf, builder, (ListType)validator,
preparedValue, params);
+                            break;
+                        case MINUS_PREPARED:
+                            ListOperation.doDiscardFromPrepared(cf, builder, (ListType)validator,
preparedValue, params, list);
+                            break;
+                    }
+                    break;
+                case SET:
+                    switch (kind)
+                    {
+                        case SET:
+                            SetOperation.doSetFromPrepared(cf, builder, (SetType)validator,
preparedValue, params);
+                            break;
+                        case PREPARED_PLUS:
+                            throw new InvalidRequestException("Unsupported syntax, cannot
add to a prepared set");
+                        case PLUS_PREPARED:
+                            SetOperation.doAddFromPrepared(cf, builder, (SetType)validator,
preparedValue, params);
+                            break;
+                        case MINUS_PREPARED:
+                            SetOperation.doDiscardFromPrepared(cf, builder, (SetType)validator,
preparedValue, params);
+                            break;
+                    }
+                    break;
+                case MAP:
+                    switch (kind)
+                    {
+                        case SET:
+                            MapOperation.doSetFromPrepared(cf, builder, (MapType)validator,
preparedValue, params);
+                            break;
+                        case PREPARED_PLUS:
+                            throw new InvalidRequestException("Unsupported syntax, cannot
put to a prepared map");
+                        case PLUS_PREPARED:
+                            MapOperation.doPutFromPrepared(cf, builder, (MapType)validator,
preparedValue, params);
+                            break;
+                        case MINUS_PREPARED:
+                            throw new InvalidRequestException("Unsuppoted syntax, discard
syntax for map not supported");
+                    }
+                    break;
+            }
+        }
+        else
+        {
+            switch (kind)
+            {
+                case SET:
+                    ColumnOperation.Set(preparedValue).execute(cf, builder, validator, params,
null);
+                    break;
+                case PREPARED_PLUS:
+                    throw new InvalidRequestException("Unsupported syntax for increment,
must be of the form X = X + <value>");
+                case PLUS_PREPARED:
+                    ColumnOperation.CounterInc(preparedValue).execute(cf, builder, validator,
params, null);
+                    break;
+                case MINUS_PREPARED:
+                    ColumnOperation.CounterDec(preparedValue).execute(cf, builder, validator,
params, null);
+                    break;
+            }
+        }
+    }
+
+    public List<Term> getValues()
+    {
+        return Collections.singletonList(preparedValue);
+    }
+
+    public boolean requiresRead(AbstractType<?> validator)
+    {
+        // Only prepared operation requiring a read is list discard
+        return (validator instanceof ListType) && kind == Kind.MINUS_PREPARED;
+    }
+
+    public Type getType()
+    {
+        return Type.PREPARED;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4f2f201/src/java/org/apache/cassandra/cql3/operations/SetOperation.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/operations/SetOperation.java b/src/java/org/apache/cassandra/cql3/operations/SetOperation.java
index 36330f7..5d63bd9 100644
--- a/src/java/org/apache/cassandra/cql3/operations/SetOperation.java
+++ b/src/java/org/apache/cassandra/cql3/operations/SetOperation.java
@@ -50,11 +50,11 @@ public class SetOperation implements Operation
 
     public void execute(ColumnFamily cf,
                         ColumnNameBuilder builder,
-                        CollectionType validator,
+                        AbstractType<?> validator,
                         UpdateParameters params,
                         List<Pair<ByteBuffer, IColumn>> list) throws InvalidRequestException
     {
-        if (validator.kind != CollectionType.Kind.SET)
+        if (!(validator instanceof SetType))
             throw new InvalidRequestException("Set operations are only supported on Set typed
columns, but " + validator + " given.");
 
         switch (kind)
@@ -62,28 +62,30 @@ public class SetOperation implements Operation
             case SET: // fallthrough on purpose; remove previous Set before setting (ADD)
the new one
                 cf.addAtom(params.makeTombstoneForOverwrite(builder.copy().build(), builder.copy().buildAsEndOfRange()));
             case ADD:
-                doAdd(cf, builder, validator, params);
+                doAdd(cf, builder, (CollectionType)validator, params);
                 break;
             case DISCARD:
-                doDiscard(cf, builder, validator, params);
+                doDiscard(cf, builder, (CollectionType)validator, params);
                 break;
             default:
                 throw new AssertionError("Unsupported Set operation: " + kind);
         }
     }
 
-    public void execute(ColumnFamily cf, ColumnNameBuilder builder, AbstractType<?>
validator, UpdateParameters params) throws InvalidRequestException
+    public static void doSetFromPrepared(ColumnFamily cf, ColumnNameBuilder builder, SetType
validator, Term values, UpdateParameters params) throws InvalidRequestException
     {
-        throw new InvalidRequestException("Set operations are only supported on Set typed
columns, but " + validator + " given.");
+        if (!values.isBindMarker())
+            throw new InvalidRequestException("Can't apply operation on column with " + validator
+ " type.");
+
+        cf.addAtom(params.makeTombstoneForOverwrite(builder.copy().build(), builder.copy().buildAsEndOfRange()));
+        doAddFromPrepared(cf, builder, validator, values, params);
     }
 
-    public static void doInsertFromPrepared(ColumnFamily cf, ColumnNameBuilder builder, SetType
validator, Term values, UpdateParameters params) throws InvalidRequestException
+    public static void doAddFromPrepared(ColumnFamily cf, ColumnNameBuilder builder, SetType
validator, Term values, UpdateParameters params) throws InvalidRequestException
     {
         if (!values.isBindMarker())
             throw new InvalidRequestException("Can't apply operation on column with " + validator
+ " type.");
 
-        cf.addAtom(params.makeTombstoneForOverwrite(builder.copy().build(), builder.copy().buildAsEndOfRange()));
-
         try
         {
             Set<?> s = validator.compose(params.variables.get(values.bindIndex));
@@ -101,6 +103,28 @@ public class SetOperation implements Operation
         }
     }
 
+    public static void doDiscardFromPrepared(ColumnFamily cf, ColumnNameBuilder builder,
SetType validator, Term values, UpdateParameters params) throws InvalidRequestException
+    {
+        if (!values.isBindMarker())
+            throw new InvalidRequestException("Can't apply operation on column with " + validator
+ " type.");
+
+        try
+        {
+            Set<?> s = validator.compose(params.variables.get(values.bindIndex));
+            Iterator<?> iter = s.iterator();
+            while (iter.hasNext())
+            {
+                ColumnNameBuilder b = iter.hasNext() ? builder.copy() : builder;
+                ByteBuffer name = b.add(validator.nameComparator().decompose(iter.next())).build();
+                cf.addColumn(params.makeTombstone(name));
+            }
+        }
+        catch (MarshalException e)
+        {
+            throw new InvalidRequestException(e.getMessage());
+        }
+    }
+
     private void doAdd(ColumnFamily cf, ColumnNameBuilder builder, CollectionType validator,
UpdateParameters params) throws InvalidRequestException
     {
         for (int i = 0; i < values.size(); ++i)
@@ -126,7 +150,7 @@ public class SetOperation implements Operation
         return values;
     }
 
-    public boolean requiresRead()
+    public boolean requiresRead(AbstractType<?> validator)
     {
         return false;
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b4f2f201/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 915e8ed..97844ad 100644
--- a/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/UpdateStatement.java
@@ -122,7 +122,7 @@ public class UpdateStatement extends ModificationStatement
             if (!(name.type instanceof ListType))
                 continue;
 
-            if (value.requiresRead())
+            if (value.requiresRead(name.type))
             {
                 if (toRead == null)
                     toRead = new TreeSet<ByteBuffer>(UTF8Type.instance);
@@ -254,7 +254,7 @@ public class UpdateStatement extends ModificationStatement
             {
                 CFDefinition.Name name = entry.getKey();
                 Operation op = entry.getValue();
-                hasCounterColumn |= addToMutation(cf, builder.copy().add(name.name.key),
name, op, params, group == null || !op.requiresRead() ? null : group.getCollection(name.name.key));
+                hasCounterColumn |= addToMutation(cf, builder.copy().add(name.name.key),
name, op, params, group == null || !op.requiresRead(name.type) ? null : group.getCollection(name.name.key));
             }
         }
 
@@ -272,31 +272,18 @@ public class UpdateStatement extends ModificationStatement
 
         switch (type)
         {
-            case COLUMN:
-                if (valueDef != null && valueDef.type.isCollection())
-                {
-                    // This means this was a prepared statement where the whole collection
was provided
-                    // For have to deserialize it since it will be multiple columns
-                    ((ColumnOperation)valueOperation).executePreparedCollection(cf, builder.copy(),
(CollectionType)valueDef.type, params);
-                }
-                else
-                {
-                    valueOperation.execute(cf, builder.copy(), valueDef == null ? null :
valueDef.type, params);
-                }
-                break;
             case COUNTER:
                 if (valueDef != null && valueDef.type.isCollection())
                     throw new InvalidRequestException("Cannot assign collection value to
column with " + valueDef.type + " type.");
-
-                valueOperation.execute(cf, builder.copy(), valueDef == null ? null : valueDef.type,
params);
                 break;
-            default:
+            case LIST:
+            case SET:
+            case MAP:
                 if (!valueDef.type.isCollection())
                     throw new InvalidRequestException("Can't apply collection operation on
column with " + valueDef.type + " type.");
-
-                valueOperation.execute(cf, builder.copy(), (CollectionType) valueDef.type,
params, list);
                 break;
         }
+        valueOperation.execute(cf, builder.copy(), valueDef == null ? null : valueDef.type,
params, list);
         return valueOperation.getType() == Operation.Type.COUNTER;
     }
 


Mime
View raw message