Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:MUMPS СУБД. Практика применения и опыт программирования.pdf
X
- •Предисловие
- •Введение
- •Среда исполнения
- •Команды
- •Команды присваивания
- •Условные команды
- •Команды передачи управления
- •Команды ввода-вывода
- •Служебные команды
- •Постусловия
- •Операторы
- •Переменные
- •Числа и строки
- •Функции
- •$DATA
- •$GET
- •$ORDER
- •$NEXT
- •$QUERY
- •$NAME
- •$QLENGTH
- •$QSUBSCRIPT
- •$ASCII
- •$CHAR
- •$EXTRACT
- •$PIECE
- •$LENGTH
- •$REVERSE
- •$FIND
- •$TRANSLATE
- •$JUSTIFY
- •$FNUMBER
- •$TEXT
- •$RANDOM
- •$VIEW
- •$SELECT
- •$STACK
- •Списковые функции
- •Битовые функции
- •Модули
- •Рутины
- •Передача параметров
- •Неопределенные значения
- •Шаблоны
- •Косвенность
- •Косвенность имени
- •Косвенность индексов
- •Косвенность метки
- •Косвенность аргумента
- •Косвенность шаблона
- •Интерпретатор
- •Голая ссылка
- •Очередность выполнения
- •Очередность вычисления выражений
- •Очередность вычисления имен
- •Стекование $test
- •Комментарий
- •Стандарт и расширения
- •Глобалы
- •B-дерево
- •Кодирование индексов
- •Размер блока
- •Кеширование блоков
- •Структуры
- •Индексация
- •Группировка
- •Каноничность индексов
- •Маппинг
- •Индексация данных
- •Общие принципы
- •Механизм поддержки индекса
- •Простой индекс
- •Составной индекс
- •Покрывающий индекс
- •Кластерный индекс
- •Хеш-индекс
- •Битмап индекс (bitmap)
- •Битслайс индекс (bitslice)
- •Нормирование значений
- •Выборки по индексу
- •Многоиндексная выборка (zig-zag)
- •Дифференциальное индексирование
- •Индексация длинных атрибутов
- •Межтабличный индекс
- •Индекс с условием на вставку
- •Индекс на вычисляемый атрибут
- •Индекс поиска по фрагменту
- •Индексация для шаблона (like)
- •Индексация уникального атрибута
- •Массовое перестроение индексов
- •Операции с древовидными индексами
- •Операции с битовыми индексами
- •Сортировка по индексу
- •Статистики и кардинальность
- •Конкурентный доступ
- •Параллельность выполнения
- •Блокировки
- •Функция $INCREMENT
- •Транзакции
- •Блокировки в транзакциях
- •Функция $BIT
- •Дедлоки
- •Обработка ошибок
- •Состояние ошибки
- •ZTRAP
- •GT.M
- •MiniM
- •ETRAP
- •Определение
- •$ETRAP
- •$ECODE
- •$ESTACK
- •Ошибки в обработчике ошибок
- •$STACK()
- •Трассировка
- •BREAK
- •MiniM Debugger
- •Serenji Debugger
- •Внешний мир
- •Общие принципы
- •Терминальный интерфейс
- •Сокеты
- •HTTP клиент
- •Вебсервер на MUMPS
- •WebLink
- •Проблемы HTTP
- •Поверх HTTP
- •Подключаемые DLL (SO)
- •Файлы
- •Внешние процессы
- •Порты
- •Практика применения
- •Терминальный режим
- •Редакторы рутин
- •Экспорт и импорт
- •Препроцессор
- •Формат $HOROLOG
- •Опции устройств
- •$X и $Y
- •Возврат результатов
- •Возврат по значению ($$)
- •Возврат по ссылке
- •Запись в предопределенную переменную
- •Возврат значений косвенно
- •Итеративный возврат
- •Потоковый возврат
- •%Z - рутины
- •Планирование файлов
- •Память и сборка мусора

