package railo.runtime.sql;

import java.util.ArrayList;
import java.util.List;

import railo.commons.lang.ParserString;
import railo.commons.lang.types.RefBoolean;
import railo.commons.lang.types.RefBooleanImpl;
import railo.runtime.exp.PageException;
import railo.runtime.op.Caster;
import railo.runtime.sql.exp.Column;
import railo.runtime.sql.exp.ColumnExpression;
import railo.runtime.sql.exp.Expression;
import railo.runtime.sql.exp.op.Operation;
import railo.runtime.sql.exp.op.Operation1;
import railo.runtime.sql.exp.op.Operation2;
import railo.runtime.sql.exp.op.Operation3;
import railo.runtime.sql.exp.op.OperationN;
import railo.runtime.sql.exp.value.ValueBoolean;
import railo.runtime.sql.exp.value.ValueDate;
import railo.runtime.sql.exp.value.ValueNull;
import railo.runtime.sql.exp.value.ValueNumber;
import railo.runtime.sql.exp.value.ValueString;

public class SelectParser {

	
	
	/*
SELECT [{LIMIT <offset> <limit> | TOP <limit>}[1]][ALL | DISTINCT]
{ <selectExpression> | table.* | * } [, ...]
[INTO [CACHED | TEMP  | TEXT][1] newTable]
FROM tableList
[WHERE Expression]
[GROUP BY Expression [, ...]]
[HAVING Expression]
[{ UNION [ALL | DISTINCT] | {MINUS [DISTINCT] | EXCEPT [DISTINCT] } | INTERSECT [DISTINCT] } selectStatement]
[ORDER BY orderExpression [, ...]]
[LIMIT <limit> [OFFSET <offset>]];
	 * */
	
	private int columnIndex=0;

	// select <select-statement> from <tables> where <where-statement> 
	public Selects parse(String sql) throws SQLParserException {
		columnIndex=0;
		ParserString raw = new ParserString(sql.trim());
		Selects selects = new Selects();
		Select select = new Select();
		
		boolean runAgain=false;
		do {
			
			// select
			if(!raw.forwardIfCurrentAndNoWordNumberAfter("select")) throw new SQLParserException("missing select");
			raw.removeSpace();
	
			// top
			if(raw.forwardIfCurrentAndNoWordNumberAfter("top")) {
				raw.removeSpace();
				ValueNumber number = number(raw);
				if(number==null)throw new SQLParserException("missing top number");
				select.setTop(number);
				raw.removeSpace();
			}
	
			// distinct
			if(raw.forwardIfCurrentAndNoWordNumberAfter("distinct")) {
				select.setDistinct(true);
				raw.removeSpace();
			}
			
			// all
			if(raw.forwardIfCurrentAndNoWordNumberAfter("all")) {
				select.setDistinct(false);
				raw.removeSpace();
			}
			
			// select expression
			selectExpressions(raw,select);
			raw.removeSpace();
			
			// from
			if(!raw.forwardIfCurrentAndNoWordNumberAfter("from")) throw new SQLParserException("missing from");
			tableList(raw,select);
			raw.removeSpace();
			
			// where
			if(raw.forwardIfCurrentAndNoWordNumberAfter("where")) whereExpressions(raw,select);
			raw.removeSpace();
			
			// group by
			if(raw.forwardIfCurrentAndNoWordNumberAfter("group by")) {
				groupByExpressions(raw,select);
				raw.removeSpace();
				
				// having
				if(raw.forwardIfCurrentAndNoWordNumberAfter("having")) havingExpressions(raw,select);
				raw.removeSpace();
			}
			selects.addSelect(select);
			
			runAgain=false;
			// union
			if(raw.forwardIfCurrentAndNoWordNumberAfter("union")) {
				select = new Select();
				raw.removeSpace();
				if(raw.forwardIfCurrentAndNoWordNumberAfter("all")){
					raw.removeSpace();
					select.setUnionDistinct(false);
				}
				else if(raw.forwardIfCurrentAndNoWordNumberAfter("distinct")){
					raw.removeSpace();
					select.setUnionDistinct(true);
				}
				else select.setDistinct(true);
				raw.removeSpace();
				runAgain=true;
			}
	
		}
		while(runAgain);
		
		          
		// order by
		if(raw.forwardIfCurrentAndNoWordNumberAfter("order by")) orderByExpressions(raw,selects);
		raw.removeSpace();
		
		if(raw.forwardIfCurrent(';'))raw.removeSpace();
		
		if(!raw.isAfterLast()) throw new SQLParserException("can not read the full sql statement (stop at:"+raw.getCurrent()+")");
		return selects;
	}

