All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.datastax.driver.core.querybuilder.Select 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.driver.core.querybuilder;

import com.datastax.driver.core.AbstractTableMetadata;
import com.datastax.driver.core.CodecRegistry;
import com.datastax.driver.core.ColumnMetadata;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.MaterializedViewMetadata;
import com.datastax.driver.core.Metadata;
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 List groupByColumnNames;
  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(
      AbstractTableMetadata table, List columnNames, boolean isDistinct, boolean isJson) {
    this(
        Metadata.quoteIfNecessary(table.getKeyspace().getName()),
        Metadata.quoteIfNecessary(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 (groupByColumnNames != null) {
      builder.append(" GROUP BY ");
      Utils.joinAndAppendNames(builder, codecRegistry, groupByColumnNames);
    }

    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 {@code 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 {@code 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 {@code ORDER BY} clause to this statement. * * @param orderings the orderings to define for this query. * @return this statement. * @throws IllegalStateException if an {@code 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"); if (orderings.length == 0) throw new IllegalArgumentException( "Invalid ORDER BY argument, the orderings must not be empty."); this.orderings = Arrays.asList(orderings); for (Ordering ordering : orderings) checkForBindMarkers(ordering); return this; } /** * Adds a {@code GROUP BY} clause to this statement. * *

Note: support for {@code GROUP BY} clause is only available from Cassandra 3.10 onwards. * * @param columns the columns to group by. * @return this statement. * @throws IllegalStateException if a {@code GROUP BY} clause has already been provided. */ public Select groupBy(Object... columns) { if (this.groupByColumnNames != null) throw new IllegalStateException("A GROUP BY clause has already been provided"); this.groupByColumnNames = Arrays.asList(columns); 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 {@code ALLOW FILTERING} directive to this statement. * * @return this statement. */ public Select allowFiltering() { allowFiltering = true; return this; } /** The {@code WHERE} clause of a {@code SELECT} statement. */ public static class Where extends BuiltStatement.ForwardingStatement