Заметки о системных таблицах InterBase

Источник: IDB Software
Смирнов Александр

Системные таблицы InterBase содержат метаданные базы данных. Они создаются автоматически сервером InterBase, когда создается сама база данных. Информация, содержащаяся в этих таблицах, определяет типы полей таблиц, их названия, связи между таблицами и пр. Эти таблицы сопровождаются сервером, и их, конечно, лучше не менять. Я бы сказал, что лучше принять все меры к тому, что бы они были недоступны пользователю.

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

Попробуем немного побеседовать на эту тему и рассмотрим некоторые SQL предложения для получения информации из системных таблиц. Для более полного понимания рекомендую скачать физическую модель служебных таблиц, выполненную в PowerDesigner DataArchitect 6.x.

Итак, по порядку:

Псевдонимы типов

Псевдонимы типов (Domains) определяют характеристики полей в таблицах базы данных, т.е. тип поля, значение по умолчанию и пр. Практически вся информация о псевдонимах содержится в таблице rdb$fields. Приведу несколько примеров SQL запросов, для получения этой информации.

  select rdb$field_name from rdb$fields
  where rdb$system_flag = 0
  order by rdb$field_name asc;

Этот SQL запрос выбирает все псевдонимы типов, которые были созданы для пользовательских таблиц (where rdb$system_flag = 0). Если Вы напишите условие следующим образом: where rdb$system_flag = 1, то получите список псевдонимов, на базе которых построены системные таблицы базы данных. Надо сказать, что имена системных псевдонимов типов хорошо продуманы и несут в себе смысловую нагрузку.

  select * from rdb$fields
  where rdb$field_name = "BUDGET";

Этот SQL запрос выдает полную информацию о псевдониме с именем BUDGET. Я думаю, что следует дать некоторые комментарии по поводу результата запроса.

Поле RDB$COMPUTED_SOURCE содержит выражение, по которому считается значение вычисляемого столбца, определенного на основе этого псевдонима.

RDB$DEFAULT_SOURCE - тут все ясно из названия. Это поле определяет, какое значение будет подставляться в столбец таблицы, если ничего не введено.

RDB$FIELD_LENGTH, RDB$FIELD_SCALE - это соответственно длина столбца и масштаб для числовых типов данных.

RDB$FIELD_TYPE - поле, содержащее тип данных столбца, построенного на основе данного псевдонима. Эта информация представлена в виде короткого целого, т.е. числа. Расшифровка значений этих чисел приведена в документации по InterBase, но я продублирую ее и здесь.

SMALLINT 7 INTEGER 8
QUAD 9 FLOAT 10
D_FLOAT 11 CHAR 14
DOUBLE 27 DATE 35
VARCHAR 37 BLOB 261

Есть еще несколько интересных полей. RDB$FIELD_SUB_TYP определяет типы BLOB полей. Значения в этом поле тоже представлены в виде короткого целого. Расшифровку можно посмотреть в документации к InterBase.

RDB$NULL_FLAG определяет, может ли столбец таблицы содержать NULL значение. Если в этом поле стоит 1, то NULL значение в столбце, определенном на этом псевдониме, содержаться не может.

RDB$NULL_FLAG определяет, может ли столбец таблицы содержать NULL значение. Если в этом поле стоит 1, то NULL значение в столбце, определенном на этом псевдониме, содержаться не может.

Таблицы

Имена таблиц, содержащихся в базе данных, и еще некоторую информацию о них можно получить из таблицы RDB$RELATIONS.

  select rdb$relation_name from rdb$relations
  where rdb$system_flag = 0
  order by rdb$relation_name asc;

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

  select rdb$relation_name from rdb$relations
  where (rdb$system_flag = 0) and (rdb$view_source is null)
  order by rdb$relation_name asc;

Видно, что здесь отсечены записи, в которых присутствует определение выражения, на котором базируется представление.

  select rdb$relation_name, RDB$FIELD_ID, RDB$OWNER_NAME
  from rdb$relations
  where (rdb$system_flag = 0) and (rdb$view_source is null)
  order by RDB$FIELD_ID;

