[Mondrian] Re: Eigenbase perforce change 11758 for review

Rushan Chen rchen at lucidera.com
Tue Oct 21 03:01:00 EDT 2008


Hi Julian,

Thought I could chime in as I posted the original suggestion for syntax 
extension, which is turning out to be a different function in its final 
shape.

An earlier post here asked for inputs for the syntax options considered:
http://lists.pentaho.org/pipermail/mondrian/2008-October/001473.html
(The URL it refers to should be 
http://pub.eigenbase.org/wiki/MondrianOrderFunctionExtension#Syntax_Options)

After a few days without new use cases that supports the alternative 
syntax, we decided on adopting a new function name because it is the 
least entangled with the existing Order function and its implementation.

Rushan

Julian Hyde wrote:
> 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 <http://p4web.eigenbase.org/@md=d&c=6PU@//11758?ac=10>
>>
>> Change 11758 by kvu at kvu.shada.eigenbase <mailto: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>
>>
>>     
> ------------------------------------------------------------------------
>
> _______________________________________________
> Mondrian mailing list
> Mondrian at pentaho.org
> http://lists.pentaho.org/mailman/listinfo/mondrian
>   





More information about the Mondrian mailing list