package io.ebeaninternal.server.expression;

import io.ebean.event.BeanQueryRequest;
import io.ebeaninternal.api.ManyWhereJoins;
import io.ebeaninternal.api.NaturalKeyQueryData;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.api.SpiExpressionValidation;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.query.CQuery;

import java.io.IOException;
import java.util.List;

class ExistsQueryExpression implements SpiExpression, UnsupportedDocStoreExpression {

  protected final boolean not;

  protected final SpiQuery<?> subQuery;

  protected List<Object> bindParams;

  protected String sql;

  ExistsQueryExpression(SpiQuery<?> subQuery, boolean not) {
    this.subQuery = subQuery;
    this.not = not;
  }

  ExistsQueryExpression(boolean not, String sql, List<Object> bindParams) {
    this.not = not;
    this.sql = sql;
    this.bindParams = bindParams;
    this.subQuery = null;
  }

  @Override
  public boolean naturalKey(NaturalKeyQueryData<?> data) {
    // can't use naturalKey cache
    return false;
  }

  @Override
  public void simplify() {
    // do nothing
  }

  @Override
  public void writeDocQuery(DocQueryContext context) throws IOException {
    throw new IllegalStateException("Not supported");
  }

  @Override
  public Object getIdEqualTo(String idName) {
    // always return null for this expression
    return null;
  }

  @Override
  public void prepareExpression(BeanQueryRequest<?> request) {

    CQuery<?> subQuery = compileSubQuery(request);
    this.bindParams = subQuery.getPredicates().getWhereExprBindValues();
    this.sql = subQuery.getGeneratedSql().replace('\n', ' ');
  }

  @Override
  public SpiExpression copyForPlanKey() {
    return this;
  }

  
Compile/build the sub query.
/** * Compile/build the sub query. */
protected CQuery<?> compileSubQuery(BeanQueryRequest<?> queryRequest) { SpiEbeanServer ebeanServer = (SpiEbeanServer) queryRequest.getEbeanServer(); return ebeanServer.compileQuery(subQuery, queryRequest.getTransaction()); } @Override public void queryPlanHash(StringBuilder builder) { builder.append("ExistsQuery[").append(" not:").append(not); builder.append(" sql:").append(sql).append(" ?:").append(bindParams.size()).append("]"); } @Override public int queryBindHash() { return subQuery.queryBindHash(); } @Override public void addSql(SpiExpressionRequest request) { if (not) { request.append(" not"); } request.append(" exists ("); request.append(sql); request.append(")"); } @Override public void addBindValues(SpiExpressionRequest request) { for (Object bindParam : bindParams) { request.addBindValue(bindParam); } } @Override public boolean isSameByBind(SpiExpression other) { ExistsQueryExpression that = (ExistsQueryExpression) other; if (this.bindParams.size() != that.bindParams.size()) { return false; } for (int i = 0; i < bindParams.size(); i++) { if (!bindParams.get(i).equals(that.bindParams.get(i))) { return false; } } return true; } @Override public String nestedPath(BeanDescriptor<?> desc) { return null; } @Override public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins) { // Nothing to do for exists expression } @Override public void validate(SpiExpressionValidation validation) { // Nothing to do for exists expression } }