Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataEntity / System / Data / Map / ViewGeneration / QueryRewriting / RewritingValidator.cs / 1305376 / RewritingValidator.cs
//---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- using System.Data.Mapping.ViewGeneration.Structures; using System.Data.Mapping.ViewGeneration.QueryRewriting; using System.Data.Common.Utils.Boolean; using System.Collections.Generic; using System.Text; using System.Diagnostics; using System.Data.Mapping.ViewGeneration.Utils; using System.Data.Metadata.Edm; using System.Data.Common.Utils; using System.Linq; using System.Globalization; using System.Data.Entity; namespace System.Data.Mapping.ViewGeneration.Validation { using BoolDomainConstraint = DomainConstraint; /// /// Validates each mapping fragment/cell (Qc = Qs) /// by unfolding update views in Qs and checking query equivalence /// internal class RewritingValidator { private ViewgenContext _viewgenContext; private MemberDomainMap _domainMap; private CellTreeNode _basicView; private IEnumerable_keyAttributes; private ErrorLog _errorLog; internal RewritingValidator(ViewgenContext context, CellTreeNode basicView) { _viewgenContext = context; _basicView = basicView; _domainMap = _viewgenContext.MemberMaps.UpdateDomainMap; _keyAttributes = MemberPath.GetKeyMembers(_viewgenContext.Extent, _domainMap); _errorLog = new ErrorLog(); } #region Main logic internal void Validate() { // turn rewritings into cell trees // plain: according to rewritings for case statements Dictionary plainMemberValueTrees = CreateMemberValueTrees(false); // complement: uses complement rewriting for the last WHEN ... THEN // This is how the final case statement will be generated in update views Dictionary complementMemberValueTrees = CreateMemberValueTrees(true); WhereClauseVisitor plainWhereClauseVisitor = new WhereClauseVisitor(_basicView, plainMemberValueTrees); WhereClauseVisitor complementWhereClauseVisitor = new WhereClauseVisitor(_basicView, complementMemberValueTrees); // produce CellTree for each SQuery foreach (LeftCellWrapper wrapper in _viewgenContext.AllWrappersForExtent) { Cell cell = wrapper.OnlyInputCell; // construct cell tree for CQuery CellTreeNode cQueryTree = new LeafCellTreeNode(_viewgenContext, wrapper); // sQueryTree: unfolded update view inside S-side of the cell CellTreeNode sQueryTree; // construct cell tree for SQuery (will be used for domain constraint checking) CellTreeNode complementSQueryTreeForCondition = complementWhereClauseVisitor.GetCellTreeNode(cell.SQuery.WhereClause); Debug.Assert(complementSQueryTreeForCondition != null, "Rewriting for S-side query is unsatisfiable"); if (complementSQueryTreeForCondition == null) { continue; // situation should never happen } if (complementSQueryTreeForCondition != _basicView) { // intersect with basic expression sQueryTree = new OpCellTreeNode(_viewgenContext, CellTreeOpType.IJ, complementSQueryTreeForCondition, _basicView); } else { sQueryTree = _basicView; } // Append in-set or in-end condition to both queries to produce more concise errors // Otherwise, the errors are of the form "if there exists an entity in extent, then violation". We don't care about empty extents BoolExpression inExtentCondition = BoolExpression.CreateLiteral(wrapper.CreateRoleBoolean(), _viewgenContext.MemberMaps.QueryDomainMap); BoolExpression unsatisfiedConstraint; if (!CheckEquivalence(cQueryTree.RightFragmentQuery, sQueryTree.RightFragmentQuery, inExtentCondition, out unsatisfiedConstraint)) { string extentName = StringUtil.FormatInvariant("{0}", _viewgenContext.Extent); // Simplify to produce more readable error messages cQueryTree.RightFragmentQuery.Condition.ExpensiveSimplify(); sQueryTree.RightFragmentQuery.Condition.ExpensiveSimplify(); String message = Strings.ViewGen_CQ_PartitionConstraint_1(extentName); ReportConstraintViolation(message, unsatisfiedConstraint, ViewGenErrorCode.PartitionConstraintViolation, cQueryTree.GetLeaves().Concat(sQueryTree.GetLeaves())); } CellTreeNode plainSQueryTreeForCondition = plainWhereClauseVisitor.GetCellTreeNode(cell.SQuery.WhereClause); Debug.Assert(plainSQueryTreeForCondition != null, "Rewriting for S-side query is unsatisfiable"); if (plainSQueryTreeForCondition != null) { // Query is non-empty. Check domain constraints on: // (a) swapped members DomainConstraintVisitor.CheckConstraints(plainSQueryTreeForCondition, wrapper, _viewgenContext, _errorLog); //If you have already found errors, just continue on to the next wrapper instead of //collecting more errors for the same if (_errorLog.Count > 0) { continue; } // (b) projected members CheckConstraintsOnProjectedConditionMembers(plainMemberValueTrees, wrapper, sQueryTree, inExtentCondition); if (_errorLog.Count > 0) { continue; } } CheckConstraintsOnNonNullableMembers(plainMemberValueTrees, wrapper, sQueryTree, inExtentCondition); } if (_errorLog.Count > 0) { ExceptionHelpers.ThrowMappingException(_errorLog, _viewgenContext.Config); } } // Checks equivalence of two C-side queries // inExtentConstraint holds a role variable that effectively denotes that some extent is non-empty private bool CheckEquivalence(FragmentQuery cQuery, FragmentQuery sQuery, BoolExpression inExtentCondition, out BoolExpression unsatisfiedConstraint) { FragmentQuery cMinusSx = _viewgenContext.RightFragmentQP.Difference(cQuery, sQuery); FragmentQuery sMinusCx = _viewgenContext.RightFragmentQP.Difference(sQuery, cQuery); // add in-extent condition FragmentQuery cMinusS = FragmentQuery.Create(BoolExpression.CreateAnd(cMinusSx.Condition, inExtentCondition)); FragmentQuery sMinusC = FragmentQuery.Create(BoolExpression.CreateAnd(sMinusCx.Condition, inExtentCondition)); unsatisfiedConstraint = null; bool forwardInclusion = true; bool backwardInclusion = true; if (_viewgenContext.RightFragmentQP.IsSatisfiable(cMinusS)) { unsatisfiedConstraint = cMinusS.Condition; forwardInclusion = false; } if (_viewgenContext.RightFragmentQP.IsSatisfiable(sMinusC)) { unsatisfiedConstraint = sMinusC.Condition; backwardInclusion = false; } if (forwardInclusion && backwardInclusion) { return true; } else { unsatisfiedConstraint.ExpensiveSimplify(); return false; } } private void ReportConstraintViolation(string message, BoolExpression extraConstraint, ViewGenErrorCode errorCode, IEnumerable relevantWrappers) { if (ErrorPatternMatcher.FindMappingErrors(_viewgenContext, _domainMap, _errorLog)) { return; } extraConstraint.ExpensiveSimplify(); // gather all relevant cell wrappers and sort them in the original input order HashSet relevantCellWrappers = new HashSet (relevantWrappers); List relevantWrapperList = new List (relevantCellWrappers); relevantWrapperList.Sort(LeftCellWrapper.OriginalCellIdComparer); StringBuilder builder = new StringBuilder(); builder.AppendLine(message); EntityConfigurationToUserString(extraConstraint, builder); _errorLog.AddEntry(new ErrorLog.Record(true, errorCode, builder.ToString(), relevantCellWrappers, "")); } // according to case statements, where WHEN ... THEN was replaced by ELSE private Dictionary CreateMemberValueTrees(bool complementElse) { Dictionary memberValueTrees = new Dictionary (); foreach (MemberPath column in _domainMap.ConditionMembers(_viewgenContext.Extent)) { List domain = new List (_domainMap.GetDomain(column)); // all domain members but the last OpCellTreeNode memberCover = new OpCellTreeNode(_viewgenContext, CellTreeOpType.Union); for (int i = 0; i < domain.Count; i++) { Constant domainValue = domain[i]; MemberValueBinding memberValue = new MemberValueBinding(column, domainValue); FragmentQuery memberConditionQuery = QueryRewriter.CreateMemberConditionQuery(column, domainValue, _keyAttributes, _domainMap); Tile rewriting; if (_viewgenContext.TryGetCachedRewriting(memberConditionQuery, out rewriting)) { // turn rewriting into a cell tree CellTreeNode cellTreeNode = QueryRewriter.TileToCellTree(rewriting, _viewgenContext); memberValueTrees[memberValue] = cellTreeNode; // collect a union of all domain constants but the last if (i < domain.Count - 1) { memberCover.Add(cellTreeNode); } } else { Debug.Fail(String.Format(CultureInfo.InvariantCulture, "No cached rewriting for {0}={1}", column, domainValue)); } } if (complementElse && domain.Count > 1) { Constant lastDomainValue = domain[domain.Count - 1]; MemberValueBinding lastMemberValue = new MemberValueBinding(column, lastDomainValue); memberValueTrees[lastMemberValue] = new OpCellTreeNode(_viewgenContext, CellTreeOpType.LASJ, _basicView, memberCover); } } return memberValueTrees; } #endregion #region Checking constraints on projected condition members private void CheckConstraintsOnProjectedConditionMembers(Dictionary memberValueTrees, LeftCellWrapper wrapper, CellTreeNode sQueryTree, BoolExpression inExtentCondition) { // for S-side condition members that are projected, // add condition on both sides of the mapping constraint, and check key equivalence // applies to columns that are (1) projected and (2) conditional foreach (MemberPath column in _domainMap.ConditionMembers(_viewgenContext.Extent)) { // Get the slot on the C side and see if it is projected int index = _viewgenContext.MemberMaps.ProjectedSlotMap.IndexOf(column); MemberProjectedSlot slot = wrapper.RightCellQuery.ProjectedSlotAt(index) as MemberProjectedSlot; if (slot != null) { foreach (Constant domainValue in _domainMap.GetDomain(column)) { CellTreeNode sQueryTreeForDomainValue; if (memberValueTrees.TryGetValue(new MemberValueBinding(column, domainValue), out sQueryTreeForDomainValue)) { BoolExpression cWhereClause = PropagateCellConstantsToWhereClause(wrapper, wrapper.RightCellQuery.WhereClause, domainValue, column, _viewgenContext.MemberMaps); FragmentQuery cCombinedQuery = FragmentQuery.Create(cWhereClause); CellTreeNode sCombinedTree = (sQueryTree == _basicView) ? sQueryTreeForDomainValue : new OpCellTreeNode(_viewgenContext, CellTreeOpType.IJ, sQueryTreeForDomainValue, sQueryTree); BoolExpression unsatisfiedConstraint; if (!CheckEquivalence(cCombinedQuery, sCombinedTree.RightFragmentQuery, inExtentCondition, out unsatisfiedConstraint)) { string memberLossMessage = Strings.ViewGen_CQ_DomainConstraint_1(slot.ToUserString()); ReportConstraintViolation(memberLossMessage, unsatisfiedConstraint, ViewGenErrorCode.DomainConstraintViolation, sCombinedTree.GetLeaves().Concat(new LeftCellWrapper[] { wrapper })); } } } } } } // effects: Given a sequence of constants that need to be propagated // to the C-side and the current boolean expression, generates a new // expression of the form "expression AND C-side Member in constants" // expression" and returns it. Each constant is propagated only if member // is projected -- if member is not projected, returns "expression" internal static BoolExpression PropagateCellConstantsToWhereClause(LeftCellWrapper wrapper, BoolExpression expression, Constant constant, MemberPath member, MemberMaps memberMaps) { MemberProjectedSlot joinSlot = wrapper.GetCSideMappedSlotForSMember(member); if (joinSlot == null) { return expression; } // Look at the constants and determine if they correspond to // typeConstants or scalarConstants // This slot is being projected. We need to add a where clause element Debug.Assert(constant is ScalarConstant || constant.IsNull() || constant is NegatedConstant, "Invalid type of constant"); // We want the possible values for joinSlot.MemberPath which is a // C-side element -- so we use the queryDomainMap IEnumerable possibleValues = memberMaps.QueryDomainMap.GetDomain(joinSlot.MemberPath); // Note: the values in constaints can be null or not null as // well (i.e., just not scalarConstants) Set allowedValues = new Set (Constant.EqualityComparer); if (constant is NegatedConstant) { // select all values from the c-side domain that are not in the negated set allowedValues.Unite(possibleValues); allowedValues.Difference(((NegatedConstant)constant).Elements); } else { allowedValues.Add(constant); } MemberRestriction restriction = new ScalarRestriction(joinSlot.MemberPath, allowedValues, possibleValues); BoolExpression result = BoolExpression.CreateAnd(expression, BoolExpression.CreateLiteral(restriction, memberMaps.QueryDomainMap)); return result; } #endregion /// /// Given a LeftCellWrapper for the S-side fragment and a non-nullable colum m, return a CQuery with nullability condition /// appended to Cquery of c-side member that column m is mapped to /// private static FragmentQuery AddNullConditionOnCSideFragment(LeftCellWrapper wrapper, MemberPath member, MemberMaps memberMaps) { MemberProjectedSlot projectedSlot = wrapper.GetCSideMappedSlotForSMember(member); if (projectedSlot == null || !projectedSlot.MemberPath.IsNullable) //don't bother checking further fore non nullable C-side member { return null; } BoolExpression expression = wrapper.RightCellQuery.WhereClause; IEnumerablepossibleValues = memberMaps.QueryDomainMap.GetDomain(projectedSlot.MemberPath); Set allowedValues = new Set (Constant.EqualityComparer); allowedValues.Add(Constant.Null); //Create a condition as conjunction of originalCondition and slot IS NULL MemberRestriction restriction = new ScalarRestriction(projectedSlot.MemberPath, allowedValues, possibleValues); BoolExpression resultingExpr = BoolExpression.CreateAnd(expression, BoolExpression.CreateLiteral(restriction, memberMaps.QueryDomainMap)); return FragmentQuery.Create(resultingExpr); } /// /// Checks whether non nullable S-side members are mapped to nullable C-query. /// It is possible that C-side attribute is nullable but the fragment's C-query is not /// private void CheckConstraintsOnNonNullableMembers(DictionarymemberValueTrees, LeftCellWrapper wrapper, CellTreeNode sQueryTree, BoolExpression inExtentCondition) { //For each non-condition member that has non-nullability constraint foreach (MemberPath column in _domainMap.NonConditionMembers(_viewgenContext.Extent)) { bool isColumnSimpleType = (column.EdmType as System.Data.Metadata.Edm.SimpleType) != null; if (!column.IsNullable && isColumnSimpleType) { FragmentQuery cFragment = AddNullConditionOnCSideFragment(wrapper, column, _viewgenContext.MemberMaps); if (cFragment != null && _viewgenContext.RightFragmentQP.IsSatisfiable(cFragment)) { _errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.NullableMappingForNonNullableColumn, Strings.Viewgen_NullableMappingForNonNullableColumn(wrapper.LeftExtent.ToString(), column.ToFullString()), wrapper.Cells, "")); } } } } #region Methods for turning a boolean condition into user string internal static void EntityConfigurationToUserString(BoolExpression condition, StringBuilder builder) { //By default write the Round tripping message EntityConfigurationToUserString(condition, builder, true); } internal static void EntityConfigurationToUserString(BoolExpression condition, StringBuilder builder, bool writeRoundTrippingMessage) { condition.AsUserString(builder, "PK", writeRoundTrippingMessage); } #endregion #region WhereClauseVisitor: turns WHERE clause into CellTreeNode private class WhereClauseVisitor : Visitor , CellTreeNode> { ViewgenContext _viewgenContext; CellTreeNode _topLevelTree; Dictionary _memberValueTrees; internal WhereClauseVisitor(CellTreeNode topLevelTree, Dictionary memberValueTrees) { _topLevelTree = topLevelTree; _memberValueTrees = memberValueTrees; _viewgenContext = topLevelTree.ViewgenContext; } // returns _topLevelTree when expression evaluates to True, null if it evaluates to False internal CellTreeNode GetCellTreeNode(BoolExpression whereClause) { return whereClause.Tree.Accept(this); } internal override CellTreeNode VisitAnd(AndExpr > expression) { IEnumerable childrenTrees = AcceptChildren(expression.Children); OpCellTreeNode node = new OpCellTreeNode(_viewgenContext, CellTreeOpType.IJ); foreach (CellTreeNode childNode in childrenTrees) { if (childNode == null) { return null; // unsatisfiable } if (childNode != _topLevelTree) { node.Add(childNode); } } return node.Children.Count == 0 ? _topLevelTree : node; } internal override CellTreeNode VisitTrue(TrueExpr > expression) { return _topLevelTree; } internal override CellTreeNode VisitTerm(TermExpr > expression) { MemberRestriction oneOf = (MemberRestriction)expression.Identifier.Variable.Identifier; Set range = expression.Identifier.Range; // create a disjunction OpCellTreeNode disjunctionNode = new OpCellTreeNode(_viewgenContext, CellTreeOpType.Union); CellTreeNode singleNode = null; foreach (Constant value in range) { if (TryGetCellTreeNode(oneOf.RestrictedMemberSlot.MemberPath, value, out singleNode)) { disjunctionNode.Add(singleNode); } // else, there is no rewriting for this member value, i.e., it is empty } switch (disjunctionNode.Children.Count) { case 0: return null; // empty rewriting case 1: return singleNode; default: return disjunctionNode; } } internal override CellTreeNode VisitFalse(FalseExpr > expression) { throw new NotImplementedException(); } internal override CellTreeNode VisitNot(NotExpr > expression) { throw new NotImplementedException(); } internal override CellTreeNode VisitOr(OrExpr > expression) { throw new NotImplementedException(); } private bool TryGetCellTreeNode(MemberPath memberPath, Constant value, out CellTreeNode singleNode) { return (_memberValueTrees.TryGetValue(new MemberValueBinding(memberPath, value), out singleNode)); } private IEnumerable AcceptChildren(IEnumerable >> children) { foreach (BoolExpr > child in children) { yield return child.Accept(this); } } } #endregion #region DomainConstraintVisitor: checks domain constraints internal class DomainConstraintVisitor : CellTreeNode.SimpleCellTreeVisitor { LeftCellWrapper m_wrapper; ViewgenContext m_viewgenContext; ErrorLog m_errorLog; private DomainConstraintVisitor(LeftCellWrapper wrapper, ViewgenContext context, ErrorLog errorLog) { m_wrapper = wrapper; m_viewgenContext = context; m_errorLog = errorLog; } internal static void CheckConstraints(CellTreeNode node, LeftCellWrapper wrapper, ViewgenContext context, ErrorLog errorLog) { DomainConstraintVisitor visitor = new DomainConstraintVisitor(wrapper, context, errorLog); node.Accept (visitor, true); } internal override bool VisitLeaf(LeafCellTreeNode node, bool dummy) { // make sure all projected attributes in wrapper correspond exactly to those in node CellQuery thisQuery = m_wrapper.RightCellQuery; CellQuery thatQuery = node.LeftCellWrapper.RightCellQuery; List collidingColumns = new List (); if (thisQuery != thatQuery) { for (int i = 0; i < thisQuery.NumProjectedSlots; i++) { MemberProjectedSlot thisSlot = thisQuery.ProjectedSlotAt(i) as MemberProjectedSlot; if (thisSlot != null) { MemberProjectedSlot thatSlot = thatQuery.ProjectedSlotAt(i) as MemberProjectedSlot; if (thatSlot != null) { MemberPath tableMember = m_viewgenContext.MemberMaps.ProjectedSlotMap[i]; if (!tableMember.IsPartOfKey) { if (!MemberPath.EqualityComparer.Equals(thisSlot.MemberPath, thatSlot.MemberPath)) { collidingColumns.Add(tableMember); } } } } } } if (collidingColumns.Count > 0) { string columnsString = MemberPath.PropertiesToUserString(collidingColumns, false); string message = Strings.ViewGen_NonKeyProjectedWithOverlappingPartitions_0(columnsString); ErrorLog.Record record = new ErrorLog.Record(true, ViewGenErrorCode.NonKeyProjectedWithOverlappingPartitions, message, new LeftCellWrapper[] { m_wrapper, node.LeftCellWrapper }, String.Empty); m_errorLog.AddEntry(record); } return true; } internal override bool VisitOpNode(OpCellTreeNode node, bool dummy) { if (node.OpType == CellTreeOpType.LASJ) { // add conditions only on the positive node node.Children[0].Accept (this, dummy); } else { foreach (CellTreeNode child in node.Children) { child.Accept (this, dummy); } } return true; } } #endregion #region MemberValueBinding struct: (MemberPath, CellConstant) pair private struct MemberValueBinding : IEquatable { internal readonly MemberPath Member; internal readonly Constant Value; public MemberValueBinding(MemberPath member, Constant value) { Member = member; Value = value; } public override string ToString() { return String.Format(CultureInfo.InvariantCulture, "{0}={1}", Member, Value); } #region IEquatable Members public bool Equals(MemberValueBinding other) { return MemberPath.EqualityComparer.Equals(Member, other.Member) && Constant.EqualityComparer.Equals(Value, other.Value); } #endregion } #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //---------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- using System.Data.Mapping.ViewGeneration.Structures; using System.Data.Mapping.ViewGeneration.QueryRewriting; using System.Data.Common.Utils.Boolean; using System.Collections.Generic; using System.Text; using System.Diagnostics; using System.Data.Mapping.ViewGeneration.Utils; using System.Data.Metadata.Edm; using System.Data.Common.Utils; using System.Linq; using System.Globalization; using System.Data.Entity; namespace System.Data.Mapping.ViewGeneration.Validation { using BoolDomainConstraint = DomainConstraint; /// /// Validates each mapping fragment/cell (Qc = Qs) /// by unfolding update views in Qs and checking query equivalence /// internal class RewritingValidator { private ViewgenContext _viewgenContext; private MemberDomainMap _domainMap; private CellTreeNode _basicView; private IEnumerable_keyAttributes; private ErrorLog _errorLog; internal RewritingValidator(ViewgenContext context, CellTreeNode basicView) { _viewgenContext = context; _basicView = basicView; _domainMap = _viewgenContext.MemberMaps.UpdateDomainMap; _keyAttributes = MemberPath.GetKeyMembers(_viewgenContext.Extent, _domainMap); _errorLog = new ErrorLog(); } #region Main logic internal void Validate() { // turn rewritings into cell trees // plain: according to rewritings for case statements Dictionary plainMemberValueTrees = CreateMemberValueTrees(false); // complement: uses complement rewriting for the last WHEN ... THEN // This is how the final case statement will be generated in update views Dictionary complementMemberValueTrees = CreateMemberValueTrees(true); WhereClauseVisitor plainWhereClauseVisitor = new WhereClauseVisitor(_basicView, plainMemberValueTrees); WhereClauseVisitor complementWhereClauseVisitor = new WhereClauseVisitor(_basicView, complementMemberValueTrees); // produce CellTree for each SQuery foreach (LeftCellWrapper wrapper in _viewgenContext.AllWrappersForExtent) { Cell cell = wrapper.OnlyInputCell; // construct cell tree for CQuery CellTreeNode cQueryTree = new LeafCellTreeNode(_viewgenContext, wrapper); // sQueryTree: unfolded update view inside S-side of the cell CellTreeNode sQueryTree; // construct cell tree for SQuery (will be used for domain constraint checking) CellTreeNode complementSQueryTreeForCondition = complementWhereClauseVisitor.GetCellTreeNode(cell.SQuery.WhereClause); Debug.Assert(complementSQueryTreeForCondition != null, "Rewriting for S-side query is unsatisfiable"); if (complementSQueryTreeForCondition == null) { continue; // situation should never happen } if (complementSQueryTreeForCondition != _basicView) { // intersect with basic expression sQueryTree = new OpCellTreeNode(_viewgenContext, CellTreeOpType.IJ, complementSQueryTreeForCondition, _basicView); } else { sQueryTree = _basicView; } // Append in-set or in-end condition to both queries to produce more concise errors // Otherwise, the errors are of the form "if there exists an entity in extent, then violation". We don't care about empty extents BoolExpression inExtentCondition = BoolExpression.CreateLiteral(wrapper.CreateRoleBoolean(), _viewgenContext.MemberMaps.QueryDomainMap); BoolExpression unsatisfiedConstraint; if (!CheckEquivalence(cQueryTree.RightFragmentQuery, sQueryTree.RightFragmentQuery, inExtentCondition, out unsatisfiedConstraint)) { string extentName = StringUtil.FormatInvariant("{0}", _viewgenContext.Extent); // Simplify to produce more readable error messages cQueryTree.RightFragmentQuery.Condition.ExpensiveSimplify(); sQueryTree.RightFragmentQuery.Condition.ExpensiveSimplify(); String message = Strings.ViewGen_CQ_PartitionConstraint_1(extentName); ReportConstraintViolation(message, unsatisfiedConstraint, ViewGenErrorCode.PartitionConstraintViolation, cQueryTree.GetLeaves().Concat(sQueryTree.GetLeaves())); } CellTreeNode plainSQueryTreeForCondition = plainWhereClauseVisitor.GetCellTreeNode(cell.SQuery.WhereClause); Debug.Assert(plainSQueryTreeForCondition != null, "Rewriting for S-side query is unsatisfiable"); if (plainSQueryTreeForCondition != null) { // Query is non-empty. Check domain constraints on: // (a) swapped members DomainConstraintVisitor.CheckConstraints(plainSQueryTreeForCondition, wrapper, _viewgenContext, _errorLog); //If you have already found errors, just continue on to the next wrapper instead of //collecting more errors for the same if (_errorLog.Count > 0) { continue; } // (b) projected members CheckConstraintsOnProjectedConditionMembers(plainMemberValueTrees, wrapper, sQueryTree, inExtentCondition); if (_errorLog.Count > 0) { continue; } } CheckConstraintsOnNonNullableMembers(plainMemberValueTrees, wrapper, sQueryTree, inExtentCondition); } if (_errorLog.Count > 0) { ExceptionHelpers.ThrowMappingException(_errorLog, _viewgenContext.Config); } } // Checks equivalence of two C-side queries // inExtentConstraint holds a role variable that effectively denotes that some extent is non-empty private bool CheckEquivalence(FragmentQuery cQuery, FragmentQuery sQuery, BoolExpression inExtentCondition, out BoolExpression unsatisfiedConstraint) { FragmentQuery cMinusSx = _viewgenContext.RightFragmentQP.Difference(cQuery, sQuery); FragmentQuery sMinusCx = _viewgenContext.RightFragmentQP.Difference(sQuery, cQuery); // add in-extent condition FragmentQuery cMinusS = FragmentQuery.Create(BoolExpression.CreateAnd(cMinusSx.Condition, inExtentCondition)); FragmentQuery sMinusC = FragmentQuery.Create(BoolExpression.CreateAnd(sMinusCx.Condition, inExtentCondition)); unsatisfiedConstraint = null; bool forwardInclusion = true; bool backwardInclusion = true; if (_viewgenContext.RightFragmentQP.IsSatisfiable(cMinusS)) { unsatisfiedConstraint = cMinusS.Condition; forwardInclusion = false; } if (_viewgenContext.RightFragmentQP.IsSatisfiable(sMinusC)) { unsatisfiedConstraint = sMinusC.Condition; backwardInclusion = false; } if (forwardInclusion && backwardInclusion) { return true; } else { unsatisfiedConstraint.ExpensiveSimplify(); return false; } } private void ReportConstraintViolation(string message, BoolExpression extraConstraint, ViewGenErrorCode errorCode, IEnumerable relevantWrappers) { if (ErrorPatternMatcher.FindMappingErrors(_viewgenContext, _domainMap, _errorLog)) { return; } extraConstraint.ExpensiveSimplify(); // gather all relevant cell wrappers and sort them in the original input order HashSet relevantCellWrappers = new HashSet (relevantWrappers); List relevantWrapperList = new List (relevantCellWrappers); relevantWrapperList.Sort(LeftCellWrapper.OriginalCellIdComparer); StringBuilder builder = new StringBuilder(); builder.AppendLine(message); EntityConfigurationToUserString(extraConstraint, builder); _errorLog.AddEntry(new ErrorLog.Record(true, errorCode, builder.ToString(), relevantCellWrappers, "")); } // according to case statements, where WHEN ... THEN was replaced by ELSE private Dictionary CreateMemberValueTrees(bool complementElse) { Dictionary memberValueTrees = new Dictionary (); foreach (MemberPath column in _domainMap.ConditionMembers(_viewgenContext.Extent)) { List domain = new List (_domainMap.GetDomain(column)); // all domain members but the last OpCellTreeNode memberCover = new OpCellTreeNode(_viewgenContext, CellTreeOpType.Union); for (int i = 0; i < domain.Count; i++) { Constant domainValue = domain[i]; MemberValueBinding memberValue = new MemberValueBinding(column, domainValue); FragmentQuery memberConditionQuery = QueryRewriter.CreateMemberConditionQuery(column, domainValue, _keyAttributes, _domainMap); Tile rewriting; if (_viewgenContext.TryGetCachedRewriting(memberConditionQuery, out rewriting)) { // turn rewriting into a cell tree CellTreeNode cellTreeNode = QueryRewriter.TileToCellTree(rewriting, _viewgenContext); memberValueTrees[memberValue] = cellTreeNode; // collect a union of all domain constants but the last if (i < domain.Count - 1) { memberCover.Add(cellTreeNode); } } else { Debug.Fail(String.Format(CultureInfo.InvariantCulture, "No cached rewriting for {0}={1}", column, domainValue)); } } if (complementElse && domain.Count > 1) { Constant lastDomainValue = domain[domain.Count - 1]; MemberValueBinding lastMemberValue = new MemberValueBinding(column, lastDomainValue); memberValueTrees[lastMemberValue] = new OpCellTreeNode(_viewgenContext, CellTreeOpType.LASJ, _basicView, memberCover); } } return memberValueTrees; } #endregion #region Checking constraints on projected condition members private void CheckConstraintsOnProjectedConditionMembers(Dictionary memberValueTrees, LeftCellWrapper wrapper, CellTreeNode sQueryTree, BoolExpression inExtentCondition) { // for S-side condition members that are projected, // add condition on both sides of the mapping constraint, and check key equivalence // applies to columns that are (1) projected and (2) conditional foreach (MemberPath column in _domainMap.ConditionMembers(_viewgenContext.Extent)) { // Get the slot on the C side and see if it is projected int index = _viewgenContext.MemberMaps.ProjectedSlotMap.IndexOf(column); MemberProjectedSlot slot = wrapper.RightCellQuery.ProjectedSlotAt(index) as MemberProjectedSlot; if (slot != null) { foreach (Constant domainValue in _domainMap.GetDomain(column)) { CellTreeNode sQueryTreeForDomainValue; if (memberValueTrees.TryGetValue(new MemberValueBinding(column, domainValue), out sQueryTreeForDomainValue)) { BoolExpression cWhereClause = PropagateCellConstantsToWhereClause(wrapper, wrapper.RightCellQuery.WhereClause, domainValue, column, _viewgenContext.MemberMaps); FragmentQuery cCombinedQuery = FragmentQuery.Create(cWhereClause); CellTreeNode sCombinedTree = (sQueryTree == _basicView) ? sQueryTreeForDomainValue : new OpCellTreeNode(_viewgenContext, CellTreeOpType.IJ, sQueryTreeForDomainValue, sQueryTree); BoolExpression unsatisfiedConstraint; if (!CheckEquivalence(cCombinedQuery, sCombinedTree.RightFragmentQuery, inExtentCondition, out unsatisfiedConstraint)) { string memberLossMessage = Strings.ViewGen_CQ_DomainConstraint_1(slot.ToUserString()); ReportConstraintViolation(memberLossMessage, unsatisfiedConstraint, ViewGenErrorCode.DomainConstraintViolation, sCombinedTree.GetLeaves().Concat(new LeftCellWrapper[] { wrapper })); } } } } } } // effects: Given a sequence of constants that need to be propagated // to the C-side and the current boolean expression, generates a new // expression of the form "expression AND C-side Member in constants" // expression" and returns it. Each constant is propagated only if member // is projected -- if member is not projected, returns "expression" internal static BoolExpression PropagateCellConstantsToWhereClause(LeftCellWrapper wrapper, BoolExpression expression, Constant constant, MemberPath member, MemberMaps memberMaps) { MemberProjectedSlot joinSlot = wrapper.GetCSideMappedSlotForSMember(member); if (joinSlot == null) { return expression; } // Look at the constants and determine if they correspond to // typeConstants or scalarConstants // This slot is being projected. We need to add a where clause element Debug.Assert(constant is ScalarConstant || constant.IsNull() || constant is NegatedConstant, "Invalid type of constant"); // We want the possible values for joinSlot.MemberPath which is a // C-side element -- so we use the queryDomainMap IEnumerable possibleValues = memberMaps.QueryDomainMap.GetDomain(joinSlot.MemberPath); // Note: the values in constaints can be null or not null as // well (i.e., just not scalarConstants) Set allowedValues = new Set (Constant.EqualityComparer); if (constant is NegatedConstant) { // select all values from the c-side domain that are not in the negated set allowedValues.Unite(possibleValues); allowedValues.Difference(((NegatedConstant)constant).Elements); } else { allowedValues.Add(constant); } MemberRestriction restriction = new ScalarRestriction(joinSlot.MemberPath, allowedValues, possibleValues); BoolExpression result = BoolExpression.CreateAnd(expression, BoolExpression.CreateLiteral(restriction, memberMaps.QueryDomainMap)); return result; } #endregion /// /// Given a LeftCellWrapper for the S-side fragment and a non-nullable colum m, return a CQuery with nullability condition /// appended to Cquery of c-side member that column m is mapped to /// private static FragmentQuery AddNullConditionOnCSideFragment(LeftCellWrapper wrapper, MemberPath member, MemberMaps memberMaps) { MemberProjectedSlot projectedSlot = wrapper.GetCSideMappedSlotForSMember(member); if (projectedSlot == null || !projectedSlot.MemberPath.IsNullable) //don't bother checking further fore non nullable C-side member { return null; } BoolExpression expression = wrapper.RightCellQuery.WhereClause; IEnumerablepossibleValues = memberMaps.QueryDomainMap.GetDomain(projectedSlot.MemberPath); Set allowedValues = new Set (Constant.EqualityComparer); allowedValues.Add(Constant.Null); //Create a condition as conjunction of originalCondition and slot IS NULL MemberRestriction restriction = new ScalarRestriction(projectedSlot.MemberPath, allowedValues, possibleValues); BoolExpression resultingExpr = BoolExpression.CreateAnd(expression, BoolExpression.CreateLiteral(restriction, memberMaps.QueryDomainMap)); return FragmentQuery.Create(resultingExpr); } /// /// Checks whether non nullable S-side members are mapped to nullable C-query. /// It is possible that C-side attribute is nullable but the fragment's C-query is not /// private void CheckConstraintsOnNonNullableMembers(DictionarymemberValueTrees, LeftCellWrapper wrapper, CellTreeNode sQueryTree, BoolExpression inExtentCondition) { //For each non-condition member that has non-nullability constraint foreach (MemberPath column in _domainMap.NonConditionMembers(_viewgenContext.Extent)) { bool isColumnSimpleType = (column.EdmType as System.Data.Metadata.Edm.SimpleType) != null; if (!column.IsNullable && isColumnSimpleType) { FragmentQuery cFragment = AddNullConditionOnCSideFragment(wrapper, column, _viewgenContext.MemberMaps); if (cFragment != null && _viewgenContext.RightFragmentQP.IsSatisfiable(cFragment)) { _errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.NullableMappingForNonNullableColumn, Strings.Viewgen_NullableMappingForNonNullableColumn(wrapper.LeftExtent.ToString(), column.ToFullString()), wrapper.Cells, "")); } } } } #region Methods for turning a boolean condition into user string internal static void EntityConfigurationToUserString(BoolExpression condition, StringBuilder builder) { //By default write the Round tripping message EntityConfigurationToUserString(condition, builder, true); } internal static void EntityConfigurationToUserString(BoolExpression condition, StringBuilder builder, bool writeRoundTrippingMessage) { condition.AsUserString(builder, "PK", writeRoundTrippingMessage); } #endregion #region WhereClauseVisitor: turns WHERE clause into CellTreeNode private class WhereClauseVisitor : Visitor , CellTreeNode> { ViewgenContext _viewgenContext; CellTreeNode _topLevelTree; Dictionary _memberValueTrees; internal WhereClauseVisitor(CellTreeNode topLevelTree, Dictionary memberValueTrees) { _topLevelTree = topLevelTree; _memberValueTrees = memberValueTrees; _viewgenContext = topLevelTree.ViewgenContext; } // returns _topLevelTree when expression evaluates to True, null if it evaluates to False internal CellTreeNode GetCellTreeNode(BoolExpression whereClause) { return whereClause.Tree.Accept(this); } internal override CellTreeNode VisitAnd(AndExpr > expression) { IEnumerable childrenTrees = AcceptChildren(expression.Children); OpCellTreeNode node = new OpCellTreeNode(_viewgenContext, CellTreeOpType.IJ); foreach (CellTreeNode childNode in childrenTrees) { if (childNode == null) { return null; // unsatisfiable } if (childNode != _topLevelTree) { node.Add(childNode); } } return node.Children.Count == 0 ? _topLevelTree : node; } internal override CellTreeNode VisitTrue(TrueExpr > expression) { return _topLevelTree; } internal override CellTreeNode VisitTerm(TermExpr > expression) { MemberRestriction oneOf = (MemberRestriction)expression.Identifier.Variable.Identifier; Set range = expression.Identifier.Range; // create a disjunction OpCellTreeNode disjunctionNode = new OpCellTreeNode(_viewgenContext, CellTreeOpType.Union); CellTreeNode singleNode = null; foreach (Constant value in range) { if (TryGetCellTreeNode(oneOf.RestrictedMemberSlot.MemberPath, value, out singleNode)) { disjunctionNode.Add(singleNode); } // else, there is no rewriting for this member value, i.e., it is empty } switch (disjunctionNode.Children.Count) { case 0: return null; // empty rewriting case 1: return singleNode; default: return disjunctionNode; } } internal override CellTreeNode VisitFalse(FalseExpr > expression) { throw new NotImplementedException(); } internal override CellTreeNode VisitNot(NotExpr > expression) { throw new NotImplementedException(); } internal override CellTreeNode VisitOr(OrExpr > expression) { throw new NotImplementedException(); } private bool TryGetCellTreeNode(MemberPath memberPath, Constant value, out CellTreeNode singleNode) { return (_memberValueTrees.TryGetValue(new MemberValueBinding(memberPath, value), out singleNode)); } private IEnumerable AcceptChildren(IEnumerable >> children) { foreach (BoolExpr > child in children) { yield return child.Accept(this); } } } #endregion #region DomainConstraintVisitor: checks domain constraints internal class DomainConstraintVisitor : CellTreeNode.SimpleCellTreeVisitor { LeftCellWrapper m_wrapper; ViewgenContext m_viewgenContext; ErrorLog m_errorLog; private DomainConstraintVisitor(LeftCellWrapper wrapper, ViewgenContext context, ErrorLog errorLog) { m_wrapper = wrapper; m_viewgenContext = context; m_errorLog = errorLog; } internal static void CheckConstraints(CellTreeNode node, LeftCellWrapper wrapper, ViewgenContext context, ErrorLog errorLog) { DomainConstraintVisitor visitor = new DomainConstraintVisitor(wrapper, context, errorLog); node.Accept (visitor, true); } internal override bool VisitLeaf(LeafCellTreeNode node, bool dummy) { // make sure all projected attributes in wrapper correspond exactly to those in node CellQuery thisQuery = m_wrapper.RightCellQuery; CellQuery thatQuery = node.LeftCellWrapper.RightCellQuery; List collidingColumns = new List (); if (thisQuery != thatQuery) { for (int i = 0; i < thisQuery.NumProjectedSlots; i++) { MemberProjectedSlot thisSlot = thisQuery.ProjectedSlotAt(i) as MemberProjectedSlot; if (thisSlot != null) { MemberProjectedSlot thatSlot = thatQuery.ProjectedSlotAt(i) as MemberProjectedSlot; if (thatSlot != null) { MemberPath tableMember = m_viewgenContext.MemberMaps.ProjectedSlotMap[i]; if (!tableMember.IsPartOfKey) { if (!MemberPath.EqualityComparer.Equals(thisSlot.MemberPath, thatSlot.MemberPath)) { collidingColumns.Add(tableMember); } } } } } } if (collidingColumns.Count > 0) { string columnsString = MemberPath.PropertiesToUserString(collidingColumns, false); string message = Strings.ViewGen_NonKeyProjectedWithOverlappingPartitions_0(columnsString); ErrorLog.Record record = new ErrorLog.Record(true, ViewGenErrorCode.NonKeyProjectedWithOverlappingPartitions, message, new LeftCellWrapper[] { m_wrapper, node.LeftCellWrapper }, String.Empty); m_errorLog.AddEntry(record); } return true; } internal override bool VisitOpNode(OpCellTreeNode node, bool dummy) { if (node.OpType == CellTreeOpType.LASJ) { // add conditions only on the positive node node.Children[0].Accept (this, dummy); } else { foreach (CellTreeNode child in node.Children) { child.Accept (this, dummy); } } return true; } } #endregion #region MemberValueBinding struct: (MemberPath, CellConstant) pair private struct MemberValueBinding : IEquatable { internal readonly MemberPath Member; internal readonly Constant Value; public MemberValueBinding(MemberPath member, Constant value) { Member = member; Value = value; } public override string ToString() { return String.Format(CultureInfo.InvariantCulture, "{0}={1}", Member, Value); } #region IEquatable Members public bool Equals(MemberValueBinding other) { return MemberPath.EqualityComparer.Equals(Member, other.Member) && Constant.EqualityComparer.Equals(Value, other.Value); } #endregion } #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- InternalConfigSettingsFactory.cs
- TextViewSelectionProcessor.cs
- DES.cs
- AssignDesigner.xaml.cs
- AddInAttribute.cs
- Brush.cs
- PngBitmapDecoder.cs
- RegistryKey.cs
- ADMembershipUser.cs
- SQLConvert.cs
- MetadataSource.cs
- HtmlInputFile.cs
- selecteditemcollection.cs
- RealProxy.cs
- SoapHeaderException.cs
- WaveHeader.cs
- ServiceHostingEnvironment.cs
- Dictionary.cs
- SqlHelper.cs
- OptimalBreakSession.cs
- UriExt.cs
- SharedHttpTransportManager.cs
- BinaryFormatter.cs
- TextEditorDragDrop.cs
- RowParagraph.cs
- DataViewSetting.cs
- PhysicalOps.cs
- OrderedParallelQuery.cs
- IItemProperties.cs
- XsltArgumentList.cs
- ActiveXContainer.cs
- WebControlsSection.cs
- AdornerDecorator.cs
- DataStreamFromComStream.cs
- CommandHelpers.cs
- ValidatedControlConverter.cs
- TextEditorThreadLocalStore.cs
- RegionInfo.cs
- TemplateControlParser.cs
- CellTreeNode.cs
- DocumentPage.cs
- TableItemProviderWrapper.cs
- NotImplementedException.cs
- MetaTableHelper.cs
- ObjectContext.cs
- ManagementObjectSearcher.cs
- SizeConverter.cs
- MsmqMessage.cs
- DataGridHeaderBorder.cs
- CheckedListBox.cs
- HitTestDrawingContextWalker.cs
- WebBrowserNavigatingEventHandler.cs
- WebHeaderCollection.cs
- BitmapData.cs
- ListViewPagedDataSource.cs
- RuntimeWrappedException.cs
- HtmlEmptyTagControlBuilder.cs
- NonClientArea.cs
- ProgressBarAutomationPeer.cs
- RectConverter.cs
- SplitContainer.cs
- initElementDictionary.cs
- WinEventHandler.cs
- ServiceDescriptions.cs
- TextProperties.cs
- TemplatedMailWebEventProvider.cs
- ProcessHostConfigUtils.cs
- _LocalDataStoreMgr.cs
- AmbientValueAttribute.cs
- _LocalDataStoreMgr.cs
- CfgParser.cs
- HwndPanningFeedback.cs
- SimpleMailWebEventProvider.cs
- HttpServerVarsCollection.cs
- XPathSelectionIterator.cs
- MouseButtonEventArgs.cs
- DbConnectionClosed.cs
- DataTableReader.cs
- QilParameter.cs
- InteropBitmapSource.cs
- StreamSecurityUpgradeAcceptorAsyncResult.cs
- BooleanConverter.cs
- ReadOnlyMetadataCollection.cs
- TreeBuilder.cs
- DataDocumentXPathNavigator.cs
- HttpCookiesSection.cs
- oledbconnectionstring.cs
- DataGridViewCellValidatingEventArgs.cs
- TransformGroup.cs
- _ShellExpression.cs
- Point3DCollectionConverter.cs
- PropertyConverter.cs
- RulePatternOps.cs
- StylusPointProperty.cs
- PersonalizationStateQuery.cs
- CodeMemberEvent.cs
- CorrelationManager.cs
- RegistrySecurity.cs
- NavigateEvent.cs
- GeneralTransform3DCollection.cs