Add pg_terminate_backend() to allow terminating only a single session.
authorBruce Momjian <bruce@momjian.us>
Tue, 15 Apr 2008 13:55:12 +0000 (13:55 +0000)
committerBruce Momjian <bruce@momjian.us>
Tue, 15 Apr 2008 13:55:12 +0000 (13:55 +0000)
doc/src/sgml/func.sgml
doc/src/sgml/runtime.sgml
src/backend/tcop/postgres.c
src/backend/utils/adt/misc.c
src/include/catalog/pg_proc.h
src/include/storage/proc.h
src/include/utils/builtins.h

index b1edec5e36d3370eb8fd0e3ec768bdd299d88f0d..4f16d90838e94b086380ce46e94e1a6fbc37f2c8 100644 (file)
@@ -11848,6 +11848,9 @@ SELECT set_config('log_statement_stats', 'off', false);
    <indexterm>
     <primary>pg_cancel_backend</primary>
    </indexterm>
+   <indexterm>
+    <primary>pg_terminate_backend</primary>
+   </indexterm>
    <indexterm>
     <primary>pg_reload_conf</primary>
    </indexterm>
@@ -11883,6 +11886,13 @@ SELECT set_config('log_statement_stats', 'off', false);
        <entry><type>boolean</type></entry>
        <entry>Cancel a backend's current query</entry>
       </row>
+      <row>
+       <entry>
+        <literal><function>pg_terminate_backend</function>(<parameter>pid</parameter> <type>int</>)</literal>
+        </entry>
+       <entry><type>boolean</type></entry>
+       <entry>Terminate a backend</entry>
+      </row>
       <row>
        <entry>
         <literal><function>pg_reload_conf</function>()</literal>
@@ -11907,9 +11917,10 @@ SELECT set_config('log_statement_stats', 'off', false);
    </para>
 
    <para>
-    <function>pg_cancel_backend</> sends a query cancel
-    (<systemitem>SIGINT</>) signal to a backend process identified by
-    process ID.  The process ID of an active backend can be found from
+    <function>pg_cancel_backend</> and <function>pg_terminate_backend</>
+    send a query cancel (<systemitem>SIGINT</>) signal to a backend process
+    identified by process ID.  The
+    process ID of an active backend can be found from
     the <structfield>procpid</structfield> column in the
     <structname>pg_stat_activity</structname> view, or by listing the
     <command>postgres</command> processes on the server with
index a64c6f148aade559f786604008419825f73a5b1a..8ead9e6ac781adb0e609a5886910fc6cef71c11f 100644 (file)
@@ -1372,6 +1372,13 @@ $ <userinput>kill -INT `head -1 /usr/local/pgsql/data/postmaster.pid`</userinput
     well.
    </para>
   </important>
+
+  <para>
+   To terminate a session while allowing other sessions to continue, use
+   <function>pg_terminate_backend()</> (<xref
+   linkend="functions-admin-signal-table">) rather than sending a signal
+   to the child process.
+  </para>
  </sect1>
 
  <sect1 id="preventing-server-spoofing">
index 56efa4cbf7a5038de214773c333680ebe00db83c..2c7ab98ae6fd4e47d59be4b8910ecede3300cf08 100644 (file)
@@ -2541,7 +2541,8 @@ StatementCancelHandler(SIGNAL_ARGS)
                 * waiting for input, however.
                 */
                if (ImmediateInterruptOK && InterruptHoldoffCount == 0 &&
-                       CritSectionCount == 0 && !DoingCommandRead)
+                       CritSectionCount == 0 &&
+                       (!DoingCommandRead || MyProc->terminate))
                {
                        /* bump holdoff count to make ProcessInterrupts() a no-op */
                        /* until we are done getting ready for it */
@@ -2621,6 +2622,10 @@ ProcessInterrupts(void)
                        ereport(ERROR,
                                        (errcode(ERRCODE_QUERY_CANCELED),
                                         errmsg("canceling autovacuum task")));
+               else if (MyProc->terminate)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_ADMIN_SHUTDOWN),
+                                        errmsg("terminating backend due to administrator command")));
                else
                        ereport(ERROR,
                                        (errcode(ERRCODE_QUERY_CANCELED),
@@ -3459,6 +3464,9 @@ PostgresMain(int argc, char *argv[], const char *username)
                /* We don't have a transaction command open anymore */
                xact_started = false;
 
+               if (MyProc->terminate)
+                       die(SIGINT);
+
                /* Now we can allow interrupts again */
                RESUME_INTERRUPTS();
        }
index 0d580539695b2241e514b0e3fd56a525ea242357..d877977014f8bf41a104e754c81cc4fe8b893382 100644 (file)
@@ -27,6 +27,7 @@
 #include "postmaster/syslogger.h"
 #include "storage/fd.h"
 #include "storage/pmsignal.h"
+#include "storage/proc.h"
 #include "storage/procarray.h"
 #include "utils/builtins.h"
 #include "tcop/tcopprot.h"
@@ -89,7 +90,7 @@ current_query(PG_FUNCTION_ARGS)
  * Functions to send signals to other backends.
  */
 static bool
-pg_signal_backend(int pid, int sig)
+pg_signal_check(int pid)
 {
        if (!superuser())
                ereport(ERROR,
@@ -106,7 +107,16 @@ pg_signal_backend(int pid, int sig)
                                (errmsg("PID %d is not a PostgreSQL server process", pid)));
                return false;
        }
+       else
+               return true;
+}
 
