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