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

7.4. ПРЕПРОЦЕССОР 471
данных, так и в значениях индексов.
В практическом применении блочный экспорт используется, насколько автору известно, редко и, в основном, для переноса больших объемов
данных, либо при необходимости выполнять быстрый экспорт. Традиционно таким способом переноса данных пользуются в основном разработчики и администраторы для переноса данных между собственными
серверами.
7.4 Препроцессор
Препроцессор относится к нестандартным дополнениям MUMPS систем,
не предусмотренным стандартом, но поддерживаемым различными производителями из соображений практической полезности.
В отличие от языков семейства Си, где препроцессор является неотъемлемым атрибутом, изначально и по умолчанию присутствующим в распоряжении программиста, в языке MUMPS его нет, и поддержка выполняется в каждой из систем самостоятельно производителями. При этом
различные реализации поддерживают большинство основных особенностей для синтаксической совместимости исходных текстов и различные
собственные особенности препроцессинга рутин.
Основной единицей трансляции в строчно - ориентированном языке является совокупность строк рутины. Именно по этой совокупности
строк система исполнения отсчитывает смещения относительно меток.
И задачей препроцессора является получение такого непосредственного
кода на языке MUMPS. Если в языке Си номера строк до препроцессирования сохраняются для отладчика и директивы __LINE__, то в системах
MUMPS такое соответствие не сохраняется и нет прямого соответствия
между номером строки в рутине с макросами и номером строки для
системы построчного исполнения. В определенном смысле это может доставить непривычные неудобства при первых применениях.
Препроцессор, несмотря на то, что появился в MUMPS системах много лет назад, до сих пор применяется редко и большинство разработок,
особенно ориентированных на возможность портирования на различные
реализации, его не используют.
По организации работы большинство препроцессоров использует три
вида рутин - стандартные рутины, определенные в языке MUMPS, называемые также рутинами непосредственного кода (INTermediate routines),
рутины содержащие макросы - макрорутины (MACro routines) и рутины,
предназначенные для включения при препроцессировании - включаемые
рутины (INClude routines).

472 ГЛАВА 7. ПРАКТИКА ПРИМЕНЕНИЯ
Имена различаются при их использовании по условному расширению,
например:
ROUNAME.MAC
ROUNAME.INC
ROUNAME.INT
Существуют также реализации MUMPS систем, в которых организация макрокода иная, и различаются только два типа рутин - INT и
MAC, при этом считается, что в качестве включаемых (INC) рутин используются MAC рутины с соответствующим именем.
Поддерживаются правила компиляции: 1) INC рутины не компилируются и не порождают исполняемый байткод, но используются MAC
рутинами для включения, 2) INT рутины компилируются в исполняемый байткод и 3) MAC рутины транслируются препроцессором макросов в INT рутину с тем же именем, после чего она компилируется в
исполняемый байткод.
Макрорутины по своему назначению являются исходным текстом для
получения INT рутины и последующего исполняемого байткода. При
препроцессировании препроцессор сканирует строку за строкой последовательно и, если в строке есть директивы препроцессора, то строка
соответствующим образом изменяется или не включается в выходную
INT рутину. Рутина с макросами может содержать просто текст на языке MUMPS без директив препроцессора, смешивание MUMPS кода с
директивами препроцессора, или одни только директивы на усмотрение
программиста.
Рутины разных типов могут иметь одинаковые имена. Текст рутин
хранится в разных глобалах. После трансляции MAC рутины полученный INT код по умолчанию доступен для редактирования и просмотра,
в некоторых реализациях может использоваться дополнительная опция
не сохранять сгенерированный промежуточный код INT рутины.
Макросы поддерживаются только в макрорутинах. В косвенных выражениях, в аргументе команды XECUTE, и в командном режиме макросы не поддерживаются. В этом случае код и подстановки косвенности
выполняются вне контекста препроцессора.
Для совместимости с традиционной разработкой без использования
макросов обычно поддерживается два вида трансляции - ориентированный на чистый MUMPS код и ориентированный на препроцессор. При
этом, если присутствует MAC рутина, то она сначала препроцессируется
для получения INT кода, и дальше вызывается транслятор INT кода. Если было указано явно расширение (или тип) рутины, то компилируется
именно этот вариант.

