PostgreSQL Copy command internals
As with all open source projects, the internals of the Postgres COPY statement can be easily discovered. As one can read in the source code comments, Postgres attempts to use batch inserts by default, except in the cases where it is not possible:


when there are any BEFORE or INSTEAD OF triggers on the table.
when the target is a partitioned table and there are any statement-level insert triggers.
when the target is a foreign table.
when the target has any volatile DEFAULT expressions.
when there are any volatile expressions in the query WHERE clause.


If none of the above is true, insert will happen in batches of up to 1000 rows (the default can be changed by recompiling the Postgres server).

For partitioned tables the decision to use batch inserts is made for each partition individually.

While most of the COPY processing happens on the server, the client (psql) responsibility is to send data from a local file one 8K buffer at a time, which is evident from the psql \copy command handler source.