Thread
-
Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Jelte Fennema-Nio <postgres@jeltef.nl> — 2025-12-14T14:40:25Z
A bunch of frontend tools, including psql, still used PQcancel to send cancel requests to the server. That function is insecure, because it does not use encryption to send the cancel request. This starts using the new cancellation APIs (introduced in 61461a300) for all these frontend tools. These APIs use the same encryption settings as the connection that's being cancelled. Since these APIs are not signal-safe this required a refactor to not send the cancel request in a signal handler anymore, but instead using a dedicated thread. Similar logic was already used for Windows anyway, so this also has the benefit that it makes the cancel logic more uniform across our supported platforms. Because this is fixing a security issue, it would be nice if we could backport it. I'm not sure that's realistic though, given the size/complexity of the change. I'm curious what others think about that. To be clear, I'm only really talking about backporting to PG17 and PG18 because those contain the new cancellation APIs in libpq. Backporting to even older versions would also require backporting the new cancellation APIs in libpq, which seems like a no-go. A possible follow up improvement to pg_dump would be to use threads for its parallel workers on UNIX as well. Then the Windows and Unix implementations would get even more aligned. I started looking into that, but that quickly became quite a big refactor, touching a lot of code unrelated to cancellation. So it seems better to do that in a separate follow-on patch if people are interested.
-
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Jelte Fennema-Nio <postgres@jeltef.nl> — 2026-01-02T15:14:54Z
On Sun Dec 14, 2025 at 3:40 PM CET, Jelte Fennema-Nio wrote: > A bunch of frontend tools, including psql, still used PQcancel to send > cancel requests to the server. That function is insecure, because it > does not use encryption to send the cancel request. This starts using > the new cancellation APIs (introduced in 61461a300) for all these > frontend tools. Fixed conflict after Copyright update.
-
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Jelte Fennema-Nio <postgres@jeltef.nl> — 2026-02-08T19:05:57Z
On Sun Dec 14, 2025 at 3:40 PM CET, Jelte Fennema-Nio wrote: > A bunch of frontend tools, including psql, still used PQcancel to send > cancel requests to the server. That function is insecure, because it > does not use encryption to send the cancel request. This starts using > the new cancellation APIs (introduced in 61461a300) for all these > frontend tools. Small update. Split up the fe_utils and pg_dump changes into separate commits, to make patches easier to review. Also use non-blocking writes to the self-pipe from the signal handler to avoid potential deadlocks (extremely unlikely for such blocks to occur, but better safe than sorry).
-
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Heikki Linnakangas <hlinnaka@iki.fi> — 2026-03-05T18:30:29Z
On 08/02/2026 21:05, Jelte Fennema-Nio wrote: > On Sun Dec 14, 2025 at 3:40 PM CET, Jelte Fennema-Nio wrote: >> A bunch of frontend tools, including psql, still used PQcancel to send >> cancel requests to the server. That function is insecure, because it >> does not use encryption to send the cancel request. This starts using >> the new cancellation APIs (introduced in 61461a300) for all these >> frontend tools. > > Small update. Split up the fe_utils and pg_dump changes into separate > commits, to make patches easier to review. Also use non-blocking writes > to the self-pipe from the signal handler to avoid potential deadlocks > (extremely unlikely for such blocks to occur, but better safe than sorry). Had a brief look at this: It took me a while to get the big picture of how this works. cancel.c could use some high-level comments explaining how to use the facility; it's a real mixed bag right now. The SIGINT handler now does three things: - Set CancelRequested global variable, - call callback if set, and - wake up the cancel thread to send the cancel message, if cancel connection is set. There's no high-level overview documentation or comments on how those three mechanism work or interact. It took me a while to understand that they are really separate, alternative ways to handle SIGINT, all mashed into the same signal handler function. At first read, I thought they're somehow part of the one same mechanism. The cancelConn mechanism is a global variable, which means that it can only be used with one connection in the process. That's OK with the current callers, but seems short-sighted. What if we wanted to use it for pgbench, for example, which uses multiple threads and connections? Or if we changed pg_dump to use multiple threads, like you also suggested as a possible follow-up. The "self-pipe trick" usually refers to interrupting the main thread from select(); this uses it to wake up the other, separate cancellation thread. That's fine, but again it took me a while to understand that that's what it does. Comments! This is racy, if the cancellation thread doesn't immediately process the wakeup. For example, because it's still busy processing a previous wakeup, because there's a network hiccup or something. By the time the cancellation thread runs, the main thread might already be running a different query than it was when the user hit CTRL-C. - Heikki
-
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Jelte Fennema-Nio <postgres@jeltef.nl> — 2026-03-06T02:12:49Z
On Thu Mar 5, 2026 at 7:30 PM CET, Heikki Linnakangas wrote: > It took me a while to get the big picture of how this works. cancel.c > could use some high-level comments explaining how to use the facility; > it's a real mixed bag right now. Attached is a version with a bunch more comments. I agree this cancel logic is hard to understand without them. It took me quite a while to understand it myself. (I don't think the code got any harder to understand with these changes though, the exact same complexity was already there for Windows. But I agree more commends are good.) > The cancelConn mechanism is a global variable, which means that it can > only be used with one connection in the process. That's OK with the > current callers, but seems short-sighted. What if we wanted to use it > for pgbench, for example, which uses multiple threads and connections? > Or if we changed pg_dump to use multiple threads, like you also > suggested as a possible follow-up. Allowing for multiple callers seems like scope-creep to me, and also hard to do in a generic way. I'd say IF we want that in a generic way I'd first want to see a version of that that solves the problem for pgdench/pg_dump, before generalizing it to cancel.c. > This is racy, if the cancellation thread doesn't immediately process the > wakeup. For example, because it's still busy processing a previous > wakeup, because there's a network hiccup or something. By the time the > cancellation thread runs, the main thread might already be running a > different query than it was when the user hit CTRL-C. I now noted this in one of the new comments. I don't think there's a way around this race condition entirely. It's simply a limitation of our cancel protocol (because it's impossible to specify which query on a connection should be cancelled). In theory we could reduce the window for the race, by having all frontend tools use async connections and have the main thread wait for either the self-pipe or a cancel. That way it would be more similar to the previous signal code in behaviour. That's a much bigger lift though, i.e. all PQexec and PQgetResult calls would need to be modified. My proposed change doesn't require changing the callsites at all. In interactive usage of psql it seems pretty unlikely that people will hit this race condition. In non-interactive use, it doesn't matter because you just want Ctrl-C to cancel the application (whichever query it currently runs). So I'd say it's currently not worth the complexity to do the less racy option.
-
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Heikki Linnakangas <hlinnaka@iki.fi> — 2026-03-06T19:51:22Z
On 06/03/2026 04:12, Jelte Fennema-Nio wrote: > On Thu Mar 5, 2026 at 7:30 PM CET, Heikki Linnakangas wrote: >> It took me a while to get the big picture of how this works. cancel.c >> could use some high-level comments explaining how to use the facility; >> it's a real mixed bag right now. > > Attached is a version with a bunch more comments. I agree this cancel > logic is hard to understand without them. It took me quite a while to > understand it myself. (I don't think the code got any harder to > understand with these changes though, the exact same complexity was > already there for Windows. But I agree more commends are good.) Thanks. I agree it was complicated before these patches. >> This is racy, if the cancellation thread doesn't immediately process >> the wakeup. For example, because it's still busy processing a previous >> wakeup, because there's a network hiccup or something. By the time the >> cancellation thread runs, the main thread might already be running a >> different query than it was when the user hit CTRL-C. > > I now noted this in one of the new comments. I don't think there's a way > around this race condition entirely. It's simply a limitation of our > cancel protocol (because it's impossible to specify which query on a > connection should be cancelled). That's true, but I still wonder if this could make it much worse. > In theory we could reduce the window for the race, by having all > frontend tools use async connections and have the main thread wait for > either the self-pipe or a cancel. That way it would be more similar to > the previous signal code in behaviour. That's a much bigger lift though, > i.e. all PQexec and PQgetResult calls would need to be modified. My > proposed change doesn't require changing the callsites at all. Yeah, it does have that advantage.. One simple thing we could is to remember the "generation" in the signal handler, and store it in another global variable ("cancelledGeneration" or such). In the cancel thread, check that the generation matches; otherwise the thread is about to send a cancellation to a query that already finished, and should not send it. I worry how this behaves if establishing the cancel connection gets stuck for a long time. Because of a network hiccup, for example. That's also not a new problem though; it's perhaps even worse today, if the signal handler gets stuck for a long time, trying to establish the connection. Still, would be good to do some testing with a bad network. - Heikki -
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Jelte Fennema-Nio <postgres@jeltef.nl> — 2026-03-07T00:01:03Z
On Fri, 6 Mar 2026 at 20:51, Heikki Linnakangas <hlinnaka@iki.fi> wrote: > > In theory we could reduce the window for the race, by having all > > frontend tools use async connections and have the main thread wait for > > either the self-pipe or a cancel. That way it would be more similar to > > the previous signal code in behaviour. That's a much bigger lift though, > > i.e. all PQexec and PQgetResult calls would need to be modified. My > > proposed change doesn't require changing the callsites at all. > > Yeah, it does have that advantage.. I let Claude Code take a stab at a POC for doing the same thread approach. So I could get a more accurate feeling of how big this lift would be. It's a much bigger change, but the general design is relatively straight forward. It's attached as the nocfbot patch (it's not built on top of any of the other patches, it's a separate one). > One simple thing we could is to remember the "generation" in the signal > handler, and store it in another global variable ("cancelledGeneration" > or such). In the cancel thread, check that the generation matches; > otherwise the thread is about to send a cancellation to a query that > already finished, and should not send it. >In the cancel thread, check that the generation matches; otherwise the thread is about to send a cancellation to a query that already finished, and should not send it. I took a look at this, and attached a fixup patch that does this. It uses C11 atomics because locks cannot be taken in the signal handler and the signal handler needs to read/write variables from/to two different threads. I'm not sure if it pulls it's own weight though. It seems a really unlikely scenario where the signal handler is fired during one generation, but then the cancel thread wakes up during another. I'm not sure if we should care about it. And I actually think it could make us miss cancel a SIGINT for non-interactive use cases of psql. > I worry how this behaves if establishing the cancel connection gets > stuck for a long time. Because of a network hiccup, for example. That's > also not a new problem though; it's perhaps even worse today, if the > signal handler gets stuck for a long time, trying to establish the > connection. Still, would be good to do some testing with a bad network. To make sure we're talking about the same situation, let me summarize it differently: Establishing the connection for the cancel is slow, but the actual query connection is still fast. I think this is an interesting case to consider (much more interesting than the kind of race the additional generation check could protect against). First of all because I think it can definitely happen. Especially with SSL the cancel needs several round trips, while a query on an already established connection only needs one direction latency. I can definitely see how this could cause out-of-order arrival even on stable high-latency networks. Especilaly if there's also some unfortunate packet drops. And as you suggested the failure modes are different: - With master, psql will become unresponsive until the client gets a response from the server (or tcp timeouts are hit) - With this patchset, a later query will get cancelled. I think for interactive psql usage both are annoying, but both are not the end of the world. I think I would personally prefer the current master behaviour. I'm not sure preserving it is worth all the additional code changes though to make all the applications use non-blocking APIs. In any case SSL for cancel keys is definitely worth the patchset behaviour to me (even though it sounds slightly worse). For non-interactive use (i.e. running scripts in psql or other frontend tools like vacuumdb). I don't think this situation applies. You want whatever query to be cancelled. -
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Jelte Fennema-Nio <postgres@jeltef.nl> — 2026-03-07T08:41:02Z
On Sat Mar 7, 2026 at 1:01 AM CET, Jelte Fennema-Nio wrote: > I took a look at this, and attached a fixup patch that does this. Now with the actual attachements...
-
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Jelte Fennema-Nio <postgres@jeltef.nl> — 2026-03-15T15:09:24Z
On Fri Mar 6, 2026 at 8:51 PM CET, Heikki Linnakangas wrote: > I worry how this behaves if establishing the cancel connection gets > stuck for a long time. Because of a network hiccup, for example. That's > also not a new problem though; it's perhaps even worse today, if the > signal handler gets stuck for a long time, trying to establish the > connection. Still, would be good to do some testing with a bad network. After thinking on this again, I thought of a much easier solution to this problem than the direction I was exploring in my previous response to this: We can have SetCancelConn() and ResetCancelConn() wait for any pending cancel to complete before letting them replace/remove the cancelConn. That way even in case of a bad network, we know that an already in-flight cancel request will never cancel a query from a next SetCancelConn() call. It does mean that you cannot submit a new query before we've received a response to the in-flight cancel request (either because the hiccup is reselved or because TCP timeouts report a failure). That's the current behaviour too with running PQcancel in the signal handler, and I also think that's the behaviour that makes the most sense. Attached is a patchset that does this. To ensure that it worked correctly, I mimicked network issues by running the following iptables command after already having connected with psql: sudo iptables -A INPUT -p tcp --dport 5432 -m state --state NEW -j DROP That command drops all new incoming connections to the server, but allows already established connections to continue working. Which means that any new cancel connections will not be able to connect. You can allow the traffic again with: sudo iptables -D INPUT -p tcp --dport 5432 -m state --state NEW -j DROP To see what happens when the connection attempt never goes through I used: sudo sysctl net.ipv4.tcp_syn_retries=2 sudo sysctl net.ipv4.tcp_retries2=3 This is what happens then: localhost jelte@postgres:5432-1484255=# select pg_sleep(5); ^CSending cancel request Time: 5005.833 ms (00:05.006) Could not send cancel request: connection to server at "localhost" (127.0.0.1), port 5432 failed: Connection timed out Is the server running on that host and accepting TCP/IP connections? localhost jelte@postgres:5432-1484255=# The query succeeds after 5 seconds, but the prompt does not become interactive until a little while after that when the cancel request error is also shown. -
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Heikki Linnakangas <hlinnaka@iki.fi> — 2026-03-16T09:57:47Z
On 15/03/2026 17:09, Jelte Fennema-Nio wrote: > On Fri Mar 6, 2026 at 8:51 PM CET, Heikki Linnakangas wrote: >> I worry how this behaves if establishing the cancel connection gets >> stuck for a long time. Because of a network hiccup, for example. >> That's also not a new problem though; it's perhaps even worse today, >> if the signal handler gets stuck for a long time, trying to establish >> the connection. Still, would be good to do some testing with a bad >> network. > > After thinking on this again, I thought of a much easier solution to > this problem than the direction I was exploring in my previous response > to this: We can have SetCancelConn() and ResetCancelConn() wait for any > pending > cancel to complete before letting them replace/remove the cancelConn. > > That way even in case of a bad network, we know that an already > in-flight cancel request will never cancel a query from a next > SetCancelConn() call. It does mean that you cannot submit a new query > before we've received a response to the in-flight cancel request (either > because the hiccup is reselved or because TCP timeouts report a > failure). That's the current behaviour too with running PQcancel in the > signal handler, and I also think that's the behaviour that makes the > most sense. +1. With a little extra effort, the cancellation can be made abortable too, so that you don't need to wait for the TCP timeout. I.e when ResetCancelConn() is called, the cancellation thread can immediately call PQcancelReset(). One a different topic, is there any guarantee on which thread will receive the SIGINT? It matters because psql's cancel callback sometimes calls longjmp(), which assumes that the signal handler is executed in the main thread. - Heikki
-
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Jelte Fennema-Nio <postgres@jeltef.nl> — 2026-03-17T10:31:04Z
On Mon, 16 Mar 2026 at 10:57, Heikki Linnakangas <hlinnaka@iki.fi> wrote: > +1. With a little extra effort, the cancellation can be made abortable > too, so that you don't need to wait for the TCP timeout. I.e when > ResetCancelConn() is called, the cancellation thread can immediately > call PQcancelReset(). I agree we could do that, but I don't think we should. Then we'd be getting into the exact situation where psql doesn't wait for an already in-flight cancel request to be processed by the server before sending the next query. i.e. while this would be fine if there's a network issue, it would be bad if the server is just slow to respond to the cancel request (e.g. because there's a PgBouncer in the middle that hasn't forwarded the request yet). > One a different topic, is there any guarantee on which thread will > receive the SIGINT? It matters because psql's cancel callback sometimes > calls longjmp(), which assumes that the signal handler is executed in > the main thread. Good point, I had thought about whether this mattered, but hadn't considered the callbacks. Attached is v7 that makes sure the signal is always handled by the main thread by blocking SIGINT before creating the cancel thread. I manually tested that this works by sending signals to specific threads using htop, and logging thread the id from the signal handler. Before this change the thread id would be from different threads, after this change it's always from the same one.
-
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Heikki Linnakangas <hlinnaka@iki.fi> — 2026-04-07T00:18:05Z
On 17/03/2026 12:31, Jelte Fennema-Nio wrote: > On Mon, 16 Mar 2026 at 10:57, Heikki Linnakangas <hlinnaka@iki.fi> wrote: >> +1. With a little extra effort, the cancellation can be made abortable >> too, so that you don't need to wait for the TCP timeout. I.e when >> ResetCancelConn() is called, the cancellation thread can immediately >> call PQcancelReset(). > > I agree we could do that, but I don't think we should. Then we'd be > getting into the exact situation where psql doesn't wait for an already > in-flight cancel request to be processed by the server before sending > the next query. i.e. while this would be fine if there's a network > issue, it would be bad if the server is just slow to respond to the > cancel request (e.g. because there's a PgBouncer in the middle that > hasn't forwarded the request yet). True, it can get messy fast. Hmm, perhaps you could check PQcancelStatus() and only cancel the cancellation if it hasn't sent the CancelRequest packet yet. >> One a different topic, is there any guarantee on which thread will >> receive the SIGINT? It matters because psql's cancel callback sometimes >> calls longjmp(), which assumes that the signal handler is executed in >> the main thread. > > Good point, I had thought about whether this mattered, but hadn't > considered the callbacks. Attached is v7 that makes sure the signal is > always handled by the main thread by blocking SIGINT before creating the > cancel thread. The more I look into the cancel handling in psql and other client programs, the more I want to gouge my eyes out. It was pretty messy before this patch, too, and now also we have an extra thread to worry about. For example, let's look at what happens at COPY IN, starting from HandleCopyResult(). It sets SetCancelConn(pset.db), and then calls handleCopyIn(). handleCopyIn() calls sigsetjmp(), enables the longjmp in the signal handler with "sigint_interrupt_enabled = true". and calls fgets(). So we rely on longjmp() to get out of the fgets(). Is that safe? I suppose, it's worked all these years. It's scary though, I would never do that in new code. And then you add threads to the mix... is it still safe in the presence of threads? glibc uses internal locks on FILE objects to make them thread-safe; if you longjmp() in the middle of fgets(), is the lock released? I suppose it doesn't matter if only one thread ever accesses it, but ugh. Because the signal handler longjmp()'d out, it never calls PQcancel() (or wakes up the thread, with the patch) in that case, even though the cancel connection is currently armed with SetCancelConn(). Is that intentional? If you then immediately SIGINT again, then the signal handler will PQcancel(). I guess there's some logic to that; if the signal arrives while you're blocked on the input, sending the CopyEnd probably still works. Perhaps is should be documented that the callback is called first. The cancel handling in wait_on_slots() in parallel_slot.c is surprising in a different way. It already uses async libpq calls and has a select() loop, but it still relies on the signal handler to do the cancellation. And it arbitrarily PQcancel()s only one of the connections it waits on. The comments you added in your patches help a lot, explaining the three different ways that cancellation can be handled, thanks for that. psql has a global variable, 'cancel_pressed', which is set in the signal handler callback. Is it redundant with 'CancelRequested' that's also set in the signal handler? Some ideas on how to make this less confusing: - Change pg_dump to use threads on Unix too. - Extract the cancel_pipe stuff into a helper function, and reuse it in pg_dump and in wait_on_slots() - Make the callback and SetCancelConn() mutually exclusive, so that when you call SetCancelConn(), it *replaces* the callback if any was set. To re-install it, call SetCancelCallback(). This would make them more symmetrical, and would remove the confusion on what happens with the active cancel connection if the callback longjmp()s. The SetCancelCallback()/ResetCancelCallback() calls would replace the sigint_interrupt_enabled variable. - Instead of SetCancelConn() and ResetCancelConn(), have functions like "StartCancelInBackground(conn)" and "WaitBackgroundCancel()". The cancel callback would then call StartCancelInBackground()". Not sure how much these really help, but maybe something to try out. Then there's the idea of switching to async libpq calls and select() everywhere... Another more radical idea: Could we move this functionality to libpq itself? Imagine that we had a function like PQrequestCancel(PGconn *conn) that just set a flag on the PGconn object to indicate that cancellation has been requested. When that flag is set, all blocking calls like PQexec() on the original connection drive the cancellation connection and sending the cancel request, instead of (or in addition to) polling the main connection. The new PQrequestCancel() would be safe to call from the signal handler, but then main thread would do all the work. That'd be very easy to use when applicable, but there's one pretty serious problem: it wouldn't work with async libpq calls and select() without more API changes. The internal cancel connection that's opened would have a different fd from the one returned by PQsocket(), so the application wouldn't know to poll it. - Heikki
-
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Jelte Fennema-Nio <postgres@jeltef.nl> — 2026-07-03T22:28:24Z
Attached a v8 based on this feedback, and I think much better than the previous version. The first 2 commits are simplifications of the current logic that I think make sense to merge even without the rest of the patchset. On Tue, 7 Apr 2026 at 02:18, Heikki Linnakangas <hlinnaka@iki.fi> wrote: > True, it can get messy fast. Hmm, perhaps you could check > PQcancelStatus() and only cancel the cancellation if it hasn't sent the > CancelRequest packet yet. I kinda don't wanna make this logic even more complicated. > The more I look into the cancel handling in psql and other client > programs, the more I want to gouge my eyes out. Same here... > It was pretty messy > before this patch, too, and now also we have an extra thread to worry about. Most of the complexity of the extra thread was already there for Windows. Making Windows and Unix behave more similarly actually reduces the overall complexity, IMO. > Perhaps is should be documented that the callback is called first. Done > The cancel handling in wait_on_slots() in parallel_slot.c is surprising > in a different way. It already uses async libpq calls and has a select() > loop, but it still relies on the signal handler to do the cancellation. > And it arbitrarily PQcancel()s only one of the connections it waits on. Addressed this in 0002 > The comments you added in your patches help a lot, explaining the three > different ways that cancellation can be handled, thanks for that. Yeah, I think > psql has a global variable, 'cancel_pressed', which is set in the signal > handler callback. Is it redundant with 'CancelRequested' that's also set > in the signal handler? I did some spelunking, and yes it is redundant. Fixed in 0001 > Some ideas on how to make this less confusing: > > - Change pg_dump to use threads on Unix too. I think that would be a good follow-up, but I don't think it makes sense as part of this patchset. The only thing that would make more aligned between OSes with respect to this patch is the worker process vs thread shutdown. > - Extract the cancel_pipe stuff into a helper function, and reuse it in > pg_dump and in wait_on_slots() This is what I did in the end and I think it improved the patchset a lot. > Another more radical idea: Could we move this functionality to libpq > itself? Imagine that we had a function like PQrequestCancel(PGconn > *conn) that just set a flag on the PGconn object to indicate that > cancellation has been requested. When that flag is set, all blocking > calls like PQexec() on the original connection drive the cancellation > connection and sending the cancel request I think it's an interesting idea (and I'm evaluating it a bit more in the background), but it's quite a big addition to libpq and I'm not sure it pulls its own weight. It also doesn't fit well with the pg_dump approach: "Fire off the cancel requests, then kill the process." By moving sending the cancel request to the blocking call, there's no way to bail out after the cancel is sent but before the blocking call returns (i.e. a query not responding to the cancel would then block the pg_dump shutdown).
-
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Heikki Linnakangas <hlinnaka@iki.fi> — 2026-07-04T19:11:54Z
On 04/07/2026 01:28, Jelte Fennema-Nio wrote: >> psql has a global variable, 'cancel_pressed', which is set in the signal >> handler callback. Is it redundant with 'CancelRequested' that's also set >> in the signal handler? > > I did some spelunking, and yes it is redundant. Fixed in 0001 Cool! > 5d43c3c54 mentions the --single-step flag as something that required > further analysis. I tried --single-step with and without this commit, > and Ctrl+C behaves the same in both. I also cannot think of a reason why > it would behave any differently. Hmm, me neither. Michael, it was a long time ago, but would you happen to remember what your concern on that was? That said, the behavior with --single-step and Ctrl-C isn't great, with or without this patch. Ctrl-C doesn't work while you're stopped on the confirmation prompt: postgres=# \set SINGLESTEP 1 postgres=# select 1; select 2; /**(Single step mode: verify command)******************************************/ select 1; /**(press return to proceed or enter x and return to cancel)*******************/ ^C^C^C^C^C Hitting Ctrl-C doesn't get you out of that prompt. It does cause the query to not execute, but you still need to hit enter. I find that surprising and I bet most users would agree. I started to dig into that, but it's a rabbit hole. I'll start a separate thread to not derail this patch. Patch 0001 looks good to me, unless Michael remembers something we're missing. - Heikki
-
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Zsolt Parragi <zsolt.parragi@percona.com> — 2026-07-04T21:29:28Z
Hello +void +setup_cancel_handler(void (*signal_callback) (void), + void (*thread_callback) (void)) +{ +... + create_cancel_thread(); + pqsignal(SIGINT, CancelSignalHandler); +#endif +} Maybe this should have a doc comment explaining that this function should only be called once (or alternatively, create_cancel_thread should have a guard against repeated callers)? -
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Heikki Linnakangas <hlinnaka@iki.fi> — 2026-07-04T22:57:53Z
On 04/07/2026 01:28, Jelte Fennema-Nio wrote: >> The comments you added in your patches help a lot, explaining the three >> different ways that cancellation can be handled, thanks for that. > > Yeah, I think I extracted just the comment in fe_utils/cancel.c to a separate patch, and modified it to reflect the reality before the rest of the changes. I'm inclined to commit that now to document the status quo. That'll also make it easier to see what the other patches change. Does the attached look correct to you? - Heikki
-
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Jelte Fennema-Nio <postgres@jeltef.nl> — 2026-07-06T10:28:23Z
On Sun, 5 Jul 2026 at 00:58, Heikki Linnakangas <hlinnaka@iki.fi> wrote: > I extracted just the comment in fe_utils/cancel.c to a separate patch, > and modified it to reflect the reality before the rest of the changes. > I'm inclined to commit that now to document the status quo. That'll also > make it easier to see what the other patches change. Does the attached > look correct to you? Sounds good and yeah looks correct.
-
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Heikki Linnakangas <hlinnaka@iki.fi> — 2026-07-06T16:27:56Z
On 06/07/2026 13:28, Jelte Fennema-Nio wrote: > On Sun, 5 Jul 2026 at 00:58, Heikki Linnakangas <hlinnaka@iki.fi> wrote: >> I extracted just the comment in fe_utils/cancel.c to a separate patch, >> and modified it to reflect the reality before the rest of the changes. >> I'm inclined to commit that now to document the status quo. That'll also >> make it easier to see what the other patches change. Does the attached >> look correct to you? > > Sounds good and yeah looks correct. Thanks, pushed that part. Attached are the remaining patches, rebased over that. - Heikki
-
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Heikki Linnakangas <hlinnaka@iki.fi> — 2026-07-06T22:12:57Z
On 04/07/2026 01:28, Jelte Fennema-Nio wrote: > On Tue, 7 Apr 2026 at 02:18, Heikki Linnakangas <hlinnaka@iki.fi> wrote: >> The cancel handling in wait_on_slots() in parallel_slot.c is surprising >> in a different way. It already uses async libpq calls and has a select() >> loop, but it still relies on the signal handler to do the cancellation. >> And it arbitrarily PQcancel()s only one of the connections it waits on. > > Addressed this in 0002 This relies on the signal to interrupt select(), but I'm afraid that's not guaranteed on all platforms. Also, there's a race condition if the signal arrives *just* before you call select(). That's what the "self-pipe hack" is for, see comments at waiteventset.c. - Heikki
-
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Michael Paquier <michael@paquier.xyz> — 2026-07-07T06:14:29Z
On Sat, Jul 04, 2026 at 10:11:54PM +0300, Heikki Linnakangas wrote: > On 04/07/2026 01:28, Jelte Fennema-Nio wrote: >> 5d43c3c54 mentions the --single-step flag as something that required >> further analysis. I tried --single-step with and without this commit, >> and Ctrl+C behaves the same in both. I also cannot think of a reason why >> it would behave any differently. > > Hmm, me neither. Michael, it was a long time ago, but would you happen to > remember what your concern on that was? I unfortunately do not have anymore my notes from 2019 lying around, but looking at the other thread and the code, it seems to me that the problem I saw back then is that cancel request was not working at all under --single-step because we would not set the flag when PQcancel() failed. Back around 5d43c3c54 the CancelRequest flag was only set after we've successfully sent a request. 92f33bb7afd3 has changed that globally, by setting the flag even if a cancel request could not be sent. So back then I am pretty sure that my line of thoughts turned around the case where PQcancel() failed. -- Michael
-
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Jelte Fennema-Nio <postgres@jeltef.nl> — 2026-07-07T07:54:09Z
On Tue Jul 7, 2026 at 12:12 AM CEST, Heikki Linnakangas wrote: > This relies on the signal to interrupt select(), but I'm afraid that's > not guaranteed on all platforms. Also, there's a race condition if the > signal arrives *just* before you call select(). That's what the > "self-pipe hack" is for, see comments at waiteventset.c. Ugh yes you're right. The comment above that block told me otherwise and I didn't question it. Attached is a new version that improves the comment and starts using the same 1 second poll on all platforms.
-
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Heikki Linnakangas <hlinnaka@iki.fi> — 2026-07-07T14:52:12Z
On 07/07/2026 10:54, Jelte Fennema-Nio wrote: > On Tue Jul 7, 2026 at 12:12 AM CEST, Heikki Linnakangas wrote: >> This relies on the signal to interrupt select(), but I'm afraid that's >> not guaranteed on all platforms. Also, there's a race condition if the >> signal arrives *just* before you call select(). That's what the "self- >> pipe hack" is for, see comments at waiteventset.c. > > Ugh yes you're right. The comment above that block told me otherwise and > I didn't question it. Attached is a new version that improves the > comment and starts using the same 1 second poll on all platforms. Yeah, I guess that works.. But It's a little unsatisfactory to have to poll, where we didn't poll before. I started to go down the rabbit hole, here are the options I can think of: a) switch to pselect() where available, fall back to select() with timeout. We don't currently use pselect() anywhere, so this needs a new configure check. b) switch to ppoll() where available, fall back to select() with timeout. ppoll() is already used in pgbench, and is a better interface in general. However, it's different from select(), so the fallback code would differ more. c) switch to ppoll() where available, fall back to poll() with timeout. Use WaitForMultipleObjectsEx() on Windows (it has neither ppoll() nor poll()). The fallback would look more similar to the main code path than with b), but the Windows implementation would be even more different. We could actually eliminate the polling in Windows too, if we added an Event object for the cancellation too. d) like c), but write a helper function to implement poll()/ppoll() on Windows using WaitForMultipleObjectsEx(). Could be used in more places then. d) seems like the nicest option in the long run, but takes a little effort, and I don't have a Windows system to test on currently. While poking around, I noticed that we actually already have a win32 implementation of select() using WaitForMultipleObjectsEx(), in src/backend/port/win32/socket.c. But AFAICS it's not used anywhere. Other prior art: In libpq we have PQsocketPoll(), which uses poll() or select(), but it only operates on a single socket. I wonder if we could bump up our minimum portability requirements, and e.g. require ppoll() on all all non-Windows systems. - Heikki
-
Re: Don't use the deprecated and insecure PQcancel in our frontend tools anymore
Jelte Fennema-Nio <postgres@jeltef.nl> — 2026-07-07T22:28:42Z
On Tue, 7 Jul 2026 at 16:52, Heikki Linnakangas <hlinnaka@iki.fi> wrote: > Yeah, I guess that works.. But It's a little unsatisfactory to have to > poll, where we didn't poll before. I started to go down the rabbit hole, > here are the options I can think of: Honestly, I like the fallback to 1-second polling quite a bit better than any of the options you proposed. The benefit of your options (afaict) is making sure a very rare 1 second delay in a cancel for pg_dump/pg_restore cannot happen on most platforms. That seems like a rather insignificant benefit. But to achieve that the code on Windows (and depending on the option other platforms) is now different, which makes testing any future changes to the code much harder for anyone who doesn't have Windows. I don't think the benefit is worth that downside in this instance. I guess my general thoughts on this is that I would like the behaviour of Windows and Unix to become closer, not further apart (unless there are significant benefits to doing so).