Adjust plperl to ensure that all strings and hash keys passed to Perl
authorTom Lane
Sun, 15 Oct 2006 18:56:39 +0000 (18:56 +0000)
committerTom Lane
Sun, 15 Oct 2006 18:56:39 +0000 (18:56 +0000)
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

index d683e42cf546f3beb35045446b21aa28d319cd87..d645c5c85924f5309a2a257b11242e3d1c275caf 100644 (file)
@@ -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);
+}