diff options
| author | Michael Paquier | 2022-10-11 02:45:52 +0000 |
|---|---|---|
| committer | Michael Paquier | 2022-10-11 02:45:52 +0000 |
| commit | 9fcdf2c787ac6da330165ea3cd50ec5155943a2b (patch) | |
| tree | 6e376c7b1258238403feb61da7fc90815d803258 /src/test | |
| parent | 0e87dfe46443286e630e9bcbc0c39a39c2c2cbb2 (diff) | |
Add support for COPY TO callback functions
This is useful as a way for extensions to process COPY TO rows in the
way they see fit (say auditing, analytics, backend, etc.) without the
need to invoke an external process running as the OS user running the
backend through PROGRAM that requires superuser rights. COPY FROM
already provides a similar callback for logical replication. For COPY
TO, the callback is triggered when we are ready to send a row in
CopySendEndOfRow(), which is the same code path as when sending a row
to a frontend or a pipe/file.
A small test module, test_copy_callbacks, is added to provide some
coverage for this facility.
Author: Bilva Sanaba, Nathan Bossart
Discussion: https://postgr.es/m/253C21D1-FCEB-41D9-A2AF-E6517015B7D7@amazon.com
Diffstat (limited to 'src/test')
10 files changed, 143 insertions, 0 deletions
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile index 6c31c8707c2..7b3f2929652 100644 --- a/src/test/modules/Makefile +++ b/src/test/modules/Makefile @@ -15,6 +15,7 @@ SUBDIRS = \ snapshot_too_old \ spgist_name_ops \ test_bloomfilter \ + test_copy_callbacks \ test_ddl_deparse \ test_extensions \ test_ginpostinglist \ diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build index a80e6e2ce29..c2e5f5ffd5a 100644 --- a/src/test/modules/meson.build +++ b/src/test/modules/meson.build @@ -9,6 +9,7 @@ subdir('snapshot_too_old') subdir('spgist_name_ops') subdir('ssl_passphrase_callback') subdir('test_bloomfilter') +subdir('test_copy_callbacks') subdir('test_ddl_deparse') subdir('test_extensions') subdir('test_ginpostinglist') diff --git a/src/test/modules/test_copy_callbacks/.gitignore b/src/test/modules/test_copy_callbacks/.gitignore new file mode 100644 index 00000000000..5dcb3ff9723 --- /dev/null +++ b/src/test/modules/test_copy_callbacks/.gitignore @@ -0,0 +1,4 @@ +# Generated subdirectories +/log/ +/results/ +/tmp_check/ diff --git a/src/test/modules/test_copy_callbacks/Makefile b/src/test/modules/test_copy_callbacks/Makefile new file mode 100644 index 00000000000..82e890150d1 --- /dev/null +++ b/src/test/modules/test_copy_callbacks/Makefile @@ -0,0 +1,23 @@ +# src/test/modules/test_copy_callbacks/Makefile + +MODULE_big = test_copy_callbacks +OBJS = \ + $(WIN32RES) \ + test_copy_callbacks.o +PGFILEDESC = "test_copy_callbacks - test COPY callbacks" + +EXTENSION = test_copy_callbacks +DATA = test_copy_callbacks--1.0.sql + +REGRESS = test_copy_callbacks + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = src/test/modules/test_copy_callbacks +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/src/test/modules/test_copy_callbacks/expected/test_copy_callbacks.out b/src/test/modules/test_copy_callbacks/expected/test_copy_callbacks.out new file mode 100644 index 00000000000..93ebeef1301 --- /dev/null +++ b/src/test/modules/test_copy_callbacks/expected/test_copy_callbacks.out @@ -0,0 +1,13 @@ +CREATE EXTENSION test_copy_callbacks; +CREATE TABLE public.test (a INT, b INT, c INT); +INSERT INTO public.test VALUES (1, 2, 3), (12, 34, 56), (123, 456, 789); +SELECT test_copy_to_callback('public.test'::pg_catalog.regclass); +NOTICE: COPY TO callback called with data "1 2 3" and length 5 +NOTICE: COPY TO callback called with data "12 34 56" and length 8 +NOTICE: COPY TO callback called with data "123 456 789" and length 11 +NOTICE: COPY TO callback has processed 3 rows + test_copy_to_callback +----------------------- + +(1 row) + diff --git a/src/test/modules/test_copy_callbacks/meson.build b/src/test/modules/test_copy_callbacks/meson.build new file mode 100644 index 00000000000..43eca8e3d9d --- /dev/null +++ b/src/test/modules/test_copy_callbacks/meson.build @@ -0,0 +1,34 @@ +# FIXME: prevent install during main install, but not during test :/ + +test_copy_callbacks_sources = files( + 'test_copy_callbacks.c', +) + +if host_system == 'windows' + test_copy_callbacks_sources += rc_lib_gen.process(win32ver_rc, extra_args: [ + '--NAME', 'test_copy_callbacks', + '--FILEDESC', 'test_copy_callbacks - test COPY callbacks',]) +endif + +test_copy_callbacks = shared_module('test_copy_callbacks', + test_copy_callbacks_sources, + kwargs: pg_mod_args, +) +testprep_targets += test_copy_callbacks + +install_data( + 'test_copy_callbacks.control', + 'test_copy_callbacks--1.0.sql', + kwargs: contrib_data_args, +) + +tests += { + 'name': 'test_copy_callbacks', + 'sd': meson.current_source_dir(), + 'bd': meson.current_build_dir(), + 'regress': { + 'sql': [ + 'test_copy_callbacks', + ], + }, +} diff --git a/src/test/modules/test_copy_callbacks/sql/test_copy_callbacks.sql b/src/test/modules/test_copy_callbacks/sql/test_copy_callbacks.sql new file mode 100644 index 00000000000..2deffba635c --- /dev/null +++ b/src/test/modules/test_copy_callbacks/sql/test_copy_callbacks.sql @@ -0,0 +1,4 @@ +CREATE EXTENSION test_copy_callbacks; +CREATE TABLE public.test (a INT, b INT, c INT); +INSERT INTO public.test VALUES (1, 2, 3), (12, 34, 56), (123, 456, 789); +SELECT test_copy_to_callback('public.test'::pg_catalog.regclass); diff --git a/src/test/modules/test_copy_callbacks/test_copy_callbacks--1.0.sql b/src/test/modules/test_copy_callbacks/test_copy_callbacks--1.0.sql new file mode 100644 index 00000000000..215cf3fad69 --- /dev/null +++ b/src/test/modules/test_copy_callbacks/test_copy_callbacks--1.0.sql @@ -0,0 +1,8 @@ +/* src/test/modules/test_copy_callbacks/test_copy_callbacks--1.0.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION test_copy_callbacks" to load this file. \quit + +CREATE FUNCTION test_copy_to_callback(pg_catalog.regclass) + RETURNS pg_catalog.void + AS 'MODULE_PATHNAME' LANGUAGE C; diff --git a/src/test/modules/test_copy_callbacks/test_copy_callbacks.c b/src/test/modules/test_copy_callbacks/test_copy_callbacks.c new file mode 100644 index 00000000000..ecdbe4eee1b --- /dev/null +++ b/src/test/modules/test_copy_callbacks/test_copy_callbacks.c @@ -0,0 +1,51 @@ +/*-------------------------------------------------------------------------- + * + * test_copy_callbacks.c + * Code for testing COPY callbacks. + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/test/modules/test_copy_callbacks/test_copy_callbacks.c + * + * ------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/table.h" +#include "commands/copy.h" +#include "fmgr.h" +#include "utils/rel.h" + +PG_MODULE_MAGIC; + +static void +to_cb(void *data, int len) +{ + ereport(NOTICE, + (errmsg("COPY TO callback called with data \"%s\" and length %d", + (char *) data, len))); +} + +PG_FUNCTION_INFO_V1(test_copy_to_callback); +Datum +test_copy_to_callback(PG_FUNCTION_ARGS) +{ + Relation rel = table_open(PG_GETARG_OID(0), AccessShareLock); + CopyToState cstate; + int64 processed; + + cstate = BeginCopyTo(NULL, rel, NULL, RelationGetRelid(rel), NULL, NULL, + to_cb, NIL, NIL); + processed = DoCopyTo(cstate); + EndCopyTo(cstate); + + ereport(NOTICE, (errmsg("COPY TO callback has processed %lld rows", + (long long) processed))); + + table_close(rel, NoLock); + + PG_RETURN_VOID(); +} diff --git a/src/test/modules/test_copy_callbacks/test_copy_callbacks.control b/src/test/modules/test_copy_callbacks/test_copy_callbacks.control new file mode 100644 index 00000000000..b7ce3f12ff8 --- /dev/null +++ b/src/test/modules/test_copy_callbacks/test_copy_callbacks.control @@ -0,0 +1,4 @@ +comment = 'Test code for COPY callbacks' +default_version = '1.0' +module_pathname = '$libdir/test_copy_callbacks' +relocatable = true |