7.4. ПРЕПРОЦЕССОР 473
В общем случае инструкции препроцессора состоят из директив пре-
процессора и макроподстановок.
При трансляции макрокода директивы выполняются, изменяя состояние внутренних определений препроцессора, или управляя условием
обработки кода. Если препроцессор встречает макроподстановку, то заменяет ее по месту на ее определение с возможными указанными для
нее параметрами.
Директивы практически всех препроцессоров состоят из условных
директив
1. #if
2. #ifdef
3. #ifndef
4. #else
5. #endif
директив определения макроподстановок
1. #define
2. #undef
и директив управления
1. #include
2. #execute
В зависимости от реализации MUMPS системы могут поддерживать
также дополнительные директивы и способы макроподстановок.
Условные директивы указывают препроцессору на необходимость или
продолжить обработку или не выполнять обработку группы строк до
окончания действия директивы или до переключения условия директивой #else. При этом директивы #ifdef и #ifndef проверяют существует
ли определение макроподстановки, а директива #if вычисляет MUMPS
выражение и программист может обращаться ко всем возможностям
MUMPS системы, например вызвать функции или прочитать значение
глобалов.
Директивы #define и #undef создают или удаляют определение макроподстановки.

474 ГЛАВА 7. ПРАКТИКА ПРИМЕНЕНИЯ
Директива #include включает указанную директиве INC рутину и
препроцессор продолжает обработку кода с первой строки этой рутины,
как если бы ее текст был целиком вставлен вместо строки с директивой
#include.
Директива #execute выполняет аргумент как последовательность команд языка MUMPS, как если бы они были аргументом команды xecute.
В этой директиве, как и в директиве #if, разработчик может обращаться
к функциям, глобальным или локальным переменным.
Макроподстановки развертываются в их определение, и в определении макроподстановки и в качестве их аргументов также могут использоваться другие макроподстановки. Генерируемый код INT рутины
порождается на момент препроцессирования так, как определено макросом. В частности, если макрос вычисляет значение, зависимое от времени, или от версии, или от состояния глобалов на момент трансляции, то
порожденный INT код будет содержать именно эти значения.
В большинстве случаев препроцессор макросов используется в качестве простого инструмента подстановки. Но при этом механизм препроцессирования довольно мощный, и может порождать другие рутины, или
генерировать текст, вызывая сложные функции. Технически разработчику доступны все возможности самой MUMPS системы для генерации
подстановок или выполнения произвольных действий на момент трансляции макрокода.
Общая схема подстановок состоит в определении имени макроса и
его аргументов и в использовании его там где необходимо выполнить
такую подстановку. Например, код:
#define DGLO(%id) ^AR67ED("tools",46,%id)
...
s $$$DGLO(idrec)=$$value(idrec)
развертывается в код
s ^AR67ED("tools",46,idrec)=$$value(idrec)
Основной задачей препроцессора макросов, таким образом, является
помощь разработчику в упрощении разработки и в сокрытии длинных и,
возможно, не очень читабельных строк в осмысленные с точки зрения
разработчика синтаксические конструкции. В техническом отношении
сложность макросов может быть произвольной.
Рутины, предназначенные для включения в макрорутины, используются преимущественно для создания набора определений макросов. Но
также могут содержать и традиционный MUMPS код. В этом случае

