Этот сайт посвящается администрированию баз данных OpenEdge Progress.
Не корысти ради, а познания для!

С уважением,
Валерий Башкатов
Сайт разработан при участии компании Progress Technologies, официального дистрибьютора Progress Software Corp. на территории стран СНГ и Латвии.

RSS RSS подписка на обновления сайта

Поиск по сайту

Лучшие материалы

Orphus System
На сайте функционирует система коррекции ошибок. Обнаружив неточность в тексте, выделите её и нажмите Ctrl+Enter



Результаты опроса: Нужны ли книги по Progress OpenEdge на русском языке? (опрос проводился с мая 2009 по ноябрь 2010)

Да, нужны. Потому что будет легче понять материал - 268
Нет, не нужны. Достаточно материалов на английском языке - 10
Не знаю, мне всё равно - 6

А знаете ли вы что..



Руководство по стандартам программирования на PROGRESS



Данная статья является переводом статьи Progress Coding Standards Manual  автор Chris Schreiber
Fast 4gl systems, inc.
Корпоративная техническая документация

Fast 4GL Systems, Inc.
http://www.fast4gl.com/
220 White Horse Pike • Audubon, NJ 08106
Phone +1 877 327-8445 • Fax +1 609 547-7876
By: Christopher Schreiber
(chris@fast4gl.com)
Thanks to all that contributed their knowledge to the creation
of this document.  I would especially like to thank Donald Christie (don@cat-it.co.nz)
from Catalyst IT Limited (http://www.cat-it.co.nz)

Содержание


Обзор
  Это руководство предназначено для того, чтобы
  Это руководство не предназначено для того, чтобы
Рассмотрение дизайна базы данных 
  Определение
  Таблицы
  Поля
  Индексы
  Ссылочная целостность
Соглашение по именованию
  Общее соглашение по именованию
  Процедуры
  Переменные
  Виджеты
  Фреймы
  Буферы
  Потоки
  Блоки
  Временные таблицы
  Внешние ссылки
Структура программы
  Общая структура
  Заголовок программы
  Переменные
  Прочие объявления
  Формы
  Порядок выполнения программы
     Точка входа
     Точка выхода
Форматирование
  Отступ
  Количество операторов на строке
  Метки блока
  Пунктуация
  Комментарии
  Словарные форматы и метки
  Инклюдники
  If / Then / Else
  Оператор CASE
  Завершение программы
  Запись наименований полей
  Запись опций форматирования (Format-phrase)
  Ключевые слова, которые нельзя использовать
  Ключевые слова, которые не стоит использовать
  Сокращения
  Прочее
  Фреймы
  Транзакции
  Управление ошибками
  Чтение записей
  Локировка записей
Переносимость
  Терминалы
  Наименования
  Операционные системы
  Цвет
  Специфика операционных систем
Производительность
  Методы программирования
Работа с несколькими БД
  Использование нескольких БД
Понимание транзакции
  Обзор области действия транзакции
  Область действия блока по умолчанию
  Контроль транзакций
  Субтранзакции
Захват записей и область видимость записи
  Захват записей
  Область видимости записи
Использование файла перекрестных ссылок (XREF) и листинга
  Листинги
  XREF
 

Обзор


Причины, по которым были созданы эти стандарты программирования.

Для того чтобы облегчить читабельность кода и будущее сопровождение, очень важно установить и поддерживать постоянный стиль написания кода. Этот документ не освещает все возможные вопросы кодирования, т.к. наш опыт показал, что чем полнее стандарт, тем меньшее одобрение он получит. Мы не ожидаем, что все согласятся со стандартами, описанными здесь, однако, эти стандарты и руководства являются результатом многолетнего опыта и их не следует нарушать, кроме особых случаев.

Документ такого рода не пишется в вакууме, и мы будем рады, если вы будете информировать нас о любых упущениях и слабых моментах, которые мы могли бы изменить, чтобы помочь другим людям начать разрабатывать качественные приложения на Progress.

Это руководство предназначено для того, чтобы

   Установить оформление и стандарты программирования для разработчиков на Progress
   Дать рекомендации для групп разработчиков и менеджеров в процессе проверки кода

Представленные здесь стандарты можно применять ко всем новым программам. Целью является внедрение эффективного стиля программирования для улучшения читабельности, сопровождения и производительности программ.

Это руководство не предназначено для того, чтобы

   Научить программированию на Progress

Этот документ не предлагает никаких стандартов при использовании смартобъектов и программировании в графическом режиме. Руководство освещает только разработку на 4GL и словарь данных.

Рассмотрение дизайна базы данных


Правильный дизайн базы данных и соглашение по именованию ее объектов, вероятно, самая важная часть разработки приложений.

Для облегчения читабельности и поддержки кода, значительное внимание должно быть уделено дизайну базы данных и соглашению по именованию ее объектов.

Определение

   Поле таблицы, ссылающиеся на другую таблицу (FK), должно иметь то же самое имя, что и в оригинальной таблице
   Дублирование наименований полей недопустимо за исключением случаев со ссылками.
   Для именования полей и таблиц должны использоваться подчеркивания («_»), а не дефисы («-») во избежании проблем с SQL приложениями от других разработчиков.
   Контекстная справка должна быть введена для каждого поля
   Следует вводить описания таблиц и полей
   Необходимо вводить формат поля по умолчанию
   Для каждой таблицы всегда должен быть единственный уникальный первичный ключ.
   Не добавляйте и не удаляйте поля / таблицы / индексы без предварительного анализа последствий этого изменения.

Таблицы

   Если совместимость с ДОС желательна, то наименования таблиц должны быть ограничены  8 символами, хотя Progress позволяет 32.
   Другая причина, по которой лучше давать короткие имена, заключается в том, что Progress хранит наименования в откомпилированном R-коде. Поэтому длинные имена непосредственно влияют на размер R-кода. Это также применимо к наименованиям полей, переменных и временных таблиц.
   Для валидации удаления в словаре данных используйте иклюдники. Это позволяет изменять условия удаления даже, если база данных используется другими пользователями. Просто измените инклюдник и перекомпилируйте связанные программы.

Поля

   Рекомендуется ограничивать наименования полей 12 символами, хотя Progress позволяет 32.
   Вы должны знать об ограничениях при использовании RECID. В процессе дампа данных Progress конвертирует значения RECID в неизвестное значение (?). Использование хранящихся в базе RECID обычно требует от разработчика написание собственных методов дампа / загрузки.
   Для DECIMAL полей число хранимых знаков после запятой должно совпадать с числом десятичных знаков, указанных в формате, либо превышать его.
   Если DECIMAL поле использует в формате символ «>», то использование опции SIDE-LABELS вызовет потерю выравнивания, в зависимости от размера и отображаемого числа (даже если формат тот же самый). Поэтому для форматирования используйте символ «z».
   Формат LOGICAL полей всегда должен иметь формат истинного значения слева и ложного - справа. Формат, типа «NO/YES» может стать причиной серьезных логических проблем.
   Для CHARACTER полей избегайте использования символов «!» и «9» в формате, кроме случаев, когда без этого НЕВОЗМОЖНО обойтись. Progress не позволит ввести пустой или небуквенный символ при использовании такого формата.
   Размер метки (LABEL) должен быть ограничен 14 символами, чтобы не усекаться при отображении с опциями WITH 1 COLUMN или WITH 2 COLUMN. Если 3 или 4 колонки используются часто, то размер меток не должен превышать 12 символов.
   Метки колонок (COLUMN-LABEL) должны иметь ту же или меньшую ширину, что и формат поля для того, чтобы выступать в качестве «короткой метки» для плотных отчетов и экранов. Если место на экране ограничено не разбивайте метки колонок более чем на 2 строки.
   Избегайте использования неизвестного значения (?) в качестве значения по умолчанию, кроме дат. В числовых полях, вычисления, связанные с неизвестным значением, в качестве результата  возвращают неизвестное значение.

Пример

UPDATE qty price.           /* пользователь вводит 5 и ? */
ASSIGN total = qty * price. /* total сейчас равна ? */

   Значение по умолчанию для логических полей всегда должно быть либо YES, либо NO. Смотри пример:

Пример

Format: N/G
Label: Net or Gross
Initial: N
"N" в качестве начального значения может привести к заблуждению, что значение по умолчанию будет "Net", тогда как реальным значением по умолчанию будет NO или "G" для "Gross".

   Обязательное значение должно быть установлено в YES для полей, где неизвестное значение недопустимо. Это предотвратит порчу логических данных, как в предыдущем примере.
   Progress не проверяет синтаксис выражений валидаций до компиляции процедуры, которая использует это выражение. Как только вы меняете или вводите новое выражение валидации, тут же его проверяйте его следующим образом:

Пример

INSERT customer.

Это проверит синтаксис всех валидационных выражений для таблицы. Помните также, что валидация, указанная в словаре данных, применяется только с операторами UPDATE, SET и PROMPT-FOR.

Индексы

   Первичным индексом должен являться наиболее часто используемый индекс для чтения записей из таблицы операторами FOR EACH, а не индексы, используемые для случайного доступа. Дампирование данных будет осуществляться в этом порядке. Когда данные последовательно загружаются в новую БД, RECIDы назначаются записям по порядку во время загрузки. Когда создается первичный индекс, он будет ссылаться на последовательные данные, этим улучшается производительность при чтении данных и уменьшается место для хранения индекса.
   Индексы, включающие в себя символьные поля, должны быть объявлены как Abbreviate (кроме случаев, когда символьное поле не является последним компонентом индекса). Это позволит использовать PROMPT-FOR / FIND USING операторы, не прибегая к функции BEGINS.
   Не создавайте индексы, содержащие одинаковые компоненты

Пример

Index-A      Index-B
Field-A      Field-A
Field-B      Field-B
Field-C

Index-B может рассматриваться как лишний, кроме случая, когда Index-A уникален, а Index-B нет.

   Рекомендуется ограничивать имена индексов 12 символами.

Ссылочная целостность

Ссылочная целостность означает, что все значения ссылок (FK) корректны в связанной таблице.

Это предполагает, что когда ссылочное поле заполняется, должна быть сделана проверка наличия записи в связанной таблице. Если запись удаляется, должна быть сделана проверка отсутствия ссылок на эту запись в связанных таблицах, если ссылки имеются, удаление должно быть запрещено.

Обычно проверки ссылочной целостности осуществляются в следующих случаях:

   Значение вводится в ссылочное поле пользователем
   Запись пишется в БД
   Запись удаляется
 
Контроль ссылочной целостности может производиться в трех местах: в словаре данных (валидации), триггерах БД (CREATE, FIND, ASSIGN, WRITE и DELETE) и в коде приложения.

Используйте DELETE триггер для обеспечения ссылочной целостности и аудита при удалении, а WRITE триггер для аудита.

Соглашение по именованию

Следующие основные соглашения по именованию в значительной мере повысят читаемость кода.

Для того, чтобы облегчить читабельность программ на Progress и их дальнейшую поддержку, важно установить и поддерживать единый стиль программирования.

Общее соглашение по именованию

   Все ключевые слова PROGRESS пишутся заглавными буквами. Все остальные слова, такие как идентификаторы переменных, пишутся либо строчными буквами, либо допускается смешанное написание, ниже приведены соответствующие примеры. Для случаев, которые не рассматриваются ниже, написание строчными буквами неключевых слов предпочтительно.
   Если наименование поля или таблицы состоит из одного слова, то название пишется с заглавной буквы. Для наименований, состоящих из нескольких слов, то они отделяются друг от друга символом подчеркивания («_»), и каждое слово начинается с заглавной буквы.

Процедуры

   Если необходима совместимость со стандартом Progress по именованию файлов, расширения файлов должны быть следующими:

Расширения файлов

.p   Процедура
.i   Инклюдник
.w   Форма, созданная в Application Builder (UIB)

Переменные

   Для того чтобы различать наименования переменных от полей БД, рекомендуется использовать префиксы или суффиксы. Это значительно облегчит работу по поддержке кода, чтобы отслеживать переменные в цепи процедур. Если переменная объявлена как LIKE, то наименование поля должно содержаться в наименовании переменной.

Наименования переменных

DEFINE               VARIABLE vDate     AS   DATE         NO-UNDO.
DEFINE               VARIABLE vItem_no  LIKE Item.Item_no NO-UNDO.
DEFINE        INPUT PARAMETER ipUpdate  AS   LOGICAL      NO-UNDO.
DEFINE        SHARED VARIABLE sGLCode   AS   CHARACTER    NO-UNDO.
DEFINE GLOBAL SHARED VARIABLE gsCompany AS   INTEGER      NO-UNDO.

   Поскольку Progress позволяет ссылаться на поле с использованием полного имени db.file.field, то рекомендуется, чтобы логические имена БД (алиасы) отличались от наименования таблиц и буферов во избежание путаницы между database.file и file.field.

Виджеты

   Соглашение по именованию для виджетов должно быть такое же, как для полей и таблиц, но с префиксом, указывающим, к  какому типу объектов относится наименование. Допускаются следующие префиксы:

Наименование виджетов

br   Browse
qu   Query
cb   Combo Box
ed   Editor Box
tg   Toggle Box
rs   Radio Set
sl   Selection List

Примеры виджетов

DEFINE QUERY  quOffice_Header FOR   Office_Header.

DEFINE BROWSE brOffice_Header QUERY quOffice_Header
         DISPLAY Office_Header.Office_Number
                 Office_Header.Office_Type
         WITH TITLE “Select a Office” 8 DOWN.

DEFINE VARIABLE cbHist_Date AS DATE FORMAT “99/99/9999”
         VIEW-AS COMBO-BOX
         LIST-ITEMS 04/07/1066, 20/01/1966/27/10/1991.

Фреймы

   Как и переменные, наименования фреймов должны также включать префиксы или суффиксы для их отличия.  Кроме того, следует указывать тип фрейма (локальный или разделяемый). В многооконных приложениях, может возникнуть необходимость в нумерации фреймов.

Наименование фреймов

DEFINE FRAME fMain.

Буферы

   Буферы также должны иметь суффикс / префикс и идентифицироваться как локальные или разделяемые. Наименование таблицы БД, к которой относится буфер, должно также содержаться в наименовании буфера.

Наименование буферов

DEFINE BUFFER bCustomer FOR Customer.

Потоки

   Потоки также должны иметь суффикс / префикс.

Наименование потоков

DEFINE STREAM sReport.

Блоки

   Метки блоков должны располагаться на отдельной линии над оператором блока.

Метки блоков

MAIN-LOOP:
REPEAT:
   DISPLAY Customer.Name.
END. /* MAIN-LOOP: */

   Метки блоков также должны иметь суффикс / префикс и давать некоторую информацию о содержимом блока, который они идентифицируют.

Наименование меток блоков

MAIN-LOOP:
REPEAT:
   /* пользовательский ввод */
   TRANS-LOOP:
      /* транзакция изменения */
   END. /* TRANS-LOOP: */
END. /* MAIN-LOOP: */

   Конец помеченного блока должен иметь комментарий, содержащий идентификатор метки, включая двоеточие для быстрого / легкого доступа к началу и концу блока.

Комментарии меток блоков

MAIN-LOOP:
DO:
   /* код блока */
END. /* MAIN-LOOP: */

   Progress позволяет дублировать метки в рамках одной процедуры, однако, во избежание путаницы следует избегать подобной практики.

 Временные таблицы

   Для отличия временных таблиц от полей БД используйте префиксы. Если временная таблица, объявляется как таблица БД, то наименование временной таблицы должно содержать наименование таблицы БД. Это облегчит ее поиск в коде.

Наименования временных таблиц

DEFINE TEMP-TABLE ttCustomer LIKE Customer.
DEFINE WORKFILE   wfCustomer LIKE Customer.

   Если индекс состоит только из одного поля, то наименование индекса должно совпадать с наименованием поля.
   Когда индекс состоит из нескольких полей, то в его наименовании нужно попытаться отобразить наименования составляющих его полей.

Наименование индексов

Index Name: cust-ord

Индекс состоит из полей cust-num и ord-no.

Внешние ссылки

   Когда вывод из программы перенаправляется в ASCII файл, придерживайтесь следующий соглашений по наименованию
   Для совместимости, имя файла не должно превышать 8 символов, а расширение – 3-х.
   Во избежания коллизий с другими пользователями, которые могут также писать в ASCII файлы на диск, следующие соглашения могут быть использованы:
   Запись должна производится в пользовательский домашний каталог или специально созданный временный каталог.
   Делайте имя файла уникальным. Обычно результат ETIME функции связывается с именем пользователя для предотвращения возможных конфликтов.
   Используйте расширение .d для файлов, содержащих данные, полученные оператором EXPORT.

Структура программы[1]

Следование этим указаниям по структурированию программ, значительно облегчит понимание поведения программы.

Общая структура

Общая структура программы должна быть следующей:

 Общая структура
 Заголовок
 Объявление переменных
 Прочие объявления
 Формы
 Тело программы
 Точка выхода

Заголовок программы

Заголовок каждой программы должен включать следующее:

 Заголовок программы
 Наименование программы 
 Наименование приложения / модуля
 Расположение программы / каталог
 Лицензионная информация
 История изменений / Ревизии / Имя пользователя / Дата
 Причины изменения
 Список инклюдников
 Файлы для ввода (Input)
 Файлы для вывода (Output)
 Цель и общее описание программы
 
Переменные

Переменные должны размещаться в следующем порядке:

Переменные

Инклюдник с общесистемными переменными.
Инклюдник со специальными переменными приложения.
new global shared
new shared
shared
local

Переменные должны располагаться в алфавитном порядке для их удобного поиска.

   Все переменные, временные таблицы и параметры должны объявляться как NO-UNDO. Все исключения должны быть комментированы во избежание проблем при изменении их в будущем.
   Переменные всегда следует, по возможности, объявлять как LIKE для лучшего самодокументирования и облегчения работы по сопровождению кода. Единственным недостатком является то,  что объявления с LIKE требуют, чтобы сессия было соединена с соответствующей БД.
   Переменным не следует давать те же имена, что и полям БД во избежание путаницы.
   Все объявления NEW SHARED/SHARED переменных должны хранится в инклюдниках, в которые можно передавать NEW в качестве параметра в процедуре, где они изначально объявляются. Это упростит поддержку кода, а также предотвратит проблемы с различиями типов данных, опций NO-UNDO, размерности массивов и т.д.
   При объявлении переменных все опции декларации должны быть выровнены для удобства чтения.

Выравнивание объявлений переменных

DEFINE VARIABLE vName  AS CHARACTER FORMAT “x(10)”
                                    LABEL “Name”   NO-UNDO.
DEFINE VARIABLE vKey   AS INTEGER   FORMAT “zz9”   NO-UNDO.

   Все переменные должны объявляться только оператором DEFINE. Progress позволяет также использовать для объявления переменных операторы DISPLAY, UPDATE, FORM и другие операторы манипулирования данными, но такие объявления трудно обнаружить и поддерживать, когда они объявляются таким образом.

Прочие объявления

Другие объявления должны вводится в следующем порядке:

Прочие объявления

 new global shared stream
 new shared stream
 shared stream
 stream
 new shared buffer
 shared buffer
 buffer
 new shared temp-table
 shared temp-table
 temp-table
 new shared frame
 shared frame
 input parameter
 output parameter
 input-output parameter

Формы

Формы объявляются в следующем порядке:

Формы

 Инклюдник с new shared фреймами
 Инклюдник с new shared фреймами
 Инклюдник с объявлениями форм
 Локальные формы

   Все опции фрейма (FRAME Phrase) должны вводится только через оператор FORM для удобства поддержки.
   Для  всех форм, описанных в программе должно указываться явное имя фрейма. Это особенно важно, когда форма объявляется в начале процедуры, тем самым  область ее видимости распространяется на весь процедурный блок.

Порядок выполнения программы

Точка входа

   Любые инклюдники, выполняющие инициализирующие действия, такие как  формирование заголовков форм и проверка безопасности должны располагаться вместе в точке запуска.
   Инициализация переменных должна выполняться там же.

Точка выхода

   Все процедуры должны иметь единственную точку выхода. Как правило, это оператор RETURN на последней строке процедуры. Оператор RETURN в любом другом месте процедуры аналогичен оператору GOTO в других языках, за исключением того, что RETURN передает управление вызывающей процедуре.
   Точка выхода должна использоваться для закрытия любых фреймов, которые не должны быть видимы при возврате в вызывающую процедуру, и для инициализации любых разделяемых переменных, которые должны быть инициализированы.
 

Форматирование

Каждый программист использует свой стиль форматирования. Однако, следование нижеизложенным предложениям позволит упростить поддержку ваших программ.

Отступ

   Рекомендуется делать отступ на 3 пробела.
   Когда уровень вложенности блоков достигает точки, где каждая строка постоянно «разрывается», допускается отступ в 2 пробела, хотя это зачастую свидетельствует о плохой структуре программы.
   Все операторы внутри блока должны писаться с отступом.:

Примеры отступов

MAIN-BLOCK:
FOR EACH Customer:
   DISPLAY Customer.
   ORDER-BLOCK:
   FOR EACH Order WHERE
         Order.Cust_Num = Customer.Cust_Num
         NO-LOCK:
      DISPLAY Order.
   END. /* ORDER-BLOCK: */
END. /* MAIN-BLOCK: */

   Оператор END  всегда должен быть на одной линии под оператором начала блока, который он завершает.

Количество операторов на строке

   Для облегчения чтения на строке допускается использование только одного оператора (имеются в виду операторы, а не ключевые слова).
   Нельзя разрывать оператор с использованием символа тильды (~).

Метки блока

   Большинству REPEAT и FOR EACH блоков должна предшествовать метка блока, которая описывает функцию, выполняемую блоком.
   Метка должна располагаться на отдельной линии над началом блока (см. предыдущий пример)
   Для DO блоков метка требуется только в случаях, если они выполняют действия, отличные от простой группировки операторов, такие как объявление нового фрейма или транзакции, или когда их содержимое превышает 10-12 строк.
   Любой блок, в котором используются операторы NEXT, LEAVE или UNDO должен иметь метку, а эти в операторах долна присутсвовать явная ссылка на эту метку.

Пунктуация

   Все операторы Progress должны завершаться точкой, кроме меток и операторов блоков (REPEAT/DO/FOR), которые должны завершаться двоеточием.

Комментарии

   Все операторы блоков (FOR EACH/REPEAT) должны предваряться комментарием, особенно если это транзакционные блоки.
   Все операторы END в блоках должны содержать комментарий, совпадающий с меткой в начале блока. Исключение составляют блоки, содержащие небольшое количество операторов.

Пример комментария

/* Изменяем записи о клиентах */
CUST-UPDATE-BLOCK:
REPEAT:
   PROMPT-FOR Customer.Cust_Num.
   FIND Customer USING Customer.Cust_Num.
   UPDATE Customer WITH 1 COLUMN.
END. /* CUST-UPDATE-BLOCK: */

   Многострочные комментарии должны оформляться следующим образом:

Пример многострочного комментария

/* Это первая строка комментария
** а это вторая строка */

   Многострочные комментарии должны оформляться следующим образом:

Пример однострочного комментария

/* Это однострочный комментарий */

   Иногда нет необходимости комментировать оператор END:

Пример ненужности комментирования END

IF true-condition THEN
DO:
   MESSAGE “ True “.
END.

Словарные форматы и метки

   Предпочтительней использовать метки, форматы, валидации и помощь из словаря данных, а не перекрывать их в процедуре.
   При перекрытии словарной метки, старайтесь использовать операторы FORM или DEFINE FRAME, при условии, если фрейм объявляется с помощью них.
   Для переменных указывайте LABEL и FORMAT в операторах DEFINE или FORM, а не в других операторах манипуляции данными, таких как DISPLAY и UPDATE.
   Используйте LABEL при объявлении боковой метки и COLUMN-LABEL при объявлении меток столбцов. Ширина COLUMN-LABEL должна совпадать с FORMAT.

Пример COLUMN-LABEL

Последняя
дата                                    Последняя дата
заказа     Последняя дата заказа заказа
-------- --------------------- ---------
12/31/99       12/31/99                12/31/99

В этом примере метка «Последняя!дата!заказа» может быть лучшей, при условии что горизонтальное пространство является более ценным чем вертикальное.

Инклюдники

   Используйте общесистемные инклюдники везде, где это возможно.
   Общесистемные инклюдники, использующиеся в нескольких приложениях должны размещаться в каталоге «include».
   Специальные инклюдники приложения должны размещаться в подкаталоге приложения (например, ar/).
   Для передачи параметров в иклюдник должны использоваться именованные параметры, а не позиционные.
   Рекомендуется, чтобы вложенность инклюдников не превышала 3-х уровней, т.к. большее число уровней затрудняет отладку. «Взрыв» инклюдников при просмотре листинга, скомпилированного с использованием опции COMPILE/LISTING,  затрудняет чтение кода.
   Использование инклюдников, простой инклюдник:

Пример простого инклюд файла

{std-var.i}

   Использование инклюдников, один параметр:

Пример инклюдника с одним параметром

{std-var.i &var-type = “NEW”}

   Использование инклюдников, много параметров:

Multiple Parameter Include Files

{ lookup.i
    &file-name    = Customer
    &frame-attr   = “NO-BOX 2 COLUMNS”
    &key          = Customer.Cust_Num
}

   Инклюдники не должны заканчивать блок, начатый не в инклюднике. И наоборот, инклюдники не должны начинать блок, конец которого не находится в инклюднике.
   Не прописывай полный путь к инклюднику. PROPATH может быть использован (динамически настраиваемый, если необходимо) для изменения каталогов во время компиляции.
   Не вставляйте комментарии внутрь кудрявых скобок вместе с инклюдником, чтобы не вводить в заблуждение компилятор.

Не используйте комментарии внутри ссылки на инклюдник

{ lookup.i   /* Это может привести к ошибке */
    &param  = “value”
}

If / Then / Else

   Использование NOT должно избегаться. Хороший стиль – проверять истинное условие, где только это возможно.
   Не рекомендуется использовать совместно AND и OR условия, но если они используются, тогда явно ограничивайте условия скобками. Помещайте каждое условие на отдельную строку с выравниванием AND/OR по вертикали.

Пример смешанного использования условий с AND / OR

IF true-cond1 AND
   true-cond2 AND
  (true-cond3 OR
   true-cond4) THEN

   Для группы условий, связанных через AND, помещайте сначала условия, истинность которых маловероятна.
   Для группы условий, связанных через OR, помещайте сначала условия, истинность которых более вероятна.
   Каждая ветка IF/THEN/ELSE должна использовать DO блок, даже если он состоит из одного оператора. Недостатком является незначительный рост R-кода. Это избавит от ошибок при дальнейшем добавлении других операторов в ветку.

Пример использования DO: END. С оператором IF.

IF true-condition THEN {include.i}.

Если инклюдник содержал только один оператор, а потом в него были добавлены другие операторы, то новые операторы будут выполняться независимо от условия. Лучше использовать:

IF true-condition THEN
DO:
   {include.i}
END.

   Единственным исключением для вложенного IF/THEN/ELSE, когда на каждой ветке выполняется только один оператор.
 

Пример неиспользования DO: END. c оператором IF.

IF selection = “A” THEN RUN add.p.
ELSE
IF selection = “C” THEN RUN change.p.
ELSE
IF selection = “D” THEN RUN delete.p.

   ELSE оператор должен писаться на отдельной строке и быть выровнен с оператором IF, к которому он относится.
   Для сложных проверок, выравнивайте ORы и ANDы.

Пример сложной проверки с AND / OR

IF condition                   AND
   vBegDate > Order.Order_Date AND
   vEndDate < Order.Order_Date THEN
DO:
   /* Операторы */
END.

   Когда проверяется логическое поле или переменная, не используйте TRUE, FALSE, YES, и NO для сравнения, потому что это приводит к снижению производительности и большему R-коду .
   Используйте CASE оператор везде, где это возможно. Если необходимо использовать вложенный IF, используйте следующую конструкцию:

Пример вложенного IF / THEN / ELSE

IF condition                   AND
   vBegDate > Order.Order_Date AND
   vEndDate < Order.Order_Date THEN
DO:
   /* Операторы */
END.
ELSE
IF NOT (condition) THEN
DO:
   /* Операторы */
END.

   Не используйте «пустые» THEN и ELSE.

Пример «пустых» THEN / ELSE

IF condition THEN . ELSE .

Этот код компилируется с «пустыми» THEN или ELSE.

Оператор CASE

   Используйте оператор CASE вместо вложенных операторов IF/THEN/ELSE.

Пример использования оператора CASE

CASE selection
  WHEN “A” THEN RUN add.p.
  WHEN “C” THEN RUN change.p.
  WHEN “D” THEN RUN delete.p.
  OTHERWISE MESSAGE “Incorrect Selection.”.
END CASE.

Завершение программы

   Все программы должны иметь единственную точку выхода. Последним оператором каждой программы должен быть RETURN, и он должен быть единственным. Для того, чтобы перейти к оператору RETURN из любого места внутри программы, используйте оператор LEAVE statement.

Пример завершения программы

MAIN:
REPEAT:
   PROMPT-FOR Customer.Cust_Num.
   FIND Customer USING Customer.Cust_Num NO-ERROR.
   IF NOT(AVAILABLE(Customer)) THEN LEAVE MAIN.
   UPDATE Customer WITH 2 COLUMNS.
END. /* MAIN */
RETURN.

Запись наименований полей

   Все наименования полей должны иметь ссылку на таблицу (буфер): filename.field-name..
   Если общие таблицы используются в нескольких БД, то поле должно дополняться ссылкой на БД: dbname.filename.fieldname.
   Перечень наименований полей в операторах DISPLAY, UPDATE, FORM и т.д. должен иметь следующий вид:

Пример с перечнем полей на одной строке

DISPLAY Customer.Cust_Num Customer.Name.

   Многострочный  перечень полей. Каждое поле на строке должно писаться с отступом в 3 пробела. Также, если используется FRAME – фраза, то она должна быть на одном уровне с  полями и переменными.

Пример с перечнем полей на нескольких строках

UPDATE
   Customer.Cust_Num
   Customer.Name
   Customer.Address
   Customer.Phone
   WITH FRAME fCustFrame.

   Для отображения подмножества элементов массива используйте следующую конструкцию:

Пример использования массива

DISPLAY array[1 FOR 5].

Этот метод более удобен для чтения и сопровождения кода, чем пречисление каждого элемента массива.

Запись опций форматирования (Format-phrase)

   Когда используются опции форматирования, их следует выравнивать по вертикали или горизонтали.

Пример опций форматирования

UPDATE
   Customer.Name
      HELP “Enter Customer Name”
      VALIDATE (Customer.Name NE “”,”Customer Name Must Be Entered”)
   Customer.Contact
      AT 20
      HELP “Enter Customer Contact”.

UPDATE
   vStartDate   AT 10  HELP “Enter Start Date”
   vEndDate     AT 50  HELP “Enter Ending Date”

UPDATE
   Customer.Name     HELP “Enter Customer Name”
                     VALIDATE (Customer.Name NE “”,”Error”)
   Customer.Contact  AT 20
                     HELP “Enter Customer Contact”.

   Размещайте опции форматирования таким образом, чтобы избежать помещения двух целых значений, относящимся к различным опциям, вместе.

Пример опций форматирования, которых следует избегать

UPDATE
   Customer.Name
   Customer.Phone
   WITH RETAIN 1 8 DOWN ROW 2 2 COLUMNS.

Пример предпочтительного использования опций форматирования

UPDATE
   Customer.Name
   Customer.Phone
   WITH 2 COLUMNS 8 DOWN ROW 2 RETAIN 1.

Ключевые слова, которые нельзя использовать

   Оператор STOP: полезен только для тестирования.

   Опция USE-INDEX: жестко привязывает запрос к использованию конкретного индекса. Используйте эту опцию только тогда, когда вы точно уверены, что автоматически выбранный Progress’ом индекс не корректен и не соответствует размещению данных.

Ключевые слова, которые не стоит использовать

   OF: OF делает код проще для чтения, но скрывает наименования полей, которые используются для связи таблиц. Можно использовать, когда нет дальнейших уточнений необходимых для выбора записи. Не используйте OF и WHERE вместе.
   Функция ENTERED: флаг ENTERED легко сбрасывается операторами UNDO, RETRY, что может приводить к неправильным результатам.
   Оператор RELEASE. Использование этого оператора внутри транзакции может указывать на непонимание программистом области действия транзакции. RELAESE не может освободить записи в статусах  SHARE-LOCK или EXCLUSIVE-LOCK внутри транзакции.
   Оператор OR: Использование его в выражении WHERE может негативно сказаться на использовании индекса.

Сокращения

   Сокращение наименований полей, переменных и таблиц не допустимо.
   Следующие ключевые слова не должны сокращаться, поскольку минимально допустимое сокращение слишком короткое:

Ключевые слова, которые нельзя сокращать

ACCUMULATE
AVAILABLE
AVERAGE
ALL COLORS (BLACK, BLUE, CYAN, ETC.)
BLINK
DATE
LIGHT
NO-VALIDATE
PROMPT-FOR
RECID
SUB-AVERAGE
TRANSACTION
UNDERLINE

   Следующие ключевые слова можно сокращать, но не больше, чем это допускает Progress:

Ключевые слова, которые могут сокращаться

KEYWORD      ALLOWED
CHARACTER    CHAR
DECIMAL      DEC
DESCENDING   DESCEND
INTEGER      INT
LOGICAL      LOG

   Все прочие сокращения, поддерживаемые Progress’ом, допустимы.

Прочее

   Жесткое кодирование значений констант в любой программе не рекомендуется, кроме следующих исключений: параметры, передаваемые в инклюдник и неизменяемые константы (например, 12 месяцев в году).
   Не допускается жесткое кодирование меток клавиш.

Пример кодирования меток клавиш

MESSAGE “Enter Data and Press “ + KBLABEL(“GO”).

   Длинные строки в операторе MESSAGE, которые не умещаются в одной строке, следует кодировать:

Пример длинного оператора MESSAGE

MESSAGE “ Это очень длинное сообщение, которое не помещается на ”
        “одной строчке кода. ”.

   Обратите внимание на то, что в предыдущем примере строка отделяется пробелами с обоих концов. Это форматирование также применимо к TITLE.

Пример TITLE

FORM
  Customer.Cust_Num
  Customer.Name
  WITH TITLE “ Информация о клиенте “ CENTERED.

   Алгебраический стиль логических выражений более предпочтителен, чем стиль языка FORTRAN. Хотя допустимы оба стиля.

Примеры операторов

Предпочтительный          Вместо
a <> b            a NE b
a <= b            a LE b
a >= b            a GE b
a > b             a GT b
a < b             a LT b
a = b             a EQ b

   При использовании циклов DO WHILE / REPEAT WHILE всегда проверяйте условие «меньше» или «больше», а не «равно». Это позволит избежать зацикливания, если условие равно не встретится.

Пример допустимой проверки

TEST-BLOCK:
DO WHILE TRUE:
   /* Операторы */
   IF vCounter <= 0 THEN
      LEAVE TEST-BLOCK.
END. /* TEST-BLOCK */

Пример недопустимой проверки

TEST-BLOCK:
DO WHILE TRUE:
   /* Операторы */
   IF vCounter = 0 THEN
      LEAVE TEST-BLOCK.
END. /* TEST-BLOCK */

   Используйте оператор TRIM для усечения лидирующих пробелов в символьных полях (особенно участвующих в сортировках).
   Поля в операторе FORM должны располагаться вертикально по одному на строке для облегчения поддержки.

Пример оператора FORM

FORM
   Customer.Cust_Num
   Customer.Name
   Customer.Phone
   WITH CENTERED ROW 2.

Пример недопустимого оператора FORM

FORM Customer.Cust_Num Customer.Name Customer.Phone WITH CENTERED.

Фреймы

   Фреймы должны быть стандартизированы с использованием рамки по умолчанию или без нее. Для фреймов, используемых для вывода на принтер, следует применять опцию NO-BOX, чтобы убрать пустую строку и столбец, которые Progress выделяет для вывода «невидимой» графической рамки вверху фрейма.
   Для стандартизации ширина фрейма в отчетах, используйте стандартные значения опции WIDTH, которые совпадают с количеством символов на дюйм, которое обычно используется в принтерах:

Опции ширины фреймов

10 CPI:  75/125
12 CPI:  90/140
etc.

   Избегайте использование ширины фрейма, превышающей 132 символов, особенно потому, что это помешает пользователю отправить развернутый отчет на терминал (с использованием оператора TERMINAL) без скроллинга. Помимо этого отпадает необходимость в поддержке сжатой печати для большинства моделей принтеров.
   Для эффективного использования бумаги  и сохранения адекватных полей, используйте PAGE-SIZE 60.

Транзакции

   Операторы блоков, объявляющих транзакцию, должны явно использовать  ключевое слово TRANSACTION,что позволит компилятору отследить возможные ошибки транзакции.

Управление ошибками

   Ошибка Progress (например, ввод дублирующего значения в уникальный индекс) не должна отправлять пользователя к началу блока, который по умолчанию имеет опции (UNDO, RETRY).  Вместо этого, используйте вложенные блоки DO ON ERROR вместе с оператором NEXT-PROMPT для удержания курсора на поле, где произошла ошибка.

   Оператор UNDO никогда не должен использоваться  без операторов RETRY, NEXT, LEAVE или RETURN, хотя Progress это допускает. Всегда сопровождайте оператор UNDO соответствующим действием (RETRY, NEXT, LEAVE или RETURN) и меткой, если нужно. Хотя Progress и допускает использование оператора RETURN вместе с UNDO, но как уже отмечалось ранее, этого следует избегать.

Чтение записей

   Всегда используйте WHERE вместо OF для того, чтобы было понятно, какие поля используются для связи между таблицами. OF используйте тогда, когда нет дополнительных условий, необходимых для выбора записи. Не используйте OF и WHERE вместе.

Пример допустимого чтения записи

ORDER-BLOCK:
FOR EACH Order WHERE
    Order.Cust_Num = “1000”
    NO-LOCK:
  DISPLAY
    Order.Cust_Num
    Order.Ord_Num.
 
  ITEM-BLOCK:
  FOR EACH Order_Line
      OF Order
      NO-LOCK:
    DISPLAY
      Order_Line.Order_Line_Num
      Order_Line.Item_Num.
  END. /* ITEM-BLOCK: */
END. /* ORDER-BLOCK: */

Пример недопустимого чтения записи

ORDER-BLOCK:
FOR EACH Order WHERE
    Order.Cust_Num = “1000”
    NO-LOCK:
  DISPLAY
    Order.Cust_Num
    Order.Ord_Num.
 
  ITEM-BLOCK:
  FOR EACH Order_Line
      OF Order WHERE
      Order_Line.Order_Qty > 0
      NO-LOCK:
    DISPLAY
      Order_Line.Order_Line_Num
      Order_Line.Item_Num.
  END. /* ITEM-BLOCK: */
END. /* ORDER-BLOCK: */

   USING может применяться вместо WHERE, потому что USING указывает ключ, который используется.
   Хороший стиль программирования подразумевает размещение наименований полей, которые будут использоваться для поиска записи, слева от оператора сравнения  в выражении WHERE.

Пример выражения WHERE

FIND Customer WHERE Customer.Cust_Num = 10.

Вместо:

FIND Customer WHERE 10 = Customer.Cust_Num.

   Если транзакция допускает, все связанные записи должны искаться одним оператором FOR  EACH.

Пример составного оператора FOR EACH

FOR EACH Customer NO-LOCK,
    EACH Order NO-LOCK WHERE
         Order.Cust_Num = Customer.Cust_Num,
    EACH Order_Line NO-LOCK WHERE
         Order_Line.Order_Num = Order.Ord_Num:
END.

   Когда в выражении WHERE  содержится несколько условий, используйте следующую форму записи (обратите внимание на отступы):

Пример выражения WHERE  с несколькими условиями

Пример 1:

FOR EACH Customer WHERE
    Customer.Cust_Num > 10  AND
    Customer.Cust_Num < 100 AND
    Customer.State    = “MA”
  NO-LOCK:
END.

Пример 2:

FOR EACH Customer NO-LOCK WHERE
      Customer.Cust_Num > 10  AND
      Customer.Cust_Num < 100 AND
      Customer.State    = “MA”,
    EACH Order NO-LOCK WHERE
      Order.Cust_Num    = Customer.Cust_Num AND
      Order.Order_Num   > 100
    BREAK
      BY Order.Order_Date
      BY Customer.Name:
END.

   Когда оператор PROMT-FOR следует за оператором FIND, используйте опцию NO-ERROR в операторе FIND, только, если вы не хотите использовать обработку ошибок по умолчанию (сообщение об ошибке, следующее за UNDO, RETRY в ближайшем блоке с UNDO). Проверка с использованием функции AVAILABLE всегда следует за опцией FIND/NO-ERROR.
   За FIND/NO-WAIT должна всегда следовать проверка с помощью функции LOCKED.
   Сравнения в выражении WHERE должны идти в таком порядке:

Порядок расположения сравнений в выражении WHERE

Поля индексов, которые используются операторами FIND/FOR EACH. Сравнения должны располагаться в том же самом порядке, что и поля в индексах.

Поля из индексов, которые не используются операторами FIND/FOR EACH.

Неиндексированные поля

Переменные

Выражения

   Используйте оператор FIND (без FIRST/NEXT/PREY/LAST) только с уникальным индексом и только, когда WHERE выражение включает в себя все поля уникального индекса, т.к. FIND ищет единственную запись по уникальному индексу. Для остальных ситуаций используйте FIND с опциями FIRST/NEXT/PREY/LAST.
   Не используйте FOR блок для поиска одной записи, связанной отношением один-ко-одному.

Не корректное использование FOR для поиска записи, связанной отношением один-ко-одному

FOR EACH Order_Line:
   DISPLAY Order_Line.
   FOR EACH Item OF Order_Line:
      DISPLAY Item.
   END.
END.

Использование оператора FOR EACH при поиске записи ITEM вводит в заблуждение, т.к. это дает повод думать, что отношения между таблицами order-line и item один-ко-многим, а не один-ко-одному.

   При проверке логических полей и переменных в выражении WHERE всегда явно указывайте истинное или ложное значение для поля с целью улучшения производительности.

Локировка записей

   Используйте NO-LOCK везде, где состояние информации не критично (большинство отчетов и запросов), для снижения вероятности конфликтов захвата и улучшения производительности.
   В случаях, когда допускается, что пользователь будет изменять существующую запись и запись не является захваченное, читайте запись с EXCLUSIVE-LOCK. Это снизит возможность залочивания (deadlock) записи, а также даст небольшой выигрыш в производительности.
   SHARE-LOCK не следует использовать никогда. Использование SHARE-LOCK может приводит к залочиванию (deadlock) записи.
   Всегда явно указывайте статус локировки. Использование параметра –NL запрещено.

 

Переносимость

С тех пор как Progress программы могут использоваться на различных платформах, очень важно при программировании учитывать некоторые вопросы переносимости.
 
Терминалы

   Т.к. Progress может использовать терминалы с различным количеством строк (25 для DOS, 24 для большинства ASCII терминалов), используйте следующие функции для вычисления доступного количества строк и для максимального использования всего доступного экранного пространства.

Функции проверки экрана

SCREEN-LINES
FRAME-DOWN
FRAME-ROW
FRAME-LINE

В опциях фрейма:

expression DOWN
ROW expression

Наименования

   Для совместимости с DOS следующие объекты должны быть ограничены 8 символами.

Ограничения DOS

Наименования файлов
Наименования программ
Наименования инклюдников

   Расширение файлов ограничено 3 символами.
   Точка не должна использоваться в наименовании файла, кроме разделения наименования файла от расширения.
   Наименования всех программ и инклюдников в коде должны быть строчными буквами.

Операционные системы

   Используйте OPSYS функцию, чтобы защитить вызовы системны функций.
   Use the OPSYS function to shield operating system calls.

Пример проверки операционной системы

CASE OPSYS
   WHEN “unix”     THEN UNIX.
   WHEN “msdos”    THEN DOS.
   WHEN “vms”      THEN VMS.
   WHEN “btos”     THEN BTOS.
   WHEN “os2”      THEN OS2.
   WHEN “os400”    THEN OS400.
   WHEN “nt”       THEN NT.
   OTHERWISE
     MESSAGE “Unknown Operating System:” OPSYS.
END CASE.

Цвет

   Используйте опцию COLOR VALUE (color-variable)  во всех фреймах. Придерживаясь этого правила легко перенести приложение с монохромного монитора на цветной.

Специфика операционных систем

   Оператор OUTPUT THRU является очень полезным и мощным, однако, не поддерживается Progress’ом на всех операционных системах.
   AUTO-RETURN никогда не должен использоваться.
   Когда транзакция завершается, пользователь должен быть информирован сообщением, что транзакция успешно завершена и зафиксирована в БД.
   Предупреждения должны предваряться словом "ПРЕДУПРЕЖДЕНИЕ:"
   Сообщения об ошибках должны предваряться словом "ОШИБКА:".  Сюда относятся, сообщения в валидациях как в словаре данных, так и валидациях фрейма.
   Все сообщения об ошибке должны сопровождаться гудком с помощью оператора BELL.
   Сообщения справки должны предваряться словом "Введите:".
  

Производительность

Хотя производительность и не является  первоочередным вопросом в программировании, важно придерживаться некоторых основных правил.

Методы программирования

   CAN-FIND болен эффективен по производительности, чем FIND, т.к. он только проверяет наличие записи по индексу (обычно 1 операция ввода-вывода ), тогда как FIND обрабатывает саму запись (минимум 2 операции ввода-вывода)
   CAN-FIND is more efficient than FIND because CAN-FIND only looks at the index (usually 1 I/O operation) where FIND retrieves the entire record (minimum of 2 I/Os).
   Для отображения константных данных используйте следующую конструкцию:

Пример вывода константных данных

FORM
  “Эта строка - константа”
  WITH FRAME fTest.
VIEW FRAME fTest.

/* Это более эффективно: */

DISPLAY “Эта строка - константа”.

   Для нескольких подряд идущих присвоений, лучше использовать оператор ASSIGN.

Пример оператора ASSIGN

ASSIGN
  vVar1 = vVar2
  vVar3 = vVar4.

/* Предыдущий код более эффективен и генерит меньший R-код, чем             ** следующий: */

vVar1 = vVar2.
vVar3 = vVar4.

   Минимизируйте использование оператора RUN (заменяйте на инклюдники, если возможно) и при использовании RUN оператора, старайтесь указывать полный путь к имени процедуры. Использование относительного пути от корневого каталога приложения позволяет достичь большей гибкости при разработке и развертывании приложения.
   Минимизируйте вызовы функций операционной системы
   Для разделения записи между программами (процедурами) используйте SHARED BUFFER вместо RECID, хранящегося в разделяемой переменной. Кроме случаев, когда чтение осуществляется с опцией NO-LOCK в вызывающей процедуре (нет  транзакции) и перечитывание записи с EXCLUSIVE-LOCK в вызываемой процедуре для ограничения транзакции  вызываемой процедурой.
   Изменение значений переменных в транзакции приводит к журналированию изменений в локальном BI-файле (.lbi). Для уменьшения объема журналирования, объявляйте переменные с опцией NO-UNDO:

Когда используются NO-UNDO переменные

Переменная представляет собой константу, которая никогда не изменяется

Переменная, всегда инициализируется перед использованием

Откат значения переменной не желателен

Переменная изменяется в цикле, в котором ошибка не возможна

   Чтение записей с NO-LOCK освобождает Progress проверять таблицу локировок, тем самым немного улучшая производительность.
   Читайте записи с опцией EXCLUSIVE-LOCK вместо SHARE-LOCK по умолчанию, т.к. SHARE-LOCK изменяется на EXCLUSIVE-LOCK, когда запись обновляется.
   Для сравнения строк используйте функции CAN-DO, LOOKUP или INDEX вместо множества сравнений, соединенных оператором OR (но не при поиске записи).

Примеры функций CAN-DO и LOOKUP

CAN-DO(“PA,NJ,DE”,vState) /* Это более эффективно */

LOOKUP(vState,”PA,NJ,DE”) <> 0

vState = “PA” OR vState = “NJ” OR vState = “DE” /* Не делайте так */

   Для повышения производительности и снижения издержек, связанных с открытием / закрытием транзакции (например, пакетное создание большого числа записей), используйте ограничивающие циклы.

Пример уменьшения транзакции

INPUT FROM datafile.
REPEAT TRANSACTION:
  REPEAT vCounter = 1 TO 100:
    CREATE Customer.
    IMPORT Customer.
  END.
END.

/* Является более эффективным чем: */

INPUT FROM datafile.
REPEAT:
  CREATE Customer.
  IMPORT Customer.
END.

   При загрузке данных в БД, используйте опцию NO-ECHO в операторе INPUT FROM.
   Загрузку данных в БД осуществляйте оператором IMPORT, а не SET или UPDATE.
   Используйте WORKFILE только для хранения фиксированного количества записей (лучше одной). Создание неограниченного количества записей в WORKFILE может привести к краху системы, при выходе за значение локального буфера (-l). Изменение размеров записей WORKFILE является потенциальной проблемой производительности. Используйте WORKFILE для хранения одной записи, для всех остальных случаев, используйте TEMP-TABLE.

 

Работа с несколькими БД

Работа с несколькими БД может выявить ряд проблем, которые должны быть учтены.

 
Использование нескольких БД

   Не используйте свойство автоматического соединения (auto-connect). Это может привести к неожиданным паузам в процедуре, где устанавливается соединение с БД.
   Для поддержания модульной организации программы, помещайте код, осуществляющий соединения с БД в начало пользовательской сессии.
   Используйте опцию NO-ERROR для оператора CONNECT, для дальнейшей проверки ее успешности используйте функцию CONNECTED.
   Используйте один оператор CONNECT для одной БД. Если в одном операторе CONNECT указано соединение с несколькими БД и с одной из баз соединение не произошло, то все последующие соединения не будут осуществлены.

 

Понимание транзакции

Транзакции, и то, как Progress ими управляет, часто является трудным для понимания программистов.

Способ управления транзакциями является одним из уникальных и мощных особенностей 4GL. Это та область, с которой мы пытаемся обычно бороться, так воображаем себе, что этот предмет более сложен, чем он является на самом деле.

Это, однако, важный предмет, т.к. плохое планирование и безответственное программирование может привести к неблагоприятным и  опасным последствиям работы вашего приложения, как со стороны пользовательского интерфейса, так и со стороны производительности и физической целостности БД.

 
Обзор области действия транзакции

Область действия транзакции – это логически сгруппированное изменение данных, для которого Progress гарантирует, что все изменения будут зафиксированы в БД, либо целиком откачены. Причины отката транзакции могут быть самыми различными, начиная от решения пользователя прекратить текущий набор изменений (нажатие ESC или F4), заканчивая ошибками в приложении и сбоями аппаратного обеспечения.

Гарантируется, что каждая завершенная транзакция будет немедленно записана в БД, тогда как любая незавершенная транзакция не будет записана в БД, если она будет прервана.

Если вы не уверены, активна ли в данный момент транзакция в вашей программе, используйте функцию TRANSACTION, которая возвращает истину, если транзакция в данный момент активна.
 

Область действия блока по умолчанию

Progress код обычно структурируется по блокам, которые имеют форму процедур[2], триггеров, DO блоков (включая IF / THEN / ELSE), FOR EACH и REPEAT. Структуры типа: процедуры, триггеры, EACH, DO ON ERROR и REPEAT начнут транзакцию, если внутри блока содержится поиск записи с EXCLUSIVE-LOCK.

Это означает, что изменения данных БД или поиск записей с EXCLUSIVE-LOCK распространят область действия транзакции до границ этих блоков.

DO блоки обладают слабой областью видимости транзакции, поэтому если изменение данных происходит внутри DO блока, то область действия транзакции распространяется до следующего внешнего транзакционного блока.
 

Пример №1

REPEAT: /* транзакция начинается здесь */
    /* какой-то код ....... */
FIND FIRST Order EXCLUSIVE-LOCK NO-WAIT NO-ERROR.
/* проверка захвата ....... */
UPDATE Order.
    /* какой-то код ....... */
END. /*транзакция заканчивается здесь */

Пример № 2

REPEAT: /* Транзакция НЕ НАЧИНАЕТСЯ здесь */
    /* какой-то код ....... */
    REPEAT: /* транзакция начинается здесь */
        FIND FIRST Order EXCLUSIVE-LOCK NO-WAIT NO-ERROR.
        /* проверка захвата ....... */
        UPDATE Order.
    END. /* транзакция заканчивается здесь */
    /* какой-то код ....... */
END. /* repeat */

Пример №3

REPEAT:    /* транзакция начинается здесь */
    /* какой-то код */
    DO WHILE TRUE:
        FIND FIRST Order EXCLUSIVE-LOCK NO-WAIT NO-ERROR.
        /* Проверка захвата ....... */
        UPDATE Order.
    END. /* DO WHILE */
    /* какой-то код....... */
END. /*транзакция заканчивается здесь */

Пример №4

/* Начало процедуры начинает здесь транзакцию */
FIND FIRST Customer EXCLUSIVE-LOCK NO-WAIT NO-ERROR.
/* Проверка захвата ....... */
UPDATE Customer.
/* какой-то код */

REPEAT:
    /* какой-то код.... */
    FIND NEXT Order OF Customer EXCLUSIVE-LOCK NO-WAIT NO-ERROR.
    /* Проверка захвата ....... */
    UPDATE Order.
    /* какой-то код.... */
END.

/*Конец процедуры заканчивает здесь транзакцию */

Контроль транзакций

Для того чтобы объявить блок транзакционным, используйте ключевое слово TRANSACTION (заметьте, что оно отличается от функции TRANSACTION, обсуждавшейся ранее), если это возможно.

Этот метод может быть использован как для уменьшения транзакции, так и для увеличения. Возьмем пример № 4, который обсуждался выше:

Пример №4

/* Начало процедуры */
DO TRANSACTION: /* Транзакция начинается здесь */
   FIND FIRST Customer EXCLUSIVE-LOCK NO-WAIT NO-ERROR.
   /* Проверка захвата ....... */
   UPDATE Customer.
   /* какой-то код */
END. /* Транзакция заканчивается здесь */

/* какой-то код */

REPEAT:
    /* какой-то код.... */
    DO TRANSACTION: /* Другая транзакция начинается здесь */
       FIND NEXT Order OF Customer EXCLUSIVE-LOCK NO-WAIT NO-ERROR.
       /* Проверка захвата ....... */
       UPDATE Order.
       /* какой-то код.... */
    END. /* Вторая транзакция заканчивается здесь */
END.

/* конец процедуры */

Транзакции в этом примере сейчас имеют более мелкую область действия.

 
Вы можете захотеть увеличить границы области действия транзакции:

Пример №5

DO TRANSACTION: /* Транзакция начинается здесь */
   FOR EACH Order EXCLUSIVE-LOCK:
      DELETE Order.
   END.
END. /* Транзакция заканчивается здесь */

Если произойдет сбой системы, то ни одна запись в таблице ORDER не будет удалена. Естественно, что такая транзакция будет держать захваты на все записи таблицы, пока она не завершится.

 
Субтранзакции

Progress допускает только одну активную транзакцию на данный момент. Если транзакционный блок становится частью уже существующей транзакции, то он открывает субтранзакцию.

Субтранзакции могут откатывать часть транзакции, однако изменения, сделанные субтранзакцией, не фиксируются в БД, пока не завершится основная транзакция.

Одним и наиболее общих способов создать субтранзакцию является вызов процедуры внутри активной транзакции.

Пример №6

DO TRANSACTION ON ERROR UNDO, RETRY: /* транзакция начинается здесь */
    IF RETRY THEN
        MESSAGE "Вы также откатили изменение информации о клиенте."
    IF NOT CAN-FIND Customer OF WHERE Customer-Name = "Test") THEN
        RUN newcust.p.
    FIND FIRST Order EXCLUSIVE-LOCK.
    UPDATE Order WITH 2 COLUMN.
