CellQuery.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataEntity / System / Data / Map / ViewGeneration / Structures / CellQuery.cs / 1 / CellQuery.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

using System.Data.Common.Utils; 
using System.Collections.Generic;
using System.Data.Mapping.ViewGeneration.Validation;
using System.Text;
using System.Diagnostics; 
using System.Collections.ObjectModel;
using System.Data.Mapping.ViewGeneration.Utils; 
using System.Data.Mapping.ViewGeneration.CqlGeneration; 
using System.Data.Metadata.Edm;
 
namespace System.Data.Mapping.ViewGeneration.Structures {

    using AttributeSet = Set;
    using System.Data.Entity; 

    // This class stores the C or S query. For example, 
    // (C) SELECT (p type Person) AS D1, p.pid, p.name FROM p in P WHERE D1 
    // (S) SELECT True AS D1, pid, name FROM SPerson WHERE D1
    // 
    // The cell query is stored in a "factored" manner for ease of
    // cell-merging and cell manipulation. It contains:
    // * Projection: A sequence of slots and a sequence of boolean slots (one
    //   for each cell in the extent) 
    // * A From part represented as a Join tree
    // * A where clause 
    internal class CellQuery : InternalBase { 

        #region Constructors 
        // effects: Creates a cell query with the given projection (slots),
        // from part (joinTreeRoot) and the predicate (whereClause)
        // Used for cell creation
        internal CellQuery(List slots, BoolExpression whereClause, ExtentJoinTreeNode joinTreeRoot) 
            : this(null, slots.ToArray(), whereClause, new List(),
                   DuplicateElimination.No, joinTreeRoot) { 
        } 

 

        // effects: Given all the fields, just sets them.
        private CellQuery(SchemaContext schemaContext,
                          ProjectedSlot[] projectedSlots, 
                          BoolExpression whereClause,
                          List boolExprs, 
                          DuplicateElimination elimDupl, ExtentJoinTreeNode joinTreeRoot) { 
            m_schemaContext = schemaContext;
            m_boolExprs = boolExprs; 
            m_projectedSlots = projectedSlots;
            m_whereClause = whereClause;
            m_originalWhereClause = whereClause;
            m_eliminateDuplicates = elimDupl; 
            m_joinTreeRoot = joinTreeRoot;
        } 
 
        /// 
        /// Copy Constructor 
        /// 
        internal CellQuery(CellQuery source)
        {
            this.m_basicCellRelation = source.m_basicCellRelation; 
            this.m_boolExprs = source.m_boolExprs;
            this.m_eliminateDuplicates = source.m_eliminateDuplicates; 
            this.m_joinTreeRoot = source.m_joinTreeRoot; 
            this.m_originalWhereClause = source.m_originalWhereClause;
            this.m_projectedSlots = source.m_projectedSlots; 
            this.m_schemaContext = source.m_schemaContext;
            this.m_whereClause = source.m_whereClause;
        }
 
        // effects: Given an existing cellquery, makes a new one based on it
        // but uses the slots as specified with newSlots 
        private CellQuery(CellQuery existing, ProjectedSlot[] newSlots) : 
            this(existing.m_schemaContext, newSlots, existing.m_whereClause, existing.m_boolExprs,
                 existing.m_eliminateDuplicates, existing.m_joinTreeRoot) { 
        }
        #endregion

        #region Fields 
        // Whether query has a 'SELECT DISTINCT' on top
        private enum DuplicateElimination { 
            Yes, 
            No
        } 

        // A reference to the schema information
        private SchemaContext m_schemaContext;
 
        // The boolean expressions that essentially capture the type information
        // Fixed-size list; NULL in the list means 'unused' 
        private List m_boolExprs; 
        // The fields including the key fields
        // May contain NULLs - means 'not in the projection' 
        private ProjectedSlot[] m_projectedSlots;
        // where clause: An expression formed using the boolExprs
        private BoolExpression m_whereClause;
        // This is the where clause created when the constructor was called 
        // i.e., when FixCellConstantDomains is called, this
        // m_originalWhereClause is not changed 
        private BoolExpression m_originalWhereClause; 
        private DuplicateElimination m_eliminateDuplicates;
        // The from part of the query 
        private ExtentJoinTreeNode m_joinTreeRoot;
        // The basic cell relation for all slots in this
        private BasicCellRelation m_basicCellRelation;
        #endregion 

        #region Properties 
        // effects: Returns the top levelextent corresponding to this cell query 
        internal EntitySetBase Extent {
            // CHANGE_[....]_FEATURE_COMPOSITION What happens with composition 
            get {
                EntitySetBase extent = m_joinTreeRoot.Extent as EntitySetBase;
                Debug.Assert(extent != null, "JoinTreeRoot in cellquery must be an extent");
                return extent; 
            }
        } 
 
        // effects: Returns the number of slots projected in the query
        internal int NumProjectedSlots { 
            get { return m_projectedSlots.Length; }
        }

        // effects: Returns the number of boolean expressions projected in the query 
        internal int NumBoolVars {
            get { return m_boolExprs.Count; } 
        } 

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 
        internal BoolExpression OriginalWhereClause {
            get { return m_originalWhereClause; }
        }
 
        internal BoolExpression WhereClause {
            get { return m_whereClause; } 
        } 

        // effects: Returns the root of the join tree 
        internal JoinTreeNode JoinTreeRoot {
            get { return m_joinTreeRoot; }
        }
 
        // effects: Returns the relation that contains all the slots present
        // in this cell query 
        internal BasicCellRelation BasicCellRelation { 
            get {
                Debug.Assert(m_basicCellRelation != null, "BasicCellRelation must be created first"); 
                return m_basicCellRelation;
            }
        }
        #endregion 

        #region ProjectedSlots related methods 
        // effects: Returns the slotnum projected slot 
        internal ProjectedSlot ProjectedSlotAt(int slotNum) {
            Debug.Assert(slotNum < m_projectedSlots.Length, "Slot number too high"); 
            return m_projectedSlots[slotNum];
        }

        // 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 slotMap = 
                new KeyToListMap(JoinTreeSlot.SpecificEqualityComparer);
 
            // 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];
                JoinTreeSlot slot = projectedSlot as JoinTreeSlot; 
                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 (JoinTreeSlot slot in slotMap.Keys) { 
                ReadOnlyCollection 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_0(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
                        JoinTreeSlot cSlot = (JoinTreeSlot)cQuery.m_projectedSlots[index];
                        tmpBuilder.Append(cSlot.ToUserString()); 
                    }
                    builder.AppendLine(Strings.ViewGen_Duplicate_CProperties_IsMapped_1(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; 
        } 

        // requires: "this" is a query on the C-side 
        // and cSideSlotIndexes corresponds to the indexes
        // (into "this") that the slot is being mapped into
        // cSideSlotIndexes.Count > 1 - that is, a particular column in "this"'s corresponding S-Query
        // has been mapped to more than one property in "this" 
        //
        // effects: Checks that the multiple mappings on the C-side are 
        // backed by an appropriate Referential constraint 
        // If a column is mapped to two properties  in a single cell:
        // (a) Must be an association 
        // (b) The two properties must be on opposite ends of the association
        // (c) The association must have a RI constraint
        // (d) Ordinal[A] == Ordinal[B] in the RI constraint
        // (c) and (d) can be stated as - the slots are equivalent, i.e., 
        // kept equal via an RI constraint
        private bool AreSlotsEquivalentViaRefConstraints(ReadOnlyCollection cSideSlotIndexes) { 
 
            // Check (a): Must be an association
            AssociationSet assocSet = Extent as AssociationSet; 
            if (assocSet == null) {
                return false;
            }
 
            // Check (b): The two properties must be on opposite ends of the association
            // There better be exactly two properties! 
            Debug.Assert(cSideSlotIndexes.Count > 1, "Method called when no duplicate mapping"); 
            if (cSideSlotIndexes.Count > 2) {
                return false; 
            }

            // They better be join tree slots (if they are mapped!) and map to opposite ends
            JoinTreeSlot slot0 = (JoinTreeSlot) m_projectedSlots[cSideSlotIndexes[0]]; 
            JoinTreeSlot slot1 = (JoinTreeSlot) m_projectedSlots[cSideSlotIndexes[1]];
 
            return slot0.MemberPath.IsEquivalentViaRefConstraint(slot1.MemberPath); 
        }
 
        // 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) {
            StringBuilder builder = new StringBuilder(); 
            bool foundError = false; 

            foreach (OneOfConst oneOfConst in GetConjunctsFromOriginalWhereClause()) { 
                if (oneOfConst.Values.ContainsNotNull()) {
                    JoinTreeSlot slot = JoinTreeSlot.GetSlotForMember(m_projectedSlots, oneOfConst.Slot.MemberPath);
                    if (slot == null) {
                       // condition of NotNull and slot not being projected 
                       builder.AppendLine(System.Data.Entity.Strings.ViewGen_NotNull_No_Projected_Slot_0(
                                          oneOfConst.Slot.MemberPath.PathToString(false))); 
                       foundError = true; 
                    }
                } 
            }
            if (false == foundError) {
                return null;
            } 
            ErrorLog.Record record = new ErrorLog.Record(true, ViewGenErrorCode.NotNullNoProjectedSlot, builder.ToString(), sourceCell, String.Empty);
            return record; 
        } 

        internal void FixMissingSlotAsDefaultConstant(int slotNumber, ConstantSlot slot) 
        {
            Debug.Assert(m_projectedSlots[slotNumber] == null, "Another attempt to plug in a default value");
            m_projectedSlots[slotNumber] = slot;
        } 

        // 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, MemberPathMapBase 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++)
            {

                JoinTreeSlot slot = m_projectedSlots[i] as JoinTreeSlot; 
                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);
        } 
 