+/*
+ * Functions to send signals to other backends.
+ */
+static bool
+pg_signal_backend(int pid, int sig)
+{
        /* If we have setsid(), signal the backend's whole process group */
 #ifdef HAVE_SETSID
        if (kill(-pid, sig))
@@ -125,7 +135,43 @@ pg_signal_backend(int pid, int sig)
 Datum
 pg_cancel_backend(PG_FUNCTION_ARGS)
 {
-       PG_RETURN_BOOL(pg_signal_backend(PG_GETARG_INT32(0), SIGINT));
+       int pid = PG_GETARG_INT32(0);
+       
+       if (pg_signal_check(pid))
+               PG_RETURN_BOOL(pg_signal_backend(pid, SIGINT));
+       else
+               PG_RETURN_BOOL(false);
+}
+
+/*
+ *     To cleanly terminate a backend, we set PGPROC(pid)->terminate
+ *     then send a cancel signal.  We get ProcArrayLock only when
+ *     setting PGPROC->terminate so the function might fail in
+ *     several places, but that is fine because in those cases the
+ *     backend is already gone.
+ */
+Datum
+pg_terminate_backend(PG_FUNCTION_ARGS)
+{
+       int pid = PG_GETARG_INT32(0);
+       volatile PGPROC *term_proc;
+
+       /* Is this the super-user, and can we find the PGPROC entry for the pid? */
+       if (pg_signal_check(pid) && (term_proc = BackendPidGetProc(pid)) != NULL)
+       {
+               LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
+               /* Recheck now that we have the ProcArray lock. */
+               if (term_proc->pid == pid)
+               {
+                       term_proc->terminate = true;
+                       LWLockRelease(ProcArrayLock);
+                       PG_RETURN_BOOL(pg_signal_backend(pid, SIGINT));
+               }
+               else
+                       LWLockRelease(ProcArrayLock);
+       }
+
+       PG_RETURN_BOOL(false);
 }
 
 Datum
@@ -169,17 +215,6 @@ pg_rotate_logfile(PG_FUNCTION_ARGS)
        PG_RETURN_BOOL(true);
 }
 
-#ifdef NOT_USED
-
-/* Disabled in 8.0 due to reliability concerns; FIXME someday */
-Datum
-pg_terminate_backend(PG_FUNCTION_ARGS)
-{
-       PG_RETURN_INT32(pg_signal_backend(PG_GETARG_INT32(0), SIGTERM));
-}
-#endif
-
-
 /* Function to find out which databases make use of a tablespace */
 
 typedef struct
index b1c2f1c8b469afe0f64341f2be783bee14bf1384..e035a9b390685a9024f0cf945aadf585ae89cdf0 100644 (file)
@@ -3157,6 +3157,8 @@ DESCR("is schema another session's temp schema?");
 
 DATA(insert OID = 2171 ( pg_cancel_backend             PGNSP PGUID 12 1 0 f f t f v 1 16 "23" _null_ _null_ _null_ pg_cancel_backend - _null_ _null_ ));
 DESCR("cancel a server process' current query");
+DATA(insert OID = 2096 ( pg_terminate_backend          PGNSP PGUID 12 1 0 f f t f v 1 16 "23" _null_ _null_ _null_ pg_terminate_backend - _null_ _null_ ));
+DESCR("terminate a server process");
 DATA(insert OID = 2172 ( pg_start_backup               PGNSP PGUID 12 1 0 f f t f v 1 25 "25" _null_ _null_ _null_ pg_start_backup - _null_ _null_ ));
 DESCR("prepare for taking an online backup");
 DATA(insert OID = 2173 ( pg_stop_backup                        PGNSP PGUID 12 1 0 f f t f v 0 25 "" _null_ _null_ _null_ pg_stop_backup - _null_ _null_ ));
index ae7a2c4cb53d513b4f9975e5f70ab0c69e95314d..6af7fc1e7c8a48ab255a978d7a90f2a18d1216fc 100644 (file)
@@ -91,6 +91,8 @@ struct PGPROC
 
        bool            inCommit;               /* true if within commit critical section */
 
+       bool            terminate;              /* admin requested termination */
+
        uint8           vacuumFlags;    /* vacuum-related flags, see above */
 
        /* Info about LWLock the process is currently waiting for, if any. */
index f9d425f7b98d53196b93bdc77fb996b157245c49..e6d478ddde11152a5be851857f7803cd4a8777e4 100644 (file)
@@ -416,6 +416,7 @@ extern Datum nonnullvalue(PG_FUNCTION_ARGS);
 extern Datum current_database(PG_FUNCTION_ARGS);
 extern Datum current_query(PG_FUNCTION_ARGS);
 extern Datum pg_cancel_backend(PG_FUNCTION_ARGS);
+extern Datum pg_terminate_backend(PG_FUNCTION_ARGS);
 extern Datum pg_reload_conf(PG_FUNCTION_ARGS);
 extern Datum pg_tablespace_databases(PG_FUNCTION_ARGS);
 extern Datum pg_rotate_logfile(PG_FUNCTION_ARGS);