/// <summary> /// Constructs a join propagator. /// </summary> /// <param name="left">Result of propagating changes in the left input to the join</param> /// <param name="right">Result of propagating changes in the right input to the join</param> /// <param name="node">Join operator in update mapping view over which to propagate changes</param> /// <param name="parent">Handler of propagation for the entire update mapping view</param> internal JoinPropagator(ChangeNode left, ChangeNode right, DbJoinExpression node, Propagator parent) { EntityUtil.CheckArgumentNull(left, "left"); EntityUtil.CheckArgumentNull(right, "right"); EntityUtil.CheckArgumentNull(node, "node"); EntityUtil.CheckArgumentNull(parent, "parent"); m_left = left; m_right = right; m_joinExpression = node; m_parent = parent; Debug.Assert(DbExpressionKind.LeftOuterJoin == node.ExpressionKind || DbExpressionKind.InnerJoin == node.ExpressionKind, "(Update/JoinPropagagtor/JoinEvaluator) " + "caller must ensure only left outer and inner joins are requested"); // Retrieve propagation rules for the join type of the expression. if (DbExpressionKind.InnerJoin == m_joinExpression.ExpressionKind) { m_insertRules = s_innerJoinInsertRules; m_deleteRules = s_innerJoinDeleteRules; } else { m_insertRules = s_leftOuterJoinInsertRules; m_deleteRules = s_leftOuterJoinDeleteRules; } // Figure out key selectors involved in the equi-join (if it isn't an equi-join, we don't support it) JoinConditionVisitor.GetKeySelectors(node.JoinCondition, out m_leftKeySelectors, out m_rightKeySelectors); // Find the key selector expressions in the left and right placeholders m_leftPlaceholderKey = ExtractKey(m_left.Placeholder, m_leftKeySelectors, m_parent); m_rightPlaceholderKey = ExtractKey(m_right.Placeholder, m_rightKeySelectors, m_parent); }
/// <summary> /// Performs join propagation. /// </summary> /// <returns>Changes propagated to the current join node in the update mapping view.</returns> internal ChangeNode Propagate() { // Construct an empty change node for the result ChangeNode result = Propagator.BuildChangeNode(m_joinExpression); // Gather all keys involved in the join JoinDictionary leftDeletes = ProcessKeys(m_left.Deleted, m_leftKeySelectors); JoinDictionary leftInserts = ProcessKeys(m_left.Inserted, m_leftKeySelectors); JoinDictionary rightDeletes = ProcessKeys(m_right.Deleted, m_rightKeySelectors); JoinDictionary rightInserts = ProcessKeys(m_right.Inserted, m_rightKeySelectors); var allKeys = leftDeletes.Keys .Concat(leftInserts.Keys) .Concat(rightDeletes.Keys) .Concat(rightInserts.Keys) .Distinct(m_parent.UpdateTranslator.KeyComparer); // Perform propagation one key at a time foreach (CompositeKey key in allKeys) { Propagate(key, result, leftDeletes, leftInserts, rightDeletes, rightInserts); } // Construct a new placeholder (see ChangeNode.Placeholder) for the join result node. result.Placeholder = CreateResultTuple(Tuple.Create((CompositeKey)null, m_left.Placeholder), Tuple.Create((CompositeKey)null, m_right.Placeholder), result); return(result); }
/// <summary> /// Constructs an evaluator for evaluating expressions for the given row. /// </summary> /// <param name="row">Row to match</param> /// <param name="parent">Propagator context</param> private Evaluator(PropagatorResult row, Propagator parent) { EntityUtil.CheckArgumentNull(row, "row"); EntityUtil.CheckArgumentNull(parent, "parent"); m_row = row; m_parent = parent; }
/// <summary> /// Propagate changes from C-Space (contained in <paramref name="parent" /> to the S-Space. /// </summary> /// <remarks> /// See Walker class for an explanation of this coding pattern. /// </remarks> /// <param name="parent">Grouper supporting retrieval of changes for C-Space /// extents referenced in the update mapping view.</param> /// <param name="table">Table for which updates are being produced.</param> /// <param name="umView">Update mapping view to propagate.</param> /// <returns>Changes in S-Space.</returns> static internal ChangeNode Propagate(UpdateTranslator parent, EntitySet table, DbQueryCommandTree umView) { // Construct a new instance of a propagator, which implements a visitor interface // for expression nodes (nodes in the update mapping view) and returns changes nodes // (seeded by C-Space extent changes returned by the grouper). DbExpressionVisitor <ChangeNode> propagator = new Propagator(parent, table); // Walk the update mapping view using the visitor pattern implemented in this class. // The update mapping view describes the S-Space table we're targeting, so the result // returned for the root of view corresponds to changes propagated to the S-Space. return(umView.Query.Accept(propagator)); }
// extracts key values from row expression private static CompositeKey ExtractKey(PropagatorResult change, ReadOnlyCollection <DbExpression> keySelectors, Propagator parent) { Debug.Assert(null != change && null != keySelectors && null != parent); PropagatorResult[] keyValues = new PropagatorResult[keySelectors.Count]; for (int i = 0; i < keySelectors.Count; i++) { PropagatorResult constant = Evaluator.Evaluate(keySelectors[i], change, parent); keyValues[i] = constant; } return(new CompositeKey(keyValues)); }
/// <summary> /// Utility method filtering out a set of rows given a predicate. /// </summary> /// <param name="predicate">Match criteria.</param> /// <param name="rows">Input rows.</param> /// <param name="parent">Propagator context</param> /// <returns>Input rows matching criteria.</returns> internal static IEnumerable <PropagatorResult> Filter(DbExpression predicate, IEnumerable <PropagatorResult> rows, Propagator parent) { foreach (PropagatorResult row in rows) { if (EvaluatePredicate(predicate, row, parent)) { yield return(row); } } }
/// <summary> /// Evaluates scalar node. /// </summary> /// <param name="node">Sub-query returning a scalar value.</param> /// <param name="row">Row to evaluate.</param> /// <param name="parent">Propagator context.</param> /// <returns>Scalar result.</returns> static internal PropagatorResult Evaluate(DbExpression node, PropagatorResult row, Propagator parent) { DbExpressionVisitor <PropagatorResult> evaluator = new Evaluator(row, parent); return(node.Accept(evaluator)); }
/// <summary> /// Utility method determining whether a row matches a predicate. /// </summary> /// <remarks> /// See Walker class for an explanation of this coding pattern. /// </remarks> /// <param name="predicate">Match criteria.</param> /// <param name="row">Input row.</param> /// <param name="parent">Propagator context</param> /// <returns><c>true</c> if the row matches the criteria; <c>false</c> otherwise</returns> internal static bool EvaluatePredicate(DbExpression predicate, PropagatorResult row, Propagator parent) { Evaluator evaluator = new Evaluator(row, parent); PropagatorResult expressionResult = predicate.Accept(evaluator); bool?result = ConvertResultToBool(expressionResult); // unknown --> false at base of predicate return(result ?? false); }