Текст
                    Хелен Борри
(bhv
Л .. £Ш* II
РУКОВОДСТВО РАЗРАБОТЧИКА БАЗ ДАННЫХ
• Проектирование и разработка клиент/серверных
приложений, использующих СУБД
Firebird/lnterbase
• Установка, конфигурация и администрирование
СУБД
• Программирование на стороне сервера и клиента
• Практические советы, рекомендации и примеры


The Firebird Book: A Reference for Database Developers HELEN BORRIE Apresse
Хелен Борри Firebird РУКОВОДСТВО РАЗРАБОТЧИКА БАЗ ДАННЫХ Санкт-Петербург «БХВ-Петербург» 2006
УДК 681.3.06 ББК 32.973.26-018.2 Б82 Борри X. Б82 Firebird: руководство разработчика баз данных: Пер. с англ. — СПб.: БХВ-Петербург, 2006. — 1104 с.: ил. ISBN 5-94157-609-9 Рассмотрены вопросы, необходимые разработчику для создания клиент- серверных приложений с использованием СУБД Firebird, явившейся развитием СУБД Borland Interbase 6. Содержится обзор концепций и моделей архитектуры клиент/сервер, а также практические рекомендации по работе с клиентскими библиотеками Firebird. Детально описаны особенности типов данных SQL, язык манипулирования данными (Data Manipulation Language, DML), а также синтак- сис и операторы языка определения данных (Data Definition Language, DDL). Большое внимание уделено описанию транзакций и приведены советы по их использованию при разработке приложений. Описано программирование на стороне клиента и сервера: написание триггеров и хранимых процедур, создание и использование событий базы данных, обработка ошибок в коде на сервере и многое другое. Материал сопровождается многочисленными примерами, сове- тами и практическими рекомендациями. Для разработчиков баз данных УДК 681.3.06 ББК 32.973.26-018.2 Группа подготовки издания: Главный редактор Зам. главного редактора Зав. редакцией Перевод с английского Редактор Екатерина Кондукова Игорь Шигиигин Григорий Добин Александра Бондаря Екатерина Капалыгина Компьютерная верстка Корректор Дизайн обложки Зав. производством Ольги Сергиенко Зинаида Дмитриева Игоря Цырульникова Николай Тверских Authorized translation from the original English language edition published by Apress L.P., 2560 Ninth Street, Suite 219, Berke- ley, CA 94710 USA. Copyright © 2002 by Apress L.P. Russian-language edition copyright © 2006 by BHV St. Petersburg. All rights reserved. No part of this work may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording, or by any information storage or retrieval system, without the prior written permission of the copyright owner and the publisher. Авторизованный перевод английской редакции, выпущенной Apress L.P., 2560 Ninth Street, Suite 219, Berkeley, CA 94710 USA. Copyright © 2002 by Apress L.P. Все права защищены. Никакая часть настоящей книги не может быть воспроизве- дена или передана в какой бы то ни было форме и какими бы то ни было средствами, будь то электронные или механи- ческие, включая фотокопирование и запись на магнитный носитель, а также через другие носители и информационные системы, если на то нет письменного разрешения собственника прав и издательства. Перевод на русский язык "БХВ-Пе- тербург", © 2006. Лицензия ИД № 02429 от £4.07.00. Подписано в печать 01.11.05. Формат 70х1001/16. Печать офсетная. Усл. печ. л. 89,01. Тираж 3000 экз. Заказ № 420 "БХВ-Петербург", 194354, Санкт-Петербург, ул. Есенина, 5Б. Санитарно-эпидемиологическое заключение на продукцию № 77.99.02.953.Д.006421.11.04 от 11.11.2004 г. выдано Федеральной службой по надзору в сфере защиты прав потребителей и благополучия человека. Отпечатано с готовых диапозитивов в ОАО "Техническая книга" 190005, Санкт-Петербург, Измайловский пр., 29 ISBN 1-59059-279-4 (англ.) ISBN 5-94157-609-9 (рус.) © 2002 by Apress L.P. © 2004 by Helen Bonne and IBPhoenix © Перевод на русский язык "БХВ-Петербург”. 2006
Оглавление Введение..............................................................31 Об авторе.............................................................31 О техническом редакторе...............................................31 О научном редакторе перевода на русский язык..........................31 Благодарности.........................................................32 Введение в Firebird...................................................33 Что такое Firebird?...............................................33 Кому нужна эта книга?.............................................33 Где найти нужную вам информацию?..................................33 Приложения и глоссарий............................................34 Происхождение Firebird............................................35 Проект Firebird...................................................36 Снарки............................................................37 Обзор возможностей................................................37 Версии Firebird...................................................37 Доступ к сети.....................................................38 Многоверсионная архитектура.......................................38 Транзакции........................................................38 Хранимые процедуры и триггеры.....................................39 Ссылочная целостность.............................................39 Оперативное копирование базы данных...............................39 Безопасность......................................................40 Пример базы данных................................................42 Соглашения по документации........................................42 Синтаксические шаблоны............................................42 Специальные символы...............................................43 ЧАСТЬ I. УЧЕБНЫЙ ЛАГЕРЬ...............................................45 Глава 1. Инсталляция..................................................47 Системные требования..................................................47 Память на сервере (все платформы).................................47 Инсталляционные диски.............................................48 Минимальные требования к машине...................................49
6 Оглавление Как получить инсталляционный комплект..................................50 Содержание комплекта...............................................50 Соглашения по именованию в комплекте инсталляции...................51 Зеркальные сайты................................................. 52 Комплект поставки для Linux............................................52 Сжатые файлы (tarballs)............................................54 Реализация NPTL для высших версий Linux............................54 Комплект поставки для Windows..........................................55 Сжатые файлы.......................................................56 Серверы............................................................56 Клиентские библиотеки..............................................56 Тестирование результатов инсталляции...................................57 Сетевой протокол...................................................57 Тестирование сервера...............................................57 Проверка, что сервер Firebird запущен..............................58 Инсталляция встраиваемого сервера......................................61 Пример структуры встроенной инсталляции............................62 Другие вещи, которые вам нужно знать...................................62 Пользователи.......................................................62 Инструмент администратора..........................................64 Размещение на диске по умолчанию.......................................65 Пора дальше............................................................69 Глава 2. Установка сети................................................70 Сетевые протоколы......................................................70 Именованные каналы.....................................................70 Локальный доступ.......................................................70 Клиент-сервер......................................................71 Встраиваемый сервер................................................71 Смешанные платформы....................................................72 Сетевой адрес для сервера..............................................72 Файл HOSTS............................................................ 73 Местонахождение файла HOSTS........................................73 Имя сервера и путь к нему..............................................74 Синтаксис строки соединения........................................75 Конфигурирование сервиса порта TCP/IP..................................76 Как сервер устанавливает прослушиваемый порт.......................77 Установка клиента для поиска порта сервиса.........................80 Конфигурирование файла services....................................81 Проверка соединения с помощью ping.....................................82 Если ping выдает ошибку............................................83 Проблемы с событиями............................................. 83 Другие сетевые проблемы............................................84 Пора дальше............................................................84
Оглавление 7 Глава 3. Конфигурирование Firebird.....................................85 Конфигурация на уровне базы данных.....................................85 Переменные окружения................................................. 85 Где устанавливаются переменные окружения...............................86 Windows............................................................86 POSIX..............................................................87 ISCJJSER и ISC_PASSWORD............................................87 Переменная FIREBIRD (или INTERBASE)................................87 FIREBIRDTMP........................................................88 *_LOCK и *JASG.....................................................88 ТМР................................................................88 Файл конфигурации Firebird.........................................89 Изменение параметров конфигурации..................................89 Пора дальше............................................................93 Глава 4. Основные операции.............................................94 Запуск Firebird на Linux/UNIX..........................................94 Суперсервер........................................................94 Классический сервер................................................96 Запуск сервера Firebird в Windows......................................98 Суперсервер........................................................98 Выполнение Firebird как сервиса в Windows NT, 2000 и ХР............99 Апплеты Firebird Manager..........................................100 Выполнение Firebird как приложения на платформах Windows..............100 Запуск сервера как приложения вручную.............................101 Останов сервера...................................................102 Алиасы базы данных....................................................103 aliases.conf......................................................103 Переносимость.....................................................103 У правление доступом..............................................104 Администрирование баз данных..........................................105 fbmgr/ibmgr.......................................................105 instsvc.exe.......................................................106 gbak..............................................................106 gsec..............................................................106 gfix..............................................................106 gstat.............................................................107 fb Jock print.....................................................107 Введение в isql.......................................................107 Запуск isql.......................................................107 Использование isql................................................107 Оператор CONNECT..................................................108 Создание базы данных с использованием isql........................109 Оператор CREA ТЕ DATABASE.........................................109 Пора дальше...........................................................110
8 Оглавление ЧАСТЬ II. КЛИЕНТ-СЕРВЕР...............................................111 Глава 5. Введение в архитектуру клиент-сервер.........................113 Базы данных клиент-сервер в сравнении с файл-серверами................114 Характеристики СУБД клиент-сервер.....................................114 Масштабируемость..................................................114 Функциональная совместимость......................................115 Защита данных.....................................................115 Распределение функций.............................................115 Двухуровневая модель..............................................116 Многоуровневая модель.............................................117 Стандартизация....................................................117 Проектирование систем клиент-сервер...................................118 Абстракция хранимых данных........................................118 "Руки прочь" от доступа к данным..................................122 Множество пользователей и параллельность..........................122 Пора дальше...........................................................123 Глава 6. Сервер Firebird..............................................124 Роль сервера..........................................................125 Платформы для операционных систем.....................................125 Примеры топологий.....................................................126 Двухуровневая архитектура клиент-сервер...........................126 Однопользовательская модель.......................................127 Серверы Firebird в среде DTP......................................128 Терминальные серверы..............................................130 Базы данных...........................................................130 Программирование на стороне сервера...................................131 Хранимые процедуры................................................131 Триггеры..........................................................131 Функции, определенные пользователем...............................132 Приложения, работающие с несколькими базами данных................132 Безопасность сервера..................................................132 Безопасность базы данных..............................................133 Пора дальше...........................................................133 Глава 7. Клиенты Firebird.............................................134 Что такое клиент Firebird?............................................134 Клиентская библиотека Firebird........................................135 Разработка приложений.................................................136 Динамические приложения клиент-сервер.............................136 Ядро API Firebird.................................................137 Встроенные приложения Firebird....................................138 Сервисы API.......................................................140
Оглавление 9 Инсталляция клиентов................................................140 Инсталляция клиента Linux/UNIX..................................141 Инсталляция клиента Windows.....................................142 Список имен и размещение клиентской библиотеки..................148 Пора дальше.........................................................149 ЧАСТЬ III. ТИПЫ ДАННЫХ FIREBIRD И ДОМЕНЫ............................151 Глава 8. О типах данных Firebird....................................153 Где задаются типы данных............................................153 Поддерживаемые типы данных..........................................153 Булевы типы данных..................................................154 "Диалекты" SQL......................................................154 Идентификаторы с разделителями в SQL-92.............................154 Контекстные переменные..............................................155 Временные значения..............................................156 Примеры использования...........................................157 Предопределенные литералы даты......................................157 Примеры использования предопределенных литералов даты...........158 Столбцы........................................................... 158 Домены..............................................................158 Преобразование типов данных.........................................159 Неявное преобразование типов....................................159 Явное преобразование типов: CASTQ...............................160 Изменение определения столбцов и доменов............................160 Изменение типа данных столбца...................................161 Изменение типа данных домена....................................161 Ключевые слова, используемые для спецификации типа данных...........161 Специальная тема миграции: диалекты SQL.............................162 ODS и диалект...................................................162 Где учитывается диалект.........................................164 Как определять диалект..........................................165 Пора дальше.........................................................166 Глава 9. Числовые типы данных.......................................167 Операции с числовыми типами.........................................168 Целые типы..........................................................168 SMALLINT........................................................169 INTEGER.........................................................169 BIGINT, NUMERIC(I8,0)...........................................169 Автоинкремент или тип IDENTITY..................................170 Генераторы......................................................170 Масштабируемые типы с фиксированной точкой..........................172 Тип данных NUMERIC..............................................173 Тип данных DECIMAL..............................................174
10 Оглавление Конвертированные базы данных....................................174 Специальные ограничения в статическом SQL.......................174 Поведение типов с фиксированной точкой в операциях..............175 Числовой ввод и показатели степени..................................178 Типы данных с плавающей точкой......................................178 FLOAT...........................................................180 DOUBLE PRECISION................................................180 Пора дальше.........................................................181 Глава 10. Типы даты и времени.......................................182 DATE................................................................182 TIMESTAMP...........................................................182 Доли секунды....................................................183 TIME................................................................183 Интервал времени................................................183 Литералы даты.......................................................183 Распознаваемые форматы литералов даты и времени.....................184 "Скользящее окно века" в Firebird...............................185 Разделители в неамериканских датах..............................186 Пробелы в литералах даты........................................186 Заключение в апострофы литералов даты...........................186 Литералы месяца.................................................187 Примеры литералов даты..........................................187 Предварительно определенные литералы даты...........................187 Неявное преобразование типов в литералах даты и времени.............188 Контекстные переменные даты и времени...............................189 Операции, использующие значения даты и времени......................190 Общие правила для операций......................................191 Выражения в качестве операндов..................................191 Использование CASTQ с типами дата/время.............................192 Преобразование между типами дата/время..........................192 Преобразование типов даты в CHAR(n) и VARCHAR(n)................193 Преобразования между типами дата/время и другими типами данных..195 Использование преобразований....................................195 Функция EXTRACTQ....................................................196 Синтаксис.......................................................196 Объединение EXTRACTQ с другими функциями........................197 Понимание функции EXTRACTQ......................................199 Пора дальше.........................................................200 Глава 11. Символьные типы данных....................................201 Основы использования строк..........................................201 Ограничитель строки.............................................201 Конкатенация....................................................202 Управляющие символы.............................................202
Оглавление 11 Ограничения символьных типов....................................203 Символьные данные фиксированной длины...........................203 CHAR(n), алиас для CHARACTER(n).................................204 NCHAR(n), алиас для NATIONAL CHARACTER(n).......................204 Символьные данные переменной длины..............................204 Наборы символов и последовательность сортировки.....................205 Набор символов клиента..........................................206 Переопределение набора символов.................................207 Наборы символов Firebird........................................208 Набор символов для клиентского соединения.......................213 Специальные наборы символов.....................................214 Последовательности сортировки...................................215 Пользовательские наборы символов и сортировки...................218 Пора дальше.........................................................220 Глава 12. BLOB и массивы............................................221 ТипыбСОЙ............................................................221 Поддерживаемые типы BLOB........................................222 Сегменты BLOB...................................................223 Операции с полями BLOB..........................................224 Когда использовать типы BLOB....................................225 Типы массивов.......................................................226 Типы ARRAY и SQL.............................................. 226 Когда использовать тип массива..................................227 Подходящие типы элементов.......................................227 Определение массивов............................................227 Многомерные массивы.............................................227 Хранение столбцов массивов......................................228 Доступ к данным массива.........................................229 Ограничения доступа динамического SQL...........................230 Пора дальше.........................................................230 Глава 13. Домены....................................................231 Создание домена.....................................................232 Идентификатор домена.......................................... 232 Тип данных домена...............................................232 Атрибут DEFAULT.................................................233 Атрибут NOT NULL................................................234 Условия CHECK...................................................234 Атрибут CHARSET/CHARACTER SET...................................237 Атрибут COLLATE.................................................237 Использование доменов в определении столбца.........................238 Пример..........................................................238 Переопределения в доменах.......................................238 Когда домены не работают........................................240
12 Оглавление Объявление домена BOOLEAN...........................................240 Изменение определения домена........................................240 Примеры.........................................................241 Ограничения на изменение типов данных...........................241 Удаление домена.....................................................242 Пора дальше....................................................... 242 ЧАСТЬ IV. БАЗА ДАННЫХ И ЕЕ ОБЪЕКТЫ..................................243 Глава 14. Чертежная доска для базы данных...........................245 Пользователь SYSDBA и пароль........................................245 Метаданные..........................................................245 Язык определения данных.........................................246 Системные таблицы...............................................246 Проектирование базы данных..........................................246 Описание и анализ...............................................247 Модель данных о база данных.....................................248 Одна база данных или много?.....................................248 Физические объекты..............................................249 Соглашения по именованию объектов базы данных и ограничения.....256 Скрипты схемы.......................................................258 Скрипты Firebird................................................258 Зачем использовать скрипты?.....................................258 Что находится в скриптах DDL?...................................259 Основные шаги...................................................262 Управление скриптами вашей схемы................................265 Пора дальше.........................................................267 Глава 15. Создание и ведение базы данных............................268 Физическое хранение базы данных ....................................268 Размещение......................................................268 Безопасность доступа................................................269 ISCJJSER и ISCJPASSWORD.........................................269 Создание базы данных................................................270 Диалект.........................................................270 Обязательные и необязательные атрибуты..........................271 Получение информации о базе данных..............................273 Базы данных из одного и нескольких файлов...........................274 Задание размера файла для однофайловой базы данных..............275 Создание многофайловой базы данных..................................275 Изменение базы данных...........................................276 Кэш базы данных.....................................................277 Ограничения и значения по умолчанию.............................278 Вычисление размера кэша.........................................279
Оглавление 13 Установка размера кэша на уровне базы данных......................280 Изменение значений по умолчанию для сервера.......................282 Проверка размера кэша.............................................283 Базы данных только для чтения.........................................283 Внешние файлы.....................................................284 Преобразование базы данных в режим только для чтения..............284 Теневые копии базы данных.............................................285 Преимущества и ограничения теневого копирования...................285 Реализация теневого копирования...................................286 Создание теневой копии............................................288 Увеличение размера теневой копии..................................291 Удаление теневой копии............................................291 "Гигиена" базы данных.................................................292 Фоновая сборка мусора.............................................292 Сборка мусора в процессе резервного копирования...................293 Проверка и ремонт.................................................293 Удаление базы данных..................................................297 Синтаксис.........................................................297 Пора дальше...........................................................297 Глава 16. Таблицы.....................................................298 О таблицах Firebird...................................................298 Структурные описания..............................................298 Создание таблиц.......................................................298 Владение таблицами и привилегии...................................299 Оператор CREATE TABLE.............................................299 Ограничения...........................................................305 Ограничения целостности...........................................306 Ссылочное ограничение.............................................306 Именованные ограничения...........................................306 Ограничения целостности...............................................307 Ограничение NOT NULL..............................................307 Ограничение PRIMARY KEY...........................................307 Ограничения CHECK.................................................314 Ограничения UNIQUE................................................316 Использование внешних файлов в качестве таблиц........................317 Синтаксис для CREATE TABLE...EXTERNAL FILE........................317 Импорт внешних файлов в таблицы Firebird..........................320 Экспорт таблиц Firebird во внешние файлы..........................322 Конвертирование внешних таблиц во внутренние......................323 Изменение таблиц......................................................323 Подготовка к выполнению ALTER TABLE...............................323 Изменение столбцов в таблице......................................324 Удаление столбцов.................................................325 Удаление ограничений..............................................326
14 Оглавление Добавление столбца................................................327 Когда недостаточно ALTER TABLE....................................328 Удаление таблицы......................................................329 DROP TABLE........................................................329 RECREATE TABLE....................................................330 Временные таблицы.....................................................330 Постоянные "временные" таблицы....................................330 Пора дальше...........................................................331 Глава 17. Ссылочная целостность данных................................332 Терминология..........................................................333 Ограничение FOREIGN KEY...............................................333 Реализация ограничения............................................334 Действия триггеров по изменению правил целостности................336 Взаимодействие ограничений............................................337 Триггеры действий пользователя........................................338 Таблицы соответствия и ваша модель данных.............................339 Привилегии на ссылки..................................................341 Обработка других видов отношений......................................342 Отношение один-к-одному...........................................342 Отношение многие-ко-многим........................................343 Ссылающиеся на себя отношения.....................................346 Обязательные отношения............................................347 Ошибка "объект находится в использовании".............................347 Пора дальше...........................................................348 Глава 18. Индексы.....................................................349 Ограничения...........................................................349 Автоматические индексы в сравнении с определенными пользователем индексами.............................................................350 Импорт существующих индексов..................................... 350 Направленные индексы..............................................350 Планы запросов........................................................351 Как могут помочь индексы..............................................351 Сортировка и группирование........................................352 Соединения........................................................352 Сравнения.........................................................352 Что индексировать.....................................................352 Когда индексировать...................................................353 Использование CREATE INDEX............................................354 Обязательные элементы.............................................354 Необязательные элементы...........................................355 Индексы для нескольких столбцов.......................................356 Предикаты OR в запросах...........................................357 Критерии поиска...................................................357
Оглавление 15 Просмотр индексов....................................................358 Изменение индекса....................................................358 Активация/деактивация............................................358 Изменение структуры индекса......................................359 Удаление индекса.....................................................359 Ограничения......................................................359 Тема оптимизации: оптимальное индексирование.........................360 Действия по обслуживанию индексов................................360 Улучшение селективности индекса..................................360 Получение статистики по индексу..................................364 Интерфейс gstat..................................................365 Другие переключатели gstat.......................................371 Пора дальше..........................................................377 ЧАСТЬ V. FIREBIRD SQL................................................379 Глава 19. Язык SQL в Firebird........................................381 Firebird и стандарты.................................................381 Операторы SQL.......................................................382 Язык определения данных (DDL)....................................383 Язык манипулирования данными (DML)...............................383 Возможности встраиваемого языка (ESQL)...........................383 Динамический в сравнении со статическим SQL......................384 Интерактивный SQL (ISQL).........................................385 Процедурный язык (PSQL)..........................................385 Диалекты SQL.........................................................385 Ресурсы SQL.........................................................;386 Книги............................................................386 Свободная поддержка SQL..........................................387 Пора дальше..........................................................387 Глава 20. Запросы DML................................................388 Наборы данных........................................................388 Таблица является набором.........................................388 Выходные наборы..................................................389 Кардинальное число и положение...................................389 Входные наборы...................................................390 Выходные наборы в качестве входных наборов.......................390 Наборы курсора...................................................391 Вложенные наборы.................................................392 Привилегии...........................................................393 Оператор SELECT......................................................393 Предложения в операторе SELECT...................................394 Запросы, подсчитывающие строки...................................403
16 Оглавление Оператор INSERT......................................................405 Добавление данных в столбцы BLOB.................................406 Использование INSERT для автоматических полей....................406 Оператор UPDATE......................................................409 Позиционированные операции в сравнении с поисковыми..............409 Использование оператора UPDATE...................................410 Оператор DELETE......................................................412 Оператор EXECUTE.....................................................412 Запросы, которые вызывают хранимые процедуры.........................413 Выполняемые процедуры............................................413 Процедуры выбора.................................................413 Использование параметров.............................................414 Замечания для пользователей Delphi...............................415 Пакетные операции....................................................416 Пакетные добавления..............................................416 Операции DML и события изменения состояния...........................417 Предложения действия ссылочной целостности.......................417 Пользовательские триггеры........................................417 Пора дальше..........................................................419 Глава 21. Выражения и предикаты......................................420 Выражения............................................................420 Предикаты............................................................421 Где проверяется истинность.......................................422 Утверждения......................................................422 Решение, что является истинным...................................423 Символы, используемые в выражениях...................................424 Операторы SQL....................................................... 426 Приоритет операторов.............................................426 Оператор конкатенации............................................427 Арифметические операции..........................................427 Операторы сравнения..............................................428 Другие предикаты сравнения.......................................429 Логические операторы.............................................432 Предикат IS [NOT] NULL...........................................433 Предикаты существования..........................................433 Обсуждение NULL......................................................437 NULL в выражениях................................................437 NULL в вычислениях...............................................438 Понимание истинности и ложности..................................438 NULL и внешние функции (UDF).....................................439 Установка значения в NULL........................................439 Использование выражений..............................................440 Вычисляемые столбцы..............................................440 Определение столбцов COMPUTED BY.................................446
Оглавление 17 Условия поиска....................................................446 Условия упорядочения и группирования..............................448 Выражения CHECK в DDL.............................................449 Выражения в PSQL..................................................449 Вызовы функций........................................................450 Внутренние функции SQL.......................................... 450 Внешние функции (UDF).............................................456 Пора дальше...........................................................461 Глава 22. Запросы к множеству таблиц..................................462 Виды многотабличных запросов..........................................462 Соединения............................................................463 Подзапросы.......................................................... 463 Запросы UNION.........................................................463 Соединения............................................................463 Внутреннее соединение.................................................464 Синтаксис неявного INNER JOIN в SQL-89............................465 Синтаксис явного INNER JOIN в SQL-92............................ 465 Смешивание неявного и явного синтаксисов..........................467 Внешние соединения....................................................467 LEFT OUTER JOIN...................................................467 RIGHT OUTER JOIN..................................................468 FULL OUTER JOIN...................................................469 Перекрестные соединения...............................................470 Естественные соединения...............................................470 Двусмысленность в запросах JOIN.......................................470 Алиасы таблиц.........................................................471 Допустимые имена алиасов таблиц...................................472 Внутренний курсор.....................................................472 Реентерабельные соединения............................................473 Курсоры для реентерабельных соединений............................473 Подзапросы............................................................475 Задание столбца при использовании подзапроса..........................475 Коррелированные подзапросы........................................476 Подзапрос или соединение?....................................... 476 Поиск с использованием подзапроса.....................................477 Реентерабельные подзапросы........................................477 Добавление данных с использованием подзапроса с соединениями..........478 Оператор UNION........................................................478 Наборы, совместимые для объединения...................................479 Использование столбцов времени выполнения в объединениях..............480 Условия поиска и упорядочивания.......................................481 Реентерабельные запросы UNION..................................:......481 UNION ALL.............................................................482
18 Оглавление Темы оптимизации: планы запросов и оптимизатор......................482 Планы и оптимизатор запросов Firebird...........................482 Улучшение плана запроса.........................................494 Пора дальше.........................................................498 Глава 23. Упорядоченные и агрегатные наборы.........................499 Обсуждение сортировки...............................................499 Задание порядка в предложениях сортировки.......................499 Временное пространство сортировки...............................500 Индексирование..................................................501 Предложение ORDER BY................................................501 Элементы сортировки.............................................502 Направление сортировки..........................................505 Размещение пустых значений......................................505 Предложение GROUP BY................................................506 Список группируемых полей..................................... 506 Группирующий элемент............................................508 Подвыражение НА VING............................................511 Подвыражение COLLATE............................................512 Использование ORDER BY в группирующем запросе...................512 Улучшенные условия группирования................................512 Пора дальше.........................................................515 Глава 24. Просмотры.................................................516 Что такое просмотр?.................................................516 Ключи и индексы.................................................516 Упорядочение и группирование строк..............................517 Некоторые простые спецификации просмотров.......................517 Почему просмотры могут быть полезными...........................518 Привилегии......................................................518 Создание просмотров.................................................519 Оператор CREATE VIEW............................................520 Просмотры только для чтения и изменяемые............................524 Просмотры только для чтения.....................................524 Естественно изменяемые просмотры................................526 Изменение поведения изменяемых просмотров.......................526 Изменить определение просмотра?.....................................527 Удаление просмотра..................................................527 Использование просмотров в SQL......................................527 Использование планов запросов для просмотров........................528 Другие наследуемые таблицы..........................................530 Хранимые процедуры выбора.......................................530 Внешние виртуальные таблицы.....................................531 Пора дальше.........................................................532
Оглавление 19 ЧАСТЬ VI. ТРАНЗАКЦИИ...................................................533 Глава 25. Обзор транзакций Firebird....................................535 Свойства ACID..........................................................536 Атомарность........................................................536 Согласованность....................................................536 Изолированность....................................................536 Устойчивость.......................................................537 Контекст транзакции....................................................537 Одна транзакция, много запросов....................................537 Транзакции и MGA.......................................................539 Пересылка в сравнении с COMMIT.....................................539 Откат..............................................................539 Блокировка строки..................................................540 Блокировка на уровне таблицы.......................................541 Добавление.........................................................542 "Старение" и статистика транзакций.....................................542 Идентификатор и возраст транзакции.................................542 "Заинтересованные транзакции"......................................543 OIT и ОЛТ должны постоянно изменяться..............................546 Условия для изменения ОТТ и ОАТ....................................547 Чистка в сравнении со сборкой мусора...............................548 Статистика транзакций..................................................549 gstat..............................................................549 isql...............................................................550 Что может рассказать вам статистика................................550 Пора дальше............................................................551 Глава 26. Конфигурирование транзакций..................................552 Параллельность.........................................................552 Факторы, влияющие на параллельность................................552 Уровень изоляции...................................................553 Способ доступа.....................................................556 Способ разрешения блокировок ("Режим блокировок")..................556 Резервирование таблиц..............................................557 Версии записей.........................................................560 Зависимые строки...................................................561 Блокировки и конфликты блокировок......................................561 Время действия.....................................................561 Конфликты блокировки...............................................562 Что такое взаимная блокировка?.....................................563 Пора дальше............................................................564
20 Оглавление Глава 27. Программирование с транзакциями...........................565 Язык для транзакций.................................................565 ESQL............................................................565 API.............................................................566 Запуск транзакции...................................................566 SQL.............................................................566 API.............................................................568 Доступ к идентификатору транзакции..................................570 Использование TID в приложениях.................................571 Процесс выполнения транзакции.......................................571 Вложенные транзакции................................................571 Пользовательские точки сохранения...............................572 Логический контекст.................................................575 Завершение транзакций...........................................575 Диагностирование исключений.....................................578 Транзакции для нескольких баз данных................................580 Зависшие транзакции.............................................581 Ограниченные базы данных........................................581 Пессим истическая блокировка...................................... 582 Блокировка на уровне таблицы....................................582 Блокировка на уровне оператора..................................583 Хранимые процедуры, триггеры и транзакции...........................588 Хранимые процедуры..............................................588 Триггеры........................................................588 "Точки сохранения" в PSQL.......................................588 Советы по оптимизации поведения транзакции..........................589 Выбор подходящей модели транзакции..............................589 Не переходите к "общему" пока вам это не понадобилось...........589 Использование возможностей множества транзакций.................589 Сохраняйте передвижение ОАТ.....................................589 Пора дальше.........................................................590 ЧАСТЬ VII. ПРОГРАММИРОВАНИЕ НА СЕРВЕРЕ..............................591 Глава 28. Введение в программирование в Firebird....................593 Обзор модулей сервера...............................................593 Хранимые процедуры..................................................594 Преимущества использования хранимых процедур....................595 Триггеры............................................................595 Преимущества использования триггеров............................596 Триггеры в качестве автоинкрементного механизма.................596 Триггеры и транзакции...........................................596 Расширения языка PSQL...............................................597 Ограничения PSQL................................................597 Типы операторов, не поддерживаемых в PSQL.......................598
Оглавление 21 Исключения...................................................................598 События......................................................................599 Безопасность.................................................................599 Внутреннее устройство технологии.........................................600 Эффекты изменений........................................................600 Пора дальше..................................................................601 Глава 29. Разработка модулей PSQL............................................602 Элементы процедур и триггеров................................................602 Оператор CREATE....................................................................................................602 Элементы заголовка.......................................................605 Элементы тела............................................................605 Элементы языка...........................................................606 Программные конструкции......................................................608 Блоки BEGIN... END.......................................................608 Условные блоки...........................................................609 Переменные...............................................................611 Операторы SELECT... INTO.................................................616 Операторы управления потоком.............................................619 EXECUTE STA ТЕМ ENT......................................................620 POST_EVENT...............................................................623 Разработка модулей...........................................................623 Добавление комментариев..................................................623 Чувствительность к регистру и пробелы....................................623 Управление вашим кодом...................................................624 Компиляция хранимых процедур и триггеров.................................625 Удаление исходных текстов модулей........................................627 Пора дальше..................................................................628 Глава 30. Хранимые процедуры................................................ 629 Выполняемые хранимые процедуры...............................................629 Хранимые процедуры выбора....................................................629 Создание хранимых процедур...................................................630 Элементы заголовка.......................................................630 Элементы тела............................................................631 Выполняемые процедуры........................................................633 Сложная обработка........................................................634 Поддержка "живых" клиентских наборов.....................................634 Операции в выполняемых процедурах........................................634 Многотабличные процедуры.................................................635 Конструкция SELECT... INTO...............................................636 Использование (вызов) выполняемых процедур...............................638 Рекурсивные процедуры....................................................639 Курсоры в PSQL...........................................................641
22 Оглавление Хранимые процедуры выбора.............................................642 Использование процедур выбора.....................................642 Техника...........................................................644 Конструкция FOR SELECT... DO......................................644 Простая процедура с вложенными операторами SELECT.................646 Вызов процедуры выбора............................................647 Вложенные процедуры...............................................648 Процедура с промежуточными итогами................................652 Просмотр массива в хранимой процедуре.............................654 Тестирование процедур.............................................656 Процедуры для совместного использования...............................656 Изменение хранимых процедур...........................................656 Влияние на приложения.............................................657 Синтаксис изменения процедур......................................657 Удаление хранимых процедур............................................660 Ограничения.......................................................660 Тема оптимизации: использование внутренних возможностей...............661 Относительно RDBSDBKEY............................................661 RDBSDBKEY в многотабличных наборах................................665 Пора дальше...........................................................666 Глава 31. Триггеры....................................................667 Фаза, событие и последовательность....................................667 Фаза и событие........................................................667 Последовательность................................................668 Состояние.............................................................669 Создание триггеров......................................,.............669 Синтаксис.........................................................670 Элементы заголовка................................................670 Тело триггера.....................................................671 Особенности PSQL для триггеров........................................671 Переменные события................................................672 Переменные NEW и OLD..............................................672 Изменение других таблиц...............................................678 Поддержание обязательного отношения...............................678 Поддержка ссылочной целостности...................................681 Изменение строк в той же таблице..................................686 Изменение триггеров...................................................687 Синтаксис для изменения триггеров.................................687 Удаление триггеров....................................................689 Пора дальше...........................................................689 Глава 32. Обработка ошибок и события..................................690 Типы исключений.......................................................690 Что такое исключение?.............................................690 Создание исключения............................................. 691
Оглавление 23 Исключения в действии.................................................... 692 Обработка исключений.................................................... 693 Оператор WHEN........................................................ 693 Вложенные исключения в качестве точек сохранения......................696 Обработка исключения REASSIGN SALES............................................................696 Протокол ошибок.......................................................698 Исключения в триггерах................................................700 Сообщения исключений во время выполнения..................................701 Список кодов ошибок.......................................................702 Написание вашего собственного списка кодов ошибок.....................702 События...................................................................704 Использование сообщений о событиях....................................704 Элементы механизма....................................................705 Синхронное прослушивание..............................................706 Асинхронная сигнализация..............................................708 Асинхронное прослушивание.............................................709 Использование POST_EVENT..............................................710 Пора дальше...............................................................711 ЧАСТЬ VIII. БЕЗОПАСНОСТЬ..................................................713 Глава 33. Безопасность в операционной среде...............................715 Физическая безопасность...................................................715 Использование защищенных файловых систем..............................716 Защита оперативных копий..............................................716 Защита, основанная на возможностях платформы..............................717 Ограничение подключения к операционной системе........................717 POSIX.................................................................717 Платформы Microsoft Windows...........................................718 Выполнение произвольного кода.............................................719 Firebird 1.0.^........................................................720 Firebird 1.5..........................................................720 Безопасность сетевого соединения..........................................721 Web и другие многозвенные серверные приложения............................721 Использование выделенных серверов.....................................722 Использование средств межсетевой защиты...............................722 Отражение атак........................................................722 Пора дальше...............................................................722 Глава 34. Защита сервера................................................ 723 Ввод учетных данных пользователя..........................................723 Шифрование пароля.....................................................724 Учет пользователей в SQL..............................................725 Пользователь SYSDBA.......................................................725 Слабое место POSIX........................................................725
24 Оглавление Утилита gsec...........................................................726 Запуск интерактивной сессии gsec...................................727 Использование gsec из командной строки.............................730 Сообщения об ошибках gsec..........................................731 Специальная тема: настройка безопасности пользователя..................732 База данных безопасности...........................................732 Предоставление пользователям возможности изменять свой собственный пароль.............................................................733 Как спрятать список пользователей/паролей..........................734 Как запротоколировать попытки соединения с базой данных............735 Как остановить злоумышленников.....................................737 Пора дальше............................................................738 Глава 35. Безопасность на уровне базы данных...........................739 Безопасность и доступ по умолчанию.....................................739 А теперь плохие новости............................................739 Привилегии............................,................................741 Упаковка привилегий................................................741 Объекты................................................................742 Ограничения привилегий.............................................743 Пользователи...........................................................743 Специальные пользователи...........................................744 Пользователь PUBLIC................................................744 Предоставление привилегий..............................................745 Права UPDATE к столбцам............................................745 Права REFERENCES к столбцам........................................746 Привилегии к объектам..............................................747 Множество привилегий и множество получателей привилегий............748 Привилегии через роли..................................................750 Создание роли......................................................751 Назначение привилегий роли.........................................751 Предоставление роли пользователям..................................751 Подключение к базе данных с использованием роли....................751 Удаление роли......................................................752 Предоставление прав на предоставление привилегий.......................752 Неожиданные эффекты................................................753 Отмена полномочий......................................................754 Использование REVOKE...............................................755 Отмена привилегий у нескольких пользователей.......................756 Отмена права предоставлять привилегии..................................758 Скрипты безопасности...................................................759 Создание скрипта...................................................759 Инсталляция полномочий непосредственно из процедуры................762 Пора дальше............................................................763
Оглавление 25 Глава 36. Конфигурация и специальные возможности....................764 Сравнение архитектуры Суперсервера и Классического сервера..........764 Выполняемые программы и процессы................................764 Управление блокировками.........................................765 Использование ресурсов..........................................765 Локальный доступ................................................766 Файл конфигурации Firebird..........................................767 Параметры.......................................................767 Параметры для конфигурирования временного пространства для сортировки.769 Параметры, связанные с ресурсами................................771 Параметры, связанные с коммуникацией............................773 Работа со встроенным сервером.......................................781 Запуск встроенного сервера..........................................781 Приложения..........................................................781 Размещение и переименование библиотеки..........................782 Жестко закодированные пути к базе данных........................782 Утилиты удаленных сервисов......................................783 Вопросы безопасности сервера....................................783 Совместимость нескольких серверов...................................784 Останов встроенного сервера.........................................784 Модули внешних кодов................................................785 Разработка ваших собственных UDF....................................785 Написание модуля функции........................................786 Фильтры BLOB........................................................792 Написание фильтров BLOB.........................................793 Объявление фильтров BLOB........................................793 Вызов фильтров BLOB.............................................794 Инструменты для написания фильтров..............................794 Модули интернациональных языков.....................................795 Конфигурирование внешних размещений.................................795 Установки в файле конфигурации......................................795 UDFAccess.......................................................796 Пора дальше.........................................................798 ЧАСТЬ IX. ИНСТРУМЕНТЫ...............................................799 Глава 37. Интерактивный SQL (утилита isql)..........................801 Интерактивный режим.................................................801 Текстовый редактор по умолчанию.................................801 Запуск isql.....................................................802 Соединение с базой данных.......................................803 Символ терминатора..............................................805 Продолжение строки подсказки....................................805 Транзакции в isql...............................................805
26 Оглавление Поиск в буфере строк..............................................806 Использование предупреждений......................................807 Обработка исключений..............................................807 Установка в isql диалекта.........................................807 Интерактивные команды.................................................808 Создание и изменение объектов базы данных.........................809 Общие команды isql....................................................809 Команды SHOW..........................................................813 Команды SET......................................................... 820 Выход из интерактивной сессии isql................................826 Командный режим.......................................................826 Выполнение isql в командном режиме................................826 Переключатели командной строки....................................827 Извлечение метаданных.................................................828 Использование isql -extract.......................................829 Использование isql -a.............................................829 Пора дальше...........................................................830 Глава 38. Резервное копирование и восстановление баз данных (gbak).....................................................831 Файлы gbak............................................................831 Другие таланты gbak...............................................832 Обновление структуры на диске (ODS)...............................833 Права на выполнение копирования и восстановления базы данных......834 Изменение владельца базы данных...................................834 Имя пользователя и пароль.........................................834 Выполнение копирования................................................835 Аргументы для gbak-b[ackup].......................................835 Переключатели копирования.........................................836 Переносимые копии.................................................838 Копирование в один файл...........................................838 Копирование многофайловой базы данных во множество файлов.........838 Копирование только метаданных.....................................839 Выполнение удаленного копирования.................................840 Решение вопросов безопасности.....................................840 Возвращаемые коды и ответная реакция..............................840 Выполнение восстановления.............................................841 Аргументы gbak для восстановления.................................841 Переключатели восстановления......................................841 Восстанавливать или создавать?....................................843 Объекты, определенные пользователем...............................844 Восстановление в один файл........................................844 Многофайловое восстановление......................................844 Возвращаемые коды и ответная реакция..............................845 Размер страницы и размер кэша по умолчанию........................845
Оглавление 27 Использование gbak с Менеджером сервисов Firebird......................846 Восстановление в POSIX.............................................847 Копирование........................................................847 Восстановление.....................................................848 Сообщение об ошибках gbak..............................................848 Пора дальше............................................................855 Глава 39. Утилита gfix.................................................856 Использование gfix.....................................................856 Получение доступа к базе данных из gfix............................857 Чистка базы данных.....................................................857 Сборка мусора......................................................858 Интервал чистки....................................................858 Выполнение ручной чистки...........................................860 Закрытие базы данных...................................................860 Закрытие базы данных перед остановом сервера.......................861 Команда gfix —shut.................................................861 Изменение установок базы данных........................................863 Установка размера кэша по умолчанию................................863 Изменение режима доступа...........................................864 Изменение диалекта базы данных.....................................864 Включение и отключение "использовать все пространство".............864 Включение и отключение синхронного вывода..........................865 Запрос версии сервера Firebird.....................................866 Проверка и починка данных..............................................867 Когда проверять базу данных........................................867 Выполнение проверки базы данных....................................867 Восстановление разрушенной базы данных.............................868 Восстановление транзакций..............................................868 Двухфазное подтверждение...........................................868 Восстановление транзакции..........................................869 Теневые копии..........................................................870 Активизация теневой копии..........................................870 Удаление недоступных теневых копий.................................871 Список переключателей gfix.............................................871 Сообщения об ошибках gfix..............................................873 Пора дальше............................................................874 Глава 40. Менеджер блокировок..........................................875 Менеджер блокировок Firebird...........................................876 Состояния блока....................................................876 Таблица блокировок.................................................877 Утилита Lock Print.....................................................879 Статичные отчеты...................................................881 Интерактивные отчеты...............................................881 Вывод отчета в файл................................................881
28 Оглавление Простое использование Lock Print................................882 Интерактивная выборка...........................................899 Установка конфигурации блокировки...................................900 Файлы конфигурации..............................................900 ПРИЛОЖЕНИЯ..........................................................903 Приложение 1. Список внешних функций................................905 Функции условной логики.............................................905 Математические функции..............................................908 Функции даты и времени..............................................915 Строковые и символьные функции......................................924 Функции BLOB........................................................931 Тригонометрические функции..........................................936 Подпрограммы форматирования данных..................................940 Приложение 2. Решение сетевых проблем...............................943 Можете ли вы вообще соединиться с базой данных?.....................943 Можете ли вы соединиться с базой данных в локально закольцованном варианте?.944 Прослушивает ли сервер порт Firebird?...............................945 Вы получили ошибку, хотя сервис выполняется?........................945 Находится ли база данных на физически локальном диске?..........945 Правильные ли имя пользователя и пароль?........................946 Имеет ли владелец серверного процесса достаточно полномочий для открытия файлов?............................................946 Может ли клиент найти хост?.....................................946 Нужно ли вам отключить автоматический набор номера для Интернета в Windows?..........................................................948 Изменить порядок сетевых адаптеров..............................948 Изменение конфигурации Internet Explorer........................949 Запретить авто дозвон через реестр..............................949 Запретить автодозвон RAS........................................949 Все еще есть проблемы?..............................................949 Приложение 3. Интерфейсы приложений.................................950 Драйверы JayBird JDBC...............................................950 ODBC................................................................951 Драйвер Firebird ODBC/JDBC......................................951 Другие драйверы ODBC............................................952 Firebird .NET Provider..............................................953 1BPP для разработки C++.............................................953 Delphi, Kylix и Borland C++ Builder.................................954 IB Objects......................................................954 FIBPlus.........................................................955 Другие..........................................................956
Оглавление 29 PHP.........................................................................957 Python......................................................................958 Perl...............................'........................................959 Приложение 4. Как выполнять ремонт базы данных..............................960 Восемь шагов восстановления.................................................960 Получение исключительного доступа.......................................960 Создание копии работающего файла........................................961 Выполнение проверки.....................................................961 Исправление разрушенных страниц.........................................962 Проверка после исправления mend.......................................................................962 Очистка и восстановление базы данных....................................962 Восстановление очищенной резервной копии в качестве новой базы данных...963 Проверка восстановленной базы данных...............................,....963 Как поступать, если проблемы остались.......................................963 Приложение 5. Инструменты администрирования.................................965 Графические инструменты администратора......................................965 Database Workbench......................................................965 IBExpert................................................................965 BlazeTop.............................................................. 966 IBAccess................................................................966 IBAdmin.................................................................967 1BSQL...................................................................967 IBOConsole..............................................................967 Инструменты резервного копирования..........................................968 DBak....................................................................968 gbak BackupRunner.......................................................968 Time To Backup..........................................................969 Разное......................................................................969 Инструменты починки базы данных IBSurgeon...............................969 Анализатор статистики IBAnalyst.........................................969 Interbase DataPump......................................................970 Advanced Data Generator для Firebird....................................970 Менеджеры полномочий....................................................970 Где посмотреть информацию о других инструментах.............................971 Приложение 6. Пример базы данных............................................972 Создание базы данных Employee с помощью скриптов............................972 Приложение 7. Ограничения Firebird..........................................974 Приложение 8. Наборы символов и порядков сортировки.........................980
30 Оглавление Приложение 9. Системные таблицы и просмотры.....................986 Системные таблицы...............................................986 Системные просмотры............................................1007 Приложение 10. Коды ошибок.....................................1009 Приложение 11. Зарезервированные слова.........................1065 Приложение 12. Литература и источники..........................1069 Рекомендуемая литература.......................................1069 Список Web-сайтов..............................................1070 Сайты проекта Firebird.....................................1070 Web-сайты ресурсов.........................................1070 Форумы Firebird................................................1071 Как стать разработчиком Firebird...............................1073 Глоссарий......................................................1074 Предметный указатель...........................................1098
Введение Об авторе Хелен Борри (Helen Borrie) работает по контракту инженером по программному обеспечению, по совместительству писательница и технический редактор. Она зани- мается разработкой баз данных более 20 лет, а с Firebird и его предшественниками работает с 1996 года. Хелен— активный участник сообщества онлайновой поддержки Firebird и одна из основателей FirebirdSQL Foundation Inc. Хелен живет в Австралии и общается через Интернет из своей домашней студии, расположенной среди эвкалиптов на живописном берегу Нового Южного Уэльса. О техническом редакторе Джефф Ворбойз (Geoff Worboys) занимается проектированием и разработкой прило- жений, связанных с базами данных, около 15 лет. В течение последних более 10 лет он использует Firebird, а перед этим применял его предшественника InterBase в каче- стве системы управления реляционными базами данных в разработке приложений, инструментов управления и компонентов для Delphi, С и C++ в зависимости от си- туации. Сейчас он работает в укромном офисе в Новом Южном Уэльсе, Австралия, разраба- тывая приложения как для клиентов из Австралии, так и для клиентов из других стран. Он наблюдает кенгуру и других обитателей дикой природы из своего окна, размышляя над проблемами проектирования баз данных и приложений — Интернет замечательная штука. О научном редакторе перевода на русский язык Кузьменко Дмитрий занимается проектированием и разработкой приложений баз данных уже 16 лет. С InterBase начал работать в 1994 году. В 2002 году Дмитрий ос- новал фирму iBase (www.ibase.ru), которая занимается техническим сопровождением InterBase и Firebird, а также обучением, консультациями и продажей программных продуктов. Дмитрий живет и работает в Москве, видел кенгуру только в зоопарке и по каналу Discovery и не откажется при удобном случае посетить Австралию.
32 Введение Благодарности Вот, наконец, мы с книгой по Firebird в руках! Ее написание избавило меня от других рискованных занятий в течение почти года. Я постоянно беспокоила моих хороших друзей из сообщества Firebird, так что я должна вначале поблагодарить всех их, даже если я не назову каждого по имени. У нас есть наша книга, спасибо вам. Павел Цизар (Pavel Cisar), мой давнишний друг по онлайновой поддержке из Kingdom of Geek, уделял мне свое время и опыт, выходящие за рамки его обязанно- стей. Павел был опорой издательской группы, он также давал отдельные неоценимые советы на основании опыта написания своей собственной книги по Firebird и InterBase в прошлом году (в Чехии) и его исследований внутренней работы оптими- затора запросов. Анна Харрисон (Ann Harrison)— "мать InterBase", превосходный специалист в большинстве премудростей сервера Firebird. Иван Преносил (Ivan Prenosil) щедро делился полученным им практическим опытом, а проницательность Дмитрия Серебрякова избавила меня от многих оплошностей. Клавдио Балдеррама (Claudio Valderrama Cortes) делился своим пониманием секретов rdb$db_key. Спасибо также Дэвиду Брукстоуну Шнепперу (David Brookestone Schnepper) за полезные комментарии о наборах символов и Грегори Дицу (Gregory Deatz) за предоставление мне документа по внешним функциям FreeUDFLib в приложении 1. Моему другу Джеффу Ворбойзу особая благодарность за огромную заботу и терпе- ние, которые он проявил при техническом просмотре содержания книги и многих деталей. Мы не всегда были согласны друг с другом, но книга по Firebird стала луч- ше благодаря ему. Спасибо также Дугу Чамберлину (Doug Chamberlin) и Иоане Пир- те (loana Pirtea) за их просмотр "читательскими глазами". Я также благодарна директорам компании IBPhoenix Полу Бигу (Paul Beach) и Анне Харрисон, а также моим сотрудникам за то, что помогли мне в вопросах временных задержек и финансирования книги. Предложения по книге о Firebird на английском языке получали в издательствах от- каз на протяжении более трех лет, пока Apress не взяла на себя риск и не выпустила в свет книгу по Firebird. Apress, сообщество Firebird благодарит вас за то, что услыша- ли нас. И, наконец, я должна от всего сердца от имени всех нас сказать "спасибо" разработ- чикам Firebird, чьи увлеченность, умения и самоотверженность дали нам Firebird. Kia ora! Kia manawa! Хелен Борри Апрель 2004
Введение 33 Введение в Firebird Что такое Firebird? Firebird— это мощная, компактная реляционная система управления базами данных (РСУБД) с архитектурой клиент-сервер. Она может выполняться на разнообразных серверных и клиентских платформах, включая Windows, Linux и на некоторых дру- гих платформах UNIX, включая FreeBSD и Mac OS X. Это РСУБД промышленного применения, чьи возможности имеют высокий уровень соответствия стандартам SQL, при этом она реализует некоторые мощные расширения языка процедурного программирования конкретного производителя. Кому нужна эта книга? Разработчики с некоторым опытом работы с базами данных, которые, возможно, пе- реходят на платформу клиент-сервер впервые, найдут в этой книге все необходимое, чтобы стать более продуктивными в Firebird. Несмотря на то, что это руководство не является начальным учебником по SQL или по проектированию баз данных, в нем делается акцент на практику проектирования хороших приложений на базе клиент- серверных реляционных СУБД; оно также содержит документацию по языку SQL для Firebird— определения, манипуляция, язык программирования— с большим количеством деталей, советов и примеров. Firebird— серьезный программный продукт, созданный для установки в малых и больших сетях, он также обладает некоторыми полезными возможностями для авто- номных конфигураций. Его небольшой размер дает возможность одиночным разра- ботчикам легко выполнять большие производственные разработки в домашнем офи- се. Для администратора базы данных или системного проектировщика книга дает основные сведения по инсталляции, конфигурированию, настройке, безопасности и инструментам. Мощь и высокий уровень соответствия стандартам делает Firebird привлекательной средой для изучения информационных технологий в университетах. Эта книга даст достаточный материал для студентов второго и третьего года обуче- ния компьютерным наукам для работы с Firebird. Тех кто использовал до настоящего момента Firebird 1.0.x или InterBase, книга по Firebird знакомит с расширениями языка, безопасностью и возможностями оптими- затора, которые были добавлены в версию 1.5. Где найти нужную вам информацию? Часть I является "учебным лагерем" для новичков в Firebird. Здесь вы найдете ос- новные сведения по инсталляции программного обеспечения, созданию и запуску клиента сети и некоторые полезные установки конфигурации. Эта часть завершается главой по самым основным операциям: соединение с базой данных примера и созда- ние вашей первой собственной базы данных с использованием утилиты isql, входя- щей в состав Firebird. В этой части вводятся различные инструменты командной строки для администратора и рассказывается, как запустить и остановить сервер. 2 Зак. 420
34 Введение Часть II содержит обзор концепций и моделей архитектуры клиент-сервер и описа- ние того, насколько им соответствует реализация Firebird. Финальная глава этой час- ти содержит некоторые общие практические инструкции по использованию клиент- ских библиотек Firebird. В части III вы найдете детальное описание каждого типа данных SQL, поддерживае- мого Firebird. Существует отдельная глава для каждого класса типа данных — числа, тип дата/время, символьные типы и т. д. — с множеством советов по их использо- ванию. Часть IV исследует объекты базы данных в подробностях, начиная с самой базы данных и переходя к таблицам, индексам и другим типам объектов. Синтаксис и ис- пользование операторов языка определения данных (Data Definition Language, DDL) представлены в этой части. Часть V содержит документацию по языку манипулирования данными (Data Manipu- lation Language, DML), используемому в SQL Firebird. Часть VI описывает транзакции: как они работают, как их конфигурировать, и со- держит советы по их использованию в ваших прикладных программах. Часть VII описывает программирование на стороне сервера в Firebird: написание триггеров и хранимых процедур, создание и использование событий базы данных, обработка ошибок в вашем коде на сервере. Часть VIII посвящена вопросам безопасности, архитектуры и конфигурации. Последняя часть IX документирует инструменты командной строки и их оболочки для администрирования. Приложения и глоссарий В приложениях и глоссарии представлены следующие материалы и детали. Приложение 1 содержит имена, описания и примеры внешних функций (UDF, User- defined functions, Определенные пользователем функции), поставляемых в библиоте- ках fb_udf и ib udf для платформ POSIX (Portable Operating System Interface for UNIX, Интерфейс переносимых операционных систем) и в свободно распространяе- мой Грегори Дитцем FreeUDFLib.dll для Windows. Приложение 2 является собранием советов по устранению ошибок; к нему вы можете обратиться, когда встречаются проблемы соединения удаленных клиентов с сервером Firebird. Приложение 3 суммирует информацию о некоторых основных драйверах и средствах программного интерфейса, доступных в Firebird. Содержит адреса сайтов загрузки и поддержки этих материалов. Приложение 4 описывает пошаговую процедуру, которой вы должны следовать, если обнаружили логические ошибки в базе данных Firebird. Приложение 5 описывает множество графических инструментов, доступных для ра- боты с вашими базами данных Firebird. Содержит ссылки на адреса загрузки инстру- ментов.
Введение 35 Приложение 6 содержит некоторые замечания о базе данных employee.fdb (employee.gdb в версии 1.0.x), которую инсталлятор Firebird устанавливает в подката- логе examples вашего корневого каталога Firebird. Приложение 7 перечисляет различные физические ограничения, применимые к базам данных Firebird 1.0.x и 1.5. Приложение 8 является полным справочником по интернациональным наборам сим- волов и связанным с языками порядкам сортировки, поставляемым с Firebird 1.5. Приложение 9 содержит спецификации описания данных для таблиц схемы, поддер- живаемых сервером Firebird внутри каждой базы данных. Включает листинги исход- ных кодов отдельных полезных просмотров, которые вы можете создавать для про- смотра системных таблиц. Приложение 10 содержит полный список в виде таблицы кодов исключений (SQLCODE и GDSCODE), определенных в Firebird 1.5, вместе с соответствующими символическими константами и текстами сообщений на английском языке. Приложение 11 содержит список всех ключевых слов, которые Firebird 1.0.x и 1.5 трактует как зарезервированные слова. Приложение 12 — собрание ресурсов, доступных пользователям Firebird. Содержит книги и другие документированные рекомендации и описания, а также ссылки на форумы поддержки. Глоссарий содержит детальные описания терминологии и концепций, которые вы, скорее всего, встретите при вашем путешествии по Firebird. Данные были получены из опросов опытных и не слишком опытных членов сообщества Firebird, которых попросили предоставить "список пожеланий" для глоссария. Происхождение Firebird Созданный как проект с открытыми исходными кодами, Firebird является первым в новом поколении потомков InterBase 6.0 Open Edition фирмы Borland, который был сформирован для разработки открытых исходных кодов в июле 2000 г. в рамках In- terBase Public License (IPL). Исходные коды Firebird поддерживаются и развиваются на основании международ- ного открытого кода на сайте SourceForge.net (http://sourceforge.net), большой груп- пой профессиональных разработчиков, в которую входят добровольцы и наемные специалисты, получающие частичное финансирование из сообщества и коммерче- ских источников. СОВЕТ. Продукты реляционной СУБД Firebird и некоторые связанные модули распространяются полностью свободными от регистрации или гонорара на осно- вании универсальной лицензии на открытые коды. Проект Firebird, его разработ- чики и его программное обеспечение никак не связаны с Borland Software Corpo- ration.
36 Введение Проект Firebird Разработка Разработчики, проектировщики и тестеры, кто предоставил вам Firebird и некоторые драйверы, являются членами проекта открытых кодов Firebird в SourceForge, изуми- тельного виртуального сообщества, которое является домом для тысяч групп про- граммного обеспечения с открытыми кодами. Адрес проекта Firebird: http://sourceforge.net/projects/firebird. На этом сайте находятся исходные коды CVS, сообщения об обнаруженных ошибках и множество технических файлов, которые могут быть загружены для различных целей, связанных с разработкой и тестировани- ем кода. Разработчики и тестеры проекта Firebird используют форум firebird-devel @lists.sourceforge.net в качестве своей "виртуальной лаборатории" для общения друг с другом по вопросам улучшения, исправления ошибок и создания новых версий Firebird. Любой, кто хочет наблюдать продвижение вперед и иметь обратную связь по вопро- сам разработки бета-версий, может присоединиться к этому форуму. Поддержка разработчиков приложений и администраторов базы данных Firebird имеет мощное сообщество добровольных помощников, включая большую группу активных разработчиков с многолетним опытом разработки и распростране- ния Firebird и его предшественника InterBase. Чувство солидарности в этой большой группе таково, что, приобретя умения и изучив "внутренние хитрости" по советам других людей, члены группы создают соответствующий список для обучения других пользователей. Основным каналом свободной поддержки является форум поддержки Firebird. Специализированные группы проекта поддерживают соответствующие форумы: Java, Delphi, C++ Builder, инструменты, Visual Basic, .NET, PHP и др. Группы пред- ставлены в виде списков e-mail, многие из них отображаются на сервере новостей. Самые свежие ссылки на эти форумы всегда могут быть найдены на главном Web- сайте сообщества (http://www.firebirdsql.org) и на дочернем коммерческом сайте IBPhoenix (http://www.ibphoenix.com). Сайт IBPhoenix также содержит огромный объем технической и пользовательской документации, ссылки на инструменты сторонних разработчиков и доску текущих новостей о событиях, произошедших в сообществе Firebird. В приложении 12 смотрите исчерпывающий список ресурсов поддержки сообщества. FirebirdSQL Foundation FirebirdSQL Foundation Inc. — это некоммерческий фонд, зарегистрированный в Но- вом Южном Уэльсе в Австралии, который собирает во всем мире денежные средства для предоставления разработчикам, работающим над основным и специальными
Введение 37 проектами для развития, тестирования и улучшения Firebird. Средства поступают в виде частной и корпоративной спонсорской помощи, пожертвований или членских взносов. Это предоставляет возможность благодарным пользователям Firebird воз- вращать вложения за свободное использование программного обеспечения и под- держку сообщества. (См. на http://www.firebirdsql.org/ff/foundation.) Спарки Спарки, молодой, алый феникс с зеленым клювом — талисман Firebird. Спарки крутился возле проекта Firebird в различных на- рядах с самого начала, однако он впервые появился "как персона" на Первой международной конференции по Firebird в г. Фулда в Германии, в мае 2003 года. Обзор возможностей Firebird является программным обеспечением для платформы клиент-сервер, разра- ботанным специально для использования в локальных и глобальных компьютерных сетях. Соответственно, его ядро состоит из двух основных программ: сервер базы данных, который выполняется на сетевом хост-компьютере, и клиентская библиоте- ка, через которую пользователи с удаленных рабочих станций соединяются и обща- ются с базой данных, управляемой сервером. /Д ВНИМАНИЕ! Администрирование и разработка в полноценной SQL реляционной / ! \ СУБД на платформе клиент-сервер, вероятно, являются совершенно новой для *—"“Д- вас территорией. Вам это может показаться слишком сложным, если вы впервые отважились обратиться к программному обеспечению управления данными, в котором заложена возможность существования множества параллельных про- цессов. Часть II этой книги содержит введение в концепцию клиент-сервер. Если вы почувствуете, что теряетесь в следующих описаниях, вы можете тут же обра- титься к части II, чтобы освоить контекст. Версии Firebird Двоичные файлы Firebird версии 1.0.x были разработаны для корректировки и улуч- шения написанных на языке С модулей, которые сообщество открытых исходных текстов наследовало от InterBase 6.0. Для Firebird 1.5 модули были полностью пере- писаны на C++ с высокой степенью стандартизации. Переход от версии 1 к версии 1.5 был в большей мере внутренним, интерфейс при- кладного программирования (Application Programming Interface, API) не изменился. Программное обеспечение приложений, написанное для версии 1, требует небольших (или вообще никаких) изменений для работы с версией 1.5. И хотя рекомендуется установить и использовать самую последнюю версию, несо- вместимость операционных систем означает (при всем уважении к Linux), что по-
38 Введение следняя версия 1.0.x— единственный выбор для некоторых сайтов. Многие новше- ства версии 1.5 были привнесены в версию 1.0.x, и регулярно выпускаются дополни- тельные сборки. Доступ к сети Сервер Firebird, запущенный на любой платформе, принимает ТСР/1Р-подключения клиентов с любой клиентской платформы, которая может выполнять Firebird API. Клиенты не могут подключиться к серверу Firebird через какую-нибудь файловую систему коллективного доступа (NFS, соединение клиентов Samba, общие ресурсы Windows или сетевой диск и т. д.). Клиент должен подключаться с указанием абсолютного физического пути. Тем не менее в Firebird 1.5 и выше средство алиасов баз данных позволяет приложениям выполнять "мягкое подключение" с использованием именованных алиасов, чьи абсо- лютные пути указаны специально для каждого сервера. К серверу Firebird, запущенному на хосте в Windows с сервисами, можно получить доступ от клиентов Windows с помощью сетевого протокола Named Pipes (именован- ные каналы). Многоверсионная архитектура Модель изоляции и управления работой множества пользователей, принятая в Fire- bird, является центральной частью архитектуры; она позволяет сохранять в базе дан- ных более одной версии записи одновременно. Множество версий одной записи мо- жет существовать одновременно — отсюда термин "многоверсионный". Каждая пользовательская задача имеет свой собственный контекстный вид состояния базы данных (см. следующий раздел) и записывает свои версии записей на диск сервера. В этот момент новая версия записи (или удаленная запись) недоступна другим зада- чам пользователей. Только самая последняя подтвержденная версия записи является видимой за преде- лами пользовательской задачи, которая успешно сохранила новую версию, и эта запись продолжает оставаться видимой для других задач. Другие задачи будут в кур- се того, что что-то произошло с этой записью, поскольку они будут блокированы от изменения или удаления этой записи, пока новая версия не станет "официальной" после подтверждения изменений. По причине использования многоверсионной архитектуры (называемой также MGA — Multi-generational architecture) для Firebird нет необходимости в двухфазной блокировке, используемой другими СУБД для управления многопользовательской работой. Транзакции Все задачи пользователей в Firebird помещаются внутрь транзакций. Задача начина- ется с оператора start transaction и завершается, когда выполненная работа под- тверждается (commit) или отменяется (rollback). Задача пользователя может выпол-
Введение 39 нять множество запросов к операциям в одной транзакции, включая операции с более чем одной базой данных. Работа сохраняется в базе данных в два этапа. На первом этапе изменения сохраня- ются на диске без изменения состояния базы данных. На втором этапе изменения подтверждаются или отменяются клиентским процессом. В версии 1.5 и выше клиен- ты могут отменить часть работы, маркируя этапы с помощью точек сохранения (savepoints) и отменяя изменения до точки сохранения без отмены всей транзакции. Транзакции в Firebird являются атомарными в том смысле, что вся работа в рамках транзакции будет сохранена или вся отменена. Транзакции можно конфигурировать с использованием трех уровней изоляции и множества стратегий тонкой настройки параллельности выполнения и условий чте- ния/записи. Хранимые процедуры и триггеры Firebird имеет богатый язык процедурных расширений, PSQL, для написания храни- мых процедур и триггеров. Это структурированный язык с поддержкой циклов for для множеств, условными переходами, обработкой ошибок и пересылкой событий. После создания код PSQL компилируется и сохраняется в двоичном виде. Триггеры имеют сильную поддержку с фазами До (Before) и После (After) каждого события манипулирования данными. Для каждой фазы/события может существовать множество триггеров, они могут содержать номера, задающие последовательность выполнения. Firebird 1.5 и выше поддерживает триггеры Before и After, которые об- рабатывают все три события манипулирования данными с условными переходами для каждого события. Ссылочная целостность Firebird имеет полную поддержку формальной, основанной на стандартах SQL, ссы- лочной целостности — иногда называемой декларативной ссылочной целост- ностью — включая необязательные каскадные изменения и удаления. Оперативное копирование базы данных Серверы Firebird могут при необходимости поддерживать создание оперативных ко- пий базы данных. Оперативная копия (shadow) является копией базы данных реаль- ного времени с некоторыми дополнительными атрибутами, которые делают ее не- доступной для чтения, пока она не будет сделана доступной сервером в качестве ба- зы данных. Оперативные копии могут переключаться либо вручную, либо автоматически. Назначение оперативного копирования — сделать базу данных дос- тупной в кратчайший срок при поломках диска. Оперативное копирование не является репликацией.
40 Введение Безопасность Безопасность сервера Firebird обеспечивает безопасность доступа пользователей к серверу с помощью идентификатора пользователя и зашифрованного пароля. Как и любой другой сервер базы данных, Firebird использует соответствующие средства защиты физического, сетевого доступа и файловой системы. Firebird может хранить зашифрованные дан- ные, но за исключением шифрования пароля он не предоставляет средств шифрова- ния самих данных. Д ВНИМАНИЕ! Поскольку встраиваемый сервер (см. разд. "Встраиваемый сер- вер") разработан для однопользовательских, автономных приложений, он совер- шенно не проверяет безопасность для хоста. Привилегии SQL, заданные на уровне базы данных, еще применяются, но приложение через встраиваемый сервер может получить доступ к любой базе данных на этом компьютере без указания пароля. (См. главу 34.) Привилегии SQL Хотя пользователь должен быть авторизован для доступа к серверу Firebird, но ника- кой пользователь, за исключением SYSDBA и владельца базы данных, не имеет ав- томатически никаких прав на индивидуальную базу данных. Безопасность на уровне базы данных поддерживается посредством привилегий SQL. Пользователям должны быть явно предоставлены привилегии к любому объекту. Роли SQL позволяют объединить несколько привилегий в группу и предоставить как "пакет" индивидуальным пользователям. Отдельный пользователь может иметь при- вилегии от нескольких ролей, хотя только одна роль может быть выбрана при соеди- нении с базой данных. Рабочие режимы Сервер Firebird может быть инсталлирован для выполнения в одном из трех рабочих режимов (operating modes): Суперсервер (Superserver), Классический сервер (Classic server) и Встраиваемый сервер (Embedded server). Различие между ними— скорее вопрос архитектуры. Любое клиентское приложение, написанное для соединения с Суперсервером, может соединяться точно тем же способом и с Классическим серве- ром и выполнять в точности те же задачи. Обратное также верно, за исключением того, что у Суперсервера более высокие требования к безопасности потоков для мо- дулей внешних функций (определенные пользователем функции, библиотеки набо- ров символов, BLOB-фильтры). Встраиваемый сервер является вариантом Суперсервера. Классический сервер Классический сервер предшествует Суперсерверу исторически. Он был разработан после 1980 года, когда ресурсы машин были скромными, и программы использовали
Введение 41 их весьма экономно. Модель Классического сервера являлась продолжением опера- ционных систем, чьи возможности по использованию потоков либо не существовали, либо были слишком ограничены для поддержания Суперсервера. Классический сер- вер остается лучшим вариантом для условий, где важна высокая производительность и использование системных ресурсов увеличивается линейно при добавлении каждо- го нового соединения. Поскольку Классический сервер может использовать множество центральных про- цессоров, он является весьма подходящим для сайтов, требующих выполнения мно- жества продолжающихся в реальном режиме времени приложений, использующих автоматически полученные коллекции данных, с минимальным или вовсе отсутст- вующим интерактивным вводом. С ПРИМЕЧАНИЕ. Классический сервер для Windows недоступен в версиях Firebird, предшествующих 1.5. Суперсервер В 1996 году в предшественнике Firebird, InterBase 4.1 появился многопоточный Су- персервер для новых тогда 32-битных платформ Windows. Он позволял лучше ис- пользовать новые возможности серверов и компьютерных сетей. Возможности Су- персервера исключить взаимоблокировку поточных процессов и динамически выде- лять кэш-память сделали его более удобным, чем Классический сервер, когда велико количество пользователей, выполняющих чтение/запись, а системные ресурсы огра- ничены. С бурным ростом операционных систем GNU/Linux на базе Intel к концу 1990-х го- дов Суперсервер стал хорошим решением для некоторых платформ POSIX. Основная структура Суперсервера для Linux была реализована в InterBase бета-версии 6.0 с открытыми исходными кодами и затем была полностью реализована в Firebird 1.0. Архитектура Суперсервера стала доступной для платформ Sun Solaris. Встраиваемый сервер Firebird 1.5 представил встроенный вариант Суперсервера для платформ Windows. В этой модели Суперсервер компилируется вместе с встроенным клиентом, который напрямую подключается к базе данных. Одна динамическая библиотека (fbembed.dll) использует межпроцессное коммуникационное пространство Windows для передачи клиентских запросов и ответов сервера. Его API идентичен API обычного Суперсер- вера или Классического сервера. В коде приложения не требуется ничего специаль- ного для использования встраиваемого сервера. Приложение, использующее встраиваемый сервер, может применять только локаль- ный метод доступа (см. главу 2) и поддерживает один и только один клиентский про- цесс. Вы можете использовать столько встроенных приложений на одной машине, сколько вам нужно, однако одна база данных может в каждый момент времени быть открыта только одним таким приложением. Приложение встраиваемого сервера мо-
42 Введение жет выполняться на той же машине одновременно с обычным сервером Firebird. Тем не менее с базой данных не может быть одновременно соединен обычный сервер и встраиваемый сервер. Встраиваемый сервер удовлетворяет самым низким требованиям масштабируемости сервера Firebird, давая возможность распространять одно высокопродуктивное при- ложение для одной базы данных с минимальными затратами. Поскольку к базе дан- ных может иметь доступ и обычный сервер, выполняющий репликацию при нерабо- тающем встроенном приложении, встраиваемый сервер особенно подходит для "компактных" установок — например, на ноутбук или даже на флэш-диск. Сравнение моделей Суперсервера и Классического сервера см. в главе 36. В той же главе в разд. "Работа со встроенным сервером” вы найдете полное описание работы со встроенным сервером под Windows. Пример базы данных Везде в этой книге языковые примеры используют пример базы данных, которая на- ходится в каталоге Examples в папке, где установлен Firebird. В дистрибутиве Fire- bird 1.0.x она называлась employee.gdb. В Firebird 1.5 это employee.fdb. Краткое описание примера базы данных содержится в приложении 6. Соглашения по документации Основной текст книги представлен этим шрифтом. Тексты, набранные данным шрифтом, являются кодом, скриптами или примерами командной строки. ПРИМЕЧАНИЕ. Фрагменты текста, выделенные как этот, — используются для того, чтобы привлечь ваше внимание к важным моментам, которые могут повли- ять на ваше решение по использованию обсуждаемой возможности Firebird. СОВЕТ. Фрагменты, выделенные как этот, содержат советы, блестящие идеи или рекомендации. ВНИМАНИЕ! Обратите особое внимание на такой фрагмент. Синтаксические шаблоны Некоторые фрагменты кода представляют синтаксические шаблоны (syntax patterns), то есть модели кода, которые демонстрируют обязательные и необязательные эле- менты синтаксиса операторов SQL или команд командной строки. Для синтаксиче-
Введение 43 ских шаблонов применяются определенные соглашения по используемым символам. Для иллюстрации этих соглашений возьмем из главы 20 пример, показывающий син- таксический шаблон для оператора SQL select: SELECT [FIRST (m) ] [SKIP (n) ] [[ALL] | DISTINCT] <список-столбцов> [, [имя столбца] | выражение I константа ] AS имя-алиаса] FROM <таблица-или-процедура-или-просмотр> [{[[INNER] [ [{LEFT | RIGHT | FULL] [OUTER]] JOIN}] <таблица -или-процедура -или-просмотр> ON <условия-соединения> [{JOIN..]] [WHERE <условия-поиска>] [GROUP BY <список-группируемых-столбцов>] [HAVING <условие~поиска>] [UNION <выражение-выбора> [ALL] ] [PLAN <выражение-плана>] [ORDER BY <список-столбцов>] [FOR UPDATE [OF столбец! [, столбец2. .]] [WITH LOCK]] Специальные символы Элементы (ключевые слова, параметры), которые обязательны во всех случаях, появ- ляются без каких-либо дополнительных пометок, они выделены таким шрифтом, как и весь код в книге. В предыдущем примере ключевые слова select и from являются обязательными для каждого оператора select. Некоторые символы, которые никогда не появляются в операторах SQL или в коман- дах командной строки, используются в синтаксических шаблонах для указания осо- бых правил по их использованию. Это символы [],{}, I, <строка> и ... (многоточие). Они используются в шаблонах следующим образом. Квадратные скобки [ ] указывают, что элемент(ы) в скобках являются необязатель- ными. Когда встречаются вложенные квадратные скобки, то вложенные или внешние элементы являются необязательными. Фигурные скобки { } указывают, что элементы внутри скобок являются обязатель- ными. Обычное использование фигурных скобок— это представление необязатель- ного элемента (заключенного в квадратные скобки), означающее: "Если необязатель- ный элемент используется, часть, заключенная в фигурные скобки, является обяза- тельной". В предыдущем примере, если используется необязательная явная фраза JOIN, [{[[INNER] | [{LEFT | RIGHT | FULL} [OUTER]] JOIN}] то внешняя пара фигурных скобок указывает, что ключевое слово join является обя- зательным. Внутренняя пара фигурных скобок означает, что если задано соединение outer, то оно должно быть определено как left, right или full с необязательным ис- пользованием ключевого слова outer.
44 Введение Символ вертикальной черты | используется для разделения взаимоисключающих элементов. В предыдущем примере left, right и full являются взаимоисключающи- ми, также внутреннее (inner) и внешнее (outer) соединения являются взаимоисклю- чающими. Параметры задаются строкой, которая заключается в угловые скобки < >. Например, [where <условмя-поиска>] указывает, что одно или более условий поиска требуется в качестве параметров для необязательного предложения where в синтаксисе оператора SELECT. В некоторых случаях <строка> может быть сокращением для более сложной конст- рукции, которая в последующих строках синтаксического шаблона будет раскры- ваться уровень за уровнем для получения полной детализации. Например, вы можете увидеть приблизительно следующее выражение: <условия-псмска> = <выражение~столбца> = <константа> | <выражение> Пара точек или многоточие ... могут быть использованы в некоторых синтаксиче- ских шаблонах для указания того, что текущий элемент является повторяемым. ПРИМЕЧАНИЕ. Ни один из этих символов не является допустимым ни в опера- торах SQL, ни в командах командной строки1. Достаточно вводных слов! Первые четыре главы предназначены для того, чтобы вы начали работать с Firebird — загрузка и инсталляция серверных и клиентских про- грамм, изменение некоторых основных установок сети, конфигурирование несколь- ких установок, если значения по умолчанию не совсем подходят для вашей среды. И, в заключение, в главе 4 начало работы с сервером и базой данных с использовани- ем основного клиентского инструмента. ' Квадратные скобки могут присутствовать в операторах DDL для задания размерности массивов. —Прим, перев.
ЧАСТЬ I Учебный лагерь Глава 1. Инсталляция Глава 2. Установка сети Глава 3. Конфигурирование Firebird Глава 4. Основные операции

ГЛАВА 1 Инсталляция В этой главе описывается, как получить инсталляционный комплект для платформы, а также версии сервера Firebird, которые вы хотите установить на вашу серверную машину. Полный вариант инсталлятора устанавливает и сервер и клиент на одну ма- шину. Удаленным клиентам не требуется сервер Firebird вовсе. Процедура инсталляции клиента Firebird несколько изменяется в зависимости от платформы (см. разд. "Ин- сталляция клиентов" в главе 7). Если вы в Firebird новичок, не пытайтесь устанавли- вать только клиента, пока не разберетесь, как все части инсталляции по умолчанию соответствуют друг другу. Системные требования Память на сервере (все платформы) Оценка памяти сервера включает множество факторов. ♦ Работа сервера Firebird. Сервер Firebird осуществляет эффективное использова- ние ресурсов сервера. Суперсервер (Superserver) после старта использует прибли- зительно 2 Мбайта памяти. Классический сервер (Classic server) в POSIX не ис- пользует памяти, пока не установлено клиентское соединение. В Windows не- большие сервисы прослушивают запросы на соединения. ♦ Клиентские соединения. Каждое соединение с Суперсервером добавляет прибли- зительно 115 Кбайт, больше или меньше, в соответствии со стилем и характери- стиками клиентских приложений, а также спроектированной схемой базы данных. Каждое соединение с Классическим сервером использует приблизительно 2 Мбайта (в зависимости от количества применяемых соединением таблиц, триг- геров, процедур и других объектов — может быть и до 30—40 Мбайт. Для баз данных среднего размера — от 4 до 15 Мбайт). ♦ Кэш базы данных. Значение по умолчанию может конфигурироваться — в стра- ницах базы данных. Суперсервер использует единый кэш (с размером по умолча- нию 2048 страниц) для всех соединений и автоматически увеличивает кэш при необходимости. Классический сервер создает индивидуальный кэш (по умолча- нию 75 страниц) на каждое соединение.
48 Часть I. Учебный лагерь На основании существующих оценок отводите 64 Мбайта RAM для сервера и 16 Мбайт для локального клиента. Чем больше клиентов вы добавляете, тем больше памяти будет использовано. Базы данных с большим размером страниц используют ресурсы из большего участка памяти, чем базы данных с меньшим размером страниц. Использование ресурсов для Классического сервера увеличивается линейно с каж- дым новым подключением клиента; для Суперсервера ресурсы разделяются между несколькими подключениями, и будут динамически увеличиваться при необходимо- сти. Firebird 1.5 будет использовать для сортировки, если она необходима, дополни- тельную RAM. Использование памяти более подробно обсуждается в главе 6. Инсталляционные диски Сервер Firebird — и любые базы данных, которые вы создаете или с которыми со- единяетесь, — должны находиться на жестком диске, который физически подключен к машине. Вы не можете разместить компоненты сервера или любой базы данных на назначенном диске, в разделяемой файловой системе или в сетевой файловой сис- теме. CD-ROM Вы не можете запускать сервер Firebird с CD-ROM. При этом вы можете соединяться с базой данных только для чтения, находящейся на устройстве CD-ROM, физически подключенным к серверу1. Дисковое пространство При оценке дискового пространства, необходимого для инсталляции, просмотрите размеры следующих исполняемых файлов. Дисковое пространство сверх указанного минимума требуется для файлов баз данных, оперативных копий (если используют- ся), файлов сортировки, протоколов и копий баз данных. ♦ Сервер. Минимальная инсталляция сервера требует дискового пространства в пределах от 9 до 12 Мбайт в зависимости от платформы и архитектуры. ♦ Клиентская библиотека. Требует 350 Кбайт (встроенная: 1.4—2 Мбайт). ♦ Инструменты командной строки. Требуют приблизительно 900 Кбайт. ♦ Утилиты администратора базы данных. Требуют 1—6 Мбайт в зависимости от выбранных утилит. См. список свободно распространяемых и коммерческих ути- лит в приложении 5. 1 Не думайте, что копирование файла базы данных на компакт-диск сделает базу данных базой только для чтения. База данных должна быть сделана только для чтения с использовани- ем утилит gbak или gfix до записи на компакт-диск. См. соответствующие главы по инстру- ментам в части IX.
Глава 1. Инсталляция 49 Минимальные требования к машине Минимальные требования зависят от того, как вы планируете использовать систему. Вы можете запустить сервер и разрабатывать схемы баз данных на персональном компьютере с минимальной конфигурацией— даже на "быстром" 486 или на Pentium II с 64 Мбайт RAM будет работать Firebird 1.0.x— но такая конфигурация не позволит использовать многие возможности при работе в сети. Для версии 1.5 и бо- лее поздних процессор 586 с 128 Мбайт RAM может рассматриваться как минимум. Windows более требовательна к CPU и оперативной памяти, чем Linux, в которой запускается сервер на консольном уровне. Версии операционной системы влияют на требования: некоторые платформы UNIX требуют больше ресурсов как для сервера, так и для клиента, а требования некоторых версий Windows неприменимы к указан- ным характеристикам, независимо от требований программного обеспечения. Поддержка SMP и HyperThreading Суперсервер и Классический сервер Firebird могут использовать разделяемую память мультипроцессоров в Linux. В Windows поддержка SMP (симметричный мультипро- цессор) доступна только для Классического сервера. Технология HyperThreading ненадежна, похоже, она зависит от нескольких условий, включая платформу операционной системы, поставщика оборудования и версию сер- вера. Некоторые пользователи сообщают об успешном использовании, другие имеют проблемы. Если у вас есть машина с такими возможностями, проверьте вначале вы- бранный вами сервер на эту возможность и будьте готовы отменить ее на уровне BIOS, если увидите замедление в работе. Характеристики процессора могут быть сконфигурированы на уровне сервера в firebird.config (версия 1.5) или в ibconfig/isc config (версия 1.0.x). В Window's для Су- персервера версий 1.0.x и 1.5 маска свойств CPU должна быть установлена в "единст- венный CPU" для SMP-машины. (См. разд. "Файл конфигурации Firebird" в главе 36.) Операционная система В табл. 1.1 показаны минимальные требования к операционной системе для выпол- нения серверов Firebird. При этом всегда смотрите файл README в каталоге /doc в вашем комплекте поставки для получения последней информации по операционной системе. Таблица 1.1. Минимальные требования Firebird к операционной системе Операционная система Версия Примечания Microsoft Windows NT 4.0 Требуется Service Pack 6а Windows 95/98/МЕ Возможно, нужны обновления: библиотеки времени выполнения Microsoft С (msvcrt.dll) версии 6 или вы- ше. Для Firebird 1.5 требуется библиотека C++ (msvcrt60.dll или выше). В Firebird 1.5 копии распола- гаются в каталоге \brrt каталога Firebird. Winsock 2 требуется для всех серверов; он может быть нужен для инсталляции под Windows 95
50 Часть I. Учебный лагерь Таблица 1.1 (окончание) Операционная система Версия Примечания Windows 2000 с Service Pack 2 Windows ХР Базы данных не должны иметь расширения gdb Server 2003 Базы данных должны располагаться в разделах, где возможность VSS (оперативное копирование тома) отключена Linux Red Hat Версия 7.1 или выше для Firebird 1.0.x, версия 8.0 или выше для Firebird 1.5. Для Red Hat 9 и выше обратитесь к замечаниям по инсталляции Linux в соответствии с реализацией NPTL SuSE Версия 7.2 или выше для Firebird 1.0.x, вер- сия 8.10 или выше для Firebird 1.5 Mandrake Версия 8.0 или выше для Firebird 1,0.x, версия 9.0 или выше для Firebird 1.5 Все платформы Linux Firebird 1.5 (сервер и клиент) требует glibc-2.2.5 или выше и libstdc++.so, связанную с libstdc++-5.0 или выше Другие ОС Solaris (Intel, SPARC), Mac OS X, FreeBSD, HP-UX 10+ Подробности смотрите в комплекте поставки Firebird Как получить инсталляционный комплект Комплект Firebird можно найти на главном сайте Firebird (http://www.firebirdsql.org) или на сайте SourceForge (http://firebird.sourceforge.net). Ссылки на этих страницах приведут вас к: http://sourceforge.net/project/showfiles.php?group_id=9028. Главная страница на сайте Firebird обычно содержит список ссылок на последние релизы для Linux и Windows. Другие ссылки будут указывать на дистрибутивы для других платформ. Если файл в своем имени содержит "src", то это созданный исход- ный код, а не инсталляционный пакет. Дистрибутивы, имеющие в своем имени "de- bug", "debuginfo" или "pdb", содержат специальные файлы для анализа и отладки сбоев сервера или клиента с помощью сред разработки и не требуются для обычной работы. Содержание комплекта Каждый инсталляционный комплект содержит все компоненты, нужные для инстал- ляции Firebird. ♦ Исполняемая программа сервера Firebird. ♦ Множество других программ, нужных при инсталляции и/или во время выполне- ния задач.
Глава 1. Инсталляция 51 ♦ Скрипты командной строки или командные файлы, нужные при инсталляции, которые также могут использоваться как утилиты сервера. ♦ Файл безопасности баз данных (isc4.gdb для версии 1.0.x; security.fdb для вер- сии 1.5). ♦ Одна или более версий клиентской библиотеки для установки на сервере и на клиентской рабочей станции. ♦ Инструменты командной строки. ♦ Стандартные библиотеки внешних функций и скрипты их описания (*.sql). ♦ Пример базы данных. ♦ Заголовочные файлы С (не нужны начинающим!). ♦ Текстовые файлы, содержащие самые последние замечания для использования в процессе инсталляции и конфигурирования. ♦ Заметки по релизу и различные файлы README (необходимы для прочтения). Соглашения по именованию в комплекте инсталляции Имена файлов комплекта поставки для разных платформ не являются одинаковыми. Увы, они даже не являются "последовательно неодинаковыми"; при создании дист- рибутива часто нужно приспосабливаться к специфическим для платформы соглаше- ниям или просто следовать их собственным правилам. Тем не менее некоторые эле- менты в именах файлов могут быть вам полезны для идентификации того комплекта, который вам нужен. Классический сервер или Суперсервер? Обычно первой частью имени является строка "Firebird". ♦ Если релиз для Windows поддерживает Классический сервер, он будет включен в тот же инсталлятор, что и Суперсервер. ♦ Для платформ POSIX, которые поддерживают обе архитектуры, отдельные ин- сталляторы поставляются для Классического сервера и Суперсервера. Имя уста- новочного пакета будет начинаться с "FirebirdCS" (для Классического сервера) или с "FirebirdSS" (для Суперсервера). ♦ Для меньших платформ архитектура может быть менее очевидной, и первая часть имени может быть названием ОС или аппаратной платформы. Номера версий Имена всех комплектов поставки должны содержать разделенную точками строку чисел в следующем порядке: номер версии, номер релиза, номер подрелиза. Напри- мер, "1.0.3"— это третий подрелиз от начального (код С) релиза Firebird версии 1, в то время как "1.5.0" — начальный подрелиз релиза 5 (код C++) версии 1. Большин-
52 Часть I. Учебный лагерь ство комплектов поставки также содержат абсолютный номер создания (например, 1.0.3.972 или 1.5.2.4731). Для некоторых малых платформ, особенно тех, которые имеют собственные правила именования и созданы различными компиляторами, но- мера версий могут быть менее очевидными. 64-битовый ввод/вывод Для платформ, которые требуют специальной компоновки для поддержки 64-бито- вого ввода/вывода, вам нужно посмотреть на инфикс "6410" где-нибудь в строке имени. Он не будет присутствовать в именах комплектов поставки для операционных систем, которые осуществляют автоматическую поддержку 64-битового ввода/вы- вода. Не пытайтесь инсталлировать комплект, отмеченный как "6410", на версии, где ОС, файловая система или аппаратура не поддерживают 64-битовый ввод/вывод2. Архитектура CPU Архитектура CPU обычно включается в имя строки инсталляционного комплекта. Например, инсталлятор RPM для UNIX обычно включает указание на набор микро- схем (например, i686). Список файлов отображает в списке загрузки обычно наибо- лее полезный указатель минимального набора микросхем, поддерживаемого инстал- ляционным пакетом. Комплект для Solaris предполагает наличие процессора Intel, если в имени комплекта поставки не присутствует "SPARC". Зеркальные сайты Когда вы найдете требуемый комплект поставки, щелкните мышью по гиперссыл- ке — имени файла. Вы перейдете на список зеркальных сайтов, как показано на рис. 1.1. Не имеет значения, какой зеркальный сайт вы выберете — комплект поставки иден- тичен для всех сайтов. Комплект поставки для Linux Прокручивайте отображаемый в SourceForge список файлов, пока не увидите файлы, показанные на рис. 1.2. Здесь представлены реальные исполняемые инсталляторы. Доступны инсталляторы RPM и инсталляторы сжатых файлов (TAR-файлы). Если ваш дистрибутив Linux поддерживает инсталляторы RPM, выберите именно его. Он создаст каталоги и уста- 2 Под 64-битовым вводом/выводом имеется в виду поддержка в сервере, операционной системе и файловой системе работы с файлами размером больше 4 Гбайт, что требует не ста- рой 32-битовой адресации, а 64-битовой. Это не имеет отношения к поддержке 64-разрядных процессоров. — Прим. науч. ред.
Глава 1. Инсталляция 53 Yuui download should begin shortly. If it does not, try httn: dieanHt.dl.souiceforue.net somcefonie firebird.FireliirdCS 1.5.0.1ZH1 (I,ibUG.ipm nr choose a different rnirrur You are using mirror: heanet.dl.sourceforge.net Host Location Continent Download i Dublin, Ireland Europe 2772 kb Minneapolis, MN 1 North America 2772 kb Reston , VA North America ВЙВМИВВИИвИйИИИИ Select Preferred Mirror . .T hea n et (1EB Рис. 1.1. Выбор зеркального сайта SourceForge Package 0 ... . Filename & Notes Size firebird [show only this package] firebird-linux-i386 [show only this package] в 1 11) Rilei i j D/L Arch. Type 2004-02-19 21:00 hrt,bjfch S 1 5 i 42 чг 0 ib°& rpm 2837886г в .гртв FirebirdCS-1.5.0.4290-0.i6S6.tar.gz FirPbfrdCs-dRhiHUnfn-i 5 И ‘Kt'QD- 2802184 □ 1386 0 i386 gz 0.i686.tar.gz uo±z406 .gz FirebirdSS-l 5 0.4290-01686 .ram : В3003269 . : В В . i: B B 0: i386:- BBB. .rpm FirebirdSS-l.5.0,4290-0 .i686.tar.gz 2990059 0 i386 .gz FirebirdSS-debuginfo-1.5.0.4290- O.i686.tar.gz 13512109 0 1386 .gz 1.0.3-Release [show only this release] 2003-06 02 22:00 FirebirdCS-1.0.3.972-0.6410.1.386.rpm 2649789 1329 i386 .rpm FirebirdCS-l.u. j ,y72-u.64io .tar.gz 2618305 5821386 .gz Fitebrri.CS 10 3 0 BBr rim 2c49105 3i68:i3863в .rpm FirebiraCb-1 ,U,3.9 /г-O .tar.qz 2618227 1406 ,386 ,gz FirebirciSS-l.0.3.972-0.6410.lOSo.rprn 2676186 1260 1386 .rpm FirebirdSS-l.0.3.972-0.6410.tar.gz 2660131 637 1386 .gz Fireb;rdSS-l .0.3.972-01386 rpm 2675525 53674386 .rpm FirebirdSS-l.0.3 972-0 tar qz 2659575 1879 1386 ,gz Рис. 1.2. Комплект поставки для Linux на SourceForge
54 Часть I. Учебный лагерь новит все необходимое, определит пароль для пользователя SYSDBA и запустит вы- бранный вами сервер. Инсталляторы имеют следующие имена: ♦ Firebird 1.5 — FirebirdCS-1.5.2.4731-0ii686.rpm (Классический) и FirebirdSS-1.5.2.4731-0.i686.rpm (Суперсервер); ♦ Firebird 1.03 — FirebirdCS-1.0.0.972-0.i386.rpm (Классический) и FirebirdSS-1.0.0.972-0.i386.rpm (Суперсервер). Посмотрите документацию соответствующей платформы по использованию Red Hat Package Manager (RPM). В большинстве дистрибутивов у вас есть возможность за- пускать инсталлятор RPM из командной строки или через графический интерфейс пользователя (GUI). Сжатые файлы (tarballs) В дистрибутивах Linux, которые не могут выполнять пакеты RPM, и во многих раз- новидностях UNIX используйте сжатые файлы (обычно .tar.gz или .bz2), т. к. они да- ют опытному пользователю Linux больший контроль над процессом инсталляции. Соответствующая утилита распаковки понадобится на вашем сервере для распаковки комплекта поставки в вашей файловой системе. Вы найдете детальные инструкции в официальных замечаниях по релизу, README-файлах и замечаниях к поставке. Знающие пользователи могут также просмотреть и настроить инсталляционные скрипты, чтобы сделать их работоспособными в менее общих версиях Linux. СОВЕТ. Скрипты поставляются для командной строки. В некоторых случаях в замечаниях к поставке могут быть инструкции, как изменять скрипты и выполнять некоторые ручные настройки. Скрипты описываются далее в этой главе. В любом случае прочтите все поставляемые текстовые файлы, а также соответст- вующие темы в официальных замечаниях по релизу, которые относятся к той версии Firebird, которую вы собираетесь инсталлировать. Могут существовать серьезные отличия между совместимыми с POSIX ОС дистрибутивами и релизами, особенно в случае открытых исходных текстов. Где только возможно, в комплекте поставки для каждой версии Firebird в замечаниях по релизу делается попытка документировать вопросы, связанные с различными версиями ядра и дистрибутивами. СОВЕТ. Если вы не нашли замечания по релизу в вашем комплекте поставки, обратитесь к главной странице сайта IBPhoenix (http://www.ibphoenix.com) и загрузите замечания по релизу оттуда. Реализация NPTL для высших версий Linux Новая библиотека потоков POSIX (Native POSIX Thread Library, NPTL) в Red Hat 9 (и, возможно, в более поздних дистрибутивах Linux) создает проблемы с Суперсер- вером и локально скомпилированными программами, а также с утилитами. Утилита
Глава 1. Инсталляция 55 gbak сообщает об ошибке "broken pipe". Для устранения этой ошибки выполните сле- дующие шаги: 1. Проверьте, что запущен сервер. В /etc/init.d/firebird выполните: LD~ASSUME_KERNEL=2.2.5 export LD_ASSUME_KERNEL 2. Вам нужно установить переменную окружения в соответствии с локальным ок- ружением, так что добавьте следующее в /etc/profile, чтобы быть уверенным, что каждый пользователь может ее использовать в утилитах командной строки. После HISTSIZE=1000 добавьте LD_ASSUME_KERNEL=2.2.5 С помощью следующей строки экспортируйте ее: export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE INPUT_RC LD_ASSUME_KERNEL Комплект поставки для Windows Официальный комплект поставки для Windows (рис. 1.3) распространяется в виде исполняемых инсталляторов. Очень рекомендуется использовать инсталлятор в вари- антах ZIP или RAR. Package fc^otes Filename firebird [show only this package] Date Size D/L Arch. Type firebird-win32 # 1.5.0-Release 2004-02-19 21:00 Firebird-1.5.0.4290_debug_win32.zip 3098378 0 i386 .zip Firebird-1.5.0.4290_embed_win32.zip 1515889 0 i386 .zip Firebird-1,5.0.4290_win32.zip 3428584 0 i386 .zip Firebird-1 5,0 4306-Wih32:®e:3eg|fJ| ||Ш13949197<: 03386 .exe (32-bit Windows) # 1.0.3-Release 2003-06 02 22:00 Firebird-1.0.3.972-Win32 .exe 2986195 34952 i'386 .exe (32-bit Windows) Рис. 1.3. Комплект поставки Windows с сайта SourceForge Комплекты поставки Firebird включают исполняемые программы и связанные файлы для двух моделей сервера: Суперсервер и Классический сервер. В диалогах инсталля- тора вас будут спрашивать, какую модель вы хотите устанавливать, какие компонен- ты не должны копироваться на диск. Единственная поддерживаемая модель для Fire- bird 1,0.x — это Суперсервер.
56 Часть I. Учебный лагерь Имена релизов инсталляторов следующие: ♦ Firebird 1.5 — Firebird-1.5.2.473 l_Win32.exe; ♦ Firebird 1.03 — Firebird-1.0.3.972-Win32.exe. Сжатые файлы Если комплект поставки для Windows содержит ZIP-файлы, вам нужно иметь утили- ты (например, WinZip, PKZip или WinRAR) для просмотра содержимого и/или рас- паковки файлов перед инсталляцией. Такой комплект поставки (не для начинающих) содержит следующее: ♦ версия 1.5 ZIP полной поставки клиент-сервер Firebird-1.5.2.4731_ win32.zip. Мо- жет быть распакован в стандартный каталог без инсталляции в системе. Некото- рые программы инсталляции должны запускаться после распаковки. Инструкции включены в различные текстовые файлы в подкаталоге /doc; ♦ отдельный комплект поставки встраиваемого сервера версии 1.5 для Windows. Имя файла релиза комплекта поставки — Firebird-1.5.2.473 l embed_win32.zip. ПРИМЕЧАНИЕ. Не существует встраиваемого сервера для версии 1.0.x. Серверы На платформах, имеющих сервисы — Windows NT, 2000 и ХР — сервер Firebird ин- сталлируется по умолчанию для запуска как сервис. Сервис инсталлируется и запус- кается автоматически по окончании процедуры инсталляции, а также при первона- чальной загрузке серверной машины. Как остановить и запустить сервер вручную см. главу 4. Младшие из платформ Windows — Windows 95, 98 и ME — не поддерживают серви- сы. После инсталляции сервер Firebird будет запускаться как приложение, защищен- ное программой Guardian. Если приложение сервера будет по разным причинам ава- рийно завершено, Guardian постарается заново запустить его. Для сервера, запущен- ного как сервис, рекомендуется также использовать Guardian. Не пытайтесь инсталлировать Классический сервер, если у вас уже установлен Су- персервер, или наоборот. Клиентские библиотеки Копии клиентских библиотек устанавливаются: ♦ для Firebird 1.0.x имя клиентской библиотеки gds32.dll; она устанавливается в системный каталог C:\WINNT\system32 для Windows, имеющей сервисы, и в C:\Windows для других версий Windows; ♦ для Firebird 1.5 и последующих версий имя клиентской библиотеки fbclient.dll; по умолчанию она устанавливается в каталог /bin корневого каталога Firebird. По умолчанию утилиты загружают ее именно оттуда, а не из системного каталога.
Глава 1. Инсталляция 57 ПРИМЕЧАНИЕ. Для обычных клиентских приложений, включающих множество компонентов базы данных и инструменты администратора, размещение и имено- вание клиентской библиотеки не являются столь строгими. См. в главе 7 альтер- нативные варианты инсталляции клиентской библиотеки — на сервере и на кли- ентских рабочих станциях — для совместимости со многими графическими инст- рументами и другими существующими приложениями. Тестирование результатов инсталляции Если все работает, как описано, сервер Firebird будет запущен на вашем сервере по окончании процесса инсталляции. Вы можете запустить некоторые тесты для про- верки инсталляции и выполнить нужные настройки вашей конфигурации. Сетевой протокол Предполагается, что вы будете использовать рекомендованный протокол TCP/IP для вашей сети клиент-сервер, чтобы получить все преимущества независимой от плат- формы сети. СОВЕТ. Чтобы получить информацию об использовании протокола NetBEUI (Named Pipes, Именованные каналы) во всех версиях Windows, см. разд. "Сете- вые протоколы" в главе 2. ВНИМАНИЕ! Firebird не поддерживает IPX/SPX и не работает на Novel Netware 3 и 4. Тестирование сервера Обычно первое, что вы захотите сделать после завершения инсталляции, — это про- верить обращение к серверу. Это даст вам реальную возможность убедиться, что ва- ша клиентская машина может видеть хост в вашей сети. Предположим, что IP-адрес вашего сервера в домене, видимый вашему клиенту, 192.13.14.1. Перейдите в ко- мандную строку и введите следующую команду: ping 192.13.14.1 Замените в этом примере IP-адрес на реальный IP-адрес вашего сервера. СОВЕТ. Если вы получили сообщение об истечении времени ожидания, обрати- тесь к главе 2 и приложению 2 для дальнейших инструкций. Если вам нужна бо- лее подробная информация об установках серверных IP-адресов, см. разд. "Се- тевой адрес для сервера" в главе 2. Если вы соединяетесь с сервером с локального клиента — т. е. клиент запущен на той же машине, что и сервер, — вы можете обратиться к виртуальной заглушке TCP/IP:
58 Часть I. Учебный лагерь ping localhost ИЛИ ping 127.0.0.1 Проверка, что сервер Firebird запущен Классический сервер POSIX Используйте команду рз в командной строке для просмотра запущенных процессов. Если какие-нибудь клиенты соединены с классическим процессом Firebird, вы долж- ны увидеть один процесс с именем fbjnet server (или gds_inet_server для Firebird 1.0.x) для каждого соединенного клиента. Команда ps имеет несколько пере- ключателей, однако следующий вариант дает подходящий список. Команда grep фильтрует вывод так, что вы будете видеть только процессы Firebird. [ххх]$ ps -aux | grep fb На рис. 1.4 запущены три клиентских процесса. USER PID «CPU #MEM VS2 RSS TTY STAT START TIME COHMAKD root 1046 0.0 0.7 6920 3552 ? S 00:07 0:00 fb_inet_sei:ver root 1052 0.1 0.6 6608 2916 ? S 00:10 0:00 fb_inet_server root 1056 1.0 0.7 6920 3548 ? S 00:12 0:00 Eb_inet_server Рис. 1.4. Список классических процессов, полученный с помощью ps Суперсервер POSIX Поскольку Суперсервер разветвляется на потоки для каждого соединения, будет ин- тересным задать переключатель -f [ork] среди других переключателей для отображе- ния его процессов и потоков. Вы получите форматированное отображение разветв- ленных процессов, похожее на представленное на рис. 1.5: [ххх]$ ps -auxf | grep fb USER PID #CPU 0ИЕМ vsz RSS TTY STAT START TIME COHMAKD root 1215 0.0 0.2 3608 1348 ? s 18:43 0:00 /opt/firebird/bin/fbguard -f root 1216 0.0 1.0 65664 5068 ? s 18:43 0:00 /opt/firebird/bin/fbserver root 1217 0.0 1.0 65664 5068 ? 5 18:43 0:00 /opt/firebird/bin/fbserver root 1220 0.0 1.0 65664 5068 ? S 18:43. 0:00 /opt/firebird/bin/fbserver root 1978 0.0 1.0 65664 5068 ? s 18:48 0:00 /opt/firebird/bin/fbserver root 1979 0.0 1.0 65664 5068 ? s 18:46 0:00 /opt/firebird/bin/fbserver root 1980 0.0 1.0 65664 5068 ? s 18:48 0:00 /opt/firebird/bin/fbserver root 1981 0.0 1.0 65664 5068 ? 5 18:48 0:00 /opt/firebird/bin/fbserver Рис. 1.5. Список процессов и потоков Суперсервера, полученный с помощью рз Та же команда рз должна отображать один процесс с именем fbguard (или ibguard), если сервер был запущен с переключателем -fforever], и один главный процесс с
Глава 1. Инсталляция 59 именем fbserver (или ibserver). Должен быть, по меньшей мере, один дочерний поток с именем fbserver (или ibserver), разделенный на несколько потоков. Эта первая груп- па является "выполняющимся сервером" без клиентских соединений, за исключением тех, которые использует сервер для прослушивания портов и сборки мусора. Далее будет группа потоков для каждого соединения. ПРИМЕЧАНИЕ. Префикс "fb" относится к Firebird 1.5, a "gdb" и "ib"— к Fire- bird 1.0.x. Используйте ps -aux | grep gds, если у вас запущена версия 1.0.x. Windows NT 4,2000 и ХР Для серверных платформ Windows запустите апплет Firebird Server Control с Панели управления (Control Panel). Апплет Server Control На рис. 1.6 показан апплет Firebird Server Control, запущенный под Windows 2000 Server. Если вы использовали инсталлятор, этот апплет будет установлен на вашу Панель управления. Его внешний вид может изменяться в зависимости от варианта сервера Windows. Рис. 1.6. Апплет Firebird Server Control Вы можете использовать этот апплет для запуска и остановки сервиса и для модифи- кации режимов запуска и выполнения. Не рекомендуется изменять режим на Run as an application (Выполнять как приложение) для многопользовательского использо- вания в целях безопасности — вы должны оставлять подключение к серверу, чтобы сервер Firebird оставался запущенным.
60 Часть I. Учебный лагерь Апплет Service Если у вас нет апплета Control Panel, вы можете использовать апплет Services (рис. 1.7) в Инструментах администрирования. В Windows NT 4 вы можете получить доступ к этому апплету напрямую через Панель управления. Action View » " > -и ,0 (В d? ; ч vJsJjalJ Tree J Name Description Status i Startup Type I Log On As *• i Services (Local; ^gjEvent Log ^Fax Service Firebird Guardian - DeFaultinstance Firebird Server - Defaultinstance ^Jlndexing Service ^Internet Connection Sharing ^IPSEC Policy Agent Logical Disk Manager nnira! Disk Mananer Administrah... Logs event... Helps you ... Provides n... Manages I,,. Logical Disk.,. Arlroinistrat.. Started Started Started Started Started Automatic Manual Automatic Manual Manual Manual Automatic Automatic Manual LocalSystem •• Localsystem ~ J LocalSystem LocalSystem LocalSystem LocalSystem I LocalSystem LocalSystem <) 1 nralSvsfem. Mil ' " ' - - - - Рис. 1.7. Апплет Services на серверных платформах Windows На рис. 1.7 показаны имена сервисов Firebird 1.5— для Guardian и сервера. Имена сервисов могут отличаться в зависимости от версий, Guardian может вовсе не появ- ляться в списке. Пользователь с привилегиями администратора может, щелкнув пра- вой кнопкой мыши по имени сервиса, остановить или запустить сервис. Если вы ис- пользуете Guardian, остановите этот сервис, чтобы остановить и Guardian, и сервер. В Windows 2000 и Windows Server 2003 Guardian скорее удобен, чем необходим, по- скольку эти две операционные системы имеют средства просмотра и повторного за- пуска сервисов. Рекомендуется оставлять Guardian активным на других платформах, если пользователь SYSDBA недоступен для рестарта сервера вручную в случаях, когда он был остановлен по различным причинам. Другие апплеты Панели управления Если вам нужен апплет Firebird Manager и вы не нашли его инсталлированным на Панели управления вашего сервера Windows, или если вам нужен апплет с языком, отличным от английского, загрузите его с сайта Firebird: http://www.ibphoenix.com. Закройте окно Панели управления и скопируйте файл CPL непосредственно в ваш системный каталог Windows. Windows 9х, ME и ХР Home Edition Windows 9х, ME и ХР Ноте Edition не поддерживают сервисы. Сервер Firebird дол- жен быть запущен как приложение, контролируемое программой Guardian. Если вы используете инсталляционный комплект, который устанавливает, но не запускает автоматически Guardian и сервер Firebird, вы можете сделать это вручную, как пока- зано далее.
Глава 1. Инсталляция 61 1. Найдите исполняемый файл программы Guardian (ibguard.exe) и создайте для него ярлык в области запуска меню кнопки Пуск. 2. Откройте диалоговое окно Свойства для этого ярлыка и перейдите к полю, где располагается командная строка. 3. Отредактируйте командную строку так, чтобы она выглядела следующим об- разом: fbguard.exe -а 4. Сохраните и закройте диалог Свойства. 5. Сделайте двойной щелчок по ярлыку для запуска Guardian. Guardian запустит fbserver.exe. Теперь Guardian должен запускаться автоматически, когда вы будете выполнять пер- воначальную загрузку вашей машины. Апплет Server Control Некоторые версии апплета Server Control могут быть установлены на платформе Windows, не поддерживающей сервисы. Если инсталлятор устанавливает его на ва- шей машине, то он может быть использован так же, как было описано для версий, поддерживающих сервисы. Невозможно только выбрать вариант Run as a service (Запускать как сервис), даже если он отображается на экране. В Windows версии Ноте, чтобы исключить путаницу, апплеты бывают скрытыми или отображаются недоступными (серым цветом). Инсталляция встраиваемого сервера Д ВНИМАНИЕ! Если вы раньше не использовали Firebird, то очень рекомендуется пропустить этот вариант, пока вы не приобретете опыт работы с сервером Firebird и "регулярными" клиентами. Вы ничего не потеряете, разрабатывая ваши первые приложения в обычной модели клиент-сервер; они будут работать так же хорошо и с встраиваемым сервером. Объединение сервера и клиента осуществляется через динамическую библиотеку fbembeded.dll, которую вы найдете в каталоге /bin после обычной инсталляции Firebird. Вы можете инсталлировать встраиваемый сервер, если вы уже инсталлиро- вали сервер или другие встраиваемые серверы. Для каждого приложения встраиваемого сервера каталог исполняемого файла вашего приложения становится корневым каталогом этого приложения встраиваемого серве- ра. Для выполнения встроенной инсталляции с вашим приложением сделайте сле- дующее: ♦ скопируйте fbembeded.dll в каталог приложения и переименуйте в fbclient.dll или в gds32.dll в соответствии с тем, какое имя клиентского файла требуется вашей программе связи с базой данных; ♦ скопируйте файлы firebird.msg, firebird.conf и aliases.conf в тот же каталог;
62 Часть I. Учебный лагерь ♦ если вы собираетесь использовать алиасы базы данных (рекомендуется), то ско- пируйте aliases.conf в каталог приложения (домашний каталог, home directory) и сконфигурируйте его для этого конкретного приложения; ♦ если внешние библиотеки требуются для вашего приложения, такие как поддерж- ка интернациональных языков (fbintl.dll), библиотеки UDF или библиотеки Blob- фильтров, создайте для них соответствующие каталоги (../inti, ../UDF) непосред- ственно внутри каталога вашего приложения и скопируйте туда эти файлы. Пример структуры встроенной инсталляции Приведем пример структуры каталогов и содержание файлов конфигурации для ин- сталлированного приложения встраиваемого сервера: D:\my_app\MyApp.ехе D:\my_app\gds32.dll D:\my_app\fb\firebird.conf D:\my_app\fb\aliases.conf D:\my_app\fb\firebird.msg D:\my_app\fb\intl\fbintl.dll D:\my_app\fb\UDF\fbudf.dll Файл firebird.conf: RootDirectory = D:\my_app\fb Файл aliases.conf: MyApplication = D:\databases\MyDB.fdb Другие вещи, которые вам нужно знать Пользователи Имя пользователя и пароль по умолчанию Пользователь SYSDBA имеет все привилегии доступа к серверу. Программа инстал- ляции создаст пользователя SYSDBA в базе данных безопасности (security.fdb). Для версий под Windows и версии 1.0.x под Linux пароль masterkey. СОВЕТ. Фактически пароль — masterke, т. к. все символы после восьмого игно- рируются. В версии 1.5 и более поздних под Linux инсталлятор в процессе установки генериру- ет случайный пароль, помещает его в базу данных безопасности и сохраняет в тек- стовом файле SYSDBA.password. Запомните этот пароль или используйте для досту- па к базе данных безопасности, чтобы изменить его на что-нибудь более простое для запоминания.
Глава 1. Инсталляция 63 ВНИМАНИЕ! Если ваш сервер совсем не защищен в Интернете, вы должны из- менить этот пароль немедленно. Как изменить пароль пользователя SYSDBA Если вы работаете в Linux или другой системе, которая может выполнять SH- скрипты, измените текущий каталог на ../bin в вашем каталоге инсталляции и найди- те скрипт с именем changeDBAPassword.sh. Все, что вам нужно сделать, — это запус- тить скрипт и ответить на подсказки. Когда вы в первый раз запустите скрипт, вам будет нужно ввести пароль, который инсталлятор записал в файл SYSDBA.password; он находится в корневом каталоге Firebird: [bin]# sh changeDBAPassword.sh ИЛИ [bin]# ./changeDBAPassword.sh Прямое использование gsec Следующая процедура будет работать под Windows и Linux. В Linux, чтобы запус- тить gsec, вы должны войти в операционную систему как суперпользователь (root). Пусть вы решили изменить у SYSDBA пароль с masterkey на icuryy4me (в Firebird 1.5 для Linux инсталлированный пароль не masterkey, он может оказаться совсем невра- зумительным!). Вам нужно выполнить следующие шаги: 1. Перейдите в окно командной строки на вашем сервере и сделайте текущим ката- лог, в котором находятся ваши утилиты командной строки. Обратитесь к табл. 1.2—1.5, чтобы определить их положение. 2. Для Windows наберите следующее, рассматривая символы, как чувствительные к регистру: gsec -user sysdba -password masterkey Для платформ POS1X наберите: ./gsec -user sysdba -password masterkey Теперь вы должны увидеть подсказку утилиты gsec: GSEC> 3. Наберите команду: GSEC> modify sysdba -pw icuryy4me 4. Нажмите клавишу <Enter>. Новый пароль icuryy4me будет зашифрован и сохра- нен, a masterkey не будет больше действовать. 5. Теперь завершите программу gsec: GSEC> quit Поскольку Firebird игнорирует все символы в пароле после восьмого, icuryy4m будет работать так же, как и icuryy4monkey.
64 Часть I. Учебный лагерь СОВЕТ. Полные инструкции по использованию gsec находятся в главе 34. Пользователи и группы в Linux/UNIX Начиная с Firebird 1.5, пользователь root больше не является пользователем по умол- чанию, который может запускать сервер. Это означает, что вам нужно поместить пользователей, не являющихся root, в группу firebird, чтобы предоставить им воз- можность доступа к базам данных. Чтобы добавить пользователя (например, sparky) в группу firebird, пользователю root нужно ввести: $ usermod -G firebird sparky Теперь sparky может соединиться с базой данных Firebird и начать работу с ней. Для отображения списка групп, в которых присутствует пользователь, наберите в командной строке: $ groups СОВЕТ. Пользователю группы firebird могут также понадобиться привилегии чте- ния и записи ко всем базам данных и привилегии чтения, записи и выполнения ко всем каталогам, где размещаются базы данных. Инструмент администратора Инсталляционный комплект Firebird не содержит инструментов администратора с графическим интерфейсом. У него есть набор инструментов командной строки (ис- полняемые программы), которые расположены в каталоге /bin каталога инсталляции Firebird. Их использование подробно описано в части IX. Отличные инструменты графического интерфейса, доступные для использования на клиентских машинах Windows, слишком многочисленны, чтобы их здесь описать. Небольшое количество графических инструментов написано на Borland Kylix для использования на клиентских машинах под Linux, они находятся на разных стадиях завершения. Список наиболее известных инструментов администратора для Firebird представлен в приложении 5. Для получения самого последнего списка зайдите на http://www.ibphoenix.com, выберите ссылку Contributed из области загрузки и на- жмите на ссылку Administration Tools. СОВЕТ. Вы можете использовать клиентские инструменты администратора в Windows для доступа к серверу Linux и наоборот.
Глава 1. Инсталляция 65 Размещение на диске по умолчанию Таблицы в этом разделе описывают размещение компонентов для Windows и Linux на диске по умолчанию. Информация дается в контексте двух версий: ♦ версии, предшествующие Firebird 1.5; ♦ версии Firebird 1.5 и последующие. Разница является существенной. Версии, предшествующие Firebird 1.5, используют размещение, имена компонентов и ссылки на ресурсы, как и InterBase 6.x и более ранние версии InterBase. Следовательно, не существует возможности запускать и сервер Firebird, и сервер InterBase этих версий на одной и той же машине. В пересмотре основного кода, который начался с версии 1.5, старые ссылки на по- стороннюю информацию InterBase были удалены, а многие из главных компонентов были переименованы. Firebird 1.5 дает возможность запускать также и сервер Inter- Base. Это верно и для Firebird 2. Табл. 1.2—1.5 показывают, где искать компоненты при стандартной инсталляции после выполнения инсталлятора. Точное расположение может изменяться от релиза к релизу. Таблица 1.2. Инсталляция Firebird 1.5 для Linux и некоторых платформ UNIX Компонент Имя файла Размещение по умолчанию Классический сервер fb_inet_server /opt/firebird/bin Программа Lock Manager (только Классический сервер) fb_lock_mgr /opt/firebird/bin Встроенный клиент для Классического сервера libfbembed.so. 1.5.0 /opt/lib Firebird Guardian (только Суперсервер) fbguard /opt/firebird/bin Суперсервер fbserver.exe /opt/firebird/bin Поточный клиент для Суперсервера и Классического сервера libfbclient.so /usr/lib Файл конфигурации firebird.conf /opt/fi rebird Файл алиасов базы данных aliases.conf /opt/firebird Файл сообщений firebird.msg /opt/firebird Файл сгенерированного пароля SYSDBA.password /opt/firebird База данных безопасности security.fdb /opt/firebird Копия базы данных безопасности security.fbk /opt/firebird Инструменты командной строки isql, gbak, gfix, gstat, gsec, gdef, gpre, qli /opt/firebird/bin Инструмент сервера (только Суперсервер) fbmgr /opt/firebird/bin 3 Зак. 420
66 Часть I. Учебный лагерь Таблица 1.2 (окончание) Компонент Имя файла Размещение по умолчанию Скрипты командной строки Различные; обрати- тесь к файлам README и заметкам по релизу /opt/firebird/bin Скрипт шаблона для Firebird (только Классический сервер) firebird.xinetd /opt/firebird/misc Библиотеки внешних функций (библиотеки UDF) ib_udf.so, fbudf.so /opt/firebird/UDF Библиотека утилиты памяти (используется в ib_udf) libib_util.so /opt/firebird/lib Скрипты DDL для библиотек внешних функций ib_udf.sql, fbudf.sql /opt/firebird/UDF Библиотека поддержки интернацио- нальных языков fbintl /opt/firebird/int! Заметки по релизу Firebird_v15.nnn_ ReleaseNotes.pdf /opt/firebird Другая документация Файлы README no различным темам /opt/firebird/doc Пример базы данных employee.fdb /opt/firebird/sample Заголовочные файлы С ibase.h, iberror.h и др. opt/firebird/include Таблица 1.3. Инсталляция Firebird 1.5 для 32-битовых платформ Windows Компонент Имя файла Размещение по умолчанию Классический сервер fb_inet_server.exe C:\Program Files\Firebird\ Firebird_1_5\bin Программа Lock Manager (только Классический сервер) fb_lock_mgr.exe C:\Program Files\Firebird\ Firebird_1_5\bin Firebird Guardian (только Суперсервер) fbguard.exe C:\Program Files\Firebird\ Firebird_1_5\bin Суперсервер fbserver.exe C:\Program Files\Firebird\ Firebird_1_5\bin Встроенный Суперсервер fbembed.dll C:\Program Files\Firebird\ Firebird_1_5\bin (инсталлировать в каталог приложе- ния и переименовать в fbciient.dll)
Гпава 1. Инсталляция 67 Таблица 1.3 (окончание) Компонент Имя файла Размещение по умолчанию Клиентская библиотека для Суперсервера и Классическо- го сервера gds32.dll (заглушка) и fbclient.dll C:\Program Files\Firebird\ Firebird_1_5\bin Файл конфигурации firebird.conf C:\Program Files\Firebird\ Firebird_1_5\bin Файл алиасов базы данных aliases.conf C:\Program Files\Firebird\ Firebird_1_5\bin Файл сообщений firebird, msg C:\Program Files\Firebird\ Firebird_1_5\bin База данных безопасности security.fdb C:\Program Files\Firebird\ Firebird_1_5\bin Копия базы данных безопас- ности security.fbk C:\Program Files\Firebird\ Firebird_1_5\bin Инструменты командной строки isql, gbak, gfix, gstat, gsec, gdef, gpre, qli, fb_lock_print C:\Program Files\Firebird\ Firebird_1_5\bin Сервисы и регистрационные инструменты instsvc.exe, instreg.exe C:\Program Files\Firebird\ Firebird_1_5\bin Библиотеки внешних функ- ций (библиотеки UDF) ib_udf.dll, fbudf.dll C:\Program Files\Firebird\ Firebird_1_5\UDF Библиотека утилиты памяти (используется в ib_udf.dll) ib_util.dll C:\Program Files\Firebird\ Firebird_1_5\bin Скрипты DDL для библиотек внешних функций ib_udf.sql, fbudf.sql C:\Program Files\Firebird\ Firebird_1_5\UDF Библиотека поддержки ин- тернациональных языков fbintl.dll C:\Program Files\Firebird\ Firebird_1_5\intl Заметки по релизу Firebird_v15.nnn_ ReleaseNotes.pdf C:\Program Files\Firebird\ Firebird_1_6 Другая документация Файлы README no различным темам C:\Program Files\Firebird\ Firebird_1_5\doc Пример базы данных employee.fdb C:\Program Files\Firebird\ Firebird_1_6\sample Заголовочные файлы С ibase.h, iberror.h и др. C AProgram Files\Firebird\ Firebird_1_5\include
68 Часть I. Учебный лагерь Таблица 1.4. Инсталляция Firebird 1.0.3 для Linux и некоторых платформ UNIX Компонент Имя файла Размещение по умолчанию Классический сервер gds_inet_server /opt/interbase/bin Программа Lock Manager (только Классический сервер) ib_lock_mgr /opt/interbase/bin Встроенный клиент для Классического сервера gdslib.so /usr/lib Суперсервер ibserver.exe /opt/interbase/bin Поточный клиент для Суперсервера и Классического сервера gdslib.so /usr/lib Файл конфигурации isc_config /opt/interbase Файл сообщений interbase.msg /opt/interbase База данных безопасности isc4.gdb /opt/interbase Копия базы данных безопасности isc4.gbk /opt/interbase Инструменты командной строки isql, gbak, gfix, gstat, gsec, gdef, gpre, qli /opt/interbase/bin Инструмент сервера (только Суперсервер) fbmgr /opt/interbase/bin Скрипты командной строки Различные; обрати- тесь к файлам README и замеча- ниям по релизу /opt/interbase/bin или /opt/interbase/sample Скрипт шаблона для Firebird (только Классический сервер) firebird.xinetd /opt/interbase/bin или /opt/interbase/sample Библиотеки внешних функций (библиотеки UDF) ib_udf.so, fbudf.so /opt/interbase/udf Библиотека утилиты памяти (ис- пользуется в ib_udf) -TSROr libib_util.so~TSR ib_util /opt/interbase/lib/udf ~TSR/opt/ interbase Скрипты DDL для библиотек внешних функций ib_udf.sql /opt/interbase/udf Библиотека поддержки интернацио- нальных языков inti или intl.so /opt/interbase/intl Пример базы данных employee.fdb /opt/interbase/sample Заголовочные файлы С ibase.h, iberror.h и др. /opt/interbase/include
Глава 1. Инсталляция 69 Таблица 1.5. Инсталляция Firebird 1.0.3 для 32-битовых платформ Windows Компонент Имя файла Размещение по умолчанию Firebird Guardian ibguard.exe CAProgram Files\Firebird\bin Суперсервер ibserver.exe C:\Program Files\Firebird\bin Клиентская библиотека gds32.dll C:\WINNT\system32 (платформа с сервисами) или C:\Windows (другие) Файл конфигурации ibconfig CAProgram Files\Firebird Файл сообщений interbase.msg CAProgram Files\Firebird База данных безопасности isc4.gdb C:\Program Files\Firebird Копия базы данных безопасности isc4.gbk C:\Program Files\Firebird Инструменты командной строки isql, gbak, gfix, gstat, gsec, gdef, gpre. qli, iblockpr C:\Program Files\Firebird\bin Сервисы и регистрационные инструменты instsvc.exe, instreg.exe CAProgram Files\Firebird\bin Библиотеки внешних функ- ций (библиотеки UDF) ib_udf.dll, fbudf.dll CAProgram Files\Firebird\UDF Библиотека утилиты памяти (используется в ib_udf.dll) ib_util.dll C:\Program Files\Firebird\bin Скрипты DDL для библиотек внешних функций ib_udf.sql, fbudf.sql CAProgram Files\Firebird\UDF Библиотека поддержки интернациональных языков gdsintl.dll CAProgram Files\Firebird\intl Документация Файлы README no различным темам CAProgram Files\Firebird Пример базы данных employee.gdb CAProgram Files\Firebird\sample Заголовочные файлы С ibase.h, iberror.h и др. CAProgram Files\Firebird\include Пора дальше Firebird состоит из программы сервера и клиентских приложений; между сервером и каждым клиентом располагается сетевой протокол. Если вы использовали соответст- вующий инсталлятор для вашей платформы и использовали значения по умолчанию, вероятно, вам больше ничего не потребуется делать, и можно сразу приступать к на- чалу работы. Вы можете пропустить две следующие главы и перейти к "основам", описанным в главе 4. Если вас интересуют сетевые протоколы или вам кажется, что у вас есть проблемы, связанные с сетевыми протоколами, прочтите следующую гла- ву, и, возможно, просмотрите некоторые советы по конфигурированию в главе 3.
ГЛАВА 2 Установка сети Поскольку реляционная система управления базами данных (РСУБД) специально создана для платформы клиент-сервер, Firebird позволяет удаленным и локальным клиентам одновременно соединяться с сервером, используя различные сетевые про- токолы. Инсталлятор создаст конфигурацию по умолчанию для соединения клиента с серве- ром и для получения соединений от клиентов с использованием установок порта по умолчанию. Если только не существует внешних причин создавать пользовательскую конфигурацию сети, то нет необходимости для запуска Firebird изменять конфигура- цию, которая была установлена при инсталляции. Сетевые протоколы Firebird поддерживает протокол TCP/IP для всех комбинаций клиентских и сервер- ных платформ. Именованные каналы Firebird поддерживает протокол Microsoft WNet Named Pipes для серверов Windows NT/2000, XP и клиентов Windows. Имя канала по умолчанию interbas. Windows 9х и ME не могут быть серверами WNet. Jg ПРИМЕЧАНИЕ. Протокол Windows Named Pipes (именованные каналы) часто F'/л называют NetBEUI. Строго говоря, NetBEUI является транспортной частью, ис- пользуемой в WNet. Локальный доступ Хотя Firebird разработан, чтобы быть сервером базы данных для удаленных клиен- тов, он предоставляет множество средств локального доступа.
Глава 2. Установка сети 71 Клиент-сервер Средства локального доступа. ♦ Локальная заглушка TCP/IP. Для многоуровневых серверных приложений и дру- гих клиентов доступ к локальному серверу на любой поддерживаемой платформе осуществляется через протокол TCP/IP: даже при отсутствии сетевой карты со- единение может быть выполнено через специальный сервер localhost с IP-адресом 127.0.0.1. ВНИМАНИЕ! Соединение с localhost невозможно для приложений встраиваемо- го сервера. ♦ Режим локального соединения Windows. Для клиентов Windows, использующих Суперсервер Firebird на той же самой физической машине, Firebird поддерживает режим локального соединения, используя межпроцессную передачу данных для моделирования сетевого соединения без интерфейса физической сети и сетевого протокола. Это полезно при доступе к базе данных в процессе разработки, для приложений встраиваемого сервера и для консольных инструментов клиента, но в этом варианте не поддерживается механизм событий Firebird и параллельная ра- бота клиентской части из разных потоков приложения. Средства локального соединения клиент-сервер являются ограниченными и не должны использоваться при поставках программ. Распространяйте автономные приложения клиент-сервер, Web-приложения и другие уровни серверов с исполь- зованием для соединения локально закольцованного протокола TCP/IP. ♦ Прямое локальное соединение в POSIX. Может ли локальный клиент соединяться с базой данных в Linux и в некоторых других системах POSIX, зависит, в первую очередь, от варианта сервера, который вы инсталлировали (Классический сервер или Суперсервер), и, во вторую очередь, от типа клиентского соединения. Суперсервер совсем не принимает локальных соединений через обычный клиент- ский API. Путь для соединения всегда должен включать имя хоста TCP/IP. Тем не менее он принимает локальные соединения от "встроенных приложений" (прило- жений, написанных с использованием встроенного SQL — Embedded SQL, ESQL). Инструменты командной строки, gsec, gfix, gbak и gstat, которые являются встроенными приложениями, могут выполнять локальные соединения с Супер- сервером. Если у вас запущен Классический сервер, с локального клиента возможно прямое соединение. Встраиваемый сервер Начиная с Firebird 1.5, пакеты Firebird для Windows включают полную функциональ- ность встраиваемого сервера (клиент и сервер соединяются как динамическая биб- лиотека). Это идентично обычной модели клиент-сервер, за исключением того, что
72 Часть I. Учебный лагерь здесь не поддерживается сетевой протокол: соединение осуществляется в стиле "ло- кальной Windows" для эмуляции сетевого соединения. ВНИМАНИЕ! Встраиваемый сервер не поддерживает пароль безопасности дос- тупа Firebird. Смешанные платформы Архитектура Firebird позволяет клиентам, запущенным в одной операционной систе- ме, получить доступ к серверу Firebird, выполняющемуся на платформе и в операци- онной системе, отличных от клиентских. Часто используемый вариант— одновре- менный запуск нескольких недорогих персональных компьютеров с Windows 98 в качестве клиентских рабочих станций, имеющих доступ к главному серверу, выпол- няющемуся под Windows NT/2000/XP, Linux или под некоторой версией UNIX. База данных, которая была создана для использования на одной модели сервера, мо- жет работать на любом из них. Например, когда приложение встраиваемого сервера неактивно, его база данных может находиться под управлением другого приложения встраиваемого сервера или полного сервера Firebird, обслуживающего удаленных клиентов. Та же база данных может быть без изменений перенесена с одной плат- формы на другую'. К тому же платформы Windows и Linux, реализации сервера Firebird (Классический, Суперсервер или оба) работают также в Mac OS X (Darwin), FreeBSD, Sun Solaris (Intel и SPARC), HP-UX, потенциально в AIX и могут быть созданы для множества дополнительных платформ UNIX. Firebird в настоящее время не поддерживает ни платформу Novell NetWare, ни другие виды сетей, которые используют устаревший протокол IPX/SPX. С прекращением со стороны Novell поддержки этого протокола сайты часто используют серверы Firebird в системе Linux с сетевыми клиентами, подключенными к этой подсистеме через TCP/IP. Сетевой адрес для сервера Для связи через TCP/IP вы должны соединяться с хостом, который имеет известный адрес IP. Определение того, какой это адрес IP (или каким должен быть, если он от- сутствует), зависит от вида сетевого оборудования вашей хост-машины. 1 Заметьте, однако, что пользовательские библиотеки внешних функций не являются пере- носимыми между платформами. Необходимо создавать версии этих библиотек для каждой платформы, на которой может располагаться база данных. Все платформы, поддерживающие Windows, могут использовать те же библиотеки Windows; библиотеки разделяемых объектов для платформ POSIX не обязательно будут выполняться на всех платформах POSIX.
Глава 2. Установка сети 73 ♦ Если вы находитесь в управляемой сети, получите IP-адрес сервера у вашего сис- темного администратора. ♦ Если у вас простая сеть из двух машин, соединенных кабелем с перекрестными проводниками, или малая переключаемая сеть, вы можете установить для вашего сервера любой подходящий уникальный адрес IP, какой вам нравится, за исклю- чением 127.0.0.1 (который резервируется для локальной заглушки TCP). ♦ Если вам известны "родные" адреса IP ваших сетевых карт, и они различны, вы можете просто использовать их. ♦ Если вы собираетесь пробовать инсталляцию на одной машине и для клиента, и для сервера, вы должны использовать адрес локально закольцованного сервера — localhost или его IP-адрес 127.0.0.1. Д ВНИМАНИЕ! В Windows один пользователь может локально соединиться с сер- вером без использования локального TCP/IP как внешний или как встроенный клиент. Серверный адрес не требуется для такого соединения и не может быть использован для проверки того, правильно ли работает TCP/IP при вашей ин- сталляции. Файл HOSTS Если в вашей TCP/IP-сети не запущен сервис имен доменов, то необходимо проин- формировать индивидуально каждый узел о распределении адресов IP в именах хоста вашей сети. Для этого измените файл HOSTS для каждого узла (сервер и клиент). При установке узлов Firebird вашей TCP/IP-сети рекомендуется конфигурировать файлы имен хоста на клиентах и использовать имена, а не напрямую адреса IP при подключении к серверу. Хотя большинство последних операционных систем могут использовать IP-адрес хоста в строке соединения вашего клиента вместо имени хос- та, соединение через имя хоста гарантирует, что адрес сервера остается статичным, несмотря на динамическое изменение адресов в сети. Д ВНИМАНИЕ! Для рабочих станций, работающих под Windows 95 и ранней вер- сии Windows 98, где клиентские станции работают в старой 32-битовой версии Windows, сетевая поддержка вовсе не распознает IP-адрес в строке соединения, потому что эти версии инсталлировались с Winsock 1. В любом случае инсталля- ция Winsock 2 более соответствует требованиям текущих версий Firebird. Сво- бодно распространяемые пакеты обновлений могут быть загружены с сайтов поддержки пользователей Microsoft. Местонахождение файла HOSTS Перечислим, где можно найти файл HOSTS. ♦ В Linux и многих версиях UNIX файл HOSTS обычно расположен в /etc/. Помни- те, что имена файлов являются чувствительными к регистру на платформах се- мейства UNIX.
74 Часть I. Учебный лагерь ♦ В Windows NT/2000 файл HOSTS располагается в C:\WINNT\system32\drivers\etc\. ♦ В Windows 95/98/ME/XP/Server2003 файл HOSTS располагается в C:\Windows. СОВЕТ. Если файл HOSTS отсутствует, вы найдете файл с именем Hosts.SAM в том же месте. Скопируйте этот файл и переименуйте в HOSTS. Примеры записей файла: 10.12.13.2 db_server # Firebird-сервер (в LAN) 65.215.221.149 apress.com # (сервер в WAN) 127.0.0.1 localhost # локальная заглушка (в Windows) 127.0.0.1 localhost.localdomain # локальная заглушка(в Linux) Откройте и отредактируйте файл HOSTS в текстовом редакторе. Создание записей простое. Тем не менее обеспечьте, чтобы: ♦ IP-адрес, если это не 127.0.0.1 (localhost), был действительным, сконфигурирован- ным для хоста в вашей сети; ♦ имя сервера было любым уникальным именем в вашей сети; СОВЕТ. Серверная машина под Windows, которая была установлена в Windows Networking (Named Pipes) с именем хоста //Server_name, будет распространять это имя как имя хоста TCP/IP. ♦ комментарии после символа # необязательны, но рекомендуются; ♦ формат записей одинаков, независимо от того, под какой системой запущен хост — Windows или Linux/UNIX. А ВНИМАНИЕ! После того как вы отредактировали файл HOSTS, проверьте, что ваш редактор сохранил HOSTS без расширения имени файла. Если необходимо, удалите любое расширение, переименовав файл. Имя сервера и путь к нему При создании или перемещении базы данных убедитесь, что она располагается на жестком диске, который физически соединен с вашей серверной машиной. Файлы базы данных, расположенные на разделенных, назначенных дисках или (для UNIX) смонтированных как файловая система SMB (Samba), невидимы для сервера. Начиная с Firebird 1.5, у вас есть возможность сохранять пути к базам данных на ва- шем сервере с использованием anuacoe путей к базе данных. Это не только упрощает дело с установкой изменяемой строки соединения в ваших приложениях, но также добавляет дополнительный уровень безопасности для ваших удаленных соединений. Алиасы путей к базам данных не дают возможности чужим программам определить,
Глава 2. Установка сети 75 где расположены ваши базы данных, и использовать эту информацию для разруше- ния ваших файлов. Об алиасах базы данных см. главу 4. Синтаксис строки соединения Это строки соединения, очевидные для каждой платформы, которые нужны вам для конфигурирования алиасов и для соединения клиентов с базой данных, через сервер Firebird тех версий, которые не поддерживают алиасов базы данных. TCP/IP Строка соединения TCP/IP содержит два элемента: имя сервера и абсолютный путь диска/файловой системы, такой, как его видит сервер. Формат следующий: ♦ для соединения с сервером Linux: имя-сервера: / путь-файловой-системы/ файл-базы-данных Вот пример для Linux или другой операционной системы семейства UNIX для сервера с именем hotchicken: hotchicken:/opt/firebirdl5/examples/LeisureStore.fdb Д ВНИМАНИЕ! Помните, что для этих платформ все имена файлов являются чув- ствительными к регистру. ♦ для соединения с сервером Windows: имя-сервера-.Диск: \путь-файловой-системы\файл-базы-данных Пример: hotchicken:C:\Program Files\Firebirdl5\examples\LeisureStore.fdb Прямая наклонная черта также допустима в Windows: hotchicken:С:/Program Files/Firebirdl5/examples/LeisureStore.fdb Локальное соединение в Windows Соединение встроенного клиента или локального внешнего клиента в локальном ре- жиме Windows: C:\Program Files\Firebirdl5\examples\LeisureStore.fdb Сеть Windows (Named Pipes/WNet) Соединение удаленного клиента сервера Windows с использованием протокола Named Pipes: \\имя-серверах Диск: \путь\файл-базы-данных
76 Часть I. Учебный лагерь где \\имя-сервера — правильный идентификатор имени узла серверной машины в сети Windows, не может быть разделяемым или назначенным диском. Например, \\hotchicken\c:\databases\LeisureStore.fdb Несовместимые строки соединения для подключений Windows Суперсервер для Windows устанавливает исключающую блокировку на файл базы данных, когда активируется первое клиентское подключение, чтобы защитить базы данных от старой ошибки. Ошибка в пути соединения Windows воспринимает две формы абсолютного локального пути к файлу — один (правильный в соответствии со стандартом DOS), имеющий вид устройство:\путь- к-базе-данных, И другой: Устройство-.путь-к-базе-да иных (отсутствует обратная НаКЛОН- ная черта после обозначения дискового устройства). Если сервер получил два клиентских запроса на соединение, первое, использующее стандартную форму пути, и второе, с использованием второй формы, он будет трак- товать эти два соединения, как если бы они были соединениями с двумя разными базами данных. Результатом параллельных операций DML в подобном случае будет разрушение базы данных. Для соединений с Суперсервером исключающая блокировка решает проблему требо- вания для всех соединений применения того же формата пути, что был использован при первом соединении. То же решение не может применяться в случае Классическо- го сервера, потому что каждое соединение работает с собственным экземпляром сер- вера. Позаботьтесь о том, чтобы ваше приложение всегда передавало согласованные строки пути. СОВЕТ. Настоятельно рекомендуется использовать алиасы базы данных (см. разд. "Алиасы базы данных" в главе 4) для всех соединений. Убедитесь также, что файл aliases.conf содержит один и только один алиас для каждой базы дан- ных. Конфигурирование сервиса порта TCP/IP По умолчанию Firebird прослушивает порт 3050 при запросе соединения от клиентов TCP/IP. Зарегистрированное имя сервиса этого порта gds_db. Хорошая новость — вы можете использовать эти установки по умолчанию и ничего не делать с конфигура- цией сервиса порта ни для сервера, ни для клиента. Вы можете использовать другой порт, другое имя сервиса порта или и то, и другое. Вам может понадобиться это сделать, если порт 3050 требуется для другого сервиса, например, если параллельно используемый gds_db сконфигурирован для другой вер-
Глава 2. Установка сети 77 сии Firebird или для сервера InterBase. Есть несколько способов перекрыть значения по умолчанию. Сервер и клиенты оба должны быть сконфигурированы для измене- ния имени сервиса или номера порта (или и того, и другого) по меньшей мере одним из следующих способов: ♦ в строке соединения клиента; ♦ в команде запуска исполняемой программы сервера; ♦ активацией параметров RemoteServicePort ИЛИ RemoteServiceName В firebird.Config (версия 1.5 и выше); ♦ в демоне конфигурации (для Классического сервера в POSIX); ♦ в записи файла services. Прежде чем проверять каждую из этих техник, будет полезным взглянуть на логику, используемую сервером для установки прослушиваемого порта, и порядок установки клиентом используемого порта. Как сервер устанавливает прослушиваемый порт Исполняемая программа сервера имеет необязательный переключатель в командной строке (-р), который можно использовать для указания номера порта или имени сер- виса порта, который будет прослушиваться сервером. Если присутствует этот пере- ключатель, то номер порта 3050 или имя сервиса порта (gds_db) будут заменены на значение аргумента, указанного в переключателе -р. Затем— или вначале, если не установлен переключатель -р,— сервер версии 1.5 проверяет наличие параметров RemoteServicePort И RemoteServiceName В firebird.COnflg. ♦ Если оба параметра закомментированы с использованием #, то принимаются зна- чения по умолчанию, и никакие дальнейшие изменения не выполняются. Любой аргумент -р игнорируется, и "отсутствующие" аргументы сохраняют значение по умолчанию. ♦ Если RemoteServiceName (но не RemoteServicePort) не закомментирован, то имя сер- виса порта заменяется, только если это имя не было уже перекрыто переключате- лем -р. ♦ Если RemoteServicePort И RemoteServiceName не закомментированы, ТО RemoteServiceName получает приоритет, если он не был перекрыт аргументом -р. Если же ИМЯ сервиса порта уже было изменено, ТО значение RemoteServiceName бу- дет проигнорировано, и новое значение RemoteServicePort заменит предыдущее значение 3050. ♦ В этой точке, если замена номера порта или имени сервиса была выполнена, то оба сервера версий 1.0 и 1.5 выполняют проверку файла services на наличие запи- си с корректной комбинацией имени сервиса и номера порта. Если соответствие найдено, то все в порядке. Если нет, и имя сервиса порта не gds db, то сервер вы- даст исключение и отменит запуск. Если имя сервиса порта gds db и ему не мо- жет быть назначен никакой другой порт, он будет использовать порт 3050 автома- тически.
78 Часть I. Учебный лагерь Если значение по умолчанию для номера порта или имени сервиса было изменено, то вам может понадобиться создать запись в файле services. Для понимания того, нужно ли это делать, выполните шаги, описанные далее в этой главе в разд. "Конфигуриро- вание файла services". Использование переключателя -р Запуск сервера с необязательным переключателем -р дает вам возможность перекры- вать значение по умолчанию для номера порта (3050) или имя сервиса порта (gds db), которые используются сервером для прослушивания запросов на соедине- ние. Переключатель может перекрывать одно значение, но не оба. В Firebird 1.5 и следующих вы можете использовать переключатель -р в комбинации с конфигураци- ей в файле firebird.conf для получения возможности перекрывать и номер порта, и имя сервиса порта. Синтаксис для TCP/IP Шаблон синтаксиса для команд: Команда-сервера <другие переключатели> -р номер-порта | имя-сервиса Например, для запуска Суперсервера как приложения и замены имени сервиса с gds_db на fb_db введите: fbserver -а -р fb_db Для замены порта 3050 на 3051 введите: fbserver -а -р 3051 Синтаксис для Wnet Для сети Wnet замените аргумент переключателя -р на обратная черта-обратная черта—точка—@: fbserver -а -р \\.@fb_db ИЛИ fbserver -а -р \\.@3051 Классический сервер в POSIX: демон inetd или xinetd В Классическом сервере Firebird для Linux или UNIX демон inetd или xinetd сконфи- гурирован на прослушивание порта по умолчанию и имя сервиса по умолчанию. Ин- сталляционный скрипт запишет соответствующую запись в файл конфигурации /etc/inetd.conf или /etc/xinetd.conf. Проблемы с подключением к Классическому серверу часто происходят по причине отсутствия или неправильной записи сервиса порта в этом файле. Вы можете прове- рить текущую запись, открыв файл в текстовом редакторе (например, vim) и скоррек- тировав ее при необходимости. Следующий пример показывает, что вы должны уви- деть в файде /etc/inetd.conf или /etc/xinetd.conf после инсталляции Классического сер- вера Firebird в Linux:
Глава 2. Установка сети 79 # default: on # description: FirebirdSQL server # service gds_db ( flags = REUSE KEEPALIVE socket_type = stream wait = no user = root if user = @FBRunUser@ log_on_success += USERID log_on_failure += USERid server = /opt/firebird/bin/fb_inet_server disable = no ) Если вы изменили сервис порта на значение, отличное от значения по умолчанию, вы должны соответственно изменить /etc/inetd.conf или /etc/xinetd.conf. Заново стартуйте xinetd (или inetd) с аргументом kill -hup, чтобы убедиться, что демон будет исполь- зовать новую конфигурацию. Д ВНИМАНИЕ! Запросы на соединения будут неуспешными, если xinetd (или inetd) и fbserver (или ibserver) оба пытаются прослушивать один и тот же порт. Если ваша хост-машина имеет подобную дублирующую конфигурацию, то необходимо сделать такие установки, при которых каждая версия сервера имела бы свой собственный порт. Использование параметров файла конфигурации В Firebird 1.5 И выше вы можете конфигурировать ИЛИ RemoteServiceName, ИЛИ RemoteServicePort в файле firebird.config для изменения номера порта по умолчанию (3050), или имени сервиса порта по умолчанию (gds_db), которые использует сервер для прослушивания запросов на соединение. Сервер будет использовать один параметр Remoteservice*, но не оба. Если вы сконфи- гурировали оба, ТО ОН будет игнорировать RemoteServicePort ВО всех Ситуациях, за исключением того случая, когда команда запуска сервера была вызвана с переключа- телем -р, перекрывающим имя сервиса порта. Следовательно, вы можете использо- вать в комбинации переключатель -р и параметр Remoteservice* для изменения номе- ра порта и имени сервиса. Если значения по умолчанию для номера порта или имени сервиса были изменены, то вам нужно создать запись в файле services. Д ВНИМАНИЕ! Если ВЫ не закомментировали НИ RemoteServiceName, НИ RemoteServicePort, но оставили значения по умолчанию нетронутыми, они будут трактоваться как измененные. Необходимо создать запись в файле services для установок значений по умолчанию сервиса порта.
80 Часть /, Учебный лагерь Установка клиента для поиска порта сервиса Если вы установили ваш сервер с инсталляционными значениями по умолчанию (сервис gds db прослушивает порт 3050), то конфигурирование не требуется. Если сервер прослушивает другой порт или используется другое имя сервиса порта, то приложение клиента и/или хост-машины требуют некоторых изменений конфигура- ции, чтобы помочь клиентской библиотеке Firebird найти прослушиваемый порт. Строка соединения, используемая клиентом, может включать информацию для опро- са прослушиваемого порта сервера разными путями. Клиенты Firebird 1.5 могут ис- пользовать локальную копию firebird.conf. Изменения также могут понадобиться и для файла services. Использование строки соединения Если были изменены только номер порта или имя сервера, включите альтернативный номер порта или имя сервера в строку соединения. Это работает для всех версий Firebird. Синтаксис соединения TCP/IP Для соединения с сервером базы данных, названным hotchicken, связанным с портом 3050 и сервисом fb db, строка соединения в POS1X должна быть следующей: hotchicken/fb_db:/data/leisurestore.fdb Если же сервер имеет имя gds db, а номер порта 3051, то строка соединения должна быть следующей: hotchicken/3051:/data/leisurestore.fdb В Windows строка соединения должна быть: hotchicken/3051:D:\data/leisurestore.fdb hotchicken/fb_db:D:/data/leisurestore.fdb Обратите внимание, что разделитель между именем сервера и портом наклонная чер- та, а не двоеточие. Двоеточие требуется в строке физического пути после имени диска. Синтаксис соединений Wnet В сети Wnet используйте стиль нотаций UNC: //hotchicken@3051/d:/leisurestore.fdb или //hotchicken@fb_db/d:\leisurestore.fdb Если номер порта или имя сервиса были изменены, то вам нужно создать запись в файле services.
Гпава 2. Установка сети 81 Использование синтаксиса порта для алиасов базы данных Для соединения через порт, не являющегося портом по умолчанию, с помощью алиа- са базы данных добавьте номер порта или имя сервиса к имени сервера, а не к алиасу. Предположим,'что алиас базы данных был сохранен в aliases.conf в виде: hotstuff = /data/leisurestore.fdb Строка соединения вашего приложения с сервером hotchicken будет следующей: hotchicken/fb_db:hotstuff или hotchicken/3051:hotstuff Использование копии firebird.conf В Firebird 1.5 и выше вы можете скопировать на клиентскую сторону в корневой ка- талог Firebird файл firebird.conf И сконфигурировать RemoteServiceName ИЛИ RemoteServicePort для помощи клиенту в поиске серверного порта. Вы можете конфигурировать один из этих двух параметров; изменение значения дру- гого параметра можно выполнить через строку соединения (см. ранее). Вы можете перекрыть значение ТОЛЬКО RemoteServiceName ИЛИ RemoteServicePort без использова- ния строки соединения. Если вам не нужно передавать имя сервиса порта или номер порта в строке соедине- ния, а сервер не использует значения по умолчанию для обоих параметров, вы може- те конфигурировать оба RemoteServiceName И RemoteServicePort. Вы МОЖете ИСПОЛЬЗО- вать эту технику, если вашему клиентскому приложению нужно сохранить возмож- ность соединяться с серверами InterBase или Firebird 1.0. Размещение данных Firebird на клиентах Если вы используете на клиентских машинах файл firebird.conf, нужно, чтобы кли- ентская библиотека знала, где его найти. Вам необходимо создать корневой каталог Firebird и проинформировать систему о его местонахождении. Используйте для этого переменную среды firebird таким же образом, как и для серверов (см. главу 3). Кли- енты Windows могут также использовать запись в реестре. При правильной установке клиента вы можете также сделать доступной локальную версию файла сообщений. Более подробную информацию см. в разд. "Инсталляция клиентов" в главе 7. Конфигурирование файла services Вам не нужно изменять запись сервиса порта для сервера или клиентов Firebird, если сервер использует значения по умолчанию, заданные при инсталляции — gds db для порта 3050. Если gds db — имя сервиса порта, который не может использовать лю- бой другой порт, то он будет применять порт 3050 автоматически. Если вы изменили номер порта или имя сервиса, вы должны сделать явные измене- ния для сервера и клиентов, чтобы отобразить эти изменения. В Linux и Windows данная информация хранится в файле services.
82 Часть I. Учебный лагерь Местонахождение файла services Местонахождение файла services на разных платформах: ♦ в Windows NT/2000/XP этот файл находится в C:\winnt\system32\drivers \etc\services; ♦ в Windows 95/98/МЕ файл находится в C:\Windows\services; ♦ в Linux/UNIX файл находится в /etc/services. Записи файла services выглядят следующим образом: gds_db 3050/tcp # Firebird Server 1.5 Откройте файл в текстовом редакторе и добавьте новую строку или измените суще- ствующую запись gds db. ♦ Для сервера или клиента Firebird 1.0.x измените имя сервиса или номер порта для согласования с параметрами запуска сервера. ♦ Для сервера Firebird 1.5 или выше отредактируйте или добавьте нужную строку. Если у вас инсталлирован сервер Firebird 1.0 или InterBase на том же хосте, со- храните нужные для них записи и добавьте новую запись для согласования с па- раметрами запуска сервера. ПРИМЕЧАНИЕ. Если данная строка является последней в файле services, то рекомендуется в конце файла добавить пустую строку. Проверка соединения с помощью ping Вы сделали все нужные изменения. Последняя проверка нужна, чтобы убедиться, что ваша клиентская машина может связываться с сервером. Вы можете быстро прове- рить, что ваш клиент TCP/IP связывается с сервером, используя в окне командной строки команду ping: ping имя-сервера// подставьте имя, которое вы записали в файле HOSTS Если соединение прошло хорошо, и все правильно сконфигурировано, вы увидите приблизительно следующее: Pinging hotchicken [10.10.0.2] with 32 bytes of data reply from 10.10.0.2: bytes=32 time<10ms TTL=128 reply from 10.10.0.2: bytes=32 time<10ms TTL=128 reply from 10.10.0.2: bytes=32 time<10ms TTL=128 reply from 10.10.0.2: bytes=32 time<10ms TTL=128 Нажмите комбинацию клавиш <Ctrl>+<C> для прекращения сообщений ping.
Гпава 2. Установка сети 83 Если ping выдает ошибку Если вы получили нечто вроде Bad IP address hotchicken (Неверный IP-адрес hotchicken) то имя хоста в записи файла для имя-сервера (в этом примере hotchicken) может быть отсутствует или неверно написано. К примеру, все идентификаторы в Linux/UNIX являются чувствительными к регистру. Другая причина может заключаться в том, что имя хоста вашего сервера просто не было сконфигурировано. Если вы увидите: Request timed out то это означает, что адрес IP, на который ссылается имя хоста в вашем файле, не мо- жет быть найден в подсети. Проверьте следующее: ♦ нет ошибок в имени хоста в записи файла; ♦ сетевой кабель подключен, провод и контакты не повреждены и не ржавые; ♦ конфигурация сети позволяет направлять сетевой трафик между клиентом и сер- вером. Ограничения подсети или системы сетевой защиты могут препятствовать серверу в получении данных от клиента. • Ограничения подсети: TCP/IP может быть сконфигурирован для ограничения трафика между подсетями. Если ваша клиентская машина является частью сложной сети или подсети, уточните у сетевого администратора, имеет ли она неограниченный доступ к серверу. ВНИМАНИЕ! WNet не позволяет направлять сетевой трафик между подсетями. • Система сетевой защиты (firewall): ваша проверка соединения может дать ошибку, если сервер базы данных находится под программной или аппаратной системой сетевой защиты, которая блокирует порт 3050 или сконфигуриро- ванный вами порт. Проблемы с событиями Несмотря на то, что каждый клиент соединяется с сервером через один канал, собы- тия Firebird — механизм обратной связи, который позволяет передавать сообщения о событиях назад клиентам с помощью триггеров и хранимых процедур, — использу- ют произвольные доступные порты. В статических замкнутых сетях без внутренних систем сетевой защиты это обычно не вызывает проблем. В сетях, где существует множество подсетей, динамическая IP-адресация и строго сконфигурированная сис- тема сетевой защиты, передача событий может закончиться неудачей. В Firebird 1.0.x ваш сетевой администратор должен установить некий способ, чтобы гарантировать наличие доступного порта, который всегда свободен, открыт и являет-
84 Часть I. Учебный лагерь ся статическим. Проблема может быть решена тем или иным образом в большинстве сетей. Firebird 1.5 упрощает дело. Он делает возможным явно задавать IP-адрес в сети для трафика событий. Используйте параметр RemoteAuxPort в файле firebird.conf с целью статической установки IP-адреса для интерфейса (карта, маршрутизатор, шлюз и т. д.), который будет доступен для направления событий. Более подробно о событиях Firebird см. главу 32. Другие сетевые проблемы Если дела идут плохо, вы можете найти немало советов в приложении 2. Пора дальше Куда дальше? Если у вас все в порядке с установками сети, и вам не терпится идти дальше, переходите прямо к главе 4 для изучения "основ", которые помогут вам со- единиться с базой данных. Если же вы инсталлировали Firebird в мультипроцессор- ной системе, или вы подозреваете, что некоторые элементы конфигурации вашей машины могут вызвать проблемы, читайте дальше. Несмотря на то, что редко бывает нужным изменять конфигурацию Firebird для основных операций, в главе 3 описыва- ется несколько опций конфигурации, которые могут помочь вам избавиться от го- ловной боли.
ГЛАВА 3 Конфигурирование Firebird После инсталляции Firebird, как правило, готов к запуску. Конфигурирование не тре- буется или минимально. Если инсталляция и настройка сети прошли по плану, нет необходимости что-нибудь делать для нового пользователя, нужно лишь продолжать узнавать возможности Firebird и экспериментировать с программным обеспечением. Вы можете просто пропустить эту главу и перейти к следующей. Тем не менее, это заявление может оказаться не совсем верным для некоторых плат- форм, или когда определенная операционная система заблокировала одну или две функции автоматического конфигурирования инсталляционного скрипта или про- граммы. Данная глава будет интересна новым пользователям при решении некото- рых из этих проблем. В первом разделе главы указано, как конфигурировать переменные окружения и ре- сурсы файловой системы для использования Firebird. Здесь описывается проверка параметров конфигурации сервера, как их модифицировать в случае особого исполь- зования и требований совместимости. Конфигурация на уровне базы данных Эта глава посвящена конфигурации на уровне сервера. Один сервер Firebird может работать с несколькими базами данных одновременно. Каждая база данных может быть сконфигурирована таким образом, чтобы соответствовать предъявляемым к ней требованиям. Конфигурирование на уровне базы данных см. в главах 15 и 39. Переменные окружения Переменные окружения — глобальные установки системы, которые используются при первоначальной загрузке операционной системы. В Windows, Linux и в боль- шинстве систем UNIX сервер Firebird распознает и использует некоторые перемен- ные окружения, если они установлены. Процессы fbserver (архитектура Суперсерве- ра) и fb inet server (архитектура Классического сервера) не распознают установок, которые ссылаются на сетевые ресурсы (диски и файловые системы), не управляемые физически серверной машиной.
86 Часть I. Учебный лагерь Где устанавливаются переменные окружения Windows Тип переменных окружения и способ их установки меняется от одной версии Windows к другой. В табл. 3.1 показаны типы (если применимы) и способы установ- ки значений переменным окружения. Таблица 3.1. Установки переменных окружения для Windows Версия Windows Тип переменной Описание Наличие Windows 95/98 He применяется Используйте Блокнот и установите пере- менные окружения в autoexec.bat или config.sys. Формат установки: SET <имя_переменной> = <значение_переменной>. Пример: SET FIREBIRD=C:\PROGRA~1\FIREBIRD. Для просмотра всех текущих установок переменных окружения наберите SET в командной строке Нет Windows NT/2000/XP Переменные пользователя. Делает перемен- ные доступными для приложений, запускаемых кон- кретным пользо- вателем, если этот пользователь подключен Используется для ограничения видимости переменных определенным пользователям. Диалоговое окно Свойства системы (System Properties), доступное через апплет Система (System applet) на Панели управ- ления или через контекстное меню, появ- ляющееся при щелчке правой кнопкой мы- ши на объекте Мой Компьютер и выборе элемента Свойства Да Windows NT Выберите Расширенное окружение | Новое (Advanced Environment | New) Windows 2000/XP Выберите Расширенное окружение | Переменные окружения | Новое (Advanced Environment | Variables | New) Windows NT/2000/XP Системные пере- менные. Доступны для всей системы (все сервисы, все пользователи). Команды set за- писаны в команд- ной строке. Пере- менные доступны только процессам, запущенным из командной строки Используйте, если Firebird запущен как сервис. Выберите Расширенное окруже- ние | Переменные окружения | Новое (Advanced Environment | Variables | New) или Редактировать (Edit), если доступно Да Полезно для установки переменной окру- жения для временного использования (например, iscjjserh isc__password для упрощения доступа из утилит командной строки при выполнении задач администра- тора). Используйте SET <имя__переменной> = <значение_переменной> для установки значения переменной; используйте set <имя~переменной> =, чтобы установить пустое значение Нет
Гпава 3. Конфигурирование Firebird 87 POSIX В Linux и UNIX самый простой путь установления переменных окружения — доба- вить их определения в общесистемный профиль значений по умолчанию. Пользователь root также может: ♦ выдать команды setenv () из командной строки или командного скрипта; ♦ для временного использования установить и экспортировать переменную из командной строки, например, export isc_user=sysdba. ISCJJSERvt ISC_PASSWORD Эффект этой опасной пары переменных окружения — дать доступ с правами пользо- вателя SYSDBA к серверу Firebird и его базам данных через утилиты командной строки или клиентские приложения любому, кто может соединиться с хост-машиной. Они удобны для разработчиков. Если вы не указываете имя пользователя и пароль при локальном соединении с базой данных, или когда вы запускаете утилиты командной строки, такие как gbak, gstat или gfix, Firebird проверяет, установлены ли переменные окружения iscjjser и isc password. Если установлены, Firebird позволяет вам соединиться без указания па- роля. Никогда не оставляйте эти переменные установленными на сервере, который содержит важные базы данных, если помещение, где располагается сервер, не явля- ется физически хорошо защищенным! Переменная FIREBIRD (или INTERBASE) Если установлена переменная окружения firebird (interbase для версии 1.0.x), то она используется и при инсталляции, и в процессе работы на всех платформах для указа- ния корневого каталога сервера Firebird. Если она присутствует, то перекрываются все другие установки — значения по умолчанию инсталляционного пакета, установ- ки в реестре Windows, конфигурация в firebird.conf, значения глобальных путей опе- рационной системы и т. д. В процессе инсталляции она указывает на каталог, в котором должен быть установ- лен Firebird. Значение переменной должно задавать полный путь, который существу- ет в физической файловой системе на хост-машине. При старте сервер читает значе- ния в файле конфигурации firebird.conf (или ib_config/isc_config в версии 1.0.x), кото- рый должен находиться в каталоге, назначенном переменной firebird (или interbase). Этот каталог должен быть родительским для каталога bin, где размещают- ся двоичные файлы Firebird. Здесь также по умолчанию находятся файлы сообщений и замков: firebird.msg (interbase.msg) и hostname.lek. Ж ПРИМЕЧАНИЕ. Вы можете указать другое размещение файлов firebird.msg ь-К (interbase.msg) и firebird.Ick (interbase.lck), установив переменные окружения \__\ firebird_msg (interbase_msg) и firebirD-Lock (interbase__lock). См. следующие разделы.
88 Часть I. Учебный лагерь Если переменная firebird не установлена, будут использованы значения по умол- чанию: ♦ /opt/firebird — для платформ Linux/UNIX; ♦ C:\Program Files\Firebird (версия 1.0.x) или C:\Program Files\Firebird\Firebird_l_5 (версия 1.5) — для платформ Windows. В табл. 1.2—1.5 в главе 1 указаны точные пути. Если Firebird установлен в каталоге, отличном от каталога по умолчанию, и задана переменная firebird, то клиентская библиотека будет читать значение переменной для определения пути инсталляции. В Windows клиентские приложения могут также найти путь инсталляции, читая в реестре поле Defaultinstance, которое создается, если проводилась инсталляция толь- ко клиента. HKEY_LOCAL_MACHINE\SOFTWARE\Firebird Project\Firebird Server\Instances Более подробное обсуждение способов поиска сервером размещения этих файлов см. далее в этой главе в разд. "Корневой каталог Firebird". Информацию об установках только клиента см. в разд. "Установка клиентов" главы 7. FIREBIRD_TMP По умолчанию Firebird будет использовать глобальное пространство временных фай- лов, обычно задаваемое как системное значение по умолчанию переменной окруже- ния тмр (обсуждается далее). Переменная окружения firebird_tmp (в версии 1.0.x — interbase tmp) задает пользовательское размещение для файлов сортировки Firebird. Значение должно указывать полный путь, который существует в физической файло- вой системе хост-машины. Доступны также другие способы определения размещения этих файлов. Конфигури- рование параметра TempDirectories (tmp_directory в версии 1.0.x) описано в разд. "Файл конфигурации Firebird" и разд. "Параметры для конфигурирования простран- ства временной сортировки" главы 36. *LOCKvi *_MSG firebird_lock (interbase_lock в версии 1,0.x) устанавливает расположение файла бло- кировок. firebird_msg (interbase_msg в версии 1.0.x) устанавливает расположение файла сообщений Firebird. Эти две переменные независимы друг от друга, они могут указывать различные раз- мещения файлов. Значение должно указывать полный путь, который существует в физической файловой системе хост-машины. ТМР Пространство сортировки на серверной машине Пространство сортировки — это место на диске, где сервер сохраняет промежуточ- ные результаты (во временных файлах) для запросов, которые требуют сортировки
Глава 3. Конфигурирование Firebird 89 или используют агрегатные функции. Firebird 1.0.x для этого использует только дис- ковые файлы. Firebird 1.5, если может, сохраняет эти файлы в RAM и отправляет их надиск только в том случае, если оперативная память исчерпана. Глобальная переменная окружения тмр (или temp в некоторых системах) указывает каталог на сервере, где приложения должны сохранять временные файлы. Firebird попытается сохранить временные файлы сортировки именно здесь, если не опреде- лена переменная окружения firebird_tmp (см. разд. "FIREBIRD TMP") и в файле конфигурации Firebird не указано пространство сортировки (см. переменные TempDirectories И TMP_DIRECTORY В главе 36). Файлы скриптов isql на клиенте Интерактивная утилита командной строки isql предоставляет возможность записы- вать последовательность интерактивных команд SQL в файл скрипта при использо- вании переключателя output. На клиентской машине переменная тмр задает располо- жение этих файлов скриптов, если не было указано другого абсолютного пути. Если переменная тмр не установлена, то клиент Firebird использует любой другой времен- ный каталог, который будет найден в локальной системе, обычно tmp в файловой системе клиента Linux/UNIX или C:\Temp в клиенте Windows. Файл конфигурации Firebird Firebird не требует постоянной сложной реконфигурации, которая необходима дру- гими тяжеловесными РСУБД. Тем не менее целый ряд вариантов конфигурирования доступен для настройки сервера Firebird и хост-системы, на которой он выполняется, под ваши специальные требования. Файл конфигурации Firebird называется firebird.conf для всех версий Firebird 1.5 и выше. В предыдущих версиях его имя зависело от операционной системы: ♦ isc_config — в Linux/UNIX; ♦ ibconfig — в Windows. В версии 1.5 было добавлено несколько новых параметров. Когда стартует сервер Firebird, он читает файл конфигурации и настраивает свои флаги времени выполнения (runtime flags), если файл конфигурации содержит значе- ния, отличные от установленных по умолчанию. Данные из файла больше не будут считываться, пока не произойдет новый запуск сервера. Параметры конфигурации по умолчанию и их значения содержатся в файле конфигурации и закомментированы символом #. Нет необходимости убирать комментарий, чтобы сделать значения по умолчанию видимыми процедуре запуска сервера. Изменение параметров конфигурации Нет необходимости изменять значения по умолчанию, если только вы не собираетесь настроить некоторые из них. Это не рекомендуется делать, если вы не в полной мере понимаете, к чему это может привести.
90 Часть I. Учебный лагерь Отдельные установки конфигурации по умолчанию, которые могут быть полезными для некоторых существующих приложений или инсталляций не по умолчанию, об- суждаются здесь вкратце. Полное описание параметров конфигурации см. в главе 36. Файл конфигурации можно редактировать любым текстовым редактором (например, vim в Linux или Блокнот в Windows). Не копируйте этот файл с машины Windows в Linux и наоборот, поскольку эти системы по-разному сохраняют символы перевода строки. Записи параметров в файле firebird.conf представлены в форме: имя_параметра = значение где имя_параметра — строка, содержащая имя параметра (без пробелов), а значение — это число, логическая константа (1=Истина, 0=Ложь) или строка, которые задают значение параметра. Для задания параметру значения, отличающегося от значения по умолчанию, удалите символ примечания (#) и отредактируйте значение. Имена параметров и синтаксис в файле ibconfig/isc_config для Firebird 1.0.x отлича- ются от того, что может содержаться в firebird.conf. Формат, размер и количество параметров являются более ограниченными. Формат в ibconfig/isc config: имя_параметра значение где промежуток между именем и значением может быть символами табуляции и про- белами (по желанию, как больше нравится). Каждая строка в файле ограничена 80 символами. Неиспользуемые параметры и инсталляционные значения по умолча- нию закомментированы с помощью символа #. Помните, что в Linux параметры чувствительны к регистру. ПРИМЕЧАНИЕ. Вы можете редактировать файл конфигурации и когда сервер запущен. Для активации изменений нужно остановить и заново запустить сервер. Корневой каталог Firebird Корневой каталог инсталляции Firebird может использоваться в разных ситуациях — при инсталляции как используемый сервером атрибут, для параметров конфигурации и для клиентов. Поскольку есть разные способы сообщения серверу, где можно найти значение этого атрибута, разработчики и системные администраторы для его пра- вильного определения должны знать, в каком порядке сервер находит это значение. 1. На любой платформе сервер в первую очередь смотрит глобальную переменную окружения firebird. Если он находит эту переменную, ее значение будет исполь- зовано без всяких условий. 2. Если переменная окружения firebird отсутствует, то для платформ Windows сер- вер отыскивает ключ в реестре HKEY_LCXIAL_MACHINE\SOFTWARE\Firebird Project\Firebird ServerMnstances
Глава 3. Конфигурирование Firebird 91 и смотрит поле Defaultinstance. Если он находит в данном поле правильный путь к каталогу, то использует это значение. Другие платформы не имеют подобного указателя. 3. Если корневой каталог все еще не найден, то предполагается, что временный кор- невой каталог располагается на один уровень выше каталога запущенного процес- са (,.\ для Windows и ../ или ссылка на /ргос/self/exe для POSIX, если доступно). 4. Теперь процедура запуска ищет в этом каталоге файл firebird.config. Если файл firebird.config найден, то процедура отыскивает параметр RootDirectory. Если па- раметр присутствует, его значение становится окончательно корневым каталогом, иначе промежуточное значение из п. 3 становится окончательным значением. Д ВНИМАНИЕ! Если файл firebird.config не был найден на уровне, предшествую- щем запускаемому процессу, это может означать, что корневой каталог не был определен по причине нестандартной инсталляции. Сервер должен найти корне- вой каталог файлов. Если вам встретилась ошибка безопасности или ошибка файловой системы в процессе соединения или во время выполнения, вы должны пересмотреть ваши пути инсталляции, чтобы убедиться, что все действия, опи- санные в данном разделе, правильно определили корневой каталог файлов и подкаталоги. Параметры, связанные с доступом к файлам Firebird имеет несколько параметров для защиты его файлов и баз данных от разру- шающего или неавторизованного доступа. Если вы перемещаете приложение работы с базой данных или используете инструмент администратора для Firebird 1.5, вам следует обратиться к разд. "Файл конфигурации Firebird" в главе 36 для получения детальной информации об указанных далее параметрах. ♦ RootDirectory — может быть использован для изменения абсолютного пути к кор- невому каталогу в локальной файловой системе. Он должен оставаться закоммен- тированным, если только вы не захотите изменить процедуру запуска, перекрыв путь к корневому каталогу инсталляции сервера Firebird (см. п. 3 в предыдущем разделе). ♦ DatabaseAccess — в Firebird 1.0.x сервер может соединяться с любой базой данных в его локальной файловой системе и всегда доступен приложениям, передающим абсолютный путь к файлу в файловой системе. Этот параметр был введен в вер- сии 1.5, чтобы обеспечить более четкое управление безопасностью при доступе к файлам базы данных, а также для поддержки средства алиасов баз данных. Инсталляция по умолчанию устанавливает значение параметра в Full, чтобы ими- тировать поведение Firebird версии 1.0.x. Альтернативные режимы могут ограни- чить доступ сервера только к базам данных с алиасами или к базам данных, раз- мещенных в указанных каталогах файловой системы. Д ВНИМАНИЕ! Настоятельно рекомендуется установить этот режим и сделать доступным средство алиасов баз данных. Информацию об алиасах базы данных см. в главе 4.
92 Часть I. Учебный лагерь ♦ ExternaiFiieAccess — заменяет параметр external_file_directory, введенный в версии 1.0. Этот параметр обеспечивает три уровня безопасности для External Files (внешние файлы — текстовые файлы фиксированного формата, к которым возможен доступ, как и к таблицам базы данных). Если вы переносите базу дан- ных, в которой определены внешние файлы таблиц, вам нужно установить этот параметр для версии 1.5, потому что он отключен по умолчанию. Конфигурация необязательная, тем не менее рекомендуется для версии 1.0.x. ♦ UDFAccess — предназначен для защиты местоположения кода внешних модулей. Он заменяет не только необязательный параметр версии 1,0.x externai_function_ directory, но также и форму представления значения. Firebird 1.5 инсталлируется с отключенным по умолчанию доступом к библиотекам внешних функций, в то время как большинство серверов до этого давали полный доступ. ♦ TempDirectories (tmp_di rectory в версии 1.0.x)— конфигурирование этого пара- метра является одним из способов задания размещения временного пространства сортировки для сервера, указав местоположение на диске. Синтаксис Firebird 1.5 отличается от синтаксиса Firebird 1.0.x. Другие полезные параметры Следующие параметры могут быть полезными в некоторых аппаратных конфигура- циях. ♦ cpuAffinityMask (cpu_affinity в версии 1.0.x)— может быть использован для на- значения процессоров, которые Суперсервер в Windows будет использовать на машинах SMP. Эта проблема известна как эффект "see-saw", когда операционная система постоянно переключает выполнение Суперсервера между процессорами на некоторых машинах SMP. Свойство CPU должно быть установлено для одного процессора, если вам встретилась такая проблема. По умолчанию маска свойств устанавливается для использования первого процес- сора в массиве. ♦ LockMemSize— параметр специфичен для Классических серверов; он определяет количество байтов разделяемой памяти, которая отводится для таблицы памяти, используемой менеджером блокировки. Вам может понадобиться изменить это количество, если вы встретите в Классическом сервере ошибку "Lock manager is out of room" (Менеджеру блокировки не хватает памяти). В связи с этой пробле- мой см. также параметр LockHashsiots. ♦ SortMemBlockSize И SortMemUpperLimit — ЭТИ два параметра были добавлены В Вер- СИЮ 1.5, чтобы позволить устанавливать и ограничивать объем оперативной памя- ти, которую использует сервер при внутренней сортировке. Для Классического сервера этот размер по умолчанию слишком велик, чтобы поддерживать доста- точно большое количество соединений. ♦ Dummypacketinterval (dummy_packet_interval в 1.0.x)— параметр— пережиток 16-битовых систем; он может стать причиной проблем в 32-битовых Windows. Это был старый параметр времени ожидания InterBase, предполагалось устанав- ливать количество секунд (целое число), в течение которых сервер должен был
Глава 3. Конфигурирование Firebird 93 ожидать от клиента сообщений, после чего сервер должен был посылать пустой пакет для получения подтверждения. По умолчанию он установлен в 0 для Firebird 1.5 (неактивен) и в 60 для Firebird 1.0.x. Он должен быть неактивным (ус- тановленным в 0) для всех систем Windows. Также настоятельно рекомендуется отключать его для других операционных систем1. ♦ RemoteBindAddress — по умолчанию могут соединиться клиенты из любой сети, из которой сервер принимает трафик. Этот параметр позволяет связать сервис Firebird с исходными запросами через один IP-адрес (например, сетевая карта) и отклонять запросы соединения от любых других сетевых интерфейсов. Это помо- гает решать проблемы некоторых сетей, где сервер является хостом для несколь- ких подсетей. Не поддерживается в версии 1.0.x. ♦ compieteBooieanEvaluation — параметр может быть использован для преобразова- ния логики вычисления логических значений в виде сокращенного вычисления булевских выражений, используемой в Firebird 1.5 и выше, в полное вычисление булевских выражений, используемое в Firebird 1.0.x. ♦ oidParameterOrdering— восстанавливает старый способ обработки порядка пара- метров для запросов с подзапросами — сначала параметры подзапроса, затем па- раметры внешнего запроса. Firebird 1.5 по умолчанию обрабатывает параметры в точном порядке их следования, что может быть несовместимо со старыми вер- сиями различных компонентов доступа, которые ориентировались на поведение InterBase/Firebird 1.0. Пора дальше Теперь нам больше ничего не осталось, кроме как соединиться с базой данных и на- чать выполнять серьезные эксперименты. Глава 4, последняя в нашем "учебном лаге- ре”, даст вам возможность соединиться с примером базы данных или с любой другой совместимой с Firebird базой данных, которая может находиться на вашем сер- вере. Из-за ошибки в Windows использование DummyPacketlnterval может привести к зависа- нию или краху Windows на клиентской стороне. Объяснения см. в следующей статье Microsoft Support: http://support.microsoft.com/default.aspx?kbid=296265. Не рекомендуется его ис- пользовать и в системах, отличных от Windows. Он может даже мешать возможному отключе-
ГЛАВА 4 Основные операции Теперь у вас есть установленный сервер Firebird, что дальше? Эта глава быстро обу- чит вас основам Firebird. Запуск Firebird на Linux/UNIX Суперсервер Каталог инсталляции по умолчанию /opt/firebird. В каталоге /bin находится в двоич- ном формате сервер Firebird fbserver (ibserver для Firebird 1.0.x), который запускается как процесс-демон в Linux/UNIX. Он запускается автоматически после инсталляции посредством RPM или скрипта и каждый раз при перезагрузке сервера запуском скрипта демона firebird, находящегося в /etc/rc.d/init.d (или /etc/init.d в SuSE), кото- рый вызывает утилиту командной строки Firebird Manager— fbmgr.bin. Firebird Man- ager может быть использована из командной строки для запуска и остановки процес- са вручную. Запуск сервера Если вы по разным причинам запустили Firebird вручную, соединитесь с ним как пользователь root или firebird. Запомните, какую учетную запись вы использовали при запуске fbserver, потому что все созданные объекты будут принадлежать пользо- вателю с этой учетной записью. Если позже другой пользователь запустит процесс с использованием другой учетной записи пользователя, то эти объекты будут ему не- доступны. Настоятельно рекомендуется создать системного пользователя с именем firebird и запускать сервер Firebird с этой учетной записью. Для запуска процесса выполните из командной строки следующую команду: ./fbmgr.bin -start -forever Для версий Firebird, предшествующих 1.5, выполните: ./ibmgr -start -forever Переключатель -forever означает, что Guardian будет управлять запуском. При ис- пользовании Guardian процесс сервера будет заново запущен, если он по каким-либо причинам завершится аварийно.
Глава 4. Основные операции 95 Для запуска сервера без использования Guardian введите: ./fbmgr.bin -start -once Для версий Firebird, предшествующих 1.5, выполните: ./ibmgr -start -once Переключатель -once означает, что если сервер будет аварийно завершен, перезапус- тить его можно будет только вручную. Остановка сервера В целях безопасности убедитесь, по возможности, что все соединения с базой данных отключены, прежде чем вы остановите сервер. Переключатель -shut отменяет все текущие транзакции и прекращает работу сервера немедленно. Вам не требуется быть подключенным как пользователь root для остановки сервера Firebird fbmgr, но вы должны иметь полномочия пользователя SYSDBA. Выполните следующую команду: ./fbmgr.bin -shut -password <паролъ SYSDBA? Используйте команду для версий, предшествующих 1.5: ./ibmgr.bin -shut -password < пароль SYSDBA> Управляемое завершение работы На этой платформе Firebird не имеет утилиты для подсчета количества пользователь- ских соединений с базой данных для Суперсервера. Если вам нужно предоставить клиентам интервал времени для завершения работы и корректного отключения, за- вершайте работу индивидуальных баз данных с использованием инструмента gfix с переключателем -shut и одним из доступных аргументов для управления отключени- ем. (См. разд. "Останов базы данных" в главе 39.) Другие команды fbmgr Синтаксис Из командной строки: ./fbmgr.bin -команда [-режим [параметр] . ..] Альтернативно вы можете стартовать интерактивную сессию fbmgr или ibmgr из ко- мандной строки (например, перейдя в режим с подсказкой). Наберите: ./fbmgr <нажмите Return/Enter> для того чтобы перейти к следующей подсказке: FBMGR>
96 Часть I. Учебный лагерь В режиме с подсказкой синтаксис команд: FBMGR> команда [-режим [параметр] ...] Например, вы можете запустить сервер одним из следующих способов. Из команд- ной строки: ,/fbmgr -start -password пароль В режиме с подсказкой: FBMGR> start -password пароль Переключатели fbmgr В табл. 4.1 представлен список переключателей fbmgr и ibmgr, доступных из команд- ной строки и из режима с подсказкой. Таблица 4.1. Переключатели fbmgr/ibmgr Переключатель Аргумент Другие переключатели Описание -start -forever | once -user, -password Запускает fbserver, если он не был еще запущен -shut -user, -password Останавливает fbserver -show Показывает хост и пользователя -user Имя пользователя SYSDBA; используется с пере- ключателями -start и -stop, если пользователь системы не является root или эквивалент- ным ему -password Пароль SYSDBA Используется с переключателя- ми -start и -stop, если пользо- ватель системы не является root или эквивалентным ему -help Выводит короткий текст помощи fbmgr -quit Используется для выхода из режима с подсказками Классический сервер Классический сервер Firebird использует процессы xinetd или inetd для обработки поступающих запросов. (Применяемый процесс зависит от версии Linux.) Нет необ- ходимости явно запускать сервер. Процесс xinetd или inetd запускается автоматиче-
Гпава 4. Основные операции 97 ски; когда он получает запрос от клиента Firebird на соединение, он порождает ко- пию процесса fbinetserver для этого клиента. Как сервер прослушивает запросы на соединение Если Классический сервер Firebird был установлен инсталлятором, использующим скрипты или RPM, то файл конфигурации запуска для fbinetserver с именем firebird должен быть добавлен в сервисы, о которых знает [xjinetd. В большинстве дистрибу- тивов Linux этот файл размещается в каталоге /etc/xinetd.d. Чтобы [x]inetd "слушал" запросы на соединение от клиентов для вашего Классического сервера Firebird, скрипт firebird должен находиться в том же каталоге, где стартует процесс [xjinetd. СОВЕТ. Если [xjinetd запущен, и ни одного запроса на соединение клиента не последовало вовсе, проверьте, действительно ли скрипт firebird находится там, где он должен быть. Если нет, то вы можете выделить скрипт firebird.xinetd из файлового комплекта инсталляции, скопировать в нужный каталог и переимено- вать в firebird. Чтобы [xjinetd "видел" сервис Firebird, остановите и заново запус- тите [xjinetd. Остановка и запуск [x]inetd и его сервисов Демон [x]inetd сам является сервисом, который по запросу управляет другими серви- сами, например Классическим демоном Firebird. Остановка [xjinetd приведет к тому, что все процессы, которыми он управляет, будут также остановлены. Его запуск или повторный запуск приведет к возобновлению прослушивания запросов и запуску управляемых им процессов. Если все сервисы в каталоге ../rc.d являются безопасными для остановки, соедини- тесь как пользователь root и остановите [xjinetd следующей командой из командной строки: 4 service xinetd stop или командой: # service inetd stop Если [xjinetd не был сконфигурирован для автоматического перезапуска после оста- новки, запустите его командой1: # service xinetd restart 1 * * 4 1 По умолчанию [xjinetd в своем файле конфигурации имеет ограничение на число стартуемых экземпляров конкретного сервиса. Это может проявляться как ошибка при подключении очередного пользователя (например, невозможно подключить более 60 поль- зователей). Отредактируйте файл конфигурации [xjinetd и установите желаемый лимит количества сервисов fb_inet_server, т. е. количества подключений к Классическому серверу Firebird. — Прим. науч. ред. 4 Зак. 420
98 Часть I. Учебный лагерь Остановка процесса Firebird Если вам нужно остановить вышедший из-под контроля процесс Firebird в Классиче- ском сервере, вы можете это сделать. Найдите ошибочный процесс, выполнив команду top из командной строки. Эта утилита отображает список наиболее интен- сивно использующих CPU процессов, постоянно обновляя этот список. Любой экземпляр fb inet server с необычным использованием ресурсов должен появиться в этом списке. Выберите идентификатор (process ID, PID) ошибочного процесса fb_inet_server из самой левой колонки в списке. Вы можете использовать этот PID в команде kin для посылки сигнала ошибочному процессу. Например, для PID 12345 вы можете попы- таться остановить процесс, выдав: # kill 12345 Если процесс остается видимым в списке утилиты top, вы можете попытаться форси- ровать остановку процесса: # kill -9 12345 Д ВНИМАНИЕ! Проявляйте величайшую осторожность при использовании коман- ! X ды kill, особенно если вы подключились как пользователь root. Запуск сервера Firebird в Windows Суперсервер Выполняемая программа Суперсервера Firebird — fbserver.exe. Хотя он может запус- каться и как самостоятельная программа, он также может находиться под управлени- ем Guardian — fbguard.exe. Guardian обеспечивает возможность эмулировать автома- тический рестарт сервисов в Windows и POSIX, запущенных с переключателем -forever. Если приложение fbserver.exe аварийно завершается, Guardian пытается за- ново его запустить. Рекомендуется использовать Guardian на хостах, работающих на платформах Windows 95/98 и ME, а также NT/XP, если сервер выполняется как при- ложение. В Windows NT и Windows 2000 программа сервера Firebird может выполняться как сервис и как приложение. Инсталляция по умолчанию устанавливает сервер Firebird — и Guardian, если выбран — для автоматического выполнения как сервисы. Вариант выполнения для обоих может быть изменен, чтобы они выполнялись как приложения. В Windows 95/98, ME и ХР Home Edition Firebird может выполняться только как при- ложение. Если Firebird выполняется как приложение, на панели задач появляется со- ответствующая иконка. Некоторые задачи администрирования могут быть выполне- ны вручную при щелчке правой кнопкой мыши на этой иконке.
Гпава 4. Основные операции 99 Выполнение Firebird как сервиса в Windows NT, 2000 и ХР Если данный компьютер используется как сервер БД, то вам настоятельно рекомен- дуется выполнять сервер Firebird как сервис. ПРИМЕЧАНИЕ. Пользователи, выполняющие миграцию с InterBase 6.0 или бо- -Ж лее раннего, должны обратить внимание, что не требуется выполнения Firebird __как приложения на хост-машинах SMP, чтобы установить наличие только одного процессора. Опция сервиса Firebird по использованию количества процессоров содержится в файле конфигурации. Более подробную информацию см. в разд. "Файл конфигурации Firebird" главы 36. Запуск и остановка сервиса вручную Для остановки сервиса вручную откройте окно командной строки и введите следую- щую команду: NET STOP Firebirdserver Для старта или рестарта сервера вручную введите команду: NET START FirebirdServer /,\ ВНИМАНИЕ! Поскольку команды NET возвращают сообщения в окно командной / • \ строки, не пытайтесь запустить их в окне Выполнить (Run) через кнопку Пуск (Start). Использование утилиты instsvc Альтернативный, "родной" для Firebird способ запуска и остановки сервисов Firebird и Guardian — использование утилиты instsvc.exe, которая находится в каталоге \bin в корневом каталоге Firebird. Утилита instsvc.exe применяется системой для запуска сервиса Firebird— и Guardian, если выбран— когда выполняется первоначальная загрузка хост-сервера. Поскольку с самого начала не ожидалось частого использова- ния ее людьми — это команда в стиле DOS, содержащая переключатели. f ПРИМЕЧАНИЕ. Firebird 1.5 содержит дополнительный необязательный переклю- чатель для соединения, чтобы позволить скриптам инсталлятора добавить воз- ___У можность создания "реального пользователя"— подключенного пользователя для установки сервиса при загрузке системы. Это рекомендуется сделать, т. к. скрипты создают пользователя Firebird с ограниченными привилегиями; соответ- ственно устанавливается инсталляция сервиса. (См. разд. "Защита, основанная на платформе" в главе 33.)
100 Часть I. Учебный лагерь Останов и рестарт сервиса с использованием instsvc Откройте окно командной строки и перейдите к каталогу \bin, находящемуся в кор- невом каталоге установки Firebird. Для останова сервиса Firebird введите: C:\Program Files\Firebird\Firebird_l_5\bin> instsvc stop Для старта (рестарта) сервиса Firebird используйте одну командную строку, изменив, при необходимости, приоритет процесса: C:\Program Files\Firebird\Firebird_l_5\bin> instsvc start . [-boostpriority I -regularpriority] ПРИМЕЧАНИЕ. Эти команды не выполняют, соответственно, деинсталляцию и инсталляцию сервиса. Апплеты Firebird Manager Когда Firebird выполняется как сервис, небольшое количество административных задач, включая останов и рестарт, может быть решено с использованием апплета Firebird Manager на Панели управления. Самый простой апплет инсталлируется при установке Firebird. Более сложные апплеты, включая версии языков, можно загрузить из Firebird CVS с сайта SourceForge или с различных сайтов, связанных с Firebird. Выполнение Firebird как приложения на платформах Windows Если сервер Firebird выполняется как приложение, вы должны увидеть иконку в сис- темной области на серверной машине, как показано на рис. 4.1. Вид иконки в сис- темной области зависит от того, запущен ли только сервер, или вы управляете его выполнением с помощью Guardian. Рекомендуется использовать Guardian при вы- полнении Суперсервера как приложения и исключить его при выполнении Классиче- ского сервера. |>-ф 3:17РМ Нф ф 3:17 РМ; Guardian Server, no Guardian Рис. 4.1. Иконка на системной панели Вы не увидите иконку, если сервер не был запущен (в случае Суперсервера) или не был инициализирован (в случае Классического сервера). Пока вы не установите ре- жим автоматического запуска сервера, вам будет нужно стартовать или инициализи- ровать его вручную.
Глава 4. Основные операции 101 Запуск сервера как приложения вручную Если Суперсервер не запущен, или Классический сервер не инициализирован, он может быть запущен вручную при выборе в меню Firebird— Пуск | Все програм- мы | Firebird (Start | Programs | Firebird). Альтернативно можно стартовать сервер или Guardian из командной строки. Вызови- те окно командной строки и перейдите к каталогу \bin в каталоге инсталляции Fire- bird. Выполните следующие действия в соответствии с тем, собираетесь ли вы ис- пользовать Guardian или будете запускать сервер без возможности автоматического рестарта. Суперсервер Программа Guardian называется fbguard.exe в Firebird 1.5 и idguard.exe в более ранних версиях. Используйте следующую команду для старта Guardian: fbguard.exe -а ibguard.exe -а /★ для версии 1.0.x */ Guardian размещает свою иконку на системной панели и автоматически стартует Су- персервер. Имя программы сервера для Суперсервера fbserver.exe (ibserver.exe в Firebird 1.0.x). Для запуска Суперсервера напрямую, минуя защиту Guardian, используйте команду: fbserver.exe -а ibserver.exe -а /* для версии 1.0.x */ Сервер стартует и размещает свою собственную иконку на системной панели. Классический сервер ПРИМЕЧАНИЕ. Этот текст относится к Firebird 1.5 и следующим. Классический сервер для Windows не поддерживается в более ранних версиях. Основное преимущество выполнения Классического сервера в Windows — его воз- можность использовать мультипроцессорные системы. Эта функция недоступна в случае Суперсервера для многих систем SMP. Тем не менее, поскольку использова- ние памяти Классическим сервером находится в прямой зависимости от количества одновременных подключений, может оказаться невозможным устанавливать систему на сайтах, где ресурсы сервера не обеспечивают поддержки большого количества пользователей в системе. Процесс, который является "ушами’’ для запросов клиентов на соединение с Класси- ческим сервером, является начальным экземпляром программы fb_inet_server.exe. Если начальный экземпляр fb_inet_server.exe не запущен, то не будет возможно со- единение клиент-сервер; при попытке соединения вы получите сообщение об ошибке
102 Часть I. Учебный лагерь "Unable to connect to the server. Database cannot be found" (Невозможно соединиться с сервером. База данных не может быть найдена). Когда клиенты соединяются с базой данных, для каждого клиентского соединения запускается один экземпляр fb_inet_server.exe (1.2 Мбайт) и, если сконфигурировано, один экземпляр Guardian на все экземпляры fb_inet_server.exe. Для каждого соедине- ния выполняется собственное выделение кэш-памяти. Классический сервер и Guardian_______________________________________ Случайно или умышленно, инсталлятор Firebird 1.5.0 имеет небольшую, но приво- дящую в замешательство аномалию. Если вы не отметите в процессе инсталляции режим Use Guardian (Использовать Guardian), инсталлятор поместит версию для Суперсервера программы Guardian в каталог \bin, и он никогда не будет работать с Классическим сервером. Если вы отметите этот режим, то в процессе инсталляции получите сообщение об ошибке, однако инсталлированная версия Guardian будет прекрасно работать с Классическим сервером. Вы можете проверить, правильная ли у вас версия, попытавшись запустить Guardian. Если вы увидите диалоговое окно сообщения об ошибке, содержащее слово "fbserver", значит, вы не выбрали под- держку Guardian в процессе инсталляции. В любом случае Guardian является лишним для Классического сервера. Вы ничего не потеряете, если не будете его инсталлировать. Я рекомендую игнорировать Guardian для Классического сервера. Для запуска начального экземпляра Классического сервера как приложения вручную вызовите окно командной строки, перейдите в каталог \bin и наберите: fb_inet_server.exe -а Иконка сервера должна появиться на системной панели. Ваш сервер теперь готов к получению запросов на соединение. Вы можете альтернативно выбрать режим Use Guardian в процессе инсталляции. Вы также можете стартовать Guardian из того же каталога: fbguard.exe -с -а В этом случае иконка Guardian появится на системной панели, однако она не может быть использована для отмены инициализации сервера (см. ранее в разд. "Клас- сический сервер" замечание о Классическом сервере). Останов сервера Останов сервера— операция, которая по-разному воздействует на Суперсервер и Классический сервер. Суперсервер Щелкните правой кнопкой мыши по иконке Guardian или сервера и выберите в кон- текстном меню Shutdown (Остановить). Если выполняется Guardian, то сначала он остановит сервер, а затем закроется сам. Подключенные в этот момент пользователи потеряют всю неподтвержденную работу.
Гпава 4. Основные операции 103 Классический сервер В большинстве случаев нет необходимости "останавливать" Классический сервер. Выбор варианта Shutdown в контекстном меню иконки сервера предотвратит новые соединения с сервером, но это не будет воздействовать на подключенные процессы. ПРИМЕЧАНИЕ. Щелчок мышью по варианту Shutdown в контекстном меню Guardian ничего не выполняет. Очень редко бывает нужным (если вообще бывает) останавливать Классический сер- вер вручную, поскольку закрытие клиентского соединения завершает этот процесс чисто и корректно. Единственный способ остановить Классический сервер, который выполняется как приложение, — применение "грубой силы", через Диспетчер задач. Алиасы базы данных Firebird 1.5 ввел концепцию алиасов базы данных не только для того, чтобы облег- чить жизнь уставших от клавиатуры разработчиков, но и для того, чтобы улучшить переносимость приложений, а также чтобы усилить контроль над внутренним и внешним доступом к файлу базы данных. aliases.conf Средство алиасов включает файл конфигурации aliases.conf. Он находится в корне- вом каталоге вашей инсталляции сервера и не должен перемещаться оттуда. Переносимость До реализации 1.5 все клиентские приложения соединялись с сервером, используя строку соединения, которая включала абсолютный путь к серверу. Формат абсолют- ного пути меняется в зависимости от того, выполняется ли сервер под Windows или на POSIX-совместимой платформе (Linux, UNIX и т. д.), а для серверов под Windows еще и от того, какой вид сетевого соединения используют клиенты — TCP/IP или NetBEUI. Предположим, у вашего сервера имя hotchicken. Если сервер выполняется на POSIX- совместимой платформе; клиенты TCP/IP будут соединяться с базами данных, ис- пользуя строку соединения следующего формата: hotchicken:/opt/databases/Employee.fdb Если же сервер работает под Windows, клиенты TCP/IP должны соединяться, исполь- зуя другой формат пути: hotchicken:D:\databases\Employee.fdb Средства алиасов базы данных делают эту разницу для клиентов TCP/IP прозрачной. Абсолютный путь строки соединения помещается в файл алиасов, связывая этот путь
104 Часть /. Учебный лагерь с простым именем алиаса. Например, в файле aliases.conf для сервера под Linux наш пример может быть сохранен как dbl = /opt/databases/Employee.fdb Для сервера в Windows, инсталлированного для клиентов TCP/IP, это может быть dbl = D:\databases\Employee.fdb Независимо от того, где установлен сервер — под Windows или POSIX — строка со- единения для клиентов становится одинаковой: hotchicken:dbl Тем не менее это средство не является столь изящным, если вы хотите сделать строку соединения вашего приложения прозрачной как для соединений TCP/IP, так и NetBEUI. Нотация UNC для сервера под Windows для клиентов NetBEUI предполага- ет совместимость, однако если алиас базы данных идентичен, то серверная часть не является переносимой: \\hotchicken\dbl в сравнении с: hotchi cken:dbl Управление доступом Основное преимущество средства алиасов в том, что оно может быть использовано в комбинации с параметром DatabaseAccess = none из файла firebird.conf для ограниче- ния доступа к файлам баз данных — доступ разрешен только к файлам, указанным в aliases.conf. Алиасы баз данных появились в Firebird 1.5. Для их использования отредактируйте файл aliases.conf в корневом каталоге инсталляции Firebird, используя текстовый ре- дактор, такой как Блокнот (в Windows) или vi (Linux). Структура файла aliases.conf Инсталлированный файл aliases.conf выглядит приблизительно таким образом: # # List of known database aliases # ---------------------------- # Examples: # # dummy = c:\data\durrany.fdb # Как и во всех файлах конфигурации Firebird, символ # является маркером коммента- риев. Для конфигурирования алиаса просто удалите символ # и замените строку dummy на соответствующий путь к базе данных:
Гпава 4. Основные операции 105 # fbdbl и aliases.conf находится на сервере Windows: fbdbl = c:\Firebirdl5\sample\Employee.fdb # fbdb2 и aliases.conf находится на сервере Linux fbdb2 = /opt/databases/killergames.fdb При каждом запросе на соединение, содержащем путь, заданный в формате алиаса, сервер обращается к файлу aliases.conf. Вы можете редактировать aliases.conf и когда сервер выполняется. Изменения не будут влиять на текущие соединения, но новые соединения будут использовать новые или измененные алиасы. Соединение с использованием алиаса пути к базе данных Для соединений TCP/IP, использующих предыдущий пример aliases.conf, измененная строка соединения в вашем приложении будет иметь следующий формат: Имя_сервера: имя_алиаса Например, hotchicken:fbdb2 Для соединений Windows Named Pipes строка выглядит следующим образом: \\hotchicken\fbdb2 Для локального соединения просто используйте собственный алиас. Администрирование баз данных Множество прекрасных графических инструментов — свободно распространяемых и коммерческих— доступны для администрирования баз данных Firebird. Информа- цию о подобных предложениях см. в приложении 5. Обновляемый каталог поддер- живается на страницах Contributed Downloads на http://www.ibphoenix.com. Firebird поставляется с набором инструментов командной строки для сервера и адми- нистрирования баз данных. Обычно они работают одинаково в Linux/UNIX и в ко- мандной строке MS-DOS. Помните, что в Linux/UNIX команды, параметры и пере- ключатели являются чувствительными к регистру. В Windows — нет. Интерактивный инструмент запросов isql упомянут в этой главе и полностью доку- ментируется в главе 37. Другие инструменты командной строки описываются в сле- дующих разделах. fbmgr/ibmgr fbmgr/ibmgr является командой и интерфейсом командной строки для демона Супер- сервера в Linux для запуска и останова Суперсервера Firebird в Linux. Скрипт ко- мандной строки fbmgr (ibmgr в версии 1.0.x) предоставляет интерфейс для выполняв-
106 Часть I. Учебный лагерь мого модуля сервера fbmgr.bin (ibmgr.bin в версии 1.0.x). Детальное описание пред- ставлено в этой главе. instsvc.exe Это интерфейс командной строки сервиса Суперсервера на платформах Windows NT для инсталляции, запуска и останова Суперсервера Firebird в Windows. Детальное описание представлено в этой главе. gbak Эта утилита предназначена для резервного копирования и восстановления баз дан- ных. Поскольку она работает на уровне структур и форматов данных, gbak является единственной корректной утилитой для копирования. Она также обнаруживает раз- рушения базы данных, освобождает дисковое пространство, появившееся в результа- те удалений, разрешает незавершенные транзакции, позволяет разделять базы данных на несколько файлов. Она также используется для создания переносимой копии с целью восстановления вашей базы данных на другой аппаратной платформе или для обновления ODS (On-Disk Structure) вашей базы данных. /\ ВНИМАНИЕ! Никогда не используйте утилиты копирования файлов типа tar/gzip, / : \ WinZip, Microsoft Backup, средства копирования файловой системы или утилиты сторонних разработчиков для копирования и переноса баз данных, если сервер работает или у вас нет твердой уверенности, что база данных не повреждена. Базы данных, которые переносятся как копии файлов, будут содержать неубран- ный мусор. Подробности использования gbak см. в главе 38. gsec Этот инструмент поддержки списка пользователей и их паролей является интерфей- сом командной строки для базы данных security.fdb; он управляет записями пользо- вателей на сервере Firebird. Подробности использования gsec см. в главе 34. gfix Это набор общих вспомогательных утилит для изменения свойств баз данных, устра- нения небольших повреждений базы данных, выполнения различных задач чистки и т. д. Утилита также предоставляет средство администратора для отключения кон- кретных баз данных до завершения работы сервера. Она может быть использована вместе с утилитой gbak для восстановления некоторых типов нарушений в базе дан- ных (см. разд. "Ремонт базы данных" приложения 4). Подробности использования gfix см. в главе 39.
Гпава 4. Основные операции 107 gstat Этот инструмент получения статистики собирает и отображает статистические све- дения по индексам и данным базы данных. Подробную информацию об использова- нии gstat см. в разд. "Темы оптимизации” главы 18. fb_lock_print Эта утилита формирует статистические данные файла блокировок, который поддер- живается в Firebird для управления последовательностью изменений базы данных несколькими транзакциями. Она может быть полезным инструментом анализа про- блем взаимной блокировки. Подробности использования fb_lock_print см. в главе 40. Введение в isql Утилита командной строки isql (Interactive SQL) объединяет инструменты и техники использования SQL для поддержки объектов базы данных, управления транзакциями, отображения метаданных и обработки скриптов определения базы данных. Интер- фейс командной строки доступен на всех платформах. Настоящее краткое введение даст вам возможность начать работу по подключению к базе данных и созданию ва- шей первой базы данных. Запуск isql Есть несколько различных способов соединения с базой данных при использовании isql. Один способ — стартовать утилиту из командной строки в интерактивном ре- жиме. Для начала в окне командной строки перейдите к каталогу /bin корневого ка- талога инсталляции Firebird, где инсталлирована программа isql, и запустите isql сле- дующим образом. Для сервера POSIX: [chick@hotchicken]# ./isql <нажмите Return/Enter> Для сервера Windows: C:\Program Files\Firebird\Firebird_l_5\bin>isql < маршите Return/Entet> Утилита выведет следующее сообщение: Use CONNECT or CREATE DATABASE to specify a database (Используйте CONNECT или CREATE DATABASE для указания базы данных) Использование isql Соединившись с базой данных, вы можете выполнять запросы к ее данным и мета- данным, используя обычные операторы динамического SQL, а также специальное подмножество операторов, которые работают только в окружении isql.
108 Часть I. Учебный лагерь Оператор CONNECT Оператор connect является стандартным оператором SQL для соединения с базой данных. Здесь предполагается, что вы пока не изменили пароль у пользователя SYSDBA. Если вы это уже сделали (что рекомендуется), то используйте ваш пароль пользователя SYSDBA. Каждый из операторов командной строки в следующих примерах является одним оператором. Для соединения с сервером Linux/UNIX введите: SQL> CONNECT 'hotchicken:/opt/firebird/examples/employee.fdb' user 'sysdba' password 'masterkey'; Для соединения с сервером Windows: SQL> CONNECT ' WINSERVER: C: \Program Files\Firebird\Firebird_l_5\examples\employee. fdb' user 'SYSDBA' password 'masterkey'; ПРИМЕЧАНИЕ. В Классическом сервере под Linux и в Суперсервере под Win- -Ж dows существует возможность соединения с базой данных локально, например: connect '/opt/firebird/examples/employee.fdb' — Linux Классический сервер. CONNECT 'c:\Program Files\Firebird\Firebird_l_5\examples\employee.fdb’ — Windows Суперсервер. Убедитесь, что вы заканчиваете каждый оператор SQL символом точка с запятой (;). Если вы забудете это сделать, то увидите следующее в подсказке продолжения ути- литы isql: CON> Когда вы увидите подсказку продолжения, просто введите точку с запятой и нажмите клавишу <Enter/Return>. В этот момент isql проинформирует вас, что вы соединены: DATABASE ’hotchicken:/opt/firebird/examples/employee.fdb', User: sysdba SQL> Если сервер работает под Windows, вы увидите следующее: DATABASE "WINSERVER:C:\Program Files\Firebird\Firebird_l_5\examples\employee.fdb", User: sysdba SQL> Продолжим играть с базой данных employee.fdb. Вы можете использовать isql для запроса данных, получения информации о метаданных, создания объектов базы дан- ных, выполнения скриптов определения данных и многого другого. Чтобы вернуться к подсказке командной строки, введите SQL> QUIT;
Гпава 4. Основные операции 109 Создание базы данных с использованием isql Существует более одного способа создания базы данных с использованием isql. Здесь приведен один простой способ интерактивного создания базы данных — тем не ме- нее для работы с серьезной базой данных вы должны создавать и поддерживать объ- екты метаданных, используя скрипты определения данных (они также называются скриптами DDL, скриптами SQL, скриптами метаданных и скриптами схемы). Эта тема детально рассматривается в разд. "Скрипты схемы"главы 14. Если сейчас вы соединены с базой данных через утилиту isql, отсоединитесь с по- мощью следующей команды: SQL> QUIT; Затем заново стартуйте утилиту без соединения с базой данных. Для сервера Linux: [chick@hotchicken]# ./isql Use CONNECT or CREATE DATABASE to specify a database Для сервера Windows: C:\Program Files\Firebird\Firebird_l_5\bin>isql Use CONNECT or CREATE DATABASE to specify a database Оператор CREATE DATABASE Теперь вы можете создать вашу новую базу данных интерактивно. Предположим, что вы хотите создать базу данных test.fdb на сервере Windows и сохранить ее в каталоге data на диске D: SQL> CREATE DATABASE 'D:\data\test.fdb' user 'SYSDBA' password 'masterkey'; База данных будет создана, и через некоторое время снова появится подсказка. Те- перь вы соединены с новой базой данных и можете продолжать создавать в ней тестовые объекты. Для проверки того, что база данных действительно существует, введите запрос: SQL> SELECT * FROM RDB$RELATIONS; <нажмите Enter> Экран будет заполнен большим количеством данных! Этот запрос выбирает все строки из системной таблицы, в которой Firebird сохраняет метаданные для таблиц. "Пустая" база данных не является пустой — она содержит базу данных, которая бу- дет заполняться метаданными, как только вы начнете в ней создавать объекты. СОВЕТ. Почти все объекты метаданных в базах данных Firebird имеют иденти- фикаторы, начинающиеся с символов "RDB$".
110 Часть I. Учебный лагерь Чтобы вернуться назад в подсказку командной строки, введите: SQL> QUIT; Полную информацию по использованию isql см. в главе 37. Пора дальше Часть II рассказывает об архитектуре клиент-сервер. В главе 5 рассматривается тер- минология и различные модели реализации сетей клиент-сервер. В гпавах 6 и 7 более подробно рассматриваются серверы и клиенты Firebird соответственно.
и ЧАСТЬ II Клиент-сервер Глава 5. Введение в архитектуру клиент-сервер Глава 6. Сервер Firebird Глава 7. Клиенты Firebird

ГЛАВА 5 Введение в архитектуру клиент-сервер Обычно система клиент-сервер является парой программных модулей, разработан- ных для связи друг с другом через сеть посредством согласованного протокола. Кли- ентский модуль отправляет запросы через сеть слушающей программе сервера, а сер- вер отвечает на запросы. Например, клиент электронной почты направляет сообщение по сети на почтовый сервер с требованием к серверу перенаправить это сообщение по адресу какого-то сервера. Если запрос соответствует принятому протоколу и адрес назначения являет- ся действительным, сервер реагирует, перенаправляя сообщение и возвращая клиенту подтверждение. Ключевой принцип в том, что задача расщепляется — или распределяется — между двумя программными компонентами, которые выполняются независимо на двух фи- зически разделенных компьютерах. Эта модель даже не требует, чтобы компоненты выполнялись в совместимых операционных или файловых системах. Клиент элек- тронной почты должен быть почтовой клиентской программой, которая выполняется под Windows, Мас или любой другой операционной системой, а почтовый сервер обычно выполняется в системах UNIX или Linux. Клиентская и серверная программы способны успешно взаимодействовать, поскольку они были спроектированы функ- ционально совместимыми. В системе клиент-серверной базы данных модель идентична. На хост-машине в сети выполняется программа, которая управляет базами данных и клиентскими соедине- ниями — сервер базы данных. Он расположен на узле сети, который известен кли- ентским программам, выполняющимся в других узлах сети. Сервер слушает запросы из сети от клиентов, которые хотят соединиться с базой данных, а также от других клиентов, которые уже соединены с базами данных. В примере с электронной почтой протокол коммуникации имеет два уровня. Как и система электронной почты, система баз данных клиент-сервер использует стандарт- ный сетевой протокол и перекрывает его другими протоколами специального назна- чения. Для электронной почты перекрытие (overlay) будет POP3, IMAP и SMTP; для системы базы данных это протоколы соединения с базой данных, безопасности, пе- реноса данных и языка.
114 Часть И. Клиент-сервер Базы данных клиент-сервер в сравнении с файл-серверами Системы совместного доступа к файлам являются другим примером систем клиент- сервер. Файловые серверы и серверы файловых систем обслуживают запросы клиен- тов к файлам и файловым системам иногда весьма запутанными способами. Примеры этому— сервисы NFS, Windows Named Pipes и NetBEUI. Файловый сервер предос- тавляет клиентам доступ к файлам так, что клиентская машина может читать и пи- сать в памяти сервера, как если бы операции ввода/вывода проводились в ее собст- венной локальной системе памяти. Настольная система управления данными имеет недостаток — собственная внутрен- няя реализация управления запросами ввода/вывода, поступающими из сети, сама является клиентом файлового сервера. Когда сервер получает запросы на ввод/вывод от своих клиентов, он полагается на средства управления операционной системы для обеспечения центрального блокирования и организации очереди, необходимые для управления конфликтными запросами. Такие файл-серверные СУБД не являются клиент-серверными системами баз данных. Программное обеспечение клиента и СУБД— клиенты сервера совместного доступа к файлам. Хотя входной и часто выходной потоки являются до известной степени управляемыми программой СУБД, физическая целостность данных находится под управлением сервисов файловой системы. В базах данных клиент-сервер клиенты — даже если они расположены на той же машине, что и сервер, — никогда не обращаются к физическим данным, кроме как отправляя сообщения серверу с указанием того, что они хотят сделать. Сервер само- стоятельно обрабатывает эти сообщения и выполняет запросы, управляя обращения- ми к дискам и правами доступа. Сервер также выполняет все физические изменения метаданных и структур хранения данных, используя физическую структуру на диске (On-Disk Structure, ODS), которая независима от программ ввода/вывода файловой системы хоста. Характеристики СУБД клиент-сервер Масштабируемость Появление сравнительно недорогих компьютерных сетей между 1980-ми и 1990-ми го- дами вызвало увеличенный спрос на масштабируемые информационные системы с дружественным пользователю интерфейсом. Программное обеспечение электрон- ных таблиц и настольных баз данных, а также графический интерфейс дали пользо- вателям, не являющимся специалистами, понимание мощности использования ком- пьютеров. Когда совместное использование файлов в сетях и различного вида про- граммного обеспечения стало стандартной практикой на больших предприятиях, заказчики запросили большего. Настольные и основанные на локальных сетях (Local Area Network, LAN ) системы управления данными также стали использоваться и в весьма малых бизнесах. Сегодня практически немыслимо проектировать информаци-
Гпава 5. Введение в архитектуру клиент-сервер 115 онную систему предприятия для монолитной модели на мэйнфрейме с текстовым терминалом. Масштабируемость оценивается в двух размерностях: горизонтальной и вертикаль- ной. Горизонтальная масштабируемость — способность системы добавлять допол- нительных пользователей без воздействия на возможности программного обеспече- ния или используемые ресурсы. Вертикальная масштабируемость связана с тем, что будет сделано для переноса системы на более простые или более сложные платфор- мы и конфигурации аппаратных средств в ответ на изменение требований нагрузки и доступа. Диапазон меняется от нижнего уровня — например, сделать систему дос- тупной для пользователей мобильных устройств — до верхнего уровня, который не имеет концептуальных ограничений. Функциональная совместимость Архитектура клиент-сервер для систем баз данных развивалась как ответ на уязви- мость, низкий уровень нагрузки и ограничения по скорости модели базы данных со- вместного доступа к файлам в компьютерных сетях при потребности увеличения ко- личества пользователей. Острая необходимость в этом совпала с параллельной раз- работкой языка SQL. Оба направления отчасти были стратегиями нейтрализации зависимости аппаратного обеспечения мэйнфреймов и программного обеспечения, которая преобладала в 1980-х годах. Настоящая архитектура баз данных клиент- сервер является неоднородной и функционально совместимой (интероперабель- ной) — она не ограничивается одной платформой аппаратных средств или одной операционной системой. Эта модель позволяет клиентам и серверам независимо раз- мещаться в узлах сети на аппаратных средствах и в операционных системах, соответ- ствующих их функциям. Приложения клиентов могут одновременно связываться с множеством серверов, выполняющихся в различных операционных системах. Защита данных Огромный недостаток систем файл-серверных баз данных— незащищенность дан- ных от ошибок, повреждений и разрушения по причине их физической доступности при совместном использовании файлов клиентами и установления над ними прямого контроля со стороны человека. В модели базы данных клиент-сервер приложения клиентов никогда не работают с физическими данными. Когда клиентский запрос изменяет состояние данных, сервер подвергает запрос строгой проверке. Он отверга- ет запросы, которые не соответствуют внутренним правилам или правилам метадан- ных. Когда выполняется успешный запрос на запись данных, фактическое изменение состояния базы данных полностью выполняется кодом, находящимся в модуле сер- вера, а структура диска находится под контролем сервера. Распределение функций Модель клиент-сервер позволяет отдельным фрагментам работы системы быть эф- фективно распределенными между компонентами аппаратуры и программного обес-
116 Часть II. Клиент-сервер печения. Сервер базы данных заботится о хранении, управлении и поиске данных, а через хранимые процедуры, триггеры и другие вызываемые процессы он обеспечива- ет большое количество возможностей обработки данных системы. Процесс клиента является "острием" приложений, транслируя их запросы в структуры коммуникации, которые формируют протоколы для доступа к базам данных и к данным. Приложения являются динамическим уровнем в этой модели. Они обеспечивают по- тенциально бесконечное множество интерфейсов, через которые люди, машины и внешние программные процессы взаимодействуют с клиентским процессом. В этой части клиентский модуль представляется приложениям через понятный, предпочти- тельно стандартизованный, независимый от языка программирования интерфейс прикладного программирования (Application Programming Interface, API). В некоторых системах приложения могут действовать почти полностью как постав- щики информации и приемники ввода, виртуально делегируя все операции манипу- лирования данными серверу базы данных. Это является идеалом клиент-серверных систем, поскольку локализует задачи, интенсивно использующие центральный про- цессор, и позволяет приложениям использовать возможности рабочей станции для лучшей реализации интерфейса пользователя. На другом конце шкалы находятся системы, в которых из-за плохого проектирования или из-за отсутствия функциональной совместимости вся обработка данных вирту- ально производится на клиентских рабочих станциях. Для таких систем часто бывает характерным плохо выполненный интерфейс пользователя, задержки при синхрони- зации состояния базы данных и ненадежность взаимодействия с сетью. Между небесами и адом находятся хорошо выполненные системы баз данных кли- ент-сервер, которые прекрасно используют возможности обработки на серверах, со- храняя некоторые функции обработки данных на рабочих станциях, когда это оправ- дано сокращением сетевого трафика или повышением гибкости выполнения задач. Двухуровневая модель Рис. 5.1 иллюстрирует классическую двухуровневую модель клиент-сервер. Проме- жуточный уровень, который может присутствовать или отсутствовать, представляет собой драйвер, такой как ODBC, JDBC, PHP, или компонент доступа к данным, кото- рый интегрирован с программным кодом приложения. Возможны и другие уровни на клиентской стороне. Приложения также могут быть написаны с использованием прямого доступа к API без промежуточного уровня. SERVER CLIENT Рис. 5.1. Двухуровневая модель клиент-сервер
Гпава 5. Введение в архитектуру клиент-сервер 117 Многоуровневая модель Увеличение возможностей масштабирования и требования большей функциональной совместимости прйводят к модели с большим количеством уровней, как показано на рис. 5.2. Клиентский интерфейс перемещается в центр модели; он объединяется с одним или более уровнями сервера приложений. В этом центральном комплексе бу- дут расположены средства промежуточного уровня и сетевые модули. Уровень при- ложения становится некоторым видом суперклиента базы данных — иногда обслу- живая множество серверов баз данных — и сам становится Proxy-сервером (серве- ром-посредником) для запросов к базам данных от приложений. Он может быть размещен на том же аппаратном оборудовании, что и сервер базы данных, но также может выполняться и на своем оборудовании. APPLICATION SERVER APPLICATION CLIENT DATABASE SERVERS Рис. 5.2. Многоуровневая модель клиент-сервер Стандартизация Признанные стандарты функциональной совместимости аппаратного и программно- го обеспечения, и особенно языка запросов и описания метаданных, являются харак- терной чертой систем баз данных клиент-сервер. Развитие систем реляционных баз данных и консолидация стандартов SQL более двух десятилетий было и остается не- разделимым. Абстрактная природа хорошо спроектированных систем реляционных баз данных вместе с их относительной нейтральностью по поводу выбора языка при- ложения для "предварительной обработки" гарантируют, что реляционные СУБД продолжают занимать свое место в качестве предпочтительной архитектуры систем клиент-сервер. Тем не менее это не отменяет другие архитектуры. Хотя в настоящее время объекты систем баз данных продолжают оставаться тесно связанными с языками приложения, объектно-реляционные архитектуры становятся значительным посягательством на реляционные традиции. Самые последние стандарты SQL представляют некоторые положения по стандартизации объектно-реляционных методов и синтаксиса. Когда
118 Часть II. Клиент-сервер люди начинают требовать стандарты для технологий, обычно это хороший индика- тор того, что технология может быть востребованной в скором времени. Проектирование систем клиент-сервер Факт, что системы клиент-сервер должны быть спроектированы для использования в сетях. Для новичков часто бывает потрясением открытие того, что "молниеносно вы- полняемая" задача, которая работала в приложении под Paradox или Access, занимает весь день после конвертирования в клиент-серверную реляционную СУБД. "Что-то не так в Firebird, — говорят они. — Это не может быть код моего приложе- ния — потому что я не изменял ничего! Это не может быть результатом моего проек- тирования базы данных — потому что то же проектирование было безупречным мно- гие годы!" Знаменитые последние слова. Основа проектирования клиентов для настольных систем резко отличается от проек- тирования удаленных клиентов в архитектуре клиент-сервер. Обязательный интер- фейс просмотра в настольных системах, где отображаются "200 000 записей за один раз", создал крупную отрасль RAD разработки компонентов DBGrid, связанных с данными (data-aware components). Разработчику никогда не нужно думать о том, ка- кое количество человек в состоянии просмотреть 200 000 записей в день, пусть толь- ко одним взглядом! Эти компоненты в RAD, которые выполнили такую замечательную работу по пред- ставлению неограниченного объема данных в настольных системах в небольших контейнерах для произвольного просмотра, не являются дружественным интерфей- сом для удаленных клиентов. Если ранее характерная клиентская операция цикла ("начать с первой записи и для каждой записи повторить"), казалась идеальной для обработки данных, которые размещались в памяти как локальные таблицы, то теперь удаленные пользователи клиентских компьютеров требуют принести им голову раз- работчиков на блюде. Действительно, общим является то, что на проектирование базы данных наиболее сильно влияет восприятие клиентского интерфейса — "Мне нужна таблица, похожая на эту электронную таблицу!"— а не элегантная мудрость абстрактной модели данных. Когда интерфейс физически отделен от данных через уровень изоляции транзакции и через сеть с загруженным каналом, то требуется больше размышления. Для выполне- ния миграции с настольной системы требуется много больше, чем просто преобразо- вания данных. Максимальное преимущество критического пересмотра проектирова- ния для пользователей, целостности базы данных и эффективности выполнения будет весьма оправданной работой. Абстракция хранимых данных Даже в современных системах клиент-сервер можно найти слишком много плохо выполняющихся, подверженных ошибкам приложений, которые были "спроектиро- ваны" с использованием отчетов и электронных таблиц в качестве основы для проек-
Гпава 5. Введение в архитектуру клиент-сервер 119 тирования базы данных и пользовательского интерфейса. В итоге существует слиш- ком много общего при переходе от настольных баз данных к Firebird с множеством следующих недружественных для платформ клиент-сервер "возможностей". ♦ Распространенная избыточность структур, которая перешла от электронных таб- лиц к базам данных с одними и теми же элементами данных, повторяющихся во многих таблицах. ♦ Иерархические структуры первичных ключей (нужны во многих настольных сис- темах баз данных для реализации зависимостей), что нарушает уточненную мо- дель ограничений внешнего ключа в зрелых реляционных базах данных. ♦ Большие составные символьные ключи, составленные из столбцов реальных дан- ных. ♦ Недостатки нормализации, приводящие к большому количеству записей содер- жащих много повторяющихся групп и редко требуемой информации. ♦ Большое количество частично совпадающих друг с другом индексов, не являю- щихся необходимыми. Это не говорит о том, что старые настольные системы не были хорошими. Они впол- не успешно выполняли свои задачи. Технология клиент-сервер просто очень сильно отличается от "настольных" баз данных. Эта технология меняет масштаб управления информацией с "обратиться к файлу такому-то и выбрать" на "хранить, управлять и манипулировать". Она переносит приложение клиента с роли настольной системы, как главного действующего лица, на роль переносчика сообщений. Эффективные клиентские интерфейсы являются легкими и очень элегантными в том, как они вы- полняют желания пользователя и выдают ему нужную информацию. Отбрасывание идеологии электронных таблиц Обшей характеристикой приложений настольных баз данных является то, что они предоставляют интерфейс в виде таблицы: данные представляются в виде строк и столбцов с полосами прокрутки и другими элементами навигации для просмотра с первой строки до последней. Часто эти таблицы представляют собой визуальную структуру, которая в точности воспроизводит структуру метаданных исходных таб- лиц. Обычная ловушка— импортировать такие таблицы в систему клиент-сервер и считать, что задача миграции выполнена. Перенос таких старых баз данных в систему клиент-сервер обычно требует большего, чем создание программы конвертирования данных. Выполните ваше конвертирова- ние и будьте готовы рассматривать объекты вашей базы данных как основу для даль- нейшей работы. Запланируйте выполнить заново анализ и новое проектирование по- лученного абстрактного стиля базы данных в структуры, которые будут хорошо ра- ботать в новом окружении. В Firebird очень просто создать новые таблицы и записать в них данные. Для хранения используйте простые ключи; преобразовывайте структу- ры больших таблиц в группу связанных нормализованных отношений; переносите группы повторяющихся столбцов в отдельные таблицы; изменяйте структуры клю- чей, которые уменьшают уровень зависимостей; устраняйте дублирование данных ит. д.
120 Часть II. Клиент-сервер Если вы находитесь в недоумении по поводу нормализации и выделения главных признаков, посмотрите специальные книги или сайты. Начните работу с небольших моделей данных (подмножество из пяти или шести основных таблиц является иде- альным) вместо того, чтобы использовать базу данных из 200 таблиц, как если бы это было единой задачей, которую вы должны решить за один день. Таким образом, кон- вертирование становится упреждающей практикой самообучения, а быстрое решение трудных задач становится более интуитивным. Например, изучите хранимые про- цедуры и триггеры и проверьте, что вам известно о написании модулей конвертиро- вания данных. Таблицы для вывода Основной частью начального проектирования реляционной базы данных является представление всех любимых отчетов, электронных таблиц и наиболее используемых отображений в виде таблиц базы данных. Все это является выходными данными, ко- торые выбираются с помощью запросов и хранимых процедур. Пользовательский интерфейс Клиентские приложения в системе, где информационные сервисы предприятия име- ют серверную программу, которая является полнокровной СУБД с мощными воз- можностями обработки данных, не изменяют вводимые пользователем данные после выполнения синтаксического разбора их исходного вида и упаковывания кода в под- готовленные контейнеры — в структуры транспортных функций API. Циклам for над сотнями и тысячами строк в клиентском буфере набора данных нет места на клиент- ском компьютере в системах клиент-сервер. Разработчик приложения должен постоянно думать о стоимости лишней работы. Пе- редача огромного объема данных по сети для просмотра перегружает сеть и разоча- ровывает пользователя. Необходимо сосредоточиться на эффективных способах по- каза информации пользователям и на получении данных от них— инструкции и новые данные, которые люди хотят добавлять. Разработка пользовательского интер- фейса должна фокусироваться на быстрых и интуитивно понятных техниках получе- ния вводимых строк и быстрой передачи их на сервер для требуемой обработки. Разработчики систем клиент-сервер могут научиться многому, просматривая интер- фейсы различных сайтов, даже если их приложения не разрабатываются для работы в Интернете, потому что браузер является очень тонким клиентом. Короткие быстрые запросы держат пользователя в курсе о состоянии базы данных и уменьшают загрузку сети. Эффективные клиенты базы данных предоставляют дета- лизированный интерфейс поиска, а не браузер таблиц, и ограничивают набор строк в количестве не более чем 200. Модель хранения реляционных данных Реляционные базы данных используют надежные структуры данных с высоким уровнем абстракции для эффективного получения предсказуемых корректных ре- зультатов операций. Полный анализ сущностей и процессов вашей системы является основной деятельностью, таким образом вы приходите к логической модели, которая свободна от избыточности и представляет любое отношение.
Глава 5. Введение в архитектуру клиент-сервер 121 Первичный ключ В процессе логического анализа первичный ключ (primary key) устанавливается для всех сгруппированных данных. Логический первичный ключ помогает определить, какой элемент (или группа элементов) способен однозначно идентифицировать груп- пу связанных данных. Физическое проектирование таблиц будет отображать логиче- скую группировку и уникальность характеристик модели данных, хотя структуры таблиц и ключевые столбцы, созданные в черновом варианте, не часто в точности соответствуют модели. Например, в таблице Employee уникальный ключ состоит из полей имени и фамилии и др. Поскольку составной уникальный ключ в модели дан- ных включает элементы большого размера, которые могут приводить человека к ошибкам, в таблицу должен быть добавлен столбец в качестве суррогатного первич- ного ключа. Реляционные СУБД предполагают, что каждая строка в каждой таблице имеет уни- кальный столбец для однозначной идентификации строк, для проверки соответствия условиям поиска и для связи элементов данных и потоков1. Отношения Отношения в модели представлены ключами в таблицах. Теоретически каждое от- ношение в модели должно быть реализовано в виде пары связанных между собой ключей. Когда ключи связаны между собой через ограничение внешнего ключа, таб- лицы становятся связанными в сеть зависимостей, которые отображают взаимодей- ствие групп данных независимо от контекста. Основные правила логики сервера ссылаются на эти зависимости для поддержания ссылочной целостности базы дан- ных. Стандарты SQL формулируют правила, описывая как зависимости целостности должны работать. От разработчика реляционной СУБД зависит решение, каким обра- зом будут реализовываться и поддерживаться эти зависимости. В зависимости от реализации конкретного сервера могут быть технические причины для отмены некоторых ограничений ключа без формального объявления и реализа- ции таких ограничений альтернативными способами. Например, большинство реля- ционных СУБД обязательно требуют неуникальных индексов для элементов колонок внешнего ключа. При некоторых условиях распределения данных могут быть неже- лательны индексы для таких колонок, если может быть использован другой способ защиты целостности. Реляционная СУБД может реализовать отношения, которые не используют ключей. Например, она может получать наборы данных, основываясь на сравнении значений или на выражениях, включающих значения различных столбцов одной таблицы или столбцов из нескольких таблиц. Язык запросов SQL, структуры хранимых данных и логические умения разработчика приложения объединяются, чтобы уменьшить сетевой трафик в системе клиент- сервер и отобразить точные результаты пользовательских запросов. 1 Строго говоря, наличие уникального ключа в таблице не является обязательным для ре- ляционных баз данных, хотя его наличие весьма желательно. — Прим, перев.
122 Часть II. Клиент-сервер "Руки прочь" от доступа к данным Реляционные СУБД, разработанные для архитектуры клиент-сервер, не предостав- ляют пользователям прямой доступ к данным. Когда пользовательское приложение хочет выполнить операции над набором данных, оно сообщает клиентскому модулю, чего оно хочет, и клиентский модуль "договаривается" с сервером об удовлетворении этой потребности. Если запрос отвергается по какой-то причине, то именно клиент- ский модуль сообщает "плохую новость" приложению. Если приложение запрашивает набор данных для чтения, то клиентский модуль берет результат выполнения сервером операции и передает его приложению. Данные, ви- димые приложению, являются образом состояния исходных данных в базе данных на момент начала "переговоров" между клиентом и сервером. Этот образ, который ви- дят пользователи, отключен — или изолирован — от базы данных. "Момент изоля- ции" может не совпадать с тем моментом, когда сервер получает запрос. В окруже- нии клиент-сервер, где предполагается, что более чем один пользователь читает и пишет данные, каждый запрос имеет контекст. Множество пользователей и параллельность СУБД разработана для того, чтобы обеспечить работу множества пользователей с образами хранимых данных и, чтобы можно было использовать изменяющие запро- сы, которые могут влиять на работу других пользователей. В этой ситуации нужны способы управления параллельностью. Параллельность — это набор условий, в ко- торых предусмотрена ситуация, когда запросы двух или более пользователей изме- няют одну и ту же строку таблицы в одно и то же время (т. е. параллельно). Развитые СУБД, такие как Firebird, реализуют некую схему, при которой каждый запрос вы- полняется в параллельном контексте. Стандартный термин для такого параллельного контекста транзакция— не путайте с "бизнес-транзакциями", которые часто реали- зуются в приложениях баз данных. Транзакции Для бывших пользователей настольных баз данных транзакция является одной из наиболее запутанных абстракций в реляционных СУБД архитектуры клиент-сервер. В настольных базах данных и программах электронных таблиц это понятие исполь- зуется для гарантии того, что если пользователь щелкнет по кнопке Сохранить и кнопка станет серого цвета, то значит операция выполнена. Также факт, что как только до разработчика дойдет, что такое транзакция, они склоняются к отказу от "идеологии электронных таблиц", которая была у них все те годы, когда старая мо- дель баз данных казалась совершенной. В Firebird все общение между клиентом и сервером происходит в контексте транзак- ций. Даже чтение небольшого количества строк таблицы не может быть выполнено, если не запущена транзакция. Транзакция стартует, когда приложение запрашивает об этом клиента. С момента, когда транзакция начинается и пока она не закончит- ся — опять же по запросу приложения, — общение клиента и сервера открыто, при- ложение может просить клиента выполнять запросы. В этот период выполняются
Глава 5. Введение в архитектуру клиент-сервер 123 операции по изменению состояния базы данных, и осуществляется запись на диск. Однако они не изменяют состояния базы данных и являются обратимыми. Транзакции завершаются, когда приложение обращается к клиенту, чтобы он запро- сил сервер подтвердить (commit) всю работу, выполненную с момента старта тран- закции (даже если ничего не выполнялось, кроме чтения), или в случае ошибок отме- нить всю работу (rollback). Правило атомарности гласит: "Если одно из изменений оканчивается неудачей и требует отмены по причине невозможности подтверждения, то все ожидающие завершения изменения в этой транзакции также должны быть от- менены". Отмена включает любые изменения, которые были сделаны триггерами и хранимыми процедурами в процессе выполнения этой транзакции. СОВЕТ. Для разработчика приложения очень полезно делать видимой каждую единицу работы с базой данных в виде задачи или группы задач, которые были завершены в контексте транзакции. Условия выполнения транзакций могут быть сконфигурированы различными способами. Например, один уровень изоляции выдаст иной вид сообщения о конфликте, чем другой уровень. Большинство эф- фективных программ приложений знает об этих вариантах и учитывает их в такой мере, что контекст каждой транзакции распространяется до рамок рабочей об- ласти приложения, окружающей действительную физическую транзакцию. Транзакции настолько важны в системах клиент-сервер, что в настоящем руковод- стве им посвящены три главы. 25, 26 и 27. Пора дальше Далее в главе 6 мы рассмотрим, как работают различные модели сервера Firebird и системы управления масштабированием от однопользовательской автономной сис- темы до смешанных сетей с сотнями одновременно работающих пользователей.
ГЛАВА 6 Сервер Firebird Сервер Firebird — это программа, которая выполняется на узле хоста в сети, и слу- шает клиентов с порта коммуникации. Она обслуживает запросы множества клиен- тов к множеству баз данных. Суперсервер (Superserver) является многопоточным процессом, который запускает новый поток для каждого соединившегося клиента. В модели Классического сервера (Classic server) новый процесс запускается для каждого соединения. Серверы Firebird могут выполняться почти на любом оборудовании персональных компьютеров и принимать клиентские соединения от приложений, выполняющихся в совершенно других операционных системах. С одной стороны, небольшой и легкий дистрибутив сервера может быть установлен на устаревшем оборудовании, даже для старых процессоров Pentium в операционной системе Windows 95 или в минималь- ных системах Linux. С другой стороны, серверы Firebird выполняются на распреде- ленном оборудовании, управляя базами данных размерами в терабайты1. Конечно, нереально планировать информационную систему предприятия, выпол- няющуюся под Windows 95. Тем не менее проще простого запустить минимально сконфигурированный сервер, а по необходимости в дальнейшем масштабировать его как по вертикали, так и по горизонтали. Серверы Firebird существуют в двух вариан- тах — Суперсервер и Классический сервер для удовлетворения различных потребно- стей пользователя. Оба могут быть масштабированы как вверх, так и вниз для обра- ботки от самых простых до наиболее сложных конфигураций. Программное обеспечение сервера Firebird эффективно использует системные ресур- сы хост-компьютера. Суперсервер использует приблизительно 2 Мбайта памяти. Каждое клиентское соединение Суперсервера добавляет примерно 115 Кбайт к ис- пользуемой сервером памяти — меньше или больше в зависимости от характеристик приложений клиента и способа проектирования базы данных. Каждое соединение Классического сервера запускает собственный серверный процесс, требующий при- близительно 2 Мбайта памяти. Требуемая серверу кэш-память зависит от конфигурации и от выбранного варианта Firebird. Обычная конфигурация кэша для сети при одновременно работающих 20—40 пользователях, скорее всего, будет 16, 32 или 64 Мбайта для Суперсервера, 1 Трудно определить, какой возможен максимальный размер баз данных Firebird. Пользо- ватели сообщают о базах данных в 900 Гбайт, которые еще "продолжают расти".
Гпава 6. Сервер Firebird 125 использующего общий пул для всех соединений. Для каждого Классического сервера назначается статический кэш с размером по умолчанию 75 Кбайт. Серверы вер- сии 1.5 также будут использовать RAM для ускорения сортировки, если память дос- тупна. Требуемое дисковое пространство для минимальной инсталляции Firebird со- ставляет от 9 до 12 Мбайт в зависимости от платформы. Дополнительное дисковое пространство требуется для временного хранения данных в процессе выполнения операций, дополнительная память также требуется для кэширования страниц базы данных. Эта память конфигурируется в соответствии с запросами обработки данных и вероятного объема и типа обрабатываемых данных. Роль сервера Работа сервера включает: ♦ управление хранением данных базы данных и выделение дискового пространства; ♦ управление всеми транзакциями, запущенными клиентами, гарантирование, что каждая получит и сохранит непротиворечивый образ постоянно хранимых дан- ных, требуемых клиенту, ♦ управление подтверждением транзакций, данными и сборкой мусора; ♦ поддержку блокировок и статистики для каждой базы данных; ♦ обработку запросов на добавление, изменение или удаление строк и поддержку текущих и устаревших версий записей; ♦ поддержку метаданных каждой базы данных и обслуживание запросов клиентов по созданию новых баз данных и объектов базы данных, изменение структур, проверку и компиляцию хранимых процедур и триггеров; ♦ обслуживание клиентских запросов на получение результирующих данных и вы- полнение хранимых процедур; ♦ маршрутизацию сообщений для клиентов; ♦ поддержку кэшированных данных для хранения часто используемых наборов данных и индексов; ♦ отдельную поддержку безопасности баз данных для проверки доступа пользова- телей. Платформы для операционных систем Платформы сервера Firebird включают следующие операционные системы, но не ог- раничиваются только ими. ♦ Linux, FreeBSD и другие варианты ОС UNIX. ♦ Платформы Microsoft Windows, поддерживающие сервисы: NT 4, Windows 2000 (сервер или рабочая станция), ХР Professional и Server 2003. Операционные сис- темы Windows 9х, ME и ХР Ноте могут быть использованы как сервер, который прослушивает порты протокола TCP, но не Named Pipes (NetBEUI).
126 Часть II. Клиент-сервер ♦ Mac OS X (Darwin). ♦ Sun Solaris SPARC и Intel. ♦ HP-UX. Примеры топологий Сервер Firebird существует в нескольких "моделях", которые обеспечивают множест- во режимов масштабирования — от однопользовательского варианта, независимой настольной системы, до мощного сервера. Двухуровневая архитектура клиент-сервер На рис. 6.1 изображена гибкая система, где множество серверов Firebird выполняют- ся на платформах с различными операционными и файловыми системами. Здесь при- сутствует смесь рабочих станций, на которых выполняются удаленные клиенты, каждый на своей платформе. Здесь же существуют шлюзы для других сетей. Сервер Windows обслуживает повседневную обработку деловых данных и располагает большим объемом дискового пространства. Для клиентов Windows возможно обще- ние с сервером Windows с использованием протокола Named Pipes — обычно назы- ваемым NetBEUI, — хотя такой протокол должен быть заменен по возможности на TCP/IP. Сервер Linux может обслуживать системы сетевой защиты, шлюзы, вспомогательные базы данных и другие системы клиент-сервер, включая электронную почту, Интернет и сервисы файлов, такие как NFS и Samba. Linux TCP/IP Суперсервер на Windows Приложения Windows TCP/IP TCP/ P Linux TCP/IP TCP/IP SYSDBA Клиент WAN Классический сервер под Linux Локальная консоль и удаленное Обслуживает репликацию и вспомогательные БД Удаленное администрирование и другие приложения Рис. 6.1. Двухуровневая топология клиент-сервер в Firebird Сервер предприятия Неоднородная сеть обслуживания баз данных является общей средой для Firebird. В небольших сетях с единственным сервером, где местный администратор может не входить в штат сотрудников, существует тенденция переносить сервер базы данных
Глава 6. Сервер Firebird 127 с одного узкоспециализированного хоста, работающего под Windows, на дешевую машину Linux с хорошей оперативной памятью и быстрым доступом к данным. Под- держка недорогая, что делает возможным передачу большинства административных функций другим организациям. Системы, подобные этой, могут расти без каких-либо сложностей. Однопользовательская модель Все серверы Firebird могут работать с локальными клиентами. Протоколы соедине- ния и режимы меняются в соответствии с выбранной вами моделью сервера. Одно- пользовательские инсталляции делятся на две категории: ♦ Независимый сервер. В этой модели сервер инсталлируется и запускается на ма- шине. Локальные соединения осуществляются с использованием протоколов в стиле сетевых, используя обычные клиентские библиотеки. ♦ Встраиваемый сервер. Никакой сервер не инсталлируется. Сервер находится в DLL, похожей на клиентскую библиотеку, и загружается приложением. Приложе- ние вместе с DLL сервера выполняется как единственный процесс на одно соеди- нение. Когда приложение завершается, то фактически завершается и работа сер- вера. Клиент-сервер В автономной модели клиент-сервер локализованное клиентское подключение к вы- полняющемуся серверу выполняется с использованием локального протокола. Сер- вер может прослушивать подключения от удаленных клиентов во время подключен- ного локального клиента. Рис. 6.2 иллюстрирует этот режим. Клиенты Windows Суперсервер и Классический Рис. 6.2. Автономные серверы
128 Часть II. Клиент-сервер Первый пример показывает модель локального подключения. В Firebird 1.5 и ниже подсистема IPSERVER моделирует сетевое подключение в том же блоке пространст- ва общения между процессами. В версии 1.5 и выше вместо локального протокола используется более быстрая и надежная подсистема XNET. Функциональный эквива- лент локального подключения используется Классическим сервером в POSIX. В двух других примерах в Windows, Linux или на любой другой поддерживаемой платформе Суперсервер использует локальную "заглушку" (loopback) протокола TCP/IP. Это обычное подключение TCP/IP к специальному IP-адресу 127.0.0.1, кото- рый большинство подсистем TCP/IP инсталлирует по умолчанию для локального хоста (localhost). В Linux Классический сервер версии 1.5 может применяться в этом режиме при использовании клиентской библиотеки libfbclient.so. Встраиваемый сервер Встраиваемые серверы поддерживаются на платформах Windows и Linux/UNIX, хотя реализация моделей различна. Под Windows библиотека встроенного сервера, кото- рый выполняется как единый процесс, называется fbembed.dll. В Linux/UNIX это стандартный режим локального подключения для Классического сервера. Библиоте- ка libfbclient.so запускает один процесс Классического сервера (fbjnetserver или ib inet server) и напрямую соединяется с базой данных. Процесс не является исклю- чительным — удаленные клиенты могут одновременно соединяться с базой данных, используя fbclient.so, другую библиотеку libfbclient.so или fbembed.dll. Более подробно встроенные серверы обсуждаются в главе 7. Серверы Firebird в среде DTP Детальное обсуждение среды распределенной обработки транзакций (Distributed Transaction Processing, DTP) не является целью данной книги. Достаточно сказать, что Суперсервер или Классический сервер Firebird хорошо подходят к различным сценариям DTP. Open Group, определившая стандарт Х/Open для DTP, предоставила три программ- ных компонента для системы DTP. Спецификация ХА определяет интерфейс между менеджером транзакций и менеджером ресурсов (Resource Manager, RM). Система имеет один RM-модуль для каждого сервера; требуется каждый RM для регистрации менеджера транзакций. На рис. 6.3 показано, как сервер Firebird может быть представлен в ХА-совместимой среде DTP. Модуль сервера приложения базы данных представляет собой мост меж- ду приложениями пользователя высокого уровня и RM, инкапсулирующим соедине- ние ХА. RM выполняет роль клиента связи с сервером базы данных для доступа к данным. Инкапсуляция соединения ХА дает возможность разработчику приложения создавать и выполнять операторы SQL в RM. Разграничение транзакций, которое требуется средствам двухфазного подтверждения для всех серверов, регулируется глобальным
Гпава 6. Сервер Firebird 129 монитором обработки транзакций (Transaction Processing Monitor, TPM). Транзакции с несколькими базами данных, находящиеся под управлением менеджера транзакций, выполняются с помощью процесса двухфазного подтверждения. В первой фазе тран- закции подготавливаются для подтверждения; во второй фазе транзакции либо пол- ностью подтверждаются, либо откатываются2. ТРМ проинформирует вызывающий модуль, если транзакция не будет по разным причинам завершена. Рис. 6.3. Firebird в распределенной среде выполнения транзакций ТРМ согласовывает распределенные транзакции в системах множества баз данных, так что одна транзакция может использовать один или более процессов и изменять одну или более баз данных. Монитор хранит информацию обо всех доступных и включенных в транзакции RM. Среда поддерживает множество баз данных для одного сервера и множество серве- ров, которые не обязательно все должны быть серверами Firebird. Версия 1.5 и выше Firebird не поддерживает использование одной базы данных несколькими серверами или обслуживание базы данных, находящейся вне компьютера, где установлен сервер Firebird. Основы сервера транзакций Сервер транзакций Microsoft (Microsoft Transaction Server, MTS) с COM+ является одним из таких сценариев. MTS/COM+ обеспечивает среду для объединенных в пул процессов, которая осуществляет использование и управление компонентами бизнес- логики, включая контроль системы, безопасность и мониторинг выполнения. Одной из наиболее значимых возможностей является декларативное управление транзак- циями. Транзакции, инициированные в MTS/COM+, управляются координатором * 5 2 О двухфазном подтверждении транзакций см. в главе 25. 5 Зак. 420
130 Часть II. Клиент-сервер распределенных транзакций (Microsoft Distributed Transaction Coordinator, DTC), ме- неджером ресурсов ХА. Родной интерфейс Firebird требует провайдера ODBC или OLE DB, который поддерживает как двухфазное подтверждение транзакций Firebird, так и контекст вызова MTS/COM+3. Терминальные серверы Firebird успешно используется в средах MTS и IBM Citrix. Во всех случаях использу- ется протокол TCP/IP для соединений по сетевым IP-адресам. /А ВНИМАНИЕ! Весьма нежелательно инсталлировать на одном и том же узле / ! \ терминальный сервер и сервер базы данных. Тем не менее в ситуациях, когда сервер приложения выполняется на том же узле, что и сервер базы данных, со- единение должно осуществляться с использованием IP-адреса этого узла или по имени locahlost. Базы данных Каждая база данных располагается в одном или более файлах, которые динамически увеличиваются при возникновении такой необходимости. Файлы базы данных долж- ны храниться на дисках, находящихся под физическим управлением машины, где располагается сервер. Только серверный процесс может выполнять прямые операции ввода/вывода для файлов базы данных. Файл базы данных Firebird состоит из блоков, называемых страницами. Размер стра- ницы базы данных может быть 1, 2, 4, 8 или 16 Кбайт; он устанавливается во время создания базы данных. Размер страницы может быть указан при создании, и изменен только во время восстановления базы данных из резервной копии при задании нового значения. Различные базы данных на одном и том же сервере могут иметь различные размеры страниц. Сервер поддерживает множество различных типов страниц в каждой базе данных — страницы данных, различные уровни индексных страниц, страницы BLOB, служеб- ные страницы для различной информации и т. д. Сервер располагает страницы в по- рядке, известном только ему. В отличие от файловых СУБД Firebird хранит таблицы не в виде физических строк и столбцов, а в непрерывном потоке на страницах. Когда страница заполняется почти полностью, и нужно записать еще строки, сервер выде- ляет новую страницу. Страницы одной таблицы не хранятся в виде непрерывной по- следовательности. Фактически страницы, содержащие данные одной таблицы, могут располагаться в нескольких файлах на разных дисках. 3 Рекомендуется свободно распространяемый драйвер IBProvider Дмитрия Коваленко. Подробности см. на http://www.ibprovider.com/rus/index.html.
Гпава 6. Сервер Firebird 131 Программирование на стороне сервера Среди мощных средств Firebird по динамическому программированию приложений клиент-сервер существует возможность компилировать на сервере исходные коды в двоичную форму для использования во время выполнения. Такие процедуры и функ- ции выполняются полностью на сервере, возвращая клиентскому приложению при необходимости значения или наборы данных. Firebird предоставляет два стиля про- граммирования на стороне сервера: хранимые процедуры и триггеры. В дополнение к этому внешние функции (или функции, определенные пользователем — User-Defined Functions, UDF) могут быть написаны на языке высокого уровня и стать доступными серверу для использования в выражениях SQL. Хранимые процедуры Язык процедур Firebird (PSQL) реализует расширения его языка SQL, предоставляя логику условий, структуры управления потоками выполнения, обработку исключе- ний (как встроенных, так и определенных пользователем), локальные переменные, механизм событий и возможность получать входные аргументы почти всех типов данных, существующих в Firebird. Он реализует мощную структуру управления по- током для обработки курсоров, что позволяет помещать наборы данных напрямую в память клиента без необходимости создания временных таблиц. Такие процедуры вызываются клиентом оператором select; разработчикам они известны как селектив- ные процедуры. Хранимые процедуры могут включать другие хранимые процедуры и могут быть рекурсивными. Все выполнение хранимой процедуры, включая выбор набора данных из процедур и внутренние вызовы других процедур, находится под управлением од- ной транзакции, которая вызвала процедуру. Соответственно, вся работа, выполнен- ная при вызове хранимой процедуры, может быть отменена при откате клиентом этой транзакции. Триггеры Триггеры являются особыми процедурами, созданными для определенных таблиц с целью автоматического выполнения в процессе завершения добавлений, изменений или удалений на сервере. Любая таблица может иметь произвольное количество триггеров, которые будут выполняться до или после добавлений, изменений или уда- лений. Порядок выполнения определяется параметром позиции в объявлении тригге- ра. Триггеры имеют некоторые расширения языка, недоступные для хранимых про- цедур или в динамическом SQL. Например, контекстные переменные old и new, при использовании которых в качестве префикса к имени столбца можно получить дос- туп к существующему (старому, old) и требуемому (новому, new) значению столбца. Триггеры могут вызывать хранимые процедуры, но не другие триггеры. Работа, выполненная триггерами, будет отменена, если транзакция, явившаяся при- чиной вызова триггера, будет отменена.
132 Часть II. Клиент-сервер Функции, определенные пользователем Для сохранения своего малого объема Firebird поставляется с весьма скромным арсе- налом встроенных (родных) функций трансформации данных. Пользователи могут писать свои собственные функции на известном им языке, таком как C/C++, Pascal или Object Pascal, для получения аргументов и возвращения единственного результа- та. Как только внешняя функция (UDF) будет определена в базе данных, она тут же станет доступной как допустимая функция SQL для приложений, хранимых про- цедур и триггеров. Firebird поставляет две готовые к использованию библиотеки UDF: ibudf, доступ- ную как для Windows, так и для Linux, и fbudf, доступную в настоящий момент для Windows и Linux в версии 1.5 и доступную только для Windows в версии 1.0.x. Firebird отыскивает UDF в библиотеках, находящихся в каталоге Zudf каталога ин- сталляции или в других каталогах, указанных в параметре udfAccess (версия 1.5) или externai_function_directory (версия 1.0.x) в файле конфигурации Firebird. Приложения, работающие с несколькими базами данных В отличие от многих реляционных баз данных приложения Firebird могут быть одно- временно соединены более чем с одной базой данных. Клиент Firebird может откры- вать и иметь доступ к любому количеству баз данных в одно и то же время. Таблицы из разных баз данных не могут быть объединены для получения связанного набора данных, но для комбинирования информации могут использоваться курсоры. Если требуется согласованность между базами данных, Firebird может управлять вы- ходными наборами данных, выполняя запросы к нескольким базам данных в контек- сте одной транзакции. Firebird обеспечивает автоматическое двухфазное подтвер- ждение транзакции (2РС) при изменениях в данных, чтобы гарантировать, что изме- нения не будут применены в одной базе данных, если изменения в другой базе данных в контексте той же транзакции были отменены или потеряны из-за ошибок в сети. Безопасность сервера Для управления доступом пользователей к серверу Firebird использует базу данных безопасности security.fdb (isc4.gdb в версии 1.0.x). Во время инсталляции эта база данных содержит одного пользователя — SYSDBA. ♦ В инсталляции Windows пароль пользователя SYSDBA masterkey. Настоятельно рекомендуется немедленно после инсталляции запустить программу gsec.exe (в каталоге инсталляции, подкаталоге /bin) и изменить этот пароль. Это один из наиболее известных паролей в мире баз данных! ♦ Инсталляторы RPM версии 1.5 для Linux генерируют случайный пароль для SYSDBA и заменяют в базе данных старый пароль masterkey. Этот пароль сохра-
Гпава 6. Сервер Firebird 133 няется в корневом каталоге инсталляции в текстовом файле с именем firebird.PASSWORD. Если вы собираетесь использовать данный пароль, то удали- те этот файл. Пользователь SYSDBA имеет полные привилегии ко всем базам данных на сервере; в текущей модели безопасности он не может быть изменен. Пользователь root в Linux/UNIX получает привилегии SYSDBA автоматически. Владелец базы данных (пользователь, создавший базу данных) имеет полные права на эту базу данных. Для всех других пользователей доступ к объектам базы данных возможен только через предоставление им привилегий SQL. Безопасность базы данных Все пользователи, за исключением тех, кто имеет полные привилегии, должны полу- чить права к каждому объекту, к которому они должны иметь доступ. SQL-оператор grant используется для назначения привилегий. Firebird поддерживает роли SQL. Вначале роль должна быть создана с использовани- ем оператора create role и подтверждена (commit). Группа привилегий может быть назначена роли, а затем роль может быть назначена пользователю. Для использова- ния этих привилегий пользователь должен при соединении с базой данных указывать и имя пользователя, и имя роли. Более подробную информацию о безопасности баз данных см. в главе 35. Пора дальше В главе 7 мы более внимательно рассмотрим клиентскую сторону в архитектуре кли- ент-сервер Firebird: библиотеку функций, которая предоставляет приложениям такие уровни системы, как средства связи и язык SQL. Если вам нужна помощь в инсталля- ции удаленного клиента, перейдите к последнему разделу этой главы.
ГЛАВА 7 Клиенты Firebird Клиенту на удаленной рабочей станции требуется клиентская библиотека и прило- жение (программа), которое может взаимодействовать с интерфейсом прикладного программирования (Application Programming Interface, API), объявленным в этой библиотеке. Клиентская библиотека предоставляет протокол соединения и транспортный уро- вень, которые ваше клиентское приложение использует для связи с сервером. Стан- дартная библиотека для клиентов Windows — это Windows DLL. Для клиентов POSIX это совместно используемый объект (библиотека SO). Размер стандартной клиентской библиотеки приблизительно 350 Кбайт. Некоторые уровни доступа, как, например провайдер Firebird .NET и драйверы JayBird Java, не требуют наличия клиентской библиотеки и напрямую реализуют се- тевой протокол. Еще один режим существует во встраиваемом сервере — библиоте- ка, которая объединяет клиентский и серверный экземпляры для использования од- ним пользователем. Клиентская рабочая станция также может иметь копию текущего файла firebird.msg или его локализованную версию для того, чтобы отображались корректные сообще- ния сервера. Обычно вы будете инсталлировать копию клиентской библиотеки на сервере для ис- пользования с некоторыми утилитами командной строки Firebird и/или для различ- ных программ управления, выполняющихся на сервере, которые вы могли бы использовать. Тем не менее многие из этих утилит могут запускаться удаленно. Ад- министратор системы может управлять некоторыми из основных сервисов, предос- тавляемыми этими утилитами для доступа к ним, через интерфейс управления услу- гами на хосте. Что такое клиент Firebird? Клиент Firebird — это приложение, обычно написанное на языке высокого уровня, которое предоставляет конечному пользователю доступ к средствам и инструментам системы управления базами данных Firebird и к данным, хранимым в базах данных. Интерактивная утилита isql и другие утилиты командной строки в вашем каталоге Firebird /bin являются примерами клиентских приложений.
Гпава 7. Клиенты Firebird 135 Клиенты Firebird обычно располагаются на удаленных рабочих станциях и через сеть соединяются с сервером Firebird, выполняющимся на хост-машине. Firebird также поддерживает автономную модель, позволяющую клиентским приложениям, клиент- ской библиотеке Firebird и серверу Firebird выполняться на одном физическом уст- ройстве. Клиентские приложения могут и вовсе не взаимодействовать с конечными пользова- телями. Клиентами могут быть демоны, скрипты и сервисы. Firebird был спроектирован для неоднородных сетей. Клиенты, выполняющиеся в одной операционной системе, могут иметь доступ к серверу на другой платформе операционной системы. Общий случай— наличие рабочих станций под Windows (98 или ME) и Linux, одновременно имеющих доступ к корпоративному серверу, вы- полняющемуся под WindowsNT или Windows 2000, либо под какой-нибудь разно- видностью UNIX или Linux. В модели клиент-сервер приложения никогда не обращаются к базе данных напря- мую. Любой процесс приложения общается с сервером через клиентскую библиотеку Firebird, копия которой должна быть инсталлирована на каждой клиентской рабочей станции. Клиентская библиотека Firebird предоставляет API, через который програм- мы выполняют обращения к функциям для поиска, сохранения и манипулирования данными и метаданными. Обычно другие уровни также вовлечены в этот интерфейс между программой приложения и клиентом Firebird, который использует обычные или специфичные для языка приложения механизмы для заполнения базы данных или вызова функций API. Для разработок Java постоянно поддерживаемые в Firebird драйверы включают JayBird JDBC/JCA-совместимый драйвер Java для гибкого, независимого от плат- формы интерфейса приложения между множеством открытых и коммерческих сис- тем разработки Java и базами данных Firebird. Открытые и сторонние интерфейсные компоненты и драйверы доступны для множества других платформ разработки, включая Borland Delphi, Kylix и C++ Builder, коммерческие и открытые варианты C++, Python, PHP и DBL'.Perl. Для приложений .NET провайдер Firebird .NET посто- янно совершенствуется. Контактные адреса и другую информацию см. в приложе- нии 3. Клиентская библиотека Firebird Клиентская библиотека Firebird поставляется во множестве вариантов, которые обра- батывают идентичные API-функции приложений для версии сервера, к которому они обращаются. В табл. 7.1 в конце этой главы представлены имена и размещение этих библиотек. В большинстве случаев клиентская библиотека использует клиентские сетевые про- токолы операционной системы для связи с одним или более серверами Firebird, реа- лизуя специальный интерфейс для архитектуры клиент-сервер Firebird на уровне приложения поверх сетевого протокола.
136 Часть II. Клиент-сервер Не смешивайте версии клиента и сервера Важным является соответствие версии клиентской библиотеки и версии сервера. Используйте клиент версии 1.0.x с сервером версии 1.0.x и клиент версии 1.5 с сер- вером версии 1.5. Помните также, что клиент версии 1.5 может быть инсталлирован в тот же каталог, что и клиент 1.0, а может быть поставлен в отдельный каталог. При переустановке новой версии изучите файл README и документацию по инсталляции (размещается в корневом каталоге инсталляции Firebird и в подкаталоге /doc на сервере), чтобы выяснить, какая информация данной книги устарела. Все клиентские приложения и промежуточное программное обеспечение должны использовать API для доступа к базам данных Firebird. API Firebird имеет обратную совместимость с API InterBase. Документ "InterBase API Guide" (Руководство no API InterBase), доступный в Borland, содержит полное описание API для разработки вы- сокопроизводительных приложений. Более поздние возможности документированы в официальных замечаниях по релизу Firebird и в ограниченном объеме в заголовоч- ных файлах, поставляемых с Firebird1. Разработка приложений Когда вы создали и заполнили данными базу данных, ее содержимое может быть доступно через клиентское приложение. Некоторые клиентские приложения — такие как инструмент Firebird isql и ряд прекрасных коммерческих и открытых инструмен- тов администратора базы данных — предоставляют возможности интерактивной вы- борки данных и создания метаданных. Любое приложение, разработанное в качестве интерфейса пользователя к одной или более базам данных Firebird, будет использовать язык запросов SQL для определения сохраняемых наборов данных и для передачи серверу операторов SQL, запрашиваю- щих операции с данными и метаданными. Firebird реализует множество операторов SQL, синтаксис которых имеет высокий уровень соответствия с признанным стандартом SQL-92. API Firebird предоставляет полные структуры для компоновки операторов SQL и связанных параметров и для получения приложениями результатов. Динамические приложения клиент-сервер Во время выполнения программы приложениям часто бывают нужны операторы SQL, которые создаются или изменяются приложениями или вводятся пользователя- ми. Приложения обычно предоставляют пользователю списки выбора, извлекаемые из таблиц базы данных, которые используют пользователи для указания критериев поиска желаемых ими данных, и операции, которые они хотят выполнить. Программа конструирует запросы на основании выборов пользователя и управляет найденными данными. 1 Список ссылок на документальные источники см. в приложении 12.
Гпава 7. Клиенты Firebird 137 Клиентские приложения используют динамический SQL (DSQL) для создания запро- сов во время выполнения. Клиент Firebird объявляет API как библиотеку функций, которые передают сложные структуры записей, формируют протокол на уровне дан- ных для связи между приложением и сервером. ПРИМЕЧАНИЕ. Программирование с использованием API — большая тема. Ее описание выходит за рамки данной книги. Но поскольку динамический SQL сам не \ X предоставляет некоторых функций, здесь описываются отдельные функции API, чтобы помочь вам понять, как драйверы и интерфейсные компоненты делают их доступными в соответствующих средах проектирования. Ядро API Firebird Программирование с использованием API необходимо при написании драйверов для создания сценариев в таких языках, как РНР и Python, и при разработке объектно- ориентированных классов доступа к данным для объектно-ориентированных языков типа Java, C++ и Object Pascal. Приложения также могут быть написаны с прямыми вызовами функций API без посредничества драйверов. Эти приложения "прямого API" могут быть мощными, гибкими, быстровыполняемыми, небольшого размера и с прекрасным управлением распределением памяти. Функциональные категории ядра API Функции API (все их имена начинаются с isc_) делятся на восемь категорий. ♦ Соединение с базой данных и отсоединение от базы данных — например, isc_attach_database(). ♦ Запуск, подготовка, подтверждение и откат транзакций — например, isc_start_transaction(). ♦ Вызовы выполнения операторов — например, isc_dsql_describe (). ♦ Вызовы BLOB — например, isc_biob_info (). ♦ Вызовы ДЛЯ массивов — например, isc_array_get_slice (). ♦ Безопасность базы данных — например, isc_attach_database (). ♦ Информационные вызовы — например, isc_database_info (). ♦ Преобразования даты и целых — например, isc_encode_date (). Более подробную информацию по программированию прямого API см. в "API Guide" документации по InterBase 6, опубликованной фирмой Borland. Интерфейсы приложений при использовании API Приложения, которые используют общие интерфейсы типа ODBC или JDBC, осно- ваны на операторах DSQL, располагающихся ниже пользовательских интерфейсов, таких как построители запросов и др.
138 Часть II. Клиент-сервер В связи с увеличением числа инструментов быстрой разработки приложений (Rapid Application Development, RAD) за последнее десятилетие инкапсуляция функций API в "обертку" классов и компонентов разработчикам под Firebird предоставлены разно- образные привлекательные средства создания приложений. Объектно-ориентированные классы Объектно-ориентированные классы и компоненты доступа к данным инкапсулируют вызовы функций и структуры данных API. Все они имеют свойства и методы, кото- рые анализируют и выполняют синтаксический разбор запрашиваемых операторов, а также управляют возвращаемыми результатами. Богатые классы включают методы и свойства, которые поддерживают специальные возможности Firebird, такие как рас- пределенные транзакции, обработка массивов, параметризованные операторы. Боль- шинство наборов компонентов реализует, по меньшей мере, один класс компонента- контейнера для буферизации одной или более строк, возвращаемых клиенту в виде результирующего набора. Некоторые реализуют продвинутые техники, такие как прокручивание курсоров, "живые данные", обратные вызовы и управление транзак- циями. Драйвер JayBird Туре 4 ("родной") JDBC поставляет интерфейс, предназначенный для независимой от платформы разработки на Java с использованием Firebird. Неко- торые наборы компонентов устанавливаются в качестве интерфейса разработчиками, использующими Delphi, Kylix и C++ Builder для написания клиентов баз данных Firebird. Двумя лучшими наборами компонентов являются IB Objects и FIBPlus. Дос- тупны некоторые другие наборы компонентов, осуществляющие минимальную под- держку возможностей Firebird. Более подробную информацию см. в приложении 3. Встроенные приложения Firebird Firebird предоставляет две различные встраиваемые модели: приложения встроенно- го SQL (Embedded SQL) и встраиваемые серверы. Приложения встроенного SQL В этой модели программа приложения включает в себя интерфейс клиент-сервер и уровень приложения конечного пользователя— все в одном исполняемом модуле. Операторы SQL находятся непосредственно в исходном коде программы, написан- ной на С, C++ или другом языке программирования. Затем исходный код приложе- ния обрабатывается препроцессором gpre, который отыскивает блоки кода, содержа- щие операторы SQL. Он подставляет макровызовы функций, которые функционально эквивалентны функциям динамической библиотеки API. Когда работа препроцессора завершается, все выполненные преобразования операторов SQL компилируются вме- сте с приложением. Такие операторы перед компилированием называются статиче- ским SQL. В таком стиле приложений доступно специальное расширенное подмножество ис- ходных команд, похожих на SQL. Встроенный SQL (Embedded SQL, ESQL) предос-
Гпава 7. Клиенты Firebird 139 тавляет программисту простой язык высокого уровня в виде "черного ящика", в то время как gpre выполняет всю работу по преобразованию сложных языковых струк- тур в эквивалентные вызовы API. Эти статические операторы дают незначительное увеличение скорости по сравнению с динамическим SQL, поскольку не требуют до- полнительных затрат на синтаксический разбор и интерпретацию операторов SQL во время выполнения. Язык и техники ESQL подробно не обсуждаются в данной книге. Документ "InterBase Embedded SQL Guide" ("Руководство по встроенному SQL InterBase", EmbedSQL.pdf), доступный в Borland, содержит описания, необходимые для разра- ботки встроенных приложений Firebird. Приложения встраиваемого сервера В модели встраиваемого сервера не существует преобразований операторов SQL. Клиент и сервер объединяются в одну компактную динамическую библиотеку для получения независимого приложения. Приложение загружает библиотеку во время запуска точно так же, как обычное приложение Firebird будет загружать клиентскую библиотеку, функции API вызываются во время выполнения. При этом нет необхо- димости инсталлировать внешний сервер, потому что такой клиент внутренне связы- вается со своим собственным экземпляром серверного процесса Firebird. Когда при- ложение завершается, оно выгружает встраиваемый сервер, так что не сохраняется никакого серверного процесса. Хотя здесь не используется и не эмулируется сетевое соединение, объединенное при- ложение клиент-сервер получает доступ к базе данных тем же способом, что и другие динамические приложения клиентов Firebird. Существующий код приложений, напи- санный для использования в обычной сети клиент-сервер, работает без каких-либо изменений со встраиваемым сервером. Встраиваемый сервер под Windows Библиотека встраиваемого сервера fbembed.dll, включенная в инсталлятор Fire- bird 1.5 для Windows, имеет архитектуру, аналогичную Firebird SuperServer. Если вы планируете инсталлировать и использовать fbembed.dll, обратите внимание на специ- альные инструкции по размещению библиотек и исполняемых программ Firebird. Измененные файлы README и другие заметки обычно размещаются в каталоге /doc серверной инсталляции. Вы можете инсталлировать встраиваемый сервер и выполнять приложения встраи- ваемого сервера на машине Windows, на которой также находится Суперсервер или Классический сервер Firebird, хотя удаленные клиенты не могут соединяться с базой данных, с которой работает приложение встраиваемого сервера. В релизе 1.5, где не лучший протокол IPSERVER все еще используется для связи клиента и сервера, воз- можно использование библиотеки встраиваемого сервера в качестве клиента других серверов. В более поздних версиях, где протокол IPSERRVER заменен на XNET, это невозможно. Firebird 1.0.x не содержит варианта встраиваемого сервера для Windows.
140 Часть II. Клиент-сервер Встраиваемый сервер под Linux/UNIX Встраиваемый сервер является "родным" режимом доступа локального клиента к Классическому серверу Firebird в Linux/UNIX, включая и версию Firebird 1.0.x. Биб- лиотека встраиваемого сервера для локального доступа— libfbembed.so для Firebird 1.5 и выше и libgds.so для Firebird 1.0.x. Как и версия IPSERVER для Windows, встроенный клиент в Linux/UNIX может так же работать, как удаленный клиент с другим Классическим сервером Firebird. При этом клиент не будет потокобезопасным (thread-safe). Для многопоточных приложе- ний необходимо использовать обычный клиент libfbclient.so. В Firebird 1.0.x удален- ный клиент поставляется в комплекте Суперсервер для Linux и двусмысленно назван libgds.so, как и встроенный клиент Классического сервера. ВНИМАНИЕ! Клиент Суперсервера для Linux является клиентом, поддержи- вающим потоки для многопоточных приложений, однако он не поддерживает выполнения нескольких потоков в одном и том же соединении с базой данных. Отдельные экземпляры соединения должны быть созданы для каждого потока. Сервисы API Открытый код InterBase 6 — на основе которого был разработан Firebird — обраба- тывает в первую очередь обращения к функциям интерфейса API для некоторых дей- ствий сервера, таких как копирование/восстановление, получение статистических данных и управление пользователями. Многие из этих обращений предоставляют программный интерфейс для инструментов командной строки. Небольшое количест- во функций сервера низкого уровня включает также некоторые функции, перекры- вающие функции ядра API. Некоторые организации разработали и распространяют сервисные компоненты, ин- капсулирующие вызовы функций сервисов API из среды разработки Delphi, Kylix и C++ Builder. Большинство из них доступны для свободной загрузки с сайтов авторов или сообщества Firebird. Более подробную информацию см. в приложении 3. В Firebird 1.0.x сервисы API и сервисные компоненты работают только с серверами типа Суперсервер. В Firebird 1.5 Classic сервисы API полностью поддерживаются только начиная с версии 1.5.2. В версиях 1.5.0 и 1.5.1 Classic поддерживаются только некоторые функции — такие как вызов модулей gbak (копирование/восстановление) и gfix (обслуживание базы данных). Инсталляция клиентов Инсталляция удаленных клиентов является основной частью установки ваших при- ложений баз данных в сети клиент-сервер. Если вы новичок в Firebird и сетях клиент- сервер, вам рекомендуется пропустить этот раздел, пока вы не получите возможность поэкспериментировать с клиентами, выполняющимися локально — на той же маши- не, что и сервер.
Гпава 7. Клиенты Firebird 141 Каждой машине удаленного клиента требуется клиентская библиотека, которая соот- ветствует версии сервера Firebird. Обычно допустимым является использование кли- ентской библиотеки из другой сборки (build) релиза — при условии, что номера вер- сий совпадают. Тем не менее при обновлении сервера прочтите документацию README, поступающую с конкретным релизом, для определения того, где можно использовать более ранние клиентские версии. Внимательно просмотрите системные пути на каждой клиентской рабочей станции, на которой вы собираетесь инсталлировать клиента Firebird, для удаления при необ- ходимости существующих клиентских инсталляций InterBase или Firebird. ♦ В Firebird 1.0.x клиентские библиотеки используют те же имена и размещение, что и его двойник InterBase. Хотя возможно так установить приложения, чтобы они использовали переименованную клиентскую библиотеку, тем не менее, стро- го рекомендуется исключить размещение приложений Firebird 1.0.x и InterBase на одной и той же рабочей станции, если вы не уверены, что ваши приложения сконфигурированы так, чтобы отыскивать и использовать правильную библио- теку. ♦ Firebird 1.5 и более поздние версии под Windows может сосуществовать с InterBase и Firebird 1.0.x на сервере и клиенте. В Firebird 1.5 все еще имеют значе- ние ручные установки, хотя с меньшими неприятностями, чем в версии 1.0.x. В версиях более поздних, чем 1.5, множество серверов и версий могут быть авто- матически инсталлированы в Windows. Инсталляция клиента Linux/UNIX Операционные системы POSIX являются весьма своеобразными. Присутствующие в этом разделе советы должны быть полезными в качестве руководства по инсталляции клиентов для большинства разновидностей Linux и UNIX, однако это область, где сомнения являются несомненными! 1. Подключитесь к клиентской машине как пользователь root и найдите клиентскую библиотеку в инсталляции сервера: • в Firebird 1,0.x ее имя libgds.so.O, размещение по умолчанию /usr/lib; • в Firebird 1.5 двоичные файлы удаленного клиента libfbclient.so. 1.5.0 инстал- лированы по умолчанию в /opt/firebird/lib. ВНИМАНИЕ! В поставке Классического сервера есть клиентская библиотека с именем libfbembed.so.1.5, которая может быть использована для встроенных приложений. Не используйте ее для удаленных клиентов. 2. Скопируйте библиотеку в /usr/lib на клиенте и создайте символическую ссылку на нее, используя следующие команды: • для версии 1.0.x: In -s /usr/lib/libgds.so.O /usr/lib/libgds.so
142 Часть II. Клиент-сервер • для версии 1.5 (две ссылки): In -s /usr/lib/libfbclient.so.1.5 /usr/lib/libfbclient.so.О In -s /usr/lib/libfbclient.so.0 /usr/lib/libfbclient.so 3. Создайте каталог/opt/firebird (/opt/interbase для версии 1.0.x) на клиенте для файла сообщений и скопируйте этот файл из корневого каталога Firebird на сервере: • для версии 1,0.x скопируйте interbase.msg в /opt/interbase/; • для версии 1.5 и выше скопируйте firebird.msg в /opt/firebird/. 4. В системном профайле оболочки по умолчанию или вызовом setenvo создайте переменную окружения, которая позволит найти утилиту для сообщений API: • для версии 1.0.x создайте переменную InterBase и свяжите ее с /opt/interbase/; • для версии 1.5 и выше создайте переменную firebird и свяжите ее с /opt/firebird/. Инсталляция клиента Windows Firebird 1.0.x В Windows клиентская библиотека всегда по умолчанию инсталлировалась в сис- темный каталог. По умолчанию это C:\WINNT\system32 для Windows NT и 2000, C:\Windows\system32 для Windows ХР и Server 2000 и C:\Windows\ или C:\Windows\system2 для Windows 9х и ME. В Firebird 1.0.x в качестве временной меры сохраняются старые имена и местоположение. Использование инсталлятора Firebird Самый простой способ инсталлировать клиента Firebird 1.0.x— это скопировать ин- сталлятор Firebird на компакт-диск или флэш-память и запустить его на клиентской машине, выбрав установку только клиента в диалоговом окне инсталлятора. Вы мо- жете инсталлировать клиента с инструментами командной строки или без них. Боль- шинству клиентов не нужны эти инструменты; не рекомендуется устанавливать их на клиентских рабочих станциях, где не требуется доступ администратора к серверу. Инсталлятор создаст корневой каталог по умолчанию в C:\Program Files\Firebird; это размещение вы можете изменить в соответствующем диалоге инсталлятора. Сюда он запишет файл сообщений interbase.msg и, если были выбраны для инсталляции инст- рументы командной строки, создаст каталог \bin, куда поместит эти инструменты. Он запишет в системный каталог gds32.dll и, если библиотека времени выполнения Microsoft С старая или вовсе отсутствует, запишет туда же msvcrt.dll. Под конец он запустит программу instreg.exe для инсталляции ключей в системном реестре. Если вы выбрали каталог инсталляции по умолчанию, ключ будет 2 На этих платформах, не имеющих поддержки сервисов, размещение может быть иным. Необходимо проверять, установлены ли предыдущие клиенты Firebird или InterBase.
Гпава 7. Клиенты Firebird 143 HKLM\Software\Borland\InterBase. ЕСЛИ какие-нибудь выполняющиеся программы бы- ли перезаписаны в процессе инсталляции, вам будет предложено перезагрузить ма- шину. Инсталляция клиента вручную Инсталляция клиента вручную требует выполнения всех предыдущих шагов. Вам нужно скопировать файлы gds32.dll, interbase.msg и instreg.exe на дискету или флэш- память. Также скопируйте из системного каталога msvcrt.dll, если на ваших клиентах она не установлена. Создав корневой каталог Firebird, скопируйте туда interbase.msg. Затем запустите с диска instreg.exe, набрав в окне командной строки: А:\> instreg.exe 'C:\Prograia Fiies\Firebird.' Если вы создали корневой каталог Firebird где-нибудь в другом месте, укажите этот путь как аргумент корневого каталога. Скопируйте gds32.dll и, если нужно msvcrt.dll, в системный каталог. f ПРИМЕЧАНИЕ, msvcrt.dll является библиотекой времени выполнения для многих программ, скомпилированных компилятором Windows С. gds32.dll — это имя кли- ___£> ентской библиотеки для серверов InterBase, а также Firebird 1.0.x. В случае если вы не можете скопировать одну из библиотек или обе по причине их использова- ния другой программой, необходимо остановить ту программу и повторить про- цесс копирования. Если все еще невозможно переписать библиотеки по причине того, что аварийно завершившаяся программа не выгрузила их, перезагрузите компьютер в безопасном режиме и выполните этот шаг. Firebird 1.5 и выше Firebird версии 1.5 и последующие клиентские инсталляции поставляются с большим количеством режимов с целью избежать "ужаса DLL" в системном каталоге Windows. До тех пор, пока сторонние инструменты, драйверы и компоненты не научатся пони- мать изменения в клиентской части Firebird 1.5, Firebird 1.5 будет поддерживать соб- ственные "ужасы DLL". Инсталляция клиента по умолчанию почти наверняка будет несовместимой с программным обеспечением, созданным с использованием таких RAD-продуктов Borland, как Delphi или C++ Builder. Внимательно прочтите этот раздел перед началом работы, чтобы создать все необхо- димое для той конкретной среды разработки, для которой вы выполняете инсталля- цию. Позже вы сможете вернуться назад и скорректировать инсталляцию вручную. Использование инсталлятора Firebird Хотя существуют другие варианты, рекомендуемый способ инсталляции клиента — использование инсталляционной программы Firebird 1.5.
144 Часть И. Клиент-сервер Если вы используете инсталлятор, то первый выбор, который вы должны сделать — размещение корневого каталога для инсталляции клиента (рис. 7.1). Рекомендуется выбрать значение по умолчанию (C:\Program Files\Firebird\Firebird_l_5), тогда будет проще в будущем выполнять обновления. Тем не менее при необходимости вы може- те указать размещение на свой вкус. Хотя вы и не собираетесь инсталлировать сервер, инсталлятор предоставляет воз- можность устанавливать в корневой каталог дополнительные элементы, в том числе ключ системного реестра, который необходим некоторым программным продуктам, используемым на клиентской машине. Если вы инсталлируете инструменты команд- ной строки, то выбранное размещение корневого каталога является существенным. При необходимости, позже, вы сможете изменить установку вручную. ! Setup I irebird Database Server 1.5 ШяШ Select Destination Directory Where should Firebird Database Server 1.5 be installed? ,.i"i.xi Select the foldei wheeyou would tt« Firebird Database Server 1.5 to be irist Ad, then click Next. The program requires at least 1.4 MB of disk space. Рис. 7.1. Выбор размещения корневого каталога инсталляции Затем вы должны принять решение о выборе режима инсталляции — нужно ли уста- навливать клиента с инструментами командной строки или без них, как показано на рис. 7.2. Большинству клиентов эти инструменты не нужны; не рекомендуется инсталлиро- вать их на клиентской рабочей станции, если нет необходимости администратору получать доступ к серверу. Для минимальной инсталляции выберите вариант Mini- mum client install — no server, no tools (Минимальная установка клиента — без сер- вера, без инструментов) и щелкните по кнопке Next. Ваш выбор в следующем диалоговом окне (рис. 7.3) особенно важен, если на клиенте вы используете программное обеспечение сторонних организаций. Предыдущие инсталляторы устанавливали старую клиентскую библиотеку gds32.dll в системный каталог вместе с библиотекой времени выполнения для языка С msvcrt.dll, если она отсутствовала.
Гпава 7. Клиенты Firebird 145 Setup - Firebird Database Servfei 1.5 Select Components Which components should be installed? Select the components you want to install clear the components you do not want to install. Click Next when you are ready to con&iua Full installation of Super Server and development tools Full installation of Super Server and development tools. Full installation of Classic Server and development tools installation o^ Client tools for Developers and database administrators. Minimum clierit install - no server лта fools Custom installation Current selection requites at least 8.8 M0 of disk space. Рис. 7.2. Выбор инсталляции только клиента Setup Firebird Database Server 1Л Select Additional I asks Which additional tasks should be performed'? Select the addifenel tasks you would like Setup to perform while installing Firebird Database Server 1.S, then dick Next. (А)- (В)- Г~ Copy Fiebird client itay to ksysterrP directory? •p Generate client Ifcrary as GDS32.DLL for legacy app. support? < S ack | Next > Рис. 7.3. Выбор "версии" и размещения для клиента В версии 1.5 инсталлятор устанавливает все DLL — новую клиентскую библиотеку fbclient.dll и (если требуется) библиотеку времени выполнения для С и С++ msvcp60.dll — в каталог \bin в корневом каталоге Firebird. ♦ (A) Reallocation of the client library (Изменение размещения клиентской библио- теки). Если вам нужна совместимость с программным обеспечением, которое ожидает найти клиентскую библиотеку в системном каталоге, отметьте эту пози- цию. ♦ (В) Name of the client library (Имя клиентской библиотеки). Если вашему про- граммному обеспечению или компонентам нужна клиентская библиотека с име-
146 Часть II. Клиент-сервер нем gds32.dll, отметьте эту позицию. Инсталлятор сгенерирует специальную ко- пию fbclient.dll с именем gds32.dll и установит строку внутренней версии для со- вместимости с драйверами InterBase фирмы Borland и компонентами. Размещение этого файла зависит от состояния первой позиции (А). Щелкните по кнопке Next для инсталляции. Инсталляция клиента вручную Инсталляция клиента вручную требует выполнения всех тех же шагов, которые вы- полнял бы инсталлятор. Вам нужно скопировать следующие файлы из каталога ин- сталляции сервера на дискету или флэш-память: ♦ %system%\gds32.dll (C:\WINNT\system32 или C:\Windows) ♦ firebird.msg ♦ bin\fbclient.dll ♦ bin\msvcrt.dll (при необходимости) ♦ bin\msvcp60.dll (при необходимости) ♦ bin\instreg.exe ♦ bin\instclient.exe ♦ bin\fbclient. local ♦ bin\msvcrt. local ♦ bin\msvcp60. local На клиенте выполните следующие шаги: 1. Создайте корневой каталог Firebird и скопируйте туда firebird.msg. 2. В этом каталоге создайте каталог bin. 3. Скопируйте файлы из каталога \bin дискеты в этот новый каталог \bin. 4. Запустите программу instreg.exe из нового каталога \bin в окне командной строки. Очень важно запустить эту программу из каталога \bin корневого каталога Firebird, где расположена программа instreg.exe. Например, если корневой каталог Firebird находится в C:\Firebird_Client, введите: С:\Firebird_Client\bin> instreg.exe install 5. Если у вас есть приложение, которому нужна клиентская библиотека с именем gds32.dll, то вам необходимо выполнить программу instclient.exe. Инструкции в следующем разделе. Выполнение instclient.exe Программа instclient.exe может быть выполнена, когда вам требуется клиентская вер- сия, к которой осуществляют доступ существующие программы, драйверы или ком- поненты, которые ожидают, что имя клиентской библиотеки gds32.dll, или что она располагается по системному пути Windows. Это программа командной строки, ко- торая находится в каталоге \bin в корневом каталоге вашей инсталляции сервера
Гпава 7. Клиенты Firebird 147 Firebird. При необходимости скопируйте данный файл в соответствующий каталог на клиентскую машину. Инсталляция клиента в системный каталог Откройте окно командной строки и перейдите в каталог \bin. Синтаксис инсталляции клиента: instclient.exe {i[nstall]} [-fforce]] {fbclient | gds32} Требуются параметры i (или install) И один ИЗ параметров fbclient ИЛИ gds32. Если программа найдет, что файл, который вы пытаетесь инсталлировать (fbclient.dll или gds32.dll), уже находится в системном каталоге, она не будет выполняться. Что- бы программа записывала файл, даже если найдет его копию, используйте переклю- чатель -f (или -force). Ваша операционная система может потребовать перезагрузку машины для заверше- ния инсталляции. Д ВНИМАНИЕ! Если вы выбираете форсированную инсталляцию, вы рискуете разрушить клиента, который был инсталлирован для использования с другим программным обеспечением, которое было создано для соединения с сервером Firebird 1.0 или InterBase. Запрос к инсталлированному клиенту Программа instclient.exe может быть использована для получения информации о вы- полняющихся на этой машине клиентах Firebird 1.5. Синтаксис запроса о клиентах: instclient.exe {qfuery] fbclient I gds32} На рис. 7.4 показана возвращаемая информация. Iе' Command Prompt ” " " ...... " 1м C:\Progras» Files\Firebirds.Firebird_l_,5\bin>instclient-exe q installed GDS32.DLL version : 6.3.0.4306 (shared DLL count 4> C:\Progran Files\Firebird\Firehird„.l_5\bin>inst.client.exe q fbclient FBCLIENT.DLL was not found in the System directory. C:\Progran File s\Fire b ird\F ire b ird_l_5\h in > ----------21 Рис. 7.4. Запрос с помощью instclient.exe Использование instclient.exe для деинсталляции клиента версии 1.5 Для удаления клиента Firebird 1.5, инсталлированного в системный каталог, исполь- зуйте следующий синтаксис: instclient.exe {rfemove] fbclient I gds32}
148 Часть II. Клиент-сервер Список имен и размещение клиентской библиотеки В табл. 7.1 представлены имена клиентской библиотеки и размещение по умолчанию на клиентах Firebird. Таблица 7.1. Имена и размещение по умолчанию клиентов Firebird Версия, вид ОС клиента Библиотека Размещение по умолча- нию Подключается к Firebird 1,0.x Классический сервер Linux/UNIX libgds.so.O, символьная ссыл- ка на libgds.so /usr/lib Только Класси- ческий сервер 1.0 Firebird 1.0 Суперсервер для Windows Windows NT/2000 gds32.dll C:\WINNTt system32 Любой сервер 1.0 Windows ХР/Server 2003 gds32.dll C:\Windows\ system32 То же Windows 9x/ME gds32.dll C:\Windows То же Firebird 1.0 Суперсервер для Linux Linux/UNIX libgds.so.O, символьная ссыл- ка на libgds.so. Обратите внима- ние, что эта биб- лиотека отличает- ся от клиента libgds.so в Класси- ческом сервере /usr/lib Любой сервер 1.0 за исключе- нием Классиче- ского сервера Firebird 1.5 Классический сервер для Linux Linux/UNIX libfbembed.so.O или libfbclient.so.O, символьная ссыл- ка на libfbembed.so или libfbclient.so, 3 соответственно /usr/lib Только Класси- ческий сервер для Linux, при- ложения без потоков, воз- можны локаль- ные соединения Firebird 1.5 Суперсервер для Linux Linux/UNIX libfbclient.so.O, символьная ссыл- ка на libfbclient.so /usr/lib Любой сервер 1.5 3 Встроенный сервер для Linux не соответствует ему же в Windows. Как и в модели под Windows, клиент libfbembed.so имеет "прямое соединение" с экземпляром сервера. В Linux та же библиотека может создавать экземпляр встроенного сервера на сетевом сервере. Однако libfbembed.so строго рекомендуется в качестве клиента, если приложение содержит потоки.
Гпава 7. Клиенты Firebird 149 Таблица 7.1 (окончание) Версия, вид ОС клиента Библиотека Размещение по умолча- нию Подключается к Firebird 1.5 Клас- сический сервер и Суперсервер для Windows Windows NT/2000 Родной: fbclient.dll Firebird rootXbin Любой сервер 1.5 Совместимость: fbclient.dll или gds32.dll, встроен- ный в instclient.exe C:\WINNT\ system32 То же Windows ХР/Server 2003 Родной: fbclient.dll Совместимость: fbclient.dll или gds32.dll, встроен- ный в instclient.exe C:\Windows\ system32 Любой сервер 1.5 Windows Эх/МЕ Родной: fbclient.dll Firebird root\bin То же Совместимость: fbclient.dll или gds32.dll, встроен- ный в instclient.exe C:\Windows или C:WVindows\ system4 То же Firebird 1.5 Встроенный сервер Все встроенные клиенты Windows fbembed.dll Корневой ка- талог испол- няемого моду- ля приложения То же Пора дальше В части III мы переходим к детальному рассмотрению типов данных, поддерживае- мых языком SQL Firebird. В следующей главе вводятся типы данных и рассматрива- ются некоторые вопросы, которые вы должны понимать при подготовке к определе- нию, сохранению и работе с данными SQL. Она заканчивается специальным обсуж- дением, которое будет вам полезным, если вы собираетесь выполнять миграцию данных из существующей базы данных в Firebird. 4 Официальный инсталлятор и instclient.exe трактуют C:\Windows\system как "системный каталог" для этих платформ. Некоторые "неофициальные" инсталляторы следуют соглашени- ям, установленным фирмой Borland для InterBase, как и некоторые инсталляторы Firebird 1.0, трактующие C:\Windows как системный каталог. Оба размещения должны быть проверены, если вам нужно отыскать инсталлированного клиента.

ЧАСТЬ III Типы данных Firebird и домены Глава 8. О типах данных Firebird Глава 9. Числовые типы данных Глава 10. Типы даты и времени Глава 11. Символьные типы данных Глава 12. BLOB и массивы Глава 13. Домены

ГЛАВА 8 О типах данных Firebird Тип данных является основным атрибутом, который должен быть определен для каждого столбца в таблице Firebird. Он устанавливает и ограничивает характеристи- ки множества данных, которые могут храниться в столбце, и операции, которые мо- гут быть выполнены над данными. Он также определяет, какое дисковое пространст- во занимает каждый элемент данных. Выбор оптимального размера значений данных является важным решением для сетевого трафика, экономии дисковой памяти и раз- мера индексов. Firebird поддерживает большую часть типов данных SQL. В дополнение он поддер- живает динамически изменяемые типизированные и не типизированные большие двоичные объекты (Binary Large Object, BLOB) и многомерные однородные массивы для большинства типов данных. Где задаются типы данных Тип данных определяется для элементов данных в следующих ситуациях: ♦ при определении столбца в операторе create table; ♦ при создании шаблона глобально используемого столбца посредством create domain; ♦ при изменении шаблона глобально используемого столбца с применением alter domain; ♦ при добавлении нового столбца в таблицу или при изменении столбца с использо- ванием ALTER TABLE; ♦ при объявлении аргументов и локальных переменных в хранимых процедурах и триггерах; ♦ при объявлении аргументов и возвращаемых значений внешних функций (функ- ций, определенных пользователем, UDF). Поддерживаемые типы данных Числовые типы данных (обсуждаемые в главе 9) следующие: ♦ BIGINT, INTEGER И SMALLINT; ♦ NUMERIC и decimal; ♦ FLOAT И DOUBLE PRECISION.
154 Часть III. Типы данных Firebird и домены Типы данных даты и времени (обсуждаемые в главе 10): ♦ date; ♦ TIME И TIMESTAMP. Символьные типы данных (обсуждаемые далее в главе 11): ♦ character; ♦ VARYING CHARACTER И NATIONAL CHARACTER. Типы данных blob и массивы (обсуждаемые далее в главе 12): ♦ blob, типизированный и нетипизированный. ♦ ARRAY (маССИВ). Булевы типы данных Firebird 1.5 и выше не поддерживает булевы (логические) типы данных. Обычной практикой является объявление односимвольного или smallint домена для общего использования, где требуются булевы типы данных. Советы по определению булевых доменов см. в главе 13. "Диалекты" SQL Firebird поддерживает три "диалекта" SQL, которые не имеют другого практического назначения, кроме конвертирования баз данных из InterBase версий 5.x в Firebird. "Родной" диалект Firebird в настоящий момент известен как диалект 3. По умолча- нию Firebird создает новую базу данных в этом родном диалекте. Если в вашем опы- те в Firebird нет ни груза существующих предположений, ни созданных баз данных, которые вам нужно обновить для Firebird, вы можете без риска "следовать естеству" и игнорировать все последующие замечания и предупреждения относительно диа- лекта 1. Если вы бывший пользователь InterBase или применяли устаревшие инструменты для преобразования данных из других СУБД в InterBase, то диалекты SQL будут для вас предметом обсуждения в нескольких отношениях. Поскольку вы можете работать с этой книгой в том порядке, который вам подходит, то вопросы влияния диалекта SQL будут отмечены соответствующим образом. Неко- торые из наиболее серьезных эффектов разных диалектов проявляются в различии между типами данных. Вопросам диалектов посвящен разд. "Специальная тема ми- грации: диалекты SQL" этой главы. Идентификаторы с разделителями в SQL-92 В базах данных диалекта 3 Firebird поддерживает соглашение ANSI SQL о необяза- тельных идентификаторах с разделителями. Для использования зарезервированных слов, строк, чувствительных к регистру, или пробелов в именах объектов заключите
Гпава 8. О типах данных Firebird 155 имя в двойные кавычки. Это имя становится идентификатором с разделителями. К идентификаторам с разделителями всегда нужно обращаться, заключив их в ка- вычки. Подробности см. в разд. "Соглашения и ограничения в именовании объектов базы данных" главы 14. Более подробную информацию об именовании объектов базы дан- ных с использованием операторов create и declare см. в части IV этой книги. В при- ложении 11 представлен список ключевых слов, которые являются зарезервирован- ными словами в SQL. Контекстные переменные Firebird делает доступным множество значений переменных, поддерживаемых сис- темой в контексте текущего соединения клиента и его деятельности. Эти контекст- ные переменные доступны для использования в SQL, включая язык триггеров и хра- нимых процедур, PSQL. Некоторые доступны только в PSQL, большинство — только в диалекте 3 базы данных. В табл. 8.1 представлены контекстные переменные Firebird. Таблица 8.1. Список контекстных переменных Контекстная переменная Тип данных Описание Доступность CURRENT_CONNECTION INTEGER Системный идентификатор соединения, при котором выполняется настоящий за- прос Firebird 1.5 и вы- ше, DSQL и PSQL current_date DATE Текущая дата по часам на сервере Firebird 1.0 и вы- ше, все окружения SQL current_role VARCHAROl) Имя роли, под которым со- единился текущий пользова- тель. Возвращает пустую строку, если текущее соеди- нение не использовало роль Firebird 1.0 и вы- ше, все окружения SQL CURRENT_TIME TIME Текущее время по часам на сервере, выраженное в се- кундах после полуночи Firebird 1.0 и вы- ше, все окружения SQL CURRENT_T IME ST AMP TIMESTAMP Текущая дата и время по часам на сервере в секундах Firebird 1.0 и вы- ше, все окружения SQL CURRENT_TRANSACTION INTEGER Системный идентификатор транзакции, в контексте кото- рой выполняется текущий запрос Firebird 1.5 и вы- ше, DSQL и PSQL CURRENT_USER VARCHAR(128) Имя пользователя, который связан сданным экземпляром клиентской библиотеки Firebird 1.0 и вы- ше, все окружения SQL
156 Часть III. Типы данных Firebird и домены Таблица 8.1 (окончание) Контекстная переменная Тип данных Описание Доступность ROW_COUNT INTEGER Счетчик строк измененных, удаленных и добавленных оператором DML после за- вершения операции Firebird 1.5 и вы- ше, DSQL и PSQL UPDATING BOOLEAN Возвращает true, если вы- полняется оператор измене- ния Firebird 1.5 и вы- ше, только диа- лект триггера PSQL INSERTING BOOLEAN Возвращает true, если вы- полняется оператор добавле- ния Firebird 1.5 и вы- ше, только диа- лект триггера PSQL DELETING BOOLEAN Возвращает true, если вы- полняется оператор удаления Firebird 1.5 и вы- ше, только диа- лекгтриггера PSQL SQLCODE INTEGER Возвращает sqlcode из блока исключения when. Использо- вание см. в главе 32 Firebird 1.5 и вы- ше, только язык процедур PSQL GDSCODE INTEGER Возвращает gdscode из блока исключения when. Использо- вание см. в главе 32 Firebird 1.5 и вы- ше, только язык процедур PSQL USER VARCHAR(128) Имя пользователя, который связан сданным экземпляром клиентской библиотеки Предшественники InterBase, все версии Firebird, все окружения SQL, доступные в диалекте 1 Временные значения current_connection и current-transaction не имеют смысла вне текущего соединения и контекста транзакции соответственно. Сервер Firebird сохранит самые последние значения этих идентификаторов в заголовочной странице базы данных. После вос- становления базы данных из резервной копии эти значения будут заново установле- ны в ноль. current_timestamp записывает время сервера на момент старта операции. Для всех записей, вставляемых или обновляемых одним оператором, значение этой перемен- ной будет одним и тем же. Хотя otrrenT-Time хранится на сервере как время после полуночи, ее тип time, а не интервал времени. Для получения интервала времени используйте timestamp при
Глава 8. О типах данных Firebird 157 старте и завершении и вычтите время старта из времени завершения. Результатом будет интервал времени в днях. Контекстные переменные даты/времени основаны на времени сервера, которое мо- жет отличаться от внутреннего времени на клиенте. Примеры использования Следующий оператор возвращает время сервера в момент, когда сервер обслуживает запрос клиента Firebird: SELECT CURRENT-TIME AS TIME-FINISHED FROM RDB$DATABASE; В следующем операторе добавления идентификатор текущей транзакции, текущие серверные дата и время, а также имя пользователя системы будут записаны в таб- лицу: INSERT INTO TRANSACTIONLOG (TRANS_ID, USERNAME, DATESTAMP) VALUES ( CURRENT-TRANSACTION, CURRENT_USER, CURRENT-TIMESTAMP) ; Предопределенные литералы даты Литералы даты — заключенные в апострофы строки, которые Firebird SQL будет воспринимать как специальные даты. В диалекте 1 эти строки используются напря- мую, в диалекте 3 они должны быть преобразованы в соответствующий тип. В табл. 8.2 показано использование дат в каждом диалекте. Таблица 8.2. Список предопределенных литералов даты Литерал даты Подставляемая дата Тип данных, Диалект 1 Тип данных, Диалект 3 'NOW Текущая дата и время date (эквивалентно timestamp в диалекте 3) TIMESTAMP 'TODAY' Текущая дата date с нулевым временем date (только дата) 'YESTERDAY' Текущая дата -1 date с нулевым временем DATE ’TOMORROW1 Текущая дата + 1 date с нулевым временем DATE ХР ПРИМЕЧАНИЕ. В диалекте 1 тип данных date эквивалентен типу данных timestamp в диалекте 3. В диалекте 3 тип данных date содержит только дату. ,—R В диалекте 1 нет эквивалентного типа.
158 Часть III. Типы данных Firebird и домены Примеры использования предопределенных литералов даты В диалектах базы данных 1 и 3 литерал даты должен быть преобразован в тип данных timestamp: SELECT CAST ('NOW AS TIMESTAMP) AS TIME_FINISHED FROM RDB$DATABASE; Следующий оператор update устанавливает значение столбца даты в серверную дату плюс один день в диалекте 1: UPDATE TABLE_A SET UPDATE-DATE = 'TOMORROW' WHERE KEY_ID = 144; Вот та же самая операция в диалекте 3 с преобразованием типа: UPDATE TABLE_A SET UPDATE_DATE = CAST('TOMORROW' AS DATE) WHERE KEY-ID = 144; Столбцы Данные в таких реляционных системах баз данных, как Firebird, логически упорядо- чены в виде множества строк и столбцов. Столбец хранит один элемент данных с атрибутами, идентичными для всех строк в наборе. Определение столбца имеет два обязательных атрибута: идентификатор (или имя столбца) и тип данных. Другие ат- рибуты могут быть включены в определение столбца, например, character set и ог- раничения ТИПа NOT NULL И UNIQUE. Множества, определенные для хранения данных, называются таблицами. Структура строк таблицы определяется при объявлении идентификатора таблицы; эта структура является списком идентификаторов столбцов, их типов данных и других необходи- мых атрибутов. Простой пример объявления таблицы: CREATE table SIMPLE ( COLUMN1 INTEGER, COLUMN2 CHAR(3), COLUMNS DATE); Полное описание объявления таблиц и столбцов см. в главе 16. Домены В Firebird вы можете сделать предварительное объявление столбца с типом данных и "шаблонным набором" атрибутов в виде домена. Как только домен будет создан и
Гпава 8. О типах данных Firebird 159 подтвержден (commit), он может быть использован в любой таблице вашей базы дан- ных, как если бы он был типом данных. ЖГ ПРИМЕЧАНИЕ. Существуют некоторые ограничения по использованию доменов. В частности, домен не может применяться в объявлении локальных переменных, входных и выходных аргументов в модулях PSQL (язык процедур). Столбцы, основанные на домене, наследуют все атрибуты домена: его тип данных, другие атрибуты, включая значение по умолчанию, ограничения на значения, набор символов и порядок сортировки. Любой атрибут за исключением типа данных может быть переопределен при исполь- зовании домена в определении столбца при определении таблицы путем замены ат- рибута на другой совместимый атрибут или при добавлении атрибута. Например, можно объявить домен с набором атрибутов, не включающих not null, для которого можно сделать допустимым пустое значение в одних случаях, a not null в других. Более подробную информацию о создании, использовании и поддержке доменов см. в главе 13. Преобразование типов данных Обычно вы должны использовать совместимые типы данных при выполнении ариф- метических операций или при сравнении данных в условиях поиска. Если вам нужно выполнить операции над смешанными типами данных, или если ваш язык програм- мирования использует типы данных, которые не поддерживаются в Firebird, то необ- ходимо выполнить преобразование типов данных до выполнения операций с базой данных. Неявное преобразование типов Поведение диалектов 1 и 3 различно при неявном преобразовании типов. Это может стать проблемой, если вам нужно преобразовать существующую базу данных в диа- лект 3 и изменить использующие их приложения. ♦ В диалекте 1 для некоторых выражений Firebird выполняет автоматическое пре- образование данных в эквивалентные типы данных (неявное преобразование ти- пов). Здесь также может быть использована функция casto, хотя в большинстве случаев она не нужна. ♦ В диалекте 3 в условиях поиска требуется функция casto для явной трансляции одного типа данных в другой для операций сравнения. Например, сравнение столбца типа date или timestamp с '12/31/2003' в диалекте 1 при- водит к неявному преобразованию строкового литерала '12/31/2003' в тип данных DATE.
160 Часть III. Типы данных Firebird и домены SELECT * FROM TABLE_A WHERE START_DATE < '12/31/2003'; В диалекте 3 требуется явное преобразование: SELECT ♦ FROM TABLE_A WHERE START_DATE < CAST('12/31/2003' AS DATE); В выражениях, где смешиваются целые данные и числовые строки, в диалекте 1 строки неявно преобразуются в целое, если это возможно. В следующей операции: з + '1' диалект 1 автоматически преобразует символ "1" в smallint, в то время как диалект 3 вернет ошибку. Он требует явного преобразования типов: 3 + CAST('1' AS SMALLINT) Оба диалекта вернут ошибку в следующем операторе, потому что Firebird не может преобразовать символ "а" в целое: 3 + 'а' Явное преобразование типов: CASTO В тех случаях, когда Firebird не может выполнить неявное преобразование типов, вы должны выполнить явное преобразование типов посредством функции casto. Ис- пользуйте cast (; для преобразования одного типа данных в другой в операторе select обычно в предложении where для сравнения различных типов данных. Синтак- сис функции: CAST (значение I NULL AS тип данных) Вы можете использовать casto для сравнения столбцов с различными типами дан- ных в той же таблице или из различных таблиц. Например, вы можете преобразовы- вать правильно сформированную строку в типы дата/время, а также во множество числовых типов. Подробную информацию о преобразованиях типов данных смотри- те в остальных главах этой части. Изменение определения столбцов и доменов В обоих диалектах вы можете изменять тип данных доменов и столбцов в таблицах. Если вы выполняете миграцию базы данных из другой СУБД, это может быть полез- ным. Существуют некоторые ограничения при изменении типа данных. ♦ Firebird не допускает изменения типа данных столбца или домена, которое может привести к потере данных. Например, количество символов в столбце не может быть меньше наибольшего размера столбца. ♦ Преобразование числового типа данных в строковый требует минимального раз- мера строкового типа, как показано в табл. 8.3.
Глава 8. О типах данных Firebird 161 Таблица 8.3. Минимальное количество символов для числовых преобразований Тип данных Минимальная длина символьного типа BIGINT 19 (или 20 для чисел со знаком) DECIMAL 20 DOUBLE 22 FLOAT 13 INTEGER 10 (11 для чисел со знаком) NUMERIC 20 (или 21 для чисел со знаком) SMALLINT 6 Изменение типа данных столбца Используйте предложение alter column в операторе alter table, например: ALTER TABLE tablel ALTER COLUMN fieldl TYPE char(20); Информацию об изменении столбцов таблицы см. в разд. "Изменение таблиц" гла- вы 16. Изменение типа данных домена Используйте предложение type в операторе alter domain для изменения типа данных домена, например, ALTER DOMAIN MyDomain TYPE VARCHAR(40); На рис. 8.1 показаны допустимые преобразования типов данных. Более подробную информацию об изменении атрибутов домена см. в главе 13. Ключевые слова, используемые для спецификации типа данных Ключевые слова для спецификации типов данных в операторах DDL представлены здесь в качестве краткой справки. Точный синтаксис см. в соответствующей главе, связанной с типами данных этой части книги, а также в главах 13 и 16. {SMALLINT I INTEGER | FLOAT | DOUBLE PRECISION} [<array_dim>] | (DATE | TIME | TIMESTAMP} [<array_dim>] [{DECIMAL [NUMERIC} [ (precision [, scale])] [<array_diffl>] | {CHAR | CHARACTER | CHARACTER VARYING | VARCHAR} [(ant)] [<array_dim>] [CHARACTER SET charnaine] | (NCHAR | NATIONAL CHARACTER | NATIONAL CHAR} [VARYING] [(int)] [<array_dim>] 6 Зак. 420
162 Часть III. Типы данных Firebird и домены | BLOB [SUB__TYPE int | subtype_name] ] [SEGMENT SIZE int] [CHARACTER SET charname] I BLOB [(seglen [, subtype] ) ] Рис. 8.1. Допустимые преобразования данных с использованием операторов alter column и alter domain Специальная тема миграции: диалекты SQL Если вы бывшие пользователи InterBase или вы использовали устаревшие инстру- менты миграции для конвертирования других СУБД в InterBase, то диалекты SQL видимо будут влиять на некоторые аспекты новой жизни ваших баз данных и прило- жений при использовании сервера Firebird. ODS и диалект Структура данных на диске (On-Disk Structure, ODS) идентифицирует базу данных в отношении версии релиза сервера Firebird или InterBase, который создает и восста- навливает базу данных. ODS базы данных влияет на совместимость с версиями сер-
Глава 8. О типах данных Firebird 163 вера. Файл, подходящий для обновления ODS, может быть создан резервным копи- рованием базы данных (backup) с использованием утилиты gbak той версии, в кото- рой была создана база данных. Утилита должна быть использована с переключателем -ttransportable]. Когда файл резервной копии будет восстановлен с использованием gbak новой версии, восстановленная база данных будет иметь новую версию ODS. Такое невозможно выполнить для "понижения" ODS для любой базы данных. Использование gbak подробно обсуждается в главе 38. Обновление ODS не изменяет диалект Обновление ODS не влияет на диалект SQL базы данных: база данных диалекта 1 останется базой данных диалекта 1. Базы данных Firebird Firebird 1.0.x имеет ODS, обозначаемый как ODS-Ю. Firebird 1.5 имеет ODS-10.1. Чтобы преобразовать базу данных ODS-Ю, созданную в Firebird 1.0.x в ODS-10.1, вам просто нужно сделать ее резервную копию и восстановить эту копию с использо- ванием gbak из Firebird 1.5. По умолчанию серверы Firebird версий 1.0.3+ и 1.5 соз- дают базы данных диалекта 3. Для проверки ваших баз данных см. разд. "Как опре- делить диалект" далее в этой главе. Базы данных InterBase 6.0.x OpenSource InterBase версий 6.0.x имеют ODS-Ю. Тем не менее для обновления баз данных InterBase 6.0.x до любой версии Firebird рекомендуется использовать gbak из InterBase 6.0 с переключателем -ttransportable]. Файл резервной копии должен быть затем восстановлен с использованием gbak соответствующей версии сервера Firebird. Если база данных InterBase 6.0 была создана с установками по умолчанию, то, веро- ятно, она имеет диалект 1. См.разд. "Как определить диалект" далее в этой главе. Базы данных InterBase 5.x Базы данных InterBase 5 имеют ODS-9 (9.0 и 9.1). Серверы Firebird могут открывать их, читать как базы данных диалекта 1, но дальнейшая работа с этими базами данных в InterBase 5.x не рекомендуется. /Г ПРИМЕЧАНИЕ. Сервер Firebird не может создавать базы данных с ODS-9. Базы данных диалекта 1, созданные сервером Firebird, не могут быть использованы \__\ сервером InterBase 5.x. Не существует такой вещи, как база данных ODS-9 диалекта 1 или диалекта 3. Для обновления базы данных ODS-9 до Firebird используйте программу gbak из InterBase 5.x, запущенную с сервера InterBase 5.6 с переключателем -ttransportable]. Обновление базы данных InterBase 5.x до Firebird не преобразует ее в диалект 3 базы данных. Ее SQL диалект будет 1, и это обновление является необратимым.
164 Часть Hi. Типы данных Firebird и домены Где учитывается диалект Концепция диалекта различает способ поддержки типов данных и возможности язы- ка, доступные в базах данных с ODS-9 (диалект 1) и ODS-10 и выше (диалект 3). Сам сервер не имеет "диалекта" — диалект базы данных сохраняется как атрибут базы данных. Он является интерфейсом клиента, который определяет, какой набор воз- можностей запрашивается у базы данных. При некоторых условиях, если вы как раз- работчик приложений или пользователь инструментов администратора используете его неправильно, вы получите ошибочные данные, сохраняемые в базе, что может привести к некорректной работе приложений и базы данных. Здесь удобно обратиться к экземпляру клиентского соединения, выполненного с по- мощью библиотеки API или пользовательского драйвера языка, такого как JayBird (Java), ODBC или провайдер .NET, как "клиент диалекта 1" или "клиент диалекта 3". Это означает, что интерфейс клиента будет установлен для получения возможностей диалекта 1 или 3. Что можно отбросить Следующий список иллюстрирует некоторые отличия диалекта 1 от диалекта 3. ♦ Диалекты 1 и 3 хранят большие масштабируемые числа по-разному. В диалекте 3 все типы с фиксированной точкой (numeric и decimal), имеющие точность больше 10, являются 64-битовыми целыми с описанием, которое включает некоторые ат- рибуты для определения точности и масштаба. В диалекте 1 числа с фиксирован- ной точкой хранятся как 16- или 32-битовые целые, а числа с точностью, превы- шающей 10, преобразуются для хранения в 64-битовый тип числа с плавающей точкой double precision. Ваши данные, вероятно, вызовут ошибку переполнения, если клиент диалекта 3 выдаст запрос на сохранение числа в базе данных диалек- та 1, или сгенерируют ошибочный результат, когда клиент диалекта 1 выдаст за- прос на операции с числами в базе данных диалекта 3. ♦ Генераторы в диалекте 3 являются 64-битовыми целыми, в то время как в диалек- те 1 генераторы — 32-битовые целые. ♦ Арифметические операции в диалекте 3 были взяты из стандарта SQL-92, в то время как диалект 1 использует нестандартные правила. Например, деление цело- го на целое в диалекте 3 возвращает усеченное целое, в то время как в диалекте оно вернет число с плавающей точкой двойной точности. Если ваше приложение сохраняет результат выражения, включающего подобную арифметическую опе- рацию, "ошибочные" результаты будут сохранены без возбуждения исключения. ♦ Оба диалекта имеют тип данных даты и времени с именем date, но это разные типы. В диалекте 1 тип date эквивалентен типу timestamp диалекта 3, а в диалек- те 3 date является типом, хранящим только дату; этот тип не поддерживается в диалекте 1. ♦ Диалект 3 поддерживает тип time (время дня), который отсутствует в диалекте 1. ♦ В базах данных диалекта 3 Firebird поддерживает соглашения ANSI SQL по иден- тификаторам с разделителями, которые заключаются в двойные кавычки; такие
Гпава 8. О типах данных Firebird 165 идентификаторы не могут использоваться в диалекте 1. Несоответствие диалектов клиента и базы данных приведет к исключениям и некорректной работе. ♦ Диалект 3 имеет больше зарезервированных ключевых слов, чем диалект 1. Су- ществующие базы данных диалекта 1, которые используют новые ключевые слова в качестве идентификаторов, не будут работать с клиентом диалекта 3. ♦ Диалекты 1 и 3 ведут себя по-разному в случае неявного преобразования типов. Это может стать проблемой, если вы хотите конвертировать существующую базу данных в диалект 3 и изменять использующие ее приложения. Диалект 2 Не существует такого предмета, как "база данных диалекта 2". Диалект 2 является клиентской установкой, которую вы можете использовать для проверки переходных требований типов данных при конвертировании базы данных диалекта 1 в диалект 3. Inprise Corporation (теперь Borland) разработала документ "Migration Guide" (Руко- водство по миграции) для InterBase 6.0 в 2000 году, где подробно описаны действия по конвертированию баз данных диалекта 1 в диалект 3. Этот документ в формате PDF доступен на некоторых сайтах сообщества Firebird. Как определять диалект Вызовите окно командной строки и перейдите в каталог /bin, где находятся инстру- менты командной строки Firebird. Запустите утилиту isql. Соединитесь с вашей базой данных: SQL> CONNECT '/opt/firebird/examples/employee.fdb' CON> user 'SYSDBA' password 'icur2yy4m'; SQL> Затем введите следующую команду ISQL: SQL> SHOW SQL DIALECT; Client SQL dialect is set to: 3 and database dialect is: 3 (Диалект SQL клиента установлен в: 3, диалект базы данных: 3) Это хорошо. Если вы найдете несоответствие, это ничему не повредит, если вы не будете пытаться добавлять или изменять данные. Вы должны принять меры, чтобы гарантировать использование клиентом корректного диалекта. Изменение диалекта клиента в isql Предположим, что сейчас в isql вы хотите закрыть ваше соединение с текущей базой данных и соединиться с другой базой данных, о которой вы знаете, что она в диалек- те 1. Вот что вы делаете: SQL> COMMIT; SQL> SET SQL DIALECT 1; WARNING: client SQL dialect has been set to 1 when connecting to Database
166 Часть III. Типы данных Firebird и домены SQL dialect 3 database. (Предупреждение: SQL-диалект клиента установлен в 1 при соединении с базой данных с SQL-диалектом 3) SQL> Здесь все в порядке, потому что вы только собираетесь соединиться с базой данных диалекта 1: SQL> CONNECT 'RSERVER:D:\DATA\SAMPLE\legacy.gdb' CON> user 'SYSDBA' password 'icur2yy4m'; SQL> SHOW SQL DIALECT; Client SQL dialect is set to: 1 and database dialect is: 1 (Диалект SQL клиента установлен в: 1, диалект базы данных: 1) Множество свободно распространяемых и коммерческих инструментов администра- тора с графическим интерфейсом предоставляют возможность интерактивной уста- новки диалекта клиента. Компоненты доступа к базе данных и драйверы имеют свой- ства или другие механизмы для передачи диалекта структуре соединения API. Пора дальше Следующие четыре главы подробно описывают типы данных, поддерживаемые для каждой из основных категорий данных: числа, дата/время, символы и BLOB. Гла- ва 13, последняя глава в этой части, описывает реализацию доменов в Firebird для объединения типа данных с группой атрибутов в одно воспроизводимое определение.
ГЛАВА 9 Числовые типы данных Firebird поддерживает числовые типы данных с фиксированной точкой (точные чис- ла) и с плавающей точкой (приблизительная точность). Десятичными типами с фик- сированной точкой являются целые типы с нулевым масштабом smallint, integer и в диалекте 3 bigint, а также два почти одинаковых масштабируемых числовых типа: numeric и decimal. Два типа с плавающей точкой: float (низкая точность) и double PRECISION1. Firebird не поддерживает беззнаковый целочисленный тип. В табл. 9.1 показаны диапазоны значений каждого числового типа в Firebird. Таблица 9.1. Границы числовых типов Firebird Числовой тип Минимум Максимум SMALLINT -32,768 32,767 INTEGER -2,147,483,648 2,147,483,647 BIGINT _263 283 - 1 (Для мазохистов) -9223372036854775808 9223372036854775807 NUMERIC* Меняется Меняется DECIMAL* Меняется Меняется FLOAT Положительные 1.175x10‘38 3.402x1038 Отрицательные -3.402Х1038 DOUBLE PRECISION Положительные 2.225x10’308 1.797ХЮ308 Отрицательные -1.797x10308 * Границы для типов numeric и decimal изменяются в зависимости от способа хранения и масштаба. Границы всегда будут соответствовать тому типу, в котором эти данные будут сохраняться* 2. Точность задает количество значащих цифр (игнорируя конечные или начальные нули), которое может храниться в типе данных без переполнения или потери данных. 2 Хранимыми типами являются smallint, integer или bigint в зависимости от объявлен-
168 Часть III. Типы данных Firebird и домены Операции с числовыми типами ♦ Операции сравнения. Используйте стандартные операторы отношений (=, <, >, >=, <=, о или !=)3. Возможны сравнения строк с использованием таких операторов SQL, как containing, starting with и like. В данных операциях числа трактуются как строки. Более подробную информацию об этих операторах см. в главе 21. ♦ Арифметические операции. Могут быть использованы стандартные бинарные арифметические операторы (+, * и /). ♦ Операции преобразования. Firebird автоматически выполняет преобразования ме- жду числами с фиксированной точкой, с плавающей точкой и строковыми типами данных при выполнении операций над смешанными типами данных. Когда опе- рация является сравнением или арифметической операцией, включающей число- вые и нечисловые типы данных, то сначала данные преобразуются в числовой тип, а затем выполняется операция. ♦ Операции сортировки. По умолчанию запрос возвращает строки точно в том по- рядке, в котором находит их в таблице, т. е., скорее всего, неупорядоченные. Вы можете отсортировать строки по значениям целочисленных столбцов, используя предложение order by оператора select в убывающем или возрастающем порядке. Если числа сохраняются как символьные типы, то порядок сортировки будет ал- фавитно-цифровым, а не числовым, например, 1—10—11 ... 19—2. Целые типы Все целые типы являются точными знаковыми числами с нулевым масштабом. Firebird поддерживает три вида разной точности целых типов данных: ♦ smallint — является знаковым коротким целым с диапазоном от -32,768 до 32,767; ♦ integer — является знаковым длинным целым с диапазоном от-2 147 483 648 до 2 147 483 647; ♦ bigint — является знаковым 64-битовым целым с диапазоном от 2 63 до 263 - 1. Недоступен в диалекте 1. ПРИМЕЧАНИЕ. В Firebird 1.0.x в диалекте 3 объявляйте 64-битовые целые как numeric(18,0) или decimal (18,0). Всегда допустимо использование этого синтак- сиса для целых типов, при этом можно опускать второй аргумент (масштаб). 3 Использование "!=" в качестве замены для "<>" допустимо в Firebird, но не соответствует стандарту. Те, у кого хорошее экстрасенсорное восприятие, будут придерживаться "о", чтобы сделать код более удобным для чтения.
Глава 9. Числовые типы данных 169 Более подробную информацию о масштабе, точности и операциях, которые могут выполняться для чисел с фиксированной точкой, см. далее в разд. "Масштабируемые типы с фиксированной точкой". Следующие два оператора создают домен и столбец, соответственно, с типами дан- ных SMALLINT и integer: CREATE DOMAIN RGB_RED_VALUE AS SMALLINT; /“/ CREATE TABLE STUDENT_ROLL ( STUDENT_ID INTEGER, . . . ) ; Каждый из следующих операторов создает домен, который является 64-битовым це- лым: CREATE DOMAIN IDENTITY BIGINT CHECK (VALUE >=0); /* Firebird 1.5 и выше */ CREATE DOMAIN IDENTITY NUMERIC(18,0) CHECK (VALUE >=0); SMALLINT smallint является 2-байтовым целым, предоставляющим компактное хранение для целых чисел с ограниченным диапазоном значений. Например, smallint может быть подходящим для хранения значений цветов в форме RGB, как показано в предыду- щем примере создания домена. smallint часто используется для определения булевых значений, обычно 0 = ложь, 1 = истина. Пример такого использования можно найти в разд. "Объявление булевых доменов" главы 13. INTEGER integer является 4-байтовым целым. В диалекте 1 генераторы (см. разд. "Генерато- ры" этой глвавы) генерируют значения типа integer. Вы можете хранить такие целые в столбцах bigint без преобразования. BIGINT, NUMERIC(18,0) Доступны только в диалекте 3. Это 8-байтовое целое, полезное для хранения целых чисел с очень маленькими и очень большими значениями. В диалекте 3 генерируют- ся числа типа bigint (см. разд. "Генераторы"). /\ ВНИМАНИЕ! В диалекте 1 базы данных Firebird отвергает все попытки объявить ! \ домен или столбец как bigint. Сообщение об ошибке не будет выдано, если вы —“4- попытаетесь объявить домен или столбец как numeric(18,0), однако они будут молча объявлены как double precision. Поскольку точность является проблемой при использовании чисел с плавающей точкой, будьте внимательны и не допус- кайте неправильного использования numeric(18, о) в диалекте 1 базы данных для объявления столбца, который вы собираетесь использовать в качестве клю- ча для поиска или объединения.
170 Часть III. Типы данных Firebird и домены Автоинкремент или тип IDENTITY Firebird не поддерживает типы автоинкремента или identity, которые вы могли встретить в других системах управления базами данных. Что у него есть, так это средство числовых генераторов и возможность поддерживать независимые, имено- ванные серии чисел bigint. Каждая серия известна как генератор. Техника их ис- пользования для реализации и поддержки первичных ключей и других автоматиче- ских инкрементных серий описана в главе 31. Генераторы Генераторы являются идеальным средством для создания значений автоинкремент- ных уникальных ключей или серий значений числового столбца, а также других се- рий. Генераторы в базе данных объявляются оператором create, как и любой другой объект базы данных: CREATE GENERATOR AGenerator; Генераторам может быть присвоено любое начальное значение: SET GENERATOR AGenerator ТО 1; Д ВНИМАНИЕ! Существуют строгие предупреждения по поводу переустановки значений генераторов, когда эти значения находятся в использовании — см. разд. "Предупреждения о переустановке значений генераторов" в этой главе. Получение следующего значения Для получения следующего значения вызывайте функцию SQL geh_id(Имягенератора, п), где Имягенератора— имя генератора, а п— целое (диалект 1) или numeric(18,0) (диалект 3), определяющее значение шага. Запрос: SELECT GEN_ID(AGenerator, 2) from RDB$DATABASE; возвращает число, которое на 2 больше последнего сгенерированного числа, и уста- навливает значение генератора в сгенерированное значение. Текущее значение генератора Следующая строка: SELECT GEN_ID(AGenerator, 0) from RDB?DATABASE; возвращает текущее значение генератора без его увеличения4. 4 Часто таблица rdb$database используется для "пустых" запросов, которые возвращают одно вычисляемое значение или контекстную переменную. Это реальная системная таблица, содержащая одну и только одну строку. Она используется как dual в Oracle.
Гпава 9. Числовые типы данных 171 PSQL, язык программирования Firebird, позволяет напрямую присваивать сгенериро- ванное значение переменной: DECLARE VARIABLE MyVar BIGINT; MyVar = GEN_ID(AGenerator, 1); Более подробную информацию об использовании генераторов в модулях PSQL — в особенности в триггерах — см. в главе 29s. Использование отрицательного шага Аргумент шаг в gen id может быть отрицательным. Следовательно, можно устанав- ливать или переустанавливать текущее значение генератора, передавая отрицатель- ный аргумент или в виде целой константы, или в виде целого выражения. Эта воз- можность иногда используется как "трюк" для установки значений генератора в PSQL, поскольку в PSQL не могут использоваться такие команды DDL, как set GENERATOR. Например, оператор: SELECT GEN_ID (AGenerator, -GEN_JD (AGenerator, 0)) from RDB$DATABASE; устанавливает значение генератора в ноль. Предупреждения о переустановке значений генераторов Основное простое правило по переустановке значений генераторов в работающей базе данных — будь то в SQL, PSQL или в некотором интерфейсе администратора — не делать этого. Основное достоинство значений генератора то, что они гарантированно являются уникальными. В отличие от других доступных пользователю операций Firebird гене- раторы работают вне контекста транзакций. Однажды сгенерированное, число ус- тановлено и не может быть изменено отменой транзакции. Это дает полную уверен- ность в том, что ничто не может вмешиваться в целостность последовательности чи- сел, предоставляемых генератором. Оставьте переустановку значений генераторов в создаваемой базе данных для редких случаев, когда это требуется условиями проектирования. Например, некоторые бух- галтерские системы, написанные в старом стиле, передают журналы в таблицы исто- рии с новым первичным ключом, очищают таблицу журналов и устанавливают по- 5 Несмотря на то, что в третьем диалекте генераторы являются числами bigint, совершен- но необязательно столбец первичного ключа, для которого будет применяться генератор, так- же делать bigint. При выборе типа данных для столбца первичного ключа следует учитывать конкретные нужды предметной области. Как правило, в большинстве случаев вполне доста- точно INTEGER. — Прим. науч. ред.
172 Часть III. Типы данных Firebird и домены следовательность первичных ключей в ноль в организациях с несколькими филиала- ми, выделяя диапазоны значений ключа каждого филиала в отдельный "фрагмент", чтобы гарантировать целостность ключей при репликации. Никогда не переустанавливайте значения генератора в попытке скорректировать про- граммные ошибки, ошибки ввода данных или для "устранения промежутков" в по- следовательности значений6. Масштабируемые типы с фиксированной точкой Типы с фиксированной точкой позволяют управлять числами, которые должны вы- числяться с дробной частью, задающейся количеством цифр после десятичной точки, или масштабом. Обычно масштабируемые типы нужны для финансовых значений и других чисел, которые являются результатом подсчета или выполнения арифметиче- ских операций над целыми элементами и частями элементов. Предсказуемость результатов умножения и деления чисел с фиксированной точкой позволяет выбрать их для хранения денежных значений. Тем не менее, поскольку типы с фиксированной точкой имеют ограниченные "рамки", в которых могут раз- мещаться числа, они являются причиной появления исключений переполнения или потери значимости возле их верхней и нижней границы. В странах, где единицы ва- люты представляют небольшие значения, нужно быть внимательным при выборе ограничений чисел. Например, следующий оператор использует ставку налога (decimal(5,4)) и чистую прибыль (NUMERIC (18,2)): UPDATE ATABLE SET INCOME_AFTER_TAX = NET_PROFIT - (NET_PROFIT * TAX_RATE) ; Пусть ставка налога будет 0.3333. Пусть чистая прибыль будет 1234567890123456.78. Результат: ISC ERROR CODE:335544779 Integer overflow. The result of an integer operation caused the most significant bit of the result to carry. (Переполнение целого числа. Результат целочисленной операции привел к переносу большинства значащих битов) 6 Существуют техники для поддержки непрерывных последовательностей с использовани- ем генераторов, которые могут быть реализованы в случае абсолютной необходимости по- вторного применения неиспользуемых промежутков в сериях, не являющихся значениями ключа. Список Tech Info "Контролируемые серии чисел" (An Auditable Series of Numbers) опи- сывает подобную технику на http://www.ibobjects.com/TechInfo.html.
Гпава 9. Числовые типы данных 173 Firebird предоставляет два типа данных чисел с фиксированной точкой или масшта- бируемых: numeric и decimal. Каждый масштабируемый тип объявляется как type(p, s), где р определяет точность (количество значащих цифр), as — масштаб (размещение десятичной точки— т. е. количество цифр справа от символа десятич- ной точки). В соответствии со стандартом SQL-92 оба типа numeric и decimal ограничивают хра- нимое число объявленным масштабом. Различие между этими двумя типами заклю- чается в способе, каким ограничивается точность. Точность должна быть такой, "как объявлено" для столбцов типа numeric, в то время как столбцы decimal могут получать числа, чья точность ио меньшей мере равна тому, что было объявлено, больше гра- ницы реализации. Типы numeric и decimal, как они реализованы в Firebird, являются идентичными, за исключение случая, когда точность меньше пяти. Оба типа действительно соответ- ствуют стандарту типа decimal, numeric не соответствует SQL-92. Внутренне Firebird хранит масштабированное число как типы smallint (16 бит), integer (32 бита) или bigint (64 бита) в соответствии с объявленным размером точно- сти. Его объявленная точность7 8 сохраняется вместе с объявленным масштабом в виде отрицательного множителя масштаба3, представляющего степень числа 10. Когда к числу происходит обращение для вывода или для расчетов, оно получается произве- . п множитель масштаба ден^ем хранимого целого на 10 Например, для столбца, объявленного как numeric(4,3), Firebird сохраняет внутренне число в виде smallint. Если вы вводите число 7.2345, Firebird без сообщений округ- ляет самую правую цифру (4) и сохраняет 16-битовое целое 7235 и множитель мас- штаба-3. Это число будет найдено как 7.235 (7235 * 103). Тип данных NUMERIC Формат типа данных numeric: NUMERIC(p,s) Например, numeric (4,2) определяет число, состоящее не более чем из четырех цифр, включая две цифры справа от десятичной точки. Следовательно, числа 89.12 и 4.321 будут сохранены в столбце numeric<4,2) как 89.12 и 4.32 соответственно. Во втором примере последняя цифра Г3 выходит за пределы масштаба и просто отбрасывается. И все же возможно хранение в этом столбце числа с большей точностью, чем было объявлено. Максимально здесь может быть 327.67 — т. е. число с точностью 5. По- скольку база данных хранит фактическое число как smallint, числа не начинают вы- зывать ошибки переполнения, пока внутренне хранимое число не станет больше 32,767 или меньше -32,768. 7 Хранится в системной таблице rdb$fields как rdb$field_precision. 8 Хранится в системной таблице rdb$ fields как rdb$field_scale.
174 Часть III. Типы данных Firebird и домены Тип данных DECIMAL Формат типа данных decimal: DECIMAL(р,S) Как и в случае numeric, decimal(4,2) определяет число, состоящее, по меньшей мере, из четырех цифр, включая две цифры справа от десятичной точки. Тем не менее, по- скольку Firebird сохраняет данные decimal с точностью 4 и меньше как integer, этот тип может в столбце decimals,1) потенциально хранить число от 214 748 364.7 до -214 748 364.8 без появления ошибки переполнения. Точные числа могут быть перепутаны, не только по причине тонкой разницы между этими двумя типами, но и потому, что диалект базы данных влияет на доступный диапазон точности. Табл. 9.2 может служить руководством по тому, какую точность и масштаб вам нужно указать для различных требований к вашим числам. Таблица 9.2. Диапазон и способ хранения в Firebird типов данных numeric и decimal Точность Тип Диалект 1 Диалект 3 1—4 NUMERIC SMALLINT SMALLINT 1—4 DECIMAL INTEGER INTEGER 5—9 NUMERIC И DECIMAL INTEGER INTEGER 10—18 NUMERIC И DECIMAL BIGINT double precision* * Точные числа с точностью больше чем 9, могут быть объявлены в диалекте 1 базы данных без появ- ления исключения. Эти числа будут храниться как DOUBLE PRECISION и будут подчиняться тем же са- мым ограничениям точности, что и любые числа с плавающей точкой. В процессе преобразования ба- зы данных диалекта 1 в диалект 3 клиент диалекта 2, открыв таблицу, содержащую столбцы DECIMAL или NUMERIC с точностью, большей 9, получит сообщение об ошибке. Более подробную информацию о преобразованиях диалекта 1 в диалект 3 см. в разд. "Специальная тема миграции: диалекты SQL" главы 8. Конвертированные базы данных Если база данных диалекта 1 была обновлена до диалекта 3 с использованием созда- ния резервной копии gbak, а затем восстановлена, то числовые поля, определенные с точностью, большей 9, сохранят тип данных double precision. Хотя они будут пред- ставлены так, как были вначале определены (например, numeric (15,2)), они будут сохраняться и использоваться в вычислениях как double precision. Более подробную информацию о преобразованиях баз данных диалекта 1 в диалект 3 см. в разд. "Специальная тема миграции: диалекты SQL" главы 8. Специальные ограничения в статическом SQL Включающий язык встроенных приложений не может использовать или распознавать малые точности типов данных numeric или decimal с дробной частью, когда они внут- ренне хранятся как типы smallint или integer. Для устранения такой проблемы в лю-
Гпава 9. Числовые типы данных 175 бой базе данных, к которой предполагается доступ из таких встроенных приложений (ESQL): ♦ не определяйте столбцы или домены с типом numeric или decimal малой точности в базе данных диалекта 1. Или храните данные как целые и пишите в приложени- ях код, учитывающий масштаб, или используйте double precision и применяйте подходящий алгоритм округления для вычислений; ♦ в базе данных диалекта 3 определяйте столбцы или домены любого размера типа numeric и decimal, используя точность не меньше 10, чтобы получить их внутрен- нее хранение как bigint. Указывайте масштаб, если вы хотите управлять точ- ностью и масштабом. Используйте ограничения check, если вам нужно контроли- ровать диапазоны значений. Поведение типов с фиксированной точкой в операциях Деление При выполнении деления типов с фиксированной точкой диалекты 1 и 3 ведут себя по-разному. В диалекте 3, когда оба операнда являются типами с фиксированной точкой, Firebird суммирует масштабы обоих операндов для определения масштаба результата (част- ного). Частное имеет точность 18. При проектировании запросов с выражениями, содержащими деление, убедитесь, что частное всегда будет иметь точность больше, чем любой из операндов, и примите меры предосторожности в случаях, когда точ- ность потенциально может превысить допустимый максимум 18. В диалекте 1 деление всегда создает частное типа double precision. Примеры В диалекте 3 частное от деления decimal(12,3) на decimalo,2) будет decimal(18,5). Масштабы суммируются: SELECT 11223344.556/1234567.89 FROM RDB$DATABASE Это дает 9.09090. Посмотрим, чем отличается частное, когда этот же запрос выполняется в диалекте 1. Первый операнд трактуется как число double precision, потому что его точность (12) превышает максимум масштабируемого типа для диалекта 1. Частное также является числом double precision. Результат 9.09090917308727 по причине ошибок, присущих типам с плавающей точкой. Для следующей таблицы, определенной в диалекте 3, операции деления дают раз- личные результаты. CREATE TABLE tl ( il INTEGER, i2 INTEGER,
176 Часть III. Типы данных Firebird и домены nl NUMERIC(16, 2) , n2 NUMERIC(16,2)); COMMIT; INSERT INTO tl VALUES (1, 3, 1.00, 3.00); COMMIT; Следующий запрос возвращает значение 0.33 типа numeric(18,2), потому что сумма масштабов 0 (операнд 1) и 2 (операнд 2) равна 2: SELECT il/n2 from tl Следующий запрос возвращает значение 0.3333 типа numeric (18,4), потому что сум- ма масштабов двух операндов равна 4: SELECT п1/п2 FROM tl Деление целого на целое Используя предыдущий пример, следующий запрос в диалекте 3 вернет целое 0, по- тому что каждый операнд имеет масштаб 0, следовательно, сумма масштабов бу- дет 0: SELECT il/i2 FROM tl В диалекте 1, как и в большинстве других СУБД, деление одного целого на другое целое даст результат с плавающей точкой типа double precision: SELECT 1/3 AS RESULT FROM RDB$DATABASE Это дает .333333333333333. Хотя настоящее правило диалекта 1 является интуитивным для языков программиро- вания, оно не соответствует стандарту SQL-92. Целые типы имеют масштаб 0. Для согласованности это требует, чтобы результат (частное) любой операции деления целого на целое соответствовал правилам масштабирования для чисел с фиксирован- ной точкой и был бы целым. Диалект 3 соответствует стандарту и усекает частное от операций деления целого на целое до целого. Следовательно, следующий оператор является неразумным: SELECT 1/3 AS RESULT FROM RDB$DATABASE Он вернет 0. Если вам нужно сохранить дробную часть в результате (в частном) деления целого на целое в диалекте 3, убедитесь, что у одного из операндов присутствует нужный мас- штаб, или включите "множитель" в выражение, чтобы гарантировать масштаб ре- зультата. Примеры: SELECT 1.00/3 AS RESULT FROM RDB$DATABASE Вернет .33. SELECT (5 * 1.00)/2 AS RESULT FROM RDB$DATABASE Этот вернет 2.50.
Гпава 9. Числовые типы данных 177 Диалект 1 базы данных с диалектом 3 клиента База данных диалекта 1, которая была открыта клиентом диалекта 3, может препод- нести некоторые сюрпризы в отношении деления целых чисел. Когда операция вы- полняет нечто, что приводит к проверке условия check, или выполняется хранимая процедура или триггер, то осуществляемые действия основаны на диалекте, на кото- ром check, хранимая процедура или триггер были определены, а не на действующем диалекте, на котором приложение выполняет проверку, хранимую процедуру или триггер. Например, в базе данных диалекта 1 таблица содержит столбцы mycoli (integer) и mycol2 (integer) и следующее условие check, которое было определено, когда база данных имела диалект 1: CHECK(MYCOLI / MYCOL2 >0.5) Пусть теперь пользователь запускает isql или приложение, задав диалект 3. Програм- ма пытается добавить строку в конвертированную базу данных: INSERT INTO MYTABLE (COL1, COL2) VALUES (2,3); Поскольку ограничение check было определено в диалекте 1, оно вернет частное 0.666666666666667, и строка будет соответствовать условию check. Обратное также верно. Если то же ограничение check было добавлено в базу данных диалекта 1 из клиента диалекта 3, то для ограничения будет сохранена арифметика диалекта 3. Предыдущий оператор insert не будет выполнен, потому что проверка вернет значение частного 0, которое нарушает это ограничение. СОВЕТ. Мораль всего этого: используйте базы данных диалекта 3 и всегда со- Vj? единяйтесь с ними, применяя диалект 3. Если вы собираетесь использовать С* Firebird, то обновите все существующие базы данных до диалекта 3 — желатель- но описав новую базу данных и поместив в нее ваши старые данные — таким образом вы сохраните покой и сможете избежать уймы неприятных сюрпризов. Умножение и деление Если оба операнда являются точными числами, умножение операндов даст точное число с масштабом, равным сумме масштабов операндов. Например, CREATE TABLE tl ( nl NUMERIC(9,2), n2 NUMERIC (9,3) ) ; COMMIT; INSERT INTO tl VALUES (12.12, 123.123); COMMIT; Следующий запрос возвращает число 1492.25076, потому что nl имеет масштаб 2, а п2 — масштаб 3. Сумма масштабов 5. SELECT П1*п2 EROM tl
178 Часть III. Типы данных Firebird и домены В диалекте 3 точность результата умножения чисел с фиксированной точкой будет равна 18. Нужно принять меры предосторожности, чтобы быть уверенным, что не будет переполнения результата при распространении масштаба в умножении. В диалекте 1, если распространение масштаба приводит к тому, что вычисление даст результат с точностью больше 9, то результатом будет double precision. Сложение и вычитание Если все операнды являются точными числами, то сложение и вычитание операндов даст точное число с масштабом, равным максимальному масштабу операндов. Например, CREATE TABLE tl ( nl NUMERIC(9, 2) , n2 NUMERIC(9, 3)) ; COMMIT; INSERT INTO tl VALUES (12.12, 123.123); COMMIT; SELECT nl + n2 FROM tl; Этот запрос возвращает 135.243, выбирая максимальный масштаб операндов. Анало- гично, следующий запрос возвращает число -111.003: SELECT nl - n2 FROM tl; В диалекте 3 результат любого сложения или вычитания имеет тип numeric(18,п). В диалекте 1 он имеет тип numeric (9, п), где п — масштаб максимального операнда. Числовой ввод и показатели степени Любые числовые строки в DSQL, которые могут быть сохранены как decimal (18, S), вычисляются без потери точности, что могло бы произойти при промежуточном со- хранении в виде double. Синтаксический анализатор DSQL можно заставить распо- знавать числовые строки как числа с плавающей точкой при использовании научной нотации — если добавить символ "е" или "Е" перед показателем степени, который может быть нулевым. Например, DSQL распознает 16.92 как масштабируемое точное число и передаст его серверу в этой форме. С другой стороны, он будет трактовать 16.92Е0 как значение с плавающей точкой. Типы данных с плавающей точкой Типы данных с плавающей точкой служат "скользящими окнами" с точностью, под- ходящей масштабу числа. По своей природе в "плавающих" типах положение деся- тичной точки не зафиксировано — допустимо хранение в одном и том же столбце одного значения как 25.33333, а другого как 25.333. Эти значения различны и оба допустимы.
Гпава 9. Числовые типы данных 179 Определяйте столбцы с плавающей точкой, когда вам нужно хранить числа с изме- няющимся масштабом. Основное простое правило выбора типа с плавающей точкой вместо типа с фиксированной точкой: "используйте их для значений, которые вы из- меряете, а не которые вы считаете". Если столбец или переменная типа плавающей точки должны использоваться для хранения денежных величин, вам нужно быть внимательным к округлению и результатам вычислений. Числа с плавающей точкой могут быть использованы для представления значений, больших, чем это возможно для масштабируемых целых. Например, тип float может содержать числа с абсолютным значением не более 3.4Е38 (т. е. 34, за которым сле- дует 37 нулей) и не менее 1.1Е-38 (37 нулей, 11 и затем десятичная точка). Ширина диапазона достигается за счет потери точности. Число с плавающей точкой содержит приблизительное представление его значения с точностью до указанного количества цифр (его точности) в соответствии с текущим значением (масштабом). Оно не может содержать значение за пределами его диапазона. Значение с плавающей точкой несет больше информации, чем указанное количество цифр точности. Например, тип float имеет точность 7 цифр, но его реальная точ- ность 6 цифр. Последняя часть предоставляет дополнительную информацию о числе, такую как индикатор для округления и некоторые другие вещи, важные при выпол- нении арифметических операций с числом. Например, float может содержать число 1000000000 (1 000 000 000 или 109). "Кон- тейнер" float рассматривает данное число как 100000*Е4. (Это лишь иллюстрация — полное представление реализации чисел с плавающей точкой выходит за рамки на- стоящей книги и очень далеко от того, что узнал автор!). Если вы прибавите 1 к зна- чению float, то будет проигнорирована информация в седьмом разряде, потому что она не является значимой для текущего значения числа и его точности. Если же вы прибавляете 10 000 — число, которое значимо для хранимого в типе float числа, — то результатом может быть 100001 *Е4. Даже значения с подходящей точностью числа с плавающей точкой могут не всегда храниться в точном представлении. Такие значения, как 1.93 или даже 123, могут быть представлены в памяти как значения, очень близкие к указанному числу. Эти значения достаточно близки— когда число с плавающей точкой округляется для вывода, оно будет отображать ожидаемое значение, когда оно используется в вычис- лениях, результат будет очень близким приближением к ожидаемому результату. Эффект такой: когда вы выполняете какое-либо вычисление, которое должно дать результат 123, оно может быть очень близким приближением к 123. При точных сравнениях (равенство, больше чем, меньше чем и т. д.) между двумя числами с пла- вающей точкой, между числом с плавающей точкой и нулем или числом с плаваю- щей точкой и числом с фиксированной точкой не следует рассчитывать на ожидае- мые результаты. По этой причине не следует рассматривать использование столбцов с плавающей точкой в качестве ключей или применять к ним ограничения уникальности. Они не будут работать предсказуемо для отношений внешнего ключа или в объединениях. Для сравнений проверяйте значения с плавающей точкой в предложении between с подходящим диапазоном вместо того, чтобы выполнять точную проверку. Тот же
180 Часть III. Типы данных Firebird и домены совет применим и для сравнения с нулем — выберите подходящий диапазон значе- ний и запишите проверку данных между нулем и близким к нулю значением или ме- жду двумя подходящими значениями, близкими к нулю9. В базе данных диалекта 1 необходимость хранения значений числовых данных, имеющих больший диапазон, чем предоставляет 32-битовое целое, может быть ре- шена выбором типа double precision. Ограничения диалекта 1 также требуют исполь- зования чисел с плавающей точкой для всех действительных чисел, если к базе дан- ных предполагается доступ из встроенного приложения (ESQL). Firebird предоставляет два приближенных числовых типа данных с плавающей точкой (float и double precision), отличающиеся только размером точности. FLOAT float является 32-битовым типом данных с плавающей точкой с приблизительно 7 цифрами точности— для надежности предполагайте 6 цифр. Число с 10 цифрами 25.33333312, добавленное в столбец float, сохраняется как 25.33333. Диапазон чисел от -3.402 х 1038 до 3.402 х 1038. Наименьшее положительное число, которое может быть сохранено, 1.175 * 10 3S. DOUBLE PRECISION double precision является 64-битовым типом данных с плавающей точкой с прибли- зительно 15 цифрами точности. Диапазон от -1.797 х 1О308 до 1.797 х 1О308. Наи- меньшее положительное число, которое может быть сохранено, 2.225 х 1О'308. Арифметические операции над смешанными типами с фиксированной и плавающей точкой Когда бинарная операция (сложение, вычитание, умножение и деление) включает в качестве операндов точные числа и числа с плавающей точкой, то результат будет типа double precision. Следующий оператор создает столбец percent_change, используя тип double precision: CREATE TABLE SALARY_HI STORY ( PERCENT__CHANGE DOUBLE PRECISION DEFAULT 0 NOT NULL CHECK (PERCENT_CHANGE BETWEEN -50 AND 50), ) ; 9 Спасибо Джеффу Ворбойзу (Geoff Worboys) за его понятные замечания по этой больной теме.
Гпава 9. Числовые типы данных 181 Следующий оператор create table дает пример использования различных числовых типов данных: integer для общего количества заказов, с фиксированной точкой decimal для общей суммы продаж в долларах и float для скидки к продаже: CREATE TABLE SALES (. . QTY_ORDERED INTEGER DEFAULT 1 CHECK (QTY_ORDERED >= 1), TOTAL_VALUE DECIMAL (9,2) CHECK (TOTAL_VALUE >= 0), DISCOUNT FLOAT DEFAULT 0 CHECK (DISCOUNT >= 0 AND DISCOUNT <= D); Пора дальше В следующей главе мы рассмотрим типы данных для хранения и обработки дат и времени в Firebird.
ГЛАВА 1 О Типы даты и времени Firebird поддерживает в диалекте 3 типы данных date, time и timestamp. В диалекте 1 поддерживается только один тип данных, подобный timestamp, который, хотя и назы- вается date, не является взаимозаменяемым с типом date диалекта 3. DATE В диалекте 3 date хранит одну дату без времени — тип "только дата" — в виде 32-битового знакового целого. Хранимый диапазон дат от 1 января 0001 года до 31 декабря 9999 года1. В диалекте 1 тип date эквивалентен типу timestamp диалекта 3. Действительно, когда вы создаете новый столбец даты в базе данных диалекта 1 с использованием isql, по- является предупреждение, информирующее вас, что тип данных был переименован! SQLTYPE будет иметь ТИП ISC_TIMESTAMP. Не существует типа "только дата" в диалекте 1. Для сохранения в диалекте 1 только даты, передайте правильное значение даты и литерал времени в виде "00:00:00.0000". Литералы даты и времени обсуждаются более подробно в следующих разделах. СОВЕТ. Если вы используете isql для проверки дат диалекта 1, вы можете вклю- чать/выключать отображение времени при выводе даты, используя команду isql set time. По умолчанию вывод времени отключен. TIMESTAMP Тип данных timestamp диалекта 3 состоит из двух 32-битовых слов, хранящих дату и время. Данные хранятся как два 32-битовых целых, что эквивалентно типу date в диалекте 1. 1 Так в оригинале. В документации по InterBase указан диапазон дат от 1 января 100 г. до 29 февраля 32 768 г. — Прим, перев.
Глава 10. Типы даты и времени 183 Доли секунды Доли секунды, если хранятся, являются десятитысячными долями секунды для всех типов даты и времени. TIME В диалекте 3 time хранит время дня без даты: "только время". Для хранения исполь- зуется 32-битовое беззнаковое целое. Диапазон времени от 00:00 до 23:59:59.9999. В диалекте 1 нет эквивалента типу time. Если нужно сохранить время дня, выделите элементы часов, минут и секунд из данных date и преобразуйте в строку. Техниче- ские советы есть дальше в этой главе — обратитесь к разд. "Комбинирование EXTRACTQ с другими функциями". Интервал времени Ошибочно предполагать, что тип time может хранить интервал времени. Он не мо- жет. Для вычисления интервала времени вычтите более позднюю дату или время из более раннего. Результатом будет число numeric(18,9), выражающее интервал в днях. Поскольку точность теряется, доли секунд надо рассматривать как миллисекунды, а не десятитысячные доли секунд. Используйте обычные арифметические операции для конвертирования дней в часы, минуты или секунды, как вам требуется. Предположим, что столбцы started и finished имеют тип timestamp (date в диалек- те 1). Для вычисления и сохранения в столбце time_elapsed типа double precision ин- тервала времени в минутах вы можете использовать следующее2: UPDATE ATABLE SET TIME_ELAPSED = (FINISHED - STARTED) * 24 * 60 WHERE ((FINISHED IS NOT NULL) AND (STARTED IS NOT NULL)); Литералы даты Литералы даты являются "читаемыми человеком" строками, заключенными в апо- строфы. Их сервер Firebird распознает как константы даты или даты-и-времени для extract и других выражений, операций insert и update, а также в предложении where оператора select. Литералы даты используются, когда нужно передать константы даты: ♦ операторам select, update и delete в условия поиска предложения where; ♦ операторам insert и update для ввода констант даты и времени; ♦ аргументу from функции extract (). 2 Для того чтобы осуществлять агрегацию интервала (суммирование, среднее), необходимо хранить не время окончания интервала, а его длительность, например в секундах, в обычном целочисленном типе данных. — Прим. науч. ред.
184 Часть III. Типы данных Firebird и домены Распознаваемые форматы литералов даты и времени Количество форматов строк, распознаваемых как литералы даты, ограничено. Эти форматы используют шаблоны для подстановки элементов строк. Табл. 10.1 описы- вает используемые соглашения. Таблица 10.1. Элементы литералов даты Элемент Представление сс Столетие. Первые две цифры года (например, 20 для двадцать первого века) YY Год столетия. Firebird всегда сохраняет полное значение года, даже если год был введен без сегмента сс, при этом используется алгоритм "скользящего окна" для определения того, какое столетие сохранять ММ Месяц— целое в диапазоне от 1 до 12. В некоторых форматах требуется две цифры МММ Месяц — ОДИН ИЗ [JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, dec] . Также допустимы полные английские названия месяцев DD День месяца— целое в диапазоне от 1 до 31. В некоторых форматах требуется две цифры. Неверное значение дня для конкретного месяца вызывает ошибку НН Часы — целое в диапазоне от 00 до 23. Требуются две цифры NN Минуты — целое в диапазоне от 00 до 59. Требуются две цифры SS Полные секунды — целое в диапазоне от 00 до 59. Требуются две цифры nnnn Десятитысячные доли секунды в диапазоне от 0 до 9999. Значение по умолча- нию 0000. Если используется, то требуется четыре цифры Распознаваемые форматы описаны в табл. 10.2. Таблица 10.2. Распознаваемые форматы литералов даты и времени Формат Диалект 3 date Диалект 3 TIMESTAMP Диалект 1 DATE 'CCYY-MM-DD' ИЛИ 1YY-MM-DD1 Сохраняет только дату Сохраняет дату и время в виде 00:00:00 Сохраняет дату и время в.виде 00:00:00 ’MM/DD/CCYY1 ИЛИ ’MM/DD/YY1 То же То же То же ’DD.MM.CCYY' ИЛИ 'DD.MM.YY' То же То же То же 'DD-MMM-CCYY' ИЛИ 1DD-MMM-YY' То же То же То же 'DD,МММ,CCYY1 ИЛИ 'DD,MMM,YY' То же То же То же
Глава 10. Типы даты и времени 185 Таблица 10.2 (окончание) Формат Диалект 3 date Диалект 3 TIMESTAMP Диалект 1 DATE 'DD МММ CCYY' ИЛИ 1DD МММ YY' То же То же То же 1 DDMMMCC Y Y' ИЛИ 1 DDMMMYY' То же То же То же В элементе мим также допустимы полные английские названия месяцев, нечувствитель- ные к регистру. Правильные названия приведены в табл. 10.3 'CCYY-MM-DD HH:NN:SS.nnnn' или 'YY-MM-DD НН:NN:SS.nnnn' (элемент ".nnnn" необязателен) Сохраняет только дату; может по- требовать преоб- разования для даты. Время не сохраняется Сохраняет дату и время Сохраняет дату и время 'MM/DD/CCYYHH:NN:SS.nnnn1 ИЛИ 'MM/DD/YY НН:NN:SS.nnnn' То же То же То же 'DD.MM.CCYYHH:NN:SS.nnnn1 ИЛИ 'DD.MM.YY HH-.NN: SS .nnnn' То же То же То же 'DD-MMM-CCYY HH:NN:SS.nnnn' ИЛИ DD-MMMYY HH:NN:SS.nnnn' То же То же То же Типы timestamp в диалекте 3 и date в диалекте 1 принимают дату и время в литерале даты. Литерал даты без времени будет сохранен и с временем в виде ' оо: оо: оо'. Тип date в диалекте 3 принимает только дату. Тип данных time принимает только время. "Скользящее окно века" в Firebird Независимо от того, как представлена часть года в литерале date или timestamp, в ви- де ccyy или yy, Firebird всегда сохраняет полное значение года. Он обращается к ал- горитму получения части сс (столетие). Он также всегда включает столетие при по- иске типов даты. Клиентские приложения отвечают за отображение года в виде двух или четырех цифр. Для получения столетия Firebird использует алгоритм скользящего окна. Задача за- ключается в интерпретации двухсимвольного значения года как ближайшего к теку- щему году в интервале предшествующих и последующих 50 лет. Например, если текущий год 2004, то двухсимвольные значения года будут интер- претироваться, как показано в табл. 10.3.
186 Часть III. Типы данных Firebird и домены Таблица 10.3. Определение года по двухсимвольному виду, если текущим является 2004 год Двухсимвольный год Полученный год Рассчитывается как 98 1998 (2004 - 1998 = 6) < (2998 - 2004 = 94) 00 2000 (2004 - 2000 = 4) < (2100 - 2004 = 96) 45 2045 (2004 - 1945 = 55) > (2045 - 2004 = 41) 50 2050 (2004 - 1950 = 54) > (2050 - 2004 = 46) 54 1954 (2004 - 1954 = 50) = (2054 - 2004 = 50)* 55 1955 (2004 - 1955 = 49) < (2055 - 2004 = 51) * Кажущееся равенство в этом сравнении может ввести в заблуждение. 1954 год ближе к 2004, чем 2054, потому что все даты между 1954 и 1955 годами ближе к 2004, чем все даты между 2054 и 2055 годами. Разделители в неамериканских датах Ничто не вызывает больше затруднений для интернациональных пользователей, как ограничения в Firebird на использование наклонной черты (/) только для американ- ского формата 'mm/dd/ccyy'. Хотя почти все другие страны используют формат 1 dd/mm/ccyy ', Firebird будет либо записывать неправильную дату, либо вызовет ис- ключение для литерала даты, использующего соглашение 'dd/mm/ccyy1. Например, литерал даты 112/01/2004' всегда будет сохраняться в смысле "1 декабря 2004 года'1, а 14/01/2004 ’ вызовет исключение выхода за границы диапазона, потому что не существует месяца 14. Однако допускается ’ccyy/mm/dd’: дата '2004/12/31' будет расшифровано как "31 декабря 2004 года". Обратите внимание, что Firebird не учитывает локальные форматы даты Windows или Linux при интерпретации литералов даты (ни на сервере, ни на клиенте). Его интер- претация числовых форматов дат основывается на символе-разделителе. Если точка (.) используется в качестве разделителя, Firebird интерпретирует дату в виде неаме- риканской нотации dd.mm, в то время как любой другой разделитель предполагает американскую нотацию mm/dd. Чтобы убрать американскую интерпретацию даты, ваши приложения должны преобразовать введенную дату dd/mm/ccyy в литерал, где наклонная черта заменена на символ точки в качестве разделителя. Дата 'dd.mm.ccyy' правильная. Могут быть использованы и другие форматы литералов даты. Пробелы в литералах даты Пробелы или символы табуляции могут присутствовать между элементами. Дата должна быть отделена от времени, по меньшей мере, одним пробелом. Заключение в апострофы литералов даты Литералы даты должны быть заключены в апострофы (ASCII 39). Допустимы только апострофы, а не двойные кавычки.
Глава 10. Типы даты и времени 187 Литералы месяца В табл. 10.4 показаны литералы месяцев. Таблица 10.4. Литералы месяцев и правильное английское написание Число Аббревиатура (не чувствительно к регистру) Полное название месяца (не чувствительно к регистру) 01 JAN January 02 FEB February 03 MAR March 04 APR April 05 MAY May 06 JUN June 07 JUL July 08 AUG August 09 SEP September 10 OCT October 11 NOV November 12 DEC December Примеры литералов даты Двадцать пятое число (25) шестого месяца (июнь) 2004 года может быть представле- но любым из следующих способов: '25.6.2004' '06/25/2004' 'June 25, 2004' '25.jun.2004' '6,25,2004' '25,jun,2004' '25jun2004' '6-25-04' 'Jun 25 09' '25 jun 2004' '2004 June 25' '20040625' ' 25-jun-2004' '2004-jun-25' '20040625' '25 JUN 04’ '2004-06-25' '2004,25,06' Предварительно определенные литералы даты Firebird поддерживает группу "предопределенных" литералов дат — английские сло- ва, заключенные в апострофы, которые Firebird выбирает или вычисляет и интерпре- тирует в контексте соответствующего типа даты/времени. Слова 'today', 'now, 'yesterday' и 'TCMORRROW интерпретируются, как показано в табл. 10.5.
188 Часть III. Типы данных Firebird и домены Таблица 10.5. Предварительно определенные литералы даты Литерал Тип диалекта 3 Тип диалекта 1 Значение 'NOW TIMESTAMP DATE Дата и время сервера, которые были текущими на момент старта операции DML. 'NOW будет преоб- разовано и корректно сохранено в ПОЛЯХ DATE, TIME И TIMESTAMP В диалекте 3 или в полях date в диа- лекте 1. Как и эквивалентная кон- текстная переменная currenT-Timestamp, значение все- гда сохраняется с долями секунды ' .0000' * ' TODAY' DATE date хранится с временем равным '00:00:00' Дата и время сервера, которые были текущими на момент старта операции. Если в процессе опера- ции миновала полночь, дата не изменяется. Эквивалентен контек- стной переменной диалекта 3 CURRENT_DATE. Недопустим для полей типа time 'TOMORRROW DATE date хранится с временем равным '00:00:00' Дата и время сервера, которые были текущими на момент старта операции, плюс 1 день. Если в процессе операции миновала пол- ночь, дата, из которой была вычис- лена дата 'TOMORRROW, не изме- няется. Недопустим для полей типа time 'YESTERDAY' DATE DATE хранится с временем равным '00:00:00' Дата и время сервера, которые были текущими на момент старта операции, минус 1 день. Если в процессе операции миновала пол- ночь, дата, из которой была вычис- лена дата 'yesterday', не изме- няется. Недопустим для полей типа time * Тем не менее ничто не теряется. Вы можете получить дату и время сервера с десятитысячными до- лями секунды, используя UDF GetExactTimestamp (...) из библиотеки UDF Firebird. Более подроб- ную информацию см. в приложении 1. Неявное преобразование типов в литералах даты и времени Когда литералы даты — неважно, обычные или предварительно определенные — используются в SQL в контексте с соответствующим типом даты/времени столбца или переменной, синтаксический анализатор SQL может корректно их интерпретиро- вать без преобразования. При этом в небольшом количестве случаев, когда не суще-
Глава 10. Типы даты и времени 189 ствует типизированного значения, которому синтаксический анализатор мог бы при- своить литерал даты, он трактует любой литерал даты как строку. Например, совершенно верным является затребовать, чтобы запрос select вернул константу, которая не связана ни с каким столбцом в базе данных. Основное "хакер- ство" в Firebird— использование системной таблицы rdb$database в запросе, по- скольку эта таблица имеет одну и только одну строку, и всегда можно получить ска- лярное значение: единственное контекстное значение от сервера. Следующие два примера иллюстрируют типичное использование этого приема: SELECT 'NOW' FROM RDB$DATABASE; Так как запрос возвращает константу, а не значение столбца, ее тип данных интер- претируется как char(3), 'now. Этот пример SELECT '2.09.2004' FROM RDBSDATABASE; вернет CHAR(9), '2.09.2004'. Чтобы получить от синтаксического анализатора правильную интерпретацию лите- рала даты при условии, что анализатор не может определить тип данных, используй- те функцию cast(): ♦ для диалекта 3: SELECT CAST('NOW' AS TIMESTAMP) FROM RDB$DATABASE; SELECT CAST('2.09.2004' AS TIMESTAMP) FROM RDB$DATABASE; ♦ для диалекта 1: SELECT CAST ('NOW AS DATE) FROM RDBSDATABASE; SELECT CAST('2.09.2004' AS DATE) FROM RDB$DATABASE; Контекстные переменные даты и времени Контекстные переменные даты и времени current_date, current_time и current timestamp возвращают значение даты и времени, полученные с сервера на момент начала выполнения оператора SQL, содержащего контекстную переменную. Табл. 10.6 описывает эти переменные. Таблица 10.6. Контекстные переменные даты и времени Переменная Тип диалекта 3 Тип диалекта 1 Значение CURRENT_T IME STAMP TIMESTAMP DATE Текущая дата и время, округ- ленное до секунд. Дробная часть секунд всегда возвра- щается равной '.0000' current_date DATE He поддерживается Текущая дата current_time TIME He поддерживается Текущее время, выраженное в часах, минутах и секундах после полуночи. Дробная часть секунд всегда возвра- щается равной ’ .0000'
190 Часть III. Типы данных Firebird и домены Операции, использующие значения даты и времени Использование арифметических операций в манипулировании данными, в вычисле- ниях и в отношениях между двумя датами были ранее рассмотрены в разд. "Интер- вал времени" этой главы. Возможность вычитания значения более ранней даты, вре- мени или даты-времени из более поздней существует благодаря способу хранения типов дата и время в Firebird. Способ хранения использует одно или два 32-битовых целых для даты/времени, только для даты или только для времени дня. Данные, представленные в этих числах, являются днями в длинном слове даты и дробной частью дней в слове времени. Дата представлена количеством дней с "нулевой да- ты”— 17 ноября 1898 г3. Время представлено в десятитысячных долях секунд, про- шедших с полуночи. В диалекте 3 date хранит только дату. В диалекте 3 time хранит только время. timestamp и в диалекте 1 date хранят обе части. С этими числовыми структурами можно довольно просто оперировать, используя несложные выражения сложения и вычитания для вычисления разницы во времени (интервал), увеличения или уменьшения дат, установления диапазонов даты или вре- мени. В табл. 10.7 описываются доступные операции и получаемые результаты. Таблица 10.7. Арифметические операции для типов данных даты и времени Операнд 1 Оператор Операнд 2 Результат DATE + TIME timestamp (арифметическая конкатена- ция) DATE + Числовое значение n** date, увеличенная на п целых дней (игнорируется дробная часть п, если указана) TIME + DATE timestamp (арифметическая конкатена- ция) TIME + Числовое значение n** time, увеличенное на п секунд* TIMESTAMP + Числовое значение n** timestamp, где дни увеличены на целую часть числа п плюс дробная часть числа п (если указана) как количество десяти- тысячных долей секунды в дне (8.64 х 10s) DATE - DATE Количество дней в интервале: DECIMALO, 0) DATE - Числовое значение n** date, уменьшенная на пдней (игнори- руется дробная часть п, если указана) 3 Вероятно, не будет ошибкой, если эта дата дает целое значение, называемое Измененным юлианским номером дня. Подробности см. на http://hermetic.nofadz.com/cal_stud/jdn.htm.
Глава 10. Типы даты и времени 191 Таблица 10.7 (окончание) Операнд 1 Оператор Операнд 2 Результат TIME - TIME Количество секунд в интервале: DECIMAL(9,4) TIME - Числовое значение п“ time, уменьшенное на п секунд* TIMESTAMP - TIMESTAMP Количество дней и части дня в интерва- ле: DECIMAL (18, 9) TIMESTAMP — Числовое значение п‘* timestamp, где дни уменьшены на це- лую часть числа п плюс дробная часть числа п (если указана) как количество десятитысячных долей секунды в дне (8.64x10s) * При необходимости повторяется (result=modulo(result, (24*60*60))) пока не будет выделена результирующая часть дней. ** В диалекте 3 для типа date п является целым, представляющим количество дней. Для типов данных timestamp и для диалекта 1 date п может быть числом, представляющим количество дней слева от десятичной точки (целая часть) и части дня справа от десятичной точки (дробная часть). Для типа time п является целым числом, представляющим количество секунд. Общие правила для операций Одно значение даты или времени может быть вычтено из другого, если: ♦ оба значения имеют один и mom же тип даты/времени; ♦ первый операнд является более поздним, чем второй. Вычитание, использующее типы дата/время, дает результаты: масштабируемое decimal в диалекте 3 и double precision в диалекте 1. Типы данных дата/время не могут складываться друг с другом. Однако можно вы- полнить конкатенацию части даты и части времени, используя: ♦ дополнительный бинарный синтаксис для конкатенации пар полей или перемен- ных; ♦ объединение строк для конкатенации литерала дата/время с другим литералом дата/время или с полем, или переменной типа дата/время. Операции умножения и деления, включающие типы данных дата/время, недопус- тимы. Выражения в качестве операндов Операндом при увеличении или уменьшении значения timestamp, time, date или date в диалекте 1 может быть константа или выражение. Выражение может быть особенно полезным в ваших приложениях, когда вам надо увеличить или уменьшить значение в секундах, минутах, часах или, например, требуется половина дня, а не целое коли- чество дней.
192 Часть III. Типы данных Firebird и домены Диалект 3 использует правило SQL-92 для деления целого на целое: результатом все- гда будет целое, округленное при необходимости в меньшую сторону. При использо- вании выражений в диалекте 3 убедитесь, что один из операндов является действи- тельным числом с достаточным количеством десятичных знаков, чтобы избежать возможной арифметической ошибки или потери точности результата при выполне- нии целочисленного деления SQL-92. В табл. 10.8 показаны некоторые примеры. Таблица 10.8. Примеры использования выражений в качестве операндов Вводимый операнд п Сложение или вычитание Альтернатива В секундах п/86400.0 (п*1.0)/(60*60*24) В минутах п/1440.0 (п*1.0)/(60*24) В часах п/24.0 Зависит от желаемого результата. Например, если п=3, а делитель для половины дня — 2, результат будет 1, а не 1.5 Половина дня п/2 То же Поскольку годы, месяцы, кварталы не являются константами, нужны более сложные алгоритмы для их использования в операциях даты/времени. Возможно, вам следует для этих целей посмотреть функции, определенные пользователем (UDF), которые вы сможете использовать в качестве выражений в операндах. Использование CAST() с типами дата/время В некоторых местах этой главы вы встречали функцию casto в выражениях, содер- жащих типы данных даты и литералы даты. В настоящем разделе рассматриваются различные аспекты преобразования даты и времени более широко и подробно. Преобразование между типами дата/время Обычно преобразование из одного типа дата/время в другой возможно, если исход- ный тип дата/время содержит подходящий вид данных для помещения в выходной тип дата/время. Например, timestamp содержит данные, которые можно преобразо- вать в тип только даты date или только время time, в то время как тип time не содер- жит достаточно данных для преобразования в тип date. Firebird предоставляет воз- можность преобразовывать тип date в timestamp, присваивая времени значение пол- ночи, и тип time в timestamp, выбирая дату из контекстной переменной current_date (серверное время). В табл. 10.9 представлены правила преобразования.
Глава 10. Типы даты и времени 193 Таблица 10.9. Преобразования между типами дата/время в диалекте 3 Исходный тип В ТИП TIMESTAMP В ТИП DATE В тип time TIMESTAMP Недоступно Да, преобразует дату, игнорируя вре- мя Да, преобразует время, игнорируя дату DATE Да, время устанавливается в значение полночи Недоступно Нет TIME Да, дате присваивается значение current__date Нет Недоступно DATE + TIME Да, CAST ( (DATEFIELD + TIMEFIELD AS TIMESTAMP) Нет Нет Преобразование типов даты в CHAR(n)v\ VARCHAR(n) Используйте в операторах SQL-функцию casto для трансляции данных даты и вре- мени в символьные типы данных. Firebird преобразует типы дата и время в форматированные строки, в которых дата (если присутствует) представлена в установленном формате — в зависимости от диа- лекта— а время представлено в стандартном для Firebird формате: нн:мм:зз.пппп. Необходимо использовать столбец, переменную типа char или varchar подходящего размера для получения желаемого вами результата. Преобразование типов дата/время можно выполнять в оба строковых типа фиксиро- ванной длины char и переменной длины varchar. Поскольку размер преобразуемой строки заранее известен, char имеет небольшое преимущество перед varchar: исполь- зование char при передаче по сети сэкономит вам два байта, которые добавляются к varchar для хранения размера. "Правильный размер" зависит от диалекта, обратите на это внимание, varchar может быть более подходящим при использовании в коде при- ложения, которое может обрабатывать оба диалекта. Если символьное поле слишком мало для результата, то появится исключение пере- полнения. Пусть, вам нужно получить строку, содержащую только дату из timestamp. Использование для этого символьного контейнера меньшего размера не будет рабо- тать — cast () не обрезает выходную строку. Необходимо выполнить двойное преоб- разование: вначале преобразование timestamp в date, а затем преобразование даты в символьный тип корректного размера — см. примеры в следующем разделе. Диалект 3 Преобразование date или timestamp создает дату в формате ISO (ccyy-mm-dd). Полная длина выходной строки 10 символов для date и 11 — для timestamp (дата и один про- бел перед временем). Нужно 13 символов для time или для времени в timestamp. 7 Зак. 420
194 Часть III. Типы данных Firebird и домены Например, выражение SELECT CAST(timestamp_col as CHAR(24)) AS TstampTxt FROM RDB$DATABASE; даст строку вроде следующей: 2004-06-25 12:15:45.2345 Это выдаст исключение переполнения: SELECT CAST(timestamp^col as CHAR(20)) AS TstampTxt FROM RDB$DATABASE; Двойное преобразование создает правильную строку: SELECT FIRST 1 CAST ( CAST (timestamp_col AS DATE) AS CHAR(10)) FROM tablei; Результатом будет: 2004-06-25 К сожалению, нет возможности путем прямого преобразования получить строку, со- держащую дату плюс время без дробных долей секунды. Это может быть выполнено с использованием сложного выражения, включающего функции casto и extracto. Пример см. в одном из следующих подразделов разд. "Функция EXTRACT()". Диалект 1 Дата в типе данных date диалекта 1 преобразуется в формат dd-mmm-ccyy, а не в фор- мат ISO, как в диалекте 3. Например, вот это SELECT CAST(dldate_col as CHAR(25)) AS DateTimeTxt FROM RDB$DATABASE; даст 26-JUN-2004 12:15:45.2345 Следовательно, преобразование дат в диалекте 1 требует 11 символов вместо 10 плюс 1 символ пробела плюс 13 для времени — всего 25. Более сложные выражения Преобразование может использовать более сложные выражения в комбинации с дру- гими выражениями, например: SELECT CAST (10 + CAST(('TODAY') AS DATE) AS CHAR(25)) TEXTTIME FROM RDB$DATABASE; ИЛИ SELECT CAST (10 + CURRENT_TIMESTAMP) AS DATE) AS CHAR(25)) TEXTTIME FROM RDBSDATABASE; возвращает текстовую строку, показывающую дату на 10 дней позже текущей даты.
Глава 10. Типы даты и времени 195 Преобразования между типами дата/время и другими типами данных Любой символьный тип или выражение, чье содержание может быть выражено в правильном литерале даты, может быть преобразовано в соответствующий тип дата/время. Типы данных времени и даты не могут быть участниками преобразований в типы или из ТИПОВ SMALLINT, INTEGER, FLOAT, DOUBLE PRECISION, NUMERIC, DECIMAL ИЛИ BLOB. Использование преобразований Обмен данными дата/время с другими приложениями Импорт данных дата/время, созданных в другом месте, — например, в другой систе- ме базы данных, во включающем языке или в устройстве получения даты — обычно требует некоторых предварительных действий, прежде чем данные дата/время могут быть помещены в базу данных Firebird. Большинство включающих языков программирования не поддерживают типы дан- ных date, time и timestamp, представляя их внутренне в виде строк или структур. Уст- ройства ввода данных обычно сохраняют дату и время в строках различных форма- тов и стилях. Типы дата/время чаще всего являются несовместимыми в различных базах данных. Преобразование обычно требует вычисления и декодирования содержимого элемен- тов даты в исходных данных. Второй частью процесса является реконструкция деко- дированных элементов и передача их SQL Firebird каким-нибудь способом. Во вклю- чающем языке, в котором не существует никакого способа передачи типов данных дата/время Firebird, использование функции casto в комбинации с допустимыми текстовыми строками для обработки в Firebird в качестве литералов даты может ока- заться очень полезным4. В некоторых случаях сохранение внешних данных в текстовых файлах в форматках литералов даты может быть лучшим решением. Firebird может открывать такие фай- лы, как входные таблицы в модулях на серверной стороне — хранимые процедуры или триггеры — и использовать cast () и другие функции для обработки данных в столбцах даты/времени в родных таблицах. Более подробную информацию см. в разд. "Использование внешних файлов в качестве таблицы " главы 16. Функция cast () также может быть использована для подготовки внутренних данных для экспорта. 4 API предоставляет программистам две полезные функции для преобразования структуры дата/время клиентского языка в формат Firebird и из формата Firebird. См. в документе фирмы Borland "API Guide" (Руководство по API) описание функций isc_encode_date() и isc_decode_date ().
196 Часть III. Типы данных Firebird и домены Использование в выражениях условия поиска Такие ситуации появляются, когда использование casto в предложении where с ти- пами дата/время решает логические проблемы сравнения столбца одного типа со столбцом другого типа данных. Предположим, что нам нужно объединить таблицу покупателей, которая содержит столбец balance_date типа date с таблицей транзакций покупателя, которая имеет столбец transudate типа timestamp. Нам нужно создать предложение where, которое выбирает набор данных, содержащий неоплаченные транзакции для текущего поку- пателя, появившиеся не позднее balance^date. Мы можем попытаться: SELECT... WHERE CUST_TRANS. TRANSDATE <= CUSTOMER.BALANCE_DATE; Этот критерий не даст нам того, что мы хотим! Он найдет все строки транзакций по- сле полуночи даты balance_date, потому что он вычислит balance_date с временем 00:00:00. Любая транзакция после полуночи этой даты не будет соответствовать кри- терию поиска. Что мы действительно хотим, так это включить все транзакции, где дата из trans date соответствует balance_date. Преобразование transudate в тип date сохраняет день: SELECT.. . WHERE CAST (CUSTJTRANS.TRANSDATE AS DATE) <= CUSTOMER. BALANCEJDATE; Использование в преобразовании диалекта Диалект 3 предоставляет более богатую поддержку типов дата/время, чем диалект 1. Одной из задач, которая, скорее всего, привлечет ваше внимание, если вы выполняе- те такое преобразование, является замена существующих в диалекте 1 столбцов типа date (который эквивалентен типу timestamp в диалекте 3) путем преобразования их в типы данных диалекта 3 date (только дата) или time (только время), casto легко вы- полняет эту работу. Пример одного из стилей преобразования с использованием casto см. в конце этой главы. Функция EXTRACT0 Функция extract о возвращает различные элементы, выделенные путем декодирова- ния полей типов дата/время. Она может работать с полями дата/время в диалекте 3 и в диалекте 1. Синтаксис Синтаксис функции extract (): EXTRACT (элемент FROM поле)
Глава 10. Типы даты и времени 197 элемент должен быть одним из допустимых элементов в типе данных поле. Не все элементы допустимы для всех типов данных дата/время. Тип данных элемента изме- няется в соответствии с выделяемым элементом. Табл. 10.10 перечисляет элементы, доступные для каждого типа дата/время. поле может быть столбцом, переменной или выражением, результатом вычисления которого является поле дата/время. Табл. 10.10 показывает ограничения на аргументы и их типы данных при использо- вании функции EXTRACT (). Таблица 10.10. Аргументы, типы и ограничения функции extracto Элемент Тип данных Ограни- чения TIMESTAMP/ диалект 1 date DATE TIME YEAR SMALLINT 0-5400 Допустимо Допустимо Не допустимо MONTH SMALLINT 1-12 Допустимо Допустимо Не допустимо DAY SMALLINT 1-31 Допустимо Допустимо Не допустимо HOUR SMALLINT 0-23 Допустимо Не допустимо Допустимо MINUTE SMALLINT 0-59 Допустимо Не допустимо Допустимо SECOND DECIMAL(6,4) 0-59.9999 Допустимо Не допустимо Допустимо WEEKDAY SMALLINT 0-6* Допустимо Допустимо Не допустимо YEARDAY SMALLINT 1-366 Допустимо Допустимо Не допустимо * 0 = воскресенье ... 6 = суббота. Объединение EXTRACTO с другими функциями Далее следуют два примера использования функции extracto внутри casto для по- лучения представлений даты, которые не могут быть получены использованием од- ной из функций. Получение даты и времени без долей секунды Хотя невозможно прямым преобразованием получить строку даты и времени без до- лей секунды, это может быть сделано при использовании выражения, включающего обе функции cast () и extract (). Выделение строки времени Эта техника больше нужна в диалекте 1, чем в диалекте 3. Тем не менее она может быть экстраполирована на любой тип даты или времени диалекта 3, если вам нужно сохранять время дня в виде строки. Функция extracto делает возможным выделение отдельных элементов типов даты и времени в значения smallint. Следующий триггер выделяет элементы времени из
198 Часть III. Типы данных Firebird и домены столбца диалекта 1 date с именем capture_date и преобразует их в char (13), имитируя стандартный в Firebird литерал времени 'HH:MM:ss.nnnn'. SET TERM Л; CREATE TRIGGER BI—ATABLE FOR ATABLE ACTIVE BEFORE INSERT POSITION 1 AS BEGIN IF (NEW.CAPTURE_DATE IS NOT NULL) THEN BEGIN NEW. CAPTURE—TIME = CAST(EXTRACT (HOUR FROM NEW.CAPTURE_DATE) AS CHAR(2)) | ! ':' I I CAST(EXTRACT (MINUTE FROM NEW.CAPTURE—DATE) ASCHAR(2))I| |j CAST (EXTRACT (SECOND FROM NEW.CAPTURE-DATE) AS CHAR(7)); END END Л SET TERM ;Л Пример преобразования типа дата/время Строка char(13), сохраняемая триггером в предыдущем примере, не имеет того "по- ведения," что тип time в диалекте 3. Тем не менее при простом преобразовании она может быть конвертирована напрямую в тип time диалекта 3 при последующем об- новлении до диалекта 3. Сначала мы добавляем новый временный столбец в таблицу для хранения конверти- рованной строки времени: ALTER TABLE ATABLE ADD TIME-CAPTURE TIME; COMMIT; Затем заполняем временный столбец строкой времени, выполняя преобразование в диалекте 1: UPDATE ATABLE SET TIME__CAPTURE = CAST (CAPTURE—TIME AS TIME) WHERE CAPTURE_TIME IS NOT NULL; COMMIT; Следующая вещь, которую мы должны сделать, — это временно удалить в нашем триггере ссылку на строку времени диалекта 1. Это нужно, чтобы устранить пробле- мы зависимости при изменении старой строки времени. SET TERM Л; RECREATE TRIGGER BI_ATABLE FOR ATABLE ACTIVE BEFORE INSERT POSITION 1 AS BEGIN /* ничего не выполняется */ END Л
Глава 10. Типы даты и времени 199 SET TERM J COMMIT; Теперь мы можем удалить старый столбец capturejtime: ALTER TABLE AT ABLE DROP CAPTURE_TIME; COMMIT; Создадим его опять, на этот раз как тип time: ALTER TABLE ATABLE ADD CAPTURE_TIME TIME; COMMIT; Перепишем данных из временного столбца в только что добавленный столбец capturejtime: UPDATE ATABLE SET CAPTUREJTIME = TIME_CAPTURE WHERE TIMEJCAPTURE IS NOT NULL; COMMIT; Удалим временный столбец: ALTER TABLE ATABLE DROP TIME_CAPTURE; COMMIT; Под конец изменим триггер так, чтобы он теперь записывал значение capture time как тип time: SET TERM Л; RECREATE TRIGGER BI_ATABLE FOR ATABLE ACTIVE BEFORE INSERT POSITION 1 AS BEGIN IF (NEW.CAPTURE_DATE IS NOT NULL) THEN BEGIN NEW. CAPTUREJTIME = CAST (NEW. CAPTURE. DATE AS TIME); END END л SET TERM ;л COMMIT; Все эти шаги могут быть записаны в скрипте SQL. Подробности использования скриптов SQL см. в разд. "Скрипты схемы" главы 14. Понимание функции EXTRACTO Функция extracto вызывает исключение, если она получает пустой аргумент. Этот факт можно использовать в простых запросах для проверки условия not null или в выражениях подзапросов при декодировании полей типа дата/время. Тем не менее во
200 Часть III. Типы данных Firebird и домены внешних соединениях это не столь просто, потому что потоки внешнего соединения, которые не соответствуют условиям последнего, возвращают null в незаполненных полях. Рекомендуется использовать подзапрос Сем. главы 21 и 22), который ограничивает вызовы функции только при ненулевых значениях. В диалекте 3 есть другое реше- ние: использовать выражение case (см. главу 21) для исключения вызова extracto при нулевых датах. Пора дальше Следующая глава охватывает большую тему использования символьных (строковых) типов данных в Firebird, включая важные вопросы определения и работы с интерна- циональными наборами символов и порядком сортировки для баз данных и столбцов.
ГЛАВА 1 1 Символьные типы данных Firebird поддерживает символьные (строковые) типы данных фиксированной и пере- менной длины. Они могут быть определены для локального использования в любом наборе символов, выбираемом из большого списка. Символьные типы фиксирован- ной длины не могут превышать 32 767 байт абсолютной длины; для типов перемен- ной длины этот предел уменьшается на два байта, которые при сохранении строки содержат счетчик символов. Firebird хранит строки очень экономно, используя простой алгоритм сжатия данных, даже если это тип char или nchar. В том случае, когда вы хотите объявить очень большой строковый столбец, помните, что существует множество причин не исполь- зовать длинные строки — ограничения клиентской памяти или размеров индекса, а для Firebird 1.0.x еще и декомпрессия строк фиксированной и переменной длины в объявленную длину до того, как они покинут сервер. Основы использования строк Атрибут символьных типов character set важен не только для совместимости с ин- терфейсом локализованных приложений, но также в некоторых случаях для опреде- ления размера столбца. Отдельные наборы символов используют несколько байтов для хранения одного символа— обычно два или три в Firebird. Когда используются такие наборы символов, максимальный размер уменьшается в два или три раза. ПРИМЕЧАНИЕ. Атрибут character set в объявлении является необязательным. -К Если никакой набор символов не определяется на уровне столбца, то атрибут х character set устанавливается в значение набора символов по умолчанию для базы данных. Механизм определения набора символов для столбцов и перемен- ных обсуждается более подробно позже в этой главе. Попытка помещения в строковый столбец Firebird строки с длиной, превышающей объявленную, вызывает ошибку переполнения. Ограничитель строки Ограничителем строк в Firebird является символ ASCII 39, или одиночная кавычка, или апостроф, например, StringVar = 'This is a string.';
202 Часть III. Типы данных Firebird и домены Двойные кавычки вовсе запрещены для ограничения строк. Вы должны помнить это, если соединяетесь с БД Firebird, используя код приложения, написанного для баз данных InterBase 5, где разрешалось использовать кавычки в качестве ограничителя строк. Строки должны быть исправлены также в исходном коде хранимых процедур и триггеров в базе данных InterBase 5, если вы планируете перекомпилировать их для Firebird. Конкатенация Firebird использует стандартный в SQL символ для конкатенации (соединения) строк: двойной символ ASCII с кодом 124, известный как двойная вертикальная черта (||). Он может быть использован для конкатенации строковых констант, строковых выра- жений и/или значений столбцов, например: MyBiggerString = 'You are my sunshine,1 || FirstName I| ' my only sunshine. Символьные элементы могут соединяться с числами и числовыми выражениями для получения алфавитно-цифровых строк. Например, для конкатенации символа с целым: NEW.TICKET_NUMBER = || NEW.PK_INTEGER; ' \ ВНИМАНИЕ! Не используйте выражения конкатенации, где один из элементов / f \ может иметь значение null. Результатом любой конкатенации, содержащей null, А- будет NULL. Управляющие символы Как правило, Firebird не поддерживает использование управляющих символов для включения непечатаемых кодов или последовательностей в строковые поля. Единст- венным исключением является "дублирование" символа апострофа (ASCII 39) для включения его в качестве хранимого символа и исключения его интерпретации как терминального ограничителя строки: SET HOSTELRY = ’О’'Flaherty1’s Pub' В строках можно хранить непечатаемые символы. Может быть объявлена функция UDF Asciichar (азс11_знатение) в библиотеке ib udf, чтобы дать возможность переда- вать в строки такие символы или их последовательности. Следующий оператор вы- водит множество текстовых полей — например, во внешний файл — с символами возврата каретки и перевода строки в последнем поле: INSERT INTO EXTFILE(DATA1, DATA1, DATA3, CRLF) VALUES ('Stringl', 'String2', 'String3', Ascii_Char(13) | |Ascii_Char(10));
Глава 11. Символьные типы данных 203 По поводу объявления Asciichar (..) и других функций в библиотеке ib udf смотрите в подкаталоге ../UDF в корневом каталоге инсталляции Firebird скрипт с именем ib udf.sql. Подробности о внешних функциях см. в приложении 1. Ограничения символьных типов Ограничения многобайтовых наборов символов Важно быть в курсе того, как многобайтовые наборы символов влияют на размеры текстовых элементов, особенно имеющих переменный размер. Например, в наборе символов unicode fss даже 256-символьный столбец будет иметь больший размер — потенциально 770 байт— как для хранения данных, так и для поиска. Дальше в этой главе будет много сказано об осторожности, которую вы должны проявить, решая вопрос о хранении текстов для многобайтовых наборов символов. Ограничения индексов При решении вопросов размера, набора символов и последовательности сортировки для символьного столбца вам нужно убедиться, что индексируемые столбцы этих типов достаточно ограничены по размеру. В настоящее время (версия 1.5) общий размер любого индекса не может превышать 252 байта— заметьте, байтов, а не символов. Многобайтовые и многие более сложные однобайтовые наборы символов используют много больше байтов, чем простые наборы символов. Многосегментные индексы используют дополнительные байты, как и последовательности сортировки. Просчитайте количество байтов в процессе проектирования! Подробности см. в главе 18, обратите внимание на разделы о наборах символов и последовательностях сортировки далее в этой главе. Использование памяти клиента Программы клиента будут выделять память для хранения копий строк, которые они считали из базы данных. Многие уровни интерфейсов выделяют достаточное количе- ство ресурсов для максимального (т. е. определенного) размера значений столбцов фиксированной и переменной длины, даже если фактически никакие данные не со- хранены в таком размере. Буферизация большого количества строк может использо- вать слишком большой объем памяти, и пользователи будут жаловаться на задержки при обновлении экрана и на потерю соединения. Рассмотрим, например, какое влияние окажет на рабочую станцию запрос, возвра- щающий 1024 строки, каждая из которых содержит один столбец, объявленный как varchar(1024) . Даже с самым "скромным" набором символов этого столбца потребу- ется, по меньшей мере, 1 Мбайт памяти клиента. Для столбца Unicode умножьте эту величину на три. Символьные данные фиксированной длины Строковые типы данных фиксированной длины в Firebird используются для хранения строк, длина которых является одной и той же или очень близкой, либо там, где фор-
204 Часть III. Типы данных Firebird и домены мат или относительная позиция символов может передавать семантическое содержа- ние. Обычно они применяются для таких элементов, как идентификационные коды, номера удаленной связи, базирующиеся на символах цифровые системы, а также для объявления полей с целью хранения предварительно форматированных строк фикси- рованной длины, чтобы конвертировать их в другие типы данных — например, лите- ралы даты Firebird. Начальные символы пробелов (символ ASCII 32) во вводимых строках фиксирован- ной длины являются значимыми, в то время как завершающие — нет. При сохране- нии строк фиксированной длины Firebird убирает конечные пробелы. Строки оты- скиваются без избыточного расширения до объявленной длины. Использование типов фиксированной длины не рекомендуется для данных, которые могут содержать значимые конечные символы пробелов, или для элементов, чья фак- тическая длина может сильно изменяться. CHAR(n), алиас для CHARACTER(n) CHAR(n), алиас для снакастек(п), является основой символьного типа фиксированной Длины, п представляет точное количество хранимых символов. Этот тип данных мо- жет хранить строки любого поддерживаемого набора символов. < ПРИМЕЧАНИЕ. Если аргумент длины, п, в объявлении отсутствует, то предпола- гается CHAR(l). Допустимо объявлять односимвольные ПОЛЯ CHAR как просто CHAR. NCHAR(n), алиас для NATIONAL CHARACTER(n) nchar (n), алиас для national снак(п), является специализированной реализацией ти- па CHAR(n) с предварительно установленным атрибутом набора символов iso8859_i. Естественно, недопустимо определять атрибут набора символов для столбца nchar, хотя последовательность сортировки — последовательность, в которой будут сорти- роваться символы для поиска упорядочения вывода, — может быть объявлена для столбца или домена, которые используют этот тип. Подробные разделы о наборах символов и последовательностях сортировки присут- ствуют далее в этой главе. Символьные данные переменной длины Строковые типы данных переменной длины в Firebird используются для хранения строк, длина которых может изменяться. Обязательный аргумент размера п ограни- чивает количество символов, которые могут храниться в столбце максимум п симво- лами. Размер типа varchar не может превышать 32 765 байтов, потому что Firebird добавляет два байта к размеру элемента для каждого объекта varchar.
Глава 11. Символьные типы данных 205 Выбор, хранение и поиск текстов переменной длины Символьный тип переменной длины используется для хранения текстов, потому что размер хранимой структуры равен фактическому размеру данных плюс два байта. Все символы, введенные в поле переменной длины, трактуются как значимые, вклю- чая начальные и конечные пробельные символы. Транспортировка в сети До Firebird 1.5 найденные текстовые элементы данных переменной длины дополня- лись на сервере до полного, объявленного размера до передачи клиенту. Начиная с Firebird 1.5, данные не дополняются. На момент написания этой книги такая возмож- ность для текстов переменной длины не была выполнена для Firebird 1.0.x, что может повлиять на ваш выбор размера и типа столбца, если вы пишете приложения для уда- ленных клиентов, соединяющихся с сервером 1,0.x в медленной сети. Хотя типы переменной длины могут хранить строки почти в 32 Кбайтах, на практике не рекомендуется использовать их для элементов данных, длиннее, чем 250 байт, особенно если их таблицы будут увеличиваться в размерах или если они часто будут субъектом запросов select. Тип данных blob с подтипом sub_type 1 (текст) обычно лучше подходит для хранения больших строковых данных. Тексты blob подробно обсуждаются в следующей главе. VARCHAR(n), алиас для CHARACTER VARYING(n) VARCHAR(n), алиас для character varying (п), является базовым строковым типом пере- менной длины, п представляет максимальное количество символов, которое может сохраняться в столбце. Этот тип хранит строки любого поддерживаемого набора символов. Если никакой набор символов не указан, атрибут примет значение набора символов по умолчанию, который был определен в операторе create database в предложении default character set. Если не существует набора символов по умолча- нию, то столбец получит CHARACTER set none. NCHAR VARYING(n), алиас для NATIONAL CHAR VARYING(n) NCHAR VARYING(n), аЛИЭС ДЛЯ NATIONAL CHAR VARYING (n), КОТОРЫЙ В СВОЮ ОЧереДЬ ЯВЛЯСТ- ся алиасом для national .character varying (n) — это специализированная реализация типа vARCHAR(n) с предварительно установленным атрибутом набора символов ISO8859 1. Недопустимо определять атрибут набора символов для столбца nchar varying, хотя последовательность сортировки — последовательность, в которой бу- дут сортироваться символы для поиска упорядочения вывода — может быть объяв- лена для столбца или домена, которые используют этот тип. Наборы символов и последовательность сортировки Набор символов, выбранный для хранения текстовых данных, определяет: ♦ символы, которые могут быть использованы в столбцах char, varchar и blob sub__type 1 (текст);
206 Часть III. Типы данных Firebird и домены ♦ число байтов, выделяемых для каждого символа; ♦ последовательность сортировки по умолчанию (алфавитно-цифровой порядок), используемая при сортировке столбцов char и varchar (столбцы blob не могут сор- тироваться — так что последовательность сортировки для них не применяется). Если для столбца вы не укажете набор символов, то для него будет использован на- бор символов по умолчанию базы данных. Если для базы данных не указан набор символов по умолчанию, то столбец получит значение character set none. Если ваша база данных используется в окружении, где присутствует только английский язык, у вас может появиться соблазн не использовать набор символов. Не соблазняйтесь! Набор символов none безропотно примет любые однобайтовые символы. Проблемы появятся— в неанглийском окружении или при наличии смешанных языков— вы получите ошибку транслитерации при выборе ваших текстовых данных. То, что уходит, не всегда то же самое, что приходит! Текст, вводимый с клавиатуры или с других устройств ввода, например с устройства считывания штрихового кода, особым образом кодируется в соответствии с некото- рой стандартной кодовой страницей, которая может быть связана с диалектом, за- данным при установке вводящего устройства. Обычно входные устройства снабжа- ются программами-адаптерами, дающими возможность пользователям по желанию переключать кодовую страницу. В одной кодовой странице числовой код, соответствующий образу некоторого сим- вола, может отличаться от кода в другой кодовой странице. В основном каждый на- бор символов Firebird отображает некоторую кодовую страницу или группу связан- ных кодовых страниц. Некоторые наборы символов работают более чем с одной ко- довой страницей, в отдельных случаях кодовая страница будет работать более чем с одним набором символов. Различные языки могут использовать один общий набор символов, но по-разному отображая пары прописные/строчные буквы, символы ва- люты и др. Помимо набора символов различные страны, языки и даже культурные группы, при- меняющие то же самое распределение символов, используют различные последова- тельности для определения "алфавитно-цифрового порядка" для сортировки и срав- нений. Следовательно, для большинства наборов символов Firebird предоставляет множество последовательностей сортировки. Некоторые последовательности сор- тировки также учитывают пары прописные/строчные буквы для решения задачи упо- рядочивания, не чувствительного к регистру. Предложение collate используется в отдельных контекстах, где важна последовательность сортировки, хотя оно и не объ- является на уровне базы данных. Серверу нужно знать, какой набор символов используется при хранении данных, чтобы определить размер требуемой памяти и оценить характеристики сортировки для правильного упорядочивания, сравнения, перевода символов в верхний и нижний регистры и т. д. Помимо этого, он безразличен к символам вводимого текста. Набор символов клиента Что по-настоящему имеет значение в отношении наборов символов — это взаимо- действие между сервером и клиентом. Клиентская библиотека Firebird должна пере- давать атрибут набора символов как часть параметров запроса на соединение.
Глава 11. Символьные типы данных 207 Если сервер обнаруживает различие между установленным для клиента набором символов и хранимым в базе данных, то автоматически будет выполнена трансля- ция — "транслитерация" — в предположении, что входящие коды являются коррект- ными для клиентской кодовой страницы. Входящие коды будут преобразовываться в коды, корректные для соответствующих символов в наборе символов объекта хра- нения. Это делает возможным хранение текстов в различных объектах, которые имеют на- боры символов, отличные от набора символов базы данных по умолчанию. Если наборы символов клиента и объекта одни и те же, то сервер предполагает, что получаемые им коды из этого набора символов, и сохраняет их без изменения. Не- приятности возникают, если данные не являются такими, как об этом сообщил кли- ент. Когда данные выбираются, отыскиваются или восстанавливаются после резерв- ного копирования, это приводит к ошибкам транслитерации. Более подробную информацию об ошибках транслитерации и их исправлении см. в разд. "Транслитерация " далее в этой главе. Приложения, подключающиеся к базе данных, должны передавать набор символов базы данных в API через блок параметров базы данных (Database Parameter Block, DPB) в параметре isc_dpb_ic_ctype. Приложение ESQL — включая утилиту isql — должно выполнить оператор set names непосредственно перед оператором connect. Команда set names <набор-символов> используется для установки набора символов в утилите isql. Графический интерфейс инструментов администратора обычно предос- тавляет возможность выбора или явного указания клиентского набора символов. Если вам нужно использовать язык, отличный от английского, потратьте некоторое время на изучение доступных наборов символов и выбора того, который наиболее соответствует вашим требованиям к вводу, хранению и выводу текстов. Не забудь- те включить этот набор символов в атрибуты базы данных при создании базы дан- ных. Синтаксис см. в разд. "Обязательные и необязательные атрибуты" главы 15. Список наборов символов, распознаваемых Firebird, см. в приложении 8. Переопределение набора символов Имея глобальный набор символов по умолчанию для базы данных, вы можете при необходимости в дальнейшем переопределить его. Вы можете включить атрибут на- бора символов при определении домена. Вы можете переопределить значение набора символов по умолчанию для базы данных или для домена при определении индиви- дуального столбца. Д ВНИМАНИЕ! Когда столбцы используют значение набора символов по умолча- нию для базы данных, изменение набора символов по умолчанию для базы дан- ных повлияет только на вновь создаваемые столбцы и домены. Существующие столбцы сохранят имеющееся значение набора символов.
208 Часть III. Типы данных Firebird и домены Наборы символов Firebird Firebird поддерживает увеличивающееся количество интернациональных наборов символов, включая 2- и 3-байтовые наборы Unicode. Во многих случаях возможен выбор последовательности подбора (сортировки). В этом разделе мы рассмотрим: ♦ происхождение наборов символов; ♦ глобальные наборы символов по умолчанию для базы данных; ♦ альтернативные наборы символов и последовательности сортировки для доменов и столбцов; ♦ последовательности сортировки для: • текстовых значений в операциях сравнения; • предложений order by и group by; ♦ как указать серверу необходимость трансляции вводимых данных в конкретный набор символов. Набор символов является собранием символов, который включает, по меньшей мере, один репертуар символов. Репертуар символов является набором символов, исполь- зуемым в конкретной культуре для публикаций, письменной коммуникации и — в контексте базы данных — для компьютерного ввода и вывода. Например, ISO Latin 1 является набором символов, который охватывает английский (А, В, С ... Z) и французский (А, А, А, В, С, Q, D ... Z) репертуары, делающие его полезным для обо- их сообществ. Именование наборов символов Большинство наборов символов Firebird определены на основании стандартов и их имена близко соответствуют этим стандартам. Например, Microsoft определяет Windows 1251, a Firebird реализует его как WIN1251. Набор символов ISO8859 1 яв- ляется "набором символов, определенным в стандарте ISO 8859-1, кодированным значениями, определенными в стандарте ISO 8859-1, каждое значение представлено одним 8-битовым байтом". Алиасы Имена алиасов наборов символов поддерживают разницу в именовании стандартов между платформами. Например, если вы найдете, что в операционной системе ис- пользуется идентификатор win 1251 для набора символов WIN1251, вы можете ис- пользовать алиас, определенный в системной таблице rdb$types, как описано в сле- дующем разделе. Хранение наборов символов и алиасов Наборы символов в настоящий момент "зашиты" в базу данных с момента ее создания. Одной из системных таблиц, создаваемых автоматически, является rdb$character_set. Для отображения имен наборов символов с последовательностью сортировки каждого из них выполните запрос:
Глава 11. Символьные типы данных 209 SELECT RDB$CHARACTER_SET_NAME, RDB$DEFAULT_COLLATE_NAME, ROBS BYTES_PER_CHARACTER FROM RDB$CHARACTER_SETS ORDER BY 1 ; Если требуется, алиасы помещаются в rdb$types— другую системную таблицу, ко- торая хранит список алиасов, используемых сервером базы данных. Для просмотра всех алиасов, которые были установлены во время создания базы данных, выполните следующий запрос, который фильтрует rdbStypes для просмотра только имен наборов символов: SELECT С. RDB$CHARACTER_SET_NAME, T.RDB$TYPE_NAME FROM RDBSTYPES T JOIN RDBSCHARACTER_SETS C ON C.RDB$CHARACTER_SET_ID = T.RDBSTYPE WHERE T.RDB$FIELD_NAME = 'RDB$CHARACTER_SET_NAME' ORDER BY 1 ; ПРИМЕЧАНИЕ. Для того чтобы использовать наборы символов, отличные от NONE, ASCII, OCTETS и UNICODE_FSS, необходимо иметь библиотеку fbintl в каталоге /inti корневого каталога Firebird. Ограничения хранения Важно понимать, как ваш выбор набора символов влияет на хранение планируемых вами ограничений для данных. В случае столбцов char и varchar Firebird ограничива- ет максимальный объем памяти хранения любого поля в столбце значениями 32 767 и 32 765 соответственно. На самом деле требуемое фактическое количество может быть сильно ограничено. Неиндексируемые столбцы, использующие последовательность сортировки по умол- чанию, МОГуТ хранить не более (количество символов')* (количество байтов на символ) для типа данных. Например, varchar<32765) с набором символов ISO 8859 1 может хранить не более 32 765 символов, тогда как при наборе символов UN1CODEFSS (который использует три байта на символ) максимальное количество 10 291 символ. Если столбец предполагается индексировать и/или изменить предложением collate, должно быть добавлено значительное количество "запасных" байтов. Даже наименее требовательный индекс — один столбец varchar, использующий однобайтовый набор символов и последовательность сортировки по умолчанию — ограничен размером 252 байта для Firebird версии 1.5 и выше. Для столбцов с многобайтовыми наборами СИМВОЛОВ количество СИМВОЛОВ меньше, чем 252/(количество байтов на символ). Мно- гостолбцовые индексы требуют больше байтов, чем одностолбцовые, а те, которые используют последовательность сортировки не по умолчанию, требуют еще больше.
210 Часть III. Типы данных Firebird и домены Более подробно об этих эффектах см. разд. "Последовательность сортировки и раз- мер индекса" далее в этой главе. СОВЕТ. При проектировании столбцов всегда рассматривайте возможные тре- бования с точки зрения использования набора символов, индексирования и клю- ча. Всегда держите "черновую" таблицу в разрабатываемой базе данных для тестирования ограничений индексов и ключей. Хранение столбцов blob, которые не являются индексируемыми, никак не ограничи- вается использованием набора символов. Набор символов по умолчанию Для базы данных Если вы не указываете набор символов по умолчанию для базы данных в объявлении create database, то набор символов по умолчанию устанавливается в none. Набор символов none не предполагает никакого набора символов для текстовых столбцов, сохраняя данные точно в том виде, в каком они были введены. Если клиентское со- единение не указывает набора символов, то данные также будут отыскиваться точно так, как они были введены. Алфавитно-цифровое упорядочение ограничено упорядо- чением кодов ASCII, а преобразование верхний/нижний регистр поддерживается только в кодах U.S.ASCII 65—90 и 97—102 соответственно. Указывайте допустимый код набора символов в предложении default character set: CREATE DATABASE '/data/adatabase.fdb' DEFAULT CHARACTER SET WIN1251; Более подробную информацию об операторе create database см. в главе 12. Переопределение набора символов на уровне столбца Атрибут набора символов может быть добавлен к индивидуальному определению домена, столбца таблицы или переменной PSQL типа char, varchar или blob sub type 1 для перекрытия набора символов по умолчанию базы данных. Например, следующий фрагмент скрипта создает базу данных с набором символов по умолчанию ISO8859_1 и таблицу, содержащую различные версии языка похожих данных в отдельных столбцах: CREATE DATABASE '/data/authors.fdb' DEFAULT CHARACTER SET ISO8859_1; CREATE TABLE COUNTRY_INTL( CNTRYCODE BIGINT NOT NULL, NOM_FR VARCHAR(30) NOT NULL, /* использует набор символов по умолчанию */ NOM_EN VARCHAR(30), /* использует набор символов по умолчанию */ NOM_RU VARCHAR(30) CHARACTER SET WIN1251, NOM_JP VARCHAR(30) CHARACTER SET SJIS_0208
Глава 11. Символьные типы данных 211 Другой фрагмент того же скрипта создает домен для хранения данных blob в наборе символов кириллицы: CREATE DOMAIN MEMO_RU AS BLOB SUB_TYPE 1 CHARACTER SET WIN1251; Позже в этом скрипте мы создаем таблицу, которая хранит некоторый текст в кирил- лице: CREATE TABLE NOTES_RU ( DOC_ID BIGINT NOT NULL, NOTES MEMO_RU ); Следующий фрагмент определяет хранимую процедуру, которая преобразует вход- ную строку в другой набор символов перед сохранением ее в таблице: CREATE PROCEDURE CONVERT_NOTES ( INPUT^TEXT VARCHAR(300) ) AS DECLARE VARIABLE CONV_STRING VARCHAR(300) CHARACTER SET WIN1251; BEGIN IF (INPUTJTEXT IS NOT NULL) THEN BEGIN CONV_STRING = JVIN1251 1'|l:INPUTJTEXT; /* использует INTRODUCER */ INSERT INTO NOTES_RU (DOC_ID, NOTES) VALUES (GEN_ID (ANYGEN, 1),:CONV_STRING); END END " Создание доменов объясняется в главе 13. Полный синтаксис оператора create table описан в главе 15. Объявление переменных в PSQL см. в главе 30. Переопределение набора символов на уровне оператора Набор символов для текстовых значений в операторе интерпретируется в соответст- вии с набором символов соединения в процессе выполнения (а не в соответствии с набором символов, определенным для столбца при его создании), если только вы не зададите маркер набора символов (или "представитель") для указания другого набора символов. INTRODUCER — маркер набора символов Маркер набора символов — также известный как introducer— состоит из имени на- бора символов, перед которым стоит символ подчеркивания. Он требуется для "пред- ставления" входной строки, когда приложение клиента соединено с базой данных с использованием набора символов, отличного оттого, который определен для столбца в базе данных. Установите маркер слева от отмечаемого текстового значения. Например, маркером для ввода в UNICODE_FSS поле является _unicode_fss:
212 Часть III. Типы данных Firebird и домены INSERT INTO EMPLOYEE(Emp_ID, Emp_Name) values(1234, _UNICODE_FSS 'Smith, John Joseph'); СОВЕТ. Для ясности вы можете вставить пробел между маркером и строкой без какого-либо влияния на способ синтаксического анализа вводимого данного. Строковый литерал Строковый литерал в условии проверки или поиска, например в предложении where, интерпретируется в соответствии с набором символов клиентского соединения в мо- мент проверки условия. Маркер потребуется, когда отыскиваемый столбец базы дан- ных имеет набор символов, отличный от того, который был указан в клиентском со- единении: ... WHERE name = _ISO8859_1 'joe'; СОВЕТ. Когда вы разрабатываете приложение со смешанными наборами сим- волов, то удобно использовать маркеры, особенно если ваше приложение будет работать со многими базами данных и/или будет распространяться интернацио- нально. Транслитерация Преобразование символов из одного набора символов Firebird в другой — например, конвертирование из DOS437 в ISO8859_1 — является транслитерацией. Транслите- рация в Firebird сохраняет точность символов: по определению она не подставляет никакого "заменителя" для входного символа, который не представлен в выходном наборе символов. Назначением такого ограничения является гарантия того, что воз- можна транслитерация одного и того же текста из одного набора символов в другой в любом направлении без потери символов в процессе транслитерации. Ошибки транслитерации Firebird выдает сообщение об ошибке, если символ во входном наборе не имеет точ- ного представления в выходном наборе. Пример, где может появиться ошибка транслитерации: когда приложение передает данные некоторого неопределенного набора символов в столбец, определенный с none, и позже пытается выбрать эти данные и поместить в другой столбец, который был определен с отличающимся набором символов. Хотя вы думаете, что это должно работать, потому что образы символов, похоже, принадлежат набору символов столбца назначения, транслитерация будет ошибочной, поскольку символ не пред- ставлен в наборе символов столбца назначения. Исправление ошибок транслитерации Как вы можете работать с группой символьных данных, которые вы сохранили с ис- пользованием неверного набора символов? "Трюк" заключается в использовании на-
Глава 11. Символьные типы данных 213 бора символов OCTETS в качестве "промежуточного аэродрома" между ошибочным и правильным кодированием. Поскольку OCTETS является специальным набором символов, который, не глядя, сохраняет то, что вы ему подсовываете (без транслите- рации), он является идеальным для того, чтобы сделать символьные коды нейтраль- ными в отношении кодовой страницы. Предположим, ваша проблемная таблица имеет столбец col_original, который вы случайно создали с набором символов none, когда имели в виду character set WIN1251. Вы загрузили в этот столбец данные на русском языке, но каждый раз, когда вы пытаетесь получить из него данные, вы получаете противную ошибку транслите- рации. Вот что вам нужно сделать: ALTER TABLE TABLEA ADD COL_WIN1251 VARCHAROO) CHARACTER SET WIN1251; COMMIT; UPDATE TABLEA SET COL_WIN1251 = CAST (COL__ORIGINAL AS CHAR(30) CHARACTER SET OCTETS); Теперь у вас есть временный столбец, созданный для хранения русских текстов, он хранит все из ваших "потерянных" текстов из неиспользуемого столбца col original. Вы можете удалить столбец col_original, а затем новый столбец col_original с кор- ректным набором символов. Просто скопируйте данные из временного столбца, и после подтверждения транзакции удалите временный столбец: ALTER TABLE TABLEA DROP COL_ORIGINAL; COMMIT; ALTER TABLE TABLEA ADD COL-ORIGINAL VARCHAR(30) CHARACTER SET WIN1251; COMMIT; UPDATE TABLEA SET COL-ORIGINAL = COL_WIN1251; COMMIT; /* Было бы разумным сейчас посмотреть ваши данные! */ ALTER TABLE TABLEA DROP COL_WIN1251; COMMIT; Набор символов для клиентского соединения Когда клиентское приложение, например, isql, соединяется с базой данных, в прото- коле соединения присутствует часть, которая информирует сервер о требуемом набо- ре символов. Набором символов соединения является нейтральный набор символов none, если не указано другое с использованием: ♦ set names во встроенном приложении или в isql; ♦ параметра isc_dpb_ic ctype в блоке параметров базы данных (DPB) для API- функции isc_attach_database (). Классы RAD соединения с базой данных для Delphi, Java и других обычно представляют этот параметр как свойство.
214 Часть III. Типы данных Firebird и домены Клиентское приложение задает набор символов до его соединения с базой данных. Например, следующая команда isql определяет, что isql использует набор символов ISO88591. По команде происходит соединение с базой данных autord.fdb из нашего предыдущего примера: SET NAMES WIN1251; CONNECT 'Iserver:/data/authors.fdb' USER 'ALICE' PASSWORD 'XINEOHP'; Специальные наборы символов Основное правило для наборов символов то, что каждый байт (пара или тройка бай- тов в случае многобайтовых наборов) специально определен по стандарту его реали- зации. Существует четыре особых исключения — NONE, OCTETS, ASCII и UNICODE FSS. В табл. 11.1 показаны специальные свойства этих наборов. Таблица 11.1. Специальные наборы символов Имя Свойства NONE Каждый байт является частью строки, но не имеется никаких предположе- ний, к какому набору символов он принадлежит. Код клиентской стороны или определенный пользователем на сервере код является ответственным за правильность символа OCTETS Байты, которые не интерпретируются как символы. Полезен для хранения двоичных данных ASCII Значения 0—127 определены как ASCII. Значения за пределами этого диа- пазона не являются символами, но поддерживаются. Firebird совершенно либерален относительно транслитерации байтов в диапазоне 0—127 сим- волов ASCII UNICODE_FSS Разработчикам нужно знать, что он эффективен при реализации UTF8. Пользователям нужно знать, что он может быть использован для хранения символов UCS16, но не UCS32 (может занимать до шести байтов на сим- вол). Недоступна ни одна последовательность сортировки, кроме двоичной последовательности по умолчанию ISO8859_1 (LATIN_1) и WIN1252 Набор символов 1SO8859_1 часто указывается для поддержки европейских языков. ISO8859_1, также известный как LATIN1, является истинным подмножеством WIN 1252. Microsoft добавил символы в позиции, которые ISO специфицировал как не являются символами (не "неопределенные", но указанные как "не символы"). Firebird поддерживает как WIN 1252, так и ISO8859 1. Вы всегда можете выполнить транслитерацию из ISO8859 1 в WIN 1252, но транслитерация WIN 1252 в ISO8859_1 может вызвать ошибки. Наборы символов для Microsoft Windows Пять наборов символов поддерживают приложения клиентов Windows, такие как Paradox for Windows. Это наборы символов WINI250, WIN1251, WIN1252, WIN1253 и WIN 1254.
Глава 11. Символьные типы данных 215 Благодаря историческим связям Borland с Paradox и dBase, имена последовательно- стей сортировки этих наборов символов, специфичных для Paradox for Windows, на- чинаются с "PXW" и соответствуют языковым драйверам Paradox/dBase, поставляе- мым с ныне устаревшим Borland Database Engine (BDE). Понимание наборов символов WINnnn___________________________________ Последовательности сортировки PXW действительно реализуют сортировку для Paradox и dBase, включая все ошибки. Одно исключение: PXW_CSY исправлен в Firebird 1.0. Следовательно, базы данных InterBase, которые его используют, напри- мер в индексах, не являются совместимыми с Firebird. Более подробную информацию о наборах символов Windows и сортировках Paradox for Windows см. в соответствующей документации по BDE и драйверам. Список международных наборов символов и последовательностей сортировки, под- держиваемые Firebird, см. в приложении 8. Последовательности сортировки Каждый набор символов имеет последовательность сортировки (collate) по умолча- нию, которая определяет, как символы сортируются и упорядочиваются. Последова- тельность сортировки определяет правила предшествования, которые Firebird ис- пользует для сортировки, сравнения и транслитерации символьных данных. Поскольку каждый набор символов имеет свое возможное подмножество последова- тельностей сортировки, то набор символов, который вы выбираете при определении столбца, ограничивает ваш выбор. Вы должны выбрать последовательность сорти- ровки, которая поддерживается набором символов, заданным для столбца. Последовательность сортировки для столбца задается при создании или модифика- ции столбца. Если устанавливается на уровне столбца, то перекрывает любую уста- новку последовательности сортировки на уровне домена. Отображение доступных последовательностей сортировки Следующий запрос дает список наборов символов с доступными последовательно- стями сортировки: SELECT С. RDB$CHARACTER_SET_NAME, С О. RDB S COLLAT IONJIAME, СО. RDB$COLLATION_ID, СО. RDB$ CHARACTER^ ET_ID, СО. RDB$ COLLAT ION_ID * 256 + CO.RDB$CHARACTER_SET_ID AS TEXTTYPEID FROM RDB$COLLATIONS CO JOIN RDB$CHARACTER_SETS C ON CO.RDB$CHARACTER~SET_ID = C.RDB$CHARACTER_SET_ID;
216 Часть III. Типы данных Firebird и домены Именование последовательностей сортировки Многие имена последовательностей Firebird используют соглашение по именованию XXYY, где XX — двухбуквенный код языка, a YY — двухбуквенный код страны. Например, DE DE — имя последовательности для немецкого языка, используемого в Германии, FRFR— для французского языка, используемого во Франции, FR CA — для французского языка, используемого в Канаде. Когда набор символов предоставляет выбор сортировки, одна из них с именем, соот- ветствующим имени набора символов, является последовательностью сортировки по умолчанию, которая реализует двоичное сравнение для набора символов. Двоичное сравнение сортирует набор символов по числовому коду, используемому для пред- ставления символов. Некоторые наборы символов поддерживают альтернативные последовательности сортировки, которые используют различные правила определе- ния предшествования. В этом разделе описывается задание последовательности сортировки для наборов символов в доменах и столбцах таблиц, в строковых сравнениях, в предложениях ORDER BY И GROUP BY. Последовательность сортировки для столбца Когда в таблице создается столбец char или varchar с использованием create table или alter table, последовательность сортировки для столбца может быть задана с использованием предложения collate. Предложение collate особенно полезно для таких наборов символов, как ISO8859 1 и DOS437, которые поддерживают множест- во различных последовательностей сортировки. К примеру, следующий динамический оператор alter table добавляет новый столбец в таблицу и задает и набор символов, и последовательность сортировки: ALTER TABLE 'EMP^CANADIEN' ADD ADDRESS VARCHAR(40) CHARACTER SET WIN1251 NOT NULL COLLATE PXW_CYRL; Полный синтаксис alter table cm. в главе 16. Последовательность сортировки для строковых сравнений Может оказаться необходимым задать последовательность сортировки при сравне- нии значений char или varchar в предложении where, если сравниваемые значения используют различные последовательности сортировки, и это влияет на результат. Чтобы указать используемую последовательность сортировки для значения в процес- се сравнения, задайте предложение collate после значения. Например, следующий фрагмент предложения where задает конкретную последовательность сортировки для значения столбца в левой части операции сравнения при сравнении с входным пара- метром: WHERE SURNAME COLLATE PXW_SYRL >= :surname;
Глава 11. Символьные типы данных 217 В этом случае при несоответствии последовательностей сортировки могут быть раз- личные кандидаты для "больше чем" при разных последовательностях сортировки. Последовательность сортировки в критериях сортировки Когда столбцы char или varchar упорядочиваются в операторе select, может оказать- ся необходимым указать порядок сортировки для упорядочивания, особенно если столбцы в предложении упорядочивания используют различные последовательности сортировки. Чтобы задать последовательность сортировки для использования в упорядочиваемых столбцах, в предложение order by добавьте collate после имени столбца. Например, в следующем предложении order by задаются последовательности сортировки для двух столбцов: order by surname collate pxw_cyrl, first_name COLLATE PXW_CYRL; Полный синтаксис предложения order by cm. в главе 23. Последовательность сортировки в предложении GROUP BY Когда столбцы char или varchar группируются в операторе select, может оказаться необходимым указать порядок сортировки для группирования, особенно если столб- цы в предложении группировки используют различные последовательности сорти- ровки. Чтобы задать последовательность сортировки для использования в группируемых столбцах, в предложение group by добавьте collate после имени столбца. Например, в следующем предложении group by задаются последовательности сортировки для нескольких столбцов: GROUP BY ADDR_3 COLLATE PXW_CYRL, SURNAME COLLATE PXW_CYRL, FIRST-NAME COLLATE PXW-CYRL; Полный синтаксис предложения group by cm. в главе 23. Последовательность сортировки и размер индекса Если для набора символов вы задаете недвоичную сортировку (отличную от сорти- ровки по умолчанию), то размер индексного ключа может стать больше, чем храни- мая строка, если сортировка включает правила предшествования второго, третьего или четвертого порядка. Например, недвоичные сортировки для ISO8859_1 используют полные словари с пробелами и знаками пунктуации с четырьмя порядками значений. ♦ Первый порядок: А отличается от В. ♦ Второй порядок: А отличается от А.
218 Часть III. Типы данных Firebird и домены ♦ Третий порядок: А отличается от а. ♦ Четвертый порядок: важным является тип знака пунктуации (дефис, пробел, апо- строф). Например: Greenfly Green fly Green-fly Greensleeves Green sleeves Green spot Если же пробелы и знаки пунктуации трактуются как символы первого порядка, то тот же самый список будет отсортирован следующим образом: Greenfly Greensleeves Green fly Green sleeves Green spot Green-fly Как недвоичные сортировки могут ограничить размер индекса Когда создается индекс, он использует последовательности сортировки, определен- ные для каждого текстового фрагмента индекса. При использовании однобайтового набора символов ISO8859_1 с сортировкой по умолчанию структура индекса может содержать приблизительно 252 символа (меньше, если это многосегментный индекс). Если же вы выбираете недвоичную сортировку для ISO8859_1 (в том числе это отно- сится к сортировке PXW_CYRL кодировки WIN1251), то структура индекса может содержать только 84 символа, несмотря на то, что символы в индексируемом столбце занимают только один байт каждый. ВНИМАНИЕ! Некоторые сортировки ISO8859_1, например DE_DE, требуют в среднем три байта на символ для индексируемого столбца. Пользовательские наборы символов и сортировки Существует возможность создать собственные наборы символов и сортировки и за- ставить сервер Firebird загружать их из библиотеки, которая должна называться fbint!2, чтобы ее можно было распознать и подключить. Также можно реализовать пользовательские наборы символов и сортировки с ис- пользованием функций, определенных пользователем (UDF) для транслитерации входных данных. Сервер Firebird 1.5 автоматически использует UDF со специальны-
Гпава 11. Символьные типы данных 219 ми именами, чтобы их можно было распознать как наборы символов и сортировки. Имя ' usER_cHARSET_rmn1 указывает набор символов, в то время как 1 user_translate_ nnn_nnn' и 1 usER_TEXTTYPE__nnn' указывает набор символов плюс последовательность сортировки (ппл представляет трехсимвольное число, обычно в диапазоне от 128 до 254). Это сложная тема, выходящая за пределы данной книги. Разработчик для fbintl2 встраиваемых пользователем наборов символов David Brookestone Schnepper создал свободно распространяемый комплект "сделай сам", который содержит пример ко- да С, отображения и инструкции, доступный на http://www.ibcollate.com. Поскольку комплект поставки содержит ясные инструкции по созданию наборов символов, он также будет вам полезным справочником, если вы планируете использовать подход UDF для реализации пользовательского набора символов. Добавление собственных алиасов В экстремальной ситуации — когда вы используете нестандартную операционную систему, которая требует имя набора символов, не поддерживаемое в Firebird в каче- стве алиаса — вы можете добавить алиас. Для этого не существует простого способа; требуется прямая корректировка системных таблиц— такая практика, как общее правило, должна быть исключена. Прежде чем вы решите добавить новый пользова- тельский алиас, убедитесь, что Firebird не поддерживает нужный вам алиас — про- смотрите в приложении 8 списки алиасов около каждого имени набора символов. Инсталляция пользовательских алиасов включает прямое добавление строки в табли- цу rdb$types. Получите идентификатор набора символов, для которого вы собирае- тесь создать алиас — в таблице rdb$character_sets это значение столбца rdbscharacter_set_id — и убедитесь, что у вас правильный строковый литерал, кото- рый ваша операционная система распознает как набор символов, который вы хотите поддерживать. Предположим, вы хотите добавить алиас для набора символов ISO8859 1, который ваша ОС сможет распознать по литералу ’lc_iso88591'. Во-первых, получите иден- тификатор набора символов по запросу к таблице rdb$character_sets, используя ути- литу isql или другой интерактивный инструмент запросов: SELECT RDB$CHARACTER_SET_ID FROM RDB$CHARACTER_SETS WHERE RDB$CHARACTER_SET_NAME = 'ISO8859_11; Этот пример вернет идентификатор набора символов "21". Затем подготовьте и вы- полните оператор insert для добавления вашего алиаса в таблицу rdb$types: INSERT INTO RDB$TYPES ( RDBSFIELDJIAME, RDBSTYPE, RDB$TYPE_NAME) VALUES ('RDB$CHARACTER_SET_NAME', 21, 'LC_ISO88591'); Эта техника относительно безболезненна, если ваш пользовательский алиас пред- ставляет набор символов, нужный для определения столбца или домена, но она не требуется для набора символов базы данных по умолчанию. Просто убедитесь, что определение алиаса существует до того, как вы создаете столбец или домен, которым нужно его использовать.
220 Часть III. Типы данных Firebird и домены Существует проблема типа "уловка-22", если ваша операционная система по- настоящему не может поддерживать набор символов или алиас для набора символов, который вам нужно использовать по умолчанию. Ваша база может "узнать" о вашем алиасе только после создания базы данных, когда таблица rdb$types уже существует. Набор символов базы данных по умолчанию определяется в create database, а в этот момент доступны только объявленные в системе алиасы. В тот момент, когда rdbStypes существует, уже поздно назначать набор символов по умолчанию. Поскольку в настоящий момент Firebird не предоставляет способов изменения атри- бута набора символов по умолчанию — это не поддерживается в синтаксисе alter database, — существует только один способ: сначала создать базу данных, а затем, до того, как что-нибудь с ней делать, установить, как был описан ваш алиас, подтвер- дить транзакцию и изменить напрямую заголовочную запись базы данных: UPDATE RDB$DATABASE SET RDB$CHARACTER_SET_NAME = 'LC_ISO885911; COMMIT; Д ВНИМАНИЕ! Никогда не пытайтесь выполнить то же для любой базы данных, которая не является "пустой" — т. е. содержит определенные пользователем объекты. Пора дальше Теперь мы переходим к типам данных, которые Firebird реализует посредством больших двоичных объектов (blob), включая текст, нетипизированные двоичные и пользовательские форматы и специализированные реализации blob, которые Firebird представляет как типы array.
ГЛАВА 1 2 BLOB и массивы Типы blob (Binary Large Objects, большие двоичные объекты) являются сложными структурами, используемыми для хранения дискретных объектов данных переменно- го размера, который может быть очень большим. Они являются "сложными" в том смысле, что Firebird сохраняет эти типы в виде двух частей: специальная гиперссыл- ка (называется blob_id) сохраняется в собственной строке, в то время как сами дан- ные хранятся за пределами строки, часто на одной или нескольких страницах базы данных, на которые указывает blob_id. Firebird использует структуры blob для различных внутренних целей. Он также пре- доставляет две главные категории для пользовательских типов, применяющие этот вид структуры хранения: blob и массивы (array). Типы array могут быть использова- ны для представления однородных массивов большинства типов данных. Типы BLOB Почти любой вид сохраняемых данных может храниться в blob: графические картин- ки, векторные рисунки, звуковые файлы, видео, документы размером с главу или целую книгу, любой вид мультимедийной информации. Поскольку blob может со- держать различные виды информации, он требует специальной обработки чтения и записи на клиенте. Типы blob могут, когда это возможно', хранить содержимое файлов, сгенерирован- ных другими приложениями, такими как текстовые процессоры, программное обес- печение CAD или редакторы XML. Преимущества могут быть в управлении транзак- циями для динамических данных, защите от внешнего интерфейса, управлении вер- сиями и возможности доступа к внешне созданным данным с помощью средств операторов SQL. Столбцы blob не могут быть проиндексированны. Тип blob не является волшебной палочкой для сохранения и поиска больших объемов файлов двоичных данных. Иногда при учете производительности накладные расходы и не- управляемость хранения нединамических объектов данных, вроде фильмов или звуков, в базе данных перевешивает преимущества, которые вы предполагали. Хранение ссылок на объекты файловой системы может быть хорошим решением.
222 Часть III. Типы данных Firebird и домены Поддерживаемые типы BLOB Firebird имеет два предварительно определенных типа blob, отличающиеся атрибутом подтипа (ключевое слово в SQL sub type), как описано в табл. 12.1. Таблица 12.1. Предварительно определенные подтипы blob Определение Алиас SQL Назначение BLOB SUB_TYPE 0 He используется Общий тип blob данных любого вида, включая текст. Общее название: "нетипизированный двоичный blob", однако Firebird ничего не знает о его содержании BLOB SUB-TYPE 1 BLOB SUB-TYPE TEXT Более специализированный подтип для хране- ния полного текста. Эквивалентен типам CLOB и мемо, реализованных в некоторых других СУБД. Рекомендуется использовать с интерфейсами приложений, таких как компоненты RAD или поисковые машины, которые выполняют спе- циальную трактовку для каждого типа Подробнее о подтипах Подтип blob является положительным или отрицательным целым, которое указывает природу данных, содержащихся в столбце. Помимо двух предопределенных типов для общего использования Firebird имеет множество подтипов, которые он применяет для внутренних целей. Все эти внутренние подтипы имеют положительные номера. Пользовательские подтипы могут быть добавлены с отличающимися идентификато- рами особых типов для объектов данных, таких как HTML, XML или текстовый про- цессор, картинки JPEG или PNG и т. д. — именно вам делать выбор. Отрицательные номера подтипов (от-1 до-32 768) резервируются для пользовательских подтипов. Система подтипов blob также позволяет выполнять специфические преобразования одного подтипа в другой. Firebird осуществляет поддержку автоматического преоб- разования между парой подтипов blob в форме въов-филътров. Фильтры blob являют- ся специальным видом внешних функций с единственным назначением: получение объекта blob одного формата и преобразование его в объект blob другого формата. Возможно создание вьов-фильтра для преобразования между пользовательским (от- рицательным) и предварительно определенным подтипами — обычно text. Объектный код для вьов-фильтров размещается в библиотеках коллективного досту- па. Фильтр, вызываемый при необходимости динамически, распознается на уровне базы данных (не сервера) при его объявлении в метаданных: DECLARE FILTER <имя~фильтра> INPUT_TYPE <подтип> /* идентифицирует тип преобразуемого объекта */ OUTPUT_TYPE <подтип> /* идентифицирует тип создаваемого объекта */ ENTRY_POINT '<имя-точки-входа>' /★ имя экспортируемой функции */ MODULE_NAME '<имя-внешней-библиотеки>'; /★ имя библиотеки BLOB-фильтра */'
Глава 12. BLOB и массивы 223 ПРИМЕЧАНИЕ. Написание и использование BLOB-фильтров выходит за преде- лы тем настоящего руководства. Информацию по этой теме можно найти в базах \ \ знаний Firebird. Firebird не проверяет тип или формат данных blob. При планировании их хранения вы должны создать такой код вашего приложения, чтобы формат данных был согла- сован с их подтипом, неважно предварительно определенным или пользовательским. Сегменты BLOB Данные blob хранятся в различных форматах в обычном столбце данных и вне столб- ца. Они хранятся в виде сегментов на одной или более страницах базы данных. Сег- менты являются дискретными фрагментами неформатированных данных, которые обычно создаются приложением в виде потока и передаются функциям API для паке- тирования и передачи по сети по одному блоку за раз непрерывно. В структуре записи ссылка на данные blob осуществляется с помощью идентифика- тора blob (blob_id). blob id является уникальной шестнадцатеричной парой, которая обеспечивает перекрестные ссылки между данными blob и содержащей их таблицей. При поступлении на сервер сегменты сохраняются в базе в том же порядке, как они были получены, хотя не обязательно с теми же размерами фрагментов, с которыми они передавались. Когда это возможно, blob сохраняются на той же странице, что и запись с остальны- ми данными. При этом большие blob могут занимать много страниц, а их начальные страницы могут содержать не данные, а массив указателей на страницы с содержи- мым blob. Примеры синтаксиса Следующий оператор определяет два столбца blob: blobi подтипа 0 (по умолчанию) и blob2 подтипа 1 (text, с набором символов по умолчанию): CREATE TABLE TABLE2 (BLOBI BLOB, /* SUB_TYPE 0 */ BLOB2 BLOB SUB_TYPE 1); Следующий оператор определяет домен, являющийся текстовым blob для хранения текстов в наборе символов ISO8859 1: CREATE DOMAIN MEMO BLOB SUB_TYPE TEXT /*BLOB SUB_TYPE 1 */ CHARACTER SET ISO8859_1; Фрагмент кода SQL показывает, как объявляется локальная переменная blob в модуле PSQL: CREATE PROCEDURE ... DECLARE VARIABLE AMEMO BLOB SUB TYPE 1;
224 Часть III. Типы данных Firebird и домены Размер сегмента Когда в таблице определяется столбец blob, определение может включать ожидаемые размеры сегментов, которые будут записываться в столбец. Значение по умолча- нию— 80 байт— совершенно случайное. Говорят, что такой размер был выбран, потому что это в точности длина строки текстового дисплея! Установка размера сегмента не влияет на производительность при обработке blob на сервере Firebird: сервер совсем его не использует. Для приложений DSQL — которые пишет большинство людей — вы можете просто игнорировать его или, если это важ- но, установите его в некоторое значение, подходящее буферу, в котором ваше при- ложение сохраняет данные blob. Для операций DML, выполняемых через API — select, insert и update — длина сег- мента указывается явно и может быть любого размера вплоть до максимума в 32 767 байт. Повторно используемые классы, драйверы и компоненты для таких сред разработки, как Delphi, C++ и Java, обычно сами заботятся о сегментации blob в их внутренних функциях и процедурах (например, в IBX и FIBPlus размер сегмента для чтения и записи blob равен 16 Кбайт и может быть изменен только глобально). Встроенные приложения В базах данных, используемых во встроенных приложениях, — здесь мы говорим о приложениях ESQL, написанных для обработки препроцессором gpre, — размер сегмента должен быть объявлен для указания максимального количества байтов, ко- торое приложение собирается записать в любой сегмент столбца. Обычно приложе- ние ESQL не должно ожидать записи сегмента, большего, чем указанная длина сег- мента, определенная в таблице; получение такого сегмента переполняет внутренний буфер сегмента и разрушает память. Полезнее указывать относительно большой сег- мент для уменьшения количества вызовов при поиске данных blob. Следующий оператор создает два столбца blob: blobi с размером сегмента по умол- чанию 80 и blob2 с заданным размером сегмента 1024: CREATE.TABLE TABLE2 (BLOBI BLOB SUB__TYPE 0, BLOB2 BLOB SEGMENT SUB_TYPE TEXT SEGMENT SIZE 1024); В следующем фрагменте кода ESQL приложение вставляет сегмент blob. Длина сег- мента указана в переменной включающего языка segment length: INSERT CURSOR BCINS VALUES (;write_segment_buffer :segment_length); Операции с полями BLOB Поле blob никогда не обновляется. Каждое обновление, которое "изменяет" blob, приводит к конструированию нового blob, создавая и новый blob id. Первоначаль- ный blob становится устаревшим, когда подтверждаются обновления. Столбец blob можно проверять на null/not null, но не существует внутренних функ- ций сравнения одного blob с другим или сравнения blob со строкой. Некоторые LJDF
Глава 12. BLOB и массивы 225 для blob, доступные на сайтах сообщества, включают сравнения двух blob на равен- ство. Невозможно выполнить конкатенацию двух blob или blob со строкой (без использо- вания сторонних Ut)F). Входная строка для столбца BLOB При получении данных для ввода столбцов blob для операций insert или update Firebird может взять строку как исходную и преобразовать ее в blob, например: INSERT INTO ATABLE (РК, ABLOB) VALUES (99, ’This is some text.*); Обратите внимание, что передача хранимой процедуре строки как входного аргумен- та, который был определен как blob, вызывает исключение. Например, следующее не будет выполнено: CREATE PROCEDURE DEMO (INPUTARG BLOB SUB_TYPE 1) AS BEGIN END л COMMIT л EXECUTE PROCEDURE DEMO('Show us what you can do with this!') л Вместо этого выполните одно из следующих: ♦ определите ваш входной аргумент как varchar, и пусть ваша процедура подстав- ляет эту строку в операторы insert или update; ♦ пусть ваша клиентская программа выполняет конвертирование строки в текст blob. Это предпочтительное решение, если длина строки не известна. Когда использовать типы BLOB blob более предпочтительны, чем символьные типы, для хранения текстовых данных неопределенно большой длины. Поскольку он преобразуется в "бессмысленные фрагменты", к нему не относится ограничение размера строк в 32 Кбайта, пока кли- ентское приложение реализует подходящие техники передачи его в требуемом серве- ром формате для его сегментирования2. Поскольку данные blob хранятся вне обычной строки данных, они не загружаются автоматически, когда выбрана строка данных. Клиент запрашивает эти данные через blob id. Следовательно, здесь имеется большой "выигрыш" по времени выборки строк с помощью select по сравнению с трафиком, когда используются строковые 2 Возможна передача строковых типов как входных данных для blob с предоставлением серверу возможности их конвертирования. При этих условиях невозможно хранение blob, который превышает ограничение для строк в 32 Кбайта. 8 Зак. 420
226 Часть III. Типы данных Firebird и домены типы для хранения больших текстовых элементов. С другой стороны, некоторые раз- работчики могут рассматривать это как недостаток, который требует дополнительной реализации "выборки по требованию". При принятии решения об использовании вьов для нетекстовых данных возникает ряд других вопросов. Удобство хранения изображений, звуковых файлов и полных документов должно быть сбалансировано с дополнительными расходами при созда- нии резервных копий базы данных. Может оказаться неразумным стремление хра- нить большое количество огромных объектов, которые никогда не будут изменяться. Безопасность Идея о том, что большие двоичные и текстовые объекты являются более защищен- ными, когда хранятся в blob, чем когда хранятся в файлах файловой системы, являет- ся в некоторой мере иллюзией. Конечно, к ним несколько сложнее получить доступ из инструментов конечного пользователя. Однако в настоящий момент привилегии базы данных не применимы к типам blob и array вне контекста таблиц, с которыми они связаны. Не будет абсурдным предположить, что "злобные хакеры", которые получают доступ к файлу базы данных, смогут написать код приложения, который будет сканировать файл в поисках blob id и читать напрямую данные из базы, как это делают внешние функции blob. Типы массивов Firebird позволяет создавать однородные массивы для большинства типов данных. Использование массива позволяет хранить множество элементов данных в виде дис- кретных, многомерных элементов в одном столбце. Firebird может выполнять опера- ции над целым массивом, эффективно трактуя его как один элемент, или он может оперировать с частью массива — подмножеством элементов массива. Часть массива может состоять из одного элемента или из набора многих смежных элементов. Типы ДЯЯДГиБОк Поскольку в Firebird не существует никакого синтаксиса динамического SQL для обработки типов array, выполнение DML и поиск таких типов из интерфейсов дина- мического SQL (DSQL) не является простым делом. API Firebird содержит структуры и функции, позволяющие динамическим приложениям работать с ними напрямую. Некоторые компоненты доступа к данным RAD (например, iBObject, использующие- ся в продуктах Borland Delphi и Kylix) содержат классы, инкапсулирующие эту функциональность API в виде свойств и методов клиентской стороны. ESQL, который не использует структуры и вызовы функций API, поддерживает неко- торый статический синтаксис SQL для обработки типов array и интеграции их с мас- сивами, объявленными во включающем языке. Для динамических и статических приложений есть подходящее, хотя и не всегда осуществимое решение: чтение данных массива в хранимой процедуре и возвраще-
Глава 12. BLOB и массивы 227 ние значений в том виде, в каком клиентское приложение может их использовать. Позже в разд. "Ограниченный доступ динамического SQL" будет приведен пример. Когда использовать тип массива Использование массивов является подходящим, когда: ♦ элементы данных естественно принимают вид множества данных одного типа; ♦ весь набор элементов данных в одном столбце базы данных должен быть пред- ставлен и должен управляться как одно целое вместо того, чтобы сохранять каж- дый элемент в отдельном столбце; ♦ к каждому элементу также должен быть индивидуальный доступ; ♦ не требуется доступ к индивидуальным значениям в триггерах или хранимых процедурах, либо у вас есть внешние функции для реализации такого доступа. Подходящие типы элементов Массивы могут содержать элементы любого поддерживаемого Firebird типа за ис- ключением blob. Массивы массивов не поддерживаются. Все элементы конкретного массива имеют один и тот же тип данных. Определение массивов Массив может быть определен как домен (с использованием create domain) или как столбец в операторе create table или alter table. Определение домена или столбца как массива похоже на определение любого другого такого объекта, здесь только добавляется указание размерности массива. Размерность массива заключается в квадратные скобки и следует за спецификацией типа данных. Например, следующий оператор определяет обычный символьный столбец и столбец в виде одноразмерного символьного массива, содержащего восемь элементов: create table atable (ID BIGINT, ARR_CHAR(14)[8] CHARACTER SET OCTETS); /* хранит 1 строку по 8 элементов */ Многомерные массивы Firebird поддерживает многомерные массивы размерностью от 1 до 16. Например, следующий оператор определяет три столбца целочисленных массивов с двумя, тре- мя и четырьмя размерностями: CREATE TABLE BTABLE ( /* хранит 4 строки по 5 элементов = 20 элементов */ ARR_INT2 INTEGER[4,5], /* 6 уровней, по 4 строки по 5 элементов = 120 элементов */ ARR_INT3 INTEGER [4,5,6],
228 Часть III. Типы данных Firebird и домены /* 7 ярусов, по 6 уровней в 4 строки по 5 элементов = 840 элементов ★/ ARR_INT6 INTEGERS, 5, 6,7] ) ; Firebird хранит многомерные массивы в порядке развертывания по строкам. В неко- торых языках, например FORTRAN, ожидается, что массивы хранятся в порядке раз- вертывания по столбцам. В таких случаях позаботьтесь о правильной трансляции порядка элементов между Firebird и используемым языком программирования. Задание диапазона значений индексов для размерностей Размерности массивов в Firebird задаются в виде верхней и нижней границ, называе- мых списком индексов. По умолчанию размерности основаны на 1 — первый элемент массива из п элементов имеет индекс 1, второй элемент имеет индекс 2, а последний элемент индекс и. Например, следующий оператор создает таблицу со столбцом, ко- торый является массивом четырех целых: CREATE TABLE TABLEC (ARR_INT INTEGER[4]); Индексы этого массива 1, 2, 3 и 4. Пользовательские (явные) границы индексов Пользовательская установка верхней и нижней границы может быть явно определена для каждой размерности массива при создании столбца array. Например, программи- сты С и Pascal, знакомые с массивами, основанными на нуле, могут создавать столб- цы массивов с нулевой нижней границей для полного соответствия со структурой массивов в коде приложения. Требуются нижняя и верхняя граница размерности при определении пользователь- ских границ. Используется следующий синтаксис: [нижняя:верхняя] Следующий пример создает таблицу с одноразмерным, основанным на нуле столб- цом массива: CREATE TABLE TABLED (ARR_INT INTEGER[0:3]); /* индексы 0, 1, 2, и 3. */ Каждое задание границ размерности отделяется от следующего запятой. Например, следующий оператор создает таблицу со столбцом массива размерности два, где обе размерности основаны на нуле: CREATE TABLE TABLEE (ARR_INT INTEGER[0:3, 0:3)); Хранение столбцов массивов Как и другие типы данных, реализованные как blob, Firebird хранит идентификатор массива в столбце таблицы базы данных, который ссылается на страницу, содержа- щую фактические данные.
Глава 12. BLOB и массивы 229 Обновления Как и в случае других типов blob, сервер Firebird не может последовательно просмат- ривать индивидуальные элементы при условном обновлении. При этом в одиночном операторе DML возможно изолировать один элемент или набор последовательных элементов, называемый фрагментом, и передавать этот фрагмент для обновления. Добавления Оператор insert не может оперировать с фрагментами. Когда строка добавляется в таблицу, содержащую столбцы массивов, необходимо конструировать и заполнять массив целиком до передачи его insert. Доступ к данным массива Некоторые интерфейсы приложений инкапсулируют функции и дескрипторы API, ограниченный доступ для чтения возможен из хранимых процедур. Дескриптор массива API предоставляет структуру дескриптора массива для передачи серверу массива или фрагмента массива для чтения и записи в базу данных. Эта структура для програм- мистов представлена в файле ibase.h (добавлены комментарии): typedef struct { short array_bound_lower; /* нижняя граница массива или фрагмента */ short array__bound_upper; /* верхняя граница массива юти фрагмента */ } ISC_ARRAY_BOUND; typedef struct { unsigned char array_desc_dtype; /* тип данных элементов */ char array_desc_scale; /* масштаб для числовых типов */ unsigned short array_desc_length; /* длина элемента массива в байтах */ char array_desc_field__name[32] ; /* идентификатор столбца */ char array_desc_relation_name[32]; /* идентификатор таблицы */ short array_dSsc_dimensions; /* количество размерностей */ short array_desc_flags; /* 0=порядок по строкам, 1=порядок по столбцам */ ISC_ARRAY_BOUND array_desc_bounds[16]; /* верхняя и нижняя границы для размерности до 16 */ } ISC_ARRAY_DESC; Документ по InterBase 6 "API Guide" (Руководство по API), опубликованный Borland, содержит детальные инструкции по манипулированию массивами с помощью струк- тур API.
230 Часть III. Типы данных Firebird и домены Ограничения доступа динамического SQL Следующий пример является простой демонстрацией того, как приложение DSQL может получить ограниченный доступ к фрагменту массива через хранимую про- цедуру: create procedure getcharslice( low_elem smallint, high_elem smallint) returns (id integer, list varchar(50)) as declare variable i smallint; declare variable string varchar(lO); begin for select al.ID from ARRAYS al into :id do begin i= low_elem; list = ''; while (i <= high_elem) do begin select a2.CHARARRAY[:i] from arrays a2 where a2.ID = :id into :string; list = list||string; if (i < high_elem) then list = list I|','; i = i + 1; end suspend; end end Пора дальше Последняя глава этой части книги описывает, как объединить тип данных и его до- полнительные атрибуты в домен, который вы можете использовать для определения типов данных в столбцах различных таблиц.
ГЛАВА 13 Домены Домены в Firebird сродни концепции "типы данных, определенные пользователем". Хотя и невозможно создать новый тип данных, в домене вы можете "упаковать" на- бор атрибутов с одним из существующих типов данных, присвоить ему идентифика- тор и после этого использовать его как параметр типа данных для определения столбцов любой таблицы. Определения доменов являются глобальными для базы данных — все столбцы в лю- бой таблице, которые были определены с одним доменом, будут иметь совершенно идентичные атрибуты за исключением тех, которые были локально переопределены. Как было отмечено, домены не могут быть подставлены вместо типов данных при определении аргументов и переменных в хранимых процедурах и триггерах. ПРИМЕЧАНИЕ. Замещение атрибутов домена на уровне столбца обсуждается позже в этой главе. Столбцы, основанные на определении домена, наследуют все атрибуты домена, ко- торые могут быть: ♦ типом данных (обязательно); ♦ значением по умолчанию для insert; ♦ состоянием null; ♦ ограничениями check; ♦ набором символов (только для символьных и blob столбцов); ♦ порядком сортировки (только для символьных столбцов). | ПРИМЕЧАНИЕ. Вы не можете использовать ограничения ссылочной целостно- \ сти данных в домене. Преимущества инкапсуляции определения данных очевидны. Для простого, но обще- го примера предположим, что вы проектируете обращения к множеству малых таб- лиц, где вы собираетесь хранить текстовые описания пронумерованных множеств — таблицы "типов" — типы счетов, типы продуктов, типы пожертвований и т. д. Вы
232 Часть III. Типы данных Firebird и домены принимаете решение, что каждый элемент каждого из этих множеств будет иметь ключ, состоящий из трех символов в верхнем регистре, который указывает на сим- вольное описание или поле заголовка, имеющее максимум 25 символов. Все это требует создания двух доменов. ♦ Домен для указателя будет снак(З) с двумя дополнительными атрибутами: огра- ничение not null, поскольку вы собираетесь его использовать в качестве первич- ного ключа и ключа поиска, и ограничение check для проверки наличия пропис- ных букв. Например: CREATE DOMAIN Туре_Кеу AS CHAR(3) NOT NULL CHECK (VALUE = UPPER (VALUE)); ♦ Домен описания будет varchar(25). Вы хотите запретить для него пустые значе- ния, поскольку таблицы, в которых вы собираетесь его использовать, являются управляющими: CREATE DOMAIN Type_Description AS VARCHAR(25) NOT NULL; Когда вы создадите эти домены, все ваши взаимосвязанные таблицы могут иметь похожие определения, а все таблицы, хранящие ключи, ссылающиеся на такие таб- лицы, будут использовать соответствующий домен для столбцов ключа. Создание домена Синтаксис языка определения данных (DDL) для создания домена: CREATE DOMAIN домен [AS] <тип-данных> [DEFAULT литерал [NULL |USER}] [NOT NULL] [CHECK (<условие-поиска-домена>)] [CHARSET набор-символов\ NONE}] [COLLATE порядок-сортировки]; Идентификатор домена При создании в базе данных домена вы должны задать идентификатор домена, кото- рый является глобально уникальным в базе данных. Разработчики часто используют префикс или суффикс в идентификаторах доменов для улучшения документирова- ния. Например: CREATE DOMAIN D_TYPE_IDENTIFIER... CREATE DOMAIN DESCRIPTION^. . . Тип данных домена Tun данных является единственным обязательным атрибутом, который должен быть установлен для домена — все другие атрибуты необязательны. Он задает тип данных SQL, который будет применен для столбца, определенного с использованием этого
Глава 13. Домены 233 домена. Может быть применен любой тип данных Firebird. Нельзя использовать до- мен как тип данных для другого домена. Следующий оператор создает домен, определяющий массив символьного типа: CREATE DOMAIN DEPTARRAY AS CHAR(31) [4:5]; Следующий оператор создает домен blob текстового подтипа, которому назначен набор символов, перекрывающий набор символов базы данных по умолчанию. Фак- тически он создает специализированный тип примечания для хранения текста на японском языке: CREATE DOMAIN DESCRIPT_JP AS BLOB SUBJTYPE TEXT CHARACTER SET SJIS; Атрибут DEFAULT Домен может определять значение по умолчанию, которое сервер будет использовать при добавлении новой строки, если оператор insert не содержит этот столбец в списке столбцов. Значения по умолчанию могут сэкономить время и избавить от ошибок в процессе ввода данных. Например, для столбца date возможным значением по умолчанию может быть текущая дата, а для столбца userName (имя пользователя) можно указать контекстную переменную currentjjser. Значение по умолчанию может быть: ♦ константой. Значением по умолчанию является заданная пользователем строка, числовое значение или значение даты — часто используется для помещения "ну- левого значения" в столбец, куда не могут помещаться пустые значения (null); ♦ current_timestamp, current.date, current_time или предварительно определенный литерал даты Firebird (см. главу 10)', ♦ user, current_user или current_role (если применяются роли); ♦ CURRENT_CONNECTION ИЛИ CURRENT_TRANSACTION. < ПРИМЕЧАНИЕ. Возможно указание в качестве значения по умолчанию null. Хотя это излишне, поскольку столбцы, допускающие пустое значение, в любом \___\ случае инициализируются по умолчанию значением null. Более того, явное ука- зание значения по умолчанию null может привести к конфликтам, если столбец, использующий домен, должен быть определен с ограничением not null (cm. разд. "Атрибут NOT NULL" далее в этой главе). Следующий оператор создает домен, который должен иметь положительное значение больше 1000. Если в операторе insert не указан столбец, созданный на основе этого домена, столбцу будет назначено значение по умолчанию 9999: CREATE DOMAIN CUSTNO AS INTEGER DEFAULT 9999 CHECK (VALUE > 1000);
234 Часть III. Типы данных Firebird и домены Если ваша операционная система поддерживает использование многобайтовых сим- волов в именах пользователей или если вы используете многобайтовый набор симво- лов при определении роли, то каждый столбец, в котором должно сохраняться такое значение по умолчанию, должен быть определен с использованием подходящего на- бора символов. Когда значения по умолчанию не работают Распространенная ошибка предполагать, что значение по умолчанию будет исполь- зовано, когда Firebird получает значение null в столбце, имеющем значение по умол- чанию. Чтобы быть уверенным в правильном использовании значений по умолча- нию, нужно понимать, что значение по умолчанию будет применено: ♦ только при добавлении новой строки; ♦ только если оператор insert не включает столбец со значением по умолчанию в списке столбцов. Если ваше приложение содержит столбец, имеющий значение по умолчанию, в опе- раторе insert и передает null в списке значений, то будет сохраняться значение null или будет вызвано исключение для столбца, не допускающего пустое значение, неза- висимо от наличия значения по умолчанию. Атрибут NOT NULL Включите этот атрибут в описание домена, если вы хотите, чтобы все столбцы, соз- даваемые на основе этого домена, имели непустое значение. null — который является не значением, а состоянием, — всегда будет недопусти- мым для любого столбца, имеющего атрибут not null. Детальное обсуждение null см. в разд. "Рассмотрение NULL" главы 21. Д ВНИМАНИЕ! Вы не можете переопределить атрибут not null для домена. Рас- смотрите преимущества невключения его в состав атрибутов домена, оставляя, таким образом, возможность добавить этот атрибут при определении столбца. Условия CHECK Ограничение check предоставляет атрибуты домена, ограничивающие содержимое данных, которое может быть сохранено в столбцах, использующих домен. Ограниче- ние CHECK задает условие поиска (условие-поиска -домена), которое должно быть истин- ным до того, как данные могут быть помещены в эти столбцы. Вот синтаксис ограничений check: <условие-поиска-домена> = VALUE <оператор> <val> | VALUE [NOT] BETWEEN <val> AND <val> [ VALUE [NOT] LIKE <val> (ESCAPE <val>]
Гпава 13. Домены 235 I VALUE [NOT] IN (<val> [, <val> . ..]) i VALUE IS [NOT] NULL i VALUE [NOT] CONTAINING <val> | VALUE [NOT] STARTING [WITH] <val> I (<условие-поиска-домена>'! | КОКусловие-поиска-домена> | <условие-поиска-домена> OR <условие-поиска-домена> | <условие-поиска-домена> AND <условие-поиска-домена> <оператор> ={=|<|>|<=|>=|!<|!>|<>|!=} Ключевое слово VALUE value является заполнителем для любой константы, значения переменной или резуль- тата выражения, которые могут быть подставлены в синтаксисе SQL для сохранения данных в столбце, использующем домен. Ограничение check указывает, что value должно проверяться на ограничения, определенные в условиях. Если проверка не соответствует, то вызывается исключение. Если значение null допустимо, то правило должно учитывать этот факт в ограниче- нии check, например: CHECK ((VALUE IS NULL) OR (VALUE > 1000)); Следующий оператор создает домен, который запрещает вводить значение 1000 и меньше, при этом он также неявно запрещает null во множестве допустимых зна- чений: CREATE DOMAIN CUSTNO AS INTEGER CHECK (VALUE > 1000); Следующий оператор ограничивает value одним из четырех заданных значений: CREATE DOMAIN PRODTYPE AS VARCHAR(8) NOT NULL CHECK (VALUE IN ('software', 'hardware', 'other', 'N/A')); Условие проверки может быть выполнено в виде поиска указанного шаблона во вво- димой строке. Например, следующее правило проверяет наличие круглых скобок в коде региона (например, (09)438894749): CREATE DOMAIN TEL_NUMBER AS VARCHAR (18) CHECK (VALUE LIKE ' (0%) % ' ) ,- Подробнее о строковых шаблонах, которые вы можете использовать в выражениях, см. в примечаниях к оператору like в разд. "Операторы SQL" главы 21. Множество условий CHECK Домен может иметь только одно предложение check, однако множество условий мо- жет быть включено в это предложение с помощью операторов and (И — конъюнкция)
236 Часть III. Типы данных Firebird и домены и or (ИЛИ — дизъюнкция). Позаботьтесь о необходимых скобках в выражениях ус- ловий, чтобы исключить получение логических исключений при подготовке опера- тора DDL. Например, следующий оператор будет ошибочным: create domain rhubarb as varchar(20) check (value is not null) and (value starting with 'J'); Он вызовет исключение с сообщением "token unknown" на слове "and". Правильный оператор заключает весь список условий во внешние скобки: create domain rhubarb as varchar(20) check ((value is not null) and (value starting with 'J')); t ПРИМЕЧАНИЕ. Предыдущий оператор проверяет, чтобы входное значение не было null. Это замечательно, однако использование ограничения not null на- прямую является более мощным средством для интерфейса приложения. API может информировать клиентское приложение во время подготовки (prepare) об ограничении not null, в то время как триггеры проверки check не будут вызы- ваться, пока запрос DML не будет фактически отправлен на сервер. Подробности о starting with и других используемых в выражениях операторах SQL см. в главе 21. Ограничение check не может быть переопределено в определении столбца, хотя стол- бец может расширить использование ограничения check домена, добавив свои собст- венные условия. Зависимости в ограничениях CHECK В таблицах ограничения check могут содержать выражения, ссылающиеся на другие столбцы в той же самой таблице или (что менее желательно) ссылающиеся на другие объекты базы данных (таблицы, хранимые процедуры). Конечно, домены не могут ссылаться на другие домены. При этом почти всегда воз- можно неудачное решение определять домен, который ссылается на столбец сущест- вующей таблицы. Например: create domain rather_silly as char(3) check (value in (select substring(registration from 1 for 3) from aircraft)); Концептуально использование выражений в доменах не является столь дикой идеей. Firebird позволяет это, однако не вникает в подробности и понимает такую конструк- цию буквально, что является проблемой проектирования, а не проблемой используе- мой функциональности. Как подход к проектированию баз данных, это плохо объединяется с возможностями обеспечения ссылочной целостности данных, полностью реализованными в Firebird.
Глава 13. Домены 237 Отношения внешнего ключа существуют повсюду, в то время как область действия условия check ограничивается вводом данных. Ограничения check с зависимостями внутри таблицы будут отменены при восстанов- лении базы данных из резервной копии. Они будут "молча" отменены при выборе данных, поскольку таблицы зависимостей не будут еще созданы. Чтобы сделать их опять действующими, нужно переустановить их вручную. Реализация таких прове- рок на уровне домена имеет потрясающие последствия. В некоторых ситуациях, когда зависимости обращаются к весьма статичным табли- цам, чьи имена являются меньшими в алфавитной последовательности (gbak восста- навливает таблицы в алфавитном порядке), такое условие check может быть весьма сомнительным. Проблема остается, если домен не управляет порядком, в котором происходит сохранение данных в таблицах за пределами событий проверки данных, вводимых для столбцов. СОВЕТ. Другим пониманием данной техники является сложность (или невоз- можность) для вспомогательных программ извлекать непробиваемые бомбами скрипты SQL из системных таблиц. Может наступить такое время, когда ваша работа усложнится из-за необходимости правильного извлечения скриптов SQL из базы данных! Если вам совершенно необходимо использовать этот вид условий проверки, обрати- тесь к дополнительным условиям при объявлении столбца. Лучше оцените все аль- тернативы— включая ручное кодирование ссылочных триггеров в случаях, когда внешние ключи для ссылочных полей приведут к проблемам избирательности (се- лективности) индексов1. Атрибут CHARSET/CHARACTER SET Для систем, которым нужно множество наборов символов внутри одной базы дан- ных, объявление доменов, связанных с наборами символов для всех ваших символь- ных столбцов (char, varchar sub_type 1 и массивов символьных типов), может быть очень элегантным способом работы. Синтаксис определения набора символов можно посмотреть в предыдущей главе. Атрибут COLLATE Предложение collate в операторе создания домена задает явную последовательность сортировки для доменов char и varchar. Вы должны выбрать порядок сортировки, который поддерживается набором символов, объявленным наследуемыми и предпо- лагаемым для домена. 1 Чтобы понять эти тонкости, обратитесь к разд. "Темы оптимизации" главы 18.
238 Часть /7/. Типы данных Firebird и домены Синтаксис определения collate см. в главе 11. Список последовательностей сорти- ровки, доступных для каждого набора символов, см. в приложении 8. Использование доменов в определении столбца Пример В некоторой базе данных есть домен sysuser, размером до 31 символа, имеющий зна- чение по умолчанию, получаемое из контекстной переменной current_user: CREATE DOMAIN SYSUSER AS VARCHAR(31) DEFAULT CURRENTJJSER; Объявляемая таблица содержит столбец updated_by, который использует домен sysuser: CREATE TABLE LOANS ( LOAN_DATE DATE, updated_by sysuser, LOAN_FEE DECIMAL(15,2)); Клиент содержит оператор insert для таблицы loans: INSERT INTO LOANS (LOAN_DATE, LOAN_FEE) VALUES ('16-MAY-20041, 10.75); Поскольку этот оператор не содержит столбец updated by в списке столбцов, Firebird автоматически задает имя текущего пользователя alicefbird: SELECT * FROM LOANS; вернет 16-MAY-2004 ALICEFBIRD 10.75 < ПРИМЕЧАНИЕ. Здесь уместно напомнить, что значения по умолчанию для до- мена и столбца применяются только при добавлении и только если столбец, . .-'-X имеющий значение по умолчанию, отсутствует во входном списке оператора insert. Триггеры имеют более надежные способы реализации значений по умол- чанию. Техники обсуждаются в главе 31. Переопределения в доменах Столбцы, созданные с использованием доменов, могут переопределять некоторые наследуемые от домена атрибуты, заменяя наследуемый атрибут эквивалентным предложением атрибута. Определение столбца также может добавлять другие атри- буты. В табл. 13.1 описано, какие атрибуты могут, а какие не могут быть переопреде- лены.
Гпава 13. Домены 239 Таблица 13.1. Переопределение атрибутов доменов и столбцов Атрибут Переопределяется? Замечания Тип данных Нет DEFAULT Да CHARACTER SET Да Также может использоваться, чтобы восстановить для столбца значения по умолчанию базы данных COLLATE Да CHECK Нет Используйте предложение check в операторе create или alter table для добавления в про- верку новых условий NOT NULL Нет Атрибут not null на уровне домена не может быть переопределен на уровне столбца. Во многих случаях лучше оставить для домена возможность пустого значения и добавлять ограничение not null для столбцов, где это необходимо, в опера- торе CREATE ИЛИ ALTER TABLE Следующий оператор демонстрирует, как расширяется список атрибутов столбца, основанного на домене, созданного в одном из предыдущих примеров: CREATE DOMAIN TEL_NUMBER AS VARCHAR(18) CHECK (VALUE LIKE ’(0%)%'); Скажем, мы хотим определить таблицу, содержащую столбец с номером телефона. Нам нужны атрибуты домена, но нам также необходимо обеспечить, чтобы нецифро- вые символы вводились в верхнем регистре: CREATE TABLE LIBRARY_USER ( USER_ID INTEGER NOT NULL. . . . <другие столбцы>, PHONE_NO TEL—NUMBER, CONSTRAINT CHK-TELNUM-UPPER CHECK (PHONE_NO = UPPER(PHONE_NO)) ); Теперь у нас есть дополнительная проверка check для этого столбца. Следующий оператор: INSERT INTO LIBRARY_USER VALUES (USER_ID, PHONE_NO) VALUES (99, '(09) 43889 wish1); будет ошибочным, потому что дополнительное ограничение check требует, чтобы телефонный номер был '(09) 43889 wish'.
240 Часть III. Типы данных Firebird и домены Когда домены не работают Домен не может быть использован: ♦ В функции CAST (<имя_домена> AS <другой_тип>); ♦ вместо явного типа данных при определении входных и выходных аргументов хранимых процедур; ♦ при объявлении типа данных локальной переменной в триггере или хранимой процедуре; ♦ при определении типа данных элементов массива. При этом сам домен может быть массивом. Объявление домена BOOLEAN Firebird не поддерживает тип данных boolean. Домены в стиле boolean являются идеа- лом, потому что вы можете объявлять атрибуты, которые будут постоянными для всех таблиц. Рекомендуется использование минимальных типов данных: char для переключателя T[rue]/F[aise] или Y[es]/N[o] или smallint для пары 1/0. Следующие примеры предлагают способы, которыми вы можете реализовать ваши типы boolean. Пример 13.1. Двухфазный переключатель со значением по умолчанию т1 (False) CREATE DOMAIN D_BOOLEAN AS CHAR DEFAULT 1F' NOT NULL CHECK(VALUE IN ('T', 'F')); Пример 13.2. Трехфазный переключатель, допускающий значение unknown (т. е. null): CREATE DOMAIN D_LOGICAL AS SMALLINT CHECK (VALUE IS NULL OR VALUE IN (1,0)); Пример 13.3. Трехфазный переключатель, представляющий unknown как значение: CREATE DOMAIN DJ3ENDER AS CHAR(4) DEFAULT 'N/K' NOT NULL CHECK (VALUE IN ('FEM', 'MASC, 'N/K')); Д ВНИМАНИЕ! He используйте boolean, unknown, true или false в качестве имен для булевых доменов. В Firebird это зарезервированные слова. Истинно логиче- ские типы планируются в Firebird 2 и могут появиться в промежуточных релизах между 1.5 и 2. Изменение определения домена Оператор языка определения данных alter domain может быть использован для из- менения любого аспекта существующего домена за исключением установки not null. Изменения, сделанные вами в определении домена, воздействуют на все определения столбцов, основанных на этом домене, если только соответствующие атрибуты не были переопределены на уровне таблицы.
Глава 13. Домены 241 Домен может быть изменен его создателем, пользователем SYSDBA или (в Linux/UNIX) любым пользователем с привилегией root операционной системы. Используя alter domain, вы можете: ♦ переименовать домен; ♦ изменить тип данных; ♦ удалить существующее значение по умолчанию; ♦ установить новое значение по умолчанию; ♦ удалить существующее ограничение check; ♦ добавить новое ограничение check. < ПРИМЕЧАНИЕ. Существует только один путь "изменить" установку not null для домена — это удалить домен и заново создать его с желаемой комбинацией ха- у; \ рактеристик. Вот синтаксис оператора: ALTER DOMAIN { имя | старое-имя ТО новое-имя }{ [SET DEFAULT {литерал | NULL | USER | etc.}] I [DROP DEFAULT] I [ADD [CONSTRAINT] CHECK ^условия-соответствия^ ] ] [DROP CONSTRAINT] (TYPE тип-данных }; Примеры Этот оператор устанавливает новое значение по умолчанию для домена book_group: ALTER DOMAIN BOOK_GROUP SET DEFAULT -1; В следующем операторе имя домена book_group заменяется на publ_group: ALTER DOMAIN BOOK_GROUP TO PUBL_GROUP; Ограничения на изменение типов данных Предложение type в alter domain позволяет заменить тип данных на другой разре- шенный тип данных. Разрешенные типы преобразований см. на рис. 8.1. Недопустим никакой тип преобразования, который может привести к потере данных. Например, количество символов в домене не может быть сделано меньше размера наибольшего значения в любом столбце, использующим этот домен2. 2 При изменении типов домена учитывайте, что в хранимых данных могут быть данные, которые не пройдут преобразование из одного типа в другой. Например, varchar в integer сконвертируется, только если varchar будет содержать цифры и числа, допустимые для integer. — Прим. науч. ред.
242 Часть III. Типы данных Firebird и домены Преобразование числового типа данных в символьный тип требует минимальной длины для символьного типа, как указано в табл. 8.3. Следующий оператор меняет тип данных домена book_title с char<80) на VARCHAR(100) : ALTER DOMAIN BOOK_TITLE TYPE VARCHAR(100); Удаление домена Оператор drop domain удаляет из базы данных существующее описание домена при условии, что домен не используется в каком-либо описании столбца базы данных. Чтобы не получить исключений, применяйте alter table для удаления всех столб- цов, использующих домен, а затем выполняйте drop domain. Лучший способ сделать это за одно задание — использовать скрипт DDL. См. разд. "Скрипты схемы" в сле- дующей главе. Домен может быть удален его создателем, пользователем SYSDBA или (в Linux/UNIX) любым пользователем с привилегией root операционной системы. Вот синтаксис оператора: DROP DOMAIN name; Следующий оператор удаляет ненужный домен: DROP DOMAIN rather_silly; Пора дальше Ознакомившись с определением данных, теперь пора приступить к более интересной теме — определению и разработке баз данных. Следующая группа глав проведет вас через концепцию баз данных, ее объектов и подмножество языка SQL, называемого Data Definition Language (DDL), который используется для управления объектами и задает правила их поведения. Глава 14 начнется с изложения базовых правил разработки моделей в реляционных базах данных. Глава закончится разделом о работе со скриптами БД.
ЧАСТЬ IV База данных и ее объекты Глава 14. Чертежная доска для базы данных Глава 15. Создание и ведение базы данных Глава 16. Таблицы Глава 17. Ссылочная целостность данных Глава 18. Индексы

ГЛАВА 14 Чертежная доска для базы данных Конечно же, база данных хранит данные. Однако данные сами по себе не могут ис- пользоваться, если они не были сохранены в соответствии с некоторыми правилами, которые, во-первых, определяют их смысл и значение и, во-вторых, позволяют их отыскивать соответствующим образом. База данных, существующая в контексте сис- темы управления базами данных (СУБД), такой как Firebird, включает в себя множе- ство "вещей" помимо данных. Firebird является реляционной системой управления базами данных. По существу она разработана для создания и поддержания абстрактных структур данных не только для хранения данных, но также для поддержки отношений, оптимизации скорости и обеспечения целостности, в соответствии с которой запрашиваемые данные могут быть возвращены клиентским приложениям. Пользователь SYSDBA и пароль Во всех версиях Firebird, включая 1.5, пользователь SYSDBA имеет полные права ко всем базам данных на сервере. Инсталляционные скрипты устанавливают базу дан- ных безопасности с паролем по умолчанию masterkey. Некоторые релизы 1.5 для Linux запускают скрипт, который генерирует новый па- роль для пользователя SYSDBA. Вы можете посмотреть сгенерированный пароль в файле SYSDBA.password в корневом каталоге Firebird. /Д ВНИМАНИЕ! Пароль masterkey широко известен. Убедитесь, что вы изменили f \ его на малопонятную восьмисимвольную строку. См. инструкции в главе 34. Метаданные Все вместе объекты, определенные в базе данных, называются метаданными или, более традиционно, ее схемой. Процесс создания и модификации метаданных назы- вается определением данных. Термин "определение данных" также часто применяется к описанию одного объекта и его атрибутов. В этом разделе в деталях рассматриваются концепции, терминология и язык опреде- ления данных.
246 Часть IV. База данных и ее объекты Язык определения данных Основные структуры базы данных — ее таблицы, просмотры и индексы — создаются с использованием подмножества языка SQL Firebird, известного как язык определе- ния данных (Data Definition Language, DDL). Оператор DDL начинается с одного из ключевых слов create, alter, recreate или drop, которые означают создание, измене- ние, пересоздание или удаление одного объекта, соответственно. База данных, ее объекты, правила и отношения объединяются для формирования структуры реляци- онной базы данных. Системные таблицы Firebird хранит метаданные в множестве таблиц, которые он создает прямо в базе данных, — в системных таблицах. Идентификаторы всех системных таблиц начи- наются с символов "RDBS". Например, таблица, которая хранит определения и дру- гую информацию о структурах всех таблиц в вашей базе данных, называется rdb$relations. Связанная с ней таблица rdb$relation_fields хранит информацию и описания всех столбцов в каждой таблице. Такая "база данных в базе данных" является высоко нормализованной. Операторы DDL разработаны для выполнения безопасных операцйй с таблицами метаданных и в полном соответствии с каскадными эффектами. Возможно изменение данных в системных таблицах посредством обычных операций SQL. Некоторые инструменты администратора, такие как isql и gfix, выполняют внутренние изменения данных в системных таблицах. При этом, будучи сложной системой управления базами данных, Firebird не была разработана в предположении, что конечный пользователь будет манипулировать строками системных таблиц. Д ВНИМАНИЕ! Не рекомендуется пренебрегать операторами DDL и самостоя- тельно изменять системные таблицы с помощью кода приложений или через интерактивные инструменты. Системные таблицы являются "мета-метаданными" любой базы данных. Любое вмешательство человека, скорее всего, приведет к непредсказуемым повреждениям. Запросы select к системным таблицам являются замечательным средством и могут быть очень полезными для отображения таких вещей, как наборы символов, зависи- мости и т. д. Полное описание системных таблиц см. в приложении 9. Проектирование базы данных Хотя реляционные базы данных являются очень гибкими, существует только один способ обеспечения целостности данных и удовлетворительной производительности базы данных — основательное проектирование базы данных. Не существует никакой встроенной защиты от скверных проектных решений.
Глава 14. Чертежная доска для базы данных 247 Хорошее проектирование базы данных имеет несколько преимуществ. ♦ Удовлетворяет требованиям пользователей к содержимому базы данных. Преж- де чем вы сможете начать проектирование базы данных, вы должны провести об- ширные исследования требований пользователей к базе данных, а также выяс- нить, как база данных будет использоваться. Наиболее гибкие проекты баз дан- ных на сегодняшний день создаются во время хорошо управляемого процесса анализа, создания прототипа и тестирования; в этот процесс вовлекаются все лю- ди, которые будут использовать базу данных. ♦ Обеспечивает полноту и целостность данных. При проектировании таблицы вы определяете некоторые атрибуты и ограничения, фильтрующие данные, которые пользователь или приложение могут вводить в таблицу и ее столбцы. Выполняя проверку данных до их помещения в таблицу, база данных реализует правила мо- дели данных и обеспечивает целостность данных. ♦ Предоставляет естественную, простую в понимании структуру информации. Хорошее проектирование делает запросы более понятными — мала вероятность того, что пользователи привнесут несогласованность в данные или что им потре- буется вводить избыточные данные. ♦ Удовлетворяет требованиям пользователей к производительности. Хорошее проектирование базы данных обеспечивает лучшую производительность. Если таблицы будут слишком большими или будет слишком много (или слишком ма- ло) индексов, результатом станет долгое ожидание. Если база данных является слишком большой с высоким объемом транзакций, проблемы производительно- сти, как результат плохого проектирования, будут увеличиваться. ♦ Изолирует систему от ошибок проектирования в последующих циклах разра- ботки. Описание и анализ База данных абстрактно представляет совокупность организации, отношений, правил и процессов. Прежде чем подойти к началу проектирования структур и правил базы данных, аналитик/дизайнер должен многое сделать, работая с людьми, вовлеченными в определение структур, правил и требований реальной жизни, из которых будет соз- дан проект базы данных. Следует особенно подчеркнуть важность скрупулезного описания и анализа. Анализ логических данных является итеративным процессом детализации и поиска сути во множестве входных данных, задач и выходных данных, которые должны быть реализованы в базе данных. Большие неупорядоченные структуры информации постепенно сокращаются до меньших, более специализированных объектов данных и понемногу начинают отображать модель данных. Важной частью такого процесса редуцирования является нормализация — разделение групп элементов данных с целью установления основных отношений, уменьшения избыточности и объединения связанных элементов данных в структуры, которыми можно эффективно манипулировать.
248 Часть IV. База данных и ее объекты Эта фаза может быть одной из наиболее сложных задач для проектировщика базы данных, особенно в окружении, где бизнес был приспособлен к работе с электрон- ными таблицами и настольными базами данных. Прискорбно, что даже в среде кли- ент-сервер можно найти слишком много медленно работающих, подверженных раз- рушению баз данных, которые были "спроектированы" с использованием отчетов и электронных таблиц в качестве своей основы1. Модель данных о база данных Тот "мир", который был получен в процессе описания и анализа, является чернови- ком для структур ваших данных. Считается, что логическая модель должна описы- вать отношения и наборы. Обычная ошибка (и западня, присущая всем инструментам CASE) слепо транслировать модель данных в схему базы данных. В сложных систе- мах управления базами данных, таких как Firebird, структура таблицы не всегда представляет оптимальный объект, из которого должны отыскиваться данные. Запро- сы, просмотры, массивы, вычисляемые столбцы и хранимые процедуры выбора яв- ляются лишь небольшой частью доступных механизмов поиска и хранения, которые будут влиять на вашу реализацию модели при физическом проектировании. Даже прекрасная модель данных будет иметь недостатки в гибкости и экономично- сти, если она не принимает в расчет производительность и экономичность динамиче- ских характеристик сервера. Динамические структуры для выборки и манипулирова- ния данными являются артериями баз данных клиент-сервер. Одна база данных или много? Один сервер Firebird— за исключением локального встроенного сервера под Windows — может управлять множеством баз данных через свою собственную физи- ческую файловую систему. Для больших предприятий не является необычным ис- пользование нескольких баз данных для обслуживания отдельных подсистем подраз- делений. Поскольку одной базе данных ничего не известно об объектах и зависимо- стях в другой, требуется аккуратное проектирование, планирование и балансировка системных ресурсов и сетевых сервисов для интеграции таких независимых систем. Обычно подобные базы данных периодически синхронизируются с помощью систе- мы репликации. При проектировании принимайте во внимание, что Firebird не поддерживает запросы, соединяющие или объединяющие таблицы из различных баз данных. При этом он поддерживает одновременные запросы к множеству баз данных в контексте одной транзакции и при двухфазном ее подтверждении. Таким образом, приложения могут выполнять задачи, которые работают с образами данных из двух и более баз данных 1 Существует море литературы, содержащей описания техник эффективного анализа задач бизнеса для проектировщика реляционных баз данных, размышления об уровне нашей про- фессиональности. Автор особенно рекомендует Data Modeling Essentials: Analysis, Design and Innovation, 2nd Edition by Graeme C. Simsion (Coriolis, 2000).
Глава 14. Чертежная доска для базы данных 249 и выполняют операторы DML к одной базе данных, используя данные, прочитанные из другой. Подробности о транзакциях с несколькими базами данных и о двухфазном подтверждении см. в главе 27. Физические объекты Таблицы Таблица базы данных обычно визуализируется в виде двумерного блока, состоящего из столбцов (вертикальная размерность) и строк (горизонтальная размерность). Хра- нимые атрибуты индивидуальных элементов данных задаются в столбцах (обычно связанных с или зависящих от другого столбца) и строках. Таблица может иметь лю- бое количество строк (до 232) или совсем ни одной строки. Хотя каждая строка в од- ной таблице совместно использует спецификации ее столбцов с любой другой стро- кой, строки не зависят от других строк в той же таблице. СОВЕТ. Firebird поддерживает техники реализации ссылающихся на себя таб- лиц — структура строк, которая задает зависимости между строками в той же таблице. Подробности см. в разд. "Ссылающиеся на себя отношения" главы 17. Файлы и страницы Если вы переходите на Firebird с системы баз данных, которая реализует таблицы в виде физических столбцов и строк файловой системы, то Firebird может преподнести вам некоторые сюрпризы. В Firebird все данные одной базы данных хранятся в одном файле или во множестве связанных файлов. В многофайловых базах данных не суще- ствует взаимозависимости между любым объектом базы данных и отдельным эле- ментом множества файлов базы данных. В рамках файла сервер Firebird управляет блоками диска одного размера, называе- мыми страницами базы данных. Он управляет некоторыми различными "типами" страниц в соответствии с хранимыми типами данных — например, обычные столбцы таблицы, blob, индексы. Когда требуется, сервер размещает новый блок в файловой системе хоста. Все страницы независимо от типа имеют одинаковый размер. Размер страницы должен быть указан в операторе create database. Он может быть изменен только при выполнении резервного копирования и восстановления базы данных с новым размером страницы при использовании утилиты gbak. В отличие от файловых систем управления данными Firebird совсем не поддерживает данные таблиц в табличном формате. Строки одной таблицы могут не находиться рядом с другими строками той же таблицы. В действительности строки одной табли- цы могут размещаться в разных файлах и на разных дисках. Сервер использует раз- нообразные типы инвентарных страниц для хранения информации о физическом расположении строк, принадлежащих каждой таблице.
250 Часть IV. База данных и ее объекты Столбцы и ПОЛЯ Обобщенно, столбец является совокупностью атрибутов, определяющих элемент данных, который может быть сохранен в одной указанной ячейке в структуре строки таблицы слева направо. При этом столбцы фактически не существуют в таблицах базы данных. Каждый раз, когда запрос обращается к серверу, этот запрос задает на- бор столбцов и одну или более операций, выполняемых над этими столбцами. Столбцы не обязательно должны располагаться в том же порядке слева направо, как они были определены в таблице. Например, оператор SELECT FIELD3, FIELD1, FIELD2 FROM ATABLE; выведет набор, где порядок столбцов будет соответствовать указанному в запросе. Запрос может указывать столбцы из разных таблиц: соединений, подзапросов, объ- единений. Он может определять столбцы, вовсе отсутствующие в базе данных, сде- лав их вычисляемыми или даже просто задавая их как именованные константы. Некоторые люди используют термин "поле" вместо столбца, например: "У меня есть таблица tablei, содержащая три поля". Часто книги по реляционным базам данных не одобряют использование "поле" как замена для "столбец", предпочитая использовать "поле" в смысле "значение в столбце" или "ссылка на столбец". В этой книге "поле" используется только как термин для обобщения концепции столбца, аргумента и локальной переменной и для ссылок на выходные элементы, создаваемые во время выполнения. "Столбец" используется для ссылок на физиче- ские столбцы, определенные для таблиц. Ключи Первичный ключ Основной частью процесса проектирования базы данных является выделение в логи- ческой модели базы данных для каждой таблицы одного уникального столбца или структуры из нескольких столбцов, которая отличает каждую строку от любой дру- гой строки в таблице. Такой уникальный столбец или комбинация столбцов является логическим первичным ключом (primary key). Когда вы создаете вашу физическую модель, вы используете ограничение primary key, чтобы сообщить СУБД, какой стол- бец или столбцы формируют такую уникальную идентификационную структуру. На таблицу вы можете определить только одно ограничение primary key. Синтаксис рас- сматривается в разд. "Ограничения" главы 16. Другие уникальные ключи В процессе вашего моделирования может случиться так, что по разным причинам вам будет нужно более одного уникального столбца или структуры столбцов в таб- лице. Для поддержания требуемой уникальности таких столбцов или структур Firebird предоставляет ключ-ограничение unique. Это является альтернативой пер- вичному ключу и при необходимости иногда может быть использовано вместо пер- вичного ключа.
Глава 14. Чертежная доска для базы данных 251 Внешние ключи "Кабелями", которые делают реляционную базу данных "реляционной", являются внешние ключи (foreign key). Это столбец или структура столбцов, которая в вашей модели данных является стороной "многие" в отношении один-ко-многим. При фи- зическом проектировании внешний ключ соответствует столбцу или структуре столбцов первичного ключа таблицы стороны "один" в этом отношении. В следующей простой модели для примера детальные строки заказа связаны с заго- ловком заказа через ключ order_number. Рис. 14.1. Простая связь 1'акая модель требует, чтобы каждая строка заголовка имела уникальный rder number и существовала, по меньшей мере, одна детальная строка заказа для каждой заголовочной строки заказа. Другие правила могут применяться к факту су- ществования и к связи. Firebird предоставляет мощные процедуры триггеров для ус- тановки, согласования и применения правил к отношениям. Дополнительно он может автоматизировать множество типичных правил управления отношениями, включая ограничение foreign key с дополнительными аргументами действий. Основой для такого ограничения являются сгенерированные системой триггеры ссылочной цело- стности. Поддержка ссылочной целостности в Firebird вкратце обсуждалась ранее в разд. "Ссылочная целостность" и будет детально рассмотрена в главе 17. Суррогатные ключи Столбец, который в вашем анализе был определен как первичный ключ, или элемент первичного ключа почти всегда хранит элемент данных, имеющий некоторое значе- ние. Возьмем, к примеру, таблицу, хранящую данные о человеке: CREATE TABLE PERSON ( FIRST_NAME VARCHAR(30) NOT NULL, LAST_NAME VARCHAR(50) NOT NULL,
252 Часть IV. База данных и ее объекты PHONE_NUMBER VARCHAR(18) NOT NULL, ADDRESS_1 VARCHAR(50), . . . ) ; Проектировщик принимает решение, что комбинация (first_name, last_name, phone number) является хорошим кандидатом для первичного ключа. Люди могут ис- пользовать один и тот же телефонный номер, но весьма маловероятно, что два чело- века с одинаковыми именем и фамилией будут использовать один и тот же номер телефона, верно? Таким образом, проектировщик делает следующее: ALTER table person ADD CONSTRAINT PK_PERSON PRIMARY KEY (LAST_NAME, FIRST_NAME, PHONE_NUMBER) ; Первая проблема с этим первичным ключом в том, что каждый элемент имеет смысл. Каждый элемент поддерживается человеком и может быть изменен или записан с ошибками. Два ключа ('Smith', 'Магу', '43889474') И ('SMITH', 'Магу', '43889474') Не являются одинаковыми и оба могут быть помещены в эту таблицу. Какая запись бу- дет изменена, если магу выйдет замуж или изменит номер телефона? Вторая проблема заключается в том, что этот сложный ключ будет распространяться в качестве внешнего ключа в любых таблицах, зависящих от person. Подвергается риску не только целостность этого отношения при изменениях или ошибках в дан- ных, но это также требует большого объема памяти — потенциально 98 символов — при реализации отношения внешнего ключа. Реальные накладки могут появиться, если эти столбцы используют многобайтовые наборы символов или не двоичные порядки сортировки. Размеры индексов ограниче- ны 252 байтами. Для ключей обязательно создаются индексы. Такой ключ будет не- возможен просто по причине слишком большого размера. Создание атомарного ключа Важным принципом для хорошего проектирования реляционной базы данных явля- ется атомарность. В контексте первичных и внешних ключей атомарность означает, что никакой ключ как элемент данных не должен иметь смысл; он не должен иметь никакой другой роли или функции, кроме как быть ключом. Решение заключается в добавлении дополнительного столбца в таблицы для исполь- зования в качестве искусственного или суррогатного первичного ключа — уникаль- ный, ограниченного размера столбец, желательно генерируемый системой, который заменяет (замещает) функцию теоретического первичного ключа. Firebird предостав- ляет объекты generator, которые могут быть использованы для создания требуемых уникальных серий чисел bigint для первичного ключа размером 8 байт или меньше. Общую технику реализации автоинкрементного первичного ключа (при отсутствии ручной работы) см. в разд. "Генераторы" главы 9 и в главе 31. ВНИМАНИЕ! Атомарность ключа должна поддерживаться в приложениях сокры- тием его от пользователя или хотя бы его свойством только для чтения.
Глава 14. Чертежная доска для базы данных 253 Итог — суррогатные ключи против естественных ключей Разработчики баз данных обычно занимают четкую позицию "за" или "против" ис- пользования суррогатных ключей. Позиция автора по использованию атомарности очевидна. Несмотря на это, в интересах справедливости аргументы за и против пред- ставлены в табл. 14.1. Таблица 14.1. Суррогатные (искусственные) ключи в сравнении с естественными Особенность За Против Атомарность Суррогатные ключи не восприни- маются как данные и никогда не изменяются Естественные ключи по своей сути нестабильны, потому что они являют- ся предметом человеческих ошибок и внешних изменений Удобство Естественные ключи несут ин- формацию, сокращающую необхо- димость выполнения соединений или дополнительных чтений для поиска данных в контексте. Естественные ключи более удоб- ны при использовании в интерак- тивных инструментах запросов Суррогатные ключи не несут никакой информации помимо их функции свя- зи, требования соединений или под- запросов поиска связанных "осмыс- ленных" данных Размер ключа Суррогатные ключи компактны Естественные ключи имеют боль- ший размер и часто усложняют со- ставные ключи, которые усложняют запросы и схему Навигация Суррогатные ключи обеспечивают чистую, быструю навигацию по коду Естественные ключи обычно не яв- ляются подходящими при навигации в стиле кода по причине порядка сорти- ровки, денормализации и размера Нормализация Суррогатные ключи могут быть нормализованы в базе данных Естественные ключи имеют тенден- цию к усложнению, распространению денормализации данных внешних ключей Должны ли вы проектировать базу данных, смешивая естественные и искусственные ключи? Крайней точкой зрения является последовательный подход в проектирова- нии — выберите естественные или искусственные ключи и применяйте это правило без исключений. Однако более умеренный подход может предоставить лучшее из обоих миров. Практичным является использование естественных ключей для ста- бильных "управляющих" таблиц соответствия, ключей, которые редко изменяются, никогда не участвуют в составных ключах и часто появляются в выходных данных. Д ВНИМАНИЕ! При проектировании ключей для базы данных Firebird помните, что ключи порождают индексы, а индексы в Firebird ограничены в размере до 252 байт. Сложные последовательности сортировки и многобайтовые междуна- родные наборы символов уменьшают количество символов в данных, которые могут быть использованы в качестве индексов.
254 Часть IV. База данных и ее объекты Ключи не являются индексами Индексы не являются ключами. Ключи— ограничения на уровне таблицы. Сервер базы данных реагирует на объявление ограничений созданием множества объектов базы данных для их поддержки. Для ограничений первичных и уникальных ключей он создает уникальный индекс из столбца (столбцов), указанных в ограничениях. Для внешних ключей он создает неуникальный индекс из указанных столбцов, сохраняет записи для отношения и создает триггеры для выполнения нужных действий. ♦ Ключи являются ограничениями. ♦ Индексы требуются для поддержания ограничений. Д ВНИМАНИЕ! Вы не должны создавать свои собственные индексы, которые дуб- лируют создаваемые системой индексы для поддержания ограничений. Это яв- ляется важной мерой предосторожности в отношении производительности, о чем неоднократно повторяется в разных местах книги. Разд. "Темы оптимизации" главы 18 объясняет, почему дублирование таких индексов может ухудшить про- изводительность запросов. Ссылочная целостность данных Случайное изменение или удаление строк, имеющих зависимости, разрушает целост- ность ваших данных. Обычно ссылочная целостность данных является выражением, описывающим уровень, на котором зависимости базы данных защищены от разру- шения. В контексте настоящего руководства мы ссылаемся на встроенные механиз- мы поддержки отношений внешних ключей и выполнения желаемых действий при изменении значения первичного ключа в главной таблице или при удалении строки этой таблицы. Синтаксис ограничений формальной ссылочной целостности данных Firebird под- робно обсуждается в главе 17. Индексы и планы запросов Если внешние ключи являются "кабелями", делающими базу данных реляционной, то индексы могут рассматриваться как поставщики "полосы частот". Хорошее индекси- рование повышает скорость; отсутствие индексов или плохие индексы замедляют поиск, соединение и сортировку. Как средство управления реляционной базой данных, Firebird может связать почти любой объект столбца с почти любым другим объектом столбца (за исключением различных типов blob, включая массивы) с использованием ссылок на их идентифи- каторы. Однако при увеличении количества строк, связанных столбцов и таблиц в запросах производительность падает. Когда столбцы, которые отыскиваются, соединяются и сортируются, индексированы удобным образом, производительность (время выполнения и использования ресур- сов) может резко улучшиться. Необходимо также сказать, что слабое индексирование может резко ухудшить производительность!
Глава 14. Чертежная доска для базы данных 255 Firebird использует алгоритмы оптимизации, которые в большой мере основаны на оценке затрат (cost-based). При подготовке запроса оптимизатор вычисляет относи- тельные стоимости использования или игнорирования доступных индексов и воз- вращает клиенту план запроса, сообщая о своем выборе. Хотя можно разработать и передать оптимизатору ваш собственный план запроса— важное средство серверов реляционных СУБД, которые используют оптимизацию, основанную на правилах, — чаще всего оптимизатор Firebird знает лучше. Планы Firebird являются более полез- ными в выявлении и устранении проблем с индексами. Проектирование и создание индексов рассматривается в главе 18. Просмотры Firebird предоставляет возможность создания и сохранения предварительно опреде- ленных спецификаций запросов, называемых просмотрами (view), которые в боль- шинстве случаев могут рассматриваться просто как если бы они были таблицами. Просмотр является классом наследуемой таблицы, которая хранит данные. Для мно- гих задач — особенно для тех, когда доступ к отдельным столбцам таблиц нужно запретить или когда спецификация отдельного запроса не может предоставить тре- буемый уровень абстракции — просмотры решают сложные проблемы. Просмотры и другие наследуемые таблицы обсуждаются в главе 24 и должны изу- чаться вместе с другими главами части V. Хранимые процедуры и триггеры Хранимые процедуры и триггеры являются модулями компилированных, выполняе- мых кодов, которые выполняются на сервере. Исходный код пишется на расширении языка SQL, называемом процедурным SQL, или PSQL. Хранимые процедуры могут быть выполняемыми процедурами и селективными про- цедурами. Они могут получать входные аргументы и возвращать выходные наборы. Выполняемые процедуры выполняются полностью на сервере и могут возвращать набор констант из одной строки (одиночный набор) по завершении выполнения. Се- лективные процедуры генерируют многострочные наборы из нуля или более строк, которые могут использоваться клиентскими приложениями различными способами, как и любой другой выходной набор. Триггеры являются специализированным видом модулей PSQL, которые могут быть объявлены для выполнения в одном или более из шести состояний/фаз операции (до и после добавления, изменения и удаления) в процессе операций манипулирова- ния данными (DML) таблицы, которая владеет этими триггерами. Группы триггеров могут быть объявлены для каждой фазы для выполнения в определенной последова- тельности. Начиная с релиза 1.5, поведение любой или всех операций DML могут быть объединены с условиями выполнения в один модуль триггера "до" или "после". Триггеры не принимают входных аргументов и не возвращают выходных наборов. Хранимые процедуры могут вызывать другие хранимые процедуры. Триггеры могут вызывать хранимые процедуры, которые, в свою очередь, могут вызывать другие хранимые процедуры. Триггеры не могут быть вызваны ни из клиентских приложе- ний, ни из хранимых процедур.
256 Часть IV. База данных и ее объекты PSQL имеет механизмы обработки исключений и внешних событий. Любое количе- ство сообщений об исключениях может быть определено в качестве объектов базы данных с использованием операторов create exception. Внешние события создаются внутри модуля PSQL, а приложения могут устанавливать структуры для их "прослу- шивания". Подробное обсуждение написания и использования модулей PSQL, исключений и событий см. в части VII. Соглашения по именованию объектов базы данных и ограничения Должны соблюдаться ограничения в именовании объектов базы данных. ♦ Начинайте каждое имя с буквенного символа (A-Z или a-z). ♦ Ограничивайте имена объектов 31 символом. Некоторые объекты, например име- на ограничений, могут иметь длину до 27 символов. ♦ Допустимые символы для имен файлов базы данных — как и для всех объектов метаданных в Firebird — включают знаки доллара ($), подчеркивания (_), цифры от 0 до 9, буквы от А до Z и от а до z. ♦ Обеспечьте требования уникальности в базе данных: • во всех случаях имена объектов одного типа — например, таблиц — должны быть уникальными; • имена столбцов в таблице должны быть уникальными в этой таблице. Все дру- гие идентификаторы объектов должны быть уникальными в базе данных. ♦ Исключите использование зарезервированных слов, пробелов, диакритических знаков и любых символов ASCII с кодом больше 127: • в диалекте 1 они вообще не могут быть использованы; • в диалекте 3 вы можете ограничить "неправильные" идентификаторы, исполь- зуя пару символов двойной кавычки. Подробности будут дальше. Идентификаторы с разделителями SQL-92 В базах данных диалекта 3 Firebird поддерживает соглашение ANSI SQL об иденти- фикаторах с разделителями. Для использования зарезервированных слов, диакрити- ческих знаков, чувствительных к регистру строк или пробелов в имени объекта за- ключите имя в двойные кавычки. Теперь оно становится идентификатором с разде- лителями. Идентификаторы с разделителями должны всегда быть представлены с двойными кавычками. ПРИМЕЧАНИЕ. В диалекте 1 зарезервированные слова, диакритические знаки и пробелы недопустимы в именах объектов; идентификаторы, чувствительные к регистру, не поддерживаются.
Глава 14. Чертежная доска для базы данных 257 Имена, заключенные в двойные кавычки, являются чувствительными к регистру. На- пример, SELECT "CodAR" FROM "МуТаЫе" отличается от SELECT "CODAR" FROM "MYTABLE" Заключать в кавычки или нет Соглашение по двойным кавычкам для идентификаторов объектов было введено для совместимости со стандартами. Для тех, кто привык в прошлом в InterBase к нечув- ствительности к регистру, новая "возможность" будет в лучшем случае сбивать с толку, в худшем — раздражать. Если вы определяете объекты в двойных кавычках, вы должны везде и всегда ис- пользовать их в двойных кавычках и обеспечивать соответствие регистра. Большин- ство опытных разработчиков Firebird рекомендует отказаться от них за исключением редких случаев, когда вам нужно использовать "неправильные" идентификаторы. Выбор за вами. Исключение соответствия регистру Если у вас все идентификаторы, заключенные в кавычки, записаны в верхнем регист- ре, вы можете использовать их в SQL без кавычек и трактовать их нечувствительны- ми к регистру. Возможность делать это связана со способом хранения идентификато- ров в таблицах внутренней схемы и с последовательностью, в которой сервер разре- шает идентификаторы в процессе поиска. СОВЕТ. Большинство графических инструментов базы данных Firebird предос- тавляет режим автоматического применения заключенных в кавычки идентифи- каторов. Один или два из этих инструментов фактически применяют идентифика- торы в кавычках для всех объектов базы данных по умолчанию. Если у вас нет серьезных оснований использовать это, рекомендуется найти "отключающий переключатель" и отменить идентификаторы в кавычках. Соглашения по именованию файлов базы данных Установленное соглашение по именованию файлов баз данных Firebird для любой платформы — использование трехсимвольного суффикса fdb для первичного файла, а для имен вторичных файлов ГО1, ГО2 и т. д. Это только соглашение— файлы Firebird могут иметь любое расширение или не иметь расширения вовсе. По причине известных проблем с серверами ХР, использующими SystemRestore для файлов с расширением gdb, разработчикам рекомендуется заменить традиционный суффикс файлов InterBase при миграции баз данных в Firebird. Имя базы данных безопасности— security.fdb в релизе 1.5 и выше, isc4.gdb в релизе 1.0.x— не должно изменяться. К сожалению, у Firebird 1.0.x нет средств изменения требуемого суффикса gdb. 9 Зак. 420
258 Часть IV. База данных и ее объекты Скрипты схемы В Firebird, как и во всех других системах управления базами данных SQL, вы создае- те вашу базу данных и ее объекты (метаданные или схема базы данных), используя операторы из специализированного подмножества операторов SQL, называемого языком определения данных (Data Definition Language, DDL). Пакет операторов DDL в текстовом файле называется скриптом. Скрипт или множество скриптов могут быть обработаны программой isql непосредственно из командной строки или при помощи инструмента, предоставляющего дружественный интерфейс. Список таких инструментов см. в приложении 5. Скрипты Firebird Скрипт для создания и изменения объектов базы данных иногда называют файлом определения данных или скриптом DDL. Скрипт DDL может содержать определен- ного рода операторы isql, а также некоторые из команд зет <нараметр>. commit также является допустимым оператором в скрипте. С ПРИМЕЧАНИЕ. Утилита isql (интерактивный SQL) является программой команд- ной строки, доступной на всех платформах; входит в состав комплекта поставки _____Firebird. Во всех случаях, кроме встроенного сервера для Windows, isql инсталли- руется в каталог /bin корневого каталога Firebird. Полные инструкции см. в гла- ве 37. Другие скрипты могут быть написаны для добавления основных, или "управляю- щих", данных в таблицы, изменения столбцов, преобразования данных и выполнения других задач, включающих манипулирование данными. Такие скрипты называются скриптами DML (для языка манипулирования данными, Data Manipulation Language). Команды DDL и DML могут одновременно присутствовать в скриптах. Однако для устранения проблем с целостностью данных строго рекомендуется размещать опера- торы DDL и DML в отдельных скриптах. Обработка скриптов позволяет "изменять" скрипты, связывая один файл скрипта с другим с помощью оператора isql input <спе- цифика ция_ файла>. Операторы скрипта выполняются в строгом порядке. Использование команды зет autoddl позволяет управлять подтверждением операторов или блоков операторов. Эта команда также позволяет откладывать подтверждение содержимого скрипта, по- ка не будет выполнен весь скрипт. Зачем использовать скрипты? Очень хорошей практикой является использование скриптов DDL для создания ва- шей базы данных и ее объектов. Перечислим несколько причин для этого. ♦ Самодокументирование. Скрипт является текстовым файлом, просто обрабаты- ваемым любым текстовым редактором. Скрипты могут (и должны) включать под-
Глава 14. Чертежная доска для базы данных 259 робные тексты комментариев. Изменения метаданных могут быть отмечены с указанием даты вручную. ♦ Управление разработкой базы данных. Представление всех описаний базы дан- ных в скриптах позволяет создание схемы тесно интегрировать с циклами проек- тирования задач и пересмотра кода. ♦ Повторяемое и отслеживаемое создание метаданных. Полностью восстанавли- ваемая схема является требованием гарантированного восстановления системы после сбоев во многих организациях. ♦ Аккуратное конструирование и реконструирование метаданных базы данных. Опытные программисты Firebird часто создают набор скриптов DDL, разработан- ных для выполнения и подтверждения в нужном порядке. Это упрощает отладку и гарантирует, что объекты будут существовать, когда позже зависимые объекты будут на них ссылаться. Что находится в скриптах DDL? Операторы SQL Скрипт DDL содержит один или более операторов SQL (create, alter, drop) для соз- дания, изменения или удаления базы данных или любого другого объекта. Он может включать операторы DML, хотя рекомендуется использовать операторы DDL и DML в разных скриптах. СОВЕТ. Довольно общей является практика включения (input) в цепочку скрип- тов DDL одного или более скриптов, содержащих операторы insert для ввода в некоторые таблицы статичных управляющих данных. Вы можете, например, включить операторы для добавления начальных строк в таблицу учетных запи- сей. Убедитесь, что все операторы DDL подтверждаются до появления других операторов DML. Операторы процедурного языка (PSQL), определенные для хранимых процедур и триггеров, также могут быть включены в скрипты. Блоки PSQL получают специаль- ную трактовку в скриптах в зависимости от символов терминатора операторов (см. далее разд. "Символы терминатора"). Комментарии Скрипт может также содержать комментарии в двух вариантах. Блок комментариев Блок комментариев в скриптах DDL использует соглашения языка С: /* Этот комментарий распространяется на множество строк в скрипте */
260 Часть IV. База данных и ее объекты Блок комментариев может появиться в той же строке, что и оператор SQL или команда isql, и может быть произвольной длины. Он начинается символами /* и заканчивается символами */. Линейные комментарии Комментарий стиля /*...*/ также может быть встроен внутрь оператора как линей- ный комментарий: CREATE TABLE USERS1 ( USER_NAME VARCHAR( 128 ) /* security user name */ , GROUP-NAME VARCHAR: 128 ) /* not used on Windows */ , PASSWD VARCHAR: 32 ) /* will be stored encrypted */ , FIRST_NAME varchar: 96 ) /* Defaulted */ , MIDDLE_NAME VARCHAR( 96 ) /* Defaulted */ , LAST_NAME varchar: 96 ) /* Defaulted */ , FULL_NAME VARCHAR( 290 ) /* Computed */ ) ; Однострочные комментарии В скриптах Firebird вы можете использовать альтернативное соглашение по коммен- тированию одной строки — двойной минус: — комментарий В релизе 1.0.x этот стиль комментария не может быть использован для линейного комментария или для "закомментирования" части строки. В релизе 1.5 и выше соглашение по комментированию — может быть использовано в любом месте строки для "комментирования" всего, начиная с маркера до конца те- кущей строки, например: CREATE TABLE MET_REPORT (ID BIGINT NOT NULL, — VARCHAR(40), невидим WEATHER_CONDITIONS BLOB SUB_TYPE TEXT, LAST_REPORT TIMESTAMP); Операторы isql Команды isql set autoddl, set sql dialect, set term и input являются допустимыми операторами в скриптах Firebird — подробности об этих командах см. в главе 37. Символы терминатора Все операторы, которые выполняются в скрипте, должны оканчиваться символом терминатора. Символ по умолчанию — точка с запятой (,•). Символ терминатора по умолчанию может быть изменен для всех операторов за ис- ключением операторов языка процедур (PSQL) при использовании в скрипте коман- ды SET TERM2. 2 Команда set term не является SQL-оператором, понимаемым Firebird. Это команда ути- литы 1SQL, позволяющая задать индикатор окончания оператора DDL. set term используется только там, где инструмент разработчика может выполнить несколько команд DDL и DML в одном скрипте. — Прим. науч. ред.
Глава 14. Чертежная доска для базы данных 261 Терминаторы и язык процедур (PSQL) PSQL не допускает никаких терминаторов за исключением точки с запятой (;). Такое ограничение необходимо, потому что create procedure, recreate procedure, alter procedure, create trigger и alter trigger вместе с их операторами PSQL являются сложными операторами с их собственными правилами. Компилятору нужно видеть точку с запятой для различения каждого оператора PSQL. Следовательно, в скриптах необходимо переопределять используемый в командах скрипта терминатор перед началом операторов PSQL для хранимых процедур и триг- геров. После последнего оператора end, когда завершается исходный текст процеду- ры, следует восстановить значение терминатора по умолчанию, используя другой оператор set term. Пример: CREATE GENERATOR GEN_MY_GEN ; SET TERM ЛЛ; CREATE TRIGGER BI__TABLEA_O FOR TABLEA ACTIVE BEFORE INSERT POSITION 0 AS BEGIN IF (NEW.PK IS NOT NULL) THEN NEW.PK = GEN_ID(GEN_MY_GEN, 1) ; END лл SET TERM ;лл Любая строка может быть использована в качестве альтернативного терминатора, например: SET TERM CREATE PROCEDURE... AS BEGIN END @1# SET TERM ;@141 /“/ COMMIT; /**/ SET TERM +; CREATE PROCEDURE... AS BEGIN END +
262 Часть IV. База данных и ее объекты SET TERM ;+ /**/ COMMIT; Оператор SQL без сообщений не выполняется, если его текст следует за символом терминатора в той же строке. Пробелы и комментарии могут следовать за терминато- ром, но не другие операторы. Например, в следующей последовательности оператор commit не будет выполняться: ALTER TABLE ATABLE ADD F2 INTEGER; COMMIT; в то время как последовательность далее правильная: ALTER TABLE ATABLE ADD F2 INTEGER; /* счетчик бобов */ COMMIT; Основные шаги Основные шаги по использованию файлов скриптов описаны в следующих разделах. Шаг 1: создание файла скрипта Используйте любой подходящий текстовый редактор. На стадии обучения вы можете за каждым оператором DDL записывать оператор commit, чтобы обеспечить види- мость объекта последующим операторам. Когда вы получите больше опыта, вы на- учитесь подтверждать операторы в блоках, применяя set autoddl on и set autoddl off с целью управления взаимозависимостями и при тестировании/отладке скриптов. ВНИМАНИЕ! Убедитесь, что каждый скрипт заканчивается символом перевода строки и, по меньшей мере, одной пустой строкой. Шаг 2: выполнение скрипта Используйте команду input в сессии isql, или кнопку Выполнить (Execute), или ана- логичную в вашем инструменте управления базой данных. isql в POS1X: SQL> INPUT /data/scripts/myscript.sql; isql в Win32: SQL> INPUT d:\data\scripts\myscript.sql; Шаг 3: просмотр результата и подтверждение изменений базы данных Разные инструменты Firebird и версии isql возвращают различную информацию в случае ошибочного выполнения скрипта при наличии неверной команды. Средства,
Глава 14. Чертежная доска для базы данных 263 добавленные после Firebird 1.0, обеспечивают более подробные сообщения об ошиб- ках скрипта, чем предыдущие версии. Как создавать скрипты Вы можете создавать скрипты DDL различными способами, включая следующие: ♦ в сессии интерактивной isql с использованием команды output для передачи серий операторов DDL в файл; ♦ в текстовом редакторе ASCII, который выполняет переводы строки в соответст- вии с правилами командной строки операционной системы, где будет выполнять- ся скрипт DDL; ♦ используя специализированные инструменты редактора скриптов, которые дос- тупны среди инструментов администратора для Firebird сторонних разработчиков. См. список в приложении 5; ♦ используя инструмент case, который может выводить скрипты DDL в соответст- вии с соглашениями Firebird (InterBase). Вы можете использовать любой текстовый редактор для создания файла скрипта SQL, если выходной формат файла является полным текстом (ASCII) и символы за- вершения строки соответствуют правилам командной строки вашей операционной системы: ♦ в Windows терминатор строки — символ возврата каретки плюс символ перевода строки (ASCII 13, за которым следует ASCII 10); ♦ в Linux/UNIX терминатор строки — символ перевода строки, или "новая строка" (ASCII 10); ♦ в Mac OS X терминатор строки— новая строка (ASCII 10), а в родных Macintosh это возврат каретки (ASCII 13). СОВЕТ. См. также в isql возможность извлечения метаданных, которая может быть полезной для извлечения схемы БД в формате скрипта. Некоторые инструменты редактирования имеют возможность сохранять данные в различных текстовых форматах. Это может быть полезным, например, при создании скриптов, совместимых с Linux, на машине Windows. При этом убедитесь, что вы используете редактор, который сохраняет только полный текст ASCII. Подготовленный файл скрипта схемы должен начинаться с оператора create database или, если база данных уже существует, с оператора connect (включая имя пользовате- ля и пароль в апострофах). Эти операторы задают базу данных, с которой оперирует файл скрипта. За ключевыми словами create или connect должна следовать полная спецификация файла базы данных в апострофах: полный абсолютный путь и имя файла базы данных.
264 Часть IV. База данных и ее объекты ПРИМЕЧАНИЕ. Не используйте алиасы в скриптах, которые создают базы данных. Пример: SET SQL DIALECT 3 ; CREATE DATABASE 'd:\databases\MyDatabase.fdb' PAGE_SIZE 8192 DEFAULT CHARACTER SET ISO8859_1 USER 'SYSDBA' PASSWORD 'masterkey'; ИЛИ CONNECT 'd:\databases\MyDatabase.gdb' USER 'SYSDBA' PASSWORD 'masterkey'; Подтверждение операторов в скрипте Операторы DDL Операторы в скриптах DDL могут подтверждаться одним или несколькими спосо- бами: ♦ включением в соответствующих местах скрипта операторов commit, чтобы гаран- тировать доступность новых объектов базы данных всем последующим завися- щим от них операторам; ♦ включением в начало скрипта следующего оператора: SET AUTODDL ON; Для отмены автоматического подтверждения операторов DDL в скрипте isql ис- пользуйте: SET AUTODDL OFF; Ключевые слова on и off необязательны. Сокращение set auto может быть ис- пользовано в качестве двухстороннего переключателя. Для большей ясности рекомендуется использовать set autoddl с явным указанием ключевых слов on И OFF. Автоматическое подтверждение isql Если вы выполняете свой скрипт в isql, то изменения базы данных операторами оп- ределения данных (DDL)— например, операторами create и alter— автоматически подтверждаются по умолчанию. Это означает, что другие пользователи базы данных видят изменения сразу после выполнения оператора DDL. Некоторые инструменты обработки скриптов намеренно отключают такое поведение автоматического подтверждения, потому что оно может усложнить отладку. Убеди- тесь, что вы понимаете поведение того инструмента сторонних разработчиков, кото- рый вы используете для обработки скриптов.
Глава 14. Чертежная доска для базы данных 265 Операторы DML Изменения базы данных, выполненные операторами манипулирования данными (DML) — insert, update и delete, — не станут постоянными, пока не будут подтвер- ждены. Явно включите операторы commit в ваш скрипт для подтверждения изменений DML. Для отмены всех изменений базы данных после последнего commit используйте rollback. Подтвержденные изменения не могут быть отменены. Выполнение скриптов Скрипты DDL могут быть выполнены в сессии интерактивного isql с использованием команды input, как было описано ранее. Многие инструменты сторонних разработ- чиков позволяют выполнять и даже интеллектуально отлаживать скрипты в среде графического интерфейса. Управление скриптами вашей схемы Хранение хорошо организованных множеств скриптов, которые точно отражают са- мое последнее состояние ваших метаданных, является полезной практикой, которая прекрасно удовлетворяет большинству надежных высококачественных систем. Очень рекомендуется использовать в скриптах подробные комментарии при архиви- ровании всех версий скриптов в версии управляющей системы. Восстановление после сбоев Наиболее очевидная цель такой практики — "возврат к последней точке" при восста- новлении после сбоев. Если беда идет за бедой — база данных разрушена, а резерв- ные копии потеряны — метаданные можно восстановить из скриптов. Сохранившие- ся данные из другой невосстанавливаемой базы данных могут быть восстановлены специалистами и помещены в базу данных. Управление разработкой Скорее всего, несколько разработчиков будут создавать базу данных в течение ее жизненного цикла. Известно, что разработчики ненавидят написание системной до- кументации! Хранение аннотированных записей скриптов о каждом изменении базы данных— включая те, которые использовались интерактивно через isql или инстру- мент сторонних разработчиков, — это безболезненное и безопасное решение, кото- рое работает во всех случаях. Извлечение метаданных Некоторые инструменты администратора для Firebird, включая isql, могут выделять метаданные из базы данных и сохранять их в виде файла скрипта. Об использовании isql см. разд. "Извлечение метаданных" главы 37. Так как извлечение метаданных является удобным помощником в вашем использовании скриптов, существует мно-
266 Часть IV. База данных и ее объекты жество причин трактовать эти инструменты как "ассистенты" и взять себе за правило вручную поддерживать ваши главные скрипты схемы. ♦ Firebird не хранит комментарии при сохранении определений метаданных. Мно- гие системные таблицы имеют столбец blob, обычно называемый rdb$description, в котором может сохраняться в виде единого целого часть предоставленного пользователем описания. При извлечении метаданных инструментом isql этот столбец не выводится, хотя некоторые инструменты сторонних разработчиков его поддерживают. ♦ Все инструменты извлечения метаданных генерируют только текущие метадан- ные. Не существует истории изменений — даты, причины или авторы изменений. ♦ Некоторые инструменты, включая isql, генерируют метаданные в неверной по- следовательности с точки зрения зависимостей, делая скрипты невозможными в использовании для перегенерации базы данных без их корректировки. Подобная задача может быть утомительной или даже невозможной в зависимости от того, насколько выполняющий ее человек хорошо знает метаданные. ♦ Даже умеренно увеличивающаяся в размерах база данных может иметь огромное количество объектов, особенно когда проектирование системы приводит к интен- сивному использованию модулей встроенного кода. Слишком большие скрипты часто завершаются с ошибками по причине различных ограничений в выполнении или в ресурсах. Большие, плохо организованные скрипты также сбивают с толку и раздражают при использовании их в качестве документации. Создание скриптов вручную Автор жестко пропагандирует поддержку полностью аннотированных скриптов схе- мы вручную и разделение их на несколько отдельных файлов. Пример набора скрип- тов в табл. 14.2 описывает и регенерирует базу данных с именем leisurestore.fdb. Таблица 14.2. Пример набора скриптов для схемы Файл Содержимое leisurestore_01 .sql Оператор create database; определения create domain, create GENERATOR И CREATE EXCEPTION -leisurestore_02.sql Все операторы create table, включая ограничения unique; операторы alter table добавляют все первичные ключи в виде именованных ограничений primary key leisurestore_03.sql Операторы alter table, добавляющие ограничения foreign key leisurestore_04.sql Операторы create index leisurestore_05.sql Операторы create trigger leisurestore_06.sql Операторы create procedure leisurestore_07.sql Скрипт операторов DML, который добавляет строки в статичные (управляющие) таблицы
Глава 14. Чертежная доска для базы данных 267 Таблица 14.2 (окончание) Файл Содержимое leisurestore_08.sql Операторы grant (скрипт безопасности) leisurestore_09.sql Более поздние изменения в корректной последовательности зависимо- стей !eisurestore_10.sql Скрипты QA (тестовые данные) Цепочки скриптов Устойчивый набор скриптов может быть соединен вместе в цепочку с использовани- ем оператора isql input в качестве последнего оператора в предыдущем скрипте. На- пример, для присоединения скрипта leisurestore_0,sql к leisurestore 01 .sql завершите скрипт следующим образом: COMMIT; — присоединение к операторам CREATE TABLE INPUT 'd:\scripts\leisurestore_02.sql' ; — не забудьте добавить пустую строку! Подход, основанный на главном скрипте Оператор input не обязательно должен быть последним в скрипте. Поэтому другим полезным подходом к поддержке набора скриптов является наличие "главного" скрипта, который вводит каждый дополнительный скрипт в нужном порядке. Это создает преимущества в поддержке больших наборов скриптов. Вы можете включить комментарии для сообщения о содержании каждого входного скрипта. Пора дальше А теперь— за создание баз данных! В следующей главе вы не только научитесь соз- давать базы данных, но также и сохранять их чистыми, безопасными и с хорошей производительностью. Существует не так много способов разрушения баз данных Firebird, однако раздел в конце этой главы описывает пять способов сделать это. Предупрежден, значит вооружен!
ГЛАВА 1 5 Создание и ведение базы данных База данных Firebird — это, прежде всего, файл файловой системы, находящийся под управлением подсистемы ввода/вывода главной машины, на которой выполняется сервер Firebird. Как только сервер создаст этот файл, его система управления начина- ет управлять его пространством, используя протокол низкого уровня для связи с под- системой ввода/вывода. По причине использования этого протокола база данных Firebird должна существо- вать на той же физической машине, что и сервер Firebird. Она не может размещаться ни на каком устройстве хранения данных, которое не находится под прямым управ- лением физической системы ввода/вывода машины сервера. Новая, "пустая" база данных занимает на диске около 540—600 Кбайт. Файл базы данных вовсе не является пустым, поскольку "акт создания" — оператор create database— приводит к созданию более 30 системных таблиц. Эти таблицы будут хранить каждую деталь метаданных, как только объект базы данных будет добавлен или изменен. Так как системные таблицы являются обычными объектами базы дан- ных Firebird, они уже содержат для себя записи метаданных. Сервер уже выделил страницы базы данных на диске для этих данных и создал инвентарные страницы для различных типов объектов. Обсуждение страниц базы данных см. в предыдущей главе. Физическое хранение базы данных Размещение До создания базы данных вы должны знать, где собираетесь ее создавать. Это не столь глупо, как звучит. Оператор create database (альтернатива— create schema) будет создавать файл или файлы с указанными вами именами, однако он не может создать каталоги и не может изменить полномочия доступа файловой системы. Этим деталям следует уделить внимание в первую очередь. Дополнительно сервер Firebird 1.5 может быть сконфигурирован для ограничения размещения баз данных. Проверьте параметр DatabaseAccess в файле firebird.conf (см. главу 3), чтобы выяснить, где ваш сервер ограничен в доступе. Если у вас установки по умолчанию (Full), то вы можете создавать базу данных в любом месте. Иначе: ♦ установка Restrict указывает файловой системе иерархию, в которой разрешен доступ к базе данных. Убедитесь, что пользователь, запускающий ваш сервер,
Глава 15. Создание и ведениебазы данных 269 имеет достаточные полномочия для создания там файла (или, в случае встроенно- го сервера Windows, подключающийся пользователь); ♦ установка None позволяет серверу соединяться только с базами данных, находя- щимися в списке в aliases.conf. Вы можете создавать базу данных в любом месте, однако, за исключением создания, никакой клиент не будет иметь возможности соединиться с ней, если алиас БД и ее абсолютный адрес не будут присутствовать в aliases.conf. ВНИМАНИЕ! Настоятельно рекомендуется устанавливать режим DatabaseAccess в none и использовать средства алиасов базы данных. Более подробную инфор- мацию об алиасах базы данных см. в разд. "Алиасы базы данных" главы 4. Безопасность доступа Для новичков не всегда бывает очевидным, что существует разница между доступом к серверу и безопасностью базы данных. Когда вы соединяетесь с базой данных Firebird, используя isql или ваш любимый инструмент администратора, вы всегда указываете имя пользователя и пароль вместе с сервером, портом (иногда) и путем к базе данных. Когда вы это делаете, вы соединяетесь с сервером и открываете со- единение с базой данных. Если база данных еще не существует, и вы запустили из командной строки isql без параметров, то произойдут две вещи: ♦ вы соединяетесь с сервером; ♦ пока вы не отправите на сервер запрос connect или create database, программа не будет соединена с базой данных. Пароль доступа всегда требуется для соединения с сервером. После этого вы можете соединяться с любой базой данных. Что вы можете делать, соединившись с базой данных, зависит от привилегий SQL, которые хранятся в базе данных. Пользователь SYSDBA имеет полные разрушительные права к любой базе данных и к любому ее объекту. Владелец (пользователь, создавший базу данных) имеет автоматические права к базе данных, но не к объектам, которые были созданы другими пользовате- лями. Хотя любой пользователь, соединившись с сервером, может соединяться с лю- бой базой данных, у него не будет прав делать что угодно с чем угодно, отличных от прав, которые были предоставлены ему явно или неявно владельцем базы данных операторами grant. Вопросы доступа к серверу и безопасности базы данных подробно обсуждаются в части VIII. ISC_USER и ISC_PASSWORD На сервере можно установить две переменные окружения: isc_user и isc_password, чтобы избежать необходимости явно записывать пароли в скриптах. Вы можете де-
270 Часть IV. База данных и ее объекты лать все, что разрешено указанному пользователю. Эта возможность удобна для административных задач, однако она должна быть использована с большой осторож- ностью, потому что оставляет доступ к вашей базе данных открытым для любого локального пользователя, кто случайно натолкнется на ваше окно командной строки. Если вы хотите играть с огнем, сделайте эти две переменные постоянными. Если же вы хотите иметь высокий уровень удобства и защищенности скриптов, временно устанавливайте их каждый раз при работе и не забывайте отменять установку при завершении работы с окном командной строки. В Linux в том же окне командной строки, из которого вы запускали приложение, вве- дите: ]# setenv ISC_USER=SYSDBA ]# setenv ISC_PASSWORD=masterkey Для отмены установок используйте следующее: ]# setenv ISCJJSER= И setenv ISC_PASSWORD= или просто закройте окно. В Windows в командной строке введите: set ISCJ3SER=SYSDBA set ISC_PASSWORD=masterkey Для отмены введите: set ISC_USER= set ISC_PASSWORD= Создание базы данных Вы можете создавать базу данных интерактивно в isql. Некоторые другие инструмен- ты администрирования баз данных могут отвечать требованиям API, перечисленным в этом разделе, и позволяют создавать базу данных интерактивно, в то время как дру- гие требуют использования скрипта. В любом случае более предпочтительным является использование файла определе- ния данных (скрипта DDL), потому что он предоставляет простой способ "проиграть снова" ваши операторы при ошибочном выполнении скрипта. Проще запустить ис- ходный файл, чем заново вводить интерактивные операторы SQL. Диалект По умолчанию Firebird создает базу данных диалекта 3. Если вы хотите создать базу данных диалекта 1, первым оператором в вашем скрипте (или первым действием в вашем инструменте администратора) должен быть: SET SQL DIALECT 1;
Глава 15. Создание и ведение базы данных 271 /,\ ВНИМАНИЕ! Если isql в настоящий момент соединен с базой данных, он пред- / • \ ложит вам подтвердить текущую транзакцию. Ответьте Yes (Да) для продолже- ния создания новой базы данных. Некоторые инструменты сторонних разработ- чиков вначале могут потребовать, чтобы вы отсоединились от базы данных. Следующий оператор — или первый для базы данных диалекта 3 — должен быть оператором create database или create schema такого синтаксиса1: CREATE {DATABASE I SCHEMA} 'спецификация-файла ' [USER 'имя-пользователя' [PASSWORD 'пароль']] [PAGERSIZE [=] целое] [LENGTH [=] целое [PAGE[S]]] [DEFAULT CHARACTER SET набор-символов] [<вторичный-файл>] ; <информация-о-файле> = LENGTH [=] целое [PAGE[S]] | STARTING [AT [PAGE]] целое [<информация-о-файле>] <вторичный-файл> = FILE ’спецификация-файла’ 1<информация-о-файле>] ]<вторичный-файл>] СОВЕТ. Используйте одиночные кавычки (апострофы) для таких строк, как име- на файлов, имена пользователей и пароли. DATABASE шиш SCHEMA? create database и create schema являются одним и тем же оператором. Это только вопрос ваших предпочтений. Обязательные и необязательные атрибуты Единственным обязательным атрибутом оператора create является спецификация файла— имя первичного файла базы данных и путь в файловой системе к его раз- мещению. Имя и путь к базе данных Спецификация файла должна быть полным абсолютным путем к файлу. Путь должен иметь правильный формат в операционной системе. Для POSIX: CREATE DATABASE 1/opt/databases/mydatabase.fdb’ 1 Замечание для программистов API. В динамическом SQL (DSQL) оператор create database или create schema должен быть выполнен с execute immediate. Дескриптор базы данных и имя транзакции, если присутствуют, должны быть инициализированы нулем до ис- пользования.
272 Часть IV. База данных и ее объекты Для Win32: CREATE SCHEMA 'd:\databases\mydatabase.fdb' Вы можете использовать наклонную черту (/) или обратную наклонную черту (\) в качестве разделителя каталогов. Firebird автоматически преобразует любой тип в тип, соответствующий серверу операционной системы. Апострофы в спецификации файла являются обязательными. Все элементы специфи- кации файла чувствительны к регистру для платформ POSIX. Создание удаленной базы данных При создании базы данных с клиентской рабочей станции или локально в Суперсер- вере для Linux — интерактивно или с использованием скрипта — вы должны вклю- чить имя хоста. Для POSIX: CREATE DATABASE 'myserver:/opt/databases/mydatabase.fdb' Для локального Linux Суперсервера— как и в предыдущем случае или так: CREATE DATABASE 'localhost:/opt/databases/mydatabase.fdb' Для Win32: CREATE SCHEMA 'NTServer:d:\databases\mydatabase.fdb' Владение базой данных Если вы подключены как пользователь SYSDBA, то SYSDBA будет владеть новой базой данных, независимо от того, как были заданы предложения user и password. Хотя указание владельца не является обязательным, очень желательно это сделать. При этом по причине безопасности вы, вероятно, захотите удалить из скрипта пароль пользователя перед архивацией скрипта с остальной системной документацией. CREATE DATABASE '/opt/databases/mydatabase.fdb' USER 'ADMINUSR' PASSWORD 'yyuryyub'; Размер страницы Необязательный атрибут page_size (размер страницы) задается в байтах. Если вы его опустите, будет принято значение по умолчанию: 4096 байт в isql. Некоторые другие инструменты применяют свое значение по умолчанию, значит, есть веский аргумент указывать это значение в скрипте явно. Размер страницы может быть 1024, 2048, 4096, 8192 или 16 384. Любые другие значения будут преобразованы в ближайшее меньшее число из этого списка. Например, если вы укажете 3072, Firebird создаст базу данных с размером страницы 2048. CREATE DATABASE '/opt/databases/mydatabase.fdb' USER 'ADMINUSR' PASSWORD 'yyuryyub' PAGE SIZE 8192
Глава 15. Создание и ведение базы данных 273 Факторы, влияющие на выбор размера страницы Выбор размера страницы не является вопросом применения некоторого "правила". Будет неплохо начать с размера по умолчанию — 4 Кбайт. Когда придет время на- страивать базу данных для улучшения производительности, вы сможете поэкспери- ментировать, создавая резервную копию базы данных и восстанавливая ее с другим размером страницы. Подробности см. в главе 38. Выбранный вами размер страницы может улучшить производительность или плохо на нее воздействовать в зависимости от множества факторов, главным образом, от структуры и порядка использования таблиц, к которым чаще всего осуществляется доступ. Каждая страница базы данных будет заполняться приблизительно на 80 процентов, следовательно, рассуждайте в терминах реального размера страниц, который должен составлять около 125 процентов от предполагаемого минимума. Размер строки наиболее часто используемых таблиц также может оказывать влия- ние. Структура записи, которая слишком велика, чтобы разместиться на одной стра- нице, требует обращения более чем к одной странице при ее чтении или записи, сле- довательно, доступ может быть оптимизирован при выборе размера страницы, кото- рый позволит разместить полностью одну строку или кратное число строк объемных таблиц. Количество строк, которое можно предположить у ваших таблиц через некоторое время, может оказывать влияние. Если множество строк может разместиться на од- ной странице, больший размер страницы может уменьшить обшее количество стра- ниц данных и индексов, которые нужно прочесть в одной операции. Набор символов по умолчанию Строго рекомендуется, чтобы все — или почти все — ваши текстовые данные были в кодировке U.S. ANSII2. CREATE DATABASE '/opt/databases/mydatabase.fdb' USER 'ADMINUSR' PASSWORD 'yyuryyub' PAGE_SIZE 8192 DEFAULT CHARACTER SET ISO8859_1; Подробнее о наборах символов см. в главе 11. Доступные наборы символов пред- ставлены в приложении 8. Получение информации о базе данных После того как вы создали базу данных и подтвердили создание (commit), вы можете в isql отобразить ее детали, используя команду show database: SQL> SHOW DATABASE; Database: /opt/databases/mydatabase.fdb Owner: ADMINUSR 2 Для "русскоязычных программ" следует использовать набор символов W1N1251.— Прим, перев.
274 Часть IV. База данных и ее объекты PAGE-SIZE 8192 Number of DB pages allocated = 176 Sweep interval = 20000 Forced Writes are ON Transaction - oldest = 5 Transaction - oldest active = 6 Transaction - oldest snapshot = 6 Transaction - Next = 9 Default character set: ISO8859_1 SQL> Интервал очистки и транзакции Информацию о чистке базы данных и интервале очистки см. в разд. "Гигиена базы данных" далее в этой главе. Значения старейшей ("старейшей заинтересованной"), старейшей активной и сле- дующей транзакций являются важными для производительности и поведения серве- ра. Подробности см. в части VI. Принудительная запись Принудительная запись (forced writes) является синонимом синхронной записи. На платформах, которые поддерживают асинхронную запись, базы данных Firebird соз- даются по умолчанию с принудительной записью. Фраза "отключение принудитель- ной записи" означает переключение поведения при записи с синхронного на асин- хронное. ♦ При включенной принудительной записи новые записи, новые версии записей и удаления физически записываются на диск сразу после завершения операции или, самое позднее, после подтверждения транзакции. ♦ Асинхронная запись приводит к тому, что новые или измененные данные хранят- ся в кэше файловой системы. Когда эти данные будут записаны на диск, зависит от поведения операционной системы. 5 ПРИМЕЧАНИЕ. Платформа Windows 95 не поддерживает асинхронную запись. Обсуждение отключения принудительной записи и инструкции по ее установке при использовании gfix см. в главе 39. Базы данных из одного и нескольких файлов Любая база данных Firebird может состоять из нескольких файлов. Вначале вам не нужно принимать решения о количестве файлов. База данных из одного файла может
Глава 15. Создание и ведение базы данных 275 быть преобразована в многофайловую в любое время при использовании alter database (обсуждается в этой главе) или инструмента gbak (см. главу 38). Задание размера файла для однофайловой базы данных Вы можете указать размер первичного файла в страницах следом за атрибутом page_size. Например, следующий оператор создает базу данных в файле размером 10 000 страниц: CREATE DATABASE '/opt/databases/mydatabase.fdb1 USER 'ADMINUSR' PASSWORD 'yyuryyub' PAGE_SIZE 8192 LENGTH 10000 PAGES /* ключевое слово PAGES необязательно */ DEFAULT CHARACTER SET WIN1251; Если база данных растет до размера большего, чем указанный размер файла, то Firebird расширяет первичный файл за пределы length, пока не будет достигнут пре- дел, существующий для файловой системы, или не будет исчерпано дисковое про- странство. Чтобы не допустить этого, вы можете хранить базу данных более чем в одном файле; дополнительные файлы называются вторичными файлами. Файлы мо- гут размещаться на нескольких дисках. Создание многофайловой базы данных Многофайловые базы данных являются результатом старых файловых систем, где абсолютный предел размера файла 2 Гбайта (FAT32, ext2) или 4 Гбайта (система NTFS с 32-битовым вводом/выводом). Они используются для решения общей про- блемы пользователей при разрушении базы данных InterBase в случае превышения лимита, когда сервер начинает перезаписывать данные файла с самого начала. Такая же проблема появляется, когда база данных исчерпает размер вторичного файла. Firebird просто отказывается осуществлять запись, когда последний файл достигнет лимита. Следовательно, разрушение существующих данных здесь предотвращено, хотя последующие записи данных будут потеряны. В следующем примере создается база данных, состоящая из трех файлов, каждый размером потенциально 2 Гбайта. Если файловая система поддерживает больший размер файлов, то последний файл будет продолжать расти, пока не достигнет этого предела. CREATE DATABASE 'LOCALHOST:/data/sample.fdb' PAGE_SIZE 8192 DEFAULT CHARACTER SET WIN1251 LENGTH 250000 PAGES FILE '/data/sample.fdl' FILE '/data/sample.fd21 STARTING AT 250001;
276 Часть IV. База данных и ее объекты Вы должны указать диапазон страниц для каждого файла либо задав количество страниц в каждом файле, либо указав начальный номер страницы для файла. Для последнего файла вам не нужно указывать размер, поскольку Firebird динамически устанавливает размер последнего файла и будет увеличивать его, пока не будет ис- черпано дисковое пространство или пока он не достигнет лимита файловой системы. В этом примере первый вторичный файл "начнет работать", когда первичный файл приблизится к размеру 2 Гбайта. "Следующий файл в цепочке" начинает применять- ся, когда запрашиваемой операции понадобится использовать больше страниц, чем могут предоставить предыдущие файлы без превышения заданных им лимитов3. Обязанностью администратора базы данных является отслеживание размеров базы данных и обеспечение того, чтобы у базы данных всегда существовала возможность необходимого расширения. Принятие решения, нужно ли и когда следует разделять файлы базы данных, зависит от того, какое ожидается увеличение размеров базы данных и как скоро. Большее количество файлов может быть добавлено в любое вре- мя при использовании оператора alter database (см. следующий раздел). При использовании многофайловых баз данных вы можете снять ограничение разме- ра базы данных одним дисковым устройством, если файловая система не обеспечива- ет размещение одного, очень большого файла на нескольких дисках. Не будет ника- ких проблем инсталлировать RAID-массив и распространять базу данных Firebird на нескольких дисках для любой поддерживаемой платформы. ПРИМЕЧАНИЕ. Все файлы должны размещаться на дисках, находящихся под прямым управлением главной машины сервера Firebird. Изменение базы данных Оператор alter database используется для добавления одного или более вторичных файлов к существующей базе данных. Он требует исключительного доступа к базе данных — см. разд. "Исключительный доступ"главы 39. База данных может изменяться ее создателем (владельцем), пользователем SYSDBA или — для Linux/UNIX — любым пользователем с привилегиями операционной сис- темы root. Синтаксис Синтаксис ALTER database: ALTER (DATABASE | SCHEMA} ADD <предложение-добавления>; 3 При создании или расширении многофайловой базы данных для всех файлов базы данных используйте либо только length (явную длину конкретного файла), либо только starting ат (указание начала нового файла), для избежания путаницы. — Прим. науч. ред.
Гпава 15. Создание и ведение базы данных 277 Спредложение-добавлений = FILE 1 спецификация-файла' <информация-о-файле> [<предложение-добавления>] <информация-о-файле> = {LENGTH [=] целое [PAGE[S]] | STARTING [AT [PAGE]] целое } [<информация-о-файле>] Первый пример добавляет два вторичных файла в базу данных, с которой в настоя- щий момент существует соединение, задавая начальные номера страниц: ALTER DATABASE ADD FILE 'mydatabase.fd2' STARTING AT PAGE 10001 ADD FILE 'mydatabase.fd3' STARTING AT PAGE 20001 ; Первичный и первый вторичный файл будет расти до 10 000 страниц. Если этого ока- зывается недостаточно для удовлетворения запросов к новым страницам, Firebird начнет сохранять новые страницы во втором вторичном файле. Следующий пример задает длину вторичного файла, а не начальный номер страницы: ALTER DATABASE ADD FILE 'mydatabase.fd2' LENGTH 10000 ADD FILE 'mydatabase.fd3' ; Результат несколько отличается от первого примера. В этом случае Firebird начнет использовать вторичный файл, когда первичный файл достигнет лимита файловой системы. Разница не оказывает влияния на производительность и на общий размер базы данных. Кэш базы данных Кэш базы данных— участок памяти, зарезервированной для базы данных, выпол- няющейся на сервере. Его назначение — хранение всех страниц базы данных (также называется буферами), которые были использованы последними. Он конфигурирует- ся по умолчанию для новых баз данных и для всех баз данных, которые не были ин- дивидуально сконфигурированы. Эта установка по умолчанию, которая задает коли- чество блоков памяти, или буферов страниц, каждый размером в страницу базы дан- ных, устанавливается в файле конфигурации сервера: ♦ для версии 1.5 и выше это параметр DefauitDbCachePages в файле firebird.config для всех платформ; ♦ для версии 1.0.x это параметр database_cache_pages в файле isc_config (POSIX) или в ibconfig (Win32). Следует подчеркнуть, что конфигурирование кэша не является обязательным. Кон- фигурация по умолчанию для Суперсервера соответствует большинству обычных потребностей, и реконфигурирование на уровне сервера может никогда не понадо- биться. Для Классического сервера значение по умолчанию больше заслуживает
278 Часть IV. База данных и ее объекты внимания, т. к. оно может оказаться слишком большим для системы с немалым коли- чеством одновременно работающих пользователей. Вновь создаваемая база данных имеет размер кэша на уровне базы данных 0 страниц. Если установка кэша остается нулевой, то соединение с этой базой данных будет ис- пользовать установку на уровне сервера. Следовательно, база данных с большим размером страницы будет использовать больший объем памяти кэша, чем база дан- ных с меньшим размером страницы. Размер кэша может быть установлен индивидуально и постоянно для базы данных. При необходимости он может быть изменен. Другие базы данных, для которых ос- тавляется нулевое значение (или устанавливается в ноль), будут использовать значе- ние по умолчанию сервера. Количество требуемых буферов кэша является приблизительным. Оно должно быть достаточно большим, чтобы удовлетворить требованиям баз данных к страницам, но не столь большим, чтобы занять память, необходимую другим операциям. До этого момента, чем больше работы может быть выполнено в кэше, тем лучше общая про- изводительность. Аксиома "серверы баз данных любят RAM" истинна и для Firebird. Однако Firebird использует RAM для других видов деятельности, которые являются, по меньшей мере, столь же важными, что и кэширование. Транзакции и индексы поддерживаются в RAM, а, начиная с версии 1.5, сортировка и слияние данных также выполняются в памяти, если она доступна. Важно понимать, что каждая система имеет критическую точку, где слишком боль- шой размер кэша будет потреблять больше памяти, чем может "предложить" система. За этой точкой увеличение кэша приведет к ухудшению производительности. Ограничения и значения по умолчанию Минимальный размер кэша— 50 страниц. Максимума не существует, пока выделяе- мый объем памяти не превышает доступный объем RAM. Величиной выделяемого кэша по умолчанию является: ♦ Суперсервер. Для каждой выполняющейся базы данных 2048 страниц. Все поль- зователи совместно используют этот пул кэша. Для оценки используемых ресурсов: одна выполняющаяся база данных с установ- ками по умолчанию для page_size (4 Кбайт) и DefaultDbCachePages (2 Кбайт) тре- бует 8 Мбайт памяти. Две базы данных, выполняющиеся с теми же установками, требуют 16 Мбайт и т. д. Используемый объем кэша по умолчанию вычисляется следующим образом: PAGE_SIZE * DefaultDbCachePages * количество баз данных ♦ Классический сервер. Для каждого клиентского соединения 75 страниц кэша. Каждое соединение выделяет свой собственный кэш. Объем требуемой памяти является суммой требований к кэшу всех клиентских соединений со всеми базами данных. Используемый объем кэша вычисляется следующим образом: PAGE_SIZE * DefaultDbCachePages * количество соединений
Глава 15. Создание и ведение базы данных 279 Вычисление размера кэша Когда Firebird читает страницу базы данных с диска, он сохраняет эту страницу в кэше. Обычно размер кэша по умолчанию является достаточным. Если ваше прило- жение использует соединения из пяти и более таблиц, Firebird Суперсервер может автоматически увеличить размер кэша, если текущего размера недостаточно. Если ваше приложение хорошо локализовано (т. е. постоянно использует одну и ту же не- большую часть базы данных), вы можете увеличить размер кэша так, что серверу никогда не понадобится удалять какую-либо страницу из кэша для помещения туда другой. Поскольку obcache сконфигурирован в страницах, очевидно, что база данных с боль- шим размером страницы потребляет больше памяти, чем база данных с меньшим размером страницы. Когда множество баз данных выполняется на одном и том же сервере, может оказаться желательным переопределить размер кэша на стороне серве- ра значением на уровне базы данных или в некоторых случаях на уровне приложения. Приложение, которое интенсивно выполняет индексированные выборки, требует больше буферов, чем приложение, преимущественно выполняющее добавление данных. Там, где много клиентов обращается к различным таблицам или к различным частям одной таблицы, потребность в памяти выше, чем в случае, когда большинство клиен- тов работает с одними и теми же или частично перекрывающимися наборами дан- ных. Может случиться, что слишком много буферов кэша будет выделено из имеющейся RAM. При наличии большого количества одновременно выполняющихся баз данных запрос может затребовать больше RAM, чем доступно в системе. Кэш будет перека- чиваться вперед и назад между RAM и диском, уничтожая преимущества кэширова- ния. Другие приложения (включая сервер) будут испытывать недостаток в памяти, если кэш будет слишком велик. Поэтому важно, чтобы в системе был установлен соответствующий объем RAM для обеспечения требований сервера баз данных к памяти. Если производительность ба- зы данных важна для ваших пользователей, то исключите создание конкуренции за использование ресурсов со стороны других выполняющихся на сервере приложений. Оценка требований к размеру Оценка размера кэша не является простой или точной наукой, особенно если у вас множество баз данных, выполняющихся одновременно. Использование сервером кэша определяется базой данных с наибольшим размером страниц. Классический сервер выделяет кэш для каждого соединения, в то время как Суперсервер объединя- ет кэш для всех соединений одной базы данных. Как отправная точка, это будет по- лезным для работы с числами и нужно для базы данных с наибольшим размером страницы. Реальные условия использования определят, где требуются корректи- ровки. Нет необходимости иметь кэш, вмещающий всю базу данных. Найдите коэффициент уменьшения для каждой базы данных, оценивая ту часть, к которой, скорее всего,
280 Часть IV. База данных и ее объекты будет доступ в процессе обычного использования. Совет по оценке простой — не существует "правила". Пусть, когда мы говорим о "DbCachePage", мы имеем в виду размер кэша, но не обязательно при условии установок сервера по умолчанию для новых и неконфигурированных баз данных. Коэффициент уменьшения г должен иметь значение между 0 и I. Размер базы данных в страницах может быть установлен следующим образом: ♦ для однофайловой базы данных возьмите максимально возможный в файловой системе размер файла минус 1 байт и разделите на размер страницы. Для опера- ционных систем, поддерживающих очень большие файлы, используйте фактиче- ский размер файла базы данных вместо максимального размера файла; ♦ для многофайловой базы данных возьмите значение starting ат первого вторич- ного файла и прибавьте значения length всех вторичных файлов. Пусть DbCachePage равен количеству страниц кэша, требуемых для этой базы данных. Для каждой базы данных вычислите: DbCachePages = г * размер Вычислите и запишите это число для каждой базы данных. /''ч СОВЕТ. Храните эти записи в другой документации по базе данных для исполь- МдХ зования, при необходимости, в настройке кэша индивидуальной базы данных. Вычисление потребностей в RAM Для вычисления требуемого объема RAM для кэша базы данных на вашем сервере возьмите значение page_size для каждой базы данных и умножьте на значение DefaultDbCachePages. Просуммированные результаты для всех баз данных дадут при- близительные требования к минимальному объему RAM для кэширования баз дан- ных. Установка размера кэша на уровне базы данных Существует несколько способов конфигурирования размера кэша для конкретной базы данных. Изменения не действуют до тех пор, пока не будет установлено новое соединение с Суперсервером Firebird или не будет соединен новый клиент с Класси- ческим сервером. Использование gfix Рекомендуемый способ установки значения на уровне базы данных для перекрытия значения DefaultDbCachePages — использование УТИЛИТЫ Командной Строки gflX СО следующими переключателями: gfix -buffers п имя-базы-данных
Глава 15. Создание и ведение базы данных 281 где п— требуемое количество страниц базы данных. Этот способ позволяет точно задать размер кэша базы данных, уменьшая риск "недоиспользования" памяти или работы со слишком малым размером кэша, когда на сервере используются несколько баз данных с разными требованиями к размеру кэша. Установленный таким способом размер кэша будет использован до тех пор, пока не будет задано новое значение. ЛГ ПРИМЕЧАНИЕ. Для запуска gfix вы должны быть пользователем SYSDBA или владельцем базы данных. Более подробную информацию об использовании gfix \ см. в главе 39. Использование инструмента запросов командной строки isql У вас есть две возможности для увеличения количества страниц кэша в процессе одной сессии утилиты командной строки isql. Первый вариант— включить количество страниц (п) как переключатель при запус- ке isql: isql -с п имя-базы-данных где п— количество используемых в сессии страниц кэша, которое временно пере- крывает любое значение, установленное В DefaultDbCachePages (database_cache_pages) или в gfix. Должно быть больше 9. В качестве альтернативы вы можете включить cache л в качестве аргумента операто- ра connect при выполняющейся isql: isql > connect имя-базы-данных CACHE л Значение л может быть любым положительным целым количеством страниц базы данных. Если кэш базы данных уже существует по причине другого соединения с базой данных, то размер кэша увеличивается, только если значение л больше текуще- го размера кэша. Использование буфера параметров базы данных В приложении размер кэша может быть установлен в буфере параметров базы дан- ных (Database Parameter Buffer, DPB) с использованием параметра isc_dpb_num_buffers или isc__dpb_set_page_buf fers в соответствии с требованиями вашего сервера. ♦ isc_dpb_num_buffers — устанавливает количество буферов (страниц кэш-памяти), которое будет использоваться для текущего соединения. Это имеет особый смысл в архитектуре Классического сервера, где каждое соединение получает статиче- ски выделенную кэш-память. Для Суперсервера настоящий параметр установит количество буферов, используемых указанной базой данных, если эта база данных еще не открыта, однако это значение теряется после того, как сервер закрывает базу данных.
282 Часть IV. База данных и ее объекты ♦ isc_dpb_set_page__buffers — используется как в Классическом сервере, так и в Су- персервере. Этот параметр имеет тот же эффект, что и использование gfix для по- стоянного перекрытия DefauitDbCachePages. /,\ ВНИМАНИЕ! Будьте осторожны, предоставляя приложению конечного пользо- / I \ вателя возможность модифицировать кэш. Хотя любой запрос на изменение размера кэша будет проигнорирован во всех запросах соединения, кроме перво- го, предоставление возможности изменения установок сервера пользователям, не являющимися техническими специалистами, может иметь непредсказуемые последствия для производительности и оказать воздействие на весь сервер. Изменение значений по умолчанию для сервера Установка на сервере для DefauitDbCachePages значения, большего, чем значение DbCachePages, является избыточным. Когда вы меняете установки по умолчанию на уровне сервера в файле конфигурации, они становятся значениями по умолчанию для каждой новой сконфигурированной с нулевым значением кэша базы данных на этом сервере. Для изменения установок откройте файл конфигурации в текстовом редакторе и най- дите нужный параметр: ♦ для версии 1.5 уберите комментарий у DefauitDbCachePages и измените число; ♦ для версии 1.0.x в файле конфигурации это defauit cache pages. Если необходимо, уберите комментарий В строке И создайте запись default_cache_pages=nnnn, где пппп — новый размер кэша. Для Суперсервера новое значение вступит в силу в следующий раз при первом со- единении с базами данных. Для Классического сервера оно будет действовать для всех соединений, выполненных после реконфигурации. Прагматичный подход Не переоценивайте важность кэша базы данных. Любой кэш устанавливает собст- венные накладные расходы на общие ресурсы памяти, а кэш файловой системы игра- ет свою роль в оптимизации выполнения системного ввода/вывода. Всегда есть точ- ка, где реальный выигрыш в общей производительности сервера не оправдывает стоимости использования ресурсов для худшего варианта запроса. Хороший совет: не бросайтесь оптимизировать размер кэша для Firebird по принципу "обязан сделать". В процессе разработки используйте установки по умолчанию, а при установке системы просто проверьте, подходит ли объем доступной памяти RAM значениям по умолчанию. Один раз используйте инструмент мониторинга для наблюдения и фиксации, сколько происходит чтений и записей из кэша для типичных и экстремальных ситуаций. Если статистика вас не устраивает, начинайте оптимизацию.
Гпава 15. Создание и ведение базы данных 283 При первом приближении к оптимизации вы можете увеличить значение по умол- чанию для кэша до размера, который займет приблизительно две трети доступной свободной памяти RAM. Если установлен недостаточный объем RAM, поставьте больше. После этого снова запустите мониторинг. Если описанная процедура не привела к улучшению дел, повторите упражнение. Проверка размера кэша Для проверки величины используемого кэша базы данных выполните следующие команды в isql: ISQL> CONNECT имя-базы-данных; ISQL> SET STATS ON; ISQL> COMMIT; Current memory = 415768 Delta memory = 2048 Max memory = 419840 Elapsed time = 0.03 sec Buffers = 2048 Reads = 0 Writes 2 Fetches = 2 ISQL> QUIT; После set stats on пустая команда commit указывает утилите isql на необходимость отображения информации об использовании памяти и буфера. Прочтите значение Buffers для определения текущего размера кэша в страницах. Базы данных только для чтения Базы данных по умолчанию создаются в режиме чтения/записи. Базы данных для чтения/записи не могут находиться на файловых системах только для чтения, даже если они используют только операторы select, потому что Firebird записывает ин- формацию о состоянии транзакций в структуру данных в файл базы данных. - База данных Firebird может поставляться как файл только для чтения, при размеще- нии каталогов, файлов и других, не относящихся к базе данных объектов, на ком- пакт-диски или другие файловые системы только для чтения. Конечно, к базам дан- ных только для чтения могут также обращаться системы чтения/записи. ПРИМЕЧАНИЕ. База данных только для чтения — это не то же самое, что файл базы данных, у которого установлен атрибут только для чтения. Копирование файла базы данных для чтения/записи на компакт-диск не сделают ее базой дан- ных только для чтения.
284 Часть IV. База данных и ее объекты Может потребоваться разработка приложения, которое не использует запросы, вклю- чающие запись в базу данных, или приложения, вызывающего исключение при по- пытках записи в базу данных. Следующие действия вызовут ошибку "Attempt to write to a read-only database" (Попытка записи в базу данных только для чтения): ♦ операции opdate, insert или delete; ♦ изменения метаданных; ♦ операции, которые пытаются увеличить генераторы. Внешние файлы Любые файлы, связанные с базой данных путем объявления CREATE TABLE имя-таблицы EXTERNAL FILE ' имя-файла' будут так же открываться, как файлы только для чтения, даже если для файла не ус- тановлен атрибут только для чтения. Преобразование базы данных в режим только для чтения Требуется исключительный доступ для переключения базы данных между режимами чтения/записи и только для чтения— см. разд. "Исключительный доступ" главы 39. Переключение режима может быть выполнено владельцем базы данных или пользо- вателем SYSDBA. Могут быть использованы утилиты gfix или gbak4: ♦ Используя gbak, выполните резервное копирование базы данных и восстановите ее в режиме только для чтения посредством опции -с[reate], например: gbak -create -mode read_only dbl.fbk dbl.fdb ♦ Используя gflX, выдайте команду-m[ode] read-only, например: gfix -mode read__only dbl. fdb СОВЕТ. Восстанавливайте базы данных только для чтения с полностраничным заполнением — используйте переключатель -use для указания "использовать полное пространство". В базе данных для чтения/записи страницы по умолчанию заполняются примерно на 80 процентов, поскольку это может помочь в оптими- зации повторного использования страниц. Резервирование пространства не име- ет смысла для базы данных только для чтения, а полностью заполненные стра- ницы являются более компактными и более быстрыми. 4 Сервис API предоставляет доступ к обоим методам для перевода базы данных в режим только для чтения. Чтобы получить информацию, изучите доступную документацию по раз- личным группам параметров действий сервиса (isc_action_svc_xxx), которые могут переда- ваться в функцию isc_service_start О. Множество компонентов доступа к базе данных реа- лизуют эти функции для использования в различных языковых средах.
Глава 15. Создание и ведение базы данных 285 СОВЕТ. Хотя сервер Firebird может управлять напрямую базами данных InterBase 5.x, эти базы данных не могут быть переведены в режим только для чтения. Вы можете обновить базу данных InterBase 5 до базы данных только для чтения Firebird, сделав транспортабельную резервную копию в gbak InterBase 5 и восстановив с использованием gbak Firebird, с переключателями -с[reate] и -mode read__only. Теневые копии базы данных Firebird имеет возможность немедленно восстанавливать базу данных в случае сбоя диска, сбоя сети, случайного удаления базы данных файловой системой5. Теневое копирование (shadowing, аналог зеркалирования) является внутренним процессом, который поддерживает физическую копию базы данных в реальном времени. Всякий раз, когда изменения записываются в базу данных, теневая копия одновременно по- лучает те же самые изменения. Активная теневая копия всегда отображает текущее состояние базы данных. При этом, хотя теневое копирование является очевидным преимуществом в качестве пре- пятствия против аппаратных сбоев, оно не является системой онлайновой реплика- ции. Преимущества и ограничения теневого копирования Основным преимуществом теневого копирования является то, что оно дает быстрый способ восстановления базы данных в случаях сбоев в аппаратном обеспечении. Ак- тивация теневой копии делает ее доступной немедленно. Теневое копирование вы- полняется незаметно для пользователей как дополнительный цикл в процессе записи данных с минимальным вниманием со стороны администратора базы данных. Создание теневой копии не требует исключительного доступа к базе данных. Теневая копия может находиться в одном или более файлах в контролируемой сервером сис- теме хранения. При этом теневое копирование не является защитой от разрушения данных. Данные, записанные в теневую копию, являются точной копией того, что записывается в базу данных со всеми изъянами и недостатками. Если пользователь ошибается, появляется ошибка диска или ошибки программного обеспечения приводят к разрушению дан- ных, то те же самые неверные данные будут помещаться в теневую копию. Теневое копирование является методом восстановления "все или ничего". Оно не обеспечивает восстановления отдельных фрагментов или возврата к конкретной вре- 5 К сожалению, теневое копирование не обеспечивает восстановление, если база данных была случайно удалена оператором drop database. Когда вы удаляете таким образом базу данных, ее теневая копия (shadow) также удаляется.
286 Часть IV. База данных и ее объекты менной точке. Оно может существовать только в той же файловой системе, что и сервер; теневая копия должна находиться на фиксированных дисках, расположенных на сервере. Она не может записываться на совместно используемые, не относящиеся к локальной файловой системе или удаленные устройства. С ПРИМЕЧАНИЕ. На системах, поддерживающих NFS (Networking File System), возможна поддержка теневых копий на файловой системе NFS. Причем это не рекомендуется, поскольку теневая копия станет изолированной — а следова- тельно, бесполезной — при потере соединения. Важные предостережения Теневое копирование не является заменой резервного копирования. Не успокаивайте себя верой в то, что теневое копирование является способом замены регулярного ре- зервного копирования и периодического восстановления базы данных. ♦ Файл теневой копии не менее уязвим от "бросков и стрел жестокой фортуны", чем любые другие файлы вашей файловой системы. ♦ Одна потеря или повреждение файла теневой копии делает все теневое копирова- ние бесполезным. ♦ Смерть диска или ненадежный модуль памяти способны принести огромный вред до того, как они полностью разрушат вашу базу данных. Каждый ошибочный фрагмент будет точно записан в теневую копию. В дополнение нужно сказать, что теневая копия не может принимать соединения. Никогда не пытайтесь соединяться с теневой копией или влиять на нее при использо- вании инструментов системы или базы данных. Сервер "знает", что он должен сде- лать с теневой копией для ее преобразования в активную базу данных. СОВЕТ. Не будет серьезных проблем, если теневая копия случайно повреждена или удалена. Пока вы знаете, что случайности могут произойти, теневая копия для здоровья базы данных может быть восстановлена в любое время просто удалением копии и новым ее созданием. Реализация теневого копирования В Firebird существует синтаксис DDL для создания и удаления теневых копий с раз- личными предложениями для задания размещения, режима работы и размера файла. Изменение теневой копии требует удаления существующей копии и создания новой с новыми спецификациями. Теневая копия, которая является одним дисковым файлом, называется файлом тене- вой копии. Теневая копия, состоящая из нескольких файлов, — которые могут распо-
Глава 15. Создание и ведение базы данных 287 лагаться более чем на одном диске, — называется набором теневых копий. Наборы теневых копий группируются в множество наборов теневых копий6. Размещение и распределение файлов теневой копии Теневая копия должна быть создана на жестком диске, отличном от диска размеще- ния файлов активной базы данных, поскольку одной из главных целей теневого ко- пирования является восстановление работоспособности при сбоях диска. Дисковое устройство должно быть физически подключено к машине, на которой вы- полняется сервер Firebird. Файлы в наборе теневых копий могут находиться на раз- ных дисках для улучшения ввода/вывода и выделения дискового пространства. Как и спецификации файлов базы данных, спецификации для теневых копий являются за- висимыми от платформы. Варианты теневой копии Режим {AUTO или MANUAL) Установка режима — автоматический (с или без атрибута условная, conditional) или ручной — определяет, что произойдет, если теневая копия станет недоступной. Режим auto устанавливается по умолчанию. Он позволяет базе данных продолжить работу в случае, когда теневая копия станет недоступной, или наоборот, теневая ко- пия будет целой, а диск с базой данных окажется поврежден. ♦ В момент, когда теневая копия станет недоступной, появится окно, чтобы проин- формировать об этом администратора базы данных. ♦ Если ставшая недоступной теневая копия была создана с атрибутом conditional, Firebird автоматически создает новую теневую копию, если это возможно. ♦ Если теневое копирование не является условным, то понадобится заново создать теневую копию вручную. Режим manual прекращает дальнейший доступ к базе данных в случае, когда теневая копия становится недоступной. Закройте ее, если продолжение теневого копирования является более важным, чем продолжение операций с базой данных. Для восстановления соединения администратор должен удалить старый файл теневой копии, удалить на нее ссылки и создать новую теневую копию. Условное теневое копирование Одной из причин, по которой теневая копия становится недоступной, является си- туация, когда она принимает на себя функции главной базы данных в случае "гибе- 6 Для автора оказалось невозможным найти кого-нибудь, кто мог бы объяснить преимуще- ства (если они существуют) ведения множества наборов теневых копий. Система нумерации могла быть результатом некоторой функциональности, которая никогда не была реализована. При отсутствии лучшей информации, похоже, имеет смысл остановиться на одном наборе
288 Часть IV. База данных и ее объекты ли" аппаратуры существующей базы данных — как-никак это основная идея теневого копирования7! Д ВНИМАНИЕ! Атрибут conditional должен также приводить к автоматическому созданию новой теневой копии, если существующая теневая копия становится операционной базой данных. Тем не менее практика показывает, что это проис- ходит не всегда. Проверьте, работает ли это, как ожидалось, и будьте готовы использовать запасной вариант. Однофайловые теневые копии в сравнении с многофайловыми По умолчанию теневая копия создается как "набор", содержащий один файл. Тем не менее набор теневой копии может включать в себя несколько файлов. Когда база данных— а следовательно, и ее теневая копия — увеличивается в размерах, теневая копия может быть переопределена и перегенерирована с большим количеством фай- лов, чтобы соответствовать требованиям увеличения пространства. Создание теневой копии Создание теневой копии не требует исключительного доступа; это также не влияет на пользователей, соединенных с базой данных. DDL-оператор create shadow создает теневую копию базы данных, с которой вы соединены в настоящий момент. Синтаксис: CREATE SHADOW номер-набора [AUTO | MANUAL] [CONDITIONAL] 'специфмкация-файла' [LENGTH [=] целое [PAGE[S]]] [<вторичный-файл>]; <вторичный-файл> = FILE 'спецификация-файпа ' [<информация-о-файле>] [<вторичный-файл>} <информация-о-файле> = {LENGTH [=] целое[PAGE[S]] | STARTING [AT [PAGE]] целое] [<информация-о-файле>] 7 conditional shadow на самом деле имеет смысл как дополнение к auto, и только. Если conditional создана как второй набор по отношению к auto, то она будет пустой до тех пор, пока с базой или с auto shadow не произойдет сбой. При сбое conditional shadow будет на- полнена страницами из целого файла и примет на себя функции альтернативной копии, таким образом даже в случае сбоя дисковой системы будет существовать две копии базы данных. И, разумеется, conditional shadow не имеет смысла как дополнение к manual shadow (как и комбинация из двух shadow— auto + manual), т. к. при manual в случае сбоя работа сервера с базой останавливается. — Прим. науч. ред.
Глава 15. Создание и ведение базы данных 289 СОВЕТ. Как и в операторе create database, спецификация файлов для теневой копии всегда является зависимой от платформы. Однофайловая теневая копия Предположим, у нас имеется сервер на Linux, соединенный с базой данных employee.gdb, которая размещена в каталоге примеров в корневом каталоге Firebird. Мы решили выполнять теневое копирование базы данных в разделе с именем /shadows. Для создания однофайловой теневой копии мы используем оператор CREATE SHADOW 1 '/shadows/employee.shd'; Номер набора теневой копии может быть любым целым. Размер страницы не вклю- чен, следовательно, атрибут page_size будет взят из спецификации самой базы данных. Используем команду isql show database, чтобы убедиться, что теневая копия сейчас существует: SQL> SHOW DATABASE; Database: /usr/local/firebird/examples/employee.gdb Shadow 1: '/shadows/employee.shd' auto PAGE_SIZE 4096 Number of DB pages allocated = 392 Sweep interval = 20000 Многофайловая теневая копия Синтаксис создания многофайловой теневой копии похож на синтаксис создания многофайловой базы данных: спецификация вторичного файла теневой копии "сцеп- ляется" со спецификацией первичного файла с указанием спецификаций и ограниче- ний размеров каждого файла. В следующем примере предположим, что мы соединены с базой данных employee.gdb, расположенной в каталоге по умолчанию Win32. Мы собираемся соз- давать теневую копию из трех файлов на дисках F, Н и J, которые являются раздела- ми жесткого диска файловой системы сервера. СОВЕТ. Размеры вторичных файлов теневой копии не обязательно должны со- ответствовать размерам вторичных файлов базы данных. Первичный файл (employeel.shd) имеет длину 10 000 страниц базы данных, а первый вторичный файл (employee2.shd) 20 000 страниц базы данных. Как и в случае с базой данных, последний вторичный файл теневой копии при необходимости будет увели- чиваться, пока не будет исчерпано дисковое пространство раздела J или пока не бу- дет достигнут предел размера для файловой системы. 10 Зак. 420
290 Часть IV. База данных и ее объекты CREATE SHADOW 25 'F:\shadows\employeel.shd' LENGTH 10000 FILE 'H:\shadows\employee2.shd' LENGTH 20000 FILE 'J:\shadows\employee3.shd'; Мы можем также указать начальные страницы вторичных файлов, вместо абсолют- ного размера первичного и не последних вторичных файлов: CREATE SHADOW 25 'F:\shadows\employeel.shd' FILE 'H:\shadows\employee2.shd' STARTING AT 10001 FILE 'J:\shadows\employee3.shd' STARTING AT 30001; Вы можете проверить в isql: SQL> SHOW DATABASE; Database: C:\Progra~l\firebird\examples\employee.gdb Owner: SYSDBA Shadow 25: 'F:\SHADOWS\EMPLOYEE1.SHD" auto length 10000 file H:\SHADOWS\EMPLOYEE2.SHD starting 10001 file J:\SHADOWS\EMPLOYEE3.SHD starting 30001 PAGE_SIZE 1024 Number of DB pages allocated =462 Sweep interval = 20000 Ручной режим В предыдущих примерах создавались теневые копии в режиме по умолчанию auto. Предположим, что теперь нам нужно, чтобы работа базы данных была остановлена каждый раз, когда работа с базой данных или ее теневой копией становится невоз- можной по различным причинам. В этом случае нам нужно создавать теневую копию в режиме manual (см. предыдущие примечания в этом разделе). Для сообщения серве- ру Firebird, что мы хотим установить это правило, мы создаем теневую копию с ис- пользованием ключевого слова manual в операторе create shadow: CREATE SHADOW 9 MANUAL '/shadows/employee.shd’; Теперь, если теневая копия будет использоваться как база данных, когда мы потеря- ем главный файл базы данных, или если теневая копия станет недоступной по раз- ным причинам, администратор базы данных должен удалить старый файл теневой копии и создать новую теневую копию до того, как клиенты смогут восстановить соединения. Условная теневая копия Во всех предыдущих примерах спецификация create shadow оставляла базы данных без теневого копирования после того, как теневая копия становилась недоступной по причине отключения от базы данных или когда она "занимала место" активной базы данных при физической смерти оригинальной базы данных. В случае теневой копии режима manual это то, что нам нужно. Мы выбираем данный режим, потому что хотим, чтобы соединения с базой данных оставались заблокиро- ванными, пока мы вручную не создадим новую теневую копию.
Гпава 15. Создание и ведение базы данных 291 В случае теневой копии режима auto соединения с базой данных могут быть восста- новлены после сбоя, как только теневая копия заменит базу данных. С этого момента работа с теневой копией не будет вестись, пока администратор не создаст вручную новую теневую копию. Если теневая копия станет недоступной по разным причинам, администратор об этом не узнает. В другом же случае мы имеем окно, где сообщает- ся об ошибке теневого копирования. Как было описано ранее в этом разделе, мы можем улучшить эту ситуацию, включив ключевое слово conditional в определение теневой копии режима auto. Результатом будет то, что когда старая теневая копия станет недоступной, сервер Firebird выпол- нит все необходимые вспомогательные действия и автоматически создаст новую те- невую копию, например: CREATE SHADOW 33 CONDITIONAL '/shadows/employee.shd'; Увеличение размера теневой копии В некоторых случаях бывает необходимым увеличить размер и количество файлов в теневой копии. Просто удалите теневую копию (как описано в следующем разделе) и создайте новую. Удаление теневой копии Теневую копию нужно удалять в следующих ситуациях: ♦ это "ручная" теневая копия, которая по разным причинам была отключена от сис- темы. Удаление ненужной теневой копии является необходимым для создания новой теневой копии и возобновления обслуживания базы данных; ♦ это безусловная автоматическая теневая копия, которая была отключена из-за не- которых системных событий. Ее нужно пересоздать для восстановления ее цело- стности; ♦ вам нужно изменить размеры файлов теневой копии, добавить больше файлов или установить новую теневую копию с другими атрибутами; ♦ теневое копирование больше не требуется. Удаление теневой копии означает от- ключение теневого копирования. Удаление теневой копии удаляет не только физические файлы, но также и ссылки на нее из метаданных базы данных. Чтобы иметь право на выполнение этой команды, вы должны быть соединены с базой данных как пользователь, который создал тене- вую копию, пользователь SYSDBA или (в POS1X) пользователь с привилегиями опе- рационной системы root. Синтаксис Используйте следующий синтаксис drop shadow: DROP SHADOW номер-набора-теневой-копии;
292 Часть IV. База данных и ее объекты Номер набора теневой копии является обязательным аргументом команды drop shadow. Для отыскания этого номера используйте в isql команду show database, будучи подключенным к этой базе данных. В следующем примере удаляются все файлы, связанные с набором оперативной ко- пии за номером 25: DROP SHADOW 25; Использование gfix-переключателя -kill Служебная утилита командной строки gfix (см. главу 39) имеет переключатель -kill, который внутренне вызывает команду drop shadow, чтобы удалить теневую копию и сделать ее недоступной. После выполнения этой команды можно будет продолжить создание новых теневых копий. Например, для удаления недоступной теневой копии нашей базы данных employee в POSIX наберите: [root@coolduck bin]# ./gfix -kill ../examples/employee.gdb -user SYSDBA -password masterkey В Win32 наберите: C:\Program Files\Firebird\bin> gfix -kill ..\examples\employee.gdb -user SYSDBA -password masterkey Тигиена" базы данных Firebird использует многоверсионную архитектуру. Это означает, что на страницах данных хранится множество версий строк данных. Когда строка изменяется или уда- ляется, Firebird сохраняет копию старого состояния записи и создает новую версию. Это размножение предыдущих версий записей может увеличить размер базы данных. Фоновая сборка мусора Для ограничения такого разрастания Firebird постоянно выполняет сборку мусора (garbage collection) на фоне активности базы данных. Фоновая сборка мусора ничего не делает с устаревшими версиями записей, которые относятся к незавершенным транзакциям — они не будут обработаны в процессе обычных служебных действий. Для полной санации базы данных Firebird может вы- полнять чистку базы данных (database sweeping). Чистка базы данных Чистка базы данных является способом систематического удаления всех устаревших версий строк и освобождения занятого ими пространства на страницах с целью его повторного использования. Периодическая чистка предотвращает разрастание базы
Глава 15. Создание и ведение базы данных 293 данных до излишних размеров. Не удивительно, что, хотя чистка осуществляется в асинхронном фоновом потоке, она может повлиять на производительность системы. По умолчанию база данных Firebird выполняет чистку, когда интервал очистки дос- тигает 20 000 транзакций. При этом поведение чистки может настраиваться: она мо- жет запускаться автоматически, интервал очистки может изменяться, автоматическая чистка может быть отменена, чтобы запускать ее вручную при необходимости. Ручная чистка может быть инициирована из служебной программы командной стро- ки gfix. Подробности см. в главе 39. Некоторые другие инструменты обеспечивают графический интерфейс для инициирования ручной чистки. Интервал очистки Сервер Firebird поддерживает инвентаризацию транзакций. Самая старая из транзак- ций, которые помечены в инвентарном списке как завершенные по rollback, называ- ется "старейшей заинтересованной" (Oldest Interesting Transaction, OIT, или Oldest transaction), и обозначает начальную точку для интервала очистки. Если интервал очистки больше нуля, Firebird запускает полную чистку базы данных, когда разница между OIT и транзакцией Oldest snaphsot превышает порог, установленный для ин- тервала очистки. Инструкции см. в главе 39. Сборка мусора в процессе резервного копирования Чистка базы данных не является единственным способом систематической сборки мусора. Резервное копирование базы данных дает тот же результат, потому что сер- вер Firebird должен читать каждую запись. Это дает возможность собирать мусор во всей базе данных. Как результат, регулярное резервное копирование базы данных может сократить необходимость в чистке и помочь поддерживать лучшую произво- дительность приложений. А ВНИМАНИЕ! Резервное копирование не является заменой чистки. Когда ре- I \ зервное копирование выполняет полную сборку мусора в базе данных, оно не —*-Д очищает учетные записи транзакций, как это делает чистка. Эффекты от чист- ки — или от игнорирования ее выполнения — обсуждаются в нескольких разде- лах главы 25. Информацию о преимуществах резервного копирования и восстановления см. в гла- ве 38. Проверка и ремонт Firebird предоставляет утилиты для проверки логических структур в базе данных и идентификации незначительных проблем, а также некоторый их ремонт. Множество таких ошибок может появляться время от времени, особенно в средах, где сети не-
294 Часть IV. База данных и ее объекты стабильные или шумные или электроснабжение является нестабильным. Поведение пользователя, а также дефекты в проектировании приложения или базы данных так- же часто приводят к логическим разрушениям. Ненормальное завершение клиентских соединений не влияет на целостность базы данных, поскольку сервер Firebird проверяет потерю соединения. Он сохраняет под- твержденные изменения данных и выполняет откат для любых данных, ожидающих подтверждения. Чистка является важным вопросом обслуживания, поскольку стра- ницы данных, которые были назначены неподтвержденным данным, остаются в виде "осиротевших". Проверка будет выявлять такие страницы и освобождать их для дальнейшего использования. Инструменты проверки достоверности способны определить и устранить незначи- тельные аномалии, явившиеся следствием ошибок операционной системы или обо- рудования. Такие ошибки обычно приводят к появлению проблем целостности базы данных по причине ошибок записи данных в страницы или потери страниц данных или индексов. Потерянные или поврежденные данные не могут быть восстановлены, такие искаже- ния должны быть удалены для восстановления целостности базы данных. Если для базы данных, содержащей такие структуры, будет выполнено резервное копирова- ние, то резервную копию будет невозможно восстановить. Поэтому важно следовать управляемому порядку действий по выявлению ошибок, их устранению, насколько это возможно, и переводу базы данных в стабильное состояние. Когда проверять достоверность и зачем Периодическая проверка достоверности должна быть частью обслуживающей дея- тельности администратора базы данных по выявлению и устранению небольших аномалий для повторного использования дискового пространства. Это также потре- буется, когда будут выявлены структурные повреждения или возникнут подозрения об их наличии. Признаки включают: ♦ ошибки "corrupt database" или "consistency check"; ♦ резервное копирование, которое закончилось ненормально; ♦ отказ или изменение напряжения электропитания при отсутствии источника бес- перебойного питания (UPS) или при предположении об отказе UPS; ♦ предполагаемые или сообщенные системой ошибки жесткого диска, сети или па- мяти; ♦ теневая копия заменяет собой базу данных после разрушения диска; ♦ база данных была перенесена с другой платформы или системы хранения; ♦ ожидаемое несанкционированное обращение к сети или базе данных со стороны внешних атак. Подробности использования gfix для выполнения проверки базы данных см. в гла- ве 39.
Глава 15. Создание и ведение базы данных 295 Что делать с разрушенной базой данных Если вы подозреваете, что у вас разрушена база данных, важно следовать правильной последовательности шагов восстановления, чтобы исключить дальнейшее разруше- ние. Первое, и самое важное дело — завершить работу всех пользователей и отклю- чить их от базы данных. В приложении 4 содержится подробное описание процедур починки разрушенной базы данных. Как разрушить базу данных Firebird В отличие от других СУБД, Firebird прекрасно справляется с травмирующими воз- действиями на базу данных. Тем не менее практика показывает несколько проверен- ных техник, полезных в случае, если разрушение вашей базы данных является одной из ваших целей. Автор желает поделиться с читателем этими средствами искажения базы данных. Модификация системных таблиц Firebird хранит и ведет все свои метаданные и ваши определенные пользователем объекты в... базе данных Firebird! Более точно он хранит их в отношениях (таблицах) прямо в самой базе данных. Идентификаторы системных таблиц, их столбцов и неко- торых других типов системных объектов начинаются с символов "RDB$". Поскольку это обычные объекты базы данных, их можно запрашивать и манипули- ровать ими как определенными пользователем объектами. Однако то, что вы може- те, не означает, что вы должны. Нельзя настоятельно рекомендовать, чтобы вы использовали только операторы DDL — непрямые операции SQL над системными таблицами — всякий раз, когда вам нужно изменять или удалять метаданные. Отложите всякие "прямые изменения", пока ваши умения в SQL и ваши знания сервера Firebird не станут более полными. Потерпевшая аварию база данных не является ни предметом приятного созерцания, ни легкой в починке. Отмена принудительной записи для Firebird 1.0.x в Windows Firebird по умолчанию устанавливается с возможностью принудительной записи (forced writes, синхронной записью). Измененные и новые данные записываются на диск немедленно после завершения операции (post). Возможно конфигурирование базы данных для использования асинхронной записи данных, когда измененные или новые данные сохраняются в памяти кэша и периоди- чески сбрасываются на диск подсистемой ввода/вывода операционной системы. Об- щий термин для такой конфигурации — отмена принудительной записи. Иногда это значение восстанавливается для улучшения производительности больших пакетных операций. Сервер платформ Win32 не сохраняет на диск кэш сервера Firebird 1.0.x, пока не бу- дет закрыт сервис Firebird. Не говоря уже о сбое в питании, может много чего плохо-
296 Часть IV. База данных и ее объекты го произойти с сервером Windows. Если он зависнет, система ввода/вывода прекра- тит работу, и работа вашего пользователя будет потеряна в процессе перезагрузки. Серьезное предупреждение: не отключайте принудительную запись на сервере Windows, если вы не используете Firebird 1.5 и выше. Firebird 1.5 по умолчанию сбрасывает кэш на диск каждые 5 секунд или каждые 100 операций записи— что произойдет быстрее. Эта частота может быть изменена В firebird.conf корректировкой ОДНОГО ИЛИ двух параметров MaxUnflushedWrites и MaxUnflushedWriteTime. Windows 95 не поддерживает асинхронную запись на диск. Серверы Linux более надежны при выполнении операций в случае временного от- ключения принудительной записи. Не оставляйте этот режим отключенным после завершения задачи, выполнявшей пакетные обновления, если у вас нет очень надеж- ной системы питания. Восстановление резервной копии в работающую базу данных Один из режимов восстановления в утилите gbak (gbak -г [estore]) позволяет восста- навливать файл резервной копии поверх существующей базы данных — база данных перезаписывается. В этом режиме возможно восстановление без предупреждения о том, что пользователи соединены с базой данных. Разрушение базы данных является практически гарантированным результатом. Ваши инструменты и процедуры администратора должны быть спроектированы та- ким образом, чтобы предотвратить перезапись базы данных, когда с ней соединены любые пользователи (включая SYSDBA). Чтобы сделать это практически, рекомендуется восстанавливать базу на резервное дисковое пространство, используя gbak -c[reate]. Прежде чем сделать восстановлен- ную базу данных активной, проверьте ее в резервной области, используя isql или ваш предпочитаемый инструмент администратора. Разрешение пользователям соединяться с базой данных в процессе ее восстановления Если вашей организации нравится жить на острие ножа, используйте переключатель -restore и позвольте пользователям соединяться с базой данных и выполнять изме- нения. Процесс восстановления создает базу данных с нуля, и как только будут соз- даны таблицы, ваши пользователи смогут (по крайней мере, потенциально, или если они все SYSDBA) обращаться к ним с операциями DML, в то время как ссылочная целостность и другие ограничения находятся еще только на подходе. В лучшем слу- чае они получат исключения и кучу неподтвержденных транзакций в частично скон- струированной базе данных. В худшем, они полностью уничтожат целостность данных. Копирование файлов базы данных, в то время как с ней соединены пользователи Используйте любую утилиту копирования или архивирования файловой системы (DOS copy, хсору, tar, gzip, WinZip, WinRAR и т. д.) для копирования файлов базы
Глава 15. Создание и ведение базы данных 297 данных, в то время как с ней соединены любые пользователи (включая SYSDBA). Копия будет поврежденной, но еще хуже, система блокировки и/или кэширования этих программ может привести к потере данных и, возможно, к разрушению исход- ного файла. Удаление базы данных Когда база данных больше не нужна, она может быть удалена с сервера. Удаление базы данных удаляет также все файлы, связанные с базой данных — первичные и вторичные файлы, файлы теневой копии, системные журналы — и все их данные. Командой удаления базы данных является drop database; она не имеет параметров. При выполнении команды вы должны быть соединены с базой данных как ее владе- лец, пользователь SYSDBA или (в Linux/UNIX) как пользователь с привилегиями операционной системы root. Синтаксис Если, будучи соединенным с базой данных, вы захотите ее удалить, используйте для этого оператор: DROP DATABASE; После удаления база данных не может быть восстановлена, следовательно: ♦ будьте уверены, что вы действительно хотите, чтобы она была потеряна навсегда; ♦ вначале сделайте резервную копию, если есть шанс, что вам может в будущем что-нибудь из нее понадобиться. Пора дальше Создание базы данных инсталлирует инфраструктуру, необходимую для начала соз- дания объектов. Первичным объектом для постоянного хранения данных в базе дан- ных является таблица. В отличие от электронных таблиц, большинства настольных систем управления данными и даже одного или двух кандидатов на "профессиональ- ную" реляционную СУБД Firebird не сохраняет данные в структурах, которые явля- ются "табличными" в виде упорядоченных строк и столбцов, распознаваемых файло- вой системой. Firebird управляет собственным дисковым пространством и использует собственные правила для размещения и отображения постоянных данных. Он под- держивает множество способов выделения данных в подобные таблицам наборы. В следующей главе мы начнем с создания постоянного SQL-объекта table.
ГЛАВА 16 Таблицы В терминологии SQL-89 и SQL-92 таблицы Firebird являются постоянными базовы- ми таблицами. Эти стандарты определяют некоторые другие типы, включая про- сматриваемые таблицы, которые Firebird реализует в виде просмотров (view, см. главу 24), и производные таблицы (derived tables), которые Firebird реализует в виде хранимых процедур выбора (см. главы 28 и 30). О таблицах Firebird В отличие от настольных баз данных, таких как Paradox и dBase, база данных Firebird не является серией "табличных файлов", физически организованных в виде строк и столбцов. Firebird хранит данные, независимо от их структуры в сжатом формате на страницах базы данных. Он может хранить одну или более записей — или, более правильно, строк— данных таблицы на одной странице. В случаях, когда данные одной строки слишком велики, чтобы разместиться на одной странице, строка может размещаться на нескольких страницах. Хотя страница, которая хранит данные таблицы, всегда будет содержать данные, принадлежащие одной таблице, страницы не хранятся рядом. Данные таблицы могут быть разбросаны по всему диску, а в случае многофайловых баз данных они могут быть распределены между разными каталогами и дисками. Данные blob хранятся отдельно от строк, которые ими владеют, в страницах базы данных другого типа. Структурные описания Метаданные— физические описания таблиц, их столбцов и атрибутов, так же как и описания всех других объектов — сами хранятся в обычных таблицах Firebird внутри базы данных. Сервер Firebird изменяет данные в этих таблицах, когда объекты базы данных создаются, изменяются или удаляются. Он постоянно к ним обращается при выполнении операций над строками. Такие таблицы называются системными таб- лицами. Более подробную информацию см. в разд. "Системные таблицы" в самом начале главы 14. Схемы системных таблиц см. в приложении 9. Создание таблиц Предполагается, что, достигнув той точки, когда вы готовы создавать таблицы, вы уже выполнили анализ данных и подготовили модель, а также вы имеете совершенно
Глава 16. Таблицы 299 четкое представление о структурах ваших главных таблиц и их взаимоотношениях. Для подготовки к созданию таких таблиц вам нужно выполнить следующие шаги: ♦ вы должны создать базу данных для их размещения — инструкции см. в главе 15; ♦ вы должны соединиться с базой данных; ♦ если вы планируете использовать домены для определения типов данных столб- цов ваших таблиц, вы должны уже создать домены (см. главу 13). Владение таблицами и привилегии Когда создается таблица, Firebird автоматически применяет к ним безопасность схе- мы по умолчанию. Человеку, который создает таблицу (ее владелец), назначаются к ней все привилегии SQL, включая право передавать привилегии другим пользовате- лям, триггерам и хранимым процедурам. Ни один другой пользователь, за исключе- нием SYSDBA, не будет иметь никакого доступа к этой таблице, пока явно не полу- чит привилегии. ВНИМАНИЕ! Эта защита будет столь же хороша (или плоха), сколь и защита / I \ доступа к вашему серверу. Любой, кто может соединиться с вашим сервером, сможет создать базу данных. Любой, кто может соединиться с вашей базой дан- ных, сможет создавать в ней таблицы. Firebird 1.5 несколько улучшает эту пе- чальную ситуацию, позволяя вам ограничивать размещения, где могут созда- ваться базы данных. См. параметр DatabaseAccess в файле firebird.conf. Информацию о привилегиях SQL см. в главе 35. Оператор CREATE TABLE В DDL для создания таблиц используется оператор create table. Его синтаксис: CREATE TABLE таблица [EXTERNAL [FILE] 'спецификация-файла'] (<определение-столбца> [, <определение-столбца> | <отраничение~таблицы> ...]); Самый первый основной аргумент в create table— идентификатор таблицы1. Он является обязательным и должен быть уникальным среди всех имен таблиц, про- смотров и процедур базы данных, иначе вы не сможете создать таблицу. Вы также должны предоставить определение, по крайней мере, одного столбца. Определение столбцов Когда вы создаете таблицу в базе данных, ваша основная задача — определить раз- личные атрибуты и ограничения для каждого столбца в этой таблице. 1 Если вы кодируете create table в приложении встроенного SQL (Embedded SQL) с на- мерением также в этом приложении заполнять данными эту таблицу, то таблица вначале должна быть объявлена в предшествующем операторе deciare table.
300 Часть IV. База данных и ее объекты Синтаксис определения столбца: Определение-столбца> = столбец {тип-данных | COMPUTED [BY] (<вьражение>) | домен} [DEFAULT [литерал! NULL | USER} ] [NOT NULL] [<ограничение-столбца>] [COLLATE порЛцок-сортировки} В следующем разделе описываются требуемые и необязательные атрибуты, которые вы можете определить для столбца. Требуемые атрибуты Приведем требуемые атрибуты. ♦ Идентификатор столбца (имя), уникальный среди столбцов этой таблицы. ♦ Одно из следующих: • тип данных SQL; • выражение для вычисляемого столбца; • имя домена для столбцов, основанных на доменах. Столбцы разделяются запятыми, например: CREATE TABLE PERSON ( PERSON_ID BIGINT NOT NULL, FIRST_NAME VARCHAR (35), LAST_NAMES VARCHAR (80), FULL_NAME COMPUTED BY FIRST_NAME | | ' 'll LAST_NAMES, PHONE_NUMBER TEL_NUMBER) ; Столбец full name является вычисляемым столбцом, который вычисляется конкате- нацией двух других описанных столбцов: first_name и last_name. Мы вернемся к вы- числяемым столбцам несколько позже. Ограничение not null применяется к person id, потому что мы хотим сделать его первичным ключом (детали рассмотрим позже). Для столбца phone_number мы используем домен, который был определен в нашем примере в главе 13: CREATE DOMAIN TEL_NUMBER AS VARCHAR(18) CHECK (VALUE LIKE '(0%)%'); Столбцы, основанные на доменах Если определение столбца основано на домене, оно может включать новое значение по умолчанию, дополнительные ограничения check, предложение collate, которые перекрывают значения, уже определенные в определении домена. Оно также может включать дополнительные атрибуты или ограничения столбца. Например, вы можете добавить ограничение not null для столбца, если домен его еще не содержит.
Глава 16. Таблицы 301 Д ВНИМАНИЕ! Домен, который был определен как not null, не может быть пере- определен на уровне столбца, как допускающий пустое значение. Например, следующий оператор создает таблицу country, ссылающуюся на домен с именем countryname, который не имеет ограничения not null: CREATE TABLE COUNTRY ( COUNTRY COUNTRYNAME NOT NULL PRIMARY KEY, CURRENCY VARCHAR(10) NOT NULL); Мы добавили ограничение not null в определение столбца countryname, потому что он будет первичным ключом таблицы country. Необязательные атрибуты Следующие разделы описывают необязательные атрибуты столбца. Значение DEFAULT Определение значения по умолчанию может сэкономить время ввода данных и пре- дотвратить ошибки ввода данных, когда новая строка добавляется в таблицу. Если строка добавляется без включения этого столбца в список столбцов, то значение по умолчанию — если определено — может быть автоматически записано в столбец. Основанный на домене столбец может включать значение по умолчанию, которое локально перекрывает значение по умолчанию, определенное для домена. Например, возможным значением по умолчанию для столбца timestamp может быть контекстная переменная current_timestamp (серверная дата и время). В символьном столбце логического стиля (тгие/raise) значение по умолчанию может быть установ- лено в ' Е”, чтобы гарантировать, что допустимое, непустое значение будет записано в каждую новую строку. Значение по умолчанию должно быть совместимым с типом данных столбца и долж- но согласовываться с любыми другими ограничениями этого столбца или лежащего в основе домена. Значение по умолчанию, соответствующее типу данных, может быть: ♦ константой (например, строкой, числом или датой); ♦ контекстной переменной (например, current_timestamp, current__user2, current_ CONNECTION И T. Д.)( ♦ предварительно определенным литералом даты (таким как 'now, 'tomorrow и т. д.); 2 Если ваша операционная система поддерживает использование многобайтовых символов в именах пользователей или вы используете многобайтовый набор символов при определении ролей, то каждый столбец, в котором будут сохраняться в качестве значений по умолчанию cuRrentjjser или CURRENT_ROLE, должен быть определен с использованием совместимого набора символов.
302 Часть IV. База данных и ее объекты ♦ null может быть установлен как значение по умолчанию только для столбцов, допускающих пустое значение3. Столбец, допускающий пустое значение, получа- ет значение по умолчанию null автоматически, однако вам может понадобиться перекрыть нежелательное значение по умолчанию на уровне домена. Не объяв- ляйте это значение по умолчанию для столбцов, имеющих ограничение not null. Д ВНИМАНИЕ! Когда вы полагаетесь на значение по умолчанию, вы должны по- нимать, что значение по умолчанию будет применяться только к добавлению новой строки и только если оператор insert не включает этот столбец со значе- нием по умолчанию в список столбцов. Если ваше приложение включает этот столбец в оператор insert и передает null в списке значений, то null и будет сохранен, независимо от любого определенного значения по умолчанию. Если столбец не допускает пустых значений, то передача значения null всегда вызо- вет исключение. Следующий пример определяет столбец created_by, который имеет в качестве значе- ния по умолчанию контекстную переменную current__user: CREATE TABLE ORDER ( ORDER_DATE DATE, CREATED—BY VARCHAR (31) DEFAULT CURRENTJUSER, ORDER_AMOUNT DECIMAL(15,2)); Новая строка добавляется пользователем jillibee; столбец created_by не указан в списке столбцов: INSERT INTO ORDER (ORDER_DATE, ORDER_AMT) VALUES ('15-SEP-2004', 1004.42); Запрос к этой таблице: SELECT * FROM ORDER; ORDER_DATE CREATED—BY ORDER_AMOUNT 15-SEP-2004 JILLIBEE 1004.42 Предложение CHARACTER SET Набор символов (character set) используется для индивидуального символьного столбца или текстового столбца blob, когда вы определяете столбец. Если вы не за- даете набор символов, то столбец принимает набор символов домена, если опреде- лен, иначе он принимает набор символов по умолчанию для базы данных. Пример: 3 См. ограничение NOT null для столбца, null может оказаться несовместимым значением
Глава 16. Таблицы 303 CREATE TABLE TITLES_RUSSIAN ( TITLE_ID BIGINT NOT NULL, TITLE_EN VARCHAR(IOO), TITLE VARCHAR(IOO) CHARACTER SET WIN1251); Подробнее о наборах символов см. главу 11. Список доступных наборов символов представлен в приложении 8. Предложение COLLATE Предложение collate может быть добавлено к столбцам char и varchar для перекры- тия последовательности сортировки, определенной для набора символов столбца в домене, на котором основывается столбец. Последовательность сортировки не при- менима ДЛЯ ТИПОВ BLOB. Следующее расширение предыдущего примера включает предложение collate: CREATE TABLE TITLES-RUSSIAN ( TITLE-ID BIGINT NOT NULL, TITLE_EN VARCHAR(100), TITLE VARCHAR(IOO) CHARACTER SET WIN1251 COLLATE PXW_CYRL); Д ВНИМАНИЕ! Будьте осторожны при применении предложения collate к столб- цам, которые должны быть индексированы. Максимальный размер индекса 252 байта может быть радикально уменьшен при некоторых последовательно- стях сортировки. Сначала проверьте! (Для collate pxw cyrl он уменьшается до 84 символов.) Подробности о последовательностях сортировки, доступных для каждого набора символов, см. в главе 11. Список доступных наборов символов и порядков сортиров- ки см. в приложении 8. СОВЕТ. Вы можете получить собственный список, который может включать и более поздние последовательности сортировки, создав новую базу и выполнив запрос из разд. "Отображение доступных последовательностей сортировки" главы 11. Вычисляемые столбцы Вычисляемые столбцы — это столбцы, чье значение вычисляется каждый раз, когда во время выполнения к столбцам происходит обращение. Это может быть удобный способ доступа к избыточным данным без отрицательных эффектов от их фактиче- ского хранения. Не удивительно, что такие столбцы не могут обрабатываться как обычные данные — см. ограничения, описанные позже в этом разделе. Синтаксис: <нмя~столбца> COMPUTED [BY] (<выражение>);
304 Часть IV. База данных и ее объекты Нет необходимости описывать тип данных (хотя это возможно) — Firebird вычислит его подходящим образом, выражение — любое скалярное выражение, допустимое для типов данных столбцов, входящих в состав выражения. Внешние функции прекрасны для использования, если вы уверены, что библиотеки этих функций существуют в готовом виде или могут быть скомпилированы для всех платформ, где может уста- навливаться база данных. (Информацию о внешних функциях, также называемых UDF, см. в главе 21. Список общих функций представлен в приложении /.) Приведем другие существующие ограничения для вычисляемых столбцов. ♦ Любой столбец, к которому обращается выражение, должен быть определен до определения вычисляемого столбца, следовательно, полезной практикой является размещение вычисляемых столбцов последними. ♦ Вычисляемый столбец не может быть определен как массив или возвращать массив. ♦ Вы можете определить вычисляемый столбец blob, используя оператор select для поиска столбца blob в другой таблице, но делать это настоятельно не рекомендуется. ♦ Вычисляемые столбцы не могут быть индексированы. ♦ Ограничения, помещенные для вычисляемого столбца, будут проигнорированы. ♦ Вычисляемые столбцы используются только для вывода и только для чтения. Включение их в операторы insert или update вызовет исключение. Д ВНИМАНИЕ! В качестве общего предупреждения: хотя возможно создание вы- числяемого столбца с использованием оператора select к другой таблице, эта практика должна быть исключена, поскольку добавляет нежелательные зависи- мости и может ухудшить производительность. Правильно нормализованная мо- дель базы данных не требует такого. Примеры вычисляемых столбцов Следующий оператор создает вычисляемый столбец full name путем конкатенации столбцов LAST_NAMES И FIRST_NAME. CREATE TABLE PERSON ( . PERSON_ID BIGINT NOT NULL, FIRST_NAME VARCHAR(35) NOT NULL, LAST_NAMES VARCHAR (80) NOT NULL, FULL_NAME COMPUTED BY FIRST_NAME | | ' 'll LAST_NAMES) ; /**/ SELECT FULL_NAME FROM PERSON WHERE LAST_NAMES STARTING WITH 'Smi1; FULL_NAME * Arthur Smiley John Smith Mary Smits
Глава 16. Таблицы 305 С ПРИМЕЧАНИЕ. Обратите внимание на ограничения not null в двух именах, объединяемых для вычисляемого столбца. Важно обращать внимание На такие детали в случае вычисляемых столбцов, потому что null как элемент конкатена- ции всегда будет давать результат null. Следующий оператор вычисляет два столбца с использованием контекстных пере- менных. Это может быть полезным для регистрации подробностей создания строки; CREATE TABLE SNIFFIT (SNIFFID INTEGER NOT NULL, SNIFF COMPUTED BY (CURRENT_USER), SNIFFDATE COMPUTED BY (CURR£NT_TIMESTAMP)); /“/ SELECT FIRST 1 FROM SNIFFIT; SNIFFID SNIFF SNIFFDATE 1 SYSDBA 2004-08-15 08:15:35.0000 Следующий пример создает таблицу с вычисляемым столбцом (new_price), который использует ранее созданные определения для old_price и percent_change: CREATE TABLE PRICE_HISTORY ( PRODUCT_ID D_IDENTITY NOT NULL, /* использует домен */ CHANGE_DATE DATE DEFAULT CURRENT_TIMESTAMP NOT NULL, UPDATER_ID D_PERSON NOT NULL, /* использует домен */ OLD_PRICE DECIMAL(13,2) NOT NULL, PERCENT—CHANGE DECIMAL (4,2) DEFAULT 0 NOT NULL CHECK (PERCENT_CHANGE BETWEEN -50.00 AND 50.00)‘, NEW-PRICE COMPUTED BY (OLD_PRICE + (OLD-PRICE * PERCENT-CHANGE / 100)) ); Ограничения На языке реляционных баз данных любое условие, налагаемое на формат, диапазон значений, содержание или зависимости структуры данных, называется ограничением (constraint). Firebird предоставляет несколько способов для реализации ограничений, включая как формальные, определенные стандартами ограничение целостности и ссылочное ограничение, так и определенные пользователем ограничения check. Ограничения видны всем транзакциям, которые выполняют доступ к базе данных, и автоматически применяются на сервере. Они различаются их областью действия. Некоторые, такие как not null, напрямую применяются к одному столбцу (ограниче- ния столбца), в то время как другие, такие как primary key и некоторые ограничения check, имеют эффект на уровне таблицы (ограничения таблицы). Ограничение foreign key имеет область действия таблица-таблица.
306 Часть IV. База данных и ее объекты Ограничения существуют "в своих собственных правах" как объекты в базе данных Firebird. Каждое ограничение уникально представлено в метаданных с правилами и зависимостями, которые представлены обычными отношениями между системными таблицами. Ограничения целостности Ограничения целостности устанавливают правила, которые управляют состоянием доступных элементов данных или отношением между столбцом и таблицей, как це- лое — часто и тем, и другим. Примерами являются not null (не допускает ввод, со- держащий неопределенное значение), unique (требует, чтобы вводимый элемент не имел соответствующего значения этого столбца в таблице) и primary key (объединяет два других ограничения, а также "представляет" таблицу для ссылочного отношения с другими таблицами). Каждое из ограничений целостности подробно обсуждается позже в этой главе. Ссылочное ограничение Ссылочное ограничение реализовано как foreign key. Ограничение внешнего ключа существует только в контексте другой таблицы и уникального ключа этой таблицы, заданного явно или неявно в предложении references при объявлении ограничения. Таблицы, связанные в отношении внешнего ключа, называются связанными ограни- чением ссылочной целостности. Следовательно, любой столбец или группа столб- цов, ограниченная в ограничениях primary key или unique, также потенциально явля- ются субъектами ссылочных ограничений. Ссылочная целостность подробно обсуждается в главе 17. Именованные ограничения При объявлении ограничения на уровне таблицы или на уровне столбца вы можете именовать ограничение, используя предложение constraint. Если вы опустите пред- ложение constraint, Firebird сгенерирует уникальное системное имя ограничения. Ограничения хранятся в системной таблице rdb$relation_constraints. Хотя присваивание имени ограничению необязательно, назначение описательного имени в предложении constraint сделает ограничение удобным при поиске для изме- нения или удаления или когда его имя появляется в сообщении о нарушении ограни- чения. Помимо преимуществ документирования такой стиль особенно полезен для отделения определений ключа от определений столбцов в скриптах. Имена для PRIMARY KEY и FOREIGN KEY Именование ограничения имеет особый смысл для ограничений primary key и foreign key, особенно в Firebird 1.5 и выше. Существует возможность перекрыть "родные" для Firebird ограничения по именованию ключей.
Глава 16. Таблицы 307 Во всех версиях указанное имя будет перекрывать имя по умолчанию iNTEG_nn и бу- дет применено к ограничению. Однако: ♦ в версии 1.5 и более поздних поддерживающий ограничение индекс будет иметь то же самое имя, что и ограничение; ♦ в версии 1.0.x будет использовано имя индекса по умолчанию (RDB$PRiMARYnn или RDBSFOREIGNnn). t ПРИМЕЧАНИЕ. Существующие имена ограничений останутся без изменений при переводе базы данных с сервера версии 1.0.x на сервер версии 1.5. Поведение при именовании ограничений более подробно описывается в следующем разделе и в следующей главе. Ограничения целостности Ограничение NOT NULL Firebird не поддерживает атрибут указания допустимости пустого значения, как это делают некоторые нестандартные СУБД. В соответствии со стандартами все столбцы в Firebird могут содержать пустое значение, если не будет явно указано ограничение not null. Необязательное ограничение not null является ограничением на уровне столбца, которое может быть применено, чтобы заставить пользователя вводить зна- чение. null не является значением, так что любая попытка ввести null в столбец или установить его в значение null приведет к исключению. Поскольку роль ограничения not null заключается в формировании ключей, вы должны знать относительно него некоторые ограничения. ♦ Оно должно применяться к определению любого столбца, который будет включен в ограничение primary key или unique. ♦ В Firebird 1.0.x оно должно применяться к определению любого столбца, который будет включен в ограничение unique или в уникальный индекс. ♦ Оно не может быть удалено из домена или столбца операторами alter domain или alter table, alter column или перекрыто на уровне столбца. Не используйте до- мен not null для определения столбца, который может иметь значение null. Для большего понимания null см. разд. "Обсуждение NULL" главы 21. Ограничение PRIMARY KEY primary key является ограничением целостности на уровне столбца — набор поддер- живаемых правил, — которое формально отмечает столбец или группу столбцов как уникальный идентификатор для каждой строки в таблице.
308 Часть IV. База данных и ее объекты Если вы пришли в Firebird из СУБД, которые поддерживают концепцию "первичного индекса" для определения ключа (обычно основанные на файлах системы, такие как Paradox, Access и MySQL), то Firebird и мир стандартов SQL вам понятны. Первич- ный ключ является не индексом, а ограничением. Одним из правил для такого огра- ничения является то, что ограничение должно иметь определенный уникальный ин- декс из одного или более связанных с ним непустых элементов. Простое создание такого индекса не создает первичный ключ. При этом создание ограничения первичного ключа приводит к созданию требуемого индекса, состояще- го из столбцов, перечисленных в объявлении этого ограничения. ,/.\ ВНИМАНИЕ! Не надо импортировать существующий "первичный индекс" из на- • \ следуемой системы, основанной на файлах, или создавать такой индекс в ожи- дании объявления ограничения первичного ключа. Firebird не может накладывать ограничение первичного ключа поверх существующего индекса — по крайней мере в существующих версиях, включая 1.5, — а оптимизатор запросов не будет правильно работать при дублировании индексов. Таблица может иметь только один первичный ключ. Когда вы определяете ограниче- ние, Firebird автоматически создает требуемый индекс, используя множество имено- ванных правил. Имена индексов первичных ключей обсуждаются далее. Jrk ВНИМАНИЕ! Если вы конвертируете базу данных в Firebird из любого другого А ! \ источника за исключением InterBase или Oracle, то вы должны обратить особое внимание на схему в отношении имен и ограничений первичного ключа. Хотя само ограничение primary key не является ссылочным ограничением, оно обычно является обязательной частью любого ссылочного ограничения, будучи по- тенциальным объектом предложения references ограничения foreign key. Подробно- сти см. в главе 17. Выбор первичного ключа Выявление столбцов в качестве кандидатов на первичный ключ выходит за рамки данного издания. Много прекрасных книг было написано о нормализации, процессе сокращения избыточности и повторяющихся групп в наборах данных, а также о пра- вильной идентификации элемента, который уникальным образом представляет одну строку в таблице. Если вы новичок в реляционных базах данных, то затраты на изу- чение хорошей книги по моделированию данных не будут слишком большими. Кандидат в первичные ключи, который может быть одним столбцом или группой столбцов, имеет два обязательных требования. ♦ Атрибут not null должен быть объявлен для всех столбцов в группе из одного или более столбцов. Целостность ключа может быть осуществлена только сравне- нием значений, a null не является значением.
Глава 16. Таблицы 309 ♦ Столбец или группа столбцов должны быть уникальными — т. е. не может в таб- лице появиться более одной строки с теми же значениями. Например, номер во- дительских прав или социального обеспечения могут рассматриваться как канди- даты, потому что они генерируются системами, которые не допускают дублика- тов номеров. К этим теоретическим "установкам" должна быть добавлена третья. ♦ Общий размер кандидата в ключи должен быть 252 байта или меньше. Дело здесь не просто в подсчете символов. Этот лимит должен быть уменьшен — в некото- рых случаях радикально — если присутствует несколько столбцов, недвоичная последовательность сортировки или многобайтовый набор символов. Как реальные данные могут привести вас к неудаче Используя таблицу employee базы данных employee.fdb из каталога /examples корне- вого каталога Firebird (employee.gdb в версии 1.0.x), давайте посмотрим, как реальные данные могут привести к ошибочности ваших теоретических предположений об уни- кальности. Вот объявление, которое показывает имеющие смысл данные, хранимые в этой таблице: CREATE TABLE EMPLOYEE ( FIRST_NAME VARCHAR(15) NOT NULL, /* предположение: служащий должен иметь имч */ LAST_NAME VARCHAR(20) NOT NULL, /* предположение: служащий должен иметь фамилию */ PHONE_EXT VARCHAR(4), HIRE_DATE DATE DEFAULT CURRENT_DATE NOT NULL, DEPT_NO CHAR(3) NOT NULL, JOB__CODE VARCHAR (5) NOT NULL, JOB_GRADE SMALLINT NOT NULL, JOB_COUNTRY VARCHAR(15) NOT NULL, SALARY NUMERIC (15, 2) DEFAULT 0 NOT NULL, FULL_NAME COMPUTED BY FIRST_NAME ( | ' 'll LAST_NAME ) ; Фактически эта структура не имеет кандидата в ключи. Невозможно идентифициро- вать одну строку служащего, используя (first_name, last_name) в качестве ключа, по- скольку комбинация двух элементов с вероятностью от средней до высокой может дублироваться в организации. Мы не сможем сохранить записи двух служащих с именем John Smith. Для получения ключей необходимо что-то изобрести. Это "что-то" — механизм, из- вестный как суррогатный'ключ. Суррогатные ключи Мы уже рассматривали суррогатный ключ во вводной теме о ключах в главе 14. Сур- рогатный первичный ключ — значение, гарантирующее уникальность и не имеющее
310 Часть IV. База данных и ее объекты смыслового содержания, которое используется в качестве заменителя ключа в струк- туре таблицы, которая не может предоставить кандидата на ключ в собственной структуре. По этой причине в таблицу employee добавляется emp_no (объявляется че- рез домен) для выполнения роли суррогатного ключа: create domain empno smallint ; COMMIT; alter table employee ADD EMP_NO EMPNO NOT NULL, ADD CONSTRAINT PK_EMPLOYEE PRIMARY KEY(EMP_NO) ; Эта база данных также содержит генератор с именем emp_no_gen и триггер Before insert (перед добавлением) с именем set_emp_no для таблицы employee для получения значения данного ключа в момент добавления новой строки. В разд. "Реализация ав- тоинкрементных ключей" главы 31 эта техника описывается в деталях. Это рекомен- дованный способ реализации суррогатных ключей в Firebird. Возможно, вам захочется рассмотреть преимущества использования суррогатного первичного ключа не только в случае, когда таблица не может предложить кандида- та, но также и в случаях, где ваш кандидат в ключи является составным. Составные первичные ключи В процессе анализа данных иногда в структуре данных можно отыскать единствен- ный уникальный столбец. Теория советует найти два или более столбцов, сгруппиро- ванных вместе в качестве ключа, которые будут гарантировать уникальность строки. Когда множество столбцов объединяются для формирования ключа, такой ключ на- зывается составным ключом (composite key) или иногда сложным ключом. Если вы имеете опыт работы с такими СУБД, как Paradox, где использовали состав- ные ключи для реализации иерархических отношений, вам, вероятно, будет тяжело расстаться с мыслью, что вам придется жить без них. Пока еще на практике состав- ные ключи должны рассматриваться очень ограниченно в таких СУБД, как Firebird, где не выполняется проход по физическим индексным структурам на диске для реа- лизации отношений. В Firebird нет необходимости в составных индексах и, более того, составные индексы создают некоторые проблемы как для разработки, так и для производительности в случае больших таблиц. ♦ Составные ключи обычно являются составленными из неатомарных элементов ключа— т. е. выбранные столбцы имеют смысловое значение (они являются "значимыми данными") и, несомненно, уязвимы для внешних изменений и оши- бок ручного ввода. ♦ Внешние ключи из других таблиц, которые ссылаются на эту таблицу, будут дуб- лировать каждый элемент составного ключа. Ссылочная целостность подвергает- ся риску при использовании неатомарных ключей. Комбинация неатомарных эле- ментов увеличивает риск.
Глава 16. Таблицы 311 ♦ Ключи — внешние, так же как и первичные — имеют постоянные индексы. Со- ставные индексы имеют более строгие ограничения по размеру, чем индексы из одного столбца. ♦ Составные индексы имеют тенденцию к большим размерам. Большие индексы используют больше страниц базы данных, что приводит к тому, что индексные операции (сортировка, соединение и сравнение) выполняются медленнее, чем не- обходимо. Атомарность столбцов первичного ключа Рекомендуется на практике не включать в первичные и внешние ключи любые столбцы, имеющие смысл как данные. Это нарушает один из основных принципов проектирования реляционных баз данных— атомарность. Принцип атомарности требует, чтобы каждый элемент данных полностью существовал сам по себе с еди- ным внутренним правилом управления его существованием. Чтобы первичный ключ был атомарным, нужно быть вне человеческих решений. Ес- ли люди составляют его или классифицируют его, он не является атомарным. Если он является субъектом любого правила за исключением требований not null и уни- кальности, он не является атомарным. В приведенном ранее примере даже водитель- ские права или номер социального обеспечения не соответствуют требованиям атомарности для первичного ключа, потому что они являются субъектами внешних систем. Синтаксис объявления первичного ключа Можно использовать несколько вариантов синтаксиса для назначения ограничения primary key столбцу или группе столбцов. Все столбцы, являющиеся элементами пер- вичного ключа, должны быть предварительно определены с атрибутом not null. Так как нельзя добавить ограничение not null в столбец после его создания, необходимо позаботиться об этом ограничении до использования других ограничений. Ограничение primary key может применяться в любой из следующих фаз создания метаданных: ♦ в определении столбца в операторах create table или alter table как часть опре- деления столбца; ♦ в определении таблицы в операторах create table или alter table как отдельно определенное ограничение таблицы. Определение первичного ключа как часть определения столбца В следующей последовательности создается и подтверждается (commit) домен, не допускающий пустое значение, затем определяется столбец первичного ключа, основанный на этом домене, и одновременно применяется ограничение primary key к этому столбцу:
312 Часть IV. База данных и ее объекты CREATE DOMAIN D_IDENTITY AS BIGINT NOT NULL; COMMIT; CREATE TABLE PERSON ( PERSON__ID D_IDENTITY PRIMARY KEY, ) ; Firebird создает ограничение таблицы с именем iNTEG_nn и индекс с именем RDB$PRiMARYnn. (nn в каждом случае — число, полученное от генератора. Эти два числа не связаны друг с другом.) Вы не можете повлиять на то, какими будут эти имена, и не можете поменять их. Результат будет похожим, если вы используете тот же подход при добавлении столб- ца, используя оператор alter table и создавая первичный ключ в одном предло- жении: ALTER TABLE BOOK ADD BOOK^ID D_IDENTITY PRIMARY KEY; Определение первичного ключа как именованного ограничения Другой способ определения первичного ключа в определении таблицы — добавить объявление ограничения в конце определений столбцов. Объявления ограничений помещаются последними, потому что они зависят от существования столбцов, к ко- торым они обращаются. Этот метод дает вам возможность именования ограничений. Следующее объявление именует ограничение первичного ключа как pk atable: CREATE TABLE ATABLE ( ID BIGINT NOT NULL, ANOTHER-COLUMN VARCHAR(20), CONSTRAINT PK^ATABLE PRIMARY KEY(ID) ); Теперь вместо использования сгенерированного системой имени rdb$рятмлкуппп Firebird использует pk atable в качестве имени этого ограничения. В Firebird 1.5 и выше он также применяет определенное пользователем имя ограничения для под- держивающего уникального индекса. В этом примере индекс получит имя pkjytable, когда в других версиях его имя будет rdb$ primary™™. Firebird 1.5 также позволяет использовать определенные пользователем имена для ограничения и поддерживающего его индекса. Использование пользовательского индекса До Firebird 1.5 не было возможности использовать убывающий индекс для поддерж- ки первичного ключа. Начиная с версии 1.5, можно поддерживать первичный ключ убывающим индексом. Чтобы это сделать, в Firebird 1.5 добавляется расширение синтаксиса в форме предложения using, позволяющего создавать индекс asc[ending] (по возрастанию) или descending] (по убыванию) и присваивать ему имя, отличное от имени ограничения.
Глава 16. Таблицы 313 asc и desc определяют направление поиска. Подробнее эта концепция обсуждается в главе 18. Следующий оператор создаст ограничение первичного ключа с именем рк атезт и поддерживающий его убывающий индекс с именем idx_pk_atest: CREATE TABLE ATEST ( ID BIGINT NOT NULL, DATA VARCHAR(10)); COMMIT; ALTER TABLE ATEST ADD CONSTRAINT PK_ATEST PRIMARY KEY(ID) USING DESC INDEX IDX_PK_ATEST; COMMIT; Альтернативный синтаксис также будет работать: CREATE TABLE ATEST ( ID BIGINT NOT NULL, DATA VARCHAR(10), CONSTRAINT PK_ATEST PRIMARY KEY(ID) USING DESC INDEX IDX__PK_ATEST; ЖА ВНИМАНИЕ! Если вы создаете индекс descending для ограничения первичного / J \ или уникального ключа, вы должны указать using desc index для всех ссылаю- щихся на него внешних ключей. Добавление первичного ключа к существующей таблице Добавление в таблицу ограничений может быть отложенным. Это общая практика разработчиков определять все свои таблицы без ограничений таблицы, а затем до- бавлять их, используя отдельный скрипт. Основная причина такой практики: боль- шие скрипты часто дают сбой, потому что авторы забывают про некоторые зависи- мости. Просто будет меньше головной боли, если создавать базу данных в последо- вательности, которая уменьшает время и раздражение при исправлении ошибок зависимостей и нового запуска скриптов. Обычно в первом скрипте мы объявляем таблицы и подтверждаем их создание: CREATE TABLE ATABLE ( ID BIGINT NOT NULL, ANOTHER^COLUMN VARCHAR (20), < другие столбцы > ); CREATE TABLE ANOTHERTABLE ( ... ); COMMIT;
314 Часть IV. База данных и ее объекты ALTER TABLE ATABLE ADD CONSTRAINT PK_ATABLE PRIMARY KEY(ID); ALTER TABLE ANOTHERTABLE... ИТ. Д. В следующей главе при рассмотрении определений foreign key станут очевидными преимущества создания базы данных в последовательности надежных зависимостей. Ограничения CHECK Ограничение check используется для проверки достоверности вводимых значений данных. Оно реализует условия или требования, которым должно удовлетворять зна- чение для успешного добавления или обновления. Оно не может изменять вводимое значение; оно лишь вернет исключение проверки достоверности, если значение не будет соответствовать условию. СОВЕТ. Ограничения check применяются после выполнения триггеров "before". Используйте триггер, если вы хотите выполнить проверку и присвоить данным допустимые значения. В определении таблицы это ограничение на уровне таблицы. В отличие от ограниче- ний check, применимых к определению домена, его элемент value является ссылкой на столбец. Например, для домена предложение check может быть следующим: CHECK (VALUE > 10) В определении таблицы то же самое условие для столбца с именем acolumn будет представлено как: CHECK (ACOLUMN > 10) Ограничение check будет активным для операций insert и update. Хотя это ограниче- ние на уровне таблицы, его область действия может выходить за пределы уровня столбца на уровень строки и, хотя это не рекомендуется, на уровень таблицы и даже за пределы таблицы. Ограничение check гарантирует целостность данных, только если значения, включенные в проверку, находятся в той же самой строке, что и проверяемое значение. Л ВНИМАНИЕ! Вы не должны использовать выражения, которые сравнивают зна- чение со значениями из других строк этой же таблицы или других таблиц, потому что любая строка, отличная от текущей, может находиться в процессе изменения или удаления в другой транзакции. В особенности не полагайтесь на ограничение check для проверки ссылочных отношений! Условие поиска может: ♦ проверять, что вводимое значение находится в указанном диапазоне; ♦ проверять, что значение присутствует в списке допустимых значений;
Глава 16. Таблицы 315 ♦ сравнивать значение с константой, выражением или со значением данных другого столбца из этой же строки. Замечания по использованию CHECK Существуют определенные условия использования ограничений check. ♦ Столбец может иметь только одно ограничение check, хотя его логика может быть представлена как сложное условие поиска — одно ограничение, много условий. ♦ Ограничение check для столбца, основанного на домене, не может перекрывать наследуемую от домена проверку. Определение столбца может использовать обычное предложение check, чтобы добавить дополнительную логику ограниче- ния в наследуемое ограничение. Оно будет соединено с наследуемым ограниче- нием конъюнкцией (логической операцией И). ♦ Ограничение check не может ссылаться на домен. Синтаксис ограничения check: CHECK (<условие-поиска>); <условие-поиска> = < val> <operator> {<val> | {<выбор-одного>'1 } | <val> [NOT] BETWEEN <val> AND <val> I <val> [NOT] LIKE <val> [ESCAPE <val>] I <val> [NOT] IN (<val> [, <val> ...] | <список-выбора>) | <val> IS [NOT] NULL I <val> { [NOT] { = I < I > } | >= I <= ) { ALL | SOME | ANY } (<список-выбора>} I EXISTS (<выражение-выбора>) I SINGULAR (<выражение-выбора>) I <val> [NOT] CONTAINING <val> [ <val> [NOT] STARTING [WITH] <val> I (<yсловно-поиска>) I NOT <условие-поиска> | <условие-поиска> OR <условие-поиска> I <условие-поиска> AND <условие-поиска> Диапазон возможностей при определении ограничений check действительно весьма широк — теоретически в нем может быть использовано почти любое условие поиска. Для разработчика важно выбрать разумные и безопасные условия, т. к. они действу- ют для каждой операции insert и update для таблицы. В части V данной книги описывается синтаксис установки различных стилей усло- вия поиска. Пример. Следующее ограничение проверяет значения двух столбцов для гарантии, что одно больше другого. Хотя оно также предполагает условия not null для обоих столбцов — проверка будет ошибочной, если хотя бы один столбец будет иметь пус- тое значение, — оно не устанавливает ограничение not null для столбца: CHECK (COL_1 > COL_2);
316 Часть IV. База данных и ее объекты Проверка даст ошибку, если арифметическая проверка будет ошибочной или если любой из столбцов соь_1 или соь_2 имеет значение null. Следующая операция будет успешной: INSERT INTO TABLE_1 (COL__1, COL_2) VALUES (6,5); Ограничения UNIQUE Ограничение unique, как и ограничение первичного ключа, гарантирует, что никакие две строки не будут иметь то же значение указанного столбца или группы столбцов. Вы можете объявить для таблицы более одного ограничения unique, но оно не может использовать тот же набор столбцов, который был использован для ограничения PRIMARY KEY ИЛИ ДруГОГО UNIQUE. Ограничение unique фактически создает уникальный ключ, который виртуально имеет те же возможности, что и первичный ключ. Он может быть выбран как управляющий ключ для ограничения ссылочной целостности. Это делает его полезным в ситуациях, когда вы определяете небольшой суррогатный первичный ключ для его атомарности и улучшения производительности в соединениях и операциях поиска, но при этом хотите сохранить режим формирования альтернативной связи внешнего ключа с уникальным ключом для нерегулярного использования. В Firebird 1.0.x атрибут not null должен быть применен для всех столбцов, с кото- рыми оперирует ограничение unique. Как и ограничение primary key, unique создает свой постоянный уникальный индекс для поддержания его правил. Правила именования ограничения и индекса соответст- вует тем же правилам поведения, применимым к другим ключам. Следующий при- мер isql иллюстрирует именование в Firebird 1.5: SQL> CREATE TABLE TEST_UQ ( CON> ID BIGINT NOT NULL, CON> DATA VARCHAR(10), CON> DATAJED BIGINT NOT NULL); SQL> COMMIT; SQL> ALTER TABLE TEST_UQ CON>ADD CONSTRAINT PK_TEST_UQ PRIMARY KEY(ID), CON>ADD CONSTRAINT UQ1_DATA UNIQUE(DATA_ID) ; SQL> COMMIT; SQL> SHOW TABLE TEST_UQ; ID BIGINT NOT NULL DATA VARCHAR(10) NULLABLE DATA_ID BIGINT NOT NULL CONSTRAINT PK_TESTJJQ: Primary key (ID) CONSTRAINT UQ1_DATA: Unique key (DATA_ID) SQL> SHOW INDICES TEST_UQ;
Глава 16. Таблицы 317 PK_TEST_UQ UNIQUE INDEX ON TEST_UQ(ID) UQ1JDATA UNIQUE INDEX ON TEST_UQ(DATA_ID) SQL> < ПРИМЕЧАНИЕ. Запомните следующее заклинание: индекс не является ключом. Вы можете создавать уникальные индексы (подробности см. в главе 18), но соз- X дание уникального индекса не создает уникального ключа. Если существует ве- роятность, что вам может понадобиться для использования уникально индекси- рованный столбец или структура в качестве ключа, создайте вместо индекса ог- раничение. Использование внешних файлов в качестве таблиц В текущем жаргоне SQL Firebird поддерживает внешние виртуальные таблицы (External Virtual Table, EVT). Файлы файловой системы в текстовом формате ASCII могут быть использованы в Firebird для чтения и манипулирования, как если бы они были таблицами, хотя и со значительными ограничениями, связанными с тем фактом, что они не являются внутренними объектами базы данных. Другие приложения мо- гут обмениваться данными с базой данных Firebird, независимо от любых специаль- ных механизмов преобразования. Внешние таблицы могут быть конвертированы во внутренние. Предложение external file позволяет определять таблицы со структурой строки, отображаемой в "поля" фиксированной длины в "записях" (обычно разделенных сим- волом перевода строки), которые размещаются во внешнем файле. Firebird может выбирать данные из файла и помещать данные в файл, как если бы это было обычной таблицей. При этом он не может выполнять операции изменения и удаления с внеш- ними таблицами. Текстовый файл, содержащий эти данные, должен быть создан или скопирован на устройство хранения, физически находящееся под управлением сервера — как обыч- но, это не должно быть устройство NFS, совместно используемый диск или отобра- жаемый диск. Совместный доступ Firebird и других приложений на уровне файла невозможен. Firebird требует исключительного доступа в течение всего времени, ко- гда файл открыт в транзакции. В остальное время этот файл может изменяться дру- гими приложениями. Синтаксис для CREATE TABLE...EXTERNAL FILE Оператор create table для внешнего файла определяет как спецификацию внешнего файла (размещение и имя файла), так и характеристики столбцов Firebird, представ- ляющих структуру хранимых записей.
318 Часть IV. База данных и ее объекты CREATE TABLE внешняя-таблица EXTERNAL FILE спецификация-файла (определение-столбца [, определение-столбца, . ..], [разделитель-строки-1 CHAR(l) [,разделитель-строки-2 CHAR(l)]]); спецификация-файла — полный путь и имя файла для внешнего файла данных. Файл не должен существовать во время создания таблицы. При этом в Firebird 1.5 и выше оператор create будет неудачным, если спецификация ссылается на несконфигуриро- ванное размещение внешнего файла. См. раздел "Организация защиты внешних фай- лов" далее в этой главе и разд. "Конфигурирование внешних размещений" главы 36. определение-столбца— обычное определение столбца Firebird. Могут быть заданы не символьные типы данных; при этом обеспечивается неявное преобразование в этот тип каждой строки, выделяемой из местоположения столбца во внешней записи. разделитель-строки— необязательный последний столбец или пара столбцов, кото- рые могут быть определены для использования в системе файла в качестве отметки конца строки текста. Хотя наличие таких разделителей делает более удобным чтение файла человеком, это не является обязательным для записей фиксированной длины, если только собирающиеся читать эти данные программы не требуют таких раздели- телей. ♦ В Linux/UNIX это один символ перевода строки ASCII 10. ♦ В Windows это упорядоченная пара ASCII 13 (возврат каретки) и ASCII 10. ♦ В Mac OS это ASCII 10 и ASCII 13. ♦ Другие операционные системы могут использовать другой порядок или другие символы. Ограничения и рекомендации Организация защиты внешних файлов Во всех версиях Firebird список каталогов должен быть сконфигурирован для огра- ничения размещений, где Firebird будет отыскивать или создавать внешние файлы. Параметры конфигурации ExternairiieAccess в firebird.conf (для серверов версии 1.5) ИЛИ external_file_directory В ibconfig/isc_config (ДЛЯ версии 1.0.x) СМ. В главе 3. По умолчанию Firebird 1.5 инсталлируется без доступа к внешним файлам, в то время как версия 1.0.x дает открытый доступ к любому файлу файловой системы. Jg ПРИМЕЧАНИЕ. Метод конфигурирования и возможности защиты системы от злоумышленных атак через доступ к внешним файлам различаются в разных _N версиях сервера. Формат внешних данных Firebird сам создаст внешний файл, если не найдет его по месту, указанному в специ- фикации create EXTERNAL TABLE ' Спецификация-файла>'. ЕСЛИ файл уже существует, ТО
Глава 16. Таблицы 319 каждая его запись должна быть фиксированной длины, состоять из полей фиксиро- ванной длины, которая должна в точности соответствовать длине в байтах специфи- кациям столбцов в определении таблицы. Если приложение, создавшее файл, исполь- зует жесткие символы перевода строки (например, двухбайтовую последователь- ность возврат каретки и перевод строки в текстовых файлах Windows), включите в описание столбец для приспособления к этой последовательности. См. далее разд. "Символы конца строки". Данные blob и массивы не могут быть считаны или записаны во внешний файл. Большинство правильно сформированных числовых данных могут читаться непо- средственно из внешней таблицы и в большинстве случаев Firebird в состоянии ис- пользовать его внутренние правила преобразования для правильной их интерпрета- ции. При этом может оказаться более простым и точным чтение чисел в символьные столбцы и последующее их преобразование с использованием функции casto. СОВЕТ. Убедитесь, что вы выделили достаточный размер для размещения ва- ших данных. Для информации о размерах обратитесь к соответствующей главе в части III. Рисунок 8.1 описывает правила преобразования типов данных. CHAR в сравнении с VARCHAR Использование varchar в определении столбца внешнего строкового поля не реко- мендуется, потому что он не является легко переносимым форматом: <2-байтовое беззнаковое короткоехстрока символьных байтов> varchar требует начального 2-байтового беззнакового числа для включения количест- ва байтов строки, непосредственно за которым следует строка4. Для многих внешних приложений может оказаться трудным или невозможным получить доступ к данным. По этой причине используйте char вместо varchar для строковых полей и убедитесь, что приложение переводит эту строку в полный размер. Символы конца строки Когда вы создаете таблицу, которая будет использована для импорта внешних дан- ных, вы должны определить столбец, содержащий символ конца строки (End-Of-Line, EOL) или новой строки, если приложение, создающее этот файл, включает такой символ. Размер столбца должен быть достаточным, чтобы хранить конкретный сис- темный символ EOL (обычно 1 или 2 байта). Для большинства версий UNIX это 1 байт. Для Windows и Macintosh — 2 байта. Советы по добавлению непечатаемых символов При добавлении данных во внешний файл внешняя функция ascii_char (десятичный- код-ASCII) из библиотеки функций ib_udf может быть использована для передачи в 4 Здесь мы говорим об архитектуре Intel. Выравнивание может отличаться в некоторых ар- хитектурах.
320 Часть IV. База данных и ее объекты оператор SQL непечатаемых символов в виде выражения для столбцов разделителей строк. Например, следующий оператор добавляет символы возврата каретки и пере- вода строки в столбец: INSERT INTO MY_EXT_TABLE ( СТОЛБЦЫ. . ., CRLF) VALUES ( Значение-столбца. . ., ASCII_CHAR(13) || ASCII_CHAR(10)) ; Альтернативой этому является создание таблицы специально для хранения непеча- таемых символов, которые могут понадобиться вашим приложениям. Просто создай- те текстовый файл на той же платформе, что и ваш сервер, используя редактор, кото- рый "отображает" непечатаемые символы. Откройте вашу "непечатаемую" таблицу, используя интерактивный инструмент, скопируйте и вставьте символы непосредст- венно в таблицу. Для операторов, выполняющих добавление во внешний файл, сим- вол может быть получен с помощью подзапроса из таблицы5. Операции Только операции insert и select могут быть выполнены над строками внешней таб- лицы. Попытки изменить или удалить строки вернут ошибки. Поскольку такие данные располагаются вне базы данных, операции с внешней таб- лицей не находятся под управлением версиями записей сервера Firebird. Поэтому добавления имеют немедленный эффект и не могут быть отменены (rolled back). СОВЕТ. Если вы хотите, чтобы ваша таблица находилась под управлением транзакции, создайте другую, внутреннюю таблицу Firebird и добавьте данные из внешней таблицы во внутреннюю. Если вы используете drop database для удаления базы данных, вы должны также удалить внешний файл — он не будет автоматически удален как результат выполне- ния DROP DATABASE. Импорт внешних файлов в таблицы Firebird Для импорта внешних файлов в таблицы Firebird вначале убедитесь, что у вас уста- новлены соответствующие условия доступа. См. разд. "Конфигурирование внешних размещений"главы 36 относительно параметра сервера ExternaiFiieAccess. 5 Создание спецификаций для чтения/записи во внешний файл может потребовать гораздо больше детальных знаний о формате, чем здесь обсуждается. Наборы символов и размеры в байтах могут оказаться проблемой. Во многих случаях использование в качестве набора сим- волов нейтрального набора символов OCTETS в определении внешней таблицы может решить некоторые проблемы. Импорт и экспорт данных не подчиняется принципу "один размер под- ходит всем".
Глава 16. Таблицы 321 I. Создайте таблицу Firebird, которая позволит вам просматривать внешние данные. Объявите все столбцы как char. Текстовый файл, содержащий данные, должен на- ходиться на сервере. В следующем примере внешний файл существует в системе UNIX, следовательно, символ EOL занимает 1 байт. CREATE TABLE EXT_TBL EXTERNAL FILE 'file.txt' ( FNAME CHAR (10) , LNAME CHAR(20), HDATE CHAR(10), NEWLINE CHAR(l)); COMMIT; 2. Создайте другую таблицу Firebird, которая в итоге будет вашей рабочей таблицей. Включите столбец для символа EOL, если вы позже собираетесь экспортировать данные из внутренней таблицы назад во внешний файл: CREATE TABLE PERSONNEL ( FIRST_NAME VARCHAR(10), LAST_NAME VARCHAR(20), HIREJ3ATE DATE, NEW-LINE CHAR(l)); COMMIT; 3. Используя текстовый редактор или приложение, которое может выводить текст фиксированного формата, создайте и заполните внешний файл. Сделайте все записи одинаковой длины, заполняя неиспользуемые символы пробелами, и до- бавьте символ(ы) EOL в конец каждой записи. Количество символов в строке EOL зависит от платформы — см. предыдущие за- мечания. Следующий пример иллюстрирует запись фиксированной длины в 41 символ, ьпредставляет пробел, ап — EOL: 12345678901234567890123456789012345678901 fname.....Iname...............hdate....п JamesbbbbbStarkeybbbbbbbbbbbbb2004-12-10n ClaudiobbbValde гramabbbbbbbbbbl00 3-10-0In 4. Оператор select для таблицы ext_tlb возвращает записи из внешнего файла: SELECT FNAME, LNAME, HDATE FROM EXT_TBL; FNAME . LNAME HDATE James Starkey 2004-12-10 Claudio Valderrama 2003-10-01 5. Добавьте данные в таблицу назначения: INSERT INTO PERSONNEL SELECT FNAME, LNAME, CAST(HDATE AS DATE), Il Зак. 420
322 Часть IV. База данных и ее объекты NEWLINE FROM EXTJTBL; COMMIT; (Лг ПРИМЕЧАНИЕ. Если вы пытаетесь обратиться к файлу, в то время как он еще открыт другим приложением, эта попытка даст сбой. Обратное также верно, и \ X более того: когда ваше приложение откроет файл как таблицу, он будет недосту- пен для других приложений, пока ваше приложение не отсоединится от базы данных6. Теперь, когда вы выполните select для personnel, данные из вашей внешней таблицы появятся в конвертированной форме: select first_name, last_name, hire_date FROM PERSONNEL; FIRST_NAME LAST_NAME HIRE_DATE James Starkey 10-DEC-2004 Claudio Valderrama 01-OCT-2003 Экспорт таблиц Firebird во внешние файлы Вернемся к примеру в предыдущем разделе. Шаги по экспорту данных в нашу внеш- нюю таблицу похожи: 1. Откройте внешний файл в текстовом редакторе и удалите в нем все. Выйдите из текстового редактора и снова выполните запрос select к таблице ext_tbl. Она должна быть пустой. 2. Используйте оператор insert для копирования записей Firebird из personnel во внешний файл file.txt: insert into extjtbl select first_name, last_name, cast (HIRE_DATE AS VARCHAR(ll), ASCII_CHAR (10) FROM PERSONNEL WHERE FIRST_NAME LIKE 'Clau%'; 3. Теперь выберите данные из внешней таблицы: SELECT FNAME, LNAME, HDATE FROM EXTJTBL; FNAME LNAME HDATE Claudio Valderrama 01-OCT-2004 6 Внешний файл должен быть освобожден, когда завершатся выполняющиеся с ним опера- ции. Для сервера некорректно оставлять его заблокированным, поскольку внешние данные не менялись. Это программная ошибка, по поводу которой читатель может запросить исправле-
Глава 16. Таблицы 323 СОВЕТ. Внешняя функция ascii_char находится в библиотеке ib_udf в каталоге /UDF каталога инсталляции Firebird. Ее объявление может быть найдено в скрип- те ib_udf.sql. Об использовании внешних функций см. главу 21. Конвертирование внешних таблиц во внутренние Можно конвертировать данные из внешних таблиц во внутреннюю таблицу посред- ством выполнения резервного копирования базы данных с помощью утилиты gbak с переключателем -convert (сокращенно -со). Все внешние таблицы, определенные в базе данных, будут конвертированы во внутренние таблицы при изготовлении ре- зервной копии. В этом случае размещение внешней таблицы не будет сохранено. Подробную информацию см. в главе 38. Изменение таблиц Оператор alter table используется для изменения структуры таблицы: добавления, изменения или удаления столбцов или ограничений. При необходимости один опера- тор может выполнять несколько изменений. Для выполнения alter table вы должны быть соединены с базой данных как создатель таблицы (ее владелец), пользователь SYSDBA или (в POSIX) как Суперпользователь. Изменение таблицы или ее триггеров подсчитывается в специальном счетчике. Каж- дая таблица может изменяться не более 255 раз, после чего вы должны будете вы- полнить копирование и восстановление базы данных. При этом счетчик изменений не влияет на переключение триггера в активное или неактивное состояние, как при использовании ALTER TRIGGER имя-триггера ACTIVE I INACTIVE СОВЕТ. Запланируйте выполнение резервного копирования и восстановления после изменений структур таблиц, если база данных содержит данные. Когда изменяется таблица или столбец, Firebird не выполняет преобразования изме- ненного формата. Для упрощения оперативного изменения метаданных он со- храняет новое описание формата и откладывает преобразование, пока данные нужны. Это могло бы оказать непредвиденное влияние на работу пользователей. Подготовка к выполнению ALTER TABLE Перед модификацией или удалением столбцов или атрибутов в таблице вам нужно выполнить три дела: I. Убедитесь, что вы имеете соответствующие привилегии к базе данных. 2. Сохраните существующие данные. 3. Удалите любые ограничения зависимостей в столбце.
324 Часть IV. База данных и ее объекты Изменение столбцов в таблице Существующие столбцы в таблице могут быть изменены в нескольких отношениях, а именно: ♦ имя столбца может быть изменено на другое имя, не используемое в таблице; ♦ столбец может быть "перенесен" на другую позицию в системе упорядочивания столбцов слева направо; ♦ возможно преобразование несимвольных данных в символьные с некоторыми ограничениями. Синтаксис Используйте следующий синтаксис для alter table: ALTER TABLE таблица ALTER [COLUMN] имя-простого-столбца изменение; изменение = новое-имя-столбца | новый-тип-столбца | новая-позиция-столбца новое-имя-столбца = ТО имя-простого-столбца новый-тип-столбца = TYPE тип-данных-или-домен новая-позиция-столбца = POSITION целое Jij ПРИМЕЧАНИЕ. Если вы пытаетесь переименовать столбец, вы можете неожи- »—данно получить проблемы зависимостей, если на столбец существуют ссылки из \ X ограничений или он используется в просмотрах, триггерах или хранимых про- цедурах. Примеры Здесь мы изменяем имя столбца с emp_no на emp_num: ALTER TABLE EMPLOYEE ALTER COLUMN EMP_NO TO EMP_NUM; /+ ключевое слово COLUMN необязательно */ Теперь изменяется позиция столбца: ALTER TABLE EMPLOYEE ALTER COLUMN EMP_NUM POSITION 4; В этот раз тип данных emp_num заменяется с integer на varchar <20): ALTER TABLE EMPLOYEE ALTER COLUMN EMP_NUM TYPE VARCHAR(20); Ограничения при изменении типа данных Firebird не позволит вам изменить тип данных столбца или домена, в результате чего могут потеряться данные.
Глава 16. Таблицы 325 ♦ Новое определение столбца должно позволять использовать существующие дан- ные. Если, например, новый тип данных имеет слишком много байтов, или не поддерживается преобразование типов данных, то возвращается ошибка, и изме- нения не выполняются. ♦ Когда числовые типы преобразуются в строковый тип, каждый числовой тип рас- сматривается как предмет с минимальной длиной в байтах в соответствии с типом (см. рис. 8.1). ♦ Преобразование символьных данных в несимвольные недопустимо. ♦ Столбцы массивов и blob не могут быть преобразованы. ВНИМАНИЕ! Любые изменения определения полей могут потребовать пересоз- дания индексов. Удаление столбцов Владелец таблицы может использовать alter table для удаления определения столб- ца и его данных из таблицы. Удаление столбца приводит к потере всех хранимых в нем данных. Удаление приводит к немедленному эффекту, независимо от других транзакций, работающих с таблицей. В этом случае другая транзакция продолжается без прерывания, a Firebird откладывает удаление до освобождения таблицы. До удаления столбца учтите условия, при которых невозможно будет выполнить уда- ление. Удаление будет ошибочным, если столбец: ♦ является частью ограничения unique, primary key или foreign key; ♦ включен в ограничение check (это могут быть ограничения check на уровне табли- цы для основанного на домене столбца в дополнение к ограничению его домена); ♦ используется в просмотре, триггере или хранимой процедуре. Зависимости должны быть удалены до удаления столбца. Столбцы, включенные в состав ограничений primary key и unique, не могут быть удалены, если на них есть ссылки в ограничениях foreign key. В этом случае удалите ограничение foreign key до удаления ограничения primary key или unique. После этого удаляйте столбец. Синтаксис: ALTER TABLE имя-таблицы DROP имя-столбца [, имя-столбца ...]; Например, следующий оператор удаляет столбец job_grade из таблицы employee: ALTER TABLE EMPLOYEE DROP JOB_GRADE; Удаление нескольких столбцов в одном операторе: ALTER TABLE EMPLOYEE DROP JOB_GRADE, DROP FULL_NAME;
326 Часть IV. База данных и ее объекты Удаление ограничений Необходимо выполнять удаление ограничений в правильной последовательности, если ограничения primary key и check имеют зависимости. СОВЕТ. Для поиска имен ограничений может оказаться полезным выполнение четырех системных просмотров, определенных в скрипте system_views.sql, пред- ставленном в приложении 9. Ограничения UNIQUE KEY и PRIMARY KEY При удалении ограничений первичного и уникального ключа необходимо вначале найти и удалить все ссылающиеся на них ограничения внешних ключей. Если речь идет об уникальном ключе, то объявление внешнего ключа перечисляет имена столбцов уникального ключа, например: FK_DATA_ID FOREIGN KEY DATA_ID REFERENCES TEST_UQ (DATA__ID) ; Если ключ, на который имеются ссылки, является первичным ключом, то имя столб- ца первичного ключа является необязательным в объявлениях внешнего ключа и час- то опускается. Например, посмотрите на базу данных ../samples/employee.gdb: ...TABLE PROJECT ( TEAM_CONSTRT FOREIGN KEY (TEAM_LEADER) REFERENCES EMPLOYEE ); Удаление ограничения внешнего ключа простое: ALTER TABLE PROJECT DROP CONSTRAINT TEAM_CONSTRT; COMMIT; После этого становится возможным удаление ограничения первичного ключа для етолбца emp_no таблицы employee: ALTER TABLE EMPLOYEE DROP CONSTRAINT EMP_NO_CONSTRT ; Ограничения CHECK Любые условия check, которые были добавлены в процессе определения таблицы, могут быть удалены без каких-либо осложнений. Условия check, наследуемые от до- мена, являются более проблематичными. Для освобождения от ограничений домена необходимо выполнить операцию alter table alter column ... type для изменения типа данных столбца или указания другого домена.
Глава 16. Таблицы 327 Добавление столбца Один или более столбцов можно добавить в таблицу в одном операторе при исполь- зовании предложения add. Каждое предложение add включает полное определение столбца. Используется тот же самый синтаксис, что и при определении столбца в операторе create table. Предложения add отделяются друг от друга запятыми. Синтаксис: ALTER TABLE таблица ADD <определение-столбца> <определение-столбца> = столбец {<тип-данных> I [COMPUTED [BY] (<выражение>) | домен] [DEFAULT {литерал [NULL [USER}] [NOT NULL] [<ограничение~столбца>] [COLLATE порядок-сортировки] <ограничение-столбца> = [CONSTRAINT ограничение] Определение - огра ничения> [ < огра ничение-столбца > ] < определение - ограничения>= PRIMARY KEY I UNIQUE I CHECK (<условие-поиска>] I REFERENCES другая-таблица [{другой-столбец [,другой-столбец...])] [ON DELETE {NO ACTION|CASCADE|SET DEFAULT|SET NULL}] [ON UPDATE {NO ACTION|CASCADE|SET DEFAULT|SET NULL}] Следующий оператор добавляет столбец emp_no в таблицу employee с использованием домена empno: ALTER TABLE EMPLOYEE ADD EMP_NO EMPNO NOT NULL; Пример Здесь мы добавляем два столбца email_id и leave_status в таблицу employee: ALTER TABLE EMPLOYEE ADD EMAIL_ID VARCHAR(10) NOT NULL, ADD LEAVE_STATUS DEFAULT 10 INTEGER NOT NULL; Включение ограничений целостности Ограничения целостности могут быть включены в столбцы, которые вы добавляете в таблицу. Например, ограничение unique может быть включено в столбец email_id в предыдущем примере: ALTER TABLE EMPLOYEE ADD EMAIL_ID VARCHAR(10) NOT NULL, ADD LEAVE_STATUS DEFAULT 10 INTEGER NOT NULL, ADD CONSTRAINT UQ_EMAIL_ID UNIQUE(EMAIL_ID);
328 Часть IV. База данных и ее объекты или ALTER TABLE EMPLOYEE ADD EMAIL_ID VARCHAR(IO) NOT NULL UNIQUE, ADD LEAVE_STATUS DEFAULT 10 INTEGER NOT NULL; Добавление новых ограничений таблицы Предложение add constraint может быть использовано для добавления ограничений на уровне таблицы для нового или существующего столбца. Синтаксис: ALTER TABLE имя ADD [CONSTRAINT ограничение] <ограничение~таблицЫ>; где ограничение-таблицы— может быть ограничением primary key, foreign key, unique или check. Фраза constraint ограничение может быть опущена, если вам не нужно имя ограничения. Пример Для добавления ограничения unique в таблицу employee вы можете использовать сле- дующий оператор: ALTER TABLE EMPLOYEE ADD CONSTRAINT UQ_PHONE_EXT UNIQUE(PHONE_EXT); Когда недостаточно ALTER TABLE Иногда бывает нужным произвести изменение столбца, которое нельзя совершить с помощью alter table. Примером может быть случай, когда столбец, хранящий меж- дународные элементы языка, имеет набор символов none, который нужно заменить на другой набор символов, чтобы исправить ошибку проектирования, или телефонный номер, определенный вначале кем-то как целое, нужно заменить на 18-символьный столбец. В первом случае невозможно изменить набор символов для столбца, следовательно, вам нужно средство, чтобы сохранить данные и сделать их доступными в правильном наборе символов. Во втором случае простое изменение типа данных столбца с теле- фонным номером не будет работать, если у нас уже существуют целые числа в этом столбце. Мы хотим сохранить существующие числа, но нам нужно преобразовать их в строки. Это не может быть выполнено в существующей структуре, потому что столбец с целым типом данных не может хранить строки. Прием заключается в создании в таблице временного столбца с правильными атри- бутами и переносе туда данных, после чего вы удалите и пересоздадите нужный столбец. 1. Добавьте в таблицу временный столбец с теми атрибутами, которые вам нужны. alter table personnel ADD TEMP_COL VARCHAR (18); COMMIT;
Глава 16. Таблицы 329 2. Скопируйте данные из столбца, который вы будете изменять, во временный стол- бец, преобразовывая их соответствующим образом (например, изменяя набор символов для конвертирования текстовых данных в правильный набор символов или, как в нашем примере, выполняя преобразование). UPDATE PERSONNEL SET TEMP-COL = CAST (TEL_NUMBER AS VARCHAR (18)) WHERE TEL_NUMBER IS NOT NULL; COMMIT; 3. После проверки того, что данные во временном столбце были изменены, как пла- нировалось, удалите старый столбец. ALTER TABLE PERSONNEL DROP TEL_NUMBER; 4. Создайте "новый" столбец, с тем же именем, как и тот, который вы только что удалили, и с теми же атрибутами, что и у временного столбца. ALTER TABLE PERSONNEL ADD TEL_NUMBER VARCHAR (18); 5. Скопируйте данные во вновь созданный столбец. UPDATE PERSONNEL SET TEL_NUMBER = TEMP_COL WHERE TEMP—COL IS NOT NULL; COMMIT; 6. После проверки того, что данные во вновь созданном столбце правильные, удали- те временный столбец. Если хотите, вы можете также переместить пересозданный столбец на его старую позицию. ALTER TABLE PERSONNEL DROP COLUMN TEMP_COL, ALTER TEL__NUMBER POSITION 6; COMMIT; Удаление таблицы DROP TABLE Используйте оператор drop table для удаления таблицы и ее данных из базы данных. Это полное удаление, оно не может быть отменено после подтверждения транзакции. DROP TABLE имя; Следующий оператор удаляет таблицу personnel: DROP TABLE PERSONNEL;
330 Часть IV. База данных и ее объекты RECREATE TABLE Иногда бывает нужно удалить таблицу и снова создать ее "с нуля". Для таких случаев Firebird имеет оператор recreate table, который делает следующее: ♦ удаляет существующую таблицу и все принадлежащие ей объекты; ♦ подтверждает изменения; ♦ создает новую таблицу в соответствии с указанной спецификацией. Синтаксис Синтаксис recreate table идентичен синтаксису оператора create table. Просто за- мените ключевое слово create на recreate. /\ ВНИМАНИЕ! Убедитесь, что вы сохранили исходные тексты триггеров, ключей и / ! \ индексов этой таблицы до выполнения запроса recreate table! Ограничения и рекомендации Если при выполнении оператора drop или recreate таблица находится в использова- нии, запрос не будет выполнен, появится сообщение "Object ххххх is in use" (Объект ххххх используется). Всегда выполняйте резервное копирование перед любыми действиями, изменяющи- ми метаданные. Хотя возможно изменение метаданных при наличии соединенных пользователей, этого делать не рекомендуется, особенно в случаях радикальных изменений типа удаления или пересоздания таблиц. Если нужно, отключите пользователей и получи- те исключительный доступ. Инструкции по получению исключительного доступа см. в главе 39. Временные таблицы Firebird не поддерживает временные таблицы, которые управляются системой. Здесь они меньше нужны, чем в других СУБД. Например, у Firebird есть возможность по- лучать виртуальные таблицы напрямую через хранимую процедуру, написанную с использованием специального синтаксиса. Более подробно об этом см. разд. "Храни- мые процедуры выбора" в главах 29 и 30. Постоянные "временные" таблицы Популярная модель хранения временных данных для доступа приложений — опре- делить постоянную структуру данных, которая включает "идентификатор сессии" или "идентификатор пакета", получающие значение от генератора, или, в Firebird 1.5,
Глава 16. Таблицы 331 значение current_transaction. Приложения могут добавлять, обрабатывать и удалять строки из такой таблицы в процессе решения задачи. Запомните, что Firebird не бло- кирует таблицы в нормальных ситуациях. В соответствии с условиями и потребностями приложения оно само будет ответст- венным за удаление временных строк по окончании сессии при использовании иден- тификатора сессии для удаления "своих" строк. Альтернативно приложение может послать в служебную таблицу строку, сигнализирующую "требуется чистка", для более поздней отложенной операции, запускаемой перед резервным копированием. СОВЕТ. Временные таблицы, скорее всего, появятся в следующем релизе Firebird. Пора дальше Одной из важных особенностей реляционной СУБД является ее возможность под- держивать отношения между группами постоянных данных, хранимых в таблицах. Далее мы рассмотрим, как Firebird реализует правила по защите ссылочной целост- ности в этих межтабличных отношениях.
ГЛАВА 1 7 Ссылочная целостность данных Термин ссылочная целостность относится к возможности базы данных защищать себя от получения входных данных, результатом которых может стать нарушение отношений. А именно ссылочная целостность базы данных существует в соответст- вии с ее способностью осуществлять и защищать отношение между двумя табли- цами. Реализация формальных ограничений целостности добавляет некоторую дополни- тельную работу разработчикам баз данных — так какова же окупаемость? Если вы новичок в этой концепции, то вы, безусловно, должны найти множество причин для оправдания дополнительного времени и внимания. ♦ Бомбоубежище: формальные ссылочные ограничения — особенно при разумном использовании других ограничений — станут надежным бомбоубежищем бизнес- правил вашей базы данных от ошибок приложений, независимо от их источника. Это будет в особенности важным, когда вы начнете устанавливать ваши системы на сайты, где неквалифицированный или частично квалифицированный персонал будет получать доступ к базе данных через утилиты сторонних организаций. ♦ Скорость запросов: индексы, автоматически созданные для ограничений ссылоч- ной целостности, увеличат скорость операций соединения (join)'. ♦ Качество управления: в процессе разработки и тестирования потенциальные ошибки имеют тенденцию проявляться раньше, потому что база данных отменяет операции, которые нарушают правила. Они эффективно уменьшают неприятности в разработке приложения при ошибочных предположениях о согласованности данных. ♦ Документированность: правила целостности, установленные в вашей базе дан- ных, дают "свободную" документацию, которая уменьшает потребность в любой описательной документации, помимо скриптов схемы. Правила, корректно опре- деленные в метаданных, становятся надежным справочником по модели данных для новых групп и будущей разработки. 1 При этом существует одно исключение. Индекс внешнего ключа с очень низкой селек- тивностью — высочайший уровень дублирования небольшого количества значений в табли- це — может оказать серьезное влияние на производительность, если его ключевые столбцы формируют условие соединения.
Глава 17. Ссылочная целостность данных 333 Терминология Если реляционная СУБД позволяет объявлять отношение между двумя таблицами, иногда это называется декларативной ссылочной целостностью — туманный тер- мин, который, похоже, распространялся писателями журнальных статей. Ссылочная целостность является целью проектирования, уровнем его качества. Автор предпочи- тает термин формальные ссылочные ограничения, когда обращается к механизмам реализации таких правил. В системе управления реляционными базами данных (реляционные СУБД) отноше- ние между двумя таблицами создается посредством ограничения внешнего ключа. Ограничение внешнего ключа реализует правила существования строк, защищая таб- лицу от попыток добавлять строки, несовместимые с моделью данных. Однако такое ограничение не работает в одиночестве. Другие ограничения целостности (подробно описанные в главе 16) могут работать в комбинации с ссылочными ограничениями для поддержания непротиворечивости отношений. Ограничение FOREIGN KEY Внешний ключ — это столбец или набор столбцов в одной таблице, которые в точно- сти соответствуют столбцу или набору столбцов, определенных как ограничение primary key или unique в другой таблице. В своей простейшей форме внешний ключ реализует необязательное отношение один-ко-многим. ПРИМЕЧАНИЕ. Необязательное отношение существует, когда отношение воз- можно в формальной структуре, но не является необходимым. То есть родитель- \ ский экземпляр может существовать без каких-либо ссылок на него со стороны дочернего элемента, но, если оба существуют, оба подчиняются ограничениям. В противоположность этому существуют обязательные отношения. Обязатель- ные отношения обсуждаются позже в этой главе. Стандартная модель объект-отношение описывает простое отношение один-ко- многим, между двумя сущностями, как показано на рис. 17.1. Рис. 17.1. Модель объект-отношение Если мы реализуем такую модель между двумя таблицами parent и child, то строки в таблице child зависят от существования связанной строки из parent. Ограничение foreign key в Firebird осуществляет это отношение следующими способами: ♦ требуется, чтобы значение столбца внешнего ключа в таблице child (child.parent id) могло быть связано с соответствующим значением уникального ключа (в нашем случае, первичного ключа) в таблице parent (parent, id);
334 Часть IV. База данных и ее объекты ♦ по умолчанию запрещено удаление строки parent или изменение значения уни- кального ключа, если существуют зависимые строки в child; ♦ должно быть реализовано отношение, которое предполагалось во время создания ссылки или когда оно в последний раз изменялось2; ♦ по умолчанию допускается пустое значение столбца внешнего ключа. Поскольку невозможно связать пустое значение с чем бы то ни было, такие строки являются зависшими — они не имеют родителя. Реализация ограничения При реализации ссылочного ограничения должны быть учтены некоторые предвари- тельные условия. В этом разделе мы рассмотрим очень простой пример. Если вы вы- полняете разработку в существующем сложном окружении, где действуют привиле- гии SQL, то вам следует позаботиться о получении привилегии references. Об этом сказано в отдельном разделе далее в этой главе. Родительская структура Необходимо начать с родительской таблицы и создать управляющий уникальный ключ, на который будет ссылаться зависимая таблица. Обычно это первичный ключ родительской таблицы, хотя это не обязательно. Внешний ключ может ссылаться на столбец или группу столбцов, объединенных ограничением unique. Для целей иллю- страции мы будем использовать первичный ключ: CREATE TABLE PARENT ( ID BIGINT NOT NULL, DATA VARCHAR(20), CONSTRAINT PK_PARENT PRIMARY KEY(ID)); COMMIT; Дочерняя структура В дочернюю структуру нам нужно включить столбец parent_id, который в точности соответствует первичному ключу родительской таблицы по типу и размеру (а также по порядку столбцов, если связь выполняется по нескольким столбцам): CREATE table CHILD ( ID BIGINT NOT NULL, CHILD_DATA VARCHAR(20), PARENT_ID BIGINT, CONSTRAINT PK_CHILD PRIMARY KEY(ID)); COMMIT; 2 Толковое добавление к этому в книге Data Modeling Essentials, 2nd Edition by Graeme Simsion (Coriolis, 2000).
Гпава 17. Ссылочная целостность данных 335 Следующее, что нам нужно сделать, это определить отношение между дочерней и родительской таблицами — создать ограничение внешнего ключа. Синтаксис определения FOREIGN KEY Синтаксис определения ссылочной целостности следующий: FOREIGN KEY {столбец [, столбец . ..]) REFERENCES (родительская-таблица [,столбец ...]) [USING [ASC I DESC] INDEX имя-индекса] /* добавлено в версии 1.5 */ [ON DELETE {NO ACTION | CASCADE | SET NULL | SET DEFAULT}] [ON UPDATE {NO ACTION | CASCADE | SET NULL | SET DEFAULT}] Определим наш внешний ключ: ALTER TABLE CHILD ADD CONSTRAINT FK_CHILD_PARENT FOREIGN KEY(PARENT_ID) REFERENCES PARENT(ID); /* также допустимо REFERENCES PARENT, поскольку ID является первичным ключом таблицы PARENT */ Firebird сохраняет ограничение fk_child_parent и создает обычный индекс для столб- ца (столбцов), перечисленных в качестве аргументов foreign key. В Firebird этот ин- декс будет также назван fk_child__parent, если вы не использовали необязательное предложение using для задания другого имени индекса. В Firebird 1.0.x индекс будет иметь имя iNTEG_nn (где пп — некоторое число). Д ВНИМАНИЕ! Если вы указали убывающий индекс для ограничения первичного или уникального ключа, вы также должны указать using descending index для каждого ссылающегося на него внешнего ключа. Наши две таблицы теперь связаны ограничением формальной ссылочной целост- ности. Мы можем добавлять новые строки в таблицу parent без каких-либо ограни- чений: INSERT INTO PARENT (ID, DATA) VALUES (1, 'Parent No. 1'); При этом существует ограничение для child. Мы можем выполнить следующее: INSERT INTO CHILD (ID, CHILD_DATA) VALUES (1, 'Child No. 1'); Поскольку допускающий пустое значение столбец parent id отсутствует в списке столбцов, в нем будет сохранено значение null. Это допускается правилами целост- ности по умолчанию. Такая строка будет зависшей (или осиротевшей, orphan). Однако мы получим ошибку ограничения, если попытаемся сделать следующее: INSERT INTO CHILD(ID, CHILD_DATA, PARENT_ID) VALUES (2, 'Child No. 2', 2);
336 Часть IV. База данных и ее объекты ISC ERROR CODE:335544466 ISC ERROR MESSAGE: violation of FOREIGN KEY constraint "FK_CHILD_PARENT" on table "CHILD" (нарушение ограничения "FK_CHILD_PARENT" для внешнего ключа таблицы CHILD) В таблице parent не существует строки, имеющей у первичного ключа значение 2, следовательно, ограничение не позволит выполнить добавление. Оба следующих действия допустимы: UPDATE CHILD SET PARENT_ID = 1 WHERE ID = 1; COMMIT; /**/ INSERT INTO CHILD (ID, CHILD_DATA, PARENT_ID) VALUES (2, 'Child No.2', 1); COMMIT; Теперь строка из parent co значением id = 1 имеет две дочерние строки. Это класси- ческая структура главная-подчиненная — простая реализация отношения один-ко- многим. Для защиты целостности данного отношения правила по умолчанию не по- зволят выполнить следующее: DELETE FROM PARENT WHERE ID = 1; Действия триггеров по изменению правил целостности Очевидно, что правила целостности применяются, когда происходят изменения дан- ных, влияющих на отношение. При этом правила по умолчанию не всегда подходят для всех требований. Мы можем захотеть перекрыть правило, которое позволяет соз- давать зависшие дочерние строки или сделать их зависшими при установке значения их внешнего ключа в null. Если для наших бизнес-правил требуется запрет удаления родительской строки, имеющей дочерние строки, мы можем пожелать, чтобы Firebird позаботился об этой проблеме автоматически. Язык SQL в Firebird позволяет сделать это с помощью необязательных автоматических действий триггеров'. [ON DELETE (NO ACTION | CASCADE | SET NULL | SET DEFAULT}] [ON UPDATE {NO ACTION | CASCADE | SET NULL | SET DEFAULT}] Автоматические действия триггеров Firebird предоставляет необязательные стандартные события DML — on update и on delete, — используемые для изменения правил ссылочной целостности. События DML и автоматическое поведение совместно определяют действия для триггера — какие действия должны быть выполнены для зависимой таблицы при изменении или удалении соответствующего ключа в родительской таблице. Определение действий включают каскадные изменения в связанной через внешний ключ таблице (таб- лицах).
Глава 17. Ссылочная целостность данных 337 Семантика действий триггера NO ACTION Поскольку это действие триггера по умолчанию, ключевое слово может быть — и часто бывает— опущено3. Операция DML над родительским первичным ключом не изменяет внешний ключ и потенциально может привести к ошибке операции над родительской таблицей. ON UPDATE CASCADE В зависимой таблице внешний ключ, соответствующий старому значению первично- го ключа, изменяется на новое значение первичного ключа. ON DELETE CASCADE В зависимой таблице удаляются строки с соответствующим значением ключа. SET NULL Внешний ключ, соответствующий старому значению родительского первичного ключа, устанавливается в null— зависимые строки становятся зависшими. Ясно, что это действие триггера не может быть применено, если столбец внешнего ключа не допускает пустых значений. SET DEFAULT Внешний ключ, соответствующий старому значению первичного ключа, устанавли- вается в его значение по умолчанию. Существует несколько понятий относительно этого действия, о которых важно знать. ♦ Используется значение по умолчанию, которое существовало в момент создания ограничения foreign key. Если значение по умолчанию у столбца будет изменено позже, то значение по умолчанию для действия set default в определении внеш- него ключа не будет изменено на новое значение — оно сохранит прежнее зна- чение. ♦ Если никакое значение по умолчанию не было явно установлено для столбца, то неявным значением по умолчанию будет null. В этом случае поведение при set default будет тем же самым, что и при set null. ♦ Если значение по умолчанию для столбца внешнего ключа — такое значение, ко- торое не имеет соответствующего значения первичного ключа в родительской таблице, то действие триггера приведет к нарушению ограничения. Взаимодействие ограничений Комбинируя формальное ссылочное ограничение с другими ограничениями целост- ности (см. главу 16), можно реализовать большинство (если не все) бизнес-правил с высокой степенью точности. Например, ограничение столбца not null будет коррек- 3 Действие no action иногда называют "ограниченным" действием.
338 Часть IV. База данных и ее объекты тировать действия и предотвратит появление зависших строк, если это необходимо, тогда как столбец внешнего ключа, который допускает пустые значения, может быть использован для реализации специальных структур данных, таких как деревья (см. разд. "Ссылающиеся на себя отношения"). СОВЕТ. Если вам нужно сделать столбец вашего внешнего ключа not null, соз- дайте "фиктивную" строку родительской таблицы с неиспользуемым значением ключа, например, 0 или -1. Используйте действие set default для эмуляции поведения set null, чтобы сделать значением по умолчанию фиктивное значе- ние ключа. Ссылочные ограничения могут быть назначены ограничениям check. В некоторых случаях ограничение check, наследуемое от домена, может также пересекаться или вступать в конфликт со ссылочным ограничением. Стоит потратить несколько минут для описания на бумаге эффектов каждого ограничения для идентификации и уменьшения потенциальных проблем. Триггеры действий пользователя Есть прекрасная возможность написания собственных триггеров для выполнения дополнительных действий по поддержке ссылочной целостности. Хотя автоматиче- ские триггеры достаточно гибкие для того, чтобы предусмотреть большинство требо- ваний, существует один особый случай, для которого обычно вызываются пользова- тельские триггеры. Это тот случай, когда создание обязательного индекса, поддер- живающего столбец внешнего ключа, нежелательно, потому что это будет индекс с очень низкой селективностью. Вообще говоря, индексы с низкой селективностью появляются, когда небольшое ко- личество возможных значений содержится в большой таблице или когда только не- большая часть возможных значений используется в конкретной таблице. В результа- те громоздкое дублирование значений в индексе — описывается как длинные цепоч- ки— может серьезно повлиять на производительность запросов при увеличении размера таблицы. СОВЕТ. Селективность индекса довольно подробно обсуждается в главе 18. Если эта тема для вас новая, то это может вас заставить основательно разо- браться в данном вопросе до принятия решения реализовывать отношение один- ко-многим в вашей модели данных с использованием формальных ограничений целостности "только потому, что я могу". При написании пользовательских ссылочных триггеров вы должны убедиться, что ваши собственные триггеры или ваше приложение будут поддерживать ссылочную целостность при изменении данных в любом ключе. Триггеры более безопасны, чем код приложений, поскольку они централизуют правила целостности данных в базе
Гпава 17. Ссылочная целостность данных 339 данных и поддерживают их для всех типов доступа к данным, будь то программы, утилиты, скрипты или приложение серверного уровня4. При отсутствии формальных действий по каскадным изменениям и удалениям ваше пользовательское решение должно позаботиться о строках дочерней таблицы, на ко- торые воздействуют изменения или удаления ключей родительской таблицы. Напри- мер, если строка удаляется из родительской таблицы, ваше решение должно вначале удалить все строки из всех таблиц, которые ссылаются на эту строку через внешние ключи. Таблицы соответствия и ваша модель данных Мы часто используем таблицы соответствия (lookup tables) — также называемые управляющими таблицами (control tables) или таблицами определения (definition ta- bles) — для хранения статичных строк, которые могут содержать расширенные тек- сты, коэффициенты преобразования, а также нечто подобное выходным наборам, часто получаемым в приложениях как списки выбора. Примерами являются таблицы "типов", которые содержат сущности, такие как типы счетов или типы документов, таблицы "коэффициентов", используемые для преобразования валют или вычисления налогов, и таблицы "соответствия кодов", хранящие такие элементы, как коды, соот- ветствующие цвету. Динамичные таблицы связаны с такими статичными таблицами через соответствие ключа первичному ключу статичных таблиц. Инструменты моделирования данных не могут отличить отношение соответствия от отношения главная-подчиненная, поскольку, если говорить несколько упрощая, строка соответствия может поставлять значения для многих "пользовательских" строк. Инструменты представляют это как зависимость родитель-потомок и могут ошибочно рекомендовать использование внешнего ключа на "дочерней" стороне. В реализованных базах данных такое отношение не является отношением главная- подчиненная, или родитель-потомок, потому что значение первичного ключа у набо- ра соответствия требует одного и только одного столбца. Это не влияет на другие отношения, в которых участвует этот "псевдопотомок". Заманчивым является применение формального ограничения первичного ключа к столбцам, которые ссылаются на таблицы соответствия. Аргументом для этого явля- ется то, что каскадное ссылочное ограничение будет обеспечивать согласованность данных. Изъян здесь в том, что правильно спроектированные таблицы соответствия никогда не изменяют их ключи, следовательно, здесь не может появиться противоре- чивости, от которой требуется защита. 4 Не следует полагаться на триггерную ссылочную целостность потому, что ограничения primary key, foreign key и unique работают вне контекста транзакций (т. е. "видят" все вер- сии записей), а пользовательские триггеры — в контексте пользовательских транзакций. В результате пользовательский триггер, проверяющий наличие определенной записи, никоим образом не узнает, что эта запись на самом деле уже удалена или изменена в другой, конкури- рующей, транзакции. —Прим. науч. ред.
340 Часть IV. База данных и ееобьекты Посмотрите на следующий пример отношения соответствия, являющийся наследием конвертирования очень плохо спроектированного приложения для Access в Firebird. Клиентские приложения Access могут выполнять изящные действия с целыми табли- цами, которые позволяют дилетантам создавать приложения RAD. Эта таблица была использована в визуальном управляющем элементе, который мог отображать табли- цу и "перемещать" значение в другую таблицу при щелчке по кнопке. CREATE TABLE COLORS (COLOR CHARACTER(20) NOT NULL PRIMARY KEY); Фрагмент DDL одной из таблиц, которая использует colors в качестве таблицы соот- ветствия: CREATE TABLE STOCK_ITEM ( COLOR CHARACTER(20) DEFAULT 'NEUTRAL', CONSTRAINT FKJCOLOR FOREIGN KEY (COLOR) REFERENCES COLORS(COLOR) ON UPDATE CASCADE ON DELETE SET DEFAULT; Существует множество проблем с этим ключом. Во-первых, таблица colors была доступна покупателям товаров для ее редактирования, как они считали нужным. Из- менения выполнялись каскадно по всей системе всякий раз, когда новые элементы добавлялись в ассортимент. Удаления часто убирают информацию о цвете в относи- тельно небольшом количестве элементов, в которых она используется. Хуже того, основная масса элементов в системе имела один цвет ’neutral’, в результате чего индекс внешнего ключа ухудшал выполнение запросов. "Реляционный путь" — устранение незапланированных нарушений данных за счет использования ключа соответствия, который будет содержать не имеющие смысл данные (т. е. атомарный ключ): CREATE TABLE COLORS ( ID INTEGER NOT NULL PRIMARY KEY, /* or UNIQUE */ COLOR CHARACTER(20)); COMMIT; INSERT INTO COLORS (ID, COLOR) VALUES (0, 'NEUTRAL'); COMMIT; CREATE TABLE STOCK__ITEM ( COLOR INTEGER DEFAULT 0, . . .) ; Такой ключ никогда не нужно изменять; он может (и должен) быть спрятан от поль- зователей. Таблицы, использующие таблицы соответствия, хранят стабильный ключ. Все допустимые изменения реализуются как новые строки таблицы соответствия с новыми ключами. Значения, уже связанные с ключами, не изменяются— они защи- щены от того, чтобы история данных подвергалась риску последующих изменений.
Глава 17. Ссылочная целостность данных 341 В этом случае, даже при большом распределении значений ключа внешний ключ бу- дет создавать индекс, который все еще будет иметь плохую селективность в большой таблице; повышение стабильности таблицы оправдывает отказ от формального ссы- лочного ограничения. Существование строки с соответствующим первичным ключом легко можно проверить с помощью пользовательского триггера. Привилегии на ссылки Firebird поддерживает безопасность SQL для всех объектов в базе данных. Каждый пользователь, за исключением владельца базы данных, пользователя SYSDBA или с системными привилегиями root, должен получить (при использовании grant) необхо- димые привилегии доступа к объекту. Привилегии SQL очень подробно обсуждаются в главе 3 7. Тем не менее одна привилегия очень важна при проектировании инфраструктуры ссылочной целостности — привилегия references. Если родительская и дочерняя таблицы имеют разных владельцев, привилегия grant references может оказаться не- обходимой для предоставления пользователям достаточных полномочий для дейст- вий ссылочного ограничения. Привилегия references предоставляется для таблицы, на которую осуществляется ссылка в отношении, — т. е. для таблицы, на которую ссылается внешний ключ, — или, по крайней мере, на каждый столбец первичного или уникального ключа. При- вилегия должна быть предоставлена для владельца ссылающейся таблицы (дочерней таблицы), а также для любого пользователя, которому необходимы права записи на ссылающуюся таблицу. Во время выполнения references срабатывает, когда сервер базы данных устанавли- вает, что вводимое во внешний ключ значение находится в таблице, на которую осу- ществляется ссылка. Поскольку такая привилегия проверяется при определении ограничения внешнего ключа, необходимо предоставить и подтвердить соответствующие разрешения забла- говременно. Если вам нужно создать внешний ключ, который ссылается на таблицу, которой владеет кто-то другой, то владелец должен предоставить вам привилегии references к этой таблице. Альтернативно владелец может предоставить привилегии references роли, а затем предоставить вам эту роль. СОВЕТ. Не делайте это сложнее, чем оно должно быть. Если нет никакого тре- бования отменять привилегии чтения для таблицы, на которую осуществляются ссылки, то передайте привилегию references к ней для всех (public). Если ваши требования содержат такие ограничения, вам может понадобиться под- держивать два разрешающих скрипта: один для разработчиков, выполняющих созда- ние таблицы, и другой для пользователей, работающих с созданной схемой.
342 Часть IV. База данных и ее объекты Обработка других видов отношений Ограничения целостности могут быть применены для других форм отношений, по- мимо формы один-ко-многим, описанной до настоящего времени. ♦ Один-к-одному. ♦ Многие-ко-многим. ♦ Ссылающееся на себя отношение один-ко-многим (вложенные или древовидные отношения). ♦ Обязательные варианты любых форм отношений. Отношение один-к-одному Структуры один-к-одному могут быть полезными, когда сущность в вашей модели данных имеет множество различных атрибутов, из которых только к некоторым час- то осуществляется доступ. Это может резко сократить занимаемую память и время чтения страниц, если хранить случайные данные в необязательных "подчиненных" отношениях, которые используют соответствующие первичные ключи. Отношение один-к-одному похоже на отношение один-ко-многим в том смысле, что оно связывает внешний ключ с уникальным ключом. Разница здесь в том, что связы- ваемый ключ должен быть уникальным для поддержания отношения один-к- одному — чтобы соединить не более одной зависимой строки с одной родительской строкой. Обычным является дублирование столбца (столбцов) первичного ключа в подчинен- ной таблице в качестве внешнего ключа для "родительской". CREATE TABLE PARENT_PEER ( ID INTEGER NOT NULL, MORE_DATA VARCHAR(10), CONSTRAINT PK_PARENT_ PEER PRIMARY KEY (ID), CONSTRAINT FK_PARENT_PEER_PARENT FOREIGN KEY (ID) REFERENCES PARENT); Результатом такого дублирования является создание двух обязательных индексов для столбца первичного ключа подчиненной таблицы: один для первичного ключа и один для внешнего ключа. Индекс внешнего ключа сохраняется так, как если бы он не был уникальным. В версиях 1.0.x и 1.5 оптимизатор игнорирует первичный индекс подчиненной таб- лицы. Например: SELECT PARENT.ID, PARENT_PEER.ID, PARENT. DATA, PARENT_PEER. MORE_DATA FROM PARENT JOIN PARENT—PEER ON PARENT.ID = PARENT—PEER.ID; игнорирует индекс первичного ключа подчиненной таблицы и создает план: PLAN JOIN (PARENT_PEER NATURAL, PARENT INDEX (PK_PARENT) )
Гпава 17. Ссылочная целостность данных 343 Влияние на производительность "разреженного" ключа (такого, какой использован в этом примере) не может быть сильным. В случае составного ключа эффект может быть значительным, особенно в случае множественных соединений, включающих отношения один-к-одному. Следует рассмотреть использование суррогатного ключа в структурах один-к-одному5. СОВЕТ. Не будет плохо, если вы решите добавить специальный столбец для подчиненного отношения с целью разделения первичного и внешнего ключей. Это может оказаться полезным и для документирования. Отношение многие-ко-многим В этом интересном случае, показанном на рис. 17.2, наша модель данных показывает, что каждая строка в таблице ТаЫеА может иметь отношения со множеством строк таблицы таЫев, и в то же время каждая строка в таЫев может иметь множественные отношения СО строками В ТаЫеА. ТаЫеА Primary key: ID Foreign key: IDB J Primary key: ID I Foreign key: IDA J TableB Рис. 17.2. Отношения многие-ко-многим Это отношение использует условие, называемое циклической ссылкой. Предлагаемый внешний ключ в таблице таЫев ссылается на первичный ключ таблицы ТаЫеА, что означает, что строка таблицы таЫев не может быть создана, если в таблице ТаЫеА нет строки с соответствующим первичным ключом. В то же время, по этой же при- чине требуемая строка не может быть добавлена в таблицу ТаЫеА, если не существу- ет соответствующего значения первичного ключа в таблице таЫев. Работа с циклическими ссылками Если ваши структурные требования диктуют необходимость существования подоб- ных циклических ссылок, это можно сделать обходным путем. Firebird позволяет внешнему ключу иметь значение null — если не указывать для столбца ограничение not null, — поскольку null означает отсутствие значения. Это не нарушит правила, по которому столбец внешнего ключа должен иметь соответствие в столбце роди- тельской таблицы, на которую ссылается внешний ключ. Присваивая значение null внешнему ключу одной таблицы, вы можете добавлять строку в эту таблицу, созда- вая первичный ключ, требуемый в другой таблице: 5 И надеяться, что эта маленькая причуда оптимизатора с ключами один-к-одному будет разрешена в следующих релизах.
344 Часть IV. База данных и ее объекты CREATE TABLE TABLEA ( ID INTEGER NOT NULL, • • • ! CONSTRAINT PK_TABLEA PRIMARY KEY (ID)); COMMIT; CREATE TABLE TABLES ( ID INTEGER NOT NULL, • • • t CONSTRAINT PK_TABLEB PRIMARY KEY (ID)); COMMIT; ALTER TABLE TABLEA ADD CONSTRAINT FK__TABLEA_TABLEB FOREIGN KEY(IDB) REFERENCES TABLEB(ID); COMMIT; ALTER TABLE TABLEB ADD CONSTRAINT FK_TABLEB_TABLEA FOREIGN KEY(IDA) REFERENCES TABLEA(ID); COMMIT; Вот этот прием: INSERT INTO TABLEB(ID) VALUES(1); /* создает строку co значением NULL в столбце IDB */ COMMIT; INSERT INTO TABLEA(ID, IDB) VALUES(22, 1); /* связывает с только что созданной строкой в TABLEB */ COMMIT; UPDATE TABLEB SET IDA = 22 WHERE ID - 1; COMMIT; Понятно, что эта модель не лишена потенциальных проблем. В большинстве систем ключи генерируются, а не поставляются приложениями. Чтобы обеспечить согласо- ванность, описанная работа выполняется для всех клиентских приложений, добав- ляющих данные в эти таблицы, чтобы они обеспечивали значения обоих ключей для обеих таблиц в контексте одной транзакции. Выполнение единой операции в храни- мой процедуре уменьшит зависимость кода приложения от такого отношения. /X ВНИМАНИЕ! На практике таблицы с отношением многие-ко-многим, реализо- / | \ ванным циклически, очень сложно представить в приложениях с графическим интерфейсом. Использование таблиц пересечения В большинстве случаев лучшей практикой разрешения отношения многие-ко-многим является добавление таблицы пересечения. Такая специальная структура имеет один
Гпава 17. Ссылочная целостность данных 345 внешний ключ для каждой таблицы в отношении многие-ко-многим. Ее собственный первичный ключ (или ограничение unique) состоит из двух внешних ключей. Две свя- занные этим отношением таблицы вовсе не имеют внешних ключей, связывающих одну с другой. Такая реализация проста для использования в приложениях. Триггеры before insert (до добавления) и before update (до изменения) для обеих таблиц выполняют при необходимости добавление строки в таблицу пересечения. Рис. 17.3 иллюстрирует, как таблица пересечения реализует отношение многие-ко-многим. Рис. 17.3. Реализация отношения многие-ко-многим Вот как это может быть реализовано: CREATE TABLE TABLEA ( ID INTEGER NOT NULL, CONSTRAINT PK_TABLEA PRIMARY KEY (ID)); COMMIT; CREATE TABLE TABLEB ( ID INTEGER NOT NULL, CONSTRAINT PK_TABLEB PRIMARY KEY (ID)); COMMIT; /**/ CREATE TABLE TABLEAJTABLEB ( IDA INTEGER NOT NULL, IDB INTEGER NOT NULL, CONSTRAINT PK_TABLEA_TABLEB PRIMARY KEY (IDA, IDB)); COMMIT; ALTER TABLE TABLEA_TABLEB ADD CONSTRAINT FKJTABLEA FOREIGN KEY (IDA) REFERENCES TABLEA, ADD CONSTRAINT FK_TABLEB FOREIGN KEY (IDB) REFERENCES TABLEB; COMMIT;
346 Часть IV. База данных и ее объекты Ссылающиеся на себя отношения Если ваша модель имеет сущность, у которой первичный ключ ссылается на внеш- ний ключ, находящийся в той же сущности, то вы имеете ссылающееся на себя от- ношение, как показано на рис. 17.4. Parent_Child Primary key: ID Foreign key: ParentJD Рис. 17.4. Ссылающееся на себя отношение Это классическая древовидная иерархия, где любой элемент (строка) может быть и родителем, и потомком — т. е. строка может иметь зависящие от нее "дочерние" строки и в то же время она может зависеть от другого элемента (строки). Здесь тре- буется ограничение check или триггеры before insert (до добавления) и before update (до изменения) для проверки того, чтобы parent_id никогда бы не указывал сам на себя. Если ваши бизнес-правила требуют, чтобы родитель существовал до того, как будет добавляться потомок, вам понадобится использование значения (например, -I) в ка- честве корневого узла в этой древовидной структуре. Тогда parent id должен быть создан с not null и значением по умолчанию, равным выбранному вами значению корневого узла. Альтернативой является разрешение для parent id пустого значения, как в следующем примере, и использование null в качестве значения корня. Вообще пользовательские триггеры before insert (до добавления) и before update (до изменения) потребуются для деревьев, имеющих более двух уровней вложенности. Для согласованности деревьев с корневым узлом null важно обеспечить такие дейст- вия ограничений, которые бы не создавали случайно зависшие дочерние строки. CREATE TABLE PARENT-CHILD ( ID INTEGER NOT NULL, PARENT_ID INTEGER CHECK (PARENT_ID <> ID)); COMMIT; ALTER TABLE PARENT_CHILD ADD CONSTRAINT PK_PARENT PRIMARY KEY(ID); COMMIT; ALTER TABLE PARENT_CHILD ADD CONSTRAINT FK_CHILD_PARENT FOREIGN KEY(PARENT_ID) REFERENCES PARENT_CHILD(ID); О древовидных структурах Можно было бы сказать гораздо больше о проектировании древовидных структур. Это перспективная тема в создании реляционных баз данных, которая расширяет
Глава 17. Ссылочная целостность данных 347 границы стандарта SQL. К сожалению, она выходит за рамки данной книги. Некото- рые интересные решения см. в "SQL for Smarties", 2nd Edition by Joe Celko (Morgan Kaufmann, 1999). Обязательные отношения Обязательное отношение — это отношение, которое требует существования как ми- нимум одной дочерней строки для каждой родительской строки. Например, структу- ра накладной (заголовок с информацией о покупателе и адресом поставки) будет не- логичной, если будет разрешено существование заголовочной строки без детальных строк. Общей ошибкой начинающих является предположение, что ограничение not null в дочерней таблице сделает отношение один-ко-многим обязательным. Этого не будет, потому что ограничение внешнего ключа действует только в контексте конкретной зависимости. В случае отсутствия строки, ссылающейся на родительскую таблицу, допустимость пустого значения для внешнего ключа не решает проблему. Обязательное отношение — это одно из тех мест, где определенные пользователем триггеры должны быть использованы для поддержания ссылочной целостности. SQL в Firebird не поддерживает "обязательных" ограничений. Он может использовать не- которую логику на клиенте и на сервере для гарантирования правильной последова- тельности событий, которая бы соответствовала и ссылочному ограничению, и тре- бованиям обязательности. Он будет использовать триггеры как для добавления, так и для удаления данных, поскольку эта логика должна поддерживать правило "мини- мально один потомок" не только во время создания, но и при удалении дочерних строк. Подробности написания триггеров, а также пример триггера для поддержания обяза- тельного отношения см. в главе 31. Ошибка "объект находится в использовании" Исключение "object is in use" (объект находится в использовании) заслуживает вни- мания в контексте применения ограничений ссылочной целостности, поскольку яв- ляется постоянным источником огорчений для новичков. Firebird не позволяет до- бавлять или удалять ссылочное ограничение foreign key, если транзакция использует любую из участвующих таблиц. Иногда для вас может быть не столь очевидным, каким образом объект находится в использовании. Другие зависимости — такие как хранимые процедуры или триггеры, которые ссылаются на ваши таблицы, или другие ссылочные ограничения, воздейст- вующие на одну или обе таблицы — могут вызвать это исключение, если таблицы используются в неподтвержденной транзакции. Кэш метаданных (блоки памяти на сервере, которые содержат метаданные, сформированные в результате выполнения последних клиентских запросов, и коды вызванных хранимых процедур и триггеров)
348 Часть IV. База данных и ее объекты хранит блокировки применяемых объектов. Каждое соединение имеет свой собст- венный кэш метаданных, даже в случае Суперсервера, следовательно, сервер может хранить блокировки объектов, которые фактически не используют ни одно соеди- нение. Настоятельно рекомендуется получить исключительный доступ к базе данных для любых изменений метаданных, особенно тех, которые используют зависимости. СОВЕТ. Если вы имеете исключительный доступ, а исключение все равно появ- ляется, то вполне возможно, что объект используете именно вы. Если вы рабо- таете с утилитой администратора, где браузер данных сфокусирован на одной из ваших таблиц, то этот объект находится в использовании! Пора дальше Firebird использует индексы для поддержания ссылочной целостности. При этом ин- дексы играют такую же важную роль в оптимизации производительности при опера- циях поиска и упорядочения, требуемых для запросов и для изменения данных. В следующей главе мы рассмотрим в полном объеме вопросы проектирования, соз- дания и тестирования индексов. Глава заканчивается специальным разделом, посвя- щенным оптимизации индексов с использованием Firebird-утилиты получения стати- стики по индексам gstat.
ГЛАВА 18 Индексы Индексы являются атрибутами таблицы, которые могут содержать один столбец или группу столбцов для ускорения поиска строк. Индекс служит логическим указателем на физическое размещение (адрес) строк в таблице; он используется почти так же, как вы применяете указатель в книге для бы- строго поиска номеров страниц разделов, которые вы собираетесь просмотреть. В большинстве случаев, если сервер может прочесть запрашиваемые строки, скани- руя индекс вместо сканирования всех строк в таблице, запросы будут выполняться быстрее. Хорошо спроектированная система индексов играет важную роль в на- стройке и оптимизации условий эксплуатации ваших приложений. Ограничения Firebird допускает до 256 определенных пользователем индексов на одну таблицу в версии 1.5 и выше, и 64 в более ранних релизах. Однако это теоретические ограниче- ния, которые могут быть скорректированы за счет размера страницы и фактического размера на диске данных описания индекса в корневой странице индекса. Вы не сможете хранить 256 индексов в базе данных с размером страницы менее 16 Кбайт. На корневой странице индекса каждому индексу нужен 31 байт для его идентифика- тора, место для описания каждого сегмента (столбца), включенного в состав индекса, и несколько байтов для хранения указателя на первую страницу индекса. Даже стра- ница в 16 Кбайт может не оказаться способной хранить 256 индексов, если в базе данных существует немало составных индексов. Для создания индексов пользователь должен иметь право соединяться с базой дан- ных. Общая длина индекса не может превышать 252 байта. В реальности количество байт может быть значительно меньше. Факторами, которые могут уменьшить количество фактических "мест", доступных для хранения символов, являются: ♦ интернациональные наборы символов, использующие несколько байт на символ; ♦ интернациональные наборы символов со сложными парами верхний/нижний ре- гистры и/'или сложным словарем правил сортировки; ♦ использование недвоичных порядков сортировки; ♦ множество сегментов (составной индекс), которые требуют дополнительных пус- тых байтов для сохранения геометрии индекса.
350 Часть IV. База данных и ее объекты Другими словами, использование любого набора символов за исключением none бу- дет влиять на ваши решения по проектированию индексов — особенно на использо- вание составных индексов. Это плохие новости. Хорошие новости— Firebird пра- вильно использует индексы из одного столбца в многостолбцовых поисках и сорти- ровках, сокращая потребность в многостолбцовых индексах, которые вы могли использовать в других СУБД. Автоматические индексы в сравнении с определенными пользователем индексами Firebird автоматически создает индексы для обеспечения различных ограничений целостности (более подробную информацию см. в главах 16 и 17). Для удаления та- ких индексов необходимо удалить ограничения, которые их используют. Использование индексов ограничений не заканчивается их работой по поддержке целостности ключей и отношений. Они рассматриваются вместе со всеми другими при подготовке запросов. При определении ваших собственных индексов крайне важно исключить создание любых индексов, которые дублируют автоматически сгенерированные индексы. Это ставит оптимизатор (см. разд. "Планы запросов") в печальную ситуацию выбора ме- жду равными индексами. В большинстве случаев он разрешит проблему, не выбрав ни одного. Импорт существующих индексов Не импортируйте "первичные индексы" таблиц при миграции из другой СУБД. Есть две важные причины отказаться от таких индексов. ♦ Многие существующие системы используют иерархические структуры индексов для реализации ссылочной целостности. Базы данных SQL не используют подоб- ную логику для реализации ссылочной целостности. Такие индексы обычно влияют на логику оптимизатора Firebird. ♦ Firebird создает свои собственные индексы для поддержки ограничений первич- ного и внешнего ключей, независимо от любого существующего индекса. Как бы- ло сказано ранее, дублирование индексов приводит к проблемам для оптимизато- ра и должно быть полностью исключено. Направленные индексы Направление сортировки индексов в Firebird является важным. Ошибочно было бы предполагать, что один и тот же индекс может быть использован для сортировки или поиска "в обоих направлениях" — от меньшего к большему и от большего к меньше- му. В практике индексы asc (ascending, в возрастающем порядке) помогут в поиске относительно небольшого количества значений, в то время как индексы desc (descending, в убывающем порядке) будут полезными при большом количестве зна- чений.
Глава 18. Индексы 351 Если автоматический индекс asc (по умолчанию), то не будет проблем, если вам нужно определить индекс desc, использующий тот же столбец (столбцы). Обратное также верно: в Firebird 1.5 и выше вы можете выбрать для автоматически создавае- мых индексов убывающий порядок. Оптимизатор не "расстроится", если вы также создадите возрастающий индекс для тех же столбцов. Планы запросов Перед выполнением запроса комплект программ подготовки — известный как опти-. мизатор— начинает анализировать столбцы и операции запроса для вычисления самого быстрого способа выполнения. Подготовка начинается с просмотра индексов таблицы и используемых столбцов. Работая таким образом с последовательностью путей решения (каждый из которых имеет свою "стоимость"), оптимизатор создает план — некий вид "дорожной карты" того пути, по которому сервер будет следовать при выполнении запроса. Конечный план выбирается по критерию "самой дешевой" дороги, оцениваемой в соответствии с индексами, которые могут быть использованы. План оптимизатора может быть просмотрен в isql двумя способами. ♦ По умолчанию isql не отображает план. Используйте set plan on для отображе- ния плана в самом начале вывода запроса select. ♦ Используйте set planonly для рассмотрения запроса и просмотра плана без фак- тического выполнения запроса. Это позволяет вам анализировать план любого за- проса, а не только запросов select. Можно перекрыть план запроса оптимизатора вашим собственным планом, включив предложение plan в оператор запроса. Большинство инструментов графического ин- терфейса сторонних разработчиков обеспечивают возможность просматривать план, выполняя или не выполняя запрос, и перекрывать его. СОВЕТ. Не перекрывайте план оптимизатора, пока вы не протестировали ваш собственный и не убедились, что он выполняется быстрее на реальных данных. Более подробную информацию о планах запроса см. в разд. "Тема оптимизации" гла- вы 20. Подробности использования isql см. в главе 37. Как могут помочь индексы Если оптимизатор принимает решение использовать индекс, он отыскивает страницы индекса для поиска требуемых значений ключа и использует указатель для локализа- ции выбранных строк на страницах данных этой таблицы. Поиск данных выполняет- ся быстро, потому что значения индекса упорядочены. Это позволяет системе лока- лизовать нужные значения напрямую по указателю и полностью исключает просмотр ненужных строк. Обычно использование индекса требует чтения меньшего количе- ства страниц, чем "прогулка по" всем строкам в таблице. Индекс по размеру мал по
352 Часть IV. База данных и ее объекты сравнению с размером строки в таблице и, если было выполнено хорошее проектиро- вание индекса, занимает меньшее количество страниц базы данных, чем строки таб- лицы. Сортировка и группирование Когда столбцы, указанные в предложениях order by или group by, являются индекси- рованными, оптимизатор может упорядочить выходные данные, просматривая ин- дексы, и собирать упорядочиваемые наборы быстрее, чем без использования индек- сов. Убывающий индекс для группы столбцов может увеличить скорость выполнения запросов для агрегатной (обобщающей) функции мах о, потому что получение строки с максимальным значением требует только одного обращения. Информацию об ис- пользовании функциональных выражений в запросах см. в главах 21 и 23. Соединения Для соединений оптимизатор выполняет процесс слияния потоков данных на основа- нии соответствия значений, явно или неявно указанных в критерии on. Если какой- нибудь индекс доступен для столбца или столбцов на одной стороне соединения, оп- тимизатор создает свой начальный поток, используя этот индекс для соответствия ключа соединения с его корреспондентом из таблицы другой стороны соединения. При отсутствии индекса на любой из сторон он должен сначала создать образ одной стороны, а затем просматривать его с целью выборки из таблицы другой стороны соединения. Сравнения Когда сравнивается индексированный столбец для определения, является ли его зна- чение больше, равно или меньше значения константы, то значение индекса использу- ется в таком сравнении, и несоответствующие строки не выбираются. При отсутст- вии индекса все строки-кандидаты будут читаться и сравниваться последовательно. Что индексировать Величина времени, затрачиваемая на поиск во всей таблице, прямо пропорциональна количеству строк в таблице. Индекс для столбца может означать разницу между не- медленным ответом на запрос и долгим ожиданием. Так почему бы не индексировать каждый столбец? Основные препятствия этому состоят в том, что индексы занимают дополнительное дисковое пространство, а добавление, удаление и изменение строк занимает больше времени для индексированных столбцов, чем для не индексированных. Индекс дол- жен быть изменен каждый раз, когда изменяется элемент данных в индексированном столбце, и каждый раз, когда строка добавляется в таблицу или удаляется из таб- лицы.
Глава 18. Индексы 353 Тем не менее повышение производительности поиска данных обычно является более важным, чем накладные расходы по поддержке требующих дополнительных ресур- сов, но полезных коллекций индексов. Вы должны создавать индекс для столбца, когда: ♦ условие поиска часто ссылается на столбец (Индекс поможет в поиске дат и чи- сел, когда ожидается прямое сравнение или вычисление between. Поисковые ин- дексы для строковых столбцов полезны, когда строки проверяются на точное со- ответствие или в предикатах starting with и containing. Они не годятся для пре- диката like1.); ♦ столбец не включен в ограничение целостности, но на него часто ссылается усло- вие в join; ♦ предложение order by часто использует столбец для сортировки данных (Когда набор данных должен быть упорядочен по нескольким столбцам, составной ин- декс, соответствующий порядку, указанному в предложении order by, может уве- личить скорость поиска1 2.); ♦ вам нужен индекс со специфическими характеристиками, не предоставляемыми данными или существующими индексами, такими как недвоичная сортировка, убывающая или возрастающая упорядоченность; ♦ производится группировка больших наборов записей (Индексы из одного столбца или подходящим образом упорядоченные составные индексы могут увеличить скорость группировки, условия которой заданы в сложном предложении group by.). Вы не должны использовать индексы для столбцов, которые: ♦ редко используются в условиях поиска; ♦ являются часто изменяемыми неключевыми значениями, такими как значение времени или идентификация пользователя; ♦ имеют небольшое количество возможных или фактических значений в большом количестве строк; ♦ представляют собой двухзначное или трехзначное логическое значение. Когда индексировать Некоторые индексы сами заявят о себе в начальном периоде проектирования — обычно через известные вам требования сортировки, группировки, вычислений. Очень хорошей практикой является консервативный подход к созданию индексов: не создавать их, пока не станет ясным их польза. Является хорошей практикой отложить 1 В случаях, когда условие поиска задает like ' string% оптимизатор обычно преобразо- вывает его к предикату starting with 1 string1 и использует индекс, если он доступен. 2 Составные индексы не являются столь важными в Firebird, как в большинстве других СУБД. Часто их использование является неоправданным, потому что Firebird интеллектуально использует индексы из одного столбца. 12 Зак. 420
354 Часть IV. База данных и ее объекты создание сомнительных индексов до того момента в разработке, когда у вас появится хороший набор тестовых данных и сведения о том, какие операции слишком мед- ленные. Преимущества отложенного проектирования индексов: ♦ уменьшение "завуалированное™ производительности", на которую может накла- дываться тестирование функциональной полноты; ♦ более быстрая идентификация реальных источников узких мест; ♦ исключение ненужного или неэффективного индексирования. Использование CREATE INDEX Оператор create index создает индекс из одного или более столбцов таблицы. Ин- декс из одного столбца отыскивает только один столбец в ответ на запрос, в то время как индекс из нескольких столбцов отыскивает один или более столбцов. Синтаксис: CREATE [UNIQUE] [ASC[ENDING] I DESC[ENDING]] INDEX имя-индекса ON имя-таблидд: (столбец [, столбец ...)); Обязательные элементы Обязательные элементы в синтаксисе create index следующие: ♦ create index имя-иадекса — именует индекс. Идентификатор должен отличаться от идентификаторов всех других объектов базы данных за исключением иденти- фикаторов ограничений и столбцов. Хорошая идея использовать систему имен объектов схемы, это также поможет лучшей документированности; Ж? ПРИМЕЧАНИЕ. Начиная с версии 1.5 и выше работает автоматическое имено- вание индекса по имени его ограничения. ♦ имя-таблицы— имя той таблицы, для которой создается индекс; ♦ ' столбец [, столбец ...] — имя столбца или разделенный запятыми список имен столбцов, которые будут ключами индекса. Порядок столбцов значим для индек- сов. Более подробную информацию см. в разд. "Индексы из нескольких столб- цов". Пример Следующее объявление создает неуникальный возрастающий индекс для столбца last name (фамилия человека) в таблице person. Он может быть полезным в условиях поиска типа WHERE LAST_NAME = 'Johnston' ИЛИ WHERE LAST_NAME STARTING WITH 'Johns': CREATE INDEX LAST__NAME_X ON PERSON (LAST_NAME) ;
Глава 18. Индексы 355 Необязательные элементы UNIQUE Ключевое слово unique может быть использовано в индексах, для которых вы хотите запретить дублирующие записи. Столбец или группа проверяется на дублированные значения, когда индекс создается, а также для существующих значений каждый раз, когда строка добавляется или изменяется. Уникальные индексы имеют смысл, только когда вам нужно обеспечить уникаль- ность внутренних характеристик элемента данного или группы. Например, вы не бу- дете создавать такой индекс для столбца, хранящего фамилию человека, потому что фамилиям не присуща уникальность. И наоборот, уникальный индекс является хо- рошей идеей для столбца, содержащего номер социального обеспечения, поскольку нарушение уникальности ключа сообщает пользователю о требующей внимания ошибке. Пример В этом примере создается уникальный индекс для трех столбцов таблицы инвентари- зации для гарантирования того, что система хранит не более одной строки для каж- дого размера и цвета элемента: CREATE UNIQUE INDEX STK_SIZE_COLOR_UQX ON STOCK_ITEM (PRODUCT_ID, SIZE, COLOR); Заметьте, что уникальный индекс не является ключом. Если вам требуется уникаль- ный ключ для ссылочных целей, используйте для этого ограничение unique для столбца (столбцов). Подробнее об использовании ограничения unique см. главу 16. Поиск дубликатов Конечно, невозможно создать уникальный индекс для столбца, который уже содер- жит дублирующие значения. Перед определением уникального индекса используйте оператор select для поиска дублирующих элементов в таблице. Например, до созда- ния уникального индекса для product_name в таблице product следующая проверка будет показывать любые дубликаты в этом столбце: SELECT PRODUCT_ID, UPPER(PRODUCT__NAME) FROM PRODUCT GROUP BY PRODUCT_ID, UPPER(PRODUCT_NAME) HAVING COUNT(*) > 1; < ПРИМЕЧАНИЕ. Перевод значения столбца в верхний регистр, чтобы сделать поиск не чувствительным к регистру, не является необходимым с точки зрения ._\ уникальности данных. Тем не менее, если уникальность была "поломана" вводом ошибочных данных, мы можем отыскать все неправильные записи. Как вы поступите с дубликатами, зависит от того, что они означают в ваших бизнес- правилах, и от количества дубликатов, которые нужно уменьшить. Обычно, храни-
356 Часть IV. База данных и ее объекты мая процедура является наиболее эффективным способом это обработать. Хранимые процедуры подробно обсуждаются в главах 28—30. ASC[ENDING] или DESCENDING] Ключевые слова asc[ending] и desc[ending] определяют вертикальный порядок сор- тировки индекса, asc задает сортировку индекса от меньшего к большему. Оно явля- ется значением по умолчанию и может быть опущено, desc сортирует значения от большего к меньшему и должно быть указано, если требуется убывающий индекс. Убывающий индекс может быть полезным для запросов, которые отыскивают наи- большие значения (наибольший возраст, самый последний, самый большой ит. д.), а также при упорядоченном поиске или для выходных данных, которые должны быть отсортированы в убывающем порядке. Пример Следующее определение создает убывающий индекс для таблицы в базе данных employee: CREATE DESCENDING INDEX DESC_X ON SALARY^HISTORY (CHANGEJDATE); Оптимизатор будет использовать этот индекс в запросах, подобных следующему, который возвращает номера служащих и их оклады для десяти последних служащих, которым были повышены оклады: SELECT FIRST 10 EMP__NO, NEW_SALARY FROM SALARY__HI STORY ORDER BY CHANGE__DATE DESCENDING; Если вы ожидаете использования как возрастающего, так и убывающего порядка сортировки по определенному столбцу, определите возрастающий и убывающий ин- дексы для этого столбца. Например, будет замечательным создание следующего ин- декса в дополнение к индексу предыдущего примера: CREATE ASCENDING INDEX ASCEND_X ON SALARY__HISTORY (CHANGE_DATE) ; Индексы для нескольких столбцов Если вашим приложениям часто требуется поиск, упорядочение или группировка по некоторой группе из нескольких столбцов в конкретной таблице, будет полезно соз- дать индекс для нескольких столбцов (также называемый составным или композит- ным индексом). Оптимизатор будет использовать подмножество сегментов такого индекса для опти- мизации запроса, если порядок слева направо, в котором запрос обращается к столб- цам в предложении order by, соответствует порядку слева направо в списке столбцов, определенному в индексе. При этом для запросов не требуется иметь в точности та- кой же список столбцов, как определено в индексе, чтобы индекс мог быть использо- ван оптимизатором. Индекс также может быть использован, если подмножество
Глава 18. Индексы 357 столбцов в предложении order by начинается с первого столбца индекса, определен- ного для нескольких столбцов. Firebird может использовать один элемент составного индекса для оптимизации по- иска, если все предшествующие элементы индекса также используются. Рассмотрим сегментированный индекс для трех столбцов col_w, col_x и Со1_у в том порядке, как показано на рис. 18.1. Col_v Col_w Col_x Col_y Col_z Рис. 18.1. Сегментированный индекс Этот индекс будет использован оптимизатором для следующего запроса: SELECT <список столбцов> FROM ATABLE ORDER BY COL__w, COL_x; Он не будет использован для следующих запросов: SELECT <список столбцов> FROM ATABLE ORDER BY COL_x, COL__y; /**/ SELECT < СПИСОК столбцов> FROM ATABLE ORDER BY COL_x, COL__w; Предикаты OR в запросах Если вы ожидаете для таблицы частого выполнения запросов, которые используют оператор or, то лучше создать индексы из одного столбца для каждого условия. По- скольку индексы из нескольких столбцов упорядочены иерархически, запрос, кото- рый использует одно из двух или более условий, должен просматривать всю таблицу, теряя преимущества использования индексов. Предположим, требуется поиск: WHERE А > 10000 OR В < 300 OR С BETWEEN 40 AND 80 Индекс для (а, в, с) будет использован для поиска строк, содержащих подходящие значения а, но он не может быть использован для поиска значений в или с. Для а убывающий индекс будет более полезным, чем возрастающий, если отыскиваемое значение находится в верхней части диапазона хранимых значений. Критерии поиска Те же самые правила, которые применяются к предложению order by, также приме- нимы к запросам, содержащим предложение where. Следующий пример создает ин- декс по нескольким столбцам для таблицы project в базе данных employee.gdb: CREATE UNIQUE INDEX PRODTYPEX ON PROJECT (PRODUCT, PROJ_NAME);
358 Часть IV. База данных и ее объекты Оптимизатор для этого запроса выберет индекс prodtypex, потому что предложение where ссылается на первый сегмент этого индекса: SELECT * FROM PROJECT WHERE PRODUCT ='software'; Напротив, он проигнорирует данный индекс для следующего запроса, потому что proj_name не является первым сегментом: SELECT * FROM PROJECT WHERE PROJ_NAME STARTING WITH 'Firebird 1'; Просмотр индексов Для просмотра всех индексов, определенных в текущей базе данных, используйте в isql команду show index: ♦ чтобы просмотреть все индексы, определенные для конкретной таблицы, исполь- зуйте команду: SHOW INDEX имя-та блицы; ♦ для просмотра информации конкретного индекса используйте: SHOW INDEX имя-индекса; Изменение индекса Активация/деактивация Оператор alter index используется для переключения состояния индекса из активно- го в неактивное и наоборот. Он может быть применен для отключения индекса перед добавлением или изменением большого пакета строк и устранения при этом допол- нительных затрат для поддержки индексов в процессе длительной операции. После этой операции индексирование может быть восстановлено, и индексы будут пересоз- даны. Другое использование этого оператора — служебные действия. В нормальных усло- виях распределение значений меняется постепенно, а при некоторых рабочих усло- виях — более часто. Структуры двоичных деревьев, в которых хранятся индексы, могут стать разбаланси- рованными. Переключение индекса из активного в неактивное состояние и обратно пересоздает и балансирует3 индекс. 3 В Firebird и InterBase индексы не могут быть "разбалансированы". —Прим. науч. ред.
Глава 18. Индексы 359 Синтаксис: ALTER INDEX имя-индекса INACTIVE | ACTIVE ; Ошибка "индекс находится в использовании" Индекс, используемый в транзакции, не может быть изменен или удален, пока не завершится использующая его транзакция. Такие попытки будут иметь различные результаты в зависимости от установок блокировки активной транзакции: ♦ в транзакции wait оператор alter index ожидает, пока не завершится транзакция; ♦ с транзакции nowait Firebird возвращает ошибку. Информацию об установках блокировок транзакций см. в разд. "Разрешение блоки- ровок" главы 26. Изменение структуры индекса В отличие от большинства операторов alter синтаксис alter index не может быть использован для изменения структуры данного объекта. Для этого необходимо уда- лить индекс и заново его создать с использованием оператора create index. Удаление индекса Оператор drop index удаляет созданный пользователем индекс из базы данных. Используйте drop index также в случае необходимости изменения структуры индек- са: добавление, удаление сегментов, изменение порядка сегментов или изменение порядка сортировки. Вначале используйте оператор drop index для удаления индекса, затем — оператор create index для создания индекса с тем же именем и новыми ха- рактеристиками. Синтаксис: DROP INDEX имя; Следующий оператор удаляет индекс из таблицы job: DROP INDEX MINSALX; Ограничения Никакой пользователь не может удалить индекс, кроме его создателя, пользователя SYSDBA или (в POSIX) пользователя с привилегиями root. Определенные системой индексы, созданные автоматически для столбцов, опреде- ленных в ограничениях уникального, первичного или внешнего ключа, не могут быть удалены. Чтобы удалить такие индексы, необходимо удалить соответствующие огра- ничения.
360 Часть IV. База данных и ее объекты Тема оптимизации: оптимальное индексирование В отличие от многих других реляционных систем баз данных для Firebird не нужен администратор базы данных на полный рабочий день с арсеналом алгоритмов для поддержания нормального выполнения базы данных. Главным образом содержащие- ся в порядке базы данных Firebird просто "сохраняются в прежнем состоянии". Индексы — это значимая составная часть производительности базы данных. Важно понимать, что они являются динамическими структурами, которым, как и движу- щимся частям двигателя, нужны время от времени "чистка и смазка". Этот раздел содержит некоторые указания по поддержанию ваших индексов в рабо- чем состоянии в полном объеме. Действия по обслуживанию индексов Индексы являются двоичными структурами, которые могут стать разбалансирован- ными после многих изменений базы данных, особенно если вы пренебрегаете общим обслуживанием базы данных. Индексы могут быть сделаны сбалансированными4 множеством способов восстановления оптимального уровня производительности. ♦ Пересоздание индекса восстановит баланс его древовидной структуры за счет удаления устаревших записей, удаления и перемещения ветвей, созданных после- довательными добавлениями данных. Инструментом для переключения индекса между активным и неактивным состояниями является оператор alter index. ♦ Полное пересоздание индекса с нуля путем удаления и повторного создания в первоначальном виде может увеличить производительность индекса для очень большой или динамической таблицы. ♦ Восстановление базы данных из резервной копии gbak также пересоздает индекс в первоначальном состоянии. Улучшение селективности индекса Вообще говоря, селективность (избирательность) индекса — это оценочное количе- ство строк, которые могут быть выбраны при поиске по каждому значению индекса. Уникальный индекс имеет максимально возможную селективность, потому что он не может выбрать более одной строки для каждого значения, в то время как индекс для столбца boolean имеет практически самую низкую селективность. 4 На самом деле индексы в Firebird и Interbase никогда не бывают "разбалансированными", т. е. глубина индекса всегда постоянна для всех листовых страниц. Соответственно, пере- страивать индекс имеет смысл, когда он поврежден и когда требуется высокая скорость массо- вых вставок, удалений или обновлений. В реальных и больших базах данных плотность запол- нения страниц индекса не опускается ниже 85%, независимо от числа дубликатов ключей. — Прим. науч. ред.
Гпава 18. Индексы 361 Индексирование столбца, который будет хранить преимущественно одно значение (например, страна рождения в предыдущем примере), будет худшим решением, чем не индексирование такого столбца совсем. Firebird достаточно эффективен при соз- дании образа для неиндексированных сортировок и поисков. Измерение селективности Селективность уникального индекса равна I. Все неуникальные индексы имеют зна- чение меньше 1. Селективность (s) вычисляется как5 s = п / количество строк в таблице где п— количество различных экземпляров значения индекса в таблице. Чем меньше количество отличающихся экземпляров, тем меньше селективность. Индексы с более высокой селективностью выполняются лучше, чем индексы с низкой селектив- ностью. Оптимизатор Firebird отыскивает коэффициент для вычисления селективности при первом обращении к таблице и сохраняет его в памяти для использования при вы- числении планов при последующих запросах к этой таблице. Со временем вычислен- ные коэффициенты для часто изменяемых таблиц становятся устаревшими, возмож- но влияя на выбор оптимизатором индекса в экстремальных случаях. Пересчет селективности Пересчет селективности индекса изменяет статистический множитель, хранящийся в системных таблицах. Оптимизатор читает его один раз при выборе плана— он не является особенно значимым для его выбора. Часто большие операции DML не обя- зательно повреждают распределение различных значений ключа индекса. Если ин- дексирование разумно, то "демография" распределения значений может изменяться очень незначительно. Знание наиболее правильной селективности индекса имеет большое значение для разработчика. Это дает основу для определения полезности индекса. Если эффективность плана со временем снижается по причине большого количества добавлений или изменений ключевого столбца (столбцов), которые изменяют рас- пределение значений ключа, быстродействие запросов может постепенно снижаться. Любой индекс, чья селективность со временем резко падает, должен быть удален, потому что он влияет на производительность. Работа с неконтролируемым индексом, который ухудшается по мере роста таблицы до того, как он начинает влиять на планы запросов, является важной частью настрой- 5 Изложенное понятие селективности обратно числам, реально используемым оптимизато- ром. Оптимизатор берет информацию о селективности из столбца rdbSstatistics таблицы rdbSindiceS, который вычисляется по формуле 1/(число ключей - число повторяющихся ключей). Чем меньше это число, тем выше селективность индекса и тем полезнее он с точки зрения оптимизатора. Для уникальных индексов селективность стремится к нулю (зависит от числа ключей). Для неуникальных индексов худшим случаем является селективность, рав- ная 1, когда все значения ключей идентичны. — Прим. науч. ред.
362 Часть IV. База данных и ее объекты ки базы данных. При этом большинство критических эффектов использования ин- декса с фактически низкой селективностью практически не влияют на оптимизатор и оказывают сильное воздействие на геометрию индекса6. Почему низкая селективность наносит ущерб Для индексов Firebird создает двоичное дерево. Он хранит эти структуры на индекс- ных страницах, которые выделяются только для хранения индексных деревьев. Каж- дое значение в сегменте индекса имеет собственный узел за пределами корня дерева. Когда в индекс добавляется новая запись, она или помещается в новый узел, если ее значение не существует в индексе, или помещается в начало стека существующих дубликатов значений. Рис. 18.2 иллюстрирует этот двоичный механизм в простейшей форме Когда появляются дублирующие значения, они помещаются в первый узел в начало "цепи" других дубликатов — это то, что происходит со значением ghi на нашей диа- грамме. Такая структура называется цепочкой дубликатов. Цепочки дубликатов7 Цепочка дубликатов сама по себе является замечательной — все неуникальные ин- дексы имеют ее. Изменение сегмента значения или удаление строки является дорого- 6 На оптимизатор в основном влияет то, что с течением времени если индекс меняется (ме- няются индексируемые данные), то статистика индекса остается неизменной, т. к. она меняется только вручную вызовом оператора set statistics. Поэтому вместо рекомендуемого здесь периодического пересоздания индексов лучше озаботиться регулярным пересчетом статистики индексов. — Прим. науч. ред. 7 В этом месте разговор идет со слов Ann Harrison, "матери InterBase".
Глава 18. Индексы 363 стоящим, если цепочка дубликатов очень длинная. Одно из самого плохого, что вы можете сделать в базе данных Firebird — определить таблицу с миллионом строк, каждая из которых имеет одно и то же значение ключа для вторичного индекса, а затем удалить все эти строки. Последний сохраненный дубликат появляется в начале списка, а первый сохраненный дубликат — в конце. Обычно удаление начинается с первой сохраненной строки, затем удаляется вторая и т. д. Код обработки индекса будет проходить через всю цепь дубликатов для каждого удаления, всегда отыскивая нужную запись в самой последней позиции. Цитата Ann Harrison: "Это перемешивает кэш так, как вы никогда не видели". Затраты на все такие "перемешивания" и "взбалтывания" никогда не связаны с тран- закцией, которая удаляет или изменяет все строки в таблице. Изменение значения ключа или его удаление влияет на индекс позже, когда старые версии будут включе- ны в процесс сборки мусора. Затраты проявятся для следующей транзакции, обра- щающейся к этим строкам и выполняющейся после завершения всех транзакций, которые были активны, когда выполнялось изменение или удаление8. Инструментарий для индекса Стандартная поставка Firebird содержит множество инструментов и приемов для по- лучения состояния индексов и поддержания их в хорошей форме. ♦ Для получения значения селективности и других значимых характеристик индек- сов используйте анализатор статистики данных gstat. Позже в этой главе мы рас- смотрим, как gstat может рассказать вам о ваших индексах. ♦ Инструментом для пересчета селективности индекса является оператор set statistics (обсуждаемый в следующем разделе), set statistics не пересоздает индекс. ♦ Лучшей из всех инструментов для чистки индексов является утилита резервного копирования и восстановления gbak. Восстановление базы данных из самой по- следней резервной копии пересоздает все индексы и заново вычисляет их селек- тивность. Использование SET STATISTICS В некоторых таблицах количество дублирующих значений в индексированных столбцах может радикально увеличиваться или уменьшаться как результат относи- тельной "популярности" отдельных значений в индексе по сравнению с другими кан- дидатами в значения. Например, индексы по датам в системе продаж могут иметь тенденцию становиться менее селективными при резком увеличении деловой актив- ности. Периодическое вычисление селективности индекса может увеличить производи- тельность индексов, которые являются субъектами значительных изменений в рас- пределении различных значений. 8 Сборка мусора в больших цепочках дубликатов ключей значительно ускорена в Firebird 2.0. Собственно, в Firebird 2.0 изменена структура индексов. — Прим. науч. ред.
364 Часть IV. База данных и ее объекты Оператор set statistics заново вычисляет селективность индекса. Этот оператор может быть выполнен в интерактивной сессии isql или запущен в приложении ESQL. Для выполнения оператора set statistics вы должны быть соединены с базой дан- ных как пользователь, создавший индекс, как пользователь SYSDBA или (в POSIX) как пользователь с привилегиями операционной системы root. Синтаксис: SET STATISTICS INDEX имя; Следующий оператор заново вычисляет селективность индекса в базе данных employee.gdb: SET STATISTICS INDEX MINSALX; Сам по себе оператор set statistics не решает текущие проблемы, являющиеся ре- зультатом предыдущего использования индекса, которые связаны с устаревшей ста- тистикой селективности, потому что он не пересоздает индекс. СОВЕТ. Для пересоздания индекса используйте alter index, или удалите и за- ново создайте его, или восстановите базу данных с резервной копии. Получение статистики по индексу Firebird предоставляет утилиту командной строки, которая отображает статистиче- ские отчеты о состоянии объектов в базе данных. Этот инструмент создает множест- во отчетов о том, что происходит в базе данных. Основное внимание в этом разделе мы уделяем статистике по индексу. Остальные отчеты описываются после отчетов по индексу. Инструмент командной строки gstat Вам нужно запускать gstat на серверной машине, потому что это полностью локаль- ная программа, которая не имеет доступа к базам данных как клиент. Ее размещение по умолчанию — каталог /bin в вашем каталоге инсталляции Firebird. Она выдает информацию об указанной базе данных и может быть использована SYSDBA или владельцем базы данных. gstat в POSIX Поскольку gstat обращается к файлам базы данных на уровне файловой системы, на платформах Linux и UNIX необходимо на уровне системы иметь доступ на чтение к этим файлам. Вы можете его получить одним из следующих способов. ♦ Соединитесь с той учетной записью, под которой выполняется сервер (по умолча- нию пользователь firebird в версии 1.5, root или interbase в версии 1.0.x). ♦ Установите разрешение на чтение для вашей группы к файлу базы данных.
Глава 18. Индексы 365 Интерфейс gstat В отличие от некоторых других инструментов командной строки gstat не имеет сво- его интерфейса командной строки. Каждый запрос заключается в вызове gstat с пере- ключателями. Синтаксис: gstat [переключатели] имя-базы-данных где имя-базы-данных— полный путь к базе данных. Графические инструменты gstat не является дружественным пользователю инструментом. Некоторые графиче- ские инструменты (например, IBAnalyst) четко выполняют ту же работу gstat по вы- воду результатов, используя Services API. Формы экранов были взяты из утилиты с открытыми кодами IBOConsole9. Переключатели В табл. 18.1 описаны переключатели gstat. Таблица 18.1. Переключатели gstat Переключатель Описание -user имя-пользователя Проверяет имя пользователя перед доступом к базе данных -password] пароль Проверяет пароль перед доступом к базе данных -header Выводит информацию заголовочной страницы, затем прекра- щает работу -log Выводит информацию заголовочной страницы и страницы про- токола, затем прекращает работу -index Отыскивает и отображает статистику по индексам базы данных -data Отыскивает и отображает статистику по таблицам пользователя базы данных -all Это значение по умолчанию, если вы не запросили -index, -data или -all. Отыскивает и отображает статистику по -index и-data -system Как и -all, но дополнительно включает статистику по систем- ным таблицам -r Отыскивает и отображает статистику по размеру и версиям записей (включая все версии) 9 IBOConsole поддерживает Lorenzo Mengoni — см. http://www.mengoni.it
366 Часть IV. База данных и ее объекты Таблица 18.1 (окончание) Переключатель Описание -t список-таблиц Используется вместе c -data. Ограничивает отображаемые данные таблицами из списка таблиц -z Печатает версию утилиты gstat Рекомендуется назначить вывод результатов в текстовый файл и просматривать его в текстовом редакторе. < ПРИМЕЧАНИЕ. Поскольку gstat выполняет собственный анализ на уровне фай- ла, она не выполняется в контексте какой-либо транзакции. Следовательно, ста- ..___ тистика по индексам также включает информацию по тем индексам, которые ис- пользуются в незавершенных транзакциях. Например, не будет предупреждений, если отчет показывает некоторые дублирующие записи в индексе первичного ключа. Переключатель -index Синтаксис: gstat -i[ndex] база-данных Этот ключ отыскивает и отображает статистику по индексам в базе данных: средняя длина ключа (в байтах), общее количество дубликатов и максимальное количество дубликатов одного ключа. Включите переключатель -sfystem], если вам нужна ин- формация о системных индексах. К сожалению, не существует способа получить статистику по одному индексу, одна- ко вы можете ограничить результат одной таблицей, используя переключатель -t, за которым следует имя таблицы. Вы можете записать разделенный пробелами список имен таблиц для получения результатов более чем по одной таблице. Если имена ваших таблиц являются чувствительными к регистру — были объявлены идентифи- каторами, заключенными в кавычки, — то аргументы переключателя -t должны быть записаны в правильном регистре, но не должны заключаться в кавычки. Для таблиц с пробелами в их именах gstat вовсе не работает. Вы можете добавить переключатель -system, чтобы включить сведения о системных индексах в отчет. Чтобы запустить утилиту для базы данных employee и направить ее вывод в тексто- вый файл с именем gstat.index.txt, выполните следующее: ♦ в POSIX наберите (все в одной строке): ./gstat -index /opt/firebird/examples/employee.fdb -t CUSTOMER -user SYSDBA -password masterkey > /opt/firebird/examples/gstat.index.txt
Глава 18. Индексы 367 ♦ в Win32 наберите (все в одной строке): gstat -index "c:\Program Files\Firebird\Firebird_l_5\examples\employee.fdb" -t CUSTOMER -user SYSDBA -password masterkey > "c:\Program Files\Firebird\Firebird_l_5\examples\gstat.index.txt" ПРИМЕЧАНИЕ. Двойные кавычки для пути к базе данных требуются в Windows, если ваш путь содержит пробелы. На рис. 18.3 показано, как отображаются данные индексной страницы. ^Database Statistic* CUSTOMER (13'.’) Index CUSTNAMEX (>) Depth: 1, leaf buckets: 1, nodes: 15 Average data length; 15.00, total dup О Index CUSTREGION (3) Depth: i, leaf buckets: .1, nodes: .15 Average data length: 17.00, total dup: Fill distribution: 0 Readout max dup: О max dup: О Рис. 18.3. Пример отображения данных индексной страницы Что все это значит Вначале появляется итоговая информация об индексе. В табл. 18.2 объясняются записи строка за строкой. Поскольку утилита gstat выполняет свой анализ на уровне файла, она не использует концепции транзакции. Следовательно, статистика по индексам также включает ин- формацию по тем индексам, которые используются в незавершенных транзакциях.
368 Часть IV. База данных и ее объекты Таблица 18.2. Вывод gstat -i[ndex] Элемент Описание Index Имя индекса Depth Количество уровней в странице индексного дерева. Если глубина дерева индексной страницы превышает 3, то доступ к записям через индекс не будет максимально эффективным. Для уменьшения глу- бины дерева индексной страницы увеличьте размер страницы. Если увеличение размера страницы не уменьшает глубины, снова уве- личьте размер страницы Leaf buckets Количество страниц самого низкого уровня (листовых) в дереве ин- декса. Это страницы, которые содержат указатели на записи. Стра- ницы высокого уровня содержат косвенные связи Nodes Общее количество записей, индексированных в дереве. Должно быть равно количеству индексированных строк в дереве, хотя отчет gstat может включать узлы, которые были удалены, но не вычищены в процессе сборки мусора. Может также включать множество элемен- тов для записей, у которых был изменен индексный ключ Average data Length Средняя длина каждого ключа в байтах. Обычно имеет много мень- шее значение, чем длина объявленного ключа, потому что выполня- ется сжатие суффикса и префикса Total dup Общее количество строк дубликатов индекса Max dup Количество дублирующих узлов в "цепочке", имеющих наибольшее количество дубликатов. Всегда будет нулем для уникальных индек- сов. Если число велико по сравнению с числом в Total dup, то это признак плохой селективности Average fill Это гистограмма с пятью 20-процентными полосами, каждая из кото- рых показывает количество индексных страниц, чей средний процент заполнения находится в указанном диапазоне. Процент заполнения определяется соотношением пространства каждой страницы, содер- жащей данные. Сумма таких чисел дает общее количество страниц, содержащих индексные данные Глубина индексов Индекс является древовидной структурой со страницами на одном уровне, ссылаю- щимися на страницы другого уровня и т. д. вплоть до страниц, указывающих на строки данных. Чем больше глубина, тем больше косвенных уровней. Строка Leaf bucket описывает страницы индекса самого низкого уровня, которые указывают на индивидуальные строки. На рис. 18.4 корневая страница индекса (создаваемая при создании базы данных) хранит указатель для каждого индекса и указатель на другую страницу указателей, которая содержит указатели этого индекса. Такая страница последовательно указы- вает на страницы, содержащие данные — фактические данные узлов — либо непо- средственно (глубина равна 1), либо косвенно (добавляя один уровень для каждого косвенного уровня).
Глава 18. Индексы 369 Н More i node , — ijpointers Index data (leaf) Рис. 18.4. Глубина индекса Два фактора оказывают влияние на глубину: размеры страницы и ключа. Если глу- бина больше 3 и размер страницы меньше 8192, то увеличение размера страницы до 8192 или 16 386 должно уменьшить количество косвенных уровней и увеличить ско- рость. СОВЕТ. Вы можете вычислить приблизительный размер (в страницах) цепочки мах dup на основании статистических данных. Для получения количества узлов на странице разделите узлы (nodes) на количество листьев (leaf buckets). Умноже- ние результата на максимальное количество дублирующих узлов (max dup) дает приблизительное количество страниц. Анализ некоторой статистики Следующие выдержки являются выводом gstat -index для базы данных с плохой производительностью. Анализ 1 Первый из поддерживаемых индексов, который был создан автоматически для внеш- него ключа: Index RDB$FOREIGN14 (3) Depth: 2, leaf buckets: 109, nodes: 73373 Average data length: 0.00, total dup: 73372, max dup: 32351 Fill distribution: 80 - 99% = 109 Строка Depth: 2, leaf buckets: 109, nodes: 73373 сообщает нам, что нижний уровень индекса имеет 109 листьев (страниц), количество узлов 73 373. Это может и не быть общим количеством строк таблицы. С одной стороны, утилита gstat ничего не знает о транзакциях, поэтому она не может сообщить, найдены ли подтвержденные или неподтвержденные страницы. С другой — столбцы могут иметь значения null и не будут попадать в статистику. Нижний уровень индекса— где хранятся узлы листьев— имеет всего 109 страниц. Кажется подозрительным наличие малого количества страниц для такого большого количества строк. Следующая статистика объясняет, почему. В строке Average data length: 0.00, total dup: 73372, max dup: 32351 ЧИСЛО max dup указывает длину самой длинной цепочки дубликатов, подсчитанной почти для поло- вины узлов. Число total dup говорит, что каждый узел, за исключением одного, яв- ляется дубликатом.
370 Часть IV. База данных и ее объекты Это самый классический случай, когда проектировщик применяет внешний ключ без рассмотрения его распределения. Вероятно, это столбец стиля boolean или таблица соответствия (lookup) либо с очень небольшим количеством значений, либо практи- чески с одним значением. Примером этому было приложение формирования списка избирателей, которое со- храняло столбец страны проживания. Избирателей было приблизительно 3 миллиона, а регистрация была принудительной. База данных имела таблицу country, содержа- щую более 300 стран, снабженную ключами в кодах стран CCCIT. Она присутство- вала чуть ли не в каждой таблице базы данных в качестве внешнего ключа. Беда была в том, что почти все избиратели жили в одной стране. Средняя длина данных (Average data length) — это средняя длина хранимого ключа. Здесь мало что можно сделать с объявленной длиной. Нулевое значение средней длины означает лишь то, что в процессе сжатия не осталось "пищи" для вычисления среднего значения. Строка Fill distribution показывает, что все 109 страниц находятся в диапазоне 80—99 процентов, что является хорошим заполнением. Распределение заполнения является долей пространства каждой страницы, используемой для данных и указате- лей. От восьмидесяти до девяноста процентов — это хорошо. Меньшее распределе- ние заполнения является весьма серьезным напоминанием, что вы должны пересоз- дать индекс. Анализ 2 Следующий пример показывает статистику сгенерированного системой индекса для первичного ключа в той же таблице: Index RDBSPRIMARY10 (0) Depth: 3, leaf buckets: 298, nodes: 73373 Average data length: 10.00, total dup: 0, max dup: 0 Fill distribution: 0 - 19% = 1 80 - 99% = 297 Длина ключа 10 означает, что выполнено некоторое сжатие. Это нормально и хоро- шо. То что одна строка мало заполнена— вполне нормально: количество узлов не соответствует точно страницам. Анализ 3 Эта база данных имеет маленькую таблицу, хранящую временные данные для про- верки достоверности. Она периодически очищается и наполняется снова. Следующая статистика генерируется для внешнего ключа этой таблицы: Index RDB$FOREIGN263 (1) Depth: 1, leaf buckets: 10, nodes: 481 Average data length: 0.00, total dup: 480, max dup: 480 Fill distribution: 0 - 19% = 10
Глава 18. Индексы 371 Total dup и max dup идентичны — каждая строка имеет одинаковое значение в ключе индекса. Селективность не может быть хуже этой. Уровень заполнения, очень низкий для всех страниц, наводит на мысль о разнородных удалениях. Если бы это не было маленькой таблицей, такой индекс был бы ужасен. Данная таблица — очередь обработки — очень динамична, она хранит до 1000 новых строк в день. После проверки данных строки переносятся в порождающие таблицы, а строки разрабатываемой таблицы удаляются, приводя к замедлению работы системы. Частое резервное копирование и восстановление базы данных необходимо, чтобы дела шли нормально. Проблема в том, что в этом случае следует избегать внешних ключей, и если они яв- ляются необходимыми, то их можно реализовать с помощью триггеров, созданных пользователем. Однако, если проектирование базы данных, безусловно, требует ограничений внеш- них ключей для временных таблиц со столбцами низкой селективности, существуют рекомендованные способы уменьшения накладных расходов и снижения ухудшения состояния индексных страниц, являющихся следствием удаления и дальнейшего на- полнения данными таблицы. Отслеживайте уровень заполнения проблемных индек- сов и принимайте меры, когда он упадет ниже 40%. Выбор действий зависит от ва- ших требований. ♦ Если возможно, удаляйте все строки за один раз, а не выполняйте их удаление одну за другой в случайном порядке. Удалите ограничение внешнего ключа, уда- лите строки и подтвердите транзакцию. Заново создайте ограничение. Поскольку это не длинная транзакция, задерживающая сборку мусора, новый индекс будет полностью пустым. ♦ Если удаления должны быть последовательными, выберите время, чтобы полу- чить исключительный доступ и использовать alter index для пересоздания ин- декса. Это будет более быстро и предсказуемо, чем инкрементная сборка мусора в огромной цепочке дубликатов. Другие переключатели gstat Статистика утилиты gstat может предоставить полезную информацию о других дей- ствиях с базой данных. Переключатель -header Эта строка gstat -header база-данных отображает суммарную информацию заголовочной страницы базы данных. На рис. 18.5 показан пример. Первая строка отображает имя и размещение первичного файла базы данных. Сле- дующие строки содержат информацию из заголовочной страницы базы данных. В табл. 18.3 описывается этот вывод.
372 Часть IV. База данных и ее объекты Рис. 18.5. Пример вывода заголовочной страницы утилитой gstat Таблица 18.3. Вывод gstat -h leader] Элемент Описание Flags Флаги Checksum Контрольная сумма заголовочной страницы. В прототипе (InterBase) это было уникальное значение, вычислявшееся по всем данным заголовочной страницы. В Firebird это всегда 12 345. Когда заголо- вочная страница сохраняется на диске, а затем считывается, кон- трольная сумма найденной страницы сравнивается с 12 345, и если они не соответствуют, то вызывается ошибка контрольной суммы. Это перехватывает некоторые виды физического разрушения Generation Каждый раз увеличивается на единицу, когда заголовочная страни- ца записывается на диск Page size Текущий размер страницы базы данных в байтах ODS version Версия структуры на диске (ODS) для базы данных. Это будет 10 для версии 1,0.x и 10.1 для версии 1.5 Oldest transaction Идентификатор самой старой "заинтересованной" транзакции. Ин- формацию об этом см. в главе 25 Oldest active Идентификатор самой старой активной транзакции
Гпава 18. Индексы 373 Таблица 18.3 (окончание) Элемент Описание Oldest snapshot Идентификатор самой старой транзакции, которая не является в настоящий момент подходящей для сборки мусора (т. е. эта и дру- гие, более поздние транзакции не являются для этого подходящи- ми) Next transaction Идентификатор, который Firebird назначит следующей транзакции. Разница между самой старой активной транзакцией и следующей транзакцией определяет, когда начнется чистка базы данных10. Значение по умолчанию 20 000. См. разд. "Гигиена базы данных" главы 15 Bumped transaction Теперь устарело Sequence number Последовательный номер заголовочной страницы. Всегда ноль Next connection ID Номер идентификатора следующего соединения с базой данных Implementation ID Архитектура аппаратуры, на которой была создана база данных Shadow count Количество наборов оперативных копий для базы данных Number of cache Размер в страницах кэша базы данных. Ноль означает, что база buffers данных использует значение по умолчанию сервера (DefauitDbCachePages в firebird.config, default_cache_pages в ibconfig/isc_config для версии 1.0.x) Next header page Номер страницы следующей заголовочной страницы — хотя, похо- же, это не поддерживается Database dialect Диалект SQL базы данных Creation date Дата создания базы данных или последнего восстановления из резервной копии Attributes force write означает режим принудительной записи. no_reserve указывает, что на страницах не резервируется место для старых версий данных. Это позволяет более плотно упаковы- вать данные на каждой странице, в силу чего база данных занимает меньше дискового пространства. Это идеал для баз данных только для чтения. shutdown означает, что работа с базой данных запрещена для всех пользователей, кроме SYSDBA Variable header data Интервал очистки (sweep interval). Информация о вторичных фай- лах (если присутствуют) 10 Это неверно, и автор подтвердил ошибку. Next transaction не имеет никакой связи со sweep. Автоматический sweep стартует, когда разница между Oldest Snapshot и Oldest Interesting больше Sweep interval (в Firebird 2.0 за верхнюю границу берется не Oldest Snapshot, a Oldest Active). —Прим. науч. ped.
374 Часть IV. База данных и ее объекты Переключатель -data Следующая строка gstat -data база-данных просматривает в базе данных таблицу за таблицей, отображая итоговую информацию о страницах данных. Для включения в отчет системных таблиц (rdb$xxx) добавьте переключатель -system. На рис. 18.6 показан пример вывода. CUSTOMER (137) Primary pointer page: 190, Index root page: 191 Data pages: 1, data page slots: Г, average fill: 26% Fill distribution: 0 - 19% = 0 2 0 - 3 9 % - 1 40 - 59% = 0 60 - 79% = 0 80 - 99% = 0 DEPARTMENT (130) Primary pointer page: 144, Index root page: 145 Data pages: 1, data page slots: 1, average fill: 24% Fill distribution: 0 - 19% = 0 20 - 39% = 1 40 - 59% = 0 60 - 79% = 0 80 - 99% = 0 EMPLOYEE (131) Рис. 18.6. Пример итогового вывода по страницам данных в gstat Вывод в командной строке аналогичен. COUNTRY (31) Primary pointer page: 190, Index root page: 19 Data pages: 1, data page slots: 1, average fill: 26% Fill distribution: 0 - 19% = 0 20 - 39% = 1 40 - 59% = 0 60 - 79% = 0 80 - 99% = 0 Для каждой таблицы базы данных отображаются числа, показанные в табл. 18.4.
Гпава 18. Индексы 375 Таблица 18.4. Вывод gstat -d[ata] Элемент Описание Primary pointer page Номер первой страницы косвенных указателей на страницы, хра- нящие данные таблицы Index root page Номер страницы, которая является первой страницей указателей на индексы таблицы Data pages Общее количество страниц, в которых хранятся данные таблицы. Этот счетчик включает страницы, хранящие неподтвержденные версии записей и мусор, потому что gstat не может их отличить друг от друга Data page slots Количество указателей на страницы базы данных, содержащихся на страницах указателей. Должно равняться числу страниц данных Average fill Это гистограмма из пяти 20-процентных "полос", каждая из которых показывает количество страниц данных, чье среднее заполнение попадает в этот диапазон. Процент заполнения определяется соот- ношением пространства каждой страницы, содержащей данные. В нашем примере среднее заполнение низкое, потому что база дан- ных employee.gdb содержит небольшие структуры записей, и их не так много. Сумма этих чисел дает общее количество страниц, со- держащих данные Fill distribution Обобщающая гистограмма распределения использования памяти для всех страниц, выделенных в таблице. В нашем примере пока используется только одна страница, и она имеет менее 40% запол- нения Ограничение вывода для gstat -data Если вам не нужен отчет по данным для всех таблиц, вы можете использовать пере- ключатель -t для задания списка таблиц, интересующих вас. Синтаксис: gstat -data база-данных -t имя-таблицы! [имя-таблицы2 [ имя-таблицыЗ ..]] ПРИМЕЧАНИЕ. Имена таблиц должны быть набраны в верхнем регистре. К со- жалению, gstat не поддерживает переключатель -t[able-list] для баз данных, которые используют чувствительные к регистру идентификаторы таблиц, заклю- ченные в кавычки. Переключатель -Records] Следующая строка gstat -г база-данных отображает статистику по размерам и версиям записей. ♦ Для строк: среднее значение длины строк в байтах и общее количество строк в таблице.
376 Часть IV. База данных и ее объекты ♦ Для старых версий: среднее значение длины версий в байтах, общее количество версий в таблице и максимальная цепочка версий для записи. Общее количество строк в таблице может включать активные и зависшие транзак- ции. Длина записей и версий применима к фактическим данным пользователя — длина не использует счетчик в заголовке, который предшествует каждой версии записи. Пример Три таблицы (cauldron, cauldroni и cauldron2) имеют одинаковые метаданные и объем 100 000 записей. Номинальная, несжатая длина записи 900 байт. cauldron — чистая таблица без старых версий строк. Средняя длина хранимой строки 121 байт — приблизительно 87% сжатия. cauldroni имеет активную, все еще выполняющуюся транзакцию: DELETE FROM CAULDRONI; Каждая строка имеет нулевую (0.00) длину, потому что первичная запись является заглушкой удаления (delete stub), которая содержит только заголовок строки. Все под- твержденные записи были восстановлены как старые версии и сжаты до того же раз- мера, который они имели, когда были первичными записями. Таблица содержит то же количество страниц (4000), что и до операции delete. Средний коэффициент за- полнения составляет от 85 до 95% для размещения всех заглушек удаления. cauldron2 имеет активную, все еще выполняющуюся транзакцию: UPDATE CAULDRON2 SET F2OFFSET =5.0 Измененные записи увеличили размер на 2 байта (с 121 до 123), что можно отнести к более низкому уровню сжатия. Значение 5.0 заменило большинство отсутствующих или нулевых значений, что сде- лало значение этого поля отличным от других полей. Теперь существует 100 000 ста- рых версий со средним значением 10 байт каждая. Средний коэффициент заполнения увеличен до 99%, а таблица выросла с 138 до 4138 страниц. > gstat proj.gdb -г -t CAULDRON CAULDRONI CAULDRON2 Analyzing database pages . . . CAULDRON (147) Primary pointer page: 259, Index root page: 260 Average record length: 120.97, total records: 100000 Average version length: 0.00, total versions: 0, max versions: 0 Data pages: 4000, data page slots: 4000, average fill: 85% Fill distribution: 0 -19%=0 20 -39%=0 40 -59%=0 60 -79%=0 80 -99%=4000
Глава 18. Индексы 377 CAULDR0N1 (148) Primary pointer page: 265, Index root page: 266 Average record length: 0.00, total records: 100000 Average version length: 120.97, total versions: 100000, max versions: 1 Data pages: 4000, data page slots: 4000, average fill: 95% Fill distribution: 0 -19%=0 20 -39%=0 40 -59%=0 60 -79%=0 80 -99%=4000 CAULDRON2 (149) Primary pointer page: 271, Index root page: 272 Average record length: 122.97, total records: 100000 Average version length: 10.00, total versions: 100000, max versions: 1 Data pages: 4138, data page slots: 4138, average fill: 99% Fill distribution: 0 -19%=0 20 -39%=0 40 -59%=0 60 -79%=0 80 -99%=4138 СОВЕТ. Для API-программистов функция API Firebird isc_database_info() и Services API предоставляют элементы, которые делают возможным синхронизи- ровать выполнение поиска и получение статистики по базе данных в ваших про- граммах приложений. Для программистов Delphi, C++ Builder и Kylix доступны различные наборы компонентов для получения статистики базы данных. Пора дальше Здесь мы завершаем обсуждение объектов базы данных и перемещаем фокус на язык создания и изменения данных. Наше исследование начинается с общего взгляда на SQL в терминах стандартов и как реализация Firebird разбивается на множество пе- рекрывающихся подмножеств. После этого в главе. 20 мы начинаем более подробное рассмотрение данных как множеств и операторов языка манипулирования данными (Data Manipulation Language, DML) для определения данных и манипуляций с ними.

ЧАСТЬ V Firebird SQL Глава 19. Язык SQL в Firebird Глава 20. Запросы DML Глава 21. Выражения и предикаты Глава 22. Запросы к множеству таблиц Глава 23. Упорядоченные и агрегатные наборы Глава 24. Просмотры

ГЛАВА 1 9 Язык SQL в Firebird SQL (произносится "эс кю эль"1) — это подъязык данных для доступа к реляционным системам управления базами данных. Его элементы, синтаксис и поведение стандар- тизовали ANSI и ISO в 1986 году. С этого времени SQL пересматривался три раза: SQL-89 (опубликован в 1989 г.), SQL-92 (1992 г. или около этого) и самый последний SQL 3, рабочая версия, опубли- кованная как часть SQL-99. Язык запросов стандарта SQL является непроцедурным языком, т. е. он ориентиро- ван на результаты операций, а не на способы получения результатов. Его назначе- ние — быть основой для подъязыка, используемого в рамках включающего языка программирования. Цель стандартов — описать механизм внешнего уровня, посред- ством которого заданный запрос вернет предсказуемый результат, независимо от то- го, как сервер базы данных внутренне манипулирует данными. Следовательно, стандарт предписывает в мельчайших подробностях способ задания элементов языка и интерпретацию логики операций, при этом он не задает правил, каким образом разработчики системы управления базами данных должны все это реализовывать. Ожидается "согласованная" реализация базового набора возможно- стей, и эта реализация может также включать другие возможности, сгруппированные и стандартизованные на уровнях выше "начального уровня". Firebird и стандарты Соответствие стандарту — это, скорее, вопрос уровня соответствия, а не абсолют. Разработчики могут свободно реализовывать возможности языка, не описанные в стандарте. Соответствие касается способов реализации возможностей, распознавае- мых стандартом и описанных в нем (если таковые есть). Соответствие изменяется обратно пропорционально с количеством стандартных возможностей, внедряемых типично. Язык SQL Firebird близко соответствует стандартам SQL-92 (1SO/IEC 9075:1992) на начальном уровне (entry level). Firebird вводит множество возможностей в соответст- вии с более поздним релизом стандарта SQL-99. Хотя SQL в Firebird близко соответ- ствует стандартам, существуют небольшие отличия. * Американцы предпочитают говорить "сиквел". —Прим, перев.
382 Часть V. Firebird SQL Операторы SQL Оператор SQL используется для выполнения запроса к базе данных. Язык запросов выражается в операторах, которые задают цель: что должно быть сделано (операция), объекты, с которыми это должно быть сделано, и детализация, как это должно быть сделано. По теории каждое возможное взаимодействие между внешним миром и ба- зой данных осуществляется через синтаксис оператора. Синтаксические конструкции операторов группируются в соответствии с двумя ос- новными целями. ♦ Операторы create, alter и drop над объектами метаданных (также называемые объектами схемы или элементами схемы). Язык таких запросов называется язы- ком определения данных (Data Definition Language, DDL). ♦ Операторы, выполняющие действия над данными. Они представляют язык для определения наборов данных в виде столбцов и строк и задают операции для: • поиска и преобразования (select) образов таких наборов данных из хранилища данных для чтения приложениями; • изменения состояния базы данных путем добавления, изменения и удаления указанных наборов (операторы insert, update и delete). Такие операторы, которые выполняют действия над данными, относятся к языку, называемому языком манипулирования данными (Data Manipulation Language, DML). Реализация в Firebird языка SQL разбивается на несколько пересекающихся подмно- жеств, каждое из которых используется для специальных целей и включает свои соб- ственные языковые расширения. ♦ Встраиваемый SQL (Embedded SQL, ESQL) — это "базовая" реализация SQL, со- стоящая из синтаксиса DDL и DML, которые включаются и в другие подмножест- ва, где это возможно. Это было первоначальной реализацией SQL в предшествен- нике InterBase, разработанной для включения в клиентские приложения и тре- бующей препроцессора GPRE. ♦ Динамический SQL (Dynamic SQL, DSQL) — это подмножество наиболее часто используется сегодня. Оно используется во всех программах интерфейса с базой - данных, которые общаются с сервером через интерфейс прикладного программи- рования (Application Programming Interface, API). Некоторые команды DDL, дос- тупные в ESQL, которые не могут быть реализованы в DSQL, заменяются вызо- вами функций API. ♦ Интерактивный SQL (Interactive SQL, ISQL)— это язык, реализованный в утили- те командной строки isql. Он основан на DSQL с расширениями просмотра мета- данных и некоторой системной статистики, а также для управления сессиями isql. ♦ Процедурный SQL (Procedural SQL, PSQL) — это язык для написания хранимых процедур и триггеров. Он состоит из всех операторов DML с добавлением множе- ства процедурных расширений.
Гпава 19. Язык SQL в Firebird 383 Язык определения данных (DDL) При определении метаданных для использования в базе данных Firebird мы исполь- зуем лексикон операторов и параметров стандартов SQL для создания объекта с его типом и именем — или идентификатором, — а также для задания и изменения его атрибутов. В этом лексиконе также присутствуют операторы для удаления объектов. Запросы с использованием DDL зарезервированы для целей определения метадан- ных, следовательно: ♦ аккуратно управляйте ими, если вы реализуете их использование в приложениях конечного пользователя; ♦ ожидайте ошибок при компиляции, если вы собираетесь их использовать в хра- нимых процедурах. Язык DDL в Firebird описан в частях III и IV. Определение просмотров, предоставле- ние и отмена привилегий SQL также относятся к DDL. Просмотры, которые объеди- няют операторы DDL и DML, обсуждаются в главе 24. Определение и поддержка разрешений SQL описывается в главе 35. Язык манипулирования данными (DML) Операторы DML, их синтаксис и выражения для поиска и манипулирования набора- ми данных являются предметом рассмотрения этой части книги. ♦ В главе 20 вводится концепция наборов, структура и синтаксис запросов DML. Глава включает разд. "Тема оптимизации", где рассматривается работа с оптими- затором запросов. ♦ Глава 21 описывает функции, операции и выражения, порядок их использования. ♦ В главе 22 рассматриваются запросы, которые оперируют с множеством таблиц при использовании соединений (joins), подзапросов (sub-queries) и объединений (unions). ♦ В главе 23 рассматриваются синтаксис и вопросы определения наборов, которые требуют сортировки или группирования, а также запросы, отыскивающие строки в нескольких таблицах без использования соединений. ♦ Глава 24 содержит описание и использование просмотров, а также других насле- дуемых объектов типа таблиц. Возможности встраиваемого языка (ESQL) Некоторые реляционные системы управления базами данных, включая Firebird, пре- доставляют возможность непосредственного включения операторов SQL в модули, написанные на языке программирования третьего поколения. Стандарт предоставля- ет концептуальные алгоритмы, по которым будет выполняться программирование приложений, но не задает никаких правил реализации. Возможность программирования приложений со встроенным SQL в Firebird включа- ет подмножество операторов, подобных операторам SQL, и конструкции, которые
384 Часть V. Firebird SQL могут быть включены в исходный код программы для обработки препроцессором перед компиляцией кода. Конструкции встроенного языка SQL называются встроен- ным SQL (ESQL). Операторы ESQL не могут генерироваться динамически. ESQL недопустим в хранимых процедурах и триггерах, а процедурный язык (PSQL) недопустим во встроенном SQL. ESQL может выполнять хранимые процедуры. ESQL используется в программах, написанных на традиционных языках программи- рования, таких как С или COBOL. Каждому оператору ESQL должен предшествовать SQL-оператор ехес. Препроцессор GPRE преобразует операторы ESQL в структуры данных используемого языка программирования и вызовы сервера Firebird. Более подробную информацию см. в "Embedded SQL" (Встраиваемый SQL) в доку- ментации Borland по InterBase 6.x или EmbedSQL.pdf в соответствующем комплекте электронной документации2. Динамический в сравнении со статическим SQL Операторы SQL, включенные в код и обработанные препроцессором, иногда назы- ваются статическим SQL. В отличие от них операторы, которые генерируются кли- ентским приложением и передаются для выполнения на сервер во время работы, на- зываются динамическим SQL (DSQL). Если вы не пишете код для приложений ESQL, вы используете DSQL. Операторы, выполняемые в утилите интерактивного SQL (isql) или в другой интерактивной ути- лите, являются операторами DSQL, таким же образом они обрабатываются в клиент- ских приложениях, которые используют напрямую или опосредованно API (через драйверы доступа к базе данных, такие как ODBC, JDBC или BDE). В приложениях с Embedded SQL статический SQL позволяет передавать запросы в API Firebird, вместо того, чтобы использовать препроцессор и макросы для формиро- вания структур API. Поскольку весь запрос полностью подготовлен, он может вы- полняться быстрее динамических операторов, которые передаются серверу, где вы- полняется их синтаксический анализ и подготовка во время выполнения программы. Разновидности подмножеств языка Преднамеренно или случайно существуют некоторые незначительные отличия меж- ду подмножествами языка SQL в Firebird. ♦ Формат некоторых обычных операторов SQL может слегка изменяться в статиче- ском и динамическом вариантах SQL. ♦ Терминаторы операторов могут изменяться в разных подмножествах языка: • операторы PSQL и ESQL завершаются точкой с запятой; • в операторах DSQL, передаваемых через структуры API, терминаторы от- сутствуют; 2 Рекомендую обратиться на http://www.interbase-world.com. Там можно найти перевод на русский язык некоторых книг из документации фирмы Borland по InterBase 7.1. — Прим, пе-
Гпава 19. Язык SQL в Firebird 385 • операторы DSQL, вводимые в интерактивной утилите запросов isql требуют терминаторов, которые могут быть установлены при использовании set term в любой печатаемый символ из первых 127 символьного подмножества ASCII. Интерактивный SQL (ISQL) Инструмент интерактивных запросов isql использует операторы DSQL вместе с дву- мя подмножествами расширенных команд (группы set ххх и show ххх), которые по- зволяют интерактивно выполнить некоторые установки и запросы к схеме соответст- венно. Некоторые команды set могут быть также включены в скрипты определения данных (скрипты DDL для пакетного выполнения в isql) и в Embedded SQL. Подробную информацию о подмножестве языка isql см. в главе 37. Процедурный язык (PSQL) Стандарт не описывает возможности процедурного языка, поскольку в принципе предполагает, что общие задачи программирования будут решены с использованием языка программирования. Не существует спецификаций для конструкций языка по манипулированию, вычислению или программному созданию данных внутри систе- мы управления базами данных. Те реляционные СУБД, которые поддерживают программирование на сервере, обыч- но предоставляют форматы и синтаксис, подобные операторам SQL, для расширения SQL. Каждый разработчик СУБД свободен предоставлять свои собственные вариан- ты таких конструкций. Обычно такие модули в базе данных называются хранимыми процедурами. Firebird предоставляет такие конструкции в виде процедурного языка (иногда назы- ваемого PSQL), который является набором расширений SQL, используемых про- граммистами вместе с вариантом языка ESQL для написания исходных кодов для хранимых процедур и триггеров. Расширения PSQL включают управление потоком выполнения, условные выражения и средства обработки ошибок. Язык включает уникальную возможность генерации многострочных выходных наборов, к которым может быть осуществлен прямой доступ с использованием операторов select. Из языка исключены некоторые конструкции SQL, в частности все операторы DDL. При этом в Firebird 1.5 и выше в PSQL поддерживается синтаксис execute statement для выполнения команд DSQL, включающих некоторые операторы DDL. PSQL для хранимых процедур и триггеров подробно описывается в части VII. Диалекты SQL В Firebird каждый клиент и база данных имеют диалект SQL, атрибут, указывающий серверу, как интерпретировать возможности и элементы, которые реализованы по- разному в базах данных Borland InterBase до версии 6. 13 Зак. 420
386 Часть V. Firebird SQL Диалект позволяет серверу Firebird распознавать, принимать и корректно обрабаты- вать более ранние возможности и элементы баз данных (диалект 1), получать доступ к этим старым данным для их преобразования в новые возможности и элементы (диалект 2) или использовать полный набор возможностей, элементов и правил Fire- bird для конвертированных или вновь создаваемых баз данных (диалект 3). Возможно создание новой базы данных в Firebird диалекта 1 или диалекта 3. Не ре- комендуется создавать новые базы в диалекте 1, поскольку, в конечном счете, пре- кратится его поддержка. Невозможно создание базы данных диалекта 2, потому что диалект 2 предназначен для конвертирования баз данных диалекта! в диалект 3. Диалект 2 может быть применен только к клиентскому соединению. Ресурсы SQL Приложение 1 содержит алфавитный список описаний внешних функций. Используйте алфавитный указатель в конце этой книги для поиска неизвестных тер- минов. Книги Если предыдущие ваши "контакты" с SQL были минимальными, хорошая книга по SQL-92 будет бесценной. Следующий список (который не является исчерпывающим) может быть вам полезен. Joe Celko пишет по SQL книги, предназначенные для решения проблем. Вот некото- рые из них: "Joe Celko’s SQL For Smarties: Advanced SQL Programming", "Joe Celko’s Data and Databases: Concepts in Practice", и "Joe Celko’s SQL Puzzles and Answers". Здесь описывается главным образом стандартный SQL, может быть с некоторым ук- лоном в Oracle. David Rozenshtein и Tom Bondur "The Essence of SQL" — очень лаконичная книга в большей мере для начинающих. Judith S. Bowman, Sandra Emerson, Marcy Damovsky "The Practical SQL Handbook" — настольный справочник по стандарту SQL, получивший хорошие отзывы. Hugh Darwen и Chris Date "A Guide to the SQL Standard" содержит все, что вам нужно знать об SQL-92, а также многое из того, о чем вы не знаете, что вы этого не знаете. Jim Melton и Alan Simon "Understanding the New SQL: A Complete Guide" описывает SQL-89 и SQL-92. Это всеобъемлющий красиво оформленный справочник для начи- нающих. Включает некоторые базовые теории моделирования. Martin Gruber "Mastering SQL" является переработанной и расширенной версией кни- ги того же автора "Understanding SQL", которая делает стандартный SQL доступным даже для начинающих. Позволяет быстро приобрести солидные умения в разра- ботке.3 3 В переводе на русский язык книга называется М. Грабер "SQL" и доступна в книжных ма- газинах начиная с 2001 г. — Прим. науч. ред.
Гпава 19. Язык SQL в Firebird 387 Свободная поддержка SQL По всем вопросам, связанным с SQL Firebird, присоединяйтесь к форуму поддержки Firebird на http://www.yahoogroups.com/community/firebird-support. Это добро- вольный список адресов электронной почты, где знающие и новые пользователи Firebird делятся своим опытом. Если вы предпочитаете интерфейс конференции, то здесь зеркально отображаются новости: news://news.atkin.com/egroups.ib-support. Вы можете послать список сообщений через зеркальную конференцию, если подпи- шетесь на список рассылки и будете использовать тот же адрес отправителя. Пора дальше Язык манипулирования данными (DML) является подмножеством языка SQL, кото- рое мы используем, чтобы определить наборы данных для чтения или обработки в приложениях. В следующей главе мы рассмотрим базовый синтаксис "большой чет- верки" операторов SQL: select, insert, update и delete.
ГЛАВА 20 Запросы DML Приложения пользователя могут получить доступ к таблицам Firebird только одним путем— выполняя к ним запрос. По своей сущности запрос является оператором SQL, который передается на сервер. Оператор SQL является выражением, состоящим из ключевых слов, фраз и предложений на формализованном языке на базе англий- ского. Этот язык имеет ограниченный словарь, который понятен клиентской библио- теке и серверу Firebird — а также, желательно, и человеку, который составляет такие выражения! Задачи создания, модификации и управления метаданными и данны- ми — все без исключения — выполняются в запросах. Строго говоря, оператор не является запросом, пока он не будет передан серверу. При этом большинство разработчиков используют термины оператор и запрос оди- наково. Все запросы DML обращаются к данным, постоянно хранящимся в базе данных в виде столбцов в таблицах. Запрос задает набор данных и одну или более операций, которые должны быть выполнены над этим набором. Он может использовать выра- жения для преобразования данных для поиска или сохранения или для предоставле- ния специальных условий поиска для наборов данных. Наборы данных Запрос DML определяет логическую совокупность элементов данных, упорядочен- ных слева направо, из одного или более столбцов, называемую набором. Запрос мо- жет ограничивать спецификацию набора одной строкой или же набор может состоять из множества строк. В наборе, состоящем из множества строк, порядок столбцов в одной строке идентичен всем другим строкам в наборе. Люди часто небрежно называют наборы "запросами". Эти слова не являются сино- нимами. Таблица является набором Таблица является набором, чья полная спецификация может быть получена из сис- темных таблиц сервером базы данных, когда к таблице происходит обращение из запроса, полученного от клиента. Сервер Firebird выполняет свои собственные внут- ренние запросы к системным таблицам для получения метаданных, необходимых для выполнения клиентских запросов.
Гпава 20. Запросы DML 389 Выходные наборы Общее использование оператора запроса, начинающегося с ключевого слова select, — получение выходного набора для клиентского приложения с целью ото- бражения данных для пользователя. Термины набор данных и набор записей являют- ся синонимами для выходного набора. Выходной набор может содержать неупорядо- ченные строки или он может быть представлен как сортированный набор, в соответ- ствии со спецификацией в предложении order by. ПРИМЕЧАНИЕ. Выражение "неупорядоченные строки" означает следующее. t'-Jrx Строки таблицы хранятся без каких-либо атрибутов, и порядок неупорядоченных у.—ы наборов может непредсказуемо меняться от одного запроса к другому. Например, следующий запрос создаст выходной набор из трех столбцов из таблицы tablea, содержащий каждую строку, которая соответствует условиям, указанным в предложении where. Строки будут отсортированными, следовательно, строка с наи- меньшим значением в соы появится первой: SELECT СОЫ, COL2, COL3 FROM TABLEA WHERE COL3 = 'Mozart' ORDER BY COL1; Если не будет задано предложение where, то набор будет содержать все строки из таблицы tablea, а не только те, в которых столбец соьз будет иметь значение 'Mozart'. Если нужны все столбцы из таблицы tablea, то вместо списка имен столбцов в опера- торе можно указать символ звездочки (*), например, оператор SELECT * FROM TABLEA; определяет выходной набор, состоящий из всех столбцов и всех строк таблицы TABLEA. Кардинальное число и положение Одним из терминов, который иногда можно встретить в отношении наборов, вклю- чая таблицы— является кардинальное число (cardinality, "мощность множества"). Оно описывает количество строк в наборе, который может быть таблицей или вы- ходным набором. Реже можно встретить кардинальное число строки или кардиналь- ное число значения ключа, что означает позицию строки в упорядоченном выходном наборе. Термин, используемый для номера столбца в наборе, — положение (degree). Кроме того, вы можете встретить фразу типа положение столбца, означающую позицию столбца в порядке столбцов в наборе слева направо.
390 Часть I/. Firebird SQL Входные наборы Как часть select ... from в запросе на поиск задает набор для вывода или для опера- ции курсора, точно так же и другие операторы DML (insert, update и delete) задают входные наборы, идентифицируя данные, над которыми выполняется операция. Входной набор запроса insert задает одну таблицу и упорядоченный слева направо список идентификаторов столбцов, которые получат входные значения в последую- щем предложении values (). Предложение values () должно поставлять список значе- ний, которые в точности соответствуют порядку входного набора и типу данных его элементов. Следующий пример определяет входной набор, состоящий из трех столбцов таблицы tableb. Константы в предложении values о будут проверяться на присутствие трех значений и на наличие у них корректных типов данных: INSERT INTO TABLEB(COLA, COLB, COLC) VALUES(99, 'Christmas 2004', '2004-12-25'); Оператор insert имеет альтернативный синтаксис, в котором предложение values о заменяется на выборку набора из одной или более других таблиц, реальных или вир- туальных. Операторы insert подробно обсуждаются в разд. "Оператор INSERT". Оператор update определяет свой входной набор, задавая одну таблицу и список из одного или более столбцов вместе с их значениями в предложении set. Он иденти- фицирует строки, принимающие участие в операции, в предложении where: UPDATE TABLEB SET COLB ='Labor Thanksgiving Day (Japan)', COLC = '2002-23-11' WHERE . . .; Операторы update подробно обсуждаются в разд. "Оператор UPDATE". Запрос delete не может задавать столбцы в своем входном наборе — этот набор все- гда определяется неявно * (все столбцы). Он идентифицирует строки, принимающие участие в операции, в предложении where. Например, следующий запрос удалит все строки, где colc (типа данных date) является датой, раньше 13 декабря 1999 г.: delete from tableb WHERE COLC < '1999-12-13'; Операторы delete подробно обсуждаются в разд. "Оператор DELETE". Выходные наборы в качестве входных наборов Сгруппированные или агрегатные запросы SQL имеет важную возможность использовать входной набор, сформированный из выходного набора, сгенерированного в том же самом запросе select — предложение group by. Вместо того чтобы выходной набор из списка столбцов передавать клиенту, этот список передается на следующую стадию обработки запроса, где предложение
Гпава 20. Запросы DML 391 group by приводит к объединению данных в одну или более вложенных групп. Обыч- но в каждой части подводится некоторый итог с помощью выражений, которые группируют (агрегируют) числовые значения на одном или более уровнях. Об агрегатных запросах см. главу 23. Потоки и реки для соединений Когда столбцы одной таблицы связываются со столбцами другой таблицы для фор- мирования соединенных наборов (join), наборы входных столбцов называются пото- ками. Вывод двух соединенных потоков называется рекой. Когда соединения вклю- чают более двух таблиц, потоки и реки становятся входом для дальнейших соедине- ний потоков и рек, пока финальная река не станет выходом для запроса. Группа подпрограмм ядра Firebird, называемая оптимизатором, проверяет специфи- кацию запроса соединения и доступные индексы на предмет стоимости обработки запроса. Этот процесс называется оптимизацией. Оптимизатор генерирует план, яв- ляющийся "лучшей по стоимости" картой, которую выбирает сервер для генерации финальной "выходной реки" в последующих выполнениях этого оператора. Синтаксис соединения (join) обсуждается в главе 22. См. разд. "Тема оптимизации" главы 22, где обсуждается оптимизатор и планы запроса. Наборы курсора Оператор select может объявлять набор, который вовсе не является выходным для клиента, но остается на сервере, чтобы работать как курсор на стороне сервера. Сам курсор является указателем; приложение дает ему указание по запросу читать по по- рядку строки одну за другой на основании подготовленного (prepared) оператора SELECT. Объявление именованного курсора, который должен быть вначале скомпилирован в ESQL или задан в структуре API в DSQL, включает переменную оператора клиент- ской стороны, которая указывает на этот оператор. Клиентское приложение является ответственным за то, что подготовленный оператор доступен для назначения соот- ветствующему указателю при выполнении программы. PSQL имеет языковое расширение, которое принимает следующую форму: FOR SELECT <любая допустимая спецификация выборки> INTO <список предварительно объявленных локальных переменных> DO BEGIN <необязательные действия над переменными>; SUSPEND; END Это синтаксис неименованного курсора'. 1 Фактически синтаксис именованного курсора доступен как "скрытая возможность" и в PSQL, хотя он не полностью реализован в Firebird 1.5. Синтаксис был расширен после вер- сии 1.5 и должен появиться в последующих релизах.
392 Часть V. Firebird SQL Целью операций с курсором является использование данных из набора курсора и последовательное "действие" с каждой строкой набора курсора. Это может быть эф- фективным для наборов пакетных процессов, где выполняется одна или более задач над текущей строкой курсора в рамках одной итерации в цикле над набором курсора. Например, в каждой итерации элемент данных должен быть получен из текущей строки набора курсора и использован для добавления новой строки в указанный входной набор. Другой оператор может быть связан с открытым курсором при использовании where current of <имя~курсора> вместо условия поиска для выполнения позиционированно- го изменения или удаления строк, "отмеченных" набором курсора. Реализация курсоров Firebird предоставляет несколько методов для реализации курсоров. ♦ Встраиваемый SQL (ESQL) предоставляет оператор declare cursor2 для реализа- ции предварительно компилированного объявления именованного курсора. ♦ Для использования курсора динамическое приложение должно предварительно объявить его в функции isc_dsqi_set_cursor_name3. Только некоторые компоненты реализуют такой интерфейс, большинство других — нет. ♦ Язык PSQL предоставляет синтаксис для работы с именованными и неименован- ными курсорами локально внутри хранимой процедуры или триггера. Подробную информацию см. в главе 29. < ПРИМЕЧАНИЕ. За исключением техник работы с курсорами в PSQL сами курсо- ры выходят за пределы темы настоящей книги. Подробную информацию см. в \ \ главе 30. Вложенные наборы Вложенные наборы формируются с использованием синтаксиса специального вида выражений SQL, называемых подзапросами. Этот вид выражений принимает форму вложенного оператора select, который может быть включен в список спецификаций столбцов главного запроса для получения одного значения строки, или в предложе- ние where как часть условия поиска. Допустимый синтаксис select изменяется в зави- симости от контекста, в котором используется подзапрос. В следующем простом примере запрос к таблице tablea использует вложенный под- запрос для включения в выходной набор на время выполнения столбца с именем description, наследующего значение из столбца cola таблицы tableb: 2 Более подробную информацию см. в Embedded SQL Guide (EmbedSQL.pdf) в документа- ции по InterBase 6.0, опубликованной Borland. 3 Программисты прямого АР! и разработчики компонентов интерфейса могут получить больше информации из InterBase API Guide (APIGuide.pdf) в документации по InterBase 6.0, опубликованной Borland.
Глава 20. Запросы DML 393 SELECT СОЫ, COL2, COL3, (SELECT COLA FROM TABLEB WHERE COLX='Espagnol') AS DESCRIPTION FROM TABLEA WHERE ... ; Обратите внимание на использование ключевого слова as для присваивания имени (description) наследуемому столбцу. Это делать необязательно, но рекомендуется для ясности кода. Очень часто включенный запрос является коррелированным с главным запросом. Коррелированный запрос — это запрос, чье предложение where связано с одним или более значениями выходного списка внешнего запроса или с другими значениями таблиц из внешнего запроса. Коррелированный подзапрос похож по своему действию на inner join и иногда может быть использован для повышения производительности вместо join. Темы подзапросов подробно обсуждаются в следующих двух главах, особенно в гла- ве 22. Привилегии Чтение из таблиц и запись в таблицы являются привилегиями базы данных, управ- ляемыми объявлениями, выполненными с помощью операторов grant и revoke (см. главу 35). Оператор SELECT Оператор select является для клиентов фундаментальным методом поиска наборов данных в базе данных. Он имеет следующую основную форму: select [FIRST (m)] [SKIP (n) ] [[ALL] I DISTINCT] <список-столбцов> [, [пмя-столбца] | выражение [ константа ] AS имя-алиаса] FROM <таблица-или-процедура-или-просмотр> [{[[INNER] | [{LEFT | RIGHT I FULL] [OUTER]] JOIN}] < та блица -юн-процедура -или-про смотр> ON <условия-соединения> [{JOIN...]] [WHERE <условия-поиска>] [GROUP BY <список-группируемых-столбцов>] [HAVING <предикат~труппирования>] [UNION <вьражение-выбора> [ALL]] [PLAN <вьражеиие-плана>] [ORDER BY <список-столбцов>] [FOR UPDATE [OF столбец! [, столбец2...]] [WITH LOCK]]
394 Часть I/. Firebird SQL Предложения в операторе SELECT В следующих подразделах мы предварительно рассмотрим каждое доступное в опе- раторе select предложение. Большинство предложений является необязательным, при этом важно их использование в правильном порядке. Необязательные режимы выборки За ключевым словом select может следовать режим выборки для управления вклю- чением строк в выходной набор, когда они соответствуют всем другим условиям. ALL Это значение по умолчанию режима выборки для выходного списка и обычно опус- кается. Оно возвращает все строки, соответствующие условиям в спецификации. DISTINCT Этот режим выборки подавляет вывод всех дубликатов строк в выходной набор. На- пример, таблица employee project хранит пересекающиеся записи для каждой комби- нации служащего (емр ыо) и проекта (proj_id), поддерживая отношение многие-ко- многим. Конкатенация емр ыо + proj id образует первичный ключ. Оператор SELECT DISTINCT EMP_NO, PROJ_ID FROM EMPLOYEE_PROJECT; вернет 28 строк— т. e. все строки, это является тем же самым, что и select [all], потому что каждое появление (emp_no + proj id) является по своей природе уникаль- ным, отличным от других. Операторы SELECT DISTINCT EMP_NO FROM EMPLOYEE_PROJECT; И SELECT DISTINCT PROJ_ID FROM EMPLOYEE_PROJECT; вернут, соответственно, 22 и 5 строк. Вычисление отличий применяется ко всем выходным столбцам, что делает оператор distinct полезным в некоторых запросах, которые используют соединения для полу- чения ненормализованного набора. Тщательно тестируйте этот режим для проверки, что он создает тот результат, который вы ожидаете4. FIRST (m) SKIP (п) Необязательные ключевые слова first (m) и/или skip (п), если присутствуют, пред- шествуют всем другим спецификациям. Они задают режим выбора первых m строк в упорядоченном наборе и игнорирования первых п строк в упорядоченном наборе, 4 Поскольку выходной набор distinct предполагает существование дубликатов, чтобы обеспечить уникальность потока поиска для индивидуального изменения, нельзя полагаться на его поля. Некоторые средства разработки явно трактуют выходные наборы, полученные от запросов distinct как неизменяемые.
Гпава 20. Запросы DML 395 соответственно. Не имеет смысла использовать эту конструкцию в неупорядоченном наборе5. Очевидно, нужно предложение order by для использования условия упоря- дочения, которое сделает осмысленным выбор кандидатов строк. Эти два ключевых слова могут быть использованы вместе или индивидуально. Аргу- менты m и п должны быть целыми или выражениями, дающими целые числа. Круглые скобки вокруг значений тип требуются для выражений и не обязательны для про- стых целых аргументов. Поскольку first и skip выполняются над набором, полученным на основании ос- тальной части спецификации, не следует ожидать, что они сделают выполнение за- проса более быстрым. Преимущества в производительности получаются от сокраще- ния сетевого трафика6. Следующий пример вернет пять строк, начиная со строки 101 упорядоченного на- бора: SELECT FIRST 5 SKIP 100 MEMBER_ID, MEMBERSHIP_TYPE, JOIN_DATE FROM MEMBERS ORDER BY JOIN_DATE; СОВЕТ. Для получения n строк с самыми большими значениями столбцов в со- ответствии с условиями упорядочения установите порядок сортировки DESC[ENDING]. SELECT <список-столбцов> Предложение select определяет список столбцов, которые будут помещаться в вы- ходной набор. Список должен содержать, по меньшей мере, один столбец, который не обязательно должен быть столбцом, присутствующим в таблице. Эта фраза не яв- ляется столь странной, как она звучит. Список столбцов действительно является вы- ходной спецификацией и относится к языку манипулирования данными (DML). Вы- ходные спецификации могут включать следующее: ♦ идентификатор столбца, который хранится в таблице, задан в просмотре или объ- явлен как выходной аргумент хранимой процедуры. При некоторых условиях идентификатор столбца должен содержать уточнение в виде имени или алиаса со- держащей его таблицы; ♦ простое или сложное выражение, сопровождаемое идентификатором времени вы- полнения; 5 В InterBase 6.5 и выше вместо first используется ключевое слово rows, следующее за order by. Этот же синтаксис может быть использован и в Firebird 2.0. Подробнее о синтаксисе ROWS см. в документации по InterBase 1.x. — Прим. науч. ред. 6 first и skip всего лишь ограничивают количество записей, выдаваемых клиенту. Запрос вне зависимости от наличия или отсутствия first и skip всегда будет выполнен целиком. — Прим. науч. ред.
396 Часть 1/. Firebird SQL ♦ константное значение, сопровождаемое идентификатором времени выполнения; ♦ контекстная переменная сервера, сопровождаемая идентификатором времени вы- полнения; ♦ символ *, часто называемый "звездочкой выбора", который задает все столбцы. Хотя select * не исключает выбор одного или более столбцов из той же таблицы индивидуально, вообще-то в этом нет смысла. Чтобы включить дубликаты столб- цов для специальных целей, применяйте для него ключевое слово as и алиас, воз- вращая его как вычисляемое (только для чтения) поле. Все следующие спецификации select правильны. Простой список столбцов: SELECT COLUMN1, COLUMN2 ... Уточненные имена столбцов, требуемые для спецификаций со многими таблицами: SELECT TABLEA.ID, TABLEA.BOOK_TITLE, TABLEB.CHAPTER_TITLE, CURRENT_TIMESTAMP AS RETRIEVE_DATE . . . Выражение (агрегирующее): SELECT MAX (COST * QUANTITY) AS BEST_SALE ... Выражение (преобразующее): SELECT 'EASTER' I I CAST (EXTRACT (YEAR FROM CURRENT-DATE) AS CHAR(4)) AS SEASON ... Переменные и константы: SELECT ACOLUMN, BCOLUMN, CURRENT_USER, /* контекстная переменная ★/ 'Jaybird' AS NICKNAME ... /* константа времени выполнения */ Все столбцы таблицы: SELECT * ... Режимы выборки: SELECT FIRST 5 SKIP 100 ACOLUMN, BCOLUMN ... /* это не будет иметь смысла при отсутствии в дальнейшем предложения ORDER BY */ Выражения и константы в качестве выходных полей Константы или выражения — которые могут включать, а могут и не включать имя столбца— могут возвращаться только как неизменяемые, вычисляемые в момент выполнения поля. Такому столбцу должно назначаться имя, уникальное среди имен
Глава 20. Запросы DML 397 выходного набора. Имена для столбцов времени выполнения называются алиасами столбцов. Для большей ясности алиас столбца необязательно может быть отмечен ключевым словом as. Возьмем предыдущий пример: SELECT 'EASTER' | | CAST (EXTRACT (YEAR FROM CURRENT_DATE) AS CHAR(4)) AS SEASON ... В 2004 году этот алиас столбца будет возвращен для каждой строки набора в виде: SEASON EASTER2004 Константы, множество различных видов выражений, включающих функции и вы- числения, скалярные подзапросы (включая коррелированные подзапросы) могут быть использованы в выходных полях только для чтения. ПРИМЕЧАНИЕ. Константы типа blob и массивы не могут быть использованы для выходных полей времени выполнения. О выражениях и функциях читайте в следующей главе. FROM <таблица-или-процедура-или-просмогр> Предложение from задает источник данных, который может быть таблицей, просмот- ром или хранимой процедурой, имеющей выходные аргументы. Если оператор вклю- чает соединение двух или более структур, то предложение from задает структуру, на- ходящуюся в левой части. Другие таблицы добавляются в спецификацию при ис- пользовании последующих предложений on (см. разд. "JOIN <спецификация>"). В следующих примерах предложения from добавляются к спецификациям select пре- дыдущих примеров: SELECT COLUMN1, COLUMN2 FROM ATABLE . . . SELECT TABLEA.ID, TABLEA.BOOKJTITLE, TABLEB.CHAPTERJHTLE, CURRENT_TIMESTAMP AS RETRIEVE_DATE FROM TABLEA . . . SELECT MAX(COST * QUANTITY) AS BEST_SALE FROM SALES ... SELECT 'EASTER'||CAST(EXTRACT(YEAR FROM CURRENT_DATE) AS CHAR(2)) AS SEASON FROM RDB$DATABASE ; SELECT ACOLUMN, BCOLUMN, CURRENTJJSER, 'Jaybird' AS NICKNAME FROM MYTABLE ...
398 Часть 1/. Firebird SQL Синтаксис внутреннего соединения SQL-89 Firebird обеспечивает поддержку устаревшего синтаксиса неявного внутреннего со- единения SQL-89. Например: SELECT TABLEA.ID, TABLEA.BOOKJTITLE, TABLEB.CHAPTERJTITLE, CURRENTJTIMESTAMP AS RETRIEVE_DATE FROM TABLEA, TABLEB ... По разным причинам вам не следует использовать этот синтаксис в новых приложе- ниях (см. главу 22). Что это за таблица RDBSDATABASE?_________________________________________ rdb$database является системной таблицей, содержащей одну и только одну строку, которая хранит сведения заголовочной информации о базе данных. Что там нахо- дится, неважно для пользователей Firebird. Тот факт, что там всегда содержится од- на строка — ни больше, ни меньше — делает эту таблицу удобной, когда мы хотим найти значение на сервере, которое не хранится ни в таблице, ни в просмотре, ни в хранимой процедуре. Например, для получения приложением нового значения генератора, что иногда может нам понадобиться для создания строк подчиненной стороны в новой структу- ре главная-подчиненная, мы создаем в приложении функцию, которая обращается к следующему запросу: SELECT GEN_ID(MyGenerator, 1) FROM RDBSDATABASE; В РСУБД Oracle для аналогичных целей используется таблица dual. JOIN <спецификация> Используйте это предложение, чтобы добавить имена и условия соединения для вто- рого и каждого последующего потока данных (таблица, просмотр или хранимая про- цедура выбора), который объединяется в многотабличном операторе select — одно предложение join ... on для каждого исходного набора. Синтаксис и использование join подробно обсуждается в главе 22. Следующий оператор иллюстрирует простое внутреннее соединение между двумя таблицами из предыдущего примера: SELECT TABLEA.ID, TABLEA.BOOKJTITLE, TABLEB.CHAPTERJTITLE, CURRENTJTIMESTAMP AS RETRIEVE_DATE FROM TABLEA JOIN TABLEB ON TABLEA.ID = TABLEB.ID В ...
Гпава 20. Запросы DML 399 Алиасы таблиц В том же фрагменте оператора идентификаторы таблиц могут быть заменены на алиасы таблиц, например: SELECT Tl.ID, Tl. BOOKJITLE, Т2. CHAPTERJTITLE, CURRENT_TIMESTAMP AS RETRIEVE—DATE FROM TABLEA Tl JOIN TABLEB T2 ON Tl.ID = T2.ID_B ... Использование алиасов таблиц в многотабличных запросах подробно рассматривает- ся в главе 22. WHERE <условия-поиска> Условия поиска, ограничивающие выводимые строки, размещаются в приложении where. Условия поиска могут изменяться от условия простого соответствия для одно- го столбца до сложных условий, включающих выражения, предикаты and, or и not, преобразование типов, условия наборов символов и последовательностей сортировки и многое другое. Предложение where является фильтрующим предложением, определяющим, какие строки являются кандидатами для включения в выходной набор. Те строки, которые не были исключены условиями поиска в предложении where, могут быть готовы для передачи инициатору запроса или они могут передаваться для последующей обра- ботки, упорядочения на основании предложения order by с объединением или без объединения на основании предложения group by. Следующие простые примеры иллюстрируют некоторые предложения where, исполь- зующие условия выборки для ограничения отыскиваемых строк: SELECT COLUMN1, COLUMN2 FROM ATABLE WHERE ADATE BETWEEN '2002-12-25' AND '2004-12-24'... /**/ SELECT Tl.ID, T2 .TITLE, CURRENT-TIMESTAMP AS RETRIEVE—DATE FROM TABLEA JOIN TABLEB ON TABLEA.ID = TABLEB.ID—В WHERE TABLEA.ID = 99 ; /«/ SELECT MAX(COST * QUANTITY) AS BEST-SALE FROM SALES WHERE SALES—DATE > '31.12.2003'... Глава 21 посвящена выражениям и предикатам, используемым в условиях поиска.
400 Часть V. Firebird SQL Параметры в предложении WHERE Интерфейсы доступа к данным, реализованные в API Firebird, имеют возможность обрабатывать константы в условиях поиска как заменяемые параметры. Следова- тельно, приложение может создавать оператор с динамическими условиями поиска в предложении where, значения которых можно устанавливать непосредственно перед выполнением запроса. Такая возможность иногда называется динамическим связыва- нием. Подробности см. в одном из следующих разд. "Использование параметров". GROUP BY <список-группируемых-сголбцов> Выход оператора select может быть разделен на одну или более вложенных групп, которые обобщают (суммируют) наборы данных, возвращенных с каждого вложен- ного уровня. Такое группирование часто использует агрегирующие выражения, т. е. выражения, содержащие функции, которые работают с множеством значений, таких как итоги, средние значения, счетчики строк, минимальные/максимальные значения. Следующий простой пример иллюстрирует группирующий запрос. Агрегатная SQL- функция sumo используется для вычисления общего количества продаж для каждого типа продукции: SELECT PRODUCT-TYPE, SUM(NUMBER—SOLD) AS SUM_SALES FROM TABLEA WHERE SERIAL_NO BETWEEN 'A' AND ' K' GROUP BY PRODUCT__TYPE; Результат может быть похожим на следующий: PRODUCT_TYPE SUM_SALES Gadgets 174 Whatsits 25 Widgets 117 Firebird предоставляет широкий диапазон возможностей группирования с весьма ог- раниченными правилами управления их логикой. Д ВНИМАНИЕ! Если вы конвертируете базу данных из InterBase в Firebird, то это одна из областей, где вам нужно помнить о различиях. Firebird является менее терпимым к нелогичным спецификациям группирования, чем его предшествен- ник. Это позволяет исключить возможность выполнения запросов, возвращаю- щих некорректные результаты. Обобщающие выражения обсуждаются в главе 21. Подробную информацию о пред- ложении group by см. в главе 23. HAVING <предикат-группирования> Необязательное предложение having может быть использовано вместе со специфика- цией группирования для включения или исключения строк или групп, как это делает
Гпава 20. Запросы DML 401 предложение where, ограничивая набор строк. Часто в группирующих запросах пред- ложение having может заменить предложение where. При этом, поскольку having опе- рирует с промежуточным набором, созданным в качестве входа для спецификации group by, может оказаться более экономичным использование условия where для ог- раничения количества строк и условия having для ограничения количества групп. Изменим предыдущий пример, добавив предложение having для получения только тех product_type, которые имели количество продаж больше 100: SELECT PRODUCT-TYPE, SUM(NUMBER_SOLD) AS SUM_SALES FROM TABLEA WHERE SERIAL_NO BETWEEN 'A' AND 'K' AND PRODUCT—TYPE = 'WIDGETS' GROUP BY PRODUCT_TYPE HAVING SUM(NUMBER_SOLD) > 100; Вывод будет таким: PRODUCT-TYPE SUM_SALES Widgets 117 UNION <выражение-выбора> Наборы union формируются объединением двух или более спецификаций запросов, которые могут использовать различные таблицы, в один выходной набор. Единст- венное ограничение — выходные столбцы в каждой выходной спецификации долж- ны соответствовать по степени, типу и размеру. Это означает, что в каждом выходе должно быть то же количество столбцов в том же порядке слева направо, и каждый столбец должен быть совместим по типу данных и размеру. По умолчанию union убирает дубликаты в финальном выходном наборе. Для сохра- нения всех дубликатов добавьте ключевое слово all7. Наборы union подробно обсуждаются в главе 23. PLAN <выражение-плана> Предложение plan позволяет включить план запроса в спецификацию запроса. План является инструкцией оптимизатору по использованию отдельных индексов, порядка соединения и методов доступа для запроса. Оптимизатор создает свой собственный план при подготовке оператора запроса. Вы можете просматривать план в isql и мно- гих других доступных утилитах графического интерфейса. Обычно "оптимизатор знает лучше", но у вас может быть опыт использования различных планов оптимиза- тора, когда запрос работал медленно. Планы запросов и синтаксис выражений плана обсуждаются в разд. "Тема оптими- зации" главы 22. 7 Если два набора, объединяемых union, принципиально не будут содержать дубликатов строк, необходимо использовать union all, чтобы сервер не занимался зря работой по удале- нию несуществующих дубликатов строк. — Прим. науч. ред.
402 Часть И Firebird SQL ORDER BY <список-столбцов> Используйте это предложение, когда вам нужно сортировать выходной набор. На- пример, следующий оператор дает список имен, отсортированный в алфавитном по- рядке по фамилии и имени: SELECT EMP_NO, LAST_NAME, FIRST_NAME FROM EMPLOYEE ORDER BY LAST_NAME, FIRST_NAME; В отличие от столбцов group by столбцы в order by не обязательно должны присут- ствовать в выходной спецификации (предложение select). Идентификатор любого упорядочиваемого столбца, который также появляется в выходной спецификации, может быть заменен на его порядковый номер в выходной спецификации при под- счете слева направо: SELECT EMP_NO, LAST_NAME, FIRST_NAME FROM EMPLOYEE ORDER BY 2, 3; Обратите особое внимание на индексы для столбцов, которые будут использованы для сортировки (см. главу 18). Подробнее о синтаксисе и проблемах см. в главе 23. Предложение FOR UPDATE Его синтаксис: [FOR UPDATE [OF coll [,CO12..]] [WITH LOCK]] Вообще говоря, предложение for update имеет смысл только в контексте оператора select, который используется для задания именованных курсоров. Оно указывает серверу, чтобы тот ждал вызова fetch, читал бы одну строку в курсор для операции "текущей строки" и затем ожидал бы следующий вызов fetch. После того как все записи будут прочитаны, они становятся доступными для операций изменения. Необязательное внутреннее предложение of <список-столбцов> может быть использо- вано для задания списка присутствующих в курсоре полей, которые могут быть из- менены. ♦ В приложениях ESQL оператор declare cursor используется для объявления име- нованного курсора. Подробную информацию см. в документации по InterBase 6.0 "Embedded SQL". ♦- Приложения интерфейса DSQL должны использовать функцию isc_ dsqi__set _ cursor name для получения именованного курсора и осмысленно использовать for update. Более подробную информацию см. в InterBase API Guide. Динамические приложения Поскольку в DSQL отсутствует fetch как элемент языка, приложения реализуют его с помощью вызова функции API с именем isc_dsqi_fetch. API "знает" порядок и формат выходных полей, потому что динамическое приложе- ние должно передавать ему описательную структуру— называемую расширенной областью дескрипторов SQL (Extended SQL Descriptor Area, XSQLDA). Одна струк-
Гпава 20. Запросы DML 403 тура XSQLDA содержит массив описателей сложных переменных, называемых sqlvar, по одному на каждое выходное поле. Клиентское приложение использует isc_dsql_fetch для запроса строки, которая толь- ко что заполнила XSQLDA. Обычное поведение большинства современных клиент- ских приложений— выполнение в цикле обращений к isc_dsqi_fetch для получения выходных строк в пакете и буферизация их в структурах клиентской стороны, кото- рые называются наборами записей, наборами данных или результирующими набо- рами. Некоторые приложения API реализуют именованные курсоры и используют поведе- ние for update, однако большинство этого не делают. Вложенное предложение WITH LOCK Firebird 1.5 вводит необязательное расширение with lock, используемое с/без пред- ложения for update, для поддержки ограниченного уровня явной пессимистической блокировки (pessimistic locking) на уровне строки. Пессимистическая блокировка является прямой противоположностью архитектуры транзакций в Firebird и добавля- ет запутанность. Ее использование рекомендуется только тем разработчикам, кото- рые хорошо понимают, как параллельная работа многих пользователей реализована в Firebird. Пессимистическая блокировка обсуждается в главе 27. Запросы, подсчитывающие строки Среди некоторых программистов существует закрепившаяся практика разработки приложений, которым нужно выполнить подсчет строк в выходном наборе. В Firebird не существует быстрого надежного способа получения количества строк, возвращае- мых в выходном наборе. Поскольку Firebird имеет многоверсионную архитектуру, у него нет механизма "узнавать" количество строк в постоянных таблицах. Если при- ложению требуется количество строк, оно может получить приблизительное значе- ние с использованием запроса select count(*). Запросы SELECT COUNTS) Оператор select с вызовом функции count () на месте идентификатора столбца вернет приблизительную мощность набора, определенного в предложении where. Функция count о принимает практически все в качестве входного аргумента: идентификатор столбца, список столбцов, символ *, который представляет "все столбцы", и даже константу. Например, все следующие операторы эквивалентны или близки. При этом select COUNT (<имя-некоторого-сто.лбца>') не ВКЛЮЧает В СЧеТЧИК строки, Где <имя-некоторого- столбца> имеет значение null: SELECT COUNT (*) FROM ATABLE WHERE COL1 BETWEEN 40 AND 75; SELECT COUNT (COLD FROM ATABLE WHERE COL1 BETWEEN 40 AND 75; SELECT COUNT (COL1, COL2, COL3) FROM ATABLE WHERE COL1 BETWEEN 40 AND 75; SELECT COUNT 1 FROM ATABLE WHERE COL1 BETWEEN 40 AND 75; SELECT COUNT ('Sticky toffee') FROM ATABLE WHERE COL1 BETWEEN 40 AND 75;
404 Часть V. Firebird SQL count'*) является очень дорогой операцией, потому что она может работать только пройдя по всему набору данных и точно подсчитав каждую строку, которая видима как подтвержденная для текущей транзакции. Это число должно трактоваться как "грубый счетчик", потому что может оказаться неверным, если другая транзакция подтверждает работу. Хотя count)*) можно включить в выходной набор, который содержит другие столб- цы, это не является ни целесообразным, ни разумным. Это приведет к тому, что весь набор данных будет просматриваться каждый раз, когда строка будет выбрана для выходного набора. Исключением является ситуация, когда count)*) включается в выходной набор, кото- рый агрегируется на основании предложения group by. При этих условиях счетчик не будет дорогим — он будет рассчитываться для агрегированной группы в процессе выполнения агрегирования. Например: SELECT СОЫ, SUM(COL2), COUNT)*) FROM TABLEA GROUP BY COL1; Подробности использования count с агрегированием см. в главе 23. Проверка существования Не используйте select count)*) как способ проверки существования строк, соответ- ствующих некоторому критерию. Такая техника часто обнаруживается в приложени- ях, которые были переведены в Firebird из основанных на файлах базах данных с блокировкой таблиц, таких как Paradox или MySQL. От этой техники нужно отка- заться. Вместо этого используйте функциональный предикат exists (), который был разработан для этих целей и является очень быстрым. См. в следующей главе под- робности об exists () и других функциональных предикатах. Вычисления "следующего значения" Другая техника, от которой нужно отказаться в Firebird, это использование count)*) и прибавление единицы для "генерации" значения первичного ключа. Это ненадежно в любой многопользовательской СУБД, которая изолирует параллельные задачи. В Firebird это к тому же выполняется крайне медленно, потому что система управле- ния таблицей не имеет "файла записей", которые могли бы быть подсчитаны метода- ми управления файлами на компьютере. Используйте генераторы для любых целей, которые преследуют уникальность число- вых последовательностей. Подробнее о генераторах см. разд. "Генераторы"главы 9. Варианты COUNT0 Результатом count)) никогда не будет null, потому что он подсчитывает строки. Если счетчик будет использован для пустого набора, он вернет ноль. Он никогда не может быть отрицательным. count (*) для таблицы подсчитывает все строки без проверки существования данных в столбцах. Оптимизатор может использовать индекс, если запрос содержит соответст- вующее условие WHERE.
Гпава 20. Запросы DML 405 Например, оператор SELECT COUNT(*) FROM EMPLOYEE WHERE LAST_NAME BETWEEN 'A%' AND ,M%‘; может быть чуть менее дорогим, если существует индекс для last_name. count (имя-столбца) подсчитывает только строки, где имя-столбца не является null. count (distinct имя-столбца) подсчитывает только отличающиеся значения в этом столбце. То есть все повторения одного и того же значения учитываются как один элемент. в count (distinct ...), если столбец допускает значение null, все строки, содержащие в этом столбце null, исключаются из подсчета. Если вы должны их сосчитать, это может быть выполнено "хакерским" способом: SELECT COUNT (DISTINCT TABLE.COLX) + (SELECT COUNT(*) FROM RDBSDATABASE WHERE EXISTS(SELECT * FROM TABLE T WHERE T.COLX IS NULL)) FROM TABLE Оператор INSERT Оператор insert используется для добавления строк в одну таблицу. SQL не позволя- ет в одном операторе insert добавлять строки более чем в одну таблицу. При некоторых условиях оператор insert может работать с просмотрами. Обсужде- ние просмотров, для которых можно применять добавление данных в лежащие в ос- нове просмотра таблицы, см. в главе 24. Оператор insert имеет две основные формы передачи значений в список входных столбцов. Используйте следующую форму для добавления списка констант: INSERT INTO имя-таблицы | имя-просмотра (Список столбцов>) VALUES (соответствующий список значений>) •Следующая форма используется для добавления из встроенного запроса: INSERT INTO <таблица> (<список столбцов>) SELECT [[FIRST m] [SKIP n] ] Соответствующий список значений из другого набора> [ORDER BY <встроенный столбец (столбцы)> [DESC]] В следующем примере предложение insert into определяет входной набор для таб- лицы tableb, а предложение select определяет соответствующий встроенный запрос к таблице tablea для получения значений для входного набора: INSERT INTO TABLEB(COLA, COLB, COLC) SELECT COL1, C0L2, C0L3 FROM TABLEA;
406 Часть И Firebird SQL ПРИМЕЧАНИЕ. He существует возможности добавлять данные во встроенный запрос. Добавление данных в столбцы BLOB Техника insert into ... select напрямую передает столбец blob в столбец blob. Как правило, если вам нужно добавить столбец blob как часть списка values о, он должен конструироваться клиентским приложением с использованием функций и структур API. В приложениях DSQL такие данные обычно передаются в виде потоков (stream). Размер сегмента может быть проигнорирован везде, кроме приложений ESQL. При этом если вы передаете в values о данные для blob в виде текста, Firebird примет символьную строку в качестве ввода, например: INSERT INTO ATABLE (BLOBMEMO) VALUES ('Now is the time for all good men to come to the aid of the party'); Эта возможность будет подходящей при условии, что сохраняемый текст никогда не превысит 32 767 байтов (или 32 765, если вы помещаете поле varchar в blob). Может показаться, что для многих интерфейсов программирования подобная проблема не существует, потому что они не могут обрабатывать такие большие строки. Однако, поскольку Firebird принимает выражения конкатенации строк SQL, такие как myvarchari 11 MWARCHAR2, следует принять меры по защите входных строк от пере- полнения. Добавление в столбцы массивов В приложениях со встроенным SQL (ESQL) возможно конструирование оператора SQL для добавления целого массива в столбец массива. Могут появиться ошибки, если данные не полностью заполняют массив. В DSQL вовсе нет возможности добавлять данные в столбцы массивов. Необходимо реализовывать пользовательский метод в приложении или в коде компонента, кото- рый будет вызывать API-функцию isc_array_put__siice. Использование INSERT для автоматических полей Определение таблицы может содержать столбцы, чьи значения автоматически запол- няются данными при добавлении новой строки. Это может происходить в нескольких случаях: ♦ столбец определен с предложением computed by; ♦ столбец или домен, на котором он основан, включает предложение default; ♦ для таблицы был создан триггер before insert для автоматического заполнения столбца.
Гпава 20. Запросы DML 407 Автоматические поля могут воздействовать на тот способ, каким вы формулируете операторы insert для таблицы. Столбцы COMPUTED BY Включение вычисляемого столбца в список входных столбцов является неверным и приводит к исключениям, как показано в следующем примере: CREATE TABLE EMPLOYEE ( EMP_NO INTEGER NOT NULL PRIMARY KEY, FIRST_NAME VARCHAR (15), LAST_NAME VARCHAR(20), BIRTH_COUNTRY VARCHAR(30) DEFAULT 'TAIWAN', FULL_NAME COMPUTED BY FIRST-NAME I I ' ' I I LAST_NAME); COMMIT; INSERT INTO EMPLOYEE (EMP_NO, FIRST_NAME, LAST_NAME, FULL_NAME) VALUES (99, 'Jiininy', 'Cricket', 'Jiininy Cricket'); Столбцы, имеющие значение по умолчанию Если столбец определен со значением по умолчанию, то это значение будет работать только при добавлении и только если этот столбец отсутствует в списке входных столбцов. Если немного изменить оператор добавления в предыдущем примере, то в столбец birtH-Country будет записано значение 'Taiwan1: INSERT INTO EMPLOYEE (EMP_NO, FIRST_NAME, LAST—NAME) VALUES (99, 'Jiininy', 'Cricket'); COMMIT; SELECT * FROM EMPLOYEE WHERE EMP-NO = 99; EMP-NO FIRST_NAME LAST_NAME BIRTH-COUNTRY FULL—NAME 99 Jiminy Cricket TAIWAN Jiminy Cricket Значения по умолчанию никогда не заменяют значений null: INSERT INTO EMPLOYEE (EMP_NO, FIRST_NAME, LAST_NAME, BIRTH-COUNTRY) VALUES (100, 'Maria', 'Callas', NULL); COMMIT; SELECT * FROM EMPLOYEE WHERE EMP_NO = 100; EMP_NO FIRST-NAME LAST-NAME BIRTH_COUNTRY FULL-NAME 100 Maria Callas Maria Cailas Если вы разрабатываете приложения с использованием компонентов, которые гене- рируют операторы insert из спецификаций столбцов операторов select для наборов данных — например, Delphi или JBuilder — учитывайте такое поведение. Если ваш компонент не поддерживает метод получения значений по умолчанию с сервера, мо- жет оказаться необходимым изменить оператор, записывающий данные.
408 Часть V. Firebird SQL Триггеры BEFORE INSERT При добавлении данных в таблицу, для которой существуют триггеры, автоматиче- ски заполняющие некоторые столбцы, убедитесь, что вы не задали эти столбцы в вашем входном списке, если вы хотите, чтобы триггер выполнил свою работу. Это вовсе не означает, что вы всегда должны опускать поля, обрабатываемые тригге- ром, во входном списке. Очень рекомендуется для поддержания целостности данных создавать триггеры, которые присваивают значения столбцам в случае, когда такие значения не задаются в операторе insert — особенно когда используется множество приложений и инструментов для доступа к вашей базе данных. Триггер должен про- верять входное значение (например, null) и выполнять свои действия в соответствии с условиями. В следующем примере первичный ключ oid заполняется в триггере: CREATE TABLE AIRCRAFT ( OID INTEGER NOT NULL, REGISTRATION VARCHAR(8) NOT NULL, CONSTRAINT PK_AIRCRAFT PRIMARY KEY (OID)); COMMIT; /**/ SET TERM A; CREATE TRIGGER BI_AIRCRAFT FOR AIRCRAFT ACTIVE BEFORE INSERT POSITION 0 AS BEGIN IF (NEW.OID IS NULL) THEN NEW.OID = GEN__ID (ANYGEN, 1) ; END Л SET TERM ;A COMMIT; /**/ INSERT INTO AIRCRAFT (REGISTRATION) SELECT FIRST 3 SKIP 2 REGISTRATION FROM AIRCRAFTJDLD ORDER BY REGISTRATION; COMMIT; В этом случае триггер получает значение генератора для первичного ключа, потому что ему не было передано значение во входном списке. При этом, поскольку триггер выполнит свою работу только в том случае, если обнаружит null, следующий опера- тор insert также будет прекрасно работать — конечно, при условии, что предостав- ляемое значение oid не нарушает ограничения уникальности для первичного ключа: INSERT IN AIRCRAFT (OID, REGISTRATION) VALUES(1033, 'ECHIDNA'); Когда вам нужно это делать? В Firebird удивительно часто. При реализации структур главная-подчиненная вы можете получить значение первичного ключа для главной
Гпава 20. Запросы DML 409 строки из генератора до того, как строка будет отправлена в базу данных, простым вызовом DSQL: SELECT GEN_ID(YOURGENERATOR, 1) FROM RDBSDATABASE; Генераторы работают вне управления транзакций, и если вы получили номер, то он ваш. Вы можете использовать его для столбцов внешнего ключа подчиненных строк в клиентских буферах, если вы их создали, без сохранения главной строки в базе данных. Если пользователь решил отменить действия, то ничего не нужно "аннули- ровать" на сервере. Если вы когда-либо старались добиться подобной возможности в СУБД, которая использует автоинкрементный или "идентифицирующий" тип, вы полюбите такую возможность. Другие аспекты этой техники см. в главе 31. Оператор UPDATE Оператор update используется для изменения значений столбцов в существующих строках таблиц. Он может также оперировать с таблицами через наборы курсора и изменяемые просмотры. SQL не позволяет одному оператору update обращаться к строкам нескольких таблиц. Обсуждение просмотров, которые могут принимать изменения для лежащих в их основе таблиц, см. в главе 24. Запрос update, который изменяет только текущую стро- ку курсора, называется позиционированным изменением. Запрос, который может из- менять множество строк, называется поисковым изменением. Позиционированные операции в сравнении с поисковыми Операторы update и delete могут быть позиционированными (выполняются над одной и только одной строкой) и поисковыми (выполняются над нулевым или большим ко- личеством строк). Строго говоря, позиционированное изменение может появиться только в контексте текущей строки операции с курсором, в то время как поисковое изменение, возможно ограниченное условиями поиска в предложении where, появля- ется во всех других контекстах. Многие компоненты интерфейсов эмулируют позиционированные операции измене- ния и удаления, используя поисковое изменение с предложением where, уникально определяющим строку. Такие однонаправленные или прокручиваемые классы набо- ров данных поддерживают "буфер текущей строки", который хранит или связан со столбцом и значением ключа той строки, которая была выбрана задачей пользователя для операции. Когда пользователь готов отправить запрос на изменение или удале- ние, компонент конструирует поисковый оператор insert или delete, который указы- вает на одну строку базы данных, используя первичный ключ (или любой другой уникальный список столбцов) в предложении where.
410 Часть И Firebird SQL /\ ВНИМАНИЕ! Не все компоненты столь "сообразительны", чтобы определить / 1\ наличие дубликатов строк в "живых" буферах. В таких продуктах задачей разра- ботчика является убедиться в уникальности или найти любой другой способ за- щитить приложение от нежелаемого изменения множества строк. Использование оператора UPDATE Оператор update имеет следующую основную форму: UPDATE имя-таблицы | имя-просмотра SET имя-столбца = значение [,имя-столбца - значение ...] [WHERE <условие-поиска> | WHERE CURRENT OF имя-курсора] При поисковых изменениях, если не задано предложение where, то изменения будут выполнены для каждой строки таблицы. Предложение SET Синтаксис предложения set: SET имя-столбца = значение [, имя-столбца = значение . . . ] Предложение set представляет собой список, указывающий каждый изменяемый столбец вместе с новым присваиваемым ему значением. Новое значение должно со- ответствовать определению столбца по типу и размеру. Если столбец допускает пус- тое значение, ему также может быть присвоено значение null. Значение может быть: ♦ константой корректного типа (например, set columns = '99'). Если для столбца указан набор символов, отличный от набора символов соединения, то заменяющее значение может быть преобразовано в этот указанный набор символов с помощью включения соответствующего дескриптора набора символов слева от константы. Дескриптор набора символов является именем набора символов, который начина- ется с символа подчеркивания, например: SET COLUMN!7 = _ISO8859_1 'fricassee' ♦ идентификатором другого столбца в той же таблице, представленным с кор- ректным типом данных. Например, set columns = columnx заменит текущее значе- ние столбца columns на текущее значение столбца columnx. При необходимости может быть использован дескриптор набора символов (см. ранее); ♦ выражением. Например, set reviewjwe = review_date + 14 изменяет значение столбца даты на две недели позже, чем текущее значение. Подробнее о выраже- ниях см. главу 21', ♦ контекстной переменной сервера (например, set date_changed = current_date). Контекстные переменные описаны в главе 8; ♦ символом-заменителем параметра, соответствующим синтаксису, реализованно- му в коде приложения (например, для Delphi set last_name = :last_name или, для других интерфейсов приложений, set last_name = ?);
Гпава 20. Запросы DML 411 ♦ вызовом определенной пользователем функции (UDF). Например, set bcolumn = upper(acolumn) использует внутреннюю SQL-функцию upper для преобразования значения столбца acolumn в верхний регистр и сохранения результата в bcolumn. Информацию об использовании функций см. в главе 21. Предложение collate может быть включено при модификации символьных столбцов (но не blob). Для большинства наборов символов может оказаться необходимым ис- пользование одного из предыдущих примеров, поскольку последовательность сорти- ровки по умолчанию (двоичная) обычно не поддерживает функцию uppero. Напри- мер, если acolumn и bcolumn описаны с набором символов ISO8859_1 и используют испанский язык, то предложение set должно быть следующим: SET BCOLUMN = UPPER (ACOLUMN) COLLATION ES_ES Подробнее о наборах символах и последовательностях сортировки см. главу 11. Переключение значений Будьте осторожны при "переключении" значений между столбцами. Выражение типа SET COLUMNA = COLUMNB приведет к тому, что текущее значение в столбце columna будет немедленно заменено текущим значением столбца columnb. Если затем вы сделаете SET COLUMNB = COLUMNA то, поскольку старое значение столбца columna было потеряно, значения столбцов columnb и columna останутся теми же, что и после первого переключения. Для переключения значений вам нужен третий столбец, в котором будет "парковаться" значение из columna, пока вы не будете готовы назначить его столбцу columnb: set COLUMNC = COLUMNA, COLUMNA = COLUMNB, COLUMNB = COLUMNC В set вы можете использовать выражения, следовательно, при переключении двух целых значений возможно использование арифметических операций над двумя зна- чениями. Предположим, что columna равняется 10, a columnb 9: SET COLUMNB = COLUMNB + COLUMNA, /* COLUMNB теперь 19 */ COLUMNA = COLUMNB - COLUMNA, /* COLUMNA теперь 9 */ COLUMNB = COLUMNB - COLUMNA /* COLUMNB теперь 10 */ Всегда проверяйте ваши предположения при выполнении переключений!
412 Часть I/. Firebird SQL Изменение столбцов BLOB Изменение столбцов blob фактически полностью заменяет старое значение blob на новое. Старый указатель blob id при изменении заменяется. Также: ♦ невозможно изменить blob путем конкатенации другого blob или строки с сущест- вующим blob; ♦ значение текстового blob может быть установлено с использованием строки в ка- честве ввода. Например, мемо в следующем примере является текстовым blob: UPDATE ATABLE SET MEMO = 'Friends, Romans, countrymen, lend me your ears: I come not to bury Caesar, but to praise him.'; Л ВНИМАНИЕ! Помните, что хотя столбцы blob не ограничены по размерам, стро- ковые типы ограничены 32 765 байтами (varchar) или 32 767 байтами (char) — это байты, а не символы. Учтите это при конкатенации многобайтовых наборов символов. Изменение столбцов массивов В приложениях со встроенным SQL (ESQL) можно конструировать предварительно скомпилированные операторы SQL, чтобы передать фрагменты массивов для изме- нения (замены) соответствующих фрагментов в хранимых массивах. Не существует возможности изменять столбцы массивов в DSQL. Для изменения массивов необходимо в коде приложения или компонента реализовать пользователь- ские методы, которые вызывают функцию API isc_array_put_slice. Значение по умолчанию для столбцов Ограничения default никогда не учитываются при выполнении операторов update. Оператор DELETE Запрос delete используется для удаления целых строк таблицы. SQL не дает возмож- ности одному оператору delete удалять строки более чем из одной таблицы. Запрос delete, который изменяет только одну текущую строку курсора, называется позицио- нированным удалением. Оператор delete имеет следующую общую форму: DELETE FROM имя-та блицы [WHERE <предикаты~поиска> [ WHERE CURRENT OF имя~курсора] Если не указано предложение where, то будут удалены все строки таблицы. Оператор EXECUTE Оператор execute доступен только в ESQL. Он используется в приложениях со встро- енным SQL для выполнения подготовленного динамического оператора SQL. В этом
Гпава 20. Запросы DML 413 его отличие от обычных операторов DML в ESQL, которые являются предварительно скомпилированными и поэтому не подготавливаются во время выполнения. < ПРИМЕЧАНИЕ. Не путайте оператор execute с синтаксисом оператора execute statement, который поставляется в расширениях PSQL в Firebird 1.5 и выше и с оператором execute procedure в DSQL (описывается далее в разд. "Выполняе- мые процедуры"). Запросы, которые вызывают хранимые процедуры Firebird поддерживает два стиля хранимых процедур: выполняемые процедуры и про- цедуры выбора (селективные процедуры). Подробности о различиях в технике напи- сания и использования этих стилей процедур см. в главах 28—30. Выполняемые процедуры В DSQL оператор execute procedure выполняет (вызывает) выполняемую хранимую процедуру — хранимую процедуру, созданную для выполнения некоторых операций на сервере, — возвращая (необязательно) одну строку из одного или более значений. Оператор для выполнения подобных процедур имеет следующий общий формат: EXECUTE PROCEDURE имя-процедуры [(<список входных значений» J Следующий простой пример иллюстрирует вызов выполняемой процедуры, которая принимает два входных аргумента, выполняет некоторые действия на сервере и за- вершает работу: EXECUTE PROCEDURE DO_IT(49, '25-DEC-2004 '); В приложениях более мощным средством является использование параметров (см. разд. "Использование параметров") в операторах запросов, которые выполняют хра- нимые процедуры, например: EXECUTE PROCEDURE DO_IT (: IKEY, : REPORTJDATE) ; или EXECUTE PROCEDURE DO_IT(?, ?); Процедуры выбора Хранимые процедуры выбора способны возвращать многострочные наборы данных в ответ на специализированную форму оператора select: SELECT <список выходных сталбцов> FROM имя-процедуры [ (<список входных значений:»)] [WHERE <предикаты поиска» [ORDER BY <список выходных столбцов>]
414 Часть V. Firebird SQL В следующем фрагменте PSQL хранимая процедура принимает один ключ в качестве входного параметра и возвращает множество строк. Предложение returns определяет выходной набор: CREATE PROCEDURE GET_COFFEE_TABLE (IKEY INTEGER) RETURNS ( BRAND JD INTEGER, VARIETY_NAME VARCHAR(40), COUNTRY—OF—ORIGIN VARCHAR (30)) AS ......... Приложение получает выходной набор из хранимой процедуры следующим образом: SELECT BRAND—ID, VARIETY_NAME, COUNTRY_OFJDRIGIN FROM GET_COFFEE_TABLE(5002); Тот же самый пример с параметризованным входным аргументом: SELECT BRAND_ID, VARIETY-NAME, COUNTRY—OF—ORIGIN FROM GET_COFFEE_TABLE(:IKEY);/* Delphi */ или SELECT BRAND_ID,,VARIETY-NAME, COUNTRY_OF_ORIGIN FROM GET—COFFEE—TABLE (?); Использование параметров "Непараметризованный" запрос использует константы в выражениях для условий поиска. Например, в запросе SELECT MAX(COST * QUANTITY) AS BEST-SALE FROM SALES WHERE SALES—DATE > '31.12.2003' ; будут обработаны те строки таблицы sales, у которых дата в sales_date более позд- няя, чем последний день 2003 года. Средства разработки доступа к данным, использующие API Firebird, имеют возмож- .ность обрабатывать константы в условиях поиска как заменяемые, параметры. API позволяет передавать на сервер оператор в виде шаблона, представляющего эти па- раметры как заполнители, которые могут заменяться конкретными значениями. Кли- ент запрашивает подготовку (prepare) оператора — проверка синтаксиса и метадан- ных — без фактического его выполнения. Приложение DSQL может создать оператор с динамическими условиями поиска в предложении where, один раз подготовить его, а затем присваивать параметрам зна- чения один или много раз непосредственно перед каждым выполнением. Такую воз- можность иногда называют динамическим связыванием. Средства разработки прило- жений отличаются по тем способам, которыми они реализуют динамическое связы- вание параметров во включающем языке. В зависимости от того, какое средство
Гпава 20. Запросы DML 415 разработки вы используете, параметризованная версия последнего примера может выглядеть несколько иначе, например, так: SELECT MAX (COST * QUANTITY) AS BEST_SALE FROM SALES WHERE SALES_DATE > ? ; Заменяемый параметр в этом примере позволяет приложению подготовить оператор, а затем получать от пользователя (или от некоторой другой части приложения) фак- тическую дату, по которой запрос должен быть отфильтрован. Этот запрос может выполняться произвольное количество раз в одной и той же транзакции или в после- довательности транзакций с различными значениями даты без необходимости по- вторной подготовки. API "знает" порядок и формат параметров, потому что приложение передает описа- тельные структуры XSQLDA, которые содержат массив sqlvar— дескрипторы пере- менных, описывающих каждый параметр, — и другие данные, описывающие массив в целом. Delphi и другие объектно-ориентированные реализации API используют методы и свойства классов, чтобы скрыть от разработчика механизм, применяемый для созда- ния и управления сырым оператором и дескрипторами. Другие средства разработки кодируют эти структуры иным образом. Оператор может передаваться на сервер приблизительно в таком виде: INSERT INTO DATATABLE (DATA1, DATA2, DATA3, DATA4, DATA5, . . . другие столбцы) VALUES (?, ?, ?, ... другие значения); Если параметры реализованы в языке программирования вашего приложения, то весьма рекомендуется их использовать. Замечания для пользователей Delphi Delphi, имеющее клеймо "made in Borland", как и InterBase, предшественник Firebird, при передаче переменных включающего языка реализует формат, который повторяет формат, используемый в PSQL для ссылки на локальные переменные в SQL- операторах и ESQL. Он требует, чтобы все параметры были явно именованы и начи- нались с символа двоеточия. В Delphi предыдущий простой пример может быть представлен в свойстве SQL объекта доступа к данным в следующем виде: SELECT MAX(COST * QUANTITY) AS BEST_SALE FROM SALES WHERE SALES_DATE > :SALES_DATE ; Один вызов метода prepare приведет к проверке оператора и передаче описания ме- таданных назад в клиентское приложение; объект доступа к данным позволяет позже присваивать значение параметру, используя локальный метод, который выполняет преобразование значения к формату, требуемому в Firebird: aQuery.ParamByName (1SALES_DATE1).AsDate := ALocalDateVariable;
416 Часть И Firebird SQL Пакетные операции Очень часто требуется выполнение задач, которые добавляют, изменяют или удаляют множество строк в одной операции. Например, приложение читает файл данных с внешнего устройства и помещает данные в операторы insert для помещения в табли- цу базы данных. Или сервис репликации обрабатывает пакеты изменений между вспомогательной и родительской базами данных. Множество похожих операторов выполняется в одной транзакции обычно с помощью подготовленного оператора и заменяемых во время выполнения параметров. Пакетные добавления Оператор для пакетных добавлений может выглядеть следующим образом: INSERT INTO DATATABLE (DATA1, DATA2, DATA3, DATA4, DATA5, ... .другие столбцы) VALUES ('х', 'у', 'z', 99, '2004-12-25', ....другие значения); При использовании параметров он будет выглядеть так: INSERT INTO DATATABLE(DATA1, DATA2, DATA3, DATA4, DATA5,.... другие столбцы) VALUES (?, '?', '?', ?, ?, .... другие значения); Часто хранимая процедура бывает наиболее элегантным способом выполнения по- вторяемой пакетной операции, особенно если есть требование преобразования дан- ных по дороге к таблице базы данных или при добавлении строк во множество таблиц. Предотвращение замедления пакетной операции Два поведения могут привести к огромному объему пакетных операций, резко замед- ляющих выполнение. ♦ Использование индексов, которые добавляют задаче работы, а также имеют тен- денцию к деформированию геометрии индексных деревьев. ♦ Накопление предыдущих версий, небольших изменений старых строк, которые отмечаются как устаревшие при подтверждении операций изменения или удале- ния. Предыдущие версии не будут доступными для сборки мусора, пока не за- вершится транзакция. В транзакциях, включающих предыдущие версии в количе- стве десятков или сотен тысяч (или более), сборка мусора никогда не будет побе- дителем в этой битве за очистку базы данных от устаревших версий записей. При этих условиях только резервное копирование и восстановление сможет пол- ностью оздоровить базу данных. Деактивация индексов Стратегией уменьшения степени замедления пакетных операций, когда другие поль- зователи не имеют доступа к входным таблицам, является деактивация вторичных индексов. Это просто сделать: ALTER INDEX имя-индекса INACTIVE;
Гпава 20. Запросы DML 417 Когда большая пакетная операция закончится, реактивируйте и пересоздайте ин- дексы: ALTER INDEX имя-индекса ACTIVE; Разбиение больших пакетов К сожалению, вы не можете отключить индексы, поддерживающие первичные и внешние ключи, без удаления соответствующих ограничений. Часто бывает нереаль- ным удаление таких ограничений по причине цепных зависимостей. По этой и дру- гим причинам рекомендуется разумное разделение задачи для компенсации ухудше- ния сбалансированности индекса и генерации необоснованно большого количества новых страниц базы данных. При помещении в базу данных большого количества данных в пакете предпочти- тельным является разбиение их на группы и подтверждение работы приблизительно через каждые 5000—10 000 строк. Фактический размер оптимального разбиения будет меняться в зависимости от размера строки и размера страниц базы данных. Оптимальные группы могут быть меньше или больше указанного диапазона. СОВЕТ. Когда вам нужно разделять огромные пакеты, часто хранимые процеду- ры являются способом это сделать. Вы можете использовать локальные пере- менные-счетчики, сообщения о событиях и возвращать значения для сохранения вызова процедуры с целью синхронизации с вызовами клиента. Операции DML и события изменения состояния Firebird включает некоторые особенности, которые могут быть использованы при проектировании базы данных для реагирования на операции DML, которые изменя- ют состояние данных, — а именно выполнение операторов insert, update и delete. Предложения действия ссылочной целостности Триггеры ссылочной целостности являются модулями компилированного кода, соз- даваемыми ядром сервера, когда вы объявляете ограничение ссылочной целостности для ваших таблиц. Включив предложения действия on update и on delete в объявле- ние ограничения foreign key, вы можете задать одно из группы действий, которое будет выполняться при наступлении соответствующего события DML. Подробности см. в главе 17. Пользовательские триггеры В пользовательских триггерах (тех, которые вы пишете сами, используя язык PSQL) у вас есть возможность точно задать, что происходит, когда сервер получает запрос на добавление, изменение или удаление строк таблицы. Пользовательские триггеры 14 Зак. 420
418 Часть И Firebird SQL могут применяться не только для изменения и удаления, но также и для добавления. Триггеры могут включать обработку исключений, обратные связи и (для Firebird 1.5) пользовательские планы запросов. Фазы событий DML Синтаксис триггера разделяет пользовательские действия DML на две фазы: первая фаза появляется до (before) события, а вторая после (after) события. ♦ Фаза before дает возможность управлять преобразованием значений, которые яв- ляются входными в операторе DML, и определять значения по умолчанию гораз- до более гибкими способами, чем это позволено в стандартном SQL-ограничении default. Фаза before завершается перед тем, как начинают проверяться любые ог- раничения столбца, таблицы или ограничения целостности. ♦ В фазе after ответные действия могут быть выполнены над другими таблицами. Обычно такие действия включают добавления, изменения или удаления данных других таблиц с использованием переменных new и old для обеспечения контек- ста текущей строки и операции. Фаза after начинается после применения всех ограничений собственной таблицы. Триггеры after не могут изменять значения в текущей строке собственной таблицы. Табл. 20.1 описывает шесть фаз/событий пользовательских триггеров. Таблица 20.1. Шесть фаз/событий пользовательских триггеров Добавление Изменение Удаление BEFORE insert BEFORE UPDATE BEFORE DELETE AFTER INSERT AFTER UPDATE AFTER DELETE Контекстные переменные NEW к OLD Сервер делает доступными для триггеров два набора контекстных переменных. Один состоит из всех значений полей текущей строки, какими они были перед последним помещением этой строки в базу данных. Идентификаторы этого набора состоят из слова "old.", за которым следует идентификатор столбца. Аналогичным образом все новые значения имеют префикс "new." перед каждым идентификатором столбца. Ра- зумеется, "old." не имеет смысла в триггерах добавления, a "new." бессмысленно ис- пользовать в триггерах удаления. Триггеры многих действий В Firebird 1.5 и выше вы можете писать триггеры с условной логикой для всех собы- тий (добавление, изменение и удаление) для одной из фаз— before или after — в одном модуле триггера. Это долгожданное улучшение, которое уменьшает кодиро- вание триггеров на две трети.
Гпава 20. Запросы DML 419 Несколько триггеров на событие Другой полезной возможностью является использование нескольких триггеров для каждой комбинации фаза/событие. Синтаксис create trigger включает ключевое сло- во position, принимающее целый аргумент, который может быть использован для установки начинающегося с нуля порядка, в котором будут выполняться триггеры для одной фазы. Подробные инструкции, синтаксис и языковые расширения для создания триггеров см. в главе 31. Пора дальше DML проявляет свою реальную мощь в возможности использовать выражения при поиске хранимых данных и преобразовании абстрактных данных в выходные, кото- рые имеют смысл для конечного пользователя как информация. В следующей главе рассматривается логика использования выражений SQL вместе с внутренними и внешними функциями, которые вы можете использовать для создания простых или сложных алгоритмов для выполнения необходимых вам преобразований.
ГЛАВА 21 Выражения и предикаты В алгебре выражение типа а + b = с может иметь решение "истина" или "ложь" при подстановке значений в а, b и с. Альтернативный вариант — если задано два значе- ния из а, Ь, с, мы можем вычислить отсутствующее значение. Это и является выра- жением SQL — формула подстановки. Выражения SQL предоставляют формальные компактные методы для вычисления, преобразования и сравнения значений. В этой главе мы подробно рассмотрим выра- жения в SQL Firebird. В конце этой главы приведено много информации по внутренним функциям SQL, доступным в Firebird для создания выражений, а также по большинству общих внеш- них функций. Выражения Хранение данных в простом, наиболее абстрактном виде — вот что делают базы дан- ных. Язык поиска — в случае Firebird это обычно SQL — вместе с ядром сервера ба- зы данных предоставляют целый арсенал готовых формул, в которые во время вы- полнения можно подставлять фактические данные для преобразования фрагментов абстрактных данных в информацию, имеющую смысл для человека. Для простого примера возьмем таблицу membership, которая имеет столбцы first_name, last_name и date_of_birth. Для получения списка, содержащего полное имя и дату рождения, мы можем использовать оператор, содержащий выражения для преобразования хранимых данных: SELECT FIRST_NAME II' ' I | LAST_NAME AS FULL_NAME, EXTRACT (MONTH FROM DATE_OF_BIRTH) I I '/'I I EXTRACT (DAY FROM DATE_OF_BIRTH) AS BIRTHDAY FROM MEMBERSHIP WHERE FIRST_NAME IS NOT NULL AND LAST_NAME IS NOT NULL ORDER BY 2; Во время отправления запроса на сервер мы не знаем, какие значения хранятся в базе данных. Однако мы знаем, на что они должны быть похожи (семантика их значений и типы данных), для нас этого достаточно, чтобы сконструировать выражения для поиска списка в том виде, который будет для нас иметь смысл.
Гпава 21. Выражения и предикаты 421 В этом одном операторе мы используем три вида выражений SQL. ♦ Для первого поля full name используется оператор конкатенации (в SQL два символа вертикальной черты 11) для создания выражения, которое объединяет два поля базы данных, разделенные пробелами в одно. ♦ Для второго поля birthday используется функция для выделения сначала месяца, а затем дня месяца из поля даты. В том же выражении опять используется оператор конкатенации для объединения вместе выделенных чисел в качестве даты рожде- ния. День отделяется от месяца наклонной чертой. ♦ В качестве условия поиска предложение where использует другой вид выраже- ния— логический предикат — для проверки подходящих строк в таблице. Стро- ки, которые не удовлетворяют этой проверке, не помещаются в выходной набор. В этом примере выражения были использованы: ♦ для преобразования найденных данных в выходные столбцы; ♦ для задания условий поиска в предложении where оператора select. Тот же подход может быть использован для условий поиска операторов update и delete. Другие контексты, где могут использоваться выражения: ♦ для задания условий проверки данных в ограничениях check; ♦ для определения вычисляемых (computed by) столбцов в операторах create table и ALTER TABLE; ♦ для преобразования или создания входных данных в процессе их сохранения в таблице при использовании операторов insert или update; ♦ для упорядочения или группирования выходных наборов; ♦ при установке во время выполнения условий определения вывода; ♦ в условиях потока управления в модулях PSQL. Предикаты Предикат — это просто выражение, которое утверждает некоторый факт о значении. Операторы SQL обычно проверяют предикаты во фразах where и в выражениях case, on является проверкой для предикатов join, having проверяет атрибуты в сгруппиро- ванном выводе. В PSQL операторы управления потоком выполнения проверяют пре- дикаты в предложениях if, while и when. Решения принимаются в соответствии с ре- зультатом вычисления предиката — истина или ложь. Строго говоря, предикат может быть истинным, ложным и неопределенным. В SQL ложный и неопределенный результаты объединяются и трактуются как ложь. Иными словами, "если он не истинный, значит он ложный". Стандартный язык SQL имеет формальные спецификации для множества операторов выражений, которые признаются необходимыми для конструирования предикатов поиска. Предикат состоит из трех базовых элементов: двух сравниваемых значений и оператора, который задает проверку утверждения об этой паре значений.
422 Часть V. Firebird SQL Все операторы, включенные в табл. 21.1 (которая появится позже в этой главе), могут быть операторами предикатов. Значения, включенные в предикат, могут быть про- стыми или чрезвычайно сложными вложенными выражениями. Пока сравниваемые выражения, которые могут растворяться среди константных значений, являются пра- вильными, то не имеет значения, насколько они сложны. Возьмем такой простой оператор, где равенство = используется для проверки точного соответствия: SELECT * FROM EMPLOYEE WHERE LAST_NAME = 'Smith'; Это предикат "значением столбца last name является 'smith'". Сравниваются две константы (текущее значение столбца и строковый литерал) для проверки их равен- ства. Выбирая каждую строку из таблицы employee, сервер будет отбрасывать все, где предикат является ложным (значение отличается от 'Smith') или неопределенным (столбец имеет значение null, следовательно, он не может быть определен, как имеющий значение 'Smith' или не имеющий значение 'Smith'). Где проверяется истинность Синтаксическими элементами, проверяющими истинность, являются: ♦ в DDL: check для проверки условий достоверности данных; ♦ в SQL: where (для условий поиска), having и not having (для условий выбора групп), on (для условий соединения) и случаи проверки множества условий case, coalesce и nullif; ♦ в PSQL: if (универсальная проверка истина/ложь), while (для проверки условий цикла) и when (для проверки кодов исключения). Утверждения Часто условия, проверяемые в where, if и т. д., не являются простыми предикатами, а группой нескольких предикатов, каждый из которых при вычислении делает вклад в вычисление общей истинности. Утверждение может состоять из одного предиката или из нескольких предикатов, связанных логическими операциями and или or. Каж- дый из этих предикатов сам может содержать вложенные предикаты. Окончательный результат вычисление истинности утверждения получается в результате процесса, который работает по направлению от внутренних предикатов к внешним. Каждый "уровень" должен быть вычислен в порядке своего приоритета, пока не будет воз- можным получить окончательное значение утверждения. В следующем наборе условий поиска проверяемое утверждение содержит два усло- вия. Ключевое слово and связывает два предиката, приводя к тому, что окончательное утверждение будет истинным, если оба предиката будут истинными: SELECT * from employee where (
Гпава 21. Выражения и предикаты 423 (HIRE_DATE > CURRENT—DATE - 366) AND (SALARY BETWEEN 25000.00 AND 39999.99)); Строки, для которых одно утверждение истинно, а другое ложно, будут отбрасы- ваться. Первый предикат (hire_date > current_date - 366) использует выражение, состоящее из переменной и операции вычисления значения, которое будет сравниваться со зна- чением столбца. В этом случае утверждение использует оператор— утверждение будет истинным, если значение столбца будет больше, чем значение, полученное из выражения. Второй предикат использует другой оператор. Ключевое слово between задает про- верку "больше или равно значению в левой части И меньше или равно значению в правой части". Круглые скобки здесь не являются обязательными, хотя для многих сложных выра- жений скобки должны быть использованы для задания приоритета вычислений вы- ражений и предикатов. В ситуациях, когда проверяются серии предикатов, использо- вание для них скобок является хотя и необязательным, но весьма полезным для до- кументирования и отладки. Решение, что является истинным На рис. 21.1 показаны возможные результаты вычисления двух предикатов из преды- дущего примера. В нашем примере вначале проверяется (hire_date > current_date - 366), потому что это самый левый предикат. Если бы у него были вложенные предикаты, то сначала проверялись бы они. Ни для одного из предикатов нет проверки на null, но мы вклю- чили сюда тот случай, когда встречается пустое ("неизвестное") значение в прове- ряемых данных, что приведет к результату "ложь", поскольку проверка не может вернуть истину. Должна быть доказана истинность предикатов, чтобы они считались истинными. Если null встречается в hire date, то проверка немедленно прекращается и возвраща- ет ложь1. Связка and означает: два предиката, соединенные операцией and, не могут давать результат истина, если один из них является ложным, следовательно, нет не- обходимости выполнять проверку второго предиката. Если проверяемые данные не являются null, то следующей является проверка, верно ли, что значение больше указанной константы. Если нет, проверка прекращается. Другими словами, null и ложь имеют один и тот же эффект, поскольку оба не явля- ются истиной. 1 Это "ускоренное" вычисление логических значений, которое реализовано в Firebird 1.5 и выше. Firebird 1.0.x использует полное логическое вычисление. Для тех, кто любит изобрета- тельные загадочные выражения, старый метод может быть восстановлен при использовании параметра конфигурации FullBooleanEvaluation.
424 Часть V. Firebird SQL ASSERTION FALSE Рис. 21.1. Вычисление истинности Если первый предикат истинный, похожий процесс вычисления выполняется для второго предиката. Только когда будет выполнено все это, процесс вычисления ут- верждения завершается. Символы, используемые в выражениях Табл. 21.1 описывает символы, которые могут появляться в выражениях SQL. Таблица 21.1. Элементы выражений SQL Элемент Описание Имя столбца Идентификаторы столбцов из указанных таблиц, пред- ставляющих поле, используемое в вычислении, или срав- нении, или в качестве условия поиска. На любой столбец базы данных может быть ссылка в выражении за исключе- нием столбцов типа массива. (Исключение: любой стол- бец, являющийся массивом, может проверяться на is [NOT] NULL) Элементы массива На элементы массива может быть ссылка в выражении
Гпава 21. Выражения и предикаты 425 Таблица 21.1 (продолжение) Элемент Описание Имена столбцов только для вывода Во время выполнения идентификаторы задают вычисляе- мые столбцы или алиасы столбцов базы данных Ключевое слово as Используется (необязательно) как маркер для имени толь- ко для выходного столбца списка столбцов в select Арифметические операторы Символы * и / используются для вычисления значе- ний Логические операторы Зарезервированные слова not, and и ок используются в простых условиях поиска или при комбинировании про- стых условий поиска для создания сложных предикатов Операторы сравнения <, >, <=, >=, = и <> используются для сравнения утвержде- ний Другие операторы сравнения LIKE, STARTING WITH, CONTAINING, BETWEEN, И IS [NOT] NULL Операторы существования Предикаторы, используемые для проверки существования значения в наборе, in может быть использован с набора- ми констант или со скалярными подзапросами, exists, singular, all, any и some могут быть использованы только с подзапросами Оператор конкатенации Пара из вертикальных черт (] |) используется для соеди- нения символьных строк. Обратите внимание, что симво- лы + и & не являются символами конкатенации в стандар- те SQL Константы Числа или заключенные в апострофы строковые литера- лы, такие как 507 или 'Tokyo', которые могут быть вклю- чены в вычисления или сравнения в качестве полей вре- мени выполнения Литералы даты Выражения, подобные строковым литералам, заключен- ным в апострофы, которые могут быть интерпретированы как значения даты, времени или даты-времени в операци- ях extract, select, insert и update. Литералами даты могут быть предварительно определенные литералы ('TODAY', 'NOW' 'YESTERDAY', 'TOMORROW') ИЛИ подходя- щие строки даты и времени, как описано в главе 10. В диалекте 3 литералы даты обычно требуют преобразо- вания (cast) в допустимый тип даты/времени при исполь- зовании в выражениях extract и select Внутренние контекстные переменные Получаемые с сервера переменные, которые возвращают зависимые от контекста значения, такие как серверное время или идентификатор текущей транзакции Подзапросы Внутренние операторы select, которые возвращают единственное (скалярное) значение для вывода или для сравнения в предикате Локальные переменные Именованные хранимые процедуры, триггеры или (в ESQL) переменные приложений, содержащие значения, которые могут изменяться в процессе выполнения
426 Часть 1/. Firebird SQL Таблица 21.1 (окончание) Элемент Описание Идентификаторы функций Идентификаторы внутренних или внешних функций в функциональных выражениях CAST(значение AS тип-данных} Функциональные выражения, явно преобразующие значе- ние одного типа данных в другой тип данных Условные выражения Функции, объявляющие два или более взаимоисключаю- щих условия для одного столбца, начинающиеся с ключе- вого слова CASE, COALESCE ИЛИ NULLIF Круглые скобки Используются для группирования выражений. Операции внутри скобок выполняются перед операциями вне скобок. Когда используются вложенные скобки, вначале вычисля- ются значения самых внутренних выражений, затем вы- числения перемещаются вверх по уровням вложенности Предложение collate Может быть применено со значениями char и varchar, чтобы использовать строковые сравнения в указанной последовательности сортировки Операторы SQL Синтаксис SQL Firebird включает операторы для сравнения и вычисления значений столбцов, констант, переменных и выражений встраиваемого SQL для получения различных утверждений. Приоритет операторов Приоритет определяет порядок, в котором операторы и создаваемые ими значения вычисляются в выражении. Когда выражение содержит несколько операторов одного и того же типа, операторы вычисляются слева направо, если только не существует конфликта, когда два опера- тора одного и того же типа воздействуют на одни и те же значения. Когда возникает конфликт, приоритет операторов определяется типом. В табл. 21.2 показаны приори- теты типов операторов Firebird от высшего к низшему. Таблица 21.2. Приоритеты типов операторов Тип оператора Приоритет Объяснение Конкатенация 1 Строки объединяются до выполнения любых других опе- раций Арифметический 2 Арифметические операции выполняются после конкатена- ции строк, но перед выполнением сравнений и логических операций
Гпава 21. Выражения и предикаты 427 Таблица 21.2 (окончание) Тип оператора Приоритет Объяснение Сравнение 3 Операции сравнения вычисляются после конкатенации строк и выполнения арифметических операций, но до логи- ческих операций Логический 4 Логические операции выполняются после всех других опе- раций. Приоритет: когда условия поиска являются комбинирован- ными, порядок вычислений определяется приоритетом используемых операторов, мот вычисляется перед and, а and вычисляется перед or. Круглые скобки могут использо- ваться для изменения порядка вычисления Оператор конкатенации Оператор конкатенации (| |) соединяет две символьные строки и создает одну строку. Символьные строки могут быть константами или значениями, полученными из столбцов: SELECT Last_name fl’, ' II First_Name AS Full_Name FROM Membership; < ПРИМЕЧАНИЕ. Firebird более требователен к переполнению строк, чем его предшественники. Он проверит длину входных столбцов и заблокирует конкате- .__X нацию, если результирующий размер потенциально может превысить лимит для varchar (32 765 байтов). Арифметические операции Арифметические выражения вычисляются слева направо за исключением случаев, когда возникает двусмысленность. В этих случаях арифметические операции вычис- ляются в соответствии с приоритетами, описанными в табл. 21.3. Например, умноже- ние выполняется до деления, а деление выполняется до вычитания. Арифметические операции всегда выполняются до сравнений и логических опера- ций. Для изменения порядка вычислений сгруппируйте операции с помощью круг- лых скобок. Таблица 21.3. Приоритеты арифметических операций Оператор Назначение Приоритет * Умножение 1 / Деление 2 Сложение 3 - Вычитание 4
428 Часть И Firebird SQL Следующий пример иллюстрирует, как сложное вычисление может быть сделано вложенным, для обеспечения того, чтобы выражение вычислялось в корректном, не- двусмысленном порядке: SET COLUMN_A = 1/((COLUMN_B * COLUMN_C/4) - ( (COLUMN_D / 10) + 28)) Сервер может определить синтаксические ошибки — такие как несбалансированные скобки или включенное приложение, которое не дает результата, — но он не может определить логические ошибки или двусмысленности, которые являются синтакси- чески корректными. Очень запутанные вложенные выражения теоретически должны работать. Для упрощения процесса объединения таких предикатов всегда начинайте с изоляции самой внутренней операции и продолжайте работать "изнутри наружу", проверяя предикаты на калькуляторе на каждом шаге. Операторы сравнения Операторы сравнения проверяют отношение между значением в левой части опера- тора и значением или диапазоном значений в правой части оператора. Каждая про- верка дает результат, который может быть истиной или ложью. Правила приоритета, применимые в вычислениях, указаны в табл. 21.4. Таблица 21.4. Приоритеты операторов сравнения Оператор Назначение Приоритет ““ Равно, идентично 1 <>, | = 1 ~=1 л= Не равно 2 > Больше 3 < Меньше 4 >= Больше или равно 5 <= Меньше или равно 6 !>, '>, л> Не больше 7 !<1 -< Не меньше 8 Сравнения, которым встречается null в левой или правой части оператора, всегда следуют правилам логики SQL, вычисляя результат сравнения как null и возвращая ложь, например: IF (YEAR_OF_BIRTH < 1950) Это сравнение вернет ложь, если year_of_birth равняется 1950, больше него или име- ет значение null. Подробное обсуждение null см. в разд. "Обсуждение NULL".
Гпава 21. Выражения и предикаты 429 Пары сравниваемых значений могут быть столбцами, константами или вычисляемы- ми выражениями2, они должны быть вычислены в том же типе данных. Функция casto может быть использована для трансляции в тип данных, совместимый для сравнения. Арифметические предикаты, использующие эти символы, не требуют пояснения. При этом символьные проверки также могут их использовать. Проверки, исполь- зующие сравнения = и о, понятны. Следующий оператор использует оператор >=, чтобы вернуть все строки, где last_name равен тестируемой строке, а также те строки, где last name рассматривается в алфавитно-цифровом порядке, как "больше, чем": SELECT * FROM EMPLOYEE WHERE LAST_NAME >= 'Smith'; В таблице EMPLOYEE нет значений 'Smith', НО запрос вернет 'Stansbury', 'Steadman' И так далее вплоть до ' Young'. База данных Employee в этом примере использует набор символов none, в котором символы в нижнем регистре имеют приоритет перед сим- волами в верхнем регистре. В нашем примере строка 'smith' не будет выбрана, пото- му что искомая строка 'зм' (83 + 77) будет меньше, чем *sm' (83 + 109). Наборы символов и последовательности сортировки Алфавитно-цифровые последовательности определяются на двух уровнях: набор символов и последовательность сортировки. Каждый набор символов имеет свои собственные уникальные правила приоритетов. Когда используется альтернативная последовательность сортировки, то правила последовательности сортировки пере- крывают правила набора символов. Последовательность сортировки по умолчанию для каждого набора символов — та последовательность, чье имя соответствует имени набора символов, — является би- нарной. Двоичная последовательность сортировки определяет возрастание по цифро- вым кодам символа в той системе, в которой он кодирован (ASCII, ANSI, Unicode и т. д.). Альтернативные последовательности сортировки обычно перекрывают поря- док по умолчанию, чтобы соблюдать локальные или специальные правила, такие как нечувствительность к регистру или упорядочение по словарю3. Другие предикаты сравнения Предикаты between, containing, like и starting with, представленные в табл. 21.5, имеют одинаковый приоритет при вычислениях в операторах сравнения, описанных 2 Операторы сравнения Firebird поддерживают сравнение значения левой части с результа- том скалярного подзапроса (встроенного запроса) в правой части оператора. Подробности см. в разд. "Запросы существования" позже в этой главе и в обсуждении подзапросов в следую- щей главе. 3 Для русского языка обычно используется кодировка WIN 1251, и два варианта collate — WIN 1251 (по умолчанию), и PXWCYRL. За особенностями использования вариантов collate, и кодировками и сортировками для других языков (в том числе стран СНГ) обрати- тесь к ReleaseNotes. — Прим. науч. ред.
430 Часть И Firebird SQL в табл. 21.4. Если они конфликтуют друг с другом, то вычисление осуществляется строго слева направо. Таблица 21.5. Другие предикаты сравнения Предикат Назначение BETWEEN ... AND ... Значение попадает во включающий диапазон значений CONTAINING Строковое значение содержит указанную строку. Сравнение является независимым от регистра IN Значение присутствует в указанном наборе* LIKE Равенство указанной строке с шаблонными подстановками STARTING WITH Строковое значение начинается с указанной строки Наиболее эффективной является ситуация, когда набор содержит небольшое количество констант. Набор не может превышать 1500 элементов. (См. разд. "Предикаты существования".) between использует два аргумента совместимых типов, разделенных ключевым сло- вом and. Меньшее значение должно быть первым аргументом, если нет, не найдет ни одной строки, соответствующей предикату. Поиск является включающим — то есть значения, соответствующие обоим аргументам, будут включены в возвращаемый набор, например: SELECT * FROM EMPLOYEE WHERE HIRE_DATE BETWEEN '01.01.1992' AND CURRENT_TIMESTAMP; between будет использовать возрастающий индекс по искомому столбцу, если таковой доступен. containing ищет строку или тип, подобный строке, отыскивая последовательность символов, которая соответствует его аргументу. Он может быть использован для алфавитно-цифрового (подобного строковому) поиска в числах и датах. Поиск containing является чувствительным к регистру. Следующий пример SELECT * FROM PROJECT WHERE PROJ_NAME CONTAINING ' Map ' ; возвращает две строки проекта: 'AutoMap' И 'MapBrowser port'. Следующий пример SELECT * FROM SALARY_HISTORY WHERE CHANGE_DATE CONTAINING 93; возвращает все строки, где датой является год 1993. Л ВНИМАНИЕ! В некоторых релизах containing является непредсказуемым, когда используется для blob, чей размер больше 1024 байт. Это определено как ошиб- ка и будет вскоре исправлено в следующем после 1.5 релизе.
Гпава 21. Выражения и предикаты 431 in принимает в качестве аргумента список значений и возвращает все строки, где значение столбца в левой части равно любому значению в множестве, например: SELECT * FROM EMPLOYEE WHERE FIRST_NAME IN('Pete', 'Ann', 'Roger'); null не может быть включен в множество поиска. Список аргументов в in о может быть представлен в виде подзапроса, который воз- вращает строки, состоящие из одного столбца. Подробности см. в разд. "Предикаты существования" далее в этой главе. Д ВНИМАНИЕ! Общей ошибкой "чайников" является трактовка предиката in (<значение>) как если бы он был эквивалентным предикату =<значение>, пото- му что оба предиката логически эквивалентны в том смысле, что оба возвращают один и тот же результат. При этом in не использует индексы. Поскольку среди равных поисков обычно лучшим является тот, который оптимизирован по индек- су, использование in является плохим решением с точки зрения производитель- ности. like является чувствительным к регистру шаблоном оператора поиска. Это довольно тупой инструмент, выполняющий сравнение с шаблоном поиска в обычном выраже- нии. Он распознает два шаблонных символа: % и _ (символ подчеркивания), которые работают следующим образом. ♦ Символ % может быть подставлен в строку поиска для представления любого ко- личества любых символов, в том числе и нулевого количества, например: LIKE '%mit%' вернет истину ДЛЯ таких строк, как 'blacksmith', 'mitgenommen', а Также И 'mit'. ♦ Символ подчеркивания (_) может быть вставлен в строку поиска для представле- ния одного неопределенного символа. Например, следующее условие задает по- иск записей, где фамилия может быть 'Smith', 'Smyth' или другой, похожей на шаблон: LIKE 'Sm_th' Если вам нужно отыскать строку, которая содержит один или оба шаблонных симво- ла в литерале шаблона, вы можете "отменить" символ литерала— то есть отметить его символом, который предшествует специальному отменяемому символу. Например, скажем, вам нужно использовать оператор like для поиска всех имен сис- темных таблиц. Предположим, что все имена системных таблиц содержат, по мень- шей мере, один символ подчеркивания— а это почти правда! Вы решили использо- вать символ # в качестве символа отмены. Вот что вам нужно сделать: SELECT RDB$RELATION_NAME FROM RDB$RELATIONS WHERE RDB$RELATION_NAME LIKE '%#_%' ESCAPE Поиски like не используют индекс. При этом предикат, использующий like 'Ваша строка*', будет преобразован в предикат starting with, который будет использовать индекс, если тот доступен.
432 Часть V. Firebird SQL Предикаты starting with являются чувствительными к регистру. Они используют правила соответствия байтов применяемого набора символов и последовательности сортировки. При необходимости они могут использовать аргумент collation для по- иска с применением конкретной последовательности сортировки. Следующий пример SELECT LAST_NAME, FIRST_NAME FROM EMPLOYEE WHERE LAST_NAME STARTING WITH ' Jo ' ; вернет строки, где фамилиями будут ' Johnson', ' Jones ', ' Joabim1 и др. В базе данных, хранящей в строковых столбцах тексты на немецком языке, где набор символов по умолчанию 1SO8859 1, вам может понадобиться преобразовать после- довательность сортировки, например: SELECT FAM_NAME, ERSTE_NAME FROM ARBEITNEHMER WHERE COLLATE DE_DE FAM_NAME STARTING WITH 'Schmi'; starting with будет использовать индекс, если он доступен. Логические операторы Firebird предоставляет три логических оператора, которые могут работать с другими предикатами разными способами. ♦ not задает отрицание условия поиска, к которому он применяется. Он имеет наи- высший приоритет. ♦ and создает сложный предикат, объединяет два или более предикатов, каждый из которых должен быть истинным, чтобы был истинным и весь предикат. Он сле- дующий по приоритету. ♦ or создает сложный предикат, объединяет два или более предикатов, из которых хотя бы один должен быть истинным, чтобы был истинным весь предикат. Явля- ется последним по приоритету. По умолчанию Firebird 1.5 использует сокращен- ную булеву логику для определения значения предиката or. Как только один из внутренних предикатов вернет истину, вычисления останавливаются, и весь пре- дикат вернет истину4. Вот пример логического предиката: COLUMN_1 = C0LUMN_2 OR ( (COLUMN_1 > COLUMN_2 AND COLUMN_3 < COLUMN_1) AND NOT (COLUMN_4 = CURRENT_DATE) ) Опишем результат такого предиката. Если column_i и column_2 будут равны, то вы- числение останавливается, и предикат возвращает истину. 4 Firebird 1.0.x выполняет полные логические вычисления. Сокращенное вычисление буле- вых выражений в более поздних версиях может быть отключено на уровне сервера при уста- новке параметра конфигурации CompleceBooleanEvaluation. Подробности см. в главе 3.
Глава 21. Выражения и предикаты 433 В противном случае проверяется следующий предикат — с двумя вложенными пре- дикатами. Первый вложенный предикат сам имеет два вложенных предиката, оба из которых должны быть истинными, чтобы этот предикат был истинным. Если column_i меньше, чем column_2, то вычисление останавливается, и весь предикат вер- нет ложь. Если column_l больше, то column_3 сравнивается с column_1. Если значение column_3 больше или равно column_i, то вычисление останавливается, и весь предикат вернет ложь. Иначе производится вычисление последнего предиката. Если column s равно текущей дате, то весь предикат вернет ложь, в противном случае он вернет истину. Включающее ИЛИ в сравнении с исключающим Оператор or в Firebird является включающим ИЛИ (одно условие или все условия должны быть истинными). Исключающее ИЛИ (которое дает истину, только когда ровно одно условие является истинным, а все остальные ложными) не поддержи- вается. Предикат IS [NOT] NULL is null и его противоположность is not null являются парой предикатов, которые не используют группирование. Поскольку null не является значением, эти операторы не являются операторами сравнения. Они проверяют утверждение, что объект в ле- вой части имеет значение (is not null) или не имеет значения (is null). Если значение имеется, то is not null возвращает истину независимо от содержимо- го, размера или типа данных значения. Новички в SQL иногда путают ограничение not null с предикатами is [not] null. Общая ошибка считать, что is null и is not null проверяют, был ли определен столбец с ограничением not null. Это не так. Они тестируют текущее содержимое столбца на наличие или отсутствие значения. Предикат is [not] null может быть использован для проверки входных данных, по- мещаемых в столбцы таблиц базы данных, имеющих ограничения not null. Обычно эти предикаты применяются в триггерах before insert (до добавления). Более подробную информацию о null см. в разд. "Рассмотрение NULL". Предикаты существования Последняя группа предикатов включает предикаты, которые используют подзапросы для передачи значений для различного вида утверждений в условиях поиска. Ключе- вые слова для этих предикатов all, any, exists, in, singular и some. Они называются предикатами существования, потому что все они используются в предикатах поиска, которые разными способами проверяют существование значения в левой части пре- диката в выходных результатах включенных запросов к другим таблицам.
434 Часть V. Firebird SQL Все эти предикаты— описанные в табл. 21.6— используют в том или ином виде подзапросы. Тема подзапросов подробно рассматривается в главе 22. Таблица 21.6. Предикаты существования Предикат Назначение ALL Проверяет, является ли сравнение истинным для всех значений, возвра- щенных подзапросом [NOT] EXISTS Существует ли (или нет) по крайней мере одно значение в выходном ре- зультате подзапроса [NOT] IN Существует ли (или нет) по крайней мере одно значение в выходном ре- зультате подзапроса [NOT] SINGULAR Проверяет, возвращает ли подзапрос в точности одно значение. Если возвращается null или более одного значения, то singular дает ложь (a NOT SINGULAR— истину) SOME Проверяет, является ли сравнение истинным по крайней мере для одного значения, возвращаемого подзапросом ANY Проверяет, является ли сравнение истинным по крайней мере для одного значения, возвращаемого подзапросом, some и any эквивалентны Предикат EXISTS Самый полезный из всех предикатов существования — exists предоставляет самый быстрый из всех возможных методов способ проверки существования значения в другой таблице. Часто в хранимых процедурах или запросах вам может понадобиться узнать, сущест- вуют ли в таблице строки, соответствующие некоторому набору критериев. Вас не интересует, сколько существует таких строк — нужно только определить, есть ли хотя бы одна. Стратегия выполнения count(*) с последующей проверкой, что полу- ченный результат больше нуля, дорого стоит в Firebird. Д ВНИМАНИЕ! Счетчик строк, полученный в контексте одной транзакции для про- верки условия последующей работы в другой транзакции, — например, для вы- числения значения "следующего" ключа — является совершенно ненадежным. Стандартный предикат SQL exists (значение -подзапроса) и его противоположный аналог not exists дают способ выполнения проверки существования набора, кото- рый является очень дешевым с точки зрения используемых ресурсов. Он не генери- рует выходной набор, а просто проходит по таблице, пока не найдет строку, соответ- ствующую условиям в подзапросе. В этот момент возвращается истина. Если же не находится ни одной строки, то возвращается ложь. В первом примере exists проверяет условия для выполнения изменения в операторе динамического SQL:
Гпава 21. Выражения и предикаты 435 UPDATE TABLEA SET C0L6 ='SOLD' WHERE C0L1 =99 AND EXISTS(SELECT COLB FROM TABLEB WHERE COLB = 99); Оператор, подобный нашему примеру, обычно использует заменяемые параметры в правой части предикатов в предложении where. В действительности многие подзапросы в предикатах exists являются коррелирован- ными, то есть условия поиска подзапроса связаны отношением с одним или более столбцами главного запроса. Возьмем предыдущий пример и заменим жестко зако- дированную константу в условии поиска ссылкой на столбец. Более реальный запрос может быть таким: UPDATE TABLEA SET TABLEA.COL6 ='SOLD' WHERE EXISTS(SELECT TABLEB.COLB FROM TABLEB WHERE TABLEB.COLB = TABLEA.COL1); Действием такого выражения существования будет: "Если в таблице tableb сущест- вует хотя бы одна соответствующая строка, то выполнить изменение". Предикат IN Предикат in, используемый в подзапросе, похож на exists постольку, поскольку он может проверять результат подзапроса. Например: UPDATE TABLEA SET COL6 ='SOLD' WHERE COL1 IN (SELECT COLB FROM TABLEB WHERE COLJ > 0); В этом случае подзапрос возвращает набор всех значений столбца colb второй табли- цы, которые соответствуют его собственному предложению where. Предикат in при- водит к сравнению сои с каждым возвращенным значением набора, пока не будет найдено соответствие. Он будет выполнять изменение каждой строки таблицы tablea, для которой значение coli соответствует значению в наборе. ПРИМЕЧАНИЕ. В Firebird предикат in, который использует набор подзапроса, фактически реализуется предикатом exists для операции сравнения. Ограничения С точки зрения производительности предикат in не будет полезен, когда подзапрос возвращает достаточно большое количество значений. Чаще он используется при формировании набора значений для сравнения из небольшой таблицы соответствия (lookup table) или из фиксированного набора констант, например:
436 Часть V. Firebird SQL UPDATE TABLEA SET C0L6 ='SOLD' WHERE C0L1 IN ('A', ' В', 'C, 'D' ) ; Количество констант в наборе имеет ограничение максимум 1500 значений— воз- можно меньше, если значения велики по размеру, и размер строки запроса превыша- ет его лимит в 64 Кбайт. Предикат ALL Его использование лучше проиллюстрировать, начав с примера: SELECT * FROM MEMBERSHIP WHERE (EXTRACT (YEAR FROM CURRENT_DATE) - EXTRACT(YEAR FROM DATE_OF_BIRTH)) < ALL (SELECT MINIMUM_AGE FROM AGE_GROUP) ; Выражение в левой части вычисляет возраст в годах для каждого человека из табли- цы membership и выводит только тех людей, кто моложе минимального возраста в таблице minimum age. Предикат all имеет ограниченное использование, поскольку он подходит только для поиска в случае сортировки от большего к меньшему при про- верке исключения. Предикат SINGULAR Предикат singular похож на all, за исключением того, что он проверяет наличие од- ного и только одного соответствующего значения в наборе. Например, следующий запрос отыскивает все заказы, которые имеют только одну детальную строку: SELECT OH.ORDER_ID FRCM ORDER_HEADER ОН WHERE OH.ORDER_ID = SINGULAR (SELECT OD.ORDER__ID FROM ORDER_DETAIL OD) ; Предикаты ANYvi SOME Эти два предиката идентичны по поведению. Очевидно, оба представлены в стандар- те SQL для взаимозаменяемого использования с целью улучшения читаемости опера- торов. При сравнении на равенство они логически эквивалентны предикату exists. При этом, поскольку они предназначены и для других сравнений, таких как >, <, >=, <=, starting with, like и containing, они в особенности полезны для проверок суще- ствования, где exists не может быть использован. Следующий оператор будет отыскивать список всех служащих, кто имеет, по край- ней мере, одно изменение оклада в течение года после приема на работу: SELECT E.EMP_NO, E.FULL_NAME, E.HIRE_DATE FROM EMPLOYEE E WHERE E.HIRE_DATE + 365 > SOME ( SELECT SH.CHANGE_DATE FROM SALARY_HISTORY SH WHERE SH.EMP_NO = E.EMP_NO);
Глава 21. Выражения и предикаты 437 Алиасы таблиц Обратите внимание на использование алиасов таблиц в последних двух примерах, что уменьшает двусмысленность в соответствующих именах столбцов двух таблиц. Firebird весьма требователен к использованию алиасов таблиц в запросах к несколь- ким таблицам. Алиасы таблиц обсуждаются в главе 22. Обсуждение NULL null может оказаться довольно сложным для людей, ранее работавших с настольны- ми базами данных, которые просто использовали null для хранения "нулевых значе- ний": пустая строка, ноль для чисел, ложь для логических столбцов и т. д. В SQL лю- бой элемент данных в столбце, допускающем пустое значение (то есть в столбце, не имеющем ограничения not null), будет хранить элемент null, если ему не было при- своено никакого значения в операторе DML или в значении по умолчанию для столбца. Значения по умолчанию для всех столбцов в Firebird допускают пустые значения. Если ваша база данных не была сознательно спроектирована для предотвращения хранения значений null, ваши выражения должны быть готовы встретить пустое зна- чение. NULL в выражениях null не является значением, следовательно, он не может быть "равным" какому-либо значению. Например, воз такой предикат WHERE (COLL = NULL) вернет ошибку, потому что оператор равенства не является действительным для null, null является состоянием, и правильным предикатом проверки на null будет is null. Скорректированный предикат для предыдущего ошибочного тестирования будет иметь вид: WHERE (COL1 IS NULL) Вы также можете выполнять проверку на непустое значение: WHERE (COL1 IS NOT NULL) Два null не равны один другому. При конструировании выражений помните о тех случаях, когда предикат будет сведен к виду: WHERE <NULL result> = <NULL result> Здесь результатом всегда будет ложь при сравнении двух null. Выражение типа WHERE COL1 > NULL будет ошибочным, потому что арифметический оператор недопустим для null.
438 Часть V. Firebird SQL NULL в вычислениях В выражении пустой операнд даст результат вычисления null. Например, следующий оператор UPDATE TABLEA SET COL4 = COL4 + COL5; присвоит столбцу col4 значение null, если значением cols является null. В агрегатном (обобщающем) выражении, использующем операторы типа sumo, avgo или count (<имя столбца>), строки, содержащие null в соответствующем столбце, бу- дут проигнорированы, avgo сформирует числитель, суммируя непустые значения, и знаменатель, подсчитывая строки, содержащие непустые значения. Понимание истинности и ложности Семантически, если предикат возвращает "неопределенность", это не является ни истиной, ни ложью. В SQL при этом утверждения разрешаются только в виде "исти- на" или "ложь" — утверждение, которое не вычисляется как "истина", рассматрива- ется как "ложь". Условие if, неявно присутствующее в предикатах поиска, может вызвать у вас гал- люцинацию, когда not используется во вложенном условии: NOT <условие, дающее ложь> дает TRUE в то время как NOT <условие, даюцее NULL> дает NULL Чтобы получить пример, когда наши предположения могут оказаться ошибочными, рассмотрим следующее: WHERE NOT (COLUMNA = COLUMNB) Если оба столбца columna и columnb имеют значения и не равны, вычисление внутрен- него предиката (того, что заключен в скобки) даст "ложь". Утверждение not (false) вернет истину — противоположность ложного значения. Однако если любой из столбцов является null, внутренний предикат даст null, имеющий семантическое значение "неопределенный" ("непроверенный", "неопозна- ваемый"). Окончательное утверждение будет not (null), а результатом станет null. Обратите также внимание, что not (null) не является тем же самым, что и is not null— чисто бинарный предикат, который никогда не вернет "неопределенное зна- чение". ВНИМАНИЕ! Этот урок заключается в том, что нужно быть внимательным при • \ работе с логикой SQL и всегда жестко проверять ваши выражения. Учитывайте ——4- условия null, если вы можете это сделать, полностью исключайте утверждения not во вложенных предикатах.
Гпава 21. Выражения и предикаты 439 NULL и внешние функции (UDF) null не может передавать в виде входа или выхода функции в большинстве библио- тек внешних функций, потому что они следуют соглашению InterBase о передаче аргументов по ссылке или по значению. Большинство доступных библиотек UDF используют это соглашение InterBase. Сервер Firebird способен передавать аргументы в UDF по дескриптору. Это меха- низм, который стандартизует аргументы типов данных Firebird, делая возможным передачу null в качестве аргумента кода включающего языка, хотя и не предоставляя возможности получать null в качестве возвращаемого значения. Функции в библио- теке fbudf (в каталоге /UDF инсталляции сервера) используют дескрипторы. Установка значения в NULL Элемент данных может быть сделан null только в столбце, для которого не указано ограничение not тъъ Сем. разд. "Ограничение NOT NULL" главы 16). В операторе update символом назначения является UPDATE FOO SET COL3 = NULL WHERE COL2 = 4; В операторе insert передавайте ключевое слово null на месте значения: INSERT INTO FOO (COL1, COL2, COL3) VALUES (1, 2, NULL); ПРИМЕЧАНИЕ. В этом случае null перекрывает любое значение по умолчанию для столбца. Другой способ помещения null с помощью оператора insert — опустить имя столб- ца, допускающего пустое значение, во входном списке. Например, следующий опе- ратор будет иметь тот же эффект, что и предыдущий, если значение по умолчанию не определено для столбца соьз: INSERT INTO FOO (COLl, COL2) VALUES (1, 2); В PSQL (языке хранимых процедур) используйте символ "=" в качестве оператора присваивания при назначении переменной null, используйте is [not] null в преди- кате проверки if: DECLARE VARIABLE foobar integer; IF (COLl IS NOT NULL) THEN FOOBAR = NULL;
440 Часть I/. Firebird SQL Использование выражений Вычисляемые столбцы Полезной возможностью SQL является способность генерировать во время выполне- ния выходные поля с использованием выражений. Firebird поддерживает два вида вычисляемого вывода: поля, создаваемые в операторах DML, и столбцы, которые с помощью DDL были определены в таблице с использованием ключевых слов comuted by как контекстные выражения. Обычно такие поля получаются из хранимых дан- ных, хотя и не обязательно. Они могут быть константами или, в общем виде, контек- стными переменными или значениями, получаемыми из контекстных переменных. Поля, создаваемые в операторах Когда выходной столбец создается с использованием выражения, он называется вы- числяемым выходным столбцом. Выходное значение всегда используется только для чтения, потому что оно не является хранимым значением. Любое выражение, которое выполняет сравнение, вычисление или преобразование и возвращает в качестве ре- зультата единственное значение, может включать в себя и такие столбцы. Такое часто происходит, когда выражение использует более одной "формулы" для получения результата. В одном из наших ранних примеров функция (extracto) и операция (конкатенация) были использованы для получения строки birthday, которая была выходной в списке. Такие сложные выражения вовсе не являются редкими. Алиасы получаемых полей и столбцов Выражения, вывод подзапросов и константы могут быть использованы в Firebird для передачи получаемых, или "искусственных" полей, в выходные наборы. Для предос- тавления возможности назначать имена во время выполнения Firebird поддерживает стандарт SQL назначения алиасов столбцам, что позволяет выводить любые столбцы с алиасами из одного или более символов. Например, следующий оператор SELECT COL_ID, COLA|I','I|COLB AS comput col FROM TABLEA; возвращает столбец с именем comput_coi, являющийся конкатенацией значений двух столбцов, разделенных запятыми. ВНИМАНИЕ! Когда два столбца соединяются подобным образом в любом выра- / ! \ жении, выходное поле будет null, если любой из столбцов является null. Это "*-+• также является стандартным поведением SQL. В следующем примере используется скалярный подзапрос к другой таблице для соз- дания выходного поля времени выполнения:
Гпава 21. Выражения и предикаты 441 SELECT COL_ID, COLA, COLB, (SELECT SOMECOL FROM TABLEB WHERE UNIQUE_ID = '99') AS B_SOMECOL FROM TABLEA Скалярный (подзапрос — это тот, который возвращает значение одного столбца из одной строки. Подзапросы обсуждаются в главе 22. Firebird позволяет стандарту слегка расслабиться в отношении ключевого слова as — оно необязательно. Опускать его не рекомендуется, потому что это может усложнить поиск алиаса столбца в исходном коде. Другая часть стандарта требует, чтобы вычисляемые столбцы или получаемые из оператора подзапроса были явно поименованы с помощью алиаса. Текущая версия сервера Firebird позволяет вам опускать имя алиаса для вычисляемых столбцов или столбцов, полученных из подзапроса. Для примера следующий запрос SELECT CAST(CURRENT_DATE as VARCHAR(10)) I ! '-' f I REGISTRATION FROM AIRCRAFT; генерирует такой вывод: <пробельная титульная строка> 2003-01-01-GORILLA 2004-02-28-KANGAROO Одни рассматривают это как ошибку, другие — как особенность. Для администрато- ра базы данных удобнее быстро создать запрос, не беспокоясь о деталях. Это строго не рекомендуется использовать как "особенность" в приложениях. Выходные столб- цы без имени слишком часто приводят к ошибкам и двусмысленностям в клиентских интерфейсах. Те же самые сложности могут появиться в интерфейсах приложений, если вы используете один и тот же алиас более одного раза в одном выходе. Константы и переменные в качестве вывода времени выполнения Существует возможность "создавать" выходной столбец в операторе select, исполь- зуя выражение, в которое включены не столбцы, а константы или контекстные пере- менные и алиасы. В следующем тривиальном примере запрос добавляет к каждой строке столбец, содержащий константное значение 'This is just a demo': SELECT LAST_NAME, FIRST_NAME, 'This is just a demo' AS DEMO_STRING FROM MEMBERSHIP; Тривиальность, как она проявляется в этом примере, может быть удобным способом настройки вывода, особенно при использовании в выражении case.
442 Часть V. Firebird SQL Использование контекстных переменных Firebird имеет набор переменных, предоставляющих мгновенный снимок значений сервера. Как вы уже видели, многие из этих значений могут быть использованы в выражениях, выполняющих вычисления. Они также могут быть использованы в вы- ражениях, сохраняющих их в столбцах базы данных, или в спецификациях computed by (см. разд. "Объявление столбцов COMPUTED BY"). Контекстные переменные также могут быть использованы в выражениях выходного столбца для передачи серверного значения непосредственно клиенту или модулю PSQL. Например: SELECT LOG_ID, LOG_DATE, CURRENTJ3ATE AS BASE_DATE, CURRENT_TRANSACTION AS BASE_TRANSACTION, FROM LOG WHERE . . . Подробный список доступных переменных и примеры их использования см. в разд. "Контекстные переменные" главы 8. Выражения, использующие CASE() и дружественные функции Firebird предоставляет из стандарта SQL-99 синтаксис выражения case и двух его "сокращенных" производных функций — coalesce:) и nullifO. Выражение cased может быть использовано для вывода константного значения, условно определенного во время выполнения в соответствии со значением указанного столбца в текущей строке запрашиваемой таблицы. CASE0 Функция cased позволяет сделать вывод для столбца зависимым от результата вы- числения группы взаимоисключающих условий. Доступность DSQL, PSQL, ISQL, ESQL, Firebird 1.5 и выше. Любая платформа. Синтаксис CASE (Оначение 1> | <пустое-предложение>} WHEN ({NULL | <значение 2>} | <предикат-поиска> ) THEN {<результат 1> I NULL } WHEN...THEN {<результат 2> | NULL} [WHEN...THEN {<результат n> ] NULL}] [ELSE {<результат (n + 1)> I NULL} ] END [, ]
Гпава 21. Выражения и предикаты 443 Дополнительные ключевые слова when. . .then являются ключевыми словами в каждом предложении условие/результат. Требуется, по меньшей мере, одно предложение условие/результат. else предшествует необязательному "последнему" результату, который возвращает- ся, если не выполняется ни одно предыдущее предложение. Аргументы значение 1 является идентификатором столбца, значение которого вычисляется. Мо- жет быть опущен. В этом случае каждое предложение when должно быть предикатом поиска, содержащим идентификатор этого столбца. значение 2 является частью соответствия для условия поиска: простая константа или выражение, которое преобразуется в тип данных, совместимый с типом данных столбца. ♦ Если идентификатор столбца (значение 1) указан в предложении case, то значе- ние 2 остается одиночным в каждом предложении when и будет сравниваться со значением 1. ♦ Если идентификатор столбца не указан в предложении case, то и значение 1, и значение 2 включаются в каждое предложение when в качестве предиката поиска в форме (значение 1 = значение 2). результат 1 — тот результат, который будет возвращен в случае, когда значение 1 будет соответствовать значению 2. результат 2— ТОТ результат, Который будет возвращен В случае, когда значение 1 будет соответствовать значению з и т. д. Возвращаемое значение Предложение case возвращает единственное значение. Если не выполняется ни одно условие и не указано предложение else, то возвращаемый результат будет null. Замечания Вы должны использовать одну форму синтаксиса или другую. Смешанный синтаксис недопустим. Использование единственного предложения условие/результат имеет смысл, только если присутствует предложение else. Хотя это менее элегантно, чем использование соответствующих функций coalesce () и nullif (). Примеры Следующие два примера демонстрируют, как могут быть использованы каждый из двух вариантов синтаксиса для оперирования с одним и тем же набором предикатов. Простой синтаксис: SELECT о.ID, о.Description, CASE о.Status
444 Часть V. Firebird SQL WHEN 1 THEN 'confirmed' WHEN 2 THEN 'in production' WHEN 3 THEN 'ready' WHEN 4 THEN 'shipped' ELSE END 'unknown status ''' |I o.Status I| '''' FROM Orders o; Этот синтаксис использует предикаты поиска в предложении when: SELECT о.ID, о.Description, CASE WHEN (0.Status IS NULL) THEN 'new' WHEN (o.Status = 1) THEN 'confirmed' WHEN (o.Status = 3) THEN 'in production' WHEN (o.Status = 4) THEN 'ready' WHEN (o.Status = 5) THEN 'shipped' ELSE END 'unknown status ''' || o.Status || '''' FROM Orders о; COALESCED Функция coalesce о позволяет вычислять значение столбца с использованием серии выражений, из которых первое выражение, возвращающее непустое значение, вернет выходное значение. Доступность DSQL, PSQL, ISQL, ESQL, Firebird 1.5 и выше. Любая платформа. Синтаксис COALESCE (значение 1> { , значение 2 [, ... значение п]}) Аргументы значение 1 — значение столбца или выражение, которое будет вычисляться. Если это не null, то оно будет возвращаемым значением. значение 2— значение столбца или выражение, которое будет вычисляться, если зна- чение 1 будет NULL. значение п— последнее значение или выражение, которое будет вычисляться, если предыдущие значения в списке будут null. Возвращаемое значение Возвращает первое не null значение из списка. Замечания Функция coalesce о может быть использована для вычисления пары условий или списка из трех или более условий.
Гпава 21. Выражения и предикаты 445 В первом варианте (простого, двоичного) синтаксиса coalesce (значение!, значение2) логика вычисления эквивалентна: CASE WHEN VI IS NOT NULL THEN VI ELSE V2 END Если присутствуют три ИЛИ более аргумента— COALESCE (значение!, значение2, ... значениеп), — логика вычисления эквивалентна следующей: CASE WHEN VI IS NOT NULL THEN Vl ELSE COALESCE (V2,..., Vn) END Последнее значение в списке должно быть задано, чтобы быть уверенным, что что- то будет возвращено. Пример В первом запросе если соединение не находит соответствия в таблице employee для team_leader из таблицы project, то запрос вернет строку ' [Not assigned]' (Не назна- чено) вместо null, которое в противном случае должно было вернуть внешнее соеди- нение в качестве значения full name: select PROJ__NAME AS Projectname, COALESCE(e.FULL_NAME,'[Not assigned]') AS Employeename FROM PROJECT p LEFT JOIN EMPLOYEE e ON (e.EMP__NO = p. TEAM_LEADER) ; В следующем запросе вычисление начинается с самой левой позиции в списке. Если присутствует значение phone, запрос проверяет, присутствует ли значение mobilephone, иначе он возвращает строку 'unknown' (Неопределенный): SELECT COALESCE(Phone, MobilePhone, 'Unknown’) AS Phonenumber FROM Relations Связанные или похожие функции Пользователи Firebird 1.0.x, смотрите внешние функции invlq и snvl() . NULLIF0 nullifo возвращает null в случае соответствия двух непустых значений, иначе он возвращает значение подвыражения. Доступность DSQL, PSQL, ISQL, ESQL, Firebird 1.5 и выше. Любая платформа. Синтаксис NULLIF (значение 1, значение 2)
446 Часть V. Firebird SQL Аргументы значение 1 — столбец или вычисляемое выражение. значение 2— константа или выражение, с которым сравнивается значение 1. Если они соответствуют, nullif возвращает null. Возвращаемое значение Возвращаемое значение будет null, если значение 1 и значение 2 соответствуют друг другу. ЕСЛИ нет соответствия, возвращается значение 1. Замечания nullifо является сокращением для следующего выражения сазе: CASE WHEN (value_l = value_2) THEN NULL ELSE value_l END Пример Этот оператор устанавливает значение столбца stock из таблицы products в ull для всех строк, где его текущее значение равно нулю: UPDATE PRODUCTS SET STOCK = NULLIF(STOCK, 0) Связанные или похожие функции Пользователи Firebird 1.0.x, смотрите inullifo и snullifo. Определение столбцов COMPUTED BY В спецификации таблицы вы можете создать столбцы, называемые вычисляемыми, которые хранят не "жесткие" значения, а выражение, вычисляющее значение, когда к столбцу обращается запрос. Выражение, определяемое для столбца, обычно включа- ет значения одного или более других столбцов текущей строки или контекстные пе- ременные сервера. Вот простая иллюстрация: ALTER TABLE MEMBERSHIP ADD FULL_NAME COMPUTED BY FIRST_NAME i I ' ' | I LAST_NAME; Также возможно использование выражения подзапроса для получения значения та- кого столбца — эту возможность следует использовать осторожно, исключив неже- лательные зависимости. Информацию о вычисляемых столбцах см. в главе 16. Условия поиска Возможность конструировать "формулы" для задания условий поиска при выборе наборов, локализации строк при изменениях и удалениях, а также применение правил для создания входных данных является фундаментальной характеристикой языка запросов. Выражения заполняют весь SQL, потому что они обеспечивают алгебру для контекста добавления данных в хранимые абстрактные данные и представления их в виде информации. Выражения также играют важную роль в контексте разбора входных данных.
Гпава 21. Выражения и предикаты 447 Предложения WHERE Предложение where в операторе задает условия для выбора строк выходного набора или определения строк, на которые воздействуют операции (update, delete). Почти все столбцы таблицы могут быть использованы в выражении поиска в предложении where. Если с элементом данных можно оперировать в SQL, то его можно проверять. Вспомните, что предикат поиска является утверждением. Простые или сложные ут- верждения являются формулами. Мы конструируем формулы для задания условий. Условия должны быть истинными для каждой строки в наборе, с которой оперирует главное предложение нашего запроса. Инверсия операндов Упрощенный синтаксис предикатов в предложении where имеет вид: WHERE значение оператор значение Другими словами, в соответствии с синтаксисом оба следующих утверждения явля- ются правильными: WHERE ACOL = значение И WHERE значение = ACOL Для предикатов, включающих символы операторов эквивалентности (= и о), а также некоторые другие (например, like), синтаксический анализатор SQL понимает оба способа и трактует их как эквивалентные. Другие операторы вызовут исключение или неопределенную ошибку при перестановке левой и правой части предиката. В случае "реверсивных" типов порядок размещения операндов в предикате является вопросом стиля. При этом по опыту автора можно сказать, что читаемость сложных предикатов SQL в исходных кодах клиента и PSQL является обратно пропорцио- нальной количеству предикатов, представленных с проверяемым значением, разме- щенных справа от оператора, как во втором примере. Например, множество предикатов в форме <выражение-или-константа> = COLUMNX делают работу по исправлению ошибок и пересмотру кода каторжным трудом по сравнению с COLUMNX = <вьражение~или-константа> Важно принимать дополнительные "усилия", чтобы сделать все выражения совмес- тимыми. Массивы, BLOB и строки Массивы вовсе не могут использоваться в предикатах поиска, поскольку SQL не име- ет средств доступа к хранящимся в массивах данным. Выражения для столбцов blob являются весьма ограниченными, blob не может сравниваться на равенство ни с дру- гим blob, ни с любым другим типом данных. Текстовый blob может использоваться в
448 Часть V. Firebird SQL starting with и с некоторыми ограничениями в предикатах like и containing. Некото- рые внешние функции могут работать с типами blob — см. функции blob в приложе- нии 1. Строки могут проверяться любыми операторами сравнения, хотя такие операторы, как <, >, >= и <=, не часто являются полезными. Условия упорядочения и группирования Когда выходное поле создается в выражении, оно может быть использовано для ус- тановления условий упорядочения или группирования набора. При этом синтаксис правил для выражений в order by и group by различается, также существуют разли- чия между версиями 1 ,0.x и 1.5. Выражения для ORDER BY На поле, созданное во время выполнения с использованием выражения, не может быть ссылок как на условие в предложении order by по его алиасу. Оно может быть использовано с помощью ссылки на его номер в наборе — то есть на его позицию в строке, считая единицей позицию самого левого выходного поля. Например, сле- дующее SELECT MEMBER-ID, LAST_NAME | j ’, ' H FIRST_NAME AS FULL-NAME, JOIN_DATE FROM MEMBERSHIP ORDER BY 2; создает список, упорядоченный по полю, полученному в результате конкатенации. В версии 1.5 и более поздних в наборах, отличных от union, условие упорядочения может быть выражением, возвращающим значение, которое может оказаться осмыс- ленным для упорядочения. Например, предложение order by в следующем операторе вызывает функцию, которая возвращает длину столбца описания и использует ее для упорядочения выходного набора от большего к меньшему: SELECT DOCUMENT—ID, TITLE, DESCRIPTION FROM DOCUMENT ‘ ORDER BY STRLEN(DESCRIPTION) DESC; Выражения для GROUP BY В SQL оператор group by используется для собирания наборов данных и иерархиче- ской их организации или суммирования. Во всех версиях Firebird вы можете выполнять иерархическое группирование, осно- ванное на вызове внешней функции (UDF), однако в версии 1.0.x вы не можете ис- пользовать порядковый номер выходного столбца в качестве условия группирования. Firebird 1.5 добавляет возможность использования в group by порядкового номера, а также несколько других типов выражений, включая выражения подзапросов.
Глава 21. Выражения и предикаты 449 Другие изменения в Firebird 1.5 ужесточают отдельные правила группирования, уби- рая поддержку некоторых неправильных вариантов синтаксиса группирования, кото- рые были допустимы в Firebird 1.0.x. Правила и взаимодействия выражений для группирования и упорядочения иногда являются сложными в реализации. Эти темы подробно обсуждаются в главе 23. Следующий пример для версии 1.5 запрашивает в таблице membership и выводит ста- тистику, показывающую количество участников, объединенных в каждом месяце: SELECT MEMBER_TYPE, EXTRACT(MONTH FROM JOINJDATE) AS MONTH_NUMBER, /* 1, 2, и т.д. */ F_CMONTHLONG(JOIN_DATE) AS SMONTH, /* UDF, возвращающая месяц в виде строки */ COUNT (*) AS MEMBERS—JOINED FROM MEMBERSHIP GROUP BY MEMBER—TYPE, EXTRACT(MONTH FROM JOIN_DATE); Выражения CHECK в DDL Применение выражений не ограничивается только DML; мы уже видели их исполь- зование в определении таблицы для описания вычисляемых столбцов. Каждый раз, когда вы определяете ограничение check для таблицы, столбца или домена, вы при- меняете выражения. По своей природе ограничение check выполняет проверку на од- но или более значений — тестирует предикат. Вот пример, где проверяется номер сотового телефона участника, который должен начинаться с нуля, если номер вообще присутствует: ALTER TABLE MEMBERSHIP ADD CONSTRAINT CHECK_CELLPHONE __NO CHECK (CELLPHONE_NO IS NULL OR CELLPHONE_NO STARTING WITH 'O’); Выражения в PSQL Процедурный язык для триггеров и хранимых процедур PSQL широко использует выражения для управления потоком выполнения. PSQL предоставляет структуры IF(<предикат>') THEN И WHILE(,<предикат>') DO. Любой предикат, КОТОрЫЙ МОЖвТ быть использован в условии поиска, также может быть предикатом для условия управле- ния потоком в программе. Важной функцией триггеров является проверка с использованием выражений посту- пающих новых значений, а также использование других выражений для преобразо- вания или создания значений для текущей строки или для строк в связанных табли- цах. Например, следующий общий триггер проверяет, является ли значение null, и если да, то вызывает функцию для присваивания ему значения: 15 Зак. 420
450 Часть V. Firebird SQL CREATE TRIGGER BI_MEMBERSHIP FOR MEMBERSHIP ACTIVE BEFORE INSERT POSITION 0 AS BEGIN IF (NEW.MEMBER_ID IS NULL) THEN NEW.MEMBER_ID = GEN_ID(GENJHEMBER_ID, 1); END Подробную информацию о написании триггеров и хранимых процедур см. в части VII. Вызовы функций После установки Firebird содержит минимальный набор внутренних функций SQL. Хотя новые функции появляются время от времени, тем не менее сохраняется одно из основных достоинств Firebird: малый объем памяти, занимаемый сервером. Функциональные возможности сервера могут быть легко расширены за счет его воз- можности получать доступ к функциям из внешних библиотек. Традиционно такие функции называются функциями, определенными пользователями (User-Defined Functions, UDF). Более корректно называть их внешними библиотеками функций. В реальности большинство администраторов баз данных используют хорошо протес- тированные библиотеки, находящиеся в общем пользовании и свободно распростра- няемые. Внутренние функции SQL В табл. 21.7 представлены внутренние функции SQL, доступные в Firebird. Таблица 21.7. Внутренне реализованные функции SQL Функция Тип Назначение CAST () Преобразование Преобразует столбец из одного типа данных в другой EXTRACT () Преобразование Выделяет части даты и времени (год, месяц, день и т. д.) из значений date, time и timestamp SUBSTRING() Строка Отыскивает последовательность символов в строке UPPER() Строка Преобразует символы в строке в верхний регистр GEN_ID() Генерация Возвращает значение генератора AVGO Агрегат Вычисляет среднее значение набора значений COUNT() Агрегат Возвращает количество строк, которые удовлетворяют условию поиска запроса MAX() Агрегат Отыскивает максимальное значение в наборе значений MIN() Агрегат Отыскивает минимальное значение в наборе значений SUMO Агрегат Суммирует значения в наборе числовых значений
Глава 21. Выражения и предикаты 451 Функции преобразования Функции преобразования трансформируют типы данных, например, путем преобра- зования их из одного типа данных в другой совместимый тип, изменения масштаба или точности числовых значений или убирая какой-нибудь наследуемый атрибут из элемента данных. Можно сказать, что многие строковые функции также являются функциями преобразования, потому что они преобразуют способ хранения строко- вых значений, представленных в выходном наборе. CAST() Функция cast о широко используется. Она позволяет преобразовывать элемент дан- ных одного типа данных в другой тип или трактовать его как другой тип данных. Доступность DSQL, PSQL, ISQL, ESQL, Firebird 1.5 и выше. Любая платформа. Синтаксис CAST(значение AS <тия-данных>) Дополнительные ключевые слова Фраза as <тип-данных> с аргументом является обязательной. Аргументы значение является столбцом или выражением, которое преобразуется к типу данных, допустимому для преобразования в тип данных, названный в ключевом слове as. <тип-данных> должен быть родным типом данных Firebird. Нельзя указывать имя домена. Рис. 8.1 показывает все допустимые преобразования из одного типа в другой. Возвращаемое значение Функция возвращает вычисляемое поле заданного типа данных. Пример В следующем фрагменте PSQL поле log_date типа данных timestamp преобразуется в тип данных date, потому что для вычисления нужно получить целые дни: IF (CURRENT_DATE - CAST(LOG_DATE AS DATE) = 30) THEN STATUS = '30 DAYS'; Следующий оператор выбирает значение из столбца типа integer, преобразует его в строку и соединяет со столбцом типа char<3) для формирования значения другого столбца: UPDATE MEMBERSHIP SET MEMBER_CODE = MEMBER_GROUP || CAST (MEMBER_ID AS CHAR(8)) WHERE MEMBER_CODE IS NULL; Связанные материалы См. главу 8, где более подробно осуждается функция casto, а также последующие главы, в которых индивидуально рассматриваются типы данных.
452 Часть V. Firebird SQL EXTRACTO Эта функция выделяет часть полей типа данных date, time и timestamp в виде числа. Доступность DSQL, PSQL, ISQL, ESQL, Firebird 1.5 и выше. Любая платформа. Синтаксис EXTRACT(часть FROM поле) Необязательные ключевые слова YEAR | MONTH I DAY I HOUR I MINUTE I SECOND | WEEKDAY | YEARDAY Аргументы часть является одним значением из указанного списка необязательных ключевых слов, weekday выделяет день недели (воскресенье = 1, понедельник = 2 и т. д.), yearday выделяет день в году (от 1 января = 1 до 366). поле— поле типа данных date, time или timestamp (столбец, переменная или выра- жение). Возвращаемое значение Все части возвращают smallint за исключением second, которое возвращает DECIMAL(6,4). Замечания extract будет работать только со значениями, которые преобразуются к полям да- та/время. Пример Следующий оператор возвращает имена и дни рождения, упорядоченные по birthday, для всех участников, имеющих день рождения в текущем месяце: SELECT FIRST_NAME, LAST_NAME, EXTRACT (DAY FROM DATE—OF_BIRTH) AS BIRTHDAY FROM MEMBERSHIP WHERE DATE—OF_BIRTH IS NOT NULL AND EXTRACT (MONTH FROM DATE_OF_BIRTH) = EXTRACT (MONTH FROM CURRENT_DATE) ORDER BY 3; Строковые функции Firebird имеет только две внутренние строковые функции. Большое количество стро- ковых функций доступно во внешних функциях (см. следующий раздел этой главы).
Глава 21. Выражения и предикаты 453 SUBSTRINGO substring о является внутренней функцией, реализующей функцию ANSI SQL substringo. Она возвращает поток, состоящий из байта с номером начальная-позиция и всех последующих байтов до конца строки значение. Если указано необязательное предложение for длина, она вернет меньшее из длина байт и количество байт до конца входного потока. Доступность DSQL, PSQL, ISQL, ESQL, Firebird 1.5 и выше. Любая платформа. Синтаксис SUBSTRING(значение FROM начальная-позиция [FOR длина]) Необязательные ключевые слова Необязательное предложение for задает длину возвращаемой подстроки. Аргументы значение может быть любым выражением, константой или идентификатором столбца, который преобразуется в строку. начальная-позиция должна преобразовываться в целое >= 1. Не может быть заменяе- мым параметром. длина должна преобразовываться в целое >= 1. Не может быть заменяемым пара- метром. Возвращаемое значение Возвращаемое значение является строкой. Замечания Значения начальная-позиция и длина являются позициями байтов, что имеет значение для многобайтовых наборов символов. Для строкового аргумента функция будет обрабатывать любой набор символов. Вы- зывающий оператор ответственен за обработку всех проблем, связанных с многобай- товыми наборами символов. Для аргументов столбцов blob указанный столбец должен быть двоичным blob (sub_type о) или текстовым BLOB (sub_type 1) с набором символов один байт на сим- вол. В настоящий момент функция не обрабатывает текстовые blob с наборами сим- волов Chinese (максимум два байта на символ) и Unicode (максимум три байта на символ). Пример Следующий оператор будет изменять значение столбца columnb, присваивая ему строку до 99 символов, начиная с четвертой позиции оригинальной строки: UPDATE ATABLE SET COLUMNB = SUBSTRING(COLUMNB FROM 4 FOR 99) WHERE ...
454 Часть I/. Firebird SQL Связанные или похожие функции См. также внешние функции substro, substrleno и rtrimo. UPPERO Преобразует все символы строки в верхний регистр. Доступность DSQL, PSQL, ISQL, ESQL; обеспечивает работу со строками в тех наборах символов и последовательностях сортировки, которые поддерживают преобразование ниж- ний/верхний регистры. Любая платформа. Синтаксис UPPER (значение') Аргументы значение является столбцом, переменной или выражением, которое преобразуется в строковый тип. Возвращаемое значение Если набор символов и последовательность сортировки поддерживают преобразова- ние в верхний регистр, функция возвращает строку, в которой все символы преобра- зованы в верхний регистр. Строка имеет ту же длину, что и входное значение. Для наборов символов, не поддерживающих преобразование в верхний регистр, функция возвращает неизмененное входное значение.5 Замечания Входное значение не может иметь тип данных blob. Пример Следующее ограничение check проверяет входную строку, выясняя, содержит ли она все символы в верхнем регистре: ALTER TABLE MEMBERSHIP ADD CONSTRAINT CHECK_LOCALITYJCASE CHECK(LOCALITY = UPPER(LOCALITY)) ; Связанные или похожие функции См. также внешние функции lower о и f_propercase (). Функция для получения значения генератора Функция gen ido является механизмом, с помощью которого модули PSQL и при- ложения получают числа от генераторов. Генераторы подробно обсуждаются в гла- ве 9. См. также разд. "Реализация автоинкрементных ключей" главы 31. 5 До Firebird 1.5 функция upper работала только для столбцов collate pxw_CYRL (или при явном указании для конкретного столбца). В Firebird 1.5 таблица перекодировки символов в верхний регистр есть и в "умолчательном" collate WIN1251. — Прим. науч. ред.
Глава 21. Выражения и предикаты 455 GENJDO gen_ido вычисляет и возвращает значение генератора. Доступность DSQL, PSQL, ISQL, ESQL, Firebird 1.5 и выше. Любая платформа. Синтаксис GEN_ID(значение!, значение2) Аргументы значение! является идентификатором существующего генератора. значениег является значением шага — целый тип или выражение, которое преобразу- ется в целый тип. Возвращаемое значение Возвращается значение типа bigint. Замечания Обычно значение шага— единица. Значение шага 0 вернет последнее значение гене- ратора. Возможно большее значение шага, так же как и отрицательное значение. При этом вам не следует использовать отрицательные значения, если только вы действи- тельно не хотите получить последующее значение. gen_ido всегда выполняется вне контекста какой-либо транзакции. Эта операция в Firebird доступна только пользователю. Как только число будет получено от генера- тора, оно никогда не будет создано тем же генератором, за исключением случаев, когда пользователь изменит последовательность, используя отрицательную величину шага, или с помощью оператора set generator. Пример Следующий оператор возвращает новое значение генератора gen_serial: SELECT GEN_ID (GEN_SERIAL, 1) FROM RDB$DATABASE; В приведенном далее примере генератор используется в триггере before insert для получения значения первичного ключа: CREATE TRIGGER BI-AUTHORS for authors ACTIVE BEFORE INSERT POSITION 0 AS BEGIN IF (NEW.AUTHOR_ID IS NULL) THEN NEW. AUTHOR__ID = GEN_ID (GEN__AUTHOR_ID, 1); END ~ Агрегатные функции Агрегатные (обобщенные) функции выполняют вычисления над значениями столбца, например, значениями, выбранными из числового столбца запрашиваемого набора.
456 Часть V. Firebird SQL Firebird имеет группу агрегатных функций, которые чаще всего используются в ком- бинации с условиями группирования для вычисления итогов или статистики на уровне группы. Агрегатными функциями являются: зимо, вычисляющая итог, мах о и mino, возвращающие наибольшее и наименьшее значение соответственно, и avgo, вычис- ляющая среднее значение. Функция count () также ведет себя как агрегатная функция в сгруппированных запросах. В главе 23 более подробно рассматривается участие агрегатных функций в сгруппи- рованных запросах. "Негруппированные" объединения В небольшом количестве случаев агрегатные функции могут оперировать с набора- ми, которые не являются субъектами предложения group by и возвращающими не более одной строки. Логически результат подобного запроса не может выводить зна- чения любого столбца базы данных или значения, полученные от неагрегатных функций. Для иллюстрации рассмотрим следующий запрос, который объединяет бюджеты одного проекта за один фискальный год. Таблица имеет одну запись бюд- жета для каждого из пяти отделов: SELECT 'MKTPR-1994' AS PROJECT, SUM(PROJECTED__BUDGET) AS TOTAL_BUDGET FROM PROJ_DEPT__BUDGET WHERE PROJ_ID = 'MKTPR' AND FISCAL__YEAR = 1994; Выходом будет одна строка: строковое поле времени выполнения и вычисленный итог. Внешние функции (UDF) Внешние функции являются вспомогательными программами, написанными на язы- ке программирования, таком как С, C++ или Pascal, и скомпилированными как со- вместно используемые двоичные библиотеки— DLL в Windows или совместно ис- пользуемые объекты для других платформ, которые поддерживают динамическую загрузку. Как и стандартные встроенные функции SQL, внешние функции могут быть разработаны для выполнения преобразований или вычислений, реализация ко- торых либо слишком сложна, либо вовсе невозможна средствами языка SQL. Вы можете использовать внешние функции, используя их в выражениях, так же, как вы используете встроенные функции SQL. Как и внутренние функции, они могут возвращать значения для переменных или выражений SQL в хранимых процедурах и триггерах. Фрагмент "определенные пользователем" в названии функций означает, что вы мо- жете писать свои собственные функции. Возможности создания пользовательских функций для вашего сервера Firebird ограничиваются лишь вашей изобретатель- ностью и умением программировать на включающем языке. Допустимо включение статистических, строковых, математических функций, функций даты, подпрограмм форматирования данных и даже пользовательского процессора для выполнения регу- лярных выражений (regular expression, regexes).
Гпава 21. Выражения и предикаты 457 Внешние функции не должны иметь собственного соединения с базой данных. Как и внутренние функции, они должны оперировать с данными в выражениях в контексте текущего серверного процесса, транзакции и одного оператора. Поскольку внешние функции могут получать аргументы только типов данных, родных для Firebird (или которые могут быть преобразованы к таким типам данных), они не могут получать в качестве аргумента набор запроса. Следовательно, невозможно, например, написать внешнюю агрегатную функцию, которая бы оперировала не с константными аргу- ментами. Существующие библиотеки Firebird поставляется с двумя предварительно встроенными библиотеками внешних функций. Библиотеки совместно используемых объектов размещаются по умолча- нию в каталоге /UDF корневого каталога Firebird. Расширения файлов: ♦ dll — для Windows; ♦ so — для других поддерживаемых платформ. Библиотека ib udf— библиотека полезных базовых функций— наследуется Firebird от своего предшественника InterBase. Она нужна для вызова некоторых утилит управления памятью, которые находятся в совместно используемом объекте с име- нем ib utii, размещенном в каталоге /bin. Эта библиотека передает параметры по значению или по ссылке в согласованном стиле InterBase. В некоторых подпрограм- мах исправлены ошибки для Firebird, так что не используйте версии, поставляемые с продуктами Borland. Библиотеку fbudf создал Клавдио Балдеррама (Claudio Valderrama). Она передает параметры через дескрипторы Firebird, что является наиболее надежным способом гарантировать отсутствие внутренних ошибок в результате выделения памяти и пре- образования типов. Свободно доступными также являются некоторые библиотеки UDF, включая биб- лиотеку FreeUDFLib, вначале написанную Грегори Диц (Gregory Deatz) для Borland Delphi. Библиотека FreeUDFLib содержит большое количество функций для строк, математических операций, типов данных blob и даты. Она поддерживалась, коррек- тировалась и дополнялась разными людьми в течение многих лет. Будьте вниматель- ны при получении версии этой библиотеки, которая правильно работает в диалекте 3 с типами данных даты и времени. Такая версия для Windows доступна на http://www.ibase.ru. Исходный код на С для версии POSIX доступен как FreeUDFLibC, однако на момент написания этой главы не было надежного бинарного кода, доступного для загрузки. Конфигурация и вопросы безопасности Коды внешних модулей являются по своей сути уязвимыми для злоумышленников и небрежных администраторов. Как-никак это просто файлы файловой системы. В Firebird 1.0.x вы можете— и должны— явно сконфигурировать положение биб- лиотек внешних функций на сервере, используя параметр externai_function_directory
458 Часть I/. Firebird SQL в файле конфигурации (isc config для серверов POSIX и ibconfig для Windows). Это следует сделать администратору базы данных для обеспечения того, что библиотеки не будут перезаписаны — случайно или неавторизованными посетителями. В Firebird 1.5 и выше доступ к внешним файлам разного вида может быть ограничен на различных уровнях доступа. По умолчанию, если вы решите разместить библио- теки внешних функций в каталогах не по умолчанию, они будут недоступны для сер- вера. Просмотрите замечания в главе 36 относительно конфигурирования параметра uDFAccess в файле firebird.conf для разрешения подобных проблем. Устойчивость Некорректная UDF приведет к аварийному завершению сервера и способна разру- шить данные. Важно проверять ваши подпрограммы для внешних функций домашне- го изготовления досконально на сервере и вне сервера прежде, чем распространять их для работающих баз данных. Объявление функции для базы данных Когда UDF написана, скомпилирована, тщательно протестирована и инсталлиро- вана в соответствующий каталог на сервере, она должна быть объявлена для базы данных, чтобы ее можно было использовать как функцию SQL. Для этого используй- те оператор DDL declare external function. Вы можете объявить функции с исполь- зованием isql, любого другого интерактивного инструмента SQL или скрипта. После того как функция будет объявлена для любой базы данных на сервере, содер- жащая ее библиотека будет загружаться во время выполнения при первом же обра- щении приложения к любой функции, включенной в эту библиотеку. Необходимо объявить каждую функцию, которую вы хотите использовать, для каждой базы дан- ных, которую вы будете использовать. Объявление функции для базы данных информирует эту базу данных относительно: ♦ имени, с которым функция будет использована в операторах SQL. В объявлении вы можете использовать свое собственное имя— см. следующие замечания по синтаксису; ♦ количества аргументов и их типов данных; ♦ типа данных возвращаемого значения; ♦ имени (entry_point), с которым функция существует в библиотеке; ♦ имени библиотеки (module_name), которая содержит функцию; Синтаксис декларации: DECLARE EXTERNAL FUNCTION имя [тип-данных | CSTRING (целое) [, тип-данных | CSTRING (целое) ...]] RETURNS {тип-данных [BY VALUE] | CSTRING (целое)} [FREE_IT] [RETURNS PARAMETER n] ENTRY_POINT 'тонка-входа' MODULE_NAME 'имя -модуля1;
Глава 21. Выражения и предикаты 459 Табл. 21.8 подробно описывает аргументы. Таблица 21.8. Аргументы оператора declare external function Аргумент Описание ИМЯ Имя DDF для использования в операторах SQL. Может отличаться от имени, указанного после ключевого слова entry_point. Если вы используете существующую библиотеку, то хорошо бы изменить предоставляемое имя — хотя бы для того, чтобы устранить путаницу при объявлении той же функции с другими именами в других базах данных тип-даиных Тип данных для входного или возвращаемого параметра. Если аргу- менты не передаются по дескриптору, это тип данных включающего языка. Все входные параметры передаются в UDF по ссылке или по дескриптору. Возвращаемые параметры могут передаваться по ссылке, по значению или по дескриптору. Тип данных не может быть массивом или элементом массива RETURNS Спецификация возвращаемого значения функции BY VALUE Указывает, что возвращаемое значение должно передаваться по значению, а не по ссылке CSTRING( целое) Указывает, что UDF возвращает значение в виде строки с нулевым терминатором с максимальной длиной целое байтов FREE_IT Освобождает память возвращаемого значения после завершения выполнения UDF. Используйте это, только если память в UDF выде- ляется динамически с использованием функции ib_util_malloc, определенной в библиотеке ib_util. Библиотека ib_util должна нахо- диться в каталоге /bin, чтобы вариант free_it мог работать RETURNS PARAMETER n Указывает, что функция возвращает n-й входной параметр. Это тре- буется для возвращения типов данных blob точка-входа Заключенная в апострофы строка, задающая имя, под которым UDF присутствует в исходном коде и сохранена в библиотеке UDF имя-модуля Заключенная в апострофы спецификация файла, идентифицирую- щая библиотеку, которая содержит UDF Скрипты библиотеки внешних функций Большинство библиотек внешних функций поставляется вместе с их собственными скриптами DDL, содержащими объявления для каждой функции и, обычно, некото- рую краткую документацию. Существует соглашение именовать скрипт так же, как и библиотеку, но с использованием расширения файла SQL или sql. Однако не все об- щедоступные библиотеки придерживаются этого соглашения. Скрипты ib udf.sql и fbudf.sql находятся в каталоге /UDF вашего каталога инсталля- ции сервера. Комплект FreeUDFLib содержит скрипт с именем ext funcs.sql. Вы мо- жете свободно копировать и вставлять объявления для компоновки вашей собствен- ной библиотеки из любимых объявлений.
460 Часть I/. Firebird SQL Следующее объявление является примером из скрипта ib_udf.sql: DECLARE EXTERNAL FUNCTION Ipad CSTRING(80), INTEGER, CSTRING(l) RETURNS CSTRING(80) FREE_IT ENTRY_POINT 'IB_UDF_lpad' MODULE_NAME 'ib_udf'; Следующее объявление содержится в fbudf, оно передает аргументы по дескриптору в некоторые функции: DECLARE EXTERNAL FUNCTION sright VARCHAR(IOO) BY DESCRIPTOR, SMALLINT, VARCHAR(100) RETURNS PARAMETER 3 ENTRY__POINT 'right' MODULE_NAME ' fbudf' ; Идентификатор (имя) функции При объявлении внешней функции для базы данных вы не ограничены использова- нием только имен, появляющихся в скрипте. Имя функции должно быть уникальным среди имен всех функций, объявленных в базе данных. Библиотеки общего использо- вания обычно соответствуют соглашению о поставляемых объявлениях, по которому эти имена "не наступают на ноги" идентификаторам, обычно используемым в другой библиотеке, поэтому вы можете увидеть некоторые странности в именах в этих скриптах. Иногда вам может понадобиться объявить одну и ту же функцию для вашей базы данных более одного раза. Пока вы используете различные имена в external function, вы можете объявлять функцию, имеющую те же entry_point и module_name, столько раз, сколько вам нужно. ВНИМАНИЕ! Никогда не изменяйте аргумент entry_point. Аргумент module_name не должен изменяться за исключением случая, когда необходимо использовать полный путь к библиотеке (см. разд. "Пути в объявлениях функций"). Размеры строковых аргументов Скрипты содержат объявления размеров по умолчанию для внешних функций, кото- рые принимают аргументы в виде строковых переменных. По причинам безопасно- сти размеры по умолчанию сохраняются малыми, чтобы избежать риска случайного или злонамеренного переполнения. Если строковые функции получают входные дан- ные или возвращают результаты, которые больше объявленного размера, то вызыва- ется исключение. Если вам нужен аргумент для строковой функции, имеющий размер больший, чем значение по умолчанию, объявите аргумент в соответствии с вашими требованиями, убедитесь, что входные и выходные данные согласованы друг с другом и ни один аргумент не превышает максимального для varchar значения 32 765 байт— заметьте: байтов, а не символов.
Гпава 21. Выражения и предикаты 461 В следующем примере функция, описанная в скрипте fbudf.sql как sright, объявляет- ся с именем srirgt2ooo, а размер параметров изменяется до 200 байт: DECLARE EXTERNAL FUNCTION sright200 VARCHAR(200) BY DESCRIPTOR, SMALLINT, VARCHAR (200) RETURNS PARAMETER 3 ENTRY_POINT 'right' MODULE__NAME ' fbudf' ; Пути в объявлениях функций Для любой платформы в ссылке на модуль может отсутствовать путь или расшире- ние файла. Это желательно, если вы собираетесь перемещать базу данных, содержа- щую объявления функций, на множество операционных систем. Конфигурация по умолчанию для Firebird 1.5 позволяет это сделать. В версии 1,0.Х нужно будет Сконфигурировать параметр external_function_directory в isc config/ibconfig для каждой платформы, на которой эти функции будут исполь- зоваться, поскольку эта версия не содержит "пути по умолчанию" для модулей функ- ций. По умолчанию должен работать каталог /UDF, однако практика показывает, что это ненадежно без явно заданной конфигурации. Для Firebird 1.5 и выше существуют другие режимы конфигурации (UDFAccess) для размещения библиотек функций (см. главу 36), включая restrict, который позволяет задать для них один или более каталогов. Если вы используете restrict, то в объяв- ление нужно включить полный путь к модулю и расширение файла. В этом случае база данных не будет переносима на другую операционную систему, пока вы не уда- лите из базы данных функции и их зависимости. Следующий пример показывает объявление функции с указанием полного пути: DECLARE EXTERNAL FUNCTION Ipad CSTRING(80), INTEGER, CSTRING(l) RETURNS CSTRING(80) FREE__IT ENTRY-POINT ’ IB_UDF_lpad’ MODULE__NAME ' /opt/extlibs/ib_udf .so' ; Список внешних функций Приложение 1 содержит детальное описание с примерами внешних функций из биб- лиотек ib_udf и fbudf из каталога /UDF каталога инсталляции Firebird, а также многие функции из FreeUDFLib. Пора дальше Важность использования логики выражений для трансформации абстрактных данных в осмысленную информацию аналогична важности использования средств SQL по слиянию данных из множества таблиц в один набор связанных данных. В следующей главе мы рассмотрим существующие в DML способы выполнения таких многотаб- личных операций. Эта глава заканчивается специальной темой оптимизации запросов Firebird и использованием планов запросов (предложение plan) в запросах select.
ГЛАВА 22 Запросы к множеству таблиц Динамический SQL Firebird поддерживает три варианта запросов к множеству таб- лиц с помощью одного оператора SQL: соединения (joins), подзапросы (subqueries) и объединения (unions). Результат соединений, столбцов подзапроса и объединений является по своей природе набором данных только для чтения. Firebird не поддерживает многотабличные запросы, которые находятся в нескольких базах данных. Тем не менее возможен запрос к нескольким таблицам одновременно из нескольких баз данных внутри одной транзакции, имеющей полное двухфазное завершение (two-phase commit— 2РС). Клиентские приложения могут сопоставлять данные и выполнять отдельные операторы манипулирования данными в пределах базы данных. Мультибазовые транзакции обсуждаются в части VI. В этой главе мы рассмотрим три метода динамического поиска данных в нескольких таблицах1 в контексте одного оператора и одной базы данных: соединения, подзапро- сы и объединения. Данная глава завершается темой оптимизации, где рассматриваются планы выполне- ния запросов, и как оптимизатор Firebird выбирает и использует индексы для созда- ния этих планов. Понимание того, как оптимизатор "думает", может быть полезным при проектировании хорошо сформированных индексов и создании операторов за- просов. Виды многотабличных запросов Три метода поиска данных во множестве таблиц довольно сильно отличаются друг от друга и, как правило, выполняют различные виды поисковых задач. Поскольку со- единения и подзапросы используют слияние потоков данных из строк различных таб- лиц, их роли частично совпадают при некоторых условиях. Коррелированный подза- прос, который может создавать относительные связи между колонками главной таб- лицы, иногда может содержать спецификации для получения того же результата, что и соединение, без использования самого соединения. С другой стороны, объединения 1 Программная логика в комбинации с техниками DSQL, описываемые в данной главе, мо- гут быть использованы в модулях PSQL, которые возвращают клиентам виртуальные таблицы, а также в других модулях PSQL. См. часть VII. Просмотры, рассматриваемые в главе 24, обес- печивают другие способы получения наборов данных из нескольких таблиц.
Гпава 22. Запросы к множеству таблиц 463 запросов не используют слияние потоков; напротив, они "собирают" строки. Их роль никогда не совпадает с ролью запросов соединения или подзапросов. Соединения, подзапросы и объединения не являются взаимоисключающими, несмот- ря на то, что результат объединения не может быть соединен один с другим или с другими результатами. Спецификации соединения и объединения могут включать подзапросы, и некоторые подзапросы могут содержать соединения. Соединения Соединение является одним из наиболее мощных средств реляционной базы данных по причине его способности поиска абстрактных нормализованных данных в храни- лище и в контексте передачи приложениям ненормализованных наборов данных. В операторах join две или более связанные таблицы комбинируются для связывания соответствующих колонок из каждой таблицы. С помощью этих связей генерируется виртуальная таблица, которая содержит колонки из всех этих таблиц. Операции соединения создают наборы данных только для чтения, для которых не могут применяться операции insert, update или delete. Интерфейс некоторых прило- жений содержит средства, позволяющие сделать поведение соединенных наборов данных таким, как если бы они были изменяемыми. Подзапросы Подзапросом является оператор select, включенный в другой запрос. Внедренный запрос, встроенный запрос, вложенный запрос являются синонимами для подзапро- са. Подзапросы используются с различными условиями для чтения данных из других таблиц в основной (внешний для подзапроса) запрос. Правила составления подзапро- сов изменяются в соответствии с целью подзапроса. Прочитанные данные всегда яв- ляются данными только для чтения. Запросы UNION Запросы объединения дают возможность выбрать строки соответствующих форматов из различных наборов данных в объединенный набор данных, который приложения могут использовать так, как если бы он был одной таблицей только для чтения. Под- множества, найденные в таблицах, не обязательно должны быть связаны друг с дру- гом — просто они должны соответствовать друг другу структурно. Соединения Соединение используется в операторах select для генерации ненормализованных наборов, содержащих столбцы из нескольких таблиц, которые хранят связанные дан- ные. Множества столбцов, выбранных из каждой таблицы, называются потоками. Процесс соединения объединяет выбранные столбцы в единый выходной набор дан-
464 Часть У. Firebird SQL ных. Простейшим случаем является соединение двух таблиц, которые связаны соот- ветствием элементов ключа, встречающихся в обеих таблицах. Для создания связи ключевые столбцы выбираются на основании того, что они реа- лизуют недвусмысленное отношение — т. е. ключ в левой таблице соединения будет отыскивать только те строки из правой таблицы, которые соответствуют отноше- нию, и он будет отыскивать все такие строки. Обычно связь осуществляется между уникальным ключом левой таблицы (первичный или любой другой уникальный ключ) и формальным или подразумеваемым внешним ключом правой таблицы. Тем не менее условия соединения не ограничены столбцами первичного и внешнего ключей; сервер допускает в выходном наборе данных дубликаты строк. Дублирован- ные строки могут стать причиной нежелательного результата. По этому вопросу об- ратитесь к замечаниям в разд. "Темы оптимизации". Внутреннее соединение Следующий оператор соединяет две таблицы, которые связаны через внешний ключ fk правой таблицы (таЬ1е2) и первичный ключ рк таблицы Tablei: SELECT Tablel.PK, Tablel.COLl, ТаЫе2.РКХ, Table2.COLX FROM Tablel INNER JOIN Table2 ON Tablel.PK = Table2.FK WHERE... <условия-поиска> Это спецификация внутреннего соединения. Вскоре мы рассмотрим внешнее соеди- нение. На рис. 22.1 показаны два потока, как они существуют в таблицах, и генери- руемый набор данных. РК COL1 99 100 101 cde 102 def 103 efg 104 fgh 105 106 107 yk 108 M ... 109 Kjm ТаЫе1 РКХ COLX FK 234 * 99 235 101 236 102 237 DO. 104 238 hi < 105 239 г% 106 240 ПС/" 109 241 h’f 111 242 114 243 115 244 116 ТаЫе2 РК COL1PKXCOLX 99 234 101 235 > 102 236 rcc 104 237 105 238 106 239 109 240 Joined set Рис. 22.1. Внутреннее соединение
Гпава 22. Запросы к множеству таблиц 465 Как показывает диаграмма, внутреннее соединение объединяет два потока таким об- разом, что несоответствующие строки в любом из потоков отбрасываются. Другое название внутреннего соединения — исключающее соединение, поскольку его прави- ла означают, что несоответствующие пары обоих потоков исключаются из выходного набора данных. Стандарты SQL описывают два варианта синтаксиса внутреннего соединения. Пре- дыдущий пример использует более современный синтаксис SQL-92, отличающийся от более старого, более ограниченного SQL-89, указывая явное соединение, потому что в примере используется явное предложение join для задания условий соедщ нения. Синтаксис неявного INNER JOIN в SQL-89 В стандарте SQL-89 таблицы, участвующие в соединении, задаются списком с разде- ляющими запятыми в предложении from запроса select.'Условия для связи таблиц задаются среди условий поиска предложения where. Не существует специального синтаксиса для указания, какие условия используются для поиска, а какие — для со- единения. Предполагается, что условия соединения самоочевидны. Обратившись назад, к введению в предложение join, можно назвать старый синтаксис неявным соединением. Синтаксис неявного соединения может осуществлять только внутреннее соедине- ние — реализация SQL, которая не поддерживает предложение join, не может вы- полнять внешнее соединение. Вот предыдущий пример, который переписан как неявное соединение: SELECT Tablel.PK, Tablel.COL1, ТаЫе2.РКХ, Table2.COLX FROM Tablel, Table2 WHERE Tablel.PK = Table2.FK AND <условия-поиска> Неявное соединение поддерживается в Firebird для совместимости с кодом сущест- вующих приложений. Оно не рекомендуется для новых приложений, потому что оно несовместимо с синтаксисом других видов соединений и делает поддержку и само- документируемость довольно неуклюжей. Некоторые программы доступа к данным, включая драйверы, могут не обрабатывать правильно синтаксис SQL-89 по причине проблем при синтаксическом анализе в различении условий соединения и условий поиска. Можно предположить, что в будущем стандарте этот синтаксис будет отсут- ствовать. Синтаксис явного INNER JOIN в SQL-92 Явное внутреннее соединение является предпочтительным для Firebird и других ре- ляционных СУБД, которые его поддерживают. Если оптимизатор способен вычис-
466 Часть I/. Firebird SQL лить план запроса, то неважно, что синтаксис SQL-92 будет лучше или хуже более раннего синтаксиса, потому что интерпретатор DSQL будет транслировать любой оператор в идентичную двоичную форму для анализа ее оптимизатором. Явное соединение делает код оператора более читаемым и согласованным с другими стилями соединения, поддерживаемыми SQL-92 и последующими стандартами. Ино- гда его синтаксис называют синтаксисом условного соединения, потому что структу- ра предложения join. .. on дает возможность отличать условия соединения от усло- вий поиска. Не удивительно, такое использование термина "условное" может все за- путать! Ключевое слово inner совсем необязательно и обычно опускается. Присутствие толь- ко слова join имеет точно тот же смысл, что и inner join. (Если слову join предшест- вуют left, right или full, то это не является внутренним соединением.) Три или более потока Если существует более двух потоков (таблиц), просто добавляйте предложения join. .. on для каждого отношения. Следующий пример добавляет третий поток в предыдущий пример, соединяя его со вторым потоком через другой внешний ключ отношения: SELECT Tablel.PK, Tablel.COLl, Table2.PK, Table2.COLX, Table3.COLY FROM Tablel JOIN Table2 ON Tablel.PK = Table2.FK JOIN Table3 ON TABLE2.PKX = Table3.FK WHERE Table3.STATUS = 'SOLD' AND <друте-условия-поиска> Связи составных ключей Если одно отношение связано более чем с одним столбцом, используйте ключевое слово and для разделения каждого условия соединения, как вы делаете в предложении where для множества условий. Возьмем, для примера, таблицу ТаЫеА с первичным ключом (pki, рк2), связанную с таблицей таЫев через внешний ключ (fki, fk2): SELECT ТаЫеА. COLl, ТаЫеА. COL2, TableB.COLX, TableB.COLY FROM ТаЫеА JOIN TableB ON ТаЫеА.PKI = TableB. FKI AND TableA.PKl = TableB.FK2 WHERE ...
Гпава 22. Запросы к множеству таблиц 467 Смешивание неявного и явного синтаксисов Написание операторов, включающих смешивание неявного и явного синтаксисов, невозможно в Firebird 1.5 и допустимо (но обескураживает) в Firebird 1.0.x. Следую- щий пример показывает, как не надо писать оператор соединения: SELECT Tablel.PK, Tablel.COLl, Table2.PK, Table2.COLX, ТаЫеЗ. COL Y FROM Tablel, Table2 JOIN ТаЫеЗ ON TABLEI. РК = ТаЫеЗ. FK AND ТаЫеЗ.STATUS = 'SOLD' /* это условие поиска !! */ WHERE Tablel.PK = Table2.FK AND <другие-условия-поиска> Внешние соединения В отличие от внутреннего соединения оператор внешнего соединения выбирает стро- ки участвующих таблиц, даже если в некоторых случаях не найдено соответствие. Когда полное соответствие строк не может быть сформировано соединением, тогда "отсутствующие" элементы данных формируются как null. Другое название внешне- го соединения — включающее соединение. Каждое внешнее соединение имеет левую и правую часть, левая часть является пото- ком, который присутствует в левой части фразы join, а правая часть— потоком, ко- торый назван аргументом фразы join. В операторе, содержащем множество соедине- ний, понятия "левый" и "правый" относительны — правый поток одного предложе- ния соединения может быть левым другого предложения. Такое бывает часто, когда спецификации соединения являются "сглаженной" иерархической структурой. "Левый" и "правый" значимы в логике спецификаций внешнего соединения. Внешнее соединение может быть левым, правым и полным. Каждый тип внешнего соединения создает различный выходной набор данных из тех же входных потоков. Ключевые слова left, right и full достаточны для установления факта, что соединение "внеш- нее"; ключевое слово outer является необязательной частью синтаксиса. LEFT OUTER JOIN Левое внешнее соединение (left outer2 join) в запросе создает набор данных, со- стоящий из полностью заполненных столбцов, где найдены соответствующие строки (как и во внутреннем соединении), а также частично заполненных строк для каждого 2 Ключевое слово outer для всех случаев является необязательным. —Прим. науч. ред.
468 Часть I/. Firebird SQL экземпляра, где соответствие правой стороны не найдено для ключа левой стороны. Несоответствующие столбцы будут "заполнены" значением null. Вот оператор, ис- пользующий те же входные потоки, что и наш пример inner join: SELECT Tablel.PK, Tablel.COL1, Table2.PKX, Table2.COLX FROM Tablel LEFT OUTER JOIN Table2 ON Tablel.PK = Table2.FK WHERE ... <условия-поиска> На рис. 22.2 показано, как будут объединены потоки для левого соединения. PKX COLX FK PK COL1 PKX COLX 234 99 99 234 AAA 235 101 100 NULL NULL 236 102 101 235 237 и* С , 104 102 236 ccc 238 105 103 NULL NULL 239 ? f 106 104 237 DDD 240 109 105 238 EEE 241 111 106 <> 239 FFF 242 114 107 NULL NULL 243 115 108 NULL NULL 244 116 109 240 Table2 Joined set Рис. 22.2. Левое соединение В этом случае, вместо того чтобы отбрасывать строки левого потока, для которых нет соответствия в правом потоке, запрос создает строку, содержащую данные из левого потока, и null для каждого указанного в правом потоке столбца. RIGHT OUTER JOIN Правое внешнее соединение (right outer join) в запросе создает набор данных, со- держащий полностью заполненные столбцы, где были найдены соответствующие строки (как и во внутреннем соединении), а также частично заполненные строки для каждого экземпляра, где существует правая строка, не имеющая соответствия с ле- вым потоком. Не имеющие соответствия столбцы будут "заполнены" значением null. Вот оператор, использующий те же входные потоки, что и наш пример inner join. Необязательное ключевое слово outer здесь отсутствует. select Tablel.PK, Tablel.COLl, Table2.PKX, Table2.COLX
Гпава 22. Запросы к множеству таблиц 469 FROM Tablel RIGHT JOIN Table2 ON Tablel.PK = Table2.FK WHERE ... <условия-поиска> На рис. 22.3 показано, как будут объединены потоки для правого соединения. РКХ COLXFK PK COL1 PKX COLX 234 99 99 234 AAA 235 101 101 ode 235 BBS 236 102 102 def 236 ccc 237 DDL 104 104 fgh 237 ODD 238 105 105 0!^ 238 EEE 239 FF1 106 106 ПЦ 239 FFF 240 Z'» 109 109 Van 240 Z*-У** ?"’• uub 241 111 NULL NULL 241 HHH 242 114 NULL NULL 242 I I I 243 115 NULL NULL 243 244 116 NULL NULL 244 KKK ТаЫе2 Joined set Рис. 22.3. Правое соединение FULL OUTER JOIN Полное внешнее соединение (full outer join) является целиком включающим. Оно возвращает одну строку для каждой пары соответствующих потоков и одну частично заполненную строку для каждой строки из каждого потока, где соответствие не было найдено. Данное соединение объединяет поведение правого и левого соединений. РКХ COLXFK 234 99 235 4 f 101 236 102 237 > 104 238 105 239 > 106 240 109 241 111 242 £ 1 I 114 243 i i 4 115 244 < 116 ТаЫе2 РК COL1 PKXCOLX 99 < 234 AAA 100 у NULL NULL 101 235 BBB 102 ..? .. z 236 CCC 103 > NULL NULL 104 i 237 ODD 105 tx i J » 238 FFP Sv. Km. 106 h 239 h ?- 107 > NULL NULL 108 NULL NULL 109 240 GGG NULL NULL 241 HHH NULL NULL 242 4 i 1 NULL NULL 243 J J J NULL NULL 244 KKK Joined set Рис. 22.4. Полное соединение
470 Часть V. Firebird SQL Вот оператор, использующий те же входные потоки, что и наш пример inner join: SELECT Tablel.PK, Tablel.COLl, Table2.PKX, Table2.COLX FROM Tablel FULL JOIN Table2 ON Tablel.PK = Table2.FK WHERE ... <условия-поиска> На рис. 22.4 показано, как будут объединены потоки для полного соединения. Перекрестные соединения Firebird не поддерживает языковых элементов для перекрестного соединения (cross join), которое создает набор данных, являющийся декартовым произведением двух таблиц. То есть для каждой строки левого потока выходной поток будет содержать каждую строку из правого потока. В редких случаях, когда вам нужно декартово произведение, вы можете использовать синтаксис SQL-89 без каких-либо критериев соединения в предложении where, например: SELECT Tl.*, Т2.* FROM Tl TABLEI, T2 TABLE2; Обработчик запросов Firebird иногда внутренне создает перекрестные соединения, когда конструирует "реки" в процессе соединения операций (см. разд. "Темы опти- мизации" этой главы). Естественные соединения Firebird не поддерживает естественное соединение (natural join), также известное как эквисоединение (equi join), которое создает набор данных, связывая два потока на основе соответствия столбцов, использующих общие идентификаторы столбцов с одинаковыми значениями. В Firebird вы всегда должны указывать условия соеди- нения. Двусмысленность в запросах JOIN В различных книгах по теории баз данных сказано, что двусмысленность может су- ществовать, только когда некоторые имена столбцов появляются в нескольких пото- ках. Человек, практически работающий с базами данных, может рассказать другую историю. Вопрос устранения двусмысленности отпадает в зависимости от способа, каким сервер базы данных реализует синтаксический разбор, создание потоков и сортировку, которые выполняются в процессе операции соединения. InterBase был снисходителен к отклонениям от недвусмысленности в синтаксисе со- единений — иногда с неудачными результатами. Если вы переносите существующий код вашего приложения, работавшего с InterBase, не пугайтесь исключений SQL, ко-
Глава 22. Запросы к множеству таблиц 471 торые выдаст Firebird в процессе первого тестового выполнения запросов с соедине- ниями. Он покажет вам места в вашем коде, где раньше допускалось выполнение запросов, которые могли возвращать неверные результаты. Firebird не примет запросы соединения, содержащие ссылки на столбцы, которые не имеют полного квалификатора таблицы. Квалификатор таблицы может быть иден- тификатором таблицы или алиасом таблицы. Начиная с версии 1.5 недопустимо их смешивать. Если вы используете версию 1.0, будьте последовательны, если хотите избежать переписывания кода при переходе на новые версии. Предыдущие примеры использовали идентификаторы таблиц. Использование алиа- сов таблиц более элегантно и компактно, а для некоторых запросов (см. разд. "Реен- терабельные соединения") является обязательным. Алиасы таблиц Если имя таблицы длинное или запутанное, или если существует много таблиц, ис- пользование алиасов таблиц будет полезным (и в некоторых случаях необходимым) для большей ясности операторов. Обработчик запроса трактует алиас таблицы как синоним таблицы, которую алиас представляет. Алиасы таблиц обязательны в запро- сах, которые формируют множество потоков из одной и той же таблицы. СОВЕТ. В базе данных диалекта 3, которая была создана с использованием ограниченных идентификаторов (delimited identifiers), комбинированных с симво- лами в нижнем регистре или при смешивании регистров в именовании объектов и/или с использованием "неправильных символов", написание запросов может стать довольно утомительным занятием, часто порождающим ошибки, если не используются алиасы. Алиас может быть использован везде, где он допустим для обозначения имени таб- лицы, как квалификатор; имена всех таблиц должны быть заменены на алиасы. Сме- шивание идентификаторов таблиц с алиасами в одном и том же запросе приведет к' исключениям в Firebird 1.5 и следующих версиях. Следующий пример использует идентификаторы таблиц: SELECT TABLEA.COL1, TABLEA.COL2, TABLEB.COLB, TABLEB.COLC, TABLEC.ACOL FROM TABLEA JOIN TABLEB ON TABLEA.COL1 = TABLEB.COLA JOIN TABLEC ON TABLEB.COLX = TABLEC.BCOL WHERE TABLEA.STATUS = 'SOLD' AND TABLEC.SALESMANJD = '44' ORDER BY TABLEA.COL1;
472 Часть V. Firebird SQL Тот же пример, использующий алиасы: SELECT A.COLl, A.COL2, B.COLB, B.COLC, C.ACOL FROM TABLEA А /* задает алиас */ JOIN TABLEB В ON A.COLl = В,COLA JOIN TABLEC C ON В.COLX = C.BCOL WHERE A.STATUS = 'SOLD' AND C. SALESMAN_ID = '44' ORDER BY A.COLl; Допустимые имена алиасов таблиц Используйте любую строку, содержащую до 31 символа, которые допустимы для имен метаданных (т. е. алфавитно-цифровые символы в кодировке ASCII в диапазо- нах 35—38, 48—57, 64—90 и 97—122). Пробелы, диакритические знаки, имена, за- ключенные в кавычки, и имена, начинающиеся с цифры, недопустимы. Внутренний курсор При чтении в потоке сервер базы данных использует указатель, чей адрес изменяется при продвижении чтения от начала к концу. Этот указатель называется курсором — не путайте с курсором набора, который создается в SQL с помощью declare cursor. Внутренние курсоры доступны клиентам только посредством синтаксиса соединений и подзапросов. Текущим адресом внутреннего курсора является абсолютное смещение от адреса первого потока в наборе, что означает, что он может продвигаться только вперед. Внутри оптимизатор использует индексы и организует входные потоки в план, чтобы гарантировать, что запрос начинает возвращать выходные данные в самое короткое по возможности время. Процесс оптимизации и планы детально обсуждаются в разд. "Темы оптимизации" в конце этой главы. РК COL1 102 def 103 Tablel РКХ COLXFK 234 ———— 99 235 101 236 102 237 ... ... 104 ТаЫе2 РК COL1 PKXCOLX | 101 I I 235 j. ] | 102 I det |--1 236 |ccc| Joined set Рис. 22.5. Внутренний курсор для соединения двух таблиц
Гпава 22. Запросы к множеству таблиц 473 В любых многотабличных операциях сервер Firebird поддерживает один внутренний курсор для каждого потока. В предыдущих примерах соединений таблиц таЫеА и таЫев каждая имеет свой курсор. Когда появляется соответствие, сервер создает объ- единенный образ для выходного потока, копируя потоки с текущего адреса из двух курсоров, как показано на рис. 22.5. Реентерабельные соединения Условия проектирования иногда требуют формирования объединенного набора из двух или более потоков, которые поступают из одной и той же таблицы. Часто таб- лица проектируется с иерархической, или древовидной, структурой, где каждая стро- ка в таблице логически является потомком родителя в той же таблице. Первичный ключ таблицы указывает на узел дерева уровня потомка, в то время как колонка внешнего ключа хранит родительский ключ, ссылающийся на значение первичного ключа в другой строке. Запрос на "выравнивание" отношения родитель-потомок требует соединения, кото- рое извлекает один поток для родителей, а другой — для потомков из той же табли- цы. Обычный термин для этого — ссылающееся на себя соединение. Термин реенте- рабельное соединение морфологически является более подходящим, т. к. существуют другие типы реентерабельных запросов, которые не используют соединений. Позже в этой главе в разд. "Подзапросы" обсуждаются другие типы реентерабельных за- просов. Курсоры для реентерабельных соединений Для выполнения реентерабельного соединения сервер поддерживает один внутрен- ний курсор для каждого потока в пределах образа одной таблицы. Концептуально он трактует контексты двух курсоров, как если бы они были отдельными таблицами. В этой ситуации синтаксис ссылок на таблицу обязательно использует алиасы для различения курсоров двух потоков. В следующем примере каждое подразделение организации хранится с родительским идентификатором, который указывает на первичный ключ его руководящего подраз- деления. Приведенный далее запрос трактует подразделения и родительские подраз- деления, как если бы они были двумя таблицами: SELECT D1.ID, D1.PARENTID, DI.DESCRIPTION AS DEPARTMENT, D2.DESCRIPTION AS PARENTJDEPT FROM DEPARTMENT DI LEFT JOIN DEPARTMENT D2 /* левое соединение обращается к корню дерева, ID 100 */ ON Dl.PARENTID = D2.ID;
474 Часть И Firebird SQL На рис. 22.6 проиллюстрировано, как выбираются записи потомков в реентерабель- ном обращении к таблице department. Parent IC ID (PK) Description (fk) Department D2 Parent 1П Pointer —► 100 Boss’s Department NULL 101 Sales 100 102 Telesales 101 103 Internet Sales 101 .1 3 (PK), Description . (FK) 104 Production 100 Boss’s Department NULL 105 Factory 104 101 Sales 100 250 Finishing 105 102 Telesales 101 252 Store 104 103 Internet Sales 101 253 Inward Goods 252 104 Production 100 254 Dispatch 252 106 Maintenance 105 250 Finishing 105 1 • • | 252 | Store | 104 | *- Department D1 253 Inward Goods 252 254 Dispatch 252 106 Maintenance 105 Рис. 22.6. Внутренние курсоры для простого реентерабельного соединения Простой двухуровневый результат показан на рис. 22.7. Результат этого запроса очень простой — одноуровневая денормализация. Операции получения древовидного результата для таблиц такого вида часто являются рекур- сивными, они используют хранимые процедуры для реализации и управления рекур- сией3. ID Parent ID Department Parent_Dept 100 NULL Boss’s Department NULL 101 100 Sales Boss's Department 102 101 Telesales Sales 103 101 Internet Sales Sales 104 100 Production Boss’s Department 105 104 Factory Production 250 105 Finishing Factory 252 104 Store Production 253 252 Inward Goods Store 254 252 Dispatch Store 106 105 Maintenance Factory Рис. 22.7. Результат простого реентерабельного соединения 3 В "SQL for Smarties" Joe Celco предлагает интересные решения для хранения и поддержки древовидных структур в реляционных базах данных (Morgan Kaufmann, 1999).
Гпава 22. Запросы к множеству таблиц 475 Подзапросы Подзапрос — это специальный вид выражения, которое фактически является запро- сом select к другой таблице, включенным в спецификацию основного запроса. Вы- ражение включенного запроса называют подзапросом, вложенным запросом, встро- енным запросом, а иногда (ошибочно) sub-SELECT. В Firebird версии 1.5 и выше выражения подзапроса используются тремя способами: ♦ для получения одной строки или многострочного входного набора для операции insert (его синтаксис описан в разд. "Оператор INSERT" главы 20); ♦ для задания в процессе выполнения выходного столбца только для чтения для запроса select; ♦ для получения значений или условий для предикатов поиска. В версиях Firebird после 1.5 появляется четвертый вариант подзапросов— виртуаль- ная таблица, который кратко обсуждается в главе 24. Задание столбца при использовании подзапроса Выходной столбец времени выполнения может быть задан запросом одного столбца из другой таблицы. Выходному столбцу должен быть назначен новый идентифика- тор, который для завершенности синтаксиса может быть отмечен необязательным ключевым словом as (см. разд. "Наследуемые поля и алиасы столбцов"главы 21). Вложенный запрос всегда должен иметь условие в предложении where для ограниче- ния вывода одним столбцом из одной строки (называется скалярным запросом); ина- че вы увидите подобное сообщение об ошибке: "Multiple rows in singleton select" (За- прос, который должен вернуть одну строку, вернул множество строк). Следующий запрос использует подзапрос для получения выходного столбца. select last_name, FIRST-NAME, ADDRESS1, ADDRESS2, POSTCODE, (SELECT START-TIME FROM ROUTES WHERE POSTCODE = '2261' AND DOW = 'MONDAY') AS START_TIME FROM MEMBERSHIP WHERE POSTCODE = '2261'; Этот подзапрос указывает одно значение postcode для получения значения поля starT-Time транспортного маршрута (таблица routes). Столбцы postcode в главном запросе и в подзапросе могут быть заменены параметрами. Чтобы сделать запрос более общим и более полезным, мы можем использовать коррелированные подза- просы.
476 Часть I/. Firebird SOL Коррелированные подзапросы Когда элемент данных, полученный из вложенного подзапроса, должен быть выбран в контексте связи значения с текущей строкой главного запроса, возможно использо- вание коррелированного подзапроса. Firebird требует явно указанных идентификато- ров в коррелированных подзапросах. В следующем примере связываемые столбцы в главном запросе и в подзапросе явля- ются коррелированными; здесь использованы алиасы таблиц для устранения какой- либо неясности: SELECT M.LAST_NAME, M.FIRST_NAME, М.ADDRESS1, M.ADDRESS2, М.POSTCODE, (SELECT R.START_TIME FROM ROUTES R WHERE R.POSTCODE = M.POSTCODE AND M.DOW = 'MONDAY') AS STARTJTIME FROM MEMBERSHIP M WHERE . . .; Этот запрос возвращает одну строку для каждого выбранного элемента, независимо от того, существует ли соответствие между полями postcode обеих таблиц. Если нет соответствия, то поле stapttime будет иметь значение null. Подзапрос или соединение? Запрос из предыдущего примера может быть составлен с использованием левого со- единения вместо подзапроса: SELECT M.LAST_NAME, М. FIRST_NAME, M.ADDRESS1, M.ADDRESS2, M.POSTCODE, R.START_TIME FROM MEMBERSHIP M LEFT JOIN ROUTES R ON R.POSTCODE = M.POSTCODE WHERE M.DOW = 'MONDAY1; Относительная стоимость этого запроса и предыдущего, использующего подзапрос, практически одинакова. Хотя каждый может достигать результата разными путями, оба требуют полного сканирования полученного потока при преобразовании иссле- дуемого потока. Разница в стоимости может стать значительной, если коррелированный запрос будет использован вместо внутреннего соединения:
Гпава 22. Запросы к множеству таблиц 477 SELECT M.LAST_NAME, М. FIRST__NAME, М.ADDRESS1, M.ADDRESS2, М.POSTCODE, R. START_TIME FROM MEMBERSHIP M JOIN ROUTES R ON R.POSTCODE = M.POSTCODE WHERE M.DOW = 'MONDAY'; Внутреннее соединение не требует просмотра каждой строки исследуемого потока, потому что оно отбрасывает любую строку исследуемого потока (routes), которая не соответствует условию поиска. В противоположность этому контекст коррелирован- ного подзапроса меняется с каждой строкой, и ни при каких условиях не исключает из процесса сканирования несовпадающее поле postcode. Следовательно, коррелиро- ванный подзапрос должен быть выполнен для каждой строки в наборе. Если выходной набор данных потенциально большой, посмотрите, насколько важна необходимость выполнить включающий поиск. Хорошо продуманный коррелиро- ванный запрос полезен для малых наборов. Не существует никаких численных оце- нок для выбора одного или другого. Как обычно, тестирование в реальных услови- ях — единственно надежный способ решить, что лучше работает для ваших конкрет- ных потребностей. Когда не надо использовать подзапросы Подход, основанный на подзапросах, становится странным, когда вам нужно полу- чить более одного поля из той же таблицы. Подзапрос может вернуть одно и только одно поле. Для получения нескольких полей требуется отдельный коррелированный подзапрос и его алиас для каждого выбираемого поля. Если левое соединение спо- собно выполнить эти условия, то его всегда следует использовать. Поиск с использованием подзапроса Использование существующих предикатов в подзапросах— особенно предиката exists о— обсуждалось в главе 21. Подзапросы могут также быть использованы другими способами в предикатах условий поиска в предложениях where и группиро- вания. Реентерабельные подзапросы Запрос может использовать реентерабельный подзапрос для задания условия поиска в той же таблице. Использование алиасов таблиц является обязательным. В следую- щем примере оператор выполняет подзапрос для поиска в главной таблице даты са- мой последней транзакции для задания условия поиска главного запроса.
478 Часть V. Firebird SQL SELECT A1.C0L1, A1.COL2, Al. TRANSACTIONJ3ATE FROM ATABLE Al WHERE Al.TRANSACTION-DATE = (SELECT MAX(A2.TRANSACTION_DATE) FROM ATABLE A2); Добавление данных с использованием подзапроса с соединениями В главе 20 мы рассматривали встроенный метод выборки для передачи данных в опе- ратор insert, например, INSERT INTO ATABLE ( COLUMN2, COLUMNS, COLUMN4) SELECT BCOLUMN, CCOLUMN, DCOLUMN FROM BTABLE WHERE. . .; Этот метод не ограничен однопоточным запросом. Ваш входной подзапрос может быть соединенным. Эта возможность очень полезна, когда вам нужно экспортировать ненормализованные данные во внешнюю таблицу для использования в другом при- ложении, таком как электронная таблица, программа работы с базой данных или про- грамма бухгалтерского учета. Например: INSERT INTO EXTABLE ( TRANDATE, INVOICE_NUMBER, EXT-CUST_CODE, VALUE) SELECT INV. TRANSACTION-DATE, INV. INVOICE-NUMBER, CUS.EXT-CUS-CODE, SUM(INVD.PRICE * INVD.NO-ITEMS) FROM INVOICE INV JOIN CUSTOMER CUS ON INV.CUST—ID = CUS.CUST—ID JOIN INVOICE-DETAIL INVD ON INVD.INVOICE-ID = INV.INVOICE—ID WHERE ... GROUP BY . . . ; Оператор UNION Оператор union может быть использован для объединения результатов двух или бо- лее операторов select и создания единого набора только для чтения, состоящего из строк, полученных из разных таблиц или из разных наборов, запрошенных из той же
Гпава 22. Запросы к множеству таблиц 479 таблицы. Множество запросов объединяются, каждое подмножество спецификаций связывается со следующим с помощью ключевого слова union. Данные должны быть выбраны так, а запрос сконструирован таким образом, чтобы все участвующие вход- ные наборы были совместимы для объединения. Наборы, совместимые для объединения Для каждой операции select, создающей входной поток для union, спецификация должна содержать список столбцов, одинаковый для всех других операций (количе- ство и порядок столбцов) с соответствующими типами данных. Предположим, мы имеем следующие спецификации двух таблиц: CREATE TABLE CURRENT_TITLES ( ID INTEGER NOT NULL, TITLE VARCHAR(60) NOT NULL, AUTHOR__LAST_NAME VARCHAR (40), AUTHOR_FIRST_NAMES VARCHAR(60), EDITION VARCHAR(IO) , PUBLICATION—DATE DATE, PUBLISHER_ID INTEGER, ISBN VARCHAR(15) , LIST_PRICE DECIMAL(9,2)); /“/ CREATE TABLE PERIODICALS ( PID INTEGER NOT NULL, PTITLE VARCHAR(60) NOT NULL, EDITOR—LAST—NAME VARCHAR(40), EDITOR—FIRST_NAMES VARCHAR (60), I SSUE_NUMBER VARCHAR (10), PUBLICATION-DATE DATE, PUBLISHER_ID INTEGER, LIST-PRICE DECIMAL (9, 2) ) ; Эти таблицы совместимы для объединения, потому что мы можем создать запрос для получения наборов с соответствующей "геометрией": SELECT ID, TITLE, AUTHOR_LAST_NAME, AUTHOR_FIRST_NAMES, EDITION VARCHAR (10), LIST_PRICE FROM CURRENT_TITLES UNION SELECT ID, TITLE, EDITOR_LAST_NAME,
480 Часть I/. Firebird SQL EDITOR_FI RST_NAMES, ISSUE_NUMBER, LIST_PRICE FROM PERIODICALS; union, содержащий select * from таблица, не будет работать, поскольку структуры таблиц различны — вторая таблица не содержит колонки isbn. Использование столбцов времени выполнения в объединениях Идентификаторы столбцов выходного набора данных задаются в первой специфика- ции select. Если вы хотите использовать альтернативные имена столбцов, алиасы столбцов могут быть использованы в выходном списке первой спецификации select. Дополнительно, если необходимо, поля, полученные из констант или переменных, могут быть включены в предложение select каждого объединяемого потока. Сле- дующий запрос содержит наиболее удобный список публикаций из наших двух таб- лиц: SELECT ID, TITLE as PUBLICATION, 'BOOK ’ AS PUBLICATIONJTYPE, CAST (AUTHOR_LAST_NAME || ’, ' || AUTHOR__FIRST_NAMES AS VARCHAR(50)) AS AUTHOR_EDITOR, EDITION AS EDITION__OR_ISSUE, PUBLICATION_DATE DATE, PUBLISHER_ID, CAST1ISBN AS VARCHAR(14)) AS ISBN, LIST_PRICE FROM CURRENT_TITLES WHERE . . . UNION SELECT ID, TITLE, 'PERIODICAL', ’ EDITOR_LAST_NAME I I ' , ' | I EDITOR_FIRST_NAMES AS AUTHOR_EDITOR, CAST (AUTHOR_LAST_NAME || ', ’ H AUTHOR_FIRST_NAMES AS VARCHAR(50)), ISSUE_NUMBER, PUBLICATION_DATE, PUBLISHER_ID, 'Not applicable', LIST_PRICE FROM PERIODICALS WHERE ... ORDER BY 2;
Гпава 22. Запросы к множеству таблиц 481 Условия поиска и упорядочивания Обратите внимание в предыдущем примере, что условия поиска возможны в каждой объединяемой спецификации select. Они являются обычными выражениями поиска, которые должны соответствовать объединяемому набору, управляемому текущим выражением select. Не существует способа коррелировать условия поиска в преде- лах поднаборов. Допустимо только одно предложение упорядочения, оно должно следовать после всех поднаборов. Синтаксис order by номер (т. е. упорядочение по номеру столбца) требуется для упорядочения объединяемых наборов. Реентерабельные запросы UNION Возможно обращение к реентерабельной технике для создания объединяющего за- проса, который извлекает несколько поднаборов из одной таблицы. Алиасы таблиц требуются в предложениях from, но ссылки на колонки не обязательно должны быть полностью специфицированы. Возвращаясь к нашей таблице current_titles, предпо- ложим, что мы хотим получить список заголовков книг в зависимости от цены. Реен- терабельный запрос должен выглядеть приблизительно следующим образом: SELECT ID, TITLE, CAST('UNDER $20' AS VARCHAR(IO)) AS RANGE, CAST (AUTHOR-LAS T_NAME || ', ' || AUTHOR—FIRST—NAMES AS VARCHAR (50) ) AS AUTHOR, EDITION, LIST_PRICE FROM CURRENT—TITLES CT1 WHERE LIST_PRICE < 20.00 UNION SELECT ID, TITLE, CASTf'UNDER $40' AS VARCHAR(IO), CAST (AUTHOR_LAST_NAME || ', ’ || AUTHOR—FI RST—NAMES AS VARCHAR(50)) , EDITION, LIST_PRICE FROM CURRENT_TITLES CT2 WHERE LIST—PRICE >= 20.00 AND LIST_PRICE < 40.00 UNION SELECT ID, TITLE, CAST ('$40 PLUS' AS VARCHAR(IO) ) , CAST (AUTHOR—LAST_NAME || ', ’ || AUTHOR_FIRST_NAMES AS VARCHAR(50) ) , EDITION, LIST_PRICE FROM CURRENT-TITLES CT3 WHERE LTST_PRICE >= 40.00; 16 Зак. 420
482 Часть V. Firebird SQL UNION ALL Если в процессе создания объединенного набора были сформированы дублирован- ные строки, то поведение по умолчанию — исключение из набора дублированных строк. Для включения дубликатов используйте union all вместо union. Темы оптимизации: планы запросов и оптимизатор В этом разделе рассматривается подсистема оптимизатора Firebird и те стратегии, применяемые им для создания планов запроса, которые будут использованы серве- ром для операторов select и подзапросов во время выполнения. Мы вкратце рас- смотрим синтаксис плана запроса, а также некоторые способы передачи ваших соб- ственных планов серверу. Планы и оптимизатор запросов Firebird Для выполнения оператора select или условия поиска сервер Firebird преобразует оператор в набор внутренних алгоритмов, называемых оптимизированным запросом. Каждый раз, когда оператор подготавливается для выполнения, оптимизатор вычис- ляет шан поиска. План План запроса создает некий вид дорожной карты, которая сообщает серверу наиме- нее дорогостоящую дорогу для требуемого процесса поиска, сортировки и поиска соответствия запрашиваемому результату. Чем более эффективный план сконструи- рует оптимизатор, тем быстрее оператор начнет возвращать результаты. План создается в соответствии с наличием доступных индексов, с тем способом, ка- ким индексы или потоки соединяются или сливаются, а также с методом поиска (ме- тодом доступа). При вычислении плана оптимизатор будет просматривать каждый доступный индекс, выбирая или отвергая индексы в соответствии с их стоимостью. Помимо существо- вания индекса он принимает во внимание другие факты, такие как размер таблицы, степень распределения различных значений в индексе. Если оптимизатор может оп- ределить, что использование индекса будет требовать больше издержек, чем после- довательный просмотр строки за строкой в потоке, он может принять решение про- игнорировать индекс, в пользу последовательного формирования промежуточного потока или создания потока естественным образом. Выражения плана Для элементов плана в SQL Firebird применяет такой же синтаксис, какой он исполь- зует для передачи серверу. Понимание сгенерированных оптимизатором планов мо-
Глава 22. Запросы к множеству таблиц 483 жет быть очень полезным как для предвидения, каким образом оптимизатор будет решать конкретную задачу, так и в качестве основы для написания пользовательских планов. Синтаксис выражений плана Шаблон синтаксиса для фразы plan: <спецификация-запроса> PLAN <выражение-плана> Этот синтаксис позволяет задавать одно отношение или соединение двух или более отношений за один раз. Могут быть использованы вложенные скобки для задания любых комбинаций соединений. Операции передают свои результаты в выражении слева направо. Символы В используемой здесь нотации круглые скобки и запятые являются элементами син- таксиса. Фигурные скобки, квадратные скобки и символ вертикальной черты не яв- ляются частью синтаксиса— как и в ранее описанном синтаксисе они указывают, соответственно, обязательные и необязательные фразы и взаимоисключающие вари- анты. plan-expression := [join-type] (plan-item-list) join-type := [JOIN] | [SORT] [MERGE] plan-rtem-list := plan-item | plan-item, plan-item-list plan-item := table-identifier access-type I plan_expression table-identifier := { table-identifier | alias-name ) [table-identifier] access-type := { NATURAL | INDEX (index-list) I ORDER index-name ). index-list := index-name | index-name, index-list Элементы Тип соединения (join-type) может быть join или merge. ♦ Тип соединения по умолчанию join (т. е. соединение двух потоков с использова- нием индекса правого потока для поиска соответствующих ключей в левом по- токе). ♦ merge выбирается, если нет используемых индексов. В этом случае два потока со- храняются в соответствующем порядке, а затем сливаются. В пользовательских планах скорость поиска будет увеличена при задании такого типа соединения, ко- гда нет доступных индексов. Идентификатор таблицы (table-identifier) задает поток. Он должен быть именем таблицы базы данных или алиасом. Если одна и та же таблица будет использована более одного раза, для нее должен быть указан алиас для каждого использования. Алиас должен следовать после имени таблицы при ее первом упоминании. Для спе- цификации базовых таблиц в просмотре синтаксис предоставляет возможность да- вать таблицам множество идентификаторов. Планы для просмотров обсуждаются в главе 24.
484 Часть V. Firebird SQL Тип доступа (access-type) должен быть одним из следующих: ♦ natural — доступ к строкам осуществляется последовательно, без какого-либо особого порядка. Это тип доступа по умолчанию, он может быть опущен, тем не менее разумно включить его в пользовательский план для документирования; ♦ index — позволяет указать один или более индексов для вычисления предикатов и проверки условий соединения в запросе; ♦ order— указывает, что результат запроса должен быть отсортирован (упорядо- чен) по самому левому потоку с использованием индекса. Элемент плана (plan-item) включает в себя план доступа, а также идентификатор таблицы или ее алиас. Оптимизатор Если вы не очень хорошо знакомы с планами запросов, вы, вероятно, будете удивле- ны, как весь этот синтаксис может транслироваться в план. Чуть позже синтаксис станет более осмысленным, когда мы посмотрим на некоторые планы, сгенерирован- ные оптимизатором. Тем не менее в настоящий момент будет полезным посмотреть, как оптимизатор использует "материал" для его операций: соединения и условия по- иска, требуемые в операторе, потоки, лежащие в основе спецификации запроса, и доступные индексы. Факторы в оценке преобразований (вычислений) Цель оптимизатора — создать отражающий стратегию план, который, в соответствии с некоторыми факторами, скорее всего, начнет выдавать выходной поток наиболее быстрым образом. Вычисление плана может оказаться довольно неточным при ис- пользовании некоторых переменных, которые могут дать только приблизительную оценку. Рассматриваемые факторы включают: ♦ доступность индекса и селективность этого индекса. Фактор селективности, ис- пользуемый в оценках, выбирается из системных таблиц при открытии базы дан- ных. Даже в начале работы он может быть неверным, поскольку может изменять- ся в процессе операций обширных изменений, выполненных с момента последне- го вычисления селективности; ♦ количество строк в таблицах потоков; ♦ существует ли критерий выбора, и если да, существует ли доступный или подхо- дящий индекс; ♦ необходимость выполнения сортировок, как промежуточных (для слияния), так и окончательной (для упорядочения и группирования). Потоки Применение термина "поток", который возникает при обсуждении оптимизатора, является просто общим способом наименования множества строк. Набор может со- держать все строки и столбцы таблицы или это может быть подмножество данных таблицы, ограниченное некоторым образом спецификациями столбцов и условиями
Гпава 22. Запросы к множеству таблиц 485 поиска. В процессе обработки запроса сервер может из существующего потока соз- давать новые потоки, такие как внутренний сортированный набор или набор значе- ний подзапроса для сравнения in о. См. также далее разд. "Реки". Использование индексов Сервер Firebird может использовать в запросах индекс для трех видов задач. ♦ Сравнение: выполняется сравнение на условия равно, больше, меньше или starting with между значением столбца и константой. Если столбец индексиро- ван, то индексный ключ используется для сокращения просматриваемых строк, делая ненужным сканирование всех строк таблицы данных. ♦ Соответствие потоков для соединений: если доступен индекс для столбцов, ука- занных в качестве условия соединения для потока с одной стороны соединения, то поток другой стороны будет отыскиваться первым, а его столбцы соединения будут проверяться на соответствие с помощью индекса. ♦ Сортировка и группирование: если условия в order by или group by указывают столбец, который индексирован, то этот индекс будет использован для поиска строк в нужном порядке, а операция сортировки не потребуется. Формирование двоичного образа потоков Когда критерий соединения использует индексированный столбец, сервер Firebird генерирует двоичный образ каждого выбранного потока. Затем, используя логику И/ИЛИ в соответствии с критерием поиска, индивидуальные потоки двоичных обра- зов строк объединяются в единый двоичный образ, который описывает все подходя- щие строки. Если запросу нужно сканировать один и тот же поток за множество про- ходов, сканирование двоичного образа выполняется гораздо быстрее, чем повторный просмотр индексов. Двоичное представление языка В основе обработчика SQL Firebird лежит его родной язык двоичного представления (Binary Language Representation, BLR) и интерпретатор, состоящий из лексического анализатора, синтаксического анализатора, таблицы символов и генератора кода. Об- работчик DSQL и включенные приложения передают BLR оптимизатору, и именно BLR, а не строки анализирует оптимизатор. Может случиться такое, что два разных оператора SQL интерпретируются в идентичные BLR. СОВЕТ. Если вам любопытно, на что похож BLR, вы можете использовать инст- румент qli (в каталоге Firebird bin) для просмотра операторов. Запустите qli и вы- дайте команду set blr для включения отображения BLR. Документ по qli в формате PDF может быть загружен с различных сайтов Firebird, в том числе с http://www.ibphoenix.com. Если у вас есть операторы select в хранимой процедуре или триггере, вы можете просмотреть их в isql, выдав команду set blob 2 и выбрав столбец RDB$PROCEDURE__BLR ИЗ Таблицы RDB$PROCEDURES.
486 Часть V. Firebird SQL Соединения без индексов Если не существует доступного индекса для условий соединения, оптимизатор задает сортированное слияние двух потоков. Сортированное слияние включает сортировку обоих потоков, а затем одновременное сканирование обоих потоков для их слияния в реку. Сортировка обоих потоков исключает необходимость постоянного сканирова- ния правого потока для поиска соответствия ключам левого потока. Реки Рекой является поток, получающийся при слиянии двух потоков в результате соеди- нения. Когда соединения являются вложенными, река может стать потоком для по- следующих операций. Различные стратегии слияния потоков в реки сравниваются по стоимости. Полученный в результате план использует лучшую стратегию, которую оптимизатор может определить для соединения пар потоков в порядке слева направо. При вычислении стоимости стратегии соединения оптимизатор определяет весовые коэффициенты для порядка, в котором соединяются потоки. Альтернативные опре- деления порядка больше подходят к внутренним соединениям, чем к внешним. Пол- ные соединения требуют особой обработки. Размер конкретной реки находится после того, как будет определена наибольшая ветвь в процессе оценки соединения. Оптимизатор выбирает наибольшую ветвь — максимальное количество пар потоков, которые могут быть соединены напрямую. Предпочитаемый метод доступа— сканирование самого большого потока (в идеале самого левого потока) и просмотр в цикле меньших потоков. При этом более важным, чем размер ветви соединения, является способ, каким чита- ются строки из правого потока. Тип доступа оценивается в соответствии с доступны- ми индексами и их атрибутами (селективностью) в сравнении с естественным поряд- ком и спецификациями сортировки, представленными в общей картине расчетов. Если лучший индекс доступен для самого короткого потока, выбор самого большого потока в качестве управляющего потока может быть менее важным, чем экономия стоимости при выборе самого короткого потока с индексом высокой селективности (в идеале уникальный индекс). В этом случае подходящие строки в искомом потоке могут быть получены в результате одного прохода, а неподходящие строки игнори- руются. Примеры планов Многие графические инструменты администратора базы данных имеют средства для просмотра планов оптимизатора при подготовке оператора. Собственная утилита Firebird isql предоставляет две интерактивные команды для просмотра планов. Просмотр планов в isql SET PLAN В интерактивной сессии isql вы можете использовать set plan с необязательными ключевыми словами on или off для переключения режима отображения плана в нача- ле консольного вывода:
Глава 22. Запросы к множеству таблиц 487 SQL>SET PLAN; SQL>SELECT FIRST_NAMESf SURNAME FROM PERSON CON>ORDER BY SURNAME; PLAN (PERSON ORDER XA__SURNAME) FIRST NAMES George Stephanie SURNAME Abraham Allen SET STATS В другой команде переключателя вы можете для isql установить отображение неко- торых статистических данных запроса в конце выводимых данных, которые могут оказаться очень полезными при тестировании альтернативных структур запроса и его планов: SQL>SET STATS ON; SQL> оапускаете ваш запрос> PLAN.. <вывод> Current memory = 728316 Delta memory = 61928 Max memory - 773416 Elapsed time =0.17 sec Buffers = 2048 Reads = 15 Writes = 0 Fetches = 539 SET PLANONLY Альтернативная команда переключателя set planonly [on । off] подготавливает оператор и отображает план без выполнения запроса: SQL>SET PLAN OFF; SQL>SET PLANONLY ON; SQL>SELECT FIRST_NAMES, SURNAME FROM PERSON CON>ORDER BY SURNAME; PLAN (PERSON ORDER XA_SURNAME) Если вы собираетесь использовать план оптимизатора в качестве стартовой точки для конструирования пользовательского предложения plan, то имейте в виду, что синтак- сис выводимого выражения идентичен требуемому синтаксису для выражения плана
488 Часть V. Firebird SQL в операторе4. Неудобным является то, что в isql не существует возможности выво- дить план оптимизатора в текстовый файл. СОВЕТ. Графические инструменты, которые отображают планы, обычно предос- тавляют средства копирования/вставки. Следующие примеры используют запросы, которые вы сами можете проверить, ис- пользуя тестовую базу данных employee.fdb, инсталлированную в ваш каталог при- меров5. Простейший запрос Этот запрос просто отыскивает все строки из таблицы соотве1ствия в произвольном порядке. Хотя оптимизатор определяет наличие индекса (индекс первичного ключа), он его не использует для поиска: SQL>SET PLANONLY ON; SQL> SELECT * FROM COUNTRY; PLAN (COUNTRY NATURAL) Упорядоченный запрос Это все еще простой запрос без соединений: SQL>SELECT * FROM COUNTRY ORDER BY COUNTRY; PLAN (COUNTRY ORDER RDB$PRIMARY1) Оптимизатор выбирает индекс первичного ключа, потому что он имеет запрашивае- мое упорядочение; при этом не требуется создание промежуточного потока для сор- тировки. Теперь давайте посмотрим, что произойдет, когда мы решим упорядочить тот же са- мый запрос по неиндексированному столбцу: SELECT * FROM COUNTRY ORDER BY CURRENCY; PLAN SORT ( (COUNTRY NATURAL,) ) 4 Многие аспекты плана, сконструированного внутренне оптимизатором, не видны в плане, показанном в isql и недоступны через синтаксис предложения plan для пользовательских пла- нов. Синтаксис показывает только подмножество реального плана, которому будет следовать сервер при выполнении запроса. 5 Поскольку база данных примера не имеет таблиц без индексов, некоторые примеры в этом разделе используют специально описанные неиндексированные версии таблиц EMPLOYEE, PROJECT и DEPARTMENT, названные EMPLOYEE1, PROJECTl и Departmentl соответствен- но. Скрипт с именем NO_INDEXES.SQL для создания этих таблиц может быт загружен с http://www.apress.com.
Гпава 22. Запросы к множеству таблиц 489 Оптимизатор выбирает выполнение сортировки, просматривая таблицу country в ес- тественном порядке. Перекрестное соединение Перекрестное соединение, которое обычно создает бесполезный набор, вовсе не ис- пользует критериев соединения: SQL> SELECT Е.*, Р.* FROM EMPLOYEE Е, PROJECT Р; PLAN JOIN (Р NATURAL,Е NATURAL) Оно просто сливает каждый поток в левой части с каждым потоком правой части. Оптимизатор не использует индексы. Однако этот пример полезен в качестве введе- ния в соединение между алиасами двух таблиц в спецификации соединения и их ис- пользовании в плане. Алиасы, указанные в запросе, используются в плане, выводимом оптимизатором. Для совместимости при создании пользовательского плана хорошей идеей является ис- пользование того же способа идентификации таблиц, как и в запросе. При этом, не- смотря на то, что синтаксический анализатор не допускает смешивания идентифика- торов таблиц и их алиасов в запросах, предложение plan допускает любое смешива- ние6. Например, следующий вариант является приемлемым. Обратите внимание, что оптимизатор допускает использование и идентификаторов таблиц в предложении PLAN. SQL> SELECT Е.*, Р.* FROM EMPLOYEE Е, PROJECT Р CON> PLAN JOIN (PROJECT NATURAL, EMPLOYEE NATURAL); PLAN JOIN (P NATURAL, E NATURAL) Соединение с индексированными ключами равенства Такое соединение денормализует отношение один-ко-многим — каждый служащий имеет одну или более записей истории заработной платы: SELECT Е.*, S.OLD_SALARY, S.NEW^S ALARY FROM EMPLOYEE E JOIN SALARYJilSTORY S ON S.EMP_NO = E.EMP_NO; PLAN JOIN (S NATURAL, E INDEX (RDB$PRIMARY7)) Оптимизатор выбирает цикл по (потенциально) самому длинному детальному потоку для поиска релевантных строк с использованием индекса уникального первичного ключа таблицы employee. В этом примере либо количество строк в каждой таблице 6 Другой причиной использования алиасов при задании запросов и планов является то, что, скорее всего, подобная согласованность будет поддерживаться в следующих версиях Firebird.
490 Часть У. Firebird SQL будет приблизительно равным, либо количество строк в таблице salary_history не превысит количество строк в таблице employee в той мере, чтобы не достичь преиму- ществ уникального индекса в качестве ключа соответствия. Это внутреннее соедине- ние, и оптимизатор разумно предполагает, что именно правый поток определит раз- мер реки. Давайте посмотрим, как оптимизатор трактует те же самые потоки, когда соединение является внешним левым'. SELECT Е.*, S . OLD_SALARY, S.NEW_SALARY FROM EMPLOYEE E LEFT JOIN SALARYJ3ISTORY S ON S.EMP_NO = E.EMP_NO; PLAN JOIN (E NATURAL, S INDEX (RDBSFOREIGN21)) В этот раз одна строка будет возвращена для каждой строки правого потока, незави- симо от того, будет ли существовать соответствующий ключ в управляющем потоке. Размер реки здесь не имеет значения, поскольку внешние соединения однозначно определяют, какая таблица должна быть в левой стороне, чтобы по ней проводить цикл просмотра. Это алгоритм внешнего соединения, который определяет метод дос- тупа, не измеряющий потоки. С таблицей employee в левой части не существует воз- можности создать внешнее соединение путем просмотра таблицы salary_history с ПОИСКОМ В EMPLOYEE. Поскольку оптимизатор не делает выбора относительно того порядка, в котором бу- дут соединяться потоки, он просто выбирает наиболее подходящий индекс из SALARY_HISTORY. Когда размер имеет значение В следующем примере размер таблицы не виден при использовании индекса уни- кального первичного ключа. Таблица department содержит 21 строку, таблица project — 6 строк, и оптимизатор выбирает меньший индекс внешнего ключа для оптимизации поиска соответствия по большей таблице: SELECT * FROM DEPARTMENT D JOIN PROJECT P ON D.MNGR_NO = P.TEAM—LEADER ; PLAN JOIN (D NATURAL,P INDEX (RDB$FOREIGN13)) Соединение с индексированным предложением ORDER BY Использование индексированных спецификаций упорядочения может повлиять на то, как оптимизатор выбирает навигацию по потокам. Возьмем следующий пример: SQL> SELECT Р.*, Е.FULL-NAME FROM PROJECT Р JOIN EMPLOYEE E
Глава 22. Запросы к множеству таблиц 491 ON E.EMP_NO = Р. TEAM_LEADER ORDER BY P. PRO J _NAME ; PLAN JOIN (P ORDER RDBSll, E INDEX (RDBS PRIMARY?) ) Уникальный индекс для employee выбирается по причине неявного условия фильтра в критерии соединения. Запрос сокращает количество служащих, которые не являются руководителями, а уникальный индекс позволяет исключить сканирование таблицы employee. Выбор индекса фильтрации может также повлиять на необходимость ис- пользования навигационного индекса в proj_name для сортировки7. Оптимизатор выбирает индекс правой стороны, потому что правый поток будет того же размера, что и левый или (потенциально) большего размера. Опять же, оптимиза- тор не может сказать, что это отношение является отношением один к одному. Стол- бец proj_name, задающий порядок выходного набора, имеет уникальный индекс, соз- данный ограничением unique, чтобы использовать для сортировки, и оптимизатор выбирает этот индекс. Индекс сортировки появляется в плане первым, указывая сер- веру на необходимость сортировки левого потока перед тем, как он будет отыскивать соответствие ключа соединения в правом потоке. Соединение равенства при отсутствии доступных индексов Таблицы в следующем запросе являются неиндексированными копиями таблиц project и employee (см. сноску 4 ранее в этой главе): SQL> SELECT Pl.*, El.FULL_NAME FROM PROJECTl Pl JOIN EMPLOYEEl El ON El.EMP-NO = Pl. TEAM__LEADER ORDER BY Pl. PROJ—NAME; PLAN SORT (MERGE (SORT (El NATURAL) , SORT (Pl NATURAL))) Потоки с обеих сторон будут сортироваться, а затем сливаться, полученная река сно- ва будет сортироваться, потому что ни один из потоков не имеет требуемого порядка сортировки. Трехстороннее соединение с индексированными равенствами Рассмотрим тройное эквисоединение в следующем примере: SQL> SELECT P.PROJJIAME, D.DEPARTMENT, PDB. PROJECTED—BUDGET FROM PROJECT P 7 "Сортировка no индексу" является недоразумением. Предложение order в плане запроса дает указания серверу читать поток вне порядка хранения (то есть с использованием навигаци- онного индекса для поиска строк). Этот метод может работать только с управлением потоком в цикле и создавать предварительно упорядоченный результат. Поскольку order может быть использован только для левого потока в соединении, любое правило, которое влияет на упоря- доченность соединения — например, внешнее соединение, которое не дает возможность пото- ку быть самым левым — будет иметь преимущество.
492 Часть И Firebird SQL JOIN PROJ_DEPT_BUDGET PDB ON P.PROJ_ID = PDB.PROJ_ID JOIN DEPARTMENT D ON PDB.DEPT_NO = D.DEPT_NO; PLAN JOIN (D NATURAL, PDB INDEX (RDB$FOREIGN18) , P INDEX (RDB$ PRIMARY12) ) Поскольку доступно множество подходящих индексов, оптимизатор выбирает метод доступа join. Индекс, связывающий поток PDB с таблицей department, будет исполь- зован для выбора потока department. При обработке результирующей реки и потока project эквисоединение между первичным ключом таблицы project и большей по размеру (потенциально) реки дает возможность формировать реку с использованием индекса первичного ключа project для выборки данных из реки. Трехстороннее соединение только с одним индексированным равенством Для этого примера мы используем неиндексированные копии таблиц project и employee для демонстрации того, как оптимизатор будет использовать доступный ин- декс, когда он может применять лучший вариант условий неиндексированного экви- соединения: SQL> SELECT Pl.PROJ_NAME, Dl.DEPARTMENT, PDB.PROJECTED_BUDGET FROM PROJECT1 Pl JOIN PROJ_DEPT_BUDGET PDB ON Pl.PROJ_ID = PDB.PROJ_ID JOIN DEPARTMENTl Dl ON PDB.DEPT_NO = Dl. DEPT_NO; PLAN MERGE (SORT (Pl NATURAL), SORT (JOIN (Dl NATURAL, PDB INDEX (RDB$FOREIGN18)))) В самом внутреннем цикле выбран индекс внешнего ключа для эквисоединения по- тока PDB и выбираемых подходящих строк потока department. Заметьте, что выбор такого индекса ничего не выполняет с внешним ключом, для поддержки которого был создан индекс, поскольку таблица, на которую ссылается внешний ключ, даже не присутствует в операторе. После этого результирующая река и поток project сортируются. В завершение (в са- мом внешнем цикле) два сортированных потока объединяются в один. Запросы с множеством планов Когда в запросе задаются подзапросы и объединения, используется несколько опера- торов select. Оптимизатор конструирует независимый план для каждого оператора select. Возьмем следующий пример: SELECT P.PROJ_NAME, (SELECT E.FULL_NAME FROM EMPLOYEE E WHERE P . TEAM_LEADER = E.EMP_NO) AS LEADER^ NAME FROM PROJECT P WHERE P.PRODUCT = 'software1;
Гпава 22. Запросы к множеству таблиц 493 PLAN (Е INDEX (RDB$ PRIMARY?) ) PLAN (P INDEX (PRODTYPEX)) Первый план выбирает индекс первичного ключа таблицы employee для просмотра кодов team_leader в первичной таблице подзапроса. Индекс prodtypex для таблицы project используется для фильтрации строк в таблице product, поскольку первым элементом ключа в этом индексе является столбец product. Интересно то, что если изменить тот же запрос, включив предложение упорядочения, то оптимизатор изменит свой выбор индекса для фильтрации и выберет уникальный индекс по proj_name для навигации по упорядочиваемому столбцу: SELECT P.PROJ_NAME, (SELECT E.FULL_NAME FROM EMPLOYEE E WHERE P.TEAM_LEADER = E.EMP_NO) AS LEADER__NAME FROM PROJECT P WHERE P.PRODUCT = 'software' ORDER BY 1; PLAN (E INDEX (RDB$PRIMARY?)) PLAN (P ORDER RDB$11) Задание вашего собственного плана Синтаксис выражений, который использует оптимизатор для создания плана и пере- дачи его серверу Firebird доступен в SQL в предложении plan. Это позволяет вам оп- ределять ваш собственный план, ограничивая оптимизатор в его выборе. Предложение plan может быть задано почти в любом операторе select, включая опе- раторы, используемые в создании просмотров, в хранимых процедурах и подзапро- сах. Firebird версии 1.5 и выше также допускает предложения plan и в триггерах. Множество планов может быть указано независимо для запроса и любого подзапро- са. При этом нет требования "все или ничего" — любое предложение плана является необязательным. Предложение plan генерируется для оператора select в хранимой процедуре выбора. Поскольку выходом хранимой процедуры выбора является виртуальный набор, лю- бые условия будут основываться на доступе natural. При этом любой оператор select в хранимой процедуре будет оптимизирован, и для него можно применять пользова- тельский план. Jg ПРИМЕЧАНИЕ. Конструирование пользовательского плана для оператора -р, select в просмотре создает собственные проблемы для разработчика. Более К подробную информацию см. в разд. "Использование планов запросов для про- смотров" главы 24. Вы должны задать имена и порядок использования для всех индексов, которые будут использованы.
494 Часть V. Firebird SQL Оптимизатор всегда создает план, даже если задан пользовательский план. Хотя оп- тимизатор не мешает созданному пользователем плану, он проверяет, какие индексы подходят для данного контекста. Альтернативные пути отбрасываются, но во всем остальном дела идут своим чередом. Таким образом, наличие пользовательского плана не отключает оптимизатор, и он все равно делает свою оценку и генерацию своего плана в тех аспектах, которые не были указаны в предложении plan. Неверный индекс приведет к тому, что запрос не будет выполнен. Если любой пре- дикат или условие соединения остаются после того, как все индексы, указанные в плане выражения, будут использованы, оптимизатор просто оценит потоки в соот- ветствии с естественным порядком и порядком сортировки по умолчанию. Представляя ваше собственное выражение плана, вы можете получить небольшое увеличение скорости за счет обхода работы оптимизатора. При этом разработка ва- шего собственного плана, основанная на структурных правилах управления вашими данными, может не дать ожидаемых вами удовлетворительных результатов, особен- но если ваш план наследован от другой СУБД, которая использует структурные пра- вила для оптимизации запросов. Оптимизатор Firebird основан на стоимости (cost-based) и обычно создает лучший план, если ваша база данных хорошо поддерживается сервисными средствами. По- скольку геометрия индексов и данных может изменяться в процессе выполнения операторов — особенно, если изменяется или удаляется большое количество строк— никакой сгенерированный оптимизатором план не может оставаться статич- ным от одной подготовки запроса к другой. Если вы создаете статичное выражение plan, то ухудшение эффективности может стать результатом снижения производи- тельности, что уберет все преимущества отмены работы оптимизатора. Смысл сказанного здесь в том, что использование вашего собственного плана может оказаться палкой о двух концах. В процессе разработки вы можете чувствовать пре- имущество от использования плана, который, как вы верите, является лучшим, чем тот, который будет получен оптимизатором. В этом случае вы отключаете преиму- щества динамического оптимизатора, который может компенсировать последующие изменения в распределении данных или индексов. Улучшение плана запроса Скверно выполняющиеся запросы в Firebird наиболее часто являются следствием плохого задания индексов и неоптимальных запросов. В разд. "Тема оптимизации" главы 18 мы рассматривали влияние индексов с плохой селективностью. В настоя- щем разделе мы продолжим рассмотрение индексов и некоторых неприятностей, ко- торые могут происходить с оптимизатором и разработчиком, когда индексы служат помехой эффективности поиска. Аккуратное индексирование Не факт, что использование индексов в соединении или поиске сделает запрос более быстрым. В действительности существуют такие структуры метаданных и виды ин-
Гпава 22. Запросы к множеству таблиц 495 дексов, при которых выбор некоторых индексов резко снизит производительность в сравнении с естественным сканированием. Дублирующиеся или перекрывающие друг друга индексы могут повлиять на исполь- зование оптимизатором других индексов. Удалите все индексы, которые дублируют автоматически создаваемые индексы для первичных или внешних ключей или для ограничений unique. Пары составных первичных и внешних ключей, особенно длин- ных или несущих смысловое содержание, могут сделать запросы уязвимыми с точки зрения производительности, привести к неправильному выбору индексов и к нема- лым человеческим ошибкам. При проектировании таблиц рассматривайте использо- вание суррогатных ключей для отделения "осмысленных" столбцов для поиска и упорядочения от формального ключа для соединений. Составные индексы Составные (сложные) индексы — это индексы, которые содержат более одного столбца. Хорошо продуманный составной индекс может увеличить скорость запро- сов и поиска в сложных конъюнктивных запросах (использующих логический опера- тор and), особенно для больших таблиц или при низкой собственной селективности отыскиваемого столбца. В составных индексах важным является ранг (количество элементов и относительная позиция слева направо). Оптимизатор может использовать один столбец составного индекса или подгруппу столбцов в операциях поиска, соединения или упорядочения, не включая оставшиеся столбцы индекса в операции. На рис. 22.8 представлены дос- тупные для оптимизатора возможности по использованию составного индекса (соы, COL2, COL3). ON | WHERE | OR COL1 = <constant> ON | WHERE COL1 =<const1> AND COL2 = <const2> (but not OR COL2 =<constant> not OR COL3 =<constant>) ON | WHERE COL1 =<const1> AND COL2 = <const2> AND COL3 = <const3> Рис. 22.8. Возможности частичного индексного ключа (but not OR COL2 =<constant> not OR COL3 =<constant>) Если составной индекс может быть выгодно использован на месте одного или более индексов, состоящих из одного столбца, то имеет смысл создавать и тестировать та- кой индекс. Не существует преимущества в создании составных индексов для дизъ- юнктивных условий (использующих логический оператор ок) — они не будут ис- пользованы. Не поддавайтесь искушению создать составной индекс в надежде, что
496 Часть V. Firebird SQL "один индекс подойдет во всех случаях". Создавайте индексы для определенных по- требностей и будьте готовы уничтожить любой из них, который не будет работать хорошо. На практике основным является рассмотрение полезности составных индексов с точ- ки зрения наиболее вероятных потребностей вывода. Чем больше вы будете порож- дать избыточных составных индексов, тем более вероятность того, что вы будете получать неоптимальные планы. Элементы ключей в составных индексах часто пере- крывают те же элементы в других индексах, заставляя оптимизатор выбирать между двумя или более конкурирующими индексами. Оптимизатор не может гарантировать выбор лучшего из индексов во всех случаях, он может даже решить вовсе не исполь- зовать индексы в случаях, когда он не может выбрать лучший из них. Убедитесь в правильности ваших предположений по поводу эффективности добавле- ния составного индекса путем тестирования не только одного запроса, а также всех регулярных или больших задач поиска, которые включают один или более тех самых столбцов8. СОВЕТ. Не будет плохой идеей сохранить все индексные структуры, которые вы тестировали, вместе с записями об их эффектах. Это поможет снизить накал страстей в тестирующей лаборатории! Индексы из одного столбца Индексы из одного столбца являются гораздо более гибкими, чем составные индек- сы. Они являются предпочтительными для всех условий, которые не имеют особых потребностей в составных индексах, они также нужны для каждого отыскиваемого столбца, который является частью дизъюнктивного условия (содержащего логиче- скую операцию ок). Естественный порядок Не расстраивайтесь, увидев natural в плане запроса, и не бойтесь его использовать при тестировании ваших планов. Естественный (natural) порядок задает сканирование таблицы сверху вниз, страница за страницей. Зачастую это бывает быстрее для неко- торых данных, особенно при нахождении соответствия и поиске по столбцам, для которых индекс имеет очень низкую селективность, а также для статичных таблиц с небольшим количеством строк. 8 Оптимизатор InterBase, Firebird 1.0 и 1.5 считает составные индексы более селективными, чем они есть на самом деле. В этом виновата особенность подсчета селективности, которая производится целиком для всего ключа. В Firebird 2.0 селективность составных индексов учи- тывается отдельно по каждому сегменту составного ключа, что позволяет избежать сильных проколов в оптимизации запросов. — Прим. науч. ред.
Гпава 22. Запросы к множеству таблиц 497 Выбор порядка соединения При соединении двух или более потоков порядок, в котором эти потоки отыскивают- ся, бывает более важным, чем все другие факторы вместе взятые. Потоки извлекают- ся в порядке слева направо и являются, вместе с крайним левым потоком конкретно- го (парного) объединения, "важнейшим контроллером", который определяет логику поиска для всех остальных соединений и слияний, связанных с ним. В идеале этот управляющий поток отыскивается один раз. Потоки, которые соеди- няются с ним, отыскиваются итеративно, пока не будут найдены все соответствую- щие строки. Отсюда следует, что поток, имеющий самую высокую стоимость поиска, должен быть помещен слева от "дешевого" потока в управляющей позиции. Послед- ний поток в списке соединения будет просматриваться множество раз — убедитесь, что это будет самый дешевый по поиску поток. Обман оптимизатора В ситуациях, когда оптимизатор выбирает индекс, который вы хотели бы проигнори- ровать, вы можете обмануть его, добавив пустое условие ок. Для простого примера предположим, что у вас есть предложение where, задающее столбец с очень низкой селективностью индекса, который вы хотели бы сохранить, например, для поддержа- ния ссылочной целостности; SELECT ... WHERE PARENTJSOUNTRY = 'AU' Если эта база данных распространяется в Австралии (код страны 'AU'), то селектив- ность parent_country будет настолько низкой, что полностью обрушит производи- тельность, однако вы привязаны к обязательному индексу. Чтобы оптимизатор про- игнорировал индекс, измените предикат поиска на9: SELECT ... WHERE PARENTJCOUNTRY = 'AU' OR 1=1 Использование интуиции Получение "картины" соединения не является точной наукой, в процессе работы вы получите интуитивное понимание создания спецификаций соединений. Однако с этой техникой борются многие люди. Для всех спецификаций запросов, особенно для запросов к множеству таблиц, секрет заключается в добросовестном их проектиро- 9 Подобное дополнительное условие может привести к тому, что вообще никакие индексы не будут использоваться. Более правильным в случае множества условий поиска является "от- ключение" низкоселективного индекса для отдельного столбца путем превращения условия сравнения в вычисляемое: для числовых столбцов — where field+0 = value, для строко- вых — where field | I ' ' = value. — Прим. науч. ped.
498 Часть И Firebird SQL вании. Если у вас мало опыта в SQL, не полагайтесь на инструменты case или утили- ты конструирования запросов, чтобы научиться их создавать. Это классическая си- туация: пока вы не приобретете опыт, вы не сможете оценить, насколько глупы дан- ные инструменты. Если вы всегда полагаетесь на тупые инструменты, вы никогда не приобретете интуицию. Ознакомьтесь с характеристиками производительности, являющимися результатом структур и содержимого ваших данных. Поймите нормализацию вашей базы данных и распознайте самый короткий путь от ваших таблиц к вашим выходным специфика- циям. Используйте карандаш и бумагу для отображения и формирования списка со- единений и условий поиска, чтобы они точно и без потерь соответствовали специфи- кациям вывода. Пора дальше Выходные наборы чаще всего не будут полезны конечным пользователям, если они будут упорядочены или сгруппированы малоосмысленными способами. В следую- щей главе рассматриваются возможности SQL по упорядочиванию наборов и по агрегированию промежуточных выходных наборов в объединенные или статистиче- ские группы.
ГЛАВА 23 Упорядоченные и агрегатные наборы В этой главе мы рассмотрим синтаксис и правила задания запросов, которые выводят упорядоченные и сгруппированные наборы. Наборы, заданные оператором set, по умолчанию читаются в произвольном порядке. Часто, особенно при отображении данных для пользователя или при печати отчетов, вам нужен определенный вид сортировки. Ясно, что список телефонов будет более полезным, если фамилии будут находиться в алфавитном порядке. Группы чисел по продажам или тестовые результаты более понятны, если они упорядочены, сгруппи- рованы и просуммированы. В SQL существует два предложения по заданию форми- рования выходных наборов. Предложение order by используется для сортировки наборов в возрастающем или убывающем порядке по одному или большему количеству столбцов. Предложение group by может разделять набор на вложенные группы, или уровни, в соответствии со столбцами из списка select и (по желанию) выполняя агрегатные вычисления на множестве числовых столбцов в границах группы. Обсуждение сортировки Хотя упорядочение и агрегирование являются операциями с различными результата- ми, на некотором уровне они взаимодействуют, когда обе используются в запросе и расположение их предложений является важным. Внутри системы они применяют некоторые общие характеристики в отношении формирования промежуточных набо- ров и использования индексов. Задание порядка в предложениях сортировки Следующая упрощенная структура синтаксиса оператора select показывает позицию предложений order by и group by в спецификациях упорядочения или группирования. Оба предложения являются необязательными и оба могут присутствовать в опера- торе: SELECT [FIRST m] [SKIP N] I [DISTINCT | ALL ] {<список-столбцов>} FROM <спецификация-таблицы> [WHERE <услсвие-поиска>]
500 Часть V. Firebird SQL [GROUP BY (элемент-группировка» [COLLATE последователыюсть-сортировки] [, <элемент-группировки> [COLLATE последовательность-сортировки ]...] [HAVING <условие-поиска>] [UNION [ALL] <вьражение-выбора>] [PLAN (выражение-плана>] [ORDER BY (список-сортируемых-элементов>'1 [FOR UPDATE [OF столбец [, столбец ...]] [WITH LOCK]]; Временное пространство сортировки Запросы с предложениями ordre by и group by "паркуют" промежуточные наборы для дальнейших операций сортировки во временном хранилище. Рекомендуется иметь доступную память, приблизительно в 2.5 раза превышающую размер самой большой таблицы, которую вы будет сортировать. Версии Firebird 1.5 и выше могут конфигурировать память для сортировки в RAM; всем версиям нужно иметь времен- ное дисковое пространство для использования в этих операциях. Оперативная память для сортировки В версии 1.5 и выше по умолчанию устанавливается размер блока памяти сортировки в 1 Мбайт. Это размер каждого участка памяти RAM, который выделяется сервером вплоть до максимального значения по умолчанию 64 Мбайт для Суперсервера и 8 Мбайт для Классического сервера. Оба эти значения могут быть сконфигурированы с помощью параметров конфигурации sortMemBiocksize и sortMemupperLimit, соответ- ственно, в файле firebird.conf. Д ВНИМАНИЕ! Не слишком увеличивайте ресурсы памяти для кэша сортировки на машине с Классическим сервером. Поскольку Классический сервер порождает отдельный серверный процесс для каждого соединения, слишком большое зна- чение верхней границы приведет к огромному размеру потребляемой памяти RAM в сильно загруженной системе. Место на диске для сортировки Если на диске не выделена память для сортировки, то сервер будет сохранять файлы сортировки в каталоге /tmp файловой системы POSIX или в каталоге, указанном пе- ременными окружения тмр и/или temp в Windows. Вы можете явно сконфигурировать пространство для сортировки двумя способами. Первый заключается в установлении каталога с использованием переменной окруже- ния firebirdjtmp (interbasejtmp для версии 1.0.x). Второй способ— сконфигуриро- вать каталоги с использованием параметра конфигурации TempDirectories в firebird.config для версии 1.5 и выше или при добавлении одной или более записей temp_directory В файл isc config (POSIX) или ibconfig (Windows) для версии 1.0.x. Инсталляция по умолчанию не выполняет никакого явного конфигурирования разме- ра памяти сортировки на диске. Подробности и синтаксис таких установок см. в разд. "Параметры для конфигурирования памяти сортировки" главы 36.
Гпава 23. Упорядоченные и агрегатные наборы 501 Индексирование Упорядоченные наборы являются дорогостоящими для ресурсов сервера и вообще для производительности. При оценке запроса и определении плана оптимизатор мо- жет выбирать между тремя методами доступа к наборам данных (называемым пото- ками), которые располагаются в указанных таблицах: natural (поиск без какого-либо порядка), index (использование индекса для управления поиском) и merge (создание двоичных образов двух потоков и их слияние по принципу один к одному). Когда набор читается в порядке индекса, порядок чтения является неестественным (т. е. не в порядке хранения). Вероятность того, что индексное чтение будет вовле- кать в обработку множество страниц, очень высока, а стоимость растет с ростом раз- мера таблицы. В худшем случае количество операций ввода/вывода будет увеличи- ваться. При методе merge (также называемый sort) каждая строка (а следовательно, и каждая страница) читается только один раз, а чтение выполняется в порядке хранения. В Firebird 1.5, который с большой вероятностью будет использовать оперативную память для сортировки, метод merge является наиболее быстрым в большинстве слу- чаев. Следует еще раз подчеркнуть важность хорошего индексирования для ускорения и улучшения процесса сортировки в упорядоченных запросах. В упорядоченных набо- рах при правильных условиях метод index с хорошим индексом быстро выдаст пер- вые строки. При этом индекс также может значительно замедлить выборку. Затраты могут быть такими, что весь набор будет выбран гораздо медленнее, чем при исполь- зовании альтернативного метода доступа merge. В методе merge первая строка оты- скивается медленнее, но весь набор выбирается быстрее. То же самое соотношение необязательно должно применяться к группируемым за- просам, поскольку они требуют полной выборки набора на сервере, прежде чем стро- ка может быть выведена. В условиях группирования может случиться так, что индекс ухудшит, а не улучшит производительность. Предложение ORDER BY Предложение order by используется для сортировки выходного набора запроса select, оно также допустимо для любого оператора select, который способен оты- скивать множество строк для вывода. Оно помещается после всех других предложе- ний, за исключением предложения for update (если присутствует) или предложения into (в хранимой процедуре). Общая форма синтаксиса предложения order by: ORDER BY <список-элементов~сортировки> <список-элементов-сортировки> = <элемент-сортировки> [, <элемент-сортировки>]
502 Часть I/. Firebird SQL <элемент-сортировки> = <столбец> | <выражение> | <номер-позиции> [COLLATE <порядок~сортировкй>] ASC[ENDING] | DESC[ENDING] [NULLS LAST | NULLS FIRST] Элементы сортировки Разделенный запятыми <список-элементов-сортировкгг> определяет вертикальный по- рядок сортировки строк. Порядок сортировки по каждому элементу сортировки мо- жет быть возрастающим (asc) или убывающим (desc). Возрастающий порядок являет- ся порядком по умолчанию и не обязательно должен быть указан. Firebird 1.5 поддерживает размещение значений null, если присутствуют, в элементе сортировки. Значение по умолчанию, nulls last, означает, что строки null будут по- мещены в конец списка. Задавайте nulls first для размещения null перед всеми дру- гими значениями. Когда присутствует несколько элементов сортировки, то горизонтальный порядок элементов в предложении является значимым — сортировка выполняется слева на- право. Столбцы Элементами сортировки обычно являются столбцы. Индексированные столбцы сор- тируются много быстрее, чем неиндексированные. Если сортировка использует мно- жество столбцов в непрерывной последовательности слева направо, то составной индекс, созданный из таких сортируемых элементов в той же последовательности слева направо с соответствующим направлением сортировки (asc/desc), может резко повысить скорость сортировки. В запросах union и group by столбец, используемый для сортировки, должен присут- ствовать в выходном списке. В других запросах невыводимые столбцы и (в вер- сии 1.5) выражения, содержащие невыводимые столбцы, допустимы для критерия упорядочения. Оператор в примере 23.1 показывает сортировку по столбцам в простейшей форме. Пример 23.1. Простейшая сортировка по столбцам SELECT COLA, COLB, COLC, COLD FROM TABLEA ORDER BY COLA, COLD; Использование алиасов таблиц При спецификации упорядоченного набора, который использует соединение или коррелированные подзапросы, вы должны указывать полные идентификаторы для всех элементов сортировки, являющихся столбцами базы данных, или обращаться к столбцам базы данных, используя идентификаторы таблиц или алиасы, которые были заданы в предложениях from и join, как показано в примере 23.2.
Гпава 23. Упорядоченные и агрегатные наборы 503 i Пример 23.2, Алиасы таблиц в предложении упорядочения SELECT A.COLA, A.COLB, B.COL2, B.C0L3 FROM TABLEA А JOIN TABLEB В ON A.COLA = B.COL1 WHERE A.COLX = 'Sold' ORDER BY A.COLA, B.COL3; Выражения В Firebird 1.5 и выше правильные выражения допустимы в качестве элементов сорти- ровки, даже если выражение не выводится в виде выходного столбца. Вы можете сортировать наборы по выражениям внутренних и внешних функций или скалярным значениям коррелированных подзапросов (пример 23.3). Если столбец выражения, по которому вы хотите выполнять сортировку, присутствует в выходном списке, вы не можете использовать его имя алиаса в качестве элемента упорядочения. Или повто- рите выражение в предложении order by, или используйте порядковый номер столб- ца (см. разд. "Порядковые номера"). Пример 23.3. Упорядочение по полю выражения SELECT EMP__NO И | | SUBSTRING (LAST_NAME FROM 1 FOR 3) AS NAMECODE FROM EMPLOYEE ORDER BY EMP_NO || | | SUBSTRING (LASTJIAME FROM 1 FOR 3); Альтернативный способ — замена выражения в предложении упорядочения на по- рядковый номер поля выражения: SELECT EMP_NO I I H SUBSTRING (LAST_NAME FROM 1 FOR 3) AS NAMECODE FROM EMPLOYEE ORDER BY 1; Если вам нужна сортировка по выражению в версии 1.0.x, необходимо включить вы- ражение в выходной список и использовать его порядковый номер в предложении ORDER BY. Сортировка по выражениям процедур или функций Сортировка по значениям, возвращаемым обычными или внешними функциями или хранимыми процедурами, похожа на другие виды сортировки: она будет выполнять- ся в соответствии с обычными правилами для типов данных возвращаемых значений. Вы ответственны за указание сортировки только по значениям, которые соответст- вуют некоторой логической последовательности. В качестве примера неправильной последовательности рассмотрим следующее: ORDER BY CAST (SALES_DATE AS VARCHAR(24)) Строки будут упорядочены в алфавитно-цифровой последовательности символов без соответствия датам, с которыми оперирует это выражение.
504 Часть I/. Firebird SQL Д ВНИМАНИЕ! При использовании выражений в качестве критериев упорядочения помните о значительном расходовании ресурсов на сервере при дублировании вычисления выражения для каждой найденной строки. Влияние этого будет в лучшем случае непредсказуемым. Умеренно используйте в этом типе операции количество наборов. Порядковые номера Особый вид выражения, который может быть задан в качестве критерия сортировки, является порядковым номером в списке выходных столбцов. Порядковый номер яв- ляется позицией столбца в выходном списке, подсчитываемой слева направо, начи- ная с единицы. В примере 23.4 снова используется простой запрос из примера 23.1, где используются порядковые номера вместо идентификаторов столбцов. Пример 23.4. Подстановка порядковых номеров вместо имен столбцов SELECT COLA, COLB, COLC, COLD FROM TABLEA ORDER BY 1, 4; Предложение упорядочения для сортировки выхода запроса объединения (union) мо- жет использовать только порядковые номера для ссылки на столбцы упорядочива- ния, как показано в примере 23.5. . Пример 23.5. Упорядочивание наборов union SELECT T.FIRST_NAME, T.LAST_NAME, 'YES' AS "TEAM LEADER?" FROM EMPLOYEE T WHERE EXISTS(SELECT 1 FROM PROJECT Pl WHERE Pl.TEAM_LEADER = T.EMP-NO) UNION SELECT E. FIRST_NAME, E.LAST_NAME, 'NO ' AS "TEAM LEADER?" FROM EMPLOYEE E WHERE NOT EXISTS(SELECT 1 FROM PROJECT P2 WHERE P2.TEAM-LEADER = E.EMP_NO) ORDER BY 2, 1; В версии 1.0.x упорядочивание по порядковым номерам является единственным спо- собом использования выражений в качестве критерия сортировки, а столбцы, полу- ченные из выражений, должны быть представлены в спецификации вывода (см. при- мер 23.6). В версии 1.5 у вас есть вариант использования выражения в качестве эле- мента сортировки без включения элемента выражения в выходной набор, как показано в примере 23.7.
Глава 23. Упорядоченные и агрегатные наборы 505 i Пример 23.6. Любая версия SELECT FULL__NAME, STRLEN (FULLAN АМЕ) FROM EMPLOYEE ORDER BY 2 DESC; • Пример 23.7. Firebird 1.5 и выше SELECT EULL_NAME FROM EMPLOYEE ORDER BY STRLEN(FULL_NAME) DESC; Использование порядкового номера полезно для сохранения контроля типов данных и устранения помех, когда выходной набор включает соединения, поскольку все столбцы базы данных, объявленные как критерий упорядочения, должны иметь пол- ностью определенные идентификаторы. Сортировка по порядковому номеру не увеличивает скорость выполнения запроса; сервер заново вычисляет выражения для операции сортировки. Направление сортировки По умолчанию сортировка выполняется в возрастающем порядке. Для выполнения сортировки в убывающем порядке добавьте ключевое слово desc, как показано в примере 23.8. Если вам нужна убывающая сортировка, создайте для нее убывающий индекс. Firebird не может "инвертировать” возрастающий индекс и использовать его для убывающей сортировки, так же как он не может использовать убывающий ин- декс для возрастающей сортировки. : Пример 23.8. Убывающий порядок сортировки SELECT FIRST 1U * FROM DOCUMENT ORDER BY STRLEN(DESCRIPTION) DESC Размещение пустых значений По умолчанию Firebird помещает сортируемые столбцы, имеющие null, в конец вы- ходного набора. Начиная с версии 1.5, вы можете использовать ключевое слово nulls first для указания того, что строки с null будут помещаться в начало набора, как показано в примере 23.9. ПРИМЕЧАНИЕ. Никакой индекс не может быть использован для сортируемого элемента, если указано nulls first.
506 Часть V. Firebird SQL Пример 23.9. Размещение пустого значения SELECT * IRuM EMPLOYЕЕ ORDER BY PHONE_EXT NULLS FIRST Предложение GROUP BY Когда запрос включает предложение group by, to спецификация столбца и таблицы, а именно: SELECT {<список-столбцов>} FROM <спецификация-таблицы> [WHERE <условие-поиска>] передается следующей стадии обработки, где строки разделяются на одну или более вложенных групп. Обычно каждая часть создает некоторый итог, используя выражения, которые вы- полняют некоторые операции над группой числовых значений. Такой тип запроса называется группирующим запросом, а его результат часто называется сгруппирован- ным набором. Его синтаксис: SELECT <список-труппируемых-полей> FROM <спецификация-таблицы> [WHERE...] GROUP BY <труппирующ1й-элемент> [COLLATE последовательность-сортировки] [, <труппируюшцй-элемент> [.. ] ] HAVING <предикат-группируеыых-столбцов> [ORDER BY .. .] ; Список группируемых полей Группа формируется путем объединения (агрегирования) всех строк, где столбец, указанный в списке столбцов и в предложении group by, имеет общее значение. Ло- гика агрегирования означает, что заданный в select список полей группирующего запроса резко ограничен полями, указанными в качестве аргументов в предложении group by. Поля, имеющие спецификации, соответствующие этим требованиям, часто называются группируемыми. Если вы задаете столбец или выражение поля, которые не являются группируемыми, то запрос будет отменен. ♦ Столбец базы данных или неагрегатное выражение не могут быть указаны в спи- ске столбцов, если они не указаны в предложении group by. ♦ Агрегатное выражение, оперирующее со столбцом базы данных, который не на- ходится в предложении group by, может быть включено в список столбцов. Строго рекомендуется использовать алиас для результата такого выражения.
Глава 23. Упорядоченные и агрегатные наборы 507 ВНИМАНИЕ! В Firebird 1.0.x и InterBase требования к допустимости группирова- ния являются менее ограничивающими. Группирующие запросы и клиентские вызовы хранимых процедур могут вызывать исключения, которые ранее не появ- лялись. Это давняя ошибка, о которой вы должны знать при миграции ваших ста- рых приложений в Firebird версии 1.5 и выше. Агрегатные выражения В Firebird существует группа агрегатных (обобщающих) функций, которые обычно используются вместе с условиями группирования для вычисления итогов и статисти- ки на уровне группы. Агрегатными функциями являются зимо, которая вычисляет итоги, min о и махо, отыскивающие наименьшее и наибольшее значение соответственно, и avgo, вычис- ляющая среднее значение. Функция count () также ведет себя как агрегатная функция в группирующих запросах, возвращая счетчик строк для всех строк ниже контекста (уровня)группы. В отличие от других группируемых элементов агрегатное выражение в списке select не может быть использовано как элемент группы (см. разд. "Элемент группирова- ния"), поскольку оно возвращает значение, которое вычисляется из значений на ниж- нем уровне группы. Таблица proJ—dept_budget содержит строки пересечения проектов и отделов. Мы за- интересованы в отыскании итога по бюджетам, выделенным каждому проекту, неза- висимо от отдела. Следующий список элементов, который рассматривался ранее в этом разделе, задает список полей из двух нужных нам элементов: SELECT PROJ__ID, SUM (PROJECTED_BUDGET) AS TOTAL_BUDGET FROM PROJ—DEPT_BUDGET WHERE FISCAL_YEAR = 1994 GROUP BY PROJ—ID; Эти две спецификации поля хороши в качестве группируемых элементов. Идентифи- катор отдела (dept_no) не присутствует в списке, потому что список задает нужные нам по проекту итоги. Для получения этих итогов мы используем аргумент proj_id в предложении group by. С другой стороны, если мы хотим получить список бюджетов отделов, независимых от проектов, список полей должен включать dept_no, чтобы он был аргументом в предложении group by: SELECT DEPT_NO, SUM(PROJECTED-BUDGET) AS TOTAL_BUDGET FROM PROJ—DEPT—BUDGET WHERE FISCAL—YEAR = 1994 GROUP BY DEPT_NO;
508 Часть V. Firebird SQL Влияние NULL на агрегатные выражения В агрегатных выражениях типа sumo, avgq и count(<имя-столбца>) строки, содержа- щие null в соответствующем столбце, игнорируются. Функция avgo создает числи- тель, суммируя все непустые значения, и знаменатель, подсчитывая строки, содер- жащие непустые значения. ПРИМЕЧАНИЕ. Если у вас есть столбцы, по которым вы собираетесь вычислять среднее значение, важно решить в процессе проектирования, как при вычислении среднего значения вы будете трактовать "пустые" экземпляры — как null (они будут исключены из вычисления) или как ноль. Вы можете реализовать нужное правило, используя значение по умолчанию или (лучше) триггер before insert1. Группирующий элемент Предложение group by получает список группирующих элементов'. ♦ в Firebird 1.0.т группирующим элементом может быть только имя столбца или соответствующее выражение внешней функции (UDF); ♦ Firebird 1.5 расширил диапазон группирующих элементов, добавив также номер, порядковое числительное, представляющее номер позиции (слева направо) соот- ветствующего элемента из списка полей select. Этот номер может быть использо- ван вместе с существующей возможностью для аргументов order by; ♦ версии Firebird 1.5 и более поздние также имеют возможность группировать по большинству функциональных выражений, таких как casto. extracto. SUBSTRING(), UPPER(), CASE() И COALESCE(). Следующий оператор завершает создание запроса, начатого в предыдущем примере: SELECT PROJ_ID, SUM(PROJECTED_BUDGET) AS TOTAL_BUDGET FROM PROJ_DEPT_BUDGET WHERE FISCAL_YEAR = 1994 GROUP BY PROJ-ID; PROJ-ID TOTAL—BUDGET GUIDE 650000.00 HWRII 520000.00 MAPDB 111000.00 MKTPR 1480000.00 VBASE 2600000.00 В этом случае также желателен и триггер before update. — Прим, перев.
Глава 23. Упорядоченные и агрегатные наборы 509 Ограничение Группирующим элементом не может быть выражение, включающее агрегатную функцию, такую как avg (), sum (), мах о, mini) или count о — они выполняют обобще- ние в том же группирующем контексте (на том же уровне), что и группирующий элемент. Это ограничение включает любые агрегатные выражения, которые встроены внутрь другого выражения. Например, синтаксический анализатор DSQL будет "ру- гаться", если вы попытаетесь сделать следующее: SELECT PROJ_ID, SUM(PROJECTED_BUDGET) AS TOTAL_BUDGET FROM PROJ_DEPT_BUDGET WHERE FISCALJfEAR = 1994 GROUP BY 2; ISC ERROR CODE:335544569 Cannot use an aggregate function in a GROUP BY clause (Нельзя использовать агрегатную функцию в предложении GROUP BY) Использование COUNT()b качестве агрегатной функции Очень "вредная" функция count о используется разумно в группирующем запросе для подсчета количества для групп. Рассмотрим следующую модификацию нашего примера. Столбец dept_no в наших требованиях не является подходящим кандидатом ни для группирующего, ни для группируемого элементов, однако он может дать ин- формацию о таких элементах в контексте группирования по proj_id: SELECT PROJ_ID, SUM(PROJECTED_BUDGET) AS TOTAL_BUDGET, COUNT (DEPT_NO) AS NUMJOEPARTMENTS FROM PROJ_DEPT_BUDGET WHERE FISCAL-YEAR = 1994 GROUP BY PROJ_ID; PROJJD TOTAL_BUDGET NUM_DEPARTMENTS GUIDE 650000.00 2 HWRII 520000.00 3 MAPDB 111000.00 3 MKTPR 1480000.00 5 VBASE 2600000.00 3 Неагрегатные выражения Возможность использования для группирования выражений внутренних и внешних функций открывает широкий диапазон возможностей преобразования хранимых ат- рибутов для генерации выходных наборов, которые нельзя получить другим путем.
510 Часть V. Firebird SQL Например, внутренняя функция extract о оперирует с типами данных даты и време- ни и возвращает части даты — числа, которые выделяют год, месяц, день, час и т. д. из подобных типов данных. Следующий пример для версии 1.5 запрашивает таблицу membership и выводит статистику, показывающую количество участников, объеди- ненных в каждом месяце, независимо от года или для даты их объединения: SELECT MEMBER_TYPE, EXTRACT(MONTH FROM JOIN-DATE) AS MONTH_NUMBER, /* 1, 2, и т. Д. */ COUNT (JOIN-DATE) AS MEMBERS—JOINED FROM MEMBERSHIP GROUP BY MEMBER-TYPE, EXTRACT(MONTH FROM JOIN_DATE); Большое количество полезных функций доступно в библиотеках внешних функций для преобразования дат, строк и чисел в элементы для группирования. Следующий пример иллюстрирует группирование при использовании некоторых функций, най- денных в поставляемой библиотеке ibudf: SELECT STRLEN(RTRIM(RDBSRELATION—NAME)), COUNT(*) FROM RDB$RELATIONS GROUP BY STRLEN(RTRIM(RDB$RELATION_NAME)) ORDER BY 2; Это будет работать в любой версии Firebird. Отдельные выражения в настоящий момент недоступны в списке group by. Например, синтаксический анализатор отклоняет группирующий элемент, который содержит символ конкатенации | |. Следовательно, запрос SELECT PROJ—ID || '-1994' AS PROJECT, SUM(PROJECTED—BUDGET) AS TOTAL-BUDGET FROM PROJ-DEPT-BUDGET WHERE FISCAL—YEAR = 1994 GROUP BY PROJ—ID I I '-1994',- л Firebird 1.5 вернет следующее исключение: ISC ERROR CODE 1335544569 Token unknown - line 6, char 21 I I Использование порядкового номера поля выражения будет работать без проблем: SELECT PROJ—ID || '-1994' AS PROJECT, SUM(PROJECTED—BUDGET) AS TOTAL-BUDGET FROM PROJ-DEPT-BUDGET WHERE FISCAL—YEAR = 1994 GROUP BY 1;
Глава 23. Упорядоченные и агрегатные наборы 511 Группирование по порядковому номеру Использование порядкового номера выходного столбца в предложении group by "ко- пирует" выражение из списка выбора (как это делает и предложение order by). Это означает, что когда порядковый номер указывает на подзапрос, этот подзапрос будет выполняться, по меньшей мере, дважды. Подвыражение HAVING Подвыражение having является фильтром для сгруппированного вывода, которое работает аналогично тому, как предложение where фильтрует несгруппированный набор. Условие having использует в точности тот же синтаксис предикатов, что и условие where, но его не следует путать с предложением where. Фильтр having применяется к группам после того, как набор будет разделен. Вам может еще понадобиться предло- жение where для фильтрации входного набора. В Firebird 1.0.x вы можете задать условие having с использованием столбцов, которые не включены в состав группируемых элементов — "ленивое предложение where" и "дефект" в терминах стандартных соглашений. Начиная с версии 1.5 в having могут быть использованы только группируемые элементы. Важно понимать влияние условия having на производительность. Условие обрабаты- вается после того, как выполнено группирование. Если оно используется вместо ус- ловий where для фильтрации ненужных элементов, возвращенных в группах, указан- ных в списке group by, то строки будут обрабатываться дважды только для того, что- бы уменьшить их количество. Чтобы улучшить производительность, используйте условия where для предваритель- ной фильтрации групп, a having используйте для фильтрации результатов, получен- ных от агрегатных выражений. Например, группа, полученная при использовании выражения sum(x), может быть отфильтрована с помощью having sum(x) > <минммалъ- ное-значение>. Следовательно, предложение having обычно принимает агрегатное вы- ражение в качестве своего аргумента. Взяв предыдущий запрос, мы можем использовать предложение where для фильтра- ции групп проектов, которые появляются в выходном наборе, и использовать пред- ложение having для установки начального диапазона сумм, которые мы хотим про- сматривать: SELECT PROJJED SUM(PROJECTED^BUDGET) AS TOTAL_BUDGET FROM PROJ_DEPT_BUDGET WHERE FISCAL_YEAR = 1994 AND PROJ_ID STARTING WITH 'M' GROUP BY PROJ_ID HAVING SUM(PROJECTED_BUDGET) >= 100000;
512 Часть К Firebird SQL PROJJD TOTAL_BUDGET MAPDB 111000.00 MKTPR 1480000.00 Предложение having может принимать сложные аргументы, содержащие логические операции and и or, которые используют ту же логику приоритетов, что и предложе- ние WHERE. Подвыражение COLLATE Если вам нужно сгруппировать текстовый столбец с использованием последователь- ности сортировки, отличной от той, которая была определена по умолчанию для это- го столбца, вы можете включить предложение collate. Подробную информацию о collate см. в разд. "Последовательность сортировки"главы 77. Использование ORDER BY в группирующем запросе Начиная с Firebird версии 1.5, элементы в списке order by в группирующем запросе должны быть либо агрегатными функциями, допустимыми в контексте группирова- ния, либо элементами, которые представлены в списке group by. Firebird 1.0.x является менее ограничивающим— он допускает упорядочение по эле- ментам или выражениям, находящимся вне контекста группирования. Улучшенные условия группирования Firebird 1.5 и более поздние версии поддерживают некоторые дополнительные усло- вия группирования, недоступные в версии 1.0.x. Подзапросы во встроенных агрегатах Группируемое поле, которое связано с выражением подзапроса, может содержать агрегатное выражение, ссылающееся на элемент агрегатного выражения в списке GROUP by. В следующем примере реентерабельный подзапрос к системной таблице rdb$relation_fields содержит агрегатное выражение (MAX(r.RDB$FiELD_posiTiON)), ре- зультат которого используется для локализации имени (rdb$field_name) столбца, имеющего наибольший номер позиции для каждой таблицы (rdb$relation_name) в базе данных: SELECT г. RDB$RELATION_NAME, МАХ(г.RDB$FIELD—POSITION) AS MAXFIELDPOS, (SELECT r2.RDB$FIELD_NAME FROM RDB$RELATION_FIELDS r2
Гпава 23. Упорядоченные и агрегатные наборы 513 WHERE r2 . RDB$ RELATION _NAME = г. RDB $ RELATE ON_NAME and r2.RDB$FIELD__POSITION = MAX(r.RDB$FIELD_POSITION)) AS FIELDNAME FROM RDB$RELATION__FIELDS r /* мы используем предложение WHERE для фильтрации системных таблиц ★/ WHERE г ,RDB$RELATION_NAME NOT STARTING WITH 'RDB$' GROUP BY 1; RDB$RELATION_NAME MAXFIELDPOS FIELDNAME COUNTRY 1 CURRENCY CROSS__RATE 3 UPDATE_DATE CUSTOMER 11 ON-HOLD DEPARTMENT 6 PHONE_NO EMPLOYEE 10 FULL_NAME EMPLOYEE-PROJECT 1 PROJ_ID JOB 7 LANGUAGE_REQ PHONE—LIST 5 PHONE_NO PROJECT 4 PRODUCT PROJ_DEPT_BUDGET 4 PROJECTED—BUDGET SALARY_HISTORY 5 NEW—SALARY SALES 12 AGED Теперь в тех же самых текстах мы используем count о для подсчета количества столбцов, хранимых в каждой таблице: SELECT rf.RDB$RELATION__NAME AS "Table Name", (SELECT r.RDB$RELATION_ID FROM RDB$RELATIONS r WHERE r.RDBSRELATION_NAME = rf. RDB$RELATION_NAME) AS ID, COUNT(*) AS "Field Count" FROM RDB$RELATION_FIELDS rf WHERE rf,RDB$RELATION_NAME NOT STARTING WITH 'RDB$' GROUP BY r f.RDB $ RELATION_NAME; Table Name ID Field Count COUNTRY 128 2 CROSS_RATE 139 4 CUSTOMER 137 12 DEPARTMENT 130 7 . . . и T. 17 Зак. 420
514 Часть И Firebird SQL Агрегаты во встроенных подзапросах Выражение агрегатной функции— count о, avgo и т. д.— может принимать аргу- мент, который является выражением подзапроса, возвращающим скалярное значе- ние. Например, далее результат запроса select count(*) передается на более высокий уровень в выражение sumo, которое для каждой таблицы (rdb$relatioN-Name) выводит произведение счетчика полей на количество индексов в таблице: SELECT г. rdbSrelation_name, SUM((SELECT COUNT(*) FROM RDB$RELATION_FIELDS rf WHERE rf.RDBSRELATION—NAME = r.RDB$RELATION_NAME)) AS "Fields * Indexes" FROM RDB$RELATIONS r JOIN RDB$INDICES i ON (i.RDB$RELATION_NAME = r.RDB$RELATION-NAME) WHERE r . RDB$RELATION_NAME NOT STARTING WITH ' RDB$ ' GROUP BY r.RDB$RELATION—NAME; RDB$RELATION-NAME Fields * Indexes COUNTRY 2 CROSS_RATE 4 CUSTOMER 48 DEPARTMENT 35 . .. и T. Д. Агрегаты на смешанных уровнях группирования Агрегатные функции для различных уровней группирования могут быть перемешаны в одном и том же группирующем запросе. В следующем примере результат выражения, полученный из подзапроса, который выполняет counto для столбца на самом низком уровне группы (rdbsindices), пере- дается на уровень группирования. Предложение having выполняет фильтрацию, оп- ределяемую двумя другими агрегатными функциями на нижнем уровне группы. SELECT г. RDB$RELATION_NAME, MAX(i.RDBSSTATISTICS) AS "Maxi", /* одно агрегатное выражение, вложенное в другое */ (SELECT COUNT!*) |( ' - ' |[ МАХ(i.RDB$STATISTICS) FROM RDB$RELATION-FIELDS rf WHERE rf.RDB$RELATION—NAME = r.RDB$RELATION_NAME) AS "Max2" FROM RDB$RELATIONS r JOIN RDB$INDICES i ON (i.RDB$RELATION_NAME = r. RDB$RELATION—NAME)
Глава 23. Упорядоченные и агрегатные наборы 515 WHERE r.RDBSRELATION_NAME NOT STARTING WITH 'RDB$' GROUP BY r. RDB$RELATION_NAME HAVING MIN(i.RDB$STATISTICS) <> MAX(i.RDB$STATISTICS); RDB$RELATION_NAME Maxi Max2 MTRANSACTION MEMBER 000000000000001 18 - 1.000000000000000 0.0135135138407 12 - 0.01351351384073496 Д ВНИМАНИЕ! Вы можете получить результат при выполнении этого запроса в Firebird 1.0.x, однако он будет некорректным. Вложенные агрегатные функции Агрегатное выражение может быть вложено внутрь другого агрегатного выражения, если внутренняя агрегатная функция находится на более низком уровне (контексте), что иллюстрируется предыдущим запросом. Пора дальше В этой главе по языку манипулирования данными мы рассмотрели возможности SQL по трансформации абстрактных данных, хранимых в таблицах, в информацию, кото- рую конечный пользователь читает в осмысленных контекстах. При некоторых усло- виях есть смысл сохранять постоянные определения полезных выходных наборов (виртуальных таблиц), чтобы не изобретать колесо каждый раз, когда требуется по- хожий набор. В следующей главе рассматриваются способы осуществления этого, в том числе и возможности просмотров.
ГЛАВА 24 Просмотры В терминологии SQL-89 и SQL-92 просмотр является стандартным типом таблицы, он также называется просматриваемой или виртуальной таблицей. Он характеризу- ется как виртуальный, потому что вместо того, чтобы хранить табличный объект и выделять страницы для хранения данных, сервер Firebird сохраняет только описание метаданных объекта. Оно содержит уникальный идентификатор, список специфика- ций столбцов и компилированный оператор select для поиска описанных в этих столбцах данных во время выполнения. Что такое просмотр? По своей природе просмотр является спецификацией таблицы, которая не хранит данные. Он действует как фильтр для столбцов и строк таблиц, на которые ссылается просмотр, — "окно", через которое просматриваются фактические данные. Запрос, определяющий просмотр, может обращаться к одной или более таблицам или к дру- гим просмотрам базы данных. Во многих случаях он ведет себя как постоянная таб- лица и инкапсулирует некоторые специальные расширения для связи с лежащими в его основе таблицами. Вы вызываете просмотр, как если бы он был обычной таблицей, выполняете соеди- нения, упорядочиваете и группируете выход, задаете условия поиска, используете в качестве подзапроса, наследуете столбцы времени выполнения из его виртуальных данных, используете выбранный из него именованный или неименованный курсор и т. д. Многие запросы могут быть "изменяемыми", позволяя изменять состояние лежащих в их основе постоянных таблиц, или они могут быть сделаны изменяемыми через триггеры. Когда подтверждаются изменения данных в таблицах, содержимое данных просмотра изменяется вместе с ними. При подтверждении изменения данных про- смотра данные лежащих в его основе таблиц изменяются соответствующим образом. Ключи и индексы Просмотры не могут иметь ключей или индексов. Лежащие в их основе таблицы, называемые базовыми таблицами, будут использованы как источник индексов при конструировании оптимизатором планов запроса. Тема плана запроса для запросов,
Глава 24. Просмотры 517 включающих просмотры, весьма запутана. Она обсуждается позже в этой главе в разд. "Использование планов запроса для просмотров". Упорядочение и группирование строк Определение просмотра не может быть упорядоченным. Добавление предложения order by вызывает исключение. Следовательно, не имеет смысла использовать в опе- раторе select ключевых слов first и/или skip, поскольку они оперируют с упорядо- ченными наборами. Спецификация группирования запроса (с использованием допустимого предложения group by) является нормальной практикой. Некоторые простые спецификации просмотров Просмотр может быть создан практически из любой спецификации запроса select. Примеры приведены в следующих разделах. Вертикальное подмножество столбцов одной таблицы Таблица job в базе данных employee.fdb имеет восемь столбцов: job_code, job_grade, JOB_COUNTRY, JOB_TITLE, MIN-SALARY, MAX_SALARY, JOB-REQUIREMENT И LANGUAGE_REQ. Следующий просмотр возвращает список диапазонов (подмножество столбцов) для всех работ (все строки) таблицы job: CREATE VIEW JOB-SALARY_RANGES AS SELECT JOB-CODE, MIN_SALARY, MAX_SALARY FROM JOB; Горизонтальное подмножество строк одной таблицы Этот просмотр возвращает только столбцы job_code и job_title только для тех ра- бот, где max_salary меньше $15 000: CREATE VIEW ENTRY_LEVEL_JOBS AS SELECT JOB_CODE, JOB-TITLE FROM JOB WHERE MAX_SALARY < 15000; Подмножество строк и столбцов для нескольких таблиц Следующий пример показывает просмотр, который соединяет таблицы job и EMPLOYEE. EMPLOYEE Содержит 1 1 Столбцов: EMP_NO, FIRST-NAME, LAST—NAME, PHONE_EXT, HIRE-DATE, DEPT-NO, JOB_CODE, JOB_GRADE, JOB-COUNTRY, SALARY И FULL_NAME. ПрОСМОТр BO3- вращает два столбца из таблицы job и два столбца из таблицы employee, фильтруя записи таким образом, что записи работников, чья зарплата составляет $15 000 и вы- ше. не попадают в выходной набор: CREATE VIEW ENTRY_LEVEL_WORKERS AS SELECT E.JOB_CODE, J. JOB_TITLE,
518 Часть V. Firebird SQL E, FIRST_NAME, E. LAST_NAME FROM JOB J JOIN EMPLOYEE E ON J.JOB_CODE = E.JOB-CODE WHERE E.SALARY < 15000; Почему просмотры могут быть полезными Требования к данным индивидуального пользователя или группы пользователей мо- гут быть довольно постоянными. Просмотры предоставляют средства для создания пользовательских версий таблиц, содержащих группы данных, подходящих для от- дельных пользователей и их задач. Перечислим преимущества просмотров. ♦ Упрощенные, повторно используемые пути доступа к данным: просмотры позво- ляют инкапсулировать подмножество данных одной или более таблиц для ис- пользования в качестве основы других запросов. ♦ Настраиваемый доступ к данным: просмотры предоставляют способ для такой адаптации выхода базы данных, что он будет ориентирован на задачу, будет под- ходить для специфических навыков и требований пользователей и будет сокра- щать объем перемещаемых по сети данных. ♦ Независимость данных: просмотры могут защитить пользовательские приложе- ния от воздействия изменений структуры базы данных. Например, если админи- стратор базы данных решает разделить одну таблицу на две, просмотр может быть создан как соединение этих двух новых таблиц. Приложения могут исполь- зовать этот просмотр, как если бы они продолжали обращаться к одной постоян- ной таблице. ♦ Безопасность данных: просмотры позволяют ограничить доступ к важным или не относящимся к делу частям таблиц. Привилегии Поскольку просмотр является объектом базы данных, он требует специальных при- вилегий для доступа к нему пользователя. Предоставляя привилегии к просмотру, можно дать пользователям очень детализированный доступ к отдельным столбцам и строкам таблиц, не давая им доступ к другим, более чувствительным данным, храни- мым в базовых таблицах. В этом случае просмотру предоставляются привилегии к таблицам, а пользователям — привилегии к этому просмотру. Привилегии владельца Пользователь, который создает просмотр, становится его владельцем. При создании просмотра пользователь должен иметь соответствующие привилегии к базовым таб- лицам. ♦ Некоторые просмотры по своей природе являются просмотрами только для чте- ния (см. разд. "Просмотры только для чтения и изменяемые"). Для формирова-
Гпава 24. Просмотры 519 ния просмотра только для чтения создателю нужны привилегии select к каждой базовой таблице. ♦ Для изменяемых просмотров создателю нужны привилегии all к базовым таб- лицам. В дополнение к этому владельцы базовых таблиц и других просмотров, к которым обращается создаваемый просмотр, должны предоставить все требуемые привилегии для доступа и модификации этих объектов через просмотр самому просмотру. То есть привилегии к этим базовым объектам должны быть предоставлены просмотру. Владелец просмотра имеет к нему все привилегии, включая возможность предостав- лять привилегии к нему другим пользователям, триггерам и хранимым процедурам. Привилегии пользователя Создатель просмотра должен предоставить соответствующие привилегии пользова- телям, хранимым процедурам и другим просмотрам, которым нужен доступ к этому просмотру. Пользователь может получить привилегии к просмотру без доступа к его базовым таблицам. В случае изменяемых просмотров привилегии insert, update и delete должны быть предоставлены всем пользователям, которым нужно выполнять операции DML над базовыми таблицами через просмотр. И наоборот, предоставляйте пользователям только привилегию select, если вы собираетесь создавать просмотр только для чтения. Если пользователь уже имеет требуемые права к базовым объектам просмотра, он автоматически будет использовать те же права к просмотру. ВНИМАНИЕ! Чем меньше привилегий предоставлено пользователю, тем боль- ше защищены объекты базы данных. При этом потенциально большое количест- во привилегий в иерархии может вызвать проблемы, если цепь будет разрушена удалением привилегий владельца просмотра. Учитывая привлекательность про- смотров в качестве механизма защиты данных от просмотра ненужными людьми, системному администратору следует поддерживать документацию по всем пре- доставленным привилегиям. Подробное описание привилегий SQL см. в главе 35. Создание просмотров Оператором DDL, определяющим запрос, который будет трансформирован в объект просмотра, является create view. Хотя он определяет таблицу (пусть и виртуальную) и позволяет употреблять пользовательские имена для объявления столбцов, его син- таксис не включает никаких определений данных для столбцов. Его структура созда- ется на основе списка столбцов оператора select и таблиц, указанных в предложении from, а также в необязательных предложениях join этого оператора.
520 Часть К Firebird SQL Все стили соединения (join) и объединения (union) наборов, которые поддерживают- ся для запросов, также поддерживаются и для просмотров. При этом не существует возможности определять просмотр, который получает выходной набор из хранимой процедуры, недопустимо также включать предложение order by. Оператор CREATE VIEW Синтаксис для create view: CREATE VIEW имя—просмотра [(имя-столбца-просмотра [, имя-столбца-просмотра [,...]])] AS <спецификация-выбора> [WITH CHECK OPTION]; Имя просмотра Имя просмотра уникально идентифицирует просмотр как объект базы данных. Имя не может быть тем же самым, что и имя другого просмотра, таблицы или хранимой процедуры. Задание имен столбцов просмотра Задание списка имен столбцов просмотра является необязательным, если не сущест- вует дубликатов имен в списке столбцов. По умолчанию будут использованы имена столбцов базовой таблицы (таблиц). В случае, когда результатом соединения является появление дубликатов имен, то здесь необходимо использовать список, который будет переименовывать столбцы. Следующий довольно безобразный пример демонстрирует, как могут появиться дуб- ликаты имен столбцов: CREATE VIEW VJOB_LISTING AS SELECT E.*, J. JOB_CODE, J.JOB_TITLE FROM EMPLOYEE E JOIN JOB J ON E.JOB_CODE = J.JOB-CODE ; ISC ERROR CODE:335544351 unsuccessful metadata update STORE RDB$RELATION-FIELDS failed attempt to store duplicate value (visible to active transactions) in unique index "RDB$INDEX_15" (ISC ERROR CODE:335544351 неудачное изменение метаданных невозможно сохранить RDB$RELATION_FIELDS
Гпава 24. Просмотры 521 попытка сохранить дубликат значения (видимый в активных транзакциях) в уникальном индексе "RDB$INDEX_15") Индекс rdb$index_15 является уникальным индексом для имени отношения и имени поля. Столбец job_code из таблицы employee уже сохранен для vjob_listing, отсюда и исключение. Необходимо именовать все столбцы в этом просмотре: CREATE VIEW VJOB_LISTING ( EMP_NO, FIRST_NAME, LASTJ4AME, PHONE_EXT, HIRE__DATE, DEPT_NO, EMP_JOB_CODE, /* альтернативное имя */ JOB_GRADE, JOB—COUNTRY, SALARY, FULL_NAME, JOB—JOB_CODE, /* альтернативное имя */ JOB_TITLE) AS SELECT E.*, J. JOB-CODE, J.JOB_TITLE FROM EMPLOYEE E JOIN JOB J ON E.JOB CODE = J.JOB_CODE ; Список также является обязательным, если список столбцов содержит какие-либо поля, полученные из выражений. Например, следующее определение будет оши- бочным: CREATE VIEW VJOB_ALTNAMES AS SELECT JOB__CODE | j 'for ' | | JOBJTITLE AS ALTNAME FROM JOB; ISC ERROR CODE:335544569 Invalid command must specify column name for view select expression (ISC ERROR CODE:335544569 Неверная команда, нужно задать имя столбца для выражения в операторе SELECT в просмотре) Следующее будет правильным: CREATE VIEW VJOB ALTNAMES (ALTNAME) AS SELECT JOB-CODE H ’ for ’ I( JOB-TITLE FROM JOB; Список имен столбцов просмотра должен соответствовать порядку и количеству столбцов, указанных в операторе select.
522 Часть 1/. Firebird SQL Задание SELECT Спецификация select— это обычный оператор select, который может включать со- единения, поля выражений, спецификации группирования и условия поиска— но не условия упорядочения. Выходной список в предложении select определяет типы, позиции и (если не заданы явно) имена столбцов просмотра. Запрос select distinct также допустим. Предложение from вместе с любыми предложениями join или подзапросами опреде- ляет базовые таблицы просмотра. ПРИМЕЧАНИЕ. Выражение select * from <отношение> допустимо, однако для просмотров не рекомендуется, если в ваших планах эффективная документируе- мость. Если используется такое выражение, то порядок следования столбцов будет соответствовать порядку в базовой таблице. Важно помнить, что вам нужно использовать предложение именования столбцов (см. разд. "Задание имен столбцов просмотра"). Вы можете включить предложение where, если вам нужно задать условия поиска. Также может быть включено предложение group by вместе с необязательным пред- ложением HAVING. Определение вычисляемых столбцов Те же самые правила, которые применяются к любым выражениям для определения полей времени выполнения в запросах, также применимы к столбцам времени вы- полнения в спецификациях просмотров. Выход просмотра почти такой же, что и вы- числяемые столбцы в таблице. При этом вычисляемый столбец имеет свои собствен- ные отличные эффекты в просмотре: ♦ он требует, чтобы обязательно присутствовал список столбцов; ♦ он делает запрос неизменяемым. Предположим, что вы хотите создать просмотр, который выводит гипотетическое увеличение на 10 процентов окладов всех служащих компании. Следующий пример создает просмотр только для чтения, который отображает всех служащих и их воз- можные новые оклады: CREATE view RAISE_BY_10 (EMPLOYEE, NEW_SALARY) AS SELECT EMP_NO, SALARY * 1.1 FROM EMPLOYEE; WITH CHECK OPTION with check option является необязательным синтаксическим элементом, используе- мым только в спецификациях просмотров. Он воздействует на изменяемые просмот-
Гпава 24. Просмотры 523 ры, которые были определены с предложением where. Это воздействие осуществляет- ся на блок любых изменяющих операций, результатом которых может быть наруше- ние условия поиска в предложении where. Предположим, вы создаете просмотр, который предоставляет доступ ко всем отделам с бюджетами от $10 000 до $500 000. Просмотр V-Subjdept может быть определен следующим образом: CREATE VIEW V_SUB_DEPT ( DEPT JI АМЕ, DEPT_NO, SUBJ9EPT—NO, LCW_BUDGET) AS SELECT DEPARTMENT, DEPT_NO, HEADJDEPT, BUDGET FROM DEPARTMENT WHERE BUDGET BETWEEN 10000 AND 500000 WITH CHECK OPTION; Пользователь с привилегиями insert к этому просмотру может добавлять новые дан- ные В столбцы DEPARTMENT, DEPT-NO, HEAD-DEPT И BUDGET баЗОВОЙ ТаблИЦЫ Через ЭТОТ просмотр, with check option гарантирует, что все значения бюджетов, вводимые че- рез этот просмотр, будут находиться в указанном для просмотра диапазоне. Следующий оператор добавляет новую строку отдела publications через просмотр v_sub_dept: INSERT INTO VJUBJDEPT ( DEPT JI АМЕ, DEPTJJO, SUB—DEPTJJO, LOW_BUDGET) VALUES ('Publications’, ’999', '670', 250000); Однако следующий оператор не будет выполнен, потому что значение low_budget выходит за пределы диапазона, заданного для базового столбца budget: INSERT INTO V—SUB—DEPT ( DEPT-NAME, DEPT_NO, SUB-DEPT_NO, LOW_BUDGET) VALUES ('Publications', '999', '670', 750000); ISC ERROR CODE:335544558 Operation violates CHECK constraint on view or table V_SUB_DEPT (ISC ERROR CODE:335544558 Операция нарушает ограничение CHECK для просмотра или таблицы V_SUB_DEPT)
524 Часть V. Firebird SQL Предложение with check option в просмотре может быть полезным, когда вы хотите предоставить пользователям изменяемый просмотр, однако вам нужно не допустить изменения ими некоторых столбцов. Просто включите условие поиска для каждого столбца, который вы хотите защитить. Полезность этого предложения несколько ог- раничена по той причине, что просмотр не может быть определен с заменяемыми параметрами. Просмотры только для чтения и изменяемые Когда над просмотром выполняется операция DML, изменения могут быть переданы базовым таблицам, на основе которых был создан просмотр, только в случае выпол- нения некоторых условий. Если просмотр соответствует этим условиям, он будет изменяемым. Если он не соответствует этим условиям, то просмотр будет только для чтения, и изменения в просмотре не смогут передаваться базовым таблицам. Через просмотр значения могут передаваться только тем столбцам, которые были поименованы в просмотре. Firebird сохраняет null во всех других столбцах. Про- смотр не будет изменяемым, если в нем не указаны столбцы, которые не допускают значения null. Просмотр только для чтения может быть сделан изменяемым с помощью триггеров. Просмотры только для чтения Просмотр будет просмотром только для чтения, если его оператор select имеет лю- бую из следующих характеристик: ♦ указывает квантификатор строк, отличный от all (т. е. distinct, first, skip); ♦ содержит поля, определенные через подзапросы или другие выражения; ♦ содержит поля, определенные через агрегатные функции, и/или содержит пред- ложение GROUP by; ♦ включает спецификации union; ♦ соединяет несколько таблиц; ♦ включает не все столбцы not null из базовой таблицы; ♦ выбирает данные из существующего просмотра, который не является изменя- емым. Преобразование просмотров только для чтения в изменяемые Вы можете написать триггеры, которые будут выполнять корректные изменения в базовых таблицах, когда для просмотра запрашиваются операции delete, update или insert. Такая возможность Firebird может перевести неизменяемые просмотры в из- меняемые.
Гпава 24. Просмотры.525 Следующий скрипт создает две таблицы, создает просмотр, который является соеди- нением этих двух таблиц, а затем создает три триггера (для операций delete, update, insert), которые будут передавать все изменения в просмотре базовым таблицам: CREATE TABLE Tablel ( ColA INTEGER NOT NULL, ColB VARCHAR(20), CONSTRAINT pk_table PRIMARY KEY(ColA) ); COMMIT; CREATE TABLE Table2 ( ColA INTEGER NOT NULL, ColC VARCHAR(20), CONSTRAINT fk_table2 FOREIGN KEY REFERENCES Tablel(ColA) ) ; COMMIT; CREATE VIEW Tableview AS SELECT Tablel.ColA, Tablel.ColB, Table2.ColC FROM Tablel, Table2 WHERE Tablel.ColA = Tablel.ColA; COMMIT; SET TERM Л; CREATE TRIGGER TableView_Delete FOR Tableview ACTIVE BEFORE DELETE AS BEGIN DELETE FROM Tablel WHERE ColA = OLD.ColA; DELETE FROM Table2 WHERE ColA = OLD.ColA; END Л CREATE TRIGGER TableView_Update FOR Tableview ACTIVE BEFORE UPDATE AS BEGIN UPDATE Tablel SET ColB = NEW.ColB WHERE ColA = OLD.ColA; UPDATE Table2 SET ColC = NEW.ColC WHERE ColA = OLD.ColA; END Л CREATE TRIGGER TableView__Insert FOR Tableview ACTIVE BEFORE INSERT AS BEGIN INSERT INTO Tablel values (NEW.ColA,NEW.ColB); INSERT INTO Tablel values (NEW.ColA,NEW.ColC); END л COMMIT л SET TERM ;
526 Часть ]/. Firebird SQL При определении триггеров для просмотров проследите, чтобы они не создавали конфликтов или неожиданных условий в отношении триггеров, определенных для базовых таблиц. Выполнение триггера для просмотра предшествует выполнению триггера для таблицы соответствующей фазы (before/after). Предположим, у вас есть триггер before insert (перед вставкой) для базовой табли- цы, который получает значение генератора для первичного ключа, если его new. value имеет значение null. Если триггер для просмотра включает оператор insert для базо- вой таблицы, не указывайте столбец первичного ключа в этом операторе в просмот- ре. Это приведет к тому, что для new.value первичного ключа будет передано значе- ние null, давая возможность триггеру таблицы выполнить свою работу. О триггерах и контекстных переменных new.* см. главу 31. Безнадежные случаи Не все просмотры могут быть сделаны изменяемыми путем создания для них тригге- ров. Например, следующий удобный маленький просмотр только для чтения читает контекстную переменную с сервера, но несмотря ни на какие триггеры, которые вы определите для него, все операции за исключением select не будут выполняться: create view systrans (CURRJTRANSACTION) AS SELECT CURRENT_TRANSACTION FROM RDB$DATABASE; Естественно изменяемые просмотры Просмотр является естественно изменяемым, если выполняются следующие два условия: ♦ спецификация просмотра является подмножеством одной таблицы или другого изменяемого просмотра; ♦ все столбцы базовой таблицы, не включенные в определение просмотра, допус- кают значение null. Следующий оператор создает естественно изменяемый просмотр: CREATE VIEW EMPJ4NGRS (FIRST, LAST, SALARY) AS SELECT FIRST_NAME, LAST_NAME, SALARY FROM EMPLOYEE WHERE JOB_CODE = 'Mngr' WITH CHECK OPTION; Поскольку предложение with check option включено в эту спецификацию, приложе- ния не смогут изменять значение столбца job code, даже если не было нарушения ограничения внешнего ключа для этого столбца в базовой таблице. Изменение поведения изменяемых просмотров Альтернативное поведение естественно изменяемых просмотров может быть задано с использованием триггеров. Для конкретной фазы операции (before/after) триггеры
Гпава 24. Просмотры 527 просмотра вызываются до триггеров базовой таблицы. Следовательно, можно береж- но и умело использовать просмотры для замены поведения обычного триггера базо- вой таблицы на планируемое. Однако также можно создать разрушение при наличии плохо спланированных триг- геров просмотров. Проверяйте, проверяйте, проверяйте! Изменить определение просмотра? Термины изменяемый и только для чтения относятся к тому, как может осуществ- ляться доступ к данным из базовых таблиц, а не к тому, может ли изменяться опреде- ление просмотра. Firebird не предоставляет синтаксис alter view. Для изменения определения просмотра вы должны удалить просмотр, а затем заново его создать. Удаление просмотра Оператор drop view позволяет владельцу просмотра удалять его описание из базы данных. Он не влияет на базовые таблицы, связанные с просмотром. Синтаксис: DROP VIEW имя-просмотра; Операция drop view не будет выполнена, если вы не соединены как владелец про- смотра, или если этот просмотр используется в другом объекте, таком как просмотр, хранимая процедура, триггер для другого просмотра, определение таблицы или огра- ничение CHECK. Следующий оператор удаляет определение просмотра: DROP VIEW SUB_DEPT; Использование просмотров в SQL В SQL просмотр ведет себя во многих отношениях как обычная таблица. Вы можете осуществлять выборку из него, используя или нет предложения order by, group by или where. Если просмотр является естественно изменяемым, или он был сделан изменяемым с помощью триггера и существуют соответствующие привилегии SQL, вы можете вы- полнять для него позиционированные и поисковые операции изменения, добавления и удаления, которые будут оперировать с базовой таблицей. Вы можете также делать следующее: ♦ создавать просмотры просмотров; ♦ выполнять выбор из просмотра в модулях PSQL;
528 Часть V. Firebird SQL ♦ выполнять соединение (join) просмотра с другими просмотрами и таблицами. В некоторых случаях вы можете соединять просмотры с хранимыми процедурами выбора. Для простой иллюстрации создадим просмотр и хранимую процедуру для таблицы employee и выполним их соединение. Вот просмотр: CREATE VIEW V_EMP_NAMES AS SELECT EMP_NO, LAST_NAME, FIRST_NAME FROM EMPLOYEE Л COMMIT Л А вот хранимая процедура: CREATE PROCEDURE P_EMP_NAMES RETURNS ( EMP_NO SMALLINT; EMP_NAME VARCHAR (35)) AS BEGIN FOR SELECT EMP_NO, FIRSTJJAME I 5 ' ' I I LAST_NAME FROM EMPLOYEE INTO :EMP_NO, :EMP_NAME DO SUSPEND; END Л COMMIT Л Запрос, который их соединяет: SELECT V.EMP_NO, V.LAST_NAME, V.FIRST_NAME, P. EMP_NAME FROM V_EMP_NAMES V JOIN P_EMPNAMES P ON V.EMP_NO = P.EMPNO Л Использование планов запросов для просмотров Просмотры могут представлять для пользователей некоторые сложности относитель- но возможности plan. В основном пользователи могут трактовать просмотры как обычные таблицы. Однако, если вы захотите определить пользовательский план, вам нужно иметь сведения об индексах и структурах базовых таблиц, участвующих в просмотре. Оптимизатор трактует ссылку на просмотр так, как если бы базовые таблицы, ис- пользуемые при создании просмотра, были добавлены в список from запроса.
Гпава 24. Просмотры 529 Предположим, просмотр был создан следующим образом: CREATE VIEW V_PROJ_ LEADERS ( PROJ_ID, PROJ_TITLE, LEADER_ID, LEADER_NAME) SELECT P.PROJ_ID, P. PROJ_NAME, P. TEAM_LEADER, E. FULL_NAME, FROM PROJECT P JOIN EMPLOYEE E ON P.TEAM-LEADER = E.EMPNO; Простой запрос к просмотру SELECT * FROM V_PROJ_LEADERS; выводит следующий план: PLAN JOIN (V—PROJ_LEADERS P NATURAL, V-PROJ_LEADERS E INDEX (RDBSPRIMARY7)) Обратите внимание, что оптимизатор обращается к индексам базовых таблиц (через алиасы р и е) для лучшего способа поиска в просмотре. Спецификация select в объ- явлении create view определяет логику выполнения соединения. Следующий запрос является чуть более сложным. В этот раз просмотр соединяется с таблицей employee project с помощью первичных ключей таблиц employee и project. Затем он опять соединяется с таблицей employee для получения ненормализованного списка, который включает имена участников всех проектов, управляемых просмот- ром: select PL.*, ЕМР. LAST_NAME FROM V—PROJ—LEADERS PL JOIN EMPLOYEE- PROJECT EP ON PL.PROJ—ID = EP.PROJ—ID JOIN EMPLOYEE EMP ON EP.EMP-NO = EMP.EIMP-NO; PLAN JOIN (EMP NATURAL, EP INDEX (RDB$FOREIGN15) , PL P INDEX (RDB$PRIMARY12) , PL E INDEX (RDB$PRIMARY?) ) В этот раз индекс внешнего ключа для столбца emp_no таблицы employee_project (с алиасом ер) используется для выбора имен участников проекта из второго "эле- мента" — employee. Как и в предыдущем случае, соединение внутри просмотра ис- пользует первичный ключ таблицы employee для поиска соответствия в team_leader.
530 Часть V. Firebird SQL Если вы решите написать свой план для запроса, который работает с просмотром, вам нужно хорошо знать определение просмотра, понимать индексы и методы дос- тупа. Известная ошибка в просмотрах в Firebird 1.0.x______________________ Если вы определяете просмотр, который является объединением (union) двух или более наборов, просмотр будет вести себя неправильно при использовании в под- запросе в Firebird 1.0.x. Например, следующий запрос приведет к краху сервера: SELECT 1 FROM Tablel WHERE EXISTS ( SELECT FIELD1 FROM UNION_VIEW WHERE <условия-поиска> ) Эта ошибка была исправлена до релиза версии 1.5. Другие наследуемые таблицы В настоящий момент Firebird поддерживает две другие формы наследуемых таблиц: хранимые процедуры выбора и внешние виртуальные таблицы (External Virtual Table, EVT). Хранимые процедуры выбора Расширения Firebird PSQL предоставляют синтаксис для определения хранимой про- цедуры, которая выводит наследуемый набор данных из чего угодно виртуального: из базы данных, контекстных переменных (даже только из входных переменных), из внешних таблиц или из любой их комбинации. Синтаксис select в PSQL и DSQL предоставляет возможность поиска в этих виртуальных таблицах, как если бы они были реальными таблицами. Выходной набор хранимой процедуры выбора определяется как набор выходных пе- ременных с использованием предложения returns из оператора create procedure. Вы- ходные данные создаются в результате цикла по набору курсора, определенному в операторе select, в процессе чтения значений указанных столбцов в эти выходные переменные или в объявленные локальные переменные. Внутри цикла возможно вы- полнение практически любых операций манипулирования данными, включая исполь- зование внутренних циклов. Хранимая процедура выбора может быть вызвана из другой хранимой процедуры. Все, что может быть выбрано, вычислено или наследо- вано, может преобразовываться в выход. В качестве простой иллюстрации следующее объявление хранимой процедуры со- держит цикл, в котором последовательно передаются строки (по одной за раз) в вы- ходной буфер: CREATE PROCEDURE SHOW_JOBS_FOR_COUNTRY ( COUNTRY VARCHAR(15)) RETURNS ( /* виртуальная таблица */ CODE VARCHAR(11),
Глава 24. Просмотры 531 TITLE VARCHAR(25), GRADE SMALLINT) AS BEGIN FOR SELECT job_code, job_title, job_grade FROM job WHERE JOB_COUNTRY = :COUNTRY INTO :CODE, :TITLE, :GRADE DO BEGIN /* начало цикла */ CODE = 'CODE: ' || CODE; /* немного похулиганить с этим значением */ SUSPEND; /* выводит одну строку цикла х/ END END После компиляции хранимой процедуры она готова к выполнению. Получение набо- ра выполняется с помощью слегка измененного оператора select, который при необ- ходимости может принимать константные аргументы в качестве входных пара- метров: SELECT * FROM SHOW_JOBS_FOR_COUNTRY ('England'); CODE TITLE GRADE CODE: Admin Administrative Assistant 5 CODE: Eng Engineer 4 CODE: Sales Sales Co-ordinator 3 CODE: SRep Sales Representative 4 Подробнее о создании и использовании хранимых процедур см. в части VII. Храни- мые процедуры выбора детально обсуждаются в главе 30. Внешние виртуальные таблицы Внешняя виртуальная таблица (EVT) является таблицей, которая получает данные от некоторого внешнего источника данных, а не из базы данных. Результаты запроса к EVT трактуются точно тем же образом, что и результаты любого другого запроса, они выглядят точно так же, как если бы они были получены из таблицы базы данных. Это позволяет интегрировать такие внешние данные, как источники данных реально- го времени, форматированные данные файлов операционной системы, другие базы данных (включая даже не реляционные базы данных), а также любые другие источ- ники табулированных данных. Firebird реализует внешние виртуальные таблицы с помощью предложения external file оператора create table. Внешние данные читаются из текстовых записей фик- сированного формата в обычные столбцы данных Firebird. Внешние таблицы Firebird могут также добавлять записи во внешние виртуальные таблицы. См. разд. "Использование внешних файлов в качестве таблиц" главы 16.
532 Часть V. Firebird SQL Пора дальше Следующая часть книги имеет дело с ключевой и малодокументированной темой транзакций. Основной акцент относительно внутренних технических подробностей в следующих трех главах делается на том, что чем лучше вы понимаете, что происхо- дит в процессе взаимодействия множества клиентов и сервера, тем более эффектив- ной, интуитивной и продуктивной может быть разработка ваших приложений.
ЧАСТЬ VI Транзакции Глава 25. Обзор транзакций Firebird Глава 26. Конфигурирование транзакций Глава 27. Программирование с транзакциями

ГЛАВА 25 Обзор транзакций Firebird В базах данных клиент-сервер, таких как Firebird, клиентское приложение никогда не касается данных, которые физически хранятся в страницах базы данных. Вместо это- го клиентские приложения ведут общение с системой управления базой данных — с "сервером" — создавая пакеты запросов и ответов внутри транзакций. Эта часть книги посвящена ключевым концепциям и вопросам управления транзакциями в Firebird. Система управления данными, которые изменяются множеством одновременно рабо- тающих пользователей, является уязвимой со стороны большого количества проблем целостности данных, если будет работать без какого-либо контроля. Перечислим та- кие проблемы. ♦ Потеря изменений (lost updates) появляется, когда два пользователя просматри- вают один и тот же набор, и один пользователь выполняет изменения, за которы- ми сразу же следуют изменения другого пользователя, перекрывающие работу первого пользователя. ♦ "Грязное" чтение (dirty read) позволяет одному пользователю видеть изменения, которые у другого пользователя находятся в процессе выполнения, без гарантии того, что изменения другого пользователя являются окончательными. ♦ Невоспроизводимое чтение (non-reproducible-reads) позволяет одному пользовате- лю непрерывно выбирать строки, которые другие пользователи изменяют или удаляют. Эта проблема зависит от среды. Например, финансовые процессы по концу месяца или инвентаризация будут ошибочными в этих условиях, поскольку приложению продажи билетов нужно содержать все пользовательские представ- ления синхронизированными, чтобы исключить двойные заказы. ♦ Фантомные строки (phanthom rows) появляются, когда один пользователь может выбирать некоторые, но не все новые строки, введенные другими пользователями. Опять же, это может быть применимо в одних ситуациях, однако это будет иска- жать результаты некоторых процессов. ♦ Перекрывающиеся транзакции могут возникать, когда изменения строки одним пользователем влияют на данные в других строках в той же таблице или в других таблицах, к которым обращаются другие пользователи. Это обычно связано с синхронизацией, проявляющейся, когда нет способа управлять или предсказать последовательность, в которой пользователи будут выполнять изменения. Для решения таких проблем Firebird использует модель управления, которая изоли- рует каждую задачу внутри уникального контекста, ограничивающего последствия,
536 Часть VI. Транзакции если работа такой задачи может вызвать перекрытие работы, выполненной другими задачами. Состояние базы данных не может изменяться, если существуют какие-либо конфликты. ПРИМЕЧАНИЕ. Firebird не допускает "грязное" чтение. При некоторых условиях он специально позволяет невоспроизводимое чтение. Свойства ACID Сейчас прошло уже более 20 лет с того времени, как два исследователя, Тэо Хедер (Theo Haerder) и Андреас Рютер (Andreas Reuter), опубликовали обзор, описывающий поддержание целостности базы данных в параллельно изменяемой среде. Они объ- единили требования в четыре правила, названные атомарность (atomicity), согласо- ванность (consistency), изолированность (isolation) и устойчивость (durability) — аббревиатура ACID1. В течение последующих лет концепция ACID стала эталоном для реализации транзакций в системах баз данных. Атомарность Транзакция (называемая также единицей работы) описывается как множество дейст- вий, преобразующих данные. Чтобы быть "атомарной", транзакция должна быть реа- лизована таким образом, чтобы обеспечить принцип "все или ничего, когда выпол- няются либо все операции, либо ни одна из них"1 2. Транзакция либо завершается пол- ностью (подтверждается, commit), либо отменяется (откатывается, rollback). Согласованность Предполагается, что транзакции выполняют корректные преобразования состояния абстрактной системы — т. е. база данных должна оставаться в согласованном со- стоянии после завершения транзакции, независимо от того, была ли она подтвержде- на или отменена. Концепция транзакции предполагает, что программисты имеют ме- ханизм, позволяющий им объявлять и проверять правила согласованности. Стандар- ты SQL для реализации этих механизмов на сервере предоставляют триггеры, ограничения ссылочной целостности и ограничения check. Изолированность В то время как транзакция изменяет совместно используемые данные, система долж- на создать для каждой транзакции иллюзию, что она выполняется изолированно; ей 1 Theo Haerder and Andreas Reuter, "Principles of Transaction-Oriented Database Recovery", ACM Computing Surveys 15(4) (1983): 287-317. 2 Andreas Reuter and Jim Gray, Transaction Processing Concepts and Techniques (San Francisco, CA: Morgan Kaufmann, 1993).
Глава 25. Обзор транзакций Firebird 537 должно казаться, что все другие транзакции выполнялись или до того, как она нача- лась, или после того, как она завершилась. В процессе перехода данных из начально- го согласованного состояния в конечное согласованное состояние может существо- вать несогласованное состояние базы данных, но никакие данные не могут быть под- вержены изменениям со стороны других транзакций, пока не будут подтверждены изменения данной транзакции. В главе 26 рассматриваются три уровня изоляции транзакций, реализованные в Firebird вместе со средствами реагирования на конфликты и предотвращения работы транзакции, которая накладывалась бы на работу другой транзакции. Устойчивость Когда транзакция завершается, ее изменения должны быть устойчивыми — т. е. но- вое состояние всех объектов, видимых другим транзакциям после подтверждения, будет сохранено и будет постоянным, независимо от наличия ошибок в оборудова- нии или краха программного обеспечения. Контекст транзакции Завершенное общение между клиентом и сервером называется транзакцией. Каждая транзакция имеет уникальный контекст, что приводит к тому, что транзакция будет изолирована от всех других транзакций указанным способом. Правила для контекста транзакции задаются в программе клиентского приложения, при передаче парамет- ров транзакции. Транзакция стартует, когда клиент сообщает серверу о ее начале, получая с сервера дескриптор транзакции. Она остается активной, пока клиент не подтвердит работу (если это возможно) или не отменит ее. Одна транзакция, много запросов В Firebird любая операция, запрашиваемая клиентом, должна появиться в контексте какой-либо активной транзакции. Одна транзакция в своих границах может включать один или много клиентских запросов и ответов сервера. Одна транзакция может ис- пользовать более одной базы данных, осуществляя операции чтения и записи во мно- гих базах данных в процессе решения задачи. Задачи, создающие базы данных или изменяющие их физическую структуру (одиночный оператор DDL или пакет таких операторов), также используют транзакции. Клиент и сервер имеют свои роли. ♦ Роль клиента: клиенты инициируют все транзакции. Когда запущена транзакция, клиент ответственен за передачу запросов на чтение и запись данных, а также за завершение (подтверждение или откат) каждой запущенной им транзакции. Одно клиентское соединение может иметь много активных транзакций. ♦ Роль сервера: задачей сервера является отслеживание каждой транзакции и под- держание согласованного вида базы данных для каждой транзакции в ее контек- сте. Он должен управлять всеми запросами по изменению базы данных так, чтобы
538 Часть VI. Транзакции соответствующим образом обрабатывались конфликты и предотвращалось нару- шение согласованности базы данных. Рис. 25.1 в общих чертах иллюстрирует типичный контекст транзакции по чтению и записи от ее начала до завершения. Он показывает, как сервер управляет многими параллельными транзакциями независимо друг от друга, даже если другие транзак- ции являются активными в том же самом клиентском приложении. Рис. 25.1. Контекст транзакции и независимость В этом примере приложение может использовать транзакцию для выбора (чтения) наборов строк, любая из которых может быть выбрана пользователем для изменения или удаления. В той же самой транзакции могут быть добавлены новые строки. За- просы на изменение, добавление или удаление "посылаются" на сервер один за дру- гим в процессе выполнения пользователем его задачи. Атомарность модели Возможность для клиентского приложения выдавать множество обратимых запросов внутри одной транзакции Firebird является очевидным преимуществом для задач, требующих многократного выполнения одного оператора с различными входными параметрами. Это и представляет атомарность для задач, где должна быть выполне- на группа операторов, а под конец либо подтверждена как единая работа, либо пол- ностью отменена, если встретилось исключение.
Гпава 25. Обзор транзакций Firebird 539 Транзакции и MGA MGA (Multi-Generational Architecture, многоверсионная архитектура) является назва- нием основной архитектурной модели управления состоянием базы данных Firebird. В модели MGA каждая строка, сохраняемая в базе данных, содержит уникальный идентификатор той транзакции, которая ее сохранила. Если другая транзакция посы- лает изменение строки, сервер записывает на диск новую версию этой строки с но- вым идентификатором транзакции и преобразует образ старой версии в ссылку (на- зываемую дельтой) на эту новую версию. Теперь сервер содержит два "поколения" одной и той же строки. Пересылка в сравнении с COMMIT Термин "пересылка" (post), вероятно, был заимствован у старых настольных бухгал- терских программ в качестве аналога пересылаемых журналов в бухгалтерских сис- темах. Такая аналогия полезна для различения двух разделенных операций записи обратимых изменений в базу данных (при выполнении оператора) и подтверждения всех изменений, выполненных одним или несколькими операторами. Отправленные изменения невидимы за пределами их контекста транзакции и могут быть отменены. Подтвержденные изменения являются постоянными и становятся видимыми в запус- каемых впоследствии транзакциях. Если появляется какой-либо конфликт при пересылке, сервер возвращает клиенту сообщение исключения, и подтверждение отменяется. Приложение должно будет обработать этот конфликт в соответствии с его типом. Для конфликта изменения ре- шением часто бывает откат транзакции, следствием чего является отмена всей рабо- ты как атомарной единицы. При отсутствии конфликта приложение может подтвер- дить работу по ее завершении. ПРИМЕЧАНИЕ. Вызовы клиентом commit или rollback являются единственно (—К возможными способами завершения транзакции. Ошибки при подтверждении не \__означают, что сервер выполнит откат транзакции. Откат Откат (rollback) никогда не завершается с ошибкой. Он отменит все изменения, кото- рые были запрошены в процессе выполнения транзакции, — изменения, которые привели к исключениям, так же как и изменения, которые были успешными и не вы- звали исключения. Некоторые отмененные транзакции не оставляют образов строк на диске. Например, некоторые отмены вызывают на сервере просто исчезновение работы, выполненной триггерами, и образы строк, созданные при добавлении данных, естественным обра- зом уничтожаются в процессе отмены вместе с автоматически отмененным протоко- лом, который поддерживается для добавления. Если одна транзакция добавляет
540 Часть VI. Транзакции большое количество записей, автоматически отмененный протокол будет уничтожен вместе с результатами, образы которых отмена будет оставлять на диске. Другие ус- ловия, при которых образы добавляемых записей могут оставаться на диске, — это крах сервера в процессе выполнения операции или использование установок тран- закции, которые явно требуют "не отменять автоматически"3. Блокировка строки В MGA наличие ожидающих завершения новых версий строки имеет следствием блокировку строки. При большинстве условий наличие новой подтвержденной вер- сии блокирует запрос на изменение или удаление этой строки — это конфликт бло- кировки. При получении запроса на изменение или удаление сервер проверяет состояние каж- дой транзакции, которая "владеет" новыми версиями строки. Если самая новая из этих владеющих транзакций является активной или была подтверждена, сервер отве- чает запрашивающей транзакции в соответствии с ее контекстом (уровень изоляции и параметры разрешения блокировки). Если транзакция самой новой версии является активной, запрашивающая транзакция по умолчанию будет ожидать ее завершения (подтверждения или отката), а затем сервер позволит ей продолжить свое выполнение. При этом если было задано nowait, сервер вернет исключение конфликта запрашивающей транзакции. Если транзакция самой новой версии подтверждена и запрашивающая транзакция имеет уровень изоляции snapshot (т. е. параллельный), сервер отвергает запрос и со- общает о конфликте блокировки. Если транзакция имеет уровень изоляции read committed с установленным по умолчанию значением record_version, сервер выпол- няет запрос и записывает новую версию записи с идентификатором этой транзакции. Ж) ПРИМЕЧАНИЕ. Возможны другие условия, когда запрашивающая транзакция ?—имеет уровень изоляции read committed. На результат любого запроса транзак- \____> ции могут также воздействовать необычные условия в контексте транзакции, ко- торая владеет ожидающими завершения изменениями. Более подробную ин- формацию об этих вариантах см. в главе 26, где детально рассматриваются па- раметры транзакции. Firebird не использует традиционную двухфазную блокировку для большинства обычных контекстов транзакции. Следовательно, все нормальные блокировки вы- полняются на уровне строки (такая блокировка называется оптимистической) — каждая строка доступна всем транзакциям чтения/записи, за исключением транзак- ции, выполняющей создание новой версии этой строки. Во время успешного подтверждения старая версия записи становится устаревшей записью. 3 Программисты API. смотрите в ТРВ константу isc_tpb_no_auto_undo.
Гпава 25. Обзор транзакций Firebird 541 ♦ Если выполнялась операция изменения, то новый образ становится самой послед- ней подтвержденной версией. Оригинальный образ этой записи с идентификато- ром последней изменяющей транзакции становится доступным для сборки му- сора. ♦ Если выполнялась операция удаления, то "остаток записи" заменяет устаревшую запись. Чистка или резервное копирование очищает этот остаток и освобождает физическое место на диске, занимаемое удаленной записью. Сведения о чистке и сборке мусора см. в разд. "Гигиена базы данных" главы 15. Ру- ководство по чистке находится в главе 39. В итоге при нормальных условиях: ♦ любая транзакция может читать любую строку, которая была подтверждена до старта этой транзакции; ♦ любая транзакция чтения/записи может запрашивать изменение или удаление строки; ♦ пересылка (post) обычно будет успешной, если никакая другая транзакция чте- ния/записи не пересылала и не подтверждала изменения для новой версии этой записи. Транзакциям read committed разрешается пересылать изменения, переза- писывающие подтвержденные версии более новых транзакций; ♦ если пересылка успешна, транзакция устанавливает "замок" на эту запись. Другие транзакции могут читать последнюю подтвержденную версию, однако ни одна пересылка изменения или удаления этой строки не будет успешной. Блокировка на уровне таблицы Транзакция может быть сконфигурирована для блокирования всей таблицы. Сущест- вует два приемлемых способа сделать это в DSQL: установив уровень изоляции тран- закции в snapshot table stability (известный также как согласованный режим, под- держиваемый повторяемым чтением, repeatable read) или с помощью резервирования таблицы. Следует подчеркнуть, что такие настройки нужны в случае, когда требуют- ся приоритетные условия. Они не рекомендуются для ежедневного использования в Firebird. Неприемлемый способ установления блокировки на уровне таблицы (Firebird 1.5 и выше) — применение пессимистической блокировки на уровне оператора, которая действует на всю таблицу. Оператор подобный следующему делает это: SELECT * FROM ATABLF. FOR UPDATE WITH LOCK; Строго говоря, это не является вопросом конфигурирования транзакции. Тем не ме- нее конфигурирование транзакции, которая владеет наборами, найденными с этой явной пессимистической блокировкой, является важным. Вопросы пессимистической блокировки обсуждаются в главе 27.
542 Часть VI. Транзакции Добавление Не существует "дельт” или блокировок для добавления. Если другая транзакция пе- ред этим не выполняла добавления в условиях блокировки на уровне таблицы, до- бавление всегда будет успешным, если оно не нарушает каких-либо ограничений или проверок достоверности. "Старение" и статистика транзакций Когда клиентский процесс передает дескриптор транзакции, он получает уникальный внутренний идентификатор для транзакции и сохраняет его в инвентарной странице транзакции (Transaction Inventory Page, TIP). Идентификатор и возраст транзакции Идентификатор транзакции является 32-битовым целым, которое генерируется с единичным приращением. Новая или только восстановленная база данных начинает Серию идентификаторов с единицы. Возраст транзакции определяется ее идентифи- катором: самая старая имеет наименьший идентификатор. Идентификаторы транзакций и ассоциированные с ними состояния данных сохраня- ются в инвентарных страницах транзакций. На заголовочной странице базы данных система учета поддерживает набор полей, содержащих идентификаторы транзакций, интересующих эту систему, а именно старейшую заинтересованную транзакцию (Oldest Interesting Transaction, OIT), старейшую активную транзакцию (Oldest Active Transaction, OAT) и число, используемое в следующей транзакции. "Мгновенный снимок" идентификатора транзакции также записывается каждый раз, когда увеличи- вается оат — обычно тот же самый идентификатор, что и оат, или близкий к нему. Получение идентификатора транзакции F’irebird 1.5 и более поздние версии имеют контекстную переменную current^ transaction, которая возвращает идентификатор данной транзакции. Она может ис- пользоваться в SQL в любом выражении. Например, для сохранения идентификатора транзакции в таблице протокола вы можете использовать следующее: INSERT INTO BOOK_OF_LIFE (TID, COMMENT, SIGNATURE) VALUES (CURRENT /TRANSACTION, 'This has been a great day for transactions', CURRENT-USER) ; Важно помнить, что идентификаторы транзакций являются циклическими. Посколь- ку нумерация сбрасывается после каждого восстановления (restore) базы данных, идентификаторы транзакций не должны использоваться для первичных ключей или ограничений уникальности.
Глава 25. Обзор транзакций Firebird 543 Переполнение идентификатора транзакции Как было сказано, идентификатор транзакции является 32-битовым целым. Если по- следовательность идентификаторов превысит лимит в 4 Гбайт и начнется новый от- счет, то могут произойти неприятные вещи. Когда завершится последняя транзакция, системная транзакция перестанет работать, и изменение метаданных будет невоз- можным. Сборка мусора прекратится. Транзакции пользователя не станут запус- каться. При 100 транзакциях в секунду это займет 1 год, 4 месяца, 11 дней, 2 часа и прибли- зительно 30 минут4. Резервное копирование и восстановление базы данных восстанавливает идентифика- торы транзакций. До последнего времени заброшенная база данных могла получить другие травмы до того, как будет исчерпана последовательность идентификаторов транзакций. Теперь при больших размерах страниц, больших объемах дисков и уменьшении необходимости отслеживать размер файлов базы, данных риск перепол- нения последовательности идентификаторов транзакций более вероятен. "Заинтересованные транзакции" Подпрограммы учета транзакций сервера и клиента используют идентификаторы транзакций для отслеживания состояния транзакций. Служебные подпрограммы ис- пользуют возраст транзакций для решения вопроса, какие старые версии записей яв- ляются "интересными", а какие нет. Неинтересные транзакции могут быть отмечены для удаления. Сервер остается "заинтересованным" в каждой транзакции, которая не была жестко подтверждена с помощью оператора commit. Активные, зависшие (limbo), отмененные и "умершие" транзакции все являются ин- тересными. Транзакции, которые были подтверждены с использованием оператора commit with retain (мягкое подтверждение или CommitRetaining), остаются актив- ными, пока не будут подтверждены жестко, и поэтому являются интересными. В не- которых случаях интересные транзакции могут стать "застрявшими" и станут тормо- зить работу. Заброшенные, застрявшие транзакции станут источником серьезного ухудшения производительности. Застрявшие oit (старейшие заинтересованные транзакции) при- ведут к росту количества инвентарных страниц транзакций. Сервер поддерживает рабочую таблицу транзакций в виде битовой маски, хранящейся в инвентарных стра- ницах транзакций. Таблица копируется и записывается в базу данных при старте каждой транзакции5. Когда она непомерно раздуется от застрявших транзакций, она 4 Спасибо, Ann Harrison! 5 В оригинале не совсем верно выбраны термины. Никакой "таблицы", конечно, нет. Есть битовый массив, который меняется при изменении состояния транзакций и который копирует- ся в память (частично) при старте транзакций snapshot. Также TIP не может быть "раздут", поскольку битовый массив просто растет в размерах при старте каждой новой транзакции и строится заново при очередном восстановлении (restore). Размер TIP равен Next Transaction / 4 (в байтах). — Прим. науч. ред.
544 Часть VI. Транзакции будет использовать гораздо больше ресурсов памяти, и память станет фрагментиро- ванной от постоянного выделения ресурсов. Старейшая заинтересованная транзакция отт является транзакцией с наименьшим номером в инвентарной странице транзак- ций, которая находится в неподтвержденном (rolled back) состоянии. Старейшая активная транзакция оат— это транзакция с наименьшим номером в инвентарной странице транзакций, которая является активной. Транзакция является активной до тех пор, пока она не подтверждена жестко, не выполнен ее откат, и если она не является зависшей (limbo)6. Транзакции только для чтения Транзакция только для чтения остается активной (и в некоторой степени интерес- ной), пока не будет подтверждена. При этом активная транзакция только для чтения, для которой рекомендуется уровень изоляции read committed (см. главу 26), никогда не становится застрявшей и не влияет на систему обслуживания. /Д ВНИМАНИЕ! Не путайте транзакцию только для чтения с транзакцией чте- / ! \ ния/записи, которая выполняет передачу выходного набора пользовательскому ——Д- интерфейсу от оператора select. Даже если приложение делает выходной набор набором только для чтения, оператор select внутри транзакции чтения/записи может стать — и часто бывает — причиной замедления работы системы. Фоновая сборка мусора Версии записей отмененных транзакций вычищаются из базы данных, когда они об- наруживаются в процессе обычной обработки данных. Как правило, если к строке обращается оператор, то любая неинтересная старая версия записи, подходящая для удаления, будет отмечена для процесса сборки мусора. Некоторые "закоулки" базы данных могут не посещаться операторами в течение дли- тельного времени, следовательно, нет гарантии, что все версии записей, созданные в отмененных транзакциях, будут отмечены и в итоге удалены. До тех пор, пока оста- ются старые версии записей, заинтересованная транзакция должна оставаться "инте- ресной" для сохранения состояния базы данных. СОВЕТ. Полное сканирование базы данных (обычно при резервном копирова- нии) выполнит сборку мусора, но не сможет изменить состояния транзакций. Это работа для "полной" сборки мусора, выполняемой при чистке (sweep) базы дан- ных. 6 Здесь и далее в тексте практически не упоминается важный параметр Oldest Snapshot Transaction (OS T). — Прим. науч. ped.
Глава 25. Обзор транзакций Firebird 545 Отмененные транзакции Транзакции в отмененном (rolled back) состоянии не включены в сборку мусора. Они остаются заинтересованными, пока чистка базы данных не отметит их как "подтвер- жденные" и не освободит их для сборки мусора. В системах с низким уровнем кон- фликтов периодическая ручная чистка базы данных может быть единственно необхо- димой работой. Некоторые системы, плохо спроектированные для работы с конфликтами, демонст- рируют высокий уровень откатов транзакций и имеют тенденцию накапливать'заин- тересованные транзакции быстрее, чем с ними могут справиться автоматические процедуры обслуживания базы данных. В подобных системах должна часто выпол- няться ручная чистка, если складывается впечатление, что автоматическая чистка не работает надлежащим образом. "Мертвые" транзакции Транзакции считаются "мертвыми", если они находятся в активном состоянии, но не существует связанного с ними контекста соединения. Обычно транзакции "умирают" в клиентских приложениях, которые не заботятся об их завершении перед отключе- нием от базы данных. Умершие транзакции также являются частью обломков, остав- ленных после краха клиентских приложений или поломки сетевых соединений. Сервер не может определить разницы между действительно активными транзакция- ми и умершими. Пока тайм-аут ожидания соединения работает, и регулярная сборка мусора своевременно убирает ненужные транзакции, умершие транзакции не созда- ют проблем. При срабатывании тайм-аута соединения выполнится их откат, и в итоге этот мусор будет убран. Частое большое накопление умерших транзакций может стать проблемой. Поскольку они не являются отмененными, слишком многие из них остаются заинтересованными на чрезмерно большой срок. В системе, где поведение пользователя, частые аварии программ, отключение питания или сетевые ошибки генерируют огромное количест- во умерших транзакций, мусор от умерших транзакций становится большой проб- лемой. Зависшие транзакции Зависшие транзакции (limbo transactions), подробно обсуждаемые в главе 26, появ- ляются в результате сбоя двухфазной операции commit для нескольких баз данных. Система распознает их как особый случай, требующий вмешательства человека, по- скольку сам сервер не может определить, будет ли их подтверждение или откат вли- ять на согласованность данных в разных базах данных. Единственный способ разрешить зависшие транзакции — это запустить для базы данных инструмент gfix с соответствующими переключателями для достижения же- лаемого результата. Утилита изменяет состояние транзакции либо на "подтвержден- ное", либо на "отмененное". С этого момента с ней осуществляется работа, как и с любой другой подтвержденной или отмененной транзакцией. 18 Зак. 420
546 Часть VI. Транзакции OITи ОАТдолжны постоянно изменяться Совет "oit и оат должны постоянно изменяться" является ключевой фразой для ре- шения всех проблем, связанных с производительностью базы данных. Время, потра- ченное на изучение цикла жизни транзакций в многоверсионной архитектуре Firebird, будет одним из лучших ваших вложений в дальнейшую работу с Firebird другими базами данных с открытыми текстами7. Для начала понимания взаимодействия между клиентскими процессами, которые "владеют" транзакциями, с одной стороны, и учетом транзакций, выполняемым в базе данных, с другой, будет полезным познакомиться с механизмом взаимодействия сервера и транзакций. Образ состояния транзакции Внутренний образ состояния транзакции (Transaction State Bitmap, TSB) является таблицей, содержащей идентификаторы транзакций и их состояние, поддерживаемой сервером и инициализируемой при начальном подключении к базе данных. В логиче- ских терминах TSB содержит каждую транзакцию, найденную в инвентарных стра- ницах транзакций, которая новее, чем oit. Пока существуют подключения к базе данных, серверный процесс поддерживает TSB динамически, добавляя новые иден- тификаторы транзакций, изменяя состояние и удаляя идентификаторы, которые ста- новятся неинтересными (т. е. будут подтверждены). Сервер записывает изменения в инвентарные страницы транзакций без повторного их чтения с диска. На рис. 25.2 иллюстрируются эти шаги. Каждый раз, когда сервер обрабатывает за- прос для транзакции, он читает заголовочную страницу базы данных, чтобы полу- чить идентификатор для следующей транзакции. Изменяется TSB, и изменения запи- сываются в инвентарные страницы базы данных. "Движение вперед oit (и/или оат)" — это термин Firebird, обозначающий увеличение значений oit и оат в заголовочной странице базы данных, когда подтверждающиеся более старые транзакции исключаются из TSB, а новые транзакции, в свою очередь, становятся "старейшими". Обновление заголовочной страницы базы данных послед- ними значениями oit, оат и следующей транзакции являются частью такой динами- ческой экологии. Если новая транзакция является транзакцией snapshot, она будет иметь свою собст- венную копию TSB для поддержания согласованного вида состояния базы данных, каким он был при ее старте. Транзакция read committed всегда обращается к самому последнему "глобальному" TSB для получения доступа к версиям, подтвержденным после ее старта. 7 Хорошо понимать цикл жизни транзакций в Firebird, если вы работаете и с другими реля- ционными СУБД, например, с PostgreSQL, которая имитирует многоверсионную архитектуру Firebird.
Гпава 25. Обзор транзакций Firebird 547 Рис. 25.2. Взаимодействие процесса клиент-сервер с TSB Берем 2222 и подчищаем TSB! Условия для изменения OITи ОАТ Каждый раз, когда сервер стартует другую транзакцию, он проверяет состояние идентификаторов транзакций, которые он хранит в TSB, удаляя те, чье состояние было изменено на "подтвержденное", и заново вычисляет значения oit и оат. Он сравнивает их с хранимыми значениями в странице базы данных и при необходимо- сти включает их в данные, сопровождающие запись в заголовок базы данных иден- тификатора новой "следующей транзакции". Значение оат будет увеличиваться, если транзакции будут достаточно короткими, чтобы предохранять активные транзакции и мусор от самых новых транзакций — от нагромождения, oit будет увеличиваться, пока клиентские процессы подтверждают свою работу в большей степени, нежели отменяют их или теряют в результате ава- рий. При этих условиях производительность базы данных будет хорошей. Застрявшая оат хуже, чем застрявшая oit. В системе с хорошей производительностью разница между оат и самой новой транзакцией должна приблизительно равняться
548 Часть VI. Транзакции количеству запущенных клиентских процессов, умноженному на среднее количество выполняющихся транзакций в каждом процессе. Выполняйте чистку базы данных во внерабочее время или используйте автоматическую чистку или оба варианта. В главе 27 вы найдете несколько стратегий клиентских приложений для оптимизации OIT И ОАТ. "Зазор" "Зазор" — еще одна часть Firebird. Он означает разницу между отт и оат. Зазор будет небольшим в сравнении с общим количеством транзакций или, в идеале, нулем. При этих условиях разумно предположить, что не существует подвешенных транзакций, что привело бы к раздуванию TSB и большому разрастанию инвентарных страниц транзакций. Собственно сам зазор не ухудшает производительность. Зазор является индикатором большого объема накладных расходов, добавляемых системой управления транзак- циями к обработке базы данных — чрезмерное использование и фрагментация памя- ти, избыточное количество читаемых страниц в процессе поиска и выделяемых стра- ниц в процессе изменения и добавления данных. Решение и устранение проблем ухудшения производительности заключается в уменьшении зазора. Чистка в сравнении со сборкой мусора Сборка мусора (Garbage Collection, GB) — это постоянный фоновый процесс, кото- рый является функцией нормальной деятельности по поиску записей и проверке вер- сий записей, которая выполняется для каждой транзакции. Когда обнаруживается устаревшая версия записи с идентификатором меньшим, чем оат, происходит одно из двух: ♦ для Классического сервера устаревшие версии записей удаляются немедленно. Это называется кооперативной сборкой мусора, потому что каждая транзакция и каждый экземпляр сервера участвуют в чистке мусора, опережая все другие; ♦ для Суперсервера устаревшие версии записей помечаются во внутреннем списке удаляемых элементов для потока сборки мусора. Когда поток сборки мусора "просыпается", он будет работать с элементами в этом списке, изменяя его. Чистка (sweep) также выполняет эту задачу, но в отличие от сборки мусора она мо- жет иметь дело с одной категорией заинтересованных транзакций: с теми, которые имеют состояние "отмененные" (rolled back). Она может также удалить "остатки" удаленных записей и освободить память для повторного использования. Зазор является важным для автоматической чистки, потому что установленный для базы данных интервал чистки (sweeping interval) управляет максимальной величиной зазора, при достижении которого стартует процесс чистки8. Автоматическая чистка 8 Во всех версиях Firebird и в InterBase до версии 7.1 sweep срабатывает, когда расстояние между OIT и Oldest Snapshot Transaction (а не ОАТ) становится больше sweep interval (см. разд. "Другие переключатели gstat" главы 18). В Firebird 2.0 срабатывание sweep будет зависеть именно от разницы ОАТ-О1Т. — Прим. науч. ред.
Гпава 25. Обзор транзакций Firebird 549 выполняется редко или никогда не выполняется для некоторых баз данных, потому что зазор не достигает порогового значения. По умолчанию каждая база данных создается с интервалом чистки (максимальной величиной зазора) в 20 000. При необходимости эта величина может быть увеличена или уменьшена, или же чистка совсем может быть отключена при установке величи- ны интервала в 0. В системе, где зазор всегда меньше интервала чистки, не является справедливым, что для базы данных никогда не требуется чистка. Все базы данных должны вычищать- ся — это вопрос управления базами данных человеком, будут ли они чиститься авто- матически, вручную с использованием gfix или обоими способами. Думайте об авто- матической чистке как о системе стабильного функционирования, а не как о замени- теле удобного управления базой данных. Статистика транзакций В Firebird есть несколько полезных утилит для получения сведений о том, насколько хорошо ваша база данных управляет зазором между oit и оат. Их вы можете также использовать для просмотра значений в заголовочной странице базы данных. gstat Инструмент командной строки gstat, используемый с переключателем -header, пока- зывает различную статистическую информацию базы данных, включая текущее зна- чение идентификатора транзакции для oit, оат и для следующей новой транзакции. Для использования gstat соединитесь с базой данных как пользователь SYSDBA из командной строки на главной машине и перейдите в каталог bin Firebird. Наберите следующий текст в Windows: gstat -h <путь-к--базе-данных> -user sysdba -password masterkey Наберите в POSIX: ./gstat -h <гтуть~к~базе~данных> -user sysdba -password masterkey Вот фрагмент выходных данных: Oldest transaction 10075 Oldest active 100152 Oldest snapshot 100152 Next transaction 100153 Терминология в этом отчете может вызвать удивление. При просмотре выхода gstat имейте в виду, что: ♦ Oldest transaction — ЭТО OIT; ♦ Oldest active — очевидно, OAT;
550 Часть VI. Транзакции ♦ oldest snapshot— обычно то же, что и оат— строка будет выведена, когда оат продвигается далее. Это фактический идентификатор транзакции, рассматривае- мый сборщиком мусора как сигнал существования мусора, который может быть обработан9. isql Вы можете получить похожий вид статистики из заголовка базы данных в сессии isql при использовании команды show database. Многие инструменты администратора Firebird сторонних разработчиков создают эк- вивалентные отчеты. Что может рассказать вам статистика Сборка мусора всегда будет выполняться над устаревшими версиями записей, не тро- гая заинтересованные транзакции. При этом oldest snapshot отмечает границы, на которых сборщик мусора останавливает просмотр подтвержденных транзакций. Любой мусор с такого номера транзакции и выше не будет обрабатываться. Если зазор между оат и next transaction определяет гораздо большее количество транзакций, чем подсчитываемое количество подключенных пользователей и их за- дач, вы можете быть уверенными, что большое количество мусора не будет обраба- тываться сборщиком мусора. Если этот зазор будет увеличиваться, работа с базой данных будет все больше и больше замедляться. Серверы будут выдавать ошибку "Out of memory" (недостаточно памяти) или просто будут аварийно завершаться, по- тому что TSB расходует большой объем памяти или приводит к тому, что сервисы управления системной памятью используют слишком сильную фрагментации памя- ти. Дешевые серверы — особенно те, которые нагружены предоставлением дополни- тельных сервисов— даже могут не получить достаточно ресурсов для записи сооб- щения в протокол. Если вам кажется, что зазоры между oit и оат или между оат и next transaction при- водят к проблемам в вашей системе, вы можете многое узнать о воздействиях чистки базы данных и улучшения ваших приложений, если сохраните протоколы стати- стики. СОВЕТ. Для получения текстовых файлов простой статистики просто перена- правьте выход утилиты gstat -h в текстовый файл в соответствии с правилами вашей командной строки. В утилите isql используйте команду output <имя файлв> для перенаправления результатов выполнения команды show database. 9 OST — это на самом деле номер транзакции, которая была активной на момент старта ОАТ. — Прим. науч. ред.
Глава 25. Обзор транзакций Firebird 551 Пора дальше Драйверы, которые скрывают от разработчика приложений явное управление тран- закциями и нивелируют разницу между различными реализациями поставщиков SQL, редко способствуют высокой производительности пользовательских приложе- ний, а также хорошему здоровью и гигиене базы данных. В следующей главе рас- сматриваются различные комбинации параметров, доступные для конфигурирования транзакций, а также стратегии, которые вы можете использовать для подгонки тран- закций к каждой задаче.
ГЛАВА 26 Конфигурирование транзакций Транзакция является "оберткой" вокруг любого фрагмента работы, влияющего на состояние базы данных или более чем одной базы данных. Пользовательский процесс запускает транзакцию и тот же самый пользовательский процесс завершает ее. По- скольку пользовательские процессы могут иметь различные вид и размер, любая транзакция является конфигурируемой. Обязательные и дополнительные свойства транзакции являются важной частью любого контекста транзакции. Параллельность Термин параллельность (concurrency) относится к тому состоянию, в котором две или более задач выполняются внутри одной и той же базы данных в одно и то же время. О базе данных при этих условиях говорят, что она поддерживает параллель- ные задачи. Процесс, владеющий транзакцией, внутри этой транзакции способен вы- полнять любые операции, которые: ♦ будут согласованными в текущем представлении базы данных; ♦ при подтверждении не будут влиять на согласованность любых других текущих представлений базы данных для активных транзакций. Каждая транзакция конфигурируется с помощью группы параметров, которые позво- ляют клиентскому процессу абсолютно точно прогнозировать поведение сервера ба- зы данных, когда он обнаружит потенциальную несогласованность. Интерпретация сервером понятия "согласованности" управляется конфигурацией транзакции. Кон- фигурация, в свою очередь, управляется клиентским приложением. Факторы, влияющие на параллельность Четырьмя параметрами конфигурации, влияющими на параллельность, являются: ♦ уровень изоляции; ♦ способ разрешения блокировок; ♦ способ доступа; ♦ резервирование таблиц. На одном из уровней изоляции (read committed) также рассматриваются текущие со- стояния версий записей.
Гпава 26. Конфигурирование транзакций 553 Уровень изоляции Firebird предоставляет три уровня изоляции транзакций для определения "глубины" согласованности требований транзакции. В одном крайнем случае транзакция может получить исключительный доступ по записи ко всей таблице, в то время как в другом крайнем случае неподтвержденная транзакция получает доступ к любым внешним изменениям состояния базы данных. Никакая транзакция в Firebird не сможет видеть неподтвержденные изменения данных от других транзакций. В Firebird уровень изоляции может быть: ♦ READ COMMITTED: • record__version; • NO record_version; ♦ snapshot; ♦ SNAPSHOT table stability. Стандартные уровни изоляции Стандарт SQL по изоляции транзакций "симпатизирует" механизму двухфазной бло- кировки, которую использует большинство реляционных СУБД для реализации изо- ляции. Это является его отличительной чертой по сравнению с большинством других стандартов. Он определяет изоляцию не столько в теоретических терминах, сколько в терминах феноменов, допускаемых каждым уровнем (или запрещаемых им). Фено- менами, с которыми имеет дело стандарт, являются: ♦ "грязное" чтение (Dirty read): появляется, если транзакция способна читать не- подтвержденные (ожидающие завершения) изменения, выполненные другими транзакциями; ♦ неповторяемое чтение (Non-repeatable read): появляется, если последующие чте- ния набора строк в рамках этой же транзакции могут отличаться от того, что было прочитано транзакцией в начале ее работы; ♦ фантомные строки (Phantom rows): появляется, если последующий набор строк, прочитанный транзакцией, отличается от набора, который был прочитан в начале работы транзакции. Фантомные явления появляются, если при последующем чте- нии появляются новые добавленные строки и/или исчезают удаленные строки, которые были подтверждены с момента первого чтения. В табл. 26.1 показаны определяемые в стандарте четыре уровня изоляции с явления- ми, которые управляют их определениями. read uncommitted вовсе не поддерживается в Firebird, read committed соответствует стандарту. На двух следующих уровнях этого уровня изоляции природа многоверси- онной архитектуры превалирует над ограничениями двухфазной блокировки, предла- гаемой стандартом. Отображение указанных в стандарте уровней repeatable read и serializable невозможно.
554 Часть VI. Транзакции Таблица 26.1. Уровни изоляции и управление феноменами в стандарте SQL Уровень изоляции "Грязное"чтение Неповторяемое чтение Фантомы READ uncommitted Допустимо Допустимо Допустимо READ COMMITTED Недопустимо Допустимо Допустимо REPEATABLE READ Недопустимо Недопустимо Допустимо SERIALIZABLE Недопустимо Недопустимо Недопустимо READ COMMITTED Самым низким уровнем изоляции является read committed. Только на этом уровне изоляции вид состояния базы данных, измененного в процессе выполнения транзак- ции, изменяется каждый раз, когда подтверждаются версии записей. Вновь подтвер- жденная версия записи заменяет ту версию, которая была видна при старте транзак- ции. Подтвержденные добавления, выполненные после старта транзакции, становят- ся видимыми для нее. Уровень изоляции read committed обеспечивает неповторяемое чтение и не избавляет от феномена фантомных строк. Это наиболее полезный уровень для операций реаль- ного времени над большим объемом данных, т. к. сокращается количество конфлик- тов данных, однако он не является подходящим для задач, которым нужен воспроиз- водимый вид. Поскольку уровень изоляции read committed имеет скоротечную природу, то тран- закция (именно этого уровня) может быть сконфигурирована в соответствии с по- требностями реагирования на подтверждение изменений и наличие ожидающих за- вершения изменений других транзакций. ♦ При установке record_version (флаг по умолчанию) сервер позволяет транзакции читать самую последнюю подтвержденную версию записи. Если транзакция име- ет режим read write (чтение/запись), то она будет способна перезаписывать самую последнюю подтвержденную версию записи, если ее идентификатор (TID) более поздний, чем идентификатор транзакции, подтвердившей самую последнюю вер- сию записи. ♦ При установке no_record_version сервер эффективно имитирует поведение сис- тем, которые используют двухфазную блокировку для управления параллель- ностью. Он блокирует для текущей транзакции чтение строки, если для нее суще- ствует изменение, ожидающее завершения. Разрешение ситуации зависит от уста- новки разрешения блокировки. • При задании wait транзакция будет ждать, когда другая транзакция либо под- твердит, либо отменит свои изменения. Эти изменения затем станут доступ- ными, если другая транзакция их отменит или если идентификатор транзакции более поздний, чем идентификатор другой транзакции. Транзакция будет за- вершена аварийно по конфликту блокировки, если идентификатор другой транзакции будет более поздним.
Глава 26. Конфигурирование транзакций 555 • При задании nowait транзакция немедленно получит сообщение о конфликте блокировки. Г ПРИМЕЧАНИЕ. Транзакция read committed может видеть все последние под- твержденные версии, которые позволяют читать установки этой транзакции, что __\ подходит для операций добавления, изменения, удаления и выполнения. При этом любые выходные наборы, полученные в транзакции по оператору select, должны получать измененный вид базы данных. SNAPSHOT (параллельность) "Средним" уровнем изоляции является snapshot, альтернативно называемый Repeatable Read (повторяемое чтение) или concurrency (параллельность). При этом изоляция snapshot в Firebird не является в точности соответствующей уровню Repeatable Read, как он определен в стандарте. Он изолирует вид базы данных для транзакции изменениями на уровне строк только для существующих строк. При этом, поскольку по своей природе многоверсионная архитектура полностью изолирует транзакции snapshot от новых строк, подтвержденных другими транзакциями, не предоставляя транзакциям snapshot доступ к глобальному образу состояния транзак- ций (TSB, см. главу 25), транзакции snapshot не могут видеть фантомные строки. Следовательно, транзакции snapshot в Firebird предоставляют более глубокий уро- вень изоляции, чем repeatable read в SQL. Пока snapshot еще не идентичен serializable, потому что другие транзакции могут изменять и удалять строки, которые находятся в зоне видимости транзакции serializable. Транзакция гарантирует неизменный вид базы данных, который до завершения тран- закции не будет зависеть от любых подтвержденных изменений в других транзакци- ях. Такой уровень полезен для "исторических" задач, таких как формирование отче- тов или экспорт данных, которые могут оказаться ошибочными, если не будут вы- полняться с воспроизводимым видом данных. snapshot является уровнем изоляции по умолчанию для инструмента обработки за- просов isql, а также для многих компонентов и драйверов интерфейса. SNAPSHOT TABLE STABILITY (согласованность) Самым "глубоким" уровнем изоляции является snapshot table stability, альтерна- тивно называемый consistency, потому что он гарантирует получение данных в неиз- менном состоянии, который останется внешне согласованным в пределах базы дан- ных, пока продолжается транзакция. Транзакции чтения/записи не могут даже читать таблицы, которые блокируются транзакцией с таким уровнем изоляции. Блокировка на уровне таблицы, создаваемая этим уровнем изоляции, включает в себя все таблицы, к которым осуществляет доступ транзакция, включая те, которые связа- ны ссылочными ограничениями.
556 Часть VI. Транзакции Этот уровень устанавливает агрессивное расширение, которое гарантирует сериали- зацию в строгом смысле, т. е. никакая другая транзакция не может добавлять или удалять — вообще изменять — строки в используемых таблицах, если любая тран- закция получает дескриптор с этим уровнем изоляции. Наоборот, транзакция table stability не сможет получить дескриптор, если какая-нибудь транзакция чте- ния/записи в настоящий момент читает любую таблицу, которая находится в зоне видимости этой транзакции. В терминах стандарта это не обязательно, поскольку изоляция snapshot уже защищает транзакции от всех трех феноменов, управляемых уровнем serializable в стандарте SQL. Транзакция consistency еще называется блокирующей транзакцией, потому что она блокирует доступ любых других транзакций чтения/записи к любым записям, к кото- рым эта транзакция обращается, и к любым записям, зависящим от этих записей. ВНИМАНИЕ! Поскольку этот уровень потенциально блокирует часть базы дан- ных для других пользователей, которым нужно выполнять изменения, snapshot table stability должен использоваться осторожно. Внимательно следите за размером применяемых наборов, воздействием на соединения, зависимые таб- лицы и особенно за длительностью такой транзакции. Способ доступа Способ доступа может быть read write (чтение/запись) или read only (только чте- ние). Транзакция read write может выбирать, добавлять, изменять и удалять данные. Транзакция read only может только выбирать данные. Способом доступа по умолчанию является read write. СОВЕТ. Одним из преимуществ транзакции read only является ее способность выбирать записи для пользовательского интерфейса без использования большо- го объема ресурсов на сервере. Убедитесь, что ваши транзакции только для чте- ния сконфигурированы с уровнем изоляции read committed, чтобы гарантиро- вать, что сборка мусора на сервере будет выполняться, минуя эту транзакцию. Способ разрешения блокировок ("Режим блокировок") Способ разрешения блокировок определяет поведение транзакции в случае, когда она пытается отправить изменение, которое конфликтует с изменением, уже отправлен- ным другой транзакцией. Значениями являются wait и nowait. WAIT wait (по умолчанию) приводит к тому, что транзакция будет ожидать, пока заблоки- рованные строки ожидающей завершения транзакцией не будут освобождены преж-
Глава 26. Конфигурирование транзакций 557 де, чем она станет определять, может ли эта транзакция их изменять. Если другая транзакция отправит более новую версию записи, то ожидающая транзакция выдаст сообщение, что возник конфликт блокировки. wait чаще всего не является предпочтительным способом блокировки для интерак- тивных систем с большим объемом данных, потому что он может замедлять работу пользователей и при некоторых условиях может приводить к тупикам (см. разд. "Что такое взаимная блокировка? "). Использование wait бессмысленно в изоляции snapshot. Если только блокирующая транзакция в итоге не выполнит откат— наименее вероятный сценарий— результа- том ожидания, естественно, будет конфликт блокировки. В транзакции read committed вероятность того, что результатом ожидания будет конфликт блокировки, весьма мала. Это не означает, что вариант wait не будет полезным при некоторых условиях. Если обработчик исключении в клиентском приложении обрабатывает конфликты, посто- янно повторяя обращение без пауз, то перегрузка системы, вызванная этими посто- янными повторениями и отказами, будет большим злом, чем, если бы было указано wait, особенно если блокирующая транзакция требует много времени для заверше- ния. В сравнении с этим wait может приводить только к одному исключению, обра- батываемому с помощью одного отката. Когда вероятность столкновений транзакций высока, но транзакции короткие, wait является предпочтительным, потому что гарантирует, что ожидающие запросы будут обрабатываться в последовательности FIFO (First In First Out, первый пришел — пер- вый ушел). При этом в пользовательских системах, где быстрые операции не гаран- тированы, транзакции wait противопоказаны, потому что потенциально задерживают сборку мусора. NO WAIT В транзакции no wait сервер немедленно сообщит клиенту об обнаружении новой неподтвержденной версии строки, которую пытается изменять транзакция. В разумно нагруженных многопользовательских системах no wait часто является предпочти- тельным для устранения риска перегрузки системы ожидающими транзакциями. По эмпирическому правилу для транзакций snapshot производительность будет вы- ше, а пользовательские интерфейсы более оперативными, если клиентское приложе- ние выберет no wait и будет обрабатывать конфликты блокировок, используя откат, синхронизированный повтор операции или другие подходящие техники. Резервирование таблиц Firebird поддерживает режим блокировки таблиц для обеспечения полной блокиров- ки одной или более таблиц в процессе выполнения транзакции. Необязательное предложение reserving <список таблиц> запрашивает немедленную полную блоки- ровку всех подтвержденных строк указанных таблиц, предоставляя транзакции ис- ключительный доступ за счет любых транзакций, конкурирующих с этой транз- акцией.
558 Часть VI. Транзакции В отличие от обычной тактики блокирования резервирование блокирует все строки пессимистически — оно действует с момента запуска транзакции, а не ожидает мо- мента, когда потребуется блокировка индивидуальной строки. Резервирование таблиц имеет три основные цели. ♦ Обеспечение блокировки таблиц на момент начала транзакции, а не в то время, когда к ним обращается первый оператор, что происходит в случае изоляции table stability, используемом для блокировки на уровне таблиц. Режим разре- шения блокировки (wait/nowait) применяется во время запроса транзакции, когда возникает конфликт с другими транзакциями, имеющими изменения данных, ожидающих завершения. В этом случае результатом будет обработка запроса при wait и отказ в обработке при nowait. Такая возможность резервирования таблиц важна, потому что резко сокращает вероятность взаимных блокировок. ♦ Обеспечение зависимой блокировки (т. е. блокировки таблиц, к которым могут происходить обращения в триггерах и в ограничениях целостности). Зависимая блокировка не является нормальной в Firebird. При этом она устраняет конфликты изменения, появляющиеся при конфликтах непрямых зависимостей. ♦ Увеличение приоритета транзакции в отношении одной или более таблиц, с кото- рыми она будет работать. Например, транзакция snapshot, которой нужен исклю- чительный доступ по записи ко всем строкам какой-либо таблицы, может ее заре- зервировать, оставляя обычным приоритет по отношению к строкам других таб- лиц. Это менее агрессивный способ применения блокировки на уровне таблицы, чем альтернативный, использующий уровень изоляции table stability. В транзакции вы можете резервировать более одной таблицы. Использование резервирования таблиц Использование резервирования таблиц с уровнями изоляции snapshot и read committed является более предпочтительным, чем с snapshot table stability, когда требуется блокировка на уровне таблицы. Резервирование таблиц является менее агрессивным и более гибким способом предварительной блокировки таблиц. Он доступен для ис- пользования с любым уровнем изоляции. При этом его использование с snapshot table stability не рекомендуется, поскольку он не имеет эффекта при ограничениях доступа к таблицам, к которым транзакции может понадобиться доступ и которые находятся за пределами предложения reserving. Преимущественная блокировка таблиц не является средством для ежедневного ис- пользования, но она может быть полезной в таких задачах, как предварительная оценка отчетности или получение отчета по "замороженным запасам" перед инвента- ризацией. Параметры резервирования таблиц Каждое резервирование таблиц может быть сконфигурировано с помощью различ- ных атрибутов для задания того, как должны трактоваться разные транзакции при запросах доступа к зарезервированным таблицам.
Гпава 26. Конфигурирование транзакций 559 С ПРИМЕЧАНИЕ. Транзакция snapshot table stability не может получить дос- туп ни к какой таблице, зарезервированной с помощью средства резервирования таблиц. Вариантами выбора являются: [PROTECTED | SHARED) {READ | WRITE) Атрибут protected предоставляет транзакции исключительный доступ к таблице по чтению и позволяет другим транзакциям с уровнями изоляции snapshot и read committed читать строки. Запись ограничивается одним или двумя модификаторами: ♦ protected write позволяет текущей транзакции писать в таблицу и блокирует запись другими транзакциями; ♦ protected read запрещает запись в таблицу для любой транзакции, включая теку- щую. Атрибут shared позволяет любой транзакции snapshot или read committed читать из таблицы и предоставляет два режима для параллельных изменений другими транзак- циями: ♦ shared write любой транзакции чтения/записи snapshot или транзакции чте- ния/записи read committed изменять строки в наборе, пока никакая транзакция не имеет или не запросит исключительного доступа по записи; ♦ shared read является наиболее либеральным условием резервирования. Этот ре- жим позволяет любой другой транзакции чтения/записи изменять таблицу. Итоги Любая другая транзакция может читать таблицу, зарезервированную текущей тран- закцией, причем не существует никаких способов сконфигурировать транзакцию так, чтобы предоставить ей исключительные права на запись для этой таблицы (т. е. все могут читать, если они были сконфигурированы только для чтения и не имеют ника- ких преимущественных прав на запись). Следующие условия всегда будут блокиро- вать другую транзакцию на чтение из таблицы, зарезервированной текущей транзак- цией: ♦ другая транзакция имеет уровень изоляции snapshot table stability; ♦ другая транзакция сконфигурирована на резервирование этой таблицы в режиме protected write (хотя она может читать эту таблицу, если текущая транзакция за- резервировала ее в режиме shared read); ♦ другая транзакция собирается зарезервировать эту таблицу в режиме shared write, а текущая транзакция зарезервировала ее в режиме protected read или protected write. В случае если это все еще вам непонятно, посмотрите на рис. 26.1, где некоторые сконфигурированные транзакции сами все расскажут.
560 Часть VI. Транзакции Рис. 26.1. Конфигурирование резервирования таблиц Версии записей Когда запрос на изменение успешно отправлен на сервер, Firebird создает и записы- вает на диск ссылку, связывающую оригинальный образ строки, видимый в транзак- ции — иногда это называется дельтой, — с новой версией строки, содержащей изме- нения запроса. Оригинальный и новый образы строки называются версиями записи. Когда новая версия записи создается в транзакции более новой, чем транзакция, ко- торая создала "живую" версию, другие транзакции не будут иметь возможности из- менять или удалять оригинал, если только транзакция, создавшая новую версию, не выполнит откат. Процесс создания версий подробно описан в главе 25. Пока транзакция, в конце концов, не подтвердит изменения, она не касается "живой" версии. В своем собственном контексте она трактует отправленную версию, как если бы она была самой последней подтвержденной версией. Тем временем другие тран- закции продолжают "видеть" самую последнюю подтвержденную версию. В случае "мгновенного снимка" базы данных (snapshot) в транзакциях, которые были запуще- ны до нашей транзакции, последняя подтвержденная версия записи, которую они видят, может быть более старой, чем та, которую видит наша транзакция и другие транзакции, либо ранее запущенные, либо имеющие уровень изоляции read committed.
Гпава 26. Конфигурирование транзакций 561 Зависимые строки Если на таблицу, изменение в которой было отправлено на сервер, ссылаются внеш- ние ключи других таблиц, сервер создает версии строк в этих таблицах, которые "принадлежат" измененной строке1. Эти зависимые строки, равно как и другие стро- ки других таблиц, зависящие от них через внешние ключи, также становятся недос- тупными другим транзакциям для изменения, пока выполняется наша транзакция. Блокировки и конфликты блокировок В Firebird блокировки управляются относительным возрастом транзакций, а записи управляются системой поддержки версий. Все блокировки применяются на уровне строки, за исключением тех случаев, когда транзакция оперирует на уровне изоляции snapshot table stability или когда используется резервирование таблицы, которое блокирует доступ по записи. Время действия Время действия блокировки строки при обычной активности чтения/записи является оптимистическим — не выполняется никакая блокировка никаких строк до того мо- мента, когда она действительно нужна. Пока изменения строки не отправлены на сервер, строка свободна для "получения" любой транзакцией чтения/записи. Пессимистическая блокировка Пессимистическая, или предварительная, блокировка может быть применена для на- боров строк или для целых таблиц. Режимы блокировки таблиц уже были описаны (см. разд. "Резервирование таблиц" и "SNAPSHOT TABLE STABILITY (согласован- ность)"). Пессимистическая блокировка на уровне строки и на уровне набора является режи- мом, при котором требование по резервированию строки или небольшого набора вы- полняется до фактической пересылки изменения или удаления. Явная блокировка Возможность осуществлять явную пессимистическую блокировку строки была до- бавлена в синтаксис SQL-оператора select в Firebird 1.5. Блокировка ограничена "внешним уровнем" операторов select, которые возвращают выходные наборы или определяют курсоры. Она не может применяться к подзапросам. Сокращенный синтаксис запроса явной пессимистической блокировки строки: SELECT <выходаой-список> FROM «таблица -или-гроцедура-или-просмотр> 1 Возможно, здесь имеются в виду внешние ключи с опцией on update/delete cascade. В этом случае при изменении мастер-записи автоматически модифицируются записи-детали. Ни в каких других случаях "зависимые" таблицы не обновляются сервером. — Прим. науч. ред.
562 Часть VI. Транзакции [WHERE <условия-поиска>] [GROUP BY <спецификация-труппирования>] [UNION <выражение-выбора> [ALL]] [PLAN <вьражение-плана>] [ORDER BY <список~столбцов>] [FOR UPDATE [OF столбец! [, столбец2 ...]] [WITH LOCK]] Предложение for update, не являющееся инструкцией блокировки, указывает, что выходной набор должен передаваться клиенту по одной строке за раз, а не в виде пакета. Необязательная фраза with lock является элементом, который задает предва- рительную блокировку строки, как только сервер передает ее с сервера. Строки, ожи- дающие вывода, не блокируются. Фиктивные изменения Традиционным способом получения пессимистической блокировки строки в Firebird являются фиктивные изменения (dummy updates). Этот трюк позволяет использовать преимущества многоверсионной архитектуры. Клиент просто посылает на сервер для строки оператор изменения, который ничего не изменяет — он просто устанавливает значение столбца в его текущее значение, что приводит к тому, что сервер создает новую версию записи и, следовательно, блокирует для других транзакций запрос на изменение или удаление этой строки. Условия, при которых пессимистическая блокировка может быть полезной, и техни- ческие рекомендации по ее использованию обсуждаются в главе 27. Конфликты блокировки Конфликт блокировки появляется, когда конкурирующие транзакции пытаются из- менить или удалить одну и ту же строку в то время, когда вид состояния базы данных для этих транзакций частично перекрывается. Конфликты блокировок являются за- планированным результатом уровней изоляции транзакций в Firebird и стратегии поддержания многих версий записи, защищая изменяемые данные от неконтроли- руемой перезаписи параллельными операциями над одними и теми же данными. Эта стратегия работает хорошо только в случае двух условий, которые приводят к конфликтам блокировки. ♦ Условие 1: одна транзакция (наша транзакция) отправила на сервер изменение или удаление строки. В это время другая транзакция, стартовавшая до того, как наша транзакция заблокировала эту строку, пытается ее изменить или удалить. Другая транзакция получает конфликт блокировки и имеет два варианта выбора: • она может отменить свою попытку и позже снова повторить ее для вновь под- твержденной версии строки; • она может ждать, пока наша транзакция либо подтвердит, либо отменит свою работу. ♦ Условие 2: наша транзакция блокирует для записи целую таблицу, если имеет уровень изоляции для таблицы snapshot table stability или использует резерви-
Глава 26. Конфигурирование транзакций 563 рование таблицы с помощью protected, а другая транзакция пытается изменить или удалить строку или добавить новую строку. Предположим, что наша транзакция посылает изменение строки. Появляется другая транзакция и запрашивает изменение или удаление той же строки. При уровне изоля- ции snapshot и режиме wait другая транзакция будет ожидать, пока наша транзакция завершит свою работу путем подтверждения или отката. Если наша транзакция подтверждает изменения, то другая транзакция получит отказ по конфликту изменения. Клиент, который запустил другую транзакцию, должен иметь обработчик исключений, который либо откатит транзакцию и заново ее запус- тит для повторной выдачи запроса, либо просто подтвердит транзакцию и завершит работу. Вызов commit в обработчике исключения конфликта блокировки не рекомендуется, поскольку он разрушает атомарность транзакции — некоторая работа будет заверше- на, некоторая нет, и впоследствии будет невозможно предсказать состояние базы данных2. К сожалению, Firebird имеет тенденцию объединять все исключения блокировки и сообщает о них как "deadlock" (взаимная блокировка). Нормальные случаи, которые были описаны, не являются взаимной блокировкой. Что такое взаимная блокировка? Взаимная блокировка является просто сокращенным названием, заимствованным из реслинга, для условия, когда две транзакции борются за изменение строк в перекры- вающихся наборах и ни одна транзакция не имеет приоритета перед другой. Например, наша транзакция имеет изменения, ожидающие завершения, для строки х и собирается изменять строку y в то время, как другая транзакция имеет изменения, ожидающие завершения, для строки у и собирается изменять строку х, и обе транзак- ции имеют режим wait. Как и в реслинге, взаимная блокировка может быть разреше- на, только если один из борцов не удержит руку. Одна транзакция должна выполнить откат, чтобы позволить другой подтвердить свои изменения. Firebird предоставляет приложениям средство разрешения взаимной блокировки пу- тем сканирования блокировок каждые несколько секунд. Он произвольно выберет одну из транзакций -взаимной блокировки и выдаст для нее исключение взаимной блокировки. Разработчики не должны бояться сообщений о взаимной блокировке. Наоборот, име- ет смысл изолировать работу многих пользователей в контекстах транзакций. Вы должны допускать наличие взаимных блокировок и эффективно их обрабатывать в вашем приложении. 2 Ничто не запрещает завершить транзакцию по commit, независимо от успеха или неуспеха выполнения в ней операторов. Разумеется, именно в обработчиках исключений вызывать commit действительно не рекомендуется. — Прим. науч. ред.
564 Часть VI. Транзакции Если наша транзакция выбрана для разрешения взаимной блокировки, обработчик исключения в приложении должен выполнить откат транзакции, чтобы позволить другой транзакции возобновить и завершить свою работу. Альтернатива — подтвер- ждение транзакции в обработчике исключения — не рекомендуется, поскольку наша транзакция станет неатомарной, а другая транзакция даст сбой по конфликту блоки- ровки. Тупиковая ситуация В редких случаях более двух транзакций может быть включено во взаимную блоки- ровку при борьбе за перекрывающиеся наборы. Иногда это называется тупиковой ситуацией (программистский жаргон — deadly embrace, смертельные объятия). Ска- нирование блокировок выберет одну транзакцию (нашу транзакцию), ситуация будет обработана в клиентском обработчике исключений, как в предыдущем примере. При этом даже если клиент выполнит откат нашей транзакции, те другие транзакции все еще останутся заблокированными. Активный тупик Клиент может запустить новую транзакцию и повторить попытку, однако другие со- перники все еще находятся в состоянии взаимной блокировки, ожидая следующего сканирования блокировок для освобождения следующего соперника с выдачей ис- ключения блокировки. Если приложение повторяет попытку с транзакцией wait, он просто ждет бесконечное время, когда будет разрешена тупиковая ситуация с други- ми транзакциями. Про транзакцию с такими тщетными попытками повтора говорят, что она находится в активном тупике. Короче говоря, важно в первую очередь исключить контексты транзакций, которые могут привести к тупиковой ситуации. В качестве дополнительной защиты обработ- чики исключений должны быть способны быстро обрабатывать блокировки и обес- печивать, чтобы проблемные транзакции завершались чисто и без задержек. Пора дальше Далее мы рассмотрим транзакции с точки зрения разработчика клиентских приложе- ний. Темы в этой главе являются нейтральными к включающим языкам. Тем не менее все современные средства разработки приложений и драйверы для Firebird ис- пользуют один и тот же API в той или иной форме, что хорошо для одного, хорошо и для другого.
ГЛАВА 27 Программирование с транзакциями Транзакция является начальной точкой для всех взаимодействий клиентского прило- жения с сервером. В этой главе мы с точки зрения различных интерфейсов клиента рассмотрим запуск, управление и завершение транзакций. Многие языки и средства разработки имеют интерфейс с Firebird. Подробное описа- ние управления транзакциями Firebird в каждом из них находится за пределами дан- ной книги. Язык для транзакций Важно обратиться к средствам реализации транзакций в Firebird. До сих пор некото- рые связанные с транзакциями особенности вовсе не реализованы в динамическом SQL, а только через API. Среди небольшого количества связанных с транзакциями операторов SQL и доступных в подмножестве DSQL только commit и rollback дос- тупны в каждом интерфейсе. В книге осознанно выбран нейтральный подход к язы- кам средств разработки. Основной акцент делается на динамический SQL, исполь- зуемый в большинстве существующих средств разработки клиентских приложений. В главе представлены некоторые проблемы, одинаково актуальные и для автора, и для читателя. Хотя эта глава не описывает ESQL1 или API* 2, следующие разделы будут рассказы- вать о них в более или менее общем виде для получения некоторого понимания того, что передается через интерфейс, когда клиенты "беседуют" с серверами о транзак- циях. ESQL Надмножество операторов SQL и подобных SQL операторов, используемых в преж- ние времена, даже до публикации API и подмножества DSQL, представляет синтак- сис стандарта set transaction для конфигурирования и старта транзакций. В некото- Для энтузиастов Borland опубликовал документ "Embedded SQL Guide" (Руководство по встраиваемому SQL) как часть комплекта документации по InterBase. Версии этого тома также доступны в формате PDF на некоторых сайтах — найдите через Google "EmbedSQL.pdf". 2 То же, "API Guide" (Руководство no API). APIGuide.pdf.
566 Часть VI. Транзакции рых формах он доступен в DSQL и может быть использован в утилите isql. Это удоб- ное средство общей проверки того, как API передает эквивалентную информацию3. API API предоставляет гибкий интерфейс множества сложных функций программистам С и C++ для создания клиентских приложений с наиболее тонким уровнем связи и со- единения. Группа функций API реализует эквивалентные операторы SQL, относя- щиеся к транзакциям, например isc_start_transaction() для оператора start TRANSACTION И isc_commit_transaction () ДЛЯ оператора COMMIT. Заголовочный файл API, ibase.h, объявляет прототипы функций, определения типов для каждой структуры, определения параметров и макросы, которые используются в функциях. Он поставляется в каталоге Firebird /include. Для некоторых объектно-ориентированных сред разработки, таких как Object Pascal, Borland C++ Builder, Java, PHP, Python и DBI::Perl классы и компоненты полностью инкапсулируют вызовы API Firebird, относящиеся к транзакциям. Пользовательские драйверы для интерфейсов соединения со стандартными базами данных SQL — в особенности ODBC, JDBC и .NET — похожим образом представляют API4. Запуск транзакции SQL Оператор SQL для запуска транзакции имеет следующий синтаксис: SET TRANSACTION [NAME <имя-транэакции>] [READ WRITE | READ ONLY] /* режим доступа */ [WAIT I NO WAIT] /* режим разрешения блокировок */ [ISOLATION LEVEL] /* уровень изоляции */ {SNAPSHOT [TABLE STABILITY] | READ COMMITTED [[NO] RECORD VERSION]]} [RESERVING <предложение-резервирования> I USING <дескриптор-базы~данных> [, дескриптор-базы-данных; Финальное предложение reserving задает необязательное резервирование таблиц, обсуждавшееся в предыдущей главе. Его синтаксис представляется в виде: set transaction, как commit и rollback, не является оператором, выполняемым на сер- вере. Будучи оператором Embedded SQL он преобразуется утилитой gpre (или isql) в вызовы Firebird API (isc_start_transaction...). — Прим. науч. ред. 4 Для большинства этих реализаций была выполнена трансляция заголовочного файла С Firebird в соответствующий язык высокого уровня для предоставления средств API Firebird (или InterBase) для этого языка. Если вы планируете разрабатывать программы с использова- нием API, хорошим решением будет поиск в Интернете существующих трансляций заголовоч- ного файла.
Глава 27. Программирование с транзакциями 567 предложение-резервирования> : := <таблица> [, <таблица> ...] [FOR [SHARED ! PROTECTED] {READ | WRITE}] [, предложение-резервирований [, <предложение-резервирования> ...]] < ПРИМЕЧАНИЕ. Необязательное имя транзакции — объявляемое в приложении и указываемое в предложении set transaction— недоступно нигде, кроме —ESQL. Вы можете проверить этот оператор в isql. Откройте базу данных employee.fdb и за- пустите новую транзакцию следующим образом: SQL> COMMIT; SQL> SET TRANSACTION READ WRITE ISOLATION LEVEL SNAPSHOT TABLE STABILITY; SQL> SELECT EMP_NO, FIRST_NAME, LAST^NAME CON> FROM EMPLOYEE WHERE FIRST_NAME = 'Robert'; EMP__NO FIRST_NAME LAST_NAME 2 Robert Nelson SQL> СОВЕТ. Ключевые слова isolation level являются необязательными, read write и wait являются значениями по умолчанию и могут быть опущены, если не требуются другие значения. Затем откройте другое окно командной строки и запустите isql или другой инстру- мент, который позволит вам конфигурировать транзакции, и запустите другую тран- закцию чтения/записи для той же базы данных: SQL> COMMIT; SQL> SET TRANSACTION READ WRITE NOWAIT SNAPSHOT; Теперь через тот же интерфейс попытайтесь изменить любую строку в базе данных employee, fdb: SQL> UPDATE EMPLOYEE SET FIRST_NAME = 'Dodger' SQL> WHERE FIRST_NAME = ' Roger' ; ISC ERROR CODE:335544345 lock conflict on no wait transaction (конфликт блокировки для транзакции, не являющейся ожидающей) Как и ожидалось, эта транзакция будет заблокирована для записи любой строки таб- лицы employee, потому что при выборе данных из той же таблицы первая транзакция запросила блокировку на уровне таблицы. Вторая транзакция имеет установку раз- решения блокировки nowait и немедленно выдает исключение, потому что не может выполнить изменение.
568 Часть VI. Транзакции Транзакция по умолчанию Следующий оператор также допустим: SQL> COMMIT; SQL> SET TRANSACTION; Он запускает транзакцию, как и большинство операторов, с конфигурацией по умол- чанию, что эквивалентно: SQL> COMMIT; SQL> SET TRANSACTION READ WRITE WAIT SNAPSHOT; ПРИМЕЧАНИЕ. Существует еще "транзакция по умолчанию", которая использу- ется в клиентах ESQL для сконфигурированной единой транзакции, определен- ной на сервере и клиенте константой gds_trans. Клиент ESQL стартует эту тран- закцию автоматически, если он передает оператор запроса, а перед этим никакая транзакция явно не запускалась. API Функция API, которая выполняет эквивалентную работу, называется isc_start_ transaction () Запуск каждой транзакции имеет три части: 1. Создание (при необходимости) и инициализация дескриптора транзакции. 2. Создание (при необходимости) и заполнение буфера параметров транзакции (Transaction Parameter Buffer, ТРВ) для хранения данных конфигурации. Это не обязательно. 3. ВЫЗОВ isc_start_transaction (). При отсутствии необязательного ТРВ клиент запускает транзакцию точно так же, как и транзакцию по умолчанию, которая стартует при выдаче оператора set transaction. Дескриптор транзакции Каждый раз, когда вы собираетесь вызвать эту функцию, вы должны иметь перемен- ную, длинный указатель — называемую дескриптором транзакции — уже объяв- 5 Фактически существуют две различные функции для запуска транзакции: isc__start_ transaction () используется в языках, которые поддерживают передачу переменного количе- ства аргументов при вызове функций; isc_start_multiple () используется в языках, требую- щих статический список аргументов. isc_start_transaction() ограничивается одной тран- закцией, которая может использовать до 16 баз данных, в то время как iso_start_multiple О запускает транзакции, обращающиеся к большему количеству баз данных или обращающиеся к переменному количеству баз данных, которое может превышать 16.
Гпава 27. Программирование с транзакциями 569 ленную в вашем приложении и инициализированную нулем6. Приложение должно иметь один дескриптор транзакции для каждой конкурирующей транзакции, вы так- же можете повторно использовать дескрипторы, заново проинициализировав их. Дескриптор транзакции должен быть установлен в ноль при его инициализации до запуска транзакции. Транзакция завершится с ошибкой, если ей будет передан нену- левой дескриптор. Буфер параметров транзакции ТРВ является байтовым массивом (или вектором) констант, каждая из которых пред- ставляет параметр транзакции и начинается с префикса isc_tpb_. Первым параметром всегда является константа isc_tpb_version3, которая определяет версию структуры ТРВ7. Все последующие элементы массива являются константами, которые представ- ляют эквивалентные SQL атрибуты транзакции. В табл. 27.1 показаны параметры транзакции SQL и эквивалентные им константы ТРВ. Типичное объявление ТРВ в языке С выглядит следующим образом: static char isc_tpb[] - { i s c_tpb_ye r s i опЗ, isc_tpb_write, isc_tpb_wait, isc_read_committed, i sc_tpb_no_r e c_ve r s i on }; Этот ТРВ по своему действию идентичен следующему: SET TRANSACTION READ WRITE WAIT READ COMMITTED NO RECORDJ7ERSION; Вы можете использовать один ТРВ для всех транзакций, которым требуются одина- ковые характеристики. Хорошим решением также является использование отдельно- го ТРВ для каждой транзакции. Классы транзакций обычно создают экземпляр ТРВ при необходимости, каждый экземпляр хранит установки интерфейса времени проек- тирования или времени выполнения в виде свойств, допускающих чтение и запись (или, иначе, элементы данных). В большинстве случаев вы можете распознать константу ТРВ по имени свойства (табл. 27.1). В стандартных драйверах DBC имена классов и их элементов больше всего диктуются требованиями стандарта и описывают атрибуты с большим соответ- ствием задаваемой функциональности. 6 Тип isc_tr_handle является указателем void, определенным в заголовочном файле ibase.h, который расположен в каталоге /include в каталоге вашей инсталляции Firebird. 7 Если в будущем структура ТРВ изменится, эта константа также будет изменена, так что сервер легко сможет обрабатывать клиентов, скомпилированных в другой версии.
570 Часть VI. Транзакции Таблица 27.1. Атрибуты транзакции и эквивалентные константы ТРВ Тип атрибута Атрибут SQL Константа ТРВ Режим доступа READ ONLY i s С—t pb_г ead. READ WRITE i s c_tpb_wri t e Уровень изоляции READ COMMITTED i s c_tpb_read_commi 11 e d SNAPSHOT isc_tpb_concurrency SNAPSHOT TABLE STABILITY isc_tpb_consistency Режим разрешения блокировок WAIT isc_tpb_wait NO WAIT isc_tpb_nowait Версия записи RECORD_VERSION i s c_re C—ve rs ion NO RECORD—VERSION i s c_no_r e c_ve rs i on Резервирование таблиц SHARED isc_tpb_shared PROTECTED isc_tpb_jprotected READ isc_tpb_lock_read WRITE isc_tpb_lock_write Нет эквивалента SQL Отключает протокол авто- отмены isc_tpb_no_auto_undo Протокол автоотмены По умолчанию сервер поддерживает в памяти протокол внутренних точек сохране- ния (internal savepoint log) для добавляемых и изменяемых строк. При нормальном течении процесса записи протокола удаляются при каждом подтверждении или отка- те транзакции. Однако при некоторых условиях система отказывается от протокола и напрямую обращается к глобальному образу состояния транзакции (TSB). Такой пе- реход обычно происходит, когда отменится очень большое количество добавлений или транзакция, использующая множество пользовательских точек сохранения (см. разд. "Вложенные транзакции"), исчерпает возможности этого протокола. Приложение может отключить использование в транзакции этого протокола автоот- мены, передав константу isc_tpb_no_auto_undo в ТРВ. Доступ к идентификатору транзакции Начиная с Firebird 1.5 идентификатор (TID) текущей транзакции (который получает- ся из хранимых данных состояния транзакции) доступен в виде контекстной пере- менной. Он доступен в DSQL, isql, триггерах и хранимых процедурах. Это перемен- ная CURRENT-TRANSACTION. Например, для получения идентификатора в запросе вы можете выполнить следую- щее: SELECT CURRENT_TRANSACTION AS TRAN_ID FROM RDB$DATABASE;
Гпава 27. Программирование с транзакциями 571 Для сохранения его в таблице нужно выполнить: INSERT INTO TASK_LOG (USER_NAME, TRAN_ID, START_TIMESTAMP) VALUES (CURRENTJJSER, CURRENT_TRANSACTION, CURRENT_TIMESTAMP) ; /А ВНИМАНИЕ! Идентификаторы транзакции не являются достаточно стабильны- / ! \ ми, чтобы их использовать в качестве ключей. Серия номеров для идентифика- торов транзакций будет устанавливаться в 1 каждый раз при восстановлении базы данных. Firebird 1.0.x не предоставляет механизма отслеживания транзакций. Использование TID в приложениях Идентификатор транзакции на сервере не является тем же самым, что и дескриптор транзакции, который клиент Firebird получает в свое приложение. Довольно обосно- ванным является ассоциирование идентификатора транзакции с дескриптором тран- закции при условии, что вы помните о необходимости изменить отношение между идентификатором и дескриптором транзакции при новой инициализации дескрипто- ра. Каждое использование дескриптора в вашем приложении должно быть атомар- ным. Идентификатор транзакции может быть полезным при отслеживании приложений и пользователей, ответственных за долгие транзакции в системе, ухудшающие ее про- изводительность. Служебная программа циклического отслеживания может записы- вать на сервер время старта и завершения транзакций (подтверждение или отмена). В частности, отсутствие времени завершения будет полезным для отыскания пользователей, привычно использующих кнопку "восстановить" для завершения задач, или приложений, в которых регулярно происходит отказ при подтверждении работы. Процесс выполнения транзакции К настоящему моменту у вас уже должно быть чувство уверенности по поводу вида операций многопользовательской системы, как серии атомарных задач, изолирован- ных в транзакциях. Задачи будут выполнены, когда клиенты запускают транзакции и когда клиенты их завершают. Помимо конфигурирования транзакции для задания задаче условий доступа, изоляции и способа разрешения блокировок, клиент имеет другие возможности для взаимодействия с транзакцией в процессе ее выполнения. На рис. 27.1 показаны подобные точки этого взаимодействия. В остальных разделах настоящей главы более подробно рассматриваются техника и реализация таких задач. Вложенные транзакции В Firebird транзакции всегда запускаются и завершаются клиентом. Некоторые дру- гие СУБД могут запускать и подтверждать транзакции из хранимых процедур, пото- му что для управления транзакциями они используют двухфазную блокировку тран-
572 Часть VI. Транзакции CLIENT START TRANSACTIQN------------ i I REQUESTS TO READ i i Select... I i Select... : I FOR UPDATE —i i Select... i I FOR UPDATE I i OF <со1итп1[, columnz...] I I Select...WITH LOCK___________ : | I REQUESTS TO EXECUTE] ___________I : Execute Procedure i [REQUESTS TO WRITE i i Insert i i | Update "POST" i i | Delete_______________________ ] i SAVEPOINT <savepoint-name>--------p- I |REQUESTS TO WRITE, ----------[ I ROLLBACK TO <savepoint-name> — i [REQUESTS TO WRITE I !-COMMIT WITH RETAIN -----.....--- i (CommitRetaining) [ — COMMIT WORK---.....:.. j— RollbackRetaining....—-——--------I ROLLBACK -............. --...-- - I; handle ---------► EXCEPTION POST EVENT ◄-----=----- ---------► -----► —► ---► —► > —► ----► > SERVER DATABASE Transaction State [FSB] Bitmap J Рис. 27.1. Взаимодействие приложения и транзакции закций. Вместо этого Firebird предоставляет другие механизмы, которые могут опе- рировать с потоком работы в транзакции без нарушения атомарности. Два из этих механизмов, блоки обработчика исключений и выполняемые строки, ограничены по использованию только в модулях PSQL и обсуждаются в части VII. Другим меха- низмом, недоступным в PSQL, являются пользовательские точки сохранения. ПРИМЕЧАНИЕ. Выполняемые строки и пользовательские точки сохранения яв- ляются новыми возможностями, добавленными в язык в Firebird 1.5. Пользовательские точки сохранения Операторы пользовательских точек сохранения (user savepoints), также называемые вложенными транзакциями, позволяют вам "упаковать" группы операций внутри транзакции и отмечать их, если пересылка в базу данных была успешной. Если позже в последовательности задач появится исключение, транзакция может выполнить от- кат к последней точке сохранения. Операции, отправленные в базу между этой точ- кой сохранения и возникшим исключением, будут отменены, а приложение может выполнить корректировку, подтверждение, полный откат, продолжить работу — в зависимости от того, что требуется сделать.
Глава 27. Программирование с транзакциями 573 Создание пользовательских точек сохранения Пользовательские точки сохранения являются операцией клиентской стороны, дос- тупной только в операторах DSQL. Оператором создания пользовательской точки сохранения является: SAVEPOINT <гщентификатор>; Идентификатор может быть любым правильным идентификатором SQL Firebird (максимум 31 алфавитно-цифровой символ ASCII, уникальный в транзакции). Вы можете заново использовать тот же идентификатор в той же транзакции, он перекро- ет существующую точку сохранения, связанную с этим именем. Возврат на точку сохранения Возврат (или откат, rollback) на точку сохранения начинается с отмены всей работы, выполненной и отправленной в базу данных после создания этой точки сохранения. Указанная точка сохранения и все предшествующие ей сохраняются. Любые точки сохранения, которые были созданы после указанной точки сохранения, пропадают. Любые блокировки (явные и неявные), которые были установлены с момента созда- ния указанной точки сохранения, будут сняты. При этом транзакции, которые были заблокированы при ожидании доступа к освобожденным строкам, все еще не получат к ним доступ, пока не завершится текущая транзакция. Это гарантирует, что ожи- дающие в настоящий момент конкуренты не повлияют на поток работы, когда он будет возобновлен. Все это не повлияет на транзакции, которые не находились в со- стоянии ожидания в момент вложенного отката. Вот шаблон синтаксиса для отката на точку сохранения: ROLLBACK [WORK] ТО [SAVEPOINT] <идентификатор>; Если транзакция позволяет продолжить работу после отката на точку сохранения, то в дальнейшем работа может осуществлять откат на эту точку сохранения столько раз, сколько потребуется. Версии записей, которые были отменены откатом, не будут доступны для сборщика мусора, потому что транзакция все еще является активной. Освобождение точек сохранения Механизм реализации точек сохранения на сервере — протокол в памяти — может требовать значительного количества ресурсов, особенно если одни и те же строки изменяются многократно в процессе выполнения задачи. Ресурсы уже ненужных то- чек сохранения могут быть освобождены при использовании оператора release savepoint: RELEASE SAVEPOINT <идентификатор> [ONLY]; Без ключевого слова only указанная точка сохранения и все точки сохранения, кото- рые были созданы после нее, будут освобождены и потеряны. Используйте only для освобождения только указанной точки сохранения.
574 Часть VI. Транзакции Следующий пример иллюстрирует работу точек сохранения: CREATE TABLE SAVEPOINT__TEST (ID INTEGER); COMMIT; INSERT INTO SAVEPOINT_TEST VALUES(99) ; COMMIT; INSERT INTO SAVEPOINT_TEST VALUES(100) ; /**/ SAVEPOINT SP1; /“/ DELETE FROM SAVEPOINT_TEST; SELECT * FROM SAVEPOINT JTEST; не вернет ничего */ /“/ ROLLBACK TO SP1; /**/ SELECT * FROM SAVEPOINT_TEST; г вернет 2 строки */ ROLLBACK; /**/ SELECT * FROM SAVEPOINT_TEST; г вернет одну подтвержденную строку */ Внутренние точки сохранения Когда ядро сервера выполняет откат, его контрольная точка по умолчанию для отме- няемой транзакции ссылается на внутреннюю точку сохранения, находящуюся в про- токоле автоотмены в оперативной памяти. При завершении отката он подтверждает транзакцию. Целью этой стратегии является сокращение количества мусора, порож- даемого откатами. Когда объем изменений, выполненных перед точкой сохранения на уровне транзак- ции, становится большим — в пределах от 10 000 до 1 миллиона строк — ядро серве- ра прекращает использовать протокол автоотмены и получает ссылки напрямую из глобального образа состояния транзакции (TSB). Если у вас есть транзакция, в кото- рой цы предполагаете выполнение операции с большим количеством изменений, от- ключение ведения протокола автоотмены предотвратит утечку потребляемых ресур- сов, которая появится, если ваш сервер решит отменить ведение протокола. Подроб- ности см. в разд. "Протокол автоотмены" ранее в этой главе. PSQL Расширения для обработки исключений Эквивалентом точек сохранения в модулях PSQL является обработка исключений. Каждый блок PSQL для обработки исключений также ограничен автоматической системой точек сохранения. Расширения PSQL предоставляют языковую оболочку для реализации того же типа вложенности транзакций, что и пользовательские точки сохранения в DSQL. Подробности см. в главе 32.
Гпава 27. Программирование с транзакциями 575 Логический контекст Простой способ рассматривать транзакцию между start transaction и commit или rollback — это смотреть на нее как на серию клиентских операций и взаимодействий клиента и сервера, которые точно отображают задачу. Это очень полезная модель для понимания того, как транзакция создает обертку для единицы работы. Эта модель не обязательно точно отражает то, как именно пользователи выполняют конкретные задачи. С пользовательской точки зрения "задача" не ограничена операторами start transaction и commit. Его задача имеет начало, середину и окончание, что может включать множество транзакций. Например, ошибки при пересылке или подтвер- ждении оператора повлекут за собой откат для завершения физической транзакции. Некоторые виды вмешательств могут привести к завершению логической задачи или в нормальном случае потребуют другой физической транзакции для завершения ло- гической задачи. Одна физическая транзакция может включать множество дискретных пользователь- ских задач, формирующих логическое "целое", что требует атомарности единой фи- зической транзакции. Другой вариант— типичная задача "пакетного ввода дан- ных" — выполняет многократные повторения похожей задачи, помещенные внутрь одной физической транзакции для сокращения количества нажатий клавиш клавиа- туры и соответствия пользовательским требованиям выполнения работы. Подводя итог, логическая задача — это то, что мы как разработчики должны проек- тировать и к чему обращаться — почти всегда выходит за пределы границ start transaction и commit. Физическая транзакция при этом является частью логического контекста. Двумя ключевыми факторами в отношении логического контекста транзакции явля- ются: ♦ как сохранять физический контекст начальной транзакции после rollback, чтобы работа пользователя не пропала, когда сервер отменит доступ; ♦ что делать, если поток работы будет прерван исключением — как диагностиро- вать исключение и как его скорректировать. Для решения этих задач мы рассмотрим операции commit и rollback, а также все за и против использования доступных режимов для сохранения логического контекста транзакций. После этого мы рассмотрим вопросы диагностирования исключений, которые могут потребовать повторного запуска транзакций. Завершение транзакций Транзакция завершается, когда клиентское приложение подтверждает ее или отменя- ет. Если оператор commit или вызов эквивалентной функции API isc_commit_ transaction не будут успешными, то транзакция не будет подтверждена. Если тран- закция, которая не может быть подтверждена, не будет отменена явным вызовом клиентом rollback (или функцией API isc_roiiback_transaction), транзакция не будет
576 Часть VI. Транзакции отменена. Эти операторы не являются силлогизмом. Ошибки при завершении тран- закций являются весьма общей проблемой, часто обсуждаемой в публичных конфе- ренциях! Оператор COMMIT Синтаксис оператора commit: COMMIT [WORK] [RETAIN [SNAPSHOT]]; Простой commit — иногда называемый жестким подтверждением по причинам, кото- рые станут понятными, — делает изменения, отправленные в базы данных, постоян- ными и освобождает все физические ресурсы, связанные с транзакцией. Если по раз- личным причинам commit завершается с ошибкой и возвращает клиенту исключение, транзакция остается в неподтвержденном состоянии. Клиентское приложение долж- но обработать ошибку подтверждения транзакции, выполнив ее явный откат или, если это возможно, исправив ошибку и заново выполнив оператор. COMMIT с режимом RETAIN Необязательное расширение retain [snapshot] оператора commit приводит к тому, что сервер сохраняет "образ" контекста физической транзакции и производит запуск но- вой транзакции как клона подтвержденной транзакции. Если это так называемое мяг- кое подтверждение используется в транзакциях snapshot или snapshot table stability, клонируемая транзакция при своем запуске сохраняет тот же образ дан- ных, что и исходная транзакция. Хотя это приводит к постоянному подтверждению работы и, следовательно, изменяет состояние базы данных, commit retain (CommitRetaining) не освобождает ресурсы. На отрезке жизни логической задачи, которая включает в себя множество повторений похожих операций, клонирование контекста уменьшает некоторые накладные расхо- ды, которые могли бы возникнуть при освобождении ресурсов каждый раз при commit; при этом сначала лишь выделяются идентичные ресурсы при старте новой транзакции. В частности, это сохраняет "открытые" в настоящий момент курсоры для выбранных наборов. Тот же самый идентификатор транзакции остается активным в TSB и никогда не ста- новится "подтвержденным". По этой причине такое действие часто называется мяг- ким подтверждением (soft commit) по сравнению с "жестким" подтверждением, вы- полняемым немодифицированным оператором commit. Каждое мягкое подтверждение похоже на точку сохранения без возможности возвра- та к ней. Все последующие операторы rollback отменяют только те изменения, кото- рые были отправлены на сервер после последнего мягкого подтверждения. Преимущество мягкого подтверждения в том, что оно облегчает жизнь программи- ста, особенно тех, кто использует компоненты, которые реализуют поведение "про- кручиваемой базы данных". Это было сделано для поддержки сетки данных (grid) в пользовательском интерфейсе, используемой многими пользователями среды раз- работки Borland Delphi (и C++ Builder). Сохраняя контекст транзакции, приложение
Гпава 27. Программирование с транзакциями 577 может отображать плавный переход состояния до-и-после, что сокращает работу программиста, которому иначе пришлось бы стартовать новые транзакции, откры- вать новые курсоры и заново синхронизировать их с наборами строк, хранящимися на клиенте. Различные реализации доступа к данным часто объединяют пересылку на сервер единичного изменения, добавления или удаления с немедленным commit retain в механизм, дублирующий "автоподтверждение". Общим для уровней интерфейса яв- ляется реализация возможности автоподтверждения для "молчаливого" управления транзакцией, запуская транзакцию невидимо для программиста в ситуациях, когда написанный программистом код пытается передавать оператор без предварительного старта транзакции. Явное управление транзакциями стоит дополнительных усилий, особенно если вы применяете продукт, использующий такие гибкие средства, которые предоставляет Firebird. В загруженном работой окружении вариант commit retain может сократить время выполнения и ресурсы, однако он имеет некоторые серьезные недостатки. ♦ Транзакция snapshot продолжает хранить первоначальный образ базы данных, что означает, что пользователь не видит подтвержденных результатов других тран- закций, которые ожидали подтверждения при старте текущей транзакции. ♦ Пока та же транзакция продолжает выполнять подтверждение в варианте retain, ресурсы, используемые на сервере, не освобождаются, что приводит к чрезмер- ному росту ресурсов памяти, потребляемых TSB. Этот рост сильно ухудшает про- изводительность, в конечном счете "замораживая" сервер, и при неблагоприятных условиях операционной системы даже может привести к краху системы. ♦ Никакие старые версии записей, ставшие устаревшими в результате операций, подтвержденных в транзакциях commit retain, не могут быть включены в сборку мусора, т. к. исходная транзакция никогда не выполняет "жесткого подтвержде- ния". Оператор ROLLBACK Как и commit, оператор rollback освобождает ресурсы на сервере и завершает физиче- ский контекст транзакции. При этом вид состояния базы данных в приложении воз- вращается к тому виду, как если бы эта транзакция никогда не стартовала. В отличие от commit данный оператор никогда не завершается с ошибкой. В логическом контексте "задачи" клиента после отката транзакции ваше приложение должно предоставить пользователю средства для разрешения проблем, которые вы- звали исключение, и для повторного запуска новой транзакции. RollbackRetaining SQL в Firebird не реализует синтаксис retain в rollback так же, как это делается в commit. При этом похожий механизм клонирования реализован в функции API isc_roiiback_retaining (). Она восстанавливает для приложения вид базы данных в то состояние, которое было, когда транзакция получила дескриптор или, в случае тран- закции read committed, в то состояние, которое было при последнем вызове 19 Зак. 420
578 Часть VI. Транзакции ise_roiiback_retaining (). Выделенные для транзакции системные ресурсы не осво- бождаются, курсоры сохраняются. rollback retain имеет те же самые ловушки, что и commit retain — и даже больше. Поскольку отдельные вызовы откатов производятся в ответ на исключение некоторо- го вида, сохранение контекста транзакции также сохранит и причины исключения. rollback retain не должен использоваться в тех случаях, если существует шанс, что ваш последующий код обработки исключения не найдет и не исправит наследуемое исключение. Любые последующие ошибки rollback retain должны обрабатываться с полным откатом транзакции для освобождения ресурсов и устранения проблемы. Диагностирование исключений Основные причины возникновения ошибок при пересылке данных или подтвержде- нии работы включают: ♦ конфликты блокировок; ♦ "плохие данные", полученные от интерфейса пользователя: арифметическое пере- полнение, деление на ноль в выражениях, пустые значения в полях, не допускаю- щих пустых значений, несоответствие набора символов и т. д.; ♦ "хорошие данные", которые нарушают ограничение check или другие проверки; ♦ нарушения первичного или внешнего ключей; ♦ и другие! Firebird распознает большое количество различных (более того, поразительных!) ис- ключений и возвращает код ошибки для их идентификации на двух уровнях: ♦ на высоком уровне существует переменная sqlcode, определенная (более или ме- нее, с ударением на "менее") в стандарте SQL; ♦ на более детальном уровне присутствует gdscode — индикатор большего размера, более точно определяющий тип исключений, которые сгруппированы на преды- дущем уровне в типе sqlcode. SQLCODE В табл. 27.2 представлены значения стандартов SQL-89 и SQL-92, определенные для SQLCODE. Таблица 27.2. Значения sqlcode SQLCODE Сообщение Интерпретация 0 SUCCESS Операция завершилась успешно 1—99 SQLWARNING Предупреждающее или информационное сообщение сервера 100 NOT FOUND Была запрошена операция, для которой не было найдено строк. Это не ошибка — код часто просто сообщает о том, что определено условие "конец файла" или "конец курсора" или что не найдено соответствующих значений
Гпава 27. Программирование с транзакциями 579 Таблица 27.2 (окончание) SQLCODE Сообщение Интерпретация <0 SQLERROR Указывает, что оператор SQL не был завершен. Числа нахо- дятся в диапазоне от -1 до -999 Описание отрицательных значений sqlcode для конкретных ошибок не содержится в стандартах. Отрицательные значения sqlcode в Firebird являются довольно обобщен- ными, они представлены группами, которые в значительной степени получены слу- чайно и очень часто своим составом изумляют. Например, можно установить, что значение sqlcode -204 означает "нечто неизвестное”, однако сам код ничего не гово- рит, что именно неизвестно. GDSCODE Второй уровень, gdscode, предоставляет гораздо больше возможностей определения исключений. Каждый код gdscode является знаковым целым, константы которого представлены в iberror.h (или в interbase.msg, если вы используете версию 1.0.x)8. Как правило, сообщение gdscode достаточно точно указывает, что произошло9. В Firebird 1.5 значительно улучшена информация, передаваемая в сообщениях. Тем не менее gdscode предоставляет для приложений наиболее полезный механизм диаг- ностики; вы можете преобразовать константы в пользовательские сообщения в ва- шем модуле на включающем языке для использования в обработчиках исключений. Получение исключений В следующем примере приложение делает попытку добавить данные в таблицу, ко- торая не существует: INSERT INTO NON_EXISTENT (TEST) VALUES ('ABCDEF'); Следующая информация об ошибке возвращается приложению (утилита администра- тора IB SQL в этом случае): ISC ERROR CODE:335544569 <- GDSCODE Dynamic SQL Error <- corresponding text from firebird.msg 8 Неанглийские версии файла сообщений не были доступны во время написания данной книги. Готовятся проекты трансляции. Если вас интересует локализованный файл сообщений, обратитесь к спискам сообщества Firebird. Сообщите, что вы заинтересованы в участии в про- ектах локализации! 9 Существует несколько неприятных исключений из этого правила. Например, ISC ERROR CODE: 335544321 "Arithmetic exception, numeric overflow, or string truncation" (Арифметиче- ское исключение, числовое переполнение или усечение строки) объединяет такой широкий диапазон возможностей, что оно обычно вызывает состояние изумления даже у программиста, который допустил возможность появления такого исключения.
580 Часть VI. Транзакции SQL error code = -204 <- SQLCODE Table unknown <- corresponding text from firebird.msg NON-EXISTENT Откуда приложение получает коды ошибок и сообщения? Ответ можно найти в век- торе состояния ошибки (error status vector), в массиве, который передается в качест- ве параметра в большинство функций АР!. Эти функции возвращают состояние и коды ошибок клиенту вместе с соответствующими строками из файла сообщений Firebird10. АР! также предоставляет клиентским приложениям служебные функции для чтения содержимого векторов состояния ошибок в локальных буферах. Обработ- чики ошибок могут затем анализировать содержимое этих буферов и использовать полученную информацию для принятия решения, как поступить с исключением, а также выдать пользователю дружественное сообщение. iberror.h Заголовочный файл iberror.h в вашем каталоге /include содержит объявления, каждое из которых связано с sqlcode и gdscode через символическую константу. Например, вот объявления констант для двух кодов ошибок из предыдущего примера: Ifdefine isc_dsql_error 335544569L ttdefine isc_dsql_relation_err 335544580L <- an SQLCODE -204 error Большинство из существующих языков высокого уровня и интерфейсов сценариев уже имеют транслированные объявления констант. Если вам нужна трансляция, ре- комендуется обратиться к спискам поддержки. Полный список кодов sqlcode, gdscode и стандартных сообщений на английском языке находится в приложении 10. Исполь- зование этих кодов ошибок и расширений языка в PSQL является темой главы 32. Транзакции для нескольких баз данных Firebird поддерживает операции над несколькими базами данных под управлением одной транзакции. Он автоматически реализует двухфазное подтверждение (Two- Phase Commit, 2РС), чтобы гарантировать, что транзакция не подтвердит работу в одной базе данных, пока не будет возможности подтвердить работу в других базах данных. Данные никогда не будут частично подтвержденными. На первой фазе двухфазного подтверждения или отката Firebird подготавливает к подтверждению (или откату) работу в каждой базе данных, разделяя транзакцию на 10 Если клиентская библиотека удаленного клиента находит локальную копию файла со- общений в известном "корневом" каталоге, Firebird 1.5 будет использовать именно ее и пере- кроет этот файл в каталоге сервера RootDirectory. Поскольку до сих пор не решено, является ли это желательной возможностью, то может быть разумным устранить зависимость от такого поведения в интересах будущей защиты. В любом случае файловые системы рабочих станций являются весьма уязвимыми при неправильном поведении пользователя.
Глава 27. Программирование с транзакциями 581 подтранзакции, по одной для каждой базы данных, и посылает (post) изменения в каждую базу данных. В этот момент все подтранзакции имеют "переходное" состоя- ние. Если первая фаза завершается, то на второй фазе каждая подтранзакция отмеча- ется для подтверждения или отката в том же порядке, в котором каждые части были подготовлены. ♦ Если это операция подтверждения и какая-нибудь подтранзакция не может быть подтверждена, возникает исключение. Все подтранзакции, отмеченные для под- тверждения, переводятся в "переходное" состояние, а состояние базы данных не изменяется ни при каких условиях. ♦ Если подтверждение везде выполнилось успешно, то все подтранзакции перево- дятся в состояние "подтвержденные", а изменения базы данных становятся посто- янными. ♦ Если это операция отката, то подтранзакции переводятся в состояние отмены. Зависшие транзакции Если нарушения в сети или ошибки диска делают одну или более баз данных недос- тупными, то двухфазное подтверждение завершается с ошибкой на второй фазе, под- транзакции остаются в их переходном состоянии, будучи отмеченными ни как под- твержденные, ни как отмененные. В каждой из этих баз данных такие подтранзакции никогда не будут завершены на второй фазе (не станут подтвержденными или отме- ненными). Такие транзакции называются зависшими (limbo). Поскольку строки в базе данных иногда становятся недоступными по причине их связи с зависшими транзакциями, становится важным разрешать такие транзакции. Восстановление Пока зависшая транзакция не будет завершена (подтверждена или отменена), она остается "заинтересованной" в Firebird, который сохраняет статистику по незавер- шенным транзакциям. Восстановление зависшей транзакции означает ее подтвер- ждение или отмену. Утилита gfix может восстановить зависшие транзакции и позво- ляет вам взаимодействовать с ними интерактивно. Более подробную информацию см. в главе 39. Ж ПРИМЕЧАНИЕ. В базах данных, с которыми работают с помощью драйверов, не ('-/Х использующих двухфазное подтверждение, таких как Borland Database Engine \ (BDE), никогда не возникает зависших транзакций. Ограниченные базы данных Транзакции над несколькими базами данных могут использовать много ресурсов сервера. В ESQL Firebird предоставляет языковую поддержку в форме предложения using для ограничения баз данных, к которым транзакциям разрешен доступ. DSQL
582 Часть VI. Транзакции не предоставляет языковую поддержку. Интерфейсы DSQL могут использовать структуры API в блоке параметров транзакции для ограничения транзакций с множе- ством баз данных различными способами. Некоторые классы компонентов доступа к данным предоставляют доступ к этим режимам через свойства. Пессимистическая блокировка В пессимистической блокировке СУБД строки, запрошенные одним пользователем или транзакцией для операции, которая может изменить состояние данных, немед- ленно становятся недоступными для чтения или записи другим пользователям или транзакциям. В некоторых системах недоступной становится целая таблица. Многие разработчики, переносящие базы данных и приложения в Firebird из подобных сис- тем, приходят в замешательство из-за оптимистической блокировки и безнадежно отыскивают способы подражать старому. В Firebird все изменения выполняются на уровне строки — не существует механизма блокировки отдельного столбца. Почти на всех уровнях изоляции транзакции ядро сервера осуществляет принцип оптимистической блокировки: все транзакции, не ог- раниченные какой-либо формой пессимистической блокировки, начинаются с про- смотра текущего подтвержденного состояния всех строк во всех таблицах— конеч- но, при соответствующих привилегиях. Когда транзакция передает запрос на измене- ние строки, старая версия этой строки остается видимой всем транзакциям. Писатели не блокируют читателей. При успешном выполнении изменения сервер внутренне создает новую версию стро- ки, которая неявно блокирует исходную версию. В зависимости от установок этой пользовательской транзакции и других транзакций появится конфликт блокировки некоторого уровня, если другая транзакция попытается изменить или удалить забло- кированную строку. Подробности об условиях блокировки Firebird см. в главе 26. Firebird разработан для интерактивного использования многими конкурирующими пользователями; редко можно найти подлинные причины для использования песси- мистической блокировки. Это не "магическая пуля", с помощью которой нужно эму- лировать поведение настольных СУБД. Пессимистические блокировки одной тран- закции приведут к конфликтам в других транзакциях. Нет возможности уйти от ответственности при работе с многопользовательской моделью транзакций и написа- нии обработчиков предполагаемых конфликтов блокировки. Блокировка на уровне таблицы Уровень изоляции транзакции table stability (или согласованная изоляция) предос- тавляет полную блокировку таблицы по записи, включая зависимые таблицы. Этот уровень слишком агрессивен для интерактивных приложений. Более предпочтительным является использование предложения reserving с уровнями изоляции read committed и snapshot, потому что оно предоставляет больше гибкости и управляемости для таблиц, которые вы хотите заблокировать в процессе выполнения
Гпава 27. Программирование с транзакциями 583 транзакции. Предложение содержит параметры, которые определяют требуемый объ- ем защиты для каждой таблицы: RESERVING <предложение -резервирования>; <предложение-резервирования> = table [, table ...] [FOR [SHARED [ PROTECTED] {READ | WRITE}] [, <предложение-резервирования^ <предложение-резервирования> может включать в себя множество спецификаций резер- вирования наборов, предоставляя возможность резервировать различные таблицы или группы таблиц с различными правами. Конкретная таблица должна появляться только один раз в предложении резервирования. Обратитесь к предыдущей главе для получения информации о резервировании таблиц. Блокировка на уровне оператора Пессимистическая блокировка на уровне оператора — влияющая на индивидуальные строки или наборы — не является прямым вопросом конфигурации транзакции. При этом воздействие этих типов блокировок напрямую управляется установками тран- закций в транзакции, в которой определена блокировка, и в других транзакциях, ко- торые пытаются получить доступ к блокированной строке или набору. Это является основным решением в рамках таких техник в терминах установок транзакции. Пессимистическая блокировка строки или набора является противоположностью то- го способа работы, для которого был спроектирован Firebird. Короче говоря, если вы жили с пессимистической блокировкой и зависели от нее до знакомства с Firebird, то пришло время получить удовольствие от многоверсионной архитектуры и преиму- ществ оптимистической блокировки строки. При этом нельзя отрицать, что иногда требования разработки вызывают необходи- мость пессимистической блокировки, хотя такая потребность бывает гораздо реже, чем в случае работы с другими СУБД. Тем не менее лучшим сценарием, включаю- щим абсолютное требование исключительного доступа к строкам, не является блоки- ровкой на уровне таблицы. Обычно прочитанная строка должна быть защищена от изменения или удаления. Такое требование иногда называется строгой сериализаци- ей задач. Для случаев, когда необходима пессимистическая блокировка на уровне строки, ме- ханизм пессимистической блокировки и поддерживаемый синтаксис SQL реализова- ны в версии 1.5. До этого сервер Firebird по существу этого не поддерживал. В дан- ном разделе мы сначала посмотрим на стандартный "трюк", который выполняют клиентские приложения— когда для этого отсутствует языковая поддержка— для получения пессимистической блокировки. Затем мы рассмотрим синтаксис и условия явного select ... with lock, который осуществляет поддержку пессимистической блокировки для SQL в Firebird 1.5. Трюк "фиктивное изменение" "Редактирование" не является деятельностью на стороне сервера, следовательно, ко- гда пользователь щелкает по кнопке Редактировать (или использует любой другой
584 Часть VI. Транзакции интерфейс для редактирования данных), это ничего не изменяет на сервере. Пока сервер не озабочен изменениями, транзакция просто читает. Никаких новых версий записей не создается. Не существует блокировок. Ничто не изменяется, пока пользо- ватель не завершит редактирование строки, и приложение фактически не отправит на сервер работу пользователя. Разработчики, которые рассматривают такое поведение как сложную проблему, об- манывают ситуацию, пересылая в своих приложениях "фиктивное изменение" стро- ки, когда пользователь запрашивает ее редактирование. "Трюк" заключается в том, что оператор изменения устанавливает значение столбца в его текущее значение. Обычно используется столбец первичного ключа, например: UPDATE ATABLE SET PKEY = PKEY WHERE PKEY = PKEY; Следовательно, сервер создает новую версию записи, в которой нет никаких отличий от последней подтвержденной версии, и задает блокировку этой строки. Когда поль- зователь сообщает, что он завершил редактирование строки, приложение посылает на сервер реальное изменение, например: UPDATE ATABLE SET COLUMN2 = 'Некоторое новое значение', COLUMN3 = 99, WHERE PKEY = <значение первичного ключа>; На сервер пересылается еще одна новая версия записи, перекрывая первую. Если вам действительно нужна пессимистическая блокировка и не существует ком- бинации атрибутов транзакции, которые подошли бы вашим специфическим потреб- ностям, такая техника будет эффективной. Это работает только для одной строки и продолжается, пока транзакция не будет подтверждена, даже если пользователь ре- шит не выполнять никаких изменений строки. Д ВНИМАНИЕ! Если вы используете эту технику, убедитесь, что триггеры условий before update и before delete, относящиеся к тем таблицам, которые использу- ются в фиктивных изменениях, не помешают выполнению необходимых дейст- вий. СОВЕТ. Может оказаться необходимым создание в вашей таблице специального скрытого столбца flag для специфического использования в качестве флага фиктивных изменений. Например, скрытый столбец типа данных integer может увеличиваться на единицу вашим оператором, выполняющим фиктивное изме- нение. Тогда в триггерах можно задать выполнение "реальных" изменений только в случае if (new.flag о old.flag).
Гпава 27. Программирование с транзакциями 585 О "дважды выполненных" изменениях____________________________________ Не рекомендуется изменять одну и ту же строку более одного раза в одной транзак- ции, потому что это будет пересекаться как с автоматической целостностью данных (ссылочная целостность), так и с пользовательскими триггерами. Когда используют- ся точки сохранения (savepoint), это приводит к сильному росту количества версий записей. Такое дублирование обычно является следствием небрежного проектиро- вания логики приложения. При этом "дважды измененное изменение" является точ- ной целью техники "фиктивных изменений". Если в этих транзакциях не используют- ся точки сохранения, а в триггерах производится проверка на реальные изменения, все будет в безопасном состоянии. Явная блокировка в версии 1.5 и более поздних Синтаксис явной блокировки: SELECT спецификация-выхода FROM имя-таблицы [WHERE условие-поиска] [FOR UPDATE [OF столбец! [, столбец? [, ...]]]] WITH LOCK; Как это работает Для каждой записи, попадающей под действие оператора с явной блокировкой, сер- вер возвращает либо самую последнюю подтвержденную версию записи, независимо от того состояния, которое имела база данных при передаче на сервер этого операто- ра, либо исключение. Поведение ожидания и сообщение о конфликте зависят от параметров транзакции, заданных в буфере параметров транзакции (ТРВ). Сервер гарантирует, что все записи, возвращенные оператором с явной блокировкой, являются заблокированными и соответствуют условиям поиска, заданным в предло- жении where, если условия поиска не зависят от любых других таблиц, указанных, например, в соединении или подзапросе. Он также гарантирует блокировку только тех строк, которые соответствуют условиям поиска. При этом потенциальный выходной набор зависит от установок транзакции: уровня изоляции, разрешения блокировок и версии записи. Строка не получит блокировку, пока она не будет отправлена в буфер строк на сервере. Не существует никаких га- рантий, будет или не будет потенциальный набор воздействовать на параллельные транзакции, которые выполняют подтверждения в процессе выполнения оператора с блокировкой, делая другие строки подходящими для выборки в вашей транзакции. В табл. 27.3 содержатся итоговые сведения о взаимодействиях между установками транзакции и явными блокировками. "Наша транзакция"— это транзакция, которая имеет или пытается получить явную блокировку строки или набора. Если указано select ... with lock и необязательное предложение for update опуще- но, то все строки в наборе будут предварительно заблокированы, неважно, изменяете вы их фактически или нет. При аккуратном конфигурировании транзакции и управ- лении буферизацией со стороны клиента блокировка будет предотвращать доступ по
586 Часть VI. Транзакции Таблица 27.3. Взаимодействие установок транзакции и явных блокировок Изоляция Разрешение блокировок Поведение isc_tpb_consistency (SNAPSHOT TABLE STABILITY) — Игнорируется. Блокировки на уровне таблицы перекрывают явные блокировки isc_tpb_concurency (SNAPSHOT) isc_tpb__nowait (NO WAIT) Если строка была изменена любой транзак- цией и подтверждена после старта нашей транзакции, или уже активная транзакция изменила строку до того, как она была поме- щена в кэш строк, то немедленно возникает исключение по конфликту изменения isc_tpb_concurency (SNAPSHOT) isc_tpb_wait (WAIT) Если строка была изменена любой транзак- цией и подтверждена после старта нашей транзакции, или уже активная транзакция изменила строку до того, как она была поме- щена в кэш строк, то немедленно возникает исключение по конфликту изменения. Если активная транзакция использует строку с явной блокировкой или с обычной блокиров- кой по записи, то наша транзакция ждет ре- зультатов блокирующей транзакции. Если блокирующая транзакция подтверждает за- тем измененную версию этой записи, то воз- никает исключение по конфликту изменения isc_tpb_read_committed (READ COMMITTED) i s c_tpb_nowait (NO WAIT) Если активная транзакция использует строку с явной блокировкой или с обычной блоки- ровкой по записи, то наша транзакция немед- ленно получает исключение по конфликту изменения isc_tpb_read_committed (READ COMMITTED) isc__tpb_wait (WAIT) Если активная транзакция использует строку с явной блокировкой или с обычной блоки- ровкой по записи, то наша транзакция ждет результатов блокирующей транзакции. Когда блокирующая транзакция завершается, наша транзакция снова пытается установить бло- кировку на эту строку. Исключения по кон- фликту изменения никогда не возникнут для оператора с явной блокировкой с такой кон- фигурацией записи к любой из этих строк или к зависимым от них строкам другим транзакциям, пока не завершится ваша транзакция. Предварительная блокировка набора, содержа- щего много строк, приведет к росту конфликтов блокировок, и ваш код приложения должен быть готовым к их обработке. Количество строк в указанном выходном наборе имеет важные последствия, если вы используете метод доступа, который запрашивает "наборы данных" или "наборы записей" в пакетах из нескольких сотен строк за один раз ("буферизованная загруз- ка"), и буферизируете их на клиенте— обычно для реализации интерфейса прокру- чивания. Если блокировка не срабатывает в процессе получения отдельной строки и
Гпава 27. Программирование с транзакциями 587 вызывает исключение, то ни одна из строк, находящихся в состоянии ожидания в буфере на сервере, не будет отправлена, а те, которые уже были переданы на клиент- ский буфер, станут ошибочными. Ваше приложение должно будет выполнить откат транзакции. При таком стиле доступа важно обеспечить в ваших приложениях способ обработки исключений по мере их появления. Используйте сильно ограничивающее предложе- ние where для уменьшения диапазона блокировок до одной или очень небольшого количества строк и исключите ошибки в частично загруженных наборах. Если ваш интерфейс доступа к данным такое поддерживает, то сделайте в вашем компоненте доступа к данным буфер для загрузки только одной строки, например: SELECT * FROM DOCUMENT WHERE ID = ? WITH LOCK /* ID - первичный ключ */ Необязательное предложение for update предоставляет способ для определения на- бора из множества строк, загрузки и обработки строк за один раз. Предложение FOR UPDATE Если присутствует предложение for update, буферизованная загрузка будет отключе- на, а блокировка будет применяться к каждой строке, одна за другой в том порядке, в котором они загружаются из кэша с серверной стороны. Если именованный кур- сор" управляет позицией обновления, это предложение может включать необяза- тельное выражение on <список~столбцов> для направления изменений указанным столбцам курсора. Поскольку к транзакции применяются обычные правила изоляции, то существует возможность для блокировки, которая была доступна в момент старта запроса, впо- следствии получить отказ. Незагруженные строки остаются "чистыми" и доступными другим транзакциям для изменения, за исключением "скользящих окон", в которых некоторые незагруженные строки могут быть заблокированы другой транзакцией, даже если блокировка появилась после того, как был запрошен набор данных. Пример использования WITH LOCK Приведенный далее оператор определяет неограниченный по количеству строк вы- ходной набор, где каждая строка будет загружаться в буфер на серверной стороне индивидуально. Следующая строка не будет загружаться до тех пор, пока сервер не сообщит о своей готовности ее принять, with lock пытается выполнить пессимисти- ческую блокировку при запросе каждой строки. Будет возвращена либо следующая строка, либо исключение. SELECT + FROM DOCUMENT WHERE PARENT ID=? FOR UPDATE WITH LOCK По причине ограничения объема книги тема именованных курсоров затрагивается лишь слегка. Для работы с ними API предоставляет группу функций isc_dsql__*. Синтаксис опера- тора declare cursor, полностью реализованный в ESQL, доступен в некоторых средах про- граммирования DSQL. Описание использования именованных курсоров в модулях PSQL см. в главах 29 и 30.
588 Часть VI. Транзакции Ограничения явной блокировки Конструкция select ... with lock доступна в DSQL и PSQL. Она может использо- ваться только в операторе select верхнего уровня для единственной таблицы. ♦ Она недоступна в подзапросе или в соединяемом наборе. ♦ Она не может быть указана с квантификаторами (оператор distinct, first или skip), с предложением group by, а также с любыми другими агрегатными опера- циями. ♦ Она не может быть использована в просмотрах, во внешних таблицах и в выход- ном наборе хранимой процедуры выбора. Хранимые процедуры, триггеры и транзакции Сведения о написании и использовании хранимых процедур и триггеров см. в час- ти VII. Хранимые процедуры Хранимые процедуры выполняются в контексте тех транзакций, которые их вызвали. Сделанная работа, включая ту, которая была выполнена в задачах встроенных или рекурсивных вызовов, будет иметь результат, если все завершится без ошибок, с об- работанными исключениями и вся работа будет подтверждена. Если результатом обработки исключения в одной операции будет откат транзакции, то вся работа этой транзакции будет отменена. Триггеры Триггеры вызываются внутри контекста оператора DML. Сделанная работа, включая ту, которая была выполнена в задачах встроенных вызовов процедур, все обновле- ния, добавления или удаления данных других таблиц или за счет внутренней ссылоч- ной целостности или другими триггерами, принадлежащими другим таблицам — все будет подтверждено или возвращено клиенту в неопределенном состоянии. Необра- ботанные исключения в одной операции приведут к отмене операции, при которой встретилась ошибка, и сохранят транзакцию в том состоянии, когда приложение сможет принять решение отменить транзакцию или попытаться исправить ошибку и заново отправить запрос. Отмена транзакции отменит все операции, выполненные в транзакции до момента появления исключения. "Точки сохранения" в PSQL Добавление возможностей создания пользовательских точек сохранения в Firebird 1.5 позволяет приложению управлять область действия отката транзакции. В PSQL все- гда была возможность обработки исключений. Подробности см. в главе 32.
Глава 27. Программирование с транзакциями 589 Советы по оптимизации поведения транзакции Выбор подходящей модели транзакции Модель "одна транзакция на все приложение" искушает неопытного разработчика игнорировать проблему многопользовательской работы в пользу "простоты про- граммирования". Результатом является такая архитектура приложения, которая плохо работает на всех уровнях: медленные запросы и ответы на обновление списков, пере- груженная сеть, не дружественная к пользователю последовательность выполняемых действий и высокий уровень конфликтов. Не переходите к "общему" пока вам это не понадобилось Общие интерфейсы приложений для баз данных, такие как ODBC или Borland BDE, объединяют одно соединение с базой данных с одной транзакцией. Поскольку их задачей является скрыть разницу между простенькими, основанными на файлах ре- позиториями данных и сложными, использующими транзакции системами управле- ния базами данных, они не поддерживают возможностей наличия множества актив- ных конкурирующих транзакций в сессии базы данных или транзакций, имеющих доступ к нескольким базам данных. В лучшем случае эти общие интерфейсы обеспечивают примитивный способ мас- штабирования простых баз данных или выравнивания разницы между различными реализациями СУБД всевозможных разработчиков. Если вам не нужен такой низко- уровневый общий знаменатель, не используйте их. Использование возможностей множества транзакций Клиент Firebird может запустить множество параллельных транзакций. Пользова- тельская работа с множеством задач в одном приложении может выполнять различ- ные действия с теми же самыми (или перекрывающимися) наборами данных. Модель транзакций Firebird обеспечивает большие преимущества в проектировании, где нужно удовлетворять требованиям модульности в многозадачном окружении в очень чувствительной манере. Важной задачей при создании программного обеспечения являются техники разработки, обеспечивающие сохранение процесса работы и син- хронизированного вида состояния базы данных для пользователя. Сохраняйте передвижение ОАГ. Медленное передвижение оат почти всегда указывает на транзакции, выполняющие- ся долго. Исключение таких транзакций является одним из наилучших навыков, ко- торые вы можете получить, обучаясь написанию клиентских приложений для Firebird.
590 Часть VI. Транзакции Проще всего обвинить поведение пользователей в появлении долгих транзакций. Вы должны помочь им научиться завершать задачи в разумное время: не отправляться пить кофе, не завершив задач, не выдавать "диких запросов" в пиковое время и т. д. При этом хорошее проектирование клиентского приложения исключает его зависи- мость от правильного поведения пользователя. ♦ Подходят механизмы, которые завершают со временем забытые транзакции. ♦ Как основное правило, исключите интерфейсы просмотра данных и используйте практичные средства. ♦ Если использование интерфейса просмотра неизбежно, изолируйте операторы, выбирающие данные для просмотра, в транзакциях read-only read committed. ♦ Убедитесь, что транзакции read/write регулярно подтверждаются— даже если пользователи используют их только для просмотра данных. ♦ Избегайте приложений, выполняющих произвольные запросы, включая в запросы where и устанавливая ограничения по времени. ♦ Убедитесь, что ваши приложения имеют средства для периодического выполне- ния "жестких подтверждений" любых транзакций, выполняющих commit retain. ♦ Возьмите за правило использовать RollbackRetaining не более одного раза в обра- ботчике исключений. Не помещайте RollbackRetaining внутрь циклов! ♦ Учитывайте, что происходит с транзакциями в сервере! Используйте gstat -h или эквивалентный инструмент для отслеживания oit и оат. Обращайте внимание на "зазор"12. ♦ Не пренебрегайте наведением порядка в базе данных. Чистка (sweep) должна вы- полняться систематически. Регулярное выполнение резервного копирования, даже без восстановления базы данных, поможет поддерживать инвентарные страницы транзакций в хорошей форме13. Пора дальше Теперь, когда вы освоили запутанные вопросы управления транзакциями, самое вре- мя направить ваши таланты на программирование на серверной стороне. В части VII вы начнете работать с мощными средствами, доступными в PSQL: хранимые про- цедуры и триггеры, обработка пользовательских исключений, механизм событий в Firebird. В главе 28 мы начнем рассматривать преимущества действий на стороне сервера для централизации бизнес-правил и сокращения сетевого трафика перед тем, как перейдем к синтаксису и техникам в следующих главах. 12 Инструмент IBAnalyst (см. www.ibase.ru) выдаст все возможные предупреждения и ре- комендации по поводу состояния транзакций на сервере. — Прим. науч. ред. 13 Выполнение backup никак не влияет на Transaction Inventory Page, a sweep может про- двинуть вперед OIT, но не более того. — Прим. науч. ред.
ЧАСТЬ VII Программирование на сервере Глава 28. Введение в программирование в Firebird Глава 29. Разработка модулей PSQL Глава 30. Хранимые процедуры Глава 31. Триггеры Глава 32. Обработка ошибок и события

ГЛАВА 28 Введение в программирование в Firebird Одним из самых больших преимуществ полнокровных реализаций реляционных баз данных SQL является их способность компилировать и выполнять внутренние моду- ли (хранимые процедуры и триггеры), представленные разработчиками в виде исход- ных кодов. Язык, который предоставляет такую возможность для сервера Firebird — PSQL — простой, но мощный набор расширений языка SQL, который объединяется с обычными операторами языка манипулирования данными (DML) для создания ком- пилируемых исходных модулей. Обзор модулей сервера Языком высокого уровня для программирования в Firebird на стороне сервера явля- ется SQL. Исходный код предоставляется серверу в форме расширений языка про- граммирования SQL— операторов и конструктов PSQL— и операторов DML. Сами эти операторы находятся внутри одного оператора DDL вида: (CREATE | RECREATE ( ALTER} {TRIGGER ( PROCEDURE} <имя> . . . AS BEGIN <один или более блоков операторов> END Синтаксис написания модулей PSQL подробно рассматривается в следующих главах. Назначением каждого из этих "супероператоров DDL" является создание и сохране- ние одного исполняемого модуля (хранимой процедуры или триггера) или переопре- деление (recreate или alter) существующего объекта. Оператор DDL также исполь- зуется для уничтожения (drop) исполняемых объектов. PSQL поддерживает три оператора манипулирования данными: insert, update и delete и возможность выборки (select) одной строки или многострочных наборов элементов данных с помещением в локальные переменные. Расширения PSQL обес- печивают перечисленную далее языковую и логическую поддержку. ♦ Локальные переменные и операторы присваивания. ♦ Условные операторы управления потоком выполнения.
594 Часть VII. Программирование на сервере ♦ Специальные контекстные переменные (только для триггеров) для доступа к ста- рому и новому значению каждого столбца во всех входных наборах DML. ♦ Отправка определенных пользователем событий базы данных прослушивающим клиентам. ♦ Исключения, в том числе определенные пользователем, объявленные как объекты базы данных, а также (в версии 1.5) специфичные для контекста сообщения об ис- ключениях, поддержка структуры и синтаксиса для обработки ошибок. ♦ Входные и выходные аргументы (только для хранимых процедур). ♦ Инкапсуляция поведения курсора в синтаксисе цикла for select ... into ... do. ♦ Оператор suspend (только для хранимых процедур), предоставляющий возмож- ность написания хранимых процедур, которые выводят виртуальную таблицу на запрос в операторе select — хранимые процедуры выбора. ♦ Внутренние вызовы хранимых процедур в хранимых процедурах и триггерах. ♦ Возможность определения множества триггеров для фаз before (до) и after (по- сле) в триггерах для каждого события DML и задание их позиции в предваритель- но определенном порядке их исполнения. В версии 1.5 появилась возможность писать условные триггеры before и after, объединяющие любые из всех возмож- ных событий DML. За исключением указанных специфических элементов все множество языка PSQL доступно для хранимых процедур и триггеров. Хранимые процедуры Хранимые процедуры могут быть использованы в приложениях различными спосо- бами. ♦ Процедуры выбора используются на месте таблицы или просмотра в операторе SELECT. ♦ Выполняемые процедуры исполняются оператором execute procedure для выпол- нения одной операции или запуска множества операций на стороне сервера. ♦ Хранимая процедура может быть вызвана из другой хранимой процедуры или из триггера. Она может вызывать сама себя рекурсивно. Все хранимые процедуры определяются в сложном операторе DDL create procedure. Объявления выполняемых хранимых процедур и хранимых процедур выбора следу- ют одним и тем же синтаксическим правилам. Необязательные языковые элементы отличаются для процедур выбора и выполняемых процедур. Одна процедура может быть вложенной в другую, каждая из которых выполняет часть атомарной последо- вательности работы, которая будет подтверждена клиентским приложением как еди- ное целое или отменена как целое.
Глава 28. Введение в программирование в Firebird 595 Преимущества использования хранимых процедур Перечислим преимущества использования процедурных модулей, которые выполня- ются внутри базы данных. ♦ Модульное проектирование: все приложения, имеющие доступ к одной базе дан- ных, совместно используют хранимые процедуры, что, следовательно, централи- зует бизнес-правила, позволяет повторно использовать код, сокращает размер приложений. ♦ Хорошо налаженная поддержка: когда процедура модифицируется, изменения автоматически распространяются на все приложения без необходимости даль- нейшей перекомпиляции на стороне приложения, за исключением изменений, влияющих на наборы входных или выходных аргументов. ♦ Улучшенное выполнение: выполнение сложной обработки делегируется серверу, сокращая сетевой трафик и нагрузки оперирования с внешними наборами. ♦ Экономия в архитектуре: клиентские приложения могут сфокусироваться на по- лучении входных данных от пользователя и на управлении интерактивными зада- чами, в то время как серверу, который предназначен для управления данными, де- легируется управление сложными данными и их зависимостями. ♦ Дополнительная функциональность: искусный доступ к данным, который не мо- жет быть достигнут средствами обычного SQL, может быть выполнен одной или группой хранимых процедур. Триггеры Триггер является подпрограммой, связанной с таблицей или просмотром, которая автоматически выполняет некоторые действия, когда строка таблицы или просмотра добавляется, изменяется или удаляется. Триггер никогда не вызывается напрямую. Когда приложение или пользователь пы- тается выполнить insert, update или delete для строки таблицы, триггеры, связанные с этой таблицей, вызываются автоматически. Триггеры могут использовать исключе- ния и события. Они также могут вызывать хранимые процедуры. Триггеры являются мощным инструментом в различных вариантах использования. Перечислим способы использования триггеров. ♦ Для выполнения коррелированных изменений при выполнении оператора DML с таблицей. Например, триггер может добавлять записи во внутренний или внеш- ний протокол изменений. Триггер after delete (после удаления) может добавить строку в таблицу истории. ♦ Для проверки исходных данных. ♦ Для преобразования данных, например, для автоматического конвертирования входного текста в буквы верхнего регистра или для получения значения автоин- крементного ключа из генератора.
596 Часть VII. Программирование на сервере ♦ Для информирования приложений об изменениях базы данных с использованием средств сообщения о событиях (event alerter). ♦ Для выполнения пользовательских каскадных изменений целостности данных. ♦ Чтобы сделать просмотр только для чтения изменяемым. Подробности см. в разд. "Преобразование просмотров только для чтения в изменяемые"главы 24. Триггеры хранятся как объекты базы данных так же, как хранимые процедуры и ис- ключения. Объявленные как active, они остаются активными, пока не будут деакти- вированы оператором alter trigger или удалены из базы данных с помощью drop TRIGGER. Триггер никогда не вызывается явно — активный триггер выполняется автоматиче- ски, когда заданная операция DML выполняется для его таблицы. Преимущества использования триггеров Перечислим преимущества использования триггеров. ♦ Автоматическое применение ограничения данных, чтобы убедиться, что пользо- ватели вводят только допустимые значения в столбцы. ♦ Сокращение объема поддержки приложений, поскольку изменения триггеров ав- томатически отражаются на всех приложениях, которые используют связанные таблицы, без необходимости их повторной компиляции и сборки. ♦ Автоматическое протоколирование изменений таблиц. Приложение может ис- пользовать протоколирование изменений с помощью триггера, который вызыва- ется при модификации таблицы. ♦ Автоматическое информирование об изменениях базы данных с помощью средств сообщения о событиях. Триггеры в качестве автоинкрементного механизма Триггеры могут быть использованы в комбинации с генераторами для реализации автоинкрементных ключей. Подробные инструкции вы можете найти в главе 31. Триггеры и транзакции Триггеры всегда выполняются в контексте конкретной операции DML, как часть этой операции и внутри той транзакции, которая осуществляет запрос оператора DML. Нет смысла отделять их от транзакции или от операции, которая привела к выполне- нию триггера. Если транзакция будет отменена, то и все действия, выполненные триггером, также будут отменены.
Гпава 28. Введение в программирование в Firebird 597 Расширения языка PSQL Расширения языка PSQL включают следующие языковые элементы: ♦ операторы begin и end для выделения блоков кода, которые могут быть вложен- ными; ♦ операторы declare variable для объявления локальных переменных; ♦ конструкция FOR SELECT <спецификация-выбора> INTO <список -переменных> DO ИН- капсулирует курсор SQL для выполнения цикла просмотра наборов. Циклы могут быть вложенными; ♦ циклы while; ♦ оператор suspend для пересылки строки в кэш строк; ♦ конструкция if ... then и else для ветвления в программе; ♦ оператор exception <объявленное-имя-исключения> для вызова пользовательских исключений; ♦ необязательные блоки when <условие-исключения> do для перехвата и обработки исключений; ♦ post event <строка> для передачи сообщений клиентам. Firebird версии 1.5 и более поздние также поддерживает: ♦ оператор execute statement для выполнения специальных операторов DML и DDL в модуле; ♦ логические контекстные переменные updating, inserting и deleting; ♦ контекстная переменная row__count для получения количества строк, полученных выполненным оператором DML в том же блоке; ♦ дополнительный синтаксис для exception без аргументов — для повторного вызо- ва исключений, а с необязательным текстовым аргументом — для передачи ин- формации клиенту. Ограничения PSQL Существуют некоторые ограничения языка для кодов в модулях PSQL. ♦ Операторы, использующие подмножество языка определения данных (DDL) SQL Firebird, не разрешены в PSQL1. ♦ Операторы управления транзакциями недопустимы в PSQL, потому что хранимьге процедуры и триггеры всегда выполняются в контексте существующей клиент- ской транзакции, a Firebird не поддерживает вложенные транзакции. В версии 1.5 возможна передача оператора DDL в строке execute statement. Делайте это только в особых случаях.
598 Часть VII. Программирование на сервере ♦ Некоторые другие типы операторов, зарезервированные для использования в дру- гих средах (например, в isql, скриптах или во встроенном SQL — см. следующий раздел). Допустимы все динамические операторы DML. ♦ Идентификаторы объектов метаданных, такие как имена таблиц, столбцов, про- смотров или хранимых процедур, не могут передаваться хранимой процедуре или возвращаться хранимой процедурой в ее аргументах. ♦ Триггеры не могут получать или возвращать аргументы. Типы операторов, не поддерживаемых в PSQL Следующие типы операторов не поддерживаются в триггерах и хранимых про- цедурах: ♦ операторы языка определения данных (т. е. операторы, начинающиеся с ключе- вых СЛОВ CREATE, RECREATE, ALTER ИЛИ DROP; SET GENERATOR; DECLARE EXTERNAL FUNCTION: DECLARE FILTER); ♦ операторы управления транзакциями: set transaction, commit, commit retain, ROLLBACK, SAVEPOINT, RELEASE SAVEPOINT, ROLLBACK TO SAVEPOINT; ♦ операторы ESQL: prepare, describe, execute; ♦ операторы connect/disconnect и отправки операторов SQL другим базам данных; ♦ grant/revoke; ♦ event init/event wait; ♦ BEGIN DECLARE SECTIOn/eND DECLARE SECTION; ♦ BASED on; ♦ WHENEVER; ♦ DECLARE CURSOR; ♦ open; ♦ fetch; ♦ любые операторы, начинающиеся с ключевых слов set и show. Исключения Обработчики исключений могут быть написаны, чтобы "съесть" ошибку, обрабаты- вая ее разными способами. Например, в итеративной подпрограмме входная строка, вызывающая исключение, необязательно должна приводить к остановке всего про- цесса. Обработка исключения внутри триггера или хранимой процедуры может по- зволить пропустить проблемную входную строку — например, поместив сообщение об ошибке в протокол в текстовый файл или в таблицу ошибок — и дав возможность продолжить дальнейшую обработку. Код в модуле может обрабатывать ошибку в необязательном фрагменте кода, назы- ваемом блоком исключения, который является последовательностью операторов,
Гпава 28. Введение в программирование в Firebird 599 заключенных в операторные скобки begin и end, которым предшествует ключевое СЛОВО WHEN. Необработанное исключение останавливает процесс, отменяет всю выполненную к этому моменту работу2 и возвращает сообщение об ошибке приложению. Вы также можете написать код, вызывающий пользовательское исключение и останавливаю- щий процесс. Вы можете обработать эту ошибку в вашем коде или остановить про- цесс и вернуть пользовательское сообщение клиентскому приложению. Если модуль является триггером, то операция DML, в которой появилась эта ошибка, также будет отменена. В базе данных вы можете создать столько пользовательских исключений, сколько вам нужно. Начиная с версии 1.5, вы можете использовать данные времени выполнения и конструировать тексты для ваших сообщений об исключениях "на лету". Обработка исключений и ошибок подробно обсуждается в главе 32. События События Firebird являются "сигналами", которые модули PSQL могут накапливать в процессе выполнения для передачи клиентским приложениям, когда работа будет подтверждена. Клиентские приложения в сети могут прослушивать— с использова- нием обработчика сообщений— конкретные события, в которых они заинтересова- ны, без необходимости специального опроса наличия события. Программирование с событиями и задание приложениям указания на их прослуши- вание рассматривается в главе 32. Безопасность Процедурам и триггерам могут быть предоставлены привилегии для специфических действий (select, insert, delete и т. д.) к таблицам точно так же, как пользователям или ролям предоставляются привилегии. Не существует специального синтаксиса: используется обычный оператор grant, но в предложении то указывается триггер или процедура вместо пользователя или роли. Аналогичным образом привилегии про- цедур и триггеров могут отменяться оператором revoke. Не всегда существует необходимость предоставлять привилегии модулям процедур или триггеров. Достаточно либо пользователю, либо модулю иметь привилегии к действиям, выполняемым модулем. Например, если пользователь выполняет update для таблицы а, что вызывает триггер, а триггер выполняет insert для таблицы в, то это действие будет допустимым, если 2 Только в отношении работы конкретного запроса SQL, выполненного из клиентского приложения, в "недрах" которого произошла ошибка. Работа, выполненная ранее другими запросами SQL в этой же транзакции, будет сохранена (если транзакция завершится по commit). — Прим. науч. ред.
600 Часть VII. Программирование на сервере пользователь имеет привилегии insert к этой таблице или триггер имеет привилегии insert к этой таблице. Если у триггера или процедуры нет достаточных привилегий для выполнения их дей- ствий, Firebird вызывает ошибку SQL и устанавливает соответствующий код ошибки. Вы можете перехватить этот код ошибки в обработчике исключений точно так же, как и другие исключения. Информацию об операторах grant и revoke см. в главе 35. Внутреннее устройство технологии Когда любой запрос вызывает хранимую процедуру, то текущее определение этой хранимой процедуры копируется в этот момент в кэш метаданных. В Классическом сервере эта копия присутствует в течение всего времени пользовательского соедине- ния. В Суперсервере она сохраняется "живой", пока не будет отключено последнее соединение. Запрос приходит от одного из следующих объектов: ♦ от клиентского приложения, которое напрямую выполняет хранимую процедуру; ♦ от триггера, который выполняет хранимую процедуру. Сюда относятся и систем- ные триггеры, являющиеся частью систем поддержания ссылочной целостности или ограничений check; ♦ от другой хранимой процедуры, которая выполняет эту хранимую процедуру. Эффекты изменений Однажды вызванный запрос к триггеру или хранимой процедуре сохраняется в кэше метаданных, пока существуют клиентские соединения с базой данных, независимо от того, использует ли какой-нибудь клиент этот триггер или хранимую процедуру. Не существует механизма убрать эти невыполняющиеся запросы из кэша метаданных. По этой причине изменения модулей PSQL являются "отложенными" в большей или меньшей степени в большинстве случаев. Возможность для клиентов видеть эти из- менения отличается для Классического сервера и Суперсервера. Суперсервер Поскольку существующие запросы удаляются из кэша метаданных только тогда, ко- гда последний клиент отключится от базы данных, они вообще никогда не будут об- новлены в системах 24/7 (т. е. в системах, работающих 24 часа в сутки и 7 дней в не- делю). Существует только один способ гарантировать, что все копии хранимых про- цедур и триггеров будут удалены из кэша метаданных — завершение всех соединений с базой данных. Когда пользователи снова соединятся с базой данных, они все увидят новые версии хранимых процедур и триггеров. Классический сервер Изменение или удаление хранимой процедуры немедленно воздействует на новые соединения, осуществляемые после подтверждения изменения. Новые соединения,
Гпава 28. Введение в программирование в Firebird 601 которые вызывают хранимую процедуру, увидят самую последнюю версию. При этом другие соединения продолжают видеть ту версию хранимой процедуры, кото- рую они видели с самого начала соединения. С точки зрения практики имеет смысл отключить всех клиентов до подтверждения изменений таких модулей. Пора дальше Теперь мы рассмотрим структуру модулей PSQL: что общего имеют триггеры и про- цедуры и чем они отличаются.
ГЛАВА 29 Разработка модулей PSQL Хранимые процедуры и триггеры объявляются при помощи операторов create procedure и create trigger соответственно. Каждый из этих сложных операторов со- стоит из заголовка и тела. Элементы процедур и триггеров Определения модулей PSQL действительно являются одним оператором SQL, кото- рый начинается предложением create и завершается терминатором. В определении модуля существует множество элементов: предложений, ключевых слов, блоков множества элементов, программных ветвей, циклов и др. Одни элементы являются обязательными, другие необязательными. Хотя комплекс объявлений модуля PSQL является оператором DDL, расширения SQL в этом комплексе являются элементами структурированного языка высокого уровня, который имеет особые правила. Одним из этих правил, которое вы должны знать до начала работы, является использование терминатора оператора. Оператор CREATE Исходный код процедуры и триггера конструируется внутри "супероператора", кото- рый начинается с ключевых слов create procedure или create trigger и завершается символом терминатора, следующим за конечным оператором end, например: CREATE PROCEDURE имя . . . AS BEGIN END л В хранимых процедурах и триггерах все операторы, следующие за ключевым словом as, включают локальные переменные (если присутствуют) и логику программного модуля. Основное различие между триггерами и хранимыми процедурами заключа- ется в заголовке оператора create (см. рис. 29.1).
Гпава 29. Разработка модулей PSQL 603 Терминатор операторов Каждый оператор внутри тела хранимой процедуры или триггера— кроме begin и end— должен заканчиваться точкой с запятой. Никакой другой символ не является допустимым терминатором операторов в PSQL. В DSQL для операторов DML и DDL в Firebird точка с запятой также является терминатором по умолчанию. Она также является стандартом SQL для завершения операторов. Такая ситуация может создать логическую проблему для синтаксического анализато- ра, который выполняет предварительную компиляцию наших модулей PSQL: какая точка с запятой завершает операторы внутри модуля, а какая завершает определение CREATE? Чтобы обойти эту проблему, в Firebird есть синтаксис переключения set term, кото- рый позволяет вам устанавливать другой внешний терминатор, который воздействует на внешние операторы, в то время как синтаксический анализатор обрабатывает оп- ределения PSQL. В скриптах опытные разработчики часто используют одиночный оператор set term в начале каждого скрипта, чтобы использовать свой любимый аль- тернативный терминатор в течение всего времени выполнения скрипта. Некоторые инструменты администратора баз данных поддерживают средства конфигурирования альтернативных терминаторов в своих редакторах и программах выделения мета- данных. Операторы set term используются в isql и в скриптах. Утилита isql предварительно анализирует каждый оператор и отправляет любой за- вершенный терминатором оператор на сервер в виде одной команды, set term явля- ется одним из собственных операторов ISQL, который используется не для пересыл- ки запроса на сервер, а для подготовки собственного синтаксического анализатора для различной интерпретации терминаторов. (Другие операторы set в ISQL также вызывают специальные действия в программе isql, которые не имеют значения вне isql.) DSQL совсем не распознает терминаторы операторов. Большинство других утилит, выполняющих скрипты, отправляют операторы DDL на сервер один за другим без терминаторов. Они выполняют свой собственный синтаксический анализ для распо- знавания начала и завершения операторов create procedure и передают внутренние терминаторы точка с запятой просто как обычные символы синтаксиса составного оператора. Когда вы используете такую утилиту для интерактивного создания модулей PSQL, она обычно выдает исключение на оператор set term, потому что как оператор SQL он не имеет смысла за пределами isql. При этом в скриптах эти утилиты обычно об- рабатывают оператор set term и внутренне используют альтернативный терминатор так же, как и утилита isql. Таким образом, включайте оператор set term в isql, если вы используете этот инст- румент интерактивно, а также включайте его в скрипты. Альтернативный терминатор может быть любой, нравящейся вам строкой символов, за исключением точки с запятой, пробела и апострофа. Если вы используете буквен- ный символ, он будет чувствительным к регистру. Если вам так нравится, можете
604 Часть VII. Программирование на сервере задать терминатор, содержащий несколько символов, включая пробелы; он только не может быть зарезервированным ключевым словом. Оба следующих оператора допус- тимы: SET TERM Л; SET TERM being! ; СОВЕТ. He будьте только слишком изобретательными при создании ваших строк терминаторов, иначе вам придется набирать большой текст! В определениях PSQL применяйте точку с запятой во всех внутренних операторах за исключением begin и end и используйте альтернативный терминатор в конечном опе- раторе end: END Для возврата к "нормальному" оператору терминатора выдайте второй оператор set term, который изменит результат первого: END Л COMMIT Л SET TERM ; /* set the terminator */ SET TERM create procedure .^Procedure ( i nput 1;;: INTEG E R, i n p ut 2i: 0 ATE j iii t-. . ) returns (outputl INTEGER, П7 out p ut 2; VA RCHAR (2 0 ) , l_ create trigger aTrigger for aTable active BEFORE INSERT /*or other phases/events */ - position 0 /*or other precedence*/ declare variable varl INTEGER; declare variable var2 VARCHAR(2O); declare variable... ; .......cd BODY hr— begin <stai з i arts and embedded begin...end block*.. end /*• erid of pro c ed ux e det i n i t io n:: / COMMIT Л /* reset the terminator if ready to */ SET TERM ;л Рис. 29.1. Обязательные элементы в определении модуля PSQL
Глава 29. Разработка модулей PSQL 605 На рис. 29.1 основные элементы определения модуля PSQL отделены для иллюстра- ции требуемых элементов в секциях HEADER (Заголовок) и BODY (Тело) модуля. Обязательные части затенены. Элементы заголовка Имя процедуры или триггера должно быть уникальным в базе данных. Для триггера: ♦ ключевое слово for и имя таблицы идентифицируют ту таблицу, операции с кото- рой вызывают данных триггер; ♦ режим (active или inactive); ♦ параметр фазы (before или after) определяет, когда вызывается триггер; ♦ параметр события (insert, update, delete)1; ♦ необязательное ключевое слово position, за которым следует целое число, указы- вающее последовательность вызова. Для хранимой процедуры; ♦ необязательный список входных параметров и их типов данных; ♦ если процедура возвращает значения вызвавшей программе, то список выходных параметров и их типов данных. Элементы тела Для хранимых процедур и триггеров: ♦ тело модуля может начинаться со списка из одного или более объявлений локаль- ных переменных (имя и тип данных SQL — домен указывать нельзя); ♦ блок операторов на языке процедур и триггеров Firebird, заключенный в опера- торные скобки begin и end. Блок сам может включать другие блоки, следователь- но, может существовать много уровней вложенности; ♦ некоторые встроенные блоки могут быть обработчиками исключений^ возникаю- щих в предшествующих блоках. Такие блоки являются условными в соответствии с предшествующим предикатом when. Модуль глобальных обработчиков исключе- ний должен предшествовать всем встроенным блокам. Начиная с версии 1.5. существует возможность объединять несколько событий в одном триггере. Подробности см. в главе 31.
606 Часть VII. Программирование на сервере Элементы языка В табл. 29.1 показаны элементы языка PSQL, доступные в Firebird. Таблица 29.1. Расширения PSQL для хранимых процедур и триггеров Оператор Описание В. 1.5 В. 1.0.x BEGIN ... END Определяет блок операторов, которые вы- полняются как одно целое. Зарезервирован- ное слово begin начинает блок; зарезерви- рованное слово end завершает его. Ни за одним из них не должна следовать точка с запятой. В версии 1.0.x нельзя выдать опе- ратор CREATE PROCEDURE без ХОТЯ бы ОДНОГО оператора между begin и end. "Пустые" оп- ределения допустимы в версии 1.5 и выше Да Да переменная = выражение Присваивает значение выражения перемен- ной— локальной переменной, входному параметру или выходному параметру Да Да /★ текст комментария */ Комментарий программиста, где текст может содержать любое количество строк между парой /* */. Может быть также использован для встроенных комментариев Да Да — текст комментария Комментарий программиста из одной строки, где текст может быть встроенным (только версия 1.5) или может занимать одну строку, где маркер двойного минуса (—) является первым элементом в строке Да Да EXCEPTION имя-исключения Вызывает именованное исключение для возможной обработки в блоке when. Само исключение должно быть предварительно определено администратором базы данных с использованием create exception Да Да EXCEPTION Вызывает исключение Да Нет EXCEPTION имя-исключения сообщение-времени- выполнения Вызывает именованное исключение и при- соединяет к нему сообщение времени вы- полнения — локальную переменную типа varchar, которой во время выполнения мо- жет быть присвоено значение. Подробности определения и использования исключений см. в главе 32 Да Нет EXECUTE PROCEDURE имя-процедуры [переменная [, переменная ...]] [RETURNING_VALUES переменная [, переменная • - .]] Выполняет хранимую процедуру имя- процедуры. Входные аргументы следуют за именем процедуры; возвращаемые значения следуют за ключевым словом returning_values. Допустимы вложенные процедуры и рекурсия. Входные и выходные параметры должны быть переменными, оп- ределенными в процедуре Да Да
Глава 29. Разработка модулей PSQL 607 Таблица 29.1 (продолжение) Оператор Описание В. 1.5 В. 1.0.x EXECUTE STATEMENT <строка> Выполняет оператор динамического SQL, содержащийся в < строка? Да Нет EXIT Переходит на конечный оператор end. Не- обязателен Да Да FOR ... SELECT ... INTO ... DO Синтаксис составного блока цикла для об- работки неявного курсора и (необязатель- ной) генерации виртуальной таблицы для направления выхода запроса select клиен- ту. Подробности см. в разд. "SELECT для множества строк" Да Да IF . . . THEN .. . [ELSE] ... Синтаксис составного ветвления. Подробно- сти см. в разд. "Условные блоки" позже в этой главе Да Да LEAVE2 Оператор не принимает параметров. Ис- пользуется для выхода из цикла. Выполне- ние переходит к первому оператору, сле- дующему за концом того блока, который включает цикл, где был выполнен оператор LEAVE Да Нет NEW.имя-столбца Только триггеры. Контекстные переменные, доступные для триггеров insert и update. Существует одна переменная new для каж- дого столбца таблицы, содержащая новое значение, передаваемое клиентским запро- сом. Также доступна в некоторых ограниче- ниях check. Заметим, что в версии 1.5 в триггерах для нескольких действий ссылка на new. переменная не является ошибкой, поскольку она вернет null, если использу- ется в контексте удаления Да Да OLD.имя-столбца Только триггеры. Контекстные переменные, доступные для триггеров insert и delete. Существует одна переменная old для каж- дого столбца таблицы, содержащая значе- ние, которое имел столбец до выдачи кли- ентского запроса. Также доступна в некото- рых ограничениях check. Заметим, что в версии 1.5 в триггерах для нескольких дей- ствий ссылка на old . переменная не являет- ся ошибкой, даже если триггер включает действия по добавлению данных. Она вер- нет null, если используется в контексте добавления Да Да 2 В версии 1.5 leave заменяет недокументированный оператор break, который был частич- но реализован в версии 1.0.x Не используйте break.
608 Часть VII. Программирование на сервере Таблица 29.1 (окончание) Оператор Описание В. 1.5 В. 1.0.x POST_EVENT имя-события Помещает событие имя-события в стек. Имя события может быть произвольной строкой длиной до 78 символов и не является пред- варительно определенным на сервере. Со- бытия из стека будут переданы клиентам, "прослушивающим" это событие через обра- ботчик сообщений. Подробности см. в разд. "События" главы 32 Да Да SELECT ... INTO ... Помещает выход обычного одиночного опе- ратора select в список предварительно объявленных переменных. Вызовет исклю- чение, если оператор вернет множество строк Да Да SUSPEND Недоступен в триггерах! Оператор исполь- зуется в процедурах, разработанных для вывода наборов множества строк в виде виртуальных таблиц — хранимые процедуры выбора. Он приостанавливает выполнение процедуры на время перемещения строки из кэша строк в клиентское приложение. Опе- ратор не имеет этого эффекта в выполняе- мых хранимых процедурах, где он эквива- лентен оператору exit Да Да WHILE <условие> DO Синтаксис условного цикла, при котором выполняется блок, пока условие не станет ложным. Подробности см. в разд. "Условные блоки" позже в этой главе Да Да WHEN {ошибка [, ошибка ...] I ANY} Синтаксис для обработки исключений. Аргу- ментами могут быть одно или более опреде- ленных пользователем исключений или внутренне определенные исключения GDSCODE ИЛИ SQLCODE. Подробности СМ. В главе 32 Да Да Программные конструкции В следующих разделах рассматриваются программные конструкции, распознаваемые в PSQL. Блоки BEGIN... END PSQL является структурированным языком. После объявления переменных про- цедурные операторы заключаются в операторные скобки begin и end. В процессе разработки логики процедуры могут быть добавлены другие блоки; любой блок может включать другой блок, заключенный в begin и end.
Глава 29. Разработка модулей PSQL 609 Символ терминатора не используется в ключевых словах begin и end, за исключением финального ключевого слова end, который закрывает процедурный блок и завершает оператор create procedure или create trigger. Это финальное ключевое слово end имеет специальный терминатор, который был определен в операторе set term до на- чала данного определения. Условные блоки PSQL распознает два типа условных структур; ♦ ветвление, управляемое блоками if ... then и, возможно, else; ♦ циклическое выполнение блока, пока условие while не станет ложным. Начиная с версии 1.5 логические контекстные переменные inserting, updating и deleting и целочисленная контекстная переменная row count доступны в качестве предикатов в блоках, выполняющих операции изменения состояния данных. Подроб- ности использования логических контекстных переменных в триггерах для множест- ва событий см. в главе 31. Конструкция IF... THEN... ELSE Конструкция if ... then ... else осуществляет ветвление в программе, проверяя указанное условие. Синтаксис; IF {<условие>) THEN <составной-оператор> [ELSE <составной-оператор>] < составной-оператор> = {<блок> | <оператор>;} Предложение условие является предикатом, который должен быть истинным, чтобы выполнился оператор или блок, следующий за then. Необязательное предложение else задает альтернативный оператор или блок, который будет выполняться, если условие окажется ложным. Условие может быть любым правильным предикатом. $ ПРИМЕЧАНИЕ. Предикат, проверяемый в if, должен быть заключен в скобки. Когда вы кодируете условный переход в SQL, использование предложения else ино- гда бывает необходимым для "нейтрализации" в случаях, когда проверяемый в if предикат может не иметь ни истинного, ни ложного значения. Такое может произой- ти, когда в предикате во время выполнения сравниваются два пустых (null) значения. Логические значения истина и ложь являются в этом случае невозможными. Ветвь else в этом случае является гарантией, что ваш блок выдаст результат. Следующий фрагмент кода иллюстрирует использование if ... else в предположе- нии, что first_name, last_name, и line2 были ранее объявлены как переменные или аргументы: 20 Зак. 420
610 Часть VII. Программирование на сервере IF (FIRSTJQAME IS NOT NULL) THEN LINE2 = FIRST_NAME I I ' ' I I LAST_NAME; ELSE BEGIN IF (LAST_NAME IS NOT NULL) THEN LINE2 = LASTNAME; ELSE LINE2 = 'NO NAME SUPPLIED1; END СОВЕТ. Программисты языка Pascal, заметьте, что if ... then завершен тер- минатором! По поводу CASE Пока PSQL не поддерживает логику case в качестве конструкции программирования. Логика выражения case, конечно, доступна в DSQL. Подробности см. в главе 21. Конструкция WHILE... DO while ... do является конструкцией цикла, который повторяет оператор или блок операторов, пока условие является истинным. Условие проверяется в начале каждого цикла, while ... do использует следующий синтаксис: WHILE (<условие>) DO BEGIN <выполнение одного или более операторов> ; <изменение значения операнда в условии> ; END /* Здесь продолжение выполнения */ В следующей простой процедуре while проверяет значение переменной 1, которая инициализируется как входной аргумент. Блок цикла уменьшает значение i при каж- дой итерации, и пока i остается больше нуля, значение выходного параметра г уве- личивается на значение i. Когда процедура завершается, значение г возвращается вызвавшему приложению. SET TERM л; CREATE PROCEDURE MORPH-ME (i INTEGER) RETURNS (r INTEGER) AS BEGIN r = 0; WHILE (i > 0) DO
Гпава 29. Разработка модулей PSQL 611 BEGIN г = г + i; i = i - 1; END END' Вызов процедуры из isql: SQL> EXECUTE PROCEDURE MORPH_ME(16); Мы получаем: R 136 Переменные Пять типов переменных может быть использовано в теле модуля с некоторыми огра- ничениями в соответствии с тем, является модуль хранимой процедурой или триг- гером. ♦ Локальные переменные для хранения значений используются только в триггерах и хранимых процедурах. ♦ Контекстные переменные new.имяСтолбца и old.имястолбца ограничены использо- ванием в триггерах; они хранят новые и старые значения каждого столбца табли- цы, когда оператор DML ожидает завершения. ♦ Другие контекстные переменные, специфичные в PSQL, также доступны в isql и PSQL. ♦ Входные аргументы используются для передачи значений хранимым процедурам из клиентских приложений, других хранимых процедур и триггеров. Недоступны в триггерах. ♦ Выходные аргументы используются для возвращения значений из хранимых про- цедур вызвавшим их объектам. Недоступны в триггерах. Любой из этих типов переменных может быть использован в теле хранимой про- цедуры везде, где может появиться выражение. Им может присваиваться значение литерала или значения, полученного из запроса или в результате вычисления выра- жения. Использование доменов Поскольку определение доменов потенциально может быть изменено, они не могут быть использованы на месте собственных типов данных SQL при объявлении пере- менных и аргументов хранимых процедур. Модули PSQL компилируются в двоич- ную форму во время их создания, и изменения доменов разрушат их, если допустить использование доменов.
612 Часть VII, Программирование на сервере Маркер двоеточия (:) для переменных В операторах SQL задавайте для имен переменных префикс в виде двоеточия (:), когда: ♦ переменная используется в операторе SQL; ♦ переменная получает значение в конструкции [for] select ... into. Опускайте двоеточие во всех других ситуациях. < ПРИМЕЧАНИЕ. Никогда не задавайте префикс двоеточия для контекстных пе- ременных. Операторы присваивания Процедура присваивает значения переменным с использованием синтаксиса: переменная - выражение; выражение может быть любой допустимой комбинацией переменных, операторов и выражений, оно может содержать вызовы внешних функций (UDF) и функций SQL, включая функцию gen_ido для увеличения и получения значения генератора. Следующий фрагмент кода выполняет некоторые присваивания: WHILE (SI < 9) DO BEGIN SI = SI + 1; /* арифметическое выражение */ IF (SUBSTRING(SMONTH FROM 1 FOR 1) - ’R’) THEN BEGIN RESPONSE = 'YES'; /* простая константа */ LEAVE; END SMONTH = SUBSTRING(SMONTH FROM 2); /* функциональное выражение */ END Переменным и аргументам должны присваиваться значения того типа данных, с ка- ким они были объявлены. Числовым переменным должны присваиваться числовые значения, а строковым — строковые значения. Хотя Firebird и выполняет автомати- ческое преобразование типов в некоторых случаях, желательно использовать явное преобразование, чтобы избежать непредвиденных несоответствий типов. Более подробную информацию о явном преобразовании и о конвертировании типов данных см. в главах 8 и 21. Локальные переменные Локальные переменные объявляются, каждая в отдельной строке, до первого опера- тора begin. Они не имеют значения вне хранимой процедуры или триггера, их об-
Гпава 29. Разработка модулей PSQL 613 ласть действия не распространяется на другие вызываемые процедуры. Они должны быть объявлены до их использования. Вам следует всегда инициализировать ваши переменные насколько возможно раньше в вашей процедуре. В Firebird 1.5 вы можете объявлять и инициализировать перемен- ную в одном операторе. Например, каждый следующий оператор допустим для объ- явления переменной вида счетчик и инициализации ее нулем: DECLARE VARIABLE COUNTER1 INTEGER DEFAULT 0; DECLARE VARIABLE COUNTER2 INTEGER = 0; Примеры использования локальных переменных Следующая процедура иллюстрирует фрагмент австралийской шутки о правильности пословицы: "Никогда не ешь свинину, если в названии месяца есть буква R". Она возвращает мнение по поводу полученной даты. Для иллюстрации в ней объявляется одна локальная переменная, которая используется для получения стартового условия для цикла while, и другая — для управления количеством повторов цикла. CREATE PROCEDURE I S_PORK_SAFE (CHECK_MONTH DATE) RETURNS (RESPONSE CHAR(3)) AS DECLARE VARIABLE SMONTH VARCHAR(9); DECLARE VARIABLE SI SMALLINT; BEGIN SI = 0; RESPONSE = ’NO '; SELECT CASE (EXTRACT (MONTH FROM :CHECK_MONTH) ) WHEN 1 THEN ’JANUARY’ WHEN 2 THEN ’FEBRUARY’ WHEN 3 THEN ’MARCH' WHEN 4 THEN 'APRIL' WHEN 5 THEN ’MAY’ WHEN 6 THEN ’JUNE’ WHEN 7 THEN ’JULY’ WHEN 8 THEN ’AUGUST’ WHEN 9 THEN 'SEPTEMBER' WHEN 10 THEN ’OCTOBER' WHEN 11 THEN ’NOVEMBER’ WHEN 12 THEN ’DECEMBER’ END FROM RDBS DATABASE INTO :SMONTH; WHILE (SI < 9) DO BEGIN SI = SI + 1; IF (SUBSTRING(SMONTH FROM 1 FOR 1) = ’R’) THEN BEGIN RESPONSE = ’YES’; LEAVE; END
614 Часть VII. Программирование на сервере SMONTH = SUBSTRING(SMONTH FROM 2); END END л COMMIT л SET TERM ;л Можно ли автору есть свинину в ее день рождения? EXECUTE PROCEDURE IS_PORK_SAFE ('2004-05-16'); RESPONSE NO СОВЕТ. Если бы это была серьезная процедура, в SQL есть более быстрый спо- соб получения того же результата. Например, вместо цикла while вы можете просто проверить переменную smonth: IF (SMONTH CONTAINING 'R') THEN RESPONSE = 'YES' ELSE RESPONSE = 'NO Вероятно, вы захотите использовать внешнюю функцию для получения названия месяца. В версии 1.5 вы можете инициализировать переменные при их объявле- нии. Входные аргументы Входные аргументы (также называемые параметрами) используются для передачи значений от приложения процедуре или от одного модуля PSQL другому. Они объ- являются списком в скобках следом за именем процедуры и отделяются друг от дру- га запятыми. Один раз объявленные, они могут использоваться в теле процедуры везде, где могут появиться выражения. Например, следующий фрагмент процедуры определяет один входной параметр, что- бы сообщить процедуре, какая страна должна отыскиваться: CREATE PROCEDURE SHOW_JOBS_FOR_COUNTRY ( COUNTRY VARCHAR(15)) Входные параметры передаются от вызывающей программы хранимой процедуре по значению. Это означает, что если процедура изменит значение входного параметра, это изменение будет иметь эффект только в процедуре. Когда управление возвраща- ется вызвавшей программе, входной параметр в любом случае будет иметь первона- чальное значение. Входные аргументы недопустимы в триггерах. Выходные аргументы Выходной аргумент (параметр) используется для задания значения, возвращаемого из процедуры вызвавшему приложению или модулю PSQL. Если задается множество
Глава 29. Разработка модулей PSQL 615 возвращаемых значений, объявляйте эти аргументы в скобках, отделяя их друг от друга запятыми, следом за ключевым словом returns в заголовке процедуры. Один раз объявленные, они могут использоваться в теле процедуры везде, где могут по- явиться выражения. Следующий код завершает определение процедуры, представленной предыдущим фрагментом. Он определяет три элемента данных для возвращения в виде виртуаль- ной таблицы вызвавшему модулю: CREATE PROCEDURE SHOW_JOBS_FOR_COUNTRY ( COUNTRY VARCHAR (15) ) RETURNS ( CODE VARCHAR (11) , TITLE VARCHAR (25) , GRA.DE SMALLINT) AS BEGIN FOR SELECT JOBJCODE, JOB_TITLE, JOB__GRADE FROM job WHERE JOB_COUNTRY = : COUNTRY INTO :CODE, :TITLE, :GRADE DO BEGIN /* начало цикла */ CODE = 'CODE: ' || CODE; /* дает немного информации о значении */ SUSPEND; /* выводит одну строку цикла */ END END Л Если вы объявляете выходные параметры в заголовке процедуры, процедура должна присвоить им значения, чтобы вернуть их вызвавшему приложению. Значения могут быть получены из любого допустимого выражения в процедуре. СОВЕТ. Всегда инициализируйте выходные параметры до начала обработки данных, которые могут быть присвоены параметрам. Контекстные переменные Л/ЕЖи OLD Триггеры могут использовать два полных набора контекстных переменных, пред- ставляющих "старое" и "новое" значение каждого столбца таблицы, old. имя-столбца ссылается на текущее или предыдущее значение именованного столбца в изменяемой или удаляемой строке. Это не имеет смысла для добавления данных, new.имя-столбца ссылается на значение, передаваемое запросом на изменение или добавление. Не имеет смысла для удаления. Если операция обновления не изменяет некоторые столбцы, то для таких столбцов переменная new будет иметь то же самое значение, что и переменная old. Контекстные переменные часто используются для сравнения значений столбцов до и после изменения. Контекстные переменные могут применяться везде, где могут быть использованы обычные переменные. Значение new для столбца строки может быть изменено только
616 Часть VII. Программирование на сервере до соответствующего действия. Значения old являются значениями только для чте- ния. Подробности и примеры использования см. в главе 31. СОВЕТ. Поскольку Firebird создает триггеры для реализации ограничений check, контекстные переменные old и new могут использоваться непосредственно в ог- раничениях check, например: ALTER TABLE EMPLOYEE ADD CONSTRAINT EMPLOYEE_SALARY_RAISE_CK CHECK ((OLD.SALARY IS NULL) OR (NEW.SALARY > OLD.SALARY)); Операторы SELECT... INTO Используйте оператор select с предложением into для поиска значений столбцов в таблицах и сохранения их в локальных переменных или выходных аргументах. Одиночный оператор SELECT Обычный оператор select в PSQL должен возвращать не более одной строки из базы данных— стандартный одиночный (singleton) оператор select. Если оператор воз- вращает более одной строки, то будет выдано исключение. Предложение order by недопустимо в одиночном select кроме случая, когда используется оператор select first 1. (Информацию об использовании квалификатора first см. в главе 21.) Обычные правила применяются к входному списку, предложению where и предложе- нию group by, если оно используется. Предложение into требуется и должно быть последним предложением в операторе. Пример одиночного оператора select в параметризованном запросе DSQL в прило- жении: SELECT SUM(BUDGET), AVG(BUDGET) FROM DEPARTMENT WHERE HEAD_DEPT = :head_dept; Для использования этого оператора в процедуре объявите локальные переменные или выходные аргументы и добавьте предложение into в конец: DECLARE VARIABLE ТОТ_BUDGET NUMERIC(18,2); DECLARE VARIABLE AVG_BUDGET NUMERIC(18,2); SELECT SUM(BUDGET), AVG(BUDGET) FROM DEPARTMENT WHERE HEAD_DEPT = :head_dept INTO :tot_budget, :avg_budget;
Гпава 29. Разработка модулей PSQL 617 Операторы SELECT для множества строк Любой модуль PSQL может оперировать с множеством входных строк, полученных из оператора select, когда он содержит структуру цикла, который может "переме- щаться" по набору и выполнять одинаковую обработку каждой строки. PSQL не мо- жет обрабатывать многострочные наборы другим способом, и при отсутствии кон- текста цикла многострочная выборка данных вызовет исключение ("Multiple rows in singleton select" — "Множество строк в одиночном операторе select"). Циклы FOR SELECT... Основным методом реализации структуры цикла для обработки многострочных входных наборов является структура for ... select ... into ... do. Его упрощен- ный синтаксис: FOR SELECT <список-спецификации-набора> FROM имя-таблицы [JOIN..] [WHERE..] [GROUP BY. . ] [ORDER BY. . ] INTO <сиисок-переменных> DO BEGIN < блок-обработки> [SUSPEND] ; END В качестве примера рассмотрим следующую процедуру, получающую набор от опе- ратора select, который передает строки, по одной за один раз, в буфер курсора про- цедуры. Она проходит по набору, устанавливая значения для набора переменных в соответствии со спецификацией таблицы. В конце цикла процедура добавляет запись во внешнюю таблицу. CREATE PROCEDURE PROJECT_MEMBERS AS DECLARE VARIABLE PROJ_NAME CHAR(23); DECLARE VARIABLE EMP_NO CHAR(6); DECLARE VARIABLE LAST^NAME CHAR(23); DECLARE VARIABLE FIRSTJNAME CHAR(18); DECLARE VARIABLE HIRE_DATE CHAR(12); DECLARE VARIABLE JOB_TITLE CHAR(27) ; DECLARE VARIABLE CRLF CHAR (2); BEGIN CRLF = ASCIIJCHAR(13) i |ASCIIJCHAR(10); /* Windows EOL - признак конца строки */ FOR SELECT DISTINCT P.PROJ_NAME, E.EMP NO,
618 Часть VII. Программирование на сервере E.LAST_NAME, E.FIRST_NAME, E.HIREJDATE, J. JOBJTITLE FROM EMPLOYEE E JOIN JOB J ON E.JOB_CODE = J.JOB_CODE JOIN EMPLOYEE_PROJECT EP ON E.EMP_NO = EP.EMP_NO JOIN PROJECT P ON P.PROJ_ID = EP.PROJ_ID ORDER BY P.PROJ_NAME, E.LAST_NAME, E. FIRST-NAME INTO /* переменные для столбцов */ :PROJ_NAME, :EMP_NO, :LAST_NAME, :FIRST_NAME, :HIRE_DATE, :JOBJTITLE DO BEGIN /* начинает цикл присваивания значений переменным */ PROJ_NAME = ""II CAST (PROJ_NAME AS CHAR(20) ) I I "" I I ', ' ; EMP_NO = CAST(EMP_NO AS CHAR (5) ) | | ' , ' ; LAST_NAME = ”"|| CAST (LAST_NAME AS CHAR(20) ) I I "" I I ', 1 ; FIRST_NAME = ""|| CAST (FIRST_NAME AS CHAR (15) ) I I "" I I ' , ’ ; HIRE-DATE = CAST(HIRE_DATE AS CHAR(11)) I I ' , ' ; JOBJTITLE = ""| | CAST (JOB-TITLE AS CHAR (25) ) I I ; INSERT INTO EXT-FILE VALUES (:PROJ_NAME, :EMP_NO, :LAST_NAME, :FIRST_NAME, :HIRE_DATE, : JOBJTITLE, :CRLF) ; END /* завершает цикл DO */ END Л ВНИМАНИЕ! Если выходному параметру не присваивается значение, его значе- ние будет непредсказуемым, что может привести к ошибкам. Процедура должна обеспечить инициализацию всех выходных параметров до начала процесса при- сваивания значений; это должно гарантировать, что оператор suspend передаст допустимые выходные данные. SUSPEND Оператор suspend имеет специфическое использование в конструкции for ... select ... into ... do. Если suspend включен в цикл do, то после того, как select прочтет строку в переменные строки, цикл будет ждать, когда эта строка будет выведена в кэш строк сервера перед получением следующей строки из курсора select. Такая операция позволяет создавать в Firebird хранимые процедуры выбора. В следующей главе мы более подробно рассмотрим использование операторов select, которые возвращают множество строк в хранимые процедуры, и технику на- писания хранимых процедур выбора. Оператор suspend недопустим в триггерах. В выполняемых хранимых процедурах он имеет тот же эффект, что и оператор exit — т. е. он немедленно завершает процеду- ру, а все операторы, следующие за ним, никогда не будут выполняться.
Глава 29. Разработка модулей PSQL 619 По контрасту, если процедура выбора имеет выполняемые операторы, следующие за последним оператором suspend в процедуре, то все эти операторы будут выполнены, даже если нет больше строк, возвращаемых вызвавшей программе. Процедуры тако- го рода завершаются финальным оператором end. Операторы управления потоком PSQL содержит множество операторов, которые влияют на поток управления в кодах модулей. Только что рассмотренный оператор suspend передает управление назад вызвавшей процедуре или клиентской программе, ожидая, когда только что обрабо- танная строка будет получена из кэша строк сервера. EXIT В процедурах выбора и в выполняемых процедурах exit вызывает переход на фи- нальный оператор end в процедуре. Он не имеет смысла в триггерах. Поведение операторов suspend, exit и end описано в табл. 29.2. Таблица 29.2. Операторы suspend, exit и end Тип модуля SUSPEND EXIT END Процедура выбора Выполнение приостанавли- вается, пока вызвавшее приложение или процедура получает следующий набор выходных переменных Возвращает значения (если присутствуют) и переходит на финаль- ный END Возвращает управление приложению и устанав- ливает SQLCODE В 100 Выполняемая процедура Переходит на финальный end— не рекомендуется Переходит на фи- нальный END Возвращает значения и передает управление приложению Триггеры Никогда не используется Переходит на фи- нальный END Передает управление следующему триггеру той же фазы (before или after), что и у текущего, если тот существует. Иначе завершает работу триггеров этой фазы LEAVE В Firebird 1.5 появился оператор leave для выхода из блоков. Он заменил оператор break, который был частично реализован в версии 1.0.x. Вот пример его использова- ния в цикле while нашей процедуры is_pork__safe: WHILE (SI < 9) DO BEGIN SI = SI + 1; /* арифметическое выражение */
620 Часть VII. Программирование на сервере IF (SUBSTRING(SMONTH FROM 1 FOR 1) = 'R') THEN BEGIN RESPONSE = 'YES'; /* простая константа */ LEAVE; END SMONTH = SUBSTRING(SMONTH FROM 2); /* функциональное выражение */ END leave приводит к выходу из цикла — в нашем случае останавливается проверка букв слова на символ "R". Если ветвь, содержащая оператор leave, не выполняется, то вы- полнение продолжается до конца цикла. EXCEPTION Оператор exception останавливает выполнение и передает управление первому блоку обработки исключений (блоку, начинающемуся с ключевого слова when), который может обработать исключение. Если для этого исключения не найден обработчик, управление передается финальному оператору end и процедура завершается аварий- но. Когда происходит такое, один или более кодов исключения передается назад кли- енту через вектор состояния ошибок (массив). Оператор exception используется в блоке if ... then ... else для вызова пользова- тельских исключений, предварительно определенных как объекты базы данных. Сер- вер Firebird вызывает свои собственные исключения для SQL и контекста ошибок. Поток управления в этих случаях точно такой же, как и при вызове пользовательских исключений. Синтаксис и техники вызова и обработки исключений описаны в главе 32. EXECUTE STATEMENT Firebird 1.5 вводит расширение PSQL, поддерживающее выполняемые строки. При- ложение или процедура могут передать оператор DSQL (DDL или DML) в виде стро- ки входного аргумента (либо процедура может сконструировать эту строку как ло- кальную переменную) для выполнения с использованием execute statement. execute statement добавляет гибкости хранимым процедурам и триггерам, но с высо- ким риском ошибок. Возвращаемые значения жестко проверяются на типы данных, чтобы избежать непредсказуемых исключений преобразования данных. Например, строка '1234' может быть преобразована в целое, a 'abc' вызовет ошибку преобразова- ния. Во время компиляции такая строка не может быть проанализирована и прове- рена. Синтаксис: [FOR] EXECUTE STATEMENT <строка> [INTO :переменная! [, :переменная2 [, :переменная!!] ] ] DO <составной-оператор>]; <составной-оператор> = {оператор | блок-операторов]
Глава 29. Разработка модулей PSQL 621 Конструирование выражения или строковой переменной для создания оператора DSQL в строке аргумента должно быть завершено к моменту выполнения execute statement. Выполняемый оператор DSQL в строке аргумента не может содержать никаких заменяемых параметров. В своей простейшей форме execute statement выполняет оператор SQL, запраши- вающий операцию, которая не возвращает строк данных, а именно: ♦ INSERT, UPDATE, DELETE; ♦ EXECUTE procedure; ♦ любой оператор DDL за исключением create/drop database. Например: CREATE PROCEDURE EXEC_PROC (PROC_NAME VARCHAR (31)) AS DECLARE VARIABLE SQL VARCHAR(1024); DECLARE VARIABLE . . . ; BEGIN SQL = 'EXECUTE PROCEDURE ' ( | PROC_NAME; EXECUTE STATEMENT SQL; END Л Процедура вызывает следующее: EXECUTE PROCEDURE EXEC_PROC ( ’ PROJECT—MEMBERS ’ ) ; Переменные значения в одиночном SELECT Следующий фрагмент кода показывает, как выполнять строку оператора select, ко- торый возвращает одну строку в набор переменных. Как и любой другой оператор select в модуле PSQL, он вызовет исключение, если оператор вернет множество строк. Здесь мы также можем выполнить нечто невозможное в обычном PSQL: вы- полнить операцию, включающую таблицу или столбец, чье имя неизвестно во время компиляции: CREATE PROCEDURE SOME-PROC (TABLE-NAME VARCHAR (31), COL_NAME VARCHAR (31)) AS DECLARE VARIABLE PARAM DATE; BEGIN EXECUTE STATEMENT 'SELECT MAX ( ' || COL-NAME || ') FROM 'H TABLE-NAME INTO : PARAM; FOR SELECT .... FROM .... WHERE END-DATE = : PARAM INTO ... DO END Л
622 Часть VII. Программирование на сервере Переменные значения в многострочном SELECT Синтаксис execute statement также поддерживает выполнение оператора select внут- ри цикла for для возвращения по одной строки за раз в список переменных. Не суще- ствует ограничений на используемый оператор select, однако помните, что во время компиляции синтаксический анализатор не может проверить содержимое строки. CREATE PROCEDURE DYNAMIC-SAMPLE ( TEXT_COL VARCHAR(31), TABLE_NAME VARCHAR (31)) RETURNS (LINE VARCHAR(32000)) AS DECLARE VARIABLE ONE_LINE VARCHAR (100) ; DECLARE VARIABLE STOP_ME SMALLINT; BEGIN LINE = ''; STOP-ME = 1; FOR EXECUTE STATEMENT 'SELECT ' || TEXTCOL |I ' FROM ’ || TABLE_NAME INTO : ONE__LINE DO BEGIN IF (STOP—ME > 320) THEN EXIT; IF (ONE_LINE IS NOT NULL) THEN LINE = LINE | | ONE_LINE H ’ ' ; STOP—ME = STOP—ME + 1; END SUSPEND; END Л Предостережения Средство execute statement предназначено только для очень осторожного использо- вания и должно применяться с учетом всех факторов. Используемые в нем операции медленны и рискованны. Возьмите за правило применять его только в случае невоз- можности получить нужные результаты другими средствами или (что маловероятно) когда это действительно улучшает выполнение оператора. Будьте в курсе, что есть риск: ♦ не существует способа проверить синтаксис оператора в строке аргумента; ♦ не проверяются зависимости или существование защиты для предотвращения удаления или изменения таблиц или столбцов; ♦ операции выполняются медленно, потому что встроенный оператор должен под- готавливаться на сервере каждый раз перед выполнением; ♦ если хранимая процедура имеет специальные привилегии к некоторым объектам, то динамический оператор, выдаваемый в строке execute statement, не наследует их. Используются те привилегии, которые имеет пользователь, выполняющий процедуру.
Глава 29. Разработка модулей PSQL 623 POSTJEVENT События Firebird предоставляют механизм сигнализации, который позволяет прило- жениям прослушивать изменения базы данных, сделанные параллельно выполняю- щимися приложениями, без необходимости расходовать ресурсы CPU или использо- вать сетевые ресурсы, опрашивая друг друга. Синтаксис оператора: POST_EVENT имя-события; Это приводит к тому, что событие имя-события "отправляется" в стек, имя-события мо- жет быть любой строкой, содержащей до 78 символов и не требующей предвари- тельного определения на сервере. Помещенные в стек события будут отправлены клиентам, "прослушивающим" события с помощью обработчика извещений (event alerter). Когда транзакция подтверждается, все сообщения, появившиеся в триггерах и хра- нимых процедурах, отправляются прослушивающим клиентским приложениям. При- ложение может отреагировать на сообщение, например, прочитав заново используе- мый набор данных. Подробности см. в разд. "События" главы 32. Разработка модулей Разработка модулей PSQL является жизненно важной частью деятельности, как раз- работчика, так и администратора базы данных. Поскольку запросы администратора Firebird ясны, обычно на практике эти две роли объединяются. В процессе разработ- ки обычно довольно большое количество разработчиков группы разрабатывает, тес- тирует и изменяет программные модули серверной стороны одновременно. Следова- тельно, меры, принимаемые для проектирования и стандартизации кода приложений, также важны и для кода PSQL. Добавление комментариев Код хранимой процедуры должен быть комментирован для помощи в отладке и в разработке приложений. Комментарии особенно важны в хранимых процедурах, по- тому что процедуры являются глобальными в базе данных и могут использоваться несколькими разработчиками приложений. В заголовок и тело модуля PSQL могут быть включены и многострочные, и встроен- ные комментарии. Синтаксис комментариев подробно обсуждался в главе 14 в разд. "Скрипты схемы". Чувствительность к регистру и пробелы Если при создании вами объектов базы данных были использованы квотированные идентификаторы, то все правила чувствительности к регистру, которые применялись
624 Часть VII. Программирование на сервере к вашим данным динамического SQL, должны также применяться, когда вы ссылае- тесь на эти объекты в операторах процедуры. Весь остальной код не является чувствительным к регистру. Например (предполагая, что не использовались идентификаторы объектов с разделителями), следующие два фрагмента операторов являются эквивалентными: CREATE PROCEDURE MYPROC... create procedure myproc... Компилятор не устанавливает ограничений на количество пробелов или символов перевода строки. Для читаемости вашего кода может быть полезным применение какой-либо формы стандартных соглашений о расположении вашего процедурного кода. Например, вы можете писать все ключевые слова в верхнем регистре, выделять код блоков, размещать объявления переменных на отдельных строках, помещать за- пятые-разделители в начале строки и т. д. Управление вашим кодом Учитывая, что языком высокого уровня для программирования на стороне сервера в Firebird является язык SQL и что исходный код представляется серверу в форме "супероператоров" DDL для компиляции в объекты базы данных, не удивительно, что вся поддержка кодов также выполняется с использованием операторов DDL. Эти операторы совместимы с соглашениями по поддержке других объектов в базе данных SQL. ♦ Переопределению скомпилированных объектов (хранимых процедур и триггеров) служит синтаксис alter procedure i trigger. Для хранимых процедур Firebird также предоставляет синтаксис recreate procedure и (начиная с версии 1.5) create или replace procedure. ♦ Операторы drop procedure i trigger используются для удаления модулей. Существует два способа управления процедурами и триггерами: интерактивно вво- дить операторы с использованием isql или другого инструмента, который может пе- редавать DSQL, или с использованием одного или более выходных файлов, содер- жащих операторы определения данных, называемых скриптами. Интерактивный интерфейс кажется более быстрым и простым способом делать эти вещи — но только до первого раза, когда вам понадобится что-нибудь изменить, проверить и пересоздать. Использование скриптов рекомендуется, потому что скрип- ты не только предоставляют необходимую документацию кодов, но также могут со- держать комментарии и могут легко модифицироваться. Инструменты редактирования Может быть использован любой текстовый редактор ASCII, который не сохраняет непечатаемые символы за исключением символа перевода строки (ASCII 13), возвра- та каретки (ASCII 10) и символа табуляции (ASCII 9). Некоторые редакторы имеют
Гпава 29. Разработка модулей PSQL 625 средства подсветки ключевых слов SQL: редакторы IDE для Borland Delphi и Kylix, а также некоторые другие инструменты, представленные в приложении 5. СОВЕТ. Утилита командной строки isql может быть использована в качестве редактора при употреблении команды edit. Эта команда будет использовать выбранный вами текстовый редактор, если вы установите в вашей системе соот- ветствующие переменные окружения. В POSIX установите переменную окруже- ния visual или editor. В Windows установите EDITOR. Полезной практикой является добавление расширения sql к именам файлов скриптов Firebird. Помимо пользы от идентификации скриптов в вашей файловой системе, расширение sql будет распознаваться как пакетный файл SQL многими инструмента- ми редактирования, которые поддерживают подсветку синтаксиса SQL. Компиляция хранимых процедур и триггеров Для компиляции любого файла скрипта вы должны включить в файл, по крайней ме- ре, одну "пустую строку" после последнего оператора или комментария. Чтобы сде- лать это, нажмите, по меньшей мере, один раз клавишу <Return> (Enter) в вашем текстовом редакторе. Когда вы завершите создание вашей процедуры, сохраните ее в файле с любым понравившимся вам именем. Для компиляции вашей хранимой процедуры просто выполните ваш скрипт с ис- пользованием команды input в isql или в интерфейсе обработки скриптов вашего ин- струмента управления базой данных. Ошибки в скриптах Firebird генерирует ошибки в процессе синтаксического разбора, если присутствует некорректный синтаксис в операторе create procedure । trigger. Сообщения об ошибках выглядят следующим образом: Dynamic SQL Error - SQL error code = -104 - Token unknown - line 4, char 9 -tmp Нумерация строк начинается co строки, содержащей оператор create, а не с начала файла скрипта. Символы подсчитываются слева направо, а неопределенный элемент указывается либо в виде номера первого символа источника ошибки, либо в виде номера крайнего правого символа источника ошибки. Если есть сомнения, проверяй- те всю строку для определения источника синтаксической ошибки. СОВЕТ. Если вы используете версию isql, более позднюю, чем Firebird 1.0, вы заметите улучшение ее возможностей в описании ошибок в скриптах и указании их расположения. Хотя Firebird не содержит средств отладки хранимых процедур, некоторые инструменты сторонних разработчиков имеют такие возможности.
626 Часть VII. Программирование на сервере Зависимости объектов Сервер Firebird педантичен при поддержке информации о взаимозависимостях между управляемыми им объектами. Необходимы специальные соглашения при выполне- нии изменений хранимых процедур, находящихся в текущий момент в использова- нии другими пользователями. Процедура находится "в использовании" (procedure is in use), когда она в настоящий момент выполняется, или если она была внутренне скомпилирована в кэш метаданных по запросу пользователя. Более того, сервер бу- дет откладывать или запрещать компиляцию операторов alter или drop, если ском- пилированная версия будет найдена в кэше метаданных. Изменения процедур не будут видны клиентским приложениям, пока они не отсо- единятся от базы данных и вновь не присоединятся к ней. ПРИМЕЧАНИЕ. Триггеры и хранимые процедуры, вызывающие процедуры, ко- торые были изменены или заново созданы, не имеют доступа к новой версии, .-..' Ь пока база данных не окажется в состоянии, когда все клиенты будут отключены. Идеальный вариант — выдача операторов create, recreate и alter для модулей PSQL в то время, когда не выполняется ни одно клиентское приложение. Изменение и удаление модулей Когда вы изменяете процедуру или триггер, новое определение процедуры заменяет старую версию. Для изменения определения процедуры или триггера выполните сле- дующие шаги: 1. Скопируйте файл исходного определения данных, содержащий оператор создания процедуры. В другом варианте — используйте isql -extract для выделения ис- ходного текста процедуры или триггера из базы данных в текстовый файл. 2. Отредактируйте файл, заменив create на recreate или alter и изменив определе- ние желаемым образом. 3. Выполните измененный скрипт при "чистых" условиях, как было описано ранее. Для удаления модуля выполните: DROP {PROCEDURE | TRIGGER} имя-модуля; Привилегии Только пользователь SYSDBA или владелец процедуры/триггера может изменять или удалять его. Ошибка "Объект находится в использовании" Ошибка "Object is in use" (Объект находится в использовании) расстраивает разра- ботчика более чем какая-либо другая. Вы соединились с базой данных как пользова- тель SYSDBA или владелец базы данных. У вас исключительный доступ, что жела- тельно при изменении метаданных, и вдруг появляется какой-то фантомный пользе-
Гпава 29. Разработка модулей PSQL 627 ватель, использующий объект, метаданные которого вы собираетесь изменить или удалить. Источником этой загадки может быть одна или более следующих ситуаций. ♦ При останове базы данных (shut down), подготовке к получению исключительного доступа вы (или другой человек) уже были соединены как пользователь SYSDBA, владелец или (в Linux/UNIX) как пользователь с подходящими привилегиями операционной системы. При проверке условий останова базы данных Firebird иг- норирует таких пользователей и все их текущие транзакции или те, которые за- пускаются после начала останова. Любая неподтвержденная транзакция (какой бы она ни была — даже select), которая использует этот объект или любой объект, зависящий от этого объекта, или объект, от которого зависит наш объект, будет вызывать эту ошибку. ♦ "Заинтересованная транзакция", которая остается в базе данных в результате не- нормального завершения работы какого-либо пользователя и которая использует зависимости, связанные с нашим объектом, вызовет такую ошибку. ♦ Вы или другой пользователь с подходящими привилегиями ранее пытались пере- определить или удалить этот объект или другой зависимый объект, и операция была отвергнута по той причине, что объект находился в использовании. Д ВНИМАНИЕ! Такая ситуация может провоцировать цепочку несогласованностей в вашей базе данных. Например, если gbak выполнялся в то время, когда база данных имела объекты в таком состоянии, то восстановление резервной копии может оказаться невозможным. Всякий раз, когда вы видите такую ошибку и верите, что вы насколько возможно уменьшили вероятность ее появления, рассматривайте ее как сигнал, что ваша база данных нуждается в проверке до того, как вы продолжите любые дальней- шие изменения метаданных (см. главу 39). Для просмотра в isql списка процедур или триггеров и их зависимостей используйте команду show procedures или show triggers соответственно. Удаление исходных текстов модулей Разработчики часто хотят "спрятать" исходные коды их модулей PSQL при распро- странении баз данных. Вы можете удалить хранимые исходные тексты без воздейст- вия на возможности модуля. Только убедитесь, что у вас есть последние версии скриптов, прежде чем это делать! Исходные тексты всех модулей хранятся в системной таблице rdb$procedures и RDB $ TRIGGERS. Удаление исходного текста процедуры: UPDATE RDBSPROCEDURES SET RDB$PROCEDURE_SOURCE = NULL WHERE RDB$PROCEDURE_NAME = 'MYPROC;
628 Часть VII. Программирование на сервере Удаление исходного текста триггера: UPDATE RDB$TRIGGERS SET RDB$TRIGGER_SOURCE = NULL WHERE RDB$TRIGGER_NAME = 'MYTRIGGER'; ВНИМАНИЕ! Имейте в виду, что такое удаление исходных кодов не остановит тех, кто серьезно собирается украсть ваш исходный код. Исполняемый код хра- нится в двоичном формате, который очень просто может быть преобразован об- ратно в PSQL. Следовательно, решите, будет ли выгода от утаивания PSQL больше затрат, которые вы и другие, кто поддерживает систему, понесут от потери возможности просмотра и выделения исходного текста. Пора дальше Далее мы подробно рассмотрим возможности языка PSQL и техники, которые вы можете использовать для разработки хранимых процедур и структуризации вашего кода. Специальная тема в конце главы описывает rdb$db_key, внутренний уникальный атрибут каждой строки в каждом наборе, который может быть полезен при оптими- зации выполнения некоторых операций PSQL.
ГЛАВА 30 Хранимые процедуры Процедура является самостоятельной программой, написанной на языке PSQL Fire- bird, скомпилированной интерпретатором во внутренний двоичный язык Firebird и сохраненной как исполняемый код в метаданных базы данных. Однажды скомпили- рованная, хранимая процедура может быть вызвана непосредственно из приложения или другого модуля PSQL с использованием оператора execute procedure или select в соответствии с заданным стилем процедуры. Хранимые процедуры могут принимать входные параметры от клиентских приложе- ний в качестве аргументов вызываемого запроса. Они могут возвращать приложени- ям набор значений в качестве выходных параметров. Язык процедур и триггеров Firebird включает SQL-операторы манипулирования дан- ными и некоторые мощные расширения, в том числе конструкции if ... then ... else, while ... do, for select ... do, определенные в системе исключения, обработку ошибок и события. Хранимые процедуры могут быть вызваны из приложений с использованием дина- мических операторов SQL. Они также могут быть вызваны интерактивно из isql и из многих других инструментов работы с базами данных, рекомендованных для исполь- зования с Firebird. Исполняемые модули, включая вложенные процедуры, могут быть использованы в скриптах с тем ограничением, что все входные параметры являются константами и не существует выходных наборов. В скриптах не существует возмож- ности передавать переменные параметры. Выполняемые хранимые процедуры Процедуры, которые вызываются с помощью оператора execute procedure, могут воз- вращать одну строку из одного или более выходных значений. Они часто использу- ются для выполнения операций добавления, изменения или удаления или для запуска набора операций, таких как пакетный импорт или экспорт данных. Хранимые процедуры выбора Хранимые процедуры выбора названы так, потому что они написаны с использова- нием некоторых специальных расширений языка для создания многострочных вы- ходных наборов, возвращающихся вызвавшей программе, которая использовала за- прос select — "виртуальные таблицы".
630 Часть VII. Программирование на сервере Сервер не различает процедуры выбора и выполняемые процедуры. Если требуется, он попытается выбрать набор записей из выполняемой процедуры или выполнить что-нибудь в процедуре выбора— и, естественно, будет вызывать исключение, если возникнут ошибки в запросе! Это ваша задача убедиться, что ваш код на сервере де- лает именно то, что вы от него ожидали, и что код приложения посылает соответст- вующие запросы. Создание хранимых процедур В вашем скрипте или в isql начните с установки символа терминатора, который будет использован для отметки конца синтаксиса create procedure. Следующий пример устанавливает символ терминатора в &: SET TERM &; Синтаксис оператора: CREATE PROCEDURE имя-процедуры [(аргумент тип-данных [, аргумент тип-данных [...]])] [RETURNS (аргумент тип-данных [, аргумент тип-данных [...]]) AS < тело -процедурна <тело-процедуры> = [DECLARE [VARIABLE] переменная тип-данных; [-.-]]] BEGIN <составной-оператор>; END <терминатор> Элементы заголовка Объявляйте в заголовке: ♦ имя процедуры, которое обязательно и должно быть уникальным в базе данных, например: CREATE PROCEDURE MyProc ♦ любые необязательные входные параметры (аргументы), требуемые в процедуре, с их типами данных. Список заключается в скобки, параметры отделяются друг от друга запятыми, например: CREATE PROCEDURE MyProc (invarl integer, invar2 date) ♦ имя каждого аргумента должно быть уникальным в процедуре. Тип данных может быть любым стандартным типом данных SQL за исключением массива типов
Глава 30. Хранимые процедуры 631 данных. Не требуется соответствия имен входных аргументов именам параметров в вызывающей программе; ♦ любые необязательные выходные параметры (аргументы), требуемые в процеду- ре, с их типами данных. Список следует за ключевым словом returns. Список за- ключается в скобки, параметры отделяются друг от друга запятыми, например: CREATE PROCEDURE MyProc (invarl INTEGER, invar2 DATE) RETURNS (outvarl INTEGER, outvar2 VARCHAR(20), outvar3 DOUBLE PRECISION) ♦ имя каждого аргумента должно быть уникальным в процедуре. Тип данных может быть любым стандартным типом данных SQL за исключением массива типов данных; ♦ ключевое слово as, которое обязательно: CREATE PROCEDURE MyProc (invarllNTEGER, invar2 DATE) RETURNS(outvarl INTEGER, outvar2 VARCHAR(20), outvar3 DOUBLE PRECISION) AS Элементы тела Синтаксис: < тело-процедуры> = [< списох-объявлений-переменных>] < соста вной - опера тор> Локальные переменные Если вам нужно объявить локальные переменные, то это следует сделать далее. Каж- дое объявление завершается точкой с запятой. В версии 1.5 переменные при их объ- явлении могут инициализироваться. Синтаксис: <список-объявлений~переменных> = DECLARE [VARIABLE] переменная тип-данных [{ т = | I DEFAULT} значение] ; [DECLARE [VARIABLE] переменная тип-данных; . ..] Пример: CREATE PROCEDURE MyProc ( invar 1 INTEGER, invar2 DATE) RETURNS ( outvarl INTEGER, 0Utvar2 VARCHAR(20), 0Utvar3 DOUBLE PRECISION)
632 Часть VII. Программирование на сервере AS DECLARE VARIABLE localvar integer DEFAULT 0; DECLARE VARIABLE anothervar DOUBLE PRECISION = 0. ОСЬ- ПРИМЕЧАНИЕ. Ключевое слово variable необязательно в версии 1.5 и выше. Главный блок кода Следом идет главный блок кода, обозначенный в описании синтаксиса как <состав- ной -операторе. Он начинается ключевым словом begin и заканчивается ключевым СЛОВОМ END. Синтаксис: <составной-оператор> = BEGIN < составной-опера тор> [<составной-оператор> . . . ] END <терминатор> Все структуры <составной-оператор> состоят из одного оператора и/или других струк- тур <составной-оператор>, которые могут включать другие вложенные структуры, на- пример: CREATE PROCEDURE MyProc ( invar 1 INTEGER, invar2 DATE) RETURNS ( outvarl INTEGER, outvarZ VARCHAR(ZO), outvar3 DOUBLE PRECISION) AS DECLARE VARIABLE localvar integer DEFAULT 0; DECLARE VARIABLE anothervar DOUBLE PRECISION = 0.00; BEGIN < составной-оператор> END & Элементами в <составной-оператор> могут быть: любой одиночный оператор, блок операторов и вложенные блоки операторов, заключенные в операторные скобки begin и end. Блоки могут включать: ♦ операторы присваивания, устанавливающие значения локальным переменным и входным/выходным параметрам; ♦ операторы select для помещения значений столбцов в переменные. Операторы select должны иметь предложение into в качестве последнего предложения и объявления соответствующих локальных переменных или выходных аргументов для каждого выбранного столбца;
Гпава 30. Хранимые процедуры 633 ♦ структуры циклов, такие как for select ... do и while ... do для выполнения условных или циклических задач; ♦ структуры ветвления с использованием if ... then ... [else]; ♦ операторы execute procedure для вызова других процедур с необязательным пред- ложением returning_values для получения значений переменных. Допустима ре- курсия; ♦ операторы suspend и exit, возвращающие управление и, возможно, значения вы- звавшему приложению или модулю PSQL; ♦ комментарии для аннотирования кода процедуры; ♦ операторы exception для возврата приложениям пользовательских сообщений об ошибках или для задания условий для обработчиков исключений; ♦ операторы when для обработки особых или общих условий ошибок. ♦ операторы post_event для добавления в стек сообщений о событиях. Пример: BEGIN FOR SELECT COL1, COL2, COL3, COL4 FROM TABLEA INTO :COL1, :COL2, :COL3 DO BEGIN < опера торы> END < опера торъ£> END & SET TERM ; & COMMIT; Обратите внимание на завершение всего объявления процедуры символом термина- тора, ранее определенного оператором set term. После текста тела процедуры символ терминатора устанавливается в значение по умолчанию точка с запятой. Так посту- пать следует не всегда. В скриптах DDL, где вы объявляете несколько модулей PSQL, вы можете сохранять альтернативный оператор текущим. Некоторые люди на практике используют альтернативный терминатор во всех своих скриптах, таким об- разом резервируя точку с запятой только для завершения оператора PSQL. Здесь дело в личных предпочтениях. Выполняемые процедуры Когда вы работаете с языком хранимых процедур Firebird и структурами программ- ных модулей, необходимо различать процедуры, которые выполняются с целью из- менения данных, и процедуры, которые собираются возвращать виртуальную табли- цу вызвавшему приложению, как и оператор select. Первый вид больше всего знаком тем, кто использовал в работе другие системы управления базами данных, — это вы- полняемые процедуры.
634 Часть VII. Программирование на сервере Сложная обработка Одним из наиболее очевидных и общих способов использования выполняемых про- цедур является выполнение сложных вычислений над входными данными и выпол- нение изменений одной или более таблиц. Сложные бизнес-правила и подпрограммы размещаются на сервере. Любое клиентское приложение с соответствующими раз- решениями может вызывать эти подпрограммы и получать результаты, независимо от включающего языка программирования. Не говоря об экономии многих часов на программирование и тестирование, выполнение на стороне сервера снижает риск нарушения целостности, который возникает за счет повторения одних и тех же слож- ных операций в различных клиентских языковых средах. Поддержка "живых" клиентских наборов Многие клиентские интерфейсы реализуют классы наборов данных или наборов запи- сей, которые получают выходные наборы от операторов select. Такие клиентские классы обычно предоставляют методы DML, которые выбирают одну строку из бу- фера, хранящего выход курсора серверной стороны. Строка выбирается пользовате- лем, и объект класса использует уникальный ключ строки для моделирования пози- ционированного изменения или удаления из таблицы базы данных. Для добавления объект "открывает пустую строку", вводит список столбцов того же типа, что и в бу- фере, и принимает ключ и другие значения в качестве входных данных для столбцов. Одиночный оператор update, delete или insert в SQL может оперировать только с одной таблицей. Когда набор данных (набор записей) выбирается из обычной табли- цы и содержит уникальный ключ таблицы, он может рассматриваться как "живой", потому что его методы могут передавать операторы update, delete или insert. Обыч- ным термином для такого типа набора является естественно изменяемый. Набор, являющийся соединением нескольких таблиц, не будет естественно изменяемым. Выполняемые хранимые процедуры могут быть созданы с входными аргументами, которые принимают ключи и значения для множества таблиц и выполняют требуе- мые операции над каждой таблицей. Такая техника позволяет клиентским приложе- ниям трактовать соединенные наборы, как если бы они были "живыми". Операции в выполняемых процедурах Практически любое манипулирование данными в SQL доступно в выполняемой хра- нимой процедуре. Все действия выполняются в контексте транзакции вызвавшей процедуру программы и подтверждаются, когда подтверждается эта транзакция. Для строк, измененных операциями в процедуре, создаются версии точно таким же спо- собом, как если бы они были отправлены запросами DML с клиента. Процедуры могут вызывать другие процедуры, передавая переменные в качестве входных аргументов и получая возвращаемые значения в переменные, используя предложение returning-values. Они могут добавлять одну или множество строк, из- менять отдельные строки, формировать курсоры для серии строк для позициониро- вания изменений и удалений и выполнять поисковые изменения и удаления.
Гпава 30. Хранимые процедуры 635 Когда процедура начинает выполняться, значения, переданные ей в качестве входных аргументов, становятся локальными переменными. Выходные аргументы являются переменными чтения/записи и могут изменять значения (но не тип данных) в процес- се работы много тысяч раз. На рис. 30.1 иллюстрируется типичная деятельность вы- полняемой процедуры. Многотабличные процедуры Выполняемая процедура delete_employee является версией процедуры, которую вы можете найти в базе данных employee в вашем каталоге Firebird /examples. Она реали- зует некоторые бизнес-правила для служащих, покидающих компанию. Объявление исключения Поскольку мы собираемся использовать исключение в этой процедуре, его нужно создать до создания процедуры: CREATE EXCEPTION REASSIGNJSALES 'Reassign the sales records before deleting this employee.’ л COMMIT A (Переназначьте записи продаж перед удалением этого служащего)
636 Часть VII. Программирование на сервере Процедура Теперь сама процедура. Входной аргумент emp_num соответствует первичному ключу таблицы employee— emp_no. Он позволяет процедуре выбирать и работать с одной записью служащего, а внешние ключи из других таблиц ссылаются через него на эту запись. CREATE PROCEDURE DELETE_EMPLOYEE ( EMP_NUM INTEGER ) AS DECLARE VARIABLE any_sales INTEGER DEFAULT 0; BEGIN Мы собираемся выяснить, имеет ли этот служащий какие-либо незавершенные зака- зы на продажи. Если да, мы выдаем исключение. В главе 32 мы соберем такую же процедуру и реализуем некоторую дополнительную обработку для перехвата исклю- чения и обработки этого условия прямо внутри процедуры. Сейчас же мы позволим процедуре завершиться и использовать это сообщение об исключении для информи- рования вызвавшей программы об этой ситуации. Конструкция SELECT... INTO Конструкция select ... into обычна для PSQL. Когда из таблицы запрашиваются значения, предложение into позволяет сохранить их в переменных — в локальных переменных или в выходных аргументах. В этой процедуре нет выходных парамет- ров. Мы используем переменную any sales, которую мы объявили и инициализиро- вали в начале тела процедуры для хранения счетчика записей продаж. Обратите вни- мание на префикс двоеточия (:) у переменной any_sales. Мы рассмотрим это, когда процедура будет готова. SELECT count(po_number) FROM sales WHERE sales_rep = :emp_num INTO :any_sales; IF (any_sales > 0) THEN EXCEPTION reassign_sales; В случае если такие записи заказов будут найдены, процедура аккуратно завершается на операторе exception, который при отсутствии обработчика исключений передает выполнение прямо на самый последний оператор end процедуры. В этих условиях процедура завершается, а сообщение об исключении передается вызвавшей про- грамме1. 1 Эта процедура является очень скромным примером программирования в PSQL. В SQL существует лучший способ проверить существование строк, чем их подсчет. В главе 32 мы снова будем обсуждать эту процедуру, выполнив некоторые изменения в ней, чтобы показать это. Если вы посмотрите исходные коды процедуры в базе данных, вы также заметите, что операторы suspend и exit щедро разбросаны в разных местах, где они не нужны. Операторы suspend и exit имеют идентичное использование в выполняемой процедуре. При этом в про- цедурах выбора оператор suspend применяется особо. Для ясности и эффективности докумен- тирования предпочтительно исключить использование suspend в качестве синонима exit.
Гпава 30. Хранимые процедуры 637 Если нет исключения, выполнение продолжается. Затем процедура должна вы- полнить небольшую работу по изменению некоторых позиций вакантной долж- ности (null), если она сохраняется для нашего служащего, удалить служащего из проектов и удалить его (или ее) историю продаж. Под конец удаляется сама запись служащего. UPDATE department SET mngr_no = NULL WHERE mngr_no = :emp_num; UPDATE project SET team_leader = NULL WHERE team_leader = :emp_num; DELETE FROM employee_project WHERE emp_no = :emp_num; DELETE FROM salary_history WHERE emp_no = :emp_num; DELETE FROM employee WHERE emp_no = ;emp_num; Работа сделана, служащий ушел. Необязательный оператор exit может быть включен в текст с целью документирования. Он может быть весьма полезным, если вы про- сматриваете скрипты, содержащие множество определений процедур, а эти процеду- ры имеют много вложенных блоков begin ... end: EXIT; END Л COMMIT Л Префикс двоеточия (:) для переменных В этой процедуре мы заметили два различных способа использования префикса двое- точия в переменных. ♦ Раньше применялось обращение к локальной переменной -.any sales, когда она использовалась в предложении into для помещения элемента данных, возвращае- мого оператором select. ♦ В более поздних операторах она использовалась с другими целями. Синтаксис PSQL требует наличия префикса двоеточия для любой переменной или аргумента, когда они используются в операторе DSQL. Эти два способа использования префикса двоеточия являются постоянными в PSQL. Если вы забыли, где их нужно применять, или используете их там, где PSQL не тре- бует, то ваша процедура не будет компилироваться, а синтаксический анализатор вызовет исключение. Хуже, если переменная с тем же именем, что и столбец табли- цы, используется в операторе SQL без двоеточия. Сервер считает, что это ссылка на столбец, выполняет оператор и вызывает исключение. Нечего и говорить, что резуль- тат такой операции будет непредсказуемым.
638 Часть VII. Программирование на сервере Использование (вызов) выполняемых процедур Выполняемая процедура вызывается оператором execute procedure. Она может воз- вращать не более одной выходной строки. Для выполнения хранимой процедуры в isql используйте следующий синтаксис: EXECUTE PROCEDURE имя [(] [ аргумент [, аргумент . . . ] ] [) ] ; Имя процедуры должно быть задано. Значения входных аргументов Правила, касающиеся аргументов, следующие: ♦ значения должны быть заданы для всех входных аргументов; ♦ если есть несколько входных аргументов, они должны отделяться друг от друга запятыми; ♦ каждый аргумент является константой, выражением, преобразуемым в константу или заменяемым параметром; ♦ переменные могут передаваться как входные аргументы только внутри модуля PSQL; ♦ заменяемые параметры могут передаваться только внешним операторам DSQL; ♦ константы и выражения, которые преобразуются в константы, являются допусти- мыми для любого вызова; ♦ выражения, которые оперируют с переменными или заменяемыми параметрами, недопустимы; ♦ скобки, заключающие список аргументов, необязательны. Поскольку наша процедура delete__employee не возвращает аргументов, синтаксис ее вызова из клиентского приложения и из другой процедуры одинаков: EXECUTE PROCEDURE DELETE__EMPLOYEE (29) ; При этом, когда процедура вызывается из другой процедуры, входные аргументы могут быть (и обычно бывают) представлены переменными. Поскольку execute procedure является оператором DSQL, синтаксис требует, чтобы имя переменной имело префикс точку с запятой: EXECUTE PROCEDURE DELETE^EMPLOYEE (:EMP_NUMBER); Другая процедура add_emp_proj получает два входных аргумента, ключ служащего и проекта соответственно. Пример вызова может быть таким: EXECUTE PROCEDURE ADD_EMP_PROJ (32, 'MKTPR'); Заменяемые параметры используются для входных аргументов при вызове этой про- цедуры из клиентского приложения: EXECUTE PROCEDURE ADD_EMP_PROJ (?, ?) ;
Гпава 30. Хранимые процедуры 639 Выводы и выходы Если выходному параметру не было определено значение, то его значение непред- сказуемо, и это может привести к ошибке, иногда достаточной для нарушения цело- стности данных. В процедуре следует обеспечить инициализацию всех выходных параметров значениями по умолчанию, до того как нужные значения получатся в процессе обработки и будут выданы при выполнении операторов suspend и exit. EXIT к SUSPEND В процедурах выбора и в выполняемых процедурах оператор exit приводит к немед- ленному переходу к финальному оператору end без выполнения других операторов. Что произойдет, если процедура достигнет финального оператора end, зависит от ее типа. ♦ В процедуре select код sqlcode будет установлен в 100 для указания того, что больше нет найденных строк, а управление перейдет вызвавшей программе. ♦ В выполняемой процедуре управление перейдет к вызвавшей программе с пере- дачей ей выходных значений, если они присутствуют. Вызовы из триггеров или процедур получают выходные данные через переменные, как задано в returning_values. Приложение получает их в структуре записи. В выполняемых процедурах suspend имеет точно такой эффект, что и end. Рекурсивные процедуры Если процедура вызывает саму себя, она является рекурсивной. Рекурсивные про- цедуры полезны для задач, включающих повторяющиеся шаги. Каждое обращение к процедуре называется экземпляром (instance), поскольку каждое обращение к процедуре является отдельной сущностью, которая выполняется так же, как если бы она была вызвана из приложения; она резервирует память и стек с уче- том требований выполнения ее задач. Хранимые процедуры могут иметь глубину вложений не более 1000. Такое ограни- чение помогает предотвратить бесконечные циклы, которые могут появиться, если в процедуре не задано условие завершения цикла. Однако ограничения памяти и раз- мера стека могут сделать количество вложенных уровней меньше 1000. Процедура dept_budget в примере базы данных employee иллюстрирует работу рекур- сивной процедуры. Она принимает в качестве входа код dno, эквивалентный коду dept_no— ключу таблицы department. Таблица department имеет многоуровневую древовидную структуру: каждый отдел, не являющийся головным отделом, имеет внешний ключ headjdept, ссылающийся на dept_no своего непосредственного "роди- теля". Процедура обращается к таблице department по этому полученному ключу. Она со- храняет значение budget этой строки в выходной переменной тот. Она также выпол- няет подсчет количества отделов, непосредственно предшествующих данному отделу в структуре отделов. Если нет подотделов, оператор exit осуществляет переход сразу
640 Часть VII. Программирование на сервере на финальный оператор end л. Текущее значение тот становится выходом процедуры, и процедура завершается. SET TERM Л ; CREATE PROCEDURE DEPT_BUDGET ( DNO CHAR(3) ) RETURNS ( TOT DECIMAL(12,2) ) AS DECLARE VARIABLE sumb DECIMAL(12, 2); DECLARE VARIABLE rdno CHAR(3); DECLARE VARIABLE ent INTEGER; BEGIN tot = 0; SELECT budget FROM department WHERE dept_no = :dno INTO :tot; SELECT count(budget) FROM department WHERE head_dept = :dno INTO :cnt; IF (ent = 0) THEN EXIT; Если существуют подотделы, то выполнение продолжается. Входной код dno исполь- зуется в предложении where курсора for ... select (см. разд. "Курсоры в PSQL”) для выделения по очереди каждой строки из таблицы department, которая содержит в ко- де headjdept то же значение, что и в dno, и помещения значение из dept_no этой стро- ки в локальную переменную rdno: FOR SELECT dept_no FROM department WHERE head_dept = :dno INTO :rdno DO BEGIN Эта локальная переменная теперь становится входным кодом для рекурсивного вы- зова процедуры. При каждой рекурсии выходное значение тот увеличивается на зна- чение возвращаемого бюджета, пока не будут обработаны все соответствующие строки: EXECUTE PROCEDURE dept_budget :rdno RETURNING_VALUES :sumb; tot = tot + sumb; END Под конец возвращаемое значение, аккумулированное в рекурсиях, передается вы- звавшей программе, и процедура завершается: EXIT; /* оператор EXIT необязателен */ ENDA COMMIT^ Вызов процедуры На этот раз наша процедура имеет входные параметры. Наш простой вызов в DSQL может выглядеть следующим образом: EXECUTE PROCEDURE DEPT BUDGET ('6001);
Гпава 30. Хранимые процедуры 641 Или же мы можем использовать заменяемый параметр: EXECUTE PROCEDURE DEPT_BUDGET (?); Курсоры в PSQL Курсоры состоят из трех основных элементов: ♦ набора строк, определенных выражением select; ♦ указателя, который перемещается через набор от первой строки к последней, изо- лируя строку для некоторого вида деятельности; ♦ набора переменных— определенных как локальные переменные, выходные ар- гументы или и те, и другие, — для получения значения столбцов, возвращаемых каждой строкой при ссылке на них указателя. Проход указателя через набор может рассматриваться как операция "цикла". Опера- ции, выполняющиеся в процессе этого "цикла", когда указатель ссылается на строку, могут быть простыми или сложными. PSQL имеет две реализации курсора, одна хорошо известная, а другая не была ранее документирована. ♦ Хорошо известная реализация представлена конструкцией for ... select, которая полностью реализует синтаксис цикла и широко используется в процедурах вы- бора. Она подробно обсуждается в следующем разделе. При этом также допусти- мо выполнение цикла курсора for ... select и внутри выполняемых процедур, как мы видели в предыдущем примере. ♦ Менее известной реализацией является ранняя реализация, иногда называемая изменяемым или именованным курсором, которая была наследована от ESQL и долгое время была осуждаема. Она представляет объект курсор и позволяет вы- полнять изменения и удаления. Ее синтаксис будет более понятен тем, кто ис- пользовал курсоры в языках процедур в других СУБД. Сейчас мы его вкратце рассмотрим. JF ПРИМЕЧАНИЕ. Синтаксис и механизм именованного курсора были заново реа- -X лизованы в процессе разработки Firebird 2. Это краткое описание является лебе- \ диной песней старых курсоров, поскольку новый синтаксис вряд ли будет совмес- тимым со старым. "Старый" изменяемый курсор Его синтаксис выглядит следующим образом: FOR SELECT <список- столбцов> FROM <имя-таблицы> FOR UPDATE INTO <переменные> 21 Зак. 420
642 Часть VII. Программирование на сервере AS CURSOR <имя-курсора> DO /* либо UPDATE . . . */ BEGIN UPDATE <1Шя~таблицЫ> SET ... WHERE CURRENT OF <имя-курсора>; END /* или DELETE */ BEGIN DELETE FROM <имя~таблицьС> WHERE CURRENT OF <имя-курсора> END Используйте его сейчас, но ждите изменений в более поздних версиях Firebird. Это очень быстрый способ добавить множество операций в хранимую процедуру, по- скольку здесь используется rdbSdb_key для локализации записей. rdb$db_key (или просто dbkey) является внутренней возможностью, которая может быть использована во множестве ситуаций. См. разд. "Темы оптимизации: использование внутреннего RDBSDBKEY". СОВЕТ. Просмотрите заметки по релизу для нового синтаксиса курсора PSQL. На момент написания данной книги описание отсутствовало. Более поздняя реализация курсоров, обсуждаемая далее, является более гибкой. Хранимые процедуры выбора Хранимые процедуры выбора называются так, потому что они разрабатываются для выполнения с помощью оператора select. Для читателей, привыкших к техникам программирования на сервере, доступным в других СУБД, концепция хранимой про- цедуры, которая передает строки непосредственно вызвавшему приложению или процедуре без создания промежуточной "временной" таблицы, будет менее понят- ной, чем выполняемые процедуры. Использование процедур выбора Процедуры выбора могут быть использованы для виртуального задания любого на- бора, однако они особенно полезны, когда нужен набор, который не может быть из- влечен или его извлечение выполняется медленно или слишком сложно в одном опе- раторе DSQL. Эзотерические наборы Техника хранимых процедур выбора предоставляет колоссальную гибкость для из- влечения наборов; эта техника превосходит логику, доступную в спецификациях
Глава 30. Хранимые процедуры 643 обычных операторов select. Она делает возможным создавать набор буквально из любой комбинации хранимых у вас данных. Вы можете выполнять вычисления и трансформацию множества столбцов и строк в выходной набор. Например, выходные наборы с промежуточными суммами сложно или невозможно получить из динамиче- ского набора, но они могут быть быстро и эффективно сгенерированы с помощью хранимых процедур выбора. Процедуры выбора могут легко генерировать наборы из данных, которые вообще не хранятся в базе данных. Мы все иногда находим применение этой технике. В сле- дующем тривиальном примере список строк, разделенных запятыми, каждая из кото- рых содержит 20 или менее символов, поступает в качестве входа. Процедура воз- вращает приложению каждую строку в нумерованном виде: CREATE PROCEDURE BREAKAPART( INPUTLIST VARCHAR(1024)) RETURNS ( NUMERO SMALLINT, ITEM VARCHAR(20) ) AS DECLARE CHARAC CHAR; DECLARE ISDONE SMALLINT = 0; BEGIN NUMERO = 0; ITEM = ' ' ; WHILE (ISDONE = 0) DO BEGIN CHARAC = SUBSTRING (INPUTLIST FROM 1 FOR 1); IF (CHARAC = '') THEN ISDONE = 1; IF (CHARAC = ' OR CHARAC = '') THEN BEGIN NUMERO = NUMERO + 1; SUSPEND; /* Отправляет строку в буфер строк */ ITEM = " ; END ELSE ITEM = ITEM |I CHARAC; INPUTLIST = SUBSTRING(INPUTLIST FROM 2); END END Л COMMIT; /* */ SELECT * FROM BREAKAPART ('ALPHA, BETA, GAMMA, DELTA; NUMERO ITEM 1 ALPHA 2 BETA 3 GAMMA 4 DELTA
644 Часть VII. Программирование на сервере Выигрыш в производительности для сложных наборов Часто сложные запросы, включающие множество соединений или подзапросов, бы- вают слишком медленными, чтобы удовлетворить интерактивные приложения. Неко- торые запросы могут быть медленными по причине непропорциональности индексов внешних ключей. Поскольку есть возможность оперировать с наборами во внутрен- них циклах, хранимые процедуры способны создавать требуемые наборы гораздо быстрее и к тому же начинаются возвращать строки раньше, чем позволяют обычные последовательности SQL. Техника Техника извлечения и манипулирования данными выходного набора использует кур- сор для чтения по порядку каждой строки из оператора select в предварительно объ- явленный набор переменных. Часто это могут быть выходные аргументы, куда по- мещаются значения столбцов, однако это могут быть и локальные переменные. Внутри цикла с переменными выполняются действия соответствующим образом: преобразовываются для вычислений, если необходимо, или используются как аргу- менты поиска для вложенных циклов, чтобы получать данные из других запросов. В конце цикла, когда все выходные аргументы получают конечные значения, опера- тор suspend приводит к паузе в выполнении, пока этот набор передается в кэш строк. Выполнение возобновляется, когда вызывается следующая пересылка. Как мы видели в предыдущем примере breakapart, оператор suspend является тем элементом, который заставляет процедуру передавать строку. Конструкция FOR SELECT... DO Для поиска множества строк в процедуре мы используем конструкцию for select ... do. Ее синтаксис: FOR <выражение -выбора> INTO <:переменная [, :переменная [, ...]] DO < составной-оператор>; <выражение-выбора> может быть любым запросом выбора, использующим соединения, объединения, просмотры, другие процедуры выбора, вызовы функций и т. д. в любой допустимой комбинации. Оператор for select отличается от стандартного оператора select тем, что требует наличия переменных, в которые помещаются значения столбцов, и спецификации полей. <составной~оператор> может быть одним оператором suspend или блоком из двух или более операторов. <составной-оператор> может иметь вложенные составные опера- торы.
Гпава 30. Хранимые процедуры 645 for select ... do является конструкцией цикла, которая отыскивает строку, задан- ную в <выражении-выбора>, и выполняет для каждой строки оператор или блок опера- торов, следующих после do. Предложение into <переменные> обязательно и должно быть последним2. Обработка в цикле На рис. 30.2 проиллюстрированы типичные виды деятельности, которые могут вы- полняться внутри циклов для генерации выхода в хранимой процедуре выбора. В следующих примерах мы посмотрим на то, как комбинации операций в PSQL мо- гут представить более интересную область SQL. 2 ESQL, "супермножество" DSQL, имеет небольшое отличие в синтаксисе предложения into. Там into помещается сразу после ключевого слова select и квантификатора строки (ес- ли присутствует). Водворение into в конец оператора в PSQL позволяет использовать наборы union в качестве входа для курсоров PSQL.
646 Часть VII. Программирование на сервере Простая процедура с вложенными операторами SELECT Процедура выбора org_chart, которая присутствует в примере базы данных employee, не получает входных аргументов. Она использует цикл for ... select, чтобы строить набор из ссылающегося на себя соединения таблицы department и передавать значе- ния столбцов по одной строке за раз набору переменных — некоторые из них ло- кальные, некоторые объявлены как выходные аргументы. CREATE PROCEDURE ORG_CHART RETURNS ( HEADJDEPT CHAR(25), DEPARTMENT CHAR(25), MNGR_NAME CHAR(2 0) , TITLE CHAR(5), EMP_CNT INTEGER ) AS DECLARE VARIABLE mngr__no INTEGER; DECLARE VARIABLE dno CHAR(3); BEGIN FOR SELECT h.department, d.department, d.mngr_no, d.dept_no FROM department d LEFT OUTER JOIN department h ON d.head_dept = h.dept_no ORDER BY d.dept_no INTO :head_dept, :department, :mngr_no, :dno DO Каждый раз, когда цикл обрабатывает строку, он помещает значение ключа (mngr_no) в локальную переменную mngr_no. Если эта переменная имеет пустое значение, про- цедура создает значения для выходных аргументов mngr_name и title. Если же эта переменная имеет значение, она передается как аргумент поиска вложенному запросу к таблице employee, уникально идентифицирующему строку и выделяющему имя и код работы менеджера отдела. Эти значения передаются остальным выходным аргу- ментам. BEGIN IF (:mngr_no IS NULL) THEN BEGIN mngr_name = ’ —TBH— '; title = ''; END ELSE SELECT ful1_name, j ob_code FROM employee WHERE emp_no = :mngr_no INTO :mngr_name, :title; SELECT COUNT (emp_no) FROM employee
Гпава 30. Хранимые процедуры 647 WHERE dept_no = :dno INTO :emp_cnt; Когда присвоены все выходные значения для одной строки, оператор suspend переда- ет строку в кэш. Управление передается опять на начало цикла, когда выполнен сле- дующий запрос на пересылку. SUSPEND; END ENDA COMMITЛ Обратите внимание, как аккуратно вложенный запрос обходит ту проблему, которую мы имели с подзапросами в DSQL— мы могли в подзапросе вернуть одно и только одно значение. Если нам нужно много значений, а логика левого соединения не рабо- тает, то нам пришлось бы использовать множество подзапросов с множеством набо- ров алиасов для выделения каждого значения из его курсора. Вызов процедуры выбора Синтаксис вызова процедуры выбора очень похож на синтаксис обращения к таблице или к просмотру. Единственным отличием является то, что процедура может иметь входные аргументы: SELECT <список~столбцов> FROM имя {[аргумент [, аргумент ...]]) WHERE <условия~поиска> ORDER BY <список-упорядочения^ имя процедуры должно быть задано. Правила входных аргументов идентичны правилам для выполняемых процедур — см. ранее разд. "Значения входных аргументов". <список-етолбцов>— разделенный запятыми список из одного или более выходных параметров, возвращаемых процедурой, или * для выбора всех столбцов. Выходной набор может быть ограничен условиями поиска в предложении where и упорядочен с помощью предложения order by. Вызов процедуры ORG_CHART Эта процедура не имеет входных параметров, следовательно, вызов select выглядит как простой выбор в таблице, а именно: SELECT * FROM ORG_CHART; Выбор агрегатных значений из процедур В дополнение к получению значений из процедуры вы можете использовать агрегат- ные функции. Например, для использования нашей процедуры с целью отображения количества отделов применяйте следующий оператор: SELECT COUNT (DEPARTMENT) FROM ORG__CHART;
648 Часть VII. Программирование на сервере Аналогично, для отображения с помощью org chart максимального и среднего коли- чества служащих в каждом отделе используйте следующий оператор: SELECT MAX(EMP_CNT), AVG(EMP_CNT) FROM ORG CHART; СОВЕТ. Если процедура получит ошибку или исключение, агрегатные функции не вернут правильных значений, поскольку процедура завершается до обработки всех строк. Вложенные процедуры Хранимая процедура сама может вызывать хранимую процедуру. Каждый раз, когда хранимая процедура вызывает другую хранимую процедуру, такой вызов называется вложенным, потому что он появляется в контексте предыдущего и все еще активного вызова первой процедуры. Хранимая процедура, вызываемая другой хранимой про- цедурой, называется вложенной процедурой. Следующая процедура возвращает список пользователей, ролей и привилегирован- ных объектов базы данных с их привилегиями SQL. Внутри процедуры два вложен- ных вызова другой процедуры. Необходимо начать с определения и подтверждения вложенной процедуры — иначе внешняя процедура выдаст ошибку при подтвержде- нии. Вы всегда должны начинать с нижней части "цепочки" при создании процедур, использующих вложенные процедуры. Приведенная далее вложенная процедура не выполняет операторов SQL3. Она просто берет непонятную константу из набора, используемого внутренне в Firebird для пред- ставления типов объектов, и возвращает строку, более осмысленную для человека: SET TERM Л ; CREATE PROCEDURE SP_GET—TYPE ( IN_TYPE SMALLINT ) RETURNS ( STRING VARCHAR(7) ) AS BEGIN STRING = 'Unknown'; IF (IN_TYPE = 0) THEN STRING = 'Table'; IF (IN_TYPE = 1) THEN STRING = 'View'; IF (IN_TYPE = 2) THEN STRING = 'Trigger'; IF (IN_TYPE = 5) THEN STRING - 'Proc'; IF (IN-TYPE = 8) THEN STRING = 'User'; 3 В Firebird 1.5 работа, выполняемая в данной вложенной процедуре, может быть реализо- вана с помощью выражения case. Подробности см. в главе 21.
Гпава 30. Хранимые процедуры 649 IF (IN_TYPE = 9) THEN STRING = 'Field'; IF (IN—TYPE = 13) THEN STRING = 'Role'; ENDA COMMIT л Теперь о внешней процедуре. Запрашиваемая в ней таблица является системной таб- лицей rbd$user_privileges. Она использует множество техник манипулирования, включая вызовы внутренней SQL-функции casto и внешней функции rtrimо из стандартной библиотеки внешних функций ib_udf для преобразования элементов charod в varcharoi). Мы это делаем, потому что собираемся выполнять конкатена- цию некоторых из этих строк и нам не нужны конечные пробелы. SET TERM ' ; CREATE PROCEDURE SP-PRIVILEGES RETURNS ( Q—ROLE—NAME VARCHAR (31), ROLE_OWNER VARCHAR(31), USER_NAME VARCHAR(31), Q—USER_TYPE VARCHAR (7), W—GRANT—OPTION CHAR(l), PRIVILEGE CHAR(6), GRANTOR VARCHAR. (31) , QUALIFIED-OBJECT VARCHAR(63), Q-OBJECT-TYPE VARCHARO) ) AS DECLARE VARIABLE RELATION-NAME VARCHAR(31); DECLARE VARIABLE FIELD-NAME VARCHAR(31); DECLARE VARIABLE OWNER-NAME VARCHAR(31); DECLARE VARIABLE ROLE_NAME VARCHAROI); DECLARE VARIABLE OBJECT—TYPE SMALLINT; DECLARE VARIABLE USER—TYPE SMALLINT; DECLARE VARIABLE GRANT_OPTION SMALLINT; DECLARE VARIABLE IS-ROLE SMALLINT; DECLARE VARIABLE IS-VIEW SMALLINT; BEGIN Вначале мы создадим цикл по таблице rbd$useR-Privileges, выделяя и направляя не- которые значения прямо в выходные аргументы, а другие в локальные переменные: FOR SELECT RTRIM(CAST(RDB$USER AS VARCHAR(31))), RDB$USER_TYPE, RTRIM (CAST (RDB$GRANTOR AS VARCHAROI))), RTRIM (CAST (RDB$RELATION_NAME AS VARCHAR (31))) , RTRIMfCAST (RDB$FIELD_NAME AS VARCHAROI))), RDB$ OBJECT—TYPE, RTRIM (CAST (RDB$PRIVILEGE AS VARCHAROI))), RDBSGRANT OPTION
650 Часть VII. Программирование на сервере FROM RDB$USER_PRIVILEGES INTO :USER-NAME, :USER-TYPE, : GRANTOR, :RELATION—NAME, : FIELD-NAME, :OBJECT_TYPE, : PRIVILEGE, : GRANT-OPTION Взяв текущее значение выходной переменной user name, мы обращаемся к rdb$roles для получения владельца роли и имени роли в случае, когда "пользователь" текущей строки фактически является ролью. Если же это не роль, то эти поля будут представ- лены на выходе в виде пунктира: DO BEGIN SELECT RTRIM (CAST (RDBSOWNER—NAME AS VARCHAR ( 31))) , RTRIM (CAST (RDB$ROLE_NAME AS VARCHAR(31))) FROM RDB?ROLES WHERE RDB$ROLE_NAME = :USER_NAME INTO : ROLE-OWNER, : ROLE—NAME; IF (ROLE-NAME IS NOT NULL) THEN Q_ROLE_NAME = ROLE-NAME; ELSE BEGIN Q_ROLE_NAME = ' - ' ; ROLE_OWNER = 1 -'; END with grant option является специальной привилегией, о которой мы хотим сообщить в нашем выводе. Следовательно, мы преобразуем этот атрибут в 'Y', если атрибут присутствует (1), или в пробел, если отсутствует: IF (GRANT—OPTION = 1) THEN W_GRANT—OPTION = 'Y'; ELSE W_GRANT—OPTION = ' ' ; Теперь другой запрос к rdb$roles, на этот раз для поиска объекта, к которому приме- няется привилегия роли. Если такой найден, мы добавляем к имени этого объекта полезный префикс. Если это не роль, мы проверяем, является ли наш объект столб- цом таблицы, и присваиваем его имени квалификатор. I-S-ROLE = NULL; SELECT 1 FROM RDBSROLES WHERE RDB$ROLE_NAME = :RELATION—NAME INTO :IS_ROLE; IF (IS_ROLE = 1) THEN QUALIFIED-OBJECT = '(Role) '||RELATION—NAME; ELSE BEGIN IF ( (FIELD_NAME IS NULL) OR (RTRIM (FIELD_№JyIE) = '')) THEN FIELD-NAME = '';
Гпава 30. Хранимые процедуры 651 ELSE FIELD_NAME = '.'IIFIELD_NAME; QUALIFIED-OBJECT = RELATION—NAME | | FIELD_NAME; END В rbdSuseR—privileges и таблицы, и просмотры имеют тип объекта 0. Это не слишком хорошо для нас, значит, следующий запрос проверяет по таблице rdbSrelations, явля- ется ли этот конкретный объект просмотром: IF (OBJECT_TYPE = 0) THEN BEGIN IS—VIEW = 0; SELECT 1 FROM RDB$RELATIONS WHERE RDB $ RELATION-NAME = : RELATION-NAME AND RDB$VIEW_SOURCE IS NOT NULL INTO :IS—VIEW; IF (IS—VIEW = 1) THEN OBJECT_TYPE = 1; END В этой точке нашего цикла мы получили почти все, что хотели. Однако наш объект все еще имеет свой внутренний номер, и мы все еще не знаем тип "пользователя". Пользователями могут быть не только люди. Именно здесь мы выполняем вложен- ные вызовы для выполнения трансляции внутренних номеров в осмысленные строки. Когда мы это сделаем, наша запись готова к выводу в кэш строк, и мы вызываем suspend для завершения цикла. Возвращаемые значения Вызовы вложенных процедур из триггеров или хранимых процедур почти идентичны вызовам, которые мы используем в DSQL. Синтаксис отличается только там, где мы обрабатываем возвращаемые значения. В DSQL сервер передает возвращаемые зна- чения клиенту в виде структуры записи. В хранимых процедурах мы используем ключевое слово PSQL returning_values и предоставляем переменные для получения этих значений. EXECUTE PROCEDURE SP_GET_TYPE(:OBJECT—TYPE) RETURNING-VALUES (:Q_OBJECT—TYPE); EXECUTE PROCEDURE SP_GET_TYPE (:USER_TYPE) RETURNING-VALUES (:Q_USER_TYPE); SUSPEND; END END^ Вызов процедуры Вот еще один простой вызов: SELECT * FROM SP_PRIVILEGES;
652 Часть VII. Программирование на сервере Если нам не нужны все столбцы или хотим получить их в особом порядке, мы можем сделать это. Предположим, мы просто хотим посмотреть привилегии всех пользова- телей-людей, отличных от SYSDBA: SELECT USER__NAME, QUALI FIED_OBJECT, PRIVILEGE FROM SP_PRIVILEGES WHERE Q_USER_TYPE = 'User' AND USER_NAME <> 'SYSDBA' ORDER BY USER_NAME, QUALIFIED__OBJECT; Могут быть использованы заменяемые параметры поиска: SELECT USER_NAME, QUALIFIED_OBJECT, PRIVILEGE FROM SP_PRIVILEGES WHERE Q_USER_TYPE = ? ORDER BY USER_NAME, QUALIFIED_OBJECT; СОВЕТ. Вы можете найти эту процедуру полезной для проверки привилегий SQL в вашей базе данных. Информацию об установке привилегий см. в главе 35. Процедура с промежуточными итогами В этой процедуре мы обрабатываем записи таблицы sales базы данных employee. Мы получим два итога: один для каждого вида продаж, а другой — для всех продаж. В качестве входа мы просто используем начальную и конечную даты для группы интересующих нас продаж. SET TERM л; CREATE PROCEDURE LOG_SALES ( .START_DATE DATE, END_DATE DATE) RETURNS (REP_NAME VARCHAR (37) , CUST VARCHAR(25), ORDDATE TIMESTAMP, ITEMTYP VARCHAR(12), ORDTOTAL NUMERIC(9,2), REPTOTAL NUMERIC(9,2), RUNNINGTOTAL NUMERIC(9, 2)) AS DECLARE VARIABLE CUSTNO INTEGER; DECLARE VARIABLE REP SMALLINT;
Гпава 30. Хранимые процедуры 653 DECLARE VARIABLE LASTREP SMALLINT DEFAULT -99; DECLARE VARIABLE LASTCUSTNO INTEGER DEFAULT -99; BEGIN RUNNINGTOTAL = 0.00; FOR SELECT CUST_NO, SALES_REP, ORDER_DATE, TOTAL_VALUE, ITEM_TYPE FROM SALES WHERE ORDER-DATE BETWEEN :START—DATE AND :END-DATE + 1 ORDER BY 2, 3 INTO :CUSTNO, :REP, :ORDDATE, :ORDTOTAL, :ITEMTYP Заметьте, что мы используем упорядоченный набор. Если вы получаете виртуальную таблицу из хранимой процедуры выбора и вам нужен упорядоченный набор, полезно сделать набор упорядоченным внутри кода процедуры. Оптимизатор может улуч- шить здесь производительность, если существуют полезные индексы, в то время как упорядочение, применяемое к выходному набору, не может использовать индексов по своей природе. Внутри цикла мы начинаем использовать данные для нашей строки и для получения итогов. Мы используем немного магии, чтобы избежать повторений имени — это выглядит изящнее при отображении только для чтения — хотя вы не должны делать этого, если вашему приложению нужно получить строки в произвольном порядке и оно использует этот столбец в качестве ключа поиска! Мы управляем именем поку- пателя похожим образом для исключения ненужного поиска, когда тот же покупа- тель появляется в последовательных записях. DO BEGIN IF(REP = LASTREP) THEN BEGIN REPTOTAL = REPTOTAL + ORDTOTAL; REP_NAME = "" ; END ELSE BEGIN REPTOTAL = ORDTOTAL; LASTREP = REP; SELECT FULL_NAME FROM EMPLOYEE WHERE EMP-NO = :REP INTO :REP-NAME; END IF (CUSTNO <> LASTCUSTNO) THEN BEGIN SELECT CUSTOMER FROM CUSTOMER
654 Часть VII. Программирование на сервере WHERE CUST_NO = :CUSTNO INTO :CUST; LASTCUSTNO - CUSTNO; END RUNNINGTOTAL = RUNNINGTOTAL + ORDTOTAL; SUSPEND; Теперь наша строка готова и отправляется в кэш строк вместе с двумя обновленными итогами. END ENDA SET TERM Вызов процедуры Наши входные аргументы имеют тип данных date— начальная дата и конечная дата. Процедура отыскивает тип данных timestamp для выборки строк для курсора. Она добавляет один день к конечной дате, чтобы гарантировать получение каждой строки вплоть до конца этого дня. Это упрощает дела: когда мы вызываем процедуру, нам нужно только предоставить первую и последнюю даты при отсутствии беспокойства по поводу записей с датой более поздней, чем полночь последнего дня. Следующий вызов возвращает целую таблицу: SELECT * FROM LOG_SALES ('16.05.1970', CURRENT_DATE); Мы можем обратиться к процедуре с использованием параметров: SELECT * FROM LOG_SALES (?, ?); Просмотр массива в хранимой процедуре Если в таблице есть столбцы, определенные как массивы, вы не сможете просмотреть данные в таком столбце в простом операторе select, потому что в таблице хранится только идентификатор массива. Хранимая процедура может быть использована для отображения значений массива, если размерность и тип данных столбца массива за- ранее известны. Таблица job в примере базы данных имеет столбец language req, содержащий тре- буемые языки. Столбец определен как массив из пяти элементов varchar(15). Следующий пример использует хранимую процедуру для просмотра содержимого этого столбца. Процедура использует цикл for ... select для поиска каждой строки из таблицы job, где столбец language_req не является пустым. Затем цикл while оты- скивает каждый элемент массива и возвращает значение вызвавшему приложению. SET TERM л; CREATE PROCEDURE VIEW_LANGS RETURNS ( code VARCHAR(5),
Гпава 30. Хранимые процедуры 655 grade SMALLINT, cty VARCHAR(15), lang VARCHAR(15)) AS DECLARE VARIABLE i INTEGER; BEGIN FOR SELECT JOB-CODE, JOB-GRADE, JOB_COUNTRY FROM JOB WHERE LANGUAGE_REQ IS NOT NULL DO BEGIN i = 1; WHILE (i <= 5) DO BEGIN SELECT LANGUAGE—REQ[:i] FROM JOB WHERE ((JOB-CODE = :code) AND (JOB-GRADE = :grade) AND (JOB_COUNTRY - :cty)) INTO :lang; i -i + 1; SUSPEND; END END END A SET TERM ; A Ее вызов: SELECT * FROM VIEW—LANGS; CODE GRADE CTY LANG Eng 3 Japan Japanese Eng 3 Japan Mandarin Eng 3 Japan English Eng 3 Japan Eng 3 Japan Eng 4 England English Eng 4 England German Eng 4 England French Процедура может быть модифицирована таким образом, что будет получать входные аргументы и возвращать другую комбинацию данных в качестве выхода.
656 Часть VII. Программирование на сервере Тестирование процедур Разработчикам не нужно напоминать о необходимости строгого тестирования моду- лей PSQL, прежде чем передавать их в работу, где они в один скверный день могут принести большой вред. Синтаксический анализатор предупредит вас об ошибках кодирования PSQL, однако как программы ваши модули не защищены от логических ошибок, как и любой код приложения, который вы пишете. Например, наша процедура log sales прекрасно работает, пока каждая запись про- даж имеет непустое значение sales rep. При этом данный столбец допускает пустое значение. Процедура генерирует результирующий набор, в котором каждая выходная строка зависит от значений в предшествующих строках. Если мы не учтем возмож- ные эффекты появления значения null в таком ключе, наша процедура выдаст невер- ный результат. Позже в этой главе в разд. "Изменение хранимых процедур" мы доба- вим меры предосторожности при работе, когда могут появиться некоторые про- блемы. Процедуры для совместного использования Хотя возможно написание процедуры выбора, которая будет выполнять операции по изменению данных в процессе конструирования выходного набора, этого делать не рекомендуется. Хранимая процедура выбора разрабатывается для вывода набора данных клиенту в контексте вызвавшей ее транзакции. Пока клиентское приложение не завершит использование этого выходного набора, транзакция остается неподтвер- жденной. Если операции DML включены в код, который генерирует выходной набор, эти запросы DML остаются неподтвержденными, пока транзакция не будет заверше- на клиентом. В частности, данные могут потенциально сохраняться в несогласованном виде, если значения выхода хранимой процедуры выступают в качестве параметров для опера- ций в других транзакциях. Изменение хранимых процедур Firebird 1.0.x предоставляет два способа изменения хранимых процедур с использо- ванием операторов DDL, a Firebird 1.5 добавляет еще и третий. Это: ♦ оператор alter procedure, который изменяет определение существующей храни- мой процедуры, сохраняя ее взаимозависимости с другими объектами; ♦ оператор recreate procedure, который выполняется, даже если указанная процеду- ра не существует. Если она существует, то эта версия удаляется, а затем заново создается. Существующие зависимости не сохраняются; ♦ оператор create or alter procedure (доступен начиная с версии 1.5) предоставля- ет лучший вариант. Если процедура существует, применяются правила alter, за-
Гпава 30. Хранимые процедуры 657 висимости сохраняются. Если не существует, то будет работать точно так же, как И CREATE PROCEDURE. Любая из этих операций вызовет исключение, если любая попытка изменений раз- рушает зависимости. Влияние на приложения Внутренние изменения процедуры прозрачны всем клиентским приложениям, ис- пользующим эту процедуру. Вам не нужно пересоздавать приложения, если измене- ния не коснулись интерфейса между вызывающей программой и процедурой — тип, количество или порядок выходных или выходных аргументов. Синтаксис изменения процедур За исключением ключевого слова, которое вы выбрали для изменения хранимой про- цедуры, синтаксис для всех операторов такой же, как и для create procedure. Как и в любом другом компилируемом или интерпретируемом модуле, не существует спосо- бов непосредственного изменения элементов без полного пересоздания всего модуля. Каждая "переделка", независимо от выбранного ключевого слова для задания опера- ции, имеет дело с созданием новой версии исходного кода и нового двоично- кодированного объекта. Синтаксис: {CREATE | ALTER | RECREATE | CREATE OR ALTER} PROCEDURE имя [ (переменная тип-данных [, переменная тип-данных ...])] [RETURNS (переменная тип-данных [, переменная тип-да иных ...])] AS тело-процедуры; ALTER PROCEDURE В alter procedure имя процедуры должно быть именем существующей процедуры. Это мягкий способ изменения кода процедуры, потому что если у нее есть зависимо- сти, на которые логически не влияют изменения, то они не будут влиять на структур- ную часть. Вообще, не оказывается влияния на зависимости, включающие другие объекты, ко- торые зависят от измененной процедуры. При этом, если изменения хранимой про- цедуры изменяют определения ее входных и выходных аргументов, нужно будет вы- полнить recreate procedure для любой другой процедуры, к которой происходит об- ращение в процессе выполнения. Д ВНИМАНИЕ! Некоторые версии Firebird 1.0.x обнаруживают ошибку, когда зави- симые объекты выдают исключение при перекомпиляции зависимого объекта, даже если изменения не оказывали воздействия на интерфейс между объектами.
658 Часть VII. Программирование на сервере RECREATE PROCEDURE Оператор recreate procedure идентичен оператору create procedure за исключением того, что для существующей процедуры с тем же именем он внутренне выполняет операцию drop procedure перед созданием нового двоичного объекта. Имя процедуры не должно существовать. Вы можете использовать его, как и alter procedure, однако оно не сохраняет сущест- вующие зависимости. Операция будет заблокирована, если существуют зависимые объекты (просмотры или другие процедуры, которые ссылаются на данную про- цедуру). Процедура не обязательно должна существовать, однако будьте внимательны с чув- ствительностью к регистру в именах объектов, если при создании процедуры исполь- зуются идентификаторы, заключенные в апострофы. Предположим, вы создаете сле- дующую процедуру: CREATE PROCEDURE "Try_Me" RETURNS (AWORD VARCHAR(10)) AS BEGIN AWORD = 'turtle'; END Л Теперь вы решаете изменить ее с использованием recreate procedure: RECREATE PROCEDURE Try_Me RETURNS (AWORD VARCHAR(10)) AS BEGIN AWORD = 'Venezuela'; END Л Исходная процедура с именем, чувствительным к регистру тгу_ме, сохраняется неиз- мененной. "Заново созданная" процедура является новой процедурой с именем, не чувствительным к регистру — tryj-ie. CREAТЕ OR ALTER PROCEDURE Новый в версии 1.5 синтаксис создает новую процедуру, если не существует про- цедуры с тем же именем, иначе изменяет ее. Исправление процедуры LOG_SALES Как пример, мы собираемся исправить процедуру log_sales, которая обещает доста- вить нам неприятности, потому что мы не обратили внимание на пустые значения ключей. Вот блок, который может решить проблемы: CREATE PROCEDURE LOG_SALES ( . . . DO BEGIN
Гпава 30. Хранимые процедуры 659 IF(REP = LASTREP) THEN /* будет иметь значение false, если оба значения null */ BEGIN REPTOTAL = REPTOTAL + ORDTOTAL; REP_NAME = ; END ELSE BEGIN REPTOTAL = ORDTOTAL; LASTREP = REP; SELECT FULL_NAME FROM EMPLOYEE WHERE EMP_NO = :REP INTO :REP__NAME; /* вернет null, если переменная REP имеет значение null */ END END Мы исправили логику для обработки пустых значений (сгруппированных вместе в конце курсора, потому что набор упорядочен по этому столбцу) и используем опера- торы create или alter для изменения кода: CREATE OR ALTER PROCEDURE LOG_SALES (... DO BEGIN /* v IF((REP - LASTREP) OR (LASTREP IS NULL)) THEN /* ************* BEGIN REPTOTAL = REPTOTAL + ORDTOTAL; REP_NAME = ; END ELSE BEGIN REPTOTAL = ORDTOTAL; LASTREP = REP; /* ************* */ IF (REP IS NOT NULL) THEN SELECT FULL_NAME FROM EMPLOYEE WHERE EMP_NO = :REP INTO :REP_NAME; ELSE REP_NAME = 'Unassigned'; ************* *! END END л COMMIT
660 Часть VII. Программирование на сервере Ошибка "Объект находится в использовании" Подтверждение изменения вызовет пресловутую ошибку (обсуждавшуюся в преды- дущей главе), если какой-нибудь пользователь в настоящий момент использует эту процедуру или любой другой объект, зависящий от нее. Даже если мы уберем это препятствие, новая версия процедуры не станет немедленно доступной в Суперсер- вере, если старая версия все еще находится в кэше. Все пользователи должны отклю- читься от базы данных, а когда они снова к ней подключатся, они смогут увидеть новую версию. В Классическом сервере новая версия будет доступна следующему клиенту, который соединится с базой данных. Удаление хранимых процедур Оператор drop procedure удаляет существующую хранимую процедуру из базы дан- ных. Вы можете использовать этот оператор везде, где можно использовать операто- ры DDL. < ПРИМЕЧАНИЕ. Операторы DDL не могут выполняться как операторы PSQL. При этом в Firebird 1.5 оператор DDL может передаваться через конструкцию execute \___Y statement. Нужно ли читающему пользователю быть осторожным в отношении использования execute statement при удалении самой процедуры? Синтаксис: DROP PROCEDURE имя; имя процедуры должно быть именем существующей процедуры. Будьте внимательны в отношении чувствительности к регистру имен объектов, если при создании про- цедуры были использованы идентификаторы в апострофах. Следующий оператор удаляет процедуру log_sales: DROP PROCEDURE LOG_SALES; Ограничения При удалении процедуры существуют ограничения. ♦ Только пользователь SYSDBA и владелец процедуры могут ее удалять. ♦ Процедура, находящаяся в использовании в любой другой транзакции, не может быть удалена. Это является особой проблемой в системах, где процедуры вызываются в транзакциях, которые подтверждаются с использованием CommitRetaining. ♦ Если другие объекты ссылаются или вызывают данную процедуру, то необходимо сначала изменить зависимые объекты, удалив такие ссылки и подтвердив работу, прежде чем удалять процедуру.
Гпава 30. Хранимые процедуры 661 ♦ Для удаления рекурсивной процедуры необходимо сначала удалить рекурсивные вызовы и подтвердить изменения. Похожие трудности существуют и для про- цедуры, вызывающей другую процедуру, которая в свою очередь вызывает про- цедуру, которую вы собираетесь удалить. Все подобные зависимости должны быть удалены и подтверждены, чтобы процедура стала доступной для удаления. Тема оптимизации: использование внутренних возможностей Firebird наследует недокументированную возможность, которая может ускорить вы- полнение запроса при некоторых условиях. Это rdbsdb key (обычно называется про- сто db key), внутренний ключ, поддерживаемый сервером базы данных для внутрен- него использования при оптимизации запросов и управлении версиями записей. Внутри контекста транзакции, где он используется, он представляет позицию строки в таблице4. Относительно RDB$DB__KEY Первый урок заключается в том, что rdb$db_key является прямым указателем, свя- занным с базой данных, а не с физическим адресом на диске. Второй — значения rdbsdb key не следуют в предсказуемой последовательности. Не используйте вычис- ления, включающие их относительные позиции! Третий урок в том, что они измен- чивы — они изменяются после резервного копирования и последующего восстанов- ления, а иногда и после подтверждения транзакции. Главным является понимание мимолетности db key и отсутствие предположений о его существовании в то время, когда ссылающаяся на него операция завершается или отменяется. Размер RDB$DB_KEY Для таблиц rdbSdb key использует 8 байт. Для просмотров он использует коэффици- ент умножения этих 8 байт, сколько таблиц используется в просмотре. Например, если просмотр соединяет три таблицы, его rdb$db_key использует 24 байт. Это важно, когда вы работаете с хранимыми процедурами и собираетесь сохранять rdb$db_key в переменных. Вы должны использовать тип данных снан(п) корректной длины. По умолчанию db key возвращается в виде шестнадцатеричного числа— две шест- надцатеричные цифры представляют каждый байт: 16 шестнадцатеричных цифр воз- вращаются для 8 байт. Сделайте для одной из ваших таблиц в isql следующее: 4 Клавдио Балдеррама (Claudio Valderrama) провел исследования по rdb$db__key, именно его примеры здесь используются. Он живет в Чили, его псевдоним "robocop". Клавдио являет- ся официальным инспектором кода в проекте Firebird. Он поддерживает обширный сборник статей и кодов для Firebird и InterBase на своем сайте: http://www.cvalde.net.
662 Часть VII. Программирование на сервере SQL> SELECT RDB$DB_KEY FROM MYTABLE; RDB$DB_KEY 000000B600000002 000000B600000004 000000B600000006 000000B600000008 000000B60000000A Преимущества Поскольку rdb$db_key напрямую указывает на место хранения записи, он будет быст- рее для поиска, чем первичный ключ. Если по каким-то причинам в таблице нет пер- вичного ключа или активного уникального индекса, или уникальный индекс допус- кает пустые значения, то возможно существование полных дубликатов строк. В этих условиях rdb$db_key является единственным способом точной идентификации каж- дой строки. Некоторые виды операций выполняются быстрее в хранимой процедуре при исполь- зовании rdb$db_key— обычно в случаях изменения и удаления при сложных услови- ях. Для добавлений (даже при огромных пакетах) rdb$db_key недоступен, потому что не существует способа определить заранее, какими будут значения. Однако, если отыскиваемые страницы базы данных для изменения или удаления уже находятся в главной памяти, разница в скорости доступа скорее всего будет незначи- тельной. То же самое верно, если отыскиваемый набор достаточно мал, а все отыски- ваемые строки расположены близко друг к другу. Оптимизация запросов Проблемы с производительностью, скорее всего, возникнут, если вы попытаетесь запустить изменения DSQL, похожие на следующий пример, для большой таблицы: UPDATE TABLEA А SET A.TOTAL = (SELECT SUM (В.VALUEFIELD) FROM TABLEB В WHERE B.FK = A.PK) WHERE <условия...> Если вы часто выполняете ту же операцию, и она использует много строк, то стоит попытаться написать хранимую процедуру, которая получит соответствующий итог для каждой строки без необходимости выполнять подзапрос: CREATE PROCEDURE ... AS BEGIN FOR SELECT B.FK, SUM(B.VALUEFIELD) FRCM TABLEB В GROUP BY B.FK
Гпава 30. Хранимые процедуры 663 INTO :B__FK, : TOTAL DO UPDATE TABLEA A SET A.TOTAL = :TOTAL WHERE A.PK = :B_FK AND ... END Хотя это и быстрее, тем не менее остается проблема, что записи в а выбираются по первичному ключу каждый раз, когда выполняется проход по циклу for ... do. Некоторые люди получают лучший результат при этом необычном синтаксисе: DECLARE VARIABLE DBK CHAR (8) ; /* 8 символов для db_key таблицы А */ FOR SELECT B.FK, SUM(В.VALUEFIELD) , A. RDB$DB_KEY FROM TABLEB В JOIN TABLEA A ON A.PK = B.FK WHERE <условия> GROUP BY B.FK, A.RDB$DB_KEY INTO :B_FK, :TOTAL, :DBK DO UPDATE TABLEA SET A.TOTAL = :TOTAL WHERE A.RDB$DB_KEY = : DBK; < ПРИМЕЧАНИЕ. В Firebird нет необходимости в ключевом столбце для создания соединения, однако он нужен в списке select, чтобы предложение group by было X—допустимым. Перечислим преимущества такого подхода. ♦ Фильтрация общих записей для айв будет эффективной, когда оптимизатор мо- жет создать хороший фильтр для явного соединения. ♦ Если соединение может применить свое собственное предложение поиска, то су- ществует выгода от выполнения фильтрации до того, как изменение будет прове- рять свое собственное условие. ♦ Строки таблицы правой стороны (а), локализованные с помощью прямых указате- лей ць_кеу, выделяются во время соединения быстрее, чем при просмотре первич- ного ключа или его индекса. Добавления Поскольку добавление не включает поиск, наиболее простые операции добавле- ния — например, чтение константных значений из импортируемого набора во внеш- ней таблице — не требуют локализации ключей. Однако не все входные значения (values) оператора insert получаются так просто. Это может быть очень сложным набором значений, полученным из выражений, со-
664 Часть VII. Программирование на сервере единений или агрегатных операций. В хранимой процедуре операция insert может разветвляться в предложении else предиката if (exists (...)), например: IF EXISTS(SELECT...) THEN ELSE BEGIN INSERT INTO TABLEA SELECT C.PKEY, SUM(B.AVALUE), AVG(B.BVALUE), COUNT(DISTINCT C.XYZ) FROM TABLEB В JOIN TABLEC C ON B.X = C.Y WHERE C.Z = 'value' AND C.PKEY NOT IN(SELECT PKEY FROM TABLEA) GROUP BY C.PKEY; END Реализация этого в хранимой процедуре: FOR SELECT C.PKEY, SUM(В.AVALUE), AVG(В.BVALUE), COUNT(DISTINCT C.XYZ) FROM TABLEB В JOIN TABLEC C ON B.X = C.Y WHERE C.Z = 'value' AND C.PKEY NOT IN(SELECT PKEY FROM TABLEA) GROUP BY C.PKEY INTO :C_KEY, :TOTAL, :B_AVG, :C_COUNT DO BEGIN SELECT A.RDB$DBKEY FROM TABLEA A WHERE A.PKEY = :C_KEY INTO :DBK; IF (DBK IS NULL) THEN /* строка не существует */ INSERT INTO TABLEA(PKEY, TOTAL, AVERAGE_B, COUNT_C) ' VALUES(:C_KEY, :TOTAL, :B_AVG, :C_COUNT); ELSE UPDATE TABLEA SET TOTAL = TOTAL + :TOTAL, AVERAGE_B = AVERAGE_B + :B_AVG, COUNT_C = COUNT_C + :C~COUNT WHERE A.RDB$DB_KEY = :DBK; END
Гпава 30. Хранимые процедуры 665 Длительность действия По умолчанию областью действия db key является текущая транзакция. Вы можете считать, что он остается правильным во время действия текущей транзакции. Под- тверждение или откат транзакции приведет к тому, что значения rdb$db_key станут непредсказуемыми. Если вы используете commitRetaining, контекст транзакции со- храняется, блокируя сборку мусора и, следовательно, предотвращая "переназначе- ние" старого db_key. При этих условиях значения rdb$db_key для любых используе- мых строк в вашей транзакции сохраняются действительными, пока не произойдет "жесткое" подтверждение или откат. После жесткого подтверждения или отката другая транзакция может удалить строку, которая была изолирована внутри контекста вашей транзакции и, следовательно, рас- сматривалась как "существующая" в вашем приложении. Любое значение rdb$db_key теперь может указывать на несуществующую строку. Если у вас достаточно большой интервал между моментом, когда начинается ваша транзакция и когда завершается ваша работа, вы должны проверять, не была ли за это время строка изменена или за- блокирована другой транзакцией. Некоторые интерфейсы приложений, например IB Objects, являются суперинтеллек- туальными в плане добавлений и могут подготовить "сегмент" для вновь добавлен- ных строк в клиентских буферах для быстрого обновления списка после подтвержде- ния. Такие возможности важны для производительности при работе в сети. Однако "интеллектуальность", подобная этой, основывается на точных реальных ключах. Поскольку db_key является просто заменителем ключа для набора, наследуемого от предыдущих подтвержденных данных, он не имеет смысла для новой строки — он не доступен при изменениях в клиентском буфере. Изменение величины длительности действия Значение длительности действия по умолчанию для rdb$db_key можно изменить во время соединения с базой данных, используя параметр API isc_dpt>__dbkey__scope. Не- которые разработки — например, компоненты IB Objects в инструментах окружения Borland Object Pascal — предоставляют его в классе соединения. Однако не рекомен- дуется расширять область действия db_key в высоко интерактивной среде, поскольку это остановит сборку мусора, приводя к нежелательному росту размера файла базы данных и замедлению работы системы вплоть до ее зависания или краха. Не исполь- зуйте соединения, имеющие область действия для db_key, отличающуюся от значения по умолчанию. RDB$DB_KEYв многотабличных наборах Все таблицы поддерживают свои собственные 8-байтовые столбцы rdb$db_key. Про- смотры и соединения во время выполнения генерируют ць_кеу путем конкатенации rdb$db_key из строк исходных таблиц. Если вы используете RDB$DB_KEY в многотаб- личных наборах, будьте особенно внимательны при задании каждого из них.
666 Часть VII. Программирование на сервере rdb$db_key не может быть использован между различными таблицами. Не существует возможности установить отношения зависимости между rdb$db_key двух таблиц, за исключением реентерабельных (ссылающихся на себя) соединений. Пора дальше Многие из техник, описанных в данной главе, применимы к любым модулям PSQL, которые вы создаете. Далее мы сфокусируем наше внимание на техниках и возмож- ностях языка PSQL, которые вы сможете использовать при написании триггеров, ав- томатически реагирующих на изменение состояния данных в строке или на добавле- ние новой строки.
ГЛАВА 31 Триггеры Триггеры — ключевые элементы среди возможностей, предоставляемых Firebird для централизованной реализации бизнес-правил внутри системы управления базой дан- ных. Триггер является автономным модулем, который выполняется автоматически, когда выполняется запрос, который будет изменять состояние данных в таблице. Для написания кодов триггеров используются техники PSQL и хранимых процедур. При этом триггеры не могут вызываться из приложений или других процедур. Соот- ветственно, они не могут получать входные и возвращать выходные аргументы, как это возможно в процедурах. В дополнение к PSQL они включают некоторые контек- стные расширения языка, применимые только в модулях триггеров. Все триггеры в Firebird выполняются на уровне строки каждый раз, когда изменяется образ строки. Firebird поддерживает высокий уровень детализации при определении времени, последовательности и условий, при которых будет выполняться конкрет- ный модуль триггера. Множество модулей может быть определено для каждой фазы и события. Триггеры являются частью работы транзакции, в которой событие DML изменяет состояние строки. Если транзакция успешно подтверждается, все действия триггеров будут "приняты". Если будет выполнен откат транзакции, все действия триггера бу- дут отменены. Фаза, событие и последовательность Триггер может выполняться в одной из двух фаз, связанных с запрошенными изме- нениями состояния данных: до (before) записи или после (after) нее. Он может приме- няться к одному из трех событий DML: добавление, изменение или удаление. Начи- ная с Firebird 1.5 возможно объединение действий триггера для двух или трех собы- тий DML в одном модуле триггера до или после. Фаза и событие В табл. 31.1 представлены восемь типов модулей триггеров.
668 Часть VII. Программирование на сервере Таблица 31.1. Комбинации фаза/событие для модулей триггеров Вид триггера Описание Версия BEFORE INSERT Вызывается до создания новой строки. Позволяет изменять входные значения Все AFTER INSERT Вызывается после создания новой строки. Не по- зволяет изменять входные значения. Обычно ис- пользуется для модификации других таблиц Все BEFORE UPDATE Вызывается до создания новой версии записи. Позволяет изменять входные значения Все AFTER UPDATE Вызывается после создания новой версии записи. Не позволяет изменять входные значения. Обычно используется для изменения других таблиц Все BEFORE DELETE Вызывается до удаления существующей строки. Не принимает изменений никаких столбцов в строке Все AFTER DELETE Вызывается после удаления строки. Не принимает изменений никаких столбцов в строке. Обычно используется для модификации других таблиц Все BEFORE <Событие> OR <событие> [OR <событие>] Вызывается до выполнения изменения любого требуемого состояния данных. Действия для собы- тия DML должны быть закодированы условно. Дей- ствие "Удаление" не может изменять никакие столбцы в строке 1.5+ AFTER <событие> OR <событие> [OR <событие>] Вызывается после выполнения изменения любого требуемого состояния данных. Действия для собы- тия DML должны быть закодированы условно. Дей- ствия не могут изменять никакие столбцы в строке. Обычно используется для модификации других таблиц 1.5+ Последовательность Для любой комбинации фаза/событие Firebird позволяет использовать множество триггеров. Вероятно, существует какое-то практическое ограничение, однако можно с уверенностью сказать, что вы можете создавать столько триггеров, сколько вам нужно с использованием целых чисел от 0 до 32 767. Последовательный номер по умолчанию (position) ноль. Хорошей практикой является задание для триггера по- рядка выполнения, однако явное указание последовательности не является обяза- тельным. Если присутствуют последовательные номера, триггеры будут выполняться в возрастающем порядке. Числа не должны быть уникальными, последовательность может иметь разрывы. Набор триггеров для фазы/события со значением по умолчанию position о будет вы- полняться в алфавитном порядке их имен. То же самое можно ожидать, если вы имеете группу триггеров, имеющих один и тот же не нулевой последовательный номер. Следующий пример демонстрирует, как будут выполняться четыре триггера измене- ния (update) для таблицы account:
Глава 31. Триггеры 669 CREATE TRIGGER BU_ACC0UNT5 FOR ACCOUNT ACTIVE BEFORE UPDATE POSITION 5 AS ... CREATE TRIGGER BU^ACCOUNTO FOR ACCOUNT ACTIVE BEFORE UPDATE POSITION 0 AS ... CREATE TRIGGER AU_ACCOUNT5 FOR ACCOUNT ACTIVE AFTER UPDATE POSITION 5 AS ... CREATE TRIGGER AU_ACCOUNT3 FOR ACCOUNT ACTIVE AFTER UPDATE POSITION 3 AS ... Кто-то изменяет некоторые строки в таблице account: UPDATE ACCOUNT SET С ='CANCELED' WHERE C2 = 5; Вот последовательность событий для каждой изменяемой строки: 1. Выполняется триггер bu_accounto. 2. Выполняется триггер bu_accounts. 3. Новая версия записи записывается на диск. 4. Выполняется триггер au_account3. 5. Выполняется триггер au_accounts. Состояние Триггер может быть активным (active) или неактивным (inactive). Запускаются толь- ко активные триггеры. См. замечания к alter trigger по поводу подробностей деак- тивации триггера. Создание триггеров Триггер определяется с помощью оператора create trigger, который состоит из заго- ловка и тела. Заголовок триггера отличается от заголовка хранимой процедуры, он содержит: ♦ имя триггера, которое должно быть уникальным в базе данных; ♦ имя таблицы, идентифицирующее таблицу, с которой ассоциируется триггер; ♦ атрибуты, которые определяют состояние, фазу, событие DML и, необязательно, последовательность. Тело триггера, как и тело хранимой процедуры, содержит: ♦ необязательный список локальных переменных и их типов данных; ♦ блок операторов на языке процедур и триггеров Firebird, заключенный в опера- торные скобки begin и end. Эти операторы выполняются, когда запускается триг- гер. Сам блок может включать другие блоки, так что может существовать много уровней вложенности.
670 Часть VII. Программирование на сервере Синтаксис Для всех версий Firebird синтаксис create trigger одинаков: CREATE TRIGGER имя FOR {таблица J просмотр} [ACTIVE ( INACTIVE] {BEFORE i AFTER} {DELETE | INSERT ( UPDATE} [POSITION число] AS <тело-триггера> А < тело - триггера > = [ < список-объявления-переменных> ] < блок> <список-объявления~переменных> - DECLARE VARIABLE переменная тип-данных; [DECLARE [VARIABLE] переменная тип-данных; ...] <блок> = BEGIN <составной-оператор> [<составной-оператор> ...] END <составной-оператор> = <блок> | оператор; В версии 1.5 возможно слияние всех событий в один триггер фазы: CREATE TRIGGER имя FOR {таблица | просмотр} [ACTIVE | INACTIVE] {BEFORE | AFTER] {DELETE OR {[INSERT [OR UPDATE]] | {INSERT OR [. . ]} I {UPDATE OR [..])) [POSITION число] AS <тело-триггера> z Элементы заголовка Все, предшествующее предложению as, является заголовком триггера. Заголовок должен задавать уникальное имя триггера и имя существующей подтвержденной таблицы или просмотра, кому принадлежит триггер. Именование триггеров Синтаксис требует, чтобы имя триггера было уникальным среди всех имен триггеров в базе данных. Хорошей практикой является применять некоторые соглашения для именования триггеров, которые имеют смысл для вас и очевидны для всех других, кто будет работать с вашей базой данных. Автор использует "формулу" для иденти- фикации фазы и события (bi | ai | ви | au | bd | ad | ва । аа—два последних представляют "до всех", Before All и "после всех", After АП), имя таблицы и номер в последовательности, если нужно1. Например, триггер до добавления для таблицы customer может быть назван bi_customeri. CREATE TRIGGER BI_CUSTOMER1 FOR CUSTOMER... 1 Хотя триггеры "привязаны" к таблицам, некоторые инструменты выводят общий список триггеров для просмотра, сортируя его по именам триггеров. В этом случае следует выбрать иной способ формирования имени триггера (из имени таблицы, его типа и других характери- стик), чтобы представление триггеров было отсортировано в нужном вам порядке. — Прим.
Глава 31. Триггеры 671 Атрибуты триггера Перечислим остальные атрибуты заголовка триггера. ♦ Состояние триггера, active или inactive, определяющее, будет ли триггер запус- каться после его создания. Значение по умолчанию active. Деактивация триггера полезна при разработке и отладке. ♦ Индикатор фазы, before или after, определяющий момент, когда триггер будет выполняться при событии DML. ♦ Индикатор события DML определяет тип операции SQL, при которой будет вы- полняться триггер: insert, update или delete. ♦ В Firebird 1.0.x может быть указан индикатор ровно одного события. Начиная с версии 1.5, необязательное расширение <событие> or <событие> ... позволяет задавать два или три события в одном модуле. Например, ... before insert or update or delete ... позволяет вам задать действия для всех трех событий. Логи- ческие контекстные переменные inserting, updating или deleting поддерживают логику переходов. ♦ Необязательный индикатор последовательности, position число, определяет мо- мент запуска триггера по отношению к другим триггерным модулям для той же фазы и события. Тело триггера Во всех кодах модулей Firebird тело состоит из необязательного объявления списка локальных переменных, за которым следует блок операторов. Программирование тела триггера в точности такое же, как и программирование тела процедуры (см. гла- ву 30). Интерес для нас в этой главе представляют некоторые специальные расшире- ния PSQL, осуществляющие поддержку контекста триггера, и некоторые особые ро- ли триггеров по реализации и поддержке бизнес-правил. Триггеры могут вызывать хранимые процедуры. Правила вызова для триггеров в точности такие же, что и для хранимых процедур. Техники обработки исключений обсуждаются в главе 32. Триггеры могут использовать курсоры, выполнять операции с другими таблицами и отправлять события. Они могут вызывать и обрабатывать исключения, включая те, которые возникли во вложенных процедурах. Триггеры никогда не вызываются процедурами, другими триггерами или приложе- ниями. Они совсем не поддерживают входные и выходные аргументы. Особенности PSQL для триггеров Два особых элемента PSQL доступны триггерам: логические контекстные перемен- ные событий inserting, updating и deleting и контекстные переменные NEW И OLD.
672 Часть VII. Программирование на сервере Переменные события В Firebird появляются логические контекстные переменные inserting, updating и deleting, чтобы поддерживать условные переходы для триггеров, используемых для нескольких событий. Возможным синтаксисом ветвления может быть: IF ({INSERTING I UPDATING | DELETING} OR {UPDATING | DELETING | INSERTING} [OR (DELETING I INSERTING ! UPDATING}]) THEN ... Работа этих полезных предикатов иллюстрируется в дальнейших примерах этой главы. Переменные NEWia OLD Контекстные переменные new и old являются расширениями PSQL, специфичными для триггеров2; они позволяют ссылаться на существующие ("старые”) и требуемые ("новые") значения каждого столбца. Переменные new.* имеют значения в событиях insert и update; переменные old.* имеют значения в событиях update и delete, new.* в событиях удаления и old.* в событиях добавления имеют значение null. Примени- мые значения new и old доступны для всех столбцов таблицы или просмотра, даже если сами столбцы не указаны в операторе DML. Значения old.* (если доступны) могут использоваться в триггерах как переменные, но изменение значения не влияет на сохраненное старое значение3. Значения new.* (если доступны) могут использоваться для чтения/записи в фазе before и только для чтения в фазе after. Если вы хотите манипулировать ими как переменными значе- ниями в триггере after, присвойте их значения локальным переменным и обращай- тесь к этим локальным переменным. Использование NEW и OLD Для использования мощи триггеров Firebird в разработке баз данных при отслежива- нии целостности данных, независимо от людей и внешних программ, переменные new и old являются основным инструментом. Они могут быть использованы для: ♦ получения допустимых значений по умолчанию в некоторых условиях; ♦ проверки и при необходимости преобразования входных данных пользователя; 2 new.* и old.* также допустимы в DDL для ограничений check на уровне таблицы — например, create table blah (id integer, data integer, constraint check_incr CHECK(NEW.DATA > OLD.DATA)). 3 В Firebird 2.0 модификация значений old.* явно запрещена и будет вызывать ошибку при попытке присвоения значений переменным old.* как при выполнении, так и при созда- нии триггера. Также запрещена модификация значений new.* в триггерах after.— Прим, науч. ред.
Глава 31. Триггеры 673 ♦ получения ключей и значений для выполнения автоматических обновлений в дру- гих таблицах; ♦ реализации автоинкрементных ключей средствами генераторов. Новые значения для строки могут быть изменены только в действиях before. Если триггер, запускаемый в фазе after, попытается присвоить значение столбцу new, это не даст никакого результата. Все значения new можно перезаписывать в фазе before, они немедленно принимают новые назначенные им значения. Новая версия записи получит переназначенные зна- чения, только когда все триггеры before будут завершены. С этого момента значения new становятся значениями только для чтения. Следовательно, если у вас несколько триггеров изменяют одни и те же значения new, важно, чтобы все они имели различ- ные номера position, правильно упорядоченные. Реализация автоинкрементных ключей Рекомендованное использование в Firebird триггеров before insert — реализация в стиле ^identity автоинкрементных первичных ключей. Эта техника проста, и боль- шинство разработчиков Firebird могут написать такие триггеры во сне. Она включает два шага: I. Создание генератора для генерации уникальных чисел ключа. 2. Написание триггера before insert для таблицы. Для иллюстрации этой техники мы реализуем автоинкрементный первичный ключ для таблицы customer, у которой первичный ключ customer_id— столбец целого типа bigint (версия 1.5) или numeric(18,0) (версия 1.0.x). В диалекте 1 базы данных CUSTOMER_ID должен иметь ТИП INTEGER. Во-первых, создадим генератор: CREATE GENERATOR GEN_PK_CUSTOMER; Затем создадим триггер: CREATE TRIGGER BI_CUSTOMER FOR CUSTOMER ACTIVE BEFORE INSERT POSITION 0 AS BEGIN IF (NEW.CUSTOMER_ID IS NULL) THEN NEW. CUSTOMER_ID = GEN__ID (GEN_PK_CUSTOMER, 1) ; END л COMMIT л Когда выполняется добавление, customer id сознательно не указывается во входном списке оператора insert: insert into customer ( LAST_NAME, FIRST_NAME, ...) VALUES (?, ?, ...); 22 Зак. 420
674 Часть VII. Программирование на сервере Без триггера этот оператор вызовет исключение, потому что первичный ключ не мо- жет иметь пустого значения. Однако триггер before insert выполняется до проверки этого ограничения, он контролирует, что customer id имеет пустое значение, и вы- полняет свое действие. Зачем проверять NEW.значение на NULL Если триггер может делать это для меня, то вы можете спросить, зачем нужно вы- полнять проверку на null? Для приложения может быть полезным знать, какое будет значение первичного клю- ча новой строки без необходимости ожидать завершения транзакции. Например, это общее требование при создании "главной" записи и связи с ней "подчиненных" запи- сей обычно с помощью внешнего ключа в одной транзакции. Довольно неуклюже — а иногда и рискованно — нарушать атомарность задачи создания главная- подчиненная, подтверждая создание главной для получения необходимого вам зна- чения внешнего ключа для подчиненных записей, полагаясь только на триггер. Приложения, написанные для Firebird, имеют преимущества, благодаря одной особой характеристике генераторов: они не зависят от пользовательских транзакций. Одна- жды сгенерированное значение не может быть выдано ни одной другой транзакции и не может быть отменено. Быстрый запрос в его собственной транзакции4 возвращает значение SELECT GEN_ID (GEN_PK_CUSTOMER, 1) AS RESULT FROM RDB$DATABASE; Если в вашем триггере опущена проверка на пустое значение и просто выполняется: AS NEW.CUSTOMER_ID = GEN_ID (GEN_PK_CUSTOMER, 1); END A то значение, полученное приложением, будет перекрыто вторым "дерганьем" генера- тора, что нарушит связь с подчиненными записями. Эта ситуация не является аргументом в пользу генерации ключей только в триггерах. Наоборот, триггер с проверкой на null обеспечивает реализацию бизнес-правил при любых условиях. Д ВНИМАНИЕ! В разработках, где нет хорошей интеграции работы приложений различных разработчиков, или где пользователи имеют свободный доступ к базе данных при использовании инструментов запросов, может оказаться необходи- мым включение в ваши триггеры более высокого уровня защиты целостности ключей, такого как контроль принадлежности диапазону или другая подходящая форма проверки. 4 Некоторые компоненты интерфейса предоставляют методы или свойства для автоматиче- ского выполнения этой маленькой задачи.
Глава 31. Триггеры 675 Преобразования Переменная new может быть использована для преобразования значения в нечто дру- гое. Общий трюк заключается в использовании триггера (или пары триггеров в вер- сии 1.0.x) для поддержания "заменителя" столбца для выполнения нечувствительных к регистру поисков по другому столбцу, который может содержать смешанные зна- чения регистра. Триггер читает значение new столбца со смешанным регистром, кон- вертирует его в верхний регистр и записывает в значение new столбца поиска. Такой столбец-"заменитель" должен иметь ограничение not null для гарантии того, что в нем всегда будет значение для поиска: CREATE TABLE MEMBER ( MEMBER_ID INTEGER NOT NULL PRIMARY KEY, LAST_NAME VARCHAR (40) NOT NULL, FI RST_NAME VARCHAR (35), PROXY-LAST_NAME VARCHAR (40), MEMBER_TYPE CHAR(3) NOT NULL, MEMBERSHIP_NUM VARCHAR(13) , ....); COMMIT; /* */ SET TERM л; CREATE TRIGGER BA_MEMBER1 FOR MEMBER ACTIVE BEFORE INSERT OR UPDATE POSITION 0 AS BEGIN NEW. PROXY_LAST_NAME = UPPER (NEW. LAST—NAME) ; END л Возможны любые виды преобразований. Предположим, мы хотим получить количе- ство элементов (membershiP-Num), собранное из member_type, за которым следует строка из десяти цифр, заполненная слева нулями и полученная из сгенерированного пер- вичного ключа таблицы member. С помощью автоматической генерации в триггере before insert мы можем это осуществить5: CREATE TRIGGER BI-MEMBER2 FOR MEMBER ACTIVE BEFORE INSERT POSITION 2 AS DECLARE VARIABLE ID_AS-STRING VARCHAR(10); 5 Такая манипуляция будет гораздо более простой, если использовать внешнюю функцию для вычисления длины преобразованного целого числа. Я выбираю — может быть немного с другой точки зрения — демонстрацию того, что можно выполнить манипуляцию строками без использования внешних функций.
676 Часть VII. Программирование на сервере BEGIN ID_AS__STRING = CAST(NEW.ID AS VARCHAR(10) ) ; WHILE (NOT (ID_AS_STRING LIKE '%')) /* 10-символьная маска */ DO ID_AS_STRING = 'O' H ID_AS_STRING; NEW.MEMBERSHIP_NUM = NEW.MEMBER_TYPE || ID_AS_STRING; END л Проверка и значения по умолчанию Триггеры могут улучшить стандартные ограничения SQL, когда они используются для проверки входных данных и применения значений по умолчанию. Проверка SQL предоставляет ограничения check для гарантии того, что будут сохраняться только "хорошие" данные. Например, значения столбцов, создаваемых на основе следующего домена, ограничены символами в верхнем регистре и цифрами: CREATE DOMAIN TYPECODE CHAR(3) CHECK (VALUE IS NULL OR VALUE = UPPER(VALUE)); Это замечательно — и мы хотим усилить это правило. Само по себе такое ограниче- ние вызовет исключение, если любое клиентское приложение попытается передать символы в нижнем регистре. С помощью триггера мы можем полностью убрать ис- ключение, исправляя попытки нарушений на месте: CREATE TRIGGER BA_ATABLE FOR ATABLE ACTIVE BEFORE INSERT OR UPDATE AS BEGIN NEW.ATYPECODE = UPPER(NEW.ATYPECODE); END A ЛГ ПРИМЕЧАНИЕ. В настоящий момент Firebird поддерживает триггеры только для V—/А таблиц и просмотров. Невозможно создать триггер для домена, однако это было \ X бы элегантным улучшением. Значения по умолчанию В определениях доменов и столбцов вы можете указать значение по умолчанию: default. Кажется хорошей идеей устанавливать значение столбца, не допускающего значение null в некоторое значение по умолчанию, однако SQL-атрибут default на деле оказывается беззубым зверем. Он работает только при двух условиях: ♦ при использовании операции insert; ♦ если этот столбец не включен во входной список оператора. Поскольку многие современные интерфейсы приложений автоматически создают оператор insert, используя выходные столбцы оператора select в качестве входного
Глава 31. Триггеры 677 списка, то все столбцы помещаются в этот список. Если само приложение не предос- тавляет значение по умолчанию, то обычным результатом является передача значе- ния null. Когда сервер получает значение null для такого столбца, он сохраняет в базе данных null. Другие ограничения столбца могут вклиниться сюда и вызвать ис- ключение — особенно ограничение not null — однако значение по умолчанию для столбца никогда не перекроет и не скорректирует любое значение, полученное от клиентского интерфейса. Вторая проблема связана, конечно, с тем, что значения по умолчанию никогда не применяются, если используется операция изменения. Короче говоря, триггеры выполняют гораздо более эффективную работу по поддер- жанию значений по умолчанию, чем это делают атрибуты значения по умолчанию для столбца. Возьмем для примера столбец, основанный на следующем домене: CREATE DOMAIN MONEY NUMERIC (18,0) NOT NULL DEFAULT 0.00; Триггер before insert or update для любого столбца, использующего домен MONEY, реализует значение по умолчанию: CREATE TRIGGER BI__ACCOUNT FOR ACCOUNT ACTIVE BEFORE INSERT OR UPDATE AS BEGIN IF (NEW.BALANCE IS NULL) THEN NEW.BALANCE = 0.00; END л СОВЕТ. Вы можете обеспечить поддержание всех значений по умолчанию для таблицы в едином триггерном модуле (версия 1.5 и выше) или в двух параллель- ных модулях: один для before insert, а другой для before update (версия 1.0.x). Автоматическое заполнение Триггеры полезны для "автоматического заполнения" контекстной информацией столбцов, созданных для подобных целей. Firebird предоставляет множество контек- стных переменных, которые вы можете использовать в операциях такого рода. Вы можете также использовать ваши собственные "флаги", которые вы вычисляете или просто поставляете в виде констант в процессе выполнения триггера. В следующем примере мы используем триггер after для множества событий с целью автоматического заполнения имени пользователя, даты, времени и идентификатора транзакции для помещения в файл протокола вместе с некоторой информацией о со- бытии. Поскольку регистрируется процесс, то, скорее всего, мы захотим выполнить это в самом конце; присвоим триггеру высокий последовательный номер: CREATE TRIGGER AA__MEMBER FOR MEMBER ACTIVE AFTER INSERT OR UPDATE OR DELETE POSITION 99
678 Часть VII. Программирование на сервере AS DECLARE VARIABLE MEM_ID INTEGER; DECLARE VARIABLE DML_EVENT CHAR(4); BEGIN IF (DELETING) THEN BEGIN MEM—ID = OLD.MEMBER-ID ; DML_EVENT = 'DEL 1 ; END ELSE BEGIN MEM_ID = NEW. MEMBER-ID; . IF (UPDATING) THEN DML_EVENT = 'EDIT1; ELSE DML—EVENT = ' NEW ' ; END INSERT INTO PROCESS_LOG ( TRANSJED, USER_JD, MEMBER_ID, DML_EVENT, TIME_JTAMP) VALUES ( CURRENT-TRANSACTION, CURRENT—USER, :MEM-ID, :DML—EVENT, CURRENT-TIMESTAMP); END ' Конечно, вы также можете заполнять ваши новые или редактируемые строки непо- средственно в триггере before. Изменение других таблиц В предыдущем примере мы увидели, как триггеры after могут выполнять изменения в других таблицах для автоматизации управления такими задачами, как ведение про- токола. Возможности расширить область действия события DML за пределы непо- средственного контекста таблицы и строки, "владеющей" данными, имеют некоторые приложения для управления сложными отношениями. Поддержание обязательного отношения Обязательное отношение существует, когда две таблицы связаны через зависимость внешнего ключа и должна существовать по меньшей мере одна строка для каждой
Глава 31. Триггеры 679 первичной строки. Поскольку SQL не предоставляет ограничения "обязательности", нужна логика триггера для осуществления правила "минимум один потомок" не только во время создания, но и при удалении зависимых строк. Следующий пример вкратце описывает один способ использования триггеров для осуществления такого обязательного отношения главная-подчиненная. В нем пред- полагается, что первичный ключ главной таблице известен в приложении до того, как посылается новая запись. Во-первых, создадим две таблицы: CREATE TABLE MASTER ( ID INTEGER NOT NULL PRIMARY KEY, DATA VARCHAR(10)); COMMIT; CREATE TABLE DETAIL ( ID INTEGER NOT NULL PRIMARY KEY, MASTERED INTEGER, /* Столбец внешнего ключа сознательно сделан в виде, допускающем пустое значение */ DATA VARCHAR(10), TEMP_FK INTEGER, CONSTRAINT FK_MASTER FOREIGN KEY(MASTER_ID) REFERENCES MASTER ON DELETE CASCADE); COMMIT; Когда приложение посылает (post) строки главной и подчиненной таблиц, оно внача- ле будет передавать подчиненные строки со значением null в столбце внешнего клю- ча и со значением первичного ключа главной таблицы в столбце temp er. Затем нам нужно исключение, которое будет возникать при попытке нарушения пра- вила обязательности и при удалении последней подчиненной строки. Мы также соз- дадим генератор для подчиненной строки. CREATE EXCEPTION CANNOT_DEL_DETAIL ’This is the only detail record; it can not be deleted.'; /* Это единственная подчиненная запись: она не может быть удалена */ CREATE GENERATOR GEN^DETAIL; COMMIT; Следующий триггер проверяет подчиненную таблицу после добавления новой вер- сии главной записи. Он может "видеть" подчиненные строки, ранее посланные в той же транзакции, которые имеют значение первичного ключа (new.id) в столбце темр ек. В случае изменения, в отличие от добавления, он также может определить подчиненные строки, которыми уже "владеет" главная строка. Любые строки, кото- рые соответствуют условию темр_ек, получают значение их внешнего ключа, а temp_fk устанавливается в null. Если не найдено строк, соответствующих этому условию, то триггер добавляет "пус- тую" подчиненную строку.
680 Часть VII. Программирование на сервере SET TERM л; CREATE TRIGGER AI_MASTER FOR MASTER ACTIVE AFTER INSERT OR UPDATE POSITION 1 AS BEGIN IF (NOT (EXISTS ( SELECT 1 FROM DETAIL WHERE MASTER_ID = NEW.ID OR TEMP^FK = NEW.ID))) THEN INSERT INTO DETAIL (MASTER_ID) VALUES (NEW.ID); ELSE IF (NOT (EXISTS ( SELECT 1 FROM DETAIL WHERE MASTER_ID = NEW.ID))) THEN UPDATE DETAIL SET MASTER_ID = NEW.ID, TEMP_FK = NULL WHERE TEMP_FK = NEW.ID; END л Подчиненная таблица получает автоматически сгенерированный ключ: CREATE TRIGGER BI_DETAIL FOR DETAIL ACTIVE BEFORE INSERT AS BEGIN IF (NEW.ID IS NULL) THEN NEW.ID = GEN_ID(GEN_DETAIL, 1); END Л Следующий триггер before delete для подчиненной таблицы не позволит удалить строку, если она единственная: CREATE TRIGGER BD_DETAIL FOR DETAIL ACTIVE BEFORE DELETE POSITION 0 AS BEGIN IF (NOT (EXISTS ( SELECT 1 FROM DETAIL WHERE MASTER_ID = OLD.MASTER_ID AND ID <> OLD.ID))) THEN EXCEPTION CANNOT_DEL_DETAIL; END л Сейчас у нас ситуация, когда обязательное отношение защищено настолько хорошо, что при попытке удалить главную строку этот триггер не позволит выполнить кас- кадное удаление. Нам нужно еще два триггера для главной таблицы, расширяющие действия триггеров, созданных системой для каскадного удаления. В триггере before delete для главной таблицы мы заполняем пустым значением внешний ключ подчи- ненной таблицы и устанавливаем значение столбца temp fk. После того как будет
Глава 31. Триггеры 681 выполнено удаление строки главной таблицы, мы возвращаемся назад и удаляем подчиненную строку. CREATE TRIGGER BD_MASTER FOR MASTER ACTIVE BEFORE DELETE AS BEGIN UPDATE DETAIL SET MASTER_ID NULL, TEMP__FK = OLD. ID WHERE MASTERED - OLD.ID; END Л /* */ CREATE TRIGGER ADJUSTER FOR MASTER ACTIVE AFTER DELETE AS BEGIN DELETE FROM DETAIL WHERE TEMP_FK = OLD.ID; END л COMMIT л SET TERM ;л К сожалению, этот пример вряд ли удовлетворит всем требованиям обязательных отношений. Обычно необходимо рассмотреть некоторые другие факторы в терминах требований бизнес-правил и интерфейса программирования. Для логики триггеров редко не появляются такие причины. Поддержка ссылочной целостности Формально — или декларативно — ограничения ссылочной целостности должны использоваться везде, где они практически нужны. Проверка, выполняющаяся при поддержке формальных ограничениях ссылочной целостности, осуществляется внут- ренне триггерами. Если вы хотите расширить действия по ссылочной целостности, вам нужно создать для этого триггеры. Реализация ссылочной целостности без использования ограничений Некоторые твердолобые люди годами разрабатывают программы под Firebird и InterBase, избегая декларативной ссылочной целостности, и используют для этого триггеры. Не существует технических причин в любой версии Firebird исключать декларативную ссылочную целостность, если она вам нужна— это работает очень хорошо и не требует многих ресурсов. Однако декларативная ссылочная целостность требует внешнего ключа, который в свою очередь требует обязательного индекса. В Firebird пока не существует способа поддерживать внешние ключи без обязательного индекса. Существует ситуация, дос- таточно общая, чтобы обратить на нее внимание, когда индекс для внешнего ключа
682 Часть VII. Программирование на сервере оказывается весьма плохим с точки зрения производительности в запросах, вклю- чающих данную таблицу. Здесь следует исключить формальные ссылочные отноше- ния. Такая ситуация возникает, когда при проектировании появляются таблицы, час- то называемые таблицами "соответствия", "системными" или "управляющими". Таблица соответствия Таблица соответствия (lookup) или управляющая таблица обычно является статичной таблицей с небольшим количеством строк, которая может быть использована похо- жим образом в нескольких различных контекстах. Она содержит небольшой первич- ный ключ и поле описания, коэффициент для вычисления или некоторое правило, к которым нужно обратиться процессу. Примерами являются таблицы налогов, типы счетов, типы транзакций, коды причин и т. д. Процесс нормализации будет нарушен, если системная таблица, связанная с другими таблицами (обычно со многими), будет сохранять ключи соответствия в пользовательской таблице. Поскольку одна строка в таблице соответствия предоставляет информацию для многих строк, строгое соблю- дение правил реляционного анализа часто приводит к внешним ключам, помещен- ным в столбцы ключей соответствия в пользовательских таблицах. Это полностью допустимый и стандартный способ использования отношений — что мы можем сделать без него? Однако существует тенденция использования небольшо- го количества значений ключей соответствия в большой динамичной пользователь- ской таблице. Такие большие таблицы часто содержат немалое количество подобных ключей соответствия в виде внешних ключей, а с ними и множество автоматических нехороших индексов, которые не могут быть удалены. Результатом небольшого ко- личества значений в большом индексе может стать увеличение размера индекса, ко- торый становится все менее и менее селективным по мере роста таблицы. По природе индексирования в Firebird понятно, что такие индексы соответствия могут попросту убить эффективность запроса. Обсуждение этой проблемы см. в разд. "Тема оптими- зации" главы 18. Индексы, поддерживающие внешние ключи, являются обязательными и могут быть удалены только при удалении этого ограничения. Кроме того, с удалением такого ограничения вы теряете защиту с автоматическими триггерами ссылочной целостно- сти. Способом разрешения такой дилеммы является написание ваших собственных триггеров ссылочной целостности. Специальное отношение: пользовательская обработка ссылочной целостности Этот раздел посвящен специальному виду отношений, системным соответствиям (lookup), которые обычно не поддерживаются в декларативной ссылочной целостно- сти. Используемая здесь терминология соответствует требованиям подобного случая, поскольку установка полностью контролируемой пользователем ссылочной целост- ности является нецелесообразной для обычных отношений главная-подчиненная. Рис. 31.1 иллюстрирует эту ситуацию. Инициатор запроса, которым может быть лю- бая таблица, имеет ключ соответствия, который указывает на одну, уникально опре-
Глава 31. Триггеры 683 деляемую ключом строку в таблице соответствия. Значение этой строки предостав- ляется таблице соответствия по запросу. Lookup Unique key: UQJD a static value Рис. 31.1. Отношение-инициатор запроса — таблица соответствия Для поддержания ссылочной целостности нам нужен триггер, который предоставит набор мер безопасности пользователям таблицы соответствия (инициаторы запроса) так же, как и декларативная ссылочная целостность обеспечивает меры безопасности для защиты зависимостей главная-подчиненная6. ♦ Строка соответствия не может быть удалена, если на нее есть ссылки. Для этого нам нужен триггер before delete к таблице соответствия для проверки такой си- туации и при необходимости выдачи исключения и остановке операции. ♦ Мы должны обеспечить осуществление правила, чтобы требуемый запросом ключ соответствия соответствовал ключу в таблице. Наше правило может допускать или не допускать для ключа соответствия пустое значение. ♦ Нам может понадобиться правило, чтобы статичное значение никогда не меня- лось. В таблице налогов, например, один и тот же код налога (внешний) может быть связан с различными суммами и формулами в разные годы. Вероятно, толь- ко главному бухгалтеру будет позволено изменять строку соответствия. ♦ Триггер before update для инициатора запроса потребуется для обработки слож- ного правила, такого как описано в предыдущем пункте для проверки дат и, возможно, других критериев для осуществления правила и выбора корректного ключа. Реализация пользовательской ссылочной целостности Предположим, мы имеем следующие две таблицы: CREATE table LOOKUP ( UQ_ID SMALLINT NOT NULL UNIQUE, VALUE1 VARCHAR(30) NOT NULL, VALUE2 CHAR(2) NOT NULL, 6 Поскольку проверки Foreign Key (fk) выполняются вне транзакций, а пользовательские триггеры работают всегда в контексте транзакций, полноценно заменить fk триггерами невоз- можно — из-за ограничений "видимости" триггер будет видеть старые данные, которые уже изменены. Приведенная схема будет работать, только если для справочной таблицы наложен ряд ограничений по удалению и изменению записей. — Прим. науч. ред.
684 Часть VII. Программирование на сервере START_DATE DATE, END_DATE DATE) ; COMMIT; /* */ CREATE TABLE REQUESTOR ( ID INTEGER NOT NULL PRIMARY KEY, LOOKUP_ID SMALLINT, DATA VARCHAR(20) TRANSAC—DATE TIMESTAMP NOT NULL) ; COMMIT; Теперь мы перейдем к установлению правил существования для двух таблиц. Мы планируем использовать исключения для остановки событий DML, которые нару- шают целостность. Следовательно, вначале мы создадим эти исключения: CREATE EXCEPTION NO_DELETE 'Can not delete row required by another table'; /* Нельзя удалять строку, нужную другой таблице */ CREATE EXCEPTION NOT_VALID_LOOKUP 'Not a valid lookup key'; /* Неверный ключ соответствия */ CREATE EXCEPTION NO-AUTHORITY 'You are not authorized to change this data'; /* Вы не можете изменять эти данные */ COMMIT; Первый триггер выполняет проверку существования при попытке удалить строку соответствия: SET TERM Л; CREATE TRIGGER BD_LOOKUP FOR LOOKUP ACTIVE BEFORE DELETE AS BEGIN IF (EXISTS( SELECT LOOKUP-ID FROM REQUESTOR WHERE LOOKUP_ID = OLD.UQ_ID)) THEN EXCEPTION NO—DELETE; END Л Другая сторона проверки существования: ключ соответствия не может быть назна- чен, если он отсутствует в таблице соответствия: CREATE TRIGGER ВА_REQUESTOR FOR REQUESTOR ACTIVE BEFORE INSERT OR UPDATE AS BEGIN IF (NEW.LOOKUP_ID IS NOT NULL AND NOT EXISTS ( SELECT UQ ID FROM LOOKUP
Гпава 31. Триггеры 685 WHERE UQ_ID = NEW.LOOKUP—ID) ) THEN EXCEPTION NOT_VALID-LOOKUP; END A Теперь мы можем добавить остальные триггеры для осуществления других нужных нам правил. Например, следующий триггер позволяет выполнять изменения или уда- ления в таблице соответствия только заданному пользователю: CREATE TRIGGER BA_LOOKUP FOR LOOKUP ACTIVE BEFORE UPDATE OR DELETE AS BEGIN IF (CURRENTJJSER <> 'CHIEFACCT') THEN EXCEPTION NO_AUTHORITY; END Л Этот триггер будет проверять входной код соответствия, чтобы убедиться, что он правилен для периода транзакции, и будет корректировать его при необходимости: CREATE TRIGGER BA_REQUESTOR1 FOR REQUESTOR ACTIVE BEFORE INSERT OR UPDATE POSITION 1 AS DECLARE VARIABLE LOOKUP_NUM SMALLINT; DECLARE VARIABLE NEED_CHECK SMALLINT = 0; BEGIN IF (INSERTING AND NEW.LOOKUP_ID IS NOT NULL) THEN NEED_CHECK = 1; IF (UPDATING) THEN IF ( (OLD.LOOKUP_ID IS NULL AND NEW.LOOKUP_ID IS NOT NULL) OR (OLD.LOOKUP_ID IS NOT NULL AND NEW.LOOKUP_ID <> OLD.LOOKUP—ID)) THEN NEED—CHECK = 1; IF (NEED-CHECK = 1) THEN BEGIN SELECT L1.UQ_ID FROM LOOKUP LI WHERE LI.START_DATE <= CAST(NEW.TRANSAC_DATE AS DATE) AND LI. END_DATE >= CAST (NEW. TRANSAC_DATE AS DATE) AND L1.VALUE2 = (SELECT L2.VALUE2 FROM LOOKUP L2 WHERE L2.UQ_ID = NEW.LOOKUP-ID) INTO :LOOKUP—NUM; NEW. LOOKUP_ID = LOOKUP—NUM; END END A COMMIT A SET TERM ;A
686 Часть VII. Программирование на сервере Изменение строк в той же таблице Прежде чем решать вопрос об использовании триггера для изменения строк в той же самой таблице, внимательно рассмотрите возможность завершения цикла вложенной деятельности. Если триггер выполняет действие, которое приводит к вызову этого же триггера, или это действие вызывает другой триггер, который выполняет действие, вызывающее данный же триггер, то результатом будет бесконечный цикл. По этой причине важно убедиться, что действия триггера никогда не приведут к вызову триг- гером самого себя, даже опосредованно. Если при проектировании базы данных вы подошли к моменту, когда вам нужно на- писать триггер, который реализует зависимости данных между строками одной и той же таблицы, то, скорее всего, это сигнал о неправильной нормализации таблиц, если только эта зависимость не относится к иерархической структуре. Если фрагмент структуры строки влияет на (или на него влияет) изменение состояния другой строки, то такой сегмент должен быть в результате нормализации перенесен в другую табли- цу с внешним ключом для поддержания правила зависимости. Ссылающиеся на себя таблицы и деревья Ссылающиеся на себя таблицы, которые реализуют древовидные структуры7, явля- ются особым случаем8. Каждая строка в подобной таблице является узлом дерева и может иметь зависимые строки. Любой узел потенциально может иметь две роли: одна— роль родителя для узлов ниже него, а другая — роль потомка узла более вы- сокого уровня. Триггеры, скорее всего, будут нужны для всех событий DML: для мо- дификации поведения ограничений ссылочной целостности и для поддержания мета- таблиц (графов), используемых в некоторых иерархических алгоритмах, делающих доступной запросам геометрию дерева. Триггеры для деревьев всегда должны быть спроектированы с условиями и переходами, которые защищают структуру от беско- нечных циклов. Изменение той же строки Никогда не пытайтесь использовать оператор SQL для изменения или удаления той же самой строки, с которой оперирует триггер. Например, не рекомендуется исполь- зовать следующий вариант: CREATE TRIGGER 0_SO_SILLY FOR ATABLE BEFORE UPDATE 7 Проектирование древовидных структур в реляционных базах данных само по себе явля- ется наукой. Будучи очаровательным, это все же выходит за пределы данного руководства. Найдите в Интернете написанную Joe Celko книгу на эту тему: Joe Celko's Trees and Hierarchies in SQL for Smarties (Morgan Kaufmann, 2004). 8 Ряд очень полезных статей по реализации древовидных структур в РСУБД вы сможете найти в соответствующем разделе страницы www.ibase.ru/develop.htm. — Прим. науч. ред.
Глава 31. Триггеры 687 AS BEGIN UPDATE ATABLE SET ACOLUMN = NEW.ACOLUMN WHERE ID = NEW.ID; END A Всегда используйте переменные new для модификаций в той же строке и никогда не пытайтесь удалять ту же строку в триггере. Изменение триггеров Firebird 1.0.x предоставляет только один способ изменения триггеров при использо- вании операторов DDL, a Firebird 1.5 добавляет еще один. ♦ alter trigger изменяет определение существующего модуля триггера, сохраняя его зависимости от других объектов. Он может быть использован с минимальным беспокойством по поводу деактивации триггера. ♦ create or alter trigger (версия 1.5 и выше) создает модуль триггера, если он не существует, и работает точно так же, как и create trigger. В противном случае применяются правила alter, и зависимости сохраняются. Любая операция завершится с исключением при любой попытке изменений, которая отменяет зависимости. Синтаксис для изменения триггеров Синтаксис: {ALTER TRIGGER имя} | {CREATE OR ALTER TRIGGER имя FOR {таблица | просмотр} [ACTIVE I INACTIVE] [{BEFORE | AFTER} {DELETE | INSERT | UPDATE}] [POSITION число} AS < тело-триггерам ALTER TRIGGER Предложение for имя, применяемое в create trigger, опускается, alter trigger не может использоваться для изменения таблицы, с которой ассоциирован триггер. Изменение только заголовка Когда вы используете alter trigger для изменения только заголовка, оператор тре- бует по меньшей мере одного изменяемого атрибута после имени триггера. Любой атрибут заголовка, опущенный в этом операторе, остается неизменным. Следующий оператор деактивирует триггер save_salary_change: ALTER TRIGGER SAVE_SALARY__CHANGE INACTIVE;
688 Часть VII. Программирование на сервере Если изменяется индикатор фазы (before или after), то событие (update, insert или delete) также должно быть указано. Например, следующий оператор заново активи- рует триггер save_salary_change и указывает, что он будет выполняться до измене- ния, а не после: ALTER TRIGGER SAVE_SALARY_CHANGE ACTIVE BEFORE UPDATE; Изменение тела Любое изменение тела триггера приводит к тому, что новое определение тела заме- няет старое определение. Оператор alter trigger не должен содержать никакую ин- формацию заголовка, кроме имени триггера. Например, следующий оператор изменяет триггер set cust no, который был создан с таким определением: CREATE TRIGGER SET_CUST_NO FOR CUSTOMER BEFORE INSERT AS BEGIN IF (NEW.CUST_NO IS NULL) THEN NEW.CUST_NO = GEN_ID(CUST_NO_GEN, 1); END" Мы изменим этот триггер, чтобы он добавлял новую строку в таблицу new customers каждый раз, когда новая строка добавляется в таблицу customer: SET TERM л; ALTER TRIGGER SET_CUST_NO BEFORE INSERT AS BEGIN IF (NEW.CUST_NO IS NULL) THEN NEW.CUST_NO = GEN_ID(CUST_NO_GEN, 1); INSERT INTO NEW_CUSTONERS (NEW. CUST_NO, CURRENT_DATE) END л SET TERM ;л CREATE OR ALTER TRIGGER В версии 1.5 этот новый синтаксис создает триггер, если триггер с указанным именем не найден, или изменяет триггер с этим именем. Просто отредактируйте исходный оператор create нужным образом, добавив ключевые слова or alter. Ошибка "объект находится в использовании" Как и в случае с хранимыми процедурами, подтверждение изменений вызовет пе- чально известную ошибку "объект находится в использовании" (Object in use), если в настоящий момент какой-нибудь пользователь использует триггер или какой-либо объект, зависящий от него. В любом случае новая версия триггера не станет немед-
Глава 31. Триггеры 689 ленно доступной в Суперсервере, если старая версия все еще находится в кэше. Все пользователи должны отключиться от базы данных, а когда они вновь подключатся, то смогут видеть новую версию. В Классическом сервере новая версия будет доступна следующему клиенту, который соединится с базой данных. Inactive/Active В версии 1.5 и более поздних выполнение alter trigger ... inactive i active обычно не приводит к ошибке "объект находится в использовании", если только существую- щая транзакция не заблокировала таблицу. Такое изменение не влияет на транзакции, которые уже используют эту таблицу. Причем данное изменение будет видимым следующей транзакции, которая запрашивает изменение состояния таблицы. Удаление триггеров В процессе проектирования базы данных и разработки приложений триггер может перестать быть полезным. Для удаления триггера соединитесь с базой данных как его владелец или пользователь SYSDBA и используйте оператор drop trigger. Его синтаксис: DROP TRIGGER имя; Имя триггера должно быть именем существующего триггера. Следующий пример удаляет триггер set_cust_no: DROP TRIGGER SET_CUST_NO; ПРИМЕЧАНИЕ. Чтобы временно сделать триггер недоступным, используйте ALTER TRIGGER имя INACTIVE. Пора дальше Последняя глава этой части добавляет сахарную глазурь в наш торт для разработчи- ков базы данных. Firebird имеет две особенности PSQL: обработка пользовательских исключений и события. С помощью исключений вы имеете весьма детальный кон- троль над перехватом и обработкой сотен внутренне определенных условий ошибок и множеством ваших собственных исключений. С помощью событий вы можете реа- лизовать механизм обратных вызовов, чтобы проинформировать приложения уда- ленных клиентов о подтвержденных изменениях, выполненных другими клиентами.
ГЛАВА 32 Обработка ошибок и события В этой главе мы рассмотрим, как при выполнении модулей PSQL — триггеров и про- цедур — можно перехватывать и обрабатывать ошибки в выполняемом коде. Стандартным поведением модулей PSQL при появлении исключений является оста- новка выполнения, отмена всей работы, выполненной с начального оператора begin, переход на конечный оператор end и возврат управления клиенту с передачей одного или более сообщений об ошибке. Если этим модулем является триггер, исключение отменит работу всех предыдущих триггеров и предотвратит посылку запрашиваемых изменений DML. Типы исключений Может появиться три типа исключений. ♦ Ошибки SQL — т. е. сообщения SQL, имеющие отрицательное значение SQLCODE. ♦ Внутренние ошибки Firebird, которые имеют отношение к конкурирующему взаимодействию, данным, метаданным и условиям окружения. У них есть девяти- символьный код ошибки, обычно начинающийся с 3355, который уникально идентифицирует код gdscode. Большинство кодов gdscode попадают в обобщаю- щие группы кодов sqlcode, и при возникновении исключения вы обычно получае- те И SQLCODE, И GDSCODE. ♦ Пользовательские исключения, которые вы объявляете как постоянные объекты базы данных и "вызываете" в коде, когда определяется специфическое условие. Что такое исключение? Исключение — это просто сообщение, которое генерируется, когда появляется ошибка. Все предварительно определенные исключения — sqlcode и gdscode — имеют ассо- циированные с ними тексты сообщений. Сообщения по умолчанию на английском языке, но могут использоваться и другие языки. Существует небольшое количество версий сообщений на других языках (включая латинский!), другие или "находятся в работе", или "ожидают желающих поработать"1. 1 Как показывает практика, такие переводы не приносят ожидаемой пользы разработчикам. Сообщения сервера ориентированы на разработчика приложений, и даже будучи переведен- ными, слишком сложны для восприятия пользователем программы. Более грамотным является
Глава 32. Обработка ошибок и события 691 В Firebird существует синтаксис DDL для создания пользовательских исключений с текстами сообщений до 78 символов. В Firebird 1.5 вы можете расширить ваши поль- зовательские исключения во время выполнения, заменить текст сообщения, посы- лаемого по сети, в зависимости от контекста. Создание исключения Создание исключения является одним из самых простых элементов DDL. Синтаксис: CREATE EXCEPTION имя-исключения <сообщение>; имя-исключения— обычный идентификатор Firebird до 31 символа длиной. Оно долж- но быть уникальным среди идентификаторов исключений, а в диалекте 3 может быть заключено в кавычки. Тогда имя будет чувствительным к регистру. <сообщениё> — заключенная в апострофы строка текста в наборе символов none. Из-за ограничения размера текст должен быть лаконичным. Например: CREATE EXCEPTION NO_DOGS 'No dogs allowed!'; COMMIT; Оператор create exception должен быть подтвержден, как и любой другой оператор DDL. Изменение и удаление исключения Как пользователь SYSDBA или владелец исключения, которое используется в хра- нимых процедурах, вы можете изменять или удалять его в любое время. Если оно используется в триггере, вы можете его только изменять и изменять только текст со- общения. Не хранится никаких зависимостей для исключений, используемых в хра- нимых процедурах. Это создает проблему в случае, когда вы удаляете исключение и забываете убрать его из хранимых процедур — будет неловко получить исключение по причине отсутствия исключения! Для удаления нашего исключения no_dogs введите: DROP EXCEPTION NO_DOGS; Для его изменения: ALTER EXCEPTION NO_DOGS 'No dogs allowed except Irish Wolfhounds!'; СОВЕТ. При конструировании скриптов схемы сгруппируйте вместе все ваши операторы create exception, чтобы их было проще отыскивать в процессе раз- работки и модификации, а также с целью документирования. Разработчики часто используют короткие префиксы или какую-нибудь систему именования исключе- ний в соответствии с категориями пользовательских исключений. корректная обработка ошибок, возникающих на сервере, в коде клиентского приложения, и выдача пользователю осмысленных и понятных сообщений в контексте прикладной области. Не составляет сложности в это же время сохранять исходные сообщения от сервера в специаль- ном файле для последующего анализа корректности работы приложения. — Прим. науч. ред.
692 Часть VII. Программирование на сервере Исключения в действии Внутренне определенные исключения вызываются ядром сервера в ответ на соответ- ствующие ошибки, которые требуют прекращения выполнения. Они охватывают большое количество условий, включая каждый вид нарушения ограничений, арифме- тические и строковые переполнения, ссылки на отсутствующие объекты, разрушение данных и т. д. Исключения sqlcode и gdscode являются теми же самыми исключения- ми, что и исключения, используемые при появлении ошибок в процессе выполнения операций динамического SQL. Они описаны в приложении 10. Пользовательские исключения, доступные только в модулях PSQL, не должны дуб- лировать работу внутренне определенных исключений. Определяйте ваши исключе- ния для использования там, где вы хотите в вашем коде выявлять ошибочные ситуа- ции, которые нарушают ваши бизнес-правила. Три вида исключений изображены на рис. 32.1. У нас был в главе 30 пример, в котором пользовательское исключение применялось в триггере для прекращения события, продолжение которого нарушило бы бизнес- правило. В этом случае хранимая процедура позаботится о том, чтобы убрать зави- симости из организационной структуры при удалении служащего. Это было объяв- лено следующим образом: CREATE EXCEPTION REASSIGN_SALES 'Reassign the sales records before deleting this employee.' л /* Переназначьте записи продаж перед удалением этого служащего */ COMMIT л BEGIN BEGIN END END SUSPEND; /* If looping */ IF (SOMETHINGJ/VRONG) THEN EXCEPTION MY_EXCEPTION; Execution jumps to here Error status array (messages) SQLCODE -nnn------ GD CODE some_error Рис. 32.1. Стандартная реакция PSQL на исключения Uh DO ALL J
Гпава 32. Обработка ошибок и события 693 В том месте, где используется это исключение, процедура проверяет, является ли данный служащий участником продаж в каком-либо документе продаж. Если да, то используется пользовательское исключение для завершения процедуры. Конечно, если возникает исключение, то все другие действия, выполненные в процедуре, от- меняются. BEGIN IF (EXISTS(SELECT PO_NUMBER FROM SALES WHERE SALES_REP = : emporium) ) THEN EXCEPTION reassign_sales; JF ПРИМЕЧАНИЕ. В хранимых процедурах выбора выходные строки, которые уже были получены клиентом в предыдущих циклах for select ... do ... suspend, \\ остаются доступными для клиента. О механизме, работающем в этом случае, см. далее разд. "Оператор WHERE". Существуют случаи, когда возможно использовать пользовательское исключение как способ вмешательства в возникшую проблему и позволить процедуре продолжать выполнение. Вы можете перехватить исключение и написать код для его обработки прямо в этой процедуре. В следующем разделе рассматривается, каким образом эта техника перехвата и исправления может быть использована при исключении reassign_sales. Обработка исключений Код PSQL может перехватывать ошибки при их появлении и затем их обрабатывать в подпрограмме обработки исключений. Если исключение будет обработано в вашем коде— вы обеспечите исправление или обход ошибки и позволите продолжить вы- полнение, — то клиенту не возвращается никакого сообщения об исключении. Рис. 32.2 иллюстрирует логику перехвата и обработки ошибок. Как и раньше, исключение приводит к прекращению выполнения в блоке. Вместо того чтобы передать выполнение на конечный оператор end, теперь процедура оты- скивает уровни во вложенных блоках, начиная с блока, где была выявлена ошибка, и переходит на внешние блоки, чтобы найти код обработчика, который "знает" о таком исключении. Она отыскивает первый оператор when, который может обработать эту ошибку. Оператор WHEN Оператор when имеет следующую форму: WHEN <исключение> DO <составной-оператор> Здесь исключение может быть одним из следующих: <имя-исключения> | GDSCODE код I SQLCODE код | ANY
694 Часть VII. Программирование на сервере BEGIN BEGIN | SQL CODE -nnn----- GD CODE some_error I IF (SOMETHING_WRONG) THEN—> । EXCEPTION MY-EXCEPTION; I : : t. —-z SUSPEND; Г If looping */ WHEN MYEXEPTION GDS CODE some_error qq SQL CODE -nnn ~___ ANY 4 I BEGIN I ~ z — I --------------- .. zzz I END END Execution resumes here after handling MY_EXEPTION 4____________ WHEN GDS CODE some error qq SQL CODE -nnn ANY BEGIN END ERRORS NOT ALREADY HANDLED Undo block where error occurred Рис. 32.2. Логика перехвата и обработки ошибок END <составной-оператор> может быть одним оператором или множеством обычных опе- раторов PSQL, заключенным в блок begin ... end. Область видимости типов исключений Принцип типов исключений, показанный в структурах синтаксиса, представляет объ- ем области видимости. Пользовательское исключение может соответствовать любому выбранному вами условию, включая правила, которые вы, может быть, не в состоянии реализовать с помощью выражений в ограничениях. Операторы when и код обработчика пользова- тельских исключений лучше всего поместить в тот же блок, где может появиться ошибка.
Гпава 32. Обработка ошибок и события 695 Следующим по глубине является gdscode. В версии 1.0.x это контекстная переменная, в которой процедура может только получить код и сравнить его с кодом, заданным в предикате when: WHEN GDSCODE foreign_key DO BEGIN END Начиная с версии 1.5, gdscode является "полнофункциональной" контекстной пере- менной. При ее чтении внутри блока, где возникло исключение, вы можете сохранить этот код в записи протокола. СОВЕТ. Все коды gdscode имеют символические константы, которые являются более или менее понятными для людей, знающих английский язык. Именно эти символические константы вы должны использовать в операторах when gdscode, а не числовые коды. Если вы посмотрите заголовочный файл iberror.h в вашем каталоге /include, вы увидите, что определение символических констант имеют префикс isc_. Этот префикс должен быть опущен в операторах when gdscode. Некоторые ошибки, выявленные в gdscode, могут быть исправлены внутри области действия блока, где они появились. Если это так, то оператор when для обработки та- ких ошибок должен находиться здесь, иначе он должен быть помещен во внешний цикл для обработки там ошибок. Код sqlcode является довольно общим, он не всегда соответствует типу ошибки. Опе- рации SQL передают sqlcode о при успешном завершении и sqlcode ioo при дости- жении конца файла. Диапазон неиспользуемых "сегментов" находится между 1 и 99 для предупреждающих и информационных сообщений. Диапазон ошибок sqlerror— отрицательные числа, которые больше -1000. Такие коды ошибок sqlerror в sqlcode обычно являются группировкой на более высоком уровне нескольких кодов gdscode. Как и gdscode, sqlcode допускает только программное чтение в версии 1.0.x, но стано- вится настоящей контекстной переменной в версии 1.5 и более поздних. Вы можете поместить это значение в протокол. ПРИМЕЧАНИЕ. Вы можете использовать либо gdscode, либо sqlcode. Вы полу- чите код для одного и null для другого. Коды sqlcode менее детализированы и в большинстве случаев менее всего подходят для условий, которые могут быть исправлены внутри модуля. Посмотрите коды в приложении 10, обратите внимание, что один код sqlcode чаще всего группирует не- сколько кодов gdscode. Обычно они более полезны во внешнем блоке модуля. Ключевое слово any— это "оставшиеся исключения". Оно означает перехват любого определенного внутренне исключения, которое не было обработано. В версии 1.5, наряду с возможностью чтения gdscode или sqlcode (в рамках области действия), у any
696 Часть VII. Программирование на сервере есть лучшая возможность создания обработчика по умолчанию, чем в предыдущих версиях. Размещение блоков WHEN Всегда помещайте ваши блоки when непосредственно перед оператором end, закры- вающим тот блок, исключения которого вы хотите обрабатывать. Не помещайте ни- какие другие операторы — даже suspend или exit — между концом ваших обработ- чиков и завершающим оператором end. Вернитесь к рис. 32.2, где описан этот поток. СОВЕТ. Если вы хотите использовать оператор exit непосредственно перед финальным оператором end вашего модуля с целью документирования, то это нормально. В этом месте оператор exit не может повлиять на поток выполнения. Когда процедура встречает ошибку в цикле курсора процедуры выбора, отменяются все операторы, начиная с последнего и до предыдущего suspend. Эта отмена не влия- ет на строки, уже выведенные предыдущими вызовами suspend; они остаются дос- тупными для клиента. Оператор suspend не должен использоваться в выполняемых процедурах. Пусть логи- ка вашего выполнения определяет, когда блоки завершаются и когда ошибки должны возвращаться в виде исключений. Вложенные исключения в качестве точек сохранения Вложенная архитектура блоков выполнения модулей PSQL означает, конечно, что PSQL поддерживает "вложенные" транзакции. Деятельность каждого модуля PSQL выполняется в контексте той транзакции, в которой он был вызван. Стандартный по- ток выполнения гарантирует, что работа либо завершится как единое целое, либо вся будет отменена. В случае хранимых процедур исключение приводит к отмене всей работы, выполненной во время вызова. В случае триггеров исключение приводит к отмене DML и всех уже выполненных действий, связанных с этой операцией. Обработка исключений предоставляет средства для разбиения выполнения на этапы, которые могут быть отменены до определенной точки без необходимости отмены всей работы модуля. Уровень отмены определяется в точке появления ошибки и ближайшего предложения обработчика when. Такая "точка сохранения" — эквивалент именованной точки сохранения в управляемой клиентом транзакции — создается в коде блока, в котором выполняется код обработчика when. Обработка исключения REASSIGN_SALES Теперь вернемся к нашей процедуре delete_employee. В главе 30, когда эта процедура сталкивалась с ситуацией, где удаляемый служащий имеет заказы в картотеке, она выдавала пользовательское исключение reassign_sales и просто прекращала работу,
Глава 32. Обработка ошибок и события 697 отменяла выполненные действия и посылала сообщение исключения работающему с ней человеку. Однако мы можем заставить процедуру обработать эту ситуацию и позволить про- цедуре завершиться. Например, обработчик может обнулить ключ saleS-Rep и по- слать сообщение другой процедуре, которая создаст записи в таблице протокола для каждой соответствующей записи продаж. Мы начинаем с создания таблицы протокола: SET TERM Л; CREATE TABLE EMPLOYEE_LOG ( EMP__NO SMALLINT, TABLE_AFFECTED CHAR(31), FIELD_AFFECTED CHAR(31), FIELD_VALUE VARCHAR(20), USERJNAME VARCHAR(31), DATESTAMP TIMESTAMP) Л COMMIT Л Затем нам нужно создать процедуру, которая будет выполнять протоколирование. Она будет достаточно общей, потому что мы полагаем, что подобная процедура про- токолирования может понадобиться и для других задач в этой системе: CREATE PROCEDURE LOG_ACTION ( EMP-_NO SMALLINT, TABLE_AFFECTED CHAR(31), FIELD-AFFECTED CHAR(31), FIELD_VALUE VARCHAR(20)) AS BEGIN INSERT INTO EMPLOYEE-LOG VALUES (:EMP_NO, :TABLE_AFFECTED, :FIELD_AFFECTED, : FIELD-VALUE, CURRENT-USER, CURRENT—TIMESTAMP) ; END Л Последнее, что нужно сделать, — это добавить код обработки исключения в нашу процедуру deletE-Employee: RECREATE PROCEDURE DELETE-EMPLOYEE ( :emp_num INTEGER) AS DECLARE VARIABLE PO_NUMBER CHAR(8); BEGIN IF (EXISTS (SELECT PO_NUMBER FROM SALES WHERE SALES-REP = remP-Rum)) THEN EXCEPTION reassigii-Sales; В этом месте, если появится исключение, последующие операторы не будут выпол- няться, и управление будет передано на первый оператор when, который может обра- батывать это исключение.
698 Часть VII. Программирование на сервере UPDATE department ... SET ... DELETE EROM employee WHERE emp_no = :emp_num; Вот блок обработчика. Первым делом он в цикле просматривает таблицу sales и ус- танавливает sales_rep в null во всех строках, где появляется код нашего удаляемого служащего. В каждой итерации этого цикла он вызывает процедуру протоколирова- ния, передавая ей код служащего вместе с информацией о записи sales: WHEN EXCEPTION REASS IGN_S ALES DO BEGIN FOR SELECT POJSIUMBER FROM SALES . WHERE SALES_REP = :emp_num INTO :PO_NUMBER AS CURSOR C DO BEGIN UPDATE SALES SET SALES__REP = NULL WHERE CURRENT OF C; EXECUTE PROCEDURE LOG_ACTION ( :emp_num, 'SALES', 'POJIUMBER', :PO_NUMBER); END После завершения цикла главная процедура еще раз вызывает саму себя для завер- шения обработки, которая была пропущена по причине исключения: EXECUTE PROCEDURE DELETE^EMPLOYEEl (:emp_num); END ENDA COMMIT Л Протокол ошибок Если для вас важно сохранять протокол ошибок, помните, что исключения, возни- кающие у клиента, приводят к отмене всей работы, выполненной в модуле. Если вы ведете протокол в таблице базы данных, то записи протокола исчезнут вместе с дру- гой отмененной работой. В случаях, когда обработчики исправят или "проглотят" каждую ошибку, внутренняя таблица будет работать просто прекрасно. Если вам нужен протокол, который будет сохраняться и после необработанного ис- ключения, используйте внешнюю таблицу. Подробности см. в разд. "Использование внешних файлов в качестве таблиц"главы 16. СОВЕТ. В конце этой главы описано применение подобной техники в процедуре, которая добавляет строки во внешнюю таблицу, хотя последняя не является таблицей протоколирования ошибок.
Гпава 32. Обработка ошибок и события 699 SQLCODE Vi GDSCODE В версии 1.5 и выше вы можете перехватить числовой код ошибки, который переда- ется внутренне определенному исключению в контекстной переменной sqlcode или GDSCODE. Это предоставляет весьма компактный способ протоколирования текущего исключения в виде фрагмента вашей подпрограммы обработки исключений. Внутренне определенные исключения имеют и sqlcode, и gdscode. Ваш код может обратиться только к одному коду, другой будет недоступен. ПРИМЕЧАНИЕ. Каждый раз, когда вы попытаетесь обратиться к этим кодам вне блока обработчика, вы получите ноль. Следующая структура блока кода завершается группой обработчиков исключений. Первые два обрабатывают ошибки sqlcode в виде пользовательских исключений. Эти пользовательские исключения могут быть обработаны во внешнем блоке или их на- значением может быть аварийное завершение процедуры и возврат клиенту полезно- го сообщения. Если не появилось ни одного из предусмотренных исключений, а было некоторое другое, то его перехватит оператор when any. Его обработчик вызывает хранимую процедуру для вывода записи протокола, передавая код sqlcode вместе с другими входными аргументами, полученными из контекста блока: BEGIN WHEN SQLCODE -802 DO EXCEPTION E_EXCEPTION_1; WHEN SQLCODE -803 DO EXCEPTION E__EXCEPTION_2; WHEN ANY DO EXECUTE PROCEDURE P__ANY__EXCEPTION (SQLCODE, другие входные данные . . .) ; END Повторный вызов исключения Предположим, вам нужно перехватить и внести во внешнюю таблицу протокола не- предвиденную ошибку, перед тем как позволить исключению выполнить свою рабо- ту и завершить процедуру или триггер. Начиная с версии 1.5, можно повторно вы- звать исключение — вы можете выполнить некоторую обработку исключения и за- вершить обработчик оператором exception для возбуждения исключения и передачи управления на конечный end. Выполнение останавливается, и управление передается клиенту с кодом или именем исключения и подходящим сообщением в массиве со- стояния ошибки. В вашем обработчике вы выбираете sqlcode или gdscode и некоторые другие контек- стные переменные, записываете запись в протокол, а затем заново вызываете исклю- чение:
700 Часть VII. Программирование на сервере BEGIN WHEN ANY DO BEGIN EXECUTE PROCEDURE P_ANY_EXCEPTION (SQLCODE, другие входные данные EXCEPTION; END END л Исключения в триггерах Пользовательские исключения в триггерах способны поддерживать бизнес-правила. В примере базы данных employee есть правило, по которому покупатели, которым отказано в доверии, отмечаются в столбце on__hold, который ограничен значениями null или Когда добавляется запись в sales или изменяется существующая запись с неотправленным товаром для такого покупателя, то такой заказ отклоняется, если флаг on_hold не имеет значения null. Другое правило гласит, что заказ, который уже был отправлен, не может изменяться. При добавлении или изменении записей заказов мы можем написать триггеры before, которые при нарушении этих правил выдают исключения и блокируют операцию. Для любой версии Firebird мы можем написать два триггера для осуществления этих правил: before insert и before update. Создадим исключения для двух условий: CREATE EXECPTION EJOANTJXCCEPT ’Operation refused. REASON: Customer is on hold.' л /* Операция отклоняется. Причина: деятельность покупателя приостановлена */ CREATE EXCEPTION EjCANT_EXTEND 'Operation refused. REASON: Order already shipped.' л /* Операция отклоняется. Причина: заказ уже отправлен */ COMMIT Л Триггеры для версии 1.0.x: CREATE TRIGGER BI_SALES0 FOR SALES ACTIVE BEFORE INSERT POSITION 0 AS BEGIN IF (EXISTS (SELECT 1 FROM CUSTOMER WHERE CUST_NO = NEW.CUST_NO AND ON_HOLD IS NOT NULL)) THEN EXCEPTION EjOANT_ACCEPT; END Л /* */ CREATE TRIGGER BU_SALES0 FOR SALES ACTIVE BEFORE UPDATE POSITION 0 AS
Гпава 32. Обработка ошибок и события 701 BEGIN IF (OLD.ORDER_STATOS = 'shipped') THEN EXCEPTION E_CANT_EXTEND; ELSE IF (EXISTS'(SELECT 1 FROM CUSTOMER WHERE CUST_NO = NEW.CUSTJIO AND' ONJIOLD IS NOT NULL) ) THEN EXCEPTION E_CANT_ACCEPT; END л Сообщения исключений во время выполнения Некоторые улучшения версии 1.5 по обработке сообщений исключений обеспечива- ют больше возможностей написания обработчиков исключений. Статическое сооб- щение исключения, определенное в create exception, во время выполнения может быть заменено на другой текст, обеспечивающий лучший контекст для пользователя, более точно идентифицирующий проблемные данные. В следующем примере мы используем возможности версии 1.5 по реализации тех же самых правил, что и в двух триггерах предыдущего примера. На этот раз мы помес- тим эти правила в один триггер и используем сообщения времени выполнения. Вот исключение: CREATE EXCEPTION E_REFUSE_ORDER 'Operation refused. ' Л /* Операция отвергнута */ А вот триггер: CREATE TRIGGER BA_SALES0 FOR SALES ACTIVE BEFORE INSERT OR UPDATE POSITION 0 AS DECLARE VARIABLE ORDER_STATE SMALLINT = 0; BEGIN IF (UPDATING AND OLD.ORDERJ5TATOS = 'shipped') THEN ORDER_STATE = 1; IF ( (EXISTS (SELECT ON_HOLD FROM CUSTOMER WHERE CUST_NO = NEW.CUST_NO AND ON_HOLD IS NOT NULL) AND (INSERTING OR ORDER__STATE = 0)) THEN ORDER_STATE = 2; IF (ORDER_STATE = 1) THEN EXCEPTION E_REFUSE_ORDER 'Order ' || NEW.PO_NUMBER || ' already shipped.'; /* EXCEPTION E_REFUSE_ORDER Заказ ' |I NEW.PO_NUMBER || ' уже отправлен' */ ELSE IF (ORDER_STATE = 2) THEN EXCEPTION E_REFUSE_ORDER
702 Часть VII. Программирование на сервере 'Order NEW.PO_NUMBER ||'. Customer ' || NEW.CUST_NO || ' is on hold.'; /* ’Заказ '|| NEW.PCyNUMBER |1’. Покупатель ’ || NEW.CUST_NO || ' заблокирован'; */ END Л В массиве состояния ошибки клиент получит имя исключения вместе с сообщением времени выполнения. Список кодов ошибок Приложение 10 содержит списки внутренне определенных исключений, включая коды sqlcode, gdscode, символы для gdscode и сообщения на английском языке, соот- ветствующие текущему состоянию релиза Firebird 1.5.0. При создании двоичного кода Firebird сообщения на английском языке выбираются из внутренней базы данных. Коды sqlcode сохраняются, но gdscode вычисляются на лету. Файл firebird.msg в вашем корневом каталоге Firebird создан в виде двоичного дерева, к которому обращаются клиент и сервер при работе сервера. Написание вашего собственного списка кодов ошибок Ради любопытства вы можете легко получить версию gbak для базы данных (с име- нем gbak.msg) из репозитория CVS проекта Firebird. Войдите на http:// sourceforge.net/projects/firebird и идите по ссылке CVS Browser, пока не доберетесь до ветви с именем ,/firebird/firebird2/src/msgs/. В нижней части дисплея щелкните по стрелке выпадающего списка для поиска интересующей вас ветви. Оттуда вы сможе- те получить файл. Следующая хранимая процедура сгенерировала список для приложения 10. Она вы- водит список во внешнюю таблицу, но вы можете модифицировать эту процедуру подходящим для вас образом. SET TERM /* Выходной файл */ CREATE TABLE ERRORCODES EXTERNAL FILE 'C:\Program Files\Firebird\Firebird_l_5\MyData\2’794applO.txt' (Listitem CHAR(169)J" COMMIT" /* При необходимости уберите комментарии в следующей секции и объявите функцию ASCII_CHAR() для получения символов возврата каретки и перевода строки */ /* DECLARE EXTERNAL FUNCTION ascii_char INTEGER RETURNS CSTRING(1) FREE_IT ENTRY_POINT 'IB_UDF_ascii_char’ MODULE_NAME 'ib_udf'" COMMIT"
Гпава 32. Обработка ошибок и события 703 /* Под конец сама хранимая процедура, создающая текстовый файл, эквивалентный приложению 10 данной книги*/ CREATE PROCEDURE OUTPUT_ERRCODES AS DECLARE VARIABLE SQC SMALLINT; DECLARE VARIABLE NUM SMALLINT; DECLARE VARIABLE FAC SMALLINT; DECLARE VARIABLE SYM VARCHAR(32); DECLARE VARIABLE TXTVARCHAR (118); DECLARE VARIABLE GDC CHAR(9) CHARACTER SET OCTETS; DECLARE VARIABLE BASED INTEGER = 335544320; DECLARE VARIABLE CALCNUM INTEGER; DECLARE VARIABLE EOL CHAR(2); BEGIN EOL = ASCII_CHAR(13) || ASCII_CHAR(10); /* последовательность конца строки */ FOR SELECT S.SQL_CODE, S.NUMBER, S.FACJDODE, S. GDS_SYMBOL, M.TEXT FROM SYSTEM_ERRORS S JOIN MESSAGES M ON M.FAC_CODE = S . FACJCODE AND M.NUMBER = S.NUMBER AND M.SYMBOL = S.GDS_SYMBOL /* Устранение некоторых нежелательных/ненужных кодов */ WHERE M.TEXTNOT CONTAINING 'journal' AND M.TEXTNOT CONTAINING 'dump' AND s.GDS_SYMBOL NOT CONTAINING 'license' AND S.GDS_SYMBOL NOT CONTAINING 'wal_' AND S. GDS_SYMBOLIS NOT NULL AND S.SQL_CODE < 102 ORDER BY1 DESC, 2 INTO :SQC, :NUM, :FAC, :SYM, :TXT DO BEGIN /* Тексты сообщений в нижнем регистре, следовательно, мы выполним небольшой трюк для перевода в верхний регистр первой буквы */ IF (TXT IS NULL) THEN TXT = '{Message unknown}'; ELSE TXT = UPPER(SUBSTRING(TXT FROM 1 FOR 1)) || SUBSTRING(TXT FROM 2); /* Разработанные значения кодов (FAC_CODE) и чисел NUMBER используются для генерации чисел GDSCODE. Очень просто получить их из последних таблиц SYSTEM_ERRORS и MESSAGES */ IF (FAC IS NOT NULL AND NUM IS NOT NULL) THEN
704 Часть VII. Программирование на сервере /* Нам не нужны наполовину приготовленные коды ошибок! */ BEGIN CALCNUM = BASED + (FAC * 65535); CALCNUM - CALCNUM + NUM + FAC; GDC = CAST(CALCNUM AS CHAR(9)); INSERT INTO ERRORCODES VALUES( /* все переменные поступают в одной строке */ : SQC | I’I’II :GDC| | ' | ' I I :SYM| I ' | ’ | 1 :ТХТ| | :EOL) ; END END END Л COMMIT*' EXECUTE PROCEDURE OUTPUT_ERRCODES Л COMMIT*4 SET TERM ;л /* Текстовый файл теперь готов к обработке текстовым процессором для небольшой корректировки, чтобы избавиться от лишних пробелов, созданных в правой части выходной строки. Быстрый поиск и замена заменят все разделители '|’ на ASCII 9 (tab), потому что это было требованием к табуляции для печати. */ События События Firebird предоставляют механизм сигнализации, с помощью которого хра- нимые процедуры и триггеры могут передавать сообщения клиентским приложени- ям, когда другие приложения подтверждают изменения данных. Клиентские прило- жения устанавливаются в режим "прослушивания" конкретных событий через ин- терфейс сервер-клиент без системных затрат на опрос наличия изменений. Клиентские подсистемы, которые запрашивают у сервера новости об изменениях состояния базы данных, не являются редкостью в мире реляционных подсистем баз данных. Однако модель сообщения о событиях в Firebird не расходует ресурсов сети или процессора при опросе. Это является подсистемой сервера, которая поддержива- ется сервером и на сервере. Клиент "регистрирует интерес" в событии и сигнализи- рует, что он ждет этого события. Когда транзакция подтверждается, сообщения обо всех произошедших событиях пе- редаются всем ожидающим их клиентским приложениям. После этого клиентское приложение может отреагировать на это событие любым образом. Использование сообщений о событиях Система сообщений о событиях в Firebird может соответствовать большому количе- ству требований приложения, которое вызывает эти средства для быстрого реагиро- вания на изменения состояния базы данных, выполненные другими пользователями базы данных. Такие техники могут быть использованы в комбинации со средствами
Гпава 32. Обработка ошибок и события 705 удаленной связи, процессом управления, технологиями планирования и передачи сообщений для автоматизации критичных по времени потоков реагирования. Эти возможности безграничны в терминах масштаба и приложения. Некоторыми примерами являются: ♦ сервисы фоновой репликации данных запрашивают новый элемент; ♦ приложение продажи билетов использует эту схему в качестве сигнала для обнов- ления открытых наборов данных в других офисах, когда происходит изменение выделенных мест или расписания; ♦ приложение инвентаризации выдает отделу закупок сообщение "запас на исходе", когда количество элементов инвентаризации находится ниже минимально допус- тимого уровня запасов; ♦ розничные магазины информируются о загрузке нового прайс-листа; ♦ устройство слежения за механическим процессом сигнализирует о низком уровне запасов сырья. Элементы механизма Инициаторами событий являются операции изменения состояния базы данных — успешно выполненные операции insert, update и delete. Сигнализация о событии выполняется в триггерах или хранимых процедурах с помощью оператора PSQL POSTJlVENT. Однако post event является только одним элементом этого механизма— изолиро- ванно он ничего не делает. Это просто посылка сигнала слушающим приложениям. Он не несет никакой информации, о каком событии базы данных он сигнализирует; задачей приложения является обеспечение собственного контекста для каждого со- бытия. Сам механизм событий состоит из нескольких взаимодействий между серверной сто- роной и приложением. Элементы на стороне сервера Элементами на стороне сервера являются: ♦ один или более триггеров или хранимых процедур, которые выдают оператор post_event; ♦ внутренняя таблица событий — адресат вызовов post_event — содержит список направленных ей событий процедурами и триггерами во время работы транзак- ций, при которых возникли события; ♦ подсистема управления внутренними событиями, которая поддерживает список прослушивающих и ожидающих приложений и работает как "полицейский- регулировщик" для направления соответствующих событий прослушивающим приложениям. 23 Зак 420
706 Часть VII. Программирование на сервере Элементы приложения На стороне приложения этому механизму нужно: ♦ приложение, которое способно зарегистрировать свой интерес в событиях; ♦ другие приложения, которые выполняют те операции DML, которые прослушива- ет заинтересованное приложение. Естественно, прослушивающему приложению также необходим механизм реагиро- вания на события. Элементы интерфейса При пересылке событий от сервера клиенту используется пара портов, отличная от порта, используемого в главном канале клиент-сервер (обычно порт 3050). Сервер и клиентская библиотека находят произвольную пару портов для использования в ка- честве трафика событий. Элементом программного обеспечения является клиентская подпрограмма, называе- мая функцией обратного вызова события. Это код на клиенте, который вызывается сервером для информирования клиента о событиях, как только подтвердится тран- закция, в рамках которой было отправлено ожидаемое событие. Для встроенных при- ложений предкомпилятор gpre генерирует код для таких функций обратного вызова. Для динамических приложений, которые хотят выполнять прослушивание синхронно (см. следующий раздел), как это делают приложения ESQL, функция обратного вы- зова содержится в клиентской библиотеке. Динамические приложения могут — и обычно так и делают — прослушивать асинхронно (см. разд. "Асинхронная сигнали- зация" и "Асинхронное прослушивание"). Для этого они должны предоставить пользо- вательскую функцию обратного вызова, называемую асинхронным перехватчиком (Asynchronous Trap, AST). СОВЕТ. Если ваша стратегия межсетевой защиты не позволяет приложениям выбирать произвольный порт, Firebird версии 1.5 и выше позволяет специально сконфигурировать такой вспомогательный порт — используйте параметр RemoteAuxPort в firebird.conf. Синхронное прослушивание На рис. 32.3 показана модель событий, которая реализована в языке ESQL для встро- енных приложений с помощью операторов event init и event wait. Динамический SQL не имеет эквивалентных операторов SQL. Для приложений динамического SQL та же синхронная модель событий реализована в API с помощью функции isc_wait _for_event(). Приложение ESQL использует event init для сообщения, что оно прослушивает со- бытие, и event wait, что оно ожидает оповещения. Оно прослушивает оповещение через вспомогательный канал между портами сети, используя главный дескриптор
Гпава 32. Обработка ошибок и события 707 канала соединения с базой данных. Когда вызывается event wait, выполнение кли- ентского приложения приостанавливается, пока не придет сообщение о событии. CLIENT MACHINE ESQL CLIENT PROCESS X I Event I response INIT_EVENT WAIT_EVENT <— big_event big_event ? -------------> <----- notification Рис. 32.3. Синхронная сигнализация Клиент в сети посылает изменение строки таблицы mytable. Его принимает и обраба- тывает сервер. В процессе фазы after update триггер посылает сообщение с именем big event для оповещения менеджера событий, что изменение завершено. Менеджер событий добавляет это событие в список своих событий. В это время из- менение не подтверждено, и менеджер событий больше ничего не делает. В своем списке прослушивающих он отыскивает процесс х, который прослушивает это собы- тие. Процесс х будет ожидать, пока не подтвердится одно или более событий big_event. При выполнении commit менеджер событий посылает процессу х и всем другим про- цессам, ожидающим событие big_event, оповещение, ЧТО big_event произошло. Даже если транзакция много раз отправляла big event, ожидающие клиенты получат одно оповещение. Если ни один процесс не зарегистрировал интерес в big event, менеджер событий просто игнорирует post event. Все процессы, к настоящему моменту выдавшие event wait для big_event, получат оповещение немедленно. Если какой-нибудь процесс зарегистрировал свой интерес в big_event, но не ожидает этого события, менеджер событий оставляет это событие, пока процесс либо не просигнализирует об ожида- нии, либо не отменит свой интерес. Если заинтересованные приложения потеряют интерес, событие big event будет удалено из этой таблицы. Приложение может ожидать не более 15 событий в одном запросе event init. Оно может распределить события между несколькими запросами event init, однако в случае синхронизированных событий оно может ожидать обработки только одного запроса event init в каждый момент времени.
708 Часть VII. Программирование на сервере Асинхронная сигнализация Асинхронная сигнализация имеет свои ограничения. В частности, она требует, чтобы приложение ожидало оповещения бесконечное время. Это ограничение модели было устранено при поддержке асинхронной сигнализации. В этой модели приложение также регистрирует интерес и продолжает ожидать и прослушивать, однако оно способно продолжать собственное выполнение и выпол- нять запросы к базе данных в процессе ожидания оповещений. Приложение имеет свою собственную очередь событий, которой оно управляет на клиентской стороне. Рис. 32.4 описывает элементы установки асинхронного прослушивания. big_event ? SERVER HOST SERVER Рис. 32.4. Механизм асинхронных событий PSQL Module POST_EVENT big event ’4 Event Manager - big_event —- EVENT TABLE — OTHER PROCESS Приложение для биржи, например, требует постоянного доступа к базе данных stocks для обеспечения брокеров в реальном времени информацией об изменении цен, однако оно также должно постоянно просматривать отдельные акции и пере- ключать соответствующую процедуру Buy (купить) или sell (продать) при появлении некоторых событий. Приложения DSQL используют вызовы API для прослушивания событий как син- хронных, так и асинхронных. В DSQL не существует для них эквивалентов, а уста- новка для интерфейсов приложений сырых составных частей является довольно сложной. Приложение регистрирует интерес в событиях с помощью буфера параметров со- бытий (Events Parameter Buffer, ЕРВ), который заполняется при вызове функции isc_event_biock (). Один ЕРВ может регистрировать не более 15 событий, задавая различные ЕРВ и список событий для каждого вызова. Именованные события долж- ны соответствовать (с учетом регистра) событиям, которые будут отправлены. При- ложения, которым нужно отвечать более чем на 15 событий, могут выполнить не- сколько ВЫЗОВОВ isc’_event_block (). Синхронное прослушивание с помощью API Установка синхронного прослушивания через API похожа на то, что вы должны сде- лать для асинхронной сигнализации за исключением того, что при этом вызывается
Гпава 32. Обработка ошибок и события 709 функция isc_wait_for_event () вместо isc_que_event (). Как и в случае с эквивалентом в ESQL, event wait, выполнение программы приостанавливается на время ожидания. Функция isc_wait_for_event () прослушивает оповещение, которое появится, когда сервер выполнит функцию обратного вызова. Асинхронное прослушивание Прежде чем вы сможете использовать функцию сигнализации API isc_que_event(), вам нужно выполнить функцию обратного вызова на клиенте, которую вызывал бы сервер при посылке события. Названием для такого типа функции является асин- хронный перехват, или AST. Функция AST Функция AST предоставляет некоторую форму глобальных флагов для оповещения приложения, когда к нему обращается сервер. Она обрабатывает список событий сервера в буферах, к которым приложение может иметь доступ при управлении соб- ственной очередью событий. Функция должна получать три аргумента: ♦ копию списка отправленных событий; ♦ длину буфера events_list; ♦ указатель на буфер events_iist. Документ InterBase API Guide2 содержит рекомендации по написанию функций AST. Функция isc_event_biock () принимает в своем параметре isc_caiiback указатель на функцию AST и в своем параметре event_function_arg— указатель на первый аргу- мент AST. Этот аргумент обычно получает значение счетчика событий, когда они изменяются. Когда приложение вызывает функцию isc_que_events О для сообщения о событиях, которые оно будет ожидать, оно передает вместе со списком указатель на функцию обратного вызова AST. Один вызов isc_que_events () может содержать до 15 событий. Приложение вызывает функцию isc_event_counts () для определения того, какое со- бытие произошло. Множество вызовов isc_que_events () может выполняться одновременно в одном процессе клиент-сервер. Приложения отключают режим ожидания при вызове функ- ции isc_cancel_events(). f ПРИМЕЧАНИЕ. Подробности установки блока событий для синхронного прослу- , шивания через isc_event_wait () такие же. События не являются постоянными, _Д как при асинхронной технике isc_que_events (). Синхронная сигнализация не требует внешней функции AST. 2 См. APlGuide.pdf в комплекте документации по InterBase, опубликованной Borland Software Inc.
710 Часть VII. Программирование на сервере Компонентные интерфейсы К счастью, почти для всех из нас фрагменты кодов для реализации событий в клиент- ских приложениях инкапсулированы в классах и компонентах в большинстве инст- рументов разработки приложений, которые поддерживают Firebird. Такие компонен- ты, включающие в себя AST, инкапсулирующие вызовы функций API isc_event* вместе с блоками параметров событий и управление буферами событий на клиент- ской стороне, обычно называются обработчиками сообщений (event alerter). Иногда этот термин в форумах и литературе вызывает путаницу, потому что триггеры и хра- нимые процедуры, вызывающие post_event, также часто называют обработчиками сообщений. Использование POST_EVENT Для использования обработчика сообщений в хранимой процедуре или триггере применяйте следующий синтаксис: POSTJLVENT <пмя~события>; Параметр <имя-событя> может быть или литералом в кавычках, или строковой пере- менной. Он является чувствительным к регистру и может начинаться с цифры. Имена событий ограничиваются 64 символами. При выполнении процедуры этот оператор сообщает о событии менеджеру событий, который сохраняет его в таблице событий. При подтверждении транзакции менеджер событий информирует приложения, ожидающие это событие. Например, следующий оператор посылает событие с именем new_order: POST_EVENT ' new__order1 ; В альтернативном варианте, при использовании переменной для имени события мож- но одним оператором посылать различные события в соответствии с текущим значе- нием СТРОКОВОЙ Переменной (например, event_name). POST_EVENT event_name; t ПРИМЕЧАНИЕ. Хотя post_event и является оператором SQL, его аргумент имя события не должен иметь префикс двоеточия. Триггер или хранимая процедура, которые посылают сообщение, иногда называются обработчиками сообщений3. Следующий скрипт создает триггер, i-оторый посылает событие менеджеру событий, когда любое приложение добавляет в таблицу данные: SET ТЕРМ Л; CREATE TRIGGER POST_NEW_ORDER FOR SALES ACTIVE AFTER INSERT POSITION 0 J He следует путать с компонентами-обработчиками сообщений, которые инкапсулируют на клиентской стороне механизм событий.
Глава 32. Обработка ошибок и события 711 AS BEGIN POST_EVENT 'new_order'; END л SET TERM ; л Триггер или процедура? Оператор post event доступен и в триггерах, и в хранимых процедурах. Как же ре- шить, где лучше его поместить для посылки событий? Эмпирическим правилом является использование триггеров, когда приложениям нужно знать о событиях на уровне строки — одной строки или множества строк, в зависимости от области действия транзакции, — и процедур для сигнализации о таких событиях, которые воздействуют на приложения в целом. Это только общие соображения — часто процедуры имеют область действия на уровне строки, и если заинтересованные клиенты хотят знать, когда произошла кон- кретная операция, событие посылается в такой хранимой процедуре. В этом случае post_event в триггере не будет иметь возможности ничего сообщить приложениям о контексте события. Разработчик может использовать события в процедуре, чтобы установить, какое приложение ответственно за выполнение соответствующей рабо- ты. В другом варианте разработчик может поместить сообщение о событии в триггер, чтобы гарантировать, что конкретное действие DML будет информировать всех, не- зависимо от контекста, в котором оно выполняется. Пора дальше Теперь мы обратимся к безопасности вашего сетевого окружения СУБД. В этой час- ти мы рассмотрим риски и меры безопасности, связанные с выполнением ваших сер- веров баз данных Firebird. Для начала в следующей главе обсуждаются некоторые слабые места в системе защиты операционного окружения и меры, которые вы може- те принять по их устранению.

ЧАСТЬ VIII Безопасность Глава 33. Безопасность в операционной среде Глава 34. Защита сервера Глава 35. Безопасность на уровне базы данных Глава 36. Конфигурация и специальные возможности

ГЛАВА 33 Безопасность в операционной среде В Firebird не существует средств для шифрования и дешифрования данных (кроме паролей пользователей), которые передаются через клиентский интерфейс. Сущест- вуют некоторые ограничения в использовании инструментов Firebird, которые осу- ществляют доступ к базам данных, но, в конечном счете, на уровне программного обеспечения нет защиты от налетчиков, которые установили доступ к вашей базе данных без авторизации. В этой главе на первый план выдвигаются некоторые вопросы, по поводу которых вы должны предпринять меры предосторожности в вашем окружении сервера и клиен- тов Firebird. Не имеет никакого смысла выполнять проектирование для решения всех относящихся к безопасности окружения вопросов, которые могут воздействовать на ваш сервер и вашу сеть. Короче говоря, если безопасность имеет серьезное значение для установки вашей системы, то и отнеситесь к ней серьезно. Исследуйте ее, опре- делите потенциальные зоны рисков и будьте готовы консультироваться со специали- стами. В главах 34 и 35 рассматриваются средства Firebird по управлению доступом на уровнях сервера Firebird и баз данных, соответственно. Глава 36 содержит подробно- сти конфигурирования серверов Firebird для уменьшения незащищенности данных при некоторых рисках для безопасности, связанных с окружающей средой. Физическая безопасность Содержите серверы и чувствительные или критичные клиентские машины в помеще- ниях с хорошо закрываемыми дверями. Если у вас на серверах или рабочих станциях установлена система FAT32, любой пользователь, локально подключившийся к од- ной такой машине, может получить доступ и ко всем другим. Если возможно, забло- кируйте такие ресурсы, как CD-ROM, накопители на гибких магнитных дисках и драйверы Zip, отключите порты, через которые можно получить доступ к устройст- вам первоначальной загрузки или установите такие режимы в BIOS, которые предот- вратят загрузку со съемных устройств. Установите в BIOS защиту по паролю, чтобы предотвратить неавторизованные изменения режимов первоначальной загрузки. Ус- тановите защиту по паролю на все серверы и рабочие станции. Основным фактором физической безопасности является защита чувствительных к безопасности машин от физических контактов неавторизованных лиц. Все остальное не даст никаких результатов, если посторонние могут получить доступ к машине и
716 Часть VIII. Безопасность осуществить кражу, или если кто-то может открыть дверь в эту комнату и украсть накопитель на жестком диске, подключенный к серверу. Использование защищенных файловых систем Пользователям удаленных баз данных (клиентским приложениям) не нужны полно- мочия файловой системы по доступу к базам данных. Им не нужны и соответствую- щие полномочия по использованию внешних приложений для записи и чтения внеш- них файлов данных, которые связаны с таблицами. Вы можете объединить полномо- чия операционной системы и средства конфигурирования на сервере параметра ExternaiFiieAccess (firebird.conf) для уменьшения риска постороннего воздействия. Пользователям встроенных приложений, включая локальное (Классическое) соеди- нение в POSIX, требуются права по доступу к файлу базы данных и другим файлам, таким как файлы блокировок, протоколов, внешних таблиц и т. д. Это же относится и к любой учетной записи того пользователя, который запускает сервер Firebird как приложение. Защитите каталоги Firebird и другие каталоги, к которым обращается Firebird, ис- пользуя максимально возможные ограничения, поддерживаемые вашей операцион- ной системой и выбранной вами файловой системой. Не храните программные файлы Firebird, базы данных, скрипты, оперативные копии или файлы данных внешнего доступа в разделах FAT32. В Windows не позволяйте использовать эти разделы обычным пользователям. Полномочия по использованию разделов NTFS, хранящих связанные с Firebird файлы и программы, должны быть максимально ограничены, насколько это возможно. Использование групповых учетных записей предпочтительнее индивидуальных учетных записей. Где только возможно, исключите членство во множестве групп. Защита оперативных копий Регулярно выполняйте резервное копирование, упаковывайте копии, копируйте на переносимые носители и сохраняйте в защищенном месте. Не оставляйте резервные копии и архивы базы данных в сетевом окружении, где гу- ляющие по сети могут их найти. Сворованная копия файла gbak может быть восста- новлена с полной прозрачностью всех данных на любом другом сервере Firebird. Это означает, что если вы позволите мне украсть копию вашей базы данных или позволи- те получить файл gbak, я смогу восстановить это на моем сервере. Как пользователь SYSDBA, я смогу открыть и просмотреть все, что угодно. Некоторые ложные предположения Пара ложных предположений используется бестолковыми администраторами базы данных, мало беспокоящимися о защите базы данных и файлов резервных копий. ♦ Файл копии разрушен, следовательно, он не может быть использован злоумыш- ленником. Не думайте, что файл копии выполняющейся базы данных будет раз- рушен и недоступен для использования. Незаконная копия прекрасно может быть
Гпава 33. Безопасность в операционной среде 717 использована, особенно если деятельность по изменению данных базы данных выполняется относительно редко, или если атакующий попытается выполнить по- вторные копирования1. ♦ Файлы gbak не являются базами данных, следовательно, не могут быть исполь- зованы злоумышленником. Любой может восстановить базу данных из копии ва- шего файла gbak. Защита, основанная на возможностях платформы Тот уровень защиты, основанный на платформе, который вы можете использовать при инсталляции вашего сервера базы данных, зависит от двух факторов: как хорошо платформа операционной системы и ее файловая система могут защитить вашу сис- тему (программы и данные) и насколько безопасной должна быть ваша система. Вто- рое может быть хорошим основанием для выбора первого. Если у вас есть очень веские причины поступить иначе, вам следует запустить сервер Firebird как сервис. Если возможно, используйте специальную учетную запись пользователя для запуска сервиса Firebird. В Firebird Е5 это реализовано по умолчанию для Linux и некоторых других POSIX-систем. Под Windows и для Firebird 1.0.x вы должны это сделать сами. Ограничение подключения к операционной системе Требуйте пароли при всех подключениях и отменяйте кэширование подключения на серверах и рабочих станциях Windows. В сетях отменяйте возможность для пользо- вателей и групп изменять их собственные установки регистрации. Задавайте строгие пароли. Усиливайте блокировки учетных записей на всех серверах и рабочих станци- ях, которые соединяются с базами данных. Заставьте использовать обычные учетные записи при нормальной работе. Ограничьте подключения root или администратора административными сессиями. Отключите учетные записи "guest", "world" и "everyone". Отслеживайте неверные подключения, ошибки при отключениях, ошибки при досту- пе к объектам файлов и программ, нарушения пользовательских привилегий, не- обычные завершения и перезагрузки системы. POSIX Linux, UNIX и другие платформы POSIX более предпочтительны, чем Windows, если требуется высокая безопасность. Технологии безопасности этих платформ являются 1 Ресурс ibsurgeon.com предлагает не только средства ремонта баз данных, но с августа 2005 года и средство для восстановления поврежденных резервных копий баз данных. — Прим. науч. ред.
718 Часть VIII. Безопасность продуманными и очень понятными в реализации. Безопасность файловой системы и надежный доступ присущи требованиям проектирования, которые определены в об- щих стандартах. Это не означает, что просто инсталляция сервера баз данных на платформе POSIX является гарантией безопасности. Это говорит о том, что тут су- ществуют элементы, необходимые для установки систем безопасности, обеспечи- вающих надежную защиту. Платформы Microsoft Windows Инсталляции сервера Windows являются столь безобразно сложными в плане безо- пасности, что требования высокой защищенности могут просто полностью исклю- чить Windows в качестве платформы для установки серверов базы данных, в которых оценка и отслеживание безопасности на местах отсутствует. В презентации лаборатории, озаглавленной "Укрепление Windows 2000"2, гуру сете- вой безопасности Philip Сох из SystemExperts Corporation начал очерчивать "Четыре шага практической безопасности в Win2K" следующим образом: 1. Локализовать систему Windows. 2. Вставить *nix CD. 3. Перезагрузиться. 4. Следовать подсказкам инсталляции. Эти слова были сказаны с насмешкой, они сопровождались обязательными "улыбоч- ками", но доклад Philip Сох содержит серьезный полезный совет системным админи- страторам, для которых запуск серверов Windows является лишь возможным вариан- том. Он основной автор официальной и очаровательно искренней книги по безопас- ности сервера Windows "Windows 2000 Security Handbook"3. Сама Microsoft публикует официальные издания с подробными практическими инструкциями по реализации безопасности на их серверных платформах и часто выпускает патчи безопасности. Сайт activewin.com является полезным источником связанных описа- ний множества патчей безопасности, которые были созданы и продолжают созда- ваться в различных версиях Windows. Windows NT/2000 и ХР В системах Windows NT/2000 и ХР сервисы доступны удаленным клиентам, даже когда ни один пользователь не соединен с сервером. При выполнении сервис Firebird, как и большинство сервисов Windows, выполняется под учетной записью localsystem. Localsystem является встроенной учетной записью, которая для Windows NT 4 и меньших версий сервера имеет мало возможностей. На более поздних серверных платформах Windows localsystem была наделена чрезмер- ным уровнем привилегий доступа к локальным системным ресурсам, включая при- 2 См. http://security.ucdavis.edu/HardenWin2Klab.pdf. 3 Windows 2000 Security Handbook (Osborne-McGraw Hill, 2000) написана Philip Сох и Tom Sheldon. Биографию Philip Сох см. на http://www.systemexperts.com/bios.html.
Гпава 33. Безопасность в операционной среде 719 вилегии, которые не могут быть предоставлены даже членам группы администра- торов. Приложения же выполняются под обычными учетными записями пользователей и требуют, чтобы пользователь был соединен с сервером. Любой стандартный пользо- ватель Windows может запустить сервер Firebird как приложение. Защита файлов Под Windows строго рекомендуется ограничить размещение каталогов (папок), где располагаются файлы базы данных и компоненты сервера, и защитить к ним доступ. Установите права к объектам и полномочия для совместно используемого ресурса для каждого файла, к которому потенциально могут обращаться сетевые пользовате- ли. Чтобы быть защищенными, каталоги и файлы должны находиться в разделах NTFS, в деревьях, предназначенных для этих целей, и должны быть сделаны читае- мыми только для учетной записи или группы с соответствующими привилегиями. Когда доступ по чтению ограничен таким образом, удаленные клиенты обязаны со- единяться с сервером через протокол TCP/IP, но не через Windows Networking (часто называемый NetBEUI). Дополнительно для Firebird 1.5 и выше вы можете (и должны) ограничить локали- зацию, где сервер может читать файлы базы данных с помощью параметра DatabaseAccess в файле конфигурации firebird.conf. Windows 95/98 и ME Когда безопасность является важной, системы Windows 95/98 и ME не должны рас- сматриваться для использования в качестве хоста для Firebird. Они не имеют под- держки сервисов или безопасности на уровне файлов. Любой человек, имеющий дос- туп к файловой системе, может легко сделать копию базы данных с помощью коман- ды копирования или архивирования файлов или просто использовать возможности GUI (Drag-and-Drop, копировать и вставить и т. д.) для воровства базы данных Firebird. Возможность В Firebird 1.5 конфигурировать ограничение DatabaseAccess в файле firebird.conf (см. главу 36) доступна на этих "не серверных" платформах. По крайней мере, это ограничит размещение каталога, в котором сервер сможет читать файлы базы данных. При этом файловая система FAT32 открыта всему миру и не предос- тавляет никакой защиты от случайного или намеренного перезаписывания баз дан- ных, кодов внешних функций или других файлов, связанных с Firebird. Выполнение произвольного кода Для плохо защищенных систем все текущие версии Firebird предоставляют возмож- ность выполнения произвольного кода с помощью внешних функций, фильтров blob и реализаций пользовательских наборов символов. Такие внешние модули выполня-
720 Часть VIII. Безопасность ются в том же адресном пространстве, что и серверный процесс, и с теми же приви- легиями. Соответственно, важно защитить сервер от возможности доступа внешних файлов и модулей, написанных неавторизованными пользователями. Firebird 1.0.x В Firebird 1.0.x вы можете сконфигурировать указанные каталоги для хранения внешних модулей и внешне отображаемых файлов данных и применить ограничения на уровне операционной системы для предотвращения неавторизованного доступа. Настоятельно рекомендуется использовать такую возможность в файловых системах, которые способны это поддерживать с полномочиями доступа файловой системы. При этом сервер Firebird 1.0.x имеет доступ к внешнему коду и данным в любом мес- те файловой системы, находящейся под управлением хоста. Firebird 1.5 В версии 1.5 и выше размещение внешних выполняемых модулей и других внешних объектов может быть жестко сконфигурировано для гарантии того, что сервер вызо- вет исключение, если получит запрос на доступ к внешним объектам, расположен- ным не в нужном месте. Подробности такого конфигурирования см. в замечаниях относительно внешних объектов в конце главы 36. Особые риски, связанные с сервисами Windows Сервисы, выполняемые под профилем localsystem, рассматриваются как часть на- дежного базового кода (Trusted Code Base, ТСВ) — предполагается, что они имеют такой же уровень надежности, что и сама Windows. Выполнение сервиса Firebird в Windows 2000 и выше несет в себе явный риск в отношении выполнения произволь- ного кода, созданного со злым умыслом. Важность обеспечения программной цело- стности внешних выполняемых модулей Firebird, даже при очень высоких условиях безопасности, является критичной. Короче говоря, программное обеспечение операционной системы Windows не дает надежных гарантий безопасности серверов базы данных в локальной сети или за ее пределами. Потенциально возможные внешние модули должны быть отключены четко сконфигурированным контролем доступа пользователей с помощью LAN и надежными продуктами сетевой защиты сторонних разработчиков для определения и блокирования атак. Встроенный сервер под Windows Библиотека встроенного сервера под Windows, естественно, разработана для работы на машинах, которые не используются как полноценный сервер. Если у вас есть ко- пия библиотеки fbembed.dll, расположенная где-нибудь на полностью серверной ма- шине, безопасность сервера подвергается большому риску. И вот почему.
Гпава 33. Безопасность в операционной среде 721 Встроенный сервер не использует идентификацию сервера для проверки, имеет ли на это права пользователь, подключающийся к базе данных. Большинство интерфейсов приложений требуют имени и пароля пользователя. Подойдет любое имя пользовате- ля и любой пароль — ничего проверяться не будет. Внутренняя безопасность базы данных, разработанная для использования во встроенном сервере, может быть обес- печена разрешениями SQL (см. главу 35), которые ограничивают доступ, разрешая его конкретному пользователю. Это само по себе является дырой в безопасности на отдельно взятой машине, которая физически доступна для любого "прохожего". При этом на машине, где выполняется сервер Firebird 1.5 и установлено программное обеспечение встроенного сервера, хакерская программа может быть запущена под встроенным сервером, когда базы данных закрыты, может соединиться с файлом security.fdb как пользователь SYSDBA, украсть или разрушить пользовательские записи, прочесть зашифрованный пароль и обратиться к другим базам данных, как SYSDBA. Когда приложение соединяется с базами через fbembed.dll, оно блокирует каждую базу данных. Хакерское приложение может сохранять соединения с базами данных — включая security.fdb — бесконечно долго. Приложения встроенного сервера и клиенты выполняются в адресном пространстве операционной системы пользователя. Для исключения риска попадания в систему злонамеренных программ встроенного сервера ограничьте доступ пользователя и группы в файловой системе пространством, где располагаются базы данных и сис- темные файлы Firebird. Безопасность сетевого соединения Множество сообщений между клиентом и сервером несет информацию, которая мо- жет быть довольно легко получена кем-то, подслушивающим сетевые сообщения. Например, зашифрованный пароль может быть получен и использован для неавтори- зованного доступа к серверу. Поэтому необходимо, чтобы все части сетевого пути между клиентом и сервером были под контролем. Можно приобрести дополнительные продукты для зашифрованной передачи данных по сети, чтобы блокировать незащищенные цепи4. Web и другие многозвенные серверные приложения Попытка полагаться на имена пользователей по умолчанию может привести к не- предвиденным результатам, таким как неожиданная передача привилегий владельца 4 Известным продуктом для шифрования сетевого трафика для Windows и Linux является ZeBeDee (www.ibase.ru/devinfo/zebedee.htm). —Прим. науч. ред.
722 Часть VIII. Безопасность базы данных или даже владельца серверного процесса обычным пользователям. Строго рекомендуется, чтобы ваше серверное приложение требовало ввода имени пользователя и пароля до каких-либо обращений к серверному процессу Firebird. Использование выделенных серверов Исключите использование на хост-машине других сервисов, особенно таких уязви- мых, как серверы Web и FTP, которые потенциально принимают безымянные под- ключения. Отключение других сервисов не требуется для запуска Firebird. В Windows ограничьте сетевой доступ к реестру на серверах баз данных. Использование средств межсетевой защиты По очевидным причинам рекомендуется размещение ваших серверных машин под управлением средств межсетевой защиты (firewall). Может быть менее очевидным то, что использование средств межсетевой защиты клиентских процессов также является хорошей идеей. Неконтролируемый пользователь может запустить процесс на надежной клиентской машине для передачи некорректной информации серверу и получить привилегированный доступ к его базам данных. Известно, что клиенты Windows ненадежны. Отражение атак Код Firebird 1.0.x содержит большое количество команд копирования строк, которые не проверяют длину копируемых данных. Некоторые из этих переполнений могут быть доступны для внешнего манипулирования путем передачи больших строк дво- ичных данных в операторах SQL или "проталкивания" произвольного мусора в порт сервера (в настоящий момент 3050). Использование таких функций является общей техникой для попыток переполнения буфера с целью взлома серверов. Такая уязвимость проще реализуется, если серверный и клиентский процессы выпол- няются не в надежной сети и/или без использования адекватной межсетевой защиты. Защищенное программирование может помочь в превентивном отражении атак на вашу систему. Например, проверка длины получаемой строки может оказаться осо- бенно полезной. Пора дальше В следующей главе обсуждается защита, предоставляемая (или не предоставляемая) средствами сервера Firebird безопасности баз данных, вместе с использованием инст- румента gsec для управления списком доступа пользователей на сервере. Глава за- канчивается специальной темой настроек безопасности баз данных для улучшения некоторых аспектов такой встроенной защиты.
ГЛАВА 34 Защита сервера Инсталляция сервера включает базу данных идентификации пользователя для хране- ния описания всех пользователей, которые имеют доступ к серверу Firebird. Чувстви- тельный к регистру пароль должен быть определен для каждого пользователя и дол- жен быть использован для доступа к серверу. Инструментом командной строки для поддержки пользователей баз данных является gsec. Именем базы данных безопасности для Firebird 1.5 и выше является security.fdb. В версии 1.0.x это файл isc4.gdb. Он должен размещаться в корневом каталоге Firebird для всех инсталляций сервера за исключением встроенного сервера для Windows'. Идентификация пользователя требуется, когда удаленный или локальный клиент со- единяется с базой данных Firebird. Единственным исключением является случай, ко- гда клиент соединяется через приложение встроенного сервера в Windows. В этой ситуации база данных потребует имя пользователя, соответствующее разрешениям SQL, определенным для ее объектов. Не будет выполняться никакой идентификации имени/пароля пользователя при соединении встроенного процесса клиент-сервер. Ввод учетных данных пользователя Основное использование gsec — или интерфейса gsec — ввод учетных данных поль- зователя. Интерфейс gsec шифрует пароли перед их сохранением. Не соединяйтесь из пользовательского приложения или из инструмента администратора непосредственно с базой данных безопасности для запуска скрипта с целью "пакетного ввода" пользо- вателей, потому что пароль в этом случае будет сохраняться в виде чистого, неза- шифрованного текста. СОВЕТ. В Firebird существуют сервисы API, которые связываются в gsec вместе с другими инструментами командной строки. Некоторые инструменты админист- ратора сторонних разработчиков предоставляют доступ к gsec для поддержания учетных данных пользователей через сервисы API в более дружественном ин- терфейсе, чем утилиты командной строки. 1 По соображениям безопасности системы не должны поставляться вместе с библиотекой встроенного сервера, доступной для активного сервера Firebird, если только корневой каталог Firebird не защищен жестко от неавторизованных посетителей. Подробности см. ранее в гла- ар -? ?
724 Часть VIII. Безопасность Требуемыми данными являются имя пользователя и пароль. Для имен пользователей и паролей в настоящий момент не поддерживаются международные наборы симво- лов. Только пользователь SYSDBA может обслуживать базу данных безопасности. Это означает, что вновь установленный Firebird не поддерживает изменения пользовате- лями своих собственных паролей. Техники настроек идентификации пользователей на вашем сервере и реализацию этих возможностей см. в разд. "Специальная тема" в конце этой главы. Имя пользователя чувствительно к регистру и должно быть уникальным. В настоя- щий момент оно может содержать только символы, допустимые для идентификато- ров объектов: А—Z (или а—z), цифры и символы !, #, $, &, @. Теоретически имя пользователя может содержать до 128 символов, но вы должны рассматривать его ограниченным 31 символом, потому что более длинное имя не будет верным при ис- пользовании разрешений SQL. ВНИМАНИЕ! gsec позволит вам определять глупые имена пользователей, такие / J \ как строки звездочек или "смайлики" вроде :), однако не прельщайтесь этим! По- —-“Ц. добные строки неприемлемы на месте, где используются имена пользовате- лей — в параметрах соединения или операторах разрешения. Пароль может содержать до 32 символов, однако только первые восемь являются значимыми. Поэтому, например, пароли masterkey и masterkeeper для сервера выгля- дят идентичными. Пароли чувствительны к регистру. Допустимые символы такие же, как и для имени пользователя, но прописные буквы отличаются от строчных. Пароли не обязаны быть уникальными, хотя это и желательно для целей повышения безопас- ности. JF ПРИМЕЧАНИЕ. Некоторые интерфейсы администратора накладывают ограни- чение в восемь символов и не допускают имена пользователей и пароли, которые \ \ начинаются с цифры. Хотя это и не является ограничением системы безопасно- сти баз данных, имеет смысл следовать такой практике. Шифрование пароля Интерфейс gsec шифрует пароли, используя скромный метод, основанный на алго- ритме хэширования DES (Data Encryption Standard, стандарт шифрования данных). По причине восьмисимвольного ограничения идентификация пользователя в Firebird на сегодняшний день не может рассматриваться как "центурион у ворот в современ- ную эпоху"2. 2 Несмотря на это в Firebird используется шифрование с лотерей данных, которое не позво- ляет восстановить исходный пароль путем дешифрования. В Firebird 2.0 вместо DES использу- ется MD5. — Прим. науч. ред.
Глава 34. Защита сервера 725 Все-таки исключите такие очевидные пароли, как password и sesame. Смешивайте регистр, включите цифры и обеспечьте регулярное изменение паролей. СОВЕТ. Поскольку невозможно отыскать потерянные пароли с помощью запро- сов к системе, вам нужна непробиваемая система фиксации паролей пользова- телей при их изменении. Глупая болтовня про отслеживание пользователей не может быть той системой, которую осознанно выбирает организация для средств безопасности! Потеря паролей может быть восстановлена пользователем SYSDBA. Если же будет потерян пароль у SYSDBA, то вся база данных безопас- ности должна быть заменена путем восстановления файла security.fdb, который вы найдете в корневом каталоге Firebird (для Firebird 1.0.x это файл isc4.gdb). Начните работу с паролем masterkey. Учет пользователей в SQL Поскольку пользователи Firebird поддерживаются на уровне сервера, не существует никакого специального SQL-оператора для этого. Причем имена пользователей ис- пользуются в SQL в качестве аргументов операторов grant ... то и revoke ... from. Подробности см. в главе 35. Имя пользователя также доступно во множестве выражений SQL через контекстную переменную current_user и серверный литерал user. Пользователь SYSDBA Новые инсталляции Firebird для Windows устанавливают в базу данных безопасности пользователя SYSDBA с паролем masterkey. Очевидно, что это известно всем, и не может использоваться для безопасности. Пароль должен быть изменен в первую оче- редь. Под Linux некоторые инсталляторы способны генерировать произвольный пароль для SYSDBA. Вы можете увидеть его в текстовом файле SYSDBA.password, разме- щенном в каталоге Firebird /bin. Пользователь SYSDBA является владельцем базы данных безопасности. Помните, что базы данных Firebird имеют "одного пользователя SYSDBA для управления все- ми". Пользователь SYSDBA имеет все деструктивные права ко всем базам данных на сервере. Пароль у SYSDBA не должен распространяться на обычных пользователей. Слабое место POSIX Firebird может допустить соединения клиентов с серверами на платформах POSIX, при которых обходится идентификация пользователя Firebird, а вместо этого исполь- зуется схема разрешений для пользователя операционной системы. Это давно суще- ствующая возможность, наследованная от InterBase. Незнание данного факта остав-
726 Часть VIII. Безопасность ляет большую дыру в безопасности на платформах POSIX, если путь доступа пользо- вателя POS1X будет широко открыт, а системный администратор будет ошибочно предполагать, что база данных безопасности является максимально защищенной. Это не так. Когда пользователи POSIX соединяются с базой данных без передачи серверу Firebird имени пользователя и пароля, подпрограмма идентификации под- ставляет текущие характеристики операционной системы вместо характеристик пользователя Firebird. Если пользователь операционной системы имеет привилегии root, опасайтесь, очень опасайтесь. Поскольку пользователи POSIX могут получить доступ к базам данных Firebird через их учетные данные операционной системы, важно определить надежные взаимосвязи между сервером и каждой клиентской рабочей станцией. Это переводится в записи в /etc/host.equiv или в другие данные, такие как файл .rhost в домашнем каталоге поль- зователя на сервере. СОВЕТ. Читатель также может поэкспериментировать с файлом /etc/gds_host.equiv — см. текст "Installing InterBase to start automatically and run as a service under Linux" (Инсталляция InterBase для автоматического запуска и вы- полнения в качестве сервиса под Linux) у Richard Combs на http://community.borland.eom/article/0,1410,27761,00.html. Переменные окружения isc^user и isc_password должны быть исключены из сис- темы. /А ВНИМАНИЕ! Автору не известны способы определения надежной взаимосвязи / J \ между рабочей станцией Windows и сервером UNIX, но это не означает, что та- -А- кое не может быть сделано. Утилита gsec В Firebird существует утилита gsec в качестве интерфейса командной строки для ра- боты с базой данных безопасности. Она имеет собственную оболочку для интерак- тивного использования, либо команда gsec может быть непосредственно введена в командной строке операционной системы или запущена из исполняемого скрипта (скрипта командной строки или пакетного файла). Любой идентифицированный пользователь может запустить gsec, но только пользо- ватель SYSDBA может изменять сведения о пользователе, хранящиеся в базе данных безопасности. Требуются имя пользователя и пароль, независимо от того, видимы ли из командной строки переменные окружения iscjjser и isc_password. В POSIX, если вы подключились как root, вы можете вызывать gsec без ввода параметров -user и -password.
Гпава 34. Защита сервера 727 Запуск интерактивной сессии gsec В командной строке в каталоге Firebird /bin введите следующее. Для POSIX: ./gsec -user sysdba -password masterkey Для Windows: gsec -user sysdba -password masterkey Подсказка командной строки изменится на gsec>, указывающую, что вы запустили gsec в интерактивном режиме. Для завершения интерактивной сессии введите quit. Запуск gsec как удаленного клиента SYSDBA может использовать gsec на клиентской машине для управления идентифи- кацией пользователей на удаленном сервере. Синтаксис вызова в этом случае не- сколько отличается: он требует переключателя -database, за которым следует полный сетевой путь к базе данных безопасности. Например (это одна команда), здесь пока- зывается, как клиент POSIX обращается к серверу Windows в версии 1.5: ./gsec -database hotchicken:с:\Program Files\Firebird\Firebird_l__5\security. fdb -user sysdba -password masterkey Следующий пример показывает обращение клиента Windows к серверу POSIX в вер- сии 1.0.x: gsec -database coolduck:/opt/firebird/isc4.gdb -user sysdba -password masterkey Интерактивные команды Командами интерактивной утилиты gsec являются отображение, добавление, моди- фикация, удаление, помощь и выход. Они не чувствительны к регистру. ♦ add, modify и delete используются для добавления и удаления пользователей и для изменения паролей. Они требуют в качестве параметра имя пользователя (username) вместе с соответствующими переключателями и аргументами. ♦ display без имени пользователя отображает список всех пользователей. Пароли не показываются. Эта команда также может получать имя пользователя. В этом слу- чае отображаются подробности этого пользователя. ♦ help или его алиас ? отображает текст помощи для утилиты. ♦ quit завершает gsec и закрывает командную строку. Таблица USERSм режимы gsec В табл. 34.1 показаны столбцы таблицы users вместе с соответствующими переклю- чателями gsec. Требуемыми полями при использовании gsec являются только USER__NAME И PASSWD.
728 Часть VIII. Безопасность Таблица 34.1. Столбцы таблицы users и режимы gsec Столбец Описание Режим gsec Аргумент USER—NAME Имя пользователя, распознаваемое иден- тификацией пользователя на сервере, gsec требует его в качестве параметра для инте- рактивных команд добавления, изменения и удаления и для соответствующих переклю- чателей командной строки -a[dd), mo[dify] и-dfelete] username параметр SYS—USER—NAME Не используется GROUP__NAME Не используется UID Для некоторых платформ POSIX идентифи- катор пользователя UNIX. Не требуется -uid integer GID Для некоторых платформ POSIX идентифи- катор группы UNIX. Не требуется -gid integer PASSWD Текущий пароль для этого пользователя. Т ребуется -pw string(10) PRIVILEGE Не используется COMMENT Не используется FIRST_NAME Имя пользователя. Не требуется -fname string(31) MIDDLE_NAME Второе имя пользователя. Не требуется -mnarne string(31) LAST-NAME Фамилия пользователя. Не требуется -Iname string(31) FULL_NAME Нет Также при запуске gsec с удаленной рабочей станции: Сервер и путь к файлу базы данных безо- пасности -database Путь и имя файла Примеры gsec display Отображает основные столбцы таблицы users базы данных безопасности. Пароли никогда не показываются. user—name uid gid full name SYSDBA MICKEY 123 345 Mickey Mouse D_DUCK 124 345 Donald Duck JULIUS 125 345 J. Caesar
Гпава 34. Защита сервера 729 Для отображения той же информации из одной строки таблицы users введите: GSEC> display username Например: GSEC> display julius user_name uid gid full name JULIUS 125 345 J. Caesar a[dd] Добавляет пользователя в таблицу users: a[dd] имя-пользователя -pw пароль [другие переключатели] Здесь имя-пользователя— уникальное новое имя пользователя, а пароль— пароль, связанный с этим пользователем. Jj ПРИМЕЧАНИЕ. Переключателем для нового пароля при добавлении пользова- теля или при изменении его пароля является -pw. Не путайте его с сокращенной _от> формой переключателя пароля при соединении SYSDBA, который задается в виде -ра. Пример: GSEC> add mmouse -pw veritas Для добавления пользователя hpotter с именем Harry Potter и паролем noMuggle вве- дите: GSEC> add hpotter -fname Harry -Iname Potter -pw noMuggle Для проверки новой записи: GSEC> display hpotter user_name uid gid full name HPOTTER Harry Potter ПРИМЕЧАНИЕ. Если вы попытаетесь ввести недопустимые символы в строке пароля, gsec просто завершит работу без выдачи сообщения. mo[dify] Используется для изменения (редактирования) значения столбца в существующей записи users. Задайте имя пользователя, для которого требуются изменения, затем укажите один или более переключателей, определяющие изменяемые элементы, и новое значение для каждого из них.
730 Часть VIII. Безопасность Например, для задания идентификатора пользователя 25 для пользователя mickey, изменения имени (first name) на Michael и изменения пароля на icecream введите сле- дующие переключатели: GSEC> modify mickey -uid 25 -fname Michael -pw icecream Вы не можете изменить имя пользователя. Удалите старого пользователя и добавьте нового. de[lete] Удаляет пользователя с указанным именем из таблицы users. Команда не получает никаких аргументов и переключателей. de[lete] имя-пользователя Пример: GSEC> delete mickey Вы можете использовать команду display, чтобы убедиться в удалении записи. h[elp] или ? Любой из этих переключателей позволяет отобразить общие сведения о командах gsec, переключателях и синтаксисе. qfait] Завершает интерактивную сессию. Использование gsec из командной строки Для использования gsec из командной строки преобразуйте каждую команду gsec в командный переключатель, добавив префикс в виде знака минус (-). Переключатели остаются теми же самыми. Например, для добавления пользователя claudio и назначения ему пароля dbkeycop в командной строке введите следующее. В Windows: ..\BIN> gsec -add claudio -pw dbkeycop -user SYSDBA -password masterkey В POSIX: bin]$ ./gsec -add claudio -pw dbkeycop -user SYSDBA -password masterkey Для отображения содержимого таблицы users введите: > gsec -display И так далее.
Гпава 34. Защита сервера 731 Сообщения об ошибках gsec В табл. 34.2 представлены сообщения об ошибках gsec. Таблица 34.2. Сообщения об ошибках gsec Сообщение Причины и рекомендуемые действия Add record error (Ошибка добавления записи) Неверный синтаксис или вы пытаетесь добавить пользователя, который уже су- ществует, или вы не являетесь пользова- телем SYSDBA. Используйте modify, если пользователь уже существует <string> already specified {<строка> уже была задана) Вы включили переключатель более одного раза в команду add или modify. Введите команду заново Error in switch specifications (Ошибка в задании переключателя) Это сообщение сопровождает другие со- общения об ошибках и указывает на то, что был использован неверный синтаксис. В этом случае просмотрите другие сооб- щения об ошибках Find/delete record error (Ошибка поиска удаляемой записи) Команда удаления не может найти указан- ного пользователя или вы не являетесь пользователем SYSDBA Find/display record error (Ошибка поиска отображаемой записи) Команда модификации не может найти указанного пользователя или вы не являе- тесь пользователем SYSDBA Incompatible switches specified (Заданы несовместимые переключатели) Например, вы ввели множество переклю- чателей для команды удаления, которая требует только одного обязательного ар- гумента имя-пользователя. Скорректируй- те синтаксис и выполните команду заново Invalid parameter, no switch defined (Неверный параметр, не задан переключатель) Вы указали значение без аргумента Invalid switch specified (Задан неверный переключатель) Вы задали нераспознанный переключа- тель. Исправьте и вновь выполните коман- ду No user name specified (Не задано имя пользователя) Задавайте имя пользователя после ко- манд или переключателей добавления, изменения или удаления Record not found for user: <string> (He найдена запись для пользователя: < строка>) Запись указанного пользователя не найде- на. Отобразите список пользователей и вновь введите команду Unable to open database (Невозможно открыть базу данных) База данных безопасности не существует или не может быть локализована на сер- вере. Запустили ли вы gsec вне каталога Firebird? Пытаетесь ли вы получить доступ к удаленному серверу, который не был инсталлирован?
732 Часть VIII. Безопасность Специальная тема: настройка безопасности пользователя Идентификация пользователя Firebird довольно проблематична. Хорошая новость: все изменится к лучшему в Firebird 2. Плохая новость: нам придется с этим жить еще некоторое время. Ivan Prenosil— эксперт, в течение длительного времени занимаю- щийся разработкой баз данных с использованием Firebird и его предшественников — создал несколько техник для настроек безопасности баз данных. Ivan согласился по- делиться с вами своими советами и скриптами в данной специальной теме. База данных безопасности Когда пользователь соединяется с базой данных Firebird, его пароль сравнивается с зашифрованным паролем в базе данных безопасности. В версии 1.0.x имя базы дан- ных isc4.gdb, в версии 1.5 — security.fdb. Это обычная база данных Firebird, таблица, используемая для идентификации пользователей, users. Ее (упрощенная) структура выглядит следующим образом: CREATE TABLE USERS ( USERJIAME VARCHAR (128), PASSWD VARCHAR(32) ); /* V GRANT SELECT ON USERS TO PUBLIC; Для проверки разрешений к этой таблице существует два очевидных препятствия. Одно — ни один пользователь не может прочесть полный список пользователей и зашифрованных паролей. Другое— только пользователь SYSDBA может изменять эту таблицу — пользователи не могут изменять их собственные пароли. При этом, если вы пользователь SYSDBA, вы можете соединиться с базой данных безопасности, как и с любой другой базой данных, и изменять ее структуру. Это оз- начает, что вы можете ее улучшать. ВНИМАНИЕ! Не забудьте сделать копию базы данных безопасности перед тем, как начнете ее изменять! Различие версий При идентификации пользователей в Firebird 1.0.x сервер соединяется с базой данных безопасности, отыскивает необходимую информацию, а затем отсоединяется. Между соединениями у вас могут быть "не очень хорошие отношения" с isc4.gdb. Однако в Firebird 1.5 соединение с базой данных безопасности сохраняется, пока пользователь соединен с любой базой данных на сервере. Поэтому в версии 1.5 вы должны выполнять все изменения с копией базы данных безопасности и позже ее инсталлировать. Нужно выполнить следующие шаги.
Гпава 34. Защита сервера 733 1. Сделайте оперативную копию security.fdb с использованием gbak. 2. Восстановите ее с другим именем. 3. Запустите скрипт для копии. 4. Закройте сервер. 5. Поменяйте местами старую и новую версии security.fdb и переименуйте. 6. Запустите заново сервер. Предоставление пользователям возможности изменять свой собственный пароль Самым простым и наиболее известным способом изменения является предоставление следующей привилегии изменения для пользователя, не являющегося SYSDBA, grant update on users то public и добавление триггера, предотвращающего изменение пользователем, не являющимся SYSDBA, пароля других пользователей. Вот скрипт: /* Copyright Ivan Prenosil 2002-2004 */ CONNECT 'C:\Program Files\Firebird\Firebird_l_5\security.fdb' USER 'SYSDBA' PASSWORD 'masterkey'; CREATE EXCEPTION E_NO_RIGHT 'You have no rights to modify this user.'; /* Вы не имеете прав для изменения этого пользователя */ SET TERM '!; CREATE TRIGGER user_name_bu FOR USERS BEFORE UPDATE AS BEGIN IF (NOT (USER = 'SYSDBA' OR USER = OLD.USER_NAME)) THEN EXCEPTION E_NO_RIGHT; END ! ! SET TERM ;!! /** Grants. “/ GRANT UPDATE (PASSWD, GROUP_NAME, UID, GID, FIRST_NAME, MIDDLE_NAME, LAST_NAME) ON USERS TO PUBLIC; Довольно неуклюже предоставлять доступ ко всем столбцам, когда вам реально ну- жен доступ только к passwd. К сожалению, это необходимо, если ваше приложение собирается использовать gsec или сервис API. Такая модификация не устраняет проблему видимости для public полного списка пользователей и их зашифрованных паролей. Это позволяет пользователям очень просто получить список паролей других пользователей и постараться сломать их ло- кально посредством грубой силы3. 3 Как уже говорилось ранее, шифрование пароля производится с потерей данных. Единст- венный способ, которым можно "взломать" пароль, — это перебор словаря, шифруемого тем же алгоритмом, что используется в Firebird. В данном случае время подбора существенно со- кращается тем, что шифруемый пароль имеет длину не более 8 символов. — Прим. науч. ред.
734 Часть VIII. Безопасность Как спрятать список пользователей/паролей Если вы переименуете таблицу users и заново создадите users как просмотр пере- именованной таблицы, вы можете получить лучший из миров. У пользователей будет возможность изменять свои собственные пароли, а полный список пользователей и паролей будет спрятан для public. Каждый пользователь, не являющийся SYSDBA, будет видеть только одну запись из security.fdb (или isc4.gdb, если у вас сервер вер- сии 1.0.x). Новые структуры в security.fdb будут похожи на следующую схему: /* Copyright Ivan Prenosil 2002-2004 */ CONNECT 'C:\Program Files\Firebird\Firebird_JL_5\security.fdb' USER 'SYSDBA' PASSWORD 'masterkey'; /** Переименование существующей таблицы USERS в USERS2. **/ CREATE TABLE USERS2 ( USER_NAME USER_NAME, SYS__USER_NAME USER_NAME, GROUP_NAME USER_NAME, UID UID, GID GID, PASSWD PASSWD, PRIVILEGE PRIVILEGE, COMMENT COMMENT, FIRST_NAME NAME_PART, MIDDLE_NAME NAME_PART, LAST-NAME NAME-PART, FULL_NAME COMPUTED BY (first_name | | -UNICODE_FSS ’ ' I I middle_naine | | _UNICODE_FSS '’ll last__name ) ) ; COMMIT; INSERT INTO USERS2 (USER_NAME, SYS—USER—NAME, GROUP_NAME, UID, GID, PASSWD, PRIVILEGE, COMMENT, FIRST_NAME, MIDDLE_NAME, LAST_NAME) SELECT USER_NAME, SYS—USER—NAME, GROUP—NAME, UID, GID, PASSWD, PRIVILEGE, COMMENT, FIRST_NAME, MIDDLE_NAME, LAST_NAME FROM USERS; COMMIT; /* */ DROP TABLE USERS; /* */ CREATE UNIQUE INDEX USER-NAME_INDEX2 ON USERS2(USER-NAME); /** Создание просмотра, который будет использован вместо первоначальной таблицы USERS. **/ CREATE VIEW USERS AS SELECT * FROM USERS2 WHERE USER = ''
Гпава 34. Защита сервера 735 OR USER = 'SYSDBA' OR USER = USER__NAME; /** Полномочия **/ GRANT SELECT ON USERS TO PUBLIC; GRANT UPDATE (PASSWD, GROUP_NAME, UID, GID, FIRST_NAME, MIDDLE_NAME, LAST_NAME) ON USERS TO PUBLIC; Реальная таблица usersg видима только для SYSDBA. Следующее условие USER = USER_NAME гарантирует, что каждый пользователь видит только свою собственную запись. Ус- ловие USER = 'SYSDBA' гарантирует, что SYSDBA может видеть все записи. Условие USER = ’ ' является важным, потому что переменные user и currenT-User содержат пустые стро- ки в процессе проверки пароля. ПРИМЕЧАНИЕ. К сожалению, две следующие техники не могут быть реализова- ны в сервере Firebird 1.5. Они используют запись в файлы протоколов. Улучше- \,__\ ния безопасности, выполненные в версии 1.5, означают, что подпрограмма иден- тификации пользователя теперь выполняется в транзакции только для чтения, следовательно, невозможно выполнять запись в протокол! Похожая схема может быть реализована путем делегирования функции протоколирования внешним функциям. Как запротоколировать попытки соединения с базой данных Замена таблицы users в базе данных безопасности на просмотр users имеет одно большое преимущество: она позволяет нам вызывать хранимую процедуру, когда пользователь пытается соединиться с базой данных. Когда сервер Firebird выполняет оператор SELECT PASSWD FROM USERS WHERE USER-NAME = ?; мы можем выполнить процедуру, которая действует как триггер выбора или триггер соединения. Эта процедура может быть написана таким образом, что она, например, будет отвергать подключения в некоторые часы дня или протоколировать время, ко- гда пользователи пытаются подключиться к базе данных.
736 Часть VIII. Безопасность Мы можем протоколировать только имена известных пользователей — т. е. только тех пользователей, имена которых уже хранятся в таблице users — и не существует возможности сообщить, было ли подключение успешным. Однако даже такая огра- ниченная информация, которую мы можем внести в протокол, является полезной. Это может проинформировать нас о попытках подключения в необычное время, что позволит обнаружить множество подозрительных подключений за короткий проме- жуток времени. Для подобной реализации нам нужна таблица для протокола и хранимая процедура для выполнения работы. Таблица протокола Таблица протокола должна быть внешней таблицей, потому что транзакция, исполь- зуемая сервером для идентификации пользователей, не подтверждается, а откатыва- ется. Для внешних таблиц не выполняется отмена записанных данных при откате транзакции, как это происходит при добавлении данных в обычную таблицу. CREATE TABLE,log-table EXTERNAL FILE 'C:\Program Files\Firebird\Firebird_l_5\security.log' ( tstamp TIMESTAMP, uname CHAR(31) ); Если вы хотите, чтобы таблица протокола была читаема как текстовый файл, вы должны использовать char(20) вместо timestamp, выполняя необходимые преобразо- вания, и добавить еще два столбца: одиночный char для разделения столбцов времени (tstamp) и имени (uname) и char(2) для заполнения кодами возврата каретки и перевода строки. Процедура подключения Хранимая процедура имеет тип процедуры выбора, которую можно вызывать из про- смотра. Если соединение успешное, происходит обращение к suspend, и строка запи- сывается в протокол. Если соединение ошибочное по той причине, что запрашивае- мая из просмотра строка запрещена, то процедура просто завершается, не записав ничего в протокол. Выходной параметр является простой формальностью, его значе- ние игнорируется. CREATE PROCEDURE log__proc (un VARCHAROI) ) RETURNS (x CHAR(l) ) AS BEGIN IF (USER = '') THEN INSERT INTO log_table (TSTAMP, UNAME) VALUES ( CURRENT_TIMESTAMP, :un);
Гпава 34. Защита сервера 737 /★ и не забудьте изменить записанные поля, если вы изменили таблицу log_table, чтобы сделать ее текстовым файлом! */ IF (USER = ’’ OR USER = ’SYSDBA1 OR USER - :un) THEN SUSPEND; END Мы проверяем (user = "), потому что, когда Firebird проверяет пароль, переменная user пустая. Это помогает отличить, когда сервер проверяет пароль, а когда пользо- ватель напрямую соединяется с базой данных безопасности. Реализация новой установки Нам нужно удалить просмотр, который использовался в нашей реструктурированной базе данных безопасности, и создать новую версию, которая вызывает хранимую процедуру: CREATE VIEW USERS (USER_NAME) AS SELECT * FROM users2 WHERE EXISTS (SELECT * FROM logjgroc(users2.user_name)); He забудьте восстановить полномочия, которые были потеряны при удалении просмотра: GRANT SELECT ON USERS TO PUBLIC; /* */ GRANT UPDATE (PASSWD, GROUPJSIAME, UID, GID, FIRST_NAME, MIDDLE^NAME, LASTJSIAME) ON USERS TO PUBLIC; Нужно добавить несколько новых полномочий, связанных с хранимой процедурой: GRANT INSERT ON log_table TO PROCEDURE log_proc; GRANT EXECUTE ON PROCEDURE log__proc TO PUBLIC; Поскольку записи протокола постоянно добавляются в iog_table, будет нужно уда- лять или переименовывать внешний файл время от времени. База данных безопасно- сти и, следовательно, внешний файл освобождаются после завершения идентифика- ции пользователя, так что переименование файла не должно вызвать проблем. Как остановить злоумышленников Поскольку мы способны протоколировать имена пользователей, которые пытались подключиться к базе данных, и время этих попыток, мы можем использовать эту ин- формацию для дальнейшего ограничения доступа. Например, можно подсчитать количество попыток подключений конкретного поль- зователя в течение последней минуты и, если это количество достигает некоторого 24 Зак. 420
738 Часть VIII. Безопасность предела, блокировать соединение для данного имени пользователя. Это позволит нам установить некоторую защиту против грубых атак, когда кто-то пытается проникнуть в базу данных, многократно сканируя возможные пароли. Мы можем установить временную блокировку для любого имени пользователя, чье поведение похоже на атаку. Интервал времени и ограничение на количество подключений должны выбираться аккуратно, чтобы остановить злоумышленников, а не наказать добросовестного поль- зователя, который просто плохо набирает текст. Open VMS использует похожий под- ход. Соответствующий фрагмент кода хранимой процедуры выглядит следующим обра- зом: DECLARE VARIABLE Cnt INTEGER; SELECT COUNT(*) FROM log_table WHERE uname = :un AND tstamp > CURRENT_TIMESTAMP - 0.0007 INTO :cnt; IF (cnt >= 3) THEN EXIT; Вы можете изменять константы, а именно 3 (допустимое количество ошибок) и 0.0007 (интервал, приблизительно равный одной минуте). Эта процедура действует для всех пользователей. Одной из возможных модификаций может быть выбор одного пользователя, не яв- ляющегося SYSDBA (не SYSDBA, потому что пользователь SYSDBA, скорее всего, и будет выбран для атак), и исключение его из блокирующей процедуры. Сделайте этого пользователя владельцем всех баз данных, наделив его правами на выполнение останова базы данных. Пора дальше Ясно, что безопасность на уровне сервера в Firebird имеет недостатки. В следующей главе обсуждается поддержка в Firebird полномочий SQL, которые могут быть реали- зованы для всех баз данных. Применение полномочий SQL запутанно, однако при аккуратном использовании они предоставляют основной внутренний уровень безо- пасности данных баз данных в системах, которые адекватно защищены от краж.
ГЛАВА 35 Безопасность на уровне базы данных Безопасность на уровне базы данных в Firebird решает две задачи: во-первых, не раз- решить доступ пользователей, идентифицированных на сервере, к данным в вашей базе данных, и, во-вторых, предоставить доступ пользователям, которые работают с вашей базой данных. Средствами реализации такой безопасности на уровне базы данных являются привилегии SQL. Первая цель, блокирование ненужных пользователей, авторизованных на сервере, используется в средах, где базы данных, которыми владеют многие объекты, запуще- ны на одном сервере — обычно, на сайте, где выполняются сервисы для множества заказчиков. В подобных ситуациях заказчики провайдера обычно не имеют доступа SYSDBA. Вторая цель требует большой работы по ограничению доступа к конфиденциальным или чувствительным данным. Привилегии SQL могут поддерживать любой уровень детализации доступа к любому элементу данных вплоть до уровня столбца. В отличие от пользователей (как обсуждалось в главе 34) привилегии применимы к уровню базы данных и хранятся непосредственно в самой базе данных в системной таблице RDB$USER_PRIVILEGES. Безопасность и доступ по умолчанию База данных и все ее объекты (таблицы, просмотры и хранимые процедуры) стано- вятся защищенными от неавторизованного доступа в момент их создания. То есть доступ является "необязательным". Ни один пользователь не может получить доступ к любому объекту базы данных, не получив на это полномочий. За исключением пользователей с особыми привилегиями— владелец, SYSDBA и (в POS1X) Супер- пользователь — пользователи должны иметь привилегии SQL к любой операции, даже к select. А теперь плохие новости Существует большая загвоздка, так что не будьте так легко внушаемы ложным чув- ством безопасности. Несуществующие объекты не являются защищенными. Любой пользователь, имеющий доступ к базе данных, может создать любой допустимый объект базы данных — включая объявления внешних функций и таблиц, связанных
740 Часть VIII. Безопасность с внешними таблицами — которые потенциально могут быть использованы в комби- нации с установкой и выполнением хакерского кода на сервере. С возможностью при инсталляции Firebird 1.5 ограничивать местоположение внеш- них объектов, к которым может обращаться сервер, ситуация несколько улучшается. При этом риски не устраняются полностью— вы должны выполнить конкретные шаги по реализации этой возможности и созданию ограничений файловой системы операционной системы. Доступ по умолчанию к внешним файлам в инсталляторе установлен в none, и каталоги внешних функций ограничены деревом UDF. Ваша за- дача — позаботиться о системных ограничениях. Системные таблицы, где Firebird хранит все метаданные, включая сами привилегии SQL, вовсе не защищены привилегиями SQL. Совет, как наказать идиотов-пользователей и плохих парней ______________________________________________________ Мой уважаемый коллега Павел Цизар (Pavel Cisar) предложил прием устранения недостатка привилегий SQL по защите метаданных Firebird, когда идиоты- пользователи, минуя DDL, пытаются изменять метаданные в системных таблицах, внося в них беспорядок. Это также отражает злонамеренные попытки разрушить ваши метаданные с помощью скрипта. Вот данное решение. Хотя кажется, что public (т. е. все) имеют доступ all к системным таблицам, тем не менее существует таинственный черный ход в управлении правами SQL, который может легко исправить ситуацию. Вы можете ограничить доступ к системным табли- цам, так же, как и к любым другим таблицам базы данных, предоставив, а затем от- менив полномочия. При этом права предоставляются только для того, чтобы быть отмененными. Все, что нужно сделать, — это просто установить управление доступом к системным Таблицам Путем выполнения Серии операторов GRANT ALL <системная таблица> то public. После этого мы можем отменить любые права. Я не знаю точного технического объяснения, каким именно образом это работает, но это мой совет. Оператор grant создает управляющий список доступа (Access Control List, ACL) для системной таблицы, который проверяется в коде. Какая-то ветвь в этом коде означает "предоставить всем все привилегии, если элемент ACL не най- ден для системной таблицы, иначе использовать ACL". Следует помнить некоторые вещи. • Эта установка не сохраняется после восстановления базы данных из резервной копии, следовательно, напишите скрипт, который вы будете использовать каж- дый раз для только что восстановленной базы данных. • Вам нужно быть внимательным по поводу удаления прав select у public к неко- торым системным таблицам, потому что функции API isc_blob_lookup_desc () и isc_array_iookup_bounds () зависят от этой возможности. От этого также могут зависеть некоторые инструментальные средства и библиотеки. • Удаление прав записи в системные таблицы не ограничивает возможность их изменения обычным нормальным способом с использованием команд DDL. Предотвращается только бесполезная прямая корректировка системных таблиц. Привилегия дает возможность пользователю иметь некий вид доступа к объекту в базе данных. Она предоставляется с помощью оператора grant и убирается с исполь- зованием оператора revoke.
Гпава 35. Безопасность на уровне базы данных 741 Синтаксис разрешения доступа: GRANT <привиления> ON <объект> ТО <пользователь>; привилегия, предоставленная пользователю к объекту, создает разрешение. Синтаксис для удаления разрешений: REVOKE <привиления> ON <объект> FROM <полъзователв>; Доступны некоторые варианты ''центрального” синтаксиса. Мы рассмотрим их не- сколько позже. Привилегии Привилегия представляет разрешение выполнять операцию DML. В табл. 35.1 опи- сываются привилегии SQL, которые могут быть предоставлены или удалены. Таблица 35.1. Привилегии SQL Привилегия Доступ SELECT Чтение данных INSERT Создание новых строк UPDATE Изменение существующих данных DELETE Удаление строк REFERENCES Ссылка на первичный ключ из внешнего ключа. Это всегда необходимо де- лать при предоставлении привилегий к таблицам, содержащим внешние клю- чи ALL Выборка, добавление, изменение, удаление и ссылка на первичный ключ из внешнего ключа EXECUTE Выполнение хранимой процедуры или вызов ее с использованием select. Эта привилегия никогда не предоставляется как часть привилегии all (см. разд. "Ключевое слово ALL") ROLE Предоставляет все привилегии, назначенные роли. Если роль существует и имеет назначенные ей привилегии, она становится привилегией, которая может явно назначаться пользователям. Роль никогда не предоставляется как часть привилегии all Упаковка привилегий SQL Firebird реализует возможности упаковки множества привилегий для назначения индивидуальным получателям, спискам или специально сгруппированным пользова- телям. Это пакет all, разделенные запятыми списки и роли SQL.
742 Часть VIII. Безопасность Ключевое слово ALL Ключевое слово all упаковывает привилегии select, insert, update, delete и references в одном назначении. Роли и привилегия execute не включены в пакет all. Списки привилегий Привилегии select, insert, update, delete и references также могут быть предоставле- ны или отменены поодиночке или в списках, разделенных запятыми. При этом в опе- раторах, где предоставляются или отменяются привилегия execute или привилегия роли, нельзя предоставлять или отменять другие привилегии. Роли Роль создается в базе данных и доступна только в этой базе данных. Думайте о роли, как о емкости для набора привилегий. Когда емкость "заполнена" некоторыми при- вилегиями, она становится доступна — как привилегия — некоторым типам пользо- вателей. Может быть создано множество ролей. Здесь основная мысль в том, чтобы упаковать и управлять дискретными наборами привилегий, которые могут назначаться и отме- няться как одно целое, вместо того, чтобы постоянно назначать и отменять большие количества индивидуальных привилегий. Роль никогда не может быть предоставлена как часть пакета all, хотя роли могут быть назначены привилегии all. Роли не являются группами Роли не похожи на пользовательские группы операционной системы. Пользователю Firebird может быть назначено более одной роли, однако он может подключиться к базе данных за один раз только с одной ролью. Группы UNIX Firebird поддерживает группы UNIX на платформах POSIX. Если реализована иден- тификация на уровне системы, вы можете предоставлять привилегии группам UNIX. См. вариант то group <итх-группа> в операторах grant и revoke. Объекты "Другой частью" полномочий является объект, к которому применяется привилегия или для которого она отменяется. Объектом может быть таблица, просмотр, храни- мая процедура или роль, хотя не все привилегии применимы ко всем типам объектов. Например, привилегия update неприменима к процедуре, а привилегия execute — к таблице или просмотру. Не существует "пакета объектов", который включает все или группу объектов. Дол- жен быть, по крайней мере, один оператор grant для каждого объекта базы данных.
Глава 35. Безопасность на уровне базы данных 743 Ограничения привилегий Привилегии select, insert, update и delete применимы только к объектам, являю- щимся таблицами или просмотрами, references применяется только к таблицам — точнее к тем, на которые ссылаются внешние ключи. В случае просмотров пользователь должен иметь привилегии к самому просмотру, однако и полномочия к базовым таблицам должны быть предоставлены так или ина- че. Правило гласит, что для владельца просмотра, для самого просмотра или для пользователя просмотра должны быть предоставлены соответствующие привилегии к базовым таблицам. Не имеет значения, как были получены привилегии, однако од- но из трех должно быть выполнено. Естественно изменяемые просмотры также требуют полномочий select, insert, update и delete к базовым таблицам. Если просмотры только для чтения становятся изменяемыми с помощью триггеров, то триггерам нужны полномочия к базовым таблицам в соответствии с операциями, определенными в событиях триггера. С ПРИМЕЧАНИЕ. Для создания просмотра необходимо иметь полномочия select к базовым таблицам. В тех редких случаях, когда любая из этих.привилегий select отменяется после создания просмотра, она должна быть добавлена для самого просмотра или для пользователей этого просмотра. execute может применяться только для хранимых процедур. Роли никогда не предос- тавляются "для" какого-либо объекта. Пользователи Пользователи являются получателями полномочий; они теряют их, когда полномочия отменяются. Пользователь может быть пользователем, определенным в базе данных безопасности, учетной записью или группой UNIX, специальным пользователем или объектом базы данных. ♦ Роли в качестве пользователей: когда роли назначены привилегии, она становит- ся пользователем. Когда роль получает требуемые привилегии к объектам, она изменяет свой статус и становится привилегией, которая может быть предостав- лена некоторым другим типам пользователей. Когда происходит подключение к базе данных под этой ролью, такие пользователи получают все привилегии, кото- рые были предоставлены этой роли. ♦ Просмотры в качестве пользователей: просмотрам нужны полномочия для дос- тупа к таблицам, другим просмотрам и к хранимым процедурам. ♦ Процедуры и триггеры в качестве пользователей: хранимой процедуре, которая обращается к таблицам и просмотрам и выполняет другие процедуры, нужны полномочия к этим объектам. Триггеру, который выполняет процедуры, нужны к ним полномочия, а также к любым таблицам и просмотрам, к которым он обра- щается, отличным от той таблицы, которой он принадлежит.
744 Часть VIII. Безопасность Специальные пользователи Пользователь SYSDBA имеет особые права ко всем базам данных и их объектам, независимо от того, какой пользователь ими владеет’. Более того, в операционных системах, где реализована концепция Суперпользователя, — пользователь с привиле- гиями root или locksmith, — такой пользователь также имеет полный доступ и дест- руктивные права ко всем базам данных и их объектам, если он соединяется под этим идентификатором. Подробности см. в разд. "Слабое место POSIX" главы 34. Вначале создатель объекта, его владелец, является единственным пользователем, кроме SYSDBA и Суперпользователя, который имеет доступ к этому объекту (табли- це, просмотру, хранимой процедуре или роли) и может позволять получать к нему доступ другим пользователям. Любой пользователь затем может запустить "цепочку" полномочий, предоставляя другим пользователям права назначать привилегии. Такое право может передаваться добавлением предложения with grant option к полномо- чиям. Похожим образом SYSDBA или владелец роли может уточнять привилегию роли, передавая ее пользователю как with admin option. При этом предполагается, что пользователь, который подключается с этой ролью, не наследует от роли права with grant option. Подробности этого далее. Пользователь PUBLIC PUBLIC является пользователем, который обозначает всех пользователей в базе дан- ных безопасности. Он не включает в себя хранимые процедуры, триггеры, просмот- ры или роли. Если множество баз данных выполняется на одном сервере, предоставление больших пакетов привилегий может сохранить много времени на набор текста, однако они же могут легко по ошибке предоставить привилегии пользователям, которые не должны их иметь. Встроенные серверы Настоятельно рекомендуется, чтобы базы данных, предназначенные для использова- ния во встроенном сервере, были жестко защищены полномочиями. Пользователь встроенного сервера под Windows намеренно не идентифицируется, следовательно, не существует базы данных безопасности! Коль скоро не выполняется проверка пользователей по операторам grant, то операторы полномочий могут быть примене- ны к "фальшивому пользователю" (т. е. вымышленное имя, которое использует ваше приложение для соединения с базой данных через встроенный сервер). 1 Сервер просто не проверяет никакие права доступа для пользователя SYSDBA. В частно- сти, поэтому операции над множеством объектов в SQL производятся от имени SYSDBA чуть быстрее, чем от имени других пользователей. — Прим. науч. ред.
Глава 35. Безопасность на уровне базы данных 745 Предоставление привилегий Привилегии доступа могут быть предоставлены к целой таблице или просмотру. Можно также ограничить привилегии update и references указанными столбцами. Оператор grant используется для предоставления пользователю, роли или хранимой процедуре конкретной привилегии к объекту. Общий синтаксис для предоставления привилегий к объектам: GRANT <привилегии> ON [TABLE] <таблица> | <просмотр> | <обгъект> | <опустить предложение ON> ТО <типичный-пользователь> [(WITH GRANT OPTION} | {WITH ADMIN OPTION}]; <привилегии> = <привилегия> | <список-привилегий> | <имя~роли> [ ALL <привилегия> = INSERT | DELETE | UPDATE [(столбец [, столбец [,..]] ) ] I REFERENCES [(столбец [, столбец [,..]] ) ] I EXECUTE <список~привилегий> = [, привилегия [, < список -привилегий^ [,...]]] Обратите внимание, что синтаксис для привилегии включает возможность ограниче- ния update или references отдельными столбцами, как обсуждается в следующем раз- деле. <объект> = <хранимая~процедура> I <роль-с-привилегиями> <типичный-пользователь> ~ <пользователь> ( PUBLIC I <список-пользова телей> | < UNIX-пользова тель> I GROUP <UNIX-ipynna> | <пользователь~обьект> <список-пользователей> = <пользователь>, {<пользователь> | <список-пользователей} <пользователь-объект> - <роль> | <триггер> | <хранимая-процедура> Здесь пользователь— обычно пользователь, определенный в таблице users в базе данных безопасности Firebird. Во всех сетях клиент-сервер в POSIX это также может быть учетная запись пользователя, которая находится в /etc/password на серверной и клиентской машине, или группа UNIX, находящаяся на обеих машинах в /etc/group. Для баз данных, используемых во встроенном сервере под Windows, допустим "фальшивый пользователь" (известный приложению). Следующий оператор предоставляет некоторые привилегии к таблице departments пользователю chalky: GRANT SELECT, UPDATE, INSERT, DELETE ON DEPARTMENTS TO CHALKY; Права UPDATE к столбцам Привилегия update, будучи неизмененной, позволяет пользователю изменять любой столбец таблицы. Однако если вы зададите список столбцов, разделенных запятыми, пользователь будет ограничен в изменении только указанными столбцами.
746 Часть VIII. Безопасность В следующем операторе все пользователи будут иметь полномочия на изменение для таблицы customer, однако они смогут изменять только столбцы contact_first, CONTACT_LAST И PHONE_NO: GRANT UPDATE (CONTACT__FIRST, CONTACT_LAST, PHONE_NO) ON CUSTOMER TO PUBLIC; ♦ Когда при предоставлении привилегии update используется список столбцов, множество полномочий сохраняется в системной таблице rdb$user_privileges, по одному для каждого столбца. Права могут предоставляться или отменяться для каждого столбца индивидуально2. ♦ Когда не используются полномочия на уровне столбца, создается только одно разрешение. Нет способа удалить права к одним столбцам и сохранить для дру- гих. Нужно будет отменить разрешения, содержащие права, которые вы хотите отменить, и добавить новые с исправленными правами. Спасибо правилам привилегий SQL — "самая забавная штука от Marx Brothers", по словам коллеги — можно предоставлять как на уровне столбца, так и на уровне таб- лицы привилегии update и references одному и тому же пользователю. Это может привести к путанице, если права пользователя на уровне столбца update или references отменяются, а те же полномочия пользователя на уровне таблицы сохра- няются. Просмотры предоставляют элегантный способ ограничения доступа к таблицам, ог- раничивая столбцы и/или строки, которые видимы пользователю, позволяя при этом выполнить любые настройки. Эта тема обсуждалась в главе 24. Права REFERENCES к столбцам Привилегия references является необходимым дополнением при предоставлении полномочий к таблице, у которой есть внешний ключ. Она нужна, если пользователь, создающий внешний ключ в таблице, не владеет таблицей, на которую ссылается этот ключ. references предоставляет полномочия к столбцам. Должны быть включены все столбцы, на которые ссылается внешний ключ таблицы, к которой предоставляются права. Если оператор grant references ссылается на таблицу без указания столбцов, то полномочия предоставляются к каждому столбцу. Столбцы, которые не указаны в связи между внешним ключом и первичным ключом главной таблицы, не включают- ся в привилегию. Вы можете задать только ключевые столбцы и, возможно, сохранить некоторую из- быточность, если первичная таблица содержит очень много столбцов. Если вы сде- 2 Все версии InterBase и Firebird имеют весьма своеобразное поведение: если выдан grant на update к столбцам явно, то успешно может быть выполнен только update всей таблицы, без указания ограничения where. Таким образом, выдача grant на update к конкретным столбцам является совершенно бесполезной. —Прим. науч. ред.
Глава 35. Безопасность на уровне базы данных 747 лаете так, вы должны задать все связанные ключевые столбцы. Упрощенный синтак- сис выглядит так: GRANT REFERENCES ON <первичная~таблица> [ (<ключевой-столбец> [, <ключевой~столбец> [, . . . ] ] ) ] ТО < пользователе [WITH GRANT OPTION] ; Следующий пример предоставляет привилегии references к таблице departments пользователю chalky, позволяя chalky записывать внешний ключ, который ссылается на первичный ключ таблицы departments, даже если он не владеет этой таблицей: GRANT REFERENCES ON DEPARTMENTS (DEPT_NO) TO CHALKY; СОВЕТ. Если видимость ключей не является проблемой, предоставьте привиле- гии REFERENCES ДЛЯ PUBLIC. Привилегии к объектам Когда для триггера, хранимой процедуры или просмотра нужен доступ к таблице или просмотру, достаточно, чтобы владелец объекта, к которому требуется доступ, сам объект или пользователь, использующий триггер, процедуру или просмотр, имел не- обходимые полномочия. С другой стороны, привилегии к таблице могут быть предоставлены процедуре, а не индивидуальным пользователям для повышения безопасности. Пользователю нужна только привилегия execute к процедуре, которая осуществляет доступ к таблице. Хранимой процедуре, просмотру или триггеру иногда нужны привилегии для досту- па к таблице или просмотру, которые имеют другого владельца. Для предоставления привилегий триггеру или хранимой процедуре включите соответствующее ключевое слово trigger или procedure перед именем модуля. В следующем примере процедуре count_chickens предоставляются полномочия insert к таблице proj_dept_budget: GRANTINSERTON PROJ_DEPT_BUDGETTO PROCEDURE COUNT_CHICKENS; Предоставление привилегии EXECUTE Для использования хранимой процедуры пользователям, триггерам или другим хра- нимым процедурам нужна к ней привилегия execute. Если просмотр выбирает вы- ходные поля из хранимой процедуры выбора, просмотр должен иметь привилегию execute, а не привилегию select. Упрощенный синтаксис выглядит следующим образом: GRANT EXECUTE ON PROCEDURE <имя~процедурь!> TO <получатель>;
748 Часть VIII. Безопасность <получатель> = [ PROCEDURE <имя-процедуры> {, < имя-процедура [, ..]]] [ TRIGGER <имя~триггера> (, <имя-триггера> [, ...]]] [ VIEW <имя~просмотра> [, <имя-просмотра> [, ...]]] | <имя-роли> | <пользователь-или-список> | PUBLIC [WITH GRANT OPTION]; Хранимой процедуре или триггеру нужна привилегия execute к хранимой процедуре, если она имеет другого владельца. Помните, что владельцем триггера является поль- зователь, создавший таблицу. Если ваш оператор grant execute предоставляет привилегии для public, то никакие другие типы получателей привилегий не могут быть представлены в качестве аргу- ментов то. В следующем примере оператор grant execute предоставляет привилегию к про- цедуре calculate_beans двум обычным пользователям flatfoot и kilroy и двум хра- нимым процедурам, чьи владельцы не являются владельцами calculate_beans: GRANT EXECUTE ON PROCEDURE CALCULATE_BEANS TO FLATFOOT, KILROY, PROCEDURE DO_STUFF, ABANDON_OLD; Привилегии к просмотрам Привилегии к просмотрам являются довольно запутанными. Владелец просмотра должен предоставить пользователям привилегию select, как это сделал бы владелец таблицы. Сложности начинаются, если просмотр является изменяемым — естествен- ным образом или через триггеры просмотра— или если просмотр включает другие просмотры или хранимые процедуры выбора. Изменения данных изменяемого про- смотра фактически выполняют изменения в базовых таблицах. Если владельцы базо- вых объектов еще не предоставили пользователю соответствующих прав (insert, update, delete, execute) к базовым таблицам и объектам, а также к хранимым про- цедурам выбора или к просмотрам, то пользователю нужно их получить от владельца просмотра. Привилегии references неприменимы к просмотрам за исключением одной (обычно опускаемой) ситуации. Если просмотр использует таблицу, которая имеет внешние ключи, ссылающиеся на другие таблицы, то просмотру нужны привилегии references к этим другим таблицам, если эти таблицы сами не используются в данном про- смотре. Подробности см. в разд. "Привилегии" главы 24. Множество привилегий и множество получателей привилегий Есть возможность предоставлять несколько привилегий в одном операторе и предос- тавлять одну или более привилегий множеству пользователей или объектов.
Гпава 35. Безопасность на уровне базы данных 749 Множество привилегий Для предоставления получателю нескольких привилегий к таблице перечислите пре- доставляемые привилегии в списке, отделяя друг от друга запятыми. Следующий оператор назначает полномочия insert и update к таблице department пользователю chalky: GRANT INSERT, UPDATE ON DEPARTMENT TO CHALKY; Список привилегий может быть любой комбинацией в любом порядке привилегий SELECT, INSERT, UPDATE, DELETE И REFERENCES. Привилегия EXECUTE ДОЛЖНа Назначаться в отдельном операторе. Привилегия references не может быть назначена просмотру. Привилегия ALL Привилегия all объединяет привилегии select, insert, update, delete и references в одном пакете. Например, следующий оператор предоставляет chalky полный пакет полномочий к таблице department: GRANT ALL ON DEPARTMENT TO CHALKY; Вы также можете назначать пакет all триггерам и процедурам. В следующем опера- торе процедура count_chickens получает полные права к таблице proudfptjvjdget: GRANT ALL ON PROJ_DEPT_BUDGET TO PROCEDURE COUNT „CHICKENS; Привилегии для множества пользователей Несколько видов синтаксиса позволяют вам предоставлять привилегии множеству пользователей в одном операторе. Вы также можете назначать привилегии: ♦ списку именованных пользователей или процедур; ♦ группе UNIX; ♦ всем пользователям (public); ♦ роли (затем назначая эту роль списку пользователей, public или группе UNIX). Список именованных пользователей Для назначения одних и тех же привилегий доступа пользователям в одном операто- ре запишите список пользователей, разделенных запятыми, на месте одного имени пользователя. Следующий оператор предоставляет полномочия insert и update к таблице department пользователям mickey, donald и hpotter: GRANT INSERT, UPDATE ON DEPARTMENTS TO MICKEY, DONALD, HPOTTER; Список процедур Для назначения привилегий нескольким процедурам в одном операторе запишите список процедур, разделенных запятыми. Здесь две процедуры получают привилегии в одном операторе:
750 Часть VIII. Безопасность GRANT INSERT, UPDATE ON PROJ_DEPT_BUDGET TO PROCEDURE CALCULATE_ODDS, COUNT__BEAN S; Группа UNIX Имена учетных записей операционной системы Linux/UNIX доступны для безопас- ности в Firebird через привилегии Firebird, которые не являются стандартом SQL. Клиент, выполняющийся как пользователь UNIX, применяет этот идентификатор пользователя в базе данных, даже если такая учетная запись не определена в базе данных безопасности Firebird. Машина, получающая доступ к серверу, должна быть в списке доверенных машин на сервере (файлы /etc/host.equiv или /etc/gds__host.equiv, или в .rhost в домашнем катало- ге пользователя на сервере). При соединении пользователь использует идентифика- цию его группы, если он не предоставил для Firebird свое имя пользователя и пароль в качестве параметров соединения — учетные записи Firebird перекрывают учетные записи UNIX. Группы Linux/UNIX совместно используют такое поведение: пользователь SYSDBA или Суперпользователь могут назначать привилегии SQL группам UNIX. Любая опе- рация над учетной записью на уровне системы, которая является членом группы, на- следует привилегии, предоставленные группе, например: GRANT ALL ON CUSTOMER TO GROUP sales; Все пользователи (PUBLIC) Для назначения одних и тех же привилегий доступа к таблице всем пользователям предоставьте привилегии public, public только пользователей — не триггеры, про- цедуры, просмотры или роли. GRANT SELECT, INSERT, UPDATE ON DEPARTMENT TO PUBLIC; Привилегии, предоставленные пользователям с помощью public, могут быть отмене- ны только путем отмены их у public. Вы не можете, например, отменить привилегию у chalky, которую chalky получил как член PUBLIC. Привилегии через роли Процесс реализации ролей состоит из четырех шагов: 1. Создание роли с использованием оператора create role. 2. Назначение привилегий этой роли посредством grant привилегия то роль. 3. Назначение роли пользователям посредством grant роль то пользователь. 4. Задание роли вместе с именем пользователя при соединении с базой данных.
Глава 35. Безопасность на уровне базы данных 751 Создание роли Синтаксис создания роли прост: CREATE ROLE <имя-роли>; Пользователь SYSDBA или владелец базы может создавать роли, предоставлять им привилегии и передавать эти "нагруженные" роли пользователям. Если роль предос- тавлена с параметром with admin option, получатель этой роли может предоставлять ее другим пользователям с with admin option или без. Назначение привилегий роли Для "загрузки" роли привилегиями просто предоставьте ей требуемые привилегии, как если бы роль была обычным пользователем: GRANT <приволегии> ТО <имя-роли>; Предоставление роли пользователям В операторе grant для предоставления роли пользователям опускается предложение on— здесь неявно используются полномочия, "загруженные" в роль. GRANT <имя-роли> [, <имя-роли> [, ...]]] ТО [USER] <имя-пользователя> [, [USER] <имя-пользователя> [, ...]]] [WITH ADMIN OPTION]; Необязательное предложение with admin option позволяет получающему роль пре- доставлять эту роль другим пользователям, а также отменять ее. Это работает таким же образом, что и with grant option для обычных полномочий — см. разд. "Предос- тавление прав на предоставление привилегий". Следующий пример создает роль maitre_d, предоставляет этой роли привилегии all к таблице department, а затем предоставляет роль пользователю hortense. Это дает пользователю hortense привилегии select, insert, update, delete и references к таблице department. CREATE ROLE MAITRE_D; COMMIT; GRANT ALL ON DEPARTMENT TO MAITRE—D; GRANT MAITRE-D TO HORTENSE; Подключение к базе данных с использованием роли При соединении включите role в список параметров соединения и укажите ту роль, чьи привилегии вы хотите использовать в этом соединении. Это будет работать, только если данному пользователю была предоставлена указанная роль:
752 Часть VIII. Безопасность CONNECT <путь-к-базе-данных> USER <ваше-имя-пол:ьзователя> ROLE <имя-роли> PASSWORD <ваш~пароль>; Удаление роли Если вы удаляете роль, то все привилегии, предоставленные этой роли, отменяются. Чтобы удалить роль maitre_d, выполните: DROP ROLE MAITRE_D; < ПРИМЕЧАНИЕ. Если вам нужно лишь удалить привилегии, предоставленные пользователю с помощью роли, или удалить привилегии роли, используйте опе- \ х ратор revoke (см. разд. "Отмена полномочий"). Предоставление прав на предоставление привилегий Вначале только владелец таблицы или просмотра или пользователь SYSDBA могут предоставлять полномочия к этому объекту другим пользователям. Добавьте with grant option в конец оператора grant для передачи пользователю права предостав- лять привилегии вместе с самими привилегиями. Следующий оператор назначает полномочия select пользователю hpotter и дает пра- во hpotter предоставлять полномочия select другим: GRANT SELECT ON DEPARTMENT TO HPOTTER WITH GRANT OPTION; with grant option не может назначаться триггерам или процедурам. Права with grant option являются кумулятивными, даже если передаются различны- ми пользователями. Например, hpotter может получить право предоставлять права select к таблице department от одного пользователя и insert к department от другого. В следующем примере hpotter имеет доступ select к таблице department с правом передавать полномочия, hpotter может предоставлять полномочия select другим пользователям. Предположим, hpotter теперь также получает полномочия insert к этой же таблице, но без права предоставлять эти полномочия: GRANT INSERT ON DEPARTMENT TO HPOTTER; Пользователь hpotter может выбирать данные и добавлять данные в таблицу department. Он может предоставлять полномочия select к таблице department, но не может назначать полномочия insert, потому что он не имеет права предоставлять эту привилегию. Существующие привилегии пользователя могут быть расширены включением права предоставлять привилегии. Для этого нужно выдать второй оператор grant для той же привилегии, который будет включать предложение with grant option.
Гпава 35. Безопасность на уровне базы данных 753 Для предоставления пользователю hpotter права предоставлять полномочия insert к таблице department просто выполните новый оператор: GRANT INSERT ON DEPARTMENT TO HPOTTER WITH GRANT OPTION; Подводя итог, скажем, что пользователь может предоставлять привилегии доступа (SELECT, INSERT, UPDATE, DELETE И REFERENCES) К объекту ДруГИМ Пользователям ИЛИ объ- ектам, если пользователь: ♦ владеет этим объектом; ♦ получил такую привилегию к этому объекту вместе с with grant option; ♦ получил эту привилегию путем предоставления ему роли, содержащей привиле- гию вместе с with admin option. SQL допускает операторы grant, которые предоставляют пользователю дубликаты полномочий. Неожиданные эффекты SQL позволяет.одному и тому же получателю прав получать одни и те же полномо- чия из различных источников, даже если предоставляемые права уже есть у получа- теля. Каждый раз, когда один пользователь расширяет у другого пользователя права передавать полномочия, он открывает еще один источник, из которого любой поль- зователь может получить полномочия. Структура полномочий потенциально может стать пресловутым "птичьим гнездом", из которого очень трудно вытащить фактиче- ское состояние полномочий индивидуального пользователя или объекта. Предположим, у нас есть два пользователя, serena и hpotter, с соответствующими привилегиями и правами предоставлять другим полномочия. Оба выдали следующий оператор: GRAN TINSERT ON DEPARTMENT ТО BRUNHILDE WITH GRANT OPTION; Позднее serena отменяет привилегию и право предоставлять полномочия у brunhilde: REVOKE INSERT ON DEPARTMENT FROM BRUNHILDE; serena считает, что brunhilde больше не имеет полномочий insert и не может предос- тавлять другим права к таблице department. Однако выполненный оператор revoke не имеет эффекта, поскольку brunhilde все еще имеет полномочия insert и право пре- доставлять привилегии, полученные от hpotter. Так как количество пользователей с привилегиями и возможностью предоставлять другим права растет, вероятно, что различные пользователи могут предоставлять
754 Часть VIII. Безопасность одни и те же привилегии и возможность предоставлять другим права одному пользо- вателю. Довольно просто, как и ядерное деление, это может выйти из-под контроля. Отмена какого-либо полномочия может оказаться большой работой. Отмена всех (или многих) полномочий у одного конкретного пользователя может оказаться астрономически сложной проблемой. Если у пользователя была возможность получать права от различных пользователей, существует два возможных решения, оба не очень приятных. ♦ Найдите каждое право, предоставленное этому пользователю, вместе с тем, кто предоставлял это право, и заставьте всех отменить каждое предоставленное право. Это станет весьма запутанным, если использовались варианты all и public, пото- му что отмена более целенаправленного права не отменяет прав, предоставленных через all, public, роли или группы. ♦ Владелец каждой таблицы и объекта (или SYSDBA) выполняет операторы revoke, действующие на всех пользователей этой таблицы, а затем выдает операторы grant для установления привилегий только тем пользователям, кому нужно со- хранить свои права. Сервер не выдает никаких сообщений для команд revoke, независимо от того, была ли она успешна или ошибочна. Это будет тот самый момент вашей первой, неприят- ности, связанной с полномочиями SQL, когда вы возденете глаза к небесам и начнете размышлять о реальном назначении комитетов по стандартам на этой Земле. Хорошо спроектированная графическая утилита управления правами может сохранить вам рассудок. К счастью, многие программы администрирования осуществляют такую поддержку. Доступно множество инструментов управления полномочиями. (См. при- ложение 12.) Более подробную информацию об отмене полномочий см. в следующем разделе. СОВЕТ. Хотя определение пользователей, ролей и назначение привилегий час- то откладывается до момента, когда система готова к поставке пользователям, тем не менее создание схемы привилегий и соотнесение ее со списком пользова- телей нужно запланировать при проектировании системы. Поддержка диаграммы такой схемы является весьма полезным делом как при проектировании и тести- ровании системы, так и при ее документировании. Отмена полномочий Оператор revoke требуется для удаления полномочий, назначенных операторами grant. Согласно стандарту, revoke должен каскадом отменить все привилегии, полу- ченные всеми пользователями как результат with grant option от данного пользова- теля. Однако вам не следует на это полагаться в Firebird, потому что конфликт пра- вил стандарта при некоторых условиях может привести к логике реализации, отлич- ной от предложенной в стандарте.
Гпава 35. Безопасность на уровне базы данных 755 Операторы revoke могут отменить любую привилегию, которую может назначить оператор grant. Только пользователь SYSDBA или пользователь, предоставивший привилегию, могут отменить ее— те же самые или другие привилегии, предостав- ленные другими пользователями, не отменяются. Полномочия, предоставленные "в куче", не могут отменяться индивидуально. Это означает: ♦ привилегия, которую пользователь получил в результате назначения all или в качестве роли, может быть отменена только предоставившим эту привилегию, пу- тем отмены all или роли соответственно; ♦ отмена привилегии у пользователя, который получил ее путем public или группы UNIX, может быть выполнена предоставившим эту привилегию путем отмены public или группы соответственно; ♦ привилегии, предоставленные для public, могут быть отменены только from public. Использование REVOKE Упрощенный синтаксис для revoke внешне отличается от синтаксиса grant. Предло- жение ТО <получатель> заменяется на FROM <получатель>‘ REVOKE < привилегии> ON <объект> FROM < получа тель> ; Следующий оператор отменяет привилегию select к таблице department у пользова- теля kilroy, если он получил ее при выполнении grant select: REVOKE SELECT ON DEPARTMENT FROM KILROY; Следующий оператор отменяет привилегию update к таблице customer для процедуры COUNT_BEANS: REVOKE UPDATE ON CUSTOMER FROM PROCEDURE COUNT_BEANS; Следующий оператор удаляет привилегию execute, которая была предоставлена про- цедуре count__beans к процедуре abandon_old: REVOKE EXECUTE ON PROCEDURE ABANDON_OLD FROM PROCEDURE COUNT_BEANS; Удаление множества привилегий Для удаления нескольких, но не всех привилегий, предоставленных пользователю или процедуре, перечислите удаляемые привилегии, отделив их друг от друга запя- тыми. К примеру, следующий оператор удаляет привилегии insert и update к таблице department у пользователя serena: REVOKE INSERT, UPDATE ON DEPARTMENT FROM SERENA; Следующий оператор удаляет две привилегии к таблице customer у хранимой про- цедуры COUNT_BEANS:
756 Часть VIII. Безопасность REVOKE INSERT, DELETE ON CUSTOMER FROM PROCEDURE COUNT_BEANS; Любая комбинация ранее назначенных привилегий select, insert, update, delete или references может быть отменена назначившим эти привилегии, неважно, были ли они назначены индивидуально, в списке или с использованием all. Как и в случае grant, отмена привилегий revoke all объединяет привилегии select, insert, update, delete и references в одном выражении. Данный оператор отменит любое из этих назначенных полномочий. Например, следующий оператор отменит все привилегии доступа к таблице departments у пользователя magpie: revoke all on departments from magpie; Если пользователь не имеет всех привилегий, включенных в all, оператор не вызовет исключения. Оператор revoke all может быть особенно полезным, если вы не знаете, какие привилегии имеет пользователь. Нет необходимости решать проблему устра- нения всех полномочий, доступных пользователю, потому что у revoke all есть ог- раничения на то, что он способен отменить. Чего не отменяет REVOKE ALL revoke all не отменяет: ♦ привилегии, наследуемые вместе с ролью; ♦ привилегии, полученные как public; ♦ привилегии execute. Отмена привилегии EXECUTE Синтаксис отмены привилегии execute к хранимой процедуре: REVOKE EXECUTE ON PROCEDURE <имя-процедуры> FROM <получатель> [, <получателъ> [, ...]]] I [TRIGGER <имя-триггера> [, <имя-триггера> [,...]]] ‘[PROCEDURE <имя-процедуры> [, <имя-процедуры> [, ...]]] [VIEW <11мя-просмотра> [, VIEW <имя-просмотра> [, ...]]]; Следующий оператор удаляет привилегию execute у пользователя hpotter к процеду- ре count__chickens: REVOKE EXECUTE ON PROCEDURE COUNT-CHICKENS FROM HPOTTER; Отмена привилегий у нескольких пользователей Теперь мы рассмотрим как получатели привилегий, указанные в предложении from оператора revoke, могут быть объединены для отмены привилегий.
Гпава 35. Безопасность на уровне базы данных 757 Список пользователей Используйте разделенный запятыми список пользователей для отмены привилегий у множества пользователей в одном операторе. Следующий оператор удаляет привиле- гии insert и update к таблице departments у троих пользователей за один раз: REVOKE INSERT, UPDATE ON DEPARTMENTS FROM MAGPIE, BRUNHILDE, KILROY; Роль Отмена привилегий, предоставленных роли, отменяет эти привилегии у всех пользо- вателей, имеющих эту роль в качестве привилегий: REVOKE UPDATE ON DEPARTMENT FROM CARTEBLANCHE; Теперь пользователи, которым была назначена роль carteblanche, больше не имеют привилегии update к таблице department, но у них остаются другие привилегии (select, insert, delete, references, execute), которые они могли наследовать от CARTEBLANCHE. Вы можете использовать один оператор для отмены одних и тех же привилегий у одной или более ролей: REVOKE DELETE, INSERT ON DEPARTMENT FROM CARTEBLANCHE, MAITRE^D; Роль пользователя Отмена назначенных роли пользователей отменяет у этих пользователей все полно- мочия, которые они получили с этой ролью. Используйте revoke для удаления роли, которую вы назначали пользователям. Следующий оператор отменяет роль carteblanche у пользователя kilroy: REVOKE CARTEBLANCHE FROM KILROY; kilroy больше не имеет привилегий доступа, полученных как результат его членства в этой роли. Однако это не оказывает влияния на других пользователей, которые по- лучили эти привилегии через членство в данной роли. Объекты Для отмены привилегий у одной или более процедур, триггеров или просмотров включите соответствующее ключевое слово (procedure, trigger, view) перед именем объекта. Вы можете отменить одну и ту же привилегию у различных типов объектов, задав разделенный запятыми список объектов каждого типа. В этом случае просто начните каждый список с ключевого слова типа объекта.
758 Часть VIII. Безопасность Следующий оператор удаляет привилегии insert и update к таблице customer для двух процедур и триггера: REVOKE INSERT, UPDATE ON CUSTOMER FROM PROCEDURE COUNT_CHICKENS, ABANDON_OLD TRIGGER AI_SALES ; Пользователь PUBLIC Для отмены привилегий, которые все пользователи получили как пользователь public, просто трактуйте public, как и любого другого пользователя. Например, сле- дующий оператор отменяет полномочия insert и delete к таблице department у всех пользователей: revoke select, insert, update ON DEPARTMENT FROM PUBLIC; После выполнения этого оператора привилегии insert и delete к таблице department сохраняются у владельца этой таблицы и у пользователя SYSDBA, равно как и у имевших эти привилегии хранимых процедур, просмотров и триггеров. Отмена при- вилегий у public также не убирает привилегии у пользователей, которые имеют их как собственные права. Отмена права предоставлять привилегии Для отмены права у пользователя предоставлять конкретную привилегию, но сохра- нить у него эту привилегию, используйте revoke grant option: revoke grant option FOR <привилегия> [, <привилегия> [,...]] ON <таблица> | (объект) FROM <полъзователь> ; Например, следующий оператор отменяет право предоставлять привилегию select к таблице department у пользователя hpotter и сохраняет у него привилегию select: revoke GRANT OPTION FOR SELECT ON DEPARTMENT FROM HPOTTER; Выполнение этого оператора приведет к каскадным отменам у всех других пользова- телей, которые получили право предоставлять эту привилегию от hpotter.
Гпава 35. Безопасность на уровне базы данных 759 Скрипты безопасности Если вы до сих пор задержались на этой главе, нет сомнений, что вы пришли к за- ключению, что реализация привилегий SQL требует набора большого объема текста. Что ж, вы правы. В действительности мы не используем интерактивные методы для выполнения этой работы. Мы пишем скрипты — или, иначе, мы пишем хранимые процедуры, которые пишут для нас скрипты. Как правило, если мы имеем хорошо работающую схему привилегий, мы можем ге- нерировать множество скриптов, которые формируют роли и общие полномочия. Обычно вручную написанный скрипт предназначен для полномочий execute, по- скольку для этого довольно сложно создать нужную формулу. С появлением в Firebird 1.5 оператора execute procedure, который позволяет нам обойти невозможность в Firebird выполнения операторов DDL в PSQL, мы можем выполнить групповую загрузку полномочий прямо в базу данных из хранимой про- цедуры. Пример подобной процедуры представлен в листинге 35.2 далее в этой главе. Существует множество инструментов с графическим интерфейсом для тех, кто их любит. У большинства есть средства, автоматически формирующие скрипты безо- пасности; некоторые создают и устанавливают полномочия только для вас. Создание скрипта Автор предпочитает генерировать скрипт безопасности. Он может быть протестиро- ван и аннотирован, он является документом, нужным для контроля качества, и даю- щим основу для распространения у пользователей. Пример подобного скрипта пред- ставлен в листинге 35.1. Для скрипта мы можем использовать внешний файл, в который помещен скрипт — инструкции и примеры см. в главе 16, примеры также содержатся в главе 30. Однако процедура permscript, описанная здесь, разработана для выполнения в isql и для пе- редачи ее выхода в текстовый файл. Листинг 35.1. Процедура для генерации скрипта полномочий /* (с) Helen Borrie 2004, free for use and inodxficatron under the Initial Developer's Public License */ SET TERM Л; CREATE PROCEDURE PERMSCRIPT( CMD VARCHAR (6) , PRIV CHAR (10) , USR VARCHAR(31), ROLENAME VARCHAR(31), GRANTOPT SMALLINT, CREATE_ROLE SMALLINT) /* введите 'G' or ’R* */ /* привилегия или 'ALL’ или ’ANY’ */ /* имя пользователя */ /* роль, существующая или нет */ /* 1 ДЛЯ ’WITH GRANT[ADMIN] OPTION’ */ /* 1 для создания новой роли ROLENAME */
760 Часть VIII. Безопасность RETURNS (PERM VARCHAR(80)) /* теоретический оператор полномочия */ AS DECLARE VARIABLE RELNAME VARCHAR(31); /* для имени таблицы или просмотра */ DECLARE VARIABLE STRING VARCHAR(80) = ’’; /* используется в процедуре */ DECLARE VARIABLE STUB VARCHAR(60) = /* используется в процедуре */ DECLARE VARIABLE VUSR VARCHAR(31); /* имя пользователя для 'TO' или 'FROM' */ DECLARE VARIABLE COMMENTS CHAR(20) = '/* */'; BEGIN /* Необходимо для некоторых редакторов пользовательского интерфейса */ IF (ROLENAME = '') THEN ROLENAME = NULL; IF (USR = ' ') THEN USR = NULL; IF (PRIV = '') THEN PRIV = NULL; /* Недостаточно данных для выполнения работы */ IF ((PRIV IS NULL AND ROLENAME IS NULL) OR USR IS NULL) THEN EXIT; /* Если это имя роли, мы будем с ней работать */ IF (ROLENAME IS NOT NULL) THEN BEGIN /* Если задано имя роли, то создается роль, если она требуется */ IF (CREATE_ROLE = 1) THEN BEGIN PERM = 'CREATE ROLE ' j j ROLENAME || ' ; 1 ; SUSPEND; PERM = 'COMMIT;'; SUSPEND; PERM = COMMENTS; SUSPEND; END VUSR = ROLENAME; END /* Если существует имя роли, мы применим полномочия к этой роли и предоставим эту роль указанному пользователю */ ELSE /* Нас не интересует роль: полномочия только для пользователя */ VUSR = USR; /* Выяснение - этот скрипт GRANT или REVOKE */ IF (CMD STARTING WITH 'G') THEN STUB = 'GRANT'; 'ELSE STUB = 'REVOKE '; IF (ROLENAME IS NOT NULL) THEN BEGIN IF (STUB = 'GRANT') THEN BEGIN /* Предоставление роли пользователю */ STRING = STUB {| ROLENAME || ' TO ' || USR; IF (GRANTOPT = 1) THEN STRING STRING || ' WITH ADMIN OPTION ;'; END
Гпава 35. Безопасность на уровне базы данных 761 ELSE STRING = STUB | | ROLENAME I I ' FROM ' | | USR | | ' ; ' ; PERM = STRING; SUSPEND; PERM = COMMENTS; SUSPEND; END /* Если передано ANY в качестве привилегии, создаем отдельно каждую привилегию */ IF (PRIV = 'ANY') THEN STUB = STUB || 'SELECT, DELETE, INSERT, UPDATE, REFERENCES ON '; ELSE STUB = STUB || PRIV H ' ON ' ; /* Просмотр всех имен таблиц и просмотров и создание для каждого оператора */ FOR SELECT RDB$RELATION_NAME FROM RDB$RELATIONS WHERE RDBSRELATIONJTAME NOT STARTING WITH 'RDB$' INTO :RELNAME DO BEGIN STRING = STUB || :RELNAME |I ' '; IF (CMD STARTING WITH 'G') THEN STRING = STRING || 'TO '; ELSE STRING = STRING || 'FROM '; STRING = STRING || VUSR; IF (CMD STARTING WITH 'G' AND GRANTOPT = 1 AND ROLENAME IS NULL) THEN STRING = STRING I| ' WITH GRANT OPTION ;'; ELSE STRING = STRING || ' ; ' ; PERM = STRING; SUSPEND; END PERM = COMMENTS; SUSPEND; END Л SET TERM Создание и выполнение скрипта Перейдите в каталог Firebird /bin и запустите isql, соединитесь с базой данных как пользователь SYSDBA. Вы используете процедуру для создания скрипта, который добавит роль 'mandrake', предоставит эту роль пользователю useri, а потом установит полномочия для этой роли. Затем снова сделает то же самое с существующей ролью 'purple' для пользователя users: SQL> OUTPUT L:\DATA\EXAMPLES\PERMSCRIPT.SQL; SQL> SELECT * FROM PERMSCRIPT ('G', 'ALL', 'USERI', 'MANDRAKE', 1, 1); SQL> COMMIT; SQL> SELECT * FROM PERMSCRIPT ('G', 'ALL', 'USERS', 'PURPLE', 1, 0);
762 Часть VIII. Безопасность SQL> COMMIT; SQL> OUTPUT; SQL> INPUT L:\DATA\EXAMPLES\PERMSCRIPT.SQL; SQL> COMMIT; SQL> SHOW GRANT; Вот и все. Вы получите сообщение об ошибке, когда утилита input встретит текст не SQL, напечатанный в output, однако это не повлияет на вывод полномочий. Инсталляция полномочий непосредственно из процедуры Процедура grant_perms, показанная в листинге 35.2, в основном похожа на процедуру permscript из листинга 35.1. Вместо того чтобы создавать набор выходных строк для выполнения под isql в качестве скрипта, она непосредственно устанавливает полно- мочия через execute statement. Такая техника недоступна в Firebird 1,0.x. Листинг 35.2. Процедура полномочий /* (с) Helen Borrie 2004, free for use and modification under the Initial Developer's Public License ★/ SET TERM A; CREATE PROCEDURE GRANT_PERMS (CMD VARCHAR(6) , PRIV CHAR(10), USR VARCHAR (31) , ROLENAME VARCHAR(31), GRANTOPT SMALLINT) AS DECLARE VARIABLE RELNAME VARCHAR(31); DECLARE VARIABLE EXESTRING VARCHAR(1024) = ''; DECLARE VARIABLE EXESTUB VARCHAR (1024 ) = BEGIN IF (ROLENAME = ' ’ ) THEN ROLENAME = NULL; IF (USR = ' ' ) THEN USR = NULL; IF (PRIV = '') THEN PRIV = NULL; IF ((PRIV IS NULL AND ROLENAME IS NULL) OR USR IS NULL) THEN EXIT; IF (CMD STARTING WITH 'G') THEN EXESTUB = 1 GRANT ’; ELSE EXESTUB = ’REVOKE 1; IF (ROLENAME IS NOT NULL) THEN BEGIN IF (EXESTUB = 'GRANT') THEN BEGIN EXESTUB = EXESTUB | | ROLENAME | | ' TO ’ I I USR; IF (GRANTOPT = 1) THEN EXESTUB = EXESTUB || ' WITH ADMIN OPTION’; END
Гпава 35. Безопасность на уровне базы данных 763 ELSE EXESTUB = EXESTUB I I ROLENAME | | ’ FROM ’ | | USR; EXECUTE STATEMENT EXESTUB; END ELSE BEGIN IF (PRIV = 'ANY') THEN EXESTUB = EXESTUB || 'SELECT,DELETE,INSERT,UPDATE,REFERENCES ON '; ELSE EXESTUB = EXESTUB || PRIV || ' ON '; FOR SELECT RDB$RELATION_NAME FROM RDB$RELATIONS WHERE RDB?RELATION_NAME NOT STARTING WITH ’RDBS’ INTO :RELNAME DO BEGIN EXESTRING = EXESTUB | | : RELNAME H ' '; IF (CMD STARTING WITH ’G') THEN EXESTRING = EXESTRING |I ’TO '; ELSE EXESTRING = EXESTRING I I 'FROM '; EXESTRING = EXESTRING || USR; IF (GRANTOPT = 1) THEN EXESTRING = EXESTRING | | ' WITH GRANT OPTION1; EXECUTE STATEMENT EXESTRING; END END END л SET TERM ;л Если вы сами хотите поэкспериментировать с этими скриптами и изменить их для ваших потребностей, вы можете найти исходные тексты в разделе Download на http://www.apress.com, в файле permscripts.sql. Пора дальше Следующая (и последняя) глава этой части содержит множество тем, связанных с архитектурными вариантами сервера, включая раздел по инсталляции и использова- нию встроенного Суперсервера под Windows. Установки файла конфигурации в firebird.conf или isc config/ibconfig могут быть найдены в этой главе вместе с темой безопасности параметров конфигурации, связанных с использованием в Firebird внешних выполняемых и других файлов.
ГЛАВА 36 Конфигурация и специальные возможности Эта глава представляет собой попурри на темы, которые будут интересны тем разра- ботчикам, кто хорошо знаком с практическими вопросами выполнения Firebird. Сначала описываются архитектурные отличия между моделями Firebird Классиче- ский сервер и Суперсервер. Затем обсуждаются различные параметры конфигурации сервера и некоторые руководящие принципы разработки для встроенного сервера под Windows. В заключение мы рассмотрим коды пользовательских внешних модулей, которые вы можете писать для использования на серверной стороне, чтобы выполнять специаль- ные вычисления и преобразования (внешние функции), конвертирование blob из од- ного формата в другой (фильтры blob) и реализовывать международные наборы сим- волов. В конце этой темы содержатся подробности по конфигурированию доступа к файловой системе сервера для этих и других внешних объектов. Сравнение архитектуры Суперсервера и Классического сервера Хотя Суперсервер и Классический сервер имеют много общих характеристик — дей- ствительно, они созданы из одного базового кода, — они представляют совершенно различные модели внутренних операций. Выполняемые программы и процессы Классический сервер выполняется как один серверный процесс на каждое соедине- ние (рис. 36.1). Когда клиент пытается соединиться с базой данных Firebird, экземп- ляр исполняемого модуля fb inet server инициализируется и продолжает оставаться предназначенным для этого клиентского соединения в течение всего времени этого соединения. Когда клиент отсоединяется от базы данных, экземпляр серверного про- цесса завершается. Суперсервер выполняется как один вызов выполняемого модуля fbserver (рис. 36.2). fbserver запускается один раз системным загрузочным скриптом или системным ад- министратором и остается запущенным, ожидая запросов на соединение. Процесс завершается явным остановом.
Гпава 36. Конфигурация и специальные возможности 765 м I NETWORK ° CLIENT L w Га| remote ' a P Client * I Library request -For remote connection request for) remote connection glistening i А REMOTE ' Р Client I Library NETWORK CLIjENT м i D D L E И A R E Application Program (USER interface) Рис. 36.1. Сеть Классического сервера и модель процесса Управление блокировками При Классическом сервере каждый клиентский процесс сервера имеет свой собст- венный, связанный с базой данных кэш, и множество процессов борются за доступ к базе данных. Подсистема управления блокировками, fb lock mgr, использует методы внутреннего процесса связи (Inner-Process Communication, IPC) для управления и синхронизации доступа параллельных процессов к страницам. В Суперсервере менеджер блокировок реализован как поток процесса fbserver и ис- пользует механизмы внутрипоточной связи вместо механизма сигнализации POSIX. Подробности внутреннего управления блокировками см. в главе 40. Использование ресурсов В Классическом сервере каждый экземпляр fb inet server выделяет в своей области памяти статический кэш для страниц базы данных. Поэтому использование ресурсов линейно возрастает с каждым дополнительным клиентским соединением. Однако, когда количество одновременных соединений относительно мало, Классический сер- вер использует меньше ресурсов.
766 Часть VIII. Безопасность Рис. 36.2. Сеть Суперсервера и модель процесса Суперсервер использует единое пространство кэша, которое совместно используется клиентскими подключениями, что позволяет более эффективно использовать и управлять памятью кэша при большом увеличении количества одновременных со- единений. Локальный доступ Классический сервер позволяет процессам приложений, которые выполняются на той же машине, где находятся сервер и базы данных, выполнять прямые операции вво- да/вывода с базой данных. За исключением встроенного сервера Суперсервер требует, чтобы приложения ис- пользовали сетевые методы для запросов ввода/вывода, и удовлетворяет эти запросы по сети. В Linux Суперсервер не поддерживает локальный прямой доступ. ♦ На платформах, отличающихся от Windows (и также рекомендовано для Windows), локальные соединения с Суперсервером через сервер localhost (с адре- сом IP по соглашению 127.0.0.1). ♦ Сервер Windows и локальный клиент могут имитировать сетевое соединение че- рез совместно используемое пространство IPC. Этот механизм, называемый ло- кальным доступом, не может безопасно обрабатывать множество соединений. После Firebird 1.5 он замещается локальным методом доступа с использованием подсистемы XNET.
Гпава 36. Конфигурация и специальные возможности 767 Встроенный сервер может использовать только "локальный для Windows" метод дос- тупа и поддерживает одно и только одно соединение с каждой локальной базой дан- ных. Встроенный клиент может единолично соединяться с множеством локальных баз данных и получать к ним доступ через встроенный сервер. Встроенный клиент может также соединяться как обычный сетевой клиент с базами данных на других серверах. Файл конфигурации Firebird Firebird не требует постоянных серьезных изменений конфигурации, как это делает большинство других мощных реляционных СУБД. Однако доступен большой диапа- зон режимов конфигурации для настройки сервера Firebird и хост-системы, на кото- рой он запущен. Файл конфигурации Firebird имеет имя firebird.conf во всех версиях Firebird, начиная с 1.5. В предыдущих версиях его имя зависит от операционной системы: ♦ в Linux/UNIX это файл isc config; ♦ в Windows его имя ibconfig. Некоторые новые параметры были добавлены в версию 1.5. Когда при запуске процесса Firebird читается файл конфигурации, то выполняется настройка его флагов времени выполнения на значения не по умолчанию, содержа- щиеся в файле конфигурации. Этот файл больше не читается, пока сервер не будет заново запущен в следующий раз. Параметры конфигурации по умолчанию и их зна- чения отображаются в файле конфигурации, закомментированные маркерами ком- ментариев #. Нет необходимости убирать комментарии для значений по умолчанию, чтобы сделать их видимыми процедуре запуска сервера. Файл конфигурации может быть отредактирован любым текстовым редактором, на- пример, vim (Linux) или Блокнот (Windows). Не копируйте этот файл с машины Win- dows на Linux и наоборот, поскольку две системы по-разному хранят символ перево- да строки. Параметры Записи имеют форму. имя-параметра = значение иия-параметра является строкой, которая не содержит пробелов и именует конфигури- руемое свойство сервера. Значение является ЧИСЛОМ, логическим значением (1 = True, 0 = False) или строкой, что задает значение параметра. Для установления значения любого параметра в значение не по умолчанию удалите маркер комментария (#) и отредактируйте значение.
768 Часть VIII. Безопасность Имена параметров Firebird 1.0.x в ibconfig/iscconfig и их синтаксис не являются рав- нозначными параметрам в firebird.conf. Формат, размер и количество параметров яв- ляются более ограниченными. Формат в ibconfig/isc_config: имя-параметра значение Здесь пробелами между именем и его значением могут быть символы табуляции или пробелы — по желанию, что больше радует глаз. Каждая строка в файле ограничена 80 символами. Неиспользуемые параметры и значения инсталляции по умолчанию закомментированы символом #. В Linux вы должны учитывать, что имена параметров чувствительны к регистру. Ж ПРИМЕЧАНИЕ. Вы можете редактировать файл конфигурации в то время, когда сервер работает. Для активации изменений конфигурации необходимо остано- вить и снова запустить сервер. "Отсутствующие" параметры в Firebird 1.0.x В версиях, предшествующих 1.5, некоторые необязательные параметры, которые не могли быть сконфигурированы по умолчанию, не вошли в файл конфигурации. ♦ Если отсутствующий параметр, принадлежащий ibconfig или isc_config, необхо- дим для работы, он может быть добавлен. ♦ Если вы включите параметр, который не поддерживается в той версии, которая у вас запущена, он будет проигнорирован. ♦ Имена параметров и синтаксические конструкции в ibconfig/isc config не являют- ся взаимозаменяемыми в firebird.conf. Установки менеджера блокировок Улучшенные установки менеджера блокировок обсуждаются позже в конце главы 40. Параметры, связанные с файловой системой RootDirectory Версия 1.5 и более поздние. Значением этого параметра является строка, задающая абсолютный путь к корневому каталогу локальной файловой системы. Этот параметр должен оставаться закоммен- тированным, если только вы не хотите, чтобы процедура запуска заменила путь к корневому каталогу инсталляции сервера Firebird, который иначе она определяет са- ма. Серверы Firebird 1.5 и выше следуют заранее определенным путем для поиска корневого каталога. Логика этого пути объясняется в главе 3.
Гпава 36. Конфигурация и специальные возможности 769 DatabaseAccess Версия 1.5 и более поздние. В Firebird 1.0.x сервер может соединиться с любой базой данных в его локальной файловой системе и всегда доступен приложениям, передающим абсолютный путь к файлу в файловой системе. Настоящий параметр был добавлен в версии 1.5 для более жесткого контроля безопасности при доступе к файлам базы данных и для поддержки системы алиасов базы данных. Этот параметр предоставляет возможность ограниче- ния доступа сервера только к базам данных, имеющим алиасы, или только к базам данных, расположенным в указанных деревьях файловой системы. DatabaseAccess МОЖеТ иметь Значения Full, None ИЛИ Restrict. ♦ Full (по умолчанию) разрешает доступ к файлам базы данных в любом месте ло- кальной файловой системы. ♦ None позволяет серверу соединяться только с базами данных, которые указаны в списке aliases.conf. ВНИМАНИЕ! Настоятельно рекомендуется установить этот режим и использо- вать средства алиасов баз данных. Алиасы баз данных описаны с примерами в главе 4. ♦ Restrict позволяет сконфигурировать размещение доступных для подключения файлов баз данных, указывая список корневых деревьев файловой системы. Для определения разрешенных размещений задавайте список из одного или более корневых деревьев, разделяя их точкой с запятой, например: POSIX: /db/databases;/userdir/data Windows: D:\data Относительные пути трактуются как дополнительные к корневому каталогу установ- ки сервера. Например, под Windows, если корневым каталогом является C:\Program Files\Firebird, то следующее значение будет ограничивать доступ сервера к файлам баз данных, размещенных на C:\Program Files\Firebird\userdata: DatabaseAccess = Restrict userdata Параметры для конфигурирования временного пространства для сортировки Когда размер внутреннего буфера сортировки слишком мал для размещения строк, включенных в операцию сортировки, для Firebird требуется создание временных файлов для сортировки в файловой системе сервера. По умолчанию он будет отыски- вать путь, заданный в переменной окружения interbase_tmp. Если эта переменная не указана, он постарается использовать путь /tmp в файловой системе Linux/UNIX или C:\Temp в Windows NT/2000/XP. Ни одно из этих размещений не может конфигури- роваться для задания размера. 25 Зак. 420
770 Часть VIII. Безопасность В Firebird существует параметр для определения дискового пространства, которое будет использовано для хранения таких временных файлов. Будет разумным его применять, чтобы гарантировать, что достаточное пространство для сортировки бу- дет доступным при любых условиях. Все запросы connect или create database совместно используют один и тот же список каталогов для временных файлов, и каждый запрос создает свои собственные вре- менные файлы. Файлы сортировки освобождаются при завершении сортировки или при отмене запроса. В версии 1.5 ИМЯ ЭТОГО параметра изменилось С tmp_directory на TempDirectories. Также поменялся и синтаксис значения параметра. TempDirectories Версия 1.5 и более поздние. Предоставляет список из одного или более каталогов, разделенных точкой с запятой (;), где могут сохраняться файлы сортировки. Каждый элемент может включать не- обязательный аргумент размера в байтах для ограничения хранения. Если этот аргу- мент опущен или задан неверно, Firebird будет использовать пространство в этом каталоге, пока не исчерпает его объем, а после чего перейдет к следующему каталогу в списке, например: POSIX: /db/sortfilesl 100000000;/firebird/sortfiles2 Windows: E:\sortfiles 500000000 Относительные пути трактуются относительно пути, который запущенный сервер распознает как корневой каталог инсталляции Firebird. Например, в Windows, если корневым каталогом является C:\Program Files\Firebird, то следующее значение со- общит серверу, что он должен сохранять временные файлы в C:\Program Files\Firebird\userdata\sortfiles с ограничением размера примерно в 477 Мбайт: TempDirectories = userdata\sortfiles 500000000 tmp_directory Версии до Firebird 1.5. Синтаксис старого значения для tmp_directory требует включения одной строки tmp directory для каждого каталога, в котором вы хотите сохранять временные фай- лы сортировки. Задавайте количество байтов, доступных в каждом каталоге, и в ка- вычках путь к каталогу, который существует на физическом накопителе с достаточ- ным объемом свободной памяти. Вы можете перечислить множество записей, по од- ной на строке; пространства не обязательно должны быть смежными или размещаться на одном устройстве хранения данных. Пример. Следующие записи задают список в одном файле конфигурации: tmp_directory 6000000 "d:\fbtemp" tmp_directory 12000000 "f:\fbtemp" tmp_directory 4000000 "w:\backwash"
Глава 36. Конфигурация и специальные возможности 771 Путь должен быть заключен в двойные кавычки, иначе сервер проигнорирует эту запись. Пространство будет использовано в соответствии с порядком задания. Если будет выбрано пространство в одном конкретном каталоге, Firebird создаст новый временный файл в следующем каталоге из списка каталогов. Если же больше нет записей в списке каталогов, Firebird выдаст сообщение об ошибке и остановит обра- ботку текущего запроса. Параметры, связанные с ресурсами CpuAffinityMask Версии 1.5 и выше под Windows. cpu_affinity Версии до Firebird 1.5 под Windows. В Суперсервере Firebird под Windows могут быть проблемы с операционной систе- мой, постоянно переключающей процесс Суперсервера туда и сюда между процессо- рами на машинах SMP. В списках поддержки это называется "эффектом качелей" и на таких системах он может оказывать сильное воздействие на производительность. Данный параметр должен быть использован для установки одного или более кон- кретных процессоров для Суперсервера Firebird. Параметры CpuAffinityMask и сра_ affinity получают значение целого числа: маску CPU. Например: CpuAffinityMask = 1 cpu_affinity = 1 Суперсервер запускается только на первом процессоре (CPU 0). CpuAffinityMask = 2 cpu_affinity = 2 Запускается только на втором процессоре (CPU 1). CpuAffinityMask = 3 cpu__af finity = 3 Запускается на первом и на втором процессорах. ВНИМАНИЕ! Этот параметр не работает в Windows 9х или ME, поскольку он J \ использует вызов NT API. Версии Windows Эх не могут использовать более одно- г0 процессора. Вычисление значения CpuAffinityMask Вы можете использовать этот параметр для установления свойства Firebird для любо- го одного процессора или (для Классического сервера) любой комбинации процессо- ров, установленных в системе.
772 Часть VIII. Безопасность Рассматривайте центральные процессоры как массив, пронумерованный от 0 до и-4, где и— количество установленных процессоров, i— номер в массиве конкретного процессора. М— другой массив, содержащий значение маски (Maskvalue) для каждо- го выбранного CPU. Значение А является суммой значений в массиве М. Используйте следующие формулы для получения Ми вычисления Maskvalue А: Mi = 21 А = М\ + М2 + М3 ... Например, для выбора первого и четвертого процессоров (процессор 0 и процессор 3) вычислите: Л = 20+ 23 = 1+ 8 = 9 /А ВНИМАНИЕ! Серверы Firebird версии 1.5 и ниже могут не поддерживать Нурег- / » X Threading на некоторых ранних моделях материнских плат. Для устранения про- блем балансировки нагрузки может оказаться необходимым отменить Hyper- Threading на уровне BIOS системы. Маска процессоров по умолчанию 1 (процессор 0). DefauitDbCachePages Версия 1.5 и более поздние. database_cache_pages Версии, предшествующие Firebird 1.5. Устанавливает глобальное для сервера значение по умолчанию (целое число) количе- ство страниц базы данных, выделяемых в памяти для каждой базы данных. Сконфи- гурированное значение может быть перекрыто на уровне базы данных. Значение по умолчанию для Суперсервера 2048 страниц. Для Классического серве- ра- 75. Суперсервер и Классический сервер выделяют и используют кэш-память по-разному. Не существует "формулы", которую можно было бы применить для установки опти- мального значения по умолчанию размера кэша, который подошел бы для всех слу- чаев. Тем не менее факторы, влияющие на производительность, подробно обсужда- ются в разд. "Кэш базы данных" главы 15. EventMemSize Версия 1.5 и более поздние. Это целое число, задающее количество байтов памяти, выделяемой для менеджера событий. Значение по умолчанию 65 536 (64 Кбайт). SortMemBlockSize Версия 1.5 и более поздние. Этот параметр позволяет сконфигурировать размер в байтах каждого блока памяти, используемого для модуля внутренней сортировки. Значение по умолчанию при ин-
Гпава 36. Конфигурация и специальные возможности 773 сталляции 1 Мбайт, который вы можете заменить любым значением, не превышаю- щим текущее значение максимума, установленного в параметре sortMemupperLimit. SortMem UpperLimit Версия 1.5 и более поздние. Максимальный размер памяти в байтах, выделяемой для модуля внутренней сорти- ровки. Значение по умолчанию при инсталляции 67 108 864 байт (64 Кбайт) для Суперсервера и 8 388 608 байт (8 Кбайт) для Классического сервера. Д ВНИМАНИЕ! Для Классического сервера значение по умолчанию слишком вели- ко, если только не подключено большое количество клиентов. Помните, что уве- личение размера любого блока или максимального ограничения в Классическом сервере влияет на каждое клиентское соединение (экземпляр сервера) и увели- чивает потребление сервером памяти в линейной пропорции. Параметры, связанные с коммуникацией ConnectionTimeout Версия 1.5 и более поздние. connection_timeout Версии, предшествующие Firebird 1.5. Задает количество секунд ожидания до прекращения попытки соединения. Значение по умолчанию 180. DummyPacketlnterval Версия 1.5 и более поздние. dummy_packet_interval Версии, предшествующие Firebird 1.5. Это старый параметр InterBase, устанавливающий количество секунд (целое число), в течение которых сервер должен ждать молчания клиентского соединения, прежде чем отправить пустой пакет для подтверждения запроса. По умолчанию устанавлива- ется в 0 (отключено) для Firebird 1.5 и в 60 для Firebird 1.0.x. /йк ВНИМАНИЕ! Этот параметр не должен использоваться в Windows совсем, и он / } \ не рекомендуется для других операционных систем1. По причине ошибки в Windows использование DummyPacketlnterval может привести к зависанию или краху Windows на клиентской стороне. Объяснения см. в статье Microsoft Support: http://support.microsoft.com/ default.aspx?kbid=296265. Не рекомендуется его также использовать и для систем, отличных от Windows, поскольку это может привести к отключе- нию активного клиента.
774 Часть VIII. Безопасность В Firebird 1.0.x откройте ibconfig (Windows) или isc config (другие системы) и до- бавьте строку: duinmy_packet_interval-0 Обычно Firebird отслеживает активные соединения с использованием режима разъ- ема so__keepalive с периодом по умолчанию 2 часа. Если вам нужно изменить период 2 ожидания, подрегулируйте установки сервера : ♦ для сервера POSIX измените содержание proc/sys/net/ipv4/tcp_keepalive__*; ♦ для Windows просмотрите инструкции в статье на http://support.microsoft.com /default.aspx?kbid=l 40325. RemoteServiceName Версия 1.5 и более поздние. Это имя сервиса на сервере. Если файл firebird.conf включен только в клиентскую инсталляцию (см. главу 7), то клиент при необходимости будет отыскивать в нем имя сервиса. См. также RemoteServicePort (описан в следующем разделе). Более подроб- ную информацию см. в разд. "Конфигурирования порта сервиса" главы 2. Default = gds_db RemoteServicePort Версия 1.5 и более поздние. Этот параметр и RemoteServiceName обеспечивают возможность перекрытия либо име- ни сервиса TCP/IP, либо номера порта TCP/IP, используемых для прослушивания запросов клиентов к соединению с базой данных, если один из них отличается от значения по умолчанию при инсталляции (gds_db и порт 3050). Изменяйте одну запись, но не обе. RemoteServiceName проверяется первым на соответ- ствие записи в файле services. Если есть соответствие, то конфигурируется номер порта для RemoteServicePort. Если нет соответствия, то используется номер порта по умолчанию при инсталляции — 3050. ПРИМЕЧАНИЕ. Если номер порта указывается в строке соединения TCP/IP, то »—0Н всегда имеет приоритет перед RemoteServicePort. RemoteAuxPort Версия 1.5 и более поздние. Унаследованное от InterBase поведение, связанное с передачей сообщений о событи- ях по сети через случайно выбранные порты TCP/IP, показывает, что в некоторых 2 Подробно по настройкам keepalive читайте статью www.ibase.ru/devinfo/keepalive.htm. — Прим. науч. ред.
I лава 36. Конфигурация и специальные возможности 775 типах инсталляций это является источником постоянных ошибок сети и конфликтов средств межсетевой защиты (firewall), иногда приводит к краху сервера. Этот пара- метр позволяет сконфигурировать один порт TCP для всего трафика сообщения о событиях. Значение инсталляции по умолчанию (0) сохраняет традиционный случайный выбор порта. Для назначения одного порта для сообщений о событиях используйте целое число, которое задает номер доступного порта. RemoteBindAddress Версия 1.5 и более поздние. По умолчанию клиенты могут соединяться из любого сетевого интерфейса, для кото- рого сервер поддерживает трафик. Этот параметр позволяет вам связывать сервис Firebird с приходящими запросами через один IP-адрес (т. е. сетевую карту) и откло- нять запросы на соединение от любых других сетевых интерфейсов. Это позволяет разрешать проблемы в некоторых сетях, где сервер используется в нескольких подсе- тях. Это строка в допустимом формате IP с использованием разделительных точек. Зна- чением по умолчанию является пустая строка. TcpRemoteBufferSize Версия 1.5 и более поздние. Ядро сервера вначале считывает клиента и может отправить некоторые строки дан- ных в одном пакете. Чем больше размер пакета, тем большая строка отправляется за одну пересылку. Используйте этот параметр (с осторожностью и полным понимани- ем его воздействия на производительность сети), если вам нужно увеличить или уменьшить размер пакета TCP/IP. Параметр воздействует и на сервер, и на клиента. Значением является целое число (размер пакета в байтах) в диапазоне от 1448 до 32 767. Значением по умолчанию при инсталляции является 8192. Параметры, специфичные для POSIX RemoteFileOpenAbility Версия 1.5 и выше, только для POSIX. |\ ВНИМАНИЕ! Используйте RemoteFileOpenAbility ТОЛЬКО С особой ОСТОрож- I 1 ностью. Это логический параметр, который, будучи установленным в True, позволяет серверу открывать файлы, которые расположены в монтированных разделах сетевой файло- вой системы (Networked FileSystem, NFS). Он предназначен для создания оператив- ных копий на накопителях в NFS, которые имеют высокую доступность. Он не явля- ется безопасным для файлов базы данных — возможно, за исключением баз данных
77& Часть VIII. Безопасность только для чтения — потому что эта файловая система находится вне контроля ло- кальной файловой системы. Он не должен использоваться для целей открытия любых баз данных для чтения/записи, чья выживаемость имеет для вас значение. TcpNoNagle Версия 1.5 и выше, только для Linux. tcp_no_nagle Версии до Firebird 1.5, только для Linux. По умолчанию в Linux библиотека sockets будет минимизировать отправку данных путем буферизации перед фактической пересылкой данных, используя при этом внутренний алгоритм (реализованный как режим tcpjtodelay при канальном соеди- нении), называемый алгоритмом Nagle. Он был разработан для исключения проблем с пересылкой большого числа малых пакетов в медленных сетях. По умолчанию параметр tcp_nodelay включен (значение 0) при инсталляции в Linux Суперсервера Firebird. В медленных сетях его отключение может увеличить ско- рость. Остерегайтесь двойного отрицания — устанавливайте значение параметра в True для отключения tcp_nodelay и в False для его включения3. В версиях 1.5 и выше эта возможность активна только для Суперсервера. Параметры, специфичные для Windows CreatelnternalWindow Версия 1.5 и выше, только для Windows. Протокол "локального Windows" использует скрытое окно для IPC (InterProcessor Communication, межпроцессорная связь) между локальным клиентом и сервером. Это окно IPC создается при запуске сервера, когда CreatelnternalWindow имеет значение True (1, по умолчанию). Установите его в 0 (отключен) для запуска сервера без этого окна и, следовательно, без локального протокола. При отключенном локальном про- токоле возможен одновременный запуск множества серверов. DeadThreadsCollection Версия 1.5 и выше, только для Windows. Задается для планировщика потоков в Windows. Этот целочисленный параметр уста- навливает величину приоритета переключения циклов (см. разд. "PrioritySwitchDelay"), которое выполняется планировщиком перед разрушением (или закрытием) потока. Немедленное разрушение (или закрытие) рабочих потоков может потребовать сема- форов и блокирующих вызовов, создающих значительные непроизводительные из- 3 Возможно, здесь ошибка — включение tcpjsiodelay как раз означает отключение алго- ритма Nagle, то есть выключение режима буферизации данных при пересылке, чему соответ- ствует установка параметра no_nagle в True. — Прим. науч. ред.
Гпава 36. Конфигурация и специальные возможности 777 держки. Вместо этого планировщик потоков поддерживает потоки в пуле. Когда по- ток завершит свою задачу, он отмечается как свободный. Незанятый поток разруша- ется (или закрывается) после п итераций цикла планировщика, где п— значение па- раметра DeadThreadsCollection. Для сервера, обрабатывающего очень большое количество соединений, — сотни и более — значение этого параметра должно быть больше его значения по умолча- нию 50. GuardianOption Версия 1.5 и выше, только для Windows. Этот логический параметр используется на серверах Windows для задания, будет ли Guardian перезапускать сервер каждый раз, когда он завершится аварийно. Значение по умолчанию при инсталляции требует этого (1 = True). Для отмены рестарта от- ключите этот параметр (0 = raise). IpcMapSize Версия 1.5 и выше. server_client_mapping Версии, предшествующие Firebird 1.5. Это размер в байтах клиентской порции отображаемого в памяти файла, используе- мого для IPC в модели соединения, применяемой для "локального соединения Win- dows". Параметр не имеет эквивалента для других платформ и не оказывает никакого влияния на сетевые соединения. Значением является целое число от 1024 до 8192. Значение по умолчанию 4096. Увеличение значения может улучшить производительность при поиске очень боль- шого набора строк данных, такого как графические blob. < ПРИМЕЧАНИЕ. Если Firebird выполняется как приложение, эта установка также может быть изменена в диалоге системной панели Guardian. Остановите и запус- ____ тите сервер, чтобы новые установки начали действовать. IpcName Версия 1.5 и выше, только для платформ Windows. Значение по умолчанию Firebirdipi. Это имя разделяемой области памяти, используемой как транспортный канал в ло- кальном протоколе. Значение по умолчанию в версии 1.5 Firebirdipi не совместимо ни со старыми вер- сиями Firebird, ни с InterBase. При необходимости используйте значение interBaseiPi для сохранения совместимости с существующим приложением, которое ссылается на разделяемую память (пространство IPC) по имени.
778 Часть VIII. Безопасность MaxUnflushed Writes Версия 1.5 и выше. Применимо только для серверов Windows. Этот параметр был введен в версии 1.5 для обработки ошибок в операционных сис- темах сервера Windows, из-за которых асинхронный вывод никогда не записывался на диск, за исключением случая, когда сервер Firebird закрывался. (Асинхронный вывод не поддерживается в Windows 9х и ME.) Поэтому в системах 24/7 (работаю- щих 24 часа в сутки, 7 дней в неделю) асинхронный вывод вообще никогда не вы- полнялся. Этот параметр определяет, как часто требующие вывода страницы будут записывать- ся на диск при отключенном режиме Forced Writes (включен асинхронный вывод). Его значением является целое число, которое определяет максимальное количество требующих вывода страниц, при достижении которого отмечается необходимость вывода после того, как в следующий раз будет подтверждена транзакция. Значение по умолчанию 100 для инсталляций Windows и -1 (отключено) для всех других платформ. Если конец цикла MaxunfiushedwriteTime (см. следующий раздел) будет достигнут до того, как счетчик ожидающих вывода страниц достигнет значения Maxunfiushedwrites, флаг вывода будет отмечен немедленно, а счетчик количества ожидающих вывода страниц будет установлен в ноль. MaxUnflushed Write Time Версия 1.5 и выше. Применимо только для серверов Windows. Этот параметр определяет максимальное значение промежутка времени, через кото- рый ожидающие вывода страницы при асинхронном выводе будут записываться на диск при отключенном режиме Forced Writes (включен асинхронный вывод). Его значением является целое число, которое задает интервал в секундах между послед- ним выводом на диск и установкой флага для выполнения вывода после того, как в следующий раз будет подтверждена транзакция. Значение по умолчанию 5 секунд для инсталляций Windows и -1 (отключено) для всех других платформ. PrioritySwitchDelay Версия 1.5 и выше, только для Windows. Устанавливается для планировщика потоков в Windows. Целое число задает время в миллисекундах, которое должно пройти, прежде чем приоритет неактивного потока будет уменьшен до low (низкий) или приоритет активного потока будет увеличен до high (высокий). Одна итерация по этой последовательности переключений представ- ляет один цикл планировщика потоков. Значением по умолчанию является 100 миллисекунд; это время выбрано на основа- нии экспериментов с процессорами Intel РШ/Р4. Для процессоров с меньшим быст- родействием потребуется большее время задержки.
Глава 36. Конфигурация и специальные возможности 779 PriorityBoost Версия 1.5 и выше, только для Windows. Это целое число задает количество дополнительных циклов, предоставляемых пото- ку, когда его приоритет переключается на high. Значение по умолчанию при инстал- ляции 5. Process PriorityLevel Версия 1.5 и выше, только для платформ Windows. Это параметр для установки приоритета уровень/класс для серверного процесса; он заменяет параметр server_priority_ciass в версиях, предшествующих 1.5, новой реа- лизацией. Значением параметра является целое число: ♦ 0: обычный приоритет; ♦ положительное значение: высокий приоритет (то же самое, что и переключатель -в[oostPriority] в режимах configure и start у instsvc.exe); ♦ отрицательное значение: низкий приоритет. С ПРИМЕЧАНИЕ. Все изменения этого значения должны быть тщательно протес- тированы, чтобы убедиться, что они действительно приводят к соответствующей ____ реакции сервера на запросы. server_priority_ class Версии, предшествующие Firebird 1.5. Этот параметр назначает класс приоритета для сервиса Firebird только в Windows NT или Windows 2000. Возможными значениями являются: 1 = низкий приоритет, 2 = высокий приоритет. Значение по умолчанию 1. RemotePipeName Версия 1.5 и выше, применим только на платформах Windows для соединений име- нованных каналов (named pipes). Этот строковый параметр является именем канала, используемого в качестве транс- портного канала для сетей Windows именованных каналов. Именованный канал явля- ется эквивалентом номера порта в TCP/IP. Значение по умолчанию, interbas, совмес- тимо с предыдущими реализациями Firebird и с InterBase. server_working_size_maxи server_working_size_min Версии, предшествующие Firebird 1.5. Это два устаревших параметра, унаследованные от предыдущих версий Firebird. Сейчас они не поддерживаются и исключены из firebird.conf.
780 Часть VIII. Безопасность Параметры совместимости CompleteBooleanEvaluation Версия 1.5 и выше. Устанавливает метод вычисления логических значений (полный или сокращенный). Значение по умолчанию (0 = False) задает сокращенное вычисление выражений, со- держащих предикаты and или or, при котором возвращается результат "истина" или "ложь", когда дальнейшее вычисление оставшейся части выражения уже не повлияет на результат. При весьма редких (обычно исключаемых) условиях может произойти такое, что операция внутри условий and или ок, которая не будет вычисляться при сокращенном поведении, может потенциально повлиять на окончательный результат. Если у вас такая беда, и вы получили приложение, которое использует такие характеристики в логике SQL, вам понадобится использование этого параметра для обеспечения пол- ного вычисления, пока у вас не появится удобный случай хирургического вмеша- тельства в эту ситуацию. Это параметр логического типа. Д ВНИМАНИЕ! Не рассматривайте этот факт как флаг, который влияет на все ло- гические вычисления, выполняемые в любых базах данных на сервере. OldParameterOrdering Версия 1.5 и выше. Версия 1.5 учитывает старую ошибку InterBase, которая приводит к тому, что выход- ные параметры возвращаются клиенту в структуре xsqlda в порядке, вызывающем у пользователя аллергию. Эта ошибка сохранялась столь долго, что многие сущест- вующие приложения, драйверы и интерфейсные компоненты имели встроенные средства для корректировки данной проблемы на клиентской стороне4. Версия 1.5 и более поздние учитывают наличие корректировок в API и инсталлиру- ются С OldParameterOrdering = 0 (False). Установите ЭТОТ логический параметр В True (1), если вам нужно вернуться к старым условиям для совместимости с существую- щим кодом. Параметры, связанные с внешними объектами Выбор и установки значений параметров для модулей внешних кодов и файлов дан- ных обсуждаются в конце разд. "Конфигурирование внешних размещений". 4 Здесь речь идет о порядке обработки параметров в запросах с подзапросами, содержащих параметры как во внешней части запроса, так и в подзапросе. В старых версиях InterBase и Firebird параметры заполнялись сначала для внешнего запроса, а потом для подзапроса, хотя по тексту запроса они располагаются наоборот. — Прим. науч. ред.
Гпава 36. Конфигурация и специальные возможности 781 Работа со встроенным сервером Встроенный сервер для Windows имеет те же возможности, что и Суперсервер, за исключением поддержки множества пользователей и защиты по паролю. Клиентская библиотека включена в сервер, и эта объединенная пара выполняет работу как клиен- та, так и сервера для одного и только одного подключенного приложения. С ПРИМЕЧАНИЕ. Встроенный сервер для Windows не поддерживается в версиях, предшествующих версии 1.5. Когда встроенный сервер отыскивает корневой каталог его инсталляции, он игнори- рует любые записи в реестре и переменную окружения firebird. Он трактует как корневой тот каталог, в котором размещается его двоичный файл (fbembed.dll, пере- именованный в fbclient.dll или gds32.dll). Вы должны иметь полный набор файлов, требуемых для встроенного сервера, в кор- невом каталоге каждого встроенного приложения. Если используются внешние фай- лы (поддержка международных языков, библиотеки UDF, библиотеки фильтров blob), встроенному серверу нужно найти в этом корневом каталоге или ниже firebird.conf, а в firebird.conf параметр RootDirectory должен указывать на каталог, в котором располагается файл библиотеки встроенного сервера. См. пример в разд. "Встроенный сервер" главы 1. Запуск встроенного сервера Единственным допустимым протоколом соединения является "локальный Windows". Встроенный сервер не поддерживает локальный loopback-протокол или любой из сетевых протоколов. Поставляемое приложение хорошо сконфигурировано, и у сервера не будет конфлик- тов с другими запущенными серверами Firebird или клиентами. Серверный процесс будет запущен, как только приложение успешно соединится с локальной базой дан- ных. Приложения Любое приложение, которое уже работает с полным сервером и локальным или уда- ленным клиентом, будет нормально работать и со встроенным сервером. Четыре де- тали вы должны учесть в ваших существующих приложениях: ♦ размещение и именование библиотеки встроенного сервера; ♦ жестко закодированный путь к базе данных; ♦ написанные вами утилиты, которые используют удаленный менеджер сервисов; ♦ безопасность и целостность как следствие того, что интерфейс приложения не проверяет полномочия пользователя по доступу к серверу.
782 Часть VIII. Безопасность Размещение и переименование библиотеки Для встроенного сервера— распространяемого как fbembed.dll— нет проблем с пе- реименованием этой библиотеки в имя gds32.dll или fbclient.dll, или любое другое нужное имя. Для того чтобы пакет встроенного сервера был самодостаточным, эта библиотека должна находиться в том же каталоге, что и выполняемый модуль при- ложения, а дополнительные файлы и каталоги для функций сервера размещались так, как рекомендовано в главе 1. Если у вас на одной и той же машине много приложений встроенного сервера, кото- рым нужна для использования эта библиотека, то существует несколько вариантов. ♦ Поместите копию библиотеки в корневой каталог каждого приложения и размес- тите дополнительные файлы и каталоги так, как рекомендовано в главе 1. Это наиболее предпочитаемый вариант, т. к. он делает "пакет" простым для поставок при инсталляции и независимым от структуры существующей файловой системы. При этом если вы хотите устанавливать множество пакетов встроенного сервера на одной и той же рабочей станции, то появится проблема избыточности. ♦ Поместите единственную копию библиотеки в некоторый специальный ката- лог— с дополнительными файлами и каталогами, корректно поименованными и размещенными относительно этой библиотеки — и создайте в реестре ключ, ко- торый будет читать каждое приложение в его аргументы загрузки библиотеки. Возможно, это менее привлекательно с точки зрения перспектив переносимости, но это упростит проблемы конфликтов и обновления. ♦ Поместите библиотеку— подходящим образом именованную— в системный каталог и используйте переменную пути firebird для указания корня древовидной структуры, где расположены дополнительные файлы и каталоги. Этот вариант будет работать только в системе, где не выполняется полный сервер Firebird вер- сии 1.5 или выше. К тому же он повышает вероятность перезаписи этой библио- теки другими инсталляторами. JF ПРИМЕЧАНИЕ. Помните, что некоторые продукты Borland жестко запрограмми- рованы на распознавание внутренних версий строк только для Borland. Даже если \ \ имя и размещение в файловой системе являются “корректными" для элементов Borland, ограничение строки версии может сделать ваше приложение несовмес- тимым со встроенным сервером. Например, приложения, скомпилированные с использованием оригинальных компонентов InterBaseXpress (IBX), не будут со- единяться cfbembed.dll без внесения изменений. Жестко закодированные пути к базе данных Строка соединения, например, WlNSERVER:C:\Program Files Firebird Firebird I 5 \employee.fdb, жестко закодированная в вашем приложении, явно вызовет проблемы при установке вашего программного обеспечения на другой машине. Ваш код дол- жен адаптироваться к размещению базы данных, что неизвестно во время проектиро- вания и часто ограничено локальным (не localhost) соединением. Это не является
Гпава 36. Конфигурация и специальные возможности 783 проблемой, появившейся в случае со встроенным сервером. Нам часто нужно по- ставлять наше программное обеспечение приложений клиент/сервер с предоставле- нием пользователям или системным администраторам возможности конфигурирова- ния размещения баз данных в сети и в файловой системе. Система алиасов баз данных позволяет вам компилировать приложения с "мягкими" путями файловой системы к базе данных. Каждый раз, когда код приложения ссыла- ется на сегмент пути в строке соединения, то используется алиас, и определение раз- мещения в файловой системе становится задачей поиска установок в aliases.conf. Предположим, например, что вы решили использовать empdata в качестве алиаса. В файле aliases.conf на вашей машине разработки вы указали для алиаса ваш путь: EMPDATA = C:\Program Files\Firebird\Firebird_l_5\employee.fdb На другой машине это может быть: EMPDATA = D:\databases\employee.fdb Это решает вопрос с сегментом пути и исключает один уровень сложности. Причем у вас еще может остаться проблема с протоколом соединения. Могут потребоваться некоторые изменения, если приложение жестко закодировано на имя хоста и формат строки для соединения TCP/IP или именованных каналов. Утилиты удаленных сервисов Утилиты, использующие Services API или старые переключатели менеджера серви- сов для удаленного администрирования, должны быть отключены или адаптированы к работе с локальным протоколом. Конкретные меры будут зависеть от того, как реа- лизована ваша архитектура клиент/сервер. Если ваше существующее приложение предоставляет возможности, которые обеспечивают безопасность и стабильность сервера, вам также нужно рассмотреть реализацию обращения приложения к серверу, где по существу обходится идентификация пользователя. Вопросы безопасности сервера Любая библиотека встроенного сервера, размещенная на машине, где находится и база данных, потенциально является Троянским конем. На уровне сервера средства безопасности оперируют в предположении, что любой пользователь, соединяющийся с базой данных, будет идентифицироваться через базу данных безопасности security.fdb. Однако, когда встроенный сервер подключает клиента, идентификация пользователя с его паролем пропускается. Поскольку любой пользователь способен соединиться с любой базой данных, безопасность сервера будет легко отменена, если только серверная машина не будет защищена физически. Можно написать приложение встроенного сервера, которое "соединится как пользо- ватель SYSDBA", используя вообще что угодно в качестве пароля SYSDBA. Не су- ществует никакого способа, чтобы сервер сообщил, что пользователь SYSDBA "прошел через стеклянный потолок" без допустимого пароля. Если SYSDBA активен в любой базе данных, то этот пользователь сможет соединяться и вытворять все, что
784 Часть VIII. Безопасность ему захочется без каких-либо ограничений. Любой, кто имеет доступ к вашей сети и имеет привилегии к файловой системе на сервере, может установить злонамеренное приложение встроенного сервера, которое сможет читать и писать любые данные в ваши базы данных или вовсе их удалить. Помните, что база данных безопасности (security.fdb) является такой же базой дан- ных, как и другие. Пользователь SYSDBA имеет к ней специальные привилегии SQL — как и ко всем базам данных — для создания, модификации и удаления чего угодно. Подготовленная машина для баз данных защищена от физических вторжений и неав- торизованного доступа к файловой системе, "вошедший" в базу данных пользова- тель, не являющийся SYSDBA, будет субъектом обычных ограничений привилегий SQL. Привилегии SQL к объектам данных в базах данных Firebird применяются на основе "участия"— никакой пользователь, за исключением SYSDBA и владельца базы данных, не имеет автоматических прав к любым объектам любых баз данных. Поэтому все еще необходимо предоставить способ для пользователя (или приложе- ния) передавать имя пользователя и, желательно, роль обычной процедуре подклю- чения. Разработчики должны предоставлять и привилегии защиты, и охранную про- цедуру. См. главы 34 и 35 по вопросам безопасности доступа к серверу и привилегиям SQL к объектам базы данных. Совместимость нескольких серверов Любое количество приложений встроенного сервера может выполняться одновре- менно без каких бы то ни было конфликтов. Однако невозможно нескольким встро- енным серверам иметь одновременный доступ к одной базе данных по причине бло- кировки, применяемой в архитектуре Суперсервера при первом успешном подклю- чении. Обычные серверы Firebird и InterBase могут без конфликтов одновременно выпол- няться на машине, на которой запущены встроенные серверы. Для доступа локально- го клиента следует обратить внимание на исключение конфликта пространства имен между обычными клиентскими библиотеками (gds32.dll и fbclient.dll) и именем, вы- бранным для библиотеки встроенного сервера. "Клиентская" часть fbembed.dll может быть обычным, удаленным клиентом других серверов; в то же самое время она внутренне связывается со своим встроенным сер- вером. Останов встроенного сервера Выполнение встроенного сервера не может быть остановлено кроме как при завер- шении клиентского приложения. Ваше приложение должно завершаться с обычным завершением транзакций и отключением от всех баз данных.
Глава 36. Конфигурация и специальные возможности 785 Модули внешних кодов Firebird может расширить свои возможности путем доступа к определенным пользо- вателям подпрограммам, написанным на включающем языке программирования и скомпилированным во внешние библиотеки общего доступа. Этот раздел содержит рассмотрение некоторых вопросов и техник, относящихся к написанию внешних функций (UDF) и фильтров blob. ♦ Внешние функции: Firebird "путешествует налегке" в отношении встроенных функций. Вместо того чтобы навешивать на сервер огромную библиотеку скры- тых функций, Firebird позволяет разработчикам выбрать — и, при необходимости, определить— их собственные библиотеки внешних функций, которые соответст- вуют потребностям вычислений и выражений в их базах данных. Функции, опре- деленные пользователем (User-Defined Function, UDF), добавляют высокую гиб- кость в среду вашей базы данных. UDF являются расширением функций сервера Firebird на серверной стороне; они объявляются для баз данных и выполняются в контексте серверного процесса. Как и стандартные встроенные функции SQL, UDF могут быть разработаны для выполнения конвертирования данных или для вычислений, которые сложно или вообще невозможно выполнить в языке SQL. UDF включают функции для стати- стики, строк, дат и математических вычислений. В разд. "Внешние функции" главы 21 подробно объясняется, как размещать, объ- являть и использовать внешние функции, а также как сконфигурировать их раз- мещение в файловой системе, чтобы исключить некоторые риски безопасности и целостности, существующие при выполнении внешнего кода вместе с ядром сер- вера. Конфигурация по умолчанию и режимы файловой системы также обсужда- лись ранее в этой главе при рассмотрении параметров udfAccess (версия 1.5) и external_file_directory (версия 1,0.x). ♦ Фильтры BLOB: сервер Firebird использует несколько внутренне определенных подпрограмм для преобразования байтовых потоков из одного формата в другой. Эти подпрограммы называются фильтрами blob. Обработчик SQL представляет их в DDL и в метаданных в виде подтипов blob. Существует также возможность писать свои собственные фильтры blob для преобразования данных blob из одного формата в другой совместимый формат. Например, фильтр blob может конверти- ровать формат XML в формат RTF или изображение BMP в JPEG. Разработка ваших собственных UDF Библиотеки UDF компилируются как стандартные библиотеки совместного исполь- зования и выполняются на сервере, где размещена база данных. Библиотеки динами- чески загружаются базой данных во время выполнения, когда на библиотеку ссыла- ется выражение SQL. Вы можете создавать библиотеки UDF на любой платформе, которая поддерживается в Firebird. Для использования одного и того же набора биб- лиотек UDF в базах данных, выполняющихся на различных платформах, создайте и
786 Часть VIII. Безопасность скомпилируйте отдельные библиотеки для каждой платформы, где располагается база данных. Библиотека в этом контексте является совместно используемым объектом, который обычно имеет расширение dll на платформе Windows, расширение so для UNIX и Solaris и расширение si на HP-UX. Она может содержать одну или более точек входа для функций, определенных пользователем. СОВЕТ. Каталог Firebird /examples содержит некоторые старые примеры make- файлов (makefile.be и makefile.msc для систем Windows, makefile для UNIX), кото- рые создают библиотеку ib_udf function из ib_udf.c. Процесс создания UDF состоит из четырех шагов: 1. Напишите функцию на любом языке программирования, который может созда- вать библиотеки совместного использования. Функция может принимать ограни- ченное количество входных параметров, но она должна возвращать один и только один результат. Функции, написанные на Java, не поддерживаются. 2. Скомпилируйте функцию и свяжите с динамически связываемой библиотекой (DLL) или с библиотекой совместно используемых объектов (SO) в соответствии с платформой. 3. Поместите библиотеку и любые требуемые символические ссылки в подходящее место на диске на серверной машине так, чтобы сервер мог получить к ней дос- туп— в каталог по умолчанию /UDF или в альтернативный каталог, который вы сконфигурировали для библиотек внешних функций. 4. Используйте declare external function для объявления каждой индивидуальной UDF для каждой базы данных, в которой вам нужно ее использовать. < ПРИМЕЧАНИЕ. Очень хорошей практикой является создание скрипта, содержа- щего объявления ваших UDF и некоторых комментариев, объясняющих исполь- ____ зование. Написание модуля функции На языке С UDF пишутся как любые стандартные функции. UDF может получать до десяти входных параметров и должна возвращать одно и только одно значение дан- ных в качестве результата. Исходный код модуля может определять одну или более функций. Если вы включите заголовочный файл Firebird ibase.h в ваш каталог Firebird /include при компиляции, ваш модуль С или C++ сможет использовать имеющиеся в нем определения типов (typedef). Возможности трансляции существуют и для других языков, включая Delphi. Например, исходный пакет для FreeUDFLib5 от Gregory Deatz включает ibase.pas. 5 Общая библиотека UDF доступна на нескольких сайтах, включая http://www.ibphoenix.com.
Гпава 36. Конфигурация и специальные возможности 787 Задание параметров Параметры, не являющиеся blob или массивами, передаются функциям UDF либо по ссылке6 с использованием типов данных включающего языка, допускающих преоб- разование в соответствующие типы данных Firebird, либо через дескриптор, исполь- зуя предварительно определенную структуру, которая описывает тип данных Firebird во включающем языке. Может быть принято до десяти параметров, соответствующих любому типу данных Firebird за исключением массива или элемента массива. Если UDF возвращает blob, то количество входных параметров ограничивается девятью. В качестве примера передачи параметров ио ссылке можно рассмотреть объявление функции в языке С fn_abs(), которая принимает один параметр, имеющий в С тип double. Когда вызывается fn_abs(), ей должен передаваться параметр, имеющий в SQL тип данных double precision. Передача параметров через дескриптор появилась в Firebird 1.0, что позволяет пере- давать родные типы данных Firebird. Для некоторых внешних функций это упрощает обработку параметров null и дает возможность перекрывать объявления. Структуру дескриптора параметра можно найти в ibase.h: typedef struct paramdsc { unsigned char signed char ISC_USHORT short ISC_USHORT unsigned char } PARAMDSC; dsc_dtype; dsc_scale; dsc_length; dsc_sub_type ; dsc_flags; *dsc address; Самое важное поле в структуре, конечно, dsc dtype, потому что оно ответственно за трансляцию родных типов данных Firebird в типы данных включающего языка. СОВЕТ. Claudio Valderrama С., который выполнил реализацию передачи через дескриптор, представил детальное описание этого процесса в статье "Using descriptors with UDFs" (Использование дескрипторов в UDF) на http:// www.cvalde.net/document/using_descriptors_with_udfs.htm. Параметры BLOB Функции UDF, которые принимают параметры blob, требуют для обработки специ- альной структуры данных, blob передается по ссылке в управляющей структуре blob, описанной в разд. "Написание функций BLOB". Спецификация возвращаемого значения К типам данных возвращаемых значений применяются те же самые ограничения, что и к входным параметрам: типы данных включающего языка должны соответствовать 6 В терминах C++ "но указателю".
788 Часть VIII. Безопасность типам данных Firebird. Например, объявление в С для функции fn_abso возвращает значение типа double, который корреспондирует с типом данных double precision в Firebird. По умолчанию возвращаемые значения передаются по ссылке. Числовые значения могут также возвращаться по значению, хотя это и не очень хороший метод, и он не рекомендован. Для возврата числового параметра по значению включите необяза- тельное ключевое слово by value после возвращаемого значения при объявлении UDF в базе данных. Символьные типы данных В UDF необходимо использовать типы данных включающего языка как для входных, так и для выходных значений. Firebird должен иметь возможность выполнить транс- ляцию между объявленным типом и типом данных SQL. В случае строк входной строкой UDF является тип cstring с заданной максимальной длиной в байтах, cstring используется для трансляции входных типов char и varchar в строку языка С, завер- шающуюся нулем, для обработки и для возвращения строки языка С переменной длины, завершающуюся нулем, для автоматического преобразования в Firebird в ти- пы char или varchar. В случае других включающих языков убедитесь, что ваша функ- ция возвращает строки, завершающиеся нулем. При объявлении UDF, которая возвращает строку языка С, char или varchar, в объяв- ление должно быть добавлено ключевое слово free_it для освобождения памяти, используемой для возвращаемого значения, если она была выделена с помощью функции ib_util_malloc. Соглашения о вызовах Соглашение о вызовах определяет, как вызывается функция и как передаются пара- метры. Функция, получающая вызов, должна быть совместима с соглашением о вы- зове CDECL, используемым в Firebird. В функциях на языке С при использовании соглашения о вызове CDECL в объявление функции должно быть добавлено зарезер- вированное слово_cdeci. В языке Pascal используйте cdeci. Вот пример на языке С, где задается CDECL: ISCJTIMESTAMP* _cdeci addmonth(ISC_TIMESTAMP *preTime) ( // здесь тело функции } Вопросы поточной обработки В реализации Суперсервера в Firebird сервер выполняется как один многопоточный процесс. Это означает, что вам нужно проявлять некоторую осторожность при ис- пользовании способов выделения и освобождения памяти при кодировании UDF, а также при объявлении UDF. При использовании памяти в одном процессе в многопо- точной архитектуре необходимо рассмотреть некоторые вопросы.
Гпава 36. Конфигурация и специальные возможности 789 ♦ Функции UDF должны выделять память с использованием функции ib utii_ maiioc из библиотеки ib_util, а не с помощью статических массивов. ♦ Выделенная динамически память не освобождается автоматически, пока процесс не завершится. Вы должны использовать зарезервированное слово free it при объявлении UDF в базе данных (declare external function). ♦ Статические переменные не сохраняются в потоке. Пользователи, выполняющие параллельно одну и ту же UDF, будут конкурировать, если они обратятся к одно- му пространству статической памяти. Неразумно использовать статические пере- менные, если вы не можете гарантировать, что только один пользователь в каж- дый конкретный момент времени будет обращаться к этой функции. Если вы не можете исключить возвращаемый указатель на статические данные, вы не должны использовать free_it. Библиотека ib_util Функция ib_utii_maiioc находится в вашем каталоге Firebird /lib, в совместно исполь- зуемой библиотеке ib util.so в POSIX, ib_util.dll в Windows и ib util.sl в HP-UX. Про- тотип функции для С и Pascal содержится в каталоге /include в файлах ib util.h и ib util.pas соответственно. Указатели на переменные в Классическом сервере При не поточном использовании Классического сервера вы можете возвращать гло- бальный указатель. В следующем примере функции fn lowero массив должен быть глобальным, чтобы исключить выход за пределы контекста: char buffer[256]; char *fn_lower(char *ups) { return (buffer); } Создание UDF, защищенных от утечки памяти При распределении памяти под возвращаемые значения для обеспечения потоко- безопасности и переносимости следует использовать функцию ib_utii_maiioc (). Вместе с ней нужно применять также ключевое слово free_it в предложении returns при объявлении функции, которая возвращает динамически создаваемые объекты. В следующем примере сервер Firebird освободит буфер, если UDF была определена с зарезервированным словом free it. Обратите внимание, что этот пример использует функцию Firebird ib_utii_maiioc () для выделения памяти: char *fn_lower(char *ups) { char ^buffer = (char *) ib_util_malloc(256);
790 Часть VIII. Безопасность return (buffer); } Вот ее объявление: DECLARE EXTERNAL FUNCTION lowercase VARCHAR (25 6) RETURNS CSTRING(256) FREE_IT ENTRY POINT 'fn_lower' MODULE_NAME 'ib_udf'; < ПРИМЕЧАНИЕ. Память должна освобождаться той же библиотекой времени выполнения, которая выделяла эту память. Замечания по компиляции и компоновке Когда модуль UDF готов, компилируйте его обычным образом в объектный или биб- лиотечный формат. Включите ibase.h или его эквивалент, если вы используете в нем объявления типов. Если сборка выполняется статически, свяжите модуль с клиентской библиотекой Firebird, если вы обращаетесь к какой-либо функции библиотеки Firebird. Для Micro- soft Visual C/C++ библиотеки fbclient_ms.lib и ib_util_ms.lib могут быть найдены в каталоге Firebird /lib. Изменение библиотеки функций Для включения UDF в существующий модуль внешней функции добавьте файл, со- держащий объектный код новой UDF, и перекомпилируйте как обычно. Некоторые платформы позволяют добавлять объектные файлы непосредственно в существую- щие библиотеки. Относительно подробностей посмотрите документацию по компи- лятору и редактору связей для конкретной платформы. Для удаления функции следуйте инструкциям редактора связей по удалению объек- тов из библиотеки. Удаление функции из библиотеки не удаляет ее объявления из базы данных — используйте для этого drop external function. Написание функции BLOB Функции blob отличаются от других внешних функций, потому что им передаются указатели на управляющие структуры blob, а не на фактические данные. Функция не может открывать и закрывать blob, а вместо этого вызывает функции API для выпол- нения доступа к blob. Создание управляющей структуры BLOB Управляющая структура blob является структурой языка С, определенной в функ- циональном модуле в виде typedef. Программисты должны описать такую управ- ляющую структуру на языке С, т. е. должны написать следующее:
Гпава 36. Конфигурация и специальные возможности 791 typedef struct blob { short (*blob_get_segment) (); isc_blob_handle blob_handle; long number__segments; long max_seglen; long total_size; void (*blob_put_segment) (); } *Blob; Табл. 36.1 описывает поля в управляющей структуре blob. Таблица 36.1. Поля в управляющей структуре blob7 Поле Описание bl ob_ge t_s е gme n t null, если внешняя функция не принимает blob в качестве входного аргумента. Иначе это поле является указателем на функцию, которая вызывается для чтения сегмента blob. Данная функция получает че- тыре аргумента: дескриптор blob, адрес буфера для сегмента blob, размер буфера и адрес переменной для хранения размера сегмента BLOB blob_handle Требуемое поле. Это дескриптор blob, который уникально идентифи- цирует blob, передаваемый функции или возвращаемый функцией numbe r_s e gme nt s null, если внешняя функция не принимает blob в качестве входного аргумента. В противном случае задает общее количество сегментов в BLOB max_seglen null, если внешняя функция не принимает blob в качестве входного аргумента. В противном случае задает в байтах размер наибольшего передаваемого сегмента total_size null, если внешняя функция не принимает blob в качестве входного аргумента. В противном случае задает в байтах фактический размер всего blob как единого целого blob_put_segment null, если внешняя функция не принимает blob в качестве входного аргумента. В противном случае содержит указатель на функцию, кото- рая вызывается для записи сегмента в blob. Эта функция принимает три аргумента: дескриптор blob, адрес буфера, содержащего данные для записи в blob, и размер в байтах записываемых данных Объявление функции BLOB Функция blob объявляется в базе данных с использованием declare external function с тем отличием, что тип ее объявления помещается перед ключевым словом returns 7 Определена в ibase.h как blobcallback (структурный тип) и blobcallback (указатель на структурный тип). Это объявление дает полное определение для функций чтения/записи указа- телей, которые упрощают использование их в современных компиляторах. См. также приме- чания перед объявлением в ibase.h.
792 Часть VIII. Безопасность в качестве последнего аргумента в списке параметров вместо возвращаемого значе- ния. Для аргумента returns используйте ключевое слово parameter и порядковый но- мер последнего параметра. Например, следующий оператор объявляет функцию blob, biob_pius_biob в модуле внешних функций с именем MyExtLib: DECLARE EXTERNAL FUNCTION blob_plus_blob Blob, Blob, Blob RETURNS PARAMETER 3 ENTRY_POINT 'blob_concat' MODULE_NAME 'MyExtLib'; Дополнительная информация Учебник по написанию внешних функций доступен на сайте сообщества Firebird. Множество толковых статей можно найти в базах знаний и через поисковые машины. Фильтры BLOB В главе 12 мы коснулись специального типа внешних функций, которые могут быть использованы в Firebird для преобразования данных blob между двумя форматами, способными представлять совместимые данные. Фильтры blob являются определен- ными пользователем служебными подпрограммами на стороне сервера — фактиче- ски, специализированными UDF — способными получать данные blob в одном фор- мате, преобразовывать их и возвращать в виде blob другого формата. Один раз ском- пилированный и объявленный в базе данных, фильтр blob может быть использован в обычных операторах DML в клиентских приложениях, хранимых процедурах, триг- герах и в isql. В SQL Firebird фильтр blob распознается по его подтипу sub type. Вы уже хорошо знакомы с двумя подтипами: 0 (для blob любого формата) и 1 (для неформатирован- ного или минимально форматированного текста). Они предварительно определены внутренне вместе с множеством других, которые все являются положительными чис- лами, их Firebird использует внутренне для метаданных и синтаксического разбора. Другие фильтры blob определяются пользователем, и могут иметь любое отрица- тельное число в качестве своего подтипа. Сервер Firebird не имеет никакого "внутреннего знания" о том, что хранится (или может храниться) в blob заданного подтипа: ноль или меньше нуля. Задача приложе- ния — обеспечить, чтобы вход и выход соответствовали целям подтипов, и какие фильтры blob будут написаны для их обработки. Пары фильтров blob могут быть использованы для управления полезным диапазоном обычных преобразований данных, требуемых вашим приложениям, приведем при- меры. ♦ Упаковка и распаковка данных. Один подтип хранит упакованные данные, в то время как другой обрабатывает их в распакованном виде. Фильтр blob может быть
Глава 36. Конфигурация и специальные возможности 793 разработан для получения blob подтипа 1 (неизвестный формат) и преобразования его в сжатый формат (например, zip или гаг) и распаковки его для обработки как SUB_TYPE 0. ♦ Вы можете использовать один подтип для хранения собственного кода приложе- ния и другие для специфического системного кода и фильтр blob для добавления системных дополнений в собственный код, когда он требуется в запросе. ♦ Вы можете иметь подтип для хранения текста формата XML и фильтры blob для трансформации его в заданные выходные форматы — HTML, RTF, PDF, файлы UNIX, формат текстового процессора — для вывода в виде другого подтипа. Возможности фильтров blob в Firebird позволяют уменьшить "разбухание", связанное с преобразованием данных для внешних процессоров. Преобразование кода в под- программе фильтра может быть настолько простым или сложным, насколько это тре- буется. Фильтры при необходимости могут вызывать другие модули, предоставляя в ваше распоряжение возможность включения существующих конвертирующих под- программ в ваши операции на сервере. Поскольку сам сервер имеет дело лишь с вхо- дом и выходом, в то время как ваш код фильтра поддерживается внешне, ваши фильтры могут быть изменены с учетом новейших технологий без воздействия на сервер. Написание фильтров BLOB Написание фильтров blob требует точно таких же усилий по управлению памятью и потоками, а также того же порядка действий, что и другие внешние функции, а именно: 1. Напишите фильтры и скомпилируйте их в объектные коды. 2. Создайте совместно используемую библиотеку фильтров (совместно используе- мый объект или DLL). 3. Сконфигурируйте сервер Firebird, чтобы он знал, где отыскать библиотеку во время выполнения. 4. Используйте declare filter для объявления фильтров в базе данных. 5. Объявите столбцы в таблицах для хранения blob с тем подтипом, который "извес- тен" фильтру. 6. Пишите приложения или модули PSQL, которым требуется фильтрация. Объявление фильтров BLOB На уровне базы данных фильтр распознается при объявлении метаданных с исполь- зованием следующего синтаксиса: DECLARE FILTER <имя-фильтра> INPUT_TYPE <.подтип> /* идентифицирует подтип конвертируемого объекта */ OUTPUT_TYPE <подтип> /* идентифицирует подтип создаваемого объекта */ ENTRY_POINT '<имя-точки-входа>' /* имя экспортируемой функции */ MODULE_NAME '<имя-внешней-библиотеки>'; /* имя библиотеки фильтров BLOB */
794 Часть VIII. Безопасность Вызов фильтров BLOB Преобразование из input_type в output type происходит автоматически, когда вызы- вается модуль module name с параметрами, которые были определены с корректными номерами подтипов. Предположим, вы создаете библиотеку с именем filters.so или filters.dll, которая на- ходится в допустимом для библиотек фильтров blob месте на сервере. В этой библио- теке у вас есть функция которая принимает blob, принимает текст, разме- ченный как XML, передает его программе, которая преобразует его в документ RTF, и, наконец, возвращает результат в новый blob. Вы сохраняете документы XML в blob sub_type -ю, а тексты RTF в blob subjtype -15. Во-первых, вы должны объявить фильтр blob в базе данных: DECLARE FILTER XML2RTF /* ваш выбор имени */ INPUT_TYPE -10 /* текст, размеченный как XML */ OUTPUT_TYPE -15 /* текст RTF, форматированный по правилам компании */ ENTRY_POINT ' xml__to_rtf1 /* точка входа для подпрограммы преобразования */ MODULE_NAME 'filters1; /* имя библиотеки фильтров BLOB */ Теперь в вашем SQL или PSQL все, что вам нужно для автоматического вызова пре- образования, это переменная или столбец базы данных, определенный как blob sub type -io, содержащий ваш замечательный, правильный документ XML, и пере- менная или столбец подтипа -15 для получения конвертированного документа. СОВЕТ. Включенные в набор Firebird специальные внутренние фильтры blob выполняют преобразование подтипа 0 в подтип 1 (текст) и подтипа 1 в подтип 0. Эти фильтры также могут преобразовывать данные blob любого подтипа Firebird — например, blr=2 в подтип 1 (текст), что может быть полезным для по- лучения содержимого столбца системной таблицы в приложении управления базой данных. В подтипе 1 блоки сегментированы с первого символа до символа начала новой строки, со следующего символа до другого символа начала строки ит. д. Инструменты для написания фильтров API Firebird содержит группу макрофункций, начинающихся с isc_biob_fiiter_, ко- торые являются "инструментом программиста" для написания фильтров blob. Доку- менты API Guide и Embedded SQL Guide из набора документов Borland по InterBase версии 6 бета имеют разделы по написанию фильтров blob. Большое количество до- кументов, описывающих как это делать, можно при минимальных затратах отыскать на сайтах сообщества. API Firebird также содержит структуры — с их заголовками в ibase.h — для передачи и чтения информации относительно blob. Если у вас разыгралось любопытство, по- смотрите дескрипторы blob и буфер параметров blob (ВРВ). Группа связанных
Гпава 36. Конфигурация и специальные возможности 795 функций может быть использована в вашем коде фильтра blob, а также для прямого обращения к фильтрам blob из вашего кода приложения. Модули интернациональных языков Firebird поставляется с постоянно расширяющейся библиотекой поддержки интерна- циональных языков и последовательностей сортировки. Четыре "базовых" языка по- ставляются в библиотеке fbintl (для POSIX) или fbintl.dll (для Windows). Firebird оты- скивает языковые библиотеки в каталоге инсталляции /inti. Можно писать ваши собственные наборы символов и порядки сортировки и сделать так, чтобы сервер Firebird загружал их из библиотеки совместного использования, которая должна быть названа fbintl2, чтобы ее можно было распознать и загрузить. David Brookestone Schnepper, разработчик fbint!2, подготовил языковой пакет "сделай сам", свободно доступный на http://www.ibcollate.com. В Firebird 1.5 и выше также возможно реализовывать пользовательские наборы сим- волов и порядки сортировки с использованием внешних функций. Поскольку ком- плект поставки fbint!2 включает ясные инструкции по каждому набору символов, он также может служить справочником, если вы планируете использовать внешние функции для реализации пользовательских наборов символов. Конфигурирование внешних размещений Существующий внешний код и данные, к которым обращается сервер, могут ока- заться слабым местом в безопасности, если файловая система сервера неадекватно защищена от вторжений или хорошо видна из сети. Такие внешние фрагменты могут быть сделаны менее уязвимыми посредством конфигурирования ограничений при доступе к ним сервера Firebird. Возможность запрета доступа к нераспознаваемому размещению помогает в обеспечении безопасности файловой системы и сети. СОВЕТ. Конфигурируйте небольшое количество размещений для сокращения области поиска сервером и требуемого уровня управления доступом. Установки в файле конфигурации Файл конфигурации Firebird, как обсуждалось ранее в этой главе, дает возможность выполнить установки по ограничению доступа к библиотекам внешних функций, модулей фильтров blob и к файлам данных, связанных с таблицами с помощью опре- деления create table <1'1мя-таблицы> external (внешние таблицы или EVT). Установ- ки для Firebird 1.5 в файле firebird.conf отличаются от установок для Firebird 1.0.x в файле iscconfig (POSIX) или ibconfig (Windows). Конфигурация версии 1.5 применима ко всем моделям сервера 1.5. Конфигурация для 1.0.x применима только к Суперсерверу.
796 Часть VIII. Безопасность UDFAccess Версия 1.5 и выше, файл firebird.conf. Этот параметр используется для ограничения доступа к библиотекам внешних функ- ций и модулям фильтров blob, рассматриваемых как потенциальная мишень для зло- намеренных атак. Вы можете выбрать один из трех уровней доступа сервера ко всем этим модулям. До версии 1.5 возможность хранения внешних модулей в различных местах файловой системы рассматривалась как преимущество. Сейчас рекомендует- ся, чтобы их хранение ограничивалось одним деревом или, в особо незащищенных ситуациях, лучше вообще их запрещать. UDFAccess МОЖвТ быть None, Restrict ИЛИ Full. ♦ None запрещает любое использование определенных пользователем внешних биб- лиотек. Это значение по умолчанию инсталляции большинства дистрибутивов. ♦ Restrict (установка по умолчанию) ограничивает размещение вызываемых внеш- них библиотек указанными размещениями файловой системы. По умолчанию по- иск начинается в каталоге /UDF в вашем корневом каталоге Firebird. Для задания размещения библиотек внешних функций или модулей фильтров blob в другом месте файловой системы предоставьте список из одного или более каталогов, разделенных точкой с запятой (;), в которых могут размещаться эти модули, на- пример: POSIX: /db/extern;/mnt/extern Windows: С:\ExternalModules Относительные пути трактуются как пути относительно корневого каталога ин- сталляции Firebird. Например, в Windows, если инсталляция Firebird была в ката- логе C:\Program Files\Firebird\Firebird_l_5, то следующее значение будет ограни- чивать доступ сервера к внешним файлам, только если они будут размещаться в C:\Program Files\Firebird\Firebird_l_5\userdata\ ExternalModules: UDFAccess = Restrict userdata\ExternalModules ♦ Full позволяет осуществлять доступ к внешним библиотекам в любом месте сис- темы. Если задан доступ Full, то требуется включить полный путь в предложение MODULE_NAME Оператора DECLARE EXTERNAL FUNCTION, КОТОрЫЙ обЪЯВЛЯвТ фуНКЦИЮ В базе данных. Подробности см. в разд. "Внешние функции (UDF)" главы 21. external_function_directory Версия 1.0.x, файл isc_config/ibconfig. Этот параметр может быть использован в версии 1.0.x для задания произвольного количества размещений для библиотек внешних функций, фильтров blob и/или моду- лей наборов символов. Если этот параметр конфигурации не присутствует, Firebird проверяет подкаталоги ..\udf или ..\intl в каталоге, который сервер распознает как корневой каталог инсталляции Firebird.
Глава 36. Конфигурация и специальные возможности 797 Вот несколько примеров: external_function_directory <луть к- каталогу, заключенный в кавычкй> external_function_directory "/opt/firebird/my_functions" external_function_directory "/opt/extlibs/lang" external_function_directory "d:\udfdir" ExternalFileAccess Версия 1.5 и выше, файл firebird.conf. Этот параметр предоставляет три уровня безопасности в отношении доступа к внеш- ним файлам из таблиц базы данных. Значением является строка, которая может быть None, Full ИЛИ Restrict. ♦ None (значение по умолчанию) отключает любое использование внешних файлов вашим сервером. ♦ Restrict предоставляет возможность ограничить размещение внешних файлов для доступа из базы данных конкретными путями. Предоставьте список из одного или более путей, разделенных точкой с запятой (;), в которых могут храниться внеш- ние файлы, например: Unix: /db/extern;/mnt/extern Windows: C:\ExternalTables Относительные пути трактуются как пути относительно каталога, который распо- знается запущенным сервером как корневой каталог инсталляции Firebird. Например, в Windows, если корнем, который запущенный сервер распознает как корневой каталог инсталляции Firebird, является C:\Program Files\Firebird, то сле- дующее значение будет ограничивать сервер по доступу к внешним файлам, толь- ко если они будут размещаться на C:\Program Files\Firebird\userdata\ExternalTables: ExternalFileAccess = Restrict userdata\ExternalTables Следующая запись в POSIX позволит осуществлять доступ только к файлам, на- ходящимся в или ниже каталогов /exportdata и /importdata: ExternalFileAccess - Restrict /exportdata;/importdata ♦ Full позволят осуществлять доступ к внешним файлам, находящимся в любом месте системы8. Подробную информацию о внешних файлах см. в разд. "Использование внешних файлов в качестве таблиц" главы 16. 8 При отсутствии ограничений на размещение внешних таблиц уже давно известны воз- можности использовать внешние таблицы как для перекачивания базы данных с сервера на клиент, так и для внедрения с клиента на сервер вредоносных UDF. — Прим. науч. ред.
798 Часть VIII. Безопасность external_file_directory Firebird 1.0.x, файл ibconfig. Только в Windows используется для группирования внешних файлов в одной или нескольких ограниченных зонах. Нет ограничения на количество каталогов, которые могут быть представлены в списке поиска. Создавайте запись из одной строки для каждого каталога, как показано далее: external_file_directory <путь к каталогу, заключенный в кавычки> external_file_directory ”d:\x-files" Пора дальше Последние главы описывают использование оставшихся инструментов командной строки, которые поставляются вместе с Firebird. Инструмент статистики gstat был описан ранее в главе 18, с инструментом управления доступом пользователя gsec вы познакомились в главе 35. Теперь рассмотрим интерактивную утилиту SQL isql.
Инструменты Глава 37. Интерактивный SQL (утилита isql) Глава 38. Резервное копирование и восстановление баз данных (gbak) Глава 39. Утилита gfix Глава 40. Менеджер блокировок

ГЛАВА 37 Интерактивный SQL (утилита isql) Утилита isql, инсталлированная в каталоге /bin ниже вашего корневого каталога Fire- bird, предоставляет неграфический интерфейс для баз данных Firebird, который со- вместим со всеми серверными и клиентскими платформами. isql принимает операторы DDL и DML, так же как и подмножество подобных SQL консольных команд, недоступных в DSQL. Утилита может быть использована как для создания и поддержания метаданных, так и для запросов к базе данных и измене- ния данных. Она включает некоторые инструменты администратора и режимы для выполнения прямых операций с базой данных из командной строки или из скриптов. Д ВНИМАНИЕ! Некоторые другие СУБД используют имя "isql" для своих интерак- тивных программ. Всегда запускайте программу isql Firebird из ее собственного каталога или задавайте абсолютный путь к файлу, если есть проблемы с вашим сервером. Интерактивный режим Интерактивная утилита isql может быть запущена локально или с удаленного клиента. ♦ Для запуска isql с удаленного клиента всегда требуются имя пользователя и па- роль. ♦ Если вы соединены локально, вы можете установить переменные операционной системы ISC_USER И ISC^PASSWORD и исключить необходимость вводить имя поль- зователя и пароль в командах. Подробнее об этих переменных см. в главе 3. Jg ПРИМЕЧАНИЕ. При вызове интерактивной утилиты могут быть использованы L—некоторые дополнительные переключатели командной строки. Они представлены \ х в табл. 37.1 в конце этой главы. Текстовый редактор по умолчанию Некоторые команды isql обращаются к текстовому редактору системы по умол- чанию. 26 Зак. 420
802 Часть IX. Инструменты ♦ В UNIX, Linux и в некоторых других платформах POS1X редактор по умолчанию определяется одной из двух переменных окружения editor и visual. Значением по умолчанию при инсталляции обычно является vi, vim или emacs, но вы можете ус- тановить его в любой другой предпочитаемый консольный (но не X) текстовый редактор. ♦ В Windows похожая история. Редактор по умолчанию определяется переменной окружения editor. В текущих версиях Windows это Notepad.exe, но вы можете ус- тановить его в любой текстовый редактор. В очень старых версиях Windows ре- дактор по умолчанию, вероятно, был mep.exe или edit.exe. Запуск isql Для запуска isql откройте командную строку и перейдите в каталог Firebird /bin ва- шей инсталляции Firebird на сервере или клиенте. Наберите следующую команду в подсказке командной строки и нажмите клавишу <Enter>: isql [имя-базы-дапных] [~u[ser] <имя-пользователе -pas [sword] <пароль>] Имя базы данных не является обязательным. Если вы его указываете, то isql будет открывать соединение с указанной базой данных. Это должен быть либо полный путь к базе данных из того места, где вы находитесь, либо правильный алиас в версии 1.5 и более поздних. Переключатели -user <имя-пользователя> и -password <пароль> являются необяза- тельными, когда вы запускаете isql без указания связи с базой данных, и требуются, когда вы запускаете isql в удаленном режиме. Если переменные окружения isc user и isc password не установлены, имя пользователя и пароль требуются при старте isql локально. Запуск программы в POSIX: ./isql а в Windows: isql ./isql -user TEMPDBA -password osoweary [в POSIX] isql -user TEMPDBA -password osoweary [в Windows] запускается программа и сохраняются заданные имя пользователя и пароль без их идентификации. isql hotchicken:/data/mydatabase.fdb -user TEMPDBA -password osoweary Указанная команда запускает программу на клиенте Windows и соединяется с базой данных на сервере POSIX, предоставляя верные на сервере имя пользователя и па- роль. ./isql /data/mydatabase.fdb запускает локально программу на сервере Linux и соединяется с базой данных в предположении, что переменные окружения isc use? и isc_password установлены и доступны вашему пользовательскому профилю пользователя в Linux.
Гпава 37. Интерактивный SQL (утилита isql) 803 Если вы подключились к базе данных при запуске isql, вы увидите консоль, похожую на рис. 37.1. Конкретный вид окна командной строки зависит от операционной сис- темы. Командная строка isql одинакова для всех платформ. Рис. 37.1. Консоль, отображаемая при запуске isql с подключением к базе данных Если вы не ввели путь к базе данных или имя пользователя и пароль не определены на сервере, вы увидите нечто, похожее на рис. 37.2. Рис. 37.2. Консоль, отображаемая при запуске isql без подключения к базе данных Если были ошибки в командной строке или возникли проблемы с идентификацией пользователя, вы можете также увидеть сообщения об ошибке. Если вы видите под- сказку sdq>, значит вы находитесь в командной строке isql и можете из нее соеди- няться с базой данных. Соединение с базой данных Для соединения с базой данных из подсказки sql> командной строки isql используйте следующий пример в качестве образца синтаксиса. Обратите внимание, что синтак- сис внутри командной строки isql отличается от синтаксиса, используемого при пе- редаче параметров соединения и системной командной строки.
804 Часть IX. Инструменты CONNECT 'HOTCHICKEN:L:\DATA\EXAMPLES\EMPLOYEE.FDB' USER 'SYSDBA' PASSWORD 'masterkey'; соединяет с удаленным или локальным сервером с именем hotchicken. CONNECT 'L:\DATA\EXAMPLES/EMPLOYEE.FDB'; соединяет с локальным сервером, на котором isql уже знает ваше имя пользователя и пароль — либо потому что вы правильно ввели их при запуске isql, либо потому что isql выполняется в командной строке, которая может видеть переменные окружения ISCJJSER И ISC_PASSWORD. CONNECT 'HOTCHICKEN:ЕМРЗ' USER 'SYSDBA' PASSWORD 'masterkey'; эквивалентно первому примеру в Firebird версии 1.5 и выше; при этом используется алиас, хранящийся в файле aliases.conf на сервере, который указывает путь. CONNECT 'L:/DATA/EXAMPLES/EMPLOYEE.FDB'; эквивалентно второму примеру. В isql наклонная черта может быть прямой и обрат- ной. Имя сервера и путь В Windows не путайте имена серверов и имена совместно используемых дисков. Сеть клиент-сервер не распознает разделяемые диски и каталоги (mapped drive, shared folder). Идентификатор диска всегда должен указывать на фактическую букву жест- кого диска или раздела на серверной машине. Идентификация пользователя Независимо от того, соединились ли вы из командной строки или сделали это внутри командной строки isql, идентификация пользователя будет ошибочной, если сервер не распознает имя пользователя или пароль. Например, на рис. 37.3 показано, что вы увидите, если оператор connect будет ошибочным. Рис. 37.3. Ошибочная идентификация пользователя
Гпава 37. Интерактивный SQL (утилита isql) 805 Если такое произошло, дважды проверьте, правильно ли вы задали имя пользователя и пароль, и в правильном ли регистре введен пароль. Пароль чувствителен к регист- ру, а имя пользователя нет. Символ терминатора Терминатором оператора по умолчанию является точка с запятой (;), которая исполь- зуется во всех примерах в этой главе. Вы можете изменить этот терминатор любым другим символом или группой символов посредством команды set term[inator] . На- пример, чтобы заменить его на " 11", используйте следующий оператор: SQL> SET TERM I!; SQL> Продолжение строки подсказки Если вы нажмете клавишу <Enter>, забыв завершить оператор терминатором, вы увидите продолжение подсказки con> вместо подсказки isql sql>: SQL> SHOW DATABASE CON> Если это было ошибкой, просто наберите терминатор и опять нажмите клавишу <Enter>. Причем вы можете использовать эту возможность, чтобы сделать ваш текст более читаемым, например: SQL> CREATE TABLE ATABLE ( CON> ID INTEGER NOT NULL, CON> DATA VARCHAR(20), CON> DATE^ENTERED DATE CON> DEFAULT CURRENTJDATE CON> ); SQL> СОВЕТ. Одна из причин применения возможности продолжения — это то, что вы можете использовать команду output для помещения введенного вами в isql тек- ста в файл. Поскольку вывод сохраняется точно в таком же виде, как вы вводите данные, все ваши пробельные отступы будут сохранены. Многие пользователи Firebird используют isql в качестве единственного редактора скриптов! Транзакции в isql Управление транзакциями в isql отличается в зависимости от того, вводите ли вы оператор DDL, команду show или другие виды операторов.
806 Часть IX. Инструменты Когда isql стартует, она запускает транзакцию с уровнем изоляции snapshot (парал- лельный) с установкой разрешения блокировок wait. Если вы выполняете не опера- торы DDL и не команды show, транзакция продолжает оставаться текущей, пока вы не выдадите оператор commit или rollback. Вы можете стартовать явную транзакцию, подтвердив текущую транзакцию, и выдав оператор set transaction. Например, для старта транзакции read committed no wait введите: SQL> COMMIT; SQL> SET TRANSACTION CON> NO WAIT READ COMMITTED; При завершении вашей задачи просто выдайте как обычно оператор commit. Следую- щий оператор вернет систему в конфигурацию по умолчанию. Операторы DDL Каждый раз, когда вы выдаете оператор DDL, isql стартует для него специальную транзакцию и немедленно ее подтверждает после того, как вы нажимаете клавишу <Enter>. Сразу после этого стартует новая транзакция. Вы можете изменить автома- тическое поведение, выдав команду set autoddl off из подсказки SQL до начала вы- полнения ваших операторов DDL: SQL> SET AUTODDL OFF; Чтобы опять включить режим автоматического подтверждения операторов DDL, введите: SQL> SET AUTODDL ON; Для переключения между режимами включено/выключено существует сокращенная версия команды: SQL> SET AUTO; Команды SHOW Команды show в isql запрашивают данные из системных таблиц. Когда вы вызываете команду show, isql подтверждает существующую транзакцию и стартует новую с уровнем изоляции read committed. Это гарантирует, что вы всегда видите самые по- следние изменения метаданных, как только они будут изменены. Поиск в буфере строк Начиная с версии 1.5 и выше, isql позволяет вам отыскивать строки в буфере спосо- бом, похожим на возможность readiine на платформах POSIX. Используйте клавиши <Т> (стрелка вверх) и <1> (стрелка вниз) для "прокручивания" буфера isql по одной строке за раз для поиска копий строк, которые вы вводили ранее.
Гпава 37. Интерактивный SQL (утилита isql) 807 Использование предупреждений По умолчанию isql выдает предупреждения при некоторых условиях, например: ♦ операторы, не выполняющие действий; ♦ двусмысленная спецификация соединений в Firebird 1.0.x (в версии 1.5 и выше это вызовет исключение); ♦ выражения, которые дадут различные результаты в различных версиях Firebird; ♦ вызовы API, которые будут изменены в будущих версиях; ♦ когда база данных переведена в состояние shutdown. Для переключения режима выдачи предупреждений в процессе выполнения сессии интерактивной isql используйте set warnings или сокращенный вариант set wng. Обработка исключений Ошибки в isql обрабатываются тем же образом, что и приложении DSQL. isql ото- бражает сообщение об ошибке, содержащее переменную sqlcode и текст сообщения из массива состояния Firebird, как показано на рис. 37.4. Рис. 37.4. Пример сообщения об ошибке в isql Ошибки SQL со значением sqlcode меньше нуля означают, что оператор не был вы- полнен. Все они представлены в приложении 10. Вы можете также увидеть одно из предупреждений SQL или информационное сообщение, а именно: ♦ 0: success (успешное выполнение); ♦ +1-99-. sqlwarning (системное предупреждение или информационное сообщение); ♦ +100 not found (указывает, что указанные строки не найдены или достигнут "ко- нец файла"; т. е. определен конец текущего активного набора строк). Установка в isql диалекта Если вы запустили isql и соединились с базой данных без указания диалекта, утилита принимает диалект базы данных.
808 Часть IX. Инструменты Вы можете установить диалект для isql несколькими способами. ♦ При запуске isql: [bin] isql -s n где n— число 1, 2 или 3. Если вы зададите диалект этим способом, isql сохранит данный диалект после соединения, если только вы явно его не измените. ♦ В сессии isql или в скрипте SQL: SET SQL DIALECT n; Утилита продолжит работать с этим диалектом, пока он не будет явно изменен. Диалект не может быть установлен как параметр оператора create database. /А ВНИМАНИЕ! Когда вы создаете базу данных интерактивно с использованием / J \ isql, база данных будет в том диалекте, который в настоящий момент существует * ~—Л- у isql во время выдачи оператора create database. Вам нужно будет учесть это, если у вас была перед этим открыта база данных с диалектом 1, потому что isql останется в диалекте 1 и после отключения от базы данных с диалектом 1. Воздействие диалектов Эффекты команд могут иметь некоторые изменения в зависимости от диалекта. ♦ Клиент диалекта 1 обрабатывает все команды в соответствии с ожиданием языка InterBase 5 и синтаксиса с некоторыми изменениями. Например, если вы создаете таблицу, в которой задается столбец типа date, вы увидите информационное со- общение, говорящее, что "DATE datatype is now called TIMESTAMP" (Тип данных date сейчас называется timestamp). ♦ В клиенте диалекта 2 элементы, имеющие различную интерпретацию в диалек- тах 1 и 3, отмечаются предупреждениями или ошибками для помощи в миграции баз данных в диалект 3. ♦ Клиент диалекта 3 анализирует все операторы в соответствии с родной семанти- кой SQL Firebird: кавычки используются для идентификаторов с ограничителями и не распознаются как ограничители строк, тип данных date содержит только да- ту, а истинные числа (числа с фиксированной точкой) с точностью больше 9 хра- нятся как bigint (numeric(18, о) в Firebird 1 .ОУ). Интерактивные команды Вы можете интерактивно вводить три вида команд или операторов в подсказке sql>. ♦ Операторы SQL определения данных (DDL), такие как create, alter, drop и revoke. Эти операторы создают, изменяют или удаляют метаданные и объекты или управляют полномочиями доступа пользователя (привилегиями) к базе данных.
Гпава 37, Интерактивный SQL (утилита isql)809 ♦ Операторы SQL манипулирования данными (DML), такие как select, insert, update и delete. Выход операторов select может быть отображен или направлен в файл (см. команду output). ♦ Команды isql, которые разделяются на три основные категории: • общие команды (например, команды чтения входного файла, записи в выход- ной файл или завершения сессии isql); • команды show (для отображения метаданных и другой информации базы дан- ных); • команды set (для изменения окружения isql). Создание и изменение объектов базы данных В сессии isql вы можете выдавать операторы DDL один за другим для создания (create) или удаления (drop) баз данных, доменов, генераторов, таблиц, индексов, триггеров и хранимых процедур. Вместе с исключениями генераторов вы также мо- жете выдавать операторы alter для любого из этих объектов. Поскольку можно создавать базу данных, выдавая и подтверждая серии операторов DDL в процессе сессии интерактивной isql, такой подход является специализирован- ным. Он не позволяет документировать процесс и оставляет дыры в процессе отлад- ки и тестирования. Скрипты Очень хорошей практикой является использование скриптов для создания базы дан- ных и ее объектов. Скрипт для создания и изменения объектов базы данных иногда называют скриптом схемы, файлом определения данных или просто DDL-скриптом. Тема использования скриптов схемы рассматривается в деталях в главе 14. Скрипты по изменению базы данных дают вам постоянную фиксацию эволюции ва- шей базы данных и возможность отслеживать и отменять любые неразумные измене- ния. Утилита isql может создавать скрипт в процессе интерактивной сессии, переда- вая ваш ввод с клавиатуры в указанный файл. Чтобы узнать, как это сделать, прочти- те примечания к команде output в следующем разделе. Вы можете запустить скрипт, созданный в isql или в любом другом текстовом редак- торе, посредством команды input. Она может быть также использована внутри скрипта для создания вложенных скриптов. Общие команды isql Общие команды isql выполняют множество полезных задач, включая чтение, запись и выполнение скриптов схемы, а также выполнение команд командной строки. Командами ЯВЛЯЮТСЯ BLOBDUMP, blobview, edit, exit, help, input, output, quit и shell.
810 Часть IX. Инструменты blobdump сохраняет данные blob в указанном файле: BLOBDUMP идентификатор-BLOB имя-файла; где идентификатор-вьов— идентификатор, содержащий два шестнадцатеричных чис- ла, разделенных двоеточием (:). Первое число является идентификатором таблицы, содержащей столбец blob. Второе — последовательный номер объекта. Для получе- ния этого идентификатора выдайте любой оператор select, который выбирает стол- бец blob. Выход покажет шестнадцатеричный идентификатор blob выше или на месте столбца blob в зависимости от того, установлен ли set [display] в on или off. имя-файла — полное имя файловой системы того файла, который получает данные. Пример: SQL> BLOBDUMP 32:d48 IMAGE.JPG ; blobview отображает данные blob в текстовом редакторе по умолчанию. BLOBVIEW идентификатор-BLOB ; где идентификатор-ввов— идентификатор, содержащий два шестнадцатеричных чис- ла, разделенных двоеточием (:). См. описание blobdump для определения идентифика- тора blob, который вы ищите. В текущей версии blobview не поддерживает редакти- рования blob в режиме онлайн. Это может быть добавлено в будущие реализации. Пример: SQL> BLOBVIEW 85:7 ; Замечание по ошибке: blobview может вернуть ошибку "Invalid transaction handle" (Неверный дескриптор транзакции) после закрытия редактора. Для исправления си- туации запустите транзакцию вручную с помощью: SQL> SET TRANSACTION; edit позволяет отредактировать и заново выполнить предыдущую команду isql или пакет команд в исходном файле. SQL> EDIT [имя-файла]; где имя-файла— (необязательно) полностью заданное имя файла для редактирования в файловой системе. Пример: SQL> EDIT /usr/mystuff/batch.sql Команда edit также может быть использована для открытия предыдущих операторов в редакторе: SQL> SELECT EMP_CODE, EMP_NAME FROM EMPLOYEE ; SQL> EDIT ; Нажмите клавишу <Enter> для отображения "прокрутки" вашей сессии isql в тексто- вом редакторе ASCII по умолчанию в вашей системе. Отредактируйте, при желании
Гпава 37. Интерактивный SQL (утилита isql) 811 сохраните и выйдите. Отредактированный пакет команд будет заново выполнен в вашей командной строке isql после того, как вы выйдете из редактора. exit подтверждает текущую транзакцию без подсказки, закрывает базу данных и за- вершает сессию isql. Если вам нужно выполнить откат, а не подтверждение транзак- ции, используйте quit. SQL> EXIT ; exit не принимает никаких аргументов. help отображает список команд isql с их описанием. Вы можете объединить эту команду с output для вывода списка в файл. SQL> HELP ; Пример: SQL> OUTPUT HELPLIST.TXT ; SQL> HELP ; SQL> OUTPUT ; /* переключает вывод опять на монитор */ help не принимает никаких аргументов. input читает и выполняет блок команд из указанного текстового файла (скрипта SQL). Входные файлы могут содержать другие команды input, предоставляя таким образом возможность проектирования цепочного или структурированного набора скриптов DDL. Для создания скриптов используйте текстовый редактор или созда- вайте их интерактивно посредством команд output или edit. SQL> INPUT имя-файла ; где имя-файла — полностью заданное имя файла, содержащего операторы и команды SQL. Пример: SQL> INPUT /data/schemascripts/myscript.sql ; В скрипте: CREATE EXCEPTION Е010 'This is an exception.'; COMMIT; - - TABLE DEFINITIONS INPUT '/data/schemascripts/tabledefs.sql'; — CONSTRAINT DEFINITIONS INPUT 'data/schemascripts/constraintdefs.sql';
812 Часть IX. Инструменты output перенаправляет выходные данные в файл на диске или (назад) на стандартное устройство вывода (монитор). Используйте команды set echo для включения или исключения из вывода команд: ♦ set echo on — для вывода команд и данных; ♦ set echo off — для вывода только данных. SQL> OUTPUT [имя—файла] ; где имя-файла — полностью заданное имя файла, содержащего операторы и команды SQL. Если имя файла не указано, результаты появятся на стандартном выводе, на мониторе (т. е. вывод в файл отключен). Пример: SQL> OUTPUT d:\data\employees.dta ; SQL> SELECT EMP_NO, EMP_NAME FROM EMPLOYEE ; /* вывод отправляется в файл */ SQL> OUTPUT ; /* переключает вывод опять на монитор */ f СОВЕТ. Если вы используете output для создания скриптов, то может понадо- биться их отредактировать, чтобы удалить случайные интерактивные команды isql. Однако, когда вы направляете этот вывод в isql, используя input, isql обычно просто игнорирует отраженные интерактивные команды. quit отменяет текущую транзакцию и закрывает окно isql. SQL> QUIT ; quit не принимает аргументов. Если вам нужно подтвердить транзакцию вместо ее отката, используйте exit. shell предоставляет временный доступ к окну командной строки без подтверждения или отката любой транзакции. SQL> SHELL [команда операционной системы] ; где команда операционной системы— (необязательно) команда или вызов, допустимый в командной строке, из которой была запущена isql. Команда будет выполнена, а управление возвращено isql. Если команда не указана, isql открывает интерактивную сессию в командной строке. Ввод exit возвращает управление isql. Пример: SQL> SHELL dir /mydir | more ; Этот пример отобразит содержимое каталога /mydir и возвратит управление isql, ко- гда отображение будет завершено или завершится утилита more при нажатии комби- нации клавиш <Ctrl>+<C>.
Гпава 37. Интерактивный SQL (утилита isql) 813 Команды SHOW Команды show используются для отображения метаданных, включая таблицы, индек- сы, процедуры, триггеры и привилегии. Они могут отображать список имен всех объектов указанного типа или предоставлять детальную информацию о конкретном объекте, заданном в команде. Команды show являются (приблизительным) интерактивным эквивалентом командной строки -extract, -х или -а режим (см. разд. "Извлечение метаданных"). Однако, хотя вы можете использовать команду output для пересылки выхода команд show в файл, сохраненный текст не будет готов к использованию в качестве скрипта схемы без редактирования. Используйте опции командной строки, если получение скрипта схе- мы является вашей целью. Каждая команда show выполняется в своей собственной транзакции read committed, гарантируя, что каждый вызов вернет самый последний вид состояния базы данных. show check отображает имена и тексты всех определенных пользователем ограниче- ний check, заданных для указанной таблицы. SQL> SHOW CHECK имя-таблицы ; где имя-таалицы— имя таблицы, которая существует в подключенной базе данных. Пример: SQL> SHOW CHECK JOB ; CONSTRAINT INTEG_12 CHECK (min_salary < max_salary) show database отображает информацию о подключенной базе данных (имя файла, размер и количество выделенных страниц, интервал очистки, номера транзакций, статус Forced Writes, набор символов по умолчанию), show db является сокращенной версией этой команды. SQL> SHOW DATABASE | DB ; show database не принимает аргументов. На рис. 37.5 показан результат, который вы можете ожидать от команды show database. СОВЕТ. Для получения информации о версии и структуре на диске (ODS) ис- пользуйте show version. show domain [S] отображает информацию домена. SQL> SHOW { DOMAINS | DOMAIN имя };
814 Часть IX. Инструменты domains — отображает список имен всех доменов, объявленных в базе данных. domain имя— отображает определение одного указанного домена. Рис. 37.5. Результат выполнения SHOW database Примеры: SQL> SHOW DOMAINS ; D_CURRENCY D_NOTES D_BOOLEAN D_PHONEFAX SQL> SHOW DOMAIN D_BOOLEAN ; D_BOOLEAN SMALLINT NOT NULL DEFAULT 0 CHECK (VALUE IN (0,1) show exception[sj отображает информацию исключения. SQL> SHOW ( EXCEPTIONS [ EXCEPTION имя }; exceptions— отображает список имен и текстов всех исключений, объявленных в базе данных. exception имя— отображает текст одного указанного исключения. Примеры: SQL> SHOW EXCEPTIONS ; Exception Name Used by, Type BAD_WIZ__TYPE UPD_FAVEFOOD, Stored procedure Invalid Wiz type, check CAPS LOCK SQL> SHOW EXCEPTION BAD_WIZ_TYPE ; Exception Name Used by, Type
Гпава 37. Интерактивный SQL (утилита isql) 815 BAD_WIZ_TYPE UPD_FAVEFOOD, Stored procedure Invalid Wiz type, check CAPS LOCK show function[s] отображает информацию о внешних функциях, объявленных в под- ключенной базе данных. SQL> SHOW { FUNCTIONS I FUNCTION имя }; functions— отображает список имен всех внешних функций, объявленных в базе данных. function имя— отображает объявление указанной внешней функции. Примеры: SQL> SHOW FUNCTIONS ; ABS MAXNUM LOWER SUBSTRLEN SQL> SHOW FUNCTION maxnum ; Function MAXNUM: Function library is /usr/firebird/udf/ib_udf.so Entry point is FN_MAX Returns BY VALUE DOUBLE PRECISION Argument 1: DOUBLE PRECISION Argument 2: DOUBLE PRECISION show generator[s] отображает информацию о генераторах, объявленных в подклю- ченной базе данных. SQL> SHOW { GENERATORS | GENERATOR имя }; generators — отображает список имен всех генераторов, объявленных в базе данных вместе с их текущими значениями. generator имя— отображает объявление указанного генератора вместе с его текущим значением. Примеры: SQL> SHOW GENERATORS ; Generator GEN_EMPNO, Next value: 1234 Generator GEN_JOBNO, Next value: 56789 Generator GEN_ORDNO, Next value: 98765 SQL> SHOW GENERATOR gen_ordno ; Generator GEN_ORDNO, Next value: 98765
816 Часть IX. Инструменты show grant отображает информацию привилегий и ролей по отношению к указан- ному объекту в подключенной базе данных или отображает членство пользователей в роли. SQL> SHOW GRANT { объект | имя-роли }; где объект— имя существующей таблицы, просмотра или процедуры в текущей базе данных. имя-роли— имя существующей роли в текущей базе данных. Используйте show role для получения списка всех ролей, определенных в этой базе данных. Примеры: SQL> SHOW GRANT JOB ; GRANT SELECT ON JOB TO ALL GRANT DELETE, INSERT, SELECT, UPDATE ON JOB TO MANAGER SQL> SHOW GRANT DOJTHIS ; GRANT DO_THIS TO MAGICIAN show index (show indices) отображает информацию об указанном индексе, об индек- сах для указанной таблицы или обо всех индексах для всех таблиц в подключенной базе данных. Эта команда может быть сокращена: show ind. SQL> SHOW {INDICES | INDEX { индекс | таблица }}; где индекс— имя существующего индекса в текущей базе данных. таблица — имя существующей таблицы в текущей базе данных. Примеры: SQL> SHOW INDEX ; RDB$PRIMARY1 UNIQUE INDEX ON COUNTRY(COUNTRY) CUSTNAMEX INDEX ON CUSTOMER(CUSTOMER) CUSTREGION INDEX ON CUSTOMER(COUNTRY, CITY) RDB$FOREIGN23 INDEX ON CUSTOMER(COUNTRY) SQL> SHOW IND COUNTRY ; RDB$PRIMARY20 UNIQUE INDEX ON CUSTOMER(CUSTNO) CUSTNAMEX INDEX ON CUSTOMER(CUSTOMER) < ПРИМЕЧАНИЕ. Для получения информации о текущем состоянии индексов в базе данных используйте gstat -i. Применение утилиты gstat обсуждалось в \ конце главы 18. show procedure[sj отображает все процедуры в подключенной базе данных с их зави- симостями или отображает текст указанной процедуры с объявлениями и типами
Гпава 37. Интерактивный SQL (утилита isql) 817 (входной/выходной) каждого аргумента. Команда может иметь сокращенный вид SHOW PROC. SQL> SHOW {PROCEDURES | PROCEDURE имя } ; procedures — отображает список имен всех процедур вместе с их зависимостями. procedure имя— для указанной процедуры отображает исходный текст, зависимости и аргументы. Примеры: SQL> SHOW PROCEDURES ; Procedure Name Dependency Type ADD EMP PROJ EMPLOYEE_PROJECT Table UNKNOWN EMP ID Exception DELETE EMPLOYEE DEPARTMENT Table EMPLOYEE Table EMPLOYEE PROJECT Table SQL> SHOW PROC ADD_EMP_PROJ ; Procedure text: BEGIN BEGIN INSERT INTO EMPLOYEE-PROJECT ( EMP—NO, PROJ_ID) VALUES ( :emp_no, :proj_id) ; WHEN SQLCODE -530 DO EXCEPTION UNKNOWN_EMP_ID; END RETURN ; END Parameters: EMP_NO INPUT SMALLINT PROJ_ID INPUT CHAR(5) show role[s] отображает имена ролей SQL в подключенной базе данных. SQL> SHOW ROLES ; show roles не принимает аргументов. Примеры: SQL> SHOW ROLES ; MAGICIAN MANAGER PARIAH SLEEPER
818 Часть IX. Инструменты show grant имя роли используется для отображения членства пользователей в ролях. show sql dialect отображает диалекты SQL клиента и подключенной базы данных, если такая есть. SQL> SHOW SQL DIALECT; Пример: SQL> SHOW SQL DIALECT; Client SQL dialect is set: 3 and database SQL dialect is: 3 show system отображает имена системных таблиц и системных просмотров в подклю- ченной базе данных. Команда имеет сокращение show sys. SQL> SHOW SYS [ TABLES ] ; Команда не принимает аргументов, tables является необязательным ключевым сло- вом, которое не влияет на поведение команды. Примеры: SQL> SHOW SYS ; RDB$CHARACTER_SETS RDB$CHECK__CONSTRAINTS RDB$COLLATIONS RDB$ DATABASE Подробную информацию о системных таблицах см. в приложении 9. show table[S] отображает все таблицы или просмотры либо информацию об указан- ной таблице или просмотре. SQL> SHOW { TABLES | TABLE имя }; show tables — отображает список имен всех таблиц и просмотров в алфавитном по- рядке. show table имя— показывает подробности указанной таблицы или просмотра. Если объект является таблицей, вывод содержит имена столбцов и их определение, primary key, foreign key, ограничения check и триггеры. Если объект является просмотром, то вывод содержит имена столбцов и оператор select, на котором основывается про- смотр. Примеры: SQL> SHOW TABLES ; COUNTRY CUSTOMER
Глава 37. Интерактивный SQL (утилита isql)819 DEPARTMENT EMPLOYEE EMPLOYEE^PROJECT JOB SQL> SHOW TABLE COUNTRY ; COUNTRY COUNTRYNAME VARCHAR (15) NOT NULL CURRENCY VARCHAR(10) NOT NULL PRIMARY KEY (COUNTRY) См. также show views позже в этой главе. show trigger[sj отображает все триггеры, определенные в базе данных вместе с их таблицами, или для указанного триггера отображает его последовательный номер, тип, статус активности (активен/не активен) и определение PSQL. Сокращенный ва- риант команды show trig. SQL> SHOW {TRIGGERS | TRIGGER имя } ; show triggers — отображает список имен всех таблиц вместе с именами их триггеров в алфавитном порядке. show trigger имя— для заданного триггера указывает таблицу, к которой он принад- лежит, отображает параметры заголовка, статус активности и исходный код PSQL тела триггера. Примеры: SQL> SHOW TRIGGERS ; Table name Trigger name EMPLOYEE SET_EMP_NO EMPLOYEE SAVEJSALARY_CHANGE CUSTOMER SET_CUST_NO SALES POST_NEW_ORDER SQL> SHOW TRIG SET_CUST_NO ; Trigger: SET_CUST_NO, Sequence: 0, Type: BEFORE INSERT, Active AS BEGIN new.custno = gen__id (cust_no_gen, 1); END show version отображает информацию о программной версии isql и серверной про- граммы Firebird, а также номер структуры на диске (ODS) подключенной базы дан- ных. Сокращенный вариант команды show ver. SQL> SHOW VERSION ; Команда не принимает аргументов.
820 Часть IX. Инструменты Пример для сервера с именем dev с запушенным Firebird 1.5 под Windows 2000: SQL> SHOW VER ; - ISQL Version: WI-V1.5.0.4306 Firebird 1.5 Firebird/x86/Windows NT (access method), version "WI-V1.5.0.4306 Firebird 1.5" Firebird/x86/Windows NT (remote server), version "WI-V1.5.0.4306 Firebird 1.5/tcp (dev)/P10" Firebird/x86/Windows NT (remote interface), version "WI-V1.5.0.4306 Firebird 1.5/tcp (dev)/Р10" on disk structure version 10.1 shcw view[s] отображает все просмотры или информацию об указанном просмотре. (См. также show tables.) SQL> SHOW { VIEWS | VIEW имя } ; show views — отображает список имен всех просмотров в алфавитном порядке. show view имя— отображает имена столбцов и оператор select, на котором основан просмотр. Пример: SQL> SHOW VIEWS ; PHONE_LIST CUSTOMER SQL> SHOW VIEW PHONE_LIST; EMP_NO (EMPNO) SMALLINT Not Null FIRSTJW4E (FIRSTNAME) VARCHAR(15) Not Null LAST_NAME (LASTNAME) VARCHAR(20) Not Null PHONE_EXT VARCHAR(4) Nullable LOCATION VARCHAR(15) Nullable PHONE_NO View Source: (PHONENUMBER) VARCHAR(20) Nullable SELECT emp_no, first_name, last_name, phone_ext, location, phone_no FROM employee, department WHERE employee.dept_no = department.dept_no Команды SET Команды set позволяют просматривать и изменять некоторые вещи, связанные со средой isql. Отдельные из них доступны в скриптах.
Гпава 37. Интерактивный SQL (утилита isql) 821 set autoddl задает, будут ли операторы DLL подтверждаться автоматически после их выполнения или будут подтверждаться после явного выполнения commit. Оператор доступен в скриптах. SQL> SET AUTODDL [ON | OFF] ; /* значение по умолчанию ON */ где on — включает автоматическое подтверждение. off— отключает автоматическое подтверждение. set auto (без аргументов) просто переключает autoddl между включено и выклю- чено. Пример: SQL> SET AUTODDL OFF ; SQL> CREATE TABLE WIZZO (x integer, у integer) ; SQL> ROLLBACK; /* таблица WIZZO не создана */ SQL>SET AUTO ON ; SQL> CREATE TABLE WIZZO (x integer, у integer) ; SQL> /* таблица WIZZO создана */ set hlobdisplay задает необходимость отображения подтипа blob и отображения дан- ных blob. Сокращенной версией этой команды является set blob. SQL> SET BLOBDISPLAY [ n I ALL |OFF ]; n — отображать blob заданного подтипа. Значение по умолчанию n = 1 (текст). По- ложительные числа определены в системе; отрицательные числа определяются поль- зователем. all — отображать данные blob любого подтипа. off— отключает отображение данных blob. Вывод показывает только идентифика- тор blob (Blob ID— два шестнадцатеричных числа, разделенных двоеточием). Пер- вое число является идентификатором таблицы, содержащей столбец blob. Второе является последовательным номером реализации и идентифицирует данные blob. Пример: SQL> SET BLOBDISPLAY OFF ; SQL> SELECT PROJ_NAME, PROJJDESC FROM PROJECT ; SQL> /* строки показывают значения PROJ_NAME и Blob ID */ SQL>SET BLOB 1 ; SQL> SELECT PROJ_NAME, PROJJDESC FROM PROJECT ; SQL> /* строки показывают значения PROJ_NAME и Blob ID, */ SQL> /* а текст BLOB появляется ниже каждой строки */
822 Часть IX. Инструменты set count включает/выключает отображение количества строк, найденных по запро- сам. SQL> SET COUNT [ON | OFF] ; on— отображать сообщение "найденные строки" (rows returned). off— не отображать сообщение "найденные строки" (rows returned). Значение по умолчанию. Пример: SQL> SET COUNT ON ; SQL> SELECT * FROM WIZZO WHERE FAVEFOOD = 'Pizza' ; SQL> /* отображаются данные */ 40 rows returned set echo включает/выключает отображение команд до их выполнения. Значение по умолчанию on, но вы можете переключить его в off при направлении вашего вывода в файл скрипта. SQL> SET ECHO [ON I OFF] ; /* значение по умолчанию ON */ on — включает отображение команд. off— отключает отображение команд. Пример скрипта wizzo.sql: SET ECHO OFF; SELECT * FROM WIZZO WHERE FAVEFOOD = 'Pizza' ; SET ECHO ON ; SELECT * FROM WIZZO WHERE FAVEFOOD = 'Sardines' ; EXIT; SQL > INPUT wizzo.sql ; WIZTYPE FAVEFOOD alpha Pizza epsilon Pizza SELECT * FROM WIZZO WHERE FAVEFOOD = 'Sardines' ; WIZTYPE FAVEFOOD gamma lamda Sardines Sardines set names задает набор символов, который будет активным в транзакциях базы дан- ных. Это особенно важно, если в вашей базе данных набором символов по умолча-
Гпава 37. Интерактивный SQL (утилита isql) 823 нию является none. Если наборы символов клиента и базы данных не соответствуют друг другу, вы рискуете получить ошибки транслитерации и сохранить неверные данные при использовании isql для выполнения изменений, добавлений или поиска данных (включая отыскиваемые изменения и удаления). Команда set names доступна в скриптах1. SQL> SET NAMES набор-символов ; где набор-символов— имя активируемого набора символов. Значение по умолчанию NONE. Пример из скрипта: SET NAMES WIN1251 ; CONNECT 'HOTCHICKEN:/usr/firebird/examples/employee.gdb' ; set plan задает, нужно ли отображать план запроса оптимизатора. SQL> SET PLAN [ON|OFF ]; on — включает отображение плана запроса. Это значение по умолчанию. off — отключает отображение плана запроса. Для сокращения вы можете опускать on | off и просто использовать set plan для пе- реключения режима. Пример из скрипта: SET PLAN ON ; SELECT JOB_COUNTRY, MIN_SALARY FROM JOB WHERE MIN_SALARY > 50000 AND JOB_COUNTRY = 'Sweden'; SQL> INPUT iscript.sql PLAN (JOB INDEX (RDB$FOREIGN3,MINSALX,MAXSALX) JOB_COUNTRY MIN—SALARY Sweden 120550.00 set planonly задает только подготовку запросов select и отображение плана без вы- полнения самого запроса. SQL> SET PLANONLY ON | OFF; Команда работает как переключатель. Аргумент необязателен. set names, то есть набор символов соединения, должен быть задан до подсоединения к базе данных.
824 Часть IX. Инструменты set sqldialect устанавливает SQL-диалект Firebird в то значение, которое было за- дано для сессии клиента. Если в сессии открыто соединение с базой данных с диа- лектом, отличным от того, который задан в команде, будет выдано предупреждение, и программа спросит вас, хотите ли вы подтвердить выполненную работу (если такая была). SQL> SET SQL DIALECT n ; где n — номер диалекта, n равен 1 для диалекта 1,2 — для диалекта 2 и 3 — для диа- лекта 3. Пример: SQL> SET SQL DIALECT 3 ; set stats определяет, отображать ли статистику выполнения, которая будет следо- вать за выходными данными запроса. SQL> SET STATS [ON|OFF]; on — включает отображение статистики выполнения. off— включает отображение статистики выполнения. Это значение по умолчанию. Вы можете опускать on,off и просто использовать set stats в качестве переключа- теля. На рис. 37.6 показан типичный статистический итог, отображаемый после вы- ходных данных запроса. i Command Prompt -isql SQL> ; SQL> SET STATS; 8QL> SELECT LA ST .ЛАМЕ PROM EMPLOYEE CON> WHERE FIRSTJNAME STARTING WITH LAST.JNAME Lanhert Weston Venin# Cook Brown Current nenory = 1376196 Delta nenory “ -4092 Max юегоогу ::: 14Э3940 Elapsed tine- 0.02 sec Buffers = 2Й48 Reads - 1 Writes 0 Fetches ~ SQL> „ Рис. 37.6. Пример SET STATS SET STATISTICS set statistics является командой SQL (не isql), которую вы можете использовать в isql — так же, как и в других программах — для вычисления селективности индек-
Гпава 37. Интерактивный SQL (утилита isql)825 са. Здесь об этом упоминается, потому что, неудивительно, люди часто путают ее с set stats. Чтобы узнать, почему селективность является важной для очень дина- мичных таблиц, см. разд. "Темы оптимизации" в конце главы 18. Синтаксис этой команды set statistics имя-мндекса. Оператор может быть выпол- нен только пользователем, который владеет индексом. set терм задает символ, который будет использоваться в качестве терминатора команды или оператора. Он доступен в скриптах. См. замечания об этой команде ра- нее в данной главе. SQL> SET TERM строка ; где строка— символ или символы, которые будут использоваться как терминатор оператора. Значение по умолчанию Пример: SET TERM AA; CREATE PROCEDURE ADDJWIZTYPE (WIZTYPE VARCHAR(16), FAVEFOOD VARCHAR(20)) AS BEGIN INSERT INTO WIZZO(WIZTYPE, FAVEFOOD) VALUES ( :WIZTYPE, :FAVEFOOD) ; END AA SET TERM ;AA set time задает, отображать ли время в значении date (только диалект 1). SQL> SET TIME [ON|OFF ]; on — включает отображение времени в значении date диалекта 1. off— отключает отображение времени в значении date диалекта 1. Это значение по умолчанию. Пример: SQL> SELECT HIRE_DATE FROM EMPLOYEE WHERE EMP_NO = 145; HIRE_DATE 16-MAY-2004 SQL>SET TIME ON ; SQL> SELECT HIRE_DATE FROM EMPLOYEE WHERE EMP_NO = 145; HIRE_DATE 16-MAY-2004 18:20:00
826 Часть IX. Инструменты set warnings задает, выводить ли предупреждающие сообщения. Можно использо- вать set wng как простой переключатель. SQL> SET WARNINGS [ON | OFF ]; on— включает отображение предупреждений, если они были отключены, или если сессия была запущена с переключателем -nowarnings. off— отключает отображение предупреждений, если они были включены. Выход из интерактивной сессии isql Для выхода из утилиты isql и отката (roll back) всей неподтвержденной работы вве- дите: SQL> QUIT; Для выхода из утилиты isql и подтверждения всей работы введите: SQL> EXIT; Командный режим Хотя isql имеет некоторые удобные интерактивные возможности, она не ограничена только этим режимом. Многие интерактивные команды доступны также в виде пере- ключателей командной строки. Некоторые функции isql, такие как извлечение мета- данных, доступны только из окна командной строки. Операции над входными и вы- ходными файлами не обязательно должны быть интерактивными— фактически вы- зов isql с переключателями -i[nput] и -o[utput] не вызывает интерактивного режима. Команды выполняются и после завершения, они автоматически возвращают управ- ление окну командной строки. Вызовы isql также могут находиться внутри скриптов командной строки или внутри пакетных файлов. Некоторые переключатели могут быть использованы при вызове интерактивного ок- на isql. Они представлены в табл. 37.1. Выполнение isql в командном режиме Откройте окно командной строки и перейдите в каталог Firebird /bin вашей инсталля- ции сервера или клиента Firebird. Используйте следующий синтаксис для вызовов isql: isql [режимы] [имя-базы-данных] [-u[ser] <имя-пользователя> -pas [sword] <пароль>] Для операций SYSDBA вы можете задать переменные операционной системы iscjjser и isc password и не вводить их в командах. Для деятельности пользователей, не являющихся SYSDBA, вам всегда нужно задавать учетные данные для операций с базой данных и/или с объектами.
Гпава 37. Интерактивный SQL (утилита isql) 827 Терминатором по умолчанию является точка с запятой (;). Вы можете заменить ее любым символом или группой символов с помощью режима в командной строке. Jg ПРИМЕЧАНИЕ. Если вы выполняете команду соединения с базой данных для выполнения скрипта, а его набор символов по умолчанию none, вам нужно в ваш V _скрипт включить команду set names. Вы можете установить диалект isql из командной строки при вызове isql: isql -s n ; где n = 1, 2 или 3. Переключатели командной строки Требуются только начальные символы переключателей. Вы также можете набрать любую часть текста в квадратных скобках, показанного в табл. 37.1, включая полное название режима. Например, задание -n, -no, -noauto имеет одинаковый эффект. Таблица 37.1. Переключатели для режимов командной строки isql Режим Описание -а Извлекает все операторы DDL из указанной базы, а также операто- ры, не являющиеся операторами DDL -dfatabase] имя Используется с переключателем -х (извлечь). Изменяет имя базы данных в операторе create database, извлеченном в файл. Путь к файлу должен быть полностью определен. Без переключателя -d create database появляется в виде комментария в стиле языка С с использованием имени базы данных, заданным в командной строке ~с[ache] Задает количество страниц в кэше, применяемом в этом соедине- нии с базой данных. Вы можете использовать этот переключатель для того, чтобы на время перекрыть размер кэша, установленный на текущий момент для базы данных -е[cho] Отображает каждый оператор перед его выполнением (так назы- ваемое эхо) -ex[tract] Извлекает операторы DDL из указанной базы данных и отображает их на экране, если только вывод не был переназначен в файл -i[nput] файл Читает команды не с клавиатурного ввода, а из входного файла. В аргументе файл должен быть указан полный путь. Входные фай- лы могут содержать команды -input, которые вызывают другие файлы, позволяя выполнить ветвление, а затем вернуться, isql подтверждает работу текущего файла до открытия следующего -m [ erge_stdeгг ] Объединяет вывод stderr с stdout. Полезно для помещения вы- вода и ошибок в один файл при выполнении isql в скрипте команд- ной строки или из пакетного файла
828 Часть IX. Инструменты Таблица 37.1 (окончание) Режим Описание -nfoauto] Отключает автоматическое подтверждение операторов DDL. По умолчанию операторы DDL подтверждаются автоматически в раз- дельных транзакциях -nowarnings Отображает предупреждающие сообщения, если и только если появилась ошибка (по умолчанию isql отображает любое сообще- ние, возвращенное в векторе состояния, даже если не появлялось ошибок) -o[utput] файл Записывает результаты в выходной файл, а не в стандартный вы- вод. Аргумент файл должен содержать полный путь к файлу -pas[sword] пароль Используется вместе с -user для задания пароля при соединении с удаленным сервером или когда это требуется для локальной опе- рации. Для доступа пароль и пользователь должны представлять существующую запись в базе данных безопасности на сервере -page[length] n В результате запроса выводит заголовки столбцов через каждые л строк, а не через 20 заданных по умолчанию -q[uiet] Подавляет вывод сообщения "Use CONNECT or CREATE DATABASE . . при отсутствии пути к базе данных в командной строке -r[ole] имя-роли Передает имя-роли вместе с учетными данными пользователя при соединении с базой данных -s[ql_dialect] n Интерпретирует последовательность команд как диалект л до конца сессии или пока диалект не будет изменен оператором set sql dialect. См. разд. "Установка диалекта в isql" ранее в этой главе -tterminator] x Изменяет символ завершения оператора со значения по умолча- нию точка с запятой (;) на х, где х— один символ или любая после- довательность символов -u[ser] пользователь Используется вместе с -password. Задает имя пользователя при соединении с удаленным сервером. Для доступа пароль и пользо- ватель должны представлять существующую запись в базе данных безопасности на сервере -x То же самое, что и -extract -z Отображает версию программы isql Извлечение метаданных В командной строке вы можете использовать режим -extract для вывода операторов DDL, которые определяют метаданные в базе данных. Все зарезервированные слова и объекты извлекаются в файл в виде символов в верх- нем регистре, если только локальный язык не использует набор символов, который не имеет верхнего регистра. Выходной скрипт создается с подтверждением каждого набора команд, следовательно, на каждую таблицу можно ссылаться в последующих
Глава 37. Интерактивный SQL (утилита isql)829 определениях. Выходной файл включает имя объекта и его владельца, если он опре- делен. Необязательный флаг -output перенаправляет вывод в указанный файл. Используйте следующий синтаксис: isql [[-extract | -х] [-a] [[-output | -о] вых одной-фа йл ]] база-данных Режим -х может быть использован в качестве сокращения для -extract. Флаг -а ука- зывает утилите isql на необходимость извлечения всех объектов базы данных. Обра- тите внимание, что спецификация выходного файла выходной-файл должна содержать полный путь к файлу и должна следовать после флага -output. Имя файла и путь из- влекаемой базы данных могут находиться в конце команды. Вы можете использовать результирующий текстовый файл для: ♦ проверки текущего состояния системных таблиц базы данных перед планирова- нием изменений. Это особенно полезно, если база данных значительно изменя- лась после ее создания; ♦ создания базы данных со схемой, идентичной схеме извлекаемой базы данных; ♦ открытия в вашем текстовом редакторе для выполнения изменений определения базы данных или для создания нового исходного файла базы данных. ПРИМЕЧАНИЕ. Функция -extract не всегда бывает столь разумной, как должна бы быть по отношению к зависимостям. Иногда необходимо редактировать вы- \ ходной файл для изменения порядка создания таблиц. Использование isql -extract Следующий оператор извлекает схему SQL из базы данных employee.fdb в файл скрипта схемы с именем employee.sql: isql -extract -output /data/scripts/employee.sql /data/employee.fdb Эта команда эквивалентна: isql -x -output /data/scripts/employee.sql /data/employee.fdb He извлекаются следующие объекты и элементы: ♦ системные таблицы и просмотры, системные триггеры; ♦ коды внешних функций и фильтров blob (они не являются частью базы данных); ♦ атрибуты владельца объектов. Использование isql -а Режим -<е) х (tract) извлекает метаданные только для объектов SQL. Если вы хотите извлечь скрипт схемы, который включает такие объявления, как declare external FUNCTION И DECLARE FILTER, ИСПОЛЬЗуЙТе реЖИМ -а.
830 Часть IX. Инструменты Например, для извлечения операторов DDL из базы данных employee.fdb и помеще- ния в файл employee.sql введите: isql -a -output /data/scripts/employee.sql /data/employee.fdb Пора дальше Теперь мы рассмотрим gbak, утилиту командной строки для резервного копирования и чистки базы данных.
ГЛАВА 38 Резервное копирование и восстановление баз данных (gbak) Корректное резервное копирование базы данных Firebird компактно сохраняет мета- данные и данные базы данных в файле на жестком диске или на другом запоминаю- щем устройстве. Настоятельно рекомендуется перезаписывать файлы резервных ко- пий на сменные носители и хранить в физически защищенном месте в стороне от сервера. Регулярное резервное копирование является основной для поддержания базы данных в хорошем состоянии, обеспечения целостности данных, безопасности и возможно- сти избежать неприятностей. Утилита командной строки резервного копирования и восстановления данных в Firebird gbak создает и восстанавливает резервные копии ваших баз данных. Она создает независимый от платформы архив фиксированного снимка базы данных, который может быть записан в дисковый файл, и он при жела- нии может быть сжат посредством утилит сторонних разработчиков. Резервная копия Firebird является "горячей" копией — нормальная работа с базой данных может продолжаться в то время, как gbak анализирует метаданные и записы- вает файл копии. Операция выполняется в транзакции snapshot, захватывающей со- стояние базы данных, которое она имела на момент старта транзакции. Изменения данных, которые клиенты подтверждают в базе данных после начала резервного ко- пирования, не записываются в файл копии. Д ВНИМАНИЕ! Операционные системы обычно включают средства для архивиро- вания файлов баз данных. Не полагайтесь на копии ваших баз данных Firebird, выполненные такими утилитами или утилитами файловой системы, или утилита- ми сжатия данных, такими как gzip или WinZip, если работа базы данных пол- ностью не завершена. Подобные копии баз данных не являются "гигиеничны- ми" — неубранный мусор будет восстановлен вместе с другими данными файла. Поскольку эти утилиты обычно используют низкоуровневые блокировки диска, их использование приводит к разрушению базы данных, если при их выполнении сохраняются живые соединения с базой данных. Файлы gbak Утилита gbak анализирует и разбивает на части файлы Firebird, сохраняя отдельно метаданные и данные в компактном формате. Копия, сделанная gbak, не является
832 Часть IX. Инструменты файлом базы данных и не будет распознаваться сервером. Чтобы стать используемой, она должна быть восстановлена в формат Firebird, который будет читаем сервером с учетом версии gbak, соответствующей версии выполняемого сервера Firebird. При восстановлении файлов копии в формат базы данных gbak выполняет проверку метаданных и данных перед внутренним использованием языка запросов для воссоз- дания базы данных и наполнения ее данными. Если обнаружено разрушение данных, gbak останавливает восстановление и выдает сообщение. Возможность этой утилиты анализировать проблемы делает ее неоцени- мо полезной при попытках восстановления разрушенной базы данных. Если вы нахо- дитесь в такой грустной ситуации, обратитесь к приложению 4. Д ВНИМАНИЕ! Выполнение резервного копирования является лишь одной сторо- ной защиты схемы. Если вы ослеплены верой в сохранение целостности при ре- зервном копировании, то, независимо от используемой вами системы резервного копирования, вы будете неизбежно наказаны. Тот факт, что копирование завер- шилось, не является гарантией, что копия будет восстановлена. Делайте тесто- вое восстановление периодически или регулярно вместе с резервным копирова- нием. Другие таланты gbak Утилита gbak также выполняет последовательность других важных задач в процессе анализа, сохранения и восстановления базы данных. Одни являются автоматически- ми, другие должны быть запрошены при использовании переключателей при вызове программы из командной строки. В процессе копирования gbak выполняет сборку мусора устаревших записей — не- обязательный переключатель, выполняется по умолчанию. Если он включен, его дей- ствие появляется, даже если вы не восстанавливаете копию, а начинаете работать с чистым файлом. Заметьте при этом, что выполнение gbak не упаковывает базу, как это делает копирование — выполняйте полную сборку мусора для выполнения этого. Задачи восстановления могут включать: ♦ балансировку индексов для улучшения производительности вашей базы данных; ♦ освобождение пространства, занимаемого удаленными записями, и упаковку ос- тальных данных. Это часто уменьшает размер базы данных и улучшает произво- дительность с "упакованными" данными; ♦ по желанию изменение размера страницы при восстановлении; ♦ изменение владельца базы данных. Это по желанию— но смотрите! Это может привести к авариям, если вы не будете осторожны; ♦ обновление базы данных InterBase до Firebird или с меньшей версии базы данных Firebird до более высокой версии (например, обновление ODS); ♦ разделение базы данных на множество файлов или изменение размеров сущест- вующих нескольких файлов. Это выполняется по желанию;
Глава 38. Резервное копирование и восстановление баз данных (gbak) 833 ♦ распределение многофайловой базы данных на несколько дисков. Это выполняет- ся по желанию. Копирование и восстановление также играют важную роль при наличии неприятных событий, которые могут разрушать базы данных. Подробности осуществления стра- тегии восстановления базы данных см. в приложении 4. Обновление структуры на диске (ODS) Вероятно, основным изменением в новых релизах сервера Firebird является измене- ние структуры на диске (On-Disk Structure, ODS). Если ODS изменилась, и вы хотите использовать преимущества новых возможностей Firebird, обновите ваши базы дан- ных до новой ODS. Новая версия сервера может работать с базами данных, создан- ными в некоторых предыдущих версиях, однако сервер не может использовать свои новые возможности для старой базы данных с более низким значением ODS. Вы можете использовать большинство баз данных Firebird 1.0.x непосредственно в Firebird 1.5, хотя ODS версии 1.5 (10.1) выше, чем в версии 1.0.x (10.0). Тем не менее рекомендуется выполнить процедуру обновления для ваших баз данных, обновив их с 10.0 до 10.1, для получения преимуществ повышения производительности за счет возможности индексирования системных таблиц в версии 1.5. Для обновления существующих баз данных до новой версии ODS выполните сле- дующие шаги: 1. Перед инсталляцией новой версии ODS Firebird выполните резервное копирова- ние баз данных с использованием старой версии gbak. 2. Установите новую версию сервера Firebird, как описано в главе 1. 3. После инсталляции новой версии восстановите базы данных с использованием утилиты gbak новой версии из каталога /bin корневого каталога Firebird. Диалект 1 баз данных Диалект 1 базы данных остается диалектом 1 и после восстановления базы данных. База данных остается ограниченной возможностями языка InterBase 5, хотя некото- рые ограничения применяются к диалекту 1 баз данных в новой версии ODS, кото- рые не применялись к InterBase 5. Например, в Firebird есть множество новых заре- зервированных слов, которые будут недопустимыми в базе данных диалекта 1. Пол- ный список зарезервированных слов см. в приложении 11. Немного отойдя от обсуждаемой темы, скажем, что можно изменить диалект восста- новленной базы данных диалекта 1 в диалект 3 при использовании инструмента gfix, обсуждаемого в главе 39. Однако такая миграция не является простым делом. Язык не является единственным, в чем различаются диалекты. Различия в типах данных не менее важны, если не более. Например, домены и столбцы чисел с фиксированной точкой сохраняют свои старые атрибуты при изменении базы данных до диалекта 3. Типы numeric и decimal с точ- ностью выше 10 в диалекте 1 хранятся в виде double precision. Преобразование в 27 Зак. 420
834 Часть IX. Инструменты 64-битовые числа с фиксированной точкой не выполняется при изменении диалекта на 3 — вы должны создать новые столбцы требуемого типа и выполнить преобразо- вание старых значений в новые столбцы. Firebird унаследовал некоторые возможности для выполнения миграции из диалек- та 1 в родной SQL-диалект Firebird (диалект 3). Известно, что существуют гораздо более простые и безопасные способы выполнить такую миграцию. Лучшие результа- ты получаются, если создать скрипт, выполнив извлечение метаданных, изменить его при необходимости и восстановить в "пустую" базу данных диалекта 3. Широко дос- тупны прекрасные свободно распространяемые или недорогие инструменты для за- полнения вашей новой базы данных вашими старыми данными. См. в приложении 5 рекомендации, а в конце главы 8 ссылки на специальные темы миграции. Права на выполнение копирования и восстановления базы данных Использование gbak для копирования и восстановления ограничено пользователем SYSDBA и владельцем этой базы данных. ПРИМЕЧАНИЕ. Любой пользователь может восстановить базу данных при ис- пользовании переключателя -с[reate] (см. разд. "Выполнение восстановле- ния"), если создаваемая база данных не будет перекрывать существующую базу данных. Изменение владельца базы данных Восстановленный файл базы данных или созданный из gbak имеет владельцем того пользователя, который выполнил восстановление. Следовательно, выполнение копи- рования и восстановления является механизмом для изменения владельца базы дан- ных. ВНИМАНИЕ! Любой может украсть базу данных Firebird путем восстановления базы данных с резервной копии на машине, где ему известен пароль пользова- теля SYSDBA. Важно обеспечить, чтобы ваши резервные копии были защищены от неавторизованного доступа1. Имя пользователя и пароль Когда Firebird проверяет полномочия при запуске gbak, он определяет пользователя в соответствии со следующей иерархией: 1 Вопросы безопасности файловой системы и других средств окружения обсуждаются в главах 33 и 34.
Гпава 38. Резервное копирование и восстановление баз данных (gbak) 835 1. Имя пользователя и пароль, заданные переключателями в команде gbak. 2. Только для локального варианта gbak2. Имя пользователя и пароль, заданные в переменных окружения isc_userh isc_password, проверяются на присутствие в ба- зе данных безопасности (security.fdb в версии 1.5 и isc4.gdb для версии 1.0.x). Со- хранение постоянно установленных значений этих переменных строго не реко- мендуется, поскольку крайне небезопасно. 3. Только для POS1X. Если не предоставлено учетных данных пользователя ни на одном из предыдущих уровней, Firebird позволяет пользователю root запускать gbak с сервера или с доверенной машины. Выполнение копирования Для вызова gbak или перейдите в каталог Firebird /bin, где размещается gbak, или укажите для утилиты полный путь. Вся команда должна размещаться в одной строке. В описании синтаксиса и примерах логически замкнутые части команды представле- ны в виде отдельных строк с отступами. POSIX: $] ./gbak -bfackup] <режимы> исходные-данные копия [п) ИЛИ $] /opt/firebird/bin/gbak -b[ackup] <режимы> исходные-даиные копия [л] Windows: C:\Program Files\Firebird\Firebird_l_5\bin> gbak -bfackup] <режмы> исходные-данные копия [л] или С:\> C:\Program Files\Firebird__l_5\bin\gbak -bfackup] <режимы> исходные-да иные копия [л] Аргументы для gbak -b[ackup] исходные-даиные— это полный путь и имя файла копируемой базы данных. В Firebird 1.5 это может быть алиас. При копировании многофайловой базы данных используйте имя только первого (первичного) файла базы данных. копия— полный путь и имя файла, куда помещается резервная копия базы данных. В случае, когда копия помещается во множество файлов, нужно указать несколько копий. Синтаксический элемент п является целым параметром, включаемым для каж- 2 Если вы по-настоящему беспечны и оставите эти переменные окружения на рабочих станциях, показывая всему миру пароль пользователя SYSDBA, удаленные пользователи так- же смогуч выполнять копирование, не задавая учетных данных. Нечего и говорить, что такая практика должна быть полностью исключена.
836 Часть IX. Инструменты дого выходного файла, кроме последнего, для указания размера файла в байтах (по умолчанию). К числу могут быть добавлены буквы в нижнем регистре для задания того, что размер указан в килобайтах (к), мегабайтах (т) или гигабайтах (д). См. сле- дующий пример. В POSIX копией также может быть stdout. В этом случае gbak записывает результат в стандартный вывод (обычно канал)3. Режимы могут быть допустимой комбинацией переключателей из табл. 38.1. Переклю- чатели чувствительны к регистру. Переключатели копирования В табл. 38.1 описаны переключатели, которые могут быть использованы в gbak при выполнении копирования. Таблица 38.1. Переключатели gbak для копирования Переключатель Эффект -b[ackup_database] Утилита gbak выполняет копирование указанной базы дан- ных в файл или на устройство -со[nvert] Конвертирует внешние файлы во внутренние таблицы. При восстановлении любая внешняя таблица преобразуется во внутреннюю таблицу базы данных, а связь с внешним фай- лом будет уничтожена -е[xpand] Создает копию без компрессии данных -fa[ctor] n Использует коэффициент блокирования п для ленточных устройств -g[arbage_collect] Подавляет сборку мусора в процессе копирования. Исполь- зуйте этот переключатель, если вы планируете сразу же восстановить базу данных с этой копии, gbak не сохраняет мусор, следовательно, не имеет смысла увеличивать на- кладные расходы, если вы не собираетесь позже использо- вать старую базу данных -iIgnore] Контрольные суммы игнорируются в процессе копирования. Вы можете использовать этот переключатель при повтор- ном запуске копирования, которое аварийно завершилось из-за ошибок контрольных сумм -1[imbo] (Буква "L" в нижнем регистре). Зависшие 2РС транзакции будут проигнорированы. Не используйте данный переключа- тель для регулярного копирования. Это позволяет выпол- нить чистку после того, как двухфазная транзакция не вы- полнила подтверждения из-за потери связи с сервером 3 В некоторых случаях администраторы БД перенаправляют вывод gbak в утилиты упаков- ки, такие как gzip. Это позволяет "на лету" получить запакованную резервную копию базы данных с меньшей нагрузкой на диск в процессе создания или восстановления резервной ко- пии. — Прим. науч. ред.
Гпава 38. Резервное копирование и восстановление баз данных (gbak) 837 Таблица 38.1 (окончание) Переключатель Эффект -m[etadata] Копируются только метаданные — данные не сохраняются. Это может быть быстрым способом для получения "пустой" базы данных при подготовке к поставке заказчикам продукта -nt Создает копию в непереносимом формате. По умолчанию данные, сохраняемые в файлах gbak, записываются в фор- мате XDR, являющемся стандартным протоколом для дан- ных, переносимых между платформами -ol[d_descriptions] Устаревший переключатель— он сохраняет метаданные в старом формате InterBase -pa[ssword] пароль Проверяет пароль перед доступом к базе данных. Это тре- буется (вместе с именем пользователя) для удаленного копирования, а также для локального, если недоступны пе- ременные окружения iscjjser и isc_password. Обратите внимание, что сокращение переключателя -password (-ра) для gbak отличается от сокращения для isql (-pas) -role имя Соединяется с базой данных под указанной ролью. В на- стоящий момент похоже, что это бессмысленный переклю- чатель, который вы можете просто проигнорировать -se[rvice] сервис Создает файлы копии на хосте, где располагаются и исход- ные файлы базы данных. Аргумент сервис вызывает Ме- неджер сервисов на сервере. Подробный синтаксис см. в разе. "Использование gbak с Менеджером сервисов Firebird" -t transportable] Сохраняет данные gbak в переносимом формате XDR. Это значение по умолчанию. Для сохранения данных в сжатом родном формате используйте переключатель -nt -u[ser] имя Проверяет имя пользователя перед доступом к базе данных. Это требуется (вместе с паролем) для удаленного копиро- вания, а также для локального, если недоступны перемен- ные окружения iscjjser и isc_password -v[erbose] Выводит подробные сообщения о том, что делает gbak. Вы можете вывести текст в файл, задав переключатель -у -у { файл | suppress_output} Направляет сообщения о состоянии в файл, полный путь к файлу, который вы хотите создать. Операция завершится с ошибкой, если файл уже существует. Если копирование завершится нормально и не был использован переключа- тель -v[erbose], то этот файл будет пустым. suppress_output может быть использовано вместо "молча- ливого" копирования, при котором не выводится вообще никаких сообщений — z Показывает версии gbak и сервера Firebird
838 Часть IX. Инструменты Переносимые копии Используйте значение по умолчанию, переключатель -transportable, если вы рабо- таете в многоплатформенном окружении. При этом данные записываются в меж- платформенном стандартном формате external Data Representation (XDR)4, позво- ляющем программе gbak читать файл на аппаратной платформе, отличной от той, на которой была создана копия. Копии разных версий Программа gbak на серверах со значением ODS более низким, чем сервер Firebird, который создал базу данных, обычно не сможет восстановить копию с более высоким значением ODS. Однако на практике версия gbak из InterBase 5.x проявляет спо- собности восстанавливать большинство баз данных диалекта 1, созданных в Firebird 1.0.x. Д ВНИМАНИЕ! Вы никогда не должны пытаться копировать базы данных програм- мой gbak с версией, не соответствующей версии сервера, с которым выполняет- ся база данных. Копирование в один файл Для простого локального копирования однофайловой или многофайловой базы дан- ных используйте: gbak -b d:\data\ourdata.fdb d:\data\backups\ourdata.fbk Имя исходного файла задается независимо от того, является ли копируемая база дан- ных однофайловой или многофайловой. Когда вы выполняете копирование много- файловой базы данных, в команде копирования задавайте только первый файл. Пути ко второму и последующим файлам будут найдены утилитой gbak в базе данных и в заголовках файлов в процессе резервного копирования. Если вы зададите имена по- следующих файлов, они будут интерпретироваться как имена файлов копии. Файл копии может иметь любое имя, какое вы ему зададите, если оно будет допус- тимым в той файловой системе, в которой этот файл записывается. Копирование многофайловой базы данных во множество файлов Когда вы копируете многофайловую базу данных во множество файлов с помощью gbak, не требуется соответствия файлов базы данных файлам копии. Если существует более одного файла копии, то имена и размеры файлов копии должны быть заданы 4 Информацию о стандарте XDR можно найти на http://asg.web.cmu.edu/rfc/rfcl832.html.
Глава 38. Резервное копирование и восстановление баз данных (gbak)839 для всех файлов за исключением последнего файла в наборе. По умолчанию размер файла (целое число) задается в байтах. Для изменения этого добавьте букву в нижнем регистре, чтобы указать утилите gbak, что задаете размер в килобайтах (к), мегабайтах (т) или гигабайтах (д). Следующая команда выполняет копирование базы данных в три файла копии в раз- личных разделах файловой системы и выводит подробный протокол. Все это одна команда, множество строк с отступами здесь показаны лишь для удобства чтения. POS1X: ./gbak -b /data/accounts.fdb /backups/accounts.fbl 2g /backups2/accounts.fb2 750m /backups3/accounts.fb3 -user SYSDBA -password mlllpOnd -v -y /logs/backups/accounts.20040703.log Windows: gbak -b d:\data\accounts.fdb e:\backups\accounts.fbl 2g f:\backups2\accounts.fb2 750m g:\backups3\accounts.fb3 -user SYSDBA -password mlllpOnd -v -y d:\data\backuplogs\accounts.20040703.log Копия однофайловой базы данных во множество файлов Если вы копируете однофайловую базу данных во множество файлов копий, синтак- сис идентичен. Фактически gbak не интересуется тем, является ли ваша исходная база данных одно- или многофайловой. Важно отметить некоторые моменты. ♦ Копирование не будет выполняться, если любой из файлов копии будет меньше чем 2048 байт. Если вы ведете протокол, причина появится в протоколе. ♦ Утилита gbak заполняет указанные файлы в порядке слева направо. Она не начнет работать со следующим файлом, пока предыдущий не достигнет указанного раз- мера. В предыдущем примере файл accounts.fb3 не будет создан, пока accounts.fb2 не будет заполнен. ♦ Пути для файлов копии не обязательно должны находиться под физическим управлением хоста, однако если вы используете переключатель -service (см. разд. "Использование gbak с Менеджером сервисов Firebird") в системах, где дей- ствуют полномочия к файлам, ваш пользовательский профиль должен иметь соот- ветствующие полномочия на запись данных. В некоторых инсталляциях версии 1.5 это может быть по умолчанию пользователь или группа firebird; в некоторых ин- сталляциях версии 1.0.x это может быть по умолчанию пользователь interbase. Копирование только метаданных Копирование только метаданных обычно требуется для создания "пустой" базы дан- ных, когда вы готовы поставлять систему пользователям, загружать данные или вое-
840 Часть IX. Инструменты станавливать базу с целью миграции. Следующая команда выполняет копирование только метаданных нашей базы данных accounts: gbak -b -m d:\data\accounts.fdb e:\QA\accounts.fbk Выполнение удаленного копирования Если вы запускаете gbak с удаленной клиентской машины, она записывает файлы копии в текущий каталог или в локальный каталог, заданный полным путем. Если вы задаете размещение для файла копии, оно должно быть доступно с машины, на кото- рой выполняется gbak. Размещение может быть одним из следующих: ♦ на диск или в разделе, который размещен на клиентской машине; ♦ на устройстве, к которому имеет доступ локальная машина (Windows); ♦ в сетевой файловой системе (NFS) (Linux/UNIX). При задании переключателя -service] вы можете вызвать Менеджер сервисов Fire- bird с удаленного сервера и заставить gbak передать выполнение вашей команды сер- веру. В этом случае локализация базы данных и файлов копии должна осуществлять- ся с точки зрения размещения с серверной машины. Каталог, из которого была запу- щена программа gbak, не действует при копировании через Менеджер сервисов. Подробнее о Менеджере сервисов см. далее разд. "Использование gbak с Менедже- ром сервисов Firebird". Решение вопросов безопасности Хорошей мерой предосторожности является присваивание атрибутов только для чте- ния вашим файлам копии на уровне файловой системы после их создания для пре- дотвращения их случайной или намеренной перезаписи. Вы можете защитить ваши базы данных от воровства в системах UNIX и Win- dows NT/2000/XP, поместив файлы копии в каталоги с ограниченным доступом. Д ВНИМАНИЕ! Файлы копий, которые хранятся в системах Windows 95/98/МЕ или в областях со свободным доступом в других системах, являются совершенно беззащитными. Возвращаемые коды и ответная реакция Копирование базы данных, выполняемое под Windows, возвращает код 0 при успеш- ном завершении и 1 при ошибках. Если встретилась ошибка, посмотрите файл firebird.log (interbase.log в версии 1.0.x). Для получения полного протокола копирова- ния используйте переключатели -у и -v.
Гпава 38. Резервное копирование и восстановление баз данных (gbak)841 Выполнение восстановления Синтаксис восстановления базы данных из копии следующий. POS1X: $] ./gbak {~c[reate] | -г[eplace_database] } <режимы> исходная-копия база-данных ИЛИ $] /opt/firebird/bin/gbak {-с[reate] | -г[eplace_database] } <режимы> исходная-копия база-данных Windows: C:\Program Files\Firebird\Firebird_l_5\bin> {-с[reate] | -г[eplace_database] } <режимы> исходная-копия база-данных ИЛИ С:\> C:\Program Files\Firebird_l_5\bin\gbak {-cfreate] | -г[eplace_database] } <режимь£> исходная-копия база-данных Аргументы gbak для восстановления исходная-копия— полный путь и имя файла копии gbak. Если копия содержит не- сколько файлов, укажите только первый (первичный) файл gbak. В POSIX исходной- копией может быть также stdin, в этом случае gbak читает свои входные данные из стандартного ввода (обычно канал). База-данных— полный путь и имя файла восстанавливаемой базы данных. В Firebird 1.5 это может быть алиас. База может быть одним файлом или множеством файлов. Возможные варианты синтаксиса баз данных обсуждаются в следующих разделах этой главы. Режимы могут быть допустимой комбинацией переключателей из табл. 38.2. Переклю- чатели нечувствительны к регистру. Переключатели восстановления В табл. 38.2 перечисляются и описываются переключатели, которые могут быть ис- пользованы в gbak при выполнении восстановления базы данных. Таблица 38.2. Переключатели gbak для выполнения восстановления базы данных Переключатель Эффект -с[reate_database] Восстанавливает базу данных в новый файл -b[uffers] Устанавливает значение по умолчанию для размера кэша (в страницах базы данных) для восстанавливаемой базы данных
842 Часть IX. Инструменты Таблица 38.2 (продолжение) Переключатель Эффект -iInactive] Делает неактивными индексы в восстанавливаемой базе дан- ных. Полезно при повторной попытке восстановления, когда первая попытка оказалась неудачной из-за ошибок индекса Подавляет создание ранее определенных теневых копий (shadow)5 -mo[de] {[read_write | read_only} Задает, будет ли восстанавливаемая запись только для чтения или для чтения/записи. Возможные значения read_write (по умолчанию) и read_only -n[o_validity] Удаляет ограничения проверки данных из восстанавливаемых метаданных. Используйте, если вам нужно повторить попытку восстановления, если первая попытка оказалась неудачной из- за нарушений ограничения check —о[ne_at_a_time] Восстанавливает одну таблицу за один раз. Может быть исполь- зовано при частичном восстановлении, если база данных со- держит разрушенные данные -p[age_size] n Устанавливает новый размер страницы в л байтах (1024, 2048, 4096, 8192 или 16 384). Размер по умолчанию 4096. Размер страницы 16 384 байт невозможен, если файловая система не поддерживает 64-битовый ввод/вывод -pafssword] пароль Проверяет пароль вместе с -u[ser] перед попыткой создания базы данных -r[eplace_database] Восстанавливает базу данных, заменяя существующий файл с тем же именем, если он существует; если нет, создает новый файл с указанным именем -se[rvice] сервис Создает восстанавливаемую базу данных на хосте, где распола- гаются файлы копии. Используйте этот переключатель, если вы запустили gbak с удаленного узла и хотите восстановить из ко- пий, размещенных на том же сервере, что и база данных. Это вызывает Менеджер сервисов Firebird на серверной машине, экономит время и сетевой трафик. См. подробности в разд. "Ис- пользование gbak с Менеджером сервисов Firebird" -u[ser] имя Проверяет пользователя имя вместе с -pa[ssword] перед по- пыткой создания базы данных -use_[all_space] Восстанавливает базу данных со 100-процентным заполнением каждой страницы данных вместо значения по умолчанию 80-процентное заполнение. Это идеал для баз данных только для чтения, поскольку им не надо иметь зарезервированное пространство на страницах базы данных для хранения версий строк при добавлении, изменении или удалении. Чтобы вернуть восстановленную базу данных к обычному коэффициенту заполнения, используйте gfix с переключателем -use (т. е. gfix -use reserve) 5 Будьте внимательны, -к обеспечивает не только восстановление БД без shadow, но и уда- ление существующих shadow с именами, сохраненными в файле резервной копии. Этот баг исправлен только в Firebird 2.0 (см. баг № 1122344 в багтрекере проекта Firebird: sf.net/projects/firebird). — Прим. науч. ред.
Гпава 38. Резервное копирование и восстановление баз данных (gbak) 843 Таблица 38.2 (окончание) Переключатель Эффект -vferbose] Выводит подробные сведения о том, что выполняет gbak. Вы можете по желанию вывести этот текст в файл при использова- нии переключателя -у -у { файл | suppre з s__output} Направляет сообщения в файл, задающий полный путь к файлу, который вы хотите создать. Вызовет ошибку, если указанный файл уже существует. Если восстановление завершится нор- мально и не был использован переключатель -vferbose], то этот файл будет пустым. suppress_output может быть исполь- зовано вместо "молчаливого" восстановления, при котором не выводится вообще никаких сообщений -z Показывает версии gbak и сервера Firebird Восстанавливать или создавать? Понятие "восстановление базы данных" при ее перезаписывании родилось в годы, когда дисковое пространство было дороже, чем прием на работу эксперта по рекон- струкции разрушенной базы данных или формирование группы персонала по данным для восстановления системы компании по бумажным носителям. Короче говоря, перезаписывание базы данных, чья жизнеспособность вас сильно волнует, не рекомендуется ни при каких условиях. Поразмышляйте о таких тягост- ных фактах: ♦ если восстановление завершается с ошибкой, переписываемая база данных навсе- гда умрет— и дела пойдут плохо при любом восстановлении; ♦ восстановление поверх существующей базы данных, которая находится в исполь- зовании, приведет к ее разрушению; ♦ возможность пользователей соединяться с частично восстановленной базой дан- ных также приведет к ее разрушению. СОВЕТ. "Горячее" копирование— нормально. "Горячее" восстановление — большая глупость. Если вы все-таки, несмотря на риск, решили использовать -r[epiace_database], то вы можете делать это, если при соединении будете предоставлять учетные данные вла- дельца базы данных или пользователя SYSDBA. Любой пользователь, описанный на сервере, может восстановить базу данных с использованием режима -cfreate]. Рас- смотрите последствия этого факта и примите соответствующие меры предосторож- ности, чтобы уберечь ваши копии от чужих рук.
844 Часть IX. Инструменты Объекты, определенные пользователем При восстановлении копии на сервер, отличный от того, с которого были сделаны копии, вы должны обеспечить существование на новом сервере наборов символов и порядков сортировки, на которые ссылается копия. Копия не может быть восстанов- лена, если отсутствуют языковые объекты. Библиотеки внешних функций и фильтров blob, на которые ссылаются объявления в базе данных, точно так же должны присутствовать, чтобы работа происходила без ошибок. Восстановление в один файл Следующая команда выполняет простое восстановление из одного файла копии в один файл базы данных: gbak -с d:\data\backups\ourdata.fbk d:\data\ourdata_trial.fdb Многофайловое восстановление Один или несколько файлов копии могут быть восстановлены в одно- или многотом- ные файлы базы данных. Не существует требования соответствия один к одному ме- жду томами файлов копии и томами файлов базы данных. При восстановлении из многофайловой копии вы должны указать все файлы копии в том порядке, в котором они создавались, gbak громко пожалуется, если получит список в неправильном порядке или какой-нибудь том будет отсутствовать. Для файлов базы данных вы должны задать параметр размера для каждого файла за исключением последнего. Минимальное значение— 200 страниц базы данных. Раз- мер последнего файла всегда увеличивается до размера, необходимого для заполне- ния доступного пространства. Восстановление однотомной копии в многотомную базу данных POSIX: ./gbak -с /backups/stocks.fbk /data/stocks_trial.fdb -user SYSDBA -password mlllpOnd -v —у /logs/backups/stocks_r.20040703.log Windows: gbak —c e:\backups\stocks.fbk d:\data\stocks_trial.fdb -user SYSDBA -password mlllpOnd -v —у d:\data\backuplogs\stocks_r.20040703.log Если вы зададите несколько файлов базы данных, но имеющих небольшой объем данных, то размер файлов будет достаточно мал — приблизительно 800 Кбайт для
Гпава 38. Резервное копирование и восстановление баз данных (gbak) 845 первого файла и 4 Кбайт для последующих. В процессе заполнения данными они будут последовательно увеличиваться в размерах до заданной величины. Восстановление многотомной копии в однотомную базу данных POSIX: /gbak -с /backups/accounts.fbl /backups2/accounts.fb2 /backups3/accounts.fb3 /data/accounts_trial.fdb -user SYSDBA -password mlllpOnd -v -y /logs/backups/accounts.20040703.log Windows: gbak —c e:\backups\accounts.fbl f:\backups2\accounts.fb2 g:\backups3\accounts.fb3 d:\data\accounts__trial.fdb -user SYSDBA -password mlllpOnd -v —у d:\data\backuplogs\accounts.20040703.log Восстановление нескольких файлов из нескольких файлов POSIX: /gbak -с /backups/accounts.fbl /backups2/accounts.fb2 /backups3/accounts.fb3 /data/accounts_trial.fdl 500000 /data/accounts_trial.fd2 -user SYSDBA -password mlllpOnd -v -y /logs/backups/accounts.20040703.log Windows: gbak —c e:\backups\accounts.fbl f:\backups2\accounts.fb2 g:\backups3\accounts.fb3 d:\data\accounts_trial.fdb 500000 d:\data\account_trial.fd2 -user SYSDBA -password mlllpOnd -v -y d:\data\backuplogs\accounts.20040703.log Возвращаемые коды и ответная реакция Восстановление базы данных, выполняемое под Windows, возвращает код 0 при успешном завершении и 1 при ошибках. Если встретилась ошибка, посмотрите файл firebird.log (interbase.log в версии 1.0.x). Размер страницы и размер кэша по умолчанию При восстановлении вы можете изменить размер страницы, включив в команду пере- ключатель -p[age_size], за которым следует целое число, задающее размер в байтах. Допустимые размеры страниц см. в табл. 38.2.
846 Часть IX. Инструменты В этом примере gbak восстанавливает базу данных с размером страницы 8192 байт: gbak -с -р 8192 d:\data\backups\ourdata.fbk d:\data\ourdata_trial.fdb Аналогичным образом вы можете использовать восстановление для изменения раз- мера кэша базы данных по умолчанию (в страницах или в "буферах"): gbak -с -buffers 10000 d:\data\backups\ourdata.fbk d:\data\ourdata_trial.fdb Размер страницы и производительность Размер восстанавливаемой базы данных задается в страницах базы данных. Размер файла базы данных по умолчанию равен 200 страницам. Размер страницы базы дан- ных по умолчанию 4 Кбайт, следовательно, если размер страницы не был изменен, то размер базы данных по умолчанию будет 800 Кбайт. Этого достаточно только для очень маленькой базы данных. Изменение размера страницы может повысить производительность при определен- ных условиях. ♦ Firebird работает лучше, если размер строк много меньше размера страницы. По- думайте об увеличении размера страницы, если база данных содержит часто ис- пользуемые таблицы с большими строками данных. ♦ Если база данных содержит большие индексы, то больший размер страницы базы данных уменьшает количество уровней в индексном дереве. Чем меньше глубина индекса, тем быстрее он может быть просмотрен. Подумайте об увеличении раз- мера страницы, если глубина индекса превышает три для любого часто исполь- зуемого индекса. См. главу 18, особенно разд. "Тема оптимизации" ближе к концу главы и замечания по использованию утилиты gstat для определения того, на- сколько хорошо выполняются ваши индексы. ♦ Хранение и отыскание данных blob наиболее эффективны, когда целый blob рас- полагается на одной странице базы данных. Если приложение хранит множество blob, превышающих 4 Кбайт, больший размер страницы сокращает время доступа к данным blob. ♦ Сокращение размера страницы может быть более подходящим, если большинство транзакций используют только небольшое количество строк данных, поскольку объем перемещаемых туда и обратно данных будет меньше, и меньше памяти бу- дет использовано для дискового кэша. Использование gbak с Менеджером сервисов Firebird Переключатель -se[rvice_mgr] вызывает Менеджер сервисов на (обычно) удаленном сервере. Это может сэкономить значительный объем времени и сетевого трафика, когда вы хотите создавать файлы копии или базы данных на том же хосте, где раз- мещается база данных. На сервере Windows с локальным соединением использование Менеджера сервисов не дает никаких преимуществ.
Гпава 38. Резервное копирование и восстановление баз данных (gbak) 847 На сервере POSIX это экономит время и трафик — даже при локальном соединении. Если вы выполняете gbak с переключателем -service, то утилита работает другим образом. Это приводит к тому, что gbak вызывает функции копирования и восста- новления Менеджера сервисов Firebird на сервере, где находится база данных. Вы можете копировать во множество файлов и восстанавливать из множества файлов при использовании Менеджера сервисов. Переключатель -se получает аргумент, который состоит из имени хоста подключен- ного сервера с константной строкой service_mgr через специальный символ. Синтак- сис этого аргумента варьируется в соответствии с используемым сетевым прото- колом: ♦ TCP/IP: hostname:service_mgr; ♦ именованные каналы (Named Pipes): \\hostname\service_mgr. Восстановление в POSIX Пользователь, который был текущим на сервере, когда был вызван Менеджер серве- ров для выполнения копирования — root, firebird или interbase — является владель- цем файла копии на уровне файловой системы, что позволяет читать его только этим пользователем. Когда вам нужно восстановить базу данных на сервере POSIX, которая была скопи- рована с использованием Менеджера сервисов, вы должны или использовать Менед- жер сервисов, или соединиться с системой как владелец этого файла. Когда режим -service не используется, владение файлом копии присваивается тому, кто выполнял gbak. Эти ограничения не применяются к платформе Windows. Копирование В этом примере мы копируем базу данных, находящуюся на диске D: удаленного сервера, в файл копии на диске F: той же самой удаленной машины. Мы направляем подробный отчет об операции в файл протокола в другом каталоге. Как обычно, пример является одной строкой: gbak -b -se hotchicken: service__mgr d: \data\stocks.fdb f:\backups\stocks.20040715.fbk -v -y f:\backups\logs\stocks.20040715.log < ПРИМЕЧАНИЕ. Порядок переключателей имеет значение, когда вы используете переключатель -se. Если вы хотите определить файл протокола, убедитесь, что X—вы поместили его после задания хост-сервера, чтобы избежать ошибки в коман- де, по причине невозможности отыскать размещение файла протокола.
848 Часть IX. Инструменты Восстановление Следующий пример восстанавливает многотомную базу данных из каталога /January на сервере hotchicken в каталог /currentdb. Он использует переключатель -г[epiace_database] и будет перезаписывать базу данных magic.fdb, если она будет найдена в /currentdb. Первые два файла восстанавливаемой базы данных имеют дли- ну 500 страниц, а последний будет увеличиваться в размерах по необходимости. gbak -г -user frodo -pas pipeweed -se hotchicken:service__mgr /january/magicl.fbk /january/magic2.fbk /january/magicLast.fbk /currentdb/magic.fdb 500 /currentdb/magic.fd2 500 /currentdb/magic.fd3 Следующий пример выполняется на сервере hotchicken и восстанавливает копию, которая находится на hotchicken, на другой сервер с именем icarus: gbak -с -user frodo -pas pipeweed -se hotchicken:service_mgr /january/magic.fbk 1 earns:/currentdb/magic.fdb Сообщение об ошибках gbak В табл. 38.3 описаны сообщения об ошибках, которые могут возникнуть в процессе копирования и восстановления, вместе с некоторыми советами, как поступать с эти- ми ошибками. Таблица 38.3. Сообщения об ошибках gbak при копировании и восстановлении Сообщение об ошибке Причины и рекомендуемые действия Array dimension for column <string> is invalid (Ошибочная размерность массива для столбца <строка>) Исправьте определение массива перед копированием Bad attribute for RDB$CHARACTER_SETS (Неверный атрибут для RDB$CHARACTER_SETS) Используется несовместимый набор сим- волов Bad attribute for RDBSCOLLAT1ONS (Неверный атрибут для RDB$COLLATIONS) Исправьте атрибут в указанной системной таблице Bad attribute for table constraint (Неверный атрибут для ограничения таблицы) Проверьте ограничения целостности. Если ошибка возникла при восстановлении, рас- смотрите возможность использования ре- жима -no_validity для удаления проверки ограничений Blocking factor parameter missing (Отсутствует параметр коэффициента блокирования) Задайте числовой аргумент для режима "коэффициент" (например, для ленточного устройства для копии)
Гпава 38. Резервное копирование и восстановление баз данных (gbak) 849 Таблица 38.3 (продолжение) Сообщение об ошибке Причины и рекомендуемые действия Cannot commit files (Невозможно подтвердить файлы) База данных содержит дефекты или нару- шения ограничений целостности метадан- ных. Попытайтесь восстановить таблицы с использованием режима -one_at_a_time или удалите проверку ограничений, исполь- зуя режим ~no_validity Cannot commit index <string> (Невозможно подтвердить индекс <строка>) Возможно, данные конфликтуют с опреде- ленными индексами. Попытайтесь выпол- нить восстановление с использованием "неактивного" режима для предотвращения создания индексов Cannot find column for blob ... (Невозможно найти столбец blob) Используйте -one_at_a_time для отыска- ния проблемной таблицы Cannot find table <string> . . . (Невозможно найти таблицу <строка>) То же Cannot open backup file <string> (Невозможно открыть файл копии <строка>) Исправьте имя файла копии и выполните снова Cannot open status and error output file <string> (Невозможно открыть выходной файл состояния и ошибок <строка>) Сообщения были направлены в файл с неверным именем или в файл, который уже существует. Проверьте формат задания файла или полномочия к каталогу выходно- го файла, либо удалите существующий файл, либо выберите другое имя для фай- ла протокола Commit failed on table <string> (Ошибка подтверждения таблицы <строка>') Разрушение данных или нарушение ограни- чения целостности в указанной таблице. Проверьте метаданные или восстановите "одну таблицу за раз" Conflicting switches for backup/restore (Конфликт переключателей для копирования/восстановления) Режим только для копирования и режим только для восстановления были использо- ваны в одной операции. Исправьте и снова выполните команду Couxd not open file name <string> (Невозможно открыть файл с именем <строка>} Исправьте имя файла и снова выполните команду Could not read from file <string> (Невозможно читать из файла <строка>) Исправьте имя файла и снова выполните команду Could not write to file <string> (Невозможно писать в файл <строка>) Исправьте имя файла и снова выполните команду Datatype n not understood (Тип данных n неизвестен) Где-то задан неверный тип данных. Про- верьте метаданные и при необходимости повторите команду с использованием - one_a t_a_t ime
850 Часть IX. Инструменты Таблица 38.3 (продолжение) Сообщение об ошибке Причины и рекомендуемые действия Database format n is too old to restore to (Формат базы данных слишком старый для восстановления) Используемая версия gbak несовместима с версией Firebird, которым создавалась база данных, указанная для копирования. Попы- тайтесь скопировать эту базу данных с ис- пользованием режима -expand или -old, а затем восстановите ее Database <string> already exists (База данных <сгрока> уже существует) Вы использовали -create при восстанов- лении файла копии, но указанная база дан- ных уже существует. Если вы действитель- но хотите заменить существующую базу данных, используйте переключатель -R, в противном случае введите другое имя файла базы данных Could not drop database <string> (database might be in use) (Невозможно удалить базу данных <строка> (возможно, база данных используется)) Вы использовали -replace при восстановлении файла в существующую базу данных, но эта база данных находится в использовании. Или измените имя у восстанавливаемой базы данных, или подождите завершения использования существующей базы данных Do not recognize record type n . . . (He распознан тип записи n) Проверьте метаданные и при необходимо- сти выполните восстановление с использо- ванием -one_at_a_time Do not recognize <string> attribute n — continuing ... (He распознан атрибут n <строка> - выполнение продолжается) Не фатальная ошибка в данных Do not understand BLOB INFO item n ... (Непонятный элемент BLOB INFO) Error accessing BLOB column <string> — continuing ... (Ошибка при обращении к столбцу BLOB <строка> - выполнение продолжается) Не фатальная ошибка в данных ERROR: Backup incomplete The backup cannot be written to the target device or file system (ОШИБКА: копирование не завершено. Копия не может быть записана на устройство или в файл системы) Причинами могут быть недостаточное про- странство, проблемы с выводом на устрой- ство или разрушение данных Error committing metadata for table <string> (Ошибка при подтверждении метаданных для таблицы <строка>) Возможно, таблица разрушена. При восста- новлении базы данных используйте -one_at_a_time для изоляции таблицы
Гпава 38. Резервное копирование и восстановление баз данных (gbak) 851 Таблица 38.3 (продолжение) Сообщение об ошибке Причины и рекомендуемые действия Exiting before completion due to errors (Выход до завершения по причине ошибок) Это сообщение сопровождает другие сооб- щения об ошибках и указывает на то, что копирование или восстановление не может быть выполнено. Причину найдите в других сообщениях об ошибках Expected array dimension n but instead found m (Ожидается размерность массива n, но найдена m) Проблемы с массивом Expected array version number n but instead found m (Ожидается номер версии массива n, но найден m) Проблемы с массивом Expected backup database <string>, found <string> (Ожидается копия базы данных <строка>, найдена <строка>} Проверьте имена восстанавливаемых фай- лов копии Expected backup description record . . . (Ожидается дескриптор записи копии ...) Expected backup start time <string>, found <string> . . . (Ожидается время начала копирования <строка>, найдено <строка>} Expected backup version 1, 2, or 3. Found n ... (Ожидается версия копии 1, 2 или 3. Найдена п) Expected blocking factor, encountered <string> (Ожидается коэффициент блокирования, встречено <строка>) Режим -factor требует числового аргу- мента Expected data attribute . . . (Ожидается атрибут данных) Expected database description record . . . (Ожидается дескриптор записи базы данных) Expected number of bytes to be skipped, encountered <string> ... (Ожидается количество пропускаемых байтов, встречено <строка>)
852 Часть IX. Инструменты Таблица 38.3 (продолжение) Сообщение об ошибке Причины и рекомендуемые действия Expected page size, encountered <string> (Ожидается размер страницы, встречено <строка>) Режим -page_size требует числового ар- гумента Expected record length . . . (Ожидается длина записи) Expected volume number n, found volume m (Ожидается номер тома n, найден том m) При копировании на множество лент или восстановлении с множества лент убеди- тесь, что задали правильный номер тома Expected XDR record length ... (Ожидается длина записи XDR) Failed in put_blr_gen_id ... (Ошибка в put_blr_gen_id ...) Failed in store_blr_gen_id ... (Ошибка в store_blr_gen_id ...) Failed to create database <string> (Ошибка создания базы данных <строка>} Указана неверная база данных, возможно, она уже существует Column <string> used in index <string> seems to have vanished (Столбец <строка>, используемый в индексе <строка>, видимо пропал) Индекс ссылается на не существующий столбец. Проверьте определение индекса или столбца Found unknown switch (Найден неизвестный переключатель) Задан режим, не распознанный утилитой gbak Index <string> omitted because n of the expected m keys were found ... (Индекс <строка> пропускается потому, что было найдено л из ожидаемых m ключей) Input and output have the same name Disallowed. (Вход и выход имеют одни и те же имена. Отменяется.) Файлы копии и базы данных должны иметь различные имена. Исправьте имена и вы- полните заново Length given for initial file (n) is less than minimum (m) (Длина, указанная для начального файла (л) , меньше минимума (т) ) Недостаточное пространство было выделе- но для восстановления базы данных во множество файлов. Firebird автоматически увеличивает размер страницы до мини- мального значения. Не требуется никаких действий Missing parameter for the number of bytes to be skipped ... (Отсутствует параметр для количества пропускаемых байтов)
Глава 38. Резервное копирование и восстановление баз данных (gbak) 853 Таблица 38.3 (продолжение) Сообщение об ошибке Причины и рекомендуемые действия Multiple sources or destinations specified (Задано множество входных или выходных имен) Только имя одного устройства может быть указано для входа или выхода No table name for data (He задана таблица для данных) База данных содержит данные, которые не относятся ни к какой таблице. Используйте gfix для проверки или починки базы данных Page size is allowed only on restore or create (Размер страницы допустим только для восстановления или создания) Режим -page_size был использован при копировании вместо восстановления Page size parameter missing (Отсутствует параметр размера страницы) Режим -page_size требует числового аргу- мента Page size specified (п bytes) rounded up to m bytes (Заданный размер страницы (n байтов) округляется до m байтов) Не фатальная ошибка. Неверный размер страницы округляется до величины 1024, 2048, 4096, 8192 или 16 384 — которая ближе к указанной Page size specified (n) greater than limit (16 384 bytes) (Указанный размер страницы (n) больше максимального значения (16 384 байт)) Задайте размер страницы 1024, 2048, 4096, 8192 или 16 384 Password parameter missing (Отсутствует параметр пароль) Копирование или восстановление обраща- ется к удаленному серверу. Используйте переключатель -password и задайте пароль Protection is not there yet (Защита пока не существует) Используется нереализованный режим -unprotected Redirect location for output is not specified (Перенаправление размещения для вывода не задано) Вы задали режим, зарезервированный для будущего использования в Firebird REPLACE specified, but the first file <string> is a database (Задано REPLACE, но первым файлом <строка> является база данных) Проверьте, чтобы именем файла, следую- щим за режимом -replace, был файл ко- пии, а не базы данных Requires both input and output file names (Требуются имена как входного, так и выходного файла) Задайте входной и выходной файл при копировании и восстановлении RESTORE: decompression length error (RESTORE: ошибка длины декомпрессии) Возможна несовместимость версии gbak, используемой для копирования и gbak, ис- пользуемой для восстановления. Проверь- те, был ли режим -expand задан при копи- ровании
854 Часть IX. Инструменты Таблица 38.3 (окончание) Сообщение об ошибке Причины и рекомендуемые действия Restore failed for record in table <string> (Ошибка при восстановлении для записи из таблицы <строка>) Возможно разрушение данных в указанной таблице Skipped n bytes after reading a bad attribute n . . . (Пропущено n байт после чтения неверного атрибута т) Не фатально Skipped n bytes looking for next valid attribute, encountered attribute m ... (Пропущено л байт при поиске следующего верного атрибута, встречен атрибут ш) Не фатально Trigger <string> is invalid . . . (Триггер <строка> неверен) Unexpected end of file on backup file (Непредвиденное завершение копируемого файла) Восстановление файла копии завершилось с ошибкой; видимо процедура копирования, которая создала этот файл копии, заверши- лась ненормально. Если возможно, создай- те новый файл копии и используйте его для восстановления базы данных Unexpected I/O error while accessing <string> backup file (Непредвиденная ошибка ввода/вывода при обращении к файлу копии <строка>') Вероятно, возникла ошибка диска или дру- гого оборудования при выполнении копиро- вания или восстановления Unknown switch <string> (Неизвестный переключатель <строка>) Был задан нераспознанный утилитой gbak режим User name parameter missing (Отсутствует параметр имя пользователя) Копирование или восстановление обраща- ется к удаленной машине. Задайте имя пользователя переключателем -user Validation error on column in table <string> (Ошибка проверки столбца в таблице <строка>) База данных не может быть восстановлена, потому что содержит данные, которые на- рушают ограничение целостности. Попы- тайтесь удалить ограничения из метадан- ных, указав при восстановлении -no_validity Warning — record could not be restored (Предупреждение - запись не может быть восстановлена) Возможно разрушение указанных данных Wrong length record, expected n encountered m . . . (Неверная длина записи, ожидается л, встретилась ш)
Гпава 38. Резервное копирование и восстановление баз данных (gbak) 855 Пора дальше Утилита gbak имеет большое значение для поддержания базы данных в работоспо- собном состоянии и для устранения некоторых типов разрушения данных. Другим инструментом, который вы можете использовать для выполнения задач администри- рования, ведения и восстановления базы данных, является программа gfix, обсуж- даемая дальше. Комбинированное использование gfix и gbak для анализа и иногда починки разрушенной базы данных описывается в приложении 4.
ГЛАВА 39 Утилита gfix Как пользователь SYSDBA или владелец базы данных, вы можете использовать ад- министративный инструмент командной строки gfix для подключения к базе данных и выполнения разнообразных задач по поддержанию работоспособности и восста- новлению базы данных. Используя gfix, вы можете: ♦ выполнять чистку (sweep); ♦ изменять интервал автоматического sweep; ♦ инициировать закрытие базы данных для получения исключительного доступа и опять переводить базу данных в активное состояние; ♦ переключаться между синхронным и асинхронным выводом; ♦ переводить базу данных для чтения/записи в базу данных только для чтения и наоборот; ♦ изменять диалект; ♦ устанавливать размер кэша базы данных; ♦ отыскивать и подтверждать или отменять зависшие транзакции; ♦ чинить разрушенные базы данных и данные при некоторых условиях; ♦ активировать и удалять теневые копии базы данных. Использование gfix Утилита gfix может быть запущена только из командной строки. Для использования этой утилиты вы должны быть владельцем базы данных или пользователем SYSDBA. Для запуска gfix откройте окно командной строки и перейдите в каталог /bin в корне- вом каталоге Firebird. Синтаксис команды gfix: gfix [ре>кимы] база-данных База-данных должна быть полным именем первичного файла базы данных, с которой вы собираетесь работать. Первичный файл однофайловой базы данных является собственно файлом базы дан- ных. Для многофайловых баз данных первичный файл является первым файлом в наборе.
Гпава 39. Утилита gfix 857 режимы являются допустимой комбинацией переключателей и, в некоторых случаях, аргументов. Они представлены позже в табл. 39.1. Для большинства переключателей могут быть использованы сокращения. Необязательные символы показаны в квад- ратных скобках ([ ]). СОВЕТ. Как и в других инструментах командной строки Firebird, вы можете вклю- чить любое количество необязательных символов в имя переключателя вплоть до полного имени переключателя, если ни один символ не будет пропущен в по- следовательности слева направо. Получение доступа к базе данных из gfix Если вы соединяетесь с сервером удаленно, то в список переключателей вы должны включить имя пользователя и пароль для пользователя SYSDBA или для владельца базы данных. Такими переключателями являются: -pas[sword] <парохь> -u[ser] <имя> Следующий пример команды gfix устанавливает синхронный режим (Forced Writes). POSIX: bin]$ ./gfix —w sync customer.fdb -pas heureuse -user SYSDBA Windows: bin> gfix -w sync customer.fdb -pas heureuse -user SYSDBA Пользователь и пароль при локальном соединении Перед стартом сервера для расширенной локальной работы с копией проблемной базы данных или для восстановления транзакции вы можете добавить две системные переменные isc_user и isc password для исключения необходимости набора имени и пароля пользователя SYSDBA или владельца базы данных в каждой команде: shell prompts SET ISC_USER=SYSDBA shell prompt> SET ISC_PASSWORD=heureuse Из соображений безопасности вы должны удалить эти переменные окружения, как только завершите вашу работу. Не рекомендуется конфигурировать эти переменные за пределами видимости вашего текущего окна командной строки или делать их по- стоянными в системе. Чистка базы данных Многоверсионная архитектура Firebird порождает ситуацию, когда множество версий строк данных хранится непосредственно на страницах базы данных. Firebird сохраня- ет старые версии при изменении или удалении строки. При нормальном ходе собы- тий устаревшие версии записей, созданные изменениями данных, вычищаются фоно-
858 Часть IX. Инструменты вой сборкой мусора. Однако при некоторых условиях эти старые версии могут оста- ваться "застрявшими" и накапливаться, приводя к росту файла (файлов) базы данных пропорционально размеру данных, к которым происходит обращение. Иногда за- стрявшие транзакции могут сильно влиять на производительность. Чистка базы данных является способом систематического удаления устаревших строк из базы данных и предотвращает ее слишком интенсивный рост. По умолча- нию базы данных Firebird всегда установлены в режим автоматически выполняемой чистки при возникновении некоторых условий. Однако, поскольку процесс чистки может влиять на производительность, чистка может быть настроена для оптимизации ее преимуществ при минимизации воздействия на пользователей. Неплохая стратегия — отключение автоматической чистки и управление чисткой вручную. Вы можете отслеживать статистику базы данных и выполнять ручную чи- стку или "по потребности", или по составленному графику. Например, можно вклю- чить команду очистки в скрипт cron или в выполняемый по расписанию пакетный файл. Информацию о том, как статистический отчет по базе данных может помочь в анали- зе требований к чистке вашей базы данных, см. в разд. "Получение статистики по индексам" главы 18. Сборка мусора Firebird выполняет сборку мусора в фоновом режиме для ограничения роста базы данных из-за устаревших версий записей. Сборка мусора освобождает память, отве- денную устаревшим версиям строк, насколько это возможно, после освобождения строк транзакциями, их использующими. Транзакции начинают сборку мусора, когда они встречают старые версии строк, отмененные другими транзакциями. Удаленные строки и версии, оставленные после отката транзакции, избегают такой сборки мусо- ра. Строки, к которым редко происходит обращение, также накапливают старые вер- сии. Еще сборка мусора происходит при резервном копировании базы данных с использо- ванием утилиты gbak, поскольку задача gbak имеет дело с каждой строкой в каждой таблице. Однако gbak не выполняет полной чистки. Как и обычная сборка мусора, она оставляет версии удаленных и отмененных по rollback записей. Чистка является единственным способом избавиться от этого, она выполняется быстрее, чем восста- новление базы данных из резервной копии. Интервал чистки Интервалом чистки (sweep interval) является установленное для базы данных целое число, которое определяет предел для некоторого набора условий, что приведет к выполнению автоматической чистки. Сервер Firebird ведет список транзакций. Любая транзакция, находящаяся в любом состоянии, кроме подтвержденного, называется заинтересованной транзакцией. Самая старая из этих "заинтересованных" транзакций (старейшая заинтересованная
Гпава 39. Утилита gfix 859 транзакция, Oldest Interesting Transaction, OIT) отмечает начальную точку для усло- вия, называемого "зазором". Противоположным концом этого зазора является старейшая транзакция, которая все еще активна: самая старая активная транзакция (Oldest Active Transaction, ОАТ). За- зор, следовательно, является разностью между OIT и ОАТ1. Когда величина зазора достигает числа, указанного как интервал чистки, то автоматически запускается чи- стка во время запуска новой транзакции. Базы данных создаются с интервалом чистки 20 000. Существует маленькое, но важное различие: автоматическая чистка не появляется каждые 20 000 транзакций. Она появляется, когда разница между OIT и ОАТ — за- зор — достигает заданного предела. Если приложения базы данных аккуратно под- тверждают каждую транзакцию, ОАТ будет увеличиваться, пока не достигнет преде- ла интервала чистки, и тогда будет запущена чистка. Изменение интервала чистки Изменение интервала чистки мало влияет на размер базы данных, если только база данных не накопила очень большого количества отмененных транзакций. Однако если вы замечаете увеличение времени запуска транзакции с момента последней чи- стки, то уменьшение интервала чистки может помочь сократить накопление объек- тов, вызванных откатом транзакций. Если интервал чистки слишком мал, производительность приложения может иметь тенденцию к ухудшению, по причине слишком частого выполнения чистки. В этом случае увеличение интервала чистки может помочь улучшить общую производи- тельность. Переключателем режима для установки интервала чистки является -housekeeping} п, где п представляет новое значение счетчика (интервала). gfix -h 10000 /data/accounts.fdb -user SYSDBA-pas masterkey устанавливает новое значение интервала чистки для accounts.fdb в 10 000. Отмена автоматической чистки Вы можете решить отменить автоматическую чистку, если вам нужно исключить случайные, непредсказуемые задержки, возникающие из-за автоматической чистки. Такая отмена не рекомендуется, если только управление старыми версиями записей эффективно не осуществляется альтернативными средствами, такими как отслежива- ние статистики и регулярное выполнение ручной чистки. Автоматическая чистка может быть отменена установкой интервала чистки в 0: ./gfix -h 0 /data/accounts.fdb -user SYSDBA-pas masterkey Это справедливо для InterBase 7.1/7.5 и Firebird 2.0. Для всех предыдущих версий Inter- Base и Firebird "зазор", выше которого стартует автоматический sweep, определяется как раз- ница между OIT и OST (Oldest snapshot). — Прим. науч. ред.
860 Часть IX. Инструменты или (Windows): gfix -h 0 d:\data\accounts.fdb -user SYSDBA-pas masterkey Выполнение ручной чистки Ручная чистка может быть выполнена в любое время для освобождения пространст- ва, занимаемого старыми версиями, особенно версиями записей, оставшихся от от- мены транзакций и после удалений. Общим правилом является выполнение чистки во время низкой активности сервера базы данных, что исключает конкуренцию с клиентами за ресурсы. Вы можете выполнять вашу собственную чистку, если: ♦ вы отслеживаете зазор и собираетесь выбрать подходящее время для устранения "застрявших" старых версий; ♦ вы считаете, что редкие изменения нечасто посещаемых записей создали запасы несобранного мусора; ♦ выполнялось большое количество удалений, и вы хотите быстро убрать мусор. Для немедленного запуска чистки: gfix -sweep С:\data\accounts.fdb -user SYSDBA-pas masterkey или (POSIX): ./gfix -sweep /data/accounts.fdb -user SYSDBA-pas masterkey Исключительный доступ для ручной чистки Чистка базы данных не требует, чтобы база данных была обязательно закрыта— она может выполняться в любое время — однако она может ухудшить производитель- ность системы и не должна проводиться во время большой загрузки сервера. Существуют преимущества выполнения чистки с исключительным доступом и когда вся работа клиентов подтверждена. При этих условиях не только больше доступной памяти для операции чистки, но чистка также способна выполнить полное очищение записей данных и состояния транзакций. Зависшие при явных или неявных обрывах соединений транзакции под конец будут переведены в устаревшие, а используемые ими ресурсы будут освобождены. Далее мы рассмотрим использование gfix для закрытия базы данных и получения к ней исключительного доступа. Закрытие базы данных Закрытие базы данных (shutdown) не является тем же самым, что и завершение рабо- ты сервера. Сервер продолжает работать и когда база данных закрыта. База данных неявно находится "в закрытом состоянии", когда не существует актив- ных соединений. Явное закрытие может быть выполнено при использовании gfix с переключателем -sh[ut], чтобы пользователь SYSDBA или владелец базы данных
Гпава 39. Утилита gfix 86 У получили к ней исключительный доступ. Когда такое явное закрытие базы данных было выполнено, база данных остается закрытой, пока явно не будет выполнено gfix -ofniine]. Эти две операции называются "закрытие базы данных" и "перевод базы данных в оперативное состояние". Закрытие базы данных перед остановом сервера Когда вам понадобится остановить сервер, находящийся в промышленной эксплуа- тации, возможно вам потребуется применить gfix -shut для закрытия используемых баз данных на этом сервере. Команда gfix -shut Синтаксис команды gfix -shut следующий: POSIX: ./gfix -sh[ut] (-at n I-t n |-f n } база-данных Windows: gfix -shfut] {-at n I-t n |-f n } база-данных Определение аргументов Переключатель gfix -shut поступает с тремя квалификаторами, которые уточняют стратегию закрытия: -atftach] n, -tr[an] п и -fforce] л. В любом случае л устанав- ливает период ожидания в секундах. Вы должны использовать один аргумент. -at[tach] п употребляется для предотвращения новых соединений с базой данных. Он не отключает существующие соединения, но блокирует любые новые соединения. Если не будет никаких соединений по прошествии времени ожидания л секунд, то база данных будет переведена в закрытое состояние. Если же еще остаются соедине- ния, то закрытие будет отменено. -trfan] п используется для предотвращения запуска новых транзакций. Это не за- крывает существующих транзакций, но не позволяет запускать новые. Если не будет никаких соединений по прошествии времени ожидания л секунд, то база данных бу- дет переведена в закрытое состояние. Если еще остаются соединения, то закрытие будет отменено. -f force] л форсированно переведет базу данных в закрытое состояние по прошест- вии л секунд, независимо от существования соединений или активных транзакций. Это радикальная операция, которая может привести к тому, что пользователи поте- ряют свою проделанную работу. Она должна быть использована с осторожностью. СОВЕТ. Если вам нужно прибегнуть к переключателю -fforce], чтобы убить дефектный запрос, то, по крайней мере, будьте вежливы с вашими "хорошо себя ведущими" пользователями и сначала используйте -at [tach] или -trfan], чтобы дать им возможность сохранить работу и элегантно выйти из своих приложений.
862 Часть IX. Инструменты Примеры: gfix -sh -at 300 инициирует закрытие базы данных, что произойдет через 5 минут, если все пользова- тели отключатся от базы данных. gfix -sh -f 600 отключит всех пользователей от системы через 10 минут. Любые еще выполняющие- ся транзакции будут отменены, а пользователи потеряют проделанную и неподтвер- жденную работу. Исключительный доступ Когда база данных находится в закрытом состоянии, пользователь SYSDBA или ее владелец могут соединиться с ней и получить исключительный доступ. Однако осте- регайтесь следующих моментов: ♦ если владелец или SYSDBA были соединены с базой данных в момент ее закры- тия, то сервер не будет блокировать другие подключения к базе данных, даже ко- гда выполнено закрытие; ♦ когда владелец или SYSDBA соединяется с базой данных после ее закрытия, то другому будет заблокирован к ней доступ — это хорошо. Если тот же пользова- тель захочет снова к ней подключиться, ему это будет разрешено, что уже не 7 столь хорошо . Это накладывает ответственность на SYSDBA или на владельца, кому нужен исклю- чительный доступ гарантировать, что ни они, ни кто-либо другой не будет соединен с базой данных с использованием визуального административного инструмента, мони- тора SQL, другого инструмента командной строки или даже другого режима gfix, например. Когда вы получили исключительный доступ, сохраните его исключитель- ным — не запускайте более одного приложения2 3. Завершение закрытия Используйте gfix -ofniine] для отмены закрытия и перевода базы данных в опера- тивное состояние для доступа множества пользователей. Вы также можете применять это для отмены закрытия по расписанию. Останов и запуск сервера Имейте в виду, что останов или запуск сервера не оказывает никакого воздействия на состояние закрытия любой базы данных. Если база данных закрыта, когда сервер 2 После того как база данных переведена в состояние shutdown, к ней могут подключиться только пользователи SYSDBA и владелец базы данных (owner). Всем другим пользователям при попытке соединения с такой базой данных будет выдано соответствующее сообщение об ошибке. — Прим. науч. ред. 3 Количество соединений SYSDBA при состоянии базы данных shutdown ничем не ограни- чено. Только в Firebird 2.0 есть режим монопольного (только одного) подсоединения SYSDBA. — Прим. науч. ред.
Гпава 39. Утилита gfix 863 остановлен, она будет оставаться в закрытом состоянии и при последующем запуске сервера. Останов сервера не переведет никакую базу данных в закрытое состояние. ПРИМЕЧАНИЕ. Если вы скопировали файл базы данных, которая находится в закрытом состоянии, а затем пытаетесь соединиться с копией, то копия будет в закрытом состоянии. Изменение установок базы данных Множество режимов команды gfix позволяет установить или изменить различные установки базы данных. Для выполнения таких команд требуются полномочия SYSDBA или владельца базы данных, а также исключительный доступ. Установка размера кэша по умолчанию Использование gfix является предпочтительным способом установки размера кэша по умолчанию для базы данных. Важно помнить некоторые моменты. ♦ Если вы увеличиваете размер страницы, размер кэша соответственно увеличится. Вы должны гарантировать, что вы получите количество физической памяти ма- шины в соответствии с измененным размером кэша. Когда кэш достигнет разме- ра, который слишком велик для хранения в RAM, он начнет записываться на диск, что полностью уберет все преимущества использования кэша. ♦ В Классическом сервере каждый клиент получает свой собственный кэш. Даже размер кэша по умолчанию в 75 страниц будет слишком большим, если база дан- ных использует большой размер страницы. Синтаксис: gfix -b[uffers] л база-данных Здесь п— размер резервируемого кэша (количество буферов с размером, равным размеру страницы). Пример: gfix -b 5000 d:\data\accounts.fdb Если размер страницы базы данных 8192, то кэш в 5000 страниц займет примерно 40 Мбайт. Другие режимы кэша В настоящий момент переключатель -с[ache] л не используется; он зарезервирован для дальнейших реализаций.
864 Часть IX. Инструменты Изменение режима доступа Используйте вариант gfix -mo[de] для переключения режима доступа любого соеди- нения с базой данных между "только для чтения" и "для чтения/записи". В базу толь- ко для чтения нельзя совсем выполнить запись— даже пользователю SYSDBA, ее владельцу или любому серверному процессу. Синтаксис: gfix -mo[de] {read_write | read_only} база-данных Для переключения базы данных из режима чтения/записи в режим только для чтения: ./gfix -mo read_only /data/accounts.fdb Для переключения базы данных из режима только для чтения в режим чтения/записи: ./gfix -mo read__write /data/accounts . fdb Изменение диалекта базы данных С переключателем -s[qi_diaiect] вы можете изменить диалект! базы данных на родной для Firebird диалект 3. После этого база данных перестает использовать диа- лект 1 и начинает подчиняться полным синтаксическим правилам SQL Firebird и мо- жет использовать все типы данных Firebird. Но база данных продолжает хранить все существующие данные и определения в со- ответствии с диалектом 1. Здесь существует несколько ловушек, в особенности в от- ношении числовых типов с фиксированной точкой. Короче говоря, изменение диалекта с помощью gfix не будет ни тактикой быстрой миграции, ни надежным способом миграции из диалекта 1 в диалект 3. Быстрое из- менение средствами gfix означает медленный способ полной миграции. Опытные пользователи предлагают вместо этого извлечь скрипт схемы вашей базы данных диалекта 1, изменить подходящим образом определения, а затем использовать инст- румент перекачки данных для перемещения старых данных в новую базу. Если же вы все-таки хотите использовать этот способ, вот синтаксис: gfix -s[ql_dialect] п база-данных Здесь п равно 1 или 3. Например, для изменения диалекта базы данных на 3 выполните: ./gfix -s 3 /data/accounts.fdb Под Windows вы можете выполнить следующее: gfix -sql_dialect 3 d:\data\accounts.fdb Включение и отключение "использовать все пространство" Firebird заполняет страницы базы данных таким образом, что объем хранимых на странице данных не превышает 80 процентов. Некоторое уплотнение можно полу-
Гпава 39. Утилита gfix 865 чить, изменив коэффициент заполнения на 100 процентов. Это может привести к улучшению производительности при очень большом объеме добавления данных, в особенности если размер строки много меньше размера страницы и множество строк может экономно храниться на одной странице. Также имеет смысл полностью заполнять страницы в базе данных, если вы планируе- те распространять ее как базу данных только для чтения, например, в качестве ката- лога или демонстрационного примера. Командным переключателем является -u[se], который имеет два аргумента: gfix -u[se} (reserve | full} reserve устанавливает использование страницы на 80 процентов, a full устанавливает использование страницы на 100 процентов. Для включения "использовать все пространство" применяйте команду: ./gfix -use full /demos/catalog.fdb Для отключения "использовать все пространство" и возврата к 80-процентному за- полнению применяйте команду: ./gfix -use reserve /demos/catalog.fdb Jtj ПРИМЕЧАНИЕ. Чтобы эта команда работала, база данных должна находиться в режиме чтения/записи. Включение и отключение синхронного вывода Синонимом синхронного вывода является Forced Writes. Когда поведение является синхронным ("Forced Writes включено"), новые записи, новые версии записей и уда- ления немедленно записываются на диск после пересылки. При асинхронном выводе ("Forced Writes отключено") новые и измененные данные сохраняются в кэше файло- вой системы, полагаясь на поведение операционной системы, когда она выведет их на диск. Термин "отключение Forced Writes" означает переключение поведения по выводу данных с синхронного на асинхронное. Сйнтаксис этой команды: gfix -wfrite] {sync I async} Для включения Forced Writes введите: gfix -w sync d:\data\accounts.fdb Для отключения Forced Writes введите: gfix -w async d:\data\accounts.fdb Firebird инсталлируется в Windows NT/2000/XP и Linux с включенным режимом Forced Writes. В очень надежной среде с надежной поддержкой бесперебойного пи- 28 Зак. 420
866 Часть IX. Инструменты тания (UPS) администратор базы данных может отключить Forced Writes для сокра- щения операций ввода/вывода и улучшения производительности. Когда отключается Forced Writes в менее надежных средах, база данных становится чувствительной к потере данных и даже к их разрушению в случае неуправляемого отключения. Forced Writes неприменимо в Windows 95. В серверах Windows 98 и ME вы никогда не должны отключать Forced Writes. Отключение Forced Writes в серверах Windows Операционные системы Windows менее надежны, чем другие системы в отношении записи на диск кэша. Это проявляется в том, что приложения не могут явно запро- сить систему Windows записать кэш на диск, система может отложить все выводы на диск до того момента, как файл базы данных будет закрыт. ♦ Firebird 1.0.x: если Forced Writes не задается для сервера Windows, работающего 24/7 (24 часа, 7 дней в неделю), то запись на диск может никогда не произойти. ♦ Firebird 1.5: были добавлены новые установки конфигурации для записи бу- феров кэша на диск в Windows. См. параметры MaxUnfiushedwrites и MaxUnflushedWriteTime (firebird.conf) В главе 36. Системное восстановление в Windows ME и ХР Windows ME вместе с ХР Ноте и Professional Edition имеет возможность, называе- мую системным восстановлением, которая вызывает копирование самой файловой системы в файлы с некоторыми суффиксами каждый раз, как происходят операции ввода/вывода. Системное восстановление не является заменой Forced Writes4. Конвертированные базы данных InterBase Вам следует знать, что база данных Firebird, которая начала свою жизнь в InterBase 6.x (коммерческом или с открытыми текстами), создана с отключенным режимом Forced Writes по умолчанию. Запрос версии сервера Firebird Переключатель -z (без параметров) показывает версию gfix и сервера Firebird, ин- сталлированного на сервере. Вот синтаксис: gfix -z 4 Суффикс .gdb включен в filelist.xml, в список файлов, к которым применяется системное восстановление, располагающихся в каталоге Windows/System. К сожалению, из списка типы файлов не могут быть удалены. Это действует на базы данных Firebird, имеющие расширение gdb — системное восстановление приводит к замедлению начального соединения с подоб- ными базами данных, и бывает, что это приводит к разрушению данных (подтвержденные
Гпава 39. Утилита gfix 867 Проверка и починка данных В повседневных операциях в базе данных иногда происходят события, создающие некоторые проблемы для структуры базы данных. ♦ Ненормальное завершение сервера. Ненормальное завершение не влияет на цело- стность базы данных. Однако если Firebird уже назначил страницу данных для неподтвержденных изменений, запрошенных клиентом, эта страница становится бесхозной, "осиротевшей". Поскольку такие страницы являются вполне безобид- ными, они лишь занимают дисковое пространство, которое должно быть возвра- щено в свободное пространство. Проверка может отыскать и освободить это про- странство. ♦ Ошибки записи операционной системы или оборудования. Ошибки записи обычно создают проблемы для целостности базы данных. Они могут привести к разруше- нию или потере структур данных, таких как страницы базы данных и индексы. В худшем случае такие разрушения структур данных могут сделать подтвержден- ные данные нечитаемыми. Иногда проверка может помочь найти такие разрушен- ные фрагменты и удалить их. Когда проверять базу данных Вы должны проверять базу данных: ♦ всякий раз при неуспешном копировании базы данных; ♦ всякий раз, когда приложение получает сообщение об ошибке "разрушена база данных"; ♦ периодически в качестве регулярной процедуры для отслеживания разрушения структур данных или нераспределенного пространства; ♦ каждый раз, когда вы подозреваете, что данные разрушены. Утилита командной строки gbak может быть использована вместе с gfix для выпол- нения последовательной проверки и починки данных. Выполнение проверки базы данных Проверка базы данных требует исключительного доступа к базе данных. Без исклю- чительного доступа вы получите сообщение об ошибке: OBJECT имя-базы-данных IS IN USE Для проверки базы данных просто введите команду: gfix -v Проверка будет молча отыскивать и освобождать неназначенные страницы или не- распределенные структуры, которые она найдет. Она будет сообщать о любых раз- рушенных структурах, но не будет пытаться их исправлять. Для получения протоко-
868 Часть IX. Инструменты ла об ошибках gfix, но без попыток освобождения пространства введите переключа- те ЛЬ ~п [o_update]' gfix -v -n Вы можете заставить проверку игнорировать ошибки контрольных сумм, добавив переключатель -i ignore]: gfix -v -i < ПРИМЕЧАНИЕ. Даже если вы сможете восстановить исправленную базу данных, которая сообщала об ошибках контрольных сумм, объем потерянных данных .__\ трудно будет определить. Если это вас беспокоит, вы можете восстановить вашу базу данных с более ранней копии. Восстановление разрушенной базы данных Если вы подозреваете, что ваша база данных разрушена, важно точно придерживать- ся последовательности шагов восстановления, чтобы избежать дальнейших разруше- ний. Подробное описание рекомендуемой процедуры восстановления см. в прило- жении 4s. Восстановление транзакций Ут^шта gfix предоставляет инструменты для восстановления зависших транзак- ций2РС— транзакций с несколькими базами данных после потери соединения с одной из них. Двухфазное подтверждение Транзакция, которая используется в нескольких базах данных Firebird, подтверждает- ся в два этапа, или в две фазы. Такое двухфазное подтверждение гарантирует, что если транзакция не может завершить изменения во всех используемых базах данных, она не будет изменять ни одну из них. На первой фазе двухфазного подтверждения Firebird подготавливает субтранзакцию для каждой базы данных, вовлеченной в операции транзакции, и записывает соответ- ствующие изменения в каждую базу' данных. На второй фазе, следуя точно в том же порядке, что и при их подготовке и записи, Firebird отмечает каждую подтранзакцию как подтвержденную. 5 Информацию по ремонту баз данных можно найти здесь: http://www.ibase.ru/dbrepair.htm и http://www.ibase.ru/devinfo/db_repair.htm. — Прим. науч. ред.
Гпава 39. Утилита gfix 869 Зависшие транзакции Зависшие (limbo) транзакции— это подтранзакции, которые остались неразрешен- ными, если возникли какие-то проблемы в соединениях с одной или более базами данных в процессе выполнения второй фазы двухфазного подтверждения, например, ошибки сети или отключение питания. Сервер не может сказать, должны ли завис- шие транзакции подтверждаться или следует выполнить их откат. Следовательно, некоторые записи в базе данных могут оказаться недоступными, пока явно не будут выполнены те действия по разрешению зависших транзакций, с кото- рыми они связаны. Восстановление транзакции При использовании gfix, вы имеете множество режимов для получения информации и для разрешения зависших транзакций после ошибок двухфазного подтверждения. Процесс идентификации зависших транзакций и их подтверждение либо откат назы- вается восстановлением транзакций. Вы можете попытаться восстановить сразу все зависшие транзакции или вы можете выполнять восстановление транзакции за транзакцией, используя идентификатор каждой индивидуальной транзакции. Поиск зависших транзакций Для получения списка всех зависших транзакций с индикацией, что произойдет с каждой, если было запрошено автоматическое двухфазное восстановление, исполь- зуйте переключатель -i[ist]: gfix -1 база-данных Подсказка для восстановления Используйте переключатель -p[romptj вместе с -i[ist] для получения от gfix списка зависших транзакций одну за другой и выдачи вам запроса на выполняемое действие: commit или rollback: gfix -1 -р база-данных Автоматическое двухфазное восстановление Поскольку зависшие транзакции являются результатом либо ошибок при подтвер- ждении, либо ошибок при откате, сервер знает, как должна каждая из них заканчи- ваться. Следовательно, автоматическое восстановление просто является способом подтверждения, что вы хотите, чтобы утилита gfix продолжала осуществлять перво- начальное намерение, каким оно было, когда двухфазное подтверждение было пре- рвано. Переключатель-t[wo_phase] {id i all} инициирует автоматическое двухфазное вос- становление.
870 Часть IX. Инструменты Используйте ан для выполнения двухфазного восстановления всех зависших тран- закций: gfix -t all база-данных Используйте id для ввода идентификатора одной транзакции, для которой вы хотите выполнить двухфазное восстановление: gfix -t nnnnnn база-данных Здесь пппппп — идентификатор транзакции. Задание подтверждения или отката Чтобы попытаться разрешить зависшие транзакции их подтверждением, используйте переключатель-с[ommit] (id i ail}. Для восстановления всех зависших транзакций таким способом введите: gfix -с all база-данных Для разрешения одной зависшей транзакции попыткой ее подтверждения введите: gfix -с пппппп база-данных Здесь пппппп — идентификатор транзакции. Для попытки разрешения зависших транзакций путем их отката используйте пере- ключатель -rfoilback] {id | ail}. Для восстановления всех зависших транзакций таким способом введите: gfix -г all база-данных Для разрешения одной зависшей транзакции попыткой ее отката введите: gfix -г пппппп база-данных Здесь пппппп — идентификатор транзакции. Теневые копии Концепция, создание и поддержка теневых копий базы данных подробно обсужда- лись в главе 15. Утилита gfix имеет средства для работы с теневыми копиями. Активизация теневой копии Ключ для активизации теневой копии, когда база данных "умерла", -acftivate]. Син- таксис: gfix -ас <путь-к-первому-то:ду-оперативной-копии> Предположим, что первым томом теневой копии является employee.shl. Находится в каталоге /opt/dbshadows. Вы можете активизировать ее следующей командой: ./gfix -ас /opt/dbshadows/employee.shl
Гпава 39, Утилита gfix 871 Удаление недоступных теневых копий Переключатель для удаления недоступных теневых копий -k[ill]. Синтаксис: gfix -k[ill] база-данных Для удаления недоступных теневых копий для базы данных employee.fdb введите: ./gfix -k /opt/firebird/examples/employee.fdb Список переключателей gfix Все доступные переключатели gfix описаны в табл. 39.1. Часто для одной задачи применимо несколько переключателей. Порядок переключателей не является важ- ным, однако просмотрите предыдущие замечания в этой главе для определения под- ходящих комбинаций. Комбинации, не имеющие логического смысла, приведут к исключениям. Таблица 39.1. Список переключателей gfix Переключатель Задача Назначение -ас[tivate] файл-оперативной-копии Оперативная копия Используется с путем к первичному файлу оперативной копии для активизации опера- тивного копирования -at[tach] Закрытие базы данных Используется с -shut для предотвращения новых подключений к базе данных за период времени ожидания в л секунд. Закрытие будет отменено, если все еще будут оста- ваться активные соединения по прошествии п секунд -bluffers] n Буферы кэша Устанавливает размер буферов кэша базы данных по умолчанию в п страниц. Это ре- комендованный способ установки размера кэша базы данных по умолчанию -ca[che] n Не используется -c[ommit] {ID | all] Восстановление транзакций Подтверждает зависшую транзакцию с иден- тификатором id или подтверждает все за- висшие транзакции -f[orce] n Закрытие базы данных Используется вместе с -shut для форсиро- ванного закрытия базы данных через п секунд — радикальное решение, которое должно быть использовано как последнее средство -full Починка данных Используется вместе с -v[alidate] для проверки структур записей и страниц; осво- бождает неназначенные фрагменты записей
872 Часть IX. Инструменты Таблица 39.1 (продолжение) Переключатель Задача Назначение -Housekeeping] Чистка Изменяет порог автоматической чистки в п транзакций. Значение по умолчанию 20 000. Устанавливайте п в 0 для отключе- ния автоматической чистки -i[gnore] Починка данных Игнорировать ошибки контрольных сумм при проверке или чистке —kfill] база-данных Теневая копия Используется вместе с путем к файлу базы данных для уничтожения всех неиспользуе- мых теневых копий -1[ist] Восстановление транзакций Отображает идентификаторы всех зависших транзакций и указывает, что должно про- изойти, если будет задано -t [wo_phase] при автоматическом двухфазном подтверждении -m[end] Починка данных Отмечает разрушенные записи как неис- пользуемые, следовательно, они будут про- пущены при последующей проверке или копировании -n[o_update] Починка данных Используется вместе с -validate] для проверки разрушенных или неразмещенных структур, сообщая о них, но не исправляя их -o Inline] Закрытие базы данных Отменяет операцию -shut, которая была в списке, или отменяет выполняющееся в настоящий момент закрытие базы данных -palssword] пароль Удаленный доступ Передает пароль для доступа к базе дан- ных. Для большинства операций gfix это должен быть пароль пользователя SYSDBA, владельца базы данных или (для POSIX) пользователя с привилегиями root -p[rompt] Восстановление транзакций Используется вместе с -l[ist] для перехо- да в режим подсказок во время восстанов- ления транзакций -rfollback] {ID | all] Восстановление транзакций Выполняет откат зависшей транзакции с идентификатором id или выполняет откат всех зависших транзакций -s[weep] Чистка Запускает немедленную чистку базы данных -sh[ut] Закрытие базы данных Закрывает базу данных. Требует указания -at[ach], -f[orce], или -tr[an] n -sql[_dialect] n Миграция n = 3. Изменяет SQL диалект базы данных с 1 на 3. Не изменяет данные и не преобразу- ет существующие типы данных -t[wo_phase] {ID I all] Восстановление транзакций Выполняет автоматическое двухфазное восстановление либо транзакции с указан- ным идентификатором id, либо всех завис- ших транзакций
Гпава 39. Утилита gfix 873 Таблица 39.1 (окончание) Переключатель Задача Назначение -tr[an] п Закрытие базы данных Используется вместе с -shut для запрета запуска новых транзакций в процессе ожи- дания л секунд. Закрытие будет отменено, если все еще будут активные транзакции по прошествии п секунд -use {reserve | full} Использование всего пространства Включает или отключает использование полного пространства, отведенного для страниц базы данных. Режим по умолчанию reserve использует 80-процентное заполне- ние. При переключении на full будет ис- пользоваться все выделенное пространство -user пользователь Удаленный доступ Передает имя пользователя для доступа к базе данных. Для большинства операций gfix это должен быть пользователь SYSDBA, владелец базы данных или (для POSIX) пользователь с привилегиями root -vfaLiclate] Починка данных Определяет и освобождает страницы, кото- рые были выделены, но не назначены ника- кой структуре данных. Также сообщает о разрушенных структурах -w[rite] (sync | async} Forced Writes Включает или отключает Forced Writes (син- хронную буферизованную запись), sync включает, async отключает -z Информация Выводит версию gfix и сервера Firebird Сообщения об ошибках gfix В табл. 39.2 представлены исключения, которые могут появиться в командах gfix вместе с советами, как скорректировать ваши команды. Таблица 39.2. Сообщения об ошибках gfix Сообщение об ошибке Причины и рекомендуемые действия Database file name <string> already given (Имя файла базы данных <строка> уже указано) Режим командной строки был интерпретиро- ван как файл базы данных, потому что режим не начинался со знака минус (-) или наклон- ной черты (/). Исправьте синтаксис Invalid switch (Неверный переключатель) Режим командной строки не был распознан Incompatible switch combinations (Несовместимая комбинация переключателей) Вы задали, по меньшей мере, два режима, которые не работают вместе, либо вы указа- ли режим, который не имеет смысла без другого режима (например, -full)
874 Часть IX. Инструменты Таблица 39.2 (окончание) Сообщение об ошибке Причины и рекомендуемые действия More limbo transactions than fit. Try again. (Зависших транзакций больше допустимого. Попытайтесь снова.) База данных содержит больше зависших транзакций, чем gfix может вывести в одной сессии. Подтвердите или отмените некото- рые из зависших транзакций, а затем снова выполните операцию Numeric value required (Требуется числовое значение) Режим -housekeeping требует одного неот- рицательного аргумента, задающего количе- ство транзакций для чистки Please retry, specifying <string> (Пожалуйста, повторите, задав <строка>) Должно быть задано имя файла и по мень- шей мере один режим Transaction number or "all" required (Требуется номер транзакции или "all") Вы указали -commit, -rollback или -two_phase без задания требуемого аргу- мента -mode read_only or read_write -mode read_only или read_write Режим -mode принимает в качестве переклю- чателя read_only или read_write "read_only" or "read_write" required (Требуется "read_only" или "read_write") Режим -mode должен сопровождаться одним из этих аргументов Пора дальше В конце этой книги глава 40 представляет техническое описание наименее дружест- венной для пользователя утилиты Lock Print, а также подсистему блокировки ресур- сов. Она включает объяснения связанных с блокировками установок в firebird.conf или isc config/ibconfig, которые иначе могли бы остаться для большинства из нас большой загадкой!
ГЛАВА 40 Менеджер блокировок Блокировки используются в многопользовательских средах для синхронизации рабо- ты и предотвращения разрушения целостности процессов работой других процессов. Firebird использует как средства блокировки операционной системы, так и собствен- ный менеджер блокировок для координации доступа к базе данных. Эта тема сложна для читателя, который является новичком в Firebird. Она погружает его в некоторые серьезные технические области, с которыми знакомят гуру1 с бле- стящими глазами на конференциях. Пока утилиты Lock Print — довольно сырые сей- час— являются серьезным инструментом для сложных задач поиска ошибок при подходах, которые некоторые разработчики применяют в их интерфейсах приложе- ний. В любом случае примите эту главу как возможный запасной вариант, который может спасти вас в один "прекрасный" день. Вы можете спросить, почему мы должны интересоваться системой блокировки, ко- торая ничего не блокирует? Параллельность и согласованность, видимые клиентам, основываются на транзакциях и управляются через версии записей. Однако ответ в том, что Firebird использует блокировки внутренне. Он поддерживает согласован- ность структуры на диске через блокировки и осторожную запись. В то же время он также использует сервисы блокировок операционной системы для управления досту- пом к файлам базы данных для предотвращения открытия Суперсервером дважды одного и того же файла под разными именами. Блокировка, основанная на транзакциях, позволяет осуществлять блокировки в лю- бой точке выполнения транзакции. Однако, однажды установленные, они могут быть сняты только в конце. Даже явная блокировка на уровне оператора, введенная в Firebird 1.5, не имеет "разблокирующего" оператора. Операторы выполняются в обычной транзакции; подтверждение или откат транзакции снимает блокировку как обычно. Firebird остерегается двухфазной блокировки для своего первичного управления па- раллельностью, поскольку она не может обеспечить адекватных уровней параллель- ности и соответствия, Firebird использует блокировки в процессе изменений для пре- Для понимания темы Lock Print с благодарностью и признательностью отправляемся к Ann Harrison. Она была первой, кто подробно документировал этот полезный, но слабо осве- щенный инструмент поиска неисправностей, в официальном документе, написанный Ann для IBPhoenix. "Reading a Lock Print" можно найти в разделе документации на http:// www.ibphoenix.com.
876 Часть IX. Инструменты дотвращения записи двумя транзакциями на одну и ту же страницу в одно и то же время. Внутренняя блокировка управляется самой системой. Объекты в системе — называемые "владельцами блокировки" или просто "владельцами" — состязаются в блокировке множества ресурсов. Не удивительно, что эти ресурсы включают в себя и страницы, содержащие записи, отмеченные для обновления. Когда транзакция получает блокировку страницы, она сохраняет эту блокировку, пока Менеджер блокировок не попросит ее освободить страницу; следовательно, транзакция может выполнить множество изменений на одной странице без ее осво- бождения и повторного чтения. Менеджер блокировок Firebird В Суперсервере о Менеджере блокировок можно думать как об отдельном "управ- ляющем центре", с которым договариваются транзакции о приобретении прав на вы- полнение запросов. Менеджер блокировок включает в себя фрагмент памяти и неко- торые подпрограммы для обработки запросов. Его память разделена на различные блоки: блоки блокировок, которые ссылаются на ресурсы; другие блоки, которые представляют транзакции и другие объекты, запрашивающие блокировки, и блоки истории. Его подпрограммы ответственны за получение и управление запросами вла- дельцев на блокировки ресурсов, выделение блоков и их освобождение. Суперсервер также управляет "защелками" для координации изменений в параллельных транзак- циях. Классический сервер проще: владельцы запрашивают получение контроля над таб- лицей блокировок, так что каждый код процесса управления блокировкой может за- прашивать, предоставлять и освобождать блокировки для их владельцев. Состояния блока Каждая операционная система предоставляет некий вид механизма использова- ния/освобождения для синхронизации событий ресурсов. Поскольку для Firebird ну- жен управляющий механизм с множеством состояний, он реализует свою собствен- ную систему управления блокировками с семью состояниями. На рис. 40.1 показано решение уровней блокировок. ♦ 0 — свободно (no lock). ♦ 1 — пустая блокировка (null lock), что соответствует интересу объекта, не накла- дывающего ограничений в использовании другими. Запрос пустой блокировки позволяет транзакции читать заблокированные данные. ♦ 2 — разделяемое чтение (shared read), что позволяет записывать. Разделяемое чте- ние является обычным режимом для блокировки таблицы, когда транзакция изме- няет некоторые части таблицы. ♦ 3 — защищенное чтение (protected read), что позволяет другим читать, но не пи- сать. Защищенное чтение является обычным режимом для блокировки страницы базы данных, которая находится в кэше и не была изменена.
Гпава 40. Менеджер блокировок 877 0 no lock 1 null lock 2 shared read 3 protected read 4 shared write 5 protected write 6 exclusive 0 по lock Y Y Y Y Y Y Y 1 null lock Y Y Y Y Y Y Y 2 shared read Y Y Y Y Y Y n 3 protected read Y Y Y Y n n n 4 shared write Y Y Y n Y n n 5 protected write Y Y Y n n n n 6 exclusive Y Y n n n n n Рис. 40.1. Состояния внутренних блокировок Firebird ♦ 4 — совместно используемая запись (shared write), другой обычный режим блоки- ровки таблицы. Совместно используемая запись совместима с разделяемым чте- нием и другими совместно используемыми записями, но не с любым защищен- ным режимом. ♦ 5 — защищенная запись (protected write), которая допускает разделяемое чтение и пустую блокировку и ничего больше. Защищенная запись используется при ре- жиме consistency и для такой блокировки базы данных, что обычные пользовате- ли не могут получить к ней доступ. ♦ 6 — исключительный доступ (exclusive), используется для внутренних структур, когда параллельный доступ может повлиять на изменения или привести к тому, что вторая транзакция будет читать незавершенные изменения данных. Таблица блокировок Менеджер блокировок управляет таблицей блокировок для координации совместного использования ресурсов в клиентских потоках. Информация, предоставляемая здесь, может быть полезной при попытках исправить ситуации взаимных блокировок, на- пример: ♦ все в настоящий момент заблокировано в системе в своем состоянии; ♦ глобальная статистика заголовков, такая как размер таблицы блокировок, множе- ство свободных блокировок, множество взаимных блокировок и т. д.; ♦ флаги процессов, указывающие, была ли передана блокировка или она осуществ- ляет ожидание. Блокировки сохраняются в последовательностях, каждая последовательность иден- тифицируется номером в соответствии с типом блокируемого ресурса. Номера по- следовательностей объясняются в табл. 40.5.
878 Часть IX. Инструменты Использование пустой блокировки Запущенная транзакция использует таблицу блокировок в качестве доски объявле- ний. Чтобы исключить сборку мусора версий записей, которые нужны другой тран- закции, каждая транзакция должна знать самое старое действие, которое видит дру- гая транзакция. Вот как это сделано: 1. При старте транзакция сохраняет в области данных собственного блока иденти- фикатор старейшей транзакции, которая еще выполняется (активна). 2. Затем она устанавливает пустую блокировку на все параллельные транзакции. Ко- гда будет получен каждый блок, ей возвращается содержимое области данных. Новая транзакция проверяет блок каждой существующей транзакции для отыскания идентификатора старейшей транзакции, о которой знает каждая активная транзакция. Свободные списки Списки владельцев, ресурсов и запросов представлены в виде цепочки с возмож- ностью проходить по ним вперед и назад. В начале каждого блока находится указа- тель на следующий блок. Списки указателей используются Менеджером блокировок, когда ему нужно разместить новый блок и постараться найти свободные блоки для повторного использования. Он выделит новый блок, только если не существует сво- бодных блоков нужного типа и размера. Свободные элементы содержат прямые и обратные указатели на первый и последний свободный блок соответственно. Взаимные блокировки Взаимная блокировка (deadlock) возникает, когда владелец А хочет блокировать ре- сурс 1, который заблокирован владельцем В, а владелец В хочет заблокировать ре- сурс 2, который заблокирован процессом А. Она также может появиться для одного ресурса, если два владельца начинают с блокировки чтения и запрашивают блоки- ровки на запись. Эту ситуацию владельцы не могут разрешить без постороннего вмешательства. Ре- шение осуществляется, когда взаимная блокировка определяется последующим ска- нированием и Менеджер блокировок возвращает ошибку одному владельцу или дру- гому. Интервал сканирования блокировок по умолчанию — параметр DeadiockTimeout в файле конфигурации— составляет 10 секунд. Этот интервал не используется при условиях, где присутствует wait. Ожидание является нормальным в системе, которая управляет параллельными изменениями, и не требует затрат на сканирование. "Взаимные блокировки", которые не являются взаимными блокировками Сообщения о взаимных блокировках могут не помочь точно указать на "проблемы deadlock", наблюдаемые в ваших приложениях. Взаимные блокировки всегда вклю- чают двух владельцев (или две раздельные транзакции), каждый из которых мешает другому. Firebird имеет привычку выдавать клиентам сообщения "взаимные блоки-
Гпава 40. Менеджер блокировок 879 ровки" при большинстве конфликтов блокировок, хотя истинная взаимная блокиров- ка — как было описано ранее — довольно редкое явление. ♦ Ошибки, возвращаемые как "конфликт блокировки" при запросах блокировки no wait, не фиксируются в таблице блокировок как взаимные блокировки, потому что ожидает только один владелец. ♦ Ошибки, возвращаемые как "взаимные блокировки" с последующим сообщением "Update conflicts with concurrent update" (Конфликты обновлений при параллель- ном обновлении), также не являются настоящими взаимными блокировками. В этом случае произошло всего лишь то, что один владелец изменил (или удалил) строку и "пошел" дальше. Другой параллельный владелец попытался изменить (или удалить) ту же самую запись, ждал освобождения ее первым владельцем, а теперь получил ошибку, потому что последняя подтвержденная версия записи была изменена. Таблица блокировок данных может быть выведена в более или менее читаемом фор- мате с помощью утилиты Lock Print. Утилита Lock Print Программой, которая выбирает статистику таблицы блокировки, является исполняе- мый модуль fb_lock_print, который можно найти в каталоге /bin каталога инсталля- ции Firebird. (Для версии 1.0.x ищите iblockpr.exe в Windows или gds_lock_pr в POSIX.) Доступны два синтаксиса: один для статичного отчета, другой для задания интерактивной выборки в указанных интервалах. Синтаксис для Firebird 1.5 и выше: fb_lock jorint <переключатели> Версия 1.0.x, POSIX: gds_lock_pr <переключатели> Версия 1.0.x, Windows: iblockpr <переключателж> Программа fb lock print имеет множество переключателей, описанных в табл. 40.1. Когда не задано ни одного переключателя, fb_lock_print выводит итоговую информа- цию, описывающую заголовок блока и владельцев, связанных с Менеджером блоки- ровок. Таблица 40.1. Переключатели для отчетов Lock Print Переключатель Описание (нет переключателей) Выводит итоговую информацию, описывающую заголовок блока и владельцев, связанных с Менеджером блокировок
880 Часть IX. Инструменты Таблица 40.1 (окончание) Переключатель Описание -а Выводит содержимое таблицы блокировок, включая заголо- вок блока, группы блоков, группы владельцев и группы за- просов. Группа блока представляет ресурс, который должен быть заблокирован (база данных, транзакция, отношение, страница базы данных и т. д.), и идентифицирует владельца, который имеет или запросил блокирование этого объекта. Группа запроса описывает запрос в процессе блокирования ресурса. Группа запроса может описывать или предостав- ленную блокировку, или ожидание завершения запроса на блокировку -с Указывает, что таблица блокировок должна быть скопирова- на, а не использована сама. Копирование является быстрым и создает мгновенный статичный снимок таблицы блокиро- вок. Тем не менее он остановит все процессы доступа к базе данных на время своего выполнения -f Задает, что анализ должен быть выполнен на указанном файле, а не на настоящем файле блокировки. К сожалению, этот переключатель не работает в Firebird -h Выводит только историю -i <переключатели> <t> <n> Запускает интерактивный режим (см. разд. "Интерактивные отчеты"). Если указано только -i, то выводятся все данные -1 Выводит только группы блоков -n Указывает, что не существует "никакого моста". Мост явля- ется переходным механизмом, распознающим множество серверов различных версий Firebird на одной машине. Это не доступно в версиях Firebird 1.0.x или 1.5, но, скорее всего, будет реализовано в следующих версиях Firebird -•0 Выводит группы владельцев -p То же, что и -о. (Группы владельцев, используемых в вызо- вах групп процессов.) - r Выводит группы запросов -s <n> Выводит заголовок таблицы блокировок, группы владельцев и блокировки указанных серий. Аргумент <п> указывает тип блокируемого ресурса, который вы хотите отобразить. Номе- ра см. в табл. 40.5 -t Выводит статистику для всех серий (только интерактивный отчет) -w Выводит "граф ожидания" — блоки владельцев с запросами ожидания, чего они ожидают, чего ожидают их владельцы и т. д. В этом отчете вы можете просмотреть, какой запрос владельца блокирует другие запросы в таблице блокировок. Это самый простой способ найти блокировки, хотя полное отображение блокировок даст вам много больше информа- ции относительно взаимосвязей, находящихся в очереди запросов
Гпава 40. Менеджер блокировок 881 Статичные отчеты Статичные отчеты выводят текущее состояние (мгновенный снимок) таблицы блоки- ровок. Допустимы любые переключатели, за исключением -1, вы можете "затолкать" в него множество переключателей. Например, для вывода "ожидающего" графа плюс группы истории введите: fb_lock_print -wh Интерактивные отчеты Вторая форма собирает заданные номера выборок фиксированных интервалов и соз- дает интерактивный отчет, отслеживающий текущую деятельность таблицы блоки- ровок. Синтаксис: fb_lock_print [~i{a,o,w}] [t n] t задает время в секундах между выборками, п задает количество требуемых выбо- рок. Если вы не задаете значений для п и г, то значением по умолчанию является п = 1. Выборки осуществляются п раз через интервалы в t секунд. Одна группа выво- дится для каждой выборки. Средние значения выборки выводятся в конце каждого столбца. Следующий оператор выводит статистику (доступ к таблице блокировок) в виде 10 выборок, осуществляемых каждые 3 секунды: fb_lock_print -ia 3 10 В конце этой главы представлен мгновенный снимок интерактивного отчета с объяс- нением значения каждого столбца. СОВЕТ. Ограничения буфера в окне командной строки могут привести к "исчез- новению" выходных данных за исключением последней части. Вы можете напра- вить вывод утилите more (или less в POSIX). fb_lock_print -wh | more Когда окно будет заполнено, нажмите или держите нажатой клавишу <Return> (Enter) для отображения дальнейшего вывода по строке за раз. Нажатие клавиш <Ctrl>+<C> завершает more или less. Вывод отчета в файл Обычно результаты являются достаточно большими, чтобы их просматривать с кон- соли. Вы можете направить их в выходной файл, например, в каталог с именем /data/server_reports/ (ваш выбор!) таким образом: fb_lock_print -а > /data/server_reports/lock.txt
882 Часть IX. Инструменты Если вы видите, что Lock Print выполняется более минуты или двух или замечаете, что он заполняет ваш диск, остановите его с помощью <Ctrl>+<C> или эквивалента вашей платформы. Группы отображаются в порядке внутренних списков. Новые группы помещаются в начало списка, следовательно, обновленная таблица блокировок будет показана в строках в обратном порядке. Поскольку группы блоков, запросов и владельцев освобождаются и используются заново, порядок становится полным беспорядком. Текстовый редактор будет весьма полезным для исследования отношений. Простое использование Lock Print Мы посмотрим на пример очень простой статистики, полученной от Lock Print без переключателей. ♦ Заголовок блока всегда будет первым. ♦ Далее идут группы владельцев — за группой владельца следуют все запросы это- го владельца. Каждый владелец цепочки выводится с его запросами. ♦ После всех владельцев и запросов идут блокировки. ♦ Последним элементом является история записей. Группа Lock_Header Вначале мы рассмотрим только группу заголовка блока, которая описывает основ- ную конфигурацию и состояние таблицы блокировок. На рис. 40.2 числа добавлены для ссылок на каждый элемент при объяснении в табл. 40.2. 1)LOCK_HEADER BLOCK (2)Version: 114,(3)Active owner: 0, (4)Length: 32768, (5)Used: 12976 (6) Seamask: 0x0, (7) Flags: 0x0001 (8)Enqs: 10, (9)Converts: 0, (10) Rejects: 0, (11) Blocks: 0 (12)Deadlock scans: 0, (13)Deadlocks: 0, (14) Scan interval: 10 (15) Acquires: 36, (16) Acquire blocks: 0, (17) Spin count: 0 (18)Mutex wait: 0.0% (19)Hash slots: 101, (20)Hash lengths (min/avg/max): 0/ 0/ 1 (21)Remove node: 0, (22) Insert queue: 0, (23)Insert prior: 0 (24)0wners (5): forward: 12056, backward: 11628 (25)Free owners (4): forward: 11804, backward: 12232 (26)Free locks(l): forward: 11560, backward: 11560 (27) Free requests (1): forward: 12116, backward: 12116 <28> L ock Ordering: Enabled Рис. 40.2. Группа Lock_Header Наш отчет Lock Print представляет только что созданную базу данных, к которой имеет доступ одна копия isql в версии 1.0.x Суперсервера для Windows.
Глава 40. Менеджер блокировок 883 Таблица 40.2. Записи группы Lock_Header № Элемент Объяснение 1 LOCK_HEADER_BLOCK Первая группа любого отчета Lock Print. Каждый отчет выводит ровно одну группу заголовка блока 2 Version (Версия) Номер версии Менеджера блокировок. В Firebird 1.5 версия будет 115 для Суперсервера и 5 для Классического серве- ра. В Firebird 1.0.x (для нашего примера) такими версиями будут 114 и 4 соответственно 3 Active owner (Активный владелец) Смещение в группе владельца, представляющее владель- ца, который в настоящий момент управляет таблицей бло- кировок, если такой существует. Если ни один процесс не пишет в таблицу блокировок, то активным владельцем будет 0 4 Length (Длина) Общий объем памяти, выделенный таблице блокировок в байтах 5 Used (Используется) Наибольшая величина смещения в таблице блокировок, которая используется в настоящий момент. В таблице мо- гут существовать свободные группы между ее началом и используемой точкой, если владельцы приходят и уходят. Перед созданием новых групп между этой точкой и концом таблицы блокировок могут быть заново использованы сво- бодные блоки 6 Senmask (Маска семафора) В системах, которые используют статичные семафоры (например, POSIX), это указатель на блок SMB, содержа- щий количество используемых семафоров. Когда нужен семафор, и ни один недоступен, Менеджер блокировок проходит через группы владельцев для поиска того вла- дельца, имеющего неиспользуемый семафор. Если такого нет, система выдает сообщение об ошибке "Semaphores are exhausted” (Семафоры исчерпаны), означающее, что все скомпилированные в системе семафоры используются 7 Flags (Флаги) Определены два битовых флага: LHB_shut_manager, кото- рый, если установлен, указывает, что база данных оста- новлена, и Менеджер блокировок не может обработать больше запросов; и LHB_lock_ordering. В Firebird значе- ние по умолчанию LHB_lock_ordering означает, что бло- кировки предоставляются в порядке запросов (порядок FIFO — первый пришел — первый ушел). Другая установка связана с устаревшей стратегией блокировки и сейчас не используется 8 Enqs (Запросы в очереди) Запросы в очереди— полученные запросы на блокировку. Это число включает запросы, которые пока не могут быть удовлетворены, и запросы, которые могут быть удовлетво- рены немедленно, но не запросы, которые пришли и ушли
884 Часть IX. Инструменты Таблица 40.2 (продолжение) № Элемент Объяснение 9 Converts (Преобразования) Запросы на повышение уровня блокировки. Процесс, под- держивающий блокировку ресурса, запросит изменение режима, если изменяется его доступ к этому ресурсу. Пре- образование происходит от блокировки низшего уровня (например, совместное чтение) к более ограничивающему уровню (например, исключительный доступ). Например, транзакция в режиме concurrency, которая читала данные таблицы, и решает изменить данные в этой таблице, будет преобразовывать свою блокировку от разделяемого чтения до разделяемой записи. Преобразования являются наибо- лее частыми для блокировок страниц, поскольку страница обычно вначале читается, а потом изменяется 10 Rejects (Отмены) Запросы, которые не могут быть удовлетворены. Это могут быть блокировки, запрошенные в режиме "no wait", или это могут быть блокировки, которые были отменены, потому что приводили к взаимным блокировкам. Так как методы доступа иногда запрашивают блокировку "по wait" для внутренних структур, то вы увидите отмены, даже если все транзакции выполняются в режиме "wait" и не существует конфликтов между их операциями 11 Blocks(Блоки) Запросы, которые не могут быть удовлетворены немедлен- но, потому что некоторый другой владелец имеет несо- вместимую блокировку на этот ресурс 12 Deadlock scans (Сканирование взаим- ных блокировок) Сколько раз Менеджер блокировок просматривает цепочку блокировок и владельцев в процессе поиска взаимных блокировок. Менеджер блокировок начинает сканирование, когда процесс ожидает блокировки в течение 10 секунд 13 Deadlocks (Взаимные блокировки) Количество найденных взаимных блокировок. См. разд. "Взаимные блокировки" 14 Scan interval (Интер- вал сканирования) Количество секунд, которые ожидает Менеджер блокиро- вок после начала запроса, прежде чем запустить скани- рование взаимных блокировок. Значение по умолчанию 10 секунд 15 Acquires(Запросы) Количество раз, сколько владелец— или сервер от имени конкретного владельца — запрашивает исключительное управление для таблицы блокировок, чтобы выполнить изменения 16 Acquire blocks (Забло- кированные запросы) Количество раз, сколько владелец— или сервер от имени конкретного владельца — находился в состоянии ожидания при запросе исключительного управления таблицей блоки- ровок 17 Spin count (Количество блокировок) Это режим для ожидания взаимной блокировки и повторе- ния запроса к таблице блокировок Firebird. По умолчанию установлено в ноль (отключено), но может быть сделано доступным в файле конфигурации 18 Mutex wait (Ожидание блокировок) Процент попыток, которые были заблокированы, когда владелец старался обратиться к таблице блокировок — т. е. ((acquire blocks) / (acquires)) * 100
Гпава 40. Менеджер блокировок 885 Таблица 40.2 (продолжение) № Элемент Объяснение 19 Hash slots (Сегменты хэша) Ресурсы размещены в хэш-таблице. Они хранятся соглас- но значению. По умолчанию хэш-таблица содержит 101 сегмент. Это значение (которое должно быть простым числом) может быть увеличено в файле конфигурации. Оно никогда не должно быть меньше, чем 101 20 Hash lengths(Длина сегмента) При каждом сегменте находятся ресурсы (заблокирован- ные группы). Этот элемент сообщает минимальную, сред- нюю и максимальную длину цепочки заблокированных групп, находящихся в сегменте. Среднее значение, боль- шее чем 15, означает, что не достаточно сегментов 21 Remove node (Удаление узла) Для устранения проблем, которые являются следствием того, что активный владелец зависает при запросе к табли- це блокировок с потенциально осуществленными ее изме- нениями, владелец сообщает о намерении удалить узел из этой таблицы. После успешного завершения операции владелец удаляет сообщение об удалении. Если любой другой владелец находит сообщение об удалении, которое он не создавал, он наводит порядок 22 Insert queue (Добавить очередь) Эквивалент предыдущей схеме удаления узла, за исклю- чением того, что здесь узел добавляется 23 Insert prior (Предыдущее добавление) Для очистки ошибочного добавления необходимо знать не только, что было добавлено, но и где это размещалось. Это как раз где 24 Owners(Владельцы) Количество владельцев, которые соединены с таблицей блокировок. Только один из этих владельцев может изме- нять таблицу в конкретный момент ("активный владелец"). Другие владельцы ожидают освобождения блокировок. В нашем примере существует четыре владельца, не яв- ляющихся активными. Два владельца подключены через isql; один может быть подключен через DSQL, а еще один — сама база данных 25 Free owners (Свободные владельцы) Количество блоков владельцев, выделенных владельцам, которые завершили их соединения, оставив блоки неис- пользованными. В этом случае, вероятно, существует две транзакции, занимавшиеся созданием базы данных, кото- рые были подтверждены 26 Free locks(Свободные блокировки) Гоуппы блокировок определяют ресурс (база данных, от- ношение, транзакция и т. д.), который заблокирован, а не блок на ресурс. Этот элемент является количеством групп блокировок, которые были освобождены, но пока повторно не использованы. В этом случае существует один свобод- ный блок. Когда владелец запрашивает блокировку на ресурс, который в настоящий момент не заблокирован, Менеджер блокировок сначала просматривает список сво- бодных блоков в заголовке блока. Если существует группа с нужным размером ключа, то эта группа блока будет ис- пользована. Если нет, то в свободной памяти будет раз- мещена новая группа блока
886 Часть IX. Инструменты Таблица 40.2 (окончание) № Элемент Объяснение 27 Free requests (Свободные запросы) Гоуппы запросов идентифицируют запросы на блокировки ресурсов, неважно, удовлетворенные или нет. Этот эле- мент является количеством групп запросов, которые были освобождены и не использованы повторно 28 Lock ordering(Порядок блокировок) Порядок блокировок означает получение запросов на бло- кировку в порядке их поступления, даже если последующие запросы должны быть обслужены немедленно. Включено, если флаг LHB_lock_ordering (см. элемент 7) установлен, и является значением по умолчанию для Firebird, т. к. обеспечивает оптимальную производительность. Альтер- нативой является (не используется сейчас) предоставле- ние блокировок всем владельцам, желающим совместно использовать и "подавлять" владельцев, которые имеют существующие запросы. Эта не используемая стратегия гарантирует, что участники будут обработаны быстро, од- нако с риском причинения вреда другим Группы владельцев Группы владельцев, изображенные на рис. 40.3, описывают транзакцию или другого пользователя в Менеджере блокировок. Владельцы делятся на несколько типов, идентифицируемых пятью числами: ♦ 1 — процесс; ♦ 2 — база данных; ♦ 3 — клиентское соединение (в Классическом сервере клиентские соединения все- гда являются процессами); ♦ 4 — транзакция; ♦ 5 — фиктивный процесс. < ПРИМЕЧАНИЕ. В некоторых причудливых соглашениях по кодированию на тран- закции никогда не ссылаются по их идентификационному номеру (4), а использу- -X. ют номер 255. (1) OWNER BLOCK 11872 (2)Owner id: 9909104,(3) type: 2,(4)flags: 0x202,(5)pending: 0, (6)semid: 3 (7)Process id: 1868, (8)UID: 0x0 (9)Alive (10)Flags: 0x02 scan (11)Requests (3): 12300, backward: 12124 (12)Blocks: *empty* Рис. 40.3. Группа владельца
Гпава 40. Менеджер блокировок 887 Смещение конкретной группы владельца в таблице блокировок (здесь это 11 872) также является идентификатором, используемым в заголовке блокировок для "актив- ного владельца", если этот пользователь активно изменяет таблицу блокировки. Пер- вая выводимая группа обычно является номером, выводимым в группу заголовка блокировок в начале списка владельцев. Значение в списке указателей является по- лем в группе, которое содержит прямые и обратные указатели на группы владельцев. В табл. 40.3 описаны записи в группе владельца. СОВЕТ. Если вы просматриваете ваш результат работы утилиты в текстовом редакторе, вы можете отыскивать запросы, принадлежащие идентификатору этого владельца. Таблица 40.3. Записи групп владельцев № Элемент Объяснение 1 OWNER BLOCK Идентифицирует конкретного владельца. Число, сле- дующее за заголовком (11 872), является смещением группы владельца в таблице блокировок и используется в качестве идентификатора владельца в этой таблице 2 Owner id (Идентификатор владельца) В Классическом сервере владельцем всегда является процесс, а идентификатором владельца всегда является идентификатор процесса. В Суперсервере владельцем является либо база данных и идентификатором будет группа базы данных, либо соединение и идентификато- ром будет эта группа соединения 3 Owner type (Тип владельца) Типом владельца является число между 1 и 4 или 255 (фиктивный процесс) 4 Flags (Флаги) Биты, которые определяют состояние. Процесс в одно и то же время может находиться более чем в одном со- стоянии (см. табл. 40.4) 5 Pending (Ожидание завершения) Смещение группы запроса блокировки, описывающей запрошенную владельцем блокировку, но еще им не по- лученную. Владелец может иметь не более одного ожи- дающего запроса в один момент времени 6 Semid (Идентификатор семафора) Идентификатор семафора, назначенного этому пользова- телю. Если он может быть использован, то слово "Available" (Доступен) должно следовать после идентифи- катора 7 Process_id (Идентификатор процесса) В Классическом сервере это идентификатор процесса владельца. В Суперсервере, если владельцем является соединение, база данных или транзакция, то этот владе- лец станет идентификатором процесса 8 UID (Идентификатор пользователя) В POSIX это идентификатор пользователя, являющегося владельцем процесса. В Windows это всегда ноль 9 Alive I Dead (Живой | Мертвый) Lock Printer вызывает подпрограмму ISC_check_process_ existence и сообщает результат
888 Часть IX. Инструменты Таблица 40.3 (окончание) № Элемент Объяснение 10 Flags (Флаги) Мнемоника флагов — корректно выводится 2 в виде 0x02 (а не 0x202, как в пункте (4)) 11 Requests(Запросы) Запросы на блокировку, обработанные или ожидающие завершения, которые связаны с этим процессом. Числа последующее и предыдущее ссылаются на последующий и предыдущий элемент в очереди запросов, принадле- жащих этому процессу. Числа задают смещения 12 Blocks (Группы) Временный список блокировок (групп запросов), блоки- рующих другие запросы на блокировку, которыми владеет этот процесс. Этот список очищается, когда процесс со- общает, что он должен освободить или понизить уровень его блокировки в предположении, что он в состоянии это сделать В табл. 40.4 описаны состояния, представленные разнообразными флагами вла- дельца. Таблица 40.4. Состояния флагов владельца Символ Значение Состояние OWN_blocking 1 Владелец заблокирован. Если установлен, значит процесс име- ет, по меньшей мере, один блок, который другой процесс не хочет совместно использовать OWN_scanned 2 Владелец был проверен в текущем сканировании взаимных блокировок OWN_manager 4 Системы, которые отключают сообщения между группами, име- ют привилегированный Менеджер блокировок, который переда- ет сообщения. Этот владелец является таким менеджером OWN_signal 8 Владелец должен был выдать сообщение, но не смог это сде- лать из-за ошибки, поэтому сигнал будет вызван менеджером блокировок OWN_wakeup 32 Владелец сообщил об освобождении блокировки OWN starved 128 Может быть, этот поток завис. Зависание случается в многопо- точной системе Solaris и означает, что владелец (процесс) вы- полнил более 500 неудачных попыток получения доступа к таб- лице блокировок для освобождения блока OWN_signaled 16 Предполагается, что сигнал был отправлен. Он обращается к флагу own. as'-flags, но был суммирован по OR с указанными флагами. Обратите внимание: похоже, что выведенное здесь шестнадцатеричное число 202 является ошибкой синтаксиче- ского анализатора
Гпава 40. Менеджер блокировок 889 Группы блокировок (группы ресурсов) Группы блокировок следуют после групп запросов в выходном протоколе, однако группы запросов будут проще в понимании, если мы вначале рассмотрим группы блокировок. Группа блокировок представляет блокируемый ресурс. Типы блокировок"серии" Блокировки ресурсов приходят различных типов или в виде серий в соответствии с типом ресурса, блокировку которого владельцы запрашивают у таблицы блокировок. В табл. 40.5 определяются и описываются различные типы блокировки ресурсов и их назначение. Таблица 40.5. Типы ресурсов (серии) Символ Серия Тип LCK_database 1 Корень дерева блокировки. В Классическом сервере блоки- ровка базы данных выполняется для каждого процесса, который соединяется с базой данных. Первый процесс по- лучает исключительную блокировку. Следующий процесс сообщает о конфликте и сигнализирует первому о необхо- димости понижения уровня его блокировки от исключитель- ной до уровня совместного использования. После этого все блокировки самой базы данных создаются для совместного чтения. В Суперсервере база данных получает на себя ис- ключительную блокировку LCK_relation 2 Индивидуальная таблица блокировки. Таблица блокировки указывает, что процесс читает и пишет в указанную таблицу в своей текущей транзакции или что он использует предло- жение reserving в операторе start transaction для со- общения своего намерения читать таблицу или писать в таблицу. В этой таблице ключевым полем является rdb$RELAtion_id. Заметьте, что оба запроса сообщают ее состояние как 2(2), указывая, что они запросили и получили блокировку на совместное чтение из таблицы LCK_bdb 3 Индивидуальный блок буфера. Блокировка bdb является блокировкой страницы базы данных. Такие блокировки воз- водятся, когда два или более владельца соединяются с базой данных в Классическом сервере. Они устанавливают- ся, когда процесс собирается читать или писать страницу, и освобождаются, когда процесс завершает работу с буфером и требует освобождения памяти, или когда другому вла- дельцу нужна эта страница LCK_tra 4 Блокировка индивидуальной транзакции. Каждое действие получает исключительную блокировку для своей транзакции при ее старте. Другие владельцы могут получать пустую блокировку для чтения их состояния LCK_rel_exist 5 Блокировка существования отношения. Предотвращает удаление таблиц, в то время как любые другие владельцы подготавливают запрос, который использует эту таблицу
890 Часть IX. Инструменты Таблица 40.5 (продолжение) Символ Серия Тип LCK_idx_exist 6 Блокировка существования индекса. Предотвращает удале- ние или дезактивацию индекса, в то время как любые другие владельцы подготавливают запрос, который использует этот ресурс LCK_attachment 7 Не используется. Блокировка соединения для поддержания блокировок записей dBase, которые могут присутствовать в пределах транзакции LCK_shadow 8 Блокировка для синхронизации добавления теневых копий (shadow). Главным образом для Классического сервера LCK_sweep 9 Блокировка чистки для одного процесса чистки. Чистка яв- ляется довольно дорогой операцией и лучше работает, если выполняется только одним потоком или соединением. Ре- альные "чистильщики" сохраняют исключительную блоки- ровку в своей серии для исключения конфликтов. Эта серия используется для межпроцессных связей в Классическом сервере LCK_file_extend 10 Блокировка на синхронизацию расширения файла. Файл расширения базы данных является другой операцией, кото- рая не выполняется таким же образом, если две транзакции пытаются выполнять операцию одновременно. Эта серия используется для межпроцессных связей в Классическом сервере LCK_retaining 11 Самая молодая транзакция, подтвержденная с сохранением контекста (commit retaining). Это используется только в VMS. Возможно, это отмечает то место, где Firebird имеет расши- ренную семантику блокировок для нужд VMS и, следова- тельно, требует специальных средств для работы с Менед- жером блокировок VMS LCK—expression 12 Механизм кэширования выражения индекса. Первоначально эта серия предназначалась для описания выражений индек- сов — как их вычислять, как должен выглядеть результат вычислений и т. д. По разным причинам сейчас это исполь- зуется при удалении индекса LCK_record_locking 13 Блок на существование блокировки записи. Эта серия ука- зывает, что была запрошена блокировка записи конкретной таблицы. Первый процесс, запрашивающий блокировку записи таблицы, также получает блокировку таблицы. Пока требуется эта блокировка, блокировки сохраняются для соединения. Когда появляется вторая транзакция, уровень блокировки таблицы снижается до совместного использова- ния. Эта серия используется только в устаревшем коде эмуляции PC LCK_record 14 Блокировка записи. Эта серия также используется только в устаревшем коде эмуляции PC и использует rdb$db_key в качестве имени блокировки
Гпава 40. Менеджер блокировок 891 Таблица 40.5 (окончание) Символ Серия Тип LCK_prc_exist 15 Блокировка существующей процедуры. Не позволяет уда- лять процедуры и триггеры, пока любой другой владелец подготавливает запрос, который использует этот ресурс (или зависит от него) LCK_range_relation 16 Блокировка диапазона отношения. Опять же, эта серия ис- пользуется только в коде эмуляции PC, который имеет кон- цепцию изменения диапазонов LCK_update_shadow 17 Блокировка синхронного изменения теневой копии. Эта се- рия используется для ограничения до единицы количества процессов, которые приводят к замене теневой копии или отмене теневого копирования Варианты синтаксиса для отображения групп блокировок Для вывода групп блокировок ресурсов заданной серии вам нужно включить номер серии в качестве аргумента: fb_lock_print -s 2 Серия 1:база данных Группа блокировки на рис. 40.4 представляет саму базу данных. Она имеет исключи- тельный доступ для одного владельца— базы данных. В Классическом сервере вы увидите несколько владельцев для базы данных. (l)LOCK BLOCK 11988 (2)Series: 1, (3)Parent: 0, (4)State: 6, (5)size: 12(6) length: 12 (7) data: 0 (8)Key: <61><172><21>x<0><0><13><0X145>k<0><0>, Flags: 0x00, Pending request count: 0 (9)Hash que (1): forward: 768, backward: 768 (lO)Requests (1): forward: 12300, backward: 12300 (ll)Request 12300, Owner: 11872, State: 6 (6), Flags; 0x00 Рис. 40.4. Группа блокировки серии 1 (база данных) В табл. 40.6 объяснено, что означают записи этого отчета. Таблица 40.6. Записи группы блокировки (ресурсов) № Значение Объяснение 1 LOCK BLOCK Идентифицирует группу описания заблокированного ресурса. Число является смещением этой группы в таблице блокировок. Оно указывает группу в другой группе, которая на нее ссылается
892 Часть IX. Инструменты Таблица 40.6 (продолжение) № Значение Объяснение 2 Series (Серия) Тип ресурса, представляемого этой блокировкой. Это серия типа 1 — ресурс базы данных 3 Parent (Родитель) Родитель для всех блокировок, связанных с базой данных. Явля- ется блокировкой самой базы данных. Единственными блокиров- ками ресурсов, которые имеют значение 0 для родителя, являют- ся блокировки базы данных и журналы. Ключи, которые иденти- фицируют блокировки в серии, имеют смысл только в контексте базы данных. Обратите внимание: несовпадения (ошибки) будут замечены внимательным читателем 4 State (Состояние) Наивысшее текущее состояние блокировки. Блокировки имеют семь возможных состояний — см. рис. 40.1. Состояния внутренних блокировок Firebird описаны в самом начале этой главы. Пустая блокировка позволяет процессу получить блокировку ресурса, независимо от того, заблокирован ли (и как) этот ресурс кем-либо другим. Получение такой блокировки позволяет этому владельцу читать данные самой блокировки. Firebird сохраняет важную, но быстро меняющуюся информацию в блокировках— см. разд. "Использование пустой блокировки" 5 Size (Размер) Длина в байтах той части группы блокировки, которая содержит ключ. Размер округляется до естественных границ машины (сло- во, двойное слово, четыре слова) 6 Length (Длина) Фактическая длина ключа, которая из-за округления может быть меньше, чем размер 7 Data (Данные) Только блокировки журналов и транзакций содержат данные. Данные являются 32-битовым целым числом 8 Key (Ключ) Идентификатор заблокированного ресурса. Комбинация ключа, серии и родителя уникально идентифицирует заблокированный ресурс. Для базы данных ключом является имя базы данных (или некоторый эквивалент). Он может не выводиться в системах, ко- торые используют целые числа как идентификаторы. Для отноше- ния и существующей блокировки отношения ключ является иден- тификатором отношения. Для существующей блокировки индекса ключом является идентификатор отношения * 1000 плюс иденти- фикатор индекса. Для блокировки теневой копии ключом является null, потому что существует только одно состояние теневого копирования базы данных. Для транзакции ключом является идентификатор транзакции. Для соединения ключом является идентификатор соединения 9 Hash queue (Очередь) Начало и конец очереди хэш для ключей ресурсов. Менеджер блокировок хранит хэш-таблицу для упрощения поиска ресурсов по имени. Когда процесс запрашивает блокировку ресурса, он задает ресурс по серии, родителю и ключу. Менеджер блокировок объединяет эти значения вместе для создания ключа хэш, а затем отыскивает в списке, связанном с этим хэш-ключом, группу нуж- ной блокировки 10 Requests (Запросы) Вначале количество запросов на блокировку этого ресурса, затем указатели вперед и назад на группы блокировок. Имейте в виду, что обратный указатель ссылается на конец последнего блока
Гпава 40. Менеджер блокировок 893 Таблица 40.6 (окончание) № Значение Объяснение — Request (Запрос) Список запросов, включая идентификатор запрашиваемой группы, процесс, выполняющий запрос, фактическое состояние блокиров- ки с запрашиваемым состоянием в круглых скобках. Состояние 6(6) в этом случае указывает фактическое состояние 6 и запра- шиваемое состояние 6 Flags (Флаги) Флаг запроса содержит биты, которые могут комбинироваться. Это: 1. Блокирование: запрос отмечается как заблокированный, если кто-то другой требует этот ресурс и не может его совместно ис- пользовать по причине текущего уровня блокировки. Блокирую- щий бит очищается, когда блокирующее сообщение отправляется блокирующему владельцу. 2. Ожидание завершения: это наиболее часто просматриваемый бит. Он указывает, что запрос ожидает, когда блокирующий про- цесс освободит эту блокировку. Вы не должны смотреть установку этого бита в блокировках bdb. 4. Конвертирование: запрос конвертируется, если процесс уже имеет блокировку этого ресурса и требует более высокого уровня блокировки, а преобразование не может быть выполнено немед- ленно. 8. Отмена: запрос блокировки отменяется, если запрос в режиме ко wait и не может быть удовлетворен немедленно или если удовлетворение запроса может привести к взаимной блокировке Серия 2: отношение На рис. 40.5 показана группа для ресурса отношения (таблица, просмотр). LOCK BLOCK 2120 Series: 2, Parent: 1220, State: 2. size: 4 length: 4 data: 0 Key: 5 Hash еще (4): forward: 148, backward: 9884 Requests (2): forward: 11432, backward: 5840 Request 11412, Process: 1124, State: 2 (2). Flags: 0 Request 5820, Process: 1124, State: 2 (2), Flags: 0 Рис. 40.5. Группа серии 2 (ресурс отношения) В этом случае оба владельца читают отношение. Для отношения ключевым полем является значение rdb$relation_id. Заметьте, что оба запроса сообщают о своем со- стоянии как 2(2), указывая, что они запросили и получили блокировку на совместное чтение таблицы.
894 Часть IX. Инструменты Серия 3: дескриптор BDB (страница базы данных) На рис. 40.6 показана группа блокировки страницы базы данных (дескриптор bdb). LOCK BLOCK 5384 Series: 3, Parent: 1220. State: 3. size: 4 length: 4 data: 0 Key: 14 Hash que (2) : forward: 11468, backward: 220 Requests (2): forward: 7644. backward: 1712 Request 7624, Process: 2556, State: 3 (3), Flags: 0 Request 1692, Process: 1124, State: 3 (3), Flags: 0 Рис. 40.6. Блокировка серии 3 (дескриптор bdb) Блокировка bdb является блокировкой страницы базы данных. Такие блокировки присутствуют, когда два или более владельца подключены к базе данных в Классиче- ском сервере. Они появляются, когда процесс собирается читать или писать страни- цу, и освобождаются, когда процесс выходит из буферов в кэше и должен освободить пространство или когда другому владельцу нужна эта страница. В этом примере оба владельца читают страницу 14 (ключевое значение). В Классическом сервере суще- ствует множество блокировок типа серия 3 — по одному на каждый буфер страниц в кэше для каждого независимого подключения. В Суперсервере большинство блоки- ровок страниц выполняется сервером и не присутствует в таблице блокировок. Серия 4: транзакция На рис. 40.7 показана группа ресурсов транзакции. LOCK BLOCK 10492 Series: 4, Parent: 1220, State: 6, size: 4 length: 4 data: 585 Key: 585 Hash que (1): forward: 748, backward: 748 Requests (2): forward: 11528, backward: 6416 Request 11508, Process: 1124, State: 6 (6), Flags: 0 Request 6396, Process: 2556. State: 0 (3), Flags: 2 Рис. 40.7. Группа серии 4 (ресурсы транзакции) При запуске каждое действие получает исключительную блокировку по идентифика- тору его транзакции. Эта группа описывает состояние блокировок для транзакции 595. Одна транзакция ожидает завершения другой и, следовательно, может решить, является ли желаемое изменение приемлемым. Когда владелец, который имеет бло- кировку, уходит, его блокировки будут освобождены, а ожидающая транзакция смо- жет читать инвентарную страницу транзакций для определения судьбы исчезнувшей транзакции.
Гпава 40. Менеджер блокировок 895 Серии 5, 6 и 15: существование На рис. 40.8 показаны группы блокировок существования для отношения. SERIES 5 RELATION EXISTENCE LOCKS LOCK BLOCK 7176 Series: 5, Parent: 1220, State: 2, size: 4 length: 4 data: 0 Key: 22 Hash que (2): forward: 284, backward: 1556 Requests (1): forward: 2192, backward: 2192 Request 2172. Process: 2556, State: 2 (2), Flags: 0 SERIES 6 INDEX EXISTENCE LOCKS LOCK BLOCK 1832 Series: 6. Parent: 1220, State: 2. size: 4 length: 4 data: 0 Key:12000 Hash que (2): forward: 2516, backward: 764 Requests (2): forward: 7880. backward: 3536 Request 7860, Process: 2556, State: 2 (2), Flags: 0 Request 3516, Process: 1124, State: 2 (2), Flags: 0 also SERIES 15 PROCEDURE EXISTENCE LOCKS Рис. 40.8. Серии 5, 6 и 15. Группы блокировок существования Блокировки существования отношений (серия 5) предотвращают удаление таблицы, когда какой-нибудь процесс подготавливает запрос, который использует эту таблицу. Эта блокировка является источником ошибок "Object in use" (Объект находится в использовании), которая часто появляется при попытках удаления таблиц. Когда оператор запроса к базе данных подготавливается, компилирующий процесс получает блокировку совместного чтения отношений и индексов, включенных в этот оператор. Такие блокировки сохраняются, пока запрос не будет освобожден или не произойдет отключение от базы данных. Когда процесс собирается удалить отношение из базы данных вместо удаления его содержимого, он должен получить исключительную блокировку на существование этого отношения. Поскольку никто не может получить исключительную блокировку на ресурс, который заблокирован для совместного чтения другим процессом, блоки- ровки совместного чтения предотвращают разрушение отношений и, следовательно, защищают операции с метаданными от аварийного завершения подготавливаемыми запросами. Эта блокировка существования отношения присутствует в отношении, которое имеет значением поля rdbsrelationald 22. Блокировки существования индексов предотвращают удаление или деактивацию ин- дексов, когда другой процесс сохраняет запрос, использующий этот индекс. Когда оператор запроса к базе данных подготавливается, компилирующий процесс также запрашивает блокировку на совместное чтение индексов, используемых в этом
896 Часть IX. Инструменты операторе. Такие блокировки сохраняются, пока запрос не будет освобожден или не произойдет отключения от базы данных. Когда процесс собирается удалить или деактивировать индекс, он должен получить исключительную блокировку на существование этого индекса. Поскольку никто не может получить исключительную блокировку на ресурс, кото- рый заблокирован для совместного чтения другим процессом, блокировки совмест- ного чтения предотвращают разрушение отношений или индексов и, следовательно, защищают операции с метаданными от разрушения компилируемыми запросами. Идентификатор блокировки существования индекса 12 000, который равен иденти- фикатору отношения, умноженному на 1000, плюс идентификатор индекса. Эта бло- кировка сообщает о заинтересованности в существовании индекса 0 для отноше- ния 12. Блокировки существования процедуры в точности аналогичны блокировкам сущест- вования отношения и индекса и служат аналогичным целям. Ключ является иденти- фикатором процедуры из системной таблицы rdb$procedures. Серия 8: теневая копия На рис. 40.9 показаны группы блокировок ресурса теневой копии (shadow). LOCK BLOCK 1352 Series: 8, Parent: 1220, State: 2, size; 4 length: 4 data: 0 Key: 0 Hash que (3) : forward: 1460. backward: 108 Requests (2) : forward: 6844, backward: 7152 Request 6824, Process: 2556, State: 2 (2), Flags: 0 Request 7132, Process: 1124. State: 2 (2), Flags: 0 Рис. 40.9. Серии 8. Группы блокировок ресурса теневой копии Каждый процесс, который подключается к базе данных, получает блокировку на со- вместное чтение на состояние теневого копирования базы данных. Если процесс со- бирается добавить новый файл теневой копии, он должен преобразовать его блоки- ровку в исключительное состояние, что сообщает всем другим процессам о том, что появляется новый файл теневой копии и что они должны писать изменения в этот файл. Эта серия используется для общения между процессами в Классическом серве- ре. Она также используется в Суперсервере, хотя не при всех эффектах, когда не тре- буется IPC. Группы запросов На рис. 40.10 показаны некоторые группы запросов. В табл. 40.7 объясняется значе- ние записей в группе запроса.
Гпава 40. Менеджер блокировок 897 (1) REQUEST BLOCK 7624 (2) Process: 2556,(3) Lock: 5384,(4) State: 3,(5) Mode: 3. (6) Flags:0 (7) AST:3afc4c30, (8) argument: 3af776e0 REQUEST BLOCK 11508 Process; 1124, Lock: 10492, State: 6, mode: 6, Flags: 0 AS'I': 0, argument: 3af76a2c REQUEST BLOCK 6396 Process:2556, Lock: 10492, State: 0. Mode. 3,Flags: 2 AST:0, argument: 0 Рис. 40.10. Некоторые группы запросов Таблица 40.7. Записи группы запросов № Значение Объяснение 1 LOCK BLOCK Идентифицирует конкретный запрос 2 Process (Процесс) Смещение группы процесса, которая описывает процесс, вы- полнивший запрос 3 Lock (Блокировка) Смещение группы блокировки, которая описывает блокируемый ресурс 4 State (Состояние) Состояние блокировки, которое назначено этому ресурсу 5 Mode(Режим) Состояние, которое было запрошено для блокировки. В первых двух примерах состояние (state) такое же, как и режим (mode). Это предоставленные блокировки. Первой был предоставлен режим защищенного чтения, второй — исключительный. В третьем примере находится в состоянии ожидания, следова- тельно, ее состояние 0 (нет блокировки), но режим 3 (защищен- ное чтение) 6 Flags (Флаги) Флаг запроса содержит биты, которые могут комбинироваться. Это: 1: блокировка; 2: ожидание; 4: преобразование; 8: отмена 7 AST Адрес подпрограммы, которая вызывается, если кто-то другой хочет получить конфликтную блокировку на ресурс, используе- мый настоящим запросом. Подпрограммы понижения уровня или освобождения блокировки всегда поставляются блокиров- кам базы данных, состоянию теневого копирования и группам дескрипторов буферов, которые идентифицируют страницы базы данных в кэше. Уровень блокировки базы данных будет понижен от исключи- тельного (для первого пользователя) до совместного чтения, когда второй пользователь появляется в классической архитек- туре. В Суперсервере база данных сохраняет для себя исклю- чительную блокировку. 29 Зак. 420
898 Часть IX. Инструменты Таблица 40.7 (окончание) № Значение Объяснение Блокировка на совместное чтение теневой копии освобождает- ся, когда другой процесс запрашивает блокировку в исключи- тельном режиме, следовательно, он может создавать новый файл (файлы) теневой копии. Коль скоро новые файлы будут созданы, любой другой сможет получить блокировку на совме- стное чтение в состоянии теневого копирования. Когда появляется конфликт для страницы базы данных, про- цесс, который держит страницу, немедленно ее освобождает и понижает уровень своей блокировки, если только страница не находится в процессе фактической модификации. Если да, то страница отмечается, как требующая освобождения, как только модификация будет выполнена 8 Argument (Аргумент) Адрес чего-либо, что может понадобиться подпрограмме AST. В случае BDB это адрес структуры в процессе, которая описы- вает буфер. В случае блокировок базы данных и теневой копии это адрес главной группы (DBB), которая описывает базу данных Группа истории Менеджер блокировок отслеживает действия ввода/вывода, которые он выполнял для каждого владельца. Самые последние действия выводятся в виде двух последних элементов отчета— истории (History) и событий (Events). На рис. 40.11 показана по- следовательность записей истории. GRANT: owner = 11628, lock = 11744, request = 12516 ENQ: owner = 12056, lock = 0, request = 11512 DEMY: owner = 12056, lock = 11744, request = 11512 ENQ: owner = 12056, lock = 0, request = 12408 ! POST: owner = 11628, lock = 11744, request = 12516 WAIT: owner 3 12056, lock = 11744, request = 12408 SCAN: owner = 12056, lock = 11744, request = 12408 POST: owner = 11628, lock = 11744, request = 12516 POST: owner = 11628, lock - 11744, request - 12516 POST: owner = 11628, lock = 11744, request = 12516 DEQ: owner = 11628, lock = 11744, request = 12516 GRANT: owner = 12056, lock s 11744, request = 12408 Рис. 40.11. Вывод записей истории Владельцу 11 628 предоставлена блокировка на ресурс 11 744. Владелец 12 056 ста- вит в очередь запрос на тот же ресурс, запрашивая его в режиме no wait. Блокировка у владельца 11 628 находится в несовместимом режиме, следовательно, этому запро- су будет отказано (deny). Владелец 12 056 опять приходит и ставит в очередь другой запрос, снова запрашивая блокировку, но уже в режиме wait. Менеджер блокировок
Гпава 40. Менеджер блокировок 899 отправляет сообщения владельцу 11 628 по поводу ресурса 11 744. Как было сказано, владелец находится в состоянии ожидания. Через 10 секунд владелец 12 056 все еще в состоянии ожидания, поэтому Менеджер блокировок запускает сканирование вза- имных блокировок. Это не дает никаких результатов, и Менеджер блокировок опять отправляет сообщения владельцу 11 628 (post, post, post). В конце концов владелец 11 628 снимает блокировку, и она предоставляется владельцу 12 056. Вывод событий содержит такую же информацию истории, но в другом формате. На рис. 40.12 показана последовательность записей истории, выводимых в части собы- тий отчета. DEL_0WER: owner = 12232, lock = 12232, request = 0 DEL_0WNER: owner = 12056, lock = 12056, request * 0 DEL_0UNER: owner = 11872, lock = 11872, request = 0 DEL_0WER: owner = 11376, lock = 11376, request = 0 DEL OWER: owner = 11804, lock = 11804, request = 0 ACTIVE: owner» xxxxxx, lock» 0, request» 0 Рис. 40.12. Вывод записей событий В Классическом сервере запись события, похожая на "активную", показанная на ри- сунке, может быть причиной для беспокойства. Она указывает, что один серверный процесс получил флаг (mutex) при доступе к ресурсу, записал свой идентификатор владельца в заголовочную группу блокировки, а затем был уничтожен, в то время как он все еще хранился в таблице блокировок. Однако вторая заголовочная группа бло- кировки должна иметь достаточно информации, чтобы позволить второму процессу отменить все действия, частично завершенные уничтоженным процессом. Интерактивная выборка Интерактивные временные ряды деятельности по блокировкам генерируются, когда fb_iock_print выполняется с переключателем -i для измерения производительности Менеджера блокировок. Это моделирует UNIX-утилиту sar (System Activity Reporter, построитель отчетов системной деятельности). Отчет, показанный на рис. 40.13, вы- бирает каждые 4 секунды десять интервалов. Выборка на рис. 40.13 была сгенериро- вана в Классическом сервере Firebird при выполнении четырех локальных процессов с большим количеством конфликтов: fb_lock_print -ia 4 10 fb_iock_print -ia "запрашивает" статистику: 1. acquire/s: среднее количество попыток в секунду обращений к таблице блокиро- вок. 2. acqwait/s: среднее количество попыток, которым пришлось ожидать, в каждую секунду.
900 Часть IX. Инструменты (1) (2) (3) |<4) (5) 14:32:55 acquire/s acqwait/з %acquait acqrtry/s rtrysuc/s 14:32:59 15654 110 0 0 0 14:33:03 16328 91 0 0 0 14:33:07 16639 106 0 0 0 14:33:11 15467 115 0 0 0 14:33:15 14864 87 0 0 0 14:33:19 15292 117 0 0 0 14:33:23 14939 85 0 0 0 14:33:27 14992 103 0 0 0 14:33:31 15660 124 0 0 0 14:33:35 14800 114 0 0 0 Average: 15463 105 0 0 0 Рис. 40.13. Интерактивная выборка 3. %acqwait: процент попыток, которые были вынуждены ожидать. 4. acqrtry/s: среднее количество повторных обращений к таблице блокировок в се- кунду (в теории только для машин SMP). 5. rtrysuc/s: среднее количество успешных повторных попыток в секунду. СОВЕТ. В качестве подсказки, что означают столбцы в различных интер- активных отчетах, прочтите статью "Reading a Lock Print" Ann Harrison на http://www.ibphoenix.com. Установка конфигурации блокировки Значения по умолчанию Менеджера блокировок должны подойти для большинства вычислительных сред. При работе, особенно в Классическом сервере, имеет смысл подрегулировать эти установки для улучшения производительности или для устране- ния дефицита ресурсов блокировки. Файлы конфигурации Файлы конфигурации размещены в корневом каталоге Firebird. Для сервера Firebird 1.5 и более поздних файл конфигурации называется firebird.conf. Для версии 1.0.x он называется ibconfig в Windows n isc config в POSIX. Используйте текстовый редактор для открытия и редактирования этого файла. LockAcquireSpins Версия 1.5 и выше, файл firebird.conf.
Гпава 40. Менеджер блокировок 901 lock_acquire_spins Версии, предшествующие Firebird 1.5, файл isc config/ibconfig. Эта установка используется только для машин SMP при выполнении Классического сервера. В Классическом сервере в любой момент времени к таблице блокировок может иметь доступ только один клиентский процесс. Флаг управляет доступом к таблице блоки- ровок. Клиентские процессы могут обращаться к флагу условно или безусловно. Если запрос условный, то он будет ошибочным и должен быть повторен. Если запрос безусловный, то он будет ожидать, пока не будет удовлетворен. LockAcquirespins ус- танавливает количество возможных попыток, если запрос к флагу условный. Требуется целое число. Значение по умолчанию 0 (безусловный). Не существует ре- комендованного минимума или максимума. LockHashSlots Версия 1.5 и выше, файл firebird.conf. Iock_hash_slots Версии, предшествующие Firebird 1.5, файл isc config/ibconfig. Используйте этот параметр для настройки списка хэша блокировок. При повышен- ной нагрузке производительность может быть улучшена увеличением области памя- ти под хэш для распределения списка на более короткие цепи хэша. Значением явля- ется целое число. Рекомендуется использовать простое число. Значение по умолча- нию 101. Этот параметр и LockMemsize (см. следующий раздел) должны вычисляться одновре- менно с использованием инструмента Lock Print. Если цепи хэш блокировок более чем в среднем 20, значит объем памяти под хэш слишком мал. Если вам надо увели- чить память под хэш, вам нужно увеличить размер таблицы блокировок на тот же процент. LockMemSize Версия 1.5 и выше, файл firebird.conf. any_lock_mem_size Версии, предшествующие Firebird 1.5, файл isc_config/ibconfig. Этот целочисленный параметр представляет количество байтов для совместно ис- пользуемой памяти для таблицы Менеджера блокировок. Для Классического сервера LockMemsize задает начальное распределение, которое будет динамически увеличи- ваться, пока не будет исчерпана память. ("Lock manager is out of room", "Исчерпана память для Менеджера блокировок" не означает, что можно отправляться пить кофе!) Значение этого параметра связано с размером кэша базы данных, поскольку каждая страница потребует отдельной блокировки в таблице. Если количество страниц базы
902 Часть IX. Инструменты данных в кэше установлено в большое значение, это может привести к проблемам памяти для таблицы блокировок. В Суперсервере объем памяти, выделенный для Менеджера блокировок, не увеличи- вается. Размером по умолчанию в Linux и в Solaris является 98 304 байта (96 Кбайт). В Windows это 262 144 (256 Кбайт). LockGrantOrder Версия 1.5 и выше, файл firebird.conf. Iock_grant_ order Версии, предшествующие Firebird 1.5, файл isc config/ibconfig. Когда соединению требуется блокировка некоторого объекта, оно получает группу запроса блокировки, которая определяет объект и требуемый уровень блокировки. Группы запросов соединяются с группами блокировок в виде удовлетворенных за- просов или в виде запросов, ожидающих разрешения. Параметр LockGrantOrder является логическим. Значение по умолчанию (1 = True) указывает, что блокировки должны предоставляться по принципу "первым при- шел — первым обслужен". Установка False (0) эмулирует поведение InterBase 3.3, когда блокировка предостав- ляется, как только она станет возможной. Результатом этого может оказаться то, что запросы будут "замороженными". Рассматривайте это как запрещенную установку и не пытайтесь ее использовать, если только это не требуется для тестирования неко- торой модификации сервера базы данных. LockSemCount Версия 1.5 и выше, файл firebird.conf. any_lock_sem_count Версии, предшествующие Firebird 1.5, файл isc_config/ibconfig. Это целочисленный параметр, задающий количество доступных семафоров для меж- процессной коммуникации (Inter-Process Communication, 1РС) Классического сервера. Значение по умолчанию 32. Устанавливайте этот параметр для Классического серве- ра для увеличения или уменьшения количества доступных семафоров.
ПРИЛОЖЕНИЯ Приложение 1. Список внешних функций Приложение 2. Решение сетевых проблем Приложение 3. Интерфейсы приложений Приложение 4. Как выполнять ремонт базы данных Приложение 5. Инструменты администрирования Приложение 6. Пример базы данных Приложение 7. Ограничения Firebird Приложение 8. Наборы символов и порядков сортировки Приложение 9. Системные таблицы и просмотры Приложение 10. Коды ошибок Приложение 11. Зарезервированные слова Приложение 12. Литература и источники

ПРИЛОЖЕНИЕ 1 Список внешних функций Функции условной логики ;ОМИЯЯ| Linux, Win32 Возвращает null для подвыражения, если оно преобразуется в непустое значение; иначе возвращает значение подвыражения. Применимо только для числовых типов с фиксированной точкой и только в Firebird 1.0.x. В Fire- bird 1.5 и выше используйте внутреннюю функцию nullifo ' Аргументы VALUEI: столбец или вычисляемое выражение VALUE2: константа или выражение, с которым сравнивается valuei. Если они равны, функция вернет null Возвращаемое значение null, если valuei и value равны; если равенства нет, будет возвращено VALUE 1 Замечания inullifo имеет результат, эквивалентный использованию внутренней SQL-функции nullifo, реализованной в Firebird 1.5 и выше для числовых типов с фиксированной точкой Внешняя функция inullifo существует в трех реализациях: две для 32-битовых и 16-битовых типов (inullif и dnullif), а третья для 64- битовых типов (i64nullif). Если вы хотите использовать ее с любыми чи- словыми типами с фиксированной точкой, объявите все реализации. Объ- явления можно найти в скрипте fbudf.sql в каталоге /UDF вашей инсталля- ции Firebird. Не объявляйте реализацию i64nullif для баз данных, которые не поддер- живают 64-битовые числа— например, для не перенесенной базы'данных InterBase 5 Пример Следующий оператор приведет к тому, что значение stock в таблице products будет установлено в null для всех строк, где оно имеет значе- ние 0: UPDATE PRODUCTS SET STOCK = INULLIF(STOCK, 0) Связанные или похожие функции См. также nullifo , sNuiiifO
906 Приложение 1 FBUDF INVL{VALUEI, VALUE2) Linux, Win32 Эта функция пытается повторить функцию Oracle nvl() только для число- вых типов с фиксированной точкой. Она возвращает указанное непустое значение, если заданный столбец имеет значение null Аргументы VALUE1: столбец или выражение, включающее столбец. Типы с плавающей точкой не поддерживаются. При необходимости используйте в вашем вы- ражении casto для преобразования значения в числовой тип. VALUE2: выражение или константа, которая будет возвращаемым значени- ем, если valuei дает null Возвращаемое значение Непустое значение. Если valueI не является null, то оно будет возвра- щаемым значением; иначе будет возвращаться valuei. Если и valuei, и VALUE2 имеют значение null, то будет возвращено значение null Замечания Логически эта функция эквивалентна простой форме функции coalesce о версии 1.5, когда она используется со столбцом числового типа с фиксиро- ванной точкой, а именно coalesce (valuei, value2) . Она должна рассматриваться как нерекомендуемая для Firebird версии 1.5 и выше. Внешняя функция iNVL() существует в трех реализациях: две для 32-би- товых и 16-битовых типа (invl и dnvl), а третья для 64-битовых типов (i64nvl). Если вы хотите использовать ее с любыми числовыми типами с фиксированной точкой, объявите все реализации. Объявления можно найти в скрипте fbudf.sql в каталоге /UDF вашей инсталляции Firebird. Не объявляйте реализацию i64nvi для баз данных, которые не поддержи- вают 64-битовые числа — например, для не перенесенной базы данных InterBase 5 Пример Следующий запрос возвращает 0, если stock имеет значение null: SELECT PRODUCT_ID, PRODUCT__NAME, INVL(STOCK, 0) FROM PRODUCTS; Связанные или похожие функции См. также sNVL(), iNullif 0 , внутреннюю функцию coalesce () FBUDF SNULLIF (VALUEI, VALUE2) Linux, Win32 Возвращает null для подвыражения, если оно иначе возвращает значение этого подвыражения. Применимо только для символьных типов и должно быть использовано только в Firebird 1,0.x. В Firebird 1.5 и выше используйте nullifo Аргументы valuei: столбец или вычисляемое выражение. VALUE2: константа или выражение, с которым сравнивается valuei. Если они равны, snullif вернет null
Список внешних функций 907 Возвращаемое значение null, если valuei и VALUE2 равны. Если не равны, то возвращается valuei Замечания snullif имеет результат, эквивалентный использованию внутренней функ- ции SQL nullifq, реализованной в Firebird 1.5 и выше с символьными типами Пример Следующий запрос устанавливает значение столбца is_registered в null для всех столбцов, где его значение ч1, a registered имеет значение null: UPDATE ATABLE SET IS_REGISTERED = SNULLIF(IS_REGISTERED, 'T') WHERE REGISTRATION-DATE IS NULL; Связанные или похожие функции См. также iNulliff). Для Firebird 1.5 и выше см. внутреннюю функцию NULLIF () FBUDF Linux, Win32 Эта функция пытается повторить функцию Oracle nvl() только для строко- вых типов. Она возвращает указанное непустое значение, если заданный столбец имеет значение null Аргументы valuei: столбец или выражение, включающее столбец. VALUE2: выражение или константа, которая будет возвращаемым значени- ем, если valuei дает null Возвращаемое значение Непустое значение. Если valuei не является null, то оно будет возвра- щаемым значением; иначе будет возвращаться valuei. Если и valuei, и VALUE2 имеют значение null, то будет возвращено значение null Замечания Логически эта функция эквивалентна простой форме функции coalesce () версии 1.5, когда она используется со столбцом символьного типа, а именно COALESCE(VALUEI, VALUE2). Она должна рассматриваться как нерекомендуемая для Firebird 1.5 и выше Пример Следующий оператор вычисляет и выводит столбец времени выполнения BIrthjtear для каждого студента. Если это значение вычисляется в null, то вместо дня рождения выводится ’Not known’ (Не известно): SELECT FIRST_NAME, LASTJUAME, SNVL(CAST(EXTRACT(YEAR FROM BIRTH_DATE) AS VARCHAR (9) ) , ’ Not known ’) AS BIRTH_YEAR FROM STUDENT_REGISTER; Связанные или похожие функции См. также iNVL(), sNullif (), внутреннюю функцию coalesce( )
908 Приложение 1 Математические функции IB_UDF ! ABS(VALUE) Linux, Win32 i Возвращает абсолютное значение числа Аргументы i value является столбцом или выражением, которое совместимо с типом i DOUBLE PRECISION Возвращаемое i ЧИСЛО DOUBLE PRECISION значение Пример i Этот оператор суммирует все отрицательные значения и возвращает итог в ( виде положительного числа: : SELECT ABS(SUM(ASSET_VALUE)) AS LIABILITY i FROM ASSET_REGISTER i' WHERE ASSET_VALUE < 0; Похожие i См. другую внешнюю функцию f DoubleAtos (), которая выполняет те же функции = действия IB_UDF j BINJUSD(VALUE1, VALUE2) Linux, Win32 Возвращает результат побитовой операции and (И), выполняемой с двумя i входными значениями Аргументы i valuei и value?, являются столбцами или выражениями, которые преобра- ; ЗУЮТСЯ К ТИПу SMALLINT ИЛИ INTEGER Возвращаемое iЗначение integer значение Пример (SELECT BIN—AND(128,24) AS ANDED_RESULT ( FROM RDB $ DATABASE; j BIN_0R(VALUEI, VALUE2) Linux, Win32 ? Возвращает результат двоичной (побитовой) операции or (ИЛИ), выпол- ; няемой с двумя входными значениями Аргументы j valuei и value2 являются столбцами или выражениями, которые преобра- ? ЗуЮТСЯ К типу SMALLINT ИЛИ INTEGER Возвращаемое ( Значение integer значение Пример (SELECT BIN_0R(128, 24) AS ORED__RESULT : FROM RDB$DATABASE; ib_udf jBIM_XOR( VALUEI, VALUE2) Linux, Win32 i Возвращает результат двоичной (побитовой) операции xor (исключающее ИЛИ), выполняемой с двумя входными значениями
Список внешних функций 909 Аргументы i valuei и values являются столбцами или выражениями, которые преобра- : зуются К типу SMALLINT ИЛИ INTEGER Возвращаемое значение i Значение integer Пример (SELECT BIN_XOR(128,24) AS EXORED_RESULT i FROM RDBSDATABASE; TB.UDF j CEILING[VALUE; Linux, Win32 Возвращает значение типа double precision, представляющее наимень- i шее целое, оильшее или равное входному значению Аргументы value является столбцом или выражением, которое дает число типа double j PRECISION Возвращаемое значение i Число double precision с нулевым количеством дробных знаков Пример ; SELECT CEILING (LAST_TOTAL) AS ROUND_UP_NEAREST • FROM SALESJ1ISTORY; illlliilie : DIV(VALUE1, VALUES; Linux, Win32 i Делит два целых числа и возвращает частное, отбрасывая дробную часть Аргументы i valuei и value2 являются столбцами или выражениями, которые преобра- i зуются к числам типа smallint или integer Возвращаемое значение i Число double precision с нулевым количеством дробных знаков Пример ° SELECT DIV (TERM, (CURRENT_DATE - START__DATE) / 365) AS ( YEARS_REMAINING i FROM MORTGAGE__ACCOUNT i WHERE ACCOUNT_ID = 12345; I DPOWER!VALUE, EXPONENT) Linux, Win32 ; Получает число и показатель степени и возвращает степень числа Аргументы i exponent является столбцом или выражением, которое дает число типа double precision Возвращаемое значение j Возвращает степень числа в виде double precision Пример (SELECT DPOWER(2.64575,2) AS NEARLYJ7 i FROM RDBSDATABASE; Связанные или j См. также sort () похожие функции (
910 Приложение 1 FREEUDFLIB I FJXXJBLEABS(VALUE; Win32 i Возвращает абсолютное значение числа с плавающей точкой Аргументы i value является столбцом или выражением, которое преобразуется в число ( типа DOUBLE PRECISION ИЛИ FLOAT Возвращаемое значение i Положительное число типа double precision Пример (SELECT ABS(SUM(ASSET-VALUE)) AS LIABILITY i FROM ASSET_REGISTER iWHERE ASSET_VALUE < 0; Связанные или похожие функции i См. также другую внешнюю функцию abs (), которая выполняет ту же самую i задачу и доступна не только на платформах Windows FREEUDFLIB j FJSDVISIBLEBY (VALUEI, VALUE2) Win32 i Определяет, является ли значение нацело делимым на другое значение (то j есть является ли VALUE2 делителем valuei). Возвращает 1, если числитель i (valuei) нацело делится на знаменатель (value2); иначе возвращает 0 Аргументы i valueI: столбец или выражение, результатом вычисления которого являет- i ся целый тип — число, которое будет делиться (числитель). i VALUE2: другое целое — число, используемое в качестве знаменателя Возвращаемое значение ( Возвращает 1, если истинно, 0, если ложно Пример ° Эта функция может быть вызвана из ограничения check, как в следующем | примере: ( ALTER TABLE ORDER-DETAIL i ADD CONSTRAINT CHECK—MULTIPLE i CHECK (ISDIVISIBLEBYIORDER-QTY, PACK-QTY) = 1); ( Подобная проверка может быть использована в случае, когда для расфасо- ; ванных элементов указана цена за единицу, а не за расфасовку -FREEUDFLIB Win32 FJ4ODULO(VALUEI, VALUE2) i Функция возвращает остаток отделения двух целых чисел Аргументы ; valuei и VALUE2 являются столбцами или выражениями, которые вычисля- ются в числа типа smallint или integer Возвращаемое значение ( Число целого типа Замечания ; Эта версия функции получения остатка от деления может быть более по- i лезной, чем mod(), в сложных выражениях, где возвращаемое значение : является частью другого выражения, которое оперирует целыми числами, i (mod () возвращает число с плавающей точкой.)
Список внешних функций 911 Пример ! Фрагмент из триггера: L..IF (MODULO (NEW. HOURS * 100, 775) > 0.25) THEN i NEW.OVERTIME_HOURS = MODULO(NEW.HOURS * 100, 775) / 100; Связанные или ) См. также функцию mod (), которая возвращает число с плавающей точкой похожие функции FREEUDFLIB I F_ROUNDFLOAT <VALUEI, VALUES) Win32 Округляет передаваемое значение до ближайшего значения, которое боль- i ше или меньше заданной дробной части Аргументы i valuei: столбец или выражение, результатом вычисления которого являет- i ся тип с плавающей точкой. Это округляемое число. i VALUE2: столбец или выражение, результатом вычисления которого являет- i ся тип с плавающей точкой. Это должно быть число меньше 1 и больше 0. i Например, передайте значение 0.25 для округления до ближайшей четверти Возвращаемое : Число с плавающей точкой, которое является valuei, округленным до бли- значение ( жайшей дробной части VALUE2 Пример ; Следующий оператор вычисляет paid_hours, округляя hours_worked до i ближайшей четверти часа: i UPDATE timesheet i SET PAID_HOURS = F_ROUNDFLOAT(HOURS_WORKED, 0.25) i WHERE DATE_TIMESTAMP > CURRENT_DATE - 7; Связанные или i См. также round () похожие функции FREEUDFLIB Win32 I FJRUNCATE (VALUE) i Усекает число с плавающей точкой до целого Аргументы i value является столбцом или выражением, результатом вычисления кото- j рого является число с плавающей точкой Возвращаемое значение =: Убирает дробную часть у value и возвращает целую часть в виде целого типа Пример : Следующий оператор вернет целое число: i SELECT FJTRUNCATE (SUM (AMT_OUTSTANDING)) AS i ESTIMATED_DEBTORS i FROM ACCOUNT (WHERE AMT_OUTSTANDING > 0; Связанные или похожие функции i См. также truncate () и round () ib_udf I FLOOR(VALUE) Linux, Win32 i Возвращает значение с плавающей точкой, представляющее наибольшее i целое, меньшее или равное value
912 Приложение 1 Аргументы ; value является столбцом или выражением, вычисляемым в число типа i DOUBLE PRECISION Возвращаемое значение i Число типа double precision с нулевой дробной частью Пример : SELECT FLOOR (CURRENT_DATE - START_DATE) AS j DAYS__ELAPSED i FROM DVD_LOANS; XBJTOF Linux, Win32 j LN(VALUE) i Возвращает натуральный логарифм числа Аргументы i value является столбцом или выражением, вычисляемым в число типа j DOUBLE PRECISION Возвращаемое значение i Число типа DOUBLE PRECISION Пример i SELECT LN ( (CURRENT_TIMESTAMP - LEASE_DATE) / 7) AS i NLOG_WEEKS i FROM LEASE_ACCOUNT; XBJW VALUE2) Linux, Win32 i Возвращает логарифм по основанию х = valuei числа у = VALUE2 Аргументы i valuei (основание логарифма) и VALUE2 (число, с которым проводится опе- i рация) являются столбцами или выражениями, вычисляемыми в числа типа ( DOUBLE PRECISION Возвращаемое значение i Число типа double precision Замечания i Firebird 1.0.x и версии InterBase в этой функции имеют ошибку: log(x,y) оши- i бочно инвертирует аргументы и возвращает логарифм по основанию у чис- i ла х. Это было исправлено в версии 1.5. Имейте в виду, что существующие i хранимые процедуры и код приложения могут иметь корректировки для этой i ошибки Пример i SELECT LOG (8, (CURRENT__TIMESTAMP - LEASE_DATE) /7)) AS ( LOG__WEEKS i FROM LEASE_ACCOUNT; IBJJDF j LOG1U (VALUE) Linux, Win32 i Возвращает десятичный логарифм входного числа Аргументы i value является столбцом или выражением, вычисляемым в число типа I DOUBLE PRECISION Возвращаемое значение | Число типа DOUBLE PRECISION
Список внешних функций 913 Пример (SELECT LOGIC ( (CURRENT_TIMESTAMP - LEASE_DATE) / 1) AS i LOG10_WEEKS j FROM LEASE_ACCOUNT; IBJJDF (MODELO (VALUEI, VALUE2) Linux, Win32 i Функция возвращает остаток отделения двух целых чисел Аргументы i valuei и value2 являются столбцами или выражениями, вычисляемыми в i числа типа smallint или integer Возвращаемое значение j Число типа double precision Пример i Фрагмент триггера: i ...IF (MODULO(NEW.HOURS * 100, 775) > 25.0) THEN : NEW.OVERTIME HOURS = MODULO(NEW.HOURS * 100, 775) / 100; Связанные или i См. также функцию f Modulo (), которая возвращает целое число похожие функции i IB_UDF \ ₽1 () Linux, Win32 Возвращает значение тг = 3.14159... Аргументы i Аргументы отсутствуют, однако скобки требуются Возвращаемое значение | Значение л в виде числа double precision Пример i SELECT PI () AS PI JZALUE i FROM RDB$DATABASE; IBJJDF Linux, Win32 i Возвращает случайное число в диапазоне между 0 и 1. Заметьте, что эта I функция не работает в Firebird 1.5 Аргументы i Аргументы отсутствуют, но скобки требуются Возвращаемое значение I Число типа DOUBLE PRECISION Замечания i В настоящее время используется для установления начального значения генератора Пример ! SELECT RAND() AS RANDOM_NUMBER j FROM RDB$DATABASE; FB UDF fROUND (VALUE) Linux, Win32 / Округляет число с фиксированной точкой до ближайшего целого
914 Приложение 1 Аргументы i value является столбцом или выражением, вычисляемым в число с фикси- ! рованной точкой с дробными разрядами >0 Возвращаемое значение i Число целого типа Замечания | Это полное округление. Если цифра сразу после десятичной точки больше i или равна 5, то к цифре перед десятичной точкой добавляется единица, и у i числа отсекаются дробные цифры. В противном случае просто отсекаются | все дробные цифры Пример i Следующий оператор составляет смету на основе результата округления i произведения двух чисел numeric(11,2): iSELECT JOB_NO, ROUND(RATE * HOURS) + 1 AS ESTIMATE j FROM QUOTATION ( WHERE RATE IS NOT NULL AND HOURS IS NOT NULL; Связанные или похожие функции i См. также truncate (), f_roundfloat () iSIGN(VALUE) Linux, Win32 i Возвращает 0, 1 или -1 в зависимости от того, является ли входное значе- ; ние положительным, нулем или отрицательным соответственно Аргументы ! value является столбцом или выражением, вычисляемым в число типа DOUBLE PRECISION Возвращаемое значение i Число типа DOUBLE PRECISION Пример i Фрагмент триггера: (...IF (SIGN(NEW.CURRENT_VALUE) < 1) THEN . . .; IBJUDF i SQRT(VALUE) Linux, Win32 i Возвращает квадратный корень числа Аргументы i value является столбцом или выражением, вычисляемым в число типа jDOUBLE PRECISION Возвращаемое значение i Число типа double precision Пример i Фрагмент триггера: i...IF (SQRT(NEW.HYPOTENUSE) = SQRT(NEW.SIDE1) + i SQRT(NEW.SIDE2)) THEN i NEW.RIGHT_ANGLED_TRIANGLE = 'T'; FBUDF (TRUNCATE (VALUE) Linux, Win32 i Усекает тип с фиксированной точкой до следующего меньшего целого
Список внешних функций 915 Аргументы value является столбцом или выражением, вычисляемым в число с фиксированной точкой с дробными разрядами > 0 Возвращаемое значение Число целого типа Замечания Как и для некоторых других функций из этой библиотеки, вам нужно два объявления, чтобы использовать 32- и 64-битовый вход. Проверьте объяв- ления в скрипте fdudf.sql truncate и i64truncate Пример Следующий оператор составляет смету на основе результата усечения произведения двух чисел numeric (11,2): SELECT JOB_NO, TRUNCATE (RATE * HOURS) + 1 AS ESTIMATE FROM QUOTATION WHERE RATE IS NOT NULL AND HOURS IS NOT NULL; Связанные или похожие функции См. также round (), fjtruncate () Функции даты и времени FBUDF DOW(VALUE) Linux, Win32 Получает тип timestamp и возвращает день недели (на английском языке) в качестве строки со смешанными строчными и прописными буквами Аргументы value— столбец или выражение, результатом вычисления которого явля- ется тип timestamp Возвращаемое значение 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' или ' Sunday' Пример Этот оператор добавляет четыре дня и возвращает день недели для на- строенной даты: SELECT DOW (CURRENT__DATE + 4) FROM RDBSDATABASE; Связанные или похожие функции См. также sdow (), внешнюю функцию extract () FBUDF SDOW(VALUE) Linux, Win32 Получает тип timestamp и возвращает день недели (на английском языке) в качестве аббревиатуры со смешанными строчными и прописными буквами Аргументы value — столбец или выражение, результатом вычисления которого явля- ется ТИП TIMESTAMP Возвращаемое значение 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ИЛИ 'Sun'
916 Приложение 1 Пример Этот оператор добавляет четыре дня и возвращает день недели для на- i строенной даты: iSELECT SDOW(CURRENT_DATE + 4) i FROM RDB$DATABASE; Связанные или похожие функции i См. также Dow (), внешнюю функцию extract () FBUDF i ADDDAY(VALUEI; VALUE2) Linux, Win32 | Добавляет полное число дней к типу даты или времени и возвращает эту i измененную дату в виде timestamp Аргументы i valuei: столбец или выражение, результатом вычисления которого являет- i ся тип даты или времени. i VALUE2: количество добавляемых дней (целое число) или целое выражение Возвращаемое значение i Настроенное timestamp (эквивалентно valuei + VALUE2) Замечания ; Если входом является тип time, то дни будут добавлены к этому времени i дня текущей даты. Если это тип date, то время дня будет полночью Пример i Следующий оператор прибавляет 4 дня к текущей дате и возвращает полу- i ченную дату и время 0 часов 0 минут 0 секунд: i SELECT ADDDAY(CURRENT__DATE, 4) i FROM RDB$DATABASE; Связанные или похожие функции i См. также addhour(), addminute () и др. FBDDF = ADDHOUR (VALUEI, VALDE2) Linux, Win32 j Добавляет полное количество часов к типу даты или времени и возвращает ; измененную дату в виде типа timestamp Аргументы i valuei: столбец или выражение, результатом вычисления которого являет- i ся тип даты или времени. 1 VALUE2: количество добавляемых часов (целое число) или целое выражение Возвращаемое значение i Настроенное значение timestamp : (эквивалентно valuei + (values / 2)) Замечания i Если входное значение имеет тип time, то часы добавляются к этому вре- i мени для текущей даты. Если это тип date, то время дня будет полночью Пример i Следующий оператор добавляет 10 часов и возвращает измененную дату и i время: i SELECT ADDHOUR(CURRENT_TIMESTAMP, 10) FROM RDBSDATABASE; Связанные или похожие функции i См. также addday (), addminute о и t. д.
Список внешних функций 917 FBUDF ; ADDMILLISECOND (VAUJEl, VALUE2) Linux, Win32 j Добавляет полное количество миллисекунд к типу даты или времени и воз- ; вращает измененную дату в виде типа timestamp Аргументы j valuei: столбец или выражение, результатом вычисления которого являет- j ся тип даты или времени. ; VALUE2: количество добавляемых миллисекунд (целое число) или целое : выражение Возвращаемое значение i ТИП TIMESTAMP Замечания i Если входное значение имеет тип time, то миллисекунды добавляются к i этому времени для текущей даты. Если это тип date, то время дня будет [ полночью Пример : Следующий оператор добавляет 61 234 миллисекунды к текущему систем- : ному времени типа timestamp: j SELECT ADDMILLISECOND (CURRENT_TIME, 61234) i FROM RDB?DATABASE; Связанные или похожие функции i См. также addday (), addminute () и т. д. FBUDF ) ADDMINUTE(VALUEI, VALUE2) Linux, Win32 ; Добавляет полное количество минут к типу даты или времени и возвращает = измененную дату в виде типа timestamp Аргументы ; valuei: столбец или выражение, результатом вычисления которого являет- : ся тип даты или времени. i VALUE2: количество добавляемых минут (целое число) или целое выраже- 5 ние Возвращаемое значение i ТИП TIMESTAMP Замечания i Если входное значение имеет тип time, то минуты добавляются к этому i времени для текущей даты. Если это тип date, то время дня будет пол- i ночью Пример i Следующий оператор добавляет 45 минут к текущему системному времени ! типа timestamp: i SELECT ADDMINUTE(CURRENT_TIME, 45) i FROM RDB$DATABASE; Связанные или похожие функции i См. также addday (), addsecond () и т. д. • FBUDF | ADDMONTH (VALUEI, VAWE2) Linux, Win32 Добавляет полное количество месяцев к типу даты или времени и возвра- ! щает измененную дату в виде типа timestamp
918 Приложение 1 Аргументы valuei: столбец или выражение, результатом вычисления которого являет- ся ТИП TIMESTAMP. values: количество добавляемых месяцев (целое число) или целое выра- жение Возвращаемое значение Тип timestamp, календарный месяц более поздний, чем valuei Замечания Если входное значение имеет тип time, то месяцы добавляются к этому времени дня для текущей даты. Если это тип date, то время дня будет пол- ночью Пример Следующий оператор использует addmonth () для вычисления даты завер- шения контракта: UPDATE CONTRACT SET FINAL_DATE = CASE CONTRACTJTERM WHEN 'HALF-YEARLY' THEN ADDMONTH(START_DATE, 6) WHEN 'YEARLY' THEN ADDMONTH(START_DATE, 12) ELSE ADDWEEK(START_DATE, TRUNCATE(CONTRACT_AMT/WEEKLY_FEE)) END WHERE START_DATE IS NOT NULL AND AMT_PAID IS NOT NULL AND WEEKLY_FEE IS NOT NULL AND CONTRACT_ID = 12345; Связанные или похожие функции См. также addday (), addyear () и т. д. FBUDF ADDSECONDfVALUEI , VALUE2) Linux, Win32 Добавляет полное количество секунд к типу даты или времени и возвраща- ет измененную дату в виде типа timestamp Аргументы valuei: столбец или выражение, результатом вычисления которого являет- ся тип даты или времени. VALUE2: количество добавляемых секунд (целое число) или целое выраже- ние Возвращаемое значение Тип TIMESTAMP Замечания Если входное значение имеет тип time, то секунды добавляются к этому времени для текущей даты. Если это тип date, то время дня будет пол- ночью Пример Следующий оператор добавляет 120 секунд к текущей системной дате: SELECT ADDSECOND (CURRENT__DATE, 120) FROM RDB$DATABASE;
Список внешних функций 919 Связанные или похожие функции См. также addmonth (), addmillisecond о и т. д. FBUDF Linux, Win32 ADDWEEK(VALUEI, VALUE2) Добавляет полное количество недель к типу даты или времени и возвраща- ет измененную дату в виде типа timestamp Аргументы valuei: столбец или выражение, результатом вычисления которого являет- ся тип даты или времени. value2: количество добавляемых недель (целое число) или целое выраже- ние Возвращаемое значение Настроенное значение timestamp (эквивалентно valuei + (7 * VALUE2)) Замечания Если входное значение имеет тип time, то недели добавляются к этому времени для текущей даты. Если это тип date, то время дня будет пол- ночью Пример Следующий оператор вычисляет количество недель выплаты гонорара и использует это в addweekO для вычисления конечной даты контракта: UPDATE CONTRACT SET FINAL_DATE = ADDWEEK(START_DATE, TRUNCATE (CONTRACT_AMT/WEEKLY__FEE) ) WHERE START_DATE IS NOT NULL AND AMT_PAID IS NOT NULL AND WEEKLY_FEE IS NOT NULL AND CONTRACTED = 12345; Связанные или похожие функции См. также addday (), addmonth () и т. д. тают Linux, Win32 ADDYEAR(VALUEI, VALUE2) Добавляет полное количество лет к типу даты или времени и возвращает измененную дату в виде типа timestamp •Аргументы valuei: столбец или выражение, результатом вычисления которого являет- ся тип TIMESTAMP. value2: количество добавляемых лет (целое число) или целое выражение Возвращаемое значение ТИП TIMESTAMP Замечания Если входное значение имеет тип time, то годы добавляются к этому вре- мени дня для текущей даты. Если это тип date, то время дня будет пол- ночью Пример Следующий оператор вычисляет конечную дату аренды, получая начальную Дату:
920 Приложение 1 i UPDATE LEASE i SET FINAL_DATE = ADDYEAR (START-DATE, 5) i WHERE START-DATE IS NOT NULL i AND LEASE_ID = 12345; Связанные или похожие функции i См. также addday (), addmonth () и т. д. FBUDF i i GETEXACTTIMESTAMP() Linux, Win32 Возвращает системное время в виде timestamp с точностью до ближайших ; миллисекунд Аргументы i Нет аргументов Возвращаемое значение i ТИП TIMESTAMP Замечания i Контекстная переменная даты и времени currentjtimestamp и предвари- i тельно определенный литерал 'NOW возвращают системное время только с i точностью до ближайшей секунды, getexacttimestamp!) в настоящий мо- i мент является единственным способом получить точное системное время Пример i Следующий оператор возвращает точное время и дату: i select GETEXACTTIMESTAMP() AS TSTAMP i FROM RDBSDATABASE; EREEUDFLIB Linux, Win32 i Вычисляет возраст в днях для даты (valuei) при ссыпке на другую дату i (values). Обычно дата, на которую ссылаются, является текущей датой, но i это не обязательно должна быть она. Например, вы можете найти время, i которое прошло с некоторого другого события Аргументы = valuei: столбец или выражение, результатом вычисления которого являет- [ ся значение типа date или timestamp. i VALUE2: столбец или выражение, результатом вычисления которого являет- := ся значение типа date или timestamp Возвращаемое значение i Положительное или отрицательное число типа integer Пример i SELECT FIRST__NAME, LAST-NAME, i F_AGEINDAYS(DATE_OF_BIRTH, CURRENT_DATE) AS i CURRENT—AGE i FROM MEMBERSHIP i WHERE DATE—OF—BIRTH < CURRENT_DATE - (5 * 365); Связанные или похожие функции i См. также F_AgeInDaysThreshold()
Список внешних функций 921 EREEUDELIB 1 - 4 Win32 \ F_AGEIHDAYSTHRESHOLD(VALUE 1, VALUE 2, MINVALUE, Вычисляет возраст в днях для даты (valuei) при ссылке на другую дату (VALUE2) и возвращает не это значение, а указанное минимальное число, которое меньше вычисленного значения или заданного максимального чис- ла, которое больше вычисленного значения Аргументы valuei: столбец или выражение, результатом вычисления которого являет- ся значение типа date или timestamp. VALUE2: столбец или выражение, результатом вычисления которого являет- ся значение типа date или timestamp. Это не обязательно должна быть дата, меньшая чем valuei Minvalue: целое число. Это возвращаемое значение, если вычисленное значение будет меньше. Установите в 0, если вам нужно использовать только MAXVALUE. USEMIN: Установите в 1, если вам нужно использовать только minvalue. Установите usemax в 0, если вам нужно использовать только minvalue. maxvalue: целое число. Это возвращаемое значение, если вычисленное значение будет больше. Установите в 0, если вам нужно использовать только minvalue. usemax: целое число. Установите в 1, если вам нужно использовать maxvalue. Установите usemin в 0, если вам нужно использовать только MAXVALUE Возвращаемое значение Число типа INTEGER Замечания Если последние четыре аргумента установлены в 0, эта функция работает так же, как и f_agethreshold Пример Следующий оператор вернет список, показывающий все счета с датами обязательства, меньше, чем 30 дней тому назад, вместе просроченными на 29 дней обязательствами и все с датами обязательства, более ранними, чем 90 дней тому назад, и просроченными обязательствами на 90 дней. Все остальные будут показаны с их фактическими сроками: SELECT ACCOUNT_NAME, F-AGEINDAYSTHRESHOLD(DUE_DATE, CURRENT_DATE, 29, 1, 90, 1) AS OVERDUE_AGE FROM ACCOUNT; Связанные или похожие функции | См. также ageindays () FREEUDFLIB i F_AGSIIMONTHS (VALUEI, VALUE2) Win32 Вычисляет возраст в месяцах для даты (valuei) при ссылке на другую дату (VALUE2). Обычно дата, на которую ссылаются, является текущей датой, но это не обязательно должна быть она. Например, вы можете найти время, которое прошло с некоторого другого события
922 Приложение 1 Аргументы VALUEI: столбец или выражение, результатом вычисления которого являет- ся значение типа date или timestamp. VALUE2: столбец или выражение, результатом вычисления которого являет- ся значение типа date или timestamp. Не обязательно должно быть более ранней датой, чем valuei Возвращаемое значение Положительное или отрицательное число типа integer Пример SELECT ACCOUNT_NAME, F_AGEINMONTHS (DUE_DATE, CURRENT JIATE) AS OVERDUE_AGE FROM ACCOUNT WHERE ACCT_BALANCE > 0 AND DUE_DATE < CURRENT_DATE - (6 * 7); Связанные или похожие функции См. также f_ageinmonthsthreshold() freeudflib F_AGEINMCWTHSTHRESHOLD(VALUE X, VALUE 2t MXKVAWE, USEMIU, MAXVALUE, USEMAX) Win32 Работает для F_AgelnMonths также, как и F_AgeInDaysThreshoLd работа- ет ДЛЯ F__AGEINDAYS Аргументы F_AgeInDays FREEUDFLIB F_AGEXNWEEKS(VALUEI, VALUE2) Win32 Вычисляет возраст в неделях для даты (valuei) при ссылке на другую дату (VALUE2). Обычно дата, на которую ссылаются, является текущей датой, но это не обязательно должна быть она. Например, вы можете найти время, которое прошло с некоторого другого события Аргументы valuei: столбец или выражение, результатом вычисления которого являет- ся значение типа date или timestamp. VALUE2: столбец или выражение, результатом вычисления которого являет- ся значение типа date или timestamp. Не обязательно должно быть более ранней датой, чем valuei Возвращаемое значение Положительное или отрицательное число типа integer Пример SELECT ACCOUNT_NAME, F_AGEINWEEKS (DUEJIATE, CURRENTJIATE) AS OVERDUE_AGE FROM ACCOUNT WHERE ACCT_BALANCE > 0 AND DUE_DATE < CURRENT_DATE - (366/2); Связанные или похожие функции См. также f_ageinweeksthreshold ()
Список внешних функций 923 FREEUDFLIB Win32 : F_AGE WEEKSTHRESHOLD (VALVE 1, VALUE 2, i MOTZALUE, I)SM( MAXVALUE, USEMAX) i X ( Работает для F AGEINWEEKS так же, как и F AgelnDaysThreshold работает i ДЛЯ F__AGEINDAYS Мши ; F_CMOUTHLORG(VALUE) Win32 i Получает timestamp или date и возвращает название месяца (на англий- i ском языке) в виде строки Аргументы value: столбец или выражение, результатом вычисления которого является 5 значение типа date или timestamp Возвращаемое 1 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', значение iSeptember’, ’October', ’November' или 'December' Пример i Следующий оператор добавляет 40 дней к текущей дате и возвращает на- i звание месяца полученной даты в виде строки: (SELECT F_CMONTHLONG(CURRENT_DATE + 40) i FROM RDBSDATABASE; Связанные или i См. также F cmonthshort (), sdow (), внешнюю функцию extract () похожие функции FREEUDFLIB 1 F_CMONTHSHOKJ (VALUE) Win32 i Получает timestamp или date и возвращает сокращенное трехсимвольное i название месяца (на английском языке) в виде строки FREEUDFLIB j FJffiXDATE(VALUEI, VM.UE2) Win32 ( Получает два значения или выражения типа даты или времени и возвраща- i ет более позднюю из них в виде даты и времени (timestamp в диалекте 3, i date в диалекте 1) Аргументы \ valuei: столбец или выражение, результатом вычисления которого являет- i ся значение типа date или timestamp. ? value2: столбец или выражение, результатом вычисления которого являет- j ся значение типа date или timestamp Возвращаемое (Наиболее поздняя дата или выражение даты, преобразованное в тип значение i TIMESTAMP FREEUDFLIB ( Е_МИЯ1ЖГЕ (VALUEI, VALUE2) Win32 i Получает два значения или выражения типа даты или времени и возвраща- i ет более раннюю из них в виде даты и времени (timestamp в диалекте 3, i date в диалекте 1)
924 Приложение 1 Аргументы е valuei: столбец или выражение, результатом вычисления которого являет- ; ся значение типа date или timestamp j VALUE2: столбец или выражение, результатом вычисления которого являет- ; ся значение типа date или timestamp Возвращаемое i Наиболее ранняя дата или выражение даты, преобразованное в тип значение ; TIMESTAMP FREEUDELXB | F_QIJARTER(VALUE) Win32 i Получает значение или выражение типа даты или времени и возвращает = квартал года Аргументы j value: столбец или выражение, результатом вычисления которого является i значение типа date или timestamp Возвращаемое । Возвращает 1 для месяцев с января по март, 2 для апреля — июня, 3 для значение : июля — сентября и 4 для октября — декабря FREEUDELXB [ FJJOY (VALUE) Win32 i Возвращает строку, являющуюся конкатенацией года и номера недели в i году Аргументы i value: столбец или выражение, результатом вычисления которого является ! значение типа date или timestamp Возвращаемое i Возвращает целое число, представляющее цифры года, за которыми идут значение i два символа, представляющие неделю года Пример ; В следующем примере возвращается 200313: i SELECT W0Y('26.03.2003') i FROM ROBSDATABASE; Строковые и символьные функции ИВИ||Д f ASCII_CHAR (VALUE) Linux, Win32 i Возвращает символ ASCII, соответствующий переданному ей десятичному i значению Аргументы i value является столбцом, константой или выражением типа smallint или !INTEGER Возвращаемое i Однобайтовый печатный или непечатный символ значение Пример i Следующий оператор добавит в столбец символ перевода строки и возвра- та каретки для каждой строки внешней таблицы: i UPDATE EXT_FILE SET EOL = ASCII^CHAR(13) || ASCII_CHAR(10);
Список внешних функций 925 ЯЯяЯЯЯЯЯЯЯ Linux, Win32 ) ASCIIJ/AL(VALUE) = Возвращает значение ASCII, соответствующее переданному ей дёсятйчно- j му значению Аргументы value является столбцом, константой или выражением типа char Возвращаемое значение ; Целое десятичное значение ASCII Пример i SELECT ASCII_VAL('&') AS ASC_NUM i FROM RDB$DATABASE; . \ LOWER (VALUE) Linux, Win32 j Возвращает входную строку в виде символов в нижнем регистре. Работает ( только с символами ASCII Аргументы \ value является столбцом или выражением, результатом вычисления кото- j рого является значение типа строки ASCII длиной 32 765 байт или меньше Возвращаемое значение i cHAR(n) или varchar(п) того же размера, что и входная строка Замечания i Эта функция может получать и возвращать до 32 767 символов, ограниче- i ние на символьные строки Firebird Пример i Следующий оператор вернет строку 'come and sit at ту table': (SELECT LOWER('Come and sit at MY TABLE') AS L_STRING ( FROM RDB$DATABASE; (LPAD< VALUE, LENGTH, IN_CHAR) Linux, Win32 Присоединяет указанный символ in_CHARk началу входной строки value до е тех пор, пока длина результирующей строки не станет равной заданному ? ЧИСЛУ LENGTH Аргументы ; value: столбец или выражение, результатом вычисления которого является i значение типа строки не длиннее, чем (32767 - length) байт. i length: константа или выражение типа целого. iN_char: один символ, который используется в качестве символа заполни- ; теля Возвращаемое значение ( cHAR(n) или VARCHAR(n), где л имеет значение входного аргумента length Замечания i Эта функция может получать и возвращать до 32 767 символов, ограниче- ( ние на символьные строки Firebird Пример ( Следующий оператор вернет строку ' ##########rhubarb ': (SELECT LPAD('RHUBARB', 17, '#') AS LPADDED_STRING ( FROM RDB$DATABASE;
926 Приложение 1 Связанные или похожие функции См. также RPAD () IBJ3DF Linux, Win32 LTRIM(VALUE) Удаляет начальные пробелы из входной строки Аргументы value — столбец или выражение, результатом вычисления которого явля- ется значение типа строки длиной не более 32 767 байт Возвращаемое значение CHAR(n) или varchar(п) без начальных пробельных символов Замечания Эта функция может принимать 32 765 байт, включая пробельные символы, ограничение для символьных строк Firebird Пример Следующий фрагмент триггера before insert будет удалять начальные пробелы из входной строки: NEW.CHARACTER_COLUMN = LTRIM(NEW.CHARACTER_COLUMN) ; Связанные или похожие функции См. также rtrim (), f_rtrim(), f_lrtrim () FBUDF Linux, Win32 BRIGHT (VALUE, LENGTH) Возвращает подстроку для value, являющуюся правой частью строки value ДЛИНОЙ LENGTH СИМВОЛОВ Аргументы Нет Возвращаемое значение value: столбец или выражение, результатом вычисления которого является значение типа строки длиной не более 32 767 байт. length: константа или выражение типа целого Замечания Эта функция может принимать 32 765 байт, ограничение для символьных строк Firebird Пример Следующий оператор вернет строку 'fox jumps over the lazy dog’: SELECT SRIGHT('The quick brown fox jumps over the lazy dog.', 28) AS RJSTRING FROM RDB$DATABASE; Связанные или похожие функции См. также substr(), substrlen (), внутреннюю функцию substring () IB_UDF Linux, Win32 №AD (VALUE, LENGTH, IN_CHAR) Присоединяет указанный символ in char к концу входной строки value до тех пор, пока длина результирующей строки не станет равной заданному числу LENGTH
Список внешних функций 927 Аргументы value: столбец или выражение, результатом вычйсления которого является значение типа строки не длиннее, чем (32765 - length) байт. length: константа или выражение типа целого. in_char: один символ, который используется в качестве символа заполни- теля Возвращаемое значение CHAR(n) или VARCHAR(n), где п имеет значение входного аргумента length Замечания Эта функция может получать и возвращать до 32 765 символов, ограниче- ние на символьные строки Firebird Пример Следующий оператор вернет строку ' Framboise*********** ’: SELECT READ('Framboise, 20, ' * ' ) AS RPADDED__STRING FROM RDBSDATABASE; Связанные или похожие функции См. также lpado liiiiHllllllllllill Linux, Win32 RTRIM (VALUE; Удаляет конечные пробелы из входной строки Аргументы value— столбец или выражение, результатом вычисления которого явля- ется значение типа строки длиной не более 32 765 байт Возвращаемое значение CHAR(n) или VARCHAR(n) без конечных пробельных символов Замечания Эта функция может принимать 32 765 байт, включая пробельные символы, ограничение для символьных строк Firebird Пример Следующий фрагмент триггера before insert будет удалять конечные пробелы из входной строки: NEW.CHARACTER_COLUMN = RTRIM(NEW.CHARACTER_COLUMN) ; Связанные или похожие функции См. также ltrim(), f_lrtrim() IBJJDF Linux, Win32 STRLEN (VALUE) Возвращает длину строки Аргументы value — столбец или выражение, результатом вычисления которого явля- ется значение типа строки длиной не более 32 765 байт Возвращаемое значение Целое число, длина (счетчик) символов в строке Замечания Эта функция может принимать 32 765 байт, включая пробельные символы, ограничение для символьных строк Firebird
928 Приложение 1 Пример Следующий фрагмент модуля PSQL присваивает длину столбца локальной переменной: DECLARE VARIABLE LEN INTEGER; SELECT COLl, COL2, COL3 FROM ATABLE INTO :V1, :V2, :V3; LEN = STRLEN(V3); Связанные или похожие функции См. также substrlen() IBJJDF SUBSTR(VALUE, POSI, POS2) Linux, Win32 Возвращает строку, состоящую из позиций с posi по POS2 включительно. Если POS2 превышает длину строки, то функция вернет все символы с пози- ции posi до конца строки Аргументы value: столбец или выражение, результатом вычисления которого является значение типа строки. POSI, POS2: столбец или выражение, результатом вычисления которого является целый тип Возвращаемое значение Строка Замечания Если вы переносите приложение, написанное для InterBase, имейте в виду, что эта версия substr() отличается от реализованной в поставляемой Borland библиотеке ib_udf функции SUBSTRO, которая возвращает null, если POS2 выходит за пределы входной строки. Эта функция может принимать 32 765 байт, включая пробельные символы, ограничение для символьных строк Firebird Пример Следующий оператор убирает первые три символа у строки columnb и уста- навливает ее значение в строку, начинающуюся с позиции 4 и заканчиваю- щуюся позицией 100. Если строка заканчивается до позиции 100, результа- том будут все символы от позиции 4 до конца строки: UPDATE ATABLE SET COLUMNB = SUBSTR(COLUMNB, 4, 100) WHERE... Связанные или похожие функции См. также substrleno , rtrimo, внутреннюю функцию substring() Linux, Win32 SUBSTRLEN(VALUE, 3TARTPOS, LENGTH) Возвращает строку длиной length, начинающуюся с позиции startpos. Длина этой строки будет меньше, чем length, если начальная позиция плюс длина превышают длину исходной строки
Список внешних функций 929 Аргументы i value: столбец или выражение, результатом вычисления которого является i значение типа строки не длиннее 32 765 байт. i startpos: столбец или выражение, результатом вычисления которого i является целый тип. length: столбец или выражение, результатом вычисления которого являет- | ся целый тип Возвращаемое значение i Строка Замечания i Эта функция может принимать 32 765 байт, включая пробельные символы, i ограничение для символьных строк Firebird Пример \ Следующий оператор берет значение столбца и изменяет его, удаляя пер- \ вые три символа и удаляя после этого все конечные символы, если остав- \ шаяся часть строки длиннее 20 символов: ( UPDATE ATABLE i SET COLUMNS = SUBSTRLEN(COLUMNB, 4, 20) i WHERE... Связанные или похожие функции i См. также substro, rtrimo, внутреннюю функцию substring!) )F_CRLF() Win32 = Возвращает строку Windows возврат каретки/начало строки ASCII(13) || i ASCII(10) Аргументы | Нет Возвращаемое значение ) Строка в стиле языка С (завершается нулем) Пример i Следующий оператор добавляет Windows-маркеры конца строки в столбец i таблицы (например, во внешнюю таблицу для экспорта в другое приложе- i ние): i INSERT INTO EXTABLE(COLUMN1, COLUMN2, EOL) i VALUES (99, 'An item of data', CRLFO); FREEUDFEIB : F_FINDWORD (VALUE, W • Win32 i Начиная с указанной позиции n, возвращает слово, содержащее символ ! этой позиции и следующие символы вплоть до следующего символа пробе- i ла Аргументы i value: столбец или выражение, результатом вычисления которого является i значение типа строки. i n: целый тип, указывающий на позицию (начинающуюся с нуля) в строке, : где находится требуемое слово Возвращаемое значение i Строка 30 Зак. 420
930 Приложение 1 Замечания Рассматривайте строки, передаваемые в f_findword(), как массивы сим- волов, начинающиеся с индекса 0 Пример Следующий оператор возвращает слово 'pie1: SELECT F_FINDWORD('I never tasted pie like Mom used to make', 15) FROM RDBSDATABASE; Win32 Возвращает первые n символов из входной строки value Аргументы value: столбец или выражение, результатом вычисления которого является значение типа строки. n: целый тип, указывающий количество возвращаемых символов из левой части VALUE Возвращаемое значение Строка из N символов Пример Следующий оператор возвращает строку 'I never tasted pie': SELECT F_LEFT('I never tasted pie like Mom used to make', 18) FROM RDBSDATABASE; Связанные или похожие функции См. также substrlen (), substr(), внутреннюю функцию substring () FREEUDFLIB .. v Win32 WIDTH) Получая начальную позицию (startpos) в строке (value) и размер (width), возвращает часть строки value, начиная с позиции startpos, которая зани- мает пространство в width символов Аргументы value: столбец или выражение, результатом вычисления которого является значение типа строки. startpos: целый тип, указывающий на позицию (начинающуюся с нуля) в строке, где находится возвращаемая строка. width: целый тип, задающий ширину столбца (печатаемого/отображаемого), которую должна занимать возвращаемая строка Возвращаемое значение Строка из width (или меньше) символов Замечания Рассматривайте строки, передаваемые в f_linewrap (), как массивы сим- волов, начинающиеся с индекса 0 Пример Следующий оператор возвращает строку 'which, taken at the flood': SELECT F_WORDWRAP('There is a tide in the affairs of men which, taken at the flood, leads on to good fortune.', 38, 25) FROM RDBSDATABASE;
Список внешних функций 931 Win32 Удаляет из строки все начальные и конечные пробелы Аргументы value: столбец или выражение, результатом вычисления которого является значение типа строки Возвращаемое значение Строка с отсутствующими начальными и конечными пробелами Пример Следующий оператор возвращает строку 'lean and mean': SELECT F_LRTRIM(' lean and mean 1) FROM RDB$DATABASE; Связанные или похожие функции См. также ltrim (), rtrim () ЯЙТОЙ1В FJ’ROPERCASE(VALUE? . Win32 Преобразует строку в "правильный" вид, то есть берет каждое слово и за- меняет первый символ на эквивалент в верхнем регистре, переводя каждый другой символ слова в нижний регистр Аргументы value: столбец или выражение, результатом вычисления которого является значение типа строки Возвращаемое значение Строка, подобная следующей: 'Now is The Time For All Good Men To Come To The Aid Of The Party' Замечания Хотя эта функция и удобна для некоторых задач, она имеет ряд ограниче- ний. В частности, не может правильно трактовать такие имена, как dOliveira, OHalloran или MacDonald Пример Следующий оператор вернет строку 'Eric S. Raymond': SELECT F_PROPERCASE('ERIC S. RAYMOND') FROM RDB$DATABASE; Связанные или похожие функции Cm. также lower (), upper () Функции BLOB' FBUDF STRING2BEOB(VALUE) - .. ; у Linux, Win32 Принимает поле строки (столбец, переменную, выражение) и возвращает текст blob Аргументы value: столбец или выражение, результатом вычисления которого является значение типа varchar 300 символов или меньше 1 Самый последний вариант библиотеки FreeUDFLib с исправленными функциями blob на- ходится на www.ibase.ru. — Прим. науч. ред.
932 Приложение 1 Возвращаемое значение Текст blob Замечания В большинстве случаев нет необходимости вызывать эту функцию. Firebird напрямую принимает строки в качестве входных данных для blob Пример Следующий фрагмент модуля PSQL выполняет конкатенацию двух строк и конвертирует результат в текст blob: DECLARE VARIABLE V-COMMENT1 VARCHAR(250) ; DECLARE VARIABLE V_COMMENT2 VARCHAR(45); DECLARE VARIABLE VJIEMO VARCHAR(296) = ' ' ; SELECK. . .другие поля. . .>, C0MMENT1, COMMENT2 FROM APPLICATION WHERE APPLICATION—ID = ;APP_ID INTO <...другие переменные...>, ;V_COMMENT1, V_COMMENT2; IF (V-COMMENT1 IS NOT NULL) THEN V—MEMO = V_~COMMENT1; IF (V_COMMENT2 IS NOT NULL) THEN BEGIN IF (V_MEMO = '') THEN V—MEMO = V_COMMENT2; ELSE V_MEM0 = VJMEMO 'll V_COMMENTS; END IF (V_MEMO <> ' ') THEN INSERT INTO MEMBERSHIP( FIRST_NAME, LASTJSIAME, APP_ID, BLOB-MEMO) VALUES ( :FIRST-NAME, :LAST_NAME, :APP_ID, STRING2BLOB(:V-MEMO)); Связанные или похожие функции См. также f_blobaspchar() lliliillllijlll F_BWB»XSEQitE«TLENOTH (VALUE) Win32 Принимаемая ссылка на blob в памяти возвращает количество сегментов для его хранения Аргументы value является идентификатором столбца blob в таблице Возвращаемое значение Целое число, являющееся количеством сегментов Пример SELECT F_BLOBSEGMENTCOUNT(BLOB_MEMO) AS SEGMENT_COUNT FROM MEMBERSHIP WHERE MEMBER ID = . ...;
Список внешних функций 933 Связанные или | См. также f_maxblobsegmentsize (), f_blobsize () похожие функции i FREEUDFLIB Win32 \ F_BLOBSIZE(VALUE) i Принимаемая ссылка на blob в памяти возвращает размер в байтах Аргументы i value является идентификатором столбца blob в таблице Возвращаемое i Целое число, являющееся размером blob в байтах значение Пример iSELECT F_BLOBSIZE(BLOB_MEMO) as size_of_memo i FROM MEMBERSHIP i WHERE MEMBER_ID = . . . .; Связанные или i См.также f blobsegmentcount(), f blobmaxsegmentsize() похожие функции FREEUDFLIB MMjillgiiiMiiMM Win32 =: Принимает ссылку на поле blob (столбец или переменная) и возвращает i его содержимое в виде строки, завершаемой нулем Аргументы i value является ссылкой на столбец или переменную blob Возвращаемое i Строка, завершаемая нулем значение Замечания i Используйте эту функцию только для тех blob, о которых вы точно знаете, i что их размер не превышает максимальную длину типа varchar i (32 765 байт). Учитывайте увеличенный размер в байтах в blob, хранящих : многобайтовые символы Пример i SELECT F_BLOBASPCHAR(BLOB_MEMO) AS MEMO__STRING i FROM MEMBERSHIP i WHERE MEMBERJED = . . . .; Связанные или i' См. также string2blob () похожие функции ; FREEUDFLIB 1 F_BLOBLEFT (VALUE, N) Win32 ; Получает ссылку на поле blob (столбец или переменная) и возвращает iпервые ысимволов Аргументы ; value: идентификатор столбца или переменной blob. i n: максимальное количество возвращаемых символов. Не должно превы- | шать максимальной длины для varchar (32 765 байт) Возвращаемое i Завершаемая нулем строка длиной N или менее символов значение
934 Приложение 1 Замечания Учитывайте увеличенный размер в байтах в blob, хранящих многобайтовые символы Пример SELECT F_BLOBLEFT(BLOB_MEMO, 20) AS MEMO_START FROM MEMBERSHIP WHERE MEMBER_ID = ....; Связанные или См. также f blobmid (), f blobright () похожие функции Win32 Получает ссылку на поле blob (столбец или переменная) и возвращает строку за номером n Аргументы value: идентификатор столбца или переменной blob. n: номер строки blob. Первая строка рассматривается как строка номер 1 Возвращаемое значение Завершаемая нулем строка Замечания Используйте эту функцию только для тех BLOB, которые содержат строки, разделенные символами CRLF (возврат каретки, перевод строки). Не используйте ее, если вы не уверены, что строки не превышают максимальную длину для типа varchar (32 765 байт). Учитывайте увеличенный размер в байтах в blob, хранящих многобайтовые символы Пример SELECT F_BLOBLINE(BLOB_MEMO, 20) AS MEMO_LINE20 FROM MEMBERSHIP WHERE MEMBER_ID = ....; Связанные или См. также F blobmid (), f blobright () похожие функции ЕВЕЕТОЙДВ FjBiaamHVMBE, startpos, n)’ Win32 Получает ссылку на поле blob (столбец или переменная) и возвращает n символов, начиная с позиции startpos Аргументы value: идентификатор столбца или переменной blob. startpos: позиция первого символа в возвращаемой строке. N: максимальное количество возвращаемых символов. Не должно превы- шать максимальной длины для varchar (32 765 байт) Возвращаемое Завершаемая нулем строка длиной N или менее символов значение Замечания Учитывайте увеличенный размер в байтах в blob, хранящих многобайтовые СИМВОЛЫ Пример SELECT F_BLOBMID (BLOB_MEMO, 140, 20) AS MEMO_SUBSTRING FROM MEMBERSHIP WHERE MEMBER_ID = ....;
Список внешних функций 935 Связанные или похожие функции i См. также f_blobright {), f_blobleft () fREEUDFLIB, | (VALUB? -WX " . '"'V" Win32 j Получает ссылку на поле blob (столбец или переменная) и возвращает jпоследние Nсимволов Аргументы I value: идентификатор столбца или переменной BLOB. i N: максимальное количество возвращаемых символов. Не должно превы- i шать максимальной длины для varchar (32 765 байт) Возвращаемое значение i Завершаемая нулем строка длиной N или менее символов Замечания i Учитывайте увеличенный размер в байтах в blob, хранящих многобайтовые i СИМВОЛЫ Пример i SELECT F_BLOBRIGHT(BLOB_MEMO, 20) AS MEMO__END ; FROM MEMBERSHIP j WHERE MEMBER_ID = . . . .; Связанные или похожие функции \ См. также f_blobmid (), f^blobleft () тжяиядв ; FJBLOBBIMCMP(VALUEI, VALUES)) , C’" : • . Win32 ; Выполняет двоичное сравнение двух полей blob (столбцы или переменные) j и возвращает значение, указывающее, являются ли они одинаковыми Аргументы valuei и VALUE2 являются идентификаторами двух столбцов или перемен- s ных blob, для которых выполняется сравнение Возвращаемое значение | 1 (истина), если поля одинаковы, и 0 в противном случае Пример j Следующий фрагмент триггера before update устанавливает флаг на ос- новании результата сравнения двух blob: : DECLARE VARIABLE CMP-BLOB BLOB SUB_TYPE TEXT; i SELECT BLOB_MEMO FROM REPL_MEMBERSHIP i WHERE MEMBER_ID = NEW.MEMBER-ID iINTO :CMP-BLOB; iIF (F_BLOBBINCMP(NEW.BLOB_MEMO, CMP_BLOB) = 1) THEN NEW.UPDATE-MEMO = ’T’; 1 ELSE NEW. UPDATE-MEMO = 1 F1;
936 Приложение 1 Тригонометрические функции IBJ3DF ACOS(VALUE) . Linux, Win32 Вычисляет арккосинус для числа между -1 и1. Если число выходит за эти границы, то возвращается NaN Аргументы value: столбец или выражение, совместимое со знаковым или беззнаковым числом double precision, которое является допустимым значением коси- нуса Возвращаемое значение Число DOUBLE PRECISION в градусах Пример Следующий фрагмент триггера преобразует значение косинуса в градусы: IE (NEW.RAW_VALUE IS NOT NULL) THEN NEW.READING1 = ACOS(NEW.RAW_VALUE); Связанные или- похожие функции См. также cos (), cosh () и другие тригонометрические функции IBJJDF Linux, Win32 ASIN(VALUE) Вычисляет арксинус для числа между -1 и 1. Если число выходит за этот диапазон, то возвращается NaN Аргументы value: столбец или выражение, совместимое со знаковым или беззнаковым числом double precision, которое является допустимым значением синуса Возвращаемое значение Число double precision в градусах Пример Следующий фрагмент триггера преобразует значение синуса в градусы: IF (NEW.RAWJ7ALUE IS NOT NULL) THEN NEW.READING1 = ACOS (NEW.RAW_VALUE) ; Связанные или похожие функции См. также sin (), sinh () и другие тригонометрические функции Linux, Win32 АТАК(VALUE) Возвращает арктангенс входного значения Аргументы value: столбец или выражение, совместимое со знаковым или беззнаковым числом double precision, которое является допустимым значением тангенса Возвращаемое значение Число double precision в градусах
Список внешних функций 937 Пример Следующий фрагмент триггера преобразует значение тангенса в арктангенс (в градусах): IF (NEW.RAW_VALUE IS NOT NULL) THEN NEW.READING1 = ATAN(NEW.RAW_VALUE); Связанные или похожие функции См. также ATAN2 (). tan() , tanho и другие тригонометрические функции Linux, Win32 Возвращает значение, являющееся арктангенсом в градусах, вычисляемое как арктангенс результата деления одного тангенса на другой Аргументы valuei и VALUE2 являются числовыми столбцами или выражениями, вычис- ляемыми в число DOUBLE precision, которое является допустимым значением тангенса Возвращаемое значение Число double precision, являющееся арктангенсом valuei / valued в градусах Пример Следующий фрагмент модуля PSQL сохраняет значение, которое является углом в градусах, как арктангенс результата деления одного тангенса на Другой: UPDATE HEAVENLY_HAPPENINGS SET INCREASE_RATIO = ATAN2 (INITIAL_TAN, FINAL_TAN) WHERE HAPPENING_ID = :happening_id; Связанные или похожие функции См. также atan (), tan (), tanh () IBJTOF Linux, Win32 Возвращает косинус значения value Аргументы value: столбец или выражение, совместимое со знаковым или беззнаковым числом double precision, преобразуемым к значению (в градусах) от -263 до 263 Возвращаемое значение Число double precision или 0, если входное значение выходит за грани- цы диапазона Замечания Если value больше или равно 263 либо меньше или равно -263, то про- изойдет потеря значения, а функция сгенерирует ошибку a tloss и вернет 0 Пример Следующий фрагмент триггера вычисляет и сохраняет косинус угла в гра- дусах: IF (NEW.READING1 is NOT NULL) THEN NEW.RDG_COSINE = COS(NEW.READING1);
938 Приложение 1 Связанные или похожие функции i См. также sin (), cos (), acos (), cosh () IBJJDF COSH{VALUE) Linux, Win32 i Возвращает гиперболический косинус значения value Аргументы i value: столбец или выражение, совместимое со знаковым или беззнаковым i числом double precision, преобразуемым к значению (в градусах) от -263 i до 263 Возвращаемое значение i Число double precision или 0, если входное значение выходит за грани- цы диапазона Замечания | Если value больше или равно 263 либо меньше или равно -263, то про- i изойдет потеря значения, а функция сгенерирует ошибку a_TLOSS и вернет i 0 Пример i Следующий фрагмент триггера вычисляет и сохраняет косинус угла в гра- j дусах: iIF (NEW. READINGl IS NOT NULL) THEN j NEW. RDG_COS__HYP = COSH (NEW. READINGl) ; Связанные или похожие функции \ См. также sinh (), tanh () и другие тригонометрические функции IBJJDF I СОТ (VALUE) Linux, Win32 ) Возвращает котангенс значения value Аргументы i value: столбец или выражение, совместимое со знаковым или беззнаковым i числом double precision, преобразуемым к значению (в градусах) от -263 i до 263 Возвращаемое значение 5 Число double precision или 0, если входное значение выходит за грани- цы диапазона Замечания i Если value больше или равно 263 либо меньше или равно -263, то про- : изойдет потеря значения, а функция сгенерирует ошибку a tloss и вернет i0 Пример i Следующий фрагмент триггера вычисляет и сохраняет котангенс угла в i градусах: iIF (NEW.READINGl IS NOT NULL) THEN i NEW.RDG-COTAN = COT(NEW.READINGl); Связанные или похожие функции i См. также tan (), atan (), tanh () IBJJDF ' ' (SIN (VALUE) Linux, Win32 i Возвращает синус значения value
Список внешних функций 939 Аргументы i value: столбец или выражение, совместимое со знаковым или беззнаковым i числом double precision, преобразуемым к значению (в градусах) от -263 | до 263 Возвращаемое значение \ Число double precision или 0, если входное значение выходит за грани- ; цы диапазона Замечания i Если value больше или равно 263 либо меньше или равно -263, то про- i изойдет потеря значения, а функция сгенерирует ошибку a_TLOSS и вер- j нет 0 Пример 1 Следующий фрагмент триггера вычисляет и сохраняет синус угла в граду- 1 сах: iIF (NEW.READING1 IS NOT NULL) THEN j NEW.RDG_SINE = SIN(NEW.READINGl); Связанные или похожие функции i См. также cos (), asin (), sinh () TB_UDF Linux, Win32 jSINH(VALUE) . ’ i Возвращает гиперболический синус значения value Аргументы i value: столбец или выражение, совместимое со знаковым или беззнаковым i числом double precision, преобразуемым к значению (в градусах) от -263 i до 263 Возвращаемое значение i Число double precision или 0, если входное значение выходит за грани- цы диапазона Замечания i Если value больше или равно 263 или меньше или равно -263, то произой- ; дет потеря значения и функция сгенерирует ошибку a_TLOSS и вернет 0 Пример i Следующий фрагмент триггера вычисляет и сохраняет гиперболический i синус угла в градусах: iIF (NEW.READINGl IS NOT NULL) THEN i NEW.RDG_SIN_HYP = SINH(NEW.READINGl); Связанные или похожие функции i См. также sin (), tanh (), cosh () = ад (VALU Linux, Win32 i Возвращает тангенс значения value Аргументы i value: столбец или выражение, совместимое со знаковым или беззнаковым i числом double precision, преобразуемым к значению (в градусах) от -263 : до 263 Возвращаемое значение = Число double precision или 0, если входное значение выходит за грани- ; цы диапазона
940 Приложение 1 Замечания Если value больше или равно 263 или меньше или равно -263, то произой- дет потеря значения и функция сгенерирует ошибку a_TLOSS и вернет 0 Пример Следующий фрагмент триггера вычисляет и сохраняет тангенс угла в граду- сах: IF (NEW.READINGl IS NOT NULL) THEN NEW.RDG_TAN = TAN(NEW.READINGl); Связанные или похожие функции См. также сот (), atan (), tanh () IBJJDF Linux, Win32 j TANH (VALUE) возвращает гиперболический тангенс значения value Аргументы value: столбец или выражение, совместимое со знаковым или беззнаковым числом double precision, преобразуемым к значению (в градусах) от -263 до 263 Возвращаемое значение Число double precision или 0, если входное значение выходит за границы диапазона Замечания Если value больше или равно 263 либо меньше или равно -263, то про- изойдет потеря значения, а функция сгенерирует ошибку a_TLOSS и вер- нет 0 Пример Следующий фрагмент триггера вычисляет и сохраняет гиперболический тангенс угла в градусах: IF (NEW.READINGl IS NOT NULL) THEN NEW.RDG_TAN_HYP = TANH(NEW.READINGl); Связанные или похожие функции См. также tan (), atan () Подпрог|: FREEUDFLIB Linux, Win32 )аммы форматирования данных FJ»LLARVAL (VALUE) Форматирует значение числа с фиксированной точкой в формат валюты, например, в 99 999 999.99, подходящий для конкатенации с символами ва- люты и другими украшениями Аргументы value: столбец или выражение, которое вычисляется в число типа double PRECISION ИЛИ FLOAT Возвращаемое значение Строка переменной длины
Список внешних функций 941 Замечания Необходимо быть внимательным по отношению к точности входного числа, особенно в случае, когда оно получается в результате вычисления. Можно ожидать неверных результатов, когда значение передается значениям char или varchar, размер которых слишком мал для приема этого значения Пример SELECT || FJ3OLLARVAL (CAST (SOM (PURCHASE—АМТ) AS DOUBLE PRECISION)) ||' USD' AS TOTAL-SPEND FROM MEMBER_ACCOUNT WHERE MEMBER_ID = 440099; Связанные или похожие функции См. также F_FixedPoint () FREEUDFLIB iF-FIXEDPOTNT(VALUEI, VALUE2) Win32 Форматирует передаваемое значение valuei как строку с фиксированной ТОЧКОЙ С КОЛИЧеСТВОМ Дробных ЗНаКОВ VALUE2 Аргументы valuei: столбец или выражение, которое вычисляется в число с плавающей точкой: преобразуемое число. value2: константа или выражение, которое вычисляется в целый тип: коли- чество знаков после десятичной точки для отображения в выходных данных Возвращаемое значение Строка переменной длины Замечания Необходимо быть внимательным по отношению к точности входного числа, особенно в случае, когда оно получается в результате вычисления. Можно ожидать неверных результатов, когда значение передается значениям char или varchar, размер которых слишком мал для приема этого значения Пример SELECT 'TOTALYARDAGE: ' || F_FIXEDPOINT(SUM(YARDAGE), 3) || ' yds' AS TOTAL_YARDAGE FROM PIECE-GOODS WHERE PRODUCT_ID = 100; Связанные или похожие функции См. также F-DollarValue () IB—UDF F_GENERATESNDXINDEX(VALUE) Win32 Получая строку value, вычисляет ее значение soundex Аргументы value: столбец или выражение, вычисляемое в символьный тип, либо имя, для которого генерируется soundex Возвращаемое значение Шестисимвольный индекс soundex
942 Приложение 1 Замечания Простой алгоритм soundex используется для вычисления индекса soundex, он генерирует алгоритмический код. Вам не нужно понимать этот алгоритм для реализации индексирования soundex Пример Следующий фрагмент триггера before insert демонстрирует, как эта функция может быть использована для автоматического сохранения столб- ца soundex в качестве столбца поиска в регистре участников: IF (NEW. LAST_NAME is NOT NULL) THEN NEW. SOUNDEX NAME = F SNDXINDEX (NEW.LAST NAME) ;
ПРИЛОЖЕНИЕ 2 Решение сетевых проблем Когда у вас возникают проблемы подключения клиента к серверу, этот набор тестов может помочь вам локализовать причину. Если все они ошибочны, не забывайте о возможности проверки сетевых кабелей: вставлены ли они в разъемы и чисты ли все контакты! Следующий список содержит основные вещи, которые вы можете попытаться сделать. Можете ли вы вообще соединиться с базой данных? Если у вас запущен Суперсервер под Windows или Классический сервер в POSIX, проверьте, можете ли вы выполнить локальное соединение. (Суперсервер в POSIX и Классический сервер в Windows не поддерживают локальных соединений.) 1. Вызовите окно командной строки на машине сервера базы данных и запустите приложение типа isql или графический инструмент под Windows, такой как IB SQL или IBAccess. 2. Попытайтесь соединиться с базой данных без задания имени хоста— просто ука- жите путь. POSIX (все в одной команде): ./isql /opt/firebird/examples/employee.fdb -user SYSDBA -password yourpwd Windows (все в одной команде): isql 'c:\Program Files\Firebird\Firebird_l_5\examples\employee.fdb1 -user SYSDBA -password yourpwd Если это работает, значит сервер запущен и путь к базе данных правильный. В про- тивном случае проверьте пароль, а также находит ли ваше приложение правильную версию клиентской библиотеки.
944 Приложение 2 Можете ли вы соединиться с базой данных в локально закольцованном варианте? Для любой версии сервера (кроме Встроенного сервера под Windows) вы можете эмулировать клиентское соединение с сервером через интерфейс удаленного клиента, соединяясь в локально закольцованном режиме TCP/IP (loopback). Это рекомендо- ванный режим для всех локальных соединений к полным серверам. Если сервисы TCP/IP сконфигурированы и запущены в системе, это должно работать, даже если не инсталлирована сетевая карта. Вначале откройте файл hosts в текстовом редакторе и убедитесь, что он содержит запись, подобную следующей: 127.0.0.1 localhost # local loopback server В Windows этот файл должен находиться в каталоге \drivers\etc каталога °/osystem°/o. В Windows 95/98 он, вероятно, находится в самом каталоге Windows. В POSIX он должен находиться в каталоге /etc. Если такая запись отсутствует, добавьте ее, сохраните и закройте файл. 1. Вызовите окно командной строки на машине сервера базы данных и запустите приложение типа isql или графический инструмент под Windows, такой как IB SQL или IBAccess. 2. Попытайтесь соединиться с базой данных. POSIX: ./isql localhost:/opt/firebird/examples/employee.fdb -user SYSDBA -password yourpwd Windows (все в одной команде): isql ’ localhost:с: \Program Files\Firebird\Firebird_l__5\examples\ employee.fdb' -user SYSDBA -password yourpwd Если это работает, значит сервер запущен, TCP/IP работает, путь к базе данных пра- вильный. < ПРИМЕЧАНИЕ. Системы POS1X не используют обозначение драйвера. При этом имя сервера, путь и имя файла чувствительны к регистру. Если локальное или локальное закольцованное соединение не проходит и вы увере- ны, что имя базы данных задано верно, следовательно есть какие-то ошибки в конфи- гурации сервера или сети.
Решение сетевых проблем 945 Прослушивает ли сервер порт Firebird? Сервис gdsdb (порт 3050) не будет отвечать, если серверный процесс не был запу- щен. См. табл. П2.1, где содержатся инструкции по запуску сервера. Таблица П2.1. Запуск сервера ОС Инструкции Суперсервер POSIX Из командной строки войдите в каталог Firebird /bin и введите команду ibmgr -start Windows: сервис Используйте апплет Панель управления менеджера Firebird и щелкните мышью по Start, или в командной строке введите команду net start FirebirdGuardianDefaultlnstance. В версии 1.0.x введите команду кет START FirebirdGuardian. Вы также можете попытаться запустить этот сер- вис из апплета Сервисы Windows: приложение Запустите Firebird Guardian или Firebird Server из меню Пуск Классический сервер POSIX Здесь не надо ничего делать. Если демон xinet (или inet на старых системах) запущен, он должен стартовать экземпляр fb_inet_server (ib_inet_server для версии 1,0.x), когда вы пытаетесь соединиться с базой данных Windows: сервис Перейдите в окно командной строки и введите net start FirebirdGuardianDefaultlnstance. Вы также можете попытаться запус- тить этот сервис из апплета Сервисы Windows: приложение Запустите Firebird Server из меню Пуск. Замечание: не пытайтесь запускать Классический сервер из апплета Панель управления, не пытайтесь запус- кать приложение Guardian Вы получили ошибку, хотя сервис выполняется? Если клиент получает доступ к серверной машине и сервис gds db отвечает, но все еще не может соединиться с базой данных, вы можете увидеть ошибку отказа в со- единении (connection rejected). Возможные варианты тестирования описаны в сле- дующих разделах. Находится ли база данных на физически локальном диске? Файл базы данных не должен размещаться в файловой системе NFS, на назначенном или совместно используемом диске. Когда процесс ibserver обнаруживает такую си- туацию, он отказывает в соединении.
946 Приложение 2 Для исправления этой ситуации переместите вашу базу данных в файловую систему на жесткий диск, который является физически локальным для сервера базы данных, и скорректируйте соответствующим образом вашу строку соединения. Правильные ли имя пользователя и пароль? Клиентское приложение должно использовать комбинацию имени пользователя и пароля, которая соответствует записи в базе данных безопасности на сервере. Эта база данных должна находиться в корневом каталоге инсталляции Firebird и должна быть перезаписываемой серверным процессом. Имена пользователей и пароли применимы к серверу, а не к отдельной базе данных. Если вы перенесли базу данных с другого сервера, на котором были установлены роли и привилегии, то вы должны установить нужных пользователей на новом сер- вере. Имеет ли владелец серверного процесса достаточно полномочий для открытия файлов? Полномочия к файловой системе, включая права к каталогам, могут вызвать пробле- мы в POSIX. Полномочия к каталогам могут вызвать проблемы в разделах Windows NTFS. Серверному процессу может понадобиться больше полномочий по созданию файлов (например, firebird.log или interbase.log) р корневом каталоге Firebird. Попытки доступа к базам данных в областях файловой системы, которые не сконфи- гурированы для доступа к базе данных, могут вызвать проблемы в Firebird 1.5 и бо- лее ПОЗДНИХ — СМ. параметр DatabaseAccess В файле firebird.conf. Jf9 ПРИМЕЧАНИЕ. Если вы изменяли firebird.conf (версия 1.5+) или ibconfig/ isc_config (версия 1.0.x), вам будет нужно остановить сервер и заново запустить его, чтобы изменения вступили в силу. Может ли клиент найти хост? Сообщение об ошибке "Unable to complete network request to host" (Невозможно завершить сетевой запрос к хосту) появляется, когда клиент Firebird не может уста- новить сетевое соединение с серверной машиной. Существует несколько общих причин. ♦ Клиент не может найти хост в вашей сети. Ваш сервер Firebird должен выпол- няться в той сети, которую вы используете. Если имя хоста определяет хост, ко- торый недоступен по причине нарушений в сети, или хост просто не включен, за- прос на соединение не может быть выполнен.
Решение сетевых проблем 947 ♦ Используются старые драйверы и/или клиентские библиотеки. Вы должны ис- пользовать драйверы (ODBC, BDE и т. д.), сертифицированные для работы с ва- шей версией сервера Firebird. Например, SQL Explorer, который поставляется вместе с некоторыми продуктами Borland, вероятно, не сможет работать с Firebird по причине использования слишком старой версии BDE. ♦ Приложение находит неверную клиентскую библиотеку. Все версии инструмен- тов администратора пытаются загрузить библиотеку libgds.so (клиенты POSIX) или gds32.dll (клиенты Windows), отыскивая ее по системному пути по умолча- нию. Клиент Firebird 1.5 имеет имя libfbclient.so или fbclient.dll и не располагается по системному пути по умолчанию. Изучите самые последние замечания в ката- логе Firebird /doc и/или в корневом каталоге, чтобы найти решение. ♦ На месте имени сервера используется адрес IP. Если вы предоставляете адрес IP вместо имени хоста (имя сервера), то драйвер TCP/IP может оказаться неспособ- ным разрешить его, или время попытки разрешения может оказаться слишком большим. Обычно это проблема Windows 95 и ранних версий NT 4. См. преды- дущие замечания по созданию записи в файле hosts и использованию ее для всех клиентских машин. ♦ Адрес IP хоста является непостоянным. Системы, которые используют динами- ческую адресацию IP, могут изменять адрес IP хоста без выдачи предупреждаю- щих сообщений. Похожие проблемы могут возникнуть, если хост-машина имеет более одной сетевой карты. Создайте записи в файле hosts на серверной и клиент- ской машинах, чтобы связать имя сервера с адресом IP. Firebird 1.5 также предос- тавляет возможность конфигурирования RemoteBindAddress для того, чтобы соеди- нения могли находить правильный канал для сервера Firebird. ♦ Отсутствует или неправильная запись gdsdh в файле services. Клиенты Firebird будут отыскивать сервис gds db в порте по умолчанию 3050. Если вы изменили символ сервиса или номер порта в файле services, вероятно, у вас недостаточно информации в строке соединения, чтобы можно было выполнить это соединение. Вернитесь к разд. "Конфигурирование сервиса порта TCP/IP" главы 2, если вам нужно сделать подобную установку. < ПРИМЕЧАНИЕ. Вероятны также проблемы с сервисом порта, если у вас сервер InterBase или другой сервер Firebird, запущенный на той же серверной машине. Хотя это можно делать в версии 1.5, однако требует аккуратности при конфигури- ровании и не может быть указано в момент установки Firebird. ♦ Указанный сетевой протокол недоступен. Синтаксис строки соединения в Fire- bird определяет сетевой протокол, используемый клиентом для соединения с сер- вером. Если ваш сервер не поддерживает протокол, указанные в строке соедине- ния, то попытка соединения будет неудачной с появлением ошибки сети. Напри- мер, строка соединения для именованных каналов Windows (NetBEUI) не будет работать, если сервер выполняется под Windows 95/98, ME или ХР, в Linux или на другой платформе POSIX. Только TCP/IP будет работать на этих платформах.
948 Приложение 2 t ПРИМЕЧАНИЕ. Клиентская библиотека Firebird не поддерживает сетевой прото- кол IPX/SPX. Соединение будет неудачным, если вы попытаетесь использовать ____ IPX/SPX, задавая его в строке соединения с базой данных в виде server@volume.7path/database.fdb. ♦ Вы пытаетесь соединиться с совместно используемым устройством. Невоз- можно соединиться с базой данных в Windows, NFS или SMB (Samba) с совмест- но используемым устройством. Должен использоваться абсолютный путь файло- вой системы, как он виден с хоста. ♦ Строка пути несовместима с существующим соединением. Суперсервер Firebird будет блокировать соединение, если полученная строка пути несовместима с пу- тем, используемым в существующем соединении. В Firebird добавлен этот меха- низм для защиты баз данных от давно существующей ошибки в предыдущем коде InterBase, что приводило к разрушению сервера с базами данных Windows. Д ВНИМАНИЕ! Классический сервер не имеет такой защиты. Если у вас Классиче- ский сервер под Windows, убедитесь, что различные клиенты всегда соединяются с использованием такой же строки пути. Несовместимость появляется, т. к. Windows примет обозначение диска без последующей наклонной черты, а имен- но C:Databases\mydb.fdb вместо C:\Databases\mydb.fdb. Это не является пробле- мой в POSIX, которая не имеет таких предпосылок к несовместимости. Нужно ли вам отключить автоматический набор номера для Интернета в Windows? Операционные системы Microsoft Windows предоставляют по умолчанию сетевую возможность, удобную для пользователей, которые используют модем для подклю- чения к Интернету — любой запрос TCP/IP, который появляется в сети, активизирует автоматическую программу набора номера. Это может стать проблемой для клиент- ских систем, использующих TCP/IP для доступа к серверу Firebird в локальной сети. Поскольку клиентские запросы применяют сервис TCP/IP, автоматически вызывается программа Windows набора номера, влияя на сетевые соединения от клиента к сер- веру. Существует несколько способов отмены возможности автоматического набора номе- ра. Для правильной работы необходимо сконфигурировать в вашей системе не более одного из этих методов. Изменить порядок сетевых адаптеров Возможно, на вашем компьютере есть драйвер соединения через модем и сетевая карта. В Windows NT и Windows 98 вы можете изменить порядок этих двух сетевых интерфейсов для того, чтобы сетевой адаптер использовался первым по отношению к сетевому драйверу модема.
Решение сетевых проблем 949 Откройте Панель управления из меню Пуск и выберите Сетевые и Модемные со- единения. Затем меню Дополнительно, и на вкладке Адаптеры и привязка в соот- ветствующем месте поменяйте порядок адаптеров. Изменение конфигурации Internet Explorer Если у вас в качестве браузера установлен IE, откройте его апплет в Панели управ- ления, и запретите автодозвон. Это находится в меню Соединения интернет- настроек браузера. Здесь вы найдете несколько переключателей, один из которых нужен вам. Конкрет- ное название переключателя меняется от версии к версии. Например, в английской версии Windows 2000 этот пункт называется Never dial a connection. Запретить автодозвон через реестр Для запрета автодозвона запустите Regedit и найдите ключ HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings Найдите ключ EnabieAutoDiai в правой панели и выберите Изменить. Поменяйте зна- чение с 1 на 0. Запретить автодозвон RAS На серверных платформах Windows вы можете запретить сервис RAS AutoDial. Для этого откройте Сервисы в Панели управления. В NT 4 сервисы доступны сразу из Панели управления. В последующих версиях Windows сервисы перенесены в Administrative Tools. Прокрутите сервисы до Remote Access Auto Connection Manager (или в NT 4 — Remote Access Dialup Manager) и выберите его. Измените опцию запуска на Manual (Вручную). Чтобы остановить сервис прямо сейчас, нажмите кнопку Стоп. Для запуска— кноп- ку Старт. Все еще есть проблемы? Если проблема с подсоединением к серверу Firebird так и не исчезла, тогда имеет смысл обратиться к более квалифицированному специалисту по настройкам сети ли- бо в один из форумов или списков рассылки. Обратитесь к приложению 12 за под- робностями.
ПРИЛОЖЕНИЕ 3 Интерфейсы приложений "Родным" интерфейсом Firebird по доступу к клиентской библиотеке является ис- пользование функции С и структур параметров, представляющих API. Заголовочный файл С ibase.h поставляется вместе с Firebird в каталоге /include. Этот заголовочный файл может быть применен при написании программ на языке С, которые использу- ют клиентскую библиотеку, однако это полезный справочник и при разработке ин- терфейсов к библиотеке из других языков. Драйверы JayBird JDBC Драйверы JayBird JDBC для Firebird являются полностью совместимой с JDBC 2.0 абстракцией API Firebird, которая может быть использована в любых IDE, которые поддерживают драйверы JDBC, например, в Eclipse и Borland JBuilder. Эти драйверы запускаются в Java 2 JRE 1.3.1 и Java 2 JRE 1,4.x и могут быть исполь- зованы со всеми популярными системами интерфейсов, поддерживающими JDBC 2.0, JDBC 2.0 Standard Extensions и JCA 1.0. Примеры включают JBoss 3.2.3, WebLogic 7.0, WebLogic8.1, ColdFusion MX, Hibernate (прозрачная постоянная сис- тема) и TJDO. Последняя версия JayBird 1.5 была подготовлена к весне 2004 года. Поддержка двухфазного подтверждения Firebird соответствует стандартному согла- шению участия в распределенных транзакциях в Java, поддерживающих реализации JCA framework и XADataSource. JayBird соответствует модели JDBC "одна транзак- ция на соединение". Она не имеет нескольких транзакций на соединение, хотя они невидимо используются в JCA framework. JDBC не поддерживает события и массивы Firebird. Лицензирование: JayBird является системой с открытыми кодами, свободно распро- страняемой или продаваемой на основании измененной лицензии BSD. Загрузка: в вашем браузере войдите на http://sourceforge.net/projects/firebird/ и про- крутите страницу, пока не найдете строку, содержащую firebird-jca-jdbc-driver. Щелкните по Download справа, чтобы перейти к странице загрузки, где вы сможете выбрать желаемый комплект поставки из списка firebird-jca-jdbc-driver— напри- мер, FirebirdSQL-Ex.zip. Поддержка: форум поддержки разработчиков и пользователей находится на http://groups.yahoo.com/group/firebird-java. Обширный, активно поддерживаемый
Интерфейсы приложений 951 список FAQ распространяется вместе с инсталляцией JayBird, а также доступен на различных сайтах сообщества, включая http://www.ibphoenix.com/main.nfs?a= ibphoenix&l=;FAQS;NAME=' JayBird'. ODBC Драйвер Firebird ODBC/JDBC Это свободно распространяемый, совместимый с JDBC драйвер ODBC с открытыми кодами для Firebird и InterBase 6.x, первоначально финансируемый IBPoenix и спон- сорами сообщества, свободно реализуемый (во всех смыслах) под Initial Developer's Public License. Версии библиотек драйвера доступны для Windows, Linux (unixODBC и iODBC), FreeDSB и Solaris. Самая последняя версия на момент выхода книги — 1.2.0060 совместима со спецификациями ODBC 3.0'. Она поддерживает все версии и модели Firebird, включая Встроенный сервер под Windows версии 1.5. Для соединения с базами данных Firebird приложения могут использовать множество дескрипторов DSN, каждый из которых реализован для различных клиентских вер- сий. Если требуется, то в одном приложении могут быть параллельные соединения. Транзакция с двухфазным подтверждением может поддерживать до десяти соедине- ний. Поддержка множества транзакций в одном соединении разрабатывалась, когда выходила эта книга. События Firebird не поддерживались. Распространяются make-файлы для создания драйвера из исходных текстов С в gcc 2.96 Linux и более поздних, gcc freeBSD, gcc для Windows (MinGW), cc Solaris, BCC55 и MsVC6. Проекты IDE c make-файлами доступны для DEV-C++ 4.8 и более поздних, а также для MsVC6. Драйвер ODBC Firebird — наиболее быстрый из доступных для Firebird и InterBase— хорошо работает с Open Office 1.1.0, Microsoft (Excel, VC6, VC7, VB6, VFP6, MsQry32, Access и т. д.) и с любыми компонентами, поддерживающими ADO. Он поддерживает зашифрованный пароль и прокручиваемые курсоры. Схемы интер- фейса включают: ♦ универсальные компоненты (Excel, VFP6, VB6 и т. д.) и поддержку столбцов мас- сивов, хранимые процедуры выбора и выполняемые хранимые процедуры с заме- . няемыми параметрами (call myproc ?), возможность {fn}, пакеты и полностью оп- ределенные имена столбцов; ♦ ADO OLEDB ODBC Manager (odbc32.dll) OdbcJdbc; ♦ OLEDB(MSDADC.DLL) ODBC Manager OdbcJdbc; ♦ интерфейс пользовательских программ c ODBC Manager OdbcJdbc. 1 Большинство современных приложений, в том числе офисных, требуют совместимости драйвера со спецификацией ODBC 3.5. Альтернативные драйверы ODBC также можно найти на www.ibase.ru. — Прим. науч. ред.
952 Приложение 3 Драйвер поддерживает AutoQuotedidentifier для полной совместимости запросов с интерфейсами запросов Microsoft. Загрузка совместно используемых библиотек: http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_60_odbc. Наиболее последние версии: http://cvs.sourceforge.net/viewcvs.py/firebird/OdbcJdbc/BuiIds/. Самые последние состояния: http://www.praktik.km.ua (Владимир Цвигун). Форум поддержки и разработки: обратитесь к списку на https://Iists.sourceforge.net/lists/listinfo/firebird-odbc-devel. Другие драйверы ODBC2 Драйвер XTG ODBC Это свободный с открытыми кодами драйвер ODBC для Firebird/InterBase 6.x под Windows, соответствующий уровню ODBC 3 API CORE и распространяемый под лицензией LGPL. Версия 1.0.0 (бета 15) содержит ошибки, но ее можно использо- вать. В двоичном виде распространяется как полный инсталлятор Windows. Загрузка двоичного кода: http://www.xtgsystems.com. Исходные тексты: http://ofbodbc.sourceforge.net/drvinfo.html. Драйвер Gemini ODBC Это коммерческий драйвер ODBC для Windows и Linux, соответствующий специфи- кации Call Level Interface (CLI), разработанный в SQL Access Group, а затем адапти- рованный X/Open и ISO/IEC в качестве приложения к стандарту текущего языка SQL. В настоящий момент в версии 2.2 бета драйвер соответствует спецификации ODBC, описанной в ODBC Programmer's Reference, версия 3.51. Более подробную информа- цию и загрузку пробной версии вы можете найти на http://www.ibdatabase.com3. Драйвер Easysoft ODBC Это коммерческий драйвер ODBC для InterBase под Windows и Linux. На некоторых платформах он поддерживает UNICODE. Более подробную информацию, загрузку пробной версии и описание форума поддержки можно найти на http:// www.easysoft.com/products/interbase. 2 Кроме драйверов ODBC еще существуют драйверы OLEDB. Великолепный драйвер, ко- торый можно использовать из офисных приложений, Visual C++ и других инструментов, соз- дан в Липецке: http://www.ibprovider.com/rus/index.html. — Прим. науч. ред. 3 Для получения этого драйвера обратитесь с запросом на адрес sales@ibase.ru. — Прим.
Интерфейсы приложений 953 Firebird .NET Provider Открытые исходные тексты Firebird .NET Provider являются интерфейсом для дан- ных, разработанным для работы приложений, созданных в окружениях Microsoft .NET. Самая поздняя стабильная версия (версия 1.5.2) поддерживает все версии Firebird, Классический сервер и Суперсервер для разработок в таких средах IDE, как: ♦ Microsoft Visual Studio 2002 и 2003; ♦ SharpDevelop (http://www.icsharpcode.net/OpenSource/SD); ♦ Borland C# Builder; ♦ Borland Delphi .NET (Delphi 8); ♦ MonoDevelop (http://www.monodevelop.com, в процессе тестирования). Версия 1.2 на стадии бета 2 (когда эта книга выходила из печати), похоже, была соз- дана летом 2004 года, поддерживает Firebird 1.5 Встроенный сервер для Windows и события Firebird. Provider не поддерживает— и это ограничение архитектуры ADO .NET — несколь- ких транзакций в соединении или двухфазного подтверждения транзакций. Поддерживаемые языки: С#, VB .NET, Microsoft Visual C++ .NET, Delphi .NET, ASP .NET и другие языки .NET. Известно, что он совместим со многими другими специа- лизированными продуктами .NET, включая Gentle .NET (http://www.mertner.com /projects/gentle), NHibernate (http://nhibernate.sourceforge.net) и aspxDelphi.net PORTAL & STORE (http://www.aspxdelphi.net). Поддержка Firebird добавляет- ся в следующей версии объектно-ориентированной оболочки LLBLGen Pro (http://www.llblgen.com/defaultgeneric.aspx). Поддерживаемыми платформами являются Microsoft 1.0 и 1.1 (только Windows) и Mono (http://www.go-mono.com), проверенная под Windows и Linux. Планируется, что Firebird .NET Provider будет следовать курсом Mono, когда она станет доступной для других платформ (Solaris, FreeBSD, HP-UX и Mac OS X). Загрузка: ссылка на загрузку текстов и документации: http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_download_dotnet. Форум поддержки и разработки: обратитесь к списку на http://lists.sourceforge.net/lists/listinfo/firebird-net-provider. Для подписчиков доступен список на http://sourceforge.net/mailarchive/forum.php7forunWirebird-net-provider. IBPP для разработки С++ Это свободная с открытыми кодами библиотека классов интерфейса клиента C++ для сервера Firebird версий 1.0, 1.5 и следующих. Она свободна от любой специфики ис- пользуемых для разработки инструментов. Разработана для обеспечения доступа к
954 Приложение 3 Firebird из любого приложения C++, созданного с помощью не визуального (объекты CORBA/COM, другие библиотеки классов и функций, "традиционный" процедурный код) или визуального (RAD-средства) окружения. IBPP предоставляет "чистый" ин- терфейс DSQL к Firebird через простые в использовании классы C++ для админист- рирования базы данных и манипулирования данными. Самой последней версией, когда выходила эта книга, была 2.3, поддерживающая все версии Firebird и модели клиент-сервер (Суперсервер, Классический сервер, Встро- енный сервер) с полной поддержкой множества транзакций, транзакций с несколь- кими базами данных и событий Firebird. IBPP поддерживает только чистый, стандартный код C++. Библиотека классов рас- пространяется в виде исходных кодов, которые могут использоваться следующими компиляторами: ♦ Windows: Borland C++ Builder 6, свободно распространяемый компилятор ко- мандной строки Borland, MSVC 6, MSVC 7, Digital Mars C++, MingW и CygWin; ♦ POSIX: gcc 3.2 или выше; ♦ BCCP может успешно компилировать на многих других конфигурациях с не- большой настройкой в основном в make-файлах. Лицензирование: Mozilla 1.1 и производные. Загрузка и подробности поддержки: http://www.ibpp.org. Форум поддержки: подпишитесь на http://lisG.sourceforge.net/lists/listinfo/ibpp- discuss. Delphi, Kylix и Borland C++ Builder IB Objects Этот продукт объединяет две давно известные системы компонентов для Firebird и InterBase, которые позволяют разработчикам использовать инструменты Borland IDE (Delphi, Kylix, и Borland C++ Builder) для реализации всех возможностей Firebird. Одна система совместима с библиотеками Borland TDataset и другими компонентами сторонних разработчиков, которые наследуют архитектуру TDataset. Другая, извест- ная как "родная IBO", основана на оригинальной иерархии классов, которые не свя- заны с архитектурой TDataset. Текущей версией является 4.3 Ах, где Хх представляет подрелизы и патчи релизов. Она поддерживает все версии Pascal для Delphi, начиная с 3, все версии Kylix, все версии C++ Builder 3 и выше и все модели клиент-сервер Firebird. Компоненты, совместимые с TDataset, разработаны для эмуляции компонентов дос- тупа к данным Borland VCL в той степени, что инструментов поиска и замены вполне достаточно для конвертирования устаревшего кода приложений BDE непосредствен- но в рабочую версию под IBO за несколько минут. Родная система IBO включает собственный класс источников данных (data source) и множество управляющих элементов для управления данными. В отличие от компо-
Интерфейсы приложений 955 нентов, основанных на TDataset, IBO может быть использован с минимальным редак- тированием инструментами Borland. Некоторые известные инструменты обеспечи- вают поддержку этих компонентов. Обе системы полностью поддерживают живые запросы, множество одновременных транзакций и транзакции к нескольким базам данных в одном приложении, события Firebird, однонаправленные и прокручиваемые курсоры и обратные вызовы. Родная система — которая совместима с невизуальными средствами на базе TDataset — под- держивает немедленное выполнение, кэширование событий DML в пределах прило- жения и расширенные операционные режимы, включая инкрементный поиск. Поддержка: списки рассылки, электронная почта, онлайн FAQ, репозитарий кода сообщества, обновляемый сайт сообщества для подписки и обширная библиотека загружаемой документации. Обратитесь к списку на http://groups.yahoo.com /community/ibobjects. Web-сайт: http://www.ibobjects.com. Сайт для подписки: http://community.ibobjects.com. Лицензирование: на доверии — полные исходные тексты (не открытые исходные тек- сты) поступают вместе с коммерческой подпиской или с некоммерческой благотво- рительной лицензией, открытыми исходными текстами проекта и для обучения. Оценка: полнофункциональный набор IB Objects с частичными исходными кодами с неограниченной проверочной версией с сообщением при выполнении вне IDE всегда учитывает дату. Может быть загружено с http://www.ibobjects.com. FIBPIus Другой мощный набор компонентов для Delphi, Kylix и C++ Builder, FIBPIus был разработан с использованием в качестве основы FreeiBComponents как свободно распространяемые компоненты доступа к данным. В настоящее время FIBPIus развился до уровня полностью коммерческого продукта, который заявляет о максимальном упрощении и гибкости в использовании. Основанные на архитектуре Borland TDataset, компоненты FIBPIus предоставляют полную совместимость со многими компонентами коммерческого характера и с от- крытыми кодами. Они предоставляют простой способ преобразования компонентов Borland InterBaseXpress. Самая последняя версия FIBPIus 5.34. Проверочная версия доступна для всех поддер- живаемых IDE: Delphi 5—7, C++ Builder 5—6 и Kylix 3. Более старые версии FIBPIus были все еще доступны для использования в более ранних продуктах Borland. FIBPIus поддерживают все версии и модели клиент-сервер в Firebird. Эти компонен- ты совместимы со всеми инструментами и инструментами сторонних разработчиков, которые связаны с архитектурой TDataset. Некоторые продукты предоставляют поль- зовательскую поддержку FIBPIus. 4 На момент перевода это версия 6.25. — Прим, перев.
956 Приложение 3 Поддержка: компания предоставляет поддержку пользовательских групп на англий- ском и русском языках, а также имеет систему "проблемных сообщений" для инди- видуальной помощи. Адреса пользовательских групп: ♦ английский язык: news://news-devrace.com/fibplus.en; ♦ русский язык: news://news-devrace.com/fibplus.ru; ♦ сайт http://www.devrace.com для сведений о продукте и сообщениях об ошибках. Другие Для интерфейса с Firebird доступны некоторые другие наборы компонентов. Компоненты UIB Это набор небольших по объему компонентов доступа к данным с открытыми кода- ми и драйверы DBXpress для Firebird, InterBase и Yaffil5. Это свободный набор под общей лицензией Mozilla. Данные компоненты работают не только с инструментами Borland (Delphi, Kylix, C++ Builder), но также со средой разработки Pascal с открытыми кодами Lazarus и FreePascal. Платформами являются Windows, Linux, и FreeBSD. Двоичные и исходные коды могут быть найдены на http://www.progdigy.com/UIB. Драйверы Firebird DBXpress Upscene Productions (http://www.upscene.com) создает драйверы DBXpress с низкой стоимостью для использования в Delphi, Kylix, C++ Builder. Часть от выручки про- даж этих драйверов идет на будущие гранты разработчикам проекта Firebird. Компоненты Zeos Это компоненты с открытыми кодами для администрирования и разработки прило- жений баз данных, включая Firebird. ♦ Объекты базы данных Zeos: библиотека компонентов Delphi для быстрого досту- па к Firebird (и другим СУБД). ♦ Управляющие элементы Zeos: библиотека визуальных компонентов Delphi для использования вместе с объектами базы данных Zeos. ♦ Библиотека классов Zeos: библиотека классов для разработки приложений C++ для платформ POSIX. Включает интерактивные утилиты графического интерфей- са для выполнения запросов, администрирования, а также для работы с BLOB. Лицензирование GPL, текущая версия 6.1.3. Подробности см. на http:// zeoslib.sourceforge.net. Существует множество доступных пакетов компонентов. См. список на http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_dev_comps, также ис- пользуйте для поиска Google. 5 Yaffil является ответвлением Firebird для Windows. Он был создан с открытыми кодами в конце 2003 года и затем был включен в коды Firebird 2.
Интерфейсы приложений 957 InterBaseXpress InterBaseXpress (IBX) содержит компоненты с открытыми исходными текстами для работы с базой данных и выполнения сервисных функций, которые поставляются вместе с некоторыми продуктами Borland, такими как Delphi, Kylix, C++ Builder. Ис- правленные от ошибок версии доступны в репозитории Code Central на http://community.borland.com. Не используйте версии, поставляемые с Delphi 5, 6 и с Kylix— это бета-версии с большим количеством ошибок, которые могут привести к разрушению базы данных. Более поздние версии с открытыми исходными кодами распространяются свободно и могут использоваться с Firebird 1.0.x. У некоторых пользователей возникли проблемы при употреблении компонентов соединения с ба- зой данных в Firebird 1.5. IBX не является идеальным выбором для использования с Firebird в течение длительного времени, потому что его разработчик сообщил, что он не будет включать в них средства для распознавания отличий между Firebird и Inter- Base6. IBOAdmin Это набор оболочек компонентов для Firebird Service API (копирование-восстанов- ление, статистика, безопасность и др.), основанных на кодах, первоначально распро- странявшихся как компоненты IBX Service. Они являются открытыми исходными кодами и требуют IB Objects. IBOAdmin свободно доступны на http:// www.mengoni.it. PHP PHP подтвердила, что является весьма подходящей платформой для разработки Web- клиентов баз данных Firebird при использовании расширений php-interbase. Сейчас стабильной версией РНР является 4.3.6. РНР 5.0 находится на поздней стадии бета- версии. В РНР 5.0 было добавлено множество новых функций для Firebird. Поищите "ibase" на http://www.php.net/ChangeLog-5.php#5.0.0RCl и последующих страни- цах. РНР (http://www.php.net) и Apache Web Server (http://httpd.apache.org)— неразде- лимая пара для разработки приложений Firebird для Web. Платформами являются Windows (CGI, ISAPI) и Linux (CGI). Приверженцы Firebird PHP Windows рекомен- дукэт использовать версию CGI на Windows Server 2003 и версию ISAPI на Windows 2000 для высокой производительности. Расширения php-interbase прекрасно выполняются со всеми версиями Firebird на Apache 1 и Apache 2, но с одной проблемой реализации. РНР не обрабатывает 64-битовые целые (тип bigint) обычным образом, так что вам нужно будет преобра- зовывать значения bigint в строки. Вероятно, это не относится к РНР 5. Новая функ- ция получения значения генератора Firebird, ibase_gen_id (), возвращает целое. 6 Тем не менее на текущий момент практически никаких проблем при использовании IBX с Firebird 1.0, 1.5 и 2.0 нет. —Прим. науч. ред.
958 Приложение 3 Как и другие общие интерфейсы доступа к данным, РНР использует модель "одна транзакция на одно соединение". При этом он не поддерживает множество транзак- ций. РНР следит за подтверждением незавершенных транзакций. Двухфазное под- тверждение и события Firebird появились в РНР 5. В Windows расширения php-interbase активизируются в файле PHP.ini после инстал- ляции. В Linux эти расширения должны создаваться из исходных кодов. Доступны многие функции API, включая управление пользователями. Lutz Bruckner создал административный интерфейс (см. http://ibwebadmin.sf.net), который предос- тавляет наиболее полные возможности доступа. Некоторые абстрактные средства доступны на страницах Firebird, включая классы ADODB (http://freshmeat.net/projects/adodb) и более компактный вариант ezSQL (http://php.justinvincent.com). PEAR-DB является другим абстрактным средством, которое формирует часть проекта РНР. Доступен также широкий выбор редакторов РНР. См. http://www.php-editors.com/review. IDE: Eclipse (httpV/www.eclipse.org) и phpEclipse (http://www.phpeclipse.de/tiki- view_articles.php) предоставляют средства отладки и приятный набор инструментов редактирования. Eclipse выполняется как под Windows, так и под Linux, и включает встроенного клиента CVS, поддержку документации и возможность плагинов (http://www.eclipse-plugins.info/eclipse/plugins.jsp). Eclipse 3 был выпущен летом 2004 года. Поддержка: разработчики Firebird РНР имеют небольшой, но активный список под- держки. Подпишитесь на http://www.yahoogroups.com/community/firebird-php. ♦ Для ADODB: http://phplens.com/lens/lensforum/topics.php?id=4. ♦ Для phpEclipse: http://www.phpeciipse.de/tiki-forums.php. Ресурсы: лучшим началом работы с РНР является учебник (http:// www.php.net/docs.php). Сайт http://www.hotscripts.com/PHP/index.html рекоменду- ется как источник огромного количества скриптов и классов, которые может исполь- зовать Firebird. Python KlnterbasDB является пакетом расширения Python, который реализует совместимую с Python Database API 2.0 поддержку Firebird. В дополнение к минимальному набору возможностей Python Database API KInterbasDB предоставляет полный API клиента Firebird. После релиза версии 3.1 была объявлена самая новая доступная версия. Пакет рас- пространяется свободно под разрешающей лицензией в стиле BSD, которую должны принять коммерческие и некоммерческие пользователи. Поддержка, загрузка, документация онлайн и другая информация на http://kinterbasdb.sourceforge.net/.
Интерфейсы приложений 959 Perl DBI — это модуль интерфейса с базой данных для Perl. Он определяет набор мето- дов, переменных и соглашений, которые предоставляют согласованный интерфейс с базой данных, зависящий от фактически используемой базы данных. DBD::InterBase являются открытыми кодами драйвера DBD для Firebird и InterBase. Располагаются на SourceForge (http://sourceforge.net/projects/dbi-interbase). Проект приглашает к участию разработчиков и пользователей. Поддержка: присоединяйтесь к списку переписки на http://lists.sourceforge.net /mailman/listinfo/dbi-inter-base-devel. Загрузка: отправляйтесь на http://www.cpan.org/moduIes/by-module/DBD для полу- чения наиболее позднего стабильного релиза, посетите http://dbi.interbase.or.id за стабильным релизом и релизом разработки.
ПРИЛОЖЕНИЕ 4 Как выполнять ремонт базы данных Вам придется потратить много сил для разрушения базы данных Firebird — система спроектирована так, чтобы выдержать тяжелые удары, которые ломают базы данных в других системах. Вы узнаете о том, что база данных разрушена, если вы не сможете с ней соединиться, не сможете выполнить ее резервное копирование или когда сооб- щение в протоколе firebird или от gbak -ь сообщает вам, что существуют нарушения в базе, или появляются сообщения об ошибке контрольной суммы. Если вы хотите понять, как могут появиться нарушения в вашей базе данных, верни- тесь к разд. "Как разрушить базу данных Firebird" в конце главы 75'. Восемь шагов восстановления В этом приложении описаны шаги, которые вам нужно выполнить при использова- нии инструментов командной строки gfix или gbak, чтобы попытаться восстановить неповрежденные данные при некоторых видах разрушения. Однако имейте в виду, что существуют отдельные виды разрушения сервера, которые такая процедура не может исправить. См. в приложении 12 ресурсы, доступные для использования в этих случаях. Для применения gfix и gbak в этой процедуре сделайте текущим каталог Firebird /bin на серверной машине. Получение исключительного доступа Первым делом, что нужно выполнить, когда появилось сообщение о разрушении, — это отключить всех пользователей от системы. Не позволяйте никому пытаться продолжить работу. Продолжение попыток использования базы данных может сделать восстанавливаемую базу данных невосстанавливаемой. 1 Во время написания этой книги автор услышала о первом случае нового способа разру- шения баз данных. Жертва проигнорировала все советы и разместила базу данных в каталоге NFS. После этого продолжалось соединение клиентов с базой данных клиентов через два Классических сервера Firebird 1.5 с различных машин, ни одна из которых не была владельцем жесткого диска, на котором находилась база данных. В результате база данных была разру-
Как выполнять ремонт базы данных 961 Проверка базы данных требует исключительного доступа к базе данных, иначе вы увидите следующее сообщение при попытке запустить gfix: OBJECT database_name IS IN USE [Объект имя-базы-данных используется] To же сообщение может появиться, если вы единственный пользователь, но имеете другую активную транзакцию. Например, утилита isql использует до трех параллель- ных транзакций. Остановив isql и другие инструменты администратора, вы можете выполнять работу. Для получения исключительного доступа выполните останов базы данных, находясь пользователем SYSDBA или пользователем, являющимся владельцем базы данных. Подробности см. в главе 39. Например, следующая команда заблокирует все другие попытки соединения с базой данных и приведет к ее останову через две минуты: gfix -sh -force 120 -user SYSDBA-password yourpword Создание копии работающего файла В этот момент gbak не может выполнить резервное копирование базы данных, со- держащей разрушенные данные. Поскольку вы имеете исключительный доступ, вы- полните копирование средствами файловой системы (а не с помощью gbak) файла базы данных. В POSIX используйте команду ср, а в Windows команду сору или экви- валентное действие копировать/вставить в графическом интерфейсе. Убедитесь, что в этот момент нет пользователей, соединенных с базой данных, — даже вас! Например, в Windows, находясь в каталоге базы данных, выполните: copy damaged.fdb repaircopy.fdb /\ ВНИМАНИЕ! Даже если вы можете восстановить исправленную базу данных, / f \ которая сообщала об ошибках контрольных сумм, может оказаться сложным оп- ределить объем потерянных данных. Если это важно, вы можете выбрать более раннюю копию, в которой вы сможете отыскать отсутствующие данные после проверки и починки разрушенных структур вашей текущей базы данных. Работайте с repaircopy.fdb. Выполнение проверки В первую очередь используются переключатели -v(aiidate) и -f [ull] утилиты gfix для проверки структур записей и страниц. Процесс проверки сообщает о разрушен- ных структурах и освобождает неназначенные фрагменты записей или "осиротевших страниц" (т. е. страниц, которые выделены, но не назначены никаким структурам данных). gfix -v -full {путь}repaircopy.fdb -user SYSDBA -password yourpword 31 Зак. 420
962 Приложение 4 Переключатель -n[o update] может быть использован вместе с -v для проверки и вы- дачи сообщений о разрушенных или не назначенных структурах без попыток их ис- править: gfix -v —п {путь}repaircopy.fdb -user SYSDBA -password yourpword Если постоянно появляются ошибки контрольных сумм в процессе такого способа проверки, то используйте переключатель -iignore], чтобы проверка их игнорировала: gfix -v -n -i {путь}гepairсору.fdb -user SYSDBA -password yourpword Исправление разрушенных страниц Если gfix сообщает о поврежденных данных, то следующий шаг— исправить (или починить) базу данных, убирая такие структуры. Переключатель -m[end] отмечает разрушенные записи как недоступные, следователь- но, они будут пропущены в процессе последующего резервного копирования. Вклю- чите переключатель —flull], чтобы запрос исправлял все разрушенные структуры, и переключатель -iignore], чтобы обходить ошибки контрольных сумм в процессе ис- правления. gfix -mend -full -ignore {путь}repaircopy.fdb -user SYSDBA -password yourpword или короче: gfix -m -f -i {nyTb}repaircopy.fdb -user SYSDBA -password yourpword Проверка после исправления -mend После того как команда с переключателем -mend завершит свою работу, опять выпол- ните gfix -v -full {дуть}repaircopy.fdb -user SYSDBA -password yourpword для проверки, остались ли еще разрушенные структуры. Очистка и восстановление базы данных Затем выполните полное резервное копирование и восстановление базы данных с использованием gbak, даже если все еще появляются сообщения об ошибках. До- бавьте переключатель -vferbose], чтобы видеть подробности. В своей простейшей форме команда резервного копирования может быть (все в одной команде): gbak —b -v -i {путь}repaircopy.fdb {путь}repaircopy.fbk -user SYSDBA -password yourpword Сложности в процессе резервного копирования Проблемы сборки мусора могут привести к аварийному завершению gbak. Если такое произошло, добавьте переключатель -[д], чтобы сообщить, что не надо собирать мусор.
Как выполнять ремонт базы данных 963 gbak -b -v -i -g {путь}repaircopy.fdb {путь}repaircopy.fbk -user SYSDBA -password yourpword Если есть разрушения в версиях записей, связанных с зависшей транзакцией, вам может понадобиться добавить переключатель -limbo: gbak —b -v -i -g -1 {путь}repaircopy.fdb {путь}repaircopy.fbk -user SYSDBA -password yourpword Восстановление очищенной резервной копии в качестве новой базы данных Теперь создайте новую базу данных из резервной копии с переключателем -v[erbose], чтобы наблюдать, что восстанавливается: gbak -create -v {путь}repaircopy.fbk {путь}reborn.fdb -user SYSDBA -password yourpword Проверка восстановленной базы данных Убедитесь, что восстановление базы данных разрешило проблемы, выполнив про- верку восстановленной базы данных с использованием переключателя -n[o update]: gfix -v -full {путь}reborn.fdb -user SYSDBA -password yourpword Если существуют проблемы с восстановлением, вам может быть придется рассмот- реть возможности новых попыток с использованием других переключателей gbak для уменьшения источников этих проблем, например: ♦ переключатель -iinactive] уменьшит проблемы с разрушенными индексами за счет восстановления без активизации каких-либо индексов. Потом вы сможете ак- тивизировать индексы вручную по одному за раз, пока не будет найден проблем- ный индекс; ♦ переключатель -o[ne_at_a_time] будет восстанавливать и подтверждать каждую таблицу одну за другой, позволяя вам восстанавливать хорошие таблицы и про- пуская проблемные. Как поступать, если проблемы остались Если предыдущие шаги не дают результатов, но вы все еще в состоянии соединяться с разрушенной базой данных, можно переслать структуры таблиц и данных из раз- рушенной базы данных в новую, используя инструмент qli (Query Language Inter- preter). Этот инструмент находится в вашем каталоге Firebird /bin, а руководство по его синтаксису в формате Adobe PDF может быть загружено с http.7/ www.ibphoenix.com/downloads/qli_syntax.pdf. qli является старым инструментом,
964 Приложение 4 однако он все еще работает, когда вам понадобится. Поищите в Интернете, как вы- полнить перекачку данных между базами данных с использованием qli. В приложении 5 есть ссылки на инструменты починки базы данных, которые, воз- можно, помогут вам в восстановлении разрушенной базы данных. Существуют компании, которые предоставляют сервис по восстановлению и починке сильно разрушенных баз данных Firebird и InterBase — хотя -во время написания этой книги автор услышала о базе данных Firebird, которая была разрушена так, что для ее исправления требовался чародей!
ПРИЛОЖЕНИЕ 5 Инструменты администрирования Одной из "приятных проблем" новичка в использовании Firebird является выбор ин- струментов. Почему? Потому что сообщество Firebird имеет огромное количество прекрасных инструментов, как коммерческих, так и свободно распространяемых. Чуть ли не все коммерческие продавцы предоставляют свободные пробные версии, так что вашей самой большой проблемой является выбор такого инструмента, кото- рый лучше всего подходил бы вам. Графические инструменты администратора Следующий список является лишь выборкой некоторых наиболее популярных эле- ментов. Полный список см. на http://www.ibphoenix.com/main.nfs?a=ibphoenix &page=ibp_admin_tools. Database Workbench Database Workbench может соединяться с любым сервером Firebird на любой плат- форме. Он имеет полный визуальный интерфейс, браузер метаданных и зависимо- стей, средства отладки хранимых процедур, инструменты миграции данных, им- порт/экспорт, редактор blob, управление пользователями и полномочиями, генератор тестовых данных, поиск метаданных, репозиторий фрагментов кода, средства печати метаданных, автоматическую генерацию триггеров для автоинкрементных ключей, не чувствительный к регистру поиск столбцов и многое другое. Окружение: Windows. Другая информация: коммерческий программный продукт от Upscene Productions. Свободная пробная версия доступна на http://www.upscene.com. IBExpert Этот нагруженный возможностями инструмент администрирования имеет средства гиперссылок и возможность хранения историй во всех редакторах. Он также имеет SQL Monitor, визуальный конструктор запросов, режимы фильтрации выхода для запросов, фоновое выполнение запросов, прямой импорт данных CSV, завершение
966 Приложение 5 кода, отладку, трассировку и советы по PSQL, настраиваемые шаблоны клавиатуры, редактирование и отображение blob (включая рисунки и двоичные коды), редакторы пользователей и полномочий, режим автоматического предоставления полномочий для создания новых объектов, моделирование сущностей, средство сравнения баз данных с созданием обновляющих скриптов, формирование планов и выполнения, инструменты для скриптов, включая скрипты по выводу данных blob, предваритель- но определенные и определенные пользователем отчеты схемы, мастер резервного копирования, средства диагностики соединения, поддержку нескольких языков и многое другое. Окружение: Windows. Другая информация: коммерческий1 программный продукт от Н-К Software GmbH. Доступно Free Personal Edition (Свободное персональное издание). Также доступна версия для образовательных целей. BlazeTop BlazeTop имеет необычный модульный интерфейс для основных инструментов, что помогает решить проблему "перегрузки возможностей" — вы инсталлируете только модули, которые вы хотите использовать. Он включает интеллектуальный текстовый редактор, который создает шаблоны операторов, а также включает средства управле- ния схемой, SQL Monitor, интерактивное редактирование данных, инспектор объек- тов в стиле Delphi для объектов базы данных и явное управление транзакциями. Он может одновременно запускать множество сессий баз данных. Окружение: Windows. Другая информация: коммерческий программный продукт от Devrace. Свободная пробная версия доступна на http://www.devrace.com/en/blazetop. IB Access Этот кроссплатформенный менеджер баз данных предоставляет графический интер- фейс для реализации всех задач, которые Firebird не доверяет выполнять инструмен- там командной строки, делая его совместимым с Классическими серверами так же, как и с Суперсерверами. Он предоставляет оперативную справку, конфигурацию ра- бочего стола для пользователя или для сервера, а также возможность запуска не- скольких экземпляров для выполнения долгих задач в фоновом режиме. В нем вы можете отслеживать операторы SQL в процессе их выполнения; просматривать и управлять объектами данных, полномочиями и полями blob; выполнять запросы с заменяемыми параметрами; выполнять скрипты шаг за шагом. Окружение: Linux, Windows. 1 Полная коммерческая версия IBExpert бесплатна для использования в России и странах СНГ, там где в качестве кодировки по умолчанию для операционной системы Windows уста- новлена таблица символов 1251. —Прим. науч. ред.
Инструменты администрирования 967 Другая информация: открытые исходные тексты (Mozila Public License), свободный программный продукт от Тони Мартира (Toni Martir). Доступны исходные тексты. См. http://www.ibaccess.org, IBAdmin Это онлайновый и офлайновый менеджер баз данных, предоставляющий визуальное проектирование баз данных — инструменты case, — отладчик SQL, анализатор кода, завершение кода, сравнение базы данных с необязательным скриптом обновления, макровозможности, инструменты выполнения скриптов, контроль версий для моду- лей PSQL, инструмент исследования базы данных, средства для добавления ваших собственных инструментов (предоставляется пример). Окружение: Windows, Linux (усеченный вариант). Другая информация: коммерческий программный продукт от Sqlly Development. Продукт доступен в профессиональной, стандартной и "живой" версиях. Загрузка пробной версии осуществляется с http://www.sqlly.com. IB_SQL Компактная утилита с множеством инструментов, является заменой старой програм- мы Borland W1SQL с "живым" браузером данных и метаданных, выделением мета- данных, перекачкой данных, экспортом, управлением пользователями, отслеживани- ем SQL и профилированием, использованием скриптов, интерактивными запросами с заменяемыми параметрами и отображением плана. Содержит средства, которые ав- томатически сохраняют подробности подключения к различным базам данных и ин- струмент Query Forms для сохранения указанных запросов для будущего использо- вания. IB SQL может быть загружен в виде двоичных кодов или настраиваемых и компили- руемых в Delphi исходных текстов, поставляемых в пробном комплекте IB Objects (IB SQL.dpr в корневом каталоге). Компилированный таким образом IB SQL пока- жет модальный экран при старте вне IDE Delphi или C++ Builder. Окружение: Windows. Другая'информация: свободный программный продукт. Исходные тексты являются субъектом лицензии Trustware. См. http://www.ibobjects.com, там содержатся исход- ные тексты IB SQL и самый последний пробный комплект IBO. IB ОС on sole Это заново спроектированная версия утилиты IBOConsole, которая поставляется с пакетом открытых текстов InterBase 6 с улучшениями, исправленными ошибками и поддержкой для разработки Firebird. Она реализует Services API (управление пользо- вателями, резервное копирование и др.), а также возможности интерактивных запро-
968 Приложение 5 сов и скриптов. Уровнем абстракции API является IB Objects, а не Borland InterBaseXpress. Окружение: Windows. Другая информация: свободный с открытыми кодами (InterBase Public License) про- граммный продукт от Lorenzo Mengoni. Доступны исходные коды. Загрузка и под- робности на http://www.men-goni.it. Инструменты резервного копирования DBak DBak является альтернативной утилитой резервного копирования базы данных, кото- рая не использует gbak или Services API. Она копирует базу данных в новую, исполь- зуя скрипты DDL и перемещение данных. Ее абстрактным уровнем API является IB Objects. Поскольку DBak оперирует с одной транзакцией snapshot, она получает надежный мгновенный снимок состояния базы данных на момент старта копирования. Вместе с копированием и восстановлением DBak также может выполнять прямое пересозда- ние, генерируя рабочую копию базы данных без каких-либо промежуточных стадий. Процесс работы может настраиваться различными способами, делая работу особенно полезной для помощи в управляемом восстановлении при некоторых вариантах раз- рушения и при автоматическом обновлении схемы. Поставка включает графический пользовательский интерфейс и консольную версию для пакетных и запланированных операций. Окружение: Windows. Другая информация: выполняемые файлы и файлы помощи от Telesis Computing Pty Ltd являются свободно распространяемыми. Полная исходная версия доступна по низкой цене под лицензией Trustware. Информация и загрузка могут быть найдены на http.7/www.telesiscomputing.com.au/dbk.htm. Обратите внимание, что это не gbak, важно прочесть документацию и понимать разницу. gbak BackupRunner Это элегантная, небольшая по объему графическая программа пользователя для ути- литы Firebird gbak. Она не использует Services API, а предоставляет вместо этого флаги для установки переключателей gbak. По требованию утилита формирует ин- формационные сообщения прямо в текстовое окно, которое может быть сохранено в качестве протокола. Она требует присутствия gbak.exe. Окружение: Windows. Другая информация: свободные двоичные коды от Marco Wobben. Информация и загрузка могут быть найдены на http://www.bcp-software.nl/backuprunner.
Инструменты администрирования 969 Time То Backup Это набор программ для Firebird, с помощью которого устанавливают копирование по расписанию в виде сервиса Windows или демона Linux на одной или более хост- машинах. Содержит конфигурацию графического интерфейса и управляемый интер- фейс для локального или удаленного использования. Конфигурация интерфейса только для командной строки доступна также для Linux. Окружение: Windows, Linux. Другая информация: программное обеспечение с открытыми исходными кодами на основании лицензии LGPL от Sqlly Development. Инструменты свободно распростра- няются с полными исходными кодами на Object Pascal. Информацию и загрузку см. на http://www.sqlly.com/timetobackup.asp. Разное Инструменты починки базы данных IBSurgeon Компания IBSurgeon распространяет множество инструментов для диагностики и починки разрушенных баз данных, а также для восстановления поврежденных ре- зервных копий БД. Ее инструменты диагностики IBFirstAid Diagnostician и IBSurgeon Viewer могут быть свободно загружены. Инструменты, выполняющие фактическую починку, должны быть куплены. Некоторые виды ошибок невозможно восстановить автоматическими инструментами, но компания утверждает, что разрушенные базы данных могут быть переведены в работоспособное состояние в 70% случаев. Прочесть об этих инструментах и получить советы, как поступать, когда у вас есть разрушенная база данных, которую не может починить gfix, можно на http://www.ib- aid.com. Анализатор статистики IBAnalyst IBAnalyst— это инструмент для анализа статистики баз данных InterBase или Firebird, поиска проблем в производительности базы данных, сопровождении, или работы приложений. IBAnalyst решает две важные задачи: ♦ визуализирует статистику базы данных и сообщает об актуальных или возможных проблемах; ♦ предлагает проверенные советы по улучшению производительности базы данных, на основе автоматического анализа ее статистики. Рекомендуется для регулярного изучения состояния базы данных. Бесплатен для России и стран СНГ: www.ibase.ru/download/ibanalyst_r.zip.
970 Приложение 5 Interbase DataPump InterBase DataPump позволяет довольно просто и с полным контролем над процессом перекачивать данные и выполнять их миграцию из любого источника ADO/BDE/ODBC (такого как dBase, Paradox, Access, MSSQL, Sybase, Oracle, DB2, InterBase и т. д.), а также из родных баз данных Firebird в новую базу данных Firebird. Он определяет такие важные детали, как порядок создания объектов с учетом зави- симостей. Инструмент может генерировать полный скрипт SQL для создания базы данных Fire- bird на основе метаданных из источника ADO/BDE/ODBC, предоставляя при этом полный список типов данных, индексов, ключей, автоинкрементных полей, ссылоч- ной целостности и проверочных ограничений. Он может создавать требуемые гене- раторы и триггеры для автоинкрементных полей и набор правильных начальных зна- чений. Окружение: Windows. Другая информация: программное обеспечение от Clever Components. Подробную информацию и загрузку можно найти на http://www.clevercomponents.com /products/datapump/ibdatapump.asp. Advanced Data Generator для Firebird Advanced Data Generator является инструментом для генерации тестовых данных, которые имитируют реальные данные. Существует возможность генерации случай- ных адресов, поддержка ссылочной целостности, поддержка blob. Содержит боль- шую библиотеку данных по компаниям, персоналу, географическим названиям; мо- жет также использовать ваши данные. Можно организовать очередь множества про- ектов. Окружение для генерации данных: Windows. Другая информация: коммерческий программный продукт от Upscene Productions. Информацию и загрузку пробной версии можно найти на http://www.upscene.com. Менеджеры полномочий Попробуйте использовать эти инструменты для упрощения управления пользовате- лями, ролями и полномочиями SQL: ♦ Grant Manager от Eadfost на http://www.eadsoft.com/english/products/ grantmanager. Пробная версия этого продукта под Windows доступна в качестве 30-дневной утилиты в виде отдельной программы или как Delphi SDK; ♦ Grant Master от Studio Plus Inc. Подробную информацию об этой программе под Windows и загрузку 30-дневной пробной версии можно найти на http:// www.studioplus.com.ua/GrantMaster/GrantMaster.phtml.
Инструменты администрирования 971 Где посмотреть информацию о других инструментах "Основной список" инструментов от сторонних разработчиков можно найти в разде- ле Downloads Cohtributed на http://www.ibphoenix.com2. Разработчики инструментов также посылают объявления в почтовый список на firebird-tools@yahoogroups.com. Вы можете подписаться на этот список, перейдя на http://groups.yahoo.com/group/firebird-tools или просто просматривая зеркальный сайт (egroups.ib-tools) на news://news.atkin.com. Не рассматривайте поиск в Google как способ найти инструменты для конкретной задачи. Обычно ввод слов "firebird", "interbase" и ключевого слова, идентифицирую- щего задачу, сгенерирует хорошие ссылки. 2 А также на сайте www.ibase.ru. — Прим. науч. ред.
ПРИЛОЖЕНИЕ 6 Пример базы данных Пример базы данных инсталлируется вместе в Firebird в каталог /examples в корне- вом каталоге Firebird. В Firebird 1.0 это база данных диалекта 1, ее имя employee.gdb. В версии 1.5 она имеет диалект 3 и имя employee.fdb. Структурно это та же самая база данных. Эта база данных была создана много лет тому назад, возможно, для тренировок пер- сонала поддержки. Она не является образцом хорошего моделирования. Более того, она не имеет набора символов по умолчанию. Однако она содержит данные, с кото- рыми можно экспериментировать. База данных поставляется в виде резервной копии (employee.gbk и employee.fbk соот- ветственно), следовательно, не имеет значения, как вы будете перемешивать в ней данные — вы всегда сможете восстановить нормальную копию. Создание базы данных Employee с помощью скриптов После выполнения инсталляции Firebird база данных Employee будет создана и будет сделана ее резервная копия из скриптов. Скрипты empddl.sql (который создает мета- данные) и empdll.sql (который заполняет базу данных) доступны в области Downloads на http://www.apress.com с некоторыми изменениями. ♦ Добавлен набор символов по умолчанию, который установлен в ISO8859 1, со- - вместимый с данными на английском языке в empdml.sql. ♦ Был добавлен оператор set sql dialect з, поскольку без него некоторые инстру- менты создавали базу данных с диалектом 1, а отдельные эксперименты, которые проводились с родным диалектом SQL Firebird, завершались с ошибкой или дава- ли неожиданный результат. ♦ Был добавлен полный путь в оператор create database, чтобы показать, как реаль- но создаются базы данных. Эта строка создает базу данных на сервере P0S1X в каталоге с именем /data/examples. Закомментированная строка будет создавать ба- зу данных в каталоге C:\data\examples. Комментируйте, убирайте комментарий и изменяйте код в соответствии с вашими потребностями. ♦ Кавычки в пути у create database были заменены на апострофы.
Пример базы данных 973 ♦ Скрипт empdml.sql начинается с оператора set names iso8859_i для обеспечения того, что дальнейшие текстовые данные будут сохраняться в правильном наборе символов. Несмотря на эти небольшие изменения, база данных Employee остается в значитель- ной степени тем, чем она всегда и была, — примером того, как не надо проектиро- вать базы данных! Новый пример базы данных для Firebird сейчас создается. Он должен быть готов к концу 2004 года на http://www.apress.com и на других сайтах сообщества Firebird. В конце концов этот пример будет поставляться в комплекте Firebird.
ПРИЛОЖЕНИЕ 7 Ограничения Firebird Большинство фактических ограничений Firebird практически шире того, что нужно в программах. Например, вы можете определить до 32 7671 столбцов в таблице, однако зачем вы будете это делать? В табл. 7.1 представлены теоретические и практические ограничения, применимые к Firebird 1.0.x и 1.5. Некоторые из этих ограничений бу- дут изменены в сторону улучшения в следующих версиях, так что просматривайте заметки по релизу, чтобы отследить изменения. Таблица П7.1. Ограничения Firebird 1.0.x и 1.5 1 2 Объект Элемент Firebird 1.0.x Firebird 1.5 Замечания Идентифи- каторы Почти все объекты 31 символ 31 символ Нельзя использовать символы вне диапазона US ASCII (ASCIIZ) Имена ограни- чений 27 символов 27 символов Даты Самые ранние и самые поздние даты 1 января 100 г. Самая поздняя 31 декабря 9999 г. Замечание: считается, что сервер может ава- рийно завершаться, если систем- ная дата на сервере установлена больше, чем 2039 . 1 На самом деле, как показывает практика, не более 16 000 столбцов integer.— Прим, науч. ред. 2 Мало какие нынешние приложения переживут 19 января 2038 года — это проблема пере- полнения даты еще более худшая, чем проблема 2000 года. Устаревший формат даты в язы- ке С (структура time_t) не способен хранить года более 2038. С другой стороны, так долго приложения не эксплуатируются, а для внедряемой сейчас 64-битовой архитектуры и новых приложений такой проблемы нет. — Прим. науч. ред.
Ограничения Firebird 975 Таблица П7.1 (продолжение) Объект Элемент Firebird 1.0.x Firebird 1.5 Замечания Сервер Максимальное количество под- ключенных кли- 3 ентов 1 024 (TCP/IP) 1 024 (TCP/IP) Теоретическое ограничение меньше для Windows с имено- ванными каналами (NetBEUI) — сервер, скорее всего, зависнет при более чем 930 одновремен- ных соединениях. Практически нормальным будет не более 150 одновременных подключений клиентов Суперсервера при обычных интерактивных прило- жениях для сервера с низкими спецификациями. Для Классиче- ского сервера это количество может быть меньше по причине того, что каждый клиент исполь- зует больше ресурсов Максимальное количество баз данных, откры- тых в одной транзакции Количество баз данных, открытых при запуске транзакции с по- мощью isc_start_multiple(), ограничивается только доступ- ными системными ресурсами. Транзакция, запущенная с по- мощью isc_start__transaction (), огра- ничивает количество одновре- менно подключенных баз данных 16 База данных Количество таб- лиц 32 767 32 767 Максимальный размер 7 Тбайт 7 Тбайт Приблизительное теоретическое ограничение. Не известна база данных Firebird с таким количест- вом записей, чтобы ее размер превышал 7 Тбайт Максимальный размер файла Зависит от файловой системы. В FAT32 и ext2 2 Гбайт. Более старая NTFS и ext3 обычно дают 4 Гбайт. Многие 64-битовые фай- ловые системы не устанавливают ограничений на размер файла с совместным доступом 3 Это число больше реального числа, которое способна обеспечить операционная система. Например, RedHat Linux без изменения настроек и перекомпиляции ядра допускает до -600 соединений. Чаще всего проблема с числом соединений возникает из за "несконфигурирован- ного" xinetd на Linux, где по умолчанию задано ограничение в -50—60 соединений на кон- кретное приложение. — Прим. науч. ред.
976 Приложение 7 Таблица П7.1 (продолжение) Объект Элемент Firebird 1.0.x Firebird 1.5 Замечания Максимальное количество файлой в базе данных Теоретически 216 (65 536) вклю- чая файлы оперативной копии (shadow). Порог будет ниже, если операционная система наклады- вает ограничения на количество файлов, которое может быть одновременно открыто в одном процессе. Некоторые позволяют увеличить эту границу Максимальный размер страни- цы 16 386 байт 16 386 байт Другими размерами являются 1024, 2048, 4096 (по умолчанию) и 8192 байт Максимальное количество бу- феров кэша 65 536 стра- ниц 65 536 стра- ниц Практическое ограничение зави- сит от доступного объема RAM. Общий размер (страницы * раз- мер страницы для Суперсерве- ра', страницы * размер страницы * количество одновременных пользователей для Классиче- ского сервера) никогда не дол- жен превышать половины дос- тупной памяти RAM. Рассматри- вайте в качестве практического ограничения 10 000 страниц и увеличивайте или уменьшайте, отходя от этого количества, в соответствии с требованиями производительности Таблицы Максимальное количество вер- сий для структу- ры таблицы 255 255 Firebird сохраняет не более 255 форматов для каждой табли- цы. Версия формата увеличива- ется на 1 каждый раз, когда вы- полняется изменение метадан- ных. Когда таблица достигает ограничения, вся база данных становится недоступной — нужно сделать ее резервную копию и выполнить восстановление Максимальный размер строки 64 Кбайт 64 Кбайт Подсчитывается в байтах. Столбцы массивов и blob каждый занимает 8 байт для хранения идентификатора; varchar — дли- на в байтах + 2; char — длина в байтах; smallint — 2; integer, FLOAT, DATE И TIME — 4; BIDINT, DOUBLE PRECISION И TIMESTAMP — 8; NUMERIC И decimal — 4 или 8 в зависимости от точности. Системные таблицы имеют ограничение на размер страницы 128 Кбайт
Ограничения Firebird 977 Таблица П7.1 (продолжение) Объект Элемент Firebird 1.0.x Firebird 1.5 Замечания Максимальное количество 4 строк 232 строки 232 строки Больше или меньше. Строки под- считываются с помощью 32-би- тового беззнакового целого для таблиц и 32-битового для индек- са. Таблица с длинной строкой — имеющая много полей или с очень длинными полями — может хранить меньше строк, чем таб- лица с очень короткими строками. Все строки — включая удален- ные — попадают в это число; поля blob, хранимые на страни- цах данных таблицы, также вхо- дят в это число Максимальное количество столбцов Зависит от используемых типов данных (см. Максимальный раз- мер строки) Максимальное количество индексов на таблицу 64 256 Максимальный размер внешне- го файла 4 Гбайт в Windows NTSF, 2 Гбайт в Windows FAT32, Linux ext2, ext3 и Solaris Индексы Максимальный 5 размер 252 байт 252 байт Этот теоретический максимум применяется к индексу из одного столбца, где набор символов является однобайтовым и ис- пользует порядок сортировки по умолчанию (двоичный). Подсчи- тываются байты, а не символы. Практический максимум умень- шается для составных индексов, многобайтовых наборов симво- лов и сложных порядков сорти- ровки. Например, индекс для одного столбца, использующего 3-байтовые символы UNICODE_FSS, может иметь максимум (253/3) = 84 символа. Некоторые последовательности сортировки для ISO8859 исполь- зуют до 4 байт на символ только для атрибутов сортировки 4 Реальное физическое ограничение на объем одной таблицы — 36.7 Гбайт. Это примерно 600 миллионов записей, состоящих из двух integer. Ограничение связано с внутренним 32-разрядным идентификатором. В Firebird 2.0 это ограничение устранено (используются 64(40)-битовые идентификаторы). — Прим. науч. ред.
978 Приложение 7 Таблица П7.1 (продолжение) Объект Элемент Firebird 1.0.x Firebird 1.5 Замечания Максимальное количество сег- ментов 16 16 Запросы Максимальное количество со- единяемых таб- лиц 256 256 Теоретическое ограничение Максимальное количество вло- женных подза- просой Теоретически предела нет, но большая вложенность подзапро- сов ухудшает производитель- ность. Производительность и потребление ресурсов определя- ется практическими лимитами, конкретно для каждого запроса Максимальный размер столбца, составляющего ПОЛЯ ORDER BY 32 Кбайт 32 Кбайт Модули PSQL Максимальный размер BLR 48 Кбайт 48 Кбайт Текст хранимых процедур и триг- геров компилируется в байт-код (BLR), который более компактен, чем исходный текст. Если вы все- таки обнаружили ограничение, попробуйте разбить вашу мону- ментальную процедуру на не- сколько частей, вызываемых из главной процедуры Максимальное количество событий в про- цедуре или триггере Нет ограни- чения Нет ограни- чения Практический лимит совпадает с ограничением на размер BLR Количество ре- курсивных вызо- вов 750 на Windows, 1000 на плат- формах POSIX BLOB Максимальный размер blob Максимум зависит от размера страницы. Для страницы 2 Кбайт максимальный размер — 512 Мбайт. Для 4 Кбайт — 4 Гбайт. Для страниц размера 4 и 8 Кбайт — 32 и 256 Гбайт соот- ветственно 5 В Firebird 2.0 максимальный размер ключа индекса— % размера страницы. Например, для страницы 4 Кбайт это 1024 байт. — Прим. науч. ред.
Ограничения Firebird 979 Таблица П7.1 (окончание) Объект Элемент Firebird 1.0.x Firebird 1.5 Замечания Максимальный размер сегмента blob хранятся посегментно, тео- ретический максимальный раз- мер которого не более 64 Кбайт. В DSQL нет возможности указать размер сегмента, поскольку его устанавливает клиентская про- грамма или библиотека-ком- понент, как правило,игнорируя размер сегмента, указанный в определении blob
ПРИЛОЖЕНИЕ 8 Наборы символов и порядков сортировки В табл. П8.1 содержатся наборы символов и порядки сортировки, которые существо- вали на момент создания Firebird 1.5.0. Некоторые из указанных элементов недос- тупны в более ранних версиях Firebird. Если вы инсталлировали более позднюю вер- сию, и набор символов или порядок сортировки, нужные вам, здесь не указаны, обра- титесь к заметкам по релизу вашей версии и любой другой версии после 1.5, чтобы узнать были ли они добавлены. Таблица П8.1. Наборы символов и порядок сортировки для Firebird 1.5.0 ID Название Байтов на символ Порядок сортировки Язык Алиас 2 ASCII 1 ASCII Английский ASCII7 USASCII 56 BIG_5 2 BIG_5 Китайский, Вьетнамский, Корейский BIG5, DOS_950, WIN_950 50 CYRL 1 CYRL DB_RUS PDOX_CYRL Русский, Русский dBase, Русский Paradox 10 DOS437 1 DOS437 DB_DEU437 DB_ESP437 DB_FIN437 DB.FRA437 DBJTA437 DB_NLD437 DB_SVE437 DBJJK437 Английский (США), Немецкий dBase, Испанский dBase, Финский dBase, Французский dBase, Итальянский dBase, Голландский dBase, Шведский dBase, Английский (Великобритания) dBase, DOS_437
Наборы символов и порядков сортировки 981 Таблица П8.1 (продолжение) ID Название Байтов на символ Порядок сортировки Язык Алиас DB_US437 PDOX_ASCII PDOX_SWEDFIN PDOXJNTL Английский (США) dBase, Кодовая страница Paradox— ASCII, Paradox Шведская / Финская кодовые страницы, Paradox международный анг- лийский кодовая страница 9 DOS737 1 DOS737 Греческий DOS-737 15 DOS775 1 DOS775 Страны Балтии DOS_775 11 DOS850 1 DOS850 DB_DEU850 DB_ESP850 DB_FRA850 DB_FRC850 DBJTA850 DB_NLD850 DB_PTB850 DB_SVE850 DBJJK850 DB_US850 Латинский I (нет символа Евро), Немецкий, Испанский, Французский, Французский — Канада, Итальянский, Голландский, Португальский — Бразилия, Шведский, Английский — Великобритания, Английский — США DOS_850 45 DOS852 1 DOS852 DB_CSY DB_PLK DB_SLO PDOX_CSY PDOX_HUN PDOX_PLK PDOX_SLO Латинский II, Чешский dBase, Польский dBase, Словацкий dBase, Чешский Paradox, Венгерский Paradox, Польский Paradox, Словацкий Paradox DOS_852 46 DOS857 1 DOS857 DB_TRK Турецкий, Турецкий dBase DOS_857 16 DOS858 1 DOS858 Латинский I с символом Евро DOS_858 13 DOS860 1 DOS860 DB_PTG860 Португальский, Португальский dBase 1
982 Приложение 8 Таблица П8.1 (продолжение) ID Название Байтов Ча символ Порядок сортировки Язык Алиас 47 DOS861 1 DOS861 PDOXJSL Исландский, Исландский Paradox DOS_861 17 DOS862 1 DOS862 Иврит DOS_862 14 DOS863 1 DOS863 DB_FRC863 Французский — Канада, Французский dBase — Канада DOS_863 18 DOS864 • 1 DOS864 Арабский DOS_864 12 DOS865 1 DOS865 DB_DAN865 DB_NOR865 PDOX_NORDAN4 Скандинавские, Датский dBase, Норвежский dBase, Paradox Норвегия и Дания DOS_865 48 DOS866 1 DOS866 Русский DOS_866 49 DOS869 1 DOS869 Современный греческий DOS_869 6 EUCJ.0208 2 EUCJ_0208 Японские EUC EUCJ 57 GB_2312 2 GB_2312 Упрощенный китайский (Гонконг, Корея) DOS_936, GB2312, WIN_936 21 ISO8859_1 1 ISO8859_1 DA_DA DE_DE DU_NL ENJJK EN_US ES_ES FI_FI FR_CA FR_FR ISJS ITJT NO_NO PT_PT sv_sv Латинский I, Датский, Немецкий, Голландский, Английский, Великобритания, Английский, США, Испанский, Финский, Французский, Канада, Французский, Исландский, Итальянский, Норвежский, Португальский, Шведский ANSI, ISO88591, LATIN 1
Наборы символов и порядков сортировки 983 Таблица П8.1 (продолжение) ID Название Байтов на s символ Порядок сортировки Язык Алиас 22 ISO8859_2 1 ISO8859_2 CS_CZ ISO_HUN Латинский 2 — Центральная Европа (хорватский, чешский, венгерский, польский, румын- ский, сербский, словацкий, сло- венский), .Чешский, Венгерский ISO-8859-2, ISO88592, LATIN2 23 ISO8859_3 1 ISO8859J3 Латинский 3 — Южная Европа (мальтийский, эсперанто) ISO-8859-3, ISO88593, LATIN3 34 ISO8859_4 1 ISO8859_4 Латинский 4 — Северная Евро- па (эстонский, латышский, ли- товский, гренландский, саам- . ский) ISO-8859-4, ISO88594, LATIN4 35 ISO8859_5 1 ISO8859_5 Кириллица (русский) ISO-8859-5, ISO88595 36 ISO8859_6 1 ISO8859_6 Арабский ISO-8859-6, ISO88596 37 ISO8859_7 1 ISO8859_7 Греческий ISO-8859-7, ISO88597 38 ISO8859_8 1 ISO8859_8 Иврит ISO-8859-8, ISO88598 39 ISO8859_9 1 ISO8859_9 Латинский 5 ISO-8859-9, ISO88599, LATIN5 40 ISO8859_1 3 1 ISO8859_13 Латинский 7 — Балтика ISO-8859- 13, ISO885913, LATIN 7 44 KSC.5601 2 KSC_5601 KSCJDICTIONARY Корейский, Корейский — словарный поря- док сортировки DOS_949, KSC5601, WIN_949
984 Приложение 8 Таблица П8.1 (продолжение) ID Название Байтов на символ Порядок сортировки Язык Алиас 19 NEXT 1 NEXT NXT_DEU NXT_FRA NXTJTA NXT_US Кодирование NeXTSTEP, Немецкий, Французский, Итальянский, Английский, США 0 NONE 1 NONE Нейтральная кодовая страница. Перевод в верхний регистр ограничен кодами ASCII 97—122 1 OCTETS 1 OCTETS Двоичный символ BINARY 5 SJIS_0208 2 SJIS_0208 Японский SJIS 3 UNICODE, FSS 3 UNICODE_FSS UNICODE SQL_TEXT, UTF-8, UTF8, UTF_FSS 51 WIN1250 1 WIN1250 PXW_CSY PXW_HUN PXW_HUNDC PXW_PLK PXW_SLO ANSI, Центральная Европа, Чешский, Венгерский, Венгерский, словарная сорти- ровка, Польский, Словацкий WIN_1250 52 WIN1251 1 WIN1251 WIN1251JJA PXW_CYRL ANSI кириллица, Украинский, Paradox кириллица (русский) WIN.1251 53 WIN1252 1 WIN1252 PXWJNTL PXWJNTL850 PXW_N0RDAN4 PXW_SPAN PXW_SWEDFIN ANSI — Латинский I, Английский интернациональ- ный, Paradox многоязыковой Латинский I, Норвежский и датский, Paradox испанский, Шведский и финский WIN.1252
Наборы символов и порядков сортировки 985 Таблица П8.1 (окончание) ID Название Байтов на символ Порядок сортировки Язык Алиас 54 WIN1253 1 WIN1253 PXWJ3REEK ANSI греческий, Paradox греческий WINJ253 55 WIN1254 1 WIN1254 PXW_TURK ANSI турецкий, Paradox турецкий WINJ254 58 WIN1255 1 WIN1255 ANSI иврит WIN_1255 59 WIN1256 1 WIN1256 ANSI арабский WIN_1256 60 WIN1257 1 WIN1257 ANSI стран Балтии WIN.1257
ПРИЛОЖЕНИЕ 9 Системные таблицы и просмотры Когда вы создаете базу данных, Firebird начинает с создания своих собственных таб- лиц, в которых он сохраняет метаданные всех объектов базы данных — не только определенных вами объектов, но также и своих собственных внутренних объектов. Эти таблицы называются системными таблицами. Просматривая определения мета- данных для системных таблиц, вы найдете листинги DDL для множества просмотров для системных таблиц, которые могут оказаться вам полезными. Системные таблицы Описания в этом разделе помогут вам в проектировании запросов для понимания и администрирования ваших баз данных. Для изменения метаданных существуют операторы DDL. Вообще не рекомендуется использовать операторы SQL для изме- нения таблиц метаданных. Риск разрушения базы данных при выполнении таких действий весьма высок. В таблицах применяются следующие аббревиатуры: ♦ 1DX—индексировано; ♦ UQ — уникальное. Где существуют составные индексы, приведены цифры для указания последователь- ности индексных сегментов. rdb$character_sets хранит ключи для наборов символов, доступных базе данных. Идентификатор столбца Тип IDX UN Описание RDB$CHARACTER_SET_NAME CHAR(31) Y Y Имя набора символов, из- вестного в Firebird RDB$FORM_OF_USE CHAR(31) Не используется RDB$NUMBERJ3F_CHARACTERS INTEGER Количество символов в набо- ре (не используется для дос- тупных наборов символов) RDB $ DEFAULT_COLLATE_NAME CHAR(31) Название двоичной последо- вательности сортировки для этого набора символов. Это название всегда совпадает с названием набора символов
Системные таблицы и просмотры 987 (окончание) Идентификатор столбца Тип IDX UN Описание RDB 5 CHARACT ER_SET_I D' SMALLINT Y Y Уникальный идентификатор для этого набора символов, если он используется RDB$SYSTEM_FLAG SMALLINT Будет 1, если набор симво- лов был определен в системе при создании базы данных; 0 для набора символов, оп- ределенного пользователем RDB$DESCRIPTION BLOB TEXT Для хранения документации RDB$FUNCTION_NAME . CHAR(31) Не используется, но может стать доступным для наборов символов, определенных пользователем, доступ к ко- торым осуществляется через внешнюю функцию RDB$BYTES_PER_CHARACTER SMALLINT Размер символов в наборе, указанный в байтах. Напри- мер, UNICODE_FSS использует 3 байта на символ rdb$check_constraints содержит перекрестные ссылки имен и триггеров для ограни- чений CHECK И NOT NULL. Идентификатор столбца Тип IDX UN Описание RDB$CONSTRAINT_NAME CHAR(31) Y Имя ограничения RDB$TRIGGER_NAME CHAR(31) Для ограничения check это имя триггера, который под- держивает данное ограниче- ние. Для ограничения not null это имя столбца, к ко- торому применяется ограни- чение — имя таблицы может быть найдено через имя ог- раничения rdb$collations хранит определения последовательностей сортировки. Идентификатор столбца Тип IDX UN Описание RDB$COLLATION__NAME VARCHAR(31) Y Y Имя последовательности сортировки RDB $ COLLATI ON__I D SMALLINT Y(1) Y(1) Вместе с идентификатором набора символов является уникальным идентификато- ром последовательности сортировки
988 Приложение 9 (окончание) Идентификатор столбца Тип IDX UN Описание RDB$CHARACTER_SET_I В> SMALLINT Y(2) Y(2) Вместе с идентификатором последовательности сорти- ровки является уникальным идентификатором RDB$ COLLATION_ATTRIBUTES CHAR(31) Не используется RDB$SYSTEM_FLAG SMALLINT Определенное пользовате- лем = 0; определенное в сис- теме = 1 или выше RDB$DESCRIPTION BLOB TEXT Для хранения документации RDB$FUNCTION_NAME CHAR(31) В настоящий момент не используется rdb$database является файлом из одной записи, содержащей основную информацию о базе данных. Идентификатор столбца Тип IDX UN Описание RDB$DESCRIPTION BLOB TEXT Текст комментария, включен- ный в оператор create DATABASE/CREATE SCHEMA, предназначен для того, чтобы быть записанным здесь. Это- го не происходит. Однако вы можете сюда добавить любое количество текста с целью документирования. Он сохра- нится после резервного копи- рования и восстановления RDB$RELATION_ID SMALLINT Число, которое каждый раз увеличивается на единицу, когда к базе данных добавля- ется новая таблица или про- смотр RDB$SECURITY_CLASS CHAR(31) Может ссылаться на класс безопасности, определенный В RDB$SECURITY_CLASSES, ДЛЯ обращения к общим для базы данных ограничениям доступа RDBS CHARACTER_SET_NAME CHAR(31) Набор символов по умолча- нию для базы данных. null — набор символов NONE
Системные таблицы и просмотры 989 rdb$dependencies хранит зависимости между объектами базы данных. Идентификатор столбца Тип IDX UN Описание RDB$DEPENDENT_NAME CHAR(31) Y Имена просмотра, процеду- ры, триггера или вычисляе- мого столбца, на которые происходит ссылка в этой записи RDB $ DEPENDED_ON_NAME CHAR(31) Y Таблица, на которую ссы- лается просмотр, процеду- ра, триггер или вычисляе- мый столбец RDBSFIELD_NAME VARCHAR (31) Имя столбца в таблице зависимости, на который ссылается просмотр, про- цедура, триггер или вычис- ляемый столбец RDB$DEPENDENT_TYPE SMALLINT Идентифицирует тип объек- та (просмотр, процедура, триггер, вычисляемый стол- бец). Номер приходит из таблицы rdb$types — объ- екты нумеруются в соответ- ствии RDB$FIELD_NAME = 'RDB$OBJECT_TYPE' RDBSDEPENDED_ON_TYPE SMALLINT Идентифицирует тип зави- симого объекта (тот же принцип, что и в RDB$DEPENDENT_TYPE) rdb$exceptions хранит пользовательские исключения. Идентификатор столбца Тип IDX UN Описание RDB $ EXC EPTION_NАМЕ CHAR(31) Y Y Имя пользовательского исключения RDB$EXCEPTION_NUMBER INTEGER Y Y Назначенный системой уникальный номер исклю- чения RDBSMESSAGE VARCHAR(78) Текст пользовательского сообщения RDB$DESCRIPTION BLOB TEXT Может быть использовано для документации RDB$SYSTEM_FLAG SMALLINT Определенное пользовате- лем = 0; определенное сис- темой = 1 или выше
990 Приложение 9 rdb$field_dimensicns сохраняет информацию о размерностях столбцов массивов. Идентификатор столбца Тип IDX UN Описание RDBS FIELD_NAME CHAR(31) Y Имя столбца массива. Должно содержаться в RDBSFIELD-NAME В таблице RDBSFIELDS RDB$ DIMENSION SMALLINT Определяет одну размер- ность столбца массива. Первым значением являет- ся 0 RDB$LOWER_BOUND INTEGER Нижняя граница этой раз- мерности RDBSUPPER-BOUND INTEGER Верхняя граница этой раз- мерности rdb$fields хранит определения доменов и имен столбцов для таблиц и просмотров. Каждая строка для столбца, не являющегося доменом, соответствует строке в rdb$relation_fields. В действительности каждый экземпляр в RDB$fields является доменом. Например, вы можете выполнить следующее: CREATE TABLE ATABLE ( EXAMPLE VARCHAR(LO) CHARACTER SET ISO8859-1); COMMIT; SELECT RDB$FIELD_SOURCE FROM RDB$RELATION-FIELDS WHERE RDBSRELATION—NAME = 'ATABLE' AND RDB$FIELD_NAME = 'EXAMPLE'; RDB$FIELD_SOURCE SQL$99 /* */ ALTER TABLE ATABLE ADD EXAMPLE2 SQL$99; COMMIT; Добавлен новый столбец, имеющий те же атрибуты, что и исходный. Идентификатор столбца Тип IDX UN Описание RDBS FIELD-NAME CHAR(31) Y Y Для доменов это имя домена. Для столбцов таблиц и просмот- ров это внутреннее, уникальное для базы данных имя поля, свя- занное С RDB$FIELD-SOURCE В RDB$RELATION-FIELDS. Замечание: Firebird создает до- мен в этой таблице для каждого определения столбца, которое не наследуется от определенно- го пользователем домена
Системные таблицы и просмотры 991 (продолжение) Идентификатор столбца Тип IDX UN Описание RDBSQUERY_NAME CHAR(31) He используется в Firebird RDBSVALIDATION_BLR , BLOB BLR He используется в Firebird RDB$VALIDATION_SOURCE BLOB TEXT He используется в Firebird RDB?COMPUTED_BLR BLOB BLR Двоичное представление выра- жения SQL, которое использует Firebird для вычисления при обращении к столбцу COMPUTED BY RDB$COMPUTED_SOURCE BLOB TEXT Оригинальный исходный текст выражения, которое определяет столбец computed by RDB?DEFAULT_VALUE BLOB BLR Правило по умолчанию для зна- чения по умолчанию в двоичном виде RDB $ DE FAULT_SOURCE BLOB TEXT То же; в исходном виде RDB$ FIELD__LENGTH SMALLINT Длина столбца в байтах, float, DATE, TIME, INTEGER занимают 4 байта. DOUBLE PRECISION, bigint, timestamp и идентифи- катор blob — 8 байт RDB$FIELD_SCALE SMALLINT Отрицательное число задает масштаб для столбцов decimal И NUMERIC RDB$FIELD_TYPE SMALLINT Числовой код типа данных для столбца: 7 = SMALLINT, 8 = INTEGER, 12 = DATE, 13 = TIME, 14 = CHAR, 16 = BIGINT, 27 = DOUBLE PRECISION, 35 = TIMESTAMP, 37 = VARCHAR, 261 = BLOB. Коды ДЛЯ DECIMAL И NUMERIC имеют тот же размер, что и их целые типы, используемые для их хранения
992 Приложение 9 (продолжение) Идентификатор столбца Тип IDX UN Описание RDB?FIELD_SUB_TYPE SMALLINT Подтип blob, а именно: 0 = не типизовано, 1 = текст, 2 = BLR (Binary Language Representation, представление в двоичном ко- де), 3 = ACL (Access Control List, список управления доступом), 5 = закодированные метаданные таблицы, 6 = описание транзак- ций между таблицами, которые не завершились нормально RDB?MISSING_VALUE BLOB BLR Не используется в Firebird RDB?MISSING_SOURCE BLOB TEXT Не используется в Firebird RDB?DESCRIPTION BLOB TEXT Доступно для использования в документации RDB?SYSTEM—FLAG SMALLINT 1 = системная таблица, все дру- гое — таблица, определенная пользователем RDB? QUERY_HEADER BLOB TEXT Не используется в Firebird RDB $ S EGMENT_LENGTH SMALLINT Для столбцов blob требуемая длина буферов blob. Не требу- ется в Firebird RDB?EDIT_STRING VARCHAR(125) Не используется в Firebird RDB?EXTERNAL_LENGTH SMALLINT Длина поля, как она представля- ется для внешней таблицы. Все- гда 0 для обычных таблиц RDB? EXTERNAL_SCALE SMALLINT Коэффициент масштаба для целого поля во внешней табли- це; задается степенью 10, на которую умножается целое RDB?EXTERNAL_TYPE SMALLINT Тип данных поля, как он пред- ставляется во внешней таблице. Типы данных те же самые, что и в обычных таблицах, только включают еще 40 = завершае- мый нулем текст (cstring) RDB?DIMENSIONS SMALLINT Задает количество размерно- стей массива, если столбец был определен как тип массива. Для столбцов, не являющихся мас- сивами, всегда 0 RDB?NULL_FLAG SMALLINT Указывает, может ли столбец принимать пустое значение (null) или не может (1)
Системные таблицы и просмотры 993 (окончание) Идентификатор столбца Тип IDX UN Описание RDB$CHARACTER_LENGTH SMALLINT Длина столбца char или varchar в символах (не в байтах) RDB ? COLLATION_ID SMALLINT Номер идентификатора после- довательности сортировки (если задана) для символьного столб- ца или домена RDB$CHARACTER__SET_ID SMALLINT Номер идентификатора набора символов для символьного столбца, столбца blob или домена. Связан со столбцом RDB$CHARACTER_SET_ID В RDB$CHARACTER_SETS RDB$ FIELD-PRECISION SMALLINT Указывает количество цифр после десятичной точки, доступ- ное для типа данных этого столбца rdb$files хранит множество деталей о вторичных файлах базы данных и о файлах оперативной копии. Идентификатор столбца Тип IDX UN Описание RDB$FILE_NAME VARCHAR(253) Имя вторичного файла (тома) базы данных в многотомной базе данных или файла опера- тивной копии RDB?FILE—SEQUENCE SMALLINT Порядковый номер вторичного файла в последовательности томов базы данных или номер в наборе оперативных копий RDB$FILE_START INTEGER Начальный номер страницы RDB$FILE_LENGTH INTEGER Длина файла в страницах базы данных RDB$FILE_FLAGS SMALLINT Для внутреннего использования RDB? SHADOWJJUMBER SMALLINT Номер набора оперативных копий. Требуется для идентифи- кации файла, как члена набора оперативных копий. Если значе- ние null или 0, Firebird предпо- лагает, что файл является вто- ричным файлом в томах базы данных 32 Зак. 420
994 Приложение 9 rdb$filters запоминает и хранит следы информации о фильтрах blob. Идентификатор столбца Тип IDX UN Описание RDB $ FUNCTION_NAME CHAR(31) Уникальное имя фильтра blob RDB$DESCRIPTION BLOB TEXT Написанная пользователем документация о фильтре blob и используемых двух подтипах RDB$MODULE_NAME VARCHAR (253) Имя динамической библиотеки / совместно используемого объекта, где расположен код фильтра blob RDB$ENTRYPOINT CHAR(31) Точка входа в библиотеке фильтров для этого фильтра BLOB RDB$INPUT_SUB_TYPE SMALLINT Y(1) Y(1) Подтип blob для преобразуе- мых данных RDB$OUTPUT_SUB_TYPE SMALLINT Y(2) Y(2) Подтип blob, в который преоб- разуются входные данные RDB$SYSTEM_FLAG SMALLINT Внешне определенный фильтр (т. е. определенный пользова- телем = 0, внутренне опреде- ленный = 1 или более) rdb$formats хранит информацию об изменениях метаданных, выполненных для таб- лиц. Каждый раз, когда таблица или просмотр изменяются, таблица получает новый номер формата. Цель этого — позволить приложениям получать доступ к изменен- ной таблице без необходимости их перекомпилировать. Когда номер формата любой таблицы достигает 255, вся база данных становится недоступной для запросов. Тогда нужно выполнить резервное копирование, восстановить эту копию и продолжить работу с заново созданной базой данных. Идентификатор столбца Тип IDX UN Описание RDB$RELATION_ID SMALLINT Y(1) Y(1) Идентификатор таблицы или Просмотра В RDB$RELATIONS ROBS FORMAT SMALLINT Y(2) Y(2) Идентификатор формата табли- цы. Форматов может быть до 255, как и строк для любой кон- кретной таблицы RDB$DESCRIPTOR BLOB FORMAT Отображение в виде blob столбцов и атрибутов данных на момент, когда была создана запись формата
Системные таблицы и просмотры 995 rdb$function_arguments хранит атрибуты аргументов (параметров) внешних функций. Идентификатор столбца Тип IDX UN Описание RDB$FUNCTION_NAME CHAR(31) Y Уникальное имя внешней функ- ции, соответствующее имени функции В RDB$FUNCTIONS RDB $ ARGUMENT^? OS ITI ON SMALLINT Позиция аргумента в списке аргументов: 1 = первый, 2 = вто- рой и т. Д. RDB5MECHANISM SMALLINT Передается ли аргумент по зна- чению (0), по ссылке (1), через дескриптор (2) или через деск- риптор BLOB (3) RDB$FIELD_TYPE SMALLINT Числовой код, задающий тип данных для столбца: 7 = SMALLINT, 8 = INTEGER, 12 = DATE, 1 3 = TIME, 14 = CHAR, 16 = BIGINT, 27 = DOUBLE PRECISION, 35 = TIMESTAMP, 37 = VARCHAR, 40 = cstring (строка, завер- шаемая нулем), 261 = BLOB RDB$FIELD_SCALE SMALLINT Масштаб для целого числа или аргумента с фиксированной точкой RDB$FIELD__LENGTH SMALLINT Длина аргумента в байтах. Дли- ны несимвольных типов см. RDB$FIELDS.RDB$ FIELD_LENGTH RDB?FIELD_SUB__TYPE SMALLINT Для аргумента blob задает под- тип BLOB RDB$CHARACTER_SET_ID SMALLINT Идентификатор набора симво- лов для символьного аргумента, если применим RDBS FIELD_PRECISION SMALLINT Количество цифр точности, до- пустимой для типа данных аргу- мента RDB$CHARACTER__LENGTH SMALLINT Длина аргумента char или varchar в символах (не в бай- тах)
996 Приложение 9 rdb$functions хранит информацию о внешних функциях. Идентификатор столбца Тип IDX UN Описание RDB$ FUNCTION_NAME CHAR(31) Y Y Уникальное имя внешней функ- ции RDB$FUNCTION_TYPE SMALLINT В настоящий момент не исполь- зуется RDB$QUERY_NAME CHAR(31) Имелось в виду, что будет аль- тернативным именем функции для использования в запросах isql. На самом деле не работает RDBSDESCRIPTION BLOB TEXT Может использоваться для до- кументации RDB$MODULE_NAME VARCHAR(253) Имя динамической библиотеки / совместно используемого объ- екта, где расположен код функ- ции RDB$ENTRYPOINT CHAR(31) Имя точки входа в библиотеке, где можно найти эту функцию RDB $ RETURN_ARGUMENT SMALLINT Номер позиции возвращаемого аргумента в списке параметров, соответствующем входным ар- гументам RDB$SYSTEM_FLAG SMALLINT Определенная пользователем функция = 1, определенная сис- темой = 0 rdb$generators хранит имена и идентификаторы генераторов. Идентификатор столбца Тип IDX UN Описание RDB$GENERATOR_NAME CHAR(31) Y Y Уникальное имя генератора RDBS GENERATOR_ID SMALLINT Назначаемый системой уни- кальный идентификатор для генератора RDB$SYSTEM_FLAG SMALLINT 0 = определенный пользовате- лем, 1 или выше = определен- ный системой. Firebird внутренне использует множество генера- торов rdb$index_segments хранит сегменты и позиции составных индексов. Идентификатор столбца Тип IDX UN Описание RDB$INDEX_NAME CHAR(31) Y Имя индекса. Должно быть со- гласованным с соответствующей главной записью в rdb$indices
Системные таблицы и просмотры 997 (окончание) Идентификатор столбца Тип IDX UN Описание RDB$FIELD_NAME CHAR(31) Имя ключевого столбца в индексе. Соответствует rdb$field_name имени столбца базы данных в RDB$RELATION_FIELDS RDB $ FIELD_POS111ON SMALLINT Последовательная позиция столбца в индексе (упорядочен- ность слева направо) rdb$indices хранит определения всех индексов. Идентификатор столбца Тип IDX UN Описание RDB$INDEX_NAME CHAR(31) Y Y Уникальное имя индекса RDB$RELATION_NAME CHAR(31) Y Имя таблицы, к которой приме- няется индекс. Соответствует RDB$RELATION_NAME В ЗЭПИСИ RDBSRELATIONS RDB$INDEX_ID SMALLINT Внутренний идентификатор ин- декса. Запись данных в этот столбец из приложения приве- дет к поломке индекса RDB$UNIQUE_FLAG SMALLINT Указывает, является ли индекс уникальным (1 = уникальный, 0 = не уникальный) RDB$DESCRIPTION BLOB TEXT Доступно для документирования RDB$ S EGMENT_COUNT SMALLINT Количество сегментов (столб- цов) в индексе RDB$INDEX_INACTIVE SMALLINT Указывает, является ли в на- стоящий момент индекс неак- тивным (1 = неактивный, 0 = активный) RDB$INPEX_TYPE SMALLINT В настоящий момент не исполь- зуется. Видимо, нужен для раз- личения обычных индексов и индексов выражений, когда та- кая возможность будет реализо- вана RDB$FOREIGN_KEY VARCHAR(31) Y Имя ассоциированного ограни- чения внешнего ключа, если существует RDB$SYSTEM_FLAG SMALLINT Указывает, является ли индекс определенным системой (1 или выше) или пользователем (0)
998 Приложение 9 (окончание) Идентификатор столбца Тип IDX UN Описание RDB $ EX PRES SION_BLR BLOB BLR Представление выражения на языке двоичного представления (BLR). Будет использовано для вычисления во время выполне- ния, когда будут реализованы индексы выражений RDB$EXPRESSION_SOURCE BLOB TEXT Исходный текст выражения. Будет использовано, когда будут реализованы индексы выраже- ний RDB$STATISTICS DOUBLE PRECISION Хранит самую последнюю се- лективность индекса, вычислен- ную при запуске или с помощью оператора set ststistics rdb$log_files является устаревшей системной таблицей. rdb$pages хранит информацию о страницах базы данных. Идентификатор столбца Тип IDX UN Описание RDB$PAGE_NUMBER INTEGER Уникальный номер страницы базы данных, которая была вы- делена физически RDB$RELATION_ID SMALLINT Идентификатор таблицы, чьи данные хранятся на этой стра- нице RDB$ PAGE_SEQUENCE INTEGER Последовательный номер этой страницы по отношению к дру- гим страницам, выделенным для этой таблицы RDB$PAGE_TYPE SMALLINT Идентифицирует тип данных, хранящихся на этой странице (данные таблицы, индекса и т. д.) rdb$procedure_₽arameters хранит параметры хранимых процедур. Идентификатор столбца Тип IDX UN Описание RDB$ Р ARAMETER_NAME CHAR(31) Y(2) Y(2) Имя параметра RDB$PROCEDURE_NAME CHAR(31) Y(1) Y(1) Имя процедуры RDB$ PARAMETER-NUMBER SMALLINT Последовательный номер пара- метра
Системные таблицы и просмотры 999 (окончание) Идентификатор столбца Тип IDX UN Описание RDB$ PARAMETER—TYRE SMALLINT Указывает, является ли пара- метр входным (0) или выходным (D RDB$FIELD—SOURCE CHAR(31) Сгенерированное системой уни- кальное имя столбца RDB$DESCRIPTION BLOB TEXT Доступно для документирования RDB$SYSTEM_FLAG SMALLINT Указывает, является ли пара- метр определенным системой (1 и выше) или пользователем (0) rdb$procedures содержит описания хранимых процедур. Идентификатор столбца Тип IDX UN Описание RDB$PROCEDURE_NAME CHAR(31) Y Y Имя процедуры RDB$PROCEDURE-!D SMALLINT Y Определенный системой уникальный идентификатор процедуры RDB$PROCEDURE—INPUTS SMALLINT Указывает, существуют входные параметры (1) или нет (0) RDB$PROCEDURE—OUTPUTS SMALLINT Указывает, существуют выход- ные параметры (1) или нет (0) RDB$DESCRIPTION BLOB TEXT Доступно для документирования RDB$ PROCEDURE—SOURCE BLOB TEXT Исходный код процедуры RDB$PROCEDURE—BLR BLOB BLR Двоичное представление (BLR) кода процедуры RDB$SECURITY_CLASS CHAR(31) Может указывать на класс безо- пасности, определенный в RDB$SECURITY_CLASSES, ДЛЯ применения ограничений управ- ления доступом RDB $ OWNER_NAME VARCHAR(31) Имя пользователя — владельца процедуры RDB$RUNTIME BLOB SUMMARY Описание метаданных процеду- ры. Внутреннее использование для оптимизации RDBSSYSTEM—FLAG SMALLINT Определена пользователем (0) или системой (1 или выше)
woo Приложение 9 rdb$ref_ccnstraints хранит действия для ссылочных ограничений. Идентификатор столбца Тип IDX UN Описание RDB$CONSTRAINT_NAME CHAR(31) Y Y Имя ссылочного ограничения RDB$CONST_NAME_UQ CHAR(31) Имя ограничения первичного или уникального ключа, на кото- рое ссылается предложение references в этом ограничении RDB$MATCH_OPTION CHAR(7) Текущим значением является null во всех случаях. Резерви- руется для будущего использо- вания RDB$UPDATE_RULE CHAR(11) Действия по ссылочной целост- ности, применимые к данному внешнему ключу, когда изменя- ется первичный ключ: no action | CASCADE ! SET NULL i SET DEFAULT RDB$DELETE_RULE CHAR(11) Действия по ссылочной целост- ности, применимые к данному внешнему ключу, когда удаляет- ся первичный ключ. Правила те же, что указаны в столбце RDB$UPDATE_RULE rdb$relation_constraints хранит информацию об ограничениях целостности на уров- не таблицы. Идентификатор столбца Тип IDX UN Описание RDB$CONSTRAINT_NAME CHAR(31) Y Y Имя ограничения на уровне таблицы RDB$CONSTRAINT_TYPE CHAR(ll) Y(2) Первичный ключ / уникальный ключ / внешний ключ / ограниче- ние CHECK / NOT NULL ROBS RELATI ON_NAME CHAR(31) Y(1) Имя таблицы, к которой приме- няется это ограничение RDB$DEFERRABLE CHAR(3) В настоящий момент во всех случаях NO. Резервируется для будущей реализации отложен- ных ограничений RDB$INITIALLY_DEFERRED CHAR(3) То же RDB$INDEX_NAME CHAR(31) Y Имя индекса, который поддер- живает это ограничение (приме- нимо, если ограничением явля- ется PRIMARY KEY, UNIQUE ИЛИ FOREIGN KEY)
Системные таблицы и просмотры 1001 rdb$relation_fields хранит определения столбцов. Идентификатор столбца Тип IDX UN Описание RDB$FIELD_NAME CHAR(31) Y(1) Y(1) Имя столбца, уникальное в таб- лице или просмотре RDB $ RELATI ON_NAME CHAR(31) Y(2) Y(2) Имя таблицы или просмотра Y (Другой индекс) RDB$FIELD_SOURCE CHAR(31) Y Имя, сгенерированное системой (SQL&nnn) для этого столбца, коррелирующееся с rdb$fields. Если столбец основан на доме- не, то два связанных столбца rdb$field_source хранят имя домена RDB$QUERY_NAME CHAR(31) Y В настоящий момент не исполь- зуется RDB$BASE_FIELD CHAR(31) Только для запроса. Имя столб- ца из базовой таблицы. Базовая таблица идентифицируется по внутреннему идентификатору В столбце RDB$VIEW_CONTEXT RDB$EDIT_STRING VARCHAR(125) Не используется в Firebird RDB$ FIELD_POSITlON SMALLINT Позиция столбца в таблице или просмотре по отношению к дру- гим столбцам. Заметьте, что для таблиц вы можете изменить это с использованием alter table ALTER COLUMN POSITION n, где n — новая позиция поля RDB$QUERY_HEADER BLOB TEXT He используется в Firebird RDB$UPDATE_FLAG SMALLINT Не используется в Firebird RDB$FIELD_ID SMALLINT Временный номер идентифика- тора, используемый внутренне. Он изменяется после копирова- ния / восстановления, следова- тельно, на него не следует по- лагаться в запросах приложе- ний. Не изменяйте его RDB$VIEW_CONTEXT SMALLINT Для столбца просмотра внут- ренний идентификатор базовой таблицы, откуда приходит это поле. Не изменяйте этот стол- бец RDB$DESCRIPTION BLOB TEXT Может документировать этот столбец
1002 Приложение 9 (окончание) Идентификатор столбца Тип IDX UN Описание RDB$DEFAULT_VALUE BLOB BLR Представление в двоичном язы- ке предложения default, если оно присутствует RDB$SYSTEM_FLAG SMALLINT Определено пользователем (0) или системой (1 или выше) RDB$SECURITY_CLASS CHAR(31) Может ссылаться на класс безо- пасности, определенный в RDB$SECURITY_CLASSES ДЛЯ применения ограничений управ- ления доступом для всех поль- зователей этого столбца RDB$COMPLEX_NAME CHAR(31) Резервируется для будущих реализаций RDB$NULL_FLAG SMALLINT Указывает, допускает ли стол- бец значения null (пусто) или не допускает RDB$DEFAULT_SOURCE BLOB TEXT Начальный исходный текст из предложения default, если присутствует RDB$COLLATION_ID SMALLINT Идентификатор последователь- ности сортировки для столбца не по умолчанию rdb$relations хранит информацию заголовка таблиц и просмотров. Идентификатор столбца Тип IDX UN Описание RDB$VIEW_BLR BLOB BLR Представление в двоичном языке спецификации запроса для просмотра; null для таблиц RDB$VIEW_SOURCE BLOB TEXT Спецификация запроса для просмотра RDB$DESCRIPTION BLOB TEXT Можно использовать для доку- ментирования RDB$RELATION_ID SMALLINT Y Внутренний идентификатор таблицы. Не изменяйте этот столбец RDB$SYSTEM_FLAG SMALLINT Указывает, создана ли таблица пользователем (0) или системой (1 или выше). Не изменяйте этот флаг для таблиц, опреде- ленных пользователем или сис- темой
Системные таблицы и просмотры 1003 (окончание) Идентификатор столбца Тип IDX UN Описание RDB$DBKEY__LENGTH SMALLINT Для просмотров общая длина ключа db key. Это 8 байт для таблиц. Для просмотров это 8 * количество таблиц, на кото- рые ссылается определение просмотра. Не изменяйте этот столбец. Более подробно о та- ких ключах см. в разд. "Темы оптимизации" в конце главы 30 RDB $ FORMAT SMALLINT Внутреннее использование — не модифицируйте RDBSFIELDJCD SMALLINT Внутреннее использование — не модифицируйте RDB$ RELATIONJSIAME CHAR(31) Y Y Имя таблицы или просмотра RDB$SECURITY_CLASS CHAR(31) Может ссылаться на класс безопасности, определенный В RDB$SECURITY_CLASSES для применения ограничений управ- ления доступом для всех поль- зователей этого столбца RDB$EXTERNAL_FILE VARCHAR(253) Полный путь к внешнему файлу данных, если присутствует RDB$RUNTIME BLOB SUMMARY Описание метаданных таблицы. Внутреннее использование для оптимизации RDBS EXTERNAL_DESCRIPTION BLOB EFD blob подтипа external_type_description, текстовый тип blob, который может быть использован для документирования RDB$OWNER_NAME VARCHAROI) Имя пользователя — владельца (создателя) таблицы или про- смотра для целей безопасности SQL RDB $ DE FAULTJCLAS S CHAR(31) Класс безопасности по умолча- нию. Применяется, когда новый столбец добавляется в таблицу RDB$ FLAGS SMALLINT Внутренние флаги rdb$roles хранит определения ролей. Идентификатор столбца Тип IDX UN Описание RDB$ROLE_NAME VARCHAROI) Y Y Имя роли
1004 Приложение 9 (окончание) Идентификатор столбца Тип IDX UN Описание RDB$OWNER_NAME VARCHAR (31) Имя пользователя владельца роли rdb$security_classes хранит и отслеживает списки управления доступом. Идентификатор столбца Тип IDX UN Описание RDB$SECURITY_CLASS CHAR(31) Y Y Имя класса безопасности. Это имя должно оставаться согласо- ванным во всех местах, где оно используется (rdbSdatabase, RDB $ RELAT IONS, RDB$ RELAT ION_FIELDS) RDBSACL BLOB ACL Список управления доступом, связанный с классом безопасно- сти. Перечисляет пользователей и их полномочия RDB$DESCRIPTION BLOB TEXT Здесь определена документация по классу безопасности rdb$transactions отслеживает транзакции с несколькими базами данных. Идентификатор столбца Тип IDX UN Описание RDB$TRANSACTION_ID INTEGER Y Y Уникальный идентификатор от- слеживаемой транзакции RDB$TRANSACTION_STATE SMALLINT Состояние транзакции: зависшая (0), подтвержденная (1), отме- ненная (2) RDBSTIMESTAMP TIMESTAMP Для будущих реализаций RDB$TRANSACTION__ DESCRIPTION BLOB TEXT Подтип BLOB transaction_description. Опи- сывает подготовленную транзак- цию к нескольким базам данных. Доступна в случае потери соеди- нения, которое не может быть восстановлено rdb$trigger_messages хранит определения сообщений триггеров (системное использо- вание). Идентификатор столбца Тип IDX UN Описание RDB$TRIGGER__NAME CHAR(31) Y Имя триггера, с которым ассо- циировано сообщение
Системные таблицы и просмотры 1005 (окончание) Идентификатор столбца Тип IDX UN Описание RDB$MESSAGE_NUMBER SMALLINT Номер сообщения (от 1 до мак- симум 32 767) RDB$MESSAGE VARCHAR(78) Текст сообщения триггера rdb$triggers хранит описания всех триггеров. Идентификатор столбца Тип IDX UN Описание RDB$TRIGGER_NAME • CHAR(31) Y Y Имя триггера RDB $ RE LATI ON_NAME CHAR(31) Y Имя таблицы или просмотра, для которого используется триг- гер RDB$TRIGGER_SEQUENCE SMALLINT Последовательность(позиция) триггера. Ноль обычно означает, что последовательность не за- дана RDB$TRIGGER_TYPE SMALLINT 1 = BEFORE INSERT, 2 = AFTER INSERT, 3 = BEFORE UPDATE, 4 = AFTER UPDATE, 5 = BEFORE DELETE, 6 = AFTER DELETE. Триггеры на многие события (Firebird 1.5 и выше) имеют раз- личные типы, которые исполь- зуют большие номера. Фактиче- ский тип кода зависит от того, какие обрабатываются события и от порядка, в котором пред- ставляются события. (Заметим, что не существует видимых при- чин для того, чтобы порядок событий влиял на код trigger_type) RDB?TRIGGER_SOURCE BLOB TEXT Хранит PSQL исходный код триг- гера RDB$TRIGGER_BLR BLOB BLR Хранит представление триггера в двоичном коде RDB$DESCRIPTION BLOB TEXT Дополнительная документация RDB$TRIGGER_INACTIVE SMALLINT Является ли триггер в настоя- щее время неактивным (1 = не- активный, 0 = активный)
1006 Приложение 9 (окончание) Идентификатор столбца Тип IDX UN Описание RDB$SYSTEM_FLAG SMALLINT Определен пользователем (0) либо системой (1 или выше) RDB$FLAGS SMALLINT Внутреннее использование rdb$types хранит описания перечисляемых типов, используемых в Firebird. Идентификатор столбца Тип IDX UN Описание RDB$FIELD_NAME CHAR(31) Имя столбца, для которого оп- ределен этот перечисляемый тип. Заметьте, что то же имя столбца появляется во множе- стве системных таблиц RDB$TYPE SMALLINT Перечислимый идентификатор для типа, который идентифици- рует rdb$FIELD-NAME. Последо- вательность чисел является уникальной для каждого отдель- ного перечисляемого типа (на- пример, 0 = таблица, 1 = про- смотр, 2 = триггер, 3 = вычис- ляемый столбец, 4 = проверка, 5 = процедура — все являются типами ИЗ RDB$OBJECT_TYPE) RDB$TYPE_NAME CHAR(31) Y Текстовое представление типа, идентифицированного значени- ем rdb$FIELD-NAME и значением RDB$TYPE RDB$DESCRIPTION BLOB TEXT Может использоваться для до- кументирования RDB$SYSTEM_FLAG SMALLINT Определен пользователем (0) или системой (1 или выше) RDB$USER_PRIVILEGES ХраНИТ ПОЛНОМОЧИЯ SQL. Идентификатор столбца Тип IDX UN Описание RDB$USER CHAR(31) Y Пользователь, которому предос- тавляется полномочие RDBSGRANTOR CHAR(31) Имя пользователя, предостав- ляющего полномочие RDB$PRIVILEGE CHAR(6) Привилегия, предоставляемая в полномочии
Системные таблицы и просмотры 1007 (окончание) Идентификатор столбца Тип IDX UN Описание RDB$GRANT_OPTION SMALLINT Содержит ли полномочие авто- ризацию WITH GRANT OPTION. 1 = да, 0 = нет RDB $ RELAT ION—NAME CHAR(31) Y Объект, к которому предостав- ляется полномочие RDBSFIELD_NAME CHAR(31) Имя столбца, к которому приме- няется привилегия на уровне столбца (только привилегии UPDATE И REFERENCES) RDB$USER__TYPE SMALLINT Идентифицирует тип пользова- теля, которому предоставляется привилегия (например, пользо- ватель, процедура, просмотр и т. д.) RDB$OBJECT_TYPE SMALLINT Идентифицирует тип объекта, к которому предоставляется привилегия rdb$view_relations является устаревшей таблицей. Системные просмотры Следующие системные просмотры являются подмножеством просмотров, опреде- ленных в стандарте SQL-92. Они предоставляют полезную информацию о ваших данных. Вы можете скопировать эти тексты в скрипты и инсталлировать просмотры во всех ваших базах данных. check_constraints отображает все ограничения check, определенные в базе данных, с исходными текстами определения ограничений. CREATE VIEW CHECK-CONSTRAINTS ( CONSTRAINT_NAME, CHECK__CIAUSE ) AS SELECT RDB$CONSTRAINT_NAME, RDB$TRIGGER_SOURCE FROM RDB$CHECK—CONSTRAINTS RC, RDBSTRIGGERS RT WHERE RT.RDB$TRIGGER_NAME = RC.RDB$TRIGGER_NAME; ccnstraints_column_usage отображает столбцы, используемые в ограничениях primary key и unique и определенные в ограничениях foreign key. CREATE VIEW CONSTRAINTSJCOLUMNJJSAGE ( TABLE_NAME,
1008 Приложение 9 COLUMN—NAME, CONSTRAINT-NAME ) AS SELECT RDB $ RELATION-NAME,, RDB$FIELD_NAME, RDB? CONSTRAINT-NAME FROM RDB?RELATION_CONSTRAINTS RC, RDB?INDEX—SEGMENTS RI WHERE RI ,RDB$INDEX-NAME = RC . RDB?INDEX_NAME; referential-constraints отображает все ссылочные ограничения, определенные в базе данных. CREATE VIEW REFERENTIAL-CONSTRAINTS ( CONSTRAINT-NAME, UNIQUE_ CONSTRAINT-NAME,. MATCH-OPTION, UPDATE_RULE, DELETE-RULE ) AS SELECT RDBSCONSTRAINT—NAME, RDB?CONST_NAME—UQ, RDB$MATCH_OPTION, RDB$UPDATE-RULE, RDB?DELETE—RULE FROM RDB?REF—CONSTRAINTS; table—constraints отображает ограничения на уровне таблицы. CREATE VIEW TABLE-CONSTRAINTS ( CONSTRAINT_NAME, TABLE_NAME, CONSTRAINT_TYPE, IS—DEFERRABLE, INITIALLY-DEFERRED ) AS SELECT RDBSCONSTRAINT-NAME, RDB?RELATION_NAME, RDBSCONSTRAINT—TYPE, RDB?DEFERRABLE, RDB?INITIALLY-DEFERRED FROM RDBSRELATION CONSTRAINTS;
ПРИЛОЖЕНИЕ 1 О Коды ошибок Коды ошибок, возвращаемые клиентам или модулям PSQL сервером Firebird 1.5.0, представлены в табл. П10.1. Некоторые коды недоступны в более ранних версиях Firebird. Важно убедиться, что сервер и клиент имеют корректную версию файла firebird.msg (interbase.msg для Firebird 1.0.x), хранящегося в корневом каталоге Fire- bird. Необязательно копировать MSG-файл на клиентские компьютеры, но если он присутствует, то должен быть корректным. Таблица П10.1. Коды ошибок Firebird 1.5.0 SQLCODE GDSCODE Символ Текст сообщения 101 335544366 segment -Segment buffer length shorter than expected. Длина сегмента буфера меньше, чем ожидается 100 335544338 f rom_no_match -No match for first value expression. Нет соответствия для первого значе- ния выражения 100 335544354 no_record -Invalid database key. Неверный ключ базы данных 100 335544367 segstr_eof -Attempted retrieval of more segments than exist. Попытка поиска больше сегментов, чем существует 100 335544374 stream_eof -Attempt to fetch past the last record in a record stream. Попытка загрузки в поток записей по- сле последней записи -84 335544554 nonsql_security_rel -Table/procedure has non-SQL security class defined. Для таблицы/процедуры определен класс безопасности, не являющийся SQL -84 335544555 nonsql_security__fld -Column has non-SQL security class defined. Для столбца определен класс безо- пасности, не являющийся SQL
1010 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -84 335544668 - dsql_procedure_use_err -Procedure <string> does not return any values. Процедура <строка> не возвращает никакого значения -85 335544747 usrname_too_long -The username entered is too long. Maximum length is 31 bytes. Введенное имя пользователя слишком длинное. Максимальная длина 31 байт -85 335544748 password_too_long -The password specified is too long. Maximum length is 8 bytes. Указанный пароль слишком длинный. Максимальная длина 8 байт -85 335544749 usrname__required -A username is required for this operation. Для этой операции требуется имя пользователя -85 335544750 password_requirecl -A password is required for this operation. Для этой операции требуется пароль -85 335544751 badjsrotocol -The network protocol specified is invalid. Указан неверный сетевой протокол -85 335544752 dup_us rname_found -A duplicate user name was found in the .security database. В базе данных безопасности обнару- жено дублирование имен пользовате- лей -85 335544753 u s rname_not_found -The user name specified was not found in the security database. Указанное имя пользователя не най- дено в базе данных безопасности -85 335544754 error_adding-Sec_record -An error occurred while attempting to add the user. Появилась ошибка при попытке добав- ления пользователя -85 335544755 error_modifying_sec_record -An error occurred while attempting to modify the user record. Появилась ошибка при попытке изме- нения записи пользователя
Коды ошибок 1011 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -85 335544756 error_deleting_sec_record -An error occurred while attempting to delete the user record. Появилась ошибка при попытке удале- ния записи пользователя -85 335544757 error_updating_sec_db -An error occurred while updating the security database. Появилась ошибка при изменении базы данных безопасности -103 335544571 dsql_constant_err -Data type for constant unknown. Неизвестен тип данных для константы -104 335544343 invalid_blr -Invalid request BLR at offset <nuinber>. Неверный запрос в BLR co смещением <число> -104 335544390 syntaxerr -BLR syntax error: expected <string> at offset <number>, encountered <number>. Ошибка синтаксиса BLR: ожидается <строка> по смещению <число>, встречено <число> -104 335544425 ctxinuse -Context already in use (BLR error). Контекст находится в использовании (ошибка BLR) -104 335544426 ctxnotdef -Context not defined (BLR error). Контекст не определен (ошибка BLR) -104 335544429 badparnum -Bad parameter number. Неверный номер параметра -104 335544440 bad_msg_vec — -104 335544456 invalid_sdl -Invalid slice description language at offset <number>. Неверный фрагмент языка описания по смещению <число> -104 335544570 dsql_command_err -Invalid command. Неверная команда -104 335544579 dsql_internal_err -Internal error. Внутренняя ошибка -104 335544590 dsql_dup_option -Option specified more than once. Режим указан более одного раза
1012 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -104 335544591 dsql_tran_err -Unknown transaction option. Неизвестный режим транзакции -104 335544592 dsql_invalid_array -Invalid array reference. Неверная ссылка на массив -104 335544608 command__end_err -Unexpected end of command. Неверное завершение команды -104 335544612 token_err -Token unknown. Неизвестный синтаксический элемент -104 335544634 dsql_token_unk_err -Token unknown — line <number>, char <number>. Неизвестный синтаксический эле- мент — строка <число>, символ <число> -104 335544709 dsql_agg_ref_err -Invalid aggregate reference. Неверная ссылка на агрегат -104 335544714 invalid_array_id -Invalid blob id. Неверный идентификатор blob -104 335544730 cse_not_supported -Client/Server Express not supported in this release. Client/Server Express не поддержива- ется в этом релизе -104 335544743 token__too__long -Token size exceeds limit. Размер синтаксического элемента превышает предел -104 335544763 invalid_string_constant -A string constant is delimited by double quotes. Строковая константа определена в кавычках -104 335544764 transitional_date -DATE must be changed to TIMESTAMP. DATE должно измениться В TIMESTAMP -104 335544796 sql_dialect_datatype_ unsupport -Client SQL dialect <number> does not support reference to <string> datatype. SQL-диалект <номер> клиента не поддерживает ссылку на тип дан- ных <строка>
Коды ошибок 1013 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -104 335544798 depend_on__uncommitted_ rel -You created an indirect dependency on uncommitted metadata. You must roll back the current transaction. Вы создали непрямую зависимость на неподтвержденные метаданные. Вы должны отменить текущую транзакцию -104 335544821 dsql_colurnn__pos_err -Invalid column position used in the <string> clause. В предложении <строка> использует- ся неверная позиция столбца -104 335544822 dsql_agg_where_err -Cannot use an aggregate function in a WHERE clause, use HAVING instead. Невозможно использовать агрегирую- щую функцию в предложении where, используйте вместо этого having -104 335544823 dsql_agg_group_err -Cannot use an aggregate function in a GROUP BY clause. Невозможно использовать агрегирую- щую функцию в предложении GROUP BY -104 335544824 ds ql_agg_column_e r r -Invalid expression in the <string> (not contained in either an aggregate function or the GROUP BY clause). Неверное выражение в <строка> (не содержится ни в агрегирующей функ- ции, ни в предложении group by) -104 335544825 dsql_agg_having_err -Invalid expression in the <string> (neither an aggregate function nor a part of the GROUP BY clause). Неверное выражение в <строка> (не агрегирующая функция, не предложе- ние GROUP BY) -104 335544826 d s ql__agg_ne s t e d_e r r -Nested aggregate functions are not allowed. Вложенные агрегирующие функции недопустимы -104 336003075 dsql_t ransitional__numeric -Precision 10 to 18 changed from DOUBLE PRECISION in SQL dialect 1 to 64-bit scaled integer in SQL dialect 3. Точность от 10 до 18 в SQL-диалекте 1 изменена для double precision до 64-битового масштабируемого целого в SQL-диалекте 3
1014 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -104 336003077 sql_db_diа1есt_dtуре_ unsupport -Database SQL dialect <number> does not support reference to <string> datatype. База данных диалекта SQL <номер> ' не поддерживает ссылку на тип дан- ных <строка> -104 336003087 dsql_invalid_label -Label <string> <string> in the current scope. Метка <строка> <строка> находится в текущей области видимости -104 336003088 dsql_datatypes_not_ comparable -Datatypes <string> are not com- parable in expression <string>. Тип данных <строка> не сравним в выражении <строка> -105 335544702 like_escape_invalid -Invalid ESCAPE sequence. Неверная последовательность escape -105 335544789 ext ract_input_mi smatch -Specified EXTRACT part does not exist in input datatype. Заданная часть extract не существует во входном типе данных -150 335544360 read_only_rel -Attempted update of read-only table. Попытка изменить таблицу только для чтения -150 335544362 re ad_on1y_vi e w -Cannot update read-only view <string>. Невозможно изменить просмотр толь- ко для чтения <строка> -150 335544446 non_updatable -Not updatable. Не изменяется -150 335544546 constaint_on_view -Cannot define constraints on views. Нельзя определить ограничения для просмотра -151 335544359 read_only_field -Attempted update of read-only column. Попытка изменить столбец только для чтения -155 335544658 dsql_base_table -<string> is not a valid base table of the specified view. <Строка> не является допустимой базовой таблицей для указанного просмотра
Коды ошибок 1015 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -157 335544598 specify_field_err -Must specify column name for view select expression. Требуется задать имя столбца для выражения select просмотра -158 335544599 num_field_err -Number of columns does not match select list. Номера столбцов не соответствуют СПИСКУ SELECT -162 335544685 no_dbkey -Dbkey not available for multi- table views. Ключ базы данных недоступен для многотабличных просмотров -170 335544512 prcmismat -Parameter mismatch for procedure <string>. Несоответствие параметра для процедуры <строка> -170 335544619 extern_func_err -External functions cannot have more than 10 parameters. Внешняя функция не может иметь более 10 параметров -171 335544439 funmismat -Function <string> could not be matched. Функции <строка> нельзя найти соот- ветствие -171 335544458 invalid_dimension -Column not array or invalid dimensions (expected <number>, encountered <number>). Столбец не является массивом или неверная размерность (ожидается <номер>, встретилось <номер>) -171 335544618 return_jnode_err -Return mode by value not allowed for this data type. Вариант возвращаемого значения недоступен для этого типа данных -172 335544438 funnotdef -Function <string> is not defined. Функция <строка> не определена -203 335544708 dyn_fld_ambiguous -Ambiguous column reference. Неоднозначная ссылка на столбец -204 335544463 gennotdef -Generator <string> is not defined. Генератор <строка> не определен
1016 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -204 335544502 stream_not_defined -Reference to invalid stream number. Ссылка на неверный номер потока -204 335544509 charset_not_found -CHARACTER SET <string> is not defined. Набор символов <строка> не опреде- лен -204 335544511 prcnotdef -Procedure <string> is not defined. Процедура <строка> не определена -204 335544515 codnotdef -Status code <string> unknown. Неизвестный код состояния <строка> -204 335544516 xcpnotdef -Exception <string> not defined. He определено исключение <строка> -204 335544532 ref_cnstrnt_notfound -Name of Referential Constraint not defined in constraints table. Имя ссылочного ограничения не опре- делено в таблице ограничений -204 335544551 grant_obj_notfound -Could not find table/procedure for GRANT. Невозможно найти таблицу/процедуру ДЛЯ GRANT -204 335544568 text_subtype -Implementation of text subtype . <number> not located. Реализация текстового подтипа <но- мер> не обнаружена -204 335544573 dsql_datatype_err -Data type unknown. Неизвестный тип данных -204 335544580 dsql_relation_err -Table unknown. Неизвестная таблица -204 335544581 dsql_procedure_err -Procedure unknown. Неизвестная процедура -204 335544588 collation_not_found -COLLATION <string> is not defined. Порядок сортировки <строка> не оп- ределен -204 335544589 collation_not_for_charset -COLLATION <string> is not valid for specified CHARACTER SET.. Порядок сортировки <строка> неве- рен для указанного набора символов
Коды ошибок 1017 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -204 335544595 db ql_t гi g ge r_e г г -Trigger unknown. Неизвестный триггер -204 335544620 alias_conflict_err -Alias <string> conflicts with an alias in the same statement. Алиас <строка> конфликтует с алиасом в том же операторе -204 335544621 procedure_conflict_error -Alias <string> conflicts with a procedure in the same statement. Алиас <строка> конфликтует с процедурой в том же операторе -204 335544622 relation_conflict_err -Alias <string> conflicts with a table in the same statement. Алиас <строка> конфликтует с табли- цей в том же операторе -204 335544635 dsql_no_relation_alias -There is no alias or table named <string> at this scope level. He существует указанного алиаса или таблицы <строка> на этом уровне видимости -204 335544636 indexname -There is no index <string> for table <string>. He существует индекса <строка> для таблицы <строка> -204 335544640 collation_requires_text -Invalid use of CHARACTER SET or COLLATE. Неверное использование набора сим- волов или порядка сортировки -204 335544662 dsql_blob_type_unknown -BLOB SUB_TYPE <string> is not defined. Подтип BLOB <строка> не определен -204 335544759 bad_default_value -Can not define a not null column with NULL as default value. Невозможно определить непустой столбец (not null) вместе co значе- нием по умолчанию null -204 335544760 invalid_clause -Invalid clause—1<string>'. Неверное предложение — '<строка>' -204 335544800 too many_contexts -Too many Contexts of Relation/Procedure/Views. Maximum allowed is 127. Слишком большой контекст в отноше- нии/процедуре/просмотре. Допустимо максимум 127
1018 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -204 335544817 к bad_limit_param -Invalid parameter to FIRST. Only integers >= 0 are allowed. Неверный параметр для first. Допустимы только целые >= 0 -204 335544818 bad_skipjoaram -Invalid parameter to SKIP. Only integers >= 0 are allowed. Неверный параметр для skip. Допустимы только целые >= 0 -204 336003085 dsql_ambiguous__field_name -Ambiguous field name between <string> and <string>. Двусмысленность имени поля между <строка> и <строка> -205 335544396 fldnotdef -Column <string> is not defined in table <string>. Столбец <строка> не определен в таблице <строка> -205 335544552 grant_fld_notfound -Could not find column for GRANT. Невозможно найти столбец для grant -206 335544578 dsql_field_err -Column unknown. Неизвестный столбец -206 335544587 dsql_blob_err -Column is not a BLOB. Столбец не является blob -206 335544596 dsql_subselect_err -Subselect illegal in this context. Вложенный оператор select неверен в данном контексте -208 335544617 order_by_err -Invalid ORDER BY clause. Неверное предложение order by -219 335544395 relnotdef -Table <string> is not defined. Таблица <строка> не определена -239 335544691 cache_too_smal1 -Insufficient memory to allocate page buffer cache. Недостаточно памяти для выделения кэша под буфер страницы -260 335544690 cache_redef -Cache redefined. Переопределение кэша -281 335544637 no_stream_jolan -Table <string> is not referenced in plan. Таблиц <строка> не указана в плане
Коды ошибок 1019 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -282 335544638 stream_twice -Table <string> is referenced more than once in plan; use aliases to distinguish. На таблицу <строка> осуществляются ссылки более одного раза в плане; используйте алиасы для различения -282 335544643 ds ql_s elf_j oin -The table <string> is referenced twice; use aliases to differentiate. На таблицу <строка> в плане осуще- ствляются ссылки дважды; используй- те алиасы для разыменования -282 335544659 duplicate_base_table -Table <string> is referenced twice in view; use an alias to distinguish. На таблицу <строка> в просмотре осуществляются ссылки дважды; ис- пользуйте алиас для различения -282 335544660 view_alias -View <string> has more than one base table; use aliases to distinguish. Просмотр <строка> имеет более од- ной базовой таблицы; используйте алиасы для различения -282 335544710 c omplex_vi ew -Navigational stream <number> references a view with more than one base table. Поток навигации <строка> ссылается на просмотр с более чем одной базо- вой таблицей -283 335544639 stream_not_found -Table <string> is referenced in the plan but not the from list. На таблицу <строка> есть ссылки в плане, однако она не указана в списке -284 335544642 -index__unused -Index <string> cannot be used in the specified plan. Индекс <строка> не может быть использован в указанном плане -291 335544531 primary_key_notnull -Column used in a PRIMARY KEY constraint must be NOT NULL. Столбец, используемый в ограничении первичного ключа, должен быть not NULL
1020 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -292 335544534 ' ref_cnstrnt_update -Cannot update constraints (RDB$REF_CONSTRAINTS). Нельзя изменять ограничения (RDB$REF__CONSTRAINTS) -293 335544535 check_cnstrnt_update -Cannot update constraints (RDB$CHECK_CONSTRAINTS). Нельзя изменять ограничения (RDB$REF_CONSTRAINTS) -294 335544536 check_cnstrnt_del -Cannot delete CHECK constraint entry (RDB$CHECK_CONSTRAINTS). Нельзя удалять запись ограничения CHECK (RDB$REF_CONSTRAINTS) -295 335544545 rel_cnstrnt_update -Cannot update constraints (RDB$RELATION_CONSTRAINTS) . Нельзя изменять ограничения (RDB$RELATION_CONSTRAINTS) -296 335544547 invld_cnstrnt_type -Internal gds software consistency check (invalid RDB$CONSTRAINT_TYPE). Проверка внутреннего программного обеспечения gds (неверный RDB$CONSTRAINT_YYPE) -297 335544558 check_constraint -Operation violates CHECK constraint <string> on view or table <string>. Операция нарушает ограничение check <строка> для просмотра или таблицы <строка> -313 335544669 dsql_count_mismatch -Count of column list and variable list do not match. Количество столбцов в списке и список переменных не соответствуют друг ДРУГУ -314 335544565 transliteration_failed -Cannot transliterate character between character sets. Невозможно выполнить транслитера- цию символов между наборами симво- лов -315 336068815 d у n_d t yp e__i n va 1 i d -Cannot change datatype for column <string>. Changing datatype is not supported for BLOB or ARRAY columns. Невозможно изменить тип данных для столбца <строка>. Изменение типа данных не поддерживается для столб- цов blob и массивов
Коды ошибок 1021 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -383 336068814 dyn_dependency_exists -Column <string> from table <string> is referenced in <string>. На столбец <строка> из таблицы <строка> есть ссылка в <строка> -401 335544647 invalid_operator -Invalid comparison operator for find operation. Неверный оператор сравнения для операции -402 335544368 segstr_no__op -Attempted invalid operation on a BLOB. Попытка использования неверной операции для blob -402 335544414 blobnotsup -BLOB and array data types are not supported for <string> operation. Типы данных blob и массивы не под- держиваются для операции <строка> -402 335544427 datnotsup -Data operation not supported. Операция с данными не поддержива- ется -406 335544457 out_of_bounds -Subscript out of bounds. Выход за пределы диапазона -407 335544435 nullsegkey -Null segment of UNIQUE KEY. Пустой сегмент для уникального ключа -413 335544334 convert_error -Conversion error from string ”<string>''. Ошибка преобразования для строки '<строка>' -413 335544454 nofilter -Filter not found to convert type <number> to type <number>. He найден фильтр для преобразова- ния типа <номер> в тип <номер> -501 335544327 bad_req_handle -Invalid request handle. Неверный запрос дескриптора -501 335544577 dsql__cursor_close_err -Attempt to reclose a closed cursor. Попытка повторного закрытия закрыто- го курсора -502 -335544574 -dsql_decl__err -Declared cursor already exists. Объявляемый курсор уже существует
1022 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -502 -33554457& -dsql_cursor_open_err -Attempt to reopen an open cursor. Попытка повторного открытия открыто- го курсора -504 -335544572 - d s ql__cu r s о r_e r r -Cursor unknown. Неизвестный курсор -508 -335544348 -no_cur_rec -No current record for fetch operation. Нет текущей записи для операции загрузки -510 -335544575 -dsql_cursor_update_err -Cursor not updatable. Курсор не является изменяемым -518 -335544582 -dsql_request_err -Request unknown. Неизвестный запрос -519 -335544688 -dsql_open__cursor_request -The prepare statement identifies a prepare statement with an open cursor. Оператор подготовки идентифициро- вал оператор подготовки для открыто- го курсора -530 -335544466 -foreign_key -Violation of FOREIGN KEY constraint "<string>" on table "<string>". Нарушение ограничения внешнего ключа "<строка>" для таблицы "<строка>” -531 -335544597 -dsql_crdb_prepare_err -Cannot prepare a CREATE DATABASE/SCHEMA statement. Невозможно подготовить оператор CREATE DATABASE/SCHEMA -532 -335544469 -trans_invalid -Transaction marked invalid by I/O error. Транзакция отмечена как ошибочная из-за ошибки ввода/вывода -551 -335544352 -no_priv -No permission for <string> access to <string> <string>. He существует полномочий для досту- па <строка> к <строка> -551 -335544790 -insufficient_svc_privile ges -Service <string> requires SYSDBA permissions. Reattach to the Service Manager using the SYSDBA account. Сервис <строка> требует полномочий SYSDBA. Соединитесь с Менеджером сервисов как пользователь SYSDBA
Коды ошибок 1023 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -552 -335544550 ,-not_rel_owner -Only the owner of a table may reassign ownership. Только владелец таблицы может переназначать владение -552 -335544553 -grant_nopriv -User does not have GRANT privileges for operation. Пользователь не имеет назначенных привилегий для операции -552 -335544707 -grant_nopriv_on_base -User does not have GRANT privileges on base table/view for operation. Пользователь не имеет назначенных привилегий к базовой табли- це/просмотру для операции -553 -335544529 -existing_priv_mod -Cannot modify an existing user privilege. Невозможно изменить существующую привилегию пользователя -595 -335544645 -stream_crack -The current position is on a crack. Текущая позиция разрушена -596 -335544644 -streamjoof -Illegal operation when at beginning of stream. Операция некорректна, поскольку ука- затель на позицию в потоке ссылается на начало потока -597 -335544632 -dsql_file_length_err -Preceding file did not specify length, so <string> must include starting page number. Предыдущий файл не содержит дли- ны, следовательно, <строка> должен включать начальный номер страницы -598 -335544633 -ds ql_shadow_nurnbe r_e r r -Shadow number must be a positive integer. Номер теневой копии должен быть положительным целым числом -599 -335544607 -node_err -Gen.с: node not supported. Узел Gen. с не поддерживается -599 -335544625 -node_name_e r r -A node name is not permitted in a secondary, shadow, cache or log file name. Имя узла не разрешено во вторичном файле, файле теневой копии, кэше или файле протокола
1024 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -600 -33554468Q -crrp_data_err -Sort error: corruption in data structure. Ошибка сортировки: разрушение структуры данных -601 -335544646 -db_or_file_exists -Database or file exists. Существует база данных или файл -604 -335544593 -dsql__max_arr_dim_exceede d -Array declared with too many dimensions. Объявлен массив слишком большой размерности -604 -335544594 -dsql_arr_range_error -Illegal array dimension range. Неверный диапазон размерности массива -605 -335544682 -dsql_field_ref -Inappropriate self-reference of column. ‘ Несоответствующая ссылка столбца на самого себя -607 -335544351 -no_meta_update -Unsuccessful metadata update. Ошибочное изменение метаданных -607 -335544549 -systrig_update -Cannot modify or erase a system trigger. Невозможно изменить или удалить системный триггер -607 -335544657 -dsql_no_blob_array -Array/BLOB/DATE data types not allowed in arithmetic. Типы данных мэссив/blob/date недо- пустимы в арифметических операциях -607 -335544746 -reftable_requires_pk -"REFERENCES table" without "(column)" requires PRIMARY KEY on referenced table. "references таблица" без указания столбца требует первичного ключа в таблицу, на которую осуществляется ссылка -607 -335544815 -generat or__name -GENERATOR <string>. Генератор <строка> -607 -335544816 -udf_name -UDF <string>. UDF <строка> -607 -336003074 -dsql_dbkey_from_non_table -Cannot SELECT RDB$DB_KEY from a stored procedure. Невозможно выполнить select rdb$db_key из хранимой процедуры
Коды ошибок 1025 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -607 -336003086 -dsql_udf_return_pos_err -External function should have return position between 1 and <number>. Внешняя функция должна иметь пози- цию возвращаемого значения между 1 и <номер> -612 -336068812 -dyn_domain_name_exists -Cannot rename domain <string> to <string>. A domain with that name already exists. Невозможно переименовать домен <строка> в <строка>. Домен с этим именем уже существует -612 -336068813 - dyn_f i e ld_name_exi s t s -Cannot rename column <string> to <string>. A column with that name already exists in table <string>. Невозможно переименовать столбец <строка> в <строка>. Столбец с этим именем уже существует в таблице < строка > -615 -335544475 -relation__lock -Lock on table <string> conflicts with existing lock. Блокировка таблицы <строка> кон- фликтует с существующей блокиров- кой -615 -335544476 -record_lock -Requested record lock conflicts with existing lock. Запрашиваемая блокировка записи конфликтует с существующей блоки- ровкой -615 -335544507 - r a n g e_i n__u s e -Refresh range number <number> already in use. Обновляемый диапазон <номер> уже находится в использовании -616 -335544530 -primary_key_ref -Cannot delete PRIMARY KEY being used in FOREIGN KEY definition. Невозможно удалить первичный ключ, используемый в определении внешне- го ключа -616 -335544539 -integ__index_del -Cannot delete index used by an Integrity Constraint. Невозможно удалить индекс, исполь- зуемый в ограничении целостности -616 -335544540 -integ_index_mod -Cannot modify index used by an Integrity Constraint. Невозможно изменить индекс, исполь- зуемый в ограничении целостности 33 Зак. 420
1026 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -616 -33554454^ -check_trig_del -Cannot delete trigger used by a CHECK Constraint. Невозможно удалить триггер, исполь- зуемый в ограничении check -616 -335544543 -cnstrnt_fld_del -Cannot delete column being used in an Integrity Constraint. Невозможно удалить столбец, исполь- зуемый в ограничении целостности -616 -335544630 -dependency -There are <number> dependencies. Существуют зависимости <номер> -616 -335544674 -del_last_field -Last column in a table cannot be deleted. Последний столбец в таблице не мо- жет быть удален -616 -335544728 -integ_index_deactivate -Cannot deactivate index used by an Integrity Constraint. Невозможно деактивировать триггер, используемый в ограничении целост- ности -616 -335544729 -integ_deactivate_primary -Cannot deactivate primary index. Невозможно деактивировать первич- ный индекс -617 -335544542 - che c k__t r i g_upda t e -Cannot update trigger used by a CHECK Constraint. Невозможно обновить триггер, исполь- зуемый в ограничении check -617 -335544544 -cnstrnt_fld_rename -Cannot rename column being used in an Integrity Constraint. Невозможно переименовать столбец, используемый в ограничении целост- ности -618 -335544537 -integ_index_seg_del -Cannot delete index segment used by an Integrity Constraint. Невозможно удалить сегмент индекса, используемый в ограничении целост- ности -618 -335544538 -integ_index_seg_mod -Cannot update index segment used by an Integrity Constraint. Невозможно изменить сегмент индек- са, используемый в ограничении целостности
Коды ошибок 1027 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -625 -335544347 -not_valid -Validation error for column <string>, value "<string>". Ошибка проверки для столбца <стро- ка>, значение "<строка>" -637 -335544664 -dsql_duplicate_spec -Duplicate specification of <string> - not supported. Дублирование спецификации для <строка> не поддерживается -660 -335544533 -foreign_key_notfound -Non-existent PRIMARY or UNIQUE KEY specified for FOREIGN KEY. He существует первичный или уни- кальный ключ, указанный для внешне- го ключа -660 -335544628 -idx__create_err -Cannot create index <string>. Невозможно создать индекс <строка> -663 -335544624 -idx_seg_err -Segment count of 0 defined for index <string>. Счетчик сегментов 0 определен для индекса <строка> -663 -335544631 -idx_key_err -Too many keys defined for index <string>. Слишком много ключей определено для индекса <строка> -663 -335544672 -key_f ield__err -Too few key columns found for index <string> (incorrect column name?). Слишком мало ключевых столбцов найдено для индекса <строка> (не- верное имя столбца?) -664 -335544434 -keytoobig -Key size exceeds implementation restriction for index "<string>". Размер ключа превышает ограничения реализации для индекса "<строка>" -677 -335544445 -ext_err -<string> extension error/ Ошибка расширения <строка> -685 -335544465 -bad_segstr_type -Invalid BLOB type for operation. Неверный тип blob для операции -685 -335544670 -blob_idx_err -Attempt to index BLOB column in index <string>. Попытка включить столбец blob в индекс <строка>
1028 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -685 -335544671 -array_idx_err -Attempt to index array column in index <string>. Попытка включить столбец массива в индекс <строка> -689 -335544403 -badpagtyp -Page <number> is of wrong type (expected <number>, found <number>) . Страница <номер> имеет неверный тип (ожидается <номер>, найдено <номер>) -689 -335544650 -page_type_err -Wrong page type. Неверный тип страницы -690 -335544679 -no_segments_err -Segments not allowed in expression index <string>. Сегменты недоступны в индексном выражении <строка> -691 -335544681 -rec_size_err -New record size of <number> bytes is too big. Новая запись размера <число> байт слишком велика -692 -335544477 -max_idx -Maximum indexes per table (<number>) exceeded. Превышено максимальное количество индексов для таблицы (<число>) -693 -335544663 -req_max_clones__exceeded -Too many concurrent executions of the same request. Слишком много выполнений того же самого запроса -694 -335544684 -no_field_access -Cannot access column <string> in view <string>. Невозможен доступ к столбцу <строка> в просмотре <строка> -802 -335544321 -arith_except -Arithmetic exception, numeric overflow, or string truncation. Арифметическое исключение, число- вое переполнение или усечение строки -803 -335544349 -no_dup -Attempt to store duplicate value (visible to active transactions) in unique index "<string>". Попытка сохранения дубликата значе- ния (видимого в активной транзакции) в уникальном индексе <строка>
Коды ошибок 1029 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -803 -3355446651 -unique_ key_violation -Violation of PRIMARY or UNIQUE KEY constraint "<string>" on table "<string>". Нарушение ограничения "<строка>" для первичного или уникального ключа для таблицы "<строка>" -804 -335544380 -wronumarg -Wrong number of arguments on call. Неверное количество аргументов при вызове -804 -335544583 -dsql_sqlda_err -SQLDA missing or incorrect version, or incorrect number/type of variables. SQLDA отсутствует или имеет невер- ную версию либо неверное количество или тип переменных -804 -335544586 -dsql_function_err -Function unknown. Неизвестная функция -804 -335544713 -dsql_sqlda_va1ue_e r r -Incorrect values within SQLDA structure. Неверные значения в структуре SQLDA -806 -335544600 -col_name_err -Only simple column names permitted for VIEW WITH CHECK OPTION. Только простые имена столбцов до- пустимы В VIEW WITH CHECK OPTION -807 -335544601 -where_err -No WHERE clause for VIEW WITH CHECK OPTION. Нет предложения where для view with CHECK OPTION -808 -335544602 -table_view_err -Only one table allowed for VIEW WITH CHECK OPTION. Только одна таблица допустима для VIEW WITH CHECK OPTION -809 -335544603 -distinct_err -DISTINCT, GROUP or HAVING not permitted for VIEW WITH CHECK OPTION. He разрешены distinct, group или HAVING В VIEW WITH CHECK OPTION -810 -335544605 -subquery__err -No subqueries permitted, for VIEW WITH CHECK OPTION. He позволены подзапросы для view WITH CHECK OPTION
1030 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -811 -335544652 -sing_select_err -Multiple rows in singleton select. Множество строк в одиночном select -816 -335544651 -ext__readonly_err -Cannot insert because the file is readonly or is on a read only medium. Невозможно добавление, потому что файл является файлом только для чтения или располагается на устрой- стве только для чтения -816 -335544715 -extfile_uns_op -Operation not supported for EXTERNAL FILE table <string>. Операция не поддерживается для таблицы внешнего файла <строка> -817 -335544361 -read_only_trans -Attempted update during read- only transaction. Попытка изменения при транзакции только для чтения -817 -335544371 -segstr_no_write -Attempted write to read-only BLOB. Попытка записи в blob только для чтения -817 -335544444 -read__only -Operation not supported. Операция не поддерживается -817 -335544765 -read_only_database -Attempted update on read-only database. Попытка изменения базы данных толь- ко для чтения -817 -335544766 -must_be_dialect_2_and_up -SQL dialect <string> is not supported in this database. Диалект SQL <строка> не поддержи- вается в этой базе данных -817 -335544793 -ddl_not_allowed_by_db_sq l_dial -Metadata update statement is not allowed by the current database SQL dialect <number>. Оператор изменения метаданных не- допустим в текущем диалекте SQL базы данных <строка> -817 -336003079 -isc_sql__dialect_conflict _num -DB dialect <nuiaber> and client dialect <number> conflict with respect to numeric precision <number>. Диалект базы данных <чиспо> и диа- лект клиента <число> конфликтуют в отношении точности чисел <строка>
Коды ошибок 1031 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -820 -335544356 -obsolete_metadata -Metadata is obsolete. Устаревшие метаданные -820 -335544379 -wrong_ods -Unsupported on-disk structure for file <string>; found <пшпЬег>, support <mber>. Неподдерживаемая структура на диске (ODS) для файла <строка>; найдено <номер>, поддерживается <номер> -820 -335544437 -wrodynver -Wrong DYN version. Неверная версия DYN -820 -335544467 -high_minor -Minor version too high found <number> expected <number>. Найдена слишком высокая минималь- ная версия <номер>, ожидается <номер> -823 -335544473 -invalid_bookmark -Invalid bookmark handle. Неверный дескриптор закладки -824 -335544474 -bad—lock_level -Invalid lock level <number>. Неверный уровень блокировки <номер> -825 -335544519 -bad—lock_handle -Invalid lock handle. Неверный дескриптор блокировки -826 -335544585 - d s ql__s tmt_handle -Invalid statement handle. Неверный дескриптор оператора -827 -335544655 -invalid—direction -Invalid direction for find operation. Неверное направление для операции поиска -827 -335544718 -invalid_key -Invalid key for find operation. Неверный ключ для операции поиска -828 -335544678 - inval_key_jposn -Invalid key position. Неверная позиция ключа -829 -335544616 -field—ref_err -Invalid column reference. Неверная ссылка на столбец -829 -336068816 -dyn_char_fld—too_small -New size specified for column <string> must be at least <number> characters. Указанный новый размер для столбца <строка> должен иметь, по меньшей мере, <номер> символов
1032 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -829 -336068817 -dуinva1id_dtype_ conversion -Cannot change datatype for <string>. Conversion from base type <string> to <string> is not supported. Невозможно изменить тип данных для <строка>. Преобразование из базово- го типа <строка> в <строка> не под- держивается -829 -336068818 -dyn_dtype__conv_invalid -Cannot change datatype for column <string> from a character type to a non-character type. Невозможно изменить тип данных для столбца <строка> из набора символов в тип, не имеющий набора символов -830 -335544615 -field_aggregate__err -Column used with aggregate. Столбец используется в агрегате -831 -335544548 -primary__key_exists -Attempt to define a second PRIMARY KEY for the same table. Попытка определения второго первич- ного ключа для той же таблицы -832 -335544604 -key_field_count_err -FOREIGN KEY column count does not match PRIMARY KEY. Количество столбцов внешнего ключа не соответствует первичному ключу -833 -335544606 -expression_eval_err -Expression evaluation not supported. Вычисление выражения не поддержи- вается -833 -335544810 -date_range_exceeded -Value exceeds the range for valid dates. Значение превышает диапазон допус- тимых дат -834 -335544508 -range_not_found -Refresh range number <number> not found. Номер диапазона обновления <номер> не найден -835 -335544649 -bad_checksum -Bad checksum. Ошибочная контрольная сумма -836 -335544517 -except -Exception <number>. Исключение <номер> -837 -335544518 -cache_restart -Restart shared cache manager. Повторный запуск менеджера совме- стно используемого кэша
Коды ошибок 1033 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -838 -335544560 -shutwarn -Database <string> shutdown in <number> seconds. База данных <строка> остановлена на <номер> секунд -841 -335544677 -version_err -Too many versions. Слишком много версий -842 -335544697 -precision_err -Precision must be from 1 to 18. Точность должна быть между 1 и 18 -842 -335544698 -scale_nogt -Scale must be between zero and precision. Масштаб должен быть между нулем и точностью -842 -335544699 -expec_short -Short integer expected. Ожидается короткое целое -842 -335544700 -expec_long -Long integer expected. Ожидается длинное целое -842 -335544701 -expec_ushort -Unsigned short integer expected. Ожидается беззнаковое короткое це- лое -842 -335544712 -expec_positive -Positive value expected. Ожидается положительное значение -901 -335544322 -bad_dbkey -Invalid database key. Неверный ключ базы данных -901 -335544326 -bad_dpb_form -Unrecognized database parameter block. Нераспознанный блок параметров базы данных -901 -335544328 -bad_segstr_handle Invalid BLOB handle. Неверный дескриптор blob -901 -335544329 -bad_segstr_id Invalid BLOB ID. Неверный идентификатор blob -901 -335544330 -bad_tpb__content -Invalid parameter in transaction parameter block. Неверный параметр в блоке парамет- ров транзакции -901 -335544331 -bad__tpb_form -Invalid format for transaction parameter block. Неверный формат блока параметров транзакции
1034 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -335544332' -bad_trans_handle -Invalid transaction handle (expecting explicit transaction start). Неверный дескриптор транзакции (ожидается явный запуск транзакции) -901 -335544337 -excess_trans -Attempt to start more than <number> transactions. Попытка запуска более чем <номер> транзакций -901 -335544339 -infinap -Information type inappropriate for object specified. Информационный тип не соответству- ет указанному объекту -901 -335544340 -infona -No information of this type available for object specified. Никакой информационный тип не дос- тупен для указанного объекта -901 -335544341 -infunk -Unknown information item. Неизвестный информационный эле- мент -901 -335544342 -integ_fail -Action cancelled by trigger (<number>) to preserve data integrity. Отменено действие в триггере (<но- мер>) для сохранения целостности данных -901 -335544345 -lock_conflict -Lock conflict on no wait transaction. Конфликт блокировки для транзакции NO WAIT -901 -335544350 -no_finish -Program attempted to exit without finishing database. Программа пытается завершиться без закрытия базы данных -901 -335544353 -no_recon -Transaction is not in limbo. Транзакция не является зависшей -901 -335544355 -no_segstr_close -BLOB was not closed. blob не был закрыт -901 -335544357 -open_trans -Cannot disconnect database with open transactions (<numt>er> active). Невозможно отключиться от базы дан- ных при наличии открытой транзакции (активная <номер>)
Коды ошибок 1035 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -335544358 -port_len -Message length error (encountered <number>, expected <number>). Ошибка длины сообщения (встречена <число>, ожидается <чиспо>) -901 -335544363 -req_no_trans -No transaction for request. Для запроса нет транзакции -901 -335544364 -req__sync -Request synchronization error. Ошибка синхронизации запроса -901 -335544365 - r eq_wr ong_db -Request referenced an unavailable database. Запрос ссылается на недоступную базу данных -901 -335544369 -segstr_no_read -Attempted read of a new, open BLOB. Попытка чтения нового, открытого BLOB -901 -335544370 -segstr_no_trans -Attempted action on blob outside transaction. Попытка действий c blob за предела- ми транзакции -901 -335544372 -s egs t r_wrong_db -Attempted reference to BLOB in unavailable database. Попытка ссылки на blob в недоступ- ной базе данных -901 -335544376 -unres__rel -Table <string> was omitted from the transaction reserving list. Таблица <строка> была опущена в зарезервированном списке транзакции -901 -335544377 -uns_ext -Request includes a DSRI extension not supported in this implementation. Запрос включает расширение DSRI, не поддерживаемое в этой реализации -901 -335544378 -wish_list -Feature is not supported. Возможность не поддерживается -901 -335544382 -random -<string>. <строка> -901 -335544383 -fatal—conflict -Unrecoverable conflict with limbo transaction <number>. Неперекрываемый конфликт с завис- шей транзакцией <число>
1036 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -335544392 -bdbincon -Internal error. Внутренняя ошибка -901 -335544407 -dbbnotzer -Database handle not zero. Дескриптор базы данных не ноль -901 -335544408 -tranotzer -Transaction handle not zero. Дескриптор транзакции не ноль -901 -335544418 -trainlim -Transaction in limbo. Зависшая транзакция -901 -335544419 -notinlim -Transaction not in limbo. Транзакция не зависшая -901 -335544420 -traoutsta -Transaction outstanding. Ожидающая выполнения транзакция -901 -335544428 -badmsgnum -Undefined message number. Неопределенный номер сообщения -901 -335544431 -blocking__signal -Blocking signal has been received. Был получен сигнал блокировки -901 -335544442 -noargacc_read -Database system cannot read argument <number>. Система базы данных не может читать аргумент <число> -901 -335544443 -noargacc_write -Database system cannot write argument <number>. Система базы данных не может писать аргумент <число> -901 -335544450 -misc_interpreted -<string>. <строка> -901 -335544468 -tra_state -Transaction <number> is <string> Транзакция <число> является <строка> -901 -335544485 -bad_stmt_handle -Invalid statement handle. Неверный дескриптор оператора -901 -335544510 -lock_timeout -Lock time-out on wait transaction. Истечение времени ожидания блоки- ровки для транзакции wait -901 -335544559 -bad_svc_handle -Invalid service handle. Неверный дескриптор сервиса
Коды ошибок 1037 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -335544961 -wrospbver -Wrong version of service parameter block. Неверная версия блока параметра сервиса -901 -335544562 -bad__spb_form -Unrecognized service parameter block. Нераспознанный блок параметров сервиса -901 -335544563 -svcnotdef -Service <stting> is not defined. Сервис <строка> не определен -901 -335544609 -index_name -INDEX <string>. Индекс <строка> -901 -335544610 -exception_name -EXCEPTION <string>. Исключение <строка> -901 -335544611 -field_name -COLUMN <string>. Столбец <строка> -901 -335544613 -union_err -Union not supported. Объединение не поддерживается -901 -335544614 -dsql_construct_err -Unsupported DSQL construct. Неподдерживаемая конструкция DSQL -901 -335544623 -dsql_domain_err -Illegal use of keyword VALUE. Неверное использование ключевого слова VALUE -901 -335544626 -table_name -TABLE <String>. Таблица <строка> -901 -335544627 -proc_name -PROCEDURE <string>. Процедура <строка> -901 -335544641 - d s ql_doma in_n о t _f own d -Specified domain or source column <string> does not exist. Указанный домен или исходный стол- бец <строка> не существует -901 -335544656 -dsql_var_conflict -Variable <string> conflicts with parameter in same procedure. Переменная <строка> конфликтует с параметром в той же процедуре -901 -335544666 -srvr_version_too_old -Server version too old to support all CREATE DATABASE options. Версия сервера слишком старая для поддержки всех режимов create DATABASE
1038 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -335544673 -no__delete -Cannot delete. Удаление невозможно -901 -335544675 -sort_err -Sort error. Ошибка сортировки -901 -335544703 -svcnoexe -Service <string> does not have an associated executable. Сервис <строка> не имеет связанного исполняемого модуля -901 -335544704 -net_lookup_err -Failed to locate host machine. Ошибка в локализации хост-машины -901 -335544705 -service_unknown -Undefined service <string>/<string>. He определен сервис <cmpo- ка>/<строка> -901 -335544706 -ho s t_un known -The specified name was not found in the hosts file or Domain Name Services. Указанное имя не было найдено в файле hosts или в сервисе имен доме- нов -901 -335544711 -unprepared_stmt -Attempt to execute an unprepared dynamic SQL statement. Попытка выполнения неподготовлен- ного оператора динамического SQL -901 -335544716 -svc_in__u.se -Service is currently busy: <string>. В настоящий момент сервис занят -901 -335544731 -tra_mus t_sweep — -901 -335544740 -udf_exception -A fatal exception occurred during the execution of a user defined function. Возникло фатальное исключение в процессе выполнения функции, опре- деленной пользователем -901 -335544741 -lost_db_connection -Connection lost to database. Потеряно соединение с базой данных -901 -335544742 -no_write_user_priv -User cannot write to RDB$USER_PRIVILEGES. Пользователь не может писать В rdb$user_privileges
Коды ошибок 1039 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -335544767 -blob_filter__exception -A fatal exception occurred during the execution of a blob filter. Возникло фатальное исключение в процессе выполнения фильтра blob -901 -335544768 -exception_access_ violation -Access violation. The code attempted to access a virtual address without privilege to do so. Нарушение доступа. Код пытается получить доступ к виртуальному адре- су без соответствующих привилегий -901 -335544769 -Datatype misalignment. The attempted to read or write a value that was not stored on a memory boundary. Неверное выравнивание типа данных. Попытка читать или писать значение, которое не было сохранено в нужных границах памяти -901 -335544770 -Array bounds exceeded. The code attempted to access an array element that is out of bounds. Превышены размеры массива. Код пытается получить доступ к элементу массива, который находится за преде- лами его границ -901 -335544771 -Float denormal operand. One of the floating-point operands is too small to represent a standard float value. Ненормализованный операнд для чис- ла с плавающей точкой. Один из опе- рандов, ссылающихся на число с пла- вающей точкой, слишком мал для представления стандартного значения с плавающей точкой -901 -335544772 -exception_float_divide_ by_zero -Floating-point divide by zero. The code attempted to divide a floating-point value by zero. Деление числа с плавающей точкой на ноль. Код пытается разделить значе- ние с плавающей точкой на ноль
1040 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -33554477^ -exception_float_inexact_ result -Floating-point inexact result. The result of a floating-point operation cannot be represented as a decimal fraction. Неточный результат для числа с пла- вающей точкой. Результат операции для чисел с плавающей точкой не мо- жет быть представлен в виде десятич- ной дробной части -901 -335544774 -exception_float_invalid_ operand -Floating-point invalid operand. An indeterminant error occurred during a floating-point operation. Неверный операнд в операции с пла- вающей точкой. Неопределенная ошибка возникает в процессе опера- ции с числами с плавающей точкой -901 -335544775 -exception_float_overflow -Floating-point overflow. The exponent of a floating-point operation is greater than the magnitude allowed. Переполнение числа с плавающей точкой. Экспонента операции с числа- ми с плавающей точкой больше, чем доступные размеры -901 -335544776 -exception_float_stack_ check -Floating-point stack check. The stack overflowed or underflowed as the result of a floating-point operation. Проверка стека чисел с плавающей точкой. Переполнение стека или поте- ря значащих разрядов является ре- зультатом операции с числами с пла- вающей точкой -901 -335544777 -exception_float_underflow -Floating-point underflow. The exponent of a floating-point operation is less than the magnitude allowed. Потеря значащих разрядов числа с плавающей точкой. Экспонента опера- ции с плавающей точкой меньше, чем допустимый размер -901 -335544778 -Integer divide by zero. The code attempted to divide an integer value by an integer divisor of zero. Деление целого на ноль. Код пытается разделить целое значение на целый делитель, который является нулем
Коды ошибок 1041 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -335544779, -exception_integer_ overflow -Integer overflow. The result of an integer operation caused the most significant bit of the result to carry. Целочисленное переполнение. Ре- зультат операции над целыми числами дает больше знаков, чем может хра- ниться -901 -335544780 -except i on_unkn own -An exception occurred that does not have a description. Exception number %X. Появилось исключение, не имеющее дескриптора. Исключение с номером %х -901 -335544781 -exception_stack_overflow -Stack overflow. The resource requirements of the runtime stack have exceeded the memory available to it. Переполнение стека. Требуемые для стека ресурсы во время выполнения исчерпали доступную память -901 -335544782 -exception__sigsegv -Segmentation Fault. The code attempted to access memory without privileges. Ошибка сегментирования. Код пытает- ся получить доступ к памяти без при- вилегий -901 -335544783 -exception_sigill -Illegal Instruction. The Code attempted to perform an illegal operation. Неверная операция. Код пытается выполнить неверную операцию -901 -335544784 -exception__sigbus -Bus Error. The Code caused a system bus error. Ошибка канала. Выполнение кода приводит к системной ошибке канала -901 -335544785 -exception_sigfpe -Floating Point Error. The Code caused an Arithmetic Exception or a floating point exception. Ошибка десятичной точки. Код выдает арифметическое исключение или ис- ключение десятичной точки -901 -335544786 -ext_file_delete -Cannot delete rows from external files. Невозможно удалить строки из внеш- них файлов 34 Зак. 420
1042 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -335544787 -ext_file_modify -Cannot update rows in external files. Невозможно изменить строки во внеш- них файлах -901 -335544788 -adm_task_denied -Unable to perform operation. You must be either SYSDBA or owner of the database. Невозможно выполнить операцию. Вы должны быть или пользователем SYSDBA, либо владельцем базы данных -901 -335544794 -cancelled -Operation was cancelled. Операция была отменена -901 -335544797 -svcnouser -User name and password are required while attaching to the services manager. Требуются имя пользователя и пароль при подключении к Менеджеру сервисов -901 -335544801 -datype_notsup -Data type not supported for arithmetic. Тип данных не поддерживается для арифметических действий -901 -335544803 -dialect_not_changed -Database dialect not changed. Диалект базы данных не был изменен -901 -335544804 -database_create_failed -Unable to create database <string>. Невозможно создать базу данных <строка> -901 -335544805 -inv_dialect_specified -Database dialect <number> is not a valid dialect. Диалект базы данных <число> не яв- ляется правильным диалектом -901 -335544806 -valid__db_dialects -Valid database dialects are <string>. Допустимыми диалектами базы дан- ных являются <строка> -901 -335544811 -inv_client_dialect_speci fied -Passed client dialect <nwnber> is not a valid dialect. Переданный клиентом диалект <чис- ло> не является верным диалектом
Коды ошибок 1043 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -335544812 -valid_client_dialects -Valid client dialects are <string>. Допустимыми клиентскими диалектами являются <строка> -901 -335544814 -service_not_supported -Services functionality will be supported in a later version of the product Функциональность сервисов будет поддерживаться на более поздних версиях продукта -901 -335740929 -gfix_dbj"iame -Data base file name (<stringr>) already given. Имя файла базы данных (<строка>) уже предоставлено -901 -335740930 -gfix_invalid_sw -Invalid switch <string>. Неверный переключатель <строка> -901 -335740932 -gfix_incmp_sw -Incompatible switch combination. Несовместимая комбинация переклю- чателей -901 -335740933 -gfix_replay_req -Replay log pathname required. Требуется путь к протоколу -901 -335740934 -gfix_pgbuf_req -Number of page buffers for cache required. Требуется буфер страниц для кэша -901 -335740935 -gfix_val_req -Numeric value required. Требуется числовое значение -901 -335740936 -g fix_pval_r e q -Positive numeric value required. Требуется положительное числовое значение -901 -335740937 -gfix_trn_req -Number of transactions per sweep required. Требуется номер транзакции для чистки -901 -335740940 -gfix_full_req -’’full" or ’’reserve" required Требуются "full" или "reserve" -901 -335740941 -g f ix_us rname_req -User name required. Требуется имя пользователя -901 -335740942 -gfix_pass_req -Password required. Требуется пароль
1044 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -335740943 -gfix__subs_name -Subsystem name. Имя подсистемы -901 -335740945 -gfix_sec_req -Number of seconds required. Требуется число или секунды -901 -335740946 -gf ix__nval_req -Numeric value between 0 and 32767 inclusive required. Требуется числовое значение между 9 и 32 767, включительно -901 -335740947 -gfix_type_shut -Must specify type of shutdown. Должен быть указан тип останова -901 -335740948 -gfix_retry -Please retry, specifying an option. Пожалуйста, повторите, указав режим -901 -335740951 -gfix_retry_db -Please retry, giving a database name. Пожалуйста, повторите, задав имя базы данных -901 -335740991 -gfix_exceed_max -Internal block exceeds maximum size. Внутренний блок превышает макси- мальный размер -901 -335740992 -gfix_corrupt_pool -Corrupt pool. Разрушен пул -901 -335740993 -gfix_mem_exhausted -Virtual memory exhausted. Исчерпана виртуальная память -901 -335740994 -gfix__bad_pool -Bad pool id. Неверный идентификатор пула -901 -335740995 -gfix_trn_not_valid -Transaction state <number> not in valid range. Состояние транзакции <строка> не находится в допустимом диапазоне -901 -335741012 -gfix_unexp_eoi -Unexpected end of input. Неверное завершение ввода -901 -335741018 -gfix_recon_fail -Failed to reconnect to a transaction in database <string>. Ошибки при повторном соединении с транзакцией в базе данных <строка> -901 -335741036 -gfix_trn_unknown -Transaction description item unknown. Неизвестное описание элемента тран- закции
Коды ошибок 1045 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -335744038 -gfix_mode_req -"read_only" or "read_write" required. Требуется "только для чтения" или "чтение и запись" -901 -336068796 -dyn_role_does_not_exist -SQL role <string> does not exist. He существует роль SQL <строка> -901 -336068797 - dy n_n o__g ran t_admi n_op t -User <string> has no grant admin option on SQL role <string>. Пользователь <строка> не получил от администратора роли SQL <строка> -901 -336068798 -dyn_user_no t_r ole_member -User <string> is not a member of SQL role <string>. Пользователь <строка> не является участником роли SQL <строка> -901 -336068799 -dyn_delete_role_failed -<string> is not the owner of SQL role <string>. <Строка> не является владельцем роли SQL <строка> -901 -336068800 -dyn_grant__role_to_user -<string> is a SQL role and not a user. <Строка> является ролью SQL, а не пользователем -901 -336068801 -dyn_inv_sql_role_name -User name <string> could not be used for SQL role. Имя пользователя <строка> не может быть использовано в качестве роли SQL -901 -336068802 -dyn_dup_sql_role -SQL role <string> already exists. Роль SQL <строка> уже существует -901 -336068803 -dyn_kywd_spec_for_role -Keyword <string> can not be used as a SQL role name. Ключевое слово <строка> не может быть использовано в качестве имени роли SQL -901 -336068804 -dyn_roles_not_supperted -SQL roles are not supported in on older versions of the database. A backup and restore of the database is required. Роли SQL не поддерживаются в ста- рых версиях базы данных. Требуется копирование и восстановление базы данных
1046 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -336068820 -dyn_zero_len_id -Zero length identifiers are not allowed. Недопустима нулевая длина иденти- фикатора -901 -336330753 -gbak___unknown_switch -Found unknown switch. Найден неизвестный переключатель -901 -336330754 -gbak_page_size_missing -Page size parameter missing. Отсутствует параметр размера стра- ницы -901 -336330755 -gbak_page_size_toobig -Page size specified (<number>) greater than limit (8192 bytes). Указанный размер страницы 1<число>) больше ограничения (8192 байт) -901 -336330756 -gbak_redir_ouput_missing -Redirect location for output is not specified. He задано перенаправление вывода -901 -336330757 -gbak_switches_conflict -Conflicting switches for backup/restore. Конфликт переключателей для копи- рования/восстановления -901 -336330758 -gbak_unknown_device -Device type <string> not known. Тип устройства <строка> не известен -901 -336330759 -gbak_no_protection -Protection is not there yet. Защита пока не установлена -901 -336330760 - gbak_pa ge_s i z e_no t__al 1 ow ed -Page size is allowed only on restore or create. Размер страницы нужен только при восстановлении или при создании -901 -336330761 -gbak_multi_source_dest -Multiple sources or destinations specified. Указано множество источников или результатов -901 -336330762 - gbak_ f i 1 ename_mi s s ing -Requires both input and output filenames. Требуются имена входного и выходно- го файлов -901 -336330763 -gbak__dup_inout_names -Input and output have the same name. Disallowed. Вход и выход имеют одинаковые име- на. Отвергаются
Коды ошибок 1047 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -336330764 -gbak—inv_page_size -Expected page size, encountered "<string>". Ожидается размер страницы, появи- лось "<строка>" -901 -336330765 - gba k_db_spe c i f i ed -REPLACE specified, but the first file <string> is a database. Указано replace, однако первый файл <строка> является базой данных -901 -336330766 -gbak_db_exists -Database <string> already exists. To replace it, use the-R switch. База данных <строка> уже существу- ет. Для ее замены используйте пере- ключатель -R -901 -336330767 -gbak_unk_device -Device type not specified. He задан тип устройства -901 -336330772 -gbak—blob—info_failed -Gds_$blob_info failed. Ошибка B Gds_$blob_info -901 -336330773 -gba k_un k_b1ob_i t em -Do not understand BLOB INFO item <number>. Неизвестный элемент blob info <число> -901 -336330774 -gbak_get_seg_f ailed -Gds_$get—segment failed. Ошибка В Gds_$get_segment -901 -336330775 -gbak_close__blob_f ailed -Gds_$close—blob failed. Ошибка В Gds_$close_blob -901 -336330776 -gbak_open_blob_failed -Gds_$open_blob failed. Ошибка в Gds_$open_blob -901 -336330777 -gbak_put_blr_gen_id_fail ed -Failed in put_blr_gen_id. Ошибка в put_blr_gen_id -901 -336330778 -gbak_unk_type -Data type <number> not understood. Тип данных <число> не известен -901 -336330779 -gbak—comp_req_failed -Gds_$compile_request failed. Ошибка В Gds_$compile-request -901 -336330780 -gbak—start_req_failed -Gds_Sstart-request failed. Ошибка В Gds_$start—request -901 -336330781 -gbak_rec_failed - gds_$receive failed. Ошибка в gds_$receive
1048 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -336330782 -gbak__rel_req_failed -Gds_$release__request failed. Ошибка В Gels._Srelease_request -901 -336330783 -gbak_db_info_failed -gds_$database_info failed. Ошибка в gds_$database_info -901 -336330784 -gbak__no_db_desc -Expected database description record. Ожидается запись описания базы дан- ных -901 -336330785 -gbak_db_create_failed -Failed to create database <string>. Ошибка при создании базы данных <строка> -901 -336330786 -gbak_decomp_len_error -RESTORE: decompression length error. restore: ошибка в длине декомпрес- сии -901 -336330787 -gbak_tbl_mis s ing -Cannot find table <string>. Невозможно найти таблицу <строка> -901 -336330788 - gba k__b 1 ob_col_mi s s ing -Cannot find column for BLOB. Невозможно найти столбец для blob -901 -336330789 -gbak_create_blob_failed -Gds_$create_blob failed. Ошибка В Gds_$create_blob -901 -336330790 -gbak_put_seg_ failed -Gds_$put_segment failed. Ошибка В Gds_$put_segment -901 -336330791 -gb a k_re c_le n_e xp -Expected record length. Ожидается длина записи -901 -336330792 -gbak_inv_rec_len -Wrong length record, expected <number> encountered <.number>. Неверная длина записи, ожидается <число>, встретилось <число> -901 -336330793 -gbak_exp_data__type -Expected data attribute. Ожидается атрибут данных -901 -336330794 -gbak_gen_id_failed -Failed in store_blr_gen_id. Ошибка в store_blr__gen_id -901 -336330795 -gba k_unk_re c_t ype -Do not recognize record type <number>. He распознан тип записи <число>
Коды ошибок 1049 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -336330796 - gb a k_inv_bkup_ve г -Expected backup version 1, 2, or 3. Found <number>. Ожидается версия копии 1, 2 или 3. Найдено <число> -901 -336330797 -gbak_missing_bkup_desc -Expected backup description record. Ожидается запись описания копии -901 -336330798 -gbak_string_trunc -String truncated. Усечение строки -901 -336330799 -gbak—cant_rest_record -Warning—record could not be restored. Предупреждение — запись не может быть восстановлена -901 -336330800 -gbak_send_failed -GdS—$send failed. Ошибка В Gds_$send -901 -336330801 -gbak_no_tbl—name -No table name for data. Для данных нет имени таблицы -901 -336330802 -gbak-Unexp—eof -Unexpected end of file on backup file. Конец файла в файле копии -901 -336330803 -gbak_db_format_too_old -Database format <r.umber> is too old to restore to. Формат базы данных <число> слишком старый для восстановления -901 -336330804 -gbak_inV—array_dim -Array dimension for column <string> is invalid. Неверная размерность массива для столбца <строка> -901 -336330807 -gbak_xdr_len_expected -Expected XDR record length. Ожидается длина записи -901 -336330817 -дЬак_ореП-Ькир_error -Cannot open backup file <string>. Невозможно открыть файл копии <строка> -901 -336330818 -gbak_open__error -Cannot open status and error output file <string>. Невозможно открыть состояние и ошибка вывода в файл <строка> -901 -336330934 -gbak—missing_Jalock_fac -Blocking factor parameter missing. Отсутствует параметр "коэффициент блокирования"
1050 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -336330935 -gbak_inv_blосk_fас -Expected blocking factor, encountered "<string>". Ожидается коэффициент блокирова- ния, встречено "<строка>" -901 -336330936 -gbak_block_fac_specified -A blocking factor may not be used in conjunction with device CT. Коэффициент блокирования не может использоваться вместе с устройством ст -901 -336330940 -gbak_missing_username -User name parameter missing. Отсутствует параметр "имя пользова- теля" -901 -336330941 -gbak__missing_password -Password parameter missing. Отсутствует параметр "пароль" -901 -336330952 -gbak_missing_skipped_byt es -Missing parameter for the number of bytes to be skipped. Отсутствует параметр количества про- пускаемых байтов -901 -336330953 -gbak_inv_skipped_bytes -Expected number of bytes to be skipped, encountered "<string>". Ожидается количество пропускаемых байтов, встречено "<строка>" -901 -336330965 -gbak__err_restore_charset -Bad attribute for RDB$CHARACTER_SETS. Неверный атрибут для RDB$CHARACTER__SETS -901 -336330967 -gbak_err_restore_collati on -Bad attribute for RDB$COLLATIONS. Неверный атрибут для RDB$COLLATIONS -901 -336330972 -gbak_read_error -Unexpected I/O error while reading from backup file. Ошибка ввода/вывода при чтении из файла копии -901 -336330973 -gbak_write__error -Unexpected I/O error while writing to backup file. Ошибка ввода/вывода при записи в файл копии -901 -336330985 -gbak_db_in_use -Could not drop database <string> (database might be in use). Невозможно удалить базу данных <строка> (возможно база данных ис- пользуется)
Коды ошибок 1051 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -336330990 -gbak_sysmemex -System memory exhausted. Исчерпана системная память -901 -336331002 -gbak_restore_role_failed -Bad attributes for restoring SQL role. Неверный атрибут для восстановления роли SQL -901 -336331005 -gbak__role_op_missing -SQL role parameter missing. Отсутствует параметр роли SQL -901 -336331010 -gbak_page__buf fers_missing -Page buffers parameter missing. Отсутствует параметр буферов стра- ниц -901 -336331011 -gbak_page_buf fers_ wrong_param -Expected page buffers, encountered "<string>". Ожидаются буферы страниц, встрече- но "<строка>" -901 -336331012 - gba k_page_bu f f e r s_r e s t о r e -Page buffers is allowed only on restore or create. Буферы страниц допустимы только при восстановлении или создании -901 -336331014 -gbak_inv_size -Size specification either missing or incorrect for file <string>. Указание размера отсутствует или неверное для файла <строка> -901 -336331015 -gbak_file_outof_sequence -File <string> out of sequence. Файл <строка> не задан в последова- тельности -901 -336331016 -gbak_join_file_missing -Can't join—one of the files missing. Соединение невозможно — один из файлов отсутствует -901 -336331017 -gbak__stdin_not_supptd -Standard input is not supported when using join operation. Стандартный ввод не поддерживается при использовании операции соедине- ния -901 -336331018 -gbak_stdout_not_supptd -Standard output is not supported when using split operation. Стандартный вывод не поддерживает- ся при использовании операции раз- деления
1052 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -336331019 -gbak_bkup_corrupt -Backup file <string> might be corrupt. Возможно файл копии <строка> раз- рушен -901 -336331020 -gbak_unk_db_file_spec -Database file specification missing. Отсутствует указание файла базы данных -901 -336331021 ygbak_hdr_,write_f ailed -Can't write a header record to file <string>. Невозможно записать заголовочную запись в файл <строка> -901 -336331022 -gbak__disk_space_ex -Free disk space exhausted. Исчерпано свободное дисковое про- странство -901 -336331023 -gbak_size_lt_min -File size given {<nuraber>') is less than minimum allowed {<number>') . Заданный размер файла (<число>) меньше минимально допустимого (<число>) -901 -336331025 -gbak_svc_name_mi s s ing -Service name parameter missing. Отсутствует параметр имени сервиса -901 -336331026 -gbak_not_ownr -Cannot restore over current database, must be SYSDBA or owner of the existing database. Невозможно восстановление поверх текущей базы данных, должен быть пользователь SYSDBA или владелец существующей базы данных -901 -336331031 -gbak_mode_req -”read_only" or "read__write” required. Требуется ’’read-only” или "read_write” -901 -336331033 -gbak_just_data -Just data ignore all constraints, etc. Данные игнорируют все ограничения -901 -336331034 -gbak_data_only -Restoring data only ignoring foreign key, unique, not null & other constraints. Восстановление данных только при игнорировании ограничений внешнего ключа, уникального ключа, not null и других ограничений
Коды ошибок 1053 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -336723983 -gsec_cant_open_db -Unable to open database. Невозможно открыть базу данных -901 -336723984 -gsec__switches_error -Error in switch specifications. Ошибка в задании переключателя -901 -336723985 -gsec _no_op__spec -No operation specified. He указана операция -901 -336723986 - g s e c_no_u s r _n ame -No user name specified. He задано имя пользователя -901 -336723987 -gsec_err__add -Add record error. Ошибка добавления записи -901 -336723988 -gsec_err_modify -Modify record error. Ошибка изменения записи -901 -336723989 - gs ec_e r r_f i nd_mod -Find/modify record error. Ошибка поиска/изменения записи -901 -336723990 -gsec_err_rec_not_found -Record not found for user: <string>. He найдена запись для пользователя <строка> -901 -336723991 -gsec_err_delete -Delete record error. Ошибка удаления записи -901 -336723992 -gsec_err_find_del -Find/delete record error. Ошибка поиска/удаления записи -901 -336723996 -gsec_err__find_disp -Find/display record error. Ошибка поиска/отображения записи -901 -336723997 -gsec_inv _param -Invalid parameter, no switch defined. Неверный параметр, не определено переключателей -901 -336723998 -gsec_op_specified -Operation already specified. Операция уже задана -901 -336723999 -gsec_pw_specified -Password already specified. Пароль уже задан -901 -336724000 -gsec_uid_specified -Uid already specified. uid уже задано -901 -336724001 -gsec_gid_specified -Gid already specified. gid уже задано
1054 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -336724002 -gsec_proj_specified -Project already specified. Проект уже задан -901 -336724003 -gsec_org_specified -Organization already specified. Организация уже задана -901 -336724004 -gsec__fname_specified -First name already specified. Имя уже задано -901 -336724005 -gsec_mname_specified -Middle name already specified. Второе имя уже задано -901 -336724006 -gsec_lname_specified -Last name already specified. Фамилия уже задана -901 -336724008 -gsec_inv_switch -Invalid switch specified. Задан неверный переключатель -901 -336724009 -gsec_amb_switch -Ambiguous switch specified. Задан неоднозначный переключатель -901 -336724010 -gsec__no__op_specified -No operation specified for parameters. Для параметров не задана операция -901 -336724011 -gsec_params_not__allowed -No parameters allowed for this operation. Параметры недопустимы для этой операции -901 -336724012 -gsec_incompat_switch -Incompatible switches specified. Заданы несовместимые переключа- тели -901 -336724044 - gs e c_i nv__u s e r n ame -Invalid user name (maximum 31 bytes allowed). Неверное имя пользователя (допусти- мо максимум 31 байт) -901 -336724045 -gsec_inv_pw_length -Warning-maximum 8 significant bytes of password used. Предупреждение — используется мак- симум 8 значащих байтов в исполь- зуемом пароле -901 -336724046 -gsec_db_specified -Database already specified. База данных уже задана -901 -336724047 -gsec_db_admin_specified -Database administrator name already specified. Имя администратора базы данных уже задано
Коды ошибок 1055 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -901 -336724048 -gsec_db_admin_pw_specifi ed -Database administrator password already specified. Пароль администратора базы данных уже задан -901 -336724049 -gsec_sql_role_specified -SQL role name already specified. Имя роли SQL уже задано -901 -336920577 -gstat_unknown_switch -Found unknown switch. Найден неизвестный переключатель -901 -336920578 -gstat_retry -Please retry, giving a database name. Пожалуйста, повторите, задав имя базы данных -901 -336920579 -gstat_wrong_ods -Wrong ODS version, expected <number>, encountered <number>. Неверная версия ODS, ожидается <число>, встречено <чцсло> -901 -336920580 -gstat_unexpected__eof -Unexpected end of database file. Конец файла базы данных -901 -336920605 -gstat_open_err -Can't open database file <string>. Невозможно открыть файл базы дан- ных <строка> -901 -336920606 -gstat_read_err -Can't read a database page. Невозможно прочесть страницу базы данных -901 -336920607 -gstat_sysmemex -System memory exhausted. Исчерпана системная память -902 -335544333 -bug__check -Internal gds software consistency check (<string>). Проверка достоверности внутреннего программного продукта gds (<строка>) -902 -335544335 -db__corrupt -Database file appears corrupt (<string>). Разрушение файла базы данных (<строка>) -902 -335544344 -io_error -I/O error for file %.0s"<string>" . Ошибка ввода/вывода для файла "<строка>" -902 -335544346 -metadata_corrupt -Corrupt system table. Разрушена системная таблица
1056 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -902 -335544373 -sys__request -Operating system directive <string> failed. Ошибочная директива операционной системы <строка> -902 -335544384 -badblk -Internal error. Внутренняя ошибка -902 -335544385 -invpoolcl -Internal error. Внутренняя ошибка -902 -335544387 -relbadblk -Internal error. Внутренняя ошибка -902 -335544388 -blktoobig -Block size exceeds implementation restriction. Размер блока превышает ограничение реализации -902 -335544394 -badodsver -Incompatible version of on-disk structure. Несовместимая версия для ODS -902 -335544397 -dirtypage -Internal error. Внутренняя ошибка -902 -335544398 -waifortra -Internal error. Внутренняя ошибка -902 -335544399 -doubleloc -Internal error. Внутренняя ошибка -902 -335544400 -nodnotfnd -Internal error. Внутренняя ошибка -902 -335544401 -dupnodfnd -Internal error. Внутренняя ошибка -902 -335544402 -locnotmar -Internal error. Внутренняя ошибка -902 -335544404 -corrupt -Database corrupted. База данных разрушена -902 -335544405 -badpage -Checksum error on database page <number>. Ошибка контрольной суммы для стра- ницы базы данных <число> -902 -335544406 -badindex -Index is broken. Индекс разрушен
Коды ошибок 1057 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -902 -335544409 -trareqmis -Transaction—request mismatch (synchronization error). Транзакция — несогласованный запрос (ошибка синхронизации) -902 -335544410 -badhndcnt -Bad handle count. Ошибочный счетчик дескриптора -902 -335544411 -wrotpbver -Wrong version of transaction parameter block. Неверная версия блока параметров транзакции -902 -335544412 -wroblrver -Unsupported BLR version (expected <number>, encountered <number>) . Неподдерживаемая версия BLR (ожи- дается <число>, встречено <число>) -902 -335544413 -wrodpbver -Wrong version of database parameter block. Неверная версия блока параметров базы данных -902 -335544415 -badrelation -Database corrupted. База данных разрушена -902 -335544416 -nodetach -Internal error. Внутренняя ошибка -902 -335544417 -notremote -Internal error. Внутренняя ошибка -902 -335544422 -dbfile -Internal error. Внутренняя ошибка -902 -335544423 -orphan -Internal error. Внутренняя ошибка -902 -335544432 -lockmanerr -Lock manager error. Ошибка менеджера блокировок -902 -335544436 -sqlerr -SQL error code = <number>. Код ошибки SQL = <число> -902 -335544448 -bad_sec_info — -902 -335544449 -invalid_sec_infо — -902 -335544470 -buf_invalid -Cache buffer for page <nuaber> invalid. Неверный буфер кэша для страницы <чиспо>
1058 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -902 -335544471 -indexnotdefined -There is no index in table <string> with id <number>. He существует индексов для таблицы <строка> с идентификатором <число> -902 -335544472 -login -Your user name and password are not defined. Ask your database administrator to set up a Firebird login. He определены ваше имя и пароль. Чтобы установить соединение с Firebird, обратитесь к администратору базы данных -902 -335544506 -shutinprog -Database <string> shutdown in progress. Выполняется останов базы данных <строка> -902 -335544528 -shutdown -Database <string> shutdown. База данных <строка> остановлена -902 -335544557 -shutfail -Database shutdown unsuccessful. Неуспешный останов базы данных -902 -335544569 -dsql_error -Dynamic SQL Error. Ошибка динамического SQL -902 -335544653 -psw_attach -Cannot attach to password database. Невозможно соединиться с базой дан- ных пароля -902 -335544654 -psw_start_trans -Cannot start transaction for password database. Невозможно стартовать транзакцию для базы данных пароля -902 -335544717 -err_stack_limit -Stack size insufficient to execute current request. Размер стека недостаточен для вы- полнения текущего запроса -902 -335544721 -network_error -Unable to complete network request to host "<string>". Невозможно завершить сетевой за- прос на хост "<строка>" -902 -335544722 -net_connect_err -Failed to establish а connection. Ошибка при установлении соединения
Коды ошибок 1059 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -902 -335544723 -net_connect_listen_err -Error while listening for an incoming connection. Ошибка при прослушивании входного соединения -902 -335544724 -net_event_connect_err -Failed to establish a secondary connection for event processing. Ошибка при установлении вторичного соединения для обработки события -902 -335544725 -net_event__listen_err -Error while listening for an incoming event connection request. Ошибка при прослушивании запроса события соединения -902 -335544726 -net__read__err -Error reading data from the connection. Ошибка чтения данных из соединения -902 -335544727 -net_write_err -Error writing data to the connection. Ошибка записи данных в соединение -902 -335544732 -unsupported_network_drive -Access to databases on file servers is not supported. Доступ к базам данных в файловых серверах не поддерживается -902 -335544733 -io_create_err -Error while trying to create file. Ошибка при попытке создания файла -902 -335544734 -io__open_err -Error while trying to open file. Ошибка при попытке открытия файла -902 -335544735 -io_close_err -Error while trying to close file. Ошибка при попытке закрытия файла -902 -335544736 -io_read_err -Error while trying to read from file. Ошибка при попытке чтения из файла -902 -335544737 -io__write__err -Error while trying to write to file. Ошибка при попытке записи в файл -902 -335544738 -io__delete_err -Error while trying to delete file. Ошибка при попытке удаления файла
1060 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -902 -335544739 -*io_access_err -Error while trying to access file. Ошибка при попытке доступа к файлу -902 -335544745 -login_same_as_role__name -Your login <string> is same as one of the SQL role name. Ask your database administrator to set up a valid Firebird login. Ваше регистрационное имя <строка> то же, что и имя роли SQL. Уточните у вашего администратора базы данных допустимое регистрационное имя Firebird -902 -335544791 -file_in_use -The file <string> is currently in use by another process. Try again later. Файл <строка> в настоящее время используется другим процессом. Попытайтесь позже -902 -335544795 -unexp_spb_form -Unexpected item in service parameter block, expected <string>. Неопределенный элемент в блоке параметров сервиса, ожидается < строка > -902 -335544809 -extern_func_dir_error -Function <string> is in <string>, which is not in a permitted directory for external functions. Функция <строка> находится в <стро- ка>, что не является доступным ката- логом для внешних функций -902 -335544819 -io_32bit_exceeded_err -File exceeded maximum size of 2GB. Add another database file or use a 64 bit I/O version of Firebird. Файл превысил максимальный размер 2 Гбайт. Добавьте другой файл базы данных или используйте 64-битовую версию Firebird -902 -335544820 -invalid_savepoint -Unable to find savepoint with name <string> in transaction context. Невозможно найти точку сохранения с именем <строка> в контексте тран- закции
Коды ошибок 1061 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -902 -335544831 -conf_access_denied -Access to <string> "<string>" is denied by server administrator. Доступ к <строка> "<строка>" отверг- нут администратором сервера -904 -335544324 -bad_db_handle -Invalid database handle (no active connection) . Неверный дескриптор базы данных (не активных соединений) -904 -335544375 -unavailable -Unavailable database. Недоступная база данных -904 -335544381 -imp_exc -Implementation limit exceeded. Исчерпан лимит выполнения -904 -335544386 -nopoolids -Too many requests. Слишком много запросов -904 -335544389 -bufexh -Buffer exhausted. Исчерпан буфер -904 -335544391 -bufinuse -Buffer in use. Буфер используется -904 -335544393 -reqinuse -Request in use. Запрос используется -904 -335544424 -no_lock_mgr -No lock manager available. Нет доступного менеджера блокировок -904 -335544430 -virmemexh -Unable to allocate memory from operating system. Невозможно выделить память в опе- рационной системе -904 -335544451 -update_conflict -Update conflicts with concurrent update. Изменение конфликтует с текущим изменением -904 -335544453 -obj_in_use -Object <string> is in use. Объект <строка> используется -904 -335544455 -shadow_accessed -Cannot attach active shadow file. Невозможно соединиться с активным файлом теневой копии -904 -335544460 -shadow_missing -A file in manual shadow <number> is unavailable. Файл в ручной теневой копии <число> недоступен
1062 Приложение 10 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -904 -335544661 -index_root_page_full -Cannot add index, index root page is full. Невозможно добавить индекс, корне- вая страница индексов заполнена -904 -335544676 -sort_mem_err -Sort error: not enough memory. Ошибка сортировки: недостаточно памяти -904 -335544683 -req_depth_exceeded -Request depth exceeded. (Recursive definition?) Превышена глубина запроса (рекур- сивное определение?) -904 -335544758 -sort_rec_si ze_e rr -Sort record size of <mimber> bytes is too big. Размер записи сортировки в <чис- ло> байт слишком велик -904 -335544761 -too_jnany_handles -Too many open handles to database. Слишком много открытых дескрипто- ров базы данных -904 -335544792 -service_att_err -Cannot attach to services manager. Невозможно подключиться к менедже- ру сервисов -904 -335544799 -svc_name_missing -The service name was not specified. He указано имя сервиса -904 -335544813 -optimizer_between_err -Unsupported field type specified in BETWEEN predicate. Указан неподдерживаемый тип поля в предикате between -904 -335544827 -exec_sql_invalid_arg -Invalid argument in EXECUTE STATEMENT- cannot convert to string. Неверный аргумент в execute statement — невозможно конвертиро- вать в строку -904 -335544828 -exec_sql_invalid_req -Wrong request type in EXECUTE STATEMENT '<stzing>’. Ошибочный тип запроса в execute STATEMENT '<строка>'
Коды ошибок 1063 Таблица П10.1 (продолжение) SQLCODE GDSCODE Символ Текст сообщения -904 -335544829 -exec_sql_invalid_var -Variable type (position <number>1 in EXECUTE STATEMENT '<string>' INTO does not match returned column type. Тип переменной (позиция <число>) В EXECUTE STATEMENT '<строка>' into не соответствует возвращаемо- му типу столбца. -904 -335544830 -exec__sql__max__call__exceed ed -Too many recursion levels of EXECUTE STATEMENT. Слишком много уровней рекурсии В EXECUTE STATEMENT -906 -335544744 -max_att_exceeded -Maximum user count exceeded. Contact your database administrator. Превышен максимум счетчика пользо- вателей -909 -335544667 -drdb_completed_with_errs -Drop database completed with errors. Удаление базы данных завершилось с ошибками -911 -335544459 -rec_in__l imbo -Record from transaction <numbet> is stuck in limbo. Запись транзакции <число> становится зависшей -913 -335544336 -deadlock -Deadlock. Взаимная блокировка -922 -335544323 -bad_db_format -File <string> is not a valid database. Файл <строка> не является допусти- мой базой данных -923 -335544421 -connect_reject -Connection rejected by remote interface. Соединение отменено удаленным интерфейсом -923 -335544461 -cant_validate -Secondary server attachments cannot validate databases. Вторичные подключения к серверу не могут проверять базы данных -923 -335544464 -cant__start__logging -Secondary server attachments cannot start logging. Вторичные подключения к серверу не могут запускать соединения
1064 Приложение 10 Таблица П10.1 (окончание) SQLCODE GDSCODE Символ Текст сообщения -924 -335544325 -bad_dpb_content -Bad parameters on attach or create database. Неверные параметры при подключе- нии или создании базы данных -924 -335544441 -bad_detach -Database detach completed with errors. Отключение от базы данных заверши- лось с ошибками -924 -335544648 -conn_lost -Connection lost to pipe server. Потеря соединения с каналом сервера -926 -335544447 -no_rollback -No rollback performed. Не выполнен откат транзакции -999 -335544689 -ib_error -Firebird error. Ошибка Firebird
ПРИЛОЖЕНИЕ 1 1 Зарезервированные слова В табл. Ш1.1 содержатся ключевые слова, которые зарезервированы в Firebird. Неко- торые имеют специальные отметки: ♦ keyword (con.) отмечает слова, которые зарезервированы в их специфическом контексте. Например, слово updating является ключевым словом в PSQL и будет недоступно в качестве имени переменной или аргумента; ♦ [keyword] отмечает слова, которые в настоящий момент не являются зарезервиро- ванными, но предполагается сделать их таковыми в будущей реализации или для совместимости с InterBase; ♦ /* keyword */ отмечает слова, которые были зарезервированными в Firebird 1.0.x, но были освобождены в Firebird 1.5. Таблица П11.1. Зарезервированные слова Firebird [ABS] ACTION ACTIVE ADD ADMIN AFTER ALL ALTER AND ANY ARE AS ASC ASCENDING AT AUTO AUTODDL AVG BASED BASENAME BASE-NAME BEFORE BEGIN BETWEEN BIGINT BLOB BLOBEDIT [BOOLEAN] [BOTH] /* BREAK */ BUFFER BY CACHE CASCADE CASE CAST CHAR CHARACTER [CHAR_LENGTH] [CHARACTER—LENGTH] CHECK CHECK_POINT_LEN CHECK_POINT_LENGTH CLOSE COALESCE (con.) COLLATE COLLATION COLUMN
1066 Приложение 11 Таблица П11.1 (продолжение) [ABS] ACTION ACTIVE COMMIT COMMITTED COMPILETIME COMPUTED CONDITIONAL CONNECT CONSTRAINT CONTAINING CONTINUE COUNT CREATE CSTRING CURRENT CURRENTJCONNECTION CURRENT_DATE CURRENT_ROLE CURRENT_TIME CURRENT_TIMESTAMP CURRENT_TRANSACTION CURRENT_USER DATABASE DATE DAY DB_KEY DEBUG DEC DECIMAL DECLARE DEFAULT [DEFERRED] DELETE DELETING (con.) DESC DESCENDING DESCRIBE /* DESCRIPTOR */ DISCONNECT DISPLAY DISTINCT DO DOMAIN DOUBLE DROP ECHO EDIT ELSE END ENTRY^POINT ESCAPE EVENT EXCEPTION EXECUTE EXISTS EXIT EXTERN EXTERNAL EXTRACT [FALSE] FETCH FILE FILTER /* FIRST */ FLOAT FOR FOREIGN FOUND FREE_IT FROM FULL FUNCTION GDSCODE GENERATOR GEN_ID [GLOBAL] GOTO GRANT GROUP GROU PJCOMMIT_WAIT GROUP_COMMIT_WAIT_TIME HAVING HEADING HELP HOUR IF /* IIF */ IMMEDIATE IN INACTIVE INDEX INDICATOR INIT INNER INPUT INPUT_TYPE INSERT INSERTING (con.)
Зарезервированные слова 1067 Таблица П11.1 (продолжение) [ABS] ACTION ACTIVE INT INTEGER INTO IS ISOLATION ISQL JOIN KEY LAST (con.) LC_MESSAGES LC_TYPE [LEADING] LEAVE (con.) LEFT LENGTH LEV LEVEL LIKE LOCK (con.) LOGFILE LOG-BUFFER-SIZE LOG_BUF_SIZE LONG MANUAL MAX MAXIMUM MAXIMUM_SEGMENT MAX~SEGMENT MERGE MESSAGE MIN MINIMUM MINUTE MODULE_NAME MONTH NAMES NATIONAL NATURAL NCHAR NO NOAUTO NOT NULL NULLIF (con.) NULLS (con.) NUM_LOG_BUFS NUM-LOG-BUFFERS NUMERIC [OCTET_LENGTH] OF ON ONLY OPEN OPTION OR ORDER OUTER OUTPUT OUTPUT_TYPE OVERFLOW PAGE PAGELENGTH PAGES PAGERSIZE PARAMETER PASSWORD [PERCENT] PLAN POSITION POST_EVENT PRECISION PREPARE [PRESERVE] PRIMARY PRIVILEGES PROCEDURE PUBLIC QUIT RAW_PARTITIONS RDB$DB_KEY READ REAL RECORD-VERSION RECREATE REFERENCES RELEASE RESERV RESERVING RESTRICT RETAIN RETURN RETURNINGJ7ALUES RETURNS REVOKE RIGHT ROLE
1068 Приложение 11 Таблица П11.1 (окончание) [ABS] ACTION ACTIVE ROLLBACK ROW_COUNT [ROWS] RUNTIME SAVEPOINT SCHEMA SECOND SELECT SET SHADOW SHARED SHELL SHOW SINGULAR SIZE /* SKIP */ SMALLINT SNAPSHOT SOME SORT SQL SQLCODE SQLERROR SQLWARNING STABILITY STARTING STARTS STATEMENT (con.) STATIC STATISTICS SUB_TYPE /* SUBSTRING */ SUM SUSPEND TABLE [TEMPORARY] TERM TERMINATOR THEN [TIES] TIME TIMESTAMP TO [TRAILING] TRANSACTION TRANSLATE TRANSLATION TRIGGER [TRIM] [TRUE] TYPE UNCOMMITTED UNION UNIQUE [UNKNOWN] UPDATE UPDATING (con.) UPPER USER USING (con.) VALUE VALUES VARCHAR VARIABLE VARYING VERSION VIEW WAIT WEEKDAY WHEN WHENEVER WHERE WHILE WITH WORK WRITE YEAR YEARDAY
ПРИЛОЖЕНИЕ 1 2 Литература и источники Firebird содержит огромное количество помощников, испытателей, документации, программного обеспечения, новостей и других ресурсов для помощи вам как разра- ботчику или администратору Firebird. Здесь представлен список некоторых из наибо- лее известных ресурсов, однако он не является исчерпывающим — новые ресурсы появляются каждый день! Рекомендуемая литература "API Guide" (APIGuide.pdf) и "Embedded SQL Guide" (EmbedSQL.pdf) для InterBase 6 и 7, опубликованные Borland. Эти руководства доступны в печатном формате в ком- плекте из магазина Borland на http://www.borland.com1. Бета-версии могут быть за- гружены с множества сайтов — найдите в Google документы в формате PDF1 2. Они также доступны со страницы Downloads InterBase на http://www.ibphoenix.com. "Data Modeling Essentials: Analysis, Design and Innovation, 2nd Edition" (Coriolis Group, 2000) автор Graeme Simsion. Пересмотрено и изменено Graham Witt и Graeme Simsion. Это книга для самого начального обучения анализу данных, отношениям, нормализации. Она является прекрасным способом решения проблем проектирова- ния. Это издание включает разделы по UML и объектно-ориентированному подходу, шаблонам и демонстрационную главу по моделированию данных для организации информационных хранилищ. Приложение включает сквозной пример по представле- нию большой модели данных. "Joe Celko’s SQL Puzzles and Answers" (Morgan Kaufmann, 1997), автор Joe Celko. Эта книга представляет практический подход к решению синтаксических проблем. "The Essence of SQL: Guide to Learning Most of SQL in the Least Amount of Time" (Coriolis Group, 1997), автор David Rozenshtein, редактор Tom Bondur. Эта очень сжа- тая книга для новичков убирает мистику языка SQL. 1 Полный комплект документации по InterBase 7.5 (на английском языке) поставляется только в электронном виде в составе серверной лицензии. Эти книги можно приобрести в пе- чатном виде только поштучно ($25 за книгу). На русском языке существует только печатная документация по InterBase 5.6, поставляемая в комплекте IB 5.6 MediaK.it Rus. — Прим. науч, ред. 2 Полный комплект документации по InterBase 6.x, включая ReleaseNotes по всем версиям InterBase и Firebird, можно найти Hawww.ibase.ru. —Прим. науч. ред.
1070 Приложение 12 "The Practical SQL Handbook: Using Structured Query Language, 3rd Edition" (Addison- Wesley Professional, 1996), авторы Judith S. Bowman и др. Это хорошо аннотирован- ный настольный справочник по стандартному SQL. "A Guide to the SQL Standard, 4th Edition" (Addison-Wesley Professional, 1997), авторы C.J. Date и Hugh Darwen. Все, что вы хотели знать — и многое из того, о чем вы не догадываетесь, что вы этого не знаете, — об SQL-92 в реляционных СУБД. "Understanding the New SQL: A Complete Guide" (Morgan Kaufmann, 1993), авторы Jim Melton и Alan Simon. В книге рассматриваются SQL-89 и SQL-92, она является все- объемлющим справочником для начинающих. Примеры последовательные, хотя час- то непрактичные. Книга содержит некоторые основы теории моделирования. "Mastering SQL" (Sybex, 2000), автор Martin Gruber. Это измененная и расширенная версия "Understanding SQL" делает стандарт SQL доступным даже новичкам и помо- гает получить серьезные навыки быстрой разработки3. Список Web-сайтов Сайты проекта Firebird http://sourceforge.net/projects/firebird является сайтом разработчиков, где вы може- те получить доступ к дереву CVS, к исходным и двоичным кодам комплекта постав- ки и просмотреть список выявленных ошибок. http://www.firebirdsql.org, алиас http://firebird.sourceforge.net. Здесь вы можете найти информацию и новости проекта. Через этот сайт можно получить доступ ко всем двоичным кодам Firebird, к страницам "как сделать", FAQ (часто задаваемым вопросам), форумам и конференциям. http://www.firebirdsql.org/index.php?op=ffoundation предоставляет информацию о FirebirdSQL Foundation Inc., некоммерческой группе, которая создала фонд для гран- тов проекта. Web-сайты ресурсов http://www.ibase.ru содержит огромное количество информации по InterBase и Fire- bird на русском языке: статьи, FAQ, файлы, форум, списки рассылки новостей и др. http://www.ibphoenix.com является центром информации и новостей для пользовате- лей, разрабатывающих приложения для Firebird или InterBase. Он имеет базу знаний, множество авторитетных статей, контакты с коммерческой поддержкой и консалтин- гом, ссылки на инструменты и проекты, сервисы подписки на компакт-диски. http://www.cvalde.net является сайтом гуру Claudio Vaiderrama, "Неофициальным сайтом InterBase", ссылающимся на сайт InterBase и связанный с многими другими 3 На русском языке— "SQL", М. Грабер, издательство "Лори", дата выхода: 2001/2003, ISBN 5-85582-109-9. — Прим. науч. ред.
Литература и источники 1071 сайтами, где люди делают интересные вещи с Firebird и инструментами разработки. Этот сайт содержит эклектическое собрание статей, различное программное обеспе- чение, ссылки на новости и многое другое. http://www.volny.cz/ipbenosilis— сайт Ivan Prenosil содержит интересные статьи Ивана и инструменты, написанные и поддерживаемые гуру Firebird. http://www.interbase-world.com полон новостей, интервью и практических статей на русском и английском языках о Firebird и InterBase. Он содержит ссылки на множе- ство других сайтов сообществ Firebird на русском языке и многих иных сайтов. http://www.ibphoenix.cz— сайт ресурсов для чешских пользователей Firebird и In- terBase, поддерживается Pavel Cisar, автором первой чешской книги по Firebird и In- terBase. Сайт содержит ссылки, загрузку и консультацию. http://firebird-fr.eu.orgis поддерживается Philippe Makowski во Франции. Включает ресурсы, статьи и новости. http://www.comunidade-firebird.org— международный сайт (на португальском язы- ке), поддерживаемый разработчиками и членами Communidade Firebird Lingua Portu- guesa. Он содержит загрузку, ссылки, сервисы, статьи и работы. http://www.firebase.com.br/fb является бразильским сайтом на португальском языке, поддерживается Carlos Cantu. Этот сайт содержит новости, статьи, обучающие про- граммы и загрузку программного обеспечения. http://tech.firebird.gr.jp является домашней страницей Ассоциации пользователей Firebird Японии. Этот сайт предоставляет новости, статьи, ссылки и загрузку. http://www.fingerbird.de— общедоступный сайт, содержащий ссылки на сайты Fire- bird, статьи, загрузку, ресурсы и многое другое. Форумы Firebird Техническая поддержка: firebird-support@yahoogroups.com. Это основной форум поддержки разработчиков баз данных и приложений. В нем можно задавать вопросы по поводу SQL, инсталляции, конфигурирования, проектирования и маленьких хит- ростей. В нем можно перейти к вопросам относительно драйверов и сред разработки приложений — для этого существуют отдельные форумы. Также есть ветка для со- общений о предполагаемых ошибках для предварительного обсуждения. Присоеди- няйтесь к http://groups.yahoo.com/community/firebird-support. Центральная лаборатория разработчиков: firebird-devel@lists.sourceforge.net. Это список для разработчиков и тестеров. Обычно участники бывают рады помочь в соз- дании вашего исходного кода, однако вам следует провести поиск, чтобы найти "ос- новной" для вас список. Поддержка вопросов и "шумов" там производится вне тем. Приветствуется обсуждение ошибок, вам предоставляется прекрасное описание оши- бок с указанием подробностей о версии Firebird, операционной системе, оборудова- нии и предоставляется система отслеживания ошибок (Bug Tracker) на сайте проекта, если ваша ошибка еще неизвестна. Присоединяйтесь к https://lists.sourceforge.net /lists/listinfo/firebird-devel.
1072 Приложение 12 Лаборатория контроля качества тестеров: firebird-test@Iists.sourceforge.net. Это форум для тестеров. Тестеры и проектировщики тестов приглашаются для присоеди- нения к этой группе. Это не повод для обсуждения каких-либо вопросов. Присоеди- няйтесь к https://lists.sourceforge.net/lists/ listinfo/firebird- test. Java: firebird-java@yahoogroups.com. Это форум разработчиков и поддержка поль- зователей драйверов JDBC/JCA (см. приложение 3). Там также есть ветка для сооб- щений о предполагаемых ошибках в драйверах Java. Присоединяйтесь к этой группе http://groups.yahoo.com/community/firebird-java. ПРИМЕЧАНИЕ. Продукты Borland InterClient и InterServer не поддерживаются и не разрабатываются для и в Firebird вовсе. Проект драйвера Firebird ODBC-JDBC: firebird-odbc-devel@lists.sourceforge.net. Это форум разработчиков, тестеров и пользователей драйвера Firebird ODBC-JDBC (см. приложение 3). Обратите внимание, что драйверы ODBC сторонних разработчи- ков не поддерживаются в этом списке — подробности см. на Web-сайтах владельцев. Присоединяйтесь к списку Firebird ODBC на https://lists.sourceforge.net /lists/listinfo/firebird-odbc-devel. .NET Provider: firebird-net-provider@lists.sourceforge.net. Это форум разработчиков и пользователей проекта интерфейса Firebird .NET Provider (см. приложение 3). При- соединяйтесь к форуму на https://lists.sourceforge.net/lists/listinfo/firebird-net- provider. PHP: firebird-PHP@yahoogroups.com. Это форум поддержки разработчиков, соз- дающих приложения Firebird в РНР (см. приложение 3). Присоединяйтесь: http://groups.yahoo.com/community/firebird-php. Visual Basic: firebird-vb@yahoogroups.com. Это форум поддержки разработчиков, создающих клиентские приложения на Visual Basic. Присоединяйтесь: http:// groups.yahoo.com/community/firebird-vb. Конвертирование из других СУБД: ib-conversions@yahoogroups.com. Это место, где вы получите советы и рекомендации по конвертированию баз данных в Firebird. Об- щие вопросы не приветствуются — обратитесь к firebird-support. Присоединяйтесь к http://groups.yahoo.com/community/ib-conversions. Архитектура и проектирование ядра СУБД: firebird-architect@yahoogroups.com. Здесь обычно околачиваются гуру по разработке ядра, обсуждаются вопросы и планы по улучшению ядра Firebird и его интерфейсов. Приглашаются участники и наблюда- тели, однако не разрешены никакие вопросы по поддержке. Обсуждения обычно весьма изысканные. Присоединяйтесь на http://groups.yahoo.com/community /firebird-architect. Инструменты: firebird-tools@yahoogroups.com. Это форум разработчиков инстру- ментов и плагинов для Firebird. Здесь регулярно появляются объявления, а разработ- чики инструментов обычно доступны, чтобы направить вас в правильную ветку для
Литература и источники 1073 получения помощи. Присоединяйтесь на http://groups.yahoo.com/community /firebird-tools. Относительно выбора доступных инструментов см. приложение 5. Общие обсуждения сообщества: firebird-general@yahoogroups.com. Недопустимы никакие вопросы поддержки, а только вещи, тем или иным образом связанные с Fire- bird. Диапазон тем простирается от логотипов до мягких игрушек, обсуждений рас- пространения, интересных ссылок на онлайновую прессу Firebird. Документация: firebird-docs@lists.sourceforge.net. Это также не для сторонних наблюдателей — форум для людей, работающих с документацией, или для тех, кто хочет начать работать. Вопросы поддержки также не приветствуются. Если у вас есть желание включиться в этот проект XML, присоединяйтесь на https://lists.sourceforge.net/lists/listinfo/firebird-docs. Web-сайт: firebird-Website@lists.sourceforge.net. Здесь нет поддержки ваших проек- тов приложений для Web. Это еще одно место не для сторонних наблюдателей — форум для людей, работающих с Web-сайтом Firebird, или для тех, кто хочет оказать помощь. Интерфейс группы новостей: все эти списки являются зеркальными для сервера но- востей на news://news.atkin.com. Любой может читать трафик списка через этот ин- терфейс, но вы должны подписаться на список сообщества, чтобы посылать сообще- ния. Как стать разработчиком Firebird У проекта Firebird постоянно открыты двери для хороших программистов C++, кто хочет сделать вклад в проектирование и разработку. Люди становятся участниками не с позволения, а по делам. Первое, что нужно сделать, — получить от SourceForge "метку", а затем присоединиться к спискам firebird-devel и firebird-architect (под- робности см. в предыдущем разделе). Найдите существующий или новый проект, который вас интересует, обсудите его и представьте код. Проект постоянно заинтересован в предложениях серьезных проектировщиков и тес- теров. FirebirdSQL Foundation имеет фонд, из которого могут быть выделены средства, что- бы помочь разработчикам внедрить важную функциональность в Firebird. 35 Зак. 420
Глоссарий Термин Определение fdb или FDB По соглашению это расширение используется для первичного файла базы данных Firebird. Это не более чем соглашение: Firebird работает с любыми расширениями файлов или вовсе без них gdb или GDB По соглашению зто расширение файла традиционно использует- ся для баз данных InterBase. Однако файл базы данных Firebird может иметь любое расширение или вовсе не иметь его. Многие разработчики Firebird вместо него используют fdb, чтобы отличить базы данных Firebird от баз данных InterBase или как часть реше- ния проблемы с "обеспечением безопасности", введенным корпо- рацией Microsoft в ее операционных системах Windows ME и ХР, используемых для файлов, имеющих расширение gdb. Почему "GDB"? Это продукт с именем компании, которая создала первоначальную версию InterBase — Groton Database Systems ADO Аббревиатура от Active Data Objects — интерфейс высокого уров- ня приложение-данные, введенный Microsoft в 1996 году. Более ранние версии могли получать доступ только к реляционным ба- зам данных с фиксированными столбцами и типами данных, од- нако более поздние версии могут соединяться и с другими моде- лями СУБД, файловыми системами, файлами данных, сообще- ниями электронной почты, иерархическими структурами и гетерогенными структурами данных aggregate (function) агрегат (функция) Функция, которая возвращает результат, полученный в виде обобщения (агрегирования) значений из набора строк, которые сгруппированы некоторым образом с использованием синтаксиса оператора SQL. Например, внутренняя функция SUM О оперирует с непустыми числовыми столбцами и возвращает результат в виде суммы значений всех столбцов для строк, выделенных в предложениях where и order by. Вывод, являющийся объединением предложе- ния where, возвращает одну выходную строку, в то время как объ- единение в предложении group by потенциально возвращает множество строк alerter (events) обработчик событий Термин, придуманный для названия клиентской подпрограммы или класса, которые способны "прослушивать" заданные в базе данных события (event), сгенерированные в триггере или храни- мой процедуре, выполняемых на сервере ALICE Внутреннее имя для кода утилиты gfix — искажение слов "all else" (все остальное)
Гпоссарий 1075 alternative key (alternate key) альтернативный ключ Термин, используемый для уникального ключа, который не явля- ется первичным ключом. Уникальный ключ создается при приме- нении ограничения unique к столбцу или группе столбцов. Внеш- ний ключ в форме отношения ссылочной целостности может ука- зывать в его предложении references на альтернативный ключ API Аббревиатура для Application Programming Interface (Интерфейс прикладного программирования). API предоставляет множество формальных структур, через которые приложения могут связы- ваться с функциями другого программного обеспечения. API Firebird предоставляет подобный интерфейс к клиентской библио- теке, скомпилированной специально для каждой поддерживаемой платформы. Структуры в API Firebird являются структурами языка С, они созданы, чтобы быть переносимыми на любой язык про- граммирования. Трансляция может выполняться для Java, Pascal, Perl различных уровней, PHP 4/5, Python и др. argument Переменная заранее описанного типа и размера, которая переда- аргумент ется функции или хранимой процедуре для выполнения с ней действий. Хранимая процедура может быть разработана для ис- пользования как входных аргументов, так и возвращаемых выход- ных аргументов. Для возвращаемых значений функций (как внут- ренних, так и определенных пользователем) термин результат используется чаще, чем аргумент. Термины параметр и аргумент часто используются как взаимо- заменяемые в отношении хранимых процедур благодаря приспо- соблению фирмой Borland термина параметр в классах доступа к данным Delphi для именования свойств, которые назначает аргу- ментам хранимая процедура array slice Непрерывный диапазон элементов массива Firebird называется срез массива срезом массива. Срез массива может состоять из любого количества смежных блоков данных из массива, от одного элемента размерности до максимального количества элементов всех определенных размерностей atomicity В контексте транзакции атомарность ссылается на вид механизма атомарность транзакции, который является "упаковкой" для группы изменений строк в одной или более таблицах для получения одной единицы работы, которая будет либо полностью подтверждена, либо пол- ностью отменена. В контексте ключа, ключ является атомарным, если его значение не имеет связи с прикладными данными AutoCommit Когда изменение отправляется в базу данных, оно не станет по- стоянным, пока не будет подтверждена транзакция в клиентском приложении, в рамках которой отправлялись изменения. Если же клиент выполняет откат транзакции, а не ее подтверждение, то отправленные изменения будут отменены. Некоторые клиентские инструменты, драйверы или библиотеки компонентов предоставляют механизм, при котором отправка любых изменений любой таблицы следом вызывает подтвержде- ние транзакции без каких-либо усилий со стороны пользователя. Этот механизм обычно называют AutoCommit или похожим терми- ном. Он не является механизмом Firebird — Firebird никогда не подтверждает транзакции, стартованные в клиенте
1076 Гпоссарий backup/restore (Firebird style) копирование/ восстановление (в стиле Firebird) Копирование (backup) является внешним процессом, иницииро- ванным пользователем— обычно SYSDBA— для помещения базы данных в набор сжатых дисковых структур, включающих метаданные и данные, которые разделяются при хранении. Вос- становление (restore) является другим внешним процессом — также инициируемым пользователем, — который полностью ре- конструирует исходную базу данных из ее сохраняемых элемен- тов. Процесс копирования также может выполнять сборку мусора в базе данных в процессе ее чтения; восстановленная база дан- ных полностью свободна от "мусора". См. также gbak BDE Аббревиатура для Borland Database Engine (Движок базы данных Borland). Первоначально создан как ядро базы данных Paradox, он был расширен для того, чтобы обеспечить промежуточный уро- вень доступа между различными реляционными базами данных и инструментами приложений Borland для платформ Microsoft DOS и Windows. Правила, заданные производителем, применимые к каждой реляционной СУБД, инкапсулированы в наборе драйве- ров, называемых SQLLinks. Драйверы SQLLinks имеют особенно- сти, изменяемые от версии к версии. С 2000 года, когда Borland создал код базы данных, на котором был разработан Firebird 1.0, BDE был признан устаревшим в пользу более современных технологий драйверов. Последняя известная версия BDE (5.2) поставляется вместе с Borland Delphi 6 и выше. Драйвер InterBase в этой поставке только час- тично поддерживает Firebird binary tree двоичное дерево Логическая структура дерева, в которой узлы могут содержать максимум две ветви. Индексы Firebird созданы на базе структур b-tree, которые в отличие от двоичных деревьев на уровне ветви BLOB могут содержать много элементов Акроним для Binary Large Object (большой двоичный объект). Это элемент данных неограниченного размера в любом формате, который можно переслать в потоке в базу данных байт за байтом и сохранить без каких-либо изменений формы. Firebird допускает BLOB различных типов, классифицированных на основании под- типов. Прародитель Firebird, InterBase был первой реляционной базой данных, поддерживающей BLOB. См. также CLOB BLOB control structure Структура языка С, объявленная в модуле UDF в виде typedef, управляющая структура BLOB с помощью которой UDF BLOB получает доступ к BLOB. UDF BLOB не может ссылаться на фактические данные BLOB, а ис- пользует вместо этого указатель на управляющую структуру BLOB BLOB filter фильтр BLOB Специализированная UDF, которая преобразует данные BLOB из одного подтипа в другой. Firebird включает множество внутренних фильтров BLOB, которые он использует в процессе сохранения и поиска метаданных. Один из внутренних фильтров конвертирует текстовые данные между подтипом 0 (никакой) и подтипом 1 (текст, иногда называемый "Мето") BLR Аббревиатура Binary Language Representation (двоичное пред- ставление языка), внутреннего реляционного языка с двоичными нотациями, который является надмножеством "читаемых челове- ком" языков и может быть использован в Firebird, конкретно в SQL и GDML. Интерфейс DSQL в Firebird для сервера транслирует запросы в BLR. Версии BLR скомпилированных триггеров, храни-
Гпоссарий 1077 мых процедур, ограничений CHECK, значений по умолчанию и просмотров хранятся в полях BLOB. Некоторые клиентские инст- рументы — например, IB_SQL и инструмент командной строки isql — имеют средства просмотра этих кодов BLR. В isql выполни- те команду set blob all, а затем выполните операторы select для получения соответствующих полей из системных таблиц buffer буфер Блок памяти для хранения копий страниц, прочитанных из базы данных. Термин "буфер" является синонимом термина "странич- ный кэш" BURP Внутреннее имя для кода gbak — акроним для Backup [and] Restore Program (программа копирования и восстановления) cache Когда страница читается с диска, она копируется в блок памяти, КЭШ который имеет название кэш базы данных или просто кэш. Кэш состоит из блоков памяти, каждый размером в страницу базы данных, определяемый параметром page_size, объявляемым при создании базы данных. Размер кэша можно настроить, задав количество страниц. Следо- вательно, для вычисления размера кэша умножьте page^size на количество страниц кзша cardinality (of a set) мощность (набора) Количество строк в физическом или заданном наборе. Карди- нальность строки указывает ее положение в наборе строк case-insensitive index индекс, не чувствительный к регистру Индекс, используемый в сортировке, когда буквы в нижнем реги- стре трактуются так, как если бы они были в верхнем регистре. Firebird 1.0 не поддерживает нечувствительные к регистру индек- сы. Небольшое количество нечувствительных к регистру порядков сортировки появилось в Firebird 1.5 cascading integrity constraints Firebird предоставляет возможность задать особые виды поведе- ния и ограничения в ответ на запросы на изменение или удаление каскадные ограничения целостности строк в таблицах, на которые есть ссылки в предложении references ограничения foreign key. Ключевое слово cascade приводит к тому, что изменения, выполненные для "родительской" строки, будут распространяться на строки в таблицах, имеющих зависимости foreign key. Например, on delete cascade приве- дет к удалению всех зависимых строк при удалении родительской строки casting преобразование Механизм для конвертирования выходных значений или значений переменных из одного типа данных в другой в выражениях. SQL Firebird предоставляет функцию casto для использования как в выражениях динамического SQL (DSQL), так и процедурного SQL (PSQL) character set В основном два надмножества печатаемых образов символов и управляющих последовательностей на сегодняшний день исполь- зуется в программных окружениях: ASCII и UNICODE. Символы ASCII, представленные в одном байте, имеют 256 вариантов, в то время как символы UNICODE, представляемые 2, 3 и 4 байтами, могут предоставить десятки тысяч возможностей. Поскольку для баз данных требуется исключить непомерно высокие накладные расходы, чтобы сделать доступными все возможные печатаемые и управляющие символы, используемые при программировании в любой точке мира, это надмножество разделено на кодовые
1078 Гпоссарий страницы, также называемые кодовыми таблицами. Каждая кодовая страница определяет подмножество требуемых символов для конкретного языка или семейства языков, представляя образ каждого символа в виде числа. Эти образы и управляющие по- следовательности в каждой кодовой странице называются набо- рами символов. Образ символа может отображаться в различные числа в различных наборах символов. Firebird поддерживает для базы данных набор символов по умол- чанию и явное определение набора символов для каждого столб- ца, имеющего тип данных character, varying character и blob sub_type 1 (текстовый blob). Если для базы данных не опреде- лен никакой набор символов, ее набор символов по умолчанию будет none, в результате чего все символьные данные будут со- храняться точно так, как они представлены без попыток конверти- рования символов (выполнения транслитерации) для любого кон- кретного набора символов Classic architecture Классическая архитектура Начальная модель InterBase, когда для каждого клиентского со- единения стартует отдельный серверный процесс. Эта архитекту- ра предшествовала модели Суперсервера, при которой для кли- ентов создаются потоки в рамках единого серверного процесса. Варианты обеих моделей архитектуры доступны для множества платформ операционной системы CLOB Акроним для Character Large OBject (большой символьный объ- ект). Этот термин появился из более раннего использования, ко- гда другие СУБД копировали поддерживаемое в Firebird хранение больших объектов в базе данных. CLOB является эквивалентом blob sob_type 1 (text). См. также BLOB coercing data types приведение типов данных В структурах XSQLDA API Firebird преобразование элемента дан- ных из одного типа SQL в другой называется приведением типов данных collation order Определяет, как операция сортировки упорядочивает символьные порядок сортировки столбцы в выходных наборах, задавая пары символов в нижнем и верхнем регистрах для функции upper!) , а также как символы в символьных столбцах сравниваются при поиске. Порядок сорти- ровки применим для конкретного набора символов. Если для на- бора символов доступно множество порядков сортировки, то один из порядков сортировки будет трактоваться как порядок по умол- чанию. По соглашению порядок сортировки по умолчанию имеет то же имя, что и набор символов column столбец В базах данных SQL данные хранятся в структурах, которые могут быть выбраны в виде таблиц, или более корректно — наборов. Набор состоит из одной или более строк, каждая из которых иден- тична в горизонтальном порядке для элементов данных, имеющих разные типы. Один отдельный элемент данных, рассматриваемый вертикально по всей длине набора, называется столбцом. Раз- работчики приложений часто называют столбцы полями (когда речь идет об одной записи или о структуре таблицы) commit Когда приложения посылают изменения, действующие на строки в подтверждение (транзакции) таблицах базы данных, то создаются новые версии таких строк во временных блоках хранения. Хотя работа видна в той транзакции, в которой она была выполнена, она не видима другим пользова- телям базы данных. Клиентская программа должна сообщить
Гпоссарий 1079 серверу о подтверждении (commit) работы, чтобы сделать эти изменения постоянными. Если транзакция не подтверждается, для нее должен быть выполнен откат (rollback), чтобы отменить эту работу CommitRetaining Установка для транзакции, которая реализует атрибут транзакции commit with retain (подтверждение с сохранением контекста). Также называется мягким подтверждением. При этом атрибуте контекст транзакции сохраняется активным на сервере, пока кли- ентское приложение окончательно не вызовет COMMIT (жесткое подтверждение) и не позволит процессу управления инвентар- ными страницами транзакций передать старые версии сборке мусора. Широкое использование в приложениях CommitRetaining является общей причиной ухудшения производительности. См. также Oldest Interesting Transaction (OIT) concurrency параллельность, одновременность, конкурентность Термин используется для названия ситуации, когда множество пользователей одновременно имеют доступ к одним и тем же данным. Этот термин также широко используется в документации и списках поддержки для ссылок на конкретный набор атрибутов, применимых к транзакции: уровень изоляции, стратегия блокиро- вок и др. Например, кто-то может вас спросить: "Какие у вас уста- новки конкуретности?". Еще более специфическим образом этот термин иногда используется как синоним уровня изоляции SNAPSHOT constraint Firebird предоставляет множество возможностей для определения ограничение формальных правил, применимых к данным. Такие формальные правила называются ограничениями. Например, primary key является ограничением, которое отмечает столбец или группу столбцов как общий в базе данных указатель для всех других столбцов в строке. Ограничение check устанавливает одно или более правил, ограничивающих те значения, которые может при- нимать столбец contention конфликт Когда две транзакции пытаются одновременно изменить в табли- це одну и ту же строку, то говорят о конфликте', а транзакции являются конфликтными correlated subquery коррелированный подзапрос Спецификация запроса может определять выходные столбцы, которые получаются из выражений. Подзапрос — это выражение специального вида, которое возвращает одно значение, являю- щееся результатом выполнения оператора select. В коррелиро- ванном подзапросе предложение where содержит один или более ключей поиска, которые связаны отношением со столбцами из главного запроса crash Жаргонный термин для ненормального завершения сервера или крах клиентского приложения crash recovery восстановление после краха Процессы или процедуры, которые реализуют восстановление сервера и/или клиентского приложения после аварийного завер- шения сервера или клиентского приложения (или обоих) в работо- способное состояние CVS Аббревиатура для Concurrent Versions System (система одновре- менных версий) — программы с открытыми кодами, которая по- зволяет разработчикам сохранять различные версии исходного кода разработки. CVS широко используется в проектах с откры- тыми кодами, включая проект Firebird
1080 Гпоссарий cyclic links циклические ссылки В контексте базы данных это зависимости между таблицами, ко- гда внешний ключ одной таблицы (ТаЫеА) ссылается на уникаль- ный ключ другой таблицы (ТаЫев), которая содержит внешний ключ, ссылающийся непосредственно или через другую таблицу на уникальный ключ таблицы ТаЫеА database база данных В самом широком смысле термин "база данных" применяется к любой постоянной файловой структуре, которая сохраняет дан- ные в некотором формате, позволяющем их отыскивать и мани- пулировать ими в приложениях DB_KEY См. RDB$DB_KEY DDL Аббревиатура от Data Definition Language (язык определения дан- ных), подмножества SQL, которое используется для определения и управления структурами объектов данных. Любой оператор SQL, начинающийся с ключевого слова create, alter, recreate, create or replace или drop, является оператором DDL. В Firebird некоторые операторы DDL начинаются с ключевого слова declare, хотя не все операторы declare относятся к DDL deadlock взаимная блокировка Когда две транзакции конкурируют в изменении одной и той же версии строки, про них говорят, что они находятся в состоянии взаимной блокировки, то есть когда одна транзакция (Т1), имею- щая блок на строку А, запрашивает изменение строки В, которая заблокирована другой транзакцией (Т2), и транзакция Т2 собира- ется изменять строку А. Обычно подлинные взаимные блокировки случаются очень редко, поскольку сервер может определить большинство таких блокировок и самостоятельно разрешить их без выдачи исключения блокировки. К сожалению, сервер Firebird объединяет все сообщения о конфликтах блокировки в один код сообщения, которое говорит о "взаимной блокировке", независимо от фактического источника конфликта. Код клиентского приложе- ния должен разрешить конфликт блокировки, выполняя откат одной транзакции, чтобы дать возможность другой транзакции подтвердить ее работу degree (of a set) степень(набора) Количество столбцов в табличном наборе. Термин степень столбца (degree of a column) указывает на его положение в по- следовательности столбцов слева направо, начиная с 1 deployment поставка Процесс развертывания и инсталляции компонентов программно- го обеспечения для промышленного использования dialect Термин, который отличает родной для Firebird язык от старого диалект языка, который был реализован в предшественнике Firebird, InterBase 5. Старая версия языка остается доступной в Firebird с близкой совместимостью со старыми базами данных в виде диа- лекта 1. Родным для Firebird является диалект 3 DML Аббревиатура от Data Manipulation Language (язык манипулирова- ния данными), основного подмножества операторов SQL, которые выполняют операции над наборами данных domain Возможность SQL Firebird, благодаря которой вы можете при- домен сваивать конкретному имени множество характеристик данных и ограничений (create domain), а затем использовать это имя вме- сто типа данных при определении столбцов таблицы
Гпоссарий 1081 DPB Аббревиатура от Database Parameter Buffer (буфер параметров базы данных), символьного массива, определенного в API Firebird. Он используется приложениями для передачи параметров, опре- деляющих характеристики требуемого клиентского соединения вместе с конкретными значениями элементов DSQL Аббревиатура от Dynamic SQL (динамический SQL). DSQL опре- деляет операторы, которые приложение передает во время вы- полнения, с параметрами или без них, в противоположность опе- раторам "статического SQL", которые кодируются непосредственно в специальных блоках кода в программе на языке программиро- вания, а затем обрабатываются препроцессором (например, GPRE) при компиляции приложений со встраиваемым SQL. При- ложения, применяющие вызовы API Firebird в "сыром" виде или через библиотеку классов, которая инкапсулирует API Firebird, DTP также используют DSQL Аббревиатура от desktop publishing (настольная публикация), дея- тельности по использованию средств компьютера для подготовки документов к публикации на принтере или для Web DUDLEY Внутреннее имя исходного кода для устаревшей утилиты работы dyn или DYN с метаданными gdef. Имя, производное от аббревиатуры DDL Кодированный по байтам язык для описания операторов опреде- ления данных. Подсистема DSQL в Firebird выполняет синтакси- ческий анализ операторов DDL и передает их компоненту, кото- рый выводит DYN для интерпретации другой подсистемой, кото- рая ответственна за изменение системных таблиц error Условие, при котором запрашиваемая операция SQL не может ошибка быть выполнена по причине ошибочности данных, предоставлен- ных в операторе или в процедуре, или из-за ошибки синтаксиса самого оператора. Когда Firebird встречает ошибку, он не продол- жает выполнять запрос, и возвращает клиентскому приложению сообщение об исключении. См. также exception error code Целочисленная константа, возвращаемая клиенту или вызвавшей код ошибки процедуре, когда Firebird встречает ошибку. См. также error, exception ESQL Аббревиатура для Embedded SQL (встроенный SQL), подмноже- ства SQL, предназначенного для статичных операторов SQL, встроенных в специальные блоки в приложениях на каком-либо языке программирования event Реализованная в Firebird возможность передачи сообщений "слу- событие шающим" клиентским приложениям через вызовы post__event в триггерах или хранимых процедурах exception Реакция сервера Firebird в ответ на ошибочную ситуацию, которая исключение появилась в процессе выполнения операции с базой данных. Не- сколько сотен ситуаций, вызывающих исключение, реализовано в виде кодов ошибок различных категорий, которые передаются клиенту в векторе состояния ошибки (массив). Исключения дос- тупны также в хранимых процедурах и триггерах, где они могут быть обработаны в пользовательской подпрограмме. Firebird так- же поддерживает исключения, определенные пользователем
1082 Глоссарий external function внешняя функция I Firebird имеет несколько (немного) встроенных стандартных \ функций SQL. С целью расширения количества функций, доступ- \ ных для использования в выражениях, ядро Firebird может обра- \ щаться к пользовательским функциям, написанным на языке про- \ граммирования, таком как С, C++ или Delphi, как если бы они бы- i ли встроенными. Несколько готовых свободно распространяемых ; библиотек внешних функций (также называемых функциями, оп- I ределенными пользователем, User-Defined Function, UDF) суще- \ ствуют в сообществе Firebird. Две из них включены в дистрибутив \ Firebird executable stored procedure выполняемая хранимая процедура i Хранимая процедура, которая вызывается оператором execute j procedure и не возвращает многострочного результирующего \ набора. См. также selectable stored procedure execute \ В клиентском приложении термин выполнение обычно использу- выполнение j ется как глагол, означающий "выполнение моего запроса", когда I оператор манипулирования данными или вызов хранимой про- \ цедуры подготовлен клиентским приложением. I В DSQL фраза execute procedure используется вместе с иден- тификатором хранимой процедуры и ее входными параметрами i для вызова выполняемой хранимой процедуры FIBPIus \ Торговая марка расширенной коммерческой версии компонентов i FreelBComponents— компонентов доступа к данным, инкапсули- i рующим функции API Firebird и InterBase, для использования в i продуктах Borland Delphi, C++ Builder и Kylix foreign key внешний ключ I Формальное ограничение для столбца или группы столбцов в i одной таблице, которая связана с соответствующим первичным \ или внешним ключом другой таблицы. Если внешний ключ не i является уникальным, а сама таблица имеет первичный ключ, то i таблица способна поддерживать отношение один-ко-многим. ) Firebird поддерживает объявление формального ограничения \ внешнего ключа, которое будет автоматически поддерживать i ссылочную целостность. Если объявлено подобное ограничение, i Firebird автоматически создает неуникальный индекс для столбца \ или столбцов, для которых применяется это ограничение, а также \ сохраняет зависимости между таблицами, связанными этим огра- |ничением garbage collection сборка мусора \ Общий термин для процесса очистки базы данных, который вы- \ полняется в базе данных в процессе обычного ее использования i при удалении устаревших версий строк, которые были изменены. ; В Суперсервере сборка мусора выполняется как фоновый поток i главного серверного процесса. Сборка мусора может также вы- \ полняться при чистке (sweep) и во время создания резервной \ копии базы данных gbak \ Утилита командной строки (располагающаяся в каталоге /bin ва- \ шего каталога инсталляции Firebird), которая выполняет резерв- i ное копирование и восстановление базы данных. Она не является \ программой копирования файла; ее операция копирования вы- ; полняет упаковку метаданных и данных и сохраняет их раздельно i в сжатом двоичном формате в файловой системе. По соглашению \ файлы копий часто имеют расширение gbk или fbk. Восстановле- I ние выполняет распаковку этого файла и восстанавливает базу Жданных как новый файл базы данных до помещения в базу дан- \ ных объектов данных и пересоздания индексов.
Гпоссарий 1083 Помимо обычных задач обеспечения безопасности базы данных, ожидаемых от утилиты копирования, gbak выполняет важную роль в регулярном поддержании "гигиены базы данных" и в восстановлении разрушенных баз данных GDML Аббревиатура для Groton Data Manipulation Language (язык мани- пулирования данными Groton), реляционного языка, похожего на SQL. GDML был первоначальным языком манипулирования дан- ными для InterBase, функционально эквивалентным языку DML в SQL Firebird, но с некоторыми возможностями определения дан- ных. Он все еще поддерживается в интерактивной утилите запро- сов qli gdef Старая утилита InterBase для создания и манипулирования мета- данными. Поскольку isql и интерфейс динамического SQL могут обрабатывать DDL, в gdef теперь нет необходимости. Однако так как она может выводить операторы языка DYN для некоторых языков программирования, таких как С, C++, Pascal, COBOL, ANSI COBOL, Fortran, BASIC, PLI и ADA, она все еще используется в разработке приложений со встраиваемым SQL generator Средство генерирования чисел для создания последовательности генератор уникальных чисел. Оператор create generator имя-генератора создает специальную хранимую 64-битовую переменную. Опера- тор set generator то л устанавливает первое значение этой переменной. Функция gen__id(имя-генератора, ш) приводит к генерации нового числа, которое на ш больше, чем последнее сгенерированное число gfix Утилита командной строки, выполняющая ряд действий по ремон- ту базы данных, активации теневых копий базы данных (shadow), переводу базы данных в режим одного пользователя (исключи- тельный доступ, останов базы данных), а также восстановлению режима базы данных для доступа многих пользователей (рестарт базы данных), gfix также может исправлять зависшие транзакции 2РС, устанавливать размер кэша базы данных, включать или выключать режим синхронной записи на диск, выполнять чистку и устанавливать интервал очистки, переключать базу данных Firebird из режима чтения/записи в режим только чтение и наобо- рот, а также устанавливать диалект базы данных gpre В разработке приложений со встроенным SQL это препроцессор для блоков статического языка SQL в исходном коде языка про- граммирования, который транслирует данный код в формат BLR при подготовке к компиляции. Он может выполнять препроцес- сорную обработку текста на языках С, C++, COBOL, Pascal и ADA на ряде платформ grant/revoke предоставление/отмена Команды SQL grant и revoke, которые используются для уста- новления и отмены привилегий пользователей для доступа к объ- ектам базы данных Groton Сокращение для Groton Data System, имени компании, которая первоначально спроектировала и разработала реляционную СУБД, названную InterBase. В итоге из InterBase появился Firebird. Двое из директоров Groton — Jim Starkey и Ann Harrison — актив- но участвуют в процессе разработки Firebird
1084 Глоссарий gsec Утилита командной строки безопасности Firebird для управления базой данных на уровне сервера, содержащей имена пользовате- лей и пароли (security.fdb в версии 1.5, isc4.gdb для версии 1.01), которая применяется для всех пользователей всех баз данных. Эта утилита не может быть использована для создания или изме- нения ролей, поскольку роли определяются в пользовательских базах данных gstat Утилита командной строки, с помощью которой можно получить статистику базы данных Firebird. Она анализирует внутренние структуры, такие как коэффициент заполнения, заголовок страни- цы, индексные страницы, страницы протокола и системные отно- шения. Можно также получить информацию о версиях записей (обычно очень объемную) от таблицы к таблице. Для этого нужно использовать совместно переключатели -г и -t имя-таблицы hierarchical database иерархическая база данных Старая концепция проектирования для реализации в базе данных отношений таблица-таблица путем создания древовидной струк- туры наследуемых индексов host language Общий термин для языка программирования, на котором написа- но приложение identity attribute идентичность атрибута Некоторые реляционные СУБД (например, MS SQL) поддержива- ют атрибут таблицы, который автоматически реализует для цело- го столбца искусственный первичный ключ для таблицы. При этом новое значение такого столбца автоматически генерируется для каждой новой добавляемой строки. Firebird напрямую не поддер- живает такой атрибут. Похожий механизм может быть получен явным определением целочисленного столбца соответствующего размера, созданием генератора для получения значений этого столбца и определением триггера before insert, который вызы- вает функцию GEN_ID() для получения следующего значения генератора IBO Аббревиатура для IB Objects, компонентов доступа к данным и связанными с данными управляющими элементами, инкапсули- рующими API Firebird и InterBase для использования в продуктах Borland Delphi, C++ Builder и Kylix IBX Аббревиатура для InterBase express, компонентов доступа к дан- ным, инкапсулирующих API InterBase, распространяемых фирмой Borland вместе с продуктами Delphi и C++ Builder index Специализированная структура данных, поддерживаемая ядром индекс Firebird, которая предоставляет компактную систему указателей на строки в таблице INET error ошибка сети В firebird.log отмечаются ошибки, полученные сетевой подпро- граммой Firebird от соединений клиент-сервер, использующих installation протокол TCP/IP Процедура и процесс копирования программного обеспечения на инсталляция компьютер и его конфигурирования для использования И для InterBase. — Прим, перев.
Гпоссарий 1085 InterBase Реляционная СУБД, которая была предшественником Firebird. Разработанная вначале в компании Gorton Data Systems, она в итоге перешла во владение компании Borland Software Corporation. InterBase 6 был реализован в открытых кодах в 2000 году в рамках InterBase Public License. Firebird был разрабо- тан независимыми разработчиками из этих открытых кодов и вскоре стал разветвленной разработкой InterClient Устаревший клиент Java типа 2 JDBC для сервера InterBase 6. В Firebird он замещен системой JayBird из семейства драйверов с открытыми кодами, совместимыми с JDBC/JCA (тип 2 и тип 4) InterServer Устаревшая оболочка, основанная на сервере управляемом драйвером Java, поставляемая с открытыми кодами InterBase 6. Как InterServer, так и сопутствующий ему InterClient заменены в Firebird на JayBird, более новый интерфейс Java с открытым ко- дом ISC, isc и т. д. Сообщения об ошибках, некоторые переменные окружения и мно- гие идентификаторы в API Firebird имеют префикс "ISC" или "isc". С точки зрения чисто исторического интереса можно сказать, что эти начальные символы являются производными от начальных букв "InterBase Software Corporation", имени дочерней компании Borland, которая существовала в процессе некоторого периода, когда Borland владел предшественником Firebird — InterBase isolation level Этот атрибут транзакции описывает способ, каким транзакция уровень изоляции уровень изолированности будет взаимодействовать с другими транзакциями, имеющими доступ к той же самой базе данных, в терминах видимости и по- ведения при блокировке. Firebird поддерживает три уровня изоля- ции: read committed (подтвержденное чтение), repeatable read (повторяемое чтение, также называемое snapshot, мгновенный снимок, или concurrency, параллельность) и snapshot table stability (согласованность). Хотя read committed является значением по умолчанию для большинства реляционных систем, значением по умолчанию для Firebird является snapshot (уровень изолированности read committed был реализован в InterBase много позже snapshot). См. также transaction isolation isql Название для интерактивной утилиты запросов командной строки Firebirds, которая единовременно может соединяться только с одной базой данных. Она имеет мощный набор команд, вклю- чающих свое собственное подмножество команд SQL Firebird в дополнение к обычному набору команд динамического SQL. Она содержит обширный набор включенных макросов для получения информации о метаданных, isql может выводить наборы команд, в том числе включенные комментарии в файл, и может также "за- пускать" наборы команд в. виде скриптов— рекомендуемый спо- соб создания и изменения объектов базы данных JDBC Аббревиатура для Java DataBase Connectivity, набора стандартов для создания драйверов для соединения приложений Java с ба- зами данных SQL join соединений join является ключевым словом для указания серверу, что ре- зультат оператора select включает объединение столбцов из нескольких таблиц, связанных соответствием одной или более пар ключей
1086 Глоссарий jrd Внутреннее имя для ядра базы данных Firebird. Оно является аббревиатурой для Jim’s Relational Database (реляционная база данных Джима), продуктом основного ядра, введенным Джимом Старки (Jim Starkey), который стал ядром InterBase и, позже, Firebird key ключ Ограничение таблицы, применимое к столбцу или группе столб- цов в структуре строк таблицы. Первичный ключ или уникальный ключ указывают на уникальную строку, в которой они присутству- ют, в то время как внешний ключ указывает на уникальную строку другой таблицы посредством связи с ее первичным ключом или другим уникальным ключом kill (shadows) уничтожение теневой копии Когда теневая копия (shadow) базы данных создается с использо- ванием ключевого слова MANUAL и оперативная копия становит- ся недоступной, дальнейшие соединения с базой данных блоки- руются. Для восстановления возможностей соединений с базой данных необходимо выполнить команду gfix -kill база-данных для удаления ссылок на теневую копию leaf bucket сегменты листьев В индексном дереве b-tree элемент данных в последнем индексе узла дерева. Число сегментов листьев, полученное в статистике индекса утилитой gstat, дает приблизительное количество строк таблицы limbo (transaction) зависшая транзакция Зависшая транзакция может появиться, когда транзакция стартует над несколькими базами данных (2РС). Транзакции со многими базами данных защищены двухфазным подтверждением, что гарантирует, что без подтверждения частей транзакции для каж- дой базы данных для всей транзакции будет выполнен откат. Ес- ли одна или несколько баз данных, используемых в транзакции, станут недоступными до завершения двухфазного подтвержде- ния, транзакция останется в неопределенном состоянии. Такую транзакцию называют зависшей locking conflict конфликт блокировок В оптимистической схеме блокировок Firebird строка становится заблокированной для изменений другими транзакциями в тот мо- мент, когда ее транзакция посылает запрос на ее изменение. Если транзакция имеет уровень изоляции snapshot table stability (также называемый Consistency), блокировка возникает, когда транзакция читает строку. Конфликт блокировок появляется, когда другая транзакция пытается послать свои собственные изменения для этой строки. Конфликты блокировок имеют множество причин, характеристик и способов разрешения в соответствии с заданны- ми установками в транзакциях, вовлеченных в конфликт lock resolution разрешение блокировки Общий термин, означающий меры, принятые кодом приложения для разрешения условий, когда другие транзакции пытаются из- менить строку, которая была заблокирована транзакцией, по- славшей запрос на изменение. В качестве специфического терми- на разрешение блокировки означает установку в транзакции па- раметра wait/nowait, который определяет реакцию транзакции на возникший конфликт блокировки metadata метаданные Общее существительное, означающее структуру всех объектов, содержащихся в базе данных. Поскольку Firebird хранит опреде- ления объектов базы данных в самой базе данных, используя свои таблицы, типы данных и триггеры, термин "метаданные" также означает данные, хранящиеся в этих системных таблицах
Глоссарий 1087 multi-generational architecture (MGA) многоверсионная архитектура Термин, применяемый в отношении ядра Firebird, использующего оптимистическое блокирование записей и высокий уровень изо- лированности транзакций, позволяющий транзакциям видеть свои и чужие изменения без блокировок чтения. Достигается путем хранения ядром множества версий одной записи и определения "возраста" этих версий по отношению к конкретной транзакции. См. также versioning architecture natural (scan) естественное сканирование Указывает, что соответствующая таблица будет просматриваться в "естественном порядке" (то есть вне определенного порядка и без использования какого-либо индекса). Иногда это видно в пла- нах запросов, созданных оптимизатором next transaction следующая транзакция Номер, который будет выдан ядром Firebird очередной транзак- ции. Может быть просмотрен в статистике, извлекаемой утилитой gstat с ключом -header non-standard SQL нестандартный SQL Термин, который часто можно услышать при ссылках на реляци- онные СУБД, имеющие низкий уровень соответствия языку ISO и стандарту синтаксиса SQL. См. также standard SQL пол-unique key неуникальный ключ Столбец или группа столбцов, которые могут служить указателем на группу строк в наборе. Ограничение внешнего ключа, исполь- зуемое для реализации отношения один-ко-многим, создается для соответствия неуникального столбца или группы столбцов в "до- чернем" или "детальном" наборе уникальному ключу в "родитель- ском" или "главном" наборе normalization нормализация Общая техника, используемая при анализе данных до начала проектирования базы данных с целью устранения повторяющихся групп во множестве таблиц и уменьшения дублирования одних и тех же "фактов" в связанных таблицах null пустое значение Иногда неправильно называется "нулевым значением". Состояние элемента данных, который не имеет известного значения. Логиче- ски это интерпретируется как неизвестное значение и по этой причине не может быть использовано при вычислении выра- жений. null не эквивалентен нулю, пробелу или пустой строке (строке с нулевой длиной); он не представляет бесконечности. Он пред- ставляет состояние элемента данных, которому либо не было присвоено значение, либо было присвоено null ODBC Аббревиатура для Open DataBase Connectivity (открытый интер- фейс доступа к базам данных). Это стандарт интерфейса на уровне вызовов, который позволяет приложениям получить дос- туп к данным в любой базе данных, для которой есть драйвер, поддерживающий этот стандарт. Существует ряд драйверов ODBC, поддерживающих Firebird, включая драйвер с открытыми исходными текстами, внутренне соответствующий стандарту JDBC ODS Аббревиатура для On-Disk Structure (структура на диске). Это число, которое указывает на версию внутренней структуры и формата базы данных Firebird или InterBase. Для InterBase 4.0 это было 8, для InterBase 4.2 было 8.2, а для InterBases— 9. Firebird 1 имел ODS 10, а 1.5 — 10.1.
1088 Глоссарий Базу данных можно перевести в более высокий уровень ODS, выполнив ее копирование gbak -bfackup] -ttransportable] с использованием старой версии программы gbak и восстановив из этого файла копии с использованием новой версии gbak OLAP Аббревиатура для OnLine Analytical Processing (онлайновая ана- литическая обработка данных) технологии, которая применима к базам данных, вырастающим до таких размеров, что к ним не- практично обращаться напрямую в качестве основы деловых ре- шений. Обычно системы OLAP разрабатываются для анализа и графического представления, идентификации и фиксирования исторических этапов или аномалий, создания проекций и гипоте- тических сценариев, сжатия больших объемов данных для отче- тов и т. д. OS Аббревиатура для Operating System (операционная система) Oldest Active Transaction (OAT) старейшая активная транзакция Статистика, поддерживаемая сервером Firebird, глобальная для базы данных. Старейшая транзакция, все еще находящаяся в базе данных, которая не была ни подтверждена, ни отменена Oldest Interesting Transaction (OIT) старейшая заинтересованная транзакция Статистика, поддерживаемая сервером Firebird, глобальная для базы данных. Идентификатор старейшей транзакции, которая была завершена по rollback. Когда номер OIT "застревает" при продвижении всех остальных номеров транзакций вперед, сборка мусора (чистка от старых версий записей) не может продолжать- ся, и операции с базой данных сильно замедляются, а в итоге полностью зависают . Номер OIT может быть просмотрен при использовании переключателя -header утилиты командной стро- ки gstat OLE DB Аббревиатура от Object Linking and Embedding for DataBases (встраивание и связывание объектов для баз данных). OLE является стандартом Microsoft, разработанным и продвигаемым для включения двоичных объектов множества различных типов (изображения, документы и т. д.) в приложения Windows вместе со связями на уровне приложений с программными объектами, кото- рые их создают и изменяют. Средство OLE DB было введено в качестве попытки предоставить разработчикам средства для обеспечения более специфичной для конкретного поставщика поддержки соединений с базами данных— в первую очередь для реляционных баз данных, — с которыми можно работать с по- мощью ODBC. Позже Microsoft создал технологию ADO над OLE DB 2 Не упомянут термин Oldest Snapshot Transaction (OST) — номер транзакции, которая бы- ла старейшей активной при старте самой старой активной на текущий момент транзакции SNAPSHOT. Именно этот номер препятствует сборке мусора. Расстояние между OIT и OST используется в Firebird 1.0 и 1.5 для определения момента старта автоматической чистки мусо- ра (sweep). — Прим. науч. ред. 3 Это не так. Существует много систем, в которых номер OIT отстает от Next на несколько миллионов, при этом база данных работает нормально. "Застревание" OIT является индикаци- ей того, что где-то в базе данных есть версии записей, отмененные no rollback, которые долж- ны быть убраны как мусор принудительным запуском sweep. — Прим. науч. ред.
Гпоссарий 1089 OLTP Аббревиатура для OnLine Transaction Processing (онлайновая обработка транзакций), рассматриваемая как одно из основных требований к ядру базы данных. Вообще говоря, OLTP больше относится к поддержке клиентов, выполняющих чтение, измене- ние или создание данных в реальном режиме времени optimization оптимизация В самом широком смысле означает техники, позволяющие сде- лать выполнение программного обеспечения приложений и баз данных настолько эффективным, насколько это возможно. Как специфический термин, он часто используется ядром Firebird при анализе операторов select и построении эффективных планов для поиска данных. Подпрограммы ядра Firebird, которые просчи- тывают эти планы, вместе называются оптимизатором Firebird page страница База данных Firebird состоит из блоков дискового пространства фиксированной длины, называемых страницами. Firebird выде- ляет страницы по мере необходимости. Поскольку страница хра- нит данные, она может быть страницей одного из десяти типов страниц, все одинакового размера — размера, определенного в атрибуте page_SIZE в процессе создания базы данных. Тип стра- ницы, сохраняемой на диске, зависит от типа объекта данных, сохраняемого на странице: данные, индекс, blob и т. д. page_size Размер каждого фиксированного блока определяется в атрибуте page_size, задаваемом для базы данных при создании или вос- становлении базы данных. Участки памяти для кэша базы данных также выделяются в единицах page size parameter параметр Широко распространенный термин во множестве контекстов Firebird. Он может именовать значения, передаваемые в качестве аргументов хранимой процедуре и получаемые из хранимой про- цедуры (входные и выходные параметры). Термин также может означать элементы данных, которые передаются в блоках функ- ций API Firebird (блок параметров базы данных, блок параметров транзакции, блок параметров сервиса), или атрибуты, видимые в приложении при соединении с базой данных (параметры соеди- нения) или атрибуты транзакции (параметры транзакции). В клиентских приложениях синтаксические элементы, которые передаются предложениям where операторов SQL для подстанов- ки значений во время выполнения, часто реализованы в виде "параметров". Отсюда термин "параметризованные запросы" PHP Аббревиатура для РНР: Hypertext Preprocessor (гипертекстовый препроцессор). Это язык скриптов встроенного HTML с открытыми исходными кодами, применяемый для создания приложений Web, особенно тех, которые используют базы данных. Он имеет хоро- шую поддержку множества сетевых протоколов и окружений про- граммирования для Web. Его сильная сторона — совместимость со многими типами баз данных. РНР также может общаться по сетям, использующим IMAP, SNMP, NNTP, POP3 или HTTP. Изо- бретателем РНР был Расмус Ледорф (Rasmus Lerdorf) в 1994 году. С 1997 года РНР находится в руках большого сообще- plan план ства открытых исходных текстов См. query plan
1090 Глоссарий platform платформа Термин, неточно используемый для названия комбинации аппа- ратных средств и программного обеспечения операционных сис- тем или только одного программного обеспечения операционной системы, например, "платформа Windows 2000", "платформа Linux", "платформы UNIX". Кроссплатформенность обычно озна- чает "применимое на множестве платформ" или "переносимое на другие платформы" prepare Функция API, которая вызывается перед первой отправкой запро- подготовка са. Она запрашивает у сервера проверку оператора, создание плана запроса и некоторых информационных элементов относи- тельно ожидаемых данных primary key первичный ключ Ограничение на уровне таблицы, отмечающее столбец или группу столбцов как ключ, который должен уникально идентифицировать каждую строку в таблице. Хотя таблица может иметь более одно- го уникального ключа, только один из этих ключей может быть первичным. Когда вы применяете ограничение primary key к столбцам в таблице Firebird, уникальность будет поддерживаться с помощью автоматически созданного уникального индекса, кото- рый по умолчанию будет возрастающим и будет назван в соот- ветствии с соглашениями PSQL Аббревиатура для Procedural SQL (процедурный SQL), подмно- жества расширенного SQL, созданного для написания хранимых процедур и триггеров. Существует небольшая разница между подмножествами PSQL, используемыми для хранимых процедур и для триггеров qli Это Query Language Interpreter (интерпретатор языка запросов), интерактивный клиентский инструмент запросов для Firebird. Он может обрабатывать операторы DDL и DML из SQL и GDML (язы- ка, используемого в предшественнике Firebird — InterBase 3). Хотя уже есть isql и другие инструменты графического интерфейса сторонних разработчиков, qli все еще имеет значение по причине его способности осуществлять некоторые операции, до сих пор не реализованные в SQL Firebird. В отличие от isql, qli может одно- временно соединяться более чем с одной базой данных и может симулировать обращение к нескольким базам данных в одном запросе query Общий термин для любого обращения SQL к базе данных, посту- запрос пающего от клиентского приложения к серверу query plan Стратегия использования индексов и методов доступа для сорти- план запроса ровки и поиска при выполнении запросов. Оптимизатор Firebird всегда создает план для каждого запроса select, включая и под- запросы. Можно задать пользовательский план с использованием RDB$— синтаксиса предложения plan Префикс, который мы видим в идентификаторах многих создан- ных системой объектов Firebird. Это след от Relational DataBase, имени более ранней реляционной базы данных, разработанной в DEC. Созданная система RDB была предшественницей InterBase, прообраза Firebird. Наследником RDB также является СУБД Oracle
Глоссарий 1091 RDB$DB_KEY Скрытый, непостоянный, уникальный ключ, который вычисляется в ядре Firebird для каждой строки таблицы из физического адреса страницы, на которую помещается строка, и ее смещения от на- чала страницы. Он напрямую связан с кардинальностью таблиц и наборов и может изменяться без предупреждений. Он всегда бу- дет меняться при восстановлении базы данных из резервной ко- пии. rdb$db_key никогда не должен трактоваться как постоянный. С аккуратностью он может быть использован в пределах одной атомарной операции для значительного ускорения некоторых операций в DSQL и PSQL RDBMS реляционная СУБД, РСУБД Аббревиатура для Relational DataBase Management System (реля- ционная система управления базами данных). Это общая концеп- ция хранения данных в соответствии с абстрактной моделью, которая использует соответствие ключей для связи одних фор- мально сгруппированных данных с другими сгруппированными данными, представляя, таким образом, отношение между двумя группами Read Committed Наименее ограничивающий уровень изоляции для транзакций подтвержденное чтение Firebird. Read Committed позволяет транзакции перечитывать дан- ные и видеть подтвержденную работу других транзакций после начала выполнения данной транзакции. Уровни изоляции snapshot и snapshot table stability не позволяют видеть чу- жие подтвержденные изменения redundancy избыточность Условие в базе данных, когда два одинаковых "факта" хранятся в двух не связанных местах. В идеале избыточность должна быть устранена в процессе нормализации при анализе данных. Однако существуют некоторые условия, при которых оправдано некото- рое количество избыточности. Например, бухгалтерские проводки часто содержат элементы данных, которые возможно могли бы быть получены из соединений, выборки или вычислений из других структур. Однако узаконенным требованием является сохранение постоянной записи, которая не будет изменяться, если после- дующее изменение отношения в базе данных будет отменять redundant indexes избыточные индексы условие сокращения избыточности Избыточные индексы часто появляются, когда существующая база данных импортируется в Firebird из других реляционных СУБД. Когда ограничение primary key, unique или foreign key применяется к столбцу или столбцам, Firebird автоматически соз- дает индекс для поддержания этого ограничения. Сделав это, Firebird игнорирует все существующие индексы, дублирующие автоматические индексы. Наличие дубликатов индексов для клю- чей или других столбцов может нарушить работу оптимизатора запросов, приводя к созданию очень медленных планов referential integrity ссылочная целостность Обычно означает, каким образом реляционная СУБД реализует механизм для формальной поддержки и защиты зависимостей между таблицами. Поддержка ссылочной целостности означает наличие элементов языка и синтаксиса, доступных для предос- тавления таких возможностей. Firebird предоставляет формальный механизм для поддержки ссылочной целостности, включающий каскадные ограничения для отношений внешнего ключа. Это иногда называется декларатив- ной ссылочной целостностью
1092 Глоссарий relation отношение В теории реляционных баз данных это замкнутый набор данных, формально распределенный по столбцам и строкам. Этот термин также взаимозаменяем с термином "таблица" за исключением того, что отношение не может иметь дублированных строк, когда таблица это может. Существующая терминология в Firebird эти имена относит к системным таблицам (например, к таблице rdb$relations, которая содержит записи для каждой таблицы базы данных) relationship отношение Абстрактный термин, указывающий на то, как отношения (или таблицы) связаны с другими через соответствующие ключи. На- пример, таблица Order Detail (детальная запись заказа) будет находиться в отношении зависимости или в отношении внеш- него ключа с таблицей Order Header (заголовок заказа) replication репликация Систематический процесс, с помощью которого записи копируют- ся из одной базы данных в другую на регулярной основе в соот- ветствии с заранее определенными правилами, с целью перевода двух или более баз данных в синхронное состояние result table результирующая таблица Набор строк, являющихся результатом запроса SQL select. Бо- лее точно этим термином называют результирующий набор, си- ноним выходного набора roles роли Стандартный механизм SQL для определения набора полномочий по использованию объектов базы данных. Когда создана роль, назначение ей полномочий осуществляется операторами grant, как если бы она была обычным пользователем. Затем роль можно назначать (grant) индивидуальным пользователям, как если бы она была привилегией. Таким путем упрощается поддержка пол- номочий пользователей к базе данных rollback откат (транзакции) Действие или процесс по отмене всей работы, которая была вы- полнена во время транзакции. Пока у транзакции есть работа, ожидающая завершения, отправленная на сервер, но не подтвер- жденная, она остается незавершенной и ее результаты не видны другим транзакциям. Если клиентское приложение вызывает rollback, вся отправленная работа отменяется, а изменения теряются. Если же транзакция подтверждается, то ее работа не может быть отменена schema схема Формальное описание базы данных, обычно помещенное в скрипт или скрипты, содержащие операторы SQL, определяющие каждый объект базы данных. Термин "схема" часто заменяется термином "метаданные" schema cache кэш схемы Механизм, при котором некоторые описательные элементы базы данных сохраняются на локальном клиентском диске или в опера- тивной памяти для быстрого обращения во время выполнения с целью устранения необходимости постоянно обращаться к базе данных для получения атрибутов схемы (метаданных) scrollable cursor прокручиваемый курсор Курсор является указателем на строку в таблице базы данных или в выходном наборе. Позиция курсора в базе данных определяется кардинальностью строки, на которую он в настоящий момент ука- зывает (то есть смещением этой строки относительно первой строки в наборе). Изменение позиции курсора требует возврата указателя к первой строке для поиска новой позиции. Прокручи-
Гпоссарий 1093 ваемый курсор способен самостоятельно помещаться на задан- ную новую позицию (вверх или вниз) относительно его текущей selectivity of an index селективность индекса, избирательность индекса позиции (не поддерживается в Firebird) Как общий термин означает диапазон возможных значений для столбца индекса во всей таблице. Чем меньше возможных значе- ний, тем ниже селективность. Низкая селективность также может появиться, когда индекс с большим количеством возможных зна- чений представлен фактическими данными с очень большим ко- личеством дубликатов небольшого количества значений. Низкая селективность — это плохо, высокая — хорошо. Уникальный ин- selectable stored procedure хранимая процедура выбора деке имеет максимально возможную селективность Хранимая процедура, написанная с использованием специально- го синтаксиса PSQL для вывода многострочного результирующего набора вызвавшему объекту. Она вызывается с использованием оператора select. См. также executable stored procedure Services API сервисы API API для обращения к функциям некоторых серверных утилит Firebird, таким как резервное копирование, статистика, sweep и др. Сервисы API могут быть неприменимыми к некоторым вер- sets наборы сиям Классического сервера В терминах реляционной базы данных набор это множество дан- ных, содержащее одну или более строк, состоящих из одного или более столбцов данных, где каждый столбец состоит из одного элемента данных заданного размера и типа. Например, специфи- кация запроса select или просмотр определяют выходной набор для клиентского приложения или модуля PSQL, в то время как спецификация запроса update определяет набор, с которым вы- полняется заданная операция shadowing/shadows теневое копирование / теневые копии Процесс, доступный на сервере Firebird, при котором в реальном времени поддерживается точная копия базы данных со всеми изъянами и недостатками на отдельном жестком диске той же серверной машины, где располагается и база данных. Такая ко- пия называется теневой копией базы данных. Ее назначением является обеспечение возможности быстрого продолжения рабо- ты после физического повреждения жесткого диска, на котором размещается база данных. Теневая копия не является подходя- щим заменителем для репликации или копирования SMP Аббревиатура для Symmetric MultiProcessing (симметричный мультипроцессор), архитектуры компьютера, которая делает мно- жество процессоров доступными для выполнения одновременных индивидуальных процессов одной операционной системой. По теории любой незанятый процессор может быть назначен любой задаче, и чем больше процессоров в системе, тем выше произво- дительность и пропускная способность 4 Сервер оценивает селективность как число, обратное разнице общего числа ключей и числа повторяющихся значений ключей индекса (от 0 до 1). Поэтому чем меньше значение селективности (столбец rdb$indices.rdb$statistics), тем лучше. Под "низкой" селектив- ностью здесь имеется в виду селективность, стремящаяся к 1. Под "высокой" — стремящаяся к 0. — Прим. науч. ред.
1094 Гпоссарий Snapshot "мгновенный снимок" snapshot является одним из трех уровней изоляции транзакций, поддерживаемых Firebird. Он обеспечивает стабильный вид базы данных, который остается постоянным для пользователя транзак- ции на время жизни этой транзакции. Он также называется конку- рентным уровнем изоляции. См. также Read Committed, Snapshot Table Stability Snapshot Table Stability snapshot table stability является самым защищенным из трех уровней изоляции транзакций в Firebird. Он поддерживает согла- сованный вид базы данных для пользователя транзакции, не по- зволяя другим транзакциям изменять любую считанную им строку, даже если транзакция еще не посылала никаких изменений. Он еще называется согласованным уровнем изоляции. См. также Read Committed, Snapshot SQL Язык запросов, разработанный для извлечения осмысленных наборов данных из реляционной базы данных. Его правильное произношение "эс-кью-эль", а не "сиквел", как думают некоторые люди ("сиквел" было именем другого языка запросов). К тому же это не является аббревиатурой от Structured Query Language5' 6 standard SQL, SQL standard стандарт SQL Означает синтаксис и реализацию элементов языка SQL, который опубликован ISO (International Organization for Standardization, Международная организация по стандартизации). Это очень сложные описания стандарта вместе с всеобъемлющим синтакси- сом и функциональностью на множестве уровней stored procedure хранимая процедура Компилированный модуль, хранимый в базе данных для вызова приложениями или другими модулями, хранимыми на сервере (триггерами, другими хранимыми процедурами). Она определяет- ся в базе данных на исходном языке— процедурном SQL, или PSQL — и состоит из обычных операторов SQL, а также из специ- альных, расширений языка SQL, которые поддерживают структу- ,ры, циклы, условную логику, локальные переменные, входные и выходные аргументы, обработку исключений и др. subquery подзапрос Спецификация запроса, который может определить выходные столбцы, которые получаются из выражений. Подзапрос является специальным видом выражения, которое возвращает результат, являющийся выходным набором оператора select. Его также называют подвыбором или встроенным запросом sub-select, subselect подвыбор Столбец подвыбора является столбцом, который получается в результате подзапроса. Такие столбцы не являются изменяемы- ми. См. также subquery Superserver architecture архитектура Суперсервера Суперсервер является именем многопользовательской модели с потоками (threads) в отличие от начальной модели InterBase, ко- торая использует отдельный серверный процесс для каждого клиентского соединения. Первоначальная модель сейчас называ- ется Классическим сервером 5 Тем не менее большинство американских программистов говорят "сиквел". — Прим, перев. 6 Согласно Оксфордскому словарю английского языка аббревиатура SQL расшифровыва- ется именно как Structured Query Language — структурированный язык запросов в отношении к базам данных. — Прим. науч. ред.
Гпоссарий 1095 surrogate key искусственный (суррогатный) ключ В определении уникального ключа (например, первичного ключа) он может появиться, когда нет столбца или комбинации столбцов, которые могли бы гарантировать уникальную идентификацию каждой строки. В этом случае может быть добавлен столбец, по- лучающий значения, которые точно являются уникальными. Такой ключ называется искусственным ключом sweeping чистка Процесс, который собирает и освобождает устаревшие версии каждой записи в базе данных, когда достигнут заданный порог. Это число, которое имеет значение по умолчанию 20 000 и назы- вается интервалом чистки, вычисляется как разность между OIT и OST. Автоматическая чистка может быть отменена установкой интервала чистки в ноль. Ручная чистка может быть вызвана спе- циально с помощью утилиты gfix. Чистка не используется в реля- ционных СУБД, которые не сохраняют устаревшие версии запи- сей SYSDBA Аббревиатура для system database administrator (системный ад- министратор базы данных), человека, ответственного за админи- стрирование баз данных system tables системные таблицы Поскольку ядро сервера базы данных является самодостаточным, все метаданные или схемы (данные, которые описывают структу- ру и атрибуты объектов базы данных) поддерживаются в базе данных в виде набора таблиц, которые создаются командой create database. Такие таблицы, которые хранят "данные о дан- ных", называются системными таблицами. Все системные таб- лицы Firebird имеют идентификаторы, которые начинаются с пре- фикса rdb$ и содержат данные о себе, так же как и данные о каждом другом объекте в базе данных table таблица Термин заимствован из технологии настольных баз данных, опи- сывает логическую структуру, которая хранит наборы данных в табличном формате в виде записей (строк) полей, где все строки по определению идентичны слева направо по количеству и отно- сительному положению полей и их типов данных и размеров. В действительности Firebird хранит данные не в физически табу- лированной форме, а в смежных блоках дискового пространства, называемых страницами transaction Логическая единица работы, которая может включать один или транзакция более операторов. Транзакция начинается, когда ее запускает клиентское приложение, и завершается, когда приложение вы- полняет ее подтверждение или откат. Транзакция является ато- марным действием — подтверждение должно быть способным подтвердить каждую часть работы, иначе вся ее работа будет ликвидирована. Откат, аналогично, отменит все части работы, которая была отправлена на сервер с момента старта транзакции transaction isolation Механизм, при котором каждая транзакция поддерживается окру- изоляция транзакции, уровень изолированности транзакции жением так, что она воспринимает себя, как если бы она выпол- нялась одна в базе данных. Когда одновременно выполняется множество транзакций, то действия всех других транзакций не видны каждой транзакции, если на момент ее старта эти действия не были подтверждены. Firebird поддерживает не один, а три уровня изоляции, включая один уровень, который может видеть результаты работы других транзакций, когда они подтверждаются. См. Read Committed, Snapshot, Snapshot Table Stability
1096 Гпоссарий transitively dependent транзитивно зависимый Ограничение или условие, при котором одна таблица С является зависимой от другой таблицы А, потому что таблица С зависит от другой таблицы В, которая зависит от таблицы А. Подобная зави- симость может, например, возникать, если таблица В имеет внеш- ний ключ, ссылающийся на первичный ключ таблицы А, а таблица С имеет внешний ключ, ссылающийся на первичный ключ таблицы В. Этот термин также используется при моделировании данных для обозначения условий, когда в процессе нормализации атрибут одной сущности имеет частичную (но не полную) зависи- мость от набора уникальных атрибутов другой сущности trigger триггер Скомпилированный модуль, принадлежащий таблице, который выполняет действие, когда происходит событие DML для строки этой таблицы. На событие до и/или после операций добавления, изменения или удаления строки таблицы можно создать любое количество триггеров, используя все возможности процедурного SQL (PSQL) tuple кортеж В терминологии реляционных баз данных это "строго корректное" имя для строки в таблице или для группы столбцов, которые яв- ляются подмножеством строки. Борцы за чистоту языка скажут, что в SQL кортеж именуется строкой UDF Аббревиатура для User Defined Function (функция, определенная пользователем). Более корректно — внешняя функция. См. также external function unbalanced index несбалансированный индекс Индексы Firebird поддерживаются в виде структур двоичных де- ревьев. Про такие структуры говорят, что они разбалансированы , когда новые узлы все время добавляются таким образом, что это приводит к значительному ветвлению одной "стороны" двоичного дерева. Обычно такое происходит, когда процесс добавляет сотни тысяч новых строк в одной транзакции. По этой причине рекомен- дуется деактивировать индексы в процессе объемных добавле- ний. Последующая активация пересоздаст полностью сбаланси- рованные индексы uninstallation Уродливое обратное словообразование, путающее говорящих не деинсталляция на английском языке, в то время как оно до сих пор не существует ни в одном уважающем себя словаре! Оно приблизительно озна- чает "процесс, обратный инсталляции" (то есть удаление ранее установленного программного продукта из компьютерной сис- темы) union объединение Предложение в спецификации запроса select, которое позволяет строки двух или более операторов select объединять в один конечный выходной набор, если объединяемые наборы соответ- ствуют друг другу по количеству, типам данных и размерам их выходных столбцов. Эти наборы могут быть выбраны из различ- ных таблиц 7 Совершенно не относится к InterBase и Firebird, так как индексы в них построены на базе структур b-tree, a b-tree это не "binary tree". В то время как слова Хелен правильны в отноше- нии binary tree, структуры b-tree по определению не могут быть "разбалансированы", т. к. ко- личество узлов от корня до листа одинаково для всех ключей индекса и равно глубине индек- са. Поэтому рекомендации по поводу "разбалансированности" индексов можно игнориро- вать. — Прим. науч. ред.
Гпоссарий 1097 updatable view изменяемый просмотр Про просмотр говорят, что он изменяемый, если он получен из обычного запроса к одной таблице и все его столбцы существуют в базовой таблице. Некоторые неизменяемые просмотры могут быть сделаны изменяемыми путем создания для них триггеров. См. также view validation Механизм, с помощью которого новые данные, помещаемые в проверка столбец таблицы, проверяются некоторым образом для опреде- ления, соответствуют ли они требуемому формату, значению или диапазону значений. Двумя способами реализации проверок в базе данных являются ограничения check и триггеры. Ограниче- ние check будет вызывать исключение, если входные данные не пройдут проверку в существующем выражении или ограничении. В триггерах значение new.value может быть протестировано более детально, и если оно ошибочно, то можно выдать пользо- вательское исключение versioning architecture версионная архитектура То же, что и Multi-Generational Architecture (MGA, многоверсионная архитектура). Механизм, создающий новую версию строки при ее изменении или удалении. До COMMIT такая версия не видна дру- гим транзакциям, кроме той, в которой было произведено это из- менение или удаление. После COMMIT новая версия записи ста- новится постоянной, а старая становится или устаревшей или все еще актуальной для конкурирующих транзакций (в зависимости от их уровня изолированности). Это позволяет целиком исключить ненужные блокировки чтения, а блокировки модификации возни- кают только тогда, когда конкурирующие транзакции пытаются обновить одну и ту же запись view Стандартный объект SQL, который хранит спецификацию запроса просмотр, представление и ведет себя так же, как и обычная таблица. Просмотр не хранит данные на диске — он действует как предварительно определен- ный контейнер для набора выходных данных, которые находятся в одной или более таблицах WNET error ошибка WNET В протоколе firebird.log отмечает ошибку, полученную сетевой подсистемой Firebird от протокола именованных каналов Windows (Windows Named Pipes) соединения клиент-сервер XSQLDA Аббревиатура для extended SQL Descriptor Area (расширенная область дескрипторов SQL). Это структура API, которая исполь- зуется для передачи данных между клиентским приложением и серверным модулем синтаксического анализа динамического SQL. XSQLDA существуют в двух видах: входные дескрипторы и выходные дескрипторы XSQLVAR Структура для определения sqlvar, важного поля в структуре XSQLDA, которая используется для передачи и получения вход- ных и выходных параметров Y valve затворка Y Имя, данное подсистеме Firebird, определяющее, который из "внутренних движков" Firebird должен быть использован при под- ключении к базе данных. Например, использовать локальное под- ключение или сетевое, или какую функциональность нужно под- ключить при работе с базой данных, имеющей конкретный номер ODS
Предметный указатель А АСЮ 536 В BLR485 D DDL 246 DSQL 137, 155, 178, 224, 226, 230, 382, 384, 385, 620 Е ESQL 138, 175, 180, 207, 224, 226, 364, 382, 384, 392,412,565 F fblockprint 107 fbmgr/ibmgr 105 Firebird: 0 база данных безопасности 732, 734, 735 0 безопасность 715 0 встроенные библиотеки 457 0 где скачать 50 0 диалекты SQL 154, 162, 164, 165 0 защита базы данных 739 0 защита сервера 723 0 изменение параметров 89 0 использование смешанных платформ 72 0 комплект для Linux 52 0 комплект для Windows 55 0 контекстные переменные 155 0 конфигурирование 85 0 корневой каталог 90 0 литералы даты 157 0 менеджер блокировок 875 0 менеджер сервисов 846 0 параметры 91, 92 0 размещение на диске 65 0 результаты инсталляции, тест 57 0 содержание комплекта 50, 51 0 таблицы 298 0 типы данных 153 ° ARRAY 226—229 ° BLOB 221—225 ° автоинкремент 170 ° булевы (логические) 154 ° даты и времени 182, 183 ° масштабируемые с фиксированной точкой 172—174 ° операции 168 ° операции над смешанными типами 180 ° поведение в операциях 175—178 ° показатели степени 178 ° с плавающей точкой 178, 180 ° символьные 201, 203 ° целый 168, 169 ° числовой 167 0 требования к установке 47, 49 0 установка встраиваемого сервера 61 0 файл алиасов 103—105 0 файл конфигурации 79, 81, 89—91, 767 G gbak 106, 140, 163, 174, 237, 249, 284, 323, 360, 627, 702,716, 831,858 0 копирование БД 835 0 переключатели 836, 841 0 сообщения об ошибках 848 0 функции 832 gfix 95, 106, 140, 246, 274, 280, 281, 284, 292, 545, 581, 856 0 восстановление транзакций 868 0 переключатели 871 0 сообщения об ошибках 873 0 функции 856
Предметный указатель 1099 gpre 138, 224, 384 gsec 106, 132, 723, 726, 727 gstat 107, 363—366, 369, 371, 549 instsvc.exe 106 isql 89, 105, 107—110, 134, 136, 165, 177, 182, 207, 213, 219, 246, 258, 260, 262, 269, 281, 283, 351, 364, 382, 385, 458, 550, 603, 759, 761,801 0 выход из интерактивного режима 826 0 запуск 802 О извлечение метаданных 828 О интерактивные команды 808 О интерактивный режим 801 О командный режим 826 О команды SET 820 О команды SHOW 813 О общие команды 809 О переключатели командной строки 827 0 соединение с БД 803 О установка диалекта 807 L N NULL 437, 508 О ODS 162 Р PSQL 131, 155, 159, 171, 210, 223, 255, 259—261, 382, 384, 385, 391, 392, 439, 449, 593,597, 606,619, 629, 671 Q qli 485 R RDBSDBKEY661 s SQL 131, 138, 155 Lock Print 879 A Агрегатные выражения 507 Агрегатные вычисления 499 Администрирование баз данных 105 Адрес IP 72 Активный тупик 564 Алиас 74, 81, 103, 208, 209, 219, 269 0 столбца 397, 440 0 таблицы 437, 471 Архитектура клиент-сервер 113 Асинхронная сигнализация 708 Атомарный ключ 252, 340 Б База данных 268 0 безопасность 133, 715, 739 0 восстановление 831, 841 0 для чтения 283, 284 0 доступ 269, 272 0 закрытие (shutdown) 860 0 запрос 382 0 защита сервера 723 О идентификатор с разделителями 256 0 изменение 276 0 имена объектов 256 0 имена файлов БД 257 0 индексы 254 0 интервал очистки 293 0 информация о 273 0 клиент-сервер 114 0 ключи 250, 251 0 копирование 831 0 кэш 277—283 0 многоверсионность 292 0 многофайловая 275
1100 Предметный указатель База данных (проб.)'. О модель данных 248 О описание 247 О переносимые копии'838 О пользователь SYSDBA 725 О поля 250 0 проверка 293, 294, 867 0 проектирование 246 0 просмотры 255 0 протокол соединений с БД 735 0 размещение 268 0 расположение 130 0 резервное копирование 293 0 системные таблицы 246 0 соединение 270 0 создание 271, 272 0 столбцы 250, 299, 300, 302, 303 0 страницы 249 0 схема 258 0 таблица 249, 298 0 теневая копия 285—291,870 0 транзакция 274 0 удаление 297 О установки 863 О учетные данные пользователя 723, 733 0 файл 249, 274 О чистка 857, 860 Блок исключения 598 Блокировка 540, 541, 556, 557, 561, 562 0 взаимная 563, 878 0 конфигурация 900 0 менеджер 875 0 оптимистическая 540 0 отчет 879, 881 0 пессимистическая 561, 582 0 таблица 877 В Версия записи 560 Внешние библиотеки функций 450 Внешний ключ 251, 333, 335, 370 Внешняя виртуальная таблица 531 Внутренний курсор 472 Встраиваемый сервер 139, 140-, 781 0 безопасность 783 0 библиотеки 782 0 запуск 781 0 останов 784 0 пути к БД 782 0 совместимость 784 Выражение 420, 424 0 арифметическое 427 0 использование 440 Вычисление истинности 423 Вычисляемые столбцы 303, 440, 446 Г Генератор 170, 171 Группировка полей 506 д Дата и время 182 0 интервал времени 183 0 контекстные переменные 189 0 литералы даты 183, 187 0 литералы месяца 187 0 нулевая дата 190 0 правила для операций 191 0 преобразование даты 188 0 преобразования между типами 192, 193, 195 0 разделители в датах 186 Декларативная ссылочная целостность 333 Демоны inetd, xinetd 78 Диалект 174, 177, 193, 194, 196, 270, 386, 833, 864 Динамическим SQL 384 Динамическое связывание 414 Домен 158,231,238 0 атрибут CHARSET/CHARACTER SET 237 0 атрибут COLLATE 237 0 атрибут DEFAULT 233 0 атрибут NOT NULL 234 0 переопределение 238 0 создание 232 0 тип данных 232 0 удаление 242 3 Запрос 388, 403, 463, 600 И Идентификатор с разделителями 155 Именованный курсор 391 Индекс 254, 349, 416, 485, 495 0 автоматический 350 0 изменение 358
Предметный указатель 1101 О импорт 350 0 направленный 350, 356 О обслуживание 360 0 пользовательский 3£0 0 применение 351—353 0 просмотр 358 0 селективность 5б0—362 0 создание 354 0 составной, композитный 356 0 статистика 363, 364 0 удаление 359 Инструмент администратора 64 Интерактивный SQL 385 Исключение 598, 690, 692 О вложенное 696 О обработка 693 О создание 691 О типы 690 К Кардинальное число 389 Классический сервер 764 Клиент Firebird 134, 140 О инсталляция под Linux/UNIX 141 О инсталляция под Windows 142 Клиентская библиотека Firebird 135, 148 Коды ошибок 702 Команда: 0 kill 98 0 ping 82, 83 О setenvQ 87 О top 98 Конструкция: О FOR ... SELECT ... INTO ... DO 617 0 IF ... THEN ... ELSE 609 0 WHILE ... DO 610 Контекст запроса 122 Контекстная переменная 442 Коррелированный подзапрос 462 Л Локальный доступ 70, 766 м Массив 226, 227 Масштаб чисел 172. 177, 178 Метаданные 245 Модуль: 0 заголовок 605 0 комментарий 623 0 параметр 614 0 переменные 611, 612 0 редактирование 624 0 тело 605 0 удаление 626 0 чувствительность к регистру 623 н Набор данных 388—390, 392 Набор символов 205—208 0 маркер 211 0 переопределение 210, 211 0 по умолчанию 210 0 пользовательский 218 0 специальный 214 0 транслитерация 212 0 хранение 208, 209 Неименованный курсор 391 Нулевая дата 190 О Объектно-ориентированные классы 138 Ограничение 305 0 CHECK 234, 314, 676 0 UNIQUE 316 0 ссылочное 334, 338 0 целостности 307, 350 Оператор 388, 423, 426 0 ALTER PROCEDURE 657 0 COMMIT 576 0 CREATE 602 0 CREATE EXCEPTION 691 0 CREATE OR ALTER PROCEDURE 658 0 CREATE TRIGGER 669 0 CREATE VIEW 520 0 DELETE 409, 412 0 DROP PROCEDURE 660 0 DROP VIEW 527 0 EXCEPTION 620 0 EXECUTE 412 0 EXECUTE STATEMENT 620 0 EXIT 619 0 GRANT 745, 751 0 INSERT 405, 406, 416
1102 Предметный указатель Оператор (прод.)-. О LEAVE 619 О RECREATE PROCEDURE 658 О REVOKE 754 О ROLLBACK 577 О SELECT 393, 395, 499, 61,6, 621 О SUSPEND 618 О UNION 478, 481,482 О UPDATE 409, 410 О WHEN 693 0 логический 432 О приоритет 426,429 О сравнения 428 Операторные скобки 608 Определение данных 245 Оптимизатор 391 О запросов 351 Отношение 121, 342 О многие-ко-многим 343, 345 О обязательное 347 О один-к-одному 342 п Пакетные добавления 416 Параллельные задачи 552 Первичный ключ 121, 250, 307, 308, 311—313, 326 Переменные окружения 85 О * | ()СК88 О *_MSG 88 О FIREBIRD 87 О FIREBIRD_TMP 88 О INTERBASE 87 О 1SC PASSW0RD 87 О ISC USER 87 О ТМР 88 О тип 86 Пессимистическая блокировка 403 План 486, 494 О запроса 255, 351 О поиска 482 Подзапрос 475, 477 Положение столбца 389 Пользователь 743, 744 Пользователь SYSDBA 62, 245 Пользовательская точка сохранения 572 Порт: О TCP/IP 76 О поиск клиентом 80 0 прослушиваемый 77, 80 Последовательность сортировки 206, 215—218 Поток 391, 463, 484, 501 Правило атомарности 123 Предикат 421 0 сравнения 429 0 существования 433—436 Предложение: <> GROUPBY448, 506, 508 0 ORDERBY448, 490, 501 0 WHERE 447 Привилегии 741 0 ALL 749 0 EXECUTE 747, 756 0 REFERENCES 341, 746 0 UPDATE 745 0 к просмотру 518 0 объекты 742 0 ограничение 743 0 отмена 754, 756 0 пользователи 743 0 предоставление 745 0 предоставление прав 752 0 установка 741 Принудительная запись 274 Принцип атомарности 311 Проверка соединения 82 Просмотр 516, 517, 518, 747 0 виды 524 0 использование 527 0 создание 519 0 удаление 527 Пространство сортировки 88 Протокол ошибок 698 Путь соединения с сервером 75, 76, 80, 81, 103 Р Ранг 495 Река 391, 486 Рекурсивная процедура 639 Реляционная система управления базами данных, РСУБД 245 Реляционные данные 120 Репертуар символов 208 Роль 133, 751, 757 С Сгруппированный вывод 511 Селективные процедуры 131
Предметный указатель 1103 Сервер: О Firebird 124, 125, 135 О базы данных 113 О безопасность 132 О имя 74 О путь 74 О соединение с75 О терминальный 130 О транзакций Microsoft, MTS 129 Сервисы API 140 Сетевые протоколы 70 Символ терминатора 260 Скалярный подзапрос 440 Скрипт DDL 258, 259, 262, 265, 266 Скрипт безопасности 759, 761, 762 Событие 83, 599, 623, 704 Соединение 463 0 включающее 467 0 внешнее 467 0 внутреннее 464, 476 0 естественное 470 0 левое внешнее 467 0 неявное внутреннее 465 0 перекрестное 470 0 полное внешнее 469 0 правое внешнее 468 0 реентерабельное 473 0 явное внутреннее 465 Сообщения 88 Сортировка 499,505 Ссылочная целостность 251, 254, 332, 417, 681, 683 Статический SQL 384 Столбец 158 Строки 201 0 атрибут CHARACTER SET 201 О конкатенация 202 0 ограничитель 201 0 переменной длины 204, 205 0 управляющие символы 202 0 фиксированной длины 203, 204 СУБД клиент-сервер 114 0 двухуровневая модель 116, 126 О защита данных 115 0 масштабируемость 115 0 многоуровневая модель 117 О однопользовательская модель 127 О пользовательский интерфейс 120 0 проектирование 118 О распределение функций 115 О стандартизация 117 0 функциональная совместимость 115 Суперсервер 764 Суррогатный ключ 121, 251, 309 Схема БД 245 т Таблица 158, 298, 388 0 блокировка 557 0 внешняя виртуальная 317 0 временная 330 0 изменение 323—328 0 импорт файлов 320 0 пересечения 344 0 просматриваемая, виртуальная 516 0 системная 298 0 создание 298, 299 0 соответствия 339, 370, 682 0 удаление 329 0 управляющая 339 0 экспорт в файлы 322 Терминатор оператора 602 Типы данных 159—161 Точка сохранения 588 Точные знаковые числа 168 Транзакция 122, 128, 131, 132, 137, 537, 552, 565, 580 0 блокирующая 556 0 буфер параметров 569 0 восстановление 869 0 завершение 575 0 запуск 566, 568 0 идентификатор 570 0 уровень изоляции 553—555 Транслитерация 212 Триггер 131,231, 255, 336 338. 345, 371, 408, 417, 419, 433, 449, 526, 588, 595, 605, 615, 625, 667, 700, 747 0 изменение 687 0 использование 676—678, 686 0 последовательность событий 668 0 событие 667 0 создание 669 0 удаление 689 0 фаза 667 Тупиковая ситуация 564 У Уникальный ключ 250 Условия поиска 446 Утверждение 422
1104 Предметный указатель Ф Фазы событий, до, после 418 Файл HOSTS 73 Файл services 81 Файл конфигурации Firebird 767, 795 О параметр cpu affinity 771 О параметр CpuAffinityMask 771 О параметр CreatelnternalWindow 776 О параметр database_ cache_pages 772 О параметр DatabaseAccess 769 О параметр DeadThreadsCollection 776 О параметр DefaultDbCachePages 772 О параметр EventMemSize 772 О параметр extemal_file_directory 798 О параметр external- function_directory 796 О параметр ExtemalFileAccess 797 О параметр GuardianOption 777 О параметр IpcMapSize 777 О параметр IpcName 777 О параметр MaxUnflushedWrites 778 О параметр MaxUnflushedWriteTime 778 О параметр PriorityBoost 779 О параметр PrioritySwitchDelay 778 О параметр ProcessPriorityLevel 779 О параметр RemoteFileOpenAbility 775 О параметр RemotePipeName 779 О параметр RootDirectory 768 О параметр server_priority_class 779 О параметр SortMemBlockSize 772 О параметр SortMemUpperLimit 773 О параметр tcpnonagle 776 О параметр TempDirectories 770 0 параметр tmp_directory 770 0 параметр UDFAccess 796 0 параметр коммуникации 773 0 параметр совместимости 780 Файл определения данных 258 Фильтр BLOB 222, 792—794 Функция: О CASEQ442 0 CASTQ 159, 160, 189, 192, 195—197, 429, 451, 649 0 COALESCEO 444 0 COUNT0 456, 509 0 EXTRACTQ 196, 197, 199, 452 О GENJDQ455 О NULLIFQ445 О SUBSTR1NGO 453 0 UPPER()454 0 внешняя, UDF 456, 458—461 0 внутренняя 450 О определенная пользователем, UDF 132, 450, 456, 785 X Хранимая процедура 131, 231,255, 413, 588, 594, 625, 629, 747 О вложенная 648 0 выбора 530, 618, 629, 642, 647 0 выполняемая 629, 633, 638 О изменение 656 О локальные переменные 631 0 создание 630 О удаление 660 ц Целостность данных 535 э Эквисоедииение 470 Я Язык: 0 запросов 382 0 манипулирования данными 382, 383 0 определения данных 382, 383