summaryrefslogtreecommitdiff
path: root/src/backend/commands/copy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/copy.c')
-rw-r--r--src/backend/commands/copy.c1363
1 files changed, 0 insertions, 1363 deletions
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
deleted file mode 100644
index 7410bff04b1..00000000000
--- a/src/backend/commands/copy.c
+++ /dev/null
@@ -1,1363 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * copy.c
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.158 2002/06/20 20:29:27 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include <unistd.h>
-#include <sys/stat.h>
-
-#include "access/genam.h"
-#include "access/heapam.h"
-#include "access/printtup.h"
-#include "catalog/catname.h"
-#include "catalog/index.h"
-#include "catalog/namespace.h"
-#include "catalog/pg_index.h"
-#include "catalog/pg_shadow.h"
-#include "catalog/pg_type.h"
-#include "commands/copy.h"
-#include "commands/trigger.h"
-#include "executor/executor.h"
-#include "libpq/libpq.h"
-#include "miscadmin.h"
-#include "tcop/pquery.h"
-#include "tcop/tcopprot.h"
-#include "utils/acl.h"
-#include "utils/builtins.h"
-#include "utils/relcache.h"
-#include "utils/syscache.h"
-
-#ifdef MULTIBYTE
-#include "mb/pg_wchar.h"
-#endif
-
-#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
-#define OCTVALUE(c) ((c) - '0')
-
-
-/* non-export function prototypes */
-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);
-static Oid GetInputFunction(Oid type);
-static Oid GetTypeElement(Oid type);
-static void CopyReadNewline(FILE *fp, int *newline);
-static char *CopyReadAttribute(FILE *fp, bool *isnull, char *delim, int *newline, char *null_print);
-static void CopyAttributeOut(FILE *fp, char *string, char *delim);
-
-static const char BinarySignature[12] = "PGBCOPY\n\377\r\n\0";
-
-/*
- * Static communication variables ... pretty grotty, but COPY has
- * never been reentrant...
- */
-int copy_lineno = 0; /* exported for use by elog() -- dz */
-static bool fe_eof;
-
-/*
- * 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.
- */
-static StringInfoData attribute_buf;
-
-#ifdef MULTIBYTE
-static int client_encoding;
-static int server_encoding;
-#endif
-
-
-/*
- * Internal communications functions
- */
-static void CopySendData(void *databuf, int datasize, FILE *fp);
-static void CopySendString(const char *str, FILE *fp);
-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);
-static void CopyDonePeek(FILE *fp, int c, bool pickup);
-
-/*
- * CopySendData sends output data either to the file
- * specified by fp or, if fp is NULL, using the standard
- * backend->frontend functions
- *
- * CopySendString does the same for null-terminated strings
- * CopySendChar does the same for single characters
- *
- * NB: no data conversion is applied by these functions
- */
-static void
-CopySendData(void *databuf, int datasize, FILE *fp)
-{
- if (!fp)
- {
- if (pq_putbytes((char *) databuf, datasize))
- fe_eof = true;
- }
- else
- {
- fwrite(databuf, datasize, 1, fp);
- if (ferror(fp))
- elog(ERROR, "CopySendData: %m");
- }
-}
-
-static void
-CopySendString(const char *str, FILE *fp)
-{
- CopySendData((void *) str, strlen(str), fp);
-}
-
-static void
-CopySendChar(char c, FILE *fp)
-{
- CopySendData(&c, 1, fp);
-}
-
-/*
- * CopyGetData reads output data either from the file
- * specified by fp or, if fp is NULL, using the standard
- * backend->frontend functions
- *
- * CopyGetChar does the same for single characters
- * CopyGetEof checks if it's EOF on the input (or, check for EOF result
- * from CopyGetChar)
- *
- * NB: no data conversion is applied by these functions
- */
-static void
-CopyGetData(void *databuf, int datasize, FILE *fp)
-{
- if (!fp)
- {
- if (pq_getbytes((char *) databuf, datasize))
- fe_eof = true;
- }
- else
- fread(databuf, datasize, 1, fp);
-}
-
-static int
-CopyGetChar(FILE *fp)
-{
- if (!fp)
- {
- int ch = pq_getbyte();
-
- if (ch == EOF)
- fe_eof = true;
- return ch;
- }
- else
- return getc(fp);
-}
-
-static int
-CopyGetEof(FILE *fp)
-{
- if (!fp)
- return fe_eof;
- else
- return feof(fp);
-}
-
-/*
- * CopyPeekChar reads a byte in "peekable" mode.
- *
- * after each call to CopyPeekChar, a call to CopyDonePeek _must_
- * follow, unless EOF was returned.
- *
- * CopyDonePeek will either take the peeked char off the stream
- * (if pickup is true) or leave it on the stream (if pickup is false).
- */
-static int
-CopyPeekChar(FILE *fp)
-{
- if (!fp)
- {
- int ch = pq_peekbyte();
-
- if (ch == EOF)
- fe_eof = true;
- return ch;
- }
- else
- return getc(fp);
-}
-
-static void
-CopyDonePeek(FILE *fp, int c, bool pickup)
-{
- if (!fp)
- {
- if (pickup)
- {
- /* We want to pick it up */
- (void) pq_getbyte();
- }
- /* 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);
- }
- /* If we wanted to pick it up, it's already done */
- }
-}
-
-
-
-/*
- * DoCopy executes the SQL COPY statement.
- *
- * Either unload or reload contents of table <relation>, 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
- * <filename>. Otherwise, transfer is between the table and our regular
- * input/output stream. The latter could be either stdin/stdout or a
- * 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
- * a file), recognize a "." on a line by itself as EOF. Also recognize
- * 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.
- */
-void
-DoCopy(const CopyStmt *stmt)
-{
- RangeVar *relation = stmt->relation;
- char *filename = stmt->filename;
- bool is_from = stmt->is_from;
- bool pipe = (stmt->filename == NULL);
- List *option;
- DefElem *dbinary = NULL;
- DefElem *doids = NULL;
- DefElem *ddelim = NULL;
- DefElem *dnull = NULL;
- bool binary = false;
- bool oids = false;
- char *delim = "\t";
- char *null_print = "\\N";
- FILE *fp;
- Relation rel;
- AclMode required_access = (is_from ? ACL_INSERT : ACL_SELECT);
- AclResult aclresult;
-
- /* Extract options from the statement node tree */
- foreach(option, stmt->options)
- {
- DefElem *defel = (DefElem *) lfirst(option);
-
- if (strcmp(defel->defname, "binary") == 0)
- {
- if (dbinary)
- elog(ERROR, "COPY: conflicting options");
- dbinary = defel;
- }
- else if (strcmp(defel->defname, "oids") == 0)
- {
- if (doids)
- elog(ERROR, "COPY: conflicting options");
- doids = defel;
- }
- else if (strcmp(defel->defname, "delimiter") == 0)
- {
- if (ddelim)
- elog(ERROR, "COPY: conflicting options");
- ddelim = defel;
- }
- else if (strcmp(defel->defname, "null") == 0)
- {
- if (dnull)
- elog(ERROR, "COPY: conflicting options");
- dnull = defel;
- }
- else
- elog(ERROR, "COPY: option \"%s\" not recognized",
- defel->defname);
- }
-
- if (dbinary)
- binary = intVal(dbinary->arg);
- if (doids)
- oids = intVal(doids->arg);
- if (ddelim)
- delim = strVal(ddelim->arg);
- if (dnull)
- null_print = strVal(dnull->arg);
-
- if (binary && ddelim)
- elog(ERROR, "You can not specify the DELIMITER in BINARY mode.");
-
- if (binary && dnull)
- elog(ERROR, "You can not specify NULL in BINARY mode.");
-
- /*
- * Open and lock the relation, using the appropriate lock type.
- */
- rel = heap_openrv(relation, (is_from ? RowExclusiveLock : AccessShareLock));
-
- /* Check permissions. */
- aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
- required_access);
- if (aclresult != ACLCHECK_OK)
- aclcheck_error(aclresult, RelationGetRelationName(rel));
- if (!pipe && !superuser())
- elog(ERROR, "You must have Postgres superuser privilege to do a COPY "
- "directly to or from a file. Anyone can COPY to stdout or "
- "from stdin. Psql's \\copy command also works for anyone.");
-
- /*
- * 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");
-
- /*
- * Presently, only single-character delimiter strings are supported.
- */
- if (strlen(delim) != 1)
- elog(ERROR, "COPY delimiter must be a single character");
-
- /*
- * Set up variables to avoid per-attribute overhead.
- */
- initStringInfo(&attribute_buf);
-#ifdef MULTIBYTE
- client_encoding = pg_get_client_encoding();
- server_encoding = GetDatabaseEncoding();
-#endif
-
- if (is_from)
- { /* copy from file to database */
- if (rel->rd_rel->relkind != RELKIND_RELATION)
- {
- if (rel->rd_rel->relkind == RELKIND_VIEW)
- elog(ERROR, "You cannot copy view %s",
- RelationGetRelationName(rel));
- else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
- elog(ERROR, "You cannot change sequence relation %s",
- RelationGetRelationName(rel));
- else
- elog(ERROR, "You cannot copy object %s",
- RelationGetRelationName(rel));
- }
- if (pipe)
- {
- if (IsUnderPostmaster)
- {
- ReceiveCopyBegin();
- fp = NULL;
- }
- else
- fp = stdin;
- }
- else
- {
- struct stat st;
-
- fp = AllocateFile(filename, PG_BINARY_R);
-
- if (fp == NULL)
- elog(ERROR, "COPY command, running in backend with "
- "effective uid %d, could not open file '%s' for "
- "reading. Errno = %s (%d).",
- (int) geteuid(), filename, strerror(errno), errno);
-
- fstat(fileno(fp), &st);
- if (S_ISDIR(st.st_mode))
- {
- FreeFile(fp);
- elog(ERROR, "COPY: %s is a directory.", filename);
- }
- }
- CopyFrom(rel, binary, oids, fp, delim, null_print);
- }
- else
- { /* copy from database to file */
- if (rel->rd_rel->relkind != RELKIND_RELATION)
- {
- if (rel->rd_rel->relkind == RELKIND_VIEW)
- elog(ERROR, "You cannot copy view %s",
- RelationGetRelationName(rel));
- else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
- elog(ERROR, "You cannot copy sequence %s",
- RelationGetRelationName(rel));
- else
- elog(ERROR, "You cannot copy object %s",
- RelationGetRelationName(rel));
- }
- if (pipe)
- {
- if (IsUnderPostmaster)
- {
- SendCopyBegin();
- pq_startcopyout();
- fp = NULL;
- }
- else
- fp = stdout;
- }
- else
- {
- mode_t oumask; /* Pre-existing umask value */
- struct stat st;
-
- /*
- * Prevent write to relative path ... too easy to shoot
- * oneself in the foot by overwriting a database file ...
- */
- if (filename[0] != '/')
- elog(ERROR, "Relative path not allowed for server side"
- " COPY command.");
-
- oumask = umask((mode_t) 022);
- fp = AllocateFile(filename, PG_BINARY_W);
- umask(oumask);
-
- if (fp == NULL)
- elog(ERROR, "COPY command, running in backend with "
- "effective uid %d, could not open file '%s' for "
- "writing. Errno = %s (%d).",
- (int) geteuid(), filename, strerror(errno), errno);
- fstat(fileno(fp), &st);
- if (S_ISDIR(st.st_mode))
- {
- FreeFile(fp);
- elog(ERROR, "COPY: %s is a directory.", filename);
- }
- }
- CopyTo(rel, binary, oids, fp, delim, null_print);
- }
-
- if (!pipe)
- FreeFile(fp);
- else if (!is_from)
- {
- if (!binary)
- CopySendData("\\.\n", 3, fp);
- if (IsUnderPostmaster)
- pq_endcopyout(false);
- }
- pfree(attribute_buf.data);
-
- /*
- * 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(rel, (is_from ? NoLock : AccessShareLock));
-}
-
-
-/*
- * Copy from relation TO file.
- */
-static void
-CopyTo(Relation rel, bool binary, bool oids, FILE *fp,
- char *delim, char *null_print)
-{
- HeapTuple tuple;
- TupleDesc tupDesc;
- HeapScanDesc scandesc;
- int attr_count,
- i;
- Form_pg_attribute *attr;
- FmgrInfo *out_functions;
- Oid *elements;
- bool *isvarlena;
- int16 fld_size;
- char *string;
- Snapshot mySnapshot;
-
- if (oids && !rel->rd_rel->relhasoids)
- elog(ERROR, "COPY: table %s does not have OIDs",
- RelationGetRelationName(rel));
-
- tupDesc = rel->rd_att;
- attr_count = rel->rd_att->natts;
- attr = rel->rd_att->attrs;
-
- /*
- * For binary copy we really only need isvarlena, but compute it
- * all...
- */
- 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,
- &out_func_oid, &elements[i], &isvarlena[i]))
- elog(ERROR, "COPY: couldn't lookup info for type %u",
- attr[i]->atttypid);
- fmgr_info(out_func_oid, &out_functions[i]);
- }
-
- if (binary)
- {
- /* 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);
- }
-
- mySnapshot = CopyQuerySnapshot();
-
- scandesc = heap_beginscan(rel, mySnapshot, 0, NULL);
-
- while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL)
- {
- bool need_delim = false;
-
- CHECK_FOR_INTERRUPTS();
-
- if (binary)
- {
- /* Binary per-tuple header */
- int16 fld_count = attr_count;
-
- 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
- {
- /* Text format has no per-tuple header, but send OID if wanted */
- if (oids)
- {
- string = DatumGetCString(DirectFunctionCall1(oidout,
- ObjectIdGetDatum(tuple->t_data->t_oid)));
- CopySendString(string, fp);
- pfree(string);
- need_delim = true;
- }
- }
-
- for (i = 0; i < attr_count; i++)
- {
- Datum origvalue,
- value;
- bool isnull;
-
- origvalue = heap_getattr(tuple, i + 1, tupDesc, &isnull);
-
- if (!binary)
- {
- if (need_delim)
- CopySendChar(delim[0], fp);
- need_delim = true;
- }
-
- if (isnull)
- {
- if (!binary)
- {
- CopySendString(null_print, fp); /* null indicator */
- }
- else
- {
- fld_size = 0; /* null marker */
- CopySendData(&fld_size, sizeof(int16), fp);
- }
- }
- else
- {
- /*
- * 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).
- */
- if (isvarlena[i])
- value = PointerGetDatum(PG_DETOAST_DATUM(origvalue));
- else
- value = origvalue;
-
- if (!binary)
- {
- string = DatumGetCString(FunctionCall3(&out_functions[i],
- value,
- ObjectIdGetDatum(elements[i]),
- Int32GetDatum(attr[i]->atttypmod)));
- CopyAttributeOut(fp, string, delim);
- pfree(string);
- }
- 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;
-
- /*
- * We need this horsing around because we don't
- * know how shorter data values are aligned within
- * a Datum.
- */
- store_att_byval(&datumBuf, value, fld_size);
- CopySendData(&datumBuf,
- fld_size,
- fp);
- }
- }
-
- /* Clean up detoasted copy, if any */
- if (value != origvalue)
- pfree(DatumGetPointer(value));
- }
- }
-
- if (!binary)
- CopySendChar('\n', fp);
- }
-
- heap_endscan(scandesc);
-
- if (binary)
- {
- /* Generate trailer for a binary copy */
- int16 fld_count = -1;
-
- CopySendData(&fld_count, sizeof(int16), fp);
- }
-
- pfree(out_functions);
- pfree(elements);
- pfree(isvarlena);
-}
-
-
-/*
- * Copy FROM file to relation.
- */
-static void
-CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
- char *delim, char *null_print)
-{
- HeapTuple tuple;
- TupleDesc tupDesc;
- Form_pg_attribute *attr;
- AttrNumber attr_count;
- FmgrInfo *in_functions;
- Oid *elements;
- int i;
- Oid in_func_oid;
- Datum *values;
- char *nulls;
- bool isnull;
- int done = 0;
- char *string;
- ResultRelInfo *resultRelInfo;
- EState *estate = CreateExecutorState(); /* for ExecConstraints() */
- TupleTable tupleTable;
- TupleTableSlot *slot;
- Oid loaded_oid = InvalidOid;
- bool skip_tuple = false;
- bool file_has_oids;
-
- tupDesc = RelationGetDescr(rel);
- attr = tupDesc->attrs;
- attr_count = tupDesc->natts;
-
- /*
- * We need a ResultRelInfo so we can use the regular executor's
- * index-entry-making machinery. (There used to be a huge amount of
- * code here that basically duplicated execUtils.c ...)
- */
- resultRelInfo = makeNode(ResultRelInfo);
- resultRelInfo->ri_RangeTableIndex = 1; /* dummy */
- resultRelInfo->ri_RelationDesc = rel;
- resultRelInfo->ri_TrigDesc = rel->trigdesc;
-
- ExecOpenIndices(resultRelInfo);
-
- estate->es_result_relations = resultRelInfo;
- estate->es_num_result_relations = 1;
- estate->es_result_relation_info = resultRelInfo;
-
- /* Set up a dummy tuple table too */
- tupleTable = ExecCreateTupleTable(1);
- slot = ExecAllocTableSlot(tupleTable);
- ExecSetSlotDescriptor(slot, tupDesc, false);
-
- if (!binary)
- {
- in_functions = (FmgrInfo *) palloc(attr_count * sizeof(FmgrInfo));
- elements = (Oid *) palloc(attr_count * sizeof(Oid));
- for (i = 0; i < attr_count; i++)
- {
- in_func_oid = (Oid) GetInputFunction(attr[i]->atttypid);
- fmgr_info(in_func_oid, &in_functions[i]);
- elements[i] = GetTypeElement(attr[i]->atttypid);
- }
- file_has_oids = oids; /* must rely on user to tell us this... */
- }
- else
- {
- /* 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;
- tmp &= ~(1 << 16);
- 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)");
- }
-
- in_functions = NULL;
- elements = NULL;
- }
-
- /* Silently drop incoming OIDs if table does not have OIDs */
- if (!rel->rd_rel->relhasoids)
- oids = false;
-
- values = (Datum *) palloc(attr_count * sizeof(Datum));
- nulls = (char *) palloc(attr_count * sizeof(char));
-
- copy_lineno = 0;
- fe_eof = false;
-
- while (!done)
- {
- CHECK_FOR_INTERRUPTS();
-
- copy_lineno++;
-
- /* Reset the per-output-tuple exprcontext */
- ResetPerTupleExprContext(estate);
-
- /* Initialize all values for row to NULL */
- MemSet(values, 0, attr_count * sizeof(Datum));
- MemSet(nulls, 'n', attr_count * sizeof(char));
-
- if (!binary)
- {
- int newline = 0;
-
- if (file_has_oids)
- {
- 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 */
- else
- {
- loaded_oid = DatumGetObjectId(DirectFunctionCall1(oidin,
- CStringGetDatum(string)));
- if (loaded_oid == InvalidOid)
- elog(ERROR, "COPY TEXT: Invalid Oid");
- }
- }
-
- for (i = 0; i < attr_count && !done; i++)
- {
- string = CopyReadAttribute(fp, &isnull, delim,
- &newline, null_print);
- if (isnull)
- {
- /* already set values[i] and nulls[i] */
- }
- else if (string == NULL)
- done = 1; /* end of file */
- else
- {
- values[i] = FunctionCall3(&in_functions[i],
- CStringGetDatum(string),
- ObjectIdGetDatum(elements[i]),
- Int32GetDatum(attr[i]->atttypmod));
- nulls[i] = ' ';
- }
- }
- if (!done)
- CopyReadNewline(fp, &newline);
- }
- else
- { /* binary */
- int16 fld_count,
- fld_size;
-
- CopyGetData(&fld_count, sizeof(int16), fp);
- if (CopyGetEof(fp) ||
- fld_count == -1)
- done = 1;
- else
- {
- 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)
- {
- 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");
- if (loaded_oid == InvalidOid)
- elog(ERROR, "COPY BINARY: Invalid Oid");
- }
-
- for (i = 0; i < (int) fld_count; i++)
- {
- CopyGetData(&fld_size, sizeof(int16), fp);
- if (CopyGetEof(fp))
- elog(ERROR, "COPY BINARY: unexpected EOF");
- if (fld_size == 0)
- continue; /* it's NULL; nulls[i] already set */
- if (fld_size != attr[i]->attlen)
- elog(ERROR, "COPY BINARY: sizeof(field %d) is %d, expected %d",
- i + 1, (int) fld_size, (int) attr[i]->attlen);
- if (fld_size == -1)
- {
- /* varlena field */
- int32 varlena_size;
- Pointer varlena_ptr;
-
- 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 */
- Pointer refval_ptr;
-
- 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;
-
- /*
- * We need this horsing around because we don't
- * know how shorter data values are aligned within
- * a Datum.
- */
- 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);
- }
-
- nulls[i] = ' ';
- }
- }
- }
-
- if (done)
- break;
-
- tuple = heap_formtuple(tupDesc, values, nulls);
-
- if (oids && file_has_oids)
- tuple->t_data->t_oid = loaded_oid;
-
- skip_tuple = false;
-
- /* BEFORE ROW INSERT Triggers */
- if (resultRelInfo->ri_TrigDesc &&
- resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0)
- {
- HeapTuple newtuple;
-
- newtuple = ExecBRInsertTriggers(estate, resultRelInfo, tuple);
-
- if (newtuple == NULL) /* "do nothing" */
- skip_tuple = true;
- else if (newtuple != tuple) /* modified by Trigger(s) */
- {
- heap_freetuple(tuple);
- tuple = newtuple;
- }
- }
-
- if (!skip_tuple)
- {
- ExecStoreTuple(tuple, slot, InvalidBuffer, false);
-
- /*
- * Check the constraints of the tuple
- */
- if (rel->rd_att->constr)
- ExecConstraints("CopyFrom", resultRelInfo, slot, estate);
-
- /*
- * OK, store the tuple and create index entries for it
- */
- simple_heap_insert(rel, tuple);
-
- if (resultRelInfo->ri_NumIndices > 0)
- ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
-
- /* AFTER ROW INSERT Triggers */
- if (resultRelInfo->ri_TrigDesc)
- ExecARInsertTriggers(estate, resultRelInfo, tuple);
- }
-
- for (i = 0; i < attr_count; i++)
- {
- if (!attr[i]->attbyval && nulls[i] != 'n')
- pfree(DatumGetPointer(values[i]));
- }
-
- heap_freetuple(tuple);
- }
-
- /*
- * Done, clean up
- */
- copy_lineno = 0;
-
- pfree(values);
- pfree(nulls);
-
- if (!binary)
- {
- pfree(in_functions);
- pfree(elements);
- }
-
- ExecDropTupleTable(tupleTable, true);
-
- ExecCloseIndices(resultRelInfo);
-}
-
-
-static Oid
-GetInputFunction(Oid type)
-{
- HeapTuple typeTuple;
- 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;
-}
-
-static Oid
-GetTypeElement(Oid type)
-{
- HeapTuple typeTuple;
- 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;
-}
-
-
-/*
- * Reads input from fp until an end of line is seen.
- */
-
-static void
-CopyReadNewline(FILE *fp, int *newline)
-{
- if (!*newline)
- {
- elog(WARNING, "CopyReadNewline: extra fields ignored");
- while (!CopyGetEof(fp) && (CopyGetChar(fp) != '\n'));
- }
- *newline = 0;
-}
-
-/*
- * Read the value of a single attribute.
- *
- * Result is either a string, or NULL (if EOF or a null attribute).
- * Note that the caller should not pfree the string!
- *
- * *isnull is set true if a null attribute, else false.
- * delim is the column delimiter string (currently always 1 character).
- * *newline remembers whether we've seen a newline ending this tuple.
- * null_print says how NULL values are represented
- */
-
-static char *
-CopyReadAttribute(FILE *fp, bool *isnull, char *delim, int *newline, char *null_print)
-{
- int c;
- int delimc = (unsigned char)delim[0];
-
-#ifdef MULTIBYTE
- int mblen;
- unsigned char s[2];
- char *cvt;
- int j;
-
- s[1] = 0;
-#endif
-
- /* reset attribute_buf to empty */
- attribute_buf.len = 0;
- attribute_buf.data[0] = '\0';
-
- /* if last delimiter was a newline return a NULL attribute */
- if (*newline)
- {
- *isnull = (bool) true;
- return NULL;
- }
-
- *isnull = (bool) false; /* set default */
-
- for (;;)
- {
- c = CopyGetChar(fp);
- if (c == EOF)
- goto endOfFile;
- if (c == '\n')
- {
- *newline = 1;
- break;
- }
- if (c == delimc)
- break;
- if (c == '\\')
- {
- c = CopyGetChar(fp);
- if (c == EOF)
- goto endOfFile;
- switch (c)
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- {
- int val;
-
- val = OCTVALUE(c);
- c = CopyPeekChar(fp);
- if (ISOCTAL(c))
- {
- val = (val << 3) + OCTVALUE(c);
- CopyDonePeek(fp, c, true /* pick up */ );
- c = CopyPeekChar(fp);
- if (ISOCTAL(c))
- {
- val = (val << 3) + OCTVALUE(c);
- CopyDonePeek(fp, c, true /* pick up */ );
- }
- else
- {
- if (c == EOF)
- goto endOfFile;
- CopyDonePeek(fp, c, false /* put back */ );
- }
- }
- else
- {
- if (c == EOF)
- goto endOfFile;
- CopyDonePeek(fp, c, false /* put back */ );
- }
- c = val & 0377;
- }
- break;
-
- /*
- * 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;
- 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 '.':
- c = CopyGetChar(fp);
- if (c != '\n')
- elog(ERROR, "CopyReadAttribute: end of record marker corrupted");
- goto endOfFile;
- }
- }
- appendStringInfoCharMacro(&attribute_buf, c);
-#ifdef MULTIBYTE
- /* XXX shouldn't this be done even when encoding is the same? */
- if (client_encoding != server_encoding)
- {
- /* 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);
- }
- }
-#endif
- }
-
-#ifdef MULTIBYTE
- if (client_encoding != server_encoding)
- {
- 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);
- }
- }
-#endif
-
- if (strcmp(attribute_buf.data, null_print) == 0)
- *isnull = true;
-
- return attribute_buf.data;
-
-endOfFile:
- return NULL;
-}
-
-static void
-CopyAttributeOut(FILE *fp, char *server_string, char *delim)
-{
- char *string;
- char c;
- char delimc = delim[0];
-
-#ifdef MULTIBYTE
- bool same_encoding;
- char *string_start;
- int mblen;
- int i;
-#endif
-
-#ifdef MULTIBYTE
- same_encoding = (server_encoding == client_encoding);
- if (!same_encoding)
- {
- string = (char *) pg_server_to_client((unsigned char *) server_string,
- strlen(server_string));
- string_start = string;
- }
- else
- {
- string = server_string;
- string_start = NULL;
- }
-#else
- string = server_string;
-#endif
-
-#ifdef MULTIBYTE
- for (; (c = *string) != '\0'; string += mblen)
-#else
- for (; (c = *string) != '\0'; string++)
-#endif
- {
-#ifdef MULTIBYTE
- mblen = 1;
-#endif
- 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;
- }
- }
-
-#ifdef MULTIBYTE
- if (string_start)
- pfree(string_start); /* pfree pg_server_to_client result */
-#endif
-}