7.4. ПРЕПРОЦЕССОР 475
он будет включаться в генерируемую INT рутину как он указан. Включаемые INC рутины могут включать другие включаемые INC рутины и
макрорутина (MAC) может включать несколько включаемых INC рутин.
Кроме того, что определение макросов создает более читабельный
для разработчика словарь терминов, упрощая написание программ и
устраняя возможность опечаток, вторым практическим преимуществом
прероцессора является согласованность MAC рутин по использованию
магических констант, определенных в одном месте, во включаемой INC
рутине.
К третьему практическому преимуществу относится то, что препроцессоры в MUMPS системах используют проверку, существует ли определение для подстановки, прежде чем ее выполнить. В случае если ее не
существует, препроцессор выдает диагностическое сообщение об ошибке трансляции. Это поведение дает разработчикам механизм проверки
на опечатки в именах переменных или функций или рутин. Для самого
языка MUMPS при трансляции строки не важно, существует ли такая
переменная или рутина, он не имеет такой информации. Но препроцессор уже может проверить опечатки по текущему набору определений
макроподстановок. При разработке крупных прикладных систем, использующих множество имен переменных, функций и рутин, такие проверки
могут существенно сократить число ошибок уже на этапе кодирования.
Хотя препроцессор и поддерживается различными системами, но к
его недостаткам можно отнести то, что в MUMPS системах не встречается утилита, аналогичная утилите MAKE, чтобы автоматически перекомпилировать MAC рутины, если изменились включаемые ими INC
рутины или перекомпилировать INT рутины если изменились соответствующие им MAC рутины. Тем не менее, такую утилиту можно составить самостоятельно, с учетом особенностей прикладного проекта.
Рассмотрим в качестве примера построение таких зависимостей. Для
того, чтобы вести информацию о зависимостях MAC файлов от INC файлов, необходимо где-то дополнительно сделать отметку о том, что при
трансляции MAC файла были транслированы включенные INC файлы.
Пусть такая зависимость ведется в глобали
^DEPENDS(macroutine,incroutine)=""
При препроцессировании INC рутины необходимо выполнить отметку
о том, что она препроцессировалась:
#execute s ^mtemp1("INC",$j,incroutine)=""
Эту строку вставляем в текст INC рутины.

476 ГЛАВА 7. ПРАКТИКА ПРИМЕНЕНИЯ
При препроцессировании MAC рутины просто вносим записи, полу-
ченные при обработке INC рутин:
#execute k ^DEPENDS(macroutine)
#execute m ^DEPENDS(macroutine)=^mtemp1("INC",$j)
#execute k ^mtemp1("INC",$j)
Эти строки вставляем в текст MAC рутины. Удаление пройденных
INC рутин нужно для того, чтобы информация о транслированных INC
рутинах не использовалась при последующей трансляции другой MAC
рутины.
При трансляции таких рутин будут автоматически заполняться зависимости MAC рутин от INC рутин в глобале
^DEPENDS(macroutine,incroutine)=""
Далее остается получить имена MAC рутин и INC рутин либо автоматически используя особенности препроцессора, либо явно указав
текущее имя, например если рутина ETRAP.INC, то вставить строку
#execute s ^mtemp1("INC",$j,"ETRAP")=""
Далее, при реализации утилиты MAKE, необходимо просто соблюсти
правила трансляции:
1. Проверять необходимость перекомпиляции по списку рутин, входящих в определенный проект.
2. Если есть INT рутина, но нет ее байткода, то транслировать.
3. Если есть MAC рутина но нет INT рутины и байткода, то транслировать.
4. Если дата изменения байткода раньше чем INT рутины или MAC
рутины то транслировать.
5. Если MAC рутина зависит от какой-либо INC рутины и есть INC
рутина с датой изменения позже чем MAC рутина, то транслировать.
Кроме того, нужно определить по документации на используемую
MUMPS систему, как именно можно получить дату и время последнего
изменения INC, MAC, INT рутин и байткода, а также как именно программно вызвать трансляцию MAC и INT рутин. Эти несколько правил

