📄 copy.c
字号:
/*------------------------------------------------------------------------- * * copy.c * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /usr/local/cvsroot/pgsql/src/backend/commands/copy.c,v 1.81 1999/07/03 00:32:39 momjian Exp $ * *------------------------------------------------------------------------- */#include <string.h>#include <unistd.h>#include <postgres.h>#include <access/heapam.h>#include <tcop/dest.h>#include <fmgr.h>#include <miscadmin.h>#include <utils/builtins.h>#include <utils/acl.h>#include <sys/stat.h>#include <catalog/pg_index.h>#include <utils/syscache.h>#include <utils/memutils.h>#include <executor/executor.h>#include <access/transam.h>#include <catalog/index.h>#include <access/genam.h>#include <catalog/pg_type.h>#include <catalog/catname.h>#include <catalog/pg_shadow.h>#include <commands/copy.h>#include "commands/trigger.h"#include <storage/fd.h>#include <libpq/libpq.h>#ifdef MULTIBYTE#include "mb/pg_wchar.h"#endif#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))#define VALUE(c) ((c) - '0')/* non-export function prototypes */static void CopyTo(Relation rel, bool binary, bool oids, FILE *fp, char *delim);static void CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim);static Oid GetOutputFunction(Oid type);static Oid GetTypeElement(Oid type);static Oid GetInputFunction(Oid type);static Oid IsTypeByVal(Oid type);static void GetIndexRelations(Oid main_relation_oid, int *n_indices, Relation **index_rels);#ifdef COPY_PATCHstatic void CopyReadNewline(FILE *fp, int *newline);static char *CopyReadAttribute(FILE *fp, bool *isnull, char *delim, int *newline);#elsestatic char *CopyReadAttribute(FILE *fp, bool *isnull, char *delim);#endifstatic void CopyAttributeOut(FILE *fp, char *string, char *delim, int is_array);static int CountTuples(Relation relation);static int lineno;/* * Internal communications functions */static void CopySendData(void *databuf, int datasize, FILE *fp);static void CopySendString(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, int 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 voidCopySendData(void *databuf, int datasize, FILE *fp){ if (!fp) pq_putbytes((char *) databuf, datasize); else fwrite(databuf, datasize, 1, fp);}static voidCopySendString(char *str, FILE *fp){ CopySendData(str, strlen(str), fp);}static voidCopySendChar(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 * * NB: no data conversion is applied by these functions */static voidCopyGetData(void *databuf, int datasize, FILE *fp){ if (!fp) pq_getbytes((char *) databuf, datasize); else fread(databuf, datasize, 1, fp);}static intCopyGetChar(FILE *fp){ if (!fp) { unsigned char ch; if (pq_getbytes((char *) &ch, 1)) return EOF; return ch; } else return getc(fp);}static intCopyGetEof(FILE *fp){ if (!fp) return 0; /* Never return EOF when talking to * frontend ? */ else return feof(fp);}/* * CopyPeekChar reads a byte in "peekable" mode. * after each call to CopyPeekChar, a call to CopyDonePeek _must_ * follow. * CopyDonePeek will either take the peeked char off the steam * (if pickup is != 0) or leave it on the stream (if pickup == 0) */static intCopyPeekChar(FILE *fp){ if (!fp) return pq_peekbyte(); else return getc(fp);}static voidCopyDonePeek(FILE *fp, int c, int pickup){ if (!fp) { if (pickup) { /* * We want to pick it up - just receive again into dummy * buffer */ char c; pq_getbytes(&c, 1); } /* 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 there */ }}/* * DoCopy executes a the SQL COPY statement. */voidDoCopy(char *relname, bool binary, bool oids, bool from, bool pipe, char *filename, char *delim){/*---------------------------------------------------------------------------- Either unload or reload contents of class <relname>, depending on <from>. If <pipe> is false, transfer is between the class and the file named <filename>. Otherwise, transfer is between the class 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. If in the text format, delimit columns with delimiter <delim>. 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. Iff <oids>, unload or reload the format that includes OID information. 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 class.----------------------------------------------------------------------------*/ FILE *fp; Relation rel; extern char *UserName; /* defined in global.c */ const AclMode required_access = from ? ACL_WR : ACL_RD; int result; rel = heap_openr(relname); if (rel == NULL) elog(ERROR, "COPY command failed. Class %s " "does not exist.", relname); result = pg_aclcheck(relname, UserName, required_access); if (result != ACLCHECK_OK) elog(ERROR, "%s: %s", relname, aclcheck_error_strings[result]); /* Above should not return */ else if (!superuser() && !pipe) 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."); /* Above should not return. */ else { if (from) { /* copy from file to database */ if (rel->rd_rel->relkind == RELKIND_SEQUENCE) elog(ERROR, "You can't change sequence relation %s", relname); if (pipe) { if (IsUnderPostmaster) { ReceiveCopyBegin(); fp = NULL; } else fp = stdin; } else {#ifndef __CYGWIN32__ fp = AllocateFile(filename, "r");#else fp = AllocateFile(filename, "rb");#endif if (fp == NULL) elog(ERROR, "COPY command, running in backend with " "effective uid %d, could not open file '%s' for " "reading. Errno = %s (%d).", geteuid(), filename, strerror(errno), errno); } CopyFrom(rel, binary, oids, fp, delim); } else { /* copy from database to file */ if (pipe) { if (IsUnderPostmaster) { SendCopyBegin(); pq_startcopyout(); fp = NULL; } else fp = stdout; } else { mode_t oumask; /* Pre-existing umask value */ oumask = umask((mode_t) 0);#ifndef __CYGWIN32__ fp = AllocateFile(filename, "w");#else fp = AllocateFile(filename, "wb");#endif 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).", geteuid(), filename, strerror(errno), errno); } CopyTo(rel, binary, oids, fp, delim); } if (!pipe) { FreeFile(fp); } else if (!from) { if (!binary) CopySendData("\\.\n", 3, fp); if (IsUnderPostmaster) pq_endcopyout(false); } }}static voidCopyTo(Relation rel, bool binary, bool oids, FILE *fp, char *delim){ HeapTuple tuple; HeapScanDesc scandesc; int32 attr_count, i; Form_pg_attribute *attr; FmgrInfo *out_functions; Oid out_func_oid; Oid *elements; int32 *typmod; Datum value; bool isnull; /* The attribute we are copying is null */ char *nulls; /* * <nulls> is a (dynamically allocated) array with one character per * attribute in the instance being copied. nulls[I-1] is 'n' if * Attribute Number I is null, and ' ' otherwise. * * <nulls> is meaningful only if we are doing a binary copy. */ char *string; int32 ntuples; TupleDesc tupDesc; scandesc = heap_beginscan(rel, 0, QuerySnapshot, 0, NULL); attr_count = rel->rd_att->natts; attr = rel->rd_att->attrs; tupDesc = rel->rd_att; if (!binary) { out_functions = (FmgrInfo *) palloc(attr_count * sizeof(FmgrInfo)); elements = (Oid *) palloc(attr_count * sizeof(Oid)); typmod = (int32 *) palloc(attr_count * sizeof(int32)); for (i = 0; i < attr_count; i++) { out_func_oid = (Oid) GetOutputFunction(attr[i]->atttypid); fmgr_info(out_func_oid, &out_functions[i]); elements[i] = GetTypeElement(attr[i]->atttypid); typmod[i] = attr[i]->atttypmod; } nulls = NULL; /* meaningless, but compiler doesn't know * that */ } else { elements = NULL; typmod = NULL; out_functions = NULL; nulls = (char *) palloc(attr_count); for (i = 0; i < attr_count; i++) nulls[i] = ' '; /* XXX expensive */ ntuples = CountTuples(rel); CopySendData(&ntuples, sizeof(int32), fp); } while (HeapTupleIsValid(tuple = heap_getnext(scandesc, 0))) { if (oids && !binary) { CopySendString(oidout(tuple->t_data->t_oid), fp); CopySendChar(delim[0], fp); } for (i = 0; i < attr_count; i++) { value = heap_getattr(tuple, i + 1, tupDesc, &isnull); if (!binary) { if (!isnull) { string = (char *) (*fmgr_faddr(&out_functions[i])) (value, elements[i], typmod[i]); CopyAttributeOut(fp, string, delim, attr[i]->attnelems); pfree(string); } else CopySendString("\\N", fp); /* null indicator */ if (i == attr_count - 1) CopySendChar('\n', fp); else { /* * when copying out, only use the first char of the * delim string */ CopySendChar(delim[0], fp); } } else { /* * only interesting thing heap_getattr tells us in this * case is if we have a null attribute or not. */ if (isnull) nulls[i] = 'n'; } } if (binary)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -