OpCellTreeNode.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataEntity / System / Data / Map / ViewGeneration / Structures / OpCellTreeNode.cs / 1305376 / OpCellTreeNode.cs

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

using System.Data.Common.Utils; 
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Mapping.ViewGeneration.CqlGeneration;
using System.Data.Mapping.ViewGeneration.QueryRewriting; 
using System.Data.Mapping.ViewGeneration.Utils;
using System.Text; 
using System.Linq; 
using System.Diagnostics;
using System.Data.Metadata.Edm; 
using System.Data.Entity;
using System.Data.Mapping.Update.Internal;

namespace System.Data.Mapping.ViewGeneration.Structures 
{
 
    using AttributeSet = Set; 

    // This class represents th intermediate nodes in the tree (non-leaf nodes) 
    internal class OpCellTreeNode : CellTreeNode
    {

        #region Constructors 
        // effects: Creates a node with operation opType and no children
        internal OpCellTreeNode(ViewgenContext context, CellTreeOpType opType) 
            : base(context) 
        {
            m_opType = opType; 
            m_attrs = new AttributeSet(MemberPath.EqualityComparer);
            m_children = new List();
        }
 
        internal OpCellTreeNode(ViewgenContext context, CellTreeOpType opType, params CellTreeNode[] children)
            : this(context, opType, (IEnumerable)children) { } 
 
        // effects: Given a sequence of children node and the opType, creates
        // an OpCellTreeNode and returns it 
        internal OpCellTreeNode(ViewgenContext context, CellTreeOpType opType, IEnumerable children)
            : this(context, opType)
        {
            // Add the children one by one so that we can get the attrs etc fixed 
            foreach (CellTreeNode child in children)
            { 
                Add(child); 
            }
        } 

        #endregion

        #region Fields 
        private Set m_attrs; // attributes from whole subtree below
        private List m_children; 
        private CellTreeOpType m_opType; 
        private FragmentQuery m_leftFragmentQuery;
        private FragmentQuery m_rightFragmentQuery; 
        #endregion

        #region Properties
        // effects: See CellTreeNode.OpType 
        internal override CellTreeOpType OpType { get { return m_opType; } }
 
        // Lazily create FragmentQuery when required 
        internal override FragmentQuery LeftFragmentQuery
        { 
            get
            {
                if (m_leftFragmentQuery == null)
                { 
                    m_leftFragmentQuery = GenerateFragmentQuery(Children, true /*isLeft*/, ViewgenContext, OpType);
                } 
                return m_leftFragmentQuery; 
            }
        } 

        internal override FragmentQuery RightFragmentQuery
        {
            get 
            {
                if (m_rightFragmentQuery == null) 
                { 
                    m_rightFragmentQuery = GenerateFragmentQuery(Children, false /*isLeft*/, ViewgenContext, OpType);
                } 
                return m_rightFragmentQuery;
            }
        }
 
        // effects: See CellTreeNode.RightDomainMap
        internal override MemberDomainMap RightDomainMap 
        { 
            get
            { 
                // Get the information from one of the children
                Debug.Assert(m_children[0].RightDomainMap != null, "EdmMember domain map missing");
                return m_children[0].RightDomainMap;
            } 
        }
 
        // effects: See CellTreeNode.Attributes 
        internal override Set Attributes { get { return m_attrs; } }
 
        // effects: See CellTreeNode.Children
        internal override List Children { get { return m_children; } }

        internal override int NumProjectedSlots 
        {
            get 
            { 
                // All children have the same number of slots
                Debug.Assert(m_children.Count > 1, "No children for op node?"); 
                return m_children[0].NumProjectedSlots;
            }
        }
 
        internal override int NumBoolSlots
        { 
            get 
            {
                Debug.Assert(m_children.Count > 1, "No children for op node?"); 
                return m_children[0].NumBoolSlots;
            }
        }
        #endregion 

        #region Methods 
        internal override TOutput Accept(SimpleCellTreeVisitor visitor, TInput param) 
        {
            return visitor.VisitOpNode(this, param); 
        }

        internal override TOutput Accept(CellTreeVisitor visitor, TInput param)
        { 
            switch (OpType)
            { 
                case CellTreeOpType.IJ: 
                    return visitor.VisitInnerJoin(this, param);
                case CellTreeOpType.LOJ: 
                    return visitor.VisitLeftOuterJoin(this, param);
                case CellTreeOpType.Union:
                    return visitor.VisitUnion(this, param);
                case CellTreeOpType.FOJ: 
                    return visitor.VisitFullOuterJoin(this, param);
                case CellTreeOpType.LASJ: 
                    return visitor.VisitLeftAntiSemiJoin(this, param); 
                default:
                    Debug.Fail("Unexpected optype: " + OpType); 
                    // To satsfy the compiler
                    return visitor.VisitInnerJoin(this, param);
            }
        } 

        // effects: Add child to the end of the current children list 
        // while ensuring the constants and attributes of the child are 
        // propagated into this (i.e., unioned)
        internal void Add(CellTreeNode child) 
        {
            Insert(m_children.Count, child);
        }
 
        // effects: Add child at the beginning of the current children list
        // while ensuring the constants and attributes of the child are 
        // propagated into this (i.e., unioned) 
        internal void AddFirst(CellTreeNode child)
        { 
            Insert(0, child);
        }

        // effects: Inserts child at "index" while ensuring the constants 
        // and attributes of the child are propagated into this
        private void Insert(int index, CellTreeNode child) 
        { 
            m_attrs.Unite(child.Attributes);
            m_children.Insert(index, child); 
            // reset fragmentQuery so it's recomputed when property FragmentQuery is accessed
            m_leftFragmentQuery = null;
            m_rightFragmentQuery = null;
        } 

        // effects: Given the required slots by the parent, 
        // generates a CqlBlock tree for the tree rooted below node 
        internal override CqlBlock ToCqlBlock(bool[] requiredSlots, CqlIdentifiers identifiers, ref int blockAliasNum,
            ref List withStatements) 
        {
            // Dispatch depending on whether we have a union node or join node
            CqlBlock result;
            if (OpType == CellTreeOpType.Union) 
            {
                result = UnionToCqlBlock(requiredSlots, identifiers, ref blockAliasNum, ref withStatements); 
            } 
            else
            { 
                result = JoinToCqlBlock(requiredSlots, identifiers, ref blockAliasNum, ref withStatements);
            }
            return result;
        } 

        internal override bool IsProjectedSlot(int slot) 
        { 
            // If any childtree projects it, return true
            foreach (CellTreeNode childNode in Children) 
            {
                if (childNode.IsProjectedSlot(slot))
                {
                    return true; 
                }
            } 
            return false; 
        }
        #endregion 

