2121#include "commands/trigger.h"
2222#include "executor/spi.h"
2323#include "fmgr.h"
24+ #include "mb/pg_wchar.h"
2425#include "miscadmin.h"
2526#include "nodes/makefuncs.h"
2627#include "parser/parse_type.h"
3334#include "utils/typcache.h"
3435
3536
37+ PG_MODULE_MAGIC ;
38+
3639#define HAVE_TCL_VERSION (maj ,min ) \
3740((TCL_MAJOR_VERSION > maj) || \
3841 (TCL_MAJOR_VERSION == maj && TCL_MINOR_VERSION >= min))
5154#undef TEXTDOMAIN
5255#define TEXTDOMAIN PG_TEXTDOMAIN("pltcl")
5356
54- #if defined(UNICODE_CONVERSION )
5557
56- #include "mb/pg_wchar.h"
58+ /*
59+ * Support for converting between UTF8 (which is what all strings going into
60+ * or out of Tcl should be) and the database encoding.
61+ *
62+ * If you just use utf_u2e() or utf_e2u() directly, they will leak some
63+ * palloc'd space when doing a conversion. This is not worth worrying about
64+ * if it only happens, say, once per PL/Tcl function call. If it does seem
65+ * worth worrying about, use the wrapper macros.
66+ */
5767
58- static unsigned char *
59- utf_u2e (unsigned char * src )
68+ static inline char *
69+ utf_u2e (const char * src )
6070{
61- return (unsignedchar * )pg_any_to_server ((char * )src ,
62- strlen (src ),
63- PG_UTF8 );
71+ return pg_any_to_server (src ,strlen (src ),PG_UTF8 );
6472}
6573
66- static unsigned char *
67- utf_e2u (unsigned char * src )
74+ static inline char *
75+ utf_e2u (const char * src )
6876{
69- return (unsignedchar * )pg_server_to_any ((char * )src ,
70- strlen (src ),
71- PG_UTF8 );
77+ return pg_server_to_any (src ,strlen (src ),PG_UTF8 );
7278}
7379
74- #define PLTCL_UTF
75- #define UTF_BEGIN do { \
76- unsigned char *_pltcl_utf_src; \
77- unsigned char *_pltcl_utf_dst
78- #define UTF_END if (_pltcl_utf_src!=_pltcl_utf_dst) \
79- pfree(_pltcl_utf_dst); } while (0)
80- #define UTF_U2E (x ) (_pltcl_utf_dst=utf_u2e(_pltcl_utf_src=(x)))
81- #define UTF_E2U (x ) (_pltcl_utf_dst=utf_e2u(_pltcl_utf_src=(x)))
82- #else /* !PLTCL_UTF */
83-
84- #define UTF_BEGIN
85- #define UTF_END
86- #define UTF_U2E (x ) (x)
87- #define UTF_E2U (x ) (x)
88- #endif /* PLTCL_UTF */
80+ #define UTF_BEGIN \
81+ do { \
82+ const char *_pltcl_utf_src = NULL; \
83+ char *_pltcl_utf_dst = NULL
8984
90- PG_MODULE_MAGIC ;
85+ #define UTF_END \
86+ if (_pltcl_utf_src != (const char *) _pltcl_utf_dst) \
87+ pfree(_pltcl_utf_dst); \
88+ } while (0)
89+
90+ #define UTF_U2E (x ) \
91+ (_pltcl_utf_dst = utf_u2e(_pltcl_utf_src = (x)))
92+
93+ #define UTF_E2U (x ) \
94+ (_pltcl_utf_dst = utf_e2u(_pltcl_utf_src = (x)))
9195
9296
9397/**********************************************************************
@@ -572,14 +576,10 @@ pltcl_init_load_unknown(Tcl_Interp *interp)
572576SPI_freetuptable (SPI_tuptable );
573577
574578if (tcl_rc != TCL_OK )
575- {
576- UTF_BEGIN ;
577579ereport (ERROR ,
578580(errcode (ERRCODE_EXTERNAL_ROUTINE_EXCEPTION ),
579581errmsg ("could not load module \"unknown\": %s" ,
580- UTF_U2E (Tcl_GetStringResult (interp )))));
581- UTF_END ;
582- }
582+ utf_u2e (Tcl_GetStringResult (interp )))));
583583
584584relation_close (pmrel ,AccessShareLock );
585585}
@@ -804,14 +804,10 @@ pltcl_func_handler(PG_FUNCTION_ARGS, bool pltrusted)
804804prodesc -> result_typioparam ,
805805-1 );
806806else
807- {
808- UTF_BEGIN ;
809807retval = InputFunctionCall (& prodesc -> result_in_func ,
810- UTF_U2E (( char * ) Tcl_GetStringResult (interp )),
808+ utf_u2e ( Tcl_GetStringResult (interp )),
811809prodesc -> result_typioparam ,
812810-1 );
813- UTF_END ;
814- }
815811
816812return retval ;
817813}
@@ -866,13 +862,13 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
866862
867863PG_TRY ();
868864{
869- /* The procedure name */
865+ /* The procedure name(note this is all ASCII, so no utf_e2u) */
870866Tcl_ListObjAppendElement (NULL ,tcl_cmd ,
871867Tcl_NewStringObj (prodesc -> internal_proname ,-1 ));
872868
873869/* The trigger name for argument TG_name */
874870Tcl_ListObjAppendElement (NULL ,tcl_cmd ,
875- Tcl_NewStringObj (trigdata -> tg_trigger -> tgname ,-1 ));
871+ Tcl_NewStringObj (utf_e2u ( trigdata -> tg_trigger -> tgname ) ,-1 ));
876872
877873/* The oid of the trigger relation for argument TG_relid */
878874/* Consider not converting to a string for more performance? */
@@ -885,13 +881,13 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
885881/* The name of the table the trigger is acting on: TG_table_name */
886882stroid = SPI_getrelname (trigdata -> tg_relation );
887883Tcl_ListObjAppendElement (NULL ,tcl_cmd ,
888- Tcl_NewStringObj (stroid ,-1 ));
884+ Tcl_NewStringObj (utf_e2u ( stroid ) ,-1 ));
889885pfree (stroid );
890886
891887/* The schema of the table the trigger is acting on: TG_table_schema */
892888stroid = SPI_getnspname (trigdata -> tg_relation );
893889Tcl_ListObjAppendElement (NULL ,tcl_cmd ,
894- Tcl_NewStringObj (stroid ,-1 ));
890+ Tcl_NewStringObj (utf_e2u ( stroid ) ,-1 ));
895891pfree (stroid );
896892
897893/* A list of attribute names for argument TG_relatts */
@@ -903,7 +899,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
903899Tcl_ListObjAppendElement (NULL ,tcl_trigtup ,Tcl_NewObj ());
904900else
905901Tcl_ListObjAppendElement (NULL ,tcl_trigtup ,
906- Tcl_NewStringObj (NameStr (tupdesc -> attrs [i ]-> attname ),-1 ));
902+ Tcl_NewStringObj (utf_e2u ( NameStr (tupdesc -> attrs [i ]-> attname ) ),-1 ));
907903}
908904Tcl_ListObjAppendElement (NULL ,tcl_cmd ,tcl_trigtup );
909905
@@ -1001,7 +997,7 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
1001997/* Finally append the arguments from CREATE TRIGGER */
1002998for (i = 0 ;i < trigdata -> tg_trigger -> tgnargs ;i ++ )
1003999Tcl_ListObjAppendElement (NULL ,tcl_cmd ,
1004- Tcl_NewStringObj (trigdata -> tg_trigger -> tgargs [i ],-1 ));
1000+ Tcl_NewStringObj (utf_e2u ( trigdata -> tg_trigger -> tgargs [i ]) ,-1 ));
10051001
10061002}
10071003PG_CATCH ();
@@ -1048,14 +1044,10 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
10481044 ************************************************************/
10491045if (Tcl_SplitList (interp ,result ,
10501046& ret_numvals ,& ret_values )!= TCL_OK )
1051- {
1052- UTF_BEGIN ;
10531047ereport (ERROR ,
10541048(errcode (ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED ),
10551049errmsg ("could not split return value from trigger: %s" ,
1056- UTF_U2E (Tcl_GetStringResult (interp )))));
1057- UTF_END ;
1058- }
1050+ utf_u2e (Tcl_GetStringResult (interp )))));
10591051
10601052/* Use a TRY to ensure ret_values will get freed */
10611053PG_TRY ();
@@ -1078,8 +1070,8 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
10781070
10791071for (i = 0 ;i < ret_numvals ;i += 2 )
10801072{
1081- const char * ret_name = ret_values [i ];
1082- const char * ret_value = ret_values [i + 1 ];
1073+ char * ret_name = utf_u2e ( ret_values [i ]) ;
1074+ char * ret_value = utf_u2e ( ret_values [i + 1 ]) ;
10831075int attnum ;
10841076Oid typinput ;
10851077Oid typioparam ;
@@ -1123,13 +1115,11 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
11231115/************************************************************
11241116 * Set the attribute to NOT NULL and convert the contents
11251117 ************************************************************/
1126- modnulls [attnum - 1 ]= ' ' ;
1127- UTF_BEGIN ;
11281118modvalues [attnum - 1 ]= InputFunctionCall (& finfo ,
1129- ( char * ) UTF_U2E ( ret_value ) ,
1119+ ret_value ,
11301120typioparam ,
11311121tupdesc -> attrs [attnum - 1 ]-> atttypmod );
1132- UTF_END ;
1122+ modnulls [ attnum - 1 ] = ' ' ;
11331123}
11341124
11351125rettup = SPI_modifytuple (trigdata -> tg_relation ,rettup ,tupdesc -> natts ,
@@ -1183,9 +1173,9 @@ pltcl_event_trigger_handler(PG_FUNCTION_ARGS, bool pltrusted)
11831173Tcl_ListObjAppendElement (NULL ,tcl_cmd ,
11841174Tcl_NewStringObj (prodesc -> internal_proname ,-1 ));
11851175Tcl_ListObjAppendElement (NULL ,tcl_cmd ,
1186- Tcl_NewStringObj (tdata -> event ,-1 ));
1176+ Tcl_NewStringObj (utf_e2u ( tdata -> event ) ,-1 ));
11871177Tcl_ListObjAppendElement (NULL ,tcl_cmd ,
1188- Tcl_NewStringObj (tdata -> tag ,-1 ));
1178+ Tcl_NewStringObj (utf_e2u ( tdata -> tag ) ,-1 ));
11891179
11901180tcl_rc = Tcl_EvalObjEx (interp ,tcl_cmd , (TCL_EVAL_DIRECT |TCL_EVAL_GLOBAL ));
11911181
@@ -1217,18 +1207,13 @@ throw_tcl_error(Tcl_Interp *interp, const char *proname)
12171207char * emsg ;
12181208char * econtext ;
12191209
1220- UTF_BEGIN ;
1221- emsg = pstrdup (UTF_U2E (Tcl_GetStringResult (interp )));
1222- UTF_END ;
1223- UTF_BEGIN ;
1224- econtext = UTF_U2E ((char * )Tcl_GetVar (interp ,"errorInfo" ,
1225- TCL_GLOBAL_ONLY ));
1210+ emsg = pstrdup (utf_u2e (Tcl_GetStringResult (interp )));
1211+ econtext = utf_u2e (Tcl_GetVar (interp ,"errorInfo" ,TCL_GLOBAL_ONLY ));
12261212ereport (ERROR ,
12271213(errcode (ERRCODE_EXTERNAL_ROUTINE_EXCEPTION ),
12281214errmsg ("%s" ,emsg ),
12291215errcontext ("%s\nin PL/Tcl function \"%s\"" ,
12301216econtext ,proname )));
1231- UTF_END ;
12321217}
12331218
12341219
@@ -1315,7 +1300,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
13151300/************************************************************
13161301 * Build our internal proc name from the function's Oid. Append
13171302 * "_trigger" when appropriate to ensure the normal and trigger
1318- * cases are kept separate.
1303+ * cases are kept separate. Note name must be all-ASCII.
13191304 ************************************************************/
13201305if (!is_trigger && !is_event_trigger )
13211306snprintf (internal_proname ,sizeof (internal_proname ),
@@ -1570,13 +1555,11 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
15701555free (prodesc -> user_proname );
15711556free (prodesc -> internal_proname );
15721557free (prodesc );
1573- UTF_BEGIN ;
15741558ereport (ERROR ,
15751559(errcode (ERRCODE_EXTERNAL_ROUTINE_EXCEPTION ),
15761560errmsg ("could not create internal procedure \"%s\": %s" ,
15771561internal_proname ,
1578- UTF_U2E (Tcl_GetStringResult (interp )))));
1579- UTF_END ;
1562+ utf_u2e (Tcl_GetStringResult (interp )))));
15801563}
15811564
15821565/************************************************************
@@ -2212,7 +2195,8 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
22122195 * Prepare the plan and check for errors
22132196 ************************************************************/
22142197UTF_BEGIN ;
2215- qdesc -> plan = SPI_prepare (UTF_U2E (Tcl_GetString (objv [1 ])),nargs ,qdesc -> argtypes );
2198+ qdesc -> plan = SPI_prepare (UTF_U2E (Tcl_GetString (objv [1 ])),
2199+ nargs ,qdesc -> argtypes );
22162200UTF_END ;
22172201
22182202if (qdesc -> plan == NULL )
@@ -2434,7 +2418,7 @@ pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp,
24342418{
24352419UTF_BEGIN ;
24362420argvalues [j ]= InputFunctionCall (& qdesc -> arginfuncs [j ],
2437- ( char * ) UTF_U2E (Tcl_GetString (callObjv [j ])),
2421+ UTF_U2E (Tcl_GetString (callObjv [j ])),
24382422qdesc -> argtypioparams [j ],
24392423-1 );
24402424UTF_END ;
@@ -2483,6 +2467,8 @@ pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp *interp,
24832467/**********************************************************************
24842468 * pltcl_set_tuple_values() - Set variables for all attributes
24852469 * of a given tuple
2470+ *
2471+ * Note: arrayname is presumed to be UTF8; it usually came from Tcl
24862472 **********************************************************************/
24872473static void
24882474pltcl_set_tuple_values (Tcl_Interp * interp ,const char * arrayname ,
@@ -2524,7 +2510,9 @@ pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname,
25242510/************************************************************
25252511 * Get the attribute name
25262512 ************************************************************/
2527- attname = NameStr (tupdesc -> attrs [i ]-> attname );
2513+ UTF_BEGIN ;
2514+ attname = pstrdup (UTF_E2U (NameStr (tupdesc -> attrs [i ]-> attname )));
2515+ UTF_END ;
25282516
25292517/************************************************************
25302518 * Get the attributes value
@@ -2552,6 +2540,8 @@ pltcl_set_tuple_values(Tcl_Interp *interp, const char *arrayname,
25522540}
25532541else
25542542Tcl_UnsetVar2 (interp ,* arrptr ,* nameptr ,0 );
2543+
2544+ pfree ((char * )attname );
25552545}
25562546}
25572547