	private void orderByExpressions(ParserString raw, Selects selects) throws SQLParserException {
		Expression exp=null;
		do {
			raw.removeSpace();
			//print.out(raw.getCurrent());
			exp=expression(raw);
			if(!(exp instanceof Column)) throw new SQLParserException("invalid order by part of query");
			Column col=(Column) exp;
			
			raw.removeSpace();
			if(raw.forwardIfCurrent("desc")) col.setDirectionBackward(true);
			if(raw.forwardIfCurrent("asc"))  col.setDirectionBackward(false);
			selects.addOrderByExpression(col);
			raw.removeSpace();
		}
		while(raw.forwardIfCurrent(','));
		raw.removeSpace();
	}

	private void whereExpressions(ParserString raw, Select select) throws SQLParserException {
		raw.removeSpace();
		Expression exp = expression(raw);
		if(exp==null) throw new SQLParserException("missing where expression");
		if(!(exp instanceof Operation)) throw new SQLParserException("invalid where expression ("+Caster.toClassName(exp)+")");
		select.setWhereExpression((Operation)exp);
		raw.removeSpace();
	}

	private void havingExpressions(ParserString raw, Select select) throws SQLParserException {
		raw.removeSpace();
		Expression exp = expression(raw);
		if(exp==null) throw new SQLParserException("missing having expression");
		if(!(exp instanceof Operation)) throw new SQLParserException("invalid having expression");
		select.setHaving((Operation)exp);
		raw.removeSpace();
	}

	private void groupByExpressions(ParserString raw, Select select) throws SQLParserException {
		Expression exp=null;
		do {
			raw.removeSpace();
			//print.out(raw.getCurrent());
			exp=expression(raw);
			if(!(exp instanceof Column)) throw new SQLParserException("invalid group by part of query");
			Column col=(Column) exp;
			
			select.addGroupByExpression(col);
			raw.removeSpace();
		}
		while(raw.forwardIfCurrent(','));
		raw.removeSpace();
	}
	
	private void tableList(ParserString raw, Select select) throws SQLParserException {
		Column column=null;
		Expression exp=null;
		do {
			raw.removeSpace();
			
			exp=column(raw);
			if(!(exp instanceof Column)) throw new SQLParserException("invalid table definition");
			column=(Column) exp;
			raw.removeSpace();
			if(raw.forwardIfCurrent("as ")) {
				String alias=identifier(raw,new RefBooleanImpl(false));
				if(alias==null) throw new SQLParserException("missing alias in select part");
				column.setAlias(alias);
			}
			else {
				int start=raw.getPos();
				RefBoolean hasBracked = new RefBooleanImpl(false);
				String alias=identifier(raw,hasBracked);//TODO having usw
				if(!hasBracked.toBooleanValue()) {
					if("where".equalsIgnoreCase(alias)) raw.setPos(start);
					else if("group".equalsIgnoreCase(alias)) raw.setPos(start);
					else if("having".equalsIgnoreCase(alias)) raw.setPos(start);
					else if("union".equalsIgnoreCase(alias)) raw.setPos(start);
					else if("order".equalsIgnoreCase(alias)) raw.setPos(start);
					else if("limit".equalsIgnoreCase(alias)) raw.setPos(start);
					else if(alias!=null) column.setAlias(alias);
				}
				else {
					if(alias!=null) column.setAlias(alias);
				}
				
			}
			select.addFromExpression(column);
			raw.removeSpace();
		}
		while(raw.forwardIfCurrent(','));
		
	
	}

