e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
1845 lines
75 KiB
C#
1845 lines
75 KiB
C#
//---------------------------------------------------------------------
|
|
// <copyright file="Command.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//
|
|
// @owner [....]
|
|
// @backupOwner [....]
|
|
//---------------------------------------------------------------------
|
|
|
|
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
|
|
}
|
|
|
|
}
|