Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 02e1456

Browse files
committed
Set psql client encoding from locale by default
Add a new libpq connection option client_encoding (which includes the existing PGCLIENTENCODING environment variable), which besides an encoding name accepts a special value "auto" that tries to determine the encoding from the locale in the client's environment, using the mechanisms that have been in use in initdb. psql sets this new connection option to "auto" when running from a terminal and not overridden by setting PGCLIENTENCODING. original code by Heikki Linnakangas, with subsequent contributions by Jaime Casanova, Peter Eisentraut, Stephen Frost, Ibrar Ahmed
1 parent 327e025 commit 02e1456

File tree

10 files changed

+136
-24
lines changed

10 files changed

+136
-24
lines changed

doc/src/sgml/libpq.sgml

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,21 @@ PGconn *PQconnectdbParams(const char **keywords, const char **values, int expand
259259
</listitem>
260260
</varlistentry>
261261

262+
<varlistentry id="libpq-connect-client-encoding" xreflabel="client_encoding">
263+
<term><literal>client_encoding</literal></term>
264+
<listitem>
265+
<para>
266+
This sets the <varname>client_encoding</varname>
267+
configuration parameter for this connection. In addition to
268+
the values accepted by the corresponding server option, you
269+
can use <literal>auto</literal> to determine the right
270+
encoding from the current locale in the client
271+
(<envar>LC_CTYPE</envar> environment variable on Unix
272+
systems).
273+
</para>
274+
</listitem>
275+
</varlistentry>
276+
262277
<varlistentry id="libpq-connect-options" xreflabel="options">
263278
<term><literal>options</literal></term>
264279
<listitem>
@@ -6345,6 +6360,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
63456360
linkend="libpq-connect-connect-timeout"> connection parameter.
63466361
</para>
63476362
</listitem>
6363+
6364+
<listitem>
6365+
<para>
6366+
<indexterm>
6367+
<primary><envar>PGCLIENTENCODING</envar></primary>
6368+
</indexterm>
6369+
<envar>PGCLIENTENCODING</envar> behaves the same as the <xref
6370+
linkend="libpq-connect-client-encoding"> connection parameter.
6371+
</para>
6372+
</listitem>
63486373
</itemizedlist>
63496374
</para>
63506375

@@ -6378,17 +6403,6 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
63786403
</para>
63796404
</listitem>
63806405

6381-
<listitem>
6382-
<para>
6383-
<indexterm>
6384-
<primary><envar>PGCLIENTENCODING</envar></primary>
6385-
</indexterm>
6386-
<envar>PGCLIENTENCODING</envar> sets the default client character
6387-
set encoding. (Equivalent to <literal>SET client_encoding TO
6388-
...</literal>.)
6389-
</para>
6390-
</listitem>
6391-
63926406
<listitem>
63936407
<para>
63946408
<indexterm>

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,17 @@ $ <userinput>psql "service=myservice sslmode=require"</userinput>
593593
privileges, server is not running on the targeted host, etc.),
594594
<application>psql</application> will return an error and terminate.
595595
</para>
596+
597+
<para>
598+
If at least one of standard input or standard output are a
599+
terminal, then <application>psql</application> sets the client
600+
encoding to <quote>auto</quote>, which will detect the
601+
appropriate client encoding from the locale settings
602+
(<envar>LC_CTYPE</envar> environment variable on Unix systems).
603+
If this doesn't work out as expected, the client encoding can be
604+
overridden using the environment
605+
variable <envar>PGCLIENTENCODING</envar>.
606+
</para>
596607
</refsect2>
597608

598609
<refsect2 id="R2-APP-PSQL-4">

src/bin/psql/command.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1487,7 +1487,7 @@ do_connect(char *dbname, char *user, char *host, char *port)
14871487