5.3. ETRAP 371
5.3 ETRAP
Обработчик ошибок ETRAP был определен в стандарте языка MUMPS
ANSI 1995-го года и в настоящее время входит в ныне действующий
стандарт ISO M UMPS. В существенной степени его функционал был
выбран похожим на наиболее сильные стороны обработчика ZTRAP, реализованного в системе GT.M.
5.3.1 Определение
Основу обработчика ETRAP составляет комплекс следующих соглашений:
1. Схема обработки ошибок стандартизирована и переносима.
2. Обработчик задается не меткой, а строкой команд, которая выполняется при переходе процесса в состояние ошибки.
3. Имеется возможность изменять установленный ранее обработчик
$ETRAP.
4. Имеется возможность указать явно область видимости действия
обработчика $ETRAP командой NEW.
5. Имеется возможность вести независимый отсчет по стеку системной переменной $ESTACK, применяя по необходимости к ней команду NEW.
6. Имеется системная переменная $STACK и парная ей системная
функция $STACK() для анализа стека.
7. Имеется системная переменная $ECODE для чтения текущего состояния ошибки, для генерации ошибки при присваивании непустой строки и для окончания обработки ошибки при присваивании
пустой строке.
В целом, весь этот комплекс соглашений реализует более мощную
и гибкую схему обработки ошибок, чем простые правила обработчиков
ZTRAP, связанные с неявной передачей управления командой GOTO на
метку.
Алгоритм выполнения обработчика ошибок по схеме ETRAP, вообще
говоря, стандартен, но, поскольку в каждой из MUMPS систем присутствует в той или иной мере схема обработки ZTRAP, в каждой из них

372 ГЛАВА 5. ОБРАБОТКА ОШИБОК
могут быть отдельные нюансы по сочетанию двух этих обработчиков, по
приоритету их действия в состоянии ошибки, и по переключению процесса в зависимости от последнего присваивания обработчика - ETRAP
или ZTRAP. Далее будет рассматриваться схема чистого обработчика
ETRAP, в состоянии, когда процесс не определял обработку по ZTRAP.
В случае, если прикладная программа сочетает оба варианта, программистам необходимо сверить по документации на используемую MUMPS
систему характер обработки ошибок для применяемой MUMPS системы,
включая возможные особенности ее версии.
5.3.2 $ETRAP
Для описания действий, которые необходимо предпринять при обработке
ошибки, используется присваивание специальной системной переменной
$ETRAP последовательности команд.
При этом системная переменная $ETRAP может быть ограничена по
области действия командой NEW. При присваивании выполняется изменение значения $ETRAP на том уровне стека, где к $ETRAP последний раз была применена команда NEW, а если не было, то изменяется
ее значение на нулевом, начальном уровне стека. При чтении переменной $ETRAP возвращается значение присвоенное на последнем уровне
стека, независимо от текущего уровня стека. В определенном смысле
такое поведение системной переменной $ETRAP аналогично обычной
локальной переменной, с тем исключением что эта переменная всегда
существует и при старте процесса по умолчанию имеет значение пустая
строка. Присваивание значения $ETRAP само по себе не выполняет автоматического ограничения области действия.
Дополнительно можно отметить, что зачастую после применения команды NEW к переменной производится ее присваивание, и для сокращения таких операций система MiniM поддерживает расширенную
(нестандартную) инициализирующую форму команды NEW. В MiniM
можно использовать оба варианта как равноценные:
new $etrap set $etrap=handler
new $etrap=handler
Интересно, что, в отличие от локальных переменных, команда NEW в
применении к системной переменной $ETRAP не изменяет ее значение,
а создает копию на текущем уровне стека, и дальнейшие команды SET
изменяют значение $ETRAP уже начиная с текущего уровня. Пример
рутины для выполнения NEW применительно к переменной $ETRAP:

5.3. ETRAP 373
setnew ; k d setnew^ETRAP w
s $et=123
w "1: ",$et,!
d subset
w "4: ",$et,!
q
subset
n $et
w "2: ",$et,!
s $et=456
w "3: ",$et,!
q
При выполнении рутины получаем последовательность значений $ETRAP:
USER>k d setnew^ETRAP w
1: 123
2: 123
3: 456
4: 123
Здесь видно, что в точке 2 значение $ETRAP осталось неизменным,
и, несмотря на изменение в точке 3, при возврате в точку 4 предыдущее
значение было восстановлено.
При переходе к обработке ошибки процесс выполняет значение переменной $ETRAP вместо текущей выполняемой строки. Процесс выполняет ее на манер команды XECUTE, но без организации нового уровня
стека.
После окончания выполнения строки команд, указанной в $ETRAP,
и, выполнив возврат по стеку, процесс проверяет значение системной переменной $ECODE, и, если оно пустое, то выполнение продолжается со
следующей строки. Если не пустое, то процесс возвращается по стеку на
предыдущий уровень и снова выполняет значение $ETRAP как последовательность команд. На предыдущем уровне стека значение $ETRAP
может уже быть другим.
После окончания выполнения строки команд выполняется неявно последовательность команд возврата по стеку в следующей форме:
quit:$quit ""
quit
Это означает выполнить безаргументный возврат, если возврат по
контексту не ожидается, и вернуть пустую строку, если возврат ожидается. При этом значение системной переменной $TEST такой логической
проверкой не затрагивается и не изменяется.

374 ГЛАВА 5. ОБРАБОТКА ОШИБОК
Для задания обработчика ошибок необходимо, как минимум, выпол-
нить два действия:
1. Присвоить переменной $ETRAP строку с последовательностью команд для обработки ошибки.
2. Эта последовательность команд должна явно или неявно изменить
или не изменить значение переменной $ECODE.
Остальные системные переменные и системные функции служат коду
обработчика ошибок индикатором состояния, по которому он принимает
решение.
Простой вариант, показывающий возврат управления по стеку при
обработке ошибок:
proc() ; k d proc^ETRAP() w
s $etrap="w $st,!"
w 1/0
q
При его выполнении получаем сначала срабатывание обработчика на
последнем, первом уровне, далее на предыдущем, нулевом:
USER>k d proc^ETRAP() w
1
0
При этом значение $ECODE не изменяется, и все время сохраняет
свое значение:
USER>w $ec
,M9,
Рассмотрим вариант остановки обработки ошибки в коде обработчика:
proc() ; k d proc^ETRAP() w
s $etrap="w ""stack="",$st,! s $ec="""""
w $$sub()+123,!
d sub()
w "last",!
q
sub()
w "sub",!
w 1/0
q

5.3. ETRAP 375
При выполнении такого кода ошибка возникает в функции sub. Если
она вызывается в контексте возврата значения, то возвращается пустая
строка, иначе выполняется просто возврат. Дальнейшее выполнение продолжается на предыдущем уровне стека:
USER>k d proc^ETRAP() w
sub
stack=2
123
sub
stack=2
last
5.3.3 $ECODE
Присваивание системной переменной $ECODE пустой строки для системы исполнения MUMPS процесса означает, что обработка ошибки
закончена и дальнейшую раскрутку стека не нужно выполнять.
Присваивание системной переменной $ECODE непустой строки, наоборот, приводит к генерации ошибки с записыванием этого значения в
переменную $ECODE. Заменим код генерации ошибки на принудительное взведение процесса в состояние ошибки таким присваиванием:
proc() ; k d proc^ETRAP() w
s $etrap="w ""stack="",$st,!,""$ec="",$ec,! s $ec="""""
w $$sub()+123,!
d sub()
w "last",!
q
sub()
w "sub",!
s $ec="trap in sub"
q
При выполнении такого кода получаем последовательность работы:
USER>k d proc^ETRAP() w
sub
stack=2
$ec=trap in sub
123
sub
stack=2
$ec=trap in sub
last

