Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Mapping / ViewGeneration / ViewgenGatekeeper.cs
1 //---------------------------------------------------------------------
2 // <copyright file="ViewgenGatekeeper.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9
10 using System.Collections.Generic;
11 using System.Data.Common.Utils;
12 using System.Data.Common.Utils.Boolean;
13 using System.Data.Mapping.ViewGeneration.Structures;
14 using System.Data.Mapping.ViewGeneration.Utils;
15 using System.Data.Mapping.ViewGeneration.Validation;
16 using System.Data.Metadata.Edm;
17 using System.Diagnostics;
18 using System.Linq;
19 using System.Text;
20
21 namespace System.Data.Mapping.ViewGeneration
22 {
23     using CellGroup = Set<Cell>;
24
25     abstract internal class ViewgenGatekeeper : InternalBase
26     {
27         /// <summary>
28         /// Entry point for View Generation
29         /// </summary>
30         /// <param name="containerMapping"></param>
31         /// <param name="workSpace"></param>
32         /// <param name="config"></param>
33         /// <returns>Generated Views for EntitySets</returns>
34         internal static ViewGenResults GenerateViewsFromMapping(StorageEntityContainerMapping containerMapping, ConfigViewGenerator config)
35         {
36             EntityUtil.CheckArgumentNull(containerMapping, "containerMapping");
37             EntityUtil.CheckArgumentNull(config, "config");
38             Debug.Assert(containerMapping.HasViews, "Precondition Violated: No mapping exists to generate views for!");
39
40             if (config.IsNormalTracing)
41             {
42                 containerMapping.Print(0);
43             }
44
45             //Create Cells from StorageEntityContainerMapping
46             CellCreator cellCreator = new CellCreator(containerMapping);
47             List<Cell> cells = cellCreator.GenerateCells(config);
48             CqlIdentifiers identifiers = cellCreator.Identifiers;
49
50             return GenerateViewsFromCells(cells, config, identifiers, containerMapping);
51         }
52
53         /// <summary>
54         /// Entry point for Type specific generation of Query Views
55         /// </summary>
56         internal static ViewGenResults GenerateTypeSpecificQueryView(StorageEntityContainerMapping containerMapping,
57                                                               ConfigViewGenerator config,
58                                                               EntitySetBase entity,
59                                                               EntityTypeBase type,
60                                                               bool includeSubtypes,
61                                                               out bool success)
62         {
63             EntityUtil.CheckArgumentNull(containerMapping, "containerMapping");
64             EntityUtil.CheckArgumentNull(config, "config");
65             EntityUtil.CheckArgumentNull(entity, "entity");
66             EntityUtil.CheckArgumentNull(type, "type");
67             Debug.Assert(!type.Abstract, "Can not generate OfType/OfTypeOnly query view for and abstract type");
68
69             if (config.IsNormalTracing)
70             {
71                 Helpers.StringTraceLine("");
72                 Helpers.StringTraceLine("<<<<<<<< Generating Query View for Entity [" + entity.Name + "] OfType" + (includeSubtypes ? "" : "Only") + "(" + type.Name + ") >>>>>>>");
73             }
74
75             if (containerMapping.GetEntitySetMapping(entity.Name).QueryView != null)
76             {
77                 //Type-specific QV does not exist in the cache, but 
78                 // there is a EntitySet QV. So we can't generate the view (no mapping exists for this EntitySet)
79                 // and we rely on Query to call us again to get the EntitySet View.
80                 success = false;
81                 return null;
82             }
83
84             //Compute Cell Groups or get it from Memoizer
85             InputForComputingCellGroups args = new InputForComputingCellGroups(containerMapping, config);
86             OutputFromComputeCellGroups result = containerMapping.GetCellgroups(args);
87             success = result.Success;
88
89             if (!success)
90             {
91                 return null;
92             }
93
94             List<ForeignConstraint> foreignKeyConstraints = result.ForeignKeyConstraints;
95             // Get a Clone of cell groups from cache since cells are modified during viewgen, and we dont want the cached copy to change
96             List<CellGroup> cellGroups = cellGroups = result.CellGroups.Select(setOfcells => new CellGroup(setOfcells.Select(cell => new Cell(cell)))).ToList();
97             List<Cell> cells = result.Cells;
98             CqlIdentifiers identifiers = result.Identifiers;
99
100
101             ViewGenResults viewGenResults = new ViewGenResults();
102             ErrorLog tmpLog = EnsureAllCSpaceContainerSetsAreMapped(cells, config, containerMapping);
103             if (tmpLog.Count > 0)
104             {
105                 viewGenResults.AddErrors(tmpLog);
106                 Helpers.StringTraceLine(viewGenResults.ErrorsToString());
107                 success = true; //atleast we tried successfully
108                 return viewGenResults;
109             }
110
111             foreach (CellGroup cellGroup in cellGroups)
112             {
113                 if (!DoesCellGroupContainEntitySet(cellGroup, entity))
114                 {
115                     continue;
116                 }
117
118                 ViewGenerator viewGenerator = null;
119                 ErrorLog groupErrorLog = new ErrorLog();
120                 try
121                 {
122                     viewGenerator = new ViewGenerator(cellGroup, config, foreignKeyConstraints, containerMapping);
123                 }
124                 catch (InternalMappingException exception)
125                 {
126                     // All exceptions have mapping errors in them
127                     Debug.Assert(exception.ErrorLog.Count > 0, "Incorrectly created mapping exception");
128                     groupErrorLog = exception.ErrorLog;
129                 }
130
131                 if (groupErrorLog.Count > 0)
132                 {
133                     break;
134                 }
135                 Debug.Assert(viewGenerator != null); //make sure there is no exception thrown that does not add error to log
136
137                 ViewGenMode mode = includeSubtypes ? ViewGenMode.OfTypeViews : ViewGenMode.OfTypeOnlyViews;
138
139                 groupErrorLog = viewGenerator.GenerateQueryViewForSingleExtent(viewGenResults.Views, identifiers, entity, type, mode);
140
141                 if (groupErrorLog.Count != 0)
142                 {
143                     viewGenResults.AddErrors(groupErrorLog);
144                 }
145             }
146
147             success = true;
148             return viewGenResults;
149         }
150
151
152         // effects: Given a list of cells in the schema, generates the query and
153         // update mapping views for OFTYPE(Extent, Type) combinations in this schema
154         // container. Returns a list of generated query and update views.
155         // If it is false and some columns in a table are unmapped, an
156         // exception is raised
157         private static ViewGenResults GenerateViewsFromCells(List<Cell> cells, ConfigViewGenerator config,
158                                                                    CqlIdentifiers identifiers,
159                                                                    StorageEntityContainerMapping containerMapping)
160         {
161             EntityUtil.CheckArgumentNull(cells, "cells");
162             EntityUtil.CheckArgumentNull(config, "config");
163             Debug.Assert(cells.Count > 0, "There must be at least one cell in the container mapping");
164
165
166             // Go through each table and determine their foreign key constraints
167             EntityContainer container = containerMapping.StorageEntityContainer;
168             Debug.Assert(container != null);
169
170             ViewGenResults viewGenResults = new ViewGenResults();
171             ErrorLog tmpLog = EnsureAllCSpaceContainerSetsAreMapped(cells, config, containerMapping);
172             if (tmpLog.Count > 0)
173             {
174                 viewGenResults.AddErrors(tmpLog);
175                 Helpers.StringTraceLine(viewGenResults.ErrorsToString());
176                 return viewGenResults;
177             }
178
179             List<ForeignConstraint> foreignKeyConstraints = ForeignConstraint.GetForeignConstraints(container);
180
181             CellPartitioner partitioner = new CellPartitioner(cells, foreignKeyConstraints);
182             List<CellGroup> cellGroups = partitioner.GroupRelatedCells();
183             foreach (CellGroup cellGroup in cellGroups)
184             {
185                 ViewGenerator viewGenerator = null;
186                 ErrorLog groupErrorLog = new ErrorLog();
187                 try
188                 {
189                     viewGenerator = new ViewGenerator(cellGroup, config, foreignKeyConstraints, containerMapping);
190                 }
191                 catch (InternalMappingException exception)
192                 {
193                     // All exceptions have mapping errors in them
194                     Debug.Assert(exception.ErrorLog.Count > 0, "Incorrectly created mapping exception");
195                     groupErrorLog = exception.ErrorLog;
196                 }
197
198                 if (groupErrorLog.Count == 0)
199                 {
200                     Debug.Assert(viewGenerator != null);
201                     groupErrorLog = viewGenerator.GenerateAllBidirectionalViews(viewGenResults.Views, identifiers);
202                 }
203
204                 if (groupErrorLog.Count != 0)
205                 {
206                     viewGenResults.AddErrors(groupErrorLog);
207                 }
208             }
209             // We used to print the errors here. Now we trace them as they are being thrown
210             //if (viewGenResults.HasErrors && config.IsViewTracing) {
211             //    Helpers.StringTraceLine(viewGenResults.ErrorsToString());
212             //}
213             return viewGenResults;
214         }
215
216
217
218         // effects: Given a container, ensures that all entity/association
219         // sets in container on the C-side have been mapped
220         private static ErrorLog EnsureAllCSpaceContainerSetsAreMapped(IEnumerable<Cell> cells,
221                                                                       ConfigViewGenerator config,
222                                                                       StorageEntityContainerMapping containerMapping)
223         {
224
225             Set<EntitySetBase> mappedExtents = new Set<EntitySetBase>();
226             string mslFileLocation = null;
227             EntityContainer container = null;
228             // Determine the container and name of the file while determining
229             // the set of mapped extents in the cells
230             foreach (Cell cell in cells)
231             {
232                 mappedExtents.Add(cell.CQuery.Extent);
233                 mslFileLocation = cell.CellLabel.SourceLocation;
234                 // All cells are from the same container
235                 container = cell.CQuery.Extent.EntityContainer;
236             }
237             Debug.Assert(container != null);
238
239             List<EntitySetBase> missingExtents = new List<EntitySetBase>();
240             // Go through all the extents in the container and determine
241             // extents that are missing
242             foreach (EntitySetBase extent in container.BaseEntitySets)
243             {
244                 if (mappedExtents.Contains(extent) == false
245                     && !(containerMapping.HasQueryViewForSetMap(extent.Name)))
246                 {
247                     AssociationSet associationSet = extent as AssociationSet;
248                     if (associationSet==null || !associationSet.ElementType.IsForeignKey)
249                     {
250                         missingExtents.Add(extent);
251                     }
252                 }
253             }
254             ErrorLog errorLog = new ErrorLog();
255             // If any extent is not mapped, add an error
256             if (missingExtents.Count > 0)
257             {
258                 StringBuilder extentBuilder = new StringBuilder();
259                 bool isFirst = true;
260                 foreach (EntitySetBase extent in missingExtents)
261                 {
262                     if (isFirst == false)
263                     {
264                         extentBuilder.Append(", ");
265                     }
266                     isFirst = false;
267                     extentBuilder.Append(extent.Name);
268                 }
269                 string message = System.Data.Entity.Strings.ViewGen_Missing_Set_Mapping(extentBuilder);
270                 // Find the cell with smallest line number - so that we can
271                 // point to the beginning of the file
272                 int lowestLineNum = -1;
273                 Cell smallestCell = null;
274                 foreach (Cell cell in cells)
275                 {
276                     if (lowestLineNum == -1 || cell.CellLabel.StartLineNumber < lowestLineNum)
277                     {
278                         smallestCell = cell;
279                         lowestLineNum = cell.CellLabel.StartLineNumber;
280                     }
281                 }
282                 Debug.Assert(smallestCell != null && lowestLineNum >= 0);
283                 EdmSchemaError edmSchemaError = new EdmSchemaError(message, (int)ViewGenErrorCode.MissingExtentMapping,
284                     EdmSchemaErrorSeverity.Error, containerMapping.SourceLocation, containerMapping.StartLineNumber,
285                     containerMapping.StartLinePosition, null);
286                 ErrorLog.Record record = new ErrorLog.Record(edmSchemaError);
287                 errorLog.AddEntry(record);
288             }
289             return errorLog;
290         }
291
292
293
294
295
296
297
298         #region Static Helpers
299         private static bool DoesCellGroupContainEntitySet(CellGroup group, EntitySetBase entity)
300         {
301             foreach (Cell cell in group)
302             {
303                 if (cell.GetLeftQuery(ViewTarget.QueryView).Extent.Equals(entity))
304                 {
305                     return true;
306                 }
307             }
308
309             return false;
310         }
311         #endregion
312
313         internal override void ToCompactString(StringBuilder builder)
314         {
315
316         }
317
318
319     }
320
321 }