/// <summary> /// The from clause can contain top level concat operators as well. /// </summary> /// <param name="fromClause"></param> /// <param name="queryModel"></param> public override void VisitMainFromClause(MainFromClause fromClause, QueryModel queryModel) { bool processed = false; if (fromClause.FromExpression is SubQueryExpression) { var sq = fromClause.FromExpression as SubQueryExpression; var qms = Split(sq.QueryModel); if (qms.Length > 1) { processed = true; // Find which Result operators we should remove for all but the last item. var lastConcat = queryModel.ResultOperators.Reverse().Where(x => x is ConcatResultOperator).FirstOrDefault(); int? lastConcatIndex = lastConcat == null ? (int?) null : queryModel.ResultOperators.IndexOf(lastConcat); // Create a new one for each from expression, and substitute in for the current one. // Recursively process it. foreach (var qSub in qms) { var qm = queryModel.Clone(); qm.MainFromClause.FromExpression = new SubQueryExpression(qSub); qm.Flatten(); if (lastConcatIndex.HasValue && qSub != qms[qms.Length - 1]) { for (int i = 0; i < lastConcatIndex + 1; i++) { qm.ResultOperators.RemoveAt(0); } } VisitQueryModel(qm); } } } if (!processed) { base.VisitMainFromClause(fromClause, queryModel); // Models that don't need further processing are "done". _models.Add(queryModel); } }
/// <summary> /// Given a query model, look at all the result operators for Concat operators, and split everything up /// into separate query models. /// </summary> /// <param name="queryModel"></param> /// <returns></returns> private static IEnumerable<QueryModel> SplitQMByConcatResultOperator(QueryModel queryModel) { // If there are no concat result operators in the list, then we just bail quickly. // This is to specifically avoid the Clone operation unless we actually need it. if (!queryModel.ResultOperators.Where(r => r is ConcatResultOperator).Any()) { return new QueryModel[] { queryModel }; } // Now, look for concat operators in the list. Pop them out when we find them. ConcatResultOperator ro = null; var qm = queryModel.Clone(); var lst = new List<QueryModel>(); while ((ro = qm.ResultOperators.Reverse().Where(r => r is ConcatResultOperator).Cast<ConcatResultOperator>().FirstOrDefault()) != null) { // We are going to make q QueryModel here that uses the second source in the Concat operator. // This means everything that comes before this query can be ignored - and we want this "source" to // become the query from clause. Note this also means messing with the "select" clause to make sure it // isn't doing anything special (select clause comes before result operators, semantically). QueryModel newQM = NewQMFromOldWithLifting(ro.Source2, qm.MainFromClause.ItemType, qm.MainFromClause.ItemName); var cc = new CloneContext(new QuerySourceMapping()); var indexToRemoveTo = qm.ResultOperators.IndexOf(ro); for (int i = indexToRemoveTo + 1; i < qm.ResultOperators.Count; i++) { newQM.ResultOperators.Add(qm.ResultOperators[i].Clone(cc)); } lst.Add(newQM.Flatten()); // Ok - we've taken one branch. We need to remove it from the list of things to look at, and work on the // next one. qm.ResultOperators.Remove(ro); } // The QueryModel left over needs to be added to the list. lst.Add(qm.Flatten()); return lst; }