On Tue, Aug 1, 2017 at 4:55 PM, Robert Haas <robertmh...@gmail.com> wrote: > > On Tue, Aug 1, 2017 at 3:37 PM, Peter Eisentraut > <peter.eisentr...@2ndquadrant.com> wrote: > > On 7/21/17 12:59, Robert Haas wrote: > >> That's an exceedingly-weak argument for rejecting this patch. The > >> fact that you can probably hack around the lack of a hook for most > >> reasonable use cases is not an argument for having a hook that does > >> what people actually want to do. > > > > Still nobody has presented a concrete use case so far. > > I've been asked for this at EDB, too. Inserting a row into some table > on each logon, for example. > > A quick Google search found 6 previous requests for this feature, some > of which describe intended use cases: > > https://www.postgresql.org/message-id/4ebc6852.5030...@fuzzy.cz (2011) > https://www.postgresql.org/message-id/CAHyXU0wrsYShxmwBxZSGYoiBJa%3DgzEJ17iAeRvaf_vA%2BcoH_qA%40mail.gmail.com > (2011) > https://www.postgresql.org/message-id/bay104-w513cf26c0046c9d28747b8d1...@phx.gbl > (2009, in Spanish) > https://www.postgresql.org/message-id/758d5e7f0803130227m558d32cdl7159bed00d21f084%40mail.gmail.com > (2008) > https://www.postgresql.org/message-id/001a01c48077%240b118e60%240200030a%40gendron.ca > (2004) > https://www.postgresql.org/message-id/f96sgcorblsaqv6updv00000...@hotmail.com > (2000) >
Hi all, I'm sending a new rebased patches and added tests to src/tests/modules as suggested before. Regards, -- Fabrízio de Royes Mello Consultoria/Coaching PostgreSQL >> Timbira: http://www.timbira.com.br >> Blog: http://fabriziomello.github.io >> Linkedin: http://br.linkedin.com/in/fabriziomello >> Twitter: http://twitter.com/fabriziomello >> Github: http://github.com/fabriziomello
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index c807b00..5c22f2d 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -165,6 +165,9 @@ static bool RecoveryConflictPending = false; static bool RecoveryConflictRetryable = true; static ProcSignalReason RecoveryConflictReason; +/* Hook for plugins to get control at start of session */ +session_start_hook_type session_start_hook = NULL; + /* ---------------------------------------------------------------- * decls for routines only used in this file * ---------------------------------------------------------------- @@ -3838,6 +3841,9 @@ PostgresMain(int argc, char *argv[], if (!IsUnderPostmaster) PgStartTime = GetCurrentTimestamp(); + if (session_start_hook) + (*session_start_hook) (dbname, username); + /* * POSTGRES main processing loop begins here * diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h index f8c535c..d349592 100644 --- a/src/include/tcop/tcopprot.h +++ b/src/include/tcop/tcopprot.h @@ -35,6 +35,11 @@ extern PGDLLIMPORT const char *debug_query_string; extern int max_stack_depth; extern int PostAuthDelay; +/* Hook for plugins to get control at start of session */ +typedef void (*session_start_hook_type) (const char *dbname, + const char *username); +extern PGDLLIMPORT session_start_hook_type session_start_hook; + /* GUC-configurable parameters */ typedef enum
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 5c22f2d..79f3099 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -165,9 +165,9 @@ static bool RecoveryConflictPending = false; static bool RecoveryConflictRetryable = true; static ProcSignalReason RecoveryConflictReason; -/* Hook for plugins to get control at start of session */ +/* Hook for plugins to get control at start or end of session */ session_start_hook_type session_start_hook = NULL; - +session_end_hook_type session_end_hook = NULL; /* ---------------------------------------------------------------- * decls for routines only used in this file * ---------------------------------------------------------------- @@ -192,6 +192,7 @@ static void drop_unnamed_stmt(void); static void log_disconnections(int code, Datum arg); static void enable_statement_timeout(void); static void disable_statement_timeout(void); +static void do_session_end_hook(int code, Datum arg); /* ---------------------------------------------------------------- @@ -3845,6 +3846,12 @@ PostgresMain(int argc, char *argv[], (*session_start_hook) (dbname, username); /* + * Setup handler to session end hook + */ + if (IsUnderPostmaster) + on_proc_exit(do_session_end_hook, 0); + + /* * POSTGRES main processing loop begins here * * If an exception is encountered, processing resumes here so we abort the @@ -4596,3 +4603,15 @@ disable_statement_timeout(void) stmt_timeout_active = false; } } + +/* + * on_proc_exit handler to call session end hook + */ +static void +do_session_end_hook(int code, Datum arg) +{ + Port *port = MyProcPort; + + if (session_end_hook) + (*session_end_hook) (port->database_name, port->user_name); +} \ No newline at end of file diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h index d349592..b7fb8c3 100644 --- a/src/include/tcop/tcopprot.h +++ b/src/include/tcop/tcopprot.h @@ -35,10 +35,14 @@ extern PGDLLIMPORT const char *debug_query_string; extern int max_stack_depth; extern int PostAuthDelay; -/* Hook for plugins to get control at start of session */ +/* Hook for plugins to get control at start and end of session */ typedef void (*session_start_hook_type) (const char *dbname, const char *username); +typedef void (*session_end_hook_type) (const char *dbname, + const char *username); + extern PGDLLIMPORT session_start_hook_type session_start_hook; +extern PGDLLIMPORT session_end_hook_type session_end_hook; /* GUC-configurable parameters */
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile index b7ed0af..a3c8c1e 100644 --- a/src/test/modules/Makefile +++ b/src/test/modules/Makefile @@ -11,6 +11,7 @@ SUBDIRS = \ snapshot_too_old \ test_ddl_deparse \ test_extensions \ + test_session_hooks \ test_parser \ test_pg_dump \ test_rbtree \ diff --git a/src/test/modules/test_session_hooks/.gitignore b/src/test/modules/test_session_hooks/.gitignore new file mode 100644 index 0000000..5dcb3ff --- /dev/null +++ b/src/test/modules/test_session_hooks/.gitignore @@ -0,0 +1,4 @@ +# Generated subdirectories +/log/ +/results/ +/tmp_check/ diff --git a/src/test/modules/test_session_hooks/Makefile b/src/test/modules/test_session_hooks/Makefile new file mode 100644 index 0000000..9cabd3f --- /dev/null +++ b/src/test/modules/test_session_hooks/Makefile @@ -0,0 +1,26 @@ +# src/test/modules/test_session_hooks/Makefile + +MODULES = test_session_hooks +PGFILEDESC = "test_session_hooks - Test session hooks with an extension" + +EXTENSION = test_session_hooks +DATA = test_session_hooks--1.0.sql + +REGRESS = test_session_hooks +REGRESS_OPTS = --temp-config=$(top_srcdir)/src/test/modules/test_session_hooks/session_hooks.conf + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = src/test/modules/test_session_hooks +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif + +check: prove-check + +prove-check: + $(prove_check) diff --git a/src/test/modules/test_session_hooks/README b/src/test/modules/test_session_hooks/README new file mode 100644 index 0000000..e6c78b8 --- /dev/null +++ b/src/test/modules/test_session_hooks/README @@ -0,0 +1,2 @@ +test_pg_dump is an extension explicitly to test pg_dump when +extensions are present in the system. diff --git a/src/test/modules/test_session_hooks/expected/test_session_hooks.out b/src/test/modules/test_session_hooks/expected/test_session_hooks.out new file mode 100644 index 0000000..602819c --- /dev/null +++ b/src/test/modules/test_session_hooks/expected/test_session_hooks.out @@ -0,0 +1,10 @@ +SHOW test_session_hooks.message; +ERROR: unrecognized configuration parameter "test_session_hooks.message" +CREATE DATABASE test; +\c test +SHOW test_session_hooks.message; + test_session_hooks.message +---------------------------- + Session Start Hook Handled +(1 row) + diff --git a/src/test/modules/test_session_hooks/session_hooks.conf b/src/test/modules/test_session_hooks/session_hooks.conf new file mode 100644 index 0000000..a66e60e --- /dev/null +++ b/src/test/modules/test_session_hooks/session_hooks.conf @@ -0,0 +1 @@ +shared_preload_libraries = 'test_session_hooks' diff --git a/src/test/modules/test_session_hooks/sql/test_session_hooks.sql b/src/test/modules/test_session_hooks/sql/test_session_hooks.sql new file mode 100644 index 0000000..b75f2f8 --- /dev/null +++ b/src/test/modules/test_session_hooks/sql/test_session_hooks.sql @@ -0,0 +1,4 @@ +SHOW test_session_hooks.message; +CREATE DATABASE test; +\c test +SHOW test_session_hooks.message; diff --git a/src/test/modules/test_session_hooks/t/001_base.pl b/src/test/modules/test_session_hooks/t/001_base.pl new file mode 100644 index 0000000..442fb8a --- /dev/null +++ b/src/test/modules/test_session_hooks/t/001_base.pl @@ -0,0 +1,24 @@ +# Test session end hook log messages + +use strict; +use warnings; + +use TestLib; +use Test::More tests => 2; +use PostgresNode; + +my $node = get_new_node('main'); +$node->init; +$node->append_conf( + 'postgresql.conf', + qq{shared_preload_libraries = 'test_session_hooks'} +); +$node->start; +$node->safe_psql('postgres', 'CREATE DATABASE test'); +my ($ret, $stdout, $stderr) = $node->psql('test','show test_session_hooks.message'); + +# check session start hook +like($stdout, qr/Session Start Hook Handled/, 'error during session start hook'); + +# check session end hook +like(slurp_file($node->logfile()), qr/Session End Hook Handled/, 'error during session end hook'); diff --git a/src/test/modules/test_session_hooks/test_session_hooks--1.0.sql b/src/test/modules/test_session_hooks/test_session_hooks--1.0.sql new file mode 100644 index 0000000..16bcee9 --- /dev/null +++ b/src/test/modules/test_session_hooks/test_session_hooks--1.0.sql @@ -0,0 +1,4 @@ +/* src/test/modules/test_hook_session/test_hook_session--1.0.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION test_hook_session" to load this file. \quit diff --git a/src/test/modules/test_session_hooks/test_session_hooks.c b/src/test/modules/test_session_hooks/test_session_hooks.c new file mode 100644 index 0000000..ff63b83 --- /dev/null +++ b/src/test/modules/test_session_hooks/test_session_hooks.c @@ -0,0 +1,80 @@ +/* ------------------------------------------------------------------------- + * + * test_session_hooks.c + * Code for testing SESSION hooks. + * + * Copyright (c) 2010-2017, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/test/modules/test_session_hooks/test_session_hooks.c + * + * ------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/xact.h" +#include "executor/spi.h" +#include "tcop/tcopprot.h" + +PG_MODULE_MAGIC; + +void _PG_init(void); +void _PG_fini(void); + +/* Original Hook */ +static session_start_hook_type prev_session_start_hook = NULL; +static session_end_hook_type prev_session_end_hook = NULL; + +/* sample session start hook function */ +static void +sample_session_start_hook(const char *dbname, const char *username) +{ + if (prev_session_start_hook) + prev_session_start_hook(dbname, username); + + if (!strcmp(dbname, "test")) + { + StartTransactionCommand(); + SPI_connect(); + SPI_exec("SET test_session_hooks.message TO 'Session Start Hook Handled'", 1); + SPI_finish(); + CommitTransactionCommand(); + } +} + +/* sample sessoin end hook function */ +static void +sample_session_end_hook(const char *dbname, const char *username) +{ + if (prev_session_end_hook) + prev_session_end_hook(dbname, username); + + if (!strcmp(dbname, "test")) + elog(LOG, "Session End Hook Handled"); +} + +/* + * Module Load Callback + */ +void +_PG_init(void) +{ + /* Save Hooks for Unload */ + prev_session_start_hook = session_start_hook; + prev_session_end_hook = session_end_hook; + + /* Set New Hooks */ + session_start_hook = sample_session_start_hook; + session_end_hook = sample_session_end_hook; +} + +/* + * Module Unload Callback + */ +void +_PG_fini(void) +{ + /* Uninstall Hooks */ + session_start_hook = prev_session_start_hook; + session_end_hook = prev_session_end_hook; +} diff --git a/src/test/modules/test_session_hooks/test_session_hooks.control b/src/test/modules/test_session_hooks/test_session_hooks.control new file mode 100644 index 0000000..7d7ef9f --- /dev/null +++ b/src/test/modules/test_session_hooks/test_session_hooks.control @@ -0,0 +1,3 @@ +comment = 'Test start/end hook session with an extension' +default_version = '1.0' +relocatable = true
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers