File: ConstraintManager.cs

package info (click to toggle)
mono 6.12.0.199%2Bdfsg-6
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 1,296,836 kB
  • sloc: cs: 11,181,803; xml: 2,850,076; ansic: 699,709; cpp: 123,344; perl: 59,361; javascript: 30,841; asm: 21,853; makefile: 20,405; sh: 15,009; python: 4,839; pascal: 925; sql: 859; sed: 16; php: 1
file content (318 lines) | stat: -rw-r--r-- 12,793 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
//---------------------------------------------------------------------
// <copyright file="ConstraintManager.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner  Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Data.Common;
using System.Data.Query.InternalTrees;
using md=System.Data.Metadata.Edm;
//using System.Diagnostics; // Please use PlanCompiler.Assert instead of Debug.Assert in this class...

// It is fine to use Debug.Assert in cases where you assert an obvious thing that is supposed
// to prevent from simple mistakes during development (e.g. method argument validation 
// in cases where it was you who created the variables or the variables had already been validated or 
// in "else" clauses where due to code changes (e.g. adding a new value to an enum type) the default 
// "else" block is chosen why the new condition should be treated separately). This kind of asserts are 
// (can be) helpful when developing new code to avoid simple mistakes but have no or little value in 
// the shipped product. 
// PlanCompiler.Assert *MUST* be used to verify conditions in the trees. These would be assumptions 
// about how the tree was built etc. - in these cases we probably want to throw an exception (this is
// what PlanCompiler.Assert does when the condition is not met) if either the assumption is not correct 
// or the tree was built/rewritten not the way we thought it was.
// Use your judgment - if you rather remove an assert than ship it use Debug.Assert otherwise use
// PlanCompiler.Assert.

//
// The ConstraintManager module manages foreign key constraints for a query. It reshapes
// referential constraints supplied by metadata into a more useful form.
//
namespace System.Data.Query.PlanCompiler
{
    /// <summary>
    /// A simple class that represents a pair of extents
    /// </summary>
    internal class ExtentPair
    {
        #region public surface
        /// <summary>
        /// Return the left component of the pair
        /// </summary>
        internal md.EntitySetBase Left { get { return m_left; } }

        /// <summary>
        /// Return the right component of the pair
        /// </summary>
        internal md.EntitySetBase Right { get { return m_right; } }

        /// <summary>
        /// Equals
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            ExtentPair other = obj as ExtentPair;
            return (other != null) && other.Left.Equals(this.Left) && other.Right.Equals(this.Right);
        }

