SimpleSqlBuilder.java

package org.microspace.table.query.sql;

import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

import org.antlr.v4.runtime.BailErrorStrategy;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.misc.ParseCancellationException;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.microspace.exception.IllegalOperationException;
import org.microspace.parser.AntlrSimpleSqlLexer;
import org.microspace.parser.AntlrSimpleSqlListener;
import org.microspace.parser.AntlrSimpleSqlParser;
import org.microspace.parser.AntlrSimpleSqlParser.Boolean_literalContext;
import org.microspace.parser.AntlrSimpleSqlParser.Boolean_literal_listContext;
import org.microspace.parser.AntlrSimpleSqlParser.Column_bool_columnContext;
import org.microspace.parser.AntlrSimpleSqlParser.Column_bool_primaryContext;
import org.microspace.parser.AntlrSimpleSqlParser.Column_in_expressionContext;
import org.microspace.parser.AntlrSimpleSqlParser.Column_listContext;
import org.microspace.parser.AntlrSimpleSqlParser.Column_nameContext;
import org.microspace.parser.AntlrSimpleSqlParser.Column_sort_listContext;
import org.microspace.parser.AntlrSimpleSqlParser.Column_sort_nameContext;
import org.microspace.parser.AntlrSimpleSqlParser.ExpressionContext;
import org.microspace.parser.AntlrSimpleSqlParser.Literal_listContext;
import org.microspace.parser.AntlrSimpleSqlParser.Literal_valueContext;
import org.microspace.parser.AntlrSimpleSqlParser.Number_literalContext;
import org.microspace.parser.AntlrSimpleSqlParser.Number_literal_listContext;
import org.microspace.parser.AntlrSimpleSqlParser.Order_clauseContext;
import org.microspace.parser.AntlrSimpleSqlParser.Relational_opContext;
import org.microspace.parser.AntlrSimpleSqlParser.Rownum_bool_primaryContext;
import org.microspace.parser.AntlrSimpleSqlParser.Rownum_expressionContext;
import org.microspace.parser.AntlrSimpleSqlParser.Select_expressionContext;
import org.microspace.parser.AntlrSimpleSqlParser.Select_expression_andContext;
import org.microspace.parser.AntlrSimpleSqlParser.Select_expression_atomContext;
import org.microspace.parser.AntlrSimpleSqlParser.Select_expression_listContext;
import org.microspace.parser.AntlrSimpleSqlParser.Select_statementContext;
import org.microspace.parser.AntlrSimpleSqlParser.String_literalContext;
import org.microspace.parser.AntlrSimpleSqlParser.String_literal_listContext;
import org.microspace.parser.AntlrSimpleSqlParser.Table_nameContext;
import org.microspace.parser.AntlrSimpleSqlParser.Top_selectContext;
import org.microspace.parser.AntlrSimpleSqlParser.Where_clauseContext;
import org.microspace.util.PojoUtil;

