File: System\Data\Query\InternalTrees\Command.cs
Project: ndp\fx\src\DataEntity\System.Data.Entity.csproj (System.Data.Entity)
//---------------------------------------------------------------------
// <copyright file="Command.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner  Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
 
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Metadata.Edm;
using System.Data.Query.PlanCompiler;
using System.Diagnostics;
using System.Linq;
 
namespace System.Data.Query.InternalTrees
{
    /// <summary>
    /// The Command object encapsulates all information relating to a single command.
    /// It includes the expression tree in question, as well as the parameters to the
    /// command.
    /// Additionally, the Command class serves as a factory for building up different
    /// nodes and Ops. Every node in the tree has a unique id, and this is enforced by
    /// the node factory methods
    /// </summary>
    internal class Command
    {
        #region private state
        private Dictionary<string, ParameterVar> m_parameterMap;
        private List<Var> m_vars;
        private List<Table> m_tables;
        private Node m_root;
        private MetadataWorkspace m_metadataWorkspace;
        private TypeUsage m_boolType;
        private TypeUsage m_intType;
        private TypeUsage m_stringType;
        private ConstantPredicateOp m_trueOp;
        private ConstantPredicateOp m_falseOp;
        private NodeInfoVisitor m_nodeInfoVisitor;
        private PlanCompiler.KeyPullup m_keyPullupVisitor;
        private int m_nextNodeId;
        private int m_nextBranchDiscriminatorValue = 1000;
 
        private bool m_disableVarVecEnumCaching;
        private Stack<VarVec.VarVecEnumerator> m_freeVarVecEnumerators;
        private Stack<VarVec> m_freeVarVecs;
        
 
        // set of referenced rel properties in this query
        private HashSet<RelProperty> m_referencedRelProperties;
        #endregion
 
        #region constructors
        /// <summary>
        /// Creates a new command
        /// </summary>
        internal Command(MetadataWorkspace metadataWorkspace)
        {
            m_parameterMap = new Dictionary<string, ParameterVar>();
            m_vars = new List<Var>();
            m_tables = new List<Table>();
            m_metadataWorkspace = metadataWorkspace;
            if(!TryGetPrimitiveType(PrimitiveTypeKind.Boolean, out m_boolType))
            {
                throw EntityUtil.ProviderIncompatible(System.Data.Entity.Strings.Cqt_General_NoProviderBooleanType);
            }
            if (!TryGetPrimitiveType(PrimitiveTypeKind.Int32, out m_intType))
            {
                throw EntityUtil.ProviderIncompatible(System.Data.Entity.Strings.Cqt_General_NoProviderIntegerType);
            }
            if (!TryGetPrimitiveType(PrimitiveTypeKind.String, out m_stringType))
            {
                throw EntityUtil.ProviderIncompatible(System.Data.Entity.Strings.Cqt_General_NoProviderStringType);
            }
            m_trueOp = new ConstantPredicateOp(m_boolType, true);
            m_falseOp = new ConstantPredicateOp(m_boolType, false);
            m_nodeInfoVisitor = new NodeInfoVisitor(this);
            m_keyPullupVisitor = new PlanCompiler.KeyPullup(this);
 
            // FreeLists
            m_freeVarVecEnumerators = new Stack<VarVec.VarVecEnumerator>();
            m_freeVarVecs = new Stack<VarVec>();
 
            m_referencedRelProperties = new HashSet<RelProperty>();
        }
        #endregion
 
        #region public methods
        /// <summary>
        /// Gets the metadata workspace associated with this command
        /// </summary>
        internal MetadataWorkspace MetadataWorkspace { get { return m_metadataWorkspace; } }
                
        /// <summary>
        /// Gets/sets the root node of the query
        /// </summary>
        internal Node Root { get { return m_root; } set { m_root = value; } }
 
        internal void DisableVarVecEnumCaching() { m_disableVarVecEnumCaching = true; } 
                
        /// <summary>
        /// Returns the next value for a UnionAll BranchDiscriminator.
        /// </summary>
        internal int NextBranchDiscriminatorValue { get { return m_nextBranchDiscriminatorValue++; } }
 
        /// <summary>
        /// Returns the next value for a node id, without incrementing it. 
        /// </summary>
        internal int NextNodeId { get { return m_nextNodeId; } }
 
        #region Metadata Helpers
        /// <summary>
        /// Helper routine to get the metadata representation for the bool type
        /// </summary>
        internal TypeUsage BooleanType
        {
            get { return m_boolType; }
        }
 
        /// <summary>
        /// Helper routine to get the metadata representation of the int type
        /// </summary>
        internal TypeUsage IntegerType
        {
            get { return m_intType; }
        }
 
        /// <summary>
        /// Helper routine to get the metadata representation of the string type
        /// </summary>
        internal TypeUsage StringType
        {
            get { return m_stringType; }
        }
 
        /// <summary>
        /// Get the primitive type by primitive type kind
        /// </summary>
        /// <param name="modelType">EdmMetadata.PrimitiveTypeKind of the primitive type</param>
        /// <param name="type">A TypeUsage that represents the specified primitive type</param>
        /// <returns><c>True</c> if the specified primitive type could be retrieved; otherwise <c>false</c>.</returns>
        private bool TryGetPrimitiveType(PrimitiveTypeKind modelType, out TypeUsage type)
        {
            type = null;
 
            if (modelType == PrimitiveTypeKind.String)
            {
                type = TypeUsage.CreateStringTypeUsage(m_metadataWorkspace.GetModelPrimitiveType(modelType), 
                                                       false /*unicode*/, 
                                                       false /*fixed*/);
            }
            else
            {
                type = m_metadataWorkspace.GetCanonicalModelTypeUsage(modelType);
            }
 
            return (null != type);
        }
        #endregion
 
        #region VarVec Creation
        /// <summary>
        /// VarVec constructor
        /// </summary>
        /// <returns>A new, empty, VarVec</returns>
        internal VarVec CreateVarVec()
        {
            VarVec vec;
            if (m_freeVarVecs.Count == 0)
            {
                vec = new VarVec(this);
            }
            else
            {
                vec = m_freeVarVecs.Pop();
                vec.Clear();
            }
            return vec;
        }
 
        /// <summary>
        /// Create a VarVec with a single Var
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        internal VarVec CreateVarVec(Var v)
        {
            VarVec varset = CreateVarVec();
            varset.Set(v);
            return varset;
        }
 
        /// <summary>
        /// Create a VarVec with the set of specified vars
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        internal VarVec CreateVarVec(IEnumerable<Var> v)
        {
            VarVec vec = CreateVarVec();
            vec.InitFrom(v);
            return vec;
        }
 
        /// <summary>
        /// Create a new VarVec from the input VarVec
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        internal VarVec CreateVarVec(VarVec v)
        {
            VarVec vec = CreateVarVec();
            vec.InitFrom(v);
            return vec;
        }
 
        /// <summary>
        /// Release a VarVec to the freelist
        /// </summary>
        /// <param name="vec"></param>
        internal void ReleaseVarVec(VarVec vec)
        {
            m_freeVarVecs.Push(vec);
        }
        #endregion
 
        #region VarVecEnumerator
        /// <summary>
        /// Create a new enumerator for a VarVec; use a free one if its
        /// available; otherwise, create a new one
        /// </summary>
        /// <param name="vec"></param>
        /// <returns></returns>
        internal VarVec.VarVecEnumerator GetVarVecEnumerator(VarVec vec)
        {
            VarVec.VarVecEnumerator enumerator;
 
            if (m_disableVarVecEnumCaching ||
                m_freeVarVecEnumerators.Count == 0)
            {
                enumerator = new VarVec.VarVecEnumerator(vec);
            }
            else
            {
                enumerator = m_freeVarVecEnumerators.Pop();
                enumerator.Init(vec);
            }
            return enumerator;
        }
 
        /// <summary>
        /// Release an enumerator; keep it in a local stack for future use
        /// </summary>
        /// <param name="enumerator"></param>
        internal void ReleaseVarVecEnumerator(VarVec.VarVecEnumerator enumerator)
        {
            if (!m_disableVarVecEnumCaching)
            {
                m_freeVarVecEnumerators.Push(enumerator);
            }
        }
        #endregion
 
        #region VarList
        /// <summary>
        /// Create an ordered list of Vars - initially empty
        /// </summary>
        /// <returns></returns>
        internal static VarList CreateVarList()
        {
            return new VarList();
        }
 
        /// <summary>
        /// Create an ordered list of Vars
        /// </summary>
        /// <param name="vars"></param>
        /// <returns></returns>
        internal static VarList CreateVarList(IEnumerable<Var> vars)
        {
            return new VarList(vars);
        }
        #endregion
 
