Fix translation of special characters in psql's LaTeX output modes.

latex_escaped_print() mistranslated \ and failed to provide any translation
for # ^ and ~, all of which would typically lead to LaTeX document syntax
errors.  In addition it didn't translate < > and |, which would typically
render as unexpected characters.

To some extent this represents shortcomings in ancient versions of LaTeX,
which if memory serves had no easy way to render these control characters
as ASCII text.  But that's been fixed for, um, decades.  In any case there
is no value in emitting guaranteed-to-fail output for these characters.

Noted while fooling with test cases added by commit 9a98984f4.  Back-patch
the code change to all supported versions.
This commit is contained in:
Tom Lane 2018-11-26 17:32:51 -05:00
parent 95dcb8fc05
commit 70d7e507ef
3 changed files with 98 additions and 74 deletions

View File

@ -2301,14 +2301,34 @@ latex_escaped_print(const char *in, FILE *fout)
for (p = in; *p; p++) for (p = in; *p; p++)
switch (*p) switch (*p)
{ {
case '&': /*
fputs("\\&", fout); * We convert ASCII characters per the recommendations in
* Scott Pakin's "The Comprehensive LATEX Symbol List",
* available from CTAN. For non-ASCII, you're on your own.
*/
case '#':
fputs("\\#", fout);
break;
case '$':
fputs("\\$", fout);
break; break;
case '%': case '%':
fputs("\\%", fout); fputs("\\%", fout);
break; break;
case '$': case '&':
fputs("\\$", fout); fputs("\\&", fout);
break;
case '<':
fputs("\\textless{}", fout);
break;
case '>':
fputs("\\textgreater{}", fout);
break;
case '\\':
fputs("\\textbackslash{}", fout);
break;
case '^':
fputs("\\^{}", fout);
break; break;
case '_': case '_':
fputs("\\_", fout); fputs("\\_", fout);
@ -2316,13 +2336,17 @@ latex_escaped_print(const char *in, FILE *fout)
case '{': case '{':
fputs("\\{", fout); fputs("\\{", fout);
break; break;
case '|':
fputs("\\textbar{}", fout);
break;
case '}': case '}':
fputs("\\}", fout); fputs("\\}", fout);
break; break;
case '\\': case '~':
fputs("\\backslash", fout); fputs("\\~{}", fout);
break; break;
case '\n': case '\n':
/* This is not right, but doing it right seems too hard */
fputs("\\\\", fout); fputs("\\\\", fout);
break; break;
default: default:

View File

@ -3443,7 +3443,7 @@ Type & func \\
\noindent \noindent
\pset tuples_only false \pset tuples_only false
prepare q as prepare q as
select 'some\more_text' as "a$title", E' &foo%\n{bar}' as "junk", select 'some\more_text' as "a$title", E' #<foo>%&^~|\n{bar}' as "junk",
' ' as "empty", n as int ' ' as "empty", n as int
from generate_series(1,2) as n; from generate_series(1,2) as n;
\pset expanded off \pset expanded off
@ -3452,8 +3452,8 @@ execute q;
\begin{tabular}{lllr} \begin{tabular}{lllr}
\textit{a\$title} & \textit{junk} & \textit{empty} & \textit{int} \\ \textit{a\$title} & \textit{junk} & \textit{empty} & \textit{int} \\
\hline \hline
some\backslashmore\_text & \&foo\%\\\{bar\} & & 1 \\ some\textbackslash{}more\_text & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} & & 1 \\
some\backslashmore\_text & \&foo\%\\\{bar\} & & 2 \\ some\textbackslash{}more\_text & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} & & 2 \\
\end{tabular} \end{tabular}
\noindent (2 rows) \\ \noindent (2 rows) \\
@ -3463,8 +3463,8 @@ execute q;
\begin{tabular}{l | l | l | r} \begin{tabular}{l | l | l | r}
\textit{a\$title} & \textit{junk} & \textit{empty} & \textit{int} \\ \textit{a\$title} & \textit{junk} & \textit{empty} & \textit{int} \\
\hline \hline
some\backslashmore\_text & \&foo\%\\\{bar\} & & 1 \\ some\textbackslash{}more\_text & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} & & 1 \\
some\backslashmore\_text & \&foo\%\\\{bar\} & & 2 \\ some\textbackslash{}more\_text & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} & & 2 \\
\end{tabular} \end{tabular}
\noindent (2 rows) \\ \noindent (2 rows) \\
@ -3475,8 +3475,8 @@ execute q;
\hline \hline
\textit{a\$title} & \textit{junk} & \textit{empty} & \textit{int} \\ \textit{a\$title} & \textit{junk} & \textit{empty} & \textit{int} \\
\hline \hline
some\backslashmore\_text & \&foo\%\\\{bar\} & & 1 \\ some\textbackslash{}more\_text & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} & & 1 \\
some\backslashmore\_text & \&foo\%\\\{bar\} & & 2 \\ some\textbackslash{}more\_text & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} & & 2 \\
\hline \hline
\end{tabular} \end{tabular}
@ -3488,9 +3488,9 @@ execute q;
\hline \hline
\textit{a\$title} & \textit{junk} & \textit{empty} & \textit{int} \\ \textit{a\$title} & \textit{junk} & \textit{empty} & \textit{int} \\
\hline \hline
some\backslashmore\_text & \&foo\%\\\{bar\} & & 1 \\ some\textbackslash{}more\_text & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} & & 1 \\
\hline \hline
some\backslashmore\_text & \&foo\%\\\{bar\} & & 2 \\ some\textbackslash{}more\_text & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} & & 2 \\
\hline \hline
\end{tabular} \end{tabular}
@ -3501,13 +3501,13 @@ some\backslashmore\_text & \&foo\%\\\{bar\} & & 2 \\
execute q; execute q;
\begin{tabular}{cl} \begin{tabular}{cl}
\multicolumn{2}{c}{\textit{Record 1}} \\ \multicolumn{2}{c}{\textit{Record 1}} \\
a\$title & some\backslashmore\_text \\ a\$title & some\textbackslash{}more\_text \\
junk & \&foo\%\\\{bar\} \\ junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\
empty & \\ empty & \\
int & 1 \\ int & 1 \\
\multicolumn{2}{c}{\textit{Record 2}} \\ \multicolumn{2}{c}{\textit{Record 2}} \\
a\$title & some\backslashmore\_text \\ a\$title & some\textbackslash{}more\_text \\
junk & \&foo\%\\\{bar\} \\ junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\
empty & \\ empty & \\
int & 2 \\ int & 2 \\
\end{tabular} \end{tabular}
@ -3518,14 +3518,14 @@ execute q;
\begin{tabular}{c|l} \begin{tabular}{c|l}
\multicolumn{2}{c}{\textit{Record 1}} \\ \multicolumn{2}{c}{\textit{Record 1}} \\
\hline \hline
a\$title & some\backslashmore\_text \\ a\$title & some\textbackslash{}more\_text \\
junk & \&foo\%\\\{bar\} \\ junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\
empty & \\ empty & \\
int & 1 \\ int & 1 \\
\multicolumn{2}{c}{\textit{Record 2}} \\ \multicolumn{2}{c}{\textit{Record 2}} \\
\hline \hline
a\$title & some\backslashmore\_text \\ a\$title & some\textbackslash{}more\_text \\
junk & \&foo\%\\\{bar\} \\ junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\
empty & \\ empty & \\
int & 2 \\ int & 2 \\
\end{tabular} \end{tabular}
@ -3537,15 +3537,15 @@ execute q;
\hline \hline
\multicolumn{2}{|c|}{\textit{Record 1}} \\ \multicolumn{2}{|c|}{\textit{Record 1}} \\
\hline \hline
a\$title & some\backslashmore\_text \\ a\$title & some\textbackslash{}more\_text \\
junk & \&foo\%\\\{bar\} \\ junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\
empty & \\ empty & \\
int & 1 \\ int & 1 \\
\hline \hline
\multicolumn{2}{|c|}{\textit{Record 2}} \\ \multicolumn{2}{|c|}{\textit{Record 2}} \\
\hline \hline
a\$title & some\backslashmore\_text \\ a\$title & some\textbackslash{}more\_text \\
junk & \&foo\%\\\{bar\} \\ junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\
empty & \\ empty & \\
int & 2 \\ int & 2 \\
\hline \hline
@ -3558,15 +3558,15 @@ execute q;
\hline \hline
\multicolumn{2}{|c|}{\textit{Record 1}} \\ \multicolumn{2}{|c|}{\textit{Record 1}} \\
\hline \hline
a\$title & some\backslashmore\_text \\ a\$title & some\textbackslash{}more\_text \\
junk & \&foo\%\\\{bar\} \\ junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\
empty & \\ empty & \\
int & 1 \\ int & 1 \\
\hline \hline
\multicolumn{2}{|c|}{\textit{Record 2}} \\ \multicolumn{2}{|c|}{\textit{Record 2}} \\
\hline \hline
a\$title & some\backslashmore\_text \\ a\$title & some\textbackslash{}more\_text \\
junk & \&foo\%\\\{bar\} \\ junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\
empty & \\ empty & \\
int & 2 \\ int & 2 \\
\hline \hline
@ -3667,7 +3667,7 @@ Type & func \\
\noindent \noindent
\pset tuples_only false \pset tuples_only false
prepare q as prepare q as
select 'some\more_text' as "a$title", E' &foo%\n{bar}' as "junk", select 'some\more_text' as "a$title", E' #<foo>%&^~|\n{bar}' as "junk",
' ' as "empty", n as int ' ' as "empty", n as int
from generate_series(1,2) as n; from generate_series(1,2) as n;
\pset expanded off \pset expanded off
@ -3680,16 +3680,16 @@ execute q;
\small\textbf{\textit{a\$title}} & \small\textbf{\textit{junk}} & \small\textbf{\textit{empty}} & \small\textbf{\textit{int}} \\ \small\textbf{\textit{a\$title}} & \small\textbf{\textit{junk}} & \small\textbf{\textit{empty}} & \small\textbf{\textit{int}} \\
\midrule \midrule
\endhead \endhead
\raggedright{some\backslashmore\_text} \raggedright{some\textbackslash{}more\_text}
& &
\raggedright{ \&foo\%\\\{bar\}} \raggedright{ \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\}}
& &
\raggedright{ } \raggedright{ }
& &
\raggedright{1} \tabularnewline \raggedright{1} \tabularnewline
\raggedright{some\backslashmore\_text} \raggedright{some\textbackslash{}more\_text}
& &
\raggedright{ \&foo\%\\\{bar\}} \raggedright{ \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\}}
& &
\raggedright{ } \raggedright{ }
& &
@ -3704,16 +3704,16 @@ execute q;
\small\textbf{\textit{a\$title}} & \small\textbf{\textit{junk}} & \small\textbf{\textit{empty}} & \small\textbf{\textit{int}} \\ \small\textbf{\textit{a\$title}} & \small\textbf{\textit{junk}} & \small\textbf{\textit{empty}} & \small\textbf{\textit{int}} \\
\midrule \midrule
\endhead \endhead
\raggedright{some\backslashmore\_text} \raggedright{some\textbackslash{}more\_text}
& &
\raggedright{ \&foo\%\\\{bar\}} \raggedright{ \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\}}
& &
\raggedright{ } \raggedright{ }
& &
\raggedright{1} \tabularnewline \raggedright{1} \tabularnewline
\raggedright{some\backslashmore\_text} \raggedright{some\textbackslash{}more\_text}
& &
\raggedright{ \&foo\%\\\{bar\}} \raggedright{ \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\}}
& &
\raggedright{ } \raggedright{ }
& &
@ -3734,16 +3734,16 @@ execute q;
\endfoot \endfoot
\bottomrule \bottomrule
\endlastfoot \endlastfoot
\raggedright{some\backslashmore\_text} \raggedright{some\textbackslash{}more\_text}
& &
\raggedright{ \&foo\%\\\{bar\}} \raggedright{ \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\}}
& &
\raggedright{ } \raggedright{ }
& &
\raggedright{1} \tabularnewline \raggedright{1} \tabularnewline
\raggedright{some\backslashmore\_text} \raggedright{some\textbackslash{}more\_text}
& &
\raggedright{ \&foo\%\\\{bar\}} \raggedright{ \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\}}
& &
\raggedright{ } \raggedright{ }
& &
@ -3763,17 +3763,17 @@ execute q;
\endfoot \endfoot
\bottomrule \bottomrule
\endlastfoot \endlastfoot
\raggedright{some\backslashmore\_text} \raggedright{some\textbackslash{}more\_text}
& &
\raggedright{ \&foo\%\\\{bar\}} \raggedright{ \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\}}
& &
\raggedright{ } \raggedright{ }
& &
\raggedright{1} \tabularnewline \raggedright{1} \tabularnewline
\hline \hline
\raggedright{some\backslashmore\_text} \raggedright{some\textbackslash{}more\_text}
& &
\raggedright{ \&foo\%\\\{bar\}} \raggedright{ \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\}}
& &
\raggedright{ } \raggedright{ }
& &
@ -3794,17 +3794,17 @@ execute q;
\endfoot \endfoot
\bottomrule \bottomrule
\endlastfoot \endlastfoot
\raggedright{some\backslashmore\_text} \raggedright{some\textbackslash{}more\_text}
& &
\raggedright{ \&foo\%\\\{bar\}} \raggedright{ \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\}}
& &
\raggedright{ } \raggedright{ }
& &
\raggedright{1} \tabularnewline \raggedright{1} \tabularnewline
\hline \hline
\raggedright{some\backslashmore\_text} \raggedright{some\textbackslash{}more\_text}
& &
\raggedright{ \&foo\%\\\{bar\}} \raggedright{ \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\}}
& &
\raggedright{ } \raggedright{ }
& &
@ -3817,13 +3817,13 @@ execute q;
execute q; execute q;
\begin{tabular}{cl} \begin{tabular}{cl}
\multicolumn{2}{c}{\textit{Record 1}} \\ \multicolumn{2}{c}{\textit{Record 1}} \\
a\$title & some\backslashmore\_text \\ a\$title & some\textbackslash{}more\_text \\
junk & \&foo\%\\\{bar\} \\ junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\
empty & \\ empty & \\
int & 1 \\ int & 1 \\
\multicolumn{2}{c}{\textit{Record 2}} \\ \multicolumn{2}{c}{\textit{Record 2}} \\
a\$title & some\backslashmore\_text \\ a\$title & some\textbackslash{}more\_text \\
junk & \&foo\%\\\{bar\} \\ junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\
empty & \\ empty & \\
int & 2 \\ int & 2 \\
\end{tabular} \end{tabular}
@ -3834,14 +3834,14 @@ execute q;
\begin{tabular}{c|l} \begin{tabular}{c|l}
\multicolumn{2}{c}{\textit{Record 1}} \\ \multicolumn{2}{c}{\textit{Record 1}} \\
\hline \hline
a\$title & some\backslashmore\_text \\ a\$title & some\textbackslash{}more\_text \\
junk & \&foo\%\\\{bar\} \\ junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\
empty & \\ empty & \\
int & 1 \\ int & 1 \\
\multicolumn{2}{c}{\textit{Record 2}} \\ \multicolumn{2}{c}{\textit{Record 2}} \\
\hline \hline
a\$title & some\backslashmore\_text \\ a\$title & some\textbackslash{}more\_text \\
junk & \&foo\%\\\{bar\} \\ junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\
empty & \\ empty & \\
int & 2 \\ int & 2 \\
\end{tabular} \end{tabular}
@ -3853,15 +3853,15 @@ execute q;
\hline \hline
\multicolumn{2}{|c|}{\textit{Record 1}} \\ \multicolumn{2}{|c|}{\textit{Record 1}} \\
\hline \hline
a\$title & some\backslashmore\_text \\ a\$title & some\textbackslash{}more\_text \\
junk & \&foo\%\\\{bar\} \\ junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\
empty & \\ empty & \\
int & 1 \\ int & 1 \\
\hline \hline
\multicolumn{2}{|c|}{\textit{Record 2}} \\ \multicolumn{2}{|c|}{\textit{Record 2}} \\
\hline \hline
a\$title & some\backslashmore\_text \\ a\$title & some\textbackslash{}more\_text \\
junk & \&foo\%\\\{bar\} \\ junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\
empty & \\ empty & \\
int & 2 \\ int & 2 \\
\hline \hline
@ -3874,15 +3874,15 @@ execute q;
\hline \hline
\multicolumn{2}{|c|}{\textit{Record 1}} \\ \multicolumn{2}{|c|}{\textit{Record 1}} \\
\hline \hline
a\$title & some\backslashmore\_text \\ a\$title & some\textbackslash{}more\_text \\
junk & \&foo\%\\\{bar\} \\ junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\
empty & \\ empty & \\
int & 1 \\ int & 1 \\
\hline \hline
\multicolumn{2}{|c|}{\textit{Record 2}} \\ \multicolumn{2}{|c|}{\textit{Record 2}} \\
\hline \hline
a\$title & some\backslashmore\_text \\ a\$title & some\textbackslash{}more\_text \\
junk & \&foo\%\\\{bar\} \\ junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\
empty & \\ empty & \\
int & 2 \\ int & 2 \\
\hline \hline
@ -3895,15 +3895,15 @@ execute q;
\hline \hline
\multicolumn{2}{|c|}{\textit{Record 1}} \\ \multicolumn{2}{|c|}{\textit{Record 1}} \\
\hline \hline
a\$title & some\backslashmore\_text \\ a\$title & some\textbackslash{}more\_text \\
junk & \&foo\%\\\{bar\} \\ junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\
empty & \\ empty & \\
int & 1 \\ int & 1 \\
\hline \hline
\multicolumn{2}{|c|}{\textit{Record 2}} \\ \multicolumn{2}{|c|}{\textit{Record 2}} \\
\hline \hline
a\$title & some\backslashmore\_text \\ a\$title & some\textbackslash{}more\_text \\
junk & \&foo\%\\\{bar\} \\ junk & \#\textless{}foo\textgreater{}\%\&\^{}\~{}\textbar{}\\\{bar\} \\
empty & \\ empty & \\
int & 2 \\ int & 2 \\
\hline \hline

View File

@ -611,7 +611,7 @@ deallocate q;
\pset tuples_only false \pset tuples_only false
prepare q as prepare q as
select 'some\more_text' as "a$title", E' &foo%\n{bar}' as "junk", select 'some\more_text' as "a$title", E' #<foo>%&^~|\n{bar}' as "junk",
' ' as "empty", n as int ' ' as "empty", n as int
from generate_series(1,2) as n; from generate_series(1,2) as n;
@ -660,7 +660,7 @@ deallocate q;
\pset tuples_only false \pset tuples_only false
prepare q as prepare q as
select 'some\more_text' as "a$title", E' &foo%\n{bar}' as "junk", select 'some\more_text' as "a$title", E' #<foo>%&^~|\n{bar}' as "junk",
' ' as "empty", n as int ' ' as "empty", n as int
from generate_series(1,2) as n; from generate_series(1,2) as n;