aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/copy.c
diff options
context:
space:
mode:
authorPeter Eisentraut <peter@eisentraut.org>2022-03-30 08:56:58 +0200
committerPeter Eisentraut <peter@eisentraut.org>2022-03-30 09:02:31 +0200
commit072132f04e55c1c3b0f1a582318da78de7334379 (patch)
treed855c0b1716968cd26966e34f41f77de0c0d0af6 /src/backend/commands/copy.c
parentedcedcc2c7bb8390858bbccda9637318598f2473 (diff)
downloadpostgresql-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.c60
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)
{