psql-nul-sep.patch
text/x-patch
Filename: psql-nul-sep.patch
Type: text/x-patch
Part: 0
Message:
psql NUL record and field separator
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| doc/src/sgml/ref/psql-ref.sgml | 39 | 0 |
| src/bin/psql/command.c | 34 | 7 |
| src/bin/psql/help.c | 6 | 2 |
| src/bin/psql/print.c | 24 | 25 |
| src/bin/psql/print.h | 8 | 2 |
| src/bin/psql/startup.c | 22 | 6 |
diff --git i/doc/src/sgml/ref/psql-ref.sgml w/doc/src/sgml/ref/psql-ref.sgml
index a9b1ed2..752d6de 100644
--- i/doc/src/sgml/ref/psql-ref.sgml
+++ w/doc/src/sgml/ref/psql-ref.sgml
@@ -193,6 +193,15 @@ PostgreSQL documentation
</varlistentry>
<varlistentry>
+ <term><option>--field-separator-zero</option></term>
+ <listitem>
+ <para>
+ Set the field separator for unaligned output to a zero byte.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>-h <replaceable class="parameter">hostname</replaceable></></term>
<term><option>--host=<replaceable class="parameter">hostname</replaceable></></term>
<listitem>
@@ -320,6 +329,16 @@ PostgreSQL documentation
</varlistentry>
<varlistentry>
+ <term><option>--record-separator-zero</option></term>
+ <listitem>
+ <para>
+ Set the record separator for unaligned output to a zero byte. This is
+ useful for interfacing, for example, with <literal>xargs -0</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>-s</></term>
<term><option>--single-step</></term>
<listitem>
@@ -1909,6 +1928,16 @@ lo_import 152801
</varlistentry>
<varlistentry>
+ <term><literal>fieldsep_zero</literal></term>
+ <listitem>
+ <para>
+ Sets the field separator to use in unaligned output format to a zero
+ byte.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>footer</literal></term>
<listitem>
<para>
@@ -2078,6 +2107,16 @@ lo_import 152801
</varlistentry>
<varlistentry>
+ <term><literal>recordsep_zero</literal></term>
+ <listitem>
+ <para>
+ Sets the record separator to use in unaligned output format to a zero
+ byte.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>tableattr</literal> (or <literal>T</literal>)</term>
<listitem>
<para>
diff --git i/src/bin/psql/command.c w/src/bin/psql/command.c
index 69fac83..9421a73 100644
--- i/src/bin/psql/command.c
+++ w/src/bin/psql/command.c
@@ -2284,11 +2284,26 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
{
if (value)
{
- free(popt->topt.fieldSep);
- popt->topt.fieldSep = pg_strdup(value);
+ free(popt->topt.fieldSep.separator);
+ popt->topt.fieldSep.separator = pg_strdup(value);
+ popt->topt.fieldSep.separator_zero = false;
}
if (!quiet)
- printf(_("Field separator is \"%s\".\n"), popt->topt.fieldSep);
+ {
+ if (popt->topt.fieldSep.separator_zero)
+ printf(_("Field separator is zero byte.\n"));
+ else
+ printf(_("Field separator is \"%s\".\n"), popt->topt.fieldSep.separator);
+ }
+ }
+
+ else if (strcmp(param, "fieldsep_zero") == 0)
+ {
+ free(popt->topt.fieldSep.separator);
+ popt->topt.fieldSep.separator = NULL;
+ popt->topt.fieldSep.separator_zero = true;
+ if (!quiet)
+ printf(_("Field separator is zero byte.\n"));
}
/* record separator for unaligned text */
@@ -2296,18 +2311,30 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
{
if (value)
{
- free(popt->topt.recordSep);
- popt->topt.recordSep = pg_strdup(value);
+ free(popt->topt.recordSep.separator);
+ popt->topt.recordSep.separator = pg_strdup(value);
+ popt->topt.recordSep.separator_zero = false;
}
if (!quiet)
{
- if (strcmp(popt->topt.recordSep, "\n") == 0)
+ if (popt->topt.recordSep.separator_zero)
+ printf(_("Record separator is zero byte.\n"));
+ else if (strcmp(popt->topt.recordSep.separator, "\n") == 0)
printf(_("Record separator is <newline>."));
else
- printf(_("Record separator is \"%s\".\n"), popt->topt.recordSep);
+ printf(_("Record separator is \"%s\".\n"), popt->topt.recordSep.separator);
}
}
+ else if (strcmp(param, "recordsep_zero") == 0)
+ {
+ free(popt->topt.recordSep.separator);
+ popt->topt.recordSep.separator = NULL;
+ popt->topt.recordSep.separator_zero = true;
+ if (!quiet)
+ printf(_("Record separator is zero byte.\n"));
+ }
+
/* toggle between full and tuples-only format */
else if (strcmp(param, "t") == 0 || strcmp(param, "tuples_only") == 0)
{
diff --git i/src/bin/psql/help.c w/src/bin/psql/help.c
index 172fd0c..fbf2f76 100644
--- i/src/bin/psql/help.c
+++ w/src/bin/psql/help.c
@@ -116,10 +116,14 @@ usage(void)
printf(_(" -F, --field-separator=STRING\n"
" set field separator (default: \"%s\")\n"),
DEFAULT_FIELD_SEP);
+ printf(_(" --field-separator-zero\n"
+ " set field separator to zero byte\n"));
printf(_(" -H, --html HTML table output mode\n"));
printf(_(" -P, --pset=VAR[=ARG] set printing option VAR to ARG (see \\pset command)\n"));
printf(_(" -R, --record-separator=STRING\n"
" set record separator (default: newline)\n"));
+ printf(_(" --record-separator-zero\n"
+ " set record separator to zero byte\n"));
printf(_(" -t, --tuples-only print rows only\n"));
printf(_(" -T, --table-attr=TEXT set HTML table tag attributes (e.g., width, border)\n"));
printf(_(" -x, --expanded turn on expanded table output\n"));
@@ -237,8 +241,8 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\H toggle HTML output mode (currently %s)\n"),
ON(pset.popt.topt.format == PRINT_HTML));
fprintf(output, _(" \\pset NAME [VALUE] set table output option\n"
- " (NAME := {format|border|expanded|fieldsep|footer|null|\n"
- " numericlocale|recordsep|tuples_only|title|tableattr|pager})\n"));
+ " (NAME := {format|border|expanded|fieldsep|fieldsep_zero|footer|null|\n"
+ " numericlocale|recordsep|recordsep_zero|tuples_only|title|tableattr|pager})\n"));
fprintf(output, _(" \\t [on|off] show only rows (currently %s)\n"),
ON(pset.popt.topt.tuples_only));
fprintf(output, _(" \\T [STRING] set HTML <table> tag attributes, or unset if none\n"));
diff --git i/src/bin/psql/print.c w/src/bin/psql/print.c
index e127edb..43ddab1 100644
--- i/src/bin/psql/print.c
+++ w/src/bin/psql/print.c
@@ -268,6 +268,16 @@ fputnbytes(FILE *f, const char *str, size_t n)
}
+static void
+print_separator(struct separator sep, FILE *fout)
+{
+ if (sep.separator_zero)
+ fputc('\000', fout);
+ else if (sep.separator)
+ fputs(sep.separator, fout);
+}
+
+
/*************************/
/* Unaligned text */
/*************************/
@@ -276,8 +286,6 @@ fputnbytes(FILE *f, const char *str, size_t n)
static void
print_unaligned_text(const printTableContent *cont, FILE *fout)
{
- const char *opt_fieldsep = cont->opt->fieldSep;
- const char *opt_recordsep = cont->opt->recordSep;
bool opt_tuples_only = cont->opt->tuples_only;
unsigned int i;
const char *const * ptr;
@@ -286,16 +294,14 @@ print_unaligned_text(const printTableContent *cont, FILE *fout)
if (cancel_pressed)
return;
- if (!opt_fieldsep)
- opt_fieldsep = "";
- if (!opt_recordsep)
- opt_recordsep = "";
-
if (cont->opt->start_table)
{
/* print title */
if (!opt_tuples_only && cont->title)
- fprintf(fout, "%s%s", cont->title, opt_recordsep);
+ {
+ fputs(cont->title, fout);
+ print_separator(cont->opt->recordSep, fout);
+ }
/* print headers */
if (!opt_tuples_only)
@@ -303,7 +309,7 @@ print_unaligned_text(const printTableContent *cont, FILE *fout)
for (ptr = cont->headers; *ptr; ptr++)
{
if (ptr != cont->headers)
- fputs(opt_fieldsep, fout);
+ print_separator(cont->opt->fieldSep, fout);
fputs(*ptr, fout);
}
need_recordsep = true;
@@ -318,7 +324,7 @@ print_unaligned_text(const printTableContent *cont, FILE *fout)
{
if (need_recordsep)
{
- fputs(opt_recordsep, fout);
+ print_separator(cont->opt->recordSep, fout);
need_recordsep = false;
if (cancel_pressed)
break;
@@ -326,7 +332,7 @@ print_unaligned_text(const printTableContent *cont, FILE *fout)
fputs(*ptr, fout);
if ((i + 1) % cont->ncolumns)
- fputs(opt_fieldsep, fout);
+ print_separator(cont->opt->fieldSep, fout);
else
need_recordsep = true;
}
@@ -342,7 +348,7 @@ print_unaligned_text(const printTableContent *cont, FILE *fout)
{
if (need_recordsep)
{
- fputs(opt_recordsep, fout);
+ print_separator(cont->opt->recordSep, fout);
need_recordsep = false;
}
fputs(f->data, fout);
@@ -359,8 +365,6 @@ print_unaligned_text(const printTableContent *cont, FILE *fout)
static void
print_unaligned_vertical(const printTableContent *cont, FILE *fout)
{
- const char *opt_fieldsep = cont->opt->fieldSep;
- const char *opt_recordsep = cont->opt->recordSep;
bool opt_tuples_only = cont->opt->tuples_only;
unsigned int i;
const char *const * ptr;
@@ -369,11 +373,6 @@ print_unaligned_vertical(const printTableContent *cont, FILE *fout)
if (cancel_pressed)
return;
- if (!opt_fieldsep)
- opt_fieldsep = "";
- if (!opt_recordsep)
- opt_recordsep = "";
-
if (cont->opt->start_table)
{
/* print title */
@@ -393,19 +392,19 @@ print_unaligned_vertical(const printTableContent *cont, FILE *fout)
if (need_recordsep)
{
/* record separator is 2 occurrences of recordsep in this mode */
- fputs(opt_recordsep, fout);
- fputs(opt_recordsep, fout);
+ print_separator(cont->opt->recordSep, fout);
+ print_separator(cont->opt->recordSep, fout);
need_recordsep = false;
if (cancel_pressed)
break;
}
fputs(cont->headers[i % cont->ncolumns], fout);
- fputs(opt_fieldsep, fout);
+ print_separator(cont->opt->fieldSep, fout);
fputs(*ptr, fout);
if ((i + 1) % cont->ncolumns)
- fputs(opt_recordsep, fout);
+ print_separator(cont->opt->recordSep, fout);
else
need_recordsep = true;
}
@@ -417,10 +416,10 @@ print_unaligned_vertical(const printTableContent *cont, FILE *fout)
{
printTableFooter *f;
- fputs(opt_recordsep, fout);
+ print_separator(cont->opt->recordSep, fout);
for (f = cont->footers; f; f = f->next)
{
- fputs(opt_recordsep, fout);
+ print_separator(cont->opt->recordSep, fout);
fputs(f->data, fout);
}
}
diff --git i/src/bin/psql/print.h w/src/bin/psql/print.h
index 86c6e75..931535e 100644
--- i/src/bin/psql/print.h
+++ w/src/bin/psql/print.h
@@ -67,6 +67,12 @@ typedef struct printTextFormat
* marks when border=0? */
} printTextFormat;
+struct separator
+{
+ char *separator;
+ bool separator_zero;
+};
+
typedef struct printTableOpt
{
enum printFormat format; /* see enum above */
@@ -81,8 +87,8 @@ typedef struct printTableOpt
bool stop_table; /* print stop decoration, eg </table> */
unsigned long prior_records; /* start offset for record counters */
const printTextFormat *line_style; /* line style (NULL for default) */
- char *fieldSep; /* field separator for unaligned text mode */
- char *recordSep; /* record separator for unaligned text mode */
+ struct separator fieldSep; /* field separator for unaligned text mode */
+ struct separator recordSep; /* record separator for unaligned text mode */
bool numericLocale; /* locale-aware numeric units separator and
* decimal marker */
char *tableAttr; /* attributes for HTML <table ...> */
diff --git i/src/bin/psql/startup.c w/src/bin/psql/startup.c
index 8b1864c..69207c1 100644
--- i/src/bin/psql/startup.c
+++ w/src/bin/psql/startup.c
@@ -150,10 +150,16 @@ main(int argc, char *argv[])
parse_psql_options(argc, argv, &options);
- if (!pset.popt.topt.fieldSep)
- pset.popt.topt.fieldSep = pg_strdup(DEFAULT_FIELD_SEP);
- if (!pset.popt.topt.recordSep)
- pset.popt.topt.recordSep = pg_strdup(DEFAULT_RECORD_SEP);
+ if (!pset.popt.topt.fieldSep.separator)
+ {
+ pset.popt.topt.fieldSep.separator = pg_strdup(DEFAULT_FIELD_SEP);
+ pset.popt.topt.fieldSep.separator_zero = false;
+ }
+ if (!pset.popt.topt.recordSep.separator)
+ {
+ pset.popt.topt.recordSep.separator = pg_strdup(DEFAULT_RECORD_SEP);
+ pset.popt.topt.recordSep.separator_zero = false;
+ }
if (options.username == NULL)
password_prompt = pg_strdup(_("Password: "));
@@ -338,6 +344,7 @@ parse_psql_options(int argc, char *argv[], struct adhoc_opts * options)
{"echo-hidden", no_argument, NULL, 'E'},
{"file", required_argument, NULL, 'f'},
{"field-separator", required_argument, NULL, 'F'},
+ {"field-separator-zero", no_argument, NULL, 10},
{"host", required_argument, NULL, 'h'},
{"html", no_argument, NULL, 'H'},
{"list", no_argument, NULL, 'l'},
@@ -349,6 +356,7 @@ parse_psql_options(int argc, char *argv[], struct adhoc_opts * options)
{"pset", required_argument, NULL, 'P'},
{"quiet", no_argument, NULL, 'q'},
{"record-separator", required_argument, NULL, 'R'},
+ {"record-separator-zero", no_argument, NULL, 11},
{"single-step", no_argument, NULL, 's'},
{"single-line", no_argument, NULL, 'S'},
{"tuples-only", no_argument, NULL, 't'},
@@ -407,7 +415,11 @@ parse_psql_options(int argc, char *argv[], struct adhoc_opts * options)
options->action_string = optarg;
break;
case 'F':
- pset.popt.topt.fieldSep = pg_strdup(optarg);
+ pset.popt.topt.fieldSep.separator = pg_strdup(optarg);
+ pset.popt.topt.fieldSep.separator_zero = false;
+ break;
+ case 10:
+ pset.popt.topt.fieldSep.separator_zero = true;
break;
case 'h':
options->host = optarg;
@@ -459,7 +471,11 @@ parse_psql_options(int argc, char *argv[], struct adhoc_opts * options)
SetVariableBool(pset.vars, "QUIET");
break;
case 'R':
- pset.popt.topt.recordSep = pg_strdup(optarg);
+ pset.popt.topt.recordSep.separator = pg_strdup(optarg);
+ pset.popt.topt.recordSep.separator_zero = false;
+ break;
+ case 11:
+ pset.popt.topt.recordSep.separator_zero = true;
break;
case 's':
SetVariableBool(pset.vars, "SINGLESTEP");