Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.datastax.driver.core.querybuilder.Select Maven / Gradle / Ivy
Go to download
Shaded version of DataStax Java Driver for Apache Cassandra
/*
* Copyright (C) 2012-2015 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.driver.core.querybuilder;
import com.datastax.driver.core.CodecRegistry;
import com.datastax.driver.core.ColumnMetadata;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.TableMetadata;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* A built SELECT statement.
*/
public class Select extends BuiltStatement {
private static final List COUNT_ALL = Collections.singletonList(new Utils.FCall("count", new Utils.RawString("*")));
private final String table;
private final boolean isDistinct;
private final boolean isJson;
private final List columnNames;
private final Where where;
private List orderings;
private Object limit;
private Object perPartitionLimit;
private boolean allowFiltering;
Select(String keyspace, String table, List columnNames, boolean isDistinct, boolean isJson) {
this(keyspace, table, null, null, columnNames, isDistinct, isJson);
}
Select(TableMetadata table, List columnNames, boolean isDistinct, boolean isJson) {
this(escapeId(table.getKeyspace().getName()),
escapeId(table.getName()),
Arrays.asList(new Object[table.getPartitionKey().size()]),
table.getPartitionKey(),
columnNames,
isDistinct,
isJson);
}
Select(String keyspace,
String table,
List routingKeyValues,
List partitionKey,
List columnNames,
boolean isDistinct,
boolean isJson) {
super(keyspace, partitionKey, routingKeyValues);
this.table = table;
this.columnNames = columnNames;
this.isDistinct = isDistinct;
this.isJson = isJson;
this.where = new Where(this);
}
@Override
StringBuilder buildQueryString(List variables, CodecRegistry codecRegistry) {
StringBuilder builder = new StringBuilder();
builder.append("SELECT ");
if (isJson)
builder.append("JSON ");
if (isDistinct)
builder.append("DISTINCT ");
if (columnNames == null) {
builder.append('*');
} else {
Utils.joinAndAppendNames(builder, codecRegistry, ",", columnNames);
}
builder.append(" FROM ");
if (keyspace != null)
Utils.appendName(keyspace, builder).append('.');
Utils.appendName(table, builder);
if (!where.clauses.isEmpty()) {
builder.append(" WHERE ");
Utils.joinAndAppend(builder, codecRegistry, " AND ", where.clauses, variables);
}
if (orderings != null) {
builder.append(" ORDER BY ");
Utils.joinAndAppend(builder, codecRegistry, ",", orderings, variables);
}
if (perPartitionLimit != null) {
builder.append(" PER PARTITION LIMIT ").append(perPartitionLimit);
}
if (limit != null) {
builder.append(" LIMIT ").append(limit);
}
if (allowFiltering) {
builder.append(" ALLOW FILTERING");
}
return builder;
}
/**
* Adds a WHERE clause to this statement.
*
* This is a shorter/more readable version for {@code where().and(clause)}.
*
* @param clause the clause to add.
* @return the where clause of this query to which more clause can be added.
*/
public Where where(Clause clause) {
return where.and(clause);
}
/**
* Returns a Where statement for this query without adding clause.
*
* @return the where clause of this query to which more clause can be added.
*/
public Where where() {
return where;
}
/**
* Adds an ORDER BY clause to this statement.
*
* @param orderings the orderings to define for this query.
* @return this statement.
* @throws IllegalStateException if an ORDER BY clause has already been
* provided.
*/
public Select orderBy(Ordering... orderings) {
if (this.orderings != null)
throw new IllegalStateException("An ORDER BY clause has already been provided");
this.orderings = Arrays.asList(orderings);
for (int i = 0; i < orderings.length; i++)
checkForBindMarkers(orderings[i]);
return this;
}
/**
* Adds a {@code LIMIT} clause to this statement.
*
* @param limit the limit to set.
* @return this statement.
* @throws IllegalArgumentException if {@code limit <= 0}.
* @throws IllegalStateException if a {@code LIMIT} clause has already been
* provided.
*/
public Select limit(int limit) {
if (limit <= 0)
throw new IllegalArgumentException("Invalid LIMIT value, must be strictly positive");
if (this.limit != null)
throw new IllegalStateException("A LIMIT value has already been provided");
this.limit = limit;
setDirty();
return this;
}
/**
* Adds a prepared {@code LIMIT} clause to this statement.
*
* @param marker the marker to use for the limit.
* @return this statement.
* @throws IllegalStateException if a {@code LIMIT} clause has already been
* provided.
*/
public Select limit(BindMarker marker) {
if (this.limit != null)
throw new IllegalStateException("A LIMIT value has already been provided");
this.limit = marker;
checkForBindMarkers(marker);
return this;
}
/**
* Adds a {@code PER PARTITION LIMIT} clause to this statement.
*
* Note: support for {@code PER PARTITION LIMIT} clause is only available from
* Cassandra 3.6 onwards.
*
* @param perPartitionLimit the limit to set per partition.
* @return this statement.
* @throws IllegalArgumentException if {@code perPartitionLimit <= 0}.
* @throws IllegalStateException if a {@code PER PARTITION LIMIT} clause has already been
* provided.
* @throws IllegalStateException if this statement is a {@code SELECT DISTINCT} statement.
*/
public Select perPartitionLimit(int perPartitionLimit) {
if (perPartitionLimit <= 0)
throw new IllegalArgumentException("Invalid PER PARTITION LIMIT value, must be strictly positive");
if (this.perPartitionLimit != null)
throw new IllegalStateException("A PER PARTITION LIMIT value has already been provided");
if (isDistinct)
throw new IllegalStateException("PER PARTITION LIMIT is not allowed with SELECT DISTINCT queries");
this.perPartitionLimit = perPartitionLimit;
setDirty();
return this;
}
/**
* Adds a prepared {@code PER PARTITION LIMIT} clause to this statement.
*
* Note: support for {@code PER PARTITION LIMIT} clause is only available from
* Cassandra 3.6 onwards.
*
* @param marker the marker to use for the limit per partition.
* @return this statement.
* @throws IllegalStateException if a {@code PER PARTITION LIMIT} clause has already been
* provided.
* @throws IllegalStateException if this statement is a {@code SELECT DISTINCT} statement.
*/
public Select perPartitionLimit(BindMarker marker) {
if (this.perPartitionLimit != null)
throw new IllegalStateException("A PER PARTITION LIMIT value has already been provided");
if (isDistinct)
throw new IllegalStateException("PER PARTITION LIMIT is not allowed with SELECT DISTINCT queries");
this.perPartitionLimit = marker;
checkForBindMarkers(marker);
return this;
}
/**
* Adds an ALLOW FILTERING directive to this statement.
*
* @return this statement.
*/
public Select allowFiltering() {
allowFiltering = true;
return this;
}
/**
* The WHERE clause of a SELECT statement.
*/
public static class Where extends BuiltStatement.ForwardingStatement {
private final List clauses = new ArrayList();
Where(Select statement) {
super(statement);
}
/**
* Adds the provided clause to this WHERE clause.
*
* @param clause the clause to add.
* @return this WHERE clause.
*/
public Where and(Clause clause) {
clauses.add(clause);
statement.maybeAddRoutingKey(clause.name(), clause.firstValue());
checkForBindMarkers(clause);
return this;
}
/**
* Adds an ORDER BY clause to the SELECT statement this WHERE clause if
* part of.
*
* @param orderings the orderings to add.
* @return the select statement this Where clause if part of.
* @throws IllegalStateException if an ORDER BY clause has already been
* provided.
*/
public Select orderBy(Ordering... orderings) {
return statement.orderBy(orderings);
}
/**
* Adds a {@code LIMIT} clause to the {@code SELECT} statement this
* {@code WHERE} clause if part of.
*
* @param limit the limit to set.
* @return the {@code SELECT} statement this {@code WHERE} clause if part of.
* @throws IllegalArgumentException if {@code limit <= 0}.
* @throws IllegalStateException if a {@code LIMIT} clause has already been
* provided.
*/
public Select limit(int limit) {
return statement.limit(limit);
}
/**
* Adds a bind marker for the {@code LIMIT} clause to the {@code SELECT} statement this
* {@code WHERE} clause if part of.
*
* @param limit the bind marker to use as limit.
* @return the {@code SELECT} statement this {@code WHERE} clause if part of.
* @throws IllegalStateException if a {@code LIMIT} clause has already been
* provided.
*/
public Select limit(BindMarker limit) {
return statement.limit(limit);
}
/**
* Adds a {@code PER PARTITION LIMIT} clause to the {@code SELECT} statement this
* {@code WHERE} clause if part of.
*
* Note: support for {@code PER PARTITION LIMIT} clause is only available from
* Cassandra 3.6 onwards.
*
* @param perPartitionLimit the limit to set per partition.
* @return the {@code SELECT} statement this {@code WHERE} clause if part of.
* @throws IllegalArgumentException if {@code perPartitionLimit <= 0}.
* @throws IllegalStateException if a {@code PER PARTITION LIMIT} clause has already been
* provided.
* @throws IllegalStateException if this statement is a {@code SELECT DISTINCT} statement.
*/
public Select perPartitionLimit(int perPartitionLimit) {
return statement.perPartitionLimit(perPartitionLimit);
}
/**
* Adds a bind marker for the {@code PER PARTITION LIMIT} clause to the {@code SELECT} statement this
* {@code WHERE} clause if part of.
*
* Note: support for {@code PER PARTITION LIMIT} clause is only available from
* Cassandra 3.6 onwards.
*
* @param limit the bind marker to use as limit per partition.
* @return the {@code SELECT} statement this {@code WHERE} clause if part of.
* @throws IllegalStateException if a {@code PER PARTITION LIMIT} clause has already been
* provided.
* @throws IllegalStateException if this statement is a {@code SELECT DISTINCT} statement.
*/
public Select perPartitionLimit(BindMarker limit) {
return statement.perPartitionLimit(limit);
}
}
/**
* An in-construction SELECT statement.
*/
public static class Builder {
List columnNames;
boolean isDistinct;
boolean isJson;
Builder() {
}
Builder(List columnNames) {
this.columnNames = columnNames;
}
/**
* Uses DISTINCT selection.
*
* @return this in-build SELECT statement.
*/
public Builder distinct() {
this.isDistinct = true;
return this;
}
/**
* Uses JSON selection.
*
* Cassandra 2.2 introduced JSON support to SELECT statements:
* the {@code JSON} keyword can be used to return each row as a single JSON encoded map.
*
* @return this in-build SELECT statement.
* @see JSON Support for CQL
* @see JSON Support in Cassandra 2.2
* @see Data retrieval using JSON
*/
public Builder json() {
this.isJson = true;
return this;
}
/**
* Adds the table to select from.
*
* @param table the name of the table to select from.
* @return a newly built SELECT statement that selects from {@code table}.
*/
public Select from(String table) {
return from(null, table);
}
/**
* Adds the table to select from.
*
* @param keyspace the name of the keyspace to select from.
* @param table the name of the table to select from.
* @return a newly built SELECT statement that selects from {@code keyspace.table}.
*/
public Select from(String keyspace, String table) {
return new Select(keyspace, table, columnNames, isDistinct, isJson);
}
/**
* Adds the table to select from.
*
* @param table the table to select from.
* @return a newly built SELECT statement that selects from {@code table}.
*/
public Select from(TableMetadata table) {
return new Select(table, columnNames, isDistinct, isJson);
}
}
/**
* An Selection clause for an in-construction SELECT statement.
*/
public static abstract class Selection extends Builder {
/**
* Uses DISTINCT selection.
*
* @return this in-build SELECT statement.
*/
@Override
public Selection distinct() {
this.isDistinct = true;
return this;
}
/**
* Uses JSON selection.
*
* Cassandra 2.2 introduced JSON support to SELECT statements:
* the {@code JSON} keyword can be used to return each row as a single JSON encoded map.
*
* @return this in-build SELECT statement.
* @see JSON Support for CQL
* @see JSON Support in Cassandra 2.2
* @see Data retrieval using JSON
*/
@Override
public Selection json() {
this.isJson = true;
return this;
}
/**
* Selects all columns (i.e. "SELECT * ...")
*
* @return an in-build SELECT statement.
* @throws IllegalStateException if some columns had already been selected for this builder.
*/
public abstract Builder all();
/**
* Selects the count of all returned rows (i.e. "SELECT count(*) ...").
*
* @return an in-build SELECT statement.
* @throws IllegalStateException if some columns had already been selected for this builder.
*/
public abstract Builder countAll();
/**
* Selects the provided column.
*
* @param name the new column name to add.
* @return this in-build SELECT statement
*/
public abstract SelectionOrAlias column(String name);
/**
* Selects the write time of provided column.
*
* This is a shortcut for {@code fcall("writetime", QueryBuilder.column(name))}.
*
* @param name the name of the column to select the write time of.
* @return this in-build SELECT statement
*/
public abstract SelectionOrAlias writeTime(String name);
/**
* Selects the ttl of provided column.
*
* This is a shortcut for {@code fcall("ttl", QueryBuilder.column(name))}.
*
* @param name the name of the column to select the ttl of.
* @return this in-build SELECT statement
*/
public abstract SelectionOrAlias ttl(String name);
/**
* Creates a function call.
*
* Please note that the parameters are interpreted as values, and so
* {@code fcall("textToBlob", "foo")} will generate the string
* {@code "textToBlob('foo')"}. If you want to generate
* {@code "textToBlob(foo)"}, i.e. if the argument must be interpreted
* as a column name (in a select clause), you will need to use the
* {@link QueryBuilder#column} method, and so
* {@code fcall("textToBlob", QueryBuilder.column(foo)}.
*
* @param name the name of the function.
* @param parameters the parameters for the function call.
* @return this in-build SELECT statement
*/
public abstract SelectionOrAlias fcall(String name, Object... parameters);
/**
* Creates a cast of an expression to a given CQL type.
*
* @param column the expression to cast. It can be a complex expression like a
* {@link QueryBuilder#fcall(String, Object...) function call}.
* @param targetType the target CQL type to cast to. Use static methods such as {@link DataType#text()}.
* @return this in-build SELECT statement.
*/
public SelectionOrAlias cast(Object column, DataType targetType) {
// This method should be abstract like others here. But adding an abstract method is not binary-compatible,
// so we add this dummy implementation to make Clirr happy.
throw new UnsupportedOperationException("Not implemented. This should only happen if you've written your own implementation of Selection");
}
/**
* Selects the provided raw expression.
*
* The provided string will be appended to the query as-is, without any form of escaping or quoting.
*
* @param rawString the raw expression to add.
* @return this in-build SELECT statement
*/
public SelectionOrAlias raw(String rawString) {
// This method should be abstract like others here. But adding an abstract method is not binary-compatible,
// so we add this dummy implementation to make Clirr happy.
throw new UnsupportedOperationException("Not implemented. This should only happen if you've written your own implementation of Selection");
}
/**
* Creates a {@code toJson()} function call.
* This is a shortcut for {@code fcall("toJson", QueryBuilder.column(name))}.
*
* Support for JSON functions has been added in Cassandra 2.2.
* The {@code toJson()} function is similar to {@code SELECT JSON} statements,
* but applies to a single column value instead of the entire row,
* and produces a JSON-encoded string representing the normal Cassandra column value.
*
* It may only be used in the selection clause of a {@code SELECT} statement.
*
* @return the function call.
* @see JSON Support for CQL
* @see JSON Support in Cassandra 2.2
*/
public SelectionOrAlias toJson(String name) {
// This method should be abstract like others here. But adding an abstract method is not binary-compatible,
// so we add this dummy implementation to make Clirr happy.
throw new UnsupportedOperationException("Not implemented. This should only happen if you've written your own implementation of Selection");
}
}
/**
* An Selection clause for an in-construction SELECT statement.
*
* This only differs from {@link Selection} in that you can add an
* alias for the previously selected item through {@link SelectionOrAlias#as}.
*/
public static class SelectionOrAlias extends Selection {
private Object previousSelection;
/**
* Adds an alias for the just selected item.
*
* @param alias the name of the alias to use.
* @return this in-build SELECT statement
*/
public Selection as(String alias) {
assert previousSelection != null;
Object a = new Utils.Alias(previousSelection, alias);
previousSelection = null;
return addName(a);
}
// We don't return SelectionOrAlias on purpose
private Selection addName(Object name) {
if (columnNames == null)
columnNames = new ArrayList();
columnNames.add(name);
return this;
}
private SelectionOrAlias queueName(Object name) {
if (previousSelection != null)
addName(previousSelection);
previousSelection = name;
return this;
}
@Override
public Builder all() {
if (columnNames != null)
throw new IllegalStateException(String.format("Some columns (%s) have already been selected.", columnNames));
if (previousSelection != null)
throw new IllegalStateException(String.format("Some columns ([%s]) have already been selected.", previousSelection));
return (Builder) this;
}
@Override
public Builder countAll() {
if (columnNames != null)
throw new IllegalStateException(String.format("Some columns (%s) have already been selected.", columnNames));
if (previousSelection != null)
throw new IllegalStateException(String.format("Some columns ([%s]) have already been selected.", previousSelection));
columnNames = COUNT_ALL;
return (Builder) this;
}
@Override
public SelectionOrAlias column(String name) {
return queueName(name);
}
@Override
public SelectionOrAlias writeTime(String name) {
return queueName(new Utils.FCall("writetime", new Utils.CName(name)));
}
@Override
public SelectionOrAlias ttl(String name) {
return queueName(new Utils.FCall("ttl", new Utils.CName(name)));
}
@Override
public SelectionOrAlias fcall(String name, Object... parameters) {
return queueName(new Utils.FCall(name, parameters));
}
@Override
public SelectionOrAlias cast(Object column, DataType targetType) {
return queueName(new Utils.Cast(column, targetType));
}
@Override
public SelectionOrAlias raw(String rawString) {
return queueName(QueryBuilder.raw(rawString));
}
@Override
public SelectionOrAlias toJson(String name) {
return queueName(new Utils.FCall("toJson", new Utils.CName(name)));
}
@Override
public Select from(String keyspace, String table) {
if (previousSelection != null)
addName(previousSelection);
previousSelection = null;
return super.from(keyspace, table);
}
@Override
public Select from(TableMetadata table) {
if (previousSelection != null)
addName(previousSelection);
previousSelection = null;
return super.from(table);
}
}
}