/**
 * Build A Select Statement.
 * 
 * @author Gaspar Sinai - {@literal gaspar.sinai@microspace.org}
 * @version 2017-07-29
 * @param <T> The type of the Table.
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public class SimpleSqlBuilder<T> implements AntlrSimpleSqlListener {

	Stack<Select> selectStack = new Stack<>();
	Stack <Expression> expressionStack = new Stack<>();
	Stack <ObjectContainer<Expression>> lastExpressionStack = new Stack <>();
	Stack<RowNumSortExpression> rowNumSortExpressionStack = new Stack<>();
	Class<T> topLevelSelectClass;
	Select lastSelect;
	List<Select> innerSelectList = new LinkedList<>();
	
	Expression lastExpression = null;
	final String topLevelSqlQuery;
	
	public SimpleSqlBuilder(String sql) {
		this(sql, null);
	}

	public SimpleSqlBuilder(String sql, Class<T> topLevelSelectClass) {
		this.topLevelSqlQuery = sql;
		this.topLevelSelectClass = topLevelSelectClass;
		AntlrSimpleSqlLexer lexer = new AntlrSimpleSqlLexer(CharStreams.fromString(sql));
		CommonTokenStream tokens = new CommonTokenStream(lexer);
		AntlrSimpleSqlParser parser = new AntlrSimpleSqlParser(tokens);
		parser.setErrorHandler(new BailErrorStrategy());
		ParseTree tree;
		try {
			tree = parser.top_select();
		} catch (ParseCancellationException ex) {
			throw new IllegalArgumentException ("Syntax Error", ex);
		}
		ParseTreeWalker walker = new ParseTreeWalker();
		walker.walk(this, tree);
	}

	public Select<T> getSelect() {
		return (Select<T>) lastSelect;
	}

	@Override
	public void enterSelect_statement(Select_statementContext ctx) {
		String tableName = ctx.table_name().getText();
		Class<Object> selectClass;
		try {
			selectClass = (Class<Object>) Class.forName(tableName);
			if (selectStack.isEmpty()) {
				if (!selectClass.equals(topLevelSelectClass)) {
					throw new IllegalArgumentException ("SELECT class expected: " + topLevelSelectClass);
				}
			}
			if (topLevelSelectClass == null) {
				
				topLevelSelectClass = (Class<T>) selectClass;
			}
		} catch (ClassNotFoundException e) {
			throw new IllegalArgumentException("Table Class Not Found", e);
		}
		
		// This may not necesserily be T class.
		Select<Object> select = new Select<>(selectClass, ctx.DISTINCT() != null);
		selectStack.push(select);
		lastExpressionStack.push(new ObjectContainer());
		rowNumSortExpressionStack.push((new RowNumSortExpression<>(selectClass)));
		
		if (ctx.column_list() != null) {
			for (Column_nameContext cn : ctx.column_list().column_name()) {
				String columnEsc = "";
				if (!cn.BACKTICK().isEmpty()) {
					columnEsc = "`";
				}
				select.addColumn(cn.ID().getText(), columnEsc);
			}
		}
	}

	@Override
	public void exitSelect_statement(Select_statementContext ctx) {
		lastSelect = selectStack.pop();
		ObjectContainer<Expression> container = lastExpressionStack.pop();
		if (container.getObject() != null) {
			if (container.getObject() != expressionStack.pop()) {
				// We are in trouble.
				throw new IllegalOperationException("Internal Error");
			}
			lastSelect.setExpression((Expression) container.getObject());
		}
		RowNumSortExpression<?> rowNumSortExpression = rowNumSortExpressionStack.pop();
		lastSelect.setRowNumSortExpression(rowNumSortExpression);
		
		
		if (selectStack.empty()) {
			lastSelect.finalize(topLevelSqlQuery, innerSelectList);
		} else {
			innerSelectList.add(lastSelect);
			lastSelect.finalize(ctx.getText(), null);
		}
		
	}
	
	@Override
	public void enterOrder_clause(Order_clauseContext ctx) {
	}
	@Override
	public void exitOrder_clause(Order_clauseContext ctx) {
		RowNumSortExpression rowNumSortExpression = rowNumSortExpressionStack.peek();
		List<String> columnList = new LinkedList<>();
		List<String> columnEscList = new LinkedList<>();
		List<EntryComparator.Type> types = new LinkedList<>();
		Column_sort_listContext colList = ctx.column_sort_list();
		for (Column_sort_nameContext cn : colList.column_sort_name()) {
			String columnEsc = "";
			if (!cn.BACKTICK().isEmpty()) {
				columnEsc = "`";
			}
			columnList.add(cn.ID().getText());
			columnEscList.add(columnEsc);
			
			EntryComparator.Type type = (cn.DESC() != null) 
					? EntryComparator.Type.DESC
					: EntryComparator.Type.ASC;
			types.add(type);
		}
		EntryComparator comparator = new EntryComparator<T>(selectStack.peek().getTableClass(), 
				EntryComparator.Type.ASC,
				columnList, types, columnEscList);
		rowNumSortExpression.setComparator (comparator);
	}

	@Override
	public void enterColumn_list(Column_listContext ctx) {
	}

	@Override
	public void exitColumn_list(Column_listContext ctx) {
	}
	
	@Override
	public void enterColumn_name(Column_nameContext ctx) {
	}

	@Override
	public void exitColumn_name(Column_nameContext ctx) {
		// TODO Auto-generated method stub

	}
	@Override
	public void enterRownum_bool_primary(Rownum_bool_primaryContext ctx) {
		RowNumSortExpression rowNumSortExpression = rowNumSortExpressionStack.peek();
		if (rowNumSortExpression == null) {
			rowNumSortExpression = new RowNumSortExpression(selectStack.peek().getTableClass());
		}
		if (ctx.relational_op().NOT_EQ() != null) {
			throw new IllegalArgumentException ("ROWNUM != Is Not Supported");
		}
		Integer limit = Integer.valueOf (ctx.INTEGER_NUM().getText());
		if (ctx.relational_op().EQ() != null) {
			rowNumSortExpression.setHighLimit(limit+1);
			rowNumSortExpression.setLowLimit(limit);
			return;
		}
		if (ctx.relational_op().LET() != null) {
			rowNumSortExpression.setHighLimit(limit+1);
		}
		if (ctx.relational_op().LTH() != null) {
			rowNumSortExpression.setHighLimit(limit);
		}
		if (ctx.relational_op().GET() != null) {
			rowNumSortExpression.setLowLimit(limit);
		}
		if (ctx.relational_op().GTH() != null) {
			rowNumSortExpression.setLowLimit(limit+1);
		}
	}

	@Override
	public void exitRownum_bool_primary(Rownum_bool_primaryContext ctx) {
		
	}
	
	@Override
	public void enterSelect_expression(Select_expressionContext ctx) {
	}

	@Override
	public void exitSelect_expression(Select_expressionContext ctx) {
		Stack <Expression> reverseStack = new Stack<>();
		for (int i=0; i< ctx.OR().size(); i++) {
			reverseStack.push(expressionStack.pop());
		}
		while (reverseStack.size() > 0) {
			Expression<T> exp1 = expressionStack.pop();
			Expression<T> exp2 = reverseStack.pop();
			ExpressionOr<T> orExp = new ExpressionOr<T>(exp1, exp2);
			expressionStack.push (orExp);
			lastExpressionStack.peek().setObject(orExp);
		}
	}
	@Override
	public void enterSelect_expression_and(Select_expression_andContext ctx) {
	}

	@Override
	public void exitSelect_expression_and(Select_expression_andContext ctx) {
		Stack <Expression> reverseStack = new Stack<>();
		for (int i=0; i< ctx.AND().size(); i++) {
			reverseStack.push(expressionStack.pop());
		}
		while (reverseStack.size() > 0) {
			Expression<T> exp1 = expressionStack.pop();
			Expression<T> exp2 = reverseStack.pop();
			ExpressionAnd<T> andExp = new ExpressionAnd<T>(exp1, exp2);
			expressionStack.push (andExp);
			lastExpressionStack.peek().setObject(andExp);
		}
	}
	
	@Override
	public void enterSelect_expression_list(Select_expression_listContext ctx) {
	}

	@Override
	public void exitSelect_expression_list(Select_expression_listContext ctx) {
		lastExpressionStack.peek().getObject().setNeedsBracket(true);
	}
	
	@Override
	public void enterColumn_bool_primary(Column_bool_primaryContext ctx) {
	}

	@Override
	public void exitColumn_bool_primary(Column_bool_primaryContext ctx) {
		OperatorType operator;
		LiteralValueType type;
		String value;
		if (ctx.IS() != null && ctx.NOT() != null && ctx.NULL() != null) {
			operator = OperatorType.IS_NOT_NULL;
			type = LiteralValueType.NULL;
			value = "NULL";
		} else if (ctx.IS() != null && ctx.NULL() != null) {
			operator = OperatorType.IS_NULL;
			type = LiteralValueType.NULL;
			value = "NULL";
		} else if (ctx.NOT() != null && ctx.LIKE() != null) {
			operator = OperatorType.NOT_LIKE;
			type = LiteralValueType.STRING;
			value = getLiteralValue(ctx.literal_value());
		} else if (ctx.LIKE() != null) {
			operator = OperatorType.LIKE;
			type = LiteralValueType.STRING;
			value = getLiteralValue(ctx.literal_value());
		} else if (ctx.relational_op() == null) {
			throw new IllegalArgumentException ("Relational Operator Expected");
		} else if (ctx.relational_op().EQ() != null) {
			operator = OperatorType.EQ;
			type = getExpressionType (ctx.literal_value());
			value = getLiteralValue(ctx.literal_value());
		} else if (ctx.relational_op().LTH() != null) {
			operator = OperatorType.LTH;
			type = getExpressionType (ctx.literal_value());
			value = getLiteralValue(ctx.literal_value());
		} else if (ctx.relational_op().GTH() != null) {
			operator = OperatorType.GTH;
			type = getExpressionType (ctx.literal_value());
			value = getLiteralValue(ctx.literal_value());
		} else if (ctx.relational_op().NOT_EQ() != null) {
			operator = OperatorType.NOT_EQ;
			type = getExpressionType (ctx.literal_value());
			value = getLiteralValue(ctx.literal_value());
		} else if (ctx.relational_op().LET() != null) {
			operator = OperatorType.LET;
			type = getExpressionType (ctx.literal_value());
			value = getLiteralValue(ctx.literal_value());
		} else if (ctx.relational_op().GET() != null) {
			operator = OperatorType.GET;
			type = getExpressionType (ctx.literal_value());
			value = getLiteralValue(ctx.literal_value());
		} else {
			throw new IllegalArgumentException ("Operator Not Handled: " + ctx.relational_op());
		}
		String columnName = ctx.column_name().ID().getText();
		Class<?> outerColumn = PojoUtil.getColumnClass(selectStack.peek().getTableClass(), columnName);
		if (outerColumn == null) {
			throw new IllegalArgumentException ("Column " + columnName + " not found.");
		}
		// Check if it can be created.
		if (type != LiteralValueType.NULL) {
			Util.createColumnObject(value, outerColumn, type);
		}
		String columnEsc = "";
		if (!ctx.column_name().BACKTICK().isEmpty()) {
			columnEsc = "`";
		}
		ExpressionBoolean<T> booleanExpression = new ExpressionBoolean<> (
				selectStack.peek().getTableClass(), operator, 
				ctx.column_name().ID().getText(), type, value, columnEsc);
		expressionStack.push(booleanExpression);
		lastExpressionStack.peek().setObject(booleanExpression);
		
	}
	
	private String getLiteralValue (AntlrSimpleSqlParser.Literal_valueContext cntx) {
		if (cntx.string_literal() == null) {
			return cntx.getText();
		}
		return Util.unquote(cntx.getText());
	}
	
	private LiteralValueType getExpressionType (AntlrSimpleSqlParser.Literal_valueContext cntx) {
		if (cntx.string_literal() != null) {
			return LiteralValueType.STRING;
		}
		if (cntx.boolean_literal() != null) {
			return LiteralValueType.BOOLEAN;
		}
		if (cntx.number_literal() != null) {
			return LiteralValueType.NUMBER;
		}
		throw new IllegalArgumentException("Unkown literal type.");
	}
	@Override
	public void enterExpression(ExpressionContext ctx) {
	}
	
	@Override
	public void exitExpression(ExpressionContext ctx) {
	}

	@Override
	public void enterEveryRule(ParserRuleContext arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void exitEveryRule(ParserRuleContext arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void visitErrorNode(ErrorNode arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void visitTerminal(TerminalNode arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void enterRelational_op(Relational_opContext ctx) {
		// TODO Auto-generated method stub

	}

	@Override
	public void exitRelational_op(Relational_opContext ctx) {
		// TODO Auto-generated method stub

	}

	@Override
	public void enterString_literal(String_literalContext ctx) {
		// TODO Auto-generated method stub

	}

	@Override
	public void exitString_literal(String_literalContext ctx) {
		// TODO Auto-generated method stub

	}

	@Override
	public void enterNumber_literal(Number_literalContext ctx) {
		// TODO Auto-generated method stub

	}

	@Override
	public void exitNumber_literal(Number_literalContext ctx) {
		// TODO Auto-generated method stub

	}

	@Override
	public void enterBoolean_literal(Boolean_literalContext ctx) {
		// TODO Auto-generated method stub

	}

	@Override
	public void exitBoolean_literal(Boolean_literalContext ctx) {
		// TODO Auto-generated method stub

	}

	@Override
	public void enterLiteral_value(Literal_valueContext ctx) {
		// TODO Auto-generated method stub

	}

	@Override
	public void exitLiteral_value(Literal_valueContext ctx) {
		// TODO Auto-generated method stub

	}

	@Override
	public void enterTable_name(Table_nameContext ctx) {
		// TODO Auto-generated method stub

	}

	@Override
	public void exitTable_name(Table_nameContext ctx) {
		// TODO Auto-generated method stub

	}


	@Override
	public void enterWhere_clause(Where_clauseContext ctx) {
	}

	@Override
	public void exitWhere_clause(Where_clauseContext ctx) {
	}


	@Override
	public void enterRownum_expression(Rownum_expressionContext ctx) {
		// TODO Auto-generated method stub

	}

	@Override
	public void exitRownum_expression(Rownum_expressionContext ctx) {
		// TODO Auto-generated method stub

	}

	@Override
	public void enterString_literal_list(String_literal_listContext ctx) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void exitString_literal_list(String_literal_listContext ctx) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void enterNumber_literal_list(Number_literal_listContext ctx) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void exitNumber_literal_list(Number_literal_listContext ctx) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void enterBoolean_literal_list(Boolean_literal_listContext ctx) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void exitBoolean_literal_list(Boolean_literal_listContext ctx) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void enterLiteral_list(Literal_listContext ctx) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void exitLiteral_list(Literal_listContext ctx) {
		// TODO Auto-generated method stub
		
	}


	@Override
	public void enterColumn_in_expression(Column_in_expressionContext ctx) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void exitColumn_in_expression(Column_in_expressionContext ctx) {
		Select<T> select = selectStack.peek();
		String columnName = ctx.column_name().ID().getText();
		Class<?> outerColumn = PojoUtil.getColumnClass(select.getTableClass(), columnName);
		if (outerColumn == null) {
			throw new IllegalArgumentException ("Column " + columnName + " not found.");
		}
		String columnEsc = "";
		if (!ctx.column_name().BACKTICK().isEmpty()) {
			columnEsc = "`";
		}
		ExpressionIn<T> expressionIn = new ExpressionIn<T>(select.getTableClass(),
				columnName,
				ctx.NOT() != null, columnEsc);
		if (ctx.select_statement() != null) {
			if (lastSelect.getColumnNames().size() != 1) {
				throw new IllegalArgumentException (
						"Inner select needs exectly one column to be selected. ");
			}
			Class inner = PojoUtil.getColumnClass((Class)lastSelect.getTableClass(), 
					(String) lastSelect.getColumnNames().get(0));
			if (!inner.equals(outerColumn)) {
				throw new IllegalArgumentException ("Inner select column type mismatch");
			}
			expressionIn.setInnerSelect(lastSelect);
		} else  if (ctx.literal_list() != null) {
			List<String> columnValuesString = new LinkedList<String> (); 
			LiteralValueType columnType = LiteralValueType.NULL;
			Literal_listContext lc = ctx.literal_list();
			
			if (lc.string_literal_list() != null) {
				columnType = LiteralValueType.STRING;
				for (String_literalContext sc : lc.string_literal_list().string_literal()) {
					columnValuesString.add(Util.unquote(sc.getText()));
				}
			} else if (lc.boolean_literal_list() != null) {
				columnType = LiteralValueType.BOOLEAN;
				for (Boolean_literalContext sc : lc.boolean_literal_list().boolean_literal()) {
					columnValuesString.add(sc.getText());
				}
			} else if (lc.number_literal_list() != null) {
				columnType = LiteralValueType.NUMBER;
				for (Number_literalContext sc : lc.number_literal_list().number_literal()) {
					columnValuesString.add(sc.getText());
				}
			} else {
				// Never.
				throw new IllegalArgumentException ("Unkonw Literal Value");
			}
			for (String s : columnValuesString) {
				// Check for Exceptions
				Util.createColumnObject(s, outerColumn, columnType);
			}
			expressionIn.setLiteralColumnValues(columnValuesString, columnType);
		} else {
			// Never.
			throw new IllegalArgumentException ("Unkonw IN argument");
		}
		expressionStack.push(expressionIn);
		lastExpressionStack.peek().setObject(expressionIn);
	}

	@Override
	public void enterTop_select(Top_selectContext ctx) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void exitTop_select(Top_selectContext ctx) {
		// TODO Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see org.microspace.parser.AntlrSimpleSqlListener#enterColumn_sort_name(org.microspace.parser.AntlrSimpleSqlParser.Column_sort_nameContext)
	 */
	@Override
	public void enterColumn_sort_name(Column_sort_nameContext ctx) {
		// TODO Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see org.microspace.parser.AntlrSimpleSqlListener#exitColumn_sort_name(org.microspace.parser.AntlrSimpleSqlParser.Column_sort_nameContext)
	 */
	@Override
	public void exitColumn_sort_name(Column_sort_nameContext ctx) {
		// TODO Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see org.microspace.parser.AntlrSimpleSqlListener#enterColumn_sort_list(org.microspace.parser.AntlrSimpleSqlParser.Column_sort_listContext)
	 */
	@Override
	public void enterColumn_sort_list(Column_sort_listContext ctx) {
		// TODO Auto-generated method stub
		
	}

	/* (non-Javadoc)
	 * @see org.microspace.parser.AntlrSimpleSqlListener#exitColumn_sort_list(org.microspace.parser.AntlrSimpleSqlParser.Column_sort_listContext)
	 */
	@Override
	public void exitColumn_sort_list(Column_sort_listContext ctx) {
		// TODO Auto-generated method stub
		
	}



	@Override
	public void enterSelect_expression_atom(Select_expression_atomContext ctx) {
		
	}

	@Override
	public void exitSelect_expression_atom(Select_expression_atomContext ctx) {
		
	}

	@Override
	public void enterColumn_bool_column(Column_bool_columnContext ctx) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void exitColumn_bool_column(Column_bool_columnContext ctx) {
		OperatorType operator;

		if (ctx.relational_op() == null) {
			throw new IllegalArgumentException ("Relational Operator Expected");
		} else if (ctx.relational_op().EQ() != null) {
			operator = OperatorType.EQ;
		} else if (ctx.relational_op().LTH() != null) {
			operator = OperatorType.LTH;
		} else if (ctx.relational_op().GTH() != null) {
			operator = OperatorType.GTH;
		} else if (ctx.relational_op().NOT_EQ() != null) {
			operator = OperatorType.NOT_EQ;
		} else if (ctx.relational_op().LET() != null) {
			operator = OperatorType.LET;
		} else if (ctx.relational_op().GET() != null) {
			operator = OperatorType.GET;
		} else {
			throw new IllegalArgumentException ("Operator Not Handled: " + ctx.relational_op());
		}
		String columnNameL = ctx.column_name(0).ID().getText();
		String columnNameR = ctx.column_name(1).ID().getText();
		Class<?> outerColumnL = PojoUtil.getColumnClass(selectStack.peek().getTableClass(), columnNameL);
		if (outerColumnL == null) {
			throw new IllegalArgumentException ("Column " + columnNameL + " not found.");
		}
		Class<?> outerColumnR = PojoUtil.getColumnClass(selectStack.peek().getTableClass(), columnNameL);
		if (outerColumnR == null) {
			throw new IllegalArgumentException ("Column " + columnNameR + " not found.");
		}
		String columnEscL = "";
		if (!ctx.column_name(0).BACKTICK().isEmpty()) {
			columnEscL = "`";
		}
		String columnEscR = "";
		if (!ctx.column_name(1).BACKTICK().isEmpty()) {
			columnEscR = "`";
		}
		ExpressionBooleanColumn<T> booleanExpression = new ExpressionBooleanColumn<> (
				selectStack.peek().getTableClass(), operator, 
				columnNameL, columnEscL, columnNameR, columnEscR);
		expressionStack.push(booleanExpression);
		lastExpressionStack.peek().setObject(booleanExpression);
		
		
	}

}