Imported Upstream version 4.0.0~alpha1

Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
This commit is contained in:
Jo Shields
2015-04-07 09:35:12 +01:00
parent 283343f570
commit 3c1f479b9d
22469 changed files with 2931443 additions and 869343 deletions

View File

@@ -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
}
}

View File

@@ -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;
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -0,0 +1,31 @@
//---------------------------------------------------------------------
// <copyright file="CellTreeOpType.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
// @owner [....]
// @backupOwner [....]
//---------------------------------------------------------------------
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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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