Index: java/engine/org/apache/derby/impl/sql/compile/OptimizerImpl.java
===================================================================
--- java/engine/org/apache/derby/impl/sql/compile/OptimizerImpl.java	(revision 607771)
+++ java/engine/org/apache/derby/impl/sql/compile/OptimizerImpl.java	(working copy)
@@ -589,7 +589,32 @@
 			rewindJoinOrder();  //fall
 			permuteState = NO_JUMP;  //give up
 		}
+		else if (!joinPosAdvanced &&
+			(proposedJoinOrder[joinPosition] >= 0))
+		{
+			/* If we didn't advance the join position then the optimizable
+			 * which currently sits at proposedJoinOrder[joinPosition]--call
+			 * it OPT_TO_BE_PULLED--is *not* going to remain there. Instead,
+			 * we're going to pull that optimizable from its position and
+			 * attempt to put another one in its place.  That said, when we
+			 * try to figure out which of the other optimizables to place at
+			 * joinPosition, we'll first do some "dependency checking", the
+			 * result of which relies on the contents of assignedTableMap.
+			 * Since assignedTableMap holds info about OPT_TO_BE_PULLED,
+			 * and since OPT_TO_BE_PULLED is *not* going to remain in the
+			 * join order, we need to remove the info for OPT_TO_BE_PULLED
+			 * from assignedTableMap.  Otherwise an Optimizable which depends
+			 * on OPT_TO_BE_PULLED could incorrectly be placed in the join
+			 * order *before* OPT_TO_BE_PULLED, which would violate the
+			 * dependency and lead to incorrect results. DERBY-3288.
+			 */
+			Optimizable pullMe =
+				optimizableList.getOptimizable(
+					proposedJoinOrder[joinPosition]);
 
+			assignedTableMap.xor(pullMe.getReferencedTableMap());
+		}
+
 		/*
 		** The join position becomes < 0 when all the permutations have been
 		** looked at.