        #region VarMap
        internal VarMap CreateVarMap()
        {
            return new VarMap();
        }
 
        #endregion
 
        #region Table Helpers
 
        private int NewTableId()
        {
            return m_tables.Count;
        }
 
 
        /// <summary>
        /// Create a table whose element type is "elementType"
        /// </summary>
        /// <param name="elementType">type of each element (row) of the table</param>
        /// <returns>a table definition object</returns>
        internal static TableMD CreateTableDefinition(TypeUsage elementType)
        {
            return new TableMD(elementType, null);
        }
 
        /// <summary>
        /// Creates a new table definition based on an extent. The element type
        /// of the extent manifests as the single column of the table
        /// </summary>
        /// <param name="extent">the metadata extent</param>
        /// <returns>A new TableMD instance based on the extent</returns>
        internal static TableMD CreateTableDefinition(EntitySetBase extent)
        {
            return new TableMD(TypeUsage.Create(extent.ElementType), extent);
        }
 
        /// <summary>
        /// Create a "flat" table definition object (ie) the table has one column 
        /// for each property of the specified row type
        /// </summary>
        /// <param name="type">the shape of each row of the table</param>
        /// <returns>the table definition</returns>
        internal TableMD CreateFlatTableDefinition(RowType type)
        {
            return CreateFlatTableDefinition(type.Properties, new List<EdmMember>(), null);
        }
 
        /// <summary>
        /// Create a "flat" table defintion. The table has one column for each property
        /// specified, and the key columns of the table are those specified in the 
        /// keyMembers parameter
        /// </summary>
        /// <param name="properties">list of columns for the table</param>
        /// <param name="keyMembers">the key columns (if any)</param>
        /// <param name="entitySet">(OPTIONAL) entityset corresponding to this table</param>
        /// <returns></returns>
        internal TableMD CreateFlatTableDefinition(IEnumerable<EdmProperty> properties, IEnumerable<EdmMember> keyMembers, EntitySetBase entitySet)
        {
            return new TableMD(properties, keyMembers, entitySet);
        }
 
        /// <summary>
        /// Creates a new table instance
        /// </summary>
        /// <param name="tableMetadata">table metadata</param>
        /// <returns>A new Table instance with columns as defined in the specified metadata</returns>
        internal Table CreateTableInstance(TableMD tableMetadata)
        {
            Table t = new Table(this, tableMetadata, NewTableId());
            m_tables.Add(t);
            return t;
        }
 
        #endregion
 
        #region Var Access
 
        /// <summary>
        /// All vars in the query
        /// </summary>
        internal IEnumerable<Var> Vars
        {
            get { return m_vars.Where(v => v.VarType != VarType.NotValid); }
        }
 
        /// <summary>
        /// Access an existing variable in the query (by its id)
        /// </summary>
        /// <param name="id">The ID of the variable to retrieve</param>
        /// <returns>The variable with the specified ID</returns>
        internal Var GetVar(int id)
        {
            Debug.Assert(m_vars[id].VarType != VarType.NotValid, "The var has been replaced by a different var and is no longer valid.");
 
            return m_vars[id];
        }
 
        /// <summary>
        /// Gets the ParameterVar that corresponds to a given named parameter
        /// </summary>
        /// <param name="paramName">The name of the parameter for which to retrieve the ParameterVar</param>
        /// <returns>The ParameterVar that corresponds to the specified parameter</returns>
        internal ParameterVar GetParameter(string paramName)
        {
            return m_parameterMap[paramName];
        }
 
        #endregion
 
        #region Var Creation
 
        private int NewVarId()
        {
            return m_vars.Count;
        }
 
        /// <summary>
        /// Creates a variable for a parameter in the query
        /// </summary>
        /// <param name="parameterName">The name of the parameter for which to create the var</param>
        /// <param name="parameterType">The type of the parameter, and therefore the new var</param>
        /// <returns>A new ParameterVar instance with the specified name and type</returns>
        internal ParameterVar CreateParameterVar(string parameterName,
            TypeUsage parameterType)
        {
            if (m_parameterMap.ContainsKey(parameterName))
                throw new Exception("duplicate parameter name: " + parameterName);
            ParameterVar v = new ParameterVar(NewVarId(), parameterType, parameterName);
            m_vars.Add(v);
            m_parameterMap[parameterName] = v;
            return v;
        }
 
        /// <summary>
        /// Creates a variable for the given parameter variable and replaces it in parameter map.
        /// </summary>
        /// <param name="oldVar">Parameter variable that needs to replaced.</param>
        /// <param name="generateReplacementType">Delegate that generates the replacement parameter's type.</param>
        /// <returns>A new ParameterVar instance created of <paramref name="oldVar"/>.</returns>
        /// <remarks>
        /// This method should be used only to replace external enum or strong spatial parameters with a counterpart whose
        /// type is the underlying type of the enum type, or the union type contating the strong spatial type of the <paramref name="oldVar"/>.
        /// The operation invalidates the <paramref name="oldVar"/>. After the operation has completed 
        /// the <paramref name="oldVar"/>) is invalidated internally and should no longer be used.
        /// </remarks>Func<
        private ParameterVar ReplaceParameterVar(ParameterVar oldVar, Func<TypeUsage, TypeUsage> generateReplacementType)
        {
            Debug.Assert(oldVar != null, "oldVar != null");
            Debug.Assert(m_vars.Contains(oldVar));
            ParameterVar v = new ParameterVar(NewVarId(), generateReplacementType(oldVar.Type), oldVar.ParameterName);
            m_parameterMap[oldVar.ParameterName] = v;
            m_vars.Add(v);
            return v;
        }
 
        /// <summary>
        /// Creates a variable for the given enum parameter variable and replaces it in parameter map.
        /// </summary>
        /// <param name="oldVar">Enum parameter variable that needs to replaced.</param>
        /// <returns>A new ParameterVar instance created of <paramref name="oldVar"/>.</returns>
        /// <remarks>
        /// This method should be used only to replace external enum parameter with a counterpart whose
        /// type is the underlying type of the enum type of the <paramref name="oldVar"/>.
        /// The operation invalidates the <paramref name="oldVar"/>. After the operation has completed 
        /// the <paramref name="oldVar"/>) is invalidated internally and should no longer be used.
        /// </remarks>
        internal ParameterVar ReplaceEnumParameterVar(ParameterVar oldVar)
        {
            return ReplaceParameterVar(oldVar, t => TypeHelpers.CreateEnumUnderlyingTypeUsage(t));
        }
 
        /// <summary>
        /// Creates a variable for the given spatial parameter variable and replaces it in parameter map.
        /// </summary>
        /// <param name="oldVar">Spatial parameter variable that needs to replaced.</param>
        /// <returns>A new ParameterVar instance created of <paramref name="oldVar"/>.</returns>
        /// <remarks>
        /// This method should be used only to replace external strong spatial parameter with a counterpart whose
        /// type is the appropriate union type for <paramref name="oldVar"/>.
        /// The operation invalidates the <paramref name="oldVar"/>. After the operation has completed 
        /// the <paramref name="oldVar"/>) is invalidated internally and should no longer be used.
        /// </remarks>
        internal ParameterVar ReplaceStrongSpatialParameterVar(ParameterVar oldVar)
        {
            return ReplaceParameterVar(oldVar, t => TypeHelpers.CreateSpatialUnionTypeUsage(t));
        }
 
 
        /// <summary>
        /// Creates a new var for a table column
        /// </summary>
        /// <param name="table">The table instance that produces the column</param>
        /// <param name="columnMD">column metadata</param>
        /// <returns>A new ColumnVar instance that references the specified column in the given table</returns>
        internal ColumnVar CreateColumnVar(Table table, ColumnMD columnMD)
        {
            // create a new column var now
            ColumnVar c = new ColumnVar(NewVarId(), table, columnMD);
            table.Columns.Add(c);
            m_vars.Add(c);
            return c;
        }
 
        /// <summary>
        /// Creates a computed var (ie) a variable that is computed by an expression
        /// </summary>
        /// <param name="type">The type of the result produced by the expression that defines the variable</param>
        /// <returns>A new ComputedVar instance with the specified result type</returns>
        internal ComputedVar CreateComputedVar(TypeUsage type)
        {
            ComputedVar v = new ComputedVar(NewVarId(), type);
            m_vars.Add(v);
            return v;
        }
 