END.

/**** newcust.p *****
/* Программа добавления нового клиента */

/* какой-то код */

DO TRANSACTION ON-ERROR UNDO, LEAVE: /* начинаем субтранзакцию */
    FIND FIRST Customer EXCLUSIVE-LOCK.
    UPDATE Customer WITH 2 COLUMNS.
END. /* конец субтранзакции */

Программа newcust.p может и не ожидать того, что она вызвана внутри другой транзакции. Любое изменение, сделанное в newcust.p может быть откачено, если  в вызывающей процедуре пользователь нажмет ESC (F4).

Другим побочным эффектом этого примера, является то, что локировка на запись в таблице CUSTOMER будет сохранена до конца основной транзакции в вызывающей процедуре.

 

Захват записей и область видимость записи

Понимание механизмов захвата записи и области видимости записи является существенным для написания эффективных программ.

 
Захват записей

Progress осуществляет захваты на уровне отдельной записи. Это означает, что захватываются конкретные записи, а не страницы или таблицы.

Существует два типа захвата: SHARE-LOCK и EXCLUSIVE-LOCK. Кроме того, чтение записей может осуществляться без ее захвата (NO-LOCK).

SHARE-LOCK позволяет читать запись другим пользователям, пока ее не пытаются модифицировать. В этом случае SHARE-LOCK превращается в EXCLUSIVE-LOCK.

EXCLUSIVE-LOCK предотвращает возможность модификации записи любым другим пользователем и ее чтение с SHARE-LOCK.

NO-LOCK позволяет считать запись не зависимо от того, в какой локировке находится запись у других пользователей. Однако, она не может быть модифицировна. NO-LOCK осуществляет, так называемое, «грязное» чтение. Другими словами, данные которые считываются могут быть не фиксированы в БД.

По умолчанию Progress считывает запись с SHARE-LOCK, т.к. это обеспечивает для пользователя лучшую защиту. Однако, эта опция не рекомендуется, если нет такого требования. В общем случае, разработчик должен знать, что может произойти с записью и должен ее считать с NO-LOCK или EXCLUSIVE-LOCK.
 

Область видимости записи

Эта тема касается того, как долго буфер с информацией о записи остается активным, и от этого зависит, сколько Progress будет держать на ней локировку.

Локировка записи сохраняется на всей области действия транзакции или области видимости записи. Область видимости записи может быть шире, чем область действия транзакции.

Захваты относятся к записи, а не к буферу (помните, что можно иметь несколько буферов, ссылающихся на одну запись).

Пример №7

FIND FIRST Customer.
/* какой-то код */

DO TRANSACTION:  /* начало транзакции */
    UPDATE Customer WITH 2 COLUMNS.
