mirror of
https://github.com/postgres/postgres.git
synced 2025-07-07 00:36:50 +03:00
The attached patch (against HEAD) implements
COPY x (a,d,c,b) from stdin; COPY x (a,c) to stdout; as well as the corresponding changes to pg_dump to use the new functionality. This functionality is not available when using the BINARY option. If a column is not specified in the COPY FROM statement, its default values will be used. In addition to this functionality, I tweaked a couple of the error messages emitted by the new COPY <options> checks. Brent Verner
This commit is contained in:
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$Header: /cvsroot/pgsql/doc/src/sgml/ref/copy.sgml,v 1.32 2002/06/20 16:00:43 momjian Exp $
|
$Header: /cvsroot/pgsql/doc/src/sgml/ref/copy.sgml,v 1.33 2002/07/18 04:43:50 momjian Exp $
|
||||||
PostgreSQL documentation
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -22,6 +22,7 @@ PostgreSQL documentation
|
|||||||
</refsynopsisdivinfo>
|
</refsynopsisdivinfo>
|
||||||
<synopsis>
|
<synopsis>
|
||||||
COPY <replaceable class="parameter">table</replaceable>
|
COPY <replaceable class="parameter">table</replaceable>
|
||||||
|
[ ( <replaceable class="parameter">column</replaceable> [, ...] ) ]
|
||||||
FROM { '<replaceable class="parameter">filename</replaceable>' | <filename>stdin</filename> }
|
FROM { '<replaceable class="parameter">filename</replaceable>' | <filename>stdin</filename> }
|
||||||
[ [ WITH ]
|
[ [ WITH ]
|
||||||
[ BINARY ]
|
[ BINARY ]
|
||||||
@ -29,6 +30,7 @@ COPY <replaceable class="parameter">table</replaceable>
|
|||||||
[ DELIMITER [ AS ] '<replaceable class="parameter">delimiter</replaceable>' ]
|
[ DELIMITER [ AS ] '<replaceable class="parameter">delimiter</replaceable>' ]
|
||||||
[ NULL [ AS ] '<replaceable class="parameter">null string</replaceable>' ] ]
|
[ NULL [ AS ] '<replaceable class="parameter">null string</replaceable>' ] ]
|
||||||
COPY <replaceable class="parameter">table</replaceable>
|
COPY <replaceable class="parameter">table</replaceable>
|
||||||
|
[ ( <replaceable class="parameter">column</replaceable> [, ...] ) ]
|
||||||
TO { '<replaceable class="parameter">filename</replaceable>' | <filename>stdout</filename> }
|
TO { '<replaceable class="parameter">filename</replaceable>' | <filename>stdout</filename> }
|
||||||
[ [ WITH ]
|
[ [ WITH ]
|
||||||
[ BINARY ]
|
[ BINARY ]
|
||||||
@ -56,6 +58,16 @@ COPY <replaceable class="parameter">table</replaceable>
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><replaceable class="parameter">column list</replaceable></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
An optional list of columns to be copied. If no column list is
|
||||||
|
specified, all columns will be used.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><replaceable class="parameter">filename</replaceable></term>
|
<term><replaceable class="parameter">filename</replaceable></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
@ -187,6 +199,14 @@ ERROR: <replaceable>reason</replaceable>
|
|||||||
whatever is in the table already).
|
whatever is in the table already).
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
When using the optional column list syntax, <command>COPY TO</command>
|
||||||
|
and <command>COPY FROM</command> will only copy the specified
|
||||||
|
columns' values to/from the table. If a column in the table
|
||||||
|
is not in the column list, <command>COPY FROM</command> will insert
|
||||||
|
default values for that column if a default value is defined.
|
||||||
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
<command>COPY</command> with a file name instructs the
|
<command>COPY</command> with a file name instructs the
|
||||||
<productname>PostgreSQL</productname> backend to directly read from
|
<productname>PostgreSQL</productname> backend to directly read from
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.158 2002/06/20 20:29:27 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.159 2002/07/18 04:43:50 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -28,6 +28,7 @@
|
|||||||
#include "commands/copy.h"
|
#include "commands/copy.h"
|
||||||
#include "commands/trigger.h"
|
#include "commands/trigger.h"
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
|
#include "rewrite/rewriteHandler.h"
|
||||||
#include "libpq/libpq.h"
|
#include "libpq/libpq.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "tcop/pquery.h"
|
#include "tcop/pquery.h"
|
||||||
@ -46,13 +47,14 @@
|
|||||||
|
|
||||||
|
|
||||||
/* non-export function prototypes */
|
/* non-export function prototypes */
|
||||||
static void CopyTo(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null_print);
|
static void CopyTo(Relation rel, List *attlist, 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 void CopyFrom(Relation rel, List *attlist, bool binary, bool oids, FILE *fp, char *delim, char *null_print);
|
||||||
static Oid GetInputFunction(Oid type);
|
static Oid GetInputFunction(Oid type);
|
||||||
static Oid GetTypeElement(Oid type);
|
static Oid GetTypeElement(Oid type);
|
||||||
static void CopyReadNewline(FILE *fp, int *newline);
|
static void CopyReadNewline(FILE *fp, int *newline);
|
||||||
static char *CopyReadAttribute(FILE *fp, bool *isnull, char *delim, int *newline, char *null_print);
|
static char *CopyReadAttribute(FILE *fp, bool *isnull, char *delim, int *newline, char *null_print);
|
||||||
static void CopyAttributeOut(FILE *fp, char *string, char *delim);
|
static void CopyAttributeOut(FILE *fp, char *string, char *delim);
|
||||||
|
static void CopyAssertAttlist(Relation rel, List* attlist, bool from);
|
||||||
|
|
||||||
static const char BinarySignature[12] = "PGBCOPY\n\377\r\n\0";
|
static const char BinarySignature[12] = "PGBCOPY\n\377\r\n\0";
|
||||||
|
|
||||||
@ -268,6 +270,7 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
bool is_from = stmt->is_from;
|
bool is_from = stmt->is_from;
|
||||||
bool pipe = (stmt->filename == NULL);
|
bool pipe = (stmt->filename == NULL);
|
||||||
List *option;
|
List *option;
|
||||||
|
List *attlist = stmt->attlist;
|
||||||
DefElem *dbinary = NULL;
|
DefElem *dbinary = NULL;
|
||||||
DefElem *doids = NULL;
|
DefElem *doids = NULL;
|
||||||
DefElem *ddelim = NULL;
|
DefElem *ddelim = NULL;
|
||||||
@ -289,25 +292,27 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
if (strcmp(defel->defname, "binary") == 0)
|
if (strcmp(defel->defname, "binary") == 0)
|
||||||
{
|
{
|
||||||
if (dbinary)
|
if (dbinary)
|
||||||
elog(ERROR, "COPY: conflicting options");
|
/* should this really be an error? */
|
||||||
|
elog(ERROR, "COPY: BINARY option appears more than once");
|
||||||
dbinary = defel;
|
dbinary = defel;
|
||||||
}
|
}
|
||||||
else if (strcmp(defel->defname, "oids") == 0)
|
else if (strcmp(defel->defname, "oids") == 0)
|
||||||
{
|
{
|
||||||
if (doids)
|
if (doids)
|
||||||
elog(ERROR, "COPY: conflicting options");
|
/* should this really be an error? */
|
||||||
|
elog(ERROR, "COPY: OIDS option appears more than once");
|
||||||
doids = defel;
|
doids = defel;
|
||||||
}
|
}
|
||||||
else if (strcmp(defel->defname, "delimiter") == 0)
|
else if (strcmp(defel->defname, "delimiter") == 0)
|
||||||
{
|
{
|
||||||
if (ddelim)
|
if (ddelim)
|
||||||
elog(ERROR, "COPY: conflicting options");
|
elog(ERROR, "COPY: DELIMITER string may only be defined once in query");
|
||||||
ddelim = defel;
|
ddelim = defel;
|
||||||
}
|
}
|
||||||
else if (strcmp(defel->defname, "null") == 0)
|
else if (strcmp(defel->defname, "null") == 0)
|
||||||
{
|
{
|
||||||
if (dnull)
|
if (dnull)
|
||||||
elog(ERROR, "COPY: conflicting options");
|
elog(ERROR, "COPY: NULL representation may only be defined once in query");
|
||||||
dnull = defel;
|
dnull = defel;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -367,6 +372,24 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
server_encoding = GetDatabaseEncoding();
|
server_encoding = GetDatabaseEncoding();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if( attlist == NIL ){
|
||||||
|
/* get list of attributes in the relation */
|
||||||
|
TupleDesc desc = RelationGetDescr(rel);
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < desc->natts; ++i){
|
||||||
|
Ident* id = makeNode(Ident);
|
||||||
|
id->name = NameStr(desc->attrs[i]->attname);
|
||||||
|
attlist = lappend(attlist,id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if( binary ){
|
||||||
|
elog(ERROR,"COPY: BINARY format cannot be used with specific column list");
|
||||||
|
}
|
||||||
|
/* verify that any user-specified attributes exist in the relation */
|
||||||
|
CopyAssertAttlist(rel,attlist,is_from);
|
||||||
|
}
|
||||||
|
|
||||||
if (is_from)
|
if (is_from)
|
||||||
{ /* copy from file to database */
|
{ /* copy from file to database */
|
||||||
if (rel->rd_rel->relkind != RELKIND_RELATION)
|
if (rel->rd_rel->relkind != RELKIND_RELATION)
|
||||||
@ -410,7 +433,7 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
elog(ERROR, "COPY: %s is a directory.", filename);
|
elog(ERROR, "COPY: %s is a directory.", filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CopyFrom(rel, binary, oids, fp, delim, null_print);
|
CopyFrom(rel, attlist, binary, oids, fp, delim, null_print);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{ /* copy from database to file */
|
{ /* copy from database to file */
|
||||||
@ -466,7 +489,7 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
elog(ERROR, "COPY: %s is a directory.", filename);
|
elog(ERROR, "COPY: %s is a directory.", filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CopyTo(rel, binary, oids, fp, delim, null_print);
|
CopyTo(rel, attlist, binary, oids, fp, delim, null_print);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pipe)
|
if (!pipe)
|
||||||
@ -494,8 +517,8 @@ DoCopy(const CopyStmt *stmt)
|
|||||||
* Copy from relation TO file.
|
* Copy from relation TO file.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
CopyTo(Relation rel, bool binary, bool oids, FILE *fp,
|
CopyTo(Relation rel, List *attlist, bool binary, bool oids,
|
||||||
char *delim, char *null_print)
|
FILE *fp, char *delim, char *null_print)
|
||||||
{
|
{
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
TupleDesc tupDesc;
|
TupleDesc tupDesc;
|
||||||
@ -509,6 +532,10 @@ CopyTo(Relation rel, bool binary, bool oids, FILE *fp,
|
|||||||
int16 fld_size;
|
int16 fld_size;
|
||||||
char *string;
|
char *string;
|
||||||
Snapshot mySnapshot;
|
Snapshot mySnapshot;
|
||||||
|
int copy_attr_count;
|
||||||
|
int* attmap;
|
||||||
|
int p = 0;
|
||||||
|
List* cur;
|
||||||
|
|
||||||
if (oids && !rel->rd_rel->relhasoids)
|
if (oids && !rel->rd_rel->relhasoids)
|
||||||
elog(ERROR, "COPY: table %s does not have OIDs",
|
elog(ERROR, "COPY: table %s does not have OIDs",
|
||||||
@ -517,6 +544,18 @@ CopyTo(Relation rel, bool binary, bool oids, FILE *fp,
|
|||||||
tupDesc = rel->rd_att;
|
tupDesc = rel->rd_att;
|
||||||
attr_count = rel->rd_att->natts;
|
attr_count = rel->rd_att->natts;
|
||||||
attr = rel->rd_att->attrs;
|
attr = rel->rd_att->attrs;
|
||||||
|
copy_attr_count = length(attlist);
|
||||||
|
{
|
||||||
|
attmap = (int*)palloc(copy_attr_count * sizeof(int));
|
||||||
|
foreach(cur,attlist){
|
||||||
|
for (i = 0; i < attr_count; i++){
|
||||||
|
if( strcmp(strVal(lfirst(cur)),NameStr(attr[i]->attname)) == 0){
|
||||||
|
attmap[p++] = i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For binary copy we really only need isvarlena, but compute it
|
* For binary copy we really only need isvarlena, but compute it
|
||||||
@ -593,13 +632,14 @@ CopyTo(Relation rel, bool binary, bool oids, FILE *fp,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < attr_count; i++)
|
for (i = 0; i < copy_attr_count; i++)
|
||||||
{
|
{
|
||||||
Datum origvalue,
|
Datum origvalue,
|
||||||
value;
|
value;
|
||||||
bool isnull;
|
bool isnull;
|
||||||
|
int mi = attmap[i];
|
||||||
|
|
||||||
origvalue = heap_getattr(tuple, i + 1, tupDesc, &isnull);
|
origvalue = heap_getattr(tuple, mi + 1, tupDesc, &isnull);
|
||||||
|
|
||||||
if (!binary)
|
if (!binary)
|
||||||
{
|
{
|
||||||
@ -628,25 +668,25 @@ CopyTo(Relation rel, bool binary, bool oids, FILE *fp,
|
|||||||
* (or for binary case, becase we must output untoasted
|
* (or for binary case, becase we must output untoasted
|
||||||
* value).
|
* value).
|
||||||
*/
|
*/
|
||||||
if (isvarlena[i])
|
if (isvarlena[mi])
|
||||||
value = PointerGetDatum(PG_DETOAST_DATUM(origvalue));
|
value = PointerGetDatum(PG_DETOAST_DATUM(origvalue));
|
||||||
else
|
else
|
||||||
value = origvalue;
|
value = origvalue;
|
||||||
|
|
||||||
if (!binary)
|
if (!binary)
|
||||||
{
|
{
|
||||||
string = DatumGetCString(FunctionCall3(&out_functions[i],
|
string = DatumGetCString(FunctionCall3(&out_functions[mi],
|
||||||
value,
|
value,
|
||||||
ObjectIdGetDatum(elements[i]),
|
ObjectIdGetDatum(elements[mi]),
|
||||||
Int32GetDatum(attr[i]->atttypmod)));
|
Int32GetDatum(attr[mi]->atttypmod)));
|
||||||
CopyAttributeOut(fp, string, delim);
|
CopyAttributeOut(fp, string, delim);
|
||||||
pfree(string);
|
pfree(string);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fld_size = attr[i]->attlen;
|
fld_size = attr[mi]->attlen;
|
||||||
CopySendData(&fld_size, sizeof(int16), fp);
|
CopySendData(&fld_size, sizeof(int16), fp);
|
||||||
if (isvarlena[i])
|
if (isvarlena[mi])
|
||||||
{
|
{
|
||||||
/* varlena */
|
/* varlena */
|
||||||
Assert(fld_size == -1);
|
Assert(fld_size == -1);
|
||||||
@ -654,7 +694,7 @@ CopyTo(Relation rel, bool binary, bool oids, FILE *fp,
|
|||||||
VARSIZE(value),
|
VARSIZE(value),
|
||||||
fp);
|
fp);
|
||||||
}
|
}
|
||||||
else if (!attr[i]->attbyval)
|
else if (!attr[mi]->attbyval)
|
||||||
{
|
{
|
||||||
/* fixed-length pass-by-reference */
|
/* fixed-length pass-by-reference */
|
||||||
Assert(fld_size > 0);
|
Assert(fld_size > 0);
|
||||||
@ -709,13 +749,13 @@ CopyTo(Relation rel, bool binary, bool oids, FILE *fp,
|
|||||||
* Copy FROM file to relation.
|
* Copy FROM file to relation.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
|
CopyFrom(Relation rel, List *attlist, bool binary, bool oids,
|
||||||
char *delim, char *null_print)
|
FILE *fp, char *delim, char *null_print)
|
||||||
{
|
{
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
TupleDesc tupDesc;
|
TupleDesc tupDesc;
|
||||||
Form_pg_attribute *attr;
|
Form_pg_attribute *attr;
|
||||||
AttrNumber attr_count;
|
AttrNumber attr_count, copy_attr_count, def_attr_count;
|
||||||
FmgrInfo *in_functions;
|
FmgrInfo *in_functions;
|
||||||
Oid *elements;
|
Oid *elements;
|
||||||
int i;
|
int i;
|
||||||
@ -732,10 +772,17 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
|
|||||||
Oid loaded_oid = InvalidOid;
|
Oid loaded_oid = InvalidOid;
|
||||||
bool skip_tuple = false;
|
bool skip_tuple = false;
|
||||||
bool file_has_oids;
|
bool file_has_oids;
|
||||||
|
int* attmap = NULL;
|
||||||
|
int* defmap = NULL;
|
||||||
|
Node** defexprs = NULL; /* array of default att expressions */
|
||||||
|
ExprContext *econtext; /* used for ExecEvalExpr for default atts */
|
||||||
|
ExprDoneCond isdone;
|
||||||
|
|
||||||
tupDesc = RelationGetDescr(rel);
|
tupDesc = RelationGetDescr(rel);
|
||||||
attr = tupDesc->attrs;
|
attr = tupDesc->attrs;
|
||||||
attr_count = tupDesc->natts;
|
attr_count = tupDesc->natts;
|
||||||
|
copy_attr_count = length(attlist);
|
||||||
|
def_attr_count = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We need a ResultRelInfo so we can use the regular executor's
|
* We need a ResultRelInfo so we can use the regular executor's
|
||||||
@ -758,15 +805,42 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
|
|||||||
slot = ExecAllocTableSlot(tupleTable);
|
slot = ExecAllocTableSlot(tupleTable);
|
||||||
ExecSetSlotDescriptor(slot, tupDesc, false);
|
ExecSetSlotDescriptor(slot, tupDesc, false);
|
||||||
|
|
||||||
|
|
||||||
if (!binary)
|
if (!binary)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* pick up the input function and default expression (if any) for
|
||||||
|
* each attribute in the relation.
|
||||||
|
*/
|
||||||
|
List* cur;
|
||||||
|
attmap = (int*)palloc(sizeof(int) * attr_count);
|
||||||
|
defmap = (int*)palloc(sizeof(int) * attr_count);
|
||||||
|
defexprs = (Node**)palloc(sizeof(Node*) * attr_count);
|
||||||
in_functions = (FmgrInfo *) palloc(attr_count * sizeof(FmgrInfo));
|
in_functions = (FmgrInfo *) palloc(attr_count * sizeof(FmgrInfo));
|
||||||
elements = (Oid *) palloc(attr_count * sizeof(Oid));
|
elements = (Oid *) palloc(attr_count * sizeof(Oid));
|
||||||
for (i = 0; i < attr_count; i++)
|
for (i = 0; i < attr_count; i++)
|
||||||
{
|
{
|
||||||
|
int p = 0;
|
||||||
|
bool specified = false;
|
||||||
in_func_oid = (Oid) GetInputFunction(attr[i]->atttypid);
|
in_func_oid = (Oid) GetInputFunction(attr[i]->atttypid);
|
||||||
fmgr_info(in_func_oid, &in_functions[i]);
|
fmgr_info(in_func_oid, &in_functions[i]);
|
||||||
elements[i] = GetTypeElement(attr[i]->atttypid);
|
elements[i] = GetTypeElement(attr[i]->atttypid);
|
||||||
|
foreach(cur,attlist){
|
||||||
|
if( strcmp(strVal(lfirst(cur)),NameStr(attr[i]->attname)) == 0){
|
||||||
|
attmap[p] = i;
|
||||||
|
specified = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++p;
|
||||||
|
}
|
||||||
|
if( ! specified ){
|
||||||
|
/* column not specified, try to get a default */
|
||||||
|
defexprs[def_attr_count] = build_column_default(rel,i+1);
|
||||||
|
if( defexprs[def_attr_count] != NULL ){
|
||||||
|
defmap[def_attr_count] = i;
|
||||||
|
++def_attr_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
file_has_oids = oids; /* must rely on user to tell us this... */
|
file_has_oids = oids; /* must rely on user to tell us this... */
|
||||||
}
|
}
|
||||||
@ -821,6 +895,8 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
|
|||||||
copy_lineno = 0;
|
copy_lineno = 0;
|
||||||
fe_eof = false;
|
fe_eof = false;
|
||||||
|
|
||||||
|
econtext = GetPerTupleExprContext(estate);
|
||||||
|
|
||||||
while (!done)
|
while (!done)
|
||||||
{
|
{
|
||||||
CHECK_FOR_INTERRUPTS();
|
CHECK_FOR_INTERRUPTS();
|
||||||
@ -855,25 +931,41 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < attr_count && !done; i++)
|
/*
|
||||||
|
* here, we only try to read as many attributes as
|
||||||
|
* were specified.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < copy_attr_count && !done; i++)
|
||||||
{
|
{
|
||||||
|
int m = attmap[i];
|
||||||
string = CopyReadAttribute(fp, &isnull, delim,
|
string = CopyReadAttribute(fp, &isnull, delim,
|
||||||
&newline, null_print);
|
&newline, null_print);
|
||||||
if (isnull)
|
|
||||||
{
|
if( isnull ){
|
||||||
/* already set values[i] and nulls[i] */
|
/* nothing */
|
||||||
}
|
}
|
||||||
else if (string == NULL)
|
else if (string == NULL)
|
||||||
done = 1; /* end of file */
|
done = 1; /* end of file */
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
values[i] = FunctionCall3(&in_functions[i],
|
values[m] = FunctionCall3(&in_functions[m],
|
||||||
CStringGetDatum(string),
|
CStringGetDatum(string),
|
||||||
ObjectIdGetDatum(elements[i]),
|
ObjectIdGetDatum(elements[m]),
|
||||||
Int32GetDatum(attr[i]->atttypmod));
|
Int32GetDatum(attr[m]->atttypmod));
|
||||||
nulls[i] = ' ';
|
nulls[m] = ' ';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* as above, we only try a default lookup if one is
|
||||||
|
* known to be available
|
||||||
|
*/
|
||||||
|
for (i = 0; i < def_attr_count && !done; i++){
|
||||||
|
bool isnull;
|
||||||
|
values[defmap[i]] = ExecEvalExpr(defexprs[i],econtext,&isnull,&isdone);
|
||||||
|
if( ! isnull )
|
||||||
|
nulls[defmap[i]] = ' ';
|
||||||
|
}
|
||||||
if (!done)
|
if (!done)
|
||||||
CopyReadNewline(fp, &newline);
|
CopyReadNewline(fp, &newline);
|
||||||
}
|
}
|
||||||
@ -1021,12 +1113,6 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
|
|||||||
ExecARInsertTriggers(estate, resultRelInfo, tuple);
|
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);
|
heap_freetuple(tuple);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1361,3 +1447,51 @@ CopyAttributeOut(FILE *fp, char *server_string, char *delim)
|
|||||||
pfree(string_start); /* pfree pg_server_to_client result */
|
pfree(string_start); /* pfree pg_server_to_client result */
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CopyAssertAttlist: elog(ERROR,...) if the specified attlist
|
||||||
|
* is not valid for the Relation
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
CopyAssertAttlist(Relation rel, List* attlist, bool from)
|
||||||
|
{
|
||||||
|
TupleDesc tupDesc;
|
||||||
|
List* cur;
|
||||||
|
char* illegalattname = NULL;
|
||||||
|
int attr_count;
|
||||||
|
const char* to_or_from;
|
||||||
|
|
||||||
|
if( attlist == NIL )
|
||||||
|
return;
|
||||||
|
|
||||||
|
to_or_from = (from == true ? "FROM" : "TO");
|
||||||
|
|
||||||
|
tupDesc = RelationGetDescr(rel);
|
||||||
|
Assert(tupDesc != NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* make sure there aren't more columns specified than are in the table
|
||||||
|
*/
|
||||||
|
attr_count = tupDesc->natts;
|
||||||
|
if( attr_count < length(attlist) )
|
||||||
|
elog(ERROR,"More columns specified in COPY %s command than in target relation",to_or_from);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* make sure no columns are specified that don't exist in the table
|
||||||
|
*/
|
||||||
|
foreach(cur,attlist)
|
||||||
|
{
|
||||||
|
int found = 0;
|
||||||
|
int i = 0;
|
||||||
|
for(;i<attr_count;++i)
|
||||||
|
{
|
||||||
|
if( strcmp(strVal(lfirst(cur)),NameStr(tupDesc->attrs[i]->attname)) == 0)
|
||||||
|
++found;
|
||||||
|
}
|
||||||
|
if( ! found )
|
||||||
|
illegalattname = strVal(lfirst(cur));
|
||||||
|
}
|
||||||
|
if( illegalattname )
|
||||||
|
elog(ERROR,"Attribute referenced in COPY %s command does not exist: \"%s.%s\"",to_or_from,RelationGetRelationName(rel),illegalattname);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.343 2002/07/18 04:41:45 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.344 2002/07/18 04:43:50 momjian Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -1262,31 +1262,32 @@ opt_id: ColId { $$ = $1; }
|
|||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*
|
*
|
||||||
* QUERY :
|
* QUERY :
|
||||||
* COPY <relname> FROM/TO [WITH options]
|
* COPY <relname> ['(' columnList ')'] FROM/TO [WITH options]
|
||||||
*
|
*
|
||||||
* BINARY, OIDS, and DELIMITERS kept in old locations
|
* BINARY, OIDS, and DELIMITERS kept in old locations
|
||||||
* for backward compatibility. 2002-06-18
|
* for backward compatibility. 2002-06-18
|
||||||
*
|
*
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
CopyStmt: COPY opt_binary qualified_name opt_oids copy_from
|
CopyStmt: COPY opt_binary qualified_name opt_column_list opt_oids
|
||||||
copy_file_name copy_delimiter opt_with copy_opt_list
|
copy_from copy_file_name copy_delimiter opt_with copy_opt_list
|
||||||
{
|
{
|
||||||
CopyStmt *n = makeNode(CopyStmt);
|
CopyStmt *n = makeNode(CopyStmt);
|
||||||
n->relation = $3;
|
n->relation = $3;
|
||||||
n->is_from = $5;
|
n->attlist = $4;
|
||||||
n->filename = $6;
|
n->is_from = $6;
|
||||||
|
n->filename = $7;
|
||||||
|
|
||||||
n->options = NIL;
|
n->options = NIL;
|
||||||
/* Concatenate user-supplied flags */
|
/* Concatenate user-supplied flags */
|
||||||
if ($2)
|
if ($2)
|
||||||
n->options = lappend(n->options, $2);
|
n->options = lappend(n->options, $2);
|
||||||
if ($4)
|
if ($5)
|
||||||
n->options = lappend(n->options, $4);
|
n->options = lappend(n->options, $5);
|
||||||
if ($7)
|
if ($8)
|
||||||
n->options = lappend(n->options, $7);
|
n->options = lappend(n->options, $8);
|
||||||
if ($9)
|
if ($10)
|
||||||
n->options = nconc(n->options, $9);
|
n->options = nconc(n->options, $10);
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.103 2002/06/20 20:29:34 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.104 2002/07/18 04:43:50 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -43,7 +43,6 @@ static List *adjustJoinTreeList(Query *parsetree, bool removert, int rt_index);
|
|||||||
static void rewriteTargetList(Query *parsetree, Relation target_relation);
|
static void rewriteTargetList(Query *parsetree, Relation target_relation);
|
||||||
static TargetEntry *process_matched_tle(TargetEntry *src_tle,
|
static TargetEntry *process_matched_tle(TargetEntry *src_tle,
|
||||||
TargetEntry *prior_tle);
|
TargetEntry *prior_tle);
|
||||||
static Node *build_column_default(Relation rel, int attrno);
|
|
||||||
static void markQueryForUpdate(Query *qry, bool skipOldNew);
|
static void markQueryForUpdate(Query *qry, bool skipOldNew);
|
||||||
static List *matchLocks(CmdType event, RuleLock *rulelocks,
|
static List *matchLocks(CmdType event, RuleLock *rulelocks,
|
||||||
int varno, Query *parsetree);
|
int varno, Query *parsetree);
|
||||||
@ -411,7 +410,7 @@ process_matched_tle(TargetEntry *src_tle,
|
|||||||
*
|
*
|
||||||
* If there is no default, return a NULL instead.
|
* If there is no default, return a NULL instead.
|
||||||
*/
|
*/
|
||||||
static Node *
|
Node *
|
||||||
build_column_default(Relation rel, int attrno)
|
build_column_default(Relation rel, int attrno)
|
||||||
{
|
{
|
||||||
TupleDesc rd_att = rel->rd_att;
|
TupleDesc rd_att = rel->rd_att;
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.271 2002/07/12 18:43:18 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.272 2002/07/18 04:43:50 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -133,6 +133,7 @@ static char *GetPrivileges(Archive *AH, const char *s, const char *type);
|
|||||||
static int dumpBlobs(Archive *AH, char *, void *);
|
static int dumpBlobs(Archive *AH, char *, void *);
|
||||||
static int dumpDatabase(Archive *AH);
|
static int dumpDatabase(Archive *AH);
|
||||||
static const char *getAttrName(int attrnum, TableInfo *tblInfo);
|
static const char *getAttrName(int attrnum, TableInfo *tblInfo);
|
||||||
|
static const char* fmtCopyColumnList(const TableInfo* ti);
|
||||||
|
|
||||||
extern char *optarg;
|
extern char *optarg;
|
||||||
extern int optind,
|
extern int optind,
|
||||||
@ -842,6 +843,7 @@ dumpClasses_nodumpData(Archive *fout, char *oid, void *dctxv)
|
|||||||
int ret;
|
int ret;
|
||||||
bool copydone;
|
bool copydone;
|
||||||
char copybuf[COPYBUFSIZ];
|
char copybuf[COPYBUFSIZ];
|
||||||
|
const char* column_list;
|
||||||
|
|
||||||
if (g_verbose)
|
if (g_verbose)
|
||||||
write_msg(NULL, "dumping out the contents of table %s\n", classname);
|
write_msg(NULL, "dumping out the contents of table %s\n", classname);
|
||||||
@ -854,17 +856,19 @@ dumpClasses_nodumpData(Archive *fout, char *oid, void *dctxv)
|
|||||||
*/
|
*/
|
||||||
selectSourceSchema(tbinfo->relnamespace->nspname);
|
selectSourceSchema(tbinfo->relnamespace->nspname);
|
||||||
|
|
||||||
|
column_list = fmtCopyColumnList(tbinfo);
|
||||||
|
|
||||||
if (oids && hasoids)
|
if (oids && hasoids)
|
||||||
{
|
{
|
||||||
appendPQExpBuffer(q, "COPY %s WITH OIDS TO stdout;",
|
appendPQExpBuffer(q, "COPY %s %s WITH OIDS TO stdout;",
|
||||||
fmtQualifiedId(tbinfo->relnamespace->nspname,
|
fmtQualifiedId(tbinfo->relnamespace->nspname,
|
||||||
classname));
|
classname),column_list);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
appendPQExpBuffer(q, "COPY %s TO stdout;",
|
appendPQExpBuffer(q, "COPY %s %s TO stdout;",
|
||||||
fmtQualifiedId(tbinfo->relnamespace->nspname,
|
fmtQualifiedId(tbinfo->relnamespace->nspname,
|
||||||
classname));
|
classname), column_list);
|
||||||
}
|
}
|
||||||
res = PQexec(g_conn, q->data);
|
res = PQexec(g_conn, q->data);
|
||||||
if (!res ||
|
if (!res ||
|
||||||
@ -1189,8 +1193,9 @@ dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout,
|
|||||||
{
|
{
|
||||||
/* Dump/restore using COPY */
|
/* Dump/restore using COPY */
|
||||||
dumpFn = dumpClasses_nodumpData;
|
dumpFn = dumpClasses_nodumpData;
|
||||||
sprintf(copyBuf, "COPY %s %sFROM stdin;\n",
|
sprintf(copyBuf, "COPY %s %s %sFROM stdin;\n",
|
||||||
fmtId(tblinfo[i].relname, force_quotes),
|
fmtQualifiedId(tblinfo[i].relnamespace->nspname,tblinfo[i].relname),
|
||||||
|
fmtCopyColumnList(&(tblinfo[i])),
|
||||||
(oids && tblinfo[i].hasoids) ? "WITH OIDS " : "");
|
(oids && tblinfo[i].hasoids) ? "WITH OIDS " : "");
|
||||||
copyStmt = copyBuf;
|
copyStmt = copyBuf;
|
||||||
}
|
}
|
||||||
@ -5860,3 +5865,38 @@ fmtQualifiedId(const char *schema, const char *id)
|
|||||||
|
|
||||||
return id_return->data;
|
return id_return->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* return a column list clause for the qualified relname.
|
||||||
|
* returns an empty string if the remote server is older than
|
||||||
|
* 7.3.
|
||||||
|
*/
|
||||||
|
static const char*
|
||||||
|
fmtCopyColumnList(const TableInfo* ti)
|
||||||
|
{
|
||||||
|
static PQExpBuffer q = NULL;
|
||||||
|
int numatts = ti->numatts;
|
||||||
|
char** attnames = ti->attnames;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (g_fout->remoteVersion < 70300 )
|
||||||
|
return "";
|
||||||
|
|
||||||
|
if (q) /* first time through? */
|
||||||
|
resetPQExpBuffer(q);
|
||||||
|
else
|
||||||
|
q = createPQExpBuffer();
|
||||||
|
|
||||||
|
resetPQExpBuffer(q);
|
||||||
|
|
||||||
|
appendPQExpBuffer(q,"(");
|
||||||
|
for (i = 0; i < numatts; i++)
|
||||||
|
{
|
||||||
|
if( i > 0 )
|
||||||
|
appendPQExpBuffer(q,",");
|
||||||
|
appendPQExpBuffer(q, fmtId(attnames[i], force_quotes));
|
||||||
|
}
|
||||||
|
appendPQExpBuffer(q, ")");
|
||||||
|
return q->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: parsenodes.h,v 1.189 2002/07/18 04:42:29 momjian Exp $
|
* $Id: parsenodes.h,v 1.190 2002/07/18 04:43:51 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -884,6 +884,7 @@ typedef struct CopyStmt
|
|||||||
{
|
{
|
||||||
NodeTag type;
|
NodeTag type;
|
||||||
RangeVar *relation; /* the relation to copy */
|
RangeVar *relation; /* the relation to copy */
|
||||||
|
List *attlist;
|
||||||
bool is_from; /* TO or FROM */
|
bool is_from; /* TO or FROM */
|
||||||
char *filename; /* if NULL, use stdin/stdout */
|
char *filename; /* if NULL, use stdin/stdout */
|
||||||
List *options; /* List of DefElem nodes */
|
List *options; /* List of DefElem nodes */
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: rewriteHandler.h,v 1.19 2002/06/20 20:29:52 momjian Exp $
|
* $Id: rewriteHandler.h,v 1.20 2002/07/18 04:43:51 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
#include "nodes/parsenodes.h"
|
#include "nodes/parsenodes.h"
|
||||||
|
|
||||||
|
|
||||||
extern List *QueryRewrite(Query *parsetree);
|
extern List *QueryRewrite(Query *parsetree);
|
||||||
|
extern Node *build_column_default(Relation rel, int attrno);
|
||||||
|
|
||||||
#endif /* REWRITEHANDLER_H */
|
#endif /* REWRITEHANDLER_H */
|
||||||
|
@ -74,4 +74,4 @@ test: select_views alter_table portals_p2 rules foreign_key
|
|||||||
# The sixth group of parallel test
|
# The sixth group of parallel test
|
||||||
# ----------
|
# ----------
|
||||||
# "plpgsql" cannot run concurrently with "rules"
|
# "plpgsql" cannot run concurrently with "rules"
|
||||||
test: limit plpgsql temp domain rangefuncs
|
test: limit plpgsql temp domain rangefuncs copy2
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# $Header: /cvsroot/pgsql/src/test/regress/serial_schedule,v 1.10 2002/06/20 17:09:42 momjian Exp $
|
# $Header: /cvsroot/pgsql/src/test/regress/serial_schedule,v 1.11 2002/07/18 04:43:51 momjian Exp $
|
||||||
# This should probably be in an order similar to parallel_schedule.
|
# This should probably be in an order similar to parallel_schedule.
|
||||||
test: boolean
|
test: boolean
|
||||||
test: char
|
test: char
|
||||||
@ -80,6 +80,7 @@ test: rules
|
|||||||
test: foreign_key
|
test: foreign_key
|
||||||
test: limit
|
test: limit
|
||||||
test: plpgsql
|
test: plpgsql
|
||||||
|
test: copy2
|
||||||
test: temp
|
test: temp
|
||||||
test: domain
|
test: domain
|
||||||
test: rangefuncs
|
test: rangefuncs
|
||||||
|
Reference in New Issue
Block a user