From d2d530680a895235c6edaadf48ae67a08c64dfe5 Mon Sep 17 00:00:00 2001 From: TatsuyaKawata Date: Tue, 25 Nov 2025 01:00:42 +0900 Subject: [PATCH v1] Adding TRIM_SPACE option to COPY --- src/backend/commands/copy.c | 13 +++++++ src/backend/commands/copyfromparse.c | 56 ++++++++++++++++++++++++++-- src/include/commands/copy.h | 1 + 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 28e878c3688..be1b3981ca0 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -557,6 +557,7 @@ ProcessCopyOptions(ParseState *pstate, bool on_error_specified = false; bool log_verbosity_specified = false; bool reject_limit_specified = false; + bool trim_space_specified = false; ListCell *option; /* Support external use for option sanity checking */ @@ -730,6 +731,13 @@ ProcessCopyOptions(ParseState *pstate, reject_limit_specified = true; opts_out->reject_limit = defGetCopyRejectLimitOption(defel); } + else if (strcmp(defel->defname, "trim_space") == 0) + { + if (trim_space_specified) + errorConflictingDefElem(defel, pstate); + trim_space_specified = true; + opts_out->trim_space = defGetBoolean(defel); + } else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), @@ -758,6 +766,11 @@ ProcessCopyOptions(ParseState *pstate, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("cannot specify %s in BINARY mode", "DEFAULT"))); + if (opts_out->binary && opts_out->trim_space) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("cannot specify %s in BINARY mode", "TRIM_SPACE"))); + /* Set defaults for omitted options */ if (!opts_out->delim) opts_out->delim = opts_out->csv_mode ? "," : "\t"; diff --git a/src/backend/commands/copyfromparse.c b/src/backend/commands/copyfromparse.c index a09e7fbace3..9417c1f9e21 100644 --- a/src/backend/commands/copyfromparse.c +++ b/src/backend/commands/copyfromparse.c @@ -1798,6 +1798,30 @@ CopyReadAttributesText(CopyFromState cstate) pg_verifymbstr(fld, output_ptr - fld, false); } + + /* Apply TRIM_SPACE if requested */ + if (cstate->opts.trim_space) + { + char *fld = cstate->raw_fields[fieldno]; + char *start = fld; + char *end = output_ptr; + + /* Trim leading spaces */ + while (start < end && *start == ' ') + start++; + + /* Trim trailing spaces */ + while (end > start && *(end - 1) == ' ') + end--; + + /* Move trimmed string to the beginning if needed */ + if (start > fld) + { + memmove(fld, start, end - start); + } + + output_ptr = fld + (end - start); + } } /* Terminate attribute value in output area */ @@ -1965,9 +1989,6 @@ CopyReadAttributesCSV(CopyFromState cstate) } endfield: - /* Terminate attribute value in output area */ - *output_ptr++ = '\0'; - /* Check whether raw input matched null marker */ input_len = end_ptr - start_ptr; if (!saw_quote && input_len == cstate->opts.null_print_len && @@ -1999,6 +2020,35 @@ endfield: NameStr(att->attname)))); } } + else + { + /* Apply TRIM_SPACE if requested */ + if (cstate->opts.trim_space) + { + char *fld = cstate->raw_fields[fieldno]; + char *start = fld; + char *end = output_ptr; + + /* Trim leading spaces */ + while (start < end && *start == ' ') + start++; + + /* Trim trailing spaces */ + while (end > start && *(end - 1) == ' ') + end--; + + /* Move trimmed string to the beginning if needed */ + if (start > fld) + { + memmove(fld, start, end - start); + } + + output_ptr = fld + (end - start); + } + } + + /* Terminate attribute value in output area */ + *output_ptr++ = '\0'; fieldno++; /* Done if we hit EOL instead of a delim */ diff --git a/src/include/commands/copy.h b/src/include/commands/copy.h index 541176e1980..1769a193789 100644 --- a/src/include/commands/copy.h +++ b/src/include/commands/copy.h @@ -85,6 +85,7 @@ typedef struct CopyFormatOptions CopyLogVerbosityChoice log_verbosity; /* verbosity of logged messages */ int64 reject_limit; /* maximum tolerable number of errors */ List *convert_select; /* list of column names (can be NIL) */ + bool trim_space; /* trim leading/trailing spaces? */ } CopyFormatOptions; /* These are private in commands/copy[from|to].c */ -- 2.34.1