mirror of https://github.com/postgres/postgres
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.compull/105/head
parent
0e87dfe464
commit
9fcdf2c787
@ -0,0 +1,4 @@ |
||||
# Generated subdirectories |
||||
/log/ |
||||
/results/ |
||||
/tmp_check/ |
@ -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 |
@ -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) |
||||
|
@ -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', |
||||
], |
||||
}, |
||||
} |
@ -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); |
@ -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; |
@ -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(); |
||||
} |
@ -0,0 +1,4 @@ |
||||
comment = 'Test code for COPY callbacks' |
||||
default_version = '1.0' |
||||
module_pathname = '$libdir/test_copy_callbacks' |
||||
relocatable = true |
Loading…
Reference in new issue