END. /* конец транзакции */

/* какой –то код */
PAUSE.

Область видимости записи распространяется на всю эту процедуру, включая оператор PAUSE. Первый FIND применяет SHARE-LOCK на запись таблицы CUSTOMER.

Когда встречается DO TRANSACTION, SHARE-LOCK превращается в EXCLUSIVE-LOCK. В конце транзакции, изменения записи фиксируются и локировка записи превращается в SHARE-LOCK.

Пример №8

FIND FIRST Customer NO-LOCK.
/* какой-то код */

DO TRANSACTION:  /* начало транзакции */
    FIND FIRST Customer EXCLUSIVE-LOCK.
    UPDATE Customer WITH 2 COLUMNS.
END.       /* SHARE-LOCK на записи таблицы CUSTOMER активен */

Обратите внимание, что в вышеприведенном примере статус локировки после завершения транзакции остается SHARE-LOCK. Другими словами, запись в этом состоянии может только читаться другими пользователями, но не изменяться.

Имейте в виду, что поиск другой записи в той же таблице снимет SHARE-LOCK.
Оператор RELEASE также может быть использован для очищения буфера записи и снятия локировки записи в конце транзакции.

Пример №9

FIND FIRST Customer NO-LOCK.
/* какой-то код */

DO TRANSACTION:  /* начало транзакции */
    FIND FIRST Customer EXCLUSIVE-LOCK.
    UPDATE Customer WITH 2 COLUMNS.
    RELEASE Customer.
