File: System\Data\Mapping\ViewGeneration\QueryRewriting\RewritingSimplifier.cs
Project: ndp\fx\src\DataEntity\System.Data.Entity.csproj (System.Data.Entity)
//---------------------------------------------------------------------
// <copyright file="RewritingSimplifier.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
 
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Text;
using System.Linq;
 
namespace System.Data.Mapping.ViewGeneration.QueryRewriting
{
    internal class RewritingSimplifier<T_Tile> where T_Tile : class
    {
        private readonly T_Tile m_originalRewriting;
        private readonly T_Tile m_toAvoid;
        private readonly RewritingProcessor<T_Tile> m_qp;
        private readonly Dictionary<T_Tile, TileOpKind> m_usedViews = new Dictionary<T_Tile, TileOpKind>();
 
        // used for join/antisemijoin simplification
        private RewritingSimplifier(T_Tile originalRewriting, T_Tile toAvoid, Dictionary<T_Tile, TileOpKind> usedViews,
                                     RewritingProcessor<T_Tile> qp)
        {
            m_originalRewriting = originalRewriting;
            m_toAvoid = toAvoid;
            m_qp = qp;
            m_usedViews = usedViews;
        }
 
        // used for union simplification
        private RewritingSimplifier(T_Tile rewriting, T_Tile toFill, T_Tile toAvoid, RewritingProcessor<T_Tile> qp)
        {
            m_originalRewriting = toFill;
            m_toAvoid = toAvoid;
            m_qp = qp;
            m_usedViews = new Dictionary<T_Tile, TileOpKind>();
            GatherUnionedSubqueriesInUsedViews(rewriting);
        }
 
        // called for top query only
        internal static bool TrySimplifyUnionRewriting(ref T_Tile rewriting, T_Tile toFill, T_Tile toAvoid, RewritingProcessor<T_Tile> qp)
        {
            RewritingSimplifier<T_Tile> simplifier = new RewritingSimplifier<T_Tile>(rewriting, toFill, toAvoid, qp);
            // gather all unioned subqueries
            T_Tile simplifiedRewriting;
            if (simplifier.SimplifyRewriting(out simplifiedRewriting))
            {
                rewriting = simplifiedRewriting;
                return true;
            }
            return false;
        }
 
        // modifies usedViews - removes all redundant views from it
        internal static bool TrySimplifyJoinRewriting(ref T_Tile rewriting, T_Tile toAvoid, Dictionary<T_Tile, TileOpKind> usedViews, RewritingProcessor<T_Tile> qp)
        {
            RewritingSimplifier<T_Tile> simplifier = new RewritingSimplifier<T_Tile>(rewriting, toAvoid, usedViews, qp);
            T_Tile simplifiedRewriting;
            if (simplifier.SimplifyRewriting(out simplifiedRewriting))
            {
                rewriting = simplifiedRewriting;
                return true;
            }
            return false;
        }
 
        private void GatherUnionedSubqueriesInUsedViews(T_Tile query)
        {
            if (query != null)
            {
                if (m_qp.GetOpKind(query) != TileOpKind.Union)
                {
                    m_usedViews[query] = TileOpKind.Union;
                }
                else
                {
                    GatherUnionedSubqueriesInUsedViews(m_qp.GetArg1(query));
                    GatherUnionedSubqueriesInUsedViews(m_qp.GetArg2(query));
                }
            }
        }
 
        // isExactAnswer: matters for Intersections/Differences only
        private bool SimplifyRewriting(out T_Tile simplifiedRewriting)
        {
            bool compacted = false;
            simplifiedRewriting = null;
            T_Tile simplifiedOnce;
            while (SimplifyRewritingOnce(out simplifiedOnce))
            {
                compacted = true;
                simplifiedRewriting = simplifiedOnce;
            }
            return compacted;
        }
 
        // try removing one redundant view from intersected and subtracted views
        // This method uses a dynamic divide-and-conquer algorithm that avoids recomputing many intersections/differences
        private bool SimplifyRewritingOnce(out T_Tile simplifiedRewriting)
        {
            // check whether removing one or multiple views from intersected and subtracted views
            // still (a) reduces extra tuples, and (b) has no missing tuples
            // First, try removing a subtracted view
            HashSet<T_Tile> remainingViews = new HashSet<T_Tile>(m_usedViews.Keys);
            foreach (T_Tile usedView in m_usedViews.Keys)
            {
                // pick an intersected view, and nail it down
                switch (m_usedViews[usedView])
                {
                    case TileOpKind.Join:
                    case TileOpKind.Union:
                        remainingViews.Remove(usedView);
                        if (SimplifyRewritingOnce(usedView, remainingViews, out simplifiedRewriting))
                        {
                            return true;
                        }
                        remainingViews.Add(usedView);
                        break;
                }
            }
            simplifiedRewriting = null;
            return false;
        }
 
        // remainingViews may contain either unions only or intersections + differences
        private bool SimplifyRewritingOnce(T_Tile newRewriting, HashSet<T_Tile> remainingViews,
                                           out T_Tile simplifiedRewriting)
        {
            simplifiedRewriting = null;
            if (remainingViews.Count == 0)
            {
                return false;
            }
            if (remainingViews.Count == 1)
            {
                // determine the remaining view
                T_Tile remainingView = remainingViews.First();
 
                // check whether rewriting obtained so far is good enough
                // try disposing of this remaining view
                bool isDisposable = false;
                switch (m_usedViews[remainingView])
                {
                    case TileOpKind.Union:
                        // check whether rewriting still covers toFill
                        isDisposable = m_qp.IsContainedIn(m_originalRewriting, newRewriting);
                        break;
                    default: // intersection
                        isDisposable = m_qp.IsContainedIn(m_originalRewriting, newRewriting) &&
                                       m_qp.IsDisjointFrom(m_toAvoid, newRewriting);
                        break;
                }
                if (isDisposable)
                {
                    // yes, the remaining view is disposable
                    simplifiedRewriting = newRewriting;
                    m_usedViews.Remove(remainingView);
                    return true;
                }
                return false; // no, can't trash the remaining view
            }
            // split remainingViews into two halves
            // Compute rewriting for first half. Call recursively on second half.
            // Then, compute rewriting for second half. Call recursively on first half.
            int halfCount = remainingViews.Count / 2;
            int count = 0;
            T_Tile firstHalfRewriting = newRewriting;
            T_Tile secondHalfRewriting = newRewriting;
            HashSet<T_Tile> firstHalf = new HashSet<T_Tile>();
            HashSet<T_Tile> secondHalf = new HashSet<T_Tile>();
            foreach (T_Tile remainingView in remainingViews)
            {
                TileOpKind viewKind = m_usedViews[remainingView];
                // add to first half
                if (count++ < halfCount)
                {
                    firstHalf.Add(remainingView);
                    firstHalfRewriting = GetRewritingHalf(firstHalfRewriting, remainingView, viewKind);
                }
                else // add to second half
                {
                    secondHalf.Add(remainingView);
                    secondHalfRewriting = GetRewritingHalf(secondHalfRewriting, remainingView, viewKind);
                }
            }
            // now, call recursively
            return SimplifyRewritingOnce(firstHalfRewriting, secondHalf, out simplifiedRewriting)
                || SimplifyRewritingOnce(secondHalfRewriting, firstHalf, out simplifiedRewriting);
        }
 
        private T_Tile GetRewritingHalf(T_Tile halfRewriting, T_Tile remainingView, TileOpKind viewKind)
        {
            switch (viewKind)
            {
                case TileOpKind.Join:
                    halfRewriting = m_qp.Join(halfRewriting, remainingView); break;
                case TileOpKind.AntiSemiJoin:
                    halfRewriting = m_qp.AntiSemiJoin(halfRewriting, remainingView); break;
                case TileOpKind.Union:
                    halfRewriting = m_qp.Union(halfRewriting, remainingView); break;
                default: Debug.Fail("unexpected"); break;
            }
            return halfRewriting;
        }
    }
}