Exemple #1
0
 /// <summary>
 /// Copy Constructor
 /// </summary>
 internal Cell(Cell source)
 {
     m_cQuery = new CellQuery(source.m_cQuery);
     m_sQuery = new CellQuery(source.m_sQuery);
     m_label = new CellLabel(source.m_label);
     m_cellNumber = source.m_cellNumber;
 }
 // effects: Creates a view cell relation for "cell" with the
 // projected slots given by slots -- cellNumber is the number of the
 // cell for debugging purposes
 // Also creates the BasicCellRelations for the left and right cell queries
 internal ViewCellRelation(Cell cell, List<ViewCellSlot> slots, int cellNumber)
     : base(cellNumber)
 {
     m_cell = cell;
     m_slots = slots;
     // We create the basiccellrelations  passing this to it so that we have
     // a reference from the basiccellrelations to this
     m_cell.CQuery.CreateBasicCellRelation(this);
     m_cell.SQuery.CreateBasicCellRelation(this);
 }
 private static List<List<int>> GetSlotNumsForColumns(Cell cell, IEnumerable<MemberPath> columns)
 {
     var slotNums = new List<List<int>>();
     var set = cell.CQuery.Extent as AssociationSet;
     //If it is an association set, the columns could be projected
     //in either end so get the slotNums from both the ends
     if (set != null)
     {
         foreach (var setEnd in set.AssociationSetEnds)
         {
             var endSlots = cell.CQuery.GetAssociationEndSlots(setEnd.CorrespondingAssociationEndMember);
             Debug.Assert(endSlots.Count > 0);
             var localslotNums = cell.SQuery.GetProjectedPositions(columns, endSlots);
             if (localslotNums != null)
             {
                 slotNums.Add(localslotNums);
             }
         }
     }
     else
     {
         var localslotNums = cell.SQuery.GetProjectedPositions(columns);
         if (localslotNums != null)
         {
             slotNums.Add(localslotNums);
         }
     }
     return slotNums;
 }
        // effects: Returns the end to which columns are exactly mapped in the
        // relationship set given by cell.CQuery.Extent -- if this extent is
        // an entityset returns null. If the columns are not mapped in this cell to an
        // end exactly or columns are not projected in cell, returns null
        private static AssociationEndMember GetRelationEndForColumns(Cell cell, IEnumerable<MemberPath> columns)
        {
            if (cell.CQuery.Extent is EntitySet)
            {
                return null;
            }

            var relationSet = (AssociationSet)cell.CQuery.Extent;

            // Go through all the ends and see if they are mapped in this cell
            foreach (var relationEnd in relationSet.AssociationSetEnds)
            {
                var endMember = relationEnd.CorrespondingAssociationEndMember;
                var prefix = new MemberPath(relationSet, endMember);
                // Note: primaryKey is the key for the entity set but
                // prefixed with the relationship's path - we are trying to
                // check if the entity's keys are mapped in this cell as an end
                var primaryKey = ExtentKey.GetPrimaryKeyForEntityType(prefix, relationEnd.EntitySet.ElementType);

                // Check if this end is mapped in this cell -- we are
                // checking on the C-side -- we get all the indexes of the
                // end's keyfields
                var endIndexes = cell.CQuery.GetProjectedPositions(primaryKey.KeyFields);
                if (endIndexes != null)
                {
                    // Get all the slots corresponding to the columns
                    // But stick to the slots with in these ends since the same column might be 
                    //projected twice in different ends
                    var columnIndexes = cell.SQuery.GetProjectedPositions(columns, endIndexes);
                    if (columnIndexes == null)
                    {
                        continue; // columns are not projected with in this end
                    }
                    // Note that the positions need not match exactly - we have a
                    // separate test that will do that for us: CheckForeignKeyColumnOrder
                    if (Helpers.IsSetEqual(columnIndexes, endIndexes, EqualityComparer<int>.Default))
                    {
                        // The columns map exactly to this end -- return it
                        return endMember;
                    }
                }
            }
            return null;
        }
        // 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;
        }
        // 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: IsForeignKeySuperSetOfPrimaryKeyInChildTable() is false
        // and primaryKeys of ChildTable are not mapped in cell. cell
        // corresponds to an association set. parentSet is the set
        // corresponding to the end that we are looking at
        // effects: Checks if the constraint is correctly maintained in
        // C-space via an association set (being a subset of the
        // corresponding entitySet)
        private bool CheckConstraintWhenOnlyParentMapped(Cell cell, EntitySet parentSet, AssociationSet assocSet, AssociationEndMember endMember,
                                                         QueryRewriter childRewriter, QueryRewriter parentRewriter,
                                                         ConfigViewGenerator config)
        {

            ViewgenContext childContext = childRewriter.ViewgenContext;
            ViewgenContext parentContext = parentRewriter.ViewgenContext;

            CellTreeNode pNode = parentRewriter.BasicView;
            Debug.Assert(pNode != null);

            RoleBoolean endRoleBoolean = new RoleBoolean(assocSet.AssociationSetEnds[endMember.Name]);
            // use query in pNode as a factory to create a bool expression for the endRoleBoolean
            BoolExpression endCondition = pNode.RightFragmentQuery.Condition.Create(endRoleBoolean);
            FragmentQuery cNodeQuery = FragmentQuery.Create(pNode.RightFragmentQuery.Attributes, endCondition);

            FragmentQueryProcessor qp = FragmentQueryProcessor.Merge(childContext.RightFragmentQP, parentContext.RightFragmentQP);
            bool cImpliesP = qp.IsContainedIn(cNodeQuery, pNode.RightFragmentQuery);
            return cImpliesP;
        }
 internal Record(bool isError, ViewGenErrorCode errorCode, string message, Cell sourceCell, string debugMessage)
 {
     Init(isError, errorCode, message, new Cell[] { sourceCell }, debugMessage);
 }
        // 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: 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;
        }
        // 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>
        /// Given a cell, a member and a boolean condition on that member, creates additional cell
        /// which with the specified restriction on the member in addition to original condition.
        /// e.i conjunction of original condition AND member in newCondition
        /// 
        /// Creation fails when the original condition contradicts new boolean condition
        /// 
        /// ViewTarget tells whether MemberPath is in Cquery or SQuery
        /// </summary>
        private bool TryCreateAdditionalCellWithCondition(
            Cell originalCell, MemberPath memberToExpand, bool conditionValue, ViewTarget viewTarget, out Cell result)
        {
            Debug.Assert(originalCell != null);
            Debug.Assert(memberToExpand != null);
            result = null;

            //Create required structures
            var leftExtent = originalCell.GetLeftQuery(viewTarget).SourceExtentMemberPath;
            var rightExtent = originalCell.GetRightQuery(viewTarget).SourceExtentMemberPath;

            //Now for the given left-side projected member, find corresponding right-side member that it is mapped to 
            var indexOfBooLMemberInProjection =
                originalCell.GetLeftQuery(viewTarget).GetProjectedMembers().TakeWhile(path => !path.Equals(memberToExpand)).Count();
            var rightConditionMemberSlot =
                ((MemberProjectedSlot)originalCell.GetRightQuery(viewTarget).ProjectedSlotAt(indexOfBooLMemberInProjection));
            var rightSidePath = rightConditionMemberSlot.MemberPath;

            var leftSlots = new List<ProjectedSlot>();
            var rightSlots = new List<ProjectedSlot>();

            //Check for impossible conditions (otehrwise we get inaccurate pre-validation errors)
            var negatedCondition = new ScalarConstant(!conditionValue);

            if (originalCell.GetLeftQuery(viewTarget).Conditions
                    .Where(restriction => restriction.RestrictedMemberSlot.MemberPath.Equals(memberToExpand))
                    .Where(restriction => restriction.Domain.Values.Contains(negatedCondition)).Any()
                || originalCell.GetRightQuery(viewTarget).Conditions
                       .Where(restriction => restriction.RestrictedMemberSlot.MemberPath.Equals(rightSidePath))
                       .Where(restriction => restriction.Domain.Values.Contains(negatedCondition)).Any())
            {
                return false;
            }
            //End check

            //Create Projected Slots
            // Map all slots in original cell (not just keys) because some may be required (non nullable and no default)
            // and others may have not_null condition so MUST be projected. Rely on the user doing the right thing, otherwise
            // they will get the error message anyway
            for (var i = 0; i < originalCell.GetLeftQuery(viewTarget).NumProjectedSlots; i++)
            {
                leftSlots.Add(originalCell.GetLeftQuery(viewTarget).ProjectedSlotAt(i));
            }

            for (var i = 0; i < originalCell.GetRightQuery(viewTarget).NumProjectedSlots; i++)
            {
                rightSlots.Add(originalCell.GetRightQuery(viewTarget).ProjectedSlotAt(i));
            }

            //Create condition boolena expressions
            var leftQueryWhereClause =
                BoolExpression.CreateLiteral(new ScalarRestriction(memberToExpand, new ScalarConstant(conditionValue)), null);
            leftQueryWhereClause = BoolExpression.CreateAnd(originalCell.GetLeftQuery(viewTarget).WhereClause, leftQueryWhereClause);

            var rightQueryWhereClause =
                BoolExpression.CreateLiteral(new ScalarRestriction(rightSidePath, new ScalarConstant(conditionValue)), null);
            rightQueryWhereClause = BoolExpression.CreateAnd(originalCell.GetRightQuery(viewTarget).WhereClause, rightQueryWhereClause);

            //Create additional Cells
            var rightQuery = new CellQuery(
                rightSlots, rightQueryWhereClause, rightExtent, originalCell.GetRightQuery(viewTarget).SelectDistinctFlag);
            var leftQuery = new CellQuery(
                leftSlots, leftQueryWhereClause, leftExtent, originalCell.GetLeftQuery(viewTarget).SelectDistinctFlag);

            Cell newCell;
            if (viewTarget == ViewTarget.UpdateView)
            {
                newCell = Cell.CreateCS(rightQuery, leftQuery, originalCell.CellLabel, m_currentCellNumber);
            }
            else
            {
                newCell = Cell.CreateCS(leftQuery, rightQuery, originalCell.CellLabel, m_currentCellNumber);
            }

            m_currentCellNumber++;
            result = newCell;
            return true;
        }
 internal Record(ViewGenErrorCode errorCode, string message, Cell sourceCell, string debugMessage)
 {
     Init(errorCode, message, new[] { sourceCell }, debugMessage);
 }
 internal LeftCellWrapper(ViewTarget viewTarget, Set <MemberPath> attrs,
                          FragmentQuery fragmentQuery,
                          CellQuery leftCellQuery, CellQuery rightCellQuery, MemberMaps memberMaps, Cell inputCell)
     : this(viewTarget, attrs, fragmentQuery, leftCellQuery, rightCellQuery, memberMaps, Enumerable.Repeat(inputCell, 1))
 {
 }
        // effects: Returns true iff there is a foreign key constraint
        // between cell1 and cell2's S extents
        private bool OverlapViaForeignKeys(Cell cell1, Cell cell2)
        {
            EntitySetBase sExtent1 = cell1.SQuery.Extent;
            EntitySetBase sExtent2 = cell2.SQuery.Extent;

            foreach (ForeignConstraint constraint in m_foreignKeyConstraints)
            {
                if (sExtent1.Equals(constraint.ParentTable) && sExtent2.Equals(constraint.ChildTable) ||
                    sExtent2.Equals(constraint.ParentTable) && sExtent1.Equals(constraint.ChildTable))
                {
                    return true;
                }
            }
            return false;
        }
 // effects: Returns true iff cell1 is an extent at the end of cell2's
 // relationship set or vice versa
 private static bool AreCellsConnectedViaRelationship(Cell cell1, Cell cell2)
 {
     AssociationSet cRelationSet1 = cell1.CQuery.Extent as AssociationSet;
     AssociationSet cRelationSet2 = cell2.CQuery.Extent as AssociationSet;
     if (cRelationSet1 != null && MetadataHelper.IsExtentAtSomeRelationshipEnd(cRelationSet1, cell2.CQuery.Extent))
     {
         return true;
     }
     if (cRelationSet2 != null && MetadataHelper.IsExtentAtSomeRelationshipEnd(cRelationSet2, cell1.CQuery.Extent))
     {
         return true;
     }
     return false;
 }