Index: src/backend/commands/copy.c =================================================================== RCS file: /var/cvsup/pgsql/src/backend/commands/copy.c,v retrieving revision 1.156 diff -c -c -3 -r1.156 copy.c *** src/backend/commands/copy.c 21 May 2002 22:59:00 -0000 1.156 --- src/backend/commands/copy.c 28 May 2002 03:28:40 -0000 *************** *** 28,33 **** --- 28,34 ---- #include "commands/copy.h" #include "commands/trigger.h" #include "executor/executor.h" + #include "rewrite/rewriteHandler.h" #include "libpq/libpq.h" #include "miscadmin.h" #include "tcop/pquery.h" *************** *** 46,58 **** /* 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); static char *CopyReadAttribute(FILE *fp, bool *isnull, char *delim, int *newline, char *null_print); static void CopyAttributeOut(FILE *fp, char *string, char *delim); static const char BinarySignature[12] = "PGBCOPY\n\377\r\n\0"; --- 47,60 ---- /* 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"; *************** *** 261,267 **** * the table. */ void ! DoCopy(const RangeVar *relation, bool binary, bool oids, bool from, bool pipe, char *filename, char *delim, char *null_print) { FILE *fp; --- 263,269 ---- * the table. */ void ! DoCopy(const RangeVar *relation, List* attlist, bool binary, bool oids, bool from, bool pipe, char *filename, char *delim, char *null_print) { FILE *fp; *************** *** 305,311 **** client_encoding = pg_get_client_encoding(); server_encoding = GetDatabaseEncoding(); #endif ! if (from) { /* copy from file to database */ if (rel->rd_rel->relkind != RELKIND_RELATION) --- 307,336 ---- client_encoding = pg_get_client_encoding(); server_encoding = GetDatabaseEncoding(); #endif ! ! if( attlist == NIL ){ ! /* ! * build an attlist of all atts in the relation. This makes the ! * Copy(To|From) code _much_ neater ! * ! * XXX: probably a better way to get this ! */ ! 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{ ! /* ! * elog(ERROR) if attlist is not valid for this relation. ! * Unsure if this ERROR should abort here, or inside Copy(To|From) ! */ ! CopyAssertAttlist(rel,attlist,from); ! } ! if (from) { /* copy from file to database */ if (rel->rd_rel->relkind != RELKIND_RELATION) *************** *** 349,355 **** elog(ERROR, "COPY: %s is a directory.", filename); } } ! CopyFrom(rel, binary, oids, fp, delim, null_print); } else { /* copy from database to file */ --- 374,380 ---- elog(ERROR, "COPY: %s is a directory.", filename); } } ! CopyFrom(rel, attlist, binary, oids, fp, delim, null_print); } else { /* copy from database to file */ *************** *** 405,411 **** elog(ERROR, "COPY: %s is a directory.", filename); } } ! CopyTo(rel, binary, oids, fp, delim, null_print); } if (!pipe) --- 430,436 ---- elog(ERROR, "COPY: %s is a directory.", filename); } } ! CopyTo(rel, attlist, binary, oids, fp, delim, null_print); } if (!pipe) *************** *** 433,440 **** * Copy from relation TO file. */ static void ! CopyTo(Relation rel, bool binary, bool oids, FILE *fp, ! char *delim, char *null_print) { HeapTuple tuple; TupleDesc tupDesc; --- 458,465 ---- * Copy from relation TO file. */ static void ! CopyTo(Relation rel, List *attlist, bool binary, bool oids, ! FILE *fp, char *delim, char *null_print) { HeapTuple tuple; TupleDesc tupDesc; *************** *** 648,660 **** * 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; --- 673,685 ---- * Copy FROM file to relation. */ static void ! CopyFrom(Relation rel, List *attlist, bool binary, bool oids, ! FILE *fp, char *delim, char *null_print) { HeapTuple tuple; TupleDesc tupDesc; Form_pg_attribute *attr; ! AttrNumber attr_count, copy_attr_count, def_attr_count; FmgrInfo *in_functions; Oid *elements; int i; *************** *** 671,680 **** --- 696,712 ---- 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; tupDesc = RelationGetDescr(rel); attr = tupDesc->attrs; 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 *************** *** 697,711 **** --- 729,770 ---- 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; + } + } } file_has_oids = oids; /* must rely on user to tell us this... */ } *************** *** 765,774 **** CHECK_FOR_INTERRUPTS(); copy_lineno++; ! /* Reset the per-output-tuple exprcontext */ ResetPerTupleExprContext(estate); ! /* Initialize all values for row to NULL */ MemSet(values, 0, attr_count * sizeof(Datum)); MemSet(nulls, 'n', attr_count * sizeof(char)); --- 824,833 ---- CHECK_FOR_INTERRUPTS(); copy_lineno++; ! /* Reset the per-output-tuple exprcontext */ ResetPerTupleExprContext(estate); ! econtext = GetPerTupleExprContext(estate); /* Initialize all values for row to NULL */ MemSet(values, 0, attr_count * sizeof(Datum)); MemSet(nulls, 'n', attr_count * sizeof(char)); *************** *** 793,818 **** elog(ERROR, "COPY TEXT: Invalid Oid"); } } ! ! for (i = 0; i < attr_count && !done; i++) { string = CopyReadAttribute(fp, &isnull, delim, &newline, null_print); ! if (isnull) ! { ! /* already set values[i] and nulls[i] */ } else if (string == NULL) done = 1; /* end of file */ else { ! values[i] = FunctionCall3(&in_functions[i], ! CStringGetDatum(string), ! ObjectIdGetDatum(elements[i]), ! Int32GetDatum(attr[i]->atttypmod)); ! nulls[i] = ' '; } } if (!done) CopyReadNewline(fp, &newline); } --- 852,893 ---- elog(ERROR, "COPY TEXT: Invalid Oid"); } } ! ! /* ! * 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, &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); } *************** *** 1300,1302 **** --- 1375,1425 ---- pfree(string_start); /* pfree pg_server_to_client result */ #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(;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); + } + Index: src/backend/parser/gram.y =================================================================== RCS file: /var/cvsup/pgsql/src/backend/parser/gram.y,v retrieving revision 2.319 diff -c -c -3 -r2.319 gram.y *** src/backend/parser/gram.y 22 May 2002 17:20:59 -0000 2.319 --- src/backend/parser/gram.y 27 May 2002 16:50:07 -0000 *************** *** 1253,1273 **** /***************************************************************************** * * QUERY : ! * COPY [BINARY] FROM/TO * [USING DELIMITERS ] * *****************************************************************************/ ! CopyStmt: COPY opt_binary qualified_name opt_with_copy copy_dirn copy_file_name copy_delimiter copy_null { CopyStmt *n = makeNode(CopyStmt); n->binary = $2; n->relation = $3; ! n->oids = $4; ! n->direction = $5; ! n->filename = $6; ! n->delimiter = $7; ! n->null_print = $8; $$ = (Node *)n; } ; --- 1253,1274 ---- /***************************************************************************** * * QUERY : ! * COPY [BINARY] ['(' ')'] FROM/TO * [USING DELIMITERS ] * *****************************************************************************/ ! CopyStmt: COPY opt_binary qualified_name opt_column_list opt_with_copy copy_dirn copy_file_name copy_delimiter copy_null { CopyStmt *n = makeNode(CopyStmt); n->binary = $2; n->relation = $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/rewrite/rewriteHandler.c =================================================================== RCS file: /var/cvsup/pgsql/src/backend/rewrite/rewriteHandler.c,v retrieving revision 1.102 diff -c -c -3 -r1.102 rewriteHandler.c *** src/backend/rewrite/rewriteHandler.c 11 Apr 2002 20:00:02 -0000 1.102 --- src/backend/rewrite/rewriteHandler.c 28 May 2002 00:07:41 -0000 *************** *** 43,49 **** static void rewriteTargetList(Query *parsetree, Relation target_relation); static TargetEntry *process_matched_tle(TargetEntry *src_tle, TargetEntry *prior_tle); - static Node *build_column_default(Relation rel, int attrno); static void markQueryForUpdate(Query *qry, bool skipOldNew); static List *matchLocks(CmdType event, RuleLock *rulelocks, int varno, Query *parsetree); --- 43,48 ---- *************** *** 411,417 **** * * If there is no default, return a NULL instead. */ ! static Node * build_column_default(Relation rel, int attrno) { TupleDesc rd_att = rel->rd_att; --- 410,416 ---- * * If there is no default, return a NULL instead. */ ! Node * build_column_default(Relation rel, int attrno) { TupleDesc rd_att = rel->rd_att; Index: src/backend/tcop/utility.c =================================================================== RCS file: /var/cvsup/pgsql/src/backend/tcop/utility.c,v retrieving revision 1.156 diff -c -c -3 -r1.156 utility.c *** src/backend/tcop/utility.c 21 May 2002 22:18:08 -0000 1.156 --- src/backend/tcop/utility.c 27 May 2002 16:52:05 -0000 *************** *** 351,356 **** --- 351,357 ---- SetQuerySnapshot(); DoCopy(stmt->relation, + stmt->attlist, stmt->binary, stmt->oids, (bool) (stmt->direction == FROM), Index: src/include/commands/copy.h =================================================================== RCS file: /var/cvsup/pgsql/src/include/commands/copy.h,v retrieving revision 1.17 diff -c -c -3 -r1.17 copy.h *** src/include/commands/copy.h 29 Mar 2002 19:06:21 -0000 1.17 --- src/include/commands/copy.h 27 May 2002 16:52:50 -0000 *************** *** 18,25 **** extern int copy_lineno; ! void DoCopy(const RangeVar *relation, bool binary, bool oids, ! bool from, bool pipe, char *filename, char *delim, char *null_print); #endif /* COPY_H */ --- 18,25 ---- extern int copy_lineno; ! void DoCopy(const RangeVar *relation, List *attlist, bool binary, ! bool oids, bool from, bool pipe, char *filename, char *delim, char *null_print); #endif /* COPY_H */ Index: src/include/nodes/parsenodes.h =================================================================== RCS file: /var/cvsup/pgsql/src/include/nodes/parsenodes.h,v retrieving revision 1.179 diff -c -c -3 -r1.179 parsenodes.h *** src/include/nodes/parsenodes.h 22 May 2002 17:21:01 -0000 1.179 --- src/include/nodes/parsenodes.h 27 May 2002 16:44:33 -0000 *************** *** 853,858 **** --- 853,859 ---- NodeTag type; bool binary; /* is a binary copy? */ RangeVar *relation; /* the relation to copy */ + List* attlist; /* optional paren'd list of att. names */ bool oids; /* copy oid's? */ int direction; /* TO or FROM */ char *filename; /* if NULL, use stdin/stdout */ Index: src/include/rewrite/rewriteHandler.h =================================================================== RCS file: /var/cvsup/pgsql/src/include/rewrite/rewriteHandler.h,v retrieving revision 1.18 diff -c -c -3 -r1.18 rewriteHandler.h *** src/include/rewrite/rewriteHandler.h 5 Nov 2001 17:46:35 -0000 1.18 --- src/include/rewrite/rewriteHandler.h 28 May 2002 00:07:22 -0000 *************** *** 16,22 **** #include "nodes/parsenodes.h" - extern List *QueryRewrite(Query *parsetree); #endif /* REWRITEHANDLER_H */ --- 16,22 ---- #include "nodes/parsenodes.h" extern List *QueryRewrite(Query *parsetree); + extern Node *build_column_default(Relation rel, int attrno); #endif /* REWRITEHANDLER_H */