com.datastax.oss.driver.api.querybuilder.QueryBuilder Maven / Gradle / Ivy
/*
* Copyright DataStax, Inc.
*
* Licensed 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 com.datastax.oss.driver.api.querybuilder;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.context.DriverContext;
import com.datastax.oss.driver.api.core.metadata.token.Token;
import com.datastax.oss.driver.api.core.type.DataType;
import com.datastax.oss.driver.api.core.type.DataTypes;
import com.datastax.oss.driver.api.core.type.UserDefinedType;
import com.datastax.oss.driver.api.core.type.codec.CodecNotFoundException;
import com.datastax.oss.driver.api.core.type.codec.TypeCodec;
import com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
import com.datastax.oss.driver.api.querybuilder.delete.DeleteSelection;
import com.datastax.oss.driver.api.querybuilder.insert.InsertInto;
import com.datastax.oss.driver.api.querybuilder.relation.Relation;
import com.datastax.oss.driver.api.querybuilder.select.SelectFrom;
import com.datastax.oss.driver.api.querybuilder.select.Selector;
import com.datastax.oss.driver.api.querybuilder.term.Term;
import com.datastax.oss.driver.api.querybuilder.truncate.Truncate;
import com.datastax.oss.driver.api.querybuilder.update.UpdateStart;
import com.datastax.oss.driver.internal.core.metadata.schema.ShallowUserDefinedType;
import com.datastax.oss.driver.internal.core.metadata.token.ByteOrderedToken;
import com.datastax.oss.driver.internal.core.metadata.token.Murmur3Token;
import com.datastax.oss.driver.internal.core.metadata.token.RandomToken;
import com.datastax.oss.driver.internal.querybuilder.ArithmeticOperator;
import com.datastax.oss.driver.internal.querybuilder.DefaultLiteral;
import com.datastax.oss.driver.internal.querybuilder.DefaultRaw;
import com.datastax.oss.driver.internal.querybuilder.delete.DefaultDelete;
import com.datastax.oss.driver.internal.querybuilder.insert.DefaultInsert;
import com.datastax.oss.driver.internal.querybuilder.select.DefaultBindMarker;
import com.datastax.oss.driver.internal.querybuilder.select.DefaultSelect;
import com.datastax.oss.driver.internal.querybuilder.term.BinaryArithmeticTerm;
import com.datastax.oss.driver.internal.querybuilder.term.FunctionTerm;
import com.datastax.oss.driver.internal.querybuilder.term.OppositeTerm;
import com.datastax.oss.driver.internal.querybuilder.term.TupleTerm;
import com.datastax.oss.driver.internal.querybuilder.term.TypeHintTerm;
import com.datastax.oss.driver.internal.querybuilder.truncate.DefaultTruncate;
import com.datastax.oss.driver.internal.querybuilder.update.DefaultUpdate;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.Arrays;
/** A Domain-Specific Language to build CQL queries using Java code. */
public class QueryBuilder {
/** Starts a SELECT query for a qualified table. */
@NonNull
public static SelectFrom selectFrom(
@Nullable CqlIdentifier keyspace, @NonNull CqlIdentifier table) {
return new DefaultSelect(keyspace, table);
}
/**
* Shortcut for {@link #selectFrom(CqlIdentifier, CqlIdentifier)
* selectFrom(CqlIdentifier.fromCql(keyspace), CqlIdentifier.fromCql(table))}
*/
@NonNull
public static SelectFrom selectFrom(@Nullable String keyspace, @NonNull String table) {
return selectFrom(
keyspace == null ? null : CqlIdentifier.fromCql(keyspace), CqlIdentifier.fromCql(table));
}
/** Starts a SELECT query for an unqualified table. */
@NonNull
public static SelectFrom selectFrom(@NonNull CqlIdentifier table) {
return selectFrom(null, table);
}
/** Shortcut for {@link #selectFrom(CqlIdentifier) selectFrom(CqlIdentifier.fromCql(table))} */
@NonNull
public static SelectFrom selectFrom(@NonNull String table) {
return selectFrom(CqlIdentifier.fromCql(table));
}
/** Starts an INSERT query for a qualified table. */
@NonNull
public static InsertInto insertInto(
@Nullable CqlIdentifier keyspace, @NonNull CqlIdentifier table) {
return new DefaultInsert(keyspace, table);
}
/**
* Shortcut for {@link #insertInto(CqlIdentifier, CqlIdentifier)
* insertInto(CqlIdentifier.fromCql(keyspace), CqlIdentifier.fromCql(table))}.
*/
@NonNull
public static InsertInto insertInto(@Nullable String keyspace, @NonNull String table) {
return insertInto(
keyspace == null ? null : CqlIdentifier.fromCql(keyspace), CqlIdentifier.fromCql(table));
}
/** Starts an INSERT query for an unqualified table. */
@NonNull
public static InsertInto insertInto(@NonNull CqlIdentifier table) {
return insertInto(null, table);
}
/** Shortcut for {@link #insertInto(CqlIdentifier) insertInto(CqlIdentifier.fromCql(table))}. */
@NonNull
public static InsertInto insertInto(@NonNull String table) {
return insertInto(CqlIdentifier.fromCql(table));
}
/** Starts an UPDATE query for a qualified table. */
@NonNull
public static UpdateStart update(@Nullable CqlIdentifier keyspace, @NonNull CqlIdentifier table) {
return new DefaultUpdate(keyspace, table);
}
/**
* Shortcut for {@link #update(CqlIdentifier, CqlIdentifier)
* update(CqlIdentifier.fromCql(keyspace), CqlIdentifier.fromCql(table))}
*/
@NonNull
public static UpdateStart update(@Nullable String keyspace, @NonNull String table) {
return update(
keyspace == null ? null : CqlIdentifier.fromCql(keyspace), CqlIdentifier.fromCql(table));
}
/** Starts an UPDATE query for an unqualified table. */
@NonNull
public static UpdateStart update(@NonNull CqlIdentifier table) {
return update(null, table);
}
/** Shortcut for {@link #update(CqlIdentifier) update(CqlIdentifier.fromCql(table))} */
@NonNull
public static UpdateStart update(@NonNull String table) {
return update(CqlIdentifier.fromCql(table));
}
/** Starts a DELETE query for a qualified table. */
@NonNull
public static DeleteSelection deleteFrom(
@Nullable CqlIdentifier keyspace, @NonNull CqlIdentifier table) {
return new DefaultDelete(keyspace, table);
}
/**
* Shortcut for {@link #deleteFrom(CqlIdentifier, CqlIdentifier)
* deleteFrom(CqlIdentifier.fromCql(keyspace), CqlIdentifier.fromCql(table))}
*/
@NonNull
public static DeleteSelection deleteFrom(@Nullable String keyspace, @NonNull String table) {
return deleteFrom(
keyspace == null ? null : CqlIdentifier.fromCql(keyspace), CqlIdentifier.fromCql(table));
}
/** Starts a DELETE query for an unqualified table. */
@NonNull
public static DeleteSelection deleteFrom(@NonNull CqlIdentifier table) {
return deleteFrom(null, table);
}
/** Shortcut for {@link #deleteFrom(CqlIdentifier) deleteFrom(CqlIdentifier.fromCql(table))} */
@NonNull
public static DeleteSelection deleteFrom(@NonNull String table) {
return deleteFrom(CqlIdentifier.fromCql(table));
}
/**
* An ordered set of anonymous terms, as in {@code WHERE (a, b) = (1, 2)} (on the right-hand
* side).
*
* For example, this can be used as the right operand of {@link Relation#columns(String...)}.
*/
@NonNull
public static Term tuple(@NonNull Iterable extends Term> components) {
return new TupleTerm(components);
}
/** Var-arg equivalent of {@link #tuple(Iterable)}. */
@NonNull
public static Term tuple(@NonNull Term... components) {
return tuple(Arrays.asList(components));
}
/** The sum of two terms, as in {@code WHERE k = left + right}. */
@NonNull
public static Term add(@NonNull Term left, @NonNull Term right) {
return new BinaryArithmeticTerm(ArithmeticOperator.SUM, left, right);
}
/** The difference of two terms, as in {@code WHERE k = left - right}. */
@NonNull
public static Term subtract(@NonNull Term left, @NonNull Term right) {
return new BinaryArithmeticTerm(ArithmeticOperator.DIFFERENCE, left, right);
}
/** The product of two terms, as in {@code WHERE k = left * right}. */
@NonNull
public static Term multiply(@NonNull Term left, @NonNull Term right) {
return new BinaryArithmeticTerm(ArithmeticOperator.PRODUCT, left, right);
}
/** The quotient of two terms, as in {@code WHERE k = left / right}. */
@NonNull
public static Term divide(@NonNull Term left, @NonNull Term right) {
return new BinaryArithmeticTerm(ArithmeticOperator.QUOTIENT, left, right);
}
/** The remainder of two terms, as in {@code WHERE k = left % right}. */
@NonNull
public static Term remainder(@NonNull Term left, @NonNull Term right) {
return new BinaryArithmeticTerm(ArithmeticOperator.REMAINDER, left, right);
}
/** The opposite of a term, as in {@code WHERE k = -argument}. */
@NonNull
public static Term negate(@NonNull Term argument) {
return new OppositeTerm(argument);
}
/** A function call as a term, as in {@code WHERE k = f(arguments)}. */
@NonNull
public static Term function(
@NonNull CqlIdentifier functionId, @NonNull Iterable arguments) {
return function(null, functionId, arguments);
}
/** Var-arg equivalent of {@link #function(CqlIdentifier, Iterable)}. */
@NonNull
public static Term function(@NonNull CqlIdentifier functionId, @NonNull Term... arguments) {
return function(functionId, Arrays.asList(arguments));
}
/**
* Shortcut for {@link #function(CqlIdentifier, Iterable)
* function(CqlIdentifier.fromCql(functionName), arguments)}.
*/
@NonNull
public static Term function(@NonNull String functionName, @NonNull Iterable arguments) {
return function(CqlIdentifier.fromCql(functionName), arguments);
}
/**
* Shortcut for {@link #function(CqlIdentifier, Term...)
* function(CqlIdentifier.fromCql(functionName), arguments)}.
*/
@NonNull
public static Term function(@NonNull String functionName, @NonNull Term... arguments) {
return function(CqlIdentifier.fromCql(functionName), arguments);
}
/** A function call as a term, as in {@code WHERE k = ks.f(arguments)}. */
@NonNull
public static Term function(
@Nullable CqlIdentifier keyspaceId,
@NonNull CqlIdentifier functionId,
@NonNull Iterable arguments) {
return new FunctionTerm(keyspaceId, functionId, arguments);
}
/** Var-arg equivalent of {@link #function(CqlIdentifier, CqlIdentifier, Iterable)}. */
@NonNull
public static Term function(
@Nullable CqlIdentifier keyspaceId,
@NonNull CqlIdentifier functionId,
@NonNull Term... arguments) {
return function(keyspaceId, functionId, Arrays.asList(arguments));
}
/**
* Shortcut for {@link #function(CqlIdentifier, CqlIdentifier, Iterable)
* function(CqlIdentifier.fromCql(keyspaceName), CqlIdentifier.fromCql(functionName), arguments)}.
*/
@NonNull
public static Term function(
@Nullable String keyspaceName,
@NonNull String functionName,
@NonNull Iterable arguments) {
return function(
keyspaceName == null ? null : CqlIdentifier.fromCql(keyspaceName),
CqlIdentifier.fromCql(functionName),
arguments);
}
/**
* Shortcut for {@link #function(CqlIdentifier, CqlIdentifier, Term...)
* function(CqlIdentifier.fromCql(keyspaceName), CqlIdentifier.fromCql(functionName), arguments)}.
*/
@NonNull
public static Term function(
@Nullable String keyspaceName, @NonNull String functionName, @NonNull Term... arguments) {
return function(
keyspaceName == null ? null : CqlIdentifier.fromCql(keyspaceName),
CqlIdentifier.fromCql(functionName),
arguments);
}
/**
* Provides a type hint for an expression, as in {@code WHERE k = (double)1/3}.
*
* To create the data type, use the constants and static methods in {@link DataTypes}, or
* {@link #udt(CqlIdentifier)}.
*/
@NonNull
public static Term typeHint(@NonNull Term term, @NonNull DataType targetType) {
return new TypeHintTerm(term, targetType);
}
/** A call to the built-in {@code now} function as a term. */
@NonNull
public static Term now() {
return function("now");
}
/** A call to the built-in {@code currentTimestamp} function as a term. */
@NonNull
public static Term currentTimestamp() {
return function("currenttimestamp");
}
/** A call to the built-in {@code currentDate} function as a term. */
@NonNull
public static Term currentDate() {
return function("currentdate");
}
/** A call to the built-in {@code currentTime} function as a term. */
@NonNull
public static Term currentTime() {
return function("currenttime");
}
/** A call to the built-in {@code currentTimeUuid} function as a term. */
@NonNull
public static Term currentTimeUuid() {
return function("currenttimeuuid");
}
/** A call to the built-in {@code minTimeUuid} function as a term. */
@NonNull
public static Term minTimeUuid(@NonNull Term argument) {
return function("mintimeuuid", argument);
}
/** A call to the built-in {@code maxTimeUuid} function as a term. */
@NonNull
public static Term maxTimeUuid(@NonNull Term argument) {
return function("maxtimeuuid", argument);
}
/** A call to the built-in {@code toDate} function as a term. */
@NonNull
public static Term toDate(@NonNull Term argument) {
return function("todate", argument);
}
/** A call to the built-in {@code toTimestamp} function as a term. */
@NonNull
public static Term toTimestamp(@NonNull Term argument) {
return function("totimestamp", argument);
}
/** A call to the built-in {@code toUnixTimestamp} function as a term. */
@NonNull
public static Term toUnixTimestamp(@NonNull Term argument) {
return function("tounixtimestamp", argument);
}
/**
* A literal term, as in {@code WHERE k = 1}.
*
*
This method can process any type for which there is a default Java to CQL mapping, namely:
* primitive types ({@code Integer=>int, Long=>bigint, String=>text, etc.}), and collections,
* tuples, and user defined types thereof.
*
*
A null argument will be rendered as {@code NULL}.
*
*
For custom mappings, use {@link #literal(Object, CodecRegistry)} or {@link #literal(Object,
* TypeCodec)}.
*
* @throws CodecNotFoundException if there is no default CQL mapping for the Java type of {@code
* value}.
*/
@NonNull
public static Literal literal(@Nullable Object value) {
return literal(value, CodecRegistry.DEFAULT);
}
/**
* A literal term, as in {@code WHERE k = 1}.
*
*
This is an alternative to {@link #literal(Object)} for custom type mappings. The provided
* registry should contain a codec that can format the value. Typically, this will be your
* session's registry, which is accessible via {@code session.getContext().getCodecRegistry()}.
*
* @see DriverContext#getCodecRegistry()
* @throws CodecNotFoundException if {@code codecRegistry} does not contain any codec that can
* handle {@code value}.
*/
@NonNull
public static Literal literal(@Nullable Object value, @NonNull CodecRegistry codecRegistry) {
if (value instanceof Murmur3Token) {
value = ((Murmur3Token) value).getValue();
} else if (value instanceof ByteOrderedToken) {
value = ((ByteOrderedToken) value).getValue();
} else if (value instanceof RandomToken) {
value = ((RandomToken) value).getValue();
} else if (value instanceof Token) {
throw new IllegalArgumentException("Unsupported token type: " + value.getClass().getName());
}
try {
return literal(value, (value == null) ? null : codecRegistry.codecFor(value));
} catch (CodecNotFoundException e) {
assert value != null;
throw new IllegalArgumentException(
String.format(
"Could not inline literal of type %s. "
+ "This happens because the driver doesn't know how to map it to a CQL type. "
+ "Try passing a TypeCodec or CodecRegistry to literal().",
value.getClass().getName()),
e);
}
}
/**
* A literal term, as in {@code WHERE k = 1}.
*
*
This is an alternative to {@link #literal(Object)} for custom type mappings. The value will
* be turned into a string with {@link TypeCodec#format(Object)}, and inlined in the query.
*/
@NonNull
public static Literal literal(@Nullable T value, @Nullable TypeCodec codec) {
// Don't handle Token here, if the user calls this directly we assume they passed a codec that
// can handle the value
return new DefaultLiteral<>(value, codec);
}
/**
* A raw CQL snippet.
*
* The contents will be appended to the query as-is, without any syntax checking or escaping.
* This method should be used with caution, as it's possible to generate invalid CQL that will
* fail at execution time; on the other hand, it can be used as a workaround to handle new CQL
* features that are not yet covered by the query builder.
*/
@NonNull
public static Raw raw(@NonNull String raw) {
return new DefaultRaw(raw);
}
/** Creates an anonymous bind marker, which appears as {@code ?} in the generated CQL. */
@NonNull
public static BindMarker bindMarker() {
return bindMarker((CqlIdentifier) null);
}
/** Creates a named bind marker, which appears as {@code :id} in the generated CQL. */
@NonNull
public static BindMarker bindMarker(@Nullable CqlIdentifier id) {
return new DefaultBindMarker(id);
}
/** Shortcut for {@link #bindMarker(CqlIdentifier) bindMarker(CqlIdentifier.fromCql(name))} */
@NonNull
public static BindMarker bindMarker(@Nullable String name) {
return bindMarker(name == null ? null : CqlIdentifier.fromCql(name));
}
/**
* Shortcut to reference a UDT in methods that use a {@link DataType}, such as {@link
* #typeHint(Term, DataType)} and {@link Selector#cast(Selector, DataType)}.
*/
@NonNull
public static UserDefinedType udt(@NonNull CqlIdentifier name) {
return new ShallowUserDefinedType(null, name, false);
}
/** Shortcut for {@link #udt(CqlIdentifier) udt(CqlIdentifier.fromCql(name))}. */
@NonNull
public static UserDefinedType udt(@NonNull String name) {
return udt(CqlIdentifier.fromCql(name));
}
/**
* Creates a new {@code TRUNCATE} query.
*
* @param table the name of the table to truncate.
* @return the truncation query.
*/
public static Truncate truncate(@NonNull CqlIdentifier table) {
return truncate(null, table);
}
/**
* Creates a new {@code TRUNCATE} query.
*
*
This is a shortcut for {@link #truncate(CqlIdentifier)
* truncate(CqlIdentifier.fromCql(table))}.
*
* @param table the name of the table to truncate.
* @return the truncation query.
*/
public static Truncate truncate(@NonNull String table) {
return truncate(null, CqlIdentifier.fromCql(table));
}
/**
* Creates a new {@code TRUNCATE} query.
*
* @param keyspace the name of the keyspace to use.
* @param table the name of the table to truncate.
* @return the truncation query.
*/
public static Truncate truncate(@Nullable CqlIdentifier keyspace, @NonNull CqlIdentifier table) {
return new DefaultTruncate(keyspace, table);
}
/**
* Creates a new {@code TRUNCATE} query.
*
*
This is a shortcut for {@link #truncate(CqlIdentifier, CqlIdentifier)
* truncate(CqlIdentifier.fromCql(keyspace), CqlIdentifier.fromCql(table))}.
*
* @param keyspace the name of the keyspace to use.
* @param table the name of the table to truncate.
* @return the truncation query.
*/
public static Truncate truncate(@Nullable String keyspace, @NonNull String table) {
return truncate(
keyspace == null ? null : CqlIdentifier.fromCql(keyspace), CqlIdentifier.fromCql(table));
}
}