Merge branch cassandra-3.0 into cassandra-3.11
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/b5df3b4c Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/b5df3b4c Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/b5df3b4c Branch: refs/heads/cassandra-3.11 Commit: b5df3b4c367c2927f309ca16a540b7be83636556 Parents: 7afb97c e51c85f Author: Benjamin Lerer <b.le...@gmail.com> Authored: Fri Jul 7 13:36:52 2017 +0200 Committer: Benjamin Lerer <b.le...@gmail.com> Committed: Fri Jul 7 13:38:22 2017 +0200 ---------------------------------------------------------------------- CHANGES.txt | 3 +- .../org/apache/cassandra/cql3/UserTypes.java | 12 +--- .../apache/cassandra/db/marshal/TupleType.java | 45 ++++--------- .../apache/cassandra/db/marshal/UserType.java | 51 +++++---------- .../cassandra/serializers/TupleSerializer.java | 64 +++++++++++++++++++ .../serializers/UserTypeSerializer.java | 67 ++++++++++++++++++++ .../validation/entities/CollectionsTest.java | 36 +++++++++++ .../cql3/validation/entities/TupleTypeTest.java | 10 ++- .../cql3/validation/entities/UserTypesTest.java | 15 ++++- 9 files changed, 223 insertions(+), 80 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/b5df3b4c/CHANGES.txt ---------------------------------------------------------------------- diff --cc CHANGES.txt index 096dfa4,c98ffd9..679d55b --- a/CHANGES.txt +++ b/CHANGES.txt @@@ -1,51 -1,12 +1,52 @@@ -3.0.15 +3.11.1 + * Properly evict pstmts from prepared statements cache (CASSANDRA-13641) +Merged from 3.0: * Allow different NUMACTL_ARGS to be passed in (CASSANDRA-13557) - * Allow native function calls in CQLSSTableWriter (CASSANDRA-12606) * Fix secondary index queries on COMPACT tables (CASSANDRA-13627) * Nodetool listsnapshots output is missing a newline, if there are no snapshots (CASSANDRA-13568) - - Merged from 2.2: -* Fix nested Tuples/UDTs validation (CASSANDRA-13646) ++Merged from 2.2: ++ * Fix nested Tuples/UDTs validation (CASSANDRA-13646) -3.0.14 +3.11.0 + * Allow native function calls in CQLSSTableWriter (CASSANDRA-12606) + * Replace string comparison with regex/number checks in MessagingService test (CASSANDRA-13216) + * Fix formatting of duration columns in CQLSH (CASSANDRA-13549) + * Fix the problem with duplicated rows when using paging with SASI (CASSANDRA-13302) + * Allow CONTAINS statements filtering on the partition key and itâs parts (CASSANDRA-13275) + * Fall back to even ranges calculation in clusters with vnodes when tokens are distributed unevenly (CASSANDRA-13229) + * Fix duration type validation to prevent overflow (CASSANDRA-13218) + * Forbid unsupported creation of SASI indexes over partition key columns (CASSANDRA-13228) + * Reject multiple values for a key in CQL grammar. (CASSANDRA-13369) + * UDA fails without input rows (CASSANDRA-13399) + * Fix compaction-stress by using daemonInitialization (CASSANDRA-13188) + * V5 protocol flags decoding broken (CASSANDRA-13443) + * Use write lock not read lock for removing sstables from compaction strategies. (CASSANDRA-13422) + * Use corePoolSize equal to maxPoolSize in JMXEnabledThreadPoolExecutors (CASSANDRA-13329) + * Avoid rebuilding SASI indexes containing no values (CASSANDRA-12962) + * Add charset to Analyser input stream (CASSANDRA-13151) + * Fix testLimitSSTables flake caused by concurrent flush (CASSANDRA-12820) + * cdc column addition strikes again (CASSANDRA-13382) + * Fix static column indexes (CASSANDRA-13277) + * DataOutputBuffer.asNewBuffer broken (CASSANDRA-13298) + * unittest CipherFactoryTest failed on MacOS (CASSANDRA-13370) + * Forbid SELECT restrictions and CREATE INDEX over non-frozen UDT columns (CASSANDRA-13247) + * Default logging we ship will incorrectly print "?:?" for "%F:%L" pattern (CASSANDRA-13317) + * Possible AssertionError in UnfilteredRowIteratorWithLowerBound (CASSANDRA-13366) + * Support unaligned memory access for AArch64 (CASSANDRA-13326) + * Improve SASI range iterator efficiency on intersection with an empty range (CASSANDRA-12915). + * Fix equality comparisons of columns using the duration type (CASSANDRA-13174) + * Obfuscate password in stress-graphs (CASSANDRA-12233) + * Move to FastThreadLocalThread and FastThreadLocal (CASSANDRA-13034) + * nodetool stopdaemon errors out (CASSANDRA-13030) + * Tables in system_distributed should not use gcgs of 0 (CASSANDRA-12954) + * Fix primary index calculation for SASI (CASSANDRA-12910) + * More fixes to the TokenAllocator (CASSANDRA-12990) + * NoReplicationTokenAllocator should work with zero replication factor (CASSANDRA-12983) + * Address message coalescing regression (CASSANDRA-12676) + * Delete illegal character from StandardTokenizerImpl.jflex (CASSANDRA-13417) + * Fix cqlsh automatic protocol downgrade regression (CASSANDRA-13307) + * Tracing payload not passed from QueryMessage to tracing session (CASSANDRA-12835) +Merged from 3.0: * Ensure int overflow doesn't occur when calculating large partition warning size (CASSANDRA-13172) * Ensure consistent view of partition columns between coordinator and replica in ColumnFilter (CASSANDRA-13004) * Failed unregistering mbean during drop keyspace (CASSANDRA-13346) http://git-wip-us.apache.org/repos/asf/cassandra/blob/b5df3b4c/src/java/org/apache/cassandra/cql3/UserTypes.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/cql3/UserTypes.java index e867179,68a0513..4edd27f --- a/src/java/org/apache/cassandra/cql3/UserTypes.java +++ b/src/java/org/apache/cassandra/cql3/UserTypes.java @@@ -95,8 -88,8 +95,8 @@@ public abstract class UserType 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())); + throw new InvalidRequestException(String.format("Invalid user type literal for %s of type %s", receiver.name, receiver.type.asCQL3Type())); UserType ut = (UserType)receiver.type; for (int i = 0; i < ut.size(); i++) @@@ -108,10 -101,7 +108,10 @@@ 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.name, 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())); ++ receiver.name, field, fieldSpec.type.asCQL3Type())); + } } } @@@ -150,52 -135,7 +150,46 @@@ } } - // 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)); - } - ++ type.validate(bytes); + return new Value(type, type.split(bytes)); + } + + public ByteBuffer get(ProtocolVersion 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; http://git-wip-us.apache.org/repos/asf/cassandra/blob/b5df3b4c/src/java/org/apache/cassandra/db/marshal/TupleType.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/db/marshal/TupleType.java index 5a90ae9,3af1054..034ad53 --- a/src/java/org/apache/cassandra/db/marshal/TupleType.java +++ b/src/java/org/apache/cassandra/db/marshal/TupleType.java @@@ -41,29 -37,26 +41,41 @@@ import org.apache.cassandra.utils.ByteB */ public class TupleType extends AbstractType<ByteBuffer> { + private static final String COLON = ":"; + private static final Pattern COLON_PAT = Pattern.compile(COLON); + private static final String ESCAPED_COLON = "\\\\:"; + private static final Pattern ESCAPED_COLON_PAT = Pattern.compile(ESCAPED_COLON); + private static final String AT = "@"; + private static final Pattern AT_PAT = Pattern.compile(AT); + private static final String ESCAPED_AT = "\\\\@"; + private static final Pattern ESCAPED_AT_PAT = Pattern.compile(ESCAPED_AT); + protected final List<AbstractType<?>> types; + private final TupleSerializer serializer; + 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; + this.serializer = new TupleSerializer(fieldSerializers(types)); + } + + private static List<TypeSerializer<?>> fieldSerializers(List<AbstractType<?>> types) + { + int size = types.size(); + List<TypeSerializer<?>> serializers = new ArrayList<>(size); + for (int i = 0; i < size; i++) + serializers.add(types.get(i).getSerializer()); + return serializers; } public static TupleType getInstance(TypeParser parser) throws ConfigurationException, SyntaxException @@@ -133,55 -120,13 +145,24 @@@ 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; + } + + while (bb2.remaining() > 0) + { + int size = bb2.getInt(); + if (size > 0) // non-null + return -1; + } - // bb1.remaining() > 0 && bb2.remaining() == 0 - return 1; + return 0; } - @Override - public void validate(ByteBuffer bytes) throws MarshalException - { - ByteBuffer input = bytes.duplicate(); - for (int i = 0; i < size(); i++) - { - // we allow the input to have less fields than declared so as to support field addition. - if (!input.hasRemaining()) - return; - - if (input.remaining() < Integer.BYTES) - throw new MarshalException(String.format("Not enough bytes to read size of %dth component", i)); - - int size = input.getInt(); - - // size < 0 means null value - if (size < 0) - continue; - - if (input.remaining() < size) - throw new MarshalException(String.format("Not enough bytes to read %dth component", i)); - - ByteBuffer field = ByteBufferUtil.readBytes(input, size); - types.get(i).validate(field); - } - - // We're allowed to get less fields than declared, but not more - if (input.hasRemaining()) - throw new MarshalException("Invalid remaining data after end of tuple value"); - } - /** * Split a tuple value into its component values. */ http://git-wip-us.apache.org/repos/asf/cassandra/blob/b5df3b4c/src/java/org/apache/cassandra/db/marshal/UserType.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/db/marshal/UserType.java index a59000a,b91dbf8..febd91c --- a/src/java/org/apache/cassandra/db/marshal/UserType.java +++ b/src/java/org/apache/cassandra/db/marshal/UserType.java @@@ -29,11 -28,10 +29,13 @@@ import org.apache.cassandra.db.rows.Cel import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.exceptions.SyntaxException; import org.apache.cassandra.serializers.MarshalException; +import org.apache.cassandra.transport.ProtocolVersion; + import org.apache.cassandra.serializers.TypeSerializer; + import org.apache.cassandra.serializers.UserTypeSerializer; import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.Pair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A user defined type. @@@ -42,26 -40,36 +44,33 @@@ */ 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<FieldIdentifier> fieldNames; private final List<String> stringFieldNames; + private final boolean isMultiCell; + private final UserTypeSerializer serializer; - public UserType(String keyspace, ByteBuffer name, List<ByteBuffer> fieldNames, List<AbstractType<?>> fieldTypes) + public UserType(String keyspace, ByteBuffer name, List<FieldIdentifier> 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 (FieldIdentifier fieldName : fieldNames) - stringFieldNames.add(fieldName.toString()); + LinkedHashMap<String , TypeSerializer<?>> fieldSerializers = new LinkedHashMap<>(fieldTypes.size()); + for (int i = 0, m = fieldNames.size(); i < m; i++) + { - ByteBuffer fieldName = fieldNames.get(i); - try - { - String stringFieldName = ByteBufferUtil.string(fieldName, StandardCharsets.UTF_8); - stringFieldNames.add(stringFieldName); - fieldSerializers.put(stringFieldName, fieldTypes.get(i).getSerializer()); - } - catch (CharacterCodingException ex) - { - throw new AssertionError("Got non-UTF8 field name for user-defined type: " + ByteBufferUtil.bytesToHex(fieldName), ex); - } ++ String stringFieldName = fieldNames.get(i).toString(); ++ stringFieldNames.add(stringFieldName); ++ fieldSerializers.put(stringFieldName, fieldTypes.get(i).getSerializer()); + } + this.serializer = new UserTypeSerializer(fieldSerializers); } public static UserType getInstance(TypeParser parser) throws ConfigurationException, SyntaxException @@@ -128,94 -117,6 +137,62 @@@ return UTF8Type.instance.compose(name); } + public int fieldPosition(FieldIdentifier fieldName) + { + return fieldNames.indexOf(fieldName); + } + + public CellPath cellPathForField(FieldIdentifier 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((short)fieldPosition(fieldName))); + } + + public ShortType nameComparator() + { + return ShortType.instance; + } + + public ByteBuffer serializeForNativeProtocol(Iterator<Cell> cells, ProtocolVersion 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); + } + + public void validateCell(Cell cell) throws MarshalException + { + if (isMultiCell) + { + ByteBuffer path = cell.path().get(0); + nameComparator().validate(path); + Short fieldPosition = nameComparator().getSerializer().deserialize(path); + fieldType(fieldPosition).validate(cell.value()); + } + else + { + validate(cell.value()); + } + } + - // 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 - { - ByteBuffer input = bytes.duplicate(); - for (int i = 0; i < size(); i++) - { - // we allow the input to have less fields than declared so as to support field addition. - if (!input.hasRemaining()) - return; - - if (input.remaining() < 4) - throw new MarshalException(String.format("Not enough bytes to read size of %dth field %s", i, fieldNameAsString(i))); - - int size = input.getInt(); - - // size < 0 means null value - if (size < 0) - continue; - - if (input.remaining() < size) - throw new MarshalException(String.format("Not enough bytes to read %dth field %s", i, fieldNameAsString(i))); - - ByteBuffer field = ByteBufferUtil.readBytes(input, size); - types.get(i).validate(field); - } - - // We're allowed to get less fields than declared, but not more - if (input.hasRemaining()) - throw new MarshalException("Invalid remaining data after end of UDT value"); - } - @Override public Term fromJSONObject(Object parsed) throws MarshalException { @@@ -395,35 -222,14 +372,41 @@@ } @Override + public boolean referencesDuration() + { + return fieldTypes().stream().anyMatch(f -> f.referencesDuration()); + } + + @Override public String toString() { - return getClass().getName() + TypeParser.stringifyUserTypeParameters(keyspace, name, fieldNames, types); + return this.toString(false); + } + + @Override + public boolean isTuple() + { + return 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(); } + + @Override + public TypeSerializer<ByteBuffer> getSerializer() + { + return serializer; + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/b5df3b4c/src/java/org/apache/cassandra/serializers/TupleSerializer.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/serializers/TupleSerializer.java index 0000000,7cf71c6..65e6654 mode 000000,100644..100644 --- a/src/java/org/apache/cassandra/serializers/TupleSerializer.java +++ b/src/java/org/apache/cassandra/serializers/TupleSerializer.java @@@ -1,0 -1,66 +1,64 @@@ + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.apache.cassandra.serializers; + + import java.nio.ByteBuffer; -import java.util.HashMap; + import java.util.List; -import java.util.Map; + + import org.apache.cassandra.utils.ByteBufferUtil; + + public class TupleSerializer extends BytesSerializer + { + public final List<TypeSerializer<?>> fields; + + public TupleSerializer(List<TypeSerializer<?>> fields) + { + this.fields = fields; + } + + @Override + public void validate(ByteBuffer bytes) throws MarshalException + { + ByteBuffer input = bytes.duplicate(); + for (int i = 0; i < fields.size(); i++) + { + // we allow the input to have less fields than declared so as to support field addition. + if (!input.hasRemaining()) + return; + - if (input.remaining() < 4) ++ if (input.remaining() < Integer.BYTES) + throw new MarshalException(String.format("Not enough bytes to read size of %dth component", i)); + + int size = input.getInt(); + + // size < 0 means null value + if (size < 0) + continue; + + if (input.remaining() < size) + throw new MarshalException(String.format("Not enough bytes to read %dth component", i)); + + ByteBuffer field = ByteBufferUtil.readBytes(input, size); + fields.get(i).validate(field); + } + + // We're allowed to get less fields than declared, but not more + if (input.hasRemaining()) + throw new MarshalException("Invalid remaining data after end of tuple value"); + } + } http://git-wip-us.apache.org/repos/asf/cassandra/blob/b5df3b4c/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/b5df3b4c/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java ---------------------------------------------------------------------- diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java index e295d82,c279e00..b78eb3e --- a/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java @@@ -52,10 -50,19 +52,19 @@@ public class UserTypesTest extends CQLT { String myType = createType("CREATE TYPE %s (f int)"); createTable("CREATE TABLE %s(pk int PRIMARY KEY, t frozen<" + myType + ">)"); - assertInvalidMessage("Not enough bytes to read 0th component", + assertInvalidMessage("Not enough bytes to read 0th field f", "INSERT INTO %s (pk, t) VALUES (?, ?)", 1, "test"); - assertInvalidMessage("Not enough bytes to read 0th component", + assertInvalidMessage("Not enough bytes to read 0th field f", "INSERT INTO %s (pk, t) VALUES (?, ?)", 1, Long.MAX_VALUE); + + String type = createType("CREATE TYPE %s (a int, b tuple<int, text, double>)"); + createTable("CREATE TABLE %s (k int PRIMARY KEY, t frozen<" + type + ">)"); + assertInvalidMessage("Invalid remaining data after end of tuple value", + "INSERT INTO %s (k, t) VALUES (0, ?)", - userType(1, tuple(1, "1", 1.0, 1))); ++ userType("a", 1, "b", tuple(1, "1", 1.0, 1))); + + assertInvalidMessage("Invalid user type literal for t: field b is not of type frozen<tuple<int, text, double>>", + "INSERT INTO %s (k, t) VALUES (0, {a: 1, b: (1, '1', 1.0, 1)})"); } @Test @@@ -128,44 -117,8 +137,44 @@@ " 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", ++ assertInvalidMessage("Invalid remaining data after end of UDT value", + "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 --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org For additional commands, e-mail: commits-h...@cassandra.apache.org