Это SQL выражение вернет нам информацию об именах таблиц, количестве полей в таблице и имя пользователя, создавшего таблицу. Все изменения в метаданных, относящихся к таблицам, фиксируются InterBase сервером. И эта информация находится в таблицах rdb$relations и RDB$FORMATS. В таблице RDB$FORMATS есть счетчик изменений, но он ограничен количеством 256. Когда счетчик достигнет этого значения, то дальнейшие изменения метаданных будут невозможны, пока не будет выполнена операция backup/restore. Модифицируем предыдущий запрос:

  select a.RDB$RELATION_NAME,a.RDB$FIELD_ID,a.RDB$OWNER_NAME, Max(b.RDB$FORMAT)
  from RDB$RELATIONS a, RDB$FORMATS b
  where a.RDB$RELATION_ID = b.RDB$RELATION_ID and a.RDB$SYSTEM_FLAG = 0
        and (a.rdb$view_source is null)
  group by a.RDB$RELATION_NAME, a.RDB$FIELD_ID, a.RDB$OWNER_NAME
  order by 4 desc;

Последний столбец будет содержать максимальное значение этого счетчика для каждой таблицы.

  select a.rdb$field_name, a.rdb$field_position, a.rdb$null_flag,
   a.rdb$field_source, a.rdb$default_source, b.rdb$computed_source,
   b.rdb$field_length, b.rdb$field_scale, b.rdb$field_type, B.rdb$field_sub_type
  from rdb$relation_fields a, rdb$fields b
  where a.rdb$field_source = b.rdb$field_name and A.rdb$relation_name = "EMPLOYEE"
  order by a.rdb$field_position asc;

Приведенный выше запрос возвращает структуру конкретной таблицы EMPLOYEE. Выбранные поля имеют следующую смысловую нагрузку:

rdb$field_name Имя поля, должно быть уникальное для таблицы.
rdb$field_position Позиция поля в структуре таблицы.
rdb$null_flag Определяет, может ли поле содержать NULL значение. 1 - не может.
rdb$field_source Имя псевдонима типа, на котором основано это поле.
rdb$default_source Содержит значение по умолчанию для этого поля.
rdb$computed_source Содержит выражение для вычисления значения поля.
rdb$field_length Длина поля.
rdb$field_scale Масштаб числовых типов данных.
rdb$field_type Тип поля.
rdb$field_sub_type Подтип поля, для уточнения типа BLOB полей.

Нужно заметить, что тип поля будет представлен в виде числа. Это удобно для дальнейшего программного анализа, но совсем не наглядно. Информация о всевозможных типах, в том числе и о типах полей, содержится в таблице RDB$TYPES. Попробуем модифицировать запрос:

  select a.rdb$field_name, a.rdb$field_position, a.rdb$null_flag,
     a.rdb$field_source, a.rdb$default_source, b.rdb$computed_source,
     b.rdb$field_length, b.rdb$field_scale, c.RDB$TYPE_NAME, B.rdb$field_sub_type
  from rdb$relation_fields a, rdb$fields b, RDB$TYPES c
  where (a.rdb$field_source = b.rdb$field_name) and (b.RDB$FIELD_TYPE = c.rdb$type)
        and (c.RDB$FIELD_NAME = "RDB$FIELD_TYPE")
        and (a.rdb$relation_name = "EMPLOYEE")
  order by a.rdb$field_position asc;

Здесь тип поля будет представлен текстовой строкой.

Псевдонимы типов - это достаточно мощный механизм, позволяющий описать свойства будущих полей. Создав псевдоним типа, Вы в дальнейшем будете использовать его при создании таблиц, таким образом, упростив запись предложения CREATE TABLE при создании таблицы и сделав его более наглядным. Следующий SQL запрос покажет список псевдонимов, созданных пользователем, которые были использованы при создании таблицы.

  select rdb$field_source
  from rdb$relation_fields
  where (rdb$relation_name = "EMPLOYEE")
   and not (rdb$field_source STARTING WITH "RDB$");

Ограничения

Поговорим немного об ограничениях, которые Вы накладываете на таблицы, что бы они удовлетворяли условиям ссылочной целостности или содержали в полях значения, удовлетворяющие определенным правилам. Одно такое ограничение уже упоминалось. Это поле rdb$null_flag из таблицы rdb$relation_fields, которое информирует Вас о том, может ли столбец таблицы содержать значение NULL.

  select RDB$CONSTRAINT_NAME, RDB$CONSTRAINT_TYPE, RDB$INDEX_NAME
  from rdb$relation_constraints
  where rdb$relation_name = quot;EMPLOYEE";

