/// <summary>
 /// Recursively qualifies all <see cref="ProjectedSlot"/>s and returns a new deeply qualified <see cref="CaseStatement"/>.
 /// </summary>
 internal CaseStatement DeepQualify(CqlBlock block)
 {
     // Go through the whenthens and else and make a new case statement with qualified slots as needed.
     var result = new CaseStatement(m_memberPath);
     foreach (var whenThen in m_clauses)
     {
         var newClause = whenThen.ReplaceWithQualifiedSlot(block);
         result.m_clauses.Add(newClause);
     }
     if (m_elseValue != null)
     {
         result.m_elseValue = m_elseValue.DeepQualify(block);
     }
     result.m_simplified = m_simplified;
     return result;
 }
Example #2
0
        /// <summary>
        /// Given the slot (<paramref name="foundSlot"/>) and its corresponding case statement (<paramref name="thisCaseStatement"/>),
        /// generates the slotinfos for the cql block producing the case statement.
        /// </summary>
        private SlotInfo[] CreateSlotInfosForCaseStatement(bool[] parentRequiredSlots, 
                                                           int foundSlot,
                                                           CqlBlock childBlock, 
                                                           CaseStatement thisCaseStatement,
                                                           IEnumerable<WithRelationship> withRelationships)
        {
            int numSlotsAddedByChildBlock = childBlock.Slots.Count - TotalSlots;
            SlotInfo[] slotInfos = new SlotInfo[TotalSlots + numSlotsAddedByChildBlock];
            for (int slotNum = 0; slotNum < TotalSlots; slotNum++)
            {
                bool isProjected = childBlock.IsProjected(slotNum);
                bool isRequiredByParent = parentRequiredSlots[slotNum];
                ProjectedSlot slot = childBlock.SlotValue(slotNum);
                MemberPath outputMember = GetOutputMemberPath(slotNum);
                if (slotNum == foundSlot)
                {
                    // We need a case statement instead for this slot that we
                    // are handling right now
                    Debug.Assert(isRequiredByParent, "Case result not needed by parent");

                    // Get a case statement with all slots replaced by aliases slots
                    CaseStatement newCaseStatement = thisCaseStatement.DeepQualify(childBlock);
                    slot = new CaseStatementProjectedSlot(newCaseStatement, withRelationships);
                    isProjected = true; // We are projecting this slot now
                }
                else if (isProjected && isRequiredByParent)
                {
                    // We only alias something that is needed and is being projected by the child.
                    // It is a qualified slot into the child block.
                    slot = childBlock.QualifySlotWithBlockAlias(slotNum);
                }
                // For slots, if it is not required by the parent, we want to
                // set the isRequiredByParent for this slot to be
                // false. Furthermore, we do not want to introduce any "NULL
                // AS something" at this stage for slots not being
                // projected. So if the child does not project that slot, we
                // declare it as not being required by the parent (if such a
                // NULL was needed, it would have been pushed all the way
                // down to a non-case block.
                // Essentially, from a Case statement's parent perspective,
                // it is saying "If you can produce a slot either by yourself
                // or your children, please do. Otherwise, do not concoct anything"
                SlotInfo slotInfo = new SlotInfo(isRequiredByParent && isProjected, isProjected, slot, outputMember);
                slotInfos[slotNum] = slotInfo;
            }
            for (int i = TotalSlots; i < TotalSlots + numSlotsAddedByChildBlock; i++)
            {
                QualifiedSlot childAddedSlot = childBlock.QualifySlotWithBlockAlias(i);
                slotInfos[i] = new SlotInfo(true, true, childAddedSlot, childBlock.MemberPath(i));
            }
            return slotInfos;
        }
 // for backward compatibility: add (WHEN True THEN Type) for non-scalar types
 internal void AddTrivialCaseStatementsForConditionMembers()
 {
     for (var memberNum = 0; memberNum < _context.MemberMaps.ProjectedSlotMap.Count; memberNum++)
     {
         var memberPath = _context.MemberMaps.ProjectedSlotMap[memberNum];
         if (!memberPath.IsScalarType()
             && !_caseStatements.ContainsKey(memberPath))
         {
             Constant typeConstant = new TypeConstant(memberPath.EdmType);
             {
                 var caseStmt = new CaseStatement(memberPath);
                 caseStmt.AddWhenThen(BoolExpression.True, new ConstantProjectedSlot(typeConstant));
                 _caseStatements[memberPath] = caseStmt;
             }
         }
     }
 }
        // Modifies _caseStatements and _topLevelWhereClause
        private List<LeftCellWrapper> RemapFromVariables()
        {
            var usedCells = new List<LeftCellWrapper>();
            // remap CellIdBooleans appearing in WHEN clauses and in topLevelWhereClause so the first used cell = 0, second = 1, etc.
            // This ordering is exploited in CQL generation
            var newNumber = 0;
            var literalRemap = new Dictionary<BoolLiteral, BoolLiteral>(BoolLiteral.EqualityIdentifierComparer);
            foreach (var leftCellWrapper in _context.AllWrappersForExtent)
            {
                if (_usedViews.Contains(leftCellWrapper.FragmentQuery))
                {
                    usedCells.Add(leftCellWrapper);
                    var oldNumber = leftCellWrapper.OnlyInputCell.CellNumber;
                    if (newNumber != oldNumber)
                    {
                        literalRemap[new CellIdBoolean(_identifiers, oldNumber)] = new CellIdBoolean(_identifiers, newNumber);
                    }
                    newNumber++;
                }
            }

            if (literalRemap.Count > 0)
            {
                // Remap _from literals in WHERE clause
                _topLevelWhereClause = _topLevelWhereClause.RemapLiterals(literalRemap);

                // Remap _from literals in case statements
                var newCaseStatements = new Dictionary<MemberPath, CaseStatement>();
                foreach (var entry in _caseStatements)
                {
                    var newCaseStatement = new CaseStatement(entry.Key);
                    Debug.Assert(entry.Value.ElseValue == null);
                    foreach (var clause in entry.Value.Clauses)
                    {
                        newCaseStatement.AddWhenThen(clause.Condition.RemapLiterals(literalRemap), clause.Value);
                    }
                    newCaseStatements[entry.Key] = newCaseStatement;
                }
                _caseStatements = newCaseStatements;
            }
            return usedCells;
        }
        private void AddElseDefaultToCaseStatement(
            MemberPath currentPath, CaseStatement caseStatement, List<Constant> domain,
            CellTreeNode rightDomainQuery, Tile<FragmentQuery> unionCaseRewriting)
        {
            Debug.Assert(_context.ViewTarget == ViewTarget.UpdateView, "Used for update views only");

            Constant defaultValue;
            var hasDefaultValue = Domain.TryGetDefaultValueForMemberPath(currentPath, out defaultValue);

            if (false == hasDefaultValue
                || false == domain.Contains(defaultValue))
            {
                Debug.Assert(unionCaseRewriting != null, "No union of rewritings for case statements");
                var unionTree = TileToCellTree(unionCaseRewriting, _context);
                var configurationNeedsDefault = _context.RightFragmentQP.Difference(
                    rightDomainQuery.RightFragmentQuery, unionTree.RightFragmentQuery);

                if (_context.RightFragmentQP.IsSatisfiable(configurationNeedsDefault))
                {
                    if (hasDefaultValue)
                    {
                        caseStatement.AddWhenThen(BoolExpression.True, new ConstantProjectedSlot(defaultValue));
                    }
                    else
                    {
                        configurationNeedsDefault.Condition.ExpensiveSimplify();
                        var builder = new StringBuilder();
                        builder.AppendLine(
                            Strings.ViewGen_No_Default_Value_For_Configuration(currentPath.PathToString(false /* for alias */)));
                        RewritingValidator.EntityConfigurationToUserString(configurationNeedsDefault.Condition, builder);
                        _errorLog.AddEntry(
                            new ErrorLog.Record(
                                ViewGenErrorCode.NoDefaultValue, builder.ToString(), _context.AllWrappersForExtent, String.Empty));
                    }
                }
            }
        }
        private void GenerateCaseStatements(
            IEnumerable<MemberPath> members,
            HashSet<FragmentQuery> outputUsedViews)
        {
            // Compute right domain query - non-simplified version of "basic view"
            // It is used below to check whether we need a default value in a case statement
            var usedCells = _context.AllWrappersForExtent.Where(w => _usedViews.Contains(w.FragmentQuery));
            CellTreeNode rightDomainQuery = new OpCellTreeNode(
                _context, CellTreeOpType.Union,
                usedCells.Select(wrapper => new LeafCellTreeNode(_context, wrapper)).ToArray());

            foreach (var currentPath in members)
            {
                // Add the types can member have, i.e., its type and its subtypes
                var domain = GetDomain(currentPath).ToList();
                var caseStatement = new CaseStatement(currentPath);

                Tile<FragmentQuery> unionCaseRewriting = null;

                // optimization for domain = {NULL, NOT_NULL}
                // Create a single case: WHEN True THEN currentPath
                // Reason: if the WHEN condition is not satisfied (say because of LOJ), then currentPath = NULL
                var needCaseStatement =
                    !(domain.Count == 2 &&
                      domain.Contains(Constant.Null, Constant.EqualityComparer) &&
                      domain.Contains(Constant.NotNull, Constant.EqualityComparer));
                {
                    // go over the domain
                    foreach (var domainValue in domain)
                    {
                        if (domainValue == Constant.Undefined
                            && _context.ViewTarget == ViewTarget.QueryView)
                        {
                            // we cannot assume closed domain for query views;
                            // if obtaining undefined is possible, we need to account for that
                            caseStatement.AddWhenThen(
                                BoolExpression.False /* arbitrary condition */,
                                new ConstantProjectedSlot(Constant.Undefined));
                            continue;
                        }
                        TraceVerbose("CASE STATEMENT FOR {0}={1}", currentPath, domainValue);

                        // construct WHERE clause for this value
                        var memberConditionQuery = CreateMemberConditionQuery(currentPath, domainValue);

                        Tile<FragmentQuery> caseRewriting;
                        if (FindRewritingAndUsedViews(
                            memberConditionQuery.Attributes, memberConditionQuery.Condition, outputUsedViews, out caseRewriting))
                        {
                            if (_context.ViewTarget
                                == ViewTarget.UpdateView)
                            {
                                unionCaseRewriting = (unionCaseRewriting != null)
                                                         ? _qp.Union(unionCaseRewriting, caseRewriting)
                                                         : caseRewriting;
                            }

                            if (needCaseStatement)
                            {
                                var isAlwaysTrue = AddRewritingToCaseStatement(caseRewriting, caseStatement, currentPath, domainValue);
                                if (isAlwaysTrue)
                                {
                                    break;
                                }
                            }
                        }
                        else
                        {
                            if (!IsDefaultValue(domainValue, currentPath))
                            {
                                Debug.Assert(_context.ViewTarget == ViewTarget.UpdateView || !_config.IsValidationEnabled);

                                if (!ErrorPatternMatcher.FindMappingErrors(_context, _domainMap, _errorLog))
                                {
                                    var builder = new StringBuilder();
                                    var extentName = StringUtil.FormatInvariant("{0}", _extentPath);
                                    var objectString = _context.ViewTarget == ViewTarget.QueryView
                                                           ? Strings.ViewGen_Entities
                                                           : Strings.ViewGen_Tuples;

                                    if (_context.ViewTarget
                                        == ViewTarget.QueryView)
                                    {
                                        builder.AppendLine(Strings.Viewgen_CannotGenerateQueryViewUnderNoValidation(extentName));
                                    }
                                    else
                                    {
                                        builder.AppendLine(Strings.ViewGen_Cannot_Disambiguate_MultiConstant(objectString, extentName));
                                    }
                                    RewritingValidator.EntityConfigurationToUserString(
                                        memberConditionQuery.Condition, builder, _context.ViewTarget == ViewTarget.UpdateView);
                                    var record = new ErrorLog.Record(
                                        ViewGenErrorCode.AmbiguousMultiConstants, builder.ToString(), _context.AllWrappersForExtent,
                                        String.Empty);
                                    _errorLog.AddEntry(record);
                                }
                            }
                        }
                    }
                }

                if (_errorLog.Count == 0)
                {
                    // for update views, add WHEN True THEN defaultValue
                    // which will ultimately be translated into a (possibly implicit) ELSE clause
                    if (_context.ViewTarget == ViewTarget.UpdateView && needCaseStatement)
                    {
                        AddElseDefaultToCaseStatement(currentPath, caseStatement, domain, rightDomainQuery, unionCaseRewriting);
                    }

                    if (caseStatement.Clauses.Count > 0)
                    {
                        TraceVerbose("{0}", caseStatement.ToString());
                        _caseStatements[currentPath] = caseStatement;
                    }
                }
            }
        }
        // returns true when the case statement is completed
        private bool AddRewritingToCaseStatement(
            Tile<FragmentQuery> rewriting, CaseStatement caseStatement, MemberPath currentPath, Constant domainValue)
        {
            var whenCondition = BoolExpression.True;
            // check whether the rewriting is always true or always false
            // if it's always true, we don't need any other WHEN clauses in the case statement
            // if it's always false, we don't need to add this WHEN clause to the case statement
            // given: domainQuery is satisfied. Check (domainQuery -> rewriting)
            var isAlwaysTrue = _qp.IsContainedIn(CreateTile(_domainQuery), rewriting);
            var isAlwaysFalse = _qp.IsDisjointFrom(CreateTile(_domainQuery), rewriting);
            Debug.Assert(!(isAlwaysTrue && isAlwaysFalse));
            if (isAlwaysFalse)
            {
                return false; // don't need an unsatisfiable WHEN clause
            }
            if (isAlwaysTrue)
            {
                Debug.Assert(caseStatement.Clauses.Count == 0);
            }

            ProjectedSlot projectedSlot;
            if (domainValue.HasNotNull())
            {
                projectedSlot = new MemberProjectedSlot(currentPath);
            }
            else
            {
                projectedSlot = new ConstantProjectedSlot(domainValue);
            }

            if (!isAlwaysTrue)
            {
                whenCondition = TileToBoolExpr(rewriting);
            }
            else
            {
                whenCondition = BoolExpression.True;
            }
            caseStatement.AddWhenThen(whenCondition, projectedSlot);

            return isAlwaysTrue;
        }
 /// <summary>
 /// Creates a slot for <paramref name="statement"/>.
 /// </summary>
 internal CaseStatementProjectedSlot(CaseStatement statement, IEnumerable<WithRelationship> withRelationships)
 {
     m_caseStatement = statement;
     m_withRelationships = withRelationships;
 }
Example #9
0
        /// <summary>
        /// Creates new <see cref="ProjectedSlot"/> that is qualified with <paramref name="block"/>.CqlAlias.
        /// If current slot is composite (such as <see cref="CaseStatementProjectedSlot"/>, then this method recursively qualifies all parts
        /// and returns a new deeply qualified slot (as opposed to <see cref="CqlBlock.QualifySlotWithBlockAlias"/>).
        /// </summary>
        internal override ProjectedSlot DeepQualify(CqlBlock block)
        {
            CaseStatement newStatement = m_caseStatement.DeepQualify(block);

            return(new CaseStatementProjectedSlot(newStatement, null));
        }
Example #10
0
 /// <summary>
 /// Creates a slot for <paramref name="statement"/>.
 /// </summary>
 internal CaseStatementProjectedSlot(CaseStatement statement, IEnumerable <WithRelationship> withRelationships)
 {
     m_caseStatement     = statement;
     m_withRelationships = withRelationships;
 }
        // 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);
        }