        // requires: All slots in this are null or non-constants
        // effects: Returns the non-null slots of this 
        internal AttributeSet GetNonNullSlots() {
            AttributeSet attributes = new AttributeSet(MemberPath.EqualityComparer);
            foreach (ProjectedSlot projectedSlot in m_projectedSlots) {
                // null means 'unused' slot -- we ignore those 
                if (projectedSlot != null) {
                    JoinTreeSlot projectedVar = projectedSlot as JoinTreeSlot; 
                    Debug.Assert(projectedVar != null, "Projected slot must not be a constant"); 
                    attributes.Add(projectedVar.MemberPath);
                } 
            }
            return attributes;
        }
 
        // 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 formatEntitySetMessage, 
            Func formatAssociationSetMessage, ViewGenErrorCode errorCode) {
            List prefixes = new List(1);
            // Keep track of the key corresponding to each prefix
            List keys = new List(1); 

            if (Extent is EntitySet) { 
                // For entity set just get the full path of the key properties 
                MemberPath prefix = new MemberPath(Extent, m_joinTreeRoot.MetadataWorkspace);
                prefixes.Add(prefix); 
                EntityType entityType = (EntityType)Extent.ElementType;
                List entitySetKeys = ExtentKey.GetKeysForEntityType(prefix, entityType);
                Debug.Assert(entitySetKeys.Count == 1, "Currently, we only support primary keys");
                keys.Add(entitySetKeys[0]); 

            } else { 
                AssociationSet relationshipSet = (AssociationSet)Extent; 
                // For association set, get the full path of the key
                // properties of each end 

                foreach (AssociationSetEnd relationEnd in relationshipSet.AssociationSetEnds) {
                    AssociationEndMember assocEndMember = relationEnd.CorrespondingAssociationEndMember;
                    MemberPath prefix = new MemberPath(relationshipSet, assocEndMember, m_joinTreeRoot.MetadataWorkspace); 
                    prefixes.Add(prefix);
                    List endKeys = ExtentKey.GetKeysForEntityType(prefix, 
                                                                             MetadataHelper.GetEntityTypeForEnd(assocEndMember)); 
                    Debug.Assert(endKeys.Count == 1, "Currently, we only support primary keys");
                    keys.Add(endKeys[0]); 
                }
            }

            for (int i = 0; i < prefixes.Count; i++) { 
                MemberPath prefix = prefixes[i];
                // Get all or none key slots that are being projected in this cell query 
                List keySlots = JoinTreeSlot.GetKeySlots(GetProjectedSlots(), prefix); 
                if (keySlots == null) {
                    ExtentKey key = keys[i]; 
                    string message;
                    if (Extent is EntitySet) {
                        string keyPropertiesString = MemberPath.PropertiesToUserString(key.KeyFields, true);
                        message = formatEntitySetMessage(keyPropertiesString, Extent.Name); 
                    } else {
                        string endName = prefix.FirstMember.Name; 
                        string keyPropertiesString = MemberPath.PropertiesToUserString(key.KeyFields, false); 
                        message = formatAssociationSetMessage(keyPropertiesString, endName, Extent.Name);
                    } 
                    ErrorLog.Record error = new ErrorLog.Record(true, errorCode, message, ownerCell, String.Empty);
                    return error;
                }
            } 
            return null;
        } 
 
        internal IEnumerable GetProjectedMembers()
        { 
            foreach (JoinTreeSlot slot in this.GetProjectedSlots())
            {
                yield return slot.MemberPath;
            } 
        }
 
        // effects: Returns the fields in this, i.e., not constants or null slots 
        private IEnumerable GetProjectedSlots() {
            foreach (ProjectedSlot slot in m_projectedSlots) { 
                JoinTreeSlot memberSlot = slot as JoinTreeSlot;
                if (memberSlot != null) {
                    yield return memberSlot;
                } 
            }
        } 
 
        // effects: Returns the fields that are used in the query (both projected and non-projected)
        // Output list is a copy, i.e., can be modified by the caller 
        internal List GetAllQuerySlots() {
            List slots = new List();
            m_joinTreeRoot.GatherDescendantSlots(slots);
            return slots; 
        }
 
        // effects: returns the index at which this slot appears in the projection 
        // or -1 if it is not projected
        internal int GetProjectedPosition(JoinTreeSlot slot) { 
            for (int i = 0; i < m_projectedSlots.Length; i++)
            {
                if (JoinTreeSlot.EqualityComparer.Equals(slot, m_projectedSlots[i])) {
                    return i; 
                }
            } 
            return -1; 
        }
 
        // effects: returns the List of indexes at which this member appears in the projection
        // or empty list if it is not projected
        internal List GetProjectedPositions(MemberPath member) {
            List pathIndexes = new List(); 
            for (int i = 0; i < m_projectedSlots.Length; i++) {
                JoinTreeSlot slot = m_projectedSlots[i] as JoinTreeSlot; 
                if (slot != null && MemberPath.EqualityComparer.Equals(member, slot.MemberPath)) { 
                    pathIndexes.Add(i);
                } 
            }
            return pathIndexes;
        }
 
        // effects: Determines the slot numbers for members in cellQuery
        // Returns a set of those paths in the same order as paths. If even 
        // one of the path entries is not projected in the cellquery, returns null 
        internal List GetProjectedPositions(IEnumerable paths) {
            List pathIndexes = new List(); 
            foreach (MemberPath member in paths) {
                // Get the index in checkQuery and add to pathIndexes
                List slotIndexes = GetProjectedPositions(member);
                Debug.Assert(slotIndexes != null); 
                if (slotIndexes.Count == 0) { // member is not projected
                    return null; 
                } 
                Debug.Assert(slotIndexes.Count == 1, "Expecting the path to be projected only once");
                pathIndexes.Add(slotIndexes[0]); 
            }
            return pathIndexes;
        }
 
        // effects : Return the slot numbers for members in Cell Query that
        //           represent the association end member passed in. 
        internal List GetAssociationEndSlots(AssociationEndMember endMember) { 
            List slotIndexes = new List();
            Debug.Assert(this.Extent is AssociationSet); 
            for (int i = 0; i < m_projectedSlots.Length; i++) {
                JoinTreeSlot slot = m_projectedSlots[i] as JoinTreeSlot;
                if (slot != null && slot.MemberPath.FirstMember.Equals(endMember)) {
                    slotIndexes.Add(i); 
                }
            } 
            return slotIndexes; 
        }
 
        // effects: Determines the slot numbers for members in cellQuery
        // Returns a set of those paths in the same order as paths. If even
        // one of the path entries is not projected in the cellquery, returns null
        // If a path is projected more than once, than we choose the one from the 
        // slotsToSearchFrom domain.
        internal List GetProjectedPositions(IEnumerable paths, List slotsToSearchFrom) { 
            List pathIndexes = new List(); 
            foreach (MemberPath member in paths) {
                // Get the index in checkQuery and add to pathIndexes 
                List slotIndexes = GetProjectedPositions(member);
                Debug.Assert(slotIndexes != null);
                if (slotIndexes.Count == 0) { // member is not projected
                    return null; 
                }
                int slotIndex = -1; 
                if (slotIndexes.Count > 1) { 
                    for (int i = 0; i < slotIndexes.Count; i++) {
                        if (slotsToSearchFrom.Contains(slotIndexes[i])) { 
                            Debug.Assert(slotIndex == -1, "Should be projected only once");
                            slotIndex = slotIndexes[i];
                        }
                    } 
                    if (slotIndex == -1) {
                        return null; 
                    } 
                } else {
                    slotIndex = slotIndexes[0]; 
                }
                pathIndexes.Add(slotIndex);
            }
            return pathIndexes; 
        }
 
 
        // requires: The CellConstantDomains in the OneOfConsts of the where
        // clause are partially done 
        // effects: Given the domains of different variables in domainMap,
        // fixes the whereClause of this such that all the
        // CellConstantDomains in OneOfConsts are fully done
        internal void FixCellConstantDomains(MemberDomainMap domainMap, ViewTarget viewTarget) { 
            List atoms = new List();
            foreach (BoolExpression atom in WhereClause.Atoms) { 
                BoolLiteral literal = atom.AsLiteral; 
                OneOfConst oneOfConst = literal as OneOfConst;
                Debug.Assert(oneOfConst != null, "All bool literals must be OneOfConst at this point"); 
                // The oneOfConst needs to be fixed with the new possible
                // values from the domainMap
                IEnumerable possibleValues = domainMap.GetDomain(oneOfConst.Slot.MemberPath);
                OneOfConst newOneOf = OneOfConst.CreateFullOneOfConst(oneOfConst, possibleValues); 

 
                // Prevent optimization of single constraint e.g: "300 in (300)" 
                // But we want to optimize type constants e.g: "category in (Category)"
                // To prevent optimization of bool expressions we add a Sentinel OneOF 

                OneOfScalarConst scalarConst = oneOfConst as OneOfScalarConst;
                bool addSentinel =
                    scalarConst != null && 
                    !scalarConst.Values.Contains(CellConstant.Null) &&
                    !scalarConst.Values.Contains(CellConstant.NotNull) && 
                    !scalarConst.Values.Contains(CellConstant.Undefined); 

                if (addSentinel) 
                {
                    domainMap.AddSentinel(newOneOf.Slot.MemberPath);
                }
 
                atoms.Add(BoolExpression.CreateLiteral(newOneOf, domainMap));
 
                if (addSentinel) 
                {
                    domainMap.RemoveSentinel(newOneOf.Slot.MemberPath); 
                }

            }
            // We create a new whereClause that has the memberDomainMap set 
            if (atoms.Count > 0) {
                m_whereClause = BoolExpression.CreateAnd(atoms.ToArray()); 
            } 
        }
        #endregion 

