|
//---------------------------------------------------------------------
// <copyright file="BasicViewGenerator.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
// @owner Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
namespace System.Data.Mapping.ViewGeneration
{
using System.Collections.Generic;
using System.Data.Common.Utils;
using System.Data.Entity;
using System.Data.Mapping.ViewGeneration.QueryRewriting;
using System.Data.Mapping.ViewGeneration.Structures;
using System.Data.Mapping.ViewGeneration.Utils;
using System.Data.Mapping.ViewGeneration.Validation;
using System.Data.Metadata.Edm;
using System.Diagnostics;
using System.Linq;
using System.Text;
// This class generates a view for an extent that may contain self-joins
// and self-unions -- this can be later simplified or optimized
// Output: A cell tree with LeftCellWrappers as nodes connected by Union, IJ,
// LOJ, FOJs
internal class BasicViewGenerator : InternalBase
{
#region Constructor
// effects: Creates a view generator object that can be used to generate views
// based on usedCells (projectedSlotMap are useful for deciphering the fields)
internal BasicViewGenerator(MemberProjectionIndex projectedSlotMap, List<LeftCellWrapper> usedCells, FragmentQuery activeDomain,
ViewgenContext context, MemberDomainMap domainMap, ErrorLog errorLog, ConfigViewGenerator config)
{
Debug.Assert(usedCells.Count > 0, "No used cells");
m_projectedSlotMap = projectedSlotMap;
m_usedCells = usedCells;
m_viewgenContext = context;
m_activeDomain = activeDomain;
m_errorLog = errorLog;
m_config = config;
m_domainMap = domainMap;
}
#endregion
#region Fields
private MemberProjectionIndex m_projectedSlotMap;
private List<LeftCellWrapper> m_usedCells;
// Active domain comprises all multiconstants that need to be reconstructed
private FragmentQuery m_activeDomain;
// these two are temporarily needed for checking containment
private ViewgenContext m_viewgenContext;
private ErrorLog m_errorLog;
private ConfigViewGenerator m_config;
private MemberDomainMap m_domainMap;
#endregion
#region Properties
private FragmentQueryProcessor LeftQP
{
get { return m_viewgenContext.LeftFragmentQP; }
}
#endregion
#region Exposed Methods
// effects: Given the set of used cells for an extent, returns a
// view to generate that extent
internal CellTreeNode CreateViewExpression()
{
// Create an initial FOJ group with all the used cells as children
OpCellTreeNode fojNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.FOJ);
// Add all the used cells as children to fojNode. This is a valid
// view for the extent. We later try to optimize it
foreach (LeftCellWrapper cell in m_usedCells)
{
LeafCellTreeNode cellNode = new LeafCellTreeNode(m_viewgenContext, cell);
fojNode.Add(cellNode);
}
//rootNode = GroupByNesting(rootNode);
// Group cells by the "right" extent (recall that we are
// generating the view for the left extent) so that cells of the
// same extent are in the same subtree
CellTreeNode rootNode = GroupByRightExtent(fojNode);
// Change some of the FOJs to Unions, IJs and LOJs
rootNode = IsolateUnions(rootNode);
// The isolation with Union is different from IsolateUnions --
// the above isolation finds collections of chidren in a
// node and connects them by union. The below one only considers
// two children at a time
rootNode = IsolateByOperator(rootNode, CellTreeOpType.Union);
rootNode = IsolateByOperator(rootNode, CellTreeOpType.IJ);
rootNode = IsolateByOperator(rootNode, CellTreeOpType.LOJ);
if (m_viewgenContext.ViewTarget == ViewTarget.QueryView)
{
rootNode = ConvertUnionsToNormalizedLOJs(rootNode);
}
return rootNode;
}
#endregion
#region Private Methods
// requires: The tree rooted at cellTreeNode is an FOJ tree of
// LeafCellTreeNodes only, i.e., there is an FOJ node with the
// children being LeafCellTreeNodes
//
// effects: Given a tree rooted at rootNode, ensures that cells
// of the same right extent are placed in their own subtree below
// cellTreeNode. That is, if there are 3 cells of extent A and 2 of
// extent B (i.e., 5 cells with an FOJ on it), the resulting tree has
// an FOJ node with two children -- FOJ nodes. These FOJ nodes have 2
// and 3 children
internal CellTreeNode GroupByRightExtent(CellTreeNode rootNode)
{
// A dictionary that maps an extent to the nodes are from that extent
// We want a ref comparer here
KeyToListMap<EntitySetBase, LeafCellTreeNode> extentMap =
new KeyToListMap<EntitySetBase, LeafCellTreeNode>(EqualityComparer<EntitySetBase>.Default);
// CR_Meek_Low: method can be simplified (Map<Extent, OpCellTreeNode>, populate as you go)
// (becomes self-documenting)
// For each leaf child, find the extent of the child and place it
// in extentMap
foreach (LeafCellTreeNode childNode in rootNode.Children)
{
// A cell may contain P, P.PA -- we return P
// CHANGE_Microsoft_FEATURE_COMPOSITION Need to fix for composition!!
EntitySetBase extent = childNode.LeftCellWrapper.RightCellQuery.Extent; // relation or extent to group by
Debug.Assert(extent != null, "Each cell must have a right extent");
// Add the childNode as a child of the FOJ tree for "extent"
extentMap.Add(extent, childNode);
}
// Now go through the extent map and create FOJ nodes for each extent
// Place the nodes for that extent in the newly-created FOJ subtree
// Also add the op node for every node as a child of the final result
OpCellTreeNode result = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.FOJ);
foreach (EntitySetBase extent in extentMap.Keys)
{
OpCellTreeNode extentFojNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.FOJ);
foreach (LeafCellTreeNode childNode in extentMap.ListForKey(extent))
{
extentFojNode.Add(childNode);
}
result.Add(extentFojNode);
}
// We call Flatten to remove any unnecessary nestings
// where an OpNode has only 1 child.
return result.Flatten();
}
// requires: cellTreeNode has a tree such that all its intermediate nodes
// are FOJ nodes only
// effects: Converts the tree rooted at rootNode (recursively) in
// following way and returns a new rootNode -- it partitions
// rootNode's children such that no two different partitions have
// any overlapping constants. These partitions are connected by Union
// nodes (since there is no overlapping).
// Note: Method may modify rootNode's contents and children
private CellTreeNode IsolateUnions(CellTreeNode rootNode)
{
if (rootNode.Children.Count <= 1)
{
// No partitioning of children needs to be done
return rootNode;
}
Debug.Assert(rootNode.OpType == CellTreeOpType.FOJ, "So far, we have FOJs only");
// Recursively, transform the subtrees rooted at cellTreeNode's children
for (int i = 0; i < rootNode.Children.Count; i++)
{
// Method modifies input as well
rootNode.Children[i] = IsolateUnions(rootNode.Children[i]);
}
// Different children groups are connected by a Union
// node -- the secltion domain of one group is disjoint from
// another group's selection domain, i.e., group A1 contributes
// tuples to the extent which are disjoint from the tuples by
// A2. So we can connect these groups by union alls.
// Inside each group, we continue to connect children of the same
// group using FOJ
OpCellTreeNode unionNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.Union);
// childrenSet keeps track of the children that need to be procesed/partitioned
ModifiableIteratorCollection<CellTreeNode> childrenSet = new ModifiableIteratorCollection<CellTreeNode>(rootNode.Children);
while (false == childrenSet.IsEmpty)
{
// Start a new group
// Make an FOJ node to connect children of the same group
OpCellTreeNode fojNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.FOJ);
// Add one of the root's children as a child to the foj node
CellTreeNode someChild = childrenSet.RemoveOneElement();
fojNode.Add(someChild);
// We now want a transitive closure of the overlap between the
// the children node. We keep checking each child with the
// fojNode and add it as a child of fojNode if there is an
// overlap. Note that when a node is added to the fojNode,
// its constants are propagated to the fojNode -- so we do
// get transitive closure in terms of intersection
foreach (CellTreeNode child in childrenSet.Elements())
{
if (!IsDisjoint(fojNode, child))
{
fojNode.Add(child);
childrenSet.RemoveCurrentOfIterator();
// To ensure that we get all overlapping node, we
// need to restart checking all the children
childrenSet.ResetIterator();
}
}
// Now we have a group of children nodes rooted at
// fojNode. Add this fojNode to the union
unionNode.Add(fojNode);
}
// The union node as the root of the view
CellTreeNode result = unionNode.Flatten();
return result;
}
/// <summary>
/// Traverse the tree and perform the following rewrites:
/// 1. Flatten unions contained as left children of LOJs: LOJ(A, Union(B, C)) -> LOJ(A, B, C).
/// 2. Rewrite flat LOJs into nested LOJs. The nesting is determined by FKs between right cell table PKs.
/// Example: if we have an LOJ(A, B, C, D) and we know there are FKs from C.PK and D.PK to B.PK,
/// we want to rewrite into this - LOJ(A, LOJ(B, C, D)).
/// 3. As a special case we also look into LOJ driving node (left most child in LOJ) and if it is an IJ,
/// then we consider attaching LOJ children to nodes inside IJ based on the same principle as above.
/// Example: LOJ(IJ(A, B, C), D, E, F) -> LOJ(IJ(LOJ(A, D), B, LOJ(C, E)), F) iff D has FK to A and E has FK to C.
///
/// This normalization enables FK-based join elimination in plan compiler, so for a query such as
/// "select e.ID from ABCDSet" we want plan compiler to produce "select a.ID from A" instead of
/// "select a.ID from A LOJ B LOJ C LOJ D".
/// </summary>
private CellTreeNode ConvertUnionsToNormalizedLOJs(CellTreeNode rootNode)
{
// Recursively, transform the subtrees rooted at rootNode's children.
for (int i = 0; i < rootNode.Children.Count; i++)
{
// Method modifies input as well.
rootNode.Children[i] = ConvertUnionsToNormalizedLOJs(rootNode.Children[i]);
}
// We rewrite only LOJs.
if (rootNode.OpType != CellTreeOpType.LOJ || rootNode.Children.Count < 2)
{
return rootNode;
}
// Create the resulting LOJ node.
var result = new OpCellTreeNode(m_viewgenContext, rootNode.OpType);
// Create working collection for the LOJ children.
var children = new List<CellTreeNode>();
// If rootNode looks something like ((V0 IJ V1) LOJ V2 LOJ V3),
// and it turns out that there are FK associations from V2 or V3 pointing, let's say at V0,
// then we want to rewrite the result as (V1 IJ (V0 LOJ V2 LOJ V3)).
// If we don't do this, then plan compiler won't have a chance to eliminate LOJ V2 LOJ V3.
// Hence, flatten the first child or rootNode if it's IJ, but remember that its parts are driving nodes for the LOJ,
// so that we don't accidentally nest them.
OpCellTreeNode resultIJDriver = null;
HashSet<CellTreeNode> resultIJDriverChildren = null;
if (rootNode.Children[0].OpType == CellTreeOpType.IJ)
{
// Create empty resultIJDriver node and add it as the first child (driving) into the LOJ result.
resultIJDriver = new OpCellTreeNode(m_viewgenContext, rootNode.Children[0].OpType);
result.Add(resultIJDriver);
children.AddRange(rootNode.Children[0].Children);
resultIJDriverChildren = new HashSet<CellTreeNode>(rootNode.Children[0].Children);
}
else
{
result.Add(rootNode.Children[0]);
}
// Flatten unions in non-driving nodes: (V0 LOJ (V1 Union V2 Union V3)) -> (V0 LOJ V1 LOJ V2 LOJ V3)
foreach (var child in rootNode.Children.Skip(1))
{
var opNode = child as OpCellTreeNode;
if (opNode != null && opNode.OpType == CellTreeOpType.Union)
{
children.AddRange(opNode.Children);
}
else
{
children.Add(child);
}
}
// A dictionary that maps an extent to the nodes that are from that extent.
// We want a ref comparer here.
var extentMap = new KeyToListMap<EntitySet, LeafCellTreeNode>(EqualityComparer<EntitySet>.Default);
// Note that we skip non-leaf nodes (non-leaf nodes don't have FKs) and attach them directly to the result.
foreach (var child in children)
{
var leaf = child as LeafCellTreeNode;
if (leaf != null)
{
EntitySetBase extent = GetLeafNodeTable(leaf);
if (extent != null)
{
extentMap.Add((EntitySet)extent, leaf);
}
}
else
{
if (resultIJDriverChildren != null && resultIJDriverChildren.Contains(child))
{
resultIJDriver.Add(child);
}
else
{
result.Add(child);
}
}
}
// We only deal with simple cases - one node per extent, remove the rest from children and attach directly to result.
var nonTrivial = extentMap.KeyValuePairs.Where(m => m.Value.Count > 1).ToArray();
foreach (var m in nonTrivial)
{
extentMap.RemoveKey(m.Key);
foreach (var n in m.Value)
{
if (resultIJDriverChildren != null && resultIJDriverChildren.Contains(n))
{
resultIJDriver.Add(n);
}
else
{
result.Add(n);
}
}
}
Debug.Assert(extentMap.KeyValuePairs.All(m => m.Value.Count == 1), "extentMap must map to single nodes only.");
// Walk the extents in extentMap and for each extent build PK -> FK1(PK1), FK2(PK2), ... map
// where PK is the primary key of the left extent, and FKn(PKn) is an FK of a right extent that
// points to the PK of the left extent and is based on the PK columns of the right extent.
// Example:
// table tBaseType(Id int, c1 int), PK = (tBaseType.Id)
// table tDerivedType1(Id int, c2 int), PK1 = (tDerivedType1.Id), FK1 = (tDerivedType1.Id -> tBaseType.Id)
// table tDerivedType2(Id int, c3 int), PK2 = (tDerivedType2.Id), FK2 = (tDerivedType2.Id -> tBaseType.Id)
// Will produce:
// (tBaseType) -> (tDerivedType1, tDerivedType2)
var pkFkMap = new KeyToListMap<EntitySet, EntitySet>(EqualityComparer<EntitySet>.Default);
// Also for each extent in extentMap, build another map (extent) -> (LOJ node).
// It will be used to construct the nesting in the next step.
var extentLOJs = new Dictionary<EntitySet, OpCellTreeNode>(EqualityComparer<EntitySet>.Default);
foreach (var extentInfo in extentMap.KeyValuePairs)
{
var principalExtent = extentInfo.Key;
foreach (var fkExtent in GetFKOverPKDependents(principalExtent))
{
// Only track fkExtents that are in extentMap.
System.Collections.ObjectModel.ReadOnlyCollection<LeafCellTreeNode> nodes;
if (extentMap.TryGetListForKey(fkExtent, out nodes))
{
// Make sure that we are not adding resultIJDriverChildren as FK dependents - we do not want them to get nested.
if (resultIJDriverChildren == null || !resultIJDriverChildren.Contains(nodes.Single()))
{
pkFkMap.Add(principalExtent, fkExtent);
}
}
}
var extentLojNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.LOJ);
extentLojNode.Add(extentInfo.Value.Single());
extentLOJs.Add(principalExtent, extentLojNode);
}
// Construct LOJ nesting inside extentLOJs based on the information in pkFkMap.
// Also, track nested extents using nestedExtents.
// Example:
// We start with nestedExtents empty extentLOJs as such:
// tBaseType -> LOJ(BaseTypeNode)
// tDerivedType1 -> LOJ(DerivedType1Node)*
// tDerivedType2 -> LOJ(DerivedType2Node)**
// Note that * and ** represent object references. So each time something is nested,
// we don't clone, but nest the original LOJ. When we get to processing the extent of that LOJ,
// we might add other children to that nested LOJ.
// As we walk pkFkMap, we end up with this:
// tBaseType -> LOJ(BaseTypeNode, LOJ(DerivedType1Node)*, LOJ(DerivedType2Node)**)
// tDerivedType1 -> LOJ(DerivedType1Node)*
// tDerivedType2 -> LOJ(DerivedType2Node)**
// nestedExtens = (tDerivedType1, tDerivedType2)
var nestedExtents = new Dictionary<EntitySet, EntitySet>(EqualityComparer<EntitySet>.Default);
foreach (var m in pkFkMap.KeyValuePairs)
{
var principalExtent = m.Key;
foreach (var fkExtent in m.Value)
{
OpCellTreeNode fkExtentLOJ;
if (extentLOJs.TryGetValue(fkExtent, out fkExtentLOJ) &&
// make sure we don't nest twice and we don't create a cycle.
!nestedExtents.ContainsKey(fkExtent) && !CheckLOJCycle(fkExtent, principalExtent, nestedExtents))
{
extentLOJs[m.Key].Add(fkExtentLOJ);
nestedExtents.Add(fkExtent, principalExtent);
}
}
}
// Now we need to grab the LOJs that have not been nested and add them to the result.
// All LOJs that have been nested must be somewhere inside the LOJs that have not been nested,
// so they as well end up in the result as part of the unnested ones.
foreach (var m in extentLOJs)
{
if (!nestedExtents.ContainsKey(m.Key))
{
// extentLOJ represents (Vx LOJ Vy LOJ(Vm LOJ Vn)) where Vx is the original node from rootNode.Children or resultIJDriverChildren.
var extentLOJ = m.Value;
if (resultIJDriverChildren != null && resultIJDriverChildren.Contains(extentLOJ.Children[0]))
{
resultIJDriver.Add(extentLOJ);
}
else
{
result.Add(extentLOJ);
}
}
}
return result.Flatten();
}
private static IEnumerable<EntitySet> GetFKOverPKDependents(EntitySet principal)
{
foreach (var pkFkInfo in principal.ForeignKeyPrincipals)
{
// If principal has a related extent with FK pointing to principal and the FK is based on PK columns of the related extent,
// then add it.
var pkColumns = pkFkInfo.Item2.ToRole.GetEntityType().KeyMembers;
var fkColumns = pkFkInfo.Item2.ToProperties;
if (pkColumns.Count == fkColumns.Count)
{
// Compare PK to FK columns, order is important (otherwise it's not an FK over PK).
int i = 0;
for (; i < pkColumns.Count && pkColumns[i].EdmEquals(fkColumns[i]); ++i);
if (i == pkColumns.Count)
{
yield return pkFkInfo.Item1.AssociationSetEnds.Where(ase => ase.Name == pkFkInfo.Item2.ToRole.Name).Single().EntitySet;
}
}
}
}
private static EntitySet GetLeafNodeTable(LeafCellTreeNode leaf)
{
return leaf.LeftCellWrapper.RightCellQuery.Extent as EntitySet;
}
private static bool CheckLOJCycle(EntitySet child, EntitySet parent, Dictionary<EntitySet, EntitySet> nestedExtents)
{
do
{
if (EqualityComparer<EntitySet>.Default.Equals(parent, child))
{
return true;
}
}
while (nestedExtents.TryGetValue(parent, out parent));
return false;
}
// requires: opTypeToIsolate must be LOJ, IJ, or Union
// effects: Given a tree rooted at rootNode, determines if there
// are any FOJs that can be replaced by opTypeToIsolate. If so,
// does that and a returns a new tree with the replaced operators
// Note: Method may modify rootNode's contents and children
internal CellTreeNode IsolateByOperator(CellTreeNode rootNode, CellTreeOpType opTypeToIsolate)
{
Debug.Assert(opTypeToIsolate == CellTreeOpType.IJ || opTypeToIsolate == CellTreeOpType.LOJ
|| opTypeToIsolate == CellTreeOpType.Union,
"IsolateJoins can only be called for IJs, LOJs, and Unions");
List<CellTreeNode> children = rootNode.Children;
if (children.Count <= 1)
{
// No child or one child - do nothing
return rootNode;
}
// Replace the FOJs with IJs/LOJs/Unions in the children's subtrees first
for (int i = 0; i < children.Count; i++)
{
// Method modifies input as well
children[i] = IsolateByOperator(children[i], opTypeToIsolate);
}
// Only FOJs and LOJs can be coverted (to IJs, Unions, LOJs) --
// so if the node is not that, we can ignore it (or if the node is already of
// the same type that we want)
if (rootNode.OpType != CellTreeOpType.FOJ && rootNode.OpType != CellTreeOpType.LOJ ||
rootNode.OpType == opTypeToIsolate)
{
return rootNode;
}
// Create a new node with the same type as the input cell node type
OpCellTreeNode newRootNode = new OpCellTreeNode(m_viewgenContext, rootNode.OpType);
// We start a new "group" with one of the children X - we create
// a newChildNode with type "opTypeToIsolate". Then we
// determine if any of the remaining children should be in the
// same group as X.
// childrenSet keeps track of the children that need to be procesed/partitioned
ModifiableIteratorCollection<CellTreeNode> childrenSet = new ModifiableIteratorCollection<CellTreeNode>(children);
// Find groups with same or subsumed constants and create a join
// or union node for them. We do this so that some of the FOJs
// can be replaced by union and join nodes
//
while (false == childrenSet.IsEmpty)
{
// Start a new "group" with some child node (for the opTypeToIsolate node type)
OpCellTreeNode groupNode = new OpCellTreeNode(m_viewgenContext, opTypeToIsolate);
CellTreeNode someChild = childrenSet.RemoveOneElement();
groupNode.Add(someChild);
// Go through the remaining children and determine if their
// constants are subsets/equal/disjoint w.r.t the joinNode
// constants.
foreach (CellTreeNode child in childrenSet.Elements())
{
// Check if we can add the child as part of this
// groupNode (with opTypeToIsolate being LOJ, IJ, or Union)
if (TryAddChildToGroup(opTypeToIsolate, child, groupNode))
{
childrenSet.RemoveCurrentOfIterator();
// For LOJ, suppose that child A did not subsume B or
// vice-versa. But child C subsumes both. To ensure
// that we can get A, B, C in the same group, we
// reset the iterator so that when C is added in B's
// loop, we can reconsider A.
//
// For IJ, adding a child to groupNode does not change the range of it,
// so there is no need to reconsider previously skipped children.
//
// For Union, adding a child to groupNode increases the range of the groupNode,
// hence previously skipped (because they weren't disjoint with groupNode) children will continue
// being ignored because they would still have an overlap with one of the nodes inside groupNode.
if (opTypeToIsolate == CellTreeOpType.LOJ)
{
childrenSet.ResetIterator();
}
}
}
// The new Union/LOJ/IJ node needs to be connected to the root
newRootNode.Add(groupNode);
}
return newRootNode.Flatten();
}
// effects: Determines if the childNode can be added as a child of the
// groupNode using te operation "opTypeToIsolate". E.g., if
// opTypeToIsolate is inner join, we can add child to group node if
// childNode and groupNode have the same multiconstantsets, i.e., they have
// the same selection condition
// Modifies groupNode to contain groupNode at the appropriate
// position (for LOJs, the child could be added to the beginning)
private bool TryAddChildToGroup(CellTreeOpType opTypeToIsolate, CellTreeNode childNode,
OpCellTreeNode groupNode)
{
switch (opTypeToIsolate)
{
case CellTreeOpType.IJ:
// For Inner join, the constants of the node and
// the child must be the same, i.e., if the cells
// are producing exactly same tuples (same selection)
if (IsEquivalentTo(childNode, groupNode))
{
groupNode.Add(childNode);
return true;
}
break;
case CellTreeOpType.LOJ:
// If one cell's selection condition subsumes
// another, we can use LOJ. We need to check for
// "subsumes" on both sides
if (IsContainedIn(childNode, groupNode))
{
groupNode.Add(childNode);
return true;
}
else if (IsContainedIn(groupNode, childNode))
{
// child subsumes the whole group -- add it first
groupNode.AddFirst(childNode);
return true;
}
break;
case CellTreeOpType.Union:
// If the selection conditions are disjoint, we can use UNION ALL
// We cannot use active domain here; disjointness is guaranteed only
// if we check the entire selection domain
if (IsDisjoint(childNode, groupNode))
{
groupNode.Add(childNode);
return true;
}
break;
}
return false;
}
private bool IsDisjoint(CellTreeNode n1, CellTreeNode n2)
{
bool isQueryView = (m_viewgenContext.ViewTarget == ViewTarget.QueryView);
bool isDisjointLeft = LeftQP.IsDisjointFrom(n1.LeftFragmentQuery, n2.LeftFragmentQuery);
if (isDisjointLeft && m_viewgenContext.ViewTarget == ViewTarget.QueryView)
{
return true;
}
CellTreeNode n = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.IJ, n1, n2);
bool isDisjointRight = n.IsEmptyRightFragmentQuery;
if (m_viewgenContext.ViewTarget == ViewTarget.UpdateView &&
isDisjointLeft && !isDisjointRight)
{
if (ErrorPatternMatcher.FindMappingErrors(m_viewgenContext, m_domainMap, m_errorLog))
{
return false;
}
StringBuilder builder = new StringBuilder(Strings.Viewgen_RightSideNotDisjoint(m_viewgenContext.Extent.ToString()));
builder.AppendLine();
//Retrieve the offending state
FragmentQuery intersection = LeftQP.Intersect(n1.RightFragmentQuery, n2.RightFragmentQuery);
if (LeftQP.IsSatisfiable(intersection))
{
intersection.Condition.ExpensiveSimplify();
RewritingValidator.EntityConfigurationToUserString(intersection.Condition, builder);
}
//Add Error
m_errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.DisjointConstraintViolation,
builder.ToString(), m_viewgenContext.AllWrappersForExtent, String.Empty));
ExceptionHelpers.ThrowMappingException(m_errorLog, m_config);
return false;
}
return (isDisjointLeft || isDisjointRight);
}
private bool IsContainedIn(CellTreeNode n1, CellTreeNode n2)
{
// Decide whether to IJ or LOJ using the domains that are filtered by the active domain
// The net effect is that some unneeded multiconstants will be pruned away in IJ/LOJ
// It is desirable to do so since we are only interested in the active domain
FragmentQuery n1Active = LeftQP.Intersect(n1.LeftFragmentQuery, m_activeDomain);
FragmentQuery n2Active = LeftQP.Intersect(n2.LeftFragmentQuery, m_activeDomain);
bool isContainedLeft = LeftQP.IsContainedIn(n1Active, n2Active);
if (isContainedLeft)
{
return true;
}
CellTreeNode n = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.LASJ, n1, n2);
bool isContainedRight = n.IsEmptyRightFragmentQuery;
return isContainedRight;
}
private bool IsEquivalentTo(CellTreeNode n1, CellTreeNode n2)
{
return IsContainedIn(n1, n2) && IsContainedIn(n2, n1);
}
#endregion
#region String methods
internal override void ToCompactString(StringBuilder builder)
{
// We just print the slotmap for now
m_projectedSlotMap.ToCompactString(builder);
}
#endregion
}
}
|