internal static Constant GetDefaultValueForMemberPath( MemberPath memberPath, IEnumerable <LeftCellWrapper> wrappersForErrorReporting, ConfigViewGenerator config) { Constant defaultValue = null; if (!TryGetDefaultValueForMemberPath(memberPath, out defaultValue)) { var message = Strings.ViewGen_No_Default_Value(memberPath.Extent.Name, memberPath.PathToString(false)); var record = new ErrorLog.Record(ViewGenErrorCode.NoDefaultValue, message, wrappersForErrorReporting, String.Empty); ExceptionHelpers.ThrowMappingException(record, config); } return(defaultValue); }
// effects: Ensures that there is a relationship mapped into the C-space for some cell in m_cellGroup. Else // adds an error to errorLog private void GuaranteeMappedRelationshipForForeignKey( QueryRewriter childRewriter, QueryRewriter parentRewriter, IEnumerable<Cell> cells, ErrorLog errorLog, ConfigViewGenerator config) { var childContext = childRewriter.ViewgenContext; var parentContext = parentRewriter.ViewgenContext; // Find a cell where this foreign key is mapped as a relationship var prefix = new MemberPath(ChildTable); var primaryKey = ExtentKey.GetPrimaryKeyForEntityType(prefix, ChildTable.ElementType); var primaryKeyFields = primaryKey.KeyFields; var foundCell = false; var foundValidParentColumnsForForeignKey = false; //we need to find only one, dont error on any one check being false List<ErrorLog.Record> errorListForInvalidParentColumnsForForeignKey = null; foreach (var cell in cells) { if (cell.SQuery.Extent.Equals(ChildTable) == false) { continue; } // The childtable is mapped to a relationship in the C-space in cell // Check that all the columns of the foreign key and the primary key in the child table are mapped to some // property in the C-space var parentEnd = GetRelationEndForColumns(cell, ChildColumns); if (parentEnd != null && CheckParentColumnsForForeignKey(cell, cells, parentEnd, ref errorListForInvalidParentColumnsForForeignKey) == false) { // Not an error unless we find no valid case continue; } else { foundValidParentColumnsForForeignKey = true; } var childEnd = GetRelationEndForColumns(cell, primaryKeyFields); Debug.Assert( childEnd == null || parentEnd != childEnd, "Ends are same => PKey and child columns are same - code should gone to other method"); // Note: If both of them are not-null, they are mapped to the // same association set -- since we checked that particular cell if (childEnd != null && parentEnd != null && FindEntitySetForColumnsMappedToEntityKeys(cells, primaryKeyFields) != null) { foundCell = true; CheckConstraintWhenParentChildMapped(cell, errorLog, parentEnd, config); break; // Done processing for the foreign key - either it was mapped correctly or it was not } else if (parentEnd != null) { // At this point, we know cell corresponds to an association set var assocSet = (AssociationSet)cell.CQuery.Extent; var parentSet = MetadataHelper.GetEntitySetAtEnd(assocSet, parentEnd); foundCell = CheckConstraintWhenOnlyParentMapped(assocSet, parentEnd, childRewriter, parentRewriter); if (foundCell) { break; } } } //CheckParentColumnsForForeignKey has returned no matches, Error. if (!foundValidParentColumnsForForeignKey) { Debug.Assert( errorListForInvalidParentColumnsForForeignKey != null && errorListForInvalidParentColumnsForForeignKey.Count > 0); foreach (var errorRecord in errorListForInvalidParentColumnsForForeignKey) { errorLog.AddEntry(errorRecord); } return; } if (foundCell == false) { // No cell found -- Declare error var message = Strings.ViewGen_Foreign_Key_Missing_Relationship_Mapping(ToUserString()); IEnumerable<LeftCellWrapper> parentWrappers = GetWrappersFromContext(parentContext, ParentTable); IEnumerable<LeftCellWrapper> childWrappers = GetWrappersFromContext(childContext, ChildTable); var bothExtentWrappers = new Set<LeftCellWrapper>(parentWrappers); bothExtentWrappers.AddRange(childWrappers); var record = new ErrorLog.Record( ViewGenErrorCode.ForeignKeyMissingRelationshipMapping, message, bothExtentWrappers, String.Empty); errorLog.AddEntry(record); } }
// requires: constraint.ChildColumns form a key in // constraint.ChildTable (actually they should subsume the primary key) private void GuaranteeForeignKeyConstraintInCSpace( QueryRewriter childRewriter, QueryRewriter parentRewriter, ErrorLog errorLog) { var childContext = childRewriter.ViewgenContext; var parentContext = parentRewriter.ViewgenContext; var cNode = childRewriter.BasicView; var pNode = parentRewriter.BasicView; var qp = FragmentQueryProcessor.Merge(childContext.RightFragmentQP, parentContext.RightFragmentQP); var cImpliesP = qp.IsContainedIn(cNode.RightFragmentQuery, pNode.RightFragmentQuery); if (false == cImpliesP) { // Foreign key constraint not being ensured in C-space var childExtents = LeftCellWrapper.GetExtentListAsUserString(cNode.GetLeaves()); var parentExtents = LeftCellWrapper.GetExtentListAsUserString(pNode.GetLeaves()); var message = Strings.ViewGen_Foreign_Key_Not_Guaranteed_InCSpace( ToUserString()); // Add all wrappers into allWrappers var allWrappers = new Set<LeftCellWrapper>(pNode.GetLeaves()); allWrappers.AddRange(cNode.GetLeaves()); var record = new ErrorLog.Record(ViewGenErrorCode.ForeignKeyNotGuaranteedInCSpace, message, allWrappers, String.Empty); errorLog.AddEntry(record); } }
// effects: Checks that this foreign key constraints for all the // tables are being ensured on the C-side as well. If not, adds // errors to the errorLog internal void CheckConstraint( Set<Cell> cells, QueryRewriter childRewriter, QueryRewriter parentRewriter, ErrorLog errorLog, ConfigViewGenerator config) { if (IsConstraintRelevantForCells(cells) == false) { // if the constraint does not deal with any cell in this group, ignore it return; } if (config.IsNormalTracing) { Trace.WriteLine(String.Empty); Trace.WriteLine(String.Empty); Trace.Write("Checking: "); Trace.WriteLine(this); } if (childRewriter == null && parentRewriter == null) { // Neither table is mapped - so we are fine return; } // If the child table has not been mapped, we used to say that we // are fine. However, if we have SPerson(pid) and SAddress(aid, // pid), where pid is an FK into SPerson, we are in trouble if // SAddress is not mapped - SPerson could get deleted. So we // check for it as well // if the parent table is not mapped, we also have a problem if (childRewriter == null) { var message = Strings.ViewGen_Foreign_Key_Missing_Table_Mapping( ToUserString(), ChildTable.Name); // Get the cells from the parent table var record = new ErrorLog.Record( ViewGenErrorCode.ForeignKeyMissingTableMapping, message, parentRewriter.UsedCells, String.Empty); errorLog.AddEntry(record); return; } if (parentRewriter == null) { var message = Strings.ViewGen_Foreign_Key_Missing_Table_Mapping( ToUserString(), ParentTable.Name); // Get the cells from the child table var record = new ErrorLog.Record( ViewGenErrorCode.ForeignKeyMissingTableMapping, message, childRewriter.UsedCells, String.Empty); errorLog.AddEntry(record); return; } // Note: we do not check if the parent columns correspond to the // table's keys - metadata checks for that //First check if the FK is covered by Foreign Key Association //If we find this, we don't need to check for independent associations. If user maps the Fk to both FK and independent associations, //the regular round tripping validation will catch the error. if (CheckIfConstraintMappedToForeignKeyAssociation(childRewriter, parentRewriter, cells)) { return; } // Check if the foreign key in the child table corresponds to the primary key, i.e., if // the foreign key (e.g., pid, pid2) is a superset of the actual key members (e.g., pid), it means // that the foreign key is also the primary key for this table -- so we can propagate the queries upto C-Space // rather than doing the cell check var initialErrorLogSize = errorLog.Count; if (IsForeignKeySuperSetOfPrimaryKeyInChildTable()) { GuaranteeForeignKeyConstraintInCSpace(childRewriter, parentRewriter, errorLog); } else { GuaranteeMappedRelationshipForForeignKey(childRewriter, parentRewriter, cells, errorLog, config); } if (initialErrorLogSize == errorLog.Count) { // Check if the order of columns in foreign key correponds to the // mappings in m_cellGroup, e.g., if <pid1, pid2> in SAddress is // a foreign key into <pid1, pid2> of the SPerson table, make // sure that this order is preserved through the mappings in m_cellGroup CheckForeignKeyColumnOrder(cells, errorLog); } }
// make sure that we can find a rewriting for each possible entity shape appearing in an extent // Possible optimization for OfType view generation: // Cache "used views" for each (currentPath, domainValue) combination private void EnsureConfigurationIsFullyMapped( MemberPath currentPath, BoolExpression currentWhereClause, HashSet<FragmentQuery> outputUsedViews, ErrorLog errorLog) { foreach (var domainValue in GetDomain(currentPath)) { if (domainValue == Constant.Undefined) { continue; // no point in trying to recover a situation that can never happen } TraceVerbose("REWRITING FOR {0}={1}", currentPath, domainValue); // construct WHERE clause for this value var domainAddedWhereClause = CreateMemberCondition(currentPath, domainValue); // AND the current where clause to it var domainWhereClause = BoolExpression.CreateAnd(currentWhereClause, domainAddedWhereClause); // first check whether we can recover instances of this type - don't care about the attributes - to produce a helpful error message Tile<FragmentQuery> rewriting; if (false == FindRewritingAndUsedViews(_keyAttributes, domainWhereClause, outputUsedViews, out rewriting)) { if (!ErrorPatternMatcher.FindMappingErrors(_context, _domainMap, _errorLog)) { var builder = new StringBuilder(); var extentName = StringUtil.FormatInvariant("{0}", _extentPath); var whereClause = rewriting.Query.Condition; whereClause.ExpensiveSimplify(); if (whereClause.RepresentsAllTypeConditions) { var tableString = Strings.ViewGen_Extent; builder.AppendLine(Strings.ViewGen_Cannot_Recover_Types(tableString, extentName)); } else { var entitiesString = Strings.ViewGen_Entities; builder.AppendLine(Strings.ViewGen_Cannot_Disambiguate_MultiConstant(entitiesString, extentName)); } RewritingValidator.EntityConfigurationToUserString(whereClause, builder); var record = new ErrorLog.Record( ViewGenErrorCode.AmbiguousMultiConstants, builder.ToString(), _context.AllWrappersForExtent, String.Empty); errorLog.AddEntry(record); } } else { var typeConstant = domainValue as TypeConstant; if (typeConstant != null) { // we are enumerating types var edmType = typeConstant.EdmType; // If can recover the type, make sure can get all the necessary attributes (key is included for EntityTypes) var nonConditionalAttributes = GetNonConditionalScalarMembers(edmType, currentPath, _domainMap).Union( GetNonConditionalComplexMembers(edmType, currentPath, _domainMap)).ToList(); IEnumerable<MemberPath> notCoverdAttributes; if (nonConditionalAttributes.Count > 0 && !FindRewritingAndUsedViews( nonConditionalAttributes, domainWhereClause, outputUsedViews, out rewriting, out notCoverdAttributes)) { //Error: No mapping specified for some attributes // remove keys nonConditionalAttributes = new List<MemberPath>(nonConditionalAttributes.Where(a => !a.IsPartOfKey)); Debug.Assert(nonConditionalAttributes.Count > 0, "Must have caught key-only case earlier"); AddUnrecoverableAttributesError(notCoverdAttributes, domainAddedWhereClause, errorLog); } else { // recurse into complex members foreach (var complexMember in GetConditionalComplexMembers(edmType, currentPath, _domainMap)) { EnsureConfigurationIsFullyMapped(complexMember, domainWhereClause, outputUsedViews, errorLog); } // recurse into scalar members foreach (var scalarMember in GetConditionalScalarMembers(edmType, currentPath, _domainMap)) { EnsureConfigurationIsFullyMapped(scalarMember, domainWhereClause, outputUsedViews, errorLog); } } } } } }
// effects: Returns an error record if the keys of the extent/associationSet being mapped are // present in the projected slots of this query. Returns null // otherwise. ownerCell indicates the cell that owns this and // resourceString is a resource used for error messages internal ErrorLog.Record VerifyKeysPresent( Cell ownerCell, Func <object, object, string> formatEntitySetMessage, Func <object, object, object, string> formatAssociationSetMessage, ViewGenErrorCode errorCode) { var prefixes = new List <MemberPath>(1); // Keep track of the key corresponding to each prefix var keys = new List <ExtentKey>(1); if (Extent is EntitySet) { // For entity set just get the full path of the key properties var prefix = new MemberPath(Extent); prefixes.Add(prefix); var entityType = (EntityType)Extent.ElementType; var entitySetKeys = ExtentKey.GetKeysForEntityType(prefix, entityType); Debug.Assert(entitySetKeys.Count == 1, "Currently, we only support primary keys"); keys.Add(entitySetKeys[0]); } else { var relationshipSet = (AssociationSet)Extent; // For association set, get the full path of the key // properties of each end foreach (var relationEnd in relationshipSet.AssociationSetEnds) { var assocEndMember = relationEnd.CorrespondingAssociationEndMember; var prefix = new MemberPath(relationshipSet, assocEndMember); prefixes.Add(prefix); var endKeys = ExtentKey.GetKeysForEntityType( prefix, MetadataHelper.GetEntityTypeForEnd(assocEndMember)); Debug.Assert(endKeys.Count == 1, "Currently, we only support primary keys"); keys.Add(endKeys[0]); } } for (var i = 0; i < prefixes.Count; i++) { var prefix = prefixes[i]; // Get all or none key slots that are being projected in this cell query var keySlots = MemberProjectedSlot.GetKeySlots(GetMemberProjectedSlots(), prefix); if (keySlots == null) { var key = keys[i]; string message; if (Extent is EntitySet) { var keyPropertiesString = MemberPath.PropertiesToUserString(key.KeyFields, true); message = formatEntitySetMessage(keyPropertiesString, Extent.Name); } else { var endName = prefix.RootEdmMember.Name; var keyPropertiesString = MemberPath.PropertiesToUserString(key.KeyFields, false); message = formatAssociationSetMessage(keyPropertiesString, endName, Extent.Name); } var error = new ErrorLog.Record(errorCode, message, ownerCell, String.Empty); return(error); } } return(null); }
// requires: All slots in this are join tree slots // This method is called for an S-side query // cQuery is the corresponding C-side query in the cell // sourceCell is the original cell for "this" and cQuery // effects: Checks if any of the columns in "this" are mapped to multiple properties in cQuery. If so, // returns an error record about the duplicated slots internal ErrorLog.Record CheckForDuplicateFields(CellQuery cQuery, Cell sourceCell) { // slotMap stores the slots on the S-side and the // C-side properties that it maps to var slotMap = new KeyToListMap <MemberProjectedSlot, int>(ProjectedSlot.EqualityComparer); // Note that this does work for self-association. In the manager // employee example, ManagerId and EmployeeId from the SEmployee // table map to the two ends -- Manager.ManagerId and // Employee.EmployeeId in the C Space for (var i = 0; i < m_projectedSlots.Length; i++) { var projectedSlot = m_projectedSlots[i]; var slot = projectedSlot as MemberProjectedSlot; Debug.Assert(slot != null, "All slots for this method must be JoinTreeSlots"); slotMap.Add(slot, i); } StringBuilder builder = null; // Now determine the entries that have more than one integer per slot var isErrorSituation = false; foreach (var slot in slotMap.Keys) { var indexes = slotMap.ListForKey(slot); Debug.Assert(indexes.Count >= 1, "Each slot must have one index at least"); if (indexes.Count > 1 && cQuery.AreSlotsEquivalentViaRefConstraints(indexes) == false) { // The column is mapped to more than one property and it // failed the "association corresponds to referential // constraints" check isErrorSituation = true; if (builder == null) { builder = new StringBuilder(Strings.ViewGen_Duplicate_CProperties(Extent.Name)); builder.AppendLine(); } var tmpBuilder = new StringBuilder(); for (var i = 0; i < indexes.Count; i++) { var index = indexes[i]; if (i != 0) { tmpBuilder.Append(", "); } // The slot must be a JoinTreeSlot. If it isn't it is an internal error var cSlot = (MemberProjectedSlot)cQuery.m_projectedSlots[index]; tmpBuilder.Append(cSlot.ToUserString()); } builder.AppendLine(Strings.ViewGen_Duplicate_CProperties_IsMapped(slot.ToUserString(), tmpBuilder.ToString())); } } if (false == isErrorSituation) { return(null); } var record = new ErrorLog.Record(ViewGenErrorCode.DuplicateCPropertiesMapped, builder.ToString(), sourceCell, String.Empty); return(record); }
// requires: all columns in constraint.ParentColumns and // constraint.ChildColumns must have been mapped in some cell in m_cellGroup // effects: Given the foreign key constraint, checks if the // constraint.ChildColumns are mapped to the constraint.ParentColumns // in m_cellGroup in the right oder. If not, adds an error to m_errorLog and returns // false. Else returns true private bool CheckForeignKeyColumnOrder(Set<Cell> cells, ErrorLog errorLog) { // Go through every cell and find the cells that are relevant to // parent and those that are relevant to child // Then for each cell pair (parent, child) make sure that the // projected foreign keys columns in C-space are aligned var parentCells = new List<Cell>(); var childCells = new List<Cell>(); foreach (var cell in cells) { if (cell.SQuery.Extent.Equals(ChildTable)) { childCells.Add(cell); } if (cell.SQuery.Extent.Equals(ParentTable)) { parentCells.Add(cell); } } // Make sure that all child cells and parent cells align on // the columns, i.e., for each DISTINCT pair C and P, get the columns // on the S-side. Then get the corresponding fields on the // C-side. The fields on the C-side should match var foundParentCell = false; var foundChildCell = false; foreach (var childCell in childCells) { var allChildSlotNums = GetSlotNumsForColumns(childCell, ChildColumns); if (allChildSlotNums.Count == 0) { // slots in present in S-side, ignore continue; } List<MemberPath> childPaths = null; List<MemberPath> parentPaths = null; Cell errorParentCell = null; foreach (var childSlotNums in allChildSlotNums) { foundChildCell = true; // Get the fields on the C-side childPaths = new List<MemberPath>(childSlotNums.Count); foreach (var childSlotNum in childSlotNums) { // Initial slots only have JoinTreeSlots var childSlot = (MemberProjectedSlot)childCell.CQuery.ProjectedSlotAt(childSlotNum); Debug.Assert(childSlot != null); childPaths.Add(childSlot.MemberPath); } foreach (var parentCell in parentCells) { var allParentSlotNums = GetSlotNumsForColumns(parentCell, ParentColumns); if (allParentSlotNums.Count == 0) { // * Parent and child cell are the same - we do not // need to check since we want to check the foreign // key constraint mapping across cells // * Some slots not in present in S-side, ignore continue; } foreach (var parentSlotNums in allParentSlotNums) { foundParentCell = true; parentPaths = new List<MemberPath>(parentSlotNums.Count); foreach (var parentSlotNum in parentSlotNums) { var parentSlot = (MemberProjectedSlot)parentCell.CQuery.ProjectedSlotAt(parentSlotNum); Debug.Assert(parentSlot != null); parentPaths.Add(parentSlot.MemberPath); } // Make sure that the last member of each of these is the same // or the paths are essentially equivalent via referential constraints // We need to check that the last member is essentially the same because it could // be a regular scenario where aid is mapped to PersonAddress and Address - there // is no ref constraint. So when projected into C-Space, we will get Address.aid // and PersonAddress.Address.aid if (childPaths.Count == parentPaths.Count) { var notAllPathsMatched = false; for (var i = 0; i < childPaths.Count && !notAllPathsMatched; i++) { var parentPath = parentPaths[i]; var childPath = childPaths[i]; if (!parentPath.LeafEdmMember.Equals(childPath.LeafEdmMember)) //Child path did not match { if (parentPath.IsEquivalentViaRefConstraint(childPath)) { //Specifying the referential constraint once in the C space should be enough. //This is the only way possible today. //We might be able to derive more knowledge by using boolean logic return true; } else { notAllPathsMatched = true; } } } if (!notAllPathsMatched) { return true; //all childPaths matched parentPaths } else { //If not this one, some other Parent Cell may match. errorParentCell = parentCell; } } } } //foreach parentCell } //If execution is at this point, no parent cell's end has matched (otherwise it would have returned true) Debug.Assert(childPaths != null, "child paths should be set"); Debug.Assert(parentPaths != null, "parent paths should be set"); Debug.Assert(errorParentCell != null, "errorParentCell should be set"); var message = Strings.ViewGen_Foreign_Key_ColumnOrder_Incorrect( ToUserString(), MemberPath.PropertiesToUserString(ChildColumns, false), ChildTable.Name, MemberPath.PropertiesToUserString(childPaths, false), childCell.CQuery.Extent.Name, MemberPath.PropertiesToUserString(ParentColumns, false), ParentTable.Name, MemberPath.PropertiesToUserString(parentPaths, false), errorParentCell.CQuery.Extent.Name); var record = new ErrorLog.Record( ViewGenErrorCode.ForeignKeyColumnOrderIncorrect, message, new[] { errorParentCell, childCell }, String.Empty); errorLog.AddEntry(record); return false; } Debug.Assert(foundParentCell, "Some cell that mapped the parent's key must be present!"); Debug.Assert( foundChildCell == true, "Some cell that mapped the child's foreign key must be present according to the requires clause!"); return true; }
// requires: All slots in this are join tree slots // This method is called for an S-side query // cQuery is the corresponding C-side query in the cell // sourceCell is the original cell for "this" and cQuery // effects: Checks if any of the columns in "this" are mapped to multiple properties in cQuery. If so, // returns an error record about the duplicated slots internal ErrorLog.Record CheckForDuplicateFields(CellQuery cQuery, Cell sourceCell) { // slotMap stores the slots on the S-side and the // C-side properties that it maps to var slotMap = new KeyToListMap<MemberProjectedSlot, int>(ProjectedSlot.EqualityComparer); // Note that this does work for self-association. In the manager // employee example, ManagerId and EmployeeId from the SEmployee // table map to the two ends -- Manager.ManagerId and // Employee.EmployeeId in the C Space for (var i = 0; i < m_projectedSlots.Length; i++) { var projectedSlot = m_projectedSlots[i]; var slot = projectedSlot as MemberProjectedSlot; Debug.Assert(slot != null, "All slots for this method must be JoinTreeSlots"); slotMap.Add(slot, i); } StringBuilder builder = null; // Now determine the entries that have more than one integer per slot var isErrorSituation = false; foreach (var slot in slotMap.Keys) { var indexes = slotMap.ListForKey(slot); Debug.Assert(indexes.Count >= 1, "Each slot must have one index at least"); if (indexes.Count > 1 && cQuery.AreSlotsEquivalentViaRefConstraints(indexes) == false) { // The column is mapped to more than one property and it // failed the "association corresponds to referential // constraints" check isErrorSituation = true; if (builder == null) { builder = new StringBuilder(Strings.ViewGen_Duplicate_CProperties(Extent.Name)); builder.AppendLine(); } var tmpBuilder = new StringBuilder(); for (var i = 0; i < indexes.Count; i++) { var index = indexes[i]; if (i != 0) { tmpBuilder.Append(", "); } // The slot must be a JoinTreeSlot. If it isn't it is an internal error var cSlot = (MemberProjectedSlot)cQuery.m_projectedSlots[index]; tmpBuilder.Append(cSlot.ToUserString()); } builder.AppendLine(Strings.ViewGen_Duplicate_CProperties_IsMapped(slot.ToUserString(), tmpBuilder.ToString())); } } if (false == isErrorSituation) { return null; } var record = new ErrorLog.Record(ViewGenErrorCode.DuplicateCPropertiesMapped, builder.ToString(), sourceCell, String.Empty); return record; }
// <summary> // Checks: // 1) Concurrency token is not defined in this Extent's ElementTypes' derived types // 2) Members with concurrency token should not have conditions specified // </summary> private void CheckConcurrencyControlTokens() { Debug.Assert(m_viewTarget == ViewTarget.QueryView); // Get the token fields for this extent var extentType = m_extent.ElementType; var tokenMembers = MetadataHelper.GetConcurrencyMembersForTypeHierarchy(extentType, m_edmItemCollection); var tokenPaths = new Set<MemberPath>(MemberPath.EqualityComparer); foreach (var tokenMember in tokenMembers) { if (!tokenMember.DeclaringType.IsAssignableFrom(extentType)) { var message = Strings.ViewGen_Concurrency_Derived_Class(tokenMember.Name, tokenMember.DeclaringType.Name, m_extent); var record = new ErrorLog.Record(ViewGenErrorCode.ConcurrencyDerivedClass, message, m_cellWrappers, String.Empty); ExceptionHelpers.ThrowMappingException(record, m_config); } tokenPaths.Add(new MemberPath(m_extent, tokenMember)); } if (tokenMembers.Count > 0) { foreach (var wrapper in m_cellWrappers) { var conditionMembers = new Set<MemberPath>( wrapper.OnlyInputCell.CQuery.WhereClause.MemberRestrictions.Select(oneOf => oneOf.RestrictedMemberSlot.MemberPath), MemberPath.EqualityComparer); conditionMembers.Intersect(tokenPaths); if (conditionMembers.Count > 0) { // There is a condition on concurrency tokens. Throw an exception. var builder = new StringBuilder(); builder.AppendLine( Strings.ViewGen_Concurrency_Invalid_Condition( MemberPath.PropertiesToUserString(conditionMembers, false), m_extent.Name)); var record = new ErrorLog.Record( ViewGenErrorCode.ConcurrencyTokenHasCondition, builder.ToString(), new[] { wrapper }, String.Empty); ExceptionHelpers.ThrowMappingException(record, m_config); } } } }
/// <summary> /// Checks that if a DISTINCT operator exists between some C-Extent and S-Extent, there are no additional /// mapping fragments between that C-Extent and S-Extent. /// We need to enforce this because DISTINCT is not understood by viewgen machinery, and two fragments may be merged /// despite one of them having DISTINCT. /// </summary> private bool CheckCellsWithDistinctFlag() { var errorLogSize = m_errorLog.Count; foreach (var cell in m_cells) { if (cell.SQuery.SelectDistinctFlag == CellQuery.SelectDistinct.Yes) { var cExtent = cell.CQuery.Extent; var sExtent = cell.SQuery.Extent; //There should be no other fragments mapping cExtent to sExtent var mapepdFragments = m_cells.Where(otherCell => otherCell != cell) .Where( otherCell => otherCell.CQuery.Extent == cExtent && otherCell.SQuery.Extent == sExtent); if (mapepdFragments.Any()) { var cellsToReport = Enumerable.Repeat(cell, 1).Union(mapepdFragments); var record = new ErrorLog.Record( ViewGenErrorCode.MultipleFragmentsBetweenCandSExtentWithDistinct, Strings.Viewgen_MultipleFragmentsBetweenCandSExtentWithDistinct(cExtent.Name, sExtent.Name), cellsToReport, String.Empty); m_errorLog.AddEntry(record); } } } return m_errorLog.Count == errorLogSize; }
// This makes sure that the mapping describes how to store all C-side data, // i.e., the view given by C-side cell queries is injective internal void EnsureExtentIsFullyMapped(HashSet<FragmentQuery> outputUsedViews) { if (_context.ViewTarget == ViewTarget.QueryView && _config.IsValidationEnabled) { // Run the check below for OfType views too so we can determine // what views are used (low overhead due to caching of rewritings) EnsureConfigurationIsFullyMapped(_extentPath, BoolExpression.True, outputUsedViews, _errorLog); if (_errorLog.Count > 0) { ExceptionHelpers.ThrowMappingException(_errorLog, _config); } } else { if (_config.IsValidationEnabled) { // Ensure that non-nullable, no-default attributes are always populated properly foreach (var memberPath in _context.MemberMaps.ProjectedSlotMap.Members) { Constant defaultConstant; if (memberPath.IsScalarType() && !memberPath.IsPartOfKey && !_domainMap.IsConditionMember(memberPath) && !Domain.TryGetDefaultValueForMemberPath(memberPath, out defaultConstant)) { var attributes = new HashSet<MemberPath>(_keyAttributes); attributes.Add(memberPath); foreach (var leftCellWrapper in _context.AllWrappersForExtent) { var fragmentQuery = leftCellWrapper.FragmentQuery; var tileQuery = new FragmentQuery( fragmentQuery.Description, fragmentQuery.FromVariable, attributes, fragmentQuery.Condition); Tile<FragmentQuery> noNullToAvoid = CreateTile(FragmentQuery.Create(_keyAttributes, BoolExpression.CreateNot(fragmentQuery.Condition))); Tile<FragmentQuery> noNullRewriting; IEnumerable<MemberPath> notCoveredAttributes; if ( !RewriteQuery( CreateTile(tileQuery), noNullToAvoid, /*_views,*/ out noNullRewriting, out notCoveredAttributes, false /* isRelaxed */)) { // force error Domain.GetDefaultValueForMemberPath(memberPath, new[] { leftCellWrapper }, _config); } } } } } // find a rewriting for each tile // some of the views may be redundant and unused foreach (var toFill in _views) { Tile<FragmentQuery> rewriting; Tile<FragmentQuery> toAvoid = CreateTile(FragmentQuery.Create(_keyAttributes, BoolExpression.CreateNot(toFill.Query.Condition))); IEnumerable<MemberPath> notCoveredAttributes; var found = RewriteQuery(toFill, toAvoid, out rewriting, out notCoveredAttributes, true /* isRelaxed */); //Must be able to find the rewriting since the query is one of the views // otherwise it means condition on the fragment is not satisfiable if (!found) { var fragment = _context.AllWrappersForExtent.First(lcr => lcr.FragmentQuery.Equals(toFill.Query)); Debug.Assert(fragment != null); var record = new ErrorLog.Record( ViewGenErrorCode.ImpopssibleCondition, Strings.Viewgen_QV_RewritingNotFound(fragment.RightExtent.ToString()), fragment.Cells, String.Empty); _errorLog.AddEntry(record); } else { outputUsedViews.UnionWith(rewriting.GetNamedQueries()); } } } }
private void GenerateCaseStatements( IEnumerable<MemberPath> members, HashSet<FragmentQuery> outputUsedViews) { // Compute right domain query - non-simplified version of "basic view" // It is used below to check whether we need a default value in a case statement var usedCells = _context.AllWrappersForExtent.Where(w => _usedViews.Contains(w.FragmentQuery)); CellTreeNode rightDomainQuery = new OpCellTreeNode( _context, CellTreeOpType.Union, usedCells.Select(wrapper => new LeafCellTreeNode(_context, wrapper)).ToArray()); foreach (var currentPath in members) { // Add the types can member have, i.e., its type and its subtypes var domain = GetDomain(currentPath).ToList(); var caseStatement = new CaseStatement(currentPath); Tile<FragmentQuery> unionCaseRewriting = null; // optimization for domain = {NULL, NOT_NULL} // Create a single case: WHEN True THEN currentPath // Reason: if the WHEN condition is not satisfied (say because of LOJ), then currentPath = NULL var needCaseStatement = !(domain.Count == 2 && domain.Contains(Constant.Null, Constant.EqualityComparer) && domain.Contains(Constant.NotNull, Constant.EqualityComparer)); { // go over the domain foreach (var domainValue in domain) { if (domainValue == Constant.Undefined && _context.ViewTarget == ViewTarget.QueryView) { // we cannot assume closed domain for query views; // if obtaining undefined is possible, we need to account for that caseStatement.AddWhenThen( BoolExpression.False /* arbitrary condition */, new ConstantProjectedSlot(Constant.Undefined)); continue; } TraceVerbose("CASE STATEMENT FOR {0}={1}", currentPath, domainValue); // construct WHERE clause for this value var memberConditionQuery = CreateMemberConditionQuery(currentPath, domainValue); Tile<FragmentQuery> caseRewriting; if (FindRewritingAndUsedViews( memberConditionQuery.Attributes, memberConditionQuery.Condition, outputUsedViews, out caseRewriting)) { if (_context.ViewTarget == ViewTarget.UpdateView) { unionCaseRewriting = (unionCaseRewriting != null) ? _qp.Union(unionCaseRewriting, caseRewriting) : caseRewriting; } if (needCaseStatement) { var isAlwaysTrue = AddRewritingToCaseStatement(caseRewriting, caseStatement, currentPath, domainValue); if (isAlwaysTrue) { break; } } } else { if (!IsDefaultValue(domainValue, currentPath)) { Debug.Assert(_context.ViewTarget == ViewTarget.UpdateView || !_config.IsValidationEnabled); if (!ErrorPatternMatcher.FindMappingErrors(_context, _domainMap, _errorLog)) { var builder = new StringBuilder(); var extentName = StringUtil.FormatInvariant("{0}", _extentPath); var objectString = _context.ViewTarget == ViewTarget.QueryView ? Strings.ViewGen_Entities : Strings.ViewGen_Tuples; if (_context.ViewTarget == ViewTarget.QueryView) { builder.AppendLine(Strings.Viewgen_CannotGenerateQueryViewUnderNoValidation(extentName)); } else { builder.AppendLine(Strings.ViewGen_Cannot_Disambiguate_MultiConstant(objectString, extentName)); } RewritingValidator.EntityConfigurationToUserString( memberConditionQuery.Condition, builder, _context.ViewTarget == ViewTarget.UpdateView); var record = new ErrorLog.Record( ViewGenErrorCode.AmbiguousMultiConstants, builder.ToString(), _context.AllWrappersForExtent, String.Empty); _errorLog.AddEntry(record); } } } } } if (_errorLog.Count == 0) { // for update views, add WHEN True THEN defaultValue // which will ultimately be translated into a (possibly implicit) ELSE clause if (_context.ViewTarget == ViewTarget.UpdateView && needCaseStatement) { AddElseDefaultToCaseStatement(currentPath, caseStatement, domain, rightDomainQuery, unionCaseRewriting); } if (caseStatement.Clauses.Count > 0) { TraceVerbose("{0}", caseStatement.ToString()); _caseStatements[currentPath] = caseStatement; } } } }
private void AddUnrecoverableAttributesError( IEnumerable<MemberPath> attributes, BoolExpression domainAddedWhereClause, ErrorLog errorLog) { var builder = new StringBuilder(); var extentName = StringUtil.FormatInvariant("{0}", _extentPath); var tableString = Strings.ViewGen_Extent; var attributesString = StringUtil.ToCommaSeparatedString(GetTypeBasedMemberPathList(attributes)); builder.AppendLine(Strings.ViewGen_Cannot_Recover_Attributes(attributesString, tableString, extentName)); RewritingValidator.EntityConfigurationToUserString(domainAddedWhereClause, builder); var record = new ErrorLog.Record( ViewGenErrorCode.AttributesUnrecoverable, builder.ToString(), _context.AllWrappersForExtent, String.Empty); errorLog.AddEntry(record); }
// requires: IsForeignKeySuperSetOfPrimaryKeyInChildTable() is false // effects: Given that both the ChildColumns in this and the // primaryKey of ChildTable are mapped. Return true iff no error occurred private bool CheckConstraintWhenParentChildMapped( Cell cell, ErrorLog errorLog, AssociationEndMember parentEnd, ConfigViewGenerator config) { var ok = true; // The foreign key constraint has been mapped to a // relationship. Check if the multiplicities are consistent // If all columns in the child table (corresponding to // the constraint) are nullable, the parent end can be // 0..1 or 1..1. Else if must be 1..1 if (parentEnd.RelationshipMultiplicity == RelationshipMultiplicity.Many) { // Parent should at most one since we are talking // about foreign keys here var message = Strings.ViewGen_Foreign_Key_UpperBound_MustBeOne( ToUserString(), cell.CQuery.Extent.Name, parentEnd.Name); var record = new ErrorLog.Record(ViewGenErrorCode.ForeignKeyUpperBoundMustBeOne, message, cell, String.Empty); errorLog.AddEntry(record); ok = false; } if (MemberPath.AreAllMembersNullable(ChildColumns) == false && parentEnd.RelationshipMultiplicity != RelationshipMultiplicity.One) { // Some column in the constraint in the child table // is non-nullable and lower bound is not 1 var message = Strings.ViewGen_Foreign_Key_LowerBound_MustBeOne( ToUserString(), cell.CQuery.Extent.Name, parentEnd.Name); var record = new ErrorLog.Record(ViewGenErrorCode.ForeignKeyLowerBoundMustBeOne, message, cell, String.Empty); errorLog.AddEntry(record); ok = false; } if (config.IsNormalTracing && ok) { Trace.WriteLine("Foreign key mapped to relationship " + cell.CQuery.Extent.Name); } return ok; }
// requires: The Where clause satisfies the same requirements a GetConjunctsFromWhereClause // effects: For each slot that has a NotNull condition in the where // clause, checks if it is projected. If all such slots are // projected, returns null. Else returns an error record internal ErrorLog.Record CheckForProjectedNotNullSlots(Cell sourceCell, IEnumerable<Cell> associationSets) { var builder = new StringBuilder(); var foundError = false; foreach (var restriction in Conditions) { if (restriction.Domain.ContainsNotNull()) { var slot = MemberProjectedSlot.GetSlotForMember(m_projectedSlots, restriction.RestrictedMemberSlot.MemberPath); if (slot == null) //member with not null condition is not mapped in this extent { var missingMapping = true; if (Extent is EntitySet) { var isCQuery = sourceCell.CQuery == this; var target = isCQuery ? ViewTarget.QueryView : ViewTarget.UpdateView; var rightCellQuery = isCQuery ? sourceCell.SQuery : sourceCell.CQuery; //Find out if there is an association mapping but only if the current Not Null condition is on an EntitySet var rightExtent = rightCellQuery.Extent as EntitySet; if (rightExtent != null) { var associations = MetadataHelper.GetAssociationsForEntitySet(rightCellQuery.Extent as EntitySet); foreach ( var association in associations.Where( association => association.AssociationSetEnds.Any( end => (end.CorrespondingAssociationEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One && (MetadataHelper.GetOppositeEnd(end).EntitySet.EdmEquals(rightExtent)))))) { foreach ( var associationCell in associationSets.Where(c => c.GetRightQuery(target).Extent.EdmEquals(association))) { if (MemberProjectedSlot.GetSlotForMember( associationCell.GetLeftQuery(target).ProjectedSlots, restriction.RestrictedMemberSlot.MemberPath) != null) { missingMapping = false; } } } } } if (missingMapping) { // condition of NotNull and slot not being projected builder.AppendLine( Strings.ViewGen_NotNull_No_Projected_Slot( restriction.RestrictedMemberSlot.MemberPath.PathToString(false))); foundError = true; } } } } if (false == foundError) { return null; } var record = new ErrorLog.Record(ViewGenErrorCode.NotNullNoProjectedSlot, builder.ToString(), sourceCell, String.Empty); return record; }
// effects: Given the foreign key constraint, checks if the // constraint.ParentColumns are mapped to the entity set E'e keys in // C-space where E corresponds to the entity set corresponding to end // Returns true iff such a mapping exists in cell private bool CheckParentColumnsForForeignKey( Cell cell, IEnumerable<Cell> cells, AssociationEndMember parentEnd, ref List<ErrorLog.Record> errorList) { // The child columns are mapped to some end of cell.CQuery.Extent. ParentColumns // must correspond to the EntitySet for this end var relationSet = (AssociationSet)cell.CQuery.Extent; var endSet = MetadataHelper.GetEntitySetAtEnd(relationSet, parentEnd); // Check if the ParentColumns are mapped to endSet's keys // Find the entity set that they map to - if any var entitySet = FindEntitySetForColumnsMappedToEntityKeys(cells, ParentColumns); if (entitySet == null || endSet.Equals(entitySet) == false) { if (errorList == null) //lazily initialize only if there is an error { errorList = new List<ErrorLog.Record>(); } // childColumns are mapped to parentEnd but ParentColumns are not mapped to the end // corresponding to the parentEnd -- this is an error var message = Strings.ViewGen_Foreign_Key_ParentTable_NotMappedToEnd( ToUserString(), ChildTable.Name, cell.CQuery.Extent.Name, parentEnd.Name, ParentTable.Name, endSet.Name); var record = new ErrorLog.Record(ViewGenErrorCode.ForeignKeyParentTableNotMappedToEnd, message, cell, String.Empty); errorList.Add(record); return false; } return true; }
// effects: Returns an error record if the keys of the extent/associationSet being mapped are // present in the projected slots of this query. Returns null // otherwise. ownerCell indicates the cell that owns this and // resourceString is a resource used for error messages internal ErrorLog.Record VerifyKeysPresent( Cell ownerCell, Func<object, object, string> formatEntitySetMessage, Func<object, object, object, string> formatAssociationSetMessage, ViewGenErrorCode errorCode) { var prefixes = new List<MemberPath>(1); // Keep track of the key corresponding to each prefix var keys = new List<ExtentKey>(1); if (Extent is EntitySet) { // For entity set just get the full path of the key properties var prefix = new MemberPath(Extent); prefixes.Add(prefix); var entityType = (EntityType)Extent.ElementType; var entitySetKeys = ExtentKey.GetKeysForEntityType(prefix, entityType); Debug.Assert(entitySetKeys.Count == 1, "Currently, we only support primary keys"); keys.Add(entitySetKeys[0]); } else { var relationshipSet = (AssociationSet)Extent; // For association set, get the full path of the key // properties of each end foreach (var relationEnd in relationshipSet.AssociationSetEnds) { var assocEndMember = relationEnd.CorrespondingAssociationEndMember; var prefix = new MemberPath(relationshipSet, assocEndMember); prefixes.Add(prefix); var endKeys = ExtentKey.GetKeysForEntityType( prefix, MetadataHelper.GetEntityTypeForEnd(assocEndMember)); Debug.Assert(endKeys.Count == 1, "Currently, we only support primary keys"); keys.Add(endKeys[0]); } } for (var i = 0; i < prefixes.Count; i++) { var prefix = prefixes[i]; // Get all or none key slots that are being projected in this cell query var keySlots = MemberProjectedSlot.GetKeySlots(GetMemberProjectedSlots(), prefix); if (keySlots == null) { var key = keys[i]; string message; if (Extent is EntitySet) { var keyPropertiesString = MemberPath.PropertiesToUserString(key.KeyFields, true); message = formatEntitySetMessage(keyPropertiesString, Extent.Name); } else { var endName = prefix.RootEdmMember.Name; var keyPropertiesString = MemberPath.PropertiesToUserString(key.KeyFields, false); message = formatAssociationSetMessage(keyPropertiesString, endName, Extent.Name); } var error = new ErrorLog.Record(errorCode, message, ownerCell, String.Empty); return error; } } return null; }
// 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 (var cell in extentCells) { var cellQuery = cell.GetLeftQuery(viewTarget); // Get the atoms from cellQuery and only keep the ones that // are condition members foreach (var condition in cellQuery.GetConjunctsFromWhereClause()) { // Note: TypeConditions are created using OneOfTypeConst and // scalars are created using OneOfScalarConst var 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))) { var message = Strings.ViewGen_InvalidCondition(memberPath.PathToString(false)); var record = new ErrorLog.Record(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 (var cell in extentCells) { var cellQuery = cell.GetLeftQuery(viewTarget); // Get the atoms from cellQuery and only keep the ones that // are condition members foreach (var slot in cellQuery.GetAllQuerySlots()) { var member = slot.MemberPath; if (m_conditionDomainMap.ContainsKey(member) == false && m_nonConditionDomainMap.ContainsKey(member) == false) { var 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)); } } } }
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 (var cell in extentCells) { var cellQuery = cell.GetLeftQuery(viewTarget); // Get the atoms from cellQuery and only keep the ones that // are condition members foreach (var condition in cellQuery.GetConjunctsFromWhereClause()) { // Note: TypeConditions are created using OneOfTypeConst and // scalars are created using OneOfScalarConst var 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))) { var message = Strings.ViewGen_InvalidCondition(memberPath.PathToString(false)); var record = new ErrorLog.Record(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 (var cell in extentCells) { var cellQuery = cell.GetLeftQuery(viewTarget); // Get the atoms from cellQuery and only keep the ones that // are condition members foreach (var slot in cellQuery.GetAllQuerySlots()) { var member = slot.MemberPath; if (m_conditionDomainMap.ContainsKey(member) == false && m_nonConditionDomainMap.ContainsKey(member) == false) { var 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)); } } } }
// requires: The Where clause satisfies the same requirements a GetConjunctsFromWhereClause // effects: For each slot that has a NotNull condition in the where // clause, checks if it is projected. If all such slots are // projected, returns null. Else returns an error record internal ErrorLog.Record CheckForProjectedNotNullSlots(Cell sourceCell, IEnumerable <Cell> associationSets) { var builder = new StringBuilder(); var foundError = false; foreach (var restriction in Conditions) { if (restriction.Domain.ContainsNotNull()) { var slot = MemberProjectedSlot.GetSlotForMember(m_projectedSlots, restriction.RestrictedMemberSlot.MemberPath); if (slot == null) //member with not null condition is not mapped in this extent { var missingMapping = true; if (Extent is EntitySet) { var isCQuery = sourceCell.CQuery == this; var target = isCQuery ? ViewTarget.QueryView : ViewTarget.UpdateView; var rightCellQuery = isCQuery ? sourceCell.SQuery : sourceCell.CQuery; //Find out if there is an association mapping but only if the current Not Null condition is on an EntitySet var rightExtent = rightCellQuery.Extent as EntitySet; if (rightExtent != null) { var associations = MetadataHelper.GetAssociationsForEntitySet(rightCellQuery.Extent as EntitySet); foreach ( var association in associations.Where( association => association.AssociationSetEnds.Any( end => (end.CorrespondingAssociationEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One && (MetadataHelper.GetOppositeEnd(end).EntitySet.EdmEquals(rightExtent)))))) { foreach ( var associationCell in associationSets.Where(c => c.GetRightQuery(target).Extent.EdmEquals(association))) { if (MemberProjectedSlot.GetSlotForMember( associationCell.GetLeftQuery(target).ProjectedSlots, restriction.RestrictedMemberSlot.MemberPath) != null) { missingMapping = false; } } } } } if (missingMapping) { // condition of NotNull and slot not being projected builder.AppendLine( Strings.ViewGen_NotNull_No_Projected_Slot( restriction.RestrictedMemberSlot.MemberPath.PathToString(false))); foundError = true; } } } } if (false == foundError) { return(null); } var record = new ErrorLog.Record(ViewGenErrorCode.NotNullNoProjectedSlot, builder.ToString(), sourceCell, String.Empty); return(record); }
// effects: Given a container, ensures that all entity/association // sets in container on the C-side have been mapped private static ErrorLog EnsureAllCSpaceContainerSetsAreMapped( IEnumerable<Cell> cells, EntityContainerMapping containerMapping) { var mappedExtents = new Set<EntitySetBase>(); string mslFileLocation = null; EntityContainer container = null; // Determine the container and name of the file while determining // the set of mapped extents in the cells foreach (var cell in cells) { mappedExtents.Add(cell.CQuery.Extent); mslFileLocation = cell.CellLabel.SourceLocation; // All cells are from the same container container = cell.CQuery.Extent.EntityContainer; } Debug.Assert(container != null); var missingExtents = new List<EntitySetBase>(); // Go through all the extents in the container and determine // extents that are missing foreach (var extent in container.BaseEntitySets) { if (mappedExtents.Contains(extent) == false && !(containerMapping.HasQueryViewForSetMap(extent.Name))) { var associationSet = extent as AssociationSet; if (associationSet == null || !associationSet.ElementType.IsForeignKey) { missingExtents.Add(extent); } } } var errorLog = new ErrorLog(); // If any extent is not mapped, add an error if (missingExtents.Count > 0) { var extentBuilder = new StringBuilder(); var isFirst = true; foreach (var extent in missingExtents) { if (isFirst == false) { extentBuilder.Append(", "); } isFirst = false; extentBuilder.Append(extent.Name); } var message = Strings.ViewGen_Missing_Set_Mapping(extentBuilder); // Find the cell with smallest line number - so that we can // point to the beginning of the file var lowestLineNum = -1; Cell smallestCell = null; foreach (var cell in cells) { if (lowestLineNum == -1 || cell.CellLabel.StartLineNumber < lowestLineNum) { smallestCell = cell; lowestLineNum = cell.CellLabel.StartLineNumber; } } Debug.Assert(smallestCell != null && lowestLineNum >= 0); var edmSchemaError = new EdmSchemaError( message, (int)ViewGenErrorCode.MissingExtentMapping, EdmSchemaErrorSeverity.Error, containerMapping.SourceLocation, containerMapping.StartLineNumber, containerMapping.StartLinePosition, null); var record = new ErrorLog.Record(edmSchemaError); errorLog.AddEntry(record); } return errorLog; }
internal void AddEntry(ErrorLog.Record record) { this.m_log.Add(record); }
internal static Constant GetDefaultValueForMemberPath( MemberPath memberPath, IEnumerable<LeftCellWrapper> wrappersForErrorReporting, ConfigViewGenerator config) { Constant defaultValue = null; if (!TryGetDefaultValueForMemberPath(memberPath, out defaultValue)) { var message = Strings.ViewGen_No_Default_Value(memberPath.Extent.Name, memberPath.PathToString(false)); var record = new ErrorLog.Record(ViewGenErrorCode.NoDefaultValue, message, wrappersForErrorReporting, String.Empty); ExceptionHelpers.ThrowMappingException(record, config); } return defaultValue; }