/**********************************************************************
 * 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"


/*
 * 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, char *message)
{
    MemoryContext oldcontext = CurrentMemoryContext;

    PG_TRY();
    {
        elog(level, "%s", message);
    }
    PG_CATCH();
    {
        ErrorData  *edata;

        /* Must reset elog.c's state */
        MemoryContextSwitchTo(oldcontext);
        edata = CopyErrorData();
        FlushErrorState();

        /* Punt the error to Perl */
        croak("%s", edata->message);
    }
    PG_END_TRY();
}

static SV  *
newSVstring_len(const char *str, STRLEN len)
{
    SV         *sv;

    sv = newSVpvn(str, len);
#if PERL_BCDVERSION >= 0x5006000L
    if (GetDatabaseEncoding() == PG_UTF8)
        SvUTF8_on(sv);
#endif
    return sv;
}

static text *
sv2text(SV *sv)
{
    STRLEN    sv_len;
    char     *sv_pv;

    if (!sv)
        sv = &PL_sv_undef;
    sv_pv = SvPV(sv, sv_len);
    return cstring_to_text_with_len(sv_pv, sv_len);
}

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, message)
    int level
    char* message
    CODE:
        if (level > ERROR)      /* no PANIC allowed thanks */
            level = ERROR;
        if (level < DEBUG5)
            level = DEBUG5;
        do_util_elog(level, message);

SV *
util_quote_literal(sv)
    SV *sv
    CODE:
    if (!sv || !SvOK(sv)) {
        RETVAL = &PL_sv_undef;
    }
    else {
        text *arg = sv2text(sv);
        text *ret = DatumGetTextP(DirectFunctionCall1(quote_literal, PointerGetDatum(arg)));
        RETVAL = newSVstring_len(VARDATA(ret), (VARSIZE(ret) - VARHDRSZ));
    }
    OUTPUT:
    RETVAL

SV *
util_quote_nullable(sv)
    SV *sv
    CODE:
    if (!sv || !SvOK(sv))
	{
        RETVAL = newSVstring_len("NULL", 4);
    }
    else
	{
        text *arg = sv2text(sv);
        text *ret = DatumGetTextP(DirectFunctionCall1(quote_nullable, PointerGetDatum(arg)));
        RETVAL = newSVstring_len(VARDATA(ret), (VARSIZE(ret) - VARHDRSZ));
    }
    OUTPUT:
    RETVAL

SV *
util_quote_ident(sv)
    SV *sv
    PREINIT:
        text *arg;
        text *ret;
    CODE:
        arg = sv2text(sv);
        ret = DatumGetTextP(DirectFunctionCall1(quote_ident, PointerGetDatum(arg)));
        RETVAL = newSVstring_len(VARDATA(ret), (VARSIZE(ret) - VARHDRSZ));
    OUTPUT:
    RETVAL

SV *
util_decode_bytea(sv)
    SV *sv
    PREINIT:
        char *arg;
        text *ret;
    CODE:
        arg = SvPV_nolen(sv);
        ret = DatumGetTextP(DirectFunctionCall1(byteain, PointerGetDatum(arg)));
        /* not newSVstring_len 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;
    CODE:
        arg = sv2text(sv);
        ret = DatumGetCString(DirectFunctionCall1(byteaout, PointerGetDatum(arg)));
        RETVAL = newSVstring_len(ret, strlen(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


BOOT:
    items = 0;  /* avoid 'unused variable' warning */