        #region Union CqlBLock Methods
        // requires: node corresponds to a Union node
        // effects: Given a union node and the slots required by the parent, 
        // generates a CqlBlock for the subtree rooted at node
        private CqlBlock UnionToCqlBlock(bool[] requiredSlots, CqlIdentifiers identifiers, ref int blockAliasNum, ref List withStatements) 
        { 
            Debug.Assert(OpType == CellTreeOpType.Union);
 
            List children = new List();
            List aliasedSlotsForAddedByChildren = new List();

            int totalSlots = requiredSlots.Length; 
            foreach (CellTreeNode child in Children)
            { 
                // Unlike Join, we pass the requiredSlots from the parent as the requirement 
                // CqlBlock childBlock = child.ToCqlBlock(requiredSlots, identifiers, ref blockAliasNum);
                bool[] childProjectedSlots = child.GetProjectedSlots(); 
                AndWith(childProjectedSlots, requiredSlots);
                CqlBlock childBlock = child.ToCqlBlock(childProjectedSlots, identifiers, ref blockAliasNum, ref withStatements);
                for (int aliasedSlotNumber = childProjectedSlots.Length; aliasedSlotNumber < childBlock.Slots.Count; aliasedSlotNumber++)
                { 
                    SlotInfo slotInfo = childBlock.Slots[aliasedSlotNumber];
                    aliasedSlotsForAddedByChildren.Add(new AliasedSlot(childBlock, slotInfo.SlotValue, slotInfo.MemberPath, aliasedSlotNumber)); 
                } 

 
                // if required, but not projected, add NULL
                SlotInfo[] paddedSlotInfo = new SlotInfo[childBlock.Slots.Count];
                ReadOnlyCollection childSlotInfo = childBlock.Slots;
                for (int slotNum = 0; slotNum < totalSlots; slotNum++) 
                {
                    if (requiredSlots[slotNum] && !childProjectedSlots[slotNum]) 
                    { 
                        if (IsBoolSlot(slotNum))
                        { 
                            paddedSlotInfo[slotNum] = new SlotInfo(true /* is required */, true /* is projected */,
                                                            new BooleanProjectedSlot(BoolExpression.False, identifiers, SlotToBoolIndex(slotNum)), null /* member path*/);
                        }
                        else 
                        {
                            // NULL as projected slot 
                            paddedSlotInfo[slotNum] = new SlotInfo(true /* is required */, true /* is projected */, 
                                                             new ConstantProjectedSlot(Constant.Null), childSlotInfo[slotNum].MemberPath);
                        } 
                    }
                    else
                    {
                        paddedSlotInfo[slotNum] = childSlotInfo[slotNum]; 
                    }
                } 
                //Add the slots that were added by children 
                //for (int slotNum = totalSlots; slotNum < childBlock.Slots.Count; slotNum++)
                //{ 
                //    paddedSlotInfo[slotNum] = childSlotInfo[slotNum];
                //}
                childBlock.Slots = new ReadOnlyCollection(paddedSlotInfo);
                children.Add(childBlock); 
                Debug.Assert(totalSlots == child.NumBoolSlots + child.NumProjectedSlots,
                    "Number of required slots is different from what each node in the tree has?"); 
            } 

            //We need to add the slots added by each child unformly for others( as nulls) 
            //since this is a union operation
            if (aliasedSlotsForAddedByChildren.Count != 0)
            {
                foreach (CqlBlock childBlock in children) 
                {
                    SlotInfo[] childSlots = new SlotInfo[totalSlots + aliasedSlotsForAddedByChildren.Count]; 
                    childBlock.Slots.CopyTo(childSlots, 0); 
                    int index = totalSlots;
                    foreach (AliasedSlot aliasedSlot in aliasedSlotsForAddedByChildren) 
                    {
                        if (aliasedSlot.Block.Equals(childBlock))
                        {
                            childSlots[index] = new SlotInfo(true /* is required */, true /* is projected */, 
                                 aliasedSlot.InnerSlot, aliasedSlot.MemberPath);
                        } 
                        else 
                        {
                            childSlots[index] = new SlotInfo(true /* is required */, true /* is projected */, 
                                 new ConstantProjectedSlot(Constant.Null), aliasedSlot.MemberPath);

                        }
                        //move on to the next slot added by children. 
                        index++;
                    } 
                    childBlock.Slots = new ReadOnlyCollection(childSlots); 
                }
            } 

            // Create the slotInfos and then Union CqlBlock
            SlotInfo[] slotInfos = new SlotInfo[totalSlots + aliasedSlotsForAddedByChildren.Count];
            // We pick the slot references from the first child, just as convention 
            // In a union, values come from both sides
            CqlBlock firstChild = children[0]; 
 
            for (int slotNum = 0; slotNum < totalSlots; slotNum++)
            { 
                ProjectedSlot slot = firstChild.ProjectedSlot(slotNum);
                MemberPath memberPath = GetMemberPath(slotNum);
                // A required slot is somehow projected by a child in Union
                // -- so set isProjected to be the same as isRequired 
                bool isRequired = requiredSlots[slotNum];
                SlotInfo slotInfo = new SlotInfo(isRequired, isRequired, slot, memberPath); 
                slotInfos[slotNum] = slotInfo; 
            }
 
            for (int i = 0, slotNum = totalSlots; slotNum < totalSlots + aliasedSlotsForAddedByChildren.Count; slotNum++, i++)
            {
                slotInfos[slotNum] = new SlotInfo(true, true, aliasedSlotsForAddedByChildren[i], aliasedSlotsForAddedByChildren[i].MemberPath);
            } 

 
            CqlBlock block = new UnionCqlBlock(slotInfos, children, identifiers, ++blockAliasNum); 
            return block;
        } 
        private static void AndWith(bool[] boolArray, bool[] another)
        {
            Debug.Assert(boolArray.Length == another.Length);
            for (int i = 0; i < boolArray.Length; i++) 
            {
                boolArray[i] &= another[i]; 
            } 
        }
        #endregion 

