public SelectManyKeyPipeWithStartEdge(SelectManyStreamable <TKey, TSource, TResult> stream, IStreamObserver <TKey, TResult> observer) : base(stream, observer) { this.selector = (Expression <Func <long, TKey, TSource, IEnumerable <TResult> > >)stream.Selector; this.selectorFunc = this.selector.Compile(); this.pool = MemoryManager.GetMemoryPool <TKey, TResult>(stream.Properties.IsColumnar); this.pool.Get(out this.batch); this.batch.Allocate(); this.iter = 0; this.errorMessages = stream.ErrorMessages; }
public static Tuple <Type, string> Generate <TKey, TPayload, TResult>(SelectManyStreamable <TKey, TPayload, TResult> stream) { Contract.Ensures(Contract.Result <Tuple <Type, string> >() == null || typeof(UnaryPipe <TKey, TPayload, TResult>).GetTypeInfo().IsAssignableFrom(Contract.Result <Tuple <Type, string> >().Item1)); string generatedClassName; string expandedCode; string errorMessages = null; try { generatedClassName = $"SelectMany_{sequenceNumber++}"; var keyType = typeof(TKey); var payloadType = typeof(TPayload); var resultType = typeof(TResult); var template = new SelectManyTemplate(generatedClassName, keyType, payloadType, resultType); var tm = new TypeMapper(keyType, payloadType, resultType); var gps = tm.GenericTypeVariables(keyType, payloadType, resultType); template.genericParameters = gps.BracketedCommaSeparatedString(); template.numberOfGenericParameters = gps.Count(); template.TKeyTResultGenericParameters = tm.GenericTypeVariables(keyType, resultType).BracketedCommaSeparatedString(); template.MemoryPoolGenericParameters = $"<{template.TKey}, {template.TResult}>"; if (resultType == typeof(int) || resultType == typeof(long) || resultType == typeof(string)) { template.MemoryPoolGenericParameters = string.Empty; } var payloadParameterIndex = 0; if (stream.HasKey && stream.HasStartEdge) { payloadParameterIndex = 2; } else if (stream.HasKey || stream.HasStartEdge) { payloadParameterIndex = 1; } var selector = stream.Selector; var payloadParameter = selector.Parameters.ElementAt(payloadParameterIndex); template.PARAMETER = payloadParameter.Name; template.resultPayloadRepresentation = new ColumnarRepresentation(resultType); template.resultFields = template.resultPayloadRepresentation.AllFields; if (template.numberOfGenericParameters > 0) { generatedClassName = generatedClassName + "`" + template.numberOfGenericParameters.ToString(CultureInfo.InvariantCulture); } expandedCode = string.Empty; Expression transformedSelector = selector; // No substitutions are made for the start edge parameter or key parameter. Both just remain in the // body of the result selector and are set as local variables in the generated code. var keyParameterIndex = stream.HasStartEdge ? 1 : 0; var tuple = OptimizeSelectMany(selector.Body); if (tuple != null) { template.enumerableRepeatSelector = true; var resultSelector = stream.Selector; var sourceMessageType = StreamMessageManager.GetStreamMessageType <TKey, TPayload>(); var pseudoLambdaParameters = new ParameterExpression[stream.HasKey ? 2 : 1]; var pseudoLambdaIndex = 0; if (stream.HasKey) { pseudoLambdaParameters[pseudoLambdaIndex++] = selector.Parameters[keyParameterIndex]; } pseudoLambdaParameters[pseudoLambdaIndex] = payloadParameter; var pseudoLambda = Expression.Lambda(tuple.Item1, pseudoLambdaParameters); var parameterSubstitutions = new List <Tuple <ParameterExpression, SelectParameterInformation> > { Tuple.Create(payloadParameter, new SelectParameterInformation() { BatchName = "batch", BatchType = sourceMessageType, IndexVariableName = "i", parameterRepresentation = new ColumnarRepresentation(payloadType) }) }; var projectionResult = SelectTransformer.Transform(pseudoLambda, parameterSubstitutions, template.resultPayloadRepresentation, true, stream.HasStartEdge); template.computedFields = projectionResult.ComputedFields; template.useEnumerator = false; var loopCounter = tuple.Item2; var newParameters = new ParameterExpression[stream.HasKey ? 2 : 1]; newParameters[0] = payloadParameter; if (stream.HasKey) { newParameters[1] = selector.Parameters[keyParameterIndex]; } var loopCounterLambda = Expression.Lambda(loopCounter, payloadParameter); var transformedLoopCounter = Extensions.TransformFunction <TKey, TPayload>(loopCounterLambda, 0); template.loopCounter = transformedLoopCounter.Body.ExpressionToCSharp(); // REVIEW: Alternative: use Inline to replace occurrences of the key parameter // with "batch.key.col[i]". if (stream.HasKey) { template.keyParameterName = selector.Parameters[keyParameterIndex].Name; } } else { transformedSelector = Extensions.TransformFunction <TKey, TPayload>(stream.Selector, payloadParameterIndex).Body; if (transformedSelector == null) { template.useEnumerator = true; template.transformedSelectorAsSource = stream.Selector.ExpressionToCSharp(); } else { var tuple2 = OptimizeSelectMany(transformedSelector); if (tuple2 != null) { template.useEnumerator = false; template.loopCounter = tuple2.Item2.ExpressionToCSharp(); template.transformedSelectorAsSource = tuple2.Item1.ExpressionToCSharp(); } else { template.useEnumerator = true; template.transformedSelectorAsSource = transformedSelector.ExpressionToCSharp(); } } } template.StartEdgeParameterName = stream.HasStartEdge ? selector.Parameters.ElementAt(0).Name : null; template.hasKey = stream.HasKey; expandedCode = template.TransformText(); var assemblyReferences = Transformer.AssemblyReferencesNeededFor(typeof(TKey), typeof(TPayload), typeof(TResult)); assemblyReferences.Add(typeof(IStreamable <,>).GetTypeInfo().Assembly); assemblyReferences.Add(Transformer.GeneratedStreamMessageAssembly <TKey, TPayload>()); assemblyReferences.Add(Transformer.GeneratedStreamMessageAssembly <TKey, TResult>()); assemblyReferences.Add(Transformer.GeneratedMemoryPoolAssembly <TKey, TResult>()); assemblyReferences.AddRange(Transformer.AssemblyReferencesNeededFor(stream.Selector)); var a = Transformer.CompileSourceCode(expandedCode, assemblyReferences, out errorMessages); var t = a.GetType(generatedClassName); t = t.InstantiateAsNecessary(typeof(TKey), typeof(TPayload), typeof(TResult)); return(Tuple.Create(t, errorMessages)); } catch { if (Config.CodegenOptions.DontFallBackToRowBasedExecution) { throw new InvalidOperationException("Code Generation failed when it wasn't supposed to!"); } return(Tuple.Create((Type)null, errorMessages)); } }