/* perl stuff */
#include "plperl.h"
+#include "plperl_helpers.h"
/* string literal macros defining chunks of perl code */
#include "perlchunks.h"
static void plperl_trusted_init(void);
static void plperl_untrusted_init(void);
static HV *plperl_spi_execute_fetch_result(SPITupleTable *, int, int);
-static SV *newSVstring(const char *str);
+static char *hek2cstr(HE *he);
static SV **hv_store_string(HV *hv, const char *key, SV *val);
static SV **hv_fetch_string(HV *hv, const char *key);
static void plperl_create_sub(plperl_proc_desc *desc, char *s, Oid fn_oid);
#endif
/*
- * Convert an SV to char * and verify the encoding via pg_verifymbstr()
+ * convert a HE (hash entry) key to a cstr in the current database encoding
*/
-static inline char *
-sv2text_mbverified(SV *sv)
+static char *
+hek2cstr(HE *he)
{
- char *val;
- STRLEN len;
-
/*
- * The value returned here might include an embedded nul byte, because
- * perl allows such things. That's OK, because pg_verifymbstr will choke
- * on it, If we just used strlen() instead of getting perl's idea of the
- * length, whatever uses the "verified" value might get something quite
- * weird.
+ * Unfortunately, while HeUTF8 is true for most things > 256, for
+ * values 128..255 it's not, but perl will treat them as
+ * unicode code points if the utf8 flag is not set ( see
+ * The "Unicode Bug" in perldoc perlunicode for more)
+ *
+ * So if we did the expected:
+ * if (HeUTF8(he))
+ * utf_u2e(key...);
+ * else // must be ascii
+ * return HePV(he);
+ * we won't match columns with codepoints from 128..255
+ *
+ * For a more concrete example given a column with the
+ * name of the unicode codepoint U+00ae (registered sign)
+ * and a UTF8 database and the perl return_next {
+ * "\N{U+00ae}=>'text } would always fail as heUTF8
+ * returns 0 and HePV() would give us a char * with 1 byte
+ * contains the decimal value 174
+ *
+ * Perl has the brains to know when it should utf8 encode
+ * 174 properly, so here we force it into an SV so that
+ * perl will figure it out and do the right thing
*/
- val = SvPV(sv, len);
- pg_verifymbstr(val, len, false);
- return val;
+ SV *sv = HeSVKEY_force(he);
+ if (HeUTF8(he))
+ SvUTF8_on(sv);
+ return sv2cstr(sv);
}
/*
eval_pv("PostgreSQL::InServer::SPI::bootstrap()", FALSE);
if (SvTRUE(ERRSV))
ereport(ERROR,
- (errmsg("%s", strip_trailing_ws(SvPV_nolen(ERRSV))),
+ (errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV))),
errcontext("while executing PostgreSQL::InServer::SPI::bootstrap")));
/* Fully initialized, so mark the hashtable entry valid */
plperl_init_interp(void)
{
PerlInterpreter *plperl;
- static int perl_sys_init_done;
static char *embedding[3 + 2] = {
"", "-e", PLC_PERLBOOT
* true when MYMALLOC is set.
*/
#if defined(PERL_SYS_INIT3) && !defined(MYMALLOC)
- /* only call this the first time through, as per perlembed man page */
- if (!perl_sys_init_done)
{
- char *dummy_env[1] = {NULL};
+ static int perl_sys_init_done;
- PERL_SYS_INIT3(&nargs, (char ***) &embedding, (char ***) &dummy_env);
- perl_sys_init_done = 1;
- /* quiet warning if PERL_SYS_INIT3 doesn't use the third argument */
- dummy_env[0] = NULL;
+ /* only call this the first time through, as per perlembed man page */
+ if (!perl_sys_init_done)
+ {
+ char *dummy_env[1] = {NULL};
+
+ PERL_SYS_INIT3(&nargs, (char ***) &embedding, (char ***) &dummy_env);
+ perl_sys_init_done = 1;
+ /* quiet warning if PERL_SYS_INIT3 doesn't use the third argument */
+ dummy_env[0] = NULL;
+ }
}
#endif
if (perl_parse(plperl, plperl_init_shared_libs,
nargs, embedding, NULL) != 0)
ereport(ERROR,
- (errmsg("%s", strip_trailing_ws(SvPV_nolen(ERRSV))),
+ (errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV))),
errcontext("while parsing Perl initialization")));
if (perl_run(plperl) != 0)
ereport(ERROR,
- (errmsg("%s", strip_trailing_ws(SvPV_nolen(ERRSV))),
+ (errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV))),
errcontext("while running Perl initialization")));
#ifdef PLPERL_RESTORE_LOCALE
eval_pv(PLC_TRUSTED, FALSE);
if (SvTRUE(ERRSV))
ereport(ERROR,
- (errmsg("%s", strip_trailing_ws(SvPV_nolen(ERRSV))),
+ (errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV))),
errcontext("while executing PLC_TRUSTED")));
- if (GetDatabaseEncoding() == PG_UTF8)
- {
- /*
- * Force loading of utf8 module now to prevent errors that can arise
- * from the regex code later trying to load utf8 modules. See
- * http://rt.perl.org/rt3/Ticket/Display.html?id=47576
- */
- eval_pv("my $a=chr(0x100); return $a =~ /\\xa9/i", FALSE);
- if (SvTRUE(ERRSV))
- ereport(ERROR,
- (errmsg("%s", strip_trailing_ws(SvPV_nolen(ERRSV))),
- errcontext("while executing utf8fix")));
- }
+ /*
+ * Force loading of utf8 module now to prevent errors that can arise
+ * from the regex code later trying to load utf8 modules. See
+ * http://rt.perl.org/rt3/Ticket/Display.html?id=47576
+ */
+ eval_pv("my $a=chr(0x100); return $a =~ /\\xa9/i", FALSE);
+ if (SvTRUE(ERRSV))
+ ereport(ERROR,
+ (errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV))),
+ errcontext("while executing utf8fix")));
/*
* Lock down the interpreter
eval_pv(plperl_on_plperl_init, FALSE);
if (SvTRUE(ERRSV))
ereport(ERROR,
- (errmsg("%s", strip_trailing_ws(SvPV_nolen(ERRSV))),
+ (errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV))),
errcontext("while executing plperl.on_plperl_init")));
}
eval_pv(plperl_on_plperlu_init, FALSE);
if (SvTRUE(ERRSV))
ereport(ERROR,
- (errmsg("%s", strip_trailing_ws(SvPV_nolen(ERRSV))),
+ (errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV))),
errcontext("while executing plperl.on_plperlu_init")));
}
}
{
TupleDesc td = attinmeta->tupdesc;
char **values;
- SV *val;
- char *key;
- I32 klen;
+ HE *he;
HeapTuple tup;
+ int i;
values = (char **) palloc0(td->natts * sizeof(char *));
hv_iterinit(perlhash);
- while ((val = hv_iternextsv(perlhash, &key, &klen)))
+ while ((he = hv_iternext(perlhash)))
{
- int attn = SPI_fnumber(td, key);
+ SV *val = HeVAL(he);
+ char *key = hek2cstr(he);
+ int attn = SPI_fnumber(td, key);
if (attn <= 0 || td->attrs[attn - 1]->attisdropped)
ereport(ERROR,
key)));
if (SvOK(val))
{
- values[attn - 1] = sv2text_mbverified(val);
+ values[attn - 1] = sv2cstr(val);
}
+
+ pfree(key);
}
hv_iterinit(perlhash);
tup = BuildTupleFromCStrings(attinmeta, values);
+
+ for(i = 0; i < td->natts; i++)
+ {
+ if (values[i])
+ pfree(values[i]);
+ }
pfree(values);
+
return tup;
}
)
);
- hv_store_string(hv, "name", newSVstring(tdata->tg_trigger->tgname));
- hv_store_string(hv, "relid", newSVstring(relid));
+ hv_store_string(hv, "name", cstr2sv(tdata->tg_trigger->tgname));
+ hv_store_string(hv, "relid", cstr2sv(relid));
if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event))
{
else
event = "UNKNOWN";
- hv_store_string(hv, "event", newSVstring(event));
+ hv_store_string(hv, "event", cstr2sv(event));
hv_store_string(hv, "argc", newSViv(tdata->tg_trigger->tgnargs));
if (tdata->tg_trigger->tgnargs > 0)
av_extend(av, tdata->tg_trigger->tgnargs);
for (i = 0; i < tdata->tg_trigger->tgnargs; i++)
- av_push(av, newSVstring(tdata->tg_trigger->tgargs[i]));
+ av_push(av, cstr2sv(tdata->tg_trigger->tgargs[i]));
hv_store_string(hv, "args", newRV_noinc((SV *) av));
}
hv_store_string(hv, "relname",
- newSVstring(SPI_getrelname(tdata->tg_relation)));
+ cstr2sv(SPI_getrelname(tdata->tg_relation)));
hv_store_string(hv, "table_name",
- newSVstring(SPI_getrelname(tdata->tg_relation)));
+ cstr2sv(SPI_getrelname(tdata->tg_relation)));
hv_store_string(hv, "table_schema",
- newSVstring(SPI_getnspname(tdata->tg_relation)));
+ cstr2sv(SPI_getnspname(tdata->tg_relation)));
if (TRIGGER_FIRED_BEFORE(tdata->tg_event))
when = "BEFORE";
when = "INSTEAD OF";
else
when = "UNKNOWN";
- hv_store_string(hv, "when", newSVstring(when));
+ hv_store_string(hv, "when", cstr2sv(when));
if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event))
level = "ROW";
level = "STATEMENT";
else
level = "UNKNOWN";
- hv_store_string(hv, "level", newSVstring(level));
+ hv_store_string(hv, "level", cstr2sv(level));
return newRV_noinc((SV *) hv);
}
{
SV **svp;
HV *hvNew;
+ HE *he;
HeapTuple rtup;
- SV *val;
- char *key;
- I32 klen;
int slotsused;
int *modattrs;
Datum *modvalues;
slotsused = 0;
hv_iterinit(hvNew);
- while ((val = hv_iternextsv(hvNew, &key, &klen)))
+ while ((he = hv_iternext(hvNew)))
{
- int attn = SPI_fnumber(tupdesc, key);
Oid typinput;
Oid typioparam;
int32 atttypmod;
FmgrInfo finfo;
+ SV *val = HeVAL(he);
+ char *key = hek2cstr(he);
+ int attn = SPI_fnumber(tupdesc, key);
if (attn <= 0 || tupdesc->attrs[attn - 1]->attisdropped)
ereport(ERROR,
atttypmod = tupdesc->attrs[attn - 1]->atttypmod;
if (SvOK(val))
{
+ char *str = sv2cstr(val);
modvalues[slotsused] = InputFunctionCall(&finfo,
- sv2text_mbverified(val),
+ str,
typioparam,
atttypmod);
modnulls[slotsused] = ' ';
+ pfree(str);
}
else
{
}
modattrs[slotsused] = attn;
slotsused++;
+
+ pfree(key);
}
hv_iterinit(hvNew);
SAVETMPS;
PUSHMARK(SP);
EXTEND(SP, 4);
- PUSHs(sv_2mortal(newSVstring(subname)));
+ PUSHs(sv_2mortal(cstr2sv(subname)));
PUSHs(sv_2mortal(newRV_noinc((SV *) pragma_hv)));
/*
* Use 'false' for $prolog in mkfunc, which is kept for compatibility
* the function compiler.
*/
PUSHs(&PL_sv_no);
- PUSHs(sv_2mortal(newSVstring(s)));
+ PUSHs(sv_2mortal(cstr2sv(s)));
PUTBACK;
/*
if (SvTRUE(ERRSV))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("%s", strip_trailing_ws(SvPV_nolen(ERRSV)))));
+ errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV)))));
if (!subref)
ereport(ERROR,
tmp = OutputFunctionCall(&(desc->arg_out_func[i]),
fcinfo->arg[i]);
- sv = newSVstring(tmp);
+ sv = cstr2sv(tmp);
PUSHs(sv_2mortal(sv));
pfree(tmp);
}
LEAVE;
/* XXX need to find a way to assign an errcode here */
ereport(ERROR,
- (errmsg("%s", strip_trailing_ws(SvPV_nolen(ERRSV)))));
+ (errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV)))));
}
retval = newSVsv(POPs);
EXTEND(sp, tg_trigger->tgnargs);
for (i = 0; i < tg_trigger->tgnargs; i++)
- PUSHs(sv_2mortal(newSVstring(tg_trigger->tgargs[i])));
+ PUSHs(sv_2mortal(cstr2sv(tg_trigger->tgargs[i])));
PUTBACK;
/* Do NOT use G_KEEPERR here */
LEAVE;
/* XXX need to find a way to assign an errcode here */
ereport(ERROR,
- (errmsg("%s", strip_trailing_ws(SvPV_nolen(ERRSV)))));
+ (errmsg("%s", strip_trailing_ws(sv2cstr(ERRSV)))));
}
retval = newSVsv(POPs);
else
{
/* Return a perl string converted to a Datum */
+ char *str;
if (prodesc->fn_retisarray && SvROK(perlret) &&
SvTYPE(SvRV(perlret)) == SVt_PVAV)
perlret = array_ret;
}
+ str = sv2cstr(perlret);
retval = InputFunctionCall(&prodesc->result_in_func,
- sv2text_mbverified(perlret),
+ str,
prodesc->result_typioparam, -1);
+ pfree(str);
}
/* Restore the previous error callback */
HeapTuple trv;
char *tmp;
- tmp = SvPV_nolen(perlret);
+ tmp = sv2cstr(perlret);
if (pg_strcasecmp(tmp, "SKIP") == 0)
trv = NULL;
trv = NULL;
}
retval = PointerGetDatum(trv);
+ pfree(tmp);
}
/* Restore the previous error callback */
outputstr = OidOutputFunctionCall(typoutput, attr);
- hv_store_string(hv, attname, newSVstring(outputstr));
+ hv_store_string(hv, attname, cstr2sv(outputstr));
pfree(outputstr);
}
result = newHV();
hv_store_string(result, "status",
- newSVstring(SPI_result_code_string(status)));
+ cstr2sv(SPI_result_code_string(status)));
hv_store_string(result, "processed",
newSViv(processed));
if (SvOK(sv))
{
+ char *str;
+
if (prodesc->fn_retisarray && SvROK(sv) &&
SvTYPE(SvRV(sv)) == SVt_PVAV)
{
sv = plperl_convert_to_pg_array(sv);
}
+ str = sv2cstr(sv);
ret = InputFunctionCall(&prodesc->result_in_func,
- sv2text_mbverified(sv),
+ str,
prodesc->result_typioparam, -1);
isNull = false;
+ pfree(str);
}
else
{
if (portal == NULL)
elog(ERROR, "SPI_cursor_open() failed:%s",
SPI_result_code_string(SPI_result));
- cursor = newSVstring(portal->name);
+ cursor = cstr2sv(portal->name);
/* Commit the inner transaction, return to outer xact context */
ReleaseCurrentSubTransaction();
typInput,
typIOParam;
int32 typmod;
+ char *typstr;
- parseTypeString(SvPV_nolen(argv[i]), &typId, &typmod);
+ typstr = sv2cstr(argv[i]);
+ parseTypeString(typstr, &typId, &typmod);
+ pfree(typstr);
getTypeInputInfo(typId, &typInput, &typIOParam);
HASH_ENTER, &found);
hash_entry->query_data = qdesc;
- return newSVstring(qdesc->qname);
+ return cstr2sv(qdesc->qname);
}
HV *
{
if (SvOK(argv[i]))
{
+ char *str = sv2cstr(argv[i]);
argvalues[i] = InputFunctionCall(&qdesc->arginfuncs[i],
- sv2text_mbverified(argv[i]),
+ str,
qdesc->argtypioparams[i],
-1);
nulls[i] = ' ';
+ pfree(str);
}
else
{
{
if (SvOK(argv[i]))
{
+ char *str = sv2cstr(argv[i]);
argvalues[i] = InputFunctionCall(&qdesc->arginfuncs[i],
- sv2text_mbverified(argv[i]),
+ str,
qdesc->argtypioparams[i],
-1);
nulls[i] = ' ';
+ pfree(str);
}
else
{
elog(ERROR, "SPI_cursor_open() failed:%s",
SPI_result_code_string(SPI_result));
- cursor = newSVstring(portal->name);
+ cursor = cstr2sv(portal->name);
/* Commit the inner transaction, return to outer xact context */
ReleaseCurrentSubTransaction();
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);
+ int32 hlen;
+ char *hkey;
+ SV **ret;
+
+ hkey = (char*)pg_do_encoding_conversion((unsigned char *)key, strlen(key), GetDatabaseEncoding(), PG_UTF8);
/*
* This seems nowhere documented, but under Perl 5.8.0 and up, hv_store()
* 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);
+ hlen = -strlen(hkey);
+ ret = hv_store(hv, hkey, hlen, val, 0);
+
+ if (hkey != key)
+ pfree(hkey);
+
+ return ret;
}
/*
static SV **
hv_fetch_string(HV *hv, const char *key)
{
- int32 klen = strlen(key);
+ int32 hlen;
+ char *hkey;
+ SV **ret;
+
+ hkey = (char*)pg_do_encoding_conversion((unsigned char *)key, strlen(key), GetDatabaseEncoding(), PG_UTF8);
/* See notes in hv_store_string */
-#if PERL_BCDVERSION >= 0x5008000L
- if (GetDatabaseEncoding() == PG_UTF8)
- klen = -klen;
-#endif
- return hv_fetch(hv, key, klen, 0);
+ hlen = -strlen(hkey);
+ ret = hv_fetch(hv, hkey, hlen, 0);
+
+ if(hkey != key)
+ pfree(hkey);
+
+ return ret;
}
/*