Brief summary of native eval logic
Mondrian includes functionality which attempts to push down some constraints when evaluating sets in a NON EMPTY context. Native NON EMPTY functionality is governed by the rolap.native.* properties (rolap.native.crossjoin, rolap.native.filter, rolap.native.topcount, rolap.native.nonempty). Note that rolap.native.nonempty property is central. If rolap.native.nonempty is false than all native evaluation is disabled.
What follows is an overview of the code involved in native evaluation.
When the TopCount, Filter, or CrossJoin functions are evaluated, they each first attempt to retrieve a NativeEvaluator via a call to RolapSchemaReader.getNativeSetEvaluator(). RSR in turn uses the RolapNativeRegistry, which first checks whether the specified function maps to a RolapNative implementation it knows about. The class diagram below shows the 3 child implementations currently available in the registry: RolapNativeTopCount, RolapNativeFilter, and RolapNativeCrossJoin.
If there is a matching RolapNative for a given function name, the registry will call .createEvaluator on it, passing the arguments from the function call. createEvaluator() will first determine whether the arguments can be handled. Part of this determination involves passing the argument(s) to CrossJoinArgFactory, which will inspect the Exp objects passed to the function and determine whether they can be translated to a CrossJoinArg implementation (see diagram below).
CrossJoinArgFactories inspection of the expression will result in construction of the corresponding type of CrossJoinArg. For example, MemberListCrossJoinArg will be returned for a enumerated set of members.
Assuming all function arguments result in valid CrossJoinArgs, each NativeEvaluator then performs further validation that the optimization can be used. For example, RolapNativeFilter() verifies no calculated members are in context, and that it can actually generate a filter expression based on the second argument.
If any of the checks up to this point fail and the RolapNative implementation determines it *cannot* handle the expression, the call to .createEvaluator() will return null, which results in Mondrian falling back to non-native evaluation of the function. If it succeeds, however, the NativeEvaluator will then create a TupleConstraint corresponding to it’s function (e.g. FilterContraint for a RolapNativeFilter), and finally create a SetEvaluator initialized with that constraint.
Two of the most important constraint classes for native evaluation are SqlContextConstraint, and SetConstraint. SetConstraint will use the CrossJoinArgs it was initialized with to form an appropriate WHERE clause. So for example, in a set that does a NonEmptyCrossJoin( Time.[Year].children, Gender.Female ), the SQL query issued when evaluating the tuples for that set will use a WHERE clause with "customer.gender = 'Female'". Similarly, SqlContextConstraint will apply the constraint from the MDX WHERE clause when issuing SQL queries for the set.
The native evaluation of TopCount and Filter are more complex. Both need to translate an MDX function arg to a SQL expression. E.g. (Measures.[Unit Sales] > 50) needs to be translated to “(sum(sales_fact_1997.unit_sales) > 50)”. The XConstraint object will generate the SQL expression via RolapNativeSql, which has expression compilers for booleans (filter) and numerics (topbottomcount). In some cases the expression may simply not be handled, in which case Mondrian needs to fall back to non-native evaluation.