        /// <summary>
        /// Creates a SetOp Var of
        /// </summary>
        /// <param name="type">Datatype of the Var</param>
        /// <returns>A new SetOp Var with the specified result type</returns>
        internal SetOpVar CreateSetOpVar(TypeUsage type)
        {
            SetOpVar v = new SetOpVar(NewVarId(), type);
            m_vars.Add(v);
            return v;
        }
 
        #endregion
 
        #region Node Creation
        //
        // The routines below help in node construction. All command tree nodes must go
        // through these routines. These routines help to stamp each node with a unique
        // id (the id is very helpful for debugging)
        //
 
        /// <summary>
        /// Creates a Node with zero children
        /// </summary>
        /// <param name="op">The operator that the Node should reference</param>
        /// <returns>A new Node with zero children that references the specified Op</returns>
        internal Node CreateNode(Op op)
        {
            return this.CreateNode(op, new List<Node>());
        }
 
        /// <summary>
        /// Creates a node with a single child Node
        /// </summary>
        /// <param name="op">The operator that the Node should reference</param>
        /// <param name="arg1">The single child Node</param>
        /// <returns>A new Node with the specified child Node, that references the specified Op</returns>
        internal Node CreateNode(Op op, Node arg1)
        {
            List<Node> l = new List<Node>();
            l.Add(arg1);
            return this.CreateNode(op, l);
        }
 
        /// <summary>
        /// Creates a node with two child Nodes
        /// </summary>
        /// <param name="op">The operator that the Node should reference</param>
        /// <param name="arg1">The first child Node</param>
        /// <param name="arg2">the second child Node</param>
        /// <returns>A new Node with the specified child Nodes, that references the specified Op</returns>
        internal Node CreateNode(Op op, Node arg1, Node arg2)
        {
            List<Node> l = new List<Node>();
            l.Add(arg1); l.Add(arg2);
            return this.CreateNode(op, l);
        }
 
        /// <summary>
        /// Creates a node with 3 child Nodes
        /// </summary>
        /// <param name="op">The operator that the Node should reference</param>
        /// <param name="arg1">The first child Node</param>
        /// <param name="arg2">The second child Node</param>
        /// <param name="arg3">The third child Node</param>
        /// <returns>A new Node with the specified child Nodes, that references the specified Op</returns>
        internal Node CreateNode(Op op, Node arg1, Node arg2, Node arg3)
        {
            List<Node> l = new List<Node>();
            l.Add(arg1); l.Add(arg2); l.Add(arg3);
            return this.CreateNode(op, l);
        }
 
        /// <summary>
        /// Create a Node with the specified list of child Nodes
        /// </summary>
        /// <param name="op">The operator that the Node should reference</param>
        /// <param name="args">The list of child Nodes</param>
        /// <returns>A new Node with the specified child nodes, that references the specified Op</returns>
        internal Node CreateNode(Op op, IList<Node> args)
        {
            return new Node(m_nextNodeId++, op, new List<Node>(args));
        }
 
        /// <summary>
        /// Create a Node with the specified list of child Nodes
        /// </summary>
        /// <param name="op">The operator that the Node should reference</param>
        /// <param name="args">The list of child Nodes</param>
        /// <returns>A new Node with the specified child nodes, that references the specified Op</returns>
        internal Node CreateNode(Op op, List<Node> args)
        {
            return new Node(m_nextNodeId++, op, args);
        }
 
        #endregion
 
        #region ScalarOps
 
        /// <summary>
        /// Creates a new ConstantOp
        /// </summary>
        /// <param name="type">The type of the constant value</param>
        /// <param name="value">The constant value (may be null)</param>
        /// <returns>A new ConstantOp with the specified type and value</returns>
        internal ConstantBaseOp CreateConstantOp(TypeUsage type, object value)
        {
            // create a NullOp if necessary
            if (value == null)
            {
                return new NullOp(type);
            }
            // Identify "safe" constants - the only safe ones are boolean (and we should
            // probably include ints eventually)
            else if (TypeSemantics.IsBooleanType(type))
            {
                return new InternalConstantOp(type, value);
            }
            else
            {
                return new ConstantOp(type, value);
            }
        }
 
        /// <summary>
        /// Create an "internal" constantOp - only for use by the plan compiler to 
        /// represent internally generated constants.
        /// User constants in the query should never get into this function
        /// </summary>
        /// <param name="type">datatype of the constant</param>
        /// <param name="value">constant value</param>
        /// <returns>a new "internal" constant op that represents the constant</returns>
        internal InternalConstantOp CreateInternalConstantOp(TypeUsage type, object value)
        {
            return new InternalConstantOp(type, value);
        }
 
        /// <summary>
        /// An internal constant that serves as a null sentinel, i.e. it is only ever used
        /// to be checked whether it is null
        /// </summary>
        /// <returns></returns>
        internal NullSentinelOp CreateNullSentinelOp()
        {
            return new NullSentinelOp(this.IntegerType, 1);
        }
 
        /// <summary>
        /// An "internal" null constant
        /// </summary>
        /// <param name="type">datatype of the null constant</param>
        /// <returns>a new "internal" null constant op</returns>
        internal NullOp CreateNullOp(TypeUsage type)
        {
            return new NullOp(type);
        }
 
        /// <summary>
        /// Create a constant predicateOp
        /// </summary>
        /// <param name="value">value of the constant predicate</param>
        /// <returns></returns>
        internal ConstantPredicateOp CreateConstantPredicateOp(bool value)
        {
            return value ? m_trueOp : m_falseOp;
        }
 
        /// <summary>
        /// Create a constant predicate with value=true
        /// </summary>
        /// <returns></returns>
        internal ConstantPredicateOp CreateTrueOp()
        {
            return m_trueOp;
        }
        /// <summary>
        /// Create a constant predicateOp with the value false
        /// </summary>
        /// <returns></returns>
        internal ConstantPredicateOp CreateFalseOp()
        {
            return m_falseOp;
        }
 
        /// <summary>
        /// Creates a new FunctionOp
        /// </summary>
        /// <param name="function">EdmFunction metadata that represents the function that is invoked by the Op</param>
        /// <returns>A new FunctionOp that references the specified function metadata</returns>
        internal FunctionOp CreateFunctionOp(EdmFunction function)
        {
            return new FunctionOp(function);
        }
 
        /// <summary>
        /// Creates a new TreatOp
        /// </summary>
        /// <param name="type">Type metadata that specifies the type that the child of the treat node should be treated as</param>
        /// <returns>A new TreatOp that references the specified type metadata</returns>
        internal TreatOp CreateTreatOp(TypeUsage type)
        {
            return new TreatOp(type, false);
        }
 
        /// <summary>
        /// Create a "dummy" treatOp (i.e.) we can actually ignore the treatOp.
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        internal TreatOp CreateFakeTreatOp(TypeUsage type)
        {
            return new TreatOp(type, true);
        }
 
        /// <summary>
        /// Creates a new IsOfOp, which tests if the argument is of the specified type or a promotable type
        /// </summary>
        /// <param name="isOfType">Type metadata that specifies the type with which the type of the argument should be compared</param>
        /// <returns>A new IsOfOp that references the specified type metadata</returns>
        internal IsOfOp CreateIsOfOp(TypeUsage isOfType)
        {
            return new IsOfOp(isOfType, false/*only*/, m_boolType);
        }
        /// <summary>
        /// Creates a new IsOfOp, which tests if the argument is of the specified type (and only the specified type)
        /// </summary>
        /// <param name="isOfType">Type metadata that specifies the type with which the type of the argument should be compared</param>
        /// <returns>A new IsOfOp that references the specified type metadata</returns>
        internal IsOfOp CreateIsOfOnlyOp(TypeUsage isOfType)
        {
            return new IsOfOp(isOfType, true /* "only" */, m_boolType);
        }
 
        /// <summary>
        /// Creates a new CastOp
        /// </summary>
        /// <param name="type">Type metadata that represents the type to which the argument should be cast</param>
        /// <returns>A new CastOp that references the specified type metadata</returns>
        internal CastOp CreateCastOp(TypeUsage type)
        {
            return new CastOp(type);
        }
 
        /// <summary>
        /// Creates a new SoftCastOp and casts the input to the desired type.
        /// 
        /// The caller is expected to determine if the cast is necessary or not
        /// </summary>
        /// <param name="type">Type metadata that represents the type to which the argument should be cast</param>
        /// <returns>A new CastOp that references the specified type metadata</returns>
        internal SoftCastOp CreateSoftCastOp(TypeUsage type)
        {
            return new SoftCastOp(type);
        }
 
