]> granicus.if.org Git - postgresql/blob - src/backend/utils/mb/mbutils.c
Fix bug in pg_convert() per report from MaC.Yui.
[postgresql] / src / backend / utils / mb / mbutils.c
1 /*
2  * This file contains public functions for conversion between
3  * client encoding and server internal encoding.
4  * (currently mule internal code (mic) is used)
5  * Tatsuo Ishii
6  * $Id: mbutils.c,v 1.32 2002/08/19 04:08:08 ishii Exp $
7  */
8 #include "postgres.h"
9 #include "access/xact.h"
10 #include "miscadmin.h"
11 #include "mb/pg_wchar.h"
12 #include "utils/builtins.h"
13 #include "utils/memutils.h"
14 #include "utils/syscache.h"
15 #include "catalog/namespace.h"
16
17 /*
18  * We handle for actual FE and BE encoding setting encoding-identificator
19  * and encoding-name too. It prevent searching and conversion from encoding
20  * to encoding name in getdatabaseencoding() and other routines.
21  */
22 static pg_enc2name *ClientEncoding = &pg_enc2name_tbl[PG_SQL_ASCII];
23 static pg_enc2name *DatabaseEncoding = &pg_enc2name_tbl[PG_SQL_ASCII];
24
25 /*
26  * Caches for conversion function info. Note that Fcinfo.flinfo is
27  * allocated in TopMemoryContext so that it survives outside
28  * transactions. See SetClientEncoding() for more details.
29  */
30 static  FmgrInfo                *ToServerConvPorc = NULL;
31 static  FmgrInfo                *ToClientConvPorc = NULL;
32
33 /* Internal functions */
34 static unsigned char *
35 perform_default_encoding_conversion(unsigned char *src, int len, bool is_client_to_server);
36
37 /*
38  * Set the client encoding and save fmgrinfo for the converion
39  * function if necessary. if encoding conversion between client/server
40  * encoding is not supported, returns -1
41 */
42 int
43 SetClientEncoding(int encoding, bool doit)
44 {
45         int                     current_server_encoding;
46         Oid                     to_server_proc, to_client_proc;
47         FmgrInfo        *to_server = NULL;
48         FmgrInfo        *to_client = NULL;
49         MemoryContext oldcontext;
50
51         current_server_encoding = GetDatabaseEncoding();
52
53         if (!PG_VALID_FE_ENCODING(encoding))
54                 return (-1);
55
56         if (current_server_encoding == encoding ||
57                 (current_server_encoding == PG_SQL_ASCII || encoding == PG_SQL_ASCII))
58         {
59                 ClientEncoding = &pg_enc2name_tbl[encoding];
60                 return 0;
61         }
62
63         /* XXX We cannot use FindDefaultConversionProc() while in
64          * bootstrap or initprocessing mode since namespace functions will
65          * not work.
66          */
67         if (IsTransactionState())
68         {
69                 to_server_proc = FindDefaultConversionProc(encoding, current_server_encoding);
70                 to_client_proc = FindDefaultConversionProc(current_server_encoding, encoding);
71
72                 if (!OidIsValid(to_server_proc) || !OidIsValid(to_client_proc))
73                         return -1;
74
75                 /*
76                  * load the fmgr info into TopMemoryContext so that it
77                  * survives outside transaction.
78                  */
79                 oldcontext = MemoryContextSwitchTo(TopMemoryContext);
80                 to_server = palloc(sizeof(FmgrInfo));
81                 to_client = palloc(sizeof(FmgrInfo));
82                 fmgr_info(to_server_proc, to_server);
83                 fmgr_info(to_client_proc, to_client);
84                 MemoryContextSwitchTo(oldcontext);
85         }
86
87         if (!doit)
88                 return 0;
89
90         if (IsTransactionState())
91         {
92                 ClientEncoding = &pg_enc2name_tbl[encoding];
93
94                 if(ToServerConvPorc != NULL)
95                 {
96                         if (ToServerConvPorc->fn_extra)
97                                 pfree(ToServerConvPorc->fn_extra);
98                         pfree(ToServerConvPorc);
99                 }
100                 ToServerConvPorc = to_server;
101
102                 if(ToClientConvPorc != NULL)
103                 {
104                         if (ToClientConvPorc->fn_extra)
105                                 pfree(ToClientConvPorc->fn_extra);
106                         pfree(ToClientConvPorc);
107                 }
108                 ToClientConvPorc = to_client;
109         }
110         return 0;
111 }
112
113 /*
114  * returns the current client encoding */
115 int
116 pg_get_client_encoding(void)
117 {
118         Assert(ClientEncoding);
119         return (ClientEncoding->encoding);
120 }
121
122 /*
123  * returns the current client encoding name
124  */
125 const char *
126 pg_get_client_encoding_name(void)
127 {
128         Assert(ClientEncoding);
129         return (ClientEncoding->name);
130 }
131
132 /*
133  * Apply encoding conversion on src and return it. The encoding
134  * conversion function is chosen from the pg_conversion system catalog
135  * marked as "default". If it is not found in the schema search path,
136  * it's taken from pg_catalog schema. If it even is not in the schema,
137  * warn and returns src. We cannot raise an error, since it will cause
138  * an infinit loop in error message sending.
139  *
140  * In the case of no coversion, src is returned.
141  *
142  * XXX We assume that storage for converted result is 4-to-1 growth in
143  * the worst case. The rate for currently supported encoding pares are within 3
144  * (SJIS JIS X0201 half width kanna -> UTF-8 is the worst case).
145  * So "4" should be enough for the moment.
146  */
147 unsigned char *
148 pg_do_encoding_conversion(unsigned char *src, int len,
149                                                   int src_encoding, int dest_encoding)
150 {
151         unsigned char *result;
152         Oid     proc;
153
154         if (!IsTransactionState())
155                 return src;
156         
157         if (src_encoding == dest_encoding)
158                 return src;
159
160         if (src_encoding == PG_SQL_ASCII || dest_encoding == PG_SQL_ASCII)
161                 return src;
162
163         proc = FindDefaultConversionProc(src_encoding, dest_encoding);
164         if (!OidIsValid(proc))
165         {
166                 elog(LOG, "default conversion proc for %s to %s not found",
167                          pg_encoding_to_char(src_encoding), pg_encoding_to_char(dest_encoding));
168                 return src;
169         }
170
171         /* XXX we shoud avoid throwing errors in OidFuctionCall. Otherwise
172          * we are going into inifinite loop!  So we have to make sure that
173          * the function exists before calling OidFunctionCall.
174          */
175         if (!SearchSysCacheExists(PROCOID,
176                                                          ObjectIdGetDatum(proc),
177                                                          0, 0, 0))
178         {
179                 elog(LOG, "default conversion proc %u for %s to %s not found in pg_proc",
180                          proc,
181                          pg_encoding_to_char(src_encoding), pg_encoding_to_char(dest_encoding));
182                 return src;
183         }
184
185         result = palloc(len * 4 + 1);
186
187         OidFunctionCall5(proc,
188                                          Int32GetDatum(src_encoding),
189                                          Int32GetDatum(dest_encoding),
190                                          CStringGetDatum(src),
191                                          CStringGetDatum(result),
192                                          Int32GetDatum(len));
193         return result;
194 }
195
196 /*
197  * Convert string using encoding_nanme. We assume that string's
198  * encoding is same as DB encoding.
199  *
200  * TEXT convert(TEXT string, NAME encoding_name) */
201 Datum
202 pg_convert(PG_FUNCTION_ARGS)
203 {
204         Datum   string = PG_GETARG_DATUM(0);
205         Datum   dest_encoding_name = PG_GETARG_DATUM(1);
206         Datum   src_encoding_name = DirectFunctionCall1(
207             namein, CStringGetDatum(DatabaseEncoding->name));
208         Datum   result;
209
210         result = DirectFunctionCall3(
211             pg_convert2, string, src_encoding_name, dest_encoding_name);
212
213         /* free memory allocated by namein */
214         pfree((void *)src_encoding_name);
215
216         PG_RETURN_TEXT_P(result);
217 }
218
219 /*
220  * Convert string using encoding_nanme.
221  *
222  * TEXT convert2(TEXT string, NAME src_encoding_name, NAME dest_encoding_name)
223  */
224 Datum
225 pg_convert2(PG_FUNCTION_ARGS)
226 {
227         text       *string = PG_GETARG_TEXT_P(0);
228         char       *src_encoding_name = NameStr(*PG_GETARG_NAME(1));
229         int                     src_encoding = pg_char_to_encoding(src_encoding_name);
230         char       *dest_encoding_name = NameStr(*PG_GETARG_NAME(2));
231         int                     dest_encoding = pg_char_to_encoding(dest_encoding_name);
232         unsigned char *result;
233         text       *retval;
234         unsigned char *str;
235         int len;
236
237         if (src_encoding < 0)
238                 elog(ERROR, "Invalid source encoding name %s", src_encoding_name);
239         if (dest_encoding < 0)
240                 elog(ERROR, "Invalid destination encoding name %s", dest_encoding_name);
241
242         /* make sure that source string is null terminated */
243         len = VARSIZE(string) - VARHDRSZ;
244         str = palloc(len + 1);
245         memcpy(str, VARDATA(string), len);
246         *(str + len) = '\0';
247
248         result = pg_do_encoding_conversion(str, len, src_encoding, dest_encoding);
249         if (result == NULL)
250                 elog(ERROR, "Encoding conversion failed");
251
252         /* build text data type structre. we cannot use textin() here,
253            since textin assumes that input string encoding is same as
254            database encoding.  */
255         len = strlen(result) + VARHDRSZ;
256         retval = palloc(len);
257         VARATT_SIZEP(retval) = len;
258         memcpy(VARDATA(retval), result, len - VARHDRSZ);
259
260         if (result != str)
261                 pfree(result);
262         pfree(str);
263
264         /* free memory if allocated by the toaster */
265         PG_FREE_IF_COPY(string, 0);
266
267         PG_RETURN_TEXT_P(retval);
268 }
269
270 /*
271  * convert client encoding to server encoding.
272  */
273 unsigned char *
274 pg_client_to_server(unsigned char *s, int len)
275 {
276         Assert(DatabaseEncoding);
277         Assert(ClientEncoding);
278
279         if (ClientEncoding->encoding == DatabaseEncoding->encoding)
280                 return s;
281
282         return perform_default_encoding_conversion(s, len, true);
283 }
284
285 /*
286  * convert server encoding to client encoding.
287  */
288 unsigned char *
289 pg_server_to_client(unsigned char *s, int len)
290 {
291         Assert(DatabaseEncoding);
292         Assert(ClientEncoding);
293
294         if (ClientEncoding->encoding == DatabaseEncoding->encoding)
295                 return s;
296
297         return perform_default_encoding_conversion(s, len, false);
298 }
299
300 /*
301  *  Perform default encoding conversion using cached FmgrInfo. Since
302  *  this function does not access database at all, it is safe to call
303  *  outside transactions. Explicit setting client encoding required
304  *  before calling this function. Otherwise no conversion is
305  *  performed.
306 */
307 static unsigned char *
308 perform_default_encoding_conversion(unsigned char *src, int len, bool is_client_to_server)
309 {
310         unsigned char *result;
311         int src_encoding, dest_encoding;
312         FmgrInfo *flinfo;
313
314         if (is_client_to_server)
315         {
316                 src_encoding = ClientEncoding->encoding;
317                 dest_encoding = DatabaseEncoding->encoding;
318                 flinfo = ToServerConvPorc;
319         }
320         else
321         {
322                 src_encoding = DatabaseEncoding->encoding;
323                 dest_encoding = ClientEncoding->encoding;
324                 flinfo = ToClientConvPorc;
325         }
326
327         if (flinfo == NULL)
328                 return src;
329
330         if (src_encoding == dest_encoding)
331                 return src;
332
333         if (src_encoding == PG_SQL_ASCII || dest_encoding == PG_SQL_ASCII)
334                 return src;
335
336         result = palloc(len * 4 + 1);
337
338         FunctionCall5(flinfo,
339                                   Int32GetDatum(src_encoding),
340                                   Int32GetDatum(dest_encoding),
341                                   CStringGetDatum(src),
342                                   CStringGetDatum(result),
343                                   Int32GetDatum(len));
344         return result;
345 }
346
347 /* convert a multi-byte string to a wchar */
348 int
349 pg_mb2wchar(const unsigned char *from, pg_wchar *to)
350 {
351         return (*pg_wchar_table[DatabaseEncoding->encoding].mb2wchar_with_len) (from, to, strlen(from));
352 }
353
354 /* convert a multi-byte string to a wchar with a limited length */
355 int
356 pg_mb2wchar_with_len(const unsigned char *from, pg_wchar *to, int len)
357 {
358         return (*pg_wchar_table[DatabaseEncoding->encoding].mb2wchar_with_len) (from, to, len);
359 }
360
361 /* returns the byte length of a multi-byte word */
362 int
363 pg_mblen(const unsigned char *mbstr)
364 {
365         return ((*pg_wchar_table[DatabaseEncoding->encoding].mblen) (mbstr));
366 }
367
368 /* returns the length (counted as a wchar) of a multi-byte string */
369 int
370 pg_mbstrlen(const unsigned char *mbstr)
371 {
372         int                     len = 0;
373
374         while (*mbstr)
375         {
376                 mbstr += pg_mblen(mbstr);
377                 len++;
378         }
379         return (len);
380 }
381
382 /* returns the length (counted as a wchar) of a multi-byte string
383    (not necessarily  NULL terminated) */
384 int
385 pg_mbstrlen_with_len(const unsigned char *mbstr, int limit)
386 {
387         int                     len = 0;
388         int                     l;
389
390         while (limit > 0 && *mbstr)
391         {
392                 l = pg_mblen(mbstr);
393                 limit -= l;
394                 mbstr += l;
395                 len++;
396         }
397         return (len);
398 }
399
400 /*
401  * returns the byte length of a multi-byte string
402  * (not necessarily  NULL terminated)
403  * that is no longer than limit.
404  * this function does not break multi-byte word boundary.
405  */
406 int
407 pg_mbcliplen(const unsigned char *mbstr, int len, int limit)
408 {
409         int                     clen = 0;
410         int                     l;
411
412         while (len > 0 && *mbstr)
413         {
414                 l = pg_mblen(mbstr);
415                 if ((clen + l) > limit)
416                         break;
417                 clen += l;
418                 if (clen == limit)
419                         break;
420                 len -= l;
421                 mbstr += l;
422         }
423         return (clen);
424 }
425
426 /*
427  * Similar to pg_mbcliplen but the limit parameter specifies the
428  * character length, not the byte length.  */
429 int
430 pg_mbcharcliplen(const unsigned char *mbstr, int len, int limit)
431 {
432         int                     clen = 0;
433         int                     nch = 0;
434         int                     l;
435
436         while (len > 0 && *mbstr)
437         {
438                 l = pg_mblen(mbstr);
439                 nch++;
440                 if (nch > limit)
441                         break;
442                 clen += l;
443                 len -= l;
444                 mbstr += l;
445         }
446         return (clen);
447 }
448
449 void
450 SetDatabaseEncoding(int encoding)
451 {
452         if (!PG_VALID_BE_ENCODING(encoding))
453                 elog(ERROR, "SetDatabaseEncoding(): invalid database encoding");
454
455         DatabaseEncoding = &pg_enc2name_tbl[encoding];
456         Assert(DatabaseEncoding->encoding == encoding);
457 }
458
459 void
460 SetDefaultClientEncoding()
461 {
462         ClientEncoding = &pg_enc2name_tbl[GetDatabaseEncoding()];
463 }
464
465 int
466 GetDatabaseEncoding(void)
467 {
468         Assert(DatabaseEncoding);
469         return (DatabaseEncoding->encoding);
470 }
471
472 const char *
473 GetDatabaseEncodingName(void)
474 {
475         Assert(DatabaseEncoding);
476         return (DatabaseEncoding->name);
477 }
478
479 Datum
480 getdatabaseencoding(PG_FUNCTION_ARGS)
481 {
482         Assert(DatabaseEncoding);
483         return DirectFunctionCall1(namein, CStringGetDatum(DatabaseEncoding->name));
484 }
485
486 Datum
487 pg_client_encoding(PG_FUNCTION_ARGS)
488 {
489         Assert(ClientEncoding);
490         return DirectFunctionCall1(namein, CStringGetDatum(ClientEncoding->name));
491 }