gt: 		String op = StringUtil.toLowerCase(expression.getOperator());
context: /**
   *
   * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either 
   * version 2.1 of the License, or (at your option) any later version.
   * 
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   * 
   * You should have received a copy of the GNU Lesser General Public 
   * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
   * 
   **/
  package lucee.runtime.db;
  
  import java.io.ByteArrayInputStream;
  import java.util.Iterator;
  import java.util.Map;
  import java.util.Vector;
  
  import lucee.commons.collection.MapFactory;
  import lucee.commons.lang.ExceptionUtil;
  import lucee.commons.lang.StringUtil;
  import lucee.commons.math.MathUtil;
  import lucee.runtime.PageContext;
  import lucee.runtime.exp.DatabaseException;
  import lucee.runtime.exp.PageException;
  import lucee.runtime.op.Caster;
  import lucee.runtime.op.OpUtil;
  import lucee.runtime.sql.old.ZConstant;
  import lucee.runtime.sql.old.ZExp;
  import lucee.runtime.sql.old.ZExpression;
  import lucee.runtime.sql.old.ZFromItem;
  import lucee.runtime.sql.old.ZOrderBy;
  import lucee.runtime.sql.old.ZQuery;
  import lucee.runtime.sql.old.ZSelectItem;
  import lucee.runtime.sql.old.ZqlParser;
  import lucee.runtime.type.Collection;
  import lucee.runtime.type.Collection.Key;
  import lucee.runtime.type.KeyImpl;
  import lucee.runtime.type.Query;
  import lucee.runtime.type.QueryColumn;
  import lucee.runtime.type.QueryImpl;
  import lucee.runtime.type.util.ListUtil;
  import lucee.runtime.type.util.QueryUtil;
  
  /**
   * 
   */
  public final class Executer {
  
  	/**
  	 * execute a SQL Statement against CFML Scopes
  	 * 
  	 * @param pc PageContext of the Request
  	 * @param sql
  	 * @param maxrows
  	 * @return result
  	 * @throws PageException
  	 */
  	public QueryImpl execute(Vector statements, PageContext pc, SQL sql, int maxrows) throws PageException {
  		// parse sql
  		if (statements.size() != 1) throw new DatabaseException("only one SQL Statement allowed at time", null, null, null);
  		ZQuery query = (ZQuery) statements.get(0);
  
  		// single table
  		if (query.getFrom().size() == 1) {
  			return testExecute(pc, sql, getSingleTable(pc, query), query, maxrows);
  
  		}
  		// multiple table
  		throw new DatabaseException("can only work with single tables yet", null, null, null);
  
  	}
  
  	public QueryImpl execute(PageContext pc, SQL sql, String prettySQL, int maxrows) throws PageException {
  		if (StringUtil.isEmpty(prettySQL)) prettySQL = SQLPrettyfier.prettyfie(sql.getSQLString());
  
  		ZqlParser parser = new ZqlParser(new ByteArrayInputStream(prettySQL.getBytes()));
  		Vector statements;
  		try {
  			statements = parser.readStatements();
  		}
  		catch (Throwable t) {
  			ExceptionUtil.rethrowIfNecessary(t);
  			throw Caster.toPageException(t);
  		}
  		return execute(statements, pc, sql, maxrows);
  
  	}
  
  	private QueryImpl testExecute(PageContext pc, SQL sql, Query qr, ZQuery query, int maxrows) throws PageException {
  
  		int recCount = qr.getRecordcount();
  		Vector vSelects = query.getSelect();
  		int selCount = vSelects.size();
  
  		Map<Collection.Key, Object> selects = MapFactory.<Collection.Key, Object>getConcurrentMap();
  		boolean isSMS = false;
  		// headers
  		for (int i = 0; i < selCount; i++) {
  			ZSelectItem select = (ZSelectItem) vSelects.get(i);
  
  			if (select.isWildcard() || (isSMS = select.getColumn().equals(SQLPrettyfier.PLACEHOLDER_ASTERIX))) {
  
  				if (!isSMS && !select.getColumn().equals("*")) throw new DatabaseException("can't execute this type of query at the moment", null, sql, null);
  				// Collection.Key[] keys = qr.keys();
  				Iterator<Key> it = qr.keyIterator();
  				Key k;
  				while (it.hasNext()) {
  					k = it.next();
  					selects.put(k, k.getString());
  				}
  				isSMS = false;
  			}
  			else {
  				// if(SQLPrettyfier.PLACEHOLDER_COUNT.equals(select.getAlias())) select.setAlias("count");
  				// if(SQLPrettyfier.PLACEHOLDER_COUNT.equals(select.getColumn())) select.setExpression(new
  				// ZConstant("count",ZConstant.COLUMNNAME));
  
  				String alias = select.getAlias();
  				String column = select.getColumn();
  				if (alias == null) alias = column;
  				alias = alias.toLowerCase();
  
  				selects.put(KeyImpl.init(alias), select);
  			}
  		}
  		Key[] headers = selects.keySet().toArray(new Collection.Key[selects.size()]);
  
  		// aHeaders.toArray(new String[aHeaders.size()]);
  		QueryImpl rtn = new QueryImpl(headers, 0, "query", sql);
  
  		// loop records
  		Vector orders = query.getOrderBy();
  		ZExp where = query.getWhere();
  		// print.out(headers);
  		// int newRecCount=0;
  		boolean hasMaxrow = maxrows > -1 && (orders == null || orders.size() == 0);
  		for (int row = 1; row <= recCount; row++) {
  			sql.setPosition(0);
  			if (hasMaxrow && maxrows <= rtn.getRecordcount()) break;
  			boolean useRow = where == null || Caster.toBooleanValue(executeExp(pc, sql, qr, where, row));
  			if (useRow) {
  
  				rtn.addRow(1);
  				for (int cell = 0; cell < headers.length; cell++) {
  					Object value = selects.get(headers[cell]);
  
  					rtn.setAt(headers[cell], rtn.getRecordcount(), getValue(pc, sql, qr, row, headers[cell], value));
  				}
  			}
  		}
  
  		// Group By
  		if (query.getGroupBy() != null) throw new DatabaseException("group by are not supported at the moment", null, sql, null);
  
  		// Order By
  		if (orders != null && orders.size() > 0) {
  
  			int len = orders.size();
  			for (int i = len - 1; i >= 0; i--) {
  				ZOrderBy order = (ZOrderBy) orders.get(i);
  				ZConstant name = (ZConstant) order.getExpression();
  				rtn.sort(name.getValue().toLowerCase(), order.getAscOrder() ? Query.ORDER_ASC : Query.ORDER_DESC);
  			}
  			if (maxrows > -1) {
  				rtn.cutRowsTo(maxrows);
  			}
  		}
  		// Distinct
  		if (query.isDistinct()) {
  			String[] keys = rtn.getColumns();
  			QueryColumn[] columns = new QueryColumn[keys.length];
  			for (int i = 0; i < columns.length; i++) {
  				columns[i] = rtn.getColumn(keys[i]);
  			}
  
  			int i;
  			outer: for (int row = rtn.getRecordcount(); row > 1; row--) {
  				for (i = 0; i < columns.length; i++) {
  					if (!OpUtil.equals(pc, QueryUtil.getValue(columns[i], row), QueryUtil.getValue(columns[i], row - 1), true)) continue outer;
  				}
  				rtn.removeRow(row);
  			}
  
  		}
  		// UNION // TODO support it
  		ZExpression set = query.getSet();
  		if (set != null) {
  			ZExp op = set.getOperand(0);
  			if (op instanceof ZQuery) throw new DatabaseException("union is not supported at the moment", null, sql, null);
  			// getInvokedTables((ZQuery)op, tablesNames);
  		}
  
  		return rtn;
  	}
  
  	/**
  	 * return value
  	 * 
  	 * @param sql
  	 * @param querySource
  	 * @param row
  	 * @param key
  	 * @param value
  	 * @return value
  	 * @throws PageException
  	 */
  	private Object getValue(PageContext pc, SQL sql, Query querySource, int row, Collection.Key key, Object value) throws PageException {
  		if (value instanceof ZSelectItem) return executeExp(pc, sql, querySource, ((ZSelectItem) value).getExpression(), row);
  		return querySource.getAt(key, row);
  	}
  
  	/**
  	 * @param pc Page Context of the Request
  	 * @param query ZQLQuery
  	 * @return Query
  	 * @throws PageException
  	 */
  	private Query getSingleTable(PageContext pc, ZQuery query) throws PageException {
  		return Caster.toQuery(pc.getVariable(((ZFromItem) query.getFrom().get(0)).getFullName()));
  	}
  
  	/**
  	 * Executes a ZEXp
  	 * 
  	 * @param sql
  	 * @param qr Query Result
  	 * @param exp expression to execute
  	 * @param row current row of resultset
  	 * @return result
  	 * @throws PageException
  	 */
  	private Object executeExp(PageContext pc, SQL sql, Query qr, ZExp exp, int row) throws PageException {
  		if (exp instanceof ZConstant) return executeConstant(sql, qr, (ZConstant) exp, row);
  		else if (exp instanceof ZExpression) return executeExpression(pc, sql, qr, (ZExpression) exp, row);
  		throw new DatabaseException("unsupported sql statement [" + exp + "]", null, sql, null);
  
  	}
  
  	/**
  	 * Executes an Expression
  	 * 
  	 * @param sql
  	 * @param qr
  	 * @param expression
  	 * @param row
  	 * @return result
  	 * @throws PageException
  	 */
  	private Object executeExpression(PageContext pc, SQL sql, Query qr, ZExpression expression, int row) throws PageException {
  