diff options
author | Peter Eisentraut <peter@eisentraut.org> | 2022-03-30 08:56:58 +0200 |
---|---|---|
committer | Peter Eisentraut <peter@eisentraut.org> | 2022-03-30 09:02:31 +0200 |
commit | 072132f04e55c1c3b0f1a582318da78de7334379 (patch) | |
tree | d855c0b1716968cd26966e34f41f77de0c0d0af6 /src/backend/commands/copy.c | |
parent | edcedcc2c7bb8390858bbccda9637318598f2473 (diff) | |
download | postgresql-072132f04e55c1c3b0f1a582318da78de7334379.tar.gz postgresql-072132f04e55c1c3b0f1a582318da78de7334379.zip |
Add header matching mode to COPY FROM
COPY FROM supports the HEADER option to silently discard the header
line from a CSV or text file. It is possible to load by mistake a
file that matches the expected format, for example, if two text
columns have been swapped, resulting in garbage in the database.
This adds a new option value HEADER MATCH that checks the column names
in the header line against the actual column names and errors out if
they do not match.
Author: Rémi Lapeyre <remi.lapeyre@lenstra.fr>
Reviewed-by: Daniel Verite <daniel@manitou-mail.org>
Reviewed-by: Peter Eisentraut <peter.eisentraut@enterprisedb.com>
Discussion: https://www.postgresql.org/message-id/flat/CAF1-J-0PtCWMeLtswwGV2M70U26n4g33gpe1rcKQqe6wVQDrFA@mail.gmail.com
Diffstat (limited to 'src/backend/commands/copy.c')
-rw-r--r-- | src/backend/commands/copy.c | 60 |
1 files changed, 59 insertions, 1 deletions
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 7a0c897cc97..689713ea580 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -314,6 +314,64 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, } /* + * Extract a CopyHeaderChoice value from a DefElem. This is like + * defGetBoolean() but also accepts the special value "match". + */ +static CopyHeaderChoice +defGetCopyHeaderChoice(DefElem *def) +{ + /* + * If no parameter given, assume "true" is meant. + */ + if (def->arg == NULL) + return COPY_HEADER_TRUE; + + /* + * Allow 0, 1, "true", "false", "on", "off", or "match". + */ + switch (nodeTag(def->arg)) + { + case T_Integer: + switch (intVal(def->arg)) + { + case 0: + return COPY_HEADER_FALSE; + case 1: + return COPY_HEADER_TRUE; + default: + /* otherwise, error out below */ + break; + } + break; + default: + { + char *sval = defGetString(def); + + /* + * The set of strings accepted here should match up with the + * grammar's opt_boolean_or_string production. + */ + if (pg_strcasecmp(sval, "true") == 0) + return COPY_HEADER_TRUE; + if (pg_strcasecmp(sval, "false") == 0) + return COPY_HEADER_FALSE; + if (pg_strcasecmp(sval, "on") == 0) + return COPY_HEADER_TRUE; + if (pg_strcasecmp(sval, "off") == 0) + return COPY_HEADER_FALSE; + if (pg_strcasecmp(sval, "match") == 0) + return COPY_HEADER_MATCH; + } + break; + } + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s requires a Boolean value or \"match\"", + def->defname))); + return COPY_HEADER_FALSE; /* keep compiler quiet */ +} + +/* * Process the statement option list for COPY. * * Scan the options list (a list of DefElem) and transpose the information @@ -394,7 +452,7 @@ ProcessCopyOptions(ParseState *pstate, if (header_specified) errorConflictingDefElem(defel, pstate); header_specified = true; - opts_out->header_line = defGetBoolean(defel); + opts_out->header_line = defGetCopyHeaderChoice(defel); } else if (strcmp(defel->defname, "quote") == 0) { |