Exemple #1
0
        private void GenerateCellRelations(int cellNumber)
        {
            // Generate the view cell relation
            List <ViewCellSlot> projectedSlots = new List <ViewCellSlot>();

            // construct a ViewCellSlot for each slot
            Debug.Assert(CQuery.NumProjectedSlots == SQuery.NumProjectedSlots,
                         "Cell queries in cell have a different number of slots");
            for (int i = 0; i < CQuery.NumProjectedSlots; i++)
            {
                ProjectedSlot cSlot = CQuery.ProjectedSlotAt(i);
                ProjectedSlot sSlot = SQuery.ProjectedSlotAt(i);
                Debug.Assert(cSlot != null, "Has cell query been normalized?");
                Debug.Assert(sSlot != null, "Has cell query been normalized?");

                // These slots better be MemberProjectedSlots. We do not have constants etc at this point.
                Debug.Assert(cSlot is MemberProjectedSlot, "cSlot is expected to be MemberProjectedSlot");
                Debug.Assert(sSlot is MemberProjectedSlot, "sSlot is expected to be MemberProjectedSlot");

                MemberProjectedSlot cJoinSlot = (MemberProjectedSlot)cSlot;
                MemberProjectedSlot sJoinSlot = (MemberProjectedSlot)sSlot;

                ViewCellSlot slot = new ViewCellSlot(i, cJoinSlot, sJoinSlot);
                projectedSlots.Add(slot);
            }
            m_viewCellRelation = new ViewCellRelation(this, projectedSlots, cellNumber);
        }
Exemple #2
0
        // requires: caseForOuterJoins corresponds the slot "slotNum"
        // effects: Adds a WhenThen corresponding to child to caseForOuterJoins.
        private void AddCaseForOuterJoins(CaseStatement caseForOuterJoins, CqlBlock child, int slotNum, CqlIdentifiers identifiers)
        {
            // Determine the cells that the slot comes from
            // and make an OR expression, e.g., WHEN _from0 or _from2 or ... THEN child[slotNum]

            ProjectedSlot         childSlot    = child.SlotValue(slotNum);
            ConstantProjectedSlot constantSlot = childSlot as ConstantProjectedSlot;

            if (constantSlot != null && constantSlot.CellConstant.IsNull())
            {
                // NULL being generated by a child - don't need to project
                return;
            }

            BoolExpression originBool = BoolExpression.False;

            for (int i = 0; i < NumBoolSlots; i++)
            {
                int boolSlotNum = BoolIndexToSlot(i);
                if (child.IsProjected(boolSlotNum))
                {
                    // OR it to the expression
                    QualifiedCellIdBoolean boolExpr = new QualifiedCellIdBoolean(child, identifiers, i);
                    originBool = BoolExpression.CreateOr(originBool, BoolExpression.CreateLiteral(boolExpr, RightDomainMap));
                }
            }
            // Qualify the slotNum with the child.CqlAlias for the THEN
            QualifiedSlot slot = child.QualifySlotWithBlockAlias(slotNum);

            caseForOuterJoins.AddWhenThen(originBool, slot);
        }
Exemple #3
0
        // effects: Given a cell, determines the paths to which the paths in
        // columns map to in the C-space and returns them. If some columns
        // are not projected in the cell, or if the corresponding properties
        // are not mapped into C-space, returns null
        internal Set <EdmProperty> GetCSlotsForTableColumns(IEnumerable <MemberPath> columns)
        {
            List <int> fieldNums = SQuery.GetProjectedPositions(columns);

            if (fieldNums == null)
            {
                return(null);
            }

            // The fields are mapped -- see if they are mapped on the
            // cSide and they correspond to the primary key of the
            // entity set

            Set <EdmProperty> cSideMembers = new Set <EdmProperty>();

            foreach (int fieldNum in fieldNums)
            {
                ProjectedSlot       projectedSlot = CQuery.ProjectedSlotAt(fieldNum);
                MemberProjectedSlot slot          = projectedSlot as MemberProjectedSlot;
                if (slot != null)
                {
                    // We can call LastMember since columns do not map to
                    // extents or memberEnds. Can cast to EdmProperty since it
                    // cannot be an association end
                    cSideMembers.Add((EdmProperty)slot.MemberPath.LeafEdmMember);
                }
                else
                {
                    return(null);
                }
            }
            return(cSideMembers);
        }