        #region BooleanExprs related Methods
        // effects: Returns a boolean expression corresponding to the
        // "varNum" boolean in this. 
        internal BoolExpression GetBoolVar(int varNum) {
            return m_boolExprs[varNum]; 
        } 

        // effects: Initalizes the booleans of this cell query to be 
        // true. Creates numBoolVars booleans and sets the cellNum boolean to true
        internal void InitializeBoolExpressions(int numBoolVars, int cellNum) {
            //Debug.Assert(m_boolExprs.Count == 0, "Overwriting existing booleans");
            m_boolExprs = new List(numBoolVars); 
            for (int i = 0; i < numBoolVars; i++) {
                m_boolExprs.Add(null); 
            } 
            Debug.Assert(cellNum < numBoolVars, "Trying to set boolean with too high an index");
            m_boolExprs[cellNum] = BoolExpression.True; 
        }

        // requires: query1 has the same number of boolean expressions as
        // query2. There should be no  index i for which query1's bools[i] != 
        // null and query2's bools[i] != null
        // effects: Given two cellqueries query1 and query2, merges their 
        // boolean expressions while ANDING query1 bools with conjunct1 and 
        // query2's bools with conjunct2 and returns the result
        static private List 
        MergeBoolExpressions(CellQuery query1, CellQuery query2,
                             BoolExpression conjunct1, BoolExpression conjunct2, CellTreeOpType opType, bool canBooleansOverlap) {

            List bools1 = query1.m_boolExprs; 
            List bools2 = query2.m_boolExprs;
 
            // Add conjuncts to both sets if needed 
            if (false == conjunct1.IsTrue) {
                bools1 = BoolExpression.AddConjunctionToBools(bools1, conjunct1); 
            }

            if (false == conjunct2.IsTrue) {
                bools2 = BoolExpression.AddConjunctionToBools(bools2, conjunct2); 
            }
 
            // Perform merge 
            Debug.Assert(bools1.Count == bools2.Count);
            List bools = new List(); 
            // Both bools1[i] and bools2[i] be null for some of the i's. When
            // we merge two (leaf) cells (say), only one boolean each is set
            // in it; the rest are all nulls. If the SP/TM rules have been
            // applied, more than one boolean may be non-null in a cell query 
            for (int i = 0; i < bools1.Count; i++) {
                BoolExpression merged = null; 
                if (bools1[i] == null) 
                {
                    merged = bools2[i]; 
                } else if (bools2[i] == null) {
                    merged = bools1[i];
                } else {
                    // This this ok for validation -- actually, we could even ignore 
                    // the booleans for validation
                    Debug.Assert(canBooleansOverlap, "Possibly incompatible booleans: " + bools1[i] + ", " + bools2[i]); 
                    if (opType == CellTreeOpType.IJ) { 
                        merged = BoolExpression.CreateAnd(bools1[i], bools2[i]);
                    } else if (opType == CellTreeOpType.Union) { 
                        merged = BoolExpression.CreateOr(bools1[i], bools2[i]);
                    } else if (opType == CellTreeOpType.LASJ) {
                        merged = BoolExpression.CreateAnd(bools1[i],
                                                          BoolExpression.CreateNot(bools2[i])); 
                    } else {
                        Debug.Fail("No other operation expected for boolean merge"); 
                    } 
                }
                if (merged != null) 
                {
                    merged.ExpensiveSimplify();
                }
                bools.Add(merged); 
            }
            return bools; 
        } 
        #endregion
        #region WhereClause related methods 
        // requires: The current whereClause corresponds to "True", "OneOfConst" or "
        // "OneOfConst AND ... AND OneOfConst"
        // effects: Yields all the conjuncts (OneOfConsts) in this (i.e., if the whereClause is
        // just True, yields nothing 
        internal IEnumerable GetConjunctsFromWhereClause() {
            return GetConjunctsFromWhereClause(m_whereClause); 
        } 

        // requires: The current originalWhereClause corresponds to "True", "OneOfConst" or " 
        // "OneOfConst AND ... AND OneOfConst"
        // effects: Yields all the conjuncts (OneOfConsts) in this (i.e., if the originalWhereClause is
        // just True, yields nothing
        // reason: we need this method simply because the WhereClause will be manipulated by the ViewGen 
        internal IEnumerable GetConjunctsFromOriginalWhereClause()
        { 
            return GetConjunctsFromWhereClause(m_originalWhereClause); 
        }
 
        private IEnumerable GetConjunctsFromWhereClause(BoolExpression whereClause)
        {
            foreach (BoolExpression boolExpr in whereClause.Atoms)
            { 
                if (boolExpr.IsTrue)
                { 
                    continue; 
                }
                OneOfConst result = boolExpr.AsLiteral as OneOfConst; 
                Debug.Assert(result != null, "Atom must be OneOfConst");
                yield return result;
            }
        } 

