### Description

As we have more rules doing analysis across plans, a number of methods are useful to add to the `QueryPlan` class itself, mainly around expressions:

```
- expressions() - return the expressions declared by this plan
- references() - return the references from the expressions above
```
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizer.java
var remaining = seenProjection.get() ? removeUnused(aggregate.aggregates(), used) : null;
                        
                        if (remaining != null) {
                            if (remaining.isEmpty()) {
                                recheck = true;
                                p = aggregate.child();
                            } else {
                                p = new Aggregate(aggregate.source(), aggregate.child(), aggregate.groupings(), remaining);
                            }
                        }
                        seenProjection.set(Boolean.TRUE);
                    } else if (p instanceof Eval eval) {
                        var remaining = seenProjection.get() ? removeUnused(eval.fields(), used) : null;
                        
                        if (remaining != null) {
                            if (remaining.isEmpty()) {
                                p = eval.child();
                                recheck = true;
                            } else {
                                p = new Eval(eval.source(), eval.child(), remaining);
                            }
                        }
                    } else if (p instanceof Project) {
                        seenProjection.set(Boolean.TRUE);
                    }
                } while (recheck);
                used.addAll(references(p));
                
                return p;
            });