	// { (selectStatement) [AS] label | tableName [AS] label}
	private void selectExpressions(ParserString raw, Select select) throws SQLParserException {
		Expression exp=null;
		do {
			raw.removeSpace();
			
			exp=expression(raw);
			if(exp==null) throw new SQLParserException("missing expression in select part of query");
			raw.removeSpace();
			if(raw.forwardIfCurrent("as ")) {
				String alias=identifier(raw,new RefBooleanImpl(false));
				if(alias==null) throw new SQLParserException("missing alias in select part");
				exp.setAlias(alias);
			}
			else {
				int start=raw.getPos();
				RefBoolean hb = new RefBooleanImpl(false);
				String alias=identifier(raw,hb);
				if(!hb.toBooleanValue() && "from".equalsIgnoreCase(alias)) raw.setPos(start);
				else if(alias!=null) exp.setAlias(alias);
			}
			select.addSelectExpression(exp);
			raw.removeSpace();
		}
		while(raw.forwardIfCurrent(','));
		
		
	}
	
	private Expression expression(ParserString raw) throws SQLParserException {
		return xorOp(raw);
	}
	
	private Expression xorOp(ParserString raw) throws SQLParserException {
		Expression expr = orOp(raw);
		
		while(raw.forwardIfCurrentAndNoWordNumberAfter("xor")) {
			raw.removeSpace();
	        expr=new Operation2(expr, orOp(raw), Operation.OPERATION2_XOR);
		}
		return expr;
	}
	
	private Expression orOp(ParserString raw) throws SQLParserException {
		Expression expr = andOp(raw);
		
		while(raw.forwardIfCurrentAndNoWordNumberAfter("or")) {
			raw.removeSpace();
	        expr=new Operation2(expr, andOp(raw),Operation.OPERATION2_OR);
		}
		return expr;
	}
	
	private Expression andOp(ParserString raw) throws SQLParserException {
		Expression expr = notOp(raw);
		
		while(raw.forwardIfCurrentAndNoWordNumberAfter("and")) {
			raw.removeSpace();
	        expr=new Operation2(expr, notOp(raw), Operation.OPERATION2_AND);
		}
		return expr;
	}
	
	private Expression notOp(ParserString raw) throws SQLParserException {
		// NOT
		if (raw.forwardIfCurrentAndNoWordNumberAfter("not")) {
			raw.removeSpace();
			return new Operation1(decsionOp(raw),Operation.OPERATION1_NOT);
		}
		return decsionOp(raw);
	}
	