        // requires: whereClause is of the form specified in GetConjunctsFromWhereClause 
        // effects: Converts the whereclause to a user-readable string 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        internal void WhereClauseToUserString(StringBuilder builder, MetadataWorkspace workspace) { 
            bool isFirst = true;
            foreach (OneOfConst oneOfConst in GetConjunctsFromWhereClause()) {
                if (isFirst == false) {
                    builder.Append(System.Data.Entity.Strings.ViewGen_AND); 
                }
                oneOfConst.ToUserString(false, builder, workspace); 
            } 
        }
        #endregion 

        #region Full CellQuery methods
        // effects: Determines all the identifiers used in this and adds them to identifiers
        internal void GetIdentifiers(CqlIdentifiers identifiers) { 
            foreach (ProjectedSlot projectedSlot in m_projectedSlots) {
                JoinTreeSlot slot = projectedSlot as JoinTreeSlot; 
                if (slot != null) { 
                    slot.MemberPath.GetIdentifiers(identifiers);
                } 
            }
            m_joinTreeRoot.GetIdentifiers(identifiers);
        }
 
        internal void CreateBasicCellRelation(ViewCellRelation viewCellRelation) {
            List slots = GetAllQuerySlots(); 
            // Create a base cell relation that has all the scalar slots of this 
            m_basicCellRelation = new BasicCellRelation(this, viewCellRelation, slots);
        } 

        // effects: Merges query2 with this according to the TM/SP rules for opType and
        // returns the merged result. canBooleansOverlap indicates whether the bools in this and query2 can overlap, i.e.
        // the same cells may have contributed to query2 and this earlier in the merge process 
        internal bool TryMerge(CellQuery query2, CellTreeOpType opType, bool canBooleansOverlap,
                               MemberDomainMap memberDomainMap, out CellQuery mergedQuery) { 
 
            mergedQuery = null;
            // Initialize g1 and g2 according to the TM/SP rules for IJ, LOJ, Union, FOJ cases 
            BoolExpression g1 = null;
            BoolExpression g2 = null;
            switch (opType) {
                case CellTreeOpType.IJ: 
                    break;
                case CellTreeOpType.LOJ: 
                case CellTreeOpType.LASJ: 
                    g2 = BoolExpression.True;
                    break; 
                case CellTreeOpType.FOJ:
                case CellTreeOpType.Union:
                    g1 = BoolExpression.True;
                    g2 = BoolExpression.True; 
                    break;
                default: 
                    Debug.Fail("Unsupported operator"); 
                    break;
            } 

            Dictionary remap =
                new Dictionary(EqualityComparer.Default);
            CellQuery query1 = this; 

            // Merge the two trees and get the new root 
            ExtentJoinTreeNode newRoot = (ExtentJoinTreeNode)query1.m_joinTreeRoot.TryMergeNode(query2.m_joinTreeRoot, 
                                                                                                 opType, ref g1, ref g2, remap, memberDomainMap);
            if (newRoot == null) { // could not merge 
                return false;
            }

            // Conjuncts for ANDing with the previous whereClauses 
            BoolExpression conjunct1 = BoolExpression.True;
            BoolExpression conjunct2 = BoolExpression.True; 
            BoolExpression whereClause = null; 

            switch (opType) { 
                case CellTreeOpType.IJ:
                    // Project[D1, D2, A, B, C] Select[cond1 and cond2] (T)
                    // We simply merge the two lists of booleans -- no conjuct is added
                    // conjunct1 and conjunct2 don't change 

                    // query1.WhereCaluse AND query2.WhereCaluse 
                    Debug.Assert(g1 == null && g2 == null, "IJ does not affect g1 and g2"); 
                    whereClause = BoolExpression.CreateAnd(query1.m_whereClause, query2.m_whereClause);
                    break; 

                case CellTreeOpType.LOJ:
                    // conjunct1 does not change since D1 remains as is
                    // Project[D1, (expr2 and cond2 and G2) as D2, A, B, C] Select[cond1] (T) 
                    // D1 does not change. New d2 is the list of booleans expressions
                    // for query2 ANDed with g2 AND query2.WhereClause 
                    Debug.Assert(g1 == null, "LOJ does not affect g1"); 
                    conjunct2 = BoolExpression.CreateAnd(query2.m_whereClause, g2);
                    // Just query1's whereclause 
                    whereClause = query1.m_whereClause;
                    break;

                case CellTreeOpType.FOJ: 
                case CellTreeOpType.Union:
                    // Project[(expr1 and cond1 and G1) as D1, (expr2 and cond2 and G2) as D2, A, B, C] Select[cond1] (T) 
                    // New D1 is a list -- newD1 = D1 AND query1.WhereClause AND g1 
                    // New D1 is a list -- newD2 = D2 AND query2.WhereClause AND g2
                    conjunct1 = BoolExpression.CreateAnd(query1.m_whereClause, g1); 
                    conjunct2 = BoolExpression.CreateAnd(query2.m_whereClause, g2);

                    // The new whereClause -- g1 AND query1.WhereCaluse OR g2 AND query2.WhereClause
                    whereClause = BoolExpression.CreateOr(BoolExpression.CreateAnd(query1.m_whereClause, g1), 
                                                          BoolExpression.CreateAnd(query2.m_whereClause, g2));
                    break; 
 
                case CellTreeOpType.LASJ:
                    // conjunct1 does not change since D1 remains as is 
                    // Project[D1, (expr2 and cond2 and G2) as D2, A, B, C] Select[cond1] (T)
                    // D1 does not change. New d2 is the list of booleans expressions
                    // for query2 ANDed with g2 AND NOT query2.WhereClause
                    Debug.Assert(g1 == null, "LASJ does not affect g1"); 
                    conjunct2 = BoolExpression.CreateAnd(query2.m_whereClause, g2);
                    whereClause = BoolExpression.CreateAnd(query1.m_whereClause, BoolExpression.CreateNot(conjunct2)); 
                    break; 
                default:
                    Debug.Fail("Unsupported operator"); 
                    break;
            }

            // Create the various remapped parts for the cell query -- 
            // boolean expressions, merged slots, whereclause, duplicate
            // elimination, join tree 
            List boolExprs = 
                MergeBoolExpressions(query1, query2, conjunct1, conjunct2, opType, canBooleansOverlap);
            BoolExpression.RemapBools(boolExprs, remap); 

            ProjectedSlot[] mergedSlots;
            if (false == ProjectedSlot.TryMergeRemapSlots(query1.m_projectedSlots, query2.m_projectedSlots, remap,
                        out mergedSlots)) { 
                // merging failed because two different right slots go to same left slot
                return false; 
            } 

            whereClause = whereClause.RemapBool(remap); 
            // Needed for nested compositions
            DuplicateElimination elimDupl = MergeDupl(query1.m_eliminateDuplicates, query2.m_eliminateDuplicates);

            Debug.Assert(query1.m_schemaContext == query2.m_schemaContext, 
                         "Merging queries with different schema?");
            whereClause.ExpensiveSimplify(); 
            mergedQuery = new CellQuery(query1.m_schemaContext, mergedSlots, whereClause, 
                                                  boolExprs, elimDupl, newRoot);
            return true; 
        }

        // effects: Given two duplicate eliination choices, returns an OR of them
        static private DuplicateElimination MergeDupl(DuplicateElimination d1, DuplicateElimination d2) { 
            if (d1 == DuplicateElimination.Yes || d2 == DuplicateElimination.Yes) {
                return DuplicateElimination.Yes; 
            } else { 
                return DuplicateElimination.No;
            } 
        }

        // effects: Validates the internal representation of this query
        internal override bool CheckRepInvariant() { 
            foreach (BoolExpression boolExpr in m_boolExprs) {
                boolExpr.CheckRepInvariant(); 
            } 
            m_whereClause.CheckRepInvariant();
 
            m_joinTreeRoot.CheckRepInvariant();
            return true;
        }
 
        // effects: Given a query that is the counterpart of this in a cell,
        // validates the contents of this against other 
        internal bool CheckRepInvariant(CellQuery other) { 
            // Validate the queries internally
            CheckRepInvariant(); 
            other.CheckRepInvariant();

            // Make sure that these queries have the same number of slots and
            // each corresponding slot is of the same type 
            ExceptionHelpers.CheckAndThrowResArgs(m_projectedSlots.Length == other.m_projectedSlots.Length,
                                                  Strings.ViewGen_SlotNumber_Mismatch_1, this, other); 
            return true; 
        }
 
        #endregion

        #region String Methods
        // effects: Modifies stringBuilder to contain a string representation 
        // of the cell query in terms of the original cells that are being used
        internal override void ToCompactString(StringBuilder stringBuilder) { 
            // This could be a simplified view where a number of cells 
            // got merged or it could be one of the original booleans. So
            // determine their numbers using the booleans in m_cellWrapper 
            List boolExprs = m_boolExprs;
            int i = 0;
            bool first = true;
            foreach (BoolExpression boolExpr in boolExprs) { 
                if (boolExpr != null) {
                    if (false == first) { 
                        stringBuilder.Append(","); 
                    } else {
                        stringBuilder.Append("["); 
                    }
                    StringUtil.FormatStringBuilder(stringBuilder, "C{0}", i);
                    first = false;
                } 
                i++;
            } 
            if (true == first) { 
                // No booleans, i.e., no compact representation. Use full string to avoid empty output
                ToFullString(stringBuilder); 
            } else {
                stringBuilder.Append("]");
            }
        } 

        internal override void ToFullString(StringBuilder builder) { 
            builder.Append("SELECT "); 
            StringUtil.ToSeparatedString(builder, m_projectedSlots, ", ", "_");
 
            if (m_boolExprs.Count > 0) {
                builder.Append(", Bool[");
                StringUtil.ToSeparatedString(builder, m_boolExprs, ", ", "_");
                builder.Append("]"); 
            }
 
            builder.Append(" FROM "); 
            m_joinTreeRoot.ToFullString(builder);
 
            if (false == m_whereClause.IsTrue) {
                builder.Append(" WHERE ");
                m_whereClause.ToFullString(builder);
            } 
        }
 
        public override string ToString() { 
            return ToFullString();
        } 

        // eSQL representation of cell query
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        internal string ToESqlString() 
        {
            StringBuilder builder = new StringBuilder(); 
            builder.Append("\n\tSELECT "); 

            foreach (ProjectedSlot ps in m_projectedSlots) 
            {
                JoinTreeSlot jtn = ps as JoinTreeSlot;
                StructuralType st = jtn.MemberPath.LastMember.DeclaringType;
                StringBuilder sb = new StringBuilder(); 
                jtn.MemberPath.AsCql(sb, "e");
                builder.AppendFormat("{0}, ", sb.ToString()); 
            } 
            //remove the extra-comma after the last slot
            builder.Remove(builder.Length - 2, 2); 

            builder.Append("\n\tFROM ");
            EntitySetBase extent = m_joinTreeRoot.Extent;
            CqlWriter.AppendEscapedQualifiedName(builder, extent.EntityContainer.Name, extent.Name); 
            builder.Append(" AS e");
 
            if (m_whereClause.IsTrue == false) 
            {
                builder.Append("\n\tWHERE "); 

                StringBuilder qbuilder = new StringBuilder();
                m_whereClause.AsCql(qbuilder, "e");
 
                builder.Append(qbuilder.ToString());
            } 
            builder.Append("\n    "); 

            return builder.ToString(); 
        }

        #endregion
 
    }
 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

