Руководство по стандартам программирования на PROGRESS ABL (4GL)
Статья является переводом статьи Progress Coding Standards Manual, автор Chris Schreiber, Fast 4gl systems, inc. Перевод выполнен Сергеем Климовым, 2003 г.
Обзор
Причины, по которым были созданы эти стандарты программирования.
Для того чтобы облегчить читабельность кода и будущее сопровождение, очень важно установить и поддерживать постоянный стиль написания кода. Этот документ не освещает все возможные вопросы кодирования, т. к. наш опыт показал, что чем полнее стандарт, тем меньшее одобрение он получит. Мы не ожидаем, что все согласятся со стандартами, описанными здесь, однако, эти стандарты и руководства являются результатом многолетнего опыта и их не следует нарушать, кроме особых случаев. Документ такого рода не пишется в вакууме, и мы будем рады, если вы будете информировать нас о любых упущениях и слабых моментах, которые мы могли бы изменить, чтобы помочь другим людям начать разрабатывать качественные приложения на Progress. Это руководство предназначено для того, чтобы:- Установить оформление и стандарты программирования для разработчиков на Progress
- Дать рекомендации для групп разработчиков и менеджеров в процессе проверки кода
- Научить программированию на Progress
Рассмотрение дизайна базы данных
Правильный дизайн базы данных и соглашение по именованию её объектов, вероятно, самая важная часть разработки приложений. Для облегчения читабельности и поддержки кода, значительное внимание должно быть уделено дизайну базы данных и соглашению по именованию ее объектов.Определение
Поле таблицы, ссылающиеся на другую таблицу (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-CIndex-B может рассматриваться как лишний, кроме случая, когда Index-A уникален, а Index-B нет. Рекомендуется ограничивать имена индексов 12 символами.
Ссылочная целостность
Ссылочная целостность означает, что все значения ссылок (FK) корректны в связанной таблице. Это предполагает, что, когда ссылочное поле заполняется, должна быть сделана проверка наличия записи в связанной таблице. Если запись удаляется, должна быть сделана проверка отсутствия ссылок на эту запись в связанных таблицах, если ссылки имеются, удаление должно быть запрещено. Обычно проверки ссылочной целостности осуществляются в следующих случаях:- Значение вводится в ссылочное поле пользователем
- Запись пишется в БД
- Запись удаляется
Соглашение по именованию
Следующие основные соглашения по именованию в значительной мере повысят читаемость кода. Для того, чтобы облегчить читабельность программ на Progress и их дальнейшую поддержку, важно установить и поддерживать единый стиль программирования.Общее соглашение по именованию
Все ключевые слова PROGRESS пишутся заглавными буквами. Все остальные слова, такие как идентификаторы переменных, пишутся либо строчными буквами, либо допускается смешанное написание, ниже приведены соответствующие примеры. Для случаев, которые не рассматриваются ниже, написание строчными буквами не ключевых слов предпочтительно. Если наименование поля или таблицы состоит из одного слова, то название пишется с заглавной буквы. Для наименований, состоящих из нескольких слов, то они отделяются друг от друга символом подчёркивания («_»), и каждое слово начинается с заглавной буквы.Процедуры
Если необходима совместимость со стандартом Progress по именованию файлов, расширения файлов должны быть следующими:- .p – Процедура
- .i – Инклюдник
- .w – Форма, созданная в Application Builder (UIB)
- .cls – Класс
Переменные
Для того чтобы различать наименования переменных от полей БД, рекомендуется использовать префиксы или суффиксы. Это значительно облегчит работу по поддержке кода, чтобы отслеживать переменные в цепи процедур. Если переменная объявлена как 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.
Структура программы
Следование этим указаниям по структурированию программ, значительно облегчит понимание поведения программы. Все эти требования должны также применяться и к локальным процедурам.Общая структура
Общая структура программы должна быть следующей: Общая структура- Заголовок
- Объявление переменных
- Прочие объявления
- Формы
- Тело программы
- Точка выхода
Заголовок программы
Заголовок каждой программы должен включать следующее:- Наименование программы
- Наименование приложения / модуля
- Расположение программы / каталог
- Лицензионная информация
- История изменений / Ревизии / Имя пользователя / Дата
- Причины изменения
- Список инклюдников
- Файлы для ввода (Input)
- Файлы для вывода (Output)
- Цель и общее описание программы
Переменные
Переменные должны размещаться в следующем порядке:- Инклюдник с общесистемными переменными.
- Инклюдник со специальными переменными приложения.
- new global shared
- new shared
- shared
- local
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 фреймами
- Инклюдник с объявлениями форм
- Локальные формы
Порядок выполнения программы
Точка входа
Любые инклюдники, выполняющие инициализирующие действия, такие как формирование заголовков форм и проверка безопасности должны располагаться вместе в точке запуска. Инициализация переменных должна выполняться там же.Точка выхода
Все процедуры должны иметь единственную точку выхода. Как правило, это оператор 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”}Использование инклюдников, много параметров:
{ lookup.i &file-name = Customer &frame-attr = “NO-BOX 2 COLUMNS” &key = Customer.Cust_Num }Инклюдники не должны заканчивать блок, начатый не в инклюднике. И наоборот, инклюдники не должны начинать блок, конец которого не находится в инклюднике. Не прописывай полный путь к инклюднику. PROPATH может быть использован (динамически настраиваемый, если необходимо) для изменения каталогов во время компиляции. Не вставляйте комментарии внутрь кудрявых скобок вместе с инклюдником, чтобы не вводить в заблуждение компилятор. Не используйте комментарии внутри ссылки на инклюдник
{ lookup.i /* Это может привести к ошибке */ ¶m = “value” }
If / Then / Else
Использование NOT должно избегаться. Хороший стиль – проверять истинное условие, где только это возможно. Не рекомендуется использовать совместно AND и OR условия, но если они используются, тогда явно ограничивайте условия скобками. Помещайте каждое условие на отдельную строку с выравниванием AND/OR по вертикали. Пример смешанного использования условий с AND / ORIF 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: */ ND. /* 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.
- Неиндексированные поля
- Переменные
- Выражения
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:- Наименования файлов
- Наименования программ
- Наименования инклюдников
Операционные системы
Используйте OPSYS функцию, чтобы защитить вызовы системных функций. Пример проверки операционной системы: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 операции ввода-вывода) Для отображения константных данных используйте следующую конструкцию: Пример вывода константных данных: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 переменные:
- Переменная представляет собой константу, которая никогда не изменяется
- Переменная, всегда инициализируется перед использованием
- Откат значения переменной не желателен
- Переменная изменяется в цикле, в котором ошибка не возможна
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 код обычно структурируется по блокам, которые имеют форму процедур (программы относятся к процедурам, поэтому все изложенное для них также справедливо), триггеров, DO блоков (включая IF / THEN / ELSE), FOR EACH и REPEAT. Структуры типа: процедуры, триггеры, EACH, DO ON ERROR и REPEAT начнут транзакцию, если внутри блока содержится поиск записи с EXCLUSIVE-LOCK. Это означает, что изменения данных БД или поиск записей с EXCLUSIVE-LOCK распространят область действия транзакции до границ этих блоков. DO блоки обладают слабой областью видимости транзакции, поэтому если изменение данных происходит внутри DO блока, то область действия транзакции распространяется до следующего внешнего транзакционного блока. Пример №1REPEAT: /* транзакция начинается здесь */ /* какой-то код ....... */ 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, который обсуждался выше: Пример №5/* Начало процедуры */ 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. /* конец процедуры */Транзакции в этом примере сейчас имеют более мелкую область действия. Вы можете захотеть увеличить границы области действия транзакции: Пример №6
DO TRANSACTION: /* Транзакция начинается здесь */ FOR EACH Order EXCLUSIVE-LOCK: DELETE Order. END. END. /* Транзакция заканчивается здесь */Если произойдёт сбой системы, то ни одна запись в таблице ORDER не будет удалена. Естественно, что такая транзакция будет держать захваты на все записи таблицы, пока она не завершится.
Субтранзакции
Progress допускает только одну активную транзакцию на данный момент. Если транзакционный блок становится частью существующей транзакции, то он открывает субтранзакцию. Субтранзакции могут откатывать часть транзакции, однако изменения, сделанные субтранзакцией, не фиксируются в БД, пока не завершится основная транзакция. Одним и наиболее общих способов создать субтранзакцию является вызов процедуры внутри активной транзакции. Пример №7DO 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 будет держать на ней локировку. Локировка записи сохраняется на всей области действия транзакции или области видимости записи. Область видимости записи может быть шире, чем область действия транзакции. Захваты относятся к записи, а не к буферу (помните, что можно иметь несколько буферов, ссылающихся на одну запись). Пример №8FIND FIRST Customer. /* какой-то код */ DO TRANSACTION: /* начало транзакции */ UPDATE Customer WITH 2 COLUMNS. END. /* конец транзакции */ /* какой –то код */ PAUSE.Область видимости записи распространяется на всю эту процедуру, включая оператор PAUSE. Первый FIND применяет SHARE-LOCK на запись таблицы CUSTOMER. Когда встречается DO TRANSACTION, SHARE-LOCK превращается в EXCLUSIVE-LOCK. В конце транзакции изменения записи фиксируются и локировка записи превращается в SHARE-LOCK. Пример №9
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 также может быть использован для очищения буфера записи и снятия локировки записи в конце транзакции. Пример №10
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. Пример №11
DO TRANSACTION: /* начало транзакции */ FIND LAST Customer EXCLUSIVE-LOCK. UPDATE Customer WITH 2 COLUMNS. RELEASE Customer. END. /* конец транзакции. Все локировки сняты. */ IF AVAILABLE Customer THEN MESSAGE "Запись в таблице Customer доступна."Свободная ссылка на таблицу Customer за пределами транзакции значительно увеличивает область видимости записи (но не транзакции). Следующий пример убедительно демонстрирует, что запись не доступна за пределами транзакции. Фактически, этот код даже не компилируется. Пример №12
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 компилятора способствует значительно лучшему пониманию работы программы и часто полезно для её отладки. Вместо опции LISTING лучше иногда использовать опцию PREPROCESS, генерирующую файл со всеми включёнными в него инклюдниками и макрозаменами, который может быть впоследствии скомпилирован и выполнен. Когда написание программы завершено, в процессе её компиляции должны быть получены два дополнительных файла. Это файл листинга и 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