Добавил:
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 - рутины
- •Планирование файлов
- •Память и сборка мусора

1.14. ИНТЕРПРЕТАТОР 161
Здесь две последних альтернативы определены рекурсивно, и парсер
сначала должен определить есть ли символ унарного оператора, запомнить его, и перейти к парсингу последующего выражения как expratom.
При его разборе считается что парсер выполняет кодогенерацию для
этого элемента. Поэтому после возврата из парсинга expratom останется
добавить к байткодам байткод унарной операции.
Например, при парсинге выражения
+$H
Сначала выполняется запоминание унарной операции плюс (+), затем кодогенерация для вычисления $H, затем добавляется байткод для
вычисления унарной операции плюс. При выполнении байткода система
исполнения уже выполнит действия в правильной последовательности,
а не так как это написано - сначала оператор плюс а потом вычисление
$H.
То же правило относится к альтернативе (expr) - эта синтаксическая
конструкция может целиком заменять конструкцию expritem.
Определение конструкции exprtail дано так:
exprtail := | | binaryop | expratom |
| | [’]truthop | |
| [’] ? pattern |
К синтаксичесим конструкциям binaryop относятся бинарные операторы, например +, -, #. К синтаксическим конструкциям truthop относятся логические операторы, например >, =, <.
Таким образом, в языке MUMPS при трансляции выражения
a + b * c
Сначала выполняется вычисление a, затем вычисление b, затем сложение, затем вычисление c, затем умножение, а при трансляции выражения
a + ( b * c )
сначала вычисляется a, затем b, затем c, затем произведение b * c, затем
сложение.
Определение синтаксических конструкций языка, таким образом, не
содержит приоритетов операций для операторов.
В полный набор операций виртуальной машины входит кроме вычисления имен, меток и значений, также множество дополнительных
служебных операций, например при трансляции кода

162 ГЛАВА 1. СРЕДА ИСПОЛНЕНИЯ
write:a+b c+d,! set ...
виртуальная машина должна сначала вычислить потсусловие (a+b), а
затем, если оно не выполнилось, пропустить в байткоде выполнение кода
для
c+d,!
для команды write и перейти сразу к вычислению аргумента следующей
за ней команды set (или его постусловия, если оно было задано).
Набор таких дополнительных служебных операций полностью определяется архитектурой виртуальной машины, каким образом она была
разработана производителем MUMPS системы.
Современные интерпретаторы MUMPS систем зачастую поддерживают кроме предопределенного функционала также возможность вызова внешних модулей. Обычно это выполняется динамической загрузкой
динамических библиотек. В этом случае DLL (или SO в Linux) должна
реализовать определенный интерфейс для того, чтобы интерпретатор мог
выполнить вызов.
При выходе последующих версий MUMPS систем традиционно принято соблюдать правила совместимости используемых форматов байткодов. Если в следующей версии MUMPS системы производятся изменения, например добавляются операторы или системные функции с
соответствующими байткодами для них, то они добавляются так, чтобы
предыдущие компилированные байткоды для этой версии MUMPS могли исполняться без изменений. Это называется совместимостью снизу
вверх.
Современные реализации MUMPS систем, поддерживающие компиляцию исходных текстов в байткод, также поддерживают возможность
отдельного экспорта и импорта компилированного байткода. Такой способ позволяет компилировать исходный текст MUMPS рутины на одном
сервере и исполнять его на другом без исходных текстов.
Такие меры по переносу только компилированного байткода могут
приниматься, если действительно необходимо скрыть исходный текст
рутин или при сокращении времени переноса программ при большом
количестве кода. В случае переноса только байткода значительно сокращается объем действий на целевом сервере, и выполняется только
сохранение байткода вместо сохранения исходного текста, компиляции
и все равно сохранения байткода.
Одновременно с тем, разработчики должны учитывать, что часть исходных текстов может не допускать такого переноса, если программе

