Skip to content

Commit 169c8a9

Browse files
committed
psql: Support zero byte field and record separators
Add new psql settings and command-line options to support setting the field and record separators for unaligned output to a zero byte, for easier interfacing with other shell tools. reviewed by Abhijit Menon-Sen
1 parent dd7c841 commit 169c8a9

File tree

6 files changed

+154
-46
lines changed

6 files changed

+154
-46
lines changed

doc/src/sgml/ref/psql-ref.sgml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,27 @@ PostgreSQL documentation
482482
</listitem>
483483
</varlistentry>
484484

485+
<varlistentry>
486+
<term><option>-z</option></term>
487+
<term><option>--field-separator-zero</option></term>
488+
<listitem>
489+
<para>
490+
Set the field separator for unaligned output to a zero byte.
491+
</para>
492+
</listitem>
493+
</varlistentry>
494+
495+
<varlistentry>
496+
<term><option>-0</option></term>
497+
<term><option>--record-separator-zero</option></term>
498+
<listitem>
499+
<para>
500+
Set the record separator for unaligned output to a zero byte. This is
501+
useful for interfacing, for example, with <literal>xargs -0</literal>.
502+
</para>
503+
</listitem>
504+
</varlistentry>
505+
485506
<varlistentry>
486507
<term><option>-1</option></term>
487508
<term><option>--single-transaction</option></term>
@@ -1908,6 +1929,16 @@ lo_import 152801
19081929
</listitem>
19091930
</varlistentry>
19101931

1932+
<varlistentry>
1933+
<term><literal>fieldsep_zero</literal></term>
1934+
<listitem>
1935+
<para>
1936+
Sets the field separator to use in unaligned output format to a zero
1937+
byte.
1938+
</para>
1939+
</listitem>
1940+
</varlistentry>
1941+
19111942
<varlistentry>
19121943
<term><literal>footer</literal></term>
19131944
<listitem>
@@ -2077,6 +2108,16 @@ lo_import 152801
20772108
</listitem>
20782109
</varlistentry>
20792110

2111+
<varlistentry>
2112+
<term><literal>recordsep_zero</literal></term>
2113+
<listitem>
2114+
<para>
2115+
Sets the record separator to use in unaligned output format to a zero
2116+
byte.
2117+
</para>
2118+
</listitem>
2119+
</varlistentry>
2120+
20802121
<varlistentry>
20812122
<term><literal>tableattr</literal> (or <literal>T</literal>)</term>
20822123
<listitem>

src/bin/psql/command.c

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2272,30 +2272,57 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
22722272
{
22732273
if (value)
22742274
{
2275-
free(popt->topt.fieldSep);
2276-
popt->topt.fieldSep = pg_strdup(value);
2275+
free(popt->topt.fieldSep.separator);
2276+
popt->topt.fieldSep.separator = pg_strdup(value);
2277+
popt->topt.fieldSep.separator_zero = false;
22772278
}
22782279
if (!quiet)
2279-
printf(_("Field separator is \"%s\".\n"), popt->topt.fieldSep);
2280+
{
2281+
if (popt->topt.fieldSep.separator_zero)
2282+
printf(_("Field separator is zero byte.\n"));
2283+
else
2284+
printf(_("Field separator is \"%s\".\n"), popt->topt.fieldSep.separator);
2285+
}
2286+
}
2287+
2288+
else if (strcmp(param, "fieldsep_zero") == 0)
2289+
{
2290+
free(popt->topt.fieldSep.separator);
2291+
popt->topt.fieldSep.separator = NULL;
2292+
popt->topt.fieldSep.separator_zero = true;
2293+
if (!quiet)
2294+
printf(_("Field separator is zero byte.\n"));
22802295
}
22812296

22822297
/* record separator for unaligned text */
22832298
else if (strcmp(param, "recordsep") == 0)
22842299
{
22852300
if (value)
22862301
{
2287-
free(popt->topt.recordSep);
2288-
popt->topt.recordSep = pg_strdup(value);
2302+
free(popt->topt.recordSep.separator);
2303+
popt->topt.recordSep.separator = pg_strdup(value);
2304+
popt->topt.recordSep.separator_zero = false;
22892305
}
22902306
if (!quiet)
22912307
{
2292-
if (strcmp(popt->topt.recordSep, "\n") == 0)
2308+
if (popt->topt.recordSep.separator_zero)
2309+
printf(_("Record separator is zero byte.\n"));
2310+
else if (strcmp(popt->topt.recordSep.separator, "\n") == 0)
22932311
printf(_("Record separator is <newline>."));
22942312
else
2295-
printf(_("Record separator is \"%s\".\n"), popt->topt.recordSep);
2313+
printf(_("Record separator is \"%s\".\n"), popt->topt.recordSep.separator);
22962314
}
22972315
}
22982316

