From a3dff39c53bddf633bc0ba2ab3dc8681be50d6bf Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 15 Oct 2006 18:56:39 +0000 Subject: [PATCH] Adjust plperl to ensure that all strings and hash keys passed to Perl are marked as UTF8 when the database encoding is UTF8. This should avoid inconsistencies like that exhibited in bug #2683 from Vitali Stupin. --- src/pl/plperl/plperl.c | 184 +++++++++++++++++++++++++---------------- 1 file changed, 114 insertions(+), 70 deletions(-) diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c index d683e42cf5..d645c5c859 100644 --- a/src/pl/plperl/plperl.c +++ b/src/pl/plperl/plperl.c @@ -1,7 +1,7 @@ /********************************************************************** * plperl.c - perl as a procedural language for PostgreSQL * - * $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.119 2006/10/04 00:30:13 momjian Exp $ + * $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.120 2006/10/15 18:56:39 tgl Exp $ * **********************************************************************/ @@ -114,6 +114,9 @@ static plperl_proc_desc *compile_plperl_function(Oid fn_oid, bool is_trigger); static SV *plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc); static void plperl_init_shared_libs(pTHX); static HV *plperl_spi_execute_fetch_result(SPITupleTable *, int, int); +static SV *newSVstring(const char *str); +static SV **hv_store_string(HV *hv, const char *key, SV *val); +static SV **hv_fetch_string(HV *hv, const char *key); /* * This routine is a crock, and so is everyplace that calls it. The problem @@ -471,61 +474,61 @@ plperl_trigger_build_args(FunctionCallInfo fcinfo) ) ); - hv_store(hv, "name", 4, newSVpv(tdata->tg_trigger->tgname, 0), 0); - hv_store(hv, "relid", 5, newSVpv(relid, 0), 0); + hv_store_string(hv, "name", newSVstring(tdata->tg_trigger->tgname)); + hv_store_string(hv, "relid", newSVstring(relid)); if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event)) { event = "INSERT"; if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event)) - hv_store(hv, "new", 3, - plperl_hash_from_tuple(tdata->tg_trigtuple, tupdesc), - 0); + hv_store_string(hv, "new", + plperl_hash_from_tuple(tdata->tg_trigtuple, + tupdesc)); } else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event)) { event = "DELETE"; if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event)) - hv_store(hv, "old", 3, - plperl_hash_from_tuple(tdata->tg_trigtuple, tupdesc), - 0); + hv_store_string(hv, "old", + plperl_hash_from_tuple(tdata->tg_trigtuple, + tupdesc)); } else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event)) { event = "UPDATE"; if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event)) { - hv_store(hv, "old", 3, - plperl_hash_from_tuple(tdata->tg_trigtuple, tupdesc), - 0); - hv_store(hv, "new", 3, - plperl_hash_from_tuple(tdata->tg_newtuple, tupdesc), - 0); + hv_store_string(hv, "old", + plperl_hash_from_tuple(tdata->tg_trigtuple, + tupdesc)); + hv_store_string(hv, "new", + plperl_hash_from_tuple(tdata->tg_newtuple, + tupdesc)); } } else event = "UNKNOWN"; - hv_store(hv, "event", 5, newSVpv(event, 0), 0); - hv_store(hv, "argc", 4, newSViv(tdata->tg_trigger->tgnargs), 0); + hv_store_string(hv, "event", newSVstring(event)); + hv_store_string(hv, "argc", newSViv(tdata->tg_trigger->tgnargs)); if (tdata->tg_trigger->tgnargs > 0) { AV *av = newAV(); for (i = 0; i < tdata->tg_trigger->tgnargs; i++) - av_push(av, newSVpv(tdata->tg_trigger->tgargs[i], 0)); - hv_store(hv, "args", 4, newRV_noinc((SV *) av), 0); + av_push(av, newSVstring(tdata->tg_trigger->tgargs[i])); + hv_store_string(hv, "args", newRV_noinc((SV *) av)); } - hv_store(hv, "relname", 7, - newSVpv(SPI_getrelname(tdata->tg_relation), 0), 0); + hv_store_string(hv, "relname", + newSVstring(SPI_getrelname(tdata->tg_relation))); - hv_store(hv, "table_name", 10, - newSVpv(SPI_getrelname(tdata->tg_relation), 0), 0); + hv_store_string(hv, "table_name", + newSVstring(SPI_getrelname(tdata->tg_relation))); - hv_store(hv, "table_schema", 12, - newSVpv(SPI_getnspname(tdata->tg_relation), 0), 0); + hv_store_string(hv, "table_schema", + newSVstring(SPI_getnspname(tdata->tg_relation))); if (TRIGGER_FIRED_BEFORE(tdata->tg_event)) when = "BEFORE"; @@ -533,7 +536,7 @@ plperl_trigger_build_args(FunctionCallInfo fcinfo) when = "AFTER"; else when = "UNKNOWN"; - hv_store(hv, "when", 4, newSVpv(when, 0), 0); + hv_store_string(hv, "when", newSVstring(when)); if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event)) level = "ROW"; @@ -541,7 +544,7 @@ plperl_trigger_build_args(FunctionCallInfo fcinfo) level = "STATEMENT"; else level = "UNKNOWN"; - hv_store(hv, "level", 5, newSVpv(level, 0), 0); + hv_store_string(hv, "level", newSVstring(level)); return newRV_noinc((SV *) hv); } @@ -567,7 +570,7 @@ plperl_modify_tuple(HV *hvTD, TriggerData *tdata, HeapTuple otup) tupdesc = tdata->tg_relation->rd_att; - svp = hv_fetch(hvTD, "new", 3, FALSE); + svp = hv_fetch_string(hvTD, "new"); if (!svp) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), @@ -741,9 +744,10 @@ plperl_validator(PG_FUNCTION_ARGS) } -/* Uses mksafefunc/mkunsafefunc to create an anonymous sub whose text is - * supplied in s, and returns a reference to the closure. */ - +/* + * Uses mksafefunc/mkunsafefunc to create an anonymous sub whose text is + * supplied in s, and returns a reference to the closure. + */ static SV * plperl_create_sub(char *s, bool trusted) { @@ -761,8 +765,8 @@ plperl_create_sub(char *s, bool trusted) ENTER; SAVETMPS; PUSHMARK(SP); - XPUSHs(sv_2mortal(newSVpv("our $_TD; local $_TD=$_[0]; shift;", 0))); - XPUSHs(sv_2mortal(newSVpv(s, 0))); + XPUSHs(sv_2mortal(newSVstring("our $_TD; local $_TD=$_[0]; shift;"))); + XPUSHs(sv_2mortal(newSVstring(s))); PUTBACK; /* @@ -900,11 +904,7 @@ plperl_call_perl_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo) tmp = OutputFunctionCall(&(desc->arg_out_func[i]), fcinfo->arg[i]); - sv = newSVpv(tmp, 0); -#if PERL_BCDVERSION >= 0x5006000L - if (GetDatabaseEncoding() == PG_UTF8) - SvUTF8_on(sv); -#endif + sv = newSVstring(tmp); XPUSHs(sv_2mortal(sv)); pfree(tmp); } @@ -965,7 +965,7 @@ plperl_call_perl_trigger_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo, tg_trigger = ((TriggerData *) fcinfo->context)->tg_trigger; for (i = 0; i < tg_trigger->tgnargs; i++) - XPUSHs(sv_2mortal(newSVpv(tg_trigger->tgargs[i], 0))); + XPUSHs(sv_2mortal(newSVstring(tg_trigger->tgargs[i]))); PUTBACK; /* Do NOT use G_KEEPERR here */ @@ -1256,7 +1256,6 @@ compile_plperl_function(Oid fn_oid, bool is_trigger) HeapTuple procTup; Form_pg_proc procStruct; char internal_proname[64]; - int proname_len; plperl_proc_desc *prodesc = NULL; int i; SV **svp; @@ -1277,12 +1276,10 @@ compile_plperl_function(Oid fn_oid, bool is_trigger) else sprintf(internal_proname, "__PLPerl_proc_%u_trigger", fn_oid); - proname_len = strlen(internal_proname); - /************************************************************ * Lookup the internal proc name in the hashtable ************************************************************/ - svp = hv_fetch(plperl_proc_hash, internal_proname, proname_len, FALSE); + svp = hv_fetch_string(plperl_proc_hash, internal_proname); if (svp) { bool uptodate; @@ -1484,8 +1481,8 @@ compile_plperl_function(Oid fn_oid, bool is_trigger) internal_proname); } - hv_store(plperl_proc_hash, internal_proname, proname_len, - newSVuv(PTR2UV(prodesc)), 0); + hv_store_string(plperl_proc_hash, internal_proname, + newSVuv(PTR2UV(prodesc))); } ReleaseSysCache(procTup); @@ -1512,36 +1509,27 @@ plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc) char *outputstr; Oid typoutput; bool typisvarlena; - int namelen; - SV *sv; if (tupdesc->attrs[i]->attisdropped) continue; attname = NameStr(tupdesc->attrs[i]->attname); - namelen = strlen(attname); attr = heap_getattr(tuple, i + 1, tupdesc, &isnull); if (isnull) { /* Store (attname => undef) and move on. */ - hv_store(hv, attname, namelen, newSV(0), 0); + hv_store_string(hv, attname, newSV(0)); continue; } /* XXX should have a way to cache these lookups */ - getTypeOutputInfo(tupdesc->attrs[i]->atttypid, &typoutput, &typisvarlena); outputstr = OidOutputFunctionCall(typoutput, attr); - sv = newSVpv(outputstr, 0); -#if PERL_BCDVERSION >= 0x5006000L - if (GetDatabaseEncoding() == PG_UTF8) - SvUTF8_on(sv); -#endif - hv_store(hv, attname, namelen, sv, 0); + hv_store_string(hv, attname, newSVstring(outputstr)); pfree(outputstr); } @@ -1627,10 +1615,10 @@ plperl_spi_execute_fetch_result(SPITupleTable *tuptable, int processed, result = newHV(); - hv_store(result, "status", strlen("status"), - newSVpv((char *) SPI_result_code_string(status), 0), 0); - hv_store(result, "processed", strlen("processed"), - newSViv(processed), 0); + hv_store_string(result, "status", + newSVstring(SPI_result_code_string(status))); + hv_store_string(result, "processed", + newSViv(processed)); if (status > 0 && tuptable) { @@ -1644,8 +1632,8 @@ plperl_spi_execute_fetch_result(SPITupleTable *tuptable, int processed, row = plperl_hash_from_tuple(tuptable->vals[i], tuptable->tupdesc); av_push(rows, row); } - hv_store(result, "rows", strlen("rows"), - newRV_noinc((SV *) rows), 0); + hv_store_string(result, "rows", + newRV_noinc((SV *) rows)); } SPI_freetuptable(tuptable); @@ -1811,7 +1799,7 @@ plperl_spi_query(char *query) if (portal == NULL) elog(ERROR, "SPI_cursor_open() failed:%s", SPI_result_code_string(SPI_result)); - cursor = newSVpv(portal->name, 0); + cursor = newSVstring(portal->name); /* Commit the inner transaction, return to outer xact context */ ReleaseCurrentSubTransaction(); @@ -2065,9 +2053,9 @@ plperl_spi_prepare(char *query, int argc, SV **argv) * Insert a hashtable entry for the plan and return * the key to the caller. ************************************************************/ - hv_store(plperl_query_hash, qdesc->qname, strlen(qdesc->qname), newSVuv(PTR2UV(qdesc)), 0); + hv_store_string(plperl_query_hash, qdesc->qname, newSVuv(PTR2UV(qdesc))); - return newSVpv(qdesc->qname, strlen(qdesc->qname)); + return newSVstring(qdesc->qname); } HV * @@ -2098,7 +2086,7 @@ plperl_spi_exec_prepared(char *query, HV *attr, int argc, SV **argv) /************************************************************ * Fetch the saved plan descriptor, see if it's o.k. ************************************************************/ - sv = hv_fetch(plperl_query_hash, query, strlen(query), 0); + sv = hv_fetch_string(plperl_query_hash, query); if (sv == NULL) elog(ERROR, "spi_exec_prepared: Invalid prepared query passed"); if (*sv == NULL || !SvOK(*sv)) @@ -2118,7 +2106,7 @@ plperl_spi_exec_prepared(char *query, HV *attr, int argc, SV **argv) limit = 0; if (attr != NULL) { - sv = hv_fetch(attr, "limit", 5, 0); + sv = hv_fetch_string(attr, "limit"); if (*sv && SvIOK(*sv)) limit = SvIV(*sv); } @@ -2239,7 +2227,7 @@ plperl_spi_query_prepared(char *query, int argc, SV **argv) /************************************************************ * Fetch the saved plan descriptor, see if it's o.k. ************************************************************/ - sv = hv_fetch(plperl_query_hash, query, strlen(query), 0); + sv = hv_fetch_string(plperl_query_hash, query); if (sv == NULL) elog(ERROR, "spi_query_prepared: Invalid prepared query passed"); if (*sv == NULL || !SvOK(*sv)) @@ -2301,7 +2289,7 @@ plperl_spi_query_prepared(char *query, int argc, SV **argv) elog(ERROR, "SPI_cursor_open() failed:%s", SPI_result_code_string(SPI_result)); - cursor = newSVpv(portal->name, 0); + cursor = newSVstring(portal->name); /* Commit the inner transaction, return to outer xact context */ ReleaseCurrentSubTransaction(); @@ -2353,7 +2341,7 @@ plperl_spi_freeplan(char *query) void *plan; plperl_query_desc *qdesc; - sv = hv_fetch(plperl_query_hash, query, strlen(query), 0); + sv = hv_fetch_string(plperl_query_hash, query); if (sv == NULL) elog(ERROR, "spi_exec_freeplan: Invalid prepared query passed"); if (*sv == NULL || !SvOK(*sv)) @@ -2376,3 +2364,59 @@ plperl_spi_freeplan(char *query) SPI_freeplan(plan); } + +/* + * Create a new SV from a string assumed to be in the current database's + * encoding. + */ +static SV * +newSVstring(const char *str) +{ + SV *sv; + + sv = newSVpv(str, 0); +#if PERL_BCDVERSION >= 0x5006000L + if (GetDatabaseEncoding() == PG_UTF8) + SvUTF8_on(sv); +#endif + return sv; +} + +/* + * Store an SV into a hash table under a key that is a string assumed to be + * in the current database's encoding. + */ +static SV ** +hv_store_string(HV *hv, const char *key, SV *val) +{ + int32 klen = strlen(key); + + /* + * This seems nowhere documented, but under Perl 5.8.0 and up, + * hv_store() recognizes a negative klen parameter as meaning + * a UTF-8 encoded key. It does not appear that hashes track + * UTF-8-ness of keys at all in Perl 5.6. + */ +#if PERL_BCDVERSION >= 0x5008000L + if (GetDatabaseEncoding() == PG_UTF8) + klen = -klen; +#endif + return hv_store(hv, key, klen, val, 0); +} + +/* + * Fetch an SV from a hash table under a key that is a string assumed to be + * in the current database's encoding. + */ +static SV ** +hv_fetch_string(HV *hv, const char *key) +{ + int32 klen = strlen(key); + + /* See notes in hv_store_string */ +#if PERL_BCDVERSION >= 0x5008000L + if (GetDatabaseEncoding() == PG_UTF8) + klen = -klen; +#endif + return hv_fetch(hv, key, klen, 0); +} -- 2.40.0