File: JoinElimination.cs

package info (click to toggle)
mono 6.12.0.199%2Bdfsg-6
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 1,296,836 kB
  • sloc: cs: 11,181,803; xml: 2,850,076; ansic: 699,709; cpp: 123,344; perl: 59,361; javascript: 30,841; asm: 21,853; makefile: 20,405; sh: 15,009; python: 4,839; pascal: 925; sql: 859; sed: 16; php: 1
file content (225 lines) | stat: -rw-r--r-- 8,597 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
//---------------------------------------------------------------------
// <copyright file="JoinElimination.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner  Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------

using System;
using System.Collections.Generic;
//using System.Diagnostics; // Please use PlanCompiler.Assert instead of Debug.Assert in this class...

// It is fine to use Debug.Assert in cases where you assert an obvious thing that is supposed
// to prevent from simple mistakes during development (e.g. method argument validation 
// in cases where it was you who created the variables or the variables had already been validated or 
// in "else" clauses where due to code changes (e.g. adding a new value to an enum type) the default 
// "else" block is chosen why the new condition should be treated separately). This kind of asserts are 
// (can be) helpful when developing new code to avoid simple mistakes but have no or little value in 
// the shipped product. 
// PlanCompiler.Assert *MUST* be used to verify conditions in the trees. These would be assumptions 
// about how the tree was built etc. - in these cases we probably want to throw an exception (this is
// what PlanCompiler.Assert does when the condition is not met) if either the assumption is not correct 
// or the tree was built/rewritten not the way we thought it was.
// Use your judgment - if you rather remove an assert than ship it use Debug.Assert otherwise use
// PlanCompiler.Assert.

using System.Globalization;

using System.Data.Query.InternalTrees;
using System.Data.Metadata.Edm;

namespace System.Data.Query.PlanCompiler
{
    /// <summary>
    /// The JoinElimination module is intended to do just that - eliminate unnecessary joins. 
    /// This module deals with the following kinds of joins
    ///    * Self-joins: The join can be eliminated, and either of the table instances can be 
    ///                  used instead
    ///    * Implied self-joins: Same as above
    ///    * PK-FK joins: (More generally, UniqueKey-FK joins): Eliminate the join, and use just the FK table, if no 
    ///       column of the PK table is used (other than the join condition)
    ///    * PK-PK joins: Eliminate the right side table, if we have a left-outer join
    /// </summary>
    internal class JoinElimination : BasicOpVisitorOfNode
    {
        #region private constants
        private const string SqlServerCeNamespaceName = "SqlServerCe";
        #endregion

        #region private state
        private PlanCompiler m_compilerState;
        private Command Command { get { return m_compilerState.Command; } }
        private ConstraintManager ConstraintManager { get { return m_compilerState.ConstraintManager;  } }
        private Dictionary<Node, Node> m_joinGraphUnnecessaryMap = new Dictionary<Node,Node>();
        private VarRemapper m_varRemapper;
        private bool m_treeModified = false;
        private VarRefManager m_varRefManager;
        private Nullable<bool> m_isSqlCe = null;
        #endregion

        #region constructors
        private JoinElimination(PlanCompiler compilerState)
        {
            m_compilerState = compilerState;
            m_varRemapper = new VarRemapper(m_compilerState.Command);
            m_varRefManager = new VarRefManager(m_compilerState.Command); 
        }
        #endregion

        #region public surface
        internal static bool Process(PlanCompiler compilerState)
        {
            JoinElimination je = new JoinElimination(compilerState);
            je.Process();
            return je.m_treeModified;
        }
        #endregion

        #region private methods

        /// <summary>
        /// Invokes the visitor
        /// </summary>
        private void Process()
        {
            this.Command.Root = VisitNode(this.Command.Root);
        }

        #region JoinHelpers

