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

com.scalar.db.sql.statement.SelectStatement Maven / Gradle / Ivy

There is a newer version: 3.14.0
Show newest version
package com.scalar.db.sql.statement;

import com.google.common.collect.ImmutableList;
import com.scalar.db.sql.AndPredicateList;
import com.scalar.db.sql.BindMarker;
import com.scalar.db.sql.Join;
import com.scalar.db.sql.NamedBindMarker;
import com.scalar.db.sql.OrPredicateList;
import com.scalar.db.sql.Ordering;
import com.scalar.db.sql.PositionalBindMarker;
import com.scalar.db.sql.Predicate;
import com.scalar.db.sql.Projection;
import com.scalar.db.sql.TableRef;
import com.scalar.db.sql.Term;
import com.scalar.db.sql.Value;
import com.scalar.db.sql.common.SqlError;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

@Immutable
public class SelectStatement
    implements DmlStatement,
        BindableStatement,
        NamespaceNameOmittable {

  public final TableRef table;
  @Nullable public final String alias;
  public final ImmutableList projections;
  public final ImmutableList joins;
  public final ImmutableList andPredicateLists; // for DNF, empty when using CNF
  public final ImmutableList orPredicateLists; // for CNF, empty when using DNF
  public final ImmutableList orderings;
  public final Term limit;

  private SelectStatement(
      ImmutableList projections,
      TableRef table,
      @Nullable String alias,
      ImmutableList joins,
      ImmutableList andPredicateLists,
      ImmutableList orPredicateLists,
      ImmutableList orderings,
      Term limit) {
    this.table = Objects.requireNonNull(table);
    this.alias = alias;
    this.projections = Objects.requireNonNull(projections);
    this.joins = Objects.requireNonNull(joins);
    this.andPredicateLists = Objects.requireNonNull(andPredicateLists);
    this.orPredicateLists = Objects.requireNonNull(orPredicateLists);
    this.orderings = Objects.requireNonNull(orderings);
    this.limit = Objects.requireNonNull(limit);
  }

  @Override
  public String toSql() {
    StringBuilder builder = new StringBuilder("SELECT ");

    if (projections.isEmpty()) {
      builder.append("*");
    } else {
      boolean first = true;
      for (Projection projection : projections) {
        if (!first) {
          builder.append(',');
        } else {
          first = false;
        }
        StatementUtils.appendColumn(builder, projection.column);
        if (projection.alias != null) {
          builder.append(" AS ");
          StatementUtils.appendObjectName(builder, projection.alias);
        }
      }
    }

    builder.append(" FROM ");
    StatementUtils.appendTable(builder, table);

    if (alias != null) {
      builder.append(" AS ");
      StatementUtils.appendObjectName(builder, alias);
    }

    for (Join join : joins) {
      builder.append(' ');

      switch (join.joinType) {
        case INNER_JOIN:
          builder.append("INNER JOIN ");
          break;
        case LEFT_OUTER_JOIN:
          builder.append("LEFT OUTER JOIN ");
          break;
        case RIGHT_OUTER_JOIN:
          builder.append("RIGHT OUTER JOIN ");
          break;
        default:
          throw new AssertionError("Unknown join type: " + join.joinType);
      }

      StatementUtils.appendTable(builder, join.table);
      if (join.alias != null) {
        builder.append(" AS ");
        StatementUtils.appendObjectName(builder, join.alias);
      }

      builder.append(" ON ");

      StatementUtils.appendJoinPredicates(builder, join.joinPredicates);
    }

    StatementUtils.appendWhere(builder, andPredicateLists, orPredicateLists);

    if (!orderings.isEmpty()) {
      builder.append(" ORDER BY ");

      boolean first = true;
      for (Ordering ordering : orderings) {
        if (!first) {
          builder.append(',');
        } else {
          first = false;
        }
        StatementUtils.appendColumn(builder, ordering.column);
        builder.append(' ').append(ordering.order);
      }
    }

    if (limit instanceof PositionalBindMarker) {
      builder.append(" LIMIT ").append('?');
    } else if (limit instanceof NamedBindMarker) {
      builder.append(" LIMIT ").append(":");
      StatementUtils.appendObjectName(builder, ((NamedBindMarker) limit).name);
    } else {
      assert limit instanceof Value;

      Value limitValue = (Value) limit;
      assert limitValue.type == Value.Type.INT;
      assert limitValue.value != null;

      int intLimit = (int) limitValue.value;
      if (intLimit > 0) {
        builder.append(" LIMIT ").append(intLimit);
      }
    }

    return builder.toString();
  }

  @Override
  public SelectStatement bind(List positionalValues) {
    Iterator positionalValueIterator = positionalValues.iterator();
    return create(
        projections,
        table,
        alias,
        joins,
        StatementUtils.bindPredicateLists(andPredicateLists, positionalValueIterator),
        StatementUtils.bindPredicateLists(orPredicateLists, positionalValueIterator),
        orderings,
        bindLimit(limit, positionalValueIterator));
  }

  private Term bindLimit(Term limit, Iterator positionalValueIterator) {
    if (positionalValueIterator.hasNext() && limit instanceof BindMarker) {
      if (limit instanceof NamedBindMarker) {
        throw new IllegalArgumentException(SqlError.NAMED_BIND_MARKER_NOT_ALLOWED.buildMessage());
      }
      return positionalValueIterator.next();
    }
    return limit;
  }

  @Override
  public SelectStatement bind(Map namedValues) {
    return create(
        projections,
        table,
        alias,
        joins,
        StatementUtils.bindPredicateLists(andPredicateLists, namedValues),
        StatementUtils.bindPredicateLists(orPredicateLists, namedValues),
        orderings,
        bindLimit(limit, namedValues));
  }

  private Term bindLimit(Term limit, Map namedValues) {
    if (limit instanceof BindMarker) {
      if (limit instanceof PositionalBindMarker) {
        throw new IllegalArgumentException(
            SqlError.POSITIONAL_BIND_MARKER_NOT_ALLOWED.buildMessage());
      }
      String name = ((NamedBindMarker) limit).name;
      if (namedValues.containsKey(name)) {
        return namedValues.get(name);
      } else {
        return limit;
      }
    }
    return limit;
  }

  @Override
  public  R accept(StatementVisitor visitor, C context) {
    return visitor.visit(this, context);
  }

  @Override
  public  R accept(DmlStatementVisitor visitor, C context) {
    return visitor.visit(this, context);
  }

  @Override
  public boolean namespaceNameOmitted() {
    if (table.namespaceName == null) {
      return true;
    }
    for (Join join : joins) {
      if (join.table.namespaceName == null) {
        return true;
      }
    }
    return false;
  }

  @Override
  public SelectStatement setNamespaceNameIfOmitted(String namespaceName) {
    if (namespaceNameOmitted()) {
      return create(
          projections,
          table.namespaceName == null ? TableRef.of(namespaceName, table.tableName) : table,
          alias,
          joins.stream()
              .map(
                  j ->
                      j.table.namespaceName == null
                          ? Join.create(
                              j.joinType,
                              TableRef.of(namespaceName, j.table.tableName),
                              j.alias,
                              j.joinPredicates)
                          : j)
              .collect(ImmutableList.toImmutableList()),
          andPredicateLists,
          orPredicateLists,
          orderings,
          limit);
    }
    return this;
  }

  @Override
  public String toString() {
    return toSql();
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (!(o instanceof SelectStatement)) {
      return false;
    }
    SelectStatement statement = (SelectStatement) o;
    return Objects.equals(table, statement.table)
        && Objects.equals(alias, statement.alias)
        && Objects.equals(projections, statement.projections)
        && Objects.equals(joins, statement.joins)
        && Objects.equals(andPredicateLists, statement.andPredicateLists)
        && Objects.equals(orPredicateLists, statement.orPredicateLists)
        && Objects.equals(orderings, statement.orderings)
        && Objects.equals(limit, statement.limit);
  }

  @Override
  public int hashCode() {
    return Objects.hash(
        table, alias, projections, joins, andPredicateLists, orPredicateLists, orderings, limit);
  }

  public static SelectStatement create(
      ImmutableList projections,
      TableRef table,
      @Nullable String alias,
      ImmutableList joins,
      ImmutableList predicates,
      ImmutableList orderings,
      Term limit) {
    return create(
        projections,
        table,
        alias,
        joins,
        predicates.isEmpty()
            ? ImmutableList.of()
            : ImmutableList.of(AndPredicateList.predicates(predicates).build()),
        ImmutableList.of(),
        orderings,
        limit);
  }

  public static SelectStatement create(
      ImmutableList projections,
      TableRef table,
      @Nullable String alias,
      ImmutableList joins,
      ImmutableList andPredicateLists,
      ImmutableList orPredicateLists,
      ImmutableList orderings,
      Term limit) {
    if (!andPredicateLists.isEmpty() && !orPredicateLists.isEmpty()) {
      throw new IllegalArgumentException(
          "Either andPredicateLists or orPredicateLists can be used");
    }
    return new SelectStatement(
        projections, table, alias, joins, andPredicateLists, orPredicateLists, orderings, limit);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy