Hlavní navigace

Embedded databáze: Aplikační rozhraní NDBM / GDBM

3. 11. 2004
Doba čtení: 4 minuty

Sdílet

Dnes se seznámíme s nejrozšířenějším rozhraním, které používají embedded databáze implementující on disk hashtables.

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.

root_podpora

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.

Tabulka č. 605
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.

Byl pro vás článek přínosný?