Система управления базами данных Euphoria (EDS)

Введение

Многие люди выражали свою заинтересованность в доступе к базам данных с использованием программ Euphoria. Эти люди хотели бы иметь также доступ к базам других широко известных систем управления базами данных из Euphoria, или возможность создания простой и легкой в использовании Euphoria-ориентированной базы для хранения своих данных. EDS как раз и создана для решения последней задачи. Она дает простую, очень гибкую систему управления базой данных для использования с программами Euphoria.

Структура базы данных EDS

В EDS база данных - это единственный файл с расширением ".edb". База данных EDS состоит из 0 или более таблиц. Каждая таблица имеет имя и содержит 0 или более записей. Каждая запись состоит из области ключа и области данных. Ключ может быть любым объектом Euphoria - атомом, рядом, глубоко вложенным рядом, если хотите. Сходным образом данные могут быть любым объектом Euphoria. На размер и структуру ключа и данных не налагается никаких ограничений. Внутри данной таблицы все ключи уникальны. Это означает, что в одной таблице невозможны записи с одним и тем же ключом.

Записи в таблице размещены по возрастанию ключевых величин. Когда вы обращаетесь к записи по ключу, применяется эффективный бинарный поиск. Возможен и прямой доступ к записям, без поиска, если вы знаете текущий номер искомой записи в таблице. Записи имеют номера от 1 до длины таблицы (текущего числа записей).

Области ключа и данных хранятся в компактной форме, но точность чисел с плавающей точкой или других любых данных Euphoria при при записи-считывании не теряется.

database.e, как она есть, будет работать под Windows, DOS или Linux. Код исполняется вдвое быстрее под Linux, чем под DOS или Windows. Файлы базы данных EDS могут быть общими для Linux и DOS/Windows.

 Пример:

    база_данных: "мои_данные.edb"
          первая таблица: "пароли"
               запись #1:  ключ: "джонс"   данные: "юфори123"
               запись #2:  ключ: "смит"    данные: "биллгейтс"
        
          вторая таблица: "части"
               запись #1:  ключ: 134525    данные: {"молоток", 15.95, 500}
               запись #2:  ключ: 134526    данные: {"пила", 25.95, 100}
               запись #3:  ключ: 134530    данные: {"отвертка", 5.50, 1500}
        

Это полностью ваше дело, как интерпретировать значения ключей и данных. В соответствии с духом Euphoria у вас в распоряжении тотальная гибкость. В отличие от большинства других систем управления базами данных записи в EDS не требуют фиксированного числа полей либо предустановленной максимальной длины полей.

Во многих случаях будет трудно подыскать какую-либо естественную величину для значения ключа к вашим записям. Тогда вы должны будете создать просто бессмысленные, но уникальные целочисленные номера в качестве ключей. Не забывайте, что вы всегда можете получить доступ к своим данным по номеру записи. Очень легко организовать циклический просмотр записей по их номерам для поиска специфического значения поля.

Как использовать эти подпрограммы

Чтобы уменьшить число параметров, которые вы должны подавать в подпрограммы, введены понятия текушей базы и текущей таблицы. Большинство подпрограмм используют эти "текущие" значения автоматически. Обычно вы начинаете с открытия (или создания) файла базы данных, а затем выбираете таблицу, с которой хотите работать.

Вы можете соотнести ключ с номером записи с помощью подпрограммы db_find_key(). В ней используется эффективный бинарный поиск. Большинство других подпрограмм уровня записи принимают номер записи в качестве аргумента. Вы можете очень быстро получить доступ к любой записи, задав ее номер. Вы можете получить доступ ко всем записям, начиная с записи номер 1 и задавая цикл с предельным значением, выдаваемым функцией db_table_size().

Как обновляется база данных?

Когда вы что-то удаляете из базы, например, запись, данные об освобождаемом пространстве подаются в перечень свободных участков для будущего использования. Смежные свободные участки объединяются в большие свободные области. Когда требуется больше пространства, а в перечне свободных участков нет подходящего, файл увеличивается в размере. В настоящее время пока нет автоматизации уменьшения файла, но вы можете использовать процедуру db_compress(), чтобы полностью переписать базу данных, удаляя неиспользуемые участки.

Безопасность / Многопользовательский доступ

Данный выпуск предусматривает простой метод закрывания всей базы данных для предотвращения небезопасного доступа со стороны других процессов.

Допустимые размеры базы данных

Внутренние указатели 4-х байтные. Это обстоятельство ограничивает размер файла базы данных 4 Гб.

