1 //---------------------------------------------------------------------
2 // <copyright file="CTreeGenerator.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 //using System.Diagnostics; // Please use PlanCompiler.Assert instead of Debug.Assert in this class...
12 // It is fine to use Debug.Assert in cases where you assert an obvious thing that is supposed
13 // to prevent from simple mistakes during development (e.g. method argument validation
14 // in cases where it was you who created the variables or the variables had already been validated or
15 // in "else" clauses where due to code changes (e.g. adding a new value to an enum type) the default
16 // "else" block is chosen why the new condition should be treated separately). This kind of asserts are
17 // (can be) helpful when developing new code to avoid simple mistakes but have no or little value in
18 // the shipped product.
19 // PlanCompiler.Assert *MUST* be used to verify conditions in the trees. These would be assumptions
20 // about how the tree was built etc. - in these cases we probably want to throw an exception (this is
21 // what PlanCompiler.Assert does when the condition is not met) if either the assumption is not correct
22 // or the tree was built/rewritten not the way we thought it was.
23 // Use your judgment - if you rather remove an assert than ship it use Debug.Assert otherwise use
24 // PlanCompiler.Assert.
27 namespace System.Data.Query.PlanCompiler
30 using System.Collections.Generic;
32 using System.Data.Common;
33 using System.Data.Common.CommandTrees;
34 using System.Data.Common.CommandTrees.ExpressionBuilder;
35 using System.Data.Common.Utils;
36 using System.Data.Metadata.Edm;
37 using System.Data.Query.InternalTrees;
38 using System.Globalization;
40 internal class CTreeGenerator : BasicOpVisitorOfT<DbExpression>
45 /// The VarInfo class tracks how a single IQT Var should be referenced in terms of CQT Expressions.
46 /// The tracked Var must have been introduced by an IQT RelOp that was converted to a DbExpression that
47 /// is subsequently used in a DbExpressionBinding, otherwise the Var is either a ParameterVar or a locally
48 /// defined Var, which are tracked by the parameters collection of the Command and the VarDefScope
49 /// class, respectively.
50 /// An IQT Var that is tracked by a VarInfo instance is reachable in the following way:
51 /// 1. By a DbVariableReferenceExpression that references the Variable of the DbExpressionBinding that contains the DbExpression that logically publishes the IQT Var.
52 /// This is tracked by the PublisherName property of the RelOpInfo class, which is used to track Vars brought into scope by a DbExpressionBinding.
53 /// Without an enclosing RelOpInfo, the VarInfo is unbound and cannot be used to instantiate a CQT expression tree that is the equivalent of a VarRef of the IQT Var)
54 /// 2. By zero or more PropertyRefExpressions starting with a property of the DbVariableReferenceExpression created in step 1.
55 /// These PropertyRefExpressions are introduced on top of the DbVariableReferenceExpression because of Join or ApplyExpressions that
56 /// occur in the CQT between the expression that publishes the Var and the expression higher in the tree that contains a VarRefOp
57 /// to the IQT Var that must be resolved to a CQT DbExpression. In such cases the DbExpression that logically publishes
58 /// the IQT Var will have a record return Type.
59 /// The required property names are tracked, in order, in the PropertyPath property of this class.
60 /// The PrependProperty method is used to update the DbPropertyExpression path required to reach
61 /// the DbVariableReferenceExpression when the referenced Variable becomes part of such a record-typed output.
65 #region Private Member Variables
67 private List<string> _propertyChain = new List<string>();
71 /// Gets the Var tracked by this VarInfo instance
73 internal Var Var { get { return _var; } }
76 /// Gets the names, in order of use, that should be used to build DbPropertyExpression around an initial DbVariableReferenceExpression in order to build a DbExpression subtree that correctly references the tracked IQT Var
78 internal List<string> PropertyPath { get { return _propertyChain; } }
81 /// Constructs a new VarInfo instance that tracks the specified Var.
83 /// <param name="target">The IQT Var that this VarInfo instance should track.</param>
84 internal VarInfo(Var target)
90 /// Adds a property name to the beginning of the property path for this VarInfo instance.
91 /// Each time a new record structure is constructed on top of the expression that logically
92 /// publishes this var, another DbPropertyExpression is required around the DbVariableReferenceExpression used
93 /// to reach the Var in the CQT. Each new DbPropertyExpression must be added immediately around the
94 /// DbVariableReferenceExpression, with previous PropertyExpressions now referring to the new DbPropertyExpression.
95 /// Therefore the new property name added by this method is inserted at the start of the property path.
96 /// See the Visit methods for the Join/ApplyOps for examples of using this method to adjust the property path.
98 /// <param name="propName">The new property name to insert at the start of the property path for the Var tracked by this VarInfo instance</param>
99 internal void PrependProperty(string propName)
101 _propertyChain.Insert(0, propName);
106 /// Groups a set of VarInfo instances together and allows certain operations (Bind/Unbind/PrependProperty)
107 /// to be performed on all instances in the VarInfoList with a single call.
109 private class VarInfoList : List<VarInfo>
112 /// Constructs a new, empty VarInfoList.
114 internal VarInfoList() : base() { }
117 /// Constructs a new VarInfoList that contains the specified VarInfo instances.
119 /// <param name="elements"></param>
120 internal VarInfoList(IEnumerable<VarInfo> elements) : base(elements) { }
123 /// Prepends the specified property name to the property path of all VarInfo instances in this list.
125 /// <param name="propName"></param>
126 internal void PrependProperty(string propName)
128 foreach (VarInfo vInf in this)
130 vInf.PropertyPath.Insert(0, propName);
135 /// Attempts to retrieve the VarInfo instance that tracks the specified IQT Var, if it is contained by this VarInfoList.
137 /// <param name="targetVar">The required IQT Var</param>
138 /// <param name="varInfo">Contains the VarInfo instance that tracks the specified Var if this method returns true</param>
139 /// <returns>True if this list contains a VarInfo instance that tracks the specified Var; otherwise false</returns>
140 internal bool TryGetInfo(Var targetVar, out VarInfo varInfo)
143 foreach (VarInfo info in this)
145 if (info.Var == targetVar)
157 /// IqtVarScope is used to represent one or more IQT Vars that are currently in scope and can be mapped to a corresponding CQT DbExpression subtree.
159 private abstract class IqtVarScope
162 /// Attempts to resolve the specified IQT Var by building or mapping to a CQT DbExpression subtree. Overridden in derived classes.
164 /// <param name="targetVar">The IQT Var to resolve</param>
165 /// <param name="resultExpr">If the methods returns true, the DbExpression to which the Var was resolved; otherwise null</param>
166 /// <returns>True if the specified Var was successfully resolved; otherwise false</returns>
167 internal abstract bool TryResolveVar(Var targetVar, out DbExpression resultExpr);
170 private abstract class BindingScope : IqtVarScope
172 private readonly VarInfoList _definedVars;
174 internal BindingScope(IEnumerable<VarInfo> boundVars)
176 _definedVars = new VarInfoList(boundVars);
180 /// Information (current binding name, property path) about the Vars logically published by the Publisher expression
182 internal VarInfoList PublishedVars { get { return _definedVars; } }
185 /// Implements the abstract IqtVarScope.TryResolveVar method. If the specified Var was published by this scope's DbExpression, it is mapped to a CQT DbExpression by calling CreateExpression on the VarInfo used to track it.
187 /// <param name="targetVar">The Var to resolve</param>
188 /// <param name="resultExpr">If the method returns true, the DbExpression to which the Var was resolved; otherwise null</param>
189 /// <returns>True if the specified Var was successfully resolved; otherwise false</returns>
190 internal override bool TryResolveVar(Var targetVar, out DbExpression resultExpr)
193 VarInfo foundInfo = null;
194 if (_definedVars.TryGetInfo(targetVar, out foundInfo))
196 resultExpr = this.BindingReference;
197 foreach (string propName in foundInfo.PropertyPath)
199 resultExpr = resultExpr.Property(propName);
208 protected abstract DbVariableReferenceExpression BindingReference { get; }
212 /// Represents a collection of IQT Vars that were brought into scope by a DbExpression used in a DbExpressionBinding. This class is also used to associate those Vars with that DbExpression, which is considered the logical 'publisher' of the Vars.
214 private class RelOpInfo : BindingScope
216 private readonly DbExpressionBinding _binding;
218 internal RelOpInfo(string bindingName, DbExpression publisher, IEnumerable<VarInfo> publishedVars)
219 : base(publishedVars)
221 PlanCompiler.Assert(TypeSemantics.IsCollectionType(publisher.ResultType), "non-collection type used as RelOpInfo publisher");
222 _binding = publisher.BindAs(bindingName);
226 /// The unique name assigned to the CQT DbExpression that logically publishes the PublishedVars. Used primarily in ExpressionBindings that contain that DbExpression
228 internal string PublisherName
230 get { return _binding.VariableName; }
234 /// The CQT DbExpression that logically publishes the PublishedVars
236 internal DbExpression Publisher { get { return _binding.Expression; } }
239 /// Creates a new DbExpressionBinding that binds the publisher DbExpression under the binding name
241 /// <returns>The new DbExpressionBinding</returns>
242 internal DbExpressionBinding CreateBinding()
247 protected override DbVariableReferenceExpression BindingReference
249 get { return _binding.Variable; }
254 /// Represents a collection of IQT Vars that were brought into scope by a DbExpression used in a DbGroupExpressionBinding.
256 private class GroupByScope : BindingScope
258 private readonly DbGroupExpressionBinding _binding;
259 private bool _referenceGroup;
261 internal GroupByScope(DbGroupExpressionBinding binding, IEnumerable<VarInfo> publishedVars)
262 : base(publishedVars)
268 /// Returns the DbGroupExpressionBinding that backs this group-by scope
270 /// <returns>The new DbExpressionBinding</returns>
271 internal DbGroupExpressionBinding Binding { get { return _binding; } }
273 internal void SwitchToGroupReference()
275 PlanCompiler.Assert(!_referenceGroup, "SwitchToGroupReference called more than once on the same GroupByScope?");
276 _referenceGroup = true;
279 protected override DbVariableReferenceExpression BindingReference
281 get { return (_referenceGroup ? _binding.GroupVariable : _binding.Variable); }
286 /// Represents a collection of IQT Vars that are in scope because they are defined locally (by VarDefOps) to an IQT Op that is being visited.
288 private class VarDefScope : IqtVarScope
290 private Dictionary<Var, DbExpression> _definedVars;
292 internal VarDefScope(Dictionary<Var, DbExpression> definedVars)
294 _definedVars = definedVars;
298 /// Implements the abstract IqtVarScope.TryResolveVar method. If the specified Var exists in this scope, it is resolved by mapping it to the DbExpression that was produced by converting the IQT child Node of the VarDefOp that defines it to a CQT DbExpression subtree.
300 /// <param name="targetVar">The Var to resolve</param>
301 /// <param name="resultExpr">If the method returns true, the DbExpression to which the Var was resolved; otherwise null</param>
302 /// <returns>True if the specified Var was successfully resolved; otherwise false</returns>
303 internal override bool TryResolveVar(Var targetVar, out DbExpression resultExpr)
306 DbExpression foundExpr = null;
307 if (_definedVars.TryGetValue(targetVar, out foundExpr))
309 resultExpr = foundExpr;
319 #region Private Instance Members
321 private Command _iqtCommand;
322 private DbQueryCommandTree _queryTree;
323 private Dictionary<ParameterVar, DbParameterReferenceExpression> _addedParams = new Dictionary<ParameterVar, DbParameterReferenceExpression>();
324 private Stack<IqtVarScope> _bindingScopes = new Stack<IqtVarScope>();
325 private Stack<VarDefScope> _varScopes = new Stack<VarDefScope>();
326 private Dictionary<DbExpression, RelOpInfo> _relOpState = new Dictionary<DbExpression, RelOpInfo>();
328 private AliasGenerator _applyAliases = new AliasGenerator("Apply");
329 private AliasGenerator _distinctAliases = new AliasGenerator("Distinct");
330 private AliasGenerator _exceptAliases = new AliasGenerator("Except");
331 private AliasGenerator _extentAliases = new AliasGenerator("Extent");
332 private AliasGenerator _filterAliases = new AliasGenerator("Filter");
333 private AliasGenerator _groupByAliases = new AliasGenerator("GroupBy");
334 private AliasGenerator _intersectAliases = new AliasGenerator("Intersect");
335 private AliasGenerator _joinAliases = new AliasGenerator("Join");
336 private AliasGenerator _projectAliases = new AliasGenerator("Project");
337 private AliasGenerator _sortAliases = new AliasGenerator("Sort");
338 private AliasGenerator _unionAllAliases = new AliasGenerator("UnionAll");
339 private AliasGenerator _elementAliases = new AliasGenerator("Element");
340 private AliasGenerator _singleRowTableAliases = new AliasGenerator("SingleRowTable");
341 private AliasGenerator _limitAliases = new AliasGenerator("Limit");
342 private AliasGenerator _skipAliases = new AliasGenerator("Skip");
346 #region (pseudo) Public API
347 internal static DbCommandTree Generate(Command itree, Node toConvert)
349 CTreeGenerator treeGenerator = new CTreeGenerator(itree, toConvert);
350 return treeGenerator._queryTree;
354 #region Constructors (private)
355 private CTreeGenerator(Command itree, Node toConvert)
358 DbExpression queryExpression = VisitNode(toConvert);
359 _queryTree = DbQueryCommandTree.FromValidExpression(itree.MetadataWorkspace, DataSpace.SSpace, queryExpression);
363 #region RelOp Helpers and PublishedVar State Maintenance
366 /// Asserts that the specified DbExpression is a 'RelOp' DbExpression, i.e. it is considered the publisher of one or more (IQT) RelVars.
368 /// <param name="expr">The DbExpression on which to Assert</param>
369 private void AssertRelOp(DbExpression expr)
371 PlanCompiler.Assert(_relOpState.ContainsKey(expr),"not a relOp expression?");
375 /// Update the DbExpression to RelOpInfo map to indicate that the specified DbExpression logically publishes the Vars
376 /// tracked in VarInfoList and that they should be bound under the specified name.
378 /// <param name="name">The name under which the Vars tracked in VarInfoList are initially considered bound. This will be a unique name based on what kind of RelOp the specified DbExpression (the publisher) corresponds to</param>
379 /// <param name="expr">The DbExpression that is considered the logical publisher of the Vars tracked in publishedVars</param>
380 /// <param name="publishedVars">A VarInfoList that contains VarInfo instances that track the IQT Vars that are logically published by the specified DbExpression</param>
381 /// <returns>A new RelOpInfo instance that associates the given binding name and published Vars with the specified DbExpression. This RelOpInfo is also added to the DbExpression to RelOpInfo map</returns>
382 private RelOpInfo PublishRelOp(string name, DbExpression expr, VarInfoList publishedVars)
384 RelOpInfo retInfo = new RelOpInfo(name, expr, publishedVars);
385 _relOpState.Add(expr, retInfo);
390 /// Removes an entry in the DbExpression to RelOpInfo map, 'consuming' it so that it is not visible higher in the converted CQT.
392 /// <param name="expr">The DbExpression for which the corresponding RelOpEntry should be removed</param>
393 /// <returns>The RelOpInfo that was removed from the DbExpression to RelOpInfo map</returns>
394 private RelOpInfo ConsumeRelOp(DbExpression expr)
397 RelOpInfo retInfo = _relOpState[expr];
398 _relOpState.Remove(expr);
402 private RelOpInfo VisitAsRelOp(Node inputNode)
404 // Assert that the Op is actually a RelOp before attempting to use it
405 PlanCompiler.Assert(inputNode.Op is RelOp, "Non-RelOp used as DbExpressionBinding Input");
408 // Visit the Op. This Visit method of this class that actually processes the Op will
409 // publish the Vars produced by the resulting DbExpression in the DbExpression to RelOpInfo
410 // map, then return that DbExpression.
412 DbExpression inputExpr = VisitNode(inputNode);
415 // Retrieve the RelOpInfo for the DbExpression, that was published as part of the above call.
416 // ConsumeRelOp is called to both retrieve and remove the RelOpInfo instance since it is being
417 // used here in a DbExpressionBinding.
419 return ConsumeRelOp(inputExpr);
424 #region Var Scope Maintenance
425 private void PushExpressionBindingScope(RelOpInfo inputState)
427 PlanCompiler.Assert(inputState != null && inputState.PublisherName != null && inputState.PublishedVars != null , "Invalid RelOpInfo produced by DbExpressionBinding Input");
428 _bindingScopes.Push(inputState);
432 /// Visit a Node that will be used as the basis of a DbExpressionBinding, optionally pushing the
433 /// Vars that are logically published by the DbExpression produced from the Node's Op onto the expression binding scopes stack.
435 /// <param name="inputNode">The Node to Visit</param>
436 /// <param name="pushScope">Indicates whether or not the Vars published by the converted form of the Node's Op should be brought into scope before this method returns</param>
437 /// <returns>The RelOpInfo that corresponds to the given Node, which details the DbExpression it was converted to, the Vars that are logically published by that DbExpression, and the unique name under which those Vars should be bound</returns>
438 private RelOpInfo EnterExpressionBindingScope(Node inputNode, bool pushScope)
440 RelOpInfo inputInfo = VisitAsRelOp(inputNode);
443 // If the pushScope flag is set, push the RelOpInfo onto the binding scopes stack to bring
444 // the Vars it tracks into scope.
448 PushExpressionBindingScope(inputInfo);
452 // Return the RelOpInfo that was produced by the input Node to the caller, providing access to the
453 // DbExpression that the Node's Op was converted to, the Vars that are logically published by that DbExpression,
454 // and the unique binding name that the Vars are considered bound under - that name should be used in
455 // the DbExpressionBinding that uses the DbExpression.
460 private RelOpInfo EnterExpressionBindingScope(Node inputNode)
462 return EnterExpressionBindingScope(inputNode, true);
465 private void ExitExpressionBindingScope(RelOpInfo scope, bool wasPushed)
469 PlanCompiler.Assert(_bindingScopes.Count > 0, "ExitExpressionBindingScope called on empty ExpressionBindingScope stack");
471 RelOpInfo bindingScope = (RelOpInfo)_bindingScopes.Pop();
473 PlanCompiler.Assert(bindingScope == scope, "ExitExpressionBindingScope called on incorrect expression");
477 private void ExitExpressionBindingScope(RelOpInfo scope)
479 ExitExpressionBindingScope(scope, true);
482 private GroupByScope EnterGroupByScope(Node inputNode)
484 RelOpInfo inputInfo = VisitAsRelOp(inputNode);
486 // The current binding name is saved for use later as the VarName in the DbGroupExpressionBinding.
487 string varName = inputInfo.PublisherName;
489 // Generate the GroupVarName, and rebind the Input Vars under that name
490 string groupVarName = string.Format(CultureInfo.InvariantCulture, "{0}Group", varName);
492 DbGroupExpressionBinding newBinding = inputInfo.CreateBinding().Expression.GroupBindAs(varName, groupVarName);
493 GroupByScope newScope = new GroupByScope(newBinding, inputInfo.PublishedVars);
494 _bindingScopes.Push(newScope);
498 private void ExitGroupByScope(GroupByScope scope)
500 PlanCompiler.Assert(_bindingScopes.Count > 0, "ExitGroupByScope called on empty ExpressionBindingScope stack");
502 GroupByScope groupScope = (GroupByScope)_bindingScopes.Pop();
504 PlanCompiler.Assert(groupScope == scope, "ExitGroupByScope called on incorrect expression");
508 /// Converts a list of VarDefOp Nodes into Expressions, builds a map of Var to DbExpression for each
509 /// defined Var, and pushes a new VarDefScope containing the map onto the stack of 'in scope' Vars.
511 /// <param name="varDefNodes">A list of Nodes. Each Node in the list must reference a VarDefOp</param>
512 private void EnterVarDefScope(List<Node> varDefNodes)
515 // Create a new dictionary to act as the Var to DbExpression map
517 Dictionary<Var, DbExpression> varDefs = new Dictionary<Var, DbExpression>();
520 // For each Node in the list:
521 // 1. Assert that the Node is actually referencing a VarDefOp
522 // 2. Assert that the Var defined by the VarDefOp is actually a ComputedVar
523 // 3. Visit the Child0 Node of the Node to produce the CQT DbExpression that defines the Var
524 // 4. Add the returned DbExpression to the Var to DbExpression map.
525 foreach (Node childNode in varDefNodes)
527 VarDefOp defOp = childNode.Op as VarDefOp;
528 PlanCompiler.Assert(defOp != null, "VarDefListOp contained non-VarDefOp child node");
529 PlanCompiler.Assert(defOp.Var is ComputedVar, "VarDefOp defined non-Computed Var");
531 varDefs.Add(defOp.Var, VisitNode(childNode.Child0));
535 // Finally, construct and push a new VarDefScope based on the Var to DbExpression map onto the stack
536 // of locally 'in scope' IQT ComputedVars. All of the Vars defined in the original list are brought into scope by
537 // this final step, and are not in scope until this is done. Therefore it is not valid for any Var
538 // in the original list to refer to a Var that occurs previously in the list (left-correlation).
540 _varScopes.Push(new VarDefScope(varDefs));
544 /// A convenience method to create a new VarDefScope from the specified VarDefListOp Node
546 /// <param name="varDefListNode">The Node that references the VarDefListOp. Its children will be used as the basis of the new VarDefScope</param>
547 private void EnterVarDefListScope(Node varDefListNode)
549 PlanCompiler.Assert(varDefListNode.Op is VarDefListOp, "EnterVarDefListScope called with non-VarDefListOp");
550 EnterVarDefScope(varDefListNode.Children);
554 /// Asserts that the top of the scope stack is actually a VarDefScope, and then pops it to remove the locally defined Vars from scope.
556 private void ExitVarDefScope()
558 PlanCompiler.Assert(_varScopes.Count > 0, "ExitVarDefScope called on empty VarDefScope stack");
563 /// Resolves an IQT Var to a CQT DbExpression.
564 /// There are 3 possible ways for an IQT Var to resolve to a valid reference expressed as a CQT DbExpression:
565 /// 1. The specified Var is a valid ParameterVar in the IQT Command being converted:
566 /// This resolves simply to ParameterRefExpression. A Parameter that corresponds to the ParameterVar
567 /// is declared on the CQT DbCommandTree is this has not already been done.
568 /// 2. The specified Var is a ComputedVar that is defined locally to the Op being visited. In this case
569 /// The DbExpression produced by converting the VarDefOp that defines the Var is returned.
570 /// 3. Otherwise, the Var must have been brought into scope because the DbExpression that logically produces it is
571 /// being used in a DbExpressionBinding which is currently in scope. Each RelOpInfo on the ExpressionBindingScopes stack
572 /// is asked to resolve the Var, if one of the RelOpInfo scopes is tracking the Var it will construct an appropriate combination
573 /// of DbVariableReferenceExpression and PropertyRefExpressions that are sufficient to logically reference the Var.
574 /// If none of the 3 above conditions are satisfied then the Var is unresolvable in the CQT being constructed and
575 /// the original IQT Command must be considered invalid for the purposes of this conversion.
577 /// <param name="referencedVar">The IQT Var to resolve</param>
578 /// <returns>The CQT DbExpression to which the specified Var resolves</returns>
579 private DbExpression ResolveVar(Var referencedVar)
581 DbExpression retExpr = null;
582 ParameterVar paramVar = referencedVar as ParameterVar;
583 if (paramVar != null)
586 // If there is already a parameter expression that corresponds to this parameter Var, reuse it.
588 DbParameterReferenceExpression paramRef;
589 if (!_addedParams.TryGetValue(paramVar, out paramRef))
591 paramRef = DbExpressionBuilder.Parameter(paramVar.Type, paramVar.ParameterName);
592 _addedParams[paramVar] = paramRef;
598 ComputedVar compVar = referencedVar as ComputedVar;
602 // If this is a ComputedVar, first check if it is defined locally to the Node of the Op being visited.
603 // Such local ComputedVars are only directly accessible from the Op being converted, so only the topmost
604 // ComputedVar scope on the stack should be considered.
606 if (_varScopes.Count > 0)
608 if (!_varScopes.Peek().TryResolveVar(compVar, out retExpr))
618 // If the Var was not resolved as a locally defined ComputedVar, then it must now be a Var that was brought
619 // into scope by a DbExpressionBinding in order to be considered valid. Each DbExpressionBinding scope (represented as a RelOpInfo)
620 // on the binding scopes stack from top to bottom is asked in turn to resolve the Var, breaking if the Var is successfully resolved.
622 DbExpression foundExpr = null;
623 foreach (IqtVarScope scope in _bindingScopes)
625 if (scope.TryResolveVar(referencedVar, out foundExpr))
634 PlanCompiler.Assert(retExpr != null, string.Format(CultureInfo.InvariantCulture, "Unresolvable Var used in Command: VarType={0}, Id={1}", Enum.GetName(typeof(VarType), referencedVar.VarType), referencedVar.Id));
639 #region Visitor Helpers
642 /// Asserts that the specified Node has exactly 2 child Nodes
644 /// <param name="n">The Node on which to Assert</param>
645 private static void AssertBinary(Node n)
647 PlanCompiler.Assert(2 == n.Children.Count, string.Format(CultureInfo.InvariantCulture, "Non-Binary {0} encountered", n.Op.GetType().Name));
650 private DbExpression VisitChild(Node n, int index)
652 PlanCompiler.Assert(n.Children.Count > index, "VisitChild called with invalid index");
653 return VisitNode(n.Children[index]);
656 private new List<DbExpression> VisitChildren(Node n)
658 List<DbExpression> retList = new List<DbExpression>();
659 foreach (Node argNode in n.Children)
661 retList.Add(VisitNode(argNode));
669 #region IOpVisitor<DbExpression> Members
671 #region ScalarOp Conversions
672 protected override DbExpression VisitConstantOp(ConstantBaseOp op, Node n)
675 // Simple conversion using the same constant value as the ConstantBaseOp in a CQT DbConstantExpression
677 return DbExpressionBuilder.Constant(op.Type, op.Value);
680 public override DbExpression Visit(ConstantOp op, Node n)
682 return VisitConstantOp(op, n);
685 public override DbExpression Visit(InternalConstantOp op, Node n)
687 return VisitConstantOp(op, n);
690 public override DbExpression Visit(NullOp op, Node n)
692 return DbExpressionBuilder.Null(op.Type);
695 public override DbExpression Visit(NullSentinelOp op, Node n)
697 return VisitConstantOp(op, n);
700 public override DbExpression Visit(ConstantPredicateOp op, Node n)
703 // Create a "true=true" for "true" predicates,
704 // Create a "true=false" expression for false predicates
706 return DbExpressionBuilder.True.Equal(op.IsTrue ? DbExpressionBuilder.True : DbExpressionBuilder.False);
709 public override DbExpression Visit(FunctionOp op, Node n)
712 // FunctionOp becomes DbFunctionExpression that references the same EdmFunction metadata and
713 // with argument Expressions produced by converting the child nodes of the FunctionOp's Node
715 return op.Function.Invoke(VisitChildren(n));
718 public override DbExpression Visit(PropertyOp op, Node n)
720 // We should never see this Op - should have been eliminated in NTE
721 throw EntityUtil.NotSupported();
724 public override DbExpression Visit(RelPropertyOp op, Node n)
726 // should have been eliminated in NTE
727 throw EntityUtil.NotSupported();
730 public override DbExpression Visit(ArithmeticOp op, Node n)
733 // ArithmeticOp converts to a DbArithmeticExpression with an appropriate DbExpressionKind.
735 DbExpression resultExpr = null;
736 if (OpType.UnaryMinus == op.OpType)
738 // If the OpType is Unary minus, only 1 child Node is required.
739 resultExpr = VisitChild(n, 0).UnaryMinus();
743 // Otherwise this is a binary operator, so visit the left and right child Nodes
744 // and convert to CQT DbExpression based on the OpType.
745 DbExpression left = VisitChild(n, 0);
746 DbExpression right = VisitChild(n, 1);
752 resultExpr = left.Divide(right);
758 resultExpr = left.Minus(right);
764 resultExpr = left.Modulo(right);
768 case OpType.Multiply:
770 resultExpr = left.Multiply(right);
776 resultExpr = left.Plus(right);
788 // The result DbExpression will only be null if a new OpType is added and this code is not updated
789 PlanCompiler.Assert(resultExpr != null, string.Format(CultureInfo.InvariantCulture, "ArithmeticOp OpType not recognized: {0}", Enum.GetName(typeof(OpType), op.OpType)));
793 public override DbExpression Visit(CaseOp op, Node n)
796 // CaseOp converts directly to DbCaseExpression.
797 // If no 'Else' Node is present a new DbNullExpression typed to the result Type of the CaseOp is used as the DbCaseExpression's Else expression.
798 // Otherwise the converted form of the 'Else' Node is used.
799 // This method assumes that the child Nodes of the CaseOp's Node are as follows:
800 // When1, Then1[..., WhenN, ThenN][, Else]
801 // that is, at least one When/Then pair MUST be present, subsequent When/Then pairs and the final Else are optional.
803 // This is the count of Nodes that contribute to the When/Then pairs, NOT the count of those pairs.
804 int caseCount = n.Children.Count;
806 // Verify the assumption made that at least one case is present.
807 PlanCompiler.Assert(caseCount > 1, "Invalid CaseOp: At least 2 child Nodes (1 When/Then pair) must be present");
809 List<DbExpression> whens = new List<DbExpression>();
810 List<DbExpression> thens = new List<DbExpression>();
811 DbExpression elseExpr = null;
813 if(0 == n.Children.Count % 2)
815 // If the number of child Nodes is divisible by 2, it is assumed that they are When/Then pairs without the optional Else Node.
816 // The Else DbExpression defaults to a properly typed DbNullExpression.
817 elseExpr = DbExpressionBuilder.Null(op.Type);
821 // Otherwise, an Else Node is present as the last child Node. It's CQT DbExpression form is used as the Else DbExpression.
822 // The count of child Nodes that contribute to the When/Then pairs must now be reduced by 1.
823 caseCount = caseCount - 1;
824 elseExpr = VisitChild(n, n.Children.Count - 1);
827 // Convert the When/Then Nodes in pairs until the number of converted Nodes is equal to the number of Nodes that contribute to the When/Then pairs.
828 for(int idx = 0; idx < caseCount; idx += 2)
830 whens.Add(VisitChild(n, idx));
831 thens.Add(VisitChild(n, idx + 1));
834 // Create and return a new DbCaseExpression using the When and Then DbExpression lists and the (converted or DbNullExpression) Else DbExpression.
835 return DbExpressionBuilder.Case(whens, thens, elseExpr);
838 public override DbExpression Visit(ComparisonOp op, Node n)
841 // ComparisonOp converts to a DbComparisonExpression with an appropriate DbExpressionKind.
842 // The ComparisonOp is only convertible to a DbComparisonExpression if it has 2 child Nodes
846 DbExpression left = VisitChild(n, 0);
847 DbExpression right = VisitChild(n, 1);
849 DbExpression compExpr = null;
855 compExpr = left.Equal(right);
861 compExpr = left.NotEqual(right);
867 compExpr = left.LessThan(right);
873 compExpr = left.GreaterThan(right);
879 compExpr = left.LessThanOrEqual(right);
885 compExpr = left.GreaterThanOrEqual(right);
896 // The result DbExpression will only be null if a new OpType is added and this code is not updated
897 PlanCompiler.Assert(compExpr != null, string.Format(CultureInfo.InvariantCulture, "ComparisonOp OpType not recognized: {0}", Enum.GetName(typeof(OpType), op.OpType)));
901 public override DbExpression Visit(ConditionalOp op, Node n)
904 // Boolean ConditionalOps convert to the corresponding And/Or/DbNotExpression. The IsNull ConditionalOp converts to DbIsNullExpression.
905 // In all cases the OpType is used to determine what kind of DbExpression to create.
908 // There will always be at least one argument (IsNull, Not) and should be at most 2 (And, Or).
909 DbExpression left = VisitChild(n, 0);
910 DbExpression condExpr = null;
915 condExpr = left.IsNull();
921 condExpr = left.And(VisitChild(n, 1));
927 condExpr = left.Or(VisitChild(n, 1));
933 // Convert Not(Not(<expression>)) to just <expression>. This is taken into account here
934 // because LeftSemi/AntiJoin conversions generate intermediate Not(Exists(<Op>)) IQT Nodes,
935 // which would then be converted to Not(Not(IsEmpty(<expression>)) if the following code were not present.
936 DbNotExpression notExpr = left as DbNotExpression;
939 condExpr = notExpr.Argument;
943 condExpr = left.Not();
955 // The result DbExpression will only be null if a new OpType is added and this code is not updated
956 PlanCompiler.Assert(condExpr != null, string.Format(CultureInfo.InvariantCulture, "ConditionalOp OpType not recognized: {0}", Enum.GetName(typeof(OpType), op.OpType)));
960 public override DbExpression Visit(LikeOp op, Node n)
963 // LikeOp converts to DbLikeExpression, with the conversions of the
964 // Node's first, second and third child nodes providing the
965 // Input, Pattern and Escape expressions.
967 return DbExpressionBuilder.Like(
974 public override DbExpression Visit(AggregateOp op, Node n)
976 // AggregateOp may only occur as the immediate child of a VarDefOp that is itself the
977 // child of a VarDefListOp used as the 'Aggregates' collection of a GroupByOp.
978 // As such, aggregates are handled directly during processing of GroupByOp.
979 // If this method is called an AggregateOp was encountered at some other (invalid) point in the IQT.
980 PlanCompiler.Assert(false, "AggregateOp encountered outside of GroupByOp");
981 throw EntityUtil.NotSupported(System.Data.Entity.Strings.Iqt_CTGen_UnexpectedAggregate);
983 public override DbExpression Visit(NavigateOp op, Node n)
985 // we should never see this Op
986 throw EntityUtil.NotSupported();
989 public override DbExpression Visit(NewEntityOp op, Node n)
991 // We should never see this Op - should have been eliminated in NTE
992 throw EntityUtil.NotSupported();
994 public override DbExpression Visit(NewInstanceOp op, Node n)
996 // We should never see this Op - should have been eliminated in NTE
997 throw EntityUtil.NotSupported();
1000 public override DbExpression Visit(DiscriminatedNewEntityOp op, Node n)
1002 // We should never see this Op - should have been eliminated in NTE
1003 throw EntityUtil.NotSupported();
1006 public override DbExpression Visit(NewMultisetOp op, Node n)
1008 // We should never see this Op
1009 throw EntityUtil.NotSupported();
1012 public override DbExpression Visit(NewRecordOp op, Node n)
1014 // We should never see this Op
1015 throw EntityUtil.NotSupported();
1018 public override DbExpression Visit(RefOp op, Node n)
1020 // We should never see this Op
1021 throw EntityUtil.NotSupported();
1024 public override DbExpression Visit(VarRefOp op, Node n)
1026 return ResolveVar(op.Var);
1029 public override DbExpression Visit(TreatOp op, Node n)
1031 // We should never see this Op
1032 throw EntityUtil.NotSupported();
1035 public override DbExpression Visit(CastOp op, Node n)
1037 // Direct conversion to DbCastExpression with the same Type and converted argument DbExpression
1038 return VisitChild(n, 0).CastTo(op.Type);
1042 /// A SoftCastOp is intended to be used only for promotion (and/or equivalence)
1043 /// and should be ignored in the CTree
1045 /// <param name="op">the softcast Op</param>
1046 /// <param name="n">the node</param>
1047 /// <returns></returns>
1048 public override DbExpression Visit(SoftCastOp op, Node n)
1050 // Microsoft 9/21/06 - temporarily removing check here
1051 // because the assert wrongly fails in some cases where the types are promotable,
1052 // but the facets are not. Put this back when that issue is solved.
1054 // PlanCompiler.Assert(TypeSemantics.IsEquivalentOrPromotableTo(n.Child0.Op.Type, op.Type),
1055 // "Invalid use of SoftCastOp: Type " + n.Child0.Op.Type.Identity + " is not promotable to " + op.Type);
1056 return VisitChild(n, 0);
1059 public override DbExpression Visit(IsOfOp op, Node n)
1061 // Direct conversion to DbIsOfExpression (with DbExpressionKind.IsOf) with the same Type and converted argument DbExpression
1063 return VisitChild(n, 0).IsOfOnly(op.IsOfType);
1065 return VisitChild(n, 0).IsOf(op.IsOfType);
1068 public override DbExpression Visit(ExistsOp op, Node n)
1071 // Exists requires a RelOp input set
1073 DbExpression inputExpr = VisitNode(n.Child0);
1076 // Information about the Vars published by the RelOp argument does not need to be maintained
1077 // since they may not now be used higher in the CQT.
1079 ConsumeRelOp(inputExpr);
1082 // Exists --> Not(IsEmpty(Input set)) via DbExpressionBuilder.Exists
1084 return inputExpr.IsEmpty().Not();
1087 public override DbExpression Visit(ElementOp op, Node n)
1089 // We create this op when turning ApplyOp into a scalar subquery
1090 DbExpression inputExpr = VisitNode(n.Child0);
1091 AssertRelOp(inputExpr);
1092 ConsumeRelOp(inputExpr);
1094 DbElementExpression elementExpr = DbExpressionBuilder.CreateElementExpressionUnwrapSingleProperty(inputExpr);
1098 public override DbExpression Visit(GetRefKeyOp op, Node n)
1100 // We should never see this Op
1101 throw EntityUtil.NotSupported();
1104 public override DbExpression Visit(GetEntityRefOp op, Node n)
1106 // We should never see this Op
1107 throw EntityUtil.NotSupported();
1110 public override DbExpression Visit(CollectOp op, Node n)
1112 // We should never get here
1113 throw EntityUtil.NotSupported();
1117 #region RelOp Conversions
1120 /// Generates a name for the specified Var.
1121 /// If the Var has a name (TryGetName), then we use the name to look up
1122 /// the right alias generator, and get a column name from the alias generator
1123 /// Otherwise, we simply get a name from the default alias generator
1125 /// <param name="projectedVar">the var in question</param>
1126 /// <param name="aliasMap">map to identify the appropriate alias generator</param>
1127 /// <param name="defaultAliasGenerator">the default alias generator</param>
1128 /// <param name="alreadyUsedNames">list of already used names</param>
1129 /// <returns></returns>
1130 private static string GenerateNameForVar(Var projectedVar, Dictionary<string, AliasGenerator> aliasMap,
1131 AliasGenerator defaultAliasGenerator, Dictionary<string, string> alreadyUsedNames)
1134 AliasGenerator aliasGenerator;
1136 if (projectedVar.TryGetName(out columnName))
1138 if (!aliasMap.TryGetValue(columnName, out aliasGenerator))
1141 // No existing column in the current row with the same name. Create
1142 // an alias-generator for future use
1144 aliasGenerator = new AliasGenerator(columnName);
1145 aliasMap[columnName] = aliasGenerator;
1150 // Column name collides with another name in the same row.
1151 // Use the alias-generator to generate a new name
1153 columnName = aliasGenerator.Next();
1159 // Must be a computed column or some such. Use the default alias generator
1161 aliasGenerator = defaultAliasGenerator;
1162 columnName = aliasGenerator.Next();
1165 // Check to see if I've used this name already
1166 while (alreadyUsedNames.ContainsKey(columnName))
1168 columnName = aliasGenerator.Next();
1171 alreadyUsedNames[columnName] = columnName;
1176 /// Called by both Visit(ProjectOp) and VisitSetOpArgument to create a DbProjectExpression
1177 /// based on the RelOpInfo of the projection input and the set of projected Vars.
1179 /// The projected Vars must have already been brought into scope (by one of the
1180 /// methods such as EnterExpressionBinding, EnterVarDefScope, etc) before this method
1181 /// is called, or the projected Vars will not be successfully resolved.
1182 /// Both Visit(ProjectOp) and VisitSetOpArgument do this"
1183 /// 1. Visit(ProjectOp) takes both DbExpressionBinding and VarDef based Vars into account
1184 /// 2. The Vars produced by a SetOpArgument projection are only allowed to be DbExpressionBinding
1185 /// based and are brought into scope when the original SetOp argument Node is visited.
1187 /// <param name="sourceInfo"></param>
1188 /// <param name="outputVars"></param>
1189 /// <returns></returns>
1190 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1309:UseOrdinalStringComparison", MessageId = "System.Collections.Generic.Dictionary`2<System.String,System.String>.#ctor(System.Collections.Generic.IEqualityComparer`1<System.String>)"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1309:UseOrdinalStringComparison", MessageId = "System.Collections.Generic.Dictionary`2<System.String,System.Data.Common.Utils.AliasGenerator>.#ctor(System.Collections.Generic.IEqualityComparer`1<System.String>)")]
1191 private DbExpression CreateProject(RelOpInfo sourceInfo, IEnumerable<Var> outputVars)
1194 // For each Var produced by the ProjectOp, call ResolveVar to retrieve the correct CQT DbExpression.
1195 // This will either be a DbExpression that references the CQT Var under which the IQT is currently
1196 // bound (if it is in scope) or it will be a copy of the DbExpression subtree that defines the Var,
1197 // if the Var is a ComputedVar defined by a local VarDefOp.
1198 // A new column name is generated to project the DbExpression, and the VarInfoList produced
1199 // by this conversion is updated to include a new VarInfo indicating that the projected Var can be
1200 // reached in the DbProjectExpression returned from this method via a property reference to the generated column name.
1201 // This is the only path element required since the Vars are an immediate product of the ProjectOp, which also hides any Vars below it.
1202 // Hence a new VarInfoList is constructed and published by the new DbProjectExpression.
1203 // The list of column name/DbExpression pairs is built to use later when constructing the projection expression.
1205 VarInfoList projectedInfo = new VarInfoList();
1206 List<KeyValuePair<string, DbExpression>> projectedCols = new List<KeyValuePair<string, DbExpression>>();
1207 AliasGenerator colGen = new AliasGenerator("C");
1208 Dictionary<string, AliasGenerator> aliasMap = new Dictionary<string, AliasGenerator>(StringComparer.InvariantCultureIgnoreCase);
1209 Dictionary<string, string> alreadyUsedAliases = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
1210 foreach (Var projectedVar in outputVars)
1212 string columnName = GenerateNameForVar(projectedVar, aliasMap, colGen, alreadyUsedAliases);
1214 DbExpression columnValue = ResolveVar(projectedVar);
1215 projectedCols.Add(new KeyValuePair<string, DbExpression>(columnName, columnValue));
1217 VarInfo colInfo = new VarInfo(projectedVar);
1218 colInfo.PrependProperty(columnName);
1219 projectedInfo.Add(colInfo);
1223 // Create a new DbProjectExpression with the converted Input and a new row (DbNewInstanceExpression) projection using the
1224 // previously constructed column names and Expressions to define the shape of the resulting row. The Input is bound
1225 // under the Var publisher name specified by its RelOpInfo, which will be a unique name based on the type of RelOp it was converted from.
1227 DbExpression retExpr = sourceInfo.CreateBinding().Project(DbExpressionBuilder.NewRow(projectedCols));
1230 // Publish the Vars produced by the new DbProjectExpression:
1231 // PublisherName: The next Project alias.
1232 // PublishedVars: The PublishedVars of the Project are those specified in the VarSet of the ProjectOp, reachable using the generated column names.
1234 PublishRelOp(_projectAliases.Next(), retExpr, projectedInfo);
1240 /// Called by both ScanTableOp and UnnestOp Visitor pattern methods to determine
1241 /// the shape of the output of the converted form of those Ops, in terms of the
1242 /// IQT Vars that are published by the resulting DbExpression and how those Vars should
1245 /// <param name="targetTable">The table that is logically produced by the Op. For non-record sourceTypes, this should consist of a single column that logically constitutes the entire 'table'</param>
1246 /// <returns>A VarInfoList containing VarInfo instances that correctly track the Var or Vars produced by the targetTable, in accordance with the shape of the sourceType</returns>
1247 private static VarInfoList GetTableVars(Table targetTable)
1249 VarInfoList outputVars = new VarInfoList();
1251 if (targetTable.TableMetadata.Flattened)
1253 // For a flat table, one Var per table column must be produced.
1254 // There should be a ColumnVar in the targetTable's Columns collection for each
1255 // column in the record type (for the table), and the VarInfo instances created here will track the
1256 // fact that each ColumnVar should be reached via DbPropertyExpression of the record column's name
1257 for (int idx = 0; idx < targetTable.Columns.Count; idx++)
1259 VarInfo colInfo = new VarInfo(targetTable.Columns[idx]);
1260 colInfo.PrependProperty(targetTable.TableMetadata.Columns[idx].Name);
1261 outputVars.Add(colInfo);
1266 // Otherwise, a single Var must be produced, which is immediately reachable
1267 outputVars.Add(new VarInfo(targetTable.Columns[0]));
1273 public override DbExpression Visit(ScanTableOp op, Node n)
1276 // Currently 2 different types of 'Table' (i.e. Extent) are supported:
1277 // Record Extents (for example an S-Space table or view)
1278 // Entity Extents (for example a C-Space EntitySet)
1279 // These extents produce results of different shapes - the EntitySet case produces simply
1280 // A collection of Entities, not a collection of records with a single Entity-typed column
1281 // This distinction is handled in the common GetTableVars method shared by ScanTableOp and
1282 // UnnestOp Visitor pattern methods.
1284 PlanCompiler.Assert(op.Table.TableMetadata.Extent != null, "Invalid TableMetadata used in ScanTableOp - no Extent specified");
1287 // We don't expect to see any view expressions here
1289 PlanCompiler.Assert(!n.HasChild0, "views are not expected here");
1291 VarInfoList outputVars = GetTableVars(op.Table);
1293 // ScanTable converts to ExtentExpression
1294 DbExpression retExpr = op.Table.TableMetadata.Extent.Scan();
1297 // Publish the Vars that are logically produced by the ExtentExpression:
1298 // PublisherName: The next Extent alias
1299 // PublishedVars: The single Var (for an Entity extent) or multiple column-bound Vars (for a structured type extent)
1300 // that are logically published by the ExtentExpression.
1302 PublishRelOp(_extentAliases.Next(), retExpr, outputVars);
1304 // Return the ExtentExpression
1308 public override DbExpression Visit(ScanViewOp op, Node n)
1310 // We should never see this Op
1311 throw EntityUtil.NotSupported();
1315 /// Translate UnnestOp which is assumed (at this stage) to wrap a native ScalarOp
1316 /// that returns a collection (e.g. a table-valued function node).
1318 public override DbExpression Visit(UnnestOp op, Node n)
1320 // support Unnest(VarDef(input)) -> input
1321 // where input is presumed to have a collection type (e.g. TVF)
1322 PlanCompiler.Assert(n.Child0.Op.OpType == OpType.VarDef,
1323 "an unnest's child must be a VarDef");
1325 // get input (first child of VarDef)
1326 Node input = n.Child0.Child0;
1329 DbExpression expr = input.Op.Accept(this, input);
1331 // verify that the result is actually a collection
1332 PlanCompiler.Assert(expr.ResultType.EdmType.BuiltInTypeKind == BuiltInTypeKind.CollectionType,
1333 "the input to unnest must yield a collection after plan compilation");
1335 // collect table vars for the unnest
1336 VarInfoList outputVars = GetTableVars(op.Table);
1337 PublishRelOp(_extentAliases.Next(), expr, outputVars);
1343 /// Builds up an "empty" projection over the input node. Well, in reality, we build
1344 /// up a dummy projection node - which simply selects out some constant (which
1345 /// is never used). This is useful in scenarios where the outputs are
1346 /// uninteresting, but the input row count is
1348 /// <param name="relOpNode">the relOp node</param>
1349 /// <returns></returns>
1350 private RelOpInfo BuildEmptyProjection(Node relOpNode)
1353 // Ignore the projectOp at the root - if any
1355 if (relOpNode.Op.OpType == OpType.Project)
1357 relOpNode = relOpNode.Child0;
1361 // Visit the Input RelOp, bring its Var(s) into scope, and retrieve and consume the RelOpInfo that describes its published Vars
1363 RelOpInfo sourceInfo = EnterExpressionBindingScope(relOpNode);
1366 // Create a new DbProjectExpression with the converted Input and a new row (DbNewInstanceExpression) projection using the
1367 // previously constructed column names and Expressions to define the shape of the resulting row. The Input is bound
1368 // under the Var publisher name specified by its RelOpInfo, which will be a unique name based on the type of RelOp it was converted from.
1370 DbExpression constExpr = DbExpressionBuilder.Constant(1);
1371 List<KeyValuePair<string, DbExpression>> projectedCols = new List<KeyValuePair<string, DbExpression>>();
1372 projectedCols.Add(new KeyValuePair<string, DbExpression>("C0", constExpr));
1374 DbExpression retExpr = sourceInfo.CreateBinding().Project(DbExpressionBuilder.NewRow(projectedCols));
1376 // Publish the Vars produced by the new DbProjectExpression:
1377 // PublisherName: The next Project alias.
1378 // PublishedVars: The PublishedVars of the Project are those specified in the VarSet of the ProjectOp, reachable using the generated column names.
1380 PublishRelOp(_projectAliases.Next(), retExpr, new VarInfoList());
1382 // remove the Input's Vars from scope, unbinding the Input's VarInfos.
1383 ExitExpressionBindingScope(sourceInfo);
1385 RelOpInfo relOpInfo = ConsumeRelOp(retExpr);
1390 /// Build up a Project Op with exactly the Vars that we want. If the input is
1391 /// a Project already, piggyback on it, and get the Vars we want. Otherwise,
1392 /// create a new ProjectOp, and define the specified Vars
1394 /// Note that the ProjectOp's output (element) type will be a record with the fields
1395 /// in exactly the order specified by the projectionVars argument
1398 /// <param name="relOpNode">the input relOpNode to cap with a Project</param>
1399 /// <param name="projectionVars">List of vars we are interested in</param>
1400 /// <returns>A ProjectOp that produces the right set of Vars</returns>
1401 private RelOpInfo BuildProjection(Node relOpNode, IEnumerable<Var> projectionVars)
1403 DbExpression retExpr = null;
1406 // If the input is a ProjectOp, then simply invoke the ProjectOp handler, but
1407 // use the requested Vars instead
1409 ProjectOp projectOp = relOpNode.Op as ProjectOp;
1410 if (projectOp != null)
1412 retExpr = VisitProject(projectOp, relOpNode, projectionVars);
1417 // Otherwise, treat it in a very similar fashion to a normal projectOp. The
1418 // only difference being that we have no VarDefList argument
1422 // Visit the Input RelOp, bring its Var(s) into scope, and retrieve and consume the RelOpInfo that describes its published Vars
1424 RelOpInfo sourceInfo = EnterExpressionBindingScope(relOpNode);
1427 // Call CreateProject to convert resolve the projected Vars,
1428 // create the DbProjectExpression, and associate the projected Vars
1429 // with the new DbProjectExpression in the DbExpression -> RelOpInfo map.
1431 retExpr = CreateProject(sourceInfo, projectionVars);
1433 // remove the Input's Vars from scope, unbinding the Input's VarInfos.
1434 ExitExpressionBindingScope(sourceInfo);
1437 RelOpInfo relOpInfo = ConsumeRelOp(retExpr);
1441 DbExpression VisitProject(ProjectOp op, Node n, IEnumerable<Var> varList)
1444 // Visit the Input RelOp, bring its Var(s) into scope, and retrieve and consume the RelOpInfo that describes its published Vars
1446 RelOpInfo sourceInfo = EnterExpressionBindingScope(n.Child0);
1449 // With the Input in scope, visit the VarDefs from the ProjectOp's VarDefList and bring their ComputedVars into scope
1451 if (n.Children.Count > 1)
1453 EnterVarDefListScope(n.Child1);
1457 // Call CreateProject to convert resolve the projected Vars,
1458 // create the DbProjectExpression, and associate the projected Vars
1459 // with the new DbProjectExpression in the DbExpression -> RelOpInfo map.
1461 DbExpression retExpr = CreateProject(sourceInfo, varList);
1463 // Take the local ComputedVars from the ProjectOp's VarDefList out of scope.
1464 if (n.Children.Count > 1)
1469 // remove the Input's Vars from scope, unbinding the Input's VarInfos.
1470 ExitExpressionBindingScope(sourceInfo);
1475 public override DbExpression Visit(ProjectOp op, Node n)
1477 return VisitProject(op, n, op.Outputs);
1480 public override DbExpression Visit(FilterOp op, Node n)
1483 // Visit the Input RelOp, bring its Var(s) into scope, and retrieve and consume the RelOpInfo that describes its published Vars
1485 RelOpInfo inputInfo = EnterExpressionBindingScope(n.Child0);
1488 // Visit the Predicate with the Input Var(s) in scope and assert that the predicate is valid
1490 DbExpression predicateExpr = VisitNode(n.Child1);
1491 PlanCompiler.Assert(TypeSemantics.IsPrimitiveType(predicateExpr.ResultType, PrimitiveTypeKind.Boolean), "Invalid FilterOp Predicate (non-ScalarOp or non-Boolean result)");
1494 // Create a new DbFilterExpression with the converted Input and Predicate.
1495 // The RelOpState produced from the Input (above) indicates the name that should be used
1496 // in the DbExpressionBinding for the Input expression (this is the name that the
1497 // Input's Vars were brought into scope with in EnterExpressionBindingScope).
1499 DbExpression retExpr = inputInfo.CreateBinding().Filter(predicateExpr);
1502 // Remove the Input's Var(s) from scope and unbind the Input's VarInfo(s)
1504 ExitExpressionBindingScope(inputInfo);
1507 // Update the tracked RelOpInfo for the new DbFilterExpression. This consists of:
1508 // PublisherName: The next Filter alias.
1509 // PublishedVars: The PublishedVars of the Filter are the same (now unbound) PublishedVars of its Input.
1511 PublishRelOp(_filterAliases.Next(), retExpr, inputInfo.PublishedVars);
1516 private List<DbSortClause> VisitSortKeys(IList<InternalTrees.SortKey> sortKeys)
1518 VarVec sortVars = _iqtCommand.CreateVarVec();
1519 List<DbSortClause> sortClauses = new List<DbSortClause>();
1520 foreach (InternalTrees.SortKey sortKey in sortKeys)
1523 // If we've already seen the same Var, then ignore it
1525 if (sortVars.IsSet(sortKey.Var))
1531 sortVars.Set(sortKey.Var);
1534 DbSortClause sortClause = null;
1535 DbExpression keyExpression = ResolveVar(sortKey.Var);
1536 if (!string.IsNullOrEmpty(sortKey.Collation))
1539 sortClause = (sortKey.AscendingSort ? keyExpression.ToSortClause(sortKey.Collation) : keyExpression.ToSortClauseDescending(sortKey.Collation));
1543 sortClause = (sortKey.AscendingSort ? keyExpression.ToSortClause() : keyExpression.ToSortClauseDescending());
1546 sortClauses.Add(sortClause);
1552 public override DbExpression Visit(SortOp op, Node n)
1555 // Visit the Input RelOp, bring its Var(s) into scope, and retrieve and consume the RelOpInfo that describes its published Vars
1557 RelOpInfo inputInfo = EnterExpressionBindingScope(n.Child0);
1558 PlanCompiler.Assert(!n.HasChild1, "SortOp can have only one child");
1561 // Visit the SortKeys with the Input's Vars in scope and create the DbSortExpression
1563 DbExpression retExpr = inputInfo.CreateBinding().Sort(VisitSortKeys(op.Keys));
1566 // Remove the Input's Vars from scope
1568 ExitExpressionBindingScope(inputInfo);
1571 // Update the tracked RelOpInfo for the new DbSortExpression. This consists of:
1572 // PublisherName: The next Sort alias.
1573 // PublishedVars: The PublishedVars of the Sort are the same as its Input.
1575 PublishRelOp(_sortAliases.Next(), retExpr, inputInfo.PublishedVars);
1579 private DbExpression CreateLimitExpression(DbExpression argument, DbExpression limit, bool withTies)
1581 PlanCompiler.Assert(!withTies, "Limit with Ties is not currently supported");
1582 return argument.Limit(limit);
1585 public override DbExpression Visit(ConstrainedSortOp op, Node n)
1587 DbExpression retExpr = null;
1588 RelOpInfo inputInfo = null;
1589 string alias = null;
1590 bool nullSkip = (OpType.Null == n.Child1.Op.OpType);
1591 bool nullLimit = (OpType.Null == n.Child2.Op.OpType);
1592 PlanCompiler.Assert(!nullSkip || !nullLimit, "ConstrainedSortOp with no Skip Count and no Limit?");
1593 if (op.Keys.Count == 0)
1595 // Without SortKeys, this ConstrainedSortOp must represent a Limit operation applied to the input.
1596 PlanCompiler.Assert(nullSkip, "ConstrainedSortOp without SortKeys cannot have Skip Count");
1599 // Visit the input Node and retrieve its RelOpInfo
1601 DbExpression inputExpr = this.VisitNode(n.Child0);
1602 inputInfo = ConsumeRelOp(inputExpr);
1605 // Create the DbLimitExpression using the converted form of the input Node's Child2 Node (the Limit Node)
1606 // together with the input DbExpression created above.
1608 retExpr = this.CreateLimitExpression(inputExpr, this.VisitNode(n.Child2), op.WithTies);
1609 alias = _limitAliases.Next();
1614 // Bring the Input into scope and visit the SortKeys to produce the equivalent SortClauses,
1615 // then remove the Input's Vars from scope.
1617 inputInfo = EnterExpressionBindingScope(n.Child0);
1618 List<DbSortClause> sortOrder = VisitSortKeys(op.Keys);
1619 ExitExpressionBindingScope(inputInfo);
1622 // SortKeys are present, so one of the following cases must be true:
1623 // - Child1 (Skip Count) is non-NullOp, Child2 (Limit) is non-NullOp => Limit(Skip(input))
1624 // - Child1 (Skip Count) is non-NullOp, Child2 (Limit) is NullOp => Skip(input)
1625 // - Child1 (Skip Count) is NullOp, Child2 (Limit) is non-NullOp => Limit(Sort(input))
1627 if (!nullSkip && !nullLimit)
1629 // Limit(Skip(input))
1631 this.CreateLimitExpression(
1632 inputInfo.CreateBinding().Skip(sortOrder, VisitChild(n, 1)),
1636 alias = _limitAliases.Next();
1638 else if (!nullSkip && nullLimit)
1641 retExpr = inputInfo.CreateBinding().Skip(sortOrder, VisitChild(n, 1));
1642 alias = _skipAliases.Next();
1644 else if (nullSkip && !nullLimit)
1646 // Limit(Sort(input))
1648 this.CreateLimitExpression(
1649 inputInfo.CreateBinding().Sort(sortOrder),
1653 alias = _limitAliases.Next();
1658 // Update the tracked RelOpInfo for the new expression. This consists of:
1659 // PublisherName: The next Skip or Limit alias depending on which expression is topmost.
1660 // PublishedVars: The PublishedVars of the Skip/Limit are the same as its Input.
1662 PublishRelOp(alias, retExpr, inputInfo.PublishedVars);
1666 public override DbExpression Visit(GroupByOp op, Node n)
1669 // Track the Vars that are logically published by this GroupBy. These will be
1671 VarInfoList publishedVars = new VarInfoList();
1674 // Visit the Input, publish its Vars and bring them into scope under a new binding name
1676 GroupByScope inputInfo = EnterGroupByScope(n.Child0);
1679 // With the Input in scope, visit the VarDefs for the Keys and build the Var to DbExpression map for them
1681 EnterVarDefListScope(n.Child1);
1684 // With the mappings for the Key Vars in scope, build the Name/DbExpression key column pairs by
1685 // generating a new column alias (prefixed with 'K') and resolving the Var, for each Var in the Keys Var list.
1686 // The list of output Vars that represent aggregates is also built here, by starting with a list
1687 // of all output Vars and removing each Key Var as it is processed.
1689 AliasGenerator keyAliases = new AliasGenerator("K");
1690 List<KeyValuePair<string, DbExpression>> keyExprs = new List<KeyValuePair<string, DbExpression>>();
1691 List<Var> outputAggVars = new List<Var>(op.Outputs);
1692 foreach (Var keyVar in op.Keys)
1695 // Generate the alias and resolve the Key Var. This will find and retrieve the DbExpression to which
1696 // the Key Var maps, which is most likely the VarDef scope that was just entered by visiting the Key VarDefListOp Node.
1697 // Track the Name/DbExpression pairs for use later in CreateGroupByExpression.
1699 string keyColName = keyAliases.Next();
1700 keyExprs.Add(new KeyValuePair<string, DbExpression>(keyColName, ResolveVar(keyVar)));
1703 // Create a new VarInfo to track this key Var. To begin with it is reachable through
1704 // a property reference of the column under which it is bound, i.e. using the column alias
1705 // generated above, so the VarInfo property path is set up to contain just that name
1706 // (the VarInfo has no binding name until later in this method when PublishRelOp is called).
1708 VarInfo keyColInfo = new VarInfo(keyVar);
1709 keyColInfo.PrependProperty(keyColName);
1710 publishedVars.Add(keyColInfo);
1713 // Remove the Key Var from the list of Aggregate Outputs
1715 outputAggVars.Remove(keyVar);
1719 // After this point, no Vars in the output Vars list of the GroupBy may be defined by the
1720 // VarDefOps from the Keys VarDefListOp Node, so the Keys VarDefScope should be removed from the scope stack.
1725 // The Vars published by the Input are currently in scope under the binding name that was generated for the Input (Extent0, Filter3, etc).
1726 // This is correct while the Keys are processed, however it is not correct for the aggregates. While the aggregates have access to exactly
1727 // the same Vars, they must be bound under a different name, which will become the GroupVarName used later in this method when a DbGroupExpressionBinding
1728 // is constructed. The GroupVarName is generated simply by appending 'Group' to the (already unique) binding name that was generated for the Input (to yield Extent0Group, Filter3Group, etc).
1730 // In aggregate arguments, the GroupBy input's Vars must be accessed using the Group variable
1731 inputInfo.SwitchToGroupReference();
1733 // Build the map of Var to Aggregate. The Aggregates VarDefListOp Node child of the GroupByOp's Node is
1734 // processed here to build the map. This is the only location in an IQT Command where an AggregateOp is valid.
1735 Dictionary<Var, DbAggregate> aggMap = new Dictionary<Var,DbAggregate>();
1736 Node aggRootNode = n.Child2;
1737 PlanCompiler.Assert(aggRootNode.Op is VarDefListOp, "Invalid Aggregates VarDefListOp Node encountered in GroupByOp");
1738 foreach (Node aggVarDefNode in aggRootNode.Children)
1740 VarDefOp aggVarDef = aggVarDefNode.Op as VarDefOp;
1741 PlanCompiler.Assert(aggVarDef != null, "Non-VarDefOp Node encountered as child of Aggregates VarDefListOp Node");
1743 Var aggVar = aggVarDef.Var;
1744 PlanCompiler.Assert(aggVar is ComputedVar, "Non-ComputedVar encountered in Aggregate VarDefOp");
1746 Node aggOpNode = aggVarDefNode.Child0;
1747 DbExpression aggDef = VisitNode(aggOpNode.Child0);
1748 AggregateOp funcAggOp = aggOpNode.Op as AggregateOp;
1749 PlanCompiler.Assert(funcAggOp != null, "Non-Aggregate Node encountered as child of Aggregate VarDefOp Node");
1750 DbFunctionAggregate newFuncAgg;
1751 if (funcAggOp.IsDistinctAggregate)
1753 newFuncAgg = funcAggOp.AggFunc.AggregateDistinct(aggDef);
1757 newFuncAgg = funcAggOp.AggFunc.Aggregate(aggDef);
1760 PlanCompiler.Assert(outputAggVars.Contains(aggVar), "Defined aggregate Var not in Output Aggregate Vars list?");
1762 aggMap.Add(aggVar, newFuncAgg);
1767 // The Vars published by the Input should no longer be considered in scope, so call ExitExpressionBindingScope to pop them off the scope stack.
1769 ExitGroupByScope(inputInfo);
1772 // Process the list of Aggregate Vars using the Var to Aggregate map created in the code above.
1773 // Note that since there is no dedicated Aggregates VarSet on the GroupByOp, it is necessary to
1774 // process the Vars in the OutputVars set, but beginning with the Var that immediately follows
1775 // the last Key Var.
1776 // Each Var is mapped to a previously created Aggregate using the Var to Aggregate map. The end
1777 // result is a list of name CQT Aggregates that can be used in the call to CreateGroupByExpression.
1779 AliasGenerator aggAliases = new AliasGenerator("A");
1780 List<KeyValuePair<string, DbAggregate>> aggregates = new List<KeyValuePair<string, DbAggregate>>();
1781 foreach(Var aggVar in outputAggVars)
1783 // Generate a new column name for the Aggregate that will be prefixed with 'A'.
1784 string aggColName = aggAliases.Next();
1786 // Map the Var to an Aggregate and add it to the list under the newly generated column name
1787 aggregates.Add(new KeyValuePair<string, DbAggregate>(aggColName, aggMap[aggVar]));
1789 // Create a new VarInfo that will track the Aggregate Var up the CQT. Its property path is
1790 // initialized to the newly generated column name to indicate that the Var must be reached
1791 // with a DbPropertyExpression of the column name.
1792 VarInfo aggColInfo = new VarInfo(aggVar);
1793 aggColInfo.PrependProperty(aggColName);
1795 // Add the Aggregate VarInfo to the list of VarInfos that are tracking the Vars that are
1796 // logically published by the DbGroupByExpression that will result from this method.
1797 publishedVars.Add(aggColInfo);
1801 // Create the DbGroupByExpression. The binding name of the input is used together with the
1802 // generated group name and the input DbExpression to create a DbGroupExpressionBinding.
1803 // The list of named Keys and Aggregates built above are specified in the call to CreateGroupExpressionBinding.
1805 DbExpression retExpr = inputInfo.Binding.GroupBy(keyExprs, aggregates);
1807 PublishRelOp(_groupByAliases.Next(), retExpr, publishedVars);
1812 public override DbExpression Visit(GroupByIntoOp op, Node n)
1814 // We should never see this Op
1815 throw EntityUtil.NotSupported();
1818 #region JoinOp Conversions - CrossJoinOp, InnerJoinOp, FullOuterJoinOp, LeftOuterJoinOp
1820 /// Massages the input to a join node.
1822 /// If the input is a Filter(ScanTable), we throw in a dummy project over
1823 /// this input. This projectOp simply looks at the "referenced" columns of
1824 /// the table, and uses those as the projection Vars
1825 /// Otherwise, sqlgen does not really know which columns are referenced, and
1826 /// ends up adding a projection with all columns of the table.
1828 /// NOTE: We may want to do this for Apply as well
1830 /// <param name="joinInputNode">one of the inputs to the join node</param>
1831 /// <returns>RelopInfo for the transformed input</returns>
1832 private RelOpInfo VisitJoinInput(Node joinInputNode)
1834 RelOpInfo relOpInfo;
1836 if (joinInputNode.Op.OpType == OpType.Filter && joinInputNode.Child0.Op.OpType == OpType.ScanTable)
1838 ScanTableOp scanTableOp = (ScanTableOp)joinInputNode.Child0.Op;
1840 // #479385: Handle "empty" projection lists
1842 if (scanTableOp.Table.ReferencedColumns.IsEmpty)
1844 relOpInfo = BuildEmptyProjection(joinInputNode);
1848 relOpInfo = BuildProjection(joinInputNode, scanTableOp.Table.ReferencedColumns);
1853 relOpInfo = EnterExpressionBindingScope(joinInputNode, false);
1860 /// Called by all Visitor pattern method that handle binary JoinOps (Inner, FullOuter, LeftOuter)
1862 /// <param name="joinNode">The IQT Node that references the JoinOp</param>
1863 /// <param name="joinKind">The CQT DbExpressionKind that represents the type of join to create</param>
1864 /// <returns></returns>
1865 private DbExpression VisitBinaryJoin(Node joinNode, DbExpressionKind joinKind)
1868 // Visit and retrieve RelOpInfo for the left Input, but do not bring its published Vars
1869 // into scope. Passing the value false as the 'pushScope' argument indicates that
1870 // EnterExpressionBindingScope should visit the specified Node, retrieve (and remove)
1871 // its RelOpInfo from the DbExpression to RelOpInfo map, but not push that RelOpInfo onto
1873 // The Vars are not brought into scope in order to prevent Left-correlation - the Vars of
1874 // the left Input should not be visible to the right Input of the same join.
1876 RelOpInfo leftInfo = VisitJoinInput(joinNode.Child0);
1878 // Do the same for the right Input to the join.
1879 RelOpInfo rightInfo = VisitJoinInput(joinNode.Child1);
1881 bool scopesPushed = false;
1882 DbExpression joinCond = null;
1883 if (joinNode.Children.Count > 2)
1885 // If a Join condition Node is present, bring the Vars from the left and right arguments into scope
1886 // and visit the Join condition Node's Op to convert the join condition. The scopesPushed flag is updated
1887 // to true to indicate that the Var scopes should be removed from the scope stack when this method completes.
1888 scopesPushed = true;
1890 PushExpressionBindingScope(leftInfo);
1891 PushExpressionBindingScope(rightInfo);
1893 joinCond = VisitNode(joinNode.Child2);
1897 // There is no join condition, so the default condition - DbConstantExpression(True) - is used.
1898 // The Vars from the left and right Inputs to the join need not be brought into scope, so the scopesPushed flag is not updated.
1899 joinCond = DbExpressionBuilder.True;
1902 // Create a new DbJoinExpression using bindings created by the RelOpInfos of the left and right Inputs,
1903 // the specified Join type, and the converted or default Join condition DbExpression.
1904 DbExpression retExpr = DbExpressionBuilder.CreateJoinExpressionByKind(
1907 leftInfo.CreateBinding(),
1908 rightInfo.CreateBinding()
1911 // Create a new VarInfoList to hold the output Vars that are logically published by the new DbJoinExpression
1912 VarInfoList outputVars = new VarInfoList();
1915 // Remove the right argument from scope. If the scopesPushed flag is true then the RelOpInfo
1916 // will be popped from the scope stack.
1918 ExitExpressionBindingScope(rightInfo, scopesPushed);
1920 // In the record type that results from the join, the Vars published by the left argument
1921 // must now be accessed using an additional DbPropertyExpression that specifies the column name
1922 // used in the join (which was also the binding name used in the DbExpressionBinding created as part of the join)
1923 // PrependProperty is called on the published Vars of the right argument to reflect this, then they
1924 // are added to the overall set of Vars that are logically published by the new DbJoinExpression.
1925 // Note that calling ExitExpressionBindingScope has already unbound these Vars, making them
1926 // ready for use by the consumer of the new DbJoinExpression.
1927 rightInfo.PublishedVars.PrependProperty(rightInfo.PublisherName);
1928 outputVars.AddRange(rightInfo.PublishedVars);
1930 // Repeat the above steps for the left argument to the join
1931 ExitExpressionBindingScope(leftInfo, scopesPushed);
1932 leftInfo.PublishedVars.PrependProperty(leftInfo.PublisherName);
1933 outputVars.AddRange(leftInfo.PublishedVars);
1936 // Update the tracked RelOpInfo for the new DbJoinExpression. This consists of:
1937 // PublisherName: The next Join alias.
1938 // PublishedVars: The PublishedVars of the Join are the (now unbound) PublishedVars of both Inputs, with appropriate column names prepended to their property paths.
1940 PublishRelOp(_joinAliases.Next(), retExpr, outputVars);
1945 public override DbExpression Visit(CrossJoinOp op, Node n)
1947 // Create a new list of DbExpressionBinding to track the bindings that will be used in the new DbJoinExpression
1948 List<DbExpressionBinding> inputBindings = new List<DbExpressionBinding>();
1950 // Create a new VarInfoList to track the Vars that will be logically published by the new DbJoinExpression
1951 VarInfoList outputVars = new VarInfoList();
1954 // For each Input Node:
1955 // 1. Visit and retrieve RelOpInfo for the Node, but do not bring it's Vars into scope
1956 // (again to avoid Left-correlation between join Inputs).
1957 // 2. Use the RelOpInfo to create a correct Expressionbinding and add it to the list of ExpressionBindings
1958 // 3. Call ExitExpressionBinding, indicating that the RelOpInfo was not originally pushed onto the scope stack
1959 // and so its published Vars should simply be unbound and the attempt should not be made to pop it from the scope stack.
1960 // 4. Update the property path for the Vars published by the Input to start with the same column name as was just used in the DbExpressionBinding for the Input
1961 // 5. Add the Vars published by the Input to the overall set of Vars that will be logically published by the new DbJoinExpression (created below).
1963 foreach (Node inputNode in n.Children)
1965 RelOpInfo inputInfo = VisitJoinInput(inputNode);
1966 inputBindings.Add(inputInfo.CreateBinding());
1967 ExitExpressionBindingScope(inputInfo, false);
1968 inputInfo.PublishedVars.PrependProperty(inputInfo.PublisherName);
1969 outputVars.AddRange(inputInfo.PublishedVars);
1972 // Create a new DbJoinExpression from the list of DbExpressionBinding (implicitly creating a CrossJoin)
1973 DbExpression retExpr = DbExpressionBuilder.CrossJoin(inputBindings);
1975 // Update the DbExpression to RelOpInfo map to indicate that the overall set of Vars collected above are logically published by the new DbJoinExpression
1976 // PublisherName will be the next Join alias.
1977 PublishRelOp(_joinAliases.Next(), retExpr, outputVars);
1979 // Return the new DbJoinExpression
1983 public override DbExpression Visit(InnerJoinOp op, Node n)
1985 // Use common handling for binary Join Ops
1986 return VisitBinaryJoin(n, DbExpressionKind.InnerJoin);
1989 public override DbExpression Visit(LeftOuterJoinOp op, Node n)
1991 // Use common handling for binary Join Ops
1992 return VisitBinaryJoin(n, DbExpressionKind.LeftOuterJoin);
1995 public override DbExpression Visit(FullOuterJoinOp op, Node n)
1997 // Use common handling for binary Join Ops
1998 return VisitBinaryJoin(n, DbExpressionKind.FullOuterJoin);
2003 #region ApplyOp Conversions - CrossApplyOp, OuterApplyOp
2006 /// Called by both CrossApply and OuterApply visitor pattern methods - command handling of both types of Apply operation
2008 /// <param name="applyNode">The Node that references the ApplyOp</param>
2009 /// <param name="applyKind">The CQT DbExpressionKind that corresponds to the ApplyOp (DbExpressionKind.CrossApply for CrossApplyOp, DbExpressionKind.OuterApply for OuterApplyOp)</param>
2010 /// <returns>A new CqtResult containing a DbApplyExpression with the correct ApplyType</returns>
2011 private DbExpression VisitApply(Node applyNode, DbExpressionKind applyKind)
2014 // Visit the Input and bring its Vars into scope for the Apply
2016 RelOpInfo inputInfo = EnterExpressionBindingScope(applyNode.Child0);
2019 // Visit the Apply - there is no need to bring its Vars into scope
2021 RelOpInfo applyInfo = EnterExpressionBindingScope(applyNode.Child1, false);
2023 DbExpression retExpr = DbExpressionBuilder.CreateApplyExpressionByKind(
2025 inputInfo.CreateBinding(),
2026 applyInfo.CreateBinding());
2029 // Unbind the Apply Vars by calling ExitExpressionBindingScope and indicating that the specified scope was not pushed onto the scope stack.
2031 ExitExpressionBindingScope(applyInfo, false);
2034 // Remove the Input Vars from scope and unbind them
2036 ExitExpressionBindingScope(inputInfo);
2039 // Update the property path to the Input and Apply vars appropriately based on the names used in their ExpressionBindings, which will then form the column names in the record output type of the AppyExpression
2041 inputInfo.PublishedVars.PrependProperty(inputInfo.PublisherName);
2042 applyInfo.PublishedVars.PrependProperty(applyInfo.PublisherName);
2045 // Build and publish the set of IQT Vars logically published by the DbApplyExpression
2046 // PublisherName: The next Apply alias.
2047 // PublishedVars: The PublishedVars of the Apply consists of the Vars published by the input plus those published by the apply.
2049 VarInfoList outputVars = new VarInfoList();
2050 outputVars.AddRange(inputInfo.PublishedVars);
2051 outputVars.AddRange(applyInfo.PublishedVars);
2053 PublishRelOp(_applyAliases.Next(), retExpr, outputVars);
2058 public override DbExpression Visit(CrossApplyOp op, Node n)
2060 // Use common handling for Apply Ops
2061 return VisitApply(n, DbExpressionKind.CrossApply);
2064 public override DbExpression Visit(OuterApplyOp op, Node n)
2066 // Use common handling for Apply Ops
2067 return VisitApply(n, DbExpressionKind.OuterApply);
2071 #region SetOp Conversions - ExceptOp, IntersectOp, UnionAllOp
2074 /// Called by VisitSetOp to convert each argument.
2075 /// Determines whether a column-reordering projection should be applied to
2076 /// the argument, and applies that projection if necessary during conversion
2077 /// to a DbExpression. A different projection is applied if no Nodes higher in
2078 /// the IQT consume the vars produced by the SetOp argument.
2080 /// <param name="argNode">
2081 /// A Node that provides one of the arguments to the SetOp
2083 /// <param name="outputVars">
2084 /// Defines the expected order of the Output Vars of the SetOp
2086 /// <param name="argVars">
2087 /// The VarMap for the SetOp argument represented by the node.
2088 /// This specifies the Output (SetOp-produced) Var to Input (Argument-produced)
2089 /// Var mappings for the Vars in the outputVars enumerable.
2092 /// A DbExpression that is the converted form of the argument
2093 /// (with an appropriate column-reording projection applied if necessary)
2095 private DbExpression VisitSetOpArgument(Node argNode, VarVec outputVars, VarMap argVars)
2097 RelOpInfo sourceInfo;
2099 List<Var> projectionVars = new List<Var>();
2102 // If the list of output vars is empty, no higher Nodes required the vars produced by
2103 // this SetOp argument. A projection must therefore be applied that performs the equivalent
2104 // of 'SELECT true FROM <SetOp Argument>'.
2106 if (outputVars.IsEmpty)
2108 sourceInfo = BuildEmptyProjection(argNode);
2113 // Build up the list of Vars that we want as the output for this argument.
2114 // The "outputVars" argument defines the order in which we need the outputs
2116 foreach (Var v in outputVars)
2118 projectionVars.Add(argVars[v]);
2122 // Build up a ProjectOp over the input that produces the required Output vars
2124 sourceInfo = BuildProjection(argNode, projectionVars);
2127 return sourceInfo.Publisher;
2131 /// Called by UnionAll, Intersect and Except (SetOp) visitor pattern methods
2133 /// <param name="op">The visited SetOp</param>
2134 /// <param name="n">The Node that references the SetOp</param>
2135 /// <param name="alias">Alias to use when publishing the SetOp's Vars</param>
2136 /// <param name="setOpBuilder">Callback to construct the SetOp DbExpression from the left and right arguments</param>
2137 /// <returns>The DbExpression equivalent of the SetOp</returns>
2138 private DbExpression VisitSetOp(SetOp op, Node n, AliasGenerator alias, Func<DbExpression, DbExpression, DbExpression> setOpExpressionBuilder)
2141 // To be convertible to a CQT Except/Intersect/DbUnionAllExpression, the SetOp must have exactly 2 arguments.
2146 // Convert the left and right arguments to expressions.
2148 DbExpression left = VisitSetOpArgument(n.Child0, op.Outputs, op.VarMap[0]);
2149 DbExpression right = VisitSetOpArgument(n.Child1, op.Outputs, op.VarMap[1]);
2152 // If the output of the SetOp is a collection of records then the Vars produced
2153 // by the SetOp must be prepended with the names of the record type's columns as
2154 // they are tracked up the tree by VarInfo instances.
2156 CollectionType outputType = TypeHelpers.GetEdmType<CollectionType>(TypeHelpers.GetCommonTypeUsage(left.ResultType, right.ResultType));
2157 IEnumerator<EdmProperty> properties = null;
2158 RowType outputElementType = null;
2159 if (TypeHelpers.TryGetEdmType<RowType>(outputType.TypeUsage, out outputElementType))
2161 properties = outputElementType.Properties.GetEnumerator();
2165 // The published Vars of the DbExpression produced from the SetOp must be its Output Vars.
2166 // These Output Vars are mapped to the Vars of each of the SetOp's arguments using an array
2167 // of VarMaps (one for each argument) on the SetOp.
2168 // A VarInfo instance is added to the published Vars list for each Output Var of the SetOp.
2169 // If the output type of the SetOp is a collection of a record type then each Var's PropertyPath
2170 // is updated to be the name of the corresponding record column.
2172 VarInfoList publishedVars = new VarInfoList();
2173 foreach (Var outputVar in op.Outputs)
2175 VarInfo newVarInfo = new VarInfo(outputVar);
2176 // Prepend a property name to this var, if the output is a record type
2177 if (outputElementType != null)
2179 if (!properties.MoveNext())
2181 PlanCompiler.Assert(false, "Record columns don't match output vars");
2183 newVarInfo.PrependProperty(properties.Current.Name);
2185 publishedVars.Add(newVarInfo);
2188 DbExpression retExpr = setOpExpressionBuilder(left, right);
2189 PublishRelOp(alias.Next(), retExpr, publishedVars);
2194 public override DbExpression Visit(UnionAllOp op, Node n)
2196 return VisitSetOp(op, n, _unionAllAliases, DbExpressionBuilder.UnionAll);
2199 public override DbExpression Visit(IntersectOp op, Node n)
2201 return VisitSetOp(op, n, _intersectAliases, DbExpressionBuilder.Intersect);
2204 public override DbExpression Visit(ExceptOp op, Node n)
2206 return VisitSetOp(op, n, _exceptAliases, DbExpressionBuilder.Except);
2210 public override DbExpression Visit(DerefOp op, Node n)
2212 throw EntityUtil.NotSupported();
2215 public override DbExpression Visit(DistinctOp op, Node n)
2218 // Build a projection above the input that gets the "keys" of the
2221 RelOpInfo sourceInfo = BuildProjection(n.Child0, op.Keys);
2224 // Build the Distinct expression now
2226 DbExpression distinctExpr = sourceInfo.Publisher.Distinct();
2229 // Publish the DbDistinctExpression's Vars:
2230 // PublisherName: The next Distinct alias
2231 // PublishedVars: The PublishedVars of the Distinct are the same (rebound) Vars published by its input
2233 PublishRelOp(_distinctAliases.Next(), distinctExpr, sourceInfo.PublishedVars);
2235 return distinctExpr;
2239 /// Convert SRO(e) => NewMultiset(Element(e'))
2240 /// where e' is the CTree version of e
2241 /// Add a Project over e, if it does not already have a ProjectOp
2243 /// <param name="op"></param>
2244 /// <param name="n"></param>
2245 /// <returns></returns>
2246 public override DbExpression Visit(SingleRowOp op, Node n)
2248 RelOpInfo inputInfo;
2249 DbExpression inputExpr;
2252 // Build a Projection over the child - sqlgen gets very confused otherwise
2254 if (n.Child0.Op.OpType != OpType.Project)
2256 ExtendedNodeInfo childNodeInfo = _iqtCommand.GetExtendedNodeInfo(n.Child0);
2258 // #484757: Handle "empty" projection lists due to projection pruning
2260 if (childNodeInfo.Definitions.IsEmpty)
2262 inputInfo = BuildEmptyProjection(n.Child0);
2266 inputInfo = BuildProjection(n.Child0, childNodeInfo.Definitions);
2268 inputExpr = inputInfo.Publisher;
2272 inputExpr = VisitNode(n.Child0);
2273 AssertRelOp(inputExpr);
2274 inputInfo = ConsumeRelOp(inputExpr);
2277 DbElementExpression elementExpr = inputExpr.Element();
2278 List<DbExpression> collectionElements = new List<DbExpression>();
2279 collectionElements.Add(elementExpr);
2280 DbNewInstanceExpression collectionExpr = DbExpressionBuilder.NewCollection(collectionElements);
2281 PublishRelOp(_elementAliases.Next(), collectionExpr, inputInfo.PublishedVars);
2283 return collectionExpr;
2287 /// Convert SingleRowTableOp into NewMultisetOp(1) - a single element
2288 /// collection. The element type of the collection doesn't really matter
2290 /// <param name="op">SingleRowTableOp</param>
2291 /// <param name="n">current subtree</param>
2292 /// <returns>CQT expression</returns>
2293 public override DbExpression Visit(SingleRowTableOp op, Node n)
2295 DbNewInstanceExpression collectionExpr = DbExpressionBuilder.NewCollection(new [] { DbExpressionBuilder.Constant(1) });
2296 PublishRelOp(_singleRowTableAliases.Next(), collectionExpr, new VarInfoList());
2297 return collectionExpr;
2302 #region Variable Definition Ops
2303 public override DbExpression Visit(VarDefOp op, Node n)
2306 // VarDef and VarDefList are handled in the conversion of the Ops in which they are valid (by calls to EnterVarDefScope/EnterVarDefListScope).
2307 // If this method is called a VarDefOp exists in an invalid location in the IQT
2309 PlanCompiler.Assert(false, "Unexpected VarDefOp");
2310 throw EntityUtil.NotSupported(System.Data.Entity.Strings.Iqt_CTGen_UnexpectedVarDef);
2313 public override DbExpression Visit(VarDefListOp op, Node n)
2316 // VarDef and VarDefList are handled in the conversion of the Ops in which they are valid (by calls to EnterVarDefScope/EnterVarDefListScope).
2317 // If this method is called a VarDefListOp exists in an invalid location in the IQT
2319 PlanCompiler.Assert(false, "Unexpected VarDefListOp");
2320 throw EntityUtil.NotSupported(System.Data.Entity.Strings.Iqt_CTGen_UnexpectedVarDefList);
2326 /// Translates the PhysicalProjectOp. Handles two cases. If the child is a ProjectOp,
2327 /// then we simply piggyback on the ProjectOp method, but with our list of Vars.
2329 /// Otherwise, we visit the child, and then create a DbProjectExpression above it.
2331 /// The reason we special case the first scenario is because we do not want to add
2332 /// an extra Project over a Project-over-Sort expression tree. This causes bad
2333 /// problems later down the line
2335 /// <param name="op">the PhysicalProjectOp</param>
2336 /// <param name="n">current subtree</param>
2337 /// <returns>the CQT expression corresponding to this subtree</returns>
2338 public override DbExpression Visit(PhysicalProjectOp op, Node n)
2340 PlanCompiler.Assert(n.Children.Count == 1, "more than one input to physicalProjectOp?");
2343 // Prune the output vars from the PhysicalProjectOp
2345 VarList prunedOutputs = new VarList();
2346 foreach (Var v in op.Outputs)
2348 if (!prunedOutputs.Contains(v))
2350 prunedOutputs.Add(v);
2354 op.Outputs.AddRange(prunedOutputs);
2357 // Build a Projection over the input with exactly the Vars that we want
2359 RelOpInfo sourceInfo = BuildProjection(n.Child0, op.Outputs);
2361 return sourceInfo.Publisher;
2364 public override DbExpression Visit(SingleStreamNestOp op, Node n)
2366 throw EntityUtil.NotSupported();
2368 public override DbExpression Visit(MultiStreamNestOp op, Node n)
2370 throw EntityUtil.NotSupported();