ostream &operator<< (ostream &output, const CalpontSelectExecutionPlan &cep)
{
	output << ">SELECT " ;
	if (cep.distinct())
		output << "DISTINCT ";
	output << "limit: " << cep.limitStart() << " - " << cep.limitNum() << endl; 
	
	switch (cep.location())
	{
		case CalpontSelectExecutionPlan::MAIN:
	    output << "MAIN" << endl;
	    break;
    case CalpontSelectExecutionPlan::FROM:
        output << "FROM" << endl;
        break;
    case CalpontSelectExecutionPlan::WHERE:
        output << "WHERE" << endl;
        break;
    case CalpontSelectExecutionPlan::HAVING:
        output << "HAVING" << endl;
        break;
   }	    
	
	// Returned Column
	CalpontSelectExecutionPlan::ReturnedColumnList retCols = cep.returnedCols();	
	output << ">>Returned Columns" << endl;
	uint seq = 0;
	for (unsigned int i = 0; i < retCols.size(); i++)
	{
		output << *retCols[i] << endl;
		if (retCols[i]->colSource() & SELECT_SUB)
		{
			output << "select sub -- " << endl;
			CalpontSelectExecutionPlan *plan = dynamic_cast<CalpontSelectExecutionPlan*>(cep.fSelectSubList[seq++].get());
			if (plan)
				output << "{" << *plan << "}" << endl; 
		}
	}
	
	// From Clause
	CalpontSelectExecutionPlan::TableList tables = cep.tableList();	
	output << ">>From Tables" << endl;
	seq = 0;
	for (unsigned int i = 0; i < tables.size(); i++)
	{
		// derived table
		if (tables[i].schema.length() == 0 && tables[i].table.length() == 0)
		{
			output << "derived table - " << tables[i].alias << endl;
			CalpontSelectExecutionPlan *plan = dynamic_cast<CalpontSelectExecutionPlan*>(cep.fDerivedTableList[seq++].get());
			if (plan)
				output << "{" << *plan << "}" << endl; 
		}
		else
		{
			output << tables[i] << endl;
		}
	}
	
	// Filters
	output << ">>Filters" << endl;
  if (cep.filters() != 0)
  	cep.filters()->walk (ParseTree::print, output);
  else
  	output << "empty filter tree" << endl;
    
	// Group by columns
	CalpontSelectExecutionPlan::GroupByColumnList groupByCols = cep.groupByCols();
	if (groupByCols.size() > 0)
	{
  	output << ">>Group By Columns" << endl;
  	for (unsigned int i = 0; i < groupByCols.size(); i++)
    	output << *groupByCols[i] << endl;
	}
    
	// Having
	if (cep.having() != 0)
	{
		output << ">>Having" << endl;
		cep.having()->walk (ParseTree::print, output);
	}
    
	// Order by columns
	CalpontSelectExecutionPlan::OrderByColumnList orderByCols = cep.orderByCols();
	if (orderByCols.size() > 0)
	{
		output << ">>Order By Columns" << endl;
		for (unsigned int i = 0; i < orderByCols.size(); i++)
			output << *orderByCols[i] << endl;
	}   
	output << "SessionID: " << cep.fSessionID << endl;
	output << "TxnID: " << cep.fTxnID << endl;
	output << "VerID: " << cep.fVerID << endl;
	output << "TraceFlags: " << cep.fTraceFlags << endl;
	output << "StatementID: " << cep.fStatementID << endl;
	output << "DistUnionNum: " << (int)cep.fDistinctUnionNum << endl;
	output << "Limit: " << cep.fLimitStart << " - " << cep.fLimitNum << endl;
	output << "String table threshold: " << cep.fStringTableThreshold << endl;
	  
	output << "--- Column Map ---" << endl;
	CalpontSelectExecutionPlan::ColumnMap::const_iterator iter;
	for (iter = cep.columnMap().begin(); iter != cep.columnMap().end(); iter++)
		output << (*iter).first << " : " << (*iter).second << endl;
	
	return output;
}