using System.Data.Common.Utils; 
using System.Collections.Generic;
using System.Data.Mapping.ViewGeneration.Validation;
using System.Text;
using System.Diagnostics; 
using System.Collections.ObjectModel;
using System.Data.Mapping.ViewGeneration.Utils; 
using System.Data.Mapping.ViewGeneration.CqlGeneration; 
using System.Data.Metadata.Edm;
 
namespace System.Data.Mapping.ViewGeneration.Structures {

    using AttributeSet = Set;
    using System.Data.Entity; 

    // This class stores the C or S query. For example, 
    // (C) SELECT (p type Person) AS D1, p.pid, p.name FROM p in P WHERE D1 
    // (S) SELECT True AS D1, pid, name FROM SPerson WHERE D1
    // 
    // The cell query is stored in a "factored" manner for ease of
    // cell-merging and cell manipulation. It contains:
    // * Projection: A sequence of slots and a sequence of boolean slots (one
    //   for each cell in the extent) 
    // * A From part represented as a Join tree
    // * A where clause 
    internal class CellQuery : InternalBase { 

        #region Constructors 
        // effects: Creates a cell query with the given projection (slots),
        // from part (joinTreeRoot) and the predicate (whereClause)
        // Used for cell creation
        internal CellQuery(List slots, BoolExpression whereClause, ExtentJoinTreeNode joinTreeRoot) 
            : this(null, slots.ToArray(), whereClause, new List(),
                   DuplicateElimination.No, joinTreeRoot) { 
        } 

 

        // effects: Given all the fields, just sets them.
        private CellQuery(SchemaContext schemaContext,
                          ProjectedSlot[] projectedSlots, 
                          BoolExpression whereClause,
                          List boolExprs, 
                          DuplicateElimination elimDupl, ExtentJoinTreeNode joinTreeRoot) { 
            m_schemaContext = schemaContext;
            m_boolExprs = boolExprs; 
            m_projectedSlots = projectedSlots;
            m_whereClause = whereClause;
            m_originalWhereClause = whereClause;
            m_eliminateDuplicates = elimDupl; 
            m_joinTreeRoot = joinTreeRoot;
        } 
 
        /// 
        /// Copy Constructor 
        /// 
        internal CellQuery(CellQuery source)
        {
            this.m_basicCellRelation = source.m_basicCellRelation; 
            this.m_boolExprs = source.m_boolExprs;
            this.m_eliminateDuplicates = source.m_eliminateDuplicates; 
            this.m_joinTreeRoot = source.m_joinTreeRoot; 
            this.m_originalWhereClause = source.m_originalWhereClause;
            this.m_projectedSlots = source.m_projectedSlots; 
            this.m_schemaContext = source.m_schemaContext;
            this.m_whereClause = source.m_whereClause;
        }
 
        // effects: Given an existing cellquery, makes a new one based on it
        // but uses the slots as specified with newSlots 
        private CellQuery(CellQuery existing, ProjectedSlot[] newSlots) : 
            this(existing.m_schemaContext, newSlots, existing.m_whereClause, existing.m_boolExprs,
                 existing.m_eliminateDuplicates, existing.m_joinTreeRoot) { 
        }
        #endregion

        #region Fields 
        // Whether query has a 'SELECT DISTINCT' on top
        private enum DuplicateElimination { 
            Yes, 
            No
        } 

        // A reference to the schema information
        private SchemaContext m_schemaContext;
 
        // The boolean expressions that essentially capture the type information
        // Fixed-size list; NULL in the list means 'unused' 
        private List m_boolExprs; 
        // The fields including the key fields
        // May contain NULLs - means 'not in the projection' 
        private ProjectedSlot[] m_projectedSlots;
        // where clause: An expression formed using the boolExprs
        private BoolExpression m_whereClause;
        // This is the where clause created when the constructor was called 
        // i.e., when FixCellConstantDomains is called, this
        // m_originalWhereClause is not changed 
        private BoolExpression m_originalWhereClause; 
        private DuplicateElimination m_eliminateDuplicates;
        // The from part of the query 
        private ExtentJoinTreeNode m_joinTreeRoot;
        // The basic cell relation for all slots in this
        private BasicCellRelation m_basicCellRelation;
        #endregion 

        #region Properties 
        // effects: Returns the top levelextent corresponding to this cell query 
        internal EntitySetBase Extent {
            // CHANGE_[....]_FEATURE_COMPOSITION What happens with composition 
            get {
                EntitySetBase extent = m_joinTreeRoot.Extent as EntitySetBase;
                Debug.Assert(extent != null, "JoinTreeRoot in cellquery must be an extent");
                return extent; 
            }
        } 
 
        // effects: Returns the number of slots projected in the query
        internal int NumProjectedSlots { 
            get { return m_projectedSlots.Length; }
        }

        // effects: Returns the number of boolean expressions projected in the query 
        internal int NumBoolVars {
            get { return m_boolExprs.Count; } 
        } 

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 
        internal BoolExpression OriginalWhereClause {
            get { return m_originalWhereClause; }
        }
 
        internal BoolExpression WhereClause {
            get { return m_whereClause; } 
        } 

        // effects: Returns the root of the join tree 
        internal JoinTreeNode JoinTreeRoot {
            get { return m_joinTreeRoot; }
        }
 
        // effects: Returns the relation that contains all the slots present
        // in this cell query 
        internal BasicCellRelation BasicCellRelation { 
            get {
                Debug.Assert(m_basicCellRelation != null, "BasicCellRelation must be created first"); 
                return m_basicCellRelation;
            }
        }
        #endregion 

        #region ProjectedSlots related methods 
        // effects: Returns the slotnum projected slot 
        internal ProjectedSlot ProjectedSlotAt(int slotNum) {
            Debug.Assert(slotNum < m_projectedSlots.Length, "Slot number too high"); 
            return m_projectedSlots[slotNum];
        }

        // 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 slotMap = 
                new KeyToListMap(JoinTreeSlot.SpecificEqualityComparer);
 
            // 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];
                JoinTreeSlot slot = projectedSlot as JoinTreeSlot; 
                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 (JoinTreeSlot slot in slotMap.Keys) { 
                ReadOnlyCollection 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_0(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
                        JoinTreeSlot cSlot = (JoinTreeSlot)cQuery.m_projectedSlots[index];
                        tmpBuilder.Append(cSlot.ToUserString()); 
                    }
                    builder.AppendLine(Strings.ViewGen_Duplicate_CProperties_IsMapped_1(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; 
        } 

        // requires: "this" is a query on the C-side 
        // and cSideSlotIndexes corresponds to the indexes
        // (into "this") that the slot is being mapped into
        // cSideSlotIndexes.Count > 1 - that is, a particular column in "this"'s corresponding S-Query
        // has been mapped to more than one property in "this" 
        //
        // effects: Checks that the multiple mappings on the C-side are 
        // backed by an appropriate Referential constraint 
        // If a column is mapped to two properties  in a single cell:
        // (a) Must be an association 
        // (b) The two properties must be on opposite ends of the association
        // (c) The association must have a RI constraint
        // (d) Ordinal[A] == Ordinal[B] in the RI constraint
        // (c) and (d) can be stated as - the slots are equivalent, i.e., 
        // kept equal via an RI constraint
        private bool AreSlotsEquivalentViaRefConstraints(ReadOnlyCollection cSideSlotIndexes) { 
 
            // Check (a): Must be an association
            AssociationSet assocSet = Extent as AssociationSet; 
            if (assocSet == null) {
                return false;
            }
 
            // Check (b): The two properties must be on opposite ends of the association
            // There better be exactly two properties! 
            Debug.Assert(cSideSlotIndexes.Count > 1, "Method called when no duplicate mapping"); 
            if (cSideSlotIndexes.Count > 2) {
                return false; 
            }

            // They better be join tree slots (if they are mapped!) and map to opposite ends
            JoinTreeSlot slot0 = (JoinTreeSlot) m_projectedSlots[cSideSlotIndexes[0]]; 
            JoinTreeSlot slot1 = (JoinTreeSlot) m_projectedSlots[cSideSlotIndexes[1]];
 
            return slot0.MemberPath.IsEquivalentViaRefConstraint(slot1.MemberPath); 
        }
 
        // 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) {
            StringBuilder builder = new StringBuilder(); 
            bool foundError = false; 

            foreach (OneOfConst oneOfConst in GetConjunctsFromOriginalWhereClause()) { 
                if (oneOfConst.Values.ContainsNotNull()) {
                    JoinTreeSlot slot = JoinTreeSlot.GetSlotForMember(m_projectedSlots, oneOfConst.Slot.MemberPath);
                    if (slot == null) {
                       // condition of NotNull and slot not being projected 
                       builder.AppendLine(System.Data.Entity.Strings.ViewGen_NotNull_No_Projected_Slot_0(
                                          oneOfConst.Slot.MemberPath.PathToString(false))); 
                       foundError = true; 
                    }
                } 
            }
            if (false == foundError) {
                return null;
            } 
            ErrorLog.Record record = new ErrorLog.Record(true, ViewGenErrorCode.NotNullNoProjectedSlot, builder.ToString(), sourceCell, String.Empty);
            return record; 
        } 

        internal void FixMissingSlotAsDefaultConstant(int slotNumber, ConstantSlot slot) 
        {
            Debug.Assert(m_projectedSlots[slotNumber] == null, "Another attempt to plug in a default value");
            m_projectedSlots[slotNumber] = slot;
        } 

        // 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, MemberPathMapBase 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++)
            {

                JoinTreeSlot slot = m_projectedSlots[i] as JoinTreeSlot; 
                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);
        } 
 