Этот запрос возвращает информацию почти обо всех ограничениях, наложенных на таблицу. Поле RDB$CONSTRAINT_NAME - это имя ограничения. RDB$INDEX_NAME - имя индекса для ограничений типа UNIQUE, PRIMARY KEY и FOREIGN KEY. Достаточно легко получить список ограничений определенного типа, например:

  SELECT RDB$CONSTRAINT_NAME
  FROM RDB$RELATION_CONSTRAINTS
  WHERE (RDB$RELATION_NAME = "EMPLOYEE") and (rdb$constraint_type = "CHECK");

Этот запрос выдает список имен CHECK ограничений. Рассмотрим подробнее каждый тип ограничения.

CHECK ограничения базируются на триггерах. Эти триггеры создаются InterBase сервером автоматически при создании соответствующего CHECK ограничения. Однако можно посмотреть, какие триггеры работают при обработке ограничения.

  select rdb$trigger_name
  from rdb$check_constraints
  where rdb$constraint_name = "INTEG_30";

В условии отбора указывается имя ограничения, полученное предыдущим SQL запросом. Нужно заметить, что для ограничений этого типа создается два триггера типа BEFORE INSERT и BEFORE UPDATE.

О том, как получить информацию о триггерах, мы поговорим ниже.

Ограничения типа UNIQUE, PRIMARY KEY и FOREIGN KEY базируются на индексах, которые также создаются InterBase сервером автоматически. Можно получить список имен ограничений и соответствующие имена индексов следующим запросом:

  SELECT RDB$CONSTRAINT_NAME, RDB$INDEX_NAME
  FROM RDB$RELATION_CONSTRAINTS
  WHERE (RDB$RELATION_NAME = "EMPLOYEE") AND
        (RDB$CONSTRAINT_TYPE = "FOREIGN KEY");

Имея имя индекса, можно просмотреть на базе каких полей построен этот индекс, активен ли он и т.д. Об этом мы поговорим чуть позже.

Индексы FOREIGN KEY поддерживают ссылочную целостность между таблицами в базе данных. При помощи их устанавливается связь, и им соответствуют индексы PRIMARY KEY. Среди служебных таблиц базы данных есть таблица, которая связывает пары FOREIGN KEY и PRIMARY KEY. Это RDB$INDICES. Учитывая все это можно определить, с какой таблицей связана данная таблица по выбранному индексу FOREIGN KEY. Следующий SQL возвращает имя таблицы и индекс PRIMARY KEY, с которым осуществляет связь индекс FOREIGN KEY. В данном случае это RDB$FOREIGN9.

  select rdb$relation_name , rdb$index_name
  from rdb$indices where rdb$index_name in
   (select rdb$foreign_key
      from rdb$indices
      where rdb$index_name = "RDB$FOREIGN9");

Учитывая, что таблица RDB$REF_CONSTRAINTS содержит список всех вторичных ключей с поставленными в соответствие первичными ключами, то можно попробовать определить пары таблиц master-detail.

  SELECT a.RDB$RELATION_NAME,a.RDB$INDEX_NAME,
  c.RDB$RELATION_NAME, c.RDB$INDEX_NAME
  FROM RDB$RELATION_CONSTRAINTS a, RDB$REF_CONSTRAINTS b,
       RDB$RELATION_CONSTRAINTS c
  WHERE (a.RDB$CONSTRAINT_NAME = b.RDB$CONSTRAINT_NAME)
        and (b.RDB$CONST_NAME_UQ = c.RDB$CONSTRAINT_NAME)
  order by a.RDB$RELATION_NAME;

Этот SQL-запрос, кроме пар таблиц master-detail, возвращает и имена соответствующих индексов PRIMARY KEY и FOREIGN KEY. Эти поля можно убрать и тогда Вы получите просто таблицу связей между таблицами.

Индексы

Индексы - важный объект базы данных. Как Вы уже видели, они используются для построения ограничений типа PRIMARY KEY, FOREIGN KEY и UNIQUE. Но, кроме этих важных индексов, создаваемых SQL сервером автоматически, в базе данных присутствуют и индексы, созданные пользователем. Они употребляются для улучшения доступа к данным, обеспечения сортировок.

Несколькими примерами (далеко не исчерпывающими все возможности) я хочу показать, какую информацию можно почерпнуть из служебных таблиц базы данных касаемо индексов.

  select RDB$INDEX_NAME
  from rdb$indices
  where rdb$relation_name = "CUSTOMER";

Это выражение вернет Вам список всех индексов, принадлежащих указанной таблице (CUSTOMER). Но в результате будут содержаться все индексы, а не только созданные пользователем. Попробуем выделить только созданные пользователем индексы.

  select RDB$INDEX_NAME
  from rdb$indices
  where (rdb$relation_name = "CUSTOMER")
  AND NOT (RDB$INDEX_NAME STARTING WITH "RDB$");

Индексы обладают и другими свойствами, а не только именем. Попробуем узнать некоторые из них.

  select RDB$INDEX_NAME, RDB$UNIQUE_FLAG, RDB$INDEX_INACTIVE, RDB$STATISTICS
  from rdb$indices
  where (rdb$relation_name = "CUSTOMER")
        AND NOT (RDB$INDEX_NAME STARTING WITH "RDB$");

Поле RDB$UNIQUE_FLAG определяет, позволяет ли индекс двойные значения. Если это поле имеет значение 1, то двойных значений быть не может. RDB$INDEX_INACTIVE показывает, работает ли индекс в данный момент, т.е. активен ли он. Когда в этом поле стоит значение 0, то индекс активен. RDB$STATISTICS - коэффициент избирательности, используется оптимизатором для выбора стратегии доступа при выполнении запроса.

Всем известно, что индексы строятся по одному или нескольким полям таблицы. Приведем пример того, как можно узнать, на базе каких полей построен индекс. Информация о полях, входящих в индекс содержится в таблице RDB$INDEX_SEGMENTS.

  select RDB$FIELD_NAME, RDB$FIELD_POSITION
  from rdb$index_segments where rdb$index_name = "CUSTREGION"
  order by rdb$field_position;

Этот запрос вернет нам список полей, по которым построен индекс, а также порядок полей в индексе.

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

  SELECT 'alter index '// RDB$INDEX_NAME //' INACTIVE;'
  FROM RDB$INDICES
  WHERE NOT (RDB$INDEX_NAME STARTING WITH "RDB$");

Этот SQL-запрос даст Вам набор строк-команд для деактивации индексов. Поочередно выбирая получившиеся строки в программе, можно использовать их как команды для деактивизации соответствующего индекса. Если этот запрос выполнить в Windows ISQL, то из области результата можно будет скопировать скрипт, выполняющий те же действия. Следующий запрос дает набор команд для обратной процедуры.

  SELECT 'alter index '// RDB$INDEX_NAME //' ACTIVE;'
  FROM RDB$INDICES
  WHERE NOT (RDB$INDEX_NAME STARTING WITH "RDB$");

Деактивизация индексов полезна и тогда, когда нужно добавить большое количество записей.

Индекс имеет такой показатель, как "полезность", который используется InterBase-сервером для выбора оптимального плана удовлетворения запросов. Его рекомендуется пересчитывать. Для получения скрипта, делающего это для всех индексов, можно попробовать следующий запрос.

  SELECT 'SET STATISTICS INDEX '// RDB$INDEX_NAME //';'
  FROM RDB$INDICES
  WHERE NOT (RDB$INDEX_NAME STARTING WITH "RDB$");

Триггеры

Триггер - это процедура базы данных, которая автоматически вызывается SQL сервером при возникновении определенных событий (добавление, удаление, обновление записей).

Попробуем узнать, какие триггеры есть в базе данных. Информация о триггерах содержится в служебной таблице RDB$TRIGGERS.

  select RDB$TRIGGER_NAME from RDB$TRIGGERS
  where (RDB$SYSTEM_FLAG = 0) or (RDB$SYSTEM_FLAG is NULL);

Условие этого SQL-запроса позволяет выбрать только триггеры, созданные пользователем. Однако нужно отметить, что в результате запроса будут и триггеры, обеспечивающие ограничения CHECK. Эти триггеры создаются не явно, когда Вы описываете ограничение. Как же получить список триггеров, действительно созданных пользователем. Все триггеры, обеспечивающие CHECK ограничения, перечислены в таблице RDB$CHECK_CONSTRAINTS. Поэтому следующим запросом мы можем их отсечь.

  select RDB$TRIGGER_NAME from RDB$TRIGGERS
  where ((RDB$SYSTEM_FLAG = 0) or (RDB$SYSTEM_FLAG is NULL)) and
            (RDB$TRIGGER_NAME not in
        (select RDB$TRIGGER_NAME from RDB$CHECK_CONSTRAINTS));

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

