private static void AnalyzeInNodeMultiIndex(ExprInNode inNode, QueryGraph queryGraph) { ExprNode[] setExpressions = GetInNodeSetExpressions(inNode); if (setExpressions.Length == 0) { return; } var perStreamExprs = new LinkedHashMap <int?, IList <ExprNode> >(); foreach (ExprNode exprNodeSet in setExpressions) { if (!(exprNodeSet is ExprIdentNode)) { continue; } var setIdent = (ExprIdentNode)exprNodeSet; AddToList(setIdent.StreamId, setIdent, perStreamExprs); } if (perStreamExprs.IsEmpty()) { return; } var testExpr = inNode.ChildNodes[0]; var testExprType = testExpr.ExprEvaluator.ReturnType.GetBoxedType(); if (perStreamExprs.Count > 1) { return; } var entry = perStreamExprs.First(); ExprNode[] exprNodes = ExprNodeUtility.ToArray(entry.Value); foreach (ExprNode node in exprNodes) { var exprType = node.ExprEvaluator.ReturnType; if (exprType.GetBoxedType() != testExprType) { return; } } int?testStreamNum; int setStream = entry.Key.Value; if (!(testExpr is ExprIdentNode)) { EligibilityDesc eligibility = EligibilityUtil.VerifyInputStream(testExpr, setStream); if (!eligibility.Eligibility.IsEligible()) { return; } if (eligibility.Eligibility == Eligibility.REQUIRE_ONE && setStream == eligibility.StreamNum) { return; } testStreamNum = eligibility.StreamNum; } else { testStreamNum = ((ExprIdentNode)testExpr).StreamId; } if (testStreamNum == null) { queryGraph.AddInSetMultiIndexUnkeyed(testExpr, setStream, exprNodes); } else { if (testStreamNum.Equals(entry.Key)) { return; } queryGraph.AddInSetMultiIndex(testStreamNum.Value, testExpr, setStream, exprNodes); } }
/// <summary> /// Creates a strategy implementation that indicates to subscribers /// the statement results based on the select-clause columns. /// </summary> /// <param name="subscriber">to indicate to</param> /// <param name="selectClauseTypes">are the types of each column in the select clause</param> /// <param name="selectClauseColumns">the names of each column in the select clause</param> /// <param name="statement">statement</param> /// <param name="methodName">method name</param> /// <param name="runtimeURI">runtime URI</param> /// <param name="importService">runtime imports</param> /// <returns>strategy for dispatching naturals</returns> /// <throws>ResultDeliveryStrategyInvalidException if the subscriber is invalid</throws> public static ResultDeliveryStrategy Create( EPStatement statement, object subscriber, string methodName, Type[] selectClauseTypes, string[] selectClauseColumns, string runtimeURI, ImportService importService) { if (selectClauseTypes == null) { selectClauseTypes = new Type[0]; selectClauseColumns = new string[0]; } if (methodName == null) { methodName = "Update"; } // sort by presence of EPStatement as the first parameter var subscriberType = subscriber.GetType(); var sorted = new List<MethodInfo>(subscriberType.GetMethods()); sorted.Sort(METHOD_PREFERENCE_COMPARATOR); // Locate update methods MethodInfo subscriptionMethod = null; IDictionary<MethodInfo, Type[]> updateMethods = new LinkedHashMap<MethodInfo, Type[]>(); foreach (var method in sorted) { if ((method.Name == methodName) && (method.IsPublic)) { // Determine parameter types without EPStatement (the normalized parameters) var normalizedParameters = GetMethodParameterTypesWithoutEPStatement(method); updateMethods.Put(method, normalizedParameters); } } // none found if (updateMethods.Count == 0) { var message = "Subscriber object does not provide a public method by name 'Update'"; throw new ResultDeliveryStrategyInvalidException(message); } // match to parameters var isMapArrayDelivery = false; var isObjectArrayDelivery = false; var isSingleRowMap = false; var isSingleRowObjectArr = false; var isTypeArrayDelivery = false; // find an exact-matching method: no conversions and not even unboxing/boxing foreach (var methodNormParameterEntry in updateMethods) { var normalized = methodNormParameterEntry.Value; if (normalized.Length == selectClauseTypes.Length) { var fits = true; for (var i = 0; i < normalized.Length; i++) { if (selectClauseTypes[i] != null && selectClauseTypes[i] != normalized[i]) { fits = false; break; } } if (fits) { subscriptionMethod = methodNormParameterEntry.Key; break; } } } // when not yet resolved, find an exact-matching method with boxing/unboxing if (subscriptionMethod == null) { foreach (var methodNormParameterEntry in updateMethods) { var normalized = methodNormParameterEntry.Value; if (normalized.Length == selectClauseTypes.Length) { var fits = true; for (var i = 0; i < normalized.Length; i++) { var boxedExpressionType = selectClauseTypes[i].GetBoxedType(); var boxedParameterType = normalized[i].GetBoxedType(); if (boxedExpressionType != null && boxedExpressionType != boxedParameterType) { fits = false; break; } } if (fits) { subscriptionMethod = methodNormParameterEntry.Key; break; } } } } // when not yet resolved, find assignment-compatible methods that may require widening (including Integer to Long etc.) var checkWidening = false; if (subscriptionMethod == null) { foreach (var methodNormParameterEntry in updateMethods) { var normalized = methodNormParameterEntry.Value; if (normalized.Length == selectClauseTypes.Length) { var fits = true; for (var i = 0; i < normalized.Length; i++) { var expressionType = selectClauseTypes[i]; var parameterType = normalized[i]; //var boxedExpressionType = expressionType.GetBoxedType(); //var boxedParameterType = parameterType.GetBoxedType(); if (expressionType != null && !expressionType.IsAssignmentCompatible(parameterType)) { fits = false; break; } } if (fits) { subscriptionMethod = methodNormParameterEntry.Key; checkWidening = true; break; } } } } // when not yet resolved, find first-fit wildcard method if (subscriptionMethod == null) { foreach (var methodNormParameterEntry in updateMethods) { var normalized = methodNormParameterEntry.Value; if (normalized.Length == 1 && normalized[0] == typeof(IDictionary<string, object>)) { isSingleRowMap = true; subscriptionMethod = methodNormParameterEntry.Key; break; } if (normalized.Length == 1 && normalized[0] == typeof(object[])) { isSingleRowObjectArr = true; subscriptionMethod = methodNormParameterEntry.Key; break; } if (normalized.Length == 2 && normalized[0] == typeof(IDictionary<string, object>[]) && normalized[1] == typeof(IDictionary<string, object>[])) { subscriptionMethod = methodNormParameterEntry.Key; isMapArrayDelivery = true; break; } if (normalized.Length == 2 && normalized[0] == typeof(object[][]) && normalized[1] == typeof(object[][])) { subscriptionMethod = methodNormParameterEntry.Key; isObjectArrayDelivery = true; break; } // Handle uniform underlying or column type array dispatch if (normalized.Length == 2 && normalized[0].Equals(normalized[1]) && normalized[0].IsArray && selectClauseTypes.Length == 1) { Type componentType = normalized[0].GetElementType(); if (selectClauseTypes[0].IsAssignmentCompatible(componentType)) { subscriptionMethod = methodNormParameterEntry.Key; isTypeArrayDelivery = true; break; } } if (normalized.Length == 0 && selectClauseTypes.Length == 1 && selectClauseTypes[0] == null) { subscriptionMethod = methodNormParameterEntry.Key; } } } if (subscriptionMethod == null) { if (updateMethods.Count > 1) { var parametersDesc = TypeHelper.GetParameterAsString(selectClauseTypes); var message = "No suitable subscriber method named 'Update' found, expecting a method that takes " + selectClauseTypes.Length + " parameter of type " + parametersDesc; throw new ResultDeliveryStrategyInvalidException(message); } KeyValuePair<MethodInfo, Type[]> firstUpdateMethod = updateMethods.First(); var parametersNormalized = firstUpdateMethod.Value; var parametersDescNormalized = TypeHelper.GetParameterAsString(selectClauseTypes); if (parametersNormalized.Length != selectClauseTypes.Length) { if (selectClauseTypes.Length > 0) { var message = "No suitable subscriber method named 'Update' found, expecting a method that takes " + selectClauseTypes.Length + " parameter of type " + parametersDescNormalized; throw new ResultDeliveryStrategyInvalidException(message); } else { var message = "No suitable subscriber method named 'Update' found, expecting a method that takes no parameters"; throw new ResultDeliveryStrategyInvalidException(message); } } for (var i = 0; i < parametersNormalized.Length; i++) { var boxedExpressionType = selectClauseTypes[i].GetBoxedType(); var boxedParameterType = parametersNormalized[i].GetBoxedType(); if (boxedExpressionType != null && !boxedExpressionType.IsAssignmentCompatible(boxedParameterType)) { var message = "Subscriber method named 'Update' for parameter number " + (i + 1) + " is not assignable, " + "expecting type '" + selectClauseTypes[i].GetParameterAsString() + "' but found type '" + parametersNormalized[i].GetParameterAsString() + "'"; throw new ResultDeliveryStrategyInvalidException(message); } } throw new ResultDeliveryStrategyInvalidException("No suitable subscriber method named 'Update' found"); } var parameterTypes = subscriptionMethod.GetParameterTypes(); // Invalid if there is a another footprint for the subscription method that does not include EPStatement if present var firstParameterIsEPStatement = IsFirstParameterEPStatement(subscriptionMethod); if (isMapArrayDelivery) { return firstParameterIsEPStatement ? new ResultDeliveryStrategyMapWStmt(statement, subscriber, subscriptionMethod, selectClauseColumns, importService) : new ResultDeliveryStrategyMap(statement, subscriber, subscriptionMethod, selectClauseColumns, importService); } if (isObjectArrayDelivery) { return firstParameterIsEPStatement ? new ResultDeliveryStrategyObjectArrWStmt(statement, subscriber, subscriptionMethod, importService) : new ResultDeliveryStrategyObjectArr(statement, subscriber, subscriptionMethod, importService); } if (isTypeArrayDelivery) { return firstParameterIsEPStatement ? new ResultDeliveryStrategyTypeArrWStmt( statement, subscriber, subscriptionMethod, parameterTypes[1].GetElementType(), importService) : new ResultDeliveryStrategyTypeArr( statement, subscriber, subscriptionMethod, parameterTypes[0].GetElementType(), importService); } // Try to find the "Start", "End" and "UpdateRStream" methods MethodInfo startMethod = null; MethodInfo endMethod = null; MethodInfo rStreamMethod = null; startMethod = subscriberType.GetMethod("UpdateStart", new Type[] { typeof(EPStatement), typeof(int), typeof(int) }); if (startMethod == null) { startMethod = subscriberType.GetMethod("UpdateStart", new Type[] {typeof(int), typeof(int)}); } endMethod = subscriberType.GetMethod("UpdateEnd", new Type[] { typeof(EPStatement) }); if (endMethod == null) { endMethod = subscriberType.GetMethod("UpdateEnd"); } // must be exactly the same footprint (may include EPStatement), since delivery convertor used for both rStreamMethod = subscriberType.GetMethod("UpdateRStream", subscriptionMethod.GetParameterTypes()); if (rStreamMethod == null) { // we don't have an "UpdateRStream" expected, make sure there isn't one with/without EPStatement if (IsFirstParameterEPStatement(subscriptionMethod)) { var classes = updateMethods.Get(subscriptionMethod); ValidateNonMatchUpdateRStream(subscriber, classes); } else { var classes = new Type[parameterTypes.Length + 1]; classes[0] = typeof(EPStatement); Array.Copy(parameterTypes, 0, classes, 1, parameterTypes.Length); ValidateNonMatchUpdateRStream(subscriber, classes); } } DeliveryConvertor convertor; if (parameterTypes.Length == 0) { convertor = DeliveryConvertorZeroLengthParam.INSTANCE; } else if (parameterTypes.Length == 1 && parameterTypes[0] == typeof(EPStatement)) { convertor = new DeliveryConvertorStatementOnly(statement); } else if (isSingleRowMap) { convertor = firstParameterIsEPStatement ? new DeliveryConvertorMapWStatement(selectClauseColumns, statement) : (DeliveryConvertor) new DeliveryConvertorMap(selectClauseColumns); } else if (isSingleRowObjectArr) { convertor = firstParameterIsEPStatement ? new DeliveryConvertorObjectArrWStatement(statement) : (DeliveryConvertor) DeliveryConvertorObjectArr.INSTANCE; } else { if (checkWidening) { var normalizedParameters = updateMethods.Get(subscriptionMethod); convertor = DetermineWideningDeliveryConvertor( firstParameterIsEPStatement, statement, selectClauseTypes, normalizedParameters, subscriptionMethod, runtimeURI); } else { convertor = firstParameterIsEPStatement ? new DeliveryConvertorNullWStatement(statement) : (DeliveryConvertor) DeliveryConvertorNull.INSTANCE; } } return new ResultDeliveryStrategyImpl( statement, subscriber, convertor, subscriptionMethod, startMethod, endMethod, rStreamMethod, importService); }