А Ковязин - Мир InterBase. Архитектура, администрирование и разработка приложений баз данных в InterBase/FireBird/Yaffil
Довольно забавно в первый раз узнать, что все метаданные, - как пользовательские таблицы, триггеры, представления, так и все системные объекты, - хранятся в таких же точно таблицах, из которых можно читать и писать данные с помощью обычных SQL-запросов. Эти таблицы "визуально" отличаются только тем, что их имена начинаются с RDB$. Эти 4 символа зарезервированы для имен системных объектов, ни одна пользовательская таблица, столбец или другой объект не имеют права обладать именами, начинающимися с этих символов. Формально ничто не мешает создать вам таблицу, название которой начинается с зарезервированных символов, однако документация InterBase настойчиво не рекомендует этого делать.
Возникает вопрос: если данные о структуре таблиц базы данных хранятся в точно таких же таблицах, как и пользовательские, то где хранится информация о самих этих таблицах, которые описывают таблицы? Классический пример проблемы "курицы и яйца" - как одно могло появиться раньше другого, если они взаимозависимы? Решение состоит в том, что системные таблицы в их первозданном состоянии "прошиты" в исходных кодах InterBase и автоматически разворачиваются при создании базы данных в определенном порядке.
Мы уже говорили о таблице RDB$Pages, которая ставит в соответствие физические страницы в файлах базы данных определенным объектам этой базы данных. Структура этой таблицы приведена в табл. 4.24:
Табл 4.24. Системная таблица RDB$Pages
Column name (имя поля)
Datatype (тип данных)
Description (описание)
RDB$PAGE_NUMBER
INTEGER
Номер физической страницы
RDB$RELATION_ID
SMALLINT
Идентификатор таблицы, для которой распределена страница
RDB$PAGE_SEQUENCE
INTEGER
Порядковый номер этой страницы
RDB$PAGE_TYPE
SMALLINT
Тип страницы - см. таблицу 4.22
Каждая страница данных в базе данных поставлена в соответствие какой- либо таблице, т. е. RELATION. Эту связь поддерживает поле RDB$RE- LATION_ID, в котором хранится ссылка на таблицу. Как было описано выше, в процессе построения внутреннего образа базы, сервер по определенному жестко "зашитому" в него алгоритму строит эту таблицу и наполняет ее данными. Если быть точным, то в момент построения образа базы данных RDB$Pages является не таблицей, а просто массивом данных определенного формата, известного InterBase. На основании определенного алгоритма сервер считывает данные из этого массива и строит следующую критическую для всей базы таблицу - RDB$Relations. Эта таблица описывает все таблицы базы. Если мы сделаем SQL-запрос: SELECT " from RDB$Relations с целью выяснить, ссылки на какие таблицы содержит RDBSRELATIONS, гочви шм. что она содержит и RDBSPages, и саму себя. Очевидно, что в данном случае сервер слегка "хитрит", "задним числом" подставляя эти и остальные системные таблицы в RDB$Relations, таким образом, "легализуя" их. Сервер регистрирует их как "нормальные" таблицы, в которые можно добавить записи или удалить, т. е. предоставляет стандартный SQL-интерфейс для работы с метаданными.
Может возникнуть вполне резонный вопрос: зачем бы разработчикам InterBase "подстраивать" свои системные данные под пользовательский интерфейс? Ведь внутренние механизмы доступа и чтения были бы быстрее. Разумеется, есть большой резон в том, чтобы предоставить универсальный механизм работы с таблицами, описывающими метаданные.
Дело в том. что логическая структура базы данных состоит не только из таблиц, но и из других объектов. В InterBase существуют следующие объекты:
* Table (таблица);
* View (представление);
* Trigger (триггер);
* Computed_field (вычисляемое поле);
* Validation (проверка);
* Procedure (процедура);
* Epression_index (вычисляемый индекс);
* Exception (исключение);
* User (польюватель),
* Field (поле);
* Index (индекс);
* Usei-Defined Function (UDF) - функция, определяемая пользователем.
Пока, может быть, не совсем очевидно назначение некоторых из этих объектов, но ясно, что их необходимо описывать и хранить в некотором виде, удобном как для пользователя, так и для доступа из ядра InterBase. Лучше всего это сделать, сохранив все эти объекты в системных таблицах. Их добавление и модификация производятся с помощью SQL-запросов. Не правда ли, элегантное решение9 Реализация сервера полностью отделена от конкретной базы данных — все взаимосвязи описываются SQL и его расширениями - языком хранимых процедур и триггеров.
Итак, все объекты сервера хранятся в таблицах. Для каждого вида объектов существует таблица, описывающая все экземпляры, описанные в базе данных. Например, для триггеров есть таблица RDB$Triggers, для процедур - RDBSProcedures, а представления описываются в таблице RDBSRelations.
Рассмотрим подробнее структуру последней таблицы, описывающей все таблицы и представления в базе данных. Структура таблицы RDBSRELATIONS вта из Language Reference for InterBase 6 и приведена в Табл 4 25
Табл 4.25. Системная таблица RDB$Relations
Колонка
Тип данных
Длина
Описание
RDB$VIEW_BLR
BLOB
80
BLR: Для представлений (view), содержит BLR (Binary Language Representation) запроса, который InterBase осуществляет каждый раз, когда идет обращение к представлению
RDB$VIEW SO URGE
BLOB
80
Текст: Для представлений содержит код SQL-запроса, который реализует это представление
RDB$ DESCRIP TION
и
80
Пользовательское описание таблицы или представления
RDBSRELATIO NJD
SMALLINT
Содержит внутренний идентификатор таблицы/представления
RDB$SYSTEM FLAG
II
Определяет тип содержимого таблицы: Пользовательские данные - 0; Системная информация > 0
RDB$DBKEY LE NGTH
N
Длина db$key
RDB$FORMAT
II
Зарезервировано для внутреннего использования InterBase Содержит счетчик изменений метаданных для данной таблицы
RDB$FIELD_ID
u
Число полей в таблице
RDB$RELATIO N_NAME
CHAR
31
Уникальное имя таблицы
В описании этой системной таблицы встречается аббревиатура BLR Чтобы понять, что это такое, придется сделать небольшой экскурс в SQL. Как известно, представления, триггеры и хранимые процедуры - это код, написанный на расширении языка SQL (для каждого сервера СУБД существуют свои расширения. Он приближен к человеческому языку, что позволяет легко составлять на нем запросы Но InterBase, очевидно, преобразовывает его во что-то более "машинное", а именно в BLR (Binary Language Representation), "твоичное" представление языка (SQL, очевидно). Любой запрос, триггер, представление, хранимая процедура обязательно транслируются в BLR, а уж затем передаются для исполнения ядру InterBase.
BLR
BLR - это специальный язык, используемый в качестве промежуточного звена между SQL-кодом, который пишет программист, и машинным кодом, который "воспринимает" сервер. Никто не пишет непосредственно на BLR - это было бы весьма затруднительно, так как для максимального быстродействия в этом языке используется так называемая обратная польская запись. Вот маленький пример: blr_begin,
bir_assignment,
blr_field, 0, 7, 'D1 , 'А', "I" , 'Е1 , 'I', 'Z', 'М1 ,
blr_variable, 1,0,
blr_assignment,
blr_field, 0, 4, 'R1,'A1,'Т','E',
blr_variable, 0,0, blr_block,
BLR для ваших запросов, процедур, триггеров и других триггеров формируется с помощью специального препроцессора, входящего в состав ядра сервера. Как показано в табл. 4.25. для представлений (VIEW) хранится как их текстовый (исходный) вид. гак и скомпилированный вид, т. е. BLR При обращении к любому объекту имеющему BLR, сервер выполняем бинарный код объекта, а не интерпретирует каждый раз заново исходный текст этих объектов, что позволяет значительно ускорять выполнение сложных запросов.
Иерархия объектов в InterBase
Чтбы более четко представлять себе, что такое объекты базы данных, мы попробуем построить иерархию объектов базы данных, исходя из принципа "кто кого содержит". Первыми нужно включить в нашу иерархию физические страницы файлов базы данных, как самый низкий уровень организации данных. Затем очевидно, идут таблицы - основополагающие объекты, которые описывают все остальные типы объектов. Таблицы описывают хранимые процедуры, триггеры, вычисляемые поля, проверки, вычисляемые индексы, исключения и т. д. Обратите внимание: только описывают! Таблицы лишь содержат декларации и определения этих объектов, а сами объекты реализуются через BLR. Поэтому мы можем изобразить таблицы в виде некоторой "рамы", поддерживающей все остальные объекты базы данных. В основание рамы мы положим BLR - как прокладку "реализации", затем поместим "слой" триггеров, хранимых процедур, вычисляемых индексов и представлений.