1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* copy.c
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2001-01-24 20:43:33 +01:00
|
|
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2002-02-24 03:32:26 +01:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.147 2002/02/24 02:32:26 momjian Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
2000-12-02 21:49:24 +01:00
|
|
|
#include "postgres.h"
|
1996-08-27 09:42:29 +02:00
|
|
|
|
1996-11-06 09:21:43 +01:00
|
|
|
#include <unistd.h>
|
1999-07-16 07:00:38 +02:00
|
|
|
#include <sys/stat.h>
|
1996-11-06 09:21:43 +01:00
|
|
|
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "access/genam.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "access/heapam.h"
|
2000-12-02 21:49:24 +01:00
|
|
|
#include "access/printtup.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "catalog/catname.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "catalog/index.h"
|
2000-06-15 05:33:12 +02:00
|
|
|
#include "catalog/pg_index.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "catalog/pg_shadow.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "catalog/pg_type.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "commands/copy.h"
|
1997-09-01 09:59:06 +02:00
|
|
|
#include "commands/trigger.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "executor/executor.h"
|
1999-07-16 01:04:24 +02:00
|
|
|
#include "libpq/libpq.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "miscadmin.h"
|
2000-08-22 06:06:22 +02:00
|
|
|
#include "tcop/pquery.h"
|
1999-09-27 22:00:44 +02:00
|
|
|
#include "tcop/tcopprot.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "utils/acl.h"
|
|
|
|
#include "utils/builtins.h"
|
2000-06-17 23:49:04 +02:00
|
|
|
#include "utils/relcache.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "utils/syscache.h"
|
1996-10-18 07:59:17 +02:00
|
|
|
|
1998-08-24 03:14:24 +02:00
|
|
|
#ifdef MULTIBYTE
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "mb/pg_wchar.h"
|
1998-08-24 03:14:24 +02:00
|
|
|
#endif
|
|
|
|
|
1996-11-02 03:01:48 +01:00
|
|
|
#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
|
2002-02-12 22:25:41 +01:00
|
|
|
#define OCTVALUE(c) ((c) - '0')
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
|
|
|
|
/* non-export function prototypes */
|
1999-12-14 01:08:21 +01:00
|
|
|
static void CopyTo(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null_print);
|
|
|
|
static void CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null_print);
|
1997-09-08 04:41:22 +02:00
|
|
|
static Oid GetInputFunction(Oid type);
|
2000-11-16 23:30:52 +01:00
|
|
|
static Oid GetTypeElement(Oid type);
|
1997-09-08 23:56:23 +02:00
|
|
|
static void CopyReadNewline(FILE *fp, int *newline);
|
1999-12-14 01:08:21 +01:00
|
|
|
static char *CopyReadAttribute(FILE *fp, bool *isnull, char *delim, int *newline, char *null_print);
|
1999-09-12 00:28:11 +02:00
|
|
|
static void CopyAttributeOut(FILE *fp, char *string, char *delim);
|
2001-01-03 21:04:10 +01:00
|
|
|
|
|
|
|
static const char BinarySignature[12] = "PGBCOPY\n\377\r\n\0";
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-07-22 04:40:07 +02:00
|
|
|
/*
|
|
|
|
* Static communication variables ... pretty grotty, but COPY has
|
|
|
|
* never been reentrant...
|
|
|
|
*/
|
2001-06-08 23:16:49 +02:00
|
|
|
int copy_lineno = 0; /* exported for use by elog() -- dz */
|
2000-04-12 19:17:23 +02:00
|
|
|
static bool fe_eof;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-01-16 22:37:50 +01:00
|
|
|
/*
|
|
|
|
* These static variables are used to avoid incurring overhead for each
|
|
|
|
* attribute processed. attribute_buf is reused on each CopyReadAttribute
|
|
|
|
* call to hold the string being read in. Under normal use it will soon
|
|
|
|
* grow to a suitable size, and then we will avoid palloc/pfree overhead
|
|
|
|
* for subsequent attributes. Note that CopyReadAttribute returns a pointer
|
|
|
|
* to attribute_buf's data buffer!
|
|
|
|
* encoding, if needed, can be set once at the start of the copy operation.
|
|
|
|
*/
|
2000-04-12 19:17:23 +02:00
|
|
|
static StringInfoData attribute_buf;
|
|
|
|
|
2000-01-16 22:37:50 +01:00
|
|
|
#ifdef MULTIBYTE
|
2001-01-06 04:33:17 +01:00
|
|
|
static int client_encoding;
|
|
|
|
static int server_encoding;
|
2000-01-16 22:37:50 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
1999-05-25 18:15:34 +02:00
|
|
|
/*
|
1999-01-11 04:56:11 +01:00
|
|
|
* Internal communications functions
|
|
|
|
*/
|
1999-05-26 14:57:23 +02:00
|
|
|
static void CopySendData(void *databuf, int datasize, FILE *fp);
|
2002-02-12 22:25:41 +01:00
|
|
|
static void CopySendString(const char *str, FILE *fp);
|
1999-05-26 14:57:23 +02:00
|
|
|
static void CopySendChar(char c, FILE *fp);
|
|
|
|
static void CopyGetData(void *databuf, int datasize, FILE *fp);
|
|
|
|
static int CopyGetChar(FILE *fp);
|
|
|
|
static int CopyGetEof(FILE *fp);
|
|
|
|
static int CopyPeekChar(FILE *fp);
|
2002-02-12 22:25:41 +01:00
|
|
|
static void CopyDonePeek(FILE *fp, int c, bool pickup);
|
1999-01-11 04:56:11 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* CopySendData sends output data either to the file
|
1999-05-25 18:15:34 +02:00
|
|
|
* specified by fp or, if fp is NULL, using the standard
|
|
|
|
* backend->frontend functions
|
1999-01-11 04:56:11 +01:00
|
|
|
*
|
|
|
|
* CopySendString does the same for null-terminated strings
|
|
|
|
* CopySendChar does the same for single characters
|
1999-04-25 05:19:27 +02:00
|
|
|
*
|
|
|
|
* NB: no data conversion is applied by these functions
|
1999-01-11 04:56:11 +01:00
|
|
|
*/
|
1999-05-26 14:57:23 +02:00
|
|
|
static void
|
1999-05-25 18:15:34 +02:00
|
|
|
CopySendData(void *databuf, int datasize, FILE *fp)
|
|
|
|
{
|
|
|
|
if (!fp)
|
1999-07-22 04:40:07 +02:00
|
|
|
{
|
|
|
|
if (pq_putbytes((char *) databuf, datasize))
|
|
|
|
fe_eof = true;
|
|
|
|
}
|
1999-05-25 18:15:34 +02:00
|
|
|
else
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
1999-05-25 18:15:34 +02:00
|
|
|
fwrite(databuf, datasize, 1, fp);
|
2000-04-12 19:17:23 +02:00
|
|
|
if (ferror(fp))
|
2000-04-16 06:27:52 +02:00
|
|
|
elog(ERROR, "CopySendData: %m");
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
1999-01-11 04:56:11 +01:00
|
|
|
}
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1999-05-26 14:57:23 +02:00
|
|
|
static void
|
2002-02-12 22:25:41 +01:00
|
|
|
CopySendString(const char *str, FILE *fp)
|
1999-05-25 18:15:34 +02:00
|
|
|
{
|
2002-02-12 22:25:41 +01:00
|
|
|
CopySendData((void *) str, strlen(str), fp);
|
1999-01-11 04:56:11 +01:00
|
|
|
}
|
|
|
|
|
1999-05-26 14:57:23 +02:00
|
|
|
static void
|
1999-05-25 18:15:34 +02:00
|
|
|
CopySendChar(char c, FILE *fp)
|
|
|
|
{
|
|
|
|
CopySendData(&c, 1, fp);
|
1999-01-11 04:56:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* CopyGetData reads output data either from the file
|
1999-05-25 18:15:34 +02:00
|
|
|
* specified by fp or, if fp is NULL, using the standard
|
|
|
|
* backend->frontend functions
|
1999-01-11 04:56:11 +01:00
|
|
|
*
|
|
|
|
* CopyGetChar does the same for single characters
|
2000-01-22 04:52:04 +01:00
|
|
|
* CopyGetEof checks if it's EOF on the input (or, check for EOF result
|
|
|
|
* from CopyGetChar)
|
1999-04-25 05:19:27 +02:00
|
|
|
*
|
|
|
|
* NB: no data conversion is applied by these functions
|
1999-01-11 04:56:11 +01:00
|
|
|
*/
|
1999-05-26 14:57:23 +02:00
|
|
|
static void
|
1999-05-25 18:15:34 +02:00
|
|
|
CopyGetData(void *databuf, int datasize, FILE *fp)
|
|
|
|
{
|
|
|
|
if (!fp)
|
1999-07-22 04:40:07 +02:00
|
|
|
{
|
|
|
|
if (pq_getbytes((char *) databuf, datasize))
|
|
|
|
fe_eof = true;
|
|
|
|
}
|
1999-05-25 18:15:34 +02:00
|
|
|
else
|
|
|
|
fread(databuf, datasize, 1, fp);
|
1999-01-11 04:56:11 +01:00
|
|
|
}
|
|
|
|
|
1999-05-26 14:57:23 +02:00
|
|
|
static int
|
1999-05-25 18:15:34 +02:00
|
|
|
CopyGetChar(FILE *fp)
|
|
|
|
{
|
|
|
|
if (!fp)
|
|
|
|
{
|
2001-12-04 20:40:17 +01:00
|
|
|
int ch = pq_getbyte();
|
1999-05-25 18:15:34 +02:00
|
|
|
|
2001-12-04 20:40:17 +01:00
|
|
|
if (ch == EOF)
|
1999-07-22 04:40:07 +02:00
|
|
|
fe_eof = true;
|
1999-05-25 18:15:34 +02:00
|
|
|
return ch;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return getc(fp);
|
1999-01-11 04:56:11 +01:00
|
|
|
}
|
|
|
|
|
1999-05-26 14:57:23 +02:00
|
|
|
static int
|
1999-05-25 18:15:34 +02:00
|
|
|
CopyGetEof(FILE *fp)
|
|
|
|
{
|
|
|
|
if (!fp)
|
1999-07-22 04:40:07 +02:00
|
|
|
return fe_eof;
|
1999-05-25 18:15:34 +02:00
|
|
|
else
|
|
|
|
return feof(fp);
|
1999-01-11 04:56:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* CopyPeekChar reads a byte in "peekable" mode.
|
2002-02-12 22:25:41 +01:00
|
|
|
*
|
1999-01-11 04:56:11 +01:00
|
|
|
* after each call to CopyPeekChar, a call to CopyDonePeek _must_
|
1999-07-22 04:40:07 +02:00
|
|
|
* follow, unless EOF was returned.
|
2002-02-12 22:25:41 +01:00
|
|
|
*
|
|
|
|
* CopyDonePeek will either take the peeked char off the stream
|
|
|
|
* (if pickup is true) or leave it on the stream (if pickup is false).
|
1999-01-11 04:56:11 +01:00
|
|
|
*/
|
1999-05-26 14:57:23 +02:00
|
|
|
static int
|
1999-05-25 18:15:34 +02:00
|
|
|
CopyPeekChar(FILE *fp)
|
|
|
|
{
|
|
|
|
if (!fp)
|
1999-07-22 04:40:07 +02:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
int ch = pq_peekbyte();
|
|
|
|
|
1999-07-22 04:40:07 +02:00
|
|
|
if (ch == EOF)
|
|
|
|
fe_eof = true;
|
|
|
|
return ch;
|
|
|
|
}
|
1999-05-25 18:15:34 +02:00
|
|
|
else
|
|
|
|
return getc(fp);
|
1999-01-11 04:56:11 +01:00
|
|
|
}
|
|
|
|
|
1999-05-26 14:57:23 +02:00
|
|
|
static void
|
2002-02-12 22:25:41 +01:00
|
|
|
CopyDonePeek(FILE *fp, int c, bool pickup)
|
1999-05-25 18:15:34 +02:00
|
|
|
{
|
|
|
|
if (!fp)
|
|
|
|
{
|
|
|
|
if (pickup)
|
|
|
|
{
|
2002-02-12 22:25:41 +01:00
|
|
|
/* We want to pick it up */
|
2001-12-04 20:40:17 +01:00
|
|
|
(void) pq_getbyte();
|
1999-05-25 18:15:34 +02:00
|
|
|
}
|
|
|
|
/* If we didn't want to pick it up, just leave it where it sits */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!pickup)
|
|
|
|
{
|
|
|
|
/* We don't want to pick it up - so put it back in there */
|
|
|
|
ungetc(c, fp);
|
|
|
|
}
|
2002-02-12 22:25:41 +01:00
|
|
|
/* If we wanted to pick it up, it's already done */
|
1999-05-25 18:15:34 +02:00
|
|
|
}
|
1999-01-11 04:56:11 +01:00
|
|
|
}
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1999-01-11 04:56:11 +01:00
|
|
|
|
|
|
|
|
1996-11-02 03:01:48 +01:00
|
|
|
/*
|
1999-11-21 05:16:17 +01:00
|
|
|
* DoCopy executes the SQL COPY statement.
|
2001-08-10 20:57:42 +02:00
|
|
|
*
|
|
|
|
* Either unload or reload contents of table <relname>, depending on <from>.
|
|
|
|
* (<from> = TRUE means we are inserting into the table.)
|
|
|
|
*
|
|
|
|
* If <pipe> is false, transfer is between the table and the file named
|
2001-10-25 07:50:21 +02:00
|
|
|
* <filename>. Otherwise, transfer is between the table and our regular
|
|
|
|
* input/output stream. The latter could be either stdin/stdout or a
|
2001-08-10 20:57:42 +02:00
|
|
|
* socket, depending on whether we're running under Postmaster control.
|
|
|
|
*
|
|
|
|
* Iff <binary>, unload or reload in the binary format, as opposed to the
|
|
|
|
* more wasteful but more robust and portable text format.
|
|
|
|
*
|
|
|
|
* Iff <oids>, unload or reload the format that includes OID information.
|
|
|
|
* On input, we accept OIDs whether or not the table has an OID column,
|
|
|
|
* but silently drop them if it does not. On output, we report an error
|
|
|
|
* if the user asks for OIDs in a table that has none (not providing an
|
|
|
|
* OID column might seem friendlier, but could seriously confuse programs).
|
|
|
|
*
|
|
|
|
* If in the text format, delimit columns with delimiter <delim> and print
|
|
|
|
* NULL values as <null_print>.
|
|
|
|
*
|
|
|
|
* When loading in the text format from an input stream (as opposed to
|
2001-10-25 07:50:21 +02:00
|
|
|
* a file), recognize a "." on a line by itself as EOF. Also recognize
|
2001-08-10 20:57:42 +02:00
|
|
|
* a stream EOF. When unloading in the text format to an output stream,
|
|
|
|
* write a "." on a line by itself at the end of the data.
|
|
|
|
*
|
|
|
|
* Do not allow a Postgres user without superuser privilege to read from
|
|
|
|
* or write to a file.
|
|
|
|
*
|
|
|
|
* Do not allow the copy if user doesn't have proper permission to access
|
|
|
|
* the table.
|
1996-11-02 03:01:48 +01:00
|
|
|
*/
|
1996-07-09 08:22:35 +02:00
|
|
|
void
|
1997-09-07 07:04:48 +02:00
|
|
|
DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe,
|
2000-01-14 23:11:38 +01:00
|
|
|
char *filename, char *delim, char *null_print)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-06-12 22:41:25 +02:00
|
|
|
FILE *fp;
|
1997-09-08 04:41:22 +02:00
|
|
|
Relation rel;
|
2001-08-10 20:57:42 +02:00
|
|
|
const AclMode required_access = (from ? ACL_INSERT : ACL_SELECT);
|
1997-09-08 04:41:22 +02:00
|
|
|
int result;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-11-27 22:52:53 +01:00
|
|
|
/*
|
|
|
|
* Open and lock the relation, using the appropriate lock type.
|
|
|
|
*/
|
2000-05-18 03:52:45 +02:00
|
|
|
rel = heap_openr(relname, (from ? RowExclusiveLock : AccessShareLock));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-09-06 16:15:31 +02:00
|
|
|
result = pg_aclcheck(relname, GetUserId(), required_access);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (result != ACLCHECK_OK)
|
1998-01-05 17:40:20 +01:00
|
|
|
elog(ERROR, "%s: %s", relname, aclcheck_error_strings[result]);
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!pipe && !superuser())
|
1998-01-05 17:40:20 +01:00
|
|
|
elog(ERROR, "You must have Postgres superuser privilege to do a COPY "
|
1997-09-07 07:04:48 +02:00
|
|
|
"directly to or from a file. Anyone can COPY to stdout or "
|
|
|
|
"from stdin. Psql's \\copy command also works for anyone.");
|
2001-03-22 05:01:46 +01:00
|
|
|
|
2000-05-18 03:52:45 +02:00
|
|
|
/*
|
|
|
|
* This restriction is unfortunate, but necessary until the frontend
|
|
|
|
* COPY protocol is redesigned to be binary-safe...
|
|
|
|
*/
|
|
|
|
if (pipe && binary)
|
|
|
|
elog(ERROR, "COPY BINARY is not supported to stdout or from stdin");
|
2000-01-14 23:11:38 +01:00
|
|
|
|
2001-12-04 22:19:57 +01:00
|
|
|
/*
|
|
|
|
* Presently, only single-character delimiter strings are supported.
|
|
|
|
*/
|
|
|
|
if (strlen(delim) != 1)
|
|
|
|
elog(ERROR, "COPY delimiter must be a single character");
|
|
|
|
|
2000-01-16 22:37:50 +01:00
|
|
|
/*
|
|
|
|
* Set up variables to avoid per-attribute overhead.
|
|
|
|
*/
|
|
|
|
initStringInfo(&attribute_buf);
|
|
|
|
#ifdef MULTIBYTE
|
2001-01-06 04:33:17 +01:00
|
|
|
client_encoding = pg_get_client_encoding();
|
|
|
|
server_encoding = GetDatabaseEncoding();
|
2000-01-16 22:37:50 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if (from)
|
|
|
|
{ /* copy from file to database */
|
2001-08-10 20:57:42 +02:00
|
|
|
if (rel->rd_rel->relkind != RELKIND_RELATION)
|
|
|
|
{
|
|
|
|
if (rel->rd_rel->relkind == RELKIND_VIEW)
|
|
|
|
elog(ERROR, "You cannot copy view %s", relname);
|
|
|
|
else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
|
|
|
|
elog(ERROR, "You cannot change sequence relation %s", relname);
|
|
|
|
else
|
|
|
|
elog(ERROR, "You cannot copy object %s", relname);
|
|
|
|
}
|
2000-01-16 22:37:50 +01:00
|
|
|
if (pipe)
|
|
|
|
{
|
|
|
|
if (IsUnderPostmaster)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2000-01-16 22:37:50 +01:00
|
|
|
ReceiveCopyBegin();
|
|
|
|
fp = NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
2000-01-16 22:37:50 +01:00
|
|
|
fp = stdin;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2002-02-23 22:46:03 +01:00
|
|
|
struct stat st;
|
2000-06-02 17:57:44 +02:00
|
|
|
fp = AllocateFile(filename, PG_BINARY_R);
|
2002-02-23 22:46:03 +01:00
|
|
|
|
|
|
|
if (fp == NULL)
|
2000-01-16 22:37:50 +01:00
|
|
|
elog(ERROR, "COPY command, running in backend with "
|
|
|
|
"effective uid %d, could not open file '%s' for "
|
|
|
|
"reading. Errno = %s (%d).",
|
2000-06-14 20:18:01 +02:00
|
|
|
(int) geteuid(), filename, strerror(errno), errno);
|
2002-02-23 22:46:03 +01:00
|
|
|
|
|
|
|
fstat(fileno(fp),&st);
|
|
|
|
if( S_ISDIR(st.st_mode) ){
|
2002-02-24 03:32:26 +01:00
|
|
|
FreeFile(fp);
|
2002-02-23 22:46:03 +01:00
|
|
|
elog(ERROR,"COPY: %s is a directory.",filename);
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2000-01-16 22:37:50 +01:00
|
|
|
CopyFrom(rel, binary, oids, fp, delim, null_print);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ /* copy from database to file */
|
2001-08-10 20:57:42 +02:00
|
|
|
if (rel->rd_rel->relkind != RELKIND_RELATION)
|
|
|
|
{
|
|
|
|
if (rel->rd_rel->relkind == RELKIND_VIEW)
|
|
|
|
elog(ERROR, "You cannot copy view %s", relname);
|
|
|
|
else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
|
|
|
|
elog(ERROR, "You cannot copy sequence %s", relname);
|
|
|
|
else
|
|
|
|
elog(ERROR, "You cannot copy object %s", relname);
|
|
|
|
}
|
2000-01-16 22:37:50 +01:00
|
|
|
if (pipe)
|
|
|
|
{
|
|
|
|
if (IsUnderPostmaster)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2000-01-16 22:37:50 +01:00
|
|
|
SendCopyBegin();
|
|
|
|
pq_startcopyout();
|
|
|
|
fp = NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
2000-01-16 22:37:50 +01:00
|
|
|
fp = stdout;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
mode_t oumask; /* Pre-existing umask value */
|
2002-02-23 22:46:03 +01:00
|
|
|
struct stat st;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-12-02 21:49:24 +01:00
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* Prevent write to relative path ... too easy to shoot
|
|
|
|
* oneself in the foot by overwriting a database file ...
|
2000-12-02 21:49:24 +01:00
|
|
|
*/
|
|
|
|
if (filename[0] != '/')
|
2000-03-23 22:38:58 +01:00
|
|
|
elog(ERROR, "Relative path not allowed for server side"
|
2000-04-12 19:17:23 +02:00
|
|
|
" COPY command.");
|
2000-03-23 22:38:58 +01:00
|
|
|
|
2000-04-16 06:27:52 +02:00
|
|
|
oumask = umask((mode_t) 022);
|
2000-06-02 17:57:44 +02:00
|
|
|
fp = AllocateFile(filename, PG_BINARY_W);
|
2000-01-16 22:37:50 +01:00
|
|
|
umask(oumask);
|
2000-04-16 06:27:52 +02:00
|
|
|
|
2000-01-16 22:37:50 +01:00
|
|
|
if (fp == NULL)
|
|
|
|
elog(ERROR, "COPY command, running in backend with "
|
|
|
|
"effective uid %d, could not open file '%s' for "
|
|
|
|
"writing. Errno = %s (%d).",
|
2000-06-14 20:18:01 +02:00
|
|
|
(int) geteuid(), filename, strerror(errno), errno);
|
2002-02-23 22:46:03 +01:00
|
|
|
fstat(fileno(fp),&st);
|
|
|
|
if( S_ISDIR(st.st_mode) ){
|
2002-02-24 03:32:26 +01:00
|
|
|
FreeFile(fp);
|
2002-02-23 22:46:03 +01:00
|
|
|
elog(ERROR,"COPY: %s is a directory.",filename);
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2000-01-16 22:37:50 +01:00
|
|
|
CopyTo(rel, binary, oids, fp, delim, null_print);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pipe)
|
|
|
|
FreeFile(fp);
|
|
|
|
else if (!from)
|
|
|
|
{
|
|
|
|
if (!binary)
|
|
|
|
CopySendData("\\.\n", 3, fp);
|
|
|
|
if (IsUnderPostmaster)
|
|
|
|
pq_endcopyout(false);
|
|
|
|
}
|
|
|
|
pfree(attribute_buf.data);
|
1999-09-18 21:08:25 +02:00
|
|
|
|
1999-11-27 22:52:53 +01:00
|
|
|
/*
|
2000-04-12 19:17:23 +02:00
|
|
|
* 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.
|
1999-11-27 22:52:53 +01:00
|
|
|
*/
|
|
|
|
heap_close(rel, (from ? NoLock : AccessShareLock));
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1996-11-02 03:01:48 +01:00
|
|
|
|
2000-12-02 21:49:24 +01:00
|
|
|
/*
|
|
|
|
* Copy from relation TO file.
|
|
|
|
*/
|
1996-07-09 08:22:35 +02:00
|
|
|
static void
|
2001-01-03 21:04:10 +01:00
|
|
|
CopyTo(Relation rel, bool binary, bool oids, FILE *fp,
|
|
|
|
char *delim, char *null_print)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
HeapTuple tuple;
|
2000-12-02 21:49:24 +01:00
|
|
|
TupleDesc tupDesc;
|
1997-09-08 04:41:22 +02:00
|
|
|
HeapScanDesc scandesc;
|
2000-12-02 21:49:24 +01:00
|
|
|
int attr_count,
|
1997-09-08 04:41:22 +02:00
|
|
|
i;
|
1998-09-01 05:29:17 +02:00
|
|
|
Form_pg_attribute *attr;
|
1998-01-15 20:46:37 +01:00
|
|
|
FmgrInfo *out_functions;
|
1997-09-08 04:41:22 +02:00
|
|
|
Oid *elements;
|
2000-12-02 21:49:24 +01:00
|
|
|
bool *isvarlena;
|
2001-01-03 21:04:10 +01:00
|
|
|
int16 fld_size;
|
1997-09-08 04:41:22 +02:00
|
|
|
char *string;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
if (oids && !rel->rd_rel->relhasoids)
|
|
|
|
elog(ERROR, "COPY: table %s does not have OIDs",
|
|
|
|
RelationGetRelationName(rel));
|
|
|
|
|
2000-12-02 21:49:24 +01:00
|
|
|
tupDesc = rel->rd_att;
|
1997-09-07 07:04:48 +02:00
|
|
|
attr_count = rel->rd_att->natts;
|
|
|
|
attr = rel->rd_att->attrs;
|
2000-12-02 21:49:24 +01:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
/*
|
|
|
|
* For binary copy we really only need isvarlena, but compute it
|
|
|
|
* all...
|
|
|
|
*/
|
2000-12-02 21:49:24 +01:00
|
|
|
out_functions = (FmgrInfo *) palloc(attr_count * sizeof(FmgrInfo));
|
|
|
|
elements = (Oid *) palloc(attr_count * sizeof(Oid));
|
|
|
|
isvarlena = (bool *) palloc(attr_count * sizeof(bool));
|
|
|
|
for (i = 0; i < attr_count; i++)
|
|
|
|
{
|
|
|
|
Oid out_func_oid;
|
|
|
|
|
|
|
|
if (!getTypeOutputInfo(attr[i]->atttypid,
|
2001-03-22 05:01:46 +01:00
|
|
|
&out_func_oid, &elements[i], &isvarlena[i]))
|
2000-12-02 21:49:24 +01:00
|
|
|
elog(ERROR, "COPY: couldn't lookup info for type %u",
|
|
|
|
attr[i]->atttypid);
|
|
|
|
fmgr_info(out_func_oid, &out_functions[i]);
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-03 21:04:10 +01:00
|
|
|
if (binary)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2001-01-03 21:04:10 +01:00
|
|
|
/* Generate header for a binary copy */
|
|
|
|
int32 tmp;
|
|
|
|
|
|
|
|
/* Signature */
|
|
|
|
CopySendData((char *) BinarySignature, 12, fp);
|
|
|
|
/* Integer layout field */
|
|
|
|
tmp = 0x01020304;
|
|
|
|
CopySendData(&tmp, sizeof(int32), fp);
|
|
|
|
/* Flags field */
|
|
|
|
tmp = 0;
|
|
|
|
if (oids)
|
|
|
|
tmp |= (1 << 16);
|
|
|
|
CopySendData(&tmp, sizeof(int32), fp);
|
|
|
|
/* No header extension */
|
|
|
|
tmp = 0;
|
|
|
|
CopySendData(&tmp, sizeof(int32), fp);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2000-12-02 21:49:24 +01:00
|
|
|
|
2001-01-03 21:04:10 +01:00
|
|
|
scandesc = heap_beginscan(rel, 0, QuerySnapshot, 0, NULL);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-08-19 04:04:17 +02:00
|
|
|
while (HeapTupleIsValid(tuple = heap_getnext(scandesc, 0)))
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2001-01-03 21:04:10 +01:00
|
|
|
bool need_delim = false;
|
|
|
|
|
2001-01-14 06:08:17 +01:00
|
|
|
CHECK_FOR_INTERRUPTS();
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-03 21:04:10 +01:00
|
|
|
if (binary)
|
|
|
|
{
|
|
|
|
/* Binary per-tuple header */
|
2001-03-22 05:01:46 +01:00
|
|
|
int16 fld_count = attr_count;
|
2001-01-03 21:04:10 +01:00
|
|
|
|
|
|
|
CopySendData(&fld_count, sizeof(int16), fp);
|
|
|
|
/* Send OID if wanted --- note fld_count doesn't include it */
|
|
|
|
if (oids)
|
|
|
|
{
|
|
|
|
fld_size = sizeof(Oid);
|
|
|
|
CopySendData(&fld_size, sizeof(int16), fp);
|
|
|
|
CopySendData(&tuple->t_data->t_oid, sizeof(Oid), fp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2001-01-03 21:04:10 +01:00
|
|
|
/* Text format has no per-tuple header, but send OID if wanted */
|
|
|
|
if (oids)
|
|
|
|
{
|
|
|
|
string = DatumGetCString(DirectFunctionCall1(oidout,
|
2001-03-22 05:01:46 +01:00
|
|
|
ObjectIdGetDatum(tuple->t_data->t_oid)));
|
2001-01-03 21:04:10 +01:00
|
|
|
CopySendString(string, fp);
|
|
|
|
pfree(string);
|
|
|
|
need_delim = true;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < attr_count; i++)
|
|
|
|
{
|
2000-12-02 21:49:24 +01:00
|
|
|
Datum origvalue,
|
|
|
|
value;
|
|
|
|
bool isnull;
|
|
|
|
|
|
|
|
origvalue = heap_getattr(tuple, i + 1, tupDesc, &isnull);
|
|
|
|
|
2001-01-03 21:04:10 +01:00
|
|
|
if (!binary)
|
|
|
|
{
|
|
|
|
if (need_delim)
|
|
|
|
CopySendChar(delim[0], fp);
|
|
|
|
need_delim = true;
|
|
|
|
}
|
|
|
|
|
2000-12-02 21:49:24 +01:00
|
|
|
if (isnull)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2000-12-02 21:49:24 +01:00
|
|
|
if (!binary)
|
2001-01-03 21:04:10 +01:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
CopySendString(null_print, fp); /* null indicator */
|
2001-01-03 21:04:10 +01:00
|
|
|
}
|
2000-12-02 21:49:24 +01:00
|
|
|
else
|
2001-01-03 21:04:10 +01:00
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
fld_size = 0; /* null marker */
|
2001-01-03 21:04:10 +01:00
|
|
|
CopySendData(&fld_size, sizeof(int16), fp);
|
|
|
|
}
|
2000-12-02 21:49:24 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* If we have a toasted datum, forcibly detoast it to
|
|
|
|
* avoid memory leakage inside the type's output routine
|
|
|
|
* (or for binary case, becase we must output untoasted
|
|
|
|
* value).
|
2000-12-02 21:49:24 +01:00
|
|
|
*/
|
|
|
|
if (isvarlena[i])
|
|
|
|
value = PointerGetDatum(PG_DETOAST_DATUM(origvalue));
|
|
|
|
else
|
|
|
|
value = origvalue;
|
|
|
|
|
|
|
|
if (!binary)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2000-05-30 06:25:00 +02:00
|
|
|
string = DatumGetCString(FunctionCall3(&out_functions[i],
|
2001-03-22 05:01:46 +01:00
|
|
|
value,
|
|
|
|
ObjectIdGetDatum(elements[i]),
|
|
|
|
Int32GetDatum(attr[i]->atttypmod)));
|
1999-09-12 00:28:11 +02:00
|
|
|
CopyAttributeOut(fp, string, delim);
|
1997-09-07 07:04:48 +02:00
|
|
|
pfree(string);
|
|
|
|
}
|
2001-01-03 21:04:10 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
fld_size = attr[i]->attlen;
|
|
|
|
CopySendData(&fld_size, sizeof(int16), fp);
|
|
|
|
if (isvarlena[i])
|
|
|
|
{
|
|
|
|
/* varlena */
|
|
|
|
Assert(fld_size == -1);
|
|
|
|
CopySendData(DatumGetPointer(value),
|
|
|
|
VARSIZE(value),
|
|
|
|
fp);
|
|
|
|
}
|
|
|
|
else if (!attr[i]->attbyval)
|
|
|
|
{
|
|
|
|
/* fixed-length pass-by-reference */
|
|
|
|
Assert(fld_size > 0);
|
|
|
|
CopySendData(DatumGetPointer(value),
|
|
|
|
fld_size,
|
|
|
|
fp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* pass-by-value */
|
|
|
|
Datum datumBuf;
|
|
|
|
|
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* We need this horsing around because we don't
|
|
|
|
* know how shorter data values are aligned within
|
|
|
|
* a Datum.
|
2001-01-03 21:04:10 +01:00
|
|
|
*/
|
|
|
|
store_att_byval(&datumBuf, value, fld_size);
|
|
|
|
CopySendData(&datumBuf,
|
|
|
|
fld_size,
|
|
|
|
fp);
|
|
|
|
}
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-12-02 21:49:24 +01:00
|
|
|
/* Clean up detoasted copy, if any */
|
|
|
|
if (value != origvalue)
|
|
|
|
pfree(DatumGetPointer(value));
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
2001-01-03 21:04:10 +01:00
|
|
|
if (!binary)
|
|
|
|
CopySendChar('\n', fp);
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-03 21:04:10 +01:00
|
|
|
heap_endscan(scandesc);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-03 21:04:10 +01:00
|
|
|
if (binary)
|
|
|
|
{
|
|
|
|
/* Generate trailer for a binary copy */
|
2001-03-22 05:01:46 +01:00
|
|
|
int16 fld_count = -1;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-03 21:04:10 +01:00
|
|
|
CopySendData(&fld_count, sizeof(int16), fp);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
2000-12-02 21:49:24 +01:00
|
|
|
pfree(out_functions);
|
|
|
|
pfree(elements);
|
|
|
|
pfree(isvarlena);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2000-12-02 21:49:24 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy FROM file to relation.
|
|
|
|
*/
|
1996-07-09 08:22:35 +02:00
|
|
|
static void
|
2000-07-15 00:18:02 +02:00
|
|
|
CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
|
|
|
|
char *delim, char *null_print)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
HeapTuple tuple;
|
2000-07-15 00:18:02 +02:00
|
|
|
TupleDesc tupDesc;
|
1998-09-01 05:29:17 +02:00
|
|
|
Form_pg_attribute *attr;
|
2000-07-15 00:18:02 +02:00
|
|
|
AttrNumber attr_count;
|
1998-01-15 20:46:37 +01:00
|
|
|
FmgrInfo *in_functions;
|
2000-07-15 00:18:02 +02:00
|
|
|
Oid *elements;
|
1998-01-15 20:46:37 +01:00
|
|
|
int i;
|
1997-09-08 04:41:22 +02:00
|
|
|
Oid in_func_oid;
|
|
|
|
Datum *values;
|
2000-07-15 00:18:02 +02:00
|
|
|
char *nulls;
|
1997-09-08 04:41:22 +02:00
|
|
|
bool isnull;
|
|
|
|
int done = 0;
|
2001-01-03 21:04:10 +01:00
|
|
|
char *string;
|
2000-11-12 01:37:02 +01:00
|
|
|
ResultRelInfo *resultRelInfo;
|
2001-03-22 05:01:46 +01:00
|
|
|
EState *estate = CreateExecutorState(); /* for ExecConstraints() */
|
1997-09-08 04:41:22 +02:00
|
|
|
TupleTable tupleTable;
|
2000-07-15 00:18:02 +02:00
|
|
|
TupleTableSlot *slot;
|
2000-04-16 06:27:52 +02:00
|
|
|
Oid loaded_oid = InvalidOid;
|
1997-09-08 04:41:22 +02:00
|
|
|
bool skip_tuple = false;
|
2001-01-03 21:04:10 +01:00
|
|
|
bool file_has_oids;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
tupDesc = RelationGetDescr(rel);
|
1997-09-07 07:04:48 +02:00
|
|
|
attr = tupDesc->attrs;
|
|
|
|
attr_count = tupDesc->natts;
|
|
|
|
|
|
|
|
/*
|
2000-11-12 01:37:02 +01:00
|
|
|
* We need a ResultRelInfo so we can use the regular executor's
|
2001-03-22 05:01:46 +01:00
|
|
|
* index-entry-making machinery. (There used to be a huge amount of
|
|
|
|
* code here that basically duplicated execUtils.c ...)
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2000-11-12 01:37:02 +01:00
|
|
|
resultRelInfo = makeNode(ResultRelInfo);
|
2001-03-22 05:01:46 +01:00
|
|
|
resultRelInfo->ri_RangeTableIndex = 1; /* dummy */
|
2000-11-12 01:37:02 +01:00
|
|
|
resultRelInfo->ri_RelationDesc = rel;
|
2001-06-01 04:41:36 +02:00
|
|
|
resultRelInfo->ri_TrigDesc = rel->trigdesc;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-11-12 01:37:02 +01:00
|
|
|
ExecOpenIndices(resultRelInfo);
|
2000-07-15 00:18:02 +02:00
|
|
|
|
2000-11-12 01:37:02 +01:00
|
|
|
estate->es_result_relations = resultRelInfo;
|
|
|
|
estate->es_num_result_relations = 1;
|
|
|
|
estate->es_result_relation_info = resultRelInfo;
|
2000-07-15 00:18:02 +02:00
|
|
|
|
|
|
|
/* Set up a dummy tuple table too */
|
|
|
|
tupleTable = ExecCreateTupleTable(1);
|
|
|
|
slot = ExecAllocTableSlot(tupleTable);
|
2001-01-29 01:39:20 +01:00
|
|
|
ExecSetSlotDescriptor(slot, tupDesc, false);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (!binary)
|
|
|
|
{
|
1998-01-15 20:46:37 +01:00
|
|
|
in_functions = (FmgrInfo *) palloc(attr_count * sizeof(FmgrInfo));
|
1997-09-07 07:04:48 +02:00
|
|
|
elements = (Oid *) palloc(attr_count * sizeof(Oid));
|
|
|
|
for (i = 0; i < attr_count; i++)
|
|
|
|
{
|
|
|
|
in_func_oid = (Oid) GetInputFunction(attr[i]->atttypid);
|
1998-01-15 20:46:37 +01:00
|
|
|
fmgr_info(in_func_oid, &in_functions[i]);
|
1997-09-07 07:04:48 +02:00
|
|
|
elements[i] = GetTypeElement(attr[i]->atttypid);
|
|
|
|
}
|
2001-01-03 21:04:10 +01:00
|
|
|
file_has_oids = oids; /* must rely on user to tell us this... */
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2001-01-03 21:04:10 +01:00
|
|
|
/* Read and verify binary header */
|
|
|
|
char readSig[12];
|
|
|
|
int32 tmp;
|
|
|
|
|
|
|
|
/* Signature */
|
|
|
|
CopyGetData(readSig, 12, fp);
|
|
|
|
if (CopyGetEof(fp) ||
|
|
|
|
memcmp(readSig, BinarySignature, 12) != 0)
|
|
|
|
elog(ERROR, "COPY BINARY: file signature not recognized");
|
|
|
|
/* Integer layout field */
|
|
|
|
CopyGetData(&tmp, sizeof(int32), fp);
|
|
|
|
if (CopyGetEof(fp) ||
|
|
|
|
tmp != 0x01020304)
|
|
|
|
elog(ERROR, "COPY BINARY: incompatible integer layout");
|
|
|
|
/* Flags field */
|
|
|
|
CopyGetData(&tmp, sizeof(int32), fp);
|
|
|
|
if (CopyGetEof(fp))
|
|
|
|
elog(ERROR, "COPY BINARY: bogus file header (missing flags)");
|
|
|
|
file_has_oids = (tmp & (1 << 16)) != 0;
|
2001-03-22 05:01:46 +01:00
|
|
|
tmp &= ~(1 << 16);
|
2001-01-03 21:04:10 +01:00
|
|
|
if ((tmp >> 16) != 0)
|
|
|
|
elog(ERROR, "COPY BINARY: unrecognized critical flags in header");
|
|
|
|
/* Header extension length */
|
|
|
|
CopyGetData(&tmp, sizeof(int32), fp);
|
|
|
|
if (CopyGetEof(fp) ||
|
|
|
|
tmp < 0)
|
|
|
|
elog(ERROR, "COPY BINARY: bogus file header (missing length)");
|
|
|
|
/* Skip extension header, if present */
|
|
|
|
while (tmp-- > 0)
|
|
|
|
{
|
|
|
|
CopyGetData(readSig, 1, fp);
|
|
|
|
if (CopyGetEof(fp))
|
|
|
|
elog(ERROR, "COPY BINARY: bogus file header (wrong length)");
|
|
|
|
}
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
in_functions = NULL;
|
|
|
|
elements = NULL;
|
|
|
|
}
|
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Silently drop incoming OIDs if table does not have OIDs */
|
|
|
|
if (!rel->rd_rel->relhasoids)
|
|
|
|
oids = false;
|
|
|
|
|
2000-07-15 00:18:02 +02:00
|
|
|
values = (Datum *) palloc(attr_count * sizeof(Datum));
|
|
|
|
nulls = (char *) palloc(attr_count * sizeof(char));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-06-08 23:16:49 +02:00
|
|
|
copy_lineno = 0;
|
1999-07-22 04:40:07 +02:00
|
|
|
fe_eof = false;
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
while (!done)
|
|
|
|
{
|
2001-01-14 06:08:17 +01:00
|
|
|
CHECK_FOR_INTERRUPTS();
|
1999-09-27 22:00:44 +02:00
|
|
|
|
2001-06-08 23:16:49 +02:00
|
|
|
copy_lineno++;
|
2000-04-16 06:27:52 +02:00
|
|
|
|
2001-01-22 01:50:07 +01:00
|
|
|
/* Reset the per-output-tuple exprcontext */
|
|
|
|
ResetPerTupleExprContext(estate);
|
|
|
|
|
2001-01-03 21:04:10 +01:00
|
|
|
/* Initialize all values for row to NULL */
|
|
|
|
MemSet(values, 0, attr_count * sizeof(Datum));
|
|
|
|
MemSet(nulls, 'n', attr_count * sizeof(char));
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!binary)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int newline = 0;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-01-03 21:04:10 +01:00
|
|
|
if (file_has_oids)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2001-01-03 21:04:10 +01:00
|
|
|
string = CopyReadAttribute(fp, &isnull, delim,
|
|
|
|
&newline, null_print);
|
|
|
|
if (isnull)
|
|
|
|
elog(ERROR, "COPY TEXT: NULL Oid");
|
|
|
|
else if (string == NULL)
|
|
|
|
done = 1; /* end of file */
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
|
|
|
{
|
2000-06-05 09:29:25 +02:00
|
|
|
loaded_oid = DatumGetObjectId(DirectFunctionCall1(oidin,
|
2001-03-22 05:01:46 +01:00
|
|
|
CStringGetDatum(string)));
|
2000-04-16 06:27:52 +02:00
|
|
|
if (loaded_oid == InvalidOid)
|
|
|
|
elog(ERROR, "COPY TEXT: Invalid Oid");
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
}
|
2001-01-03 21:04:10 +01:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
for (i = 0; i < attr_count && !done; i++)
|
|
|
|
{
|
2001-01-03 21:04:10 +01:00
|
|
|
string = CopyReadAttribute(fp, &isnull, delim,
|
|
|
|
&newline, null_print);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (isnull)
|
|
|
|
{
|
2001-01-03 21:04:10 +01:00
|
|
|
/* already set values[i] and nulls[i] */
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else if (string == NULL)
|
2001-01-03 21:04:10 +01:00
|
|
|
done = 1; /* end of file */
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
|
|
|
{
|
2000-05-30 06:25:00 +02:00
|
|
|
values[i] = FunctionCall3(&in_functions[i],
|
|
|
|
CStringGetDatum(string),
|
2001-03-22 05:01:46 +01:00
|
|
|
ObjectIdGetDatum(elements[i]),
|
|
|
|
Int32GetDatum(attr[i]->atttypmod));
|
2001-01-03 21:04:10 +01:00
|
|
|
nulls[i] = ' ';
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!done)
|
|
|
|
CopyReadNewline(fp, &newline);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ /* binary */
|
2001-03-22 05:01:46 +01:00
|
|
|
int16 fld_count,
|
|
|
|
fld_size;
|
2001-01-03 21:04:10 +01:00
|
|
|
|
|
|
|
CopyGetData(&fld_count, sizeof(int16), fp);
|
|
|
|
if (CopyGetEof(fp) ||
|
|
|
|
fld_count == -1)
|
1997-09-07 07:04:48 +02:00
|
|
|
done = 1;
|
|
|
|
else
|
|
|
|
{
|
2001-01-03 21:04:10 +01:00
|
|
|
if (fld_count <= 0 || fld_count > attr_count)
|
|
|
|
elog(ERROR, "COPY BINARY: tuple field count is %d, expected %d",
|
|
|
|
(int) fld_count, attr_count);
|
|
|
|
|
|
|
|
if (file_has_oids)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2001-01-03 21:04:10 +01:00
|
|
|
CopyGetData(&fld_size, sizeof(int16), fp);
|
|
|
|
if (CopyGetEof(fp))
|
|
|
|
elog(ERROR, "COPY BINARY: unexpected EOF");
|
|
|
|
if (fld_size != (int16) sizeof(Oid))
|
|
|
|
elog(ERROR, "COPY BINARY: sizeof(Oid) is %d, expected %d",
|
|
|
|
(int) fld_size, (int) sizeof(Oid));
|
|
|
|
CopyGetData(&loaded_oid, sizeof(Oid), fp);
|
|
|
|
if (CopyGetEof(fp))
|
|
|
|
elog(ERROR, "COPY BINARY: unexpected EOF");
|
2000-04-16 06:27:52 +02:00
|
|
|
if (loaded_oid == InvalidOid)
|
|
|
|
elog(ERROR, "COPY BINARY: Invalid Oid");
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2001-01-03 21:04:10 +01:00
|
|
|
|
|
|
|
for (i = 0; i < (int) fld_count; i++)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2001-01-03 21:04:10 +01:00
|
|
|
CopyGetData(&fld_size, sizeof(int16), fp);
|
|
|
|
if (CopyGetEof(fp))
|
|
|
|
elog(ERROR, "COPY BINARY: unexpected EOF");
|
|
|
|
if (fld_size == 0)
|
2001-03-22 05:01:46 +01:00
|
|
|
continue; /* it's NULL; nulls[i] already set */
|
2001-01-03 21:04:10 +01:00
|
|
|
if (fld_size != attr[i]->attlen)
|
|
|
|
elog(ERROR, "COPY BINARY: sizeof(field %d) is %d, expected %d",
|
2001-03-22 05:01:46 +01:00
|
|
|
i + 1, (int) fld_size, (int) attr[i]->attlen);
|
2001-01-03 21:04:10 +01:00
|
|
|
if (fld_size == -1)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2001-01-03 21:04:10 +01:00
|
|
|
/* varlena field */
|
2001-03-22 05:01:46 +01:00
|
|
|
int32 varlena_size;
|
|
|
|
Pointer varlena_ptr;
|
2001-01-03 21:04:10 +01:00
|
|
|
|
|
|
|
CopyGetData(&varlena_size, sizeof(int32), fp);
|
|
|
|
if (CopyGetEof(fp))
|
|
|
|
elog(ERROR, "COPY BINARY: unexpected EOF");
|
|
|
|
if (varlena_size < (int32) sizeof(int32))
|
|
|
|
elog(ERROR, "COPY BINARY: bogus varlena length");
|
|
|
|
varlena_ptr = (Pointer) palloc(varlena_size);
|
|
|
|
VARATT_SIZEP(varlena_ptr) = varlena_size;
|
|
|
|
CopyGetData(VARDATA(varlena_ptr),
|
|
|
|
varlena_size - sizeof(int32),
|
|
|
|
fp);
|
|
|
|
if (CopyGetEof(fp))
|
|
|
|
elog(ERROR, "COPY BINARY: unexpected EOF");
|
|
|
|
values[i] = PointerGetDatum(varlena_ptr);
|
|
|
|
}
|
|
|
|
else if (!attr[i]->attbyval)
|
|
|
|
{
|
|
|
|
/* fixed-length pass-by-reference */
|
2001-03-22 05:01:46 +01:00
|
|
|
Pointer refval_ptr;
|
2001-01-03 21:04:10 +01:00
|
|
|
|
|
|
|
Assert(fld_size > 0);
|
|
|
|
refval_ptr = (Pointer) palloc(fld_size);
|
|
|
|
CopyGetData(refval_ptr, fld_size, fp);
|
|
|
|
if (CopyGetEof(fp))
|
|
|
|
elog(ERROR, "COPY BINARY: unexpected EOF");
|
|
|
|
values[i] = PointerGetDatum(refval_ptr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* pass-by-value */
|
|
|
|
Datum datumBuf;
|
|
|
|
|
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* We need this horsing around because we don't
|
|
|
|
* know how shorter data values are aligned within
|
|
|
|
* a Datum.
|
2001-01-03 21:04:10 +01:00
|
|
|
*/
|
|
|
|
Assert(fld_size > 0 && fld_size <= sizeof(Datum));
|
|
|
|
CopyGetData(&datumBuf, fld_size, fp);
|
|
|
|
if (CopyGetEof(fp))
|
|
|
|
elog(ERROR, "COPY BINARY: unexpected EOF");
|
|
|
|
values[i] = fetch_att(&datumBuf, true, fld_size);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
2001-01-03 21:04:10 +01:00
|
|
|
nulls[i] = ' ';
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2001-01-03 21:04:10 +01:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (done)
|
2001-01-03 21:04:10 +01:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
tuple = heap_formtuple(tupDesc, values, nulls);
|
2001-01-03 21:04:10 +01:00
|
|
|
|
|
|
|
if (oids && file_has_oids)
|
1998-11-27 20:52:36 +01:00
|
|
|
tuple->t_data->t_oid = loaded_oid;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
skip_tuple = false;
|
2000-07-15 00:18:02 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* BEFORE ROW INSERT Triggers */
|
2001-06-01 04:41:36 +02:00
|
|
|
if (resultRelInfo->ri_TrigDesc &&
|
|
|
|
resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
HeapTuple newtuple;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-06-01 04:41:36 +02:00
|
|
|
newtuple = ExecBRInsertTriggers(estate, resultRelInfo, tuple);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (newtuple == NULL) /* "do nothing" */
|
|
|
|
skip_tuple = true;
|
|
|
|
else if (newtuple != tuple) /* modified by Trigger(s) */
|
|
|
|
{
|
1999-12-16 23:20:03 +01:00
|
|
|
heap_freetuple(tuple);
|
1997-09-07 07:04:48 +02:00
|
|
|
tuple = newtuple;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!skip_tuple)
|
|
|
|
{
|
2001-03-14 22:47:50 +01:00
|
|
|
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
|
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2000-07-15 00:18:02 +02:00
|
|
|
* Check the constraints of the tuple
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
if (rel->rd_att->constr)
|
2000-11-12 01:37:02 +01:00
|
|
|
ExecConstraints("CopyFrom", resultRelInfo, slot, estate);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2001-03-22 07:16:21 +01:00
|
|
|
/*
|
2000-07-15 00:18:02 +02:00
|
|
|
* OK, store the tuple and create index entries for it
|
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
heap_insert(rel, tuple);
|
|
|
|
|
2000-11-12 01:37:02 +01:00
|
|
|
if (resultRelInfo->ri_NumIndices > 0)
|
2000-07-15 00:18:02 +02:00
|
|
|
ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* AFTER ROW INSERT Triggers */
|
2001-06-01 04:41:36 +02:00
|
|
|
if (resultRelInfo->ri_TrigDesc)
|
|
|
|
ExecARInsertTriggers(estate, resultRelInfo, tuple);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < attr_count; i++)
|
|
|
|
{
|
2000-12-28 00:59:14 +01:00
|
|
|
if (!attr[i]->attbyval && nulls[i] != 'n')
|
2001-01-03 21:04:10 +01:00
|
|
|
pfree(DatumGetPointer(values[i]));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
1999-12-16 23:20:03 +01:00
|
|
|
heap_freetuple(tuple);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2000-07-15 00:18:02 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Done, clean up
|
|
|
|
*/
|
2001-06-08 23:16:49 +02:00
|
|
|
copy_lineno = 0;
|
2000-07-15 00:18:02 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
pfree(values);
|
1998-02-10 17:04:38 +01:00
|
|
|
pfree(nulls);
|
1999-05-25 18:15:34 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!binary)
|
1998-02-10 17:04:38 +01:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
pfree(in_functions);
|
1998-02-10 17:04:38 +01:00
|
|
|
pfree(elements);
|
|
|
|
}
|
1998-12-15 13:47:01 +01:00
|
|
|
|
2000-07-15 00:18:02 +02:00
|
|
|
ExecDropTupleTable(tupleTable, true);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2000-11-12 01:37:02 +01:00
|
|
|
ExecCloseIndices(resultRelInfo);
|
2000-07-15 00:18:02 +02:00
|
|
|
}
|
1996-11-02 03:01:48 +01:00
|
|
|
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
static Oid
|
2000-11-16 23:30:52 +01:00
|
|
|
GetInputFunction(Oid type)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
HeapTuple typeTuple;
|
2000-11-16 23:30:52 +01:00
|
|
|
Oid result;
|
|
|
|
|
|
|
|
typeTuple = SearchSysCache(TYPEOID,
|
|
|
|
ObjectIdGetDatum(type),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(typeTuple))
|
|
|
|
elog(ERROR, "GetInputFunction: Cache lookup of type %u failed", type);
|
|
|
|
result = ((Form_pg_type) GETSTRUCT(typeTuple))->typinput;
|
|
|
|
ReleaseSysCache(typeTuple);
|
|
|
|
return result;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
static Oid
|
2000-11-16 23:30:52 +01:00
|
|
|
GetTypeElement(Oid type)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
HeapTuple typeTuple;
|
2000-11-16 23:30:52 +01:00
|
|
|
Oid result;
|
|
|
|
|
|
|
|
typeTuple = SearchSysCache(TYPEOID,
|
|
|
|
ObjectIdGetDatum(type),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(typeTuple))
|
|
|
|
elog(ERROR, "GetTypeElement: Cache lookup of type %u failed", type);
|
|
|
|
result = ((Form_pg_type) GETSTRUCT(typeTuple))->typelem;
|
|
|
|
ReleaseSysCache(typeTuple);
|
|
|
|
return result;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1996-12-19 05:58:24 +01:00
|
|
|
/*
|
|
|
|
* Reads input from fp until an end of line is seen.
|
|
|
|
*/
|
|
|
|
|
1998-10-26 02:00:13 +01:00
|
|
|
static void
|
1997-09-08 23:56:23 +02:00
|
|
|
CopyReadNewline(FILE *fp, int *newline)
|
1996-12-19 05:58:24 +01:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
if (!*newline)
|
|
|
|
{
|
2000-04-16 06:27:52 +02:00
|
|
|
elog(NOTICE, "CopyReadNewline: extra fields ignored");
|
1999-01-11 04:56:11 +01:00
|
|
|
while (!CopyGetEof(fp) && (CopyGetChar(fp) != '\n'));
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
*newline = 0;
|
1996-12-19 05:58:24 +01:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
1999-09-12 00:28:11 +02:00
|
|
|
* Read the value of a single attribute.
|
|
|
|
*
|
2000-01-16 22:37:50 +01:00
|
|
|
* Result is either a string, or NULL (if EOF or a null attribute).
|
|
|
|
* Note that the caller should not pfree the string!
|
1999-09-12 00:28:11 +02:00
|
|
|
*
|
2000-01-16 22:37:50 +01:00
|
|
|
* *isnull is set true if a null attribute, else false.
|
2001-12-04 22:19:57 +01:00
|
|
|
* delim is the column delimiter string (currently always 1 character).
|
1999-09-12 00:28:11 +02:00
|
|
|
* *newline remembers whether we've seen a newline ending this tuple.
|
1999-12-14 01:08:21 +01:00
|
|
|
* null_print says how NULL values are represented
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
static char *
|
1999-12-14 01:08:21 +01:00
|
|
|
CopyReadAttribute(FILE *fp, bool *isnull, char *delim, int *newline, char *null_print)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-02-09 01:10:11 +01:00
|
|
|
int c;
|
2001-12-04 22:19:57 +01:00
|
|
|
int delimc = delim[0];
|
2000-04-12 19:17:23 +02:00
|
|
|
|
1998-08-24 03:14:24 +02:00
|
|
|
#ifdef MULTIBYTE
|
1998-09-01 06:40:42 +02:00
|
|
|
int mblen;
|
|
|
|
unsigned char s[2];
|
1999-09-12 00:28:11 +02:00
|
|
|
char *cvt;
|
1998-09-01 06:40:42 +02:00
|
|
|
int j;
|
|
|
|
|
1998-08-24 03:14:24 +02:00
|
|
|
s[1] = 0;
|
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-01-16 22:37:50 +01:00
|
|
|
/* reset attribute_buf to empty */
|
|
|
|
attribute_buf.len = 0;
|
|
|
|
attribute_buf.data[0] = '\0';
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* if last delimiter was a newline return a NULL attribute */
|
|
|
|
if (*newline)
|
|
|
|
{
|
|
|
|
*isnull = (bool) true;
|
1998-09-01 05:29:17 +02:00
|
|
|
return NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-12-19 05:58:24 +01:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
*isnull = (bool) false; /* set default */
|
1999-09-12 00:28:11 +02:00
|
|
|
|
|
|
|
for (;;)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-01-11 04:56:11 +01:00
|
|
|
c = CopyGetChar(fp);
|
2000-01-22 04:52:04 +01:00
|
|
|
if (c == EOF)
|
1999-09-12 00:28:11 +02:00
|
|
|
goto endOfFile;
|
2000-01-22 04:52:04 +01:00
|
|
|
if (c == '\n')
|
|
|
|
{
|
|
|
|
*newline = 1;
|
|
|
|
break;
|
|
|
|
}
|
2001-12-04 22:19:57 +01:00
|
|
|
if (c == delimc)
|
2000-01-22 04:52:04 +01:00
|
|
|
break;
|
1999-09-12 00:28:11 +02:00
|
|
|
if (c == '\\')
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-01-11 04:56:11 +01:00
|
|
|
c = CopyGetChar(fp);
|
2000-01-22 04:52:04 +01:00
|
|
|
if (c == EOF)
|
1999-09-12 00:28:11 +02:00
|
|
|
goto endOfFile;
|
1997-09-07 07:04:48 +02:00
|
|
|
switch (c)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
case '0':
|
|
|
|
case '1':
|
|
|
|
case '2':
|
|
|
|
case '3':
|
|
|
|
case '4':
|
|
|
|
case '5':
|
|
|
|
case '6':
|
|
|
|
case '7':
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int val;
|
|
|
|
|
2002-02-12 22:25:41 +01:00
|
|
|
val = OCTVALUE(c);
|
1999-01-11 04:56:11 +01:00
|
|
|
c = CopyPeekChar(fp);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (ISOCTAL(c))
|
|
|
|
{
|
2002-02-12 22:25:41 +01:00
|
|
|
val = (val << 3) + OCTVALUE(c);
|
|
|
|
CopyDonePeek(fp, c, true /*pick up*/);
|
1999-01-11 04:56:11 +01:00
|
|
|
c = CopyPeekChar(fp);
|
1999-05-25 18:15:34 +02:00
|
|
|
if (ISOCTAL(c))
|
|
|
|
{
|
2002-02-12 22:25:41 +01:00
|
|
|
val = (val << 3) + OCTVALUE(c);
|
|
|
|
CopyDonePeek(fp, c, true /*pick up*/);
|
1999-01-11 04:56:11 +01:00
|
|
|
}
|
1997-09-08 04:41:22 +02:00
|
|
|
else
|
|
|
|
{
|
2000-01-22 04:52:04 +01:00
|
|
|
if (c == EOF)
|
1999-09-12 00:28:11 +02:00
|
|
|
goto endOfFile;
|
2002-02-12 22:25:41 +01:00
|
|
|
CopyDonePeek(fp, c, false /*put back*/);
|
1997-09-08 04:41:22 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-01-22 04:52:04 +01:00
|
|
|
if (c == EOF)
|
1999-09-12 00:28:11 +02:00
|
|
|
goto endOfFile;
|
2002-02-12 22:25:41 +01:00
|
|
|
CopyDonePeek(fp, c, false /*put back*/);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1997-09-08 04:41:22 +02:00
|
|
|
c = val & 0377;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This is a special hack to parse `\N' as
|
|
|
|
* <backslash-N> rather then just 'N' to provide
|
|
|
|
* compatibility with the default NULL output. -- pe
|
|
|
|
*/
|
|
|
|
case 'N':
|
|
|
|
appendStringInfoCharMacro(&attribute_buf, '\\');
|
|
|
|
c = 'N';
|
|
|
|
break;
|
1997-09-08 04:41:22 +02:00
|
|
|
case 'b':
|
|
|
|
c = '\b';
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
c = '\f';
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
c = '\n';
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
c = '\r';
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
c = '\t';
|
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
c = '\v';
|
|
|
|
break;
|
|
|
|
case '.':
|
1999-01-11 04:56:11 +01:00
|
|
|
c = CopyGetChar(fp);
|
1997-09-08 04:41:22 +02:00
|
|
|
if (c != '\n')
|
2000-04-16 06:27:52 +02:00
|
|
|
elog(ERROR, "CopyReadAttribute: end of record marker corrupted");
|
1999-09-12 00:28:11 +02:00
|
|
|
goto endOfFile;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
}
|
2000-01-22 04:52:04 +01:00
|
|
|
appendStringInfoCharMacro(&attribute_buf, c);
|
1998-08-24 03:14:24 +02:00
|
|
|
#ifdef MULTIBYTE
|
2002-02-12 22:25:41 +01:00
|
|
|
/* XXX shouldn't this be done even when encoding is the same? */
|
2001-01-06 04:33:17 +01:00
|
|
|
if (client_encoding != server_encoding)
|
1998-09-01 06:40:42 +02:00
|
|
|
{
|
2001-01-06 04:33:17 +01:00
|
|
|
/* get additional bytes of the char, if any */
|
|
|
|
s[0] = c;
|
|
|
|
mblen = pg_encoding_mblen(client_encoding, s);
|
|
|
|
for (j = 1; j < mblen; j++)
|
|
|
|
{
|
|
|
|
c = CopyGetChar(fp);
|
|
|
|
if (c == EOF)
|
|
|
|
goto endOfFile;
|
|
|
|
appendStringInfoCharMacro(&attribute_buf, c);
|
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
}
|
1998-08-24 03:14:24 +02:00
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1999-09-12 00:28:11 +02:00
|
|
|
|
1998-08-24 03:14:24 +02:00
|
|
|
#ifdef MULTIBYTE
|
2001-01-06 04:33:17 +01:00
|
|
|
if (client_encoding != server_encoding)
|
1999-09-12 00:28:11 +02:00
|
|
|
{
|
2001-01-06 04:33:17 +01:00
|
|
|
cvt = (char *) pg_client_to_server((unsigned char *) attribute_buf.data,
|
|
|
|
attribute_buf.len);
|
|
|
|
if (cvt != attribute_buf.data)
|
|
|
|
{
|
|
|
|
/* transfer converted data back to attribute_buf */
|
|
|
|
attribute_buf.len = 0;
|
|
|
|
attribute_buf.data[0] = '\0';
|
|
|
|
appendBinaryStringInfo(&attribute_buf, cvt, strlen(cvt));
|
|
|
|
pfree(cvt);
|
|
|
|
}
|
1999-09-12 00:28:11 +02:00
|
|
|
}
|
1998-08-24 03:14:24 +02:00
|
|
|
#endif
|
2000-01-16 22:37:50 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
if (strcmp(attribute_buf.data, null_print) == 0)
|
|
|
|
*isnull = true;
|
1999-12-14 01:08:21 +01:00
|
|
|
|
1999-09-12 00:28:11 +02:00
|
|
|
return attribute_buf.data;
|
|
|
|
|
|
|
|
endOfFile:
|
|
|
|
return NULL;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1996-07-23 04:23:54 +02:00
|
|
|
static void
|
1999-09-12 00:28:11 +02:00
|
|
|
CopyAttributeOut(FILE *fp, char *server_string, char *delim)
|
1996-07-23 04:23:54 +02:00
|
|
|
{
|
1998-09-01 06:40:42 +02:00
|
|
|
char *string;
|
1998-07-27 21:38:40 +02:00
|
|
|
char c;
|
2002-02-12 22:25:41 +01:00
|
|
|
char delimc = delim[0];
|
2000-04-12 19:17:23 +02:00
|
|
|
|
1998-08-24 03:14:24 +02:00
|
|
|
#ifdef MULTIBYTE
|
2002-02-12 22:25:41 +01:00
|
|
|
bool same_encoding;
|
1999-09-12 00:28:11 +02:00
|
|
|
char *string_start;
|
|
|
|
int mblen;
|
1998-09-01 06:40:42 +02:00
|
|
|
int i;
|
1998-08-24 03:14:24 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef MULTIBYTE
|
2002-02-12 22:25:41 +01:00
|
|
|
same_encoding = (server_encoding == client_encoding);
|
|
|
|
if (!same_encoding)
|
2001-01-06 04:33:17 +01:00
|
|
|
{
|
|
|
|
string = (char *) pg_server_to_client((unsigned char *) server_string,
|
|
|
|
strlen(server_string));
|
|
|
|
string_start = string;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
string = server_string;
|
2002-02-12 22:25:41 +01:00
|
|
|
string_start = NULL;
|
2001-01-06 04:33:17 +01:00
|
|
|
}
|
1998-08-24 03:14:24 +02:00
|
|
|
#else
|
|
|
|
string = server_string;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef MULTIBYTE
|
2002-02-12 22:25:41 +01:00
|
|
|
for (; (c = *string) != '\0'; string += mblen)
|
1998-08-24 03:14:24 +02:00
|
|
|
#else
|
1997-09-07 07:04:48 +02:00
|
|
|
for (; (c = *string) != '\0'; string++)
|
1998-08-24 03:14:24 +02:00
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1998-08-24 03:14:24 +02:00
|
|
|
#ifdef MULTIBYTE
|
2002-02-12 22:25:41 +01:00
|
|
|
mblen = 1;
|
1998-08-24 03:14:24 +02:00
|
|
|
#endif
|
2002-02-12 22:25:41 +01:00
|
|
|
switch (c)
|
|
|
|
{
|
|
|
|
case '\b':
|
|
|
|
CopySendString("\\b", fp);
|
|
|
|
break;
|
|
|
|
case '\f':
|
|
|
|
CopySendString("\\f", fp);
|
|
|
|
break;
|
|
|
|
case '\n':
|
|
|
|
CopySendString("\\n", fp);
|
|
|
|
break;
|
|
|
|
case '\r':
|
|
|
|
CopySendString("\\r", fp);
|
|
|
|
break;
|
|
|
|
case '\t':
|
|
|
|
CopySendString("\\t", fp);
|
|
|
|
break;
|
|
|
|
case '\v':
|
|
|
|
CopySendString("\\v", fp);
|
|
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
CopySendString("\\\\", fp);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (c == delimc)
|
|
|
|
CopySendChar('\\', fp);
|
|
|
|
CopySendChar(c, fp);
|
|
|
|
#ifdef MULTIBYTE
|
|
|
|
/* XXX shouldn't this be done even when encoding is same? */
|
|
|
|
if (!same_encoding)
|
|
|
|
{
|
|
|
|
/* send additional bytes of the char, if any */
|
|
|
|
mblen = pg_encoding_mblen(client_encoding, string);
|
|
|
|
for (i = 1; i < mblen; i++)
|
|
|
|
CopySendChar(string[i], fp);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1999-09-12 00:28:11 +02:00
|
|
|
|
|
|
|
#ifdef MULTIBYTE
|
2002-02-12 22:25:41 +01:00
|
|
|
if (string_start)
|
1999-09-12 00:28:11 +02:00
|
|
|
pfree(string_start); /* pfree pg_server_to_client result */
|
|
|
|
#endif
|
1996-07-23 04:23:54 +02:00
|
|
|
}
|