Welcome to mirror list, hosted at ThFree Co, Russian Federation.

VarRemapper.cs « PlanCompiler « Query « Data « System « System.Data.Entity « referencesource « class « mcs - github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: df62621b53b1a468242215ce57f213eb5198daa9 (plain)
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
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
//---------------------------------------------------------------------
// <copyright file="VarRemapper.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.Data.Query.InternalTrees;

namespace System.Data.Query.PlanCompiler
{
    /// <summary>
    /// The VarRemapper is a utility class that can be used to "remap" Var references
    /// in a node, or a subtree. 
    /// </summary>
    internal class VarRemapper : BasicOpVisitor
    {
        #region Private state
        private readonly Dictionary<Var, Var> m_varMap;
        protected readonly Command m_command;
        #endregion

        #region Constructors
        /// <summary>
        /// Internal constructor
        /// </summary>
        /// <param name="command">Current iqt command</param>
        internal VarRemapper(Command command)
            :this(command, new Dictionary<Var,Var>())
        {
        }

        /// <summary>
        /// Internal constructor
        /// </summary>
        /// <param name="command">Current iqt command</param>
        /// <param name="varMap">Var map to be used</param>
        internal VarRemapper(Command command, Dictionary<Var, Var> varMap)
        {
            m_command = command;
            m_varMap = varMap;
        }
        #endregion

        #region Public surface
        /// <summary>
        /// Add a mapping for "oldVar" - when the replace methods are invoked, they
        /// will replace all references to "oldVar" by "newVar"
        /// </summary>
        /// <param name="oldVar">var to replace</param>
        /// <param name="newVar">the replacement var</param>
        internal void AddMapping(Var oldVar, Var newVar)
        {
            m_varMap[oldVar] = newVar;
        }

        /// <summary>
        /// Update vars in just this node (and not the entire subtree) 
        /// Does *not* recompute the nodeinfo - there are at least some consumers of this
        /// function that do not want the recomputation - transformation rules, for example
        /// </summary>
        /// <param name="node">current node</param>
        internal virtual void RemapNode(Node node)
        {
            if (m_varMap.Count == 0)
            {
                return;
            }
            VisitNode(node);
        }

        /// <summary>
        /// Update vars in this subtree. Recompute the nodeinfo along the way
        /// </summary>
        /// <param name="subTree">subtree to "remap"</param>
        internal virtual void RemapSubtree(Node subTree)
        {
            if (m_varMap.Count == 0)
            {
                return;
            }

            foreach (Node chi in subTree.Children)
            {
                RemapSubtree(chi);
            }

            RemapNode(subTree);
            m_command.RecomputeNodeInfo(subTree);
        }

        /// <summary>
        /// Produce a a new remapped varList
        /// </summary>
        /// <param name="varList"></param>
        /// <returns>remapped varList</returns>
        internal VarList RemapVarList(VarList varList)
        {
            return Command.CreateVarList(MapVars(varList));
        }

        /// <summary>
        /// Remap the given varList using the given varMap
        /// </summary>
        /// <param name="command"></param>
        /// <param name="varMap"></param>
        /// <param name="varList"></param>
        internal static VarList RemapVarList(Command command, Dictionary<Var, Var> varMap, VarList varList)
        {
            VarRemapper varRemapper = new VarRemapper(command, varMap);
            return varRemapper.RemapVarList(varList);
        }
        #endregion

        #region Private methods
        /// <summary>
        /// Get the mapping for a Var - returns the var itself, mapping was found
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        private Var Map(Var v)
        {
            Var newVar;
            while (true)
            {
                if (!m_varMap.TryGetValue(v, out newVar))
                {
                    return v;
                }
                v = newVar;
            }
        }

        private IEnumerable<Var> MapVars(IEnumerable<Var> vars)
        {
            foreach (Var v in vars)
            {
                yield return Map(v);
            }
        }

        private void Map(VarVec vec)
        {
            VarVec newVec = m_command.CreateVarVec(MapVars(vec));
            vec.InitFrom(newVec);
        }

        private void Map(VarList varList)
        {
            VarList newList = Command.CreateVarList(MapVars(varList));
            varList.Clear();
            varList.AddRange(newList);
        }

