Развитие идей и приложений реляционной СУБД System R

         

Трансляция запросов и поддержка времени выполнения


Конечно, главным компонентом System R, поддерживающим выполнение запросов к базам данных, является подсистема управления памятью RSS. В ней осуществляется непосредственная выборка и запись данных на внешнюю память, она ведает вопросами синхронизации доступа к объектам баз данных и осуществляет необходимые действия по обеспечению надежности баз данных. Однако, RSS - это всего лишь поддержка System R на нижнем, "физическом" уровне. Подсистема RSS обладает интерфейсом, который, во-первых, не является реляционным, а во-вторых, он вообще не является интерфейсом, доступным пользователю (например, в нем отсутствуют имена объектов). System R становится полной системой управления реляционными базами данных только при наличии других компонентов, позволяющих взаимодействовать с базами данных на базовом языке этой системы - SQL.

Как мы уже отмечали во введении, разработчики System R выбрали при реализации SQL подход, основанный на компиляции предложений этого языка в коды объектной ЭВМ (IBM/370). Это означает, что при непосредственном выполнении запроса выполняется программа, содержащая только программный код, необходимый для выполнения этого запроса (включающий, конечно, обращения к RSS), и ничего лишнего. Как неоднократно утверждают разработчики (например, в [24-27]), они пошли на этот шаг в стремлении повысить эффективность выполнения запросов за счет прямого выполнения программного кода, не содержащего лишних команд. Утверждается, что даже в случае интерактивной работы, когда откомпилированный запрос будет заведомо выполняться только один раз, накладные расходы на генерацию машинного кода окупаются эффективностью рабочей программы. Это подтверждается приводимыми в публикациях экспериментальными данными.

Тем не менее, существует альтернативный подход, при котором компиляция запросов, конечно, тоже производится, но генерируется не объектный код, а некоторое процедурное внутреннее представление запроса на промежуточном языке, которое затем интерпретируется при выполнении запроса.
Такой подход применяется, например, в широко известной реляционной СУБД INGRES [72]. Как нам кажется, кроме отмечаемой разработчиками System R увеличивающейся эффективности выполнения запроса, при выборе их подхода существенны и другие обстоятельства. Во-первых, System R - не мобильная система (в отличие от INGRES). Она ориентирована исключительно на ЭВМ семейства IBM/370 и поэтому перед ее разработчиками не стояла проблема переписывания генератора кодов при переносе системы на ЭВМ с другой системой команд. Во-вторых, в силу особенностей операционных систем IBM перед разработчиками не стояла проблема динамической загрузки программы в память задачи во время ее выполнения. Все операционные системы IBM поддерживают операцию "загрузи и выполни". Поэтому нет проблем с выполнением динамически компилируемых запросов. Наконец, в-третьих, генератор кодов компилятора SQL в System R достаточно прост. Как мы покажем ниже, его удалось реализовать в виде "сборщика" рабочей программы из сравнительно небольшого числа ранее подготовленных программных фрагментов. Заметим еще по этому поводу, что и в System R не сразу появилась компиляция предложений SQL в коды машины. В начальных версиях системы использовалась интерпретация, и только накопленный опыт позволил выработать окончательный подход. На уровне пользователя System R допускает два вида интерфейса (оба на основе SQL). Первый вид интерфейса основан на использовании традиционного языка программирования (основным таким языком является традиционный язык фирмы IBM PL/1), в который разрешается включать предложения SQL, соответствующим образом помеченные. Язык SQL содержит средства, позволяющие параметризовать запросы значениями переменных объемлящей программы и получать результаты выполнения запросов в переменных этой программы. Для "стыковки" языка SQL, результатами выполнения запросов которого являются отношения, т.е. множества кортежей, с традиционным языком программирования в SQL введены средства последовательного покортежного просмотра результирующих отношений (аппарат курсоров).