Exemple #4
0
            internal WhenThen ReplaceWithQualifiedSlot(CqlBlock block)
            {
                // Change the THEN part
                ProjectedSlot newValue = m_value.DeepQualify(block);

                return(new WhenThen(m_condition, newValue));
            }
Exemple #5
0
        // requires: children to be a list of nodes that are children of an
        // Inner Join node. slotNum does not correspond to the key slot
        // effects: Determines the child number from which the slot should be
        // picked up.
        private static int GetInnerJoinChildForSlot(List <CqlBlock> children, int slotNum)
        {
            // Picks the child with the non-constant slot first. If none, picks a non-null constant slot.
            // If not een that, picks any one
            int result = -1;

            for (int i = 0; i < children.Count; i++)
            {
                CqlBlock child = children[i];
                if (false == child.IsProjected(slotNum))
                {
                    continue;
                }
                ProjectedSlot         slot         = child.SlotValue(slotNum);
                ConstantProjectedSlot constantSlot = slot as ConstantProjectedSlot;
                MemberProjectedSlot   joinSlot     = slot as MemberProjectedSlot;
                if (joinSlot != null)
                { // Pick the non-constant slot
                    result = i;
                }
                else if (constantSlot != null && constantSlot.CellConstant.IsNull())
                {
                    if (result == -1)
                    { // In case, all are null
                        result = i;
                    }
                }
                else
                {
                    // Just pick anything
                    result = i;
                }
            }
            return(result);
        }
Exemple #6
0
        /// <summary>
        /// Adds an expression of the form "WHEN <paramref name="condition"/> THEN <paramref name="value"/>".
        /// This operation is not allowed after the <see cref="Simplify"/> call.
        /// </summary>
        internal void AddWhenThen(BoolExpression condition, ProjectedSlot value)
        {
            Debug.Assert(!m_simplified, "Attempt to modify a simplified case statement");
            Debug.Assert(value != null);

            condition.ExpensiveSimplify();
            m_clauses.Add(new WhenThen(condition, value));
        }
Exemple #7
0
 private static StringBuilder CaseSlotValueAsEsql(StringBuilder builder, ProjectedSlot slot, MemberPath outputMember, string blockAlias, IEnumerable <WithRelationship> withRelationships, int indentLevel)
 {
     // We should never have THEN as a BooleanProjectedSlot.
     Debug.Assert(slot is MemberProjectedSlot || slot is QualifiedSlot || slot is ConstantProjectedSlot,
                  "Case statement THEN can only have constants or members.");
     slot.AsEsql(builder, outputMember, blockAlias, 1);
     WithRelationshipsClauseAsEsql(builder, withRelationships, blockAlias, indentLevel, slot);
     return(builder);
 }
Exemple #8
0
        private static DbExpression CaseSlotValueAsCqt(DbExpression row, ProjectedSlot slot, MemberPath outputMember, IEnumerable <WithRelationship> withRelationships)
        {
            // We should never have THEN as a BooleanProjectedSlot.
            Debug.Assert(slot is MemberProjectedSlot || slot is QualifiedSlot || slot is ConstantProjectedSlot,
                         "Case statement THEN can only have constants or members.");
            DbExpression cqt = slot.AsCqt(row, outputMember);

            cqt = WithRelationshipsClauseAsCqt(row, cqt, withRelationships, slot);
            return(cqt);
        }
        protected override bool IsEqualTo(ProjectedSlot right)
        {
            ConstantProjectedSlot rightSlot = right as ConstantProjectedSlot;

            if (rightSlot == null)
            {
                return(false);
            }
            return(Constant.EqualityComparer.Equals(m_constant, rightSlot.m_constant));
        }
        protected override bool IsEqualTo(ProjectedSlot right)
        {
            MemberProjectedSlot rightSlot = right as MemberProjectedSlot;

            if (rightSlot == null)
            {
                return(false);
            }
            // We want equality of the paths
            return(MemberPath.EqualityComparer.Equals(m_memberPath, rightSlot.m_memberPath));
        }
