package ModSQL;
import java.sql.*;

/* $Id: Aggregate_Count.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.
 */

/**
 * Implementation of SQL aggregate function COUNT().
 *
 * @author chris.studholme@utoronto.ca
 */
final class Aggregate_Count extends AbstractAggregate {

  /** True if we are counting rows, false to count non-null values. */
  private boolean count_all=false;
  /** Current count. */
  private long count=0;

  /**
   * Returns a new instance of this class if name is "count".
   *
   * @param name name of function to find
   * @return instance of this class or null if name does not match
   */
  public static Function forName(String name) {
    if (name.equals("count"))
      return new Aggregate_Count();
    return null;
  }

  /**
   * Adds a value (function) to the list of parameters maintained by this
   * function.
   *
   * @param item function to add
   * @throws SQLException if too many parameters have been added
   */
  public void addParameter(Function item) throws SQLException {
    if (count_all)
      throw new SQLException("COUNT only supports one parameter");
    if (item instanceof IndirectFunction &&
	((IndirectFunction)item).getDescription().equals("*"))
      count_all=true;
    else
      super.addParameter(item);
  }

  /**
   * <p>Prepare the function for use.  COUNT() requires exactly one 
   * parameter, unless count_all, in which case no parameters are acceptable.
   * If we are doing COUNT(DISTINCT ...), we require a parameter.
   *
   * @throws SQLException if the parameters are invalid
   */
  public void optimize() throws SQLException {
    super.optimize();
    if (parameters.length>(count_all?0:1))
      throw new SQLException("COUNT only supports one parameter");
    if (distinct) {
      if (count_all) 
	throw new SQLException("cannot count all distinct rows");
      if (parameters.length==0)
	throw new SQLException("COUNT DISTINCT requires a parameter");
    }
  }

  /**
   * Returns "COUNT".
   *
   * @return name of function
   */
  public String functionName() {
    return "COUNT";
  }

  /**
   * COUNT always returns BIGINT integers.
   *
   * @return SQL type of data to be returned
   */
  public int getSQLType() {
    return Types.BIGINT;
  }

  /**
   * Returns -1.
   *
   * @return maximum size of String returned or -1 if unknown
   */
  public int getMaxResultSize() {
    return -1;
  }

  /**
   * Resets the current count to zero.
   *
   * @throws SQLException if a database error occurs
   */
  public void reset() throws SQLException {
    super.reset();
    count=0;
  }

  /**
   * <p>Evaluate the parameter and update the count.  If aggregate is
   * true, the current count is returned and the parameter is not evaluated.
   * This method returns null if aggregate is false.
   *
   * @param aggregate true to return current count
   * @return Long object if aggregate is true, null otherwise
   * @throws SQLException if a database-access error occurs
   * @throws EndOfTable if thrown by a parameter
   */
  public Object evaluate(boolean aggregate) throws SQLException, EndOfTable {
    if (aggregate)
      return new Long(count);

    if (count_all)
      ++count;
    
    else if (parameters.length>0) {
      Object value = parameters[0].evaluate(false);
      if (value!=null && (!distinct || addDistinctValue(value))) 
	++count;
    }

    return null;
  }
};