После обработки текста программы прекомпилятором SQL и компилятором PL/1 производится набор объектных модулей, компоновка которых дает рабочую программу, при выполнении которой будут выполнены и те запросы, которые встретятся в потоке управления программы. Это очень грубая схема, которая тем не менее соответствует представлениям пользователей. Далее мы детализируем ее. Второй вид интерфейса, поддерживаемого System R, - интерактивный. При работе с этим интерфейсом пользователи используют базовое "реляционное" подмножество SQL, в котором запрещено использование средств, предназначенных для стыковки с традиционным языком программирования (в частности, запрещено использование аппарата курсоров). Результатами выполнения запросов пользователей являются таблицы-отношения, которые и отображаются на экранах терминалов. Естественно, что средства интерактивного взаимодействия включают редактор, облегчающий формулирование запросов, и компонент, позволяющий описывать - 58 форматы таблиц при изображении их на экране. Нас эти особенности в этой статье занимать не будут. Здесь для нас более интересно то, что интерактивный интерфейс реализуется на основе использования интерфейса первого рода. На самом деле, программа, обеспечивающая интерактивный интерфейс с System R, строится как и любая другая программа, включающая предложения SQL. Возможности обеспечения интерактивного режима, когда заранее неизвестен набор запросов, которые поступят с терминала, обеспечиваются двумя операторами SQL, позволяющими динамически откомпилировать и выполнить запрос. Параметром предложения SQL PREPARE является текстовая строка, которая должна содержать текст предложения SQL, допустимый для использования в интерактивном режиме. Оператор PREPARE - выполняемый. При его выполнении производится компиляция задаваемого предложения SQL, и после его выполнения можно выполнить динамически появляющуюся рабочую программу выполнения запроса с помощью оператора EXECUTE. (Мы опускаем некоторые детали; например, после выполнения оператора PREPARE можно узнать тип компилировавшегося запроса, т.е.




допустимо ли по отношению к его результату использовать курсор.) Таким образом, базовым интерфейсом System R следует считать интерфейс первого типа, когда предложения SQL встраиваются в программу на традиционном языке программирования. С использованием этого базового интерфейса можно создать множество различных прикладных систем, в том числе, и интерактивных. Так что средство интерактивного взаимодействие с использованием базового диалекта SQL - это лишь пример одной из возможных прикладных программ. В коммерческих развитиях System R DB2 и SQL/DS таких интерфейсных подсистем гораздо больше. Например, на тех же принципах реализован интерфейс Query-By-Ecsample [73]. Поэтому в дальнейшей части этого подраздела нас будет интересовать реализация в System R только базового интерфейса. Как описывется в [26], схема обработки программы на традиционном языке (PL/1, Cobol или ассемблер), включающей предложения SQL, является следующей. Программа обрабатывается прекомпилятором SQL, который просматривает исходный текст в поиске специальным образом помеченных предложений SQL. При нахождении каждого такого предложения прекомпилятор выполняет его трехфазную обработку. Для выполнения первой фазы обработки вызывается программа грамматического разбора предложений SQL. Эта программа производит синтаксический анализ полученного предложения и формирует его внутреннее древовидное представление. Кроме того, программа грамматического разбора формирует два списка имен - список имен "входных" переменных, значениями которых должен параметризоваться запрос, и список имен "выходных" переменных, в которые должны помещаться значения полей кортежей результирующего отношения запроса при его последовательном просмотре. Внутреннее представление запроса и два этих списка являются ответными параметрами программы грамматического разбора. На второй фазе, которая начинается, естественно, только при успешном завершении первой, выработанное дерево запроса передается следующему компоненту, который в System R называется оптимизатором. (Мы выделили это слово, поскольку компонент выполняет далеко не только оптимизирующие действия.) Если при выполнении грамматического разбора предложений SQL взаимодействие с базой данных не требуется, то оптимизатор активно использует данные из служебных отношений-каталогов базы данных.