Exemple #11
0
        /// <summary>
        /// Simplifies the <see cref="CaseStatement"/> so that unnecessary WHEN/THENs for nulls/undefined values are eliminated.
        /// Also, adds an ELSE clause if possible.
        /// </summary>
        internal void Simplify()
        {
            if (m_simplified)
            {
                return;
            }

            List <CaseStatement.WhenThen> clauses = new List <CaseStatement.WhenThen>();
            // remove all WHEN clauses where the value gets set to "undefined"
            // We eliminate the last clause for now - we could determine the
            // "most complicated" WHEN clause and eliminate it
            bool eliminatedNullClauses = false;

            foreach (WhenThen clause in m_clauses)
            {
                ConstantProjectedSlot constantSlot = clause.Value as ConstantProjectedSlot;
                // If null or undefined, remove it
                if (constantSlot != null && (constantSlot.CellConstant.IsNull() || constantSlot.CellConstant.IsUndefined()))
                {
                    eliminatedNullClauses = true;
                }
                else
                {
                    clauses.Add(clause);
                    if (clause.Condition.IsTrue)
                    {
                        // none of subsequent case statements will be evaluated - ignore them
                        break;
                    }
                }
            }

            if (eliminatedNullClauses && clauses.Count == 0)
            {
                // There is nothing left -- we should add a null as the value
                m_elseValue = new ConstantProjectedSlot(Constant.Null, m_memberPath);
            }

            // If we eliminated some undefined or null clauses, we do not want an else clause
            if (clauses.Count > 0 && false == eliminatedNullClauses)
            {
                // turn the last WHEN clause into an ELSE
                int lastIndex = clauses.Count - 1;
                m_elseValue = clauses[lastIndex].Value;
                clauses.RemoveAt(lastIndex);
            }
            m_clauses = clauses;

            m_simplified = true;
        }
Exemple #12
0
        private bool TryGetInstantiatedType(ProjectedSlot slot, out EdmType type)
        {
            type = null;
            ConstantProjectedSlot constantSlot = slot as ConstantProjectedSlot;

            if (constantSlot != null)
            {
                TypeConstant typeConstant = constantSlot.CellConstant as TypeConstant;
                if (typeConstant != null)
                {
                    type = typeConstant.EdmType;
                    return(true);
                }
            }
            return(false);
        }
Exemple #13
0
        internal MemberProjectedSlot GetRightSideMappedSlotForLeftSideMember(MemberPath member)
        {
            int projectedPosition = LeftCellQuery.GetProjectedPosition(new MemberProjectedSlot(member));

            if (projectedPosition == -1)
            {
                return(null);
            }

            ProjectedSlot slot = RightCellQuery.ProjectedSlotAt(projectedPosition);

            if (slot == null || slot is ConstantProjectedSlot)
            {
                return(null);
            }

            return(slot as MemberProjectedSlot);
        }
Exemple #14
0
        // requires: projectedSlotMap which contains a mapping of the fields
        // for "this" to integers
        // effects: Align the fields of this cell query using the
        // projectedSlotMap and generates a new query into newMainQuery
        // Based on the re-aligned fields in this, re-aligns the
        // corresponding fields in otherQuery as well and modifies
        // newOtherQuery to contain it
        // Example:
        //    input:  Proj[A,B,"5"] = Proj[F,"7",G]
        //            Proj[C,B]     = Proj[H,I]
        //            projectedSlotMap: A -> 0, B -> 1, C -> 2
        //   output:  Proj[A,B,null] = Proj[F,"7",null]
        //            Proj[null,B,C] = Proj[null,I,H]
        internal void CreateFieldAlignedCellQueries(CellQuery otherQuery, MemberProjectionIndex projectedSlotMap,
                                                    out CellQuery newMainQuery, out CellQuery newOtherQuery)
        {
            // mainSlots and otherSlots hold the new slots for two queries
            int numAlignedSlots = projectedSlotMap.Count;

            ProjectedSlot[] mainSlots  = new ProjectedSlot[numAlignedSlots];
            ProjectedSlot[] otherSlots = new ProjectedSlot[numAlignedSlots];

            // Go through the slots for this query and find the new slot for them
            for (int i = 0; i < m_projectedSlots.Length; i++)
            {
                MemberProjectedSlot slot = m_projectedSlots[i] as MemberProjectedSlot;
                Debug.Assert(slot != null, "All slots during cell normalization must field slots");
                // Get the the ith slot's variable and then get the
                // new slot number from the field map
                int newSlotNum = projectedSlotMap.IndexOf(slot.MemberPath);
                Debug.Assert(newSlotNum >= 0, "Field projected but not in projectedSlotMap");
                mainSlots[newSlotNum]  = m_projectedSlots[i];
                otherSlots[newSlotNum] = otherQuery.m_projectedSlots[i];

                // We ignore constants -- note that this is not the
                // isHighpriority or discriminator case.  An example of this
                // is when (say) Address does not have zip but USAddress
                // does.  Then the constraint looks like Pi_NULL, A, B(E) =
                // Pi_x, y, z(S)

                // We don't care about this null in the view generation of
                // the left side. Note that this could happen in inheritance
                // or in cases when say the S side has 20 fields but the C
                // side has only 3 - the other 17 are null or default.

                // NOTE: We allow such constants only on the C side and not
                // ont the S side. Otherwise, we can have a situation Pi_A,
                // B, C(E) = Pi_5, y, z(S) Then someone can set A to 7 and we
                // will not roundtrip. We check for this in validation
            }

            // Make the new cell queries with the new slots
            newMainQuery  = new CellQuery(this, mainSlots);
            newOtherQuery = new CellQuery(otherQuery, otherSlots);
        }
