From 9e6fd40a83f24bdc9d23f95af21e8c8bed57303e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Lapeyre?= <remi.lapeyre@lenstra.fr>
Date: Sat, 3 Oct 2020 23:13:44 +0200
Subject: [PATCH v5 3/3] Report an error when options are set multiple times in
 COPY
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------2.28.0"

This is a multi-part message in MIME format.
--------------2.28.0
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit


Discussion: https://www.postgresql.org/message-id/flat/CAF1-J-0PtCWMeLtswwGV2M70U26n4g33gpe1rcKQqe6wVQDrFA@mail.gmail.com
---
 src/backend/commands/copy.c         |  9 +++++++--
 src/test/regress/input/copy.source  |  6 ++++++
 src/test/regress/output/copy.source | 10 ++++++++++
 3 files changed, 23 insertions(+), 2 deletions(-)


--------------2.28.0
Content-Type: text/x-patch; name="v5-0003-Report-an-error-when-options-are-set-multiple-tim.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="v5-0003-Report-an-error-when-options-are-set-multiple-tim.patch"

diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 97e8514b51..dd8c43da28 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1227,6 +1227,8 @@ ProcessCopyOptions(ParseState *pstate,
 				   List *options)
 {
 	bool		format_specified = false;
+	bool		header_specified = false;
+	bool		freeze_specified = false;
 	ListCell   *option;
 
 	/* Support external use for option sanity checking */
@@ -1266,11 +1268,13 @@ ProcessCopyOptions(ParseState *pstate,
 		}
 		else if (strcmp(defel->defname, "freeze") == 0)
 		{
-			if (cstate->freeze)
+			if (freeze_specified)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
 						 errmsg("conflicting or redundant options"),
 						 parser_errposition(pstate, defel->location)));
+
+			freeze_specified = true;
 			cstate->freeze = defGetBoolean(defel);
 		}
 		else if (strcmp(defel->defname, "delimiter") == 0)
@@ -1293,12 +1297,13 @@ ProcessCopyOptions(ParseState *pstate,
 		}
 		else if (strcmp(defel->defname, "header") == 0)
 		{
-			if (cstate->header_line)
+			if (header_specified)
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
 						 errmsg("conflicting or redundant options"),
 						 parser_errposition(pstate, defel->location)));
 
+			header_specified = true;
 			cstate->header_line = DefGetCopyHeader(defel);
 		}
 		else if (strcmp(defel->defname, "quote") == 0)
diff --git a/src/test/regress/input/copy.source b/src/test/regress/input/copy.source
index 4d21c7d524..bb6cea519f 100644
--- a/src/test/regress/input/copy.source
+++ b/src/test/regress/input/copy.source
@@ -146,6 +146,9 @@ this is just a line full of junk that would error out if parsed
 
 copy copytest4 to stdout (header);
 
+-- specifying header multiple times should report an error
+copy copytest4 to stdout (header off, header on);
+
 -- test copy from with a partitioned table
 create table parted_copytest (
 	a int,
@@ -182,6 +185,9 @@ group by tableoid order by tableoid::regclass::name;
 
 truncate parted_copytest;
 
+-- specifying freeze multiple times should report an error
+copy copytest4 to stdout (freeze off, freeze on);
+
 -- create before insert row trigger on parted_copytest_a2
 create function part_ins_func() returns trigger language plpgsql as $$
 begin
diff --git a/src/test/regress/output/copy.source b/src/test/regress/output/copy.source
index b792181fe3..b6652de074 100644
--- a/src/test/regress/output/copy.source
+++ b/src/test/regress/output/copy.source
@@ -103,6 +103,11 @@ copy copytest4 to stdout (header);
 c1	col with tabulation: \t
 1	a
 2	b
+-- specifying header multiple times should report an error
+copy copytest4 to stdout (header off, header on);
+ERROR:  conflicting or redundant options
+LINE 1: copy copytest4 to stdout (header off, header on);
+                                              ^
 -- test copy from with a partitioned table
 create table parted_copytest (
 	a int,
@@ -136,6 +141,11 @@ group by tableoid order by tableoid::regclass::name;
 (2 rows)
 
 truncate parted_copytest;
+-- specifying freeze multiple times should report an error
+copy copytest4 to stdout (freeze off, freeze on);
+ERROR:  conflicting or redundant options
+LINE 1: copy copytest4 to stdout (freeze off, freeze on);
+                                              ^
 -- create before insert row trigger on parted_copytest_a2
 create function part_ins_func() returns trigger language plpgsql as $$
 begin

--------------2.28.0--