2317+
else if (strcmp(param, "recordsep_zero") == 0)
2318+
{
2319+
free(popt->topt.recordSep.separator);
2320+
popt->topt.recordSep.separator = NULL;
2321+
popt->topt.recordSep.separator_zero = true;
2322+
if (!quiet)
2323+
printf(_("Record separator is zero byte.\n"));
2324+
}
2325+
22992326
/* toggle between full and tuples-only format */
23002327
else if (strcmp(param, "t") == 0 || strcmp(param, "tuples_only") == 0)
23012328
{

src/bin/psql/help.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ usage(void)
123123
printf(_(" -t, --tuples-only print rows only\n"));
124124
printf(_(" -T, --table-attr=TEXT set HTML table tag attributes (e.g., width, border)\n"));
125125
printf(_(" -x, --expanded turn on expanded table output\n"));
126+
printf(_(" -z, --field-separator-zero\n"
127+
" set field separator to zero byte\n"));
128+
printf(_(" -0, --record-separator-zero\n"
129+
" set record separator to zero byte\n"));
126130

127131
printf(_("\nConnection options:\n"));
128132
/* Display default host */
@@ -237,8 +241,8 @@ slashUsage(unsigned short int pager)
237241
fprintf(output, _(" \\H toggle HTML output mode (currently %s)\n"),
238242
ON(pset.popt.topt.format == PRINT_HTML));
239243
fprintf(output, _(" \\pset NAME [VALUE] set table output option\n"
240-
" (NAME := {format|border|expanded|fieldsep|footer|null|\n"
241-
" numericlocale|recordsep|tuples_only|title|tableattr|pager})\n"));
244+
" (NAME := {format|border|expanded|fieldsep|fieldsep_zero|footer|null|\n"
245+
" numericlocale|recordsep|recordsep_zero|tuples_only|title|tableattr|pager})\n"));
242246
fprintf(output, _(" \\t [on|off] show only rows (currently %s)\n"),
243247
ON(pset.popt.topt.tuples_only));
244248
fprintf(output, _(" \\T [STRING] set HTML <table> tag attributes, or unset if none\n"));

src/bin/psql/print.c

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,16 @@ fputnbytes(FILE *f, const char *str, size_t n)
268268
}
269269

270270

271+
static void
272+
print_separator(struct separator sep, FILE *fout)
273+
{
274+
if (sep.separator_zero)
275+
fputc('\000', fout);
276+
else if (sep.separator)
277+
fputs(sep.separator, fout);
278+
}
279+
280+
271281
/*************************/
272282
/* Unaligned text */
273283
/*************************/
@@ -276,8 +286,6 @@ fputnbytes(FILE *f, const char *str, size_t n)
276286
static void
277287
print_unaligned_text(const printTableContent *cont, FILE *fout)
278288
{
279-
const char *opt_fieldsep = cont->opt->fieldSep;
280-
const char *opt_recordsep = cont->opt->recordSep;
281289
bool opt_tuples_only = cont->opt->tuples_only;
282290
unsigned int i;
283291
const char *const * ptr;
@@ -286,24 +294,22 @@ print_unaligned_text(const printTableContent *cont, FILE *fout)
286294
if (cancel_pressed)
287295
return;
288296

289-
if (!opt_fieldsep)
290-
opt_fieldsep = "";
291-
if (!opt_recordsep)
292-
opt_recordsep = "";
293-
294297
if (cont->opt->start_table)
295298
{
296299
/* print title */
297300
if (!opt_tuples_only && cont->title)
298-
fprintf(fout, "%s%s", cont->title, opt_recordsep);
301+
{
302+
fputs(cont->title, fout);
303+
print_separator(cont->opt->recordSep, fout);
304+
}
299305

300306
/* print headers */
301307
if (!opt_tuples_only)
302308
{
303309
for (ptr = cont->headers; *ptr; ptr++)
304310
{
305311
if (ptr != cont->headers)
306-
fputs(opt_fieldsep, fout);
312+
print_separator(cont->opt->fieldSep, fout);
307313
fputs(*ptr, fout);
308314
}
309315
need_recordsep = true;
@@ -318,15 +324,15 @@ print_unaligned_text(const printTableContent *cont, FILE *fout)
318324
{
319325
if (need_recordsep)
320326
{
321-
fputs(opt_recordsep, fout);
327+
print_separator(cont->opt->recordSep, fout);
322328
need_recordsep = false;
323329
if (cancel_pressed)
324330
break;
325331
}
326332
fputs(*ptr, fout);
327333

328334
if ((i + 1) % cont->ncolumns)
329-
fputs(opt_fieldsep, fout);
335+
print_separator(cont->opt->fieldSep, fout);
330336
else
331337
need_recordsep = true;
332338
}
@@ -342,25 +348,32 @@ print_unaligned_text(const printTableContent *cont, FILE *fout)
342348
{
343349
if (need_recordsep)
344350
{
345-
fputs(opt_recordsep, fout);
351+
print_separator(cont->opt->recordSep, fout);
346352
need_recordsep = false;
347353
}
348354
fputs(f->data, fout);
349355
need_recordsep = true;
350356
}
351357
}
352-
/* the last record needs to be concluded with a newline */
358+
/*
359+
* The last record is terminated by a newline, independent of the set
360+
* record separator. But when the record separator is a zero byte, we
361+
* use that (compatible with find -print0 and xargs).
362+
*/
353363
if (need_recordsep)
354-
fputc('\n', fout);
364+
{
365+
if (cont->opt->recordSep.separator_zero)
366+
print_separator(cont->opt->recordSep, fout);
367+
else
368+
fputc('\n', fout);
369+
}
355370
}
356371
}
357372