В настоящее время на каждую запись в текущей таблице в памяти резервируется 4 байта. Это означает, что вам нужно по меньшей мере 4 Мб оперативной памяти на миллион записей на диске.

Бинарный поиск для ключей будет работать достаточно хорошо с большими таблицами.

Вставка и удаление становятся слегка медленнее, если таблица увеличивается.

На нижнем конце шкалы размеров возможно создание экстремально компактных баз данных без больших потерь на накладные расходы дискового пространства.

Ограничение ответственности

Не размещайте в этой базе ценные данные, не сделав их резервное копирование. RDS не будет нести ответственности за любое повреждение или утрату данных.


Подпрограммы управления базой данных

 
db_create - создает новую базу данных
db_open - открывает существующую базу данных
db_select - назначает базу данных текущей
db_close - закрывает базу данных
db_create_table - создает новую таблицу в базе данных
db_select_table - назначает таблицу текущей
db_delete_table - удаляет таблицу
db_table_list - выдает список всех имен таблиц в базе данных
db_table_size - выдает число записей в текущей таблице
db_find_key - быстро находит запись по значению ключа
db_record_key - выдает ключевую область записи
db_record_data - выдает область данных записи
db_insert - вставляет новую запись в текущую таблицу
db_delete_record - удаляет запись из текущей таблицы
db_replace_data - заменяет область данных в записи
db_compress - сжимает базу данных
db_dump - печатает содержимое базы данных
db_fatal_id - обрабатывает фатальные ошибки базы данных



db_create

Синтаксис: include database.e
i1 = db_create(s, i2)
Описание: Создает новую базу данных. В файле, который задан путем s, будет создана база данных. i2 обозначает тип замка, который будет применен к файлу, когда он будет создан. i1 содержит код ошибки, обозначающий успешность операции или ошибку в ней. Величина для i2 может принимать значение или DB_LOCK_NO (замка нет), или DB_LOCK_EXCLUSIVE (замок исключительный). i1 будет иметь значение DB_OK, если новая база данных успешно создана. Эта база данных становится текущей базой, к которой будут применяться все последующие операции, относящиеся к базам данных.
Комментарии: Если в пути s нет расширения .edb, оно будет добавлено автоматически.

Если база данных уже существует, она не переписывается. db_create() выдает в этом случае DB_EXISTS_ALREADY.

Номер версии помещается в файл базы данных, поэтому будущие версии систем управления смогут распознать формат и будут способны выполнять чтение и обмен с базой методом, соответствующим версии.

Пример:
if db_create("mydata", DB_LOCK_NO) != DB_OK then
    puts(2, "Couldn't create the database!\n")
    abort(1)
end if
См. также: db_open, db_close

db_open

Синтаксис: include database.e
i1 = db_open(s, i2)
Описание: Открывает существующую базу данных Euphoria. Файл, содержащий базу данных, задается в s. i1 присваивается значение кода ошибки, обозначающего успешность или ошибку операции. i2 задается тип замка, который вы хотите применить к базе данных, пока файл базы данных открыт. Эта база данных становится текущей базой, к которой будут применяться все последующие операции.
Комментарии: Типы замков, которые вы можете использовать: DB_LOCK_NO (нет замка), DB_LOCK_SHARED (общий замок, закрыта запись) и DB_LOCK_EXCLUSIVE (исключительный замок, закрыты чтение/запись). Тип DB_LOCK_SHARED поддерживается только под Linux. Он разрешает вам читать базу данных, но не позволяет ничего туда записывать. Если вы потребовали DB_LOCK_SHARED под WIN32 или DOS32, запрос будет обработан, как если бы вы потребовали DB_LOCK_EXCLUSIVE.

Выдаваемые коды: DB_OK - успех, DB_OPEN_FAIL - невозможность открытия файла и DB_LOCK_FAIL - невозможность включения замка требуемого типа. Если замок не создан, ваша программа будет ожидать несколько секунд, а затем повторит попытку его создания, так как другой процесс в данный момент может иметь доступ к базе.

Программы DOS будут обычно получать сообщение "critical error", если они попытаются получить доступ к базе, которая в данный момент закрыта.

Пример:
tries = 0
while 1 do
    err = db_open("mydata", DB_LOCK_SHARED) 
    if err = DB_OK then
        exit
    elsif err = DB_LOCK_FAIL then
    	tries += 1
    	if tries > 10 then
            puts(2, "too many tries, giving up\n")
            abort(1)
    	else    
    	    sleep(5)
    	end if
    else
    	puts(2, "Couldn't open the database!\n")
    	abort(1)
    end if
end while

См. также: db_create, db_close

db_select

