[PATCH] immediately kill psql process if server is not running.
Hi ,
When a user tries to enter a large query, thinking the server is still running, and to their surprise, they discover that the server has crashed or stopped, I have observed that the psql process will still be running if it is idle even after the server crashes or stops.
solved this using 2 methods:
1) using a thread (supports --with-readline and --without-readline)
To address this surprise circumstance, I created a patch that creates a new thread within the psql process to monitor the File Descriptor of the psql process,this thread will monitor for -1 or 0 on psql’s FD using select() and recv() functions .If either one of the value is returned we terminate the psql process.
2) Doing the same thing as above but in single process using readline callbacks(only supports --with-readline)
Regards,
Srinath Reddy Sadipiralla
Member Technical Staff
Zoho
Attachments:
0001-Kill-psql-process-with-thread-implementation.patchapplication/octet-stream; name=0001-Kill-psql-process-with-thread-implementation.patchDownload
From 61221ab134ce06363a9f0c20aa3d5829db374a89 Mon Sep 17 00:00:00 2001
From: Srinath Reddy Sadipiralla <srinath.reddy@zohocorp.com>
Date: Fri, 8 Nov 2024 11:10:40 +0530
Subject: [PATCH] Kill psql process immediately if server crashed
---
src/bin/psql/input.c | 21 +++++++
src/bin/psql/input.h | 3 +
src/bin/psql/startup.c | 113 +++++++++++++++++++++++++++++++++---
src/bin/psql/tab-complete.c | 7 +++
4 files changed, 136 insertions(+), 8 deletions(-)
diff --git a/src/bin/psql/input.c b/src/bin/psql/input.c
index 01b7ef74c3..3912f00e38 100644
--- a/src/bin/psql/input.c
+++ b/src/bin/psql/input.c
@@ -47,6 +47,9 @@ static int history_lines_added;
#define NL_IN_HISTORY 0x01
#endif
+bool waiting_for_input = true;
+pthread_mutex_t waiting_for_input_lock = PTHREAD_MUTEX_INITIALIZER;
+
static void finishInput(void);
@@ -87,8 +90,17 @@ gets_interactive(const char *prompt, PQExpBuffer query_buf)
/* Enable SIGINT to longjmp to sigint_interrupt_jmp */
sigint_interrupt_enabled = true;
+
+ /* Let the thread monitoring psql_fd know that main thread is waiting for user input */
+ pthread_mutex_lock(&waiting_for_input_lock);
+ waiting_for_input = true;
+ pthread_mutex_unlock(&waiting_for_input_lock);
result = readline(prompt);
+
+ pthread_mutex_lock(&waiting_for_input_lock);
+ waiting_for_input = false;
+ pthread_mutex_unlock(&waiting_for_input_lock);
/* Disable SIGINT again */
sigint_interrupt_enabled = false;
@@ -201,9 +213,18 @@ gets_fromFile(FILE *source)
/* Enable SIGINT to longjmp to sigint_interrupt_jmp */
sigint_interrupt_enabled = true;
+ /* Let the thread monitoring psql_fd know that main thread is waiting for user input */
+ pthread_mutex_lock(&waiting_for_input_lock);
+ waiting_for_input = true;
+ pthread_mutex_unlock(&waiting_for_input_lock);
+
/* Get some data */
result = fgets(line, sizeof(line), source);
+ pthread_mutex_lock(&waiting_for_input_lock);
+ waiting_for_input = false;
+ pthread_mutex_unlock(&waiting_for_input_lock);
+
/* Disable SIGINT again */
sigint_interrupt_enabled = false;
diff --git a/src/bin/psql/input.h b/src/bin/psql/input.h
index 4c486d67d2..5afb6f58b3 100644
--- a/src/bin/psql/input.h
+++ b/src/bin/psql/input.h
@@ -37,6 +37,9 @@
#include "pqexpbuffer.h"
+#include <pthread/pthread.h>
+extern bool waiting_for_input;
+extern pthread_mutex_t waiting_for_input_lock;
extern char *gets_interactive(const char *prompt, PQExpBuffer query_buf);
extern char *gets_fromFile(FILE *source);
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index 036caaec2f..ab352eeb89 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -26,6 +26,13 @@
#include "mainloop.h"
#include "settings.h"
+#include <pthread.h>
+#include <sys/socket.h>
+
+pthread_t psql_fd_monitor_thread;
+bool main_thread_exited = false;
+pthread_mutex_t main_thread_exited_lock = PTHREAD_MUTEX_INITIALIZER;
+
/*
* Global psql options
*/
@@ -117,6 +124,87 @@ empty_signal_handler(SIGNAL_ARGS)
}
#endif
+static void clean_up()
+{
+ /* clean up */
+ if (pset.logfile)
+ fclose(pset.logfile);
+ if (pset.db)
+ PQfinish(pset.db);
+ if (pset.dead_conn)
+ PQfinish(pset.dead_conn);
+ setQFout(NULL);
+}
+
+static void terminate_process()
+{
+ pthread_mutex_lock(&main_thread_exited_lock);
+ if (!main_thread_exited)
+ {
+ pthread_mutex_unlock(&main_thread_exited_lock);
+ clean_up();
+ kill(getpid(), SIGKILL);
+ }
+ pthread_mutex_unlock(&main_thread_exited_lock);
+ pthread_exit(NULL);
+}
+
+/* Thread function for monitoring the psql's file descriptor */
+static void *psql_fd_monitor_thread_func()
+{
+ pgsocket psql_fd = PQsocket(pset.db);
+ fd_set read_fds;
+ int retval;
+
+ while (1)
+ {
+ /* Check for server process death only if psql is waiting for input */
+ pthread_mutex_lock(&waiting_for_input_lock);
+ if (waiting_for_input)
+ {
+ struct timeval timeout = {};
+ pthread_mutex_unlock(&waiting_for_input_lock);
+
+ FD_ZERO(&read_fds);
+ FD_SET(psql_fd, &read_fds);
+
+ retval = select(psql_fd + 1, &read_fds, NULL, NULL, &timeout);
+ if (retval == -1)
+ {
+ terminate_process();
+ }
+
+ if (FD_ISSET(psql_fd, &read_fds))
+ {
+ char buf;
+ int len = recv(psql_fd, &buf, 1, 0);
+ if (len <= 0)
+ {
+ // If recv fails, we consider it as a trigger to terminate the process
+ terminate_process();
+ }
+ }
+ }
+ else
+ {
+ pthread_mutex_unlock(&waiting_for_input_lock);
+ }
+ }
+
+ return NULL;
+}
+
+static void create_psql_fd_monitor_thread()
+{
+
+ if (pthread_create(&psql_fd_monitor_thread, NULL, psql_fd_monitor_thread_func, NULL) != 0)
+ {
+ pg_log_error("Failed to create fd monitor thread");
+ exit(EXIT_FAILURE);
+ }
+
+}
+
/*
*
* main
@@ -459,17 +547,26 @@ error:
if (!pset.quiet)
printf(_("Type \"help\" for help.\n\n"));
initializeInput(options.no_readline ? 0 : 1);
+ /* Create a thread to monitor the psql's file descriptor */
+ create_psql_fd_monitor_thread();
successResult = MainLoop(stdin);
}
- /* clean up */
- if (pset.logfile)
- fclose(pset.logfile);
- if (pset.db)
- PQfinish(pset.db);
- if (pset.dead_conn)
- PQfinish(pset.dead_conn);
- setQFout(NULL);
+ clean_up();
+
+ pthread_mutex_lock(&main_thread_exited_lock);
+ main_thread_exited = true;
+ pthread_mutex_unlock(&main_thread_exited_lock);
+
+ pthread_mutex_lock(&waiting_for_input_lock);
+ waiting_for_input = true;
+ pthread_mutex_unlock(&waiting_for_input_lock);
+
+ // Wait for the psql's fd monitoring thread to exit
+ if (pthread_join(psql_fd_monitor_thread, NULL) != 0)
+ {
+ pg_log_error("Failed to join psql's fd monitor thread");
+ }
return successResult;
}
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 2e55607b8a..023b5096cf 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1752,6 +1752,10 @@ psql_completion(const char *text, int start, int end)
char *text_copy = pnstrdup(rl_line_buffer + start, end - start);
text = text_copy;
+ pthread_mutex_lock(&waiting_for_input_lock);
+ waiting_for_input = false;
+ pthread_mutex_unlock(&waiting_for_input_lock);
+
/* Remember last char of the given input word. */
completion_last_char = (end > start) ? text[end - start - 1] : '\0';
@@ -5060,6 +5064,9 @@ psql_completion(const char *text, int start, int end)
completion_ref_object = NULL;
free(completion_ref_schema);
completion_ref_schema = NULL;
+ pthread_mutex_lock(&waiting_for_input_lock);
+ waiting_for_input = true;
+ pthread_mutex_unlock(&waiting_for_input_lock);
/* Return our Grand List O' Matches */
return matches;
--
2.39.3 (Apple Git-146)
0001-Kill-psql-process-with-single-process-implementation.patchapplication/octet-stream; name=0001-Kill-psql-process-with-single-process-implementation.patchDownload
From f493be68e085fec4a7fb9518bb53d3e6da2acd18 Mon Sep 17 00:00:00 2001
From: Srinath Reddy Sadipiralla <srinath.reddy@zohocorp.com>
Date: Fri, 8 Nov 2024 23:53:46 +0530
Subject: [PATCH] Kill psql process with single process implementation
---
src/bin/psql/input.c | 105 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 103 insertions(+), 2 deletions(-)
diff --git a/src/bin/psql/input.c b/src/bin/psql/input.c
index 01b7ef74c3..0f50e9c866 100644
--- a/src/bin/psql/input.c
+++ b/src/bin/psql/input.c
@@ -18,6 +18,8 @@
#include "input.h"
#include "settings.h"
#include "tab-complete.h"
+#include <pthread.h>
+#include <sys/socket.h>
#ifndef WIN32
#define PSQLHISTORY ".psql_history"
@@ -47,8 +49,107 @@ static int history_lines_added;
#define NL_IN_HISTORY 0x01
#endif
+static char *line_buffer = NULL;
+static bool received_line = false;
+char *full_query_buffer = NULL;
+
static void finishInput(void);
+static void handle_line(char *line)
+{
+ if (line == NULL)
+ {
+ rl_callback_handler_remove();
+ exit(0);
+ }
+
+ if (line_buffer != NULL)
+ {
+ free(line_buffer);
+ line_buffer = NULL;
+ }
+
+ if (full_query_buffer == NULL)
+ {
+ full_query_buffer = strdup(line);
+ }
+ else
+ {
+ char *temp = malloc(strlen(full_query_buffer) + strlen(line) + 2);
+ sprintf(temp, "%s\n%s", full_query_buffer, line);
+ free(full_query_buffer);
+ full_query_buffer = temp;
+ }
+
+ if (full_query_buffer[strlen(full_query_buffer) - 1] == ';')
+ {
+ line_buffer = strdup(full_query_buffer);
+ free(full_query_buffer);
+ full_query_buffer = NULL;
+ received_line = true;
+ }
+ else
+ {
+ line_buffer = strdup(line);
+ received_line = true;
+ }
+ free(line);
+ line = NULL;
+}
+
+static char *monitor_stdin_psql_fds(const char *prompt)
+{
+ fd_set read_fds;
+ int max_fd;
+ struct timeval timeout = {};
+ int retval;
+
+ pgsocket psql_fd = PQsocket(pset.db);
+ int terminal_fd = fileno(stdin);
+
+ rl_callback_handler_install(prompt, handle_line);
+
+ while (1)
+ {
+ FD_ZERO(&read_fds);
+ FD_SET(terminal_fd, &read_fds);
+ FD_SET(psql_fd, &read_fds);
+ max_fd = (terminal_fd > psql_fd) ? terminal_fd : psql_fd;
+
+ retval = select(max_fd + 1, &read_fds, NULL, NULL, &timeout);
+
+ if (retval == -1 && errno != EINTR && errno != EAGAIN)
+ {
+ rl_callback_handler_remove();
+ return NULL;
+ }
+
+ if (FD_ISSET(terminal_fd, &read_fds))
+ {
+
+ rl_callback_read_char();
+ if (received_line)
+ {
+ char *result = line_buffer;
+ line_buffer = NULL;
+ received_line = false;
+ return result;
+ }
+ }
+
+ if (FD_ISSET(psql_fd, &read_fds))
+ {
+ char buf;
+ int len = recv(psql_fd, &buf, 1, 0);
+ if (len <= 0 && errno != EINTR && errno != EAGAIN)
+ {
+ rl_callback_handler_remove();
+ /* If recv fails, we consider it as a trigger to terminate the process */
+ return NULL;
+ }
+ }
+ }
+}
/*
* gets_interactive()
@@ -88,8 +189,8 @@ gets_interactive(const char *prompt, PQExpBuffer query_buf)
/* Enable SIGINT to longjmp to sigint_interrupt_jmp */
sigint_interrupt_enabled = true;
- result = readline(prompt);
-
+ result = monitor_stdin_psql_fds(prompt);
+
/* Disable SIGINT again */
sigint_interrupt_enabled = false;
--
2.39.3 (Apple Git-146)
Import Notes
Reply to msg id not found:
On 11/8/24 19:42, Srinath Reddy Sadipiralla wrote:
Hi ,
When a user tries to enter a large query, thinking the server is still
running, and to their surprise, they discover that the server has
crashed or stopped, I have observed that the psql process will still be
running if it is idle even after the server crashes or stops.solved this using 2 methods:
1) using a thread (supports --with-readline and --without-readline)
To address this surprise circumstance, I created a patch that creates a
new thread within the psql process to monitor the File Descriptor of the
psql process,this thread will monitor for -1 or 0 on psql’s FD using
select() and recv() functions .If either one of the value is returned
we terminate the psql process.2) Doing the same thing as above but in single process using readline
callbacks(only supports --with-readline)
Yes, it's true that if the connection breaks, the client may not notice
that until the next command. But why would we want to kill psql in that
case? How does that improve the user experience?
If you're in an interactive session (which is where this matters), and
you're not running any commands, you're most likely not paying any
attention to it. So if psql terminates, you won't notice that either. It
doesn't change anything.
Also, it's a long-standing behavior that it the connection closes, I can
simply repeat the query and it's automatically re-opened.
test=# select 1;
FATAL: terminating connection due to administrator command
server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
The connection to the server was lost. Attempting reset: Succeeded.
test=# select 1;
?column?
----------
1
(1 row)
If we just kill psql instead, this won't work anymore. Why would that be
better?
Also, just killing the process (no matter if by kill or exit), this
means the usual cleanup doesn't happen. So stuff is not written to
.psql_history, for example. That doesn't seem great.
Overall, I don't quite see why the current behavior is a problem, and/or
why would this be an improvement.
thanks
--
Tomas Vondra
Tomas Vondra <tomas@vondra.me> writes:
Yes, it's true that if the connection breaks, the client may not notice
that until the next command. But why would we want to kill psql in that
case? How does that improve the user experience?
If you're in an interactive session (which is where this matters), and
you're not running any commands, you're most likely not paying any
attention to it. So if psql terminates, you won't notice that either. It
doesn't change anything.
Yeah, I concur that this isn't an improvement. In addition to the
points Tomas raises, consider the possibility that you're typing into
a psql session and not watching closely, and the patch causes psql
to quit asynchronously. Now you are typing at a shell prompt.
If you still aren't watching and hit return, unpleasant results
might ensue.
Perhaps there's something that we could do here, but I'm not sure
what would be a safe behavioral change. We've generally avoided
allowing psql to take actions asynchronously --- as an example,
it doesn't report NOTIFY or NOTICE events during user command input,
even though they might be quite old by the time they do get reported.
One idea that seems like it could be safe is to change the prompt,
so that your experience could be like
postgres=# SELECT foo, bar, baz, <enter>
<DEAD!>-# (uh-oh)
This only helps for people who are entering multi-line SQL, but
perhaps the most aggravating form of this is where you spend awhile
crafting a long command only to see it fail. On the other hand,
as Tomas says, up-arrow and retry works regardless of command
length. So maybe it's not worth the trouble.
Anyway, I'm inclined to close this CF entry as returned-with-feedback.
Perhaps there's something we can do here, but the current patch feels
like a dead end.
regards, tom lane
I wrote:
Anyway, I'm inclined to close this CF entry as returned-with-feedback.
Perhaps there's something we can do here, but the current patch feels
like a dead end.
Also, the submitter seems to have disappeared:
----- The following addresses had permanent fatal errors -----
<srinath.reddy@zohocorp.com>
(reason: 550 5.1.1 <srinath.reddy@zohocorp.com> User unknown)
----- Transcript of session follows -----
... while talking to mx.zohocorp.com.:
RCPT To:<srinath.reddy@zohocorp.com>
<<< 550 5.1.1 <srinath.reddy@zohocorp.com> User unknown
550 5.1.1 <srinath.reddy@zohocorp.com>... User unknown
DATA
<<< 503 No recipients specified
451 4.4.1 reply: read error from mx.zohocorp.com.
So I'm not going to hold my breath waiting for a reply.
regards, tom lane
On Sun, Dec 8, 2024 at 4:31 AM Tomas Vondra <tomas@vondra.me> wrote:
Yes, it's true that if the connection breaks, the client may not notice
that until the next command. But why would we want to kill psql in that
case? How does that improve the user experience?
Hi Tomas,sorry for the (very!) late reply ,I almost lost hope waiting for
response to my thread :)
sequence of events:
1)server goes down
2)psql still running
3)user is unaware about the server and enters a query
4)boom for a surprise user came to know "server is down"
if in the very 1st place if we killed the psql process if server goes down
,the user won't get the "surprise" that the server has crashed right?,pls
correct me if i am wrong.
Also, it's a long-standing behavior that it the connection closes, I can
simply repeat the query and it's automatically re-opened.
test=# select 1;
FATAL: terminating connection due to administrator command
server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
The connection to the server was lost. Attempting reset: Succeeded.
test=# select 1;
?column?
----------
1
(1 row)If we just kill psql instead, this won't work anymore. Why would that be
better?
agreed that psql tries to reconnect but only if the user re-tries entering
the query and it's true if the server starts up immediately.
Also, just killing the process (no matter if by kill or exit), this
means the usual cleanup doesn't happen. So stuff is not written to
.psql_history, for example. That doesn't seem great.I am assuming cleanup means you are talking about the one which happens at
the end of main where PQfinish,fclose which has already been handled in
this patch.
But as you mentioned, history is not written to .psql_history in this patch
,we can save to psql_history by calling finishInput() ,for this I updated
the patch.
Thanks for the feedback Tomas.
Srinath Reddy Sadipiralla,
Attachments:
v2-Kill-psql-process-with-thread-implementation.patchapplication/octet-stream; name=v2-Kill-psql-process-with-thread-implementation.patchDownload
From 6f5e609f8b3d4c36d0251b5b70a061b1430d463e Mon Sep 17 00:00:00 2001
From: Srinath Reddy Sadipiralla <srinath2133@gmail.com>
Date: Mon, 20 Jan 2025 10:54:28 +0530
Subject: [PATCH] Kill psql process with thread implementation
---
src/bin/psql/input.c | 24 +++++++-
src/bin/psql/input.h | 4 ++
src/bin/psql/startup.c | 114 +++++++++++++++++++++++++++++++++---
src/bin/psql/tab-complete.c | 7 +++
4 files changed, 139 insertions(+), 10 deletions(-)
diff --git a/src/bin/psql/input.c b/src/bin/psql/input.c
index 01b7ef74c3..ae8f9bbd5f 100644
--- a/src/bin/psql/input.c
+++ b/src/bin/psql/input.c
@@ -47,7 +47,9 @@ static int history_lines_added;
#define NL_IN_HISTORY 0x01
#endif
-static void finishInput(void);
+bool waiting_for_input = true;
+pthread_mutex_t waiting_for_input_lock = PTHREAD_MUTEX_INITIALIZER;
+
/*
@@ -87,8 +89,17 @@ gets_interactive(const char *prompt, PQExpBuffer query_buf)
/* Enable SIGINT to longjmp to sigint_interrupt_jmp */
sigint_interrupt_enabled = true;
+
+ /* Let the thread monitoring psql_fd know that main thread is waiting for user input */
+ pthread_mutex_lock(&waiting_for_input_lock);
+ waiting_for_input = true;
+ pthread_mutex_unlock(&waiting_for_input_lock);
result = readline(prompt);
+
+ pthread_mutex_lock(&waiting_for_input_lock);
+ waiting_for_input = false;
+ pthread_mutex_unlock(&waiting_for_input_lock);
/* Disable SIGINT again */
sigint_interrupt_enabled = false;
@@ -201,9 +212,18 @@ gets_fromFile(FILE *source)
/* Enable SIGINT to longjmp to sigint_interrupt_jmp */
sigint_interrupt_enabled = true;
+ /* Let the thread monitoring psql_fd know that main thread is waiting for user input */
+ pthread_mutex_lock(&waiting_for_input_lock);
+ waiting_for_input = true;
+ pthread_mutex_unlock(&waiting_for_input_lock);
+
/* Get some data */
result = fgets(line, sizeof(line), source);
+ pthread_mutex_lock(&waiting_for_input_lock);
+ waiting_for_input = false;
+ pthread_mutex_unlock(&waiting_for_input_lock);
+
/* Disable SIGINT again */
sigint_interrupt_enabled = false;
@@ -536,7 +556,7 @@ printHistory(const char *fname, unsigned short int pager)
}
-static void
+void
finishInput(void)
{
#ifdef USE_READLINE
diff --git a/src/bin/psql/input.h b/src/bin/psql/input.h
index 4c486d67d2..6c42742856 100644
--- a/src/bin/psql/input.h
+++ b/src/bin/psql/input.h
@@ -37,6 +37,9 @@
#include "pqexpbuffer.h"
+#include <pthread/pthread.h>
+extern bool waiting_for_input;
+extern pthread_mutex_t waiting_for_input_lock;
extern char *gets_interactive(const char *prompt, PQExpBuffer query_buf);
extern char *gets_fromFile(FILE *source);
@@ -47,5 +50,6 @@ extern bool printHistory(const char *fname, unsigned short int pager);
extern void pg_append_history(const char *s, PQExpBuffer history_buf);
extern void pg_send_history(PQExpBuffer history_buf);
+extern void finishInput(void);
#endif /* INPUT_H */
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index 036caaec2f..13f0c913a2 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -26,6 +26,13 @@
#include "mainloop.h"
#include "settings.h"
+#include <pthread.h>
+#include <sys/socket.h>
+
+pthread_t psql_fd_monitor_thread;
+bool main_thread_exited = false;
+pthread_mutex_t main_thread_exited_lock = PTHREAD_MUTEX_INITIALIZER;
+
/*
* Global psql options
*/
@@ -117,6 +124,88 @@ empty_signal_handler(SIGNAL_ARGS)
}
#endif
+static void clean_up()
+{
+ /* clean up */
+ if (pset.logfile)
+ fclose(pset.logfile);
+ if (pset.db)
+ PQfinish(pset.db);
+ if (pset.dead_conn)
+ PQfinish(pset.dead_conn);
+ setQFout(NULL);
+}
+
+static void terminate_process()
+{
+ pthread_mutex_lock(&main_thread_exited_lock);
+ if (!main_thread_exited)
+ {
+ pthread_mutex_unlock(&main_thread_exited_lock);
+ clean_up();
+ finishInput();
+ kill(getpid(), SIGKILL);
+ }
+ pthread_mutex_unlock(&main_thread_exited_lock);
+ pthread_exit(NULL);
+}
+
+/* Thread function for monitoring the psql's file descriptor */
+static void *psql_fd_monitor_thread_func()
+{
+ pgsocket psql_fd = PQsocket(pset.db);
+ fd_set read_fds;
+ int retval;
+
+ while (1)
+ {
+ /* Check for server process death only if psql is waiting for input */
+ pthread_mutex_lock(&waiting_for_input_lock);
+ if (waiting_for_input)
+ {
+ struct timeval timeout = {};
+ pthread_mutex_unlock(&waiting_for_input_lock);
+
+ FD_ZERO(&read_fds);
+ FD_SET(psql_fd, &read_fds);
+
+ retval = select(psql_fd + 1, &read_fds, NULL, NULL, &timeout);
+ if (retval == -1)
+ {
+ terminate_process();
+ }
+
+ if (FD_ISSET(psql_fd, &read_fds))
+ {
+ char buf;
+ int len = recv(psql_fd, &buf, 1, 0);
+ if (len <= 0)
+ {
+ // If recv fails, we consider it as a trigger to terminate the process
+ terminate_process();
+ }
+ }
+ }
+ else
+ {
+ pthread_mutex_unlock(&waiting_for_input_lock);
+ }
+ }
+
+ return NULL;
+}
+
+static void create_psql_fd_monitor_thread()
+{
+
+ if (pthread_create(&psql_fd_monitor_thread, NULL, psql_fd_monitor_thread_func, NULL) != 0)
+ {
+ pg_log_error("Failed to create fd monitor thread");
+ exit(EXIT_FAILURE);
+ }
+
+}
+
/*
*
* main
@@ -459,17 +548,26 @@ error:
if (!pset.quiet)
printf(_("Type \"help\" for help.\n\n"));
initializeInput(options.no_readline ? 0 : 1);
+ /* Create a thread to monitor the psql's file descriptor */
+ create_psql_fd_monitor_thread();
successResult = MainLoop(stdin);
}
- /* clean up */
- if (pset.logfile)
- fclose(pset.logfile);
- if (pset.db)
- PQfinish(pset.db);
- if (pset.dead_conn)
- PQfinish(pset.dead_conn);
- setQFout(NULL);
+ clean_up();
+
+ pthread_mutex_lock(&main_thread_exited_lock);
+ main_thread_exited = true;
+ pthread_mutex_unlock(&main_thread_exited_lock);
+
+ pthread_mutex_lock(&waiting_for_input_lock);
+ waiting_for_input = true;
+ pthread_mutex_unlock(&waiting_for_input_lock);
+
+ // Wait for the psql's fd monitoring thread to exit
+ if (pthread_join(psql_fd_monitor_thread, NULL) != 0)
+ {
+ pg_log_error("Failed to join psql's fd monitor thread");
+ }
return successResult;
}
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 1bd01ff865..ab926627c2 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1752,6 +1752,10 @@ psql_completion(const char *text, int start, int end)
char *text_copy = pnstrdup(rl_line_buffer + start, end - start);
text = text_copy;
+ pthread_mutex_lock(&waiting_for_input_lock);
+ waiting_for_input = false;
+ pthread_mutex_unlock(&waiting_for_input_lock);
+
/* Remember last char of the given input word. */
completion_last_char = (end > start) ? text[end - start - 1] : '\0';
@@ -5060,6 +5064,9 @@ psql_completion(const char *text, int start, int end)
completion_ref_object = NULL;
free(completion_ref_schema);
completion_ref_schema = NULL;
+ pthread_mutex_lock(&waiting_for_input_lock);
+ waiting_for_input = true;
+ pthread_mutex_unlock(&waiting_for_input_lock);
/* Return our Grand List O' Matches */
return matches;
--
2.47.1
On Sun, Jan 19, 2025 at 3:55 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
One idea that seems like it could be safe is to change the prompt,
so that your experience could be likepostgres=# SELECT foo, bar, baz, <enter>
<DEAD!>-# (uh-oh)This only helps for people who are entering multi-line SQL,
+1,will look into it.
Thanks.
Srinath Reddy Sadipiralla,