Exemple #15
0
        private static DbExpression WithRelationshipsClauseAsCqt(DbExpression row, DbExpression slotValueExpr, IEnumerable <WithRelationship> withRelationships, ProjectedSlot slot)
        {
            List <DbRelatedEntityRef> relatedEntityRefs = new List <DbRelatedEntityRef>();

            WithRelationshipsClauseAsCql(
                // emitWithRelationship action
                (withRelationship) =>
            {
                relatedEntityRefs.Add(withRelationship.AsCqt(row));
            },
                withRelationships,
                slot);

            if (relatedEntityRefs.Count > 0)
            {
                DbNewInstanceExpression typeConstructor = slotValueExpr as DbNewInstanceExpression;
                Debug.Assert(typeConstructor != null && typeConstructor.ResultType.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType,
                             "WITH RELATIONSHIP clauses should be specified for entity type constructors only.");
                return(DbExpressionBuilder.CreateNewEntityWithRelationshipsExpression(
                           (EntityType)typeConstructor.ResultType.EdmType,
                           typeConstructor.Arguments,
                           relatedEntityRefs));
            }
            else
            {
                return(slotValueExpr);
            }
        }
Exemple #16
0
        // effects: Generates a SlotInfo object for a slot of a join node. It
        // uses the type of the join operation (opType), whether the slot is
        // required by the parent or not (isRequiredSlot), the children of
        // this node (children) and the number of the slotNum
        private SlotInfo GetJoinSlotInfo(CellTreeOpType opType, bool isRequiredSlot,
                                         List <CqlBlock> children, int slotNum, CqlIdentifiers identifiers)
        {
            if (false == isRequiredSlot)
            {
                // The slot will not be used. So we can set the projected slot to be null
                SlotInfo unrequiredSlotInfo = new SlotInfo(false, false, null, GetMemberPath(slotNum));
                return(unrequiredSlotInfo);
            }

            // For a required slot, determine the child who is contributing to this value
            int           childDefiningSlot = -1;
            CaseStatement caseForOuterJoins = null;

            for (int childNum = 0; childNum < children.Count; childNum++)
            {
                CqlBlock child = children[childNum];
                if (false == child.IsProjected(slotNum))
                {
                    continue;
                }
                // For keys, we can pick any child block. So the first
                // one that we find is fine as well
                if (IsKeySlot(slotNum))
                {
                    childDefiningSlot = childNum;
                    break;
                }
                else if (opType == CellTreeOpType.IJ)
                {
                    // For Inner Joins, most of the time, the entries will be
                    // the same in all the children. However, in some cases,
                    // we will end up with NULL in one child and an actual
                    // value in another -- we should pick up the actual value in that case
                    childDefiningSlot = GetInnerJoinChildForSlot(children, slotNum);
                    break;
                }
                else
                {
                    // For LOJs, we generate a case statement if more than
                    // one child generates the value - until then we do not
                    // create the caseForOuterJoins object
                    if (childDefiningSlot != -1)
                    {
                        // We really need a case statement now
                        // We have the value being generated by another child
                        // We need to fetch the variable from the appropriate child
                        Debug.Assert(false == IsBoolSlot(slotNum), "Boolean slots cannot come from two children");
                        if (caseForOuterJoins == null)
                        {
                            MemberPath outputMember = GetMemberPath(slotNum);
                            caseForOuterJoins = new CaseStatement(outputMember);
                            // Add the child that we had not added in the first shot
                            AddCaseForOuterJoins(caseForOuterJoins, children[childDefiningSlot], slotNum, identifiers);
                        }
                        AddCaseForOuterJoins(caseForOuterJoins, child, slotNum, identifiers);
                    }
                    childDefiningSlot = childNum;
                }
            }

            MemberPath    memberPath = GetMemberPath(slotNum);
            ProjectedSlot slot       = null;

            // Generate the slot value -- case statement slot, or a qualified slot or null or false.
            // If case statement slot has nothing, treat it as null/empty.
            if (caseForOuterJoins != null && (caseForOuterJoins.Clauses.Count > 0 || caseForOuterJoins.ElseValue != null))
            {
                caseForOuterJoins.Simplify();
                slot = new CaseStatementProjectedSlot(caseForOuterJoins, null);
            }
            else if (childDefiningSlot >= 0)
            {
                slot = children[childDefiningSlot].QualifySlotWithBlockAlias(slotNum);
            }
            else
            {
                // need to produce output slot, but don't have a value
                // output NULL for fields or False for bools
                if (IsBoolSlot(slotNum))
                {
                    slot = new BooleanProjectedSlot(BoolExpression.False, identifiers, SlotToBoolIndex(slotNum));
                }
                else
                {
                    slot = new ConstantProjectedSlot(Domain.GetDefaultValueForMemberPath(memberPath, GetLeaves(), ViewgenContext.Config), memberPath);
                }
            }

            // We need to ensure that _from variables are never null since
            // view generation uses 2-valued boolean logic.
            // They can become null in outer joins. We compensate for it by
            // adding AND NOT NULL condition on boolean slots coming from outer joins.
            bool enforceNotNull = IsBoolSlot(slotNum) &&
                                  ((opType == CellTreeOpType.LOJ && childDefiningSlot > 0) ||
                                   opType == CellTreeOpType.FOJ);
            // We set isProjected to be true since we have come up with some value for it
            SlotInfo slotInfo = new SlotInfo(true, true, slot, memberPath, enforceNotNull);

            return(slotInfo);
        }