Соответствующий доступ к базе данных производится с использованием операций интерфейса RSS. При первом обращении прекомпилятора к оптимизатору запускается служебная транзакция, которая завершается в конце работы прекомпилятора, т.е. при завершении обработки текста исходной программы. Как и при выполнении любой другой транзакции, в ходе выполнения этой служебной транзакции в RSS накапливаются захваты объектов базы данных и снимаются при завершении служебной транзакции. Поэтому работа прекомпилятора может производиться параллельно с выполнением любых других транзакций (в частности, параллельно могут выполняться несколько прекомпиляций программ). Работа оптимизатора выполняется в несколько шагов, причем не все шаги обязательно вовлекаются при обработке любого запроса. Первый (обязательный) шаг работы оптимизатора состоит в том, что в дереве грамматического разбора запроса, полученном при работе процедуры грамматического разбора, производится замена имен отношений и полей на их идентификаторы. Для этого используется служебное отношение-каталог описателей отношений. Как мы уже отмечали, внутренним идентификатором отношения служит tid его описателя в отношении-каталоге. Поля идентифицируются своими номерами в данном отношении. При выполнении первого шага работы оптимизатора имена некоторых отношений могут быть не найдены в каталоге. Это не рассматривается как ошибка, поскольку к моменту реального выполнения обрабатываемого запроса именованное в нем отношение может уже существовать. В случае возникновения такой ситуации обработка запроса на этом завершается, и формируется секция модуля доступа (модуль доступа является конечным продуктом прекомпиляции; на его структуре мы остановимся ниже), содержащая дерево грамматического разбора. Окончательная обработка запроса откладывается до попытки его реального выполнения. Если выполнение первого шага работы оптимизатора успешно завершается, сформированное на этом шаге модифицированное дерево грамматического разбора (дерево запроса)передается второму шагу оптимизатора.


На втором шаге оптимизатора проверяются права пользователя, от имени которого производится прекомпиляция, на выполнение действий, выражаемых запросом. Для этого используется служебное отношение-каталог описаний прав доступа зарегистрированных пользователей базы данных. Нарушение прав доступа расценивается как фатальная ошибка прекомпиляции, выполнение которой прекращается с выдачей соответствующей диагностики и без формирования какого-либо результата. Имеется класс предложений SQL, для которых процесс компиляции заканчивается на втором шаге оптимизатора, - класс "интерпретируемых" предложений. К этому классу относятся такие предложения, как CREATE TABLE, CREATE IMAGE и т.д. Выполнение такого типа предложений производится по жесткой предопределенной для каждого вида предложений схеме, и было бы расточительностью генерировать для каждого предложения рабочую программу, которая, в сущности, является стандартной подпрограммой. Такие предложения и выполняются в System R стандартными подпрограммами подсистемы поддержки времени выполнения. Прекомпилятор же для запроса этого класса формирует специальную секцию модуля доступа, которая содержит необходимую информацию для вызова соответствующей стандартной подпрограммы при выполнении запроса. При успешном завершении второго шага работы оптимизатора и при наличии потребности (т.е. если обрабатываемый запрос не относится к классу интерпретируемых) вызывается третий шаг оптимизатора с передачей ему модифицированного дерева запроса. В запросе могут употребляться имена базовых (хранимых) отношений и имена представлений базы данных. При выполнении третьего шага на основе анализа каталога отношений определяется, используются ли в данном запросе представления. Если оказывается, что используются, то для каждого представления из каталога представлений выбирается его внутренняя форма в виде дерева разбора с именами, замененными на идентификаторы, и дерево обрабатываемого запроса сливается с деревьями представлений. В результате порождается преобразованное дерево исходного запроса, в котором употребляются только идентификаторы базовых отношений.


Здесь следует сделать два замечания. Первое замечание касается обработки запросов на изменение отношений базы данных (INSERT, DELETE и UPDATE). Как и в любом другом запросе, в запросах этого вида изменения могут относиться и к базовым отношениям, и к представлениям. Но далеко не для всех представлений определена семантика операций изменения. На самом деле, при выполнении оператора SQL CREATE VIEW производится анализ создаваемого представления на предмет возможности выполнения над ним операций изменения. Мы не будем вдаваться в подробности этого анализа (это отдельная и важная тема, которой до сих пор посвящается много публикаций), но заметим, что в описателе представления помечено, разрешены ли над представлением операции изменения. Если при выполнении третьего шага оптимизатора выясняется, что в запросе на изменение отношения базы данных используется "неизменяемое" представление, то это рассматривается как фатальная ошибка прекомпиляции с соответствующими последствиями. Второе замечание относится к учету существующих в базе данных условий целостности и условных воздействий. В [26] по этому поводу ничего не говорится, но третий шаг работы оптимизатора - наиболее удобный и естественный момент для проведения соответствующих действий. Для этого необходимо воспользоваться каталогами условий целостности и условных воздействий. Если оказывается, что выполнение обрабатываемого запроса должно привести в действие выполнение некоторых условных воздействий, то естественно было бы для каждого такого условного воздействия выбрать внутренее представление собственно воздействия из его описателя и произвести слияние с деревом обрабатываемого запроса. В базе данных могут содержаться условия целостности двух типов. К первому типу относятся условия целостности, определяемые предложением SQL ASSERT с ключевым словом IMMEDIATE. В соответствии с семантикой SQL такие условия должны проверяться немедленно при совершении соответствующих действий. Если анализ показывает, что обрабатываемый запрос должен вызвать немедленную проверку некоторых условий, то естественно для каждого из них произвести слияние внутреннего представления условия с деревом обрабатываемого запроса.


Ко второму типу условий целостности относятся условия, проверка которых должна производиться при завершении транзакции или при выполнении явного оператора SQL INFORCE INTEGRITY. Достаточно неразумно проверять при окончании транзакции все условия целостности, существующие в базе данных. Поэтому при выполнении третьего шага оптимизатора естественно было бы выбрать из множества условий целостности второго типа подмножество, соответствующее данному запросу, и передать идентификаторы этих условий прекомпилятору для их общей сборки. На четвертом шаге вырабатывается внутреннее процедурное представление запроса. Для этого сначала с использованием отношения-каталога индексов опознается множество индексов, которые можно было бы использовать при выполнении обрабатываемого запроса. Далее составляются все возможные планы выполнения данного запроса, т.е. способы его выполнения, представленные во внутренней форме. (Здесь следует оговориться, что не для всех запросов формируются все возможные планы. Для некоторых типов запросов, например, для запросов с соединениями нескольких отношений, их слишком много. В этом случае на основе эвристик некоторые планы вообще не рассматриваются.) Вслед за этим происходит процесс, который и следовало бы называть оптимизацией. Из множества возможных планов выполнения запроса выбирается один, который на основании существующей ко времени прекомпиляции статистической информации является оптимальным по времени выполнения. В силу особой важности этого момента мы рассмотрим его отдельно в следующем подразделе. Выбранный оптимальный план выполнения запроса (путь доступа) представляется путем структурных модификаций дерева запроса на внутреннем процедурном языке ASL (Access Specification Language - язык спецификации доступа) [16]. Опорными элементами ASL являются операции RSS, но конструкции языка имеют более высокий уровень, включая, например, возможности задания итераций в форме, наиболее часто требующейся при выполнении запросов; возможности вычисления встроенных функций и т.д.


Язык ориентирован на упоминавшийся выше подход к генерации кодов на основе сборки рабочей программы из сравнительно небольшого числа заранее подготовленных фрагментов. Формированием внутреннего представления запроса на языке ASL завершается работа оптимизатора, и прекомпилятор приступает к выполнению третьей фазы прекомпиляции - генерации рабочей программы выполнения запроса в машинных кодах. Соотвествующая процедура формирует рабочую программу выполнения запроса в кодах IBM/370, используя способ ее сборки из фрагментов (в [16] отмечается, что используется библиотека, включающая около 100 фрагментов). В результате образуется секция модуля доступа. Мы не упомянули о еще одном специфическом случае - обработке предложения динамического вызова компилятора SQL PREPARE. Прекомпилятор распознает предложения PREPARE (и EXECUTE), и для каждого предложения PREPARE производит специальную "пустую" секцию модуля доступа, содержащую лишь информацию о том, что во время выполнения предложения нужно будет вызвать компилятор SQL с передачей ему динамически полученной текстовой строки. Итак, при работе прекомпилятора могут образовываться секции модуля доступа четырех типов, называемые в [26] PARSESECT, INTERSECT, COMPILESECT и INDEFSECT. Секция типа PARSESECT соответствует запросу, который успешно прошел грамматический разбор, но в котором употребляются имена отношений, не существующих к моменту прекомпиляции. Такая секция содержит дерево грамматического разбора и начальный текст запроса на SQL. Секция типа INTERSECT соответствует "интерпретируемому" запросу и содержит также дерево запроса (но с именами, замененными на идентификаторы) и начальный текст запроса. Секция типа COMPILESECT содержит машинные коды полученной при прекомпиляции рабочей программы выполнения запроса и исходный текст запроса. Наконец, секция типа INDEFSECT соответствует предложению SQL PREPARE. О содержимом секций такого типа мы уже говорили. Для каждого обрабатываемого предложения SQL тем самым естественно выделяются фазы грамматического разбора, оптимизации, генерации кодов и собственно выполнения.


