Example #1
0
        // requires: The tree rooted at cellTreeNode is an FOJ tree of
        // LeafCellTreeNodes only, i.e., there is an FOJ node with the
        // children being LeafCellTreeNodes
        //
        // effects: Given a tree rooted at rootNode, ensures that cells
        // of the same right extent are placed in their own subtree below
        // cellTreeNode. That is, if there are 3 cells of extent A and 2 of
        // extent B (i.e., 5 cells with an FOJ on it), the resulting tree has
        // an FOJ node with two children -- FOJ nodes. These FOJ nodes have 2
        // and 3 children
        internal CellTreeNode GroupByRightExtent(CellTreeNode rootNode)
        {
            // A dictionary that maps an extent to the nodes are from that extent
            // We want a ref comparer here
            var extentMap =
                new KeyToListMap <EntitySetBase, LeafCellTreeNode>(EqualityComparer <EntitySetBase> .Default);

            // CR_Meek_Low: method can be simplified (Map<Extent, OpCellTreeNode>, populate as you go)
            // (becomes self-documenting)
            // For each leaf child, find the extent of the child and place it
            // in extentMap
            foreach (LeafCellTreeNode childNode in rootNode.Children)
            {
                // A cell may contain P, P.PA -- we return P
                // CHANGE_ADYA_FEATURE_COMPOSITION Need to fix for composition!!
                var extent = childNode.LeftCellWrapper.RightCellQuery.Extent; // relation or extent to group by
                Debug.Assert(extent != null, "Each cell must have a right extent");

                // Add the childNode as a child of the FOJ tree for "extent"
                extentMap.Add(extent, childNode);
            }
            // Now go through the extent map and create FOJ nodes for each extent
            // Place the nodes for that extent in the newly-created FOJ subtree
            // Also add the op node for every node as a child of the final result
            var result = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.FOJ);

            foreach (var extent in extentMap.Keys)
            {
                var extentFojNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.FOJ);
                foreach (var childNode in extentMap.ListForKey(extent))
                {
                    extentFojNode.Add(childNode);
                }
                result.Add(extentFojNode);
            }
            // We call Flatten to remove any unnecessary nestings
            // where an OpNode has only 1 child.
            return(result.Flatten());
        }
        internal CellTreeNode GroupByRightExtent(CellTreeNode rootNode)
        {
            KeyToListMap <EntitySetBase, LeafCellTreeNode> keyToListMap = new KeyToListMap <EntitySetBase, LeafCellTreeNode>((IEqualityComparer <EntitySetBase>)EqualityComparer <EntitySetBase> .Default);

            foreach (LeafCellTreeNode child in rootNode.Children)
            {
                EntitySetBase extent = child.LeftCellWrapper.RightCellQuery.Extent;
                keyToListMap.Add(extent, child);
            }
            OpCellTreeNode opCellTreeNode1 = new OpCellTreeNode(this.m_viewgenContext, CellTreeOpType.FOJ);

            foreach (EntitySetBase key in keyToListMap.Keys)
            {
                OpCellTreeNode opCellTreeNode2 = new OpCellTreeNode(this.m_viewgenContext, CellTreeOpType.FOJ);
                foreach (LeafCellTreeNode leafCellTreeNode in keyToListMap.ListForKey(key))
                {
                    opCellTreeNode2.Add((CellTreeNode)leafCellTreeNode);
                }
                opCellTreeNode1.Add((CellTreeNode)opCellTreeNode2);
            }
            return(opCellTreeNode1.Flatten());
        }
        private bool IsDisjoint(CellTreeNode n1, CellTreeNode n2)
        {
            int  viewTarget = (int)this.m_viewgenContext.ViewTarget;
            bool flag       = this.LeftQP.IsDisjointFrom(n1.LeftFragmentQuery, n2.LeftFragmentQuery);

            if (flag && this.m_viewgenContext.ViewTarget == ViewTarget.QueryView)
            {
                return(true);
            }
            bool rightFragmentQuery = new OpCellTreeNode(this.m_viewgenContext, CellTreeOpType.IJ, new CellTreeNode[2]
            {
                n1,
                n2
            }).IsEmptyRightFragmentQuery;

            if (this.m_viewgenContext.ViewTarget == ViewTarget.UpdateView && flag && !rightFragmentQuery)
            {
                if (ErrorPatternMatcher.FindMappingErrors(this.m_viewgenContext, this.m_domainMap, this.m_errorLog))
                {
                    return(false);
                }
                StringBuilder builder = new StringBuilder(Strings.Viewgen_RightSideNotDisjoint((object)this.m_viewgenContext.Extent.ToString()));
                builder.AppendLine();
                FragmentQuery query = this.LeftQP.Intersect(n1.RightFragmentQuery, n2.RightFragmentQuery);
                if (this.LeftQP.IsSatisfiable(query))
                {
                    query.Condition.ExpensiveSimplify();
                    RewritingValidator.EntityConfigurationToUserString(query.Condition, builder);
                }
                this.m_errorLog.AddEntry(new ErrorLog.Record(ViewGenErrorCode.DisjointConstraintViolation, builder.ToString(), (IEnumerable <LeftCellWrapper>) this.m_viewgenContext.AllWrappersForExtent, string.Empty));
                ExceptionHelpers.ThrowMappingException(this.m_errorLog, this.m_config);
                return(false);
            }
            if (!flag)
            {
                return(rightFragmentQuery);
            }
            return(true);
        }
        private bool TryAddChildToGroup(
            CellTreeOpType opTypeToIsolate,
            CellTreeNode childNode,
            OpCellTreeNode groupNode)
        {
            switch (opTypeToIsolate)
            {
            case CellTreeOpType.Union:
                if (this.IsDisjoint(childNode, (CellTreeNode)groupNode))
                {
                    groupNode.Add(childNode);
                    return(true);
                }
                break;

            case CellTreeOpType.LOJ:
                if (this.IsContainedIn(childNode, (CellTreeNode)groupNode))
                {
                    groupNode.Add(childNode);
                    return(true);
                }
                if (this.IsContainedIn((CellTreeNode)groupNode, childNode))
                {
                    groupNode.AddFirst(childNode);
                    return(true);
                }
                break;

            case CellTreeOpType.IJ:
                if (this.IsEquivalentTo(childNode, (CellTreeNode)groupNode))
                {
                    groupNode.Add(childNode);
                    return(true);
                }
                break;
            }
            return(false);
        }
Example #5
0
            internal override CellTreeNode VisitAnd(
                AndExpr <DomainConstraint <BoolLiteral, Constant> > expression)
            {
                IEnumerable <CellTreeNode> cellTreeNodes  = this.AcceptChildren((IEnumerable <BoolExpr <DomainConstraint <BoolLiteral, Constant> > >)expression.Children);
                OpCellTreeNode             opCellTreeNode = new OpCellTreeNode(this._viewgenContext, CellTreeOpType.IJ);

                foreach (CellTreeNode child in cellTreeNodes)
                {
                    if (child == null)
                    {
                        return((CellTreeNode)null);
                    }
                    if (child != this._topLevelTree)
                    {
                        opCellTreeNode.Add(child);
                    }
                }
                if (opCellTreeNode.Children.Count != 0)
                {
                    return((CellTreeNode)opCellTreeNode);
                }
                return(this._topLevelTree);
            }
Example #6
0
        private Dictionary <RewritingValidator.MemberValueBinding, CellTreeNode> CreateMemberValueTrees(
            bool complementElse)
        {
            Dictionary <RewritingValidator.MemberValueBinding, CellTreeNode> dictionary = new Dictionary <RewritingValidator.MemberValueBinding, CellTreeNode>();

            foreach (MemberPath conditionMember in this._domainMap.ConditionMembers(this._viewgenContext.Extent))
            {
                List <Constant> constantList   = new List <Constant>(this._domainMap.GetDomain(conditionMember));
                OpCellTreeNode  opCellTreeNode = new OpCellTreeNode(this._viewgenContext, CellTreeOpType.Union);
                for (int index1 = 0; index1 < constantList.Count; ++index1)
                {
                    Constant domainValue = constantList[index1];
                    RewritingValidator.MemberValueBinding index2 = new RewritingValidator.MemberValueBinding(conditionMember, domainValue);
                    Tile <FragmentQuery> rewriting;
                    if (this._viewgenContext.TryGetCachedRewriting(QueryRewriter.CreateMemberConditionQuery(conditionMember, domainValue, this._keyAttributes, this._domainMap), out rewriting))
                    {
                        CellTreeNode cellTree = QueryRewriter.TileToCellTree(rewriting, this._viewgenContext);
                        dictionary[index2] = cellTree;
                        if (index1 < constantList.Count - 1)
                        {
                            opCellTreeNode.Add(cellTree);
                        }
                    }
                }
                if (complementElse && constantList.Count > 1)
                {
                    Constant constant = constantList[constantList.Count - 1];
                    RewritingValidator.MemberValueBinding index = new RewritingValidator.MemberValueBinding(conditionMember, constant);
                    dictionary[index] = (CellTreeNode) new OpCellTreeNode(this._viewgenContext, CellTreeOpType.LASJ, new CellTreeNode[2]
                    {
                        this._basicView,
                        (CellTreeNode)opCellTreeNode
                    });
                }
            }
            return(dictionary);
        }
        // effects: Given the set of used cells for an extent, returns a
        // view to generate that extent
        internal CellTreeNode CreateViewExpression()
        {
            // Create an initial FOJ group with all the used cells as children
            var fojNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.FOJ);

            // Add all the used cells as children to fojNode. This is a valid
            // view for the extent. We later try to optimize it
            foreach (var cell in m_usedCells)
            {
                var cellNode = new LeafCellTreeNode(m_viewgenContext, cell);
                fojNode.Add(cellNode);
            }

            //rootNode = GroupByNesting(rootNode);
            // Group cells by the "right" extent (recall that we are
            // generating the view for the left extent) so that cells of the
            // same extent are in the same subtree
            var rootNode = GroupByRightExtent(fojNode);

            // Change some of the FOJs to Unions, IJs and LOJs
            rootNode = IsolateUnions(rootNode);

            // The isolation with Union is different from IsolateUnions --
            // the above isolation finds collections of chidren in a
            // node and connects them by union. The below one only considers
            // two children at a time
            rootNode = IsolateByOperator(rootNode, CellTreeOpType.Union);
            rootNode = IsolateByOperator(rootNode, CellTreeOpType.IJ);
            rootNode = IsolateByOperator(rootNode, CellTreeOpType.LOJ);
            if (m_viewgenContext.ViewTarget
                == ViewTarget.QueryView)
            {
                rootNode = ConvertUnionsToNormalizedLOJs(rootNode);
            }

            return rootNode;
        }
