Load and keep conversion function info when SET CLIENT_ENCODING TO is
authorTatsuo Ishii
Thu, 8 Aug 2002 06:35:26 +0000 (06:35 +0000)
committerTatsuo Ishii
Thu, 8 Aug 2002 06:35:26 +0000 (06:35 +0000)
executed to prevent database access while performing encoding
conversion.

src/backend/utils/mb/mbutils.c

index 1ebe13ea77ea38a85067e1e6b3b8c943253922db..9b1b2021c4d11adc73474113fae5a9fd35f840a0 100644 (file)
@@ -3,13 +3,14 @@
  * client encoding and server internal encoding.
  * (currently mule internal code (mic) is used)
  * Tatsuo Ishii
- * $Id: mbutils.c,v 1.29 2002/07/25 10:07:12 ishii Exp $
+ * $Id: mbutils.c,v 1.30 2002/08/08 06:35:26 ishii Exp $
  */
 #include "postgres.h"
 #include "access/xact.h"
 #include "miscadmin.h"
 #include "mb/pg_wchar.h"
 #include "utils/builtins.h"
+#include "utils/memutils.h"
 #include "utils/syscache.h"
 #include "catalog/namespace.h"
 
@@ -22,13 +23,30 @@ static pg_enc2name *ClientEncoding = &pg_enc2name_tbl[PG_SQL_ASCII];
 static pg_enc2name *DatabaseEncoding = &pg_enc2name_tbl[PG_SQL_ASCII];
 
 /*
- * set the client encoding. if encoding conversion between
- * client/server encoding is not supported, returns -1
+ * Caches for conversion function info. Note that Fcinfo.flinfo is
+ * allocated in TopMemoryContext so that it survives outside
+ * transactions. See SetClientEncoding() for more details.
  */
+static     FmgrInfo        *ToServerConvPorc = NULL;
+static     FmgrInfo        *ToClientConvPorc = NULL;
+
+/* Internal functions */
+static unsigned char *
+perform_default_encoding_conversion(unsigned char *src, int len, bool is_client_to_server);
+
+/*
+ * Set the client encoding and save fmgrinfo for the converion
+ * function if necessary. if encoding conversion between client/server
+ * encoding is not supported, returns -1
+*/
 int
 SetClientEncoding(int encoding, bool doit)
 {
    int         current_server_encoding;
+   Oid         to_server_proc, to_client_proc;
+   FmgrInfo    *to_server = NULL;
+   FmgrInfo    *to_client = NULL;
+   MemoryContext oldcontext;
 
    current_server_encoding = GetDatabaseEncoding();
 
@@ -46,18 +64,35 @@ SetClientEncoding(int encoding, bool doit)
     * bootstrap or initprocessing mode since namespace functions will
     * not work.
     */
-   if (IsNormalProcessingMode())
+   if (IsTransactionState())
    {
-       if (!OidIsValid(FindDefaultConversionProc(encoding, current_server_encoding)) ||
-           !OidIsValid(FindDefaultConversionProc(current_server_encoding, encoding)))
-           return (-1);
+       to_server_proc = FindDefaultConversionProc(encoding, current_server_encoding);
+       to_client_proc = FindDefaultConversionProc(current_server_encoding, encoding);
+
+       if (!OidIsValid(to_server_proc) || !OidIsValid(to_client_proc))
+           return -1;
+
+       /*
+        * load the fmgr info into TopMemoryContext so that it
+        * survives outside transaction.
+        */
+       oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+       to_server = palloc(sizeof(FmgrInfo));
+       to_client = palloc(sizeof(FmgrInfo));
+       fmgr_info(to_server_proc, to_server);
+       fmgr_info(to_client_proc, to_client);
+       MemoryContextSwitchTo(oldcontext);
    }
 
    if (!doit)
        return 0;
 
-   ClientEncoding = &pg_enc2name_tbl[encoding];
-
+   if (IsTransactionState())
+   {
+       ClientEncoding = &pg_enc2name_tbl[encoding];
+       ToServerConvPorc = to_server;
+       ToClientConvPorc = to_client;
+   }
    return 0;
 }
 
@@ -95,7 +130,6 @@ pg_get_client_encoding_name(void)
  * (SJIS JIS X0201 half width kanna -> UTF-8 is the worst case).
  * So "4" should be enough for the moment.
  */
-
 unsigned char *
 pg_do_encoding_conversion(unsigned char *src, int len,
                          int src_encoding, int dest_encoding)
@@ -231,8 +265,7 @@ pg_client_to_server(unsigned char *s, int len)
    if (ClientEncoding->encoding == DatabaseEncoding->encoding)
        return s;
 
-   return pg_do_encoding_conversion(s, len, ClientEncoding->encoding, 
-                                    DatabaseEncoding->encoding);
+   return perform_default_encoding_conversion(s, len, true);
 }
 
 /*
@@ -247,8 +280,54 @@ pg_server_to_client(unsigned char *s, int len)
    if (ClientEncoding->encoding == DatabaseEncoding->encoding)
        return s;
 
-   return pg_do_encoding_conversion(s, len, DatabaseEncoding->encoding,
-                                    ClientEncoding->encoding);
+   return perform_default_encoding_conversion(s, len, false);
+}
+
+/*
+ *  Perform default encoding conversion using cached FmgrInfo. Since
+ *  this function does not access database at all, it is safe to call
+ *  outside transactions. Explicit setting client encoding required
+ *  before calling this function. Otherwise no conversion is
+ *  performed.
+*/
+static unsigned char *
+perform_default_encoding_conversion(unsigned char *src, int len, bool is_client_to_server)
+{
+   unsigned char *result;
+   int src_encoding, dest_encoding;
+   FmgrInfo *flinfo;
+
+   if (is_client_to_server)
+   {
+       src_encoding = ClientEncoding->encoding;
+       dest_encoding = DatabaseEncoding->encoding;
+       flinfo = ToServerConvPorc;
+   }
+   else
+   {
+       src_encoding = DatabaseEncoding->encoding;
+       dest_encoding = ClientEncoding->encoding;
+       flinfo = ToClientConvPorc;
+   }
+
+   if (flinfo == NULL)
+       return src;
+
+   if (src_encoding == dest_encoding)
+       return src;
+
+   if (src_encoding == PG_SQL_ASCII || dest_encoding == PG_SQL_ASCII)
+       return src;
+
+   result = palloc(len * 4 + 1);
+
+   FunctionCall5(flinfo,
+                 Int32GetDatum(src_encoding),
+                 Int32GetDatum(dest_encoding),
+                 CStringGetDatum(src),
+                 CStringGetDatum(result),
+                 Int32GetDatum(len));
+   return result;
 }
 
 /* convert a multi-byte string to a wchar */