From 49cd2b93d7dbceefdf9a71cc301d284a2dd234c3 Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Mon, 22 Jan 2024 13:32:28 +0900 Subject: [PATCH] Add test module injection_points MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This provides basic coverage for injection points within a single process, while providing some callbacks that can be used for other tests. There are plans to extend this module later with more advanced capabilities for tests. Author: Michael Paquier, with comment fixes from Ashutosh Bapat. Reviewed-by: Ashutosh Bapat, Nathan Bossart, Álvaro Herrera, Dilip Kumar, Amul Sul, Nazir Bilal Yavuz Discussion: https://postgr.es/m/ZTiV8tn_MIb_H2rE@paquier.xyz --- src/test/modules/Makefile | 7 ++ src/test/modules/injection_points/.gitignore | 4 + src/test/modules/injection_points/Makefile | 20 +++ .../expected/injection_points.out | 118 ++++++++++++++++++ .../injection_points--1.0.sql | 35 ++++++ .../injection_points/injection_points.c | 95 ++++++++++++++ .../injection_points/injection_points.control | 4 + src/test/modules/injection_points/meson.build | 37 ++++++ .../injection_points/sql/injection_points.sql | 34 +++++ src/test/modules/meson.build | 1 + 10 files changed, 355 insertions(+) create mode 100644 src/test/modules/injection_points/.gitignore create mode 100644 src/test/modules/injection_points/Makefile create mode 100644 src/test/modules/injection_points/expected/injection_points.out create mode 100644 src/test/modules/injection_points/injection_points--1.0.sql create mode 100644 src/test/modules/injection_points/injection_points.c create mode 100644 src/test/modules/injection_points/injection_points.control create mode 100644 src/test/modules/injection_points/meson.build create mode 100644 src/test/modules/injection_points/sql/injection_points.sql diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile index f656032589..e32c8925f6 100644 --- a/src/test/modules/Makefile +++ b/src/test/modules/Makefile @@ -38,6 +38,13 @@ SUBDIRS = \ worker_spi \ xid_wraparound + +ifeq ($(enable_injection_points),yes) +SUBDIRS += injection_points +else +ALWAYS_SUBDIRS += injection_points +endif + ifeq ($(with_ssl),openssl) SUBDIRS += ssl_passphrase_callback else diff --git a/src/test/modules/injection_points/.gitignore b/src/test/modules/injection_points/.gitignore new file mode 100644 index 0000000000..5dcb3ff972 --- /dev/null +++ b/src/test/modules/injection_points/.gitignore @@ -0,0 +1,4 @@ +# Generated subdirectories +/log/ +/results/ +/tmp_check/ diff --git a/src/test/modules/injection_points/Makefile b/src/test/modules/injection_points/Makefile new file mode 100644 index 0000000000..2cbbae4e0a --- /dev/null +++ b/src/test/modules/injection_points/Makefile @@ -0,0 +1,20 @@ +# src/test/modules/injection_points/Makefile + +MODULES = injection_points + +EXTENSION = injection_points +DATA = injection_points--1.0.sql +PGFILEDESC = "injection_points - facility for injection points" + +REGRESS = injection_points + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = src/test/modules/injection_points +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/src/test/modules/injection_points/expected/injection_points.out b/src/test/modules/injection_points/expected/injection_points.out new file mode 100644 index 0000000000..827456ccc5 --- /dev/null +++ b/src/test/modules/injection_points/expected/injection_points.out @@ -0,0 +1,118 @@ +CREATE EXTENSION injection_points; +SELECT injection_points_attach('TestInjectionBooh', 'booh'); +ERROR: incorrect action "booh" for injection point creation +SELECT injection_points_attach('TestInjectionError', 'error'); + injection_points_attach +------------------------- + +(1 row) + +SELECT injection_points_attach('TestInjectionLog', 'notice'); + injection_points_attach +------------------------- + +(1 row) + +SELECT injection_points_attach('TestInjectionLog2', 'notice'); + injection_points_attach +------------------------- + +(1 row) + +SELECT injection_points_run('TestInjectionBooh'); -- nothing + injection_points_run +---------------------- + +(1 row) + +SELECT injection_points_run('TestInjectionLog2'); -- notice +NOTICE: notice triggered for injection point TestInjectionLog2 + injection_points_run +---------------------- + +(1 row) + +SELECT injection_points_run('TestInjectionLog'); -- notice +NOTICE: notice triggered for injection point TestInjectionLog + injection_points_run +---------------------- + +(1 row) + +SELECT injection_points_run('TestInjectionError'); -- error +ERROR: error triggered for injection point TestInjectionError +-- Re-load cache and run again. +\c +SELECT injection_points_run('TestInjectionLog2'); -- notice +NOTICE: notice triggered for injection point TestInjectionLog2 + injection_points_run +---------------------- + +(1 row) + +SELECT injection_points_run('TestInjectionLog'); -- notice +NOTICE: notice triggered for injection point TestInjectionLog + injection_points_run +---------------------- + +(1 row) + +SELECT injection_points_run('TestInjectionError'); -- error +ERROR: error triggered for injection point TestInjectionError +-- Remove one entry and check the remaining entries. +SELECT injection_points_detach('TestInjectionError'); -- ok + injection_points_detach +------------------------- + +(1 row) + +SELECT injection_points_run('TestInjectionLog'); -- notice +NOTICE: notice triggered for injection point TestInjectionLog + injection_points_run +---------------------- + +(1 row) + +SELECT injection_points_run('TestInjectionError'); -- nothing + injection_points_run +---------------------- + +(1 row) + +-- More entries removed, letting TestInjectionLog2 to check the same +-- callback used in more than one point. +SELECT injection_points_detach('TestInjectionLog'); -- ok + injection_points_detach +------------------------- + +(1 row) + +SELECT injection_points_run('TestInjectionLog'); -- nothing + injection_points_run +---------------------- + +(1 row) + +SELECT injection_points_run('TestInjectionError'); -- nothing + injection_points_run +---------------------- + +(1 row) + +SELECT injection_points_run('TestInjectionLog2'); -- notice +NOTICE: notice triggered for injection point TestInjectionLog2 + injection_points_run +---------------------- + +(1 row) + +SELECT injection_points_detach('TestInjectionLog'); -- fails +ERROR: injection point "TestInjectionLog" not found +SELECT injection_points_run('TestInjectionLog2'); -- notice +NOTICE: notice triggered for injection point TestInjectionLog2 + injection_points_run +---------------------- + +(1 row) + +DROP EXTENSION injection_points; diff --git a/src/test/modules/injection_points/injection_points--1.0.sql b/src/test/modules/injection_points/injection_points--1.0.sql new file mode 100644 index 0000000000..5944c41716 --- /dev/null +++ b/src/test/modules/injection_points/injection_points--1.0.sql @@ -0,0 +1,35 @@ +/* src/test/modules/injection_points/injection_points--1.0.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION injection_points" to load this file. \quit + +-- +-- injection_points_attach() +-- +-- Attaches the action to the given injection point. +-- +CREATE FUNCTION injection_points_attach(IN point_name TEXT, + IN action text) +RETURNS void +AS 'MODULE_PATHNAME', 'injection_points_attach' +LANGUAGE C STRICT PARALLEL UNSAFE; + +-- +-- injection_points_run() +-- +-- Executes the action attached to the injection point. +-- +CREATE FUNCTION injection_points_run(IN point_name TEXT) +RETURNS void +AS 'MODULE_PATHNAME', 'injection_points_run' +LANGUAGE C STRICT PARALLEL UNSAFE; + +-- +-- injection_points_detach() +-- +-- Detaches the current action, if any, from the given injection point. +-- +CREATE FUNCTION injection_points_detach(IN point_name TEXT) +RETURNS void +AS 'MODULE_PATHNAME', 'injection_points_detach' +LANGUAGE C STRICT PARALLEL UNSAFE; diff --git a/src/test/modules/injection_points/injection_points.c b/src/test/modules/injection_points/injection_points.c new file mode 100644 index 0000000000..e843e6594f --- /dev/null +++ b/src/test/modules/injection_points/injection_points.c @@ -0,0 +1,95 @@ +/*-------------------------------------------------------------------------- + * + * injection_points.c + * Code for testing injection points. + * + * Injection points are able to trigger user-defined callbacks in pre-defined + * code paths. + * + * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/test/modules/injection_points/injection_points.c + * + * ------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "fmgr.h" +#include "storage/lwlock.h" +#include "storage/shmem.h" +#include "utils/builtins.h" +#include "utils/injection_point.h" +#include "utils/wait_event.h" + +PG_MODULE_MAGIC; + +extern PGDLLEXPORT void injection_error(const char *name); +extern PGDLLEXPORT void injection_notice(const char *name); + + +/* Set of callbacks available to be attached to an injection point. */ +void +injection_error(const char *name) +{ + elog(ERROR, "error triggered for injection point %s", name); +} + +void +injection_notice(const char *name) +{ + elog(NOTICE, "notice triggered for injection point %s", name); +} + +/* + * SQL function for creating an injection point. + */ +PG_FUNCTION_INFO_V1(injection_points_attach); +Datum +injection_points_attach(PG_FUNCTION_ARGS) +{ + char *name = text_to_cstring(PG_GETARG_TEXT_PP(0)); + char *action = text_to_cstring(PG_GETARG_TEXT_PP(1)); + char *function; + + if (strcmp(action, "error") == 0) + function = "injection_error"; + else if (strcmp(action, "notice") == 0) + function = "injection_notice"; + else + elog(ERROR, "incorrect action \"%s\" for injection point creation", action); + + InjectionPointAttach(name, "injection_points", function); + + PG_RETURN_VOID(); +} + +/* + * SQL function for triggering an injection point. + */ +PG_FUNCTION_INFO_V1(injection_points_run); +Datum +injection_points_run(PG_FUNCTION_ARGS) +{ + char *name = text_to_cstring(PG_GETARG_TEXT_PP(0)); + + INJECTION_POINT(name); + + PG_RETURN_VOID(); +} + +/* + * SQL function for dropping an injection point. + */ +PG_FUNCTION_INFO_V1(injection_points_detach); +Datum +injection_points_detach(PG_FUNCTION_ARGS) +{ + char *name = text_to_cstring(PG_GETARG_TEXT_PP(0)); + + InjectionPointDetach(name); + + PG_RETURN_VOID(); +} diff --git a/src/test/modules/injection_points/injection_points.control b/src/test/modules/injection_points/injection_points.control new file mode 100644 index 0000000000..b4214f3bdc --- /dev/null +++ b/src/test/modules/injection_points/injection_points.control @@ -0,0 +1,4 @@ +comment = 'Test code for injection points' +default_version = '1.0' +module_pathname = '$libdir/injection_points' +relocatable = true diff --git a/src/test/modules/injection_points/meson.build b/src/test/modules/injection_points/meson.build new file mode 100644 index 0000000000..ee37573f2a --- /dev/null +++ b/src/test/modules/injection_points/meson.build @@ -0,0 +1,37 @@ +# Copyright (c) 2022-2024, PostgreSQL Global Development Group + +if not get_option('injection_points') + subdir_done() +endif + +injection_points_sources = files( + 'injection_points.c', +) + +if host_system == 'windows' + injection_points_sources += rc_lib_gen.process(win32ver_rc, extra_args: [ + '--NAME', 'injection_points', + '--FILEDESC', 'injection_points - facility for injection points',]) +endif + +injection_points = shared_module('injection_points', + injection_points_sources, + kwargs: pg_test_mod_args, +) +test_install_libs += injection_points + +test_install_data += files( + 'injection_points.control', + 'injection_points--1.0.sql', +) + +tests += { + 'name': 'injection_points', + 'sd': meson.current_source_dir(), + 'bd': meson.current_build_dir(), + 'regress': { + 'sql': [ + 'injection_points', + ], + }, +} diff --git a/src/test/modules/injection_points/sql/injection_points.sql b/src/test/modules/injection_points/sql/injection_points.sql new file mode 100644 index 0000000000..23c7e435ad --- /dev/null +++ b/src/test/modules/injection_points/sql/injection_points.sql @@ -0,0 +1,34 @@ +CREATE EXTENSION injection_points; + +SELECT injection_points_attach('TestInjectionBooh', 'booh'); +SELECT injection_points_attach('TestInjectionError', 'error'); +SELECT injection_points_attach('TestInjectionLog', 'notice'); +SELECT injection_points_attach('TestInjectionLog2', 'notice'); + +SELECT injection_points_run('TestInjectionBooh'); -- nothing +SELECT injection_points_run('TestInjectionLog2'); -- notice +SELECT injection_points_run('TestInjectionLog'); -- notice +SELECT injection_points_run('TestInjectionError'); -- error + +-- Re-load cache and run again. +\c +SELECT injection_points_run('TestInjectionLog2'); -- notice +SELECT injection_points_run('TestInjectionLog'); -- notice +SELECT injection_points_run('TestInjectionError'); -- error + +-- Remove one entry and check the remaining entries. +SELECT injection_points_detach('TestInjectionError'); -- ok +SELECT injection_points_run('TestInjectionLog'); -- notice +SELECT injection_points_run('TestInjectionError'); -- nothing +-- More entries removed, letting TestInjectionLog2 to check the same +-- callback used in more than one point. +SELECT injection_points_detach('TestInjectionLog'); -- ok +SELECT injection_points_run('TestInjectionLog'); -- nothing +SELECT injection_points_run('TestInjectionError'); -- nothing +SELECT injection_points_run('TestInjectionLog2'); -- notice + +SELECT injection_points_detach('TestInjectionLog'); -- fails + +SELECT injection_points_run('TestInjectionLog2'); -- notice + +DROP EXTENSION injection_points; diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build index 2c3b8d73bc..397e0906e6 100644 --- a/src/test/modules/meson.build +++ b/src/test/modules/meson.build @@ -5,6 +5,7 @@ subdir('commit_ts') subdir('delay_execution') subdir('dummy_index_am') subdir('dummy_seclabel') +subdir('injection_points') subdir('ldap_password_func') subdir('libpq_pipeline') subdir('plsample')