private void Join(TableRefBinding table, JoinCondition joinCondition) { _usedTableList.Add(table); _usedJoinConditionList.Add(joinCondition); if (_usedTableList.Count == _tables.Length) { // We have joined all tables, create the join order. // // First we create a list of the join conditions we used. List<Join> joinList = new List<Join>(); for (int i = 0; i < _usedTableList.Count; i++) { TableRefBinding usedTable = _usedTableList[i]; JoinCondition usedJoinCondition = _usedJoinConditionList[i]; // The first entry will be null, since we do not join from anywhere if (usedJoinCondition != null) { // Since we swapping the join sides according to the direction // we are joining from we need to create a clone. The clone // is a flat copy meaning that the tables and expressions // themselves are not cloned. usedJoinCondition = usedJoinCondition.Clone(); // If the right table is not the table we are joining to // swap the join sides. if (usedJoinCondition.RightTable != usedTable) usedJoinCondition.SwapSides(); } Join join = new Join(); join.JoinCondition = usedJoinCondition; join.TableRefBinding = usedTable; joinList.Add(join); } // Secondly and very important: We also have to create a list of all // join conditions NOT used. Later theses conditions will be combined // with the and-parts since they must also be checked. List<ExpressionNode> unusedConditionList = new List<ExpressionNode>(); foreach (JoinCondition jc in _joinConditions) { if (!_usedJoinConditionList.Contains(jc)) unusedConditionList.Add(jc.ToExpression()); } JoinOrder joinOrder = new JoinOrder(); joinOrder.Joins = joinList.ToArray(); joinOrder.UnusedConditions = unusedConditionList.ToArray(); _joinOrderList.Add(joinOrder); } else { // We are not yet finished with all tables. Find next table // to join with. bool hasJoin = false; foreach (JoinCondition nextJoin in _joinConditions) { if (!_usedJoinConditionList.Contains(nextJoin)) { TableRefBinding nextTable; if (_usedTableList.Contains(nextJoin.LeftTable)) { nextTable = nextJoin.RightTable; } else if (_usedTableList.Contains(nextJoin.RightTable)) { nextTable = nextJoin.LeftTable; } else { continue; } if (_usedTableList.Contains(nextTable)) continue; Join(nextTable, nextJoin); hasJoin = true; } } if (!hasJoin) { foreach (TableRefBinding t in _tables) { if (!_usedTableList.Contains(t)) Join(t, null); } } } _usedJoinConditionList.RemoveAt(_usedJoinConditionList.Count - 1); _usedTableList.RemoveAt(_usedTableList.Count - 1); }
public override AlgebraNode VisitJoinAlgebraNode(JoinAlgebraNode node) { // Check if node only consists of INNER join nodes and simple table reference nodes. // This algorithm assumes that the table references have been lineraized so that // // - JoinedTableReference appear on the LHS only (the last one must be NamedTableReference ovbiviously) // - NamedTableReference appear on the RHS only // // While scanning the node's children we create a list of all JoinedTableReferences and // NamedTableReferences. InnerJoinTableExtractor innerJoinTableExtractor = new InnerJoinTableExtractor(); innerJoinTableExtractor.Visit(node); if (!innerJoinTableExtractor.ConsistsOnlyOfInnerJoinsFiltersAndTables) { node.Left = VisitAlgebraNode(node.Left); node.Right = VisitAlgebraNode(node.Right); return(node); } else { TableAlgebraNode[] algebraNodes = innerJoinTableExtractor.GetTableNodes(); Dictionary <TableRefBinding, TableAlgebraNode> tableRefToNodeDictionary = new Dictionary <TableRefBinding, TableAlgebraNode>(); List <TableRefBinding> tableList = new List <TableRefBinding>(); foreach (TableAlgebraNode algebraNode in algebraNodes) { tableRefToNodeDictionary.Add(algebraNode.TableRefBinding, algebraNode); tableList.Add(algebraNode.TableRefBinding); } // Create a mapping RowBufferEntry -> ColumnRefBinding Dictionary <RowBufferEntry, ColumnRefBinding> rowBufferColumnDictionary = new Dictionary <RowBufferEntry, ColumnRefBinding>(); foreach (TableRefBinding tableRefBinding in tableList) { foreach (ColumnRefBinding columnRefBinding in tableRefBinding.ColumnRefs) { rowBufferColumnDictionary.Add(columnRefBinding.ValueDefinition.Target, columnRefBinding); } } // Create list of all possible join conditions and remaining AND-parts. List <JoinCondition> joinConditionList = new List <JoinCondition>(); List <ExpressionNode> andPartList = new List <ExpressionNode>(); ExpressionNode filter = AstUtil.CombineConditions(LogicalOperator.And, innerJoinTableExtractor.GetFilters()); foreach (ExpressionNode andPart in AstUtil.SplitCondition(LogicalOperator.And, filter)) { JoinCondition joinCondition = ConvertToJoinCondition(rowBufferColumnDictionary, andPart); if (joinCondition != null) { joinConditionList.Add(joinCondition); } else { andPartList.Add(andPart); } } // After creating the list of all join conditions and AND-parts we have all we need to create // an optimimal join order between all tables of this part of the table tree. JoinOrder bestJoinOrder = GetBestJoinOrder(tableList.ToArray(), joinConditionList.ToArray(), andPartList.ToArray()); // Get all tables that are introduced by this join order Dictionary <RowBufferEntry, ColumnValueDefinition> introducedColumns = GetIntroducedColumns(bestJoinOrder); // Combine AND-part list with all unused join conditions. andPartList.AddRange(bestJoinOrder.UnusedConditions); // Now we will re-create this part of the tree using the this join order. AlgebraNode lastAlgebraNode = null; for (int joinIndex = 0; joinIndex < bestJoinOrder.Joins.Length; joinIndex++) { Join join = bestJoinOrder.Joins[joinIndex]; AlgebraNode tableInput; TableAlgebraNode tableNode = tableRefToNodeDictionary[join.TableRefBinding]; ExpressionNode tableFilter = ExtractConditionsApplicableToTable(introducedColumns, andPartList, join.TableRefBinding); if (tableFilter == null) { tableInput = tableNode; } else { FilterAlgebraNode filterAlgebraNode = new FilterAlgebraNode(); filterAlgebraNode.Input = tableNode; filterAlgebraNode.Predicate = tableFilter; tableInput = filterAlgebraNode; } if (lastAlgebraNode == null) { // This was the first one. lastAlgebraNode = tableInput; } else { // Not the first one, we can create a join with the current table reference // and last table reference. // Get all AND-parts that can be applied to the tables already joined. // This expression is merged to one condition. ExpressionNode[] applicableAndParts = GetAndPartsApplicableToJoin(introducedColumns, bestJoinOrder, joinIndex, andPartList, true); ExpressionNode condition = AstUtil.CombineConditions(LogicalOperator.And, applicableAndParts); ExpressionNode joinCondition; if (join.JoinCondition == null) { joinCondition = null; } else { joinCondition = join.JoinCondition.ToExpression(); } ExpressionNode completeCondition = AstUtil.CombineConditions(LogicalOperator.And, condition, joinCondition); JoinAlgebraNode joinAlgebraNode = new JoinAlgebraNode(); joinAlgebraNode.Op = JoinAlgebraNode.JoinOperator.InnerJoin; joinAlgebraNode.Left = lastAlgebraNode; joinAlgebraNode.Right = tableInput; joinAlgebraNode.Predicate = completeCondition; // Next time this newly created join is the last table reference. lastAlgebraNode = joinAlgebraNode; } } return(lastAlgebraNode); } }
private void Join(TableRefBinding table, JoinCondition joinCondition) { _usedTableList.Add(table); _usedJoinConditionList.Add(joinCondition); if (_usedTableList.Count == _tables.Length) { // We have joined all tables, create the join order. // // First we create a list of the join conditions we used. List <Join> joinList = new List <Join>(); for (int i = 0; i < _usedTableList.Count; i++) { TableRefBinding usedTable = _usedTableList[i]; JoinCondition usedJoinCondition = _usedJoinConditionList[i]; // The first entry will be null, since we do not join from anywhere if (usedJoinCondition != null) { // Since we swapping the join sides according to the direction // we are joining from we need to create a clone. The clone // is a flat copy meaning that the tables and expressions // themselves are not cloned. usedJoinCondition = usedJoinCondition.Clone(); // If the right table is not the table we are joining to // swap the join sides. if (usedJoinCondition.RightTable != usedTable) { usedJoinCondition.SwapSides(); } } Join join = new Join(); join.JoinCondition = usedJoinCondition; join.TableRefBinding = usedTable; joinList.Add(join); } // Secondly and very important: We also have to create a list of all // join conditions NOT used. Later theses conditions will be combined // with the and-parts since they must also be checked. List <ExpressionNode> unusedConditionList = new List <ExpressionNode>(); foreach (JoinCondition jc in _joinConditions) { if (!_usedJoinConditionList.Contains(jc)) { unusedConditionList.Add(jc.ToExpression()); } } JoinOrder joinOrder = new JoinOrder(); joinOrder.Joins = joinList.ToArray(); joinOrder.UnusedConditions = unusedConditionList.ToArray(); _joinOrderList.Add(joinOrder); } else { // We are not yet finished with all tables. Find next table // to join with. bool hasJoin = false; foreach (JoinCondition nextJoin in _joinConditions) { if (!_usedJoinConditionList.Contains(nextJoin)) { TableRefBinding nextTable; if (_usedTableList.Contains(nextJoin.LeftTable)) { nextTable = nextJoin.RightTable; } else if (_usedTableList.Contains(nextJoin.RightTable)) { nextTable = nextJoin.LeftTable; } else { continue; } if (_usedTableList.Contains(nextTable)) { continue; } Join(nextTable, nextJoin); hasJoin = true; } } if (!hasJoin) { foreach (TableRefBinding t in _tables) { if (!_usedTableList.Contains(t)) { Join(t, null); } } } } _usedJoinConditionList.RemoveAt(_usedJoinConditionList.Count - 1); _usedTableList.RemoveAt(_usedTableList.Count - 1); }