Merge branch 'cassandra-3.11' into trunk
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/c6cd8246 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/c6cd8246 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/c6cd8246 Branch: refs/heads/trunk Commit: c6cd8246280acde5e2244d8960b2d5c17353424f Parents: 7d4d1a3 cb2a1c8 Author: Jason Brown <jasedbr...@gmail.com> Authored: Tue Sep 12 14:14:54 2017 -0700 Committer: Jason Brown <jasedbr...@gmail.com> Committed: Tue Sep 12 14:18:00 2017 -0700 ---------------------------------------------------------------------- CHANGES.txt | 1 + src/java/org/apache/cassandra/cql3/Tuples.java | 28 ++++++++++++++------ .../cql3/validation/entities/TupleTypeTest.java | 17 +++++++++++- 3 files changed, 37 insertions(+), 9 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/c6cd8246/CHANGES.txt ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/c6cd8246/src/java/org/apache/cassandra/cql3/Tuples.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/cql3/Tuples.java index bae756a,01f3466..317e192 --- a/src/java/org/apache/cassandra/cql3/Tuples.java +++ b/src/java/org/apache/cassandra/cql3/Tuples.java @@@ -68,12 -65,7 +68,12 @@@ public class Tuple public Term prepare(String keyspace, ColumnSpecification receiver) throws InvalidRequestException { - validateAssignableTo(keyspace, receiver); + // The parser cannot differentiate between a tuple with one element and a term between parenthesis. + // By consequence, we need to wait until we know the target type to determine which one it is. - if (elements.size() == 1 && !(receiver.type instanceof TupleType)) ++ if (elements.size() == 1 && !checkIfTupleType(receiver.type)) + return elements.get(0).prepare(keyspace, receiver); + + validateTupleAssignableTo(receiver, elements); List<Term> values = new ArrayList<>(elements.size()); boolean allTerminal = true; @@@ -110,14 -102,38 +110,14 @@@ return allTerminal ? value.bind(QueryOptions.DEFAULT) : value; } - private void validateAssignableTo(String keyspace, ColumnSpecification receiver) throws InvalidRequestException - { - if (!checkIfTupleType(receiver.type)) - throw new InvalidRequestException(String.format("Invalid tuple type literal for %s of type %s", receiver.name, receiver.type.asCQL3Type())); - - TupleType tt = getTupleType(receiver.type); - 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())); - } - - Term.Raw value = elements.get(i); - ColumnSpecification spec = componentSpecOf(receiver, i); - if (!value.testAssignment(keyspace, spec).isAssignable()) - throw new InvalidRequestException(String.format("Invalid tuple literal for %s: component %d is not of type %s", receiver.name, i, spec.type.asCQL3Type())); - } - } - public AssignmentTestable.TestResult testAssignment(String keyspace, ColumnSpecification receiver) { - try - { - validateAssignableTo(keyspace, receiver); - return AssignmentTestable.TestResult.WEAKLY_ASSIGNABLE; - } - catch (InvalidRequestException e) - { - return AssignmentTestable.TestResult.NOT_ASSIGNABLE; - } + // The parser cannot differentiate between a tuple with one element and a term between parenthesis. + // By consequence, we need to wait until we know the target type to determine which one it is. - if (elements.size() == 1 && !(receiver.type instanceof TupleType)) ++ if (elements.size() == 1 && !checkIfTupleType(receiver.type)) + return elements.get(0).testAssignment(keyspace, receiver); + + return testTupleAssignment(receiver, elements); } @Override @@@ -420,100 -436,29 +420,112 @@@ } } - public static String tupleToString(List<?> items) + /** + * Create a <code>String</code> representation of the tuple containing the specified elements. + * + * @param elements the tuple elements + * @return a <code>String</code> representation of the tuple + */ + public static String tupleToString(List<?> elements) + { + return tupleToString(elements, Object::toString); + } + + /** + * Create a <code>String</code> representation of the tuple from the specified items associated to + * the tuples elements. + * + * @param items items associated to the tuple elements + * @param mapper the mapper used to map the items to the <code>String</code> representation of the tuple elements + * @return a <code>String</code> representation of the tuple + */ + public static <T> String tupleToString(Iterable<T> items, java.util.function.Function<T, String> mapper) + { + return StreamSupport.stream(items.spliterator(), false) + .map(e -> mapper.apply(e)) + .collect(Collectors.joining(", ", "(", ")")); + } + + /** + * Returns the exact TupleType from the items if it can be known. + * + * @param items the items mapped to the tuple elements + * @param mapper the mapper used to retrieve the element types from the items + * @return the exact TupleType from the items if it can be known or <code>null</code> + */ + public static <T> AbstractType<?> getExactTupleTypeIfKnown(List<T> items, + java.util.function.Function<T, AbstractType<?>> mapper) + { + List<AbstractType<?>> types = new ArrayList<>(items.size()); + for (T item : items) + { + AbstractType<?> type = mapper.apply(item); + if (type == null) + return null; + types.add(type); + } + return new TupleType(types); + } + + /** + * Checks if the tuple with the specified elements can be assigned to the specified column. + * + * @param receiver the receiving column + * @param elements the tuple elements + * @throws InvalidRequestException if the tuple cannot be assigned to the specified column. + */ + public static void validateTupleAssignableTo(ColumnSpecification receiver, + List<? extends AssignmentTestable> elements) { - if (!(receiver.type instanceof TupleType)) ++ if (!checkIfTupleType(receiver.type)) + throw invalidRequest("Invalid tuple type literal for %s of type %s", receiver.name, receiver.type.asCQL3Type()); - TupleType tt = (TupleType)receiver.type; - StringBuilder sb = new StringBuilder("("); - for (int i = 0; i < items.size(); i++) ++ TupleType tt = getTupleType(receiver.type); + for (int i = 0; i < elements.size(); i++) + { + if (i >= tt.size()) + { + throw invalidRequest("Invalid tuple literal for %s: too many elements. Type %s expects %d but got %d", + receiver.name, tt.asCQL3Type(), tt.size(), elements.size()); + } + + AssignmentTestable value = elements.get(i); + ColumnSpecification spec = componentSpecOf(receiver, i); + if (!value.testAssignment(receiver.ksName, spec).isAssignable()) + throw invalidRequest("Invalid tuple literal for %s: component %d is not of type %s", + receiver.name, i, spec.type.asCQL3Type()); + } + } + + /** + * Tests that the tuple with the specified elements can be assigned to the specified column. + * + * @param receiver the receiving column + * @param elements the tuple elements + */ + public static AssignmentTestable.TestResult testTupleAssignment(ColumnSpecification receiver, + List<? extends AssignmentTestable> elements) + { + try + { + validateTupleAssignableTo(receiver, elements); + return AssignmentTestable.TestResult.WEAKLY_ASSIGNABLE; + } + catch (InvalidRequestException e) { - sb.append(items.get(i)); - if (i < items.size() - 1) - sb.append(", "); + return AssignmentTestable.TestResult.NOT_ASSIGNABLE; } - sb.append(')'); - return sb.toString(); } + + public static boolean checkIfTupleType(AbstractType<?> tuple) + { + return (tuple instanceof TupleType) || + (tuple instanceof ReversedType && ((ReversedType) tuple).baseType instanceof TupleType); + + } + + public static TupleType getTupleType(AbstractType<?> tuple) + { + return (tuple instanceof ReversedType ? ((TupleType) ((ReversedType) tuple).baseType) : (TupleType)tuple); + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/c6cd8246/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java ---------------------------------------------------------------------- diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java index 4dc8d18,bace751..28430cb --- a/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/TupleTypeTest.java @@@ -213,10 -217,12 +217,21 @@@ public class TupleTypeTest extends CQLT } @Test + public void testTupleModification() throws Throwable + { + createTable("CREATE TABLE %s(pk int PRIMARY KEY, value tuple<int, int>)"); + assertInvalidMessage("Invalid operation (value = value + (1, 1)) for tuple column value", + "UPDATE %s SET value += (1, 1) WHERE k=0;"); + } - } ++ ++ @Test + public void testReversedTypeTuple() throws Throwable + { + // CASSANDRA-13717 + createTable("CREATE TABLE %s (id int, tdemo frozen<tuple<timestamp, varchar>>, primary key (id, tdemo)) with clustering order by (tdemo desc)"); + execute("INSERT INTO %s (id, tdemo) VALUES (1, ('2017-02-03 03:05+0000','Europe'))"); + DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mmX", Locale.ENGLISH); + assertRows(execute("SELECT tdemo FROM %s"), row(tuple( df.parse("2017-02-03 03:05+0000"), "Europe"))); + } + } ++ --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org For additional commands, e-mail: commits-h...@cassandra.apache.org