        /// <summary>
        /// Hashcode
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return (this.Left.GetHashCode() << 4) ^ this.Right.GetHashCode();
        }
        #endregion

        #region constructors
        internal ExtentPair(md.EntitySetBase left, md.EntitySetBase right)
        {
            m_left = left;
            m_right = right;
        }
        #endregion

        #region private state
        private md.EntitySetBase m_left;
        private md.EntitySetBase m_right;
        #endregion
    }

    /// <summary>
    /// Information about a foreign-key constraint
    /// </summary>
    internal class ForeignKeyConstraint
    {
        #region public surface

        /// <summary>
        /// Parent key properties
        /// </summary>
        internal List<string> ParentKeys { get { return m_parentKeys; } }
        /// <summary>
        /// Child key properties
        /// </summary>
        internal List<string> ChildKeys { get { return m_childKeys; } }

        /// <summary>
        /// Get the parent-child pair
        /// </summary>
        internal ExtentPair Pair { get { return m_extentPair; } }

        /// <summary>
        /// Return the child rowcount
        /// </summary>
        internal md.RelationshipMultiplicity ChildMultiplicity { get { return m_constraint.ToRole.RelationshipMultiplicity; } }

        /// <summary>
        /// Get the corresponding parent (key) property, for a specific child (foreign key) property
        /// </summary>
        /// <param name="childPropertyName">child (foreign key) property name</param>
        /// <param name="parentPropertyName">corresponding parent property name</param>
        /// <returns>true, if the parent property was found</returns>
        internal bool GetParentProperty(string childPropertyName, out string parentPropertyName)
        {
            BuildKeyMap();
            return m_keyMap.TryGetValue(childPropertyName, out parentPropertyName);
        }
        #endregion

        #region constructors
        internal ForeignKeyConstraint(md.RelationshipType relType, md.RelationshipSet relationshipSet, md.ReferentialConstraint constraint)
        {
            md.AssociationSet assocSet = relationshipSet as md.AssociationSet;
            md.AssociationEndMember fromEnd = constraint.FromRole as md.AssociationEndMember;
            md.AssociationEndMember toEnd = constraint.ToRole as md.AssociationEndMember;

            // Currently only Associations are supported
            if (null == assocSet || null == fromEnd || null == toEnd)
            {
                throw EntityUtil.NotSupported();
            }

            m_constraint = constraint;
            md.EntitySet parent = System.Data.Common.Utils.MetadataHelper.GetEntitySetAtEnd(assocSet, fromEnd);// relationshipSet.GetRelationshipEndExtent(constraint.FromRole);
            md.EntitySet child = System.Data.Common.Utils.MetadataHelper.GetEntitySetAtEnd(assocSet, toEnd);// relationshipSet.GetRelationshipEndExtent(constraint.ToRole);
            m_extentPair = new ExtentPair(parent, child);
            m_childKeys = new List<string>();
            foreach (md.EdmProperty prop in constraint.ToProperties)
            {
                m_childKeys.Add(prop.Name);
            }

            m_parentKeys = new List<string>();
            foreach (md.EdmProperty prop in constraint.FromProperties)
            {
                m_parentKeys.Add(prop.Name);
            }

            PlanCompiler.Assert((md.RelationshipMultiplicity.ZeroOrOne == fromEnd.RelationshipMultiplicity || md.RelationshipMultiplicity.One == fromEnd.RelationshipMultiplicity), "from-end of relationship constraint cannot have multiplicity greater than 1");
        }
        #endregion

        #region private state
        private ExtentPair m_extentPair;
        private List<string> m_parentKeys;
        private List<string> m_childKeys;
        private md.ReferentialConstraint m_constraint;
        private Dictionary<string, string> m_keyMap;
        #endregion

        #region private methods

        /// <summary>
        /// Build up an equivalence map of primary keys and foreign keys (ie) for each
        /// foreign key column, identify the corresponding primary key property
        /// </summary>
        private void BuildKeyMap()
        {
            if (m_keyMap != null)
            {
                return;
            }

            m_keyMap = new Dictionary<string, string>();
            IEnumerator<md.EdmProperty> parentProps = m_constraint.FromProperties.GetEnumerator();
            IEnumerator<md.EdmProperty> childProps = m_constraint.ToProperties.GetEnumerator();
            while (true)
            {
                bool parentOver = !parentProps.MoveNext();
                bool childOver = !childProps.MoveNext();
                PlanCompiler.Assert(parentOver == childOver, "key count mismatch");
                if (parentOver)
                {
                    break;
                }
                m_keyMap[childProps.Current.Name] = parentProps.Current.Name;
            }
        }
        #endregion
    }

    /// <summary>
    /// Keeps track of all foreign key relationships
    /// </summary>
    internal class ConstraintManager
    {
        #region public methods
        /// <summary>
        /// Is there a parent child relationship between table1 and table2 ?
        /// </summary>
        /// <param name="table1">parent table ?</param>
        /// <param name="table2">child table ?</param>
        /// <param name="constraints">list of constraints ?</param>
        /// <returns>true if there is at least one constraint</returns>
        internal bool IsParentChildRelationship(md.EntitySetBase table1, md.EntitySetBase table2,
            out List<ForeignKeyConstraint> constraints)
        {
            LoadRelationships(table1.EntityContainer);
            LoadRelationships(table2.EntityContainer);

            ExtentPair extentPair = new ExtentPair(table1, table2);
            return m_parentChildRelationships.TryGetValue(extentPair, out constraints);
        }

        /// <summary>
        /// Load all relationships in this entity container
        /// </summary>
        /// <param name="entityContainer"></param>
        internal void LoadRelationships(md.EntityContainer entityContainer)
        {
            // Check to see if I've already loaded information for this entity container
            if (m_entityContainerMap.ContainsKey(entityContainer))
            {
                return;
            }

            // Load all relationships from this entitycontainer
            foreach (md.EntitySetBase e in entityContainer.BaseEntitySets)
            {
                md.RelationshipSet relationshipSet = e as md.RelationshipSet;
                if (relationshipSet == null)
                {
                    continue;
                }

                // Relationship sets can only contain relationships
                md.RelationshipType relationshipType = (md.RelationshipType)relationshipSet.ElementType;
                md.AssociationType assocType = relationshipType as md.AssociationType;

                //
                // Handle only binary Association relationships for now
                //
                if (null == assocType || !IsBinary(relationshipType))
                {
                    continue;
                }

                foreach (md.ReferentialConstraint constraint in assocType.ReferentialConstraints)
                {
                    List<ForeignKeyConstraint> fkConstraintList;
                    ForeignKeyConstraint fkConstraint = new ForeignKeyConstraint(relationshipType, relationshipSet, constraint);
                    if (!m_parentChildRelationships.TryGetValue(fkConstraint.Pair, out fkConstraintList))
                    {
                        fkConstraintList = new List<ForeignKeyConstraint>();
                        m_parentChildRelationships[fkConstraint.Pair] = fkConstraintList;
                    }
                    //
                    // Theoretically, we can have more than one fk constraint between
                    // the 2 tables (though, it is unlikely)
                    //
                    fkConstraintList.Add(fkConstraint);
                }
            }

            // Mark this entity container as already loaded
            m_entityContainerMap[entityContainer] = entityContainer;
        }
        #endregion

        #region constructors
        internal ConstraintManager()
        {
            m_entityContainerMap = new Dictionary<md.EntityContainer, md.EntityContainer>();
            m_parentChildRelationships = new Dictionary<ExtentPair, List<ForeignKeyConstraint>>();
        }
        #endregion

        #region private state
        private Dictionary<md.EntityContainer, md.EntityContainer> m_entityContainerMap;
        private Dictionary<ExtentPair, List<ForeignKeyConstraint>> m_parentChildRelationships;
        #endregion

        #region private methods

        /// <summary>
        /// Is this relationship a binary relationship (ie) does it have exactly 2 end points?
        /// 
        /// This should ideally be a method supported by RelationType itself
        /// </summary>
        /// <param name="relationshipType"></param>
        /// <returns>true, if this is a binary relationship</returns>
        private static bool IsBinary(md.RelationshipType relationshipType)
        {
            int endCount = 0;
            foreach(md.EdmMember member in relationshipType.Members)
            {
                if (member is md.RelationshipEndMember)
                {
                    endCount++;
                    if (endCount > 2)
                    {
                        return false;
                    }
                }
            }
            return (endCount == 2);
        }
        #endregion
    }
}