1 //---------------------------------------------------------------------
2 // <copyright file="StaticContext.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
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
;
19 /// Represents a scope of key-value pairs.
21 internal sealed class Scope
: IEnumerable
<KeyValuePair
<string, ScopeEntry
>>
23 private readonly Dictionary
<string, ScopeEntry
> _scopeEntries
;
26 /// Initialize using a given key comparer.
28 /// <param name="keyComparer"></param>
29 internal Scope(IEqualityComparer
<string> keyComparer
)
31 _scopeEntries
= new Dictionary
<string, ScopeEntry
>(keyComparer
);
35 /// Add new key to the scope. If key already exists - throw.
37 internal Scope
Add(string key
, ScopeEntry
value)
39 _scopeEntries
.Add(key
, value);
44 /// Remove an entry from the scope.
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;
59 /// Returns true if the key belongs to the scope.
61 internal bool Contains(string key
)
63 return _scopeEntries
.ContainsKey(key
);
67 /// Search item by key. Returns true in case of success and false otherwise.
69 internal bool TryLookup(string key
, out ScopeEntry
value)
71 return (_scopeEntries
.TryGetValue(key
, out value));
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();
92 internal enum ScopeEntryKind
96 ProjectionItemDefinition
,
99 /// Represents a group input scope entry that should no longer be referenced.
105 /// Represents an entry in the scope.
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; }
122 /// Returns CQT expression corresponding to the scope entry.
124 internal abstract DbExpression
GetExpression(string refName
, ErrorContext errCtx
);
127 internal interface IGroupExpressionExtendedInfo
130 /// Returns <see cref="DbGroupExpressionBinding.GroupVariable"/> based expression during the <see cref="DbGroupByExpression"/> construction process, otherwise null.
132 DbExpression GroupVarBasedExpression { get; }
135 /// Returns <see cref="DbGroupAggregate"/> based expression during the <see cref="DbGroupByExpression"/> construction process, otherwise null.
137 DbExpression GroupAggBasedExpression { get; }
140 internal interface IGetAlternativeName
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.
146 string[] AlternativeName { get; }
150 /// Represents simple source var scope entry.
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; }
197 /// Prepend <paramref name="parentVarRef"/> to the property chain.
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
);
225 /// Replace existing var at the head of the property chain with the new <paramref name="parentVarRef"/>.
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
;
242 Debug
.Assert(_propRefs
.Count
> 0, "_propRefs.Count > 0");
243 _propRefs
.RemoveAt(_propRefs
.Count
- 1);
244 AddParentVar(parentVarRef
);
249 /// Rebuild the current scope entry expression as the property chain off the <paramref name="parentVarRef"/> expression.
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"/>(...).
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'
267 // | |_Left : '_##join0'
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'
277 // | |_Left : '_##join0'
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
]);
312 /// Rolls back the <see cref="AdjustToGroupVar"/>(...) adjustment, clears the <see cref="IGroupExpressionExtendedInfo.GroupVarBasedExpression"/>.
314 internal void RollbackAdjustmentToGroupVar(DbVariableReferenceExpression pregroupParentVarRef
)
316 Debug
.Assert(_groupVarBasedExpression
!= null, "_groupVarBasedExpression != null");
318 _groupVarBasedExpression
= null;
319 _groupAggBasedExpression
= null;
320 ReplaceParentVar(pregroupParentVarRef
);
325 /// Represents a group input scope entry that should no longer be referenced.
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
));
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.
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; }
386 /// Represents a projection item definition scope entry.
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
)
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.
408 internal sealed class FreeVariableScopeEntry
: ScopeEntry
410 private readonly DbVariableReferenceExpression _varRef
;
412 internal FreeVariableScopeEntry(DbVariableReferenceExpression varRef
)
413 : base(ScopeEntryKind
.FreeVar
)
418 internal override DbExpression
GetExpression(string refName
, ErrorContext errCtx
)
425 /// Represents a generic list of scopes.
427 internal sealed class ScopeManager
429 private readonly IEqualityComparer
<string> _keyComparer
;
430 private readonly List
<Scope
> _scopes
= new List
<Scope
>();
433 /// Initialize scope manager using given key-string comparer.
435 internal ScopeManager(IEqualityComparer
<string> keyComparer
)
437 _keyComparer
= keyComparer
;
441 /// Enter a new scope.
443 internal void EnterScope()
445 _scopes
.Add(new Scope(_keyComparer
));
449 /// Leave the current scope.
451 internal void LeaveScope()
453 Debug
.Assert(CurrentScopeIndex
>= 0);
454 _scopes
.RemoveAt(CurrentScopeIndex
);
458 /// Return current scope index.
459 /// Outer scopes have smaller index values than inner scopes.
461 internal int CurrentScopeIndex
465 return _scopes
.Count
- 1;
470 /// Return current scope.
472 internal Scope CurrentScope
476 return _scopes
[CurrentScopeIndex
];
481 /// Get a scope by the index.
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
];
495 /// Rollback all scopes to the scope at the index.
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
;
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");
526 /// True if key exists in current scope.
528 internal bool IsInCurrentScope(string key
)
530 return CurrentScope
.Contains(key
);