Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Mapping / ViewGeneration / ViewGenerator.cs
blob96d9d83e985df5bd4562f197ea48f2398324b8f3
1 //---------------------------------------------------------------------
2 // <copyright file="ViewGenerator.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //
6 // @owner Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 using System.Data.Common.CommandTrees;
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.Validation;
15 using System.Data.Mapping.ViewGeneration.QueryRewriting;
16 using System.Collections.Generic;
17 using System.Text;
18 using System.Diagnostics;
19 using System.Data.Mapping.ViewGeneration.Utils;
20 using System.Data.Metadata.Edm;
21 using System.Linq;
23 namespace System.Data.Mapping.ViewGeneration
25 using ViewSet = KeyToListMap<EntitySetBase, GeneratedView>;
26 using CellGroup = Set<Cell>;
27 using WrapperBoolExpr = BoolExpr<LeftCellWrapper>;
28 using WrapperTrueExpr = TrueExpr<LeftCellWrapper>;
29 using WrapperFalseExpr = FalseExpr<LeftCellWrapper>;
30 using WrapperNotExpr = NotExpr<LeftCellWrapper>;
31 using WrapperOrExpr = OrExpr<LeftCellWrapper>;
33 // This class is responsible for generating query or update mapping
34 // views from the initial cells.
35 internal class ViewGenerator : InternalBase
37 #region Fields
38 private CellGroup m_cellGroup; // The initial cells from which we produce views
39 private ConfigViewGenerator m_config; // Configuration variables
40 private MemberDomainMap m_queryDomainMap;
41 private MemberDomainMap m_updateDomainMap;
42 private Dictionary<EntitySetBase, QueryRewriter> m_queryRewriterCache;
43 private List<ForeignConstraint> m_foreignKeyConstraints;
44 private StorageEntityContainerMapping m_entityContainerMapping;
45 #endregion
47 #region Internal API - Only Gatekeeper calls it
49 // effects: Creates a ViewGenerator object that is capable of
50 // producing query or update mapping views given the relevant schema
51 // given the "cells"
52 internal ViewGenerator(CellGroup cellGroup, ConfigViewGenerator config,
53 List<ForeignConstraint> foreignKeyConstraints,
54 StorageEntityContainerMapping entityContainerMapping)
57 m_cellGroup = cellGroup;
58 m_config = config;
59 m_queryRewriterCache = new Dictionary<EntitySetBase, QueryRewriter>();
60 m_foreignKeyConstraints = foreignKeyConstraints;
61 m_entityContainerMapping = entityContainerMapping;
63 Dictionary<EntityType, Set<EntityType>> inheritanceGraph = MetadataHelper.BuildUndirectedGraphOfTypes(entityContainerMapping.StorageMappingItemCollection.EdmItemCollection);
64 SetConfiguration(entityContainerMapping);
66 // We fix all the cells at this point
67 m_queryDomainMap = new MemberDomainMap(ViewTarget.QueryView, m_config.IsValidationEnabled, cellGroup, entityContainerMapping.StorageMappingItemCollection.EdmItemCollection, m_config, inheritanceGraph);
68 m_updateDomainMap = new MemberDomainMap(ViewTarget.UpdateView, m_config.IsValidationEnabled, cellGroup, entityContainerMapping.StorageMappingItemCollection.EdmItemCollection, m_config, inheritanceGraph);
70 // We now go and fix the queryDomain map so that it has all the
71 // values from the S-side as well -- this is needed for domain
72 // constraint propagation, i.e., values from the S-side get
73 // propagated to te oneOfConst on the C-side. So we better get
74 // the "possiblveValues" stuff to contain those constants as well
75 MemberDomainMap.PropagateUpdateDomainToQueryDomain(cellGroup, m_queryDomainMap, m_updateDomainMap);
77 UpdateWhereClauseForEachCell(cellGroup, m_queryDomainMap, m_updateDomainMap, m_config);
79 // We need to simplify cell queries, yet we don't want the conditions to disappear
80 // So, add an extra value to the domain, temporarily
81 MemberDomainMap queryOpenDomain = m_queryDomainMap.GetOpenDomain();
82 MemberDomainMap updateOpenDomain = m_updateDomainMap.GetOpenDomain();
84 // Make sure the WHERE clauses of the cells reflect the changes
85 foreach (Cell cell in cellGroup)
87 cell.CQuery.WhereClause.FixDomainMap(queryOpenDomain);
88 cell.SQuery.WhereClause.FixDomainMap(updateOpenDomain);
89 cell.CQuery.WhereClause.ExpensiveSimplify();
90 cell.SQuery.WhereClause.ExpensiveSimplify();
91 cell.CQuery.WhereClause.FixDomainMap(m_queryDomainMap);
92 cell.SQuery.WhereClause.FixDomainMap(m_updateDomainMap);
96 private void SetConfiguration(StorageEntityContainerMapping entityContainerMapping)
98 m_config.IsValidationEnabled = entityContainerMapping.Validate;
99 m_config.GenerateUpdateViews = entityContainerMapping.GenerateUpdateViews;
102 // effects: Generates views for the particular cellgroup in this. Returns an
103 // error log describing the errors that were encountered (if none
104 // were encountered, the ErrorLog.Count is 0). Places the generated
105 // views in result
106 internal ErrorLog GenerateAllBidirectionalViews(ViewSet views, CqlIdentifiers identifiers)
109 // Allow missing attributes for now to make entity splitting run through
110 // we cannot do this for query views in general: need to obtain the exact enumerated domain
112 if (m_config.IsNormalTracing)
114 StringBuilder builder = new StringBuilder();
115 Cell.CellsToBuilder(builder, m_cellGroup);
116 Helpers.StringTraceLine(builder.ToString());
119 m_config.SetTimeForFinishedActivity(PerfType.CellCreation);
120 // Check if the cellgroup is consistent and all known S constraints are
121 // satisified by the known C constraints
122 CellGroupValidator validator = new CellGroupValidator(m_cellGroup, m_config);
123 ErrorLog errorLog = validator.Validate();
125 if (errorLog.Count > 0)
127 errorLog.PrintTrace();
128 return errorLog;
131 m_config.SetTimeForFinishedActivity(PerfType.KeyConstraint);
133 // We generate update views first since they perform the main
134 // validation checks
135 if (m_config.GenerateUpdateViews)
137 errorLog = GenerateDirectionalViews(ViewTarget.UpdateView, identifiers, views);
138 if (errorLog.Count > 0)
140 return errorLog; // If we have discovered errors here, do not generate query views
144 // Make sure that the foreign key constraints are not violated
145 if (m_config.IsValidationEnabled)
147 CheckForeignKeyConstraints(errorLog);
149 m_config.SetTimeForFinishedActivity(PerfType.ForeignConstraint);
151 if (errorLog.Count > 0)
153 errorLog.PrintTrace();
154 return errorLog; // If we have discovered errors here, do not generate query views
157 // Query views - do not allow missing attributes
158 // For the S-side, we add NOT ... for each scalar constant so
159 // that if we have C, P in the mapping but the store has C, P, S,
160 // we can handle it in the query views
161 m_updateDomainMap.ExpandDomainsToIncludeAllPossibleValues();
163 errorLog = GenerateDirectionalViews(ViewTarget.QueryView, identifiers, views);
165 return errorLog;
168 internal ErrorLog GenerateQueryViewForSingleExtent(ViewSet views, CqlIdentifiers identifiers, EntitySetBase entity, EntityTypeBase type, ViewGenMode mode)
170 Debug.Assert(mode != ViewGenMode.GenerateAllViews);
172 if (m_config.IsNormalTracing)
174 StringBuilder builder = new StringBuilder();
175 Cell.CellsToBuilder(builder, m_cellGroup);
176 Helpers.StringTraceLine(builder.ToString());
179 // Check if the cellgroup is consistent and all known S constraints are
180 // satisified by the known C constraints
181 CellGroupValidator validator = new CellGroupValidator(m_cellGroup, m_config);
182 ErrorLog errorLog = validator.Validate();
183 if (errorLog.Count > 0)
185 errorLog.PrintTrace();
186 return errorLog;
189 // Make sure that the foreign key constraints are not violated
190 if (m_config.IsValidationEnabled)
192 CheckForeignKeyConstraints(errorLog);
195 if (errorLog.Count > 0)
197 errorLog.PrintTrace();
198 return errorLog; // If we have discovered errors here, do not generate query views
201 // For the S-side, we add NOT ... for each scalar constant so
202 // that if we have C, P in the mapping but the store has C, P, S,
203 // we can handle it in the query views
204 m_updateDomainMap.ExpandDomainsToIncludeAllPossibleValues();
206 foreach (Cell cell in m_cellGroup)
208 cell.SQuery.WhereClause.FixDomainMap(m_updateDomainMap);
211 errorLog = GenerateQueryViewForExtentAndType(m_entityContainerMapping, identifiers, views, entity, type, mode);
213 return errorLog;
217 #endregion
221 #region Private Methods
223 // effects: Given the extent cells and a map for the domains of all
224 // variables in it, fixes the cell constant domains of the where
225 // clauses in the left queries of cells (left is defined using viewTarget)
226 private static void UpdateWhereClauseForEachCell(IEnumerable<Cell> extentCells, MemberDomainMap queryDomainMap,
227 MemberDomainMap updateDomainMap, ConfigViewGenerator config)
229 foreach (Cell cell in extentCells)
231 cell.CQuery.UpdateWhereClause(queryDomainMap);
232 cell.SQuery.UpdateWhereClause(updateDomainMap);
235 // Fix enumerable domains - currently it is only applicable to boolean type. Note that it is
236 // not applicable to enumerated types since we allow any value of the underlying type of the enum type.
237 queryDomainMap.ReduceEnumerableDomainToEnumeratedValues(ViewTarget.QueryView, config);
238 updateDomainMap.ReduceEnumerableDomainToEnumeratedValues(ViewTarget.UpdateView, config);
242 private ErrorLog GenerateQueryViewForExtentAndType(StorageEntityContainerMapping entityContainerMapping, CqlIdentifiers identifiers, ViewSet views, EntitySetBase entity, EntityTypeBase type, ViewGenMode mode)
244 Debug.Assert(mode != ViewGenMode.GenerateAllViews);
246 // Keep track of the mapping exceptions that we have generated
247 ErrorLog errorLog = new ErrorLog();
249 if (m_config.IsViewTracing)
251 Helpers.StringTraceLine(String.Empty);
252 Helpers.StringTraceLine(String.Empty);
253 Helpers.FormatTraceLine("================= Generating {0} Query View for: {1} ===========================",
254 (mode == ViewGenMode.OfTypeViews) ? "OfType" : "OfTypeOnly",
255 entity.Name);
256 Helpers.StringTraceLine(String.Empty);
257 Helpers.StringTraceLine(String.Empty);
262 // (1) view generation (checks that extents are fully mapped)
263 ViewgenContext context = CreateViewgenContext(entity, ViewTarget.QueryView, identifiers);
264 QueryRewriter queryRewriter = GenerateViewsForExtentAndType(type, context, identifiers, views, mode);
266 catch (InternalMappingException exception)
268 // All exceptions have mapping errors in them
269 Debug.Assert(exception.ErrorLog.Count > 0, "Incorrectly created mapping exception");
270 errorLog.Merge(exception.ErrorLog);
273 return errorLog;
277 // requires: schema refers to C-side or S-side schema for the cells
278 // inside this. if schema.IsQueryView is true, the left side of cells refers
279 // to the C side (and vice-versa for the right side)
280 // effects: Generates the relevant views for the schema side and
281 // returns them. If allowMissingAttributes is true and attributes
282 // are missing on the schema side, substitutes them with NULL
283 // Modifies views to contain the generated views for different
284 // extents specified by cells and the the schemaContext
285 private ErrorLog GenerateDirectionalViews(ViewTarget viewTarget, CqlIdentifiers identifiers, ViewSet views)
287 bool isQueryView = viewTarget == ViewTarget.QueryView;
289 // Partition cells by extent.
290 KeyToListMap<EntitySetBase, Cell> extentCellMap = GroupCellsByExtent(m_cellGroup, viewTarget);
292 // Keep track of the mapping exceptions that we have generated
293 ErrorLog errorLog = new ErrorLog();
295 // Generate views for each extent
296 foreach (EntitySetBase extent in extentCellMap.Keys)
298 if (m_config.IsViewTracing)
300 Helpers.StringTraceLine(String.Empty);
301 Helpers.StringTraceLine(String.Empty);
302 Helpers.FormatTraceLine("================= Generating {0} View for: {1} ===========================",
303 isQueryView ? "Query" : "Update", extent.Name);
304 Helpers.StringTraceLine(String.Empty);
305 Helpers.StringTraceLine(String.Empty);
309 // (1) view generation (checks that extents are fully mapped)
310 QueryRewriter queryRewriter = GenerateDirectionalViewsForExtent(viewTarget, extent, identifiers, views);
312 // (2) validation for update views
313 if (viewTarget == ViewTarget.UpdateView &&
314 m_config.IsValidationEnabled)
316 if (m_config.IsViewTracing)
318 Helpers.StringTraceLine(String.Empty);
319 Helpers.StringTraceLine(String.Empty);
320 Helpers.FormatTraceLine("----------------- Validation for generated update view for: {0} -----------------",
321 extent.Name);
322 Helpers.StringTraceLine(String.Empty);
323 Helpers.StringTraceLine(String.Empty);
326 RewritingValidator validator = new RewritingValidator(queryRewriter.ViewgenContext, queryRewriter.BasicView);
327 validator.Validate();
331 catch (InternalMappingException exception)
333 // All exceptions have mapping errors in them
334 Debug.Assert(exception.ErrorLog.Count > 0,
335 "Incorrectly created mapping exception");
336 errorLog.Merge(exception.ErrorLog);
339 return errorLog;
343 // effects: Generates a view for an extent "extent" that belongs to
344 // schema "schema". extentCells are the cells for this extent.
345 // Adds the view corrsponding to the extent to "views"
346 private QueryRewriter GenerateDirectionalViewsForExtent(ViewTarget viewTarget, EntitySetBase extent, CqlIdentifiers identifiers, ViewSet views)
349 // First normalize the cells in terms of multiconstants, etc
350 // and then generate the view for the extent
351 ViewgenContext context = CreateViewgenContext(extent, viewTarget, identifiers);
352 QueryRewriter queryRewriter = null;
354 if (m_config.GenerateViewsForEachType)
356 // generate views for each OFTYPE(Extent, Type) combination
357 foreach (EdmType type in MetadataHelper.GetTypeAndSubtypesOf(extent.ElementType, m_entityContainerMapping.StorageMappingItemCollection.EdmItemCollection, false /*includeAbstractTypes*/))
359 if (m_config.IsViewTracing && false == type.Equals(extent.ElementType))
361 Helpers.FormatTraceLine("CQL View for {0} and type {1}", extent.Name, type.Name);
363 queryRewriter = GenerateViewsForExtentAndType(type, context, identifiers, views, ViewGenMode.OfTypeViews);
366 else
368 // generate the view for Extent only
369 queryRewriter = GenerateViewsForExtentAndType(extent.ElementType, context, identifiers, views, ViewGenMode.OfTypeViews);
371 if (viewTarget == ViewTarget.QueryView)
373 m_config.SetTimeForFinishedActivity(PerfType.QueryViews);
375 else
377 m_config.SetTimeForFinishedActivity(PerfType.UpdateViews);
380 // cache this rewriter (and context inside it) for future use in FK checking
381 m_queryRewriterCache[extent] = queryRewriter;
382 return queryRewriter;
385 // effects: Returns a context corresponding to extent (if one does not exist, creates one)
386 private ViewgenContext CreateViewgenContext(EntitySetBase extent, ViewTarget viewTarget, CqlIdentifiers identifiers)
388 QueryRewriter queryRewriter;
389 if (!m_queryRewriterCache.TryGetValue(extent, out queryRewriter))
391 // collect the cells that belong to this extent (just a few of them since we segment the mapping first)
392 var cellsForExtent = m_cellGroup.Where(c => c.GetLeftQuery(viewTarget).Extent == extent);
394 return new ViewgenContext(viewTarget, extent, cellsForExtent, identifiers, m_config, m_queryDomainMap, m_updateDomainMap, m_entityContainerMapping);
396 else
398 return queryRewriter.ViewgenContext;
403 private QueryRewriter GenerateViewsForExtentAndType(EdmType generatedType, ViewgenContext context, CqlIdentifiers identifiers, ViewSet views, ViewGenMode mode)
406 Debug.Assert(mode != ViewGenMode.GenerateAllViews, "By definition this method can not handle generating views for all extents");
408 QueryRewriter queryRewriter = new QueryRewriter(generatedType, context, mode);
409 queryRewriter.GenerateViewComponents();
411 // Get the basic view
412 CellTreeNode basicView = queryRewriter.BasicView;
414 if (m_config.IsNormalTracing)
416 Helpers.StringTrace("Basic View: ");
417 Helpers.StringTraceLine(basicView.ToString());
420 CellTreeNode simplifiedView = GenerateSimplifiedView(basicView, queryRewriter.UsedCells);
422 if (m_config.IsNormalTracing)
424 Helpers.StringTraceLine(String.Empty);
425 Helpers.StringTrace("Simplified View: ");
426 Helpers.StringTraceLine(simplifiedView.ToString());
429 CqlGenerator cqlGen = new CqlGenerator(simplifiedView,
430 queryRewriter.CaseStatements,
431 identifiers,
432 context.MemberMaps.ProjectedSlotMap,
433 queryRewriter.UsedCells.Count,
434 queryRewriter.TopLevelWhereClause,
435 m_entityContainerMapping.StorageMappingItemCollection);
437 string eSQLView ;
438 DbQueryCommandTree commandTree;
439 if (m_config.GenerateEsql)
441 eSQLView = cqlGen.GenerateEsql();
442 commandTree = null;
444 else
446 eSQLView = null;
447 commandTree = cqlGen.GenerateCqt();
450 GeneratedView generatedView = GeneratedView.CreateGeneratedView(context.Extent, generatedType, commandTree, eSQLView, m_entityContainerMapping.StorageMappingItemCollection, m_config);
451 views.Add(context.Extent, generatedView);
453 return queryRewriter;
456 private CellTreeNode GenerateSimplifiedView(CellTreeNode basicView, List<LeftCellWrapper> usedCells)
458 Debug.Assert(false == basicView.IsEmptyRightFragmentQuery, "Basic view is empty?");
460 // create 'joined' variables, one for each cell
461 // We know (say) that out of the 10 cells that we were given, only 7 (say) were
462 // needed to construct the view for this extent.
463 int numBoolVars = usedCells.Count;
464 // We need the boolean expressions in Simplify. Precisely ont boolean expression is set to
465 // true in each cell query
467 for (int i = 0; i < numBoolVars; i++)
469 // In the ith cell, set its boolean to be true (i.e., ith boolean)
470 usedCells[i].RightCellQuery.InitializeBoolExpressions(numBoolVars, i);
473 CellTreeNode simplifiedView = CellTreeSimplifier.MergeNodes(basicView);
474 return simplifiedView;
477 private void CheckForeignKeyConstraints(ErrorLog errorLog)
479 foreach (ForeignConstraint constraint in m_foreignKeyConstraints)
481 QueryRewriter childRewriter = null;
482 QueryRewriter parentRewriter = null;
483 m_queryRewriterCache.TryGetValue(constraint.ChildTable, out childRewriter);
484 m_queryRewriterCache.TryGetValue(constraint.ParentTable, out parentRewriter);
485 constraint.CheckConstraint(m_cellGroup, childRewriter, parentRewriter, errorLog, m_config);
489 // effects: Given all the cells for a container, groups the cells by
490 // the left query's extent and returns a dictionary for it
491 private static KeyToListMap<EntitySetBase, Cell> GroupCellsByExtent(IEnumerable<Cell> cells, ViewTarget viewTarget)
494 // Partition cells by extent -- extent is the top node in
495 // the tree. Even for compositions for now? CHANGE_Microsoft_FEATURE_COMPOSITION
496 KeyToListMap<EntitySetBase, Cell> extentCellMap =
497 new KeyToListMap<EntitySetBase, Cell>(EqualityComparer<EntitySetBase>.Default);
498 foreach (Cell cell in cells)
500 // Get the cell query and determine its extent
501 CellQuery cellQuery = cell.GetLeftQuery(viewTarget);
502 extentCellMap.Add(cellQuery.Extent, cell);
504 return extentCellMap;
507 #endregion
509 #region String Methods
510 internal override void ToCompactString(StringBuilder builder)
512 Cell.CellsToBuilder(builder, m_cellGroup);
514 #endregion