В той же таблице RDB$TRIGGERS находятся и другие параметры триггера. Попробуем извлечь их.

   select RDB$TRIGGER_NAME, RDB$RELATION_NAME, RDB$TRIGGER_SEQUENCE,
   RDB$TRIGGER_TYPE, RDB$TRIGGER_SOURCE, RDB$TRIGGER_INACTIVE
  from RDB$TRIGGERS
  where ((RDB$SYSTEM_FLAG = 0) or (RDB$SYSTEM_FLAG is NULL)) and
           (RDB$TRIGGER_NAME not in
        (select RDB$TRIGGER_NAME from RDB$CHECK_CONSTRAINTS));

В результате мы получим не просто список триггеров, а узнаем, какой таблице триггер принадлежит (RDB$RELATION_NAME), порядковый номер триггера (RDB$TRIGGER_SEQUENCE), определяющий, в какой последовательности триггеры будут выполнятся, если они одного типа и принадлежат одной таблице, тип триггера (RDB$TRIGGER_TYPE), исходный код триггера (RDB$TRIGGER_SOURCE). Кроме того, мы узнаем состояние триггера, активен ли он в данный момент (RDB$TRIGGER_INACTIVE). Значение этого поля, установленное в 0, говорит о том, что триггер активен. Скажем еще пару слов о типе триггера. Поле RDB$TRIGGER_TYPE имеет тип короткого целого, и в нем числами зашифрован тип триггера. Посмотрим, можно ли представить эти значения более читабельными. Различная информация о подобных зашифрованных типах содержится в таблице RDB$TYPES.

   select a.RDB$TRIGGER_NAME, a.RDB$RELATION_NAME, a.RDB$TRIGGER_SEQUENCE,
   b.RDB$TYPE_NAME, a.RDB$TRIGGER_SOURCE, a.RDB$TRIGGER_INACTIVE
  from RDB$TRIGGERS a, RDB$TYPES b
  where (a.RDB$TRIGGER_TYPE=b.RDB$TYPE) and
        (b.RDB$FIELD_NAME="RDB$TRIGGER_TYPE") and
        ((a.RDB$SYSTEM_FLAG = 0) or (a.RDB$SYSTEM_FLAG is NULL))
        and (a.RDB$TRIGGER_NAME not in (select RDB$TRIGGER_NAME
  from RDB$CHECK_CONSTRAINTS));

Поле b.RDB$TYPE_NAME будет содержать расшифровку типа триггера, хотя она и отличается от той, к которой мы привыкли. На всякий случай приведу таблицу сооветствия значений типов, взятых из разных источников.

Код Значение из документации Значение из таблицы RDB$TYPES
1 BEFORE INSERT PRE_STORE
2 AFTER INSERT POST_STORE
3 BEFORE UPDATE PRE_MODIFY
4 AFTER UPDATE POST_MODIFY
5 BEFORE DELETE PRE_ERASE
6 AFTER DELETE POST_ERASE

Триггеры - это очень хорошо. Но бывают ситуации, когда нужно приостановить их работу. Например: нужно перенести данные из базы данных формата dbf, где ссылки между таблицами определяются значениями в некоторых полях. Самое простое - это перенести все значения как они есть, чтоб не нарушать ссылочную целостность, но, если в базе данных InterBase существуют триггеры, генерирующие уникальные значения, то возможет конфликт или нарушение ссылок. Сам попадался на этом. По этому было бы не плохо временно остановить работу триггеров. Попробуем получить скрипт для выполнения подобной операции.

  SELECT "ALTER TRIGGER " // RDB$TRIGGER_NAME // "INACTIVE;"
  FROM RDB$TRIGGERS
  WHERE ((RDB$SYSTEM_FLAG = 0) OR (RDB$SYSTEM_FLAG IS NULL)) AND
        (RDB$TRIGGER_NAME NOT IN
        (SELECT RDB$TRIGGER_NAME FROM RDB$CHECK_CONSTRAINTS));

Если выполнить этот SQL-запрос в Windows ISQL, то из области результата можно скопировать готовый скрипт. Учтите, что в него попадут, не только триггеры, гинерирующие уникальные значения полей, но и другие, активность которых вполне можкт быть уместной. Поэтому рекомендую, получившийся скрипт скорректировать.

Представления (Views)

Представления помогают нам организовать доступ к информации, находящейся в одной или нескольких таблицах. Особенно это удобно, когда такую информацию нужно получать часто, а условия отбора остаются неизменными. Можно ли узнать, какие представления есть в базе данных? Они перечислены в таблице RDB$RELATIONS вместе с таблицами. Попробуем их получить следующим SQL-запросом.

  select rdb$relation_name
  from rdb$relations
  where (rdb$system_flag = 0) and rdb$view_source is not null;

