mirror of
https://github.com/postgres/postgres.git
synced 2025-04-24 10:47:04 +03:00
Extend COPY to support COPY (SELECT ...) TO ...
Bernd Helmle
This commit is contained in:
parent
0d5065781d
commit
85188ab883
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$PostgreSQL: pgsql/doc/src/sgml/ref/copy.sgml,v 1.74 2006/04/22 03:03:11 momjian Exp $
|
$PostgreSQL: pgsql/doc/src/sgml/ref/copy.sgml,v 1.75 2006/08/30 23:34:20 tgl Exp $
|
||||||
PostgreSQL documentation
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -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
|
|||||||
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
|
|||||||
<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
|
|||||||
<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
|
|||||||
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>
|
|||||||
|
|
||||||
<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>
|
|||||||
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';
|
|||||||
</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>
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.167 2006/08/29 22:25:04 tgl Exp $
|
$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.168 2006/08/30 23:34:21 tgl Exp $
|
||||||
PostgreSQL documentation
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -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=>
|
|||||||
</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
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.268 2006/07/14 14:52:18 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.269 2006/08/30 23:34:21 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -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
|
|||||||
|
|
||||||
/* 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
|
|||||||
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
|
|||||||
|
|
||||||
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";
|
|||||||
/* 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,
|
|||||||
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)
|
|||||||
* 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
|
|||||||
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)
|
|||||||
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,10 +941,31 @@ 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")));
|
||||||
|
|
||||||
|
/* Disallow file COPY except to superusers. */
|
||||||
|
if (!pipe && !superuser())
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
|
errmsg("must be superuser to COPY to or from a file"),
|
||||||
|
errhint("Anyone can COPY to stdout or from stdin. "
|
||||||
|
"psql's \\copy command also works for anyone.")));
|
||||||
|
|
||||||
|
if (stmt->relation)
|
||||||
|
{
|
||||||
|
Assert(!stmt->query);
|
||||||
|
cstate->queryDesc = NULL;
|
||||||
|
|
||||||
/* Open and lock the relation, using the appropriate lock type. */
|
/* Open and lock the relation, using the appropriate lock type. */
|
||||||
cstate->rel = heap_openrv(relation,
|
cstate->rel = heap_openrv(stmt->relation,
|
||||||
(is_from ? RowExclusiveLock : AccessShareLock));
|
(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 */
|
/* check read-only transaction */
|
||||||
if (XactReadOnly && is_from &&
|
if (XactReadOnly && is_from &&
|
||||||
!isTempNamespace(RelationGetNamespace(cstate->rel)))
|
!isTempNamespace(RelationGetNamespace(cstate->rel)))
|
||||||
@ -931,19 +973,6 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
|
(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
|
||||||
errmsg("transaction is read-only")));
|
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())
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
||||||
errmsg("must be superuser to COPY to or from a file"),
|
|
||||||
errhint("Anyone can COPY to stdout or from stdin. "
|
|
||||||
"psql's \\copy command also works for anyone.")));
|
|
||||||
|
|
||||||
/* Don't allow COPY w/ OIDs to or from a table without them */
|
/* Don't allow COPY w/ OIDs to or from a table without them */
|
||||||
if (cstate->oids && !cstate->rel->rd_rel->relhasoids)
|
if (cstate->oids && !cstate->rel->rd_rel->relhasoids)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
@ -951,19 +980,106 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
errmsg("table \"%s\" does not have OIDs",
|
errmsg("table \"%s\" does not have OIDs",
|
||||||
RelationGetRelationName(cstate->rel))));
|
RelationGetRelationName(cstate->rel))));
|
||||||
|
|
||||||
/* Generate or convert list of attributes to process */
|
tupDesc = RelationGetDescr(cstate->rel);
|
||||||
cstate->attnumlist = CopyGetAttnums(cstate->rel, attnamelist);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Query *query = stmt->query;
|
||||||
|
List *rewritten;
|
||||||
|
Plan *plan;
|
||||||
|
DestReceiver *dest;
|
||||||
|
|
||||||
/* Convert FORCE QUOTE name list to column numbers, check validity */
|
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 */
|
||||||
|
cstate->attnumlist = CopyGetAttnums(tupDesc, cstate->rel, attnamelist);
|
||||||
|
|
||||||
|
num_phys_attrs = tupDesc->natts;
|
||||||
|
|
||||||
|
/* 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_atts)
|
foreach(cur, attnums)
|
||||||
{
|
{
|
||||||
int attnum = lfirst_int(cur);
|
int attnum = lfirst_int(cur);
|
||||||
|
|
||||||
@ -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_atts)
|
foreach(cur, attnums)
|
||||||
{
|
{
|
||||||
int attnum = lfirst_int(cur);
|
int attnum = lfirst_int(cur);
|
||||||
|
|
||||||
@ -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)
|
|||||||
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 */
|
|
||||||
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(filename, PG_BINARY_R);
|
|
||||||
|
|
||||||
if (cstate->copy_file == NULL)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode_for_file_access(),
|
|
||||||
errmsg("could not open file \"%s\" for reading: %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)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CopyFrom(cstate);
|
CopyFrom(cstate);
|
||||||
}
|
else /* copy from database to file */
|
||||||
|
DoCopyTo(cstate);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
else
|
||||||
{ /* copy from database to file */
|
{
|
||||||
|
/* Close down the query and free resources. */
|
||||||
|
ExecutorEnd(cstate->queryDesc);
|
||||||
|
FreeQueryDesc(cstate->queryDesc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clean up storage (probably not really necessary) */
|
||||||
|
processed = cstate->processed;
|
||||||
|
|
||||||
|
pfree(cstate->attribute_buf.data);
|
||||||
|
pfree(cstate->line_buf.data);
|
||||||
|
pfree(cstate->raw_buf);
|
||||||
|
pfree(cstate);
|
||||||
|
|
||||||
|
return processed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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,6 +1199,8 @@ 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 (pipe)
|
||||||
{
|
{
|
||||||
if (whereToSendOutput == DestRemote)
|
if (whereToSendOutput == DestRemote)
|
||||||
@ -1106,20 +1217,20 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
* Prevent write to relative path ... too easy to shoot oneself in
|
* Prevent write to relative path ... too easy to shoot oneself in
|
||||||
* the foot by overwriting a database file ...
|
* the foot by overwriting a database file ...
|
||||||
*/
|
*/
|
||||||
if (!is_absolute_path(filename))
|
if (!is_absolute_path(cstate->filename))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_NAME),
|
(errcode(ERRCODE_INVALID_NAME),
|
||||||
errmsg("relative path not allowed for COPY to file")));
|
errmsg("relative path not allowed for COPY to file")));
|
||||||
|
|
||||||
oumask = umask((mode_t) 022);
|
oumask = umask((mode_t) 022);
|
||||||
cstate->copy_file = AllocateFile(filename, PG_BINARY_W);
|
cstate->copy_file = AllocateFile(cstate->filename, PG_BINARY_W);
|
||||||
umask(oumask);
|
umask(oumask);
|
||||||
|
|
||||||
if (cstate->copy_file == NULL)
|
if (cstate->copy_file == NULL)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode_for_file_access(),
|
(errcode_for_file_access(),
|
||||||
errmsg("could not open file \"%s\" for writing: %m",
|
errmsg("could not open file \"%s\" for writing: %m",
|
||||||
filename)));
|
cstate->filename)));
|
||||||
|
|
||||||
fstat(fileno(cstate->copy_file), &st);
|
fstat(fileno(cstate->copy_file), &st);
|
||||||
if (S_ISDIR(st.st_mode))
|
if (S_ISDIR(st.st_mode))
|
||||||
@ -1127,49 +1238,10 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
FreeFile(cstate->copy_file);
|
FreeFile(cstate->copy_file);
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||||
errmsg("\"%s\" is a directory", filename)));
|
errmsg("\"%s\" is a directory", cstate->filename)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DoCopyTo(cstate);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pipe)
|
|
||||||
{
|
|
||||||
/* we assume only the write case could fail here */
|
|
||||||
if (FreeFile(cstate->copy_file))
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode_for_file_access(),
|
|
||||||
errmsg("could not write to file \"%s\": %m",
|
|
||||||
filename)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Close the relation. 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.
|
|
||||||
*/
|
|
||||||
heap_close(cstate->rel, (is_from ? NoLock : AccessShareLock));
|
|
||||||
|
|
||||||
/* Clean up storage (probably not really necessary) */
|
|
||||||
processed = cstate->processed;
|
|
||||||
|
|
||||||
pfree(cstate->attribute_buf.data);
|
|
||||||
pfree(cstate->line_buf.data);
|
|
||||||
pfree(cstate->raw_buf);
|
|
||||||
pfree(cstate);
|
|
||||||
|
|
||||||
return processed;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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)
|
|||||||
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)
|
|||||||
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,7 +1321,7 @@ 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.)
|
||||||
*/
|
*/
|
||||||
mycontext = AllocSetContextCreate(CurrentMemoryContext,
|
cstate->rowcontext = AllocSetContextCreate(CurrentMemoryContext,
|
||||||
"COPY TO",
|
"COPY TO",
|
||||||
ALLOCSET_DEFAULT_MINSIZE,
|
ALLOCSET_DEFAULT_MINSIZE,
|
||||||
ALLOCSET_DEFAULT_INITSIZE,
|
ALLOCSET_DEFAULT_INITSIZE,
|
||||||
@ -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,29 +1377,73 @@ CopyTo(CopyState cstate)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cstate->rel)
|
||||||
|
{
|
||||||
|
Datum *values;
|
||||||
|
bool *nulls;
|
||||||
|
HeapScanDesc scandesc;
|
||||||
|
HeapTuple tuple;
|
||||||
|
|
||||||
|
values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
|
||||||
|
nulls = (bool *) palloc(num_phys_attrs * sizeof(bool));
|
||||||
|
|
||||||
scandesc = heap_beginscan(cstate->rel, ActiveSnapshot, 0, NULL);
|
scandesc = heap_beginscan(cstate->rel, ActiveSnapshot, 0, NULL);
|
||||||
|
|
||||||
while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL)
|
while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL)
|
||||||
{
|
{
|
||||||
bool need_delim = false;
|
|
||||||
|
|
||||||
CHECK_FOR_INTERRUPTS();
|
CHECK_FOR_INTERRUPTS();
|
||||||
|
|
||||||
MemoryContextReset(mycontext);
|
/* Deconstruct the tuple ... faster than repeated heap_getattr */
|
||||||
oldcontext = MemoryContextSwitchTo(mycontext);
|
heap_deform_tuple(tuple, tupDesc, values, nulls);
|
||||||
|
|
||||||
|
/* Format and send the data */
|
||||||
|
CopyOneRowTo(cstate, HeapTupleGetOid(tuple), values, nulls);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
if (cstate->binary)
|
||||||
{
|
{
|
||||||
/* Binary per-tuple header */
|
/* Binary per-tuple header */
|
||||||
CopySendInt16(cstate, attr_count);
|
CopySendInt16(cstate, list_length(cstate->attnumlist));
|
||||||
/* Send OID if wanted --- note attr_count doesn't include it */
|
/* Send OID if wanted --- note attnumlist doesn't include it */
|
||||||
if (cstate->oids)
|
if (cstate->oids)
|
||||||
{
|
{
|
||||||
Oid oid = HeapTupleGetOid(tuple);
|
|
||||||
|
|
||||||
/* Hack --- assume Oid is same size as int32 */
|
/* Hack --- assume Oid is same size as int32 */
|
||||||
CopySendInt32(cstate, sizeof(int32));
|
CopySendInt32(cstate, sizeof(int32));
|
||||||
CopySendInt32(cstate, oid);
|
CopySendInt32(cstate, tupleOid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1341,7 +1453,7 @@ CopyTo(CopyState cstate)
|
|||||||
if (cstate->oids)
|
if (cstate->oids)
|
||||||
{
|
{
|
||||||
string = DatumGetCString(DirectFunctionCall1(oidout,
|
string = DatumGetCString(DirectFunctionCall1(oidout,
|
||||||
ObjectIdGetDatum(HeapTupleGetOid(tuple))));
|
ObjectIdGetDatum(tupleOid)));
|
||||||
CopySendString(cstate, string);
|
CopySendString(cstate, string);
|
||||||
need_delim = true;
|
need_delim = true;
|
||||||
}
|
}
|
||||||
@ -1350,10 +1462,8 @@ CopyTo(CopyState cstate)
|
|||||||
foreach(cur, cstate->attnumlist)
|
foreach(cur, cstate->attnumlist)
|
||||||
{
|
{
|
||||||
int attnum = lfirst_int(cur);
|
int attnum = lfirst_int(cur);
|
||||||
Datum value;
|
Datum value = values[attnum - 1];
|
||||||
bool isnull;
|
bool isnull = nulls[attnum - 1];
|
||||||
|
|
||||||
value = heap_getattr(tuple, attnum, tupDesc, &isnull);
|
|
||||||
|
|
||||||
if (!cstate->binary)
|
if (!cstate->binary)
|
||||||
{
|
{
|
||||||
@ -1365,7 +1475,7 @@ CopyTo(CopyState cstate)
|
|||||||
if (isnull)
|
if (isnull)
|
||||||
{
|
{
|
||||||
if (!cstate->binary)
|
if (!cstate->binary)
|
||||||
CopySendString(cstate, null_print_client);
|
CopySendString(cstate, cstate->null_print_client);
|
||||||
else
|
else
|
||||||
CopySendInt32(cstate, -1);
|
CopySendInt32(cstate, -1);
|
||||||
}
|
}
|
||||||
@ -1377,7 +1487,7 @@ CopyTo(CopyState cstate)
|
|||||||
value);
|
value);
|
||||||
if (cstate->csv_mode)
|
if (cstate->csv_mode)
|
||||||
CopyAttributeOutCSV(cstate, string,
|
CopyAttributeOutCSV(cstate, string,
|
||||||
force_quote[attnum - 1],
|
cstate->force_quote_flags[attnum - 1],
|
||||||
list_length(cstate->attnumlist) == 1);
|
list_length(cstate->attnumlist) == 1);
|
||||||
else
|
else
|
||||||
CopyAttributeOutText(cstate, string);
|
CopyAttributeOutText(cstate, string);
|
||||||
@ -1400,22 +1510,6 @@ CopyTo(CopyState cstate)
|
|||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
|
||||||
cstate->processed++;
|
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);
|
|
||||||
|
|
||||||
pfree(out_functions);
|
|
||||||
pfree(force_quote);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
&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)
|
|||||||
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)
|
|||||||
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,
|
|||||||
* 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)
|
|||||||
{
|
{
|
||||||
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)
|
||||||
|
{
|
||||||
|
if (rel != NULL)
|
||||||
ereport(ERROR,
|
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)
|
|||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
|
@ -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.349 2006/08/25 04:06:49 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.350 2006/08/30 23:34:21 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -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);
|
||||||
|
@ -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.283 2006/08/25 04:06:49 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.284 2006/08/30 23:34:21 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -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);
|
||||||
|
@ -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.348 2006/08/25 04:06:51 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.349 2006/08/30 23:34:21 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -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,
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.558 2006/08/25 04:06:51 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.559 2006/08/30 23:34:21 tgl Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -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
|
|||||||
{
|
{
|
||||||
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
|
|||||||
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:
|
|||||||
/*
|
/*
|
||||||
* 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; }
|
||||||
|
@ -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 @@
|
|||||||
|
|
||||||
#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)
|
|||||||
|
|
||||||
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)
|
|||||||
case DestSPI:
|
case DestSPI:
|
||||||
case DestTuplestore:
|
case DestTuplestore:
|
||||||
case DestIntoRel:
|
case DestIntoRel:
|
||||||
|
case DestCopyOut:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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)
|
|||||||
case DestSPI:
|
case DestSPI:
|
||||||
case DestTuplestore:
|
case DestTuplestore:
|
||||||
case DestIntoRel:
|
case DestIntoRel:
|
||||||
|
case DestCopyOut:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.67 2006/08/29 15:19:50 tgl Exp $
|
* $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.68 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 @@
|
|||||||
* \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)
|
|||||||
|
|
||||||
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)
|
||||||
|
@ -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.27 2006/03/05 15:58:55 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/commands/copy.h,v 1.28 2006/08/30 23:34:22 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -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 */
|
||||||
|
@ -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.325 2006/08/25 04:06:56 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.326 2006/08/30 23:34:22 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -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;
|
||||||
|
|
||||||
|
@ -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.51 2006/08/12 02:52:06 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/tcop/dest.h,v 1.52 2006/08/30 23:34:22 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -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;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
126
src/test/regress/expected/copyselect.out
Normal file
126
src/test/regress/expected/copyselect.out
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
--
|
||||||
|
-- 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;
|
@ -1,6 +1,6 @@
|
|||||||
# ----------
|
# ----------
|
||||||
# The first group of parallel test
|
# The first group of parallel test
|
||||||
# $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.34 2006/08/12 02:52:06 tgl Exp $
|
# $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.35 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
|
|||||||
# 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
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.32 2006/08/12 02:52:06 tgl Exp $
|
# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.33 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
|
|||||||
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
|
||||||
|
82
src/test/regress/sql/copyselect.sql
Normal file
82
src/test/regress/sql/copyselect.sql
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
--
|
||||||
|
-- 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;
|
Loading…
x
Reference in New Issue
Block a user