It seems to me that the problem is in pqpath.c pq_get_last_result() function. The loop assumes PQgetResult won't block, which seems always the case with the direct connection, but PQisBusy() actually returns 1 connecting through the tunnel.
So at a first read, the simplification given by pq_get_last_result() is broken: the results should be returned one by one in the normal loop going through PQisBusy(), PQconsumeInput(), and the polling machinery. It seems a chunky refactoring but as it is now I don't like pq_get_last_result() anymore :(
What changes should I made to fix this? Or it should be fixed in psycopg2 itself.