1
1
/*
2
2
* This file contains public functions for conversion between
3
- * client encoding and server internal encoding.
4
- * (currently mule internal code (mic) is used)
3
+ * client encoding and server (database) encoding.
4
+ *
5
5
* Tatsuo Ishii
6
6
*
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 $
8
8
*/
9
9
#include "postgres.h"
10
10
28
28
#define MAX_CONVERSION_GROWTH 4
29
29
30
30
/*
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.
34
38
*/
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 */
37
48
38
49
/*
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.
43
52
*/
44
- static MemoryContext MbProcContext = NULL ;
45
53
static FmgrInfo * ToServerConvProc = NULL ;
46
54
static FmgrInfo * ToClientConvProc = NULL ;
47
55
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
+
48
62
/*
49
63
* During backend startup we can't set client encoding because we (a)
50
64
* can't look up the conversion functions, and (b) may not know the database
70
84
SetClientEncoding (int encoding , bool doit )
71
85
{
72
86
int 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 ;
78
88
79
89
if (!PG_VALID_FE_ENCODING (encoding ))
80
90
return -1 ;
@@ -101,79 +111,117 @@ SetClientEncoding(int encoding, bool doit)
101
111
ClientEncoding = & pg_enc2name_tbl [encoding ];
102
112
ToServerConvProc = NULL ;
103
113
ToClientConvProc = NULL ;
104
- if (MbProcContext )
105
- MemoryContextReset (MbProcContext );
106
114
}
107
115
return 0 ;
108
116
}
109
117
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 ;
119
139
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 ;
131
145
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 );
137
162
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 */
144
190
}
145
191
else
146
192
{
147
193
/*
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.
151
200
*/
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 );
166
204
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
+ }
170
217
171
- return 0 ;
218
+ return -1 ; /* it's not cached, so fail */
219
+ }
172
220
}
173
221
174
222
/*
175
223
* Initialize client encoding if necessary.
176
- * called from InitPostgres() once during backend starting up .
224
+ * called from InitPostgres() once during backend startup .
177
225
*/
178
226
void
179
227
InitializeClientEncoding (void )
@@ -196,7 +244,8 @@ InitializeClientEncoding(void)
196
244
}
197
245
198
246
/*
199
- * returns the current client encoding */
247
+ * returns the current client encoding
248
+ */
200
249
int
201
250
pg_get_client_encoding (void )
202
251
{
@@ -511,10 +560,9 @@ pg_server_to_client(const char *s, int len)
511
560
/*
512
561
* Perform default encoding conversion using cached FmgrInfo. Since
513
562
* 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
+ */
518
566
static char *
519
567
perform_default_encoding_conversion (const char * src , int len , bool is_client_to_server )
520
568
{
@@ -919,12 +967,6 @@ pg_bind_textdomain_codeset(const char *domainname, int encoding)
919
967
#endif
920
968
}
921
969
922
- void
923
- SetDefaultClientEncoding (void )
924
- {
925
- ClientEncoding = & pg_enc2name_tbl [GetDatabaseEncoding ()];
926
- }
927
-
928
970
int
929
971
GetDatabaseEncoding (void )
930
972
{
0 commit comments