END. /* конец транзакции. Все локировки сняты. */

/* какой-то код */
PAUSE.   

В этом примере локировка снимается в конце транзакции, но тогда запись таблицы CUSTOMER становится недоступной.

Использование оператора RELEASE зачастую свидетельствует о том, что разработчик не умеет правильно контролировать запись и область действия транзакции. Кроме того, этот оператор ведет себя по-разному, если он используется в процедуре, которая вызывается внутри существующей области видимости записи. В этом случае локировка не снимается, а превращается в SHARE-LOCK в конце процедуры.

Наконец, можно сузить или расширить область видимости записи с помощью блока DO FOR tablename. Это сильно привязывает запись к блоку, и никакие ссылки на блок не могут быть сделаны за пределами этого блока без использования оператора FIND.

Пример №10

DO TRANSACTION:  /* начало транзакции */
    FIND LAST Customer EXCLUSIVE-LOCK.
    UPDATE Customer WITH 2 COLUMNS.
    RELEASE Customer.
END. /* конец транзакции. Все локировки сняты. */

IF AVAILABLE Customer THEN
    MESSAGE "Запись в таблице Customer доступна."

Свободная ссылка на таблицу Customer за пределами транзакции значительно увеличивает область видимости записи (но не транзакции).

Следующий пример убедительно демонстрирует, что запись не доступна за пределами транзакции. Фактически, этот код даже не компилируется.