        /// <summary>
        /// Creates a new ComparisonOp of the specified type
        /// </summary>
        /// <param name="opType">An OpType that specifies one of the valid comparison OpTypes: EQ, GT, GE, NE, LT, LE</param>
        /// <returns>A new ComparisonOp of the specified comparison OpType</returns>
        internal ComparisonOp CreateComparisonOp(OpType opType)
        {
            return new ComparisonOp(opType, this.BooleanType);
        }
 
        /// <summary>
        /// Creates a new LikeOp
        /// </summary>
        /// <returns>The new LikeOp</returns>
        internal LikeOp CreateLikeOp()
        {
            return new LikeOp(this.BooleanType);
        }
 
        /// <summary>
        /// Creates a new ConditionalOp of the specified type
        /// </summary>
        /// <param name="opType">An OpType that specifies one of the valid condition operations: And, Or, Not, IsNull</param>
        /// <returns>A new ConditionalOp with the specified conditional OpType</returns>
        internal ConditionalOp CreateConditionalOp(OpType opType)
        {
            return new ConditionalOp(opType, this.BooleanType);
        }
 
        /// <summary>
        /// Creates a new CaseOp
        /// </summary>
        /// <param name="type">The result type of the CaseOp</param>
        /// <returns>A new CaseOp with the specified result type</returns>
        internal CaseOp CreateCaseOp(TypeUsage type)
        {
            return new CaseOp(type);
        }
 
        /// <summary>
        /// Creates a new AggregateOp
        /// </summary>
        /// <param name="aggFunc">EdmFunction metadata that specifies the aggregate function</param>
        /// <param name="distinctAgg">Indicates whether or not the aggregate is a distinct aggregate</param>
        /// <returns>A new AggregateOp with the specified function metadata and distinct property</returns>
        internal AggregateOp CreateAggregateOp(EdmFunction aggFunc, bool distinctAgg)
        {
            return new AggregateOp(aggFunc, distinctAgg);
        }
 
        /// <summary>
        /// Creates a named type constructor
        /// </summary>
        /// <param name="type">Type metadata that specifies the type of the instance to construct</param>
        /// <returns>A new NewInstanceOp with the specified result type</returns>
        internal NewInstanceOp CreateNewInstanceOp(TypeUsage type)
        {
            return new NewInstanceOp(type);
        }
 
        /// <summary>
        /// Build out a new NewEntityOp constructing the entity <paramref name="type"/> scoped to the <paramref name="entitySet"/>.
        /// </summary>
        internal NewEntityOp CreateScopedNewEntityOp(TypeUsage type, List<RelProperty> relProperties, EntitySet entitySet)
        {
            return new NewEntityOp(type, relProperties, true, entitySet);
        }
 
        /// <summary>
        /// Build out a new NewEntityOp constructing the uscoped entity <paramref name="type"/>.
        /// </summary>
        internal NewEntityOp CreateNewEntityOp(TypeUsage type, List<RelProperty> relProperties)
        {
            return new NewEntityOp(type, relProperties, false, null);
        }
 
        /// <summary>
        /// Create a discriminated named type constructor
        /// </summary>
        /// <param name="type">Type metadata that specifies the type of the instance to construct</param>
        /// <param name="discriminatorMap">Mapping information including discriminator values</param>
        /// <param name="entitySet">the entityset that this instance belongs to</param>
        /// <param name="relProperties">list of rel properties that have corresponding values</param>
        /// <returns>A new DiscriminatedNewInstanceOp with the specified result type and discrimination behavior</returns>
        internal DiscriminatedNewEntityOp CreateDiscriminatedNewEntityOp(TypeUsage type, ExplicitDiscriminatorMap discriminatorMap,
            EntitySet entitySet, List<RelProperty> relProperties)
        {
            return new DiscriminatedNewEntityOp(type, discriminatorMap, entitySet, relProperties);
        }
 
        /// <summary>
        /// Creates a multiset constructor
        /// </summary>
        /// <param name="type">Type metadata that specifies the type of the multiset to construct</param>
        /// <returns>A new NewMultiSetOp with the specified result type</returns>
        internal NewMultisetOp CreateNewMultisetOp(TypeUsage type)
        {
            return new NewMultisetOp(type);
        }
 
        /// <summary>
        /// Creates a record constructor
        /// </summary>
        /// <param name="type">Type metadata that specifies that record type to construct</param>
        /// <returns>A new NewRecordOp with the specified result type</returns>
        internal NewRecordOp CreateNewRecordOp(TypeUsage type)
        {
            return new NewRecordOp(type);
        }
 
        /// <summary>
        /// Creates a record constructor
        /// </summary>
        /// <param name="type">Type metadata that specifies that record type to construct</param>
        /// <returns>A new NewRecordOp with the specified result type</returns>
        internal NewRecordOp CreateNewRecordOp(RowType type)
        {
            return new NewRecordOp(TypeUsage.Create(type));
        }
 
        /// <summary>
        /// A variant of the above method to create a NewRecordOp. An additional
        /// argument - fields - is supplied, and the semantics is that only these fields
        /// have any values specified as part of the Node. All other fields are
        /// considered to be null.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="fields"></param>
        /// <returns></returns>
        internal NewRecordOp CreateNewRecordOp(TypeUsage type,
            List<EdmProperty> fields)
        {
            return new NewRecordOp(type, fields);
        }
 
        /// <summary>
        /// Creates a new VarRefOp
        /// </summary>
        /// <param name="v">The variable to reference</param>
        /// <returns>A new VarRefOp that references the specified variable</returns>
        internal VarRefOp CreateVarRefOp(Var v)
        {
            return new VarRefOp(v);
        }
        /// <summary>
        /// Creates a new ArithmeticOp of the specified type
        /// </summary>
        /// <param name="opType">An OpType that specifies one of the valid arithmetic operations: Plus, Minus, Multiply, Divide, Modulo, UnaryMinus</param>
        /// <param name="type">Type metadata that specifies the result type of the arithmetic operation</param>
        /// <returns>A new ArithmeticOp of the specified arithmetic OpType</returns>
        internal ArithmeticOp CreateArithmeticOp(OpType opType, TypeUsage type)
        {
            return new ArithmeticOp(opType, type);
        }
 
        /// <summary>
        /// Creates a new PropertyOp
        /// </summary>
        /// <param name="prop">EdmProperty metadata that specifies the property</param>
        /// <returns>A new PropertyOp that references the specified property metadata</returns>
        internal PropertyOp CreatePropertyOp(EdmMember prop)
        {
            //
            // Track all rel-properties
            //
            NavigationProperty navProp = prop as NavigationProperty;
            if (navProp != null)
            {
                RelProperty relProperty = new RelProperty(navProp.RelationshipType, navProp.FromEndMember, navProp.ToEndMember);
                AddRelPropertyReference(relProperty);
                RelProperty inverseRelProperty = new RelProperty(navProp.RelationshipType, navProp.ToEndMember, navProp.FromEndMember);
                AddRelPropertyReference(inverseRelProperty);
            }
 
            // Actually create the propertyOp
            return new PropertyOp(Helper.GetModelTypeUsage(prop), prop);
        }
 
        /// <summary>
        /// Create a "relationship" propertyOp
        /// </summary>
        /// <param name="prop">the relationship property</param>
        /// <returns>a RelPropertyOp</returns>
        internal RelPropertyOp CreateRelPropertyOp(RelProperty prop)
        {
            AddRelPropertyReference(prop);
            return new RelPropertyOp(prop.ToEnd.TypeUsage, prop);
        }
 
        /// <summary>
        /// Creates a new RefOp
        /// </summary>
        /// <param name="entitySet">The EntitySet to which the ref refers</param>
        /// <param name="type">The result type of the RefOp</param>
        /// <returns>A new RefOp that references the specified EntitySet and has the specified result type</returns>
        internal RefOp CreateRefOp(EntitySet entitySet, TypeUsage type)
        {
            return new RefOp(entitySet, type);
        }
 
        /// <summary>
        /// Creates a new ExistsOp
        /// </summary>
        /// <returns>A new ExistsOp</returns>
        internal ExistsOp CreateExistsOp()
        {
            return new ExistsOp(this.BooleanType);
        }
        /// <summary>
        /// Creates a new ElementOp
        /// </summary>
        /// <param name="type">Type metadata that specifies the result (element) type</param>
        /// <returns>A new ElementOp with the specified result type</returns>
        internal ElementOp CreateElementOp(TypeUsage type)
        {
            return new ElementOp(type);
        }
 
