1 //--------------------------------------------------------------------- 2 // <copyright file="Propagator.JoinPropagator.JoinPredicateVisitor.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // 6 // @owner Microsoft 7 // @backupOwner Microsoft 8 //--------------------------------------------------------------------- 9 10 using System.Data.Common.CommandTrees; 11 using System.Collections.Generic; 12 using System.Diagnostics; 13 using System.Collections.ObjectModel; 14 namespace System.Data.Mapping.Update.Internal 15 { 16 internal partial class Propagator 17 { 18 private partial class JoinPropagator 19 { 20 /// <summary> 21 /// Extracts equi-join properties from a join condition. 22 /// </summary> 23 /// <remarks> 24 /// Assumptions: 25 /// <list> 26 /// <item>Only conjunctions of equality predicates are supported</item> 27 /// <item>Each equality predicate is of the form (left property == right property). The order 28 /// is important.</item> 29 /// </list> 30 /// </remarks> 31 private class JoinConditionVisitor : UpdateExpressionVisitor<object> 32 { 33 #region Constructors 34 /// <summary> 35 /// Initializes a join predicate visitor. The visitor will populate the given property 36 /// lists with expressions describing the left and right hand side of equi-join 37 /// sub-clauses. 38 /// </summary> JoinConditionVisitor()39 private JoinConditionVisitor() 40 { 41 m_leftKeySelectors = new List<DbExpression>(); 42 m_rightKeySelectors = new List<DbExpression>(); 43 } 44 #endregion 45 46 #region Fields 47 private readonly List<DbExpression> m_leftKeySelectors; 48 private readonly List<DbExpression> m_rightKeySelectors; 49 private static readonly string s_visitorName = typeof(JoinConditionVisitor).FullName; 50 #endregion 51 52 #region Properties 53 override protected string VisitorName 54 { 55 get { return s_visitorName; } 56 } 57 #endregion 58 59 #region Methods 60 #region Static helper methods 61 /// <summary> 62 /// Determine properties from the left and right inputs to an equi-join participating 63 /// in predicate. 64 /// </summary> 65 /// <remarks> 66 /// The property definitions returned are 'aligned'. If the join predicate reads: 67 /// <code> 68 /// a = b AND c = d AND e = f 69 /// </code> 70 /// then the output is as follows: 71 /// <code> 72 /// leftProperties = {a, c, e} 73 /// rightProperties = {b, d, f} 74 /// </code> 75 /// See Walker class for an explanation of this coding pattern. 76 /// </remarks> GetKeySelectors(DbExpression joinCondition, out ReadOnlyCollection<DbExpression> leftKeySelectors, out ReadOnlyCollection<DbExpression> rightKeySelectors)77 static internal void GetKeySelectors(DbExpression joinCondition, out ReadOnlyCollection<DbExpression> leftKeySelectors, out ReadOnlyCollection<DbExpression> rightKeySelectors) 78 { 79 EntityUtil.CheckArgumentNull(joinCondition, "joinCondition"); 80 81 // Constructs a new predicate visitor, which implements a visitor for expression nodes 82 // and returns no values. This visitor instead builds up a list of properties as leaves 83 // of the join predicate are visited. 84 JoinConditionVisitor visitor = new JoinConditionVisitor(); 85 86 // Walk the predicate using the predicate visitor. 87 joinCondition.Accept(visitor); 88 89 // Retrieve properties discovered visiting predicate leaf nodes. 90 leftKeySelectors = visitor.m_leftKeySelectors.AsReadOnly(); 91 rightKeySelectors = visitor.m_rightKeySelectors.AsReadOnly(); 92 93 Debug.Assert(leftKeySelectors.Count == rightKeySelectors.Count, 94 "(Update/JoinPropagator) The equi-join must have an equal number of left and right properties"); 95 } 96 #endregion 97 98 #region Visitor implementation 99 /// <summary> 100 /// Visit and node after its children have visited. There is nothing to do here 101 /// because only leaf equality nodes contain properties extracted by this visitor. 102 /// </summary> 103 /// <param name="node">And expression node</param> 104 /// <returns>Results ignored by this visitor implementation.</returns> Visit(DbAndExpression node)105 public override object Visit(DbAndExpression node) 106 { 107 EntityUtil.CheckArgumentNull(node, "node"); 108 109 Visit(node.Left); 110 Visit(node.Right); 111 112 return null; 113 } 114 115 /// <summary> 116 /// Perform work for an equality expression node. 117 /// </summary> 118 /// <param name="node">Equality expresion node</param> 119 /// <returns>Results ignored by this visitor implementation.</returns> Visit(DbComparisonExpression node)120 public override object Visit(DbComparisonExpression node) 121 { 122 EntityUtil.CheckArgumentNull(node, "node"); 123 124 if (DbExpressionKind.Equals == node.ExpressionKind) 125 { 126 m_leftKeySelectors.Add(node.Left); 127 m_rightKeySelectors.Add(node.Right); 128 return null; 129 } 130 else 131 { 132 throw ConstructNotSupportedException(node); 133 } 134 } 135 #endregion 136 #endregion 137 } 138 } 139 } 140 } 141