        #region Join CqlBLock Methods
        // requires: node corresponds to an IJ, LOJ, FOJ node
        // effects: Given a union node and the slots required by the parent, 
        // generates a CqlBlock for the subtree rooted at node
        private CqlBlock JoinToCqlBlock(bool[] requiredSlots, CqlIdentifiers identifiers, ref int blockAliasNum, ref List withStatements) 
        { 
            int totalSlots = requiredSlots.Length;
 
            Debug.Assert(OpType == CellTreeOpType.IJ ||
                         OpType == CellTreeOpType.LOJ ||
                         OpType == CellTreeOpType.FOJ, "Only these join operations handled");
 
            List children = new List();
            List aliasedSlotsForAddedByChildren = new List(); 
 
            // First get the children nodes (FROM part)
            foreach (CellTreeNode child in Children) 
            {
                // Determine the slots that are projected by this child.
                // These are the required slots as well - unlike Union, we do
                // not need the child to project any extra nulls 
                bool[] childProjectedSlots = child.GetProjectedSlots();
                AndWith(childProjectedSlots, requiredSlots); 
                //List subAddedSlotsByChild = new List(); 
                CqlBlock childBlock = child.ToCqlBlock(childProjectedSlots, identifiers, ref blockAliasNum, ref withStatements);
                //addedSlotsByChild.AddRange(subAddedSlotsByChild); 
                children.Add(childBlock);
                //foreach (SlotInfo slotInfo in subAddedSlotsByChild)
                for (int aliasedSlotNumber = childProjectedSlots.Length; aliasedSlotNumber < childBlock.Slots.Count; aliasedSlotNumber++)
                { 
                    SlotInfo slotInfo = childBlock.Slots[aliasedSlotNumber];
                    aliasedSlotsForAddedByChildren.Add(new AliasedSlot(childBlock, slotInfo.SlotValue, slotInfo.MemberPath, aliasedSlotNumber)); 
                } 
                Debug.Assert(totalSlots == child.NumBoolSlots + child.NumProjectedSlots,
                    "Number of required slots is different from what each node in the tree has?"); 
            }

            // Now get the slots that are projected out by this node (SELECT part)
            SlotInfo[] slotInfos = new SlotInfo[totalSlots + aliasedSlotsForAddedByChildren.Count]; 
            for (int slotNum = 0; slotNum < totalSlots; slotNum++)
            { 
                // Note: this call could create a CaseStatementSlot (i.e., slotInfo.SlotValue is CaseStatementSlot) 
                // which uses "from" booleans that need to be projected by children
                SlotInfo slotInfo = GetJoinSlotInfo(OpType, requiredSlots[slotNum], children, slotNum, identifiers); 
                slotInfos[slotNum] = slotInfo;
            }

 
            for (int i = 0, slotNum = totalSlots; slotNum < totalSlots + aliasedSlotsForAddedByChildren.Count; slotNum++, i++)
            { 
                slotInfos[slotNum] = new SlotInfo(true, true, aliasedSlotsForAddedByChildren[i], aliasedSlotsForAddedByChildren[i].MemberPath); 
            }
 
            // Generate the ON conditions: For each child, generate an ON
            // clause with the 0th child on the key fields
            List onClauses = new List();
 
            for (int i = 1; i < children.Count; i++)
            { 
                CqlBlock child = children[i]; 
                JoinCqlBlock.OnClause onClause = new JoinCqlBlock.OnClause();
                foreach (int keySlotNum in KeySlots) 
                {
                    SlotInfo slotInfo = slotInfos[keySlotNum];

                    if (ViewgenContext.Config.IsValidationEnabled) 
                    {
                        Debug.Assert(child.IsProjected(keySlotNum), "Key is not in child"); 
                        Debug.Assert(children[0].IsProjected(keySlotNum), "Key is not in 0th child"); 
                    }
                    else 
                    {
                        if (!child.IsProjected(keySlotNum) || !children[0].IsProjected(keySlotNum))
                        {
                            ErrorLog errorLog = new ErrorLog(); 
                            errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.NoJoinKeyOrFKProvidedInMapping,
                                    Strings.Viewgen_NoJoinKeyOrFK, ViewgenContext.AllWrappersForExtent, String.Empty)); 
                            ExceptionHelpers.ThrowMappingException(errorLog, ViewgenContext.Config); 
                        }
                    } 
                    AliasedSlot first = new AliasedSlot(children[0], slotInfo.SlotValue,
                                                        slotInfo.MemberPath, keySlotNum);
                    AliasedSlot second = new AliasedSlot(child, slotInfo.SlotValue, slotInfo.MemberPath,
                                                         keySlotNum); 
                    onClause.Add(first, second);
                } 
                onClauses.Add(onClause); 
            }
 