На Рис. 5 показано разделение функций для разных типов секций между этапами прекомпиляции и выполнения. После успешного завершения обработки всех встретившихся в тексте исходной программы предложений SQL прекомпилятор выполняет завершающие действия, которые состоят в формировании из образованных секций модуля доступа - модуля, содержащего программы и данные всех образованных при прекомпиляции секций, который, как мы опишем ниже, специальным образом регистрируется в каталоге базы данных и который загружается в память задачи на стадии выполнения программы. Кроме того, прекомпилятор модифицирует текст исходной программы для обеспечения ее связи с модулем доступа. Эта модификация состоит в том, что все вхождения в текст исходной программы выполняемых предложений SQL заменяются на вызовы стандартной программы поддержки выполнения, называемой в [26] XRDI, с передачей ей параметров, характеризующих соответствующую секцию модуля доступа и требуемую операцию в этой секции. Мы выделили в предыдущем предложении слово "выполняемых", потому что SQL включает один невыполняемый оператор LET identifier BE <query>. Обработка такого предложения на стадии прекомпиляции приводит к образованию соответствующей секции модуля доступа, по отношению к которой в дальнейшем можно употреблять операторы SQL OPEN, FETCH, DESCRIBE и CLOSE. Семантически идентификатор, с которым связан запрос на выборку, содержащийся в предложении LET, означает идентификатор курсора; он указывается в других операциях, связанных с тем же запросом. Из текста исходной программы оператор LET убирается. На Рис.6 перечислены типы секций и возможные типы операций, которые могут вызываться в этих секциях (с использованием вызова упомянутой выше стандартной программы XRDI). Приведем некоторые пояснения по поводу Рис.6. В секциях модуля доступа, полученных при обработке предложений SQL, отличных от запросов на выборку, можно вызвать операции Auxcall, Describecall и Setupcall. Обращение к операции Auxcall вызывает выполнение либо программы в машинных кодах (если секция имеет тип Compilesect), либо соответствующей типу предложения стандартной программы (если секция имеет тип Intersect).


Как отмечается в [26], в секциях этого рода допускается и выполнение операции Describecall, но при этом всегда возвращается один и тот же результат, по которому можно определить, что секция соответствует не запросу на выборку. Выполнение такого проверочного действия требуется, если секция образовалась динамически на стадии выполнения путем вовлечения оператора PREPARE, поскольку тогда в динамике неизвестно, какому типу запроса она соответствует. Выполнение операции Setupcall допустимо только для секций, которые были сформированы динамически при выполнении операции PREPARE. Вызов операции Setupcall производится при выполнении следующей операции PREPARE, относящейся к той же секции. Производится обработка строки, содержащей слудующее предложение SQL, путем последовательного прохождения фаз грамматического разбора, оптимизации и генерации кодов. В результате содержимое секции в вируальной памяти полностью заменяется на новое. При этом, в частности, может измениться и тип секции. В секциях модуля доступа, полученных при прекомпиляции или динамической компиляции предложений SQL типа SELECT (т.е. запросов на выборку), доступны пять операций - Opencall, Fetchcall, Closecall, Describecall и Setupcall. Если секция образована в период прекомпиляции, то используются главным образом три первые операции. При этом первой операцией по отношению к одной секции должна быть операция Opencall. Концептуально выполнение этой операции можно рассматривать как формирование временного отношения, содержащего результат запроса. Достаточно часто это и на самом деле так, например, в случае, когда в запросе указана необходимость сортировки результатов. В более простых случаях можно избежать реального построения такого временного отношения и генерировать кортежи результата по мере потребности. В любом случае после выполнения вызова Opencall, соответствующего операции SQL OPEN <идентификатор курсора>, секция приводится в состояние, позволяющее получать очередной кортеж результата в памяти программы с помощью вызова операции Fetchcall.


