Index: src/backend/commands/copy.c =================================================================== RCS file: /var/cvsup/pgsql/src/backend/commands/copy.c,v retrieving revision 1.144 diff -c -r1.144 copy.c *** src/backend/commands/copy.c 4 Dec 2001 21:19:57 -0000 1.144 --- src/backend/commands/copy.c 14 Jan 2002 02:35:35 -0000 *************** *** 45,52 **** /* non-export function prototypes */ ! static void CopyTo(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null_print); ! static void CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null_print); static Oid GetInputFunction(Oid type); static Oid GetTypeElement(Oid type); static void CopyReadNewline(FILE *fp, int *newline); --- 45,52 ---- /* non-export function prototypes */ ! static void CopyTo(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null_print,List *attlist); ! static void CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, char *delim, char *null_print,List *attlist); static Oid GetInputFunction(Oid type); static Oid GetTypeElement(Oid type); static void CopyReadNewline(FILE *fp, int *newline); *************** *** 261,267 **** */ void DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe, ! char *filename, char *delim, char *null_print) { FILE *fp; Relation rel; --- 261,267 ---- */ void DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe, ! char *filename, char *delim, char *null_print, List* attlist) { FILE *fp; Relation rel; *************** *** 333,339 **** "reading. Errno = %s (%d).", (int) geteuid(), filename, strerror(errno), errno); } ! CopyFrom(rel, binary, oids, fp, delim, null_print); } else { /* copy from database to file */ --- 333,339 ---- "reading. Errno = %s (%d).", (int) geteuid(), filename, strerror(errno), errno); } ! CopyFrom(rel, binary, oids, fp, delim, null_print, attlist); } else { /* copy from database to file */ *************** *** 379,385 **** "writing. Errno = %s (%d).", (int) geteuid(), filename, strerror(errno), errno); } ! CopyTo(rel, binary, oids, fp, delim, null_print); } if (!pipe) --- 379,385 ---- "writing. Errno = %s (%d).", (int) geteuid(), filename, strerror(errno), errno); } ! CopyTo(rel, binary, oids, fp, delim, null_print, attlist); } if (!pipe) *************** *** 408,414 **** */ static void CopyTo(Relation rel, bool binary, bool oids, FILE *fp, ! char *delim, char *null_print) { HeapTuple tuple; TupleDesc tupDesc; --- 408,414 ---- */ static void CopyTo(Relation rel, bool binary, bool oids, FILE *fp, ! char *delim, char *null_print, List* attlist) { HeapTuple tuple; TupleDesc tupDesc; *************** *** 614,651 **** pfree(isvarlena); } /* * Copy FROM file to relation. */ static void CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, ! char *delim, char *null_print) { HeapTuple tuple; TupleDesc tupDesc; Form_pg_attribute *attr; AttrNumber attr_count; FmgrInfo *in_functions; Oid *elements; int i; 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; tupDesc = RelationGetDescr(rel); ! attr = tupDesc->attrs; ! attr_count = tupDesc->natts; /* * We need a ResultRelInfo so we can use the regular executor's --- 614,749 ---- pfree(isvarlena); } + /* XXX: move this and the implementation to a saner place */ + Node * build_column_default(Relation rel, int attrno); /* * Copy FROM file to relation. */ static void CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, ! char *delim, char *null_print, List* attlist) { HeapTuple tuple; TupleDesc tupDesc; Form_pg_attribute *attr; AttrNumber attr_count; FmgrInfo *in_functions; + Node** defexprs = NULL; + int* attr_map = NULL; Oid *elements; int i; + ExprDoneCond isdone; Oid in_func_oid; Datum *values; char *nulls; int done = 0; char *string; ResultRelInfo *resultRelInfo; EState *estate = CreateExecutorState(); /* for ExecConstraints() */ + ExprContext *econtext = GetPerTupleExprContext(estate); TupleTable tupleTable; TupleTableSlot *slot; Oid loaded_oid = InvalidOid; bool skip_tuple = false; ! bool isnull, file_has_oids; tupDesc = RelationGetDescr(rel); ! attr_count = tupDesc->natts; ! ! attr_map = (int*)palloc(sizeof(int) * attr_count); ! ! if( attlist ) ! { ! int lastatt; ! List* cur; ! Node* testnode; ! ! attr = (Form_pg_attribute*)palloc(sizeof(Form_pg_attribute*) * attr_count); ! defexprs = (Node**)palloc(sizeof(Node*) * MaxHeapAttributeNumber ); ! lastatt = length(attlist); ! ! if( attr_count < lastatt ) ! elog(ERROR,"More columns specified in COPY FROM command than in destination table"); ! /* ! * XXX: verify that there are no duplicates in attlist, and ! * that any name in attlist exists in the table. ! * (just too tired to do this right now. ! */ ! ! /* ! * Mapping the specified column data to the actual table structure. ! * ! * For the table: ! * Table "test" ! * Column | Type | Modifiers ! * --------+---------+------------------------------------------------- ! * id | integer | not null default nextval('"test_id_seq"'::text) ! * a | integer | ! * b | text | ! * c | integer | ! * ! * We might have a COPY from of: ! * COPY test(b,c) FROM STDIN; ! * whatever 100 ! * however 200 ! * stuff 300 ! * \. ! * ! * So our actual rel->rd_att->attrs is as follows: ! * attrs[0] -> "id" ! * attrs[1] -> "a" ! * attrs[2] -> "b" ! * attrs[3] -> "c" ! * Our input, on the other hand, will look like: ! * values[0] -> "b" ! * values[1] -> "c" ! * ! * The code below creates a mapping from the COPY input columns ! * to their correct table attrs, as well as picking up any ! * default column expressions for evaluation prior to creating ! * the new tuple if a column is not present in the COPY data. ! */ ! for(i=0;iattrs[i]->attname)) == 0) ! { ! /* column is specified in the COPY attlist */ ! attr_map[c] = i; ! attr[c] = tupDesc->attrs[i]; ! ++found; ! defexprs[i] = NULL; ! } ! ++c; ! } ! if( ! found ) ! { ! /* column in relation was not specified in attlist. ! * get a default Expr* for use prior to heap_formtuple ! */ ! testnode = build_column_default(rel,i+1); ! if( testnode ) /* is this necessary? */ ! { ! defexprs[i] = testnode; ! attr_map[lastatt] = i; ! attr[lastatt] = tupDesc->attrs[i]; ! ++lastatt; ! } ! } ! } ! } ! else{ ! /* no attlist was present, so we just fill the attr_map for ! * use later. ! */ ! attr = tupDesc->attrs; ! for(i=0;iatttypmod)); ! nulls[i] = ' '; } } if (!done) --- 875,885 ---- done = 1; /* end of file */ else { ! values[attr_map[i]] = FunctionCall3(&in_functions[i], CStringGetDatum(string), ObjectIdGetDatum(elements[i]), Int32GetDatum(attr[i]->atttypmod)); ! nulls[attr_map[i]] = ' '; } } if (!done) *************** *** 845,851 **** fp); if (CopyGetEof(fp)) elog(ERROR, "COPY BINARY: unexpected EOF"); ! values[i] = PointerGetDatum(varlena_ptr); } else if (!attr[i]->attbyval) { --- 943,949 ---- fp); if (CopyGetEof(fp)) elog(ERROR, "COPY BINARY: unexpected EOF"); ! values[attr_map[i]] = PointerGetDatum(varlena_ptr); } else if (!attr[i]->attbyval) { *************** *** 857,863 **** CopyGetData(refval_ptr, fld_size, fp); if (CopyGetEof(fp)) elog(ERROR, "COPY BINARY: unexpected EOF"); ! values[i] = PointerGetDatum(refval_ptr); } else { --- 955,961 ---- CopyGetData(refval_ptr, fld_size, fp); if (CopyGetEof(fp)) elog(ERROR, "COPY BINARY: unexpected EOF"); ! values[attr_map[i]] = PointerGetDatum(refval_ptr); } else { *************** *** 873,882 **** CopyGetData(&datumBuf, fld_size, fp); if (CopyGetEof(fp)) elog(ERROR, "COPY BINARY: unexpected EOF"); ! values[i] = fetch_att(&datumBuf, true, fld_size); } ! nulls[i] = ' '; } } } --- 971,980 ---- CopyGetData(&datumBuf, fld_size, fp); if (CopyGetEof(fp)) elog(ERROR, "COPY BINARY: unexpected EOF"); ! values[attr_map[i]] = fetch_att(&datumBuf, true, fld_size); } ! nulls[attr_map[i]] = ' '; } } } *************** *** 884,889 **** --- 982,1012 ---- if (done) break; + /* + * set any default columns that are not in the COPY attlist + */ + if( attlist ) + { + for(i=0;iattbyval && nulls[i] != 'n') ! pfree(DatumGetPointer(values[i])); } heap_freetuple(tuple); --- 1056,1063 ---- for (i = 0; i < attr_count; i++) { ! if (!attr[i]->attbyval && nulls[attr_map[i]] != 'n') ! pfree(DatumGetPointer(values[attr_map[i]])); } heap_freetuple(tuple); *************** *** 947,958 **** --- 1070,1094 ---- pfree(values); pfree(nulls); + pfree(attr_map); if (!binary) { pfree(in_functions); pfree(elements); } + if( attlist ) + { + pfree(attr); + for(i=0;ird_att; --- 348,354 ---- * If not, generate a constant of the default value for the attribute type, * or a NULL if the type has no default value either. */ ! Node * build_column_default(Relation rel, int attrno) { TupleDesc rd_att = rel->rd_att; Index: src/backend/parser/gram.y =================================================================== RCS file: /var/cvsup/pgsql/src/backend/parser/gram.y,v retrieving revision 2.276 diff -c -r2.276 gram.y *** src/backend/parser/gram.y 9 Dec 2001 04:39:39 -0000 2.276 --- src/backend/parser/gram.y 13 Jan 2002 14:56:43 -0000 *************** *** 1193,1203 **** --- 1193,1217 ---- CopyStmt *n = makeNode(CopyStmt); n->binary = $2; n->relname = $3; + n->attlist = NIL; n->oids = $4; n->direction = $5; n->filename = $6; n->delimiter = $7; n->null_print = $8; + $$ = (Node *)n; + } + | COPY opt_binary relation_name opt_column_list opt_with_copy copy_dirn copy_file_name copy_delimiter copy_null + { + CopyStmt *n = makeNode(CopyStmt); + n->binary = $2; + n->relname = $3; + n->attlist = $4; + n->oids = $5; + n->direction = $6; + n->filename = $7; + n->delimiter = $8; + n->null_print = $9; $$ = (Node *)n; } ; Index: src/backend/tcop/utility.c =================================================================== RCS file: /var/cvsup/pgsql/src/backend/tcop/utility.c,v retrieving revision 1.124 diff -c -r1.124 utility.c *** src/backend/tcop/utility.c 3 Jan 2002 23:21:32 -0000 1.124 --- src/backend/tcop/utility.c 13 Jan 2002 13:42:56 -0000 *************** *** 354,360 **** */ stmt->filename, stmt->delimiter, ! stmt->null_print); } break; --- 354,361 ---- */ stmt->filename, stmt->delimiter, ! stmt->null_print, ! stmt->attlist); } break; Index: src/include/commands/copy.h =================================================================== RCS file: /var/cvsup/pgsql/src/include/commands/copy.h,v retrieving revision 1.16 diff -c -r1.16 copy.h *** src/include/commands/copy.h 5 Nov 2001 17:46:33 -0000 1.16 --- src/include/commands/copy.h 13 Jan 2002 13:52:56 -0000 *************** *** 14,22 **** #ifndef COPY_H #define COPY_H extern int copy_lineno; void DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe, ! char *filename, char *delim, char *null_print); #endif /* COPY_H */ --- 14,24 ---- #ifndef COPY_H #define COPY_H + #include "utils/portal.h" + extern int copy_lineno; void DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe, ! char *filename, char *delim, char *null_print, List* _al); #endif /* COPY_H */ Index: src/include/nodes/parsenodes.h =================================================================== RCS file: /var/cvsup/pgsql/src/include/nodes/parsenodes.h,v retrieving revision 1.151 diff -c -r1.151 parsenodes.h *** src/include/nodes/parsenodes.h 5 Nov 2001 17:46:34 -0000 1.151 --- src/include/nodes/parsenodes.h 13 Jan 2002 13:40:22 -0000 *************** *** 183,188 **** --- 183,189 ---- char *filename; /* if NULL, use stdin/stdout */ char *delimiter; /* delimiter character, \t by default */ char *null_print; /* how to print NULLs, `\N' by default */ + List *attlist; /* list of attributes */ } CopyStmt; /* ----------------------