Index: doc/src/sgml/ref/copy.sgml =================================================================== RCS file: /home/alvherre/cvs/pgsql/doc/src/sgml/ref/copy.sgml,v retrieving revision 1.74 diff -c -p -r1.74 copy.sgml *** doc/src/sgml/ref/copy.sgml 22 Apr 2006 03:03:11 -0000 1.74 --- doc/src/sgml/ref/copy.sgml 27 Aug 2006 04:53:19 -0000 *************** *** 1,5 **** --- 1,5 ---- *************** COPY tabl *** 33,39 **** [ ESCAPE [ AS ] 'escape' ] [ FORCE NOT NULL column [, ...] ] ! COPY tablename [ ( column [, ...] ) ] TO { 'filename' | STDOUT } [ [ WITH ] [ BINARY ] --- 33,39 ---- [ ESCAPE [ AS ] 'escape' ] [ FORCE NOT NULL column [, ...] ] ! COPY { tablename | viewname | ( select_statement ) } [ ( column [, ...] ) ] TO { 'filename' | STDOUT } [ [ WITH ] [ BINARY ] *************** COPY tabl *** 55,61 **** COPY moves data between PostgreSQL tables and standard file-system files. COPY TO copies the contents of a table ! to a file, while COPY FROM copies data from a file to a table (appending the data to whatever is in the table already). --- 55,63 ---- COPY moves data between PostgreSQL tables and standard file-system files. COPY TO copies the contents of a table ! to a file, which also work on views and arbitrary ! SELECT statements. (Internally, the view case is rewitten as ! SELECT * FROM viewname.) COPY FROM copies data from a file to a table (appending the data to whatever is in the table already). *************** COPY tabl *** 65,71 **** only copy the data in the specified columns to or from the file. If there are any columns in the table that are not in the column list, COPY FROM will insert the default values for ! those columns. --- 67,76 ---- only copy the data in the specified columns to or from the file. If there are any columns in the table that are not in the column list, COPY FROM will insert the default values for ! those columns. COPY TO also accepts the list of ! columns, which can be useful for exporting only a subset of the ! columns that are in the table, view or select statement, or ! reordering them in the export. *************** COPY tabl *** 148,154 **** Specifies copying the OID for each row. (An error is raised if OIDS is specified for a table that does not ! have OIDs.) --- 153,159 ---- Specifies copying the OID for each row. (An error is raised if OIDS is specified for a table that does not ! have OIDs, or in the case of COPY (SELECT) TO.) Index: src/backend/commands/copy.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/copy.c,v retrieving revision 1.268 diff -c -p -r1.268 copy.c *** src/backend/commands/copy.c 14 Jul 2006 14:52:18 -0000 1.268 --- src/backend/commands/copy.c 28 Aug 2006 03:12:07 -0000 *************** *** 31,38 **** #include "libpq/pqformat.h" #include "mb/pg_wchar.h" #include "miscadmin.h" ! #include "parser/parse_relation.h" #include "rewrite/rewriteHandler.h" #include "storage/fd.h" #include "tcop/tcopprot.h" #include "utils/acl.h" --- 31,39 ---- #include "libpq/pqformat.h" #include "mb/pg_wchar.h" #include "miscadmin.h" ! #include "parser/analyze.h" #include "rewrite/rewriteHandler.h" + #include "optimizer/planner.h" #include "storage/fd.h" #include "tcop/tcopprot.h" #include "utils/acl.h" *************** typedef struct CopyStateData *** 96,104 **** bool need_transcoding; /* client encoding diff from server? */ bool encoding_embeds_ascii; /* ASCII can be non-first byte? */ uint64 processed; /* # of tuples processed */ /* parameters from the COPY command */ ! Relation rel; /* relation to copy to or from */ List *attnumlist; /* integer list of attnums to copy */ bool binary; /* binary format? */ bool oids; /* include OIDs? */ --- 97,125 ---- bool need_transcoding; /* client encoding diff from server? */ bool encoding_embeds_ascii; /* ASCII can be non-first byte? */ uint64 processed; /* # of tuples processed */ + FmgrInfo *out_functions; /* array of output functions (COPY TO) */ + MemoryContext rowcxt; /* memory for output formatting */ + List *force_quote_orig; /* quote attribute names */ + List *force_notnull; + bool *force_quote; /* quote attribute? */ + char *null_print_client; /* final version of null_print */ + Datum *values; /* row data (COPY TO) */ + bool *isnull; /* row 'isnull' flags (COPY TO) */ + Oid out_func_oid; + bool isvarlena; /* tuple should be de-toasted */ + TupleDesc attrinfo; + int natts; /* parameters from the COPY command */ ! /* relation to copy to or from, in the "COPY table" case */ ! Relation rel; ! ! /* The following are valid in the "COPY (SELECT)" case: */ ! DestReceiver *dest; /* DestReceiver for the COPY (SELECT) case */ ! QueryDesc *desc; /* descriptor for SELECT query */ ! Plan *plan; /* plan for SELECT */ ! ! /* These are valid in both cases */ List *attnumlist; /* integer list of attnums to copy */ bool binary; /* binary format? */ bool oids; /* include OIDs? */ *************** typedef struct CopyStateData *** 153,158 **** --- 174,184 ---- typedef CopyStateData *CopyState; + typedef struct + { + DestReceiver pub; /* publicly-known function pointers */ + void *cstate; /* CopyStateData we are working with */ + } DR_copy; /* * These macros centralize code used to process line_buf and raw_buf buffers. *************** static const char BinarySignature[11] = *** 223,230 **** --- 249,259 ---- /* non-export function prototypes */ + static uint64 DoCopyRelation(const CopyStmt *stmt, CopyState cstate); + static uint64 DoCopySelect(const CopyStmt *stmt, CopyState cstate); static void DoCopyTo(CopyState cstate); static void CopyTo(CopyState cstate); + static void CopyValuesTo(CopyState cstate, Oid oid, Datum *values, bool *isnull); static void CopyFrom(CopyState cstate); static bool CopyReadLine(CopyState cstate); static bool CopyReadLineText(CopyState cstate); *************** static Datum CopyReadBinaryAttribute(Cop *** 239,246 **** static void CopyAttributeOutText(CopyState cstate, char *string); static void CopyAttributeOutCSV(CopyState cstate, char *string, bool use_quote, bool single_attr); ! static List *CopyGetAttnums(Relation rel, List *attnamelist); static char *limit_printout_length(const char *str); /* Low-level communications functions */ static void SendCopyBegin(CopyState cstate); --- 268,276 ---- static void CopyAttributeOutText(CopyState cstate, char *string); static void CopyAttributeOutCSV(CopyState cstate, char *string, bool use_quote, bool single_attr); ! static List *CopyGetAttnums(TupleDesc tupDesc, List *attnamelist); static char *limit_printout_length(const char *str); + static void prepare_cstate_info(CopyState cstate, TupleDesc typeinfo, int numAttrs); /* Low-level communications functions */ static void SendCopyBegin(CopyState cstate); *************** uint64 *** 697,713 **** DoCopy(const CopyStmt *stmt) { CopyState cstate; - RangeVar *relation = stmt->relation; - char *filename = stmt->filename; - bool is_from = stmt->is_from; - bool pipe = (stmt->filename == NULL); - List *attnamelist = stmt->attlist; - List *force_quote = NIL; - List *force_notnull = NIL; - AclMode required_access = (is_from ? ACL_INSERT : ACL_SELECT); - AclResult aclresult; ListCell *option; - uint64 processed; /* Allocate workspace and zero all fields */ cstate = (CopyStateData *) palloc0(sizeof(CopyStateData)); --- 727,733 ---- *************** DoCopy(const CopyStmt *stmt) *** 783,801 **** } else if (strcmp(defel->defname, "force_quote") == 0) { ! if (force_quote) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); ! force_quote = (List *) defel->arg; } else if (strcmp(defel->defname, "force_notnull") == 0) { ! if (force_notnull) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); ! force_notnull = (List *) defel->arg; } else elog(ERROR, "option \"%s\" not recognized", --- 803,821 ---- } else if (strcmp(defel->defname, "force_quote") == 0) { ! if (cstate->force_quote_orig) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); ! cstate->force_quote_orig = (List *) defel->arg; } else if (strcmp(defel->defname, "force_notnull") == 0) { ! if (cstate->force_notnull) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("conflicting or redundant options"))); ! cstate->force_notnull = (List *) defel->arg; } else elog(ERROR, "option \"%s\" not recognized", *************** DoCopy(const CopyStmt *stmt) *** 888,908 **** errmsg("COPY escape must be a single character"))); /* Check force_quote */ ! if (!cstate->csv_mode && force_quote != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY force quote available only in CSV mode"))); ! if (force_quote != NIL && is_from) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY force quote only available using COPY TO"))); /* Check force_notnull */ ! if (!cstate->csv_mode && force_notnull != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY force not null available only in CSV mode"))); ! if (force_notnull != NIL && !is_from) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY force not null only available using COPY FROM"))); --- 908,928 ---- errmsg("COPY escape must be a single character"))); /* Check force_quote */ ! if (!cstate->csv_mode && cstate->force_quote != NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY force quote available only in CSV mode"))); ! if (cstate->force_quote != NULL && stmt->is_from) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY force quote only available using COPY TO"))); /* Check force_notnull */ ! if (!cstate->csv_mode && cstate->force_notnull != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY force not null available only in CSV mode"))); ! if (cstate->force_notnull != NIL && !stmt->is_from) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("COPY force not null only available using COPY FROM"))); *************** DoCopy(const CopyStmt *stmt) *** 920,931 **** (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("CSV quote character must not appear in the NULL specification"))); /* Open and lock the relation, using the appropriate lock type. */ cstate->rel = heap_openrv(relation, ! (is_from ? RowExclusiveLock : AccessShareLock)); /* check read-only transaction */ ! if (XactReadOnly && is_from && !isTempNamespace(RelationGetNamespace(cstate->rel))) ereport(ERROR, (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), --- 940,975 ---- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("CSV quote character must not appear in the NULL specification"))); + if (stmt->filename != NULL && !superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to COPY to or from a file"), + errhint("Anyone can COPY to stdout or from stdin. " + "psql's \\copy command also works for anyone."))); + + if (stmt->relation) + return DoCopyRelation(stmt, cstate); + else + return DoCopySelect(stmt, cstate); + } + + static uint64 + DoCopyRelation(const CopyStmt *stmt, CopyState cstate) + { + RangeVar *relation = stmt->relation; + char *filename = stmt->filename; + bool pipe = (stmt->filename == NULL); + List *attnamelist = stmt->attlist; + AclMode required_access = (stmt->is_from ? ACL_INSERT : ACL_SELECT); + AclResult aclresult; + uint64 processed; + /* Open and lock the relation, using the appropriate lock type. */ cstate->rel = heap_openrv(relation, ! (stmt->is_from ? RowExclusiveLock : AccessShareLock)); /* check read-only transaction */ ! if (XactReadOnly && stmt->is_from && !isTempNamespace(RelationGetNamespace(cstate->rel))) ereport(ERROR, (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), *************** DoCopy(const CopyStmt *stmt) *** 937,948 **** if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_CLASS, RelationGetRelationName(cstate->rel)); - if (!pipe && !superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to COPY to or from a file"), - errhint("Anyone can COPY to stdout or from stdin. " - "psql's \\copy command also works for anyone."))); /* Don't allow COPY w/ OIDs to or from a table without them */ if (cstate->oids && !cstate->rel->rd_rel->relhasoids) --- 981,986 ---- *************** DoCopy(const CopyStmt *stmt) *** 952,967 **** RelationGetRelationName(cstate->rel)))); /* Generate or convert list of attributes to process */ ! cstate->attnumlist = CopyGetAttnums(cstate->rel, attnamelist); /* Convert FORCE QUOTE name list to column numbers, check validity */ ! if (force_quote) { TupleDesc tupDesc = RelationGetDescr(cstate->rel); Form_pg_attribute *attr = tupDesc->attrs; ListCell *cur; ! cstate->force_quote_atts = CopyGetAttnums(cstate->rel, force_quote); foreach(cur, cstate->force_quote_atts) { --- 990,1005 ---- RelationGetRelationName(cstate->rel)))); /* Generate or convert list of attributes to process */ ! cstate->attnumlist = CopyGetAttnums(RelationGetDescr(cstate->rel), attnamelist); /* Convert FORCE QUOTE name list to column numbers, check validity */ ! if (cstate->force_quote_orig) { TupleDesc tupDesc = RelationGetDescr(cstate->rel); Form_pg_attribute *attr = tupDesc->attrs; ListCell *cur; ! cstate->force_quote_atts = CopyGetAttnums(tupDesc, cstate->force_quote_orig); foreach(cur, cstate->force_quote_atts) { *************** DoCopy(const CopyStmt *stmt) *** 976,989 **** } /* Convert FORCE NOT NULL name list to column numbers, check validity */ ! if (force_notnull) { TupleDesc tupDesc = RelationGetDescr(cstate->rel); Form_pg_attribute *attr = tupDesc->attrs; ListCell *cur; ! cstate->force_notnull_atts = CopyGetAttnums(cstate->rel, ! force_notnull); foreach(cur, cstate->force_notnull_atts) { --- 1014,1026 ---- } /* Convert FORCE NOT NULL name list to column numbers, check validity */ ! if (cstate->force_notnull) { TupleDesc tupDesc = RelationGetDescr(cstate->rel); Form_pg_attribute *attr = tupDesc->attrs; ListCell *cur; ! cstate->force_notnull_atts = CopyGetAttnums(tupDesc, cstate->force_notnull); foreach(cur, cstate->force_notnull_atts) { *************** DoCopy(const CopyStmt *stmt) *** 1019,1025 **** cstate->copy_dest = COPY_FILE; /* default */ ! if (is_from) { /* copy from file to database */ if (cstate->rel->rd_rel->relkind != RELKIND_RELATION) { --- 1056,1062 ---- cstate->copy_dest = COPY_FILE; /* default */ ! if (stmt->is_from) { /* copy from file to database */ if (cstate->rel->rd_rel->relkind != RELKIND_RELATION) { *************** DoCopy(const CopyStmt *stmt) *** 1149,1159 **** * got; if writing, we should hold the lock until end of transaction to * ensure that updates will be committed before lock is released. */ ! heap_close(cstate->rel, (is_from ? NoLock : AccessShareLock)); /* Clean up storage (probably not really necessary) */ processed = cstate->processed; pfree(cstate->attribute_buf.data); pfree(cstate->line_buf.data); pfree(cstate->raw_buf); --- 1186,1372 ---- * got; if writing, we should hold the lock until end of transaction to * ensure that updates will be committed before lock is released. */ ! heap_close(cstate->rel, (stmt->is_from ? NoLock : AccessShareLock)); ! ! /* Clean up storage (probably not really necessary) */ ! processed = cstate->processed; ! ! pfree(cstate->attribute_buf.data); ! pfree(cstate->line_buf.data); ! pfree(cstate->raw_buf); ! pfree(cstate); ! ! return processed; ! } ! ! static uint64 ! DoCopySelect(const CopyStmt *stmt, CopyState cstate) ! { ! char *filename = stmt->filename; ! bool pipe = (stmt->filename == NULL); ! List *attnamelist = stmt->attlist; ! uint64 processed = 0; ! List *queries; ! Query *query; ! ! /* Don't allow COPY w/ OIDs from a select */ ! if (cstate->oids) ! ereport(ERROR, ! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("COPY (SELECT) WITH OIDS is not supported"))); ! ! queries = parse_analyze((Node *) stmt->selectstmt, NULL, NULL, 0); ! ! /* shouldn't happen */ ! if (list_length(queries) != 1) ! elog(ERROR, "COPY (SELECT) returns more than one Query"); ! query = linitial(queries); ! ! /* pass the query through the rewriter */ ! queries = QueryRewrite(query); ! ! /* shouldn't happen, or could it? */ ! if (list_length(queries) != 1) ! elog(ERROR, "COPY (SELECT) returns more than one Query after rewrite"); ! query = linitial(queries); ! ! cstate->plan = planner(query, true, 0, NULL); ! ! cstate->dest = CreateDestReceiver(DestCopyDR, NULL); ! ((DR_copy *) cstate->dest)->cstate = cstate; ! cstate->desc = CreateQueryDesc(query, cstate->plan, ! ActiveSnapshot, InvalidSnapshot, ! cstate->dest, NULL, false); ! /* Execute query */ ! ExecutorStart(cstate->desc, false); ! ! /* Generate or convert list of attributes to process */ ! cstate->attnumlist = CopyGetAttnums(cstate->desc->tupDesc, attnamelist); ! ! /* Convert FORCE QUOTE name list to column numbers, check validity */ ! if (cstate->force_quote_orig) ! { ! TupleDesc tupDesc = cstate->desc->tupDesc; ! Form_pg_attribute *attr = tupDesc->attrs; ! ListCell *cur; ! ! cstate->force_quote_atts = CopyGetAttnums(tupDesc, cstate->force_quote_orig); ! ! foreach(cur, cstate->force_quote_atts) ! { ! int attnum = lfirst_int(cur); ! ! if (!list_member_int(cstate->attnumlist, attnum)) ! ereport(ERROR, ! (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), ! errmsg("FORCE QUOTE column \"%s\" not referenced by COPY", ! NameStr(attr[attnum - 1]->attname)))); ! } ! } ! ! /* Convert FORCE NOT NULL name list to column numbers, check validity */ ! if (cstate->force_notnull) ! { ! TupleDesc tupDesc = cstate->desc->tupDesc; ! Form_pg_attribute *attr = tupDesc->attrs; ! ListCell *cur; ! ! cstate->force_notnull_atts = CopyGetAttnums(tupDesc, cstate->force_notnull); ! ! foreach(cur, cstate->force_notnull_atts) ! { ! int attnum = lfirst_int(cur); ! ! if (!list_member_int(cstate->attnumlist, attnum)) ! ereport(ERROR, ! (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), ! errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY", ! NameStr(attr[attnum - 1]->attname)))); ! } ! } ! ! /* Set up variables to avoid per-attribute overhead. */ ! initStringInfo(&cstate->attribute_buf); ! initStringInfo(&cstate->line_buf); ! cstate->line_buf_converted = false; ! cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1); ! cstate->raw_buf_index = cstate->raw_buf_len = 0; ! cstate->processed = 0; ! ! /* ! * Set up encoding conversion info. Even if the client and server ! * encodings are the same, we must apply pg_client_to_server() to ! * validate data in multibyte encodings. ! */ ! cstate->client_encoding = pg_get_client_encoding(); ! cstate->need_transcoding = ! (cstate->client_encoding != GetDatabaseEncoding() || ! pg_database_encoding_max_length() > 1); ! /* See Multibyte encoding comment above */ ! cstate->encoding_embeds_ascii = PG_ENCODING_IS_CLIENT_ONLY(cstate->client_encoding); ! ! cstate->copy_dest = COPY_FILE; /* default */ ! ! /* copy from database to file */ ! if (pipe) ! { ! if (whereToSendOutput == DestRemote) ! cstate->fe_copy = true; ! else ! cstate->copy_file = stdout; ! } ! else ! { ! mode_t oumask; /* Pre-existing umask value */ ! struct stat st; ! ! /* ! * Prevent write to relative path ... too easy to shoot oneself in ! * the foot by overwriting a database file ... ! */ ! if (!is_absolute_path(filename)) ! ereport(ERROR, ! (errcode(ERRCODE_INVALID_NAME), ! errmsg("relative path not allowed for COPY to file"))); ! ! oumask = umask((mode_t) 022); ! cstate->copy_file = AllocateFile(filename, PG_BINARY_W); ! umask(oumask); ! ! if (cstate->copy_file == NULL) ! ereport(ERROR, ! (errcode_for_file_access(), ! errmsg("could not open file \"%s\" for writing: %m", ! filename))); ! ! fstat(fileno(cstate->copy_file), &st); ! if (S_ISDIR(st.st_mode)) ! { ! FreeFile(cstate->copy_file); ! ereport(ERROR, ! (errcode(ERRCODE_WRONG_OBJECT_TYPE), ! errmsg("\"%s\" is a directory", filename))); ! } ! } ! ! DoCopyTo(cstate); ! ! if (!pipe) ! { ! /* we assume only the write case could fail here */ ! if (FreeFile(cstate->copy_file)) ! ereport(ERROR, ! (errcode_for_file_access(), ! errmsg("could not write to file \"%s\": %m", ! filename))); ! } /* Clean up storage (probably not really necessary) */ processed = cstate->processed; + ExecutorEnd(cstate->desc); + FreeQueryDesc(cstate->desc); + pfree(cstate->attribute_buf.data); pfree(cstate->line_buf.data); pfree(cstate->raw_buf); *************** DoCopyTo(CopyState cstate) *** 1193,1250 **** PG_END_TRY(); } /* ! * Copy from relation TO file. */ static void CopyTo(CopyState cstate) { - HeapTuple tuple; TupleDesc tupDesc; - HeapScanDesc scandesc; int num_phys_attrs; - int attr_count; Form_pg_attribute *attr; - FmgrInfo *out_functions; - bool *force_quote; - char *string; - char *null_print_client; ListCell *cur; - MemoryContext oldcontext; - MemoryContext mycontext; ! tupDesc = cstate->rel->rd_att; attr = tupDesc->attrs; num_phys_attrs = tupDesc->natts; ! attr_count = list_length(cstate->attnumlist); ! null_print_client = cstate->null_print; /* default */ /* We use fe_msgbuf as a per-row buffer regardless of copy_dest */ cstate->fe_msgbuf = makeStringInfo(); /* Get info about the columns we need to process. */ ! out_functions = (FmgrInfo *) palloc(num_phys_attrs * sizeof(FmgrInfo)); ! force_quote = (bool *) palloc(num_phys_attrs * sizeof(bool)); foreach(cur, cstate->attnumlist) { int attnum = lfirst_int(cur); - Oid out_func_oid; - bool isvarlena; - - if (cstate->binary) - getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid, - &out_func_oid, - &isvarlena); - else - getTypeOutputInfo(attr[attnum - 1]->atttypid, - &out_func_oid, - &isvarlena); - fmgr_info(out_func_oid, &out_functions[attnum - 1]); if (list_member_int(cstate->force_quote_atts, attnum)) ! force_quote[attnum - 1] = true; else ! force_quote[attnum - 1] = false; } /* --- 1406,1479 ---- PG_END_TRY(); } + static void + prepare_cstate_info(CopyState cstate, TupleDesc typeinfo, int numAttrs) + { + Form_pg_attribute *attr; + ListCell *cur; + + /* get rid of any old data */ + attr = typeinfo->attrs; + cstate->attrinfo = typeinfo; + cstate->natts = numAttrs; + if (numAttrs <= 0) + return; + + foreach(cur, cstate->attnumlist) + { + int attnum = lfirst_int(cur); + + if (cstate->binary) + getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid, + &cstate->out_func_oid, + &cstate->isvarlena); + else + getTypeOutputInfo(attr[attnum - 1]->atttypid, + &cstate->out_func_oid, + &cstate->isvarlena); + fmgr_info(cstate->out_func_oid, &cstate->out_functions[attnum - 1]); + } + } + /* ! * Copy from select or relation TO file. */ static void CopyTo(CopyState cstate) { TupleDesc tupDesc; int num_phys_attrs; Form_pg_attribute *attr; ListCell *cur; ! if (cstate->rel) ! tupDesc = cstate->rel->rd_att; ! else ! tupDesc = cstate->desc->tupDesc; attr = tupDesc->attrs; num_phys_attrs = tupDesc->natts; ! cstate->null_print_client = cstate->null_print; /* default */ /* We use fe_msgbuf as a per-row buffer regardless of copy_dest */ cstate->fe_msgbuf = makeStringInfo(); /* Get info about the columns we need to process. */ ! cstate->out_functions = (FmgrInfo *) palloc(num_phys_attrs * sizeof(FmgrInfo)); ! cstate->force_quote = (bool *) palloc(num_phys_attrs * sizeof(bool)); ! ! cstate->isnull = (bool *) palloc(num_phys_attrs * sizeof(bool)); ! cstate->values = (Datum *) palloc(num_phys_attrs * sizeof(Datum)); ! ! prepare_cstate_info(cstate, tupDesc, tupDesc->natts); ! foreach(cur, cstate->attnumlist) { int attnum = lfirst_int(cur); if (list_member_int(cstate->force_quote_atts, attnum)) ! cstate->force_quote[attnum - 1] = true; else ! cstate->force_quote[attnum - 1] = false; } /* *************** CopyTo(CopyState cstate) *** 1253,1259 **** * datatype output routines, and should be faster than retail pfree's * anyway. (We don't need a whole econtext as CopyFrom does.) */ ! mycontext = AllocSetContextCreate(CurrentMemoryContext, "COPY TO", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, --- 1482,1488 ---- * datatype output routines, and should be faster than retail pfree's * anyway. (We don't need a whole econtext as CopyFrom does.) */ ! cstate->rowcxt = AllocSetContextCreate(CurrentMemoryContext, "COPY TO", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, *************** CopyTo(CopyState cstate) *** 1282,1288 **** * encoding, because it will be sent directly with CopySendString. */ if (cstate->need_transcoding) ! null_print_client = pg_server_to_client(cstate->null_print, cstate->null_print_len); /* if a header has been requested send the line */ --- 1511,1517 ---- * encoding, because it will be sent directly with CopySendString. */ if (cstate->need_transcoding) ! cstate->null_print_client = pg_server_to_client(cstate->null_print, cstate->null_print_len); /* if a header has been requested send the line */ *************** CopyTo(CopyState cstate) *** 1309,1408 **** } } ! scandesc = heap_beginscan(cstate->rel, ActiveSnapshot, 0, NULL); ! ! while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL) { ! bool need_delim = false; ! ! CHECK_FOR_INTERRUPTS(); ! MemoryContextReset(mycontext); ! oldcontext = MemoryContextSwitchTo(mycontext); ! ! if (cstate->binary) { ! /* Binary per-tuple header */ ! CopySendInt16(cstate, attr_count); ! /* Send OID if wanted --- note attr_count doesn't include it */ ! if (cstate->oids) ! { ! Oid oid = HeapTupleGetOid(tuple); ! /* Hack --- assume Oid is same size as int32 */ ! CopySendInt32(cstate, sizeof(int32)); ! CopySendInt32(cstate, oid); ! } ! } ! else ! { ! /* Text format has no per-tuple header, but send OID if wanted */ ! /* Assume digits don't need any quoting or encoding conversion */ if (cstate->oids) ! { ! string = DatumGetCString(DirectFunctionCall1(oidout, ! ObjectIdGetDatum(HeapTupleGetOid(tuple)))); ! CopySendString(cstate, string); ! need_delim = true; ! } ! } ! ! foreach(cur, cstate->attnumlist) ! { ! int attnum = lfirst_int(cur); ! Datum value; ! bool isnull; ! ! value = heap_getattr(tuple, attnum, tupDesc, &isnull); ! ! if (!cstate->binary) ! { ! if (need_delim) ! CopySendChar(cstate, cstate->delim[0]); ! need_delim = true; ! } ! if (isnull) ! { ! if (!cstate->binary) ! CopySendString(cstate, null_print_client); ! else ! CopySendInt32(cstate, -1); ! } ! else ! { ! if (!cstate->binary) ! { ! string = OutputFunctionCall(&out_functions[attnum - 1], ! value); ! if (cstate->csv_mode) ! CopyAttributeOutCSV(cstate, string, ! force_quote[attnum - 1], ! list_length(cstate->attnumlist) == 1); ! else ! CopyAttributeOutText(cstate, string); ! } ! else ! { ! bytea *outputbytes; ! ! outputbytes = SendFunctionCall(&out_functions[attnum - 1], ! value); ! CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); ! CopySendData(cstate, VARDATA(outputbytes), ! VARSIZE(outputbytes) - VARHDRSZ); ! } ! } } ! CopySendEndOfRow(cstate); ! ! MemoryContextSwitchTo(oldcontext); ! ! cstate->processed++; } ! ! heap_endscan(scandesc); if (cstate->binary) { --- 1538,1567 ---- } } ! if (cstate->rel) { ! HeapScanDesc scandesc; ! HeapTuple tuple; ! ! scandesc = heap_beginscan(cstate->rel, ActiveSnapshot, 0, NULL); ! while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL) { ! Oid oid = InvalidOid; ! CHECK_FOR_INTERRUPTS(); ! if (cstate->oids) ! oid = HeapTupleGetOid(tuple); ! heap_deform_tuple(tuple, tupDesc, cstate->values, cstate->isnull); ! CopyValuesTo(cstate, oid, cstate->values, cstate->isnull); } ! heap_endscan(scandesc); } ! else ! ExecutorRun(cstate->desc, ForwardScanDirection, 0); if (cstate->binary) { *************** CopyTo(CopyState cstate) *** 1412,1421 **** CopySendEndOfRow(cstate); } ! MemoryContextDelete(mycontext); ! pfree(out_functions); ! pfree(force_quote); } --- 1571,1582 ---- CopySendEndOfRow(cstate); } ! MemoryContextDelete(cstate->rowcxt); ! pfree(cstate->out_functions); ! pfree(cstate->force_quote); ! pfree(cstate->values); ! pfree(cstate->isnull); } *************** CopyAttributeOutCSV(CopyState cstate, ch *** 3057,3070 **** * columns). */ static List * ! CopyGetAttnums(Relation rel, List *attnamelist) { List *attnums = NIL; if (attnamelist == NIL) { /* Generate default column list */ - TupleDesc tupDesc = RelationGetDescr(rel); Form_pg_attribute *attr = tupDesc->attrs; int attr_count = tupDesc->natts; int i; --- 3218,3230 ---- * columns). */ static List * ! CopyGetAttnums(TupleDesc tupDesc, List *attnamelist) { List *attnums = NIL; if (attnamelist == NIL) { /* Generate default column list */ Form_pg_attribute *attr = tupDesc->attrs; int attr_count = tupDesc->natts; int i; *************** CopyGetAttnums(Relation rel, List *attna *** 3085,3099 **** { char *name = strVal(lfirst(l)); int attnum; /* Lookup column name */ ! /* Note we disallow system columns here */ ! attnum = attnameAttNum(rel, name, false); if (attnum == InvalidAttrNumber) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), ! errmsg("column \"%s\" of relation \"%s\" does not exist", ! name, RelationGetRelationName(rel)))); /* Check for duplicates */ if (list_member_int(attnums, attnum)) ereport(ERROR, --- 3245,3269 ---- { char *name = strVal(lfirst(l)); int attnum; + int i; /* Lookup column name */ ! attnum = InvalidAttrNumber; ! for (i = 0; i < tupDesc->natts; i++) ! { ! if (tupDesc->attrs[i]->attisdropped) ! continue; ! if (namestrcmp(&(tupDesc->attrs[i]->attname), name) == 0) ! { ! attnum = tupDesc->attrs[i]->attnum; ! break; ! } ! } if (attnum == InvalidAttrNumber) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), ! errmsg("column \"%s\" does not exist", ! name))); /* Check for duplicates */ if (list_member_int(attnums, attnum)) ereport(ERROR, *************** CopyGetAttnums(Relation rel, List *attna *** 3106,3108 **** --- 3276,3438 ---- return attnums; } + + /* + * Copies data from 'values' array + */ + static void + CopyValuesTo(CopyState cstate, Oid oid, Datum *values, bool *isnull) + { + int attr_count; + char *string; + ListCell *cur; + MemoryContext oldcontext; + bool need_delim = false; + + Assert(cstate); + Assert(values); + Assert(isnull); + + attr_count = list_length(cstate->attnumlist); + + MemoryContextReset(cstate->rowcxt); + oldcontext = MemoryContextSwitchTo(cstate->rowcxt); + + if (cstate->binary) + { + /* Binary per-tuple header */ + CopySendInt16(cstate, attr_count); + /* Send OID if wanted --- note attr_count doesn't include it */ + if (cstate->oids) + { + Assert(oid); + /* Hack --- assume Oid is same size as int32 */ + CopySendInt32(cstate, sizeof(int32)); + CopySendInt32(cstate, oid); + } + } + else + { + /* Text format has no per-tuple header, but send OID if wanted */ + /* Assume digits don't need any quoting or encoding conversion */ + if (cstate->oids) + { + Assert(oid); + string = DatumGetCString(DirectFunctionCall1(oidout, + ObjectIdGetDatum(oid))); + CopySendString(cstate, string); + need_delim = true; + } + } + + foreach(cur, cstate->attnumlist) + { + int attnum = lfirst_int(cur); + + if (!cstate->binary) + { + if (need_delim) + CopySendChar(cstate, cstate->delim[0]); + need_delim = true; + } + if (isnull[attnum-1]) + { + if (!cstate->binary) + CopySendString(cstate, cstate->null_print_client); + else + CopySendInt32(cstate, -1); + } + else + { + if (!cstate->binary) + { + string = DatumGetCString(FunctionCall1( + &cstate->out_functions[attnum - 1], + values[attnum - 1])); + if (cstate->csv_mode) + CopyAttributeOutCSV(cstate, string, + cstate->force_quote[attnum - 1], + list_length(cstate->attnumlist) == 1); + else + CopyAttributeOutText(cstate, string); + } + else + { + bytea *outputbytes; + + outputbytes = SendFunctionCall(&cstate->out_functions[attnum - 1], + values[attnum - 1]); + + /* We assume the result will not have been toasted */ + CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); + CopySendData(cstate, VARDATA(outputbytes), + VARSIZE(outputbytes) - VARHDRSZ); + } + } + } + CopySendEndOfRow(cstate); + MemoryContextSwitchTo(oldcontext); + + cstate->processed++; + } + + + /* + * Callback for executor destination receiver (COPY view TO) + */ + static void + copy_dest_printtup(TupleTableSlot *slot, DestReceiver *self) + { + TupleDesc typeinfo = slot->tts_tupleDescriptor; + DR_copy *copyDR = (DR_copy *)self; + CopyState cstate = copyDR->cstate; + int natts = typeinfo->natts; + + if (cstate->attrinfo != typeinfo ||cstate->natts != natts) + prepare_cstate_info(cstate, typeinfo, natts); + + /* Make sure the tuple is fully deconstructed */ + slot_getallattrs(slot); + + if (slot->tts_tuple) + { + /* Executor returns standard tuple */ + heap_deform_tuple(slot->tts_tuple, typeinfo, cstate->values, cstate->isnull); + CopyValuesTo(cstate, InvalidOid, cstate->values, cstate->isnull); + } + else + /* Executor returns "virtual" tuple */ + CopyValuesTo(cstate, InvalidOid, slot->tts_values, slot->tts_isnull); + + } + + static void + copy_startup(DestReceiver *self, int operation, TupleDesc typeinfo) + { + /* no-op */ + } + + static void + copy_shutdown(DestReceiver *self) + { + /* no-op */ + } + + static void + copy_destroy(DestReceiver *self) + { + /* no-op */ + } + + DestReceiver * + CreateCopyDestReceiver(void) + { + DR_copy *self = (DR_copy *) palloc(sizeof(DR_copy)); + + self->pub.receiveSlot = copy_dest_printtup; + self->pub.rStartup = copy_startup; + self->pub.rShutdown = copy_shutdown; + self->pub.rDestroy = copy_destroy; + self->pub.mydest = DestCopyDR; + return (DestReceiver *)self; + } Index: src/backend/parser/gram.y =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/parser/gram.y,v retrieving revision 2.558 diff -c -p -r2.558 gram.y *** src/backend/parser/gram.y 25 Aug 2006 04:06:51 -0000 2.558 --- src/backend/parser/gram.y 27 Aug 2006 05:33:07 -0000 *************** ClosePortalStmt: *** 1614,1624 **** /***************************************************************************** * * QUERY : ! * COPY ['(' columnList ')'] FROM/TO [WITH options] * * BINARY, OIDS, and DELIMITERS kept in old locations * for backward compatibility. 2002-06-18 * *****************************************************************************/ CopyStmt: COPY opt_binary qualified_name opt_column_list opt_oids --- 1614,1627 ---- /***************************************************************************** * * QUERY : ! * COPY ['(' columnList ')'] FROM/TO [WITH options] * * BINARY, OIDS, and DELIMITERS kept in old locations * for backward compatibility. 2002-06-18 * + * COPY ( SELECT ... ) TO [WITH options] + * This form doesn't have the backwards-compatible locations for options. + * *****************************************************************************/ CopyStmt: COPY opt_binary qualified_name opt_column_list opt_oids *************** CopyStmt: COPY opt_binary qualified_name *** 1626,1631 **** --- 1629,1635 ---- { CopyStmt *n = makeNode(CopyStmt); n->relation = $3; + n->selectstmt = NULL; n->attlist = $4; n->is_from = $6; n->filename = $7; *************** CopyStmt: COPY opt_binary qualified_name *** 1642,1647 **** --- 1646,1664 ---- n->options = list_concat(n->options, $10); $$ = (Node *)n; } + | COPY select_with_parens TO copy_file_name opt_with + copy_opt_list + { + CopyStmt *n = makeNode(CopyStmt); + n->relation = NULL; + n->selectstmt = (SelectStmt *) $2; + n->attlist = NIL; + n->is_from = false; + n->filename = $4; + + n->options = $6; + $$ = (Node *)n; + } ; copy_from: *************** copy_from: *** 1652,1658 **** /* * copy_file_name NULL indicates stdio is used. Whether stdin or stdout is * used depends on the direction. (It really doesn't make sense to copy from ! * stdout. We silently correct the "typo". - AY 9/94 */ copy_file_name: Sconst { $$ = $1; } --- 1669,1675 ---- /* * copy_file_name NULL indicates stdio is used. Whether stdin or stdout is * used depends on the direction. (It really doesn't make sense to copy from ! * stdout. We silently correct the "typo".) - AY 9/94 */ copy_file_name: Sconst { $$ = $1; } Index: src/backend/tcop/dest.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/tcop/dest.c,v retrieving revision 1.69 diff -c -p -r1.69 dest.c *** src/backend/tcop/dest.c 12 Aug 2006 02:52:05 -0000 1.69 --- src/backend/tcop/dest.c 27 Aug 2006 04:53:19 -0000 *************** *** 30,35 **** --- 30,36 ---- #include "access/printtup.h" #include "access/xact.h" + #include "commands/copy.h" #include "executor/executor.h" #include "executor/tstoreReceiver.h" #include "libpq/libpq.h" *************** CreateDestReceiver(CommandDest dest, Por *** 117,122 **** --- 118,126 ---- case DestSPI: return &spi_printtupDR; + case DestCopyDR: + return CreateCopyDestReceiver(); + case DestTuplestore: if (portal == NULL) elog(ERROR, "no portal specified for DestTuplestore receiver"); *************** EndCommand(const char *commandTag, Comma *** 153,158 **** --- 157,163 ---- case DestSPI: case DestTuplestore: case DestIntoRel: + case DestCopyDR: break; } } *************** NullCommand(CommandDest dest) *** 192,197 **** --- 197,203 ---- case DestSPI: case DestTuplestore: case DestIntoRel: + case DestCopyDR: break; } } *************** ReadyForQuery(CommandDest dest) *** 233,238 **** --- 239,245 ---- case DestSPI: case DestTuplestore: case DestIntoRel: + case DestCopyDR: break; } } Index: src/bin/psql/copy.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/bin/psql/copy.c,v retrieving revision 1.66 diff -c -p -r1.66 copy.c *** src/bin/psql/copy.c 14 Jun 2006 16:49:02 -0000 1.66 --- src/bin/psql/copy.c 27 Aug 2006 04:53:19 -0000 *************** *** 36,42 **** * -- parses \copy command line * * The documented preferred syntax is: ! * \copy tablename [(columnlist)] from|to filename * [ with ] [ binary ] [ oids ] [ delimiter [as] char ] [ null [as] string ] * * The pre-7.3 syntax was: --- 36,42 ---- * -- parses \copy command line * * The documented preferred syntax is: ! * \copy { tablename | viewname | ( select stmt ) } [(columnlist)] from|to filename * [ with ] [ binary ] [ oids ] [ delimiter [as] char ] [ null [as] string ] * * The pre-7.3 syntax was: *************** parse_slash_copy(const char *args) *** 142,147 **** --- 142,168 ---- result->table = pg_strdup(token); + /* Handle COPY (SELECT) case */ + if (token[0] == '(') + { + char *selectstmt; + int brackets = 1; + + while (brackets > 0) + { + token = strtokx(NULL, whitespace, ".,()", "\"'", + 0, false, false, pset.encoding); + if (!token) + goto error; + if (token[0] == '(') + brackets++; + else if (token[0] == ')') + brackets--; + xstrcat(&result->table, " "); + xstrcat(&result->table, token); + } + } + token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding); if (!token) Index: src/include/commands/copy.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/commands/copy.h,v retrieving revision 1.27 diff -c -p -r1.27 copy.h *** src/include/commands/copy.h 5 Mar 2006 15:58:55 -0000 1.27 --- src/include/commands/copy.h 27 Aug 2006 04:53:19 -0000 *************** *** 7,13 **** * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * ! * $PostgreSQL: pgsql/src/include/commands/copy.h,v 1.26 2006/03/03 19:54:10 tgl Exp $ * *------------------------------------------------------------------------- */ --- 7,13 ---- * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * ! * $PostgreSQL: pgsql/src/include/commands/copy.h,v 1.27 2006-03-05 15:58:55 momjian Exp $ * *------------------------------------------------------------------------- */ *************** *** 15,22 **** --- 15,24 ---- #define COPY_H #include "nodes/parsenodes.h" + #include "tcop/dest.h" + extern DestReceiver *CreateCopyDestReceiver(void); extern uint64 DoCopy(const CopyStmt *stmt); #endif /* COPY_H */ Index: src/include/nodes/parsenodes.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/nodes/parsenodes.h,v retrieving revision 1.325 diff -c -p -r1.325 parsenodes.h *** src/include/nodes/parsenodes.h 25 Aug 2006 04:06:56 -0000 1.325 --- src/include/nodes/parsenodes.h 27 Aug 2006 05:26:28 -0000 *************** typedef struct GrantRoleStmt *** 1017,1023 **** --- 1017,1025 ---- typedef struct CopyStmt { NodeTag type; + /* one of the two following must be NULL and the other must not be: */ RangeVar *relation; /* the relation to copy */ + SelectStmt *selectstmt; /* the SELECT to copy */ List *attlist; /* List of column names (as Strings), or NIL * for all columns */ bool is_from; /* TO or FROM */ Index: src/include/tcop/dest.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/tcop/dest.h,v retrieving revision 1.51 diff -c -p -r1.51 dest.h *** src/include/tcop/dest.h 12 Aug 2006 02:52:06 -0000 1.51 --- src/include/tcop/dest.h 27 Aug 2006 04:53:19 -0000 *************** typedef enum *** 85,91 **** DestRemoteExecute, /* sent to frontend, in Execute command */ DestSPI, /* results sent to SPI manager */ DestTuplestore, /* results sent to Tuplestore */ ! DestIntoRel /* results sent to relation (SELECT INTO) */ } CommandDest; /* ---------------- --- 85,92 ---- DestRemoteExecute, /* sent to frontend, in Execute command */ DestSPI, /* results sent to SPI manager */ DestTuplestore, /* results sent to Tuplestore */ ! DestIntoRel, /* results sent to relation (SELECT INTO) */ ! DestCopyDR /* results sent to file */ } CommandDest; /* ---------------- Index: src/interfaces/ecpg/test/Makefile.regress =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/interfaces/ecpg/test/Makefile.regress,v retrieving revision 1.3 diff -c -p -r1.3 Makefile.regress *** src/interfaces/ecpg/test/Makefile.regress 9 Aug 2006 22:48:17 -0000 1.3 --- src/interfaces/ecpg/test/Makefile.regress 28 Aug 2006 02:50:19 -0000 *************** *** 1,4 **** ! override CPPFLAGS := -I$(srcdir)/../../include -I$(libpq_srcdir) $(CPPFLAGS) override CFLAGS += $(PTHREAD_CFLAGS) override LDFLAGS := -L../../ecpglib -L../../pgtypeslib -L../../../libpq $(LDFLAGS) --- 1,4 ---- ! override CPPFLAGS := -I$(srcdir)/../../include -I$(top_builddir)/$(subdir)/../../include -I$(libpq_srcdir) $(CPPFLAGS) override CFLAGS += $(PTHREAD_CFLAGS) override LDFLAGS := -L../../ecpglib -L../../pgtypeslib -L../../../libpq $(LDFLAGS) Index: src/test/regress/parallel_schedule =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/test/regress/parallel_schedule,v retrieving revision 1.34 diff -c -p -r1.34 parallel_schedule *** src/test/regress/parallel_schedule 12 Aug 2006 02:52:06 -0000 1.34 --- src/test/regress/parallel_schedule 27 Aug 2006 04:53:19 -0000 *************** test: create_function_2 *** 34,40 **** # execute two copy tests parallel, to check that copy itself # is concurrent safe. # ---------- ! test: copy # ---------- # The third group of parallel test --- 34,40 ---- # execute two copy tests parallel, to check that copy itself # is concurrent safe. # ---------- ! test: copy copyselect # ---------- # The third group of parallel test Index: src/test/regress/expected/alter_table.out =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/test/regress/expected/alter_table.out,v retrieving revision 1.97 diff -c -p -r1.97 alter_table.out *** src/test/regress/expected/alter_table.out 21 Aug 2006 00:57:26 -0000 1.97 --- src/test/regress/expected/alter_table.out 27 Aug 2006 04:53:19 -0000 *************** alter table test drop a; *** 947,955 **** copy test to stdout; 2 3 copy test(a) to stdout; ! ERROR: column "a" of relation "test" does not exist copy test("........pg.dropped.1........") to stdout; ! ERROR: column "........pg.dropped.1........" of relation "test" does not exist copy test from stdin; ERROR: extra data after last expected column CONTEXT: COPY test, line 1: "10 11 12" --- 947,955 ---- copy test to stdout; 2 3 copy test(a) to stdout; ! ERROR: column "a" does not exist copy test("........pg.dropped.1........") to stdout; ! ERROR: column "........pg.dropped.1........" does not exist copy test from stdin; ERROR: extra data after last expected column CONTEXT: COPY test, line 1: "10 11 12" *************** select * from test; *** 968,976 **** (2 rows) copy test(a) from stdin; ! ERROR: column "a" of relation "test" does not exist copy test("........pg.dropped.1........") from stdin; ! ERROR: column "........pg.dropped.1........" of relation "test" does not exist copy test(b,c) from stdin; select * from test; b | c --- 968,976 ---- (2 rows) copy test(a) from stdin; ! ERROR: column "a" does not exist copy test("........pg.dropped.1........") from stdin; ! ERROR: column "........pg.dropped.1........" does not exist copy test(b,c) from stdin; select * from test; b | c Index: src/test/regress/expected/copy2.out =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/test/regress/expected/copy2.out,v retrieving revision 1.25 diff -c -p -r1.25 copy2.out *** src/test/regress/expected/copy2.out 27 Feb 2006 16:09:50 -0000 1.25 --- src/test/regress/expected/copy2.out 27 Aug 2006 04:53:19 -0000 *************** COPY x (b, d) from stdin; *** 28,34 **** COPY x (a, b, c, d, e) from stdin; -- non-existent column in column list: should fail COPY x (xyz) from stdin; ! ERROR: column "xyz" of relation "x" does not exist -- too many columns in column list: should fail COPY x (a, b, c, d, e, d, c) from stdin; ERROR: column "d" specified more than once --- 28,34 ---- COPY x (a, b, c, d, e) from stdin; -- non-existent column in column list: should fail COPY x (xyz) from stdin; ! ERROR: 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: column "d" specified more than once