Каждый вызов такой операции, соответствующий операции Fetch <идентификатор курсора> приводит к занесению в указанные переменные программы значений полей очередного кортежа результата. Такой покортежный просмотр результирующего отношения запроса прекращается либо при исчерпании результата, либо при вызове в секции операции Closecall, соответстующем операции SQL Close <идентификатор курсора>. В любом случае выполнение операции Close требуется перед повторным использованием секции, начиная с операции Open. При выполнении операции Describecall в секциях, соотвествующих запросам на выборку, вырабатывается описание результирующего отношения запроса, содержащее для каждого поля этого отношения его имя (если оно у него есть; в результирующем списке запроса могут указываться не только имена полей, но и выражения) и тип. Естественно, что для запросов, обрабатываемых в период прекомпиляции, эта операция не требуется, поскольку вырабатываемая информация доступна в статике. Однако, эта операция необходима при выполнении динамически компилируемых запросов. Например, при использовании SQL в интерактивном режиме операция Describecall позволяет правильно отформатировать результат запроса на экране дисплея, не заставляя специальным образом анализировать введенный запрос. Наконец, использование операции Setupcall в секциях этого рода допустимо (как и раньше) только для секций, которые были образованы динамически при выполнении операции PREPARE. Если секция модуля доступа образована при обработке прекомпилятором предложения SQL PREPARE, то в ней доступна только операция Setupcall, которая производит в виртуальной памяти первый готовый к выполнению вариант этой секции (т.е. вариант не типа Indefsect). Как мы уже отмечали, при успешном завершении работы прекомпилятора, образуются модифицированный текст исходной программы, не содержащий уже предложений SQL, и модуль доступа, в который входят все построенные секции. Далее исходная программа обрабатывается обычным образом с использованием соответствущего компилятора, редактора связей и т.д.


При выполнении первого обращения к XRDI производится динамическая загрузка модуля доступа в виртуальную память задачи (в среде MVS модуль доступа загружается в область памяти с отличным от пользовательского ключем защиты) и далее выполнение транзакции происходит путем соответствующих вызовов операций в секциях модуля доступа (и, конечно, стоящих за ними вызовов операций RSS). Однако, имеется достаточно сложный аспект, связанный с контролем и поддержкой корректности модуля доступа. Дело в том, что содержимое модуля доступа корректно относительно того состояния базы данных, которым она обладала в период прекомпиляции. При прекомпиляции было проверено (и использовано) существование всех используемых объектов базы данных, проверены полномочия пользователя, от имени которого выполнялась прекомпиляция, и т.д. При выборе способов выполнения запросов использовалась информация о существующих в период прекомпиляции индексах. Вся эта информация может измениться к моменту реального использования модуля доступа. Если не отслеживать корректность произведенных ранее модулей доступа при изменениях базы данных, то выполнение модуля доступа может привести к непредсказуемым результатам. Естественно, что до конца служебной транзакции, выполняемой в процессе прекомпиляции, никакая критическая для корректности модуля доступа информация в базе данных измениться не может за счет синхронизационных захватов описателей используемых объектов базы данных. Как и для любой другой транзакции, эти захваты удерживаются до ее завершения, и поэтому ни в какой другой транзакции не может быть, например, уничтожено отношение, описатель которого читался при работе прекомпилятора. После произведения модуля доступа и до завершения служебной транзакции прекомпилятор заносит модуль доступа в базу данных (подробности того, как он хранится в базе данных, в публикациях не приводятся) и регистрирует его в служебном отношении-каталоге модулей доступа. Описатель модуля доступа, помещаемый в этот каталог, содержит, в частности, признак корректности модуля доступа, идентификатор пользователя-создателя этого модуля доступа и координаты модуля доступа в базе данных.