	private Expression decsionOp(ParserString raw) throws SQLParserException {

		Expression expr = plusMinusOp(raw);
		boolean hasChanged=false;
		do {
			hasChanged=false;

			// value BETWEEN value AND value
			if (raw.forwardIfCurrent("between ")) {
				raw.removeSpace();
				Expression left = plusMinusOp(raw);
				raw.removeSpace();
				if(!raw.forwardIfCurrent("and "))throw new SQLParserException("invalid operation (between) missing operator and");
				raw.removeSpace();
				Expression right = plusMinusOp(raw);
				raw.removeSpace();
				expr= new Operation3(expr,left,right, Operation.OPERATION3_BETWEEN);
				hasChanged=true;
			}
			
			// value like value [escape value]
			else if (raw.forwardIfCurrentAndNoWordNumberAfter("like")) {
				raw.removeSpace();
				Expression left = plusMinusOp(raw);
				raw.removeSpace();
				if(raw.forwardIfCurrentAndNoWordNumberAfter("escape ")){
					raw.removeSpace();
					Expression right = plusMinusOp(raw);
					raw.removeSpace();
					expr= new Operation3(expr,left,right, Operation.OPERATION3_LIKE);
				}
				else {
					raw.removeSpace();
					expr= new Operation2(expr,left, Operation.OPERATION2_LIKE);
				}
				hasChanged=true;
			}
				
			
			// IS [NOT] NULL
			else if (raw.isCurrent("is ")) {
				int start=raw.getPos();
				if (raw.forwardIfCurrentAndNoWordNumberAfter("is null")) {
					raw.removeSpace();
					return new Operation1(expr, Operation.OPERATION1_IS_NULL);
			        
				} 
				else if (raw.forwardIfCurrentAndNoWordNumberAfter("is not null")) {
					raw.removeSpace();
					return new Operation1(expr, Operation.OPERATION1_IS_NOT_NULL);
			        
				}
				else {
					raw.setPos(start);
					raw.removeSpace();
				}
			}

			// not in
			else if (raw.forwardIfCurrent("not in",'(')) {
				expr=new OperationN("not_in",readArguments(raw,expr));
				hasChanged=true;
			}
			// in
			else if (raw.forwardIfCurrent("in",'(')) {
				expr=new OperationN("in",readArguments(raw,expr));
				hasChanged=true;
			}	
			// not like
			if (raw.forwardIfCurrentAndNoWordNumberAfter("not like")) {
				expr = decisionOpCreate(raw,Operation.OPERATION2_NOT_LIKE,expr);
				hasChanged=true;
			}
			/* / like
			else if (raw.forwardIfCurrentAndNoWordNumberAfter("like")) {
				expr = decisionOpCreate(raw,Operation.OPERATION2_LIKE,expr);
				hasChanged=true;
			}*/			
			// =
			else if (raw.forwardIfCurrent('=')) {
				expr = decisionOpCreate(raw,Operation.OPERATION2_EQ,expr);
				hasChanged=true;
			}
			// !=
			else if (raw.forwardIfCurrent("!=")) {
				expr = decisionOpCreate(raw,Operation.OPERATION2_NEQ,expr);
				hasChanged=true;
			}
			// <>
			else if (raw.forwardIfCurrent("<>")) {
				expr = decisionOpCreate(raw,Operation.OPERATION2_LTGT,expr);
				hasChanged=true;
			}
			// <, <=
			else if (raw.isCurrent('<')) {
				if (raw.forwardIfCurrent("<=")) {
					expr = decisionOpCreate(raw,Operation.OPERATION2_LTE,expr);
					hasChanged=true;
				} 
				else {
					raw.next();
					expr = decisionOpCreate(raw,Operation.OPERATION2_LT,expr);
					hasChanged=true;
				}				
			}
			// >, =>
			else if (raw.isCurrent('>')) {
				if (raw.forwardIfCurrent("=>")) {
					expr = decisionOpCreate(raw,Operation.OPERATION2_GTE,expr);
					hasChanged=true;
				} 
				if (raw.forwardIfCurrent(">=")) {
					expr = decisionOpCreate(raw,Operation.OPERATION2_GTE,expr);
					hasChanged=true;
				} 
				else {
					raw.next();
					expr = decisionOpCreate(raw,Operation.OPERATION2_GT,expr);
					hasChanged=true;
				}				
			}
		}while(hasChanged);
		return expr;
	}
	private Expression decisionOpCreate(ParserString raw,int operation, Expression left) throws SQLParserException {
		raw.removeSpace();
        return new Operation2(left, plusMinusOp(raw), operation);
	}
	private Expression plusMinusOp(ParserString raw) throws SQLParserException {
		Expression expr = modOp(raw);
		
		while(!raw.isLast()) {
			
			// Plus Operation
			if (raw.forwardIfCurrent('+')) {
				raw.removeSpace();
                expr=new Operation2(expr, modOp(raw), Operation.OPERATION2_PLUS);
			}
			// Minus Operation
			else if (raw.forwardIfCurrent('-')) {
				raw.removeSpace();
                expr=new Operation2(expr, modOp(raw), Operation.OPERATION2_MINUS);
			}
			else break;
		}
		return expr;
	}
	
	private Expression modOp(ParserString raw) throws SQLParserException {
		Expression expr = divMultiOp(raw);
		
		// Modulus Operation
		while(raw.forwardIfCurrent('%')) {
			raw.removeSpace();
            expr=new Operation2(expr, divMultiOp(raw), Operation.OPERATION2_MOD);
		}
		return expr;
	}
	
	private Expression divMultiOp(ParserString raw) throws SQLParserException {
		Expression expr = expoOp(raw);
		while (!raw.isLast()) {
				// Multiply Operation
				if(raw.forwardIfCurrent('*')) {
                    raw.removeSpace();
                    expr=new Operation2(expr, expoOp(raw), Operation.OPERATION2_MULTIPLY);
				}
				// Divide Operation
				else if (raw.forwardIfCurrent('/')) {
					raw.removeSpace();
                    expr=new Operation2(expr, expoOp(raw), Operation.OPERATION2_DIVIDE);
				}
				else {
					break;
				}
			
		}
		return expr;
	}

	private Expression expoOp(ParserString raw) throws SQLParserException {
		Expression exp = negateMinusOp(raw);

		// Modulus Operation
		while(raw.forwardIfCurrent('^')) {
			raw.removeSpace();
            exp=new Operation2(exp, negateMinusOp(raw),Operation.OPERATION2_EXP);
		}
		return exp;
	}
	