        /// <summary>
        /// Creates a new GetEntityRefOp: a ref-extractor (from an entity instance) Op
        /// </summary>
        /// <param name="type">Type metadata that specifies the result type</param>
        /// <returns>A new GetEntityKeyOp with the specified result type</returns>
        internal GetEntityRefOp CreateGetEntityRefOp(TypeUsage type)
        {
            return new GetEntityRefOp(type);
        }
        /// <summary>
        /// Creates a new GetRefKeyOp: a key-extractor (from a ref instance) Op
        /// </summary>
        /// <param name="type">Type metadata that specifies the result type</param>
        /// <returns>A new GetRefKeyOp with the specified result type</returns>
        internal GetRefKeyOp CreateGetRefKeyOp(TypeUsage type)
        {
            return new GetRefKeyOp(type);
        }
 
        /// <summary>
        /// Creates a new CollectOp
        /// </summary>
        /// <param name="type">Type metadata that specifies the result type of the Nest operation</param>
        /// <returns>A new NestOp with the specified result type</returns>
        internal CollectOp CreateCollectOp(TypeUsage type)
        {
            return new CollectOp(type);
        }
 
        /// <summary>
        /// Create a DerefOp
        /// </summary>
        /// <param name="type">Entity type of the target entity</param>
        /// <returns>a DerefOp</returns>
        internal DerefOp CreateDerefOp(TypeUsage type)
        {
            return new DerefOp(type);
        }
 
        /// <summary>
        /// Create a new NavigateOp node
        /// </summary>
        /// <param name="type">the output type of the navigateOp</param>
        /// <param name="relProperty">the relationship property</param>
        /// <returns>the navigateOp</returns>
        internal NavigateOp CreateNavigateOp(TypeUsage type, RelProperty relProperty)
        {
            // keep track of rel-properties
            AddRelPropertyReference(relProperty);
            return new NavigateOp(type, relProperty);
        }
 
        #endregion
 
        #region AncillaryOps
 
        /// <summary>
        /// Creates a VarDefListOp
        /// </summary>
        /// <returns>A new VarDefListOp</returns>
        internal VarDefListOp CreateVarDefListOp()
        {
            return VarDefListOp.Instance;
        }
        /// <summary>
        /// Creates a VarDefOp (for a computed var)
        /// </summary>
        /// <param name="v">The computed var</param>
        /// <returns>A new VarDefOp that references the computed var</returns>
        internal VarDefOp CreateVarDefOp(Var v)
        {
            return new VarDefOp(v);
        }
 
        /// <summary>
        /// Create a VarDefOp and the associated node for an expression.
        /// We create a computedVar first - of the same type as the expression, and
        /// then create a VarDefOp for the computed Var. Finally, we create a Node for
        /// the VarDefOp
        /// </summary>
        /// <param name="definingExpr"></param>
        /// <param name="computedVar">new Var produced</param>
        /// <returns></returns>
        internal Node CreateVarDefNode(Node definingExpr, out Var computedVar)
        {
            Debug.Assert(definingExpr.Op != null);
            ScalarOp scalarOp = definingExpr.Op as ScalarOp;
            Debug.Assert(scalarOp != null);
            computedVar = this.CreateComputedVar(scalarOp.Type);
            VarDefOp varDefOp = this.CreateVarDefOp(computedVar);
            Node varDefNode = this.CreateNode(varDefOp, definingExpr);
            return varDefNode;
        }
 
        /// <summary>
        /// Creates a VarDefListOp with a single child - a VarDefOp created as in the function
        /// above.
        /// </summary>
        /// <param name="definingExpr"></param>
        /// <param name="computedVar">the computed Var produced</param>
        /// <returns></returns>
        internal Node CreateVarDefListNode(Node definingExpr, out Var computedVar)
        {
            Node varDefNode = this.CreateVarDefNode(definingExpr, out computedVar);
            VarDefListOp op = this.CreateVarDefListOp();
            Node varDefListNode = this.CreateNode(op, varDefNode);
            return varDefListNode;
        }
        #endregion
 
        #region RelOps
 
        /// <summary>
        /// Creates a new ScanTableOp
        /// </summary>
        /// <param name="tableMetadata">A Table metadata instance that specifies the table that should be scanned</param>
        /// <returns>A new ScanTableOp that references a new Table instance based on the specified table metadata</returns>
        internal ScanTableOp CreateScanTableOp(TableMD tableMetadata)
        {
            Table table = this.CreateTableInstance(tableMetadata);
            return CreateScanTableOp(table);
        }
        /// <summary>
        /// A variant of the above
        /// </summary>
        /// <param name="table">The table instance</param>
        /// <returns>a new ScanTableOp</returns>
        internal ScanTableOp CreateScanTableOp(Table table)
        {
            return new ScanTableOp(table);
        }
 
        /// <summary>
        /// Creates an instance of a ScanViewOp
        /// </summary>
        /// <param name="table">the table instance</param>
        /// <returns>a new ScanViewOp</returns>
        internal ScanViewOp CreateScanViewOp(Table table)
        {
            return new ScanViewOp(table);
        }
        /// <summary>
        /// Creates an instance of a ScanViewOp
        /// </summary>
        /// <param name="tableMetadata">the table metadata</param>
        /// <returns>a new ScanViewOp</returns>
        internal ScanViewOp CreateScanViewOp(TableMD tableMetadata)
        {
            Table table = this.CreateTableInstance(tableMetadata);
            return this.CreateScanViewOp(table);
        }
        /// <summary>
        /// Creates a new UnnestOp, which creates a streaming result from a scalar (non-RelOp) value
        /// </summary>
        /// <param name="v">The Var that indicates the value to unnest</param>
        /// <returns>A new UnnestOp that targets the specified Var</returns>
        internal UnnestOp CreateUnnestOp(Var v)
        {
            Table t = this.CreateTableInstance(Command.CreateTableDefinition(TypeHelpers.GetEdmType<CollectionType>(v.Type).TypeUsage));
            return CreateUnnestOp(v, t);
        }
 
        /// <summary>
        /// Creates a new UnnestOp - a variant of the above with the Table supplied
        /// </summary>
        /// <param name="v">the unnest Var</param>
        /// <param name="t">the table instance</param>
        /// <returns>a new UnnestOp</returns>
        internal UnnestOp CreateUnnestOp(Var v, Table t)
        {
            return new UnnestOp(v, t);
        }
 
        /// <summary>
        /// Creates a new FilterOp
        /// </summary>
        /// <returns>A new FilterOp</returns>
        internal FilterOp CreateFilterOp()
        {
            return FilterOp.Instance;
        }
 
        /// <summary>
        /// Creates a new ProjectOp
        /// </summary>
        /// <param name="vars">A VarSet that specifies the Vars produced by the projection</param>
        /// <returns>A new ProjectOp with the specified output VarSet</returns>
        internal ProjectOp CreateProjectOp(VarVec vars)
        {
            return new ProjectOp(vars);
        }
        /// <summary>
        /// A variant of the above where the ProjectOp produces exactly one var
        /// </summary>
        /// <param name="v"></param>
        /// <returns></returns>
        internal ProjectOp CreateProjectOp(Var v)
        {
            VarVec varSet = this.CreateVarVec();
            varSet.Set(v);
            return new ProjectOp(varSet);
        }
 
        #region JoinOps
 
        /// <summary>
        /// Creates a new InnerJoinOp
        /// </summary>
        /// <returns>A new InnerJoinOp</returns>
        internal InnerJoinOp CreateInnerJoinOp()
        {
            return InnerJoinOp.Instance;
        }
 
        /// <summary>
        /// Creates a new LeftOuterJoinOp
        /// </summary>
        /// <returns>A new LeftOuterJoinOp</returns>
        internal LeftOuterJoinOp CreateLeftOuterJoinOp()
        {
            return LeftOuterJoinOp.Instance;
        }
 
        /// <summary>
        /// Creates a new FullOuterJoinOp
        /// </summary>
        /// <returns>A new FullOuterJoinOp</returns>
        internal FullOuterJoinOp CreateFullOuterJoinOp()
        {
            return FullOuterJoinOp.Instance;
        }
 
        /// <summary>
        /// Creates a new CrossJoinOp
        /// </summary>
        /// <returns>A new CrossJoinOp</returns>
        internal CrossJoinOp CreateCrossJoinOp()
        {
            return CrossJoinOp.Instance;
        }
 
        #endregion
 
        #region ApplyOps
 
        /// <summary>
        /// Creates a new CrossApplyOp
        /// </summary>
        /// <returns>A new CrossApplyOp</returns>
        internal CrossApplyOp CreateCrossApplyOp()
        {
            return CrossApplyOp.Instance;
        }
        /// <summary>
        /// Creates a new OuterApplyOp
        /// </summary>
        /// <returns>A new OuterApplyOp</returns>
        internal OuterApplyOp CreateOuterApplyOp()
        {
            return OuterApplyOp.Instance;
        }
 
