- Типы и таблицы
- Ссылочные типы и идентификаторы объектов
- Шаблоны типов, коллекции и выделенные типы
- Атрибуты
Статья рассматривает эволюционный подход Oracle к созданию объектной СУБД. В ней показывается, как теоретико-множественные основы реляционной модели эффективно сочетаются с современной системой типов при создании совместимых расширений SQL в ANSI/ISO SQLЗ.Типы и подтипы могут определяться иерархически с множественным наследованием, инкапсуляцией, методами и полиморфизмом. "Объекты" (то есть экземпляры этих типов и подтипов) подобны обобщенным строкам, собранным в таблицы и подтаблицы в соответствующих иерархиях. Иерархии таблиц могут быть вида "многие-к-одному" с иерархией типов в каждом узле. Тогда возможно расширение языка манипулирования данными SQL для применения его к таблицам объектов, а процедурный язык, подобный PL/SQL, может быть дополнен возможностями определения и манипулирования объектами.
Введение
Сегодня достоинства концепции объектного подхода в языках программирования в отрасли разработки программного обеспечения, в целом, получили широкое признание. Многие пользователи Oracle все чаще обращаются к объектной технологии при разработке нового ПО. Зачастую такие пользователи уже создали объектные слои над реляционной СУБД, но в дальнейшем они хотят использовать решения разработчика этой СУБД и стандартные интерфейсы, а не заниматься самостоятельными улучшениями и их сопровождением.
Основными критериями для оценки СУБД, с точки зрения поддержки объектной технологии, являются, по-видимому, следующие:
1. Выбор правильного стратегического решения, скажем, на 1995 год и далее, в смысле стабильности системных интерфейсов (предпочтительнее стандартизованные сами по себе и обязательно хорошо согласующиеся с другими стандартами в мире "распределенных объектов") и продолжения взаимоотношений с поставщиком СУБД, который будет предоставлять промышленные системы, с отличной поддержкой и быстрой реакцией на новые требования.
2. Поиск эффективного пути усовершенствования процесса построения новых и развития существующих приложений на ближайший период.
Ядром объектного подхода является система типов (или классов). Но как СУБД должна поддерживать эту систему? Существует, по крайней мере, три очевидных подхода: реализовать напрямую систему типов какого-нибудь конкретного языка, например, С++, или спроектировать новую систему объектных типов, как, скажем, в 02, или расширить систему типов реляционной модели. Первые два привлекательны, когда не требуется значительного взаимодействия с другими языками или использование/расширение данных в реляционных системах. Но даже в этом случае совсем непросто, начав все сначала, построить промышленные системы, которые должны превосходить сегодняшние реляционные системы, поддерживая более богатые объектные модели, и, тем не менее, обеспечивать надежность, наращиваемость и многие другие прагматические характеристики, требуемые от серьезной СУБД. Поэтому эволюционный подход обобщения реляционной модели заслуживает тщательного рассмотрения. Помимо появления все новых интерфейсов и инструментария, он открывает возможность построения систем на базе существующих приложений и реализаций СУБД, а также улучшения множества уже разработанных языковых интерфейсов и инструментария. Центральное звено в таком расширении реляционной модели, даже если конечный пользователь явно и не замечает, - это расширенный SQL.
Обзор SQL3
Самому понятию "объектного SQL" несколько лет, и названия, подобные этому, давались разнообразным частным диалектам, но ни один из них не стал окончательным. Однако существует общедоступная разработка такого рода. Комитеты ANSI и ISO по языку SQL, завершая свою работу по обновлению реляционного стандарта, неформально известного как SQL2, с декабря 1990 года разрабатывают объектные расширения в рамках SQL3.
Особую активность в этом вопросе проявил комитет ANSI по языку SQL (Oracle - среди главных участников комитета), и в результате был достигнут быстрый прогресс в определении системы типов с множественным наследованием, инкапсуляцией, методами и полиморфизмом. Один из способов написания тела метода доступен в самом SQL3 путем использования процедурных расширений, аналогичных PL/SQL. Язык манипулирования данными SQL может быть применен затем к таблицам объектов, то есть к таблицам, в которых каждая "строка" есть объект.
Хотя SQL3 содержит ряд расширений, по существу остающихся реляционными, большая часть работы над SQL3 за последние два года была посвящена объектным расширениям. Эти расширения полностью совместимы с реляционным языком и включают реляционную модель, а не являются наложением объектной модели. Тип реляционного кортежа (строки) в первой нормальной форме является частным случаем объектного типа, так же как структуры С являются частным случаем классов С++. Чтобы подчеркнуть, что модель SQL3 содержит реляционную модель внутри себя как частный случай, ее можно назвать надреляционной (circumrelational).
Поскольку система объектных типов требует определения функций, то в SQL3 были добавлены процедурные расширения как средство, хотя и не единственное, описания тел функций. Таким образом, SQL3 больше не является лишь подъязыком баз данных - это вычислительно полный язык программирования, с акцентом на приложения баз данных. Не требуется, чтобы он был универсальным языком; более важное требование - быть очень удобным инструментом реализации процедур и функций баз данных при определении семантики данных в БД.
Версия SQL2 была завершена и формально одобрена как ISO, так и ANSI, в 1992 году и стала поэтому называться SQL-92. Предыдущими версиями стандарта были SQL-86 и SQL-89, так что если будет выдержан такой же темп, то SQLЗ может стать SQL-95. Конечно, возможности, особенно необходимые пользователям, скорее всего, будут реализованы до формальной стандартизации, как это уже случилось с триггерами и хранимыми процедурами, которые не были включены в SQL-92, но имеют предварительные определения в SQL3.
Еще одна причина, настоятельно требующая стабилизировать существенные черты SQL3, - активный международный интерес к разработке библиотеки типов SQL3 с определениями типов для различных функциональных областей. Объекты могут храниться в БД наряду с функциями, определенными над ними, и будут доступны затем приложениям, написанным на разных языках программирования. (SQL-92 определяет 7 способов связывания со стандартными языками программирования, и при разработке SQL3 преследуется цель усилить эту возможность эффективной работы со многими языками. При этом используются преимущества подобия системы типов SQL3 и системы типов языков программирования, что позволяет определить высокоуровневые способы связывания, включающие объекты целиком, которые, в свою очередь, могут быть совокупностями объектов.) В появляющемся новом проекте ISO - SQL MultiMedia - уже есть предложение, чтобы текстовый тип SQL3 обладал свойствами полноэкранной текстовой области (Full Text area), и ожидается, что скоро появятся определения типов SQL3 для областей пространственных данных (Spatial Data area).
Главная черта объектной модели, по сравнению с реляционной, - это богатство ее системы типов. Обладая ограниченным набором предопределенных базовых типов, реляционная модель вводит строгую (и часто здравую) дисциплину, позволяя конструировать из этих типов только один вид составной сущности - а именно таблицу, совокупность строк в первой нормальной форме, сформированную из элементов базовых типов (или простых доменов, определенных над базовыми типами). Строка часто представляет объект реального мира, например, сотрудника или отдел, и, на самом деле, является простым случаем объекта в смысле современного ПО.
Типы и таблицы
Поскольку в SQL3 система типов должна быть обогащена, а тип объекта (строки) неявно присутствует в определении реляционной таблицы, то хорошим началом было бы предоставление возможности определять поименованный тип отдельно от таблицы.
Create type Address ( number char (6), street char (30), aptno integer, city char (30), state char (2), zip integer ); Create table Addresses of Address;
Это эквивалентно связанной форме:
Create table Addresses of new type Address ( number char(6), street char(30), aptno integer, city char (30), state char(2), zip integer );
Фраза "of new type" просто позволяет специфицировать имя типа в связанной форме. Тип определяет горизонтальную размерность одиночного объекта, а таблица добавляет затем вертикальную размерность совокупности таких объектов. Таким образом, "атрибут" типа соответствует столбцу таблицы.
Именование типа позволяет создавать другие таблицы того же самого типа:
Create table HomeAddresses of Address;
или использовать тип как тип данных столбца:
reate table People of new type Person ( name char (30), address Address, birthdate date );
Тип Person - первый нереляционный пример, так как у него есть атрибут составного (composite) типа (по любому разумному определению "составного типа"). Теперь легко видеть, как расширить систему типов возможностями, необходимыми для нее, чтобы называться объектной системой:
Create table People of new type Person ( name char(30), address Address, function age ( :р ref(Person)) return interval day; return ; function set age(:р ref(Person), :d interval day) returns ref(Person); begin :р.birthdate:=current_date - :d; return :p; end; private birthdate date );
Некоторые (или все) атрибуты могут быть инкапсулированы подобно атрибуту birthdate из предыдущего примера, если им предшествует слово private. Это означает, что birthdate напрямую доступен только внутри определения типа. (Вспомогательные функции, вызываемые только из функций типа, могут быть также инкапсулированы с помощью спецификации их как private). Как и в С++, имеется в наличии спецификатор protected - он позволяет осуществлять доступ и из определения типа, и из определений подтипов (производных типов).
Теги private или public могут предшествовать любому атрибуту или функции определения типа и действуют до следующего тега. По умолчанию действует тег public. Таким образом, определение реляционной таблицы специфицирует тип, в котором все атрибуты имеют спецификацию public и принципиально общедоступны (accessible in principle). Для того чтобы определить, может ли данный пользователь иметь доступ к принципиально общедоступному атрибуту или вызвать принципиально общедоступную функцию, используется авторизация БД.
Правила инкапсуляции выполняются не только в процедурном языке, но также в операторах SQL:
Select birthdate from People; / * неверно, если только не внутри */ / * определения тина Person */
Аналогия определения типа в SQL3 и определения класса в С++ очевидна и преднамеренна, поскольку взаимное соответствие между этими языками имеет, вероятно, фундаментальную важность для будущего обработки информации.
В SQLЗ вместо слова class используется слово type, а первое в области баз данных переопределяется и обозначает там тип, или совокупность экземпляров типа, или и то и другое. Фактически, эти определяемые пользователем типы часто называют абстрактными типами данных (Abstract Data Types), хотя они выходят за пределы базовых концепций в поддержке наследования и полиморфизма.
Наследование определяется с помощью фразы under.
Create type Employee under Person ( emp no char(10), dept ref(Department) );
Поддерживается множественное наследование, но в рамках консервативного подхода, считающегося разумным в схемах баз данных, - конфликты имен должны быть разрешены путем переименования наследуемых элементов, осуществляемого во фразе under. Доступен динамический полиморфизм как часть правила совместного использования (overloading), унифицирующих конкретизацию в иерархии типов с совместно используемым именем процедуры (функции). Дальнейшее обсуждение этих возможностей здесь опущено, поскольку они не так существенны в последующем контексте.
Ссылочные типы и идентификаторы объектов
Ссылочные значения - это указатели, на которые налагаются некоторые ограничения, работающие весьма схожим с внешними ключами образом. К ним применима проверка ограничений целостности, когда они используются для хранимых объектов (persistent objects). Объекты обладают системными идентификаторами, выступающими в качестве первичных ключей (если пользователь явно не указал первичный ключ в определении типа).
Концептуально, у экземпляра типа есть идентификатор объекта, который единственным образом определяет его, но не является частью его значения, - что-то похожее на неизменный идентификатор строки. Формально, экземпляр - это пара:
где только часть value используется в сравнениях и присваиваниях.
Значение oid может быть извлечено подобно значению столбца (но не может быть изменено):
Select ha.oid from HomeAddresses ha where ?;
Заметим, что Select * возвращает только часть value строки (совместимо и полезно!).
Значение oid генерируется неявно при создании экземпляра, например:
Insert into HomeAddresses values(?) alias :chezvous;
Опция alias позволяет связать символ и oid для дальнейшего использования:
Update HomeAddresses set zip=94022 where oid=:chezvous;
Ссылочный тип использует ключевое слово ref и всегда уточняется конкретным типом, на который он ссылается, например:
ref(Person)
Нельзя использовать эту конструкцию в определении первичного ключа, но, как мы уже видели, она используется в определениях внешних ключей:
Create type Person ( name char (30), address Address, birthdate date, bestfriend ref(Person) );
Тип ref(Person) будет неявно присвоен атрибуту bestfriend, если мы захотим написать определение таблицы в связанной форме со ссылочным ограничением целостности:
Create table ObjectPeople of new type Person ( name char (30), address Address, birthdate date, bestfriend references ObjectPeople );
Другие ограничения целостности, например, check и unique, перешли из реляционных таблиц в объектный мир. Ограничения целостности и операции, работающие с множествами, - это два преимущества баз данных, которые SQL3 совмещает с объектными возможностями, обычными для языков программирования. При создании типа или таблицы предоставляются два способа оптимизации механизма идентификации объектов. Во-первых, если тип Т никогда не будет использован в ссылочном типе ref(T), то он может быть специфицирован как without oid. Во-вторых, если у типа значения oid будут использоваться только для ссылок внутри базы данных, то он может быть описан как with oid not visible. Такая спецификация означает, что значения oid не могут использоваться нигде, кроме БД, так что СУБД не должна генерировать значения oid которые будут действительными неопределенно долго, а может использовать что-нибудь подобное rowid. По умолчанию, типы верхнего уровня считаются with oid notvisible, а подтипы наследуют "самую сильную" спецификацию своих супертипов или могут усилить то, что они наследуют по умолчанию.
Для иллюстрации подобия ref-значений внешним ключам рассмотрим оператор:
Insert into ObjectPeoyle (name) values ('О.-J. Dahl') alias :ole johan;
Ole Johan здесь - значение типа ref(Person), и, кроме того, оно ссылается на ObjectPeople, так что это значение может быть присвоено атрибуту bestfriend.
Insert into ObjectPeople (name, bestfriend) values ('К. Nygaard', :ole johan) alias :kristen;
Тогда следующий оператор
Select op.bestfriend into :bf from ObjectPeople ор where oid=:kristen;
даст значение oid другого человека, которое иногда является тем, что нам нужно, и мы связали переменную с этим значением.
Или мы могли бы выполнить другой оператор:
Select name(bestfriend) from ObjectPeople ор where oid=:kristen;
в котором функция name неявно наименовывает значение bestfriend типа ref(Person), так что результатом является соединение таблиц (join). (SQL3 допускает использование имен атрибутов как функции, решая, таким образом, проблему роллирования окна вдоль непрерывных уточняющих точек при попытке отследить соотношения между объектами.)
Шаблоны типов, коллекции и выделенные типы
Поддерживаются шаблоны типов, с синтаксисом, аналогичным оператору Create type
Create type template Set(:Т type) ( ); Create table People of пеи type Person ( name char(30), address Set(Address), birthdate date );
Использование выражения Set(Address) создает тип из шаблона, подставляя везде в теле шаблона Address вместо :Т. Допускается использование нескольких параметров, которые не обязательно должны быть типами.
Типы-множества, подобные вышеприведенному, являются одним из обычных способов использования шаблонов типов.
Выделенные типы (distinct types) - это строго типизированные синонимы типов, например:
Create distinct tyge Kilometres as integer; Create distinct tyge Kilogrammes as integer;
Строгая типизация означает, что километры не могут быть присвоены килограммам, и наоборот.
Create distinct type Sad as Set(ADDRESS);
Возможно использование одного слова для обозначения определения типа произвольной сложности.
Атрибуты
Атрибуты могут быть либо хранимыми, либо виртуальными. Хранимые атрибуты (подобно столбцам в таблице) выполняют двойную функцию: позволяют использовать более лаконичную нотацию, чем пары функций get и set, и определяют как интерфейс, так и представление. Чтобы пользоваться лаконичной нотацией и, тем не менее, не утратить инкапсудяцию (независимость интерфейса от представления), SQL3 вводит механизм виртуальных (virtual) атрибутов. Нотация теперь не имеет отношения к представлению; на пользователей интерфейса изменения представления не влияют. Например:
Create table People of new type Person ( name char(30), address Address, age virtual, function age(:р ref(Person)) return interval day; return _ ; function set_age(:р ref(Person), :d interval day) returns ref(Person); begin :p.birthdate:=current_date - :d; return :p; end; private birthdate date );
Здесь используются значения по умолчанию. Полностью мы могли бы написать:
age updatable virtual get with foo set with bar,
где foo и bar - произвольные функции, определенные внутри типа с соответствующими типами параметров и возвращаемого значения. Можно определить read only virtual (виртуальный, только для чтения) атрибут, у которого есть только функция get with.
Операторы вставки (Insert) и обновления (Update) могут устанавливать как хранимые, так и изменяемые виртуальные атрибуты:
Insert into ObjectPeople (name, age) values ('В. Myhraug', current_date - date'1932-02-29') alias :bjorn; Update ObjectPeople р set name='B. Myhraug', age=current date - date '1932-03-29' where oid=:bjorn;
Заметим, что введение виртуальных атрибутов не нарушает инкапсуляции. Конечно, можно было бы сделать все атрибуты закрытыми (private) и пользоваться лаконичной нотацией только внутри тел методов. Но теперь "атрибутная" нотация может быть использована в общедоступном интерфейсе независимо от представления.
То же самое остается верным и для столбцов таблиц: часть столбцов может быть открыта, а часть закрыта; аналогично, одни столбцы могут быть хранимыми, а другие - виртуальными. Между реляционной моделью и инкапсуляцией нет противоречия - реляционная модель лишь не поддерживает инкапсуляцию для базовых таблиц, в них все столбцы хранимые и изменяемые. Заметьте, однако, что представления дают частичную поддержку инкапсуляции. Столбцовый способ записи может быть использован, не только когда столбец имеет хранимое представление (и, таким образом, является "изменяемым"), но и в противном случае (когда столбец является столбцом "только для чтения").
Заключение
Эволюционный подход к объектным базам данных выглядит привлекательно. В его пользу говорят такие неопровержимые аргументы, как преемственность развития (даже более сильные, чем в случае эволюции от С к С++ в области языков программирования). Действительно, серьезных проблем при ответе на вопросы, которые мы сейчас рассмотрим, не возникает.
1. Возможно ли аккуратное расширение SQL, или результат будет значительно хуже того, что мог бы быть получен при расширении какого-нибудь другого языка или при разработке нового языка?
Кажется, что SQL3 дает обнадеживающий ответ. Расширениям, проиллюстрированным выше, мало вредит, если вредит вообще, решение принять дисциплину совместимости со SQL2, и они выигрывают от существующих возможностей SQL, таких как запросы, ограничения целостности и авторизация. Простота реляционной модели, а также простота ее выражения средствами SQL, наложили слабые ограничения на проектирование расширений - SQL задает более легкие стартовые условия, чем, например, С, - в то время как попытки определить язык запросов в рамках расширений языков программирования не дали выдающихся результатов, а разработка новых языков таит непредвиденные проблемы.
2. Могут ли быть определены удовлетворительные механизмы связывания с языками программирования, чтобы более не существовала неприемлемая "абсолютная нестыковка" между интерфейсами языков программирования и систем баз данных?
Работа над детальными механизмами связывания с языками еще не закончена, так что ответ на этот вопрос пока еще не окончательный. Однако мы ожидаем, что результат будет успешным. Гораздо большее подобие между системами типов баз данных и языков программирования делает возможным соотнесение и перенос объектов целиком (а не простых элементарных значений), а также описание операций над объектами одинаковым образом, независимо оттого, являются объекты временными (transient) или хранимыми (persistent). Языки с возможностями совместного использования (overloading), например, С++, могут использовать это для осуществления прозрачности при присваивании или ссылке на временные или хранимые объекты. Так или иначе, маловероятно, что весь мир вдруг начнет использовать один единственный язык, так что соотнесение между объектами все же необходимо. В SQL уже определены соотнесения между стандартными элементарными типами, и они дополняются, чтобы обеспечить контроль над соотнесением объектных типов, сконструированных из элементарных. Как только соотнесения определенных пользователем типов установлены, они становятся прозрачными при использовании.
3. Смогут ли реализации реляционных систем баз данных успешно эволюционировать так, чтобы справиться с гораздо более мощной моделью данных?
После 3 лет исследований и разработок мы убеждены, что наиболее эффективный для нас путь достижения цели - промышленная объектная СУБД - состоит в эволюции того, что у нас есть, а не в разработке новой реализации. Система Oracle уже развивалась в течение многих лет и изменилась радикальным образом, но не пожертвовала главными чертами, которые не нужно заново реализовывать в каждой новой версии. Для того чтобы поддерживать объекты понятно и эффективно, некоторые части системы должны быть существенно изменены или расширены, в то время как другие, хорооттестированные и испытанные в промышленном использовании, могут быть перенесены с незначительными изменениями. Сделать это в соответствии с теми высокими стандартами качества, которые мы сами перед собой ставим, нелегкая задача, но мы убеждены, что это правильный подход в установлении жизнеспособности повсеместного промышленного использования технологии объектных баз данных.
* D. Beech. Object Data Bases Directions. Статья печатается с разрешения автора.