Exemple #17
0
        private static void WithRelationshipsClauseAsEsql(StringBuilder builder, IEnumerable <WithRelationship> withRelationships, string blockAlias, int indentLevel, ProjectedSlot slot)
        {
            bool first = true;

            WithRelationshipsClauseAsCql(
                // emitWithRelationship action
                (withRelationship) =>
            {
                if (first)
                {
                    builder.Append(" WITH ");
                    first = false;
                }
                withRelationship.AsEsql(builder, blockAlias, indentLevel);
            },
                withRelationships,
                slot);
        }
        internal override CqlBlock ToCqlBlock(bool[] requiredSlots, CqlIdentifiers identifiers, ref int blockAliasNum, ref List <WithRelationship> withRelationships)
        {
            // Get the projected slots and the boolean expressions
            int       totalSlots = requiredSlots.Length;
            CellQuery cellQuery  = LeftCellWrapper.RightCellQuery;

            SlotInfo[] projectedSlots = new SlotInfo[totalSlots];
            Debug.Assert(cellQuery.NumProjectedSlots + cellQuery.NumBoolVars == totalSlots,
                         "Wrong number of projected slots in node");

            Debug.Assert(cellQuery.NumProjectedSlots == ProjectedSlotMap.Count,
                         "Different number of slots in cell query and what we have mappings for");
            // Add the regular fields
            for (int i = 0; i < cellQuery.NumProjectedSlots; i++)
            {
                ProjectedSlot slot = cellQuery.ProjectedSlotAt(i);
                // If the slot is not null, we will project it
                // For extents, we say that all requiredlots are the only the
                // ones that are CLR non-null. Recall that "real" nulls are
                // handled by having a CellConstant.Null in ConstantSlot
                if (requiredSlots[i] && slot == null)
                {
                    MemberPath            memberPath   = ProjectedSlotMap[i];
                    ConstantProjectedSlot defaultValue = new ConstantProjectedSlot(Domain.GetDefaultValueForMemberPath(memberPath, GetLeaves(), ViewgenContext.Config), memberPath);
                    cellQuery.FixMissingSlotAsDefaultConstant(i, defaultValue);
                    slot = defaultValue;
                }
                SlotInfo slotInfo = new SlotInfo(requiredSlots[i], slot != null,
                                                 slot, ProjectedSlotMap[i]);
                projectedSlots[i] = slotInfo;
            }

            // Add the boolean fields
            for (int boolNum = 0; boolNum < cellQuery.NumBoolVars; boolNum++)
            {
                BoolExpression       expr = cellQuery.GetBoolVar(boolNum);
                BooleanProjectedSlot boolSlot;
                if (expr != null)
                {
                    boolSlot = new BooleanProjectedSlot(expr, identifiers, boolNum);
                }
                else
                {
                    boolSlot = new BooleanProjectedSlot(BoolExpression.False, identifiers, boolNum);
                }
                int      slotIndex = BoolIndexToSlot(boolNum);
                SlotInfo slotInfo  = new SlotInfo(requiredSlots[slotIndex], expr != null,
                                                  boolSlot, null);
                projectedSlots[slotIndex] = slotInfo;
            }

            // See if we are generating a query view and whether there are any colocated foreign keys for which
            // we have to add With statements.
            IEnumerable <SlotInfo> totalProjectedSlots = projectedSlots;

            if ((cellQuery.Extent.EntityContainer.DataSpace == DataSpace.SSpace) &&
                (this.m_cellWrapper.LeftExtent.BuiltInTypeKind == BuiltInTypeKind.EntitySet))
            {
                IEnumerable <StorageAssociationSetMapping> associationSetMaps =
                    this.ViewgenContext.EntityContainerMapping.GetRelationshipSetMappingsFor(this.m_cellWrapper.LeftExtent, cellQuery.Extent);
                List <SlotInfo> foreignKeySlots = new List <SlotInfo>();
                foreach (StorageAssociationSetMapping colocatedAssociationSetMap in associationSetMaps)
                {
                    WithRelationship withRelationship;
                    if (TryGetWithRelationship(colocatedAssociationSetMap, this.m_cellWrapper.LeftExtent, cellQuery.SourceExtentMemberPath, ref foreignKeySlots, out withRelationship))
                    {
                        withRelationships.Add(withRelationship);
                        totalProjectedSlots = projectedSlots.Concat(foreignKeySlots);
                    }
                }
            }
            ExtentCqlBlock result = new ExtentCqlBlock(cellQuery.Extent, cellQuery.SelectDistinctFlag, totalProjectedSlots.ToArray(),
                                                       cellQuery.WhereClause, identifiers, ++blockAliasNum);

            return(result);
        }