Example #8
0
        // effects: Given the set of used cells for an extent, returns a
        // view to generate that extent
        internal CellTreeNode CreateViewExpression()
        {
            // Create an initial FOJ group with all the used cells as children
            var fojNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.FOJ);

            // Add all the used cells as children to fojNode. This is a valid
            // view for the extent. We later try to optimize it
            foreach (var cell in m_usedCells)
            {
                var cellNode = new LeafCellTreeNode(m_viewgenContext, cell);
                fojNode.Add(cellNode);
            }

            //rootNode = GroupByNesting(rootNode);
            // Group cells by the "right" extent (recall that we are
            // generating the view for the left extent) so that cells of the
            // same extent are in the same subtree
            var rootNode = GroupByRightExtent(fojNode);

            // Change some of the FOJs to Unions, IJs and LOJs
            rootNode = IsolateUnions(rootNode);

            // The isolation with Union is different from IsolateUnions --
            // the above isolation finds collections of chidren in a
            // node and connects them by union. The below one only considers
            // two children at a time
            rootNode = IsolateByOperator(rootNode, CellTreeOpType.Union);
            rootNode = IsolateByOperator(rootNode, CellTreeOpType.IJ);
            rootNode = IsolateByOperator(rootNode, CellTreeOpType.LOJ);
            if (m_viewgenContext.ViewTarget
                == ViewTarget.QueryView)
            {
                rootNode = ConvertUnionsToNormalizedLOJs(rootNode);
            }

            return(rootNode);
        }
        internal void Validate()
        {
            // turn rewritings into cell trees
            // plain: according to rewritings for case statements
            var plainMemberValueTrees = CreateMemberValueTrees(false);
            // complement: uses complement rewriting for the last WHEN ... THEN
            // This is how the final case statement will be generated in update views
            var complementMemberValueTrees = CreateMemberValueTrees(true);

            var plainWhereClauseVisitor      = new WhereClauseVisitor(_basicView, plainMemberValueTrees);
            var complementWhereClauseVisitor = new WhereClauseVisitor(_basicView, complementMemberValueTrees);

            // produce CellTree for each SQuery
            foreach (var wrapper in _viewgenContext.AllWrappersForExtent)
            {
                var cell = wrapper.OnlyInputCell;
                // construct cell tree for CQuery
                CellTreeNode cQueryTree = new LeafCellTreeNode(_viewgenContext, wrapper);
                // sQueryTree: unfolded update view inside S-side of the cell
                CellTreeNode sQueryTree;
                // construct cell tree for SQuery (will be used for domain constraint checking)
                var complementSQueryTreeForCondition = complementWhereClauseVisitor.GetCellTreeNode(cell.SQuery.WhereClause);
                Debug.Assert(complementSQueryTreeForCondition != null, "Rewriting for S-side query is unsatisfiable");
                if (complementSQueryTreeForCondition == null)
                {
                    continue; // situation should never happen
                }
                if (complementSQueryTreeForCondition != _basicView)
                {
                    // intersect with basic expression
                    sQueryTree = new OpCellTreeNode(_viewgenContext, CellTreeOpType.IJ, complementSQueryTreeForCondition, _basicView);
                }
                else
                {
                    sQueryTree = _basicView;
                }

                // Append in-set or in-end condition to both queries to produce more concise errors
                // Otherwise, the errors are of the form "if there exists an entity in extent, then violation". We don't care about empty extents
                var inExtentCondition = BoolExpression.CreateLiteral(wrapper.CreateRoleBoolean(), _viewgenContext.MemberMaps.QueryDomainMap);

                BoolExpression unsatisfiedConstraint;
                if (!CheckEquivalence(
                        cQueryTree.RightFragmentQuery, sQueryTree.RightFragmentQuery, inExtentCondition,
                        out unsatisfiedConstraint))
                {
                    var extentName = StringUtil.FormatInvariant("{0}", _viewgenContext.Extent);

                    // Simplify to produce more readable error messages
                    cQueryTree.RightFragmentQuery.Condition.ExpensiveSimplify();
                    sQueryTree.RightFragmentQuery.Condition.ExpensiveSimplify();

                    var message = Strings.ViewGen_CQ_PartitionConstraint(extentName);

                    ReportConstraintViolation(
                        message, unsatisfiedConstraint, ViewGenErrorCode.PartitionConstraintViolation,
                        cQueryTree.GetLeaves().Concat(sQueryTree.GetLeaves()));
                }

                var plainSQueryTreeForCondition = plainWhereClauseVisitor.GetCellTreeNode(cell.SQuery.WhereClause);
                Debug.Assert(plainSQueryTreeForCondition != null, "Rewriting for S-side query is unsatisfiable");
                if (plainSQueryTreeForCondition != null)
                {
                    // Query is non-empty. Check domain constraints on:
                    // (a) swapped members
                    DomainConstraintVisitor.CheckConstraints(plainSQueryTreeForCondition, wrapper, _viewgenContext, _errorLog);
                    //If you have already found errors, just continue on to the next wrapper instead of                    //collecting more errors for the same
                    if (_errorLog.Count > 0)
                    {
                        continue;
                    }
                    // (b) projected members
                    CheckConstraintsOnProjectedConditionMembers(plainMemberValueTrees, wrapper, sQueryTree, inExtentCondition);
                    if (_errorLog.Count > 0)
                    {
                        continue;
                    }
                }
                CheckConstraintsOnNonNullableMembers(wrapper);
            }

            if (_errorLog.Count > 0)
            {
                ExceptionHelpers.ThrowMappingException(_errorLog, _viewgenContext.Config);
            }
        }
        private bool IsDisjoint(CellTreeNode n1, CellTreeNode n2)
        {
            var isQueryView = (m_viewgenContext.ViewTarget == ViewTarget.QueryView);

            var isDisjointLeft = LeftQP.IsDisjointFrom(n1.LeftFragmentQuery, n2.LeftFragmentQuery);

            if (isDisjointLeft && m_viewgenContext.ViewTarget == ViewTarget.QueryView)
            {
                return true;
            }

            CellTreeNode n = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.IJ, n1, n2);
            var isDisjointRight = n.IsEmptyRightFragmentQuery;

            if (m_viewgenContext.ViewTarget == ViewTarget.UpdateView &&
                isDisjointLeft
                && !isDisjointRight)
            {
                if (ErrorPatternMatcher.FindMappingErrors(m_viewgenContext, m_domainMap, m_errorLog))
                {
                    return false;
                }

                var builder = new StringBuilder(Strings.Viewgen_RightSideNotDisjoint(m_viewgenContext.Extent.ToString()));
                builder.AppendLine();

                //Retrieve the offending state
                var intersection = LeftQP.Intersect(n1.RightFragmentQuery, n2.RightFragmentQuery);
                if (LeftQP.IsSatisfiable(intersection))
                {
                    intersection.Condition.ExpensiveSimplify();
                    RewritingValidator.EntityConfigurationToUserString(intersection.Condition, builder);
                }

                //Add Error
                m_errorLog.AddEntry(
                    new ErrorLog.Record(
                        ViewGenErrorCode.DisjointConstraintViolation,
                        builder.ToString(), m_viewgenContext.AllWrappersForExtent, String.Empty));

                ExceptionHelpers.ThrowMappingException(m_errorLog, m_config);

                return false;
            }

            return (isDisjointLeft || isDisjointRight);
        }
        private bool IsContainedIn(CellTreeNode n1, CellTreeNode n2)
        {
            // Decide whether to IJ or LOJ using the domains that are filtered by the active domain
            // The net effect is that some unneeded multiconstants will be pruned away in IJ/LOJ
            // It is desirable to do so since we are only interested in the active domain
            var n1Active = LeftQP.Intersect(n1.LeftFragmentQuery, m_activeDomain);
            var n2Active = LeftQP.Intersect(n2.LeftFragmentQuery, m_activeDomain);

            var isContainedLeft = LeftQP.IsContainedIn(n1Active, n2Active);

            if (isContainedLeft)
            {
                return true;
            }

            CellTreeNode n = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.LASJ, n1, n2);
            var isContainedRight = n.IsEmptyRightFragmentQuery;

            return isContainedRight;
        }
        // requires: opTypeToIsolate must be LOJ, IJ, or Union
        // effects: Given a tree rooted at rootNode, determines if there
        // are any FOJs that can be replaced by opTypeToIsolate. If so,
        // does that and a returns a new tree with the replaced operators
        // Note: Method may modify rootNode's contents and children
        internal CellTreeNode IsolateByOperator(CellTreeNode rootNode, CellTreeOpType opTypeToIsolate)
        {
            Debug.Assert(
                opTypeToIsolate == CellTreeOpType.IJ || opTypeToIsolate == CellTreeOpType.LOJ
                || opTypeToIsolate == CellTreeOpType.Union,
                "IsolateJoins can only be called for IJs, LOJs, and Unions");

            var children = rootNode.Children;
            if (children.Count <= 1)
            {
                // No child or one child -  do nothing
                return rootNode;
            }

            // Replace the FOJs with IJs/LOJs/Unions in the children's subtrees first
            for (var i = 0; i < children.Count; i++)
            {
                // Method modifies input as well
                children[i] = IsolateByOperator(children[i], opTypeToIsolate);
            }
            // Only FOJs and LOJs can be coverted (to IJs, Unions, LOJs) --
            // so if the node is not that, we can ignore it (or if the node is already of
            // the same type that we want)
            if (rootNode.OpType != CellTreeOpType.FOJ && rootNode.OpType != CellTreeOpType.LOJ
                ||
                rootNode.OpType == opTypeToIsolate)
            {
                return rootNode;
            }

            // Create a new node with the same type as the input cell node type
            var newRootNode = new OpCellTreeNode(m_viewgenContext, rootNode.OpType);

            // We start a new "group" with one of the children X - we create
            // a newChildNode with type "opTypeToIsolate". Then we
            // determine if any of the remaining children should be in the
            // same group as X.

            // childrenSet keeps track of the children that need to be procesed/partitioned
            var childrenSet = new ModifiableIteratorCollection<CellTreeNode>(children);

            // Find groups with same or subsumed constants and create a join
            // or union node for them. We do this so that some of the FOJs
            // can be replaced by union and join nodes
            // 
            while (false == childrenSet.IsEmpty)
            {
                // Start a new "group" with some child  node (for the opTypeToIsolate node type)

                var groupNode = new OpCellTreeNode(m_viewgenContext, opTypeToIsolate);
                var someChild = childrenSet.RemoveOneElement();
                groupNode.Add(someChild);

                // Go through the remaining children and determine if their
                // constants are subsets/equal/disjoint w.r.t the joinNode
                // constants.

                foreach (var child in childrenSet.Elements())
                {
                    // Check if we can add the child as part of this
                    // groupNode (with opTypeToIsolate being LOJ, IJ, or Union)
                    if (TryAddChildToGroup(opTypeToIsolate, child, groupNode))
                    {
                        childrenSet.RemoveCurrentOfIterator();

                        // For LOJ, suppose that child A did not subsume B or
                        // vice-versa. But child C subsumes both. To ensure
                        // that we can get A, B, C in the same group, we
                        // reset the iterator so that when C is added in B's
                        // loop, we can reconsider A.
                        //
                        // For IJ, adding a child to groupNode does not change the range of it,
                        // so there is no need to reconsider previously skipped children.
                        //
                        // For Union, adding a child to groupNode increases the range of the groupNode,
                        // hence previously skipped (because they weren't disjoint with groupNode) children will continue 
                        // being ignored because they would still have an overlap with one of the nodes inside groupNode.

                        if (opTypeToIsolate == CellTreeOpType.LOJ)
                        {
                            childrenSet.ResetIterator();
                        }
                    }
                }
                // The new Union/LOJ/IJ node needs to be connected to the root
                newRootNode.Add(groupNode);
            }
            return newRootNode.Flatten();
        }
        // effects: Determines if the childNode can be added as a child of the
        // groupNode using te operation "opTypeToIsolate". E.g., if
        // opTypeToIsolate is inner join, we can add child to group node if
        // childNode and groupNode have the same multiconstantsets, i.e., they have
        // the same selection condition
        // Modifies groupNode to contain groupNode at the appropriate
        // position (for LOJs, the child could be added to the beginning)
        private bool TryAddChildToGroup(
            CellTreeOpType opTypeToIsolate, CellTreeNode childNode,
            OpCellTreeNode groupNode)
        {
            switch (opTypeToIsolate)
            {
                case CellTreeOpType.IJ:
                    // For Inner join, the constants of the node and
                    // the child must be the same, i.e., if the cells
                    // are producing exactly same tuples (same selection)
                    if (IsEquivalentTo(childNode, groupNode))
                    {
                        groupNode.Add(childNode);
                        return true;
                    }
                    break;

                case CellTreeOpType.LOJ:
                    // If one cell's selection condition subsumes
                    // another, we can use LOJ. We need to check for
                    // "subsumes" on both sides
                    if (IsContainedIn(childNode, groupNode))
                    {
                        groupNode.Add(childNode);
                        return true;
                    }
                    else if (IsContainedIn(groupNode, childNode))
                    {
                        // child subsumes the whole group -- add it first
                        groupNode.AddFirst(childNode);
                        return true;
                    }
                    break;

                case CellTreeOpType.Union:
                    // If the selection conditions are disjoint, we can use UNION ALL
                    // We cannot use active domain here; disjointness is guaranteed only
                    // if we check the entire selection domain
                    if (IsDisjoint(childNode, groupNode))
                    {
                        groupNode.Add(childNode);
                        return true;
                    }
                    break;
            }
            return false;
        }
        // requires: cellTreeNode has a tree such that all its intermediate nodes
        // are FOJ nodes only
        // effects: Converts the tree rooted at rootNode (recursively) in
        // following way and returns a new rootNode -- it partitions
        // rootNode's children such that no two different partitions have
        // any overlapping constants. These partitions are connected by Union
        // nodes (since there is no overlapping).
        // Note: Method may modify rootNode's contents and children
        private CellTreeNode IsolateUnions(CellTreeNode rootNode)
        {
            if (rootNode.Children.Count <= 1)
            {
                // No partitioning of children needs to be done
                return rootNode;
            }

            Debug.Assert(rootNode.OpType == CellTreeOpType.FOJ, "So far, we have FOJs only");

            // Recursively, transform the subtrees rooted at cellTreeNode's children
            for (var i = 0; i < rootNode.Children.Count; i++)
            {
                // Method modifies input as well
                rootNode.Children[i] = IsolateUnions(rootNode.Children[i]);
            }

            // Different children groups are connected by a Union
            // node -- the secltion domain of one group is disjoint from
            // another group's selection domain, i.e., group A1 contributes
            // tuples to the extent which are disjoint from the tuples by
            // A2. So we can connect these groups by union alls.
            // Inside each group, we continue to connect children of the same
            // group using FOJ
            var unionNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.Union);

            // childrenSet keeps track of the children that need to be procesed/partitioned
            var childrenSet = new ModifiableIteratorCollection<CellTreeNode>(rootNode.Children);

            while (false == childrenSet.IsEmpty)
            {
                // Start a new group
                // Make an FOJ node to connect children of the same group
                var fojNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.FOJ);

                // Add one of the root's children as a child to the foj node
                var someChild = childrenSet.RemoveOneElement();
                fojNode.Add(someChild);

                // We now want a transitive closure of the overlap between the 
                // the children node. We keep checking each child with the
                // fojNode and add it as a child of fojNode if there is an
                // overlap. Note that when a node is added to the fojNode,
                // its constants are propagated to the fojNode -- so we do
                // get transitive closure in terms of intersection 
                foreach (var child in childrenSet.Elements())
                {
                    if (!IsDisjoint(fojNode, child))
                    {
                        fojNode.Add(child);
                        childrenSet.RemoveCurrentOfIterator();
                        // To ensure that we get all overlapping node, we
                        // need to restart checking all the children
                        childrenSet.ResetIterator();
                    }
                }
                // Now we have a group of children nodes rooted at
                // fojNode. Add this fojNode to the union
                unionNode.Add(fojNode);
            }

            // The union node as the root of the view
            var result = unionNode.Flatten();
            return result;
        }
        private CellTreeNode ConvertUnionsToNormalizedLOJs(CellTreeNode rootNode)
        {
            // Recursively, transform the subtrees rooted at rootNode's children.
            for (var i = 0; i < rootNode.Children.Count; i++)
            {
                // Method modifies input as well.
                rootNode.Children[i] = ConvertUnionsToNormalizedLOJs(rootNode.Children[i]);
            }

            // We rewrite only LOJs.
            if (rootNode.OpType != CellTreeOpType.LOJ
                || rootNode.Children.Count < 2)
            {
                return rootNode;
            }

            // Create the resulting LOJ node.
            var result = new OpCellTreeNode(m_viewgenContext, rootNode.OpType);

            // Create working collection for the LOJ children.
            var children = new List<CellTreeNode>();

            // If rootNode looks something like ((V0 IJ V1) LOJ V2 LOJ V3),
            // and it turns out that there are FK associations from V2 or V3 pointing, let's say at V0,
            // then we want to rewrite the result as (V1 IJ (V0 LOJ V2 LOJ V3)).
            // If we don't do this, then plan compiler won't have a chance to eliminate LOJ V2 LOJ V3.
            // Hence, flatten the first child or rootNode if it's IJ, but remember that its parts are driving nodes for the LOJ,
            // so that we don't accidentally nest them.
            OpCellTreeNode resultIJDriver = null;
            HashSet<CellTreeNode> resultIJDriverChildren = null;
            if (rootNode.Children[0].OpType
                == CellTreeOpType.IJ)
            {
                // Create empty resultIJDriver node and add it as the first child (driving) into the LOJ result.
                resultIJDriver = new OpCellTreeNode(m_viewgenContext, rootNode.Children[0].OpType);
                result.Add(resultIJDriver);

                children.AddRange(rootNode.Children[0].Children);
                resultIJDriverChildren = new HashSet<CellTreeNode>(rootNode.Children[0].Children);
            }
            else
            {
                result.Add(rootNode.Children[0]);
            }

            // Flatten unions in non-driving nodes: (V0 LOJ (V1 Union V2 Union V3)) -> (V0 LOJ V1 LOJ V2 LOJ V3) 
            foreach (var child in rootNode.Children.Skip(1))
            {
                var opNode = child as OpCellTreeNode;
                if (opNode != null
                    && opNode.OpType == CellTreeOpType.Union)
                {
                    children.AddRange(opNode.Children);
                }
                else
                {
                    children.Add(child);
                }
            }

            // A dictionary that maps an extent to the nodes that are from that extent.
            // We want a ref comparer here.
            var extentMap = new KeyToListMap<EntitySet, LeafCellTreeNode>(EqualityComparer<EntitySet>.Default);
            // Note that we skip non-leaf nodes (non-leaf nodes don't have FKs) and attach them directly to the result.
            foreach (var child in children)
            {
                var leaf = child as LeafCellTreeNode;
                if (leaf != null)
                {
                    EntitySetBase extent = GetLeafNodeTable(leaf);
                    if (extent != null)
                    {
                        extentMap.Add((EntitySet)extent, leaf);
                    }
                }
                else
                {
                    if (resultIJDriverChildren != null
                        && resultIJDriverChildren.Contains(child))
                    {
                        resultIJDriver.Add(child);
                    }
                    else
                    {
                        result.Add(child);
                    }
                }
            }

            // We only deal with simple cases - one node per extent, remove the rest from children and attach directly to result.
            var nonTrivial = extentMap.KeyValuePairs.Where(m => m.Value.Count > 1).ToArray();
            foreach (var m in nonTrivial)
            {
                extentMap.RemoveKey(m.Key);
                foreach (var n in m.Value)
                {
                    if (resultIJDriverChildren != null
                        && resultIJDriverChildren.Contains(n))
                    {
                        resultIJDriver.Add(n);
                    }
                    else
                    {
                        result.Add(n);
                    }
                }
            }
            Debug.Assert(extentMap.KeyValuePairs.All(m => m.Value.Count == 1), "extentMap must map to single nodes only.");

            // Walk the extents in extentMap and for each extent build PK -> FK1(PK1), FK2(PK2), ... map
            // where PK is the primary key of the left extent, and FKn(PKn) is an FK of a right extent that 
            // points to the PK of the left extent and is based on the PK columns of the right extent.
            // Example:
            //           table tBaseType(Id int, c1 int), PK = (tBaseType.Id)
            //           table tDerivedType1(Id int, c2 int), PK1 = (tDerivedType1.Id), FK1 = (tDerivedType1.Id -> tBaseType.Id)
            //           table tDerivedType2(Id int, c3 int), PK2 = (tDerivedType2.Id), FK2 = (tDerivedType2.Id -> tBaseType.Id)
            // Will produce:
            //           (tBaseType) -> (tDerivedType1, tDerivedType2)
            var pkFkMap = new KeyToListMap<EntitySet, EntitySet>(EqualityComparer<EntitySet>.Default);
            // Also for each extent in extentMap, build another map (extent) -> (LOJ node).
            // It will be used to construct the nesting in the next step.
            var extentLOJs = new Dictionary<EntitySet, OpCellTreeNode>(EqualityComparer<EntitySet>.Default);
            foreach (var extentInfo in extentMap.KeyValuePairs)
            {
                var principalExtent = extentInfo.Key;
                foreach (var fkExtent in GetFKOverPKDependents(principalExtent))
                {
                    // Only track fkExtents that are in extentMap.
                    ReadOnlyCollection<LeafCellTreeNode> nodes;
                    if (extentMap.TryGetListForKey(fkExtent, out nodes))
                    {
                        // Make sure that we are not adding resultIJDriverChildren as FK dependents - we do not want them to get nested.
                        if (resultIJDriverChildren == null
                            || !resultIJDriverChildren.Contains(nodes.Single()))
                        {
                            pkFkMap.Add(principalExtent, fkExtent);
                        }
                    }
                }
                var extentLojNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.LOJ);
                extentLojNode.Add(extentInfo.Value.Single());
                extentLOJs.Add(principalExtent, extentLojNode);
            }

            // Construct LOJ nesting inside extentLOJs based on the information in pkFkMap.
            // Also, track nested extents using nestedExtents.
            // Example:
            // We start with nestedExtents empty extentLOJs as such:
            //      tBaseType -> LOJ(BaseTypeNode)
            //      tDerivedType1 -> LOJ(DerivedType1Node)*
            //      tDerivedType2 -> LOJ(DerivedType2Node)**
            // Note that * and ** represent object references. So each time something is nested, 
            // we don't clone, but nest the original LOJ. When we get to processing the extent of that LOJ,
            // we might add other children to that nested LOJ.
            // As we walk pkFkMap, we end up with this:
            //      tBaseType -> LOJ(BaseTypeNode, LOJ(DerivedType1Node)*, LOJ(DerivedType2Node)**)
            //      tDerivedType1 -> LOJ(DerivedType1Node)*
            //      tDerivedType2 -> LOJ(DerivedType2Node)**
            // nestedExtens = (tDerivedType1, tDerivedType2)
            var nestedExtents = new Dictionary<EntitySet, EntitySet>(EqualityComparer<EntitySet>.Default);
            foreach (var m in pkFkMap.KeyValuePairs)
            {
                var principalExtent = m.Key;
                foreach (var fkExtent in m.Value)
                {
                    OpCellTreeNode fkExtentLOJ;
                    if (extentLOJs.TryGetValue(fkExtent, out fkExtentLOJ) &&
                        // make sure we don't nest twice and we don't create a cycle.
                        !nestedExtents.ContainsKey(fkExtent)
                        && !CheckLOJCycle(fkExtent, principalExtent, nestedExtents))
                    {
                        extentLOJs[m.Key].Add(fkExtentLOJ);
                        nestedExtents.Add(fkExtent, principalExtent);
                    }
                }
            }

            // Now we need to grab the LOJs that have not been nested and add them to the result.
            // All LOJs that have been nested must be somewhere inside the LOJs that have not been nested,
            // so they as well end up in the result as part of the unnested ones.
            foreach (var m in extentLOJs)
            {
                if (!nestedExtents.ContainsKey(m.Key))
                {
                    // extentLOJ represents (Vx LOJ Vy LOJ(Vm LOJ Vn)) where Vx is the original node from rootNode.Children or resultIJDriverChildren.
                    var extentLOJ = m.Value;
                    if (resultIJDriverChildren != null
                        && resultIJDriverChildren.Contains(extentLOJ.Children[0]))
                    {
                        resultIJDriver.Add(extentLOJ);
                    }
                    else
                    {
                        result.Add(extentLOJ);
                    }
                }
            }

            return result.Flatten();
        }
        // effects: Restructure tree so that it is better positioned for merges
        private CellTreeNode RestructureTreeForMerges(CellTreeNode rootNode)
        {
            List<CellTreeNode> children = rootNode.Children;
            if (CellTreeNode.IsAssociativeOp(rootNode.OpType) == false || children.Count <= 1)
            {
                return rootNode;
            }

            // If this node's operator is associative and each child's
            // operator is also associative, check if there is a common set
            // of leaf nodes across all grandchildren

            Set<LeafCellTreeNode> commonGrandChildren = GetCommonGrandChildren(children);
            if (commonGrandChildren == null)
            {
                return rootNode;
            }

            CellTreeOpType commonChildOpType = children[0].OpType;

            //  We do have the structure that we are looking for
            // (common op2 gc2) op1 (common op2 gc3) op1 (common op2 gc4) becomes
            // common op2 (gc2 op1 gc3 op1 gc4)
            // e.g., (A IJ B IJ X IJ Y) UNION (A IJ B IJ Y IJ Z) UNION (A IJ B IJ R IJ S)
            // becomes A IJ B IJ ((X IJ Y) UNION (Y IJ Z) UNION (R IJ S))

            // From each child in children, get the nodes other than commonGrandChildren - these are gc2, gc3, ...
            // Each gc2 must be connected by op2 as before, i.e., ABC + ACD = A(BC + CD)

            // All children must be OpCellTreeNodes!
            List<OpCellTreeNode> newChildren = new List<OpCellTreeNode>(children.Count);
            foreach (OpCellTreeNode child in children)
            {
                // Remove all children in child that belong to commonGrandChildren
                // All grandChildren must be leaf nodes at this point
                List<LeafCellTreeNode> newGrandChildren = new List<LeafCellTreeNode>(child.Children.Count);
                foreach (LeafCellTreeNode grandChild in child.Children)
                {
                    if (commonGrandChildren.Contains(grandChild) == false)
                    {
                        newGrandChildren.Add(grandChild);
                    }
                }
                // In the above example, child.OpType is IJ
                Debug.Assert(child.OpType == commonChildOpType);
                OpCellTreeNode newChild = new OpCellTreeNode(m_viewgenContext, child.OpType,
                                                             Helpers.AsSuperTypeList<LeafCellTreeNode, CellTreeNode>(newGrandChildren));
                newChildren.Add(newChild);
            }
            // Connect gc2 op1 gc3 op1 gc4 - op1 is UNION in this
            // ((X IJ Y) UNION (Y IJ Z) UNION (R IJ S))
            // rootNode.Type is UNION
            CellTreeNode remainingNodes = new OpCellTreeNode(m_viewgenContext, rootNode.OpType,
                                                             Helpers.AsSuperTypeList<OpCellTreeNode, CellTreeNode>(newChildren));
            // Take the common grandchildren and connect via commonChildType
            // i.e., A IJ B
            CellTreeNode commonNodes = new OpCellTreeNode(m_viewgenContext, commonChildOpType,
                                                            Helpers.AsSuperTypeList<LeafCellTreeNode, CellTreeNode>(commonGrandChildren));

            // Connect both by commonChildType
            CellTreeNode result = new OpCellTreeNode(m_viewgenContext, commonChildOpType,
                                                     new CellTreeNode[] { commonNodes, remainingNodes });

            result = result.AssociativeFlatten();
            return result;
        }
        // requires: The tree rooted at cellTreeNode is an FOJ tree of
        // LeafCellTreeNodes only, i.e., there is an FOJ node with the
        // children being LeafCellTreeNodes
        // 
        // effects: Given a tree rooted at rootNode, ensures that cells
        // of the same right extent are placed in their own subtree below
        // cellTreeNode. That is, if there are 3 cells of extent A and 2 of
        // extent B (i.e., 5 cells with an FOJ on it), the resulting tree has
        // an FOJ node with two children -- FOJ nodes. These FOJ nodes have 2
        // and 3 children
        internal CellTreeNode GroupByRightExtent(CellTreeNode rootNode)
        {
            // A dictionary that maps an extent to the nodes are from that extent
            // We want a ref comparer here
            var extentMap =
                new KeyToListMap<EntitySetBase, LeafCellTreeNode>(EqualityComparer<EntitySetBase>.Default);

            // CR_Meek_Low: method can be simplified (Map<Extent, OpCellTreeNode>, populate as you go)
            // (becomes self-documenting)
            // For each leaf child, find the extent of the child and place it
            // in extentMap
            foreach (LeafCellTreeNode childNode in rootNode.Children)
            {
                // A cell may contain P, P.PA -- we return P
                // CHANGE_ADYA_FEATURE_COMPOSITION Need to fix for composition!!
                var extent = childNode.LeftCellWrapper.RightCellQuery.Extent; // relation or extent to group by
                Debug.Assert(extent != null, "Each cell must have a right extent");

                // Add the childNode as a child of the FOJ tree for "extent"
                extentMap.Add(extent, childNode);
            }
            // Now go through the extent map and create FOJ nodes for each extent
            // Place the nodes for that extent in the newly-created FOJ subtree
            // Also add the op node for every node as a child of the final result
            var result = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.FOJ);

            foreach (var extent in extentMap.Keys)
            {
                var extentFojNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.FOJ);
                foreach (var childNode in extentMap.ListForKey(extent))
                {
                    extentFojNode.Add(childNode);
                }
                result.Add(extentFojNode);
            }
            // We call Flatten to remove any unnecessary nestings
            // where an OpNode has only 1 child.
            return result.Flatten();
        }
