- •Глава 13. Семантическое моделирование
- •Часть III Проектирование базы данных
- •Часть IV
- •14.1. Введение
- •14.2. Транзакции
- •14.3. Восстановление транзакции
- •14.4. Восстановление системы
- •14.5. Восстановление носителей
- •14.6. Двухфазная фиксация
- •14.7. Поддержка языка sql
- •14.8. Резюме
- •15.1. Введение
- •15.2. Три проблемы параллельности
- •15.3. Блокировка
- •15.4. Устранение трех проблем параллельности
- •15.5. Взаимная блокировка
- •15.6. Упорядочиваемость
- •15.7. Уровни изоляции
- •15.8. Блокировка намерения
- •15.9. Средства языка sql
- •15.10. Резюме
- •Часть V
- •16.1. Введение
- •16.2. Избирательная схема управления доступом
- •16.3. Мандатная схема управления доступом
- •16.4. Статистические базы данных
- •16.5. Шифрование данных
- •16.6. Средства языка sql
- •16.7. Резюме
- •17.1. Введение
- •17.2. Пример выполнения оптимизации
- •17.3. Оптимизация запросов
- •17.4. Преобразование выражений
- •17.5. Статистические показатели базы данных
- •17.6. Стратегия по принципу "разделяй и властвуй"
- •17.7. Реализация реляционных операторов
- •17.8. Резюме
- •18.1. Введение
- •18.2. Обзор концепции трехзначной логики
- •18.3. Некоторые следствия изложенной схемы
- •18.4. Отсутствующие значения и ключи
- •18.5. Внешнее соединение
- •18.6. Специальные значения
- •18.7. Поддержка неопределенных значений в языке sql
- •18.8. Резюме
- •Глава 19
- •19.1. Введение
- •19.2. Иерархия типов
- •19.3. Полиморфизм и заменимость
- •19.4. Переменные и операция присвоения
- •19.5. Специализация по ограничениям
- •19.6. Операции сравнения
- •19.7. Операторы, версии и сигнатуры
- •19.8. Является ли окружность эллипсом
- •19.9. Пересмотр специализации ограничением
- •19.10. Резюме
- •20.1. Введение
- •20.2. Предварительные сведения
- •20.3. Двенадцать основных целей
- •1. Локальная независимость
- •2. Отсутствие опоры на центральный узел
- •3. Непрерывное функционирование
- •4. Независимость от расположения
- •5. Независимость от фрагментации
- •6. Независимость от репликации
- •7. Обработка распределенных запросов
- •8. Управление распределенными транзакциями
- •9. Аппаратная независимость
- •10. Независимость от операционной системы
- •11. Независимость от сети
- •12. Независимость от типа субд
- •20.4. Проблемы распределенных систем
- •Транзакция т1х
- •20.5. Системы "клиент/сервер"
- •20.6. Независимость от субд
19.4. Переменные и операция присвоения
Предположим, что имеются две переменные, Е и С, с объявленными типами ELLIPSE и CIRCLE соответственно.
VAR Е ELLIPSE ; VAR С CIRCLE ;
Сначала проинициализируем переменную С значением некоторой окружности; ска- жем для определенности, окружности с радиусом 3 и центром в начале координат.
С := CIRCLE ( LENGTH ( 3.0 ), POINT ( 0.0, 0.0 )) ;
В правой части оператора присвоения вызывается операция выбора типа CIRCLE. (Напомним, что, как указывалось в главе 5, для каждого объявленного возможного пред- ставления имеется соответствующий оператор выбора с тем же именем и с параметрами, соответствующими компонентам рассматриваемого возможного представления. Опера- тор выбора служит для того, чтобы пользователь мог указать или "выбрать" некоторое значение заданного типа, предоставляя некоторое значение для каждого компонента рас- сматриваемого возможного представления.)
Теперь рассмотрим следующее присвоение.
Е := С ;
Обычно при отсутствии подтипов и наследования операция присвоения требует, чтобы переменная, указанная слева в операции присвоения, и значение, указанное в операции при- своения выражением справа, были одного и того же типа (т.е. одного и того же объявленного типа в случае переменной). Однако из принципа заменимости значения следует, что там, где система предполагает некоторое значение типа ELLIPSE, его всегда можно заменить значени- ем типа CIRCLE, так что показанный выше оператор присвоения является допустимым (фактически это присвоение представляет собой полиморфный оператор). В результате вы- полнения этого оператора окружность из переменной С будет скопирована в переменную Е и, в частности, значение переменной Е после присвоения будет относиться к типу CIRCLE, а не просто к типу ELLIPSE. Иными словами, можно сделать следующие утверждения.
■ Значения сохраняют свой наиболее конкретный тип при их присвоении пере- менным, объявленным с менее конкретным типом. Преобразование типа при та- ком присвоении не происходит (в нашем примере окружность не становится "просто эллипсом"). Отметим, что для нас были бы нежелательны такие преобразования, по- скольку это привело бы к потере наиболее характерного поведения значения и в на- шем случае, например, могло бы означать, что после присвоения будет невозможно получить радиус окружности, сохраненной как значение переменной Е.
Замечание. Ниже, в подразделе "Оператор TREAT DOWN", обсуждается проце- дура получения этого радиуса.
■ Из свойства заменимости следует, что переменная объявленного типа Т может иметь значение, конкретный тип которого будет являться любым подтипом типа Т. Поэтому нужно строго различать объявленный тип данной переменной и ее реальный тип, т.е., говоря точнее, конкретный тип (текущее значение). В сле- дующем подразделе мы возвратимся к этому важному вопросу.
Чтобы продолжить рассмотрение примера, предположим, что у нас есть и другая пе- ременная А с объявленным типом AREA.
VAR A AREA ;
Рассмотрим приведенное ниже присвоение. А := AREA ( Е ) ;
В данном случае произойдет следующее.
Во-первых, во время компиляции система выполнит проверку типа в выражении AREA(E). Проверка даст положительный результат, поскольку переменная Е имеет объявленный тип ELLIPSE и единственный параметр оператора AREA также имеет объявленный тип ELLIPSE (см. раздел 19.2).
Во-вторых, во время выполнения система обнаружит, что текущий конкретный тип для переменной Е — CIRCLE, и поэтому вызовет ту версию оператора AREA, ко- торая используется для работы с окружностями (иными словами, система осуще- ствит связывание во время выполнения, что обсуждалось в предыдущем разделе).
Конечно, тот факт, что в данном случае при выполнении оператора AREA вызывается версия, применяемая для окружностей, а не для эллипсов, должен быть скрыт от пользо- вателя (для пользователя, повторим, существует единственный оператор AREA).
Скалярные переменные
Мы уже видели, что текущее значение v скалярной переменной V с объявленным ти- пом Т может относиться к любому подтипу типа Т, равно как и к самому данному типу. Поэтому можно представить модель V в виде упорядоченной тройки вида <DT,MST,v>. Указанные параметры имеют следующий смысл.
DT — объявленный тип переменной V.
MST - текущий конкретный тип переменной V.
■ v - значение конкретного типа MST, а именно — текущее значение переменной V. Мы используем обозначения DT(V), MST(V) и v(V) соответственно для компонентов
DT, MST и v модели скалярной переменной V. Отметим, что MST(V), конечно же, всегда является подтипом, хотя и необязательно действительным подтипом наподобие DT(V); в общем случае MST(V) и v(V) изменяются во времени; в действительности MST(V) следует из v( V), поскольку каждое значение относится ровно к одному конкретному типу.
Эта модель скалярной переменной полезна, когда необходимо придерживаться точ- ной семантики различных операций, включая, в частности, операции присвоения. Одна- ко, прежде чем разбираться в этом вопросе, необходимо показать, как можно расширить обозначения объявленного типа и текущего конкретного типа для применения к произ- вольному выражению и, в частности, к скалярным переменным. Пусть X является таким выражением. Тогда получим следующее.
■ Выражение X имеет объявленный тип, DT(X) (точнее говоря, его имеет результат вычисления выражения X), производный очевидным образом от объявленных типов операндов выражения X (включая объявленные типы результатов любых вызовов операторов, содержащихся в выражении X) и известный во время компиляции.
■ Выражение X также имеет текущий конкретный тип, MST(X) (точнее говоря, его имеет результат вычисления выражения X), производный очевидным образом от текущих значений операндов выражения X (включая текущие значения результа- тов любых вызовов операторов, содержащихся в выражении X) и в общем случае неизвестный до времени выполнения.
Теперь можно точно описать выполнение операции присвоения, Рассмотрим сле- дующую операцию.
V := X ;
Здесь V— скалярная переменная, а X— скалярное выражение. Тип DT(X) должен быть подтипом типа DT(V), иначе присвоение недопустимо (эта проверка выполняется во время компиляции). Если присвоение допустимо, то в результате тип MST(V) станет рав- ным MST(X), а тип v(V) станет равным v(X).
По ходу заметим, что если текущий конкретный тип переменной V — это тип Т, то каждый действительный супертип типа Т является также "текущим типом" переменной V. Например, если переменная Е (объявленного типа ELLIPSE) имеет текущее значение, от- носящееся к конкретному типу CIRCLE, то типы CIRCLE, ELLIPSE и PLANE_FIGDRE все яв- ляются "текущими типами" переменной Е. Однако выражение "текущий тип X" обычно применяют, подразумевая, по крайней мере неформально, именно типМЭТ(Х).
Пересмотр понятия заменимости
Рассмотрим следующее определение оператора.
OPERATOR COPY ( Е ELLIPSE ) RETURNS ( ELLIPSE ) ;
RETURNS { E ) ; END OPERATOR ;
Благодаря свойству заменимости оператор COPY может, очевидно, быть вызван с ар- гументом конкретного типа (либо ELLIPSE, либо CIRCLE) и возвратит результат, относя- щийся к тому же конкретному типу. Отсюда следует, что понятие заменимости имеет еще одно следствие: если оператор Ор определен и его результат имеет объявленный тип Т, то реальный результат выполнения оператора Ор может относиться к любому подтипу типа Т (в общем случае). Другими словами, как ссылка на переменную объяв- ленного типа Т может в действительности обозначать некоторое значение любого под- типа типа Т (в общем случае), так и вызов оператора с объявленным типом результата Т может фактически возвращать некоторое значение любого подтипа типа Т (опять-таки, в общем случае).
Оператор TREAT DOWN
Еще раз рассмотрим пример, приведенный в начале данного раздела.
VAR Е ELLIPSE ; VAR С CIRCLE ;
С := CIRCLE ( LENGTH ( 3.0 ), POINT ( 0.0, 0.0 ) ) ; E := С ;
Здесь тип MST(E) — CIRCLE, Предположим, что требуется получить радиус данной ок- ружности и присвоить его некоторой переменной L. Можно попытаться сделать это так.
VAR L LENGTH ;
L := THE_R ( Е ) ; /* Ошибка времени компиляции - недопустимый тип!!! */
Однако, как явствует из комментария, этот код во время компиляции выдает сообщение об ошибке о несоответствии типов. Точнее говоря, ошибка возникает из-за того, что опера- тор THE R ("радиус") в правой части оператора присвоения требует, чтобы его аргумент от- носился к типу CIRCLE, а объявленный тип его аргумента Е — ELLIPSE, а не CIRCLE.
Замечание. Если бы проверка во время компиляции не выполнялась и текущее значе- ние Е во время выполнения оказалось бы эллипсом, а не окружностью, возникла бы ошибка времени выполнения, что еще хуже. В нашем случае, безусловно, заранее извест- но, что значение во время выполнения будет окружностью, и проблема состоит лишь в том, что мы знаем об этом, а компилятор — нет.
Чтобы решить эту проблему, введем новый оператор, который неформально будем называть TREAT DOWN (трактовать). Теперь можно корректно обратиться к оператору THE R и получить радиус окружности.
L := THE_R { TREAT_DOWN_AS_CIRCLE ( Е ) ) ;
Выражение TREAT DOWN_AS_CIRCLE(E) задается для того, чтобы для переменной Е в данном случае был объявлен тип CIRCLE и проверка при компиляции завершилась ус- пешно. При этом во время выполнения произойдет следующее.
Если текущее значение переменной Е относится к типу CIRCLE, значит, выражение в целом корректно и возвращает радиус данной окружности. А точнее, обращение к оператору TREAT DOWN дает результат (скажем, Z) с объявленным типом DT(Z), равным CIRCLE, поскольку задано уточнение ..._AS_CIRCLE, текущим конкретным типом MST(Z), равным типу MST(E), который в нашем примере имеет значение CIRCLE, и текущим значением v(Z), равным v(E). В результате при вычислении выражения THE_R(Z) будет получен требуемый радиус (который может быть при- своен переменной L).
Однако если текущее значение переменной Е в действительности будет относиться к типу ELLIPSE, а не к типу CIRCLE, то обращение к оператору TREAT DOWN приве- дет к ошибке из-за несоответствия типа во время выполнения.
Общее назначение оператора TREAT DOWN — обеспечить, чтобы ошибки несоответ- ствия типов во время выполнения могли произойти только в рамках вызова оператора TREAT DOWN.
Замечание. Предположим, что тип CIRCLE, в свою очередь, имеет собственный под- тип (скажем, О CIRCLE, где О CIRCLE — окружность, центр которой расположен в на- чале координат).
TYPE 0_CIRCLE POSSREP ( R LENGTH ) SUBTYPE_0F ( CIRCLE )
CONSTRAINT ( THE CTR ( 0_CIRCLE ) = POINT ( 0.0, 0.0 ) ) ;
Тогда текущее значение переменной Е в данное время может иметь конкретный тип 0_CIRCLE, а не просто CIRCLE, Рассмотрим вызов оператора TREAT DOWN в этом случае.
TREAT_DOWN_AS_CIRCLE ( Е )
Его выполнение завершится нормально, и будет выдан результат (скажем, Z) с объяв- ленным типом DT(Z), равным CIRCLE, поскольку задано уточнение ... AS CIRCLE, теку- щим конкретным типом MST(Z), равным типу 0_CIRCLE, поскольку О CIRCLE — это кон- кретный тип переменной Е, и текущим значением v(Z), равным v(E). Короче говоря, оператор TREAT DOWN всегда возвращает конкретный тип; он никогда не "повышает" тип, чтобы сделать его менее конкретным, чем он был прежде.
Приведем более формальное изложение семантики вызова оператора TREAT_D0WN_AS_T (X), где X — некоторое скалярное выражение. Во-первых, тип Т должен быть подтипом типа DT(X) (это проверяется во время компиляции). Во-вторых, конкрет- ный тип MST (X) должен быть подтипом типа Т (это проверяется во время выполнения). Если данные условия удовлетворяются, то после обращения к оператору возвращается результат Z с типом DT(Z), равным Т, типом MSTJZ), равным типу MST(X), и текущим значением v (Z), равным v (X).
Замечание. В [3.3] также определяется общая форма оператора TREAT DOWN, которая допускает, чтобы один операнд "трактовался" как имеющий тип другого операнда вме- сто некоторого явно указанного конкретного типа.