Jak se stát hackerem II
Implementace pojmenovaných parametrů a variadických funkcí, podpora protokolu JSON
Tento článek navazuje na článek Jak se stát hackerem.
Autor: Pavel Stěhule - únor 2008
V Postgresu chybí možnost implementace funkcí s variabilním počtem parametrů - tyto funkce se označují jako variadické. Variadické funkce se sice v pg vyskytují, jedná se ale o implementaci na úrovni parseru, kdy se určité funkce nevolají prostřednictvím executoru funkcí, ale přímo. Tj. nedochází k vyhledávání implementace funkce v tabulce pg_proc. Tato tabulka obsahuje seznam všech funkcí s fixním počtem parametrů (včetně přetížených funkcí). Vyhledává se na základě shody počtu parametrů, a na základě shody typů případně možnosti přetypování a dosažení shody typů parametrů. Velká většina vestavěných funkcí je nevariadické - pokud mohou mít různé typy parametrů, tak jsou implementovány jako množina přetížených funkcí. Najdou se ale výjimky - např. funkce greatest nebo least (případně novější xmlforest). Při implementaci těchto funkcí bylo nutné modifikovat parser a executor.
Cílem je umožnit implementaci variadických funkcí bez nutnosti modifikace parseru. Plánem je změna procedury, která porovnává shodu parametrů a to tak, že pokud nedojde ke shodě, tak se vygeneruje dynamicky (bez zápisu do pg_proc) fiktivní funkce, která bude obsahovat nutný počet anyelement parametrů. Aby bylo možné tyto funkce spouštět z jazyka SQL a PL/pgSQL, tak při volání se nevytvoří seznam parametrů, ale pole parametrů - za předpokladu, že skutečné parametry nejsou pole. K tomu je potřeba vytvořit nový fiktivní datovy typ: ANYNONARRAYS. Funkce může obsahovat pouze jeden parametr tohoto typu a tento parametr musí být uložen jako poslední.
CREATE OR REPLACE FUNCTION Least(ANYNONARRAYS) RETURNS ANYELEMENT AS $$ SELECT MIN($1[i]) FROM generate_series(array_lower($1,1), array_upper($1,1)) g(i) $$ LANGUAGE SQL IMMUTABLE; SELECT Least(10,1,8,8,1,3); --> 1
Zavedení nových fiktivních typů
Vytvořím nové datové typy ANYPARAMS (variadic k ANY) a ANYNONARRAYS (variadic k ANYNONARRAY). Veškeré vestavěné typy jsou evidovány v souboru (src/include/catalog/pg_type.h). Na základě obsahu tohoto souboru se generují systémové tabulky.
DATA(insert OID = 3534 ( anyparams PGNSP PGUID 4 t p t \054 0 0 0 anyparams_in anyparams_out - - - - - i p f 0 -1 0 _null_ _null_ )); #define ANYPARAMSOID 3534 DATA(insert OID = 3535 ( anynonarrays PGNSP PGUID 4 t p t \054 0 0 0 anynonarrays_in anynonarrays_out - - - - - i p f 0 -1 0 _null_ _null_ )); #define ANYNONARRAYSOID 3535
Každý datový typ bez výjimky musí mít přiřazeny vstupní a vstupní funkce (/src/include/catalog/pg_proc.h):
DATA(insert OID = 2964 ( anyparams_in PGNSP PGUID 12 1 0 f f t f i 1 3534 "2275" _null_ _null_ _null_ anyelement_in - _null_ _null_ )); DESCR("I/O"); DATA(insert OID = 2967 ( anyparams_out PGNSP PGUID 12 1 0 f f t f i 1 2275 "3534" _null_ _null_ _null_ anyelement_out - _null_ _null_ )); DESCR("I/O"); DATA(insert OID = 2965 ( anynonarrays_in PGNSP PGUID 12 1 0 f f t f i 1 3535 "2275" _null_ _null_ _null_ anyelement_in - _null_ _null_ )); DESCR("I/O"); DATA(insert OID = 2966 ( anynonarrays_out PGNSP PGUID 12 1 0 f f t f i 1 2275 "3534" _null_ _null_ _null_ anyelement_out - _null_ _null_ )); DESCR("I/O");
Reálně nelze do těchto typů uložit žádnou hodnotu. Proto jsou v/v funkce triviální - při volání spustí výjimku (pseudotypes.c):
/* * anyparams_in - input routine for pseudo-type ANYPARAMS. */ Datum anyparams_in(PG_FUNCTION_ARGS) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot accept a value of type anyparams"))); PG_RETURN_VOID(); /* keep compiler quiet */ }
Po přeložení nesmíme zapomenout inicializovat db cluster příkazem initdb. Přidáním vestavěných datových typů a funkcí dojde k posunu přiřazených jedinečných identifikátorů, čímž se datové soubory stanou nekompatibilní.
Úprava parseru
Při parsování dochází k určení Oid funkce na základě shody parametrů volané funkce a záznamu v systémové tabulce pg_proc. Klíčovou roli hraje funkce FuncnameGetCandidates (/src/backend/catalog/namespace.c). Parametrem této funkce je úplný název funkce (může obsahovat schéma) a počet parametrů. Výstupem je pak seznam potenciálních funkcí, které teoreticky mohou obsloužit požadavek (teoreticky - jinde se kontroluje shoda parametrů). Celý trik je v tom, že pokud zjistíme, že funkce obsahuje parametr ANYPARAMS nebo ANYNONARRAYS, tak nahradíme tento parametr n parametry ANY nebo ANYNONARRAY, tak aby souhlasil seznam parametrů:
Transformace seznamu parametrů na pole parametrů
Po tomto kroku můžeme psát variadické funkce - zatím pouze v jazyce C.