        // requires: All slots in this are null or non-constants
        // effects: Returns the non-null slots of this 
        internal AttributeSet GetNonNullSlots() {
            AttributeSet attributes = new AttributeSet(MemberPath.EqualityComparer);
            foreach (ProjectedSlot projectedSlot in m_projectedSlots) {
                // null means 'unused' slot -- we ignore those 
                if (projectedSlot != null) {
                    JoinTreeSlot projectedVar = projectedSlot as JoinTreeSlot; 
                    Debug.Assert(projectedVar != null, "Projected slot must not be a constant"); 
                    attributes.Add(projectedVar.MemberPath);
                } 
            }
            return attributes;
        }
 
        // 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 formatEntitySetMessage, 
            Func formatAssociationSetMessage, ViewGenErrorCode errorCode) {
            List prefixes = new List(1);
            // Keep track of the key corresponding to each prefix
            List keys = new List(1); 

            if (Extent is EntitySet) { 
                // For entity set just get the full path of the key properties 
                MemberPath prefix = new MemberPath(Extent, m_joinTreeRoot.MetadataWorkspace);
                prefixes.Add(prefix); 
                EntityType entityType = (EntityType)Extent.ElementType;
                List entitySetKeys = ExtentKey.GetKeysForEntityType(prefix, entityType);
                Debug.Assert(entitySetKeys.Count == 1, "Currently, we only support primary keys");
                keys.Add(entitySetKeys[0]); 

            } else { 
                AssociationSet relationshipSet = (AssociationSet)Extent; 
                // For association set, get the full path of the key
                // properties of each end 

                foreach (AssociationSetEnd relationEnd in relationshipSet.AssociationSetEnds) {
                    AssociationEndMember assocEndMember = relationEnd.CorrespondingAssociationEndMember;
                    MemberPath prefix = new MemberPath(relationshipSet, assocEndMember, m_joinTreeRoot.MetadataWorkspace); 
                    prefixes.Add(prefix);
                    List endKeys = ExtentKey.GetKeysForEntityType(prefix, 
                                                                             MetadataHelper.GetEntityTypeForEnd(assocEndMember)); 
                    Debug.Assert(endKeys.Count == 1, "Currently, we only support primary keys");
                    keys.Add(endKeys[0]); 
                }
            }

            for (int i = 0; i < prefixes.Count; i++) { 
                MemberPath prefix = prefixes[i];
                // Get all or none key slots that are being projected in this cell query 
                List keySlots = JoinTreeSlot.GetKeySlots(GetProjectedSlots(), prefix); 
                if (keySlots == null) {
                    ExtentKey key = keys[i]; 
                    string message;
                    if (Extent is EntitySet) {
                        string keyPropertiesString = MemberPath.PropertiesToUserString(key.KeyFields, true);
                        message = formatEntitySetMessage(keyPropertiesString, Extent.Name); 
                    } else {
                        string endName = prefix.FirstMember.Name; 
                        string keyPropertiesString = MemberPath.PropertiesToUserString(key.KeyFields, false); 
                        message = formatAssociationSetMessage(keyPropertiesString, endName, Extent.Name);
                    } 
                    ErrorLog.Record error = new ErrorLog.Record(true, errorCode, message, ownerCell, String.Empty);
                    return error;
                }
            } 
            return null;
        } 
 
        internal IEnumerable GetProjectedMembers()
        { 
            foreach (JoinTreeSlot slot in this.GetProjectedSlots())
            {
                yield return slot.MemberPath;
            } 
        }
 
        // effects: Returns the fields in this, i.e., not constants or null slots 
        private IEnumerable GetProjectedSlots() {
            foreach (ProjectedSlot slot in m_projectedSlots) { 
                JoinTreeSlot memberSlot = slot as JoinTreeSlot;
                if (memberSlot != null) {
                    yield return memberSlot;
                } 
            }
        } 
 
        // effects: Returns the fields that are used in the query (both projected and non-projected)
        // Output list is a copy, i.e., can be modified by the caller 
        internal List GetAllQuerySlots() {
            List slots = new List();
            m_joinTreeRoot.GatherDescendantSlots(slots);
            return slots; 
        }
 
        // effects: returns the index at which this slot appears in the projection 
        // or -1 if it is not projected
        internal int GetProjectedPosition(JoinTreeSlot slot) { 
            for (int i = 0; i < m_projectedSlots.Length; i++)
            {
                if (JoinTreeSlot.EqualityComparer.Equals(slot, m_projectedSlots[i])) {
                    return i; 
                }
            } 
            return -1; 
        }
 
        // effects: returns the List of indexes at which this member appears in the projection
        // or empty list if it is not projected
        internal List GetProjectedPositions(MemberPath member) {
            List pathIndexes = new List(); 
            for (int i = 0; i < m_projectedSlots.Length; i++) {
                JoinTreeSlot slot = m_projectedSlots[i] as JoinTreeSlot; 
                if (slot != null && MemberPath.EqualityComparer.Equals(member, slot.MemberPath)) { 
                    pathIndexes.Add(i);
                } 
            }
            return pathIndexes;
        }
 
        // effects: Determines the slot numbers for members in cellQuery
        // Returns a set of those paths in the same order as paths. If even 
        // one of the path entries is not projected in the cellquery, returns null 
        internal List GetProjectedPositions(IEnumerable paths) {
            List pathIndexes = new List(); 
            foreach (MemberPath member in paths) {
                // Get the index in checkQuery and add to pathIndexes
                List slotIndexes = GetProjectedPositions(member);
                Debug.Assert(slotIndexes != null); 
                if (slotIndexes.Count == 0) { // member is not projected
                    return null; 
                } 
                Debug.Assert(slotIndexes.Count == 1, "Expecting the path to be projected only once");
                pathIndexes.Add(slotIndexes[0]); 
            }
            return pathIndexes;
        }
 
        // effects : Return the slot numbers for members in Cell Query that
        //           represent the association end member passed in. 
        internal List GetAssociationEndSlots(AssociationEndMember endMember) { 
            List slotIndexes = new List();
            Debug.Assert(this.Extent is AssociationSet); 
            for (int i = 0; i < m_projectedSlots.Length; i++) {
                JoinTreeSlot slot = m_projectedSlots[i] as JoinTreeSlot;
                if (slot != null && slot.MemberPath.FirstMember.Equals(endMember)) {
                    slotIndexes.Add(i); 
                }
            } 
            return slotIndexes; 
        }
 
        // effects: Determines the slot numbers for members in cellQuery
        // Returns a set of those paths in the same order as paths. If even
        // one of the path entries is not projected in the cellquery, returns null
        // If a path is projected more than once, than we choose the one from the 
        // slotsToSearchFrom domain.
        internal List GetProjectedPositions(IEnumerable paths, List slotsToSearchFrom) { 
            List pathIndexes = new List(); 
            foreach (MemberPath member in paths) {
                // Get the index in checkQuery and add to pathIndexes 
                List slotIndexes = GetProjectedPositions(member);
                Debug.Assert(slotIndexes != null);
                if (slotIndexes.Count == 0) { // member is not projected
                    return null; 
                }
                int slotIndex = -1; 
                if (slotIndexes.Count > 1) { 
                    for (int i = 0; i < slotIndexes.Count; i++) {
                        if (slotsToSearchFrom.Contains(slotIndexes[i])) { 
                            Debug.Assert(slotIndex == -1, "Should be projected only once");
                            slotIndex = slotIndexes[i];
                        }
                    } 
                    if (slotIndex == -1) {
                        return null; 
                    } 
                } else {
                    slotIndex = slotIndexes[0]; 
                }
                pathIndexes.Add(slotIndex);
            }
            return pathIndexes; 
        }
 
 
        // requires: The CellConstantDomains in the OneOfConsts of the where
        // clause are partially done 
        // effects: Given the domains of different variables in domainMap,
        // fixes the whereClause of this such that all the
        // CellConstantDomains in OneOfConsts are fully done
        internal void FixCellConstantDomains(MemberDomainMap domainMap, ViewTarget viewTarget) { 
            List atoms = new List();
            foreach (BoolExpression atom in WhereClause.Atoms) { 
                BoolLiteral literal = atom.AsLiteral; 
                OneOfConst oneOfConst = literal as OneOfConst;
                Debug.Assert(oneOfConst != null, "All bool literals must be OneOfConst at this point"); 
                // The oneOfConst needs to be fixed with the new possible
                // values from the domainMap
                IEnumerable possibleValues = domainMap.GetDomain(oneOfConst.Slot.MemberPath);
                OneOfConst newOneOf = OneOfConst.CreateFullOneOfConst(oneOfConst, possibleValues); 

 
                // Prevent optimization of single constraint e.g: "300 in (300)" 
                // But we want to optimize type constants e.g: "category in (Category)"
                // To prevent optimization of bool expressions we add a Sentinel OneOF 

                OneOfScalarConst scalarConst = oneOfConst as OneOfScalarConst;
                bool addSentinel =
                    scalarConst != null && 
                    !scalarConst.Values.Contains(CellConstant.Null) &&
                    !scalarConst.Values.Contains(CellConstant.NotNull) && 
                    !scalarConst.Values.Contains(CellConstant.Undefined); 

                if (addSentinel) 
                {
                    domainMap.AddSentinel(newOneOf.Slot.MemberPath);
                }
 
                atoms.Add(BoolExpression.CreateLiteral(newOneOf, domainMap));
 
                if (addSentinel) 
                {
                    domainMap.RemoveSentinel(newOneOf.Slot.MemberPath); 
                }

            }
            // We create a new whereClause that has the memberDomainMap set 
            if (atoms.Count > 0) {
                m_whereClause = BoolExpression.CreateAnd(atoms.ToArray()); 
            } 
        }
        #endregion 

