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);
}
}