Jak se stát hackerem II

Z PostgreSQL
Verze z 24. 2. 2008, 13:12, kterou vytvořil 85.160.73.157 (diskuse)
(rozdíl) ← Starší verze | zobrazit aktuální verzi (rozdíl) | Novější verze → (rozdíl)
Skočit na navigaci Skočit na vyhledávání

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.