mirror of https://github.com/apache/cassandra
Support for non-frozen UDTS
Patch by Tyler Hobbs; reviewed by Benjamin Lerer for CASSANDRA-7423
This commit is contained in:
parent
66fb8f51ed
commit
677230df69
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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', ';'])
|
||||
|
|
|
|||
|
|
@ -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))); }
|
||||
)
|
||||
)
|
||||
)
|
||||
;
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
{
|
||||
case LIST: return new Lists.Marker(bindIndex, receiver);
|
||||
case SET: return new Sets.Marker(bindIndex, receiver);
|
||||
case MAP: return new Maps.Marker(bindIndex, receiver);
|
||||
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();
|
||||
}
|
||||
}
|
||||
throw new AssertionError();
|
||||
else if (receiver.type.isUDT())
|
||||
{
|
||||
return new UserTypes.Marker(bindIndex, receiver);
|
||||
}
|
||||
|
||||
return new Constants.Marker(bindIndex, receiver);
|
||||
}
|
||||
|
||||
public AssignmentTestable.TestResult testAssignment(String keyspace, ColumnSpecification receiver)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
return isInCondition ? new CollectionInBound(this, options) : new CollectionBound(this, options);
|
||||
else
|
||||
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 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,71 +418,46 @@ 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());
|
||||
}
|
||||
}
|
||||
else // ListType
|
||||
{
|
||||
ListType listType = (ListType) column.type;
|
||||
valueType = listType.getElementsType();
|
||||
if (column.type.isMultiCell())
|
||||
{
|
||||
cellValue = cellValueAtIndex(getCells(row, column), ElementAccessBound.getListIndex(collectionElement));
|
||||
}
|
||||
else
|
||||
{
|
||||
Cell cell = getCell(row, column);
|
||||
cellValue = cell == null
|
||||
? null
|
||||
: listType.getSerializer().getElement(cell.value(), ElementAccessBound.getListIndex(collectionElement));
|
||||
}
|
||||
}
|
||||
|
||||
ListType listType = (ListType) column.type;
|
||||
AbstractType<?> elementsType = listType.getElementsType();
|
||||
if (column.type.isMultiCell())
|
||||
for (ByteBuffer value : inValues)
|
||||
{
|
||||
ByteBuffer columnValue = ElementAccessBound.getListItem(getCells(row, column), ElementAccessBound.getListIndex(collectionElement));
|
||||
|
||||
for (ByteBuffer value : inValues)
|
||||
{
|
||||
if (compareWithOperator(Operator.EQ, elementsType, value, columnValue))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Cell cell = getCell(row, column);
|
||||
ByteBuffer listElementValue = cell == null
|
||||
? null
|
||||
: listType.getSerializer().getElement(cell.value(), ElementAccessBound.getListIndex(collectionElement));
|
||||
|
||||
for (ByteBuffer value : inValues)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
if (listElementValue == null)
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
if (elementsType.compare(value, listElementValue) == 0)
|
||||
return true;
|
||||
}
|
||||
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,59 +891,142 @@ 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);
|
||||
}
|
||||
|
||||
public ColumnCondition prepare(String keyspace, ColumnDefinition receiver) throws InvalidRequestException
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/** 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, 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);
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
|
||||
int fieldPosition = userType.fieldPosition(fieldIdentifier);
|
||||
if (fieldPosition == -1)
|
||||
throw new InvalidRequestException(String.format("Unknown field %s for column %s", fieldIdentifier, receiver.name));
|
||||
|
||||
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
|
||||
{
|
||||
if (operator == Operator.IN)
|
||||
{
|
||||
|
|
@ -768,39 +1042,6 @@ public class ColumnCondition
|
|||
return ColumnCondition.condition(receiver, value.prepare(keyspace, receiver), operator);
|
||||
}
|
||||
}
|
||||
|
||||
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, 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);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ColumnCondition.condition(receiver, collectionElement.prepare(keyspace, elementSpec), value.prepare(keyspace, valueSpec), operator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,12 +20,18 @@ package org.apache.cassandra.cql3;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.cassandra.config.ColumnDefinition;
|
||||
import org.apache.cassandra.cql3.functions.Function;
|
||||
import org.apache.cassandra.db.DecoratedKey;
|
||||
import org.apache.cassandra.db.marshal.TupleType;
|
||||
import org.apache.cassandra.db.marshal.UTF8Type;
|
||||
import org.apache.cassandra.db.marshal.UserType;
|
||||
import org.apache.cassandra.db.rows.CellPath;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||
|
||||
import static org.apache.cassandra.cql3.Constants.UNSET_VALUE;
|
||||
|
||||
/**
|
||||
* Static helper methods and classes for user types.
|
||||
*/
|
||||
|
|
@ -78,8 +84,10 @@ public abstract class UserTypes
|
|||
{
|
||||
// We had some field that are not part of the type
|
||||
for (ColumnIdentifier id : entries.keySet())
|
||||
{
|
||||
if (!ut.fieldNames().contains(id.bytes))
|
||||
throw new InvalidRequestException(String.format("Unknown field '%s' in value of user defined type %s", id, ut.getNameAsString()));
|
||||
}
|
||||
}
|
||||
|
||||
DelayedValue value = new DelayedValue(((UserType)receiver.type), values);
|
||||
|
|
@ -88,7 +96,7 @@ public abstract class UserTypes
|
|||
|
||||
private void validateAssignableTo(String keyspace, ColumnSpecification receiver) throws InvalidRequestException
|
||||
{
|
||||
if (!(receiver.type instanceof UserType))
|
||||
if (!receiver.type.isUDT())
|
||||
throw new InvalidRequestException(String.format("Invalid user type literal for %s of type %s", receiver, receiver.type.asCQL3Type()));
|
||||
|
||||
UserType ut = (UserType)receiver.type;
|
||||
|
|
@ -101,7 +109,10 @@ public abstract class UserTypes
|
|||
|
||||
ColumnSpecification fieldSpec = fieldSpecOf(receiver, i);
|
||||
if (!value.testAssignment(keyspace, fieldSpec).isAssignable())
|
||||
throw new InvalidRequestException(String.format("Invalid user type literal for %s: field %s is not of type %s", receiver, field, fieldSpec.type.asCQL3Type()));
|
||||
{
|
||||
throw new InvalidRequestException(String.format("Invalid user type literal for %s: field %s is not of type %s",
|
||||
receiver, field, fieldSpec.type.asCQL3Type()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -135,7 +146,52 @@ public abstract class UserTypes
|
|||
}
|
||||
}
|
||||
|
||||
// Same purpose than Lists.DelayedValue, except we do handle bind marker in that case
|
||||
public static class Value extends Term.MultiItemTerminal
|
||||
{
|
||||
private final UserType type;
|
||||
public final ByteBuffer[] elements;
|
||||
|
||||
public Value(UserType type, ByteBuffer[] elements)
|
||||
{
|
||||
this.type = type;
|
||||
this.elements = elements;
|
||||
}
|
||||
|
||||
public static Value fromSerialized(ByteBuffer bytes, UserType type)
|
||||
{
|
||||
ByteBuffer[] values = type.split(bytes);
|
||||
if (values.length > type.size())
|
||||
{
|
||||
throw new InvalidRequestException(String.format(
|
||||
"UDT value contained too many fields (expected %s, got %s)", type.size(), values.length));
|
||||
}
|
||||
|
||||
return new Value(type, type.split(bytes));
|
||||
}
|
||||
|
||||
public ByteBuffer get(int protocolVersion)
|
||||
{
|
||||
return TupleType.buildValue(elements);
|
||||
}
|
||||
|
||||
public boolean equals(UserType userType, Value v)
|
||||
{
|
||||
if (elements.length != v.elements.length)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < elements.length; i++)
|
||||
if (userType.fieldType(i).compare(elements[i], v.elements[i]) != 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<ByteBuffer> getElements()
|
||||
{
|
||||
return Arrays.asList(elements);
|
||||
}
|
||||
}
|
||||
|
||||
public static class DelayedValue extends Term.NonTerminal
|
||||
{
|
||||
private final UserType type;
|
||||
|
|
@ -168,20 +224,27 @@ public abstract class UserTypes
|
|||
|
||||
private ByteBuffer[] bindInternal(QueryOptions options) throws InvalidRequestException
|
||||
{
|
||||
if (values.size() > type.size())
|
||||
{
|
||||
throw new InvalidRequestException(String.format(
|
||||
"UDT value contained too many fields (expected %s, got %s)", type.size(), values.size()));
|
||||
}
|
||||
|
||||
ByteBuffer[] buffers = new ByteBuffer[values.size()];
|
||||
for (int i = 0; i < type.size(); i++)
|
||||
{
|
||||
buffers[i] = values.get(i).bindAndGet(options);
|
||||
// Since A UDT value is always written in its entirety Cassandra can't preserve a pre-existing value by 'not setting' the new value. Reject the query.
|
||||
if (buffers[i] == ByteBufferUtil.UNSET_BYTE_BUFFER)
|
||||
// Since a frozen UDT value is always written in its entirety Cassandra can't preserve a pre-existing
|
||||
// value by 'not setting' the new value. Reject the query.
|
||||
if (!type.isMultiCell() && buffers[i] == ByteBufferUtil.UNSET_BYTE_BUFFER)
|
||||
throw new InvalidRequestException(String.format("Invalid unset value for field '%s' of user defined type %s", type.fieldNameAsString(i), type.getNameAsString()));
|
||||
}
|
||||
return buffers;
|
||||
}
|
||||
|
||||
public Constants.Value bind(QueryOptions options) throws InvalidRequestException
|
||||
public Value bind(QueryOptions options) throws InvalidRequestException
|
||||
{
|
||||
return new Constants.Value(bindAndGet(options));
|
||||
return new Value(type, bindInternal(options));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -190,4 +253,113 @@ public abstract class UserTypes
|
|||
return UserType.buildValue(bindInternal(options));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Marker extends AbstractMarker
|
||||
{
|
||||
protected Marker(int bindIndex, ColumnSpecification receiver)
|
||||
{
|
||||
super(bindIndex, receiver);
|
||||
assert receiver.type.isUDT();
|
||||
}
|
||||
|
||||
public Terminal bind(QueryOptions options) throws InvalidRequestException
|
||||
{
|
||||
ByteBuffer value = options.getValues().get(bindIndex);
|
||||
if (value == null)
|
||||
return null;
|
||||
if (value == ByteBufferUtil.UNSET_BYTE_BUFFER)
|
||||
return UNSET_VALUE;
|
||||
return Value.fromSerialized(value, (UserType) receiver.type);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Setter extends Operation
|
||||
{
|
||||
public Setter(ColumnDefinition column, Term t)
|
||||
{
|
||||
super(column, t);
|
||||
}
|
||||
|
||||
public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException
|
||||
{
|
||||
Term.Terminal value = t.bind(params.options);
|
||||
if (value == UNSET_VALUE)
|
||||
return;
|
||||
|
||||
Value userTypeValue = (Value) value;
|
||||
if (column.type.isMultiCell())
|
||||
{
|
||||
// setting a whole UDT at once means we overwrite all cells, so delete existing cells
|
||||
params.setComplexDeletionTimeForOverwrite(column);
|
||||
if (value == null)
|
||||
return;
|
||||
|
||||
Iterator<ByteBuffer> fieldNameIter = userTypeValue.type.fieldNames().iterator();
|
||||
for (ByteBuffer buffer : userTypeValue.elements)
|
||||
{
|
||||
ByteBuffer fieldName = fieldNameIter.next();
|
||||
if (buffer == null)
|
||||
continue;
|
||||
|
||||
CellPath fieldPath = userTypeValue.type.cellPathForField(fieldName);
|
||||
params.addCell(column, fieldPath, buffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// for frozen UDTs, we're overwriting the whole cell value
|
||||
if (value == null)
|
||||
params.addTombstone(column);
|
||||
else
|
||||
params.addCell(column, value.get(params.options.getProtocolVersion()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class SetterByField extends Operation
|
||||
{
|
||||
private final ColumnIdentifier field;
|
||||
|
||||
public SetterByField(ColumnDefinition column, ColumnIdentifier field, Term t)
|
||||
{
|
||||
super(column, t);
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException
|
||||
{
|
||||
// we should not get here for frozen UDTs
|
||||
assert column.type.isMultiCell() : "Attempted to set an individual field on a frozen UDT";
|
||||
|
||||
Term.Terminal value = t.bind(params.options);
|
||||
if (value == UNSET_VALUE)
|
||||
return;
|
||||
|
||||
CellPath fieldPath = ((UserType) column.type).cellPathForField(field.bytes);
|
||||
if (value == null)
|
||||
params.addTombstone(column, fieldPath);
|
||||
else
|
||||
params.addCell(column, fieldPath, value.get(params.options.getProtocolVersion()));
|
||||
}
|
||||
}
|
||||
|
||||
public static class DeleterByField extends Operation
|
||||
{
|
||||
private final ColumnIdentifier field;
|
||||
|
||||
public DeleterByField(ColumnDefinition column, ColumnIdentifier field)
|
||||
{
|
||||
super(column, null);
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
public void execute(DecoratedKey partitionKey, UpdateParameters params) throws InvalidRequestException
|
||||
{
|
||||
// we should not get here for frozen UDTs
|
||||
assert column.type.isMultiCell() : "Attempted to delete a single field from a frozen UDT";
|
||||
|
||||
CellPath fieldPath = ((UserType) column.type).cellPathForField(field.bytes);
|
||||
params.addTombstone(column, fieldPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ public abstract class AbstractFunction implements Function
|
|||
// We should ignore the fact that the receiver type is frozen in our comparison as functions do not support
|
||||
// frozen types for return type
|
||||
AbstractType<?> returnType = returnType();
|
||||
if (receiver.type.isFrozenCollection())
|
||||
if (receiver.type.isFreezable() && !receiver.type.isMultiCell())
|
||||
returnType = returnType.freeze();
|
||||
|
||||
if (receiver.type.equals(returnType))
|
||||
|
|
|
|||
|
|
@ -99,16 +99,24 @@ public class FunctionCall extends Term.NonTerminal
|
|||
|
||||
private static Term.Terminal makeTerminal(Function fun, ByteBuffer result, int version) throws InvalidRequestException
|
||||
{
|
||||
if (!(fun.returnType() instanceof CollectionType))
|
||||
return new Constants.Value(result);
|
||||
|
||||
switch (((CollectionType)fun.returnType()).kind)
|
||||
if (fun.returnType().isCollection())
|
||||
{
|
||||
case LIST: return Lists.Value.fromSerialized(result, (ListType)fun.returnType(), version);
|
||||
case SET: return Sets.Value.fromSerialized(result, (SetType)fun.returnType(), version);
|
||||
case MAP: return Maps.Value.fromSerialized(result, (MapType)fun.returnType(), version);
|
||||
switch (((CollectionType) fun.returnType()).kind)
|
||||
{
|
||||
case LIST:
|
||||
return Lists.Value.fromSerialized(result, (ListType) fun.returnType(), version);
|
||||
case SET:
|
||||
return Sets.Value.fromSerialized(result, (SetType) fun.returnType(), version);
|
||||
case MAP:
|
||||
return Maps.Value.fromSerialized(result, (MapType) fun.returnType(), version);
|
||||
}
|
||||
}
|
||||
throw new AssertionError();
|
||||
else if (fun.returnType().isUDT())
|
||||
{
|
||||
return UserTypes.Value.fromSerialized(result, (UserType) fun.returnType());
|
||||
}
|
||||
|
||||
return new Constants.Value(result);
|
||||
}
|
||||
|
||||
public static class Raw extends Term.Raw
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ public class UDAggregate extends AbstractFunction implements AggregateFunction
|
|||
&& Functions.typesMatch(returnType, that.returnType)
|
||||
&& Objects.equal(stateFunction, that.stateFunction)
|
||||
&& Objects.equal(finalFunction, that.finalFunction)
|
||||
&& Objects.equal(stateType, that.stateType)
|
||||
&& ((stateType == that.stateType) || ((stateType != null) && stateType.equals(that.stateType, true))) // ignore freezing
|
||||
&& Objects.equal(initcond, that.initcond);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ public final class StatementRestrictions
|
|||
WhereClause whereClause,
|
||||
VariableSpecifications boundNames,
|
||||
boolean selectsOnlyStaticColumns,
|
||||
boolean selectACollection,
|
||||
boolean selectsComplexColumn,
|
||||
boolean useFiltering,
|
||||
boolean forView) throws InvalidRequestException
|
||||
{
|
||||
|
|
@ -218,7 +218,7 @@ public final class StatementRestrictions
|
|||
throw invalidRequest("Cannot restrict clustering columns when selecting only static columns");
|
||||
}
|
||||
|
||||
processClusteringColumnsRestrictions(hasQueriableIndex, selectsOnlyStaticColumns, selectACollection, forView);
|
||||
processClusteringColumnsRestrictions(hasQueriableIndex, selectsOnlyStaticColumns, selectsComplexColumn, forView);
|
||||
|
||||
// Covers indexes on the first clustering column (among others).
|
||||
if (isKeyRange && hasQueriableClusteringColumnIndex)
|
||||
|
|
@ -447,11 +447,11 @@ public final class StatementRestrictions
|
|||
* @param hasQueriableIndex <code>true</code> if some of the queried data are indexed, <code>false</code> otherwise
|
||||
* @param selectsOnlyStaticColumns <code>true</code> if the selected or modified columns are all statics,
|
||||
* <code>false</code> otherwise.
|
||||
* @param selectACollection <code>true</code> if the query should return a collection column
|
||||
* @param selectsComplexColumn <code>true</code> if the query should return a collection column
|
||||
*/
|
||||
private void processClusteringColumnsRestrictions(boolean hasQueriableIndex,
|
||||
boolean selectsOnlyStaticColumns,
|
||||
boolean selectACollection,
|
||||
boolean selectsComplexColumn,
|
||||
boolean forView) throws InvalidRequestException
|
||||
{
|
||||
checkFalse(!type.allowClusteringColumnSlices() && clusteringColumnsRestrictions.isSlice(),
|
||||
|
|
@ -466,7 +466,7 @@ public final class StatementRestrictions
|
|||
}
|
||||
else
|
||||
{
|
||||
checkFalse(clusteringColumnsRestrictions.isIN() && selectACollection,
|
||||
checkFalse(clusteringColumnsRestrictions.isIN() && selectsComplexColumn,
|
||||
"Cannot restrict clustering columns by IN relations when a collection is selected by the query");
|
||||
checkFalse(clusteringColumnsRestrictions.isContains() && !hasQueriableIndex,
|
||||
"Cannot restrict clustering columns by a CONTAINS relation without a secondary index");
|
||||
|
|
|
|||
|
|
@ -277,23 +277,23 @@ public abstract class Selectable
|
|||
{
|
||||
Selector.Factory factory = selected.newSelectorFactory(cfm, defs);
|
||||
AbstractType<?> type = factory.newInstance().getType();
|
||||
if (!(type instanceof UserType))
|
||||
if (!type.isUDT())
|
||||
{
|
||||
throw new InvalidRequestException(
|
||||
String.format("Invalid field selection: %s of type %s is not a user type",
|
||||
selected,
|
||||
type.asCQL3Type()));
|
||||
selected,
|
||||
type.asCQL3Type()));
|
||||
}
|
||||
|
||||
UserType ut = (UserType) type;
|
||||
for (int i = 0; i < ut.size(); i++)
|
||||
int fieldIndex = ((UserType) type).fieldPosition(field);
|
||||
if (fieldIndex == -1)
|
||||
{
|
||||
if (!ut.fieldName(i).equals(field.bytes))
|
||||
continue;
|
||||
return FieldSelector.newFactory(ut, i, factory);
|
||||
throw new InvalidRequestException(String.format("%s of type %s has no field %s",
|
||||
selected, type.asCQL3Type(), field));
|
||||
}
|
||||
throw new InvalidRequestException(String.format("%s of type %s has no field %s",
|
||||
selected,
|
||||
type.asCQL3Type(),
|
||||
field));
|
||||
|
||||
return FieldSelector.newFactory(ut, fieldIndex, factory);
|
||||
}
|
||||
|
||||
public static class Raw implements Selectable.Raw
|
||||
|
|
|
|||
|
|
@ -112,14 +112,14 @@ public abstract class Selection
|
|||
}
|
||||
|
||||
/**
|
||||
* Checks if this selection contains a collection.
|
||||
* Checks if this selection contains a complex column.
|
||||
*
|
||||
* @return <code>true</code> if this selection contains a collection, <code>false</code> otherwise.
|
||||
* @return <code>true</code> if this selection contains a multicell collection or UDT, <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean containsACollection()
|
||||
public boolean containsAComplexColumn()
|
||||
{
|
||||
for (ColumnDefinition def : getColumns())
|
||||
if (def.type.isCollection() && def.type.isMultiCell())
|
||||
if (def.isComplex())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ public abstract class Selector implements AssignmentTestable
|
|||
// We should ignore the fact that the output type is frozen in our comparison as functions do not support
|
||||
// frozen types for arguments
|
||||
AbstractType<?> receiverType = receiver.type;
|
||||
if (getType().isFrozenCollection())
|
||||
if (getType().isFreezable() && !getType().isMultiCell())
|
||||
receiverType = receiverType.freeze();
|
||||
|
||||
if (getType().isReversed())
|
||||
|
|
|
|||
|
|
@ -166,11 +166,11 @@ public abstract class AlterTypeStatement extends SchemaAlteringStatement
|
|||
|
||||
// If it's directly the type we've updated, then just use the new one.
|
||||
if (keyspace.equals(ut.keyspace) && toReplace.equals(ut.name))
|
||||
return updated;
|
||||
return type.isMultiCell() ? updated : updated.freeze();
|
||||
|
||||
// Otherwise, check for nesting
|
||||
List<AbstractType<?>> updatedTypes = updateTypes(ut.fieldTypes(), keyspace, toReplace, updated);
|
||||
return updatedTypes == null ? null : new UserType(ut.keyspace, ut.name, new ArrayList<>(ut.fieldNames()), updatedTypes);
|
||||
return updatedTypes == null ? null : new UserType(ut.keyspace, ut.name, new ArrayList<>(ut.fieldNames()), updatedTypes, type.isMultiCell());
|
||||
}
|
||||
else if (type instanceof TupleType)
|
||||
{
|
||||
|
|
@ -275,7 +275,7 @@ public abstract class AlterTypeStatement extends SchemaAlteringStatement
|
|||
newTypes.addAll(toUpdate.fieldTypes());
|
||||
newTypes.add(addType);
|
||||
|
||||
return new UserType(toUpdate.keyspace, toUpdate.name, newNames, newTypes);
|
||||
return new UserType(toUpdate.keyspace, toUpdate.name, newNames, newTypes, toUpdate.isMultiCell());
|
||||
}
|
||||
|
||||
private UserType doAlter(UserType toUpdate, KeyspaceMetadata ksm) throws InvalidRequestException
|
||||
|
|
@ -294,7 +294,7 @@ public abstract class AlterTypeStatement extends SchemaAlteringStatement
|
|||
List<AbstractType<?>> newTypes = new ArrayList<>(toUpdate.fieldTypes());
|
||||
newTypes.set(idx, type.prepare(keyspace()).getType());
|
||||
|
||||
return new UserType(toUpdate.keyspace, toUpdate.name, newNames, newTypes);
|
||||
return new UserType(toUpdate.keyspace, toUpdate.name, newNames, newTypes, toUpdate.isMultiCell());
|
||||
}
|
||||
|
||||
protected UserType makeUpdatedType(UserType toUpdate, KeyspaceMetadata ksm) throws InvalidRequestException
|
||||
|
|
@ -330,7 +330,7 @@ public abstract class AlterTypeStatement extends SchemaAlteringStatement
|
|||
newNames.set(idx, to.bytes);
|
||||
}
|
||||
|
||||
UserType updated = new UserType(toUpdate.keyspace, toUpdate.name, newNames, newTypes);
|
||||
UserType updated = new UserType(toUpdate.keyspace, toUpdate.name, newNames, newTypes, toUpdate.isMultiCell());
|
||||
CreateTypeStatement.checkForDuplicateNames(updated);
|
||||
return updated;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ public class CreateTableStatement extends SchemaAlteringStatement
|
|||
private List<AbstractType<?>> keyTypes;
|
||||
private List<AbstractType<?>> clusteringTypes;
|
||||
|
||||
private final Map<ByteBuffer, CollectionType> collections = new HashMap<>();
|
||||
private final Map<ByteBuffer, AbstractType> multicellColumns = new HashMap<>();
|
||||
|
||||
private final List<ColumnIdentifier> keyAliases = new ArrayList<>();
|
||||
private final List<ColumnIdentifier> columnAliases = new ArrayList<>();
|
||||
|
|
@ -223,10 +223,24 @@ public class CreateTableStatement extends SchemaAlteringStatement
|
|||
{
|
||||
ColumnIdentifier id = entry.getKey();
|
||||
CQL3Type pt = entry.getValue().prepare(keyspace(), udts);
|
||||
if (pt.isCollection() && ((CollectionType)pt.getType()).isMultiCell())
|
||||
stmt.collections.put(id.bytes, (CollectionType)pt.getType());
|
||||
if (pt.getType().isMultiCell())
|
||||
stmt.multicellColumns.put(id.bytes, pt.getType());
|
||||
if (entry.getValue().isCounter())
|
||||
stmt.hasCounters = true;
|
||||
|
||||
// check for non-frozen UDTs or collections in a non-frozen UDT
|
||||
if (pt.getType().isUDT() && pt.getType().isMultiCell())
|
||||
{
|
||||
for (AbstractType<?> innerType : ((UserType) pt.getType()).fieldTypes())
|
||||
{
|
||||
if (innerType.isMultiCell())
|
||||
{
|
||||
assert innerType.isCollection(); // shouldn't get this far with a nested non-frozen UDT
|
||||
throw new InvalidRequestException("Non-frozen UDTs with nested non-frozen collections are not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stmt.columns.put(id, pt.getType()); // we'll remove what is not a column below
|
||||
}
|
||||
|
||||
|
|
@ -285,8 +299,8 @@ public class CreateTableStatement extends SchemaAlteringStatement
|
|||
// For COMPACT STORAGE, we reject any "feature" that we wouldn't be able to translate back to thrift.
|
||||
if (useCompactStorage)
|
||||
{
|
||||
if (!stmt.collections.isEmpty())
|
||||
throw new InvalidRequestException("Non-frozen collection types are not supported with COMPACT STORAGE");
|
||||
if (!stmt.multicellColumns.isEmpty())
|
||||
throw new InvalidRequestException("Non-frozen collections and UDTs are not supported with COMPACT STORAGE");
|
||||
if (!staticColumns.isEmpty())
|
||||
throw new InvalidRequestException("Static columns are not supported in COMPACT STORAGE tables");
|
||||
|
||||
|
|
@ -350,8 +364,13 @@ public class CreateTableStatement extends SchemaAlteringStatement
|
|||
AbstractType type = columns.get(t);
|
||||
if (type == null)
|
||||
throw new InvalidRequestException(String.format("Unknown definition %s referenced in PRIMARY KEY", t));
|
||||
if (type.isCollection() && type.isMultiCell())
|
||||
throw new InvalidRequestException(String.format("Invalid collection type for PRIMARY KEY component %s", t));
|
||||
if (type.isMultiCell())
|
||||
{
|
||||
if (type.isCollection())
|
||||
throw new InvalidRequestException(String.format("Invalid non-frozen collection type for PRIMARY KEY component %s", t));
|
||||
else
|
||||
throw new InvalidRequestException(String.format("Invalid non-frozen user-defined type for PRIMARY KEY component %s", t));
|
||||
}
|
||||
|
||||
columns.remove(t);
|
||||
Boolean isReversed = properties.definedOrdering.get(t);
|
||||
|
|
|
|||
|
|
@ -74,8 +74,12 @@ public class CreateTypeStatement extends SchemaAlteringStatement
|
|||
throw new InvalidRequestException(String.format("A user type of name %s already exists", name));
|
||||
|
||||
for (CQL3Type.Raw type : columnTypes)
|
||||
{
|
||||
if (type.isCounter())
|
||||
throw new InvalidRequestException("A user type cannot contain counters");
|
||||
if (type.isUDT() && !type.isFrozen())
|
||||
throw new InvalidRequestException("A user type cannot contain non-frozen UDTs");
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkForDuplicateNames(UserType type) throws InvalidRequestException
|
||||
|
|
@ -109,7 +113,7 @@ public class CreateTypeStatement extends SchemaAlteringStatement
|
|||
for (CQL3Type.Raw type : columnTypes)
|
||||
types.add(type.prepare(keyspace()).getType());
|
||||
|
||||
return new UserType(name.getKeyspace(), name.getUserTypeName(), names, types);
|
||||
return new UserType(name.getKeyspace(), name.getUserTypeName(), names, types, true);
|
||||
}
|
||||
|
||||
public Event.SchemaChange announceMigration(boolean isLocalOnly) throws InvalidRequestException, ConfigurationException
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ public class DeleteStatement extends ModificationStatement
|
|||
// list. However, we support having the value name for coherence with the static/sparse case
|
||||
checkFalse(def.isPrimaryKeyColumn(), "Invalid identifier %s for deletion (should not be a PRIMARY KEY part)", def.name);
|
||||
|
||||
Operation op = deletion.prepare(cfm.ksName, def);
|
||||
Operation op = deletion.prepare(cfm.ksName, def, cfm);
|
||||
op.collectMarkerSpecification(boundNames);
|
||||
operations.add(op);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -831,7 +831,7 @@ public abstract class ModificationStatement implements CQLStatement
|
|||
ColumnDefinition def = metadata.getColumnDefinition(id);
|
||||
checkNotNull(metadata.getColumnDefinition(id), "Unknown identifier %s in IF conditions", id);
|
||||
|
||||
ColumnCondition condition = entry.right.prepare(keyspace(), def);
|
||||
ColumnCondition condition = entry.right.prepare(keyspace(), def, metadata);
|
||||
condition.collectMarkerSpecification(boundNames);
|
||||
|
||||
checkFalse(def.isPrimaryKeyColumn(), "PRIMARY KEY column '%s' cannot have IF conditions", id);
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ import org.apache.cassandra.db.filter.*;
|
|||
import org.apache.cassandra.db.marshal.CollectionType;
|
||||
import org.apache.cassandra.db.marshal.CompositeType;
|
||||
import org.apache.cassandra.db.marshal.Int32Type;
|
||||
import org.apache.cassandra.db.marshal.UserType;
|
||||
import org.apache.cassandra.db.partitions.PartitionIterator;
|
||||
import org.apache.cassandra.db.rows.ComplexColumnData;
|
||||
import org.apache.cassandra.db.rows.Row;
|
||||
|
|
@ -759,13 +760,14 @@ public class SelectStatement implements CQLStatement
|
|||
{
|
||||
if (def.isComplex())
|
||||
{
|
||||
// Collections are the only complex types we have so far
|
||||
assert def.type.isCollection() && def.type.isMultiCell();
|
||||
assert def.type.isMultiCell();
|
||||
ComplexColumnData complexData = row.getComplexColumnData(def);
|
||||
if (complexData == null)
|
||||
result.add((ByteBuffer)null);
|
||||
result.add(null);
|
||||
else if (def.type.isCollection())
|
||||
result.add(((CollectionType) def.type).serializeForNativeProtocol(complexData.iterator(), protocolVersion));
|
||||
else
|
||||
result.add(((CollectionType)def.type).serializeForNativeProtocol(def, complexData.iterator(), protocolVersion));
|
||||
result.add(((UserType) def.type).serializeForNativeProtocol(complexData.iterator(), protocolVersion));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -883,7 +885,7 @@ public class SelectStatement implements CQLStatement
|
|||
whereClause,
|
||||
boundNames,
|
||||
selection.containsOnlyStaticColumns(),
|
||||
selection.containsACollection(),
|
||||
selection.containsAComplexColumn(),
|
||||
parameters.allowFiltering,
|
||||
forView);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ public class UpdateStatement extends ModificationStatement
|
|||
}
|
||||
else
|
||||
{
|
||||
Operation operation = new Operation.SetValue(value).prepare(keyspace(), def);
|
||||
Operation operation = new Operation.SetValue(value).prepare(cfm, def);
|
||||
operation.collectMarkerSpecification(boundNames);
|
||||
operations.add(operation);
|
||||
}
|
||||
|
|
@ -239,7 +239,7 @@ public class UpdateStatement extends ModificationStatement
|
|||
}
|
||||
else
|
||||
{
|
||||
Operation operation = new Operation.SetValue(raw).prepare(keyspace(), def);
|
||||
Operation operation = new Operation.SetValue(raw).prepare(cfm, def);
|
||||
operation.collectMarkerSpecification(boundNames);
|
||||
operations.add(operation);
|
||||
}
|
||||
|
|
@ -308,7 +308,7 @@ public class UpdateStatement extends ModificationStatement
|
|||
|
||||
checkFalse(def.isPrimaryKeyColumn(), "PRIMARY KEY part %s found in SET part", def.name);
|
||||
|
||||
Operation operation = entry.right.prepare(keyspace(), def);
|
||||
Operation operation = entry.right.prepare(cfm, def);
|
||||
operation.collectMarkerSpecification(boundNames);
|
||||
operations.add(operation);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -312,11 +312,21 @@ public abstract class AbstractType<T> implements Comparator<ByteBuffer>
|
|||
return false;
|
||||
}
|
||||
|
||||
public boolean isUDT()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isMultiCell()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isFreezable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public AbstractType<?> freeze()
|
||||
{
|
||||
return this;
|
||||
|
|
@ -418,6 +428,17 @@ public abstract class AbstractType<T> implements Comparator<ByteBuffer>
|
|||
return getClass().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if two types are equal when ignoring or not ignoring differences in being frozen, depending on
|
||||
* the value of the ignoreFreezing parameter.
|
||||
* @param other type to compare
|
||||
* @param ignoreFreezing if true, differences in the types being frozen will be ignored
|
||||
*/
|
||||
public boolean equals(Object other, boolean ignoreFreezing)
|
||||
{
|
||||
return this.equals(other);
|
||||
}
|
||||
|
||||
public void checkComparable()
|
||||
{
|
||||
switch (comparisonType)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import java.io.IOException;
|
|||
import java.util.List;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.apache.cassandra.config.ColumnDefinition;
|
||||
import org.apache.cassandra.cql3.CQL3Type;
|
||||
import org.apache.cassandra.cql3.ColumnSpecification;
|
||||
import org.apache.cassandra.cql3.Lists;
|
||||
|
|
@ -133,13 +132,19 @@ public abstract class CollectionType<T> extends AbstractType<T>
|
|||
return kind == Kind.MAP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFreezable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Overrided by maps
|
||||
protected int collectionSize(List<ByteBuffer> values)
|
||||
{
|
||||
return values.size();
|
||||
}
|
||||
|
||||
public ByteBuffer serializeForNativeProtocol(ColumnDefinition def, Iterator<Cell> cells, int version)
|
||||
public ByteBuffer serializeForNativeProtocol(Iterator<Cell> cells, int version)
|
||||
{
|
||||
assert isMultiCell();
|
||||
List<ByteBuffer> values = serializedValues(cells);
|
||||
|
|
@ -203,6 +208,27 @@ public abstract class CollectionType<T> extends AbstractType<T>
|
|||
return new CQL3Type.Collection(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o, boolean ignoreFreezing)
|
||||
{
|
||||
if (this == o)
|
||||
return true;
|
||||
|
||||
if (!(o instanceof CollectionType))
|
||||
return false;
|
||||
|
||||
CollectionType other = (CollectionType)o;
|
||||
|
||||
if (kind != other.kind)
|
||||
return false;
|
||||
|
||||
if (!ignoreFreezing && isMultiCell() != other.isMultiCell())
|
||||
return false;
|
||||
|
||||
return nameComparator().equals(other.nameComparator(), ignoreFreezing) &&
|
||||
valueComparator().equals(other.valueComparator(), ignoreFreezing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,11 +23,13 @@ import java.util.Arrays;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import org.apache.cassandra.cql3.*;
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.exceptions.SyntaxException;
|
||||
import org.apache.cassandra.serializers.*;
|
||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||
|
|
@ -50,11 +52,17 @@ public class TupleType extends AbstractType<ByteBuffer>
|
|||
protected final List<AbstractType<?>> types;
|
||||
|
||||
public TupleType(List<AbstractType<?>> types)
|
||||
{
|
||||
this(types, true);
|
||||
}
|
||||
|
||||
protected TupleType(List<AbstractType<?>> types, boolean freezeInner)
|
||||
{
|
||||
super(ComparisonType.CUSTOM);
|
||||
for (int i = 0; i < types.size(); i++)
|
||||
types.set(i, types.get(i).freeze());
|
||||
this.types = types;
|
||||
if (freezeInner)
|
||||
this.types = types.stream().map(AbstractType::freeze).collect(Collectors.toList());
|
||||
else
|
||||
this.types = types;
|
||||
}
|
||||
|
||||
public static TupleType getInstance(TypeParser parser) throws ConfigurationException, SyntaxException
|
||||
|
|
@ -118,11 +126,22 @@ public class TupleType extends AbstractType<ByteBuffer>
|
|||
return cmp;
|
||||
}
|
||||
|
||||
if (bb1.remaining() == 0)
|
||||
return bb2.remaining() == 0 ? 0 : -1;
|
||||
// handle trailing nulls
|
||||
while (bb1.remaining() > 0)
|
||||
{
|
||||
int size = bb1.getInt();
|
||||
if (size > 0) // non-null
|
||||
return 1;
|
||||
}
|
||||
|
||||
// bb1.remaining() > 0 && bb2.remaining() == 0
|
||||
return 1;
|
||||
while (bb2.remaining() > 0)
|
||||
{
|
||||
int size = bb2.getInt();
|
||||
if (size > 0) // non-null
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -171,6 +190,15 @@ public class TupleType extends AbstractType<ByteBuffer>
|
|||
int size = input.getInt();
|
||||
components[i] = size < 0 ? null : ByteBufferUtil.readBytes(input, size);
|
||||
}
|
||||
|
||||
// error out if we got more values in the tuple/UDT than we expected
|
||||
if (input.hasRemaining())
|
||||
{
|
||||
throw new InvalidRequestException(String.format(
|
||||
"Expected %s %s for %s column, but got more",
|
||||
size(), size() == 1 ? "value" : "values", this.asCQL3Type()));
|
||||
}
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
|
|
@ -200,6 +228,9 @@ public class TupleType extends AbstractType<ByteBuffer>
|
|||
@Override
|
||||
public String getString(ByteBuffer value)
|
||||
{
|
||||
if (value == null)
|
||||
return "null";
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
ByteBuffer input = value.duplicate();
|
||||
for (int i = 0; i < size(); i++)
|
||||
|
|
|
|||
|
|
@ -538,7 +538,8 @@ public class TypeParser
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String stringifyUserTypeParameters(String keysace, ByteBuffer typeName, List<ByteBuffer> columnNames, List<AbstractType<?>> columnTypes)
|
||||
public static String stringifyUserTypeParameters(String keysace, ByteBuffer typeName, List<ByteBuffer> columnNames,
|
||||
List<AbstractType<?>> columnTypes, boolean ignoreFreezing)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('(').append(keysace).append(",").append(ByteBufferUtil.bytesToHex(typeName));
|
||||
|
|
@ -547,8 +548,7 @@ public class TypeParser
|
|||
{
|
||||
sb.append(',');
|
||||
sb.append(ByteBufferUtil.bytesToHex(columnNames.get(i))).append(":");
|
||||
// omit FrozenType(...) from fields because it is currently implicit
|
||||
sb.append(columnTypes.get(i).toString(true));
|
||||
sb.append(columnTypes.get(i).toString(ignoreFreezing));
|
||||
}
|
||||
sb.append(')');
|
||||
return sb.toString();
|
||||
|
|
|
|||
|
|
@ -25,11 +25,15 @@ import java.util.*;
|
|||
import com.google.common.base.Objects;
|
||||
|
||||
import org.apache.cassandra.cql3.*;
|
||||
import org.apache.cassandra.db.rows.Cell;
|
||||
import org.apache.cassandra.db.rows.CellPath;
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
import org.apache.cassandra.exceptions.SyntaxException;
|
||||
import org.apache.cassandra.serializers.*;
|
||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||
import org.apache.cassandra.utils.Pair;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A user defined type.
|
||||
|
|
@ -38,19 +42,24 @@ import org.apache.cassandra.utils.Pair;
|
|||
*/
|
||||
public class UserType extends TupleType
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(UserType.class);
|
||||
|
||||
public final String keyspace;
|
||||
public final ByteBuffer name;
|
||||
private final List<ByteBuffer> fieldNames;
|
||||
private final List<String> stringFieldNames;
|
||||
private final boolean isMultiCell;
|
||||
|
||||
public UserType(String keyspace, ByteBuffer name, List<ByteBuffer> fieldNames, List<AbstractType<?>> fieldTypes)
|
||||
public UserType(String keyspace, ByteBuffer name, List<ByteBuffer> fieldNames, List<AbstractType<?>> fieldTypes, boolean isMultiCell)
|
||||
{
|
||||
super(fieldTypes);
|
||||
super(fieldTypes, false);
|
||||
assert fieldNames.size() == fieldTypes.size();
|
||||
this.keyspace = keyspace;
|
||||
this.name = name;
|
||||
this.fieldNames = fieldNames;
|
||||
this.stringFieldNames = new ArrayList<>(fieldNames.size());
|
||||
this.isMultiCell = isMultiCell;
|
||||
|
||||
for (ByteBuffer fieldName : fieldNames)
|
||||
{
|
||||
try
|
||||
|
|
@ -74,9 +83,28 @@ public class UserType extends TupleType
|
|||
for (Pair<ByteBuffer, AbstractType> p : params.right)
|
||||
{
|
||||
columnNames.add(p.left);
|
||||
columnTypes.add(p.right.freeze());
|
||||
columnTypes.add(p.right);
|
||||
}
|
||||
return new UserType(keyspace, name, columnNames, columnTypes);
|
||||
|
||||
return new UserType(keyspace, name, columnNames, columnTypes, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUDT()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMultiCell()
|
||||
{
|
||||
return isMultiCell;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFreezable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public AbstractType<?> fieldType(int i)
|
||||
|
|
@ -109,6 +137,55 @@ public class UserType extends TupleType
|
|||
return UTF8Type.instance.compose(name);
|
||||
}
|
||||
|
||||
public short fieldPosition(ColumnIdentifier field)
|
||||
{
|
||||
return fieldPosition(field.bytes);
|
||||
}
|
||||
|
||||
public short fieldPosition(ByteBuffer fieldName)
|
||||
{
|
||||
for (short i = 0; i < fieldNames.size(); i++)
|
||||
if (fieldName.equals(fieldNames.get(i)))
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
public CellPath cellPathForField(ByteBuffer fieldName)
|
||||
{
|
||||
// we use the field position instead of the field name to allow for field renaming in ALTER TYPE statements
|
||||
return CellPath.create(ByteBufferUtil.bytes(fieldPosition(fieldName)));
|
||||
}
|
||||
|
||||
public ShortType nameComparator()
|
||||
{
|
||||
return ShortType.instance;
|
||||
}
|
||||
|
||||
public ByteBuffer serializeForNativeProtocol(Iterator<Cell> cells, int protocolVersion)
|
||||
{
|
||||
assert isMultiCell;
|
||||
|
||||
ByteBuffer[] components = new ByteBuffer[size()];
|
||||
short fieldPosition = 0;
|
||||
while (cells.hasNext())
|
||||
{
|
||||
Cell cell = cells.next();
|
||||
|
||||
// handle null fields that aren't at the end
|
||||
short fieldPositionOfCell = ByteBufferUtil.toShort(cell.path().get(0));
|
||||
while (fieldPosition < fieldPositionOfCell)
|
||||
components[fieldPosition++] = null;
|
||||
|
||||
components[fieldPosition++] = cell.value();
|
||||
}
|
||||
|
||||
// append trailing nulls for missing cells
|
||||
while (fieldPosition < size())
|
||||
components[fieldPosition++] = null;
|
||||
|
||||
return TupleType.buildValue(components);
|
||||
}
|
||||
|
||||
// Note: the only reason we override this is to provide nicer error message, but since that's not that much code...
|
||||
@Override
|
||||
public void validate(ByteBuffer bytes) throws MarshalException
|
||||
|
|
@ -216,20 +293,80 @@ public class UserType extends TupleType
|
|||
return sb.append("}").toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserType freeze()
|
||||
{
|
||||
if (isMultiCell)
|
||||
return new UserType(keyspace, name, fieldNames, fieldTypes(), false);
|
||||
else
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(keyspace, name, fieldNames, types);
|
||||
return Objects.hashCode(keyspace, name, fieldNames, types, isMultiCell);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValueCompatibleWith(AbstractType<?> previous)
|
||||
{
|
||||
if (this == previous)
|
||||
return true;
|
||||
|
||||
if (!(previous instanceof UserType))
|
||||
return false;
|
||||
|
||||
UserType other = (UserType) previous;
|
||||
if (isMultiCell != other.isMultiCell())
|
||||
return false;
|
||||
|
||||
if (!keyspace.equals(other.keyspace))
|
||||
return false;
|
||||
|
||||
Iterator<AbstractType<?>> thisTypeIter = types.iterator();
|
||||
Iterator<AbstractType<?>> previousTypeIter = other.types.iterator();
|
||||
while (thisTypeIter.hasNext() && previousTypeIter.hasNext())
|
||||
{
|
||||
if (!thisTypeIter.next().isCompatibleWith(previousTypeIter.next()))
|
||||
return false;
|
||||
}
|
||||
|
||||
// it's okay for the new type to have additional fields, but not for the old type to have additional fields
|
||||
return !previousTypeIter.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
return o instanceof UserType && equals(o, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o, boolean ignoreFreezing)
|
||||
{
|
||||
if(!(o instanceof UserType))
|
||||
return false;
|
||||
|
||||
UserType that = (UserType)o;
|
||||
return keyspace.equals(that.keyspace) && name.equals(that.name) && fieldNames.equals(that.fieldNames) && types.equals(that.types);
|
||||
|
||||
if (!keyspace.equals(that.keyspace) || !name.equals(that.name) || !fieldNames.equals(that.fieldNames))
|
||||
return false;
|
||||
|
||||
if (!ignoreFreezing && isMultiCell != that.isMultiCell)
|
||||
return false;
|
||||
|
||||
if (this.types.size() != that.types.size())
|
||||
return false;
|
||||
|
||||
Iterator<AbstractType<?>> otherTypeIter = that.types.iterator();
|
||||
for (AbstractType<?> type : types)
|
||||
{
|
||||
if (!type.equals(otherTypeIter.next(), ignoreFreezing))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -248,6 +385,21 @@ public class UserType extends TupleType
|
|||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return getClass().getName() + TypeParser.stringifyUserTypeParameters(keyspace, name, fieldNames, types);
|
||||
return this.toString(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(boolean ignoreFreezing)
|
||||
{
|
||||
boolean includeFrozenType = !ignoreFreezing && !isMultiCell();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (includeFrozenType)
|
||||
sb.append(FrozenType.class.getName()).append("(");
|
||||
sb.append(getClass().getName());
|
||||
sb.append(TypeParser.stringifyUserTypeParameters(keyspace, name, fieldNames, types, ignoreFreezing || !isMultiCell));
|
||||
if (includeFrozenType)
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import java.nio.ByteBuffer;
|
|||
import java.security.MessageDigest;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.cassandra.db.marshal.UserType;
|
||||
import org.apache.cassandra.io.util.DataOutputPlus;
|
||||
import org.apache.cassandra.io.util.DataInputPlus;
|
||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||
|
|
@ -39,11 +40,11 @@ public abstract class CellPath
|
|||
public abstract int size();
|
||||
public abstract ByteBuffer get(int i);
|
||||
|
||||
// The only complex we currently have are collections that have only one value.
|
||||
// The only complex paths we currently have are collections and UDTs, which both have a depth of one
|
||||
public static CellPath create(ByteBuffer value)
|
||||
{
|
||||
assert value != null;
|
||||
return new CollectionCellPath(value);
|
||||
return new SingleItemCellPath(value);
|
||||
}
|
||||
|
||||
public int dataSize()
|
||||
|
|
@ -98,13 +99,13 @@ public abstract class CellPath
|
|||
public void skip(DataInputPlus in) throws IOException;
|
||||
}
|
||||
|
||||
private static class CollectionCellPath extends CellPath
|
||||
private static class SingleItemCellPath extends CellPath
|
||||
{
|
||||
private static final long EMPTY_SIZE = ObjectSizes.measure(new CollectionCellPath(ByteBufferUtil.EMPTY_BYTE_BUFFER));
|
||||
private static final long EMPTY_SIZE = ObjectSizes.measure(new SingleItemCellPath(ByteBufferUtil.EMPTY_BYTE_BUFFER));
|
||||
|
||||
protected final ByteBuffer value;
|
||||
|
||||
private CollectionCellPath(ByteBuffer value)
|
||||
private SingleItemCellPath(ByteBuffer value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
|
@ -122,7 +123,7 @@ public abstract class CellPath
|
|||
|
||||
public CellPath copy(AbstractAllocator allocator)
|
||||
{
|
||||
return new CollectionCellPath(allocator.clone(value));
|
||||
return new SingleItemCellPath(allocator.clone(value));
|
||||
}
|
||||
|
||||
public long unsharedHeapSizeExcludingData()
|
||||
|
|
|
|||
|
|
@ -251,7 +251,6 @@ public class ComplexColumnData extends ColumnData implements Iterable<Cell>
|
|||
|
||||
public void addCell(Cell cell)
|
||||
{
|
||||
assert cell.column().equals(column);
|
||||
builder.add(cell);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ public final class Functions implements Iterable<Function>
|
|||
*/
|
||||
public static boolean typesMatch(AbstractType<?> t1, AbstractType<?> t2)
|
||||
{
|
||||
return t1.asCQL3Type().toString().equals(t2.asCQL3Type().toString());
|
||||
return t1.freeze().asCQL3Type().toString().equals(t2.freeze().asCQL3Type().toString());
|
||||
}
|
||||
|
||||
public static boolean typesMatch(List<AbstractType<?>> t1, List<AbstractType<?>> t2)
|
||||
|
|
|
|||
|
|
@ -827,7 +827,7 @@ public final class LegacySchemaMigrator
|
|||
.map(LegacySchemaMigrator::parseType)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new UserType(keyspaceName, bytes(typeName), names, types);
|
||||
return new UserType(keyspaceName, bytes(typeName), names, types, true);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -22,11 +22,7 @@ import java.util.*;
|
|||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.MapDifference;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.*;
|
||||
|
||||
import org.apache.cassandra.cql3.CQL3Type;
|
||||
import org.apache.cassandra.db.marshal.AbstractType;
|
||||
|
|
@ -139,7 +135,30 @@ public final class Types implements Iterable<UserType>
|
|||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
return this == o || (o instanceof Types && types.equals(((Types) o).types));
|
||||
if (this == o)
|
||||
return true;
|
||||
|
||||
if (!(o instanceof Types))
|
||||
return false;
|
||||
|
||||
Types other = (Types) o;
|
||||
|
||||
if (types.size() != other.types.size())
|
||||
return false;
|
||||
|
||||
Iterator<Map.Entry<ByteBuffer, UserType>> thisIter = this.types.entrySet().iterator();
|
||||
Iterator<Map.Entry<ByteBuffer, UserType>> otherIter = other.types.entrySet().iterator();
|
||||
while (thisIter.hasNext())
|
||||
{
|
||||
Map.Entry<ByteBuffer, UserType> thisNext = thisIter.next();
|
||||
Map.Entry<ByteBuffer, UserType> otherNext = otherIter.next();
|
||||
if (!thisNext.getKey().equals(otherNext.getKey()))
|
||||
return false;
|
||||
|
||||
if (!thisNext.getValue().equals(otherNext.getValue(), true)) // ignore freezing
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -156,7 +175,7 @@ public final class Types implements Iterable<UserType>
|
|||
|
||||
public static final class Builder
|
||||
{
|
||||
final ImmutableMap.Builder<ByteBuffer, UserType> types = ImmutableMap.builder();
|
||||
final ImmutableSortedMap.Builder<ByteBuffer, UserType> types = ImmutableSortedMap.naturalOrder();
|
||||
|
||||
private Builder()
|
||||
{
|
||||
|
|
@ -169,6 +188,7 @@ public final class Types implements Iterable<UserType>
|
|||
|
||||
public Builder add(UserType type)
|
||||
{
|
||||
assert type.isMultiCell();
|
||||
types.put(type.name, type);
|
||||
return this;
|
||||
}
|
||||
|
|
@ -293,7 +313,7 @@ public final class Types implements Iterable<UserType>
|
|||
.map(t -> t.prepareInternal(keyspace, types).getType())
|
||||
.collect(toList());
|
||||
|
||||
return new UserType(keyspace, bytes(name), preparedFieldNames, preparedFieldTypes);
|
||||
return new UserType(keyspace, bytes(name), preparedFieldNames, preparedFieldTypes, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -431,6 +431,7 @@ public class MigrationManager
|
|||
|
||||
public static void announceTypeUpdate(UserType updatedType, boolean announceLocally)
|
||||
{
|
||||
logger.info(String.format("Update type '%s.%s' to %s", updatedType.keyspace, updatedType.getNameAsString(), updatedType));
|
||||
announceNewType(updatedType, announceLocally);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ public enum DataType implements OptionCodec.Codecable<DataType>
|
|||
fieldNames.add(UTF8Type.instance.decompose(CBUtil.readString(cb)));
|
||||
fieldTypes.add(DataType.toType(codec.decodeOne(cb, version)));
|
||||
}
|
||||
return new UserType(ks, name, fieldNames, fieldTypes);
|
||||
return new UserType(ks, name, fieldNames, fieldTypes, true);
|
||||
case TUPLE:
|
||||
n = cb.readUnsignedShort();
|
||||
List<AbstractType<?>> types = new ArrayList<>(n);
|
||||
|
|
|
|||
|
|
@ -636,7 +636,7 @@ public class CQL3TypeLiteralTest
|
|||
names.add(UTF8Type.instance.fromString('f' + randLetters(i)));
|
||||
types.add(randomNestedType(level));
|
||||
}
|
||||
return new UserType("ks", UTF8Type.instance.fromString("u" + randInt(1000000)), names, types);
|
||||
return new UserType("ks", UTF8Type.instance.fromString("u" + randInt(1000000)), names, types, true);
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
|||
|
|
@ -139,6 +139,11 @@ public abstract class CQLTester
|
|||
private boolean usePrepared = USE_PREPARED_VALUES;
|
||||
private static boolean reusePrepared = REUSE_PREPARED;
|
||||
|
||||
protected boolean usePrepared()
|
||||
{
|
||||
return usePrepared;
|
||||
}
|
||||
|
||||
public static void prepareServer()
|
||||
{
|
||||
if (isServerPrepared)
|
||||
|
|
@ -1117,6 +1122,24 @@ public abstract class CQLTester
|
|||
e.getMessage().contains(text));
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface CheckedFunction {
|
||||
void apply() throws Throwable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the given function before and after a flush of sstables. This is useful for checking that behavior is
|
||||
* the same whether data is in memtables or sstables.
|
||||
* @param runnable
|
||||
* @throws Throwable
|
||||
*/
|
||||
public void beforeAndAfterFlush(CheckedFunction runnable) throws Throwable
|
||||
{
|
||||
runnable.apply();
|
||||
flush();
|
||||
runnable.apply();
|
||||
}
|
||||
|
||||
private static String replaceValues(String query, Object[] values)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
|
@ -1327,7 +1350,7 @@ public abstract class CQLTester
|
|||
if (value instanceof ByteBuffer)
|
||||
return (ByteBuffer)value;
|
||||
|
||||
return type.decompose(value);
|
||||
return type.decompose(serializeTuples(value));
|
||||
}
|
||||
|
||||
private static String formatValue(ByteBuffer bb, AbstractType<?> type)
|
||||
|
|
@ -1354,7 +1377,19 @@ public abstract class CQLTester
|
|||
|
||||
protected Object userType(Object... values)
|
||||
{
|
||||
return new TupleValue(values).toByteBuffer();
|
||||
if (values.length % 2 != 0)
|
||||
throw new IllegalArgumentException("userType() requires an even number of arguments");
|
||||
|
||||
String[] fieldNames = new String[values.length / 2];
|
||||
Object[] fieldValues = new Object[values.length / 2];
|
||||
int fieldNum = 0;
|
||||
for (int i = 0; i < values.length; i += 2)
|
||||
{
|
||||
fieldNames[fieldNum] = (String) values[i];
|
||||
fieldValues[fieldNum] = values[i + 1];
|
||||
fieldNum++;
|
||||
}
|
||||
return new UserTypeValue(fieldNames, fieldValues);
|
||||
}
|
||||
|
||||
protected Object list(Object...values)
|
||||
|
|
@ -1468,7 +1503,7 @@ public abstract class CQLTester
|
|||
|
||||
private static class TupleValue
|
||||
{
|
||||
private final Object[] values;
|
||||
protected final Object[] values;
|
||||
|
||||
TupleValue(Object[] values)
|
||||
{
|
||||
|
|
@ -1502,4 +1537,43 @@ public abstract class CQLTester
|
|||
return "TupleValue" + toCQLString();
|
||||
}
|
||||
}
|
||||
|
||||
private static class UserTypeValue extends TupleValue
|
||||
{
|
||||
private final String[] fieldNames;
|
||||
|
||||
UserTypeValue(String[] fieldNames, Object[] fieldValues)
|
||||
{
|
||||
super(fieldValues);
|
||||
this.fieldNames = fieldNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toCQLString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("{");
|
||||
boolean haveEntry = false;
|
||||
for (int i = 0; i < values.length; i++)
|
||||
{
|
||||
if (values[i] != null)
|
||||
{
|
||||
if (haveEntry)
|
||||
sb.append(", ");
|
||||
sb.append(ColumnIdentifier.maybeQuote(fieldNames[i]));
|
||||
sb.append(": ");
|
||||
sb.append(formatForCQL(values[i]));
|
||||
haveEntry = true;
|
||||
}
|
||||
}
|
||||
assert haveEntry;
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return "UserTypeValue" + toCQLString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ public class ColumnConditionTest
|
|||
ColumnDefinition definition = ColumnDefinition.regularDef("ks", "cf", "c", ListType.getInstance(Int32Type.instance, true));
|
||||
|
||||
// EQ
|
||||
ColumnCondition condition = ColumnCondition.condition(definition, null, new Lists.Value(Arrays.asList(ONE)), Operator.EQ);
|
||||
ColumnCondition condition = ColumnCondition.condition(definition, new Lists.Value(Arrays.asList(ONE)), Operator.EQ);
|
||||
ColumnCondition.CollectionBound bound = (ColumnCondition.CollectionBound) condition.bind(QueryOptions.DEFAULT);
|
||||
assertTrue(listAppliesTo(bound, list(ONE), list(ONE)));
|
||||
assertTrue(listAppliesTo(bound, list(), list()));
|
||||
|
|
@ -202,7 +202,7 @@ public class ColumnConditionTest
|
|||
assertTrue(listAppliesTo(bound, list(ByteBufferUtil.EMPTY_BYTE_BUFFER), list(ByteBufferUtil.EMPTY_BYTE_BUFFER)));
|
||||
|
||||
// NEQ
|
||||
condition = ColumnCondition.condition(definition, null, new Lists.Value(Arrays.asList(ONE)), Operator.NEQ);
|
||||
condition = ColumnCondition.condition(definition, new Lists.Value(Arrays.asList(ONE)), Operator.NEQ);
|
||||
bound = (ColumnCondition.CollectionBound) condition.bind(QueryOptions.DEFAULT);
|
||||
assertFalse(listAppliesTo(bound, list(ONE), list(ONE)));
|
||||
assertFalse(listAppliesTo(bound, list(), list()));
|
||||
|
|
@ -218,7 +218,7 @@ public class ColumnConditionTest
|
|||
assertFalse(listAppliesTo(bound, list(ByteBufferUtil.EMPTY_BYTE_BUFFER), list(ByteBufferUtil.EMPTY_BYTE_BUFFER)));
|
||||
|
||||
// LT
|
||||
condition = ColumnCondition.condition(definition, null, new Lists.Value(Arrays.asList(ONE)), Operator.LT);
|
||||
condition = ColumnCondition.condition(definition, new Lists.Value(Arrays.asList(ONE)), Operator.LT);
|
||||
bound = (ColumnCondition.CollectionBound) condition.bind(QueryOptions.DEFAULT);
|
||||
assertFalse(listAppliesTo(bound, list(ONE), list(ONE)));
|
||||
assertFalse(listAppliesTo(bound, list(), list()));
|
||||
|
|
@ -234,7 +234,7 @@ public class ColumnConditionTest
|
|||
assertFalse(listAppliesTo(bound, list(ByteBufferUtil.EMPTY_BYTE_BUFFER), list(ByteBufferUtil.EMPTY_BYTE_BUFFER)));
|
||||
|
||||
// LTE
|
||||
condition = ColumnCondition.condition(definition, null, new Lists.Value(Arrays.asList(ONE)), Operator.LTE);
|
||||
condition = ColumnCondition.condition(definition, new Lists.Value(Arrays.asList(ONE)), Operator.LTE);
|
||||
bound = (ColumnCondition.CollectionBound) condition.bind(QueryOptions.DEFAULT);
|
||||
assertTrue(listAppliesTo(bound, list(ONE), list(ONE)));
|
||||
assertTrue(listAppliesTo(bound, list(), list()));
|
||||
|
|
@ -250,7 +250,7 @@ public class ColumnConditionTest
|
|||
assertTrue(listAppliesTo(bound, list(ByteBufferUtil.EMPTY_BYTE_BUFFER), list(ByteBufferUtil.EMPTY_BYTE_BUFFER)));
|
||||
|
||||
// GT
|
||||
condition = ColumnCondition.condition(definition, null, new Lists.Value(Arrays.asList(ONE)), Operator.GT);
|
||||
condition = ColumnCondition.condition(definition, new Lists.Value(Arrays.asList(ONE)), Operator.GT);
|
||||
bound = (ColumnCondition.CollectionBound) condition.bind(QueryOptions.DEFAULT);
|
||||
assertFalse(listAppliesTo(bound, list(ONE), list(ONE)));
|
||||
assertFalse(listAppliesTo(bound, list(), list()));
|
||||
|
|
@ -266,7 +266,7 @@ public class ColumnConditionTest
|
|||
assertFalse(listAppliesTo(bound, list(ByteBufferUtil.EMPTY_BYTE_BUFFER), list(ByteBufferUtil.EMPTY_BYTE_BUFFER)));
|
||||
|
||||
// GTE
|
||||
condition = ColumnCondition.condition(definition, null, new Lists.Value(Arrays.asList(ONE)), Operator.GTE);
|
||||
condition = ColumnCondition.condition(definition, new Lists.Value(Arrays.asList(ONE)), Operator.GTE);
|
||||
bound = (ColumnCondition.CollectionBound) condition.bind(QueryOptions.DEFAULT);
|
||||
assertTrue(listAppliesTo(bound, list(ONE), list(ONE)));
|
||||
assertTrue(listAppliesTo(bound, list(), list()));
|
||||
|
|
@ -315,7 +315,7 @@ public class ColumnConditionTest
|
|||
ColumnDefinition definition = ColumnDefinition.regularDef("ks", "cf", "c", ListType.getInstance(Int32Type.instance, true));
|
||||
|
||||
// EQ
|
||||
ColumnCondition condition = ColumnCondition.condition(definition, null, new Sets.Value(set(ONE)), Operator.EQ);
|
||||
ColumnCondition condition = ColumnCondition.condition(definition, new Sets.Value(set(ONE)), Operator.EQ);
|
||||
ColumnCondition.CollectionBound bound = (ColumnCondition.CollectionBound) condition.bind(QueryOptions.DEFAULT);
|
||||
assertTrue(setAppliesTo(bound, set(ONE), list(ONE)));
|
||||
assertTrue(setAppliesTo(bound, set(), list()));
|
||||
|
|
@ -331,7 +331,7 @@ public class ColumnConditionTest
|
|||
assertTrue(setAppliesTo(bound, set(ByteBufferUtil.EMPTY_BYTE_BUFFER), list(ByteBufferUtil.EMPTY_BYTE_BUFFER)));
|
||||
|
||||
// NEQ
|
||||
condition = ColumnCondition.condition(definition, null, new Sets.Value(set(ONE)), Operator.NEQ);
|
||||
condition = ColumnCondition.condition(definition, new Sets.Value(set(ONE)), Operator.NEQ);
|
||||
bound = (ColumnCondition.CollectionBound) condition.bind(QueryOptions.DEFAULT);
|
||||
assertFalse(setAppliesTo(bound, set(ONE), list(ONE)));
|
||||
assertFalse(setAppliesTo(bound, set(), list()));
|
||||
|
|
@ -347,7 +347,7 @@ public class ColumnConditionTest
|
|||
assertFalse(setAppliesTo(bound, set(ByteBufferUtil.EMPTY_BYTE_BUFFER), list(ByteBufferUtil.EMPTY_BYTE_BUFFER)));
|
||||
|
||||
// LT
|
||||
condition = ColumnCondition.condition(definition, null, new Lists.Value(Arrays.asList(ONE)), Operator.LT);
|
||||
condition = ColumnCondition.condition(definition, new Lists.Value(Arrays.asList(ONE)), Operator.LT);
|
||||
bound = (ColumnCondition.CollectionBound) condition.bind(QueryOptions.DEFAULT);
|
||||
assertFalse(setAppliesTo(bound, set(ONE), list(ONE)));
|
||||
assertFalse(setAppliesTo(bound, set(), list()));
|
||||
|
|
@ -363,7 +363,7 @@ public class ColumnConditionTest
|
|||
assertFalse(setAppliesTo(bound, set(ByteBufferUtil.EMPTY_BYTE_BUFFER), list(ByteBufferUtil.EMPTY_BYTE_BUFFER)));
|
||||
|
||||
// LTE
|
||||
condition = ColumnCondition.condition(definition, null, new Lists.Value(Arrays.asList(ONE)), Operator.LTE);
|
||||
condition = ColumnCondition.condition(definition, new Lists.Value(Arrays.asList(ONE)), Operator.LTE);
|
||||
bound = (ColumnCondition.CollectionBound) condition.bind(QueryOptions.DEFAULT);
|
||||
assertTrue(setAppliesTo(bound, set(ONE), list(ONE)));
|
||||
assertTrue(setAppliesTo(bound, set(), list()));
|
||||
|
|
@ -379,7 +379,7 @@ public class ColumnConditionTest
|
|||
assertTrue(setAppliesTo(bound, set(ByteBufferUtil.EMPTY_BYTE_BUFFER), list(ByteBufferUtil.EMPTY_BYTE_BUFFER)));
|
||||
|
||||
// GT
|
||||
condition = ColumnCondition.condition(definition, null, new Lists.Value(Arrays.asList(ONE)), Operator.GT);
|
||||
condition = ColumnCondition.condition(definition, new Lists.Value(Arrays.asList(ONE)), Operator.GT);
|
||||
bound = (ColumnCondition.CollectionBound) condition.bind(QueryOptions.DEFAULT);
|
||||
assertFalse(setAppliesTo(bound, set(ONE), list(ONE)));
|
||||
assertFalse(setAppliesTo(bound, set(), list()));
|
||||
|
|
@ -395,7 +395,7 @@ public class ColumnConditionTest
|
|||
assertFalse(setAppliesTo(bound, set(ByteBufferUtil.EMPTY_BYTE_BUFFER), list(ByteBufferUtil.EMPTY_BYTE_BUFFER)));
|
||||
|
||||
// GTE
|
||||
condition = ColumnCondition.condition(definition, null, new Lists.Value(Arrays.asList(ONE)), Operator.GTE);
|
||||
condition = ColumnCondition.condition(definition, new Lists.Value(Arrays.asList(ONE)), Operator.GTE);
|
||||
bound = (ColumnCondition.CollectionBound) condition.bind(QueryOptions.DEFAULT);
|
||||
assertTrue(setAppliesTo(bound, set(ONE), list(ONE)));
|
||||
assertTrue(setAppliesTo(bound, set(), list()));
|
||||
|
|
@ -448,7 +448,7 @@ public class ColumnConditionTest
|
|||
Maps.Value placeholder = new Maps.Value(placeholderMap);
|
||||
|
||||
// EQ
|
||||
ColumnCondition condition = ColumnCondition.condition(definition, null, placeholder, Operator.EQ);
|
||||
ColumnCondition condition = ColumnCondition.condition(definition, placeholder, Operator.EQ);
|
||||
ColumnCondition.CollectionBound bound = (ColumnCondition.CollectionBound) condition.bind(QueryOptions.DEFAULT);
|
||||
|
||||
assertTrue(mapAppliesTo(bound, map(ONE, ONE), map(ONE, ONE)));
|
||||
|
|
@ -470,7 +470,7 @@ public class ColumnConditionTest
|
|||
assertTrue(mapAppliesTo(bound, map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER), map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER)));
|
||||
|
||||
// NEQ
|
||||
condition = ColumnCondition.condition(definition, null, placeholder, Operator.NEQ);
|
||||
condition = ColumnCondition.condition(definition, placeholder, Operator.NEQ);
|
||||
bound = (ColumnCondition.CollectionBound) condition.bind(QueryOptions.DEFAULT);
|
||||
|
||||
assertFalse(mapAppliesTo(bound, map(ONE, ONE), map(ONE, ONE)));
|
||||
|
|
@ -492,7 +492,7 @@ public class ColumnConditionTest
|
|||
assertFalse(mapAppliesTo(bound, map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER), map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER)));
|
||||
|
||||
// LT
|
||||
condition = ColumnCondition.condition(definition, null, placeholder, Operator.LT);
|
||||
condition = ColumnCondition.condition(definition, placeholder, Operator.LT);
|
||||
bound = (ColumnCondition.CollectionBound) condition.bind(QueryOptions.DEFAULT);
|
||||
|
||||
assertFalse(mapAppliesTo(bound, map(ONE, ONE), map(ONE, ONE)));
|
||||
|
|
@ -514,7 +514,7 @@ public class ColumnConditionTest
|
|||
assertFalse(mapAppliesTo(bound, map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER), map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER)));
|
||||
|
||||
// LTE
|
||||
condition = ColumnCondition.condition(definition, null, placeholder, Operator.LTE);
|
||||
condition = ColumnCondition.condition(definition, placeholder, Operator.LTE);
|
||||
bound = (ColumnCondition.CollectionBound) condition.bind(QueryOptions.DEFAULT);
|
||||
|
||||
assertTrue(mapAppliesTo(bound, map(ONE, ONE), map(ONE, ONE)));
|
||||
|
|
@ -536,7 +536,7 @@ public class ColumnConditionTest
|
|||
assertTrue(mapAppliesTo(bound, map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER), map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER)));
|
||||
|
||||
// GT
|
||||
condition = ColumnCondition.condition(definition, null, placeholder, Operator.GT);
|
||||
condition = ColumnCondition.condition(definition, placeholder, Operator.GT);
|
||||
bound = (ColumnCondition.CollectionBound) condition.bind(QueryOptions.DEFAULT);
|
||||
|
||||
assertFalse(mapAppliesTo(bound, map(ONE, ONE), map(ONE, ONE)));
|
||||
|
|
@ -558,7 +558,7 @@ public class ColumnConditionTest
|
|||
assertFalse(mapAppliesTo(bound, map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER), map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER)));
|
||||
|
||||
// GTE
|
||||
condition = ColumnCondition.condition(definition, null, placeholder, Operator.GTE);
|
||||
condition = ColumnCondition.condition(definition, placeholder, Operator.GTE);
|
||||
bound = (ColumnCondition.CollectionBound) condition.bind(QueryOptions.DEFAULT);
|
||||
|
||||
assertTrue(mapAppliesTo(bound, map(ONE, ONE), map(ONE, ONE)));
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ public class SelectionColumnMappingTest extends CQLTester
|
|||
" v1 int," +
|
||||
" v2 ascii," +
|
||||
" v3 frozen<" + typeName + ">)");
|
||||
userType = Schema.instance.getKSMetaData(KEYSPACE).types.get(ByteBufferUtil.bytes(typeName)).get();
|
||||
userType = Schema.instance.getKSMetaData(KEYSPACE).types.get(ByteBufferUtil.bytes(typeName)).get().freeze();
|
||||
functionName = createFunction(KEYSPACE, "int, ascii",
|
||||
"CREATE FUNCTION %s (i int, a ascii) " +
|
||||
"CALLED ON NULL INPUT " +
|
||||
|
|
|
|||
|
|
@ -73,32 +73,86 @@ public class UserTypesTest extends CQLTester
|
|||
execute("INSERT INTO %s(k, v) VALUES (?, {x:?})", 1, -104.99251);
|
||||
execute("UPDATE %s SET b = ? WHERE k = ?", true, 1);
|
||||
|
||||
assertRows(execute("SELECT v.x FROM %s WHERE k = ? AND v = {x:?}", 1, -104.99251),
|
||||
row(-104.99251)
|
||||
);
|
||||
|
||||
flush();
|
||||
|
||||
assertRows(execute("SELECT v.x FROM %s WHERE k = ? AND v = {x:?}", 1, -104.99251),
|
||||
row(-104.99251)
|
||||
beforeAndAfterFlush(() ->
|
||||
assertRows(execute("SELECT v.x FROM %s WHERE k = ? AND v = {x:?}", 1, -104.99251),
|
||||
row(-104.99251)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateInvalidTablesWithUDT() throws Throwable
|
||||
public void testInvalidUDTStatements() throws Throwable
|
||||
{
|
||||
String myType = createType("CREATE TYPE %s (f int)");
|
||||
String typename = createType("CREATE TYPE %s (a int)");
|
||||
String myType = KEYSPACE + '.' + typename;
|
||||
|
||||
// Using a UDT without frozen shouldn't work
|
||||
assertInvalidMessage("Non-frozen User-Defined types are not supported, please use frozen<>",
|
||||
"CREATE TABLE " + KEYSPACE + ".wrong (k int PRIMARY KEY, v " + KEYSPACE + '.' + myType + ")");
|
||||
// non-frozen UDTs in a table PK
|
||||
assertInvalidMessage("Invalid non-frozen user-defined type for PRIMARY KEY component k",
|
||||
"CREATE TABLE " + KEYSPACE + ".wrong (k " + myType + " PRIMARY KEY , v int)");
|
||||
assertInvalidMessage("Invalid non-frozen user-defined type for PRIMARY KEY component k2",
|
||||
"CREATE TABLE " + KEYSPACE + ".wrong (k1 int, k2 " + myType + ", v int, PRIMARY KEY (k1, k2))");
|
||||
|
||||
// non-frozen UDTs in a collection
|
||||
assertInvalidMessage("Non-frozen UDTs are not allowed inside collections: list<" + myType + ">",
|
||||
"CREATE TABLE " + KEYSPACE + ".wrong (k int PRIMARY KEY, v list<" + myType + ">)");
|
||||
assertInvalidMessage("Non-frozen UDTs are not allowed inside collections: set<" + myType + ">",
|
||||
"CREATE TABLE " + KEYSPACE + ".wrong (k int PRIMARY KEY, v set<" + myType + ">)");
|
||||
assertInvalidMessage("Non-frozen UDTs are not allowed inside collections: map<" + myType + ", int>",
|
||||
"CREATE TABLE " + KEYSPACE + ".wrong (k int PRIMARY KEY, v map<" + myType + ", int>)");
|
||||
assertInvalidMessage("Non-frozen UDTs are not allowed inside collections: map<int, " + myType + ">",
|
||||
"CREATE TABLE " + KEYSPACE + ".wrong (k int PRIMARY KEY, v map<int, " + myType + ">)");
|
||||
|
||||
// non-frozen UDT in a collection (as part of a UDT definition)
|
||||
assertInvalidMessage("Non-frozen UDTs are not allowed inside collections: list<" + myType + ">",
|
||||
"CREATE TYPE " + KEYSPACE + ".wrong (a int, b list<" + myType + ">)");
|
||||
|
||||
// non-frozen UDT in a UDT
|
||||
assertInvalidMessage("A user type cannot contain non-frozen UDTs",
|
||||
"CREATE TYPE " + KEYSPACE + ".wrong (a int, b " + myType + ")");
|
||||
|
||||
// referencing a UDT in another keyspace
|
||||
assertInvalidMessage("Statement on keyspace " + KEYSPACE + " cannot refer to a user type in keyspace otherkeyspace;" +
|
||||
" user types can only be used in the keyspace they are defined in",
|
||||
"CREATE TABLE " + KEYSPACE + ".wrong (k int PRIMARY KEY, v frozen<otherKeyspace.myType>)");
|
||||
|
||||
// referencing an unknown UDT
|
||||
assertInvalidMessage("Unknown type " + KEYSPACE + ".unknowntype",
|
||||
"CREATE TABLE " + KEYSPACE + ".wrong (k int PRIMARY KEY, v frozen<" + KEYSPACE + '.' + "unknownType>)");
|
||||
|
||||
// bad deletions on frozen UDTs
|
||||
createTable("CREATE TABLE %s (a int PRIMARY KEY, b frozen<" + myType + ">, c int)");
|
||||
assertInvalidMessage("Frozen UDT column b does not support field deletion", "DELETE b.a FROM %s WHERE a = 0");
|
||||
assertInvalidMessage("Invalid field deletion operation for non-UDT column c", "DELETE c.a FROM %s WHERE a = 0");
|
||||
|
||||
// bad updates on frozen UDTs
|
||||
assertInvalidMessage("Invalid operation (b.a = 0) for frozen UDT column b", "UPDATE %s SET b.a = 0 WHERE a = 0");
|
||||
assertInvalidMessage("Invalid operation (c.a = 0) for non-UDT column c", "UPDATE %s SET c.a = 0 WHERE a = 0");
|
||||
|
||||
// bad deletions on non-frozen UDTs
|
||||
createTable("CREATE TABLE %s (a int PRIMARY KEY, b " + myType + ", c int)");
|
||||
assertInvalidMessage("UDT column b does not have a field named foo", "DELETE b.foo FROM %s WHERE a = 0");
|
||||
|
||||
// bad updates on non-frozen UDTs
|
||||
assertInvalidMessage("UDT column b does not have a field named foo", "UPDATE %s SET b.foo = 0 WHERE a = 0");
|
||||
|
||||
// bad insert on non-frozen UDTs
|
||||
assertInvalidMessage("Unknown field 'foo' in value of user defined type", "INSERT INTO %s (a, b, c) VALUES (0, {a: 0, foo: 0}, 0)");
|
||||
if (usePrepared())
|
||||
{
|
||||
assertInvalidMessage("Expected 1 value for " + typename + " column, but got more",
|
||||
"INSERT INTO %s (a, b, c) VALUES (0, ?, 0)", userType("a", 0, "foo", 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
assertInvalidMessage("Unknown field 'foo' in value of user defined type " + typename,
|
||||
"INSERT INTO %s (a, b, c) VALUES (0, ?, 0)", userType("a", 0, "foo", 0));
|
||||
}
|
||||
|
||||
// non-frozen UDT with non-frozen nested collection
|
||||
String typename2 = createType("CREATE TYPE %s (bar int, foo list<int>)");
|
||||
String myType2 = KEYSPACE + '.' + typename2;
|
||||
assertInvalidMessage("Non-frozen UDTs with nested non-frozen collections are not supported",
|
||||
"CREATE TABLE " + KEYSPACE + ".wrong (k int PRIMARY KEY, v " + myType2 + ")");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -106,24 +160,61 @@ public class UserTypesTest extends CQLTester
|
|||
{
|
||||
String myType = KEYSPACE + '.' + createType("CREATE TYPE %s (a int)");
|
||||
createTable("CREATE TABLE %s (a int PRIMARY KEY, b frozen<" + myType + ">)");
|
||||
execute("INSERT INTO %s (a, b) VALUES (1, {a: 1})");
|
||||
execute("INSERT INTO %s (a, b) VALUES (1, ?)", userType("a", 1));
|
||||
|
||||
assertRows(execute("SELECT b.a FROM %s"), row(1));
|
||||
|
||||
flush();
|
||||
|
||||
execute("ALTER TYPE " + myType + " ADD b int");
|
||||
execute("INSERT INTO %s (a, b) VALUES (2, {a: 2, b :2})");
|
||||
schemaChange("ALTER TYPE " + myType + " ADD b int");
|
||||
execute("INSERT INTO %s (a, b) VALUES (2, ?)", userType("a", 2, "b", 2));
|
||||
|
||||
assertRows(execute("SELECT b.a, b.b FROM %s"),
|
||||
row(1, null),
|
||||
row(2, 2));
|
||||
beforeAndAfterFlush(() ->
|
||||
assertRows(execute("SELECT b.a, b.b FROM %s"),
|
||||
row(1, null),
|
||||
row(2, 2))
|
||||
);
|
||||
}
|
||||
|
||||
flush();
|
||||
@Test
|
||||
public void testAlterNonFrozenUDT() throws Throwable
|
||||
{
|
||||
String myType = KEYSPACE + '.' + createType("CREATE TYPE %s (a int, b text)");
|
||||
createTable("CREATE TABLE %s (k int PRIMARY KEY, v " + myType + ")");
|
||||
execute("INSERT INTO %s (k, v) VALUES (0, ?)", userType("a", 1, "b", "abc"));
|
||||
|
||||
assertRows(execute("SELECT b.a, b.b FROM %s"),
|
||||
row(1, null),
|
||||
row(2, 2));
|
||||
beforeAndAfterFlush(() -> {
|
||||
assertRows(execute("SELECT v FROM %s"), row(userType("a", 1, "b", "abc")));
|
||||
assertRows(execute("SELECT v.a FROM %s"), row(1));
|
||||
assertRows(execute("SELECT v.b FROM %s"), row("abc"));
|
||||
});
|
||||
|
||||
schemaChange("ALTER TYPE " + myType + " RENAME b TO foo");
|
||||
assertRows(execute("SELECT v FROM %s"), row(userType("a", 1, "b", "abc")));
|
||||
assertRows(execute("SELECT v.a FROM %s"), row(1));
|
||||
assertRows(execute("SELECT v.foo FROM %s"), row("abc"));
|
||||
|
||||
execute("UPDATE %s SET v.foo = 'def' WHERE k = 0");
|
||||
assertRows(execute("SELECT v FROM %s"), row(userType("a", 1, "foo", "def")));
|
||||
assertRows(execute("SELECT v.a FROM %s"), row(1));
|
||||
assertRows(execute("SELECT v.foo FROM %s"), row("def"));
|
||||
|
||||
execute("INSERT INTO %s (k, v) VALUES (0, ?)", userType("a", 2, "foo", "def"));
|
||||
assertRows(execute("SELECT v FROM %s"), row(userType("a", 2, "foo", "def")));
|
||||
assertRows(execute("SELECT v.a FROM %s"), row(2));
|
||||
assertRows(execute("SELECT v.foo FROM %s"), row("def"));
|
||||
|
||||
schemaChange("ALTER TYPE " + myType + " ADD c int");
|
||||
assertRows(execute("SELECT v FROM %s"), row(userType("a", 2, "foo", "def", "c", null)));
|
||||
assertRows(execute("SELECT v.a FROM %s"), row(2));
|
||||
assertRows(execute("SELECT v.foo FROM %s"), row("def"));
|
||||
assertRows(execute("SELECT v.c FROM %s"), row(new Object[] {null}));
|
||||
|
||||
execute("INSERT INTO %s (k, v) VALUES (0, ?)", userType("a", 3, "foo", "abc", "c", 0));
|
||||
beforeAndAfterFlush(() -> {
|
||||
assertRows(execute("SELECT v FROM %s"), row(userType("a", 3, "foo", "abc", "c", 0)));
|
||||
assertRows(execute("SELECT v.c FROM %s"), row(0));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -134,11 +225,14 @@ public class UserTypesTest extends CQLTester
|
|||
String myOtherType = createType("CREATE TYPE %s (a frozen<" + myType + ">)");
|
||||
createTable("CREATE TABLE %s (k int PRIMARY KEY, v frozen<" + myType + ">, z frozen<" + myOtherType + ">)");
|
||||
|
||||
assertInvalidMessage("Invalid unset value for field 'y' of user defined type " + myType,
|
||||
"INSERT INTO %s (k, v) VALUES (10, {x:?, y:?})", 1, unset());
|
||||
if (usePrepared())
|
||||
{
|
||||
assertInvalidMessage("Invalid unset value for field 'y' of user defined type " + myType,
|
||||
"INSERT INTO %s (k, v) VALUES (10, {x:?, y:?})", 1, unset());
|
||||
|
||||
assertInvalidMessage("Invalid unset value for field 'y' of user defined type " + myType,
|
||||
"INSERT INTO %s (k, v, z) VALUES (10, {x:?, y:?}, {a:{x: ?, y: ?}})", 1, 1, 1, unset());
|
||||
assertInvalidMessage("Invalid unset value for field 'y' of user defined type " + myType,
|
||||
"INSERT INTO %s (k, v, z) VALUES (10, {x:?, y:?}, {a:{x: ?, y: ?}})", 1, 1, 1, unset());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -153,28 +247,22 @@ public class UserTypesTest extends CQLTester
|
|||
|
||||
createTable("CREATE TABLE %s (x int PRIMARY KEY, y " + columnType + ")");
|
||||
|
||||
execute("INSERT INTO %s (x, y) VALUES(1, {'firstValue':{a:1}})");
|
||||
assertRows(execute("SELECT * FROM %s"), row(1, map("firstValue", userType(1))));
|
||||
execute("INSERT INTO %s (x, y) VALUES(1, ?)", map("firstValue", userType("a", 1)));
|
||||
assertRows(execute("SELECT * FROM %s"), row(1, map("firstValue", userType("a", 1))));
|
||||
flush();
|
||||
|
||||
execute("ALTER TYPE " + KEYSPACE + "." + ut1 + " ADD b int");
|
||||
execute("INSERT INTO %s (x, y) VALUES(2, {'secondValue':{a:2, b:2}})");
|
||||
execute("INSERT INTO %s (x, y) VALUES(3, {'thirdValue':{a:3}})");
|
||||
execute("INSERT INTO %s (x, y) VALUES(4, {'fourthValue':{b:4}})");
|
||||
execute("INSERT INTO %s (x, y) VALUES(2, ?)", map("secondValue", userType("a", 2, "b", 2)));
|
||||
execute("INSERT INTO %s (x, y) VALUES(3, ?)", map("thirdValue", userType("a", 3, "b", null)));
|
||||
execute("INSERT INTO %s (x, y) VALUES(4, ?)", map("fourthValue", userType("a", null, "b", 4)));
|
||||
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(1, map("firstValue", userType(1))),
|
||||
row(2, map("secondValue", userType(2, 2))),
|
||||
row(3, map("thirdValue", userType(3, null))),
|
||||
row(4, map("fourthValue", userType(null, 4))));
|
||||
|
||||
flush();
|
||||
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(1, map("firstValue", userType(1))),
|
||||
row(2, map("secondValue", userType(2, 2))),
|
||||
row(3, map("thirdValue", userType(3, null))),
|
||||
row(4, map("fourthValue", userType(null, 4))));
|
||||
beforeAndAfterFlush(() ->
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(1, map("firstValue", userType("a", 1))),
|
||||
row(2, map("secondValue", userType("a", 2, "b", 2))),
|
||||
row(3, map("thirdValue", userType("a", 3, "b", null))),
|
||||
row(4, map("fourthValue", userType("a", null, "b", 4))))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -190,28 +278,22 @@ public class UserTypesTest extends CQLTester
|
|||
|
||||
createTable("CREATE TABLE %s (x int PRIMARY KEY, y " + columnType + ")");
|
||||
|
||||
execute("INSERT INTO %s (x, y) VALUES(1, {1} )");
|
||||
assertRows(execute("SELECT * FROM %s"), row(1, set(userType(1))));
|
||||
execute("INSERT INTO %s (x, y) VALUES(1, ?)", set(userType("a", 1)));
|
||||
assertRows(execute("SELECT * FROM %s"), row(1, set(userType("a", 1))));
|
||||
flush();
|
||||
|
||||
execute("ALTER TYPE " + KEYSPACE + "." + ut1 + " ADD b int");
|
||||
execute("INSERT INTO %s (x, y) VALUES(2, {{a:2, b:2}})");
|
||||
execute("INSERT INTO %s (x, y) VALUES(3, {{a:3}})");
|
||||
execute("INSERT INTO %s (x, y) VALUES(4, {{b:4}})");
|
||||
execute("INSERT INTO %s (x, y) VALUES(2, ?)", set(userType("a", 2, "b", 2)));
|
||||
execute("INSERT INTO %s (x, y) VALUES(3, ?)", set(userType("a", 3, "b", null)));
|
||||
execute("INSERT INTO %s (x, y) VALUES(4, ?)", set(userType("a", null, "b", 4)));
|
||||
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(1, set(userType(1))),
|
||||
row(2, set(userType(2, 2))),
|
||||
row(3, set(userType(3, null))),
|
||||
row(4, set(userType(null, 4))));
|
||||
|
||||
flush();
|
||||
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(1, set(userType(1))),
|
||||
row(2, set(userType(2, 2))),
|
||||
row(3, set(userType(3, null))),
|
||||
row(4, set(userType(null, 4))));
|
||||
beforeAndAfterFlush(() ->
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(1, set(userType("a", 1))),
|
||||
row(2, set(userType("a", 2, "b", 2))),
|
||||
row(3, set(userType("a", 3, "b", null))),
|
||||
row(4, set(userType("a", null, "b", 4))))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -227,28 +309,22 @@ public class UserTypesTest extends CQLTester
|
|||
|
||||
createTable("CREATE TABLE %s (x int PRIMARY KEY, y " + columnType + ")");
|
||||
|
||||
execute("INSERT INTO %s (x, y) VALUES(1, [1] )");
|
||||
assertRows(execute("SELECT * FROM %s"), row(1, list(userType(1))));
|
||||
execute("INSERT INTO %s (x, y) VALUES(1, ?)", list(userType("a", 1)));
|
||||
assertRows(execute("SELECT * FROM %s"), row(1, list(userType("a", 1))));
|
||||
flush();
|
||||
|
||||
execute("ALTER TYPE " + KEYSPACE + "." + ut1 + " ADD b int");
|
||||
execute("INSERT INTO %s (x, y) VALUES(2, [{a:2, b:2}])");
|
||||
execute("INSERT INTO %s (x, y) VALUES(3, [{a:3}])");
|
||||
execute("INSERT INTO %s (x, y) VALUES(4, [{b:4}])");
|
||||
execute("INSERT INTO %s (x, y) VALUES (2, ?)", list(userType("a", 2, "b", 2)));
|
||||
execute("INSERT INTO %s (x, y) VALUES (3, ?)", list(userType("a", 3, "b", null)));
|
||||
execute("INSERT INTO %s (x, y) VALUES (4, ?)", list(userType("a", null, "b", 4)));
|
||||
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(1, list(userType(1))),
|
||||
row(2, list(userType(2, 2))),
|
||||
row(3, list(userType(3, null))),
|
||||
row(4, list(userType(null, 4))));
|
||||
|
||||
flush();
|
||||
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(1, list(userType(1))),
|
||||
row(2, list(userType(2, 2))),
|
||||
row(3, list(userType(3, null))),
|
||||
row(4, list(userType(null, 4))));
|
||||
beforeAndAfterFlush(() ->
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(1, list(userType("a", 1))),
|
||||
row(2, list(userType("a", 2, "b", 2))),
|
||||
row(3, list(userType("a", 3, "b", null))),
|
||||
row(4, list(userType("a", null, "b", 4))))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -259,28 +335,22 @@ public class UserTypesTest extends CQLTester
|
|||
|
||||
createTable("CREATE TABLE %s (a int PRIMARY KEY, b frozen<tuple<int, " + KEYSPACE + "." + type + ">>)");
|
||||
|
||||
execute("INSERT INTO %s (a, b) VALUES(1, (1, {a:1, b:1}))");
|
||||
assertRows(execute("SELECT * FROM %s"), row(1, tuple(1, userType(1, 1))));
|
||||
execute("INSERT INTO %s (a, b) VALUES(1, (1, ?))", userType("a", 1, "b", 1));
|
||||
assertRows(execute("SELECT * FROM %s"), row(1, tuple(1, userType("a", 1, "b", 1))));
|
||||
flush();
|
||||
|
||||
execute("ALTER TYPE " + KEYSPACE + "." + type + " ADD c int");
|
||||
execute("INSERT INTO %s (a, b) VALUES(2, (2, {a: 2, b: 2, c: 2}))");
|
||||
execute("INSERT INTO %s (a, b) VALUES(3, (3, {a: 3, b: 3}))");
|
||||
execute("INSERT INTO %s (a, b) VALUES(4, (4, {b:4}))");
|
||||
execute("INSERT INTO %s (a, b) VALUES (2, (2, ?))", userType("a", 2, "b", 2, "c", 2));
|
||||
execute("INSERT INTO %s (a, b) VALUES (3, (3, ?))", userType("a", 3, "b", 3, "c", null));
|
||||
execute("INSERT INTO %s (a, b) VALUES (4, (4, ?))", userType("a", null, "b", 4, "c", null));
|
||||
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(1, tuple(1, userType(1, 1))),
|
||||
row(2, tuple(2, userType(2, 2, 2))),
|
||||
row(3, tuple(3, userType(3, 3, null))),
|
||||
row(4, tuple(4, userType(null, 4, null))));
|
||||
|
||||
flush();
|
||||
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(1, tuple(1, userType(1, 1))),
|
||||
row(2, tuple(2, userType(2, 2, 2))),
|
||||
row(3, tuple(3, userType(3, 3, null))),
|
||||
row(4, tuple(4, userType(null, 4, null))));
|
||||
beforeAndAfterFlush(() ->
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(1, tuple(1, userType("a", 1, "b", 1))),
|
||||
row(2, tuple(2, userType("a", 2, "b", 2, "c", 2))),
|
||||
row(3, tuple(3, userType("a", 3, "b", 3, "c", null))),
|
||||
row(4, tuple(4, userType("a", null, "b", 4, "c", null))))
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -290,28 +360,22 @@ public class UserTypesTest extends CQLTester
|
|||
|
||||
createTable("CREATE TABLE %s (a int PRIMARY KEY, b frozen<tuple<int, tuple<int, " + KEYSPACE + "." + type + ">>>)");
|
||||
|
||||
execute("INSERT INTO %s (a, b) VALUES(1, (1, (1, {a:1, b:1})))");
|
||||
assertRows(execute("SELECT * FROM %s"), row(1, tuple(1, tuple(1, userType(1, 1)))));
|
||||
execute("INSERT INTO %s (a, b) VALUES(1, (1, (1, ?)))", userType("a", 1, "b", 1));
|
||||
assertRows(execute("SELECT * FROM %s"), row(1, tuple(1, tuple(1, userType("a", 1, "b", 1)))));
|
||||
flush();
|
||||
|
||||
execute("ALTER TYPE " + KEYSPACE + "." + type + " ADD c int");
|
||||
execute("INSERT INTO %s (a, b) VALUES(2, (2, (1, {a: 2, b: 2, c: 2})))");
|
||||
execute("INSERT INTO %s (a, b) VALUES(3, (3, (1, {a: 3, b: 3})))");
|
||||
execute("INSERT INTO %s (a, b) VALUES(4, (4, (1, {b:4})))");
|
||||
execute("INSERT INTO %s (a, b) VALUES(2, (2, (1, ?)))", userType("a", 2, "b", 2, "c", 2));
|
||||
execute("INSERT INTO %s (a, b) VALUES(3, (3, ?))", tuple(1, userType("a", 3, "b", 3, "c", null)));
|
||||
execute("INSERT INTO %s (a, b) VALUES(4, ?)", tuple(4, tuple(1, userType("a", null, "b", 4, "c", null))));
|
||||
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(1, tuple(1, tuple(1, userType(1, 1)))),
|
||||
row(2, tuple(2, tuple(1, userType(2, 2, 2)))),
|
||||
row(3, tuple(3, tuple(1, userType(3, 3, null)))),
|
||||
row(4, tuple(4, tuple(1, userType(null, 4, null)))));
|
||||
|
||||
flush();
|
||||
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(1, tuple(1, tuple(1, userType(1, 1)))),
|
||||
row(2, tuple(2, tuple(1, userType(2, 2, 2)))),
|
||||
row(3, tuple(3, tuple(1, userType(3, 3, null)))),
|
||||
row(4, tuple(4, tuple(1, userType(null, 4, null)))));
|
||||
beforeAndAfterFlush(() ->
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(1, tuple(1, tuple(1, userType("a", 1, "b", 1)))),
|
||||
row(2, tuple(2, tuple(1, userType("a", 2, "b", 2, "c", 2)))),
|
||||
row(3, tuple(3, tuple(1, userType("a", 3, "b", 3, "c", null)))),
|
||||
row(4, tuple(4, tuple(1, userType("a", null, "b", 4, "c", null)))))
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -322,28 +386,24 @@ public class UserTypesTest extends CQLTester
|
|||
|
||||
createTable("CREATE TABLE %s (a int PRIMARY KEY, b frozen<" + KEYSPACE + "." + otherType + ">)");
|
||||
|
||||
execute("INSERT INTO %s (a, b) VALUES(1, {x: {a:1, b:1}})");
|
||||
execute("INSERT INTO %s (a, b) VALUES(1, {x: ?})", userType("a", 1, "b", 1));
|
||||
assertRows(execute("SELECT b.x.a, b.x.b FROM %s"), row(1, 1));
|
||||
execute("INSERT INTO %s (a, b) VALUES(1, ?)", userType("x", userType("a", 1, "b", 1)));
|
||||
assertRows(execute("SELECT b.x.a, b.x.b FROM %s"), row(1, 1));
|
||||
flush();
|
||||
|
||||
execute("ALTER TYPE " + KEYSPACE + "." + type + " ADD c int");
|
||||
execute("INSERT INTO %s (a, b) VALUES(2, {x: {a: 2, b: 2, c: 2}})");
|
||||
execute("INSERT INTO %s (a, b) VALUES(3, {x: {a: 3, b: 3}})");
|
||||
execute("INSERT INTO %s (a, b) VALUES(4, {x: {b:4}})");
|
||||
execute("INSERT INTO %s (a, b) VALUES(2, {x: ?})", userType("a", 2, "b", 2, "c", 2));
|
||||
execute("INSERT INTO %s (a, b) VALUES(3, {x: ?})", userType("a", 3, "b", 3));
|
||||
execute("INSERT INTO %s (a, b) VALUES(4, {x: ?})", userType("a", null, "b", 4));
|
||||
|
||||
assertRows(execute("SELECT b.x.a, b.x.b, b.x.c FROM %s"),
|
||||
row(1, 1, null),
|
||||
row(2, 2, 2),
|
||||
row(3, 3, null),
|
||||
row(null, 4, null));
|
||||
|
||||
flush();
|
||||
|
||||
assertRows(execute("SELECT b.x.a, b.x.b, b.x.c FROM %s"),
|
||||
row(1, 1, null),
|
||||
row(2, 2, 2),
|
||||
row(3, 3, null),
|
||||
row(null, 4, null));
|
||||
beforeAndAfterFlush(() ->
|
||||
assertRows(execute("SELECT b.x.a, b.x.b, b.x.c FROM %s"),
|
||||
row(1, 1, null),
|
||||
row(2, 2, 2),
|
||||
row(3, 3, null),
|
||||
row(null, 4, null))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -383,10 +443,11 @@ public class UserTypesTest extends CQLTester
|
|||
|
||||
createTable("CREATE TABLE %s (id int PRIMARY KEY, val frozen<" + type2 + ">)");
|
||||
|
||||
execute("INSERT INTO %s (id, val) VALUES (0, { s : {{ s : {'foo', 'bar'}, m : { 'foo' : 'bar' }, l : ['foo', 'bar']} }})");
|
||||
execute("INSERT INTO %s (id, val) VALUES (0, ?)",
|
||||
userType("s", set(userType("s", set("foo", "bar"), "m", map("foo", "bar"), "l", list("foo", "bar")))));
|
||||
|
||||
// TODO: check result once we have an easy way to do it. For now we just check it doesn't crash
|
||||
execute("SELECT * FROM %s");
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(0, userType("s", set(userType("s", set("foo", "bar"), "m", map("foo", "bar"), "l", list("foo", "bar"))))));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -398,9 +459,11 @@ public class UserTypesTest extends CQLTester
|
|||
String typeName = createType("CREATE TYPE %s (fooint int, fooset set <text>)");
|
||||
createTable("CREATE TABLE %s (key int PRIMARY KEY, data frozen <" + typeName + ">)");
|
||||
|
||||
execute("INSERT INTO %s (key, data) VALUES (1, {fooint: 1, fooset: {'2'}})");
|
||||
execute("INSERT INTO %s (key, data) VALUES (1, ?)", userType("fooint", 1, "fooset", set("2")));
|
||||
execute("ALTER TYPE " + keyspace() + "." + typeName + " ADD foomap map <int,text>");
|
||||
execute("INSERT INTO %s (key, data) VALUES (1, {fooint: 1, fooset: {'2'}, foomap: {3 : 'bar'}})");
|
||||
execute("INSERT INTO %s (key, data) VALUES (1, ?)", userType("fooint", 1, "fooset", set("2"), "foomap", map(3, "bar")));
|
||||
assertRows(execute("SELECT * FROM %s"),
|
||||
row(1, userType("fooint", 1, "fooset", set("2"), "foomap", map(3, "bar"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -464,13 +527,13 @@ public class UserTypesTest extends CQLTester
|
|||
|
||||
type1 = createType("CREATE TYPE %s (foo ascii)");
|
||||
String type2 = createType("CREATE TYPE %s (foo frozen<" + type1 + ">)");
|
||||
assertComplexInvalidAlterDropStatements(type1, type2, "{foo: 'abc'}");
|
||||
assertComplexInvalidAlterDropStatements(type1, type2, "{foo: {foo: 'abc'}}");
|
||||
|
||||
type1 = createType("CREATE TYPE %s (foo ascii)");
|
||||
type2 = createType("CREATE TYPE %s (foo frozen<" + type1 + ">)");
|
||||
assertComplexInvalidAlterDropStatements(type1,
|
||||
"list<frozen<" + type2 + ">>",
|
||||
"[{foo: 'abc'}]");
|
||||
"[{foo: {foo: 'abc'}}]");
|
||||
|
||||
type1 = createType("CREATE TYPE %s (foo ascii)");
|
||||
type2 = createType("CREATE TYPE %s (foo frozen<set<" + type1 + ">>)");
|
||||
|
|
@ -515,6 +578,110 @@ public class UserTypesTest extends CQLTester
|
|||
assertInvalidMessage("Cannot drop user type " + typeWithKs(t), "DROP TYPE " + typeWithKs(t) + ';');
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertNonFrozenUDT() throws Throwable
|
||||
{
|
||||
String typeName = createType("CREATE TYPE %s (a int, b text)");
|
||||
createTable("CREATE TABLE %s (k int PRIMARY KEY, v " + typeName + ")");
|
||||
|
||||
execute("INSERT INTO %s (k, v) VALUES (?, {a: ?, b: ?})", 0, 0, "abc");
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", "abc")));
|
||||
|
||||
execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", 0, "b", "abc"));
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", "abc")));
|
||||
|
||||
execute("INSERT INTO %s (k, v) VALUES (?, {a: ?, b: ?})", 0, 0, null);
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", null)));
|
||||
|
||||
execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", null, "b", "abc"));
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", null, "b", "abc")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateNonFrozenUDT() throws Throwable
|
||||
{
|
||||
String typeName = createType("CREATE TYPE %s (a int, b text)");
|
||||
createTable("CREATE TABLE %s (k int PRIMARY KEY, v " + typeName + ")");
|
||||
|
||||
execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", 0, "b", "abc"));
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", "abc")));
|
||||
|
||||
// overwrite the whole UDT
|
||||
execute("UPDATE %s SET v = ? WHERE k = ?", userType("a", 1, "b", "def"), 0);
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 1, "b", "def")));
|
||||
|
||||
execute("UPDATE %s SET v = ? WHERE k = ?", userType("a", 0, "b", null), 0);
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", null)));
|
||||
|
||||
execute("UPDATE %s SET v = ? WHERE k = ?", userType("a", null, "b", "abc"), 0);
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", null, "b", "abc")));
|
||||
|
||||
// individually set fields to non-null values
|
||||
execute("UPDATE %s SET v.a = ? WHERE k = ?", 1, 0);
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 1, "b", "abc")));
|
||||
|
||||
execute("UPDATE %s SET v.b = ? WHERE k = ?", "def", 0);
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 1, "b", "def")));
|
||||
|
||||
execute("UPDATE %s SET v.a = ?, v.b = ? WHERE k = ?", 0, "abc", 0);
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", "abc")));
|
||||
|
||||
execute("UPDATE %s SET v.b = ?, v.a = ? WHERE k = ?", "abc", 0, 0);
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", "abc")));
|
||||
|
||||
// individually set fields to null values
|
||||
execute("UPDATE %s SET v.a = ? WHERE k = ?", null, 0);
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", null, "b", "abc")));
|
||||
|
||||
execute("UPDATE %s SET v.a = ? WHERE k = ?", 0, 0);
|
||||
execute("UPDATE %s SET v.b = ? WHERE k = ?", null, 0);
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", null)));
|
||||
|
||||
execute("UPDATE %s SET v.a = ? WHERE k = ?", null, 0);
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, null));
|
||||
|
||||
assertInvalid("UPDATE %s SET v.bad = ? FROM %s WHERE k = ?", 0, 0);
|
||||
assertInvalid("UPDATE %s SET v = ? FROM %s WHERE k = ?", 0, 0);
|
||||
assertInvalid("UPDATE %s SET v = ? FROM %s WHERE k = ?", userType("a", 1, "b", "abc", "bad", 123), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteNonFrozenUDT() throws Throwable
|
||||
{
|
||||
String typeName = createType("CREATE TYPE %s (a int, b text)");
|
||||
createTable("CREATE TABLE %s (k int PRIMARY KEY, v " + typeName + ")");
|
||||
|
||||
execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", 0, "b", "abc"));
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", "abc")));
|
||||
|
||||
execute("DELETE v.b FROM %s WHERE k = ?", 0);
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", 0, "b", null)));
|
||||
|
||||
execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", 0, "b", "abc"));
|
||||
execute("DELETE v.a FROM %s WHERE k = ?", 0);
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, userType("a", null, "b", "abc")));
|
||||
|
||||
execute("DELETE v.b FROM %s WHERE k = ?", 0);
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, null));
|
||||
|
||||
// delete both fields at once
|
||||
execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", 0, "b", "abc"));
|
||||
execute("DELETE v.a, v.b FROM %s WHERE k = ?", 0);
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, null));
|
||||
|
||||
// same, but reverse field order
|
||||
execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", 0, "b", "abc"));
|
||||
execute("DELETE v.b, v.a FROM %s WHERE k = ?", 0);
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, null));
|
||||
|
||||
// delete the whole thing at once
|
||||
execute("INSERT INTO %s (k, v) VALUES (?, ?)", 0, userType("a", 0, "b", "abc"));
|
||||
execute("DELETE v FROM %s WHERE k = ?", 0);
|
||||
assertRows(execute("SELECT * FROM %s WHERE k = ?", 0), row(0, null));
|
||||
|
||||
assertInvalid("DELETE v.bad FROM %s WHERE k = ?", 0);
|
||||
}
|
||||
|
||||
private String typeWithKs(String type1)
|
||||
{
|
||||
return keyspace() + '.' + type1;
|
||||
|
|
|
|||
|
|
@ -384,6 +384,377 @@ public class InsertUpdateIfConditionTest extends CQLTester
|
|||
assertRows(execute("INSERT INTO %s (partition, key, owner) VALUES ('a', 'c', 'x') IF NOT EXISTS"), row(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWholeUDT() throws Throwable
|
||||
{
|
||||
String typename = createType("CREATE TYPE %s (a int, b text)");
|
||||
String myType = KEYSPACE + '.' + typename;
|
||||
|
||||
for (boolean frozen : new boolean[] {false, true})
|
||||
{
|
||||
createTable(String.format("CREATE TABLE %%s (k int PRIMARY KEY, v %s)",
|
||||
frozen
|
||||
? "frozen<" + myType + ">"
|
||||
: myType));
|
||||
|
||||
Object v = userType("a", 0, "b", "abc");
|
||||
execute("INSERT INTO %s (k, v) VALUES (0, ?)", v);
|
||||
|
||||
checkAppliesUDT("v = {a: 0, b: 'abc'}", v);
|
||||
checkAppliesUDT("v != null", v);
|
||||
checkAppliesUDT("v != {a: 1, b: 'abc'}", v);
|
||||
checkAppliesUDT("v != {a: 0, b: 'def'}", v);
|
||||
checkAppliesUDT("v > {a: -1, b: 'abc'}", v);
|
||||
checkAppliesUDT("v > {a: 0, b: 'aaa'}", v);
|
||||
checkAppliesUDT("v > {a: 0}", v);
|
||||
checkAppliesUDT("v >= {a: 0, b: 'aaa'}", v);
|
||||
checkAppliesUDT("v >= {a: 0, b: 'abc'}", v);
|
||||
checkAppliesUDT("v < {a: 0, b: 'zzz'}", v);
|
||||
checkAppliesUDT("v < {a: 1, b: 'abc'}", v);
|
||||
checkAppliesUDT("v < {a: 1}", v);
|
||||
checkAppliesUDT("v <= {a: 0, b: 'zzz'}", v);
|
||||
checkAppliesUDT("v <= {a: 0, b: 'abc'}", v);
|
||||
checkAppliesUDT("v IN (null, {a: 0, b: 'abc'}, {a: 1})", v);
|
||||
|
||||
// multiple conditions
|
||||
checkAppliesUDT("v > {a: -1, b: 'abc'} AND v > {a: 0}", v);
|
||||
checkAppliesUDT("v != null AND v IN ({a: 0, b: 'abc'})", v);
|
||||
|
||||
// should not apply
|
||||
checkDoesNotApplyUDT("v = {a: 0, b: 'def'}", v);
|
||||
checkDoesNotApplyUDT("v = {a: 1, b: 'abc'}", v);
|
||||
checkDoesNotApplyUDT("v = null", v);
|
||||
checkDoesNotApplyUDT("v != {a: 0, b: 'abc'}", v);
|
||||
checkDoesNotApplyUDT("v > {a: 1, b: 'abc'}", v);
|
||||
checkDoesNotApplyUDT("v > {a: 0, b: 'zzz'}", v);
|
||||
checkDoesNotApplyUDT("v >= {a: 1, b: 'abc'}", v);
|
||||
checkDoesNotApplyUDT("v >= {a: 0, b: 'zzz'}", v);
|
||||
checkDoesNotApplyUDT("v < {a: -1, b: 'abc'}", v);
|
||||
checkDoesNotApplyUDT("v < {a: 0, b: 'aaa'}", v);
|
||||
checkDoesNotApplyUDT("v <= {a: -1, b: 'abc'}", v);
|
||||
checkDoesNotApplyUDT("v <= {a: 0, b: 'aaa'}", v);
|
||||
checkDoesNotApplyUDT("v IN ({a: 0}, {b: 'abc'}, {a: 0, b: 'def'}, null)", v);
|
||||
checkDoesNotApplyUDT("v IN ()", v);
|
||||
|
||||
// multiple conditions
|
||||
checkDoesNotApplyUDT("v IN () AND v IN ({a: 0, b: 'abc'})", v);
|
||||
checkDoesNotApplyUDT("v > {a: 0, b: 'aaa'} AND v < {a: 0, b: 'aaa'}", v);
|
||||
|
||||
// invalid conditions
|
||||
checkInvalidUDT("v = {a: 1, b: 'abc', c: 'foo'}", v, InvalidRequestException.class);
|
||||
checkInvalidUDT("v = {foo: 'foo'}", v, InvalidRequestException.class);
|
||||
checkInvalidUDT("v < {a: 1, b: 'abc', c: 'foo'}", v, InvalidRequestException.class);
|
||||
checkInvalidUDT("v < null", v, InvalidRequestException.class);
|
||||
checkInvalidUDT("v <= {a: 1, b: 'abc', c: 'foo'}", v, InvalidRequestException.class);
|
||||
checkInvalidUDT("v <= null", v, InvalidRequestException.class);
|
||||
checkInvalidUDT("v > {a: 1, b: 'abc', c: 'foo'}", v, InvalidRequestException.class);
|
||||
checkInvalidUDT("v > null", v, InvalidRequestException.class);
|
||||
checkInvalidUDT("v >= {a: 1, b: 'abc', c: 'foo'}", v, InvalidRequestException.class);
|
||||
checkInvalidUDT("v >= null", v, InvalidRequestException.class);
|
||||
checkInvalidUDT("v IN null", v, SyntaxException.class);
|
||||
checkInvalidUDT("v IN 367", v, SyntaxException.class);
|
||||
checkInvalidUDT("v CONTAINS KEY 123", v, SyntaxException.class);
|
||||
checkInvalidUDT("v CONTAINS 'bar'", v, SyntaxException.class);
|
||||
|
||||
|
||||
/////////////////// null suffix on stored udt ////////////////////
|
||||
v = userType("a", 0, "b", null);
|
||||
execute("INSERT INTO %s (k, v) VALUES (0, ?)", v);
|
||||
|
||||
checkAppliesUDT("v = {a: 0}", v);
|
||||
checkAppliesUDT("v = {a: 0, b: null}", v);
|
||||
checkAppliesUDT("v != null", v);
|
||||
checkAppliesUDT("v != {a: 1, b: null}", v);
|
||||
checkAppliesUDT("v != {a: 1}", v);
|
||||
checkAppliesUDT("v != {a: 0, b: 'def'}", v);
|
||||
checkAppliesUDT("v > {a: -1, b: 'abc'}", v);
|
||||
checkAppliesUDT("v > {a: -1}", v);
|
||||
checkAppliesUDT("v >= {a: 0}", v);
|
||||
checkAppliesUDT("v >= {a: -1, b: 'abc'}", v);
|
||||
checkAppliesUDT("v < {a: 0, b: 'zzz'}", v);
|
||||
checkAppliesUDT("v < {a: 1, b: 'abc'}", v);
|
||||
checkAppliesUDT("v < {a: 1}", v);
|
||||
checkAppliesUDT("v <= {a: 0, b: 'zzz'}", v);
|
||||
checkAppliesUDT("v <= {a: 0}", v);
|
||||
checkAppliesUDT("v IN (null, {a: 0, b: 'abc'}, {a: 0})", v);
|
||||
|
||||
// multiple conditions
|
||||
checkAppliesUDT("v > {a: -1, b: 'abc'} AND v >= {a: 0}", v);
|
||||
checkAppliesUDT("v != null AND v IN ({a: 0}, {a: 0, b: null})", v);
|
||||
|
||||
// should not apply
|
||||
checkDoesNotApplyUDT("v = {a: 0, b: 'def'}", v);
|
||||
checkDoesNotApplyUDT("v = {a: 1}", v);
|
||||
checkDoesNotApplyUDT("v = {b: 'abc'}", v);
|
||||
checkDoesNotApplyUDT("v = null", v);
|
||||
checkDoesNotApplyUDT("v != {a: 0}", v);
|
||||
checkDoesNotApplyUDT("v != {a: 0, b: null}", v);
|
||||
checkDoesNotApplyUDT("v > {a: 1, b: 'abc'}", v);
|
||||
checkDoesNotApplyUDT("v > {a: 0}", v);
|
||||
checkDoesNotApplyUDT("v >= {a: 1, b: 'abc'}", v);
|
||||
checkDoesNotApplyUDT("v >= {a: 1}", v);
|
||||
checkDoesNotApplyUDT("v < {a: -1, b: 'abc'}", v);
|
||||
checkDoesNotApplyUDT("v < {a: -1}", v);
|
||||
checkDoesNotApplyUDT("v < {a: 0}", v);
|
||||
checkDoesNotApplyUDT("v <= {a: -1, b: 'abc'}", v);
|
||||
checkDoesNotApplyUDT("v <= {a: -1}", v);
|
||||
checkDoesNotApplyUDT("v IN ({a: 1}, {b: 'abc'}, {a: 0, b: 'def'}, null)", v);
|
||||
checkDoesNotApplyUDT("v IN ()", v);
|
||||
|
||||
// multiple conditions
|
||||
checkDoesNotApplyUDT("v IN () AND v IN ({a: 0})", v);
|
||||
checkDoesNotApplyUDT("v > {a: -1} AND v < {a: 0}", v);
|
||||
|
||||
|
||||
/////////////////// null prefix on stored udt ////////////////////
|
||||
v = userType("a", null, "b", "abc");
|
||||
execute("INSERT INTO %s (k, v) VALUES (0, ?)", v);
|
||||
|
||||
checkAppliesUDT("v = {a: null, b: 'abc'}", v);
|
||||
checkAppliesUDT("v = {b: 'abc'}", v);
|
||||
checkAppliesUDT("v != null", v);
|
||||
checkAppliesUDT("v != {a: 0, b: 'abc'}", v);
|
||||
checkAppliesUDT("v != {a: 0}", v);
|
||||
checkAppliesUDT("v != {b: 'def'}", v);
|
||||
checkAppliesUDT("v > {a: null, b: 'aaa'}", v);
|
||||
checkAppliesUDT("v > {b: 'aaa'}", v);
|
||||
checkAppliesUDT("v >= {a: null, b: 'aaa'}", v);
|
||||
checkAppliesUDT("v >= {b: 'abc'}", v);
|
||||
checkAppliesUDT("v < {a: null, b: 'zzz'}", v);
|
||||
checkAppliesUDT("v < {a: 0, b: 'abc'}", v);
|
||||
checkAppliesUDT("v < {a: 0}", v);
|
||||
checkAppliesUDT("v < {b: 'zzz'}", v);
|
||||
checkAppliesUDT("v <= {a: null, b: 'zzz'}", v);
|
||||
checkAppliesUDT("v <= {a: 0}", v);
|
||||
checkAppliesUDT("v <= {b: 'abc'}", v);
|
||||
checkAppliesUDT("v IN (null, {a: null, b: 'abc'}, {a: 0})", v);
|
||||
checkAppliesUDT("v IN (null, {a: 0, b: 'abc'}, {b: 'abc'})", v);
|
||||
|
||||
// multiple conditions
|
||||
checkAppliesUDT("v > {b: 'aaa'} AND v >= {b: 'abc'}", v);
|
||||
checkAppliesUDT("v != null AND v IN ({a: 0}, {a: null, b: 'abc'})", v);
|
||||
|
||||
// should not apply
|
||||
checkDoesNotApplyUDT("v = {a: 0, b: 'def'}", v);
|
||||
checkDoesNotApplyUDT("v = {a: 1}", v);
|
||||
checkDoesNotApplyUDT("v = {b: 'def'}", v);
|
||||
checkDoesNotApplyUDT("v = null", v);
|
||||
checkDoesNotApplyUDT("v != {b: 'abc'}", v);
|
||||
checkDoesNotApplyUDT("v != {a: null, b: 'abc'}", v);
|
||||
checkDoesNotApplyUDT("v > {a: 1, b: 'abc'}", v);
|
||||
checkDoesNotApplyUDT("v > {a: null, b: 'zzz'}", v);
|
||||
checkDoesNotApplyUDT("v > {b: 'zzz'}", v);
|
||||
checkDoesNotApplyUDT("v >= {a: null, b: 'zzz'}", v);
|
||||
checkDoesNotApplyUDT("v >= {a: 1}", v);
|
||||
checkDoesNotApplyUDT("v >= {b: 'zzz'}", v);
|
||||
checkDoesNotApplyUDT("v < {a: null, b: 'aaa'}", v);
|
||||
checkDoesNotApplyUDT("v < {b: 'aaa'}", v);
|
||||
checkDoesNotApplyUDT("v <= {a: null, b: 'aaa'}", v);
|
||||
checkDoesNotApplyUDT("v <= {b: 'aaa'}", v);
|
||||
checkDoesNotApplyUDT("v IN ({a: 1}, {a: 1, b: 'abc'}, {a: null, b: 'def'}, null)", v);
|
||||
checkDoesNotApplyUDT("v IN ()", v);
|
||||
|
||||
// multiple conditions
|
||||
checkDoesNotApplyUDT("v IN () AND v IN ({b: 'abc'})", v);
|
||||
checkDoesNotApplyUDT("v IN () AND v IN ({a: null, b: 'abc'})", v);
|
||||
checkDoesNotApplyUDT("v > {a: -1} AND v < {a: 0}", v);
|
||||
|
||||
|
||||
/////////////////// null udt ////////////////////
|
||||
v = null;
|
||||
execute("INSERT INTO %s (k, v) VALUES (0, ?)", v);
|
||||
|
||||
checkAppliesUDT("v = null", v);
|
||||
checkAppliesUDT("v IN (null, {a: null, b: 'abc'}, {a: 0})", v);
|
||||
checkAppliesUDT("v IN (null, {a: 0, b: 'abc'}, {b: 'abc'})", v);
|
||||
|
||||
// multiple conditions
|
||||
checkAppliesUDT("v = null AND v IN (null, {a: 0}, {a: null, b: 'abc'})", v);
|
||||
|
||||
// should not apply
|
||||
checkDoesNotApplyUDT("v = {a: 0, b: 'def'}", v);
|
||||
checkDoesNotApplyUDT("v = {a: 1}", v);
|
||||
checkDoesNotApplyUDT("v = {b: 'def'}", v);
|
||||
checkDoesNotApplyUDT("v != null", v);
|
||||
checkDoesNotApplyUDT("v > {a: 1, b: 'abc'}", v);
|
||||
checkDoesNotApplyUDT("v > {a: null, b: 'zzz'}", v);
|
||||
checkDoesNotApplyUDT("v > {b: 'zzz'}", v);
|
||||
checkDoesNotApplyUDT("v >= {a: null, b: 'zzz'}", v);
|
||||
checkDoesNotApplyUDT("v >= {a: 1}", v);
|
||||
checkDoesNotApplyUDT("v >= {b: 'zzz'}", v);
|
||||
checkDoesNotApplyUDT("v < {a: null, b: 'aaa'}", v);
|
||||
checkDoesNotApplyUDT("v < {b: 'aaa'}", v);
|
||||
checkDoesNotApplyUDT("v <= {a: null, b: 'aaa'}", v);
|
||||
checkDoesNotApplyUDT("v <= {b: 'aaa'}", v);
|
||||
checkDoesNotApplyUDT("v IN ({a: 1}, {a: 1, b: 'abc'}, {a: null, b: 'def'})", v);
|
||||
checkDoesNotApplyUDT("v IN ()", v);
|
||||
|
||||
// multiple conditions
|
||||
checkDoesNotApplyUDT("v IN () AND v IN ({b: 'abc'})", v);
|
||||
checkDoesNotApplyUDT("v > {a: -1} AND v < {a: 0}", v);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUDTField() throws Throwable
|
||||
{
|
||||
String typename = createType("CREATE TYPE %s (a int, b text)");
|
||||
String myType = KEYSPACE + '.' + typename;
|
||||
|
||||
for (boolean frozen : new boolean[] {false, true})
|
||||
{
|
||||
createTable(String.format("CREATE TABLE %%s (k int PRIMARY KEY, v %s)",
|
||||
frozen
|
||||
? "frozen<" + myType + ">"
|
||||
: myType));
|
||||
|
||||
Object v = userType("a", 0, "b", "abc");
|
||||
execute("INSERT INTO %s (k, v) VALUES (0, ?)", v);
|
||||
|
||||
checkAppliesUDT("v.a = 0", v);
|
||||
checkAppliesUDT("v.b = 'abc'", v);
|
||||
checkAppliesUDT("v.a < 1", v);
|
||||
checkAppliesUDT("v.b < 'zzz'", v);
|
||||
checkAppliesUDT("v.b <= 'bar'", v);
|
||||
checkAppliesUDT("v.b > 'aaa'", v);
|
||||
checkAppliesUDT("v.b >= 'abc'", v);
|
||||
checkAppliesUDT("v.a != -1", v);
|
||||
checkAppliesUDT("v.b != 'xxx'", v);
|
||||
checkAppliesUDT("v.a != null", v);
|
||||
checkAppliesUDT("v.b != null", v);
|
||||
checkAppliesUDT("v.a IN (null, 0, 1)", v);
|
||||
checkAppliesUDT("v.b IN (null, 'xxx', 'abc')", v);
|
||||
checkAppliesUDT("v.b > 'aaa' AND v.b < 'zzz'", v);
|
||||
checkAppliesUDT("v.a = 0 AND v.b > 'aaa'", v);
|
||||
|
||||
// do not apply
|
||||
checkDoesNotApplyUDT("v.a = -1", v);
|
||||
checkDoesNotApplyUDT("v.b = 'xxx'", v);
|
||||
checkDoesNotApplyUDT("v.a < -1", v);
|
||||
checkDoesNotApplyUDT("v.b < 'aaa'", v);
|
||||
checkDoesNotApplyUDT("v.b <= 'aaa'", v);
|
||||
checkDoesNotApplyUDT("v.b > 'zzz'", v);
|
||||
checkDoesNotApplyUDT("v.b >= 'zzz'", v);
|
||||
checkDoesNotApplyUDT("v.a != 0", v);
|
||||
checkDoesNotApplyUDT("v.b != 'abc'", v);
|
||||
checkDoesNotApplyUDT("v.a IN (null, -1)", v);
|
||||
checkDoesNotApplyUDT("v.b IN (null, 'xxx')", v);
|
||||
checkDoesNotApplyUDT("v.a IN ()", v);
|
||||
checkDoesNotApplyUDT("v.b IN ()", v);
|
||||
checkDoesNotApplyUDT("v.b != null AND v.b IN ()", v);
|
||||
|
||||
// invalid
|
||||
checkInvalidUDT("v.c = null", v, InvalidRequestException.class);
|
||||
checkInvalidUDT("v.a < null", v, InvalidRequestException.class);
|
||||
checkInvalidUDT("v.a <= null", v, InvalidRequestException.class);
|
||||
checkInvalidUDT("v.a > null", v, InvalidRequestException.class);
|
||||
checkInvalidUDT("v.a >= null", v, InvalidRequestException.class);
|
||||
checkInvalidUDT("v.a IN null", v, SyntaxException.class);
|
||||
checkInvalidUDT("v.a IN 367", v, SyntaxException.class);
|
||||
checkInvalidUDT("v.b IN (1, 2, 3)", v, InvalidRequestException.class);
|
||||
checkInvalidUDT("v.a CONTAINS 367", v, SyntaxException.class);
|
||||
checkInvalidUDT("v.a CONTAINS KEY 367", v, SyntaxException.class);
|
||||
|
||||
|
||||
/////////////// null suffix on udt ////////////////
|
||||
v = userType("a", 0, "b", null);
|
||||
execute("INSERT INTO %s (k, v) VALUES (0, ?)", v);
|
||||
|
||||
checkAppliesUDT("v.a = 0", v);
|
||||
checkAppliesUDT("v.b = null", v);
|
||||
checkAppliesUDT("v.b != 'xxx'", v);
|
||||
checkAppliesUDT("v.a != null", v);
|
||||
checkAppliesUDT("v.a IN (null, 0, 1)", v);
|
||||
checkAppliesUDT("v.b IN (null, 'xxx', 'abc')", v);
|
||||
checkAppliesUDT("v.a = 0 AND v.b = null", v);
|
||||
|
||||
// do not apply
|
||||
checkDoesNotApplyUDT("v.b = 'abc'", v);
|
||||
checkDoesNotApplyUDT("v.a < -1", v);
|
||||
checkDoesNotApplyUDT("v.b < 'aaa'", v);
|
||||
checkDoesNotApplyUDT("v.b <= 'aaa'", v);
|
||||
checkDoesNotApplyUDT("v.b > 'zzz'", v);
|
||||
checkDoesNotApplyUDT("v.b >= 'zzz'", v);
|
||||
checkDoesNotApplyUDT("v.a != 0", v);
|
||||
checkDoesNotApplyUDT("v.b != null", v);
|
||||
checkDoesNotApplyUDT("v.a IN (null, -1)", v);
|
||||
checkDoesNotApplyUDT("v.b IN ('xxx', 'abc')", v);
|
||||
checkDoesNotApplyUDT("v.a IN ()", v);
|
||||
checkDoesNotApplyUDT("v.b IN ()", v);
|
||||
checkDoesNotApplyUDT("v.b != null AND v.b IN ()", v);
|
||||
|
||||
|
||||
/////////////// null prefix on udt ////////////////
|
||||
v = userType("a", null, "b", "abc");
|
||||
execute("INSERT INTO %s (k, v) VALUES (0, ?)", v);
|
||||
|
||||
checkAppliesUDT("v.a = null", v);
|
||||
checkAppliesUDT("v.b = 'abc'", v);
|
||||
checkAppliesUDT("v.a != 0", v);
|
||||
checkAppliesUDT("v.b != null", v);
|
||||
checkAppliesUDT("v.a IN (null, 0, 1)", v);
|
||||
checkAppliesUDT("v.b IN (null, 'xxx', 'abc')", v);
|
||||
checkAppliesUDT("v.a = null AND v.b = 'abc'", v);
|
||||
|
||||
// do not apply
|
||||
checkDoesNotApplyUDT("v.a = 0", v);
|
||||
checkDoesNotApplyUDT("v.a < -1", v);
|
||||
checkDoesNotApplyUDT("v.b >= 'zzz'", v);
|
||||
checkDoesNotApplyUDT("v.a != null", v);
|
||||
checkDoesNotApplyUDT("v.b != 'abc'", v);
|
||||
checkDoesNotApplyUDT("v.a IN (-1, 0)", v);
|
||||
checkDoesNotApplyUDT("v.b IN (null, 'xxx')", v);
|
||||
checkDoesNotApplyUDT("v.a IN ()", v);
|
||||
checkDoesNotApplyUDT("v.b IN ()", v);
|
||||
checkDoesNotApplyUDT("v.b != null AND v.b IN ()", v);
|
||||
|
||||
|
||||
/////////////// null udt ////////////////
|
||||
v = null;
|
||||
execute("INSERT INTO %s (k, v) VALUES (0, ?)", v);
|
||||
|
||||
checkAppliesUDT("v.a = null", v);
|
||||
checkAppliesUDT("v.b = null", v);
|
||||
checkAppliesUDT("v.a != 0", v);
|
||||
checkAppliesUDT("v.b != 'abc'", v);
|
||||
checkAppliesUDT("v.a IN (null, 0, 1)", v);
|
||||
checkAppliesUDT("v.b IN (null, 'xxx', 'abc')", v);
|
||||
checkAppliesUDT("v.a = null AND v.b = null", v);
|
||||
|
||||
// do not apply
|
||||
checkDoesNotApplyUDT("v.a = 0", v);
|
||||
checkDoesNotApplyUDT("v.a < -1", v);
|
||||
checkDoesNotApplyUDT("v.b >= 'zzz'", v);
|
||||
checkDoesNotApplyUDT("v.a != null", v);
|
||||
checkDoesNotApplyUDT("v.b != null", v);
|
||||
checkDoesNotApplyUDT("v.a IN (-1, 0)", v);
|
||||
checkDoesNotApplyUDT("v.b IN ('xxx', 'abc')", v);
|
||||
checkDoesNotApplyUDT("v.a IN ()", v);
|
||||
checkDoesNotApplyUDT("v.b IN ()", v);
|
||||
checkDoesNotApplyUDT("v.b != null AND v.b IN ()", v);
|
||||
}
|
||||
}
|
||||
|
||||
void checkAppliesUDT(String condition, Object value) throws Throwable
|
||||
{
|
||||
assertRows(execute("UPDATE %s SET v = ? WHERE k = 0 IF " + condition, value), row(true));
|
||||
assertRows(execute("SELECT * FROM %s"), row(0, value));
|
||||
}
|
||||
|
||||
void checkDoesNotApplyUDT(String condition, Object value) throws Throwable
|
||||
{
|
||||
assertRows(execute("UPDATE %s SET v = ? WHERE k = 0 IF " + condition, value),
|
||||
row(false, value));
|
||||
assertRows(execute("SELECT * FROM %s"), row(0, value));
|
||||
}
|
||||
|
||||
void checkInvalidUDT(String condition, Object value, Class<? extends Throwable> expected) throws Throwable
|
||||
{
|
||||
assertInvalidThrow(expected, "UPDATE %s SET v = ? WHERE k = 0 IF " + condition, value);
|
||||
assertRows(execute("SELECT * FROM %s"), row(0, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrated from cql_tests.py:TestCQL.whole_list_conditional_test()
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ public class LegacySchemaMigratorTest
|
|||
}
|
||||
|
||||
// make sure that we've read *exactly* the same set of keyspaces/tables/types/functions
|
||||
assertEquals(expected, actual);
|
||||
assertEquals(expected.diff(actual).toString(), expected, actual);
|
||||
|
||||
// check that the build status of all indexes has been updated to use the new
|
||||
// format of index name: the index_name column of system.IndexInfo used to
|
||||
|
|
@ -311,17 +311,20 @@ public class LegacySchemaMigratorTest
|
|||
UserType udt1 = new UserType(keyspace,
|
||||
bytes("udt1"),
|
||||
new ArrayList<ByteBuffer>() {{ add(bytes("col1")); add(bytes("col2")); }},
|
||||
new ArrayList<AbstractType<?>>() {{ add(UTF8Type.instance); add(Int32Type.instance); }});
|
||||
new ArrayList<AbstractType<?>>() {{ add(UTF8Type.instance); add(Int32Type.instance); }},
|
||||
true);
|
||||
|
||||
UserType udt2 = new UserType(keyspace,
|
||||
bytes("udt2"),
|
||||
new ArrayList<ByteBuffer>() {{ add(bytes("col3")); add(bytes("col4")); }},
|
||||
new ArrayList<AbstractType<?>>() {{ add(BytesType.instance); add(BooleanType.instance); }});
|
||||
new ArrayList<AbstractType<?>>() {{ add(BytesType.instance); add(BooleanType.instance); }},
|
||||
true);
|
||||
|
||||
UserType udt3 = new UserType(keyspace,
|
||||
bytes("udt3"),
|
||||
new ArrayList<ByteBuffer>() {{ add(bytes("col5")); }},
|
||||
new ArrayList<AbstractType<?>>() {{ add(AsciiType.instance); }});
|
||||
new ArrayList<AbstractType<?>>() {{ add(AsciiType.instance); }},
|
||||
true);
|
||||
|
||||
return KeyspaceMetadata.create(keyspace,
|
||||
KeyspaceParams.simple(1),
|
||||
|
|
@ -431,12 +434,14 @@ public class LegacySchemaMigratorTest
|
|||
UserType udt1 = new UserType(keyspace,
|
||||
bytes("udt1"),
|
||||
new ArrayList<ByteBuffer>() {{ add(bytes("col1")); add(bytes("col2")); }},
|
||||
new ArrayList<AbstractType<?>>() {{ add(UTF8Type.instance); add(Int32Type.instance); }});
|
||||
new ArrayList<AbstractType<?>>() {{ add(UTF8Type.instance); add(Int32Type.instance); }},
|
||||
true);
|
||||
|
||||
UserType udt2 = new UserType(keyspace,
|
||||
bytes("udt2"),
|
||||
new ArrayList<ByteBuffer>() {{ add(bytes("col1")); add(bytes("col2")); }},
|
||||
new ArrayList<AbstractType<?>>() {{ add(ListType.getInstance(udt1, false)); add(Int32Type.instance); }});
|
||||
new ArrayList<AbstractType<?>>() {{ add(ListType.getInstance(udt1, false)); add(Int32Type.instance); }},
|
||||
true);
|
||||
|
||||
UDFunction udf1 = UDFunction.create(new FunctionName(keyspace, "udf"),
|
||||
ImmutableList.of(new ColumnIdentifier("col1", false), new ColumnIdentifier("col2", false)),
|
||||
|
|
@ -478,12 +483,14 @@ public class LegacySchemaMigratorTest
|
|||
UserType udt1 = new UserType(keyspace,
|
||||
bytes("udt1"),
|
||||
new ArrayList<ByteBuffer>() {{ add(bytes("col1")); add(bytes("col2")); }},
|
||||
new ArrayList<AbstractType<?>>() {{ add(UTF8Type.instance); add(Int32Type.instance); }});
|
||||
new ArrayList<AbstractType<?>>() {{ add(UTF8Type.instance); add(Int32Type.instance); }},
|
||||
true);
|
||||
|
||||
UserType udt2 = new UserType(keyspace,
|
||||
bytes("udt2"),
|
||||
new ArrayList<ByteBuffer>() {{ add(bytes("col1")); add(bytes("col2")); }},
|
||||
new ArrayList<AbstractType<?>>() {{ add(ListType.getInstance(udt1, false)); add(Int32Type.instance); }});
|
||||
new ArrayList<AbstractType<?>>() {{ add(ListType.getInstance(udt1, false)); add(Int32Type.instance); }},
|
||||
true);
|
||||
|
||||
UDFunction udf1 = UDFunction.create(new FunctionName(keyspace, "udf1"),
|
||||
ImmutableList.of(new ColumnIdentifier("col1", false), new ColumnIdentifier("col2", false)),
|
||||
|
|
|
|||
|
|
@ -184,7 +184,8 @@ public class SerDeserTest
|
|||
UserType udt = new UserType("ks",
|
||||
bb("myType"),
|
||||
Arrays.asList(bb("f1"), bb("f2"), bb("f3"), bb("f4")),
|
||||
Arrays.asList(LongType.instance, lt, st, mt));
|
||||
Arrays.asList(LongType.instance, lt, st, mt),
|
||||
true);
|
||||
|
||||
Map<ColumnIdentifier, Term.Raw> value = new HashMap<>();
|
||||
value.put(ci("f1"), lit(42));
|
||||
|
|
|
|||
Loading…
Reference in New Issue