You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@ -0,0 +1,193 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="FragmentQuery.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Data.Common.Utils.Boolean;
|
||||
using System.Data.Mapping.ViewGeneration.Structures;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Linq;
|
||||
using System.Globalization;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.QueryRewriting
|
||||
{
|
||||
internal class FragmentQuery : ITileQuery
|
||||
{
|
||||
private BoolExpression m_fromVariable; // optional
|
||||
private string m_label; // optional
|
||||
|
||||
private HashSet<MemberPath> m_attributes;
|
||||
private BoolExpression m_condition;
|
||||
|
||||
public HashSet<MemberPath> Attributes
|
||||
{
|
||||
get { return m_attributes; }
|
||||
}
|
||||
|
||||
public BoolExpression Condition
|
||||
{
|
||||
get { return m_condition; }
|
||||
}
|
||||
|
||||
public static FragmentQuery Create(BoolExpression fromVariable, CellQuery cellQuery)
|
||||
{
|
||||
BoolExpression whereClause = cellQuery.WhereClause;
|
||||
whereClause = whereClause.MakeCopy();
|
||||
whereClause.ExpensiveSimplify();
|
||||
return new FragmentQuery(null /*label*/, fromVariable, new HashSet<MemberPath>(cellQuery.GetProjectedMembers()), whereClause);
|
||||
}
|
||||
|
||||
public static FragmentQuery Create(string label, RoleBoolean roleBoolean, CellQuery cellQuery)
|
||||
{
|
||||
BoolExpression whereClause = cellQuery.WhereClause.Create(roleBoolean);
|
||||
whereClause = BoolExpression.CreateAnd(whereClause, cellQuery.WhereClause);
|
||||
//return new FragmentQuery(label, null /* fromVariable */, new HashSet<MemberPath>(cellQuery.GetProjectedMembers()), whereClause);
|
||||
// don't need any attributes
|
||||
whereClause = whereClause.MakeCopy();
|
||||
whereClause.ExpensiveSimplify();
|
||||
return new FragmentQuery(label, null /* fromVariable */, new HashSet<MemberPath>(), whereClause);
|
||||
}
|
||||
|
||||
public static FragmentQuery Create(IEnumerable<MemberPath> attrs, BoolExpression whereClause)
|
||||
{
|
||||
return new FragmentQuery(null /* no name */, null /* no fromVariable*/, attrs, whereClause);
|
||||
}
|
||||
|
||||
public static FragmentQuery Create(BoolExpression whereClause)
|
||||
{
|
||||
return new FragmentQuery(null /* no name */, null /* no fromVariable*/, new MemberPath[] { }, whereClause);
|
||||
}
|
||||
|
||||
internal FragmentQuery(string label, BoolExpression fromVariable, IEnumerable<MemberPath> attrs, BoolExpression condition)
|
||||
{
|
||||
m_label = label;
|
||||
m_fromVariable = fromVariable;
|
||||
m_condition = condition;
|
||||
m_attributes = new HashSet<MemberPath>(attrs);
|
||||
}
|
||||
|
||||
public BoolExpression FromVariable
|
||||
{
|
||||
get { return m_fromVariable; }
|
||||
}
|
||||
|
||||
public string Description
|
||||
{
|
||||
get
|
||||
{
|
||||
string label = m_label;
|
||||
if (label == null && m_fromVariable != null)
|
||||
{
|
||||
label = m_fromVariable.ToString();
|
||||
}
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
// attributes
|
||||
StringBuilder b = new StringBuilder();
|
||||
foreach (MemberPath value in this.Attributes)
|
||||
{
|
||||
if (b.Length > 0) { b.Append(','); }
|
||||
b.Append(value.ToString());
|
||||
}
|
||||
|
||||
if (Description != null && Description != b.ToString())
|
||||
{
|
||||
return String.Format(CultureInfo.InvariantCulture, "{0}: [{1} where {2}]", Description, b, this.Condition);
|
||||
}
|
||||
else
|
||||
{
|
||||
return String.Format(CultureInfo.InvariantCulture, "[{0} where {1}]", b, this.Condition);
|
||||
}
|
||||
}
|
||||
|
||||
#region Static methods
|
||||
|
||||
// creates a condition member=value
|
||||
internal static BoolExpression CreateMemberCondition(MemberPath path, Constant domainValue, MemberDomainMap domainMap)
|
||||
{
|
||||
if (domainValue is TypeConstant)
|
||||
{
|
||||
return BoolExpression.CreateLiteral(new TypeRestriction(new MemberProjectedSlot(path),
|
||||
new Domain(domainValue, domainMap.GetDomain(path))), domainMap);
|
||||
}
|
||||
else
|
||||
{
|
||||
return BoolExpression.CreateLiteral(new ScalarRestriction(new MemberProjectedSlot(path),
|
||||
new Domain(domainValue, domainMap.GetDomain(path))), domainMap);
|
||||
}
|
||||
}
|
||||
|
||||
internal static IEqualityComparer<FragmentQuery> GetEqualityComparer(FragmentQueryProcessor qp)
|
||||
{
|
||||
return new FragmentQueryEqualityComparer(qp);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Equality Comparer
|
||||
// Two queries are "equal" if they project the same set of attributes
|
||||
// and their WHERE clauses are equivalent
|
||||
private class FragmentQueryEqualityComparer : IEqualityComparer<FragmentQuery>
|
||||
{
|
||||
FragmentQueryProcessor _qp;
|
||||
|
||||
internal FragmentQueryEqualityComparer(FragmentQueryProcessor qp)
|
||||
{
|
||||
_qp = qp;
|
||||
}
|
||||
|
||||
#region IEqualityComparer<FragmentQuery> Members
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2140:TransparentMethodsMustNotReferenceCriticalCode", Justification = "Based on Bug VSTS Pioneer #433188: IsVisibleOutsideAssembly is wrong on generic instantiations.")]
|
||||
public bool Equals(FragmentQuery x, FragmentQuery y)
|
||||
{
|
||||
if (!x.Attributes.SetEquals(y.Attributes))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return _qp.IsEquivalentTo(x, y);
|
||||
}
|
||||
|
||||
// Hashing a bit naive: it exploits syntactic properties,
|
||||
// i.e., some semantically equivalent queries may produce different hash codes
|
||||
// But that's fine for usage scenarios in QueryRewriter.cs
|
||||
public int GetHashCode(FragmentQuery q)
|
||||
{
|
||||
int attrHashCode = 0;
|
||||
foreach (MemberPath member in q.Attributes)
|
||||
{
|
||||
attrHashCode ^= MemberPath.EqualityComparer.GetHashCode(member);
|
||||
}
|
||||
int varHashCode = 0;
|
||||
int constHashCode = 0;
|
||||
foreach (MemberRestriction oneOf in q.Condition.MemberRestrictions)
|
||||
{
|
||||
varHashCode ^= MemberPath.EqualityComparer.GetHashCode(oneOf.RestrictedMemberSlot.MemberPath);
|
||||
foreach (Constant constant in oneOf.Domain.Values)
|
||||
{
|
||||
constHashCode ^= Constant.EqualityComparer.GetHashCode(constant);
|
||||
}
|
||||
}
|
||||
return attrHashCode * 13 + varHashCode * 7 + constHashCode;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,216 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="FragmentQueryKB.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Data.Common.Utils.Boolean;
|
||||
using System.Data.Mapping.ViewGeneration.Structures;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Linq;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.QueryRewriting
|
||||
{
|
||||
internal class FragmentQueryKB : KnowledgeBase<DomainConstraint<BoolLiteral, Constant>>
|
||||
{
|
||||
private BoolExpr<DomainConstraint<BoolLiteral, Constant>> _kbExpression = TrueExpr<DomainConstraint<BoolLiteral, Constant>>.Value;
|
||||
|
||||
internal override void AddFact(BoolExpr<DomainConstraint<BoolLiteral, Constant>> fact)
|
||||
{
|
||||
base.AddFact(fact);
|
||||
_kbExpression = new AndExpr<DomainConstraint<BoolLiteral, Constant>>(_kbExpression, fact);
|
||||
}
|
||||
internal BoolExpr<DomainConstraint<BoolLiteral, Constant>> KbExpression
|
||||
{
|
||||
get { return _kbExpression; }
|
||||
}
|
||||
|
||||
internal void CreateVariableConstraints(EntitySetBase extent, MemberDomainMap domainMap, EdmItemCollection edmItemCollection)
|
||||
{
|
||||
CreateVariableConstraintsRecursion(extent.ElementType, new MemberPath(extent), domainMap, edmItemCollection);
|
||||
}
|
||||
|
||||
internal void CreateAssociationConstraints(EntitySetBase extent, MemberDomainMap domainMap, EdmItemCollection edmItemCollection)
|
||||
{
|
||||
AssociationSet assocSet = extent as AssociationSet;
|
||||
if (assocSet != null)
|
||||
{
|
||||
BoolExpression assocSetExpr = BoolExpression.CreateLiteral(new RoleBoolean(assocSet), domainMap);
|
||||
|
||||
//Set of Keys for this Association Set
|
||||
//need to key on EdmMember and EdmType because A, B subtype of C, can have the same id (EdmMember) that is defined in C.
|
||||
HashSet<Pair<EdmMember, EntityType>> associationkeys = new HashSet<Pair<EdmMember, EntityType>>();
|
||||
|
||||
|
||||
//foreach end, add each Key
|
||||
foreach (var endMember in assocSet.ElementType.AssociationEndMembers)
|
||||
{
|
||||
EntityType type = (EntityType)((RefType)endMember.TypeUsage.EdmType).ElementType;
|
||||
type.KeyMembers.All(member => associationkeys.Add(new Pair<EdmMember, EntityType>(member, type)) || true /* prevent early termination */);
|
||||
}
|
||||
|
||||
foreach (AssociationSetEnd end in assocSet.AssociationSetEnds)
|
||||
{
|
||||
// construct type condition
|
||||
HashSet<EdmType> derivedTypes = new HashSet<EdmType>();
|
||||
derivedTypes.UnionWith(MetadataHelper.GetTypeAndSubtypesOf(end.CorrespondingAssociationEndMember.TypeUsage.EdmType, edmItemCollection, false));
|
||||
|
||||
BoolExpression typeCondition = CreateIsOfTypeCondition(new MemberPath(end.EntitySet),
|
||||
derivedTypes, domainMap);
|
||||
|
||||
BoolExpression inRoleExpression = BoolExpression.CreateLiteral(new RoleBoolean(end), domainMap);
|
||||
BoolExpression inSetExpression = BoolExpression.CreateAnd(
|
||||
BoolExpression.CreateLiteral(new RoleBoolean(end.EntitySet), domainMap),
|
||||
typeCondition);
|
||||
|
||||
// InRole -> (InSet AND type(Set)=T)
|
||||
AddImplication(inRoleExpression.Tree, inSetExpression.Tree);
|
||||
|
||||
if (MetadataHelper.IsEveryOtherEndAtLeastOne(assocSet, end.CorrespondingAssociationEndMember))
|
||||
{
|
||||
AddImplication(inSetExpression.Tree, inRoleExpression.Tree);
|
||||
}
|
||||
|
||||
// Add equivalence between association set an End/Role if necessary.
|
||||
// Equivalence is added when a given association end's keys subsumes keys for
|
||||
// all the other association end.
|
||||
|
||||
// For example: We have Entity Sets A[id1], B[id2, id3] and an association A_B between them.
|
||||
// Ref Constraint A.id1 = B.id2
|
||||
// In this case, the Association Set has Key <id1, id2, id3>
|
||||
// id1 alone can not identify a unique tuple in the Association Set, but <id2, id3> can.
|
||||
// Therefore we add a constraint: InSet(B) <=> InEnd(A_B.B)
|
||||
|
||||
if (MetadataHelper.DoesEndKeySubsumeAssociationSetKey(assocSet,
|
||||
end.CorrespondingAssociationEndMember,
|
||||
associationkeys))
|
||||
{
|
||||
AddEquivalence(inRoleExpression.Tree, assocSetExpr.Tree);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// add rules for referential constraints (borrowed from LeftCellWrapper.cs)
|
||||
AssociationType assocType = assocSet.ElementType;
|
||||
|
||||
foreach (ReferentialConstraint constraint in assocType.ReferentialConstraints)
|
||||
{
|
||||
AssociationEndMember toEndMember = (AssociationEndMember)constraint.ToRole;
|
||||
EntitySet toEntitySet = MetadataHelper.GetEntitySetAtEnd(assocSet, toEndMember);
|
||||
// Check if the keys of the entitySet's are equal to what is specified in the constraint
|
||||
// How annoying that KeyMembers returns EdmMember and not EdmProperty
|
||||
IEnumerable<EdmMember> toProperties = Helpers.AsSuperTypeList<EdmProperty, EdmMember>(constraint.ToProperties);
|
||||
if (Helpers.IsSetEqual(toProperties, toEntitySet.ElementType.KeyMembers, EqualityComparer<EdmMember>.Default))
|
||||
{
|
||||
// Now check that the FromEnd is 1..1 (only then will all the Addresses be present in the assoc set)
|
||||
if (constraint.FromRole.RelationshipMultiplicity.Equals(RelationshipMultiplicity.One))
|
||||
{
|
||||
// Make sure that the ToEnd is not 0..* because then the schema is broken
|
||||
Debug.Assert(constraint.ToRole.RelationshipMultiplicity.Equals(RelationshipMultiplicity.Many) == false);
|
||||
// Equate the ends
|
||||
BoolExpression inRoleExpression1 = BoolExpression.CreateLiteral(new RoleBoolean(assocSet.AssociationSetEnds[0]), domainMap);
|
||||
BoolExpression inRoleExpression2 = BoolExpression.CreateLiteral(new RoleBoolean(assocSet.AssociationSetEnds[1]), domainMap);
|
||||
AddEquivalence(inRoleExpression1.Tree, inRoleExpression2.Tree);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void CreateEquivalenceConstraintForOneToOneForeignKeyAssociation(AssociationSet assocSet, MemberDomainMap domainMap,
|
||||
EdmItemCollection edmItemCollection)
|
||||
{
|
||||
AssociationType assocType = assocSet.ElementType;
|
||||
foreach (ReferentialConstraint constraint in assocType.ReferentialConstraints)
|
||||
{
|
||||
AssociationEndMember toEndMember = (AssociationEndMember)constraint.ToRole;
|
||||
AssociationEndMember fromEndMember = (AssociationEndMember)constraint.FromRole;
|
||||
EntitySet toEntitySet = MetadataHelper.GetEntitySetAtEnd(assocSet, toEndMember);
|
||||
EntitySet fromEntitySet = MetadataHelper.GetEntitySetAtEnd(assocSet, fromEndMember);
|
||||
|
||||
// Check if the keys of the entitySet's are equal to what is specified in the constraint
|
||||
IEnumerable<EdmMember> toProperties = Helpers.AsSuperTypeList<EdmProperty, EdmMember>(constraint.ToProperties);
|
||||
if (Helpers.IsSetEqual(toProperties, toEntitySet.ElementType.KeyMembers, EqualityComparer<EdmMember>.Default))
|
||||
{
|
||||
//make sure that the method called with a 1:1 association
|
||||
Debug.Assert(constraint.FromRole.RelationshipMultiplicity.Equals(RelationshipMultiplicity.One));
|
||||
Debug.Assert(constraint.ToRole.RelationshipMultiplicity.Equals(RelationshipMultiplicity.One));
|
||||
// Create an Equivalence between the two Sets participating in this AssociationSet
|
||||
BoolExpression fromSetExpression = BoolExpression.CreateLiteral(new RoleBoolean(fromEntitySet), domainMap);
|
||||
BoolExpression toSetExpression = BoolExpression.CreateLiteral(new RoleBoolean(toEntitySet), domainMap);
|
||||
AddEquivalence(fromSetExpression.Tree, toSetExpression.Tree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateVariableConstraintsRecursion(EdmType edmType, MemberPath currentPath, MemberDomainMap domainMap, EdmItemCollection edmItemCollection)
|
||||
{
|
||||
// Add the types can member have, i.e., its type and its subtypes
|
||||
HashSet<EdmType> possibleTypes = new HashSet<EdmType>();
|
||||
possibleTypes.UnionWith(MetadataHelper.GetTypeAndSubtypesOf(edmType, edmItemCollection, true));
|
||||
|
||||
foreach (EdmType possibleType in possibleTypes)
|
||||
{
|
||||
// determine type domain
|
||||
|
||||
HashSet<EdmType> derivedTypes = new HashSet<EdmType>();
|
||||
derivedTypes.UnionWith(MetadataHelper.GetTypeAndSubtypesOf(possibleType, edmItemCollection, false));
|
||||
if (derivedTypes.Count != 0)
|
||||
{
|
||||
BoolExpression typeCondition = CreateIsOfTypeCondition(currentPath, derivedTypes, domainMap);
|
||||
BoolExpression typeConditionComplement = BoolExpression.CreateNot(typeCondition);
|
||||
if (false == typeConditionComplement.IsSatisfiable())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
StructuralType structuralType = (StructuralType)possibleType;
|
||||
foreach (EdmProperty childProperty in structuralType.GetDeclaredOnlyMembers<EdmProperty>())
|
||||
{
|
||||
MemberPath childPath = new MemberPath(currentPath, childProperty);
|
||||
bool isScalar = MetadataHelper.IsNonRefSimpleMember(childProperty);
|
||||
|
||||
if (domainMap.IsConditionMember(childPath) || domainMap.IsProjectedConditionMember(childPath))
|
||||
{
|
||||
BoolExpression nullCondition;
|
||||
List<Constant> childDomain = new List<Constant>(domainMap.GetDomain(childPath));
|
||||
if (isScalar)
|
||||
{
|
||||
nullCondition = BoolExpression.CreateLiteral(new ScalarRestriction(new MemberProjectedSlot(childPath),
|
||||
new Domain(ScalarConstant.Undefined, childDomain)), domainMap);
|
||||
}
|
||||
else
|
||||
{
|
||||
nullCondition = BoolExpression.CreateLiteral(new TypeRestriction(new MemberProjectedSlot(childPath),
|
||||
new Domain(TypeConstant.Undefined, childDomain)), domainMap);
|
||||
}
|
||||
// Properties not occuring in type are UNDEFINED
|
||||
AddEquivalence(typeConditionComplement.Tree, nullCondition.Tree);
|
||||
}
|
||||
|
||||
// recurse into complex types
|
||||
if (false == isScalar)
|
||||
{
|
||||
CreateVariableConstraintsRecursion(childPath.EdmType, childPath, domainMap, edmItemCollection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static BoolExpression CreateIsOfTypeCondition(MemberPath currentPath, IEnumerable<EdmType> derivedTypes, MemberDomainMap domainMap)
|
||||
{
|
||||
Domain typeDomain = new Domain(derivedTypes.Select(derivedType => (Constant)new TypeConstant(derivedType)), domainMap.GetDomain(currentPath));
|
||||
BoolExpression typeCondition = BoolExpression.CreateLiteral(new TypeRestriction(new MemberProjectedSlot(currentPath), typeDomain), domainMap);
|
||||
return typeCondition;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="FragmentQueryProcessor.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Data.Common.Utils;
|
||||
using System.Data.Common.Utils.Boolean;
|
||||
using System.Data.Mapping.ViewGeneration.Structures;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Linq;
|
||||
using System.Globalization;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.QueryRewriting
|
||||
{
|
||||
using BoolDomainConstraint = DomainConstraint<BoolLiteral, Constant>;
|
||||
|
||||
internal class FragmentQueryProcessor : TileQueryProcessor<FragmentQuery>
|
||||
{
|
||||
private FragmentQueryKB _kb;
|
||||
|
||||
public FragmentQueryProcessor(FragmentQueryKB kb)
|
||||
{
|
||||
_kb = kb;
|
||||
}
|
||||
|
||||
internal static FragmentQueryProcessor Merge(FragmentQueryProcessor qp1, FragmentQueryProcessor qp2)
|
||||
{
|
||||
FragmentQueryKB mergedKB = new FragmentQueryKB();
|
||||
mergedKB.AddKnowledgeBase(qp1.KnowledgeBase);
|
||||
mergedKB.AddKnowledgeBase(qp2.KnowledgeBase);
|
||||
return new FragmentQueryProcessor(mergedKB);
|
||||
}
|
||||
|
||||
internal FragmentQueryKB KnowledgeBase
|
||||
{
|
||||
get { return _kb; }
|
||||
}
|
||||
|
||||
// resulting query contains an intersection of attributes
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2140:TransparentMethodsMustNotReferenceCriticalCode", Justification = "Based on Bug VSTS Pioneer #433188: IsVisibleOutsideAssembly is wrong on generic instantiations.")]
|
||||
internal override FragmentQuery Union(FragmentQuery q1, FragmentQuery q2)
|
||||
{
|
||||
HashSet<MemberPath> attributes = new HashSet<MemberPath>(q1.Attributes);
|
||||
attributes.IntersectWith(q2.Attributes);
|
||||
|
||||
BoolExpression condition = BoolExpression.CreateOr(q1.Condition, q2.Condition);
|
||||
|
||||
return FragmentQuery.Create(attributes, condition);
|
||||
}
|
||||
|
||||
internal bool IsDisjointFrom(FragmentQuery q1, FragmentQuery q2)
|
||||
{
|
||||
return !IsSatisfiable(Intersect(q1, q2));
|
||||
}
|
||||
|
||||
internal bool IsContainedIn(FragmentQuery q1, FragmentQuery q2)
|
||||
{
|
||||
return !IsSatisfiable(Difference(q1, q2));
|
||||
}
|
||||
|
||||
internal bool IsEquivalentTo(FragmentQuery q1, FragmentQuery q2)
|
||||
{
|
||||
return IsContainedIn(q1, q2) && IsContainedIn(q2, q1);
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2140:TransparentMethodsMustNotReferenceCriticalCode", Justification = "Based on Bug VSTS Pioneer #433188: IsVisibleOutsideAssembly is wrong on generic instantiations.")]
|
||||
internal override FragmentQuery Intersect(FragmentQuery q1, FragmentQuery q2)
|
||||
{
|
||||
HashSet<MemberPath> attributes = new HashSet<MemberPath>(q1.Attributes);
|
||||
attributes.IntersectWith(q2.Attributes);
|
||||
|
||||
BoolExpression condition = BoolExpression.CreateAnd(q1.Condition, q2.Condition);
|
||||
|
||||
return FragmentQuery.Create(attributes, condition);
|
||||
}
|
||||
|
||||
internal override FragmentQuery Difference(FragmentQuery qA, FragmentQuery qB)
|
||||
{
|
||||
return FragmentQuery.Create(qA.Attributes, BoolExpression.CreateAndNot(qA.Condition, qB.Condition));
|
||||
}
|
||||
|
||||
internal override bool IsSatisfiable(FragmentQuery query)
|
||||
{
|
||||
return IsSatisfiable(query.Condition);
|
||||
}
|
||||
|
||||
private bool IsSatisfiable(BoolExpression condition)
|
||||
{
|
||||
// instantiate conversion context for each check - gives better performance
|
||||
BoolExpression conditionUnderKB = condition.Create(
|
||||
new AndExpr<BoolDomainConstraint>(_kb.KbExpression, condition.Tree));
|
||||
var context = IdentifierService<BoolDomainConstraint>.Instance.CreateConversionContext();
|
||||
var converter = new Converter<BoolDomainConstraint>(conditionUnderKB.Tree, context);
|
||||
bool isSatisfiable = converter.Vertex.IsZero() == false;
|
||||
return isSatisfiable;
|
||||
}
|
||||
|
||||
// creates "derived" views that may be helpful for answering the query
|
||||
// for example, view = SELECT ID WHERE B=2, query = SELECT ID,B WHERE B=2
|
||||
// Created derived view: SELECT ID,B WHERE B=2 by adding the attribute whose value is determined by the where clause to projected list
|
||||
internal override FragmentQuery CreateDerivedViewBySelectingConstantAttributes(FragmentQuery view)
|
||||
{
|
||||
HashSet<MemberPath> newProjectedAttributes = new HashSet<MemberPath>();
|
||||
// collect all variables from the view
|
||||
IEnumerable<DomainVariable<BoolLiteral, Constant>> variables = view.Condition.Variables;
|
||||
foreach (DomainVariable<BoolLiteral, Constant> var in variables)
|
||||
{
|
||||
MemberRestriction variableCondition = var.Identifier as MemberRestriction;
|
||||
if (variableCondition != null)
|
||||
{
|
||||
// Is this attribute not already projected?
|
||||
MemberPath conditionMember = variableCondition.RestrictedMemberSlot.MemberPath;
|
||||
// Iterating through the variable domain var.Domain could be wasteful
|
||||
// Instead, consider the actual condition values on the variable. Usually, they don't get repeated (if not, we could cache and check)
|
||||
Domain conditionValues = variableCondition.Domain;
|
||||
|
||||
if ((false == view.Attributes.Contains(conditionMember))
|
||||
&& !(conditionValues.AllPossibleValues.Any(it => it.HasNotNull()))) //Don't add member to the projected list if the condition involves a
|
||||
{
|
||||
foreach (Constant value in conditionValues.Values)
|
||||
{
|
||||
// construct constraint: X = value
|
||||
DomainConstraint<BoolLiteral, Constant> constraint = new DomainConstraint<BoolLiteral, Constant>(var,
|
||||
new Set<Constant>(new Constant[] { value }, Constant.EqualityComparer));
|
||||
// is this constraint implied by the where clause?
|
||||
BoolExpression exclusion = view.Condition.Create(
|
||||
new AndExpr<DomainConstraint<BoolLiteral, Constant>>(view.Condition.Tree,
|
||||
new NotExpr<DomainConstraint<BoolLiteral, Constant>>(new TermExpr<DomainConstraint<BoolLiteral, Constant>>(constraint))));
|
||||
bool isImplied = false == IsSatisfiable(exclusion);
|
||||
if (isImplied)
|
||||
{
|
||||
// add this variable to the projection, if it is used in the query
|
||||
newProjectedAttributes.Add(conditionMember);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newProjectedAttributes.Count > 0)
|
||||
{
|
||||
newProjectedAttributes.UnionWith(view.Attributes);
|
||||
FragmentQuery derivedView = new FragmentQuery(String.Format(CultureInfo.InvariantCulture, "project({0})", view.Description), view.FromVariable,
|
||||
newProjectedAttributes, view.Condition);
|
||||
return derivedView;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _kb.ToString();
|
||||
}
|
||||
|
||||
#region Private class AttributeSetComparator
|
||||
|
||||
private class AttributeSetComparator : IEqualityComparer<HashSet<MemberPath>>
|
||||
{
|
||||
internal static readonly AttributeSetComparator DefaultInstance = new AttributeSetComparator();
|
||||
|
||||
#region IEqualityComparer<HashSet<MemberPath>> Members
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2140:TransparentMethodsMustNotReferenceCriticalCode", Justification = "Based on Bug VSTS Pioneer #433188: IsVisibleOutsideAssembly is wrong on generic instantiations.")]
|
||||
public bool Equals(HashSet<MemberPath> x, HashSet<MemberPath> y)
|
||||
{
|
||||
return x.SetEquals(y);
|
||||
}
|
||||
|
||||
public int GetHashCode(HashSet<MemberPath> attrs)
|
||||
{
|
||||
int hashCode = 123;
|
||||
foreach (MemberPath attr in attrs)
|
||||
{
|
||||
hashCode += MemberPath.EqualityComparer.GetHashCode(attr) * 7;
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,251 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="RewritingPass.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.QueryRewriting
|
||||
{
|
||||
// Goal: use the next view to get rewritingSoFar to be closer to the goal
|
||||
internal class RewritingPass<T_Tile> where T_Tile : class
|
||||
{
|
||||
// region that rewriting needs to cover
|
||||
private readonly T_Tile m_toFill;
|
||||
// region that rewriting needs to be disjoint with
|
||||
private readonly T_Tile m_toAvoid;
|
||||
private readonly List<T_Tile> m_views;
|
||||
private readonly RewritingProcessor<T_Tile> m_qp;
|
||||
private readonly Dictionary<T_Tile, TileOpKind> m_usedViews = new Dictionary<T_Tile, TileOpKind>();
|
||||
|
||||
public RewritingPass(T_Tile toFill, T_Tile toAvoid, List<T_Tile> views, RewritingProcessor<T_Tile> qp)
|
||||
{
|
||||
m_toFill = toFill;
|
||||
m_toAvoid = toAvoid;
|
||||
m_views = views;
|
||||
m_qp = qp;
|
||||
}
|
||||
|
||||
public static bool RewriteQuery(T_Tile toFill, T_Tile toAvoid, out T_Tile rewriting, List<T_Tile> views, RewritingProcessor<T_Tile> qp)
|
||||
{
|
||||
RewritingPass<T_Tile> rewritingPass = new RewritingPass<T_Tile>(toFill, toAvoid, views, qp);
|
||||
if (rewritingPass.RewriteQuery(out rewriting))
|
||||
{
|
||||
RewritingSimplifier<T_Tile>.TrySimplifyUnionRewriting(ref rewriting, toFill, toAvoid, qp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool RewriteQueryInternal(T_Tile toFill, T_Tile toAvoid, out T_Tile rewriting, List<T_Tile> views, HashSet<T_Tile> recentlyUsedViews, RewritingProcessor<T_Tile> qp)
|
||||
{
|
||||
if (qp.REORDER_VIEWS && recentlyUsedViews.Count > 0)
|
||||
{
|
||||
// move recently used views toward the end
|
||||
List<T_Tile> reorderedViews = new List<T_Tile>();
|
||||
foreach (T_Tile view in views)
|
||||
{
|
||||
if (false == recentlyUsedViews.Contains(view))
|
||||
{
|
||||
reorderedViews.Add(view);
|
||||
}
|
||||
}
|
||||
reorderedViews.AddRange(recentlyUsedViews);
|
||||
views = reorderedViews;
|
||||
}
|
||||
|
||||
RewritingPass<T_Tile> rewritingPass = new RewritingPass<T_Tile>(toFill, toAvoid, views, qp);
|
||||
return rewritingPass.RewriteQuery(out rewriting);
|
||||
}
|
||||
|
||||
private bool RewriteQuery(out T_Tile rewriting)
|
||||
{
|
||||
rewriting = m_toFill;
|
||||
T_Tile rewritingSoFar;
|
||||
|
||||
if (false == FindRewritingByIncludedAndDisjoint(out rewritingSoFar))
|
||||
{
|
||||
if (false == FindContributingView(out rewritingSoFar))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasExtraTuples = !m_qp.IsDisjointFrom(rewritingSoFar, m_toAvoid);
|
||||
|
||||
// try to cut off extra tuples using joins
|
||||
if (hasExtraTuples)
|
||||
{
|
||||
foreach (T_Tile view in AvailableViews)
|
||||
{
|
||||
if (TryJoin(view, ref rewritingSoFar))
|
||||
{
|
||||
hasExtraTuples = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try to cut off extra tuples using anti-semijoins
|
||||
if (hasExtraTuples)
|
||||
{
|
||||
foreach (T_Tile view in AvailableViews)
|
||||
{
|
||||
if (TryAntiSemiJoin(view, ref rewritingSoFar))
|
||||
{
|
||||
hasExtraTuples = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasExtraTuples)
|
||||
{
|
||||
return false; // won't be able to cut off extra tuples
|
||||
}
|
||||
|
||||
// remove redundant joins and anti-semijoins
|
||||
RewritingSimplifier<T_Tile>.TrySimplifyJoinRewriting(ref rewritingSoFar, m_toAvoid, m_usedViews, m_qp);
|
||||
|
||||
// find rewriting for missing tuples, if any
|
||||
T_Tile missingTuples = m_qp.AntiSemiJoin(m_toFill, rewritingSoFar);
|
||||
if (!m_qp.IsEmpty(missingTuples))
|
||||
{
|
||||
T_Tile rewritingForMissingTuples;
|
||||
if (false == RewritingPass<T_Tile>.RewriteQueryInternal(missingTuples, m_toAvoid, out rewritingForMissingTuples, m_views, new HashSet<T_Tile>(m_usedViews.Keys), m_qp))
|
||||
{
|
||||
rewriting = rewritingForMissingTuples;
|
||||
return false; // failure
|
||||
}
|
||||
else
|
||||
{
|
||||
// Although a more general optimization for UNIONs will handle this case,
|
||||
// adding this check reduces the overall number of containment tests
|
||||
if (m_qp.IsContainedIn(rewritingSoFar, rewritingForMissingTuples))
|
||||
{
|
||||
rewritingSoFar = rewritingForMissingTuples;
|
||||
}
|
||||
else
|
||||
{
|
||||
rewritingSoFar = m_qp.Union(rewritingSoFar, rewritingForMissingTuples);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we reached this point, we have a successful rewriting
|
||||
rewriting = rewritingSoFar;
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns true if no more extra tuples are left
|
||||
private bool TryJoin(T_Tile view, ref T_Tile rewriting)
|
||||
{
|
||||
T_Tile newRewriting = m_qp.Join(rewriting, view);
|
||||
if (!m_qp.IsEmpty(newRewriting))
|
||||
{
|
||||
m_usedViews[view] = TileOpKind.Join;
|
||||
rewriting = newRewriting;
|
||||
return m_qp.IsDisjointFrom(rewriting, m_toAvoid);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// returns true if no more extra tuples are left
|
||||
private bool TryAntiSemiJoin(T_Tile view, ref T_Tile rewriting)
|
||||
{
|
||||
T_Tile newRewriting = m_qp.AntiSemiJoin(rewriting, view);
|
||||
if (!m_qp.IsEmpty(newRewriting))
|
||||
{
|
||||
m_usedViews[view] = TileOpKind.AntiSemiJoin;
|
||||
rewriting = newRewriting;
|
||||
return m_qp.IsDisjointFrom(rewriting, m_toAvoid);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to find a rewriting by intersecting all views which contain the query
|
||||
// and subtracting all views that are disjoint from the query
|
||||
private bool FindRewritingByIncludedAndDisjoint(out T_Tile rewritingSoFar)
|
||||
{
|
||||
// intersect all views in which m_toFill is contained
|
||||
rewritingSoFar = null;
|
||||
foreach (T_Tile view in AvailableViews)
|
||||
{
|
||||
if (m_qp.IsContainedIn(m_toFill, view)) // query <= view
|
||||
{
|
||||
if (rewritingSoFar == null)
|
||||
{
|
||||
rewritingSoFar = view;
|
||||
m_usedViews[view] = TileOpKind.Join;
|
||||
}
|
||||
else
|
||||
{
|
||||
T_Tile newRewriting = m_qp.Join(rewritingSoFar, view);
|
||||
if (!m_qp.IsContainedIn(rewritingSoFar, newRewriting))
|
||||
{
|
||||
rewritingSoFar = newRewriting;
|
||||
m_usedViews[view] = TileOpKind.Join; // it is a useful join
|
||||
}
|
||||
else
|
||||
{
|
||||
continue; // useless join
|
||||
}
|
||||
}
|
||||
if (m_qp.IsContainedIn(rewritingSoFar, m_toFill))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// subtract all views that are disjoint from m_toFill
|
||||
if (rewritingSoFar != null)
|
||||
{
|
||||
foreach (T_Tile view in AvailableViews)
|
||||
{
|
||||
if (m_qp.IsDisjointFrom(m_toFill, view)) // query ^ view = {}
|
||||
{
|
||||
if (!m_qp.IsDisjointFrom(rewritingSoFar, view))
|
||||
{
|
||||
rewritingSoFar = m_qp.AntiSemiJoin(rewritingSoFar, view);
|
||||
m_usedViews[view] = TileOpKind.AntiSemiJoin;
|
||||
if (m_qp.IsContainedIn(rewritingSoFar, m_toFill))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rewritingSoFar != null;
|
||||
}
|
||||
|
||||
private bool FindContributingView(out T_Tile rewriting)
|
||||
{
|
||||
// find some view that helps reduce toFill
|
||||
foreach (T_Tile view in AvailableViews)
|
||||
{
|
||||
if (false == m_qp.IsDisjointFrom(view, m_toFill))
|
||||
{
|
||||
rewriting = view;
|
||||
m_usedViews[view] = TileOpKind.Join; // positive, intersected
|
||||
return true;
|
||||
}
|
||||
}
|
||||
rewriting = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private IEnumerable<T_Tile> AvailableViews
|
||||
{
|
||||
get { return m_views.Where(view => !m_usedViews.ContainsKey(view)); }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,288 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="RewritingProcessor.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.QueryRewriting
|
||||
{
|
||||
internal abstract class TileProcessor<T_Tile>
|
||||
{
|
||||
internal abstract bool IsEmpty(T_Tile tile);
|
||||
internal abstract T_Tile Union(T_Tile a, T_Tile b);
|
||||
internal abstract T_Tile Join(T_Tile a, T_Tile b);
|
||||
internal abstract T_Tile AntiSemiJoin(T_Tile a, T_Tile b);
|
||||
|
||||
internal abstract T_Tile GetArg1(T_Tile tile);
|
||||
internal abstract T_Tile GetArg2(T_Tile tile);
|
||||
internal abstract TileOpKind GetOpKind(T_Tile tile);
|
||||
}
|
||||
|
||||
internal class RewritingProcessor<T_Tile> : TileProcessor<T_Tile> where T_Tile : class
|
||||
{
|
||||
public double PERMUTE_FRACTION = 0.0;
|
||||
public int MIN_PERMUTATIONS = 0;
|
||||
public int MAX_PERMUTATIONS = 0;
|
||||
public bool REORDER_VIEWS = false;
|
||||
|
||||
private int m_numSATChecks;
|
||||
private int m_numIntersection;
|
||||
private int m_numDifference;
|
||||
private int m_numUnion;
|
||||
|
||||
private int m_numErrors;
|
||||
|
||||
private TileProcessor<T_Tile> m_tileProcessor;
|
||||
|
||||
public RewritingProcessor(TileProcessor<T_Tile> tileProcessor)
|
||||
{
|
||||
m_tileProcessor = tileProcessor;
|
||||
}
|
||||
|
||||
internal TileProcessor<T_Tile> TileProcessor
|
||||
{
|
||||
get { return m_tileProcessor; }
|
||||
}
|
||||
|
||||
public void GetStatistics(out int numSATChecks, out int numIntersection, out int numUnion, out int numDifference, out int numErrors)
|
||||
{
|
||||
numSATChecks = m_numSATChecks;
|
||||
numIntersection = m_numIntersection;
|
||||
numUnion = m_numUnion;
|
||||
numDifference = m_numDifference;
|
||||
numErrors = m_numErrors;
|
||||
}
|
||||
|
||||
public void PrintStatistics()
|
||||
{
|
||||
Console.WriteLine("{0} containment checks, {4} set operations ({1} intersections + {2} unions + {3} differences)",
|
||||
m_numSATChecks, m_numIntersection, m_numUnion, m_numDifference,
|
||||
m_numIntersection + m_numUnion + m_numDifference);
|
||||
Console.WriteLine("{0} errors", m_numErrors);
|
||||
}
|
||||
|
||||
internal override T_Tile GetArg1(T_Tile tile)
|
||||
{
|
||||
return m_tileProcessor.GetArg1(tile);
|
||||
}
|
||||
|
||||
internal override T_Tile GetArg2(T_Tile tile)
|
||||
{
|
||||
return m_tileProcessor.GetArg2(tile);
|
||||
}
|
||||
|
||||
internal override TileOpKind GetOpKind(T_Tile tile)
|
||||
{
|
||||
return m_tileProcessor.GetOpKind(tile);
|
||||
}
|
||||
|
||||
internal override bool IsEmpty(T_Tile a)
|
||||
{
|
||||
m_numSATChecks++;
|
||||
return m_tileProcessor.IsEmpty(a);
|
||||
}
|
||||
|
||||
public bool IsDisjointFrom(T_Tile a, T_Tile b)
|
||||
{
|
||||
return m_tileProcessor.IsEmpty(Join(a, b));
|
||||
}
|
||||
|
||||
internal bool IsContainedIn(T_Tile a, T_Tile b)
|
||||
{
|
||||
T_Tile difference = AntiSemiJoin(a, b);
|
||||
return IsEmpty(difference);
|
||||
}
|
||||
|
||||
internal bool IsEquivalentTo(T_Tile a, T_Tile b)
|
||||
{
|
||||
bool aInB = IsContainedIn(a, b);
|
||||
bool bInA = IsContainedIn(b, a);
|
||||
return aInB && bInA;
|
||||
}
|
||||
|
||||
internal override T_Tile Union(T_Tile a, T_Tile b)
|
||||
{
|
||||
m_numUnion++;
|
||||
return m_tileProcessor.Union(a, b);
|
||||
}
|
||||
|
||||
internal override T_Tile Join(T_Tile a, T_Tile b)
|
||||
{
|
||||
if (a == null)
|
||||
{
|
||||
return b;
|
||||
}
|
||||
m_numIntersection++;
|
||||
return m_tileProcessor.Join(a, b);
|
||||
}
|
||||
|
||||
internal override T_Tile AntiSemiJoin(T_Tile a, T_Tile b)
|
||||
{
|
||||
m_numDifference++;
|
||||
return m_tileProcessor.AntiSemiJoin(a, b);
|
||||
}
|
||||
|
||||
public void AddError()
|
||||
{
|
||||
m_numErrors++;
|
||||
}
|
||||
|
||||
public int CountOperators(T_Tile query)
|
||||
{
|
||||
int count = 0;
|
||||
if (query != null)
|
||||
{
|
||||
if (GetOpKind(query) != TileOpKind.Named)
|
||||
{
|
||||
count++;
|
||||
count += CountOperators(GetArg1(query));
|
||||
count += CountOperators(GetArg2(query));
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public int CountViews(T_Tile query)
|
||||
{
|
||||
HashSet<T_Tile> views = new HashSet<T_Tile>();
|
||||
GatherViews(query, views);
|
||||
return views.Count;
|
||||
}
|
||||
|
||||
public void GatherViews(T_Tile rewriting, HashSet<T_Tile> views)
|
||||
{
|
||||
if (rewriting != null)
|
||||
{
|
||||
if (GetOpKind(rewriting) == TileOpKind.Named)
|
||||
{
|
||||
views.Add(rewriting);
|
||||
}
|
||||
else
|
||||
{
|
||||
GatherViews(GetArg1(rewriting), views);
|
||||
GatherViews(GetArg2(rewriting), views);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<T> AllButOne<T>(IEnumerable<T> list, int toSkipPosition)
|
||||
{
|
||||
int valuePosition = 0;
|
||||
foreach (T value in list)
|
||||
{
|
||||
if (valuePosition++ != toSkipPosition)
|
||||
{
|
||||
yield return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<T> Concat<T>(T value, IEnumerable<T> rest)
|
||||
{
|
||||
yield return value;
|
||||
foreach (T restValue in rest)
|
||||
{
|
||||
yield return restValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<IEnumerable<T>> Permute<T>(IEnumerable<T> list)
|
||||
{
|
||||
IEnumerable<T> rest = null;
|
||||
int valuePosition = 0;
|
||||
foreach (T value in list)
|
||||
{
|
||||
rest = AllButOne<T>(list, valuePosition++);
|
||||
foreach (IEnumerable<T> restPermutation in Permute<T>(rest))
|
||||
{
|
||||
yield return Concat<T>(value, restPermutation);
|
||||
}
|
||||
}
|
||||
if (rest == null)
|
||||
{
|
||||
yield return list; // list is empty enumeration
|
||||
}
|
||||
}
|
||||
|
||||
static Random rnd = new Random(1507);
|
||||
public static List<T> RandomPermutation<T>(IEnumerable<T> input)
|
||||
{
|
||||
List<T> output = new List<T>(input);
|
||||
for (int i = 0; i < output.Count; i++)
|
||||
{
|
||||
int j = rnd.Next(output.Count);
|
||||
T tmp = output[i];
|
||||
output[i] = output[j];
|
||||
output[j] = tmp;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> Reverse<T>(IEnumerable<T> input, HashSet<T> filter)
|
||||
{
|
||||
List<T> output = new List<T>(input);
|
||||
output.Reverse();
|
||||
foreach (T t in output)
|
||||
{
|
||||
if (filter.Contains(t))
|
||||
{
|
||||
yield return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool RewriteQuery(T_Tile toFill, T_Tile toAvoid, IEnumerable<T_Tile> views, out T_Tile rewriting)
|
||||
{
|
||||
if (RewriteQueryOnce(toFill, toAvoid, views, out rewriting))
|
||||
{
|
||||
HashSet<T_Tile> usedViews = new HashSet<T_Tile>();
|
||||
GatherViews(rewriting, usedViews);
|
||||
int opCount = CountOperators(rewriting);
|
||||
|
||||
// try several permutations of views, pick one with fewer operators
|
||||
T_Tile newRewriting;
|
||||
int permuteTries = 0;
|
||||
int numPermutations = Math.Min(MAX_PERMUTATIONS, Math.Max(MIN_PERMUTATIONS, (int)(usedViews.Count * PERMUTE_FRACTION)));
|
||||
while (permuteTries++ < numPermutations)
|
||||
{
|
||||
IEnumerable<T_Tile> permutedViews;
|
||||
if (permuteTries == 1)
|
||||
{
|
||||
permutedViews = Reverse(views, usedViews);
|
||||
}
|
||||
else
|
||||
{
|
||||
permutedViews = RandomPermutation(usedViews); // Tradeoff: views vs. usedViews!
|
||||
}
|
||||
bool succeeded = RewriteQueryOnce(toFill, toAvoid, permutedViews, out newRewriting);
|
||||
Debug.Assert(succeeded);
|
||||
int newOpCount = CountOperators(newRewriting);
|
||||
if (newOpCount < opCount)
|
||||
{
|
||||
opCount = newOpCount;
|
||||
rewriting = newRewriting;
|
||||
}
|
||||
HashSet<T_Tile> newUsedViews = new HashSet<T_Tile>();
|
||||
GatherViews(newRewriting, newUsedViews);
|
||||
usedViews = newUsedViews; // can only be fewer!
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool RewriteQueryOnce(T_Tile toFill, T_Tile toAvoid, IEnumerable<T_Tile> views, out T_Tile rewriting)
|
||||
{
|
||||
List<T_Tile> viewList = new List<T_Tile>(views);
|
||||
return RewritingPass<T_Tile>.RewriteQuery(toFill, toAvoid, out rewriting, viewList, this);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,211 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="RewritingSimplifier.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,127 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="RoleBoolean.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.Structures
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common.CommandTrees;
|
||||
using System.Data.Entity;
|
||||
using System.Data.Metadata.Edm;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// Denotes the fact that the key of the current tuple comes from a specific extent, or association role.
|
||||
/// </summary>
|
||||
internal sealed class RoleBoolean : TrueFalseLiteral
|
||||
{
|
||||
#region Constructor
|
||||
internal RoleBoolean(EntitySetBase extent)
|
||||
{
|
||||
m_metadataItem = extent;
|
||||
}
|
||||
internal RoleBoolean(AssociationSetEnd end)
|
||||
{
|
||||
m_metadataItem = end;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
private readonly MetadataItem m_metadataItem;
|
||||
#endregion
|
||||
|
||||
#region BoolLiteral members
|
||||
/// <summary>
|
||||
/// Not supported in this class.
|
||||
/// </summary>
|
||||
internal override StringBuilder AsEsql(StringBuilder builder, string blockAlias, bool skipIsNotNull)
|
||||
{
|
||||
Debug.Fail("Should not be called.");
|
||||
return null; // To keep the compiler happy
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not supported in this class.
|
||||
/// </summary>
|
||||
internal override DbExpression AsCqt(DbExpression row, bool skipIsNotNull)
|
||||
{
|
||||
Debug.Fail("Should not be called.");
|
||||
return null; // To keep the compiler happy
|
||||
}
|
||||
|
||||
internal override StringBuilder AsUserString(StringBuilder builder, string blockAlias, bool skipIsNotNull)
|
||||
{
|
||||
AssociationSetEnd end = m_metadataItem as AssociationSetEnd;
|
||||
if (end != null)
|
||||
{
|
||||
builder.Append(Strings.ViewGen_AssociationSet_AsUserString(blockAlias, end.Name, end.ParentAssociationSet));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append(Strings.ViewGen_EntitySet_AsUserString(blockAlias, m_metadataItem.ToString()));
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal override StringBuilder AsNegatedUserString(StringBuilder builder, string blockAlias, bool skipIsNotNull)
|
||||
{
|
||||
AssociationSetEnd end = m_metadataItem as AssociationSetEnd;
|
||||
if (end != null)
|
||||
{
|
||||
builder.Append(Strings.ViewGen_AssociationSet_AsUserString_Negated(blockAlias, end.Name, end.ParentAssociationSet));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append(Strings.ViewGen_EntitySet_AsUserString_Negated(blockAlias, m_metadataItem.ToString()));
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
internal override void GetRequiredSlots(MemberProjectionIndex projectedSlotMap, bool[] requiredSlots)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override bool IsEqualTo(BoolLiteral right)
|
||||
{
|
||||
RoleBoolean rightBoolean = right as RoleBoolean;
|
||||
if (rightBoolean == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return m_metadataItem == rightBoolean.m_metadataItem;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return m_metadataItem.GetHashCode();
|
||||
}
|
||||
|
||||
internal override BoolLiteral RemapBool(Dictionary<MemberPath, MemberPath> remap)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Other Methods
|
||||
internal override void ToCompactString(StringBuilder builder)
|
||||
{
|
||||
AssociationSetEnd end = m_metadataItem as AssociationSetEnd;
|
||||
if (end != null)
|
||||
{
|
||||
builder.Append("InEnd:" + end.ParentAssociationSet + "_" + end.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append("InSet:" + m_metadataItem.ToString());
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -0,0 +1,254 @@
|
||||
//---------------------------------------------------------------------
|
||||
// <copyright file="Tile.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//
|
||||
// @owner [....]
|
||||
// @backupOwner [....]
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Globalization;
|
||||
|
||||
namespace System.Data.Mapping.ViewGeneration.QueryRewriting
|
||||
{
|
||||
internal enum TileOpKind
|
||||
{
|
||||
Union,
|
||||
Join,
|
||||
AntiSemiJoin,
|
||||
// Project,
|
||||
Named
|
||||
}
|
||||
|
||||
internal interface ITileQuery
|
||||
{
|
||||
string Description { get; }
|
||||
}
|
||||
|
||||
internal abstract class TileQueryProcessor<T_Query> where T_Query : ITileQuery
|
||||
{
|
||||
internal abstract T_Query Intersect(T_Query arg1, T_Query arg2);
|
||||
internal abstract T_Query Difference(T_Query arg1, T_Query arg2);
|
||||
internal abstract T_Query Union(T_Query arg1, T_Query arg2);
|
||||
internal abstract bool IsSatisfiable(T_Query query);
|
||||
internal abstract T_Query CreateDerivedViewBySelectingConstantAttributes(T_Query query);
|
||||
}
|
||||
|
||||
internal class DefaultTileProcessor<T_Query> : TileProcessor<Tile<T_Query>> where T_Query : ITileQuery
|
||||
{
|
||||
private readonly TileQueryProcessor<T_Query> _tileQueryProcessor;
|
||||
|
||||
internal DefaultTileProcessor(TileQueryProcessor<T_Query> tileQueryProcessor)
|
||||
{
|
||||
_tileQueryProcessor = tileQueryProcessor;
|
||||
}
|
||||
|
||||
internal TileQueryProcessor<T_Query> QueryProcessor
|
||||
{
|
||||
get { return _tileQueryProcessor; }
|
||||
}
|
||||
|
||||
internal override bool IsEmpty(Tile<T_Query> tile)
|
||||
{
|
||||
return false == _tileQueryProcessor.IsSatisfiable(tile.Query);
|
||||
}
|
||||
|
||||
internal override Tile<T_Query> Union(Tile<T_Query> arg1, Tile<T_Query> arg2)
|
||||
{
|
||||
return new TileBinaryOperator<T_Query>(arg1, arg2, TileOpKind.Union, _tileQueryProcessor.Union(arg1.Query, arg2.Query));
|
||||
}
|
||||
|
||||
internal override Tile<T_Query> Join(Tile<T_Query> arg1, Tile<T_Query> arg2)
|
||||
{
|
||||
return new TileBinaryOperator<T_Query>(arg1, arg2, TileOpKind.Join, _tileQueryProcessor.Intersect(arg1.Query, arg2.Query));
|
||||
}
|
||||
|
||||
internal override Tile<T_Query> AntiSemiJoin(Tile<T_Query> arg1, Tile<T_Query> arg2)
|
||||
{
|
||||
return new TileBinaryOperator<T_Query>(arg1, arg2, TileOpKind.AntiSemiJoin, _tileQueryProcessor.Difference(arg1.Query, arg2.Query));
|
||||
}
|
||||
|
||||
internal override Tile<T_Query> GetArg1(Tile<T_Query> tile)
|
||||
{
|
||||
return tile.Arg1;
|
||||
}
|
||||
|
||||
internal override Tile<T_Query> GetArg2(Tile<T_Query> tile)
|
||||
{
|
||||
return tile.Arg2;
|
||||
}
|
||||
|
||||
internal override TileOpKind GetOpKind(Tile<T_Query> tile)
|
||||
{
|
||||
return tile.OpKind;
|
||||
}
|
||||
|
||||
internal bool IsContainedIn(Tile<T_Query> arg1, Tile<T_Query> arg2)
|
||||
{
|
||||
return IsEmpty(AntiSemiJoin(arg1, arg2));
|
||||
}
|
||||
|
||||
internal bool IsEquivalentTo(Tile<T_Query> arg1, Tile<T_Query> arg2)
|
||||
{
|
||||
return IsContainedIn(arg1, arg2) && IsContainedIn(arg2, arg1);
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class Tile<T_Query> where T_Query : ITileQuery
|
||||
{
|
||||
private readonly T_Query m_query;
|
||||
private readonly TileOpKind m_opKind;
|
||||
|
||||
protected Tile(TileOpKind opKind, T_Query query)
|
||||
{
|
||||
m_opKind = opKind;
|
||||
m_query = query;
|
||||
}
|
||||
|
||||
public T_Query Query
|
||||
{
|
||||
get { return m_query; }
|
||||
}
|
||||
|
||||
public abstract string Description { get; }
|
||||
|
||||
// multiple occurrences possible
|
||||
public IEnumerable<T_Query> GetNamedQueries()
|
||||
{
|
||||
return GetNamedQueries(this);
|
||||
}
|
||||
private static IEnumerable<T_Query> GetNamedQueries(Tile<T_Query> rewriting)
|
||||
{
|
||||
if (rewriting != null)
|
||||
{
|
||||
if (rewriting.OpKind == TileOpKind.Named)
|
||||
{
|
||||
yield return ((TileNamed<T_Query>)rewriting).NamedQuery;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (T_Query query in GetNamedQueries(rewriting.Arg1))
|
||||
{
|
||||
yield return query;
|
||||
}
|
||||
foreach (T_Query query in GetNamedQueries(rewriting.Arg2))
|
||||
{
|
||||
yield return query;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string formattedQuery = this.Description;
|
||||
if (formattedQuery != null)
|
||||
{
|
||||
return String.Format(CultureInfo.InvariantCulture, "{0}: [{1}]", this.Description, this.Query);
|
||||
}
|
||||
else
|
||||
{
|
||||
return String.Format(CultureInfo.InvariantCulture, "[{0}]", this.Query);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract Tile<T_Query> Arg1
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract Tile<T_Query> Arg2
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public TileOpKind OpKind
|
||||
{
|
||||
get { return m_opKind; }
|
||||
}
|
||||
|
||||
internal abstract Tile<T_Query> Replace(Tile<T_Query> oldTile, Tile<T_Query> newTile);
|
||||
}
|
||||
|
||||
internal class TileNamed<T_Query> : Tile<T_Query> where T_Query : ITileQuery
|
||||
{
|
||||
public TileNamed(T_Query namedQuery)
|
||||
: base(TileOpKind.Named, namedQuery)
|
||||
{
|
||||
Debug.Assert(namedQuery != null);
|
||||
}
|
||||
|
||||
public T_Query NamedQuery
|
||||
{
|
||||
get { return this.Query; }
|
||||
}
|
||||
|
||||
public override Tile<T_Query> Arg1 { get { return null; } }
|
||||
public override Tile<T_Query> Arg2 { get { return null; } }
|
||||
|
||||
public override string Description
|
||||
{
|
||||
get { return this.Query.Description; }
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this.Query.ToString();
|
||||
}
|
||||
|
||||
internal override Tile<T_Query> Replace(Tile<T_Query> oldTile, Tile<T_Query> newTile)
|
||||
{
|
||||
return (this == oldTile) ? newTile : this;
|
||||
}
|
||||
}
|
||||
|
||||
internal class TileBinaryOperator<T_Query> : Tile<T_Query> where T_Query : ITileQuery
|
||||
{
|
||||
private readonly Tile<T_Query> m_arg1;
|
||||
private readonly Tile<T_Query> m_arg2;
|
||||
|
||||
public TileBinaryOperator(Tile<T_Query> arg1, Tile<T_Query> arg2, TileOpKind opKind, T_Query query)
|
||||
: base(opKind, query)
|
||||
{
|
||||
Debug.Assert(arg1 != null && arg2 != null);
|
||||
m_arg1 = arg1;
|
||||
m_arg2 = arg2;
|
||||
}
|
||||
|
||||
public override Tile<T_Query> Arg1 { get { return m_arg1; } }
|
||||
public override Tile<T_Query> Arg2 { get { return m_arg2; } }
|
||||
|
||||
public override string Description
|
||||
{
|
||||
get
|
||||
{
|
||||
string descriptionFormat = null;
|
||||
switch (OpKind)
|
||||
{
|
||||
case TileOpKind.Join: descriptionFormat = "({0} & {1})"; break;
|
||||
case TileOpKind.AntiSemiJoin: descriptionFormat = "({0} - {1})"; break;
|
||||
case TileOpKind.Union: descriptionFormat = "({0} | {1})"; break;
|
||||
default: Debug.Fail("Unexpected binary operator"); break;
|
||||
}
|
||||
return String.Format(CultureInfo.InvariantCulture, descriptionFormat, this.Arg1.Description, this.Arg2.Description);
|
||||
}
|
||||
}
|
||||
|
||||
internal override Tile<T_Query> Replace(Tile<T_Query> oldTile, Tile<T_Query> newTile)
|
||||
{
|
||||
Tile<T_Query> newArg1 = Arg1.Replace(oldTile, newTile);
|
||||
Tile<T_Query> newArg2 = Arg2.Replace(oldTile, newTile);
|
||||
if (newArg1 != Arg1 || newArg2 != Arg2)
|
||||
{
|
||||
return new TileBinaryOperator<T_Query>(newArg1, newArg2, OpKind, Query);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user