Exemple #19
0
        private static void WithRelationshipsClauseAsCql(Action <WithRelationship> emitWithRelationship, IEnumerable <WithRelationship> withRelationships, ProjectedSlot slot)
        {
            if (withRelationships != null && withRelationships.Count() > 0)
            {
                ConstantProjectedSlot constantSlot = slot as ConstantProjectedSlot;
                Debug.Assert(constantSlot != null, "WITH RELATIONSHIP clauses should be specified for type constant slots only.");
                TypeConstant typeConstant = constantSlot.CellConstant as TypeConstant;
                Debug.Assert(typeConstant != null, "WITH RELATIONSHIP clauses should be there for type constants only.");
                EdmType fromType = typeConstant.EdmType;

                foreach (WithRelationship withRelationship in withRelationships)
                {
                    // Add With statement for the types that participate in the association.
                    if (withRelationship.FromEndEntityType.IsAssignableFrom(fromType))
                    {
                        emitWithRelationship(withRelationship);
                    }
                }
            }
        }
Exemple #20
0
 /// <summary>
 /// Creates WHEN condition THEN value.
 /// </summary>
 internal WhenThen(BoolExpression condition, ProjectedSlot value)
 {
     m_condition = condition;
     m_value     = value;
 }
Exemple #21
0
        // 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
            KeyToListMap <MemberProjectedSlot, int> 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 (int i = 0; i < m_projectedSlots.Length; i++)
            {
                ProjectedSlot       projectedSlot = m_projectedSlots[i];
                MemberProjectedSlot 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
            bool isErrorSituation = false;

            foreach (MemberProjectedSlot slot in slotMap.Keys)
            {
                ReadOnlyCollection <int> 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(System.Data.Entity.Strings.ViewGen_Duplicate_CProperties(Extent.Name));
                        builder.AppendLine();
                    }
                    StringBuilder tmpBuilder = new StringBuilder();
                    for (int i = 0; i < indexes.Count; i++)
                    {
                        int index = indexes[i];
                        if (i != 0)
                        {
                            tmpBuilder.Append(", ");
                        }
                        // The slot must be a JoinTreeSlot. If it isn't it is an internal error
                        MemberProjectedSlot 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);
            }

            ErrorLog.Record record = new ErrorLog.Record(true, ViewGenErrorCode.DuplicateCPropertiesMapped, builder.ToString(), sourceCell, String.Empty);
            return(record);
        }