Мобильное программирование приложений реального времени в стандарте POSIX

       

Функции для работы с простыми базами данных


Описываемые ниже функции полезны для реализации "игрушечных" баз данных при изготовлении прототипов приложений. Нет и речи о поддержке многопользовательского режима. Ключ в записи может быть только один. Суммарная длина ключа и данных (точнее, всех ключей, имеющих один хэш-код, и ассоциированных с ними данных) не должна превышать 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. Описание функций для работы с простыми базами данных. (html, txt)

Функцию 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. Возможные результаты выполнения программы, применяющей функции для работы с простыми базами данных.


Содержание раздела