Index: doc/src/sgml/ref/copy.sgml
===================================================================
RCS file: /var/lib/cvs/pgsql/doc/src/sgml/ref/copy.sgml,v
retrieving revision 1.33
diff -c -r1.33 copy.sgml
*** doc/src/sgml/ref/copy.sgml 18 Jul 2002 04:43:50 -0000 1.33
--- doc/src/sgml/ref/copy.sgml 26 Jul 2002 01:04:21 -0000
***************
*** 199,210 ****
whatever is in the table already).
!
! When using the optional column list syntax, COPY TO
! and COPY FROM will only copy the specified
! columns' values to/from the table. If a column in the table
! is not in the column list, COPY FROM will insert
! default values for that column if a default value is defined.
--- 199,210 ----
whatever is in the table already).
!
! If a list of columns is specified, COPY will
! only copy the data in the specified columns to or from the table.
! If there are any columns in the table that are not in the table,
! COPY FROM will insert the default value for
! that column.
***************
*** 266,273 ****
! COPY FROM neither invokes rules nor acts on column
! defaults. It does invoke triggers and check constraints.
--- 266,273 ----
! COPY FROM will invoke any triggers or check
! constraints. However, it will not invoke rules.
***************
*** 330,336 ****
The attribute values themselves are strings generated by the
output function, or acceptable to the input function, of each
attribute's data type. The specified null-value string is used in
! place of attributes that are NULL.
If OIDS is specified, the OID is read or written as the first column,
--- 330,341 ----
The attribute values themselves are strings generated by the
output function, or acceptable to the input function, of each
attribute's data type. The specified null-value string is used in
! place of attributes that are NULL. When using COPY
! FROM without a column list, each row of the input file
! must contain data for each attribute in the table: no missing data
! is allowed. Similarly, COPY FROM will raise
! an error if it encounters any data in the input file that would
! not be inserted into the table: extra data is not allowed.
If OIDS is specified, the OID is read or written as the first column,
Index: src/backend/commands/copy.c
===================================================================
RCS file: /var/lib/cvs/pgsql/src/backend/commands/copy.c,v
retrieving revision 1.160
diff -c -r1.160 copy.c
*** src/backend/commands/copy.c 20 Jul 2002 05:16:57 -0000 1.160
--- src/backend/commands/copy.c 26 Jul 2002 01:04:21 -0000
***************
*** 45,60 ****
#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
#define OCTVALUE(c) ((c) - '0')
/* non-export function prototypes */
static void CopyTo(Relation rel, List *attlist, 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 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 void CopyAssertAttlist(Relation rel, List* attlist, bool from);
static const char BinarySignature[12] = "PGBCOPY\n\377\r\n\0";
--- 45,65 ----
#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
#define OCTVALUE(c) ((c) - '0')
+ typedef enum CopyReadResult
+ {
+ NORMAL_ATTR,
+ END_OF_LINE,
+ END_OF_FILE
+ } CopyReadResult;
/* non-export function prototypes */
static void CopyTo(Relation rel, List *attlist, 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 GetTypeElement(Oid type);
! static char *CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result);
static void CopyAttributeOut(FILE *fp, char *string, char *delim);
! static void CopyCheckAttlist(Relation rel, List *attlist);
static const char BinarySignature[12] = "PGBCOPY\n\377\r\n\0";
***************
*** 271,284 ****
bool pipe = (stmt->filename == NULL);
List *option;
List *attlist = stmt->attlist;
- 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);
--- 276,285 ----
bool pipe = (stmt->filename == NULL);
List *option;
List *attlist = stmt->attlist;
bool binary = false;
bool oids = false;
! char *delim = NULL;
! char *null_print = NULL;
FILE *fp;
Relation rel;
AclMode required_access = (is_from ? ACL_INSERT : ACL_SELECT);
***************
*** 289,339 ****
{
DefElem *defel = (DefElem *) lfirst(option);
if (strcmp(defel->defname, "binary") == 0)
{
! if (dbinary)
! /* should this really be an error? */
elog(ERROR, "COPY: BINARY option appears more than once");
! dbinary = defel;
}
else if (strcmp(defel->defname, "oids") == 0)
{
! if (doids)
! /* should this really be an error? */
elog(ERROR, "COPY: OIDS option appears more than once");
! doids = defel;
}
else if (strcmp(defel->defname, "delimiter") == 0)
{
! if (ddelim)
elog(ERROR, "COPY: DELIMITER string may only be defined once in query");
! ddelim = defel;
}
else if (strcmp(defel->defname, "null") == 0)
{
! if (dnull)
elog(ERROR, "COPY: NULL representation may only be defined once in query");
! 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.
--- 290,342 ----
{
DefElem *defel = (DefElem *) lfirst(option);
+ /* XXX: Should we bother checking for doubled options? */
+
if (strcmp(defel->defname, "binary") == 0)
{
! if (binary)
elog(ERROR, "COPY: BINARY option appears more than once");
!
! binary = intVal(defel->arg);
}
else if (strcmp(defel->defname, "oids") == 0)
{
! if (oids)
elog(ERROR, "COPY: OIDS option appears more than once");
!
! oids = intVal(defel->arg);
}
else if (strcmp(defel->defname, "delimiter") == 0)
{
! if (delim)
elog(ERROR, "COPY: DELIMITER string may only be defined once in query");
!
! delim = strVal(defel->arg);
}
else if (strcmp(defel->defname, "null") == 0)
{
! if (null_print)
elog(ERROR, "COPY: NULL representation may only be defined once in query");
!
! null_print = strVal(defel->arg);
}
else
elog(ERROR, "COPY: option \"%s\" not recognized",
defel->defname);
}
! if (binary && delim)
elog(ERROR, "You can not specify the DELIMITER in BINARY mode.");
! if (binary && null_print)
elog(ERROR, "You can not specify NULL in BINARY mode.");
+
+ /* Set defaults */
+ if (!delim)
+ delim = "\t";
+
+ if (!null_print)
+ null_print = "\\N";
/*
* Open and lock the relation, using the appropriate lock type.
***************
*** 364,369 ****
--- 367,379 ----
elog(ERROR, "COPY delimiter must be a single character");
/*
+ * Don't allow COPY w/ OIDs to or from a table without them
+ */
+ if (oids && !rel->rd_rel->relhasoids)
+ elog(ERROR, "COPY: table \"%s\" does not have OIDs",
+ RelationGetRelationName(rel));
+
+ /*
* Set up variables to avoid per-attribute overhead.
*/
initStringInfo(&attribute_buf);
***************
*** 372,393 ****
server_encoding = GetDatabaseEncoding();
#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)
--- 382,405 ----
server_encoding = GetDatabaseEncoding();
#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");
!
! CopyCheckAttlist(rel, attlist);
}
if (is_from)
***************
*** 532,558 ****
int16 fld_size;
char *string;
Snapshot mySnapshot;
! int copy_attr_count;
! int* attmap;
! int p = 0;
! List* cur;
!
! 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;
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;
! }
}
}
}
--- 544,571 ----
int16 fld_size;
char *string;
Snapshot mySnapshot;
! int copy_attr_count;
! int *attmap;
! int p = 0;
! List *cur;
tupDesc = rel->rd_att;
attr_count = rel->rd_att->natts;
attr = rel->rd_att->attrs;
+
copy_attr_count = length(attlist);
+ attmap = (int *) palloc(copy_attr_count * sizeof(int));
+
+ foreach(cur, attlist)
{
! const char *currAtt = strVal(lfirst(cur));
!
! for (i = 0; i < attr_count; i++)
! {
! if (namestrcmp(&attr[i]->attname, currAtt) == 0)
! {
! attmap[p++] = i;
! continue;
}
}
}
***************
*** 642,648 ****
Datum origvalue,
value;
bool isnull;
! int mi = attmap[i];
origvalue = heap_getattr(tuple, mi + 1, tupDesc, &isnull);
--- 655,661 ----
Datum origvalue,
value;
bool isnull;
! int mi = attmap[i];
origvalue = heap_getattr(tuple, mi + 1, tupDesc, &isnull);
***************
*** 767,785 ****
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;
! int* attmap = NULL;
! int* defmap = NULL;
! Node** defexprs = NULL; /* array of default att expressions */
ExprContext *econtext; /* used for ExecEvalExpr for default atts */
ExprDoneCond isdone;
--- 780,794 ----
Oid in_func_oid;
Datum *values;
char *nulls;
int done = 0;
ResultRelInfo *resultRelInfo;
EState *estate = CreateExecutorState(); /* for ExecConstraints() */
TupleTable tupleTable;
TupleTableSlot *slot;
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;
***************
*** 810,849 ****
slot = ExecAllocTableSlot(tupleTable);
ExecSetSlotDescriptor(slot, tupDesc, false);
-
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));
elements = (Oid *) palloc(attr_count * sizeof(Oid));
for (i = 0; i < attr_count; i++)
{
! int p = 0;
! bool specified = false;
in_func_oid = (Oid) GetInputFunction(attr[i]->atttypid);
fmgr_info(in_func_oid, &in_functions[i]);
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;
}
}
}
--- 819,865 ----
slot = ExecAllocTableSlot(tupleTable);
ExecSetSlotDescriptor(slot, tupDesc, false);
if (!binary)
{
/*
* pick up the input function and default expression (if any) for
* each attribute in the relation.
*/
! 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));
elements = (Oid *) palloc(attr_count * sizeof(Oid));
for (i = 0; i < attr_count; i++)
{
! List *l;
! int p = 0;
! bool specified = false;
!
in_func_oid = (Oid) GetInputFunction(attr[i]->atttypid);
fmgr_info(in_func_oid, &in_functions[i]);
elements[i] = GetTypeElement(attr[i]->atttypid);
!
! foreach(l, attlist)
! {
! if (namestrcmp(&attr[i]->attname, strVal(lfirst(l))) == 0)
! {
attmap[p] = i;
specified = true;
continue;
}
! p++;
}
!
! /* if column not specified, use default value if one exists */
! if (! specified)
! {
! defexprs[def_attr_count] = build_column_default(rel, i + 1);
!
! if (defexprs[def_attr_count] != NULL)
! {
defmap[def_attr_count] = i;
! def_attr_count++;
}
}
}
***************
*** 857,869 ****
/* 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);
--- 873,883 ----
/* 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);
***************
*** 875,882 ****
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)
--- 889,895 ----
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)
***************
*** 890,899 ****
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));
--- 903,908 ----
***************
*** 904,909 ****
--- 913,921 ----
while (!done)
{
+ bool skip_tuple;
+ Oid loaded_oid;
+
CHECK_FOR_INTERRUPTS();
copy_lineno++;
***************
*** 917,932 ****
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,
--- 929,945 ----
if (!binary)
{
! CopyReadResult result;
! char *string;
if (file_has_oids)
{
! string = CopyReadAttribute(fp, delim, &result);
!
! if (result == END_OF_FILE)
! done = 1;
! else if (string == NULL || strcmp(string, null_print) == 0)
elog(ERROR, "COPY TEXT: NULL Oid");
else
{
loaded_oid = DatumGetObjectId(DirectFunctionCall1(oidin,
***************
*** 943,978 ****
for (i = 0; i < copy_attr_count && !done; i++)
{
int m = attmap[i];
- string = CopyReadAttribute(fp, &isnull, delim,
- &newline, null_print);
! if( isnull ){
! /* nothing */
}
- else if (string == NULL)
- done = 1; /* end of file */
else
{
values[m] = FunctionCall3(&in_functions[m],
! CStringGetDatum(string),
! ObjectIdGetDatum(elements[m]),
! Int32GetDatum(attr[m]->atttypmod));
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)
- CopyReadNewline(fp, &newline);
}
else
{ /* binary */
--- 956,1000 ----
for (i = 0; i < copy_attr_count && !done; i++)
{
int m = attmap[i];
! string = CopyReadAttribute(fp, delim, &result);
!
! /* If we got an end-of-line before we expected, bail out */
! if (result == END_OF_LINE && i < (copy_attr_count - 1))
! elog(ERROR, "COPY TEXT: Missing data for attribute %d", i + 1);
!
! if (result == END_OF_FILE)
! done = 1;
! else if (strcmp(string, null_print) == 0)
! {
! /* we read an SQL NULL, no need to do anything */
}
else
{
values[m] = FunctionCall3(&in_functions[m],
! CStringGetDatum(string),
! ObjectIdGetDatum(elements[m]),
! Int32GetDatum(attr[m]->atttypmod));
nulls[m] = ' ';
}
}
!
! if (result == NORMAL_ATTR && !done)
! elog(ERROR, "COPY TEXT: Extra data encountered");
!
/*
* 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]] = ' ';
}
}
else
{ /* binary */
***************
*** 980,987 ****
fld_size;
CopyGetData(&fld_count, sizeof(int16), fp);
! if (CopyGetEof(fp) ||
! fld_count == -1)
done = 1;
else
{
--- 1002,1008 ----
fld_size;
CopyGetData(&fld_count, sizeof(int16), fp);
! if (CopyGetEof(fp) || fld_count == -1)
done = 1;
else
{
***************
*** 1173,1208 ****
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];
--- 1194,1218 ----
return result;
}
/*
* Read the value of a single attribute.
*
! * Results are returned in the status indicator, as well as the
! * return value. If a value was successfully read but there is
! * more to read before EOL, NORMAL_ATTR is set and the value read
! * is returned. If a value was read and we hit EOL, END_OF_LINE
! * is set and the value read is returned. If we hit the EOF,
! * END_OF_FILE is set and NULL is returned.
*
! * Note: This function does not care about SQL NULL values -- it
! * is the caller's responsibility to check if the returned string
! * matches what the user specified for the SQL NULL value.
! *
! * delim is the column delimiter string.
*/
static char *
! CopyReadAttribute(FILE *fp, const char *delim, CopyReadResult *result)
{
int c;
int delimc = (unsigned char)delim[0];
***************
*** 1220,1242 ****
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)
--- 1230,1249 ----
attribute_buf.len = 0;
attribute_buf.data[0] = '\0';
! /* set default */
! *result = NORMAL_ATTR;
for (;;)
{
c = CopyGetChar(fp);
if (c == EOF)
! {
! *result = END_OF_FILE;
! return NULL;
! }
if (c == '\n')
{
! *result = END_OF_LINE;
break;
}
if (c == delimc)
***************
*** 1245,1251 ****
{
c = CopyGetChar(fp);
if (c == EOF)
! goto endOfFile;
switch (c)
{
case '0':
--- 1252,1261 ----
{
c = CopyGetChar(fp);
if (c == EOF)
! {
! *result = END_OF_FILE;
! return NULL;
! }
switch (c)
{
case '0':
***************
*** 1274,1287 ****
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;
--- 1284,1303 ----
else
{
if (c == EOF)
! {
! *result = END_OF_FILE;
! return NULL;
! }
CopyDonePeek(fp, c, false /* put back */ );
}
}
else
{
if (c == EOF)
! {
! *result = END_OF_FILE;
! return NULL;
! }
CopyDonePeek(fp, c, false /* put back */ );
}
c = val & 0377;
***************
*** 1319,1325 ****
c = CopyGetChar(fp);
if (c != '\n')
elog(ERROR, "CopyReadAttribute: end of record marker corrupted");
! goto endOfFile;
}
}
appendStringInfoCharMacro(&attribute_buf, c);
--- 1335,1342 ----
c = CopyGetChar(fp);
if (c != '\n')
elog(ERROR, "CopyReadAttribute: end of record marker corrupted");
! *result = END_OF_FILE;
! return NULL;
}
}
appendStringInfoCharMacro(&attribute_buf, c);
***************
*** 1334,1340 ****
{
c = CopyGetChar(fp);
if (c == EOF)
! goto endOfFile;
appendStringInfoCharMacro(&attribute_buf, c);
}
}
--- 1351,1360 ----
{
c = CopyGetChar(fp);
if (c == EOF)
! {
! *result = END_OF_FILE;
! return NULL;
! }
appendStringInfoCharMacro(&attribute_buf, c);
}
}
***************
*** 1357,1369 ****
}
#endif
- if (strcmp(attribute_buf.data, null_print) == 0)
- *isnull = true;
-
return attribute_buf.data;
-
- endOfFile:
- return NULL;
}
static void
--- 1377,1383 ----
***************
*** 1454,1502 ****
}
/*
! * 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(;iattrs[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);
}
--- 1468,1517 ----
}
/*
! * CopyCheckAttlist: elog(ERROR,...) if the specified attlist
* is not valid for the Relation
*/
static void
! CopyCheckAttlist(Relation rel, List *attlist)
{
! TupleDesc tupDesc;
! int attr_count;
! List *l;
! if (attlist == NIL)
! return;
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, "COPY: Too many columns specified");
!
! /*
! * make sure no columns are specified that don't exist in the table
! */
! foreach(l, attlist)
! {
! char *colName = strVal(lfirst(l));
! bool found = false;
! int i;
!
! for (i = 0; i < attr_count; i++)
! {
! if (namestrcmp(&tupDesc->attrs[i]->attname, colName) == 0)
! {
! found = true;
! break;
! }
! }
!
! if (!found)
! elog(ERROR, "COPY: Specified column \"%s\" does not exist",
! colName);
! }
}
Index: src/test/regress/expected/copy2.out
===================================================================
RCS file: /var/lib/cvs/pgsql/src/test/regress/expected/copy2.out,v
retrieving revision 1.2
diff -c -r1.2 copy2.out
*** src/test/regress/expected/copy2.out 18 Jul 2002 17:11:49 -0000 1.2
--- src/test/regress/expected/copy2.out 26 Jul 2002 01:04:22 -0000
***************
*** 23,32 ****
FOR EACH ROW EXECUTE PROCEDURE fn_x_after();
CREATE TRIGGER trg_x_before BEFORE INSERT ON x
FOR EACH ROW EXECUTE PROCEDURE fn_x_before();
! COPY x (a,b,c,d,e) from stdin;
! COPY x (b,d) from stdin;
! COPY x (b,d) from stdin;
! COPY x (a,b,c,d,e) from stdin;
COPY x TO stdout;
10000 21 31 41 before trigger fired
10001 22 32 42 before trigger fired
--- 23,66 ----
FOR EACH ROW EXECUTE PROCEDURE fn_x_after();
CREATE TRIGGER trg_x_before BEFORE INSERT ON x
FOR EACH ROW EXECUTE PROCEDURE fn_x_before();
! COPY x (a, b, c, d, e) from stdin;
! COPY x (b, d) from stdin;
! COPY x (b, d) from stdin;
! COPY x (a, b, c, d, e) from stdin;
! -- non-existent column in column list: should fail
! COPY x (xyz) from stdin;
! ERROR: COPY: Specified column "xyz" does not exist
! -- too many columns in column list: should fail
! COPY x (a, b, c, d, e, d, c) from stdin;
! ERROR: COPY: Too many columns specified
! -- missing data: should fail
! COPY x from stdin;
! ERROR: copy: line 1, COPY TEXT: Missing data for attribute 1
! lost synchronization with server, resetting connection
! COPY x from stdin;
! ERROR: copy: line 1, COPY TEXT: Missing data for attribute 4
! lost synchronization with server, resetting connection
! COPY x from stdin;
! ERROR: copy: line 1, COPY TEXT: Missing data for attribute 4
! lost synchronization with server, resetting connection
! -- extra data: should fail
! COPY x from stdin;
! ERROR: copy: line 1, COPY TEXT: Extra data encountered
! lost synchronization with server, resetting connection
! -- various COPY options: delimiters, oids, NULL string
! COPY x (b, c, d, e) from stdin with oids delimiter ',' null 'x';
! -- COPY w/ oids on a table w/o oids should fail
! CREATE TABLE no_oids (
! a int,
! b int
! ) WITHOUT OIDS;
! INSERT INTO no_oids (a, b) VALUES (5, 10);
! INSERT INTO no_oids (a, b) VALUES (20, 30);
! -- should fail
! COPY no_oids FROM stdin WITH OIDS;
! ERROR: COPY: table "no_oids" does not have OIDs
! COPY no_oids TO stdout WITH OIDS;
! ERROR: COPY: table "no_oids" does not have OIDs
COPY x TO stdout;
10000 21 31 41 before trigger fired
10001 22 32 42 before trigger fired
***************
*** 34,44 ****
--- 68,92 ----
10003 24 34 44 before trigger fired
10004 25 35 45 before trigger fired
10005 26 36 46 before trigger fired
+ 6 \N 45 80 before trigger fired
1 1 stuff test_1 after trigger fired
2 2 stuff test_2 after trigger fired
3 3 stuff test_3 after trigger fired
4 4 stuff test_4 after trigger fired
5 5 stuff test_5 after trigger fired
+ COPY x (c, e) TO stdout;
+ 31 before trigger fired
+ 32 before trigger fired
+ 33 before trigger fired
+ 34 before trigger fired
+ 35 before trigger fired
+ 36 before trigger fired
+ 45 before trigger fired
+ stuff after trigger fired
+ stuff after trigger fired
+ stuff after trigger fired
+ stuff after trigger fired
+ stuff after trigger fired
DROP TABLE x;
DROP FUNCTION fn_x_before();
DROP FUNCTION fn_x_after();
Index: src/test/regress/sql/copy2.sql
===================================================================
RCS file: /var/lib/cvs/pgsql/src/test/regress/sql/copy2.sql,v
retrieving revision 1.2
diff -c -r1.2 copy2.sql
*** src/test/regress/sql/copy2.sql 18 Jul 2002 22:31:44 -0000 1.2
--- src/test/regress/sql/copy2.sql 26 Jul 2002 01:04:22 -0000
***************
*** 26,47 ****
CREATE TRIGGER trg_x_before BEFORE INSERT ON x
FOR EACH ROW EXECUTE PROCEDURE fn_x_before();
! COPY x (a,b,c,d,e) from stdin;
10000 21 31 41 51
\.
! COPY x (b,d) from stdin;
1 test_1
\.
! COPY x (b,d) from stdin;
2 test_2
3 test_3
4 test_4
5 test_5
\.
! COPY x (a,b,c,d,e) from stdin;
10001 22 32 42 52
10002 23 33 43 53
10003 24 34 44 54
--- 26,47 ----
CREATE TRIGGER trg_x_before BEFORE INSERT ON x
FOR EACH ROW EXECUTE PROCEDURE fn_x_before();
! COPY x (a, b, c, d, e) from stdin;
10000 21 31 41 51
\.
! COPY x (b, d) from stdin;
1 test_1
\.
! COPY x (b, d) from stdin;
2 test_2
3 test_3
4 test_4
5 test_5
\.
! COPY x (a, b, c, d, e) from stdin;
10001 22 32 42 52
10002 23 33 43 53
10003 24 34 44 54
***************
*** 49,55 ****
--- 49,96 ----
10005 26 36 46 56
\.
+ -- non-existent column in column list: should fail
+ COPY x (xyz) from stdin;
+
+ -- too many columns in column list: should fail
+ COPY x (a, b, c, d, e, d, c) from stdin;
+
+ -- missing data: should fail
+ COPY x from stdin;
+
+ \.
+ COPY x from stdin;
+ 2000 230 23 23
+ \.
+ COPY x from stdin;
+ 2001 231 \N \N
+ \.
+
+ -- extra data: should fail
+ COPY x from stdin;
+ 2002 232 40 50 60 70 80
+ \.
+
+ -- various COPY options: delimiters, oids, NULL string
+ COPY x (b, c, d, e) from stdin with oids delimiter ',' null 'x';
+ 500000,x,45,80,90
+ \.
+
+ -- COPY w/ oids on a table w/o oids should fail
+ CREATE TABLE no_oids (
+ a int,
+ b int
+ ) WITHOUT OIDS;
+
+ INSERT INTO no_oids (a, b) VALUES (5, 10);
+ INSERT INTO no_oids (a, b) VALUES (20, 30);
+
+ -- should fail
+ COPY no_oids FROM stdin WITH OIDS;
+ COPY no_oids TO stdout WITH OIDS;
+
COPY x TO stdout;
+ COPY x (c, e) TO stdout;
DROP TABLE x;
DROP FUNCTION fn_x_before();
DROP FUNCTION fn_x_after();