Example #18
0
        // requires: opTypeToIsolate must be LOJ, IJ, or Union
        // effects: Given a tree rooted at rootNode, determines if there
        // are any FOJs that can be replaced by opTypeToIsolate. If so,
        // does that and a returns a new tree with the replaced operators
        // Note: Method may modify rootNode's contents and children
        internal CellTreeNode IsolateByOperator(CellTreeNode rootNode, CellTreeOpType opTypeToIsolate)
        {
            Debug.Assert(
                opTypeToIsolate == CellTreeOpType.IJ || opTypeToIsolate == CellTreeOpType.LOJ ||
                opTypeToIsolate == CellTreeOpType.Union,
                "IsolateJoins can only be called for IJs, LOJs, and Unions");

            var children = rootNode.Children;

            if (children.Count <= 1)
            {
                // No child or one child -  do nothing
                return(rootNode);
            }

            // Replace the FOJs with IJs/LOJs/Unions in the children's subtrees first
            for (var i = 0; i < children.Count; i++)
            {
                // Method modifies input as well
                children[i] = IsolateByOperator(children[i], opTypeToIsolate);
            }
            // Only FOJs and LOJs can be coverted (to IJs, Unions, LOJs) --
            // so if the node is not that, we can ignore it (or if the node is already of
            // the same type that we want)
            if (rootNode.OpType != CellTreeOpType.FOJ && rootNode.OpType != CellTreeOpType.LOJ
                ||
                rootNode.OpType == opTypeToIsolate)
            {
                return(rootNode);
            }

            // Create a new node with the same type as the input cell node type
            var newRootNode = new OpCellTreeNode(m_viewgenContext, rootNode.OpType);

            // We start a new "group" with one of the children X - we create
            // a newChildNode with type "opTypeToIsolate". Then we
            // determine if any of the remaining children should be in the
            // same group as X.

            // childrenSet keeps track of the children that need to be procesed/partitioned
            var childrenSet = new ModifiableIteratorCollection <CellTreeNode>(children);

            // Find groups with same or subsumed constants and create a join
            // or union node for them. We do this so that some of the FOJs
            // can be replaced by union and join nodes
            //
            while (false == childrenSet.IsEmpty)
            {
                // Start a new "group" with some child  node (for the opTypeToIsolate node type)

                var groupNode = new OpCellTreeNode(m_viewgenContext, opTypeToIsolate);
                var someChild = childrenSet.RemoveOneElement();
                groupNode.Add(someChild);

                // Go through the remaining children and determine if their
                // constants are subsets/equal/disjoint w.r.t the joinNode
                // constants.

                foreach (var child in childrenSet.Elements())
                {
                    // Check if we can add the child as part of this
                    // groupNode (with opTypeToIsolate being LOJ, IJ, or Union)
                    if (TryAddChildToGroup(opTypeToIsolate, child, groupNode))
                    {
                        childrenSet.RemoveCurrentOfIterator();

                        // For LOJ, suppose that child A did not subsume B or
                        // vice-versa. But child C subsumes both. To ensure
                        // that we can get A, B, C in the same group, we
                        // reset the iterator so that when C is added in B's
                        // loop, we can reconsider A.
                        //
                        // For IJ, adding a child to groupNode does not change the range of it,
                        // so there is no need to reconsider previously skipped children.
                        //
                        // For Union, adding a child to groupNode increases the range of the groupNode,
                        // hence previously skipped (because they weren't disjoint with groupNode) children will continue
                        // being ignored because they would still have an overlap with one of the nodes inside groupNode.

                        if (opTypeToIsolate == CellTreeOpType.LOJ)
                        {
                            childrenSet.ResetIterator();
                        }
                    }
                }
                // The new Union/LOJ/IJ node needs to be connected to the root
                newRootNode.Add(groupNode);
            }
            return(newRootNode.Flatten());
        }