376 ГЛАВА 5. ОБРАБОТКА ОШИБОК
Здесь происходит все то же самое, что и при ошибке деления на ноль,
но мы явно задаем, какую ошибку надо генерировать. Формально говоря, мы можем присвоить переменной $ECODE произвольное значение.
Но стандарт содержит явные рекомендации использовать соглашения по
формированию такого значения:
1. Содержание $ECODE задается последовательностью кодов ошибок, разделенных запятой.
2. При происхождении очередной ошибки, если значение $ECODE не
пусто, код ошибки надо дописывать в конец этого списка.
3. Коды ошибок следует начинать с символа M для ошибок, зарезервированных и определенных стандартом, с символа Z для расширенных ошибок реализации MUMPS системы и с символа U для
ошибок прикладной программы.
Список стандартных ошибок, начинающихся с символа M, приведен в
приложении. В рассмотренном примере код ошибки "M9" соответствует
ошибке деления на ноль.
Кроме того, некоторые MUMPS системы поддерживают, для совместимости с обработчиком ZTRAP, также команду ZTRAP для явной генерации ошибки, и в этом случае в значение $ECODE должнен быть занесен код ошибки, начинающийся с символа Z, поскольку такая команда
не описана в стандарте, генерируемая ей ошибка не входит в описанные,
и поддерживается в качестве расширенной команды MUMPS системы:
USER>s $et=""
USER>ztrap "custom"
<Zcustom>
USER>w $ec
,ZZTRAP,
USER>w $ze
<Zcustom>
Здесь стандарт определяет поведение переменной $ECODE, а значение расширенных $Z переменных остается на усмотрение реализации и
в разных MUMPS системах может как совпадать, так и незначительно
отличаться.

5.3. ETRAP 377
Что важно отметить, в языке допускается присваивание переменной
$ECODE, и содержание преимущественно трактуется как список разделенный запятыми, но присваивание системной переменной в левосторонней форме функцией $PIECE() в языке синтаксически не поддерживается.
USER>s $p($ec,",",2)=45
S $P($EC,",",2)=45
^
<SYNTAX>
при выполнении в Cach´e и
USER>s $p($ec,",",2)=45
<SYNTAX> :SET: *s $p($ec,",",2)=45
при выполнении в MiniM.
Способ создания нового уровня стека для обработчика $ETRAP не
важен, это может быть вызов метки командой DO, вызов функции с
ожиданием возврата, команда XECUTE, или блочная форма команды
DO:
proc() ; k d proc^ETRAP() w
s $etrap="w ""stack="",$st,!,""$ec="",$ec,! s $ec="""""
d
. w 1/0
x "w 1/0"
w "last",!
q
При выполнении такого кода управление продолжается на уровне стека на уровень выше, чем уровень произошедшей ошибки:
USER>k d proc^ETRAP() w
stack=2
$ec=,M9,
stack=2
$ec=,M9,
last
Для того, чтобы продолжить выполнение программы на том же уровне
стека, необходимо передать управление из обработчика командой GOTO,
например:

378 ГЛАВА 5. ОБРАБОТКА ОШИБОК
proc() ; k d proc^ETRAP() w
s $etrap="w $st,! s $ec="""" goto continue"
w 1/0
continue
w "last",!
w $st,!
q
В случае покидания управления из последовательности обработки
ошибок, заданной в обработчике $ETRAP командой GOTO, неявный
возврат управления на уровень стека выше не выполняется, при выполнении такого кода исполнение программы продолжается с заданной
метки:
USER>k d proc^ETRAP() w
1
last
1
В частности, обработчик ошибок может скорректировать ситуацию с
данными, приведшими к ошибке и продолжить выполнение программы,
в том числе с места, вызвавшего ошибку:
proc() ; k d proc^ETRAP() w
s $etrap="goto err"
k a
do
w a,!
w 1/0
q
err
w "err trap at $st=",$st,", $ec=",$ec,!
i ’$d(a) s $ec="" s a=123 goto do
q:$q "" q
Выполнение такого кода показывает последовательность обработки
обеих ошибок - сначала вызывается обработчик из-за неопределенности
переменной a, значение a корректируется, затем выполнение продолжается с использования переменной a, затем генерируется ошибка деления на
ноль, и обработчик в этом случае не останавливает обработку очисткой
переменной $ECODE, управление возвращается на уровень выше, снова
срабатывает обработчик $ETRAP, но на верхнем уровне, вне контекста
текущей рутины, конечно нет возможности определить метку err.
USER>k d proc^ETRAP() w
err trap at $st=1, $ec=,M6,

