You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@@ -0,0 +1,470 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="BoolExpression.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common.CommandTrees;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Data.Common.Utils.Boolean;
|
||||
using System.Data.Entity;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.Structures
|
||||
{
|
||||
using BoolDomainConstraint = DomainConstraint<BoolLiteral, Constant>;
|
||||
using DomainAndExpr = AndExpr<DomainConstraint<BoolLiteral, Constant>>;
|
||||
using DomainBoolExpr = BoolExpr<DomainConstraint<BoolLiteral, Constant>>;
|
||||
using DomainFalseExpr = FalseExpr<DomainConstraint<BoolLiteral, Constant>>;
|
||||
using DomainNotExpr = NotExpr<DomainConstraint<BoolLiteral, Constant>>;
|
||||
using DomainOrExpr = OrExpr<DomainConstraint<BoolLiteral, Constant>>;
|
||||
using DomainTermExpr = TermExpr<DomainConstraint<BoolLiteral, Constant>>;
|
||||
using DomainTrueExpr = TrueExpr<DomainConstraint<BoolLiteral, Constant>>;
|
||||
|
||||
|
||||
// This class represents an arbitrary boolean expression
|
||||
internal partial class BoolExpression : InternalBase
|
||||
{
|
||||
|
||||
#region Constructors
|
||||
// effects: Create a boolean expression from a literal value
|
||||
internal static BoolExpression CreateLiteral(BoolLiteral literal, MemberDomainMap memberDomainMap)
|
||||
{
|
||||
DomainBoolExpr expr = literal.GetDomainBoolExpression(memberDomainMap);
|
||||
return new BoolExpression(expr, memberDomainMap);
|
||||
}
|
||||
|
||||
// effects: Creates a new boolean expression using the memberDomainMap of this expression
|
||||
internal BoolExpression Create(BoolLiteral literal)
|
||||
{
|
||||
DomainBoolExpr expr = literal.GetDomainBoolExpression(m_memberDomainMap);
|
||||
return new BoolExpression(expr, m_memberDomainMap);
|
||||
}
|
||||
|
||||
// effects: Create a boolean expression of the form "NOT expression"
|
||||
internal static BoolExpression CreateNot(BoolExpression expression)
|
||||
{
|
||||
return new BoolExpression(ExprType.Not, new BoolExpression[] { expression });
|
||||
}
|
||||
|
||||
// effects: Create a boolean expression of the form "children[0] AND
|
||||
// children[1] AND ..."
|
||||
internal static BoolExpression CreateAnd(params BoolExpression[] children)
|
||||
{
|
||||
return new BoolExpression(ExprType.And, children);
|
||||
}
|
||||
|
||||
// effects: Create a boolean expression of the form "children[0] OR
|
||||
// children[1] OR ..."
|
||||
internal static BoolExpression CreateOr(params BoolExpression[] children)
|
||||
{
|
||||
return new BoolExpression(ExprType.Or, children);
|
||||
}
|
||||
|
||||
internal static BoolExpression CreateAndNot(BoolExpression e1, BoolExpression e2)
|
||||
{
|
||||
return CreateAnd(e1, CreateNot(e2));
|
||||
}
|
||||
|
||||
// effects: Creates a new boolean expression using the memberDomainMap of this expression
|
||||
internal BoolExpression Create(DomainBoolExpr expression)
|
||||
{
|
||||
return new BoolExpression(expression, m_memberDomainMap);
|
||||
}
|
||||
|
||||
// effects: Creates a boolean expression corresponding to TRUE (if
|
||||
// isTrue is true) or FALSE (if isTrue is false)
|
||||
private BoolExpression(bool isTrue)
|
||||
{
|
||||
if (isTrue)
|
||||
{
|
||||
m_tree = DomainTrueExpr.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tree = DomainFalseExpr.Value;
|
||||
}
|
||||
}
|
||||
|
||||
// effects: Given the operation type (AND/OR/NOT) and the relevant number of
|
||||
// children, returns the corresponding bool expression
|
||||
private BoolExpression(ExprType opType, IEnumerable<BoolExpression> children)
|
||||
{
|
||||
List<BoolExpression> childList = new List<BoolExpression>(children);
|
||||
Debug.Assert(childList.Count > 0);
|
||||
// If any child is other than true or false, it will have m_memberDomainMap set
|
||||
foreach (BoolExpression child in children)
|
||||
{
|
||||
if (child.m_memberDomainMap != null)
|
||||
{
|
||||
m_memberDomainMap = child.m_memberDomainMap;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (opType)
|
||||
{
|
||||
case ExprType.And:
|
||||
m_tree = new DomainAndExpr(ToBoolExprList(childList));
|
||||
break;
|
||||
case ExprType.Or:
|
||||
m_tree = new DomainOrExpr(ToBoolExprList(childList));
|
||||
break;
|
||||
case ExprType.Not:
|
||||
Debug.Assert(childList.Count == 1);
|
||||
m_tree = new DomainNotExpr(childList[0].m_tree);
|
||||
break;
|
||||
default:
|
||||
Debug.Fail("Unknown expression type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// effects: Creates a boolean expression based on expr
|
||||
internal BoolExpression(DomainBoolExpr expr, MemberDomainMap memberDomainMap)
|
||||
{
|
||||
m_tree = expr;
|
||||
m_memberDomainMap = memberDomainMap;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private DomainBoolExpr m_tree; // The actual tree that has the expression
|
||||
// Domain map for various member paths - can be null
|
||||
private readonly MemberDomainMap m_memberDomainMap;
|
||||
private Converter<BoolDomainConstraint> m_converter;
|
||||
|
||||
internal static readonly IEqualityComparer<BoolExpression> EqualityComparer = new BoolComparer();
|
||||
internal static readonly BoolExpression True = new BoolExpression(true);
|
||||
internal static readonly BoolExpression False = new BoolExpression(false);
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
// requires: this is of the form "True", "Literal" or "Literal AND ... AND Literal".
|
||||
// effects: Yields the individual atoms in this (for True does not
|
||||
// yield anything)
|
||||
internal IEnumerable<BoolExpression> Atoms
|
||||
{
|
||||
get
|
||||
{
|
||||
// Create the terms visitor and visit it to get atoms (it
|
||||
// ensures that there are no ANDs or NOTs in the expression)
|
||||
IEnumerable<DomainTermExpr> atoms = TermVisitor.GetTerms(m_tree, false);
|
||||
foreach (DomainTermExpr atom in atoms)
|
||||
{
|
||||
yield return new BoolExpression(atom, m_memberDomainMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// effects: Yields all the leaves in this
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal IEnumerable<BoolLiteral> Leaves
|
||||
{
|
||||
get
|
||||
{
|
||||
// Create the term visitor and visit it to get terms
|
||||
IEnumerable<DomainTermExpr> terms = TermVisitor.GetTerms(m_tree, true);
|
||||
foreach (DomainTermExpr term in terms)
|
||||
{
|
||||
yield return term.Identifier.Variable.Identifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// effects: if this expression is a boolean expression of type BoolLiteral
|
||||
// Returns the literal, else returns null
|
||||
internal BoolLiteral AsLiteral
|
||||
{
|
||||
get
|
||||
{
|
||||
DomainTermExpr literal = m_tree as DomainTermExpr;
|
||||
if (literal == null) { return null; }
|
||||
BoolLiteral result = GetBoolLiteral(literal);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// effects: Given a term expression, extracts the BoolLiteral from it
|
||||
internal static BoolLiteral GetBoolLiteral(DomainTermExpr term)
|
||||
{
|
||||
DomainConstraint<BoolLiteral, Constant> domainConstraint = term.Identifier;
|
||||
DomainVariable<BoolLiteral, Constant> variable = domainConstraint.Variable;
|
||||
return variable.Identifier;
|
||||
}
|
||||
|
||||
// effects: Returns true iff this corresponds to the boolean literal "true"
|
||||
internal bool IsTrue
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_tree.ExprType == ExprType.True;
|
||||
}
|
||||
}
|
||||
|
||||
// effects: Returns true iff this corresponds to the boolean literal "false"
|
||||
internal bool IsFalse
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_tree.ExprType == ExprType.False;
|
||||
}
|
||||
}
|
||||
|
||||
// effects: Returns true if the expression always evaluates to true
|
||||
internal bool IsAlwaysTrue()
|
||||
{
|
||||
InitializeConverter();
|
||||
return m_converter.Vertex.IsOne();
|
||||
}
|
||||
|
||||
// effects: Returns true if there is a possible assignment to
|
||||
// variables in this such that the expression evaluates to true
|
||||
internal bool IsSatisfiable()
|
||||
{
|
||||
return !IsUnsatisfiable();
|
||||
}
|
||||
|
||||
// effects: Returns true if there is no possible assignment to
|
||||
// variables in this such that the expression evaluates to true,
|
||||
// i.e., the expression will always evaluate to false
|
||||
internal bool IsUnsatisfiable()
|
||||
{
|
||||
InitializeConverter();
|
||||
return m_converter.Vertex.IsZero();
|
||||
}
|
||||
|
||||
// effects: Returns the internal tree in this
|
||||
internal DomainBoolExpr Tree
|
||||
{
|
||||
get { return m_tree; }
|
||||
}
|
||||
|
||||
internal IEnumerable<DomainConstraint<BoolLiteral, Constant>> VariableConstraints
|
||||
{
|
||||
get { return LeafVisitor<DomainConstraint<BoolLiteral, Constant>>.GetLeaves(m_tree); }
|
||||
}
|
||||
|
||||
internal IEnumerable<DomainVariable<BoolLiteral, Constant>> Variables
|
||||
{
|
||||
get { return VariableConstraints.Select(domainConstraint => domainConstraint.Variable); }
|
||||
}
|
||||
|
||||
internal IEnumerable<MemberRestriction> MemberRestrictions
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (DomainVariable<BoolLiteral, Constant> var in Variables)
|
||||
{
|
||||
MemberRestriction variableCondition = var.Identifier as MemberRestriction;
|
||||
if (variableCondition != null)
|
||||
{
|
||||
yield return variableCondition;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
// effects: Given a sequence of boolean expressions, yields the
|
||||
// corresponding trees in it in the same order
|
||||
private IEnumerable<DomainBoolExpr> ToBoolExprList(IEnumerable<BoolExpression> nodes)
|
||||
{
|
||||
foreach (BoolExpression node in nodes)
|
||||
{
|
||||
yield return node.m_tree;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Whether the boolean expression contains only OneOFTypeConst variables.
|
||||
/// </summary>
|
||||
internal bool RepresentsAllTypeConditions
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.MemberRestrictions.All(var => (var is TypeRestriction));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal BoolExpression RemapLiterals(Dictionary<BoolLiteral, BoolLiteral> remap)
|
||||
{
|
||||
var rewriter = new BooleanExpressionTermRewriter<BoolDomainConstraint, BoolDomainConstraint>(
|
||||
// term => remap[BoolExpression.GetBoolLiteral(term)].GetDomainBoolExpression(m_memberDomainMap));
|
||||
delegate(DomainTermExpr term)
|
||||
{
|
||||
BoolLiteral newLiteral;
|
||||
return remap.TryGetValue(BoolExpression.GetBoolLiteral(term), out newLiteral) ?
|
||||
newLiteral.GetDomainBoolExpression(m_memberDomainMap) : term;
|
||||
});
|
||||
return new BoolExpression(m_tree.Accept(rewriter), m_memberDomainMap);
|
||||
}
|
||||
|
||||
// effects: Given a boolean expression, modifies requiredSlots
|
||||
// to indicate which slots are required to generate the expression
|
||||
// projectedSlotMap indicates a mapping from member paths to slot
|
||||
// numbers (that need to be checked off in requiredSlots)
|
||||
internal virtual void GetRequiredSlots(MemberProjectionIndex projectedSlotMap, bool[] requiredSlots)
|
||||
{
|
||||
RequiredSlotsVisitor.GetRequiredSlots(m_tree, projectedSlotMap, requiredSlots);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given the <paramref name="blockAlias"/> for the block in which the expression resides, converts the expression into eSQL.
|
||||
/// </summary>
|
||||
internal StringBuilder AsEsql(StringBuilder builder, string blockAlias)
|
||||
{
|
||||
return AsEsqlVisitor.AsEsql(m_tree, builder, blockAlias);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given the <paramref name="row"/> for the input, converts the expression into CQT.
|
||||
/// </summary>
|
||||
internal DbExpression AsCqt(DbExpression row)
|
||||
{
|
||||
return AsCqtVisitor.AsCqt(m_tree, row);
|
||||
}
|
||||
|
||||
internal StringBuilder AsUserString(StringBuilder builder, string blockAlias, bool writeRoundtrippingMessage)
|
||||
{
|
||||
if (writeRoundtrippingMessage)
|
||||
{
|
||||
builder.AppendLine(Strings.Viewgen_ConfigurationErrorMsg(blockAlias));
|
||||
builder.Append(" ");
|
||||
}
|
||||
return AsUserStringVisitor.AsUserString(m_tree, builder, blockAlias);
|
||||
}
|
||||
|
||||
internal override void ToCompactString(StringBuilder builder)
|
||||
{
|
||||
CompactStringVisitor.ToBuilder(m_tree, builder);
|
||||
}
|
||||
|
||||
// effects: Given a mapping from old jointree nodes to new ones,
|
||||
// creates a boolean expression from "this" in which the references
|
||||
// to old join tree nodes are replaced by references to new nodes
|
||||
// from remap (boolean expressions other than constants can contain
|
||||
// references to jointree nodes, e.g., "var in values" -- var is a
|
||||
// reference to a JoinTreeNode
|
||||
internal BoolExpression RemapBool(Dictionary<MemberPath, MemberPath> remap)
|
||||
{
|
||||
DomainBoolExpr expr = RemapBoolVisitor.RemapExtentTreeNodes(m_tree, m_memberDomainMap, remap);
|
||||
return new BoolExpression(expr, m_memberDomainMap);
|
||||
}
|
||||
|
||||
// effects: Given a list of bools, returns a list of boolean expressions where each
|
||||
// boolean in bools has been ANDed with conjunct
|
||||
// CHANGE_[....]_IMPROVE: replace with lambda pattern
|
||||
internal static List<BoolExpression> AddConjunctionToBools(List<BoolExpression> bools,
|
||||
BoolExpression conjunct)
|
||||
{
|
||||
List<BoolExpression> result = new List<BoolExpression>();
|
||||
// Go through the list -- AND each non-null boolean with conjunct
|
||||
foreach (BoolExpression b in bools)
|
||||
{
|
||||
if (null == b)
|
||||
{ // unused boolean -- leave as it is
|
||||
result.Add(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Add(CreateAnd(b, conjunct));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void InitializeConverter()
|
||||
{
|
||||
if (null != m_converter)
|
||||
{
|
||||
// already done
|
||||
return;
|
||||
}
|
||||
|
||||
m_converter = new Converter<BoolDomainConstraint>(m_tree,
|
||||
IdentifierService<BoolDomainConstraint>.Instance.CreateConversionContext());
|
||||
}
|
||||
|
||||
internal BoolExpression MakeCopy()
|
||||
{
|
||||
BoolExpression copy = Create(m_tree.Accept(CopyVisitorInstance));
|
||||
return copy;
|
||||
}
|
||||
static readonly CopyVisitor CopyVisitorInstance = new CopyVisitor();
|
||||
private class CopyVisitor : BasicVisitor<BoolDomainConstraint> { }
|
||||
|
||||
internal void ExpensiveSimplify()
|
||||
{
|
||||
if (!IsFinal())
|
||||
{
|
||||
m_tree = m_tree.Simplify();
|
||||
return;
|
||||
}
|
||||
|
||||
InitializeConverter();
|
||||
m_tree = m_tree.ExpensiveSimplify(out m_converter);
|
||||
// this call is needed because the possible values on restriction and TrueFalseLiterals
|
||||
// may change and need to be synchronized
|
||||
FixDomainMap(m_memberDomainMap);
|
||||
}
|
||||
|
||||
internal void FixDomainMap(MemberDomainMap domainMap)
|
||||
{
|
||||
Debug.Assert(domainMap != null, "Member domain map is not set");
|
||||
m_tree = FixRangeVisitor.FixRange(m_tree, domainMap);
|
||||
}
|
||||
|
||||
private bool IsFinal()
|
||||
{
|
||||
// First call simplify to get rid of tautologies and true, false
|
||||
// etc. and then collapse the OneOfs
|
||||
return (m_memberDomainMap != null && IsFinalVisitor.IsFinal(m_tree));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Comparer class
|
||||
// This class compares boolean expressions
|
||||
private class BoolComparer : IEqualityComparer<BoolExpression>
|
||||
{
|
||||
|
||||
#region IEqualityComparer<BoolExpression> Members
|
||||
public bool Equals(BoolExpression left, BoolExpression right)
|
||||
{
|
||||
// Quick check with references
|
||||
if (object.ReferenceEquals(left, right))
|
||||
{
|
||||
// Gets the Null and Undefined case as well
|
||||
return true;
|
||||
}
|
||||
// One of them is non-null at least
|
||||
if (left == null || right == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Both are non-null at this point
|
||||
return left.m_tree.Equals(right.m_tree);
|
||||
}
|
||||
|
||||
public int GetHashCode(BoolExpression expression)
|
||||
{
|
||||
return expression.m_tree.GetHashCode();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,206 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="BoolLiteral.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System.Data.Common.CommandTrees;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.Structures
|
||||
{
|
||||
using DomainConstraint = System.Data.Common.Utils.Boolean.DomainConstraint<BoolLiteral, Constant>;
|
||||
using DomainVariable = System.Data.Common.Utils.Boolean.DomainVariable<BoolLiteral, Constant>;
|
||||
using DomainBoolExpr = System.Data.Common.Utils.Boolean.BoolExpr<System.Data.Common.Utils.Boolean.DomainConstraint<BoolLiteral, Constant>>;
|
||||
using DomainNotExpr = System.Data.Common.Utils.Boolean.NotExpr <System.Data.Common.Utils.Boolean.DomainConstraint<BoolLiteral, Constant>>;
|
||||
using DomainTermExpr = System.Data.Common.Utils.Boolean.TermExpr<System.Data.Common.Utils.Boolean.DomainConstraint<BoolLiteral, Constant>>;
|
||||
|
||||
/// <summary>
|
||||
/// A class that ties up all the literals in boolean expressions.
|
||||
/// Conditions represented by <see cref="BoolLiteral"/>s need to be synchronized with <see cref="DomainConstraint"/>s,
|
||||
/// which may be modified upon calling <see cref="BoolExpression.ExpensiveSimplify"/>. This is what the method <see cref="BoolLiteral.FixRange"/> is used for.
|
||||
/// </summary>
|
||||
internal abstract class BoolLiteral : InternalBase
|
||||
{
|
||||
#region Fields
|
||||
internal static readonly IEqualityComparer<BoolLiteral> EqualityComparer = new BoolLiteralComparer();
|
||||
internal static readonly IEqualityComparer<BoolLiteral> EqualityIdentifierComparer = new IdentifierComparer();
|
||||
#endregion
|
||||
|
||||
#region Static MakeTermExpression methods
|
||||
/// <summary>
|
||||
/// Creates a term expression of the form: "<paramref name="literal"/> in <paramref name="range"/> with all possible values being <paramref name="domain"/>".
|
||||
/// </summary>
|
||||
internal static DomainTermExpr MakeTermExpression(BoolLiteral literal, IEnumerable<Constant> domain, IEnumerable<Constant> range)
|
||||
{
|
||||
Set<Constant> domainSet = new Set<Constant>(domain, Constant.EqualityComparer);
|
||||
Set<Constant> rangeSet = new Set<Constant>(range, Constant.EqualityComparer);
|
||||
return MakeTermExpression(literal, domainSet, rangeSet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a term expression of the form: "<paramref name="literal"/> in <paramref name="range"/> with all possible values being <paramref name="domain"/>".
|
||||
/// </summary>
|
||||
internal static DomainTermExpr MakeTermExpression(BoolLiteral literal, Set<Constant> domain, Set<Constant> range)
|
||||
{
|
||||
domain.MakeReadOnly();
|
||||
range.MakeReadOnly();
|
||||
|
||||
DomainVariable variable = new DomainVariable(literal, domain, EqualityIdentifierComparer);
|
||||
DomainConstraint constraint = new DomainConstraint(variable, range);
|
||||
DomainTermExpr result = new DomainTermExpr(EqualityComparer<DomainConstraint>.Default, constraint);
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Virtual methods
|
||||
/// <summary>
|
||||
/// Fixes the range of the literal using the new values provided in <paramref name="range"/> and returns a boolean expression corresponding to the new value.
|
||||
/// </summary>
|
||||
internal abstract DomainBoolExpr FixRange(Set<Constant> range, MemberDomainMap memberDomainMap);
|
||||
|
||||
internal abstract DomainBoolExpr GetDomainBoolExpression(MemberDomainMap domainMap);
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="BoolExpression.RemapBool"/>.
|
||||
/// </summary>
|
||||
internal abstract BoolLiteral RemapBool(Dictionary<MemberPath, MemberPath> remap);
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="BoolExpression.GetRequiredSlots"/>.
|
||||
/// </summary>
|
||||
/// <param name="projectedSlotMap"></param>
|
||||
/// <param name="requiredSlots"></param>
|
||||
internal abstract void GetRequiredSlots(MemberProjectionIndex projectedSlotMap, bool[] requiredSlots);
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="BoolExpression.AsEsql"/>.
|
||||
/// </summary>
|
||||
internal abstract StringBuilder AsEsql(StringBuilder builder, string blockAlias, bool skipIsNotNull);
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="BoolExpression.AsCqt"/>.
|
||||
/// </summary>
|
||||
internal abstract DbExpression AsCqt(DbExpression row, bool skipIsNotNull);
|
||||
|
||||
internal abstract StringBuilder AsUserString(StringBuilder builder, string blockAlias, bool skipIsNotNull);
|
||||
|
||||
internal abstract StringBuilder AsNegatedUserString(StringBuilder builder, string blockAlias, bool skipIsNotNull);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the identifier in this is the same as the one in <paramref name="right"/>.
|
||||
/// </summary>
|
||||
protected virtual bool IsIdentifierEqualTo(BoolLiteral right)
|
||||
{
|
||||
return IsEqualTo(right);
|
||||
}
|
||||
|
||||
protected abstract bool IsEqualTo(BoolLiteral right);
|
||||
|
||||
/// <summary>
|
||||
/// Get the hash code based on the identifier.
|
||||
/// </summary>
|
||||
protected virtual int GetIdentifierHash()
|
||||
{
|
||||
return GetHashCode();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Comparer class
|
||||
/// <summary>
|
||||
/// This class compares boolean expressions.
|
||||
/// </summary>
|
||||
private sealed class BoolLiteralComparer : IEqualityComparer<BoolLiteral>
|
||||
{
|
||||
public bool Equals(BoolLiteral left, BoolLiteral right)
|
||||
{
|
||||
// Quick check with references
|
||||
if (object.ReferenceEquals(left, right))
|
||||
{
|
||||
// Gets the Null and Undefined case as well
|
||||
return true;
|
||||
}
|
||||
// One of them is non-null at least
|
||||
if (left == null || right == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Both are non-null at this point
|
||||
return left.IsEqualTo(right);
|
||||
}
|
||||
|
||||
public int GetHashCode(BoolLiteral literal)
|
||||
{
|
||||
return literal.GetHashCode();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Identifier Comparer class
|
||||
/// <summary>
|
||||
/// This class compares just the identifier in boolean expressions.
|
||||
/// </summary>
|
||||
private sealed class IdentifierComparer : IEqualityComparer<BoolLiteral>
|
||||
{
|
||||
public bool Equals(BoolLiteral left, BoolLiteral right)
|
||||
{
|
||||
// Quick check with references
|
||||
if (object.ReferenceEquals(left, right))
|
||||
{
|
||||
// Gets the Null and Undefined case as well
|
||||
return true;
|
||||
}
|
||||
// One of them is non-null at least
|
||||
if (left == null || right == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Both are non-null at this point
|
||||
return left.IsIdentifierEqualTo(right);
|
||||
}
|
||||
|
||||
public int GetHashCode(BoolLiteral literal)
|
||||
{
|
||||
return literal.GetIdentifierHash();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
internal abstract class TrueFalseLiteral : BoolLiteral
|
||||
{
|
||||
internal override DomainBoolExpr GetDomainBoolExpression(MemberDomainMap domainMap)
|
||||
{
|
||||
// Essentially say that the variable can take values true or false and here its value is only true
|
||||
IEnumerable<Constant> actualValues = new Constant[] { new ScalarConstant(true) };
|
||||
IEnumerable<Constant> possibleValues = new Constant[] { new ScalarConstant(true), new ScalarConstant(false) };
|
||||
Set<Constant> variableDomain = new Set<Constant>(possibleValues, Constant.EqualityComparer).MakeReadOnly();
|
||||
Set<Constant> thisDomain = new Set<Constant>(actualValues, Constant.EqualityComparer).MakeReadOnly();
|
||||
|
||||
DomainTermExpr result = MakeTermExpression(this, variableDomain, thisDomain);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal override DomainBoolExpr FixRange(Set<Constant> range, MemberDomainMap memberDomainMap)
|
||||
{
|
||||
Debug.Assert(range.Count == 1, "For BoolLiterals, there should be precisely one value - true or false");
|
||||
ScalarConstant scalar = (ScalarConstant)range.First();
|
||||
DomainBoolExpr expr = GetDomainBoolExpression(memberDomainMap);
|
||||
|
||||
if ((bool)scalar.Value == false)
|
||||
{
|
||||
// The range of the variable was "inverted". Return a NOT of
|
||||
// the expression
|
||||
expr = new DomainNotExpr(expr);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,455 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="CaseStatement.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System.Data.Common.CommandTrees;
|
||||
using System.Data.Common.CommandTrees.ExpressionBuilder;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Mapping.ViewGeneration.CqlGeneration;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.Structures
|
||||
{
|
||||
/// <summary>
|
||||
/// A class to denote a case statement:
|
||||
/// CASE
|
||||
/// WHEN condition1 THEN value1
|
||||
/// WHEN condition2 THEN value2
|
||||
/// ...
|
||||
/// END
|
||||
/// </summary>
|
||||
internal sealed class CaseStatement : InternalBase
|
||||
{
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Creates a case statement for the <paramref name="memberPath"/> with no clauses.
|
||||
/// </summary>
|
||||
internal CaseStatement(MemberPath memberPath)
|
||||
{
|
||||
m_memberPath = memberPath;
|
||||
m_clauses = new List<WhenThen>();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
/// <summary>
|
||||
/// The field.
|
||||
/// </summary>
|
||||
private readonly MemberPath m_memberPath;
|
||||
/// <summary>
|
||||
/// All the WHEN THENs.
|
||||
/// </summary>
|
||||
private List<WhenThen> m_clauses;
|
||||
/// <summary>
|
||||
/// Value for the else clause.
|
||||
/// </summary>
|
||||
private ProjectedSlot m_elseValue;
|
||||
private bool m_simplified = false;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
internal MemberPath MemberPath
|
||||
{
|
||||
get { return m_memberPath; }
|
||||
}
|
||||
|
||||
internal List<WhenThen> Clauses
|
||||
{
|
||||
get { return m_clauses; }
|
||||
}
|
||||
|
||||
internal ProjectedSlot ElseValue
|
||||
{
|
||||
get { return m_elseValue; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Recursively qualifies all <see cref="ProjectedSlot"/>s and returns a new deeply qualified <see cref="CaseStatement"/>.
|
||||
/// </summary>
|
||||
internal CaseStatement DeepQualify(CqlBlock block)
|
||||
{
|
||||
// Go through the whenthens and else and make a new case statement with qualified slots as needed.
|
||||
CaseStatement result = new CaseStatement(m_memberPath);
|
||||
foreach (WhenThen whenThen in m_clauses)
|
||||
{
|
||||
WhenThen newClause = whenThen.ReplaceWithQualifiedSlot(block);
|
||||
result.m_clauses.Add(newClause);
|
||||
}
|
||||
if (m_elseValue != null)
|
||||
{
|
||||
result.m_elseValue = m_elseValue.DeepQualify(block);
|
||||
}
|
||||
result.m_simplified = m_simplified;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an expression of the form "WHEN <paramref name="condition"/> THEN <paramref name="value"/>".
|
||||
/// This operation is not allowed after the <see cref="Simplify"/> call.
|
||||
/// </summary>
|
||||
internal void AddWhenThen(BoolExpression condition, ProjectedSlot value)
|
||||
{
|
||||
Debug.Assert(!m_simplified, "Attempt to modify a simplified case statement");
|
||||
Debug.Assert(value != null);
|
||||
|
||||
condition.ExpensiveSimplify();
|
||||
m_clauses.Add(new WhenThen(condition, value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the <see cref="CaseStatement"/> depends on (projects) its slot in THEN value or ELSE value.
|
||||
/// </summary>
|
||||
internal bool DependsOnMemberValue
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_elseValue is MemberProjectedSlot)
|
||||
{
|
||||
Debug.Assert(m_memberPath.Equals(((MemberProjectedSlot)m_elseValue).MemberPath), "case statement slot (ELSE) must depend only on its own slot value");
|
||||
return true;
|
||||
}
|
||||
foreach (WhenThen whenThen in m_clauses)
|
||||
{
|
||||
if (whenThen.Value is MemberProjectedSlot)
|
||||
{
|
||||
Debug.Assert(m_memberPath.Equals(((MemberProjectedSlot)whenThen.Value).MemberPath), "case statement slot (THEN) must depend only on its own slot value");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<EdmType> InstantiatedTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (WhenThen whenThen in m_clauses)
|
||||
{
|
||||
EdmType type;
|
||||
if (TryGetInstantiatedType(whenThen.Value, out type))
|
||||
{
|
||||
yield return type;
|
||||
}
|
||||
}
|
||||
EdmType elseType;
|
||||
if (TryGetInstantiatedType(m_elseValue, out elseType))
|
||||
{
|
||||
yield return elseType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetInstantiatedType(ProjectedSlot slot, out EdmType type)
|
||||
{
|
||||
type = null;
|
||||
ConstantProjectedSlot constantSlot = slot as ConstantProjectedSlot;
|
||||
if (constantSlot != null)
|
||||
{
|
||||
TypeConstant typeConstant = constantSlot.CellConstant as TypeConstant;
|
||||
if (typeConstant != null)
|
||||
{
|
||||
type = typeConstant.EdmType;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simplifies the <see cref="CaseStatement"/> so that unnecessary WHEN/THENs for nulls/undefined values are eliminated.
|
||||
/// Also, adds an ELSE clause if possible.
|
||||
/// </summary>
|
||||
internal void Simplify()
|
||||
{
|
||||
if (m_simplified)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<CaseStatement.WhenThen> clauses = new List<CaseStatement.WhenThen>();
|
||||
// remove all WHEN clauses where the value gets set to "undefined"
|
||||
// We eliminate the last clause for now - we could determine the
|
||||
// "most complicated" WHEN clause and eliminate it
|
||||
bool eliminatedNullClauses = false;
|
||||
foreach (WhenThen clause in m_clauses)
|
||||
{
|
||||
ConstantProjectedSlot constantSlot = clause.Value as ConstantProjectedSlot;
|
||||
// If null or undefined, remove it
|
||||
if (constantSlot != null && (constantSlot.CellConstant.IsNull() || constantSlot.CellConstant.IsUndefined()))
|
||||
{
|
||||
eliminatedNullClauses = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
clauses.Add(clause);
|
||||
if (clause.Condition.IsTrue)
|
||||
{
|
||||
// none of subsequent case statements will be evaluated - ignore them
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (eliminatedNullClauses && clauses.Count == 0)
|
||||
{
|
||||
// There is nothing left -- we should add a null as the value
|
||||
m_elseValue = new ConstantProjectedSlot(Constant.Null, m_memberPath);
|
||||
}
|
||||
|
||||
// If we eliminated some undefined or null clauses, we do not want an else clause
|
||||
if (clauses.Count > 0 && false == eliminatedNullClauses)
|
||||
{
|
||||
// turn the last WHEN clause into an ELSE
|
||||
int lastIndex = clauses.Count - 1;
|
||||
m_elseValue = clauses[lastIndex].Value;
|
||||
clauses.RemoveAt(lastIndex);
|
||||
}
|
||||
m_clauses = clauses;
|
||||
|
||||
m_simplified = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates eSQL for the current <see cref="CaseStatement"/>.
|
||||
/// </summary>
|
||||
internal StringBuilder AsEsql(StringBuilder builder, IEnumerable<WithRelationship> withRelationships, string blockAlias, int indentLevel)
|
||||
{
|
||||
if (this.Clauses.Count == 0)
|
||||
{
|
||||
// This is just a single ELSE: no condition at all.
|
||||
Debug.Assert(this.ElseValue != null, "CASE statement with no WHEN/THENs must have ELSE.");
|
||||
CaseSlotValueAsEsql(builder, this.ElseValue, this.MemberPath, blockAlias, withRelationships, indentLevel);
|
||||
return builder;
|
||||
}
|
||||
|
||||
// Generate the Case WHEN .. THEN ..., WHEN ... THEN ..., END
|
||||
builder.Append("CASE");
|
||||
foreach (CaseStatement.WhenThen clause in this.Clauses)
|
||||
{
|
||||
StringUtil.IndentNewLine(builder, indentLevel + 2);
|
||||
builder.Append("WHEN ");
|
||||
clause.Condition.AsEsql(builder, blockAlias);
|
||||
builder.Append(" THEN ");
|
||||
CaseSlotValueAsEsql(builder, clause.Value, this.MemberPath, blockAlias, withRelationships, indentLevel + 2);
|
||||
}
|
||||
|
||||
if (this.ElseValue != null)
|
||||
{
|
||||
StringUtil.IndentNewLine(builder, indentLevel + 2);
|
||||
builder.Append("ELSE ");
|
||||
CaseSlotValueAsEsql(builder, this.ElseValue, this.MemberPath, blockAlias, withRelationships, indentLevel + 2);
|
||||
}
|
||||
StringUtil.IndentNewLine(builder, indentLevel + 1);
|
||||
builder.Append("END");
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates CQT for the current <see cref="CaseStatement"/>.
|
||||
/// </summary>
|
||||
internal DbExpression AsCqt(DbExpression row, IEnumerable<WithRelationship> withRelationships)
|
||||
{
|
||||
// Generate the Case WHEN .. THEN ..., WHEN ... THEN ..., END
|
||||
List<DbExpression> conditions = new List<DbExpression>();
|
||||
List<DbExpression> values = new List<DbExpression>();
|
||||
foreach (CaseStatement.WhenThen clause in this.Clauses)
|
||||
{
|
||||
conditions.Add(clause.Condition.AsCqt(row));
|
||||
values.Add(CaseSlotValueAsCqt(row, clause.Value, this.MemberPath, withRelationships));
|
||||
}
|
||||
|
||||
// Generate ELSE
|
||||
DbExpression elseValue = this.ElseValue != null ?
|
||||
CaseSlotValueAsCqt(row, this.ElseValue, this.MemberPath, withRelationships) :
|
||||
Constant.Null.AsCqt(row, this.MemberPath);
|
||||
|
||||
if (this.Clauses.Count > 0)
|
||||
{
|
||||
return DbExpressionBuilder.Case(conditions, values, elseValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(elseValue != null, "CASE statement with no WHEN/THENs must have ELSE.");
|
||||
return elseValue;
|
||||
}
|
||||
}
|
||||
|
||||
private static StringBuilder CaseSlotValueAsEsql(StringBuilder builder, ProjectedSlot slot, MemberPath outputMember, string blockAlias, IEnumerable<WithRelationship> withRelationships, int indentLevel)
|
||||
{
|
||||
// We should never have THEN as a BooleanProjectedSlot.
|
||||
Debug.Assert(slot is MemberProjectedSlot || slot is QualifiedSlot || slot is ConstantProjectedSlot,
|
||||
"Case statement THEN can only have constants or members.");
|
||||
slot.AsEsql(builder, outputMember, blockAlias, 1);
|
||||
WithRelationshipsClauseAsEsql(builder, withRelationships, blockAlias, indentLevel, slot);
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static void WithRelationshipsClauseAsEsql(StringBuilder builder, IEnumerable<WithRelationship> withRelationships, string blockAlias, int indentLevel, ProjectedSlot slot)
|
||||
{
|
||||
bool first = true;
|
||||
WithRelationshipsClauseAsCql(
|
||||
// emitWithRelationship action
|
||||
(withRelationship) =>
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
builder.Append(" WITH ");
|
||||
first = false;
|
||||
}
|
||||
withRelationship.AsEsql(builder, blockAlias, indentLevel);
|
||||
},
|
||||
withRelationships,
|
||||
slot);
|
||||
}
|
||||
|
||||
private static DbExpression CaseSlotValueAsCqt(DbExpression row, ProjectedSlot slot, MemberPath outputMember, IEnumerable<WithRelationship> withRelationships)
|
||||
{
|
||||
// We should never have THEN as a BooleanProjectedSlot.
|
||||
Debug.Assert(slot is MemberProjectedSlot || slot is QualifiedSlot || slot is ConstantProjectedSlot,
|
||||
"Case statement THEN can only have constants or members.");
|
||||
DbExpression cqt = slot.AsCqt(row, outputMember);
|
||||
cqt = WithRelationshipsClauseAsCqt(row, cqt, withRelationships, slot);
|
||||
return cqt;
|
||||
}
|
||||
|
||||
private static DbExpression WithRelationshipsClauseAsCqt(DbExpression row, DbExpression slotValueExpr, IEnumerable<WithRelationship> withRelationships, ProjectedSlot slot)
|
||||
{
|
||||
List<DbRelatedEntityRef> relatedEntityRefs = new List<DbRelatedEntityRef>();
|
||||
WithRelationshipsClauseAsCql(
|
||||
// emitWithRelationship action
|
||||
(withRelationship) =>
|
||||
{
|
||||
relatedEntityRefs.Add(withRelationship.AsCqt(row));
|
||||
},
|
||||
withRelationships,
|
||||
slot);
|
||||
|
||||
if (relatedEntityRefs.Count > 0)
|
||||
{
|
||||
DbNewInstanceExpression typeConstructor = slotValueExpr as DbNewInstanceExpression;
|
||||
Debug.Assert(typeConstructor != null && typeConstructor.ResultType.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType,
|
||||
"WITH RELATIONSHIP clauses should be specified for entity type constructors only.");
|
||||
return DbExpressionBuilder.CreateNewEntityWithRelationshipsExpression(
|
||||
(EntityType)typeConstructor.ResultType.EdmType,
|
||||
typeConstructor.Arguments,
|
||||
relatedEntityRefs);
|
||||
}
|
||||
else
|
||||
{
|
||||
return slotValueExpr;
|
||||
}
|
||||
}
|
||||
|
||||
private static void WithRelationshipsClauseAsCql(Action<WithRelationship> emitWithRelationship, IEnumerable<WithRelationship> withRelationships, ProjectedSlot slot)
|
||||
{
|
||||
if (withRelationships != null && withRelationships.Count() > 0)
|
||||
{
|
||||
ConstantProjectedSlot constantSlot = slot as ConstantProjectedSlot;
|
||||
Debug.Assert(constantSlot != null, "WITH RELATIONSHIP clauses should be specified for type constant slots only.");
|
||||
TypeConstant typeConstant = constantSlot.CellConstant as TypeConstant;
|
||||
Debug.Assert(typeConstant != null, "WITH RELATIONSHIP clauses should be there for type constants only.");
|
||||
EdmType fromType = typeConstant.EdmType;
|
||||
|
||||
foreach (WithRelationship withRelationship in withRelationships)
|
||||
{
|
||||
// Add With statement for the types that participate in the association.
|
||||
if (withRelationship.FromEndEntityType.IsAssignableFrom(fromType))
|
||||
{
|
||||
emitWithRelationship(withRelationship);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal override void ToCompactString(StringBuilder builder)
|
||||
{
|
||||
builder.AppendLine("CASE");
|
||||
foreach (WhenThen clause in m_clauses)
|
||||
{
|
||||
builder.Append(" WHEN ");
|
||||
clause.Condition.ToCompactString(builder);
|
||||
builder.Append(" THEN ");
|
||||
clause.Value.ToCompactString(builder);
|
||||
builder.AppendLine();
|
||||
}
|
||||
if (m_elseValue != null)
|
||||
{
|
||||
builder.Append(" ELSE ");
|
||||
m_elseValue.ToCompactString(builder);
|
||||
builder.AppendLine();
|
||||
}
|
||||
builder.Append(" END AS ");
|
||||
m_memberPath.ToCompactString(builder);
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// A class that stores WHEN condition THEN value.
|
||||
/// </summary>
|
||||
internal sealed class WhenThen : InternalBase
|
||||
{
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates WHEN condition THEN value.
|
||||
/// </summary>
|
||||
internal WhenThen(BoolExpression condition, ProjectedSlot value)
|
||||
{
|
||||
m_condition = condition;
|
||||
m_value = value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private readonly BoolExpression m_condition;
|
||||
private readonly ProjectedSlot m_value;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Returns WHEN condition.
|
||||
/// </summary>
|
||||
internal BoolExpression Condition
|
||||
{
|
||||
get { return m_condition; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns THEN value.
|
||||
/// </summary>
|
||||
internal ProjectedSlot Value
|
||||
{
|
||||
get { return m_value; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region String Methods
|
||||
internal WhenThen ReplaceWithQualifiedSlot(CqlBlock block)
|
||||
{
|
||||
// Change the THEN part
|
||||
ProjectedSlot newValue = m_value.DeepQualify(block);
|
||||
return new WhenThen(m_condition, newValue);
|
||||
}
|
||||
|
||||
internal override void ToCompactString(StringBuilder builder)
|
||||
{
|
||||
builder.Append("WHEN ");
|
||||
m_condition.ToCompactString(builder);
|
||||
builder.Append("THEN ");
|
||||
m_value.ToCompactString(builder);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="CaseStatementProjectedSlot.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System.Data.Mapping.ViewGeneration.CqlGeneration;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common.CommandTrees;
|
||||
using System.Data.Common.CommandTrees.ExpressionBuilder;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.Structures
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is just a wrapper over case statements so that we don't pollute the <see cref="CaseStatement"/> class itself.
|
||||
/// </summary>
|
||||
internal sealed class CaseStatementProjectedSlot : ProjectedSlot
|
||||
{
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a slot for <paramref name="statement"/>.
|
||||
/// </summary>
|
||||
internal CaseStatementProjectedSlot(CaseStatement statement, IEnumerable<WithRelationship> withRelationships)
|
||||
{
|
||||
m_caseStatement = statement;
|
||||
m_withRelationships = withRelationships;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
/// <summary>
|
||||
/// The actual case statement.
|
||||
/// </summary>
|
||||
private readonly CaseStatement m_caseStatement;
|
||||
private readonly IEnumerable<WithRelationship> m_withRelationships;
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Creates new <see cref="ProjectedSlot"/> that is qualified with <paramref name="block"/>.CqlAlias.
|
||||
/// If current slot is composite (such as <see cref="CaseStatementProjectedSlot"/>, then this method recursively qualifies all parts
|
||||
/// and returns a new deeply qualified slot (as opposed to <see cref="CqlBlock.QualifySlotWithBlockAlias"/>).
|
||||
/// </summary>
|
||||
internal override ProjectedSlot DeepQualify(CqlBlock block)
|
||||
{
|
||||
CaseStatement newStatement = m_caseStatement.DeepQualify(block);
|
||||
return new CaseStatementProjectedSlot(newStatement, null);
|
||||
}
|
||||
|
||||
internal override StringBuilder AsEsql(StringBuilder builder, MemberPath outputMember, string blockAlias, int indentLevel)
|
||||
{
|
||||
m_caseStatement.AsEsql(builder, m_withRelationships, blockAlias, indentLevel);
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal override DbExpression AsCqt(DbExpression row, MemberPath outputMember)
|
||||
{
|
||||
return m_caseStatement.AsCqt(row, m_withRelationships);
|
||||
}
|
||||
|
||||
internal override void ToCompactString(StringBuilder builder)
|
||||
{
|
||||
m_caseStatement.ToCompactString(builder);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,254 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="Cell.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System.Data.Common.Utils;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Mapping.ViewGeneration.Validation;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using System.Data.Metadata.Edm;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.Structures
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This class contains a pair of cell queries which is essentially a
|
||||
/// constraint that they are equal. A cell is initialized with a C or an
|
||||
/// S Query which it exposes as properties but it also has the notion of
|
||||
/// "Left" and "Right" queries -- left refers to the side for which a
|
||||
/// view is being generated
|
||||
/// For example, to
|
||||
/// specify a mapping for CPerson to an SPerson table, we have
|
||||
///
|
||||
/// [(p type Person) in P : SPerson]
|
||||
/// (p.pid, pid)
|
||||
/// (p.name, name)
|
||||
///
|
||||
/// This really denotes the equality of two queries:
|
||||
/// (C) SELECT (p type Person) AS D1, p.pid, p.name FROM p in P WHERE D1
|
||||
/// (S) SELECT True AS D1, pid, name FROM SPerson WHERE D1
|
||||
///
|
||||
/// For more details, see the design doc
|
||||
/// </summary>
|
||||
internal class Cell : InternalBase
|
||||
{
|
||||
#region Constructor
|
||||
// effects: Creates a cell with the C and S queries
|
||||
private Cell(CellQuery cQuery, CellQuery sQuery, CellLabel label, int cellNumber)
|
||||
{
|
||||
Debug.Assert(label != null, "Cell lacks label");
|
||||
m_cQuery = cQuery;
|
||||
m_sQuery = sQuery;
|
||||
m_label = label;
|
||||
m_cellNumber = cellNumber;
|
||||
Debug.Assert(m_sQuery.NumProjectedSlots == m_cQuery.NumProjectedSlots,
|
||||
"Cell queries disagree on the number of projected fields");
|
||||
}
|
||||
/// <summary>
|
||||
/// Copy Constructor
|
||||
/// </summary>
|
||||
internal Cell(Cell source)
|
||||
{
|
||||
m_cQuery = new CellQuery(source.m_cQuery);
|
||||
m_sQuery = new CellQuery(source.m_sQuery);
|
||||
m_label = new CellLabel(source.m_label);
|
||||
m_cellNumber = source.m_cellNumber;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private CellQuery m_cQuery;
|
||||
private CellQuery m_sQuery;
|
||||
private int m_cellNumber; // cell number that identifies this cell
|
||||
private CellLabel m_label; // The File and Path Info for the CSMappingFragment
|
||||
// that the Cell was constructed over.
|
||||
// The view cell relation for all projected slots in this
|
||||
private ViewCellRelation m_viewCellRelation;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
// effects: Returns the C query
|
||||
internal CellQuery CQuery
|
||||
{
|
||||
get { return m_cQuery; }
|
||||
}
|
||||
|
||||
// effects: Returns the S query
|
||||
internal CellQuery SQuery
|
||||
{
|
||||
get { return m_sQuery; }
|
||||
}
|
||||
|
||||
// effects: Returns the CSMappingFragment (if any)
|
||||
// that the Cell was constructed over.
|
||||
internal CellLabel CellLabel
|
||||
{
|
||||
get { return m_label; }
|
||||
}
|
||||
|
||||
// effects: Returns the cell label (if any)
|
||||
internal int CellNumber
|
||||
{
|
||||
get { return m_cellNumber; }
|
||||
}
|
||||
|
||||
internal string CellNumberAsString
|
||||
{
|
||||
get { return StringUtil.FormatInvariant("V{0}", CellNumber); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
// effects: Determines all the identifiers used in this and adds them to identifiers
|
||||
internal void GetIdentifiers(CqlIdentifiers identifiers)
|
||||
{
|
||||
m_cQuery.GetIdentifiers(identifiers);
|
||||
m_sQuery.GetIdentifiers(identifiers);
|
||||
}
|
||||
|
||||
// effects: Given a cell, determines the paths to which the paths in
|
||||
// columns map to in the C-space and returns them. If some columns
|
||||
// are not projected in the cell, or if the corresponding properties
|
||||
// are not mapped into C-space, returns null
|
||||
internal Set<EdmProperty> GetCSlotsForTableColumns(IEnumerable<MemberPath> columns)
|
||||
{
|
||||
List<int> fieldNums = SQuery.GetProjectedPositions(columns);
|
||||
if (fieldNums == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// The fields are mapped -- see if they are mapped on the
|
||||
// cSide and they correspond to the primary key of the
|
||||
// entity set
|
||||
|
||||
Set<EdmProperty> cSideMembers = new Set<EdmProperty>();
|
||||
foreach (int fieldNum in fieldNums)
|
||||
{
|
||||
ProjectedSlot projectedSlot = CQuery.ProjectedSlotAt(fieldNum);
|
||||
MemberProjectedSlot slot = projectedSlot as MemberProjectedSlot;
|
||||
if (slot != null)
|
||||
{
|
||||
// We can call LastMember since columns do not map to
|
||||
// extents or memberEnds. Can cast to EdmProperty since it
|
||||
// cannot be an association end
|
||||
cSideMembers.Add((EdmProperty)slot.MemberPath.LeafEdmMember);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return cSideMembers;
|
||||
}
|
||||
|
||||
// effects: Returns the C query for ViewTarget.QueryView and S query for ViewTarget.UpdateView
|
||||
internal CellQuery GetLeftQuery(ViewTarget side)
|
||||
{
|
||||
return side == ViewTarget.QueryView ? m_cQuery : m_sQuery;
|
||||
}
|
||||
|
||||
// effects: Returns the S query for ViewTarget.QueryView and C query for ViewTarget.UpdateView
|
||||
internal CellQuery GetRightQuery(ViewTarget side)
|
||||
{
|
||||
return side == ViewTarget.QueryView ? m_sQuery : m_cQuery;
|
||||
}
|
||||
|
||||
// effects: Returns the relation that contains all the slots being
|
||||
// projected in this cell
|
||||
internal ViewCellRelation CreateViewCellRelation(int cellNumber)
|
||||
{
|
||||
if (m_viewCellRelation != null)
|
||||
{
|
||||
return m_viewCellRelation;
|
||||
}
|
||||
GenerateCellRelations(cellNumber);
|
||||
return m_viewCellRelation;
|
||||
}
|
||||
|
||||
private void GenerateCellRelations(int cellNumber)
|
||||
{
|
||||
// Generate the view cell relation
|
||||
List<ViewCellSlot> projectedSlots = new List<ViewCellSlot>();
|
||||
// construct a ViewCellSlot for each slot
|
||||
Debug.Assert(CQuery.NumProjectedSlots == SQuery.NumProjectedSlots,
|
||||
"Cell queries in cell have a different number of slots");
|
||||
for (int i = 0; i < CQuery.NumProjectedSlots; i++)
|
||||
{
|
||||
ProjectedSlot cSlot = CQuery.ProjectedSlotAt(i);
|
||||
ProjectedSlot sSlot = SQuery.ProjectedSlotAt(i);
|
||||
Debug.Assert(cSlot != null, "Has cell query been normalized?");
|
||||
Debug.Assert(sSlot != null, "Has cell query been normalized?");
|
||||
|
||||
// These slots better be MemberProjectedSlots. We do not have constants etc at this point.
|
||||
Debug.Assert(cSlot is MemberProjectedSlot, "cSlot is expected to be MemberProjectedSlot");
|
||||
Debug.Assert(sSlot is MemberProjectedSlot, "sSlot is expected to be MemberProjectedSlot");
|
||||
|
||||
MemberProjectedSlot cJoinSlot = (MemberProjectedSlot)cSlot;
|
||||
MemberProjectedSlot sJoinSlot = (MemberProjectedSlot)sSlot;
|
||||
|
||||
ViewCellSlot slot = new ViewCellSlot(i, cJoinSlot, sJoinSlot);
|
||||
projectedSlots.Add(slot);
|
||||
}
|
||||
m_viewCellRelation = new ViewCellRelation(this, projectedSlots, cellNumber);
|
||||
}
|
||||
|
||||
internal override void ToCompactString(StringBuilder builder)
|
||||
{
|
||||
CQuery.ToCompactString(builder);
|
||||
builder.Append(" = ");
|
||||
SQuery.ToCompactString(builder);
|
||||
}
|
||||
|
||||
internal override void ToFullString(StringBuilder builder)
|
||||
{
|
||||
CQuery.ToFullString(builder);
|
||||
builder.Append(" = ");
|
||||
SQuery.ToFullString(builder);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return ToFullString();
|
||||
}
|
||||
|
||||
// effects: Prints the cells in some human-readable form
|
||||
internal static void CellsToBuilder(StringBuilder builder, IEnumerable<Cell> cells)
|
||||
{
|
||||
// Print mapping
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("=========================================================================");
|
||||
foreach (Cell cell in cells)
|
||||
{
|
||||
builder.AppendLine();
|
||||
StringUtil.FormatStringBuilder(builder, "Mapping Cell V{0}:", cell.CellNumber);
|
||||
builder.AppendLine();
|
||||
|
||||
builder.Append("C: ");
|
||||
cell.CQuery.ToFullString(builder);
|
||||
builder.AppendLine();
|
||||
builder.AppendLine();
|
||||
|
||||
builder.Append("S: ");
|
||||
cell.SQuery.ToFullString(builder);
|
||||
builder.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Factory methods
|
||||
internal static Cell CreateCS(CellQuery cQuery, CellQuery sQuery, CellLabel label, int cellNumber)
|
||||
{
|
||||
return new Cell(cQuery, sQuery, label, cellNumber);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,118 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="CellIdBoolean.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.Structures
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common.CommandTrees;
|
||||
using System.Data.Common.CommandTrees.ExpressionBuilder;
|
||||
using System.Data.Mapping.ViewGeneration.CqlGeneration;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// Wraps from0, from1, etc. boolean fields that identify the source of tuples (# of respective cell query) in the view statements.
|
||||
/// </summary>
|
||||
internal class CellIdBoolean : TrueFalseLiteral
|
||||
{
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Creates a boolean expression for the variable name specified by <paramref name="index"/>, e.g., 0 results in from0, 1 into from1.
|
||||
/// </summary>
|
||||
internal CellIdBoolean(CqlIdentifiers identifiers, int index)
|
||||
{
|
||||
Debug.Assert(index >= 0);
|
||||
m_index = index;
|
||||
m_slotName = identifiers.GetFromVariable(index);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
/// <summary>
|
||||
/// e.g., from0, from1.
|
||||
/// </summary>
|
||||
private readonly int m_index;
|
||||
private readonly string m_slotName;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Returns the slotName corresponding to this, ie., _from0 etc.
|
||||
/// </summary>
|
||||
internal string SlotName
|
||||
{
|
||||
get { return m_slotName; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region BoolLiteral members
|
||||
internal override StringBuilder AsEsql(StringBuilder builder, string blockAlias, bool skipIsNotNull)
|
||||
{
|
||||
// Get e.g., T2._from1 using the table alias
|
||||
string qualifiedName = CqlWriter.GetQualifiedName(blockAlias, SlotName);
|
||||
builder.Append(qualifiedName);
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal override DbExpression AsCqt(DbExpression row, bool skipIsNotNull)
|
||||
{
|
||||
// Get e.g., row._from1
|
||||
return row.Property(SlotName);
|
||||
}
|
||||
|
||||
internal override StringBuilder AsUserString(StringBuilder builder, string blockAlias, bool skipIsNotNull)
|
||||
{
|
||||
return AsEsql(builder, blockAlias, skipIsNotNull);
|
||||
}
|
||||
|
||||
internal override StringBuilder AsNegatedUserString(StringBuilder builder, string blockAlias, bool skipIsNotNull)
|
||||
{
|
||||
builder.Append("NOT(");
|
||||
builder = AsUserString(builder, blockAlias, skipIsNotNull);
|
||||
builder.Append(")");
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal override void GetRequiredSlots(MemberProjectionIndex projectedSlotMap, bool[] requiredSlots)
|
||||
{
|
||||
// The slot corresponding to from1, etc
|
||||
int numBoolSlots = requiredSlots.Length - projectedSlotMap.Count;
|
||||
int slotNum = projectedSlotMap.BoolIndexToSlot(m_index, numBoolSlots);
|
||||
requiredSlots[slotNum] = true;
|
||||
}
|
||||
|
||||
protected override bool IsEqualTo(BoolLiteral right)
|
||||
{
|
||||
CellIdBoolean rightBoolean = right as CellIdBoolean;
|
||||
if (rightBoolean == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return m_index == rightBoolean.m_index;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return m_index.GetHashCode();
|
||||
}
|
||||
|
||||
internal override BoolLiteral RemapBool(Dictionary<MemberPath, MemberPath> remap)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Other Methods
|
||||
internal override void ToCompactString(StringBuilder builder)
|
||||
{
|
||||
builder.Append(SlotName);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="CellLabel.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
|
||||
using System.Data.Common.Utils;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.Structures
|
||||
{
|
||||
|
||||
// A class that abstracts the notion of identifying table mapping
|
||||
// fragments or cells, e.g., line numbers, etc
|
||||
internal class CellLabel
|
||||
{
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Copy Constructor
|
||||
/// </summary>
|
||||
internal CellLabel(CellLabel source)
|
||||
{
|
||||
this.m_startLineNumber = source.m_startLineNumber;
|
||||
this.m_startLinePosition = source.m_startLinePosition;
|
||||
this.m_sourceLocation = source.m_sourceLocation;
|
||||
}
|
||||
|
||||
internal CellLabel(StorageMappingFragment fragmentInfo) :
|
||||
this(fragmentInfo.StartLineNumber, fragmentInfo.StartLinePosition, fragmentInfo.SourceLocation) { }
|
||||
|
||||
internal CellLabel(int startLineNumber, int startLinePosition, string sourceLocation)
|
||||
{
|
||||
m_startLineNumber = startLineNumber;
|
||||
m_startLinePosition = startLinePosition;
|
||||
m_sourceLocation = sourceLocation;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private int m_startLineNumber;
|
||||
private int m_startLinePosition;
|
||||
private string m_sourceLocation;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
internal int StartLineNumber
|
||||
{
|
||||
get { return m_startLineNumber; }
|
||||
}
|
||||
|
||||
internal int StartLinePosition
|
||||
{
|
||||
get { return m_startLinePosition; }
|
||||
}
|
||||
|
||||
internal string SourceLocation
|
||||
{
|
||||
get { return m_sourceLocation; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,228 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="CellTreeNode.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.Structures
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Data.Mapping.ViewGeneration.CqlGeneration;
|
||||
using System.Data.Mapping.ViewGeneration.QueryRewriting;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
// This class represents a node in the update or query mapping view tree
|
||||
// (of course, the root node represents the full view)
|
||||
// Each node represents an expression of the form:
|
||||
// SELECT <Attributes> FROM <Expression> WHERE <Clause>
|
||||
// The WHERE clause is of the form X1 OR X2 OR ... where each Xi is a multiconstant
|
||||
internal abstract partial class CellTreeNode : InternalBase
|
||||
{
|
||||
|
||||
#region Constructor
|
||||
// effects: Creates a cell tree node with a reference to projectedSlotMap for
|
||||
// deciphering the fields in this
|
||||
protected CellTreeNode(ViewgenContext context)
|
||||
{
|
||||
m_viewgenContext = context;
|
||||
}
|
||||
|
||||
// effects: returns a copy of the tree below node
|
||||
internal CellTreeNode MakeCopy()
|
||||
{
|
||||
DefaultCellTreeVisitor<bool> visitor = new DefaultCellTreeVisitor<bool>();
|
||||
CellTreeNode result = Accept<bool, CellTreeNode>(visitor, true);
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private ViewgenContext m_viewgenContext;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
// effects: Returns the operation being performed by this node
|
||||
internal abstract CellTreeOpType OpType { get; }
|
||||
|
||||
// effects: Returns the right domain map associated with this celltreenode
|
||||
internal abstract MemberDomainMap RightDomainMap { get; }
|
||||
|
||||
internal abstract FragmentQuery LeftFragmentQuery { get; }
|
||||
|
||||
internal abstract FragmentQuery RightFragmentQuery { get; }
|
||||
|
||||
internal bool IsEmptyRightFragmentQuery
|
||||
{
|
||||
get { return !m_viewgenContext.RightFragmentQP.IsSatisfiable(RightFragmentQuery); }
|
||||
}
|
||||
|
||||
// effects: Returns the attributes available/projected from this node
|
||||
internal abstract Set<MemberPath> Attributes { get; }
|
||||
|
||||
// effects: Returns the children of this node
|
||||
internal abstract List<CellTreeNode> Children { get; }
|
||||
|
||||
// effects: Returns the number of slots projected from this node
|
||||
internal abstract int NumProjectedSlots { get; }
|
||||
|
||||
// effects: Returns the number of boolean slots in this node
|
||||
internal abstract int NumBoolSlots { get; }
|
||||
|
||||
internal MemberProjectionIndex ProjectedSlotMap
|
||||
{
|
||||
get { return m_viewgenContext.MemberMaps.ProjectedSlotMap; }
|
||||
}
|
||||
|
||||
internal ViewgenContext ViewgenContext
|
||||
{
|
||||
get { return m_viewgenContext; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Abstract Methods
|
||||
// effects: Given a leaf cell node and the slots required by the parent, returns
|
||||
// a CqlBlock corresponding to the tree rooted at this
|
||||
internal abstract CqlBlock ToCqlBlock(bool[] requiredSlots, CqlIdentifiers identifiers, ref int blockAliasNum,
|
||||
ref List<WithRelationship> withRelationships);
|
||||
|
||||
// Effects: Returns true if slot at slot number "slot" is projected
|
||||
// by some node in tree rooted at this
|
||||
internal abstract bool IsProjectedSlot(int slot);
|
||||
|
||||
// Standard accept method for visitor pattern. TOutput is the return
|
||||
// type for visitor methods.
|
||||
internal abstract TOutput Accept<TInput, TOutput>(CellTreeVisitor<TInput, TOutput> visitor, TInput param);
|
||||
internal abstract TOutput Accept<TInput, TOutput>(SimpleCellTreeVisitor<TInput, TOutput> visitor, TInput param);
|
||||
#endregion
|
||||
|
||||
#region Visitor methods
|
||||
// effects: Given a cell tree node , removes unnecessary
|
||||
// "nesting" that occurs in the tree -- an unnecessary nesting
|
||||
// occurs when a node has exactly one child.
|
||||
internal CellTreeNode Flatten()
|
||||
{
|
||||
return FlatteningVisitor.Flatten(this);
|
||||
}
|
||||
|
||||
// effects: Gets all the leaves in this
|
||||
internal List<LeftCellWrapper> GetLeaves()
|
||||
{
|
||||
return GetLeafNodes().Select(leafNode => leafNode.LeftCellWrapper).ToList();
|
||||
}
|
||||
|
||||
// effects: Gets all the leaves in this
|
||||
internal IEnumerable<LeafCellTreeNode> GetLeafNodes()
|
||||
{
|
||||
return LeafVisitor.GetLeaves(this);
|
||||
}
|
||||
|
||||
// effects: Like Flatten, flattens the tree and then collapses
|
||||
// associative operators, e.g., (A IJ B) IJ C is changed to A IJ B IJ C
|
||||
internal CellTreeNode AssociativeFlatten()
|
||||
{
|
||||
return AssociativeOpFlatteningVisitor.Flatten(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper methods, e.g., for slots and strings
|
||||
// effects: Returns true iff the Op (e.g., IJ) is associative, i.e.,
|
||||
// A OP (B OP C) is the same as (A OP B) OP C or A OP B OP C
|
||||
internal static bool IsAssociativeOp(CellTreeOpType opType)
|
||||
{
|
||||
// This is not true for LOJ and LASJ
|
||||
return opType == CellTreeOpType.IJ || opType == CellTreeOpType.Union ||
|
||||
opType == CellTreeOpType.FOJ;
|
||||
}
|
||||
|
||||
// effects: Returns an array of booleans where bool[i] is set to true
|
||||
// iff some node in the tree rooted at node projects that slot
|
||||
internal bool[] GetProjectedSlots()
|
||||
{
|
||||
// Gets the information on the normal and the boolean slots
|
||||
int totalSlots = ProjectedSlotMap.Count + NumBoolSlots;
|
||||
bool[] slots = new bool[totalSlots];
|
||||
for (int i = 0; i < totalSlots; i++)
|
||||
{
|
||||
slots[i] = IsProjectedSlot(i);
|
||||
}
|
||||
return slots;
|
||||
}
|
||||
|
||||
// effects: Given a slot number, slotNum, returns the output member path
|
||||
// that this slot contributes/corresponds to in the extent view. If
|
||||
// the slot corresponds to one of the boolean variables, returns null
|
||||
protected MemberPath GetMemberPath(int slotNum)
|
||||
{
|
||||
return ProjectedSlotMap.GetMemberPath(slotNum, NumBoolSlots);
|
||||
}
|
||||
|
||||
// effects: Given the index of a boolean variable (e.g., of from1),
|
||||
// returns the slot number for that boolean in this
|
||||
protected int BoolIndexToSlot(int boolIndex)
|
||||
{
|
||||
// Booleans appear after the regular slot
|
||||
return ProjectedSlotMap.BoolIndexToSlot(boolIndex, NumBoolSlots);
|
||||
}
|
||||
|
||||
// effects: Given a slotNum corresponding to a boolean slot, returns
|
||||
// the cel number that the cell corresponds to
|
||||
protected int SlotToBoolIndex(int slotNum)
|
||||
{
|
||||
return ProjectedSlotMap.SlotToBoolIndex(slotNum, NumBoolSlots);
|
||||
}
|
||||
|
||||
// effects: Returns true if slotNum corresponds to a key slot in the
|
||||
// output extent view
|
||||
protected bool IsKeySlot(int slotNum)
|
||||
{
|
||||
return ProjectedSlotMap.IsKeySlot(slotNum, NumBoolSlots);
|
||||
}
|
||||
|
||||
// effects: Returns true if slotNum corresponds to a bool slot and
|
||||
// not a regular field
|
||||
protected bool IsBoolSlot(int slotNum)
|
||||
{
|
||||
return ProjectedSlotMap.IsBoolSlot(slotNum, NumBoolSlots);
|
||||
}
|
||||
|
||||
|
||||
// effects: Returns the slot numbers corresponding to the key fields
|
||||
// in the m_projectedSlotMap
|
||||
protected IEnumerable<int> KeySlots
|
||||
{
|
||||
get
|
||||
{
|
||||
int numMembers = ProjectedSlotMap.Count;
|
||||
for (int slotNum = 0; slotNum < numMembers; slotNum++)
|
||||
{
|
||||
if (true == IsKeySlot(slotNum))
|
||||
{
|
||||
yield return slotNum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// effects: Modifies builder to contain a Cql query corresponding to
|
||||
// the tree rooted at this
|
||||
internal override void ToFullString(StringBuilder builder)
|
||||
{
|
||||
int blockAliasNum = 0;
|
||||
// Get the required slots, get the block and then get the string
|
||||
bool[] requiredSlots = GetProjectedSlots();
|
||||
// Using empty identifiers over here since we do not use this for the actual CqlGeneration
|
||||
CqlIdentifiers identifiers = new CqlIdentifiers();
|
||||
List<WithRelationship> withRelationships = new List<WithRelationship>();
|
||||
CqlBlock block = ToCqlBlock(requiredSlots, identifiers, ref blockAliasNum, ref withRelationships);
|
||||
block.AsEsql(builder, false, 1);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,252 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="CellTreeNodeVisitors.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common.Utils.Boolean;
|
||||
using System.Diagnostics;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Data.Metadata.Edm;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.Structures
|
||||
{
|
||||
|
||||
using WrapperBoolExpr = BoolExpr<LeftCellWrapper>;
|
||||
using WrapperTreeExpr = TreeExpr<LeftCellWrapper>;
|
||||
using WrapperAndExpr = AndExpr<LeftCellWrapper>;
|
||||
using WrapperOrExpr = OrExpr<LeftCellWrapper>;
|
||||
using WrapperNotExpr = NotExpr<LeftCellWrapper>;
|
||||
using WrapperTermExpr = TermExpr<LeftCellWrapper>;
|
||||
using WrapperTrueExpr = TrueExpr<LeftCellWrapper>;
|
||||
using WrapperFalseExpr = FalseExpr<LeftCellWrapper>;
|
||||
|
||||
internal partial class CellTreeNode
|
||||
{
|
||||
|
||||
#region Abstract Visitors
|
||||
// Abstract visitor implementation for Cell trees
|
||||
// TOutput is the return type of the visitor and TInput is a single
|
||||
// parameter that can be passed in
|
||||
internal abstract class CellTreeVisitor<TInput, TOutput>
|
||||
{
|
||||
internal abstract TOutput VisitLeaf(LeafCellTreeNode node, TInput param);
|
||||
internal abstract TOutput VisitUnion(OpCellTreeNode node, TInput param);
|
||||
internal abstract TOutput VisitInnerJoin(OpCellTreeNode node, TInput param);
|
||||
internal abstract TOutput VisitLeftOuterJoin(OpCellTreeNode node, TInput param);
|
||||
internal abstract TOutput VisitFullOuterJoin(OpCellTreeNode node, TInput param);
|
||||
internal abstract TOutput VisitLeftAntiSemiJoin(OpCellTreeNode node, TInput param);
|
||||
}
|
||||
|
||||
// Another abstract visitor that does not distinguish between different
|
||||
// operation nodes
|
||||
internal abstract class SimpleCellTreeVisitor<TInput, TOutput>
|
||||
{
|
||||
internal abstract TOutput VisitLeaf(LeafCellTreeNode node, TInput param);
|
||||
internal abstract TOutput VisitOpNode(OpCellTreeNode node, TInput param);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Default CellTree Visitor
|
||||
// Default visitor implementation for CellTreeVisitor
|
||||
// TInput is the type of the parameter that can be passed in to each visit
|
||||
// Returns a CellTreeVisitor as output
|
||||
private class DefaultCellTreeVisitor<TInput> : CellTreeVisitor<TInput, CellTreeNode>
|
||||
{
|
||||
|
||||
internal override CellTreeNode VisitLeaf(LeafCellTreeNode node, TInput param)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
internal override CellTreeNode VisitUnion(OpCellTreeNode node, TInput param)
|
||||
{
|
||||
return AcceptChildren(node, param);
|
||||
}
|
||||
|
||||
internal override CellTreeNode VisitInnerJoin(OpCellTreeNode node, TInput param)
|
||||
{
|
||||
return AcceptChildren(node, param);
|
||||
}
|
||||
|
||||
internal override CellTreeNode VisitLeftOuterJoin(OpCellTreeNode node, TInput param)
|
||||
{
|
||||
return AcceptChildren(node, param);
|
||||
}
|
||||
|
||||
internal override CellTreeNode VisitFullOuterJoin(OpCellTreeNode node, TInput param)
|
||||
{
|
||||
return AcceptChildren(node, param);
|
||||
}
|
||||
|
||||
internal override CellTreeNode VisitLeftAntiSemiJoin(OpCellTreeNode node, TInput param)
|
||||
{
|
||||
return AcceptChildren(node, param);
|
||||
}
|
||||
|
||||
private OpCellTreeNode AcceptChildren(OpCellTreeNode node, TInput param)
|
||||
{
|
||||
List<CellTreeNode> newChildren = new List<CellTreeNode>();
|
||||
foreach (CellTreeNode child in node.Children)
|
||||
{
|
||||
newChildren.Add(child.Accept(this, param));
|
||||
}
|
||||
return new OpCellTreeNode(node.ViewgenContext, node.OpType, newChildren);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Flattening Visitor
|
||||
// Flattens the tree, i.e., pushes up nodes that just have just one child
|
||||
private class FlatteningVisitor : SimpleCellTreeVisitor<bool, CellTreeNode>
|
||||
{
|
||||
#region Constructor/Fields/Invocation
|
||||
protected FlatteningVisitor()
|
||||
{
|
||||
}
|
||||
|
||||
// effects: Flattens node and returns a new tree that is flattened
|
||||
internal static CellTreeNode Flatten(CellTreeNode node)
|
||||
{
|
||||
FlatteningVisitor visitor = new FlatteningVisitor();
|
||||
return node.Accept<bool, CellTreeNode>(visitor, true);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Visitors
|
||||
internal override CellTreeNode VisitLeaf(LeafCellTreeNode node, bool dummy)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
// effects: Visits an internal Op node and processes it
|
||||
internal override CellTreeNode VisitOpNode(OpCellTreeNode node, bool dummy)
|
||||
{
|
||||
// Flatten the children first
|
||||
List<CellTreeNode> flattenedChildren = new List<CellTreeNode>();
|
||||
foreach (CellTreeNode child in node.Children)
|
||||
{
|
||||
CellTreeNode flattenedChild = child.Accept<bool, CellTreeNode>(this, dummy);
|
||||
flattenedChildren.Add(flattenedChild);
|
||||
}
|
||||
|
||||
Debug.Assert(flattenedChildren.Count >= 1, "node must have more than 1 child and be an OpCellTreeNode");
|
||||
// If only one child, return that
|
||||
if (flattenedChildren.Count == 1)
|
||||
{
|
||||
return flattenedChildren[0];
|
||||
}
|
||||
|
||||
Debug.Assert(flattenedChildren.Count > 1, "Opnode has 0 children?");
|
||||
Debug.Assert(node.OpType != CellTreeOpType.Leaf, "Wrong op type for operation node");
|
||||
|
||||
OpCellTreeNode result = new OpCellTreeNode(node.ViewgenContext, node.OpType, flattenedChildren);
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region AssociativeOpFlatteningVisitor
|
||||
// Flattens associative ops and single children nodes. Like the
|
||||
// FlatteningVisitor, it gets rid of the single children
|
||||
// nodes. Furthermore, it also collapses nodes of associative operations,
|
||||
// i.e., A IJ (B IJ C) is changed to A IJ B IJ C
|
||||
private class AssociativeOpFlatteningVisitor : SimpleCellTreeVisitor<bool, CellTreeNode>
|
||||
{
|
||||
#region Constructor/Fields/Invocation
|
||||
private AssociativeOpFlatteningVisitor()
|
||||
{
|
||||
}
|
||||
|
||||
internal static CellTreeNode Flatten(CellTreeNode node)
|
||||
{
|
||||
// First do simple flattening and then associative op flattening
|
||||
CellTreeNode newNode = FlatteningVisitor.Flatten(node);
|
||||
AssociativeOpFlatteningVisitor visitor = new AssociativeOpFlatteningVisitor();
|
||||
return newNode.Accept<bool, CellTreeNode>(visitor, true);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Visitors
|
||||
internal override CellTreeNode VisitLeaf(LeafCellTreeNode node, bool dummy)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
internal override CellTreeNode VisitOpNode(OpCellTreeNode node, bool dummy)
|
||||
{
|
||||
List<CellTreeNode> flattenedChildren = new List<CellTreeNode>();
|
||||
// Flatten the children first
|
||||
foreach (CellTreeNode child in node.Children)
|
||||
{
|
||||
CellTreeNode flattenedChild = child.Accept<bool, CellTreeNode>(this, dummy);
|
||||
flattenedChildren.Add(flattenedChild);
|
||||
}
|
||||
|
||||
Debug.Assert(flattenedChildren.Count > 1, "node must have more than 1 child and be an OpCellTreeNode");
|
||||
|
||||
// If this op is associative and a child's OP is the same as this
|
||||
// op, add those to be this nodes children
|
||||
List<CellTreeNode> finalChildren = flattenedChildren;
|
||||
if (CellTreeNode.IsAssociativeOp(node.OpType))
|
||||
{
|
||||
finalChildren = new List<CellTreeNode>();
|
||||
foreach (CellTreeNode child in flattenedChildren)
|
||||
{
|
||||
if (child.OpType == node.OpType)
|
||||
{
|
||||
finalChildren.AddRange(child.Children);
|
||||
}
|
||||
else
|
||||
{
|
||||
finalChildren.Add(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OpCellTreeNode result = new OpCellTreeNode(node.ViewgenContext, node.OpType, finalChildren);
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region LeafVisitor
|
||||
// This visitor returns all the leaf tree nodes in this
|
||||
private class LeafVisitor : SimpleCellTreeVisitor<bool, IEnumerable<LeafCellTreeNode>>
|
||||
{
|
||||
|
||||
private LeafVisitor() { }
|
||||
|
||||
internal static IEnumerable<LeafCellTreeNode> GetLeaves(CellTreeNode node)
|
||||
{
|
||||
LeafVisitor visitor = new LeafVisitor();
|
||||
return node.Accept<bool, IEnumerable<LeafCellTreeNode>>(visitor, true);
|
||||
}
|
||||
|
||||
internal override IEnumerable<LeafCellTreeNode> VisitLeaf(LeafCellTreeNode node, bool dummy)
|
||||
{
|
||||
yield return node;
|
||||
}
|
||||
|
||||
internal override IEnumerable<LeafCellTreeNode> VisitOpNode(OpCellTreeNode node, bool dummy)
|
||||
{
|
||||
foreach (CellTreeNode child in node.Children)
|
||||
{
|
||||
IEnumerable<LeafCellTreeNode> children = child.Accept<bool, IEnumerable<LeafCellTreeNode>>(this, dummy);
|
||||
foreach (LeafCellTreeNode leafNode in children)
|
||||
{
|
||||
yield return leafNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="CellTreeOpType.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner srimand
|
||||
// @backupOwner cmeek
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.Structures
|
||||
{
|
||||
|
||||
// This enum identifies for which side we are generating the view
|
||||
internal enum ViewTarget
|
||||
{
|
||||
QueryView,
|
||||
UpdateView
|
||||
}
|
||||
|
||||
// Different operations that are used in the CellTreeNode nodes
|
||||
internal enum CellTreeOpType
|
||||
{
|
||||
Leaf, // Leaf Node
|
||||
Union, // union all
|
||||
FOJ, // full outerjoin
|
||||
LOJ, // left outerjoin
|
||||
IJ, // inner join
|
||||
LASJ // left antisemijoin
|
||||
}
|
||||
}
|
@@ -0,0 +1,345 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="Constant.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.Structures
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common.CommandTrees;
|
||||
using System.Data.Common.CommandTrees.ExpressionBuilder;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Data.Mapping.ViewGeneration.CqlGeneration;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// This class denotes a constant that can be stored in multiconstants or projected in fields.
|
||||
/// </summary>
|
||||
internal abstract class Constant : InternalBase
|
||||
{
|
||||
#region Fields
|
||||
internal static readonly IEqualityComparer<Constant> EqualityComparer = new CellConstantComparer();
|
||||
internal static readonly Constant Null = NullConstant.Instance;
|
||||
internal static readonly Constant NotNull = new NegatedConstant( new Constant[] { NullConstant.Instance });
|
||||
internal static readonly Constant Undefined = UndefinedConstant.Instance;
|
||||
/// <summary>
|
||||
/// Represents scalar constants within a finite set that are not specified explicitly in the domain.
|
||||
/// Currently only used as a Sentinel node to prevent expression optimization
|
||||
/// </summary>
|
||||
internal static readonly Constant AllOtherConstants = AllOtherConstantsConstant.Instance;
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
internal abstract bool IsNull();
|
||||
|
||||
internal abstract bool IsNotNull();
|
||||
|
||||
internal abstract bool IsUndefined();
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this constant contains not null.
|
||||
/// Implemented in <see cref="NegatedConstant"/> class, all other implementations return false.
|
||||
/// </summary>
|
||||
internal abstract bool HasNotNull();
|
||||
|
||||
/// <summary>
|
||||
/// Generates eSQL for the constant expression.
|
||||
/// </summary>
|
||||
/// <param name="outputMember">The member to which this constant is directed</param>
|
||||
internal abstract StringBuilder AsEsql(StringBuilder builder, MemberPath outputMember, string blockAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Generates CQT for the constant expression.
|
||||
/// </summary>
|
||||
/// <param name="row">The input row.</param>
|
||||
/// <param name="outputMember">The member to which this constant is directed</param>
|
||||
internal abstract DbExpression AsCqt(DbExpression row, MemberPath outputMember);
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
Constant cellConst = obj as Constant;
|
||||
if (cellConst == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return IsEqualTo(cellConst);
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return base.GetHashCode();
|
||||
}
|
||||
|
||||
protected abstract bool IsEqualTo(Constant right);
|
||||
|
||||
internal abstract string ToUserString();
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal static void ConstantsToUserString(StringBuilder builder, Set<Constant> constants)
|
||||
{
|
||||
bool isFirst = true;
|
||||
foreach (Constant constant in constants)
|
||||
{
|
||||
if (isFirst == false)
|
||||
{
|
||||
builder.Append(System.Data.Entity.Strings.ViewGen_CommaBlank);
|
||||
}
|
||||
isFirst = false;
|
||||
string constrStr = constant.ToUserString();
|
||||
builder.Append(constrStr);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Comparer class
|
||||
private class CellConstantComparer : IEqualityComparer<Constant>
|
||||
{
|
||||
public bool Equals(Constant left, Constant right)
|
||||
{
|
||||
// Quick check with references
|
||||
if (object.ReferenceEquals(left, right))
|
||||
{
|
||||
// Gets the Null and Undefined case as well
|
||||
return true;
|
||||
}
|
||||
// One of them is non-null at least. So if the other one is
|
||||
// null, we cannot be equal
|
||||
if (left == null || right == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Both are non-null at this point
|
||||
return left.IsEqualTo(right);
|
||||
}
|
||||
|
||||
public int GetHashCode(Constant key)
|
||||
{
|
||||
EntityUtil.CheckArgumentNull(key, "key");
|
||||
return key.GetHashCode();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Special constant classes (NullConstant, UndefinedConstant, AllOtherConstants)
|
||||
private sealed class NullConstant : Constant
|
||||
{
|
||||
internal static readonly Constant Instance = new NullConstant();
|
||||
|
||||
private NullConstant() { }
|
||||
|
||||
#region Methods
|
||||
internal override bool IsNull()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
internal override bool IsNotNull()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
internal override bool IsUndefined()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
internal override bool HasNotNull()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
internal override StringBuilder AsEsql(StringBuilder builder, MemberPath outputMember, string blockAlias)
|
||||
{
|
||||
Debug.Assert(outputMember.LeafEdmMember != null, "Constant can't correspond to an empty member path.");
|
||||
EdmType constType = Helper.GetModelTypeUsage(outputMember.LeafEdmMember).EdmType;
|
||||
|
||||
builder.Append("CAST(NULL AS ");
|
||||
CqlWriter.AppendEscapedTypeName(builder, constType);
|
||||
builder.Append(')');
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal override DbExpression AsCqt(DbExpression row, MemberPath outputMember)
|
||||
{
|
||||
Debug.Assert(outputMember.LeafEdmMember != null, "Constant can't correspond to an empty path.");
|
||||
EdmType constType = Helper.GetModelTypeUsage(outputMember.LeafEdmMember).EdmType;
|
||||
|
||||
return TypeUsage.Create(constType).Null();
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected override bool IsEqualTo(Constant right)
|
||||
{
|
||||
Debug.Assert(Object.ReferenceEquals(this, Instance), "this must be == Instance for NullConstant");
|
||||
return Object.ReferenceEquals(this, right);
|
||||
}
|
||||
|
||||
internal override string ToUserString()
|
||||
{
|
||||
return System.Data.Entity.Strings.ViewGen_Null;
|
||||
}
|
||||
|
||||
internal override void ToCompactString(StringBuilder builder)
|
||||
{
|
||||
builder.Append("NULL");
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
private sealed class UndefinedConstant : Constant
|
||||
{
|
||||
internal static readonly Constant Instance = new UndefinedConstant();
|
||||
|
||||
private UndefinedConstant() { }
|
||||
|
||||
#region Methods
|
||||
internal override bool IsNull()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
internal override bool IsNotNull()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
internal override bool IsUndefined()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
internal override bool HasNotNull()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not supported in this class.
|
||||
/// </summary>
|
||||
internal override StringBuilder AsEsql(StringBuilder builder, MemberPath outputMember, string blockAlias)
|
||||
{
|
||||
Debug.Fail("Should not be called.");
|
||||
return null; // To keep the compiler happy
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not supported in this class.
|
||||
/// </summary>
|
||||
internal override DbExpression AsCqt(DbExpression row, MemberPath outputMember)
|
||||
{
|
||||
Debug.Fail("Should not be called.");
|
||||
return null; // To keep the compiler happy
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected override bool IsEqualTo(Constant right)
|
||||
{
|
||||
Debug.Assert(Object.ReferenceEquals(this, Instance), "this must be == Instance for NullConstant");
|
||||
return Object.ReferenceEquals(this, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not supported in this class.
|
||||
/// </summary>
|
||||
internal override string ToUserString()
|
||||
{
|
||||
Debug.Fail("We should not emit a message about Undefined constants to the user.");
|
||||
return null;
|
||||
}
|
||||
|
||||
internal override void ToCompactString(StringBuilder builder)
|
||||
{
|
||||
builder.Append("?");
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
private sealed class AllOtherConstantsConstant : Constant
|
||||
{
|
||||
internal static readonly Constant Instance = new AllOtherConstantsConstant();
|
||||
|
||||
private AllOtherConstantsConstant() { }
|
||||
|
||||
#region Methods
|
||||
internal override bool IsNull()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
internal override bool IsNotNull()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
internal override bool IsUndefined()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
internal override bool HasNotNull()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not supported in this class.
|
||||
/// </summary>
|
||||
internal override StringBuilder AsEsql(StringBuilder builder, MemberPath outputMember, string blockAlias)
|
||||
{
|
||||
Debug.Fail("Should not be called.");
|
||||
return null; // To keep the compiler happy
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not supported in this class.
|
||||
/// </summary>
|
||||
internal override DbExpression AsCqt(DbExpression row, MemberPath outputMember)
|
||||
{
|
||||
Debug.Fail("Should not be called.");
|
||||
return null; // To keep the compiler happy
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected override bool IsEqualTo(Constant right)
|
||||
{
|
||||
Debug.Assert(Object.ReferenceEquals(this, Instance), "this must be == Instance for NullConstant");
|
||||
return Object.ReferenceEquals(this, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not supported in this class.
|
||||
/// </summary>
|
||||
internal override string ToUserString()
|
||||
{
|
||||
Debug.Fail("We should not emit a message about Undefined constants to the user.");
|
||||
return null;
|
||||
}
|
||||
|
||||
internal override void ToCompactString(StringBuilder builder)
|
||||
{
|
||||
builder.Append("AllOtherConstants");
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="ConstantProjectedSlot.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System.Data.Mapping.ViewGeneration.CqlGeneration;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using System.Data.Common.CommandTrees;
|
||||
using System.Data.Common.CommandTrees.ExpressionBuilder;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.Structures
|
||||
{
|
||||
/// <summary>
|
||||
/// A constant that can be projected in a cell query.
|
||||
/// </summary>
|
||||
internal sealed class ConstantProjectedSlot : ProjectedSlot
|
||||
{
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Creates a slot with constant value being <paramref name="value"/>.
|
||||
/// </summary>
|
||||
internal ConstantProjectedSlot(Constant value, MemberPath memberPath)
|
||||
{
|
||||
Debug.Assert(value != null);
|
||||
Debug.Assert(value.IsNotNull() == false, "Cannot store NotNull in a slot - NotNull is only for conditions");
|
||||
m_constant = value;
|
||||
m_memberPath = memberPath;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
/// <summary>
|
||||
/// The actual value.
|
||||
/// </summary>
|
||||
private readonly Constant m_constant;
|
||||
private readonly MemberPath m_memberPath;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// Returns the value stored in this constant.
|
||||
/// </summary>
|
||||
internal Constant CellConstant
|
||||
{
|
||||
get { return m_constant; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
internal override ProjectedSlot DeepQualify(CqlBlock block)
|
||||
{
|
||||
return this; // Nothing to create
|
||||
}
|
||||
|
||||
internal override StringBuilder AsEsql(StringBuilder builder, MemberPath outputMember, string blockAlias, int indentLevel)
|
||||
{
|
||||
return m_constant.AsEsql(builder, outputMember, blockAlias);
|
||||
}
|
||||
|
||||
internal override DbExpression AsCqt(DbExpression row, MemberPath outputMember)
|
||||
{
|
||||
return m_constant.AsCqt(row, outputMember);
|
||||
}
|
||||
|
||||
protected override bool IsEqualTo(ProjectedSlot right)
|
||||
{
|
||||
ConstantProjectedSlot rightSlot = right as ConstantProjectedSlot;
|
||||
if (rightSlot == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Constant.EqualityComparer.Equals(m_constant, rightSlot.m_constant);
|
||||
}
|
||||
|
||||
protected override int GetHash()
|
||||
{
|
||||
return Constant.EqualityComparer.GetHashCode(m_constant);
|
||||
}
|
||||
|
||||
internal override void ToCompactString(StringBuilder builder)
|
||||
{
|
||||
m_constant.ToCompactString(builder);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,245 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="ErrorLog.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.Structures
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Data.Entity;
|
||||
using System.Data.Mapping.ViewGeneration.Utils;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
internal class ErrorLog : InternalBase
|
||||
{
|
||||
|
||||
#region Constructors
|
||||
internal ErrorLog()
|
||||
{
|
||||
m_log = new List<Record>();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private List<Record> m_log;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
internal int Count
|
||||
{
|
||||
get { return m_log.Count; }
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] // referenced (indirectly) by System.Data.Entity.Design.dll
|
||||
internal IEnumerable<EdmSchemaError> Errors
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (Record record in m_log)
|
||||
{
|
||||
yield return record.Error;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
internal void AddEntry(Record record)
|
||||
{
|
||||
EntityUtil.CheckArgumentNull(record, "record");
|
||||
m_log.Add(record);
|
||||
}
|
||||
|
||||
internal void Merge(ErrorLog log)
|
||||
{
|
||||
foreach (Record record in log.m_log)
|
||||
{
|
||||
m_log.Add(record);
|
||||
}
|
||||
}
|
||||
|
||||
internal void PrintTrace()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
ToCompactString(builder);
|
||||
Helpers.StringTraceLine(builder.ToString());
|
||||
}
|
||||
|
||||
internal override void ToCompactString(StringBuilder builder)
|
||||
{
|
||||
foreach (Record record in m_log)
|
||||
{
|
||||
record.ToCompactString(builder);
|
||||
}
|
||||
}
|
||||
|
||||
internal string ToUserString()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
foreach (Record record in m_log)
|
||||
{
|
||||
string recordString = record.ToUserString();
|
||||
builder.AppendLine(recordString);
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Nested classes/struct
|
||||
internal class Record : InternalBase
|
||||
{
|
||||
#region Constructor
|
||||
// effects: Creates an error record for wrappers, a debug message
|
||||
// and an error message given by "message". Note: wrappers cannot
|
||||
// be null
|
||||
internal Record(bool isError, ViewGenErrorCode errorCode, string message,
|
||||
IEnumerable<LeftCellWrapper> wrappers, string debugMessage)
|
||||
{
|
||||
Debug.Assert(wrappers != null);
|
||||
IEnumerable<Cell> cells = LeftCellWrapper.GetInputCellsForWrappers(wrappers);
|
||||
Init(isError, errorCode, message, cells, debugMessage);
|
||||
}
|
||||
|
||||
internal Record(bool isError, ViewGenErrorCode errorCode, string message, Cell sourceCell, string debugMessage)
|
||||
{
|
||||
Init(isError, errorCode, message, new Cell[] { sourceCell }, debugMessage);
|
||||
}
|
||||
|
||||
internal Record(bool isError, ViewGenErrorCode errorCode, string message, IEnumerable<Cell> sourceCells,
|
||||
string debugMessage)
|
||||
{
|
||||
Init(isError, errorCode, message, sourceCells, debugMessage);
|
||||
}
|
||||
|
||||
//There are cases when we want to create a ViewGen error that is not specific to any mapping fragment
|
||||
//In this case, it is better to just create the EdmSchemaError directly and hold on to it.
|
||||
internal Record(EdmSchemaError error)
|
||||
{
|
||||
m_debugMessage = error.ToString();
|
||||
m_mappingError = error;
|
||||
}
|
||||
|
||||
|
||||
private void Init(bool isError, ViewGenErrorCode errorCode, string message,
|
||||
IEnumerable<Cell> sourceCells, string debugMessage)
|
||||
{
|
||||
m_sourceCells = new List<Cell>(sourceCells);
|
||||
|
||||
Debug.Assert(m_sourceCells.Count > 0, "Error record must have at least one cell");
|
||||
|
||||
// For certain foreign key messages, we may need the SSDL line numbers and file names
|
||||
CellLabel label = m_sourceCells[0].CellLabel;
|
||||
string sourceLocation = label.SourceLocation;
|
||||
int lineNumber = label.StartLineNumber;
|
||||
int columnNumber = label.StartLinePosition;
|
||||
|
||||
string userMessage = InternalToString(message, debugMessage, m_sourceCells, sourceLocation, errorCode, isError, false);
|
||||
m_debugMessage = InternalToString(message, debugMessage, m_sourceCells, sourceLocation, errorCode, isError, true);
|
||||
m_mappingError = new EdmSchemaError(userMessage, (int)errorCode, EdmSchemaErrorSeverity.Error, sourceLocation,
|
||||
lineNumber, columnNumber);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private EdmSchemaError m_mappingError;
|
||||
private List<Cell> m_sourceCells;
|
||||
private string m_debugMessage;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] // referenced (indirectly) by System.Data.Entity.Design.dll
|
||||
internal EdmSchemaError Error
|
||||
{
|
||||
get { return m_mappingError; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
internal override void ToCompactString(StringBuilder builder)
|
||||
{
|
||||
builder.Append(m_debugMessage);
|
||||
}
|
||||
|
||||
// effects: adds a comma-separated list of line numbers to the string builder
|
||||
private static void GetUserLinesFromCells(IEnumerable<Cell> sourceCells, StringBuilder lineBuilder, bool isInvariant)
|
||||
{
|
||||
var orderedCells = sourceCells.OrderBy<Cell, int>(cell => cell.CellLabel.StartLineNumber, Comparer<int>.Default);
|
||||
|
||||
bool isFirst = true;
|
||||
// Get the line numbers
|
||||
foreach (Cell cell in orderedCells)
|
||||
{
|
||||
if (isFirst == false)
|
||||
{
|
||||
lineBuilder.Append(isInvariant ? EntityRes.GetString(EntityRes.ViewGen_CommaBlank) : ", ");
|
||||
}
|
||||
isFirst = false;
|
||||
lineBuilder.AppendFormat(CultureInfo.InvariantCulture, "{0}", cell.CellLabel.StartLineNumber);
|
||||
}
|
||||
Debug.Assert(isFirst == false, "No cells");
|
||||
}
|
||||
|
||||
// effects: Converts the message/debugMessage to a user-readable
|
||||
// message using resources (if isInvariant is false) or a test
|
||||
// message (if isInvariant is true)
|
||||
static private string InternalToString(string message, string debugMessage,
|
||||
List<Cell> sourceCells, string sourceLocation, ViewGenErrorCode errorCode,
|
||||
bool isError, bool isInvariant)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
if (isInvariant)
|
||||
{
|
||||
builder.AppendLine(debugMessage);
|
||||
|
||||
builder.Append(isInvariant ? "ERROR" : System.Data.Entity.Strings.ViewGen_Error);
|
||||
StringUtil.FormatStringBuilder(builder, " ({0}): ", (int)errorCode);
|
||||
}
|
||||
|
||||
StringBuilder lineBuilder = new StringBuilder();
|
||||
GetUserLinesFromCells(sourceCells, lineBuilder, isInvariant);
|
||||
|
||||
if (isInvariant)
|
||||
{
|
||||
if (sourceCells.Count > 1)
|
||||
{
|
||||
StringUtil.FormatStringBuilder(builder, "Problem in Mapping Fragments starting at lines {0}: ", lineBuilder.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
StringUtil.FormatStringBuilder(builder, "Problem in Mapping Fragment starting at line {0}: ", lineBuilder.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sourceCells.Count > 1)
|
||||
{
|
||||
builder.Append(Strings.ViewGen_ErrorLog2(lineBuilder.ToString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append(Strings.ViewGen_ErrorLog(lineBuilder.ToString()));
|
||||
}
|
||||
}
|
||||
builder.AppendLine(message);
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
internal string ToUserString()
|
||||
{
|
||||
return m_mappingError.ToString();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,319 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="LeafCellTreeNode.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Data.Mapping.ViewGeneration.CqlGeneration;
|
||||
using System.Data.Mapping.ViewGeneration.QueryRewriting;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using System.Data.Metadata.Edm;
|
||||
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.Structures
|
||||
{
|
||||
// This class represents the nodes that reside at the leaves of the tree
|
||||
internal class LeafCellTreeNode : CellTreeNode
|
||||
{
|
||||
|
||||
#region Constructor
|
||||
// effects: Encapsulate the cell wrapper in the node
|
||||
internal LeafCellTreeNode(ViewgenContext context, LeftCellWrapper cellWrapper)
|
||||
: base(context)
|
||||
{
|
||||
m_cellWrapper = cellWrapper;
|
||||
m_leftFragmentQuery = cellWrapper.FragmentQuery;
|
||||
cellWrapper.AssertHasUniqueCell();
|
||||
m_rightFragmentQuery = FragmentQuery.Create(
|
||||
cellWrapper.OriginalCellNumberString,
|
||||
cellWrapper.CreateRoleBoolean(),
|
||||
cellWrapper.RightCellQuery);
|
||||
}
|
||||
internal LeafCellTreeNode(ViewgenContext context, LeftCellWrapper cellWrapper, FragmentQuery rightFragmentQuery)
|
||||
: base(context)
|
||||
{
|
||||
m_cellWrapper = cellWrapper;
|
||||
m_leftFragmentQuery = cellWrapper.FragmentQuery;
|
||||
m_rightFragmentQuery = rightFragmentQuery;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
internal static readonly IEqualityComparer<LeafCellTreeNode> EqualityComparer = new LeafCellTreeNodeComparer();
|
||||
|
||||
// The cell at the leaf level
|
||||
private LeftCellWrapper m_cellWrapper;
|
||||
private FragmentQuery m_leftFragmentQuery;
|
||||
private FragmentQuery m_rightFragmentQuery;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
internal LeftCellWrapper LeftCellWrapper
|
||||
{
|
||||
get { return m_cellWrapper; }
|
||||
}
|
||||
|
||||
internal override MemberDomainMap RightDomainMap
|
||||
{
|
||||
get { return m_cellWrapper.RightDomainMap; }
|
||||
}
|
||||
|
||||
// effects: See CellTreeNode.FragmentQuery
|
||||
internal override FragmentQuery LeftFragmentQuery { get { return m_cellWrapper.FragmentQuery; } }
|
||||
|
||||
internal override FragmentQuery RightFragmentQuery
|
||||
{
|
||||
get
|
||||
{
|
||||
Debug.Assert(m_rightFragmentQuery != null, "Unassigned right fragment query");
|
||||
return m_rightFragmentQuery;
|
||||
}
|
||||
}
|
||||
|
||||
// effects: See CellTreeNode.Attributes
|
||||
internal override Set<MemberPath> Attributes { get { return m_cellWrapper.Attributes; } }
|
||||
|
||||
// effects: See CellTreeNode.Children
|
||||
internal override List<CellTreeNode> Children { get { return new List<CellTreeNode>(); } }
|
||||
|
||||
// effects: See CellTreeNode.OpType
|
||||
internal override CellTreeOpType OpType { get { return CellTreeOpType.Leaf; } }
|
||||
|
||||
internal override int NumProjectedSlots
|
||||
{
|
||||
get { return LeftCellWrapper.RightCellQuery.NumProjectedSlots; }
|
||||
}
|
||||
|
||||
internal override int NumBoolSlots
|
||||
{
|
||||
get { return LeftCellWrapper.RightCellQuery.NumBoolVars; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
internal override TOutput Accept<TInput, TOutput>(CellTreeVisitor<TInput, TOutput> visitor, TInput param)
|
||||
{
|
||||
return visitor.VisitLeaf(this, param);
|
||||
}
|
||||
|
||||
internal override TOutput Accept<TInput, TOutput>(SimpleCellTreeVisitor<TInput, TOutput> visitor, TInput param)
|
||||
{
|
||||
return visitor.VisitLeaf(this, param);
|
||||
}
|
||||
|
||||
internal override bool IsProjectedSlot(int slot)
|
||||
{
|
||||
CellQuery cellQuery = LeftCellWrapper.RightCellQuery;
|
||||
if (IsBoolSlot(slot))
|
||||
{
|
||||
return cellQuery.GetBoolVar(SlotToBoolIndex(slot)) != null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return cellQuery.ProjectedSlotAt(slot) != null;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Leaf CqlBlock Methods
|
||||
internal override CqlBlock ToCqlBlock(bool[] requiredSlots, CqlIdentifiers identifiers, ref int blockAliasNum, ref List<WithRelationship> withRelationships)
|
||||
{
|
||||
// Get the projected slots and the boolean expressions
|
||||
int totalSlots = requiredSlots.Length;
|
||||
CellQuery cellQuery = LeftCellWrapper.RightCellQuery;
|
||||
|
||||
SlotInfo[] projectedSlots = new SlotInfo[totalSlots];
|
||||
Debug.Assert(cellQuery.NumProjectedSlots + cellQuery.NumBoolVars == totalSlots,
|
||||
"Wrong number of projected slots in node");
|
||||
|
||||
Debug.Assert(cellQuery.NumProjectedSlots == ProjectedSlotMap.Count,
|
||||
"Different number of slots in cell query and what we have mappings for");
|
||||
// Add the regular fields
|
||||
for (int i = 0; i < cellQuery.NumProjectedSlots; i++)
|
||||
{
|
||||
ProjectedSlot slot = cellQuery.ProjectedSlotAt(i);
|
||||
// If the slot is not null, we will project it
|
||||
// For extents, we say that all requiredlots are the only the
|
||||
// ones that are CLR non-null. Recall that "real" nulls are
|
||||
// handled by having a CellConstant.Null in ConstantSlot
|
||||
if (requiredSlots[i] && slot == null)
|
||||
{
|
||||
MemberPath memberPath = ProjectedSlotMap[i];
|
||||
ConstantProjectedSlot defaultValue = new ConstantProjectedSlot(Domain.GetDefaultValueForMemberPath(memberPath, GetLeaves(), ViewgenContext.Config), memberPath);
|
||||
cellQuery.FixMissingSlotAsDefaultConstant(i, defaultValue);
|
||||
slot = defaultValue;
|
||||
}
|
||||
SlotInfo slotInfo = new SlotInfo(requiredSlots[i], slot != null,
|
||||
slot, ProjectedSlotMap[i]);
|
||||
projectedSlots[i] = slotInfo;
|
||||
}
|
||||
|
||||
// Add the boolean fields
|
||||
for (int boolNum = 0; boolNum < cellQuery.NumBoolVars; boolNum++)
|
||||
{
|
||||
BoolExpression expr = cellQuery.GetBoolVar(boolNum);
|
||||
BooleanProjectedSlot boolSlot;
|
||||
if (expr != null)
|
||||
{
|
||||
boolSlot = new BooleanProjectedSlot(expr, identifiers, boolNum);
|
||||
}
|
||||
else
|
||||
{
|
||||
boolSlot = new BooleanProjectedSlot(BoolExpression.False, identifiers, boolNum);
|
||||
}
|
||||
int slotIndex = BoolIndexToSlot(boolNum);
|
||||
SlotInfo slotInfo = new SlotInfo(requiredSlots[slotIndex], expr != null,
|
||||
boolSlot, null);
|
||||
projectedSlots[slotIndex] = slotInfo;
|
||||
}
|
||||
|
||||
// See if we are generating a query view and whether there are any colocated foreign keys for which
|
||||
// we have to add With statements.
|
||||
IEnumerable<SlotInfo> totalProjectedSlots = projectedSlots;
|
||||
if ((cellQuery.Extent.EntityContainer.DataSpace == DataSpace.SSpace)
|
||||
&& (this.m_cellWrapper.LeftExtent.BuiltInTypeKind == BuiltInTypeKind.EntitySet))
|
||||
{
|
||||
IEnumerable<StorageAssociationSetMapping> associationSetMaps =
|
||||
this.ViewgenContext.EntityContainerMapping.GetRelationshipSetMappingsFor(this.m_cellWrapper.LeftExtent, cellQuery.Extent);
|
||||
List<SlotInfo> foreignKeySlots = new List<SlotInfo>();
|
||||
foreach (StorageAssociationSetMapping colocatedAssociationSetMap in associationSetMaps)
|
||||
{
|
||||
WithRelationship withRelationship;
|
||||
if (TryGetWithRelationship(colocatedAssociationSetMap, this.m_cellWrapper.LeftExtent, cellQuery.SourceExtentMemberPath, ref foreignKeySlots, out withRelationship))
|
||||
{
|
||||
withRelationships.Add(withRelationship);
|
||||
totalProjectedSlots = projectedSlots.Concat(foreignKeySlots);
|
||||
}
|
||||
}
|
||||
}
|
||||
ExtentCqlBlock result = new ExtentCqlBlock(cellQuery.Extent, cellQuery.SelectDistinctFlag, totalProjectedSlots.ToArray(),
|
||||
cellQuery.WhereClause, identifiers, ++blockAliasNum);
|
||||
return result;
|
||||
}
|
||||
|
||||
private bool TryGetWithRelationship(StorageAssociationSetMapping colocatedAssociationSetMap,
|
||||
EntitySetBase thisExtent,
|
||||
MemberPath sRootNode,
|
||||
ref List<SlotInfo> foreignKeySlots,
|
||||
out WithRelationship withRelationship)
|
||||
{
|
||||
Debug.Assert(foreignKeySlots != null);
|
||||
withRelationship = null;
|
||||
|
||||
//Get the map for foreign key end
|
||||
StorageEndPropertyMapping foreignKeyEndMap = GetForeignKeyEndMapFromAssocitionMap(colocatedAssociationSetMap, thisExtent);
|
||||
if (foreignKeyEndMap == null || foreignKeyEndMap.EndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AssociationEndMember toEnd = (AssociationEndMember)foreignKeyEndMap.EndMember;
|
||||
AssociationEndMember fromEnd = MetadataHelper.GetOtherAssociationEnd(toEnd);
|
||||
EntityType toEndEntityType = (EntityType)((RefType)(toEnd.TypeUsage.EdmType)).ElementType;
|
||||
EntityType fromEndEntityType = (EntityType)(((RefType)fromEnd.TypeUsage.EdmType).ElementType);
|
||||
|
||||
// Get the member path for AssociationSet
|
||||
AssociationSet associationSet = (AssociationSet)colocatedAssociationSetMap.Set;
|
||||
MemberPath prefix = new MemberPath(associationSet, toEnd);
|
||||
|
||||
// Collect the member paths for edm scalar properties that belong to the target entity key.
|
||||
// These will be used as part of WITH RELATIONSHIP.
|
||||
// Get the key properties from edm type since the query parser depends on the order of key members
|
||||
IEnumerable<StorageScalarPropertyMapping> propertyMaps = foreignKeyEndMap.Properties.Cast<StorageScalarPropertyMapping>();
|
||||
List<MemberPath> toEndEntityKeyMemberPaths = new List<MemberPath>();
|
||||
foreach (EdmProperty edmProperty in toEndEntityType.KeyMembers)
|
||||
{
|
||||
IEnumerable<StorageScalarPropertyMapping> scalarPropertyMaps = propertyMaps.Where(propMap => (propMap.EdmProperty.Equals(edmProperty)));
|
||||
Debug.Assert(scalarPropertyMaps.Count() == 1, "Can't Map the same column multiple times in the same end");
|
||||
StorageScalarPropertyMapping scalarPropertyMap = scalarPropertyMaps.First();
|
||||
|
||||
// Create SlotInfo for Freign Key member that needs to be projected.
|
||||
MemberProjectedSlot sSlot = new MemberProjectedSlot(new MemberPath(sRootNode, scalarPropertyMap.ColumnProperty));
|
||||
MemberPath endMemberKeyPath = new MemberPath(prefix, edmProperty);
|
||||
toEndEntityKeyMemberPaths.Add(endMemberKeyPath);
|
||||
foreignKeySlots.Add(new SlotInfo(true, true, sSlot, endMemberKeyPath));
|
||||
}
|
||||
|
||||
// Parent assignable from child: Ensures they are in the same hierarchy.
|
||||
if (thisExtent.ElementType.IsAssignableFrom(fromEndEntityType))
|
||||
{
|
||||
// Now create the WITH RELATIONSHIP with all the needed info.
|
||||
withRelationship = new WithRelationship(associationSet, fromEnd, fromEndEntityType, toEnd, toEndEntityType, toEndEntityKeyMemberPaths);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//Gets the end that is not mapped to the primary key of the table
|
||||
private StorageEndPropertyMapping GetForeignKeyEndMapFromAssocitionMap(StorageAssociationSetMapping colocatedAssociationSetMap, EntitySetBase thisExtent)
|
||||
{
|
||||
StorageMappingFragment mapFragment = colocatedAssociationSetMap.TypeMappings.First().MappingFragments.First();
|
||||
EntitySet storeEntitySet = (EntitySet)(colocatedAssociationSetMap.StoreEntitySet);
|
||||
IEnumerable<EdmMember> keyProperties = storeEntitySet.ElementType.KeyMembers;
|
||||
//Find the end that's mapped to primary key
|
||||
foreach (StorageEndPropertyMapping endMap in mapFragment.Properties)
|
||||
{
|
||||
IEnumerable<EdmMember> endStoreMembers = endMap.StoreProperties;
|
||||
if (endStoreMembers.SequenceEqual(keyProperties, EqualityComparer<EdmMember>.Default))
|
||||
{
|
||||
//Return the map for the other end since that is the foreign key end
|
||||
IEnumerable<StorageEndPropertyMapping> otherEnds = mapFragment.Properties.OfType<StorageEndPropertyMapping>().Where(eMap => (!eMap.Equals(endMap)));
|
||||
Debug.Assert(otherEnds.Count() == 1);
|
||||
return otherEnds.First();
|
||||
}
|
||||
}
|
||||
//This is probably defensive, but there should be no problem in falling back on the
|
||||
//AssociationSetMap if colocated foreign key is not found for some reason.
|
||||
return null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region String Methods
|
||||
// effects: See CellTreeNode.ToString
|
||||
internal override void ToCompactString(StringBuilder stringBuilder)
|
||||
{
|
||||
m_cellWrapper.ToCompactString(stringBuilder);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEqualityComparer<LeafCellTreeNode>
|
||||
// A comparer that equates leaf nodes if the wrapper is the same
|
||||
private class LeafCellTreeNodeComparer : IEqualityComparer<LeafCellTreeNode>
|
||||
{
|
||||
|
||||
public bool Equals(LeafCellTreeNode left, LeafCellTreeNode right)
|
||||
{
|
||||
// Quick check with references
|
||||
if (object.ReferenceEquals(left, right))
|
||||
{
|
||||
// Gets the Null and Undefined case as well
|
||||
return true;
|
||||
}
|
||||
// One of them is non-null at least
|
||||
if (left == null || right == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Both are non-null at this point
|
||||
return left.m_cellWrapper.Equals(right.m_cellWrapper);
|
||||
}
|
||||
|
||||
public int GetHashCode(LeafCellTreeNode node)
|
||||
{
|
||||
return node.m_cellWrapper.GetHashCode();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,416 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="LeftCellWrapper.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Data.Common.Utils.Boolean;
|
||||
using System.Data.Mapping.ViewGeneration.Validation;
|
||||
using System.Data.Mapping.ViewGeneration.QueryRewriting;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.Structures
|
||||
{
|
||||
|
||||
// This class essentially stores a cell but in a special form. When we
|
||||
// are generating a view for an extent, we denote the extent's side (C or
|
||||
// S) as the "left side" and the side being used in the view as the right
|
||||
// side. For example, in query views, the C side is the left side.
|
||||
//
|
||||
// Each LeftCellWrapper is a cell of the form:
|
||||
// Project[A1,...,An] (Select[var IN {domain}] (Extent)) = Expr
|
||||
// Where
|
||||
// - "domain" is a set of multiconstants that correspond to the different
|
||||
// variable values allowed for the cell query
|
||||
// - A1 ... An are denoted by Attributes in this and corresponds to
|
||||
// the list of attributes that are projected
|
||||
// - Extent is the extent for which th view is being generated
|
||||
// - Expr is the expression on the other side to produce the left side of
|
||||
// the cell
|
||||
internal class LeftCellWrapper : InternalBase
|
||||
{
|
||||
|
||||
#region Fields
|
||||
internal static readonly IEqualityComparer<LeftCellWrapper> BoolEqualityComparer = new BoolWrapperComparer();
|
||||
|
||||
private Set<MemberPath> m_attributes;// project: attributes computed by
|
||||
|
||||
// Expr (projected attributes that get set)
|
||||
private MemberMaps m_memberMaps;
|
||||
private CellQuery m_leftCellQuery; // expression that computes this portion
|
||||
private CellQuery m_rightCellQuery; // expression that computes this portion
|
||||
|
||||
private HashSet<Cell> m_mergedCells; // Cells that this LeftCellWrapper (MergedCell) wraps.
|
||||
// At first it starts off with a single cell and during cell merging
|
||||
// cells from both LeftCellWrappers are concatenated.
|
||||
private ViewTarget m_viewTarget;
|
||||
private FragmentQuery m_leftFragmentQuery; // Fragment query corresponding to the left cell query of the cell
|
||||
|
||||
internal static readonly IComparer<LeftCellWrapper> Comparer = new LeftCellWrapperComparer();
|
||||
internal static readonly IComparer<LeftCellWrapper> OriginalCellIdComparer = new CellIdComparer();
|
||||
#endregion
|
||||
|
||||
|
||||
#region Constructor
|
||||
// effects: Creates a LeftCellWrapper of the form:
|
||||
// Project[attrs] (Select[var IN {domain}] (Extent)) = cellquery
|
||||
// memberMaps is the set of maps used for producing the query or update views
|
||||
internal LeftCellWrapper(ViewTarget viewTarget, Set<MemberPath> attrs,
|
||||
FragmentQuery fragmentQuery,
|
||||
CellQuery leftCellQuery, CellQuery rightCellQuery, MemberMaps memberMaps, IEnumerable<Cell> inputCells)
|
||||
{
|
||||
m_leftFragmentQuery = fragmentQuery;
|
||||
m_rightCellQuery = rightCellQuery;
|
||||
m_leftCellQuery = leftCellQuery;
|
||||
m_attributes = attrs;
|
||||
m_viewTarget = viewTarget;
|
||||
m_memberMaps = memberMaps;
|
||||
m_mergedCells = new HashSet<Cell>(inputCells);
|
||||
}
|
||||
|
||||
internal LeftCellWrapper(ViewTarget viewTarget, Set<MemberPath> attrs,
|
||||
FragmentQuery fragmentQuery,
|
||||
CellQuery leftCellQuery, CellQuery rightCellQuery, MemberMaps memberMaps, Cell inputCell)
|
||||
: this(viewTarget, attrs, fragmentQuery, leftCellQuery, rightCellQuery, memberMaps, Enumerable.Repeat(inputCell, 1)) { }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region Properties
|
||||
|
||||
internal FragmentQuery FragmentQuery
|
||||
{
|
||||
get { return m_leftFragmentQuery; }
|
||||
}
|
||||
|
||||
// effects: Returns the projected fields on the left side
|
||||
internal Set<MemberPath> Attributes
|
||||
{
|
||||
get { return m_attributes; }
|
||||
}
|
||||
|
||||
// effects: Returns the original cell number from which the wrapper came
|
||||
internal string OriginalCellNumberString
|
||||
{
|
||||
get
|
||||
{
|
||||
return StringUtil.ToSeparatedString(m_mergedCells.Select(cell => cell.CellNumberAsString), "+", "");
|
||||
}
|
||||
}
|
||||
|
||||
// effects: Returns the right domain map associated with the right query
|
||||
internal MemberDomainMap RightDomainMap
|
||||
{
|
||||
get { return m_memberMaps.RightDomainMap; }
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
internal void AssertHasUniqueCell()
|
||||
{
|
||||
Debug.Assert(m_mergedCells.Count == 1);
|
||||
}
|
||||
|
||||
internal IEnumerable<Cell> Cells
|
||||
{
|
||||
get { return m_mergedCells; }
|
||||
}
|
||||
|
||||
// requires: There is only one input cell in this
|
||||
// effects: Returns the input cell provided to view generation as part of the mapping
|
||||
internal Cell OnlyInputCell
|
||||
{
|
||||
get
|
||||
{
|
||||
AssertHasUniqueCell();
|
||||
return m_mergedCells.First();
|
||||
}
|
||||
}
|
||||
|
||||
// effects: Returns the right CellQuery
|
||||
internal CellQuery RightCellQuery
|
||||
{
|
||||
get { return m_rightCellQuery; }
|
||||
}
|
||||
|
||||
internal CellQuery LeftCellQuery
|
||||
{
|
||||
get { return m_leftCellQuery; }
|
||||
}
|
||||
|
||||
|
||||
// effects: Returns the extent for which the wrapper was built
|
||||
internal EntitySetBase LeftExtent
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_mergedCells.First().GetLeftQuery(m_viewTarget).Extent;
|
||||
}
|
||||
}
|
||||
|
||||
// effects: Returns the extent of the right cellquery
|
||||
internal EntitySetBase RightExtent
|
||||
{
|
||||
get
|
||||
{
|
||||
EntitySetBase result = m_rightCellQuery.Extent;
|
||||
Debug.Assert(result != null, "Bad root value in join tree");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
// effects: Yields the input cells in wrappers
|
||||
internal static IEnumerable<Cell> GetInputCellsForWrappers(IEnumerable<LeftCellWrapper> wrappers)
|
||||
{
|
||||
foreach (LeftCellWrapper wrapper in wrappers)
|
||||
{
|
||||
foreach (Cell cell in wrapper.m_mergedCells)
|
||||
{
|
||||
yield return cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// effects: Creates a boolean variable representing the right extent or association end
|
||||
internal RoleBoolean CreateRoleBoolean()
|
||||
{
|
||||
if (RightExtent is AssociationSet)
|
||||
{
|
||||
Set<AssociationEndMember> ends = GetEndsForTablePrimaryKey();
|
||||
if (ends.Count == 1)
|
||||
{
|
||||
AssociationSetEnd setEnd = ((AssociationSet)RightExtent).AssociationSetEnds[ends.First().Name];
|
||||
return new RoleBoolean(setEnd);
|
||||
}
|
||||
}
|
||||
return new RoleBoolean(RightExtent);
|
||||
}
|
||||
|
||||
// effects: Given a set of wrappers, returns a string that contains the list of extents in the
|
||||
// rightcellQueries of the wrappers
|
||||
internal static string GetExtentListAsUserString(IEnumerable<LeftCellWrapper> wrappers)
|
||||
{
|
||||
Set<EntitySetBase> extents = new Set<EntitySetBase>(EqualityComparer<EntitySetBase>.Default);
|
||||
foreach (LeftCellWrapper wrapper in wrappers)
|
||||
{
|
||||
extents.Add(wrapper.RightExtent);
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
bool isFirst = true;
|
||||
foreach (EntitySetBase extent in extents)
|
||||
{
|
||||
if (isFirst == false)
|
||||
{
|
||||
builder.Append(", ");
|
||||
}
|
||||
isFirst = false;
|
||||
builder.Append(extent.Name);
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
internal override void ToFullString(StringBuilder builder)
|
||||
{
|
||||
builder.Append("P[");
|
||||
StringUtil.ToSeparatedString(builder, m_attributes, ",");
|
||||
builder.Append("] = ");
|
||||
m_rightCellQuery.ToFullString(builder);
|
||||
}
|
||||
|
||||
// effects: Modifies stringBuilder to contain the view corresponding
|
||||
// to the right cellquery
|
||||
internal override void ToCompactString(StringBuilder stringBuilder)
|
||||
{
|
||||
stringBuilder.Append(OriginalCellNumberString);
|
||||
}
|
||||
|
||||
// effects: Writes m_cellWrappers to builder
|
||||
internal static void WrappersToStringBuilder(StringBuilder builder, List<LeftCellWrapper> wrappers,
|
||||
string header)
|
||||
{
|
||||
builder.AppendLine()
|
||||
.Append(header)
|
||||
.AppendLine();
|
||||
// Sort them according to the original cell number
|
||||
LeftCellWrapper[] cellWrappers = wrappers.ToArray();
|
||||
Array.Sort(cellWrappers, LeftCellWrapper.OriginalCellIdComparer);
|
||||
|
||||
foreach (LeftCellWrapper wrapper in cellWrappers)
|
||||
{
|
||||
wrapper.ToCompactString(builder);
|
||||
builder.Append(" = ");
|
||||
wrapper.ToFullString(builder);
|
||||
builder.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// requires: RightCellQuery.Extent corresponds to a relationship set
|
||||
// effects: Returns the ends to which the key of the corresponding
|
||||
// table (i.e., the left query) maps to in the relationship set. For
|
||||
// example, if RightCellQuery.Extent is OrderOrders and it maps to
|
||||
// <oid, otherOid> of table SOrders with key oid, this returns the
|
||||
// end to which oid is mapped. Similarly, if we have a link table
|
||||
// with the whole key mapped to two ends of the association set, it
|
||||
// returns both ends
|
||||
private Set<AssociationEndMember> GetEndsForTablePrimaryKey()
|
||||
{
|
||||
CellQuery rightQuery = RightCellQuery;
|
||||
Set<AssociationEndMember> result = new Set<AssociationEndMember>(EqualityComparer<AssociationEndMember>.Default);
|
||||
// Get the key slots for the table (they are in the slotMap) and
|
||||
// check for that slot on the C-side
|
||||
foreach (int keySlot in m_memberMaps.ProjectedSlotMap.KeySlots)
|
||||
{
|
||||
MemberProjectedSlot slot = (MemberProjectedSlot)rightQuery.ProjectedSlotAt(keySlot);
|
||||
MemberPath path = slot.MemberPath;
|
||||
// See what end it maps to in the relationSet
|
||||
AssociationEndMember endMember = (AssociationEndMember)path.RootEdmMember;
|
||||
Debug.Assert(endMember != null, "Element in path before scalar path is not end property?");
|
||||
result.Add(endMember);
|
||||
}
|
||||
Debug.Assert(result != null, "No end found for keyslots of table?");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
internal MemberProjectedSlot GetLeftSideMappedSlotForRightSideMember(MemberPath member)
|
||||
{
|
||||
int projectedPosition = RightCellQuery.GetProjectedPosition(new MemberProjectedSlot(member));
|
||||
if (projectedPosition == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ProjectedSlot slot = LeftCellQuery.ProjectedSlotAt(projectedPosition);
|
||||
|
||||
if (slot == null || slot is ConstantProjectedSlot)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return slot as MemberProjectedSlot;
|
||||
}
|
||||
|
||||
internal MemberProjectedSlot GetRightSideMappedSlotForLeftSideMember(MemberPath member)
|
||||
{
|
||||
int projectedPosition = LeftCellQuery.GetProjectedPosition(new MemberProjectedSlot(member));
|
||||
if (projectedPosition == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ProjectedSlot slot = RightCellQuery.ProjectedSlotAt(projectedPosition);
|
||||
|
||||
if (slot == null || slot is ConstantProjectedSlot)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return slot as MemberProjectedSlot;
|
||||
}
|
||||
|
||||
internal MemberProjectedSlot GetCSideMappedSlotForSMember(MemberPath member)
|
||||
{
|
||||
if (m_viewTarget == ViewTarget.QueryView)
|
||||
{
|
||||
return GetLeftSideMappedSlotForRightSideMember(member);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetRightSideMappedSlotForLeftSideMember(member);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Equality Comparer class
|
||||
// This class compares wrappers based on the Right Where Clause and
|
||||
// Extent -- needed for the boolean engine
|
||||
private class BoolWrapperComparer : IEqualityComparer<LeftCellWrapper>
|
||||
{
|
||||
|
||||
public bool Equals(LeftCellWrapper left, LeftCellWrapper right)
|
||||
{
|
||||
// Quick check with references
|
||||
if (object.ReferenceEquals(left, right))
|
||||
{
|
||||
// Gets the Null and Undefined case as well
|
||||
return true;
|
||||
}
|
||||
// One of them is non-null at least
|
||||
if (left == null || right == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Both are non-null at this point
|
||||
bool whereClauseEqual = BoolExpression.EqualityComparer.Equals(left.RightCellQuery.WhereClause,
|
||||
right.RightCellQuery.WhereClause);
|
||||
|
||||
return left.RightExtent.Equals(right.RightExtent) && whereClauseEqual;
|
||||
}
|
||||
|
||||
public int GetHashCode(LeftCellWrapper wrapper)
|
||||
{
|
||||
return BoolExpression.EqualityComparer.GetHashCode(wrapper.RightCellQuery.WhereClause) ^ wrapper.RightExtent.GetHashCode();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Comparer
|
||||
// A class that compares two cell wrappers. Useful for guiding heuristics
|
||||
// and to ensure that the largest selection domain (i.e., the number of
|
||||
// multiconstants in "mc in {...}") is first in the list
|
||||
private class LeftCellWrapperComparer : IComparer<LeftCellWrapper>
|
||||
{
|
||||
|
||||
public int Compare(LeftCellWrapper x, LeftCellWrapper y)
|
||||
{
|
||||
|
||||
// More attributes first -- so that we get most attributes
|
||||
// with very few intersections (when we use the sortings for
|
||||
// that). When we are subtracting, attributes are not important
|
||||
|
||||
// Use FragmentQuery's attributes instead of LeftCellWrapper's original attributes in the comparison
|
||||
// since the former might have got extended to include all attributes whose value is determined
|
||||
// by the WHERE clause (e.g., if we have WHERE ProductName='Camera' we can assume ProductName is projected)
|
||||
|
||||
if (x.FragmentQuery.Attributes.Count > y.FragmentQuery.Attributes.Count)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (x.FragmentQuery.Attributes.Count < y.FragmentQuery.Attributes.Count)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
// Since the sort may not be stable, we use the original cell number string to break the tie
|
||||
return String.CompareOrdinal(x.OriginalCellNumberString, y.OriginalCellNumberString);
|
||||
}
|
||||
}
|
||||
|
||||
// A class that compares two cell wrappers based on original cell number
|
||||
internal class CellIdComparer : IComparer<LeftCellWrapper>
|
||||
{
|
||||
|
||||
public int Compare(LeftCellWrapper x, LeftCellWrapper y)
|
||||
{
|
||||
return StringComparer.Ordinal.Compare(x.OriginalCellNumberString, y.OriginalCellNumberString);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,430 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="MemberDomainMap.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.Structures
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Data.Mapping.ViewGeneration.Utils;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using CellConstantSet = Common.Utils.Set<Constant>;
|
||||
|
||||
// This class keeps track of the domain values of the different members
|
||||
// in a schema. E.g., for a discriminator, it keeps track of "P",
|
||||
// "C"; for type of Person, it keeps track of Person, Customer, etc
|
||||
// It exposes two concepts -- the domain of a member variable and the
|
||||
// different possible values for that member, e.g., the possible values
|
||||
// could be 3, 4, 5 but the domain could be 3, 4 (domain is always a
|
||||
// subset of possibleVales
|
||||
internal class MemberDomainMap : InternalBase
|
||||
{
|
||||
|
||||
#region Fields
|
||||
// Keep track of the actual domain for each member on which we have conditions
|
||||
// Note: some subtleties: For QueryDomainMap it holds just C-side condition members. For UpdateDominMap
|
||||
// it now holds S-side condition members as well as members with no s-side condition but C-side condition
|
||||
// such that C-side condition restricts the domain of the member(column).
|
||||
private Dictionary<MemberPath, CellConstantSet> m_conditionDomainMap;
|
||||
// Keep track of the actual domain for each member on which we have no conditions
|
||||
// CellConstantSet in m_nonConditionDomainMap is really CellConstantSetInfo
|
||||
private Dictionary<MemberPath, CellConstantSet> m_nonConditionDomainMap;
|
||||
|
||||
// members on C-side that are projected, don't have conditions, but the respective S-side members do
|
||||
// we need to threat those just as regular members except in validation, where S-side conditions are
|
||||
// projected to C-side. For that, KB needs to add the respective constraints involving this members
|
||||
// For example: CPerson1.Phone IN {?, NOT(?, NULL)) on C-side. We need to know that
|
||||
// type(CPerson1)=Customer <-> !(CPerson1.Phone IN {?}) for validation of domain constraints
|
||||
private Set<MemberPath> m_projectedConditionMembers = new Set<MemberPath>();
|
||||
|
||||
private EdmItemCollection m_edmItemCollection;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Constructor
|
||||
private MemberDomainMap(Dictionary<MemberPath, CellConstantSet> domainMap,
|
||||
Dictionary<MemberPath, CellConstantSet> nonConditionDomainMap, EdmItemCollection edmItemCollection)
|
||||
{
|
||||
m_conditionDomainMap = domainMap;
|
||||
m_nonConditionDomainMap = nonConditionDomainMap;
|
||||
m_edmItemCollection = edmItemCollection;
|
||||
}
|
||||
// effects: Creates a map with all the condition member constants
|
||||
// from extentCells. viewtarget determines whether the view is an
|
||||
// update or query view
|
||||
internal MemberDomainMap(ViewTarget viewTarget, bool isValidationEnabled, IEnumerable<Cell> extentCells, EdmItemCollection edmItemCollection, ConfigViewGenerator config, Dictionary<EntityType, Set<EntityType>> inheritanceGraph)
|
||||
{
|
||||
m_conditionDomainMap = new Dictionary<MemberPath, CellConstantSet>(MemberPath.EqualityComparer);
|
||||
m_edmItemCollection = edmItemCollection;
|
||||
|
||||
Dictionary<MemberPath, CellConstantSet> domainMap = null;
|
||||
if (viewTarget == ViewTarget.UpdateView)
|
||||
{
|
||||
domainMap = Domain.ComputeConstantDomainSetsForSlotsInUpdateViews(extentCells, m_edmItemCollection);
|
||||
}
|
||||
else
|
||||
{
|
||||
domainMap = Domain.ComputeConstantDomainSetsForSlotsInQueryViews(extentCells, m_edmItemCollection, isValidationEnabled);
|
||||
}
|
||||
|
||||
foreach (Cell cell in extentCells)
|
||||
{
|
||||
CellQuery cellQuery = cell.GetLeftQuery(viewTarget);
|
||||
// Get the atoms from cellQuery and only keep the ones that
|
||||
// are condition members
|
||||
foreach (MemberRestriction condition in cellQuery.GetConjunctsFromWhereClause())
|
||||
{
|
||||
// Note: TypeConditions are created using OneOfTypeConst and
|
||||
// scalars are created using OneOfScalarConst
|
||||
MemberPath memberPath = condition.RestrictedMemberSlot.MemberPath;
|
||||
|
||||
Debug.Assert(condition is ScalarRestriction || condition is TypeRestriction,
|
||||
"Unexpected restriction");
|
||||
|
||||
// Take the narrowed domain from domainMap, if any
|
||||
CellConstantSet domainValues;
|
||||
if (!domainMap.TryGetValue(memberPath, out domainValues))
|
||||
{
|
||||
domainValues = Domain.DeriveDomainFromMemberPath(memberPath, edmItemCollection, isValidationEnabled);
|
||||
}
|
||||
|
||||
//Don't count conditions that are satisfied through IsNull=false
|
||||
if (!domainValues.Contains(Constant.Null))
|
||||
{
|
||||
//multiple values of condition represent disjunction in conditions (not currently supported)
|
||||
// if there is any condition constant that is NotNull
|
||||
if (condition.Domain.Values.All(conditionConstant => (conditionConstant.Equals(Constant.NotNull))))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
//else there is atleast one condition value that is allowed, continue view generation
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
//| Nullable | IsNull | Test case |
|
||||
//| T | T | T |
|
||||
//| T | F | T |
|
||||
//| F | T | F |
|
||||
//| F | F | T |
|
||||
//------------------------------------------
|
||||
//IsNull condition on a member that is non nullable is an invalid condition
|
||||
if (domainValues.Count <= 0 || (!domainValues.Contains(Constant.Null) && condition.Domain.Values.Contains(Constant.Null)))
|
||||
{
|
||||
string message = System.Data.Entity.Strings.ViewGen_InvalidCondition(memberPath.PathToString(false));
|
||||
ErrorLog.Record record = new ErrorLog.Record(true, ViewGenErrorCode.InvalidCondition, message, cell, String.Empty);
|
||||
ExceptionHelpers.ThrowMappingException(record, config);
|
||||
}
|
||||
if (memberPath.IsAlwaysDefined(inheritanceGraph) == false)
|
||||
{
|
||||
domainValues.Add(Constant.Undefined);
|
||||
}
|
||||
|
||||
AddToDomainMap(memberPath, domainValues);
|
||||
}
|
||||
}
|
||||
|
||||
// Fill up the domains for the remaining slots as well
|
||||
m_nonConditionDomainMap = new Dictionary<MemberPath, CellConstantSet>(MemberPath.EqualityComparer);
|
||||
foreach (Cell cell in extentCells)
|
||||
{
|
||||
CellQuery cellQuery = cell.GetLeftQuery(viewTarget);
|
||||
// Get the atoms from cellQuery and only keep the ones that
|
||||
// are condition members
|
||||
foreach (MemberProjectedSlot slot in cellQuery.GetAllQuerySlots())
|
||||
{
|
||||
MemberPath member = slot.MemberPath;
|
||||
if (m_conditionDomainMap.ContainsKey(member) == false && m_nonConditionDomainMap.ContainsKey(member) == false)
|
||||
{
|
||||
CellConstantSet memberSet = Domain.DeriveDomainFromMemberPath(member, m_edmItemCollection, true /* Regardless of validation, leave the domain unbounded because this is not a condition member */);
|
||||
if (member.IsAlwaysDefined(inheritanceGraph) == false)
|
||||
{ // nonConditionMember may belong to subclass
|
||||
memberSet.Add(Constant.Undefined);
|
||||
}
|
||||
memberSet = Domain.ExpandNegationsInDomain(memberSet, memberSet);
|
||||
m_nonConditionDomainMap.Add(member, new CellConstantSetInfo(memberSet, slot));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Properties
|
||||
internal bool IsProjectedConditionMember(MemberPath memberPath)
|
||||
{
|
||||
return m_projectedConditionMembers.Contains(memberPath);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
// effects: Returns an "open-world" domain, i.e.,
|
||||
// one in which not-null constants are used to represent some other value from the domain
|
||||
internal MemberDomainMap GetOpenDomain()
|
||||
{
|
||||
var domainMap = m_conditionDomainMap.ToDictionary(p => p.Key, p => new Set<Constant>(p.Value, Constant.EqualityComparer));
|
||||
ExpandDomainsIfNeeded(domainMap);
|
||||
return new MemberDomainMap(domainMap, m_nonConditionDomainMap, m_edmItemCollection);
|
||||
}
|
||||
|
||||
// effects: Creates a deep copy of MemberDomainMap
|
||||
// nonConditionDomainMap is read-only so it is reused without cloning
|
||||
internal MemberDomainMap MakeCopy()
|
||||
{
|
||||
var domainMap = m_conditionDomainMap.ToDictionary(p => p.Key, p => new Set<Constant>(p.Value, Constant.EqualityComparer));
|
||||
return new MemberDomainMap(domainMap, m_nonConditionDomainMap, m_edmItemCollection);
|
||||
}
|
||||
|
||||
// effects: Adds negated constants to the possible set of values if none exists in that set.
|
||||
// Needed so that we can handle cases when discriminator in the store as P, C but could have other values
|
||||
// as well.
|
||||
internal void ExpandDomainsToIncludeAllPossibleValues()
|
||||
{
|
||||
ExpandDomainsIfNeeded(m_conditionDomainMap);
|
||||
}
|
||||
|
||||
private void ExpandDomainsIfNeeded(Dictionary<MemberPath, CellConstantSet> domainMapForMembers)
|
||||
{
|
||||
// For the S-side, we always says that NOT(...) is
|
||||
// present. For example, if we are told "C", "P", we assume
|
||||
// that NOT(C, P) is possibly present in that column
|
||||
foreach (MemberPath path in domainMapForMembers.Keys)
|
||||
{
|
||||
CellConstantSet possibleValues = domainMapForMembers[path];
|
||||
if (path.IsScalarType() &&
|
||||
possibleValues.Any(c => c is NegatedConstant) == false)
|
||||
{
|
||||
if (MetadataHelper.HasDiscreteDomain(path.EdmType))
|
||||
{
|
||||
// for a discrete domain, add all values that are not currently represented
|
||||
// in the domain
|
||||
Set<Constant> completeDomain = Domain.DeriveDomainFromMemberPath(path, m_edmItemCollection, true /* leaveDomainUnbounded */);
|
||||
possibleValues.Unite(completeDomain);
|
||||
}
|
||||
else
|
||||
{
|
||||
// for a non-discrete domain, add NOT("C", "P")
|
||||
NegatedConstant negatedConstant = new NegatedConstant(possibleValues);
|
||||
possibleValues.Add(negatedConstant);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// effects: Shrinks the domain of members whose types can be enumerated - currently it applies
|
||||
// only to boolean type as for enums we don't restrict enum values to specified members only.
|
||||
// For example NOT(False, True, Null) for a boolean domain should be removed
|
||||
internal void ReduceEnumerableDomainToEnumeratedValues(ViewTarget target, ConfigViewGenerator config)
|
||||
{
|
||||
// Go through the two maps
|
||||
|
||||
ReduceEnumerableDomainToEnumeratedValues(target, m_conditionDomainMap, config, m_edmItemCollection);
|
||||
ReduceEnumerableDomainToEnumeratedValues(target, m_nonConditionDomainMap, config, m_edmItemCollection);
|
||||
}
|
||||
|
||||
// effects: Fixes the domains of variables in this as specified in FixEnumerableDomains
|
||||
private static void ReduceEnumerableDomainToEnumeratedValues(ViewTarget target, Dictionary<MemberPath, CellConstantSet> domainMap, ConfigViewGenerator config,
|
||||
EdmItemCollection edmItemCollection)
|
||||
{
|
||||
foreach (MemberPath member in domainMap.Keys)
|
||||
{
|
||||
if (MetadataHelper.HasDiscreteDomain(member.EdmType) == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
CellConstantSet domain = Domain.DeriveDomainFromMemberPath(member, edmItemCollection, true /* leaveDomainUnbounded */);
|
||||
CellConstantSet extra = domainMap[member].Difference(domain);
|
||||
extra.Remove(Constant.Undefined);
|
||||
if (extra.Count > 0)
|
||||
{ // domainMap has extra members -- we should get rid of them
|
||||
if (config.IsNormalTracing)
|
||||
{
|
||||
Helpers.FormatTraceLine("Changed domain of {0} from {1} - subtract {2}", member, domainMap[member], extra);
|
||||
}
|
||||
domainMap[member].Subtract(extra);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// requires: this domainMap has been created for the C-side
|
||||
// effects: Fixes the mergedDomain map in this by merging entries
|
||||
// available in updateDomainMap
|
||||
internal static void PropagateUpdateDomainToQueryDomain(IEnumerable<Cell> cells, MemberDomainMap queryDomainMap, MemberDomainMap updateDomainMap)
|
||||
{
|
||||
|
||||
foreach (Cell cell in cells)
|
||||
{
|
||||
CellQuery cQuery = cell.CQuery;
|
||||
CellQuery sQuery = cell.SQuery;
|
||||
|
||||
for (int i = 0; i < cQuery.NumProjectedSlots; i++)
|
||||
{
|
||||
MemberProjectedSlot cSlot = cQuery.ProjectedSlotAt(i) as MemberProjectedSlot;
|
||||
MemberProjectedSlot sSlot = sQuery.ProjectedSlotAt(i) as MemberProjectedSlot;
|
||||
|
||||
if (cSlot == null || sSlot == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the domain for sSlot and merge with cSlot's
|
||||
MemberPath cPath = cSlot.MemberPath;
|
||||
MemberPath sPath = sSlot.MemberPath;
|
||||
CellConstantSet cDomain = queryDomainMap.GetDomainInternal(cPath);
|
||||
CellConstantSet sDomain = updateDomainMap.GetDomainInternal(sPath);
|
||||
|
||||
// skip NULL because if c-side member is nullable, it's already there, and otherwise can't be taken
|
||||
// skip negated because negated values are translated in a special way
|
||||
cDomain.Unite(sDomain.Where(constant => !constant.IsNull() && !(constant is NegatedConstant)));
|
||||
|
||||
if (updateDomainMap.IsConditionMember(sPath) && !queryDomainMap.IsConditionMember(cPath))
|
||||
{
|
||||
// record this member so KB knows we have to generate constraints for it
|
||||
queryDomainMap.m_projectedConditionMembers.Add(cPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExpandNegationsInDomainMap(queryDomainMap.m_conditionDomainMap);
|
||||
ExpandNegationsInDomainMap(queryDomainMap.m_nonConditionDomainMap);
|
||||
}
|
||||
|
||||
private static void ExpandNegationsInDomainMap(Dictionary<MemberPath, Set<Constant>> domainMap)
|
||||
{
|
||||
foreach (var path in domainMap.Keys.ToArray())
|
||||
{
|
||||
domainMap[path] = Domain.ExpandNegationsInDomain(domainMap[path]);
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsConditionMember(MemberPath path)
|
||||
{
|
||||
return m_conditionDomainMap.ContainsKey(path);
|
||||
}
|
||||
|
||||
internal IEnumerable<MemberPath> ConditionMembers(EntitySetBase extent)
|
||||
{
|
||||
foreach (MemberPath path in m_conditionDomainMap.Keys)
|
||||
{
|
||||
if (path.Extent.Equals(extent))
|
||||
{
|
||||
yield return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal IEnumerable<MemberPath> NonConditionMembers(EntitySetBase extent)
|
||||
{
|
||||
foreach (MemberPath path in m_nonConditionDomainMap.Keys)
|
||||
{
|
||||
if (path.Extent.Equals(extent))
|
||||
{
|
||||
yield return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds AllOtherConstants element to the domain set given by MemberPath
|
||||
/// </summary>
|
||||
internal void AddSentinel(MemberPath path)
|
||||
{
|
||||
CellConstantSet set = GetDomainInternal(path);
|
||||
set.Add(Constant.AllOtherConstants);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes AllOtherConstant element from the domain set given by MemberPath
|
||||
/// </summary>
|
||||
internal void RemoveSentinel(MemberPath path)
|
||||
{
|
||||
CellConstantSet set = GetDomainInternal(path);
|
||||
set.Remove(Constant.AllOtherConstants);
|
||||
}
|
||||
|
||||
// requires member exist in this
|
||||
// effects: Returns the possible values/domain for that member
|
||||
internal IEnumerable<Constant> GetDomain(MemberPath path)
|
||||
{
|
||||
return GetDomainInternal(path);
|
||||
}
|
||||
|
||||
private CellConstantSet GetDomainInternal(MemberPath path)
|
||||
{
|
||||
CellConstantSet result;
|
||||
bool found = m_conditionDomainMap.TryGetValue(path, out result);
|
||||
if (!found)
|
||||
{
|
||||
result = m_nonConditionDomainMap[path]; // It better be in this one!
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// keeps the same set identity for the updated cell constant domain
|
||||
internal void UpdateConditionMemberDomain(MemberPath path, IEnumerable<Constant> domainValues)
|
||||
{
|
||||
// update domainMap
|
||||
Set<Constant> oldDomain = m_conditionDomainMap[path];
|
||||
oldDomain.Clear();
|
||||
oldDomain.Unite(domainValues);
|
||||
}
|
||||
|
||||
// effects: For member, adds domainValues as the set of values that
|
||||
// member can take. Merges them with any existing values if present
|
||||
private void AddToDomainMap(MemberPath member, IEnumerable<Constant> domainValues)
|
||||
{
|
||||
CellConstantSet possibleValues;
|
||||
if (false == m_conditionDomainMap.TryGetValue(member, out possibleValues))
|
||||
{
|
||||
possibleValues = new CellConstantSet(Constant.EqualityComparer);
|
||||
}
|
||||
possibleValues.Unite(domainValues);
|
||||
// Add the normalized domain to the map so that later uses of the
|
||||
// domain are consistent
|
||||
m_conditionDomainMap[member] = Domain.ExpandNegationsInDomain(possibleValues, possibleValues);
|
||||
}
|
||||
|
||||
internal override void ToCompactString(StringBuilder builder)
|
||||
{
|
||||
foreach (MemberPath memberPath in m_conditionDomainMap.Keys)
|
||||
{
|
||||
builder.Append('(');
|
||||
memberPath.ToCompactString(builder);
|
||||
IEnumerable<Constant> domain = GetDomain(memberPath);
|
||||
builder.Append(": ");
|
||||
StringUtil.ToCommaSeparatedStringSorted(builder, domain);
|
||||
builder.Append(") ");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
// struct to keep track of the constant set for a particular slot
|
||||
private class CellConstantSetInfo : CellConstantSet
|
||||
{
|
||||
internal CellConstantSetInfo(Set<Constant> iconstants, MemberProjectedSlot islot)
|
||||
: base(iconstants)
|
||||
{
|
||||
slot = islot;
|
||||
}
|
||||
|
||||
internal MemberProjectedSlot slot;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return base.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,80 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="MemberMaps.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.Structures
|
||||
{
|
||||
|
||||
// This class manages the different maps used in the view generation
|
||||
// process. These maps keep track of indexes of memberpaths, domains of
|
||||
// member paths, etc
|
||||
internal class MemberMaps
|
||||
{
|
||||
|
||||
#region Fields
|
||||
private MemberProjectionIndex m_projectedSlotMap;
|
||||
private MemberDomainMap m_queryDomainMap;
|
||||
private MemberDomainMap m_updateDomainMap;
|
||||
private ViewTarget m_viewTarget;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
internal MemberMaps(ViewTarget viewTarget, MemberProjectionIndex projectedSlotMap,
|
||||
MemberDomainMap queryDomainMap, MemberDomainMap updateDomainMap)
|
||||
{
|
||||
|
||||
m_projectedSlotMap = projectedSlotMap;
|
||||
m_queryDomainMap = queryDomainMap;
|
||||
m_updateDomainMap = updateDomainMap;
|
||||
|
||||
Debug.Assert(m_queryDomainMap != null);
|
||||
Debug.Assert(m_updateDomainMap != null);
|
||||
Debug.Assert(m_projectedSlotMap != null);
|
||||
m_viewTarget = viewTarget;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region Properties
|
||||
|
||||
internal MemberProjectionIndex ProjectedSlotMap
|
||||
{
|
||||
get { return m_projectedSlotMap; }
|
||||
}
|
||||
|
||||
internal MemberDomainMap QueryDomainMap
|
||||
{
|
||||
get { return m_queryDomainMap; }
|
||||
}
|
||||
|
||||
internal MemberDomainMap UpdateDomainMap
|
||||
{
|
||||
get { return m_updateDomainMap; }
|
||||
}
|
||||
|
||||
internal MemberDomainMap RightDomainMap
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_viewTarget == ViewTarget.QueryView ? m_updateDomainMap : m_queryDomainMap;
|
||||
}
|
||||
}
|
||||
|
||||
internal MemberDomainMap LeftDomainMap
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_viewTarget == ViewTarget.QueryView ? m_queryDomainMap : m_updateDomainMap;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user