358373

359374
static void
360375
print_unaligned_vertical(const printTableContent *cont, FILE *fout)
361376
{
362-
const char *opt_fieldsep = cont->opt->fieldSep;
363-
const char *opt_recordsep = cont->opt->recordSep;
364377
bool opt_tuples_only = cont->opt->tuples_only;
365378
unsigned int i;
366379
const char *const * ptr;
@@ -369,11 +382,6 @@ print_unaligned_vertical(const printTableContent *cont, FILE *fout)
369382
if (cancel_pressed)
370383
return;
371384

372-
if (!opt_fieldsep)
373-
opt_fieldsep = "";
374-
if (!opt_recordsep)
375-
opt_recordsep = "";
376-
377385
if (cont->opt->start_table)
378386
{
379387
/* print title */
@@ -393,19 +401,19 @@ print_unaligned_vertical(const printTableContent *cont, FILE *fout)
393401
if (need_recordsep)
394402
{
395403
/* record separator is 2 occurrences of recordsep in this mode */
396-
fputs(opt_recordsep, fout);
397-
fputs(opt_recordsep, fout);
404+
print_separator(cont->opt->recordSep, fout);
405+
print_separator(cont->opt->recordSep, fout);
398406
need_recordsep = false;
399407
if (cancel_pressed)
400408
break;
401409
}
402410

403411
fputs(cont->headers[i % cont->ncolumns], fout);
404-
fputs(opt_fieldsep, fout);
412+
print_separator(cont->opt->fieldSep, fout);
405413
fputs(*ptr, fout);
406414

407415
if ((i + 1) % cont->ncolumns)
408-
fputs(opt_recordsep, fout);
416+
print_separator(cont->opt->recordSep, fout);
409417
else
410418
need_recordsep = true;
411419
}
@@ -417,15 +425,19 @@ print_unaligned_vertical(const printTableContent *cont, FILE *fout)
417425
{
418426
printTableFooter *f;
419427

420-
fputs(opt_recordsep, fout);
428+
print_separator(cont->opt->recordSep, fout);
421429
for (f = cont->footers; f; f = f->next)
422430
{
423-
fputs(opt_recordsep, fout);
431+
print_separator(cont->opt->recordSep, fout);
424432
fputs(f->data, fout);
425433
}
426434
}
427435

428-
fputc('\n', fout);
436+
/* see above in print_unaligned_text() */
437+
if (cont->opt->recordSep.separator_zero)
438+
print_separator(cont->opt->recordSep, fout);
439+
else
440+
fputc('\n', fout);
429441
}
430442
}
431443

src/bin/psql/print.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ typedef struct printTextFormat
6767
* marks when border=0? */
6868
} printTextFormat;
6969

70+
struct separator
71+
{
72+
char *separator;
73+
bool separator_zero;
74+
};
75+
7076
typedef struct printTableOpt
7177
{
7278
enum printFormat format; /* see enum above */
@@ -81,8 +87,8 @@ typedef struct printTableOpt
8187
bool stop_table; /* print stop decoration, eg </table> */
8288
unsigned long prior_records; /* start offset for record counters */
8389
const printTextFormat *line_style; /* line style (NULL for default) */
84-
char *fieldSep; /* field separator for unaligned text mode */
85-
char *recordSep; /* record separator for unaligned text mode */
90+
struct separator fieldSep; /* field separator for unaligned text mode */
91+
struct separator recordSep; /* record separator for unaligned text mode */
8692
bool numericLocale; /* locale-aware numeric units separator and
8793
* decimal marker */
8894
char *tableAttr; /* attributes for HTML <table ...> */

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy