From: | Pavel Stehule <pavel(dot)stehule(at)gmail(dot)com> |
---|---|
To: | Emmanuel Cecchet <manu(at)asterdata(dot)com> |
Cc: | Robert Haas <robertmhaas(at)gmail(dot)com>, Emmanuel Cecchet <Emmanuel(dot)Cecchet(at)asterdata(dot)com>, Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>, Josh Berkus <josh(at)agliodbs(dot)com>, PostgreSQL-development <pgsql-hackers(at)postgresql(dot)org> |
Subject: | Re: generic copy options |
Date: | 2009-09-17 04:10:41 |
Message-ID: | 162867790909162110p6e3adfd1t96046980bae51b84@mail.gmail.com |
Views: | Raw Message | Whole Thread | Download mbox | Resend email |
Thread: | |
Lists: | pgsql-hackers |
Hello
I am not sure about syntax change. Isn't better solve this problem
well. This is too simple solution. I thinking, so we able to add new
parser for COPY statement and share this paraser between SQL and psql.
regards
Pavel Stehule
2009/9/17 Emmanuel Cecchet <manu(at)asterdata(dot)com>:
> Robert Haas wrote:
>>
>> I don't think the way the doc changes are formatted is consistent with
>> what we've done elsewhere. I think that breaking the options out as a
>> separate block could be OK (because otherwise they have to be
>> duplicated between COPY TO and COPY FROM) but it should be done more
>> like the way that the SELECT page is done.
>
> I looked at the way it is done in SELECT and there is a section per clause
> (from clause, where clause, ...). So I am not sure how you want to apply
> that here besides the copy parameters and the option clause.
>>
>> Also, you haven't
>> documented the syntax 100% correctly: the boolean options work just
>> like the boolean explain options - they take an optional argument
>> which if omitted defaults to true, but you can also specify 0, 1,
>> true, false, on, off. See defGetBoolean. So those should be
>> specified as:
>>
>> BINARY [boolean]
>> OIDS [boolean]
>> CSV [boolean]
>> CSV_HEADER [boolean]
>>
>> See how we did it in sql-explain.html.
>>
>
> Ok, fixed.
>>>
>>> I changed the name of the CSV options to prefix them with csv_ to avoid
>>> confusion with any future options. I also had to change the grammar to
>>> allow
>>> '*' as a parameter (needed for cvs_force_quote).
>>>
>>
>> You seem to have introduced a LARGE number of unnecessary whitespace
>> changes here which are not going to fly. You need to go through and
>> revert all of those. It's hard to tell what you've really changed
>> here, but also every whitespace change that gets committed is a
>> potential merge conflict for someone else; plus pgindent will
>> eventually change it back, thus creating another potential merge
>> conflict for someone else.
>>
>
> Sorry, I overlooked a format in Eclipse that formatted the whole file
> instead of the block I was working on. This should be fixed now.
>>
>> I am not 100% sold on renaming all of the CSV-specific options to add
>> "csv_". I would like to get an opinion from someone else on whether
>> that is a good idea or not. I am fairly certain it is NOT a good idea
>> to support BOTH the old and new option names, as you've done here. If
>> you're going to rename them, you should update gram.y and change the
>> makeDefElem() calls within the copy_opt_list productions to emit the
>> new names.
>>
>
> Agreed for the makeDefElem().
> For changing the names, I think that names like 'header', 'escape' and
> 'quote' are too generic to not conflict with something that is not csv. If
> you think of another format that could be added to copy, it is likely to
> re-use the same variable names. The only thing that seems odd is that if you
> use a CSV_* option, you still have to add CSV [on] to the option list which
> seems kind of redundant.
>
>>> When we decide to drop the old syntax (in 8.6?), we will be able to clean
>>> a
>>> lot especially in psql.
>>>
>>
>> Considering that we are still carrying syntax that was deprecated in
>> 7.3, I don't think it's likely that we'll phase out the present syntax
>> anywhere nearly that quickly. But it's reasonable to ask whether we
>> should think about removing support for the pre-7.3 syntax altogether
>> for 8.5. It doesn't seem to cost us much to keep that support around,
>> but then again it's been deprecated for seven major releases, so it
>> might be about time.
>>
>
> While I understand the need for the server to still support the syntax, is
> it necessary for newer version of psql to support the old syntax?
>
> I am attaching the new version of the patch with the current modifications
> addressing your comments.
>
> Emmanuel
>
> --
> Emmanuel Cecchet
> Aster Data Systems
> Web: http://www.asterdata.com
>
>
> ### Eclipse Workspace Patch 1.0
> #P Postgres8.5-COPY
> Index: src/test/regress/sql/copy2.sql
> ===================================================================
> RCS file: /home/manu/cvsrepo/pgsql/src/test/regress/sql/copy2.sql,v
> retrieving revision 1.18
> diff -u -r1.18 copy2.sql
> --- src/test/regress/sql/copy2.sql 25 Jul 2009 00:07:14 -0000 1.18
> +++ src/test/regress/sql/copy2.sql 17 Sep 2009 03:14:48 -0000
> @@ -73,17 +73,17 @@
> \.
>
> -- various COPY options: delimiters, oids, NULL string
> -COPY x (b, c, d, e) from stdin with oids delimiter ',' null 'x';
> +COPY x (b, c, d, e) from stdin (oids, delimiter ',', null 'x');
> 500000,x,45,80,90
> 500001,x,\x,\\x,\\\x
> 500002,x,\,,\\\,,\\
> \.
>
> -COPY x from stdin WITH DELIMITER AS ';' NULL AS '';
> +COPY x from stdin (DELIMITER ';', NULL '');
> 3000;;c;;
> \.
>
> -COPY x from stdin WITH DELIMITER AS ':' NULL AS E'\\X';
> +COPY x from stdin (DELIMITER ':', NULL E'\\X');
> 4000:\X:C:\X:\X
> 4001:1:empty::
> 4002:2:null:\X:\X
> @@ -108,13 +108,13 @@
> INSERT INTO no_oids (a, b) VALUES (20, 30);
>
> -- should fail
> -COPY no_oids FROM stdin WITH OIDS;
> -COPY no_oids TO stdout WITH OIDS;
> +COPY no_oids FROM stdin (OIDS);
> +COPY no_oids TO stdout (OIDS);
>
> -- check copy out
> COPY x TO stdout;
> COPY x (c, e) TO stdout;
> -COPY x (b, e) TO stdout WITH NULL 'I''m null';
> +COPY x (b, e) TO stdout (NULL 'I''m null');
>
> CREATE TEMP TABLE y (
> col1 text,
> @@ -130,11 +130,23 @@
> COPY y TO stdout WITH CSV FORCE QUOTE col2 ESCAPE E'\\';
> COPY y TO stdout WITH CSV FORCE QUOTE *;
>
> +-- Test new 8.5 syntax
> +
> +COPY y TO stdout (CSV);
> +COPY y TO stdout (CSV, CSV_QUOTE '''', DELIMITER '|');
> +COPY y TO stdout (CSV, CSV_FORCE_QUOTE (col2), CSV_ESCAPE E'\\');
> +COPY y TO stdout (CSV, CSV_FORCE_QUOTE *);
> +
> +\COPY y TO stdout (CSV)
> +\COPY y TO stdout (CSV, CSV_QUOTE '''', DELIMITER '|')
> +\COPY y TO stdout (CSV, CSV_FORCE_QUOTE (col2), CSV_ESCAPE E'\\')
> +\COPY y TO stdout (CSV, CSV_FORCE_QUOTE *)
> +
> --test that we read consecutive LFs properly
>
> CREATE TEMP TABLE testnl (a int, b text, c int);
>
> -COPY testnl FROM stdin CSV;
> +COPY testnl FROM stdin (CSV);
> 1,"a field with two LFs
>
> inside",2
> @@ -143,14 +155,14 @@
> -- test end of copy marker
> CREATE TEMP TABLE testeoc (a text);
>
> -COPY testeoc FROM stdin CSV;
> +COPY testeoc FROM stdin (CSV);
> a\.
> \.b
> c\.d
> "\."
> \.
>
> -COPY testeoc TO stdout CSV;
> +COPY testeoc TO stdout (CSV);
>
> DROP TABLE x, y;
> DROP FUNCTION fn_x_before();
> Index: src/test/regress/sql/aggregates.sql
> ===================================================================
> RCS file: /home/manu/cvsrepo/pgsql/src/test/regress/sql/aggregates.sql,v
> retrieving revision 1.15
> diff -u -r1.15 aggregates.sql
> --- src/test/regress/sql/aggregates.sql 25 Apr 2009 16:44:56 -0000 1.15
> +++ src/test/regress/sql/aggregates.sql 17 Sep 2009 03:14:48 -0000
> @@ -104,7 +104,7 @@
> BIT_OR(i4) AS "?"
> FROM bitwise_test;
>
> -COPY bitwise_test FROM STDIN NULL 'null';
> +COPY bitwise_test FROM STDIN (NULL 'null');
> 1 1 1 1 1 B0101
> 3 3 3 null 2 B0100
> 7 7 7 3 4 B1100
> @@ -171,7 +171,7 @@
> BOOL_OR(b3) AS "n"
> FROM bool_test;
>
> -COPY bool_test FROM STDIN NULL 'null';
> +COPY bool_test FROM STDIN (NULL 'null');
> TRUE null FALSE null
> FALSE TRUE null null
> null TRUE FALSE null
> Index: src/test/regress/sql/copyselect.sql
> ===================================================================
> RCS file: /home/manu/cvsrepo/pgsql/src/test/regress/sql/copyselect.sql,v
> retrieving revision 1.2
> diff -u -r1.2 copyselect.sql
> --- src/test/regress/sql/copyselect.sql 7 Aug 2008 01:11:52 -0000 1.2
> +++ src/test/regress/sql/copyselect.sql 17 Sep 2009 03:14:48 -0000
> @@ -61,7 +61,7 @@
> --
> -- Test headers, CSV and quotes
> --
> -copy (select t from test1 where id = 1) to stdout csv header force quote t;
> +copy (select t from test1 where id = 1) to stdout (csv, csv_header,
> csv_force_quote (t));
> --
> -- Test psql builtins, plain table
> --
> Index: src/test/regress/expected/aggregates.out
> ===================================================================
> RCS file:
> /home/manu/cvsrepo/pgsql/src/test/regress/expected/aggregates.out,v
> retrieving revision 1.19
> diff -u -r1.19 aggregates.out
> --- src/test/regress/expected/aggregates.out 25 Apr 2009 16:44:56 -0000
> 1.19
> +++ src/test/regress/expected/aggregates.out 17 Sep 2009 03:14:48 -0000
> @@ -326,7 +326,7 @@
> |
> (1 row)
>
> -COPY bitwise_test FROM STDIN NULL 'null';
> +COPY bitwise_test FROM STDIN (NULL 'null');
> SELECT
> BIT_AND(i2) AS "1",
> BIT_AND(i4) AS "1",
> @@ -401,7 +401,7 @@
> |
> (1 row)
>
> -COPY bool_test FROM STDIN NULL 'null';
> +COPY bool_test FROM STDIN (NULL 'null');
> SELECT
> BOOL_AND(b1) AS "f",
> BOOL_AND(b2) AS "t",
> Index: src/test/regress/expected/copyselect.out
> ===================================================================
> RCS file:
> /home/manu/cvsrepo/pgsql/src/test/regress/expected/copyselect.out,v
> retrieving revision 1.2
> diff -u -r1.2 copyselect.out
> --- src/test/regress/expected/copyselect.out 7 Aug 2008 01:11:52 -0000
> 1.2
> +++ src/test/regress/expected/copyselect.out 17 Sep 2009 03:14:48 -0000
> @@ -93,7 +93,7 @@
> --
> -- Test headers, CSV and quotes
> --
> -copy (select t from test1 where id = 1) to stdout csv header force quote t;
> +copy (select t from test1 where id = 1) to stdout (csv, csv_header,
> csv_force_quote (t));
> t
> "a"
> --
> Index: src/test/regress/expected/copy2.out
> ===================================================================
> RCS file: /home/manu/cvsrepo/pgsql/src/test/regress/expected/copy2.out,v
> retrieving revision 1.27
> diff -u -r1.27 copy2.out
> --- src/test/regress/expected/copy2.out 25 Jul 2009 00:07:14 -0000 1.27
> +++ src/test/regress/expected/copy2.out 17 Sep 2009 03:14:48 -0000
> @@ -47,9 +47,9 @@
> ERROR: extra data after last expected column
> CONTEXT: COPY x, line 1: "2002 232 40 50 60 70
> 80"
> -- various COPY options: delimiters, oids, NULL string
> -COPY x (b, c, d, e) from stdin with oids delimiter ',' null 'x';
> -COPY x from stdin WITH DELIMITER AS ';' NULL AS '';
> -COPY x from stdin WITH DELIMITER AS ':' NULL AS E'\\X';
> +COPY x (b, c, d, e) from stdin (oids, delimiter ',', null 'x');
> +COPY x from stdin (DELIMITER ';', NULL '');
> +COPY x from stdin (DELIMITER ':', NULL E'\\X');
> -- check results of copy in
> SELECT * FROM x;
> a | b | c | d | e
> @@ -89,9 +89,9 @@
> INSERT INTO no_oids (a, b) VALUES (5, 10);
> INSERT INTO no_oids (a, b) VALUES (20, 30);
> -- should fail
> -COPY no_oids FROM stdin WITH OIDS;
> +COPY no_oids FROM stdin (OIDS);
> ERROR: table "no_oids" does not have OIDs
> -COPY no_oids TO stdout WITH OIDS;
> +COPY no_oids TO stdout (OIDS);
> ERROR: table "no_oids" does not have OIDs
> -- check copy out
> COPY x TO stdout;
> @@ -146,7 +146,7 @@
> stuff after trigger fired
> stuff after trigger fired
> stuff after trigger fired
> -COPY x (b, e) TO stdout WITH NULL 'I''m null';
> +COPY x (b, e) TO stdout (NULL 'I''m null');
> I'm null before trigger fired
> 21 before trigger fired
> 22 before trigger fired
> @@ -195,13 +195,46 @@
> "Jackson, Sam","\h"
> "It is ""perfect""."," "
> "",
> +-- Test new 8.5 syntax
> +COPY y TO stdout (CSV);
> +"Jackson, Sam",\h
> +"It is ""perfect"".",
> +"",
> +COPY y TO stdout (CSV, CSV_QUOTE '''', DELIMITER '|');
> +Jackson, Sam|\h
> +It is "perfect".|
> +''|
> +COPY y TO stdout (CSV, CSV_FORCE_QUOTE (col2), CSV_ESCAPE E'\\');
> +"Jackson, Sam","\\h"
> +"It is \"perfect\"."," "
> +"",
> +COPY y TO stdout (CSV, CSV_FORCE_QUOTE *);
> +"Jackson, Sam","\h"
> +"It is ""perfect""."," "
> +"",
> +\COPY y TO stdout (CSV)
> +"Jackson, Sam",\h
> +"It is ""perfect"".",
> +"",
> +\COPY y TO stdout (CSV, CSV_QUOTE '''', DELIMITER '|')
> +Jackson, Sam|\h
> +It is "perfect".|
> +''|
> +\COPY y TO stdout (CSV, CSV_FORCE_QUOTE (col2), CSV_ESCAPE E'\\')
> +"Jackson, Sam",\h
> +"It is \"perfect\".",
> +"",
> +\COPY y TO stdout (CSV, CSV_FORCE_QUOTE *)
> +"Jackson, Sam",\h
> +"It is ""perfect"".",
> +"",
> --test that we read consecutive LFs properly
> CREATE TEMP TABLE testnl (a int, b text, c int);
> -COPY testnl FROM stdin CSV;
> +COPY testnl FROM stdin (CSV);
> -- test end of copy marker
> CREATE TEMP TABLE testeoc (a text);
> -COPY testeoc FROM stdin CSV;
> -COPY testeoc TO stdout CSV;
> +COPY testeoc FROM stdin (CSV);
> +COPY testeoc TO stdout (CSV);
> a\.
> \.b
> c\.d
> Index: doc/src/sgml/ref/copy.sgml
> ===================================================================
> RCS file: /home/manu/cvsrepo/pgsql/doc/src/sgml/ref/copy.sgml,v
> retrieving revision 1.87
> diff -u -r1.87 copy.sgml
> --- doc/src/sgml/ref/copy.sgml 5 Sep 2009 23:58:01 -0000 1.87
> +++ doc/src/sgml/ref/copy.sgml 17 Sep 2009 03:14:48 -0000
> @@ -24,27 +24,24 @@
> <synopsis>
> COPY <replaceable class="parameter">tablename</replaceable> [ (
> <replaceable class="parameter">column</replaceable> [, ...] ) ]
> FROM { '<replaceable class="parameter">filename</replaceable>' | STDIN }
> - [ [ WITH ]
> - [ BINARY ]
> - [ OIDS ]
> - [ DELIMITER [ AS ] '<replaceable
> class="parameter">delimiter</replaceable>' ]
> - [ NULL [ AS ] '<replaceable class="parameter">null
> string</replaceable>' ]
> - [ CSV [ HEADER ]
> - [ QUOTE [ AS ] '<replaceable
> class="parameter">quote</replaceable>' ]
> - [ ESCAPE [ AS ] '<replaceable
> class="parameter">escape</replaceable>' ]
> - [ FORCE NOT NULL <replaceable
> class="parameter">column</replaceable> [, ...] ]
> + [ ( options [,...] ) ]
>
> COPY { <replaceable class="parameter">tablename</replaceable> [ (
> <replaceable class="parameter">column</replaceable> [, ...] ) ] | (
> <replaceable class="parameter">query</replaceable> ) }
> TO { '<replaceable class="parameter">filename</replaceable>' | STDOUT }
> - [ [ WITH ]
> - [ BINARY ]
> - [ OIDS ]
> - [ DELIMITER [ AS ] '<replaceable
> class="parameter">delimiter</replaceable>' ]
> - [ NULL [ AS ] '<replaceable class="parameter">null
> string</replaceable>' ]
> - [ CSV [ HEADER ]
> - [ QUOTE [ AS ] '<replaceable
> class="parameter">quote</replaceable>' ]
> - [ ESCAPE [ AS ] '<replaceable
> class="parameter">escape</replaceable>' ]
> - [ FORCE QUOTE { <replaceable
> class="parameter">column</replaceable> [, ...] | * } ]
> + [ ( options [,...] ) ]
> +
> +Currently available options are:
> + - BINARY [ <replaceable class="parameter">boolean</replaceable> ]
> + - OIDS [ <replaceable class="parameter">boolean</replaceable> ]
> + - DELIMITER '<replaceable class="parameter">delimiter</replaceable>'
> + - NULL '<replaceable class="parameter">null string</replaceable>'
> + - CSV [ <replaceable class="parameter">boolean</replaceable> ]
> + - CSV_HEADER [ <replaceable class="parameter">boolean</replaceable> ]
> + - CSV_QUOTE '<replaceable class="parameter">quote</replaceable>'
> + - CSV_ESCAPE '<replaceable class="parameter">escape</replaceable>'
> + - CSV_FORCE_QUOTE { ( <replaceable
> class="parameter">column</replaceable> [, ...] ) | * }
> + - CSV_FORCE_NOT_NULL ( <replaceable
> class="parameter">column</replaceable> [, ...] )
> +
> </synopsis>
> </refsynopsisdiv>
>
> @@ -143,6 +140,27 @@
> </para>
> </listitem>
> </varlistentry>
> + </variablelist>
> + </refsect1>
> +
> + <refsect1>
> + <title>Options</title>
> +
> + <variablelist>
> +
> + <varlistentry>
> + <term><replaceable class="parameter">boolean</replaceable></term>
> + <listitem>
> + <para>
> + Specifies whether the selected option should be turned on or off.
> + You can write <literal>TRUE</literal>, <literal>ON</>, or
> + <literal>1</literal> to enable the option, and
> <literal>FALSE</literal>,
> + <literal>OFF</>, or <literal>0</literal> to disable it. The
> + <replaceable class="parameter">boolean</replaceable> value can also
> + be omitted, in which case <literal>TRUE</literal> is assumed.
> + </para>
> + </listitem>
> + </varlistentry>
>
> <varlistentry>
> <term><literal>BINARY</literal></term>
> @@ -168,7 +186,7 @@
> </varlistentry>
>
> <varlistentry>
> - <term><replaceable class="parameter">delimiter</replaceable></term>
> + <term><literal>DELIMITER</literal></term>
> <listitem>
> <para>
> The single ASCII character that separates columns within each row
> @@ -179,7 +197,7 @@
> </varlistentry>
>
> <varlistentry>
> - <term><replaceable class="parameter">null string</replaceable></term>
> + <term><literal>NULL</literal></term>
> <listitem>
> <para>
> The string that represents a null value. The default is
> @@ -211,7 +229,7 @@
> </varlistentry>
>
> <varlistentry>
> - <term><literal>HEADER</literal></term>
> + <term><literal>CSV_HEADER</literal></term>
> <listitem>
> <para>
> Specifies that the file contains a header line with the names of each
> @@ -222,7 +240,7 @@
> </varlistentry>
>
> <varlistentry>
> - <term><replaceable class="parameter">quote</replaceable></term>
> + <term><literal>CSV_QUOTE</literal></term>
> <listitem>
> <para>
> Specifies the ASCII quotation character in <literal>CSV</> mode.
> @@ -232,18 +250,18 @@
> </varlistentry>
>
> <varlistentry>
> - <term><replaceable class="parameter">escape</replaceable></term>
> + <term><literal>CSV_ESCAPE</literal></term>
> <listitem>
> <para>
> Specifies the ASCII character that should appear before a
> - <literal>QUOTE</> data character value in <literal>CSV</> mode.
> - The default is the <literal>QUOTE</> value (usually double-quote).
> + <literal>CSV_QUOTE</> data character value in <literal>CSV</> mode.
> + The default is the <literal>CSV_QUOTE</> value (usually
> double-quote).
> </para>
> </listitem>
> </varlistentry>
>
> <varlistentry>
> - <term><literal>FORCE QUOTE</></term>
> + <term><literal>CSV_FORCE_QUOTE</></term>
> <listitem>
> <para>
> In <literal>CSV</> <command>COPY TO</> mode, forces quoting to be
> @@ -256,7 +274,7 @@
> </varlistentry>
>
> <varlistentry>
> - <term><literal>FORCE NOT NULL</></term>
> + <term><literal>CSV_FORCE_NOT_NULL</></term>
> <listitem>
> <para>
> In <literal>CSV</> <command>COPY FROM</> mode, process each
> @@ -295,7 +313,7 @@
> </para>
>
> <para>
> - The <literal>BINARY</literal> key word causes all data to be
> + The <literal>BINARY</literal> option causes all data to be
> stored/read as binary format rather than as text. It is
> somewhat faster than the normal text mode, but a binary-format
> file is less portable across machine architectures and
> @@ -538,12 +556,12 @@
> <para>
> The values in each record are separated by the <literal>DELIMITER</>
> character. If the value contains the delimiter character, the
> - <literal>QUOTE</> character, the <literal>NULL</> string, a carriage
> + <literal>CSV_QUOTE</> character, the <literal>NULL</> string, a
> carriage
> return, or line feed character, then the whole value is prefixed and
> - suffixed by the <literal>QUOTE</> character, and any occurrence
> - within the value of a <literal>QUOTE</> character or the
> - <literal>ESCAPE</> character is preceded by the escape character.
> - You can also use <literal>FORCE QUOTE</> to force quotes when
> outputting
> + suffixed by the <literal>CSV_QUOTE</> character, and any occurrence
> + within the value of a <literal>CSV_QUOTE</> character or the
> + <literal>CSV_ESCAPE</> character is preceded by the escape character.
> + You can also use <literal>CSV_FORCE_QUOTE</> to force quotes when
> outputting
> non-<literal>NULL</> values in specific columns.
> </para>
>
> @@ -557,7 +575,7 @@
> settings, a <literal>NULL</> is written as an unquoted empty
> string, while an empty string is written with double quotes
> (<literal>""</>). Reading values follows similar rules. You can
> - use <literal>FORCE NOT NULL</> to prevent <literal>NULL</> input
> + use <literal>CSV_FORCE_NOT_NULL</> to prevent <literal>NULL</> input
> comparisons for specific columns.
> </para>
>
> @@ -577,7 +595,7 @@
> <para>
> In <literal>CSV</> mode, all characters are significant. A quoted value
> surrounded by white space, or any characters other than
> - <literal>DELIMITER</>, will include those characters. This can cause
> + <literal>CSV_DELIMITER</>, will include those characters. This can
> cause
> errors if you import data from a system that pads <literal>CSV</>
> lines with white space out to some fixed width. If such a situation
> arises you might need to preprocess the <literal>CSV</> file to remove
> @@ -759,7 +777,7 @@
> The following example copies a table to the client
> using the vertical bar (<literal>|</literal>) as the field delimiter:
> <programlisting>
> -COPY country TO STDOUT WITH DELIMITER '|';
> +COPY country TO STDOUT (DELIMITER '|');
> </programlisting>
> </para>
>
> @@ -809,6 +827,12 @@
> 0000200 M B A B W E 377 377 377 377 377 377
> </programlisting>
> </para>
> + <para>
> + Multiple options are separated by a comma like:
> +<programlisting>
> +COPY (SELECT t FROM foo WHERE id = 1) TO STDOUT (CSV, CSV_HEADER,
> CSV_FORCE_QUOTE (t));
> +</programlisting>
> + </para>
> </refsect1>
>
> <refsect1>
> @@ -817,7 +841,35 @@
> <para>
> There is no <command>COPY</command> statement in the SQL standard.
> </para>
> + <para>
> + The following syntax was used before <productname>PostgreSQL</>
> + version 8.5 and is still supported:
> +<synopsis>
> +COPY <replaceable class="parameter">tablename</replaceable> [ (
> <replaceable class="parameter">column</replaceable> [, ...] ) ]
> + FROM { '<replaceable class="parameter">filename</replaceable>' | STDIN
> }
> + [ [ WITH ]
> + [ BINARY ]
> + [ OIDS ]
> + [ DELIMITER [ AS ] '<replaceable
> class="parameter">delimiter</replaceable>' ]
> + [ NULL [ AS ] '<replaceable class="parameter">null
> string</replaceable>' ]
> + [ CSV [ HEADER ]
> + [ QUOTE [ AS ] '<replaceable
> class="parameter">quote</replaceable>' ]
> + [ ESCAPE [ AS ] '<replaceable
> class="parameter">escape</replaceable>' ]
> + [ FORCE NOT NULL <replaceable
> class="parameter">column</replaceable> [, ...] ]
>
> +COPY { <replaceable class="parameter">tablename</replaceable> [ (
> <replaceable class="parameter">column</replaceable> [, ...] ) ] | (
> <replaceable class="parameter">query</replaceable> ) }
> + TO { '<replaceable class="parameter">filename</replaceable>' | STDOUT }
> + [ [ WITH ]
> + [ BINARY ]
> + [ OIDS ]
> + [ DELIMITER [ AS ] '<replaceable
> class="parameter">delimiter</replaceable>' ]
> + [ NULL [ AS ] '<replaceable class="parameter">null
> string</replaceable>' ]
> + [ CSV [ HEADER ]
> + [ QUOTE [ AS ] '<replaceable
> class="parameter">quote</replaceable>' ]
> + [ ESCAPE [ AS ] '<replaceable
> class="parameter">escape</replaceable>' ]
> + [ FORCE QUOTE { <replaceable
> class="parameter">column</replaceable> [, ...] | * } ]
> +</synopsis>
> + </para>
> <para>
> The following syntax was used before <productname>PostgreSQL</>
> version 7.3 and is still supported:
> Index: src/backend/parser/gram.y
> ===================================================================
> RCS file: /home/manu/cvsrepo/pgsql/src/backend/parser/gram.y,v
> retrieving revision 2.677
> diff -u -r2.677 gram.y
> --- src/backend/parser/gram.y 18 Aug 2009 23:40:20 -0000 2.677
> +++ src/backend/parser/gram.y 17 Sep 2009 03:14:48 -0000
> @@ -373,6 +373,10 @@
> %type <node> explain_option_arg
> %type <defelt> explain_option_elem
> %type <list> explain_option_list
> +%type <str> copy_generic_option_name
> +%type <node> copy_generic_option_arg copy_generic_option_arg_item
> +%type <defelt> copy_generic_option_elem
> +%type <list> copy_generic_option_list copy_generic_option_arg_list
>
> %type <typnam> Typename SimpleTypename ConstTypename
> GenericType Numeric opt_float
> @@ -1934,14 +1938,19 @@
> /*****************************************************************************
> *
> * QUERY :
> - * COPY relname ['(' columnList ')'] FROM/TO
> file [WITH options]
> - *
> - * BINARY, OIDS, and DELIMITERS kept in old
> locations
> - * for backward compatibility. 2002-06-18
> + * New, more generic syntax, supported
> beginning with PostgreSQL
> + * 8.5. Options are comma-separated.
> + * COPY relname ['(' columnList ')'] FROM/TO
> file '(' options ')'
> *
> + * Older syntax, used from 7.3 to 8.4 and still
> supported for
> + * backwards compatibility
> + * COPY relname ['(' columnList ')'] FROM/TO
> file [WITH options]
> * COPY ( SELECT ... ) TO file [WITH options]
> - * This form doesn't have the
> backwards-compatible option
> - * syntax.
> + *
> + * Really old syntax, from versions 7.2 and
> prior:
> + * COPY [ BINARY ] table [ WITH OIDS ] FROM/TO
> file
> + * [ [ USING ] DELIMITERS 'delimiter' ]
> ]
> + * [ WITH NULL AS 'null string' ]
> *
> *****************************************************************************/
>
> @@ -2001,6 +2010,7 @@
>
> copy_opt_list:
> copy_opt_list copy_opt_item
> { $$ = lappend($1, $2); }
> + | '(' copy_generic_option_list ')' { $$
> = $2 ; }
> | /* EMPTY */
> { $$ = NIL; }
> ;
>
> @@ -2028,27 +2038,27 @@
> }
> | HEADER_P
> {
> - $$ = makeDefElem("header", (Node
> *)makeInteger(TRUE));
> + $$ = makeDefElem("csv_header", (Node
> *)makeInteger(TRUE));
> }
> | QUOTE opt_as Sconst
> {
> - $$ = makeDefElem("quote", (Node
> *)makeString($3));
> + $$ = makeDefElem("csv_quote", (Node
> *)makeString($3));
> }
> | ESCAPE opt_as Sconst
> {
> - $$ = makeDefElem("escape", (Node
> *)makeString($3));
> + $$ = makeDefElem("csv_escape", (Node
> *)makeString($3));
> }
> | FORCE QUOTE columnList
> {
> - $$ = makeDefElem("force_quote",
> (Node *)$3);
> + $$ = makeDefElem("csv_force_quote",
> (Node *)$3);
> }
> | FORCE QUOTE '*'
> {
> - $$ = makeDefElem("force_quote",
> (Node *)makeNode(A_Star));
> + $$ = makeDefElem("csv_force_quote",
> (Node *)makeNode(A_Star));
> }
> | FORCE NOT NULL_P columnList
> {
> - $$ = makeDefElem("force_notnull",
> (Node *)$4);
> + $$ =
> makeDefElem("csv_force_not_null", (Node *)$4);
> }
> ;
>
> @@ -2084,6 +2094,53 @@
> | /*EMPTY*/
> {}
> ;
>
> +copy_generic_option_list:
> + copy_generic_option_elem
> + {
> + $$ = list_make1($1);
> + }
> + | copy_generic_option_list ','
> copy_generic_option_elem
> + {
> + $$ = lappend($1, $3);
> + }
> + ;
> +
> +copy_generic_option_elem:
> + copy_generic_option_name copy_generic_option_arg
> + {
> + $$ = makeDefElem($1, $2);
> + }
> + ;
> +
> +copy_generic_option_name:
> + ColLabel
> { $$ = $1; }
> + ;
> +
> +copy_generic_option_arg:
> + copy_generic_option_arg_item { $$
> = $1; }
> + | '(' copy_generic_option_arg_list ')' { $$ = (Node
> *) $2; }
> + | '*'
> { $$ = (Node *)makeNode(A_Star); }
> + | '(' ')'
> { $$ = NULL; }
> + | /* EMPTY */
> { $$ = NULL; }
> + ;
> +
> +copy_generic_option_arg_list:
> + copy_generic_option_arg_item
> + {
> + $$ = list_make1($1);
> + }
> + | copy_generic_option_arg_list ','
> copy_generic_option_arg_item
> + {
> + $$ = lappend($1, $3);
> + }
> + ;
> +
> +copy_generic_option_arg_item:
> + opt_boolean { $$ = (Node
> *) makeString($1); }
> + | ColId_or_Sconst { $$ = (Node *)
> makeString($1); }
> + | NumericOnly { $$ = (Node *) $1;
> }
> + ;
> +
>
> /*****************************************************************************
> *
> Index: src/bin/psql/copy.c
> ===================================================================
> RCS file: /home/manu/cvsrepo/pgsql/src/bin/psql/copy.c,v
> retrieving revision 1.82
> diff -u -r1.82 copy.c
> --- src/bin/psql/copy.c 7 Aug 2009 20:16:11 -0000 1.82
> +++ src/bin/psql/copy.c 17 Sep 2009 03:14:48 -0000
> @@ -26,20 +26,27 @@
> #include "prompt.h"
> #include "stringutils.h"
>
> -
> /*
> * parse_slash_copy
> * -- parses \copy command line
> *
> * The documented syntax is:
> + * Since 8.5:
> + * \copy tablename [(columnlist)] from|to filename [( options )]
> + *
> + * options is a comma separated list of options. Currently supported
> options:
> + * binary, oids, delimiter, null, csv, csv_header, csv_espace, csv_quote,
> + * csv_force_not_null, csv_force_quote
> + *
> + * Prior 8.5:
> * \copy tablename [(columnlist)] from|to filename
> * [ with ] [ binary ] [ oids ] [ delimiter [as] char ] [ null [as]
> string ]
> - * [ csv [ header ] [ quote [ AS ] string ] escape [as] string
> + * [ csv [ header ] [ quote [ AS ] string ] [ escape [as] string ]
> * [ force not null column [, ...] | force quote column [, ...]
> | * ] ]
> *
> * \copy ( select stmt ) to filename
> * [ with ] [ binary ] [ delimiter [as] char ] [ null [as] string ]
> - * [ csv [ header ] [ quote [ AS ] string ] escape [as] string
> + * [ csv [ header ] [ quote [ AS ] string ] [ escape [as] string ]
> * [ force quote column [, ...] | * ] ]
> *
> * Force quote only applies for copy to; force not null only applies for
> @@ -103,6 +110,45 @@
> *var = newvar;
> }
>
> +/* Extract parameters of the CSV_FORCE_QUOTE or CSV_FORCE_NOT_NULL options.
> + * Return true if the list was successfully extracted or false if an error
> + * occurred. */
> +static bool extract_csv_force_list(char *list, char *token)
> +{
> + const char *whitespace = " \t\n\r";
> + const char *separator = "(,)";
> +
> + for (;;)
> + {
> + token = strtokx(NULL, whitespace, separator, "\"", 0, false,
> false,
> + pset.encoding);
> +
> + if (!token)
> + return false;
> + if (strchr(separator, token[0]) != NULL)
> + continue; /* fetch next token */
> + if (!list)
> + {
> + list = pg_strdup(token);
> + if (token[0] == '*')
> + return true;
> + }
> + else
> + {
> + if (token[0] == '*') /* '*' can only be accepted as
> a single argument */
> + return false;
> + xstrcat(&list, token);
> + }
> + token = strtokx(NULL, whitespace, separator, "\"", 0, false,
> false,
> + pset.encoding);
> + if (!token || strchr(")", token[0]))
> + break;
> + if (strchr(separator, token[0]) != NULL)
> + continue; // fetch next token
> + xstrcat(&list, token);
> + }
> + return true;
> +}
>
> static struct copy_options *
> parse_slash_copy(const char *args)
> @@ -241,18 +287,29 @@
> expand_tilde(&result->file);
> }
>
> - token = strtokx(NULL, whitespace, NULL, NULL,
> + token = strtokx(NULL, whitespace, "(", NULL,
> 0, false, false, pset.encoding);
>
> if (token)
> {
> + char *option_separator = NULL;
> +
> + /* New 8.5 syntax, option are between () */
> + if (token[0] == '(')
> + {
> + option_separator = ",)";
> + token = strtokx(NULL, whitespace, NULL,
> option_separator, 0, false, false,
> + pset.encoding);
> + }
> /*
> * WITH is optional. Also, the backend will allow WITH
> followed by
> * nothing, so we do too.
> */
> - if (pg_strcasecmp(token, "with") == 0)
> - token = strtokx(NULL, whitespace, NULL, NULL,
> - 0, false, false,
> pset.encoding);
> + else if (pg_strcasecmp(token, "with") == 0)
> + {
> + token = strtokx(NULL, whitespace, NULL, NULL, 0,
> false, false,
> + pset.encoding);
> + }
>
> while (token)
> {
> @@ -266,7 +323,8 @@
> result->binary = true;
> else if (pg_strcasecmp(token, "csv") == 0)
> result->csv_mode = true;
> - else if (pg_strcasecmp(token, "header") == 0)
> + else if ((pg_strcasecmp(token, "header") == 0) ||
> (pg_strcasecmp(
> + token, "csv_header") == 0))
> result->header = true;
> else if (pg_strcasecmp(token, "delimiter") == 0)
> {
> @@ -296,7 +354,8 @@
> else
> goto error;
> }
> - else if (pg_strcasecmp(token, "quote") == 0)
> + else if ((pg_strcasecmp(token, "quote") == 0) ||
> (pg_strcasecmp(
> + token, "csv_quote") == 0))
> {
> if (result->quote)
> goto error;
> @@ -310,7 +369,8 @@
> else
> goto error;
> }
> - else if (pg_strcasecmp(token, "escape") == 0)
> + else if ((pg_strcasecmp(token, "escape") == 0) ||
> (pg_strcasecmp(
> + token, "csv_escape") == 0))
> {
> if (result->escape)
> goto error;
> @@ -334,22 +394,8 @@
> goto error;
> /* handle column list */
> fetch_next = false;
> - for (;;)
> - {
> - token = strtokx(NULL,
> whitespace, ",", "\"",
> -
> 0, false, false, pset.encoding);
> - if (!token || strchr(",",
> token[0]))
> - goto error;
> - if
> (!result->force_quote_list)
> -
> result->force_quote_list = pg_strdup(token);
> - else
> -
> xstrcat(&result->force_quote_list, token);
> - token = strtokx(NULL,
> whitespace, ",", "\"",
> -
> 0, false, false, pset.encoding);
> - if (!token || token[0] !=
> ',')
> - break;
> -
> xstrcat(&result->force_quote_list, token);
> - }
> + if
> (!extract_csv_force_list(result->force_quote_list, token))
> + goto error;
> }
> else if (pg_strcasecmp(token, "not") == 0)
> {
> @@ -361,32 +407,40 @@
> goto error;
> /* handle column list */
> fetch_next = false;
> - for (;;)
> - {
> - token = strtokx(NULL,
> whitespace, ",", "\"",
> -
> 0, false, false, pset.encoding);
> - if (!token || strchr(",",
> token[0]))
> - goto error;
> - if
> (!result->force_notnull_list)
> -
> result->force_notnull_list = pg_strdup(token);
> - else
> -
> xstrcat(&result->force_notnull_list, token);
> - token = strtokx(NULL,
> whitespace, ",", "\"",
> -
> 0, false, false, pset.encoding);
> - if (!token || token[0] !=
> ',')
> - break;
> -
> xstrcat(&result->force_notnull_list, token);
> - }
> + if
> (!extract_csv_force_list(result->force_notnull_list,
> + token))
> + goto error;
> }
> else
> goto error;
> }
> + else if (pg_strcasecmp(token, "csv_force_quote") ==
> 0)
> + {
> + if (result->force_quote_list)
> + goto error;
> + if
> (!extract_csv_force_list(result->force_quote_list, token))
> + goto error;
> + }
> + else if (pg_strcasecmp(token, "csv_force_not_null")
> == 0)
> + {
> + if (result->force_notnull_list)
> + goto error;
> + if
> (!extract_csv_force_list(result->force_notnull_list, token))
> + goto error;
> + }
> else
> goto error;
>
> if (fetch_next)
> - token = strtokx(NULL, whitespace, NULL,
> NULL,
> - 0, false,
> false, pset.encoding);
> + {
> + token = strtokx(NULL, whitespace,
> option_separator, NULL, 0,
> + false, false,
> pset.encoding);
> + if ((option_separator != NULL) &&
> (strchr(option_separator, token[0])))
> + {
> + token = strtokx(NULL, whitespace,
> option_separator, NULL,
> + 0, false, false,
> pset.encoding);
> + }
> + }
> }
> }
>
> Index: src/test/regress/output/copy.source
> ===================================================================
> RCS file: /home/manu/cvsrepo/pgsql/src/test/regress/output/copy.source,v
> retrieving revision 1.13
> diff -u -r1.13 copy.source
> --- src/test/regress/output/copy.source 21 Aug 2007 01:11:31 -0000 1.13
> +++ src/test/regress/output/copy.source 17 Sep 2009 03:14:48 -0000
> @@ -71,3 +71,49 @@
> c1,"col with , comma","col with "" quote"
> 1,a,1
> 2,b,2
> +-- Repeat the above tests with the new 8.5 option syntax
> +copy copytest to
> '/home/manu/workspace/Postgres8.5-COPY/src/test/regress/results/copytest.csv'
> (csv);
> +truncate copytest2;
> +copy copytest2 from
> '/home/manu/workspace/Postgres8.5-COPY/src/test/regress/results/copytest.csv'
> (csv);
> +select * from copytest except select * from copytest2;
> + style | test | filler
> +-------+------+--------
> +(0 rows)
> +
> +truncate copytest2;
> +copy copytest to
> '/home/manu/workspace/Postgres8.5-COPY/src/test/regress/results/copytest.csv'
> (csv, csv_quote '''', csv_escape E'\\');
> +copy copytest2 from
> '/home/manu/workspace/Postgres8.5-COPY/src/test/regress/results/copytest.csv'
> (csv, csv_quote '''', csv_escape E'\\');
> +select * from copytest except select * from copytest2;
> + style | test | filler
> +-------+------+--------
> +(0 rows)
> +
> +truncate copytest3;
> +copy copytest3 from stdin (csv, csv_header);
> +copy copytest3 to stdout (csv, csv_header);
> +c1,"col with , comma","col with "" quote"
> +1,a,1
> +2,b,2
> +-- Repeat the above tests with the new 8.5 option syntax from psql
> +\copy copytest to
> '/home/manu/workspace/Postgres8.5-COPY/src/test/regress/results/copytest.csv'
> (csv)
> +truncate copytest2;
> +\copy copytest2 from
> '/home/manu/workspace/Postgres8.5-COPY/src/test/regress/results/copytest.csv'
> (csv)
> +select * from copytest except select * from copytest2;
> + style | test | filler
> +-------+------+--------
> +(0 rows)
> +
> +truncate copytest2;
> +\copy copytest to
> '/home/manu/workspace/Postgres8.5-COPY/src/test/regress/results/copytest.csv'
> (csv, csv_quote '''', csv_escape E'\\')
> +\copy copytest2 from
> '/home/manu/workspace/Postgres8.5-COPY/src/test/regress/results/copytest.csv'
> (csv, csv_quote '''', csv_escape E'\\')
> +select * from copytest except select * from copytest2;
> + style | test | filler
> +-------+------+--------
> +(0 rows)
> +
> +truncate copytest3;
> +\copy copytest3 from stdin (csv, csv_header)
> +\copy copytest3 to stdout (csv, csv_header)
> +c1,"col with , comma","col with "" quote"
> +1,a,1
> +2,b,2
> Index: src/test/regress/input/copy.source
> ===================================================================
> RCS file: /home/manu/cvsrepo/pgsql/src/test/regress/input/copy.source,v
> retrieving revision 1.15
> diff -u -r1.15 copy.source
> --- src/test/regress/input/copy.source 21 Aug 2007 01:11:31 -0000 1.15
> +++ src/test/regress/input/copy.source 17 Sep 2009 03:14:48 -0000
> @@ -107,3 +107,58 @@
>
> copy copytest3 to stdout csv header;
>
> +-- Repeat the above tests with the new 8.5 option syntax
> +
> +copy copytest to '@abs_builddir@/results/copytest.csv' (csv);
> +
> +truncate copytest2;
> +
> +copy copytest2 from '@abs_builddir@/results/copytest.csv' (csv);
> +
> +select * from copytest except select * from copytest2;
> +
> +truncate copytest2;
> +
> +copy copytest to '@abs_builddir@/results/copytest.csv' (csv, csv_quote
> '''', csv_escape E'\\');
> +
> +copy copytest2 from '@abs_builddir@/results/copytest.csv' (csv, csv_quote
> '''', csv_escape E'\\');
> +
> +select * from copytest except select * from copytest2;
> +
> +truncate copytest3;
> +
> +copy copytest3 from stdin (csv, csv_header);
> +this is just a line full of junk that would error out if parsed
> +1,a,1
> +2,b,2
> +\.
> +
> +copy copytest3 to stdout (csv, csv_header);
> +
> +-- Repeat the above tests with the new 8.5 option syntax from psql
> +
> +\copy copytest to '@abs_builddir@/results/copytest.csv' (csv)
> +
> +truncate copytest2;
> +
> +\copy copytest2 from '@abs_builddir@/results/copytest.csv' (csv)
> +
> +select * from copytest except select * from copytest2;
> +
> +truncate copytest2;
> +
> +\copy copytest to '@abs_builddir@/results/copytest.csv' (csv, csv_quote
> '''', csv_escape E'\\')
> +
> +\copy copytest2 from '@abs_builddir@/results/copytest.csv' (csv, csv_quote
> '''', csv_escape E'\\')
> +
> +select * from copytest except select * from copytest2;
> +
> +truncate copytest3;
> +
> +\copy copytest3 from stdin (csv, csv_header)
> +this is just a line full of junk that would error out if parsed
> +1,a,1
> +2,b,2
> +\.
> +
> +\copy copytest3 to stdout (csv, csv_header)
> Index: src/backend/commands/copy.c
> ===================================================================
> RCS file: /home/manu/cvsrepo/pgsql/src/backend/commands/copy.c,v
> retrieving revision 1.316
> diff -u -r1.316 copy.c
> --- src/backend/commands/copy.c 29 Jul 2009 20:56:18 -0000 1.316
> +++ src/backend/commands/copy.c 17 Sep 2009 03:14:48 -0000
> @@ -25,6 +25,7 @@
> #include "catalog/namespace.h"
> #include "catalog/pg_type.h"
> #include "commands/copy.h"
> +#include "commands/defrem.h"
> #include "commands/trigger.h"
> #include "executor/executor.h"
> #include "libpq/libpq.h"
> @@ -745,7 +746,7 @@
> ereport(ERROR,
>
> (errcode(ERRCODE_SYNTAX_ERROR),
> errmsg("conflicting or
> redundant options")));
> - cstate->binary = intVal(defel->arg);
> + cstate->binary = defGetBoolean(defel);
> }
> else if (strcmp(defel->defname, "oids") == 0)
> {
> @@ -753,7 +754,7 @@
> ereport(ERROR,
>
> (errcode(ERRCODE_SYNTAX_ERROR),
> errmsg("conflicting or
> redundant options")));
> - cstate->oids = intVal(defel->arg);
> + cstate->oids = defGetBoolean(defel);
> }
> else if (strcmp(defel->defname, "delimiter") == 0)
> {
> @@ -761,7 +762,7 @@
> ereport(ERROR,
>
> (errcode(ERRCODE_SYNTAX_ERROR),
> errmsg("conflicting or
> redundant options")));
> - cstate->delim = strVal(defel->arg);
> + cstate->delim = defGetString(defel);
> }
> else if (strcmp(defel->defname, "null") == 0)
> {
> @@ -769,7 +770,7 @@
> ereport(ERROR,
>
> (errcode(ERRCODE_SYNTAX_ERROR),
> errmsg("conflicting or
> redundant options")));
> - cstate->null_print = strVal(defel->arg);
> + cstate->null_print = defGetString(defel);
> }
> else if (strcmp(defel->defname, "csv") == 0)
> {
> @@ -777,33 +778,33 @@
> ereport(ERROR,
>
> (errcode(ERRCODE_SYNTAX_ERROR),
> errmsg("conflicting or
> redundant options")));
> - cstate->csv_mode = intVal(defel->arg);
> + cstate->csv_mode = defGetBoolean(defel);
> }
> - else if (strcmp(defel->defname, "header") == 0)
> + else if (strcmp(defel->defname, "csv_header") == 0)
> {
> if (cstate->header_line)
> ereport(ERROR,
>
> (errcode(ERRCODE_SYNTAX_ERROR),
> errmsg("conflicting or
> redundant options")));
> - cstate->header_line = intVal(defel->arg);
> + cstate->header_line = defGetBoolean(defel);
> }
> - else if (strcmp(defel->defname, "quote") == 0)
> + else if (strcmp(defel->defname, "csv_quote") == 0)
> {
> if (cstate->quote)
> ereport(ERROR,
>
> (errcode(ERRCODE_SYNTAX_ERROR),
> errmsg("conflicting or
> redundant options")));
> - cstate->quote = strVal(defel->arg);
> + cstate->quote = defGetString(defel);
> }
> - else if (strcmp(defel->defname, "escape") == 0)
> + else if (strcmp(defel->defname, "csv_escape") == 0)
> {
> if (cstate->escape)
> ereport(ERROR,
>
> (errcode(ERRCODE_SYNTAX_ERROR),
> errmsg("conflicting or
> redundant options")));
> - cstate->escape = strVal(defel->arg);
> + cstate->escape = defGetString(defel);
> }
> - else if (strcmp(defel->defname, "force_quote") == 0)
> + else if (strcmp(defel->defname, "csv_force_quote") == 0)
> {
> if (force_quote || force_quote_all)
> ereport(ERROR,
> @@ -811,20 +812,45 @@
> errmsg("conflicting or
> redundant options")));
> if (defel->arg && IsA(defel->arg, A_Star))
> force_quote_all = true;
> - else
> + else if (defel->arg && IsA(defel->arg, List))
> + {
> + ListCell *lc;
> +
> force_quote = (List *) defel->arg;
> + foreach (lc, force_quote)
> + {
> + if (!IsA(lfirst(lc), String))
> + ereport(ERROR,
> +
> (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> + errmsg("argument to
> option \"%s\" must be a list of column names",
> +
> defel->defname)));
> + }
> + }
> + else
> + ereport(ERROR,
> +
> (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> + errmsg("argument to option \"%s\"
> must be a list of column names",
> + defel->defname)));
> }
> - else if (strcmp(defel->defname, "force_notnull") == 0)
> + else if (strcmp(defel->defname, "csv_force_not_null") == 0)
> {
> if (force_notnull)
> ereport(ERROR,
>
> (errcode(ERRCODE_SYNTAX_ERROR),
> errmsg("conflicting or
> redundant options")));
> - force_notnull = (List *) defel->arg;
> + if (defel->arg && IsA(defel->arg, List))
> + force_notnull = (List *) defel->arg;
> + else
> + ereport(ERROR,
> +
> (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
> + errmsg("argument to option \"%s\"
> must be a list",
> + defel->defname)));
> }
> else
> - elog(ERROR, "option \"%s\" not recognized",
> - defel->defname);
> + ereport(ERROR,
> + (errcode(ERRCODE_SYNTAX_ERROR),
> + errmsg("option \"%s\" not
> recognized",
> + defel->defname)));
> }
>
> /* Check for incompatible options */
>
>
> --
> Sent via pgsql-hackers mailing list (pgsql-hackers(at)postgresql(dot)org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-hackers
>
>
From | Date | Subject | |
---|---|---|---|
Next Message | Tom Lane | 2009-09-17 04:50:01 | Re: generic copy options |
Previous Message | Emmanuel Cecchet | 2009-09-17 03:19:58 | Re: generic copy options |