        private void Map(VarMap varMap)
        {
            VarMap newVarMap = new VarMap();
            foreach (KeyValuePair<Var, Var> kv in varMap)
            {
                Var newVar = Map(kv.Value);
                newVarMap.Add(kv.Key, newVar);
            }
            varMap.Clear();
            foreach (KeyValuePair<Var, Var> kv in newVarMap)
            {
                varMap.Add(kv.Key, kv.Value);
            }
        }
        private void Map(List<InternalTrees.SortKey> sortKeys)
        {
            VarVec sortVars = m_command.CreateVarVec();
            bool hasDuplicates = false;

            // 
            // Map each var in the sort list. Remapping may introduce duplicates, and
            // we should get rid of duplicates, since sql doesn't like them
            //
            foreach (InternalTrees.SortKey sk in sortKeys)
            {
                sk.Var = Map(sk.Var);
                if (sortVars.IsSet(sk.Var))
                {
                    hasDuplicates = true;
                }
                sortVars.Set(sk.Var);
            }

            //
            // Get rid of any duplicates
            //
            if (hasDuplicates)
            {
                List<InternalTrees.SortKey> newSortKeys = new List<SortKey>(sortKeys);
                sortKeys.Clear();
                sortVars.Clear();
                foreach (InternalTrees.SortKey sk in newSortKeys)
                {
                    if (!sortVars.IsSet(sk.Var))
                    {
                        sortKeys.Add(sk);
                    }
                    sortVars.Set(sk.Var);
                }
            }
        }

        #region VisitorMethods
        /// <summary>
        ///  Default visitor for a node - does not visit the children 
        /// The reason we have this method is because the default VisitDefault
        /// actually visits the children, and we don't want to do that
        /// </summary>
        /// <param name="n"></param>
        protected override void VisitDefault(Node n)
        {
            // Do nothing. 
        }

        #region ScalarOps
        public override void Visit(VarRefOp op, Node n)
        {
            VisitScalarOpDefault(op, n);
            Var newVar = Map(op.Var);
            if (newVar != op.Var)
            {
                n.Op = m_command.CreateVarRefOp(newVar);
            }
        }
        #endregion

        #region AncillaryOps
        #endregion

        #region PhysicalOps
        protected override void VisitNestOp(NestBaseOp op, Node n)
        {
            throw EntityUtil.NotSupported();
        }

        public override void Visit(PhysicalProjectOp op, Node n)
        {
            VisitPhysicalOpDefault(op, n);
            Map(op.Outputs);

            SimpleCollectionColumnMap newColumnMap = (SimpleCollectionColumnMap)ColumnMapTranslator.Translate(op.ColumnMap, m_varMap);
            n.Op = m_command.CreatePhysicalProjectOp(op.Outputs, newColumnMap);
        }
        #endregion

        #region RelOps
        protected override void VisitGroupByOp(GroupByBaseOp op, Node n)
        {
            VisitRelOpDefault(op, n);
            Map(op.Outputs);
            Map(op.Keys);
        }
        public override void Visit(GroupByIntoOp op, Node n)
        {
            VisitGroupByOp(op, n);
            Map(op.Inputs);
        }
        public override void Visit(DistinctOp op, Node n)
        {
            VisitRelOpDefault(op, n);
            Map(op.Keys);
        }
        public override void Visit(ProjectOp op, Node n)
        {
            VisitRelOpDefault(op, n);
            Map(op.Outputs);
        }
        public override void Visit(UnnestOp op, Node n)
        {
            VisitRelOpDefault(op, n);
            Var newVar = Map(op.Var);
            if (newVar != op.Var)
            {
                n.Op = m_command.CreateUnnestOp(newVar, op.Table);
            }
        }
        protected override void VisitSetOp(SetOp op, Node n)
        {
            VisitRelOpDefault(op, n);
            Map(op.VarMap[0]);
            Map(op.VarMap[1]);
        }
        protected override void VisitSortOp(SortBaseOp op, Node n)
        {
            VisitRelOpDefault(op, n);
            Map(op.Keys);
        }
        #endregion

        #endregion

        #endregion
    }
}