cassandra-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From tylerho...@apache.org
Subject [3/3] cassandra git commit: Support for non-frozen UDTS
Date Fri, 08 Apr 2016 20:28:38 GMT
Support for non-frozen UDTS

Patch by Tyler Hobbs; reviewed by Benjamin Lerer for CASSANDRA-7423


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

Branch: refs/heads/trunk
Commit: 677230df694752c7ecf6d5459eee60ad7cf45ecf
Parents: 66fb8f5
Author: Tyler Hobbs <tylerlhobbs@gmail.com>
Authored: Fri Apr 8 11:56:39 2016 -0500
Committer: Tyler Hobbs <tylerlhobbs@gmail.com>
Committed: Fri Apr 8 15:26:12 2016 -0500

----------------------------------------------------------------------
 CHANGES.txt                                     |   2 +
 bin/cqlsh.py                                    |   3 +-
 doc/cql3/CQL.textile                            |  27 +-
 pylib/cqlshlib/cql3handling.py                  |  86 +++-
 pylib/cqlshlib/test/test_cqlsh_completion.py    |   6 +-
 src/antlr/Parser.g                              |  20 +-
 .../cassandra/config/ColumnDefinition.java      |  16 +-
 .../apache/cassandra/cql3/AbstractMarker.java   |  26 +-
 .../org/apache/cassandra/cql3/CQL3Type.java     |  86 ++--
 .../apache/cassandra/cql3/ColumnCondition.java  | 505 ++++++++++++++-----
 .../apache/cassandra/cql3/ColumnIdentifier.java |   4 +-
 .../org/apache/cassandra/cql3/Constants.java    |   2 +-
 .../org/apache/cassandra/cql3/Operation.java    | 145 ++++--
 src/java/org/apache/cassandra/cql3/Tuples.java  |  15 +-
 .../apache/cassandra/cql3/UntypedResultSet.java |   2 +-
 .../org/apache/cassandra/cql3/UserTypes.java    | 186 ++++++-
 .../cql3/functions/AbstractFunction.java        |   2 +-
 .../cassandra/cql3/functions/FunctionCall.java  |  24 +-
 .../cassandra/cql3/functions/UDAggregate.java   |   2 +-
 .../restrictions/StatementRestrictions.java     |  10 +-
 .../cassandra/cql3/selection/Selectable.java    |  22 +-
 .../cassandra/cql3/selection/Selection.java     |   8 +-
 .../cassandra/cql3/selection/Selector.java      |   2 +-
 .../cql3/statements/AlterTypeStatement.java     |  10 +-
 .../cql3/statements/CreateTableStatement.java   |  33 +-
 .../cql3/statements/CreateTypeStatement.java    |   6 +-
 .../cql3/statements/DeleteStatement.java        |   2 +-
 .../cql3/statements/ModificationStatement.java  |   2 +-
 .../cql3/statements/SelectStatement.java        |  12 +-
 .../cql3/statements/UpdateStatement.java        |   6 +-
 .../cassandra/db/marshal/AbstractType.java      |  21 +
 .../cassandra/db/marshal/CollectionType.java    |  30 +-
 .../apache/cassandra/db/marshal/TupleType.java  |  45 +-
 .../apache/cassandra/db/marshal/TypeParser.java |   6 +-
 .../apache/cassandra/db/marshal/UserType.java   | 166 +++++-
 .../org/apache/cassandra/db/rows/CellPath.java  |  13 +-
 .../cassandra/db/rows/ComplexColumnData.java    |   1 -
 .../org/apache/cassandra/schema/Functions.java  |   2 +-
 .../cassandra/schema/LegacySchemaMigrator.java  |   2 +-
 src/java/org/apache/cassandra/schema/Types.java |  36 +-
 .../cassandra/service/MigrationManager.java     |   1 +
 .../apache/cassandra/transport/DataType.java    |   2 +-
 .../cassandra/cql3/CQL3TypeLiteralTest.java     |   2 +-
 .../org/apache/cassandra/cql3/CQLTester.java    |  80 ++-
 .../cassandra/cql3/ColumnConditionTest.java     |  36 +-
 .../selection/SelectionColumnMappingTest.java   |   2 +-
 .../cql3/validation/entities/UserTypesTest.java | 463 +++++++++++------
 .../operations/InsertUpdateIfConditionTest.java | 371 ++++++++++++++
 .../schema/LegacySchemaMigratorTest.java        |  23 +-
 .../cassandra/transport/SerDeserTest.java       |   3 +-
 50 files changed, 2054 insertions(+), 523 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/677230df/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 74ba07e..5b71af1 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,6 @@
 3.6
+ * Support for non-frozen user-defined types, updating
+   individual fields of user-defined types (CASSANDRA-7423)
  * Make LZ4 compression level configurable (CASSANDRA-11051)
  * Allow per-partition LIMIT clause in CQL (CASSANDRA-7017)
  * Make custom filtering more extensible with UserExpression (CASSANDRA-11295)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/677230df/bin/cqlsh.py
----------------------------------------------------------------------
diff --git a/bin/cqlsh.py b/bin/cqlsh.py
index c3fcc48..2593486 100644
--- a/bin/cqlsh.py
+++ b/bin/cqlsh.py
@@ -898,8 +898,7 @@ class Shell(cmd.Cmd):
         except KeyError:
             raise UserTypeNotFound("User type %r not found" % typename)
 
-        return [(field_name, field_type.cql_parameterized_type())
-                for field_name, field_type in zip(user_type.field_names, user_type.field_types)]
+        return zip(user_type.field_names, user_type.field_types)
 
     def get_userfunction_names(self, ksname=None):
         if ksname is None:

http://git-wip-us.apache.org/repos/asf/cassandra/blob/677230df/doc/cql3/CQL.textile
----------------------------------------------------------------------
diff --git a/doc/cql3/CQL.textile b/doc/cql3/CQL.textile
index 83899d2..b0173a6 100644
--- a/doc/cql3/CQL.textile
+++ b/doc/cql3/CQL.textile
@@ -870,13 +870,17 @@ bc(syntax)..
                | <identifier> '=' <identifier> ('+' | '-') (<int-term> | <set-literal> | <list-literal>)
                | <identifier> '=' <identifier> '+' <map-literal>
                | <identifier> '[' <term> ']' '=' <term>
+               | <identifier> '.' <field> '=' <term>
 
 <condition> ::= <identifier> <op> <term>
-              | <identifier> IN (<variable> | '(' ( <term> ( ',' <term> )* )? ')')
+              | <identifier> IN <in-values>
               | <identifier> '[' <term> ']' <op> <term>
-              | <identifier> '[' <term> ']' IN <term>
+              | <identifier> '[' <term> ']' IN <in-values>
+              | <identifier> '.' <field> <op> <term>
+              | <identifier> '.' <field> IN <in-values>
 
 <op> ::= '<' | '<=' | '=' | '!=' | '>=' | '>'
+<in-values> ::= (<variable> | '(' ( <term> ( ',' <term> )* )? ')')
 
 <where-clause> ::= <relation> ( AND <relation> )*
 
@@ -913,6 +917,8 @@ The @c = c + 3@ form of @<assignment>@ is used to increment/decrement counters.
 
 The @id = id + <collection-literal>@ and @id[value1] = value2@ forms of @<assignment>@ are for collections. Please refer to the "relevant section":#collections for more details.
 
