public FunDef getDef(Exp[] args, String funName, Syntax syntax) { // Compute signature first. It makes debugging easier. final String signature = syntax.getSignature(funName, Category.Unknown, ExpBase.getTypes(args)); // Resolve function by its upper-case name first. If there is only one // function with that name, stop immediately. If there is more than // function, use some custom method, which generally involves looking // at the type of one of its arguments. List<Resolver> resolvers = funTable.getResolvers(funName, syntax); assert resolvers != null; final List<Resolver.Conversion> conversionList = new ArrayList<Resolver.Conversion>(); int minConversionCost = Integer.MAX_VALUE; List<FunDef> matchDefs = new ArrayList<FunDef>(); List<Resolver.Conversion> matchConversionList = null; for (Resolver resolver : resolvers) { conversionList.clear(); FunDef def = resolver.resolve(args, this, conversionList); if (def != null) { int conversionCost = sumConversionCost(conversionList); if (conversionCost < minConversionCost) { minConversionCost = conversionCost; matchDefs.clear(); matchDefs.add(def); matchConversionList = new ArrayList<Resolver.Conversion>(conversionList); } else if (conversionCost == minConversionCost) { matchDefs.add(def); } else { // ignore this match -- it required more coercions than // other overloadings we've seen } } } switch (matchDefs.size()) { case 0: throw MondrianResource.instance().NoFunctionMatchesSignature.ex(signature); case 1: break; default: final StringBuilder buf = new StringBuilder(); for (FunDef matchDef : matchDefs) { if (buf.length() > 0) { buf.append(", "); } buf.append(matchDef.getSignature()); } throw MondrianResource.instance() .MoreThanOneFunctionMatchesSignature .ex(signature, buf.toString()); } final FunDef matchDef = matchDefs.get(0); for (Resolver.Conversion conversion : matchConversionList) { conversion.checkValid(); conversion.apply(this, Arrays.asList(args)); } return matchDef; }
private boolean requiresExpression(int n) { if (n < 1) { return false; } final Object parent = stack.get(n - 1); if (parent instanceof Formula) { return ((Formula) parent).isMember(); } else if (parent instanceof ResolvedFunCall) { final ResolvedFunCall funCall = (ResolvedFunCall) parent; if (funCall.getFunDef().getSyntax() == Syntax.Parentheses) { return requiresExpression(n - 1); } else { int k = whichArg(funCall, (Exp) stack.get(n)); if (k < 0) { // Arguments of call have mutated since call was placed // on stack. Presumably the call has already been // resolved correctly, so the answer we give here is // irrelevant. return false; } final FunDef funDef = funCall.getFunDef(); final int[] parameterTypes = funDef.getParameterCategories(); return parameterTypes[k] != Category.Set; } } else if (parent instanceof UnresolvedFunCall) { final UnresolvedFunCall funCall = (UnresolvedFunCall) parent; if (funCall.getSyntax() == Syntax.Parentheses || funCall.getFunName().equals("*")) { return requiresExpression(n - 1); } else { int k = whichArg(funCall, (Exp) stack.get(n)); if (k < 0) { // Arguments of call have mutated since call was placed // on stack. Presumably the call has already been // resolved correctly, so the answer we give here is // irrelevant. return false; } return requiresExpression(funCall, k); } } else { return false; } }
NativeEvaluator createEvaluator(RolapEvaluator evaluator, FunDef fun, Exp[] args) { if (!isEnabled()) { return null; } if (!FilterConstraint.isValidContext(evaluator, restrictMemberTypes())) { return null; } // is this "Filter(<set>, <numeric expr>)" String funName = fun.getName(); if (!"Filter".equalsIgnoreCase(funName)) { return null; } if (args.length != 2) { return null; } // extract the set expression List<CrossJoinArg[]> allArgs = crossJoinArgFactory().checkCrossJoinArg(evaluator, args[0]); // checkCrossJoinArg returns a list of CrossJoinArg arrays. The first // array is the CrossJoin dimensions. The second array, if any, // contains additional constraints on the dimensions. If either the // list or the first array is null, then native cross join is not // feasible. if (allArgs == null || allArgs.isEmpty() || allArgs.get(0) == null) { return null; } CrossJoinArg[] cjArgs = allArgs.get(0); if (isPreferInterpreter(cjArgs, false)) { return null; } // extract "order by" expression SchemaReader schemaReader = evaluator.getSchemaReader(); DataSource ds = schemaReader.getDataSource(); // generate the WHERE condition // Need to generate where condition here to determine whether // or not the filter condition can be created. The filter // condition could change to use an aggregate table later in evaluation SqlQuery sqlQuery = SqlQuery.newQuery(ds, "NativeFilter"); RolapNativeSql sql = new RolapNativeSql(sqlQuery, null, evaluator, cjArgs[0].getLevel()); final Exp filterExpr = args[1]; String filterExprStr = sql.generateFilterCondition(filterExpr); if (filterExprStr == null) { return null; } // Check to see if evaluator contains a calculated member that can't be // expanded. This is necessary due to the SqlConstraintsUtils. // addContextConstraint() // method which gets called when generating the native SQL. if (SqlConstraintUtils.containsCalculatedMember(evaluator.getNonAllMembers(), true)) { return null; } LOGGER.debug("using native filter"); final int savepoint = evaluator.savepoint(); try { overrideContext(evaluator, cjArgs, sql.getStoredMeasure()); // Now construct the TupleConstraint that contains both the CJ // dimensions and the additional filter on them. CrossJoinArg[] combinedArgs = cjArgs; if (allArgs.size() == 2) { CrossJoinArg[] predicateArgs = allArgs.get(1); if (predicateArgs != null) { // Combined the CJ and the additional predicate args. combinedArgs = Util.appendArrays(cjArgs, predicateArgs); } } TupleConstraint constraint = new FilterConstraint(combinedArgs, evaluator, filterExpr); return new SetEvaluator(cjArgs, schemaReader, constraint); } finally { evaluator.restore(savepoint); } }