<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN">
<HTML>
<HEAD>
  <META HTTP-EQUIV="Content-Type" CONTENT="text/html; CHARSET=UTF-8">
  <META NAME="GENERATOR" CONTENT="GtkHTML/3.18.3">
</HEAD>
<BODY>
Khanh,<BR>
<BR>
I saw you added a 'MondrianProperties props' member to FunctionTest. The <BR>
<BR>
T save&nbsp; = MondrianProperties.property.get();<BR>
try {<BR>
&nbsp;&nbsp; MondrianProperties.instance().property.set(newValue);<BR>
&nbsp;&nbsp; &lt;&lt; test &gt;&gt;<BR>
} finally {<BR>
&nbsp;&nbsp; MondrianProperties.instance().property.set(save);<BR>
}<BR>
<BR>
pattern has become ubiquitous in mondrian tests, so I have introduced mondrian.test.PropertySaver to do deal with it.&nbsp; Just create a PropertySaver as a protected final member variable, set properties via the saver, and make sure your test has a tearDown() method that calls PropertySaver.reset().<BR>
<BR>
See change 11828 <A HREF="http://p4web.eigenbase.org/@md=d&amp;c=6PU@//11768?ac=10">http://p4web.eigenbase.org/@md=d&amp;c=6PU@//11828?ac=10</A><BR>
<BR>
Others,<BR>
<BR>
Please use PropertySaver when you see this pattern. Please also convert existing code that you see.<BR>
<BR>
Julian<BR>
<BR>
<BR>
<BR>
On Sun, 2008-10-19 at 11:40 -0700, Khanh Vu wrote:
<BLOCKQUOTE TYPE=CITE>
<PRE>
<A HREF="http://p4web.eigenbase.org/@md=d&amp;c=6PU@//11768?ac=10">http://p4web.eigenbase.org/@md=d&amp;c=6PU@//11768?ac=10</A>

Change 11768 by <A HREF="mailto:kvu@kvu.shada.eigenbase">kvu@kvu.shada.eigenbase</A> on 2008/10/19 11:39:47

        MONDRIAN: itegrates change 11758 to mondrian head

Affected files ...

... //open/mondrian/src/main/mondrian/olap/fun/BuiltinFunTable.java#154 integrate
... //open/mondrian/src/main/mondrian/olap/fun/FunUtil.java#112 integrate
... //open/mondrian/src/main/mondrian/olap/fun/OrderSetFunDef.java#1 branch
... //open/mondrian/testsrc/main/mondrian/olap/fun/FunctionTest.java#136 integrate
... //open/mondrian/testsrc/main/mondrian/test/Main.java#96 integrate
... //open/mondrian/testsrc/main/mondrian/test/clearview/OrderSetTest.java#1 branch
... //open/mondrian/testsrc/main/mondrian/test/clearview/OrderSetTest.ref.xml#1 branch
... //open/mondrian/testsrc/main/mondrian/xmla/XmlaBasicTest.ref.xml#67 integrate

Differences ...

==== //open/mondrian/src/main/mondrian/olap/fun/BuiltinFunTable.java#154 (ktext) ====

2c2
&lt; // $Id: //open/mondrian/src/main/mondrian/olap/fun/BuiltinFunTable.java#153 $
---
&gt; // $Id: //open/mondrian/src/main/mondrian/olap/fun/BuiltinFunTable.java#154 $
42c42
&lt;  * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/BuiltinFunTable.java#153 $
---
&gt;  * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/BuiltinFunTable.java#154 $
1091a1092
&gt;         define(OrderSetFunDef.Resolver);

==== //open/mondrian/src/main/mondrian/olap/fun/FunUtil.java#112 (ktext) ====

2c2
&lt; // $Id: //open/mondrian/src/main/mondrian/olap/fun/FunUtil.java#111 $
---
&gt; // $Id: //open/mondrian/src/main/mondrian/olap/fun/FunUtil.java#112 $
20a21,22
&gt; import mondrian.calc.MemberCalc;
&gt; import mondrian.calc.impl.*;
36c38
&lt;  * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/FunUtil.java#111 $
---
&gt;  * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/FunUtil.java#112 $
256a259
&gt;         ;
328c331
&lt;     static Map&lt;Member, Object&gt; evaluateMembers(
---
&gt;     static Map&lt;Object, Object&gt; evaluateMembers(
338c341
&lt;         Map&lt;Member, Object&gt; mapMemberToValue = new HashMap&lt;Member, Object&gt;();
---
&gt;         Map&lt;Object, Object&gt; mapMemberToValue = new HashMap&lt;Object, Object&gt;();
420a424,611
&gt;      * For each tuple/member in a list, evaluates the member value expression
&gt;      * and populates a map from tuples/members to members
&gt;      *
&gt;      * @param &lt;T&gt; Generic type, can take a member or a tuple
&gt;      * @param evaluator Evaluation context
&gt;      * @param exp Expression to evaluate
&gt;      * @param members List of members
&gt;      * @param parentsToo If true, evaluate the expression for all ancestors
&gt;      *            of the members as well
&gt;      * @return the map from members or tuples to members
&gt;      */
&gt;     static &lt;T&gt; Map&lt;Object, Object&gt; evaluateMemberValueExps(
&gt;         Evaluator evaluator,
&gt;         Calc exp,
&gt;         List&lt;T&gt; members,
&gt;         boolean parentsToo)
&gt;     {
&gt;         MemberCalc mcalc = (MemberCalc) exp;
&gt;         Map&lt;Object, Object&gt; mapMemberToMember = new HashMap&lt;Object, Object&gt;();
&gt; 
&gt;         // try to optimize when exp is currentMember
&gt;         boolean isCurrMemExp = mcalc instanceof DimensionCurrentMemberFunDef.CalcImpl;
&gt;         int idx = -1;
&gt;         if (isCurrMemExp) {
&gt;             Object first = members.get(0);
&gt;             if (first instanceof Member[]) {
&gt;                 Member[] firstMem = (Member[]) first;
&gt;                 int n = firstMem.length;
&gt;                 Dimension dim = mcalc.getType().getDimension();
&gt;                 for (int i = 0; i &lt; n; i++) {
&gt;                     if (dim.equals(firstMem[i].getDimension())) {
&gt;                         idx = i;
&gt;                         break;
&gt;                     }
&gt;                 }
&gt;                 assert idx != -1;
&gt;             }
&gt;         }
&gt;         // populate the map
&gt;         Object obj;
&gt;         Member result;
&gt;         for (int i = 0, n = members.size(); i &lt; n; i++) {
&gt;             obj = members.get(i);
&gt;             if (obj instanceof Member) {
&gt;                 while (true) {
&gt;                     if (isCurrMemExp) {
&gt;                         mapMemberToMember.put(obj, obj);
&gt;                     } else {
&gt;                         evaluator.setContext((Member) obj);
&gt;                         result = mcalc.evaluateMember(evaluator);
&gt;                         if (result == null) {
&gt;                             result = NullMember;
&gt;                         }
&gt;                         mapMemberToMember.put(obj, result);
&gt;                     }
&gt;                     if (!parentsToo) {
&gt;                         break;
&gt;                     }
&gt;                     obj = ((Member) obj).getParentMember();
&gt;                     if (obj == null) {
&gt;                         break;
&gt;                     }
&gt;                     if (mapMemberToMember.containsKey((Member) obj)) {
&gt;                         break;
&gt;                     }
&gt;                 }
&gt;             } else if (obj instanceof Member[]) {
&gt;                 evaluator.setContext((Member[]) obj);
&gt;                 if (isCurrMemExp) {
&gt;                     mapMemberToMember.put(
&gt;                         new ArrayHolder&lt;Member&gt;((Member[]) obj), ((Member[]) obj)[idx]);
&gt;                 } else {
&gt;                     // populate tuples map
&gt;                     result = mcalc.evaluateMember(evaluator);
&gt;                     if (result == null) {
&gt;                         result = NullMember;
&gt;                     }
&gt;                     mapMemberToMember.put(
&gt;                         new ArrayHolder&lt;Member&gt;((Member[]) obj), result);
&gt;                 }
&gt;             }
&gt;         }
&gt;         return mapMemberToMember;
&gt;     }
&gt; 
&gt;     /**
&gt;      * Populates the list of maps from tuples/members to values/members.
&gt;      * Each member of the list corresponds to a sort key. Since it is
&gt;      * unlikely that all sort keys will be used to compare any pair
&gt;      * of members, prepopulate the first half only
&gt;      *
&gt;      * @param &lt;T&gt; Generic type; can be member or tuple
&gt;      * @param listMapMemberToValue List of maps
&gt;      * @param evaluator Evaluation context
&gt;      * @param members List of members
&gt;      * @param keySpecList List of sort key specifications
&gt;      */
&gt;     static &lt;T&gt; void populateMembersMap(
&gt;         List&lt;Map&lt;Object, Object&gt;&gt; listMapMemberToValue,
&gt;         Evaluator evaluator,
&gt;         List&lt;T&gt; members,
&gt;         List&lt;SortKeySpec&gt; keySpecList)
&gt;     {
&gt;         final int keyCount = keySpecList.size();
&gt;         // magic number - pre-populate the map
&gt;         // for one plus the first half of sort keys
&gt;         final int depth = keyCount / 2 + 1;
&gt;         Object first = members.get(0);
&gt;         for (int i = 0; i &lt; depth; i++) {
&gt;             SortKeySpec sortKey = keySpecList.get(i);
&gt;             if (sortKey.isMemberValueExp()) {
&gt;                 // get member
&gt;                 listMapMemberToValue.add(
&gt;                     evaluateMemberValueExps(
&gt;                         evaluator,
&gt;                         sortKey.getKey(),
&gt;                         members,
&gt;                         !sortKey.getDirection().brk));
&gt;             } else {
&gt;                 // get value
&gt;                 if (first instanceof Member) {
&gt;                     listMapMemberToValue.add(
&gt;                         evaluateMembers(
&gt;                             evaluator,
&gt;                             sortKey.getKey(),
&gt;                             (List&lt;Member&gt;) members,
&gt;                             !sortKey.getDirection().brk));
&gt;                 } else {
&gt;                     // populate map for tuples only
&gt;                     // meaning optimize for break hierarchy comparison only
&gt;                     listMapMemberToValue.add(
&gt;                         evaluateTuples(
&gt;                             evaluator,
&gt;                             sortKey.getKey(),
&gt;                             (List&lt;Member[]&gt;) members));
&gt;                 }
&gt;             }
&gt;         }
&gt;         for (int i = depth; i &lt; keyCount; i++) {
&gt;             listMapMemberToValue.add(new HashMap&lt;Object, Object&gt;());
&gt;         }
&gt;     }
&gt; 
&gt;     /**
&gt;      * Gets the value from the map or evaluates the member using the expression
&gt;      * if the value is not ready
&gt;      *
&gt;      * @param member Key of the map
&gt;      * @param exp Expression to evaluate
&gt;      * @param evaluator Evaluation context
&gt;      * @param mapTuples Map of members or tuples to members or cell values
&gt;      * @return Value from the map
&gt;      */
&gt;     static Object getFromMapOrEvaluate(
&gt;         Object member,
&gt;         Calc exp,
&gt;         Evaluator evaluator,
&gt;         boolean isMemValExp,
&gt;         Map&lt;Object, Object&gt; mapTuples)
&gt;     {
&gt;         Object key;
&gt;         boolean isTuple = (member instanceof Member[]);
&gt;         if (isTuple) {
&gt;             key = new ArrayHolder&lt;Member&gt;((Member[]) member);
&gt;         } else {
&gt;             key = member;
&gt;         }
&gt;         Object val = mapTuples.get(key);
&gt;         if (val == null) {
&gt;             if (isTuple) {
&gt;                 evaluator.setContext((Member[]) member);
&gt;             } else {
&gt;                 evaluator.setContext((Member) member);
&gt;             }
&gt;             val = exp.evaluate(evaluator);
&gt;             if (val == null) {
&gt;                 if (isMemValExp) {
&gt;                     val = NullMember;
&gt;                 } else {
&gt;                     val = Util.nullValue;
&gt;                 }
&gt;             }
&gt;             mapTuples.put(key, val);
&gt;         }
&gt;         return val;
&gt;     }
&gt; 
&gt;     /**
436c627
&lt;         Map&lt;Member, Object&gt; mapMemberToValue;
---
&gt;         Map&lt;Object, Object&gt; mapMemberToValue;
483a675,707
&gt;      * Helper function to sortMembers a list of members according to a list
&gt;      * of expressions and a list of sorting flags.
&gt;      *
&gt;      * &lt;p&gt;NOTE: This function does not preserve the contents of the validator.
&gt;      */
&gt;     public static void sortMembers(
&gt;         Evaluator evaluator, List&lt;Member&gt; members, List&lt;SortKeySpec&gt; keySpecList)
&gt;     {
&gt;         if (members.isEmpty()) {
&gt;             return;
&gt;         }
&gt;         Object first = members.get(0);
&gt;         List&lt;Map&lt;Object, Object&gt;&gt; listMapMemberToValue =
&gt;             new ArrayList&lt;Map&lt;Object, Object&gt;&gt;();
&gt;         populateMembersMap(
&gt;             listMapMemberToValue, evaluator, members, keySpecList);
&gt;         if (first instanceof Member) {
&gt;             Comparator&lt;Member&gt; comparator =
&gt;                 new MultiKeysMemberComparator(
&gt;                     evaluator, keySpecList, listMapMemberToValue).wrap();
&gt;             Collections.sort(members, comparator);
&gt;         } else {
&gt;             Util.assertTrue(first instanceof Member[]);
&gt;             List&lt;Member[]&gt; tupleList = Util.cast(members);
&gt;             final int arity = ((Member[]) first).length;
&gt;             Comparator&lt;Member[]&gt; comparator =
&gt;                 new MultiKeysArrayComparator(
&gt;                     evaluator, keySpecList, listMapMemberToValue, arity).wrap();
&gt;             Collections.sort(tupleList, comparator);
&gt;         }
&gt;     }
&gt; 
&gt;     /**
1633a1858,1948
&gt;     public static int compareMembersByOrderKeys(
&gt;         Member m1, Member m2)
&gt;     {
&gt;         // calculated members collate after non-calculated
&gt;         final boolean calculated1 = m1.isCalculatedInQuery();
&gt;         final boolean calculated2 = m2.isCalculatedInQuery();
&gt;         if (calculated1) {
&gt;             if (!calculated2) {
&gt;                 return 1;
&gt;             }
&gt;         } else {
&gt;             if (calculated2) {
&gt;                 return -1;
&gt;             }
&gt;         }
&gt;         final Comparable k1 = m1.getOrderKey();
&gt;         final Comparable k2 = m2.getOrderKey();
&gt;         if ((k1 != null) &amp;&amp; (k2 != null)) {
&gt;             return k1.compareTo(k2);
&gt;         } else {
&gt;             return m1.compareTo(m2);
&gt;         }
&gt;     }
&gt; 
&gt;     public static int compareMembers(
&gt;         Member m1,
&gt;         Member m2,
&gt;         Evaluator evaluator,
&gt;         Calc exp,
&gt;         boolean isMemValExp,
&gt;         Map&lt;Object, Object&gt; mapMemberToValue)
&gt;     {
&gt;         if (isMemValExp) {
&gt;             return FunUtil.compareMembersByOrderKeys(m1, m2);
&gt;         } else {
&gt;             Member old = evaluator.setContext(m1);
&gt;             Object v1 = getFromMapOrEvaluate(
&gt;                 m1, exp, evaluator, false, mapMemberToValue);
&gt;             Object v2 = getFromMapOrEvaluate(
&gt;                 m2, exp, evaluator, false, mapMemberToValue);
&gt;             // important to restore the evaluator state -- and this is faster
&gt;             // than calling evaluator.push()
&gt;             evaluator.setContext(old);
&gt;             return FunUtil.compareValues(v1, v2);
&gt;         }
&gt;     }
&gt; 
&gt;     public static int compareMembersHierarchically(
&gt;         Member m1,
&gt;         Member m2,
&gt;         Evaluator evaluator,
&gt;         Calc exp,
&gt;         boolean desc,
&gt;         boolean isMemValExp,
&gt;         Map&lt;Object, Object&gt; mapMemberToValue)
&gt;     {
&gt;         if (FunUtil.equals(m1, m2)) {
&gt;             return 0;
&gt;         }
&gt;         while (true) {
&gt;             int depth1 = m1.getDepth(),
&gt;                     depth2 = m2.getDepth();
&gt;             if (depth1 &lt; depth2) {
&gt;                 m2 = m2.getParentMember();
&gt;                 if (FunUtil.equals(m1, m2)) {
&gt;                     return -1;
&gt;                 }
&gt;             } else if (depth1 &gt; depth2) {
&gt;                 m1 = m1.getParentMember();
&gt;                 if (FunUtil.equals(m1, m2)) {
&gt;                     return 1;
&gt;                 }
&gt;             } else {
&gt;                 Member prev1 = m1, prev2 = m2;
&gt;                 m1 = m1.getParentMember();
&gt;                 m2 = m2.getParentMember();
&gt;                 if (FunUtil.equals(m1, m2)) {
&gt;                     // including case where both parents are null
&gt;                     int c = FunUtil.compareMembers(
&gt;                         prev1, prev2, evaluator, exp, isMemValExp, mapMemberToValue);
&gt;                     // skip the below compare if isMemValExp is true
&gt;                     if ((c == 0) &amp;&amp; !(isMemValExp)) {
&gt;                         c = FunUtil.compareMembersByOrderKeys(
&gt;                             prev1, prev2);
&gt;                     }
&gt;                     return desc ? -c : c;
&gt;                 }
&gt;             }
&gt;         }
&gt;     }
&gt; 
1829c2144
&lt;         List &lt;Member&gt; memberList =
---
&gt;         List&lt;Member&gt; memberList =
1880c2195
&lt;         Map&lt;Member, Object&gt; mapMemberToValue;
---
&gt;         Map&lt;Object, Object&gt; mapMemberToValue;
1881a2197
&gt;         Evaluator evaluator;
1883c2199
&lt;         MemberComparator(Map&lt;Member, Object&gt; mapMemberToValue, boolean desc) {
---
&gt;         MemberComparator(Map&lt;Object, Object&gt; mapMemberToValue, boolean desc) {
1887a2204,2210
&gt;         MemberComparator(
&gt;             Evaluator evaluator,
&gt;             List&lt;SortKeySpec&gt; keySpecList)
&gt;         {
&gt;             this.evaluator = evaluator;
&gt;         }
&gt; 
1890c2213
&lt;             if (LOGGER.isDebugEnabled()) {
---
&gt;             if (LOGGER.isDebugEnabled() &amp;&amp; (mapMemberToValue != null)) {
1961c2284
&lt;             Map&lt;Member, Object&gt; mapMemberToValue, boolean desc)
---
&gt;             Map&lt;Object, Object&gt; mapMemberToValue, boolean desc)
1972c2295
&lt;         BreakMemberComparator(Map&lt;Member, Object&gt; mapMemberToValue, boolean desc) {
---
&gt;         BreakMemberComparator(Map&lt;Object, Object&gt; mapMemberToValue, boolean desc) {
1980a2304,2365
&gt;     private static class MultiKeysMemberComparator extends MemberComparator
&gt;     {
&gt;         List&lt;SortKeySpec&gt; keySpecList;
&gt;         List&lt;Map&lt;Object, Object&gt;&gt; listMapMemberToValue;
&gt;         int keySpecCount;
&gt; 
&gt;         MultiKeysMemberComparator(
&gt;             Evaluator evaluator,
&gt;             List&lt;SortKeySpec&gt; keySpecList,
&gt;             List&lt;Map&lt;Object, Object&gt;&gt; listMapMemberToValue)
&gt;         {
&gt;             super(evaluator, keySpecList);
&gt;             this.keySpecList = keySpecList;
&gt;             this.listMapMemberToValue = listMapMemberToValue;
&gt;             this.keySpecCount = keySpecList.size();
&gt;         }
&gt; 
&gt;         public final int compare(Member m1, Member m2)
&gt;         {
&gt;             int c = 0;
&gt;             for (int i = 0; i &lt; keySpecCount; i++) {
&gt;                 SortKeySpec sortKey = keySpecList.get(i);
&gt;                 Calc exp = sortKey.key;
&gt;                 Flag flag = sortKey.direction;
&gt;                 boolean isMemValExp = sortKey.isMemberValExp;
&gt;                 Map&lt;Object, Object&gt; mapMemberToValue =
&gt;                     listMapMemberToValue.get(i);
&gt;                 //this.evaluator.push(); //check if this is needed
&gt;                 Member tm1 = null, tm2 = null;
&gt;                 if (isMemValExp) {
&gt;                     tm1 = (Member) getFromMapOrEvaluate(
&gt;                         m1, exp, evaluator, true, mapMemberToValue);
&gt;                     tm2 = (Member) getFromMapOrEvaluate(
&gt;                         m2, exp, evaluator, true, mapMemberToValue);
&gt;                 }
&gt;                 if (flag.brk) {
&gt;                     int c1 = compareMembers(
&gt;                         isMemValExp ? tm1 : m1,
&gt;                         isMemValExp ? tm2 : m2,
&gt;                         evaluator,
&gt;                         exp,
&gt;                         isMemValExp,
&gt;                         mapMemberToValue);
&gt;                     c = flag.descending ? -c1 : c1;
&gt;                 } else {
&gt;                     c = compareMembersHierarchically(
&gt;                         isMemValExp ? tm1 : m1,
&gt;                         isMemValExp ? tm2 : m2,
&gt;                         evaluator,
&gt;                         exp,
&gt;                         flag.descending,
&gt;                         isMemValExp,
&gt;                         mapMemberToValue);
&gt;                 }
&gt;                 if (c != 0) {
&gt;                     return c;
&gt;                 }
&gt;             }
&gt;             return c;
&gt;         }
&gt;     }
&gt; 
2044c2429
&lt;         final Calc calc;
---
&gt;         Calc calc;
2050a2436,2442
&gt; 
&gt;         ArrayExpComparator(
&gt;             Evaluator evaluator,
&gt;             int arity) {
&gt;             super(arity);
&gt;             this.evaluator = evaluator;
&gt;         }
2142a2535,2640
&gt;     private static class MultiKeysArrayComparator
&gt;         extends ArrayExpComparator
&gt;     {
&gt;         List&lt;SortKeySpec&gt; keySpecList;
&gt;         List&lt;Map&lt;Object, Object&gt;&gt; listMapMemberToValue;
&gt;         int keySpecCount;
&gt; 
&gt;         MultiKeysArrayComparator(
&gt;             Evaluator evaluator,
&gt;             List&lt;SortKeySpec&gt; keySpecList,
&gt;             List&lt;Map&lt;Object, Object&gt;&gt; listMapMemberToValue,
&gt;             int arity)
&gt;         {
&gt;             super(evaluator, arity);
&gt;             this.keySpecList = keySpecList;
&gt;             this.keySpecCount = keySpecList.size();
&gt;             this.listMapMemberToValue = listMapMemberToValue;
&gt;         }
&gt; 
&gt;         public int compare(Member[] a1, Member[] a2) {
&gt;             int c = 0;
&gt;             for (int i = 0; i &lt; keySpecCount; i++) {
&gt;                 SortKeySpec sortKey = keySpecList.get(i);
&gt;                 if (sortKey.direction.brk) {
&gt;                     c = compareBreakHierarchy(
&gt;                         a1,
&gt;                         a2,
&gt;                         sortKey.key,
&gt;                         sortKey.direction.descending,
&gt;                         sortKey.isMemberValExp,
&gt;                         listMapMemberToValue.get(i));
&gt;                 } else {
&gt;                     c = compareHierarchically(
&gt;                         a1,
&gt;                         a2,
&gt;                         sortKey.key,
&gt;                         sortKey.direction.descending,
&gt;                         sortKey.isMemberValExp,
&gt;                         listMapMemberToValue.get(i));
&gt;                 }
&gt;                 if (c != 0) {
&gt;                     return c;
&gt;                 }
&gt;             }
&gt;             return c;
&gt;         }
&gt; 
&gt;         public int compareBreakHierarchy(
&gt;             Member[] a1,
&gt;             Member[] a2,
&gt;             Calc exp,
&gt;             boolean desc,
&gt;             boolean isMemValExp,
&gt;             Map&lt;Object, Object&gt; mapTupleToValue)
&gt;         {
&gt;             if (isMemValExp) {
&gt;                 Member m1 = (Member) getFromMapOrEvaluate(
&gt;                     a1, exp, evaluator, true, mapTupleToValue);
&gt;                 Member m2 = (Member) getFromMapOrEvaluate(
&gt;                     a2, exp, evaluator, true, mapTupleToValue);
&gt;                 return desc ?
&gt;                     - compareMembersByOrderKeys(m1, m2) :
&gt;                     compareMembersByOrderKeys(m1, m2);
&gt;             } else {
&gt;                 Object v1 = getFromMapOrEvaluate(
&gt;                     a1, exp, evaluator, false, mapTupleToValue);
&gt;                 Object v2 = getFromMapOrEvaluate(
&gt;                     a2, exp, evaluator, false, mapTupleToValue);
&gt;                 return desc ?
&gt;                     - compareValues(v1, v2) :
&gt;                     compareValues(v1, v2);
&gt;             }
&gt;         }
&gt; 
&gt;         public int compareHierarchically(
&gt;             Member[] a1,
&gt;             Member[] a2,
&gt;             Calc exp,
&gt;             boolean desc,
&gt;             boolean isMemValExp,
&gt;             Map&lt;Object, Object&gt; mapTupleToValue)
&gt;         {
&gt;             int c = 0;
&gt;             if (isMemValExp) {
&gt;                 Member m1 = (Member) getFromMapOrEvaluate(
&gt;                     a1, exp, evaluator, true, mapTupleToValue);
&gt;                 Member m2 = (Member) getFromMapOrEvaluate(
&gt;                     a2, exp, evaluator, true, mapTupleToValue);
&gt;                 c = compareMembersHierarchically(
&gt;                     m1, m2, evaluator, exp, desc, isMemValExp, mapTupleToValue);
&gt;             } else {
&gt;                 for (int i = 0; i &lt; arity; i++) {
&gt;                     Member m1 = a1[i],
&gt;                             m2 = a2[i];
&gt;                     c = compareMembersHierarchically(
&gt;                         m1, m2, evaluator, exp, desc, isMemValExp, mapTupleToValue);
&gt;                     if (c != 0) {
&gt;                         break;
&gt;                     }
&gt;                     evaluator.setContext(m1);
&gt;                 }
&gt;             }
&gt;             return c;
&gt;         }
&gt;     }
&gt; 
2386a2885,2946
&gt; 
&gt;     /**
&gt;      * Enumeration of the flags allowed to the &lt;code&gt;ORDER&lt;/code&gt; MDX function.
&gt;      */
&gt;     enum Flag {
&gt;         ASC(false, false),
&gt;         DESC(true, false),
&gt;         BASC(false, true),
&gt;         BDESC(true, true);
&gt; 
&gt;         final boolean descending;
&gt;         final boolean brk;
&gt; 
&gt;         Flag(boolean descending, boolean brk) {
&gt;             this.descending = descending;
&gt;             this.brk = brk;
&gt;         }
&gt; 
&gt;         public static String[] getNames() {
&gt;             List&lt;String&gt; names = new ArrayList&lt;String&gt;();
&gt;             for (Flag flags : Flag.class.getEnumConstants()) {
&gt;                 names.add(flags.name());
&gt;             }
&gt;             return names.toArray(new String[names.size()]);
&gt;         }
&gt;     }
&gt; 
&gt;     class SortKeySpec {
&gt;         private Calc key;
&gt;         private boolean isMemberValExp = true;
&gt;         private Flag direction;
&gt; 
&gt;         SortKeySpec(Calc key, boolean memberValExp, Flag dir) {
&gt;             this.key = key;
&gt;             this.isMemberValExp = memberValExp;
&gt;             this.direction = dir;
&gt;         }
&gt; 
&gt;         Calc getKey() {
&gt;             return this.key;
&gt;         }
&gt; 
&gt;         boolean isMemberValueExp() {
&gt;             return this.isMemberValExp;
&gt;         }
&gt; 
&gt;         Flag getDirection() {
&gt;             return this.direction;
&gt;         }
&gt; 
&gt;         void setKey(Calc key) {
&gt;             this.key = key;
&gt;         }
&gt; 
&gt;         void setMemberValueExp(boolean m) {
&gt;             this.isMemberValExp = m;
&gt;         }
&gt; 
&gt;         void setDirection(Flag dir) {
&gt;             this.direction = dir;
&gt;         }
&gt;     }

==== //open/mondrian/testsrc/main/mondrian/olap/fun/FunctionTest.java#136 (ktext) ====

2c2
&lt; // $Id: //open/mondrian/testsrc/main/mondrian/olap/fun/FunctionTest.java#135 $
---
&gt; // $Id: //open/mondrian/testsrc/main/mondrian/olap/fun/FunctionTest.java#136 $
34c34
&lt;  * @version $Id: //open/mondrian/testsrc/main/mondrian/olap/fun/FunctionTest.java#135 $
---
&gt;  * @version $Id: //open/mondrian/testsrc/main/mondrian/olap/fun/FunctionTest.java#136 $
37a38,39
&gt;     private MondrianProperties props;
&gt; 
161a164
&gt;         props = MondrianProperties.instance();
165a169
&gt;         props = MondrianProperties.instance();
5982a5987,6434
&gt;     public void testOrderSetEmpty() {
&gt;         String query =
&gt;             &quot;select \n&quot; +
&gt;             &quot;  OrderSet(&quot; +
&gt;             &quot;    {},&quot; +
&gt;             &quot;    [Customers].currentMember, BDESC) \n&quot; +
&gt;             &quot;on 0 from [Sales]&quot;;
&gt; 
&gt;         assertQueryReturns(query,
&gt;             fold(
&gt;                 &quot;Axis #0:\n&quot; +
&gt;                 &quot;{}\n&quot; +
&gt;                 &quot;Axis #1:\n&quot;));
&gt;     }
&gt; 
&gt;     public void testOrderOne() {
&gt;         String query =
&gt;             &quot;select \n&quot; +
&gt;             &quot;  OrderSet(&quot; +
&gt;             &quot;    {[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]},&quot; +
&gt;             &quot;    [Customers].currentMember, BDESC) \n&quot; +
&gt;             &quot;on 0 from [Sales]&quot;;
&gt; 
&gt;         assertQueryReturns(query,
&gt;             fold(
&gt;                 &quot;Axis #0:\n&quot; +
&gt;                 &quot;{}\n&quot; +
&gt;                 &quot;Axis #1:\n&quot; +
&gt;                 &quot;{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n&quot; +
&gt;                 &quot;Row #0: 75\n&quot;));
&gt;     }
&gt; 
&gt;     public void testOrderSetMemberMemberValueExpNew() {
&gt;         String query =
&gt;             &quot;select \n&quot; +
&gt;             &quot;  OrderSet(&quot; +
&gt;             &quot;    {[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]},&quot; +
&gt;             &quot;    [Customers].currentMember, BDESC) \n&quot; +
&gt;             &quot;on 0 from [Sales]&quot;;
&gt; 
&gt;         boolean saved = props.CompareSiblingsByOrderKey.get();
&gt;         props.CompareSiblingsByOrderKey.set(true);
&gt;         Connection conn = null;
&gt;         try {
&gt;             // Use a fresh connection to make sure bad member ordinals haven't
&gt;             // been assigned by previous tests.
&gt;             conn = getTestContext().getFoodMartConnection(false);
&gt;             TestContext context = getTestContext(conn);
&gt;             context.assertQueryReturns(query,
&gt;                 fold(
&gt;                     &quot;Axis #0:\n&quot; +
&gt;                     &quot;{}\n&quot; +
&gt;                     &quot;Axis #1:\n&quot; +
&gt;                     &quot;{[Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n&quot; +
&gt;                     &quot;{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n&quot; +
&gt;                     &quot;Row #0: 33\n&quot; +
&gt;                     &quot;Row #0: 75\n&quot;));
&gt;         } finally {
&gt;             props.CompareSiblingsByOrderKey.set(saved);
&gt;             if (conn != null) {
&gt;                 conn.close();
&gt;             }
&gt;         }
&gt;     }
&gt; 
&gt;     public void testOrderSetMemberDefaultFlag1() {
&gt;         // flags not specified default to ASC
&gt;         String query =
&gt;             &quot;with \n&quot; +
&gt;             &quot;  Member [Measures].[Zero] as '0' \n&quot; +
&gt;             &quot;select \n&quot; +
&gt;             &quot;  OrderSet(&quot; +
&gt;             &quot;    {[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]},&quot; +
&gt;             &quot;    [Customers].currentMember) \n&quot; +
&gt;             &quot;on 0 from [Sales]&quot;;
&gt;         assertQueryReturns(query,
&gt;             fold(
&gt;                 &quot;Axis #0:\n&quot; +
&gt;                 &quot;{}\n&quot; +
&gt;                 &quot;Axis #1:\n&quot; +
&gt;                 &quot;{[Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n&quot; +
&gt;                 &quot;{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n&quot; +
&gt;                 &quot;Row #0: 33\n&quot; +
&gt;                 &quot;Row #0: 75\n&quot;));
&gt;     }
&gt; 
&gt;     public void testOrderSetMemberDefaultFlag2() {
&gt;         // flags not specified default to ASC
&gt;         String query =
&gt;             &quot;with \n&quot; +
&gt;             &quot;  Member [Measures].[Zero] as '0' \n&quot; +
&gt;             &quot;select \n&quot; +
&gt;             &quot;  OrderSet(&quot; +
&gt;             &quot;    {[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]},&quot; +
&gt;             &quot;    [Measures].[Store Cost]) \n&quot; +
&gt;             &quot;on 0 from [Sales]&quot;;
&gt;         assertQueryReturns(query,
&gt;             fold(
&gt;                 &quot;Axis #0:\n&quot; +
&gt;                 &quot;{}\n&quot; +
&gt;                 &quot;Axis #1:\n&quot; +
&gt;                 &quot;{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n&quot; +
&gt;                 &quot;{[Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n&quot; +
&gt;                 &quot;Row #0: 75\n&quot; +
&gt;                 &quot;Row #0: 33\n&quot;));
&gt;     }
&gt; 
&gt;     public void testOrderSetMemberMemberValueExpHierarchy() {
&gt;         // Santa Monica and Woodland Hills both don't have orderkey
&gt;         // members are sorted by the order of their keys
&gt;         String query =
&gt;             &quot;select \n&quot; +
&gt;             &quot;  OrderSet(&quot; +
&gt;             &quot;    {[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]},&quot; +
&gt;             &quot;    [Customers].currentMember, DESC) \n&quot; +
&gt;             &quot;on 0 from [Sales]&quot;;
&gt;         assertQueryReturns(query,
&gt;             fold(
&gt;                 &quot;Axis #0:\n&quot; +
&gt;                 &quot;{}\n&quot; +
&gt;                 &quot;Axis #1:\n&quot; +
&gt;                 &quot;{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n&quot; +
&gt;                 &quot;{[Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n&quot; +
&gt;                 &quot;Row #0: 75\n&quot; +
&gt;                 &quot;Row #0: 33\n&quot;));
&gt;     }
&gt; 
&gt;     public void testOrderSetMemberMultiKeysMemberValueExp1() {
&gt;         String query =
&gt;             &quot;select \n&quot; +
&gt;             &quot;  OrderSet(&quot; +
&gt;             &quot;    {[Customers].[USA].[WA].[Issaquah].[Abe Tramel],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]},&quot; +
&gt;             &quot;    [Measures].[Unit Sales], BDESC, [Customers].currentMember, BDESC) \n&quot; +
&gt;             &quot;on 0 from [Sales]&quot;;
&gt;         assertQueryReturns(query,
&gt;             fold(
&gt;                 &quot;Axis #0:\n&quot; +
&gt;                 &quot;{}\n&quot; +
&gt;                 &quot;Axis #1:\n&quot; +
&gt;                 &quot;{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n&quot; +
&gt;                 &quot;{[Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n&quot; +
&gt;                 &quot;{[Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n&quot; +
&gt;                 &quot;Row #0: 75\n&quot; +
&gt;                 &quot;Row #0: 33\n&quot; +
&gt;                 &quot;Row #0: 33\n&quot;));
&gt;     }
&gt; 
&gt;     public void testOrderSetMemberMultiKeysMemberValueExp2() {
&gt;         String query =
&gt;             &quot;select \n&quot; +
&gt;             &quot;  OrderSet(&quot; +
&gt;             &quot;    {[Customers].[USA].[WA].[Issaquah].[Abe Tramel],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]},&quot; +
&gt;             &quot;    [Customers].currentMember.Parent.Parent, BASC, [Customers].currentMember, BDESC) \n&quot; +
&gt;             &quot;on 0 from [Sales]&quot;;
&gt; 
&gt;         boolean saved = props.CompareSiblingsByOrderKey.get();
&gt;         props.CompareSiblingsByOrderKey.set(true);
&gt;         Connection conn = null;
&gt;         try {
&gt;             // Use a fresh connection to make sure bad member ordinals haven't
&gt;             // been assigned by previous tests.
&gt;             conn = getTestContext().getFoodMartConnection(false);
&gt;             TestContext context = getTestContext(conn);
&gt;             context.assertQueryReturns(query,
&gt;                 fold(
&gt;                     &quot;Axis #0:\n&quot; +
&gt;                     &quot;{}\n&quot; +
&gt;                     &quot;Axis #1:\n&quot; +
&gt;                     &quot;{[Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n&quot; +
&gt;                     &quot;{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n&quot; +
&gt;                     &quot;{[Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n&quot; +
&gt;                     &quot;Row #0: 33\n&quot; +
&gt;                     &quot;Row #0: 75\n&quot; +
&gt;                     &quot;Row #0: 33\n&quot;));
&gt;         } finally {
&gt;             props.CompareSiblingsByOrderKey.set(saved);
&gt;             if (conn != null) {
&gt;                 conn.close();
&gt;             }
&gt;         }
&gt;     }
&gt; 
&gt;     public void testOrderSetMemberMultiKeysMemberValueExpDepends() {
&gt;         // should preserve order of Abe and Adeline (note second key is [Time])
&gt;         String query =
&gt;             &quot;select \n&quot; +
&gt;             &quot;  OrderSet(&quot; +
&gt;             &quot;    {[Customers].[USA].[WA].[Issaquah].[Abe Tramel],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]},&quot; +
&gt;             &quot;    [Measures].[Unit Sales], BDESC, [Time].currentMember, BDESC) \n&quot; +
&gt;             &quot;on 0 from [Sales]&quot;;
&gt;         assertQueryReturns(query,
&gt;             fold(
&gt;                 &quot;Axis #0:\n&quot; +
&gt;                 &quot;{}\n&quot; +
&gt;                 &quot;Axis #1:\n&quot; +
&gt;                 &quot;{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n&quot; +
&gt;                 &quot;{[Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n&quot; +
&gt;                 &quot;{[Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n&quot; +
&gt;                 &quot;Row #0: 75\n&quot; +
&gt;                 &quot;Row #0: 33\n&quot; +
&gt;                 &quot;Row #0: 33\n&quot;));
&gt;     }
&gt; 
&gt;     public void testOrderSetTupleSingleKeysNew() {
&gt;         String query =
&gt;             &quot;with \n&quot; +
&gt;             &quot;  set [NECJ] as \n&quot; +
&gt;             &quot;    'NonEmptyCrossJoin( \n&quot; +
&gt;             &quot;    {[Customers].[USA].[WA].[Issaquah].[Abe Tramel],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]},&quot; +
&gt;             &quot;    {[Store].[USA].[WA].[Seattle],\n&quot; +
&gt;             &quot;     [Store].[USA].[CA],\n&quot; +
&gt;             &quot;     [Store].[USA].[OR]})'\n&quot; +
&gt;             &quot;select \n&quot; +
&gt;             &quot; OrderSet([NECJ], [Customers].currentMember, BDESC) \n&quot; +
&gt;             &quot;on 0 from [Sales]&quot;;
&gt; 
&gt;         boolean saved = props.CompareSiblingsByOrderKey.get();
&gt;         props.CompareSiblingsByOrderKey.set(true);
&gt;         Connection conn = null;
&gt;         try {
&gt;             // Use a fresh connection to make sure bad member ordinals haven't
&gt;             // been assigned by previous tests.
&gt;             conn = getTestContext().getFoodMartConnection(false);
&gt;             TestContext context = getTestContext(conn);
&gt;             context.assertQueryReturns(query,
&gt;                 fold(
&gt;                     &quot;Axis #0:\n&quot; +
&gt;                     &quot;{}\n&quot; +
&gt;                     &quot;Axis #1:\n&quot; +
&gt;                     &quot;{[Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun], [Store].[All Stores].[USA].[CA]}\n&quot; +
&gt;                     &quot;{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young], [Store].[All Stores].[USA].[CA]}\n&quot; +
&gt;                     &quot;{[Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel], [Store].[All Stores].[USA].[WA].[Seattle]}\n&quot; +
&gt;                     &quot;Row #0: 33\n&quot; +
&gt;                     &quot;Row #0: 75\n&quot; +
&gt;                     &quot;Row #0: 33\n&quot;));
&gt;         } finally {
&gt;             props.CompareSiblingsByOrderKey.set(saved);
&gt;             if (conn != null) {
&gt;                 conn.close();
&gt;             }
&gt;         }
&gt;     }
&gt; 
&gt;     public void testOrderSetTupleMultiKeys1() {
&gt;         String query =
&gt;             &quot;with \n&quot; +
&gt;             &quot;  set [NECJ] as \n&quot; +
&gt;             &quot;    'NonEmptyCrossJoin( \n&quot; +
&gt;             &quot;    {[Store].[USA].[CA],\n&quot; +
&gt;             &quot;     [Store].[USA].[WA]},\n&quot; +
&gt;             &quot;    {[Customers].[USA].[WA].[Issaquah].[Abe Tramel],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]})' \n&quot; +
&gt;             &quot;select \n&quot; +
&gt;             &quot; OrderSet([NECJ], [Store].currentMember, BDESC, [Measures].[Unit Sales], BDESC) \n&quot; +
&gt;             &quot;on 0 from [Sales]&quot;;
&gt;         assertQueryReturns(query,
&gt;             fold(
&gt;                 &quot;Axis #0:\n&quot; +
&gt;                 &quot;{}\n&quot; +
&gt;                 &quot;Axis #1:\n&quot; +
&gt;                 &quot;{[Store].[All Stores].[USA].[WA], [Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n&quot; +
&gt;                 &quot;{[Store].[All Stores].[USA].[CA], [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n&quot; +
&gt;                 &quot;{[Store].[All Stores].[USA].[CA], [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n&quot; +
&gt;                 &quot;Row #0: 33\n&quot; +
&gt;                 &quot;Row #0: 75\n&quot; +
&gt;                 &quot;Row #0: 33\n&quot;));
&gt;     }
&gt; 
&gt;     public void testOrderSetTupleMultiKeys2() {
&gt;         String query =
&gt;             &quot;with \n&quot; +
&gt;             &quot;  set [NECJ] as \n&quot; +
&gt;             &quot;    'NonEmptyCrossJoin( \n&quot; +
&gt;             &quot;    {[Store].[USA].[CA],\n&quot; +
&gt;             &quot;     [Store].[USA].[WA]},\n&quot; +
&gt;             &quot;    {[Customers].[USA].[WA].[Issaquah].[Abe Tramel],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]})' \n&quot; +
&gt;             &quot;select \n&quot; +
&gt;             &quot; OrderSet([NECJ], [Measures].[Unit Sales], BDESC, Ancestor([Customers].currentMember, [Customers].[Name]), BDESC) \n&quot; +
&gt;             &quot;on 0 from [Sales]&quot;;
&gt;         assertQueryReturns(query,
&gt;             fold(
&gt;                 &quot;Axis #0:\n&quot; +
&gt;                 &quot;{}\n&quot; +
&gt;                 &quot;Axis #1:\n&quot; +
&gt;                 &quot;{[Store].[All Stores].[USA].[CA], [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n&quot; +
&gt;                 &quot;{[Store].[All Stores].[USA].[CA], [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n&quot; +
&gt;                 &quot;{[Store].[All Stores].[USA].[WA], [Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n&quot; +
&gt;                 &quot;Row #0: 75\n&quot; +
&gt;                 &quot;Row #0: 33\n&quot; +
&gt;                 &quot;Row #0: 33\n&quot;));
&gt;     }
&gt; 
&gt;     public void testOrderSetTupleMultiKeys3() {
&gt;         // WA unit sales is greater than CA unit sales
&gt;         // Santa Monica unit sales (2660) is greater that Woodland hills (2516)
&gt;         String query =
&gt;             &quot;with \n&quot; +
&gt;             &quot;  set [NECJ] as \n&quot; +
&gt;             &quot;    'NonEmptyCrossJoin( \n&quot; +
&gt;             &quot;    {[Store].[USA].[CA],\n&quot; +
&gt;             &quot;     [Store].[USA].[WA]},\n&quot; +
&gt;             &quot;    {[Customers].[USA].[WA].[Issaquah].[Abe Tramel],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]})' \n&quot; +
&gt;             &quot;select \n&quot; +
&gt;             &quot; OrderSet([NECJ], [Measures].[Unit Sales], DESC, Ancestor([Customers].currentMember, [Customers].[Name]), BDESC) \n&quot; +
&gt;             &quot;on 0 from [Sales]&quot;;
&gt;         assertQueryReturns(query,
&gt;             fold(
&gt;                 &quot;Axis #0:\n&quot; +
&gt;                 &quot;{}\n&quot; +
&gt;                 &quot;Axis #1:\n&quot; +
&gt;                 &quot;{[Store].[All Stores].[USA].[WA], [Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n&quot; +
&gt;                 &quot;{[Store].[All Stores].[USA].[CA], [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n&quot; +
&gt;                 &quot;{[Store].[All Stores].[USA].[CA], [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n&quot; +
&gt;                 &quot;Row #0: 33\n&quot; +
&gt;                 &quot;Row #0: 33\n&quot; +
&gt;                 &quot;Row #0: 75\n&quot;));
&gt;     }
&gt; 
&gt;     public void testOrderSetTupleMultiKeyswithVCube() {
&gt;         // WA unit sales is greater than CA unit sales
&gt;         String query =
&gt;             &quot;with \n&quot; +
&gt;             &quot;  set [CJ] as \n&quot; +
&gt;             &quot;    'CrossJoin( \n&quot; +
&gt;             &quot;    {[Position].[Store Management].children},\n&quot; +
&gt;             &quot;    {[Customers].[USA].[WA].[Issaquah].[Abe Tramel],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]})' \n&quot; +
&gt;             &quot;select \n&quot; +
&gt;             &quot;  [Measures].[Org Salary] on columns, \n&quot; +
&gt;             &quot;  OrderSet([CJ], [Position].currentMember, BASC, Ancestor([Customers].currentMember, [Customers].[Name]), BDESC) \n&quot; +
&gt;             &quot;on rows \n&quot; +
&gt;             &quot;from [Sales vs HR]&quot;;
&gt; 
&gt;         boolean saved = props.CompareSiblingsByOrderKey.get();
&gt;         props.CompareSiblingsByOrderKey.set(true);
&gt;         Connection conn = null;
&gt;         try {
&gt;             // Use a fresh connection to make sure bad member ordinals haven't
&gt;             // been assigned by previous tests.
&gt;             conn = getTestContext().getFoodMartConnection(false);
&gt;             TestContext context = getTestContext(conn);
&gt;             // a non-sense cube just to test ordering by order key
&gt;             context = context.create(
&gt;                 null, null,
&gt;                 &quot;&lt;VirtualCube name=\&quot;Sales vs HR\&quot;&gt;\n&quot; +
&gt;                     &quot;&lt;VirtualCubeDimension cubeName=\&quot;Sales\&quot; name=\&quot;Customers\&quot;/&gt;\n&quot; +
&gt;                     &quot;&lt;VirtualCubeDimension cubeName=\&quot;HR\&quot; name=\&quot;Position\&quot;/&gt;\n&quot; +
&gt;                     &quot;&lt;VirtualCubeMeasure cubeName=\&quot;HR\&quot; name=\&quot;[Measures].[Org Salary]\&quot;/&gt;\n&quot; +
&gt;                     &quot;&lt;/VirtualCube&gt;&quot;,
&gt;                 null, null, null);
&gt; 
&gt;             context.assertQueryReturns(query,
&gt;                 fold(
&gt;                     &quot;Axis #0:\n&quot; +
&gt;                     &quot;{}\n&quot; +
&gt;                     &quot;Axis #1:\n&quot; +
&gt;                     &quot;{[Measures].[Org Salary]}\n&quot; +
&gt;                     &quot;Axis #2:\n&quot; +
&gt;                     &quot;{[Position].[All Position].[Store Management].[Store Manager], [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n&quot; +
&gt;                     &quot;{[Position].[All Position].[Store Management].[Store Manager], [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n&quot; +
&gt;                     &quot;{[Position].[All Position].[Store Management].[Store Manager], [Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n&quot; +
&gt;                     &quot;{[Position].[All Position].[Store Management].[Store Assistant Manager], [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n&quot; +
&gt;                     &quot;{[Position].[All Position].[Store Management].[Store Assistant Manager], [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n&quot; +
&gt;                     &quot;{[Position].[All Position].[Store Management].[Store Assistant Manager], [Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n&quot; +
&gt;                     &quot;{[Position].[All Position].[Store Management].[Store Shift Supervisor], [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n&quot; +
&gt;                     &quot;{[Position].[All Position].[Store Management].[Store Shift Supervisor], [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n&quot; +
&gt;                     &quot;{[Position].[All Position].[Store Management].[Store Shift Supervisor], [Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n&quot; +
&gt;                     &quot;Row #0: \n&quot; +
&gt;                     &quot;Row #1: \n&quot; +
&gt;                     &quot;Row #2: \n&quot; +
&gt;                     &quot;Row #3: \n&quot; +
&gt;                     &quot;Row #4: \n&quot; +
&gt;                     &quot;Row #5: \n&quot; +
&gt;                     &quot;Row #6: \n&quot; +
&gt;                     &quot;Row #7: \n&quot; +
&gt;                     &quot;Row #8: \n&quot;));
&gt;         } finally {
&gt;             props.CompareSiblingsByOrderKey.set(saved);
&gt;             if (conn != null) {
&gt;                 conn.close();
&gt;             }
&gt;         }
&gt;     }
&gt; 
&gt;     public void testOrderSetConstant1() {
&gt;         String query =
&gt;             &quot;select \n&quot; +
&gt;             &quot;  OrderSet(&quot; +
&gt;             &quot;    {[Customers].[USA].[WA].[Issaquah].[Abe Tramel],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]},&quot; +
&gt;             &quot;    [Customers].[USA], BDESC) \n&quot; +
&gt;             &quot;on 0 from [Sales]&quot;;
&gt; 
&gt;         assertQueryReturns(query,
&gt;             fold(
&gt;                 &quot;Axis #0:\n&quot; +
&gt;                 &quot;{}\n&quot; +
&gt;                 &quot;Axis #1:\n&quot; +
&gt;                 &quot;{[Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n&quot; +
&gt;                 &quot;{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n&quot; +
&gt;                 &quot;{[Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n&quot; +
&gt;                 &quot;Row #0: 33\n&quot; +
&gt;                 &quot;Row #0: 75\n&quot; +
&gt;                 &quot;Row #0: 33\n&quot;));
&gt;     }
&gt; 
&gt;     public void testOrderSetDiffrentDim() {
&gt;         String query =
&gt;             &quot;select \n&quot; +
&gt;             &quot;  OrderSet(&quot; +
&gt;             &quot;    {[Customers].[USA].[WA].[Issaquah].[Abe Tramel],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young],&quot; +
&gt;             &quot;     [Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]},&quot; +
&gt;             &quot;    [Product].currentMember, BDESC, [Gender].currentMember, BDESC) \n&quot; +
&gt;             &quot;on 0 from [Sales]&quot;;
&gt; 
&gt;         assertQueryReturns(query,
&gt;             fold(
&gt;                 &quot;Axis #0:\n&quot; +
&gt;                 &quot;{}\n&quot; +
&gt;                 &quot;Axis #1:\n&quot; +
&gt;                 &quot;{[Customers].[All Customers].[USA].[WA].[Issaquah].[Abe Tramel]}\n&quot; +
&gt;                 &quot;{[Customers].[All Customers].[USA].[CA].[Woodland Hills].[Abel Young]}\n&quot; +
&gt;                 &quot;{[Customers].[All Customers].[USA].[CA].[Santa Monica].[Adeline Chun]}\n&quot; +
&gt;                 &quot;Row #0: 33\n&quot; +
&gt;                 &quot;Row #0: 75\n&quot; +
&gt;                 &quot;Row #0: 33\n&quot;));
&gt;     }
&gt; 

==== //open/mondrian/testsrc/main/mondrian/test/Main.java#96 (ktext) ====

2c2
&lt; // $Id: //open/mondrian/testsrc/main/mondrian/test/Main.java#95 $
---
&gt; // $Id: //open/mondrian/testsrc/main/mondrian/test/Main.java#96 $
49c49
&lt;  * @version $Id: //open/mondrian/testsrc/main/mondrian/test/Main.java#95 $
---
&gt;  * @version $Id: //open/mondrian/testsrc/main/mondrian/test/Main.java#96 $
198a199
&gt;             addTest(suite, OrderSetTest.class, &quot;suite&quot;);

==== //open/mondrian/testsrc/main/mondrian/xmla/XmlaBasicTest.ref.xml#67 (ktext) ====

5626a5627,5636
&gt;                         &lt;FUNCTION_NAME&gt;OrderSet&lt;/FUNCTION_NAME&gt;
&gt;                         &lt;DESCRIPTION&gt;Arranges members of a set,
&gt;                             optionally preserving or breaking the hierarchy.&lt;/DESCRIPTION&gt;
&gt;                         &lt;PARAMETER_LIST&gt;(none)&lt;/PARAMETER_LIST&gt;
&gt;                         &lt;RETURN_TYPE&gt;1&lt;/RETURN_TYPE&gt;
&gt;                         &lt;ORIGIN&gt;1&lt;/ORIGIN&gt;
&gt;                         &lt;INTERFACE_NAME/&gt;
&gt;                         &lt;CAPTION&gt;OrderSet&lt;/CAPTION&gt;
&gt;                     &lt;/row&gt;
&gt;                     &lt;row&gt;

</PRE>
</BLOCKQUOTE>
</BODY>
</HTML>