Removed Cc: to pgsql-hackers. Zoltán,
Zoltan Boszormenyi wrote: > >Your patch has been added to the PostgreSQL unapplied patches list at: > > > > http://momjian.postgresql.org/cgi-bin/pgpatches > > > >It will be applied as soon as one of the PostgreSQL committers reviews > >and approves it. > > Thanks. Would you please add this instead? > psql built-in \copy (select ...) now also work. Please check this one out. I took the version you posted here and changed the stuff in the parser that I didn't like, and removed the ugly "SELECT * FROM" stuff that was bothering me. I also removed the transformCopyStmt stuff as it seems unnecessary to me. I did all that stuff in a cleaner way (IMO). I also cleaned up the grammar -- basically added a separate case from the regular COPY. I took the opportunity to remove the backwards-compatible options from there. I didn't check that stuff very much but it should continue to work ... I noticed that this works: alvherre=# copy (values (1, 'uno'), (2, 'dos'), (3, 'tr;es'), (4, NULL)) to stdout with delimiter ';' null 'NUL' csv quote as '"'; 1;uno 2;dos 3;"tr;es" 4;NUL which is nice. With this patch, the COPY view FROM stdout path now throws an error -- in your version it worked (because of that "COPY * FROM" stuff), and from previous discussion it seems reasonable to behave differently for views than for plain tables (i.e. it's reasonable that we fail for views). I also broke the check for a FOR UPDATE clause. Not sure where but it must be easy to fix :-) I'd do it myself but I'm heading to bed right now. I also wanted to check these hunks in your patch, which I didn't like very much: -ERROR: column "a" of relation "test" does not exist +ERROR: column "a" does not exist but didn't got around to it. I also noticed that the new copyselect regression test is not added to the serial schedule. I'll repost a reworked version at some point, if no one beats me to it. -- Alvaro Herrera http://www.CommandPrompt.com/ The PostgreSQL Company - Command Prompt, Inc.
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 **** <!-- ! $PostgreSQL: pgsql/doc/src/sgml/ref/copy.sgml,v 1.73 2006/03/03 19:54:10 tgl Exp $ PostgreSQL documentation --> --- 1,5 ---- <!-- ! $PostgreSQL: pgsql/doc/src/sgml/ref/copy.sgml,v 1.74 2006-04-22 03:03:11 momjian Exp $ PostgreSQL documentation --> *************** COPY <replaceable class="parameter">tabl *** 33,39 **** [ ESCAPE [ AS ] '<replaceable class="parameter">escape</replaceable>' ] [ FORCE NOT NULL <replaceable class="parameter">column</replaceable> [, ...] ] ! COPY <replaceable class="parameter">tablename</replaceable> [ ( <replaceable class="parameter">column</replaceable> [, ...] ) ] TO { '<replaceable class="parameter">filename</replaceable>' | STDOUT } [ [ WITH ] [ BINARY ] --- 33,39 ---- [ ESCAPE [ AS ] '<replaceable class="parameter">escape</replaceable>' ] [ FORCE NOT NULL <replaceable class="parameter">column</replaceable> [, ...] ] ! COPY { <replaceable class="parameter">tablename</replaceable> | <replaceable class="parameter">viewname</replaceable> | ( select_statement ) } [ ( <replaceable class="parameter">column</replaceable> [, ...] ) ] TO { '<replaceable class="parameter">filename</replaceable>' | STDOUT } [ [ WITH ] [ BINARY ] *************** COPY <replaceable class="parameter">tabl *** 55,61 **** <command>COPY</command> moves data between <productname>PostgreSQL</productname> tables and standard file-system files. <command>COPY TO</command> copies the contents of a table ! <emphasis>to</> a file, while <command>COPY FROM</command> copies data <emphasis>from</> a file to a table (appending the data to whatever is in the table already). </para> --- 55,63 ---- <command>COPY</command> moves data between <productname>PostgreSQL</productname> tables and standard file-system files. <command>COPY TO</command> copies the contents of a table ! <emphasis>to</> a file, which also work on views and arbitrary ! SELECT statements. (Internally, the view case is rewitten as ! <command>SELECT * FROM viewname</command>.) <command>COPY FROM</command> copies data <emphasis>from</> a file to a table (appending the data to whatever is in the table already). </para> *************** COPY <replaceable class="parameter">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, <command>COPY FROM</command> will insert the default values for ! those columns. </para> <para> --- 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, <command>COPY FROM</command> will insert the default values for ! those columns. <command>COPY TO</command> 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. </para> <para> *************** COPY <replaceable class="parameter">tabl *** 148,154 **** <para> Specifies copying the OID for each row. (An error is raised if <literal>OIDS</literal> is specified for a table that does not ! have OIDs.) </para> </listitem> </varlistentry> --- 153,159 ---- <para> Specifies copying the OID for each row. (An error is raised if <literal>OIDS</literal> is specified for a table that does not ! have OIDs, or in the case of COPY (SELECT) TO.) </para> </listitem> </varlistentry> 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 <relname> ['(' 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 <relname> ['(' columnList ')'] FROM/TO <file> [WITH options] * * BINARY, OIDS, and DELIMITERS kept in old locations * for backward compatibility. 2002-06-18 * + * COPY ( SELECT ... ) TO <file> [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
---------------------------(end of broadcast)--------------------------- TIP 1: if posting/reading through Usenet, please send an appropriate subscribe-nomail command to [EMAIL PROTECTED] so that your message can get through to the mailing list cleanly