On Thu, Jan 26, 2006 at 10:17:05PM -0500, Tom Lane wrote: > David Fetter <[EMAIL PROTECTED]> writes: > > I have seed database scripts quasi-generated from pg_dump which > > include COPY statements, but the data is hard to edit (especially > > cut & paste operations) when the COPY delimiter is some > > non-visible character like \t. > > This seems like an awfully weak use-case for adding to pg_dump's > already overly complicated feature set.
Those who don't use it will never see it. > The difficulty of parsing COPY output is not simplified by making > the delimiter variable --- more likely the reverse. It's fairly straight-forward. > Furthermore, it's quite unclear why you'd use pg_dump at all to > generate a data file that you intend to feed to some other program. In my case, it's about being copy/paste friendly. > Seems to me that "psql -c 'COPY ...'" is a more likely front-end for > such a process. Actually, it's not. I'm attaching my preliminary patch, as I see I haven't explained it well enough. Cheers, D -- David Fetter [EMAIL PROTECTED] http://fetter.org/ phone: +1 415 235 3778 Remember to vote!
? pg_dump_copy.diff Index: doc/src/sgml/ref/pg_dump.sgml =================================================================== RCS file: /projects/cvsroot/pgsql/doc/src/sgml/ref/pg_dump.sgml,v retrieving revision 1.81 diff -c -r1.81 pg_dump.sgml *** doc/src/sgml/ref/pg_dump.sgml 1 Nov 2005 21:09:50 -0000 1.81 --- doc/src/sgml/ref/pg_dump.sgml 27 Jan 2006 02:22:41 -0000 *************** *** 163,168 **** --- 163,188 ---- </varlistentry> <varlistentry> + <term><option>--copy-delimiter=<replaceable class="parameter">delimiter</replaceable></option></term> + <listitem> + <para> + Use <replaceable class="parameter">delimiter</replaceable> + instead of the default tab character in <command>COPY</command> statements. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--copy-null=<replaceable class="parameter">string_for_nulls</replaceable></option></term> + <listitem> + <para> + Use <replaceable class="parameter">string_for_nulls</replaceable> instead of the + default \N in <command>COPY</command> statements. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><option>-d</option></term> <term><option>--inserts</option></term> <listitem> Index: src/bin/pg_dump/pg_dump.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v retrieving revision 1.427 diff -c -r1.427 pg_dump.c *** src/bin/pg_dump/pg_dump.c 21 Jan 2006 02:16:20 -0000 1.427 --- src/bin/pg_dump/pg_dump.c 27 Jan 2006 02:22:48 -0000 *************** *** 111,116 **** --- 111,121 ---- /* flag to turn on/off dollar quoting */ static int disable_dollar_quoting = 0; + /* Things used when caller invokes COPY options. */ + const char *copy_delimiter_default = "\t"; + const char *copy_delimiter = "\t"; + const char *copy_null_default = "\\N"; + const char *copy_null = "\\N"; static void help(const char *progname); static NamespaceInfo *findNamespace(Oid nsoid, Oid objoid); *************** *** 246,251 **** --- 251,265 ---- {"disable-triggers", no_argument, &disable_triggers, 1}, {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, + /* + * The following options don't have an equivalent short option + * letter, and are not available as -X long-name. Use the + * long form instead. + */ + + {"copy-delimiter", required_argument, NULL, 2}, + {"copy-null", required_argument, NULL, 3}, + {NULL, 0, NULL, 0} }; int optindex; *************** *** 414,419 **** --- 428,453 ---- break; /* This covers the long options equivalent to -X xxx. */ + case 2: + copy_delimiter = strdup(optarg); + if (strlen(copy_delimiter) != 1) + { + fprintf(stderr, _("In %s, copy-delimiter must be exactly one byte long, not %d\n"), + progname, strlen(copy_delimiter)); + exit(1); + } + if ( (*copy_delimiter == '\r') || (*copy_delimiter == '\n') ) + { + fprintf(stderr, _("In %s, copy-delimiter may not be \\r or \\n.\n"), + progname); + exit(1); + } + break; + + case 3: + copy_null = strdup(optarg); + break; + case 0: break; *************** *** 816,836 **** column_list = fmtCopyColumnList(tbinfo); else column_list = ""; /* can't select columns in COPY */ - if (oids && hasoids) - { - appendPQExpBuffer(q, "COPY %s %s WITH OIDS TO stdout;", - fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, - classname), - column_list); - } - else - { - appendPQExpBuffer(q, "COPY %s %s TO stdout;", - fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, - classname), - column_list); - } res = PQexec(g_conn, q->data); check_sql_result(res, g_conn, q->data, PGRES_COPY_OUT); --- 850,869 ---- column_list = fmtCopyColumnList(tbinfo); else column_list = ""; /* can't select columns in COPY */ + appendPQExpBuffer(q, "COPY %s %s %sTO stdout", + fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, classname), + column_list, + /* dump OIDs if requested */ + (oids && hasoids) ? "WITH OIDS " : "" + ); + /* Add a DELIMITER if copy_delimiter is not the default */ + if (strcmp(copy_delimiter, copy_delimiter_default) != 0) + appendPQExpBuffer(q, " DELIMITER AS '%s'", copy_delimiter); + /* Add a NULL AS stanza if copy_null is not the default */ + if (strcmp(copy_null,copy_null_default) != 0) + appendPQExpBuffer(q, " NULL AS '%s'", copy_null); + appendPQExpBuffer(q, ";"); res = PQexec(g_conn, q->data); check_sql_result(res, g_conn, q->data, PGRES_COPY_OUT); *************** *** 1085,1093 **** /* must use 2 steps here 'cause fmtId is nonreentrant */ appendPQExpBuffer(copyBuf, "COPY %s ", fmtId(tbinfo->dobj.name)); ! appendPQExpBuffer(copyBuf, "%s %sFROM stdin;\n", ! fmtCopyColumnList(tbinfo), ! (tdinfo->oids && tbinfo->hasoids) ? "WITH OIDS " : ""); copyStmt = copyBuf->data; } else --- 1118,1135 ---- /* must use 2 steps here 'cause fmtId is nonreentrant */ appendPQExpBuffer(copyBuf, "COPY %s ", fmtId(tbinfo->dobj.name)); ! appendPQExpBuffer(copyBuf, "%s %sFROM stdin", ! fmtCopyColumnList(tbinfo), ! (tdinfo->oids && tbinfo->hasoids) ? "WITH OIDS " : "" ! ); ! /* Add DELIMITER AS stanza if not the default */ ! if (strcmp(copy_delimiter, copy_delimiter_default) != 0) ! appendPQExpBuffer(copyBuf, " DELIMITER AS '%s'", copy_delimiter); ! /* Add NULL AS stanza if not the default */ ! if (strcmp(copy_null, copy_null_default) != 0) ! appendPQExpBuffer(copyBuf, " NULL AS '%s'", copy_null); ! appendPQExpBuffer(copyBuf, ";\n"); ! copyStmt = copyBuf->data; } else
---------------------------(end of broadcast)--------------------------- TIP 5: don't forget to increase your free space map settings