11/*
22 * This file contains public functions for conversion between
3- * client encoding and serverinternal encoding.
4- * (currently mule internal code (mic) is used)
3+ * client encoding and server(database) encoding.
4+ *
55 * Tatsuo Ishii
66 *
7- * $PostgreSQL: pgsql/src/backend/utils/mb/mbutils.c,v 1.82 2009/03/09 00:01:32 alvherre Exp $
7+ * $PostgreSQL: pgsql/src/backend/utils/mb/mbutils.c,v 1.83 2009/04/02 17:30:53 tgl Exp $
88 */
99#include "postgres.h"
1010
2828#define MAX_CONVERSION_GROWTH 4
2929
3030/*
31- * We handle for actual FE and BE encoding setting encoding-identificator
32- * and encoding-name too. It prevent searching and conversion from encoding
33- * to encoding name in getdatabaseencoding() and other routines.
31+ * We maintain a simple linked list caching the fmgr lookup info for the
32+ * currently selected conversion functions, as well as any that have been
33+ * selected previously in the current session. (We remember previous
34+ * settings because we must be able to restore a previous setting during
35+ * transaction rollback, without doing any fresh catalog accesses.)
36+ *
37+ * Since we'll never release this data, we just keep it in TopMemoryContext.
3438 */
35- static pg_enc2name * ClientEncoding = & pg_enc2name_tbl [PG_SQL_ASCII ];
36- static pg_enc2name * DatabaseEncoding = & pg_enc2name_tbl [PG_SQL_ASCII ];
39+ typedef struct ConvProcInfo
40+ {
41+ int s_encoding ;/* server and client encoding IDs */
42+ int c_encoding ;
43+ FmgrInfo to_server_info ;/* lookup info for conversion procs */
44+ FmgrInfo to_client_info ;
45+ }ConvProcInfo ;
46+
47+ static List * ConvProcList = NIL ;/* List of ConvProcInfo */
3748
3849/*
39- * Caches for conversion function info. These values are allocated in
40- * MbProcContext. That context is a child of TopMemoryContext,
41- * which allows these values to survive across transactions. See
42- * SetClientEncoding() for more details.
50+ * These variables point to the currently active conversion functions,
51+ * or are NULL when no conversion is needed.
4352 */
44- static MemoryContext MbProcContext = NULL ;
4553static FmgrInfo * ToServerConvProc = NULL ;
4654static FmgrInfo * ToClientConvProc = NULL ;
4755
56+ /*
57+ * These variables track the currently selected FE and BE encodings.
58+ */
59+ static pg_enc2name * ClientEncoding = & pg_enc2name_tbl [PG_SQL_ASCII ];
60+ static pg_enc2name * DatabaseEncoding = & pg_enc2name_tbl [PG_SQL_ASCII ];
61+
4862/*
4963 * During backend startup we can't set client encoding because we (a)
5064 * can't look up the conversion functions, and (b) may not know the database
7084SetClientEncoding (int encoding ,bool doit )
7185{
7286int current_server_encoding ;
73- Oid to_server_proc ,
74- to_client_proc ;
75- FmgrInfo * to_server ;
76- FmgrInfo * to_client ;
77- MemoryContext oldcontext ;
87+ ListCell * lc ;
7888
7989if (!PG_VALID_FE_ENCODING (encoding ))
8090return -1 ;
@@ -101,79 +111,117 @@ SetClientEncoding(int encoding, bool doit)
101111ClientEncoding = & pg_enc2name_tbl [encoding ];
102112ToServerConvProc = NULL ;
103113ToClientConvProc = NULL ;
104- if (MbProcContext )
105- MemoryContextReset (MbProcContext );
106114}
107115return 0 ;
108116}
109117
110- /*
111- * If we're not inside a transaction then we can't do catalog lookups, so
112- * fail. After backend startup, this could only happen if we are
113- * re-reading postgresql.conf due to SIGHUP --- so basically this just
114- * constrains the ability to change client_encoding on the fly from
115- * postgresql.conf. Which would probably be a stupid thing to do anyway.
116- */
117- if (!IsTransactionState ())
118- return -1 ;
118+ if (IsTransactionState ())
119+ {
120+ /*
121+ * If we're in a live transaction, it's safe to access the catalogs,
122+ * so look up the functions. We repeat the lookup even if the info
123+ * is already cached, so that we can react to changes in the contents
124+ * of pg_conversion.
125+ */
126+ Oid to_server_proc ,
127+ to_client_proc ;
128+ ConvProcInfo * convinfo ;
129+ MemoryContext oldcontext ;
130+
131+ to_server_proc = FindDefaultConversionProc (encoding ,
132+ current_server_encoding );
133+ if (!OidIsValid (to_server_proc ))
134+ return -1 ;
135+ to_client_proc = FindDefaultConversionProc (current_server_encoding ,
136+ encoding );
137+ if (!OidIsValid (to_client_proc ))
138+ return -1 ;
119139
120- /*
121- * Look up the conversion functions.
122- */
123- to_server_proc = FindDefaultConversionProc (encoding ,
124- current_server_encoding );
125- if (!OidIsValid (to_server_proc ))
126- return -1 ;
127- to_client_proc = FindDefaultConversionProc (current_server_encoding ,
128- encoding );
129- if (!OidIsValid (to_client_proc ))
130- return -1 ;
140+ /*
141+ * Done if not wanting to actually apply setting.
142+ */
143+ if (!doit )
144+ return 0 ;
131145
132- /*
133- * Done if not wanting to actually apply setting.
134- */
135- if (!doit )
136- return 0 ;
146+ /*
147+ * Load the fmgr info into TopMemoryContext (could still fail here)
148+ */
149+ convinfo = (ConvProcInfo * )MemoryContextAlloc (TopMemoryContext ,
150+ sizeof (ConvProcInfo ));
151+ convinfo -> s_encoding = current_server_encoding ;
152+ convinfo -> c_encoding = encoding ;
153+ fmgr_info_cxt (to_server_proc ,& convinfo -> to_server_info ,
154+ TopMemoryContext );
155+ fmgr_info_cxt (to_client_proc ,& convinfo -> to_client_info ,
156+ TopMemoryContext );
157+
158+ /* Attach new info to head of list */
159+ oldcontext = MemoryContextSwitchTo (TopMemoryContext );
160+ ConvProcList = lcons (convinfo ,ConvProcList );
161+ MemoryContextSwitchTo (oldcontext );
137162
138- /* Before loading the new fmgr info, remove the old info, if any */
139- ToServerConvProc = NULL ;
140- ToClientConvProc = NULL ;
141- if (MbProcContext != NULL )
142- {
143- MemoryContextReset (MbProcContext );
163+ /*
164+ * Everything is okay, so apply the setting.
165+ */
166+ ClientEncoding = & pg_enc2name_tbl [encoding ];
167+ ToServerConvProc = & convinfo -> to_server_info ;
168+ ToClientConvProc = & convinfo -> to_client_info ;
169+
170+ /*
171+ * Remove any older entry for the same encoding pair (this is just
172+ * to avoid memory leakage).
173+ */
174+ foreach (lc ,ConvProcList )
175+ {
176+ ConvProcInfo * oldinfo = (ConvProcInfo * )lfirst (lc );
177+
178+ if (oldinfo == convinfo )
179+ continue ;
180+ if (oldinfo -> s_encoding == convinfo -> s_encoding &&
181+ oldinfo -> c_encoding == convinfo -> c_encoding )
182+ {
183+ ConvProcList = list_delete_ptr (ConvProcList ,oldinfo );
184+ pfree (oldinfo );
185+ break ;/* need not look further */
186+ }
187+ }
188+
189+ return 0 ;/* success */
144190}
145191else
146192{
147193/*
148- * This is the first time through, so create the context. Make it a
149- * child of TopMemoryContext so that these values survive across
150- * transactions.
194+ * If we're not in a live transaction, the only thing we can do
195+ * is restore a previous setting using the cache. This covers all
196+ * transaction-rollback cases. The only case it might not work for
197+ * is trying to change client_encoding on the fly by editing
198+ * postgresql.conf and SIGHUP'ing. Which would probably be a stupid
199+ * thing to do anyway.
151200 */
152- MbProcContext = AllocSetContextCreate (TopMemoryContext ,
153- "MbProcContext" ,
154- ALLOCSET_SMALL_MINSIZE ,
155- ALLOCSET_SMALL_INITSIZE ,
156- ALLOCSET_SMALL_MAXSIZE );
157- }
158-
159- /* Load the fmgr info into MbProcContext */
160- oldcontext = MemoryContextSwitchTo (MbProcContext );
161- to_server = palloc (sizeof (FmgrInfo ));
162- to_client = palloc (sizeof (FmgrInfo ));
163- fmgr_info (to_server_proc ,to_server );
164- fmgr_info (to_client_proc ,to_client );
165- MemoryContextSwitchTo (oldcontext );
201+ foreach (lc ,ConvProcList )
202+ {
203+ ConvProcInfo * oldinfo = (ConvProcInfo * )lfirst (lc );
166204
167- ClientEncoding = & pg_enc2name_tbl [encoding ];
168- ToServerConvProc = to_server ;
169- ToClientConvProc = to_client ;
205+ if (oldinfo -> s_encoding == current_server_encoding &&
206+ oldinfo -> c_encoding == encoding )
207+ {
208+ if (doit )
209+ {
210+ ClientEncoding = & pg_enc2name_tbl [encoding ];
211+ ToServerConvProc = & oldinfo -> to_server_info ;
212+ ToClientConvProc = & oldinfo -> to_client_info ;
213+ }
214+ return 0 ;
215+ }
216+ }
170217
171- return 0 ;
218+ return -1 ;/* it's not cached, so fail */
219+ }
172220}
173221
174222/*
175223 * Initialize client encoding if necessary.
176- *called from InitPostgres() once during backendstarting up .
224+ *called from InitPostgres() once during backendstartup .
177225 */
178226void
179227InitializeClientEncoding (void )
@@ -196,7 +244,8 @@ InitializeClientEncoding(void)
196244}
197245
198246/*
199- * returns the current client encoding */
247+ * returns the current client encoding
248+ */
200249int
201250pg_get_client_encoding (void )
202251{
@@ -511,10 +560,9 @@ pg_server_to_client(const char *s, int len)
511560/*
512561 *Perform default encoding conversion using cached FmgrInfo. Since
513562 *this function does not access database at all, it is safe to call
514- *outside transactions. Explicit setting client encoding required
515- *before calling this function. Otherwise no conversion is
516- *performed.
517- */
563+ *outside transactions. If the conversion has not been set up by
564+ *SetClientEncoding(), no conversion is performed.
565+ */
518566static char *
519567perform_default_encoding_conversion (const char * src ,int len ,bool is_client_to_server )
520568{
@@ -919,12 +967,6 @@ pg_bind_textdomain_codeset(const char *domainname, int encoding)
919967#endif
920968}
921969
922- void
923- SetDefaultClientEncoding (void )
924- {
925- ClientEncoding = & pg_enc2name_tbl [GetDatabaseEncoding ()];
926- }
927-
928970int
929971GetDatabaseEncoding (void )
930972{