public EquiJoinStreamable(IStreamable <TKey, TLeft> left, IStreamable <TKey, TRight> right, Expression <Func <TLeft, TRight, TResult> > selector) : base(left.Properties.Join(right.Properties, selector), left, right) { Contract.Requires(selector != null); this.Selector = selector; // This operator uses the equality method on payloads if (left.Properties.IsColumnar && !left.Properties.IsStartEdgeOnly && !left.Properties.PayloadEqualityComparer.CanUsePayloadEquality()) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Type of left side of join, '{0}', does not have a valid equality operator for columnar mode.", typeof(TLeft).FullName)); } // This operator uses the equality method on payloads if (right.Properties.IsColumnar && !right.Properties.IsStartEdgeOnly && !right.Properties.PayloadEqualityComparer.CanUsePayloadEquality()) { throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Type of right side of join, '{0}', does not have a valid equality operator for columnar mode.", typeof(TRight).FullName)); } if (left.Properties.IsStartEdgeOnly && right.Properties.IsStartEdgeOnly) { if ((left.Properties.KeyComparer != null) && (right.Properties.KeyComparer != null) && (left.Properties.KeyComparer.ExpressionEquals(right.Properties.KeyComparer) && (typeof(TKey).GetPartitionType() == null))) { this.joinKind = JoinKind.IncreasingOrderEquiJoin; this.fallbackGenerator = (s, e, o) => new IncreasingOrderEquiJoinPipe <TKey, TLeft, TRight, TResult>(s, e, o); this.partitionedGenerator = null; this.columnarGenerator = (k => IncreasingOrderEquiJoinTemplate.Generate(this, this.Selector)); } else { this.joinKind = JoinKind.StartEdgeEquijoin; this.fallbackGenerator = (s, e, o) => new StartEdgeEquiJoinPipe <TKey, TLeft, TRight, TResult>(s, e, o); this.partitionedGenerator = (s, e, o) => (BinaryPipe <TKey, TLeft, TRight, TResult>)Activator.CreateInstance( typeof(PartitionedStartEdgeEquiJoinPipe <, , , ,>).MakeGenericType( typeof(TKey), typeof(TLeft), typeof(TRight), typeof(TResult), typeof(TKey).GetPartitionType()), s, e, o); this.columnarGenerator = (k => StartEdgeEquiJoinTemplate.Generate(this, this.Selector)); } } else { this.joinKind = JoinKind.EquiJoin; this.fallbackGenerator = (s, e, o) => new EquiJoinPipe <TKey, TLeft, TRight, TResult>(s, e, o); this.partitionedGenerator = (s, e, o) => (BinaryPipe <TKey, TLeft, TRight, TResult>)Activator.CreateInstance( CreatePartitionedEquiJoinType(), s, e, o); this.columnarGenerator = (k => EquiJoinTemplate.Generate(this, this.Selector)); } Initialize(); }
public EquiJoinStreamable(IStreamable <TKey, TLeft> left, IStreamable <TKey, TRight> right, Expression <Func <TLeft, TRight, TResult> > selector) : base(left.Properties.Join(right.Properties, selector), left, right) { Contract.Requires(selector != null); this.Selector = selector; if (left.Properties.IsStartEdgeOnly && right.Properties.IsStartEdgeOnly) { if ((left.Properties.KeyComparer != null) && (right.Properties.KeyComparer != null) && left.Properties.KeyComparer.ExpressionEquals(right.Properties.KeyComparer) && (typeof(TKey).GetPartitionType() == null)) { this.joinKind = JoinKind.IncreasingOrderEquiJoin; this.fallbackGenerator = (s, e, o) => new IncreasingOrderEquiJoinPipe <TKey, TLeft, TRight, TResult>(s, e, o); this.partitionedGenerator = null; this.columnarGenerator = k => IncreasingOrderEquiJoinTemplate.Generate(this, this.Selector); } else { this.joinKind = JoinKind.StartEdgeEquijoin; this.fallbackGenerator = (s, e, o) => new StartEdgeEquiJoinPipe <TKey, TLeft, TRight, TResult>(s, e, o); this.partitionedGenerator = (s, e, o) => (BinaryPipe <TKey, TLeft, TRight, TResult>)Activator.CreateInstance( typeof(PartitionedStartEdgeEquiJoinPipe <, , , ,>).MakeGenericType( typeof(TKey), typeof(TLeft), typeof(TRight), typeof(TResult), typeof(TKey).GetPartitionType()), s, e, o); this.columnarGenerator = k => StartEdgeEquiJoinTemplate.Generate(this, this.Selector); } } else if (left.Properties.IsConstantDuration && right.Properties.IsConstantDuration) { this.joinKind = JoinKind.FixedIntervalEquiJoin; this.fallbackGenerator = (s, e, o) => new FixedIntervalEquiJoinPipe <TKey, TLeft, TRight, TResult>(s, e, o); this.partitionedGenerator = (s, e, o) => (BinaryPipe <TKey, TLeft, TRight, TResult>)Activator.CreateInstance( CreatePartitionedFixedIntervalEquiJoinType(), s, e, o); this.columnarGenerator = k => FixedIntervalEquiJoinTemplate.Generate(this, this.Selector); } else { this.joinKind = JoinKind.EquiJoin; this.fallbackGenerator = (s, e, o) => new EquiJoinPipe <TKey, TLeft, TRight, TResult>(s, e, o); this.partitionedGenerator = (s, e, o) => (BinaryPipe <TKey, TLeft, TRight, TResult>)Activator.CreateInstance( CreatePartitionedEquiJoinType(), s, e, o); this.columnarGenerator = k => EquiJoinTemplate.Generate(this, this.Selector); } Initialize(); }
/// <summary> /// Generate a batch class definition to be used as StartEdgeEquiJoin operator. /// Compile the definition, dynamically load the assembly containing it, and return the Type representing the /// aggregate class. /// </summary> /// <typeparam name="TKey">The key type for both sides.</typeparam> /// <typeparam name="TLeft">The payload type for the left side.</typeparam> /// <typeparam name="TRight">The payload type for the right side.</typeparam> /// <typeparam name="TResult">The payload type for the resulting stream.</typeparam> /// <returns> /// A type that is defined to be a subtype of BinaryPipe<<typeparamref name="TKey"/>,<typeparamref name="TLeft"/>, <typeparamref name="TRight"/>, <typeparamref name="TKey"/>, <typeparamref name="TResult"/>>. /// </returns> internal static Tuple <Type, string> Generate <TKey, TLeft, TRight, TResult>( BinaryStreamable <TKey, TLeft, TRight, TResult> stream, Expression <Func <TLeft, TRight, TResult> > selector) { Contract.Requires(stream != null); Contract.Ensures(Contract.Result <Tuple <Type, string> >() == null || typeof(BinaryPipe <TKey, TLeft, TRight, TResult>).GetTypeInfo().IsAssignableFrom(Contract.Result <Tuple <Type, string> >().Item1)); string errorMessages = null; try { var template = new IncreasingOrderEquiJoinTemplate($"GeneratedIncreasingOrderEquiJoin_{IOOEJSequenceNumber++}", typeof(TKey), typeof(TLeft), typeof(TRight), typeof(TResult)); template.leftMessageRepresentation = new ColumnarRepresentation(template.leftType); template.leftFields = template.leftMessageRepresentation.AllFields; template.rightMessageRepresentation = new ColumnarRepresentation(template.rightType); template.rightFields = template.rightMessageRepresentation.AllFields; var resultRepresentation = new ColumnarRepresentation(template.resultType); var leftMessageType = StreamMessageManager.GetStreamMessageType <TKey, TLeft>(); var rightMessageType = StreamMessageManager.GetStreamMessageType <TKey, TRight>(); #region Key Comparer var keyComparer = stream.Left.Properties.KeyComparer.GetCompareExpr(); if (!ConstantExpressionFinder.IsClosedExpression(keyComparer)) { return(null); } template.joinKeyOrderComparer = (left, right) => keyComparer.Inline(left, right); #endregion template.BatchGeneratedFrom_TKey_TLeft = Transformer.GetBatchClassName(template.keyType, template.leftType); template.TKeyTLeftGenericParameters = string.Empty; // BUGBUG template.BatchGeneratedFrom_TKey_TRight = Transformer.GetBatchClassName(template.keyType, template.rightType); template.TKeyTRightGenericParameters = string.Empty; // BUGBUG template.BatchGeneratedFrom_TKey_TResult = Transformer.GetBatchClassName(template.keyType, template.resultType); template.TKeyTResultGenericParameters = string.Empty; // BUGBUG template.outputFields = resultRepresentation.AllFields; if (!ConstantExpressionFinder.IsClosedExpression(selector)) { return(null); } #region LeftBatchSelector { var parameterSubsitutions = new List <Tuple <ParameterExpression, SelectParameterInformation> >() { Tuple.Create(selector.Parameters[0], new SelectParameterInformation() { BatchName = "leftBatch", BatchType = leftMessageType, IndexVariableName = "i", parameterRepresentation = template.leftMessageRepresentation, }), }; var projectionResult = SelectTransformer.Transform(selector, parameterSubsitutions, resultRepresentation, true); if (projectionResult.Error) { errorMessages = "error while transforming the result selector"; throw new InvalidOperationException(); } template.leftBatchSelector = (leftBatch, leftIndex, rightEvent) => { var d = new Dictionary <ParameterExpression, string> { { Expression.Variable(leftMessageType, "leftBatch"), leftBatch }, { Expression.Variable(typeof(int), "i"), leftIndex }, { selector.Parameters[1], rightEvent } }; var sb = new System.Text.StringBuilder(); sb.AppendLine("{"); foreach (var kv in projectionResult.ComputedFields) { var f = kv.Key; var e = kv.Value; if (f.OptimizeString()) { sb.AppendFormat( "output.{0}.AddString({1});\n", f.Name, e.ExpressionToCSharpStringWithParameterSubstitution(d)); } else { sb.AppendFormat( "output.{0}.col[index] = {1};\n", f.Name, e.ExpressionToCSharpStringWithParameterSubstitution(d)); } } sb.AppendLine("}"); return(sb.ToString()); }; } #endregion #region RightBatchSelector { var parameterSubsitutions = new List <Tuple <ParameterExpression, SelectParameterInformation> >() { Tuple.Create(selector.Parameters[1], new SelectParameterInformation() { BatchName = "rightBatch", BatchType = rightMessageType, IndexVariableName = "j", parameterRepresentation = template.rightMessageRepresentation, }), }; var projectionResult = SelectTransformer.Transform(selector, parameterSubsitutions, resultRepresentation, true); if (projectionResult.Error) { errorMessages = "error while transforming the result selector"; throw new InvalidOperationException(); } template.rightBatchSelector = (leftEvent, rightBatch, rightIndex) => { var d = new Dictionary <ParameterExpression, string> { { selector.Parameters[0], leftEvent }, { Expression.Variable(rightMessageType, "rightBatch"), rightBatch }, { Expression.Variable(typeof(int), "j"), rightIndex } }; var sb = new System.Text.StringBuilder(); sb.AppendLine("{"); foreach (var kv in projectionResult.ComputedFields) { var f = kv.Key; var e = kv.Value; if (f.OptimizeString()) { sb.AppendFormat( "output.{0}.AddString({1});\n", f.Name, e.ExpressionToCSharpStringWithParameterSubstitution(d)); } else { sb.AppendFormat( "output.{0}.col[index] = {1};\n", f.Name, e.ExpressionToCSharpStringWithParameterSubstitution(d)); } } sb.AppendLine("}"); return(sb.ToString()); }; } #endregion template.getOutputBatch = string.Format( "pool.Get(out genericOutputBatch); output = ({0}{1})genericOutputBatch;", Transformer.GetBatchClassName(template.keyType, template.resultType), template.TKeyTResultGenericParameters); return(template.Generate <TKey, TLeft, TRight, TResult>(selector)); } catch { if (Config.CodegenOptions.DontFallBackToRowBasedExecution) { throw new InvalidOperationException("Code Generation failed when it wasn't supposed to!"); } return(Tuple.Create((Type)null, errorMessages)); } }