7.4. ПРЕПРОЦЕССОР 477
приведены навскидку и в реальном проекте и утилите перекомпиляции,
конечно, могут учитываться более сложные правила, включая ведение
возможных зависимостей MAC рутин от версии, от изменений управляющих данных в глобалах, и так далее.
При выполнении препроцессирования разработчик может использовать информацию о текущей версии MUMPS системы и, в зависимости
от нее, управлять генерацией MUMPS кода. Например, директивами
препроцессора определять, для какой MUMPS системы или операционной системы выполняется трансляция, и использовать более удачные,
или специфические, или оптимизированные для нее возможности. Например, генерация кода блокировки на чтение в зависимости от версии
MUMPS системы:
#if $zv["MiniM"
#define MINIM
#else
#define CACHE
#endif
...
...
...
#ifdef MINIM
l +^A4("WH","MD","F",cubeId,$$LockName^ST51()):1
#else
l +(^A4("WH","MD","F",cubeId)#"S"):1
#endif
...
Здесь предполагается, что код может выполняться либо на системе
MiniM, либо на системе Cach´e.
Поскольку рутины для макропроцессора различаются по типу, то к
традиционным форматам экспорта рутин системы, поддерживающие препроцессор, дополнительно поддерживают расширенный формат экспорта рутин, сохраняющий информацию о типе рутины. Это формат RSA
(Routine Save Archive).
MUMPS системы, поддерживающие препроцессор макросов, также, в
принципе, могут быть использованы для кроссразработки для получения
кода для целевой MUMPS системы, не поддерживающей препроцессор.
Те места кода, которые должны отличаться в зависимости от версии,
должны генерироваться по условию какую из целевых систем использовать. Например, перед трансляцией пакета рутин можно внести запись
в глобал о версии и директивой #if проверить имя целевой системы.
Полученный пакет INT рутин затем может быть экспортирован в
стандартном формате и перенесен на целевую MUMPS систему.

478 ГЛАВА 7. ПРАКТИКА ПРИМЕНЕНИЯ
Вариант кроссразработки под систему, не поддерживающую макросы, очень экзотичен и специфичен, а вот вариант с кроссразработкой под
другую систему или под другую версию системы в практике встречается чаще. Но традиционно, при необходимости иметь код, работающий на
различных реализациях MUMPS, планируется иначе - либо выполнением специфичного от версии кода через xecute, либо комплектованием
прикладной системы набором специфичных для целевой системы рутин,
либо, в крайнем случае, комбинированием соответствующих команд if
или постусловий.
7.5 Формат $HOROLOG
Системная переменная $HOROLOG возвращает значение текущей даты
и времени в локальном времени системы (с учетом часового пояса).
Дата и время возвращаются в виде двух чисел, разделенных запятой.
Первое число - количество дней, прошедших начиная с пятницы, 31
декабря 1840 года. Второе число показывает количество секунд дня,
прошедших с полуночи.
Формат и точку отсчета для переменной $HOROLOG выбрал James
M. Poitras, один из разработчиков системы MDH, ранней версии современных MUMPS систем, в 1969-м году. MDH - это крупная автоматизированная система для учета медицинских сведений. С его слов:
- Я вспомнил, что старейший (видимо, наиболее старейший) гражданин США, ветеран Первой Мировой Войны, имел к тому времени
возраст 121 год. Поэтому я захотел представить дату в юлианском календаре так чтобы возраст можно было легко вычислять и представить
любую дату в виде числа. Я решил, что начальной даты отсчета 1840
должно быть достаточно.
Другая легенда выбора отсчета 1840 года гласит, что в этом году
была произведена первая запись в систему MDH. Но это шутка.
В частности, отсчет 60000 приходится на 10 апреля 2005-го года.
Формат системной переменной $HOROLOG является наиболее распространенным из всех используемых форматов дат и времени в прикладных системах на MUMPS. С этим форматом работает множество
расширенных $Z функций различных расширений, и дополнительные
расширенные системные переменные, например $ZTIMESTAMP, также
придерживаются выбранного формата.
При возврате значение переменной выглядит так:
USER>w $h
62542,57317

7.5. ФОРМАТ $HOROLOG 479
Разделитель запятая не должен при этом восприниматься как десятичная точка, это только разделитель количества дней и количества
секунд. Число секунд не дополняется лидирующими нулями. Это обстоятельство необходимо учитывать при сортировке по дате и времени.
Для приведения формата $HOROLOG к виду, допускающему сортировку, применяется или метод дополнения обоих чисел до строки или
приведение к числу секунд. При дополнении до строки обе части дополняются так, чтобы формат строки при строковой сортировке приводил к
корректной сортировке дат, например такой:
USER>s h=$h w $j($p(h,","),6)_","_$j($p(h,",",2),6)
62542, 57693
При приведении к числу секунд часто используется два метода. Первый основан на том, что в сутках содержится:
USER>w 24*60*60
86400
секунд, поэтому общее число секунд вычисляется по формуле:
USER>s h=$h w $p(h,",")*86400+$p(h,",",2)
5403686644
Для получения значений даты и времени в формате $HOROLOG из
такого числа применяются операции деления нацело и взятие остатка от
деления нацело на 86400.
Второй метод основан на условном числе секунд 100000 так, чтобы
при вычислении в результате было видно обе части - и дата и время,
каждая из частей в формате $HOROLOG:
USER>s h=$h s total=$p(h,",")*100000+$p(h,",",2)
USER>w
h="62542,58032"
total=6254258032
Для получения значений даты и времени в формате $HOROLOG обратно из такого числа применяется деление нацело и остаток от деления
нацело на 100000.
Первый вариант приведения к числу (домножение номера даты на
86400), кроме того, используется для вычисления разности дат и времени в секундах для двух дат, заданных с указанием времени.

480 ГЛАВА 7. ПРАКТИКА ПРИМЕНЕНИЯ
Нужно обратить внимание на то, что значение $HOROLOG, как системной переменной, волатильно, и два различных обращения к этой
переменной могут дать не только различные значения секунд, но и различные значения дат (при работе программы около полуночи). Поэтому
на практике применяется взятие значения $HOROLOG однократно, а затем использование этого значения в нескольких операциях вычислений.
В настоящее время современные MUMPS системы строго поддерживают лишь положительный отсчет числа дней в $HOROLOG, но в случае, если необходимо оперировать также отрицательными значениями,
необходимо проверить, как эта операция поддерживается на применяемой и целевой MUMPS системах, или написать функции преобразования
дат самостоятельно.
Нужно отметить, что корректное преобразование даты, заданной числом относительно точки отсчета в номер года, месяца и дня, а также
обратно, является не совсем тривиальной задачей, поскольку такая операция должна учитывать, что каждый 4-й год високосный, при этом
каждый 100-й не високосный, но каждый 400-й високосный.
В частности, на языке МUMPS один из вариантов декодирования
значения даты в формате $HOROLOG в значения года, месяца и дня,
может быть таким:
DATEDECO ; $horolog decoding to year, month, day
; MiniM internals
; http://www.minimdb.com
q
IsLeapYear(Y)
q (Y#4=0)&((Y#100)!’(Y#400))
DECODE(H,Year,Month,Day) ; d DECODE^DATEDECO($H,.Y,.M,.D)
n D1,D4,D100,D400
s D1=365,D4=D1*4+1,D100=D4*25-1,D400=D100*4+1
n Y,M,D,I,T,DayTable
s T=H+672046
; return zeroes if date before 0 year
i T’>0 s Year=0,Month=0,Day=0 q
s T=T-1,Y=1
f q:T<D400 s T=T-D400,Y=Y+400
s I=T\D100,D=T#D100
i I=4 s I=I-1,D=D+D100
s Y=Y+(I*100),I=D\D4,D=D#D4,Y=Y+(I*4),I=D\D1,D=D#D1
i I=4 s I=I-1,D=D+D1
s Y=Y+I,DayTable="31,28,31,30,31,30,31,31,30,31,30,31"
i $$IsLeapYear(Y) s $p(DayTable,",",2)=29
s M=1
f s I=$p(DayTable,",",M) q:D<I s D=D-I,M=M+1
; return decoded day
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
