Commit a90db34b authored by Bruce Momjian's avatar Bruce Momjian

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
parent fc5372e0
<!-- <!--
$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
--> -->
...@@ -21,7 +21,8 @@ PostgreSQL documentation ...@@ -21,7 +21,8 @@ PostgreSQL documentation
<date>1999-12-11</date> <date>1999-12-11</date>
</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> ...@@ -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 ]
...@@ -55,6 +57,16 @@ COPY <replaceable class="parameter">table</replaceable> ...@@ -55,6 +57,16 @@ COPY <replaceable class="parameter">table</replaceable>
</para> </para>
</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>
...@@ -187,6 +199,14 @@ ERROR: <replaceable>reason</replaceable> ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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";
...@@ -267,7 +269,8 @@ DoCopy(const CopyStmt *stmt) ...@@ -267,7 +269,8 @@ DoCopy(const CopyStmt *stmt)
char *filename = stmt->filename; char *filename = stmt->filename;
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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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,12 +895,14 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, ...@@ -821,12 +895,14 @@ 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();
copy_lineno++; copy_lineno++;
/* Reset the per-output-tuple exprcontext */ /* Reset the per-output-tuple exprcontext */
ResetPerTupleExprContext(estate); ResetPerTupleExprContext(estate);
...@@ -854,26 +930,42 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, ...@@ -854,26 +930,42 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
elog(ERROR, "COPY TEXT: Invalid Oid"); elog(ERROR, "COPY TEXT: Invalid Oid");
} }
} }
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);
} }
...@@ -975,7 +1067,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, ...@@ -975,7 +1067,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
break; break;
tuple = heap_formtuple(tupDesc, values, nulls); tuple = heap_formtuple(tupDesc, values, nulls);
if (oids && file_has_oids) if (oids && file_has_oids)
tuple->t_data->t_oid = loaded_oid; tuple->t_data->t_oid = loaded_oid;
...@@ -1021,12 +1113,6 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, ...@@ -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) ...@@ -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 @@ ...@@ -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; } ...@@ -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 @@ ...@@ -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); ...@@ -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, ...@@ -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 @@ ...@@ -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); ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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 @@ ...@@ -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 ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 ...@@ -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
# $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 ...@@ -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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment