
We were passing error message texts to croak() verbatim, which turns out not to work if the text contains non-ASCII characters; Perl mangles their encoding, as reported in bug #13638 from Michal Leinweber. To fix, convert the text into a UTF8-encoded SV first. It's hard to test this without risking failures in different database encodings; but we can follow the lead of plpython, which is already assuming that no-break space (U+00A0) has an equivalent in all encodings we care about running the regression tests in (cf commit 2dfa15de5). Back-patch to 9.1. The code is quite different in 9.0, and anyway it seems too risky to put something like this into 9.0's final minor release. Alex Hunsaker, with suggestions from Tim Bunce and Tom Lane
230 lines
4.7 KiB
Plaintext
230 lines
4.7 KiB
Plaintext
/**********************************************************************
|
|
* PostgreSQL::InServer::Util
|
|
*
|
|
* src/pl/plperl/Util.xs
|
|
*
|
|
* Defines plperl interfaces for general-purpose utilities.
|
|
* This module is bootstrapped as soon as an interpreter is initialized.
|
|
* Currently doesn't define a PACKAGE= so all subs are in main:: to avoid
|
|
* the need for explicit importing.
|
|
*
|
|
**********************************************************************/
|
|
|
|
/* this must be first: */
|
|
#include "postgres.h"
|
|
#include "fmgr.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/bytea.h" /* for byteain & byteaout */
|
|
#include "mb/pg_wchar.h" /* for GetDatabaseEncoding */
|
|
/* Defined by Perl */
|
|
#undef _
|
|
|
|
/* perl stuff */
|
|
#include "plperl.h"
|
|
#include "plperl_helpers.h"
|
|
|
|
/*
|
|
* Implementation of plperl's elog() function
|
|
*
|
|
* If the error level is less than ERROR, we'll just emit the message and
|
|
* return. When it is ERROR, elog() will longjmp, which we catch and
|
|
* turn into a Perl croak(). Note we are assuming that elog() can't have
|
|
* any internal failures that are so bad as to require a transaction abort.
|
|
*
|
|
* This is out-of-line to suppress "might be clobbered by longjmp" warnings.
|
|
*/
|
|
static void
|
|
do_util_elog(int level, SV *msg)
|
|
{
|
|
MemoryContext oldcontext = CurrentMemoryContext;
|
|
char * volatile cmsg = NULL;
|
|
|
|
PG_TRY();
|
|
{
|
|
cmsg = sv2cstr(msg);
|
|
elog(level, "%s", cmsg);
|
|
pfree(cmsg);
|
|
}
|
|
PG_CATCH();
|
|
{
|
|
ErrorData *edata;
|
|
|
|
/* Must reset elog.c's state */
|
|
MemoryContextSwitchTo(oldcontext);
|
|
edata = CopyErrorData();
|
|
FlushErrorState();
|
|
|
|
if (cmsg)
|
|
pfree(cmsg);
|
|
|
|
/* Punt the error to Perl */
|
|
croak_cstr(edata->message);
|
|
}
|
|
PG_END_TRY();
|
|
}
|
|
|
|
static text *
|
|
sv2text(SV *sv)
|
|
{
|
|
char *str = sv2cstr(sv);
|
|
text *text;
|
|
|
|
text = cstring_to_text(str);
|
|
pfree(str);
|
|
return text;
|
|
}
|
|
|
|
MODULE = PostgreSQL::InServer::Util PREFIX = util_
|
|
|
|
PROTOTYPES: ENABLE
|
|
VERSIONCHECK: DISABLE
|
|
|
|
int
|
|
_aliased_constants()
|
|
PROTOTYPE:
|
|
ALIAS:
|
|
DEBUG = DEBUG2
|
|
LOG = LOG
|
|
INFO = INFO
|
|
NOTICE = NOTICE
|
|
WARNING = WARNING
|
|
ERROR = ERROR
|
|
CODE:
|
|
/* uses the ALIAS value as the return value */
|
|
RETVAL = ix;
|
|
OUTPUT:
|
|
RETVAL
|
|
|
|
|
|
void
|
|
util_elog(level, msg)
|
|
int level
|
|
SV *msg
|
|
CODE:
|
|
if (level > ERROR) /* no PANIC allowed thanks */
|
|
level = ERROR;
|
|
if (level < DEBUG5)
|
|
level = DEBUG5;
|
|
do_util_elog(level, msg);
|
|
|
|
SV *
|
|
util_quote_literal(sv)
|
|
SV *sv
|
|
CODE:
|
|
if (!sv || !SvOK(sv)) {
|
|
RETVAL = &PL_sv_undef;
|
|
}
|
|
else {
|
|
text *arg = sv2text(sv);
|
|
text *quoted = DatumGetTextP(DirectFunctionCall1(quote_literal, PointerGetDatum(arg)));
|
|
char *str;
|
|
|
|
pfree(arg);
|
|
str = text_to_cstring(quoted);
|
|
RETVAL = cstr2sv(str);
|
|
pfree(str);
|
|
}
|
|
OUTPUT:
|
|
RETVAL
|
|
|
|
SV *
|
|
util_quote_nullable(sv)
|
|
SV *sv
|
|
CODE:
|
|
if (!sv || !SvOK(sv))
|
|
{
|
|
RETVAL = cstr2sv("NULL");
|
|
}
|
|
else
|
|
{
|
|
text *arg = sv2text(sv);
|
|
text *quoted = DatumGetTextP(DirectFunctionCall1(quote_nullable, PointerGetDatum(arg)));
|
|
char *str;
|
|
|
|
pfree(arg);
|
|
str = text_to_cstring(quoted);
|
|
RETVAL = cstr2sv(str);
|
|
pfree(str);
|
|
}
|
|
OUTPUT:
|
|
RETVAL
|
|
|
|
SV *
|
|
util_quote_ident(sv)
|
|
SV *sv
|
|
PREINIT:
|
|
text *arg;
|
|
text *quoted;
|
|
char *str;
|
|
CODE:
|
|
arg = sv2text(sv);
|
|
quoted = DatumGetTextP(DirectFunctionCall1(quote_ident, PointerGetDatum(arg)));
|
|
|
|
pfree(arg);
|
|
str = text_to_cstring(quoted);
|
|
RETVAL = cstr2sv(str);
|
|
pfree(str);
|
|
OUTPUT:
|
|
RETVAL
|
|
|
|
SV *
|
|
util_decode_bytea(sv)
|
|
SV *sv
|
|
PREINIT:
|
|
char *arg;
|
|
text *ret;
|
|
CODE:
|
|
arg = SvPVbyte_nolen(sv);
|
|
ret = DatumGetTextP(DirectFunctionCall1(byteain, PointerGetDatum(arg)));
|
|
/* not cstr2sv because this is raw bytes not utf8'able */
|
|
RETVAL = newSVpvn(VARDATA(ret), (VARSIZE(ret) - VARHDRSZ));
|
|
OUTPUT:
|
|
RETVAL
|
|
|
|
SV *
|
|
util_encode_bytea(sv)
|
|
SV *sv
|
|
PREINIT:
|
|
text *arg;
|
|
char *ret;
|
|
STRLEN len;
|
|
CODE:
|
|
/* not sv2text because this is raw bytes not utf8'able */
|
|
ret = SvPVbyte(sv, len);
|
|
arg = cstring_to_text_with_len(ret, len);
|
|
ret = DatumGetCString(DirectFunctionCall1(byteaout, PointerGetDatum(arg)));
|
|
RETVAL = cstr2sv(ret);
|
|
OUTPUT:
|
|
RETVAL
|
|
|
|
SV *
|
|
looks_like_number(sv)
|
|
SV *sv
|
|
CODE:
|
|
if (!SvOK(sv))
|
|
RETVAL = &PL_sv_undef;
|
|
else if ( looks_like_number(sv) )
|
|
RETVAL = &PL_sv_yes;
|
|
else
|
|
RETVAL = &PL_sv_no;
|
|
OUTPUT:
|
|
RETVAL
|
|
|
|
SV *
|
|
encode_typed_literal(sv, typname)
|
|
SV *sv
|
|
char *typname;
|
|
PREINIT:
|
|
char *outstr;
|
|
CODE:
|
|
outstr = plperl_sv_to_literal(sv, typname);
|
|
if (outstr == NULL)
|
|
RETVAL = &PL_sv_undef;
|
|
else
|
|
RETVAL = cstr2sv(outstr);
|
|
OUTPUT:
|
|
RETVAL
|
|
|
|
BOOT:
|
|
items = 0; /* avoid 'unused variable' warning */
|