В условии отбора мы указали, что нас интересуют только объекты, созданные пользователем (rdb$system_flag = 0), и имеют заполненное поле rdb$view_source, что говорит о том, что мы имеем дело с представлением.

Известно, что представления строятся на базе SQL-запросов. Попробуем получить текст этого запроса для некоторого представления.

  select rdb$view_source
  from rdb$relations
  where rdb$relation_name = "PHONE_LIST";

Поле rdb$view_source имеет тип MEMO, и в нем содержится текст SQL.

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

   SELECT A.RDB$FIELD_NAME, A.RDB$FIELD_POSITION, A.RDB$NULL_FLAG,
     A.RDB$FIELD_SOURCE, A.RDB$DEFAULT_SOURCE,
     B.RDB$COMPUTED_SOURCE, B.RDB$FIELD_LENGTH,
     B.RDB$FIELD_SCALE, B.RDB$FIELD_TYPE, B.RDB$FIELD_SUB_TYPE
  FROM RDB$RELATION_FIELDS A, RDB$FIELDS B
  WHERE A.RDB$FIELD_SOURCE = B.RDB$FIELD_NAME AND A.RDB$RELATION_NAME = "PHONE_LIST"
  ORDER BY A.RDB$FIELD_POSITION ASC;

Процедуры

Сохраненные процедуры - готовые части кода, расположенные в базе данных и выполняющиеся на сервере. Что мы можем узнать про них из системных таблиц базы данных. Да, практически, все! Наснем с того, что получим список процедур.

  select rdb$procedure_name
  from rdb$procedures
  where rdb$system_flag = 0;

Все процедуры и основные их параметры содержатся в таблице rdb$procedures. Попробуем узнать побольше о какой-либо процедуре.

  select RDB$PROCEDURE_INPUTS, RDB$PROCEDURE_OUTPUTS,
  RDB$PROCEDURE_SOURCE, RDB$OWNER_NAME
  from rdb$procedures
  where rdb$procedure_name = "DEPT_BUDGET";

Показанный выше SQL-запрос вернет количество входных и выходных параметров, исходный текст тела процедуры и имя пользователя, создавшего процедуру.

Попробуем узнать про входные и выходные параметры подробнее.

   select a.RDB$PARAMETER_NAME, a.RDB$PARAMETER_NUMBER,
     a.RDB$PARAMETER_TYPE, b.RDB$VALIDATION_SOURCE, b.RDB$COMPUTED_SOURCE,
     b.RDB$DEFAULT_SOURCE, b.RDB$FIELD_LENGTH,
     b.RDB$FIELD_SCALE, b.RDB$FIELD_TYPE, b.RDB$FIELD_SUB_TYPE, b.RDB$NULL_FLAG
  from RDB$PROCEDURE_PARAMETERS a, RDB$FIELDS b
  where (a.RDB$FIELD_SOURCE=b.RDB$FIELD_NAME) and
        (a.RDB$PROCEDURE_NAME="DEPT_BUDGET")
  order by a.RDB$PARAMETER_TYPE, a.RDB$PARAMETER_NUMBER;

Этот запрос связывает две таблицы RDB$PROCEDURE_PARAMETERS и RDB$FIELDS и возвращает имя параметра, номер параметра среди параметров одного типа, тип параметра (0-входной, 1-выходной), а далее уже знакомые поля из таблицы RDB$FIELDS.

Функции

Функции позволяют наращивать возможности InterBase-сервера за счет подключения к базе данных модулей из dll, которые могут быть реализованы на любом языке программирования. Прежде чем пользоваться функциями, давайте сначала узнаем, а какие функции декларированы в базе данных. Основная информация о функциях находится в таблицах RDB$FUNCTIONS и RDB$FUNCTION_ARGUMENTS.

  select rdb$function_name
  from rdb$functions
  where ((rdb$system_flag = 0) or (rdb$system_flag is null));

Этот запрос вернет Вам список всех декларированных функций. Но это только список. Давайте посмотрим, где находятся эти функции и под каким именем их вызывать из библиотек.

  select RDB$FUNCTION_NAME, RDB$MODULE_NAME,
  RDB$ENTRYPOINT, RDB$RETURN_ARGUMENT
  from rdb$functions
  where ((rdb$system_flag = 0) or (rdb$system_flag is null));

