|
|
|
|
@ -7,18 +7,18 @@ |
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* IDENTIFICATION |
|
|
|
|
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.124 2000/11/16 22:30:19 tgl Exp $ |
|
|
|
|
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.125 2000/12/02 20:49:24 tgl Exp $ |
|
|
|
|
* |
|
|
|
|
*------------------------------------------------------------------------- |
|
|
|
|
*/ |
|
|
|
|
#include "postgres.h" |
|
|
|
|
|
|
|
|
|
#include <unistd.h> |
|
|
|
|
#include <sys/stat.h> |
|
|
|
|
|
|
|
|
|
#include "postgres.h" |
|
|
|
|
|
|
|
|
|
#include "access/genam.h" |
|
|
|
|
#include "access/heapam.h" |
|
|
|
|
#include "access/printtup.h" |
|
|
|
|
#include "catalog/catname.h" |
|
|
|
|
#include "catalog/index.h" |
|
|
|
|
#include "catalog/pg_index.h" |
|
|
|
|
@ -47,13 +47,11 @@ |
|
|
|
|
/* 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 GetOutputFunction(Oid type); |
|
|
|
|
static Oid GetInputFunction(Oid type); |
|
|
|
|
static Oid GetTypeElement(Oid type); |
|
|
|
|
static bool IsTypeByVal(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 int CountTuples(Relation relation); |
|
|
|
|
|
|
|
|
|
@ -61,7 +59,7 @@ static int CountTuples(Relation relation); |
|
|
|
|
* Static communication variables ... pretty grotty, but COPY has |
|
|
|
|
* never been reentrant... |
|
|
|
|
*/ |
|
|
|
|
int lineno = 0; /* used by elog() -- dz */ |
|
|
|
|
int lineno = 0; /* exported for use by elog() -- dz */ |
|
|
|
|
static bool fe_eof; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
@ -344,7 +342,11 @@ DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe, |
|
|
|
|
{ |
|
|
|
|
mode_t oumask; /* Pre-existing umask value */ |
|
|
|
|
|
|
|
|
|
if (*filename != '/') |
|
|
|
|
/*
|
|
|
|
|
* 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."); |
|
|
|
|
|
|
|
|
|
@ -382,27 +384,22 @@ DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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; |
|
|
|
|
|
|
|
|
|
int32 attr_count, |
|
|
|
|
int attr_count, |
|
|
|
|
i; |
|
|
|
|
|
|
|
|
|
#ifdef _DROP_COLUMN_HACK__ |
|
|
|
|
bool *valid; |
|
|
|
|
|
|
|
|
|
#endif /* _DROP_COLUMN_HACK__ */ |
|
|
|
|
Form_pg_attribute *attr; |
|
|
|
|
FmgrInfo *out_functions; |
|
|
|
|
Oid out_func_oid; |
|
|
|
|
Oid *elements; |
|
|
|
|
bool *isvarlena; |
|
|
|
|
int32 *typmod; |
|
|
|
|
Datum value; |
|
|
|
|
bool isnull; /* The attribute we are copying is null */ |
|
|
|
|
char *nulls; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
@ -413,47 +410,39 @@ CopyTo(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null_p |
|
|
|
|
* <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); |
|
|
|
|
|
|
|
|
|
tupDesc = rel->rd_att; |
|
|
|
|
attr_count = rel->rd_att->natts; |
|
|
|
|
attr = rel->rd_att->attrs; |
|
|
|
|
tupDesc = rel->rd_att; |
|
|
|
|
|
|
|
|
|
/* 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)); |
|
|
|
|
typmod = (int32 *) palloc(attr_count * sizeof(int32)); |
|
|
|
|
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]); |
|
|
|
|
typmod[i] = attr[i]->atttypmod; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!binary) |
|
|
|
|
{ |
|
|
|
|
out_functions = (FmgrInfo *) palloc(attr_count * sizeof(FmgrInfo)); |
|
|
|
|
elements = (Oid *) palloc(attr_count * sizeof(Oid)); |
|
|
|
|
typmod = (int32 *) palloc(attr_count * sizeof(int32)); |
|
|
|
|
#ifdef _DROP_COLUMN_HACK__ |
|
|
|
|
valid = (bool *) palloc(attr_count * sizeof(bool)); |
|
|
|
|
#endif /* _DROP_COLUMN_HACK__ */ |
|
|
|
|
for (i = 0; i < attr_count; i++) |
|
|
|
|
{ |
|
|
|
|
#ifdef _DROP_COLUMN_HACK__ |
|
|
|
|
if (COLUMN_IS_DROPPED(attr[i])) |
|
|
|
|
{ |
|
|
|
|
valid[i] = false; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
valid[i] = true; |
|
|
|
|
#endif /* _DROP_COLUMN_HACK__ */ |
|
|
|
|
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; |
|
|
|
|
int32 ntuples; |
|
|
|
|
|
|
|
|
|
nulls = (char *) palloc(attr_count); |
|
|
|
|
for (i = 0; i < attr_count; i++) |
|
|
|
|
nulls[i] = ' '; |
|
|
|
|
@ -480,18 +469,31 @@ CopyTo(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null_p |
|
|
|
|
|
|
|
|
|
for (i = 0; i < attr_count; i++) |
|
|
|
|
{ |
|
|
|
|
value = heap_getattr(tuple, i + 1, tupDesc, &isnull); |
|
|
|
|
if (!binary) |
|
|
|
|
Datum origvalue, |
|
|
|
|
value; |
|
|
|
|
bool isnull; |
|
|
|
|
|
|
|
|
|
origvalue = heap_getattr(tuple, i + 1, tupDesc, &isnull); |
|
|
|
|
|
|
|
|
|
if (isnull) |
|
|
|
|
{ |
|
|
|
|
#ifdef _DROP_COLUMN_HACK__ |
|
|
|
|
if (!valid[i]) |
|
|
|
|
{ |
|
|
|
|
if (i == attr_count - 1) |
|
|
|
|
CopySendChar('\n', fp); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
#endif /* _DROP_COLUMN_HACK__ */ |
|
|
|
|
if (!isnull) |
|
|
|
|
if (!binary) |
|
|
|
|
CopySendString(null_print, fp); /* null indicator */ |
|
|
|
|
else |
|
|
|
|
nulls[i] = 'n'; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
/*
|
|
|
|
|
* If we have a toasted datum, forcibly detoast it to avoid |
|
|
|
|
* memory leakage inside the type's output routine. |
|
|
|
|
*/ |
|
|
|
|
if (isvarlena[i]) |
|
|
|
|
value = PointerGetDatum(PG_DETOAST_DATUM(origvalue)); |
|
|
|
|
else |
|
|
|
|
value = origvalue; |
|
|
|
|
|
|
|
|
|
if (!binary) |
|
|
|
|
{ |
|
|
|
|
string = DatumGetCString(FunctionCall3(&out_functions[i], |
|
|
|
|
value, |
|
|
|
|
@ -500,9 +502,14 @@ CopyTo(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null_p |
|
|
|
|
CopyAttributeOut(fp, string, delim); |
|
|
|
|
pfree(string); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
CopySendString(null_print, fp); /* null indicator */ |
|
|
|
|
|
|
|
|
|
/* Clean up detoasted copy, if any */ |
|
|
|
|
if (value != origvalue) |
|
|
|
|
pfree(DatumGetPointer(value)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!binary) |
|
|
|
|
{ |
|
|
|
|
if (i == attr_count - 1) |
|
|
|
|
CopySendChar('\n', fp); |
|
|
|
|
else |
|
|
|
|
@ -515,16 +522,6 @@ CopyTo(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null_p |
|
|
|
|
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) |
|
|
|
|
@ -561,16 +558,19 @@ CopyTo(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null_p |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
heap_endscan(scandesc); |
|
|
|
|
|
|
|
|
|
pfree(out_functions); |
|
|
|
|
pfree(elements); |
|
|
|
|
pfree(isvarlena); |
|
|
|
|
pfree(typmod); |
|
|
|
|
if (binary) |
|
|
|
|
pfree(nulls); |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
pfree(out_functions); |
|
|
|
|
pfree(elements); |
|
|
|
|
pfree(typmod); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Copy FROM file to relation. |
|
|
|
|
*/ |
|
|
|
|
static void |
|
|
|
|
CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, |
|
|
|
|
char *delim, char *null_print) |
|
|
|
|
@ -635,10 +635,6 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, |
|
|
|
|
typmod = (int32 *) palloc(attr_count * sizeof(int32)); |
|
|
|
|
for (i = 0; i < attr_count; i++) |
|
|
|
|
{ |
|
|
|
|
#ifdef _DROP_COLUMN_HACK__ |
|
|
|
|
if (COLUMN_IS_DROPPED(attr[i])) |
|
|
|
|
continue; |
|
|
|
|
#endif /* _DROP_COLUMN_HACK__ */ |
|
|
|
|
in_func_oid = (Oid) GetInputFunction(attr[i]->atttypid); |
|
|
|
|
fmgr_info(in_func_oid, &in_functions[i]); |
|
|
|
|
elements[i] = GetTypeElement(attr[i]->atttypid); |
|
|
|
|
@ -662,13 +658,6 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, |
|
|
|
|
for (i = 0; i < attr_count; i++) |
|
|
|
|
{ |
|
|
|
|
nulls[i] = ' '; |
|
|
|
|
#ifdef _DROP_COLUMN_HACK__ |
|
|
|
|
if (COLUMN_IS_DROPPED(attr[i])) |
|
|
|
|
{ |
|
|
|
|
byval[i] = 'n'; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
#endif /* _DROP_COLUMN_HACK__ */ |
|
|
|
|
byval[i] = IsTypeByVal(attr[i]->atttypid); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -704,14 +693,6 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, |
|
|
|
|
} |
|
|
|
|
for (i = 0; i < attr_count && !done; i++) |
|
|
|
|
{ |
|
|
|
|
#ifdef _DROP_COLUMN_HACK__ |
|
|
|
|
if (COLUMN_IS_DROPPED(attr[i])) |
|
|
|
|
{ |
|
|
|
|
values[i] = PointerGetDatum(NULL); |
|
|
|
|
nulls[i] = 'n'; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
#endif /* _DROP_COLUMN_HACK__ */ |
|
|
|
|
string = CopyReadAttribute(fp, &isnull, delim, &newline, null_print); |
|
|
|
|
if (isnull) |
|
|
|
|
{ |
|
|
|
|
@ -889,22 +870,6 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static Oid |
|
|
|
|
GetOutputFunction(Oid type) |
|
|
|
|
{ |
|
|
|
|
HeapTuple typeTuple; |
|
|
|
|
Oid result; |
|
|
|
|
|
|
|
|
|
typeTuple = SearchSysCache(TYPEOID, |
|
|
|
|
ObjectIdGetDatum(type), |
|
|
|
|
0, 0, 0); |
|
|
|
|
if (!HeapTupleIsValid(typeTuple)) |
|
|
|
|
elog(ERROR, "GetOutputFunction: Cache lookup of type %u failed", type); |
|
|
|
|
result = ((Form_pg_type) GETSTRUCT(typeTuple))->typoutput; |
|
|
|
|
ReleaseSysCache(typeTuple); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static Oid |
|
|
|
|
GetInputFunction(Oid type) |
|
|
|
|
{ |
|
|
|
|
|