Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
Postgres FD Implementation
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Abuhujair Javed
Postgres FD Implementation
Commits
85188ab8
Commit
85188ab8
authored
Aug 30, 2006
by
Tom Lane
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Extend COPY to support COPY (SELECT ...) TO ...
Bernd Helmle
parent
0d506578
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
843 additions
and
328 deletions
+843
-328
doc/src/sgml/ref/copy.sgml
doc/src/sgml/ref/copy.sgml
+24
-16
doc/src/sgml/ref/psql-ref.sgml
doc/src/sgml/ref/psql-ref.sgml
+3
-6
src/backend/commands/copy.c
src/backend/commands/copy.c
+519
-290
src/backend/nodes/copyfuncs.c
src/backend/nodes/copyfuncs.c
+2
-1
src/backend/nodes/equalfuncs.c
src/backend/nodes/equalfuncs.c
+2
-1
src/backend/parser/analyze.c
src/backend/parser/analyze.c
+14
-1
src/backend/parser/gram.y
src/backend/parser/gram.y
+20
-3
src/backend/tcop/dest.c
src/backend/tcop/dest.c
+8
-1
src/bin/psql/copy.c
src/bin/psql/copy.c
+24
-1
src/include/commands/copy.h
src/include/commands/copy.h
+4
-1
src/include/nodes/parsenodes.h
src/include/nodes/parsenodes.h
+8
-2
src/include/tcop/dest.h
src/include/tcop/dest.h
+3
-2
src/test/regress/expected/copyselect.out
src/test/regress/expected/copyselect.out
+126
-0
src/test/regress/parallel_schedule
src/test/regress/parallel_schedule
+2
-2
src/test/regress/serial_schedule
src/test/regress/serial_schedule
+2
-1
src/test/regress/sql/copyselect.sql
src/test/regress/sql/copyselect.sql
+82
-0
No files found.
doc/src/sgml/ref/copy.sgml
View file @
85188ab8
<!--
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/copy.sgml,v 1.7
4 2006/04/22 03:03:11 momjian
Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/copy.sgml,v 1.7
5 2006/08/30 23:34:20 tgl
Exp $
PostgreSQL documentation
PostgreSQL documentation
-->
-->
...
@@ -33,7 +33,7 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
...
@@ -33,7 +33,7 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
[ ESCAPE [ AS ] '<replaceable class="parameter">escape</replaceable>' ]
[ ESCAPE [ AS ] '<replaceable class="parameter">escape</replaceable>' ]
[ FORCE NOT NULL <replaceable class="parameter">column</replaceable> [, ...] ]
[ FORCE NOT NULL <replaceable class="parameter">column</replaceable> [, ...] ]
COPY
<replaceable class="parameter">tablename</replaceable> [ ( <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 }
TO { '<replaceable class="parameter">filename</replaceable>' | STDOUT }
[ [ WITH ]
[ [ WITH ]
[ BINARY ]
[ BINARY ]
...
@@ -57,7 +57,8 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
...
@@ -57,7 +57,8 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
files. <command>COPY TO</command> copies the contents of a table
files. <command>COPY TO</command> copies the contents of a table
<emphasis>to</> a file, while <command>COPY FROM</command> copies
<emphasis>to</> a file, while <command>COPY FROM</command> copies
data <emphasis>from</> a file to a table (appending the data to
data <emphasis>from</> a file to a table (appending the data to
whatever is in the table already).
whatever is in the table already). <command>COPY TO</command>
can also copy the results of a <command>SELECT</> query.
</para>
</para>
<para>
<para>
...
@@ -97,7 +98,17 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
...
@@ -97,7 +98,17 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
<listitem>
<listitem>
<para>
<para>
An optional list of columns to be copied. If no column list is
An optional list of columns to be copied. If no column list is
specified, all columns will be used.
specified, all columns of the table will be copied.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">query</replaceable></term>
<listitem>
<para>
A <command>SELECT</> query whose results are to be copied.
Note that parentheses are required around the query.
</para>
</para>
</listitem>
</listitem>
</varlistentry>
</varlistentry>
...
@@ -148,7 +159,8 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
...
@@ -148,7 +159,8 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
<para>
<para>
Specifies copying the OID for each row. (An error is raised if
Specifies copying the OID for each row. (An error is raised if
<literal>OIDS</literal> is specified for a table that does not
<literal>OIDS</literal> is specified for a table that does not
have OIDs.)
have OIDs, or in the case of copying a <replaceable
class="parameter">query</replaceable>.)
</para>
</para>
</listitem>
</listitem>
</varlistentry>
</varlistentry>
...
@@ -265,7 +277,7 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
...
@@ -265,7 +277,7 @@ COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable cla
COPY <replaceable class="parameter">count</replaceable>
COPY <replaceable class="parameter">count</replaceable>
</screen>
</screen>
The <replaceable class="parameter">count</replaceable> is the number
The <replaceable class="parameter">count</replaceable> is the number
of rows
inserted into or copied from the table
.
of rows
copied
.
</para>
</para>
</refsect1>
</refsect1>
...
@@ -274,7 +286,8 @@ COPY <replaceable class="parameter">count</replaceable>
...
@@ -274,7 +286,8 @@ COPY <replaceable class="parameter">count</replaceable>
<para>
<para>
<command>COPY</command> can only be used with plain tables, not
<command>COPY</command> can only be used with plain tables, not
with views.
with views. However, you can write <literal>COPY (SELECT * FROM
<replaceable class="parameter">viewname</replaceable>) TO ...</literal>.
</para>
</para>
<para>
<para>
...
@@ -320,8 +333,8 @@ COPY <replaceable class="parameter">count</replaceable>
...
@@ -320,8 +333,8 @@ COPY <replaceable class="parameter">count</replaceable>
server in the case of <command>COPY TO</command>, but for
server in the case of <command>COPY TO</command>, but for
<command>COPY FROM</command> you do have the option of reading from
<command>COPY FROM</command> you do have the option of reading from
a file specified by a relative path. The path will be interpreted
a file specified by a relative path. The path will be interpreted
relative to the working directory of the server process (
somewhere below
relative to the working directory of the server process (
normally
the data directory), not the client's working directory.
the
cluster's
data directory), not the client's working directory.
</para>
</para>
<para>
<para>
...
@@ -737,14 +750,9 @@ COPY country FROM '/usr1/proj/bray/sql/country_data';
...
@@ -737,14 +750,9 @@ COPY country FROM '/usr1/proj/bray/sql/country_data';
</para>
</para>
<para>
<para>
To copy into a file just the countries whose names start with 'A'
To copy into a file just the countries whose names start with 'A':
using a temporary table which is automatically deleted:
<programlisting>
<programlisting>
BEGIN;
COPY (SELECT * FROM country WHERE country_name LIKE 'A%') TO '/usr1/proj/bray/sql/a_list_countries.copy';
CREATE TEMP TABLE a_list_countries AS
SELECT * FROM country WHERE country_name LIKE 'A%';
COPY a_list_countries TO '/usr1/proj/bray/sql/a_list_countries.copy';
ROLLBACK;
</programlisting>
</programlisting>
</para>
</para>
...
...
doc/src/sgml/ref/psql-ref.sgml
View file @
85188ab8
<!--
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.16
7 2006/08/29 22:25:04
tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.16
8 2006/08/30 23:34:21
tgl Exp $
PostgreSQL documentation
PostgreSQL documentation
-->
-->
...
@@ -739,8 +739,7 @@ testdb=>
...
@@ -739,8 +739,7 @@ testdb=>
</varlistentry>
</varlistentry>
<varlistentry>
<varlistentry>
<term><literal>\copy <replaceable class="parameter">table</replaceable>
<term><literal>\copy { <replaceable class="parameter">table</replaceable> [ ( <replaceable class="parameter">column_list</replaceable> ) ] | ( <replaceable class="parameter">query</replaceable> ) }
[ ( <replaceable class="parameter">column_list</replaceable> ) ]
{ <literal>from</literal> | <literal>to</literal> }
{ <literal>from</literal> | <literal>to</literal> }
{ <replaceable class="parameter">filename</replaceable> | stdin | stdout | pstdin | pstdout }
{ <replaceable class="parameter">filename</replaceable> | stdin | stdout | pstdin | pstdout }
[ with ]
[ with ]
...
@@ -779,9 +778,7 @@ testdb=>
...
@@ -779,9 +778,7 @@ testdb=>
</para>
</para>
<para>
<para>
<literal>\copy <replaceable
<literal>\copy ... from stdin | to stdout</literal>
class="parameter">table</replaceable> from <replaceable
class="parameter">stdin | stdout</replaceable></literal>
reads/writes based on the command input and output respectively.
reads/writes based on the command input and output respectively.
All rows are read from the same source that issued the command,
All rows are read from the same source that issued the command,
continuing until <literal>\.</literal> is read or the stream
continuing until <literal>\.</literal> is read or the stream
...
...
src/backend/commands/copy.c
View file @
85188ab8
...
@@ -8,7 +8,7 @@
...
@@ -8,7 +8,7 @@
*
*
*
*
* IDENTIFICATION
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.26
8 2006/07/14 14:52:18 momjian
Exp $
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.26
9 2006/08/30 23:34:21 tgl
Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -31,6 +31,7 @@
...
@@ -31,6 +31,7 @@
#include "libpq/pqformat.h"
#include "libpq/pqformat.h"
#include "mb/pg_wchar.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "miscadmin.h"
#include "optimizer/planner.h"
#include "parser/parse_relation.h"
#include "parser/parse_relation.h"
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteHandler.h"
#include "storage/fd.h"
#include "storage/fd.h"
...
@@ -99,18 +100,21 @@ typedef struct CopyStateData
...
@@ -99,18 +100,21 @@ typedef struct CopyStateData
/* parameters from the COPY command */
/* parameters from the COPY command */
Relation
rel
;
/* relation to copy to or from */
Relation
rel
;
/* relation to copy to or from */
QueryDesc
*
queryDesc
;
/* executable query to copy from */
List
*
attnumlist
;
/* integer list of attnums to copy */
List
*
attnumlist
;
/* integer list of attnums to copy */
char
*
filename
;
/* filename, or NULL for STDIN/STDOUT */
bool
binary
;
/* binary format? */
bool
binary
;
/* binary format? */
bool
oids
;
/* include OIDs? */
bool
oids
;
/* include OIDs? */
bool
csv_mode
;
/* Comma Separated Value format? */
bool
csv_mode
;
/* Comma Separated Value format? */
bool
header_line
;
/* CSV header line? */
bool
header_line
;
/* CSV header line? */
char
*
null_print
;
/* NULL marker string (server encoding!) */
char
*
null_print
;
/* NULL marker string (server encoding!) */
int
null_print_len
;
/* length of same */
int
null_print_len
;
/* length of same */
char
*
null_print_client
;
/* same converted to client encoding */
char
*
delim
;
/* column delimiter (must be 1 byte) */
char
*
delim
;
/* column delimiter (must be 1 byte) */
char
*
quote
;
/* CSV quote char (must be 1 byte) */
char
*
quote
;
/* CSV quote char (must be 1 byte) */
char
*
escape
;
/* CSV escape char (must be 1 byte) */
char
*
escape
;
/* CSV escape char (must be 1 byte) */
List
*
force_quote_atts
;
/* integer list of attnums to FQ
*/
bool
*
force_quote_flags
;
/* per-column CSV FQ flags
*/
List
*
force_notnull_atts
;
/* integer list of attnums to FNN
*/
bool
*
force_notnull_flags
;
/* per-column CSV FNN flags
*/
/* these are just for error messages, see copy_in_error_callback */
/* these are just for error messages, see copy_in_error_callback */
const
char
*
cur_relname
;
/* table name for error messages */
const
char
*
cur_relname
;
/* table name for error messages */
...
@@ -118,6 +122,12 @@ typedef struct CopyStateData
...
@@ -118,6 +122,12 @@ typedef struct CopyStateData
const
char
*
cur_attname
;
/* current att for error messages */
const
char
*
cur_attname
;
/* current att for error messages */
const
char
*
cur_attval
;
/* current att value for error messages */
const
char
*
cur_attval
;
/* current att value for error messages */
/*
* Working state for COPY TO
*/
FmgrInfo
*
out_functions
;
/* lookup info for output functions */
MemoryContext
rowcontext
;
/* per-row evaluation context */
/*
/*
* These variables are used to reduce overhead in textual COPY FROM.
* These variables are used to reduce overhead in textual COPY FROM.
*
*
...
@@ -153,6 +163,13 @@ typedef struct CopyStateData
...
@@ -153,6 +163,13 @@ typedef struct CopyStateData
typedef
CopyStateData
*
CopyState
;
typedef
CopyStateData
*
CopyState
;
/* DestReceiver for COPY (SELECT) TO */
typedef
struct
{
DestReceiver
pub
;
/* publicly-known function pointers */
CopyState
cstate
;
/* CopyStateData for the command */
}
DR_copy
;
/*
/*
* These macros centralize code used to process line_buf and raw_buf buffers.
* These macros centralize code used to process line_buf and raw_buf buffers.
...
@@ -225,6 +242,8 @@ static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
...
@@ -225,6 +242,8 @@ static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
/* non-export function prototypes */
/* non-export function prototypes */
static
void
DoCopyTo
(
CopyState
cstate
);
static
void
DoCopyTo
(
CopyState
cstate
);
static
void
CopyTo
(
CopyState
cstate
);
static
void
CopyTo
(
CopyState
cstate
);
static
void
CopyOneRowTo
(
CopyState
cstate
,
Oid
tupleOid
,
Datum
*
values
,
bool
*
nulls
);
static
void
CopyFrom
(
CopyState
cstate
);
static
void
CopyFrom
(
CopyState
cstate
);
static
bool
CopyReadLine
(
CopyState
cstate
);
static
bool
CopyReadLine
(
CopyState
cstate
);
static
bool
CopyReadLineText
(
CopyState
cstate
);
static
bool
CopyReadLineText
(
CopyState
cstate
);
...
@@ -239,7 +258,8 @@ static Datum CopyReadBinaryAttribute(CopyState cstate,
...
@@ -239,7 +258,8 @@ static Datum CopyReadBinaryAttribute(CopyState cstate,
static
void
CopyAttributeOutText
(
CopyState
cstate
,
char
*
string
);
static
void
CopyAttributeOutText
(
CopyState
cstate
,
char
*
string
);
static
void
CopyAttributeOutCSV
(
CopyState
cstate
,
char
*
string
,
static
void
CopyAttributeOutCSV
(
CopyState
cstate
,
char
*
string
,
bool
use_quote
,
bool
single_attr
);
bool
use_quote
,
bool
single_attr
);
static
List
*
CopyGetAttnums
(
Relation
rel
,
List
*
attnamelist
);
static
List
*
CopyGetAttnums
(
TupleDesc
tupDesc
,
Relation
rel
,
List
*
attnamelist
);
static
char
*
limit_printout_length
(
const
char
*
str
);
static
char
*
limit_printout_length
(
const
char
*
str
);
/* Low-level communications functions */
/* Low-level communications functions */
...
@@ -668,7 +688,8 @@ CopyLoadRawBuf(CopyState cstate)
...
@@ -668,7 +688,8 @@ CopyLoadRawBuf(CopyState cstate)
* DoCopy executes the SQL COPY statement.
* DoCopy executes the SQL COPY statement.
*
*
* Either unload or reload contents of table <relation>, depending on <from>.
* Either unload or reload contents of table <relation>, depending on <from>.
* (<from> = TRUE means we are inserting into the table.)
* (<from> = TRUE means we are inserting into the table.) In the "TO" case
* we also support copying the output of an arbitrary SELECT query.
*
*
* If <pipe> is false, transfer is between the table and the file named
* If <pipe> is false, transfer is between the table and the file named
* <filename>. Otherwise, transfer is between the table and our regular
* <filename>. Otherwise, transfer is between the table and our regular
...
@@ -697,8 +718,6 @@ uint64
...
@@ -697,8 +718,6 @@ uint64
DoCopy
(
const
CopyStmt
*
stmt
)
DoCopy
(
const
CopyStmt
*
stmt
)
{
{
CopyState
cstate
;
CopyState
cstate
;
RangeVar
*
relation
=
stmt
->
relation
;
char
*
filename
=
stmt
->
filename
;
bool
is_from
=
stmt
->
is_from
;
bool
is_from
=
stmt
->
is_from
;
bool
pipe
=
(
stmt
->
filename
==
NULL
);
bool
pipe
=
(
stmt
->
filename
==
NULL
);
List
*
attnamelist
=
stmt
->
attlist
;
List
*
attnamelist
=
stmt
->
attlist
;
...
@@ -707,6 +726,8 @@ DoCopy(const CopyStmt *stmt)
...
@@ -707,6 +726,8 @@ DoCopy(const CopyStmt *stmt)
AclMode
required_access
=
(
is_from
?
ACL_INSERT
:
ACL_SELECT
);
AclMode
required_access
=
(
is_from
?
ACL_INSERT
:
ACL_SELECT
);
AclResult
aclresult
;
AclResult
aclresult
;
ListCell
*
option
;
ListCell
*
option
;
TupleDesc
tupDesc
;
int
num_phys_attrs
;
uint64
processed
;
uint64
processed
;
/* Allocate workspace and zero all fields */
/* Allocate workspace and zero all fields */
...
@@ -920,23 +941,7 @@ DoCopy(const CopyStmt *stmt)
...
@@ -920,23 +941,7 @@ DoCopy(const CopyStmt *stmt)
(
errcode
(
ERRCODE_FEATURE_NOT_SUPPORTED
),
(
errcode
(
ERRCODE_FEATURE_NOT_SUPPORTED
),
errmsg
(
"CSV quote character must not appear in the NULL specification"
)));
errmsg
(
"CSV quote character must not appear in the NULL specification"
)));
/* Open and lock the relation, using the appropriate lock type. */
/* Disallow file COPY except to superusers. */
cstate
->
rel
=
heap_openrv
(
relation
,
(
is_from
?
RowExclusiveLock
:
AccessShareLock
));
/* check read-only transaction */
if
(
XactReadOnly
&&
is_from
&&
!
isTempNamespace
(
RelationGetNamespace
(
cstate
->
rel
)))
ereport
(
ERROR
,
(
errcode
(
ERRCODE_READ_ONLY_SQL_TRANSACTION
),
errmsg
(
"transaction is read-only"
)));
/* Check permissions. */
aclresult
=
pg_class_aclcheck
(
RelationGetRelid
(
cstate
->
rel
),
GetUserId
(),
required_access
);
if
(
aclresult
!=
ACLCHECK_OK
)
aclcheck_error
(
aclresult
,
ACL_KIND_CLASS
,
RelationGetRelationName
(
cstate
->
rel
));
if
(
!
pipe
&&
!
superuser
())
if
(
!
pipe
&&
!
superuser
())
ereport
(
ERROR
,
ereport
(
ERROR
,
(
errcode
(
ERRCODE_INSUFFICIENT_PRIVILEGE
),
(
errcode
(
ERRCODE_INSUFFICIENT_PRIVILEGE
),
...
@@ -944,26 +949,137 @@ DoCopy(const CopyStmt *stmt)
...
@@ -944,26 +949,137 @@ DoCopy(const CopyStmt *stmt)
errhint
(
"Anyone can COPY to stdout or from stdin. "
errhint
(
"Anyone can COPY to stdout or from stdin. "
"psql's
\\
copy command also works for anyone."
)));
"psql's
\\
copy command also works for anyone."
)));
/* Don't allow COPY w/ OIDs to or from a table without them */
if
(
stmt
->
relation
)
if
(
cstate
->
oids
&&
!
cstate
->
rel
->
rd_rel
->
relhasoids
)
{
ereport
(
ERROR
,
Assert
(
!
stmt
->
query
);
(
errcode
(
ERRCODE_UNDEFINED_COLUMN
),
cstate
->
queryDesc
=
NULL
;
errmsg
(
"table
\"
%s
\"
does not have OIDs"
,
RelationGetRelationName
(
cstate
->
rel
))));
/* Open and lock the relation, using the appropriate lock type. */
cstate
->
rel
=
heap_openrv
(
stmt
->
relation
,
(
is_from
?
RowExclusiveLock
:
AccessShareLock
));
/* Check relation permissions. */
aclresult
=
pg_class_aclcheck
(
RelationGetRelid
(
cstate
->
rel
),
GetUserId
(),
required_access
);
if
(
aclresult
!=
ACLCHECK_OK
)
aclcheck_error
(
aclresult
,
ACL_KIND_CLASS
,
RelationGetRelationName
(
cstate
->
rel
));
/* check read-only transaction */
if
(
XactReadOnly
&&
is_from
&&
!
isTempNamespace
(
RelationGetNamespace
(
cstate
->
rel
)))
ereport
(
ERROR
,
(
errcode
(
ERRCODE_READ_ONLY_SQL_TRANSACTION
),
errmsg
(
"transaction is read-only"
)));
/* Don't allow COPY w/ OIDs to or from a table without them */
if
(
cstate
->
oids
&&
!
cstate
->
rel
->
rd_rel
->
relhasoids
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_UNDEFINED_COLUMN
),
errmsg
(
"table
\"
%s
\"
does not have OIDs"
,
RelationGetRelationName
(
cstate
->
rel
))));
tupDesc
=
RelationGetDescr
(
cstate
->
rel
);
}
else
{
Query
*
query
=
stmt
->
query
;
List
*
rewritten
;
Plan
*
plan
;
DestReceiver
*
dest
;
Assert
(
query
);
Assert
(
!
is_from
);
cstate
->
rel
=
NULL
;
/* Don't allow COPY w/ OIDs from a select */
if
(
cstate
->
oids
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_FEATURE_NOT_SUPPORTED
),
errmsg
(
"COPY (SELECT) WITH OIDS is not supported"
)));
if
(
query
->
into
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_FEATURE_NOT_SUPPORTED
),
errmsg
(
"COPY (SELECT INTO) is not supported"
)));
/*
* The query has already been through parse analysis, but not
* rewriting or planning. Do that now.
*
* Because the planner is not cool about not scribbling on its input,
* we make a preliminary copy of the source querytree. This prevents
* problems in the case that the COPY is in a portal or plpgsql
* function and is executed repeatedly. (See also the same hack in
* EXPLAIN, DECLARE CURSOR and PREPARE.) XXX the planner really
* shouldn't modify its input ... FIXME someday.
*/
query
=
copyObject
(
query
);
Assert
(
query
->
commandType
==
CMD_SELECT
);
/*
* Must acquire locks in case we didn't come fresh from the parser.
* XXX this also scribbles on query, another reason for copyObject
*/
AcquireRewriteLocks
(
query
);
/* Rewrite through rule system */
rewritten
=
QueryRewrite
(
query
);
/* We don't expect more or less than one result query */
if
(
list_length
(
rewritten
)
!=
1
)
elog
(
ERROR
,
"unexpected rewrite result"
);
query
=
(
Query
*
)
linitial
(
rewritten
);
Assert
(
query
->
commandType
==
CMD_SELECT
);
/* plan the query */
plan
=
planner
(
query
,
false
,
0
,
NULL
);
/*
* Update snapshot command ID to ensure this query sees results of any
* previously executed queries. (It's a bit cheesy to modify
* ActiveSnapshot without making a copy, but for the limited ways in
* which COPY can be invoked, I think it's OK, because the active
* snapshot shouldn't be shared with anything else anyway.)
*/
ActiveSnapshot
->
curcid
=
GetCurrentCommandId
();
/* Create dest receiver for COPY OUT */
dest
=
CreateDestReceiver
(
DestCopyOut
,
NULL
);
((
DR_copy
*
)
dest
)
->
cstate
=
cstate
;
/* Create a QueryDesc requesting no output */
cstate
->
queryDesc
=
CreateQueryDesc
(
query
,
plan
,
ActiveSnapshot
,
InvalidSnapshot
,
dest
,
NULL
,
false
);
/*
* Call ExecutorStart to prepare the plan for execution.
*
* ExecutorStart computes a result tupdesc for us
*/
ExecutorStart
(
cstate
->
queryDesc
,
0
);
tupDesc
=
cstate
->
queryDesc
->
tupDesc
;
}
/* Generate or convert list of attributes to process */
/* Generate or convert list of attributes to process */
cstate
->
attnumlist
=
CopyGetAttnums
(
cstate
->
rel
,
attnamelist
);
cstate
->
attnumlist
=
CopyGetAttnums
(
tupDesc
,
cstate
->
rel
,
attnamelist
);
num_phys_attrs
=
tupDesc
->
natts
;
/* Convert FORCE QUOTE name list to column numbers, check validity */
/* Convert FORCE QUOTE name list to per-column flags, check validity */
cstate
->
force_quote_flags
=
(
bool
*
)
palloc0
(
num_phys_attrs
*
sizeof
(
bool
));
if
(
force_quote
)
if
(
force_quote
)
{
{
TupleDesc
tupDesc
=
RelationGetDescr
(
cstate
->
rel
);
List
*
attnums
;
Form_pg_attribute
*
attr
=
tupDesc
->
attrs
;
ListCell
*
cur
;
ListCell
*
cur
;
cstate
->
force_quote_atts
=
CopyGetAttnums
(
cstate
->
rel
,
force_quote
);
attnums
=
CopyGetAttnums
(
tupDesc
,
cstate
->
rel
,
force_quote
);
foreach
(
cur
,
cstate
->
force_quote_att
s
)
foreach
(
cur
,
attnum
s
)
{
{
int
attnum
=
lfirst_int
(
cur
);
int
attnum
=
lfirst_int
(
cur
);
...
@@ -971,21 +1087,21 @@ DoCopy(const CopyStmt *stmt)
...
@@ -971,21 +1087,21 @@ DoCopy(const CopyStmt *stmt)
ereport
(
ERROR
,
ereport
(
ERROR
,
(
errcode
(
ERRCODE_INVALID_COLUMN_REFERENCE
),
(
errcode
(
ERRCODE_INVALID_COLUMN_REFERENCE
),
errmsg
(
"FORCE QUOTE column
\"
%s
\"
not referenced by COPY"
,
errmsg
(
"FORCE QUOTE column
\"
%s
\"
not referenced by COPY"
,
NameStr
(
attr
[
attnum
-
1
]
->
attname
))));
NameStr
(
tupDesc
->
attrs
[
attnum
-
1
]
->
attname
))));
cstate
->
force_quote_flags
[
attnum
-
1
]
=
true
;
}
}
}
}
/* Convert FORCE NOT NULL name list to column numbers, check validity */
/* Convert FORCE NOT NULL name list to per-column flags, check validity */
cstate
->
force_notnull_flags
=
(
bool
*
)
palloc0
(
num_phys_attrs
*
sizeof
(
bool
));
if
(
force_notnull
)
if
(
force_notnull
)
{
{
TupleDesc
tupDesc
=
RelationGetDescr
(
cstate
->
rel
);
List
*
attnums
;
Form_pg_attribute
*
attr
=
tupDesc
->
attrs
;
ListCell
*
cur
;
ListCell
*
cur
;
cstate
->
force_notnull_atts
=
CopyGetAttnums
(
cstate
->
rel
,
attnums
=
CopyGetAttnums
(
tupDesc
,
cstate
->
rel
,
force_notnull
);
force_notnull
);
foreach
(
cur
,
cstate
->
force_notnull_att
s
)
foreach
(
cur
,
attnum
s
)
{
{
int
attnum
=
lfirst_int
(
cur
);
int
attnum
=
lfirst_int
(
cur
);
...
@@ -993,7 +1109,8 @@ DoCopy(const CopyStmt *stmt)
...
@@ -993,7 +1109,8 @@ DoCopy(const CopyStmt *stmt)
ereport
(
ERROR
,
ereport
(
ERROR
,
(
errcode
(
ERRCODE_INVALID_COLUMN_REFERENCE
),
(
errcode
(
ERRCODE_INVALID_COLUMN_REFERENCE
),
errmsg
(
"FORCE NOT NULL column
\"
%s
\"
not referenced by COPY"
,
errmsg
(
"FORCE NOT NULL column
\"
%s
\"
not referenced by COPY"
,
NameStr
(
attr
[
attnum
-
1
]
->
attname
))));
NameStr
(
tupDesc
->
attrs
[
attnum
-
1
]
->
attname
))));
cstate
->
force_notnull_flags
[
attnum
-
1
]
=
true
;
}
}
}
}
...
@@ -1018,67 +1135,59 @@ DoCopy(const CopyStmt *stmt)
...
@@ -1018,67 +1135,59 @@ DoCopy(const CopyStmt *stmt)
cstate
->
encoding_embeds_ascii
=
PG_ENCODING_IS_CLIENT_ONLY
(
cstate
->
client_encoding
);
cstate
->
encoding_embeds_ascii
=
PG_ENCODING_IS_CLIENT_ONLY
(
cstate
->
client_encoding
);
cstate
->
copy_dest
=
COPY_FILE
;
/* default */
cstate
->
copy_dest
=
COPY_FILE
;
/* default */
cstate
->
filename
=
stmt
->
filename
;
if
(
is_from
)
if
(
is_from
)
/* copy from file to database */
{
/* copy from file to database */
CopyFrom
(
cstate
);
if
(
cstate
->
rel
->
rd_rel
->
relkind
!=
RELKIND_RELATION
)
else
/* copy from database to file */
{
DoCopyTo
(
cstate
);
if
(
cstate
->
rel
->
rd_rel
->
relkind
==
RELKIND_VIEW
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_WRONG_OBJECT_TYPE
),
errmsg
(
"cannot copy to view
\"
%s
\"
"
,
RelationGetRelationName
(
cstate
->
rel
))));
else
if
(
cstate
->
rel
->
rd_rel
->
relkind
==
RELKIND_SEQUENCE
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_WRONG_OBJECT_TYPE
),
errmsg
(
"cannot copy to sequence
\"
%s
\"
"
,
RelationGetRelationName
(
cstate
->
rel
))));
else
ereport
(
ERROR
,
(
errcode
(
ERRCODE_WRONG_OBJECT_TYPE
),
errmsg
(
"cannot copy to non-table relation
\"
%s
\"
"
,
RelationGetRelationName
(
cstate
->
rel
))));
}
if
(
pipe
)
{
if
(
whereToSendOutput
==
DestRemote
)
ReceiveCopyBegin
(
cstate
);
else
cstate
->
copy_file
=
stdin
;
}
else
{
struct
stat
st
;
cstate
->
copy_file
=
AllocateFile
(
filename
,
PG_BINARY_R
);
/*
* Close the relation or query. If reading, we can release the
* AccessShareLock we got; if writing, we should hold the lock until end
* of transaction to ensure that updates will be committed before lock is
* released.
*/
if
(
cstate
->
rel
)
heap_close
(
cstate
->
rel
,
(
is_from
?
NoLock
:
AccessShareLock
));
else
{
/* Close down the query and free resources. */
ExecutorEnd
(
cstate
->
queryDesc
);
FreeQueryDesc
(
cstate
->
queryDesc
);
}
if
(
cstate
->
copy_file
==
NULL
)
/* Clean up storage (probably not really necessary) */
ereport
(
ERROR
,
processed
=
cstate
->
processed
;
(
errcode_for_file_access
(),
errmsg
(
"could not open file
\"
%s
\"
for reading: %m"
,
filename
)));
fstat
(
fileno
(
cstate
->
copy_file
),
&
st
);
pfree
(
cstate
->
attribute_buf
.
data
);
if
(
S_ISDIR
(
st
.
st_mode
))
pfree
(
cstate
->
line_buf
.
data
);
{
pfree
(
cstate
->
raw_buf
);
FreeFile
(
cstate
->
copy_file
);
pfree
(
cstate
);
ereport
(
ERROR
,
(
errcode
(
ERRCODE_WRONG_OBJECT_TYPE
),
errmsg
(
"
\"
%s
\"
is a directory"
,
filename
)));
}
}
CopyFrom
(
cstate
);
return
processed
;
}
}
else
{
/* copy from database to file */
/*
* This intermediate routine exists mainly to localize the effects of setjmp
* so we don't need to plaster a lot of variables with "volatile".
*/
static
void
DoCopyTo
(
CopyState
cstate
)
{
bool
pipe
=
(
cstate
->
filename
==
NULL
);
if
(
cstate
->
rel
)
{
if
(
cstate
->
rel
->
rd_rel
->
relkind
!=
RELKIND_RELATION
)
if
(
cstate
->
rel
->
rd_rel
->
relkind
!=
RELKIND_RELATION
)
{
{
if
(
cstate
->
rel
->
rd_rel
->
relkind
==
RELKIND_VIEW
)
if
(
cstate
->
rel
->
rd_rel
->
relkind
==
RELKIND_VIEW
)
ereport
(
ERROR
,
ereport
(
ERROR
,
(
errcode
(
ERRCODE_WRONG_OBJECT_TYPE
),
(
errcode
(
ERRCODE_WRONG_OBJECT_TYPE
),
errmsg
(
"cannot copy from view
\"
%s
\"
"
,
errmsg
(
"cannot copy from view
\"
%s
\"
"
,
RelationGetRelationName
(
cstate
->
rel
))));
RelationGetRelationName
(
cstate
->
rel
)),
errhint
(
"Try the COPY (SELECT ...) TO variant."
)));
else
if
(
cstate
->
rel
->
rd_rel
->
relkind
==
RELKIND_SEQUENCE
)
else
if
(
cstate
->
rel
->
rd_rel
->
relkind
==
RELKIND_SEQUENCE
)
ereport
(
ERROR
,
ereport
(
ERROR
,
(
errcode
(
ERRCODE_WRONG_OBJECT_TYPE
),
(
errcode
(
ERRCODE_WRONG_OBJECT_TYPE
),
...
@@ -1090,86 +1199,49 @@ DoCopy(const CopyStmt *stmt)
...
@@ -1090,86 +1199,49 @@ DoCopy(const CopyStmt *stmt)
errmsg
(
"cannot copy from non-table relation
\"
%s
\"
"
,
errmsg
(
"cannot copy from non-table relation
\"
%s
\"
"
,
RelationGetRelationName
(
cstate
->
rel
))));
RelationGetRelationName
(
cstate
->
rel
))));
}
}
if
(
pipe
)
{
if
(
whereToSendOutput
==
DestRemote
)
cstate
->
fe_copy
=
true
;
else
cstate
->
copy_file
=
stdout
;
}
else
{
mode_t
oumask
;
/* Pre-existing umask value */
struct
stat
st
;
/*
* Prevent write to relative path ... too easy to shoot oneself in
* the foot by overwriting a database file ...
*/
if
(
!
is_absolute_path
(
filename
))
ereport
(
ERROR
,
(
errcode
(
ERRCODE_INVALID_NAME
),
errmsg
(
"relative path not allowed for COPY to file"
)));
oumask
=
umask
((
mode_t
)
022
);
cstate
->
copy_file
=
AllocateFile
(
filename
,
PG_BINARY_W
);
umask
(
oumask
);
if
(
cstate
->
copy_file
==
NULL
)
ereport
(
ERROR
,
(
errcode_for_file_access
(),
errmsg
(
"could not open file
\"
%s
\"
for writing: %m"
,
filename
)));
fstat
(
fileno
(
cstate
->
copy_file
),
&
st
);
if
(
S_ISDIR
(
st
.
st_mode
))
{
FreeFile
(
cstate
->
copy_file
);
ereport
(
ERROR
,
(
errcode
(
ERRCODE_WRONG_OBJECT_TYPE
),
errmsg
(
"
\"
%s
\"
is a directory"
,
filename
)));
}
}
DoCopyTo
(
cstate
);
}
}
if
(
!
pipe
)
if
(
pipe
)
{
{
/* we assume only the write case could fail here */
if
(
whereToSendOutput
==
DestRemote
)
if
(
FreeFile
(
cstate
->
copy_file
))
cstate
->
fe_copy
=
true
;
ereport
(
ERROR
,
else
(
errcode_for_file_access
(),
cstate
->
copy_file
=
stdout
;
errmsg
(
"could not write to file
\"
%s
\"
: %m"
,
filename
)));
}
}
else
{
mode_t
oumask
;
/* Pre-existing umask value */
struct
stat
st
;
/*
/*
* Close the relation. If reading, we can release the AccessShareLock we
* Prevent write to relative path ... too easy to shoot oneself in
* got; if writing, we should hold the lock until end of transaction to
* the foot by overwriting a database file ...
* ensure that updates will be committed before lock is released.
*/
*/
if
(
!
is_absolute_path
(
cstate
->
filename
))
heap_close
(
cstate
->
rel
,
(
is_from
?
NoLock
:
AccessShareLock
));
ereport
(
ERROR
,
(
errcode
(
ERRCODE_INVALID_NAME
),
/* Clean up storage (probably not really necessary) */
errmsg
(
"relative path not allowed for COPY to file"
)));
processed
=
cstate
->
processed
;
pfree
(
cstate
->
attribute_buf
.
data
);
oumask
=
umask
((
mode_t
)
022
);
pfree
(
cstate
->
line_buf
.
data
);
cstate
->
copy_file
=
AllocateFile
(
cstate
->
filename
,
PG_BINARY_W
);
pfree
(
cstate
->
raw_buf
);
umask
(
oumask
);
pfree
(
cstate
);
return
processed
;
if
(
cstate
->
copy_file
==
NULL
)
}
ereport
(
ERROR
,
(
errcode_for_file_access
(),
errmsg
(
"could not open file
\"
%s
\"
for writing: %m"
,
cstate
->
filename
)));
fstat
(
fileno
(
cstate
->
copy_file
),
&
st
);
if
(
S_ISDIR
(
st
.
st_mode
))
{
FreeFile
(
cstate
->
copy_file
);
ereport
(
ERROR
,
(
errcode
(
ERRCODE_WRONG_OBJECT_TYPE
),
errmsg
(
"
\"
%s
\"
is a directory"
,
cstate
->
filename
)));
}
}
/*
* This intermediate routine just exists to localize the effects of setjmp
* so we don't need to plaster a lot of variables with "volatile".
*/
static
void
DoCopyTo
(
CopyState
cstate
)
{
PG_TRY
();
PG_TRY
();
{
{
if
(
cstate
->
fe_copy
)
if
(
cstate
->
fe_copy
)
...
@@ -1191,40 +1263,41 @@ DoCopyTo(CopyState cstate)
...
@@ -1191,40 +1263,41 @@ DoCopyTo(CopyState cstate)
PG_RE_THROW
();
PG_RE_THROW
();
}
}
PG_END_TRY
();
PG_END_TRY
();
if
(
!
pipe
)
{
if
(
FreeFile
(
cstate
->
copy_file
))
ereport
(
ERROR
,
(
errcode_for_file_access
(),
errmsg
(
"could not write to file
\"
%s
\"
: %m"
,
cstate
->
filename
)));
}
}
}
/*
/*
* Copy from relation TO file.
* Copy from relation
or query
TO file.
*/
*/
static
void
static
void
CopyTo
(
CopyState
cstate
)
CopyTo
(
CopyState
cstate
)
{
{
HeapTuple
tuple
;
TupleDesc
tupDesc
;
TupleDesc
tupDesc
;
HeapScanDesc
scandesc
;
int
num_phys_attrs
;
int
num_phys_attrs
;
int
attr_count
;
Form_pg_attribute
*
attr
;
Form_pg_attribute
*
attr
;
FmgrInfo
*
out_functions
;
bool
*
force_quote
;
char
*
string
;
char
*
null_print_client
;
ListCell
*
cur
;
ListCell
*
cur
;
MemoryContext
oldcontext
;
MemoryContext
mycontext
;
tupDesc
=
cstate
->
rel
->
rd_att
;
if
(
cstate
->
rel
)
tupDesc
=
RelationGetDescr
(
cstate
->
rel
);
else
tupDesc
=
cstate
->
queryDesc
->
tupDesc
;
attr
=
tupDesc
->
attrs
;
attr
=
tupDesc
->
attrs
;
num_phys_attrs
=
tupDesc
->
natts
;
num_phys_attrs
=
tupDesc
->
natts
;
attr_count
=
list_length
(
cstate
->
attnumlist
);
cstate
->
null_print_client
=
cstate
->
null_print
;
/* default */
null_print_client
=
cstate
->
null_print
;
/* default */
/* We use fe_msgbuf as a per-row buffer regardless of copy_dest */
/* We use fe_msgbuf as a per-row buffer regardless of copy_dest */
cstate
->
fe_msgbuf
=
makeStringInfo
();
cstate
->
fe_msgbuf
=
makeStringInfo
();
/* Get info about the columns we need to process. */
/* Get info about the columns we need to process. */
out_functions
=
(
FmgrInfo
*
)
palloc
(
num_phys_attrs
*
sizeof
(
FmgrInfo
));
cstate
->
out_functions
=
(
FmgrInfo
*
)
palloc
(
num_phys_attrs
*
sizeof
(
FmgrInfo
));
force_quote
=
(
bool
*
)
palloc
(
num_phys_attrs
*
sizeof
(
bool
));
foreach
(
cur
,
cstate
->
attnumlist
)
foreach
(
cur
,
cstate
->
attnumlist
)
{
{
int
attnum
=
lfirst_int
(
cur
);
int
attnum
=
lfirst_int
(
cur
);
...
@@ -1239,12 +1312,7 @@ CopyTo(CopyState cstate)
...
@@ -1239,12 +1312,7 @@ CopyTo(CopyState cstate)
getTypeOutputInfo
(
attr
[
attnum
-
1
]
->
atttypid
,
getTypeOutputInfo
(
attr
[
attnum
-
1
]
->
atttypid
,
&
out_func_oid
,
&
out_func_oid
,
&
isvarlena
);
&
isvarlena
);
fmgr_info
(
out_func_oid
,
&
out_functions
[
attnum
-
1
]);
fmgr_info
(
out_func_oid
,
&
cstate
->
out_functions
[
attnum
-
1
]);
if
(
list_member_int
(
cstate
->
force_quote_atts
,
attnum
))
force_quote
[
attnum
-
1
]
=
true
;
else
force_quote
[
attnum
-
1
]
=
false
;
}
}
/*
/*
...
@@ -1253,11 +1321,11 @@ CopyTo(CopyState cstate)
...
@@ -1253,11 +1321,11 @@ CopyTo(CopyState cstate)
* datatype output routines, and should be faster than retail pfree's
* datatype output routines, and should be faster than retail pfree's
* anyway. (We don't need a whole econtext as CopyFrom does.)
* anyway. (We don't need a whole econtext as CopyFrom does.)
*/
*/
my
context
=
AllocSetContextCreate
(
CurrentMemoryContext
,
cstate
->
row
context
=
AllocSetContextCreate
(
CurrentMemoryContext
,
"COPY TO"
,
"COPY TO"
,
ALLOCSET_DEFAULT_MINSIZE
,
ALLOCSET_DEFAULT_MINSIZE
,
ALLOCSET_DEFAULT_INITSIZE
,
ALLOCSET_DEFAULT_INITSIZE
,
ALLOCSET_DEFAULT_MAXSIZE
);
ALLOCSET_DEFAULT_MAXSIZE
);
if
(
cstate
->
binary
)
if
(
cstate
->
binary
)
{
{
...
@@ -1282,7 +1350,7 @@ CopyTo(CopyState cstate)
...
@@ -1282,7 +1350,7 @@ CopyTo(CopyState cstate)
* encoding, because it will be sent directly with CopySendString.
* encoding, because it will be sent directly with CopySendString.
*/
*/
if
(
cstate
->
need_transcoding
)
if
(
cstate
->
need_transcoding
)
null_print_client
=
pg_server_to_client
(
cstate
->
null_print
,
cstate
->
null_print_client
=
pg_server_to_client
(
cstate
->
null_print
,
cstate
->
null_print_len
);
cstate
->
null_print_len
);
/* if a header has been requested send the line */
/* if a header has been requested send the line */
...
@@ -1309,113 +1377,139 @@ CopyTo(CopyState cstate)
...
@@ -1309,113 +1377,139 @@ CopyTo(CopyState cstate)
}
}
}
}
scandesc
=
heap_beginscan
(
cstate
->
rel
,
ActiveSnapshot
,
0
,
NULL
);
if
(
cstate
->
rel
)
while
((
tuple
=
heap_getnext
(
scandesc
,
ForwardScanDirection
))
!=
NULL
)
{
{
bool
need_delim
=
false
;
Datum
*
values
;
bool
*
nulls
;
HeapScanDesc
scandesc
;
HeapTuple
tuple
;
CHECK_FOR_INTERRUPTS
();
values
=
(
Datum
*
)
palloc
(
num_phys_attrs
*
sizeof
(
Datum
));
nulls
=
(
bool
*
)
palloc
(
num_phys_attrs
*
sizeof
(
bool
));
MemoryContextReset
(
mycontext
);
scandesc
=
heap_beginscan
(
cstate
->
rel
,
ActiveSnapshot
,
0
,
NULL
);
oldcontext
=
MemoryContextSwitchTo
(
mycontext
);
if
(
cstate
->
binary
)
while
((
tuple
=
heap_getnext
(
scandesc
,
ForwardScanDirection
))
!=
NULL
)
{
{
/* Binary per-tuple header */
CHECK_FOR_INTERRUPTS
();
CopySendInt16
(
cstate
,
attr_count
);
/* Send OID if wanted --- note attr_count doesn't include it */
if
(
cstate
->
oids
)
{
Oid
oid
=
HeapTupleGetOid
(
tuple
);
/* Hack --- assume Oid is same size as int32 */
/* Deconstruct the tuple ... faster than repeated heap_getattr */
CopySendInt32
(
cstate
,
sizeof
(
int32
));
heap_deform_tuple
(
tuple
,
tupDesc
,
values
,
nulls
);
CopySendInt32
(
cstate
,
oid
);
}
/* Format and send the data */
CopyOneRowTo
(
cstate
,
HeapTupleGetOid
(
tuple
),
values
,
nulls
);
}
}
else
heap_endscan
(
scandesc
);
}
else
{
/* run the plan --- the dest receiver will send tuples */
ExecutorRun
(
cstate
->
queryDesc
,
ForwardScanDirection
,
0L
);
}
if
(
cstate
->
binary
)
{
/* Generate trailer for a binary copy */
CopySendInt16
(
cstate
,
-
1
);
/* Need to flush out the trailer */
CopySendEndOfRow
(
cstate
);
}
MemoryContextDelete
(
cstate
->
rowcontext
);
}
/*
* Emit one row during CopyTo().
*/
static
void
CopyOneRowTo
(
CopyState
cstate
,
Oid
tupleOid
,
Datum
*
values
,
bool
*
nulls
)
{
bool
need_delim
=
false
;
FmgrInfo
*
out_functions
=
cstate
->
out_functions
;
MemoryContext
oldcontext
;
ListCell
*
cur
;
char
*
string
;
MemoryContextReset
(
cstate
->
rowcontext
);
oldcontext
=
MemoryContextSwitchTo
(
cstate
->
rowcontext
);
if
(
cstate
->
binary
)
{
/* Binary per-tuple header */
CopySendInt16
(
cstate
,
list_length
(
cstate
->
attnumlist
));
/* Send OID if wanted --- note attnumlist doesn't include it */
if
(
cstate
->
oids
)
{
{
/* Text format has no per-tuple header, but send OID if wanted */
/* Hack --- assume Oid is same size as int32 */
/* Assume digits don't need any quoting or encoding conversion */
CopySendInt32
(
cstate
,
sizeof
(
int32
));
if
(
cstate
->
oids
)
CopySendInt32
(
cstate
,
tupleOid
);
{
string
=
DatumGetCString
(
DirectFunctionCall1
(
oidout
,
ObjectIdGetDatum
(
HeapTupleGetOid
(
tuple
))));
CopySendString
(
cstate
,
string
);
need_delim
=
true
;
}
}
}
}
foreach
(
cur
,
cstate
->
attnumlist
)
else
{
/* Text format has no per-tuple header, but send OID if wanted */
/* Assume digits don't need any quoting or encoding conversion */
if
(
cstate
->
oids
)
{
{
int
attnum
=
lfirst_int
(
cur
);
string
=
DatumGetCString
(
DirectFunctionCall1
(
oidout
,
Datum
value
;
ObjectIdGetDatum
(
tupleOid
)));
bool
isnull
;
CopySendString
(
cstate
,
string
);
need_delim
=
true
;
}
}
foreach
(
cur
,
cstate
->
attnumlist
)
{
int
attnum
=
lfirst_int
(
cur
);
Datum
value
=
values
[
attnum
-
1
];
bool
isnull
=
nulls
[
attnum
-
1
];
value
=
heap_getattr
(
tuple
,
attnum
,
tupDesc
,
&
isnull
);
if
(
!
cstate
->
binary
)
{
if
(
need_delim
)
CopySendChar
(
cstate
,
cstate
->
delim
[
0
]);
need_delim
=
true
;
}
if
(
isnull
)
{
if
(
!
cstate
->
binary
)
CopySendString
(
cstate
,
cstate
->
null_print_client
);
else
CopySendInt32
(
cstate
,
-
1
);
}
else
{
if
(
!
cstate
->
binary
)
if
(
!
cstate
->
binary
)
{
{
if
(
need_delim
)
string
=
OutputFunctionCall
(
&
out_functions
[
attnum
-
1
],
CopySendChar
(
cstate
,
cstate
->
delim
[
0
]);
value
);
need_delim
=
true
;
if
(
cstate
->
csv_mode
)
}
CopyAttributeOutCSV
(
cstate
,
string
,
cstate
->
force_quote_flags
[
attnum
-
1
],
if
(
isnull
)
list_length
(
cstate
->
attnumlist
)
==
1
);
{
if
(
!
cstate
->
binary
)
CopySendString
(
cstate
,
null_print_client
);
else
else
Copy
SendInt32
(
cstate
,
-
1
);
Copy
AttributeOutText
(
cstate
,
string
);
}
}
else
else
{
{
if
(
!
cstate
->
binary
)
bytea
*
outputbytes
;
{
string
=
OutputFunctionCall
(
&
out_functions
[
attnum
-
1
],
value
);
if
(
cstate
->
csv_mode
)
CopyAttributeOutCSV
(
cstate
,
string
,
force_quote
[
attnum
-
1
],
list_length
(
cstate
->
attnumlist
)
==
1
);
else
CopyAttributeOutText
(
cstate
,
string
);
}
else
{
bytea
*
outputbytes
;
outputbytes
=
SendFunctionCall
(
&
out_functions
[
attnum
-
1
],
outputbytes
=
SendFunctionCall
(
&
out_functions
[
attnum
-
1
],
value
);
value
);
CopySendInt32
(
cstate
,
VARSIZE
(
outputbytes
)
-
VARHDRSZ
);
CopySendInt32
(
cstate
,
VARSIZE
(
outputbytes
)
-
VARHDRSZ
);
CopySendData
(
cstate
,
VARDATA
(
outputbytes
),
CopySendData
(
cstate
,
VARDATA
(
outputbytes
),
VARSIZE
(
outputbytes
)
-
VARHDRSZ
);
VARSIZE
(
outputbytes
)
-
VARHDRSZ
);
}
}
}
}
}
CopySendEndOfRow
(
cstate
);
MemoryContextSwitchTo
(
oldcontext
);
cstate
->
processed
++
;
}
heap_endscan
(
scandesc
);
if
(
cstate
->
binary
)
{
/* Generate trailer for a binary copy */
CopySendInt16
(
cstate
,
-
1
);
/* Need to flush out the trailer */
CopySendEndOfRow
(
cstate
);
}
}
MemoryContextDelete
(
mycontext
);
CopySendEndOfRow
(
cstate
);
pfree
(
out_functions
);
MemoryContextSwitchTo
(
oldcontext
);
pfree
(
force_quote
);
cstate
->
processed
++
;
}
}
...
@@ -1528,6 +1622,7 @@ limit_printout_length(const char *str)
...
@@ -1528,6 +1622,7 @@ limit_printout_length(const char *str)
static
void
static
void
CopyFrom
(
CopyState
cstate
)
CopyFrom
(
CopyState
cstate
)
{
{
bool
pipe
=
(
cstate
->
filename
==
NULL
);
HeapTuple
tuple
;
HeapTuple
tuple
;
TupleDesc
tupDesc
;
TupleDesc
tupDesc
;
Form_pg_attribute
*
attr
;
Form_pg_attribute
*
attr
;
...
@@ -1538,7 +1633,6 @@ CopyFrom(CopyState cstate)
...
@@ -1538,7 +1633,6 @@ CopyFrom(CopyState cstate)
FmgrInfo
oid_in_function
;
FmgrInfo
oid_in_function
;
Oid
*
typioparams
;
Oid
*
typioparams
;
Oid
oid_typioparam
;
Oid
oid_typioparam
;
bool
*
force_notnull
;
int
attnum
;
int
attnum
;
int
i
;
int
i
;
Oid
in_func_oid
;
Oid
in_func_oid
;
...
@@ -1558,6 +1652,56 @@ CopyFrom(CopyState cstate)
...
@@ -1558,6 +1652,56 @@ CopyFrom(CopyState cstate)
MemoryContext
oldcontext
=
CurrentMemoryContext
;
MemoryContext
oldcontext
=
CurrentMemoryContext
;
ErrorContextCallback
errcontext
;
ErrorContextCallback
errcontext
;
Assert
(
cstate
->
rel
);
if
(
cstate
->
rel
->
rd_rel
->
relkind
!=
RELKIND_RELATION
)
{
if
(
cstate
->
rel
->
rd_rel
->
relkind
==
RELKIND_VIEW
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_WRONG_OBJECT_TYPE
),
errmsg
(
"cannot copy to view
\"
%s
\"
"
,
RelationGetRelationName
(
cstate
->
rel
))));
else
if
(
cstate
->
rel
->
rd_rel
->
relkind
==
RELKIND_SEQUENCE
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_WRONG_OBJECT_TYPE
),
errmsg
(
"cannot copy to sequence
\"
%s
\"
"
,
RelationGetRelationName
(
cstate
->
rel
))));
else
ereport
(
ERROR
,
(
errcode
(
ERRCODE_WRONG_OBJECT_TYPE
),
errmsg
(
"cannot copy to non-table relation
\"
%s
\"
"
,
RelationGetRelationName
(
cstate
->
rel
))));
}
if
(
pipe
)
{
if
(
whereToSendOutput
==
DestRemote
)
ReceiveCopyBegin
(
cstate
);
else
cstate
->
copy_file
=
stdin
;
}
else
{
struct
stat
st
;
cstate
->
copy_file
=
AllocateFile
(
cstate
->
filename
,
PG_BINARY_R
);
if
(
cstate
->
copy_file
==
NULL
)
ereport
(
ERROR
,
(
errcode_for_file_access
(),
errmsg
(
"could not open file
\"
%s
\"
for reading: %m"
,
cstate
->
filename
)));
fstat
(
fileno
(
cstate
->
copy_file
),
&
st
);
if
(
S_ISDIR
(
st
.
st_mode
))
{
FreeFile
(
cstate
->
copy_file
);
ereport
(
ERROR
,
(
errcode
(
ERRCODE_WRONG_OBJECT_TYPE
),
errmsg
(
"
\"
%s
\"
is a directory"
,
cstate
->
filename
)));
}
}
tupDesc
=
RelationGetDescr
(
cstate
->
rel
);
tupDesc
=
RelationGetDescr
(
cstate
->
rel
);
attr
=
tupDesc
->
attrs
;
attr
=
tupDesc
->
attrs
;
num_phys_attrs
=
tupDesc
->
natts
;
num_phys_attrs
=
tupDesc
->
natts
;
...
@@ -1599,7 +1743,6 @@ CopyFrom(CopyState cstate)
...
@@ -1599,7 +1743,6 @@ CopyFrom(CopyState cstate)
typioparams
=
(
Oid
*
)
palloc
(
num_phys_attrs
*
sizeof
(
Oid
));
typioparams
=
(
Oid
*
)
palloc
(
num_phys_attrs
*
sizeof
(
Oid
));
defmap
=
(
int
*
)
palloc
(
num_phys_attrs
*
sizeof
(
int
));
defmap
=
(
int
*
)
palloc
(
num_phys_attrs
*
sizeof
(
int
));
defexprs
=
(
ExprState
**
)
palloc
(
num_phys_attrs
*
sizeof
(
ExprState
*
));
defexprs
=
(
ExprState
**
)
palloc
(
num_phys_attrs
*
sizeof
(
ExprState
*
));
force_notnull
=
(
bool
*
)
palloc
(
num_phys_attrs
*
sizeof
(
bool
));
for
(
attnum
=
1
;
attnum
<=
num_phys_attrs
;
attnum
++
)
for
(
attnum
=
1
;
attnum
<=
num_phys_attrs
;
attnum
++
)
{
{
...
@@ -1616,11 +1759,6 @@ CopyFrom(CopyState cstate)
...
@@ -1616,11 +1759,6 @@ CopyFrom(CopyState cstate)
&
in_func_oid
,
&
typioparams
[
attnum
-
1
]);
&
in_func_oid
,
&
typioparams
[
attnum
-
1
]);
fmgr_info
(
in_func_oid
,
&
in_functions
[
attnum
-
1
]);
fmgr_info
(
in_func_oid
,
&
in_functions
[
attnum
-
1
]);
if
(
list_member_int
(
cstate
->
force_notnull_atts
,
attnum
))
force_notnull
[
attnum
-
1
]
=
true
;
else
force_notnull
[
attnum
-
1
]
=
false
;
/* Get default info if needed */
/* Get default info if needed */
if
(
!
list_member_int
(
cstate
->
attnumlist
,
attnum
))
if
(
!
list_member_int
(
cstate
->
attnumlist
,
attnum
))
{
{
...
@@ -1810,7 +1948,8 @@ CopyFrom(CopyState cstate)
...
@@ -1810,7 +1948,8 @@ CopyFrom(CopyState cstate)
NameStr
(
attr
[
m
]
->
attname
))));
NameStr
(
attr
[
m
]
->
attname
))));
string
=
field_strings
[
fieldno
++
];
string
=
field_strings
[
fieldno
++
];
if
(
cstate
->
csv_mode
&&
string
==
NULL
&&
force_notnull
[
m
])
if
(
cstate
->
csv_mode
&&
string
==
NULL
&&
cstate
->
force_notnull_flags
[
m
])
{
{
/* Go ahead and read the NULL string */
/* Go ahead and read the NULL string */
string
=
cstate
->
null_print
;
string
=
cstate
->
null_print
;
...
@@ -1972,13 +2111,21 @@ CopyFrom(CopyState cstate)
...
@@ -1972,13 +2111,21 @@ CopyFrom(CopyState cstate)
pfree
(
typioparams
);
pfree
(
typioparams
);
pfree
(
defmap
);
pfree
(
defmap
);
pfree
(
defexprs
);
pfree
(
defexprs
);
pfree
(
force_notnull
);
ExecDropSingleTupleTableSlot
(
slot
);
ExecDropSingleTupleTableSlot
(
slot
);
ExecCloseIndices
(
resultRelInfo
);
ExecCloseIndices
(
resultRelInfo
);
FreeExecutorState
(
estate
);
FreeExecutorState
(
estate
);
if
(
!
pipe
)
{
if
(
FreeFile
(
cstate
->
copy_file
))
ereport
(
ERROR
,
(
errcode_for_file_access
(),
errmsg
(
"could not read from file
\"
%s
\"
: %m"
,
cstate
->
filename
)));
}
}
}
...
@@ -3055,16 +3202,17 @@ CopyAttributeOutCSV(CopyState cstate, char *string,
...
@@ -3055,16 +3202,17 @@ CopyAttributeOutCSV(CopyState cstate, char *string,
* The input attnamelist is either the user-specified column list,
* The input attnamelist is either the user-specified column list,
* or NIL if there was none (in which case we want all the non-dropped
* or NIL if there was none (in which case we want all the non-dropped
* columns).
* columns).
*
* rel can be NULL ... it's only used for error reports.
*/
*/
static
List
*
static
List
*
CopyGetAttnums
(
Relation
rel
,
List
*
attnamelist
)
CopyGetAttnums
(
TupleDesc
tupDesc
,
Relation
rel
,
List
*
attnamelist
)
{
{
List
*
attnums
=
NIL
;
List
*
attnums
=
NIL
;
if
(
attnamelist
==
NIL
)
if
(
attnamelist
==
NIL
)
{
{
/* Generate default column list */
/* Generate default column list */
TupleDesc
tupDesc
=
RelationGetDescr
(
rel
);
Form_pg_attribute
*
attr
=
tupDesc
->
attrs
;
Form_pg_attribute
*
attr
=
tupDesc
->
attrs
;
int
attr_count
=
tupDesc
->
natts
;
int
attr_count
=
tupDesc
->
natts
;
int
i
;
int
i
;
...
@@ -3085,15 +3233,33 @@ CopyGetAttnums(Relation rel, List *attnamelist)
...
@@ -3085,15 +3233,33 @@ CopyGetAttnums(Relation rel, List *attnamelist)
{
{
char
*
name
=
strVal
(
lfirst
(
l
));
char
*
name
=
strVal
(
lfirst
(
l
));
int
attnum
;
int
attnum
;
int
i
;
/* Lookup column name */
/* Lookup column name */
/* Note we disallow system columns here */
attnum
=
InvalidAttrNumber
;
attnum
=
attnameAttNum
(
rel
,
name
,
false
);
for
(
i
=
0
;
i
<
tupDesc
->
natts
;
i
++
)
{
if
(
tupDesc
->
attrs
[
i
]
->
attisdropped
)
continue
;
if
(
namestrcmp
(
&
(
tupDesc
->
attrs
[
i
]
->
attname
),
name
)
==
0
)
{
attnum
=
tupDesc
->
attrs
[
i
]
->
attnum
;
break
;
}
}
if
(
attnum
==
InvalidAttrNumber
)
if
(
attnum
==
InvalidAttrNumber
)
ereport
(
ERROR
,
{
if
(
rel
!=
NULL
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_UNDEFINED_COLUMN
),
(
errcode
(
ERRCODE_UNDEFINED_COLUMN
),
errmsg
(
"column
\"
%s
\"
of relation
\"
%s
\"
does not exist"
,
errmsg
(
"column
\"
%s
\"
of relation
\"
%s
\"
does not exist"
,
name
,
RelationGetRelationName
(
rel
))));
name
,
RelationGetRelationName
(
rel
))));
else
ereport
(
ERROR
,
(
errcode
(
ERRCODE_UNDEFINED_COLUMN
),
errmsg
(
"column
\"
%s
\"
does not exist"
,
name
)));
}
/* Check for duplicates */
/* Check for duplicates */
if
(
list_member_int
(
attnums
,
attnum
))
if
(
list_member_int
(
attnums
,
attnum
))
ereport
(
ERROR
,
ereport
(
ERROR
,
...
@@ -3106,3 +3272,66 @@ CopyGetAttnums(Relation rel, List *attnamelist)
...
@@ -3106,3 +3272,66 @@ CopyGetAttnums(Relation rel, List *attnamelist)
return
attnums
;
return
attnums
;
}
}
/*
* copy_dest_startup --- executor startup
*/
static
void
copy_dest_startup
(
DestReceiver
*
self
,
int
operation
,
TupleDesc
typeinfo
)
{
/* no-op */
}
/*
* copy_dest_receive --- receive one tuple
*/
static
void
copy_dest_receive
(
TupleTableSlot
*
slot
,
DestReceiver
*
self
)
{
DR_copy
*
myState
=
(
DR_copy
*
)
self
;
CopyState
cstate
=
myState
->
cstate
;
/* Make sure the tuple is fully deconstructed */
slot_getallattrs
(
slot
);
/* And send the data */
CopyOneRowTo
(
cstate
,
InvalidOid
,
slot
->
tts_values
,
slot
->
tts_isnull
);
}
/*
* copy_dest_shutdown --- executor end
*/
static
void
copy_dest_shutdown
(
DestReceiver
*
self
)
{
/* no-op */
}
/*
* copy_dest_destroy --- release DestReceiver object
*/
static
void
copy_dest_destroy
(
DestReceiver
*
self
)
{
pfree
(
self
);
}
/*
* CreateCopyDestReceiver -- create a suitable DestReceiver object
*/
DestReceiver
*
CreateCopyDestReceiver
(
void
)
{
DR_copy
*
self
=
(
DR_copy
*
)
palloc
(
sizeof
(
DR_copy
));
self
->
pub
.
receiveSlot
=
copy_dest_receive
;
self
->
pub
.
rStartup
=
copy_dest_startup
;
self
->
pub
.
rShutdown
=
copy_dest_shutdown
;
self
->
pub
.
rDestroy
=
copy_dest_destroy
;
self
->
pub
.
mydest
=
DestCopyOut
;
self
->
cstate
=
NULL
;
/* will be set later */
return
(
DestReceiver
*
)
self
;
}
src/backend/nodes/copyfuncs.c
View file @
85188ab8
...
@@ -15,7 +15,7 @@
...
@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.3
49 2006/08/25 04:06:49
tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.3
50 2006/08/30 23:34:21
tgl Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -1934,6 +1934,7 @@ _copyCopyStmt(CopyStmt *from)
...
@@ -1934,6 +1934,7 @@ _copyCopyStmt(CopyStmt *from)
CopyStmt
*
newnode
=
makeNode
(
CopyStmt
);
CopyStmt
*
newnode
=
makeNode
(
CopyStmt
);
COPY_NODE_FIELD
(
relation
);
COPY_NODE_FIELD
(
relation
);
COPY_NODE_FIELD
(
query
);
COPY_NODE_FIELD
(
attlist
);
COPY_NODE_FIELD
(
attlist
);
COPY_SCALAR_FIELD
(
is_from
);
COPY_SCALAR_FIELD
(
is_from
);
COPY_STRING_FIELD
(
filename
);
COPY_STRING_FIELD
(
filename
);
...
...
src/backend/nodes/equalfuncs.c
View file @
85188ab8
...
@@ -18,7 +18,7 @@
...
@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.28
3 2006/08/25 04:06:49
tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.28
4 2006/08/30 23:34:21
tgl Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -863,6 +863,7 @@ static bool
...
@@ -863,6 +863,7 @@ static bool
_equalCopyStmt
(
CopyStmt
*
a
,
CopyStmt
*
b
)
_equalCopyStmt
(
CopyStmt
*
a
,
CopyStmt
*
b
)
{
{
COMPARE_NODE_FIELD
(
relation
);
COMPARE_NODE_FIELD
(
relation
);
COMPARE_NODE_FIELD
(
query
);
COMPARE_NODE_FIELD
(
attlist
);
COMPARE_NODE_FIELD
(
attlist
);
COMPARE_SCALAR_FIELD
(
is_from
);
COMPARE_SCALAR_FIELD
(
is_from
);
COMPARE_STRING_FIELD
(
filename
);
COMPARE_STRING_FIELD
(
filename
);
...
...
src/backend/parser/analyze.c
View file @
85188ab8
...
@@ -6,7 +6,7 @@
...
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.34
8 2006/08/25 04:06:5
1 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.34
9 2006/08/30 23:34:2
1 tgl Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -341,6 +341,19 @@ transformStmt(ParseState *pstate, Node *parseTree,
...
@@ -341,6 +341,19 @@ transformStmt(ParseState *pstate, Node *parseTree,
}
}
break
;
break
;
case
T_CopyStmt
:
{
CopyStmt
*
n
=
(
CopyStmt
*
)
parseTree
;
result
=
makeNode
(
Query
);
result
->
commandType
=
CMD_UTILITY
;
if
(
n
->
query
)
n
->
query
=
transformStmt
(
pstate
,
(
Node
*
)
n
->
query
,
extras_before
,
extras_after
);
result
->
utilityStmt
=
(
Node
*
)
parseTree
;
}
break
;
case
T_AlterTableStmt
:
case
T_AlterTableStmt
:
result
=
transformAlterTableStmt
(
pstate
,
result
=
transformAlterTableStmt
(
pstate
,
(
AlterTableStmt
*
)
parseTree
,
(
AlterTableStmt
*
)
parseTree
,
...
...
src/backend/parser/gram.y
View file @
85188ab8
...
@@ -11,7 +11,7 @@
...
@@ -11,7 +11,7 @@
*
*
*
*
* IDENTIFICATION
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.55
8 2006/08/25 04:06:5
1 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.55
9 2006/08/30 23:34:2
1 tgl Exp $
*
*
* HISTORY
* HISTORY
* AUTHOR DATE MAJOR EVENT
* AUTHOR DATE MAJOR EVENT
...
@@ -1614,11 +1614,15 @@ ClosePortalStmt:
...
@@ -1614,11 +1614,15 @@ ClosePortalStmt:
/*****************************************************************************
/*****************************************************************************
*
*
* QUERY :
* QUERY :
* COPY
<relname> ['(' columnList ')'] FROM/TO
[WITH options]
* COPY
relname ['(' columnList ')'] FROM/TO file
[WITH options]
*
*
* BINARY, OIDS, and DELIMITERS kept in old locations
* BINARY, OIDS, and DELIMITERS kept in old locations
* for backward compatibility. 2002-06-18
* for backward compatibility. 2002-06-18
*
*
* COPY ( SELECT ... ) TO file [WITH options]
* This form doesn't have the backwards-compatible option
* syntax.
*
*****************************************************************************/
*****************************************************************************/
CopyStmt: COPY opt_binary qualified_name opt_column_list opt_oids
CopyStmt: COPY opt_binary qualified_name opt_column_list opt_oids
...
@@ -1626,6 +1630,7 @@ CopyStmt: COPY opt_binary qualified_name opt_column_list opt_oids
...
@@ -1626,6 +1630,7 @@ CopyStmt: COPY opt_binary qualified_name opt_column_list opt_oids
{
{
CopyStmt *n = makeNode(CopyStmt);
CopyStmt *n = makeNode(CopyStmt);
n->relation = $3;
n->relation = $3;
n->query = NULL;
n->attlist = $4;
n->attlist = $4;
n->is_from = $6;
n->is_from = $6;
n->filename = $7;
n->filename = $7;
...
@@ -1642,6 +1647,18 @@ CopyStmt: COPY opt_binary qualified_name opt_column_list opt_oids
...
@@ -1642,6 +1647,18 @@ CopyStmt: COPY opt_binary qualified_name opt_column_list opt_oids
n->options = list_concat(n->options, $10);
n->options = list_concat(n->options, $10);
$$ = (Node *)n;
$$ = (Node *)n;
}
}
| COPY select_with_parens TO copy_file_name opt_with
copy_opt_list
{
CopyStmt *n = makeNode(CopyStmt);
n->relation = NULL;
n->query = (Query *) $2;
n->attlist = NIL;
n->is_from = false;
n->filename = $4;
n->options = $6;
$$ = (Node *)n;
}
;
;
copy_from:
copy_from:
...
@@ -1652,7 +1669,7 @@ copy_from:
...
@@ -1652,7 +1669,7 @@ copy_from:
/*
/*
* copy_file_name NULL indicates stdio is used. Whether stdin or stdout is
* copy_file_name NULL indicates stdio is used. Whether stdin or stdout is
* used depends on the direction. (It really doesn't make sense to copy from
* used depends on the direction. (It really doesn't make sense to copy from
* stdout. We silently correct the "typo". - AY 9/94
* stdout. We silently correct the "typo".
)
- AY 9/94
*/
*/
copy_file_name:
copy_file_name:
Sconst { $$ = $1; }
Sconst { $$ = $1; }
...
...
src/backend/tcop/dest.c
View file @
85188ab8
...
@@ -8,7 +8,7 @@
...
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/dest.c,v 1.
69 2006/08/12 02:52:05
tgl Exp $
* $PostgreSQL: pgsql/src/backend/tcop/dest.c,v 1.
70 2006/08/30 23:34:21
tgl Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -30,6 +30,7 @@
...
@@ -30,6 +30,7 @@
#include "access/printtup.h"
#include "access/printtup.h"
#include "access/xact.h"
#include "access/xact.h"
#include "commands/copy.h"
#include "executor/executor.h"
#include "executor/executor.h"
#include "executor/tstoreReceiver.h"
#include "executor/tstoreReceiver.h"
#include "libpq/libpq.h"
#include "libpq/libpq.h"
...
@@ -128,6 +129,9 @@ CreateDestReceiver(CommandDest dest, Portal portal)
...
@@ -128,6 +129,9 @@ CreateDestReceiver(CommandDest dest, Portal portal)
case
DestIntoRel
:
case
DestIntoRel
:
return
CreateIntoRelDestReceiver
();
return
CreateIntoRelDestReceiver
();
case
DestCopyOut
:
return
CreateCopyDestReceiver
();
}
}
/* should never get here */
/* should never get here */
...
@@ -153,6 +157,7 @@ EndCommand(const char *commandTag, CommandDest dest)
...
@@ -153,6 +157,7 @@ EndCommand(const char *commandTag, CommandDest dest)
case
DestSPI
:
case
DestSPI
:
case
DestTuplestore
:
case
DestTuplestore
:
case
DestIntoRel
:
case
DestIntoRel
:
case
DestCopyOut
:
break
;
break
;
}
}
}
}
...
@@ -192,6 +197,7 @@ NullCommand(CommandDest dest)
...
@@ -192,6 +197,7 @@ NullCommand(CommandDest dest)
case
DestSPI
:
case
DestSPI
:
case
DestTuplestore
:
case
DestTuplestore
:
case
DestIntoRel
:
case
DestIntoRel
:
case
DestCopyOut
:
break
;
break
;
}
}
}
}
...
@@ -233,6 +239,7 @@ ReadyForQuery(CommandDest dest)
...
@@ -233,6 +239,7 @@ ReadyForQuery(CommandDest dest)
case
DestSPI
:
case
DestSPI
:
case
DestTuplestore
:
case
DestTuplestore
:
case
DestIntoRel
:
case
DestIntoRel
:
case
DestCopyOut
:
break
;
break
;
}
}
}
}
src/bin/psql/copy.c
View file @
85188ab8
...
@@ -3,7 +3,7 @@
...
@@ -3,7 +3,7 @@
*
*
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
*
*
* $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.6
7 2006/08/29 15:19:50
tgl Exp $
* $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.6
8 2006/08/30 23:34:22
tgl Exp $
*/
*/
#include "postgres_fe.h"
#include "postgres_fe.h"
#include "copy.h"
#include "copy.h"
...
@@ -39,6 +39,9 @@
...
@@ -39,6 +39,9 @@
* \copy tablename [(columnlist)] from|to filename
* \copy tablename [(columnlist)] from|to filename
* [ with ] [ binary ] [ oids ] [ delimiter [as] char ] [ null [as] string ]
* [ with ] [ binary ] [ oids ] [ delimiter [as] char ] [ null [as] string ]
*
*
* \copy ( select stmt ) to filename
* [ with ] [ binary ] [ delimiter [as] char ] [ null [as] string ]
*
* The pre-7.3 syntax was:
* The pre-7.3 syntax was:
* \copy [ binary ] tablename [(columnlist)] [with oids] from|to filename
* \copy [ binary ] tablename [(columnlist)] [with oids] from|to filename
* [ [using] delimiters char ] [ with null as string ]
* [ [using] delimiters char ] [ with null as string ]
...
@@ -142,6 +145,26 @@ parse_slash_copy(const char *args)
...
@@ -142,6 +145,26 @@ parse_slash_copy(const char *args)
result
->
table
=
pg_strdup
(
token
);
result
->
table
=
pg_strdup
(
token
);
/* Handle COPY (SELECT) case */
if
(
token
[
0
]
==
'('
)
{
int
parens
=
1
;
while
(
parens
>
0
)
{
token
=
strtokx
(
NULL
,
whitespace
,
".,()"
,
"
\"
'"
,
nonstd_backslash
,
true
,
false
,
pset
.
encoding
);
if
(
!
token
)
goto
error
;
if
(
token
[
0
]
==
'('
)
parens
++
;
else
if
(
token
[
0
]
==
')'
)
parens
--
;
xstrcat
(
&
result
->
table
,
" "
);
xstrcat
(
&
result
->
table
,
token
);
}
}
token
=
strtokx
(
NULL
,
whitespace
,
".,()"
,
"
\"
"
,
token
=
strtokx
(
NULL
,
whitespace
,
".,()"
,
"
\"
"
,
0
,
false
,
false
,
pset
.
encoding
);
0
,
false
,
false
,
pset
.
encoding
);
if
(
!
token
)
if
(
!
token
)
...
...
src/include/commands/copy.h
View file @
85188ab8
...
@@ -7,7 +7,7 @@
...
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* $PostgreSQL: pgsql/src/include/commands/copy.h,v 1.2
7 2006/03/05 15:58:55 momjian
Exp $
* $PostgreSQL: pgsql/src/include/commands/copy.h,v 1.2
8 2006/08/30 23:34:22 tgl
Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -15,8 +15,11 @@
...
@@ -15,8 +15,11 @@
#define COPY_H
#define COPY_H
#include "nodes/parsenodes.h"
#include "nodes/parsenodes.h"
#include "tcop/dest.h"
extern
uint64
DoCopy
(
const
CopyStmt
*
stmt
);
extern
uint64
DoCopy
(
const
CopyStmt
*
stmt
);
extern
DestReceiver
*
CreateCopyDestReceiver
(
void
);
#endif
/* COPY_H */
#endif
/* COPY_H */
src/include/nodes/parsenodes.h
View file @
85188ab8
...
@@ -7,7 +7,7 @@
...
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.32
5 2006/08/25 04:06:56
tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.32
6 2006/08/30 23:34:22
tgl Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -1012,16 +1012,22 @@ typedef struct GrantRoleStmt
...
@@ -1012,16 +1012,22 @@ typedef struct GrantRoleStmt
/* ----------------------
/* ----------------------
* Copy Statement
* Copy Statement
*
* We support "COPY relation FROM file", "COPY relation TO file", and
* "COPY (query) TO file". In any given CopyStmt, exactly one of "relation"
* and "query" must be non-NULL. Note: "query" is a SelectStmt before
* parse analysis, and a Query afterwards.
* ----------------------
* ----------------------
*/
*/
typedef
struct
CopyStmt
typedef
struct
CopyStmt
{
{
NodeTag
type
;
NodeTag
type
;
RangeVar
*
relation
;
/* the relation to copy */
RangeVar
*
relation
;
/* the relation to copy */
Query
*
query
;
/* the query to copy */
List
*
attlist
;
/* List of column names (as Strings), or NIL
List
*
attlist
;
/* List of column names (as Strings), or NIL
* for all columns */
* for all columns */
bool
is_from
;
/* TO or FROM */
bool
is_from
;
/* TO or FROM */
char
*
filename
;
/*
if NULL, use stdin/stdout
*/
char
*
filename
;
/*
filename, or NULL for STDIN/STDOUT
*/
List
*
options
;
/* List of DefElem nodes */
List
*
options
;
/* List of DefElem nodes */
}
CopyStmt
;
}
CopyStmt
;
...
...
src/include/tcop/dest.h
View file @
85188ab8
...
@@ -54,7 +54,7 @@
...
@@ -54,7 +54,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* $PostgreSQL: pgsql/src/include/tcop/dest.h,v 1.5
1 2006/08/12 02:52:06
tgl Exp $
* $PostgreSQL: pgsql/src/include/tcop/dest.h,v 1.5
2 2006/08/30 23:34:22
tgl Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -85,7 +85,8 @@ typedef enum
...
@@ -85,7 +85,8 @@ typedef enum
DestRemoteExecute
,
/* sent to frontend, in Execute command */
DestRemoteExecute
,
/* sent to frontend, in Execute command */
DestSPI
,
/* results sent to SPI manager */
DestSPI
,
/* results sent to SPI manager */
DestTuplestore
,
/* results sent to Tuplestore */
DestTuplestore
,
/* results sent to Tuplestore */
DestIntoRel
/* results sent to relation (SELECT INTO) */
DestIntoRel
,
/* results sent to relation (SELECT INTO) */
DestCopyOut
/* results sent to COPY TO code */
}
CommandDest
;
}
CommandDest
;
/* ----------------
/* ----------------
...
...
src/test/regress/expected/copyselect.out
0 → 100644
View file @
85188ab8
--
-- Test cases for COPY (select) TO
--
create table test1 (id serial, t text);
NOTICE: CREATE TABLE will create implicit sequence "test1_id_seq" for serial column "test1.id"
insert into test1 (t) values ('a');
insert into test1 (t) values ('b');
insert into test1 (t) values ('c');
insert into test1 (t) values ('d');
insert into test1 (t) values ('e');
create table test2 (id serial, t text);
NOTICE: CREATE TABLE will create implicit sequence "test2_id_seq" for serial column "test2.id"
insert into test2 (t) values ('A');
insert into test2 (t) values ('B');
insert into test2 (t) values ('C');
insert into test2 (t) values ('D');
insert into test2 (t) values ('E');
create view v_test1
as select 'v_'||t from test1;
--
-- Test COPY table TO
--
copy test1 to stdout;
1 a
2 b
3 c
4 d
5 e
--
-- This should fail
--
copy v_test1 to stdout;
ERROR: cannot copy from view "v_test1"
HINT: Try the COPY (SELECT ...) TO variant.
--
-- Test COPY (select) TO
--
copy (select t from test1 where id=1) to stdout;
a
--
-- Test COPY (select for update) TO
--
copy (select t from test1 where id=3 for update) to stdout;
c
--
-- This should fail
--
copy (select t into temp test3 from test1 where id=3) to stdout;
ERROR: COPY (SELECT INTO) is not supported
--
-- This should fail
--
copy (select * from test1) from stdin;
ERROR: syntax error at or near "from"
LINE 1: copy (select * from test1) from stdin;
^
--
-- This should fail
--
copy (select * from test1) (t,id) to stdout;
ERROR: syntax error at or near "("
LINE 1: copy (select * from test1) (t,id) to stdout;
^
--
-- Test JOIN
--
copy (select * from test1 join test2 using (id)) to stdout;
1 a A
2 b B
3 c C
4 d D
5 e E
--
-- Test UNION SELECT
--
copy (select t from test1 where id = 1 UNION select * from v_test1) to stdout;
a
v_a
v_b
v_c
v_d
v_e
--
-- Test subselect
--
copy (select * from (select t from test1 where id = 1 UNION select * from v_test1) t1) to stdout;
a
v_a
v_b
v_c
v_d
v_e
--
-- Test headers, CSV and quotes
--
copy (select t from test1 where id = 1) to stdout csv header force quote t;
t
"a"
--
-- Test psql builtins, plain table
--
\copy test1 to stdout
1 a
2 b
3 c
4 d
5 e
--
-- This should fail
--
\copy v_test1 to stdout
ERROR: cannot copy from view "v_test1"
HINT: Try the COPY (SELECT ...) TO variant.
\copy: ERROR: cannot copy from view "v_test1"
HINT: Try the COPY (SELECT ...) TO variant.
--
-- Test \copy (select ...)
--
\copy (select "id",'id','id""'||t,(id + 1)*id,t,"test1"."t" from test1 where id=3) to stdout
3 id id""c 12 c c
--
-- Drop everything
--
drop table test2;
drop view v_test1;
drop table test1;
src/test/regress/parallel_schedule
View file @
85188ab8
# ----------
# ----------
# The first group of parallel test
# The first group of parallel test
# $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.3
4 2006/08/12 02:52:06
tgl Exp $
# $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.3
5 2006/08/30 23:34:22
tgl Exp $
# ----------
# ----------
test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric
test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric
...
@@ -34,7 +34,7 @@ test: create_function_2
...
@@ -34,7 +34,7 @@ test: create_function_2
# execute two copy tests parallel, to check that copy itself
# execute two copy tests parallel, to check that copy itself
# is concurrent safe.
# is concurrent safe.
# ----------
# ----------
test: copy
test: copy
copyselect
# ----------
# ----------
# The third group of parallel test
# The third group of parallel test
...
...
src/test/regress/serial_schedule
View file @
85188ab8
# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.3
2 2006/08/12 02:52:06
tgl Exp $
# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.3
3 2006/08/30 23:34:22
tgl Exp $
# This should probably be in an order similar to parallel_schedule.
# This should probably be in an order similar to parallel_schedule.
test: boolean
test: boolean
test: char
test: char
...
@@ -43,6 +43,7 @@ test: create_type
...
@@ -43,6 +43,7 @@ test: create_type
test: create_table
test: create_table
test: create_function_2
test: create_function_2
test: copy
test: copy
test: copyselect
test: constraints
test: constraints
test: triggers
test: triggers
test: create_misc
test: create_misc
...
...
src/test/regress/sql/copyselect.sql
0 → 100644
View file @
85188ab8
--
-- Test cases for COPY (select) TO
--
create
table
test1
(
id
serial
,
t
text
);
insert
into
test1
(
t
)
values
(
'a'
);
insert
into
test1
(
t
)
values
(
'b'
);
insert
into
test1
(
t
)
values
(
'c'
);
insert
into
test1
(
t
)
values
(
'd'
);
insert
into
test1
(
t
)
values
(
'e'
);
create
table
test2
(
id
serial
,
t
text
);
insert
into
test2
(
t
)
values
(
'A'
);
insert
into
test2
(
t
)
values
(
'B'
);
insert
into
test2
(
t
)
values
(
'C'
);
insert
into
test2
(
t
)
values
(
'D'
);
insert
into
test2
(
t
)
values
(
'E'
);
create
view
v_test1
as
select
'v_'
||
t
from
test1
;
--
-- Test COPY table TO
--
copy
test1
to
stdout
;
--
-- This should fail
--
copy
v_test1
to
stdout
;
--
-- Test COPY (select) TO
--
copy
(
select
t
from
test1
where
id
=
1
)
to
stdout
;
--
-- Test COPY (select for update) TO
--
copy
(
select
t
from
test1
where
id
=
3
for
update
)
to
stdout
;
--
-- This should fail
--
copy
(
select
t
into
temp
test3
from
test1
where
id
=
3
)
to
stdout
;
--
-- This should fail
--
copy
(
select
*
from
test1
)
from
stdin
;
--
-- This should fail
--
copy
(
select
*
from
test1
)
(
t
,
id
)
to
stdout
;
--
-- Test JOIN
--
copy
(
select
*
from
test1
join
test2
using
(
id
))
to
stdout
;
--
-- Test UNION SELECT
--
copy
(
select
t
from
test1
where
id
=
1
UNION
select
*
from
v_test1
)
to
stdout
;
--
-- Test subselect
--
copy
(
select
*
from
(
select
t
from
test1
where
id
=
1
UNION
select
*
from
v_test1
)
t1
)
to
stdout
;
--
-- Test headers, CSV and quotes
--
copy
(
select
t
from
test1
where
id
=
1
)
to
stdout
csv
header
force
quote
t
;
--
-- Test psql builtins, plain table
--
\
copy
test1
to
stdout
--
-- This should fail
--
\
copy
v_test1
to
stdout
--
-- Test \copy (select ...)
--
\
copy
(
select
"id"
,
'id'
,
'id""'
||
t
,(
id
+
1
)
*
id
,
t
,
"test1"
.
"t"
from
test1
where
id
=
3
)
to
stdout
--
-- Drop everything
--
drop
table
test2
;
drop
view
v_test1
;
drop
table
test1
;
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment