Структура языка
Язык PL/pgSQL имеет относительно простую структуру, что объясняется в основном тем, что каждый логически обособленный фрагмент кода существует в виде функции. Хотя на первый взгляд PL/pgSQL мало похож на другие языки программирования (такие, как язык С), сходство все же существует: логические фрагменты создаются и выполняются в виде функций, все переменные обязательно объявляются перед использованием, функции получают аргументы при вызове и возвращают некоторое значение в конце своей работы.
Регистр символов в именах функций PL/pgSQL не учитывается. В ключевых словах и идентификаторах допускается использование произвольных комбинаций символов верхнего и нижнего регистров. Также обратите внимание на частое удвоение апострофов во многих местах этой главы — всюду, где обычно используются одиночные апострофы. Удвоение экранирует апострофы в определениях функций, поскольку определение функции в действительности представляет собой большую строковую константу в команде CREATE FUNCTION.
В этом разделе рассматривается блочная структура программ PL/pgSQL, комментарии, структура выражений PL/pgSQL и использование команд.
Блоки
Программы PL/pgSQL состоят из блоков. Такой метод организации программного кода обычно называется блочной структурой. Программные блоки вводятся в командах SQL CREATE FUNCTION, которые используются для определения функций PL/pgSQL в базах данных PostgreSQL. Команда CREATE FUNCTION определяет имя функции, типы ее аргументов и возвращаемого значения. Основной блок функции начинается с секции объявлений.
Все переменные объявляются (а также могут инициализироваться значениями по умолчанию) в секции объявлений программного блока. В объявлении указывается имя и тип переменной. Секция объявлений обозначается ключевым словом DECLARE, а каждое объявление завершается символом точки с запятой (;).
После объявления переменных следует ключевое слово BEGIN, обозначающее начало основного программного блока. За ключевым словом BEGIN находятся команды, входящие в блок.
Конец программного блока обозначается ключевым словом END. Основной блок функции PL/pgSQL должен вернуть значение заданного типа, а все вложенные блоки (блоки, начинающиеся внутри других блоков) должны быть завершены до достижения ключевого слова END.
Структура программного блока PL/pgSQL он пеана в листинге 11.5.
Листинг 11.5. Структура программного блока PL/pgSQL
CREATE FUNCTION идентификатор (аргументы) RETURNS тип AS '
DECLARE
объявление: [...]
BEGIN команда: [...]
END:
' LANGUAGE 'plpgsql':
Программный блок PL/pgSQL может содержать неограниченное количество вложенных блоков, которые читаются и интерпретируются по тем же правилам, что и обычные блоки. В свою очередь, они могут содержать свои вложенные блоки.
Вложенные блоки упрощают структуру кода в больших функциях PL/pgSQL. Структура вложенных блоков не отличается от структуры обычных блоков: они также начинаются с ключевого слова DECLARE, за которым следует ключевое слово BEGIN и последовательность команд, а затем ключевое слово END.
Комментарии
В PL/pgSQL поддерживаются два вида комментариев, у которых имеются аналоги в других языках программирования: однострочные и блочные (многострочные) комментарии.
Синтаксис комментариев
Комментарии первого типа — однострочные комментарии — начинаются с двух дефисов (--) и не имеют специального завершителя. Модуль лексического разбоpa интерпретирует все символы, следующие после двух дефисов, как часть комментария. Пример использования однострочных комментариев приведен в листинге 11.6.
Листинг 11.6. Однострочный комментарий
-- This will be interpreted as a single-line comment.
Комментарии второго типа — блочные комментарии — знакомы каждому, кто когда-либо программировал на других языках. Блочный комментарий начинается с последовательности символов /* и завершается последовательностью */. Они могут распространяться на несколько строк, при этом весь текст между начальной и завершающей парой /* и */ считается комментарием. Пример блочного комментария приведен в листинге 11.7.
Листинг 11.7. Блочный комментарий
/*
* This is a
* block comment. */
ПРИМЕЧАНИЕ
Хотя блочные комментарии могут содержать вложенные однострочные комментарии, вложение блочных комментариев в другие блочные комментарии не допускается.
Хороший стиль комментирования
Содержательные комментарии приносят пользу в любом языке программирования. Комментарий считается содержательным, если он помогает разъяснить, почему некоторая часть программы была написана так, а не иначе, или почему некоторая синтаксическая конструкция используется нестандартным или неочевидным способом. Простой «пересказ» программы в комментариях тоже иногда приносит пользу, по хороший комментарий должен объяснять, почему выполняются те или иные действия (а не то, что происходит в программе).
Дальнейшие примеры кода PL/pgSQL снабжаются подробными комментариями. Это сделано для того, чтобы новичкам в программировании PL/pgSQL было проще освоить язык и его практические применения.
Команды и выражения
Программы PL/pgSQL, как и в большинстве языков программирования, состоят из команд и выражений. Вероятно, вам довольно часто придется пользоваться выражениями, потому что они крайне важны для некоторых типов манипуляций с данными. Общие концепции команд и выражений одинаковы (или, по крайней мере, очень похожи) во всех языках. Если вы прежде работали с другими языками программирования, то наверняка знакомы с этими концепциями.
Команды
Команда выполняет некоторое действие в коде PL/pgSQL — например, присваивает значение переменной или выполняет запрос. Последовательность команд в программных блоках PL/pgSQL определяет порядок выполнения действий в этом блоке. Большая часть команд обычно размещается в основной части блока, находящейся между ключевыми словами BEGIN и END. Некоторые команды также могут присутствовать в секции объявлений (после ключевого слова DECLARE), но они всего лишь объявляют и/или инициализируют переменные, используемые в программном блоке.
Каждая команда завершается символом точки с запятой (;). В этом прослеживается сходство с языком SQL, в котором команды завершаются этим же символом. Почти вся оставшаяся часть этой главы посвящена типам команд, их использованию и основным задачам, решаемым при помощи команд в PL/pgSQL.
Выражения
Выражения представляют собой условную запись последовательности операций, результат которой принадлежит одному из базовых типов данных PostgreSQL. В листинге 11.8 приведена простая функция PL/pgSQL, возвращающая результат простого выражения, а в листинге 11.9 продемонстрирован результат вызова этой функции в psql.
Листинг 11.8. Использование выражений
CREATE FUNCTION a_function () RETURNS int4 AS '
DECLARE
an_integer 1nt4;
BEGIN
an_integer := 10 * 10:
return an_integer;
END:
' LANGUAGE 'plpgsql';
Листинг 11.9. Результат вызова функции a_function()
booktown=# SELECT a_function() AS output:
output
100
(1 row)
Если не считать динамических запросов (запросов SQL, выполняемых с ключевым словом EXECUTE), все выражения PL/pgSQL в функциях обрабатываются только один раз на протяжении работы серверного процесса PostgreSQL. Поскольку выражения обрабатываются однократно, константные значения (такие, как временные метки now и current) в выражениях PL/pgSQL тоже обрабатываются только один раз, что приводит к нарушению работы программ, требующих интерпретации констант на стадии выполнения. В листинге 11.10 показано, как в PL/pgSQL организуется обработка константных временных меток во время работы функции.
Функция add_sh1pment в листинге 11.11 использует многие приемы и особенности языка, описанные ниже в этой главе. Функция получает код покупателя и код ISBN книги и вычисляет код следующей поставки, увеличивая на 1 текущий максимальный код поставки, после чего вставляет запись в таблицу shipments с временной меткой now.
Если бы метка now использовалась непосредственно в команде INSERT INTO, то строка now была бы преобразована в текущее время при создании функции и полученное значение использовалось бы во всех последующих вызовах функции.
Листинг 11.10. Правильное использование временных меток
CREATE FUNCTION add_shipment (integer, text) RETURNS timestamp AS '
DECLARE
-- Объявление псевдонимов для аргументов функции,
customerjd ALIAS FOR $1:
isbn ALIAS FOR $2;
-- Объявление переменных для хранения кода поставки и текущего времени.
shipment_1d integer;
rightjiow timestamp;
BEGIN
-- Присвоить переменной текущего времени строку ''now'
right_now := ''now'':
-- Упорядочить существующие поставки по убыванию кодов
-- и присвоить первый код переменной shipment_id.
SELECT INTO shlpmentjd id FROM shipments ORDER BY id DESC:
-- Увеличить переменную shipment_id на 1.
shipment_id := shipment_id + 1:
-- Вставить запись в таблицу shipments.
-- Переменная rightjiow преобразуется к временной пометке на стадии
-- выполнения программы, вследствие чего константное значение now
-- интерпретируется заново при каждом вызове функции.
INSERT INTO shipments VALUES ( shipmentjd. customeMd. isbn. rightjnow ):
-- Вернуть временную пометку, используя константу now.
RETURN rightjiow:
END:
' LANGUAGE 'plpgsql';