        #endregion
 
        #region SortKeys
 
        /// <summary>
        /// Creates a new SortKey with the specified var, order and collation
        /// </summary>
        /// <param name="v">The variable to sort on</param>
        /// <param name="asc">The sort order (true for ascending, false for descending)</param>
        /// <param name="collation">The sort collation</param>
        /// <returns>A new SortKey with the specified var, order and collation</returns>
        internal static SortKey CreateSortKey(Var v, bool asc, string collation)
        {
            return new SortKey(v, asc, collation);
        }
        /// <summary>
        /// Creates a new SortKey with the specified var and order
        /// </summary>
        /// <param name="v">The variable to sort on</param>
        /// <param name="asc">The sort order (true for ascending, false for descending)</param>
        /// <returns>A new SortKey with the specified var and order</returns>
        internal static SortKey CreateSortKey(Var v, bool asc)
        {
            return new SortKey(v, asc, "");
        }
 
        /// <summary>
        /// Creates a new SortKey with the specified var
        /// </summary>
        /// <param name="v">The variable to sort on</param>
        /// <returns>A new SortKey with the specified var</returns>
        internal static SortKey CreateSortKey(Var v)
        {
            return new SortKey(v, true, "");
        }
 
        #endregion
 
        /// <summary>
        /// Creates a new SortOp
        /// </summary>
        /// <param name="sortKeys">The list of SortKeys that define the sort var, order and collation for each sort key</param>
        /// <returns>A new SortOp with the specified sort keys</returns>
        internal SortOp CreateSortOp(List<SortKey> sortKeys)
        {
            return new SortOp(sortKeys);
        }
 
        /// <summary>
        /// Creates a new ConstrainedSortOp
        /// </summary>
        /// <param name="sortKeys">The list of SortKeys that define the sort var, order and collation for each sort key</param>
        /// <returns>A new ConstrainedSortOp with the specified sort keys and a default WithTies value of false</returns>
        internal ConstrainedSortOp CreateConstrainedSortOp(List<SortKey> sortKeys)
        {
            return new ConstrainedSortOp(sortKeys, false);
        }
 
        /// <summary>
        /// Creates a new ConstrainedSortOp
        /// </summary>
        /// <param name="sortKeys">The list of SortKeys that define the sort var, order and collation for each sort key</param>
        /// <param name="withTies">The value to use for the WithTies property of the new ConstrainedSortOp</param>
        /// <returns>A new ConstrainedSortOp with the specified sort keys and WithTies value</returns>
        internal ConstrainedSortOp CreateConstrainedSortOp(List<SortKey> sortKeys, bool withTies)
        {
            return new ConstrainedSortOp(sortKeys, withTies);
        }
 
        /// <summary>
        /// Creates a new GroupByOp
        /// </summary>
        /// <param name="gbyKeys">A VarSet that specifies the Key variables produced by the GroupByOp</param>
        /// <param name="outputs">A VarSet that specifies all (Key and Aggregate) variables produced by the GroupByOp</param>
        /// <returns>A new GroupByOp with the specified key and output VarSets</returns>
        internal GroupByOp CreateGroupByOp(VarVec gbyKeys, VarVec outputs)
        {
            return new GroupByOp(gbyKeys, outputs);
        }
 
        /// <summary>
        /// Creates a new GroupByIntoOp
        /// </summary>
        /// <param name="gbyKeys">A VarSet that specifies the Key variables produced by the GroupByOp</param>
        /// <param name="outputs">A VarSet that specifies the vars from the input that represent the real grouping input</param>
        /// <param name="inputs">A VarSet that specifies all (Key and Aggregate) variables produced by the GroupByOp</param>
        /// <returns>A new GroupByOp with the specified key and output VarSets</returns>
        internal GroupByIntoOp CreateGroupByIntoOp(VarVec gbyKeys, VarVec inputs, VarVec outputs)
        {
            return new GroupByIntoOp(gbyKeys, inputs, outputs);
        }
 
        /// <summary>
        /// Creates a new DistinctOp
        /// <param name="keyVars">list of key vars</param>
        /// </summary>
        /// <returns>A new DistinctOp</returns>
        internal DistinctOp CreateDistinctOp(VarVec keyVars)
        {
            return new DistinctOp(keyVars);
        }
        /// <summary>
        /// An overload of the above - where the distinct has exactly one key
        /// </summary>
        /// <param name="keyVar"></param>
        /// <returns></returns>
        internal DistinctOp CreateDistinctOp(Var keyVar)
        {
            return new DistinctOp(this.CreateVarVec(keyVar));
        }
        
        /// <summary>
        /// Creates a new UnionAllOp
        /// </summary>
        /// <param name="leftMap">Mappings from the Output Vars to the Vars produced by the left argument</param>
        /// <param name="rightMap">Mappings from the Output Vars to the Vars produced by the right argument</param>
        /// <returns>A UnionAllOp that references the specified left and right Vars</returns>
        internal UnionAllOp CreateUnionAllOp(VarMap leftMap, VarMap rightMap)
        {
            return CreateUnionAllOp(leftMap, rightMap, null);
        }
 
        /// <summary>
        /// Creates a new UnionAllOp, with a branch descriminator.
        /// </summary>
        /// <param name="leftMap">Mappings from the Output Vars to the Vars produced by the left argument</param>
        /// <param name="rightMap">Mappings from the Output Vars to the Vars produced by the right argument</param>
        /// <param name="branchDiscriminator">Var that contains the branch discrimination value (may be null until key pullup occurs)</param>
        /// <returns>A UnionAllOp that references the specified left and right Vars</returns>
        internal UnionAllOp CreateUnionAllOp(VarMap leftMap, VarMap rightMap, Var branchDiscriminator) 
        {
            Debug.Assert(leftMap.Count == rightMap.Count, "VarMap count mismatch");
            VarVec vec = this.CreateVarVec();
            foreach (Var v in leftMap.Keys)
            {
                vec.Set(v);
            }
            return new UnionAllOp(vec, leftMap, rightMap, branchDiscriminator);
        }
 
        /// <summary>
        /// Creates a new IntersectOp
        /// </summary>
        /// <param name="leftMap">Mappings from the Output Vars to the Vars produced by the left argument</param>
        /// <param name="rightMap">Mappings from the Output Vars to the Vars produced by the right argument</param>
        /// <returns>An IntersectOp that references the specified left and right Vars</returns>
        internal IntersectOp CreateIntersectOp(VarMap leftMap, VarMap rightMap)
        {
            Debug.Assert(leftMap.Count == rightMap.Count, "VarMap count mismatch");
            VarVec vec = this.CreateVarVec();
            foreach (Var v in leftMap.Keys)
            {
                vec.Set(v);
            }
            return new IntersectOp(vec, leftMap, rightMap);
        }
        /// <summary>
        /// Creates a new ExceptOp
        /// </summary>
        /// <param name="leftMap">Mappings from the Output Vars to the Vars produced by the left argument</param>
        /// <param name="rightMap">Mappings from the Output Vars to the Vars produced by the right argument</param>
        /// <returns>An ExceptOp that references the specified left and right Vars</returns>
        internal ExceptOp CreateExceptOp(VarMap leftMap, VarMap rightMap)
        {
            Debug.Assert(leftMap.Count == rightMap.Count, "VarMap count mismatch");
            VarVec vec = this.CreateVarVec();
            foreach (Var v in leftMap.Keys)
            {
                vec.Set(v);
            }
            return new ExceptOp(vec, leftMap, rightMap);
        }
 
        /// <summary>
        /// Create a single-row-op (the relop analog of Element)
        /// </summary>
        /// <returns></returns>
        internal SingleRowOp CreateSingleRowOp()
        {
            return SingleRowOp.Instance;
        }
 
        /// <summary>
        /// Create a SingleRowTableOp - a table with exactly one row (and no columns)
        /// </summary>
        /// <returns></returns>
        internal SingleRowTableOp CreateSingleRowTableOp()
        {
            return SingleRowTableOp.Instance;
        }
 
        #endregion
 
