Subject: Re: Proposal to allow setting cursor options on Portals

From: Jacob Champion <[email-masked]>
Date: Wed, 7 Jan 2026 16:39:33 -0800

On Wed, Jan 7, 2026 at 6:48 AM Jelte Fennema-Nio <[email-masked]> wrote:
> I had a quick discord chat with Dave. And we don't disagree much with
> each other: We both would like to use a version bump for these kinds
> of very simple to implement features.

I asked because I'm worried that the strongest technical argument for
this strategy is "it's simpler (for us)", outweighing all
consideration of downstream consequences. I'm not really on board with
that.

Dave seems not to be particularly worried about our compatibility with
third parties. You seem to be hoping to _force_ clients to update,
even if they disagree with you that they need the new features. I
think I'm on record as saying these are both bad starting points when
making changes to a widely implemented protocol. (If not, now I am.)
That combination will burn hard-earned trust and goodwill.

> Having a single version is only 1 option,

Seems like clients must support 3.0 up to 3.N in practice, and test
all of those. If you want a feature in 3.6 and the server says it only
supports 3.4, you're speaking 3.4 now. That's still N options.

You're saying "well hopefully clients don't actually have to support
all of them," but I don't think you gave a reason why that would be
okay for a production implementation. Is there an unstated assumption
here, that we'll eventually drop support for 3.0 at some point
relatively soon? (And then 3.2, and then...) If so, I'd prefer to
focus the conversation on that assumption. Because that seems like a
complete nonstarter to me, personally.

--Jacob

---

From: Tom Lane <[email-masked]>
Date: Wed, 07 Jan 2026 21:51:04 -0500

> complete nonstarter to me, personally.

After we introduced protocol version 3.0, it took us 18 years to drop
support for version 2 (2003..2021).  We introduced 3.2 in 2025, so on
the same schedule we won't drop 3.0 support before 2043.  If anyone's
thinking that it can happen significantly faster this time around,
I'm here to disabuse you of that notion.  Considering how much larger
the Postgres ecosystem is now than it was in 2003, it will probably
take *longer* for everything to be sufficiently on-board for another
hard break.

I'm pretty bemused by this entire discussion.  We have a perfectly
good design for handling new protocol features without any hard
protocol break, so I don't understand why people are insisting on
doing things incompatibly when they could be doing them compatibly.
I quote from Robert's commit ae65f6066 (the same one that invented
NegotiateProtocolVersion):

    In addition, if the startup packet includes name/value pairs where
    the name starts with "_pq_.", assume that those are protocol options,
    not GUCs.  Include those we don't support (i.e. all of them, at
    present) in the NegotiateProtocolVersion message so that the client
    knows they were not understood.  This makes it possible for the
    client to request previously-unsupported features without bumping
    the protocol version at all; the client can tell from the server's
    response whether the option was understood.

I think that the right way forward is that the protocol version
stays at 3.2 for several decades more, and we implement requests for
individual protocol-level features through the "_pq_." mechanism.
I would not have any use for 3.2 as such at all, except that
asking for that is needed to ensure that the server knows about
NegotiateProtocolVersion.

			regards, tom lane

---

From: Jelte Fennema-Nio <[email-masked]>
Date: Thu, 8 Jan 2026 08:38:28 +0100

> supports 3.4, you're speaking 3.4 now. That's still N options.

I meant 1 per yearly set of protocol features. So if there's M years
with protocol features and on average N features per each of those
years the amount of different options to consider over time are:
For only version bumps: M
For completely orthogonal protocol extensions: M*N
For completely non-orthogonal options: (M*N)^2

> okay for a production implementation.

Clients don't have to care about every postgres version that ever
existed, or every possible proxy in existence. In 5 years, a client
author might just say: well I only care about my client being able to
connect to supported postgres servers, so if a server downgrades to
protocol 3.0 I close the connection.

This is a decision for the client author to make and for the client
author only. And they don't need to care about any of the other
clients in existence at all. Only the servers their client should be
able to connect to.

