Index: src/backend/commands/copy.c =================================================================== RCS file: /var/cvsup/pgsql/src/backend/commands/copy.c,v retrieving revision 1.158 diff -c -r1.158 copy.c *** src/backend/commands/copy.c 20 Jun 2002 20:29:27 -0000 1.158 --- src/backend/commands/copy.c 10 Jul 2002 05:50:48 -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"; *************** *** 267,273 **** char *filename = stmt->filename; bool is_from = stmt->is_from; bool pipe = (stmt->filename == NULL); ! List *option; DefElem *dbinary = NULL; DefElem *doids = NULL; DefElem *ddelim = NULL; --- 269,276 ---- char *filename = stmt->filename; bool is_from = stmt->is_from; bool pipe = (stmt->filename == NULL); ! List *option; ! List *attlist = stmt->attlist; DefElem *dbinary = NULL; DefElem *doids = NULL; DefElem *ddelim = NULL; *************** *** 289,313 **** if (strcmp(defel->defname, "binary") == 0) { if (dbinary) ! elog(ERROR, "COPY: conflicting options"); dbinary = defel; } else if (strcmp(defel->defname, "oids") == 0) { if (doids) ! elog(ERROR, "COPY: conflicting options"); doids = defel; } else if (strcmp(defel->defname, "delimiter") == 0) { if (ddelim) ! elog(ERROR, "COPY: conflicting options"); ddelim = defel; } else if (strcmp(defel->defname, "null") == 0) { if (dnull) ! elog(ERROR, "COPY: conflicting options"); dnull = defel; } else --- 292,318 ---- 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 *************** *** 367,372 **** --- 372,395 ---- 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) { /* copy from file to database */ if (rel->rd_rel->relkind != RELKIND_RELATION) *************** *** 410,416 **** elog(ERROR, "COPY: %s is a directory.", filename); } } ! CopyFrom(rel, binary, oids, fp, delim, null_print); } else { /* copy from database to file */ --- 433,439 ---- elog(ERROR, "COPY: %s is a directory.", filename); } } ! CopyFrom(rel, attlist, binary, oids, fp, delim, null_print); } else { /* copy from database to file */ *************** *** 466,472 **** elog(ERROR, "COPY: %s is a directory.", filename); } } ! CopyTo(rel, binary, oids, fp, delim, null_print); } if (!pipe) --- 489,495 ---- elog(ERROR, "COPY: %s is a directory.", filename); } } ! CopyTo(rel, attlist, binary, oids, fp, delim, null_print); } if (!pipe) *************** *** 494,501 **** * 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; --- 517,524 ---- * 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; *************** *** 509,514 **** --- 532,541 ---- 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", *************** *** 517,522 **** --- 544,561 ---- 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; + } + } + } + } /* * For binary copy we really only need isvarlena, but compute it *************** *** 593,605 **** } } ! for (i = 0; i < attr_count; i++) { Datum origvalue, value; bool isnull; ! origvalue = heap_getattr(tuple, i + 1, tupDesc, &isnull); if (!binary) { --- 632,645 ---- } } ! for (i = 0; i < copy_attr_count; i++) { Datum origvalue, value; bool isnull; + int mi = attmap[i]; ! origvalue = heap_getattr(tuple, mi + 1, tupDesc, &isnull); if (!binary) { *************** *** 628,652 **** * (or for binary case, becase we must output untoasted * value). */ ! if (isvarlena[i]) value = PointerGetDatum(PG_DETOAST_DATUM(origvalue)); else value = origvalue; if (!binary) { ! string = DatumGetCString(FunctionCall3(&out_functions[i], value, ! ObjectIdGetDatum(elements[i]), ! Int32GetDatum(attr[i]->atttypmod))); CopyAttributeOut(fp, string, delim); pfree(string); } else { ! fld_size = attr[i]->attlen; CopySendData(&fld_size, sizeof(int16), fp); ! if (isvarlena[i]) { /* varlena */ Assert(fld_size == -1); --- 668,692 ---- * (or for binary case, becase we must output untoasted * value). */ ! if (isvarlena[mi]) value = PointerGetDatum(PG_DETOAST_DATUM(origvalue)); else value = origvalue; if (!binary) { ! string = DatumGetCString(FunctionCall3(&out_functions[mi], value, ! ObjectIdGetDatum(elements[mi]), ! Int32GetDatum(attr[mi]->atttypmod))); CopyAttributeOut(fp, string, delim); pfree(string); } else { ! fld_size = attr[mi]->attlen; CopySendData(&fld_size, sizeof(int16), fp); ! if (isvarlena[mi]) { /* varlena */ Assert(fld_size == -1); *************** *** 654,660 **** VARSIZE(value), fp); } ! else if (!attr[i]->attbyval) { /* fixed-length pass-by-reference */ Assert(fld_size > 0); --- 694,700 ---- VARSIZE(value), fp); } ! else if (!attr[mi]->attbyval) { /* fixed-length pass-by-reference */ Assert(fld_size > 0); *************** *** 709,721 **** * 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; --- 749,761 ---- * 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; *************** *** 732,741 **** --- 772,788 ---- 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 *************** *** 758,772 **** --- 805,846 ---- 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... */ } *************** *** 826,835 **** 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)); --- 900,909 ---- 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)); *************** *** 854,879 **** 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); } --- 928,969 ---- 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); } *************** *** 1361,1363 **** --- 1451,1501 ---- 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.337 diff -c -r2.337 gram.y *** src/backend/parser/gram.y 6 Jul 2002 20:16:35 -0000 2.337 --- src/backend/parser/gram.y 10 Jul 2002 05:50:48 -0000 *************** *** 1275,1298 **** * *****************************************************************************/ ! CopyStmt: COPY opt_binary qualified_name opt_oids copy_from ! copy_file_name copy_delimiter opt_with copy_opt_list { CopyStmt *n = makeNode(CopyStmt); n->relation = $3; ! n->is_from = $5; ! n->filename = $6; n->options = NIL; /* Concatenate user-supplied flags */ if ($2) n->options = lappend(n->options, $2); ! if ($4) ! n->options = lappend(n->options, $4); ! if ($7) ! n->options = lappend(n->options, $7); ! if ($9) ! n->options = nconc(n->options, $9); $$ = (Node *)n; } ; --- 1275,1299 ---- * *****************************************************************************/ ! CopyStmt: COPY opt_binary qualified_name opt_column_list opt_oids ! copy_from copy_file_name copy_delimiter opt_with copy_opt_list { CopyStmt *n = makeNode(CopyStmt); n->relation = $3; ! n->attlist = $4; ! n->is_from = $6; ! n->filename = $7; n->options = NIL; /* Concatenate user-supplied flags */ if ($2) n->options = lappend(n->options, $2); ! if ($5) ! n->options = lappend(n->options, $5); ! if ($8) ! n->options = lappend(n->options, $8); ! if ($10) ! n->options = nconc(n->options, $10); $$ = (Node *)n; } ; Index: src/backend/rewrite/rewriteHandler.c =================================================================== RCS file: /var/cvsup/pgsql/src/backend/rewrite/rewriteHandler.c,v retrieving revision 1.103 diff -c -r1.103 rewriteHandler.c *** src/backend/rewrite/rewriteHandler.c 20 Jun 2002 20:29:34 -0000 1.103 --- src/backend/rewrite/rewriteHandler.c 10 Jul 2002 05:50:48 -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/bin/pg_dump/pg_dump.c =================================================================== RCS file: /var/cvsup/pgsql/src/bin/pg_dump/pg_dump.c,v retrieving revision 1.270 diff -c -r1.270 pg_dump.c *** src/bin/pg_dump/pg_dump.c 4 Jul 2002 15:35:07 -0000 1.270 --- src/bin/pg_dump/pg_dump.c 10 Jul 2002 05:54:57 -0000 *************** *** 133,138 **** --- 133,139 ---- static int dumpBlobs(Archive *AH, char *, void *); static int dumpDatabase(Archive *AH); static const char *getAttrName(int attrnum, TableInfo *tblInfo); + static const char* fmtColumnList(const TableInfo* ti); extern char *optarg; extern int optind, *************** *** 842,847 **** --- 843,849 ---- int ret; bool copydone; char copybuf[COPYBUFSIZ]; + const char* column_list; if (g_verbose) write_msg(NULL, "dumping out the contents of table %s\n", classname); *************** *** 854,870 **** */ selectSourceSchema(tbinfo->relnamespace->nspname); if (oids && hasoids) { ! appendPQExpBuffer(q, "COPY %s WITH OIDS TO stdout;", fmtQualifiedId(tbinfo->relnamespace->nspname, ! classname)); } else { ! appendPQExpBuffer(q, "COPY %s TO stdout;", fmtQualifiedId(tbinfo->relnamespace->nspname, ! classname)); } res = PQexec(g_conn, q->data); if (!res || --- 856,875 ---- */ selectSourceSchema(tbinfo->relnamespace->nspname); + column_list = fmtColumnList(tbinfo); + // fmtQualifiedId(tbinfo->relnamespace->nspname,classname), + if (oids && hasoids) { ! appendPQExpBuffer(q, "COPY %s %s WITH OIDS TO stdout;", fmtQualifiedId(tbinfo->relnamespace->nspname, ! classname),column_list); } else { ! appendPQExpBuffer(q, "COPY %s %s TO stdout;", fmtQualifiedId(tbinfo->relnamespace->nspname, ! classname), column_list); } res = PQexec(g_conn, q->data); if (!res || *************** *** 1189,1196 **** { /* Dump/restore using COPY */ dumpFn = dumpClasses_nodumpData; ! sprintf(copyBuf, "COPY %s %sFROM stdin;\n", ! fmtId(tblinfo[i].relname, force_quotes), (oids && tblinfo[i].hasoids) ? "WITH OIDS " : ""); copyStmt = copyBuf; } --- 1194,1202 ---- { /* Dump/restore using COPY */ dumpFn = dumpClasses_nodumpData; ! sprintf(copyBuf, "COPY %s %s %sFROM stdin;\n", ! fmtQualifiedId(tblinfo[i].relnamespace->nspname,tblinfo[i].relname), ! fmtColumnList(&(tblinfo[i])), (oids && tblinfo[i].hasoids) ? "WITH OIDS " : ""); copyStmt = copyBuf; } *************** *** 5859,5861 **** --- 5865,5897 ---- return id_return->data; } + + /* + * return a column list clause for the qualified relname + */ + static const char* + fmtColumnList(const TableInfo* ti) + { + static PQExpBuffer q = NULL; + int numatts = ti->numatts; + char** attnames = ti->attnames; + int i; + + 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; + } + Index: src/include/nodes/parsenodes.h =================================================================== RCS file: /var/cvsup/pgsql/src/include/nodes/parsenodes.h,v retrieving revision 1.183 diff -c -r1.183 parsenodes.h *** src/include/nodes/parsenodes.h 1 Jul 2002 15:27:56 -0000 1.183 --- src/include/nodes/parsenodes.h 10 Jul 2002 05:50:48 -0000 *************** *** 858,863 **** --- 858,864 ---- { NodeTag type; RangeVar *relation; /* the relation to copy */ + List *attlist; bool is_from; /* TO or FROM */ char *filename; /* if NULL, use stdin/stdout */ List *options; /* List of DefElem nodes */ Index: src/include/rewrite/rewriteHandler.h =================================================================== RCS file: /var/cvsup/pgsql/src/include/rewrite/rewriteHandler.h,v retrieving revision 1.19 diff -c -r1.19 rewriteHandler.h *** src/include/rewrite/rewriteHandler.h 20 Jun 2002 20:29:52 -0000 1.19 --- src/include/rewrite/rewriteHandler.h 10 Jul 2002 05:50:48 -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 */