Example #19
0
        // requires: cellTreeNode has a tree such that all its intermediate nodes
        // are FOJ nodes only
        // effects: Converts the tree rooted at rootNode (recursively) in
        // following way and returns a new rootNode -- it partitions
        // rootNode's children such that no two different partitions have
        // any overlapping constants. These partitions are connected by Union
        // nodes (since there is no overlapping).
        // Note: Method may modify rootNode's contents and children
        private CellTreeNode IsolateUnions(CellTreeNode rootNode)
        {
            if (rootNode.Children.Count <= 1)
            {
                // No partitioning of children needs to be done
                return(rootNode);
            }

            Debug.Assert(rootNode.OpType == CellTreeOpType.FOJ, "So far, we have FOJs only");

            // Recursively, transform the subtrees rooted at cellTreeNode's children
            for (var i = 0; i < rootNode.Children.Count; i++)
            {
                // Method modifies input as well
                rootNode.Children[i] = IsolateUnions(rootNode.Children[i]);
            }

            // Different children groups are connected by a Union
            // node -- the secltion domain of one group is disjoint from
            // another group's selection domain, i.e., group A1 contributes
            // tuples to the extent which are disjoint from the tuples by
            // A2. So we can connect these groups by union alls.
            // Inside each group, we continue to connect children of the same
            // group using FOJ
            var unionNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.Union);

            // childrenSet keeps track of the children that need to be procesed/partitioned
            var childrenSet = new ModifiableIteratorCollection <CellTreeNode>(rootNode.Children);

            while (false == childrenSet.IsEmpty)
            {
                // Start a new group
                // Make an FOJ node to connect children of the same group
                var fojNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.FOJ);

                // Add one of the root's children as a child to the foj node
                var someChild = childrenSet.RemoveOneElement();
                fojNode.Add(someChild);

                // We now want a transitive closure of the overlap between the
                // the children node. We keep checking each child with the
                // fojNode and add it as a child of fojNode if there is an
                // overlap. Note that when a node is added to the fojNode,
                // its constants are propagated to the fojNode -- so we do
                // get transitive closure in terms of intersection
                foreach (var child in childrenSet.Elements())
                {
                    if (!IsDisjoint(fojNode, child))
                    {
                        fojNode.Add(child);
                        childrenSet.RemoveCurrentOfIterator();
                        // To ensure that we get all overlapping node, we
                        // need to restart checking all the children
                        childrenSet.ResetIterator();
                    }
                }
                // Now we have a group of children nodes rooted at
                // fojNode. Add this fojNode to the union
                unionNode.Add(fojNode);
            }

            // The union node as the root of the view
            var result = unionNode.Flatten();

            return(result);
        }