Пример №11

DO FOR Customer TRANSACTION: /* начало транзакции *?
    FIND FIRST Customer EXCLUSIVE-LOCK.
    UPDATE Customer WITH 2 COLUMNS.
    RELEASE Customer.
END. /* конец транзакции. Все локировки сняты. */

IF AVAILABLE Customer THEN
    MESSAGE " Запись в таблице Customer доступна.".


Использование файла перекрестных ссылок (XREF) и листинга

Использование  опций XREF и LISTING[3] компилятора способствует значительно лучшему пониманию работы программы и часто полезно для ее отладки.

Когда написание программы завершено,  в процессе ее компиляции должны быть получены два дополнительных файла. Это файл листинга и XREF файл. Эти файлы содержат основную информацию, гарантирующую, что ваша программа функционирует, как вы ожидаете. Используйте эти файлы для отладки модулей и ревизии кода.

Листинги

Файл листинга раскрывает все инклюдники и нумерует строки программы поблочно. В конце файла дается общая информация о каждом блоке программы, о самой программе, являющейся основным блоком.

В файле показываются области видимости записи и области действия транзакции, а также области видимости фреймов.

Используйте команду COMPILE filename.p LISTING filename.lst в редакторе PROGRESS или опцию LISTING в Application Compiler для получения файла листинга

