]> granicus.if.org Git - postgresql/blob - src/backend/utils/mb/mbutils.c
More minor cosmetic improvements:
[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.48 2004/10/13 01:25:12 neilc 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 unsigned char *perform_default_encoding_conversion(unsigned char *src,
47                                                                         int len, bool is_client_to_server);
48 static int      cliplen(const unsigned 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,
111          * so 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
115          * anyway.
116          */
117         if (!IsTransactionState())
118                 return -1;
119
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;
131
132         /*
133          * Done if not wanting to actually apply setting.
134          */
135         if (!doit)
136                 return 0;
137
138         /*
139          * load the fmgr info into TopMemoryContext so that it survives
140          * outside transaction.
141          */
142         oldcontext = MemoryContextSwitchTo(TopMemoryContext);
143         to_server = palloc(sizeof(FmgrInfo));
144         to_client = palloc(sizeof(FmgrInfo));
145         fmgr_info(to_server_proc, to_server);
146         fmgr_info(to_client_proc, to_client);
147         MemoryContextSwitchTo(oldcontext);
148
149         ClientEncoding = &pg_enc2name_tbl[encoding];
150
151         if (ToServerConvProc != NULL)
152         {
153                 if (ToServerConvProc->fn_extra)
154                         pfree(ToServerConvProc->fn_extra);
155                 pfree(ToServerConvProc);
156         }
157         ToServerConvProc = to_server;
158
159         if (ToClientConvProc != NULL)
160         {
161                 if (ToClientConvProc->fn_extra)
162                         pfree(ToClientConvProc->fn_extra);
163                 pfree(ToClientConvProc);
164         }
165         ToClientConvProc = to_client;
166
167         return 0;
168 }
169
170 /*
171  * Initialize client encoding if necessary.
172  *              called from InitPostgres() once during backend starting up.
173  */
174 void
175 InitializeClientEncoding(void)
176 {
177         Assert(!backend_startup_complete);
178         backend_startup_complete = true;
179
180         if (SetClientEncoding(pending_client_encoding, true) < 0)
181         {
182                 /*
183                  * Oops, the requested conversion is not available. We couldn't
184                  * fail before, but we can now.
185                  */
186                 ereport(FATAL,
187                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
188                                  errmsg("conversion between %s and %s is not supported",
189                                                 pg_enc2name_tbl[pending_client_encoding].name,
190                                                 GetDatabaseEncodingName())));
191         }
192 }
193
194 /*
195  * returns the current client encoding */
196 int
197 pg_get_client_encoding(void)
198 {
199         Assert(ClientEncoding);
200         return (ClientEncoding->encoding);
201 }
202
203 /*
204  * returns the current client encoding name
205  */
206 const char *
207 pg_get_client_encoding_name(void)
208 {
209         Assert(ClientEncoding);
210         return (ClientEncoding->name);
211 }
212
213 /*
214  * Apply encoding conversion on src and return it. The encoding
215  * conversion function is chosen from the pg_conversion system catalog
216  * marked as "default". If it is not found in the schema search path,
217  * it's taken from pg_catalog schema. If it even is not in the schema,
218  * warn and returns src. We cannot raise an error, since it will cause
219  * an infinit loop in error message sending.
220  *
221  * In the case of no conversion, src is returned.
222  *
223  * XXX We assume that storage for converted result is 4-to-1 growth in
224  * the worst case. The rate for currently supported encoding pares are within 3
225  * (SJIS JIS X0201 half width kanna -> UTF-8 is the worst case).
226  * So "4" should be enough for the moment.
227  */
228 unsigned char *
229 pg_do_encoding_conversion(unsigned char *src, int len,
230                                                   int src_encoding, int dest_encoding)
231 {
232         unsigned char *result;
233         Oid                     proc;
234
235         if (!IsTransactionState())
236                 return src;
237
238         if (src_encoding == dest_encoding)
239                 return src;
240
241         if (src_encoding == PG_SQL_ASCII || dest_encoding == PG_SQL_ASCII)
242                 return src;
243
244         if (len <= 0)
245                 return src;
246
247         proc = FindDefaultConversionProc(src_encoding, dest_encoding);
248         if (!OidIsValid(proc))
249         {
250                 ereport(LOG,
251                                 (errcode(ERRCODE_UNDEFINED_FUNCTION),
252                                  errmsg("default conversion function for encoding \"%s\" to \"%s\" does not exist",
253                                                 pg_encoding_to_char(src_encoding),
254                                                 pg_encoding_to_char(dest_encoding))));
255                 return src;
256         }
257
258         /*
259          * XXX we should avoid throwing errors in OidFunctionCall. Otherwise
260          * we are going into infinite loop!  So we have to make sure that the
261          * function exists before calling OidFunctionCall.
262          */
263         if (!SearchSysCacheExists(PROCOID,
264                                                           ObjectIdGetDatum(proc),
265                                                           0, 0, 0))
266         {
267                 elog(LOG, "cache lookup failed for function %u", proc);
268                 return src;
269         }
270
271         result = palloc(len * 4 + 1);
272
273         OidFunctionCall5(proc,
274                                          Int32GetDatum(src_encoding),
275                                          Int32GetDatum(dest_encoding),
276                                          CStringGetDatum(src),
277                                          CStringGetDatum(result),
278                                          Int32GetDatum(len));
279         return result;
280 }
281
282 /*
283  * Convert string using encoding_nanme. We assume that string's
284  * encoding is same as DB encoding.
285  *
286  * TEXT convert(TEXT string, NAME encoding_name) */
287 Datum
288 pg_convert(PG_FUNCTION_ARGS)
289 {
290         Datum           string = PG_GETARG_DATUM(0);
291         Datum           dest_encoding_name = PG_GETARG_DATUM(1);
292         Datum           src_encoding_name = DirectFunctionCall1(
293                                                 namein, CStringGetDatum(DatabaseEncoding->name));
294         Datum           result;
295
296         result = DirectFunctionCall3(
297                          pg_convert2, string, src_encoding_name, dest_encoding_name);
298
299         /* free memory allocated by namein */
300         pfree((void *) src_encoding_name);
301
302         PG_RETURN_TEXT_P(result);
303 }
304
305 /*
306  * Convert string using encoding_nanme.
307  *
308  * TEXT convert2(TEXT string, NAME src_encoding_name, NAME dest_encoding_name)
309  */
310 Datum
311 pg_convert2(PG_FUNCTION_ARGS)
312 {
313         text       *string = PG_GETARG_TEXT_P(0);
314         char       *src_encoding_name = NameStr(*PG_GETARG_NAME(1));
315         int                     src_encoding = pg_char_to_encoding(src_encoding_name);
316         char       *dest_encoding_name = NameStr(*PG_GETARG_NAME(2));
317         int                     dest_encoding = pg_char_to_encoding(dest_encoding_name);
318         unsigned char *result;
319         text       *retval;
320         unsigned char *str;
321         int                     len;
322
323         if (src_encoding < 0)
324                 ereport(ERROR,
325                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
326                                  errmsg("invalid source encoding name \"%s\"",
327                                                 src_encoding_name)));
328         if (dest_encoding < 0)
329                 ereport(ERROR,
330                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
331                                  errmsg("invalid destination encoding name \"%s\"",
332                                                 dest_encoding_name)));
333
334         /* make sure that source string is null terminated */
335         len = VARSIZE(string) - VARHDRSZ;
336         str = palloc(len + 1);
337         memcpy(str, VARDATA(string), len);
338         *(str + len) = '\0';
339
340         result = pg_do_encoding_conversion(str, len, src_encoding, dest_encoding);
341         if (result == NULL)
342                 elog(ERROR, "encoding conversion failed");
343
344         /*
345          * build text data type structure. we cannot use textin() here, since
346          * textin assumes that input string encoding is same as database
347          * encoding.
348          */
349         len = strlen(result) + VARHDRSZ;
350         retval = palloc(len);
351         VARATT_SIZEP(retval) = len;
352         memcpy(VARDATA(retval), result, len - VARHDRSZ);
353
354         if (result != str)
355                 pfree(result);
356         pfree(str);
357
358         /* free memory if allocated by the toaster */
359         PG_FREE_IF_COPY(string, 0);
360
361         PG_RETURN_TEXT_P(retval);
362 }
363
364 /*
365  * convert client encoding to server encoding.
366  */
367 unsigned char *
368 pg_client_to_server(unsigned char *s, int len)
369 {
370         Assert(DatabaseEncoding);
371         Assert(ClientEncoding);
372
373         if (ClientEncoding->encoding == DatabaseEncoding->encoding)
374                 return s;
375
376         return perform_default_encoding_conversion(s, len, true);
377 }
378
379 /*
380  * convert server encoding to client encoding.
381  */
382 unsigned char *
383 pg_server_to_client(unsigned char *s, int len)
384 {
385         Assert(DatabaseEncoding);
386         Assert(ClientEncoding);
387
388         if (ClientEncoding->encoding == DatabaseEncoding->encoding)
389                 return s;
390
391         return perform_default_encoding_conversion(s, len, false);
392 }
393
394 /*
395  *      Perform default encoding conversion using cached FmgrInfo. Since
396  *      this function does not access database at all, it is safe to call
397  *      outside transactions. Explicit setting client encoding required
398  *      before calling this function. Otherwise no conversion is
399  *      performed.
400 */
401 static unsigned char *
402 perform_default_encoding_conversion(unsigned char *src, int len, bool is_client_to_server)
403 {
404         unsigned char *result;
405         int                     src_encoding,
406                                 dest_encoding;
407         FmgrInfo   *flinfo;
408
409         if (len <= 0)
410                 return src;
411
412         if (is_client_to_server)
413         {
414                 src_encoding = ClientEncoding->encoding;
415                 dest_encoding = DatabaseEncoding->encoding;
416                 flinfo = ToServerConvProc;
417         }
418         else
419         {
420                 src_encoding = DatabaseEncoding->encoding;
421                 dest_encoding = ClientEncoding->encoding;
422                 flinfo = ToClientConvProc;
423         }
424
425         if (flinfo == NULL)
426                 return src;
427
428         if (src_encoding == dest_encoding)
429                 return src;
430
431         if (src_encoding == PG_SQL_ASCII || dest_encoding == PG_SQL_ASCII)
432                 return src;
433
434         result = palloc(len * 4 + 1);
435
436         FunctionCall5(flinfo,
437                                   Int32GetDatum(src_encoding),
438                                   Int32GetDatum(dest_encoding),
439                                   CStringGetDatum(src),
440                                   CStringGetDatum(result),
441                                   Int32GetDatum(len));
442         return result;
443 }
444
445 /* convert a multibyte string to a wchar */
446 int
447 pg_mb2wchar(const unsigned char *from, pg_wchar *to)
448 {
449         return (*pg_wchar_table[DatabaseEncoding->encoding].mb2wchar_with_len) (from, to, strlen(from));
450 }
451
452 /* convert a multibyte string to a wchar with a limited length */
453 int
454 pg_mb2wchar_with_len(const unsigned char *from, pg_wchar *to, int len)
455 {
456         return (*pg_wchar_table[DatabaseEncoding->encoding].mb2wchar_with_len) (from, to, len);
457 }
458
459 /* returns the byte length of a multibyte word */
460 int
461 pg_mblen(const unsigned char *mbstr)
462 {
463         return ((*pg_wchar_table[DatabaseEncoding->encoding].mblen) (mbstr));
464 }
465
466 /* returns the display length of a multibyte word */
467 int
468 pg_dsplen(const unsigned char *mbstr)
469 {
470         return ((*pg_wchar_table[DatabaseEncoding->encoding].dsplen) (mbstr));
471 }
472
473 /* returns the length (counted as a wchar) of a multibyte string */
474 int
475 pg_mbstrlen(const unsigned char *mbstr)
476 {
477         int                     len = 0;
478
479         /* optimization for single byte encoding */
480         if (pg_database_encoding_max_length() == 1)
481                 return strlen((char *) mbstr);
482
483         while (*mbstr)
484         {
485                 mbstr += pg_mblen(mbstr);
486                 len++;
487         }
488         return (len);
489 }
490
491 /* returns the length (counted as a wchar) of a multibyte string
492    (not necessarily  NULL terminated) */
493 int
494 pg_mbstrlen_with_len(const unsigned char *mbstr, int limit)
495 {
496         int                     len = 0;
497         int                     l;
498
499         while (limit > 0 && *mbstr)
500         {
501                 l = pg_mblen(mbstr);
502                 limit -= l;
503                 mbstr += l;
504                 len++;
505         }
506         return (len);
507 }
508
509 /*
510  * returns the byte length of a multibyte string
511  * (not necessarily  NULL terminated)
512  * that is no longer than limit.
513  * this function does not break multibyte word boundary.
514  */
515 int
516 pg_mbcliplen(const unsigned char *mbstr, int len, int limit)
517 {
518         int                     clen = 0;
519         int                     l;
520
521         /* optimization for single byte encoding */
522         if (pg_database_encoding_max_length() == 1)
523                 return cliplen(mbstr, len, limit);
524
525         while (len > 0 && *mbstr)
526         {
527                 l = pg_mblen(mbstr);
528                 if ((clen + l) > limit)
529                         break;
530                 clen += l;
531                 if (clen == limit)
532                         break;
533                 len -= l;
534                 mbstr += l;
535         }
536         return (clen);
537 }
538
539 /*
540  * Similar to pg_mbcliplen except the limit parameter specifies the
541  * character length, not the byte length.  */
542 int
543 pg_mbcharcliplen(const unsigned char *mbstr, int len, int limit)
544 {
545         int                     clen = 0;
546         int                     nch = 0;
547         int                     l;
548
549         /* optimization for single byte encoding */
550         if (pg_database_encoding_max_length() == 1)
551                 return cliplen(mbstr, len, limit);
552
553         while (len > 0 && *mbstr)
554         {
555                 l = pg_mblen(mbstr);
556                 nch++;
557                 if (nch > limit)
558                         break;
559                 clen += l;
560                 len -= l;
561                 mbstr += l;
562         }
563         return (clen);
564 }
565
566 void
567 SetDatabaseEncoding(int encoding)
568 {
569         if (!PG_VALID_BE_ENCODING(encoding))
570                 elog(ERROR, "invalid database encoding");
571
572         DatabaseEncoding = &pg_enc2name_tbl[encoding];
573         Assert(DatabaseEncoding->encoding == encoding);
574 }
575
576 void
577 SetDefaultClientEncoding(void)
578 {
579         ClientEncoding = &pg_enc2name_tbl[GetDatabaseEncoding()];
580 }
581
582 int
583 GetDatabaseEncoding(void)
584 {
585         Assert(DatabaseEncoding);
586         return (DatabaseEncoding->encoding);
587 }
588
589 const char *
590 GetDatabaseEncodingName(void)
591 {
592         Assert(DatabaseEncoding);
593         return (DatabaseEncoding->name);
594 }
595
596 Datum
597 getdatabaseencoding(PG_FUNCTION_ARGS)
598 {
599         Assert(DatabaseEncoding);
600         return DirectFunctionCall1(namein, CStringGetDatum(DatabaseEncoding->name));
601 }
602
603 Datum
604 pg_client_encoding(PG_FUNCTION_ARGS)
605 {
606         Assert(ClientEncoding);
607         return DirectFunctionCall1(namein, CStringGetDatum(ClientEncoding->name));
608 }
609
610 static int
611 cliplen(const unsigned char *str, int len, int limit)
612 {
613         int                     l = 0;
614         const unsigned char *s;
615
616         for (s = str; *s; s++, l++)
617         {
618                 if (l >= len || l >= limit)
619                         return l;
620         }
621         return (s - str);
622 }