        #region BooleanExprs related Methods
        // effects: Returns a boolean expression corresponding to the
        // "varNum" boolean in this. 
        internal BoolExpression GetBoolVar(int varNum) {
            return m_boolExprs[varNum]; 
        } 

        // effects: Initalizes the booleans of this cell query to be 
        // true. Creates numBoolVars booleans and sets the cellNum boolean to true
        internal void InitializeBoolExpressions(int numBoolVars, int cellNum) {
            //Debug.Assert(m_boolExprs.Count == 0, "Overwriting existing booleans");
            m_boolExprs = new List(numBoolVars); 
            for (int i = 0; i < numBoolVars; i++) {
                m_boolExprs.Add(null); 
            } 
            Debug.Assert(cellNum < numBoolVars, "Trying to set boolean with too high an index");
            m_boolExprs[cellNum] = BoolExpression.True; 
        }

        // requires: query1 has the same number of boolean expressions as
        // query2. There should be no  index i for which query1's bools[i] != 
        // null and query2's bools[i] != null
        // effects: Given two cellqueries query1 and query2, merges their 
        // boolean expressions while ANDING query1 bools with conjunct1 and 
        // query2's bools with conjunct2 and returns the result
        static private List 
        MergeBoolExpressions(CellQuery query1, CellQuery query2,
                             BoolExpression conjunct1, BoolExpression conjunct2, CellTreeOpType opType, bool canBooleansOverlap) {

            List bools1 = query1.m_boolExprs; 
            List bools2 = query2.m_boolExprs;
 
            // Add conjuncts to both sets if needed 
            if (false == conjunct1.IsTrue) {
                bools1 = BoolExpression.AddConjunctionToBools(bools1, conjunct1); 
            }

            if (false == conjunct2.IsTrue) {
                bools2 = BoolExpression.AddConjunctionToBools(bools2, conjunct2); 
            }
 
            // Perform merge 
            Debug.Assert(bools1.Count == bools2.Count);
            List bools = new List(); 
            // Both bools1[i] and bools2[i] be null for some of the i's. When
            // we merge two (leaf) cells (say), only one boolean each is set
            // in it; the rest are all nulls. If the SP/TM rules have been
            // applied, more than one boolean may be non-null in a cell query 
            for (int i = 0; i < bools1.Count; i++) {
                BoolExpression merged = null; 
                if (bools1[i] == null) 
                {
                    merged = bools2[i]; 
                } else if (bools2[i] == null) {
                    merged = bools1[i];
                } else {
                    // This this ok for validation -- actually, we could even ignore 
                    // the booleans for validation
                    Debug.Assert(canBooleansOverlap, "Possibly incompatible booleans: " + bools1[i] + ", " + bools2[i]); 
                    if (opType == CellTreeOpType.IJ) { 
                        merged = BoolExpression.CreateAnd(bools1[i], bools2[i]);
                    } else if (opType == CellTreeOpType.Union) { 
                        merged = BoolExpression.CreateOr(bools1[i], bools2[i]);
                    } else if (opType == CellTreeOpType.LASJ) {
                        merged = BoolExpression.CreateAnd(bools1[i],
                                                          BoolExpression.CreateNot(bools2[i])); 
                    } else {
                        Debug.Fail("No other operation expected for boolean merge"); 
                    } 
                }
                if (merged != null) 
                {
                    merged.ExpensiveSimplify();
                }
                bools.Add(merged); 
            }
            return bools; 
        } 
        #endregion
        #region WhereClause related methods 
        // requires: The current whereClause corresponds to "True", "OneOfConst" or "
        // "OneOfConst AND ... AND OneOfConst"
        // effects: Yields all the conjuncts (OneOfConsts) in this (i.e., if the whereClause is
        // just True, yields nothing 
        internal IEnumerable GetConjunctsFromWhereClause() {
            return GetConjunctsFromWhereClause(m_whereClause); 
        } 

        // requires: The current originalWhereClause corresponds to "True", "OneOfConst" or " 
        // "OneOfConst AND ... AND OneOfConst"
        // effects: Yields all the conjuncts (OneOfConsts) in this (i.e., if the originalWhereClause is
        // just True, yields nothing
        // reason: we need this method simply because the WhereClause will be manipulated by the ViewGen 
        internal IEnumerable GetConjunctsFromOriginalWhereClause()
        { 
            return GetConjunctsFromWhereClause(m_originalWhereClause); 
        }
 
        private IEnumerable GetConjunctsFromWhereClause(BoolExpression whereClause)
        {
            foreach (BoolExpression boolExpr in whereClause.Atoms)
            { 
                if (boolExpr.IsTrue)
                { 
                    continue; 
                }
                OneOfConst result = boolExpr.AsLiteral as OneOfConst; 
                Debug.Assert(result != null, "Atom must be OneOfConst");
                yield return result;
            }
        } 