        #region PhysicalOps
        /// <summary>
        /// Create a PhysicalProjectOp - with a columnMap describing the output
        /// </summary>
        /// <param name="outputVars">list of output vars</param>
        /// <param name="columnMap">columnmap describing the output element</param>
        /// <returns></returns>
        internal PhysicalProjectOp CreatePhysicalProjectOp(VarList outputVars, SimpleCollectionColumnMap columnMap)
        {
            return new PhysicalProjectOp(outputVars, columnMap);
        }
        /// <summary>
        /// Create a physicalProjectOp - with a single column output
        /// </summary>
        /// <param name="outputVar">the output element</param>
        /// <returns></returns>
        internal PhysicalProjectOp CreatePhysicalProjectOp(Var outputVar)
        {
            VarList varList = Command.CreateVarList();
            varList.Add(outputVar);
            VarRefColumnMap varRefColumnMap = new VarRefColumnMap(outputVar);
 
            SimpleCollectionColumnMap collectionColumnMap = new SimpleCollectionColumnMap(
                TypeUtils.CreateCollectionType(varRefColumnMap.Type),   // type
                null,                                                   // name
                varRefColumnMap,                                        // element map
                new SimpleColumnMap[0],                                 // keys
                new SimpleColumnMap[0]);                                // foreign keys
            return CreatePhysicalProjectOp(varList, collectionColumnMap);
        }
 
        /// <summary>
        /// Another overload - with an additional discriminatorValue.
        /// Should this be a subtype instead?
        /// </summary>
        /// <param name="collectionVar">the collectionVar</param>
        /// <param name="columnMap">column map for the collection element</param>
        /// <param name="flattenedElementVars">elementVars with any nested collections pulled up</param>
        /// <param name="keys">keys specific to this collection</param>
        /// <param name="sortKeys">sort keys specific to this collecion</param>
        /// <param name="discriminatorValue">discriminator value for this collection (under the current nestOp)</param>
        /// <returns>a new CollectionInfo instance</returns>
        internal static CollectionInfo CreateCollectionInfo(Var collectionVar, ColumnMap columnMap, VarList flattenedElementVars, VarVec keys, List<InternalTrees.SortKey> sortKeys, object discriminatorValue)
        {
            return new CollectionInfo(collectionVar, columnMap, flattenedElementVars, keys, sortKeys, discriminatorValue);
        }
 
        /// <summary>
        /// Create a singleStreamNestOp
        /// </summary>
        /// <param name="keys">keys for the nest operation</param>
        /// <param name="prefixSortKeys">list of prefix sort keys</param>
        /// <param name="postfixSortKeys">list of postfix sort keys</param>
        /// <param name="outputVars">List of outputVars</param>
        /// <param name="collectionInfoList">CollectionInfo for each collection </param>
        /// <param name="discriminatorVar">Var describing the discriminator</param>
        /// <returns></returns>
        internal SingleStreamNestOp CreateSingleStreamNestOp(VarVec keys,
            List<SortKey> prefixSortKeys, List<SortKey> postfixSortKeys,
            VarVec outputVars,
            List<CollectionInfo> collectionInfoList, Var discriminatorVar)
        {
            return new SingleStreamNestOp(keys, prefixSortKeys, postfixSortKeys, outputVars, collectionInfoList, discriminatorVar);
        }
 
        /// <summary>
        /// Create a MultiStreamNestOp
        /// </summary>
        /// <param name="prefixSortKeys">list of prefix sort keys</param>
        /// <param name="outputVars">List of outputVars</param>
        /// <param name="collectionInfoList">CollectionInfo for each collection element</param>
        /// <returns></returns>
        internal MultiStreamNestOp CreateMultiStreamNestOp(List<SortKey> prefixSortKeys, VarVec outputVars,
            List<CollectionInfo> collectionInfoList)
        {
            return new MultiStreamNestOp(prefixSortKeys, outputVars, collectionInfoList);
        }
        #endregion
 
        #region NodeInfo
        /// <summary>
        /// Get auxilliary information for a Node
        /// </summary>
        /// <param name="n">the node</param>
        /// <returns>node info for this node</returns>
        internal NodeInfo GetNodeInfo(Node n)
        {
            return n.GetNodeInfo(this);
        }
 
        /// <summary>
        /// Get extended node information for a RelOpNode
        /// </summary>
        /// <param name="n">the node</param>
        /// <returns>extended node info for this node</returns>
        internal ExtendedNodeInfo GetExtendedNodeInfo(Node n)
        {
            return n.GetExtendedNodeInfo(this);
        }
        /// <summary>
        /// Recompute the nodeinfo for a node, but only if has already been computed
        /// </summary>
        /// <param name="n">Node in question</param>
        internal void RecomputeNodeInfo(Node n)
        {
            m_nodeInfoVisitor.RecomputeNodeInfo(n);
        }
        #endregion
 
        #region KeyInfo
        /// <summary>
        /// Pulls up keys if necessary and gets the key information for a Node
        /// </summary>
        /// <param name="n">node</param>
        /// <returns>key information</returns>
        internal KeyVec PullupKeys(Node n)
        {
            return m_keyPullupVisitor.GetKeys(n);
        }
        #endregion
 
        #region Type Comparisons
        //
        // The functions described in this region are used through out the 
        // PlanCompiler to reason about type equality. Make sure that you 
        // use these and these alone
        //
 
        /// <summary>
        /// Check to see if two types are considered "equal" for the purposes
        /// of the plan compiler. 
        /// Two types are considered to be equal if their "identities" are equal.
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns>true, if the types are "equal"</returns>
        internal static bool EqualTypes(TypeUsage x, TypeUsage y)
        {
            return PlanCompiler.TypeUsageEqualityComparer.Instance.Equals(x, y);
        }
        /// <summary>
        /// Check to see if two types are considered "equal" for the purposes
        /// of the plan compiler
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns>true, if the types are "equal"</returns>
        internal static bool EqualTypes(EdmType x, EdmType y)
        {
            return PlanCompiler.TypeUsageEqualityComparer.Equals(x, y);
        }
        #endregion
 
        #region Builder Methods
        /// <summary>
        /// Builds out a UNION-ALL ladder from a sequence of node,var pairs.
        /// Assumption: Each node produces exactly one Var
        /// 
        /// If the input sequence has zero elements, we return null
        /// If the input sequence has one element, we return that single element
        /// Otherwise, we build out a UnionAll ladder from each of the inputs. If the input sequence was {A,B,C,D},
        /// we build up a union-all ladder that looks like 
        ///     (((A UA B) UA C) UA D)
        /// </summary>
        /// <param name="inputNodes">list of input nodes - one for each branch</param>
        /// <param name="inputVars">list of input vars - N for each branch</param>
        /// <param name="resultNode">the resulting union-all subtree</param>
        /// <param name="resultVar">the output vars from the union-all subtree</param>
        internal void BuildUnionAllLadder(
            IList<Node> inputNodes, IList<Var> inputVars,
            out Node resultNode, out IList<Var> resultVars)
        {
            if (inputNodes.Count == 0)
            {
                resultNode = null;
                resultVars = null;
                return;
            }
 
            int varPerNode = inputVars.Count / inputNodes.Count;
            Debug.Assert((inputVars.Count % inputNodes.Count == 0) && (varPerNode >= 1), "Inconsistent nodes/vars count:" + inputNodes.Count + "," + inputVars.Count);
 
            if (inputNodes.Count == 1)
            {
                resultNode = inputNodes[0];
                resultVars = inputVars;
                return;
            }
 
            List<Var> unionAllVars = new List<Var>();
 
            Node unionAllNode = inputNodes[0];
            for (int j = 0; j < varPerNode; j++)
            {
                unionAllVars.Add(inputVars[j]);
            }
 
            for (int i = 1; i < inputNodes.Count; i++)
            {
                VarMap leftVarMap = this.CreateVarMap();
                VarMap rightVarMap = this.CreateVarMap();
                List<Var> setOpVars = new List<Var>();
                for (int j = 0; j < varPerNode; j++)
                {
                    SetOpVar newVar = this.CreateSetOpVar(unionAllVars[j].Type);
                    setOpVars.Add(newVar);
                    leftVarMap.Add(newVar, unionAllVars[j]);
                    rightVarMap.Add(newVar, inputVars[i * varPerNode + j]);
                }
                Op unionAllOp = this.CreateUnionAllOp(leftVarMap, rightVarMap);
                unionAllNode = this.CreateNode(unionAllOp, unionAllNode, inputNodes[i]);
                unionAllVars = setOpVars;
            }
 
            resultNode = unionAllNode;
            resultVars = unionAllVars;
        }
 
