package ModSQL;
import java.sql.*;
import java.util.*;
 
/* $Id: Operator_Exists.java,v 1.5 2003/07/09 06:54:49 cvs Exp $
 *
 * Copyright (c) 2003 Chris Studholme <chris.studholme@utoronto.ca>
 *
 * May be copied or modified under the terms of the GNU General Public
 * License.  See COPYING for more information.
 */

/**
 * <p>Implementation of EXISTS operator.
 *
 * @author chris.studholme@utoronto.ca
 */
final class Operator_Exists extends Operator {

  /** Table to scan. */
  private Table table = null;
  /** True if operator returns a constant value (table is constant). */
  private boolean is_constant;
  /** If is_constant, this variable holds the constant result. */
  private Boolean constant_result;


  /**
   * Specify the table to be scanned.
   *
   * @param table table to scan
   */
  public void setTable(Table table) {
    this.table = table;
  }

  /**
   * Since the table is not a regular parameter, this method is need to
   * ensure the table can register itself with objects as needed.
   *
   * @param o object to register with
   * @throws SQLException if a database error occurs
   */
  public void registerWith(Object o) throws SQLException {
    super.registerWith(o);
    if (table!=null)
      table.registerWith(o);
  }

  /**
   * <p>Prepare the function for use.  This method ensures that there are
   * no parameters (except the table).  If the table is constant, it is
   * checked for rows and the result is stored.
   *
   * @throws SQLException if the parameters are invalid
   */
  public void optimize() throws SQLException {
    super.optimize();

    // check number of parameters
    if (parameters.length!=0) 
      throw new SQLException("EXISTS does not accept non-table parameters");

    // make sure we have a table
    if (table==null)
      throw new SQLException("table required");
    table.optimize();

    is_constant = table.isConstant();
    if (is_constant) {
      if (DriverConfig.debugLevel>0)
	System.err.println("Operator_Exists: evaluating constant");
      try {
        table.beforeFirst();
	constant_result = new Boolean(table.next());
      }
      catch (EndOfTable e) {
        throw new SQLExceptionWithCause("SOFTWARE BUG",e);
      }
    }
  }
  
  /**
   * Returns "#EXISTS".
   *
   * @return name of function 
   */
  public String functionName() {
    return "#EXISTS";
  }

  /**
   * Returns table as string if set.
   *
   * @return table as string
   */
  public String postfixParameters() {
    return table!=null ? table.toString() : "";
  }

  /****************  result meta-data  ****************/

  /**
   * Returns true if the table is constant.
   *
   * @return true if the value returned is constant
   */
  public boolean isConstant() {
    return is_constant;
  }

  /**
   * Returns Types.BIT.
   *
   * @return SQL type of data to be returned
   */
  public int getSQLType() {
    return Types.BIT;
  }


  /**
   * Returns -1 as this operator will never return a String object.
   *
   * @return maximum size of String returned or -1 if unknown
   */
  public int getMaxResultSize() {
    return -1;
  }


  /****************  evaluation methods  ****************/


  /**
   * Evaluate table and compute the function.
   *
   * @param aggregate ignored
   * @return result object
   * @throws SQLException if a database-access error occurs
   * @throws EndOfTable if thrown by a parameter
   */
  public Object evaluate(boolean aggregate) throws SQLException, EndOfTable {
    if (is_constant)
      return constant_result;
    if (DriverConfig.debugLevel>0)
      System.err.println("Operator_Exists: reseting table");
    table.beforeFirst();
    if (DriverConfig.debugLevel>0)
      System.err.println("Operator_Exists: advancing to first row");
    boolean result = table.next();
    if (DriverConfig.debugLevel>0)
      System.err.println("Operator_Exists: result="+result);
    return new Boolean(result);
  }

  /**
   * Evaluate table and compute the function.
   *
   * @param match_op how the value should be matched
   * @param match_value desired value
   * @return result object
   * @throws SQLException if a database-access error occurs
   * @throws EndOfTable if thrown by a parameter
   */
  public Object evaluate(int match_op, Object match_value)
    throws SQLException, EndOfTable {
    // can we do better here?
    return evaluate(false);
  }
};
