Технологические интерфейсы
Функции для работы с простыми базами данных
Описываемые ниже функции полезны для реализации "игрушечных" баз данных при изготовлении прототипов приложений. Нет и речи о поддержке многопользовательского режима. Ключ в записи может быть только один. Суммарная длина ключа и данных (точнее, всех ключей, имеющих один хэш-код, и ассоциированных с ними данных) не должна превышать 1023 байта и т.д. и т.п.
Набор стандартизованных функций "традиционно минимален" (см. листинг 9.10). Базу данных можно открыть ( dbm_open() ) и закрыть ( dbm_close() ), выбрать ( dbm_fetch() ), сохранить ( dbm_store() ) и удалить ( dbm_delete() ) запись по ключу, перебрать имеющиеся в базе ключи ( dbm_firstkey(), dbm_nextkey() ), опросить статус ошибки ( dbm_error() ) и очистить его ( dbm_clearerr() ).
#include <ndbm.h> DBM *dbm_open (const char *file, int open_flags, mode_t file_mode); void dbm_close (DBM *db); datum dbm_fetch (DBM *db, datum key); int dbm_store (DBM *db, datum key, datum content, int store_mode); int dbm_delete (DBM *db, datum key); datum dbm_firstkey (DBM *db); datum dbm_nextkey (DBM *db); int dbm_error (DBM *db); int dbm_clearerr (DBM *db);Листинг 9.10. Описание функций для работы с простыми базами данных.
Функцию dbm_open() можно считать аналогом open(), только в роли возвращаемого в качестве результата дескриптора базы данных выступает указатель на объект типа DBM (структура последнего скрыта от приложения), база хранится в двух файлах – file.dir и file.pag, ее нельзя открыть только на запись, а применение флага O_APPEND ведет к неспецифицированным последствиям. В случае ошибки результат вызова dbm_open() равняется (DBM *) NULL.
При работе с ключами и данными центральную роль играет структура типа datum, которая по стандарту должна содержать по крайней мере два поля.
void *dptr; /* Указатель на прикладные данные */ size_t dsize; /* Размер прикладных данных */
Функция dbm_fetch() служит для выборки записи по ключу key. Если таковая отсутствует или обнаруживается ошибка, то в возвращаемом объекте типа datum элемент dptr равняется пустому указателю.
Функция dbm_store() позволяет поместить данные, заданные аргументом content, в базу. Аргумент store_mode определяет способ сохранения новых данных. Если в базе уже есть запись с заданным ключом key, то в режиме DBM_REPLACE новая запись замещает старую, а в режиме DBM_INSERT она (новая запись ) игнорируется (и результат вызова равняется единице). Если записи с ключом key в базе нет, новая запись добавляется к базе.
Функция dbm_delete() предназначена для удаления данных и ключа key.
Нормальным результатом функций dbm_store() и dbm_delete() является нуль; в случае ошибки возвращается отрицательное значение.
Функция dbm_nextkey() является итератором по ключам, хранящимся в базе, а dbm_firstkey() инициализирует этот итератор, возвращая первый ключ. При завершении перебора и при наличии ошибок возвращается объект типа datum с пустым указателем в качестве значения элемента dptr. Если по ходу итераций содержимое базы менялось (вызывались функции dbm_store() и/или dbm_delete() ), перебор ключей нужно начинать заново.
Функция dbm_error() возвращает ненулевое значение при установленном статусе ошибки, функция dbm_clearerr() делает статус "безошибочным". Таким образом, вся диагностика по сути сводится к одному биту, интерпретировать который должно приложение.
Несмотря на "игрушечность" описанного интерфейса, он находит определенное применение на Unix-системах. В качестве примера рассмотрим программу, которая распечатывает содержимое базы данных aliases, расположенной в каталоге /etc/mail на SPARC -станции (см. листинг 9.11).
/* * * * * * * * * * * * * * * * * * * * * * * * */ /* Программа перебирает все ключи в базе данных */ /* и выдает ассоциированную с ними информацию. */ /* Предполагается, что ключи и данные – текстовые*/ /* * * * * * * * * * * * * * * * * * * * * * * * */ #include <ndbm.h> #include <stdio.h> #include <fcntl.h> int main (int argc, char *argv []) { DBM *dbdes; /* Дескриптор открытой базы */ datum ckey; /* Текущий ключ */ datum cdat; /* Текущие данные */ int nkeys = 0; /* Число ключей */ if (argc != 2) { fprintf (stderr, "Использование: %s имя_базы\n", argv [0]); return (1); } if ((dbdes = dbm_open (argv [1], O_RDONLY, 0777)) == (DBM *) NULL) { fprintf (stderr, "Не удалось открыть базу данных %s\n", argv [1]); return (2); } for (ckey = dbm_firstkey (dbdes); ckey.dptr != NULL; ckey = dbm_nextkey (dbdes)) { nkeys++; printf ("Длина ключа номер %d: %d\n", nkeys, ckey.dsize); printf ("Ключ номер %d: %s\n", nkeys, ckey.dptr); if (cdat = dbm_fetch (dbdes, ckey), cdat.dptr != NULL) { printf ("Длина данных для ключа номер %d: %d\n", nkeys, cdat.dsize); printf ("Данные для ключа номер %d: %s\n", nkeys, cdat.dptr); } else { fprintf (stderr, "Отсутствуют данные для " "ключа номер %d\n", nkeys); } } printf ("Число ключей в базе: %d\n", nkeys); dbm_close (dbdes); return 0; }Листинг 9.11. Пример применения функций для работы с простыми базами данных.
Результаты работы этой программы могут выглядеть так, как показано на листинге 9.12.
Длина ключа номер 1: 16 Ключ номер 1: YP_LAST_MODIFIED Длина данных для ключа номер 1: 10 Данные для ключа номер 1: 0898782331 Длина ключа номер 2: 14 Ключ номер 2: mailer-daemon Длина данных для ключа номер 2: 11 Данные для ключа номер 2: postmaster Длина ключа номер 3: 14 Ключ номер 3: YP_MASTER_NAME Длина данных для ключа номер 3: 3 Данные для ключа номер 3: t41 Длина ключа номер 4: 11 Ключ номер 4: postmaster Длина данных для ключа номер 4: 5 Данные для ключа номер 4: root Длина ключа номер 5: 7 Ключ номер 5: nobody Длина данных для ключа номер 5: 10 Данные для ключа номер 5: /dev/null Длина ключа номер 6: 2 Ключ номер 6: @ Длина данных для ключа номер 6: 2 Данные для ключа номер 6: @ Число ключей в базе: 6Листинг 9.12. Возможные результаты выполнения программы, применяющей функции для работы с простыми базами данных.