	private Expression negateMinusOp(ParserString raw) throws SQLParserException {
		// And Operation
		if (raw.forwardIfCurrent('-')) {
			raw.removeSpace();
			return new Operation1(clip(raw),Operation.OPERATION1_MINUS);
		}
		else if (raw.forwardIfCurrent('+')) {
			raw.removeSpace();
			return new Operation1(clip(raw),Operation.OPERATION1_PLUS);
		}
		return clip(raw);
	}
	
	//     { Expression | COUNT(*) | {COUNT | MIN | MAX | SUM | AVG | SOME | EVERY | VAR_POP | VAR_SAMP | STDDEV_POP | STDDEV_SAMP} ([ALL | DISTINCT][1]] Expression) } [[AS] label]
	private Expression clip(ParserString raw) throws SQLParserException {
		Expression exp=column(raw);
		//if(exp==null)exp=brackedColumn(raw);
		if(exp==null)exp=date(raw);
		if(exp==null)exp=bracked(raw);
		if(exp==null)exp=number(raw);
		if(exp==null)exp=string(raw);
		return exp;
	}

	private Expression bracked(ParserString raw) throws SQLParserException {
		if(!raw.forwardIfCurrent('(')) return null;
		raw.removeSpace();
		Expression exp = expression(raw);
		raw.removeSpace();
		if(!raw.forwardIfCurrent(')')) throw new SQLParserException("missing closing )");
		raw.removeSpace();
		return exp;//new BracketExpression(exp);
	}

	private Expression column(ParserString raw) throws SQLParserException {
		RefBoolean hb = new RefBooleanImpl(false);
		String name = identifier(raw,hb);
		if(name==null) return null;
		if(!hb.toBooleanValue()) {
			if("true".equalsIgnoreCase(name)) return ValueBoolean.TRUE;
			if("false".equalsIgnoreCase(name)) return ValueBoolean.FALSE;
			if("null".equalsIgnoreCase(name)) return ValueNull.NULL;
		}
		
		ColumnExpression column=new ColumnExpression(name,name.equals("?")?columnIndex++:0);
		raw.removeSpace();
		while(raw.forwardIfCurrent(".")) {
			raw.removeSpace();
			String sub=identifier(raw,hb);
			if(sub==null) throw new SQLParserException("invalid column definition");
			column.setSub(sub);
		}
		raw.removeSpace();
		if(raw.forwardIfCurrent('(')) {
			return new OperationN(column.getFullName(),readArguments(raw));
		}
		return column;
	}
	
	
	

	private List readArguments(ParserString raw) throws SQLParserException {
		return readArguments(raw,null);
	}
	private List readArguments(ParserString raw,Expression exp) throws SQLParserException {
		List args=new ArrayList();
		Expression arg;
		if(exp!=null)args.add(exp);
		do {
			raw.removeSpace();
			if(raw.isCurrent(')')) break;
			
			args.add(arg=expression(raw));
			raw.removeSpace();
			// check for alias
			if(raw.forwardIfCurrent("as ")) {
				raw.removeSpace();
				arg.setAlias(identifier(raw,null));
			raw.removeSpace();
		}
			
			
		}
		while(raw.forwardIfCurrent(','));
		if(!raw.forwardIfCurrent(')')) throw new SQLParserException("missing closing )");
		raw.removeSpace();
		return args;
	}

	private ValueNumber number(ParserString raw) throws SQLParserException {
		// check first character is a number literal representation
		if(!(raw.isCurrentBetween('0','9') || raw.isCurrent('.'))) return null;
		
		StringBuffer rtn=new StringBuffer();
		
		// get digit on the left site of the dot
		if(raw.isCurrent('.')) rtn.append('0');
		else rtn.append(digit(raw));
		// read dot if exist
		if(raw.forwardIfCurrent('.')) {
			rtn.append('.');
			String rightSite=digit(raw);
			if(rightSite.length()> 0 && raw.forwardIfCurrent('e')) {
			    if(raw.isCurrentBetween('0','9')) {
			        rightSite+='e'+digit(raw);
			    }
			    else {
			        raw.previous();
			    }
			}
			// read right side of the dot
			if(rightSite.length()==0)
				throw new SQLParserException("Number can't end with [.]");
			rtn.append(rightSite);
		}
        raw.removeSpace();
        return new ValueNumber(rtn.toString());
        
		
	}
	
