001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2009-2009 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.olap;
011
012import mondrian.mdx.*;
013import mondrian.olap.fun.Resolver;
014import mondrian.olap.type.Type;
015import mondrian.olap.type.TypeUtil;
016import mondrian.resource.MondrianResource;
017import mondrian.util.ArrayStack;
018
019import java.util.*;
020
021/**
022 * Default implementation of {@link mondrian.olap.Validator}.
023 *
024 * <p>Uses a stack to help us guess the type of our parent expression
025 * before we've completely resolved our children -- necessary,
026 * unfortunately, when figuring out whether the "*" operator denotes
027 * multiplication or crossjoin.
028 *
029 * <p>Keeps track of which nodes have already been resolved, so we don't
030 * try to resolve nodes which have already been resolved. (That would not
031 * be wrong, but can cause resolution to be an <code>O(2^N)</code>
032 * operation.)
033 *
034 * <p>The concrete implementing class needs to implement
035 * {@link #getQuery()} and {@link #defineParameter(Parameter)}.
036 *
037 * @author jhyde
038 */
039abstract class ValidatorImpl implements Validator {
040    protected final ArrayStack<QueryPart> stack = new ArrayStack<QueryPart>();
041    private final FunTable funTable;
042    private final Map<QueryPart, QueryPart> resolvedNodes =
043        new HashMap<QueryPart, QueryPart>();
044    private final QueryPart placeHolder = Literal.zero;
045    private final Map<FunCall, List<String>> scopeExprs =
046        new HashMap<FunCall, List<String>>();
047
048    /**
049     * Creates a ValidatorImpl.
050     *
051     * @param funTable Function table
052     *
053     * @pre funTable != null
054     */
055    protected ValidatorImpl(FunTable funTable) {
056        Util.assertPrecondition(funTable != null, "funTable != null");
057        this.funTable = funTable;
058    }
059
060    public Exp validate(Exp exp, boolean scalar) {
061        Exp resolved;
062        try {
063            resolved = (Exp) resolvedNodes.get(exp);
064        } catch (ClassCastException e) {
065            // A classcast exception will occur if there is a String
066            // placeholder in the map. This is an internal error -- should
067            // not occur for any query, valid or invalid.
068            throw Util.newInternal(
069                e,
070                "Infinite recursion encountered while validating '"
071                + Util.unparse(exp) + "'");
072        }
073        if (resolved == null) {
074            try {
075                stack.push((QueryPart) exp);
076                // To prevent recursion, put in a placeholder while we're
077                // resolving.
078                resolvedNodes.put((QueryPart) exp, placeHolder);
079                resolved = exp.accept(this);
080                Util.assertTrue(resolved != null);
081                resolvedNodes.put((QueryPart) exp, (QueryPart) resolved);
082            } finally {
083                stack.pop();
084            }
085        }
086
087        if (scalar) {
088            final Type type = resolved.getType();
089            if (!TypeUtil.canEvaluate(type)) {
090                String exprString = Util.unparse(resolved);
091                throw MondrianResource.instance().MdxMemberExpIsSet.ex(
092                    exprString);
093            }
094        }
095
096        return resolved;
097    }
098
099    public void validate(ParameterExpr parameterExpr) {
100        ParameterExpr resolved =
101            (ParameterExpr) resolvedNodes.get(parameterExpr);
102        if (resolved != null) {
103            return; // already resolved
104        }
105        try {
106            stack.push(parameterExpr);
107            resolvedNodes.put(parameterExpr, placeHolder);
108            resolved = (ParameterExpr) parameterExpr.accept(this);
109            assert resolved != null;
110            resolvedNodes.put(parameterExpr, resolved);
111        } finally {
112            stack.pop();
113        }
114    }
115
116    public void validate(MemberProperty memberProperty) {
117        MemberProperty resolved =
118            (MemberProperty) resolvedNodes.get(memberProperty);
119        if (resolved != null) {
120            return; // already resolved
121        }
122        try {
123            stack.push(memberProperty);
124            resolvedNodes.put(memberProperty, placeHolder);
125            memberProperty.resolve(this);
126            resolvedNodes.put(memberProperty, memberProperty);
127        } finally {
128            stack.pop();
129        }
130    }
131
132    public void validate(QueryAxis axis) {
133        final QueryAxis resolved = (QueryAxis) resolvedNodes.get(axis);
134        if (resolved != null) {
135            return; // already resolved
136        }
137        try {
138            stack.push(axis);
139            resolvedNodes.put(axis, placeHolder);
140            axis.resolve(this);
141            resolvedNodes.put(axis, axis);
142        } finally {
143            stack.pop();
144        }
145    }
146
147    public void validate(Formula formula) {
148        final Formula resolved = (Formula) resolvedNodes.get(formula);
149        if (resolved != null) {
150            return; // already resolved
151        }
152        try {
153            stack.push(formula);
154            resolvedNodes.put(formula, placeHolder);
155            formula.accept(this);
156            resolvedNodes.put(formula, formula);
157        } finally {
158            stack.pop();
159        }
160    }
161
162    public FunDef getDef(
163        Exp[] args,
164        String funName,
165        Syntax syntax)
166    {
167        // Compute signature first. It makes debugging easier.
168        final String signature =
169            syntax.getSignature(
170                funName, Category.Unknown, ExpBase.getTypes(args));
171
172        // Resolve function by its upper-case name first.  If there is only one
173        // function with that name, stop immediately.  If there is more than
174        // function, use some custom method, which generally involves looking
175        // at the type of one of its arguments.
176        List<Resolver> resolvers = funTable.getResolvers(funName, syntax);
177        assert resolvers != null;
178
179        final List<Resolver.Conversion> conversionList =
180            new ArrayList<Resolver.Conversion>();
181        int minConversionCost = Integer.MAX_VALUE;
182        List<FunDef> matchDefs = new ArrayList<FunDef>();
183        List<Resolver.Conversion> matchConversionList = null;
184        for (Resolver resolver : resolvers) {
185            conversionList.clear();
186            FunDef def = resolver.resolve(args, this, conversionList);
187            if (def != null) {
188                int conversionCost = sumConversionCost(conversionList);
189                if (conversionCost < minConversionCost) {
190                    minConversionCost = conversionCost;
191                    matchDefs.clear();
192                    matchDefs.add(def);
193                    matchConversionList =
194                        new ArrayList<Resolver.Conversion>(conversionList);
195                } else if (conversionCost == minConversionCost) {
196                    matchDefs.add(def);
197                } else {
198                    // ignore this match -- it required more coercions than
199                    // other overloadings we've seen
200                }
201            }
202        }
203        switch (matchDefs.size()) {
204        case 0:
205            throw MondrianResource.instance().NoFunctionMatchesSignature.ex(
206                signature);
207        case 1:
208            break;
209        default:
210            final StringBuilder buf = new StringBuilder();
211            for (FunDef matchDef : matchDefs) {
212                if (buf.length() > 0) {
213                    buf.append(", ");
214                }
215                buf.append(matchDef.getSignature());
216            }
217            throw MondrianResource.instance()
218                .MoreThanOneFunctionMatchesSignature.ex(
219                    signature,
220                    buf.toString());
221        }
222
223        final FunDef matchDef = matchDefs.get(0);
224        for (Resolver.Conversion conversion : matchConversionList) {
225            conversion.checkValid();
226            conversion.apply(this, Arrays.asList(args));
227        }
228
229        return matchDef;
230    }
231
232    public boolean alwaysResolveFunDef() {
233        return false;
234    }
235
236    private int sumConversionCost(
237        List<Resolver.Conversion> conversionList)
238    {
239        int cost = 0;
240        for (Resolver.Conversion conversion : conversionList) {
241            cost += conversion.getCost();
242        }
243        return cost;
244    }
245
246    public boolean canConvert(
247        int ordinal, Exp fromExp, int to, List<Resolver.Conversion> conversions)
248    {
249        return TypeUtil.canConvert(
250            ordinal,
251            fromExp.getType(),
252            to,
253            conversions);
254    }
255
256    public boolean requiresExpression() {
257        return requiresExpression(stack.size() - 1);
258    }
259
260    private boolean requiresExpression(int n) {
261        if (n < 1) {
262            return false;
263        }
264        final Object parent = stack.get(n - 1);
265        if (parent instanceof Formula) {
266            return ((Formula) parent).isMember();
267        } else if (parent instanceof ResolvedFunCall) {
268            final ResolvedFunCall funCall = (ResolvedFunCall) parent;
269            if (funCall.getFunDef().getSyntax() == Syntax.Parentheses) {
270                return requiresExpression(n - 1);
271            } else {
272                int k = whichArg(funCall, (Exp) stack.get(n));
273                if (k < 0) {
274                    // Arguments of call have mutated since call was placed
275                    // on stack. Presumably the call has already been
276                    // resolved correctly, so the answer we give here is
277                    // irrelevant.
278                    return false;
279                }
280                final FunDef funDef = funCall.getFunDef();
281                final int[] parameterTypes = funDef.getParameterCategories();
282                return parameterTypes[k] != Category.Set;
283            }
284        } else if (parent instanceof UnresolvedFunCall) {
285            final UnresolvedFunCall funCall = (UnresolvedFunCall) parent;
286            if (funCall.getSyntax() == Syntax.Parentheses
287                || funCall.getFunName().equals("*"))
288            {
289                return requiresExpression(n - 1);
290            } else {
291                int k = whichArg(funCall, (Exp) stack.get(n));
292                if (k < 0) {
293                    // Arguments of call have mutated since call was placed
294                    // on stack. Presumably the call has already been
295                    // resolved correctly, so the answer we give here is
296                    // irrelevant.
297                    return false;
298                }
299                return requiresExpression(funCall, k);
300            }
301        } else {
302            return false;
303        }
304    }
305
306    /**
307     * Returns whether the <code>k</code>th argument to a function call
308     * has to be an expression.
309     */
310    boolean requiresExpression(
311        UnresolvedFunCall funCall,
312        int k)
313    {
314        // The function call has not been resolved yet. In fact, this method
315        // may have been invoked while resolving the child. Consider this:
316        //   CrossJoin([Measures].[Unit Sales] * [Measures].[Store Sales])
317        //
318        // In order to know whether to resolve '*' to the multiplication
319        // operator (which returns a scalar) or the crossjoin operator
320        // (which returns a set) we have to know what kind of expression is
321        // expected.
322        List<Resolver> resolvers =
323            funTable.getResolvers(
324                funCall.getFunName(),
325                funCall.getSyntax());
326        for (Resolver resolver2 : resolvers) {
327            if (!resolver2.requiresExpression(k)) {
328                // This resolver accepts a set in this argument position,
329                // therefore we don't REQUIRE a scalar expression.
330                return false;
331            }
332        }
333        return true;
334    }
335
336    public FunTable getFunTable() {
337        return funTable;
338    }
339
340    public Parameter createOrLookupParam(
341        boolean definition,
342        String name,
343        Type type,
344        Exp defaultExp,
345        String description)
346    {
347        final SchemaReader schemaReader = getQuery().getSchemaReader(false);
348        Parameter param = schemaReader.getParameter(name);
349
350        if (definition) {
351            if (param != null) {
352                if (param.getScope() == Parameter.Scope.Statement) {
353                    ParameterImpl paramImpl = (ParameterImpl) param;
354                    paramImpl.setDescription(description);
355                    paramImpl.setDefaultExp(defaultExp);
356                    paramImpl.setType(type);
357                }
358                return param;
359            }
360            param = new ParameterImpl(
361                name,
362                defaultExp, description, type);
363
364            // Append it to the list of known parameters.
365            defineParameter(param);
366            return param;
367        } else {
368            if (param != null) {
369                return param;
370            }
371            throw MondrianResource.instance().UnknownParameter.ex(name);
372        }
373    }
374
375    private int whichArg(final FunCall node, final Exp arg) {
376        final Exp[] children = node.getArgs();
377        for (int i = 0; i < children.length; i++) {
378            if (children[i] == arg) {
379                return i;
380            }
381        }
382        return -1;
383    }
384
385    /**
386     * Defines a parameter.
387     *
388     * @param param Parameter
389     */
390    protected abstract void defineParameter(Parameter param);
391}
392
393// End ValidatorImpl.java
394