From 43c06a26a14c914f6bc3d72d01accd95e3b0f8e8 Mon Sep 17 00:00:00 2001 From: Masahiko Sawada Date: Mon, 22 Jun 2026 09:23:09 -0700 Subject: [PATCH v3 3/4] Add an hook for custom COPY format option validation. Author: Reviewed-by: Discussion: https://postgr.es/m/ --- src/backend/commands/copy.c | 9 +++++++++ src/backend/commands/copyapi.c | 4 +++- src/include/commands/copyapi.h | 14 +++++++++++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 0fda54905cd..dab523c68db 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -1119,6 +1119,15 @@ ProcessCopyOptions(ParseState *pstate, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY format \"%s\" cannot be used with COPY TO", opts_out->custom_format_ent->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 (opts_out->custom_format_ent->validate_fn != NULL) + opts_out->custom_format_ent->validate_fn(opts_out, is_from); } } diff --git a/src/backend/commands/copyapi.c b/src/backend/commands/copyapi.c index f007d73e867..29fb98ff4c2 100644 --- a/src/backend/commands/copyapi.c +++ b/src/backend/commands/copyapi.c @@ -48,7 +48,8 @@ is_builtin_copy_format(const char *name) */ void RegisterCopyCustomFormat(const char *name, const CopyToRoutine *to, - const CopyFromRoutine *from, ProcessOneCopyOptionFn option_fn) + const CopyFromRoutine *from, ProcessOneCopyOptionFn option_fn, + ValidateCopyOptionsFn validate_fn) { Assert(name != NULL && name[0] != '\0'); @@ -94,6 +95,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++; } diff --git a/src/include/commands/copyapi.h b/src/include/commands/copyapi.h index 1b323b23bba..f1924424df8 100644 --- a/src/include/commands/copyapi.h +++ b/src/include/commands/copyapi.h @@ -117,6 +117,15 @@ typedef struct CopyFromRoutine typedef bool (*ProcessOneCopyOptionFn) (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 (*ValidateCopyOptionsFn) (CopyFormatOptions *opts, bool is_from); + /* * Sturct to store the registered custom format information. */ @@ -126,11 +135,14 @@ typedef struct CopyCustomFormatEntry const CopyToRoutine *to_routine; const CopyFromRoutine *from_routine; ProcessOneCopyOptionFn option_fn; + ValidateCopyOptionsFn validate_fn; } CopyCustomFormatEntry; extern void RegisterCopyCustomFormat(const char *name, const CopyToRoutine *to, const CopyFromRoutine *from, - ProcessOneCopyOptionFn option_fn); + ProcessOneCopyOptionFn option_fn, + ValidateCopyOptionsFn validate_fn); + extern const CopyCustomFormatEntry *GetCopyCustomFormatRoutines(const char *name); #endif /* COPYAPI_H */ -- 2.54.0