]> granicus.if.org Git - postgresql/blob - src/backend/utils/mb/mbutils.c
f8dc7a319223eb2dac9dda28c1724e8608d644f8
[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  *
7  * $PostgreSQL: pgsql/src/backend/utils/mb/mbutils.c,v 1.52 2005/10/15 02:49:33 momjian Exp $
8  */
9 #include "postgres.h"
10
11 #include "access/xact.h"
12 #include "miscadmin.h"
13 #include "mb/pg_wchar.h"
14 #include "utils/builtins.h"
15 #include "utils/memutils.h"
16 #include "utils/syscache.h"
17 #include "catalog/namespace.h"
18
19 /*
20  * We handle for actual FE and BE encoding setting encoding-identificator
21  * and encoding-name too. It prevent searching and conversion from encoding
22  * to encoding name in getdatabaseencoding() and other routines.
23  */
24 static pg_enc2name *ClientEncoding = &pg_enc2name_tbl[PG_SQL_ASCII];
25 static pg_enc2name *DatabaseEncoding = &pg_enc2name_tbl[PG_SQL_ASCII];
26
27 /*
28  * Caches for conversion function info. Note that these values are
29  * allocated in TopMemoryContext so that they survive across
30  * transactions. See SetClientEncoding() for more details.
31  */
32 static FmgrInfo *ToServerConvProc = NULL;
33 static FmgrInfo *ToClientConvProc = NULL;
34
35 /*
36  * During backend startup we can't set client encoding because we (a)
37  * can't look up the conversion functions, and (b) may not know the database
38  * encoding yet either.  So SetClientEncoding() just accepts anything and
39  * remembers it for InitializeClientEncoding() to apply later.
40  */
41 static bool backend_startup_complete = false;
42 static int      pending_client_encoding = PG_SQL_ASCII;
43
44
45 /* Internal functions */
46 static char *perform_default_encoding_conversion(const char *src,
47                                                                         int len, bool is_client_to_server);
48 static int      cliplen(const char *str, int len, int limit);
49
50
51 /*
52  * Set the client encoding and save fmgrinfo for the conversion
53  * function if necessary.  Returns 0 if okay, -1 if not (bad encoding
54  * or can't support conversion)
55  */
56 int
57 SetClientEncoding(int encoding, bool doit)
58 {
59         int                     current_server_encoding;
60         Oid                     to_server_proc,
61                                 to_client_proc;
62         FmgrInfo   *to_server;
63         FmgrInfo   *to_client;
64         MemoryContext oldcontext;
65
66         if (!PG_VALID_FE_ENCODING(encoding))
67                 return (-1);
68
69         /* Can't do anything during startup, per notes above */
70         if (!backend_startup_complete)
71         {
72                 if (doit)
73                         pending_client_encoding = encoding;
74                 return 0;
75         }
76
77         current_server_encoding = GetDatabaseEncoding();
78
79         /*
80          * Check for cases that require no conversion function.
81          */
82         if (current_server_encoding == encoding ||
83                 (current_server_encoding == PG_SQL_ASCII ||
84                  encoding == PG_SQL_ASCII))
85         {
86                 if (doit)
87                 {
88                         ClientEncoding = &pg_enc2name_tbl[encoding];
89
90                         if (ToServerConvProc != NULL)
91                         {
92                                 if (ToServerConvProc->fn_extra)
93                                         pfree(ToServerConvProc->fn_extra);
94                                 pfree(ToServerConvProc);
95                         }
96                         ToServerConvProc = NULL;
97
98                         if (ToClientConvProc != NULL)
99                         {
100                                 if (ToClientConvProc->fn_extra)
101                                         pfree(ToClientConvProc->fn_extra);
102                                 pfree(ToClientConvProc);
103                         }
104                         ToClientConvProc = NULL;
105                 }
106                 return 0;
107         }
108
109         /*
110          * If we're not inside a transaction then we can't do catalog lookups, so
111          * fail.  After backend startup, this could only happen if we are
112          * re-reading postgresql.conf due to SIGHUP --- so basically this just
113          * constrains the ability to change client_encoding on the fly from
114          * postgresql.conf.  Which would probably be a stupid thing to do anyway.
115          */
116         if (!IsTransactionState())
117                 return -1;
118
119         /*
120          * Look up the conversion functions.
121          */
122         to_server_proc = FindDefaultConversionProc(encoding,
123                                                                                            current_server_encoding);
124         if (!OidIsValid(to_server_proc))
125                 return -1;
126         to_client_proc = FindDefaultConversionProc(current_server_encoding,
127                                                                                            encoding);
128         if (!OidIsValid(to_client_proc))
129                 return -1;
130
131         /*
132          * Done if not wanting to actually apply setting.
133          */
134         if (!doit)
135                 return 0;
136
137         /*
138          * load the fmgr info into TopMemoryContext so that it survives outside
139          * transaction.
140          */
141         oldcontext = MemoryContextSwitchTo(TopMemoryContext);
142         to_server = palloc(sizeof(FmgrInfo));
143         to_client = palloc(sizeof(FmgrInfo));
144         fmgr_info(to_server_proc, to_server);
145         fmgr_info(to_client_proc, to_client);
146         MemoryContextSwitchTo(oldcontext);
147
148         ClientEncoding = &pg_enc2name_tbl[encoding];
149
150         if (ToServerConvProc != NULL)
151         {
152                 if (ToServerConvProc->fn_extra)
153                         pfree(ToServerConvProc->fn_extra);
154                 pfree(ToServerConvProc);
155         }
156         ToServerConvProc = to_server;
157
158         if (ToClientConvProc != NULL)
159         {
160                 if (ToClientConvProc->fn_extra)
161                         pfree(ToClientConvProc->fn_extra);
162                 pfree(ToClientConvProc);
163         }
164         ToClientConvProc = to_client;
165
166         return 0;
167 }
168
169 /*
170  * Initialize client encoding if necessary.
171  *              called from InitPostgres() once during backend starting up.
172  */
173 void
174 InitializeClientEncoding(void)
175 {
176         Assert(!backend_startup_complete);
177         backend_startup_complete = true;
178
179         if (SetClientEncoding(pending_client_encoding, true) < 0)
180         {
181                 /*
182                  * Oops, the requested conversion is not available. We couldn't fail
183                  * before, but we can now.
184                  */
185                 ereport(FATAL,
186                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
187                                  errmsg("conversion between %s and %s is not supported",
188                                                 pg_enc2name_tbl[pending_client_encoding].name,
189                                                 GetDatabaseEncodingName())));
190         }
191 }
192
193 /*
194  * returns the current client encoding */
195 int
196 pg_get_client_encoding(void)
197 {
198         Assert(ClientEncoding);
199         return (ClientEncoding->encoding);
200 }
201
202 /*
203  * returns the current client encoding name
204  */
205 const char *
206 pg_get_client_encoding_name(void)
207 {
208         Assert(ClientEncoding);
209         return (ClientEncoding->name);
210 }
211
212 /*
213  * Apply encoding conversion on src and return it. The encoding
214  * conversion function is chosen from the pg_conversion system catalog
215  * marked as "default". If it is not found in the schema search path,
216  * it's taken from pg_catalog schema. If it even is not in the schema,
217  * warn and returns src. We cannot raise an error, since it will cause
218  * an infinit loop in error message sending.
219  *
220  * In the case of no conversion, src is returned.
221  *
222  * XXX We assume that storage for converted result is 4-to-1 growth in
223  * the worst case. The rate for currently supported encoding pares are within 3
224  * (SJIS JIS X0201 half width kanna -> UTF8 is the worst case).
225  * So "4" should be enough for the moment.
226  */
227 unsigned char *
228 pg_do_encoding_conversion(unsigned char *src, int len,
229                                                   int src_encoding, int dest_encoding)
230 {
231         unsigned char *result;
232         Oid                     proc;
233
234         if (!IsTransactionState())
235                 return src;
236
237         if (src_encoding == dest_encoding)
238                 return src;
239
240         if (src_encoding == PG_SQL_ASCII || dest_encoding == PG_SQL_ASCII)
241                 return src;
242
243         if (len <= 0)
244                 return src;
245
246         proc = FindDefaultConversionProc(src_encoding, dest_encoding);
247         if (!OidIsValid(proc))
248         {
249                 ereport(LOG,
250                                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
251                                  errmsg("default conversion function for encoding \"%s\" to \"%s\" does not exist",
252                                                 pg_encoding_to_char(src_encoding),
253                                                 pg_encoding_to_char(dest_encoding))));
254                 return src;
255         }
256
257         /*
258          * XXX we should avoid throwing errors in OidFunctionCall. Otherwise we
259          * are going into infinite loop!  So we have to make sure that the
260          * function exists before calling OidFunctionCall.
261          */
262         if (!SearchSysCacheExists(PROCOID,
263                                                           ObjectIdGetDatum(proc),
264                                                           0, 0, 0))
265         {
266                 elog(LOG, "cache lookup failed for function %u", proc);
267                 return src;
268         }
269
270         result = palloc(len * 4 + 1);
271
272         OidFunctionCall5(proc,
273                                          Int32GetDatum(src_encoding),
274                                          Int32GetDatum(dest_encoding),
275                                          CStringGetDatum(src),
276                                          CStringGetDatum(result),
277                                          Int32GetDatum(len));
278         return result;
279 }
280
281 /*
282  * Convert string using encoding_nanme. We assume that string's
283  * encoding is same as DB encoding.
284  *
285  * TEXT convert(TEXT string, NAME encoding_name) */
286 Datum
287 pg_convert(PG_FUNCTION_ARGS)
288 {
289         Datum           string = PG_GETARG_DATUM(0);
290         Datum           dest_encoding_name = PG_GETARG_DATUM(1);
291         Datum           src_encoding_name = DirectFunctionCall1(
292                                                         namein, CStringGetDatum(DatabaseEncoding->name));
293         Datum           result;
294
295         result = DirectFunctionCall3(
296                                  pg_convert2, string, src_encoding_name, dest_encoding_name);
297
298         /* free memory allocated by namein */
299         pfree((void *) src_encoding_name);
300
301         PG_RETURN_TEXT_P(result);
302 }
303
304 /*
305  * Convert string using encoding_name.
306  *
307  * TEXT convert2(TEXT string, NAME src_encoding_name, NAME dest_encoding_name)
308  */
309 Datum
310 pg_convert2(PG_FUNCTION_ARGS)
311 {
312         text       *string = PG_GETARG_TEXT_P(0);
313         char       *src_encoding_name = NameStr(*PG_GETARG_NAME(1));
314         int                     src_encoding = pg_char_to_encoding(src_encoding_name);
315         char       *dest_encoding_name = NameStr(*PG_GETARG_NAME(2));
316         int                     dest_encoding = pg_char_to_encoding(dest_encoding_name);
317         unsigned char *result;
318         text       *retval;
319         unsigned char *str;
320         int                     len;
321
322         if (src_encoding < 0)
323                 ereport(ERROR,
324                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
325                                  errmsg("invalid source encoding name \"%s\"",
326                                                 src_encoding_name)));
327         if (dest_encoding < 0)
328                 ereport(ERROR,
329                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
330                                  errmsg("invalid destination encoding name \"%s\"",
331                                                 dest_encoding_name)));
332
333         /* make sure that source string is null terminated */
334         len = VARSIZE(string) - VARHDRSZ;
335         str = palloc(len + 1);
336         memcpy(str, VARDATA(string), len);
337         *(str + len) = '\0';
338
339         result = pg_do_encoding_conversion(str, len, src_encoding, dest_encoding);
340         if (result == NULL)
341                 elog(ERROR, "encoding conversion failed");
342
343         /*
344          * build text data type structure. we cannot use textin() here, since
345          * textin assumes that input string encoding is same as database encoding.
346          */
347         len = strlen((char *) result) + VARHDRSZ;
348         retval = palloc(len);
349         VARATT_SIZEP(retval) = len;
350         memcpy(VARDATA(retval), result, len - VARHDRSZ);
351
352         if (result != str)
353                 pfree(result);
354         pfree(str);
355
356         /* free memory if allocated by the toaster */
357         PG_FREE_IF_COPY(string, 0);
358
359         PG_RETURN_TEXT_P(retval);
360 }
361
362 /*
363  * convert client encoding to server encoding.
364  */
365 char *
366 pg_client_to_server(const char *s, int len)
367 {
368         Assert(DatabaseEncoding);
369         Assert(ClientEncoding);
370
371         if (ClientEncoding->encoding == DatabaseEncoding->encoding)
372                 return (char *) s;
373
374         return perform_default_encoding_conversion(s, len, true);
375 }
376
377 /*
378  * convert server encoding to client encoding.
379  */
380 char *
381 pg_server_to_client(const char *s, int len)
382 {
383         Assert(DatabaseEncoding);
384         Assert(ClientEncoding);
385
386         if (ClientEncoding->encoding == DatabaseEncoding->encoding)
387                 return (char *) s;
388
389         return perform_default_encoding_conversion(s, len, false);
390 }
391
392 /*
393  *      Perform default encoding conversion using cached FmgrInfo. Since
394  *      this function does not access database at all, it is safe to call
395  *      outside transactions. Explicit setting client encoding required
396  *      before calling this function. Otherwise no conversion is
397  *      performed.
398 */
399 static char *
400 perform_default_encoding_conversion(const char *src, int len, bool is_client_to_server)
401 {
402         char       *result;
403         int                     src_encoding,
404                                 dest_encoding;
405         FmgrInfo   *flinfo;
406
407         if (len <= 0)
408                 return (char *) src;
409
410         if (is_client_to_server)
411         {
412                 src_encoding = ClientEncoding->encoding;
413                 dest_encoding = DatabaseEncoding->encoding;
414                 flinfo = ToServerConvProc;
415         }
416         else
417         {
418                 src_encoding = DatabaseEncoding->encoding;
419                 dest_encoding = ClientEncoding->encoding;
420                 flinfo = ToClientConvProc;
421         }
422
423         if (flinfo == NULL)
424                 return (char *) src;
425
426         if (src_encoding == dest_encoding)
427                 return (char *) src;
428
429         if (src_encoding == PG_SQL_ASCII || dest_encoding == PG_SQL_ASCII)
430                 return (char *) src;
431
432         result = palloc(len * 4 + 1);
433
434         FunctionCall5(flinfo,
435                                   Int32GetDatum(src_encoding),
436                                   Int32GetDatum(dest_encoding),
437                                   CStringGetDatum(src),
438                                   CStringGetDatum(result),
439                                   Int32GetDatum(len));
440         return result;
441 }
442
443 /* convert a multibyte string to a wchar */
444 int
445 pg_mb2wchar(const char *from, pg_wchar *to)
446 {
447         return (*pg_wchar_table[DatabaseEncoding->encoding].mb2wchar_with_len) ((const unsigned char *) from, to, strlen(from));
448 }
449
450 /* convert a multibyte string to a wchar with a limited length */
451 int
452 pg_mb2wchar_with_len(const char *from, pg_wchar *to, int len)
453 {
454         return (*pg_wchar_table[DatabaseEncoding->encoding].mb2wchar_with_len) ((const unsigned char *) from, to, len);
455 }
456
457 /* returns the byte length of a multibyte word */
458 int
459 pg_mblen(const char *mbstr)
460 {
461         return ((*pg_wchar_table[DatabaseEncoding->encoding].mblen) ((const unsigned char *) mbstr));
462 }
463
464 /* returns the display length of a multibyte word */
465 int
466 pg_dsplen(const char *mbstr)
467 {
468         return ((*pg_wchar_table[DatabaseEncoding->encoding].dsplen) ((const unsigned char *) mbstr));
469 }
470
471 /* returns the length (counted in wchars) of a multibyte string */
472 int
473 pg_mbstrlen(const char *mbstr)
474 {
475         int                     len = 0;
476
477         /* optimization for single byte encoding */
478         if (pg_database_encoding_max_length() == 1)
479                 return strlen(mbstr);
480
481         while (*mbstr)
482         {
483                 mbstr += pg_mblen(mbstr);
484                 len++;
485         }
486         return (len);
487 }
488
489 /* returns the length (counted in wchars) of a multibyte string
490  * (not necessarily NULL terminated)
491  */
492 int
493 pg_mbstrlen_with_len(const char *mbstr, int limit)
494 {
495         int                     len = 0;
496
497         /* optimization for single byte encoding */
498         if (pg_database_encoding_max_length() == 1)
499                 return limit;
500
501         while (limit > 0 && *mbstr)
502         {
503                 int                     l = pg_mblen(mbstr);
504
505                 limit -= l;
506                 mbstr += l;
507                 len++;
508         }
509         return (len);
510 }
511
512 /*
513  * returns the byte length of a multibyte string
514  * (not necessarily  NULL terminated)
515  * that is no longer than limit.
516  * this function does not break multibyte word boundary.
517  */
518 int
519 pg_mbcliplen(const char *mbstr, int len, int limit)
520 {
521         int                     clen = 0;
522         int                     l;
523
524         /* optimization for single byte encoding */
525         if (pg_database_encoding_max_length() == 1)
526                 return cliplen(mbstr, len, limit);
527
528         while (len > 0 && *mbstr)
529         {
530                 l = pg_mblen(mbstr);
531                 if ((clen + l) > limit)
532                         break;
533                 clen += l;
534                 if (clen == limit)
535                         break;
536                 len -= l;
537                 mbstr += l;
538         }
539         return (clen);
540 }
541
542 /*
543  * Similar to pg_mbcliplen except the limit parameter specifies the
544  * character length, not the byte length.  */
545 int
546 pg_mbcharcliplen(const char *mbstr, int len, int limit)
547 {
548         int                     clen = 0;
549         int                     nch = 0;
550         int                     l;
551
552         /* optimization for single byte encoding */
553         if (pg_database_encoding_max_length() == 1)
554                 return cliplen(mbstr, len, limit);
555
556         while (len > 0 && *mbstr)
557         {
558                 l = pg_mblen(mbstr);
559                 nch++;
560                 if (nch > limit)
561                         break;
562                 clen += l;
563                 len -= l;
564                 mbstr += l;
565         }
566         return (clen);
567 }
568
569 void
570 SetDatabaseEncoding(int encoding)
571 {
572         if (!PG_VALID_BE_ENCODING(encoding))
573                 elog(ERROR, "invalid database encoding");
574
575         DatabaseEncoding = &pg_enc2name_tbl[encoding];
576         Assert(DatabaseEncoding->encoding == encoding);
577 }
578
579 void
580 SetDefaultClientEncoding(void)
581 {
582         ClientEncoding = &pg_enc2name_tbl[GetDatabaseEncoding()];
583 }
584
585 int
586 GetDatabaseEncoding(void)
587 {
588         Assert(DatabaseEncoding);
589         return (DatabaseEncoding->encoding);
590 }
591
592 const char *
593 GetDatabaseEncodingName(void)
594 {
595         Assert(DatabaseEncoding);
596         return (DatabaseEncoding->name);
597 }
598
599 Datum
600 getdatabaseencoding(PG_FUNCTION_ARGS)
601 {
602         Assert(DatabaseEncoding);
603         return DirectFunctionCall1(namein, CStringGetDatum(DatabaseEncoding->name));
604 }
605
606 Datum
607 pg_client_encoding(PG_FUNCTION_ARGS)
608 {
609         Assert(ClientEncoding);
610         return DirectFunctionCall1(namein, CStringGetDatum(ClientEncoding->name));
611 }
612
613 static int
614 cliplen(const char *str, int len, int limit)
615 {
616         int                     l = 0;
617         const char *s;
618
619         for (s = str; *s; s++, l++)
620         {
621                 if (l >= len || l >= limit)
622                         return l;
623         }
624         return (s - str);
625 }