Example #20
0
        private void GenerateCaseStatements(
            IEnumerable<MemberPath> members,
            HashSet<FragmentQuery> outputUsedViews)
        {
            // Compute right domain query - non-simplified version of "basic view"
            // It is used below to check whether we need a default value in a case statement
            var usedCells = _context.AllWrappersForExtent.Where(w => _usedViews.Contains(w.FragmentQuery));
            CellTreeNode rightDomainQuery = new OpCellTreeNode(
                _context, CellTreeOpType.Union,
                usedCells.Select(wrapper => new LeafCellTreeNode(_context, wrapper)).ToArray());

            foreach (var currentPath in members)
            {
                // Add the types can member have, i.e., its type and its subtypes
                var domain = GetDomain(currentPath).ToList();
                var caseStatement = new CaseStatement(currentPath);

                Tile<FragmentQuery> unionCaseRewriting = null;

                // optimization for domain = {NULL, NOT_NULL}
                // Create a single case: WHEN True THEN currentPath
                // Reason: if the WHEN condition is not satisfied (say because of LOJ), then currentPath = NULL
                var needCaseStatement =
                    !(domain.Count == 2 &&
                      domain.Contains(Constant.Null, Constant.EqualityComparer) &&
                      domain.Contains(Constant.NotNull, Constant.EqualityComparer));
                {
                    // go over the domain
                    foreach (var domainValue in domain)
                    {
                        if (domainValue == Constant.Undefined
                            && _context.ViewTarget == ViewTarget.QueryView)
                        {
                            // we cannot assume closed domain for query views;
                            // if obtaining undefined is possible, we need to account for that
                            caseStatement.AddWhenThen(
                                BoolExpression.False /* arbitrary condition */,
                                new ConstantProjectedSlot(Constant.Undefined));
                            continue;
                        }
                        TraceVerbose("CASE STATEMENT FOR {0}={1}", currentPath, domainValue);

                        // construct WHERE clause for this value
                        var memberConditionQuery = CreateMemberConditionQuery(currentPath, domainValue);

                        Tile<FragmentQuery> caseRewriting;
                        if (FindRewritingAndUsedViews(
                            memberConditionQuery.Attributes, memberConditionQuery.Condition, outputUsedViews, out caseRewriting))
                        {
                            if (_context.ViewTarget
                                == ViewTarget.UpdateView)
                            {
                                unionCaseRewriting = (unionCaseRewriting != null)
                                                         ? _qp.Union(unionCaseRewriting, caseRewriting)
                                                         : caseRewriting;
                            }

                            if (needCaseStatement)
                            {
                                var isAlwaysTrue = AddRewritingToCaseStatement(caseRewriting, caseStatement, currentPath, domainValue);
                                if (isAlwaysTrue)
                                {
                                    break;
                                }
                            }
                        }
                        else
                        {
                            if (!IsDefaultValue(domainValue, currentPath))
                            {
                                Debug.Assert(_context.ViewTarget == ViewTarget.UpdateView || !_config.IsValidationEnabled);

                                if (!ErrorPatternMatcher.FindMappingErrors(_context, _domainMap, _errorLog))
                                {
                                    var builder = new StringBuilder();
                                    var extentName = StringUtil.FormatInvariant("{0}", _extentPath);
                                    var objectString = _context.ViewTarget == ViewTarget.QueryView
                                                           ? Strings.ViewGen_Entities
                                                           : Strings.ViewGen_Tuples;

                                    if (_context.ViewTarget
                                        == ViewTarget.QueryView)
                                    {
                                        builder.AppendLine(Strings.Viewgen_CannotGenerateQueryViewUnderNoValidation(extentName));
                                    }
                                    else
                                    {
                                        builder.AppendLine(Strings.ViewGen_Cannot_Disambiguate_MultiConstant(objectString, extentName));
                                    }
                                    RewritingValidator.EntityConfigurationToUserString(
                                        memberConditionQuery.Condition, builder, _context.ViewTarget == ViewTarget.UpdateView);
                                    var record = new ErrorLog.Record(
                                        ViewGenErrorCode.AmbiguousMultiConstants, builder.ToString(), _context.AllWrappersForExtent,
                                        String.Empty);
                                    _errorLog.AddEntry(record);
                                }
                            }
                        }
                    }
                }

                if (_errorLog.Count == 0)
                {
                    // for update views, add WHEN True THEN defaultValue
                    // which will ultimately be translated into a (possibly implicit) ELSE clause
                    if (_context.ViewTarget == ViewTarget.UpdateView && needCaseStatement)
                    {
                        AddElseDefaultToCaseStatement(currentPath, caseStatement, domain, rightDomainQuery, unionCaseRewriting);
                    }

                    if (caseStatement.Clauses.Count > 0)
                    {
                        TraceVerbose("{0}", caseStatement.ToString());
                        _caseStatements[currentPath] = caseStatement;
                    }
                }
            }
        }
        // effects: Simplifies the tree rooted at rootNode and returns a new
        // tree -- it ensures that the returned tree has at most one node for
        // any particular extent unless the tree has nodes of the same extent
        // embedded two leaves below LASJ or LOJ, e.g., if we have a tree
        // (where Ni indicates a node for extent i - one Ni can be different
        // from anohter Ni: 
        // [N0 IJ N1] LASJ N0 --> This will not be simplified
        // canBooleansOverlap indicates whether an original input cell
        // contributes to multiple nodes in this tree, e.g., V1 IJ V2 UNION V2 IJ V3
        private CellTreeNode SimplifyTreeByMergingNodes(CellTreeNode rootNode)
        {

            if (rootNode is LeafCellTreeNode)
            { // View already simple!
                return rootNode;
            }
            Debug.Assert(rootNode.OpType == CellTreeOpType.LOJ || rootNode.OpType == CellTreeOpType.IJ ||
                         rootNode.OpType == CellTreeOpType.FOJ || rootNode.OpType == CellTreeOpType.Union ||
                         rootNode.OpType == CellTreeOpType.LASJ,
                         "Only handle these operations");

            // Before we apply any rule, check if we can improve the opportunity to
            // collapse the nodes
            rootNode = RestructureTreeForMerges(rootNode);

            List<CellTreeNode> children = rootNode.Children;
            Debug.Assert(children.Count > 0, "OpCellTreeNode has no children?");

            // Apply recursively
            for (int i = 0; i < children.Count; i++)
            {
                children[i] = SimplifyTreeByMergingNodes(children[i]);
            }

            // Essentially, we have a node with IJ, LOJ, U or FOJ type that
            // has some children. Check if some of the children can be merged
            // with one another using the corresponding TM/SP rule

            // Ops such as IJ, Union and FOJ are associative, i.e., A op (B
            // op C) is the same as (A op B) op C. This is not true for LOJ
            // and LASJ
            bool isAssociativeOp = CellTreeNode.IsAssociativeOp(rootNode.OpType);
            if (isAssociativeOp)
            {
                // Group all the leaf cells of an extent together so that we can
                // later simply run through them without running nested loops
                // We do not do this for LOJ/LASJ nodes since LOJ (LASJ) is not commutative
                // (or associative);
                children = GroupLeafChildrenByExtent(children);
            }
            else
            {
                children = GroupNonAssociativeLeafChildren(children);
            }

            // childrenSet keeps track of the children that need to be procesed/partitioned
            OpCellTreeNode newNode = new OpCellTreeNode(m_viewgenContext, rootNode.OpType);
            CellTreeNode lastChild = null;
            bool skipRest = false;
            foreach (CellTreeNode child in children)
            {
                if (lastChild == null)
                {
                    // First time in the loop. Just set lastChild
                    lastChild = child;
                    continue;
                }

                bool mergedOk = false;
                // try to merge lastChild and child
                if (false == skipRest && lastChild.OpType == CellTreeOpType.Leaf &&
                    child.OpType == CellTreeOpType.Leaf)
                {
                    // Both are cell queries. Can try to merge them
                    // We do not add lastChild since it could merge
                    // further. It will be added in a later loop or outside the loop
                    mergedOk = TryMergeCellQueries(rootNode.OpType, ref lastChild, child);
                }

                if (false == mergedOk)
                {
                    // No merge occurred. Simply add the previous child as it
                    // is (Note lastChild will be added in the next loop or if
                    // the loop finishes, outside the loop
                    newNode.Add(lastChild);
                    lastChild = child;
                    if (false == isAssociativeOp)
                    {
                        // LOJ is not associative:
                        // (P loj PA) loj PO != P loj (PA loj PO). The RHS does not have
                        // Persons who have orders but no addresses
                        skipRest = true;
                    }
                }
            }

            newNode.Add(lastChild);
            CellTreeNode result = newNode.AssociativeFlatten();
            return result;
        }
        // requires: node1 and node2 are two children of the same parent
        // connected by opType
        // effects: Given two cell tree nodes, node1 and node2, runs the
        // TM/SP rule on them to merge them (if they belong to the same
        // extent). Returns true if the merge succeeds
        private bool TryMergeCellQueries(CellTreeOpType opType, ref CellTreeNode node1,
                                         CellTreeNode node2)
        {

            LeafCellTreeNode leaf1 = node1 as LeafCellTreeNode;
            LeafCellTreeNode leaf2 = node2 as LeafCellTreeNode;

            Debug.Assert(leaf1 != null, "Merge only possible on leaf nodes (1)");
            Debug.Assert(leaf2 != null, "Merge only possible on leaf nodes (2)");

            CellQuery mergedLeftCellQuery;
            CellQuery mergedRightCellQuery;
            if (!TryMergeTwoCellQueries(leaf1.LeftCellWrapper.RightCellQuery, leaf2.LeftCellWrapper.RightCellQuery, opType, m_viewgenContext.MemberMaps.RightDomainMap, out mergedRightCellQuery))
            {
                return false;
            }

            if (!TryMergeTwoCellQueries(leaf1.LeftCellWrapper.LeftCellQuery, leaf2.LeftCellWrapper.LeftCellQuery, opType, m_viewgenContext.MemberMaps.LeftDomainMap, out mergedLeftCellQuery))
            {
                return false;
            }

            // Create a temporary node and add the two children
            // so that we can get the merged selectiondomains and attributes
            // Note that temp.SelectionDomain below determines the domain
            // based on the opType, e.g., for IJ, it intersects the
            // multiconstants of all the children
            OpCellTreeNode temp = new OpCellTreeNode(m_viewgenContext, opType);
            temp.Add(node1);
            temp.Add(node2);
            // Note: We are losing the original cell number information here and the line number information
            // But we will not raise any

            // We do not create CellExpressions with LOJ, FOJ - canBooleansOverlap is true for validation
            CellTreeOpType inputOpType = opType;
            if (opType == CellTreeOpType.FOJ || opType == CellTreeOpType.LOJ)
            {
                inputOpType = CellTreeOpType.IJ;
            }

            LeftCellWrapper wrapper = new LeftCellWrapper(m_viewgenContext.ViewTarget, temp.Attributes,
                                                          temp.LeftFragmentQuery,
                                                          mergedLeftCellQuery,
                                                          mergedRightCellQuery,
                                                          m_viewgenContext.MemberMaps,
                                                          leaf1.LeftCellWrapper.Cells.Concat(leaf2.LeftCellWrapper.Cells));
            node1 = new LeafCellTreeNode(m_viewgenContext, wrapper, temp.RightFragmentQuery);
            return true;
        }
Example #23
0
        private CellTreeNode ConvertUnionsToNormalizedLOJs(CellTreeNode rootNode)
        {
            // Recursively, transform the subtrees rooted at rootNode's children.
            for (var i = 0; i < rootNode.Children.Count; i++)
            {
                // Method modifies input as well.
                rootNode.Children[i] = ConvertUnionsToNormalizedLOJs(rootNode.Children[i]);
            }

            // We rewrite only LOJs.
            if (rootNode.OpType != CellTreeOpType.LOJ ||
                rootNode.Children.Count < 2)
            {
                return(rootNode);
            }

            // Create the resulting LOJ node.
            var result = new OpCellTreeNode(m_viewgenContext, rootNode.OpType);

            // Create working collection for the LOJ children.
            var children = new List <CellTreeNode>();

            // If rootNode looks something like ((V0 IJ V1) LOJ V2 LOJ V3),
            // and it turns out that there are FK associations from V2 or V3 pointing, let's say at V0,
            // then we want to rewrite the result as (V1 IJ (V0 LOJ V2 LOJ V3)).
            // If we don't do this, then plan compiler won't have a chance to eliminate LOJ V2 LOJ V3.
            // Hence, flatten the first child or rootNode if it's IJ, but remember that its parts are driving nodes for the LOJ,
            // so that we don't accidentally nest them.
            OpCellTreeNode         resultIJDriver         = null;
            HashSet <CellTreeNode> resultIJDriverChildren = null;

            if (rootNode.Children[0].OpType
                == CellTreeOpType.IJ)
            {
                // Create empty resultIJDriver node and add it as the first child (driving) into the LOJ result.
                resultIJDriver = new OpCellTreeNode(m_viewgenContext, rootNode.Children[0].OpType);
                result.Add(resultIJDriver);

                children.AddRange(rootNode.Children[0].Children);
                resultIJDriverChildren = new HashSet <CellTreeNode>(rootNode.Children[0].Children);
            }
            else
            {
                result.Add(rootNode.Children[0]);
            }

            // Flatten unions in non-driving nodes: (V0 LOJ (V1 Union V2 Union V3)) -> (V0 LOJ V1 LOJ V2 LOJ V3)
            foreach (var child in rootNode.Children.Skip(1))
            {
                var opNode = child as OpCellTreeNode;
                if (opNode != null &&
                    opNode.OpType == CellTreeOpType.Union)
                {
                    children.AddRange(opNode.Children);
                }
                else
                {
                    children.Add(child);
                }
            }

            // A dictionary that maps an extent to the nodes that are from that extent.
            // We want a ref comparer here.
            var extentMap = new KeyToListMap <EntitySet, LeafCellTreeNode>(EqualityComparer <EntitySet> .Default);

            // Note that we skip non-leaf nodes (non-leaf nodes don't have FKs) and attach them directly to the result.
            foreach (var child in children)
            {
                var leaf = child as LeafCellTreeNode;
                if (leaf != null)
                {
                    EntitySetBase extent = GetLeafNodeTable(leaf);
                    if (extent != null)
                    {
                        extentMap.Add((EntitySet)extent, leaf);
                    }
                }
                else
                {
                    if (resultIJDriverChildren != null &&
                        resultIJDriverChildren.Contains(child))
                    {
                        resultIJDriver.Add(child);
                    }
                    else
                    {
                        result.Add(child);
                    }
                }
            }

            // We only deal with simple cases - one node per extent, remove the rest from children and attach directly to result.
            var nonTrivial = extentMap.KeyValuePairs.Where(m => m.Value.Count > 1).ToArray();

            foreach (var m in nonTrivial)
            {
                extentMap.RemoveKey(m.Key);
                foreach (var n in m.Value)
                {
                    if (resultIJDriverChildren != null &&
                        resultIJDriverChildren.Contains(n))
                    {
                        resultIJDriver.Add(n);
                    }
                    else
                    {
                        result.Add(n);
                    }
                }
            }
            Debug.Assert(extentMap.KeyValuePairs.All(m => m.Value.Count == 1), "extentMap must map to single nodes only.");

            // Walk the extents in extentMap and for each extent build PK -> FK1(PK1), FK2(PK2), ... map
            // where PK is the primary key of the left extent, and FKn(PKn) is an FK of a right extent that
            // points to the PK of the left extent and is based on the PK columns of the right extent.
            // Example:
            //           table tBaseType(Id int, c1 int), PK = (tBaseType.Id)
            //           table tDerivedType1(Id int, c2 int), PK1 = (tDerivedType1.Id), FK1 = (tDerivedType1.Id -> tBaseType.Id)
            //           table tDerivedType2(Id int, c3 int), PK2 = (tDerivedType2.Id), FK2 = (tDerivedType2.Id -> tBaseType.Id)
            // Will produce:
            //           (tBaseType) -> (tDerivedType1, tDerivedType2)
            var pkFkMap = new KeyToListMap <EntitySet, EntitySet>(EqualityComparer <EntitySet> .Default);
            // Also for each extent in extentMap, build another map (extent) -> (LOJ node).
            // It will be used to construct the nesting in the next step.
            var extentLOJs = new Dictionary <EntitySet, OpCellTreeNode>(EqualityComparer <EntitySet> .Default);

            foreach (var extentInfo in extentMap.KeyValuePairs)
            {
                var principalExtent = extentInfo.Key;
                foreach (var fkExtent in GetFKOverPKDependents(principalExtent))
                {
                    // Only track fkExtents that are in extentMap.
                    ReadOnlyCollection <LeafCellTreeNode> nodes;
                    if (extentMap.TryGetListForKey(fkExtent, out nodes))
                    {
                        // Make sure that we are not adding resultIJDriverChildren as FK dependents - we do not want them to get nested.
                        if (resultIJDriverChildren == null ||
                            !resultIJDriverChildren.Contains(nodes.Single()))
                        {
                            pkFkMap.Add(principalExtent, fkExtent);
                        }
                    }
                }
                var extentLojNode = new OpCellTreeNode(m_viewgenContext, CellTreeOpType.LOJ);
                extentLojNode.Add(extentInfo.Value.Single());
                extentLOJs.Add(principalExtent, extentLojNode);
            }

            // Construct LOJ nesting inside extentLOJs based on the information in pkFkMap.
            // Also, track nested extents using nestedExtents.
            // Example:
            // We start with nestedExtents empty extentLOJs as such:
            //      tBaseType -> LOJ(BaseTypeNode)
            //      tDerivedType1 -> LOJ(DerivedType1Node)*
            //      tDerivedType2 -> LOJ(DerivedType2Node)**
            // Note that * and ** represent object references. So each time something is nested,
            // we don't clone, but nest the original LOJ. When we get to processing the extent of that LOJ,
            // we might add other children to that nested LOJ.
            // As we walk pkFkMap, we end up with this:
            //      tBaseType -> LOJ(BaseTypeNode, LOJ(DerivedType1Node)*, LOJ(DerivedType2Node)**)
            //      tDerivedType1 -> LOJ(DerivedType1Node)*
            //      tDerivedType2 -> LOJ(DerivedType2Node)**
            // nestedExtens = (tDerivedType1, tDerivedType2)
            var nestedExtents = new Dictionary <EntitySet, EntitySet>(EqualityComparer <EntitySet> .Default);

            foreach (var m in pkFkMap.KeyValuePairs)
            {
                var principalExtent = m.Key;
                foreach (var fkExtent in m.Value)
                {
                    OpCellTreeNode fkExtentLOJ;
                    if (extentLOJs.TryGetValue(fkExtent, out fkExtentLOJ)
                        &&
                        // make sure we don't nest twice and we don't create a cycle.
                        !nestedExtents.ContainsKey(fkExtent) &&
                        !CheckLOJCycle(fkExtent, principalExtent, nestedExtents))
                    {
                        extentLOJs[m.Key].Add(fkExtentLOJ);
                        nestedExtents.Add(fkExtent, principalExtent);
                    }
                }
            }

            // Now we need to grab the LOJs that have not been nested and add them to the result.
            // All LOJs that have been nested must be somewhere inside the LOJs that have not been nested,
            // so they as well end up in the result as part of the unnested ones.
            foreach (var m in extentLOJs)
            {
                if (!nestedExtents.ContainsKey(m.Key))
                {
                    // extentLOJ represents (Vx LOJ Vy LOJ(Vm LOJ Vn)) where Vx is the original node from rootNode.Children or resultIJDriverChildren.
                    var extentLOJ = m.Value;
                    if (resultIJDriverChildren != null &&
                        resultIJDriverChildren.Contains(extentLOJ.Children[0]))
                    {
                        resultIJDriver.Add(extentLOJ);
                    }
                    else
                    {
                        result.Add(extentLOJ);
                    }
                }
            }

            return(result.Flatten());
        }