        // requires: whereClause is of the form specified in GetConjunctsFromWhereClause 
        // effects: Converts the whereclause to a user-readable string 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        internal void WhereClauseToUserString(StringBuilder builder, MetadataWorkspace workspace) { 
            bool isFirst = true;
            foreach (OneOfConst oneOfConst in GetConjunctsFromWhereClause()) {
                if (isFirst == false) {
                    builder.Append(System.Data.Entity.Strings.ViewGen_AND); 
                }
                oneOfConst.ToUserString(false, builder, workspace); 
            } 
        }
        #endregion 

        #region Full CellQuery methods
        // effects: Determines all the identifiers used in this and adds them to identifiers
        internal void GetIdentifiers(CqlIdentifiers identifiers) { 
            foreach (ProjectedSlot projectedSlot in m_projectedSlots) {
                JoinTreeSlot slot = projectedSlot as JoinTreeSlot; 
                if (slot != null) { 
                    slot.MemberPath.GetIdentifiers(identifiers);
                } 
            }
            m_joinTreeRoot.GetIdentifiers(identifiers);
        }
 
        internal void CreateBasicCellRelation(ViewCellRelation viewCellRelation) {
            List slots = GetAllQuerySlots(); 
            // Create a base cell relation that has all the scalar slots of this 
            m_basicCellRelation = new BasicCellRelation(this, viewCellRelation, slots);
        } 

        // effects: Merges query2 with this according to the TM/SP rules for opType and
        // returns the merged result. canBooleansOverlap indicates whether the bools in this and query2 can overlap, i.e.
        // the same cells may have contributed to query2 and this earlier in the merge process 
        internal bool TryMerge(CellQuery query2, CellTreeOpType opType, bool canBooleansOverlap,
                               MemberDomainMap memberDomainMap, out CellQuery mergedQuery) { 
 
            mergedQuery = null;
            // Initialize g1 and g2 according to the TM/SP rules for IJ, LOJ, Union, FOJ cases 
            BoolExpression g1 = null;
            BoolExpression g2 = null;
            switch (opType) {
                case CellTreeOpType.IJ: 
                    break;
                case CellTreeOpType.LOJ: 
                case CellTreeOpType.LASJ: 
                    g2 = BoolExpression.True;
                    break; 
                case CellTreeOpType.FOJ:
                case CellTreeOpType.Union:
                    g1 = BoolExpression.True;
                    g2 = BoolExpression.True; 
                    break;
                default: 
                    Debug.Fail("Unsupported operator"); 
                    break;
            } 

            Dictionary remap =
                new Dictionary(EqualityComparer.Default);
            CellQuery query1 = this; 

            // Merge the two trees and get the new root 
            ExtentJoinTreeNode newRoot = (ExtentJoinTreeNode)query1.m_joinTreeRoot.TryMergeNode(query2.m_joinTreeRoot, 
                                                                                                 opType, ref g1, ref g2, remap, memberDomainMap);
            if (newRoot == null) { // could not merge 
                return false;
            }

            // Conjuncts for ANDing with the previous whereClauses 
            BoolExpression conjunct1 = BoolExpression.True;
            BoolExpression conjunct2 = BoolExpression.True; 
            BoolExpression whereClause = null; 

            switch (opType) { 
                case CellTreeOpType.IJ:
                    // Project[D1, D2, A, B, C] Select[cond1 and cond2] (T)
                    // We simply merge the two lists of booleans -- no conjuct is added
                    // conjunct1 and conjunct2 don't change 

                    // query1.WhereCaluse AND query2.WhereCaluse 
                    Debug.Assert(g1 == null && g2 == null, "IJ does not affect g1 and g2"); 
                    whereClause = BoolExpression.CreateAnd(query1.m_whereClause, query2.m_whereClause);
                    break; 

                case CellTreeOpType.LOJ:
                    // conjunct1 does not change since D1 remains as is
                    // Project[D1, (expr2 and cond2 and G2) as D2, A, B, C] Select[cond1] (T) 
                    // D1 does not change. New d2 is the list of booleans expressions
                    // for query2 ANDed with g2 AND query2.WhereClause 
                    Debug.Assert(g1 == null, "LOJ does not affect g1"); 
                    conjunct2 = BoolExpression.CreateAnd(query2.m_whereClause, g2);
                    // Just query1's whereclause 
                    whereClause = query1.m_whereClause;
                    break;

                case CellTreeOpType.FOJ: 
                case CellTreeOpType.Union:
                    // Project[(expr1 and cond1 and G1) as D1, (expr2 and cond2 and G2) as D2, A, B, C] Select[cond1] (T) 
                    // New D1 is a list -- newD1 = D1 AND query1.WhereClause AND g1 
                    // New D1 is a list -- newD2 = D2 AND query2.WhereClause AND g2
                    conjunct1 = BoolExpression.CreateAnd(query1.m_whereClause, g1); 
                    conjunct2 = BoolExpression.CreateAnd(query2.m_whereClause, g2);

                    // The new whereClause -- g1 AND query1.WhereCaluse OR g2 AND query2.WhereClause
                    whereClause = BoolExpression.CreateOr(BoolExpression.CreateAnd(query1.m_whereClause, g1), 
                                                          BoolExpression.CreateAnd(query2.m_whereClause, g2));
                    break; 
 
                case CellTreeOpType.LASJ:
                    // conjunct1 does not change since D1 remains as is 
                    // Project[D1, (expr2 and cond2 and G2) as D2, A, B, C] Select[cond1] (T)
                    // D1 does not change. New d2 is the list of booleans expressions
                    // for query2 ANDed with g2 AND NOT query2.WhereClause
                    Debug.Assert(g1 == null, "LASJ does not affect g1"); 
                    conjunct2 = BoolExpression.CreateAnd(query2.m_whereClause, g2);
                    whereClause = BoolExpression.CreateAnd(query1.m_whereClause, BoolExpression.CreateNot(conjunct2)); 
                    break; 
                default:
                    Debug.Fail("Unsupported operator"); 
                    break;
            }

            // Create the various remapped parts for the cell query -- 
            // boolean expressions, merged slots, whereclause, duplicate
            // elimination, join tree 
            List boolExprs = 
                MergeBoolExpressions(query1, query2, conjunct1, conjunct2, opType, canBooleansOverlap);
            BoolExpression.RemapBools(boolExprs, remap); 

            ProjectedSlot[] mergedSlots;
            if (false == ProjectedSlot.TryMergeRemapSlots(query1.m_projectedSlots, query2.m_projectedSlots, remap,
                        out mergedSlots)) { 
                // merging failed because two different right slots go to same left slot
                return false; 
            } 

            whereClause = whereClause.RemapBool(remap); 
            // Needed for nested compositions
            DuplicateElimination elimDupl = MergeDupl(query1.m_eliminateDuplicates, query2.m_eliminateDuplicates);

            Debug.Assert(query1.m_schemaContext == query2.m_schemaContext, 
                         "Merging queries with different schema?");
            whereClause.ExpensiveSimplify(); 
            mergedQuery = new CellQuery(query1.m_schemaContext, mergedSlots, whereClause, 
                                                  boolExprs, elimDupl, newRoot);
            return true; 
        }

        // effects: Given two duplicate eliination choices, returns an OR of them
        static private DuplicateElimination MergeDupl(DuplicateElimination d1, DuplicateElimination d2) { 
            if (d1 == DuplicateElimination.Yes || d2 == DuplicateElimination.Yes) {
                return DuplicateElimination.Yes; 
            } else { 
                return DuplicateElimination.No;
            } 
        }

        // effects: Validates the internal representation of this query
        internal override bool CheckRepInvariant() { 
            foreach (BoolExpression boolExpr in m_boolExprs) {
                boolExpr.CheckRepInvariant(); 
            } 
            m_whereClause.CheckRepInvariant();
 
            m_joinTreeRoot.CheckRepInvariant();
            return true;
        }
 
        // effects: Given a query that is the counterpart of this in a cell,
        // validates the contents of this against other 
        internal bool CheckRepInvariant(CellQuery other) { 
            // Validate the queries internally
            CheckRepInvariant(); 
            other.CheckRepInvariant();

            // Make sure that these queries have the same number of slots and
            // each corresponding slot is of the same type 
            ExceptionHelpers.CheckAndThrowResArgs(m_projectedSlots.Length == other.m_projectedSlots.Length,
                                                  Strings.ViewGen_SlotNumber_Mismatch_1, this, other); 
            return true; 
        }
 
        #endregion

        #region String Methods
        // effects: Modifies stringBuilder to contain a string representation 
        // of the cell query in terms of the original cells that are being used
        internal override void ToCompactString(StringBuilder stringBuilder) { 
            // This could be a simplified view where a number of cells 
            // got merged or it could be one of the original booleans. So
            // determine their numbers using the booleans in m_cellWrapper 
            List boolExprs = m_boolExprs;
            int i = 0;
            bool first = true;
            foreach (BoolExpression boolExpr in boolExprs) { 
                if (boolExpr != null) {
                    if (false == first) { 
                        stringBuilder.Append(","); 
                    } else {
                        stringBuilder.Append("["); 
                    }
                    StringUtil.FormatStringBuilder(stringBuilder, "C{0}", i);
                    first = false;
                } 
                i++;
            } 
            if (true == first) { 
                // No booleans, i.e., no compact representation. Use full string to avoid empty output
                ToFullString(stringBuilder); 
            } else {
                stringBuilder.Append("]");
            }
        } 

        internal override void ToFullString(StringBuilder builder) { 
            builder.Append("SELECT "); 
            StringUtil.ToSeparatedString(builder, m_projectedSlots, ", ", "_");
 
            if (m_boolExprs.Count > 0) {
                builder.Append(", Bool[");
                StringUtil.ToSeparatedString(builder, m_boolExprs, ", ", "_");
                builder.Append("]"); 
            }
 
            builder.Append(" FROM "); 
            m_joinTreeRoot.ToFullString(builder);
 
            if (false == m_whereClause.IsTrue) {
                builder.Append(" WHERE ");
                m_whereClause.ToFullString(builder);
            } 
        }
 
        public override string ToString() { 
            return ToFullString();
        } 

        // eSQL representation of cell query
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        internal string ToESqlString() 
        {
            StringBuilder builder = new StringBuilder(); 
            builder.Append("\n\tSELECT "); 

            foreach (ProjectedSlot ps in m_projectedSlots) 
            {
                JoinTreeSlot jtn = ps as JoinTreeSlot;
                StructuralType st = jtn.MemberPath.LastMember.DeclaringType;
                StringBuilder sb = new StringBuilder(); 
                jtn.MemberPath.AsCql(sb, "e");
                builder.AppendFormat("{0}, ", sb.ToString()); 
            } 
            //remove the extra-comma after the last slot
            builder.Remove(builder.Length - 2, 2); 

            builder.Append("\n\tFROM ");
            EntitySetBase extent = m_joinTreeRoot.Extent;
            CqlWriter.AppendEscapedQualifiedName(builder, extent.EntityContainer.Name, extent.Name); 
            builder.Append(" AS e");
 
            if (m_whereClause.IsTrue == false) 
            {
                builder.Append("\n\tWHERE "); 

                StringBuilder qbuilder = new StringBuilder();
                m_whereClause.AsCql(qbuilder, "e");
 
                builder.Append(qbuilder.ToString());
            } 
            builder.Append("\n    "); 

            return builder.ToString(); 
        }

        #endregion
 
    }
 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK