Index: java/engine/org/apache/derby/impl/sql/compile/OrderByColumn.java
===================================================================
--- java/engine/org/apache/derby/impl/sql/compile/OrderByColumn.java	(revision 628123)
+++ java/engine/org/apache/derby/impl/sql/compile/OrderByColumn.java	(working copy)
@@ -196,8 +196,14 @@
 			ResultColumnList targetCols = target.getResultColumns();
 			columnPosition = ((Integer)expression.getConstantValueAsObject()).intValue();
 			resultCol = targetCols.getOrderByColumn(columnPosition);
-			
-			if (resultCol == null) {
+
+			/* Column is out of range if either a) resultCol is null, OR
+			 * b) resultCol points to a column that is not visible to the
+			 * user (i.e. it was generated internally).
+			 */
+			if ((resultCol == null) ||
+				(resultCol.getColumnPosition() > targetCols.visibleSize()))
+			{
 				throw StandardException.newException(SQLState.LANG_COLUMN_OUT_OF_RANGE, 
 								     String.valueOf(columnPosition));
 			}
@@ -222,7 +228,37 @@
     private void resolveAddedColumn(ResultSetNode target)
     {
         ResultColumnList targetCols = target.getResultColumns();
-        columnPosition = targetCols.size() - targetCols.getOrderBySelect() + addedColumnOffset + 1;
+
+        /* Position is w.r.t. the original, user-specified result column
+         * list--which is what "visibleSize()" gives us.  I.e. To get
+         * this OrderByColumn's position, first subtract out all columns
+         * which were "pulled" into the RCL for GROUP BY or ORDER BY,
+         * then add this.addedColumnOffset. As an example, if the
+         * query was:
+         *
+         *   select sum(j) as s from t1 group by i, k order by k, sum(j)
+         *
+         * then we will internally add columns "K" and "SUM(J)" to
+         * the RCL for ORDER BY, *AND* we will add a generated column
+         * "I" to the RCL for GroupBy.  So we end up with:
+         *
+         *          (1)        (2)  (3)   (4)
+         *  select sum(j) as s, K, SUM(J), I from t1 ...
+         *
+         * So when we get here and want to resolve the added column
+         * "SUM(J)", we take the total number of VISIBLE columns,
+         * which is 1 (i.e. 4 total minus 1 for GROUP BY minus
+         * 2 for ORDER BY), and we add the "addedColumnOffset" for
+         * SUM(J), which will be "1" (because SUM(J) was the second
+         * ORDER BY column that we added and counting starts at 0).
+         * Then we add 1 more since addedColumnOffset is 0-based
+         * while column positions are 1-based. This gives:
+         *
+         *  position = 1 + 1 + 1 = 3
+         *
+         * which points to SUM(J) in the RCL, as we want. DERBY-3303.
+         */
+        columnPosition = targetCols.visibleSize() + addedColumnOffset + 1;
         resultCol = targetCols.getResultColumn( columnPosition);
     }
 
Index: java/testing/org/apache/derbyTesting/functionTests/tests/lang/orderby.sql
===================================================================
--- java/testing/org/apache/derbyTesting/functionTests/tests/lang/orderby.sql	(revision 628123)
+++ java/testing/org/apache/derbyTesting/functionTests/tests/lang/orderby.sql	(working copy)
@@ -753,3 +753,53 @@
        group by trim(trailing ' ' from 'abc');
 drop table d2352;
 
+-- DERBY-3303: Failures in MergeSort when GROUP BY is used with
+-- an ORDER BY on an expression (as opposed to an ORDER BY on
+-- a column reference).
+
+create table d3303 (i int, j int, k int);
+insert into d3303 values (1, 1, 2), (1, 3, 3), (2, 3, 1), (2, 2, 4);
+select * from d3303;
+
+-- All of these should execute without error.  Note the variance
+-- in expressions and sort order for the ORDER BY clause.
+
+select sum(j) as s from d3303 group by i order by 1;
+select sum(j) as s from d3303 group by i order by s;
+select sum(j) as s from d3303 group by i order by s desc;
+select sum(j) as s from d3303 group by i order by abs(1), s;
+select sum(j) as s from d3303 group by i order by sum(k), s desc;
+select sum(j) as s from d3303 group by k order by abs(k) desc;
+select sum(j) as s from d3303 group by k order by abs(k) asc;
+select sum(j) as s from d3303 group by i order by abs(i);
+select sum(j) as s from d3303 group by i order by abs(i) desc;
+
+-- Sanity check that a DISTINCT with a GROUP BY is ok, too.
+select distinct sum(j) as s from d3303 group by i;
+
+-- Slightly more complex queries, more in line with the query
+-- that was reported in DERBY-3303.  Try out various ORDER
+-- BY clauses to make sure they are actually being enforced.
+
+select max(i) as m1, max(j) as m2, sum(k) - max(j) as mdiff
+  from d3303 group by j order by abs(sum(k) - max(j)) asc;
+
+select max(i) as m1, max(j) as m2, sum(k) - max(j) as mdiff
+  from d3303 group by j order by abs(sum(k) - max(j)) desc;
+
+select max(i) as m1, max(j) as m2, sum(k) - max(j) as mdiff
+  from d3303 group by j order by abs(sum(k) - max(j)) desc, m2 asc;
+
+select max(i) as m1, max(j) as m2, sum(k) - max(j) as mdiff
+  from d3303 group by j order by abs(sum(k) - max(j)) desc, m2 desc;
+
+-- These should all fail with error 42X77 (as opposed to an
+-- ASSERT or an IndexOutOfBoundsException or an execution time
+-- NPE).
+
+select k as s from d3303 order by 2;
+select sum(k) as s from d3303 group by i order by 2;
+select k from d3303 group by i,k order by 2;
+select k as s from d3303 group by i,k order by 2;
+
+drop table d3303;
Index: java/testing/org/apache/derbyTesting/functionTests/master/orderby.out
===================================================================
--- java/testing/org/apache/derbyTesting/functionTests/master/orderby.out	(revision 628123)
+++ java/testing/org/apache/derbyTesting/functionTests/master/orderby.out	(working copy)
@@ -1903,4 +1903,123 @@
 abc 
 ij> drop table d2352;
 0 rows inserted/updated/deleted
+ij> -- DERBY-3303: Failures in MergeSort when GROUP BY is used with
+-- an ORDER BY on an expression (as opposed to an ORDER BY on
+-- a column reference).
+
+create table d3303 (i int, j int, k int);
+0 rows inserted/updated/deleted
+ij> insert into d3303 values (1, 1, 2), (1, 3, 3), (2, 3, 1), (2, 2, 4);
+4 rows inserted/updated/deleted
+ij> select * from d3303;
+I          |J          |K          
+-----------------------------------
+1          |1          |2          
+1          |3          |3          
+2          |3          |1          
+2          |2          |4          
+ij> -- All of these should execute without error.  Note the variance
+-- in expressions and sort order for the ORDER BY clause.
+
+select sum(j) as s from d3303 group by i order by 1;
+S          
+-----------
+4          
+5          
+ij> select sum(j) as s from d3303 group by i order by s;
+S          
+-----------
+4          
+5          
+ij> select sum(j) as s from d3303 group by i order by s desc;
+S          
+-----------
+5          
+4          
+ij> select sum(j) as s from d3303 group by i order by abs(1), s;
+S          
+-----------
+4          
+5          
+ij> select sum(j) as s from d3303 group by i order by sum(k), s desc;
+S          
+-----------
+5          
+4          
+ij> select sum(j) as s from d3303 group by k order by abs(k) desc;
+S          
+-----------
+2          
+3          
+1          
+3          
+ij> select sum(j) as s from d3303 group by k order by abs(k) asc;
+S          
+-----------
+3          
+1          
+3          
+2          
+ij> select sum(j) as s from d3303 group by i order by abs(i);
+S          
+-----------
+4          
+5          
+ij> select sum(j) as s from d3303 group by i order by abs(i) desc;
+S          
+-----------
+5          
+4          
+ij> -- Sanity check that a DISTINCT with a GROUP BY is ok, too.
+select distinct sum(j) as s from d3303 group by i;
+S          
+-----------
+4          
+5          
+ij> -- Slightly more complex queries, more in line with the query
+-- that was reported in DERBY-3303.  Try out various ORDER
+-- BY clauses to make sure they are actually being enforced.
+
+select max(i) as m1, max(j) as m2, sum(k) - max(j) as mdiff
+  from d3303 group by j order by abs(sum(k) - max(j)) asc;
+M1         |M2         |MDIFF      
+-----------------------------------
+2          |3          |1          
+1          |1          |1          
+2          |2          |2          
+ij> select max(i) as m1, max(j) as m2, sum(k) - max(j) as mdiff
+  from d3303 group by j order by abs(sum(k) - max(j)) desc;
+M1         |M2         |MDIFF      
+-----------------------------------
+2          |2          |2          
+2          |3          |1          
+1          |1          |1          
+ij> select max(i) as m1, max(j) as m2, sum(k) - max(j) as mdiff
+  from d3303 group by j order by abs(sum(k) - max(j)) desc, m2 asc;
+M1         |M2         |MDIFF      
+-----------------------------------
+2          |2          |2          
+1          |1          |1          
+2          |3          |1          
+ij> select max(i) as m1, max(j) as m2, sum(k) - max(j) as mdiff
+  from d3303 group by j order by abs(sum(k) - max(j)) desc, m2 desc;
+M1         |M2         |MDIFF      
+-----------------------------------
+2          |2          |2          
+2          |3          |1          
+1          |1          |1          
+ij> -- These should all fail with error 42X77 (as opposed to an
+-- ASSERT or an IndexOutOfBoundsException or an execution time
+-- NPE).
+
+select k as s from d3303 order by 2;
+ERROR 42X77: Column position '2' is out of range for the query expression.
+ij> select sum(k) as s from d3303 group by i order by 2;
+ERROR 42X77: Column position '2' is out of range for the query expression.
+ij> select k from d3303 group by i,k order by 2;
+ERROR 42X77: Column position '2' is out of range for the query expression.
+ij> select k as s from d3303 group by i,k order by 2;
+ERROR 42X77: Column position '2' is out of range for the query expression.
+ij> drop table d3303;
+0 rows inserted/updated/deleted
 ij> 
