package com.alvazan.orm.parser.antlr; public class OptimizeAddJoinInfo { private ParsedNode root; public ParsedNode walkTree(ParsedNode originalRoot) { this.root = originalRoot; walkTheTree(originalRoot); return this.root; } private void walkTheTree(ParsedNode node) { ParsedNode left = node.getChild(ChildSide.LEFT); ParsedNode right = node.getChild(ChildSide.RIGHT); if(node.isAndOrType()) { walkTheTree(left); walkTheTree(right); JoinMeta info = switchChildrenIfNeeded(node, left, right); node.setJoinMeta(info); return; } else if(node.isBetweenExpression()) { ParsedNode leftGrand = left.getChild(ChildSide.LEFT); ViewInfoImpl viewInfo = leftGrand.getViewInfo(); JoinInfo info = new JoinInfo(viewInfo, null, null, null, JoinType.NONE); JoinMeta meta1 = new JoinMeta(info, info.getJoinType()); node.setJoinMeta(meta1); return; } else if(node.isInExpression()) { ViewInfoImpl viewInfo = left.getViewInfo(); JoinInfo info = new JoinInfo(viewInfo, null, null, null, JoinType.NONE); JoinMeta meta1 = new JoinMeta(info, info.getJoinType()); node.setJoinMeta(meta1); return; } //let's find out the join type JoinMeta meta = findDirectJoin(left, right); node.setJoinMeta(meta); } private JoinMeta switchChildrenIfNeeded(ParsedNode node, ParsedNode left, ParsedNode right) { JoinMeta leftType = left.getJoinMeta(); JoinMeta rightType = right.getJoinMeta(); JoinMeta info = leftType.fetchJoinMeta(rightType); JoinInfo primary = info.getPrimaryJoinInfo(); if(primary.getJoinType() != JoinType.NONE) { ViewInfoImpl primaryTable = primary.getPrimaryTable(); if(!rightType.contains(primaryTable)) { //we need to rewrite the tree so the joining table is on the left(right now, joining is the right side) checkNoSidesHavePrimaryView(leftType, primaryTable); node.setChild(ChildSide.LEFT, right); node.setChild(ChildSide.RIGHT, left); } else if(leftType.contains(primaryTable)) throw new RuntimeException("bug, should never get here. Both sides should not both contain the primary table unless jointype=NONE"); } return info; } private void checkNoSidesHavePrimaryView(JoinMeta leftType, ViewInfoImpl primaryTable) { if(!leftType.contains(primaryTable)) throw new RuntimeException("bug, should never get here, one side should have primarytable or we can't join"); } private JoinMeta findDirectJoin(ParsedNode left, ParsedNode right) { //At this point, we know the left will be a table, but the rightside could be a constant or another table or same table ViewInfoImpl view1 = left.getViewInfo(); if(right.isConstant() || right.isParameter()) { JoinInfo info = new JoinInfo(view1, null, null, null, JoinType.NONE); return new JoinMeta(info, info.getJoinType()); } //okay, table vs. table then ViewInfoImpl view2 = right.getViewInfo(); if(view1.equals(view2)) { JoinInfo info = new JoinInfo(view1, null, null, null, JoinType.NONE); return new JoinMeta(info, info.getJoinType()); } JoinInfo info = view1.getJoinInfo(view2); if(info == null) throw new IllegalArgumentException("Sorry, but you have a and/or clause on alias="+view1.getAlias()+" and alias="+view2.getAlias()+ " where the two tables have another join between them that needs " + "to happen first. Rewrite your query. (ie. something like b&(c or a)" + " needs to be rewritten to b&c or b&a as b is in the middle"); JoinMeta comp = new JoinMeta(info, info.getJoinType()); return comp; } }