mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Define a new, more extensible syntax for COPY options.
This is intentionally similar to the recently revised syntax for EXPLAIN options, ie, (name value, ...). The old syntax is still supported for backwards compatibility, but we intend that any options added in future will be provided only in the new syntax. Robert Haas, Emmanuel Cecchet
This commit is contained in:
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.316 2009/07/29 20:56:18 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.317 2009/09/21 20:10:21 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -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"
|
||||
@ -723,6 +724,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
|
||||
List *force_quote = NIL;
|
||||
List *force_notnull = NIL;
|
||||
bool force_quote_all = false;
|
||||
bool format_specified = false;
|
||||
AclMode required_access = (is_from ? ACL_INSERT : ACL_SELECT);
|
||||
AclMode relPerms;
|
||||
AclMode remainingPerms;
|
||||
@ -739,13 +741,25 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
|
||||
{
|
||||
DefElem *defel = (DefElem *) lfirst(option);
|
||||
|
||||
if (strcmp(defel->defname, "binary") == 0)
|
||||
if (strcmp(defel->defname, "format") == 0)
|
||||
{
|
||||
if (cstate->binary)
|
||||
char *fmt = defGetString(defel);
|
||||
|
||||
if (format_specified)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("conflicting or redundant options")));
|
||||
cstate->binary = intVal(defel->arg);
|
||||
format_specified = true;
|
||||
if (strcmp(fmt, "text") == 0)
|
||||
/* default format */ ;
|
||||
else if (strcmp(fmt, "csv") == 0)
|
||||
cstate->csv_mode = true;
|
||||
else if (strcmp(fmt, "binary") == 0)
|
||||
cstate->binary = true;
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("COPY format \"%s\" not recognized", fmt)));
|
||||
}
|
||||
else if (strcmp(defel->defname, "oids") == 0)
|
||||
{
|
||||
@ -753,7 +767,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
|
||||
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 +775,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
|
||||
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,15 +783,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("conflicting or redundant options")));
|
||||
cstate->null_print = strVal(defel->arg);
|
||||
}
|
||||
else if (strcmp(defel->defname, "csv") == 0)
|
||||
{
|
||||
if (cstate->csv_mode)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("conflicting or redundant options")));
|
||||
cstate->csv_mode = intVal(defel->arg);
|
||||
cstate->null_print = defGetString(defel);
|
||||
}
|
||||
else if (strcmp(defel->defname, "header") == 0)
|
||||
{
|
||||
@ -785,7 +791,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
|
||||
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)
|
||||
{
|
||||
@ -793,7 +799,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
|
||||
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)
|
||||
{
|
||||
@ -801,7 +807,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
|
||||
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)
|
||||
{
|
||||
@ -811,33 +817,44 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
|
||||
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))
|
||||
force_quote = (List *) defel->arg;
|
||||
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, "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 of column names",
|
||||
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 */
|
||||
/*
|
||||
* Check for incompatible options (must do these two before inserting
|
||||
* defaults)
|
||||
*/
|
||||
if (cstate->binary && cstate->delim)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("cannot specify DELIMITER in BINARY mode")));
|
||||
|
||||
if (cstate->binary && cstate->csv_mode)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("cannot specify CSV in BINARY mode")));
|
||||
|
||||
if (cstate->binary && cstate->null_print)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
|
@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/define.c,v 1.105 2009/07/26 23:34:17 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/define.c,v 1.106 2009/09/21 20:10:21 tgl Exp $
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The "DefineFoo" routines take the parse tree and pick out the
|
||||
@ -88,6 +88,8 @@ defGetString(DefElem *def)
|
||||
return TypeNameToString((TypeName *) def->arg);
|
||||
case T_List:
|
||||
return NameListToString((List *) def->arg);
|
||||
case T_A_Star:
|
||||
return pstrdup("*");
|
||||
default:
|
||||
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(def->arg));
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.677 2009/08/18 23:40:20 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.678 2009/09/21 20:10:21 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@ -373,6 +373,10 @@ static TypeName *TableFuncTypeName(List *columns);
|
||||
%type <node> explain_option_arg
|
||||
%type <defelt> explain_option_elem
|
||||
%type <list> explain_option_list
|
||||
%type <node> copy_generic_opt_arg copy_generic_opt_arg_list_item
|
||||
%type <defelt> copy_generic_opt_elem
|
||||
%type <list> copy_generic_opt_list copy_generic_opt_arg_list
|
||||
%type <list> copy_options
|
||||
|
||||
%type <typnam> Typename SimpleTypename ConstTypename
|
||||
GenericType Numeric opt_float
|
||||
@ -1934,19 +1938,23 @@ ClosePortalStmt:
|
||||
/*****************************************************************************
|
||||
*
|
||||
* QUERY :
|
||||
* COPY relname ['(' columnList ')'] FROM/TO file [WITH options]
|
||||
* COPY relname [(columnList)] FROM/TO file [WITH] [(options)]
|
||||
* COPY ( SELECT ... ) TO file [WITH] [(options)]
|
||||
*
|
||||
* BINARY, OIDS, and DELIMITERS kept in old locations
|
||||
* for backward compatibility. 2002-06-18
|
||||
* In the preferred syntax the options are comma-separated
|
||||
* and use generic identifiers instead of keywords. The pre-8.5
|
||||
* syntax had a hard-wired, space-separated set of 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' ]
|
||||
* This option placement is not supported with COPY (SELECT...).
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
CopyStmt: COPY opt_binary qualified_name opt_column_list opt_oids
|
||||
copy_from copy_file_name copy_delimiter opt_with copy_opt_list
|
||||
copy_from copy_file_name copy_delimiter opt_with copy_options
|
||||
{
|
||||
CopyStmt *n = makeNode(CopyStmt);
|
||||
n->relation = $3;
|
||||
@ -1967,8 +1975,7 @@ CopyStmt: COPY opt_binary qualified_name opt_column_list opt_oids
|
||||
n->options = list_concat(n->options, $10);
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| COPY select_with_parens TO copy_file_name opt_with
|
||||
copy_opt_list
|
||||
| COPY select_with_parens TO copy_file_name opt_with copy_options
|
||||
{
|
||||
CopyStmt *n = makeNode(CopyStmt);
|
||||
n->relation = NULL;
|
||||
@ -1997,18 +2004,20 @@ copy_file_name:
|
||||
| STDOUT { $$ = NULL; }
|
||||
;
|
||||
|
||||
copy_options: copy_opt_list { $$ = $1; }
|
||||
| '(' copy_generic_opt_list ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
|
||||
/* old COPY option syntax */
|
||||
copy_opt_list:
|
||||
copy_opt_list copy_opt_item { $$ = lappend($1, $2); }
|
||||
| /* EMPTY */ { $$ = NIL; }
|
||||
;
|
||||
|
||||
|
||||
copy_opt_item:
|
||||
BINARY
|
||||
{
|
||||
$$ = makeDefElem("binary", (Node *)makeInteger(TRUE));
|
||||
$$ = makeDefElem("format", (Node *)makeString("binary"));
|
||||
}
|
||||
| OIDS
|
||||
{
|
||||
@ -2024,7 +2033,7 @@ copy_opt_item:
|
||||
}
|
||||
| CSV
|
||||
{
|
||||
$$ = makeDefElem("csv", (Node *)makeInteger(TRUE));
|
||||
$$ = makeDefElem("format", (Node *)makeString("csv"));
|
||||
}
|
||||
| HEADER_P
|
||||
{
|
||||
@ -2048,16 +2057,16 @@ copy_opt_item:
|
||||
}
|
||||
| FORCE NOT NULL_P columnList
|
||||
{
|
||||
$$ = makeDefElem("force_notnull", (Node *)$4);
|
||||
$$ = makeDefElem("force_not_null", (Node *)$4);
|
||||
}
|
||||
;
|
||||
|
||||
/* The following exist for backward compatibility */
|
||||
/* The following exist for backward compatibility with very old versions */
|
||||
|
||||
opt_binary:
|
||||
BINARY
|
||||
{
|
||||
$$ = makeDefElem("binary", (Node *)makeInteger(TRUE));
|
||||
$$ = makeDefElem("format", (Node *)makeString("binary"));
|
||||
}
|
||||
| /*EMPTY*/ { $$ = NULL; }
|
||||
;
|
||||
@ -2071,7 +2080,6 @@ opt_oids:
|
||||
;
|
||||
|
||||
copy_delimiter:
|
||||
/* USING DELIMITERS kept for backward compatibility. 2002-06-15 */
|
||||
opt_using DELIMITERS Sconst
|
||||
{
|
||||
$$ = makeDefElem("delimiter", (Node *)makeString($3));
|
||||
@ -2084,6 +2092,51 @@ opt_using:
|
||||
| /*EMPTY*/ {}
|
||||
;
|
||||
|
||||
/* new COPY option syntax */
|
||||
copy_generic_opt_list:
|
||||
copy_generic_opt_elem
|
||||
{
|
||||
$$ = list_make1($1);
|
||||
}
|
||||
| copy_generic_opt_list ',' copy_generic_opt_elem
|
||||
{
|
||||
$$ = lappend($1, $3);
|
||||
}
|
||||
;
|
||||
|
||||
copy_generic_opt_elem:
|
||||
ColLabel copy_generic_opt_arg
|
||||
{
|
||||
$$ = makeDefElem($1, $2);
|
||||
}
|
||||
;
|
||||
|
||||
copy_generic_opt_arg:
|
||||
opt_boolean { $$ = (Node *) makeString($1); }
|
||||
| ColId_or_Sconst { $$ = (Node *) makeString($1); }
|
||||
| NumericOnly { $$ = (Node *) $1; }
|
||||
| '*' { $$ = (Node *) makeNode(A_Star); }
|
||||
| '(' copy_generic_opt_arg_list ')' { $$ = (Node *) $2; }
|
||||
| /* EMPTY */ { $$ = NULL; }
|
||||
;
|
||||
|
||||
copy_generic_opt_arg_list:
|
||||
copy_generic_opt_arg_list_item
|
||||
{
|
||||
$$ = list_make1($1);
|
||||
}
|
||||
| copy_generic_opt_arg_list ',' copy_generic_opt_arg_list_item
|
||||
{
|
||||
$$ = lappend($1, $3);
|
||||
}
|
||||
;
|
||||
|
||||
/* beware of emitting non-string list elements here; see commands/define.c */
|
||||
copy_generic_opt_arg_list_item:
|
||||
opt_boolean { $$ = (Node *) makeString($1); }
|
||||
| ColId_or_Sconst { $$ = (Node *) makeString($1); }
|
||||
;
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
|
@ -195,6 +195,39 @@ COPY y TO stdout WITH CSV FORCE QUOTE *;
|
||||
"Jackson, Sam","\h"
|
||||
"It is ""perfect""."," "
|
||||
"",
|
||||
-- Repeat above tests with new 8.5 option syntax
|
||||
COPY y TO stdout (FORMAT CSV);
|
||||
"Jackson, Sam",\h
|
||||
"It is ""perfect"".",
|
||||
"",
|
||||
COPY y TO stdout (FORMAT CSV, QUOTE '''', DELIMITER '|');
|
||||
Jackson, Sam|\h
|
||||
It is "perfect".|
|
||||
''|
|
||||
COPY y TO stdout (FORMAT CSV, FORCE_QUOTE (col2), ESCAPE E'\\');
|
||||
"Jackson, Sam","\\h"
|
||||
"It is \"perfect\"."," "
|
||||
"",
|
||||
COPY y TO stdout (FORMAT CSV, FORCE_QUOTE *);
|
||||
"Jackson, Sam","\h"
|
||||
"It is ""perfect""."," "
|
||||
"",
|
||||
\copy y TO stdout (FORMAT CSV)
|
||||
"Jackson, Sam",\h
|
||||
"It is ""perfect"".",
|
||||
"",
|
||||
\copy y TO stdout (FORMAT CSV, QUOTE '''', DELIMITER '|')
|
||||
Jackson, Sam|\h
|
||||
It is "perfect".|
|
||||
''|
|
||||
\copy y TO stdout (FORMAT CSV, FORCE_QUOTE (col2), ESCAPE E'\\')
|
||||
"Jackson, Sam","\\h"
|
||||
"It is \"perfect\"."," "
|
||||
"",
|
||||
\copy y TO stdout (FORMAT 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;
|
||||
|
@ -130,6 +130,18 @@ COPY y TO stdout WITH CSV QUOTE '''' DELIMITER '|';
|
||||
COPY y TO stdout WITH CSV FORCE QUOTE col2 ESCAPE E'\\';
|
||||
COPY y TO stdout WITH CSV FORCE QUOTE *;
|
||||
|
||||
-- Repeat above tests with new 8.5 option syntax
|
||||
|
||||
COPY y TO stdout (FORMAT CSV);
|
||||
COPY y TO stdout (FORMAT CSV, QUOTE '''', DELIMITER '|');
|
||||
COPY y TO stdout (FORMAT CSV, FORCE_QUOTE (col2), ESCAPE E'\\');
|
||||
COPY y TO stdout (FORMAT CSV, FORCE_QUOTE *);
|
||||
|
||||
\copy y TO stdout (FORMAT CSV)
|
||||
\copy y TO stdout (FORMAT CSV, QUOTE '''', DELIMITER '|')
|
||||
\copy y TO stdout (FORMAT CSV, FORCE_QUOTE (col2), ESCAPE E'\\')
|
||||
\copy y TO stdout (FORMAT CSV, FORCE_QUOTE *)
|
||||
|
||||
--test that we read consecutive LFs properly
|
||||
|
||||
CREATE TEMP TABLE testnl (a int, b text, c int);
|
||||
|
Reference in New Issue
Block a user