protected Tuple fetchNext() throws TransactionAbortedException, DbException {
        /*while (hasMoreChild1Tuples) {
            while (hasMoreChild2Tuples) { 
                // Loop through every child1 tuple in the cached page
                for (int i = child1Index; i < child1Max; i++) {
                    Tuple child1Tuple = child1Tuples[i];
                    // Loop through every child2 tuple in the cached page
                    for (int j = child2Index; j < child2Max; j++) {
                        Tuple child2Tuple = child2Tuples[j];
                        if (p.filter(child1Tuple, child2Tuple)) {
                            Tuple mergedTuple = new Tuple(getTupleDesc());
                            int numFieldsFromChild1 = child1Tuple.getTupleDesc().numFields();
                            for (int k = 0; k < numFieldsFromChild1; k++) {
                                mergedTuple.setField(k, child1Tuple.getField(k));
                            }
                            for (int k = 0; k < child2Tuple.getTupleDesc().numFields(); k++) {
                                mergedTuple.setField(k + numFieldsFromChild1, child2Tuple.getField(k));
                            }
                            child1Index = i;
                            child2Index = j + 1;
                            return mergedTuple;
                        }
                    }
                    child2Index = 0;
                }      
                // If we went through every tuple in the cached child1 page and compared
                // them with every tuple from the cached child2 page, get next child2 page
                loadMoreChild2Tuples();
            }
            // If we went through every child2 page for this child1 page, load a new
            // child1 page and reset the child2 page back to the beginning
            child2.rewind();
            loadMoreChild1Tuples();
            loadMoreChild2Tuples();
        }
        // If we went through every tuple in all pages, return null
        return null;*/

        // We'll keep child1Tuple as our 'persistent' tuple from last iteration
        // If we still have child2 tuples to go through for this child1 tuple
        while (child1.hasNext() || child1Tuple != null) {
            if (child1Tuple != null) {
                // Then go through each child2 tuple until we find one which matches
                while (child2.hasNext()) {
                    Tuple child2Tuple = child2.next();
                    if (p.filter(child1Tuple, child2Tuple)) {
                        Tuple mergedTuple = new Tuple(getTupleDesc());
                        int numFieldsFromChild1 = child1Tuple.getTupleDesc().numFields();
                        for (int i = 0; i < numFieldsFromChild1; i++) {
                            mergedTuple.setField(i, child1Tuple.getField(i));
                        }
                        for (int i = 0; i < child2Tuple.getTupleDesc().numFields(); i++) {
                            mergedTuple.setField(i + numFieldsFromChild1, child2Tuple.getField(i));
                        }
                        return mergedTuple;
                    }
                }
                // If we run out of child2 tuples, rewind so that we can check the next child1 tuple
                child2.rewind();
            }
            if (child1.hasNext()) {
                child1Tuple = child1.next();
            } else {
                break;
            }
        }
        // Lastly, we're out of tuples for child1 and child2
        return null;
    }