Пример листинга

1 showlist.p
     2
     3     {}    Line  Blk
     4     --    ----  ---
     5           1     1 DO FOR Account:
     6           2     2     DO TRANSACTION: /* начало транзакции*/
     7           3     2           FIND FIRST account EXCLUSIVE-LOCK
     8           4     2           MESSAGE TRANSACTION. /* "yes" or "no" */
     9           5     2                 PAUSE.
     10          6     1     END.
     11          7     1   
     12          8     2     REPEAT: /* начало транзакции */
     13          9     2           FIND FIRST policy_header EXCLUSIVE-LOCK.
     14          10    2           LEAVE.
     15          11    1     END.
     16          12    1
     17          13    1     /* показывает, что транзакция завершена */
     18          14    1     MESSAGE TRANSACTION.
     19          15    1     PAUSE
     20          16       END. /* DO FOR Account */
     21
     23  File Name     Line  Blk Type    Tran  Blk. Label
     24  ----------    ----  ------------      ----  -------------------
     25  showlist.p    0     Procedure   No
     26  showlist.p    1     Do          No
     27    Buffers: policypl.Account
     28
     29  showlist.p    2     Do          Yes
     30  showlist.p    8     Repeat            Yes
     31    Buffers:policypl.Office_Header
     32

Начало каждого блока выделяется как в самом листинге, так и в конце отчета. 