Приведенный выше запрос возвращает, кроме наименования функции, под которым она известна в базе данных, наименование модуля, точку входа (наименование в модуле), позицию аргумента, возвращаемого программе вызова. Сами аргументы функции содержаться в таблице RDB$FUNCTION_ARGUMENTS. Покажем, как можно узнать, какие аргументы имеет некоторая функция.

   SELECT a.RDB$FUNCTION_NAME, b.RDB$ARGUMENT_POSITION,
     b.RDB$MECHANISM, b.RDB$FIELD_TYPE,
     b.RDB$FIELD_SCALE, b.RDB$FIELD_LENGTH, b.RDB$FIELD_SUB_TYPE
  FROM RDB$FUNCTIONS a, RDB$FUNCTION_ARGUMENTS b
  WHERE (a.RDB$FUNCTION_NAME = b.RDB$FUNCTION_NAME) and
        ((a.RDB$SYSTEM_FLAG = 0) OR (a.RDB$SYSTEM_FLAG IS NULL))
        and (b.RDB$ARGUMENT_POSITION<>a.RDB$RETURN_ARGUMENT);

Этот запрос возвратит для кажной функции информацию о входных аргументах (их порядок, механизм передачи, о котором можно почитать в документации, а далее уже известные вещи: тип, длина и т.д.). Условие (b.RDB$ARGUMENT_POSITION <> a.RDB$RETURN_ARGUMENT) отбирает только входные аргументы. Если нужно посмотреть выходные аргументы, то можно воспользоваться следующим запросом.

   SELECT a.RDB$FUNCTION_NAME, b.RDB$ARGUMENT_POSITION,
    b.RDB$MECHANISM, b.RDB$FIELD_TYPE,
    b.RDB$FIELD_SCALE , b.RDB$FIELD_LENGTH, b.RDB$FIELD_SUB_TYPE
  FROM RDB$FUNCTIONS a, RDB$FUNCTION_ARGUMENTS b
  WHERE (a.RDB$FUNCTION_NAME = b.RDB$FUNCTION_NAME) and
        ((a.RDB$SYSTEM_FLAG = 0) OR (a.RDB$SYSTEM_FLAG IS NULL))
        and (b.RDB$ARGUMENT_POSITION = a.RDB$RETURN_ARGUMENT);

Генераторы

Генераторы позволяют создавать уникальные идентификаторы, значениями которых удобно заполнять ключевые поля таблиц. Использую служебные таблицы базы данных, можно достаточно легко определить, какие генераторы есть и в какие значения они установлены. Вся информация о генераторах сосредоточена в таблице RDB$GENERATORS. Посмотрим, как можно получить список генераторов, созданных пользователем.

  select rdb$generator_name
  from rdb$generators
  where ((rdb$system_flag = 0) or (rdb$system_flag is null));

Следующий SQL-запрос позволит посмотреть текущие значения генераторов.

select distinct gen_id(CUST_NO_GEN, 0) from RDB$GENERATORS;

Этот запрос возвращает значение генератора CUST_NO_GEN.

Исключения

Исключения - это созданные пользователем ошибки с ассоциированными сообщениями, которые могут быть использованы в сохраненных процедурах и триггерах. Основываясь на служебной таблице RDB$EXCEPTIONS, можно узнать какие исключения и с какими сообщениями присутствуют в базе данных.

  select rdb$exception_name, RDB$MESSAGE
  from rdb$exceptions
  where ((rdb$system_flag = 0) or (rdb$system_flag is null));

Зависимости

Представьте себе такую картину: Вы создали таблицу, у нее есть какие-то поля. Потом Вы создали представление, которое выбирает из этой таблицы некоторые поля. И вот у Вас возникла необходимость удалить таблицу или некоторые ее поля. Что будет с представлением. Скорее оно работать не будет. Чтобы подобных ситуаций не происходило, InterBase-сервер отслеживает зависимости между объектами в базе данных и хранит их в служебной таблице RDB$DEPENDENCIES. Описание полей этой таблицы можно найти в документации, а тут я попробую привести примеры запросов, которые позволят посмотреть, какой объект от какого зависит.

  select distinct a.RDB$DEPENDENT_NAME, a.RDB$DEPENDENT_TYPE, b.RDB$TYPE_NAME
  from RDB$DEPENDENCIES a, RDB$TYPES b
  where (a.RDB$DEPENDENT_TYPE=b.RDB$TYPE) and
        (b.RDB$FIELD_NAME="RDB$OBJECT_TYPE")
        and (a.RDB$DEPENDED_ON_NAME = "JOB");

Приведенный выше запрос показывает, какие объекты зависят от объекта JOB. a.RDB$DEPENDENT_NAME - наименование объекта, который зависит от JOB, a.RDB$DEPENDENT_TYPE - тип этого объекта (код), b.RDB$TYPE_NAME - расшифровка типа объекта (представление, процедура и пр.).

А приведенный ниже запрос показывает, от каких объектов зависит объект "PHONE_LIST".

  select a.RDB$DEPENDED_ON_NAME, a.RDB$FIELD_NAME, a.RDB$DEPENDED_ON_TYPE,
     b.RDB$TYPE_NAME
  from RDB$DEPENDENCIES a, RDB$TYPES b
  where (a.RDB$DEPENDED_ON_TYPE=b.RDB$TYPE) and
        (b.RDB$FIELD_NAME="RDB$OBJECT_TYPE") and
        a.RDB$DEPENDENT_NAME="PHONE_LIST";

a.RDB$DEPENDED_ON_NAME - наименование объекта, который используется при работе PHONE_LIST. a.RDB$FIELD_NAME - имена полей вышеупомянутых объектов, которые используются объектом PHONE_LIST. Далее, как и в предыдущем запросе, возвращается тип объекта, содержащегося в a.RDB$DEPENDED_ON_NAME.

Пользователи, права

Для того, что бы получить доступ к базе данных, нужно быть пользователем, прописанным на InterBase-сервере, и иметь пароль. Но этого не достаточно, чтобы оперировать с объектами базы данных. Пользователь должен иметь права на выполнение той или иной операции.

Посмотреть, какие пользователи известны серверу можно, используя базу данных Isc4.gdb.

  select USER_NAME, FIRST_NAME, MIDDLE_NAME, LAST_NAME
  from USERS
  order by USER_NAME;

Этот запрос даст список имен пользователей, а также их имена и фамилии. Помимо пользователей обычных можно определять роли, давать им права, а потом пользователям назначать роли.

Посмотрим, какие роли есть в базе данных.

select * from RDB$ROLES;

Поле RDB$ROLE_NAME - содержит имя роли, а поле RDB$OWNER_NAME - содержит имя пользователя, создавшего роль.

Посмотрим теперь, что можно узнать про права того или иного пользователя, по отношению к объектам базы данных. Информацию об этом можно найти в таблице RDB$USER_PRIVILEGES.

  select RDB$GRANTOR, RDB$PRIVILEGE, RDB$GRANT_OPTION, RDB$RELATION_NAME
  from RDB$USER_PRIVILEGES
  where RDB$USER="SVETA";

Приведенный выше SQL-запрос показывает все права пользователя SVETA. Поле RDB$GRANTOR - содержит имя пользователя, предоставившего это право. RDB$PRIVILEGE - описывает привилегию (расшифровка значений приведена в документации). RDB$GRANT_OPTION определяет, может ли пользователь, получивший эту привилегию, передать ее другому пользователю. Если значение равно единице, то такая возможность есть. RDB$RELATION_NAME - наименование объекта базы данных, для которого привилегия допустима. А как узнать привилегии, назначенные ролям?

  select a.RDB$USER, a.RDB$GRANTOR, a.RDB$PRIVILEGE,
    a.RDB$GRANT_OPTION, a.RDB$RELATION_NAME
  from RDB$USER_PRIVILEGES a, RDB$ROLES b
  where a.RDB$USER = b.RDB$ROLE_NAME;

Этот запрос почти идентичен предыдущему по выдаваемым результатам, кроме поля a.RDB$USER. Оно содержит, в данном случае, наименование роли.

Изменив немного предыдущий запрос, можно узнать, права каких ролей кому предоставлены.

  select a.RDB$USER, a.RDB$GRANTOR, a.RDB$PRIVILEGE,
     a.RDB$GRANT_OPTION, a.RDB$RELATION_NAME
  from RDB$USER_PRIVILEGES a, RDB$ROLES b
  where a.RDB$RELATION_NAME = b.RDB$ROLE_NAME;

В дополнение

В дополнение ко всему выше сказанному вот дописываю.

На днях один знакомый по Internet спросил, как можно изменить параметры поля, не теряя данных (конкретно: поле было NOT NULL, нужно это убрать). Я попробовал для этого следующее SQL-выражение:

  UPDATE RDB$RELATION_FIELDS
  SET RDB$NULL_FLAG = NULL
  WHERE RDB$RELATION_NAME = "CUSTOMER" AND (RDB$FIELD_NAME = "CUSTOMER");

Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=4221