Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Common / EntitySql / StaticContext.cs
blob1000b4d4dffdb18dc7eb36605d1cdde9c170c795
1 //---------------------------------------------------------------------
2 // <copyright file="StaticContext.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //
6 // @owner Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 namespace System.Data.Common.EntitySql
12 using System.Collections.Generic;
13 using System.Data.Common.CommandTrees;
14 using System.Data.Common.CommandTrees.ExpressionBuilder;
15 using System.Data.Entity;
16 using System.Diagnostics;
18 /// <summary>
19 /// Represents a scope of key-value pairs.
20 /// </summary>
21 internal sealed class Scope : IEnumerable<KeyValuePair<string, ScopeEntry>>
23 private readonly Dictionary<string, ScopeEntry> _scopeEntries;
25 /// <summary>
26 /// Initialize using a given key comparer.
27 /// </summary>
28 /// <param name="keyComparer"></param>
29 internal Scope(IEqualityComparer<string> keyComparer)
31 _scopeEntries = new Dictionary<string, ScopeEntry>(keyComparer);
34 /// <summary>
35 /// Add new key to the scope. If key already exists - throw.
36 /// </summary>
37 internal Scope Add(string key, ScopeEntry value)
39 _scopeEntries.Add(key, value);
40 return this;
43 /// <summary>
44 /// Remove an entry from the scope.
45 /// </summary>
46 internal void Remove(string key)
48 Debug.Assert(Contains(key));
49 _scopeEntries.Remove(key);
52 internal void Replace(string key, ScopeEntry value)
54 Debug.Assert(Contains(key));
55 _scopeEntries[key] = value;
58 /// <summary>
59 /// Returns true if the key belongs to the scope.
60 /// </summary>
61 internal bool Contains(string key)
63 return _scopeEntries.ContainsKey(key);
66 /// <summary>
67 /// Search item by key. Returns true in case of success and false otherwise.
68 /// </summary>
69 internal bool TryLookup(string key, out ScopeEntry value)
71 return (_scopeEntries.TryGetValue(key, out value));
74 #region GetEnumerator
75 public Dictionary<string, ScopeEntry>.Enumerator GetEnumerator()
77 return _scopeEntries.GetEnumerator();
80 System.Collections.Generic.IEnumerator<KeyValuePair<string, ScopeEntry>> System.Collections.Generic.IEnumerable<KeyValuePair<string, ScopeEntry>>.GetEnumerator()
82 return _scopeEntries.GetEnumerator();
85 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
87 return _scopeEntries.GetEnumerator();
89 #endregion
92 internal enum ScopeEntryKind
94 SourceVar,
95 GroupKeyDefinition,
96 ProjectionItemDefinition,
97 FreeVar,
98 /// <summary>
99 /// Represents a group input scope entry that should no longer be referenced.
100 /// </summary>
101 InvalidGroupInputRef
104 /// <summary>
105 /// Represents an entry in the scope.
106 /// </summary>
107 internal abstract class ScopeEntry
109 private readonly ScopeEntryKind _scopeEntryKind;
111 internal ScopeEntry(ScopeEntryKind scopeEntryKind)
113 _scopeEntryKind = scopeEntryKind;
116 internal ScopeEntryKind EntryKind
118 get { return _scopeEntryKind; }
121 /// <summary>
122 /// Returns CQT expression corresponding to the scope entry.
123 /// </summary>
124 internal abstract DbExpression GetExpression(string refName, ErrorContext errCtx);
127 internal interface IGroupExpressionExtendedInfo
129 /// <summary>
130 /// Returns <see cref="DbGroupExpressionBinding.GroupVariable"/> based expression during the <see cref="DbGroupByExpression"/> construction process, otherwise null.
131 /// </summary>
132 DbExpression GroupVarBasedExpression { get; }
134 /// <summary>
135 /// Returns <see cref="DbGroupAggregate"/> based expression during the <see cref="DbGroupByExpression"/> construction process, otherwise null.
136 /// </summary>
137 DbExpression GroupAggBasedExpression { get; }
140 internal interface IGetAlternativeName
142 /// <summary>
143 /// If current scope entry reperesents an alternative group key name (see SemanticAnalyzer.ProcessGroupByClause(...) for more info)
144 /// then this property returns the alternative name, otherwise null.
145 /// </summary>
146 string[] AlternativeName { get; }
149 /// <summary>
150 /// Represents simple source var scope entry.
151 /// </summary>
152 internal sealed class SourceScopeEntry : ScopeEntry, IGroupExpressionExtendedInfo, IGetAlternativeName
154 private readonly string[] _alternativeName;
155 private List<string> _propRefs;
156 private DbExpression _varBasedExpression;
157 private DbExpression _groupVarBasedExpression;
158 private DbExpression _groupAggBasedExpression;
159 private bool _joinClauseLeftExpr = false;
161 internal SourceScopeEntry(DbVariableReferenceExpression varRef) : this(varRef, null) { }
163 internal SourceScopeEntry(DbVariableReferenceExpression varRef, string[] alternativeName)
164 : base(ScopeEntryKind.SourceVar)
166 _varBasedExpression = varRef;
167 _alternativeName = alternativeName;
170 internal override DbExpression GetExpression(string refName, ErrorContext errCtx)
172 return _varBasedExpression;
175 DbExpression IGroupExpressionExtendedInfo.GroupVarBasedExpression
177 get { return _groupVarBasedExpression; }
180 DbExpression IGroupExpressionExtendedInfo.GroupAggBasedExpression
182 get { return _groupAggBasedExpression; }
185 internal bool IsJoinClauseLeftExpr
187 get { return _joinClauseLeftExpr; }
188 set { _joinClauseLeftExpr = value; }
191 string[] IGetAlternativeName.AlternativeName
193 get { return _alternativeName; }
196 /// <summary>
197 /// Prepend <paramref name="parentVarRef"/> to the property chain.
198 /// </summary>
199 internal SourceScopeEntry AddParentVar(DbVariableReferenceExpression parentVarRef)
202 // No parent var adjustment is allowed while adjusted to group var (see AdjustToGroupVar(...) for more info).
204 Debug.Assert(_groupVarBasedExpression == null, "_groupVarBasedExpression == null");
205 Debug.Assert(_groupAggBasedExpression == null, "_groupAggBasedExpression == null");
207 if (_propRefs == null)
209 Debug.Assert(_varBasedExpression is DbVariableReferenceExpression, "_varBasedExpression is DbVariableReferenceExpression");
210 _propRefs = new List<string>(2);
211 _propRefs.Add(((DbVariableReferenceExpression)_varBasedExpression).VariableName);
214 _varBasedExpression = parentVarRef;
215 for (int i = _propRefs.Count - 1; i >= 0; --i)
217 _varBasedExpression = _varBasedExpression.Property(_propRefs[i]);
219 _propRefs.Add(parentVarRef.VariableName);
221 return this;
224 /// <summary>
225 /// Replace existing var at the head of the property chain with the new <paramref name="parentVarRef"/>.
226 /// </summary>
227 internal void ReplaceParentVar(DbVariableReferenceExpression parentVarRef)
230 // No parent var adjustment is allowed while adjusted to group var (see AdjustToGroupVar(...) for more info).
232 Debug.Assert(_groupVarBasedExpression == null, "_groupVarBasedExpression == null");
233 Debug.Assert(_groupAggBasedExpression == null, "_groupAggBasedExpression == null");
235 if (_propRefs == null)
237 Debug.Assert(_varBasedExpression is DbVariableReferenceExpression, "_varBasedExpression is DbVariableReferenceExpression");
238 _varBasedExpression = parentVarRef;
240 else
242 Debug.Assert(_propRefs.Count > 0, "_propRefs.Count > 0");
243 _propRefs.RemoveAt(_propRefs.Count - 1);
244 AddParentVar(parentVarRef);
248 /// <summary>
249 /// Rebuild the current scope entry expression as the property chain off the <paramref name="parentVarRef"/> expression.
250 /// Also build
251 /// - <see cref="IGroupExpressionExtendedInfo.GroupVarBasedExpression"/> off the <paramref name="parentGroupVarRef"/> expression;
252 /// - <see cref="IGroupExpressionExtendedInfo.GroupAggBasedExpression"/> off the <paramref name="groupAggRef"/> expression.
253 /// This adjustment is reversable by <see cref="RollbackAdjustmentToGroupVar"/>(...).
254 /// </summary>
255 internal void AdjustToGroupVar(DbVariableReferenceExpression parentVarRef, DbVariableReferenceExpression parentGroupVarRef, DbVariableReferenceExpression groupAggRef)
257 // Adjustment is not reentrant.
258 Debug.Assert(_groupVarBasedExpression == null, "_groupVarBasedExpression == null");
259 Debug.Assert(_groupAggBasedExpression == null, "_groupAggBasedExpression == null");
262 // Let's assume this entry represents variable "x" in the following query:
263 // select x, y, z from {1, 2} as x join {2, 3} as y on x = y join {3, 4} as z on y = z
264 // In this case _propRefs contains x._##join0._##join1 and the corresponding input expression looks like this:
265 // |_Input : '_##join1'
266 // | |_InnerJoin
267 // | |_Left : '_##join0'
268 // | | |_InnerJoin
269 // | | |_Left : 'x'
270 // | | |_Right : 'y'
271 // | |_Right : 'z'
272 // When we start processing a group by, like in this query:
273 // select k1, k2, k3 from {1, 2} as x join {2, 3} as y on x = y join {3, 4} as z on y = z group by x as k1, y as k2, z as k3
274 // we are switching to the following input expression:
275 // |_Input : '_##geb2', '_##group3'
276 // | |_InnerJoin
277 // | |_Left : '_##join0'
278 // | | |_InnerJoin
279 // | | |_Left : 'x'
280 // | | |_Right : 'y'
281 // | |_Right : 'z'
282 // where _##join1 is replaced by _##geb2 for the regular expression and by _##group3 for the group var based expression.
283 // So the switch, or the adjustment, is done by
284 // a. replacing _##join1 with _##geb2 in _propRefs and rebuilding the regular expression accordingly to get
285 // the following property chain: _##geb2._##join1.x
286 // b. building a group var based expression using _##group3 instead of _##geb2 to get
287 // the following property chain: _##group3._##join1.x
291 // Rebuild ScopeEntry.Expression using the new parent var.
293 ReplaceParentVar(parentVarRef);
296 // Build the GroupVarBasedExpression and GroupAggBasedExpression,
297 // take into account that parentVarRef has already been added to the _propRefs in the AdjustToParentVar(...) call, so ignore it.
299 _groupVarBasedExpression = parentGroupVarRef;
300 _groupAggBasedExpression = groupAggRef;
301 if (_propRefs != null)
303 for (int i = _propRefs.Count - 2/*ignore the parentVarRef*/; i >= 0; --i)
305 _groupVarBasedExpression = _groupVarBasedExpression.Property(_propRefs[i]);
306 _groupAggBasedExpression = _groupAggBasedExpression.Property(_propRefs[i]);
311 /// <summary>
312 /// Rolls back the <see cref="AdjustToGroupVar"/>(...) adjustment, clears the <see cref="IGroupExpressionExtendedInfo.GroupVarBasedExpression"/>.
313 /// </summary>
314 internal void RollbackAdjustmentToGroupVar(DbVariableReferenceExpression pregroupParentVarRef)
316 Debug.Assert(_groupVarBasedExpression != null, "_groupVarBasedExpression != null");
318 _groupVarBasedExpression = null;
319 _groupAggBasedExpression = null;
320 ReplaceParentVar(pregroupParentVarRef);
324 /// <summary>
325 /// Represents a group input scope entry that should no longer be referenced.
326 /// </summary>
327 internal sealed class InvalidGroupInputRefScopeEntry : ScopeEntry
329 internal InvalidGroupInputRefScopeEntry()
330 : base(ScopeEntryKind.InvalidGroupInputRef) { }
332 internal override DbExpression GetExpression(string refName, ErrorContext errCtx)
334 throw EntityUtil.EntitySqlError(errCtx, Strings.InvalidGroupIdentifierReference(refName));
338 /// <summary>
339 /// Represents group key during GROUP BY clause processing phase, used during group aggregate search mode.
340 /// This entry will be replaced by the <see cref="SourceScopeEntry"/> when GROUP BY processing is complete.
341 /// </summary>
342 internal sealed class GroupKeyDefinitionScopeEntry : ScopeEntry, IGroupExpressionExtendedInfo, IGetAlternativeName
344 private readonly DbExpression _varBasedExpression;
345 private readonly DbExpression _groupVarBasedExpression;
346 private readonly DbExpression _groupAggBasedExpression;
347 private readonly string[] _alternativeName;
349 internal GroupKeyDefinitionScopeEntry(
350 DbExpression varBasedExpression,
351 DbExpression groupVarBasedExpression, DbExpression
352 groupAggBasedExpression,
353 string[] alternativeName) : base(ScopeEntryKind.GroupKeyDefinition)
355 _varBasedExpression = varBasedExpression;
356 _groupVarBasedExpression = groupVarBasedExpression;
357 _groupAggBasedExpression = groupAggBasedExpression;
358 _alternativeName = alternativeName;
361 internal override DbExpression GetExpression(string refName, ErrorContext errCtx)
363 return _varBasedExpression;
366 DbExpression IGroupExpressionExtendedInfo.GroupVarBasedExpression
370 return _groupVarBasedExpression;
374 DbExpression IGroupExpressionExtendedInfo.GroupAggBasedExpression
376 get { return _groupAggBasedExpression; }
379 string[] IGetAlternativeName.AlternativeName
381 get { return _alternativeName; }
385 /// <summary>
386 /// Represents a projection item definition scope entry.
387 /// </summary>
388 internal sealed class ProjectionItemDefinitionScopeEntry : ScopeEntry
390 private readonly DbExpression _expression;
392 internal ProjectionItemDefinitionScopeEntry(DbExpression expression)
393 : base(ScopeEntryKind.ProjectionItemDefinition)
395 _expression = expression;
398 internal override DbExpression GetExpression(string refName, ErrorContext errCtx)
400 return _expression;
404 /// <summary>
405 /// Represents a free variable scope entry.
406 /// Example: parameters of an inline function definition are free variables in the scope of the function definition.
407 /// </summary>
408 internal sealed class FreeVariableScopeEntry : ScopeEntry
410 private readonly DbVariableReferenceExpression _varRef;
412 internal FreeVariableScopeEntry(DbVariableReferenceExpression varRef)
413 : base(ScopeEntryKind.FreeVar)
415 _varRef = varRef;
418 internal override DbExpression GetExpression(string refName, ErrorContext errCtx)
420 return _varRef;
424 /// <summary>
425 /// Represents a generic list of scopes.
426 /// </summary>
427 internal sealed class ScopeManager
429 private readonly IEqualityComparer<string> _keyComparer;
430 private readonly List<Scope> _scopes = new List<Scope>();
432 /// <summary>
433 /// Initialize scope manager using given key-string comparer.
434 /// </summary>
435 internal ScopeManager(IEqualityComparer<string> keyComparer)
437 _keyComparer = keyComparer;
440 /// <summary>
441 /// Enter a new scope.
442 /// </summary>
443 internal void EnterScope()
445 _scopes.Add(new Scope(_keyComparer));
448 /// <summary>
449 /// Leave the current scope.
450 /// </summary>
451 internal void LeaveScope()
453 Debug.Assert(CurrentScopeIndex >= 0);
454 _scopes.RemoveAt(CurrentScopeIndex);
457 /// <summary>
458 /// Return current scope index.
459 /// Outer scopes have smaller index values than inner scopes.
460 /// </summary>
461 internal int CurrentScopeIndex
465 return _scopes.Count - 1;
469 /// <summary>
470 /// Return current scope.
471 /// </summary>
472 internal Scope CurrentScope
476 return _scopes[CurrentScopeIndex];
480 /// <summary>
481 /// Get a scope by the index.
482 /// </summary>
483 internal Scope GetScopeByIndex(int scopeIndex)
485 Debug.Assert(scopeIndex >= 0, "scopeIndex >= 0");
486 Debug.Assert(scopeIndex <= CurrentScopeIndex, "scopeIndex <= CurrentScopeIndex");
487 if (0 > scopeIndex || scopeIndex > CurrentScopeIndex)
489 throw EntityUtil.EntitySqlError(Strings.InvalidScopeIndex);
491 return _scopes[scopeIndex];
494 /// <summary>
495 /// Rollback all scopes to the scope at the index.
496 /// </summary>
497 internal void RollbackToScope(int scopeIndex)
500 // assert preconditions
502 Debug.Assert(scopeIndex >= 0, "[PRE] savePoint.ScopeIndex >= 0");
503 Debug.Assert(scopeIndex <= CurrentScopeIndex, "[PRE] savePoint.ScopeIndex <= CurrentScopeIndex");
504 Debug.Assert(CurrentScopeIndex >= 0, "[PRE] CurrentScopeIndex >= 0");
506 if (scopeIndex > CurrentScopeIndex || scopeIndex < 0 || CurrentScopeIndex < 0)
508 throw EntityUtil.EntitySqlError(Strings.InvalidSavePoint);
511 int delta = CurrentScopeIndex - scopeIndex;
512 if (delta > 0)
514 _scopes.RemoveRange(scopeIndex + 1, CurrentScopeIndex - scopeIndex);
518 // make sure invariants are preserved
520 Debug.Assert(scopeIndex == CurrentScopeIndex, "[POST] savePoint.ScopeIndex == CurrentScopeIndex");
521 Debug.Assert(CurrentScopeIndex >= 0, "[POST] CurrentScopeIndex >= 0");
525 /// <summary>
526 /// True if key exists in current scope.
527 /// </summary>
528 internal bool IsInCurrentScope(string key)
530 return CurrentScope.Contains(key);