NDBM API
Aplikační rozhraní NDBM je standardním aplikačním rozhraním, které používají embedded databáze poskytující persistentní asociativní pole. Rozhraní NDBM, přesněji řečeno jeho podmnožina, je součástí standardu Single UNIX Specification v2. Toto rozhraní, NDBM, se jmenuje podle programu, ve kterém bylo poprvé použito. Historií těchto databází jsem se zabýval v minulém dílu.
NDBM API je vytvořeno pro programy v jazyce C. Ačkoliv je možné databáze nabízející toto rozhraní využívat i z jiných jazyků, nebylo NDBM API pro tyto jazyky oficiálně definováno a různé objektově orientované nadstavby pojaly přebalení tohoto rozhraní do OO prostředí různě.
Definice tohoto API se obvykle nachází v souboru ndbm.h, případně v dbm.h. Vzhledem k tomu, že většina databází toto rozhraní podporuje, nastává zde problém s konfliktními jmény hlavičkových souborů, pokud nainstalujete více než jednu databázi podporující toto rozhraní. Některé produkty řeší tento problém tak, že NDBM kompatibilní rozhraní umísťují do jiného souboru nebo přímo rovnou do svého hlavního .h. Téměř každá databáze podporuje kromě klasického NDBM API také své specifické rozhraní, kde nabízí pár funkcí navíc – povětšinou administrativního charakeru.
Nabízené funkce
Nabízené funkce je možné rozdělit do několika skupin. Všechny funkce začínají dbm_ prefixem.
1. Otevření db
DBM* dbm_open name,flags,mode dbm_close DBM*
dbm_open otevře, případě vytvoří databázi a vrátí ukazatel na DBM strukturu nebo NULL při chybě. Tato struktura má podobný význam a použití jako struktura FILE *. U jména databáze se neuvádí koncovka, ta je automaticky doplněna v závislosti na implementaci, povětšinou na .db. flags jsou flagy předávané příkazu open – viz man 2 open – a mode jsou přístupová práva nově vytvořeného souboru.
V naprosté většině implementací je možné pouze jedno současné otevření databáze pro zápis. Otevření pro zápis obvykle vyžaduje výhradní přístup k databázi, takže ji není možné ani současně číst.
fukce dbm_close se podobá fclose, jen s tím rozdílem, že nehlásí chyby. Tuto funkci je třeba vždy zavolat, jinak povětšinou skončíte s nekonzistentní nebo dokonce poškozenou databází. Je to také nejčastější příčina poničených databází.
2. Manipulace se záznamy
Asociativní pole ukládá kombinace párů klíč:hodnota. V NDBM se k oběma hodnotám přistupuje přes strukturu datum, která vypadá takto:
typedef struct { char *dptr; int dsize; } datum;
Jak je vidět z definice, klíč i hodnota mohou být libovolná binární data. Některé implementace však omezují jejich maximální velikost a také maximální celkovou velikost databáze – tento fakt je třeba si ověřit v dokumentaci k příslušnému db engine. Toto a thread-safe chování je jediný rozdíl, se kterým se v ndbm-like databázích setkáte. Pokud se používá řetězec jako klíč, což je myslím nejčastější případ, je zvykem nezahrnovat do klíče ukončující znak \0.
int dbm_store DBM*, key, data, flags
vložení nového záznamu; pokud jsou flags nastavené na DBM_INSERT, nedojde k přepsání záznamu uloženého pod stejným klíčem.
datum dbm_fetch DBM*, key
vrátí ukazatel na data přislušející k zadanému klíči nebo NULL, pokud klíč v databázi neexistuje. Naprostá většina implementací používá statický buffer, takže následující fetch přepíše předcházející data. Pokud to není žádoucí, je potřeba data před dalším fetch překopírovat jinam. Taktéž je velmi nevhodné volat funkce ndbm z několika různých threadů současně, pokud to není explicitně povoleno v dokumentaci.
int dbm_delete DBM*, key
Vymazání záznamu z databáze. V případě úspěchu je vrácena nula.
3. Procházení dat
NDBM API nabízí kromě přímého přístupu k datům přes klíč také možnost získání hodnot všech klíčů uložených v databázi. Tyto klíče jsou vráceny v předem nedefinovaném pořadí.
datum dbm_firstkey DBM* datum dbm_nextkey DBM*
Před zavoláním druhé funkce je nutné nejprve zavolat tu první, jinak většina implementací segfaultuje.
4. Práce s chybami
Pokud nastane při operacích fetch, store, delete kritická chyba, je vrácena hodnota –1 a hodnota proměnné errno je uložena. K této uložené hodnotě se lze dostat po zavolání první funkce. Druhá fukce tuto hodnotu vynuluje.
errno_t dbm_error DBM* 0 dbm_clearerr DBM*
5. Získání file descriptoru
Originální NDBM používala pro každou databázi dva soubory. Těmito fukcemi je možné se dostat k file descriptorům náležejícím k otevřené databázi. Tyto funkce, ačkoliv byly v ndbm podporovány, jsou dnes nadstandardní a většina implementací je nemá.
int dbm_dirfno DBM* int dbm_pagfno DBM*
6. Ostatní
int dbm_rdonly DBM*
Pokud je databáze otevřená pouze pro čtení, vrátí 1. Nestandardní funkce.
Srovnání NDBM a GDBM API
Pro zajímavost jsem připravil tuto tabulku ukazující rozdíly mezi gdbm a ndbm API. Ačkoliv si nemyslím, že by někdo ještě dnes použil u nově napsané aplikace gdbm api, tabulka se bude hodit, pokud budete přepisovat aplikace využívající gdbm. Do tabulky nejsou zahrnuty funkce, které má gdbm navíc, např. gdbm_sync.
NDBM Funkce | GDBM funkce | GDBM Rozdíly |
---|---|---|
DBM* | GDBM_FILE | handle databázového souboru; je předáván hodnotou, nikoliv ukazatelem |
dbm_open | gdbm_open | ke jménu se nepřidává extenze, deklarace velikosti databázového bloku, flagy NEJSOU totožné s fukncí open 2, přidána funkce pro report poškozené db |
dbm_store | gdbm_store | shodné až na flagy, které mají GDBM_ prefix místo DBM_ |
dbm_fetch | gdbm_fetch | není použit statický buffer, data je nutné uvolnit pomocí free() |
dbm_nextkey | gdbm_nextkey | je vyžadováno předání hodnoty předchozího klíče |
dbm_dirfno | gdbm_fdesc | pouze jiný název |
dbm_error | proměnná gdbm_errno | automaticky se vynuluje, pokud následující operace skončí úspěšně |
V příštím dílu bude slibovaný crash and speed test několika embedded db enginů. Zatím předběžně počítám s db4, db1, tdb, gdbm, qdbm, sqlite.