/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.cassandra.cql3.selection; import java.util.*; import java.util.stream.Collectors; import org.apache.commons.lang3.text.StrBuilder; import org.apache.cassandra.cql3.*; import org.apache.cassandra.cql3.functions.*; import org.apache.cassandra.cql3.selection.Selector.Factory; import org.apache.cassandra.db.marshal.*; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.schema.ColumnMetadata; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.utils.Pair; import static org.apache.cassandra.cql3.selection.SelectorFactories.createFactoriesAndCollectColumnDefinitions; import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest; public interface Selectable extends AssignmentTestable { public Selector.Factory newSelectorFactory(TableMetadata table, AbstractType<?> expectedType, List<ColumnMetadata> defs, VariableSpecifications boundNames); /** * The type of the {@code Selectable} if it can be infered. * * @param keyspace the keyspace on which the statement for which this is a * {@code Selectable} is on. * @return the type of this {@code Selectable} if inferrable, or {@code null} * otherwise (for instance, the type isn't inferable for a bind marker. Even for * literals, the exact type is not inferrable since they are valid for many * different types and so this will return {@code null} too). */ public AbstractType<?> getExactTypeIfKnown(String keyspace); // Term.Raw overrides this since some literals can be WEAKLY_ASSIGNABLE default public TestResult testAssignment(String keyspace, ColumnSpecification receiver) { AbstractType<?> type = getExactTypeIfKnown(keyspace); return type == null ? TestResult.NOT_ASSIGNABLE : type.testAssignment(keyspace, receiver); } default int addAndGetIndex(ColumnMetadata def, List<ColumnMetadata> l) { int idx = l.indexOf(def); if (idx < 0) { idx = l.size(); l.add(def); } return idx; } public static abstract class Raw { public abstract Selectable prepare(TableMetadata table); /** * Returns true if any processing is performed on the selected column. **/ public boolean processesSelection() { // ColumnIdentifier is the only case that returns false and override this return true; } } public static class WithTerm implements Selectable { /** * The names given to unamed bind markers found in selection. In selection clause, we often don't have a good * name for bind markers, typically if you have: * SELECT (int)? FROM foo; * there isn't a good name for that marker. So we give the same name to all the markers. Note that we could try * to differenciate the names by using some increasing number in the name (so [selection_1], [selection_2], ...) * but it's actually not trivial to do in the current code and it's not really more helpful since if users wants * to bind by position (which they will have to in this case), they can do so at the driver level directly. And * so we don't bother. * Note that users should really be using named bind markers if they want to be able to bind by names. */ private static final ColumnIdentifier bindMarkerNameInSelection = new ColumnIdentifier("[selection]", true); private final Term.Raw rawTerm; public WithTerm(Term.Raw rawTerm) { this.rawTerm = rawTerm; } @Override public TestResult testAssignment(String keyspace, ColumnSpecification receiver) { return rawTerm.testAssignment(keyspace, receiver); } public Selector.Factory newSelectorFactory(TableMetadata table, AbstractType<?> expectedType, List<ColumnMetadata> defs, VariableSpecifications boundNames) throws InvalidRequestException { /* * expectedType will be null if we have no constraint on what the type should be. For instance, if this term is a bind marker: * - it will be null if we do "SELECT ? FROM foo" * - it won't be null (and be LongType) if we do "SELECT bigintAsBlob(?) FROM foo" because the function constrain it. * * In the first case, we have to error out: we need to infer the type of the metadata of a SELECT at preparation time, which we can't * here (users will have to do "SELECT (varint)? FROM foo" for instance). * But in the 2nd case, we're fine and can use the expectedType to "prepare" the bind marker/collect the bound type. * * Further, the term might not be a bind marker, in which case we sometimes can default to some most-general type. For instance, in * SELECT 3 FROM foo * we'll just default the type to 'varint' as that's the most generic type for the literal '3' (this is mostly for convenience, the query * is not terribly useful in practice and use can force the type as for the bind marker case through "SELECT (int)3 FROM foo"). * But note that not all literals can have such default type. For instance, there is no way to infer the type of a UDT literal in a vacuum, * and so we simply error out if we have something like: * SELECT { foo: 'bar' } FROM foo * * Lastly, note that if the term is a terminal literal, we don't have to check it's compatibility with 'expectedType' as any incompatibility * would have been found at preparation time. */ AbstractType<?> type = getExactTypeIfKnown(table.keyspace); if (type == null) { type = expectedType; if (type == null) throw new InvalidRequestException("Cannot infer type for term " + this + " in selection clause (try using a cast to force a type)"); } // The fact we default the name to "[selection]" inconditionally means that any bind marker in a // selection will have this name. Which isn't terribly helpful, but it's unclear how to provide // something a lot more helpful and in practice user can bind those markers by position or, even better, // use bind markers. Term term = rawTerm.prepare(table.keyspace, new ColumnSpecification(table.keyspace, table.name, bindMarkerNameInSelection, type)); term.collectMarkerSpecification(boundNames); return TermSelector.newFactory(rawTerm.getText(), term, type); } @Override public AbstractType<?> getExactTypeIfKnown(String keyspace) { return rawTerm.getExactTypeIfKnown(keyspace); } @Override public String toString() { return rawTerm.getText(); } public static class Raw extends Selectable.Raw { private final Term.Raw term; public Raw(Term.Raw term) { this.term = term; } public Selectable prepare(TableMetadata table) { return new WithTerm(term); } } } public static class WritetimeOrTTL implements Selectable { public final ColumnMetadata column; public final boolean isWritetime; public WritetimeOrTTL(ColumnMetadata column, boolean isWritetime) { this.column = column; this.isWritetime = isWritetime; } @Override public String toString() { return (isWritetime ? "writetime" : "ttl") + "(" + column.name + ")"; } public Selector.Factory newSelectorFactory(TableMetadata table, AbstractType<?> expectedType, List<ColumnMetadata> defs, VariableSpecifications boundNames) { if (column.isPrimaryKeyColumn()) throw new InvalidRequestException( String.format("Cannot use selection function %s on PRIMARY KEY part %s", isWritetime ? "writeTime" : "ttl", column.name)); if (column.type.isCollection()) throw new InvalidRequestException(String.format("Cannot use selection function %s on collections", isWritetime ? "writeTime" : "ttl")); return WritetimeOrTTLSelector.newFactory(column, addAndGetIndex(column, defs), isWritetime); } public AbstractType<?> getExactTypeIfKnown(String keyspace) { return isWritetime ? LongType.instance : Int32Type.instance; } public static class Raw extends Selectable.Raw { private final ColumnMetadata.Raw id; private final boolean isWritetime; public Raw(ColumnMetadata.Raw id, boolean isWritetime) { this.id = id; this.isWritetime = isWritetime; } public WritetimeOrTTL prepare(TableMetadata table) { return new WritetimeOrTTL(id.prepare(table), isWritetime); } } } public static class WithFunction implements Selectable { public final Function function; public final List<Selectable> args; public WithFunction(Function function, List<Selectable> args) { this.function = function; this.args = args; } @Override public String toString() { return function.columnName(args.stream().map(Object::toString).collect(Collectors.toList())); } public Selector.Factory newSelectorFactory(TableMetadata table, AbstractType<?> expectedType, List<ColumnMetadata> defs, VariableSpecifications boundNames) { SelectorFactories factories = SelectorFactories.createFactoriesAndCollectColumnDefinitions(args, function.argTypes(), table, defs, boundNames); return AbstractFunctionSelector.newFactory(function, factories); } public AbstractType<?> getExactTypeIfKnown(String keyspace) { return function.returnType(); } public static class Raw extends Selectable.Raw { private final FunctionName functionName; private final List<Selectable.Raw> args; public Raw(FunctionName functionName, List<Selectable.Raw> args) { this.functionName = functionName; this.args = args; } public static Raw newCountRowsFunction() { return new Raw(AggregateFcts.countRowsFunction.name(), Collections.emptyList()); } public static Raw newOperation(char operator, Selectable.Raw left, Selectable.Raw right) { return new Raw(OperationFcts.getFunctionNameFromOperator(operator), Arrays.asList(left, right)); } public static Raw newNegation(Selectable.Raw arg) { return new Raw(FunctionName.nativeFunction(OperationFcts.NEGATION_FUNCTION_NAME), Collections.singletonList(arg)); } public Selectable prepare(TableMetadata table) { List<Selectable> preparedArgs = new ArrayList<>(args.size()); for (Selectable.Raw arg : args) preparedArgs.add(arg.prepare(table)); FunctionName name = functionName; // We need to circumvent the normal function lookup process for toJson() because instances of the function // are not pre-declared (because it can accept any type of argument). We also have to wait until we have the // selector factories of the argument so we can access their final type. if (functionName.equalsNativeFunction(ToJsonFct.NAME)) { return new WithToJSonFunction(preparedArgs); } // Also, COUNT(x) is equivalent to COUNT(*) for any non-null term x (since count(x) don't care about it's argument outside of check for nullness) and // for backward compatibilty we want to support COUNT(1), but we actually have COUNT(x) method for every existing (simple) input types so currently COUNT(1) // will throw as ambiguous (since 1 works for any type). So we have have to special case COUNT. else if (functionName.equalsNativeFunction(FunctionName.nativeFunction("count")) && preparedArgs.size() == 1 && (preparedArgs.get(0) instanceof WithTerm) && (((WithTerm)preparedArgs.get(0)).rawTerm instanceof Constants.Literal)) { // Note that 'null' isn't a Constants.Literal name = AggregateFcts.countRowsFunction.name(); preparedArgs = Collections.emptyList(); } Function fun = FunctionResolver.get(table.keyspace, name, preparedArgs, table.keyspace, table.name, null); if (fun == null) throw new InvalidRequestException(String.format("Unknown function '%s'", functionName)); if (fun.returnType() == null) throw new InvalidRequestException(String.format("Unknown function %s called in selection clause", functionName)); return new WithFunction(fun, preparedArgs); } } } public static class WithToJSonFunction implements Selectable { public final List<Selectable> args; private WithToJSonFunction(List<Selectable> args) { this.args = args; } @Override public String toString() { return new StrBuilder().append(ToJsonFct.NAME) .append("(") .appendWithSeparators(args, ", ") .append(")") .toString(); } public Selector.Factory newSelectorFactory(TableMetadata table, AbstractType<?> expectedType, List<ColumnMetadata> defs, VariableSpecifications boundNames) { SelectorFactories factories = SelectorFactories.createFactoriesAndCollectColumnDefinitions(args, null, table, defs, boundNames); Function fun = ToJsonFct.getInstance(factories.getReturnTypes()); return AbstractFunctionSelector.newFactory(fun, factories); } public AbstractType<?> getExactTypeIfKnown(String keyspace) { return UTF8Type.instance; } } public static class WithCast implements Selectable { private final CQL3Type type; private final Selectable arg; public WithCast(Selectable arg, CQL3Type type) { this.arg = arg; this.type = type; } @Override public String toString() { return String.format("cast(%s as %s)", arg, type.toString().toLowerCase()); } public Selector.Factory newSelectorFactory(TableMetadata table, AbstractType<?> expectedType, List<ColumnMetadata> defs, VariableSpecifications boundNames) { List<Selectable> args = Collections.singletonList(arg); SelectorFactories factories = SelectorFactories.createFactoriesAndCollectColumnDefinitions(args, null, table, defs, boundNames); Selector.Factory factory = factories.get(0); // If the user is trying to cast a type on its own type we simply ignore it. if (type.getType().equals(factory.getReturnType())) return factory; FunctionName name = FunctionName.nativeFunction(CastFcts.getFunctionName(type)); Function fun = FunctionResolver.get(table.keyspace, name, args, table.keyspace, table.name, null); if (fun == null) { throw new InvalidRequestException(String.format("%s cannot be cast to %s", defs.get(0).name, type)); } return AbstractFunctionSelector.newFactory(fun, factories); } public AbstractType<?> getExactTypeIfKnown(String keyspace) { return type.getType(); } public static class Raw extends Selectable.Raw { private final CQL3Type type; private final Selectable.Raw arg; public Raw(Selectable.Raw arg, CQL3Type type) { this.arg = arg; this.type = type; } public WithCast prepare(TableMetadata table) { return new WithCast(arg.prepare(table), type); } } } public static class WithFieldSelection implements Selectable { public final Selectable selected; public final FieldIdentifier field; public WithFieldSelection(Selectable selected, FieldIdentifier field) { this.selected = selected; this.field = field; } @Override public String toString() { return String.format("%s.%s", selected, field); } public Selector.Factory newSelectorFactory(TableMetadata table, AbstractType<?> expectedType, List<ColumnMetadata> defs, VariableSpecifications boundNames) { AbstractType<?> expectedUdtType = null; // If the UDT is between parentheses, we know that it is not a tuple with a single element. if (selected instanceof BetweenParenthesesOrWithTuple) { BetweenParenthesesOrWithTuple betweenParentheses = (BetweenParenthesesOrWithTuple) selected; expectedUdtType = betweenParentheses.selectables.get(0).getExactTypeIfKnown(table.keyspace); } Selector.Factory factory = selected.newSelectorFactory(table, expectedUdtType, defs, boundNames); AbstractType<?> type = factory.getColumnSpecification(table).type; if (!type.isUDT()) { throw new InvalidRequestException( String.format("Invalid field selection: %s of type %s is not a user type", selected, type.asCQL3Type())); } UserType ut = (UserType) type; int fieldIndex = ut.fieldPosition(field); if (fieldIndex == -1) { throw new InvalidRequestException(String.format("%s of type %s has no field %s", selected, type.asCQL3Type(), field)); } return FieldSelector.newFactory(ut, fieldIndex, factory); } public AbstractType<?> getExactTypeIfKnown(String keyspace) { AbstractType<?> selectedType = selected.getExactTypeIfKnown(keyspace); if (selectedType == null || !(selectedType instanceof UserType)) return null; UserType ut = (UserType) selectedType; int fieldIndex = ut.fieldPosition(field); if (fieldIndex == -1) return null; return ut.fieldType(fieldIndex); } public static class Raw extends Selectable.Raw { private final Selectable.Raw selected; private final FieldIdentifier field; public Raw(Selectable.Raw selected, FieldIdentifier field) { this.selected = selected; this.field = field; } public WithFieldSelection prepare(TableMetadata table) { return new WithFieldSelection(selected.prepare(table), field); } } } /** * {@code Selectable} for {@code Selectable} between parentheses or tuples. * <p>The parser cannot differentiate between a single element between parentheses or a single element tuple. * By consequence, we are forced to wait until the type is known to be able to differentiate them.</p> */ public static class BetweenParenthesesOrWithTuple implements Selectable { /** * The tuple elements or the element between the parentheses */ private final List<Selectable> selectables; public BetweenParenthesesOrWithTuple(List<Selectable> selectables) { this.selectables = selectables; } @Override public TestResult testAssignment(String keyspace, ColumnSpecification receiver) { if (selectables.size() == 1 && !receiver.type.isTuple()) return selectables.get(0).testAssignment(keyspace, receiver); return Tuples.testTupleAssignment(receiver, selectables); } @Override public Factory newSelectorFactory(TableMetadata cfm, AbstractType<?> expectedType, List<ColumnMetadata> defs, VariableSpecifications boundNames) { AbstractType<?> type = getExactTypeIfKnown(cfm.keyspace); if (type == null) { type = expectedType; if (type == null) throw invalidRequest("Cannot infer type for term %s in selection clause (try using a cast to force a type)", this); } if (selectables.size() == 1 && !type.isTuple()) return newBetweenParenthesesSelectorFactory(cfm, expectedType, defs, boundNames); return newTupleSelectorFactory(cfm, (TupleType) type, defs, boundNames); } private Factory newBetweenParenthesesSelectorFactory(TableMetadata cfm, AbstractType<?> expectedType, List<ColumnMetadata> defs, VariableSpecifications boundNames) { Selectable selectable = selectables.get(0); final Factory factory = selectable.newSelectorFactory(cfm, expectedType, defs, boundNames); return new ForwardingFactory() { protected Factory delegate() { return factory; } protected String getColumnName() { return String.format("(%s)", factory.getColumnName()); } }; } private Factory newTupleSelectorFactory(TableMetadata cfm, TupleType tupleType, List<ColumnMetadata> defs, VariableSpecifications boundNames) { SelectorFactories factories = createFactoriesAndCollectColumnDefinitions(selectables, tupleType.allTypes(), cfm, defs, boundNames); return TupleSelector.newFactory(tupleType, factories); } @Override public AbstractType<?> getExactTypeIfKnown(String keyspace) { // If there is only one element we cannot know if it is an element between parentheses or a tuple // with only one element. By consequence, we need to force the user to specify the type. if (selectables.size() == 1) return null; return Tuples.getExactTupleTypeIfKnown(selectables, p -> p.getExactTypeIfKnown(keyspace)); } @Override public String toString() { return Tuples.tupleToString(selectables); } public static class Raw extends Selectable.Raw { private final List<Selectable.Raw> raws; public Raw(List<Selectable.Raw> raws) { this.raws = raws; } public Selectable prepare(TableMetadata cfm) { return new BetweenParenthesesOrWithTuple(raws.stream().map(p -> p.prepare(cfm)).collect(Collectors.toList())); } } } /** * <code>Selectable</code> for literal Lists. */ public static class WithList implements Selectable { /** * The list elements */ private final List<Selectable> selectables; public WithList(List<Selectable> selectables) { this.selectables = selectables; } @Override public TestResult testAssignment(String keyspace, ColumnSpecification receiver) { return Lists.testListAssignment(receiver, selectables); } @Override public Factory newSelectorFactory(TableMetadata cfm, AbstractType<?> expectedType, List<ColumnMetadata> defs, VariableSpecifications boundNames) { AbstractType<?> type = getExactTypeIfKnown(cfm.keyspace); if (type == null) { type = expectedType; if (type == null) throw invalidRequest("Cannot infer type for term %s in selection clause (try using a cast to force a type)", this); } ListType<?> listType = (ListType<?>) type; List<AbstractType<?>> expectedTypes = new ArrayList<>(selectables.size()); for (int i = 0, m = selectables.size(); i < m; i++) expectedTypes.add(listType.getElementsType()); SelectorFactories factories = createFactoriesAndCollectColumnDefinitions(selectables, expectedTypes, cfm, defs, boundNames); return ListSelector.newFactory(type, factories); } @Override public AbstractType<?> getExactTypeIfKnown(String keyspace) { return Lists.getExactListTypeIfKnown(selectables, p -> p.getExactTypeIfKnown(keyspace)); } @Override public String toString() { return Lists.listToString(selectables); } public static class Raw extends Selectable.Raw { private final List<Selectable.Raw> raws; public Raw(List<Selectable.Raw> raws) { this.raws = raws; } public Selectable prepare(TableMetadata cfm) { return new WithList(raws.stream().map(p -> p.prepare(cfm)).collect(Collectors.toList())); } } } /** * <code>Selectable</code> for literal Sets. */ public static class WithSet implements Selectable { /** * The set elements */ private final List<Selectable> selectables; public WithSet(List<Selectable> selectables) { this.selectables = selectables; } @Override public TestResult testAssignment(String keyspace, ColumnSpecification receiver) { return Sets.testSetAssignment(receiver, selectables); } @Override public Factory newSelectorFactory(TableMetadata cfm, AbstractType<?> expectedType, List<ColumnMetadata> defs, VariableSpecifications boundNames) { AbstractType<?> type = getExactTypeIfKnown(cfm.keyspace); if (type == null) { type = expectedType; if (type == null) throw invalidRequest("Cannot infer type for term %s in selection clause (try using a cast to force a type)", this); } // The parser treats empty Maps as Sets so if the type is a MapType we know that the Map is empty if (type instanceof MapType) return MapSelector.newFactory(type, Collections.emptyList()); SetType<?> setType = (SetType<?>) type; if (setType.getElementsType() == DurationType.instance) throw invalidRequest("Durations are not allowed inside sets: %s", setType.asCQL3Type()); List<AbstractType<?>> expectedTypes = new ArrayList<>(selectables.size()); for (int i = 0, m = selectables.size(); i < m; i++) expectedTypes.add(setType.getElementsType()); SelectorFactories factories = createFactoriesAndCollectColumnDefinitions(selectables, expectedTypes, cfm, defs, boundNames); return SetSelector.newFactory(type, factories); } @Override public AbstractType<?> getExactTypeIfKnown(String keyspace) { return Sets.getExactSetTypeIfKnown(selectables, p -> p.getExactTypeIfKnown(keyspace)); } @Override public String toString() { return Sets.setToString(selectables); } public static class Raw extends Selectable.Raw { private final List<Selectable.Raw> raws; public Raw(List<Selectable.Raw> raws) { this.raws = raws; } public Selectable prepare(TableMetadata cfm) { return new WithSet(raws.stream().map(p -> p.prepare(cfm)).collect(Collectors.toList())); } } } /** * {@code Selectable} for literal Maps or UDTs. * <p>The parser cannot differentiate between a Map or a UDT in the selection cause because a * {@code ColumnMetadata} is equivalent to a {@code FieldIdentifier} from a syntax point of view. * By consequence, we are forced to wait until the type is known to be able to differentiate them.</p> */ public static class WithMapOrUdt implements Selectable { /** * The column family metadata. We need to store them to be able to build the proper data once the type has been * identified. */ private final TableMetadata cfm; /** * The Map or UDT raw elements. */ private final List<Pair<Selectable.Raw, Selectable.Raw>> raws; public WithMapOrUdt(TableMetadata cfm, List<Pair<Selectable.Raw, Selectable.Raw>> raws) { this.cfm = cfm; this.raws = raws; } @Override public TestResult testAssignment(String keyspace, ColumnSpecification receiver) { return receiver.type.isUDT() ? UserTypes.testUserTypeAssignment(receiver, getUdtFields((UserType) receiver.type)) : Maps.testMapAssignment(receiver, getMapEntries(cfm)); } @Override public Factory newSelectorFactory(TableMetadata cfm, AbstractType<?> expectedType, List<ColumnMetadata> defs, VariableSpecifications boundNames) { AbstractType<?> type = getExactTypeIfKnown(cfm.keyspace); if (type == null) { type = expectedType; if (type == null) throw invalidRequest("Cannot infer type for term %s in selection clause (try using a cast to force a type)", this); } if (type.isUDT()) return newUdtSelectorFactory(cfm, expectedType, defs, boundNames); return newMapSelectorFactory(cfm, defs, boundNames, type); } private Factory newMapSelectorFactory(TableMetadata cfm, List<ColumnMetadata> defs, VariableSpecifications boundNames, AbstractType<?> type) { MapType<?, ?> mapType = (MapType<?, ?>) type; if (mapType.getKeysType() == DurationType.instance) throw invalidRequest("Durations are not allowed as map keys: %s", mapType.asCQL3Type()); return MapSelector.newFactory(type, getMapEntries(cfm).stream() .map(p -> Pair.create(p.left.newSelectorFactory(cfm, mapType.getKeysType(), defs, boundNames), p.right.newSelectorFactory(cfm, mapType.getValuesType(), defs, boundNames))) .collect(Collectors.toList())); } private Factory newUdtSelectorFactory(TableMetadata cfm, AbstractType<?> expectedType, List<ColumnMetadata> defs, VariableSpecifications boundNames) { UserType ut = (UserType) expectedType; Map<FieldIdentifier, Factory> factories = new LinkedHashMap<>(ut.size()); for (Pair<Selectable.Raw, Selectable.Raw> raw : raws) { if (!(raw.left instanceof RawIdentifier)) throw invalidRequest("%s is not a valid field identifier of type %s ", raw.left, ut.getNameAsString()); FieldIdentifier fieldName = ((RawIdentifier) raw.left).toFieldIdentifier(); int fieldPosition = ut.fieldPosition(fieldName); if (fieldPosition == -1) throw invalidRequest("Unknown field '%s' in value of user defined type %s", fieldName, ut.getNameAsString()); AbstractType<?> fieldType = ut.fieldType(fieldPosition); factories.put(fieldName, raw.right.prepare(cfm).newSelectorFactory(cfm, fieldType, defs, boundNames)); } return UserTypeSelector.newFactory(expectedType, factories); } @Override public AbstractType<?> getExactTypeIfKnown(String keyspace) { // Lets force the user to specify the type. return null; } @Override public String toString() { return raws.stream() .map(p -> String.format("%s: %s", p.left instanceof RawIdentifier ? p.left : p.left.prepare(cfm), p.right.prepare(cfm))) .collect(Collectors.joining(", ", "{", "}")); } private List<Pair<Selectable, Selectable>> getMapEntries(TableMetadata cfm) { return raws.stream() .map(p -> Pair.create(p.left.prepare(cfm), p.right.prepare(cfm))) .collect(Collectors.toList()); } private Map<FieldIdentifier, Selectable> getUdtFields(UserType ut) { Map<FieldIdentifier, Selectable> fields = new LinkedHashMap<>(ut.size()); for (Pair<Selectable.Raw, Selectable.Raw> raw : raws) { if (!(raw.left instanceof RawIdentifier)) throw invalidRequest("%s is not a valid field identifier of type %s ", raw.left, ut.getNameAsString()); FieldIdentifier fieldName = ((RawIdentifier) raw.left).toFieldIdentifier(); int fieldPosition = ut.fieldPosition(fieldName); if (fieldPosition == -1) throw invalidRequest("Unknown field '%s' in value of user defined type %s", fieldName, ut.getNameAsString()); fields.put(fieldName, raw.right.prepare(cfm)); } return fields; } public static class Raw extends Selectable.Raw { private final List<Pair<Selectable.Raw, Selectable.Raw>> raws; public Raw(List<Pair<Selectable.Raw, Selectable.Raw>> raws) { this.raws = raws; } public Selectable prepare(TableMetadata cfm) { return new WithMapOrUdt(cfm, raws); } } } /** * <code>Selectable</code> for type hints (e.g. (int) ?). */ public static class WithTypeHint implements Selectable { /** * The name of the type as specified in the query. */ private final String typeName; /** * The type specified by the hint. */ private final AbstractType<?> type; /** * The selectable to which the hint should be applied. */ private final Selectable selectable; public WithTypeHint(String typeName, AbstractType<?> type, Selectable selectable) { this.typeName = typeName; this.type = type; this.selectable = selectable; } @Override public TestResult testAssignment(String keyspace, ColumnSpecification receiver) { if (receiver.type.equals(type)) return AssignmentTestable.TestResult.EXACT_MATCH; else if (receiver.type.isValueCompatibleWith(type)) return AssignmentTestable.TestResult.WEAKLY_ASSIGNABLE; else return AssignmentTestable.TestResult.NOT_ASSIGNABLE; } @Override public Factory newSelectorFactory(TableMetadata cfm, AbstractType<?> expectedType, List<ColumnMetadata> defs, VariableSpecifications boundNames) { final ColumnSpecification receiver = new ColumnSpecification(cfm.keyspace, cfm.name, new ColumnIdentifier(toString(), true), type); if (!selectable.testAssignment(cfm.keyspace, receiver).isAssignable()) throw new InvalidRequestException(String.format("Cannot assign value %s to %s of type %s", this, receiver.name, receiver.type.asCQL3Type())); final Factory factory = selectable.newSelectorFactory(cfm, type, defs, boundNames); return new ForwardingFactory() { protected Factory delegate() { return factory; } protected AbstractType<?> getReturnType() { return type; } protected String getColumnName() { return String.format("(%s)%s", typeName, factory.getColumnName()); } }; } @Override public AbstractType<?> getExactTypeIfKnown(String keyspace) { return type; } @Override public String toString() { return String.format("(%s)%s", typeName, selectable); } public static class Raw extends Selectable.Raw { private final CQL3Type.Raw typeRaw; private final Selectable.Raw raw; public Raw( CQL3Type.Raw typeRaw, Selectable.Raw raw) { this.typeRaw = typeRaw; this.raw = raw; } public Selectable prepare(TableMetadata cfm) { Selectable selectable = raw.prepare(cfm); AbstractType<?> type = this.typeRaw.prepare(cfm.keyspace).getType(); if (type.isFreezable()) type = type.freeze(); return new WithTypeHint(typeRaw.toString(), type, selectable); } } } /** * In the selection clause, the parser cannot differentiate between Maps and UDTs as a column identifier and field * identifier have the same syntax. By consequence, we need to wait until the type is known to create the proper * Object: {@code ColumnMetadata} or {@code FieldIdentifier}. */ public static final class RawIdentifier extends Selectable.Raw { private final String text; private final boolean quoted; /** * Creates a {@code RawIdentifier} from an unquoted identifier string. */ public static Raw forUnquoted(String text) { return new RawIdentifier(text, false); } /** * Creates a {@code RawIdentifier} from a quoted identifier string. */ public static Raw forQuoted(String text) { return new RawIdentifier(text, true); } private RawIdentifier(String text, boolean quoted) { this.text = text; this.quoted = quoted; } @Override public Selectable prepare(TableMetadata cfm) { ColumnMetadata.Raw raw = quoted ? ColumnMetadata.Raw.forQuoted(text) : ColumnMetadata.Raw.forUnquoted(text); return raw.prepare(cfm); } public FieldIdentifier toFieldIdentifier() { return quoted ? FieldIdentifier.forQuoted(text) : FieldIdentifier.forUnquoted(text); } @Override public String toString() { return text; } } }