        /// <summary>
        /// A simplified version of the method above - each branch can produce only one var
        /// </summary>
        /// <param name="inputNodes"></param>
        /// <param name="inputVars"></param>
        /// <param name="resultNode"></param>
        /// <param name="resultVar"></param>
        internal void BuildUnionAllLadder(IList<Node> inputNodes, IList<Var> inputVars,
            out Node resultNode, out Var resultVar)
        {
            Debug.Assert(inputNodes.Count == inputVars.Count, "Count mismatch:" + inputNodes.Count + "," + inputVars.Count);
            IList<Var> varList;
            BuildUnionAllLadder(inputNodes, inputVars, out resultNode, out varList);
            if (varList != null && varList.Count > 0)
            {
                resultVar = varList[0];
            }
            else
            {
                resultVar = null;
            }
        }
 
        /// <summary>
        /// Build a projectOp tree over the input. 
        /// This function builds a projectOp tree over the input. The Outputs (vars) of the project are the 
        /// list of vars from the input (inputVars), plus one computed Var for each of the computed expressions
        /// (computedExpressions)
        /// </summary>
        /// <param name="inputNode">the input relop to the project</param>
        /// <param name="inputVars">List of vars from the input that need to be projected</param>
        /// <param name="computedExpressions">list (possibly empty) of any computed expressions</param>
        /// <returns></returns>
        internal Node BuildProject(Node inputNode, IEnumerable<Var> inputVars,
            IEnumerable<Node> computedExpressions)
        {
            Debug.Assert(inputNode.Op.IsRelOp, "Expected a RelOp. Found " + inputNode.Op.OpType);
 
            VarDefListOp varDefListOp = this.CreateVarDefListOp();
            Node varDefListNode = this.CreateNode(varDefListOp);
            VarVec projectVars = this.CreateVarVec(inputVars);
            foreach (Node expr in computedExpressions)
            {
                Var v = this.CreateComputedVar(expr.Op.Type);
                projectVars.Set(v);
                VarDefOp varDefOp = this.CreateVarDefOp(v);
                Node varDefNode = this.CreateNode(varDefOp, expr);
                varDefListNode.Children.Add(varDefNode);
            }
            Node projectNode = this.CreateNode(
                this.CreateProjectOp(projectVars),
                inputNode,
                varDefListNode);
            return projectNode;
        }
 
        /// <summary>
        /// A "simpler" builder method for ProjectOp. The assumption is that the only output is the
        /// (var corresponding to) the computedExpression. None of the Vars of the "input" are projected out
        /// 
        /// The single output Var is returned in the "outputVar" parameter
        /// </summary>
        /// <param name="input">the input relop</param>
        /// <param name="computedExpression">the computed expression</param>
        /// <param name="projectVar">(output) the computed var corresponding to the computed expression</param>
        /// <returns>the new project subtree node</returns>
        internal Node BuildProject(Node input, Node computedExpression, out Var projectVar)
        {
            Node projectNode = BuildProject(input, new Var[] { }, new Node[] { computedExpression });
            projectVar = ((ProjectOp)projectNode.Op).Outputs.First;
            return projectNode;
        }
 
        /// <summary>
        /// Build the equivalent of an OfTypeExpression over the input (ie) produce the set of values from the
        /// input that are of the desired type (exactly of the desired type, if the "includeSubtypes" parameter is false).
        /// 
        /// Further more, "update" the result element type to be the desired type.
        /// 
        /// We accomplish this by first building a FilterOp with an IsOf (or an IsOfOnly) predicate for the desired 
        /// type. We then build out a ProjectOp over the FilterOp, where we introduce a "Fake" TreatOp over the input
        /// element to cast it to the right type. The "Fake" TreatOp is only there for "compile-time" typing reasons,
        /// and will be ignored in the rest of the plan compiler
        /// </summary>
        /// <param name="inputNode">the input collection</param>
        /// <param name="inputVar">the single Var produced by the input collection</param>
        /// <param name="desiredType">the desired element type </param>
        /// <param name="includeSubtypes">do we include subtypes of the desired element type</param>
        /// <param name="resultNode">the result subtree</param>
        /// <param name="resultVar">the single Var produced by the result subtree</param>
        internal void BuildOfTypeTree(Node inputNode, Var inputVar, TypeUsage desiredType, bool includeSubtypes,
            out Node resultNode, out Var resultVar)
        {
            Op isOfOp = includeSubtypes ? this.CreateIsOfOp(desiredType) : this.CreateIsOfOnlyOp(desiredType);
            Node predicate = this.CreateNode(isOfOp, this.CreateNode(this.CreateVarRefOp(inputVar)));
            Node filterNode = this.CreateNode(this.CreateFilterOp(), inputNode, predicate);
 
            resultNode = BuildFakeTreatProject(filterNode, inputVar, desiredType, out resultVar);
        }
 
        /// Builds out a ProjectOp over the input that introduces a "Fake" TreatOp over the input Var to cast it to the desired type
        /// The "Fake" TreatOp is only there for "compile-time" typing reasons, and will be ignored in the rest of the plan compiler.
        /// </summary>
        /// <param name="inputNode">the input collection</param>
        /// <param name="inputVar">the single Var produced by the input collection</param>
        /// <param name="desiredType">the desired element type </param>
        /// <param name="resultVar">the single Var produced by the result subtree</param>
        /// <returns>the result subtree</returns>
        internal Node BuildFakeTreatProject(Node inputNode, Var inputVar, TypeUsage desiredType, out Var resultVar)
        {
            Node treatNode = this.CreateNode(this.CreateFakeTreatOp(desiredType), 
                this.CreateNode(this.CreateVarRefOp(inputVar)));
            Node resultNode = this.BuildProject(inputNode, treatNode, out resultVar);
            return resultNode;
        }
 
        /// <summary>
        /// Build a comparisonOp over the input arguments. Build SoftCasts over the inputs, if we need
        /// to.
        /// </summary>
        /// <param name="opType">the comparison optype</param>
        /// <param name="arg0">Arg 0</param>
        /// <param name="arg1">Arg 1</param>
        /// <returns>the resulting comparison tree</returns>
        internal Node BuildComparison(OpType opType, Node arg0, Node arg1)
        {
            if (!Command.EqualTypes(arg0.Op.Type, arg1.Op.Type))
            {
                TypeUsage commonType = TypeHelpers.GetCommonTypeUsage(arg0.Op.Type, arg1.Op.Type);
                Debug.Assert(commonType != null, "No common type for " + arg0.Op.Type + " and " + arg1.Op.Type);
                if (!EqualTypes(commonType, arg0.Op.Type))
                {
                    arg0 = this.CreateNode(this.CreateSoftCastOp(commonType), arg0);
                }
                if (!EqualTypes(commonType, arg1.Op.Type))
                {
                    arg1 = this.CreateNode(this.CreateSoftCastOp(commonType), arg1);
                }
            }
            Node newNode = this.CreateNode(this.CreateComparisonOp(opType), arg0, arg1);
            return newNode;
        }
 
        /// <summary>
        /// Build up a CollectOp over a relop tree
        /// </summary>
        /// <param name="relOpNode">the relop tree</param>
        /// <param name="relOpVar">the single output var from the relop tree</param>
        /// <returns></returns>
        internal Node BuildCollect(Node relOpNode, Var relOpVar)
        {
            Node physicalProjectNode = this.CreateNode(this.CreatePhysicalProjectOp(relOpVar), relOpNode);
            TypeUsage collectOpType = TypeHelpers.CreateCollectionTypeUsage(relOpVar.Type);
            Node collectNode = this.CreateNode(this.CreateCollectOp(collectOpType), physicalProjectNode);
            return collectNode;
        }
        #endregion
 
        #region Rel Properties
        /// <summary>
        /// Mark this rel-property as "referenced" in the current query, if the target
        /// end has multiplicity of one (or zero_or_one)
        /// </summary>
        /// <param name="relProperty">the rel-property</param>
        private void AddRelPropertyReference(RelProperty relProperty)
        {
            if (relProperty.ToEnd.RelationshipMultiplicity != RelationshipMultiplicity.Many &&
                !m_referencedRelProperties.Contains(relProperty))
            {
                m_referencedRelProperties.Add(relProperty);
            }
        }
 
        /// <summary>
        /// The set of referenced rel properties in the current query
        /// </summary>
        internal HashSet<RelProperty> ReferencedRelProperties
        {
            get { return m_referencedRelProperties; }
        }
 
        /// <summary>
        /// Is this rel-property referenced in the query so far
        /// </summary>
        /// <param name="relProperty">the rel-property</param>
        /// <returns>true, if the rel property was referenced in the query</returns>
        internal bool IsRelPropertyReferenced(RelProperty relProperty)
        {
            bool ret = m_referencedRelProperties.Contains(relProperty);
            return ret;
        }
        #endregion
 
        #endregion
    }
 
}