Кроме того, в дополнительные каталоги базы данных (структура которых также не описывается в публикациях) помещается информация о зависимостях данного модуля доступа от существования соответствующих объектов базы данных. Путем коррекции каталога прав доступа пользователь-создатель модуля получает привилегию на его выполнение (как и любая другая привилегия она может быть передана другому пользователю с помощью оператора SQL GRANT). При изменении базы данных (уничтожении отношения или индекса, изменении прав доступа зарегистрированных пользователей, уничтожении или появлении новых условий целостности или условных воздействий) система автоматически обнаруживает на основе информации в системных каталогах те модули доступа, которые становятся некорректными после выполнения соответствующих действий. При этом различаются два вида возникающей некорректности - исправляемая и неисправляемая. Некорректности первого рода возникают при удалении индексов и уничтожении или порождении новых условий целостности и условных воздействий. Эти некорректности устранимы; для этого достаточно переформировать одну или несколько секций модуля доступа в соответствии с заново возникшей обстановкой. При обнаружении некорректности первого рода в некотором модуле доступа в его описателе проставляется соответствующий признак. Некорректности второго рода могут возникнуть при удалении отношений (базовых или представлений) или при лишении пользователя прав доступа, которыми он ранее располагал. Если модуль становится некорректным в этом смысле, его уже невозможно вернуть в корректное состояние, т.е. если бы внешняя обстановка была такой в период прекомпиляции, прекомпилятор просто не сформировал бы модуль доступа. Поэтому при обнаружении некорректности второго рода в некотором модуле доступа он уничтожается вместе со всеми сопутствующими ему описателями в каталогах базы данных. Тем самым к моменту реального выполнения модуля доступа возможны следующие ситуации: модуль доступа отсутствует в базе данных по причине возникшей ранее некорректности в нем второго типа; модуль доступа имеется в наличии, но в его описателе проставлен признак некорректности; наконец, модуль доступа имеется, и он корректен.


Первый и третий случаи тривиальны: в первом случае транзакция не выполняется, во втором - модуль доступа нормально загружается, и транзакция выполняется обычным путем. Во втором случае до загрузки модуля доступа для выполнения производится его коррекция. Для этого секции модуля доступа, ставшие некорректными, последовательно проходят повторную обработку, исходя из находящихся в них исходных представлений предложений SQL. При этом, естественно, могут быть выбраны другие способы выполнения запросов в соответствии с существующими к этому моменту индексами. Кстати, вполне может быть, что новые способы будут эффективнее тех, которые были произведены при прекомпиляции, за счет возможного появления новых индексов или благоприятного изменения статистической информации. После завершения коррекции модуль доступа заново заносится в базу данных, и корректируются все сопутствующие ему описатели в отношениях-каталогах. И только потом производится загрузка модуля доступа в виртуальную память задачи для его реального выполнения. Заметим, что модуль доступа не может стать некорректным во время выполнения. Как и многое другое в System R, это достигается на основе правильно построенного протокола синхронизационных захватов. До загрузки модуля доступа в виртуальную память интерфейсная программа XRDI начинает служебную транзакцию (вернее, служебную часть пользовательской транзакции), в ходе которой читает описатель модуля доступа из отношения каталога. При этом, как обычно, устанавливается синхронизационный захват описателя, который удерживается до конца транзакции. Синхронизация при выполнении операций изменения базы данных, могущих повлечь некорректность модулей доступа, ведется таким образом, что реальные изменения не производятся до удовлетворения захвата на описатели модулей и тем самым откладываются до завершения пользовательской транзакции, в которой выполняется потенциально некорректный модуль доступа. | |


Содержание раздела