1.15. ГОЛАЯ ССЫЛКА 163
требуется читать строки кода функцией $TEXT, или если при компиляции макрокода производится генерация кода рутин, зависимая от конфигурации сервера, версии MUMPS системы, или времени трансляции.
Кроме того, для многих современных интерпретаторов MUMPS систем возможно выполнение декомпилятора байткода с получением исходного текста. Возможно, исходный текст не будет в точности повторять
оригинальный, например декомпилятор может не определить в каком
регистре была написана команда и была ли использована полная или сокращенная форма. Как правило, от версии к версии производители могут
добавлять к определению байткода дополнительные элементы и декомпиляторы, тем самым, устаревают. Но в каких-то случаях они могут
быть полезны для восстановления оригинального текста.
Можно отметить, что перенос только компилированного байткода в
реальной практике применяется очень редко и такой код составляется с
особенным вниманием к перечню версий MUMPS системы, на которой
он должен работать. Если какие-то из использованных функций поддерживаются не во всех версиях, то их обычно вызывают через команду
XECUTE и при выполнении соответствующих проверок.
1.15 Голая ссылка
Голая ссылка (naked indicator) - это сокращенное обозначение обращения
к последнему имени глобала, к которому было обращение. Обозначается
опусканием имени глобала и добавлением индексов:
set ^Data(id,"root","rel","sub","color")="Синий"
set ^("figure")="Квадрат" ; use naked
set ^("count")=15
С точки зрения выполнения кода это полный эквивалент более многословного варианта:
set ^Data(id,"root","rel","sub","color")="Синий"
set ^Data(id,"root","rel","sub","figure")="Квадрат"
set ^Data(id,"root","rel","sub","count")=15
Само значение голой ссылки хранится как часть текущего состояния
процесса. При обращении к глобалам это значение меняется на имя глобала, к которому было обращение, включая значения индексов. При этом
не имеет значения контекст такого обращения, это могут быть команды
присваивания, системные функции, или контекст вычисления постусловия.

164 ГЛАВА 1. СРЕДА ИСПОЛНЕНИЯ
При старте процесса и при смене текущей базы данных значение
очищается и равно пустой строке. У каждого процесса значение голой
ссылки собственное, независимое от действий других процессов.
В примере выше первая строка обращается к глобалу
^Data(id,"root","rel","sub","color")
и значение голой ссылки принимает значение этого имени.
Узнать значение голой ссылки, согласно стандарта языка напрямую
нельзя. Но большинство современных реализаций поддерживает специальную расширенную системную переменную $ZREFERENCE, которая
возвращает имя голой ссылки.
В рекомендации стандарта (но не в сам стандарт) входит поддержка
системной переменной $REFERENCE с именно таким поведением, но
многие MUMPS системы могут не поддерживать такую переменную.
Обращение к голой ссылке во многом похоже на ключевое слово
WITH в языке Pascal, допускающее сокращение записи для обращения
к элементам структуры или объекта, отличающиеся лишь именем поля
структуры. В сокращенной записи опускаются полные операции доступа
к объекту или структуре и указываются лишь последние, отличающиеся
друг от друга, имена.
При доступе к глобалу значение голой ссылки может принять как
индексированное, так и неиндексированное значение. При использовании
голой ссылки система выполнения берет имя голой ссылки, отбрасывает
последний индекс и дописывает к полученному имени указанные после
символа (ˆ) индексы.
Эта операция не может быть корректно выполнена если значение
голой ссылки пустая строка или неиндексированное имя. В этом случае
MUMPS система генерирует стандартную ошибку (M1).
Использование голой ссылки дает существенное преимущество при
записи кода, обращающегося к глобалам, имена которых отличаются
лишь последними индексами. Например, типовой код добавления записи:
Add(Color,Figure,Count)
n id
s id=$i(^DATA)
s ^DATA(id,"Color")=Color
s ^DATA(id,"Figure")=Figure
s ^DATA(id,"Count")=Count
q id
в реальных программах может быть намного большим. Использование
голой ссылки может существенно сократить как объем кода, так и количество возможных опечаток.

1.15. ГОЛАЯ ССЫЛКА 165
При этом значение голой ссылки меняется каждый раз при доступе
к глобалу и является побочным эффектом многих функций и команд.
В отношении порядка взведения голой ссылки и порядка вычисления
выражений и имен в книге есть отдельные статьи, их также нужно изучить подробнее. При практическом использовании голой ссылки именно возможность побочного эффекта при иных операциях может создать
проблему модификации кода на MUMPS.
Например, если имеем код
Add(Color,Figure,Count)
n id
s id=$i(^DATA)
s ^DATA(id,"Color")=Color
s ^("Figure")=Figure
s ^("Count")=Count
q id
и необходимо добавить дополнительные действия
s ^("Figure")=$$Check(Figure)
то после возврата из функци $$Check и перед присваиванием значение голой ссылки может измениться и запись произойдет уже не в ту
глобаль, которая предполагалась до модификации кода, более того, промежуточный код может взвести значение голой ссылки в значение пустой строки или неиндексированного имени. В целом, все эти варианты
приводят к ошибке выполнения кода с голой ссылкой после возврата из
фунции $$Check.
По требованиям совместимости с имеющимися программами разработанными на языке MUMPS в стандарт уже нельзя ввести стекование
голой ссылки. Поэтому в некоторых случаях необходимо иметь механизм восстановления значения голой ссылки в подпрограммах. Это можно выполнить стандартными средствами языка, если перед выполнением
подпрограммы запомнить значение ссылки, а затем, при выходе, восстановить его. Например, если есть код:
Check(Figure)
n ret
s ret=Figure_$d(^ANYGLOBBAL)
q ret
то к нему можно добавить сохранение и восстановление голой ссылки:

166 ГЛАВА 1. СРЕДА ИСПОЛНЕНИЯ
Check2(Figure)
n saveref
; save naked
s saveref=$na(^("any"))
s ret=Figure_$d(^ANYGLOBBAL)
; reset naked
i $d(@saveref)
q ret
Здесь сохраняется не само имя голой ссылки, а имя с затертым значением последнего индекса. В любом случае, при использовании голой
ссылки, это значение не имеет применения, поскольку будет автоматически замещаться на первый индекс указанный после символа голой
ссылки.
Но в этом коде есть проблема с сохранением значения. Если значение
голой ссылки было пустой строкой или неиндексированым именем то
операция обращения к имени
s saveref=$na(^("any"))
генерирует ошибку.
Для компенсации такой проблемы реализации MUMPS поддерживают системную переменную $ZREFERENCE или ее аналог, которая
возвращает имя голой ссылки как есть.
Многие современные реализации MUMPS систем также поддерживают присваивание этой системной переменной или ее синониму $REFERENCE. В этом случае код подпрограммы можно выполнить более
устойчивым к ошибке доступа к имени голой ссылки:
Check3(Figure)
n saveref
; save naked
s saveref=$zr
s ret=Figure_$d(^ANYGLOBBAL)
; reset naked
s $zr=saveref
q ret
Чтение и возможность присваивания системным переменным $ZREFERENCE и $REFERENCE не входят в настоящее время в стандарт
и поддерживаются производителями различных MUMPS систем самостоятельно. Можно отметить, то вероятность что в используемой Вами системе поддерживается переменная $ZREFERENCE, очень высока.
Присваивание таким переменным поддерживают меньшее число систем.
В любом случае необходимо проверить в документации, поддерживается
ли системная переменная возвращающая имя голой ссылки как есть и
как именно.

1.16. ОЧЕРЕДНОСТЬ ВЫПОЛНЕНИЯ 167
1.16 Очередность выполнения
Одна из основных особенностей языка MUMPS и его отличия от других
состоит в недостаточно точном понимании программистами очередности
вычисления выражений и выполнения операций в языке. В других языках точно так же присутствует модель вычисления выражений и определенные соглашения, которые могут поставить программиста в тупик при
смене языка. Например, выражение
int i = 5;
i = ++i + ++i;
в разных языках может дать различные результаты:
C++ 14
cl 14
bcc 14
lcc 13
gcc 13
php 13
C# 13
С языком MUMPS тоже не все так просто, как кажется на первый взгляд. Но, в отличие от многих других языков, в языке MUMPS
полностью отсутствует undefined behavior, или поведение на усмотрение
реализации.
Очередность выполнения операций и вычисления выражений в языке
MUMPS строго определены стандартом. К очередности выполнения и
вычисления могут быть отнесены два основных пункта:
1. Очередность вычисления выражений.
2. Очередность вычисления имен.
И различные дополнительные
1. Очередность вычисления параметров функций.
2. Очередность применения команд к аргументам.
3. Очередность определения альтернатив в функции $SELECT.
Если дополнительным правилам вычисления всегда соответствует порядок слева - направо, то первые два нужно рассматривать детально, поскольку при их применении или, что не менее важно, при модификации,
могут возникнуть затруднения.

