com.datastax.oss.driver.internal.querybuilder.select.DefaultSelect Maven / Gradle / Ivy
The newest version!
/*
* 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.
*/
/*
* Copyright (C) 2022 ScyllaDB
*
* Modified by ScyllaDB
*/
package com.datastax.oss.driver.internal.querybuilder.select;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.cql.SimpleStatementBuilder;
import com.datastax.oss.driver.api.core.data.CqlDuration;
import com.datastax.oss.driver.api.core.metadata.schema.ClusteringOrder;
import com.datastax.oss.driver.api.querybuilder.BindMarker;
import com.datastax.oss.driver.api.querybuilder.relation.Relation;
import com.datastax.oss.driver.api.querybuilder.select.Select;
import com.datastax.oss.driver.api.querybuilder.select.SelectFrom;
import com.datastax.oss.driver.api.querybuilder.select.Selector;
import com.datastax.oss.driver.internal.querybuilder.CqlHelper;
import com.datastax.oss.driver.internal.querybuilder.ImmutableCollections;
import com.datastax.oss.driver.shaded.guava.common.base.Preconditions;
import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList;
import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.Map;
import net.jcip.annotations.Immutable;
@Immutable
public class DefaultSelect implements SelectFrom, Select {
private static final ImmutableList SELECT_ALL = ImmutableList.of(AllSelector.INSTANCE);
private final CqlIdentifier keyspace;
private final CqlIdentifier table;
private final boolean isJson;
private final boolean isDistinct;
private final ImmutableList selectors;
private final ImmutableList relations;
private final ImmutableList groupByClauses;
private final ImmutableMap orderings;
private final Object limit;
private final Object perPartitionLimit;
private final boolean allowsFiltering;
private final boolean bypassCache;
private final Object timeout;
public DefaultSelect(@Nullable CqlIdentifier keyspace, @NonNull CqlIdentifier table) {
this(
keyspace,
table,
false,
false,
ImmutableList.of(),
ImmutableList.of(),
ImmutableList.of(),
ImmutableMap.of(),
null,
null,
false,
false,
null);
}
/**
* This constructor is public only as a convenience for custom extensions of the query builder.
*
* @param selectors if it contains {@link AllSelector#INSTANCE}, that must be the only element.
* This isn't re-checked because methods that call this constructor internally already do it,
* make sure you do it yourself.
*/
public DefaultSelect(
@Nullable CqlIdentifier keyspace,
@NonNull CqlIdentifier table,
boolean isJson,
boolean isDistinct,
@NonNull ImmutableList selectors,
@NonNull ImmutableList relations,
@NonNull ImmutableList groupByClauses,
@NonNull ImmutableMap orderings,
@Nullable Object limit,
@Nullable Object perPartitionLimit,
boolean allowsFiltering,
boolean bypassCache,
@Nullable Object timeout) {
this.groupByClauses = groupByClauses;
this.orderings = orderings;
Preconditions.checkArgument(
limit == null
|| (limit instanceof Integer && (Integer) limit > 0)
|| limit instanceof BindMarker,
"limit must be a strictly positive integer or a bind marker");
this.keyspace = keyspace;
this.table = table;
this.isJson = isJson;
this.isDistinct = isDistinct;
this.selectors = selectors;
this.relations = relations;
this.limit = limit;
this.perPartitionLimit = perPartitionLimit;
this.allowsFiltering = allowsFiltering;
this.bypassCache = bypassCache;
Preconditions.checkArgument(
timeout == null || timeout instanceof CqlDuration || timeout instanceof BindMarker,
"TIMEOUT value must be a BindMarker or a CqlDuration");
this.timeout = timeout;
}
@NonNull
@Override
public SelectFrom json() {
return new DefaultSelect(
keyspace,
table,
true,
isDistinct,
selectors,
relations,
groupByClauses,
orderings,
limit,
perPartitionLimit,
allowsFiltering,
bypassCache,
timeout);
}
@NonNull
@Override
public SelectFrom distinct() {
return new DefaultSelect(
keyspace,
table,
isJson,
true,
selectors,
relations,
groupByClauses,
orderings,
limit,
perPartitionLimit,
allowsFiltering,
bypassCache,
timeout);
}
@NonNull
@Override
public Select selector(@NonNull Selector selector) {
ImmutableList newSelectors;
if (selector == AllSelector.INSTANCE) {
// '*' cancels any previous one
newSelectors = SELECT_ALL;
} else if (SELECT_ALL.equals(selectors)) {
// previous '*' gets cancelled
newSelectors = ImmutableList.of(selector);
} else {
newSelectors = ImmutableCollections.append(selectors, selector);
}
return withSelectors(newSelectors);
}
@NonNull
@Override
public Select selectors(@NonNull Iterable additionalSelectors) {
ImmutableList.Builder newSelectors = ImmutableList.builder();
if (!SELECT_ALL.equals(selectors)) { // previous '*' gets cancelled
newSelectors.addAll(selectors);
}
for (Selector selector : additionalSelectors) {
if (selector == AllSelector.INSTANCE) {
throw new IllegalArgumentException("Can't pass the * selector to selectors()");
}
newSelectors.add(selector);
}
return withSelectors(newSelectors.build());
}
@NonNull
@Override
public Select as(@NonNull CqlIdentifier alias) {
if (SELECT_ALL.equals(selectors)) {
throw new IllegalStateException("Can't alias the * selector");
} else if (selectors.isEmpty()) {
throw new IllegalStateException("Can't alias, no selectors defined");
}
return withSelectors(ImmutableCollections.modifyLast(selectors, last -> last.as(alias)));
}
@NonNull
public Select withSelectors(@NonNull ImmutableList newSelectors) {
return new DefaultSelect(
keyspace,
table,
isJson,
isDistinct,
newSelectors,
relations,
groupByClauses,
orderings,
limit,
perPartitionLimit,
allowsFiltering,
bypassCache,
timeout);
}
@NonNull
@Override
public Select where(@NonNull Relation relation) {
return withRelations(ImmutableCollections.append(relations, relation));
}
@NonNull
@Override
public Select where(@NonNull Iterable additionalRelations) {
return withRelations(ImmutableCollections.concat(relations, additionalRelations));
}
@NonNull
public Select withRelations(@NonNull ImmutableList newRelations) {
return new DefaultSelect(
keyspace,
table,
isJson,
isDistinct,
selectors,
newRelations,
groupByClauses,
orderings,
limit,
perPartitionLimit,
allowsFiltering,
bypassCache,
timeout);
}
@NonNull
@Override
public Select groupBy(@NonNull Selector groupByClause) {
return withGroupByClauses(ImmutableCollections.append(groupByClauses, groupByClause));
}
@NonNull
@Override
public Select groupBy(@NonNull Iterable newGroupByClauses) {
return withGroupByClauses(ImmutableCollections.concat(groupByClauses, newGroupByClauses));
}
@NonNull
public Select withGroupByClauses(@NonNull ImmutableList newGroupByClauses) {
return new DefaultSelect(
keyspace,
table,
isJson,
isDistinct,
selectors,
relations,
newGroupByClauses,
orderings,
limit,
perPartitionLimit,
allowsFiltering,
bypassCache,
timeout);
}
@NonNull
@Override
public Select orderBy(@NonNull CqlIdentifier columnId, @NonNull ClusteringOrder order) {
return withOrderings(ImmutableCollections.append(orderings, columnId, order));
}
@NonNull
@Override
public Select orderByIds(@NonNull Map newOrderings) {
return withOrderings(ImmutableCollections.concat(orderings, newOrderings));
}
@NonNull
public Select withOrderings(@NonNull ImmutableMap newOrderings) {
return new DefaultSelect(
keyspace,
table,
isJson,
isDistinct,
selectors,
relations,
groupByClauses,
newOrderings,
limit,
perPartitionLimit,
allowsFiltering,
bypassCache,
timeout);
}
@NonNull
@Override
public Select limit(int limit) {
Preconditions.checkArgument(limit > 0, "Limit must be strictly positive");
return new DefaultSelect(
keyspace,
table,
isJson,
isDistinct,
selectors,
relations,
groupByClauses,
orderings,
limit,
perPartitionLimit,
allowsFiltering,
bypassCache,
timeout);
}
@NonNull
@Override
public Select limit(@Nullable BindMarker bindMarker) {
return new DefaultSelect(
keyspace,
table,
isJson,
isDistinct,
selectors,
relations,
groupByClauses,
orderings,
bindMarker,
perPartitionLimit,
allowsFiltering,
bypassCache,
timeout);
}
@NonNull
@Override
public Select perPartitionLimit(int perPartitionLimit) {
Preconditions.checkArgument(
perPartitionLimit > 0, "perPartitionLimit must be strictly positive");
return new DefaultSelect(
keyspace,
table,
isJson,
isDistinct,
selectors,
relations,
groupByClauses,
orderings,
limit,
perPartitionLimit,
allowsFiltering,
bypassCache,
timeout);
}
@NonNull
@Override
public Select perPartitionLimit(@Nullable BindMarker bindMarker) {
return new DefaultSelect(
keyspace,
table,
isJson,
isDistinct,
selectors,
relations,
groupByClauses,
orderings,
limit,
bindMarker,
allowsFiltering,
bypassCache,
timeout);
}
@NonNull
@Override
public Select allowFiltering() {
return new DefaultSelect(
keyspace,
table,
isJson,
isDistinct,
selectors,
relations,
groupByClauses,
orderings,
limit,
perPartitionLimit,
true,
bypassCache,
timeout);
}
@NonNull
@Override
public Select bypassCache() {
return new DefaultSelect(
keyspace,
table,
isJson,
isDistinct,
selectors,
relations,
groupByClauses,
orderings,
limit,
perPartitionLimit,
allowsFiltering,
true,
timeout);
}
@NonNull
@Override
public Select usingTimeout(@NonNull final CqlDuration timeout) {
return new DefaultSelect(
keyspace,
table,
isJson,
isDistinct,
selectors,
relations,
groupByClauses,
orderings,
limit,
perPartitionLimit,
allowsFiltering,
bypassCache,
timeout);
}
@NonNull
@Override
public Select usingTimeout(@NonNull BindMarker timeout) {
return new DefaultSelect(
keyspace,
table,
isJson,
isDistinct,
selectors,
relations,
groupByClauses,
orderings,
limit,
perPartitionLimit,
allowsFiltering,
bypassCache,
timeout);
}
@NonNull
@Override
public String asCql() {
StringBuilder builder = new StringBuilder();
builder.append("SELECT");
if (isJson) {
builder.append(" JSON");
}
if (isDistinct) {
builder.append(" DISTINCT");
}
CqlHelper.append(selectors, builder, " ", ",", null);
builder.append(" FROM ");
CqlHelper.qualify(keyspace, table, builder);
CqlHelper.append(relations, builder, " WHERE ", " AND ", null);
CqlHelper.append(groupByClauses, builder, " GROUP BY ", ",", null);
boolean first = true;
for (Map.Entry entry : orderings.entrySet()) {
if (first) {
builder.append(" ORDER BY ");
first = false;
} else {
builder.append(",");
}
builder.append(entry.getKey().asCql(true)).append(" ").append(entry.getValue().name());
}
if (limit != null) {
builder.append(" LIMIT ");
if (limit instanceof BindMarker) {
((BindMarker) limit).appendTo(builder);
} else {
builder.append(limit);
}
}
if (perPartitionLimit != null) {
builder.append(" PER PARTITION LIMIT ");
if (perPartitionLimit instanceof BindMarker) {
((BindMarker) perPartitionLimit).appendTo(builder);
} else {
builder.append(perPartitionLimit);
}
}
if (allowsFiltering) {
builder.append(" ALLOW FILTERING");
}
if (bypassCache) {
builder.append(" BYPASS CACHE");
}
if (timeout != null) {
builder.append(" USING TIMEOUT ");
if (timeout instanceof BindMarker) {
((BindMarker) timeout).appendTo(builder);
} else {
((CqlDuration) timeout).appendTo(builder);
}
}
return builder.toString();
}
@NonNull
@Override
public SimpleStatement build() {
return builder().build();
}
@NonNull
@Override
public SimpleStatement build(@NonNull Object... values) {
return builder().addPositionalValues(values).build();
}
@NonNull
@Override
public SimpleStatement build(@NonNull Map namedValues) {
SimpleStatementBuilder builder = builder();
for (Map.Entry entry : namedValues.entrySet()) {
builder.addNamedValue(entry.getKey(), entry.getValue());
}
return builder.build();
}
@NonNull
@Override
public SimpleStatementBuilder builder() {
// SELECT statements are always idempotent
return SimpleStatement.builder(asCql()).setIdempotence(true);
}
@Nullable
public CqlIdentifier getKeyspace() {
return keyspace;
}
@NonNull
public CqlIdentifier getTable() {
return table;
}
public boolean isJson() {
return isJson;
}
public boolean isDistinct() {
return isDistinct;
}
@NonNull
public ImmutableList getSelectors() {
return selectors;
}
@NonNull
public ImmutableList getRelations() {
return relations;
}
@NonNull
public ImmutableList getGroupByClauses() {
return groupByClauses;
}
@NonNull
public ImmutableMap getOrderings() {
return orderings;
}
@Nullable
public Object getLimit() {
return limit;
}
@Nullable
public Object getPerPartitionLimit() {
return perPartitionLimit;
}
public boolean allowsFiltering() {
return allowsFiltering;
}
public boolean bypassesCache() {
return bypassCache;
}
@Nullable
public Object getTimeout() {
return timeout;
}
@Override
public String toString() {
return asCql();
}
}