v2-0003-Add-an-hook-for-custom-COPY-format-option-validat.patch
text/x-patch
Filename: v2-0003-Add-an-hook-for-custom-COPY-format-option-validat.patch
Type: text/x-patch
Part: 2
Patch
Format: format-patch
Series: patch v2-0003
Subject: Add an hook for custom COPY format option validation.
| File | + | − |
|---|---|---|
| src/backend/commands/copyapi.c | 8 | 2 |
| src/backend/commands/copy.c | 12 | 1 |
| src/include/commands/copyapi.h | 13 | 2 |
From c0e1e5986d10125b41f3fde10cdb3ba931db00b3 Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <sawada.mshk@gmail.com>
Date: Mon, 22 Jun 2026 09:23:09 -0700
Subject: [PATCH v2 3/4] Add an hook for custom COPY format option validation.
Author:
Reviewed-by:
Discussion: https://postgr.es/m/
---
src/backend/commands/copy.c | 13 ++++++++++++-
src/backend/commands/copyapi.c | 10 ++++++++--
src/include/commands/copyapi.h | 15 +++++++++++++--
3 files changed, 33 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 2fdba026ee0..45908d0c1e5 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -599,6 +599,7 @@ ProcessCopyOptions(ParseState *pstate,
*/
List *deferred_options = NIL;
ProcessOneOptionFn custom_process_option_fn = NULL;
+ ValidateOptionsFn custom_validate_options_fn = NULL;
char *custom_format_name = NULL;
/* Support external use for option sanity checking */
@@ -631,7 +632,8 @@ ProcessCopyOptions(ParseState *pstate,
opts_out->format = COPY_FORMAT_JSON;
else if (GetCopyCustomFormatRoutines(fmt, &opts_out->to_routine,
&opts_out->from_routine,
- &custom_process_option_fn))
+ &custom_process_option_fn,
+ &custom_validate_options_fn))
{
opts_out->format = COPY_FORMAT_CUSTOM;
custom_format_name = fmt;
@@ -1126,6 +1128,15 @@ ProcessCopyOptions(ParseState *pstate,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("COPY format \"%s\" cannot be used with COPY TO",
custom_format_name)));
+
+ /*
+ * Let the format validate its fully-parsed options as a whole. This
+ * runs even when no format-specific options were given, so a format
+ * can reject incompatible core options or enforce cross-option
+ * constraints.
+ */
+ if (custom_validate_options_fn != NULL)
+ custom_validate_options_fn(opts_out, is_from);
}
}
diff --git a/src/backend/commands/copyapi.c b/src/backend/commands/copyapi.c
index 168efbcf30b..0bd1cb71af2 100644
--- a/src/backend/commands/copyapi.c
+++ b/src/backend/commands/copyapi.c
@@ -28,6 +28,7 @@ typedef struct CopyCustomFormatEntry
const CopyToRoutine *to_routine;
const CopyFromRoutine *from_routine;
ProcessOneOptionFn option_fn;
+ ValidateOptionsFn validate_fn;
} CopyCustomFormatEntry;
static CopyCustomFormatEntry *CopyCustomFormatArray = NULL;
@@ -56,7 +57,8 @@ is_builtin_copy_format(const char *name)
*/
void
RegisterCopyCustomFormat(const char *name, const CopyToRoutine *to,
- const CopyFromRoutine *from, ProcessOneOptionFn option_fn)
+ const CopyFromRoutine *from, ProcessOneOptionFn option_fn,
+ ValidateOptionsFn validate_fn)
{
Assert(name != NULL && name[0] != '\0');
@@ -102,6 +104,7 @@ RegisterCopyCustomFormat(const char *name, const CopyToRoutine *to,
CopyCustomFormatArray[CopyCustomFormatsAssigned].to_routine = to;
CopyCustomFormatArray[CopyCustomFormatsAssigned].from_routine = from;
CopyCustomFormatArray[CopyCustomFormatsAssigned].option_fn = option_fn;
+ CopyCustomFormatArray[CopyCustomFormatsAssigned].validate_fn = validate_fn;
CopyCustomFormatsAssigned++;
}
@@ -111,7 +114,8 @@ RegisterCopyCustomFormat(const char *name, const CopyToRoutine *to,
*/
bool
GetCopyCustomFormatRoutines(const char *name, const CopyToRoutine **to,
- const CopyFromRoutine **from, ProcessOneOptionFn * option_fn)
+ const CopyFromRoutine **from, ProcessOneOptionFn * option_fn,
+ ValidateOptionsFn * validate_fn)
{
for (int i = 0; i < CopyCustomFormatsAssigned; i++)
{
@@ -123,6 +127,8 @@ GetCopyCustomFormatRoutines(const char *name, const CopyToRoutine **to,
*from = CopyCustomFormatArray[i].from_routine;
if (option_fn)
*option_fn = CopyCustomFormatArray[i].option_fn;
+ if (validate_fn)
+ *validate_fn = CopyCustomFormatArray[i].validate_fn;
return true;
}
diff --git a/src/include/commands/copyapi.h b/src/include/commands/copyapi.h
index 8eb5fe9c7dc..c47c89a858f 100644
--- a/src/include/commands/copyapi.h
+++ b/src/include/commands/copyapi.h
@@ -120,6 +120,15 @@ typedef struct CopyFromRoutine
typedef bool (*ProcessOneOptionFn) (CopyFormatOptions *opts, bool is_from,
DefElem *option);
+/*
+ * Optional callback to validate a custom format's fully-parsed options as a
+ * whole. Invoked once from ProcessCopyOptions() after all options have been
+ * processed, so it can enforce cross-option constraints and reject
+ * incompatible core options. It runs even when no format-specific options were
+ * supplied. Reports problems with ereport().
+ */
+typedef void (*ValidateOptionsFn) (CopyFormatOptions *opts, bool is_from);
+
/*
* Register a COPY format under 'name', mapping it to its TO and/or FROM
* routines and optional option/validation callbacks. Intended to be called
@@ -129,7 +138,8 @@ typedef bool (*ProcessOneOptionFn) (CopyFormatOptions *opts, bool is_from,
*/
extern void RegisterCopyCustomFormat(const char *name, const CopyToRoutine *to,
const CopyFromRoutine *from,
- ProcessOneOptionFn option_fn);
+ ProcessOneOptionFn option_fn,
+ ValidateOptionsFn validate_fn);
/*
* Look up a previously registered custom format. Returns false if 'name' is
@@ -137,6 +147,7 @@ extern void RegisterCopyCustomFormat(const char *name, const CopyToRoutine *to,
*/
extern bool GetCopyCustomFormatRoutines(const char *name, const CopyToRoutine **to,
const CopyFromRoutine **from,
- ProcessOneOptionFn * option_fn);
+ ProcessOneOptionFn * option_fn,
+ ValidateOptionsFn * validate_fn);
#endif /* COPYAPI_H */
--
2.54.0