Области видимости записей
Когда Вы выполняете обращение к таблице базы данных, и ABL делает запись этой таблицы доступной для использования, Вы фактически используете буфер записи. Progress создает буфер для каждой таблицы, на которую есть ссылка в операторах FIND или блоках FOR EACH, REPEAT, FOR или DO FOR. Имя буфера по умолчанию совпадает с именем таблицы. Буфер представляет собой область памяти для хранения записей в процессе передачи их между базой данных и операторами в Вашей процедуре.
Сложная бизнес-логика часто требует наличия буферов для двух и более записей из одной и той же таблицы – в этом случае необходимо описать нужное количество буферов с разными именами. При этом явно заданное имя одного из буферов может совпадать с именем таблицы в БД.
Можно определять свои собственные буферы явно:
DEFINE BUFFER <buffer-name> FOR <table-name>.
Явное задание буферов улучшает читабельность кода. Кроме того, использование явно определенного буфера повышает надежность кода, уменьшая вероятность унаследовать определение буфера из другой процедуры в стеке вызовов.
Вообще, следует заметить, что ABL предоставляет богатый набор умолчаний, что может быть полезно, но при разработке реальных бизнес-приложений следует стремиться явно описывать все Ваши определения – что может сохранить Вам массу времени при поиске неожиданных странных ошибок.
Для всех элементов в ABL определена область видимости – часть приложения, в которой вы можете ссылаться на элемент. Буферы записей не являются исключением.
Существует три вида областей видимости записи (Record Scope):
- Сильная (Strong Scope)
- Слабая (Weak Scope)
- Свободная ссылка (Free Reference).
Сильная область видимости задается конструкцией: DO FOR/REPEAT FOR. Эти блоки не являются блоками чтения записей, и задание буфера записи в заголовке блока явно указывает компилятору, что Вы намереваетесь использовать буфер только внутри блока. Попытка обратиться к записи вне блока приведет к ошибке компиляции.
Слабая область видимости возникает при использовании блоков FOR EACH или PRESELECT EACH. Эти блоки должны использовать имя буфера в заголовке, так как они являются итеративными блоками чтения записей. ABL «понимает», что буфер используется в данном блоке, но при этом не считает, что запись может быть использована исключительно внутри блока.
Любая другая ссылка на буфер приводит к возникновению свободной ссылки. Обычно это ссылки в операторах FIND. Свободная ссылка сама по себе не привязана к определенному блоку кода.
Правило 1: Каждая сильная или слабая ссылка является замкнутой в блоке.
Можно комбинировать такие блоки внутри процедуры, так как каждый из них обеспечивает видимость записи внутри блока. Это правило выполняется до тех пор, пока нет более слабых ссылок, расширяющих видимость буфера за границы этих блоков.
Пример – Программа 3.
Здесь имеется два FOR EACH блока, каждый определяет слабую видимость для буфера Customer. Блоки выполняются последовательно, и ABL может использовать один и тот же буфер без конфликта.
Программа 3. Видимость записей
FOR EACH Customer BY creditLimit DESCENDING: DISPLAY "Highest:" CustNum NAME CreditLimit WITH 1 DOWN. LEAVE. END. FOR EACH Customer WHERE state = "NH" BY CreditLimit DESCENDING: DISPLAY CustNum NAME CreditLimit. END.
Листинг компиляции данной программы приведен на Рис. 6. Из него видно, что в строке 1 начинается блок FOR, он не начинает транзакцию. Следующая строка, Buffers: sports2000.Customer – означает, что буфер Customer имеет область видимости в этом блоке. Другой блок FOR начинается в строке 7. Буфер Customer также (независимо) имеет область видимости в этом блоке.
{} Line Blk -- ---- --- 1 1 FOR EACH Customer BY creditLimit DESCENDING: 2 1 DISPLAY "Highest:" CustNum NAME CreditLimit 3 1 WITH 1 DOWN. 4 1 LEAVE. 5 END. 6 7 FOR EACH Customer WHERE state = "NH" BY CreditLimit DESCENDING: 8 1 DISPLAY CustNum NAME CreditLimit. 9 END. 10 File Name Line Blk. Type Tran Blk. Label -------------------- ---- --------- ---- ------------------------------- .\testscope.p 0 Procedure No .\testscope.p 1 For No Buffers: sports2000.Customer Frames: Unnamed .\testscope.p 7 For No Buffers: sports2000.Customer Frames: Unnamed
Рис. 6. Листинг программы
Можно смешивать блоки со слабой и сильной видимостью, смотри Программа 4.
Программа 4. Видимость записей
DO FOR Customer: FIND FIRST Customer WHERE CreditLimit > 60000. DISPLAY CustNum NAME CreditLimit. END. FOR EACH Customer WHERE state = "NH" BY CreditLimit DESCENDING: DISPLAY CustNum NAME CreditLimit. END.
Правило 2. Две слабых области видимости одного буфера не могут быть вложены.
В примере (Программа 5) это правило нарушается, что приводит к ошибке компиляции.
Программа 5. Видимость записей
DEFINE VARIABLE dLimit AS DECIMAL NO-UNDO. FOR EACH Customer WHERE state = "NH" BY CreditLimit DESCENDING: DISPLAY CustNum NAME CreditLimit. dLimit = Customer.CreditLimit. FOR EACH Customer WHERE CreditLimit > dLimit: DISPLAY CustNum NAME CreditLimit. END. END.
Правило 3: Блок со слабой видимостью не может содержать свободных ссылок на тот же буфер.
Это ограничение имеет тот же смысл, что и Правило 2. Суть ограничений Правил 2 и 3 заключается в том, что вторичное обращение к тому же буферу портит информацию, извлеченную при первом обращении. ABL распознает и запрещает такую ситуацию. Пример приведен в (Программа 6), он тоже приводит к ошибке компиляции.
Программа 6. Видимость записей
DEFINE VARIABLE dLimit AS DECIMAL NO-UNDO. FOR EACH Customer WHERE state = "NH" BY CreditLimit DESCENDING: DISPLAY CustNum NAME CreditLimit. dLimit = Customer.CreditLimit. FIND FIRST Customer WHERE CreditLimit > dLimit. DISPLAY CustNum NAME CreditLimit. END.
Правило 4: Если имеется свободная ссылка на буфер, Progress пытается расширить область видимости этого буфера до ближайшего охватывающего блока со свойствами определения видимости (FOR EACH, DO FOR, или REPEAT). Если такого блока нет, область видимости расширяется до всей процедуры.
Свободная ссылка сама по себе не определяет область видимости, но она должна быть установлена, поэтому Progress пытается ее определить, привязав к блоку. В примере (Программа 7) область видимости буфера Customer расширена до целой процедуры.
Программа 7. Видимость записей
DEFINE VARIABLE dLimit AS DECIMAL NO-UNDO INIT 0. FOR EACH Customer WHERE State = "NH" BY CreditLimit DESCENDING: IF dLimit = 0 THEN dLimit = Customer.CreditLimit. DISPLAY CustNum NAME CreditLimit. END. FIND FIRST Customer WHERE CreditLimit > dLimit. DISPLAY CustNum NAME CreditLimit.
Листинг компиляции приведен на Рис. 7. Из него видно, что область видимости буфера определена для строки 0, то есть до целой процедуры. В блоке FOR (строка 3) видимость буфера не указана, так как она уже расширена за пределы данного блока.
Это правило критически важно. В сложных процедурах комбинация использованных ссылок на буфер может заставить Progress установить область видимости буфера шире, чем Вы ожидаете. Хотя обычно это не приводит к каким либо видимым проблемам, когда Вы просто читаете данные, при выполнении транзакций это становится реальной и серьезной проблемой.
{} Line Blk -- ---- --- 1 DEFINE VARIABLE dLimit AS DECIMAL NO-UNDO INIT 0. 2 3 1 FOR EACH Customer WHERE State = "NH" BY CreditLimit DESCENDING: 4 1 IF dLimit = 0 THEN 5 1 dLimit = Customer.CreditLimit. 6 1 DISPLAY CustNum NAME CreditLimit. 7 END. 8 9 FIND FIRST Customer WHERE CreditLimit > dLimit. 10 DISPLAY CustNum NAME CreditLimit. 11 File Name Line Blk. Type Tran Blk. Label -------------------- ---- --------- ---- -------------------------------- .\testscope.p 0 Procedure No Buffers: sports2000.Customer Frames: Unnamed .\testscope.p 3 For No Frames: Unnamed
Рис. 7. Листинг программы
Правило 5: Если имеется сильная область видимости для буфера, нельзя использовать свободную ссылку, которая расширяет область видимости до любого охватывающего блока.
Смысл использования сильной области видимости, такой как блок DO FOR, – ограничить область видимости только этим и только этим блоком. Если имеется другой оператор (FIND) вне блока с сильной областью видимости, ABL должен попытаться расширить область видимости за пределы этого блока. А это разрушит сильную область видимости. Пример – Программа 8. Попытка выполнить такую программу приведет к ошибке компиляции.
Программа 8. Видимость записей
DEFINE VARIABLE dLimit AS DECIMAL NO-UNDO. DO FOR Customer: FIND FIRST customer WHERE state = "MA". DISPLAY CustNum NAME CreditLimit. dLimit = Customer.CreditLimit. END. FIND FIRST Customer WHERE Customer.CreditLimit > dLimit. DISPLAY CustNum NAME CreditLimit.
Обратите внимание на разницу между правилами 1 и 5. Вы можете иметь в процедуре несколько блоков с сильной областью видимости для одного и того же буфера. Но вы не можете ссылаться на этот буфер вне этих блоков.
Замечание:
Вы можете иметь блок со слабой областью видимости или свободную ссылку внутри блока с сильной областью видимости – требуемое при этом расширение области видимости не выйдет за пределы блока с сильной областью видимости.