// effects: Creates a map with all the condition member constants // from extentCells. viewtarget determines whether the view is an // update or query view internal MemberDomainMap(ViewTarget viewTarget, bool isValidationEnabled, IEnumerable <Cell> extentCells, EdmItemCollection edmItemCollection, ConfigViewGenerator config, Dictionary <EntityType, Set <EntityType> > inheritanceGraph) { m_conditionDomainMap = new Dictionary <MemberPath, CellConstantSet>(MemberPath.EqualityComparer); m_edmItemCollection = edmItemCollection; Dictionary <MemberPath, CellConstantSet> domainMap = null; if (viewTarget == ViewTarget.UpdateView) { domainMap = Domain.ComputeConstantDomainSetsForSlotsInUpdateViews(extentCells, m_edmItemCollection); } else { domainMap = Domain.ComputeConstantDomainSetsForSlotsInQueryViews(extentCells, m_edmItemCollection, isValidationEnabled); } foreach (Cell cell in extentCells) { CellQuery cellQuery = cell.GetLeftQuery(viewTarget); // Get the atoms from cellQuery and only keep the ones that // are condition members foreach (MemberRestriction condition in cellQuery.GetConjunctsFromWhereClause()) { // Note: TypeConditions are created using OneOfTypeConst and // scalars are created using OneOfScalarConst MemberPath memberPath = condition.RestrictedMemberSlot.MemberPath; Debug.Assert(condition is ScalarRestriction || condition is TypeRestriction, "Unexpected restriction"); // Take the narrowed domain from domainMap, if any CellConstantSet domainValues; if (!domainMap.TryGetValue(memberPath, out domainValues)) { domainValues = Domain.DeriveDomainFromMemberPath(memberPath, edmItemCollection, isValidationEnabled); } //Don't count conditions that are satisfied through IsNull=false if (!domainValues.Contains(Constant.Null)) { //multiple values of condition represent disjunction in conditions (not currently supported) // if there is any condition constant that is NotNull if (condition.Domain.Values.All(conditionConstant => (conditionConstant.Equals(Constant.NotNull)))) { continue; } //else there is atleast one condition value that is allowed, continue view generation } //------------------------------------------ //| Nullable | IsNull | Test case | //| T | T | T | //| T | F | T | //| F | T | F | //| F | F | T | //------------------------------------------ //IsNull condition on a member that is non nullable is an invalid condition if (domainValues.Count <= 0 || (!domainValues.Contains(Constant.Null) && condition.Domain.Values.Contains(Constant.Null))) { string message = System.Data.Entity.Strings.ViewGen_InvalidCondition(memberPath.PathToString(false)); ErrorLog.Record record = new ErrorLog.Record(true, ViewGenErrorCode.InvalidCondition, message, cell, String.Empty); ExceptionHelpers.ThrowMappingException(record, config); } if (memberPath.IsAlwaysDefined(inheritanceGraph) == false) { domainValues.Add(Constant.Undefined); } AddToDomainMap(memberPath, domainValues); } } // Fill up the domains for the remaining slots as well m_nonConditionDomainMap = new Dictionary <MemberPath, CellConstantSet>(MemberPath.EqualityComparer); foreach (Cell cell in extentCells) { CellQuery cellQuery = cell.GetLeftQuery(viewTarget); // Get the atoms from cellQuery and only keep the ones that // are condition members foreach (MemberProjectedSlot slot in cellQuery.GetAllQuerySlots()) { MemberPath member = slot.MemberPath; if (m_conditionDomainMap.ContainsKey(member) == false && m_nonConditionDomainMap.ContainsKey(member) == false) { CellConstantSet memberSet = Domain.DeriveDomainFromMemberPath(member, m_edmItemCollection, true /* Regardless of validation, leave the domain unbounded because this is not a condition member */); if (member.IsAlwaysDefined(inheritanceGraph) == false) { // nonConditionMember may belong to subclass memberSet.Add(Constant.Undefined); } memberSet = Domain.ExpandNegationsInDomain(memberSet, memberSet); m_nonConditionDomainMap.Add(member, new CellConstantSetInfo(memberSet, slot)); } } } }
internal DbExpression AsCqt(DbExpression row, IEnumerable <Constant> constants, MemberPath outputMember, bool skipIsNotNull) { DbExpression cqt = null; AsCql( // trueLiteral action () => cqt = DbExpressionBuilder.True, // varIsNotNull action () => cqt = outputMember.AsCqt(row).IsNull().Not(), // varNotEqualsTo action (constant) => { DbExpression notEqualsExpr = outputMember.AsCqt(row).NotEqual(constant.AsCqt(row, outputMember)); if (cqt != null) { cqt = cqt.And(notEqualsExpr); } else { cqt = notEqualsExpr; } }, constants, outputMember, skipIsNotNull); return(cqt); }
/// <summary> /// Generates eSQL for the constant expression. /// </summary> /// <param name="outputMember">The member to which this constant is directed</param> internal abstract StringBuilder AsEsql(StringBuilder builder, MemberPath outputMember, string blockAlias);
/// <summary> /// Creates a scalar member restriction with the meaning "<paramref name="member"/> in <paramref name="values"/>". /// </summary> internal ScalarRestriction(MemberPath member, IEnumerable <Constant> values, IEnumerable <Constant> possibleValues) : base(new MemberProjectedSlot(member), values, possibleValues) { }
// effects: Creates a cell query with the given projection (slots), // from part (joinTreeRoot) and the predicate (whereClause) // Used for cell creation internal CellQuery(List <ProjectedSlot> slots, BoolExpression whereClause, MemberPath rootMember, SelectDistinct eliminateDuplicates) : this(slots.ToArray(), whereClause, new List <BoolExpression>(), eliminateDuplicates, rootMember) { }
/// <summary> /// Not supported in this class. /// </summary> internal override StringBuilder AsEsql(StringBuilder builder, MemberPath outputMember, string blockAlias) { Debug.Fail("Should not be called."); return(null); // To keep the compiler happy }
private static DbExpression CaseSlotValueAsCqt(DbExpression row, ProjectedSlot slot, MemberPath outputMember, IEnumerable <WithRelationship> withRelationships) { // We should never have THEN as a BooleanProjectedSlot. Debug.Assert(slot is MemberProjectedSlot || slot is QualifiedSlot || slot is ConstantProjectedSlot, "Case statement THEN can only have constants or members."); DbExpression cqt = slot.AsCqt(row, outputMember); cqt = WithRelationshipsClauseAsCqt(row, cqt, withRelationships, slot); return(cqt); }
private StringBuilder ToStringHelper(StringBuilder builder, string blockAlias, IEnumerable <Constant> constants, MemberPath outputMember, bool skipIsNotNull, bool userString) { bool anyAdded = false; AsCql( // trueLiteral action () => builder.Append("true"), // varIsNotNull action () => { if (userString) { outputMember.ToCompactString(builder, blockAlias); builder.Append(" is not NULL"); } else { outputMember.AsEsql(builder, blockAlias); builder.Append(" IS NOT NULL"); } anyAdded = true; }, // varNotEqualsTo action (constant) => { if (anyAdded) { builder.Append(" AND "); } anyAdded = true; if (userString) { outputMember.ToCompactString(builder, blockAlias); builder.Append(" <>"); constant.ToCompactString(builder); } else { outputMember.AsEsql(builder, blockAlias); builder.Append(" <>"); constant.AsEsql(builder, outputMember, blockAlias); } }, constants, outputMember, skipIsNotNull); return(builder); }
internal override DbExpression AsCqt(DbExpression row, MemberPath outputMember) { return(m_caseStatement.AsCqt(row, m_withRelationships)); }
private static StringBuilder CaseSlotValueAsEsql(StringBuilder builder, ProjectedSlot slot, MemberPath outputMember, string blockAlias, IEnumerable <WithRelationship> withRelationships, int indentLevel) { // We should never have THEN as a BooleanProjectedSlot. Debug.Assert(slot is MemberProjectedSlot || slot is QualifiedSlot || slot is ConstantProjectedSlot, "Case statement THEN can only have constants or members."); slot.AsEsql(builder, outputMember, blockAlias, 1); WithRelationshipsClauseAsEsql(builder, withRelationships, blockAlias, indentLevel, slot); return(builder); }
internal override StringBuilder AsEsql(StringBuilder builder, MemberPath outputMember, string blockAlias, int indentLevel) { m_caseStatement.AsEsql(builder, m_withRelationships, blockAlias, indentLevel); return(builder); }
// effects: Generates a SlotInfo object for a slot of a join node. It // uses the type of the join operation (opType), whether the slot is // required by the parent or not (isRequiredSlot), the children of // this node (children) and the number of the slotNum private SlotInfo GetJoinSlotInfo(CellTreeOpType opType, bool isRequiredSlot, List <CqlBlock> children, int slotNum, CqlIdentifiers identifiers) { if (false == isRequiredSlot) { // The slot will not be used. So we can set the projected slot to be null SlotInfo unrequiredSlotInfo = new SlotInfo(false, false, null, GetMemberPath(slotNum)); return(unrequiredSlotInfo); } // For a required slot, determine the child who is contributing to this value int childDefiningSlot = -1; CaseStatement caseForOuterJoins = null; for (int childNum = 0; childNum < children.Count; childNum++) { CqlBlock child = children[childNum]; if (false == child.IsProjected(slotNum)) { continue; } // For keys, we can pick any child block. So the first // one that we find is fine as well if (IsKeySlot(slotNum)) { childDefiningSlot = childNum; break; } else if (opType == CellTreeOpType.IJ) { // For Inner Joins, most of the time, the entries will be // the same in all the children. However, in some cases, // we will end up with NULL in one child and an actual // value in another -- we should pick up the actual value in that case childDefiningSlot = GetInnerJoinChildForSlot(children, slotNum); break; } else { // For LOJs, we generate a case statement if more than // one child generates the value - until then we do not // create the caseForOuterJoins object if (childDefiningSlot != -1) { // We really need a case statement now // We have the value being generated by another child // We need to fetch the variable from the appropriate child Debug.Assert(false == IsBoolSlot(slotNum), "Boolean slots cannot come from two children"); if (caseForOuterJoins == null) { MemberPath outputMember = GetMemberPath(slotNum); caseForOuterJoins = new CaseStatement(outputMember); // Add the child that we had not added in the first shot AddCaseForOuterJoins(caseForOuterJoins, children[childDefiningSlot], slotNum, identifiers); } AddCaseForOuterJoins(caseForOuterJoins, child, slotNum, identifiers); } childDefiningSlot = childNum; } } MemberPath memberPath = GetMemberPath(slotNum); ProjectedSlot slot = null; // Generate the slot value -- case statement slot, or a qualified slot or null or false. // If case statement slot has nothing, treat it as null/empty. if (caseForOuterJoins != null && (caseForOuterJoins.Clauses.Count > 0 || caseForOuterJoins.ElseValue != null)) { caseForOuterJoins.Simplify(); slot = new CaseStatementProjectedSlot(caseForOuterJoins, null); } else if (childDefiningSlot >= 0) { slot = children[childDefiningSlot].QualifySlotWithBlockAlias(slotNum); } else { // need to produce output slot, but don't have a value // output NULL for fields or False for bools if (IsBoolSlot(slotNum)) { slot = new BooleanProjectedSlot(BoolExpression.False, identifiers, SlotToBoolIndex(slotNum)); } else { slot = new ConstantProjectedSlot(Domain.GetDefaultValueForMemberPath(memberPath, GetLeaves(), ViewgenContext.Config), memberPath); } } // We need to ensure that _from variables are never null since // view generation uses 2-valued boolean logic. // They can become null in outer joins. We compensate for it by // adding AND NOT NULL condition on boolean slots coming from outer joins. bool enforceNotNull = IsBoolSlot(slotNum) && ((opType == CellTreeOpType.LOJ && childDefiningSlot > 0) || opType == CellTreeOpType.FOJ); // We set isProjected to be true since we have come up with some value for it SlotInfo slotInfo = new SlotInfo(true, true, slot, memberPath, enforceNotNull); return(slotInfo); }
// requires: node corresponds to a Union node // effects: Given a union node and the slots required by the parent, // generates a CqlBlock for the subtree rooted at node private CqlBlock UnionToCqlBlock(bool[] requiredSlots, CqlIdentifiers identifiers, ref int blockAliasNum, ref List <WithRelationship> withRelationships) { Debug.Assert(OpType == CellTreeOpType.Union); List <CqlBlock> children = new List <CqlBlock>(); List <Tuple <CqlBlock, SlotInfo> > additionalChildSlots = new List <Tuple <CqlBlock, SlotInfo> >(); int totalSlots = requiredSlots.Length; foreach (CellTreeNode child in Children) { // Unlike Join, we pass the requiredSlots from the parent as the requirement. bool[] childProjectedSlots = child.GetProjectedSlots(); AndWith(childProjectedSlots, requiredSlots); CqlBlock childBlock = child.ToCqlBlock(childProjectedSlots, identifiers, ref blockAliasNum, ref withRelationships); for (int qualifiedSlotNumber = childProjectedSlots.Length; qualifiedSlotNumber < childBlock.Slots.Count; qualifiedSlotNumber++) { additionalChildSlots.Add(Tuple.Create(childBlock, childBlock.Slots[qualifiedSlotNumber])); } // if required, but not projected, add NULL SlotInfo[] paddedSlotInfo = new SlotInfo[childBlock.Slots.Count]; for (int slotNum = 0; slotNum < totalSlots; slotNum++) { if (requiredSlots[slotNum] && !childProjectedSlots[slotNum]) { if (IsBoolSlot(slotNum)) { paddedSlotInfo[slotNum] = new SlotInfo(true /* is required */, true /* is projected */, new BooleanProjectedSlot(BoolExpression.False, identifiers, SlotToBoolIndex(slotNum)), null /* member path*/); } else { // NULL as projected slot MemberPath memberPath = childBlock.MemberPath(slotNum); paddedSlotInfo[slotNum] = new SlotInfo(true /* is required */, true /* is projected */, new ConstantProjectedSlot(Constant.Null, memberPath), memberPath); } } else { paddedSlotInfo[slotNum] = childBlock.Slots[slotNum]; } } childBlock.Slots = new ReadOnlyCollection <SlotInfo>(paddedSlotInfo); children.Add(childBlock); Debug.Assert(totalSlots == child.NumBoolSlots + child.NumProjectedSlots, "Number of required slots is different from what each node in the tree has?"); } // We need to add the slots added by each child uniformly for others (as nulls) since this is a union operation. if (additionalChildSlots.Count != 0) { foreach (CqlBlock childBlock in children) { SlotInfo[] childSlots = new SlotInfo[totalSlots + additionalChildSlots.Count]; childBlock.Slots.CopyTo(childSlots, 0); int index = totalSlots; foreach (var addtionalChildSlotInfo in additionalChildSlots) { var slotInfo = addtionalChildSlotInfo.Item2; if (addtionalChildSlotInfo.Item1.Equals(childBlock)) { childSlots[index] = new SlotInfo(true /* is required */, true /* is projected */, slotInfo.SlotValue, slotInfo.OutputMember); } else { childSlots[index] = new SlotInfo(true /* is required */, true /* is projected */, new ConstantProjectedSlot(Constant.Null, slotInfo.OutputMember), slotInfo.OutputMember); } //move on to the next slot added by children. index++; } childBlock.Slots = new ReadOnlyCollection <SlotInfo>(childSlots); } } // Create the slotInfos and then Union CqlBlock SlotInfo[] slotInfos = new SlotInfo[totalSlots + additionalChildSlots.Count]; // We pick the slot references from the first child, just as convention // In a union, values come from both sides CqlBlock firstChild = children[0]; for (int slotNum = 0; slotNum < totalSlots; slotNum++) { SlotInfo slotInfo = firstChild.Slots[slotNum]; // A required slot is somehow projected by a child in Union, so set isProjected to be the same as isRequired. bool isRequired = requiredSlots[slotNum]; slotInfos[slotNum] = new SlotInfo(isRequired, isRequired, slotInfo.SlotValue, slotInfo.OutputMember); } for (int slotNum = totalSlots; slotNum < totalSlots + additionalChildSlots.Count; slotNum++) { var aslot = firstChild.Slots[slotNum]; slotInfos[slotNum] = new SlotInfo(true, true, aslot.SlotValue, aslot.OutputMember); } CqlBlock block = new UnionCqlBlock(slotInfos, children, identifiers, ++blockAliasNum); return(block); }
internal StringBuilder AsUserString(StringBuilder builder, string blockAlias, IEnumerable <Constant> constants, MemberPath outputMember, bool skipIsNotNull) { return(ToStringHelper(builder, blockAlias, constants, outputMember, skipIsNotNull, true)); }
/// <summary> /// Creates a case statement for the <paramref name="memberPath"/> with no clauses. /// </summary> internal CaseStatement(MemberPath memberPath) { m_memberPath = memberPath; m_clauses = new List <WhenThen>(); }
/// <summary> /// Given a set of positive <paramref name="constants"/> generates a simplified negated constant Cql expression. /// Examples: /// - 7, NOT(7, NULL) means NOT(NULL) /// - 7, 8, NOT(7, 8, 9, 10) means NOT(9, 10) /// </summary> private void AsCql(Action trueLiteral, Action varIsNotNull, Action <Constant> varNotEqualsTo, IEnumerable <Constant> constants, MemberPath outputMember, bool skipIsNotNull) { bool isNullable = outputMember.IsNullable; // Remove all the constants from negated and then print "x <> C1 .. AND x <> C2 .. AND x <> C3 ..." Set <Constant> negatedConstants = new Set <Constant>(this.Elements, Constant.EqualityComparer); foreach (Constant constant in constants) { if (constant.Equals(this)) { continue; } Debug.Assert(negatedConstants.Contains(constant), "Negated constant must contain all positive constants"); negatedConstants.Remove(constant); } if (negatedConstants.Count == 0) { // All constants cancel out - emit True. trueLiteral(); } else { bool hasNull = negatedConstants.Contains(Constant.Null); negatedConstants.Remove(Constant.Null); // We always add IS NOT NULL if the property is nullable (and we cannot skip IS NOT NULL). // Also, if the domain contains NOT NULL, we must add it. if (hasNull || (isNullable && !skipIsNotNull)) { varIsNotNull(); } foreach (Constant constant in negatedConstants) { varNotEqualsTo(constant); } } }
internal bool IsProjectedConditionMember(MemberPath memberPath) { return(m_projectedConditionMembers.Contains(memberPath)); }
// effects: Returns an error record if the keys of the extent/associationSet being mapped are // present in the projected slots of this query. Returns null // otherwise. ownerCell indicates the cell that owns this and // resourceString is a resource used for error messages internal ErrorLog.Record VerifyKeysPresent(Cell ownerCell, Func <object, object, string> formatEntitySetMessage, Func <object, object, object, string> formatAssociationSetMessage, ViewGenErrorCode errorCode) { List <MemberPath> prefixes = new List <MemberPath>(1); // Keep track of the key corresponding to each prefix List <ExtentKey> keys = new List <ExtentKey>(1); if (Extent is EntitySet) { // For entity set just get the full path of the key properties MemberPath prefix = new MemberPath(Extent); prefixes.Add(prefix); EntityType entityType = (EntityType)Extent.ElementType; List <ExtentKey> entitySetKeys = ExtentKey.GetKeysForEntityType(prefix, entityType); Debug.Assert(entitySetKeys.Count == 1, "Currently, we only support primary keys"); keys.Add(entitySetKeys[0]); } else { AssociationSet relationshipSet = (AssociationSet)Extent; // For association set, get the full path of the key // properties of each end foreach (AssociationSetEnd relationEnd in relationshipSet.AssociationSetEnds) { AssociationEndMember assocEndMember = relationEnd.CorrespondingAssociationEndMember; MemberPath prefix = new MemberPath(relationshipSet, assocEndMember); prefixes.Add(prefix); List <ExtentKey> endKeys = ExtentKey.GetKeysForEntityType(prefix, MetadataHelper.GetEntityTypeForEnd(assocEndMember)); Debug.Assert(endKeys.Count == 1, "Currently, we only support primary keys"); keys.Add(endKeys[0]); } } for (int i = 0; i < prefixes.Count; i++) { MemberPath prefix = prefixes[i]; // Get all or none key slots that are being projected in this cell query List <MemberProjectedSlot> keySlots = MemberProjectedSlot.GetKeySlots(GetMemberProjectedSlots(), prefix); if (keySlots == null) { ExtentKey key = keys[i]; string message; if (Extent is EntitySet) { string keyPropertiesString = MemberPath.PropertiesToUserString(key.KeyFields, true); message = formatEntitySetMessage(keyPropertiesString, Extent.Name); } else { string endName = prefix.RootEdmMember.Name; string keyPropertiesString = MemberPath.PropertiesToUserString(key.KeyFields, false); message = formatAssociationSetMessage(keyPropertiesString, endName, Extent.Name); } ErrorLog.Record error = new ErrorLog.Record(true, errorCode, message, ownerCell, String.Empty); return(error); } } return(null); }
internal bool IsConditionMember(MemberPath path) { return(m_conditionDomainMap.ContainsKey(path)); }
/// <summary> /// Given the <paramref name="outputMember"/> in the output extent view, generates a constructor expression for /// <paramref name="outputMember"/>'s type, i.e, an expression of the form "Type(....)" /// If <paramref name="outputMember"/> is an association end then instead of constructing an Entity or Complex type, constructs a reference. /// </summary> private void AsCql(Action <EntitySet, IList <MemberPath> > createRef, Action <IList <MemberPath> > createType, MemberPath outputMember) { EntitySet refScopeEntitySet = outputMember.GetScopeOfRelationEnd(); if (refScopeEntitySet != null) { // Construct a scoped reference: CreateRef(CPerson1Set, NewRow(pid1, pid2), CPerson1) EntityType entityType = refScopeEntitySet.ElementType; List <MemberPath> keyMemberOutputPaths = new List <MemberPath>(entityType.KeyMembers.Select(km => new MemberPath(outputMember, km))); createRef(refScopeEntitySet, keyMemberOutputPaths); } else { // Construct an entity/complex/Association type in the Members order for fields: CPerson(CPerson1_Pid, CPerson1_Name) Debug.Assert(m_edmType is StructuralType, "m_edmType must be a structural type."); List <MemberPath> memberOutputPaths = new List <MemberPath>(); foreach (EdmMember structuralMember in Helper.GetAllStructuralMembers(m_edmType)) { memberOutputPaths.Add(new MemberPath(outputMember, structuralMember)); } createType(memberOutputPaths); } }
/// <summary> /// Removes AllOtherConstant element from the domain set given by MemberPath /// </summary> internal void RemoveSentinel(MemberPath path) { CellConstantSet set = GetDomainInternal(path); set.Remove(Constant.AllOtherConstants); }
/// <summary> /// Not supported in this class. /// </summary> internal override DbExpression AsCqt(DbExpression row, MemberPath outputMember) { Debug.Fail("Should not be called."); return(null); // To keep the compiler happy }
// requires member exist in this // effects: Returns the possible values/domain for that member internal IEnumerable <Constant> GetDomain(MemberPath path) { return(GetDomainInternal(path)); }
/// <summary> /// Generates CQT for the constant expression. /// </summary> /// <param name="row">The input row.</param> /// <param name="outputMember">The member to which this constant is directed</param> internal abstract DbExpression AsCqt(DbExpression row, MemberPath outputMember);
/// <summary> /// Creates a scalar member restriction with the meaning "<paramref name="member"/> = <paramref name="value"/>". /// This constructor is used for creating discriminator type conditions. /// </summary> internal ScalarRestriction(MemberPath member, Constant value) : base(new MemberProjectedSlot(member), value) { Debug.Assert(value is ScalarConstant || value.IsNull() || value.IsNotNull(), "value is expected to be ScalarConstant, NULL, or NOT_NULL."); }