Синтаксис: include database.e
i = db_select(s)
Описание: Выбирает новую, уже открытую, базу данных в качестве текущей базы. Последующие операции системы управления будут выполняться с этой базой. s является путем к файлу базы данных, по которому файл был открыт подпрограммой db_open() или db_create(). i содержит код, обозначающий успех (DB_OK) или неудачу.
Комментарии: Когда вы создаете (db_create) или открываете (db_open) базу данных, она автоматически становится текущей базой. Используйте db_select(), когда вам необходимо переходить между открытыми базами данных, к примеру, для копирования записей из одной в другую.

После выбора новой базы данных вы должны выбрать таблицу внутри базы данных с использованием подпрограммы db_select_table().

Пример:
if db_select("employees") != DB_OK then
    puts(2, "Couldn't select employees database\n")
end if
См. также: db_open

db_close

Синтаксис: include database.e
db_close()
Описание: Удаляет замок и закрывает текущую базу данных.
Комментарии: Вызывайте эту подпрограмму, когда вы закончили работу с текущей базой данных. Любой из замков будет удален, разрешая другим процессам доступ к файлу базы данных.
См. также: db_open

db_create_table

Синтаксис: include database.e
i = db_create_table(s)
Описание: Создает новую таблицу внутри текущей базы данных. Имя таблицы задается рядом символов, s, и не должно совпадать с именем любой существующей таблицы в текущей базе.
Комментарии: Таблица, которую вы создаете, будет сначала иметь 0 записей. Она становится текущей таблицей.
Пример:
if db_create_table("my_new_table") != DB_OK then
    puts(2, "Couldn't create my_new_table!\n")
end if
См. также: db_delete_table

db_select_table

Синтаксис: include database.e
i = db_select_table(s)
Описание: Таблица с именем, заданным s, становится текущей таблицей. Выдаваемый код, i, будет равен DB_OK, если таблица существует в текущей базе данных, в противном случае вы получите DB_OPEN_FAIL.
Комментарии: Все операции с записями в базе данных автоматически применяются к текущей таблице.
Пример:
if db_select_table("salary") != DB_OK then
    puts(2, "Couldn't find salary table!\n")
    abort(1)
end if
См. также: db_create_table, db_delete_table

db_delete_table

Синтаксис: include database.e
delete_table(s)
Описание: Удаляет таблицу из текущей базы данных. Имя удаляемой таблицы задается в s.
Комментарии: Все записи удаляются и все дисковое пространство, занятое таблицей, освобождается. Если таблица была текущей таблицей, текущая таблица становится неопределенной. Если таблицы с именем, заданным в s, не существует, не происходит ничего.
См. также: db_create_table db_select_table

db_table_list

Синтаксис: s = db_table_list()
Описание: Выдает ряд, содержащий все имена таблиц в текущей базе данных. Каждый элемент ряда s является рядом символов, из которых состоит имя таблицы.
Пример:
sequence names

names = db_table_list()
for i = 1 to length(names) do
    puts(1, names[i] & '\n')
end for
См. также: db_create_table

db_table_size

Синтаксис: include database.e
i = db_table_size()
Описание: Выдает текущее число записей в текущей таблице.
Пример:
-- просмотр всех записей в текущей таблице
for i = 1 to db_table_size() do
    if db_record_key(i) = 0 then
    	puts(1, "0 key found\n")
    	exit
    end if
end for
См. также: db_select_table

db_find_key

Синтаксис: include database.e
i = db_find_key(x)
Описание: Отыскивает запись в текущей таблице по величине ключа x. Если находит, выдается номер записи. Если не находит, выдается отрицательное число, которое соответствовало бы с обратным знаком номеру записи, если бы запись с заданной ключевой величиной была вставлена в базу данных.
Комментарии: Для отыскания ключа применяется эффективный бинарный поиск в текущей таблице. Число сравнений пропорционально логарифму числа записей в таблице.

Вы можете выбрать диапазон записей, задавая первую и последнюю ключевые величины этого диапазона. Если эти ключевые величины не существуют, вы получите отрицательное число, показывающее, где они могли бы быть, если бы существовали.

Пример:
rec_num = db_find_key("Millennium")
if rec_num > 0 then
    ? db_record_key(rec_num)
    ? db_record_data(rec_num)
else
    puts(2, "Not found, but if you insert it,\n")
    puts(2, "it will be #%d\n", -rec_num)
end if
См. также: db_record_key, db_record_data, db_insert

db_record_key