5.3. ETRAP 379
123
err trap at $st=1, $ec=,M9,
goto err
^
<NOLINE>
Последнее сообщение в этом примере может отличаться для разных
MUMPS систем. Одни могут запретить переход по GOTO по причине
отсутствия текущей рутины, другие могут запретить выполнение GOTO
на нулевом уровне стека, не проводя проверок на существование рутины
и метки.
Также, как и последовательность команд, выполняемая в качестве
аргумента командой XECUTE, всегда заканчивается неявной командой
QUIT, даже если это не было указано, также и последовательность команд, задаваемая обработчику ошибок $ETRAP, всегда заканчивается
неявной последовательностью команд
quit:$quit "" quit
Важно отметить, что в случае, если обработчику ошибок $ETRAP
был указан код, явно передающий управление на другую строку командой GOTO, уровень стека сохраняется и ответственность за стек и
управление возвратом по стеку берет на себя разработчик. Если управление было явно передано по стеку командой QUIT, то именно эта команда
и будет использована для возрата по стеку и дальнейшие команды, указанные в $ETRAP, в том числе и неявный возврат, исполняться не будут,
до них просто не дойдет управление.
5.3.4 $ESTACK
Парной к системной переменной $ETRAP является системная переменная $ESTACK. Эта переменная используется в качестве маркера стека
для обработки ошибки в форме, где это важно. Типовое назначение переменной $ESTACK - маркировать уровень стека, чтобы выполнить раскрутку стека до определенного уровня, отмеченного этим маркером, и
только после этого выполнить обработчик ошибок.
Переменная $ESTACK была введена в стандарт языка в 1995-м году, наряду с системной переменной $STACK и системной функцией
$STACK(). Все три используются для программного анализа состояния
стека.
Переменная $STACK при старте процесса всегда равна 0, при увеличении уровня стека при вызове подпрограмм, блочной командой DO

380 ГЛАВА 5. ОБРАБОТКА ОШИБОК
или командой XECUTE, всегда увеличивается на 1 и при возврате по
стеку командой QUIT либо неявной командой QUIT всегда принимает
предыдущее значение, или возвращается к значению на 1 меньше. В
определенном смысле переменная $STACK показывает абсолютное значение уровня стека.
Переменная $ESTACK также при старте процесса равна 0, и при
каждом увеличении уровня стека увеличивается на 1. Но при возврате по стеку принимает предыдущее значение. Отличие от переменной
$STACK в том, что если к переменной $ESTACK применена команда
NEW, то на этом уровне стека переменная $ESTACK обнуляется и ее
рост продолжается снова на 1 на каждый уровень стека. Но при возврате
по стеку значение $ESTACK восстанавливается. Таким образом, если на
уровне N к переменной $ESTACK применить команду NEW, то на нем
она станет равной 0, и при возврате по стеку станет равной N − 1.
Пример такого поведения показывает программа:
estack ; k d estack^ETRAP w
w "1: ",$es,!
d subes
w "7: ",$es,!
q
subes
w "2: ",$es,!
n $es
w "3: ",$es,!
d subes2
w "6: ",$es,!
q
subes2
w "4: ",$es,!
n $es
w "5: ",$es,!
q
При выполнении получаем последовательность значений $ESTACK:
USER>k d estack^ETRAP w
1: 1
2: 2
3: 0
4: 1
5: 0
6: 0
7: 1
Переменная $ESTACK по своему поведению предназначена для относительной маркировки стека, и, в частности, для обработки ошибок,
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