Вышеприведенный листинг показывает, что область видимости буфера ACCOUNT распространяется до внешнего блока DO FOR ACCOUNT, также видно, что никакой транзакции не начинается в этом блоке. Внутренний DO TRANSACTION блок открывает транзакцию.

Блок REPEAT содержит область видимости записи (OFFICE_HEADER) и транзакцию.

 
XREF

Опция XREF записывает информацию о перекрестных ссылках между процедурами и объектами Progress, включая процедуры, инклюдники, таблицы, поля, индексы, переменные, фреймы и символьные строки.

Разработчики должны использовать эту возможность для проверки эффективности своих запросов. Как только используется запрос, в файле XREF появляется запись, начинающаяся со слова SEARCH, за которым следует наименование таблицы и наименование индекса, который будет использоваться в запросе. Если запрос не использует индексных скобок (т.е. не существует условий, ограничивающих область поиска) или Progress не смог подобрать подходящего индекса для выполнения запроса, слово WHOLE-INDEX появляется после наименования индекса.

В последнем случае, используется наименование первичного индекса, который только определяет порядок возвращения записей клиенту. Сам запрос будет происходить по всей таблице, что необходимо избегать.

Пример программы для XREF

FIND FIRST Account NO-LOCK.  /* нет условий – нет индексных скобок */

/* условие по неиндексированному полю */
FIND FIRST Office_Header.
    WHERE Office_Header.Effective_Date = TODAY NO-LOCK

/* условие по полю, входящему во второй индекс */
FIND FIRST Office_Header.
WHERE Office_Header.Account_No = Account.Account_No NO-LOCK.

Этот пример генерирует много строк в XREF файле, ниже будут рассмотрены только строки, содержащие слово SEARCH:

Пример XREF файла

Showref.p showxref.p   1  COMPILE showxref.p
Showref.p showxref.p   1  SEARCH Account main_key WHOLE-INDEX
Showref.p showxref.p   3  SEARCH Office_Header main_key WHOLE-INDEX
Showref.p showxref.p   6  SEARCH Office_Header account

Вторая строка связана с безусловным поиском оператором FIND, это допускается.

Третья строка показывает, что поиск ограничен условием (с использованием Effective_Date), которое не может использовать индекс. Первичный индекс (main_key) указывает на порядок, в котором записи будут передаваться клиенту, слово WHOLE-INDEX указывает, что вся таблица будет просмотрена при выполнении поиска. Это должно рассматриваться как ошибка, т.к. это влечет серьезные проблемы с производительностью.

Последняя выделенная строка показывает, что поиск ограничен условием и будет выполняться с использованием индекса (account). Этот индекс был изучен и выглядит подходящим, т.к. Account_no (ограничивающее условие) является первой компонентой этого индекса.

 

Translated by © Serguey Klimoff, 2003, Russia

 
[1] Все эти требования должны также применяться и к локальным процедурам

[2] Программы относятся к процедурам, поэтому все изложенное  для них также справедливо

[3] Вместо опции LISTING лучше иногда использовать опцию PREPROCESS, генерирующую  файл со всеми включенными в него инклюдниками и макрозаменами, который может быть впоследствии скомпилирован и выполнен.




Главная |  Статьи |  Книги |  Гостевая |  Ссылки |  От автора |  Download ProKb


������ ᠩ� pr Online ProKB Blogger Welcome to Russian Progress Users Group at Facebook Welcome to Russian Progress Users Group at LinkedIn
© 2009 - 2011 Все права на материалы, находящиеся на сайте www.openedge.ru, охраняются в соответствии с законодательством РФ, в том числе, об авторском праве и смежных правах.
При любом использовании материалов сайта ссылка на источник обязательна.