Polymorfní funkce quote literal

Z PostgreSQL
Skočit na navigaci Skočit na vyhledávání

Tento zdrojový kód je ukázkou polymorfní SPI funkce (polymorfní - její parametr je typu ANYELEMENT, SPI - Server Programming Interface). Funkčnost této funkce je stejná jako u vestavěné funkce quote_literal. Rozdíl je v parametrech. Zatímco parametr vestavěné funkce je typu TEXT (přetypování na tento typ zajistí systém), parametr funkce z příkladu je ANYELEMENT, a funkce sama musí zajistit korektní přetypování.

#include "executor/spi.h"               /* this is what you need to work with SPI */
#include "utils/lsyscache.h"

PG_MODULE_MAGIC;

extern Datum quote_literal(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(quote_literal);

Datum
quote_literal(PG_FUNCTION_ARGS)
{
        text *result;
        Oid         valtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
        Datum       value = PG_GETARG_DATUM(0);
        Oid                     typoutput;
        bool            typIsVarlena;
        StringInfoData buf;

        if (!OidIsValid(valtype))
                elog(ERROR, "could not determine data type of input");

        initStringInfo(&buf);

        appendStringInfoChar(&buf, '\'');

        getTypeOutputInfo(valtype, &typoutput, &typIsVarlena);
        appendStringInfoString(&buf, OidOutputFunctionCall(typoutput, value));

        appendStringInfoChar(&buf, '\'');

        result = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(buf.data)));

        PG_RETURN_TEXT_P(result);
}

Za povšimnutí stojí vícenásobná registrace funkce. Jednou pro libovolný typ, jednou pro typ text. To samotnou funkci nijak neovlivní. Všechny systémové parametry jsou stejné a to ať je funkce volaná s typem ANYELEMENT nebo s typem TEXT. quote_literal(text) umožní najít tuto funkci pro typově neurčený řetězec (což je nejčastější případ). Pokud by tato verze funkce chyběla, systém zahlásí chybu ve smyslu, že nemůže použít neznámý typ. Toto omezení postrádá smyslu u C funkcí, neboť neurčený typ je Cstring, který v C je nejen že bezproblémový, ale hlavně jedním se základních typů.

CREATE OR REPLACE FUNCTION quote_literal(anyelement)
RETURNS text
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;

CREATE OR REPLACE FUNCTION quote_literal(text)
RETURNS text
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;

Tato funkce byla navržena tak, aby emulovala původní chování funkce quote_literal ve verzi 8.3. V této verzi se obecně omezilo automatické přetypování na text. Existuje ale podstatně jednodušší řešení postavené na pg_proc funkci (v jazyce SQL).

CREATE FUNCTION quote_literal(anyelement)
RETURNS text AS
$$ SELECT quote_literal($1::text) $$
LANGUAGE SQL STRICT;

Zhruba 20-30% časové úspory při zobrazování velkých tabulek obsahujících tuto funkci dosáhneme použitím cache. Pro každý sloupec v rámci SQL příkazu má každá funkce vyhrazenou unikátní cache. V tomto případě bude sloužit jako úložiště Oid výstupní funkce (každý typ má přiřazenou tzv. výstupní funkci, která převádí binární hodnotu na hodnotu typ Cstring). Díky tomu odpadne opakované volání funkce getTypeOutputInfo (resp. tato funkce se volá pouze jednou během zpracování SQL příkazu).

Datum
quote(PG_FUNCTION_ARGS)
{
        text *result;
        Oid         valtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
        Datum       value = PG_GETARG_DATUM(0);
        StringInfoData buf;
        Oid             *ptr;

        if (!OidIsValid(valtype))
                elog(ERROR, "could not determine data type of input");

        ptr = (Oid *) fcinfo->flinfo->fn_extra;
        if (ptr == NULL)
        {
                Oid                     typoutput;
                bool            typIsVarlena;

                /* First time calling for current query: allocate storage */
                fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
                                                                                    sizeof(Oid));
                ptr = (Oid *) fcinfo->flinfo->fn_extra;

                getTypeOutputInfo(valtype, &typoutput, &typIsVarlena);
                *ptr = typoutput;
        }

        initStringInfo(&buf);

        appendStringInfoChar(&buf, '\'');
        appendStringInfoString(&buf, OidOutputFunctionCall(*ptr, value));
        appendStringInfoChar(&buf, '\'');

        result = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(buf.data)));

        PG_RETURN_TEXT_P(result);
}