0006-Add-regression-test-module-for-custom-COPY-format.patch
application/octet-stream
Filename: 0006-Add-regression-test-module-for-custom-COPY-format.patch
Type: application/octet-stream
Part: 1
Patch
Format: format-patch
Series: patch 0006
Subject: Add regression test module for custom COPY format.
| File | + | − |
|---|---|---|
| src/test/modules/Makefile | 1 | 0 |
| src/test/modules/meson.build | 1 | 0 |
| src/test/modules/test_copy_custom_format/expected/test_copy_custom_format.out | 97 | 0 |
| src/test/modules/test_copy_custom_format/.gitignore | 3 | 0 |
| src/test/modules/test_copy_custom_format/Makefile | 21 | 0 |
| src/test/modules/test_copy_custom_format/meson.build | 35 | 0 |
| src/test/modules/test_copy_custom_format/sql/test_copy_custom_format.sql | 34 | 0 |
| src/test/modules/test_copy_custom_format/test_copy_custom_format.c | 217 | 0 |
| src/test/modules/test_copy_custom_format/test_copy_custom_format.conf | 1 | 0 |
From 2718f015e44354dca5ba99927d6ef0aa2dbb3b1f Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <sawada.mshk@gmail.com>
Date: Mon, 10 Nov 2025 22:18:28 -0800
Subject: [PATCH 6/6] Add regression test module for custom COPY format.
---
src/test/modules/Makefile | 1 +
src/test/modules/meson.build | 1 +
.../test_copy_custom_format/.gitignore | 3 +
.../modules/test_copy_custom_format/Makefile | 21 ++
.../expected/test_copy_custom_format.out | 97 ++++++++
.../test_copy_custom_format/meson.build | 35 +++
.../sql/test_copy_custom_format.sql | 34 +++
.../test_copy_custom_format.c | 217 ++++++++++++++++++
.../test_copy_custom_format.conf | 1 +
9 files changed, 410 insertions(+)
create mode 100644 src/test/modules/test_copy_custom_format/.gitignore
create mode 100644 src/test/modules/test_copy_custom_format/Makefile
create mode 100644 src/test/modules/test_copy_custom_format/expected/test_copy_custom_format.out
create mode 100644 src/test/modules/test_copy_custom_format/meson.build
create mode 100644 src/test/modules/test_copy_custom_format/sql/test_copy_custom_format.sql
create mode 100644 src/test/modules/test_copy_custom_format/test_copy_custom_format.c
create mode 100644 src/test/modules/test_copy_custom_format/test_copy_custom_format.conf
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index 902a7954101..710ccb74990 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -19,6 +19,7 @@ SUBDIRS = \
test_bitmapset \
test_bloomfilter \
test_copy_callbacks \
+ test_copy_custom_format \
test_custom_rmgrs \
test_ddl_deparse \
test_dsa \
diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build
index 14fc761c4cf..5a2e4e03b80 100644
--- a/src/test/modules/meson.build
+++ b/src/test/modules/meson.build
@@ -18,6 +18,7 @@ subdir('test_binaryheap')
subdir('test_bitmapset')
subdir('test_bloomfilter')
subdir('test_copy_callbacks')
+subdir('test_copy_custom_format')
subdir('test_custom_rmgrs')
subdir('test_ddl_deparse')
subdir('test_dsa')
diff --git a/src/test/modules/test_copy_custom_format/.gitignore b/src/test/modules/test_copy_custom_format/.gitignore
new file mode 100644
index 00000000000..8d3af519eb1
--- /dev/null
+++ b/src/test/modules/test_copy_custom_format/.gitignore
@@ -0,0 +1,3 @@
+/log/
+/results/
+/tmp_check/
\ No newline at end of file
diff --git a/src/test/modules/test_copy_custom_format/Makefile b/src/test/modules/test_copy_custom_format/Makefile
new file mode 100644
index 00000000000..8ad1cebaba6
--- /dev/null
+++ b/src/test/modules/test_copy_custom_format/Makefile
@@ -0,0 +1,21 @@
+# src/test/modules/test_copy_custom_format/Makefile
+
+MODULE_big = test_copy_custom_format
+OBJS = \
+ $(WIN32RES) \
+ test_copy_custom_format.o
+PGFILEDESC = "test_copy_custom_format - test custom COPY FORMAT"
+
+REGRESS = test_copy_custom_format
+REGRESS_OPTS = --temp-config $(top_srcdir)/src/test/modules/test_copy_custom_format/test_copy_custom_format.conf
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_copy_custom_format
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_copy_custom_format/expected/test_copy_custom_format.out b/src/test/modules/test_copy_custom_format/expected/test_copy_custom_format.out
new file mode 100644
index 00000000000..a91ac293447
--- /dev/null
+++ b/src/test/modules/test_copy_custom_format/expected/test_copy_custom_format.out
@@ -0,0 +1,97 @@
+CREATE TABLE copy_data (a smallint, b integer, c bigint);
+INSERT INTO copy_data VALUES (1, 2, 3), (12, 34, 56), (123, 456, 789);
+COPY copy_data FROM stdin WITH (FORMAT 'test_format');
+NOTICE: CopyFromInFunc: attribute: smallint
+NOTICE: CopyFromInFunc: attribute: integer
+NOTICE: CopyFromInFunc: attribute: bigint
+NOTICE: CopyFromStart: the number of attributes: 3
+NOTICE: common_int 0 common_bool 0 from_str "(null)"
+NOTICE: CopyFromOneRow
+NOTICE: CopyFromEnd
+COPY copy_data TO stdout WITH (FORMAT 'test_format');
+NOTICE: CopyToOutFunc: attribute: smallint
+NOTICE: CopyToOutFunc: attribute: integer
+NOTICE: CopyToOutFunc: attribute: bigint
+NOTICE: CopyToStart: the number of attributes: 3
+NOTICE: common_int 0 common_bool 0 to_str "(null)"
+NOTICE: CopyToOneRow: the number of valid values: 3
+NOTICE: CopyToOneRow: the number of valid values: 3
+NOTICE: CopyToOneRow: the number of valid values: 3
+NOTICE: CopyToEnd
+-- Error
+COPY copy_data FROM stdin WITH (FORMAT 'error');
+ERROR: COPY format "error" not recognized
+LINE 1: COPY copy_data FROM stdin WITH (FORMAT 'error');
+ ^
+COPY copy_data FROM stdin WITH (FORMAT 'text', FORMAT 'error');
+ERROR: conflicting or redundant options
+LINE 1: COPY copy_data FROM stdin WITH (FORMAT 'text', FORMAT 'error...
+ ^
+-- Option handling, COPY FROM
+COPY copy_data FROM stdin WITH (FORMAT 'test_format', common_int 10);
+NOTICE: CopyFromInFunc: attribute: smallint
+NOTICE: CopyFromInFunc: attribute: integer
+NOTICE: CopyFromInFunc: attribute: bigint
+NOTICE: CopyFromStart: the number of attributes: 3
+NOTICE: common_int 10 common_bool 0 from_str "(null)"
+NOTICE: CopyFromOneRow
+NOTICE: CopyFromEnd
+COPY copy_data FROM stdin WITH (FORMAT 'test_format', common_int -10);
+NOTICE: CopyFromInFunc: attribute: smallint
+NOTICE: CopyFromInFunc: attribute: integer
+NOTICE: CopyFromInFunc: attribute: bigint
+NOTICE: CopyFromStart: the number of attributes: 3
+NOTICE: common_int -10 common_bool 0 from_str "(null)"
+NOTICE: CopyFromOneRow
+NOTICE: CopyFromEnd
+COPY copy_data FROM stdin WITH (FORMAT 'test_format', common_int 'a'); -- error
+ERROR: common_int requires an integer value
+COPY copy_data FROM stdin WITH (FORMAT 'test_format', common_bool 'true');
+NOTICE: CopyFromInFunc: attribute: smallint
+NOTICE: CopyFromInFunc: attribute: integer
+NOTICE: CopyFromInFunc: attribute: bigint
+NOTICE: CopyFromStart: the number of attributes: 3
+NOTICE: common_int 0 common_bool 1 from_str "(null)"
+NOTICE: CopyFromOneRow
+NOTICE: CopyFromEnd
+COPY copy_data FROM stdin WITH (FORMAT 'test_format', common_bool 'false');
+NOTICE: CopyFromInFunc: attribute: smallint
+NOTICE: CopyFromInFunc: attribute: integer
+NOTICE: CopyFromInFunc: attribute: bigint
+NOTICE: CopyFromStart: the number of attributes: 3
+NOTICE: common_int 0 common_bool 0 from_str "(null)"
+NOTICE: CopyFromOneRow
+NOTICE: CopyFromEnd
+COPY copy_data FROM stdin WITH (FORMAT 'test_format', common_bool 'hello'); -- error
+ERROR: common_bool requires a Boolean value
+COPY copy_data FROM stdin WITH (FORMAT 'test_format', common_int 100, common_bool false, from_str 'from option');
+NOTICE: CopyFromInFunc: attribute: smallint
+NOTICE: CopyFromInFunc: attribute: integer
+NOTICE: CopyFromInFunc: attribute: bigint
+NOTICE: CopyFromStart: the number of attributes: 3
+NOTICE: common_int 100 common_bool 0 from_str "from option"
+NOTICE: CopyFromOneRow
+NOTICE: CopyFromEnd
+COPY copy_data FROM stdin WITH (FORMAT 'test_format', invalid 'option'); -- error
+ERROR: COPY format "invalid" not recognized
+LINE 1: ... copy_data FROM stdin WITH (FORMAT 'test_format', invalid 'o...
+ ^
+COPY copy_data FROM stdin WITH (FORMAT 'test_format', format 'text'); -- error
+ERROR: conflicting or redundant options
+LINE 1: ... copy_data FROM stdin WITH (FORMAT 'test_format', format 'te...
+ ^
+-- Option handling, COPY FROM
+COPY copy_data TO stdout WITH (FORMAT 'test_format', common_int -10, common_bool false, to_str 'to option');
+NOTICE: CopyToOutFunc: attribute: smallint
+NOTICE: CopyToOutFunc: attribute: integer
+NOTICE: CopyToOutFunc: attribute: bigint
+NOTICE: CopyToStart: the number of attributes: 3
+NOTICE: common_int -10 common_bool 0 to_str "to option"
+NOTICE: CopyToOneRow: the number of valid values: 3
+NOTICE: CopyToOneRow: the number of valid values: 3
+NOTICE: CopyToOneRow: the number of valid values: 3
+NOTICE: CopyToEnd
+COPY copy_data TO stdout WITH (FORMAT 'test_format', from_str 'to option'); -- error
+ERROR: COPY format "from_str" not recognized
+LINE 1: ...Y copy_data TO stdout WITH (FORMAT 'test_format', from_str '...
+ ^
diff --git a/src/test/modules/test_copy_custom_format/meson.build b/src/test/modules/test_copy_custom_format/meson.build
new file mode 100644
index 00000000000..6cc6c8b60fd
--- /dev/null
+++ b/src/test/modules/test_copy_custom_format/meson.build
@@ -0,0 +1,35 @@
+# Copyright (c) 2025, PostgreSQL Global Development Group
+
+test_copy_custom_format_sources = files(
+'test_copy_custom_format.c',
+)
+
+if host_system == 'windows'
+ test_copy_custom_format_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+ '--NAME', 'test_copy_custom_format',
+ '--FILEDESC', 'test_copy_custom_format - test custom COPY FORMAT',])
+endif
+
+test_copy_custom_format = shared_module('test_copy_custom_format',
+ test_copy_custom_format_sources,
+ kwargs: pg_test_mod_args,
+)
+test_install_libs += test_copy_custom_format
+
+tests += {
+ 'name': 'test_copy_custom_format',
+ 'sd': meson.current_source_dir(),
+ 'bd': meson.current_build_dir(),
+ 'regress': {
+ 'sql': [
+ 'test_copy_custom_format',
+ ],
+ 'regress_args': [
+ '--temp-config', files('test_copy_custom_format.conf'),
+ ],
+ # Disabled because these tests require
+ # "shared_preload_libraries=test_custom_copy_format", which typical
+ # runningcheck users do not have (e.g. buildfarm clients).
+ 'runningcheck': false,
+ },
+}
diff --git a/src/test/modules/test_copy_custom_format/sql/test_copy_custom_format.sql b/src/test/modules/test_copy_custom_format/sql/test_copy_custom_format.sql
new file mode 100644
index 00000000000..b6564f0355b
--- /dev/null
+++ b/src/test/modules/test_copy_custom_format/sql/test_copy_custom_format.sql
@@ -0,0 +1,34 @@
+CREATE TABLE copy_data (a smallint, b integer, c bigint);
+INSERT INTO copy_data VALUES (1, 2, 3), (12, 34, 56), (123, 456, 789);
+
+COPY copy_data FROM stdin WITH (FORMAT 'test_format');
+\.
+COPY copy_data TO stdout WITH (FORMAT 'test_format');
+
+-- Error
+COPY copy_data FROM stdin WITH (FORMAT 'error');
+COPY copy_data FROM stdin WITH (FORMAT 'text', FORMAT 'error');
+
+
+-- Option handling, COPY FROM
+COPY copy_data FROM stdin WITH (FORMAT 'test_format', common_int 10);
+\.
+COPY copy_data FROM stdin WITH (FORMAT 'test_format', common_int -10);
+\.
+COPY copy_data FROM stdin WITH (FORMAT 'test_format', common_int 'a'); -- error
+
+COPY copy_data FROM stdin WITH (FORMAT 'test_format', common_bool 'true');
+\.
+COPY copy_data FROM stdin WITH (FORMAT 'test_format', common_bool 'false');
+\.
+COPY copy_data FROM stdin WITH (FORMAT 'test_format', common_bool 'hello'); -- error
+
+COPY copy_data FROM stdin WITH (FORMAT 'test_format', common_int 100, common_bool false, from_str 'from option');
+\.
+
+COPY copy_data FROM stdin WITH (FORMAT 'test_format', invalid 'option'); -- error
+COPY copy_data FROM stdin WITH (FORMAT 'test_format', format 'text'); -- error
+
+-- Option handling, COPY FROM
+COPY copy_data TO stdout WITH (FORMAT 'test_format', common_int -10, common_bool false, to_str 'to option');
+COPY copy_data TO stdout WITH (FORMAT 'test_format', from_str 'to option'); -- error
diff --git a/src/test/modules/test_copy_custom_format/test_copy_custom_format.c b/src/test/modules/test_copy_custom_format/test_copy_custom_format.c
new file mode 100644
index 00000000000..a63390e875b
--- /dev/null
+++ b/src/test/modules/test_copy_custom_format/test_copy_custom_format.c
@@ -0,0 +1,217 @@
+/*--------------------------------------------------------------------------
+ *
+ * test_copy_custom_format.c
+ * Code for testing custom COPY format.
+ *
+ * Portions Copyright (c) 2025, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/test/modules/test_copy_custom_format/test_copy_custom_format.c
+ *
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "commands/copystate.h"
+#include "commands/copyapi.h"
+#include "commands/defrem.h"
+#include "utils/builtins.h"
+
+PG_MODULE_MAGIC;
+
+typedef struct TestCopyCommonOption
+{
+ int common_int;
+ bool common_bool;
+} TestCopyCommonOption;
+
+typedef struct TestCopyFromState
+{
+ CopyFromStateData base;
+
+ TestCopyCommonOption common_opts;
+ char *from_str_option;
+} TestCopyFromState;
+
+typedef struct TestCopyToState
+{
+ CopyToStateData base;
+
+ TestCopyCommonOption common_opts;
+ char *to_str_option;
+} TestCopyToState;
+
+static Size
+TestCopyToEsimateSpace(void)
+{
+ return sizeof(TestCopyToState);
+}
+
+static Size
+TestCopyFromEsimateSpace(void)
+{
+ return sizeof(TestCopyFromState);
+}
+
+static bool
+TestCopyProcessCommonOption(TestCopyCommonOption * opt, DefElem *option)
+{
+ if (strcmp(option->defname, "common_int") == 0)
+ {
+ int val = defGetInt32(option);
+
+ opt->common_int = val;
+
+ return true;
+ }
+ else if (strcmp(option->defname, "common_bool") == 0)
+ {
+ bool val = defGetBoolean(option);
+
+ opt->common_bool = val;
+
+ return true;
+ }
+
+ return false;
+}
+
+static bool
+TestCopyFromProcessOneOption(CopyFromState ccstate, DefElem *option)
+{
+ TestCopyFromState *cstate = (TestCopyFromState *) ccstate;
+
+ if (TestCopyProcessCommonOption(&cstate->common_opts, option))
+ {
+ return true;
+ }
+ else if (strcmp(option->defname, "from_str") == 0)
+ {
+ char *val = defGetString(option);
+
+ cstate->from_str_option = val;
+ return true;
+ }
+
+ return false;
+}
+
+static bool
+TestCopyToProcessOneOption(CopyToState ccstate, DefElem *option)
+{
+ TestCopyToState *cstate = (TestCopyToState *) ccstate;
+
+ if (TestCopyProcessCommonOption(&cstate->common_opts, option))
+ {
+ return true;
+ }
+ else if (strcmp(option->defname, "to_str") == 0)
+ {
+ char *val = defGetString(option);
+
+ cstate->to_str_option = val;
+ return true;
+ }
+
+ return false;
+}
+
+static void
+TestCopyFromInFunc(CopyFromState cstate, Oid atttypid,
+ FmgrInfo *finfo, Oid *typioparam)
+{
+ ereport(NOTICE,
+ errmsg("CopyFromInFunc: attribute: %s", format_type_be(atttypid)));
+}
+
+static void
+TestCopyFromStart(CopyFromState ccstate, TupleDesc tupDesc)
+{
+ TestCopyFromState *cstate = (TestCopyFromState *) ccstate;
+
+ ereport(NOTICE,
+ errmsg("CopyFromStart: the number of attributes: %d", tupDesc->natts));
+ ereport(NOTICE,
+ errmsg("common_int %d common_bool %d from_str \"%s\"",
+ cstate->common_opts.common_int,
+ cstate->common_opts.common_bool,
+ cstate->from_str_option));
+}
+
+static bool
+TestCopyFromOneRow(CopyFromState cstate, ExprContext *econtext, Datum *values, bool *nulls,
+ CopyFromRowInfo * rowinfo)
+{
+ ereport(NOTICE,
+ errmsg("CopyFromOneRow"));
+
+ return false;
+}
+
+static void
+TestCopyFromEnd(CopyFromState cstate)
+{
+ ereport(NOTICE,
+ errmsg("CopyFromEnd"));
+}
+
+static void
+TestCopyToOutFunc(CopyToState cstate, Oid atttypid, FmgrInfo *finfo)
+{
+ ereport(NOTICE,
+ errmsg("CopyToOutFunc: attribute: %s", format_type_be(atttypid)));
+}
+
+static void
+TestCopyToStart(CopyToState ccstate, TupleDesc tupDesc)
+{
+ TestCopyToState *cstate = (TestCopyToState *) ccstate;
+
+ ereport(NOTICE,
+ errmsg("CopyToStart: the number of attributes: %d", tupDesc->natts));
+ ereport(NOTICE,
+ errmsg("common_int %d common_bool %d to_str \"%s\"",
+ cstate->common_opts.common_int,
+ cstate->common_opts.common_bool,
+ cstate->to_str_option));
+}
+
+static void
+TestCopyToOneRow(CopyToState cstate, TupleTableSlot *slot)
+{
+ ereport(NOTICE, (errmsg("CopyToOneRow: the number of valid values: %u", slot->tts_nvalid)));
+}
+
+static void
+TestCopyToEnd(CopyToState cstate)
+{
+ ereport(NOTICE, (errmsg("CopyToEnd")));
+}
+
+static const CopyToRoutine CopyToRoutineTestCopyFormat = {
+ .CopyToEstimateStateSpace = TestCopyToEsimateSpace,
+ .CopyToProcessOneOption = TestCopyToProcessOneOption,
+ .CopyToOutFunc = TestCopyToOutFunc,
+ .CopyToStart = TestCopyToStart,
+ .CopyToOneRow = TestCopyToOneRow,
+ .CopyToEnd = TestCopyToEnd,
+};
+
+
+static const CopyFromRoutine CopyFromRoutineTestCopyFormat = {
+ .CopyFromEstimateStateSpace = TestCopyFromEsimateSpace,
+ .CopyFromProcessOneOption = TestCopyFromProcessOneOption,
+ .CopyFromInFunc = TestCopyFromInFunc,
+ .CopyFromStart = TestCopyFromStart,
+ .CopyFromOneRow = TestCopyFromOneRow,
+ .CopyFromEnd = TestCopyFromEnd,
+};
+
+void
+_PG_init(void)
+{
+ RegisterCopyCustomFormat("test_format",
+ &CopyFromRoutineTestCopyFormat,
+ &CopyToRoutineTestCopyFormat);
+}
diff --git a/src/test/modules/test_copy_custom_format/test_copy_custom_format.conf b/src/test/modules/test_copy_custom_format/test_copy_custom_format.conf
new file mode 100644
index 00000000000..fb7cf22a3b1
--- /dev/null
+++ b/src/test/modules/test_copy_custom_format/test_copy_custom_format.conf
@@ -0,0 +1 @@
+shared_preload_libraries = 'test_copy_custom_format'
--
2.47.3