        #region Building JoinGraphs
        /// <summary>
        /// Do we need to build a join graph for this node - returns false, if we've already
        /// processed this
        /// </summary>
        /// <param name="joinNode"></param>
        /// <returns></returns>
        private bool NeedsJoinGraph(Node joinNode)
        {
            return !m_joinGraphUnnecessaryMap.ContainsKey(joinNode);
        }

        /// <summary>
        /// Do the real processing of the join graph. 
        /// </summary>
        /// <param name="joinNode">current join node</param>
        /// <returns>modified join node</returns>
        private Node ProcessJoinGraph(Node joinNode)
        {
            // Build the join graph
            JoinGraph joinGraph = new JoinGraph(this.Command, this.ConstraintManager, this.m_varRefManager, joinNode, this.IsSqlCeProvider);

            // Get the transformed node tree
            VarMap remappedVars;
            Dictionary<Node, Node> processedNodes;
            Node newNode = joinGraph.DoJoinElimination(out remappedVars, out processedNodes);

            // Get the set of vars that need to be renamed
            foreach (KeyValuePair<Var, Var> kv in remappedVars)
            {
                m_varRemapper.AddMapping(kv.Key, kv.Value);
            }
            // get the set of nodes that have already been processed
            foreach (Node n in processedNodes.Keys)
            {
                m_joinGraphUnnecessaryMap[n] = n;
            }

            return newNode;
        }

        /// <summary>
        /// Indicates whether we are running against a SQL CE provider or not.
        /// </summary>
        private bool IsSqlCeProvider
        {
            get
            {
                if (!m_isSqlCe.HasValue)
                {
                    // Figure out if we are using SQL CE by asking the store provider manifest for its namespace name.
                    PlanCompiler.Assert(m_compilerState != null, "Plan compiler cannot be null");
                    var sspace = (StoreItemCollection)m_compilerState.MetadataWorkspace.GetItemCollection(Metadata.Edm.DataSpace.SSpace);
                    if (sspace != null)
                    {
                        m_isSqlCe = sspace.StoreProviderManifest.NamespaceName == JoinElimination.SqlServerCeNamespaceName;
                    }
                }
                // If the sspace was null then m_isSqlCe still doesn't have a value. Use 'false' as default.
                return m_isSqlCe.HasValue ? m_isSqlCe.Value : false;
            }
        }
        
        /// <summary>
        /// Default handler for a node. Simply visits the children, then handles any var
        /// remapping, and then recomputes the node info
        /// </summary>
        /// <param name="n"></param>
        /// <returns></returns>
        private Node VisitDefaultForAllNodes(Node n)
        {
            VisitChildren(n);
            m_varRemapper.RemapNode(n);
            this.Command.RecomputeNodeInfo(n);
            return n;
        }

        #endregion

        #endregion

        #region Visitor overrides

        /// <summary>
        /// Invokes default handling for a node and adds the child-parent tracking info to the VarRefManager.
        /// </summary>
        /// <param name="n"></param>
        /// <returns></returns>
        protected override Node VisitDefault(Node n)
        {
            m_varRefManager.AddChildren(n);
            return VisitDefaultForAllNodes(n);
        }

        #region RelOps
        #region JoinOps

        /// <summary>
        /// Build a join graph for this node for this node if necessary, and process it
        /// </summary>
        /// <param name="op">current join op</param>
        /// <param name="joinNode">current join node</param>
        /// <returns></returns>
        protected override Node VisitJoinOp(JoinBaseOp op, Node joinNode)
        {
            Node newNode;

            // Build and process a join graph if necessary
            if (NeedsJoinGraph(joinNode))
            {
                newNode = ProcessJoinGraph(joinNode);
                if (newNode != joinNode)
                {
                    m_treeModified = true;
                }
            }
            else
            {
                newNode = joinNode;
            }

            // Now do the default processing (ie) visit the children, compute the nodeinfo etc.
            return VisitDefaultForAllNodes(newNode);
        }

        #endregion

        #endregion
        #endregion

        #endregion

    }
}