> Is there an unstated assumption

No, that's not what I meant. Because postgres has to care about the
clients it wants to support. And that's all of them. There will
probably be clients supporting only 3.0 for a very long time. So the
postgres server will need to be supporting 3.0 for a very long time.

---

From: Tom Lane <[email-masked]>
Date: Thu, 08 Jan 2026 14:22:50 -0500

Robert Haas <[email-masked]> writes:
> That sounds like the right approach to me. Note that I have also
> previously expressed my disagreement with the idea of bumping the
> protocol version regularly. I'm not entirely comfortable with the idea
> of using protocol extensions for everything, because I really imagined
> that they would be used for larger features that made a cluster of
> related changes rather than solitary changes, and that there wouldn't
> be many of them.

I kind of doubt that there will ever be many of them, but if we start
to feel like there's a lot, we could invent abbreviations: single
feature names that clients can ask for that are defined to represent
a particular set of older features.  But I'd argue that those sets
should be groups of related functions, not "whatever random stuff
exists as of Postgres 27".  I think it'll be highly useful for clients
to declare which features they want, rather than leave people
wondering exactly which features this client intends to support.

			regards, tom lane

---

From: Dave Cramer <[email-masked]>
Date: Fri, 9 Jan 2026 13:20:34 -0500

> wondering exactly which features this client intends to support.

For the particular case of adding the ability to create holdable cursors at
the protocol level which is what my patch is concerned with I don't think
it's even necessary to bump the protocol or create a protocol extension.
The change in the message is backward compatible and clients only need to
know that after version 18 they can use the message to create a holdable
cursor. I would argue that this patch is just rectifying an oversight in
the original protocol.

Dave

---

From: Robert Haas <[email-masked]>
Date: Wed, 14 Jan 2026 14:24:09 -0500

On Fri, Jan 9, 2026 at 1:20 PM Dave Cramer <[email-masked]> wrote:
> For the particular case of adding the ability to create holdable cursors at the protocol level which is what my patch is concerned with I don't think it's even necessary to bump the protocol or create a protocol extension.
> The change in the message is backward compatible and clients only need to know that after version 18 they can use the message to create a holdable cursor. I would argue that this patch is just rectifying an oversight in the original protocol.

I'm not sure what I think about the patch itself, but I find myself
somewhat in agreement with this logic. If the server is supposed to
start sending something different to the client, or the client must
send something different to the server, that's clearly got to be
negotiated. But I wonder whether we should just consider slipping
things like this into the protocol without bumping the version at all.
That makes me a bit nervous because it kind of makes a mockery of the
idea of a version number, but it's also not entirely without
precedent. For example, COPY BOTH mode didn't use to exist, and now it
does, and the protocol version number didn't change in the process.

-- 
Robert Haas
EDB: http://www.enterprisedb.com

---

From: Jelte Fennema-Nio <[email-masked]>
Date: Thu, 15 Jan 2026 00:12:03 +0100

On Wed, 14 Jan 2026 at 20:24, Robert Haas <[email-masked]> wrote:
> But I wonder whether we should just consider slipping

I feel like I've said this many times already, but I really do not
understand why there's such a hesitation on bumping the minor protocol
version. Bumping the minor protocol version has zero downsides to me.
IMO we could bump it every PG release even if we don't make any
changes to the protocol. Nothing would break. The only thing that
breaks things is requesting anything other than 3.0, because most
proxies haven't implemented NegotiateProtocolVersion. But hopefully
that will be a concern of the past once Jacob merges the grease
stuff[1]. After that it doesn't matter what minor version is
requested, if it's 3.2, 3.3, or 3.456. Requesting any of those will
not cause breakage.

> does, and the protocol version number didn't change in the process.

I think CopyBoth is very special, because it's only used in logical
replication. In a sense it's practically a protocol extension, because
it is only sent by the server if the client explicitly requested it
through a keyword in the StartupMessage (replication=database).

As a maintainer of a proxy, I'd be pretty annoyed if clients start
sending fields or messages that I don't expect, without *something* in
the StartupMessage advertising that that will happen. This specific
patch would be fine for pgbouncer, because it only parses the first
few fields of the Bind message. But e.g pgcat seems to parse the whole
Bind message[2]

[1]: https://www.postgresql.org/message-id/flat/[email-masked]
[2]: https://github.com/postgresml/pgcat/blob/5b038813eb14f181434ab7b5509e74d9b1fe123b/src/messages.rs#L1019-L1064

P.S. This *really* is my final on-list message on the topic of bumping
protocol version numbers . Off-list I'm happy to explain to people and
maybe I'll reconsider re-opening the discussion in a few years. But
the current discussion is only going in circles, and I'd rather spend
my energy on something that is useful.

---

From: Tom Lane <[email-masked]>
Date: Wed, 14 Jan 2026 18:30:14 -0500

> version. Bumping the minor protocol version has zero downsides to me.

I think you have that backwards.  The right way to think about it
is that bumping the minor version has zero upside.  What we actually
want is for the client and server to agree on what specific optional
features they will use, and we have a design that allows doing that
in a fine-grained, extensible way.  We don't need to change the
protocol version number ever again, as long as we use protocol
options correctly.

Having said that, I share Robert's distaste for "silent" protocol
bumps that change the behavior without any negotiation.

			regards, tom lane

---

From: Dave Cramer <[email-masked]>
Date: Thu, 15 Jan 2026 05:33:11 -0500

Dave Cramer

> protocol version number ever again, as long as we use protocol

I would argue in the case of "cursor with hold" this should have been in
the original protocol.
This is not an added feature this just enables an existing feature in the
server. This is not unlike widening the cancel key.
Something like encryption would be a feature that I could see using the
extension mechanism

> bumps that change the behavior without any negotiation.
My understanding reading his message he was in favour of it

As for proxies or "middleboxes" I will concede that not advertising that we
are going to change that message is a non-starter

Dave

---

From: Robert Haas <[email-masked]>
Date: Thu, 15 Jan 2026 14:00:00 -0500

On Thu, Jan 15, 2026 at 5:33 AM Dave Cramer <[email-masked]> wrote:
> I would argue in the case of "cursor with hold" this should have been in the original protocol.
> This is not an added feature this just enables an existing feature in the server. This is not unlike widening the cancel key.
> Something like encryption would be a feature that I could see using the extension mechanism

I agree that encryption is a better fit for the extension mechanism.
But I don't really agree that this isn't a new feature. Exposing
existing functionality in new ways counts as a new feature in my book.

> My understanding reading his message he was in favour of it

Well, my feelings are mixed on that point, honestly. If the server
needs to know whether the client supports something, you pretty much
have to have a protocol negotiation of some kind, whether that's a
version bump or an extension. Otherwise, the server simply has no way
to find out what the client supports. In the opposite direction, it's
more fuzzy: the client can see the server version number, and so can
draw some conclusions on that basis. We have used this for protocol
changes in the past. However, something like changing the cancel key
or adding cursor with hold affects a lot more clients than adding
CopyBoth that is only triggered by a very specific command in a
special mode. And it's not that theoretically appealing to conflate
the *protocol* version number with the *server* version number.
Sometimes things that are not theoretically appealing are nonetheless
pragmatically appealing. But it is not really clear to me whether this
is one of those cases, and it sounds like Tom and Jacob think it
isn't. I could probably be persuaded either way on the best thing to
do here in a vacuum, but I'm strongly disinclined to argue against two
committers who have a firm position on the topic already.

I think what I like least about this proposal is the feeling that
we're about to embark on a long slippery slope of changing the
protocol a little bit in every new PG version. The cancel key thing is
a small change, look here's another. If we just keep doing that, we'll
end up with either a lot of minor version bumps or a lot of
extensions. I don't foresee a good outcome either way. This is a
widely used, widely adopted protocol. The idea that we can just start
tweaking it a little bit every year and have nothing bad happened
seems wild, regardless of how we do the tweaking.

