So, I can propose two options. First - don't clean only the current root structure, but also make cleanup of the parent. Although it looks safe, I am not happy with this approach - it seems too simple: we should have a genuine reason for such a cleaning because it potentially adds overhead. The second option is to add a flag for not altering queries in remove_nulling_relids() - it looks like a mistake when we have two different query trees in the root and its parent. Also, it reduces memory usage a bit. So, if my analysis is correct, it is better to use the second way (see attachment).
Alternatively, can we look at subroot->parse->targetList instead of subquery->targetList where we call estimate_num_groups on the output of the subquery?