            CqlBlock result = new JoinCqlBlock(OpType, slotInfos, children, onClauses, identifiers, ++blockAliasNum);
            return result;
        }
 
        // effects: Generates a SlotInfo object for a slot of a join node. It
        // uses the type of the join operation (opType), whether the slot is 
        // required by the parent or not (isRequiredSlot), the children of 
        // this node (children) and the number of the slotNum
        private SlotInfo GetJoinSlotInfo(CellTreeOpType opType, bool isRequiredSlot, 
                                         List children, int slotNum, CqlIdentifiers identifiers)
        {
            if (false == isRequiredSlot)
            { 
                // The slot will not be used. So we can set the projected slot to be null
                SlotInfo unrequiredSlotInfo = new SlotInfo(false, false, null, GetMemberPath(slotNum)); 
                return unrequiredSlotInfo; 
            }
 
            // For a required slot, determine the child who is contributing to this value
            int childDefiningSlot = -1;
            CaseStatement caseForOuterJoins = null;
 
            for (int childNum = 0; childNum < children.Count; childNum++)
            { 
                CqlBlock child = children[childNum]; 
                if (false == child.IsProjected(slotNum))
                { 
                    continue;
                }
                // For keys, we can pick any child block. So the first
                // one that we find is fine as well 
                if (IsKeySlot(slotNum))
                { 
                    childDefiningSlot = childNum; 
                    break;
                } 
                else if (opType == CellTreeOpType.IJ)
                {
                    // For Inner Joins, most of the time, the entries will be
                    // the same in all the children. However, in some cases, 
                    // we will end up with NULL in one child and an actual
                    // value in another -- we should pick up the actual value in that case 
                    childDefiningSlot = GetInnerJoinChildForSlot(children, slotNum); 
                    break;
                } 
                else
                {
                    // For LOJs, we generate a case statement if more than
                    // one child generates the value - until then we do not 
                    // create the caseForOuterJoins object
                    if (childDefiningSlot != -1) 
                    { 
                        // We really need a case statement now
                        // We have the value being generated by another child 
                        // We need to fetch the variable from the appropriate child
                        Debug.Assert(false == IsBoolSlot(slotNum), "Boolean slots cannot come from two children");
                        if (caseForOuterJoins == null)
                        { 
                            MemberPath outputMember = GetMemberPath(slotNum);
                            caseForOuterJoins = new CaseStatement(outputMember); 
                            // Add the child that we had not added in the first shot 
                            AddCaseForOuterJoins(caseForOuterJoins, children[childDefiningSlot], slotNum, identifiers);
                        } 
                        AddCaseForOuterJoins(caseForOuterJoins, child, slotNum, identifiers);
                    }
                    childDefiningSlot = childNum;
                } 
            }
 
            MemberPath memberPath = GetMemberPath(slotNum); 
            ProjectedSlot slot = null;
 
            // Generate the slot value -- case statement slot, or an aliased slot
            // or null or false. If case statement slot has nothing, treat it as null/empty
            if (caseForOuterJoins != null && (caseForOuterJoins.Clauses.Count > 0 || caseForOuterJoins.ElseValue != null))
            { 
                caseForOuterJoins.Simplify();
                slot = new CaseStatementProjectedSlot(caseForOuterJoins, null); 
            } 
            else if (childDefiningSlot >= 0)
            { 
                slot = new AliasedSlot(children[childDefiningSlot],
                                       children[childDefiningSlot].ProjectedSlot(slotNum), memberPath, slotNum);
            }
            else 
            {
                // need to produce output slot, but don't have a value 
                // output NULL for fields or False for bools 
                if (IsBoolSlot(slotNum))
                { 
                    slot = new BooleanProjectedSlot(BoolExpression.False, identifiers, SlotToBoolIndex(slotNum));
                }
                else
                { 
                    slot = new ConstantProjectedSlot(Domain.GetDefaultValueForMemberPath(memberPath, GetLeaves(), ViewgenContext.Config));
                } 
            } 

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

        // requires: children to be a list of nodes that are children of an
        // Inner Join node. slotNum does not correspond to the key slot 
        // effects: Determines the child number from which the slot should be
        // picked up. 
        private static int GetInnerJoinChildForSlot(List children, int slotNum) 
        {
            // Picks the child with the non-constant slot first. If none, picks a non-null constant slot. 
            // If not een that, picks any one
            int result = -1;
            for (int i = 0; i < children.Count; i++)
            { 
                CqlBlock child = children[i];
                if (false == child.IsProjected(slotNum)) 
                { 
                    continue;
                } 
                ProjectedSlot slot = child.ProjectedSlot(slotNum);
                ConstantProjectedSlot constantSlot = slot as ConstantProjectedSlot;
                MemberProjectedSlot joinSlot = slot as MemberProjectedSlot;
                if (joinSlot != null) 
                { // Pick the non-constant slot
                    result = i; 
                } 
                else if (constantSlot != null && constantSlot.CellConstant.IsNull())
                { 
                    if (result == -1)
                    { // In case, all are null
                        result = i;
                    } 
                }
                else 
                { 
                    // Just pick anything
                    result = i; 
                }
            }
            return result;
        } 

        // requires: caseForOuterJoins corresponds the slot "slotNum" 
        // effects: Adds a WhenThen corresponding to child to caseForOuterJoins. 
        private void AddCaseForOuterJoins(CaseStatement caseForOuterJoins, CqlBlock child, int slotNum, CqlIdentifiers identifiers)
        { 
            // Determine the cells that the slot comes from
            // and make an OR expression, e.g., WHEN _from0 or _from2 or ... THEN child[slotNum]

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

            BoolExpression originBool = BoolExpression.False;
            for (int i = 0; i < NumBoolSlots; i++) 
            {
                int boolSlotNum = BoolIndexToSlot(i); 
                if (child.IsProjected(boolSlotNum)) 
                {
                    // OR it to the expression 
                    QualifiedCellIdBoolean boolExpr = new QualifiedCellIdBoolean(child, identifiers, i);
                    originBool = BoolExpression.CreateOr(originBool, BoolExpression.CreateLiteral(boolExpr, RightDomainMap));
                }
            } 
            // Make an aliased slot corresponding to child[slotNum] for the THEN
            MemberPath outputMember = GetMemberPath(slotNum); 
            AliasedSlot slot = new AliasedSlot(child, childSlot, outputMember, slotNum); 
            caseForOuterJoins.AddWhenThen(originBool, slot);
        } 

        private static FragmentQuery GenerateFragmentQuery(IEnumerable children, bool isLeft, ViewgenContext context, CellTreeOpType OpType)
        {
            Debug.Assert(children.Any()); 
            FragmentQuery fragmentQuery = isLeft ? children.First().LeftFragmentQuery : children.First().RightFragmentQuery;
 
            FragmentQueryProcessor qp = isLeft ? context.LeftFragmentQP : context.RightFragmentQP; 
            foreach (var child in children.Skip(1))
            { 
                FragmentQuery nextQuery = isLeft ? child.LeftFragmentQuery : child.RightFragmentQuery;
                switch (OpType)
                {
                    case CellTreeOpType.IJ: 
                        fragmentQuery = qp.Intersect(fragmentQuery, nextQuery);
                        break; 
                    case CellTreeOpType.LOJ: 
                        // Left outer join means keeping the domain of the leftmost child
                        break; 
                    case CellTreeOpType.LASJ:
                        // not used in basic view generation but current validation calls Simplify, so add this for debugging
                        fragmentQuery = qp.Difference(fragmentQuery, nextQuery);
                        break; 
                    default:
                        // All other operators (Union, FOJ) require union of the domains 
                        fragmentQuery = qp.Union(fragmentQuery, nextQuery); 
                        break;
                } 
            }
            return fragmentQuery;
        }
 

        #endregion 
 
        #region String methods
        // effects: Given an optype, returns a SQL-acceptable string 
        // corresponding to the op
        internal static string OpToCql(CellTreeOpType opType)
        {
            switch (opType) 
            {
                case CellTreeOpType.FOJ: return "FULL OUTER JOIN"; 
                case CellTreeOpType.IJ: return "INNER JOIN"; 
                //case CellTreeOpType.LASJ: return "LEFT ANTISEMIJOIN";
                case CellTreeOpType.LOJ: return "LEFT OUTER JOIN"; 
                case CellTreeOpType.Union: return "UNION ALL";
            }
            Debug.Fail("Unknown operator");
            return null; 
        }
 
        internal override void ToCompactString(StringBuilder stringBuilder) 
        {
            //            Debug.Assert(m_children.Count > 1, "Tree not flattened?"); 
            stringBuilder.Append("(");
            for (int i = 0; i < m_children.Count; i++)
            {
                CellTreeNode child = m_children[i]; 
                child.ToCompactString(stringBuilder);
                if (i != m_children.Count - 1) 
                { 
                    StringUtil.FormatStringBuilder(stringBuilder, " {0} ", OpType);
                } 
            }
            stringBuilder.Append(")");
        }
        #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.Collections.ObjectModel;
using System.Data.Mapping.ViewGeneration.CqlGeneration;
using System.Data.Mapping.ViewGeneration.QueryRewriting; 
using System.Data.Mapping.ViewGeneration.Utils;
using System.Text; 
using System.Linq; 
using System.Diagnostics;
using System.Data.Metadata.Edm; 
using System.Data.Entity;
using System.Data.Mapping.Update.Internal;

namespace System.Data.Mapping.ViewGeneration.Structures 
{
 
    using AttributeSet = Set; 

    // This class represents th intermediate nodes in the tree (non-leaf nodes) 
    internal class OpCellTreeNode : CellTreeNode
    {

        #region Constructors 
        // effects: Creates a node with operation opType and no children
        internal OpCellTreeNode(ViewgenContext context, CellTreeOpType opType) 
            : base(context) 
        {
            m_opType = opType; 
            m_attrs = new AttributeSet(MemberPath.EqualityComparer);
            m_children = new List();
        }
 
        internal OpCellTreeNode(ViewgenContext context, CellTreeOpType opType, params CellTreeNode[] children)
            : this(context, opType, (IEnumerable)children) { } 
 
        // effects: Given a sequence of children node and the opType, creates
        // an OpCellTreeNode and returns it 
        internal OpCellTreeNode(ViewgenContext context, CellTreeOpType opType, IEnumerable children)
            : this(context, opType)
        {
            // Add the children one by one so that we can get the attrs etc fixed 
            foreach (CellTreeNode child in children)
            { 
                Add(child); 
            }
        } 

        #endregion

        #region Fields 
        private Set m_attrs; // attributes from whole subtree below
        private List m_children; 
        private CellTreeOpType m_opType; 
        private FragmentQuery m_leftFragmentQuery;
        private FragmentQuery m_rightFragmentQuery; 
        #endregion

        #region Properties
        // effects: See CellTreeNode.OpType 
        internal override CellTreeOpType OpType { get { return m_opType; } }
 
        // Lazily create FragmentQuery when required 
        internal override FragmentQuery LeftFragmentQuery
        { 
            get
            {
                if (m_leftFragmentQuery == null)
                { 
                    m_leftFragmentQuery = GenerateFragmentQuery(Children, true /*isLeft*/, ViewgenContext, OpType);
                } 
                return m_leftFragmentQuery; 
            }
        } 

        internal override FragmentQuery RightFragmentQuery
        {
            get 
            {
                if (m_rightFragmentQuery == null) 
                { 
                    m_rightFragmentQuery = GenerateFragmentQuery(Children, false /*isLeft*/, ViewgenContext, OpType);
                } 
                return m_rightFragmentQuery;
            }
        }
 
        // effects: See CellTreeNode.RightDomainMap
        internal override MemberDomainMap RightDomainMap 
        { 
            get
            { 
                // Get the information from one of the children
                Debug.Assert(m_children[0].RightDomainMap != null, "EdmMember domain map missing");
                return m_children[0].RightDomainMap;
            } 
        }
 
        // effects: See CellTreeNode.Attributes 
        internal override Set Attributes { get { return m_attrs; } }
 
        // effects: See CellTreeNode.Children
        internal override List Children { get { return m_children; } }

        internal override int NumProjectedSlots 
        {
            get 
            { 
                // All children have the same number of slots
                Debug.Assert(m_children.Count > 1, "No children for op node?"); 
                return m_children[0].NumProjectedSlots;
            }
        }
 
        internal override int NumBoolSlots
        { 
            get 
            {
                Debug.Assert(m_children.Count > 1, "No children for op node?"); 
                return m_children[0].NumBoolSlots;
            }
        }
        #endregion 

        #region Methods 
        internal override TOutput Accept(SimpleCellTreeVisitor visitor, TInput param) 
        {
            return visitor.VisitOpNode(this, param); 
        }

        internal override TOutput Accept(CellTreeVisitor visitor, TInput param)
        { 
            switch (OpType)
            { 
                case CellTreeOpType.IJ: 
                    return visitor.VisitInnerJoin(this, param);
                case CellTreeOpType.LOJ: 
                    return visitor.VisitLeftOuterJoin(this, param);
                case CellTreeOpType.Union:
                    return visitor.VisitUnion(this, param);
                case CellTreeOpType.FOJ: 
                    return visitor.VisitFullOuterJoin(this, param);
                case CellTreeOpType.LASJ: 
                    return visitor.VisitLeftAntiSemiJoin(this, param); 
                default:
                    Debug.Fail("Unexpected optype: " + OpType); 
                    // To satsfy the compiler
                    return visitor.VisitInnerJoin(this, param);
            }
        } 

        // effects: Add child to the end of the current children list 
        // while ensuring the constants and attributes of the child are 
        // propagated into this (i.e., unioned)
        internal void Add(CellTreeNode child) 
        {
            Insert(m_children.Count, child);
        }
 
        // effects: Add child at the beginning of the current children list
        // while ensuring the constants and attributes of the child are 
        // propagated into this (i.e., unioned) 
        internal void AddFirst(CellTreeNode child)
        { 
            Insert(0, child);
        }

        // effects: Inserts child at "index" while ensuring the constants 
        // and attributes of the child are propagated into this
        private void Insert(int index, CellTreeNode child) 
        { 
            m_attrs.Unite(child.Attributes);
            m_children.Insert(index, child); 
            // reset fragmentQuery so it's recomputed when property FragmentQuery is accessed
            m_leftFragmentQuery = null;
            m_rightFragmentQuery = null;
        } 

        // effects: Given the required slots by the parent, 
        // generates a CqlBlock tree for the tree rooted below node 
        internal override CqlBlock ToCqlBlock(bool[] requiredSlots, CqlIdentifiers identifiers, ref int blockAliasNum,
            ref List withStatements) 
        {
            // Dispatch depending on whether we have a union node or join node
            CqlBlock result;
            if (OpType == CellTreeOpType.Union) 
            {
                result = UnionToCqlBlock(requiredSlots, identifiers, ref blockAliasNum, ref withStatements); 
            } 
            else
            { 
                result = JoinToCqlBlock(requiredSlots, identifiers, ref blockAliasNum, ref withStatements);
            }
            return result;
        } 

        internal override bool IsProjectedSlot(int slot) 
        { 
            // If any childtree projects it, return true
            foreach (CellTreeNode childNode in Children) 
            {
                if (childNode.IsProjectedSlot(slot))
                {
                    return true; 
                }
            } 
            return false; 
        }
        #endregion 

        #region Union CqlBLock Methods
        // requires: node corresponds to a Union node
        // effects: Given a union node and the slots required by the parent, 
        // generates a CqlBlock for the subtree rooted at node
        private CqlBlock UnionToCqlBlock(bool[] requiredSlots, CqlIdentifiers identifiers, ref int blockAliasNum, ref List withStatements) 
        { 
            Debug.Assert(OpType == CellTreeOpType.Union);
 
            List children = new List();
            List aliasedSlotsForAddedByChildren = new List();

            int totalSlots = requiredSlots.Length; 
            foreach (CellTreeNode child in Children)
            { 
                // Unlike Join, we pass the requiredSlots from the parent as the requirement 
                // CqlBlock childBlock = child.ToCqlBlock(requiredSlots, identifiers, ref blockAliasNum);
                bool[] childProjectedSlots = child.GetProjectedSlots(); 
                AndWith(childProjectedSlots, requiredSlots);
                CqlBlock childBlock = child.ToCqlBlock(childProjectedSlots, identifiers, ref blockAliasNum, ref withStatements);
                for (int aliasedSlotNumber = childProjectedSlots.Length; aliasedSlotNumber < childBlock.Slots.Count; aliasedSlotNumber++)
                { 
                    SlotInfo slotInfo = childBlock.Slots[aliasedSlotNumber];
                    aliasedSlotsForAddedByChildren.Add(new AliasedSlot(childBlock, slotInfo.SlotValue, slotInfo.MemberPath, aliasedSlotNumber)); 
                } 

 
                // if required, but not projected, add NULL
                SlotInfo[] paddedSlotInfo = new SlotInfo[childBlock.Slots.Count];
                ReadOnlyCollection childSlotInfo = childBlock.Slots;
                for (int slotNum = 0; slotNum < totalSlots; slotNum++) 
                {
                    if (requiredSlots[slotNum] && !childProjectedSlots[slotNum]) 
                    { 
                        if (IsBoolSlot(slotNum))
                        { 
                            paddedSlotInfo[slotNum] = new SlotInfo(true /* is required */, true /* is projected */,
                                                            new BooleanProjectedSlot(BoolExpression.False, identifiers, SlotToBoolIndex(slotNum)), null /* member path*/);
                        }
                        else 
                        {
                            // NULL as projected slot 
                            paddedSlotInfo[slotNum] = new SlotInfo(true /* is required */, true /* is projected */, 
                                                             new ConstantProjectedSlot(Constant.Null), childSlotInfo[slotNum].MemberPath);
                        } 
                    }
                    else
                    {
                        paddedSlotInfo[slotNum] = childSlotInfo[slotNum]; 
                    }
                } 
                //Add the slots that were added by children 
                //for (int slotNum = totalSlots; slotNum < childBlock.Slots.Count; slotNum++)
                //{ 
                //    paddedSlotInfo[slotNum] = childSlotInfo[slotNum];
                //}
                childBlock.Slots = new ReadOnlyCollection(paddedSlotInfo);
                children.Add(childBlock); 
                Debug.Assert(totalSlots == child.NumBoolSlots + child.NumProjectedSlots,
                    "Number of required slots is different from what each node in the tree has?"); 
            } 

            //We need to add the slots added by each child unformly for others( as nulls) 
            //since this is a union operation
            if (aliasedSlotsForAddedByChildren.Count != 0)
            {
                foreach (CqlBlock childBlock in children) 
                {
                    SlotInfo[] childSlots = new SlotInfo[totalSlots + aliasedSlotsForAddedByChildren.Count]; 
                    childBlock.Slots.CopyTo(childSlots, 0); 
                    int index = totalSlots;
                    foreach (AliasedSlot aliasedSlot in aliasedSlotsForAddedByChildren) 
                    {
                        if (aliasedSlot.Block.Equals(childBlock))
                        {
                            childSlots[index] = new SlotInfo(true /* is required */, true /* is projected */, 
                                 aliasedSlot.InnerSlot, aliasedSlot.MemberPath);
                        } 
                        else 
                        {
                            childSlots[index] = new SlotInfo(true /* is required */, true /* is projected */, 
                                 new ConstantProjectedSlot(Constant.Null), aliasedSlot.MemberPath);

                        }
                        //move on to the next slot added by children. 
                        index++;
                    } 
                    childBlock.Slots = new ReadOnlyCollection(childSlots); 
                }
            } 

            // Create the slotInfos and then Union CqlBlock
            SlotInfo[] slotInfos = new SlotInfo[totalSlots + aliasedSlotsForAddedByChildren.Count];
            // We pick the slot references from the first child, just as convention 
            // In a union, values come from both sides
            CqlBlock firstChild = children[0]; 
 
            for (int slotNum = 0; slotNum < totalSlots; slotNum++)
            { 
                ProjectedSlot slot = firstChild.ProjectedSlot(slotNum);
                MemberPath memberPath = GetMemberPath(slotNum);
                // A required slot is somehow projected by a child in Union
                // -- so set isProjected to be the same as isRequired 
                bool isRequired = requiredSlots[slotNum];
                SlotInfo slotInfo = new SlotInfo(isRequired, isRequired, slot, memberPath); 
                slotInfos[slotNum] = slotInfo; 
            }
 
            for (int i = 0, slotNum = totalSlots; slotNum < totalSlots + aliasedSlotsForAddedByChildren.Count; slotNum++, i++)
            {
                slotInfos[slotNum] = new SlotInfo(true, true, aliasedSlotsForAddedByChildren[i], aliasedSlotsForAddedByChildren[i].MemberPath);
            } 

 
            CqlBlock block = new UnionCqlBlock(slotInfos, children, identifiers, ++blockAliasNum); 
            return block;
        } 
        private static void AndWith(bool[] boolArray, bool[] another)
        {
            Debug.Assert(boolArray.Length == another.Length);
            for (int i = 0; i < boolArray.Length; i++) 
            {
                boolArray[i] &= another[i]; 
            } 
        }
        #endregion 

        #region Join CqlBLock Methods
        // requires: node corresponds to an IJ, LOJ, FOJ node
        // effects: Given a union node and the slots required by the parent, 
        // generates a CqlBlock for the subtree rooted at node
        private CqlBlock JoinToCqlBlock(bool[] requiredSlots, CqlIdentifiers identifiers, ref int blockAliasNum, ref List withStatements) 
        { 
            int totalSlots = requiredSlots.Length;
 
            Debug.Assert(OpType == CellTreeOpType.IJ ||
                         OpType == CellTreeOpType.LOJ ||
                         OpType == CellTreeOpType.FOJ, "Only these join operations handled");
 
            List children = new List();
            List aliasedSlotsForAddedByChildren = new List(); 
 
            // First get the children nodes (FROM part)
            foreach (CellTreeNode child in Children) 
            {
                // Determine the slots that are projected by this child.
                // These are the required slots as well - unlike Union, we do
                // not need the child to project any extra nulls 
                bool[] childProjectedSlots = child.GetProjectedSlots();
                AndWith(childProjectedSlots, requiredSlots); 
                //List subAddedSlotsByChild = new List(); 
                CqlBlock childBlock = child.ToCqlBlock(childProjectedSlots, identifiers, ref blockAliasNum, ref withStatements);
                //addedSlotsByChild.AddRange(subAddedSlotsByChild); 
                children.Add(childBlock);
                //foreach (SlotInfo slotInfo in subAddedSlotsByChild)
                for (int aliasedSlotNumber = childProjectedSlots.Length; aliasedSlotNumber < childBlock.Slots.Count; aliasedSlotNumber++)
                { 
                    SlotInfo slotInfo = childBlock.Slots[aliasedSlotNumber];
                    aliasedSlotsForAddedByChildren.Add(new AliasedSlot(childBlock, slotInfo.SlotValue, slotInfo.MemberPath, aliasedSlotNumber)); 
                } 
                Debug.Assert(totalSlots == child.NumBoolSlots + child.NumProjectedSlots,
                    "Number of required slots is different from what each node in the tree has?"); 
            }

            // Now get the slots that are projected out by this node (SELECT part)
            SlotInfo[] slotInfos = new SlotInfo[totalSlots + aliasedSlotsForAddedByChildren.Count]; 
            for (int slotNum = 0; slotNum < totalSlots; slotNum++)
            { 
                // Note: this call could create a CaseStatementSlot (i.e., slotInfo.SlotValue is CaseStatementSlot) 
                // which uses "from" booleans that need to be projected by children
                SlotInfo slotInfo = GetJoinSlotInfo(OpType, requiredSlots[slotNum], children, slotNum, identifiers); 
                slotInfos[slotNum] = slotInfo;
            }

 
            for (int i = 0, slotNum = totalSlots; slotNum < totalSlots + aliasedSlotsForAddedByChildren.Count; slotNum++, i++)
            { 
                slotInfos[slotNum] = new SlotInfo(true, true, aliasedSlotsForAddedByChildren[i], aliasedSlotsForAddedByChildren[i].MemberPath); 
            }
 
            // Generate the ON conditions: For each child, generate an ON
            // clause with the 0th child on the key fields
            List onClauses = new List();
 
            for (int i = 1; i < children.Count; i++)
            { 
                CqlBlock child = children[i]; 
                JoinCqlBlock.OnClause onClause = new JoinCqlBlock.OnClause();
                foreach (int keySlotNum in KeySlots) 
                {
                    SlotInfo slotInfo = slotInfos[keySlotNum];

                    if (ViewgenContext.Config.IsValidationEnabled) 
                    {
                        Debug.Assert(child.IsProjected(keySlotNum), "Key is not in child"); 
                        Debug.Assert(children[0].IsProjected(keySlotNum), "Key is not in 0th child"); 
                    }
                    else 
                    {
                        if (!child.IsProjected(keySlotNum) || !children[0].IsProjected(keySlotNum))
                        {
                            ErrorLog errorLog = new ErrorLog(); 
                            errorLog.AddEntry(new ErrorLog.Record(true, ViewGenErrorCode.NoJoinKeyOrFKProvidedInMapping,
                                    Strings.Viewgen_NoJoinKeyOrFK, ViewgenContext.AllWrappersForExtent, String.Empty)); 
                            ExceptionHelpers.ThrowMappingException(errorLog, ViewgenContext.Config); 
                        }
                    } 
                    AliasedSlot first = new AliasedSlot(children[0], slotInfo.SlotValue,
                                                        slotInfo.MemberPath, keySlotNum);
                    AliasedSlot second = new AliasedSlot(child, slotInfo.SlotValue, slotInfo.MemberPath,
                                                         keySlotNum); 
                    onClause.Add(first, second);
                } 
                onClauses.Add(onClause); 
            }
 
            CqlBlock result = new JoinCqlBlock(OpType, slotInfos, children, onClauses, identifiers, ++blockAliasNum);
            return result;
        }
 
        // effects: Generates a SlotInfo object for a slot of a join node. It
        // uses the type of the join operation (opType), whether the slot is 
        // required by the parent or not (isRequiredSlot), the children of 
        // this node (children) and the number of the slotNum
        private SlotInfo GetJoinSlotInfo(CellTreeOpType opType, bool isRequiredSlot, 
                                         List children, int slotNum, CqlIdentifiers identifiers)
        {
            if (false == isRequiredSlot)
            { 
                // The slot will not be used. So we can set the projected slot to be null
                SlotInfo unrequiredSlotInfo = new SlotInfo(false, false, null, GetMemberPath(slotNum)); 
                return unrequiredSlotInfo; 
            }
 
            // For a required slot, determine the child who is contributing to this value
            int childDefiningSlot = -1;
            CaseStatement caseForOuterJoins = null;
 
            for (int childNum = 0; childNum < children.Count; childNum++)
            { 
                CqlBlock child = children[childNum]; 
                if (false == child.IsProjected(slotNum))
                { 
                    continue;
                }
                // For keys, we can pick any child block. So the first
                // one that we find is fine as well 
                if (IsKeySlot(slotNum))
                { 
                    childDefiningSlot = childNum; 
                    break;
                } 
                else if (opType == CellTreeOpType.IJ)
                {
                    // For Inner Joins, most of the time, the entries will be
                    // the same in all the children. However, in some cases, 
                    // we will end up with NULL in one child and an actual
                    // value in another -- we should pick up the actual value in that case 
                    childDefiningSlot = GetInnerJoinChildForSlot(children, slotNum); 
                    break;
                } 
                else
                {
                    // For LOJs, we generate a case statement if more than
                    // one child generates the value - until then we do not 
                    // create the caseForOuterJoins object
                    if (childDefiningSlot != -1) 
                    { 
                        // We really need a case statement now
                        // We have the value being generated by another child 
                        // We need to fetch the variable from the appropriate child
                        Debug.Assert(false == IsBoolSlot(slotNum), "Boolean slots cannot come from two children");
                        if (caseForOuterJoins == null)
                        { 
                            MemberPath outputMember = GetMemberPath(slotNum);
                            caseForOuterJoins = new CaseStatement(outputMember); 
                            // Add the child that we had not added in the first shot 
                            AddCaseForOuterJoins(caseForOuterJoins, children[childDefiningSlot], slotNum, identifiers);
                        } 
                        AddCaseForOuterJoins(caseForOuterJoins, child, slotNum, identifiers);
                    }
                    childDefiningSlot = childNum;
                } 
            }
 
            MemberPath memberPath = GetMemberPath(slotNum); 
            ProjectedSlot slot = null;
 
            // Generate the slot value -- case statement slot, or an aliased slot
            // or null or false. If case statement slot has nothing, treat it as null/empty
            if (caseForOuterJoins != null && (caseForOuterJoins.Clauses.Count > 0 || caseForOuterJoins.ElseValue != null))
            { 
                caseForOuterJoins.Simplify();
                slot = new CaseStatementProjectedSlot(caseForOuterJoins, null); 
            } 
            else if (childDefiningSlot >= 0)
            { 
                slot = new AliasedSlot(children[childDefiningSlot],
                                       children[childDefiningSlot].ProjectedSlot(slotNum), memberPath, slotNum);
            }
            else 
            {
                // need to produce output slot, but don't have a value 
                // output NULL for fields or False for bools 
                if (IsBoolSlot(slotNum))
                { 
                    slot = new BooleanProjectedSlot(BoolExpression.False, identifiers, SlotToBoolIndex(slotNum));
                }
                else
                { 
                    slot = new ConstantProjectedSlot(Domain.GetDefaultValueForMemberPath(memberPath, GetLeaves(), ViewgenContext.Config));
                } 
            } 

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

        // requires: children to be a list of nodes that are children of an
        // Inner Join node. slotNum does not correspond to the key slot 
        // effects: Determines the child number from which the slot should be
        // picked up. 
        private static int GetInnerJoinChildForSlot(List children, int slotNum) 
        {
            // Picks the child with the non-constant slot first. If none, picks a non-null constant slot. 
            // If not een that, picks any one
            int result = -1;
            for (int i = 0; i < children.Count; i++)
            { 
                CqlBlock child = children[i];
                if (false == child.IsProjected(slotNum)) 
                { 
                    continue;
                } 
                ProjectedSlot slot = child.ProjectedSlot(slotNum);
                ConstantProjectedSlot constantSlot = slot as ConstantProjectedSlot;
                MemberProjectedSlot joinSlot = slot as MemberProjectedSlot;
                if (joinSlot != null) 
                { // Pick the non-constant slot
                    result = i; 
                } 
                else if (constantSlot != null && constantSlot.CellConstant.IsNull())
                { 
                    if (result == -1)
                    { // In case, all are null
                        result = i;
                    } 
                }
                else 
                { 
                    // Just pick anything
                    result = i; 
                }
            }
            return result;
        } 

        // requires: caseForOuterJoins corresponds the slot "slotNum" 
        // effects: Adds a WhenThen corresponding to child to caseForOuterJoins. 
        private void AddCaseForOuterJoins(CaseStatement caseForOuterJoins, CqlBlock child, int slotNum, CqlIdentifiers identifiers)
        { 
            // Determine the cells that the slot comes from
            // and make an OR expression, e.g., WHEN _from0 or _from2 or ... THEN child[slotNum]

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

            BoolExpression originBool = BoolExpression.False;
            for (int i = 0; i < NumBoolSlots; i++) 
            {
                int boolSlotNum = BoolIndexToSlot(i); 
                if (child.IsProjected(boolSlotNum)) 
                {
                    // OR it to the expression 
                    QualifiedCellIdBoolean boolExpr = new QualifiedCellIdBoolean(child, identifiers, i);
                    originBool = BoolExpression.CreateOr(originBool, BoolExpression.CreateLiteral(boolExpr, RightDomainMap));
                }
            } 
            // Make an aliased slot corresponding to child[slotNum] for the THEN
            MemberPath outputMember = GetMemberPath(slotNum); 
            AliasedSlot slot = new AliasedSlot(child, childSlot, outputMember, slotNum); 
            caseForOuterJoins.AddWhenThen(originBool, slot);
        } 

        private static FragmentQuery GenerateFragmentQuery(IEnumerable children, bool isLeft, ViewgenContext context, CellTreeOpType OpType)
        {
            Debug.Assert(children.Any()); 
            FragmentQuery fragmentQuery = isLeft ? children.First().LeftFragmentQuery : children.First().RightFragmentQuery;
 
            FragmentQueryProcessor qp = isLeft ? context.LeftFragmentQP : context.RightFragmentQP; 
            foreach (var child in children.Skip(1))
            { 
                FragmentQuery nextQuery = isLeft ? child.LeftFragmentQuery : child.RightFragmentQuery;
                switch (OpType)
                {
                    case CellTreeOpType.IJ: 
                        fragmentQuery = qp.Intersect(fragmentQuery, nextQuery);
                        break; 
                    case CellTreeOpType.LOJ: 
                        // Left outer join means keeping the domain of the leftmost child
                        break; 
                    case CellTreeOpType.LASJ:
                        // not used in basic view generation but current validation calls Simplify, so add this for debugging
                        fragmentQuery = qp.Difference(fragmentQuery, nextQuery);
                        break; 
                    default:
                        // All other operators (Union, FOJ) require union of the domains 
                        fragmentQuery = qp.Union(fragmentQuery, nextQuery); 
                        break;
                } 
            }
            return fragmentQuery;
        }
 

        #endregion 
 
        #region String methods
        // effects: Given an optype, returns a SQL-acceptable string 
        // corresponding to the op
        internal static string OpToCql(CellTreeOpType opType)
        {
            switch (opType) 
            {
                case CellTreeOpType.FOJ: return "FULL OUTER JOIN"; 
                case CellTreeOpType.IJ: return "INNER JOIN"; 
                //case CellTreeOpType.LASJ: return "LEFT ANTISEMIJOIN";
                case CellTreeOpType.LOJ: return "LEFT OUTER JOIN"; 
                case CellTreeOpType.Union: return "UNION ALL";
            }
            Debug.Fail("Unknown operator");
            return null; 
        }
 
        internal override void ToCompactString(StringBuilder stringBuilder) 
        {
            //            Debug.Assert(m_children.Count > 1, "Tree not flattened?"); 
            stringBuilder.Append("(");
            for (int i = 0; i < m_children.Count; i++)
            {
                CellTreeNode child = m_children[i]; 
                child.ToCompactString(stringBuilder);
                if (i != m_children.Count - 1) 
                { 
                    StringUtil.FormatStringBuilder(stringBuilder, " {0} ", OpType);
                } 
            }
            stringBuilder.Append(")");
        }
        #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