Example #24
0
        // effects: Restructure tree so that it is better positioned for merges
        private CellTreeNode RestructureTreeForMerges(CellTreeNode rootNode)
        {
            var children = rootNode.Children;

            if (CellTreeNode.IsAssociativeOp(rootNode.OpType) == false ||
                children.Count <= 1)
            {
                return(rootNode);
            }

            // If this node's operator is associative and each child's
            // operator is also associative, check if there is a common set
            // of leaf nodes across all grandchildren

            var commonGrandChildren = GetCommonGrandChildren(children);

            if (commonGrandChildren == null)
            {
                return(rootNode);
            }

            var commonChildOpType = children[0].OpType;

            //  We do have the structure that we are looking for
            // (common op2 gc2) op1 (common op2 gc3) op1 (common op2 gc4) becomes
            // common op2 (gc2 op1 gc3 op1 gc4)
            // e.g., (A IJ B IJ X IJ Y) UNION (A IJ B IJ Y IJ Z) UNION (A IJ B IJ R IJ S)
            // becomes A IJ B IJ ((X IJ Y) UNION (Y IJ Z) UNION (R IJ S))

            // From each child in children, get the nodes other than commonGrandChildren - these are gc2, gc3, ...
            // Each gc2 must be connected by op2 as before, i.e., ABC + ACD = A(BC + CD)

            // All children must be OpCellTreeNodes!
            var newChildren = new List <OpCellTreeNode>(children.Count);

            foreach (OpCellTreeNode child in children)
            {
                // Remove all children in child that belong to commonGrandChildren
                // All grandChildren must be leaf nodes at this point
                var newGrandChildren = new List <LeafCellTreeNode>(child.Children.Count);
                foreach (LeafCellTreeNode grandChild in child.Children)
                {
                    if (commonGrandChildren.Contains(grandChild) == false)
                    {
                        newGrandChildren.Add(grandChild);
                    }
                }
                // In the above example, child.OpType is IJ
                Debug.Assert(child.OpType == commonChildOpType);
                var newChild = new OpCellTreeNode(
                    m_viewgenContext, child.OpType,
                    Helpers.AsSuperTypeList <LeafCellTreeNode, CellTreeNode>(newGrandChildren));
                newChildren.Add(newChild);
            }
            // Connect gc2 op1 gc3 op1 gc4 - op1 is UNION in this
            // ((X IJ Y) UNION (Y IJ Z) UNION (R IJ S))
            // rootNode.Type is UNION
            CellTreeNode remainingNodes = new OpCellTreeNode(
                m_viewgenContext, rootNode.OpType,
                Helpers.AsSuperTypeList <OpCellTreeNode, CellTreeNode>(newChildren));
            // Take the common grandchildren and connect via commonChildType
            // i.e., A IJ B
            CellTreeNode commonNodes = new OpCellTreeNode(
                m_viewgenContext, commonChildOpType,
                Helpers.AsSuperTypeList <LeafCellTreeNode, CellTreeNode>(commonGrandChildren));

            // Connect both by commonChildType
            CellTreeNode result = new OpCellTreeNode(
                m_viewgenContext, commonChildOpType,
                new[] { commonNodes, remainingNodes });

            result = result.AssociativeFlatten();
            return(result);
        }
