I too was thinking of network timeouts (and in such a case I thought that even if PQsendQuery was run successfully, the following PQgetResult would have to block for a long time). And yes, that situation can occur for normal queries, but in such a query, the extra functions call is done only when aborting the remote transaction, in a way libpq functions don't have to wait for a network timeout.
I've copied the ERROR-behavior that was already done in postgresGetAnalyzeInfoForForeignTable(), which I assume will be to your liking.
Structural error checks (how many columns does the result set have, etc) are now elog errors, bad PQresultStatus-es are now errors as well.
The warnings when the query ran fine but no data was found are now NOTICE level.
I forgot to mention this, but when we have reltuples = 0 for v14 or later, the patch tries to import both relstats and attstats, but in that case I think it would be OK to do so only for the former for the consistency with the normal processing. What do you think about that?
I've update the logic that we don't try to fetch attrstats if the reltuples is 0 or -1, and though the comments still mention the difference in server version, the code behaves the same in new versions and old. My thinking is that either value means "you are not going to find attstats" and our reaction is the same either way.
I've also moved the column matching - finding which rows of the attstats result set match to which columns in the local table - up into the fetch portion, something you had mentioned wanting to see as well. This resulted in some significant refactoring, but overall I think you will find the changes for the better.