package jeql.engine.query.join; import jeql.api.row.BasicRow; import jeql.api.row.Row; import jeql.api.row.RowIterator; import jeql.api.row.RowList; import jeql.api.row.RowSchema; import jeql.engine.Scope; import jeql.engine.index.EqualityRowIndex; import jeql.engine.query.QueryScope; import jeql.syntax.BinaryExpressionNode; import jeql.syntax.FromItem; import jeql.syntax.ParseTreeNode; import jeql.syntax.TableColumnNode; import jeql.syntax.operation.Operation; public class IndexedJoinRowList implements RowList { public static int indexableOperandSide(ParseTreeNode condExpr, FromItem from, RowSchema fromSchema) { // check for acceptable pattern if (! (condExpr instanceof BinaryExpressionNode)) return -1; BinaryExpressionNode binExpr = (BinaryExpressionNode) condExpr; if (binExpr.getOpCode() != Operation.EQ) return -1; int fromKeyIndex = -1; String fromTblName = from.getAliasOrTableName(); if (isColumnInTable(binExpr.getSide(0), fromTblName, fromSchema)) { fromKeyIndex = 0; } else if (isColumnInTable(binExpr.getSide(1), fromTblName, fromSchema)) { fromKeyIndex = 1; } return fromKeyIndex; } private static boolean isColumnInTable(ParseTreeNode expr, String tblName, RowSchema schema) { if (! (expr instanceof TableColumnNode)) return false; TableColumnNode tblCol = (TableColumnNode) expr; String tcTableName = tblCol.getTableName(); boolean isMatchingTblName = tcTableName == null || tblName.equals(tcTableName); String tcColName = tblCol.getColName(); boolean isColumnPresent = schema.hasCol(tcColName); return isMatchingTblName && isColumnPresent; } public static String keyColName(ParseTreeNode condExpr, int indexSide) { BinaryExpressionNode binExpr = (BinaryExpressionNode) condExpr; TableColumnNode tblCol = (TableColumnNode) binExpr.getSide(indexSide); return tblCol.getColName(); } public static ParseTreeNode driveTblExpr(ParseTreeNode condExpr, int indexSide) { BinaryExpressionNode binExpr = (BinaryExpressionNode) condExpr; return binExpr.getSide(1 - indexSide); } private RowSchema schema = null; private RowList driveRS; private int joinType = FromItem.JOIN_INNER; private QueryScope scope; private ParseTreeNode driveTblExpr; private String keyCol; private EqualityRowIndex index; private JoinIteratorProvider joinProv; public IndexedJoinRowList(RowList driveRS, RowList joinRS, int indexColSide, ParseTreeNode equalityExpr, int joinType, QueryScope scope) { this.joinType = joinType; this.scope = scope; this.driveRS = driveRS; schema = new RowSchema(driveRS.getSchema(), joinRS.getSchema()); keyCol = keyColName(equalityExpr, indexColSide); index = new EqualityRowIndex(joinRS, keyCol); driveTblExpr = driveTblExpr(equalityExpr, indexColSide); joinProv = new IndexedJoinIteratorProvider(joinRS, equalityExpr, indexColSide); } public RowSchema getSchema() { return schema; } public RowIterator iterator() { IndexedJoinRowIterator joinIt = new IndexedJoinRowIterator(schema, driveRS, //driveTblExpr, index, joinProv, joinType, scope); return joinIt; } private static class IndexedJoinRowIterator implements RowIterator { private RowSchema schema; private int schema1Size = -1; private int schema2Size = -1; //private ParseTreeNode driveExpr; //private EqualityRowIndex index; private JoinIteratorProvider joinProv; private QueryScope scope; private RowIterator driveRowIt; private RowIterator joinRowIt; private boolean isLeftOuterJoin = false; //private boolean applyFilter = true; public IndexedJoinRowIterator(RowSchema schema, RowList driveRS, JoinIteratorProvider joinProv, //ParseTreeNode driveExpr, //EqualityRowIndex index, int joinType, QueryScope scope) { this.schema = schema; //this.driveExpr = driveExpr; //this.index = index; this.joinProv = joinProv; this.scope = scope; schema1Size = driveRS.getSchema().size(); schema2Size = schema.size() - schema1Size; if (joinType == FromItem.JOIN_LEFT_OUTER) isLeftOuterJoin = true; driveRowIt = driveRS.iterator(); } public RowSchema getSchema() { return schema; } private Row driveRow = null; private boolean isNewLeftRow = false; public Row next() { while (true) { if (driveRow == null && driveRowIt != null) { // get next drive row driveRow = driveRowIt.next(); isNewLeftRow = true; joinRowIt = null; } // at this point if driveRow is null drive iteration is finished if (driveRow == null) { driveRowIt = null; return null; } Row joinRow = nextJoinRow(driveRow); if (joinRow == null) { // compute LEFT OUTER JOIN with null RHS Row currDriveRow = driveRow; driveRow = null; if (isNewLeftRow && isLeftOuterJoin) { //applyFilter = false; joinRowIt = null; return BasicRow.createRow(currDriveRow, schema1Size, null, schema2Size); } // else loop to get new driveRow continue; } else { // have non-null index row - can return a row isNewLeftRow = false; return BasicRow.createRow(driveRow, schema1Size, joinRow, schema2Size); } } } private Row nextJoinRow(Row driveRow) { if (joinRowIt == null) { ((QueryScope) scope).setRow(driveRow); joinRowIt = joinProv.iterator(scope); /* Object val = driveExpr.eval(scope); joinRowIt = index.iterator(val); */ // key not found in index if (joinRowIt == null) return null; } Row joinRow = joinRowIt.next(); // at end of join table if (joinRow == null) return null; return joinRow; } } interface JoinIteratorProvider { RowSchema getSchema(); RowIterator iterator(Scope scope); } static class IndexedJoinIteratorProvider implements JoinIteratorProvider { private RowSchema schema; private ParseTreeNode driveTblExpr; private EqualityRowIndex index; public IndexedJoinIteratorProvider(RowList joinRS, ParseTreeNode equalityExpr, int indexColSide ) { schema = joinRS.getSchema(); String keyCol = keyColName(equalityExpr, indexColSide); index = new EqualityRowIndex(joinRS, keyCol); driveTblExpr = driveTblExpr(equalityExpr, indexColSide); } public RowSchema getSchema() { return schema; } public RowIterator iterator(Scope scope) { Object val = driveTblExpr.eval(scope); RowIterator joinRowIt = index.iterator(val); return joinRowIt; } } }