diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 7ea7edc..ebb3d35 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -246,7 +246,7 @@ EOF
Use separator as the
- field separator for unaligned output. This is equivalent to
+ field separator for unaligned and csv outputs. This is equivalent to
\pset fieldsep or \f.
@@ -376,7 +376,7 @@ EOF
Use separator as the
- record separator for unaligned output. This is equivalent to
+ record separator for unaligned and csv outputs. This is equivalent to
\pset recordsep.
@@ -551,8 +551,8 @@ EOF
- Set the field separator for unaligned output to a zero byte. This is
- equvalent to \pset fieldsep_zero.
+ Set the field separator for unaligned and csv outputs to a zero byte.
+ This is equivalent to \pset fieldsep_zero.
@@ -562,8 +562,9 @@ EOF
- Set the record separator for unaligned output to a zero byte. This is
- useful for interfacing, for example, with xargs -0.
+ Set the record separator for unaligned and csv outputs to a zero byte.
+ This is useful for interfacing, for example, with
+ xargs -0.
This is equivalent to \pset recordsep_zero.
@@ -1918,9 +1919,9 @@ Tue Oct 26 21:40:57 CEST 1999
- Sets the field separator for unaligned query output. The default
- is the vertical bar (|). It is equivalent to
- \pset fieldsep.
+ Sets the field separator for unaligned and csv query outputs. The
+ default is the vertical bar (|). It is equivalent
+ to \pset fieldsep.
@@ -2527,8 +2528,8 @@ lo_import 152801
fieldsep
- Specifies the field separator to be used in unaligned output
- format. That way one can create, for example, tab- or
+ Specifies the field separator to be used in unaligned and csv output
+ formats. That way one can create, for example, tab- or
comma-separated output, which other programs might prefer. To
set a tab as field separator, type \pset fieldsep
'\t'. The default field separator is
@@ -2541,8 +2542,8 @@ lo_import 152801
fieldsep_zero
- Sets the field separator to use in unaligned output format to a zero
- byte.
+ Sets the field separator to use in unaligned or csv output formats to
+ a zero byte.
@@ -2565,9 +2566,13 @@ lo_import 152801
format
- Sets the output format to one of unaligned,
- aligned, wrapped,
- html, asciidoc,
+ Sets the output format to one of
+ unaligned,
+ aligned,
+ csv,
+ wrapped,
+ html,
+ asciidoc,
latex (uses tabular),
latex-longtable, or
troff-ms.
@@ -2582,6 +2587,12 @@ lo_import 152801
format).
+ csv format is similar to
+ unaligned, except that column contents are
+ enclosed in double quotes and quoted when necessary according to the
+ rules of the CSV format, and that no title or footer are printed.
+
+
aligned format is the standard, human-readable,
nicely formatted text output; this is the default.
@@ -2728,8 +2739,8 @@ lo_import 152801
recordsep
- Specifies the record (line) separator to use in unaligned
- output format. The default is a newline character.
+ Specifies the record (line) separator to use in unaligned or
+ csv output formats. The default is a newline character.
@@ -2738,8 +2749,8 @@ lo_import 152801
recordsep_zero
- Sets the record separator to use in unaligned output format to a zero
- byte.
+ Sets the record separator to use in unaligned or csv output
+ formats to a zero byte.
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 3560318..24f5a11 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -3603,6 +3603,9 @@ _align2string(enum printFormat in)
case PRINT_TROFF_MS:
return "troff-ms";
break;
+ case PRINT_CSV:
+ return "csv";
+ break;
}
return "unknown";
}
@@ -3674,9 +3677,11 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
popt->topt.format = PRINT_LATEX_LONGTABLE;
else if (pg_strncasecmp("troff-ms", value, vallen) == 0)
popt->topt.format = PRINT_TROFF_MS;
+ else if (pg_strncasecmp("csv", value, vallen) == 0)
+ popt->topt.format = PRINT_CSV;
else
{
- psql_error("\\pset: allowed formats are unaligned, aligned, wrapped, html, asciidoc, latex, latex-longtable, troff-ms\n");
+ psql_error("\\pset: allowed formats are unaligned, aligned, csv, wrapped, html, asciidoc, latex, latex-longtable, troff-ms\n");
return false;
}
}
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 8bc4a19..05171a9 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -3603,8 +3603,8 @@ psql_completion(const char *text, int start, int end)
if (TailMatchesCS1("format"))
{
static const char *const my_list[] =
- {"unaligned", "aligned", "wrapped", "html", "asciidoc",
- "latex", "latex-longtable", "troff-ms", NULL};
+ {"unaligned", "aligned", "csv", "wrapped", "html", "asciidoc",
+ "latex", "latex-longtable", "troff-ms", NULL};
COMPLETE_WITH_LIST_CS(my_list);
}
diff --git a/src/fe_utils/print.c b/src/fe_utils/print.c
index ec5ad45..45baa52 100644
--- a/src/fe_utils/print.c
+++ b/src/fe_utils/print.c
@@ -2783,6 +2783,125 @@ print_troff_ms_vertical(const printTableContent *cont, FILE *fout)
}
}
+/*************************/
+/* CSV */
+/*************************/
+static void
+csv_escaped_print(const char *text, FILE *fout)
+{
+ const char *p;
+
+ fputc('"', fout);
+ for (p = text; *p; p++)
+ {
+ if (*p == '"')
+ fputc('"', fout); /* double quotes are doubled */
+ fputc(*p, fout);
+ }
+ fputc('"', fout);
+}
+
+static void
+print_csv_text(const printTableContent *cont, FILE *fout)
+{
+ const char *const *ptr;
+ bool need_recordsep = false;
+ int i;
+
+ if (cancel_pressed)
+ return;
+
+ /*
+ * The title and footer are never printed in csv format.
+ * The header is printed if opt_tuples_only is false.
+ */
+
+ if (cont->opt->start_table && !cont->opt->tuples_only)
+ {
+ /* print headers */
+ for (ptr = cont->headers; *ptr; ptr++)
+ {
+ if (ptr != cont->headers)
+ print_separator(cont->opt->fieldSep, fout);
+ fputs(*ptr, fout);
+ }
+ need_recordsep = true;
+ }
+
+ /* print cells */
+ for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
+ {
+ if (need_recordsep)
+ {
+ print_separator(cont->opt->recordSep, fout);
+ need_recordsep = false;
+ if (cancel_pressed)
+ break;
+ }
+
+ /*
+ * Enclose and escape field contents when one of these conditions is
+ * met:
+ * - the field separator is found in the contents
+ * - the field contains a CR or LF
+ * - the field contains a double quote
+ */
+ if ((cont->opt->fieldSep.separator != NULL &&
+ *cont->opt->fieldSep.separator != '\0' &&
+ strstr(*ptr, cont->opt->fieldSep.separator) != NULL) ||
+ strcspn(*ptr, "\r\n\"") != strlen(*ptr))
+ {
+ csv_escaped_print(*ptr, fout);
+ }
+ else
+ fputs(*ptr, fout);
+
+ if ((i + 1) % cont->ncolumns)
+ print_separator(cont->opt->fieldSep, fout);
+ else
+ need_recordsep = true;
+ }
+
+ if (cont->opt->stop_table && need_recordsep)
+ {
+ print_separator(cont->opt->recordSep, fout);
+ }
+}
+
+static void
+print_csv_vertical(const printTableContent *cont, FILE *fout)
+{
+ unsigned int i;
+ const char *const *ptr;
+ bool need_recordsep = false;
+
+ if (cancel_pressed)
+ return;
+
+ /* print records */
+ for (i = 0, ptr = cont->cells; *ptr; i++, ptr++)
+ {
+ if (need_recordsep)
+ {
+ /* record separator is 2 occurrences of recordsep in this mode */
+ 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);
+ print_separator(cont->opt->fieldSep, fout);
+ fputs(*ptr, fout);
+
+ if ((i + 1) % cont->ncolumns)
+ print_separator(cont->opt->recordSep, fout);
+ else
+ need_recordsep = true;
+ }
+}
+
/********************************/
/* Public functions */
@@ -3234,6 +3353,12 @@ printTable(const printTableContent *cont,
else
print_aligned_text(cont, fout, is_pager);
break;
+ case PRINT_CSV:
+ if (cont->opt->expanded == 1)
+ print_csv_vertical(cont, fout);
+ else
+ print_csv_text(cont, fout);
+ break;
case PRINT_HTML:
if (cont->opt->expanded == 1)
print_html_vertical(cont, fout);
diff --git a/src/include/fe_utils/print.h b/src/include/fe_utils/print.h
index 83320d0..82e50fb 100644
--- a/src/include/fe_utils/print.h
+++ b/src/include/fe_utils/print.h
@@ -33,7 +33,8 @@ enum printFormat
PRINT_ASCIIDOC,
PRINT_LATEX,
PRINT_LATEX_LONGTABLE,
- PRINT_TROFF_MS
+ PRINT_TROFF_MS,
+ PRINT_CSV
/* add your favourite output format here ... */
};