1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-02 09:02:37 +03:00

Add CSV table output mode in psql.

"\pset format csv", or --csv, selects comma-separated values table format.
This is compliant with RFC 4180, except that we aren't too picky about
whether the record separator is LF or CRLF; also, the user may choose a
field separator other than comma.

This output format is directly compatible with the server's COPY CSV
format, and will also be useful as input to other programs.  It's
considerably safer for that purpose than the old recommendation to
use "unaligned" format, since the latter couldn't handle data
containing the field separator character.

Daniel Vérité, reviewed by Fabien Coelho and David Fetter, some
tweaking by me

Discussion: https://postgr.es/m/a8de371e-006f-4f92-ab72-2bbe3ee78f03@manitou-mail.org
This commit is contained in:
Tom Lane
2018-11-26 15:18:55 -05:00
parent 9a98984f49
commit aa2ba50c2c
10 changed files with 402 additions and 34 deletions

View File

@ -1957,8 +1957,8 @@ exec_command_pset(PsqlScanState scan_state, bool active_branch)
int i;
static const char *const my_list[] = {
"border", "columns", "expanded", "fieldsep", "fieldsep_zero",
"footer", "format", "linestyle", "null",
"border", "columns", "csv_fieldsep", "expanded", "fieldsep",
"fieldsep_zero", "footer", "format", "linestyle", "null",
"numericlocale", "pager", "pager_min_lines",
"recordsep", "recordsep_zero",
"tableattr", "title", "tuples_only",
@ -3616,6 +3616,9 @@ _align2string(enum printFormat in)
case PRINT_ASCIIDOC:
return "asciidoc";
break;
case PRINT_CSV:
return "csv";
break;
case PRINT_HTML:
return "html";
break;
@ -3696,6 +3699,7 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
/* remember to update error message below when adding more */
{"aligned", PRINT_ALIGNED},
{"asciidoc", PRINT_ASCIIDOC},
{"csv", PRINT_CSV},
{"html", PRINT_HTML},
{"latex", PRINT_LATEX},
{"troff-ms", PRINT_TROFF_MS},
@ -3737,7 +3741,7 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
}
else
{
psql_error("\\pset: allowed formats are aligned, asciidoc, html, latex, latex-longtable, troff-ms, unaligned, wrapped\n");
psql_error("\\pset: allowed formats are aligned, asciidoc, csv, html, latex, latex-longtable, troff-ms, unaligned, wrapped\n");
return false;
}
}
@ -3836,6 +3840,26 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
popt->topt.expanded = !popt->topt.expanded;
}
/* field separator for CSV format */
else if (strcmp(param, "csv_fieldsep") == 0)
{
if (value)
{
/* CSV separator has to be a one-byte character */
if (strlen(value) != 1)
{
psql_error("\\pset: csv_fieldsep must be a single one-byte character\n");
return false;
}
if (value[0] == '"' || value[0] == '\n' || value[0] == '\r')
{
psql_error("\\pset: csv_fieldsep cannot be a double quote, a newline, or a carriage return\n");
return false;
}
popt->topt.csvFieldSep[0] = value[0];
}
}
/* locale-aware numeric output */
else if (strcmp(param, "numericlocale") == 0)
{
@ -4006,6 +4030,13 @@ printPsetInfo(const char *param, struct printQueryOpt *popt)
printf(_("Expanded display is off.\n"));
}
/* show field separator for CSV format */
else if (strcmp(param, "csv_fieldsep") == 0)
{
printf(_("Field separator for CSV is \"%s\".\n"),
popt->topt.csvFieldSep);
}
/* show field separator for unaligned text */
else if (strcmp(param, "fieldsep") == 0)
{
@ -4207,6 +4238,8 @@ pset_value_string(const char *param, struct printQueryOpt *popt)
return psprintf("%d", popt->topt.border);
else if (strcmp(param, "columns") == 0)
return psprintf("%d", popt->topt.columns);
else if (strcmp(param, "csv_fieldsep") == 0)
return pset_quoted_string(popt->topt.csvFieldSep);
else if (strcmp(param, "expanded") == 0)
return pstrdup(popt->topt.expanded == 2
? "auto"