1 //---------------------------------------------------------------------
2 // <copyright file="CellQuery.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
8 //---------------------------------------------------------------------
10 using System.Collections.Generic;
11 using System.Collections.ObjectModel;
12 using System.Data.Common.Utils;
13 using System.Data.Mapping.ViewGeneration.CqlGeneration;
14 using System.Data.Mapping.ViewGeneration.Utils;
15 using System.Data.Mapping.ViewGeneration.Validation;
16 using System.Data.Metadata.Edm;
17 using System.Diagnostics;
21 namespace System.Data.Mapping.ViewGeneration.Structures
23 using System.Data.Entity;
24 using AttributeSet = Set<MemberPath>;
27 /// This class stores the C or S query. For example,
28 /// (C) SELECT (p type Person) AS D1, p.pid, p.name FROM p in P WHERE D1
29 /// (S) SELECT True AS D1, pid, name FROM SPerson WHERE D1
31 /// The cell query is stored in a "factored" manner for ease of
32 /// cell-merging and cell manipulation. It contains:
33 /// * Projection: A sequence of slots and a sequence of boolean slots (one
34 /// for each cell in the extent)
35 /// * A From part represented as a Join tree
38 internal class CellQuery : InternalBase
42 /// Whether query has a 'SELECT DISTINCT' on top.
44 internal enum SelectDistinct
50 // The boolean expressions that essentially capture the type information
51 // Fixed-size list; NULL in the list means 'unused'
52 private List<BoolExpression> m_boolExprs;
53 // The fields including the key fields
54 // May contain NULLs - means 'not in the projection'
55 private ProjectedSlot[] m_projectedSlots;
56 // where clause: An expression formed using the boolExprs
57 private BoolExpression m_whereClause;
58 private BoolExpression m_originalWhereClause; // m_originalWhereClause is not changed
60 private SelectDistinct m_selectDistinct;
61 // The from part of the query
62 private MemberPath m_extentMemberPath;
63 // The basic cell relation for all slots in this
64 private BasicCellRelation m_basicCellRelation;
68 // effects: Creates a cell query with the given projection (slots),
69 // from part (joinTreeRoot) and the predicate (whereClause)
70 // Used for cell creation
71 internal CellQuery(List<ProjectedSlot> slots, BoolExpression whereClause, MemberPath rootMember, SelectDistinct eliminateDuplicates)
72 : this(slots.ToArray(), whereClause, new List<BoolExpression>(), eliminateDuplicates, rootMember)
78 // effects: Given all the fields, just sets them.
79 internal CellQuery(ProjectedSlot[] projectedSlots,
80 BoolExpression whereClause,
81 List<BoolExpression> boolExprs,
82 SelectDistinct elimDupl, MemberPath rootMember)
85 m_boolExprs = boolExprs;
86 m_projectedSlots = projectedSlots;
87 m_whereClause = whereClause;
88 m_originalWhereClause = whereClause;
89 m_selectDistinct = elimDupl;
90 m_extentMemberPath = rootMember;
96 internal CellQuery(CellQuery source)
98 this.m_basicCellRelation = source.m_basicCellRelation;
99 this.m_boolExprs = source.m_boolExprs;
100 this.m_selectDistinct = source.m_selectDistinct;
101 this.m_extentMemberPath = source.m_extentMemberPath;
102 this.m_originalWhereClause = source.m_originalWhereClause;
103 this.m_projectedSlots = source.m_projectedSlots;
104 this.m_whereClause = source.m_whereClause;
107 // effects: Given an existing cellquery, makes a new one based on it
108 // but uses the slots as specified with newSlots
109 private CellQuery(CellQuery existing, ProjectedSlot[] newSlots) :
110 this(newSlots, existing.m_whereClause, existing.m_boolExprs,
111 existing.m_selectDistinct, existing.m_extentMemberPath)
118 internal SelectDistinct SelectDistinctFlag
120 get { return m_selectDistinct; }
123 // effects: Returns the top levelextent corresponding to this cell query
124 internal EntitySetBase Extent
128 EntitySetBase extent = m_extentMemberPath.Extent as EntitySetBase;
129 Debug.Assert(extent != null, "JoinTreeRoot in cellquery must be an extent");
134 // effects: Returns the number of slots projected in the query
135 internal int NumProjectedSlots
137 get { return m_projectedSlots.Length; }
140 internal ProjectedSlot[] ProjectedSlots
142 get { return m_projectedSlots; }
145 internal List<BoolExpression> BoolVars
147 get { return m_boolExprs; }
150 // effects: Returns the number of boolean expressions projected in the query
151 internal int NumBoolVars
153 get { return m_boolExprs.Count; }
156 internal BoolExpression WhereClause
158 get { return m_whereClause; }
161 // effects: Returns the root of the join tree
162 internal MemberPath SourceExtentMemberPath
164 get { return m_extentMemberPath; }
167 // effects: Returns the relation that contains all the slots present
168 // in this cell query
169 internal BasicCellRelation BasicCellRelation
173 Debug.Assert(m_basicCellRelation != null, "BasicCellRelation must be created first");
174 return m_basicCellRelation;
180 /// After cell merging boolean expression can (most likely) have disjunctions (OR node)
181 /// to represent the condition that a tuple came from either of the merged cells.
182 /// In this case original where clause IS MERGED CLAUSE with OR!!!
183 /// So don't call this after merging. It'll throw or debug assert from within GetConjunctsFromWC()
185 internal IEnumerable<MemberRestriction> Conditions
187 get { return GetConjunctsFromOriginalWhereClause(); }
192 #region ProjectedSlots related methods
193 // effects: Returns the slotnum projected slot
194 internal ProjectedSlot ProjectedSlotAt(int slotNum)
196 Debug.Assert(slotNum < m_projectedSlots.Length, "Slot number too high");
197 return m_projectedSlots[slotNum];
200 // requires: All slots in this are join tree slots
201 // This method is called for an S-side query
202 // cQuery is the corresponding C-side query in the cell
203 // sourceCell is the original cell for "this" and cQuery
204 // effects: Checks if any of the columns in "this" are mapped to multiple properties in cQuery. If so,
205 // returns an error record about the duplicated slots
206 internal ErrorLog.Record CheckForDuplicateFields(CellQuery cQuery, Cell sourceCell)
208 // slotMap stores the slots on the S-side and the
209 // C-side properties that it maps to
210 KeyToListMap<MemberProjectedSlot, int> slotMap = new KeyToListMap<MemberProjectedSlot, int>(ProjectedSlot.EqualityComparer);
212 // Note that this does work for self-association. In the manager
213 // employee example, ManagerId and EmployeeId from the SEmployee
214 // table map to the two ends -- Manager.ManagerId and
215 // Employee.EmployeeId in the C Space
217 for (int i = 0; i < m_projectedSlots.Length; i++)
219 ProjectedSlot projectedSlot = m_projectedSlots[i];
220 MemberProjectedSlot slot = projectedSlot as MemberProjectedSlot;
221 Debug.Assert(slot != null, "All slots for this method must be JoinTreeSlots");
222 slotMap.Add(slot, i);
225 StringBuilder builder = null;
227 // Now determine the entries that have more than one integer per slot
228 bool isErrorSituation = false;
230 foreach (MemberProjectedSlot slot in slotMap.Keys)
232 ReadOnlyCollection<int> indexes = slotMap.ListForKey(slot);
233 Debug.Assert(indexes.Count >= 1, "Each slot must have one index at least");
235 if (indexes.Count > 1 &&
236 cQuery.AreSlotsEquivalentViaRefConstraints(indexes) == false)
238 // The column is mapped to more than one property and it
239 // failed the "association corresponds to referential
240 // constraints" check
242 isErrorSituation = true;
245 builder = new StringBuilder(System.Data.Entity.Strings.ViewGen_Duplicate_CProperties(Extent.Name));
246 builder.AppendLine();
248 StringBuilder tmpBuilder = new StringBuilder();
249 for (int i = 0; i < indexes.Count; i++)
251 int index = indexes[i];
254 tmpBuilder.Append(", ");
256 // The slot must be a JoinTreeSlot. If it isn't it is an internal error
257 MemberProjectedSlot cSlot = (MemberProjectedSlot)cQuery.m_projectedSlots[index];
258 tmpBuilder.Append(cSlot.ToUserString());
260 builder.AppendLine(Strings.ViewGen_Duplicate_CProperties_IsMapped(slot.ToUserString(), tmpBuilder.ToString()));
264 if (false == isErrorSituation)
269 ErrorLog.Record record = new ErrorLog.Record(true, ViewGenErrorCode.DuplicateCPropertiesMapped, builder.ToString(), sourceCell, String.Empty);
273 // requires: "this" is a query on the C-side
274 // and cSideSlotIndexes corresponds to the indexes
275 // (into "this") that the slot is being mapped into
276 // cSideSlotIndexes.Count > 1 - that is, a particular column in "this"'s corresponding S-Query
277 // has been mapped to more than one property in "this"
279 // effects: Checks that the multiple mappings on the C-side are
280 // backed by an appropriate Referential constraint
281 // If a column is mapped to two properties <A, B> in a single cell:
282 // (a) Must be an association
283 // (b) The two properties must be on opposite ends of the association
284 // (c) The association must have a RI constraint
285 // (d) Ordinal[A] == Ordinal[B] in the RI constraint
286 // (c) and (d) can be stated as - the slots are equivalent, i.e.,
287 // kept equal via an RI constraint
288 private bool AreSlotsEquivalentViaRefConstraints(ReadOnlyCollection<int> cSideSlotIndexes)
291 // Check (a): Must be an association
292 AssociationSet assocSet = Extent as AssociationSet;
293 if (assocSet == null)
298 // Check (b): The two properties must be on opposite ends of the association
299 // There better be exactly two properties!
300 Debug.Assert(cSideSlotIndexes.Count > 1, "Method called when no duplicate mapping");
301 if (cSideSlotIndexes.Count > 2)
306 // They better be join tree slots (if they are mapped!) and map to opposite ends
307 MemberProjectedSlot slot0 = (MemberProjectedSlot)m_projectedSlots[cSideSlotIndexes[0]];
308 MemberProjectedSlot slot1 = (MemberProjectedSlot)m_projectedSlots[cSideSlotIndexes[1]];
310 return slot0.MemberPath.IsEquivalentViaRefConstraint(slot1.MemberPath);
313 // requires: The Where clause satisfies the same requirements a GetConjunctsFromWhereClause
314 // effects: For each slot that has a NotNull condition in the where
315 // clause, checks if it is projected. If all such slots are
316 // projected, returns null. Else returns an error record
317 internal ErrorLog.Record CheckForProjectedNotNullSlots(Cell sourceCell, IEnumerable<Cell> associationSets)
319 StringBuilder builder = new StringBuilder();
320 bool foundError = false;
322 foreach (MemberRestriction restriction in Conditions)
324 if (restriction.Domain.ContainsNotNull())
326 MemberProjectedSlot slot = MemberProjectedSlot.GetSlotForMember(m_projectedSlots, restriction.RestrictedMemberSlot.MemberPath);
327 if (slot == null) //member with not null condition is not mapped in this extent
329 bool missingMapping = true;
330 if(Extent is EntitySet)
332 bool isCQuery = sourceCell.CQuery == this;
333 ViewTarget target = isCQuery ? ViewTarget.QueryView : ViewTarget.UpdateView;
334 CellQuery rightCellQuery = isCQuery? sourceCell.SQuery : sourceCell.CQuery;
336 //Find out if there is an association mapping but only if the current Not Null condition is on an EntitySet
337 EntitySet rightExtent = rightCellQuery.Extent as EntitySet;
338 if (rightExtent != null)
340 List<AssociationSet> associations = MetadataHelper.GetAssociationsForEntitySet(rightCellQuery.Extent as EntitySet);
341 foreach (var association in associations.Where(association => association.AssociationSetEnds.Any(end => ( end.CorrespondingAssociationEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One &&
342 (MetadataHelper.GetOppositeEnd(end).EntitySet.EdmEquals(rightExtent))))))
344 foreach (var associationCell in associationSets.Where(c => c.GetRightQuery(target).Extent.EdmEquals(association)))
346 if (MemberProjectedSlot.GetSlotForMember(associationCell.GetLeftQuery(target).ProjectedSlots, restriction.RestrictedMemberSlot.MemberPath) != null)
348 missingMapping = false;
357 // condition of NotNull and slot not being projected
358 builder.AppendLine(System.Data.Entity.Strings.ViewGen_NotNull_No_Projected_Slot(
359 restriction.RestrictedMemberSlot.MemberPath.PathToString(false)));
365 if (false == foundError)
369 ErrorLog.Record record = new ErrorLog.Record(true, ViewGenErrorCode.NotNullNoProjectedSlot, builder.ToString(), sourceCell, String.Empty);
373 internal void FixMissingSlotAsDefaultConstant(int slotNumber, ConstantProjectedSlot slot)
375 Debug.Assert(m_projectedSlots[slotNumber] == null, "Another attempt to plug in a default value");
376 m_projectedSlots[slotNumber] = slot;
379 // requires: projectedSlotMap which contains a mapping of the fields
380 // for "this" to integers
381 // effects: Align the fields of this cell query using the
382 // projectedSlotMap and generates a new query into newMainQuery
383 // Based on the re-aligned fields in this, re-aligns the
384 // corresponding fields in otherQuery as well and modifies
385 // newOtherQuery to contain it
387 // input: Proj[A,B,"5"] = Proj[F,"7",G]
388 // Proj[C,B] = Proj[H,I]
389 // projectedSlotMap: A -> 0, B -> 1, C -> 2
390 // output: Proj[A,B,null] = Proj[F,"7",null]
391 // Proj[null,B,C] = Proj[null,I,H]
392 internal void CreateFieldAlignedCellQueries(CellQuery otherQuery, MemberProjectionIndex projectedSlotMap,
393 out CellQuery newMainQuery, out CellQuery newOtherQuery)
396 // mainSlots and otherSlots hold the new slots for two queries
397 int numAlignedSlots = projectedSlotMap.Count;
398 ProjectedSlot[] mainSlots = new ProjectedSlot[numAlignedSlots];
399 ProjectedSlot[] otherSlots = new ProjectedSlot[numAlignedSlots];
401 // Go through the slots for this query and find the new slot for them
402 for (int i = 0; i < m_projectedSlots.Length; i++)
405 MemberProjectedSlot slot = m_projectedSlots[i] as MemberProjectedSlot;
406 Debug.Assert(slot != null, "All slots during cell normalization must field slots");
407 // Get the the ith slot's variable and then get the
408 // new slot number from the field map
409 int newSlotNum = projectedSlotMap.IndexOf(slot.MemberPath);
410 Debug.Assert(newSlotNum >= 0, "Field projected but not in projectedSlotMap");
411 mainSlots[newSlotNum] = m_projectedSlots[i];
412 otherSlots[newSlotNum] = otherQuery.m_projectedSlots[i];
414 // We ignore constants -- note that this is not the
415 // isHighpriority or discriminator case. An example of this
416 // is when (say) Address does not have zip but USAddress
417 // does. Then the constraint looks like Pi_NULL, A, B(E) =
420 // We don't care about this null in the view generation of
421 // the left side. Note that this could happen in inheritance
422 // or in cases when say the S side has 20 fields but the C
423 // side has only 3 - the other 17 are null or default.
425 // NOTE: We allow such constants only on the C side and not
426 // ont the S side. Otherwise, we can have a situation Pi_A,
427 // B, C(E) = Pi_5, y, z(S) Then someone can set A to 7 and we
428 // will not roundtrip. We check for this in validation
431 // Make the new cell queries with the new slots
432 newMainQuery = new CellQuery(this, mainSlots);
433 newOtherQuery = new CellQuery(otherQuery, otherSlots);
436 // requires: All slots in this are null or non-constants
437 // effects: Returns the non-null slots of this
438 internal AttributeSet GetNonNullSlots()
440 AttributeSet attributes = new AttributeSet(MemberPath.EqualityComparer);
441 foreach (ProjectedSlot projectedSlot in m_projectedSlots)
443 // null means 'unused' slot -- we ignore those
444 if (projectedSlot != null)
446 MemberProjectedSlot projectedVar = projectedSlot as MemberProjectedSlot;
447 Debug.Assert(projectedVar != null, "Projected slot must not be a constant");
448 attributes.Add(projectedVar.MemberPath);
454 // effects: Returns an error record if the keys of the extent/associationSet being mapped are
455 // present in the projected slots of this query. Returns null
456 // otherwise. ownerCell indicates the cell that owns this and
457 // resourceString is a resource used for error messages
458 internal ErrorLog.Record VerifyKeysPresent(Cell ownerCell, Func<object, object, string> formatEntitySetMessage,
459 Func<object, object, object, string> formatAssociationSetMessage, ViewGenErrorCode errorCode)
461 List<MemberPath> prefixes = new List<MemberPath>(1);
462 // Keep track of the key corresponding to each prefix
463 List<ExtentKey> keys = new List<ExtentKey>(1);
465 if (Extent is EntitySet)
467 // For entity set just get the full path of the key properties
468 MemberPath prefix = new MemberPath(Extent);
469 prefixes.Add(prefix);
470 EntityType entityType = (EntityType)Extent.ElementType;
471 List<ExtentKey> entitySetKeys = ExtentKey.GetKeysForEntityType(prefix, entityType);
472 Debug.Assert(entitySetKeys.Count == 1, "Currently, we only support primary keys");
473 keys.Add(entitySetKeys[0]);
478 AssociationSet relationshipSet = (AssociationSet)Extent;
479 // For association set, get the full path of the key
480 // properties of each end
482 foreach (AssociationSetEnd relationEnd in relationshipSet.AssociationSetEnds)
484 AssociationEndMember assocEndMember = relationEnd.CorrespondingAssociationEndMember;
485 MemberPath prefix = new MemberPath(relationshipSet, assocEndMember);
486 prefixes.Add(prefix);
487 List<ExtentKey> endKeys = ExtentKey.GetKeysForEntityType(prefix,
488 MetadataHelper.GetEntityTypeForEnd(assocEndMember));
489 Debug.Assert(endKeys.Count == 1, "Currently, we only support primary keys");
490 keys.Add(endKeys[0]);
494 for (int i = 0; i < prefixes.Count; i++)
496 MemberPath prefix = prefixes[i];
497 // Get all or none key slots that are being projected in this cell query
498 List<MemberProjectedSlot> keySlots = MemberProjectedSlot.GetKeySlots(GetMemberProjectedSlots(), prefix);
499 if (keySlots == null)
501 ExtentKey key = keys[i];
503 if (Extent is EntitySet)
505 string keyPropertiesString = MemberPath.PropertiesToUserString(key.KeyFields, true);
506 message = formatEntitySetMessage(keyPropertiesString, Extent.Name);
510 string endName = prefix.RootEdmMember.Name;
511 string keyPropertiesString = MemberPath.PropertiesToUserString(key.KeyFields, false);
512 message = formatAssociationSetMessage(keyPropertiesString, endName, Extent.Name);
514 ErrorLog.Record error = new ErrorLog.Record(true, errorCode, message, ownerCell, String.Empty);
521 internal IEnumerable<MemberPath> GetProjectedMembers()
523 foreach (MemberProjectedSlot slot in this.GetMemberProjectedSlots())
525 yield return slot.MemberPath;
529 // effects: Returns the fields in this, i.e., not constants or null slots
530 private IEnumerable<MemberProjectedSlot> GetMemberProjectedSlots()
532 foreach (ProjectedSlot slot in m_projectedSlots)
534 MemberProjectedSlot memberSlot = slot as MemberProjectedSlot;
535 if (memberSlot != null)
537 yield return memberSlot;
542 // effects: Returns the fields that are used in the query (both projected and non-projected)
543 // Output list is a copy, i.e., can be modified by the caller
544 internal List<MemberProjectedSlot> GetAllQuerySlots()
546 HashSet<MemberProjectedSlot> slots = new HashSet<MemberProjectedSlot>(GetMemberProjectedSlots());
547 slots.Add(new MemberProjectedSlot(SourceExtentMemberPath));
548 foreach (var restriction in Conditions)
550 slots.Add(restriction.RestrictedMemberSlot);
552 return new List<MemberProjectedSlot>(slots);
555 // effects: returns the index at which this slot appears in the projection
556 // or -1 if it is not projected
557 internal int GetProjectedPosition(MemberProjectedSlot slot)
559 for (int i = 0; i < m_projectedSlots.Length; i++)
561 if (MemberProjectedSlot.EqualityComparer.Equals(slot, m_projectedSlots[i]))
569 // effects: returns the List of indexes at which this member appears in the projection
570 // or empty list if it is not projected
571 internal List<int> GetProjectedPositions(MemberPath member)
573 List<int> pathIndexes = new List<int>();
574 for (int i = 0; i < m_projectedSlots.Length; i++)
576 MemberProjectedSlot slot = m_projectedSlots[i] as MemberProjectedSlot;
577 if (slot != null && MemberPath.EqualityComparer.Equals(member, slot.MemberPath))
585 // effects: Determines the slot numbers for members in cellQuery
586 // Returns a set of those paths in the same order as paths. If even
587 // one of the path entries is not projected in the cellquery, returns null
588 internal List<int> GetProjectedPositions(IEnumerable<MemberPath> paths)
590 List<int> pathIndexes = new List<int>();
591 foreach (MemberPath member in paths)
593 // Get the index in checkQuery and add to pathIndexes
594 List<int> slotIndexes = GetProjectedPositions(member);
595 Debug.Assert(slotIndexes != null);
596 if (slotIndexes.Count == 0)
597 { // member is not projected
600 Debug.Assert(slotIndexes.Count == 1, "Expecting the path to be projected only once");
601 pathIndexes.Add(slotIndexes[0]);
606 // effects : Return the slot numbers for members in Cell Query that
607 // represent the association end member passed in.
608 internal List<int> GetAssociationEndSlots(AssociationEndMember endMember)
610 List<int> slotIndexes = new List<int>();
611 Debug.Assert(this.Extent is AssociationSet);
612 for (int i = 0; i < m_projectedSlots.Length; i++)
614 MemberProjectedSlot slot = m_projectedSlots[i] as MemberProjectedSlot;
615 if (slot != null && slot.MemberPath.RootEdmMember.Equals(endMember))
623 // effects: Determines the slot numbers for members in cellQuery
624 // Returns a set of those paths in the same order as paths. If even
625 // one of the path entries is not projected in the cellquery, returns null
626 // If a path is projected more than once, than we choose the one from the
627 // slotsToSearchFrom domain.
628 internal List<int> GetProjectedPositions(IEnumerable<MemberPath> paths, List<int> slotsToSearchFrom)
630 List<int> pathIndexes = new List<int>();
631 foreach (MemberPath member in paths)
633 // Get the index in checkQuery and add to pathIndexes
634 List<int> slotIndexes = GetProjectedPositions(member);
635 Debug.Assert(slotIndexes != null);
636 if (slotIndexes.Count == 0)
637 { // member is not projected
641 if (slotIndexes.Count > 1)
643 for (int i = 0; i < slotIndexes.Count; i++)
645 if (slotsToSearchFrom.Contains(slotIndexes[i]))
647 Debug.Assert(slotIndex == -1, "Should be projected only once");
648 slotIndex = slotIndexes[i];
658 slotIndex = slotIndexes[0];
660 pathIndexes.Add(slotIndex);
666 // requires: The CellConstantDomains in the OneOfConsts of the where
667 // clause are partially done
668 // effects: Given the domains of different variables in domainMap,
669 // fixes the whereClause of this such that all the
670 // CellConstantDomains in OneOfConsts are complete
671 internal void UpdateWhereClause(MemberDomainMap domainMap)
673 List<BoolExpression> atoms = new List<BoolExpression>();
674 foreach (BoolExpression atom in WhereClause.Atoms)
676 BoolLiteral literal = atom.AsLiteral;
677 MemberRestriction restriction = literal as MemberRestriction;
678 Debug.Assert(restriction != null, "All bool literals must be OneOfConst at this point");
679 // The oneOfConst needs to be fixed with the new possible values from the domainMap.
680 IEnumerable<Constant> possibleValues = domainMap.GetDomain(restriction.RestrictedMemberSlot.MemberPath);
681 MemberRestriction newOneOf = restriction.CreateCompleteMemberRestriction(possibleValues);
683 // Prevent optimization of single constraint e.g: "300 in (300)"
684 // But we want to optimize type constants e.g: "category in (Category)"
685 // To prevent optimization of bool expressions we add a Sentinel OneOF
687 ScalarRestriction scalarConst = restriction as ScalarRestriction;
689 scalarConst != null &&
690 !scalarConst.Domain.Contains(Constant.Null) &&
691 !scalarConst.Domain.Contains(Constant.NotNull) &&
692 !scalarConst.Domain.Contains(Constant.Undefined);
696 domainMap.AddSentinel(newOneOf.RestrictedMemberSlot.MemberPath);
699 atoms.Add(BoolExpression.CreateLiteral(newOneOf, domainMap));
703 domainMap.RemoveSentinel(newOneOf.RestrictedMemberSlot.MemberPath);
707 // We create a new whereClause that has the memberDomainMap set
710 m_whereClause = BoolExpression.CreateAnd(atoms.ToArray());
715 #region BooleanExprs related Methods
716 // effects: Returns a boolean expression corresponding to the
717 // "varNum" boolean in this.
718 internal BoolExpression GetBoolVar(int varNum)
720 return m_boolExprs[varNum];
723 // effects: Initalizes the booleans of this cell query to be
724 // true. Creates numBoolVars booleans and sets the cellNum boolean to true
725 internal void InitializeBoolExpressions(int numBoolVars, int cellNum)
727 //Debug.Assert(m_boolExprs.Count == 0, "Overwriting existing booleans");
728 m_boolExprs = new List<BoolExpression>(numBoolVars);
729 for (int i = 0; i < numBoolVars; i++)
731 m_boolExprs.Add(null);
733 Debug.Assert(cellNum < numBoolVars, "Trying to set boolean with too high an index");
734 m_boolExprs[cellNum] = BoolExpression.True;
738 #region WhereClause related methods
739 // requires: The current whereClause corresponds to "True", "OneOfConst" or "
740 // "OneOfConst AND ... AND OneOfConst"
741 // effects: Yields all the conjuncts (OneOfConsts) in this (i.e., if the whereClause is
742 // just True, yields nothing
743 internal IEnumerable<MemberRestriction> GetConjunctsFromWhereClause()
745 return GetConjunctsFromWhereClause(m_whereClause);
748 internal IEnumerable<MemberRestriction> GetConjunctsFromOriginalWhereClause()
750 return GetConjunctsFromWhereClause(m_originalWhereClause);
754 private IEnumerable<MemberRestriction> GetConjunctsFromWhereClause(BoolExpression whereClause)
756 foreach (BoolExpression boolExpr in whereClause.Atoms)
762 MemberRestriction result = boolExpr.AsLiteral as MemberRestriction;
763 Debug.Assert(result != null, "Atom must be restriction");
768 // requires: whereClause is of the form specified in GetConjunctsFromWhereClause
769 // effects: Converts the whereclause to a user-readable string
770 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
771 internal void WhereClauseToUserString(StringBuilder builder, MetadataWorkspace workspace)
774 foreach (MemberRestriction restriction in GetConjunctsFromWhereClause())
776 if (isFirst == false)
778 builder.Append(System.Data.Entity.Strings.ViewGen_AND);
780 restriction.ToUserString(false, builder, workspace);
785 #region Full CellQuery methods
786 // effects: Determines all the identifiers used in this and adds them to identifiers
787 internal void GetIdentifiers(CqlIdentifiers identifiers)
789 foreach (ProjectedSlot projectedSlot in m_projectedSlots)
791 MemberProjectedSlot slot = projectedSlot as MemberProjectedSlot;
794 slot.MemberPath.GetIdentifiers(identifiers);
797 m_extentMemberPath.GetIdentifiers(identifiers);
800 internal void CreateBasicCellRelation(ViewCellRelation viewCellRelation)
802 List<MemberProjectedSlot> slots = GetAllQuerySlots();
803 // Create a base cell relation that has all the scalar slots of this
804 m_basicCellRelation = new BasicCellRelation(this, viewCellRelation, slots);
811 #region String Methods
812 // effects: Modifies stringBuilder to contain a string representation
813 // of the cell query in terms of the original cells that are being used
814 internal override void ToCompactString(StringBuilder stringBuilder)
816 // This could be a simplified view where a number of cells
817 // got merged or it could be one of the original booleans. So
818 // determine their numbers using the booleans in m_cellWrapper
819 List<BoolExpression> boolExprs = m_boolExprs;
822 foreach (BoolExpression boolExpr in boolExprs)
824 if (boolExpr != null)
828 stringBuilder.Append(",");
832 stringBuilder.Append("[");
834 StringUtil.FormatStringBuilder(stringBuilder, "C{0}", i);
841 // No booleans, i.e., no compact representation. Use full string to avoid empty output
842 ToFullString(stringBuilder);
846 stringBuilder.Append("]");
850 internal override void ToFullString(StringBuilder builder)
852 builder.Append("SELECT ");
854 if (m_selectDistinct == SelectDistinct.Yes)
856 builder.Append("DISTINCT ");
859 StringUtil.ToSeparatedString(builder, m_projectedSlots, ", ", "_");
861 if (m_boolExprs.Count > 0)
863 builder.Append(", Bool[");
864 StringUtil.ToSeparatedString(builder, m_boolExprs, ", ", "_");
868 builder.Append(" FROM ");
869 m_extentMemberPath.ToFullString(builder);
871 if (false == m_whereClause.IsTrue)
873 builder.Append(" WHERE ");
874 m_whereClause.ToFullString(builder);
878 public override string ToString()
880 return ToFullString();
883 // eSQL representation of cell query
884 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
885 internal string ToESqlString()
887 StringBuilder builder = new StringBuilder();
888 builder.Append("\n\tSELECT ");
890 if (m_selectDistinct == SelectDistinct.Yes)
892 builder.Append("DISTINCT ");
895 foreach (ProjectedSlot ps in m_projectedSlots)
897 MemberProjectedSlot jtn = ps as MemberProjectedSlot;
898 StructuralType st = jtn.MemberPath.LeafEdmMember.DeclaringType;
899 StringBuilder sb = new StringBuilder();
900 jtn.MemberPath.AsEsql(sb, "e");
901 builder.AppendFormat("{0}, ", sb.ToString());
903 //remove the extra-comma after the last slot
904 builder.Remove(builder.Length - 2, 2);
906 builder.Append("\n\tFROM ");
907 EntitySetBase extent = m_extentMemberPath.Extent;
908 CqlWriter.AppendEscapedQualifiedName(builder, extent.EntityContainer.Name, extent.Name);
909 builder.Append(" AS e");
911 if (m_whereClause.IsTrue == false)
913 builder.Append("\n\tWHERE ");
915 StringBuilder qbuilder = new StringBuilder();
916 m_whereClause.AsEsql(qbuilder, "e");
918 builder.Append(qbuilder.ToString());
920 builder.Append("\n ");
922 return builder.ToString();