Example #25
0
        // effects: Simplifies the tree rooted at rootNode and returns a new
        // tree -- it ensures that the returned tree has at most one node for
        // any particular extent unless the tree has nodes of the same extent
        // embedded two leaves below LASJ or LOJ, e.g., if we have a tree
        // (where Ni indicates a node for extent i - one Ni can be different
        // from anohter Ni:
        // [N0 IJ N1] LASJ N0 --> This will not be simplified
        // canBooleansOverlap indicates whether an original input cell
        // contributes to multiple nodes in this tree, e.g., V1 IJ V2 UNION V2 IJ V3
        private CellTreeNode SimplifyTreeByMergingNodes(CellTreeNode rootNode)
        {
            if (rootNode is LeafCellTreeNode)
            {
                // View already simple!
                return(rootNode);
            }
            Debug.Assert(
                rootNode.OpType == CellTreeOpType.LOJ || rootNode.OpType == CellTreeOpType.IJ ||
                rootNode.OpType == CellTreeOpType.FOJ || rootNode.OpType == CellTreeOpType.Union ||
                rootNode.OpType == CellTreeOpType.LASJ,
                "Only handle these operations");

            // Before we apply any rule, check if we can improve the opportunity to
            // collapse the nodes
            rootNode = RestructureTreeForMerges(rootNode);

            var children = rootNode.Children;

            Debug.Assert(children.Count > 0, "OpCellTreeNode has no children?");

            // Apply recursively
            for (var i = 0; i < children.Count; i++)
            {
                children[i] = SimplifyTreeByMergingNodes(children[i]);
            }

            // Essentially, we have a node with IJ, LOJ, U or FOJ type that
            // has some children. Check if some of the children can be merged
            // with one another using the corresponding TM/SP rule

            // Ops such as IJ, Union and FOJ are associative, i.e., A op (B
            // op C) is the same as (A op B) op C. This is not true for LOJ
            // and LASJ
            var isAssociativeOp = CellTreeNode.IsAssociativeOp(rootNode.OpType);

            if (isAssociativeOp)
            {
                // Group all the leaf cells of an extent together so that we can
                // later simply run through them without running nested loops
                // We do not do this for LOJ/LASJ nodes since LOJ (LASJ) is not commutative
                // (or associative);
                children = GroupLeafChildrenByExtent(children);
            }
            else
            {
                children = GroupNonAssociativeLeafChildren(children);
            }

            // childrenSet keeps track of the children that need to be procesed/partitioned
            var          newNode   = new OpCellTreeNode(m_viewgenContext, rootNode.OpType);
            CellTreeNode lastChild = null;
            var          skipRest  = false;

            foreach (var child in children)
            {
                if (lastChild == null)
                {
                    // First time in the loop. Just set lastChild
                    lastChild = child;
                    continue;
                }

                var mergedOk = false;
                // try to merge lastChild and child
                if (false == skipRest &&
                    lastChild.OpType == CellTreeOpType.Leaf
                    &&
                    child.OpType == CellTreeOpType.Leaf)
                {
                    // Both are cell queries. Can try to merge them
                    // We do not add lastChild since it could merge
                    // further. It will be added in a later loop or outside the loop
                    mergedOk = TryMergeCellQueries(rootNode.OpType, ref lastChild, child);
                }

                if (false == mergedOk)
                {
                    // No merge occurred. Simply add the previous child as it
                    // is (Note lastChild will be added in the next loop or if
                    // the loop finishes, outside the loop
                    newNode.Add(lastChild);
                    lastChild = child;
                    if (false == isAssociativeOp)
                    {
                        // LOJ is not associative:
                        // (P loj PA) loj PO != P loj (PA loj PO). The RHS does not have
                        // Persons who have orders but no addresses
                        skipRest = true;
                    }
                }
            }

            newNode.Add(lastChild);
            var result = newNode.AssociativeFlatten();

            return(result);
        }
        private CellTreeNode ConvertUnionsToNormalizedLOJs(CellTreeNode rootNode)
        {
            for (int index = 0; index < rootNode.Children.Count; ++index)
            {
                rootNode.Children[index] = this.ConvertUnionsToNormalizedLOJs(rootNode.Children[index]);
            }
            if (rootNode.OpType != CellTreeOpType.LOJ || rootNode.Children.Count < 2)
            {
                return(rootNode);
            }
            OpCellTreeNode         opCellTreeNode1  = new OpCellTreeNode(this.m_viewgenContext, rootNode.OpType);
            List <CellTreeNode>    cellTreeNodeList = new List <CellTreeNode>();
            OpCellTreeNode         opCellTreeNode2  = (OpCellTreeNode)null;
            HashSet <CellTreeNode> cellTreeNodeSet  = (HashSet <CellTreeNode>)null;

            if (rootNode.Children[0].OpType == CellTreeOpType.IJ)
            {
                opCellTreeNode2 = new OpCellTreeNode(this.m_viewgenContext, rootNode.Children[0].OpType);
                opCellTreeNode1.Add((CellTreeNode)opCellTreeNode2);
                cellTreeNodeList.AddRange((IEnumerable <CellTreeNode>)rootNode.Children[0].Children);
                cellTreeNodeSet = new HashSet <CellTreeNode>((IEnumerable <CellTreeNode>)rootNode.Children[0].Children);
            }
            else
            {
                opCellTreeNode1.Add(rootNode.Children[0]);
            }
            foreach (CellTreeNode cellTreeNode in rootNode.Children.Skip <CellTreeNode>(1))
            {
                OpCellTreeNode opCellTreeNode3 = cellTreeNode as OpCellTreeNode;
                if (opCellTreeNode3 != null && opCellTreeNode3.OpType == CellTreeOpType.Union)
                {
                    cellTreeNodeList.AddRange((IEnumerable <CellTreeNode>)opCellTreeNode3.Children);
                }
                else
                {
                    cellTreeNodeList.Add(cellTreeNode);
                }
            }
            KeyToListMap <EntitySet, LeafCellTreeNode> keyToListMap1 = new KeyToListMap <EntitySet, LeafCellTreeNode>((IEqualityComparer <EntitySet>)EqualityComparer <EntitySet> .Default);

            foreach (CellTreeNode child in cellTreeNodeList)
            {
                LeafCellTreeNode leaf = child as LeafCellTreeNode;
                if (leaf != null)
                {
                    EntitySetBase leafNodeTable = (EntitySetBase)BasicViewGenerator.GetLeafNodeTable(leaf);
                    if (leafNodeTable != null)
                    {
                        keyToListMap1.Add((EntitySet)leafNodeTable, leaf);
                    }
                }
                else if (cellTreeNodeSet != null && cellTreeNodeSet.Contains(child))
                {
                    opCellTreeNode2.Add(child);
                }
                else
                {
                    opCellTreeNode1.Add(child);
                }
            }
            foreach (KeyValuePair <EntitySet, List <LeafCellTreeNode> > keyValuePair in keyToListMap1.KeyValuePairs.Where <KeyValuePair <EntitySet, List <LeafCellTreeNode> > >((Func <KeyValuePair <EntitySet, List <LeafCellTreeNode> >, bool>)(m => m.Value.Count > 1)).ToArray <KeyValuePair <EntitySet, List <LeafCellTreeNode> > >())
            {
                keyToListMap1.RemoveKey(keyValuePair.Key);
                foreach (LeafCellTreeNode leafCellTreeNode in keyValuePair.Value)
                {
                    if (cellTreeNodeSet != null && cellTreeNodeSet.Contains((CellTreeNode)leafCellTreeNode))
                    {
                        opCellTreeNode2.Add((CellTreeNode)leafCellTreeNode);
                    }
                    else
                    {
                        opCellTreeNode1.Add((CellTreeNode)leafCellTreeNode);
                    }
                }
            }
            KeyToListMap <EntitySet, EntitySet>    keyToListMap2 = new KeyToListMap <EntitySet, EntitySet>((IEqualityComparer <EntitySet>)EqualityComparer <EntitySet> .Default);
            Dictionary <EntitySet, OpCellTreeNode> dictionary    = new Dictionary <EntitySet, OpCellTreeNode>((IEqualityComparer <EntitySet>)EqualityComparer <EntitySet> .Default);

            foreach (KeyValuePair <EntitySet, List <LeafCellTreeNode> > keyValuePair in keyToListMap1.KeyValuePairs)
            {
                EntitySet key = keyValuePair.Key;
                foreach (EntitySet fkOverPkDependent in BasicViewGenerator.GetFKOverPKDependents(key))
                {
                    ReadOnlyCollection <LeafCellTreeNode> valueCollection;
                    if (keyToListMap1.TryGetListForKey(fkOverPkDependent, out valueCollection) && (cellTreeNodeSet == null || !cellTreeNodeSet.Contains((CellTreeNode)valueCollection.Single <LeafCellTreeNode>())))
                    {
                        keyToListMap2.Add(key, fkOverPkDependent);
                    }
                }
                OpCellTreeNode opCellTreeNode3 = new OpCellTreeNode(this.m_viewgenContext, CellTreeOpType.LOJ);
                opCellTreeNode3.Add((CellTreeNode)keyValuePair.Value.Single <LeafCellTreeNode>());
                dictionary.Add(key, opCellTreeNode3);
            }
            Dictionary <EntitySet, EntitySet> nestedExtents = new Dictionary <EntitySet, EntitySet>((IEqualityComparer <EntitySet>)EqualityComparer <EntitySet> .Default);

            foreach (KeyValuePair <EntitySet, List <EntitySet> > keyValuePair in keyToListMap2.KeyValuePairs)
            {
                EntitySet key = keyValuePair.Key;
                foreach (EntitySet entitySet in keyValuePair.Value)
                {
                    OpCellTreeNode opCellTreeNode3;
                    if (dictionary.TryGetValue(entitySet, out opCellTreeNode3) && !nestedExtents.ContainsKey(entitySet) && !BasicViewGenerator.CheckLOJCycle(entitySet, key, nestedExtents))
                    {
                        dictionary[keyValuePair.Key].Add((CellTreeNode)opCellTreeNode3);
                        nestedExtents.Add(entitySet, key);
                    }
                }
            }
            foreach (KeyValuePair <EntitySet, OpCellTreeNode> keyValuePair in dictionary)
            {
                if (!nestedExtents.ContainsKey(keyValuePair.Key))
                {
                    OpCellTreeNode opCellTreeNode3 = keyValuePair.Value;
                    if (cellTreeNodeSet != null && cellTreeNodeSet.Contains(opCellTreeNode3.Children[0]))
                    {
                        opCellTreeNode2.Add((CellTreeNode)opCellTreeNode3);
                    }
                    else
                    {
                        opCellTreeNode1.Add((CellTreeNode)opCellTreeNode3);
                    }
                }
            }
            return(opCellTreeNode1.Flatten());
        }