	private String digit(ParserString raw) {
		String rtn="";
		while (raw.isValidIndex()) {
			if(!raw.isCurrentBetween('0','9'))break;
			rtn+=raw.getCurrentLower();
			raw.next();
		}
		return rtn;
	}
	
	
	private ValueString string(ParserString raw) throws SQLParserException {
		
		// check starting character for a string literal
		if(!raw.isCurrent('\''))
			return null;
		
		// Init Parameter
		StringBuffer str=new StringBuffer();
		
		while(raw.hasNext()) {
			raw.next();
		
			// check quoter
			if(raw.isCurrent('\'')) {
				// Ecaped sharp
				if(raw.isNext('\'')){
					raw.next();
					str.append('\'');
				}
				// finsish
				else {
					break;
				}				
			}
			// all other character
			else {
				str.append(raw.getCurrent());
			}
		}
		if(!raw.forwardIfCurrent('\''))
			throw new SQLParserException("Invalid Syntax Closing ['] not found");
		
		raw.removeSpace();
		return new ValueString(str.toString());
	}
	
private ValueDate date(ParserString raw) throws SQLParserException {
		
		if(!raw.isCurrent('{'))
			return null;
		
		// Init Parameter
		StringBuilder str=new StringBuilder();
		
		while(raw.hasNext()) {
			raw.next();
			if(raw.isCurrent('}'))break;
			str.append(raw.getCurrent());
			
		}
		if(!raw.forwardIfCurrent('}'))
			throw new SQLParserException("Invalid Syntax Closing [}] not found");
		
		raw.removeSpace();
		try {
			return new ValueDate("{"+str.toString()+"}");
		} 
		catch (PageException e) {
			throw new SQLParserException("can't cast value [{"+str.toString()+"}] to date object");
		}
	}
	
	private String identifier(ParserString raw,RefBoolean hasBracked) throws SQLParserException {
		
		if(hasBracked!=null && raw.forwardIfCurrent('[')) {
			hasBracked.setValue(true);
			return identifierBracked(raw);
		}
		else if(!(raw.isCurrentLetter() || raw.isCurrent('*') || raw.isCurrent('?')|| raw.isCurrent('_'))) return null;
		
		
		int start = raw.getPos();
		do {
			raw.next();
			if(!(raw.isCurrentLetter() || raw.isCurrentBetween('0','9') || raw.isCurrent('*') || raw.isCurrent('?')|| raw.isCurrent('_'))) {
					break;
			}
		}
		while (raw.isValidIndex());
		String str = raw.substring(start,raw.getPos()-start);
		raw.removeSpace();
		
		return str;
	}
	private String identifierBracked(ParserString raw) throws SQLParserException {
		int start = raw.getPos();
		do {
			raw.next();
		}
		while (raw.isValidIndex() && !raw.isCurrent(']'));
		
		String str = raw.substring(start,raw.getPos()-start);
		if(!raw.forwardIfCurrent(']')) throw new SQLParserException("missing ending ] of identifier");
		return str;
	}



	public static void main(String[] args) {
		
		
		//print.out(new SelectParser().parse("select a, b as c, d e from test limit 3").toString());
		
		
		
		//String sql="select cast(susi as integer) as y,a=b as x from source";//WHERE (lft BETWEEN 1 AND 4 AND ID = 111)
		//print.out(new SelectParser().parse(sql).toString());
		
		/*
		 print.out(new SelectParser().parse("select (a and b) s,'abc'<b as c,'abc'+b as c,'abc'%b as c,'abc'*b as c,'susi''s lustige dings', 'sss' as a, 'xxx' b, 1.1,1.2 as c, 1.3 d, e, f as g, h i, a.b.c.d as j, 1^1 as k  " +
				"from test, a as x, b y,a.b.c as y " +
				" where not t=d and x>y or x=0 xor '1'>'2' ").toString());
		*/
		//print.out("*****************************");
		/*
		print.out(new SelectParser().parse("select a, b as c, d e from test").toString());
		print.out(new SelectParser().parse("select c is not null,c is null as x from test").toString());
		print.out(new SelectParser().parse("select c between 'a' and 1,c between 'a' and 1 as x,a from test").toString());
		print.out(new SelectParser().parse("select c from test where a like b and c=1").toString());
		print.out(new SelectParser().parse("select true as x,b in(1,a,'x'), count(c),count(c) as x from test where s not in(1,2,3) and a like b and c=count(c)").toString());
		*/
		//print.out(new SelectParser().parse("select c from test where x=y order by test,a.b desc , a.b.c asc ").toString());
		//print.out(new SelectParser().parse("select *, *.lastname from test").toString());
		//Selects select = new SelectParser().parse("SELECT * FROM qTest WHERE (ID = 7 AND data_ID = 1) OR (data_ID = 110422)");
		//select = new SelectParser().parse("\nSELECT          *\nFROM            qAllTodos\nWHERE           (\n                        (\n                        (\n                        toeditor_ID = ? OR\n                        workflowtaskseditor_ID = ? OR\n                        (editorgroup_ID IN (?) AND\n                        workflowmethod_ID = 1) OR\n                        (editorgroup_ID IN (?) AND\n                        workflowmethod_ID = 2 AND status <> 6) OR\n                        (editorgroup_ID IN (?,?) AND\n                        workflowmethod_ID = 3) OR\n                        (editorgroup_ID IN (?) AND\n                        workflowmethod_ID = 4) OR\n                        (editorgroup_ID IN (?)  AND\n                        ? <> editor_ID AND\n                        workflowmethod_ID = 5) OR\n                        (acceptedbyeditor_ID = ?) OR\n                        (passedtoeditor_ID = ?)\n                        ) AND\n                        status IN (1,2,6) AND\n                        (acceptedbyeditor_ID = ? OR\n                        acceptedbyeditor_ID = 0) AND\n\n                        (site_ID = ? OR\n                        workflowprocessessite_ID = ? OR\n                        workflowprocessessite_ID = 0) AND\n\n                        startdate <= '2007-09-14 17:09:48' AND\n\n                        istimeouttask = 0\n                        )\n\n\n                        OR\n\n                        (\n\n                        confirm_needed <> 0 AND\n\n                        (confirm_editor_ID = ?  OR\n\n                        (confirm_editorgroup_ID IN (?) AND\n                        confirm_method_ID = 1) OR\n\n                        (confirm_editorgroup_ID IN (?) AND\n                        confirm_method_ID = 2) OR\n\n                        (confirm_editorgroup_ID IN (?,?) AND\n                        confirm_method_ID = 3) OR\n\n                        (confirm_editorgroup_ID IN (?) AND\n                        confirm_method_ID = 4) OR\n\n                        (confirm_editorgroup_ID IN (?) AND\n                        donebyeditor_ID <> ? AND\n                        confirm_method_ID = 5)\n                        ) AND\n\n                        confirmedbyeditor_ID = 0 AND\n\n                        status = 3 AND\n\n                        (workflowprocessessite_ID = ? OR\n                        workflowprocessessite_ID = 0)\n                        )\n\n\n                        OR\n\n                        (\n\n                        istimeouttask <> 0 AND\n\n                        (timeout_editor_ID = ?  OR\n\n                        (timeout_editorgroup_ID IN (?) AND\n                        timeout_method_ID = 1) OR\n\n                        (timeout_editorgroup_ID IN (?) AND\n                        timeout_method_ID = 2 AND\n                        status <> 6)    OR\n\n                        (timeout_editorgroup_ID IN (?,?) AND\n                        timeout_method_ID = 3) OR\n\n                        (timeout_editorgroup_ID IN (?) AND\n                        timeout_method_ID = 4) OR\n\n                        (timeout_editorgroup_ID IN (?) AND\n                        ? <> deputyeditor_ID AND\n                        timeout_method_ID = 5) OR\n\n                        (acceptedbyeditor_ID = ?) OR\n\n                        (passedtoeditor_ID = ?)\n                        ) AND\n\n                        status IN (1,2,6) AND\n\n                        (acceptedbyeditor_ID = ? OR\n                        acceptedbyeditor_ID = 0) AND\n\n                        (workflowprocessessite_ID = ? OR\n                        workflowprocessessite_ID = 0)\n\n                        )\n                        )\n");
		//print.out(select.toString());
		//print.out(select.getTables().length);
		//railo.print.out(new SelectParser().parse("select * from qryData where tableName like '%@_array' or x=1").toString());
		//railo.print.out(new SelectParser().parse("select * from qryData where tableName like '%@_array' escape '@' or x=1").toString());
		
		
		
		
	}
}
