diff --git b/doc/src/sgml/libpq.sgml a/doc/src/sgml/libpq.sgml index 7b14366..6b95be8 100644 --- b/doc/src/sgml/libpq.sgml +++ a/doc/src/sgml/libpq.sgml @@ -7351,7 +7351,17 @@ typedef struct This function must return 1 for success, and 0 for failure. On failure this function should set the error message with PGsetRowProcessorErrMsg if the cause - is other than out of memory. + is other than out of memory. When non-blocking API is in use, + it can also return 2 for early exit + from PQisBusy function. The + supplied res + and columns values will stay valid so + row can be processed outside of callback. Caller is + responsible for tracking whether + the PQisBusy returned early from + callback or for other reasons. Usually this should happen via + setting cached values to NULL before + calling PQisBusy. diff --git b/src/interfaces/libpq/fe-protocol2.c a/src/interfaces/libpq/fe-protocol2.c index 7498580..ae4d7b0 100644 --- b/src/interfaces/libpq/fe-protocol2.c +++ a/src/interfaces/libpq/fe-protocol2.c @@ -820,6 +820,9 @@ getAnotherTuple(PGconn *conn, bool binary) rp= conn->rowProcessor(result, conn->rowProcessorParam, rowbuf); if (rp == 1) return 0; + else if (rp == 2 && pqIsnonblocking(conn)) + /* processor requested early exit */ + return EOF; else if (rp != 0) PQsetRowProcessorErrMsg(result, libpq_gettext("invalid return value from row processor\n")); diff --git b/src/interfaces/libpq/fe-protocol3.c a/src/interfaces/libpq/fe-protocol3.c index a67e3ac..0260ba6 100644 --- b/src/interfaces/libpq/fe-protocol3.c +++ a/src/interfaces/libpq/fe-protocol3.c @@ -697,6 +697,11 @@ getAnotherTuple(PGconn *conn, int msgLength) /* everything is good */ return 0; } + if (rp == 2 && pqIsnonblocking(conn)) + { + /* processor requested early exit */ + return EOF; + } /* there was some problem */ if (rp == 0)