From 362ceb242c7b026bcbb077c910b26512b08e1e9d Mon Sep 17 00:00:00 2001 From: Masahiko Sawada Date: Mon, 10 Nov 2025 14:53:51 -0800 Subject: [PATCH 4/6] Refactor Copy option processing. This commits refactors the COPY option handling to support custom COPY option callback. Since extensions can define custom COPY format with its own state data, it's necessary to pass the state data to the option process callback. This commits separates ProcessCopyOptions into ProcessCopyFromOptions and ProcessCopyToOptions and passes a pointer to Copy{From,To}State respectively. The subsequent patch introduces a new callback to allow extensions to define their own option processing callback. --- contrib/file_fdw/file_fdw.c | 2 +- src/backend/commands/copy.c | 18 ++++--------- src/backend/commands/copyfrom.c | 31 ++++++++++++++++++++++- src/backend/commands/copyto.c | 32 +++++++++++++++++++++++- src/include/commands/copyfrom_internal.h | 2 ++ 5 files changed, 69 insertions(+), 16 deletions(-) diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c index af2d86e5b5c..447fcb44241 100644 --- a/contrib/file_fdw/file_fdw.c +++ b/contrib/file_fdw/file_fdw.c @@ -338,7 +338,7 @@ file_fdw_validator(PG_FUNCTION_ARGS) /* * Now apply the core COPY code's validation logic for more checks. */ - ProcessCopyOptions(NULL, NULL, true, other_options); + ProcessCopyFromOptions(NULL, other_options, NULL); /* * Either filename or program option is required for file_fdw foreign diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 28e878c3688..d4ee649c3c6 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -546,10 +546,8 @@ defGetCopyLogVerbosityChoice(DefElem *def, ParseState *pstate) * self-consistency of the options list. */ void -ProcessCopyOptions(ParseState *pstate, - CopyFormatOptions *opts_out, - bool is_from, - List *options) +ProcessCopyBuiltinOptions(List *options, CopyFormatOptions *opts_out, + bool is_from, List **other_options, ParseState *pstate) { bool format_specified = false; bool freeze_specified = false; @@ -559,10 +557,6 @@ ProcessCopyOptions(ParseState *pstate, bool reject_limit_specified = false; ListCell *option; - /* Support external use for option sanity checking */ - if (opts_out == NULL) - opts_out = (CopyFormatOptions *) palloc0(sizeof(CopyFormatOptions)); - opts_out->file_encoding = -1; /* Extract options from the statement node tree */ @@ -583,6 +577,8 @@ ProcessCopyOptions(ParseState *pstate, opts_out->csv_mode = true; else if (strcmp(fmt, "binary") == 0) opts_out->binary = true; + else if (FindCustomCopyFormat(fmt)) + /* just validate option value */ ; else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -731,11 +727,7 @@ ProcessCopyOptions(ParseState *pstate, opts_out->reject_limit = defGetCopyRejectLimitOption(defel); } else - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("option \"%s\" not recognized", - defel->defname), - parser_errposition(pstate, defel->location))); + *other_options = lappend(*other_options, defel); } /* diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 592be4fcb5d..6ca5466f244 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -1705,7 +1705,7 @@ BeginCopyFrom(ParseState *pstate, oldcontext = MemoryContextSwitchTo(cstate->copycontext); /* Extract options from the statement node tree */ - ProcessCopyOptions(pstate, &cstate->opts, true /* is_from */ , options); + ProcessCopyFromOptions(cstate, options, pstate); /* Process the target relation */ cstate->rel = rel; @@ -2028,3 +2028,32 @@ ClosePipeFromProgram(CopyFromState cstate) errdetail_internal("%s", wait_result_to_str(pclose_rc)))); } } + +void +ProcessCopyFromOptions(CopyFromState cstate, List *options, ParseState *pstate) +{ + bool temp_state = false; + List *other_options = NIL; + CopyFormatOptions *opts; + + if (cstate == NULL) + { + cstate = create_copyfrom_state(pstate, options); + temp_state = true; + } + + opts = &cstate->opts; + + ProcessCopyBuiltinOptions(options, opts, true, &other_options, pstate); + + foreach_node(DefElem, option, other_options) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("COPY format \"%s\" not recognized", option->defname), + parser_errposition(pstate, option->location))); + } + + if (temp_state) + pfree(cstate); +} diff --git a/src/backend/commands/copyto.c b/src/backend/commands/copyto.c index b1b3ae141eb..ea31fa911f9 100644 --- a/src/backend/commands/copyto.c +++ b/src/backend/commands/copyto.c @@ -84,6 +84,7 @@ static void CopyAttributeOutCSV(CopyToStateTextLike * cstate, const char *string bool use_quote); static void CopyRelationTo(CopyToState cstate, Relation rel, Relation root_rel, uint64 *processed); +static void ProcessCopyToOptions(CopyToState cstate, List *options, ParseState *pstate); /* built-in format-specific routines */ static Size CopyToEstimateStateTextLike(void); @@ -785,7 +786,7 @@ BeginCopyTo(ParseState *pstate, oldcontext = MemoryContextSwitchTo(cstate->copycontext); /* Extract options from the statement node tree */ - ProcessCopyOptions(pstate, &cstate->opts, false /* is_from */ , options); + ProcessCopyToOptions(cstate, options, pstate); /* Process the source/target relation or query */ if (rel) @@ -1570,3 +1571,32 @@ CreateCopyDestReceiver(void) return (DestReceiver *) self; } + +static void +ProcessCopyToOptions(CopyToState cstate, List *options, ParseState *pstate) +{ + bool temp_state = false; + List *other_options = NIL; + CopyFormatOptions *opts; + + if (cstate == NULL) + { + cstate = create_copyto_state(pstate, options); + temp_state = true; + } + + opts = &cstate->opts; + + ProcessCopyBuiltinOptions(options, opts, false, &other_options, pstate); + + foreach_node(DefElem, option, options) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("COPY format \"%s\" not recognized", option->defname), + parser_errposition(pstate, option->location))); + } + + if (temp_state) + pfree(cstate); +} diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index d2d19536151..f457e2bee50 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -100,6 +100,8 @@ typedef struct CopyFromStateBuiltins extern void ReceiveCopyBegin(CopyFromState cstate); extern void ReceiveCopyBinaryHeader(CopyFromState cstate); +extern void ProcessCopyFromOptions(CopyFromState cstate, List *options, ParseState *pstate); + /* One-row callbacks for built-in formats defined in copyfromparse.c */ extern bool CopyFromTextOneRow(CopyFromState cstate, ExprContext *econtext, Datum *values, bool *nulls, CopyFromRowInfo * rowinfo); -- 2.47.3