1
//---------------------------------------------------------------------
2 // <copyright file="FragmentQueryKB.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
11 using System
.Diagnostics
;
12 using System
.Collections
.Generic
;
14 using System
.Data
.Common
.Utils
;
15 using System
.Data
.Common
.Utils
.Boolean
;
16 using System
.Data
.Mapping
.ViewGeneration
.Structures
;
17 using System
.Data
.Metadata
.Edm
;
20 namespace System
.Data
.Mapping
.ViewGeneration
.QueryRewriting
22 internal class FragmentQueryKB
: KnowledgeBase
<DomainConstraint
<BoolLiteral
, Constant
>>
24 private BoolExpr
<DomainConstraint
<BoolLiteral
, Constant
>> _kbExpression
= TrueExpr
<DomainConstraint
<BoolLiteral
, Constant
>>.Value
;
26 internal override void AddFact(BoolExpr
<DomainConstraint
<BoolLiteral
, Constant
>> fact
)
29 _kbExpression
= new AndExpr
<DomainConstraint
<BoolLiteral
, Constant
>>(_kbExpression
, fact
);
31 internal BoolExpr
<DomainConstraint
<BoolLiteral
, Constant
>> KbExpression
33 get { return _kbExpression; }
36 internal void CreateVariableConstraints(EntitySetBase extent
, MemberDomainMap domainMap
, EdmItemCollection edmItemCollection
)
38 CreateVariableConstraintsRecursion(extent
.ElementType
, new MemberPath(extent
), domainMap
, edmItemCollection
);
41 internal void CreateAssociationConstraints(EntitySetBase extent
, MemberDomainMap domainMap
, EdmItemCollection edmItemCollection
)
43 AssociationSet assocSet
= extent
as AssociationSet
;
46 BoolExpression assocSetExpr
= BoolExpression
.CreateLiteral(new RoleBoolean(assocSet
), domainMap
);
48 //Set of Keys for this Association Set
49 //need to key on EdmMember and EdmType because A, B subtype of C, can have the same id (EdmMember) that is defined in C.
50 HashSet
<Pair
<EdmMember
, EntityType
>> associationkeys
= new HashSet
<Pair
<EdmMember
, EntityType
>>();
53 //foreach end, add each Key
54 foreach (var endMember
in assocSet
.ElementType
.AssociationEndMembers
)
56 EntityType type
= (EntityType
)((RefType
)endMember
.TypeUsage
.EdmType
).ElementType
;
57 type
.KeyMembers
.All(member
=> associationkeys
.Add(new Pair
<EdmMember
, EntityType
>(member
, type
)) || true /* prevent early termination */);
60 foreach (AssociationSetEnd end
in assocSet
.AssociationSetEnds
)
62 // construct type condition
63 HashSet
<EdmType
> derivedTypes
= new HashSet
<EdmType
>();
64 derivedTypes
.UnionWith(MetadataHelper
.GetTypeAndSubtypesOf(end
.CorrespondingAssociationEndMember
.TypeUsage
.EdmType
, edmItemCollection
, false));
66 BoolExpression typeCondition
= CreateIsOfTypeCondition(new MemberPath(end
.EntitySet
),
67 derivedTypes
, domainMap
);
69 BoolExpression inRoleExpression
= BoolExpression
.CreateLiteral(new RoleBoolean(end
), domainMap
);
70 BoolExpression inSetExpression
= BoolExpression
.CreateAnd(
71 BoolExpression
.CreateLiteral(new RoleBoolean(end
.EntitySet
), domainMap
),
74 // InRole -> (InSet AND type(Set)=T)
75 AddImplication(inRoleExpression
.Tree
, inSetExpression
.Tree
);
77 if (MetadataHelper
.IsEveryOtherEndAtLeastOne(assocSet
, end
.CorrespondingAssociationEndMember
))
79 AddImplication(inSetExpression
.Tree
, inRoleExpression
.Tree
);
82 // Add equivalence between association set an End/Role if necessary.
83 // Equivalence is added when a given association end's keys subsumes keys for
84 // all the other association end.
86 // For example: We have Entity Sets A[id1], B[id2, id3] and an association A_B between them.
87 // Ref Constraint A.id1 = B.id2
88 // In this case, the Association Set has Key <id1, id2, id3>
89 // id1 alone can not identify a unique tuple in the Association Set, but <id2, id3> can.
90 // Therefore we add a constraint: InSet(B) <=> InEnd(A_B.B)
92 if (MetadataHelper
.DoesEndKeySubsumeAssociationSetKey(assocSet
,
93 end
.CorrespondingAssociationEndMember
,
96 AddEquivalence(inRoleExpression
.Tree
, assocSetExpr
.Tree
);
101 // add rules for referential constraints (borrowed from LeftCellWrapper.cs)
102 AssociationType assocType
= assocSet
.ElementType
;
104 foreach (ReferentialConstraint constraint
in assocType
.ReferentialConstraints
)
106 AssociationEndMember toEndMember
= (AssociationEndMember
)constraint
.ToRole
;
107 EntitySet toEntitySet
= MetadataHelper
.GetEntitySetAtEnd(assocSet
, toEndMember
);
108 // Check if the keys of the entitySet's are equal to what is specified in the constraint
109 // How annoying that KeyMembers returns EdmMember and not EdmProperty
110 IEnumerable
<EdmMember
> toProperties
= Helpers
.AsSuperTypeList
<EdmProperty
, EdmMember
>(constraint
.ToProperties
);
111 if (Helpers
.IsSetEqual(toProperties
, toEntitySet
.ElementType
.KeyMembers
, EqualityComparer
<EdmMember
>.Default
))
113 // Now check that the FromEnd is 1..1 (only then will all the Addresses be present in the assoc set)
114 if (constraint
.FromRole
.RelationshipMultiplicity
.Equals(RelationshipMultiplicity
.One
))
116 // Make sure that the ToEnd is not 0..* because then the schema is broken
117 Debug
.Assert(constraint
.ToRole
.RelationshipMultiplicity
.Equals(RelationshipMultiplicity
.Many
) == false);
119 BoolExpression inRoleExpression1
= BoolExpression
.CreateLiteral(new RoleBoolean(assocSet
.AssociationSetEnds
[0]), domainMap
);
120 BoolExpression inRoleExpression2
= BoolExpression
.CreateLiteral(new RoleBoolean(assocSet
.AssociationSetEnds
[1]), domainMap
);
121 AddEquivalence(inRoleExpression1
.Tree
, inRoleExpression2
.Tree
);
128 internal void CreateEquivalenceConstraintForOneToOneForeignKeyAssociation(AssociationSet assocSet
, MemberDomainMap domainMap
,
129 EdmItemCollection edmItemCollection
)
131 AssociationType assocType
= assocSet
.ElementType
;
132 foreach (ReferentialConstraint constraint
in assocType
.ReferentialConstraints
)
134 AssociationEndMember toEndMember
= (AssociationEndMember
)constraint
.ToRole
;
135 AssociationEndMember fromEndMember
= (AssociationEndMember
)constraint
.FromRole
;
136 EntitySet toEntitySet
= MetadataHelper
.GetEntitySetAtEnd(assocSet
, toEndMember
);
137 EntitySet fromEntitySet
= MetadataHelper
.GetEntitySetAtEnd(assocSet
, fromEndMember
);
139 // Check if the keys of the entitySet's are equal to what is specified in the constraint
140 IEnumerable
<EdmMember
> toProperties
= Helpers
.AsSuperTypeList
<EdmProperty
, EdmMember
>(constraint
.ToProperties
);
141 if (Helpers
.IsSetEqual(toProperties
, toEntitySet
.ElementType
.KeyMembers
, EqualityComparer
<EdmMember
>.Default
))
143 //make sure that the method called with a 1:1 association
144 Debug
.Assert(constraint
.FromRole
.RelationshipMultiplicity
.Equals(RelationshipMultiplicity
.One
));
145 Debug
.Assert(constraint
.ToRole
.RelationshipMultiplicity
.Equals(RelationshipMultiplicity
.One
));
146 // Create an Equivalence between the two Sets participating in this AssociationSet
147 BoolExpression fromSetExpression
= BoolExpression
.CreateLiteral(new RoleBoolean(fromEntitySet
), domainMap
);
148 BoolExpression toSetExpression
= BoolExpression
.CreateLiteral(new RoleBoolean(toEntitySet
), domainMap
);
149 AddEquivalence(fromSetExpression
.Tree
, toSetExpression
.Tree
);
154 private void CreateVariableConstraintsRecursion(EdmType edmType
, MemberPath currentPath
, MemberDomainMap domainMap
, EdmItemCollection edmItemCollection
)
156 // Add the types can member have, i.e., its type and its subtypes
157 HashSet
<EdmType
> possibleTypes
= new HashSet
<EdmType
>();
158 possibleTypes
.UnionWith(MetadataHelper
.GetTypeAndSubtypesOf(edmType
, edmItemCollection
, true));
160 foreach (EdmType possibleType
in possibleTypes
)
162 // determine type domain
164 HashSet
<EdmType
> derivedTypes
= new HashSet
<EdmType
>();
165 derivedTypes
.UnionWith(MetadataHelper
.GetTypeAndSubtypesOf(possibleType
, edmItemCollection
, false));
166 if (derivedTypes
.Count
!= 0)
168 BoolExpression typeCondition
= CreateIsOfTypeCondition(currentPath
, derivedTypes
, domainMap
);
169 BoolExpression typeConditionComplement
= BoolExpression
.CreateNot(typeCondition
);
170 if (false == typeConditionComplement
.IsSatisfiable())
175 StructuralType structuralType
= (StructuralType
)possibleType
;
176 foreach (EdmProperty childProperty
in structuralType
.GetDeclaredOnlyMembers
<EdmProperty
>())
178 MemberPath childPath
= new MemberPath(currentPath
, childProperty
);
179 bool isScalar
= MetadataHelper
.IsNonRefSimpleMember(childProperty
);
181 if (domainMap
.IsConditionMember(childPath
) || domainMap
.IsProjectedConditionMember(childPath
))
183 BoolExpression nullCondition
;
184 List
<Constant
> childDomain
= new List
<Constant
>(domainMap
.GetDomain(childPath
));
187 nullCondition
= BoolExpression
.CreateLiteral(new ScalarRestriction(new MemberProjectedSlot(childPath
),
188 new Domain(ScalarConstant
.Undefined
, childDomain
)), domainMap
);
192 nullCondition
= BoolExpression
.CreateLiteral(new TypeRestriction(new MemberProjectedSlot(childPath
),
193 new Domain(TypeConstant
.Undefined
, childDomain
)), domainMap
);
195 // Properties not occuring in type are UNDEFINED
196 AddEquivalence(typeConditionComplement
.Tree
, nullCondition
.Tree
);
199 // recurse into complex types
200 if (false == isScalar
)
202 CreateVariableConstraintsRecursion(childPath
.EdmType
, childPath
, domainMap
, edmItemCollection
);
209 private static BoolExpression
CreateIsOfTypeCondition(MemberPath currentPath
, IEnumerable
<EdmType
> derivedTypes
, MemberDomainMap domainMap
)
211 Domain typeDomain
= new Domain(derivedTypes
.Select(derivedType
=> (Constant
)new TypeConstant(derivedType
)), domainMap
.GetDomain(currentPath
));
212 BoolExpression typeCondition
= BoolExpression
.CreateLiteral(new TypeRestriction(new MemberProjectedSlot(currentPath
), typeDomain
), domainMap
);
213 return typeCondition
;