Синтаксис: include database.e
x = db_record_key(i)
Описание: Выдает область ключа, соответствующую записи номер i в текущей таблице.
Комментарии: Каждая запись в базе данных Euphoria состоит из области ключа и области данных. Каждая из этих областей может быть любым атомом или рядом Euphoria.
Пример:
puts(1, "The 6th record has key value: ")
? db_record_key(6)
См. также: db_record_data

db_record_data

Синтаксис: include database.e
x = db_record_data(i)
Описание: Выдает область данных, соответствующую записи номер i в текущей таблице.
Комментарии: Каждая запись в базе данных Euphoria состоит из области ключа и области данных. Каждая из этих областей может быть любым атомом или рядом Euphoria.
Пример:
puts(1, "The 6th record has data value: ")
? db_record_data(6)
См. также: db_record_key

db_insert

Синтаксис: include database.e
i = db_insert(x1, x2)
Описание: Вставляет новую запись в текущую таблицу. Ключ записи должен находиться в x1, данные записи должны находиться в x2. И x1, и x2 могут быть любым из объектов данных Euphoria, атомов или рядов. Выдаваемый код помещается в i.
Комментарии: Внутри таблицы все ключи должны быть уникальными. Операция db_insert() будет неуспешной с выдачей DB_EXISTS_ALREADY, если уже существует запись с той же самой ключевой величиной, что задана в x1.
Пример:
if db_insert("Smith", {"Peter", 100, 34.5}) != DB_OK then
    puts(2, "insert failed!\n")
end if
См. также: db_find_key, db_record_key, db_record_data

db_delete_record

Синтаксис: include database.e
db_delete_record(i)
Описание: Удаляет запись номер i из текущей таблицы.
Комментарии: Номер записи, i, должен быть типа integer с величиной от 1 до числа записей в текущей таблице.
Пример:
db_delete_record(55)
См. также: db_insert, db_table_size

db_replace_data

Синтаксис: include database.e
db_replace_data(i, x)
Описание: Заменяет область данных в записи номер i на x. x может быть любым атомом или рядом Euphoria.
Комментарии: Номер записи, i, должен быть величиной от 1 до числа записей в текущей таблице.
Пример:
db_replace(67, {"Peter", 150, 34.5})
См. также: db_delete_record

db_compress

Синтаксис: include database.e
i = db_compress()
Описание: Сжимает текущую базу данных. Текущая база данных копируется в новый файл так, что все блоки с незанятым дисковым пространством удаляются. В случае успеха i устанавливается в DB_OK, а новый файл базы данных сохраняет старое имя. В качестве резервной копии несжатый файл будет переименован с расширением .t0 (или .t1, .t2 ,..., .t99). Если сжатие не удалось, база данных сохраняется без изменений и резервная копия не создается.
Комментарии: Когда вы удаляете пункты из базы данных, внутри файла остаются блоки свободного дискового пространства. Система сохраняет сведения об этих свободных блоках и старается использовать их для размещения тех новых данных, которые вы вставляете. db_compress() копирует текущую базу данных без копирования свободных блоков. Поэтому размер файла базы данных может после сжатия уменьшиться.

Если имена резервных файлов достигают .t99, вы должны будете удалить некоторые из них.

Пример:
if db_compress() != DB_OK then
    puts(2, "compress failed!\n")
end if
См. также: db_create

db_dump

Синтаксис: include database.e
db_dump(fn, i)
Описание: Распечатывает содержимое уже открытой базы данных Euphoria. Содержимое распечатывается в файл или на устройство fn. Отображаются все записи во всех таблицах. Если i не-ноль, отображается также низкоуровневый по-байтный дамп. Этот низкоуровневый дамп будет понятен только тому, кто хорошо знаком с внутренним форматом базы данных Euphoria.
Пример:
if db_open("mydata", DB_LOCK_SHARED) != DB_OK then
    puts(2, "Couldn't open the database!\n")
    abort(1)
end if
fn = open("db.txt", "w")
db_dump(fn, 0)

См. также: db_open

db_fatal_id

Синтаксис: include database.e
db_fatal_id = i
Описание: Вы можете отлавливать определенные фатальные ошибки базы данных, установив свой собственный обработчик таких ошибок. Просто перепишите значение глобальной переменной db_fatal_id на идентификатор одной из своих собственных процедур. Процедура должна принимать единственный аргумент, который является рядом. Когда эти определенные ошибки будут происходить, ваша процедура будет вызвана с сообщением об ошибке, заданным как строковый аргумент. Ваша процедура должна заканчиваться вызовом abort().
Пример:
procedure my_fatal(sequence msg)
    puts(2, "A fatal error occurred - " & msg & '\n')
    abort(1)
end procedure

db_fatal_id = routine_id("my_fatal")
См. также: db_close