/// <summary> /// Selects a join type (merge or hash) for the given join edge, and returns a join node /// which merges the left and right nodes of the given join edge. /// </summary> /// <param name="joinEdge">The join edge.</param> /// <returns> /// The join node merging the left and right nodes from the given join edge. /// </returns> public jg::Node ChooseJoinType(jg::Edge joinEdge) { // // get the operators for the left and right nodes. if a node does not have an operator, // then it must have an SAP and we select a scan operator for it here. Operator leftOp, rightOp; if (joinEdge.Left.HasOperator) { leftOp = joinEdge.Left.Operator; } else { leftOp = ChooseScan(joinEdge.Left); } if (joinEdge.Right.HasOperator) { rightOp = joinEdge.Right.Operator; } else { rightOp = ChooseScan(joinEdge.Right); } // // create a new, empty join node var joinNode = new jg::Node(null, null); if (CanDoMergeJoin(leftOp, rightOp)) { // // heuristic: if we can do a merge join, do so var leftSortOrder = leftOp.GetOutputSortOrder(); var rightSortOrder = rightOp.GetOutputSortOrder(); var joinVars = new HashSet <long>(leftSortOrder); joinVars.IntersectWith(rightSortOrder); // // the merge joinoperator is then constructed in the Datastructures.Queries.QueryPlan // class. joinNode.Operator = QueryPlan.GetMergeJoin(leftOp, rightOp, joinVars.ToArray()); } else { // // if we cannot do a merge join, do a hash join instead. the hash join operator is // then constructed in the Datastructures.Queries.QueryPlan class. joinNode.Operator = QueryPlan.GetHashJoin(leftOp, rightOp); } return(joinNode); }
/// <summary> /// Merges a pair of given join nodes, causing all edges connecting to either of the two /// join nodes to connect to the newly formed node which merges the two. /// </summary> /// <param name="joinGraph">The join graph.</param> /// <param name="oldEdge">The old edge which is to be replaced.</param> /// <param name="newNode">The new node which takes merges the two nodes belonging to the given edge.</param> private static void MergeJoinNodes(jg::Graph joinGraph, jg::Edge oldEdge, jg::Node newNode) { // // remove the old edge and its nodes from the join graph joinGraph.Edges.Remove(oldEdge); joinGraph.Nodes.Remove(oldEdge.Left); joinGraph.Nodes.Remove(oldEdge.Right); // // any edges connecting to the removed nodes will be redirected to the new node that // merges the two foreach (var edge in joinGraph.Edges) { if (edge.Left.Equals(oldEdge.Left) || edge.Left.Equals(oldEdge.Right)) { edge.Left = newNode; } if (edge.Right.Equals(oldEdge.Left) || edge.Right.Equals(oldEdge.Right)) { edge.Right = newNode; } } // // we might end up with duplicate edges pointing to the new node, so make sure to // filter those out var uniqueEdges = joinGraph.Edges.Distinct().ToList(); joinGraph.Edges.Clear(); joinGraph.Edges.AddRange(uniqueEdges); // // insert the new node into the join graph joinGraph.Nodes.Add(newNode); }
/// <summary> /// Computes a descriptive query plan for a given Basic Graph Pattern within a given /// database context. /// </summary> /// <param name="context">The database context.</param> /// <param name="pattern">The Basic Graph Pattern.</param> /// <returns> /// A descriptive query plan for the given BGP. /// </returns> public static QueryPlan ComputePlan(Database context, params Triple <TripleItem, TripleItem, TripleItem>[] pattern) { // // initialize the decision engine var decisionEngine = GetDecisionEngine(context); // // compute the atom collapse var collapse = AtomCollapse.Compute(pattern); // // step 1: compute join graph // // working copy of collapse graph nodes var currentCollapse = new List <ac::Node>(); foreach (var node in collapse.Nodes) { currentCollapse.Add(node.Copy()); } List <ac::Node> seeds; var todo = new List <Triple <TripleItem, TripleItem, TripleItem> >(); var eval = new List <ac::Node>(); do { // // compute the seeds seeds = ComputeSeeds(currentCollapse); // // pick one var seed = decisionEngine.ChooseSeed(seeds, collapse, currentCollapse).Copy(); // // compute new todo's if (todo.Contains(seed.FirstSAP)) { todo.Remove(seed.FirstSAP); } foreach (var sap in ComputeNewTodos(seed, currentCollapse)) { todo.AddIfNotPresent(sap); } // // update the current collapse foreach (var node in currentCollapse) { if (node.SAPs.Contains(seed.FirstSAP)) { node.SAPs.Remove(seed.FirstSAP); } } currentCollapse.RemoveAll(n => n.SAPs.Count == 0); // // add the seed to the evaluation list eval.Add(seed); } while (todo.Count > 0); // // compute edges var collapseEdges = new List <ac::Edge>(); foreach (var edge in collapse.Edges) { foreach (var x in eval) { foreach (var y in eval) { if (edge.Left.Equals(x) && edge.Right.Equals(y)) { collapseEdges.Add(edge); } } } } // // create join graph nodes var joinNodes = new List <jg::Node>(); foreach (var cNode in eval) { var jNode = new jg::Node(cNode.Item, cNode.FirstSAP); joinNodes.Add(jNode); } // // create join graph edges var joinEdges = new List <jg::Edge>(); foreach (var cEdge in collapseEdges) { foreach (var x in joinNodes) { foreach (var y in joinNodes) { if (cEdge.Left.Item.Equals(x.Item) && cEdge.Left.FirstSAP.Equals(x.SAP)) { if (cEdge.Right.Item.Equals(y.Item) && cEdge.Right.FirstSAP.Equals(y.SAP)) { var jEdge = new jg::Edge(x, y); foreach (var lbl in cEdge.Labels) { jEdge.Labels.Add(lbl); } joinEdges.Add(jEdge); } } } } } // // make the join graph var joinGraph = new jg::Graph(joinNodes, joinEdges); // // step 2: compute query plan if (joinGraph.Edges.Count == 0) { var n = joinGraph.Nodes.ElementAt(0); n.Operator = decisionEngine.ChooseScan(n); } else { while (joinGraph.Nodes.Count > 1) { // // choose a join to perform var joinEdge = decisionEngine.ChooseJoinEdge(joinGraph); // // compute the join node for this edge var newNode = decisionEngine.ChooseJoinType(joinEdge); // // merge the nodes of the join edge MergeJoinNodes(joinGraph, joinEdge, newNode); } } // // create plan object var plan = new QueryPlan(joinGraph.Nodes.ElementAt(0).Operator); // // assign memory to operators AssignMemory(context, plan.Root); // // return the query plan return(plan); }