> As for proxies or "middleboxes" I will concede that not advertising that we are going to change that message is a non-starter

Yeah.

---

From: Dave Cramer <[email-masked]>
Date: Thu, 15 Jan 2026 14:11:22 -0500

On Thu, 15 Jan 2026 at 14:00, Robert Haas <[email-masked]> wrote:

> > This is not an added feature this just enables an existing feature in
> the server. This is not unlike widening the cancel key.

This leaves us with an all or nothing solution, which practically means we
do nothing, since we have to wait until we have a sufficient backlog of
changes or features to change the protocol. I see that as untenable, unless
you are now advocating for using extensions for everything ?

Dave

---

From: Hannu Krosing <[email-masked]>
Date: Thu, 15 Jan 2026 22:06:23 +0100

First, let me say that I very much support getting this into the wire protocol.

As for ways to extend the protocol, I have been myself working on
another patch + extension where one can return extra info in
ReadyForQuery message

The first things to add are
* CommitLSN so we can make use of ability to WAIT FOR LSN on replica
and two connection-pooling helpers
* a flag telling that there are HOLD CURSORS
* a flag telling that there are temp tables

This extra feedback is enabled by setting a flag, so no flag --
nothing to confuse the client.
The extras themselves are uniform (length, tag, data) so client can
ignore any tag they do not recognize.

> On Thu, 15 Jan 2026 at 14:00, Robert Haas <[email-masked]> wrote:
...
>> seems wild, regardless of how we do the tweaking.

I think "tweaking ait  little bit" and only whhere needed is exactly
the right approach, if it can be cleanly isolated.

My approach to protocol extension modulation *is* the ability to
enable different parts of the protocol individually.

For example the protocol extension to allow cursor/portal flags could
be implemented this way

Client has to set a flag to PROTOCOL_PORTAL_OPTIONS=true to tell the
server that new protocol messages are coming
- If flag setting fails, client will not send the new protocol messages
- If flag setting succeeds, then it is ok to send the new messages
corresponding to the flag.

This way the extra packets are disconnected from protocol version and
can be enabled/disabled individually and per connection

And it also lets one cleanly backport the change as needed without
affecting anything else.

> This leaves us with an all or nothing solution, which practically means we do nothing, since we have to wait until we have a sufficient backlog of
> changes or features to change the protocol. I see that as untenable, unless you are now advocating for using extensions for everything ?
>
> Dave

---

From: Dave Cramer <[email-masked]>
Date: Wed, 4 Mar 2026 09:26:05 -0500

On Thu, 15 Jan 2026 at 16:06, Hannu Krosing <[email-masked]> wrote:

> > This leaves us with an all or nothing solution, which practically means
> we do nothing, since we have to wait until we have a sufficient backlog of
> > changes or features to change the protocol. I see that as untenable,
> unless you are now advocating for using extensions for everything ?
> >
> > Dave
>

I have modified the patch to use protocol options instead of protocol
version

See new version attached

---

From: Dave Cramer <[email-masked]>
Date: Wed, 4 Mar 2026 10:35:48 -0500

> I have modified the patch to use protocol options instead of protocol

resending with a different patch name as I think the commitfest app won't
pick it up with the same name

---

From: Dave Cramer <[email-masked]>
Date: Tue, 17 Mar 2026 10:41:41 -0400

Patch rebased, now ready for review

Dave Cramer

On Wed, 4 Mar 2026 at 10:35, Dave Cramer <[email-masked]> wrote:

>>> > This leaves us with an all or nothing solution, which practically
>>> means we do nothing, since we have to wait until we have a sufficient
>>> backlog of

---

From: Dave Cramer <[email-masked]>
Date: Wed, 18 Mar 2026 14:00:25 -0400

>> pick it up with the same name
Apparently that patch does not apply. Checked this one against master

Dave

---

From: Sami Imseih <[email-masked]>
Date: Wed, 18 Mar 2026 19:52:22 -0500

Hi,

> Apparently that patch does not apply. Checked this one against master

I spent some time today reviewing the discussion and testing the patch. I have
some comments:

# 1. Remove protocol 3.3 version bump, since we will be using the protocol
extension mechanism. Right?

+ * Test holdable cursors using protocol 3.3 cursor options in Bind message.
+ */
+static void
+test_holdable_cursor(PGconn *conn)
+{
+       PGresult   *res;
+
+       fprintf(stderr, "holdable cursor... ");
+
+       /* Verify protocol 3.3 */
+       if (PQfullProtocolVersion(conn) < 30003)
+               pg_fatal("protocol 3.3 required, got %d",
PQfullProtocolVersion(conn));
+

+       if (strcmp(value, "3.3") == 0)
+       {
+               *result = PG_PROTOCOL(3, 3);
+               return true;
+       }

# 2. Handle case where _pq_.holdable_portal is rejected in
pqGetNegotiateProtocolVersion3

I was testing on a patched client and unpatched server with a
connection string of:
"host=localhost dbname=postgres holdable_portal=1"

I received this error:

```
failed: received invalid protocol negotiation message: server reported
an unsupported parameter that was not requested
("_pq_.holdable_portal")
```

The patch only checks _pq_.test_protocol_negotiation (the grease test
parameter), but we also need to deal
with the rejected extension parameters, rather than fall through to the error.

@@ -1544,6 +1547,16 @@ pqGetNegotiateProtocolVersion3(PGconn *conn)
                        strcmp(conn->workBuffer.data,
"_pq_.test_protocol_negotiation") == 0)
                {
                        found_test_protocol_negotiation = true;
+                       continue;
+               }
+
+               /*
+                * Handle rejected protocol extensions we requested. Disable the
+                * corresponding feature so the client doesn't try to use it.
+                */
+               if (strcmp(conn->workBuffer.data, "_pq_.holdable_portal") == 0)
+               {
+                       conn->holdable_portal_enabled = false;
                }
                else
                {

The above is the change I tested with.
This will be the pattern for other protocol extensions that will be
added in the future as well.

Also, should there also be an API such `PQholdablePortalEnabled` which
takes a connection and
returns if conn->holdable_portal_enabled is enabled? This lets the
client detect at
runtime whether the extension was enabled, or disabled because the
server rejected it.

# 3. Remove PQsendQueryPreparedWithCursorOptions

This was also asked earlier [1], but I don't see an answer. Why do we need this?
Wouldn't PQsendBindWithCursorOption() be sufficient?

# 4. PQsendBindWithCursorOptions

a. Looking at other PQSend patterns, it looking like
PQsendBindWithCursorOptions/PQsendQueryPreparedWithCursorOptions are
missing
`pqAppendCmdQueueEntry(conn, entry)` before returning.

b. The last message type sent in `PQsendBindWithCursorOptions` is a
describe, so the query class should be PGQUERY_DESCRIBE, right?

c. Require a named portal (not just for the HOLD case). An unnamed
portal will not be executed in this API, so
subsequent calls to this API with an unnamed portal will just
overwrite the last one.

# 5. Replace hex with constants defined in libpq-fe.h which mirror parsenodes.h

for example 0x0020 → PQ_CURSOR_OPT_HOLD, etc.

Although I am not sure how these could remain in sync with core.

# 6. In the test_holdable_cursor, can we use PQsendClosePortal() instead of
"CLOSE"? Not for this patch, but it will be good to also have an API
for FETCH.

[1] [https://www.postgresql.org/message-id/CADK3HH%2B9V58vJzCkgvMwk2fyaCtYwr-Dv5em7rXzgUiVrnpuFA%40mail.gmail.com]

--
Sami Imseih
Amazon Web Services (AWS)

---

From: Sami Imseih <[email-masked]>
Date: Mon, 23 Mar 2026 20:59:11 -0500

Hi,

One thing that just occurred to me is why must we limit
the new protocol extension to holdable cursors?

PQsendBindWithCursorOptions() will work with any cursor option,
Maybe it should be called "_pq_.named_portal" or perhaps better "_pq_.cursor"?

---

From: Dave Cramer <[email-masked]>
Date: Tue, 24 Mar 2026 06:01:05 -0400

> Maybe it should be called "_pq_.named_portal" or perhaps better

Well there are currently ways to name a portal with V3 so named portal
doesn't make sense.

_pq_.cursor would be fine.

Dave

---

From: Sami Imseih <[email-masked]>
Date: Tue, 24 Mar 2026 15:54:41 -0500

> Well there are currently ways to name a portal with V3 so named portal doesn't make sense.
>

I spent some time today on this, and implemented this as a new protocol
extension called _pq_.protocol_cursor, and the libpq option will also have
the same name. Maybe someone else has a better name, but I like this
as it differentiates from a sql-level cursor. Maybe someone has a better
suggestion.

All the cursor options can be passed, though CURSOR_OPT_BINARY is
irrelevant in the extended query protocol as noted here [1]. Binary output is
controlled by the result format codes on the FETCH instead. So,
CURSOR_OPT_BINARY can be passed as a cursor option, but will be
silently ignored.

I added a new test module libpq_protocol_cursor that expands the
tests to cover all options, except for cursor sensitivity. Also, there
are tests for multiple flags, enabling/disabling the extension and
unnamed portal being used ( which should not be allowed ).

> Also, should there also be an API such `PQholdablePortalEnabled` which

I did not implement this and instead rely on
if (cursorOptions != 0 && !conn->protocol_cursor_enabled) to reject the
usage of the API. So, if the API is called with cursor options but
protocol_cursor is not enabled, we reject the attempt. So, I am
not sure this Enabled() API has any value after thinking about it a bit more.

> for example 0x0020 → PQ_CURSOR_OPT_HOLD, etc.

Also, did not implement this since as mentioned earlier, I am not sure
how to keep these in sync with core. The docs do mention that
parsenodes.h is to be referenced for the bitmasks that are defined
as CURSOR_OPT_*. That may be good enough.

See v3 attached.

[1] https://www.postgresql.org/docs/18/protocol-flow.html#PROTOCOL-FLOW-EXT-QUERY

---

From: Jelte Fennema-Nio <[email-masked]>
Date: Wed, 25 Mar 2026 08:27:05 +0100

> CURSOR_OPT_BINARY can be passed as a cursor option, but will be

This is what this patch originally did in one of the earlier versions.
And if I understand correctly it was changed after this feedback from
me:

On Sun, 14 Dec 2025 at 14:41, Jelte Fennema-Nio <[email-masked]> wrote:
> As mentioned upthread, I'm not sure BINARY makes sense. For any other
> options, the protocol docs should specify which ones are allowed and
> what their bits are. Looking at the DECLARE docs[2].
> 1. I think supporting ASENSITVE/INSENSITIVE/SENSITIVE bits is
> unnecessary, since postgres cursors are always INSENSITIVE.
> 2. For SCROLL vs NO SCROLL, it would be nice if we could get rid of
> the intermediate mode where if neither SCROLL or NO SCROLL is
> specified, it's still SCROLL sometimes. I'm not sure backwards
> compatibility would allow that, i.e. can you currently sometimes do a
> BACKWARD scan on a portal created with Bind. I guess we could make it
> so that if you specify the portal flags, then you have to be explicit
> abuot specifying SCROLL or NO SCROLL
> 3. All the flags with no SQL variant probably shouldn't be
> configurable through the protocol too (e.g. CURSOR_OPT_FAST_PLAN)

---

From: Jelte Fennema-Nio <[email-masked]>
Date: Wed, 25 Mar 2026 08:46:35 +0100

> _pq_.cursor would be fine.

I think that makes sense as a name for the option. I think adding flag
support for SCROLL and NO SCROLL would make sense in that case.

Some notes on the patch (but I didn't look look at the client side
libpq code in detail):

For the protocol definition I'd like a few changes:
1. I'd like the new field in the bind message that you add to be
described as an extension bitmap, not specifically for cursor options,
so that future extensions could add bits too it too.
2. Related to that, I think the used bits should not align with the
internal bits. Having the only valid flag bit be 0x0020 is kinda
weird. Let's just make that 0x0001. We could update the internal ones
to match if desired, but I think it's fine for the protocol bits to
differ from the bits in the postgres server.

Docs still mention CURSOR_OPT_BINARY, but support for that has been
removed from the code afaict (which I think is indeed what should
happen)

There's a bunch of protocol version 3.3 code still around, which
should be removed now that the protocol option is added.

PQsendBindWithCursorOptions and PQsendQueryPreparedWithCursorOptions
should error out if conn->holdable_portal_enabled is false. Right now
it silently skips the cursor options if the connection does not
support the protocol extension.

There should be a libpq function to inspect whether the connection
supports cursor options, so some kind of graceful fallback logic can
be implemented by the application when it's not supported.

libpq docs are missing

---

From: Dave Cramer <[email-masked]>
Date: Wed, 25 Mar 2026 10:34:42 -0400

> be implemented by the application when it's not supported.

Attached is v4 of the patch
Co-Authored by Sami Imseih

Adds docs and test module

Dave

---

From: Jelte Fennema-Nio <[email-masked]>
Date: Mon, 6 Apr 2026 11:37:01 +0200

> Adds docs and test module

It's looking much more finished

> The portal can
>       later be operated on with cursor commands such as FETCH, MOVE,
>       and CLOSE.

This made me realize that, adding the Bind side of cursors is only
half of the equation. The "Execute" message should also gain new
behaviour to support all the same functionality as FETCH and MOVE. I
think we can do that fairly easily by adding similar flags to Execute.
I think we'd need three flags:
1. MOVE
2. BACKWARD
3. ABSOLUTE

I do realize the scope creep of this, but it feels that without
addressing Execute we have a half-finished feature. That could be
fine, but then I don't think we should call the option
_pq_.protocol_cursor. Because that sounds like it solves the whole
half-baked protocol-level cursor implemention that we currently have.
Maybe _pq_.cursor_bind instead.

I think the "protocol_" part in _pq_.protocol_cursor is duplicative.
The _pq_ part already indicates that it's a protocol option, so I'd
leave that out.

>         <symbol>PQ_BIND_CURSOR_SCROLL</symbol> (scroll),
>        <symbol>PQ_BIND_CURSOR_NO_SCROLL</symbol> (no scroll), and
>        <symbol>PQ_BIND_CURSOR_HOLD</symbol> (hold).
>        These are defined in <filename>libpq-fe.h</filename>.

and

>        <para>
>         Bitmap set by protocol extensions.
>        </para>

I think the Message Formats page should list the actual flag values
that are valid. The protocol docs should not require you to look at
the postgres source code.

        /*
         * Only override the default cursorOptions when the client has
         * explicitly set flags.  A value of 0 means no cursor options were
         * requested, so keep the CreatePortal defaults.
         */

What is the difference between setting cursorOptions = 0 and the
CreatePortal defaults?

>     {"protocol_cursor", NULL, "0", NULL,
>        "Protocol-Cursor", "", 1,
>    offsetof(struct pg_conn, protocol_cursor)},

In my GoAway patchset I linked enabling the protocol extension to the
user requesting protocol v3.2 (or higher).

>         /* Reject any bits we don't recognize */
>         if (bind_ext_flags & ~0x0007)

Let's use PQ_BIND_CURSOR_VALID_FLAGS here too instead of this magic number.

> src/test/modules/libpq_protocol_cursor/libpq_protocol_cursor.c

I think it'd be better to put these tests in the libpq_pipeline test
file. Then we can keep all the libpq tests together so they can share
the helper logic.