168 ГЛАВА 1. СРЕДА ИСПОЛНЕНИЯ
1.16.1 Очередность вычисления выражений
Выражения в MUMPS вычисляются слева направо, приоритеты операций отсутствуют. Если нужно указать иную очередность вычисления
выражения, то следует поставить скобки. Это обычно первая проблема,
с которой могут столкнуться программисты при прочтении кода на языке
MUMPS.
Например:
>w 1+2*3
9
Если при написании программы программист обычно при первом же
тестовом прогоне обнаруживает, что он неправильно записал выражение, то при внесении исправлений в имеющийся код могут возникнуть
ошибки. Типичная ситуация - пусть имеется код
>s a=1,b=2 i a=1 w "#",!
#
Со временем может возникнуть необходимость усложнить условие,
например a=1 и b=2 одновременно. Обычно первое что пишет программист, это:
>s a=1,b=2 i a=1&b=2 w "#",!
И условие уже не выполняется. Потому что здесь применяется очередность вычисления выражения слева направо, и сначала выполняется
сравнение a и 1, результат 1. Потом вычисляется операция & с полученным результатом (1) и значением b, получаем 1, и этот результат уже
сравнивается с значением 2. Получаем 0.
Решение конфликта состоит в расстановке приоритетов явным заданием порядка вычисления скобками:
>s a=1,b=2 i (a=1)&(b=2) w "#",!
#
Теперь все работает нормально. Вывод: нужно внимательно относиться как к написанию сложных выражений, так и к их изменению.
Поскольку автор исходного выражения может использовать отсутствие
приоритетов для расположения операций для правильного вычисления
выражения. Бояться тут нечего, надо соблюдать принятые в языке соглашения.

1.16. ОЧЕРЕДНОСТЬ ВЫПОЛНЕНИЯ 169
1.16.2 Очередность вычисления имен
Имена вычисляются слева направо, значения индексов имени вычисляются в том порядке, в котором они следуют, и вычисляются как выражения.
Например:
>k s a($i(a))=$i(a) w
a=2
a(1)=2
Здесь видно, что сначала было вычисление значения индекса (получили 1), потом значения выражения стоящего справа (получили 2).
Если нужно явно задать порядок вычисления "сначала присваиваемое
значение, потом имя", то для гарантирования следует явно разнести эти
операции на две:
>k s tmp=$i(a) s a($i(a))=tmp w
a=2
a(2)=1
tmp=1
Все на первый взгляд просто, но тут тоже есть нюанс, на который
необходимо обратить внимание. Это голая ссылка (naked indicator) и порядок ее взведения. При вычислении имени, образованном голой ссылкой, сначала вычисляются записанные программистом индексы, потом
команда использующая имя вычисляет полное имя непосредственно перед использованием, используя текущее значение голой ссылки (naked
indicator).
Например, при вычислении имени
^($h)
Сначала вычисляется выражение $h. И лишь при непосредственном
использовании имени выполняется подстановка голой ссылки. Пример:
; очищаем экспериментальную глобаль
>k ^a
; взводим индикатор голой ссылки
>i $d(^a(1))
; проверяем чему он равен
>w $zr
^a(1)

170 ГЛАВА 1. СРЕДА ИСПОЛНЕНИЯ
; выполняем присваивание с использованием
; naked indicator
>s ^(123,$zr)=$d(^(2,3))
; смотрим результат
>zw ^a
^a(2,123,"^a(1)")=0
В этом примере четко видно, в какой последовательности реально было вычислено имя глобала для присваивания. Если бы вычисление имени
выполнялось слева направо, то сначала был бы взят naked indicator (значение ˆa(1)), и от него было бы образовано имя
^a(123,"^a(1)")
Но, в действительности, стандарт языка MUMPS предписывает вычислять слева направо лишь значения индексов имени, и применять голую ссылку непосредственно при использовании имени.
В нашем примере выполняется сначала вычисление индексов, и имя
присваивания развертывается с значения
^(123,$zr)
до
^(123,"^a(1)")
После чего вычисляется правая часть выражения - берется голая
ссылка (ˆa(1)) и от нее вычисляется новое имя (ˆa(2,3)). По этому имени
берется выражение ($d() в нашем случае возвращает 0). Функция $d()
меняет индикатор голой ссылки на новое значение (ˆa(2,3)). Команда
set выполняет присваивание. При этом получает на вход имя с голой
ссылкой. Голая ссылка со значения
^(123,"^a(1)")
используя текущее значение naked indicator равный
^a(2,3)
развертывается до полного имени
^a(2,123,"^a(1)")
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
