[Mondrian] Re: Eigenbase perforce change 11758 for review

Julian Hyde jhyde at pentaho.com
Sat Oct 18 19:22:44 EDT 2008


Khanh,

I presume that you decided to call it OrderSet because it was not
possible to make it a superset of the Order function. Can you confirm
that and elaborate? (There may have been an email describing these
problems that I missed - if so I apologise if I am asking you to repeat
- but I am on vacation, have lost some email and am hugely behind on the
rest.)

I see a few worrying signs that the code path has become more complex -
a weakening of the map type from Map<Member,Object> to
Map<Object,Object>, and more 'if' statements and '?' expressions in
inner code.

I wondered whether this functionality would be possible using CHAINING
comparator. If we made the current set of comparators chaining, if the
comparison evaluated to 0, then they would look to see whether they had
a successor comparator, and if so, return that comparator's result
instead. The recursive call would not be worrisome because it is not
very deep (maximum depth the number of order keys) and would not happen
very often (if you are ordering by x,y,z most comparisons are settled
using x).

Was that an approach you considered, and do you think it would make the
code faster & more performant?

I know Marc is working on the Order function also, so I have asked him
to review your change. He will ask you to integrate to main if he feels
that your work would conflict with his.

Other than those points, it looks like a job well done. Especially the
comprehensive set of unit tests.

Julian

On Fri, 2008-10-17 at 18:32 -0700, Khanh Vu wrote:

> http://p4web.eigenbase.org/@md=d&c=6PU@//11758?ac=10
> 
> Change 11758 by kvu at kvu.shada.eigenbase on 2008/10/17 18:31:56
> 
> 	MONDRIAN: implements OrderSet() function (an extension of Order())
> 
> Affected files ...
> 
> ... //open/lu/release/mondrian/wm/src/main/mondrian/olap/fun/BuiltinFunTable.java#2 edit
> ... //open/lu/release/mondrian/wm/src/main/mondrian/olap/fun/FunUtil.java#2 edit
> ... //open/lu/release/mondrian/wm/src/main/mondrian/olap/fun/OrderSetFunDef.java#1 add
> ... //open/lu/release/mondrian/wm/testsrc/main/mondrian/olap/fun/FunctionTest.java#2 edit
> ... //open/lu/release/mondrian/wm/testsrc/main/mondrian/test/Main.java#2 edit
> ... //open/lu/release/mondrian/wm/testsrc/main/mondrian/test/clearview/OrderSetTest.java#1 add
> ... //open/lu/release/mondrian/wm/testsrc/main/mondrian/test/clearview/OrderSetTest.ref.xml#1 add
> ... //open/lu/release/mondrian/wm/testsrc/main/mondrian/xmla/XmlaBasicTest.ref.xml#2 edit
> 
> Differences ...
> 
> ==== //open/lu/release/mondrian/wm/src/main/mondrian/olap/fun/BuiltinFunTable.java#2 (ktext) ====
> 
> 2c2
> < // $Id: //open/lu/release/mondrian/wm/src/main/mondrian/olap/fun/BuiltinFunTable.java#1 $
> ---
> > // $Id: //open/lu/release/mondrian/wm/src/main/mondrian/olap/fun/BuiltinFunTable.java#2 $
> 42c42
> <  * @version $Id: //open/lu/release/mondrian/wm/src/main/mondrian/olap/fun/BuiltinFunTable.java#1 $
> ---
> >  * @version $Id: //open/lu/release/mondrian/wm/src/main/mondrian/olap/fun/BuiltinFunTable.java#2 $
> 1090a1091
> >         define(OrderSetFunDef.Resolver);
> 
> ==== //open/lu/release/mondrian/wm/src/main/mondrian/olap/fun/FunUtil.java#2 (ktext) ====
> 
> 2c2
> < // $Id: //open/lu/release/mondrian/wm/src/main/mondrian/olap/fun/FunUtil.java#1 $
> ---
> > // $Id: //open/lu/release/mondrian/wm/src/main/mondrian/olap/fun/FunUtil.java#2 $
> 20a21,22
> > import mondrian.calc.MemberCalc;
> > import mondrian.calc.impl.*;
> 36c38
> <  * @version $Id: //open/lu/release/mondrian/wm/src/main/mondrian/olap/fun/FunUtil.java#1 $
> ---
> >  * @version $Id: //open/lu/release/mondrian/wm/src/main/mondrian/olap/fun/FunUtil.java#2 $
> 256a259
> >         ;
> 328c331
> <     static Map<Member, Object> evaluateMembers(
> ---
> >     static Map<Object, Object> evaluateMembers(
> 338c341
> <         Map<Member, Object> mapMemberToValue = new HashMap<Member, Object>();
> ---
> >         Map<Object, Object> mapMemberToValue = new HashMap<Object, Object>();
> 420a424,611
> >      * For each tuple/member in a list, evaluates the member value expression
> >      * and populates a map from tuples/members to members
> >      *
> >      * @param <T> Generic type, can take a member or a tuple
> >      * @param evaluator Evaluation context
> >      * @param exp Expression to evaluate
> >      * @param members List of members
> >      * @param parentsToo If true, evaluate the expression for all ancestors
> >      *            of the members as well
> >      * @return the map from members or tuples to members
> >      */
> >     static <T> Map<Object, Object> evaluateMemberValueExps(
> >         Evaluator evaluator,
> >         Calc exp,
> >         List<T> members,
> >         boolean parentsToo)
> >     {
> >         MemberCalc mcalc = (MemberCalc) exp;
> >         Map<Object, Object> mapMemberToMember = new HashMap<Object, Object>();
> > 
> >         // try to optimize when exp is currentMember
> >         boolean isCurrMemExp = mcalc instanceof DimensionCurrentMemberFunDef.CalcImpl;
> >         int idx = -1;
> >         if (isCurrMemExp) {
> >             Object first = members.get(0);
> >             if (first instanceof Member[]) {
> >                 Member[] firstMem = (Member[]) first;
> >                 int n = firstMem.length;
> >                 Dimension dim = mcalc.getType().getDimension();
> >                 for (int i = 0; i < n; i++) {
> >                     if (dim.equals(firstMem[i].getDimension())) {
> >                         idx = i;
> >                         break;
> >                     }
> >                 }
> >                 assert idx != -1;
> >             }
> >         }
> >         // populate the map
> >         Object obj;
> >         Member result;
> >         for (int i = 0, n = members.size(); i < n; i++) {
> >             obj = members.get(i);
> >             if (obj instanceof Member) {
> >                 while (true) {
> >                     if (isCurrMemExp) {
> >                         mapMemberToMember.put(obj, obj);
> >                     } else {
> >                         evaluator.setContext((Member) obj);
> >                         result = mcalc.evaluateMember(evaluator);
> >                         if (result == null) {
> >                             result = NullMember;
> >                         }
> >                         mapMemberToMember.put(obj, result);
> >                     }
> >                     if (!parentsToo) {
> >                         break;
> >                     }
> >                     obj = ((Member) obj).getParentMember();
> >                     if (obj == null) {
> >                         break;
> >                     }
> >                     if (mapMemberToMember.containsKey((Member) obj)) {
> >                         break;
> >                     }
> >                 }
> >             } else if (obj instanceof Member[]) {
> >                 evaluator.setContext((Member[]) obj);
> >                 if (isCurrMemExp) {
> >                     mapMemberToMember.put(
> >                         new ArrayHolder<Member>((Member[]) obj), ((Member[]) obj)[idx]);
> >                 } else {
> >                     // populate tuples map
> >                     result = mcalc.evaluateMember(evaluator);
> >                     if (result == null) {
> >                         result = NullMember;
> >                     }
> >                     mapMemberToMember.put(
> >                         new ArrayHolder<Member>((Member[]) obj), result);
> >                 }
> >             }
> >         }
> >         return mapMemberToMember;
> >     }
> > 
> >     /**
> >      * Populates the list of maps from tuples/members to values/members.
> >      * Each member of the list corresponds to a sort key. Since it is
> >      * unlikely that all sort keys will be used to compare any pair
> >      * of members, prepopulate the first half only
> >      *
> >      * @param <T> Generic type; can be member or tuple
> >      * @param listMapMemberToValue List of maps
> >      * @param evaluator Evaluation context
> >      * @param members List of members
> >      * @param keySpecList List of sort key specifications
> >      */
> >     static <T> void populateMembersMap(
> >         List<Map<Object, Object>> listMapMemberToValue,
> >         Evaluator evaluator,
> >         List<T> members,
> >         List<SortKeySpec> keySpecList)
> >     {
> >         final int keyCount = keySpecList.size();
> >         // magic number - pre-populate the map
> >         // for one plus the first half of sort keys
> >         final int depth = keyCount / 2 + 1;
> >         Object first = members.get(0);
> >         for (int i = 0; i < depth; i++) {
> >             SortKeySpec sortKey = keySpecList.get(i);
> >             if (sortKey.isMemberValueExp()) {
> >                 // get member
> >                 listMapMemberToValue.add(
> >                     evaluateMemberValueExps(
> >                         evaluator,
> >                         sortKey.getKey(),
> >                         members,
> >                         !sortKey.getDirection().brk));
> >             } else {
> >                 // get value
> >                 if (first instanceof Member) {
> >                     listMapMemberToValue.add(
> >                         evaluateMembers(
> >                             evaluator,
> >                             sortKey.getKey(),
> >                             (List<Member>) members,
> >                             !sortKey.getDirection().brk));
> >                 } else {
> >                     // populate map for tuples only
> >                     // meaning optimize for break hierarchy comparison only
> >                     listMapMemberToValue.add(
> >                         evaluateTuples(
> >                             evaluator,
> >                             sortKey.getKey(),
> >                             (List<Member[]>) members));
> >                 }
> >             }
> >         }
> >         for (int i = depth; i < keyCount; i++) {
> >             listMapMemberToValue.add(new HashMap<Object, Object>());
> >         }
> >     }
> > 
> >     /**
> >      * Gets the value from the map or evaluates the member using the expression
> >      * if the value is not ready
> >      *
> >      * @param member Key of the map
> >      * @param exp Expression to evaluate
> >      * @param evaluator Evaluation context
> >      * @param mapTuples Map of members or tuples to members or cell values
> >      * @return Value from the map
> >      */
> >     static Object getFromMapOrEvaluate(
> >         Object member,
> >         Calc exp,
> >         Evaluator evaluator,
> >         boolean isMemValExp,
> >         Map<Object, Object> mapTuples)
> >     {
> >         Object key;
> >         boolean isTuple = (member instanceof Member[]);
> >         if (isTuple) {
> >             key = new ArrayHolder<Member>((Member[]) member);
> >         } else {
> >             key = member;
> >         }
> >         Object val = mapTuples.get(key);
> >         if (val == null) {
> >             if (isTuple) {
> >                 evaluator.setContext((Member[]) member);
> >             } else {
> >                 evaluator.setContext((Member) member);
> >             }
> >             val = exp.evaluate(evaluator);
> >             if (val == null) {
> >                 if (isMemValExp) {
> >                     val = NullMember;
> >                 } else {
> >                     val = Util.nullValue;
> >                 }
> >             }
> >             mapTuples.put(key, val);
> >         }
> >         return val;
> >     }
> > 
> >     /**
> 436c627
> <         Map<Member, Object> mapMemberToValue;
> ---
> >         Map<Object, Object> mapMemberToValue;
> 483a675,707
> >      * Helper function to sortMembers a list of members according to a list
> >      * of expressions and a list of sorting flags.
> >      *
> >      * <p>NOTE: This function does not preserve the contents of the validator.
> >      */
> >     public static void sortMembers(
> >         Evaluator evaluator, List<Member> members, List<SortKeySpec> keySpecList)
> >     {
> >         if (members.isEmpty()) {
> >             return;
> >         }
> >         Object first = members.get(0);
> >         List<Map<Object, Object>> listMapMemberToValue =
> >             new ArrayList<Map<Object, Object>>();
> >         populateMembersMap(
> >             listMapMemberToValue, evaluator, members, keySpecList);
> >         if (first instanceof Member) {
> >             Comparator<Member> comparator =
> >                 new MultiKeysMemberComparator(
> >                     evaluator, keySpecList, listMapMemberToValue).wrap();
> >             Collections.sort(members, comparator);
> >         } else {
> >             Util.assertTrue(first instanceof Member[]);
> >             List<Member[]> tupleList = Util.cast(members);
> >             final int arity = ((Member[]) first).length;
> >             Comparator<Member[]> comparator =
> >                 new MultiKeysArrayComparator(
> >                     evaluator, keySpecList, listMapMemberToValue, arity).wrap();
> >             Collections.sort(tupleList, comparator);
> >         }
> >     }
> > 
> >     /**
> 1633a1858,1948
> >     public static int compareMembersByOrderKeys(
> >         Member m1, Member m2)
> >     {
> >         // calculated members collate after non-calculated
> >         final boolean calculated1 = m1.isCalculatedInQuery();
> >         final boolean calculated2 = m2.isCalculatedInQuery();
> >         if (calculated1) {
> >             if (!calculated2) {
> >                 return 1;
> >             }
> >         } else {
> >             if (calculated2) {
> >                 return -1;
> >             }
> >         }
> >         final Comparable k1 = m1.getOrderKey();
> >         final Comparable k2 = m2.getOrderKey();
> >         if ((k1 != null) && (k2 != null)) {
> >             return k1.compareTo(k2);
> >         } else {
> >             return m1.compareTo(m2);
> >         }
> >     }
> > 
> >     public static int compareMembers(
> >         Member m1,
> >         Member m2,
> >         Evaluator evaluator,
> >         Calc exp,
> >         boolean isMemValExp,
> >         Map<Object, Object> mapMemberToValue)
> >     {
> >         if (isMemValExp) {
> >             return FunUtil.compareMembersByOrderKeys(m1, m2);
> >         } else {
> >             Member old = evaluator.setContext(m1);
> >             Object v1 = getFromMapOrEvaluate(
> >                 m1, exp, evaluator, false, mapMemberToValue);
> >             Object v2 = getFromMapOrEvaluate(
> >                 m2, exp, evaluator, false, mapMemberToValue);
> >             // important to restore the evaluator state -- and this is faster
> >             // than calling evaluator.push()
> >             evaluator.setContext(old);
> >             return FunUtil.compareValues(v1, v2);
> >         }
> >     }
> > 
> >     public static int compareMembersHierarchically(
> >         Member m1,
> >         Member m2,
> >         Evaluator evaluator,
> >         Calc exp,
> >         boolean desc,
> >         boolean isMemValExp,
> >         Map<Object, Object> mapMemberToValue)
> >     {
> >         if (FunUtil.equals(m1, m2)) {
> >             return 0;
> >         }
> >         while (true) {
> >             int depth1 = m1.getDepth(),
> >                     depth2 = m2.getDepth();
> >             if (depth1 < depth2) {
> >                 m2 = m2.getParentMember();
> >                 if (FunUtil.equals(m1, m2)) {
> >                     return -1;
> >                 }
> >             } else if (depth1 > depth2) {
> >                 m1 = m1.getParentMember();
> >                 if (FunUtil.equals(m1, m2)) {
> >                     return 1;
> >                 }
> >             } else {
> >                 Member prev1 = m1, prev2 = m2;
> >                 m1 = m1.getParentMember();
> >                 m2 = m2.getParentMember();
> >                 if (FunUtil.equals(m1, m2)) {
> >                     // including case where both parents are null
> >                     int c = FunUtil.compareMembers(
> >                         prev1, prev2, evaluator, exp, isMemValExp, mapMemberToValue);
> >                     // skip the below compare if isMemValExp is true
> >                     if ((c == 0) && !(isMemValExp)) {
> >                         c = FunUtil.compareMembersByOrderKeys(
> >                             prev1, prev2);
> >                     }
> >                     return desc ? -c : c;
> >                 }
> >             }
> >         }
> >     }
> > 
> 1829c2144
> <         List <Member> memberList =
> ---
> >         List<Member> memberList =
> 1880c2195
> <         Map<Member, Object> mapMemberToValue;
> ---
> >         Map<Object, Object> mapMemberToValue;
> 1881a2197
> >         Evaluator evaluator;
> 1883c2199
> <         MemberComparator(Map<Member, Object> mapMemberToValue, boolean desc) {
> ---
> >         MemberComparator(Map<Object, Object> mapMemberToValue, boolean desc) {
> 1887a2204,2210
> >         MemberComparator(
> >             Evaluator evaluator,
> >             List<SortKeySpec> keySpecList)
> >         {
> >             this.evaluator = evaluator;
> >         }
> > 
> 1890c2213
> <             if (LOGGER.isDebugEnabled()) {
> ---
> >             if (LOGGER.isDebugEnabled() && (mapMemberToValue != null)) {
> 1961c2284
> <             Map<Member, Object> mapMemberToValue, boolean desc)
> ---
> >             Map<Object, Object> mapMemberToValue, boolean desc)
> 1972c2295
> <         BreakMemberComparator(Map<Member, Object> mapMemberToValue, boolean desc) {
> ---
> >         BreakMemberComparator(Map<Object, Object> mapMemberToValue, boolean desc) {
> 1980a2304,2365
> >     private static class MultiKeysMemberComparator extends MemberComparator
> >     {
> >         List<SortKeySpec> keySpecList;
> >         List<Map<Object, Object>> listMapMemberToValue;
> >         int keySpecCount;
> > 
> >         MultiKeysMemberComparator(
> >             Evaluator evaluator,
> >             List<SortKeySpec> keySpecList,
> >             List<Map<Object, Object>> listMapMemberToValue)
> >         {
> >             super(evaluator, keySpecList);
> >             this.keySpecList = keySpecList;
> >             this.listMapMemberToValue = listMapMemberToValue;
> >             this.keySpecCount = keySpecList.size();
> >         }
> > 
> >         public final int compare(Member m1, Member m2)
> >         {
> >             int c = 0;
> >             for (int i = 0; i < keySpecCount; i++) {
> >                 SortKeySpec sortKey = keySpecList.get(i);
> >                 Calc exp = sortKey.key;
> >                 Flag flag = sortKey.direction;
> >                 boolean isMemValExp = sortKey.isMemberValExp;
> >                 Map<Object, Object> mapMemberToValue =
> >                     listMapMemberToValue.get(i);
> >                 //this.evaluator.push(); //check if this is needed
> >                 Member tm1 = null, tm2 = null;
> >                 if (isMemValExp) {
> >                     tm1 = (Member) getFromMapOrEvaluate(
> >                         m1, exp, evaluator, true, mapMemberToValue);
> >                     tm2 = (Member) getFromMapOrEvaluate(
> >                         m2, exp, evaluator, true, mapMemberToValue);
> >                 }
> >                 if (flag.brk) {
> >                     int c1 = compareMembers(
> >                         isMemValExp ? tm1 : m1,
> >                         isMemValExp ? tm2 : m2,
> >                         evaluator,
> >                         exp,
> >                         isMemValExp,
> >                         mapMemberToValue);
> >                     c = flag.descending ? -c1 : c1;
> >                 } else {
> >                     c = compareMembersHierarchically(
> >                         isMemValExp ? tm1 : m1,
> >                         isMemValExp ? tm2 : m2,
> >                         evaluator,
> >                         exp,
> >                         flag.descending,
> >                         isMemValExp,
> >                         mapMemberToValue);
> >                 }
> >                 if (c != 0) {
> >                     return c;
> >                 }
> >             }
> >             return c;
> >         }
> >     }
> > 
> 2044c2429
> <         final Calc calc;
> ---
> >         Calc calc;
> 2050a2436,2442
> > 
> >         ArrayExpComparator(
> >             Evaluator evaluator,
> >             int arity) {
> >             super(arity);
> >             this.evaluator = evaluator;
> >         }
> 2142a2535,2640
> >     private static class MultiKeysArrayComparator
> >         extends ArrayExpComparator
> >     {
> >         List<SortKeySpec> keySpecList;
> >         List<Map<Object, Object>> listMapMemberToValue;
> >         int keySpecCount;
> > 
> >         MultiKeysArrayComparator(
> >             Evaluator evaluator,
> >             List<SortKeySpec> keySpecList,
> >             List<Map<Object, Object>> listMapMemberToValue,
> >             int arity)
> >         {
> >             super(evaluator, arity);
> >             this.keySpecList = keySpecList;
> >             this.keySpecCount = keySpecList.size();
> >             this.listMapMemberToValue = listMapMemberToValue;
> >         }
> > 
> >         public int compare(Member[] a1, Member[] a2) {
> >             int c = 0;
> >             for (int i = 0; i < keySpecCount; i++) {
> >                 SortKeySpec sortKey = keySpecList.get(i);
> >                 if (sortKey.direction.brk) {
> >                     c = compareBreakHierarchy(
> >                         a1,
> >                         a2,
> >                         sortKey.key,
> >                         sortKey.direction.descending,
> >                         sortKey.isMemberValExp,
> >                         listMapMemberToValue.get(i));
> >                 } else {
> >                     c = compareHierarchically(
> >                         a1,
> >                         a2,
> >                         sortKey.key,
> >                         sortKey.direction.descending,
> >                         sortKey.isMemberValExp,
> >                         listMapMemberToValue.get(i));
> >                 }
> >                 if (c != 0) {
> >                     return c;
> >                 }
> >             }
> >             return c;
> >         }
> > 
> >         public int compareBreakHierarchy(
> >             Member[] a1,
> >             Member[] a2,
> >             Calc exp,
> >             boolean desc,
> >             boolean isMemValExp,
> >             Map<Object, Object> mapTupleToValue)
> >         {
> >             if (isMemValExp) {
> >                 Member m1 = (Member) getFromMapOrEvaluate(
> >                     a1, exp, evaluator, true, mapTupleToValue);
> >                 Member m2 = (Member) getFromMapOrEvaluate(
> >                     a2, exp, evaluator, true, mapTupleToValue);
> >                 return desc ?
> >                     - compareMembersByOrderKeys(m1, m2) :
> >                     compareMembersByOrderKeys(m1, m2);
> >             } else {
> >                 Object v1 = getFromMapOrEvaluate(
> >                     a1, exp, evaluator, false, mapTupleToValue);
> >                 Object v2 = getFromMapOrEvaluate(
> >                     a2, exp, evaluator, false, mapTupleToValue);
> >                 return desc ?
> >                     - compareValues(v1, v2) :
> >                     compareValues(v1, v2);
> >             }
> >         }
> > 
> >         public int compareHierarchically(
> >             Member[] a1,
> >             Member[] a2,
> >             Calc exp,
> >             boolean desc,
> >             boolean isMemValExp,
> >             Map<Object, Object> mapTupleToValue)
> >         {
> >             int c = 0;
> >             if (isMemValExp) {
> >                 Member m1 = (Member) getFromMapOrEvaluate(
> >                     a1, exp, evaluator, true, mapTupleToValue);
> >                 Member m2 = (Member) getFromMapOrEvaluate(
> >                     a2, exp, evaluator, true, mapTupleToValue);
> >                 c = compareMembersHierarchically(
> >                     m1, m2, evaluator, exp, desc, isMemValExp, mapTupleToValue);
> >             } else {
> >                 for (int i = 0; i < arity; i++) {
> >                     Member m1 = a1[i],
> >                             m2 = a2[i];
> >                     c = compareMembersHierarchically(
> >                         m1, m2, evaluator, exp, desc, isMemValExp, mapTupleToValue);
> >                     if (c != 0) {
> >                         break;
> >                     }
> >                     evaluator.setContext(m1);
> >                 }
> >             }
> >             return c;
> >         }
> >     }
> > 
> 2386a2885,2946
> > 
> >     /**
> >      * Enumeration of the flags allowed to the <code>ORDER</code> MDX function.
> >      */
> >     enum Flag {
> >         ASC(false, false),
> >         DESC(true, false),
> >         BASC(false, true),
> >         BDESC(true, true);
> > 
> >         final boolean descending;
> >         final boolean brk;
> > 
> >         Flag(boolean descending, boolean brk) {
> >             this.descending = descending;
> >             this.brk = brk;
> >         }
> > 
> >         public static String[] getNames() {
> >             List<String> names = new ArrayList<String>();
> >             for (Flag flags : Flag.class.getEnumConstants()) {
> >                 names.add(flags.name());
> >             }
> >             return names.toArray(new String[names.size()]);
> >         }
> >     }
> > 
> >     class SortKeySpec {
> >         private Calc key;
> >         private boolean isMemberValExp = true;
> >         private Flag direction;
> > 
> >         SortKeySpec(Calc key, boolean memberValExp, Flag dir) {
> >             this.key = key;
> >             this.isMemberValExp = memberValExp;
> >             this.direction = dir;
> >         }
> > 
> >         Calc getKey() {
> >             return this.key;
> >         }
> > 
> >         boolean isMemberValueExp() {
> >             return this.isMemberValExp;
> >         }
> > 
> >         Flag getDirection() {
> >             return this.direction;
> >         }
> > 
> >         void setKey(Calc key) {
> >             this.key = key;
> >         }
> > 
> >         void setMemberValueExp(boolean m) {
> >             this.isMemberValExp = m;
> >         }
> > 
> >         void setDirection(Flag dir) {
> >             this.direction = dir;
> >         }
> >     }
> 
> ==== //open/lu/release/mondrian/wm/testsrc/main/mondrian/olap/fun/FunctionTest.java#2 (ktext) ====
> 
> 2c2
> < // $Id: //open/lu/release/mondrian/wm/testsrc/main/mondrian/olap/fun/FunctionTest.java#1 $
> ---
> > // $Id: //open/lu/release/mondrian/wm/testsrc/main/mondrian/olap/fun/FunctionTest.java#2 $
> 34c34
> <  * @version $Id: //open/lu/release/mondrian/wm/testsrc/main/mondrian/olap/fun/FunctionTest.java#1 $
> ---
> >  * @version $Id: //open/lu/release/mondrian/wm/testsrc/main/mondrian/olap/fun/FunctionTest.java#2 $
> 37a38,39
> >     private MondrianProperties props;
> > 
> 161a164
> >         props = MondrianProperties.instance();
> 165a169
> >         props = MondrianProperties.instance();
> 5962a5967,6414
> >     public void testOrderSetEmpty() {
> >         String query =
> >             "select \n" +
> >             "  OrderSet(" +
> >             "    {}," +
> >             "    [Customers].currentMember, BDESC) \n" +
> >             "on 0 from [Sales]";
> > 
> >         assertQueryReturns(query,
> >             fold(
> >                 "Axis #0:\n" +
> >                 "{}\n" +
> >                 "Axis #1:\n"));
> >     }
> > 
> >     public void testOrderOne() {
> >         String query =
> >             "select \n" +
> >             "  OrderSet(" +
> >             "    {[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}," +
> >             "    [Customers].currentMember, BDESC) \n" +
> >             "on 0 from [Sales]";
> > 
> >         assertQueryReturns(query,
> >             fold(
> >                 "Axis #0:\n" +
> >                 "{}\n" +
> >                 "Axis #1:\n" +
> >                 "{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n" +
> >                 "Row #0: 75\n"));
> >     }
> > 
> >     public void testOrderSetMemberMemberValueExpNew() {
> >         String query =
> >             "select \n" +
> >             "  OrderSet(" +
> >             "    {[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}," +
> >             "    [Customers].currentMember, BDESC) \n" +
> >             "on 0 from [Sales]";
> > 
> >         boolean saved = props.CompareSiblingsByOrderKey.get();
> >         props.CompareSiblingsByOrderKey.set(true);
> >         Connection conn = null;
> >         try {
> >             // Use a fresh connection to make sure bad member ordinals haven't
> >             // been assigned by previous tests.
> >             conn = getTestContext().getFoodMartConnection(false);
> >             TestContext context = getTestContext(conn);
> >             context.assertQueryReturns(query,
> >                 fold(
> >                     "Axis #0:\n" +
> >                     "{}\n" +
> >                     "Axis #1:\n" +
> >                     "{[Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n" +
> >                     "{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n" +
> >                     "Row #0: 33\n" +
> >                     "Row #0: 75\n"));
> >         } finally {
> >             props.CompareSiblingsByOrderKey.set(saved);
> >             if (conn != null) {
> >                 conn.close();
> >             }
> >         }
> >     }
> > 
> >     public void testOrderSetMemberDefaultFlag1() {
> >         // flags not specified default to ASC
> >         String query =
> >             "with \n" +
> >             "  Member [Measures].[Zero] as '0' \n" +
> >             "select \n" +
> >             "  OrderSet(" +
> >             "    {[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}," +
> >             "    [Customers].currentMember) \n" +
> >             "on 0 from [Sales]";
> >         assertQueryReturns(query,
> >             fold(
> >                 "Axis #0:\n" +
> >                 "{}\n" +
> >                 "Axis #1:\n" +
> >                 "{[Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n" +
> >                 "{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n" +
> >                 "Row #0: 33\n" +
> >                 "Row #0: 75\n"));
> >     }
> > 
> >     public void testOrderSetMemberDefaultFlag2() {
> >         // flags not specified default to ASC
> >         String query =
> >             "with \n" +
> >             "  Member [Measures].[Zero] as '0' \n" +
> >             "select \n" +
> >             "  OrderSet(" +
> >             "    {[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}," +
> >             "    [Measures].[Store Cost]) \n" +
> >             "on 0 from [Sales]";
> >         assertQueryReturns(query,
> >             fold(
> >                 "Axis #0:\n" +
> >                 "{}\n" +
> >                 "Axis #1:\n" +
> >                 "{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n" +
> >                 "{[Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n" +
> >                 "Row #0: 75\n" +
> >                 "Row #0: 33\n"));
> >     }
> > 
> >     public void testOrderSetMemberMemberValueExpHierarchy() {
> >         // Santa Monica and Woodland Hills both don't have orderkey
> >         // members are sorted by the order of their keys
> >         String query =
> >             "select \n" +
> >             "  OrderSet(" +
> >             "    {[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}," +
> >             "    [Customers].currentMember, DESC) \n" +
> >             "on 0 from [Sales]";
> >         assertQueryReturns(query,
> >             fold(
> >                 "Axis #0:\n" +
> >                 "{}\n" +
> >                 "Axis #1:\n" +
> >                 "{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n" +
> >                 "{[Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n" +
> >                 "Row #0: 75\n" +
> >                 "Row #0: 33\n"));
> >     }
> > 
> >     public void testOrderSetMemberMultiKeysMemberValueExp1() {
> >         String query =
> >             "select \n" +
> >             "  OrderSet(" +
> >             "    {[Customers].[USA].[WA].[Issaquah].[Abe Tramel]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}," +
> >             "    [Measures].[Unit Sales], BDESC, [Customers].currentMember, BDESC) \n" +
> >             "on 0 from [Sales]";
> >         assertQueryReturns(query,
> >             fold(
> >                 "Axis #0:\n" +
> >                 "{}\n" +
> >                 "Axis #1:\n" +
> >                 "{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n" +
> >                 "{[Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n" +
> >                 "{[Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n" +
> >                 "Row #0: 75\n" +
> >                 "Row #0: 33\n" +
> >                 "Row #0: 33\n"));
> >     }
> > 
> >     public void testOrderSetMemberMultiKeysMemberValueExp2() {
> >         String query =
> >             "select \n" +
> >             "  OrderSet(" +
> >             "    {[Customers].[USA].[WA].[Issaquah].[Abe Tramel]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}," +
> >             "    [Customers].currentMember.Parent.Parent, BASC, [Customers].currentMember, BDESC) \n" +
> >             "on 0 from [Sales]";
> > 
> >         boolean saved = props.CompareSiblingsByOrderKey.get();
> >         props.CompareSiblingsByOrderKey.set(true);
> >         Connection conn = null;
> >         try {
> >             // Use a fresh connection to make sure bad member ordinals haven't
> >             // been assigned by previous tests.
> >             conn = getTestContext().getFoodMartConnection(false);
> >             TestContext context = getTestContext(conn);
> >             context.assertQueryReturns(query,
> >                 fold(
> >                     "Axis #0:\n" +
> >                     "{}\n" +
> >                     "Axis #1:\n" +
> >                     "{[Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n" +
> >                     "{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n" +
> >                     "{[Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n" +
> >                     "Row #0: 33\n" +
> >                     "Row #0: 75\n" +
> >                     "Row #0: 33\n"));
> >         } finally {
> >             props.CompareSiblingsByOrderKey.set(saved);
> >             if (conn != null) {
> >                 conn.close();
> >             }
> >         }
> >     }
> > 
> >     public void testOrderSetMemberMultiKeysMemberValueExpDepends() {
> >         // should preserve order of Abe and Adeline (note second key is [Time])
> >         String query =
> >             "select \n" +
> >             "  OrderSet(" +
> >             "    {[Customers].[USA].[WA].[Issaquah].[Abe Tramel]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}," +
> >             "    [Measures].[Unit Sales], BDESC, [Time].currentMember, BDESC) \n" +
> >             "on 0 from [Sales]";
> >         assertQueryReturns(query,
> >             fold(
> >                 "Axis #0:\n" +
> >                 "{}\n" +
> >                 "Axis #1:\n" +
> >                 "{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n" +
> >                 "{[Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n" +
> >                 "{[Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n" +
> >                 "Row #0: 75\n" +
> >                 "Row #0: 33\n" +
> >                 "Row #0: 33\n"));
> >     }
> > 
> >     public void testOrderSetTupleSingleKeysNew() {
> >         String query =
> >             "with \n" +
> >             "  set [NECJ] as \n" +
> >             "    'NonEmptyCrossJoin( \n" +
> >             "    {[Customers].[USA].[WA].[Issaquah].[Abe Tramel]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}," +
> >             "    {[Store].[USA].[WA].[Seattle],\n" +
> >             "     [Store].[USA].[CA],\n" +
> >             "     [Store].[USA].[OR]})'\n" +
> >             "select \n" +
> >             " OrderSet([NECJ], [Customers].currentMember, BDESC) \n" +
> >             "on 0 from [Sales]";
> > 
> >         boolean saved = props.CompareSiblingsByOrderKey.get();
> >         props.CompareSiblingsByOrderKey.set(true);
> >         Connection conn = null;
> >         try {
> >             // Use a fresh connection to make sure bad member ordinals haven't
> >             // been assigned by previous tests.
> >             conn = getTestContext().getFoodMartConnection(false);
> >             TestContext context = getTestContext(conn);
> >             context.assertQueryReturns(query,
> >                 fold(
> >                     "Axis #0:\n" +
> >                     "{}\n" +
> >                     "Axis #1:\n" +
> >                     "{[Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun], [Store].[All Stores].[USA].[CA]}\n" +
> >                     "{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young], [Store].[All Stores].[USA].[CA]}\n" +
> >                     "{[Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel], [Store].[All Stores].[USA].[WA].[Seattle]}\n" +
> >                     "Row #0: 33\n" +
> >                     "Row #0: 75\n" +
> >                     "Row #0: 33\n"));
> >         } finally {
> >             props.CompareSiblingsByOrderKey.set(saved);
> >             if (conn != null) {
> >                 conn.close();
> >             }
> >         }
> >     }
> > 
> >     public void testOrderSetTupleMultiKeys1() {
> >         String query =
> >             "with \n" +
> >             "  set [NECJ] as \n" +
> >             "    'NonEmptyCrossJoin( \n" +
> >             "    {[Store].[USA].[CA],\n" +
> >             "     [Store].[USA].[WA]},\n" +
> >             "    {[Customers].[USA].[WA].[Issaquah].[Abe Tramel]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]})' \n" +
> >             "select \n" +
> >             " OrderSet([NECJ], [Store].currentMember, BDESC, [Measures].[Unit Sales], BDESC) \n" +
> >             "on 0 from [Sales]";
> >         assertQueryReturns(query,
> >             fold(
> >                 "Axis #0:\n" +
> >                 "{}\n" +
> >                 "Axis #1:\n" +
> >                 "{[Store].[All Stores].[USA].[WA], [Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n" +
> >                 "{[Store].[All Stores].[USA].[CA], [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n" +
> >                 "{[Store].[All Stores].[USA].[CA], [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n" +
> >                 "Row #0: 33\n" +
> >                 "Row #0: 75\n" +
> >                 "Row #0: 33\n"));
> >     }
> > 
> >     public void testOrderSetTupleMultiKeys2() {
> >         String query =
> >             "with \n" +
> >             "  set [NECJ] as \n" +
> >             "    'NonEmptyCrossJoin( \n" +
> >             "    {[Store].[USA].[CA],\n" +
> >             "     [Store].[USA].[WA]},\n" +
> >             "    {[Customers].[USA].[WA].[Issaquah].[Abe Tramel]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]})' \n" +
> >             "select \n" +
> >             " OrderSet([NECJ], [Measures].[Unit Sales], BDESC, Ancestor([Customers].currentMember, [Customers].[Name]), BDESC) \n" +
> >             "on 0 from [Sales]";
> >         assertQueryReturns(query,
> >             fold(
> >                 "Axis #0:\n" +
> >                 "{}\n" +
> >                 "Axis #1:\n" +
> >                 "{[Store].[All Stores].[USA].[CA], [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n" +
> >                 "{[Store].[All Stores].[USA].[CA], [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n" +
> >                 "{[Store].[All Stores].[USA].[WA], [Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n" +
> >                 "Row #0: 75\n" +
> >                 "Row #0: 33\n" +
> >                 "Row #0: 33\n"));
> >     }
> > 
> >     public void testOrderSetTupleMultiKeys3() {
> >         // WA unit sales is greater than CA unit sales
> >         // Santa Monica unit sales (2660) is greater that Woodland hills (2516)
> >         String query =
> >             "with \n" +
> >             "  set [NECJ] as \n" +
> >             "    'NonEmptyCrossJoin( \n" +
> >             "    {[Store].[USA].[CA],\n" +
> >             "     [Store].[USA].[WA]},\n" +
> >             "    {[Customers].[USA].[WA].[Issaquah].[Abe Tramel]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]})' \n" +
> >             "select \n" +
> >             " OrderSet([NECJ], [Measures].[Unit Sales], DESC, Ancestor([Customers].currentMember, [Customers].[Name]), BDESC) \n" +
> >             "on 0 from [Sales]";
> >         assertQueryReturns(query,
> >             fold(
> >                 "Axis #0:\n" +
> >                 "{}\n" +
> >                 "Axis #1:\n" +
> >                 "{[Store].[All Stores].[USA].[WA], [Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n" +
> >                 "{[Store].[All Stores].[USA].[CA], [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n" +
> >                 "{[Store].[All Stores].[USA].[CA], [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n" +
> >                 "Row #0: 33\n" +
> >                 "Row #0: 33\n" +
> >                 "Row #0: 75\n"));
> >     }
> > 
> >     public void testOrderSetTupleMultiKeyswithVCube() {
> >         // WA unit sales is greater than CA unit sales
> >         String query =
> >             "with \n" +
> >             "  set [CJ] as \n" +
> >             "    'CrossJoin( \n" +
> >             "    {[Position].[Store Management].children},\n" +
> >             "    {[Customers].[USA].[WA].[Issaquah].[Abe Tramel]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]})' \n" +
> >             "select \n" +
> >             "  [Measures].[Org Salary] on columns, \n" +
> >             "  OrderSet([CJ], [Position].currentMember, BASC, Ancestor([Customers].currentMember, [Customers].[Name]), BDESC) \n" +
> >             "on rows \n" +
> >             "from [Sales vs HR]";
> > 
> >         boolean saved = props.CompareSiblingsByOrderKey.get();
> >         props.CompareSiblingsByOrderKey.set(true);
> >         Connection conn = null;
> >         try {
> >             // Use a fresh connection to make sure bad member ordinals haven't
> >             // been assigned by previous tests.
> >             conn = getTestContext().getFoodMartConnection(false);
> >             TestContext context = getTestContext(conn);
> >             // a non-sense cube just to test ordering by order key
> >             context = context.create(
> >                 null, null,
> >                 "<VirtualCube name=\"Sales vs HR\">\n" +
> >                     "<VirtualCubeDimension cubeName=\"Sales\" name=\"Customers\"/>\n" +
> >                     "<VirtualCubeDimension cubeName=\"HR\" name=\"Position\"/>\n" +
> >                     "<VirtualCubeMeasure cubeName=\"HR\" name=\"[Measures].[Org Salary]\"/>\n" +
> >                     "</VirtualCube>",
> >                 null, null, null);
> > 
> >             context.assertQueryReturns(query,
> >                 fold(
> >                     "Axis #0:\n" +
> >                     "{}\n" +
> >                     "Axis #1:\n" +
> >                     "{[Measures].[Org Salary]}\n" +
> >                     "Axis #2:\n" +
> >                     "{[Position].[All Position].[Store Management].[Store Manager], [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n" +
> >                     "{[Position].[All Position].[Store Management].[Store Manager], [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n" +
> >                     "{[Position].[All Position].[Store Management].[Store Manager], [Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n" +
> >                     "{[Position].[All Position].[Store Management].[Store Assistant Manager], [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n" +
> >                     "{[Position].[All Position].[Store Management].[Store Assistant Manager], [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n" +
> >                     "{[Position].[All Position].[Store Management].[Store Assistant Manager], [Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n" +
> >                     "{[Position].[All Position].[Store Management].[Store Shift Supervisor], [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n" +
> >                     "{[Position].[All Position].[Store Management].[Store Shift Supervisor], [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n" +
> >                     "{[Position].[All Position].[Store Management].[Store Shift Supervisor], [Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n" +
> >                     "Row #0: \n" +
> >                     "Row #1: \n" +
> >                     "Row #2: \n" +
> >                     "Row #3: \n" +
> >                     "Row #4: \n" +
> >                     "Row #5: \n" +
> >                     "Row #6: \n" +
> >                     "Row #7: \n" +
> >                     "Row #8: \n"));
> >         } finally {
> >             props.CompareSiblingsByOrderKey.set(saved);
> >             if (conn != null) {
> >                 conn.close();
> >             }
> >         }
> >     }
> > 
> >     public void testOrderSetConstant1() {
> >         String query =
> >             "select \n" +
> >             "  OrderSet(" +
> >             "    {[Customers].[USA].[WA].[Issaquah].[Abe Tramel]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}," +
> >             "    [Customers].[USA], BDESC) \n" +
> >             "on 0 from [Sales]";
> > 
> >         assertQueryReturns(query,
> >             fold(
> >                 "Axis #0:\n" +
> >                 "{}\n" +
> >                 "Axis #1:\n" +
> >                 "{[Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n" +
> >                 "{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n" +
> >                 "{[Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n" +
> >                 "Row #0: 33\n" +
> >                 "Row #0: 75\n" +
> >                 "Row #0: 33\n"));
> >     }
> > 
> >     public void testOrderSetDiffrentDim() {
> >         String query =
> >             "select \n" +
> >             "  OrderSet(" +
> >             "    {[Customers].[USA].[WA].[Issaquah].[Abe Tramel]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]," +
> >             "     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}," +
> >             "    [Product].currentMember, BDESC, [Gender].currentMember, BDESC) \n" +
> >             "on 0 from [Sales]";
> > 
> >         assertQueryReturns(query,
> >             fold(
> >                 "Axis #0:\n" +
> >                 "{}\n" +
> >                 "Axis #1:\n" +
> >                 "{[Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n" +
> >                 "{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n" +
> >                 "{[Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n" +
> >                 "Row #0: 33\n" +
> >                 "Row #0: 75\n" +
> >                 "Row #0: 33\n"));
> >     }
> > 
> 
> ==== //open/lu/release/mondrian/wm/testsrc/main/mondrian/test/Main.java#2 (ktext) ====
> 
> 2c2
> < // $Id: //open/lu/release/mondrian/wm/testsrc/main/mondrian/test/Main.java#1 $
> ---
> > // $Id: //open/lu/release/mondrian/wm/testsrc/main/mondrian/test/Main.java#2 $
> 49c49
> <  * @version $Id: //open/lu/release/mondrian/wm/testsrc/main/mondrian/test/Main.java#1 $
> ---
> >  * @version $Id: //open/lu/release/mondrian/wm/testsrc/main/mondrian/test/Main.java#2 $
> 198a199
> >             addTest(suite, OrderSetTest.class, "suite");
> 
> ==== //open/lu/release/mondrian/wm/testsrc/main/mondrian/xmla/XmlaBasicTest.ref.xml#2 (ktext) ====
> 
> 5554a5555,5564
> >                         <FUNCTION_NAME>OrderSet</FUNCTION_NAME>
> >                         <DESCRIPTION>Arranges members of a set,
> >                             optionally preserving or breaking the hierarchy.</DESCRIPTION>
> >                         <PARAMETER_LIST>(none)</PARAMETER_LIST>
> >                         <RETURN_TYPE>1</RETURN_TYPE>
> >                         <ORIGIN>1</ORIGIN>
> >                         <INTERFACE_NAME/>
> >                         <CAPTION>OrderSet</CAPTION>
> >                     </row>
> >                     <row>
> 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.pentaho.org/pipermail/mondrian/attachments/20081018/121eb02c/attachment.html 


More information about the Mondrian mailing list