+The @id.field = <term>@ form of @<assignemt>@ is for setting the value of a single field on a non-frozen user-defined types.
+
 h4(#updateOptions). @<options>@
 
 The @UPDATE@ and @INSERT@ statements support the following options:
@@ -931,7 +937,9 @@ bc(syntax)..
                   WHERE <where-clause>
                   ( IF ( EXISTS | ( <condition> ( AND <condition> )*) ) )?
 
-<selection> ::= <identifier> ( '[' <term> ']' )?
+<selection> ::= <identifier>
+              | <identifier> '[' <term> ']'
+              | <identifier> '.' <field>
 
 <where-clause> ::= <relation> ( AND <relation> )*
 
@@ -943,11 +951,14 @@ bc(syntax)..
              | '(' <identifier> (',' <identifier>)* ')' IN <variable>
 
 <op> ::= '=' | '<' | '>' | '<=' | '>='
+<in-values> ::= (<variable> | '(' ( <term> ( ',' <term> )* )? ')')
 
 <condition> ::= <identifier> (<op> | '!=') <term>
-              | <identifier> IN (<variable> | '(' ( <term> ( ',' <term> )* )? ')')
+              | <identifier> IN <in-values>
               | <identifier> '[' <term> ']' (<op> | '!=') <term>
-              | <identifier> '[' <term> ']' IN <term>
+              | <identifier> '[' <term> ']' IN <in-values>
+              | <identifier> '.' <field> (<op> | '!=') <term>
+              | <identifier> '.' <field> IN <in-values>
 
 p. 
 __Sample:__
@@ -957,7 +968,7 @@ DELETE FROM NerdMovies USING TIMESTAMP 1240003134 WHERE movie = 'Serenity';
 
 DELETE phone FROM Users WHERE userid IN (C73DE1D3-AF08-40F3-B124-3FF3E5109F22, B70DE1D0-9908-4AE3-BE34-5573E5B09F14);
 p. 
-The @DELETE@ statement deletes columns and rows. If column names are provided directly after the @DELETE@ keyword, only those columns are deleted from the row indicated by the @<where-clause>@ (the @id[value]@ syntax in @<selection>@ is for collection, please refer to the "collection section":#collections for more details).  Otherwise, whole rows are removed. The @<where-clause>@ specifies which rows are to be deleted.  Multiple rows may be deleted with one statement by using an @IN@ clause.  A range of rows may be deleted using an inequality operator (such as @>=@).
+The @DELETE@ statement deletes columns and rows. If column names are provided directly after the @DELETE@ keyword, only those columns are deleted from the row indicated by the @<where-clause>@.  The @id[value]@ syntax in @<selection>@ is for non-frozen collections (please refer to the "collection section":#collections for more details).  The @id.field@ syntax is for the deletion of non-frozen user-defined types.  Otherwise, whole rows are removed. The @<where-clause>@ specifies which rows are to be deleted.  Multiple rows may be deleted with one statement by using an @IN@ clause.  A range of rows may be deleted using an inequality operator (such as @>=@).
 
 @DELETE@ supports the @TIMESTAMP@ option with the same semantics as the "@UPDATE@":#updateStmt statement.
 
@@ -2318,6 +2329,10 @@ h3. 3.4.2
 * "@ALTER TABLE@":#alterTableStmt @ADD@ and @DROP@ now allow mutiple columns to be added/removed
 * New "@PER PARTITION LIMIT@":#selectLimit option (see "CASSANDRA-7017":https://issues.apache.org/jira/browse/CASSANDRA-7017).
 
+h3. 3.4.2
+
+* User-defined types may now be stored in a non-frozen form, allowing individual fields to be updated and deleted in "@UPDATE@ statements":#updateStmt and "@DELETE@ statements":#deleteStmt, respectively.  (CASSANDRA-7423)
+
 h3. 3.4.1
 
 * Adds @CAST@ functions. See "@Cast@":#castFun.

http://git-wip-us.apache.org/repos/asf/cassandra/blob/677230df/pylib/cqlshlib/cql3handling.py
----------------------------------------------------------------------
diff --git a/pylib/cqlshlib/cql3handling.py b/pylib/cqlshlib/cql3handling.py
index 673234b..7abed2c 100644
--- a/pylib/cqlshlib/cql3handling.py
+++ b/pylib/cqlshlib/cql3handling.py
@@ -552,13 +552,13 @@ def ks_name_completer(ctxt, cass):
 
 
 @completer_for('nonSystemKeyspaceName', 'ksname')
-def ks_name_completer(ctxt, cass):
+def non_system_ks_name_completer(ctxt, cass):
     ksnames = [n for n in cass.get_keyspace_names() if n not in SYSTEM_KEYSPACES]
     return map(maybe_escape_name, ksnames)
 
 
 @completer_for('alterableKeyspaceName', 'ksname')
-def ks_name_completer(ctxt, cass):
+def alterable_ks_name_completer(ctxt, cass):
     ksnames = [n for n in cass.get_keyspace_names() if n not in NONALTERBALE_KEYSPACES]
     return map(maybe_escape_name, ksnames)
 
@@ -864,7 +864,6 @@ def insert_newval_completer(ctxt, cass):
 
 @completer_for('insertStatement', 'valcomma')
 def insert_valcomma_completer(ctxt, cass):
-    layout = get_table_meta(ctxt, cass)
     numcols = len(ctxt.get_binding('colname', ()))
     numvals = len(ctxt.get_binding('newval', ()))
     if numcols > numvals:
@@ -888,21 +887,27 @@ syntax_rules += r'''
                         ( "IF" ( "EXISTS" | <conditions> ))?
                     ;
 <assignment> ::= updatecol=<cident>
-                    ( "=" update_rhs=( <term> | <cident> )
+                    (( "=" update_rhs=( <term> | <cident> )
                                 ( counterop=( "+" | "-" ) inc=<wholenumber>
-                                | listadder="+" listcol=<cident> )?
-                    | indexbracket="[" <term> "]" "=" <term> )
+                                | listadder="+" listcol=<cident> )? )
+                    | ( indexbracket="[" <term> "]" "=" <term> )
+                    | ( udt_field_dot="." udt_field=<identifier> "=" <term> ))
                ;
 <conditions> ::=  <condition> ( "AND" <condition> )*
                ;
-<condition> ::= <cident> ( "[" <term> "]" )? (("=" | "<" | ">" | "<=" | ">=" | "!=") <term>
-                                             | "IN" "(" <term> ( "," <term> )* ")")
+<condition_op_and_rhs> ::= (("=" | "<" | ">" | "<=" | ">=" | "!=") <term>)
+                           | ("IN" "(" <term> ( "," <term> )* ")" )
+                         ;
+<condition> ::= conditioncol=<cident>
+                    ( (( indexbracket="[" <term> "]" )
+                      |( udt_field_dot="." udt_field=<identifier> )) )?
+                    <condition_op_and_rhs>
               ;
 '''
 
 
 @completer_for('updateStatement', 'updateopt')
-def insert_option_completer(ctxt, cass):
+def update_option_completer(ctxt, cass):
     opts = set('TIMESTAMP TTL'.split())
     for opt in ctxt.get_binding('updateopt', ()):
         opts.discard(opt.split()[0])
@@ -971,6 +976,62 @@ def update_indexbracket_completer(ctxt, cass):
         return ['[']
     return []
 
+
+@completer_for('assignment', 'udt_field_dot')
+def update_udt_field_dot_completer(ctxt, cass):
+    layout = get_table_meta(ctxt, cass)
+    curcol = dequote_name(ctxt.get_binding('updatecol', ''))
+    return ["."] if _is_usertype(layout, curcol) else []
+
+
+@completer_for('assignment', 'udt_field')
+def assignment_udt_field_completer(ctxt, cass):
+    layout = get_table_meta(ctxt, cass)
+    curcol = dequote_name(ctxt.get_binding('updatecol', ''))
+    return _usertype_fields(ctxt, cass, layout, curcol)
+
+
+def _is_usertype(layout, curcol):
+    coltype = layout.columns[curcol].cql_type
+    return coltype not in simple_cql_types and coltype not in ('map', 'set', 'list')
+
+
+def _usertype_fields(ctxt, cass, layout, curcol):
+    if not _is_usertype(layout, curcol):
+        return []
+
+    coltype = layout.columns[curcol].cql_type
+    ks = ctxt.get_binding('ksname', None)
+    if ks is not None:
+        ks = dequote_name(ks)
+    user_type = cass.get_usertype_layout(ks, coltype)
+    return [field_name for (field_name, field_type) in user_type]
+
+
+@completer_for('condition', 'indexbracket')
+def condition_indexbracket_completer(ctxt, cass):
+    layout = get_table_meta(ctxt, cass)
+    curcol = dequote_name(ctxt.get_binding('conditioncol', ''))
+    coltype = layout.columns[curcol].cql_type
+    if coltype in ('map', 'list'):
+        return ['[']
+    return []
+
+
+@completer_for('condition', 'udt_field_dot')
+def condition_udt_field_dot_completer(ctxt, cass):
+    layout = get_table_meta(ctxt, cass)
+    curcol = dequote_name(ctxt.get_binding('conditioncol', ''))
+    return ["."] if _is_usertype(layout, curcol) else []
+
+
+@completer_for('condition', 'udt_field')
+def condition_udt_field_completer(ctxt, cass):
+    layout = get_table_meta(ctxt, cass)
+    curcol = dequote_name(ctxt.get_binding('conditioncol', ''))
+    return _usertype_fields(ctxt, cass, layout, curcol)
+
+
 syntax_rules += r'''
 <deleteStatement> ::= "DELETE" ( <deleteSelector> ( "," <deleteSelector> )* )?
                         "FROM" cf=<columnFamilyName>
@@ -978,7 +1039,9 @@ syntax_rules += r'''
                         "WHERE" <whereClause>
                         ( "IF" ( "EXISTS" | <conditions> ) )?
                     ;
-<deleteSelector> ::= delcol=<cident> ( memberbracket="[" memberselector=<term> "]" )?
+<deleteSelector> ::= delcol=<cident>
+                     ( ( "[" <term> "]" )
+                     | ( "." <identifier> ) )?
                    ;
 <deleteOption> ::= "TIMESTAMP" <wholenumber>
                  ;
@@ -998,6 +1061,7 @@ def delete_delcol_completer(ctxt, cass):
     layout = get_table_meta(ctxt, cass)
     return map(maybe_escape_name, regular_column_names(layout))
 
+
 syntax_rules += r'''
 <batchStatement> ::= "BEGIN" ( "UNLOGGED" | "COUNTER" )? "BATCH"
                         ( "USING" [batchopt]=<usingOption>
@@ -1459,7 +1523,7 @@ def get_trigger_names(ctxt, cass):
 
 
 @completer_for('dropTriggerStatement', 'triggername')
-def alter_type_field_completer(ctxt, cass):
+def drop_trigger_completer(ctxt, cass):
     names = get_trigger_names(ctxt, cass)
     return map(maybe_escape_name, names)
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/677230df/pylib/cqlshlib/test/test_cqlsh_completion.py
----------------------------------------------------------------------
diff --git a/pylib/cqlshlib/test/test_cqlsh_completion.py b/pylib/cqlshlib/test/test_cqlsh_completion.py
index 0f0cc4d..1f1fb48 100644
--- a/pylib/cqlshlib/test/test_cqlsh_completion.py
+++ b/pylib/cqlshlib/test/test_cqlsh_completion.py
@@ -367,12 +367,12 @@ class TestCqlshCompletion(CqlshCompletionCase):
                             choices=['EXISTS', '<quotedName>', '<identifier>'])
 
         self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE TOKEN(lonelykey) <= TOKEN(13) IF EXISTS ",
-                            choices=['>=', '!=', '<=', 'IN', '[', ';', '=', '<', '>'])
+                            choices=['>=', '!=', '<=', 'IN', '[', ';', '=', '<', '>', '.'])
 
     def test_complete_in_delete(self):
         self.trycompletions('DELETE F', choices=['FROM', '<identifier>', '<quotedName>'])
 
-        self.trycompletions('DELETE a ', choices=['FROM', '[', ','])
+        self.trycompletions('DELETE a ', choices=['FROM', '[', '.', ','])
         self.trycompletions('DELETE a [',
                             choices=['<wholenumber>', 'false', '-', '<uuid>',
                                      '<pgStringLiteral>', '<float>', 'TOKEN',
@@ -449,7 +449,7 @@ class TestCqlshCompletion(CqlshCompletionCase):
                             choices=['EXISTS', '<identifier>', '<quotedName>'])
         self.trycompletions(('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 WHERE '
                              'TOKEN(a) >= TOKEN(0) IF b '),
-                            choices=['>=', '!=', '<=', 'IN', '[', '=', '<', '>'])
+                            choices=['>=', '!=', '<=', 'IN', '=', '<', '>'])
         self.trycompletions(('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 WHERE '
                              'TOKEN(a) >= TOKEN(0) IF b < 0 '),
                             choices=['AND', ';'])

http://git-wip-us.apache.org/repos/asf/cassandra/blob/677230df/src/antlr/Parser.g
----------------------------------------------------------------------
diff --git a/src/antlr/Parser.g b/src/antlr/Parser.g
index 36d4e20..cd17475 100644
--- a/src/antlr/Parser.g
+++ b/src/antlr/Parser.g
@@ -429,6 +429,7 @@ deleteSelection returns [List<Operation.RawDeletion> operations]
 deleteOp returns [Operation.RawDeletion op]
     : c=cident                { $op = new Operation.ColumnDeletion(c); }
     | c=cident '[' t=term ']' { $op = new Operation.ElementDeletion(c, t); }
+    | c=cident '.' field=cident { $op = new Operation.FieldDeletion(c, field); }
     ;
 
 usingClauseDelete[Attributes.Raw attrs]
@@ -1282,7 +1283,8 @@ columnOperation[List<Pair<ColumnIdentifier.Raw, Operation.RawUpdate>> operations
 
 columnOperationDifferentiator[List<Pair<ColumnIdentifier.Raw, Operation.RawUpdate>> operations, ColumnIdentifier.Raw key]
     : '=' normalColumnOperation[operations, key]
-    | '[' k=term ']' specializedColumnOperation[operations, key, k]
+    | '[' k=term ']' collectionColumnOperation[operations, key, k]
+    | '.' field=cident udtColumnOperation[operations, key, field]
     ;
 
 normalColumnOperation[List<Pair<ColumnIdentifier.Raw, Operation.RawUpdate>> operations, ColumnIdentifier.Raw key]
@@ -1315,13 +1317,20 @@ normalColumnOperation[List<Pair<ColumnIdentifier.Raw, Operation.RawUpdate>> oper
       }
     ;
 
-specializedColumnOperation[List<Pair<ColumnIdentifier.Raw, Operation.RawUpdate>> operations, ColumnIdentifier.Raw key, Term.Raw k]
+collectionColumnOperation[List<Pair<ColumnIdentifier.Raw, Operation.RawUpdate>> operations, ColumnIdentifier.Raw key, Term.Raw k]
     : '=' t=term
       {
           addRawUpdate(operations, key, new Operation.SetElement(k, t));
       }
     ;
 
+udtColumnOperation[List<Pair<ColumnIdentifier.Raw, Operation.RawUpdate>> operations, ColumnIdentifier.Raw key, ColumnIdentifier.Raw field]
+    : '=' t=term
+      {
+          addRawUpdate(operations, key, new Operation.SetField(field, t));
+      }
+    ;
+
 columnCondition[List<Pair<ColumnIdentifier.Raw, ColumnCondition.Raw>> conditions]
     // Note: we'll reject duplicates later
     : key=cident
@@ -1337,6 +1346,13 @@ columnCondition[List<Pair<ColumnIdentifier.Raw, ColumnCondition.Raw>> conditions
                 | marker=inMarker { conditions.add(Pair.create(key, ColumnCondition.Raw.collectionInCondition(element, marker))); }
                 )
             )
+        | '.' field=cident
+            ( op=relationType t=term { conditions.add(Pair.create(key, ColumnCondition.Raw.udtFieldCondition(t, field, op))); }
+            | K_IN
+                ( values=singleColumnInValues { conditions.add(Pair.create(key, ColumnCondition.Raw.udtFieldInCondition(field, values))); }
+                | marker=inMarker { conditions.add(Pair.create(key, ColumnCondition.Raw.udtFieldInCondition(field, marker))); }
+                )
+            )
         )
     ;
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/677230df/src/java/org/apache/cassandra/config/ColumnDefinition.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/ColumnDefinition.java b/src/java/org/apache/cassandra/config/ColumnDefinition.java
index 6bcc2e0..2c2cbb7 100644
--- a/src/java/org/apache/cassandra/config/ColumnDefinition.java
+++ b/src/java/org/apache/cassandra/config/ColumnDefinition.java
@@ -164,10 +164,13 @@ public class ColumnDefinition extends ColumnSpecification implements Comparable<
 
     private static Comparator<CellPath> makeCellPathComparator(Kind kind, AbstractType<?> type)
     {
-        if (kind.isPrimaryKeyKind() || !type.isCollection() || !type.isMultiCell())
+        if (kind.isPrimaryKeyKind() || !type.isMultiCell())
             return null;
 
-        CollectionType collection = (CollectionType) type;
+        AbstractType<?> nameComparator = type.isCollection()
+                                       ? ((CollectionType) type).nameComparator()
+                                       : ((UserType) type).nameComparator();
+
 
         return new Comparator<CellPath>()
         {
@@ -184,7 +187,7 @@ public class ColumnDefinition extends ColumnSpecification implements Comparable<
 
                 // This will get more complicated once we have non-frozen UDT and nested collections
                 assert path1.size() == 1 && path2.size() == 1;
-                return collection.nameComparator().compare(path1.get(0), path2.get(0));
+                return nameComparator.compare(path1.get(0), path2.get(0));
             }
         };
     }
@@ -365,8 +368,11 @@ public class ColumnDefinition extends ColumnSpecification implements Comparable<
         if (!isComplex())
             throw new MarshalException("Only complex cells should have a cell path");
 
-        assert type instanceof CollectionType;
-        ((CollectionType)type).nameComparator().validate(path.get(0));
+        assert type.isMultiCell();
+        if (type.isCollection())
+            ((CollectionType)type).nameComparator().validate(path.get(0));
+        else
+            ((UserType)type).nameComparator().validate(path.get(0));
     }
 
     public static String toCQLString(Iterable<ColumnDefinition> defs)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/677230df/src/java/org/apache/cassandra/cql3/AbstractMarker.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/AbstractMarker.java b/src/java/org/apache/cassandra/cql3/AbstractMarker.java
index d2bc022..e490bbf 100644
--- a/src/java/org/apache/cassandra/cql3/AbstractMarker.java
+++ b/src/java/org/apache/cassandra/cql3/AbstractMarker.java
@@ -67,16 +67,26 @@ public abstract class AbstractMarker extends Term.NonTerminal
 
         public NonTerminal prepare(String keyspace, ColumnSpecification receiver) throws InvalidRequestException
         {
-            if (!(receiver.type instanceof CollectionType))
-                return new Constants.Marker(bindIndex, receiver);
-
-            switch (((CollectionType)receiver.type).kind)
+            if (receiver.type.isCollection())
+            {
+                switch (((CollectionType) receiver.type).kind)
+                {
+                    case LIST:
+                        return new Lists.Marker(bindIndex, receiver);
+                    case SET:
+                        return new Sets.Marker(bindIndex, receiver);
+                    case MAP:
+                        return new Maps.Marker(bindIndex, receiver);
+                    default:
+                        throw new AssertionError();
+                }
+            }
+            else if (receiver.type.isUDT())
             {
-                case LIST: return new Lists.Marker(bindIndex, receiver);
-                case SET:  return new Sets.Marker(bindIndex, receiver);
-                case MAP:  return new Maps.Marker(bindIndex, receiver);
+                return new UserTypes.Marker(bindIndex, receiver);
             }
-            throw new AssertionError();
+
+            return new Constants.Marker(bindIndex, receiver);
         }
 
         public AssignmentTestable.TestResult testAssignment(String keyspace, ColumnSpecification receiver)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/677230df/src/java/org/apache/cassandra/cql3/CQL3Type.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/CQL3Type.java b/src/java/org/apache/cassandra/cql3/CQL3Type.java
index 98c0d97..d5dfeed 100644
--- a/src/java/org/apache/cassandra/cql3/CQL3Type.java
+++ b/src/java/org/apache/cassandra/cql3/CQL3Type.java
@@ -39,7 +39,16 @@ public interface CQL3Type
 {
     static final Logger logger = LoggerFactory.getLogger(CQL3Type.class);
 
-    public boolean isCollection();
+    default boolean isCollection()
+    {
+        return false;
+    }
+
+    default boolean isUDT()
+    {
+        return false;
+    }
+
     public AbstractType<?> getType();
 
     /**
@@ -82,11 +91,6 @@ public interface CQL3Type
             this.type = type;
         }
 
-        public boolean isCollection()
-        {
-            return false;
-        }
-
         public AbstractType<?> getType()
         {
             return type;
@@ -125,11 +129,6 @@ public interface CQL3Type
             this(TypeParser.parse(className));
         }
 
-        public boolean isCollection()
-        {
-            return false;
-        }
-
         public AbstractType<?> getType()
         {
             return type;
@@ -305,9 +304,9 @@ public interface CQL3Type
             return new UserDefined(UTF8Type.instance.compose(type.name), type);
         }
 
-        public boolean isCollection()
+        public boolean isUDT()
         {
-            return false;
+            return true;
         }
 
         public AbstractType<?> getType()
@@ -377,7 +376,10 @@ public interface CQL3Type
         @Override
         public String toString()
         {
-            return "frozen<" + ColumnIdentifier.maybeQuote(name) + '>';
+            if (type.isMultiCell())
+                return ColumnIdentifier.maybeQuote(name);
+            else
+                return "frozen<" + ColumnIdentifier.maybeQuote(name) + '>';
         }
     }
 
@@ -395,11 +397,6 @@ public interface CQL3Type
             return new Tuple(type);
         }
 
-        public boolean isCollection()
-        {
-            return false;
-        }
-
         public AbstractType<?> getType()
         {
             return type;
@@ -485,12 +482,7 @@ public interface CQL3Type
     {
         protected boolean frozen = false;
 
-        protected abstract boolean supportsFreezing();
-
-        public boolean isCollection()
-        {
-            return false;
-        }
+        public abstract boolean supportsFreezing();
 
         public boolean isFrozen()
         {
@@ -507,6 +499,11 @@ public interface CQL3Type
             return false;
         }
 
+        public boolean isUDT()
+        {
+            return false;
+        }
+
         public String keyspace()
         {
             return null;
@@ -588,7 +585,7 @@ public interface CQL3Type
                 return type;
             }
 
-            protected boolean supportsFreezing()
+            public boolean supportsFreezing()
             {
                 return false;
             }
@@ -627,7 +624,7 @@ public interface CQL3Type
                 frozen = true;
             }
 
-            protected boolean supportsFreezing()
+            public boolean supportsFreezing()
             {
                 return true;
             }
@@ -652,7 +649,7 @@ public interface CQL3Type
                 assert values != null : "Got null values type for a collection";
 
                 if (!frozen && values.supportsFreezing() && !values.frozen)
-                    throw new InvalidRequestException("Non-frozen collections are not allowed inside collections: " + this);
+                    throwNestedNonFrozenError(values);
 
                 // we represent Thrift supercolumns as maps, internally, and we do allow counters in supercolumns. Thus,
                 // for internal type parsing (think schema) we have to make an exception and allow counters as (map) values
@@ -664,22 +661,31 @@ public interface CQL3Type
                     if (keys.isCounter())
                         throw new InvalidRequestException("Counters are not allowed inside collections: " + this);
                     if (!frozen && keys.supportsFreezing() && !keys.frozen)
-                        throw new InvalidRequestException("Non-frozen collections are not allowed inside collections: " + this);
+                        throwNestedNonFrozenError(keys);
                 }
 
+                AbstractType<?> valueType = values.prepare(keyspace, udts).getType();
                 switch (kind)
                 {
                     case LIST:
-                        return new Collection(ListType.getInstance(values.prepare(keyspace, udts).getType(), !frozen));
+                        return new Collection(ListType.getInstance(valueType, !frozen));
                     case SET:
-                        return new Collection(SetType.getInstance(values.prepare(keyspace, udts).getType(), !frozen));
+                        return new Collection(SetType.getInstance(valueType, !frozen));
                     case MAP:
                         assert keys != null : "Got null keys type for a collection";
-                        return new Collection(MapType.getInstance(keys.prepare(keyspace, udts).getType(), values.prepare(keyspace, udts).getType(), !frozen));
+                        return new Collection(MapType.getInstance(keys.prepare(keyspace, udts).getType(), valueType, !frozen));
                 }
                 throw new AssertionError();
             }
 
+            private void throwNestedNonFrozenError(Raw innerType)
+            {
+                if (innerType instanceof RawCollection)
+                    throw new InvalidRequestException("Non-frozen collections are not allowed inside collections: " + this);
+                else
+                    throw new InvalidRequestException("Non-frozen UDTs are not allowed inside collections: " + this);
+            }
+
             public boolean referencesUserType(String name)
             {
                 return (keys != null && keys.referencesUserType(name)) || values.referencesUserType(name);
@@ -721,7 +727,7 @@ public interface CQL3Type
 
             public boolean canBeNonFrozen()
             {
-                return false;
+                return true;
             }
 
             public CQL3Type prepare(String keyspace, Types udts) throws InvalidRequestException
@@ -744,9 +750,8 @@ public interface CQL3Type
                 if (type == null)
                     throw new InvalidRequestException("Unknown type " + name);
 
-                if (!frozen)
-                    throw new InvalidRequestException("Non-frozen User-Defined types are not supported, please use frozen<>");
-
+                if (frozen)
+                    type = type.freeze();
                 return new UserDefined(name.toString(), type);
             }
 
@@ -755,7 +760,12 @@ public interface CQL3Type
                 return this.name.getStringTypeName().equals(name);
             }
 
-            protected boolean supportsFreezing()
+            public boolean supportsFreezing()
+            {
+                return true;
+            }
+
+            public boolean isUDT()
             {
                 return true;
             }
@@ -776,7 +786,7 @@ public interface CQL3Type
                 this.types = types;
             }
 
-            protected boolean supportsFreezing()
+            public boolean supportsFreezing()
             {
                 return true;
             }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/677230df/src/java/org/apache/cassandra/cql3/ColumnCondition.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/ColumnCondition.java b/src/java/org/apache/cassandra/cql3/ColumnCondition.java
index 0c2ea2a..8db0ecc 100644
--- a/src/java/org/apache/cassandra/cql3/ColumnCondition.java
+++ b/src/java/org/apache/cassandra/cql3/ColumnCondition.java
@@ -23,6 +23,7 @@ import java.util.*;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Iterators;
 
+import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.config.ColumnDefinition;
 import org.apache.cassandra.cql3.functions.Function;
 import org.apache.cassandra.db.rows.*;
@@ -31,8 +32,6 @@ import org.apache.cassandra.exceptions.InvalidRequestException;
 import org.apache.cassandra.transport.Server;
 import org.apache.cassandra.utils.ByteBufferUtil;
 
-import static com.google.common.collect.Lists.newArrayList;
-
 /**
  * A CQL3 condition on the value of a column or collection element.  For example, "UPDATE .. IF a = 0".
  */
@@ -43,51 +42,71 @@ public class ColumnCondition
     // For collection, when testing the equality of a specific element, null otherwise.
     private final Term collectionElement;
 
+    // For UDT, when testing the equality of a specific field, null otherwise.
+    private final ColumnIdentifier field;
+
     private final Term value;  // a single value or a marker for a list of IN values
     private final List<Term> inValues;
 
     public final Operator operator;
 
-    private ColumnCondition(ColumnDefinition column, Term collectionElement, Term value, List<Term> inValues, Operator op)
+    private ColumnCondition(ColumnDefinition column, Term collectionElement, ColumnIdentifier field, Term value, List<Term> inValues, Operator op)
     {
         this.column = column;
         this.collectionElement = collectionElement;
+        this.field = field;
         this.value = value;
         this.inValues = inValues;
         this.operator = op;
 
+        assert field == null || collectionElement == null;
         if (operator != Operator.IN)
             assert this.inValues == null;
     }
 
     public static ColumnCondition condition(ColumnDefinition column, Term value, Operator op)
     {
-        return new ColumnCondition(column, null, value, null, op);
+        return new ColumnCondition(column, null, null, value, null, op);
     }
 
     public static ColumnCondition condition(ColumnDefinition column, Term collectionElement, Term value, Operator op)
     {
-        return new ColumnCondition(column, collectionElement, value, null, op);
+        return new ColumnCondition(column, collectionElement, null, value, null, op);
+    }
+
+    public static ColumnCondition condition(ColumnDefinition column, ColumnIdentifier udtField, Term value, Operator op)
+    {
+        return new ColumnCondition(column, null, udtField, value, null, op);
     }
 
     public static ColumnCondition inCondition(ColumnDefinition column, List<Term> inValues)
     {
-        return new ColumnCondition(column, null, null, inValues, Operator.IN);
+        return new ColumnCondition(column, null, null, null, inValues, Operator.IN);
     }
 
     public static ColumnCondition inCondition(ColumnDefinition column, Term collectionElement, List<Term> inValues)
     {
-        return new ColumnCondition(column, collectionElement, null, inValues, Operator.IN);
+        return new ColumnCondition(column, collectionElement, null, null, inValues, Operator.IN);
+    }
+
+    public static ColumnCondition inCondition(ColumnDefinition column, ColumnIdentifier udtField, List<Term> inValues)
+    {
+        return new ColumnCondition(column, null, udtField, null, inValues, Operator.IN);
     }
 
     public static ColumnCondition inCondition(ColumnDefinition column, Term inMarker)
     {
-        return new ColumnCondition(column, null, inMarker, null, Operator.IN);
+        return new ColumnCondition(column, null, null, inMarker, null, Operator.IN);
     }
 
     public static ColumnCondition inCondition(ColumnDefinition column, Term collectionElement, Term inMarker)
     {
-        return new ColumnCondition(column, collectionElement, inMarker, null, Operator.IN);
+        return new ColumnCondition(column, collectionElement, null, inMarker, null, Operator.IN);
+    }
+
+    public static ColumnCondition inCondition(ColumnDefinition column, ColumnIdentifier udtField, Term inMarker)
+    {
+        return new ColumnCondition(column, null, udtField, inMarker, null, Operator.IN);
     }
 
     public Iterable<Function> getFunctions()
@@ -131,11 +150,19 @@ public class ColumnCondition
         boolean isInCondition = operator == Operator.IN;
         if (column.type instanceof CollectionType)
         {
-            if (collectionElement == null)
+            if (collectionElement != null)
+                return isInCondition ? new ElementAccessInBound(this, options) : new ElementAccessBound(this, options);
+            else
                 return isInCondition ? new CollectionInBound(this, options) : new CollectionBound(this, options);
+        }
+        else if (column.type.isUDT())
+        {
+            if (field != null)
+                return isInCondition ? new UDTFieldAccessInBound(this, options) : new UDTFieldAccessBound(this, options);
             else
-                return isInCondition ? new ElementAccessInBound(this, options) : new ElementAccessBound(this, options);
+                return isInCondition ? new UDTInBound(this, options) : new UDTBound(this, options);
         }
+
         return isInCondition ? new SimpleInBound(this, options) : new SimpleBound(this, options);
     }
 
@@ -216,6 +243,35 @@ public class ColumnCondition
         return complexData == null ? Collections.<Cell>emptyIterator() : complexData.iterator();
     }
 
+    private static boolean evaluateComparisonWithOperator(int comparison, Operator operator)
+    {
+        // called when comparison != 0
+        switch (operator)
+        {
+            case EQ:
+                return false;
+            case LT:
+            case LTE:
+                return comparison < 0;
+            case GT:
+            case GTE:
+                return comparison > 0;
+            case NEQ:
+                return true;
+            default:
+                throw new AssertionError();
+        }
+    }
+
+    private static ByteBuffer cellValueAtIndex(Iterator<Cell> iter, int index)
+    {
+        int adv = Iterators.advance(iter, index);
+        if (adv == index && iter.hasNext())
+            return iter.next().value();
+        else
+            return null;
+    }
+
     /**
      * A condition on a single non-collection column. This does not support IN operators (see SimpleInBound).
      */
@@ -226,7 +282,7 @@ public class ColumnCondition
         private SimpleBound(ColumnCondition condition, QueryOptions options) throws InvalidRequestException
         {
             super(condition.column, condition.operator);
-            assert !(column.type instanceof CollectionType) && condition.collectionElement == null;
+            assert !(column.type instanceof CollectionType) && condition.field == null;
             assert condition.operator != Operator.IN;
             this.value = condition.value.bindAndGet(options);
         }
@@ -247,7 +303,7 @@ public class ColumnCondition
         private SimpleInBound(ColumnCondition condition, QueryOptions options) throws InvalidRequestException
         {
             super(condition.column, condition.operator);
-            assert !(column.type instanceof CollectionType) && condition.collectionElement == null;
+            assert !(column.type instanceof CollectionType) && condition.field == null;
             assert condition.operator == Operator.IN;
             if (condition.inValues == null)
                 this.inValues = ((Lists.Value) condition.value.bind(options)).getElements();
@@ -311,7 +367,7 @@ public class ColumnCondition
             ListType listType = (ListType) column.type;
             if (column.type.isMultiCell())
             {
-                ByteBuffer columnValue = getListItem(getCells(row, column), getListIndex(collectionElement));
+                ByteBuffer columnValue = cellValueAtIndex(getCells(row, column), getListIndex(collectionElement));
                 return compareWithOperator(operator, ((ListType)column.type).getElementsType(), value, columnValue);
             }
             else
@@ -330,15 +386,6 @@ public class ColumnCondition
             return idx;
         }
 
-        static ByteBuffer getListItem(Iterator<Cell> iter, int index)
-        {
-            int adv = Iterators.advance(iter, index);
-            if (adv == index && iter.hasNext())
-                return iter.next().value();
-            else
-                return null;
-        }
-
         public ByteBuffer getCollectionElementValue()
         {
             return collectionElement;
@@ -371,72 +418,47 @@ public class ColumnCondition
             if (collectionElement == null)
                 throw new InvalidRequestException("Invalid null value for " + (column.type instanceof MapType ? "map" : "list") + " element access");
 
+            ByteBuffer cellValue;
+            AbstractType<?> valueType;
             if (column.type instanceof MapType)
             {
                 MapType mapType = (MapType) column.type;
-                AbstractType<?> valueType = mapType.getValuesType();
+                valueType = mapType.getValuesType();
                 if (column.type.isMultiCell())
                 {
-                    Cell item = getCell(row, column, CellPath.create(collectionElement));
-                    for (ByteBuffer value : inValues)
-                    {
-                        if (isSatisfiedByValue(value, item, valueType, Operator.EQ))
-                            return true;
-                    }
-                    return false;
+                    Cell cell = getCell(row, column, CellPath.create(collectionElement));
+                    cellValue = cell == null ? null : cell.value();
                 }
                 else
                 {
                     Cell cell = getCell(row, column);
-                    ByteBuffer mapElementValue = cell == null
-                                               ? null
-                                               : mapType.getSerializer().getSerializedValue(cell.value(), collectionElement, mapType.getKeysType());
-                    for (ByteBuffer value : inValues)
-                    {
-                        if (value == null)
-                        {
-                            if (mapElementValue == null)
-                                return true;
-                            continue;
-                        }
-                        if (valueType.compare(value, mapElementValue) == 0)
-                            return true;
-                    }
-                    return false;
+                    cellValue = cell == null
+                              ? null
+                              : mapType.getSerializer().getSerializedValue(cell.value(), collectionElement, mapType.getKeysType());
                 }
             }
-
-            ListType listType = (ListType) column.type;
-            AbstractType<?> elementsType = listType.getElementsType();
-            if (column.type.isMultiCell())
+            else // ListType
             {
-                ByteBuffer columnValue = ElementAccessBound.getListItem(getCells(row, column), ElementAccessBound.getListIndex(collectionElement));
-
-                for (ByteBuffer value : inValues)
+                ListType listType = (ListType) column.type;
+                valueType = listType.getElementsType();
+                if (column.type.isMultiCell())
                 {
-                    if (compareWithOperator(Operator.EQ, elementsType, value, columnValue))
-                        return true;
+                    cellValue = cellValueAtIndex(getCells(row, column), ElementAccessBound.getListIndex(collectionElement));
                 }
-            }
-            else
-            {
-                Cell cell = getCell(row, column);
-                ByteBuffer listElementValue = cell == null
-                                            ? null
-                                            : listType.getSerializer().getElement(cell.value(), ElementAccessBound.getListIndex(collectionElement));
-
-                for (ByteBuffer value : inValues)
+                else
                 {
-                    if (value == null)
-                    {
-                        if (listElementValue == null)
-                            return true;
-                        continue;
-                    }
-                    if (elementsType.compare(value, listElementValue) == 0)
-                        return true;
+                    Cell cell = getCell(row, column);
+                    cellValue = cell == null
+                              ? null
+                              : listType.getSerializer().getElement(cell.value(), ElementAccessBound.getListIndex(collectionElement));
                 }
             }
+
+            for (ByteBuffer value : inValues)
+            {
+                if (compareWithOperator(Operator.EQ, valueType, value, cellValue))
+                    return true;
+            }
             return false;
         }
     }
@@ -539,26 +561,6 @@ public class ColumnCondition
             return operator == Operator.EQ || operator == Operator.LTE || operator == Operator.GTE;
         }
 
-        private static boolean evaluateComparisonWithOperator(int comparison, Operator operator)
-        {
-            // called when comparison != 0
-            switch (operator)
-            {
-                case EQ:
-                    return false;
-                case LT:
-                case LTE:
-                    return comparison < 0;
-                case GT:
-                case GTE:
-                    return comparison > 0;
-                case NEQ:
-                    return true;
-                default:
-                    throw new AssertionError();
-            }
-        }
-
         static boolean listAppliesTo(ListType type, Iterator<Cell> iter, List<ByteBuffer> elements, Operator operator)
         {
             return setOrListAppliesTo(type.getElementsType(), iter, elements.iterator(), operator, false);
@@ -691,6 +693,195 @@ public class ColumnCondition
         }
     }
 
+    /** A condition on a UDT field. IN operators are not supported here, see UDTFieldAccessInBound. */
+    static class UDTFieldAccessBound extends Bound
+    {
+        public final ColumnIdentifier field;
+        public final ByteBuffer value;
+
+        private UDTFieldAccessBound(ColumnCondition condition, QueryOptions options) throws InvalidRequestException
+        {
+            super(condition.column, condition.operator);
+            assert column.type.isUDT() && condition.field != null;
+            assert condition.operator != Operator.IN;
+            this.field = condition.field;
+            this.value = condition.value.bindAndGet(options);
+        }
+
+        public boolean appliesTo(Row row) throws InvalidRequestException
+        {
+            UserType userType = (UserType) column.type;
+            int fieldPosition = userType.fieldPosition(field);
+            assert fieldPosition >= 0;
+
+            ByteBuffer cellValue;
+            if (column.type.isMultiCell())
+            {
+                Cell cell = getCell(row, column, userType.cellPathForField(field.bytes));
+                cellValue = cell == null ? null : cell.value();
+            }
+            else
+            {
+                Cell cell = getCell(row, column);
+                cellValue = cell == null
+                          ? null
+                          : userType.split(cell.value())[fieldPosition];
+            }
+            return compareWithOperator(operator, userType.fieldType(fieldPosition), value, cellValue);
+        }
+    }
+
+    /** An IN condition on a UDT field.  For example: IF user.name IN ('a', 'b') */
+    static class UDTFieldAccessInBound extends Bound
+    {
+        public final ColumnIdentifier field;
+        public final List<ByteBuffer> inValues;
+
+        private UDTFieldAccessInBound(ColumnCondition condition, QueryOptions options) throws InvalidRequestException
+        {
+            super(condition.column, condition.operator);
+            assert column.type.isUDT() && condition.field != null;
+            this.field = condition.field;
+
+            if (condition.inValues == null)
+                this.inValues = ((Lists.Value) condition.value.bind(options)).getElements();
+            else
+            {
+                this.inValues = new ArrayList<>(condition.inValues.size());
+                for (Term value : condition.inValues)
+                    this.inValues.add(value.bindAndGet(options));
+            }
+        }
+
+        public boolean appliesTo(Row row) throws InvalidRequestException
+        {
+            UserType userType = (UserType) column.type;
+            int fieldPosition = userType.fieldPosition(field);
+            assert fieldPosition >= 0;
+
+            ByteBuffer cellValue;
+            if (column.type.isMultiCell())
+            {
+                Cell cell = getCell(row, column, userType.cellPathForField(field.bytes));
+                cellValue = cell == null ? null : cell.value();
+            }
+            else
+            {
+                Cell cell = getCell(row, column);
+                cellValue = cell == null ? null : userType.split(getCell(row, column).value())[fieldPosition];
+            }
+
+            AbstractType<?> valueType = userType.fieldType(fieldPosition);
+            for (ByteBuffer value : inValues)
+            {
+                if (compareWithOperator(Operator.EQ, valueType, value, cellValue))
+                    return true;
+            }
+            return false;
+        }
+    }
+
+    /** A non-IN condition on an entire UDT.  For example: IF user = {name: 'joe', age: 42}). */
+    static class UDTBound extends Bound
+    {
+        private final ByteBuffer value;
+        private final int protocolVersion;
+
+        private UDTBound(ColumnCondition condition, QueryOptions options) throws InvalidRequestException
+        {
+            super(condition.column, condition.operator);
+            assert column.type.isUDT() && condition.field == null;
+            assert condition.operator != Operator.IN;
+            protocolVersion = options.getProtocolVersion();
+            value = condition.value.bindAndGet(options);
+        }
+
+        public boolean appliesTo(Row row) throws InvalidRequestException
+        {
+            UserType userType = (UserType) column.type;
+            ByteBuffer rowValue;
+            if (userType.isMultiCell())
+            {
+                Iterator<Cell> iter = getCells(row, column);
+                rowValue = iter.hasNext() ? userType.serializeForNativeProtocol(iter, protocolVersion) : null;
+            }
+            else
+            {
+                Cell cell = getCell(row, column);
+                rowValue = cell == null ? null : cell.value();
+            }
+
+            if (value == null)
+            {
+                if (operator == Operator.EQ)
+                    return rowValue == null;
+                else if (operator == Operator.NEQ)
+                    return rowValue != null;
+                else
+                    throw new InvalidRequestException(String.format("Invalid comparison with null for operator \"%s\"", operator));
+            }
+
+            return compareWithOperator(operator, userType, value, rowValue);
+        }
+    }
+
+    /** An IN condition on an entire UDT.  For example: IF user IN ({name: 'joe', age: 42}, {name: 'bob', age: 23}). */
+    public static class UDTInBound extends Bound
+    {
+        private final List<ByteBuffer> inValues;
+        private final int protocolVersion;
+
+        private UDTInBound(ColumnCondition condition, QueryOptions options) throws InvalidRequestException
+        {
+            super(condition.column, condition.operator);
+            assert column.type.isUDT() && condition.field == null;
+            assert condition.operator == Operator.IN;
+            protocolVersion = options.getProtocolVersion();
+            inValues = new ArrayList<>();
+            if (condition.inValues == null)
+            {
+                Lists.Marker inValuesMarker = (Lists.Marker) condition.value;
+                for (ByteBuffer buffer : ((Lists.Value)inValuesMarker.bind(options)).elements)
+                    this.inValues.add(buffer);
+            }
+            else
+            {
+                for (Term value : condition.inValues)
+                    this.inValues.add(value.bindAndGet(options));
+            }
+        }
+
+        public boolean appliesTo(Row row) throws InvalidRequestException
+        {
+            UserType userType = (UserType) column.type;
+            ByteBuffer rowValue;
+            if (userType.isMultiCell())
+            {
+                Iterator<Cell> cells = getCells(row, column);
+                rowValue = cells.hasNext() ? userType.serializeForNativeProtocol(cells, protocolVersion) : null;
+            }
+            else
+            {
+                Cell cell = getCell(row, column);
+                rowValue = cell == null ? null : cell.value();
+            }
+
+            for (ByteBuffer value : inValues)
+            {
+                if (value == null || rowValue == null)
+                {
+                    if (value == rowValue) // both null
+                        return true;
+                }
+                else if (userType.compare(value, rowValue) == 0)
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
     public static class Raw
     {
         private final Term.Raw value;
@@ -700,106 +891,156 @@ public class ColumnCondition
         // Can be null, only used with the syntax "IF m[e] = ..." (in which case it's 'e')
         private final Term.Raw collectionElement;
 
+        // Can be null, only used with the syntax "IF udt.field = ..." (in which case it's 'field')
+        private final ColumnIdentifier.Raw udtField;
+
         private final Operator operator;
 
-        private Raw(Term.Raw value, List<Term.Raw> inValues, AbstractMarker.INRaw inMarker, Term.Raw collectionElement, Operator op)
+        private Raw(Term.Raw value, List<Term.Raw> inValues, AbstractMarker.INRaw inMarker, Term.Raw collectionElement,
+                    ColumnIdentifier.Raw udtField, Operator op)
         {
             this.value = value;
             this.inValues = inValues;
             this.inMarker = inMarker;
             this.collectionElement = collectionElement;
+            this.udtField = udtField;
             this.operator = op;
         }
 
         /** A condition on a column. For example: "IF col = 'foo'" */
         public static Raw simpleCondition(Term.Raw value, Operator op)
         {
-            return new Raw(value, null, null, null, op);
+            return new Raw(value, null, null, null, null, op);
         }
 
         /** An IN condition on a column. For example: "IF col IN ('foo', 'bar', ...)" */
         public static Raw simpleInCondition(List<Term.Raw> inValues)
         {
-            return new Raw(null, inValues, null, null, Operator.IN);
+            return new Raw(null, inValues, null, null, null, Operator.IN);
         }
 
         /** An IN condition on a column with a single marker. For example: "IF col IN ?" */
         public static Raw simpleInCondition(AbstractMarker.INRaw inMarker)
         {
-            return new Raw(null, null, inMarker, null, Operator.IN);
+            return new Raw(null, null, inMarker, null, null, Operator.IN);
         }
 
         /** A condition on a collection element. For example: "IF col['key'] = 'foo'" */
         public static Raw collectionCondition(Term.Raw value, Term.Raw collectionElement, Operator op)
         {
-            return new Raw(value, null, null, collectionElement, op);
+            return new Raw(value, null, null, collectionElement, null, op);
         }
 
         /** An IN condition on a collection element. For example: "IF col['key'] IN ('foo', 'bar', ...)" */
         public static Raw collectionInCondition(Term.Raw collectionElement, List<Term.Raw> inValues)
         {
-            return new Raw(null, inValues, null, collectionElement, Operator.IN);
+            return new Raw(null, inValues, null, collectionElement, null, Operator.IN);
         }
 
         /** An IN condition on a collection element with a single marker. For example: "IF col['key'] IN ?" */
         public static Raw collectionInCondition(Term.Raw collectionElement, AbstractMarker.INRaw inMarker)
         {
-            return new Raw(null, null, inMarker, collectionElement, Operator.IN);
+            return new Raw(null, null, inMarker, collectionElement, null, Operator.IN);
+        }
+
+        /** A condition on a UDT field. For example: "IF col.field = 'foo'" */
+        public static Raw udtFieldCondition(Term.Raw value, ColumnIdentifier.Raw udtField, Operator op)
+        {
+            return new Raw(value, null, null, null, udtField, op);
         }
 
-        public ColumnCondition prepare(String keyspace, ColumnDefinition receiver) throws InvalidRequestException
+        /** An IN condition on a collection element. For example: "IF col.field IN ('foo', 'bar', ...)" */
+        public static Raw udtFieldInCondition(ColumnIdentifier.Raw udtField, List<Term.Raw> inValues)
+        {
+            return new Raw(null, inValues, null, null, udtField, Operator.IN);
+        }
+
+        /** An IN condition on a collection element with a single marker. For example: "IF col.field IN ?" */
+        public static Raw udtFieldInCondition(ColumnIdentifier.Raw udtField, AbstractMarker.INRaw inMarker)
+        {
+            return new Raw(null, null, inMarker, null, udtField, Operator.IN);
+        }
+
+        public ColumnCondition prepare(String keyspace, ColumnDefinition receiver, CFMetaData cfm) throws InvalidRequestException
         {
             if (receiver.type instanceof CounterColumnType)
                 throw new InvalidRequestException("Conditions on counters are not supported");
 
-            if (collectionElement == null)
+            if (collectionElement != null)
             {
+                if (!(receiver.type.isCollection()))
+                    throw new InvalidRequestException(String.format("Invalid element access syntax for non-collection column %s", receiver.name));
+
+                ColumnSpecification elementSpec, valueSpec;
+                switch ((((CollectionType) receiver.type).kind))
+                {
+                    case LIST:
+                        elementSpec = Lists.indexSpecOf(receiver);
+                        valueSpec = Lists.valueSpecOf(receiver);
+                        break;
+                    case MAP:
+                        elementSpec = Maps.keySpecOf(receiver);
+                        valueSpec = Maps.valueSpecOf(receiver);
+                        break;
+                    case SET:
+                        throw new InvalidRequestException(String.format("Invalid element access syntax for set column %s", receiver.name));
+                    default:
+                        throw new AssertionError();
+                }
                 if (operator == Operator.IN)
                 {
                     if (inValues == null)
-                        return ColumnCondition.inCondition(receiver, inMarker.prepare(keyspace, receiver));
+                        return ColumnCondition.inCondition(receiver, collectionElement.prepare(keyspace, elementSpec), inMarker.prepare(keyspace, valueSpec));
                     List<Term> terms = new ArrayList<>(inValues.size());
                     for (Term.Raw value : inValues)
-                        terms.add(value.prepare(keyspace, receiver));
-                    return ColumnCondition.inCondition(receiver, terms);
+                        terms.add(value.prepare(keyspace, valueSpec));
+                    return ColumnCondition.inCondition(receiver, collectionElement.prepare(keyspace, elementSpec), terms);
                 }
                 else
                 {
-                    return ColumnCondition.condition(receiver, value.prepare(keyspace, receiver), operator);
+                    return ColumnCondition.condition(receiver, collectionElement.prepare(keyspace, elementSpec), value.prepare(keyspace, valueSpec), operator);
                 }
             }
+            else if (udtField != null)
+            {
+                UserType userType = (UserType) receiver.type;
+                ColumnIdentifier fieldIdentifier = udtField.prepare(cfm);
 
-            if (!(receiver.type.isCollection()))
-                throw new InvalidRequestException(String.format("Invalid element access syntax for non-collection column %s", receiver.name));
+                int fieldPosition = userType.fieldPosition(fieldIdentifier);
+                if (fieldPosition == -1)
+                    throw new InvalidRequestException(String.format("Unknown field %s for column %s", fieldIdentifier, receiver.name));
 
-            ColumnSpecification elementSpec, valueSpec;
-            switch ((((CollectionType)receiver.type).kind))
-            {
-                case LIST:
-                    elementSpec = Lists.indexSpecOf(receiver);
-                    valueSpec = Lists.valueSpecOf(receiver);
-                    break;
-                case MAP:
-                    elementSpec = Maps.keySpecOf(receiver);
-                    valueSpec = Maps.valueSpecOf(receiver);
-                    break;
-                case SET:
-                    throw new InvalidRequestException(String.format("Invalid element access syntax for set column %s", receiver.name));
-                default:
-                    throw new AssertionError();
-            }
-            if (operator == Operator.IN)
-            {
-                if (inValues == null)
-                    return ColumnCondition.inCondition(receiver, collectionElement.prepare(keyspace, elementSpec), inMarker.prepare(keyspace, valueSpec));
-                List<Term> terms = new ArrayList<>(inValues.size());
-                for (Term.Raw value : inValues)
-                    terms.add(value.prepare(keyspace, valueSpec));
-                return ColumnCondition.inCondition(receiver, collectionElement.prepare(keyspace, elementSpec), terms);
+                ColumnSpecification fieldReceiver = UserTypes.fieldSpecOf(receiver, fieldPosition);
+                if (operator == Operator.IN)
+                {
+                    if (inValues == null)
+                        return ColumnCondition.inCondition(receiver, udtField.prepare(cfm), inMarker.prepare(keyspace, fieldReceiver));
+
+                    List<Term> terms = new ArrayList<>(inValues.size());
+                    for (Term.Raw value : inValues)
+                        terms.add(value.prepare(keyspace, fieldReceiver));
+                    return ColumnCondition.inCondition(receiver, udtField.prepare(cfm), terms);
+                }
+                else
+                {
+                    return ColumnCondition.condition(receiver, udtField.prepare(cfm), value.prepare(keyspace, fieldReceiver), operator);
+                }
             }
             else
             {
-                return ColumnCondition.condition(receiver, collectionElement.prepare(keyspace, elementSpec), value.prepare(keyspace, valueSpec), operator);
+                if (operator == Operator.IN)
+                {
+                    if (inValues == null)
+                        return ColumnCondition.inCondition(receiver, inMarker.prepare(keyspace, receiver));
+                    List<Term> terms = new ArrayList<>(inValues.size());
+                    for (Term.Raw value : inValues)
+                        terms.add(value.prepare(keyspace, receiver));
+                    return ColumnCondition.inCondition(receiver, terms);
+                }
+                else
+                {
+                    return ColumnCondition.condition(receiver, value.prepare(keyspace, receiver), operator);
+                }
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/677230df/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java b/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java
index 74d0d28..f202145 100644
--- a/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java
+++ b/src/java/org/apache/cassandra/cql3/ColumnIdentifier.java
@@ -24,6 +24,7 @@ import java.util.concurrent.ConcurrentMap;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.MapMaker;
 
 import org.apache.cassandra.cache.IMeasurableMemory;
@@ -329,7 +330,8 @@ public class ColumnIdentifier extends Selectable implements IMeasurableMemory, C
         }
     }
 
-    static String maybeQuote(String text)
+    @VisibleForTesting
+    public static String maybeQuote(String text)
     {
         if (UNQUOTED_IDENTIFIER.matcher(text).matches())
             return text;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/677230df/src/java/org/apache/cassandra/cql3/Constants.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Constants.java b/src/java/org/apache/cassandra/cql3/Constants.java
index 4982c49..2efce59 100644
--- a/src/java/org/apache/cassandra/cql3/Constants.java
+++ b/src/java/org/apache/cassandra/cql3/Constants.java
@@ -162,7 +162,7 @@ public abstract class Constants
         public AssignmentTestable.TestResult testAssignment(String keyspace, ColumnSpecification receiver)
         {
             CQL3Type receiverType = receiver.type.asCQL3Type();
-            if (receiverType.isCollection())
+            if (receiverType.isCollection() || receiverType.isUDT())
                 return AssignmentTestable.TestResult.NOT_ASSIGNABLE;
 
             if (!(receiverType instanceof CQL3Type.Native))

http://git-wip-us.apache.org/repos/asf/cassandra/blob/677230df/src/java/org/apache/cassandra/cql3/Operation.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Operation.java b/src/java/org/apache/cassandra/cql3/Operation.java
index 51c8969..bfb05e7 100644
--- a/src/java/org/apache/cassandra/cql3/Operation.java
+++ b/src/java/org/apache/cassandra/cql3/Operation.java
@@ -19,6 +19,7 @@ package org.apache.cassandra.cql3;
 
 import java.util.Collections;
 
+import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.config.ColumnDefinition;
 import org.apache.cassandra.cql3.functions.Function;
 import org.apache.cassandra.db.DecoratedKey;
@@ -107,12 +108,10 @@ public abstract class Operation
          * It returns an Operation which can be though as post-preparation well-typed
          * Operation.
          *
-         * @param receiver the "column" this operation applies to. Note that
-         * contrarly to the method of same name in Term.Raw, the receiver should always
-         * be a true column.
+         * @param receiver the column this operation applies to.
          * @return the prepared update operation.
          */
-        public Operation prepare(String keyspace, ColumnDefinition receiver) throws InvalidRequestException;
+        public Operation prepare(CFMetaData cfm, ColumnDefinition receiver) throws InvalidRequestException;
 
         /**
          * @return whether this operation can be applied alongside the {@code
@@ -146,7 +145,7 @@ public abstract class Operation
          * @param receiver the "column" this operation applies to.
          * @return the prepared delete operation.
          */
-        public Operation prepare(String keyspace, ColumnDefinition receiver) throws InvalidRequestException;
+        public Operation prepare(String keyspace, ColumnDefinition receiver, CFMetaData cfm) throws InvalidRequestException;
     }
 
     public static class SetValue implements RawUpdate
@@ -158,26 +157,32 @@ public abstract class Operation
             this.value = value;
         }
 
-        public Operation prepare(String keyspace, ColumnDefinition receiver) throws InvalidRequestException
+        public Operation prepare(CFMetaData cfm, ColumnDefinition receiver) throws InvalidRequestException
         {
-            Term v = value.prepare(keyspace, receiver);
+            Term v = value.prepare(cfm.ksName, receiver);
 
             if (receiver.type instanceof CounterColumnType)
                 throw new InvalidRequestException(String.format("Cannot set the value of counter column %s (counters can only be incremented/decremented, not set)", receiver.name));
 
-            if (!(receiver.type.isCollection()))
-                return new Constants.Setter(receiver, v);
-
-            switch (((CollectionType)receiver.type).kind)
+            if (receiver.type.isCollection())
             {
-                case LIST:
-                    return new Lists.Setter(receiver, v);
-                case SET:
-                    return new Sets.Setter(receiver, v);
-                case MAP:
-                    return new Maps.Setter(receiver, v);
+                switch (((CollectionType) receiver.type).kind)
+                {
+                    case LIST:
+                        return new Lists.Setter(receiver, v);
+                    case SET:
+                        return new Sets.Setter(receiver, v);
+                    case MAP:
+                        return new Maps.Setter(receiver, v);
+                    default:
+                        throw new AssertionError();
+                }
             }
-            throw new AssertionError();
+
+            if (receiver.type.isUDT())
+                return new UserTypes.Setter(receiver, v);
+
+            return new Constants.Setter(receiver, v);
         }
 
         protected String toString(ColumnSpecification column)
@@ -204,7 +209,7 @@ public abstract class Operation
             this.value = value;
         }
 
-        public Operation prepare(String keyspace, ColumnDefinition receiver) throws InvalidRequestException
+        public Operation prepare(CFMetaData cfm, ColumnDefinition receiver) throws InvalidRequestException
         {
             if (!(receiver.type instanceof CollectionType))
                 throw new InvalidRequestException(String.format("Invalid operation (%s) for non collection column %s", toString(receiver), receiver.name));
@@ -214,14 +219,14 @@ public abstract class Operation
             switch (((CollectionType)receiver.type).kind)
             {
                 case LIST:
-                    Term idx = selector.prepare(keyspace, Lists.indexSpecOf(receiver));
-                    Term lval = value.prepare(keyspace, Lists.valueSpecOf(receiver));
+                    Term idx = selector.prepare(cfm.ksName, Lists.indexSpecOf(receiver));
+                    Term lval = value.prepare(cfm.ksName, Lists.valueSpecOf(receiver));
                     return new Lists.SetterByIndex(receiver, idx, lval);
                 case SET:
                     throw new InvalidRequestException(String.format("Invalid operation (%s) for set column %s", toString(receiver), receiver.name));
                 case MAP:
-                    Term key = selector.prepare(keyspace, Maps.keySpecOf(receiver));
-                    Term mval = value.prepare(keyspace, Maps.valueSpecOf(receiver));
+                    Term key = selector.prepare(cfm.ksName, Maps.keySpecOf(receiver));
+                    Term mval = value.prepare(cfm.ksName, Maps.valueSpecOf(receiver));
                     return new Maps.SetterByKey(receiver, key, mval);
             }
             throw new AssertionError();
@@ -240,6 +245,47 @@ public abstract class Operation
         }
     }
 
+    public static class SetField implements RawUpdate
+    {
+        private final ColumnIdentifier.Raw field;
+        private final Term.Raw value;
+
+        public SetField(ColumnIdentifier.Raw field, Term.Raw value)
+        {
+            this.field = field;
+            this.value = value;
+        }
+
+        public Operation prepare(CFMetaData cfm, ColumnDefinition receiver) throws InvalidRequestException
+        {
+            if (!receiver.type.isUDT())
+                throw new InvalidRequestException(String.format("Invalid operation (%s) for non-UDT column %s", toString(receiver), receiver.name));
+            else if (!receiver.type.isMultiCell())
+                throw new InvalidRequestException(String.format("Invalid operation (%s) for frozen UDT column %s", toString(receiver), receiver.name));
+
+            ColumnIdentifier fieldIdentifier = field.prepare(cfm);
+            int fieldPosition = ((UserType) receiver.type).fieldPosition(fieldIdentifier);
+            if (fieldPosition == -1)
+                throw new InvalidRequestException(String.format("UDT column %s does not have a field named %s", receiver.name, fieldIdentifier));
+
+            Term val = value.prepare(cfm.ksName, UserTypes.fieldSpecOf(receiver, fieldPosition));
+            return new UserTypes.SetterByField(receiver, fieldIdentifier, val);
+        }
+
+        protected String toString(ColumnSpecification column)
+        {
+            return String.format("%s.%s = %s", column.name, field, value);
+        }
+
+        public boolean isCompatibleWith(RawUpdate other)
+        {
+            if (other instanceof SetField)
+                return !((SetField) other).field.equals(field);
+            else
+                return !(other instanceof SetValue);
+        }
+    }
+
     public static class Addition implements RawUpdate
     {
         private final Term.Raw value;
@@ -249,9 +295,9 @@ public abstract class Operation
             this.value = value;
         }
 
-        public Operation prepare(String keyspace, ColumnDefinition receiver) throws InvalidRequestException
+        public Operation prepare(CFMetaData cfm, ColumnDefinition receiver) throws InvalidRequestException
         {
-            Term v = value.prepare(keyspace, receiver);
+            Term v = value.prepare(cfm.ksName, receiver);
 
             if (!(receiver.type instanceof CollectionType))
             {
@@ -294,13 +340,13 @@ public abstract class Operation
             this.value = value;
         }
 
-        public Operation prepare(String keyspace, ColumnDefinition receiver) throws InvalidRequestException
+        public Operation prepare(CFMetaData cfm, ColumnDefinition receiver) throws InvalidRequestException
         {
             if (!(receiver.type instanceof CollectionType))
             {
                 if (!(receiver.type instanceof CounterColumnType))
                     throw new InvalidRequestException(String.format("Invalid operation (%s) for non counter column %s", toString(receiver), receiver.name));
-                return new Constants.Substracter(receiver, value.prepare(keyspace, receiver));
+                return new Constants.Substracter(receiver, value.prepare(cfm.ksName, receiver));
             }
             else if (!(receiver.type.isMultiCell()))
                 throw new InvalidRequestException(String.format("Invalid operation (%s) for frozen collection column %s", toString(receiver), receiver.name));
@@ -308,16 +354,16 @@ public abstract class Operation
             switch (((CollectionType)receiver.type).kind)
             {
                 case LIST:
-                    return new Lists.Discarder(receiver, value.prepare(keyspace, receiver));
+                    return new Lists.Discarder(receiver, value.prepare(cfm.ksName, receiver));
                 case SET:
-                    return new Sets.Discarder(receiver, value.prepare(keyspace, receiver));
+                    return new Sets.Discarder(receiver, value.prepare(cfm.ksName, receiver));
                 case MAP:
                     // The value for a map subtraction is actually a set
                     ColumnSpecification vr = new ColumnSpecification(receiver.ksName,
                                                                      receiver.cfName,
                                                                      receiver.name,
                                                                      SetType.getInstance(((MapType)receiver.type).getKeysType(), false));
-                    return new Sets.Discarder(receiver, value.prepare(keyspace, vr));
+                    return new Sets.Discarder(receiver, value.prepare(cfm.ksName, vr));
             }
             throw new AssertionError();
         }
@@ -342,9 +388,9 @@ public abstract class Operation
             this.value = value;
         }
 
-        public Operation prepare(String keyspace, ColumnDefinition receiver) throws InvalidRequestException
+        public Operation prepare(CFMetaData cfm, ColumnDefinition receiver) throws InvalidRequestException
         {
-            Term v = value.prepare(keyspace, receiver);
+            Term v = value.prepare(cfm.ksName, receiver);
 
             if (!(receiver.type instanceof ListType))
                 throw new InvalidRequestException(String.format("Invalid operation (%s) for non list column %s", toString(receiver), receiver.name));
@@ -379,7 +425,7 @@ public abstract class Operation
             return id;
         }
 
-        public Operation prepare(String keyspace, ColumnDefinition receiver) throws InvalidRequestException
+        public Operation prepare(String keyspace, ColumnDefinition receiver, CFMetaData cfm) throws InvalidRequestException
         {
             // No validation, deleting a column is always "well typed"
             return new Constants.Deleter(receiver);
@@ -402,7 +448,7 @@ public abstract class Operation
             return id;
         }
 
-        public Operation prepare(String keyspace, ColumnDefinition receiver) throws InvalidRequestException
+        public Operation prepare(String keyspace, ColumnDefinition receiver, CFMetaData cfm) throws InvalidRequestException
         {
             if (!(receiver.type.isCollection()))
                 throw new InvalidRequestException(String.format("Invalid deletion operation for non collection column %s", receiver.name));
@@ -424,4 +470,35 @@ public abstract class Operation
             throw new AssertionError();
         }
     }
+
+    public static class FieldDeletion implements RawDeletion
+    {
+        private final ColumnIdentifier.Raw id;
+        private final ColumnIdentifier.Raw field;
+
+        public FieldDeletion(ColumnIdentifier.Raw id, ColumnIdentifier.Raw field)
+        {
+            this.id = id;
+            this.field = field;
+        }
+
+        public ColumnIdentifier.Raw affectedColumn()
+        {
+            return id;
+        }
+
+        public Operation prepare(String keyspace, ColumnDefinition receiver, CFMetaData cfm) throws InvalidRequestException
+        {
+            if (!receiver.type.isUDT())
+                throw new InvalidRequestException(String.format("Invalid field deletion operation for non-UDT column %s", receiver.name));
+            else if (!receiver.type.isMultiCell())
+                throw new InvalidRequestException(String.format("Frozen UDT column %s does not support field deletions", receiver.name));
+
+            ColumnIdentifier fieldIdentifier = field.prepare(cfm);
+            if (((UserType) receiver.type).fieldPosition(fieldIdentifier) == -1)
+                throw new InvalidRequestException(String.format("UDT column %s does not have a field named %s", receiver.name, fieldIdentifier));
+
+            return new UserTypes.DeleterByField(receiver, fieldIdentifier);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/677230df/src/java/org/apache/cassandra/cql3/Tuples.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Tuples.java b/src/java/org/apache/cassandra/cql3/Tuples.java
index 6c7df47..ca4b6d9 100644
--- a/src/java/org/apache/cassandra/cql3/Tuples.java
+++ b/src/java/org/apache/cassandra/cql3/Tuples.java
@@ -111,8 +111,10 @@ public class Tuples
             for (int i = 0; i < elements.size(); i++)
             {
                 if (i >= tt.size())
+                {
                     throw new InvalidRequestException(String.format("Invalid tuple literal for %s: too many elements. Type %s expects %d but got %d",
-                                                                    receiver.name, tt.asCQL3Type(), tt.size(), elements.size()));
+                            receiver.name, tt.asCQL3Type(), tt.size(), elements.size()));
+                }
 
                 Term.Raw value = elements.get(i);
                 ColumnSpecification spec = componentSpecOf(receiver, i);
@@ -154,6 +156,13 @@ public class Tuples
 
         public static Value fromSerialized(ByteBuffer bytes, TupleType type)
         {
+            ByteBuffer[] values = type.split(bytes);
+            if (values.length > type.size())
+            {
+                throw new InvalidRequestException(String.format(
+                        "Tuple value contained too many fields (expected %s, got %s)", type.size(), values.length));
+            }
+
             return new Value(type.split(bytes));
         }
 
@@ -199,6 +208,10 @@ public class Tuples
 
         private ByteBuffer[] bindInternal(QueryOptions options) throws InvalidRequestException
         {
+            if (elements.size() > type.size())
+                throw new InvalidRequestException(String.format(
+                        "Tuple value contained too many fields (expected %s, got %s)", type.size(), elements.size()));
+
             ByteBuffer[] buffers = new ByteBuffer[elements.size()];
             for (int i = 0; i < elements.size(); i++)
             {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/677230df/src/java/org/apache/cassandra/cql3/UntypedResultSet.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/UntypedResultSet.java b/src/java/org/apache/cassandra/cql3/UntypedResultSet.java
index fb8d567..3d70051 100644
--- a/src/java/org/apache/cassandra/cql3/UntypedResultSet.java
+++ b/src/java/org/apache/cassandra/cql3/UntypedResultSet.java
@@ -245,7 +245,7 @@ public abstract class UntypedResultSet implements Iterable<UntypedResultSet.Row>
                 {
                     ComplexColumnData complexData = row.getComplexColumnData(def);
                     if (complexData != null)
-                        data.put(def.name.toString(), ((CollectionType)def.type).serializeForNativeProtocol(def, complexData.iterator(), Server.VERSION_3));
+                        data.put(def.name.toString(), ((CollectionType)def.type).serializeForNativeProtocol(complexData.iterator(), Server.VERSION_3));
                 }
             }
 


Mime
View raw message