14881488
while (true)
14891489
{
1490-
#define PARAMS_ARRAY_SIZE 7
1490+
#define PARAMS_ARRAY_SIZE 8
14911491
const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
14921492
const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
14931493

@@ -1503,8 +1503,10 @@ do_connect(char *dbname, char *user, char *host, char *port)
15031503
values[4] = dbname;
15041504
keywords[5] = "fallback_application_name";
15051505
values[5] = pset.progname;
1506-
keywords[6] = NULL;
1507-
values[6] = NULL;
1506+
keywords[6] = "client_encoding";
1507+
values[6] = (pset.notty || getenv("PGCLIENTENCODING")) ? NULL : "auto";
1508+
keywords[7] = NULL;
1509+
values[7] = NULL;
15081510

15091511
n_conn = PQconnectdbParams(keywords, values, true);
15101512

src/bin/psql/startup.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ main(int argc, char *argv[])
171171
/* loop until we have a password if requested by backend */
172172
do
173173
{
174-
#define PARAMS_ARRAY_SIZE 7
174+
#define PARAMS_ARRAY_SIZE 8
175175
const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
176176
const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
177177

@@ -189,8 +189,10 @@ main(int argc, char *argv[])
189189
"postgres" : options.dbname;
190190
keywords[5] = "fallback_application_name";
191191
values[5] = pset.progname;
192-
keywords[6] = NULL;
193-
values[6] = NULL;
192+
keywords[6] = "client_encoding";
193+
values[6] = (pset.notty || getenv("PGCLIENTENCODING")) ? NULL : "auto";
194+
keywords[7] = NULL;
195+
values[7] = NULL;
194196

195197
new_pass = false;
196198
pset.db = PQconnectdbParams(keywords, values, true);

src/interfaces/libpq/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/exports.list
2+
/chklocale.c
23
/crypt.c
34
/getaddrinfo.c
45
/inet_aton.c

src/interfaces/libpq/Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
3535
fe-protocol2.o fe-protocol3.o pqexpbuffer.o pqsignal.o fe-secure.o \
3636
libpq-events.o
3737
# libpgport C files we always use
38-
OBJS += inet_net_ntop.o noblock.o pgstrcasecmp.o thread.o
38+
OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o thread.o
3939
# libpgport C files that are needed if identified by configure
4040
OBJS += $(filter crypt.o getaddrinfo.o inet_aton.o open.o snprintf.o strerror.o strlcpy.o win32error.o, $(LIBOBJS))
4141
# backend/libpq
@@ -88,7 +88,7 @@ backend_src = $(top_srcdir)/src/backend
8888
# For some libpgport modules, this only happens if configure decides
8989
# the module is needed (see filter hack in OBJS, above).
9090

91-
crypt.c getaddrinfo.c inet_aton.c inet_net_ntop.c noblock.c open.c pgsleep.c pgstrcasecmp.c snprintf.c strerror.c strlcpy.c thread.c win32error.c: % : $(top_srcdir)/src/port/%
91+
chklocale.c crypt.c getaddrinfo.c inet_aton.c inet_net_ntop.c noblock.c open.c pgsleep.c pgstrcasecmp.c snprintf.c strerror.c strlcpy.c thread.c win32error.c: % : $(top_srcdir)/src/port/%
9292
rm -f $@ && $(LN_S) $< .
9393

9494
ip.c md5.c: % : $(backend_src)/libpq/%
@@ -135,7 +135,7 @@ clean distclean: clean-lib
135135
# Might be left over from a Win32 client-only build
136136
rm -f pg_config_paths.h
137137
rm -f inet_net_ntop.c noblock.c pgstrcasecmp.c thread.c
138-
rm -f crypt.c getaddrinfo.c inet_aton.c open.c snprintf.c strerror.c strlcpy.c win32error.c
138+
rm -f chklocale.c crypt.c getaddrinfo.c inet_aton.c open.c snprintf.c strerror.c strlcpy.c win32error.c
139139
rm -f pgsleep.c
140140
rm -f md5.c ip.c
141141
rm -f encnames.c wchar.c

src/interfaces/libpq/fe-connect.c

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ static const PQconninfoOption PQconninfoOptions[] = {
175175
{"port", "PGPORT", DEF_PGPORT_STR, NULL,
176176
"Database-Port", "", 6},
177177

178+
{"client_encoding", "PGCLIENTENCODING", NULL, NULL,
179+
"Client-Encoding", "", 10},
180+
178181
/*
179182
* "tty" is no longer used either, but keep it present for backwards
180183
* compatibility.
@@ -270,9 +273,6 @@ static const PQEnvironmentOption EnvironmentOptions[] =
270273
{
271274
"PGTZ", "timezone"
272275
},
273-
{
274-
"PGCLIENTENCODING", "client_encoding"
275-
},
276276
/* internal performance-related settings */
277277
{
278278
"PGGEQO", "geqo"
@@ -612,6 +612,8 @@ fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
612612
conn->pgpass = tmp ? strdup(tmp) : NULL;
613613
tmp = conninfo_getval(connOptions, "connect_timeout");
614614
conn->connect_timeout = tmp ? strdup(tmp) : NULL;
615+
tmp = conninfo_getval(connOptions, "client_encoding");
616+
conn->client_encoding_initial = tmp ? strdup(tmp) : NULL;
615617
tmp = conninfo_getval(connOptions, "keepalives");
616618
conn->keepalives = tmp ? strdup(tmp) : NULL;
617619
tmp = conninfo_getval(connOptions, "keepalives_idle");
@@ -786,6 +788,16 @@ connectOptions2(PGconn *conn)
786788
else
787789
conn->sslmode = strdup(DefaultSSLMode);
788790

791+
/*
792+
* Resolve special "auto" client_encoding from the locale
793+
*/
794+
if (conn->client_encoding_initial &&
795+
strcmp(conn->client_encoding_initial, "auto") == 0)
796+
{
797+
free(conn->client_encoding_initial);
798+
conn->client_encoding_initial = strdup(pg_encoding_to_char(pg_get_encoding_from_locale(NULL, true)));
799+
}
800+
789801
/*
790802
* Only if we get this far is it appropriate to try to connect. (We need a
791803
* state flag, rather than just the boolean result of this function, in
@@ -2508,7 +2520,7 @@ PQconnectPoll(PGconn *conn)
25082520
if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
25092521
{
25102522
conn->status = CONNECTION_SETENV;
2511-
conn->setenv_state = SETENV_STATE_OPTION_SEND;
2523+
conn->setenv_state = SETENV_STATE_CLIENT_ENCODING_SEND;
25122524
conn->next_eo = EnvironmentOptions;
25132525
return PGRES_POLLING_WRITING;
25142526
}
@@ -4661,6 +4673,10 @@ PQsetClientEncoding(PGconn *conn, const char *encoding)
46614673
if (!encoding)
46624674
return -1;
46634675

4676+
/* Resolve special "auto" value from the locale */
4677+
if (strcmp(encoding, "auto") == 0)
4678+
encoding = pg_encoding_to_char(pg_get_encoding_from_locale(NULL, true));
4679+
46644680
/* check query buffer overflow */
46654681
if (sizeof(qbuf) < (sizeof(query) + strlen(encoding)))
46664682
return -1;

src/interfaces/libpq/fe-protocol2.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ pqSetenvPoll(PGconn *conn)
5858
switch (conn->setenv_state)
5959
{
6060
/* These are reading states */
61+
case SETENV_STATE_CLIENT_ENCODING_WAIT:
6162
case SETENV_STATE_OPTION_WAIT:
6263
case SETENV_STATE_QUERY1_WAIT:
6364
case SETENV_STATE_QUERY2_WAIT:
@@ -74,6 +75,7 @@ pqSetenvPoll(PGconn *conn)
7475
}
7576

7677
/* These are writing states, so we just proceed. */
78+
case SETENV_STATE_CLIENT_ENCODING_SEND:
7779
case SETENV_STATE_OPTION_SEND:
7880
case SETENV_STATE_QUERY1_SEND:
7981
case SETENV_STATE_QUERY2_SEND:
@@ -98,6 +100,39 @@ pqSetenvPoll(PGconn *conn)
98100
{
99101
switch (conn->setenv_state)
100102
{
103+
/*
104+
* The _CLIENT_ENCODING_SEND code is slightly different
105+
* from _OPTION_SEND below (e.g., no getenv() call), which
106+
* is why a different state is used.
107+
*/
108+
case SETENV_STATE_CLIENT_ENCODING_SEND:
109+
{
110+
char setQuery[100]; /* note length limit in
111+
* sprintf below */
112+
const char *val = conn->client_encoding_initial;
113+
114+
if (val)
115+
{
116+
if (pg_strcasecmp(val, "default") == 0)
117+
sprintf(setQuery, "SET client_encoding = DEFAULT");
118+
else
119+
sprintf(setQuery, "SET client_encoding = '%.60s'",
120+
val);
121+
#ifdef CONNECTDEBUG
122+
fprintf(stderr,
123+
"Sending client_encoding with %s\n",
124+
setQuery);
125+
#endif
126+
if (!PQsendQuery(conn, setQuery))
127+
goto error_return;
128+
129+
conn->setenv_state = SETENV_STATE_CLIENT_ENCODING_WAIT;
130+
}
131+
else
132+
conn->setenv_state = SETENV_STATE_OPTION_SEND;
133+
break;
134+
}
135+
101136
case SETENV_STATE_OPTION_SEND:
102137
{
103138
/*
@@ -142,6 +177,31 @@ pqSetenvPoll(PGconn *conn)
142177
break;
143178
}
144179

180+
case SETENV_STATE_CLIENT_ENCODING_WAIT:
181+
{
182+
if (PQisBusy(conn))
183+
return PGRES_POLLING_READING;
184+
185+
res = PQgetResult(conn);
186+
187+
if (res)
188+
{
189+
if (PQresultStatus(res) != PGRES_COMMAND_OK)
190+
{
191+
PQclear(res);
192+
goto error_return;
193+
}
194+
PQclear(res);
195+
/* Keep reading until PQgetResult returns NULL */
196+
}
197+
else
198+
{
199+
/* Query finished, so send the next option */
200+
conn->setenv_state = SETENV_STATE_OPTION_SEND;
201+
}
202+
break;
203+
}
204+
145205
case SETENV_STATE_OPTION_WAIT:
146206
{
147207
if (PQisBusy(conn))

src/interfaces/libpq/fe-protocol3.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1933,6 +1933,9 @@ build_startup_packet(const PGconn *conn, char *packet,
19331933
ADD_STARTUP_OPTION("application_name", val);
19341934
}
19351935

1936+
if (conn->client_encoding_initial && conn->client_encoding_initial[0])
1937+
ADD_STARTUP_OPTION("client_encoding", conn->client_encoding_initial);
1938+
19361939
/* Add any environment-driven GUC settings needed */
19371940
for (next_eo = options; next_eo->envName; next_eo++)
19381941
{

src/interfaces/libpq/libpq-int.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,8 @@ typedef enum
235235
/* (this is used only for 2.0-protocol connections) */
236236
typedef enum
237237
{
238+
SETENV_STATE_CLIENT_ENCODING_SEND, /* About to send an Environment Option */
239+
SETENV_STATE_CLIENT_ENCODING_WAIT, /* Waiting for above send to complete */
238240
SETENV_STATE_OPTION_SEND, /* About to send an Environment Option */
239241
SETENV_STATE_OPTION_WAIT, /* Waiting for above send to complete */
240242
SETENV_STATE_QUERY1_SEND, /* About to send a status query */
@@ -293,6 +295,7 @@ struct pg_conn
293295
char *pgtty; /* tty on which the backend messages is
294296
* displayed (OBSOLETE, NOT USED) */
295297
char *connect_timeout; /* connection timeout (numeric string) */
298+
char *client_encoding_initial; /* encoding to use */
296299
char *pgoptions; /* options to start the backend with */
297300
char *appname; /* application name */
298301
char *fbappname; /* fallback application name */

0 commit comments

Comments
 (0)