blob: 31b3fd0bd4d206ccf078d05cba08294f4947cc86 [file] [log] [blame]
/*
* 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;
import java.nio.ByteBuffer;
import java.util.*;
import org.apache.cassandra.cql3.Term.MultiItemTerminal;
import org.apache.cassandra.cql3.Term.Terminal;
import org.apache.cassandra.cql3.functions.Function;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.transport.ProtocolVersion;
/**
* A set of {@code Terms}
*/
public interface Terms
{
/**
* The {@code List} returned when the list was not set.
*/
@SuppressWarnings("rawtypes")
public static final List UNSET_LIST = new AbstractList()
{
@Override
public Object get(int index)
{
throw new UnsupportedOperationException();
}
@Override
public int size()
{
return 0;
}
};
/**
* Adds all functions (native and user-defined) used by any of the terms to the specified list.
* @param functions the list to add to
*/
public void addFunctionsTo(List<Function> functions);
/**
* Collects the column specifications for the bind variables in the terms.
* This is obviously a no-op if the terms are Terminal.
*
* @param boundNames the variables specification where to collect the
* bind variables of the terms in.
*/
public void collectMarkerSpecification(VariableSpecifications boundNames);
/**
* Bind the values in these terms to the values contained in {@code options}.
* This is obviously a no-op if the term is Terminal.
*
* @param options the values to bind markers to.
* @return the result of binding all the variables of these NonTerminals or an {@code UNSET_LIST} if the term
* was unset.
*/
public List<Terminal> bind(QueryOptions options);
public List<ByteBuffer> bindAndGet(QueryOptions options);
/**
* Creates a {@code Terms} for the specified list marker.
*
* @param marker the list marker
* @param type the element type
* @return a {@code Terms} for the specified list marker
*/
public static Terms ofListMarker(final Lists.Marker marker, final AbstractType<?> type)
{
return new Terms()
{
@Override
public void addFunctionsTo(List<Function> functions)
{
}
@Override
public void collectMarkerSpecification(VariableSpecifications boundNames)
{
marker.collectMarkerSpecification(boundNames);
}
@Override
public List<ByteBuffer> bindAndGet(QueryOptions options)
{
Terminal terminal = marker.bind(options);
if (terminal == null)
return null;
if (terminal == Constants.UNSET_VALUE)
return UNSET_LIST;
return ((MultiItemTerminal) terminal).getElements();
}
@Override
public List<Terminal> bind(QueryOptions options)
{
Terminal terminal = marker.bind(options);
if (terminal == null)
return null;
if (terminal == Constants.UNSET_VALUE)
return UNSET_LIST;
java.util.function.Function<ByteBuffer, Term.Terminal> deserializer = deserializer(options.getProtocolVersion());
List<ByteBuffer> boundValues = ((MultiItemTerminal) terminal).getElements();
List<Term.Terminal> values = new ArrayList<>(boundValues.size());
for (int i = 0, m = boundValues.size(); i < m; i++)
{
ByteBuffer buffer = boundValues.get(i);
Term.Terminal value = buffer == null ? null : deserializer.apply(buffer);
values.add(value);
}
return values;
}
public java.util.function.Function<ByteBuffer, Term.Terminal> deserializer(ProtocolVersion version)
{
if (type.isCollection())
{
switch (((CollectionType<?>) type).kind)
{
case LIST:
return e -> Lists.Value.fromSerialized(e, (ListType<?>) type);
case SET:
return e -> Sets.Value.fromSerialized(e, (SetType<?>) type);
case MAP:
return e -> Maps.Value.fromSerialized(e, (MapType<?, ?>) type);
}
throw new AssertionError();
}
return e -> new Constants.Value(e);
}
};
}
/**
* Creates a {@code Terms} containing a single {@code Term}.
*
* @param term the {@code Term}
* @return a {@code Terms} containing a single {@code Term}.
*/
public static Terms of(final Term term)
{
return new Terms()
{
@Override
public void addFunctionsTo(List<Function> functions)
{
term.addFunctionsTo(functions);
}
@Override
public void collectMarkerSpecification(VariableSpecifications boundNames)
{
term.collectMarkerSpecification(boundNames);
}
@Override
public List<ByteBuffer> bindAndGet(QueryOptions options)
{
return Collections.singletonList(term.bindAndGet(options));
}
@Override
public List<Terminal> bind(QueryOptions options)
{
return Collections.singletonList(term.bind(options));
}
};
}
/**
* Creates a {@code Terms} containing a set of {@code Term}.
*
* @param term the {@code Term}
* @return a {@code Terms} containing a set of {@code Term}.
*/
public static Terms of(final List<Term> terms)
{
return new Terms()
{
@Override
public void addFunctionsTo(List<Function> functions)
{
addFunctions(terms, functions);
}
@Override
public void collectMarkerSpecification(VariableSpecifications boundNames)
{
for (int i = 0, m = terms.size(); i <m; i++)
{
Term term = terms.get(i);
term.collectMarkerSpecification(boundNames);
}
}
@Override
public List<Terminal> bind(QueryOptions options)
{
int size = terms.size();
List<Terminal> terminals = new ArrayList<>(size);
for (int i = 0; i < size; i++)
{
Term term = terms.get(i);
terminals.add(term.bind(options));
}
return terminals;
}
@Override
public List<ByteBuffer> bindAndGet(QueryOptions options)
{
int size = terms.size();
List<ByteBuffer> buffers = new ArrayList<>(size);
for (int i = 0; i < size; i++)
{
Term term = terms.get(i);
buffers.add(term.bindAndGet(options));
}
return buffers;
}
};
}
/**
* Adds all functions (native and user-defined) of the specified terms to the list.
* @param functions the list to add to
*/
public static void addFunctions(Iterable<Term> terms, List<Function> functions)
{
for (Term term : terms)
{
if (term != null)
term.addFunctionsTo(functions);
}
}
public static ByteBuffer asBytes(String keyspace, String term, AbstractType type)
{
ColumnSpecification receiver = new ColumnSpecification(keyspace, "--dummy--", new ColumnIdentifier("(dummy)", true), type);
Term.Raw rawTerm = CQLFragmentParser.parseAny(CqlParser::term, term, "CQL term");
return rawTerm.prepare(keyspace, receiver).bindAndGet(QueryOptions.DEFAULT);
}
}