RussianLDP Рейтинг@Mail.ru
WebMoney: 
WMZ Z294115950220 
WMR R409981405661 
WME E134003968233 
Visa 
4274 3200 2453 6495 

Глава 21. Сохраненные программы и представления

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

Сохраненные программы включают эти объекты:

  • Сохраненные подпрограммы, то есть, хранимые процедуры и функции. Хранимая процедура вызвана, используя CALL. Процедура не имеет возвращаемого значения, но может изменить параметры для более позднего просмотра. Это может также произвести наборы результатов, которые будут возвращены программе клиента. Сохраненная функция используется как встроенная функция. Вы вызываете это в выражении, и она возвращает значение во время оценки выражения.

  • Триггеры. Триггер названный объект базы данных, который связан с таблицей и активирован, когда особое событие имеет место для таблицы такое, как вставка или обновление.
  • События. Событие задача, которую сервер выполняет согласно графику.

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

Эта глава описывает, как использовать сохраненные программы и представления. Следующие разделы обеспечивают дополнительную информацию о синтаксисе SQL для запросов, связанных с этими объектами:

  • Для каждого типа объекта есть команды CREATE, ALTER и DROP, которые управляют тем, какие объекты существуют и как они определены. См. раздел 14.1.

  • CALL используется, чтобы вызвать хранимые процедуры. См. раздел 14.2.1.
  • Сохраненные определения программы включают тело, которое может использовать составные запросы, циклы, условные предложения и объявленные переменные. См. раздел 14.6 .

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

21.1. Определение сохраненных программ

Каждая сохраненная программа содержит тело, которое состоит из запроса SQL.Это запрос может быть составным запросом, составленным из нескольких, отделенных точкой с запятой (;). Например, тело следующей хранимой процедуры составляет блок BEGIN ... END, который содержит запрос SET и цикл REPEAT, который непосредственно содержит другой SET:

CREATE PROCEDURE dorepeat(p1 INT)
BEGIN
  SET @x = 0;
  REPEAT SET @x = @x + 1; UNTIL @x > p1 END REPEAT;
END;
Если Вы используете mysql, чтобы определить сохраненную программу, содержащую символы точки с запятой, возникает проблема. По умолчанию mysql признает точку с запятой разделителем запроса, таким образом, Вы должны пересмотреть разделитель временно, чтобы заставить mysql передавать все сохраненное определение программы серверу.

Чтобы пересмотреть разделитель, используйте команду delimiter. Следующий пример показывает, как сделать это для процедуры dorepeat(). Разделитель изменен на //, чтобы позволить всему определению быть переданным к серверу как единственный запрос, а затем ; восстанавливается прежде, чем вызвать процедуру. Это включает разделитель ;, используемый в теле процедуры, который передается к серверу вместо того, чтобы интерпретироваться непосредственно mysql.

mysql> delimiter //
mysql> CREATE PROCEDURE dorepeat(p1 INT)
    -> BEGIN
    ->   SET @x = 0;
    ->   REPEAT SET @x = @x + 1; UNTIL @x > p1 END REPEAT;
    -> END
    -> //
Query OK, 0 rows affected (0.00 sec)

mysql> delimiter ;
mysql> CALL dorepeat(1000);
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @x;
+------+
| @x   |
+------+
| 1001 |
+------+
1 row in set (0.00 sec)
Вы можете переделать разделитель на строку вовсе не обязательно именно //, разделитель может состоять из одного или нескольких символов. Вы должны избегать использования наклонной черты влево (\), потому что это символ ESC для MySQL.

Следующее пример функции, которая берет параметр, выполняет работу, используя функцию SQL, и возвращает результат. В этом случае не нужно использовать delimiter, потому что функциональное определение не содержит внутренний разделитель запроса:

mysql> CREATE FUNCTION hello (s CHAR(20))
mysql> RETURNS CHAR(50) DETERMINISTIC
    -> RETURN CONCAT('Hello, ',s,'!');
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT hello('world');
+----------------+
| hello('world') |
+----------------+
| Hello, world!  |
+----------------+
1 row in set (0.00 sec)

21.2. Использование сохраненных подпрограмм (процедуры и функции)

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

Сохраненные подпрограммы могут быть особенно полезными в определенных ситуациях:

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

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

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

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

MySQL следует за синтаксисом SQL:2003 для сохраненных подпрограмм, который также используется DB2 IBM. Весь синтаксис, описанный здесь, поддерживается, любые ограничения и расширения зарегистрированы.

Дополнительные ресурсы

  • Вы можете найти Stored Procedures User Forum полезным, работая с хранимыми процедурами и функциями.

  • Для ответов на некоторые обычно задаваемые вопросы относительно сохраненных подпрограмм в MySQL см. раздел A.4.
  • Есть некоторые ограничения на использование сохраненных подпрограмм. См. раздел C.1.
  • Двоичное журналирование для сохраненных подпрограмм имеет место как описано в разделе 21.7.

21.2.1. Синтаксис подпрограмм

Сохраненная подпрограмма процедура или функция. Сохраненные подпрограммы создаются с помощью CREATE PROCEDURE и CREATE FUNCTION (см. раздел 14.1.13 ). Процедура вызвана, используя CALL (см. раздел 14.2.1), и может вернуть значения, используя только выходные переменные. Функция может быть вызвана из запроса точно так же, как любая другая функция (то есть, обращаясь к имени функции) и может возвратить скалярное значение. Тело сохраненной подпрограммы может использовать составные запросы (см. раздел 14.6).

Сохраненные подпрограммы могут быть удалены через DROP PROCEDURE и DROP FUNCTION (см. раздел 14.1.23) и изменены с помощью ALTER PROCEDURE и ALTER FUNCTION (см. раздел 14.1.5).

Хранимая процедура или функция связана с особой базой данных. У этого есть несколько значений:

  • Когда подпрограмма вызвана, выполнено неявное USE db_name (и отменено, когда подпрограмма заканчивается). Запросы USE в пределах сохраненных подпрограмм не разрешены.

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

Сохраненные функции не могут быть рекурсивными.

Рекурсия в хранимых процедурах разрешена, но отключена по умолчанию. Чтобы включить рекурсии, установите переменную max_sp_recursion_depth к значению больше ноля. Рекурсия хранимой процедуры увеличивает требование к пространству стека потока. Если Вы увеличиваете значение max_sp_recursion_depth, может быть необходимо увеличить размер стека потока, увеличивая значение thread_stack при запуске сервера. См. раздел 6.1.5.

MySQL поддерживает очень полезное расширение, которое включает использование регулярных запросов SELECT (то есть, не используя курсоры или местные переменные) в хранимой процедуре. Набор результатов такого запроса просто посылают непосредственно клиенту. Многократный SELECT производит много наборов результатов, таким образом, клиент должен пользоваться библиотекой клиента MySQL, которая поддерживает многократные наборы результатов. Это означает, что клиент должен пользоваться библиотекой клиента от версии MySQL, по крайней мере, 4.1. Клиент должен также определить опцию CLIENT_MULTI_RESULTS, когда соединяется. Для программ C это может быть сделано с помощью функции mysql_real_connect() C API. См. разделы 25.8.7.54 и 25.8.17.

21.2.2. Сохраненные подпрограммы и привилегии MySQL

Система привилегий MySQL принимает сохраненные подпрограммы во внимание следующим образом:

  • Привилегия CREATE ROUTINE необходима, чтобы создать сохраненные подпрограммы.

  • Привилегия ALTER ROUTINE необходима, чтобы изменить или удалить сохраненные подпрограммы. Эту привилегию предоставляют автоматически создателю подпрограммы в случае необходимости и удаляют, когда подпрограмма удалена.
  • Привилегия EXECUTE требуется, чтобы выполнять сохраненные подпрограммы. Однако, эту привилегию предоставляют автоматически создателю подпрограммы в случае необходимости (и удаляют, когда подпрограмма удалена). Кроме того, значение по умолчанию SQL SECURITY для подпрограммы DEFINER, что позволяет пользователям, у которых есть доступ к базе данных, с которой подпрограмма связана, выполнить подпрограмму.
  • Если переменная automatic_sp_privileges равна 0, привилегии EXECUTE и ALTER ROUTINE автоматически не предоставляются и не удаляются.
  • Создатель подпрограммы это учетная запись, используемая, чтобы выполнить CREATE.

Сервер управляет таблицей mysql.proc в ответ на запросы, которые создают, изменяют или удаляют сохраненные подпрограммы. Не поддержано, чтобы сервер замечал ручные манипуляции с этой таблицей.

21.2.3. Сохраненные метаданные

Метаданные о сохраненных подпрограммах могут быть получены следующим образом:

21.2.4. Хранимые процедуры, функции, триггеры и LAST_INSERT_ID()

В пределах тела сохраненной подпрограммы (процедуры или функции) или триггера, значение LAST_INSERT_ID() изменяется тем же самым путем, что касается запрсов, выполненных вне тела этих видов объектов (см. раздел 13.14). Эффект сохраненной подпрограммы или триггера на значение LAST_INSERT_ID() зависит от вида подпрограммы:

  • Если хранимая процедура выполняет запросы, которые изменяют значение LAST_INSERT_ID() , измененное значение замечено запросами, которые следуют за вызовом процедуры.

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

21.3. Использование триггеров

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

Триггер определен, чтобы активироваться, когда запрос вставляет, обновляет или удаляет строки в связанной таблице. Эти операции события триггера. Например, строки могут быть вставлены INSERT или LOAD DATA, и триггер вставки активируется для каждой вставленной строки. Триггер может быть установлен на активацию прежде или после события. Например, у Вас может быть триггер, который активируется перед каждой строкой, которая вставлена в таблицу или после каждой строки, которая обновлена.

Триггеры MySQL активируются только для изменений, произведенных в таблицах запросами SQL. Они не активируются для изменений в представлениях или изменений таблиц, сделанных API, которые не передают запроса SQL серверу MySQL. Это означает, что триггеры не активированы изменениями в таблицах INFORMATION_SCHEMA или performance_schema, потому что эти таблицы фактически представления.

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

21.3.1. Синтаксис и примеры триггеров

Чтобы создать или удалить триггер, используйте CREATE TRIGGER или DROP TRIGGER, описанные в разделах 14.1.17 и 14.1.27.

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

mysql> CREATE TABLE account (acct_num INT, amount DECIMAL(10,2));
Query OK, 0 rows affected (0.03 sec)

mysql> CREATE TRIGGER ins_sum BEFORE INSERT ON account
    ->        FOR EACH ROW SET @sum = @sum + NEW.amount;
Query OK, 0 rows affected (0.06 sec)
Запрос CREATE TRIGGER создает триггер ins_sum, связанный с таблицей account. Это также включает пункты, которые определяют время действия, инициирующее событие, и что сделать, когда триггер активируется:

  • Ключевое слово BEFORE указывает время действия. В этом случае триггер активируется прежде, чем каждая строка будет вставлена в таблицу. Другое разрешенное ключевое слово здесь AFTER.

  • Ключевое слово INSERT указывает на событие, то есть, тип работы, которая активирует триггер. В примере INSERT вызывает активацию. Вы можете также создать триггеры для DELETE и UPDATE.
  • Указание FOR EACH ROW определяет тело триггера, то есть, запрос, чтобы выполнить каждый раз, когда триггер активируется, что происходит однажды для каждой строки, затронутой инициирующим событием. В примере тело простой SET, который накапливает в пользовательскую переменную значения, вставленные в столбец amount. Запрос обращается к столбцу как к NEW.amount.

Чтобы использовать триггер, установите переменную сумматора в ноль, выполните INSERT и посмотрите новое значение переменной:

mysql> SET @sum = 0;
mysql> INSERT INTO account VALUES(137,14.98),(141,1937.50),(97,-100.00);
mysql> SELECT @sum AS 'Total amount inserted';
+-----------------------+
| Total amount inserted |
+-----------------------+
| 1852.48               |
+-----------------------+
В этом случае значение @sum после INSERT 14.98 + 1937.50 - 100 или 1852.48.

Чтобы разрушить триггер, используйте DROP TRIGGER. Вы должны определить имя схемы, если триггер не находится в схеме по умолчанию:

mysql> DROP TRIGGER test.ins_sum;
Если Вы удаляете таблицу, любые триггеры для таблицы также удалятся.

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

Возможно определить много триггеров для данной таблицы, у которых есть то же самое событие и время действия. Например, Вы можете иметь два триггера BEFORE UPDATE для таблицы. По умолчанию триггеры, у которых есть то же самое событие и время действия, активируются в порядке, в котором они создавались. Чтобы затронуть порядок, определите пункт после FOR EACH ROW, который указывает FOLLOWS или PRECEDES и название существующего триггера, у которого также есть то же самое событие и время действия. С PRECEDES новый триггер активируется после существующего.

Например, следующее определение задает другой триггер BEFORE INSERT для таблицы account:

mysql> CREATE TRIGGER ins_transaction BEFORE INSERT ON account
    -> FOR EACH ROW PRECEDES ins_sum
    -> SET
    -> @deposits = @deposits + IF(NEW.amount>0,NEW.amount,0),
    -> @withdrawals = @withdrawals + IF(NEW.amount<0,-NEW.amount,0);
Query OK, 0 rows affected (0.02 sec)
Этот триггер, ins_transaction, подобен ins_sum, но накапливает deposits и withdrawals порознь. У этого есть предложение PRECEDES, которое заставляет его активироваться прежде ins_sum: без того пункта триггер активировался бы после ins_sum, так как создан позднее ins_sum.

В пределах тела ключевые слова OLD и NEW позволяют Вам доступ к столбцам в строках, затронутых триггером. OLD и NEW расширения MySQL для триггеров, они не являются чувствительными к регистру.

В триггере INSERT может использоваться только NEW.col_name: нет никакой старой строки. В триггере DELETE нет никакой новой строки, поэтому может использоваться только OLD.col_name. В триггере UPDATE можно применить OLD.col_name, чтобы обратиться к столбцам строки прежде, чем они будут обновлены, и NEW.col_name, чтобы обратиться после того, как это обновлено.

Столбец, названный OLD, только для чтения. Вы можете обратиться к нему (если Вы имеете привилегию SELECT), но не изменить. Вы можете обратиться к столбцу, названному NEW, если Вы имеете привилегию SELECT для него. В триггере BEFORE Вы можете также изменить его значение с SET NEW.col_name = value , если Вы имеете привилегию UPDATE для него. Это означает, что Вы можете использовать триггер, чтобы изменить значения, которые будут вставляться в новую строку или использоваться, чтобы обновить строку. Такой запрос SET не имеет никакого эффекта в триггере AFTER, потому что изменение строки уже произойдет.

В триггере BEFORE значение NEW для столбца AUTO_INCREMENT 0, а не порядковый номер, который произведен автоматически, когда новая строка фактически вставлена.

При использовании BEGIN ... END Вы можете определить триггер, который выполняет много запросов. В пределах блока BEGIN Вы также можете использовать другой синтаксис, который разрешен в пределах сохраненных подпрограмм, например, условные предложения и циклы. Однако, так же, как для сохраненных подпрограмм, если Вы используете mysql, чтобы определить триггер, который выполняет много запросов, необходимо пересмотреть разделитель запроса так, чтобы Вы могли использовать ; в пределах определения триггера. Следующий пример иллюстрирует эти тезисы. Это определяет триггер UPDATE, который проверяет новое значение, которое будет использоваться для того, чтобы обновить каждую строку, и изменяет значение, чтобы оно было в пределах диапазона от 0 до 100. Это должен быть триггер BEFORE, потому что значение должно быть проверено прежде, чем оно будет использоваться, чтобы обновить строку:

mysql> delimiter //
mysql> CREATE TRIGGER upd_check BEFORE UPDATE ON account FOR EACH ROW
    -> BEGIN
    ->   IF NEW.amount < 0 THEN
    ->      SET NEW.amount = 0;
    ->   ELSEIF NEW.amount > 100 THEN
    ->      SET NEW.amount = 100;
    ->   END IF;
    -> END;//
mysql> delimiter ;
Может быть легче определить хранимую процедуру отдельно и затем вызвать ее из триггера, используя простой CALL . Это также выгодно, если Вы хотите выполнить тот же самый код из нескольких триггеров.

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

  • Триггер не может использовать CALL, чтобы вызвать хранимые процедуры, которые возвращают данные клиенту или используют динамический SQL. Хранимым процедурам разрешают возвратить данные триггеру через параметры OUT или INOUT.

  • Триггер не может использовать запросы, которые явно или неявно начинают или заканчивают транзакцию, например, START TRANSACTION, COMMIT, or ROLLBACK. ROLLBACK to SAVEPOINT разрешен, потому что это не заканчивает транзакцию.

MySQL обрабатывает ошибки в триггере следующим образом:

  • Если триггер BEFORE терпит неудачу, работа на соответствующей строке не выполнена.

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

Триггеры могут содержать прямые ссылки на таблицы по имени, такие как триггер testref, показанный в этом примере:

CREATE TABLE test1(a1 INT);
CREATE TABLE test2(a2 INT);
CREATE TABLE test3(a3 INT NOT NULL AUTO_INCREMENT PRIMARY KEY);
CREATE TABLE test4(a4 INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
                   b4 INT DEFAULT 0);
delimiter |
CREATE TRIGGER testref BEFORE INSERT ON test1 FOR EACH ROW
BEGIN
   INSERT INTO test2 SET a2 = NEW.a1;
   DELETE FROM test3 WHERE a3 = NEW.a1;
   UPDATE test4 SET b4 = b4 + 1 WHERE a4 = NEW.a1;
END;
|
delimiter ;
INSERT INTO test3 (a3) VALUES
   (NULL), (NULL), (NULL), (NULL), (NULL),
   (NULL), (NULL), (NULL), (NULL), (NULL);
INSERT INTO test4 (a4) VALUES
   (0), (0), (0), (0), (0), (0), (0), (0), (0), (0);
Предположите, что Вы вставляете следующие значения в таблицу test1 как показано здесь:
mysql> INSERT INTO test1 VALUES
    ->        (1), (3), (1), (7), (1), (8), (4), (4);
Query OK, 8 rows affected (0.01 sec)
Records: 8  Duplicates: 0  Warnings: 0
В результате эти четыре таблицы содержат следующие данные:
mysql> SELECT * FROM test1;
+----+
| a1 |
+----+
| 1  |
| 3  |
| 1  |
| 7  |
| 1  |
| 8  |
| 4  |
| 4  |
+----+
8 rows in set (0.00 sec)

mysql> SELECT * FROM test2;
+----+
| a2 |
+----+
| 1  |
| 3  |
| 1  |
| 7  |
| 1  |
| 8  |
| 4  |
| 4  |
+----+
8 rows in set (0.00 sec)

mysql> SELECT * FROM test3;
+----+
| a3 |
+----+
|  2 |
|  5 |
|  6 |
|  9 |
| 10 |
+----+
5 rows in set (0.00 sec)

mysql> SELECT * FROM test4;
+----+----+
| a4 | b4 |
+----+----+
|  1 | 3  |
|  2 | 0  |
|  3 | 1  |
|  4 | 2  |
|  5 | 0  |
|  6 | 0  |
|  7 | 1  |
|  8 | 1  |
|  9 | 0  |
| 10 | 0  |
+----+----+
10 rows in set (0.00 sec)

21.3.2. Метаданные триггеров

Метаданные триггеров могут быть получены следующим образом:

21.4. Использование планировщика событий

MySQL Event Scheduler управляет планированием и выполнением событий, то есть, задач, которые работают согласно графику. Следующее обсуждение покрывает планировщик событий.

Сохраненные подпрограммы требуют таблицы event в базе данных mysql. Эта таблица составлена во время процессов установки MySQL 8.0. Если Вы обновляетесь до MySQL 8.0 от более ранней версии, убедитесь, что обновили свои таблицы, чтобы удостовериться, что таблица event существует. См. раздел 5.4.5.

21.4.1. Краткий обзор планировщика событий

События MySQL это задачи, которые работают согласно графику. Поэтому мы иногда именуем их намеченные события. Когда Вы создаете событие, Вы создаете названный объект базы данных, содержащий один или более запросов SQL, которые будут выполнены в одном или более равных интервалах, начинаясь и заканчиваясь в определенную дату и время. Концептуально это подобно идее Unix crontab (также известный как cron job) или Windows Task Scheduler.

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

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

У событий MySQL есть следующие основные функции и свойства:

  • В MySQL событие уникально идентифицировано его именем и схемой, на которую назначено.

  • Событие выполняет определенное действие согласно графику. Это действие состоит из запроса SQL, который может быть составным запросом в блоке BEGIN ... END при желании (см. раздел 14.6). Синхронизация события может быть одноразовой или текущей. Одноразовое событие выполняется только один раз. Повторяющееся событие повторяет свое действие в равном интервале, можно назначить определенный день запуска и время, день конца и время, оба варианта сразу или ни одного. По умолчанию график события начинается, как только оно создается, и продолжается неопределенно долго, пока оно не отключено или не удалено.

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

  • Пользователи могут создать, изменить и удалить намеченные события, используя запросы SQL, предназначенные в этих целях. Синтаксически недопустимые запросы создания и модификации событий терпят неудачу с соответствующим сообщением об ошибке. Пользователь может включать запросы в действие события, которые требуют привилегий, которые фактически не имеет пользователь. Запрос создания или модификации событий преуспевает, но действие терпит неудачу. См. раздел 21.4.6.
  • Многие из свойств события могут быть установлены или изменены через использование запроса SQL. Эти свойства включают имя, синхронизацию, постоянство (то есть, сохранено ли это после истечения его графика), состояние (включен или отключен), действие, которое будет выполнено, и схема, на которую это назначено. См. раздел 14.1.2 .

    По умолчанию создатель события пользователь, который создал событие, если событие не был изменено, тогда создатель пользователь, который выполнил последний запрос ALTER EVENT, затрагивающий это событие. Событие может быть изменено любым пользователем, имеющим привилегию EVENT на базе данных, для которой определено событие. См. раздел 21.4.6.

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

21.4.2. Конфигурация планировщика событий

События запущены потоком планировщика, когда мы обращаемся к планировщику событий, мы фактически обращаемся к этому потоку. Работая, поток планировщика событий и его текущее состояние могут быть замечены пользователями, имеющими привилегию PROCESS в выводе SHOW PROCESSLIST, как показано далее.

Глобальная переменная event_scheduler определяет, включен ли планировщик событий. У этого есть одно из 3 значений, которые затрагивают событие, как описано здесь:

  • OFF: Планировщик событий остановлен. Поток планировщика событий не работает, не показан в выводе SHOW PROCESSLIST и никакие запланированные события не запущены. OFF значение по умолчанию для event_scheduler.

    Когда планировщик событий остановлен (event_scheduler OFF), это может быть запущено, устанавливая значение event_scheduler в ON.

  • ON: Планировщик событий запущен, поток планировщика событий выполняется и запускает все запланированные события. Когда планировщик событий ON, поток планировщика событий перечислен в выводе SHOW PROCESSLIST как процесс демона, и его статус представлен как показано здесь:
    mysql> SHOW PROCESSLIST\G
    *************************** 1. row ***************************
     Id: 1
       User: root
       Host: localhost
     db: NULL
    Command: Query
       Time: 0
      State: NULL
       Info: show processlist
    *************************** 2. row ***************************
     Id: 2
       User: event_scheduler
       Host: localhost
     db: NULL
    Command: Daemon
       Time: 3
      State: Waiting for next activation
       Info: NULL
    2 rows in set (0.00 sec)
    
    Планирование событий может быть остановлено, устанавливая значение event_scheduler в OFF.
  • DISABLED: Это значение представляет не влияющий на эксплуатацию планировщик событий. Когда планировщик событий DISABLED, поток планировщика событий не работает (и не появляется в выводе SHOW PROCESSLIST ). Кроме того, его статус не может быть изменен во время выполнения.

Если состояние планировщика событий не было установлено в DISABLED, event_scheduler может быть переключена между ON и OFF (с использованием SET). Также возможно использовать 0 для OFF и 1 для ON, устанавливая эту переменную. Таким образом, любой из следующих 4 запросов может использоваться в клиенте mysql, чтобы включить планировщик событий:

SET GLOBAL event_scheduler = ON;
SET @@global.event_scheduler = ON;
SET GLOBAL event_scheduler = 1;
SET @@global.event_scheduler = 1;
Точно так же любой из этих 4 запросов может использоваться, чтобы выключить планировщик событий:
SET GLOBAL event_scheduler = OFF;
SET @@global.event_scheduler = OFF;
SET GLOBAL event_scheduler = 0;
SET @@global.event_scheduler = 0;
Хотя ON и OFF имеют числовые эквиваленты, значение, выведенное на экран для event_scheduler через SELECT или SHOW VARIABLES всегда OFF, ON или DISABLED. DISABLED не имеет никакого числового эквивалента . Поэтому ON и OFF обычно предпочитаются 1 и 0, устанавливая эту переменную.

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

mysql< SET @@event_scheduler = OFF;
ERROR 1229 (HY000): Variable 'event_scheduler' is a GLOBAL
variable and should be set with SET GLOBAL

Возможно установить планировщик событий в DISABLED только при запуске сервера. Если event_scheduler ON или OFF, Вы не можете установить это в DISABLED во время выполнения. Кроме того, если планировщик событий установлен в DISABLED при запуске Вы не можете изменить значение event_scheduler во время выполнения.

Чтобы отключить планировщик событий, используйте один из следующих двух методов:

  • Как параметр командной строки, запуская сервер:

    --event-scheduler=DISABLED
    
  • В конфигурационном файле сервера (my.cnf, или my.ini в Windows) включите строку, где она будет считана сервером (например, в раздел [mysqld]):
    event_scheduler=DISABLED
    

Чтобы включить планировщик событий, перезапустите сервер без параметра командной строки --event-scheduler=DISABLED или после удаления (или комментирования) строки в конфигурационном файле сервера, содержащей event-scheduler=DISABLED. Альтернативно, Вы можете использовать ON (или 1) или OFF (или 0 ) вместо DISABLED, запуская сервер.

Вы можете сделать запрос манипуляции событий, когда event_scheduler DISABLED. Никакие предупреждения или ошибки не произведены в таких случаях (при условии, что запрос самостоятельно допустим). Однако, запланированные события не могут выполниться, пока эта переменная не установлена в ON (или 1). Как только это было сделано, поток планировщика событий запускает все события, условия планирования которых удовлетворены.

Запуск сервера MySQL с опцией --skip-grant-tables установит event_scheduler в DISABLED, переопределяя любое другое значение в командной строке или в файле my.cnf (или my.ini).

Для запросов SQL, используемых, чтобы создать, изменить и удалить события см. раздел 21.4.3.

MySQL обеспечивает таблицу EVENTS в базе данных INFORMATION_SCHEMA. Эта таблица может быть запрошена, чтобы получить информацию о запланированных событиях, которые были определены на сервере. См. разделы 21.4.4 и 22.7.

21.4.3. Синтаксис событий

MySQL обеспечивает несколько запросов SQL для того, чтобы они работали с запланированными событиями:

  • Новые события определены, используя CREATE EVENT. См. раздел 14.1.10.

  • Определение существующего события может быть изменено посредством ALTER EVENT. См. раздел 14.1.2.
  • Когда запланированное событие больше не требуется, оно может быть удалено с сервера создателем события с помощью DROP EVENT. См. раздел 14.1.20. Сохраняется ли событие после конца его графика, также зависит от его предложения ON COMPLETION, если оно есть. См. раздел 14.1.10.

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

21.4.4. Метаданные событий

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

Представление времени планировщика событий

У каждого сеанса в MySQL есть часовой пояс сеанса (STZ). Это сеансовая переменная time_zone, которая инициализирована от глобального значения сервера time_zone, когда сеанс начинается, но может быть изменена во время сеанса.

Часовой пояс сеанса, который актуален, когда выполняется CREATE EVENT или ALTER EVENT, используется, чтобы интерпретировать времена, определенные при создании события. Это становится часовым поясом событий (ETZ), то есть, часовой пояс, который используется для планирования событий и является в действительности часовым поясом, в котором выполняется событие.

Для представления информации о событии в таблице mysql.event времена execute_at, starts и ends преобразованы в UTC и сохранены наряду с часовым поясом события. Это позволяет выполнению событий продолжиться как определено независимо от любых последующих изменений часового пояса сервера или эффектов летнего времени. Время last_executed также сохранено в UTC.

Если Вы выбираете информацию из mysql.event, времена получены как значения UTC. Эти времена могут также быть получены, выбирая из INFORMATION_SCHEMA.EVENTS или через SHOW EVENTS, но о них сообщают как о значениях ETZ. Другие времена, доступные из этих источников, указывают, когда событие создавалоси или последний раз изменилось, они выведены на экран как значения STZ. Следующая таблица суммирует представление времен событий.

Значение mysql.event INFORMATION_SCHEMA.EVENTS SHOW EVENTS
Execute atUTCETZETZ
StartsUTCETZETZ
EndsUTCETZETZ
Last executedUTCETZn/a
CreatedSTZSTZn/a
Last alteredSTZSTZn/a

21.4.5. Состояние планировщика событий

Планировщик событий пишет информацию о выполнении события, которое заканчивается с ошибкой или предупреждением в журнал ошибок сервера MySQL. См. раздел 21.4.6.

Чтобы получить информацию о статусе планировщика событий для отладки и поиска неисправностей, выполните mysqladmin debug (см. раздел 5.5.2). После выполнения этой команды журнал ошибок сервера содержит вывод, касающийся планировщика событий, подобный тому, что показывают здесь:

Events status:
LLA = Last Locked At  LUA = Last Unlocked At
WOC = Waiting On Condition  DL = Data Locked

Event scheduler status:
State: INITIALIZED
Thread id  : 0
LLA: init_scheduler:313
LUA: init_scheduler:318
WOC: NO
Workers: 0
Executed   : 0
Data locked: NO

Event queue status:
Element count   : 1
Data locked : NO
Attempting lock : NO
LLA   : init_queue:148
LUA   : init_queue:168
WOC   : NO
Next activation : 0000-00-00 00:00:00
В запросах, которые происходят как часть событий, запущенных планировщиком событий, сообщения диагностики (не только ошибки, а также и предупреждения) написаны в журнал ошибок и в Windows в журнал событий приложения. Для часто запускаемых событий это может привести ко многим зарегистрированным сообщениям. Например, для запроса SELECT ... INTO var_list, если запрос не возвращает строк, предупреждения с кодом ошибки 1329, происходят (No data), и переменные значения остаются неизменными. Если запрос возвращает много строк, происходит ошибка 1172 (Result consisted of more than one row ). Для любого условия Вы можете избежать регистрации предупреждений, объявляя обработчик условия, см. раздел 14.6.7.2. Для запросов, которые могут получить много строк, другая стратегия состоит в том, чтобы использовать LIMIT 1, чтобы ограничить набор результатов единственной строкой.

21.4.6. Привилегии планировщика и событий MySQL

Чтобы включить или отключить выполнение запланированных событий, необходимо установить значение глобальной переменной event_scheduler. Это требует привилегии SUPER .

Привилегия EVENT управляет созданием, модификацией и удалением событий. Эта привилегия может дароваться, используя GRANT. Например, этот запрос GRANT предоставляет привилегию EVENT для схемы myschema пользователю jon@ghidora:

GRANT EVENT ON myschema.* TO jon@ghidora;
Мы предполагаем, что эта учетная запись пользователя уже существует.

Чтобы предоставить тому же самому пользователю привилегию EVENT на всех схемах, используйте следующее запрос:

GRANT EVENT ON *.* TO jon@ghidora;
Привилегия EVENT имеет глобальный контекст или на уровне схемы. Поэтому попытка предоставить это на единственной таблице приводит к ошибке как показано:
mysql> GRANT EVENT ON myschema.mytable TO jon@ghidora;
ERROR 1144 (42000): Illegal GRANT/REVOKE command; please
consult the manual to see which privileges can be used
Важно понять, что событие запущено с привилегиями его создателя, и что оно не может выполнить действия, для которых у его автора нет необходимых привилегий. Например, предположите, что jon@ghidora имеет привилегию EVENT для myschema. Предположите также, что этот пользователь имеет привилегию SELECT для myschema, но никаких других привилегий для этой схемы. Для jon@ghidora возможно создать новое событие:
CREATE EVENT e_store_ts ON SCHEDULE EVERY 10 SECOND DO
       INSERT INTO myschema.mytable VALUES (UNIX_TIMESTAMP());
Пользователь ждет в течение приблизительно одной минуты, и затем выполняет запрос SELECT * FROM mytable;, ожидая увидеть несколько новых строк в таблице. Вместо этого таблица пуста. Так как пользователь не имеет привилегии INSERT для рассматриваемой таблицы, событие не имеет никакого эффекта.

Если Вы просматриваете журнал ошибок MySQL (hostname.err), Вы можете видеть, что событие выполняется, но действие, которое оно пытается выполнить, терпит неудачу:

2013-09-24T12:41:31.261992Z 25 [ERROR] Event Scheduler:
[jon@ghidora][cookbook.e_store_ts] INSERT command denied to user
'jon'@'ghidora' for table 'mytable'
2013-09-24T12:41:31.262022Z 25 [Note] Event Scheduler:
[jon@ghidora].[myschema.e_store_ts] event execution failed.
2013-09-24T12:41:41.271796Z 26 [ERROR] Event Scheduler:
[jon@ghidora][cookbook.e_store_ts] INSERT command denied to user
'jon'@'ghidora' for table 'mytable'
2013-09-24T12:41:41.272761Z 26 [Note] Event Scheduler:
[jon@ghidora].[myschema.e_store_ts] event execution failed.
Так как у этого пользователя очень вероятно нет доступа к журналу ошибок, возможно проверить, допустим ли запрос, выполняя это непосредственно:
mysql> INSERT INTO myschema.mytable VALUES (UNIX_TIMESTAMP());
ERROR 1142 (42000): INSERT command denied to user
'jon'@'ghidora' for table 'mytable'

Просмотр таблицы INFORMATION_SCHEMA.EVENTS показывает, что e_store_ts существует и включен, но столбец LAST_EXECUTED NULL:

mysql> SELECT * FROM INFORMATION_SCHEMA.EVENTS
     >          WHERE EVENT_NAME='e_store_ts' AND
     >          EVENT_SCHEMA='myschema'\G
*************************** 1. row ***************************
   EVENT_CATALOG: NULL
EVENT_SCHEMA: myschema
EVENT_NAME: e_store_ts
 DEFINER: jon@ghidora
EVENT_BODY: SQL
EVENT_DEFINITION: INSERT INTO myschema.mytable VALUES (UNIX_TIMESTAMP())
EVENT_TYPE: RECURRING
EXECUTE_AT: NULL
  INTERVAL_VALUE: 5
  INTERVAL_FIELD: SECOND
SQL_MODE: NULL
STARTS: 0000-00-00 00:00:00
  ENDS: 0000-00-00 00:00:00
STATUS: ENABLED
   ON_COMPLETION: NOT PRESERVE
 CREATED: 2006-02-09 22:36:06
LAST_ALTERED: 2006-02-09 22:36:06
   LAST_EXECUTED: NULL
   EVENT_COMMENT:
1 row in set (0.00 sec)
Отменить привилегию EVENT можно через REVOKE. В этом примере привилегия EVENT на схеме myschema удалена из учетной записи пользователя jon@ghidora:
REVOKE EVENT ON myschema.* FROM jon@ghidora;

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

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

Предположите что пользователю jon@ghidora были даны привилегии EVENT и INSERT на схеме myschema. Этот пользователь тогда создает следующий событие:

CREATE EVENT e_insert ON SCHEDULE EVERY 7 SECOND DO
       INSERT INTO myschema.mytable;
После того, как это событие было создано, root отменяет привилегию EVENT для jon@ghidora. Однако, e_insert продолжает выполняться, вставляя новую строку в mytable каждые семь секунд. То же самое было бы истиной, если root сделал любой из этих запросов:

  • DROP USER jon@ghidora;

  • RENAME USER jon@ghidora TO someotherguy@ghidora;

Вы можете проверить, что это истина, исследуя таблицу mysql.event или INFORMATION_SCHEMA.EVENTS (см. раздел 22.7) до и после выполнения DROP USER или RENAME USER.

Определения событий сохранены в таблице mysql.event. Чтобы удалить событие, созданное другой учетной записью пользователя, MySQL root (или другой пользователь с необходимыми привилегиями) может удалить строки из этой таблицы. Например, чтобы удалить событие e_insert root может использовать следующий запрос:

DELETE FROM mysql.event WHERE db = 'myschema' AND
       definer = 'jon@ghidora' AND name = 'e_insert';
Очень важно соответствовать имени события, имени схемы базы данных и учетной записи пользователя, удаляя строки из mysql.event. Тот же самый пользователь может создать различные события с тем же самым именем в различных схемах.

Пользовательские привилегии EVENT сохранены в столбцах Event_priv таблиц mysql.user и mysql.db. В обоих случаях этот столбец содержит одно из значений 'Y' или 'N' (по умолчанию 'N'). mysql.user.Event_priv установлен в 'Y' для данного пользователя, только если у этого пользователя есть глобальная привилегия EVENT (то есть, если привилегия даровалась, используя GRANT EVENT ON *.*). Для привилегии EVENT на уровне схемы GRANT создает строку в mysql.db и устанавливает столбец Db в имя схемы, столбец User к имени пользователя, а столбец Event_priv в 'Y'. Никогда не должно быть никакой потребности управлять этими таблицами непосредственно, запросы GRANT EVENT и REVOKE EVENT выполняют необходимые операции на них.

Пять переменных состояния предоставляют счетчики в связанных с событиями операциях (но НЕ запросов, выполненных событиями, см. раздел C.1). Это:

  • Com_create_event: Число CREATE EVENT, выполненных начиная с последнего перезапуска сервера.

  • Com_alter_event: Число ALTER EVENT, выполненных начиная с последнего перезапуска сервера.
  • Com_drop_event: Число DROP EVENT, выполненных начиная с последнего перезапуска сервера.
  • Com_show_create_event: Число SHOW CREATE EVENT, выполненных начиная с последнего перезапуска сервера.
  • Com_show_events: Число SHOW EVENTS, выполненных начиная с последнего перезапуска сервера.

Вы можете рассмотреть текущие значения для всех них, выполняя запрос SHOW STATUS LIKE '%event%';.

21.5. Использование представлений

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

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

21.5.1. Синтаксис представления

Запрос CREATE VIEW создает новое представление (см. раздел 14.1.18). Чтобы изменить определение представления или удалить представление, надо использовать ALTER VIEW (см. раздел 14.1.8) или DROP VIEW (см. раздел 14.1.28).

Представление может быть создано из многих видов запросов SELECT. Это может сослаться на базовые таблицы или другие представления. Это может использовать объединения, UNION и подзапросы. SELECT не должен даже обратиться ни к каким таблицам. Следующий пример определяет представление, которое выбирает два столбца из другой таблицы, так же как выражение, вычисленное от тех столбцов:

mysql> CREATE TABLE t (qty INT, price INT);
mysql> INSERT INTO t VALUES(3, 50), (5, 60);
mysql> CREATE VIEW v AS SELECT qty, price, qty*price AS value FROM t;
mysql> SELECT * FROM v;
+------+-------+-------+
| qty  | price | value |
+------+-------+-------+
| 3    | 50    |   150 |
| 5    | 60    |   300 |
+------+-------+-------+

mysql> SELECT * FROM v WHERE qty = 5;
+------+-------+-------+
| qty  | price | value |
+------+-------+-------+
| 5    | 60    |   300 |
+------+-------+-------+

21.5.2. Алгоритмы обработки представления

Дополнительное предложение ALGORITHM для CREATE VIEW или ALTER VIEW это расширение MySQL для SQL. Это затрагивает, как MySQL обрабатывает представление. ALGORITHM берет три значения: MERGE, TEMPTABLE или UNDEFINED.

  • MERGE: текст запроса, который относится к представлению и определению представления, слит таким образом, что части определения представления заменяют соответствующие части запроса.

  • TEMPTABLE: результаты представления получены во временную таблицу, которая используется, чтобы выполнить запрос.
  • UNDEFINED: MySQL выбирает, который алгоритм использовать. Это предпочитает MERGE если возможно, потому что MERGE обычно более эффективен, и представление не может быть обновляемым, если временная таблица используется.
  • Если нет предложения ALGORITHM, алгоритм значения по умолчанию определен значением флага derived_merge переменной optimizer_switch . См. раздел 9.2.1.18.3.

Причина определить TEMPTABLE явно: блокировки могут быть сняты на основных таблицах после того, как временная таблица была составлена и прежде, чем она будет использоваться, чтобы закончить обрабатывать запрос. Это могло бы привести к более быстрому снятию блокировки, чем алгоритм MERGE так, чтобы другие клиенты, которые используют представление, не были заблокированы надолго.

Алгоритм представления может быть UNDEFINED по трем причинам:

  • Предложение ALGORITHM не присутствует в запросе CREATE VIEW.

  • CREATE VIEW имеет явное предложение ALGORITHM = UNDEFINED.
  • ALGORITHM = MERGE определен для представления, которое может быть обработано только с временной таблицей. В этом случае MySQL производит предупреждение и устанавливает алгоритм в UNDEFINED.

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

CREATE ALGORITHM = MERGE VIEW v_merge (vc1, vc2) AS
       SELECT c1, c2 FROM t WHERE c3 > 100;
Пример 1: Предположите, что мы делаем этот запрос:
SELECT * FROM v_merge;
MySQL обрабатывает запрос следующим образом:

  • v_merge становится t.

  • * становится vc1, vc2, которые соответствуют c1, c2.
  • Предложение WHERE представления добавлен.

Получается запрос, который будет выполнен:

SELECT c1, c2 FROM t WHERE c3 > 100;
Пример 2: Предположите, что мы делаем этот запрос:
SELECT * FROM v_merge WHERE vc1 < 100;
Это запрос обработан так же, как предыдущий, за исключением того, что vc1 < 100 становится c1 < 100 и предложение WHERE добавлено к WHERE запроса, используя AND (и круглые скобки добавлены, чтобы удостовериться, что части предложения выполнены с правильным приоритетом). Получается запрос, который будет выполнен:
SELECT c1, c2 FROM t WHERE (c3 > 100) AND (c1 < 100);
Эффективно, у запроса, который будет выполнен, есть WHERE этой формы:
WHERE (select WHERE) AND (view WHERE)
Если алгоритм MERGE не может использоваться, временная таблица должна использоваться вместо этого. MERGE не может использоваться, если представление содержит какие-либо конструкции в следующем списке. Эти конструкции также предотвращают слияние полученных таблиц (см. раздел 9.2.1.18.3).

  • Совокупные функции (SUM(), MIN(), MAX(), COUNT() и подобные).

  • DISTINCT.
  • GROUP BY.
  • HAVING.
  • LIMIT.
  • UNION или UNION ALL.
  • Подзапрос в списке select.
  • Назначение на пользовательские переменные.
  • Обращается только к буквальным значениям (в этом случае нет никакой основной таблицы).

21.5.3. Обновляемые и вставляемые представления

Некоторые представления обновляемые и ссылки на них могут использоваться, чтобы определить таблицы, которые будут обновлены в запросах изменения данных. Таким образом, Вы можете использовать их в таких запросах, как UPDATE, DELETE или INSERT, чтобы обновить содержание основной таблицы. Полученные таблицы могут также быть определены в таблице запроса UPDATE и DELETE, но могут использоваться только для того, чтобы считать данные, чтобы определить строки, которые будут обновлены или удалены. Вообще, ссылки представления должны быть обновляемыми, означая, что они могут быть слиты и не осуществлены. У сложных представлений есть более сложные правила.

Для представления, чтобы быть обновлемым, должны быть непосредственные отношения между строками в представлении и строками в основной таблице. Есть также определенные другие конструкции, которые делают представление необновлемым. Чтобы быть более определенным, представление не обновляемое, если это содержит какое-либо следующие элементы:

  • Совокупные функции (SUM(), MIN(), MAX(), COUNT() и подобные).

  • DISTINCT.
  • GROUP BY.
  • HAVING.
  • LIMIT.
  • UNION или UNION ALL.
  • Подзапрос в списке select. Независимые подзапросы в списке терпят неудачу для INSERT, но работают для UPDATE и DELETE. Для зависимых подзапросов не разрешены никакие запросы изменения данных.
  • Определенные соединения (см. дополнительное обсуждение соединения позже в этом разделе).
  • Ссылка на необновляемое представление в предложении FROM.
  • Подзапрос в WHERE, который относится к таблице в FROM.
  • Обращается только к буквальным значениям (в этом случае нет никакой основной таблицы, чтобы обновить).
  • ALGORITHM = TEMPTABLE (использование временной таблицы всегда делает представление необновляемым).
  • Многократные ссылки на любой столбец базовой таблицы (терпит неудачу для INSERT, работает для UPDATE и DELETE).

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

Для многотабличного представления иногда возможно быть обновляемым, предполагая, что это может быть обработано с алгоритмом MERGE. Для того, чтобы это работало, представление должно использовать внутреннее соединение (не внешнее соединение или UNION ). Кроме того, только единственная таблица в определении представления может быть обновлена, таким образом, предложение SET должно назвать только столбцы одной из таблиц в представлении. Представления, которые используют UNION ALL не разрешены даже при том, что они могли бы быть теоретически обновляемыми.

Относительно вставляемых (то есть обновляемых запросом INSERT) представлений. Представление вставляемое, если оно обновляемое, а также удовлетворяет эти дополнительные требования для столбцов представления:

  • Не должно быть никаких двойных имен столбцов представления.

  • Представление должно содержать все столбцы в базовой таблице, у которых нет значения по умолчанию.
  • Столбцы представления должны быть простыми ссылками столбца. Они не должны быть выражениями, такими как:
    3.14159
    col1 + 3
    UPPER(col2)
    col3 / col4
    (подзапрос)
    

MySQL устанавливает флаг, названный обновляемым представлением, во время CREATE VIEW. Флаг установлен в to YES (true), если UPDATE и DELETE (и подобные операции), являются законными для представления. Иначе, флаг установлен в NO (false). Столбец IS_UPDATABLE в INFORMATION_SCHEMA.VIEWS выводит на экран состояние этого флага. Это означает, что сервер всегда знает, является ли представление обновляемым.

Если представление не обновляемое, такие запросы, как UPDATE, DELETE и INSERT незаконны и отклонены. Отметьте, что, даже если представление обновляемое, невозможно вставить в него, как описано в другом месте в этом разделе.

Обновляемость представлений может быть затронут значением переменной updatable_views_with_limit. См. раздел 6.1.5.

Для следующего обсуждения, предположите, что эти таблицы и представления существуют:

CREATE TABLE t1 (x INTEGER);
CREATE TABLE t2 (c INTEGER);
CREATE VIEW vmat AS SELECT SUM(x) AS s FROM t1;
CREATE VIEW vup AS SELECT * FROM t2;
CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.c;
Запросы INSERT, UPDATE и DELETE разрешены следующим образом:

  • INSERT: Таблица вставки может быть ссылкой представления, которая слита. Если представление является представлением соединения, все компоненты представления должны быть обновляемы (не осуществленные). Для многотабличного обновляемого представления INSERT может работать, если это вставляет в единственную таблицу.

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

    INSERT INTO vjoin (c) VALUES (1);
    
    Это запрос допустим: представление не содержит осуществленных компонентов:
    INSERT INTO vup (c) VALUES (1);
    
  • UPDATE: Таблица или таблицы, которые будут обновлены в UPDATE могут быть ссылками представления, которые слиты. Если представление это представление соединения, по крайней мере один компонент представления должен быть обновляемым (это отличается от INSERT).

    В многотабличном UPDATE обновленные табличные ссылки запроса должны быть базовыми таблицами или ссылками обновляемого представления. Необновленные табличные ссылки могут быть осуществленными представлениями или полученными таблицами.

    Это запрос допустим: столбец c из обновляемой части представления соединения:

    UPDATE vjoin SET c=c+1;
    
    Это запрос недопустим: столбец x из необновляемой части:
    UPDATE vjoin SET x=x+1;
    
    Это запрос допустим: обновленная табличная ссылка многотабличного UPDATE является обновляемым представление (vup):
    UPDATE vup JOIN (SELECT SUM(x) AS s FROM t1) AS dt ON ...
    SET c=c+1;
    
    Это запрос недопустимо: пытается обновить осуществленную полученную таблицу:
    UPDATE vup JOIN (SELECT SUM(x) AS s FROM t1) AS dt ON ...
    SET s=s+1;
    
  • DELETE: Таблица или таблицы, из которых данные будут удалены в DELETE должны быть слиты представлением. Представления соединения не позволены (это отличается от INSERT и UPDATE).

    Это запрос недопустим, потому что представление с соединением:

    DELETE vjoin WHERE ...;
    
    Это запрос допустим, потому что представление слитое (обновляемое):
    DELETE vup WHERE ...;
    
    Это запрос допустим, потому что он удаляет из слитого (обновляемого) представления:
    DELETE vup FROM vup JOIN (SELECT SUM(x) AS s FROM t1) AS dt ON ...;
    

Более раннее обсуждение в этом разделе указало, что представление не вставляемое, если не все столбцы простые ссылки столбца (например, если это содержит столбцы, которые являются выражениями или сложными выражениями). Хотя такое представление не вставляемое, это может быть обновляемым, если Вы обновляете только столбцы, которые не являются выражениями. Рассмотрите это представление:

CREATE VIEW v AS SELECT col1, 1 AS col2 FROM t;
Это представление не вставляемое, потому что col2 выражение. Но оно обновляемое, если обновление не пытается обновить col2. Это обновление допустимо:
UPDATE v SET col1 = 0;
Это обновление не допустимо, потому что оно пытается обновить столбец выражения:
UPDATE v SET col2 = 0;
Если таблица содержит столбец AUTO_INCREMENT, вставка во вставляемое представление на таблице, которая не включает столбец AUTO_INCREMENT, не изменяет значение LAST_INSERT_ID() , потому что побочные эффекты вставки значений по умолчанию в столбцы, не являющиеся частью представления, не должны быть видимыми.

21.5.4. Предложение WITH CHECK OPTION

Предложение WITH CHECK OPTION может быть дано для обновляемого представления, чтобы предотвратить вставку строк, для которых WHERE в select_statement не true. Это предотвращает обновления строк, для которых WHERE true, но обновление заставило бы это быть не истиной (другими словами, это препятствует тому, чтобы видимые строки были обновлены к невидимым строкам).

В предложении WITH CHECK OPTION для обновляемого представления ключевые слова LOCAL и CASCADED определяют контекст тестирования проверки, когда представление определено с точки зрения другого представления. Когда никакое ключевое слово не задано, значение по умолчанию CASCADED.

Тестирование WITH CHECK OPTION следует правилам:

  • С LOCAL предложение WHERE представления проверено, затем проверка рекурсивно проверяет основные представления и применяет те же самые правила.

  • С CASCADED предложение WHERE представления проверено, затем проверка рекурсивно проверяет основные представления, добавляет к ним опцию WITH CASCADED CHECK OPTION (в целях проверки, их определения остаются неизменными) и применяет те же самые правила.
  • Без опции проверки предложение WHERE представления не проверено, затем проверка рекурсивно проверяет основные представления и применяет те же самые правила.

Рассмотрите определения для следующей таблицы и набора представлений:

CREATE TABLE t1 (a INT);
CREATE VIEW v1 AS SELECT * FROM t1 WHERE a < 2 WITH CHECK OPTION;
CREATE VIEW v2 AS SELECT * FROM v1 WHERE a > 0 WITH LOCAL CHECK OPTION;
CREATE VIEW v3 AS SELECT * FROM v1 WHERE a > 0 WITH CASCADED CHECK OPTION;
Здесь представления v2 и v3 определены с точки зрения другого представления v1.

Вставки для v2 проверены по опции LOCAL, затем проверка переходит к v1 и правила применены снова. Правила для v1 вызовут отказ проверки. Проверка на v3 также терпит неудачу:

mysql> INSERT INTO v2 VALUES (2);
ERROR 1369 (HY000): CHECK OPTION failed 'test.v2'
mysql> INSERT INTO v3 VALUES (2);
ERROR 1369 (HY000): CHECK OPTION failed 'test.v3'

21.5.5. Метаданные о представлении

Метаданные о представлениях могут быть получены следующим образом:

21.6. Управление доступом для сохраненных программ и представлений

Сохраненные программы и представления определены до использования и выполняются в пределах контекста безопасности, который определяет их привилегии. Этими привилегиями управляет их атрибут DEFINER и, если есть, характеристика SQL SECURITY.

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

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

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

Контексты безопасности отличаются следующим образом:

  • Сохраненная программа или представление, которое выполняется в контексте безопасности создателя, выполняют с привилегиями учетной записи, названной DEFINER. Эти привилегии могут полностью отличаться от таковых пользователя вызова. У вызывающего должны быть соответствующие привилегии, чтобы сослаться на объект (например, EXECUTE, чтобы вызвать хранимую процедуру или SELECT, чтобы выбрать данные из представления), но когда объект выполняется, эти привилегии проигнорированы и учитываются только привилегии учетной записи DEFINER. Если у этой учетной записи есть немного привилегий, объект соответственно ограничен в операциях, которые это может выполнить. Если DEFINER имеет большие права (например, это root), объект может выполнить сильные операции независимо от того, кто его вызывает.

  • Сохраненная подпрограмма или представление, которое выполняется в контексте безопасности вызывающего, могут выполнить только операции, для которых у вызывающего есть привилегии. DEFINER признак может быть определен, но не имеет никакого эффекта для объектов, которые выполняются в контексте безопасности вызывающего.

Рассмотрите следующую хранимую процедуру:

CREATE DEFINER = 'admin'@'localhost' PROCEDURE p1()
SQL SECURITY DEFINER
BEGIN
  UPDATE t1 SET counter = counter + 1;
END;
Любой пользователь, который имеет привилегию EXECUTE для p1 может вызвать это с CALL . Однако, когда p1 выполняется, это работает в контексте безопасности DEFINER и таким образом выполняется с привилегиями 'admin'@'localhost', учетной записм, названной в атрибуте DEFINER. Эта учетная запись должна иметь привилегию EXECUTE для p1 так же как UPDATE для t1. Иначе процедура терпит неудачу.

Теперь рассмотрите эту хранимую процедуру, которая идентична p1 за исключением того, что характеристика SQL SECURITY INVOKER:

CREATE DEFINER = 'admin'@'localhost' PROCEDURE p2()
SQL SECURITY INVOKER
BEGIN
  UPDATE t1 SET counter = counter + 1;
END;
p2, в отличие от p1, выполняется в контексте безопасности INVOKER. Атрибут DEFINER не важен и p2 выполняется с привилегиями пользователя вызова. p2 терпит неудачу, если вызывающему недостает привилегии EXECUTE для p2 или UPDATE для таблицы с именем t1.

MySQL использует следующие правила, чтобы управлять тем, какие учетные записи пользователь может определить в атрибуте DEFINER объекта:

  • Вы можете определить значение DEFINER не к своей собственной учетной записи, только если Вы имеете привилегию SUPER.

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

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

  • Для сохраненной подпрограммы или представления, надо использовать SQL SECURITY INVOKER в определении объекта, когда возможно, чтобы это могло использоваться только пользователями с разрешениями, подходящими для операций, выполненных объектом.

  • Если Вы создаете сохраненные программы контекста создателя, используя учетную запись, которая имеет привилегию SUPER, определите явно признак DEFINER, который называет учетную запись, обладающую только привилегиями требуемыми для операций выполненных объектом. Определите чрезвычайно привилегированный DEFINER только когда абсолютно необходимо.
  • Администраторы могут препятствовать тому, чтобы пользователи определили чрезвычайно привилегированные учетные записи в DEFINER, не предоставляя им привилегию SUPER .
  • Объекты контекста создателя должны быть написаны, имея в виду, что они могут быть в состоянии получить доступ к данным, для которых у пользователя вызова нет никаких привилегий. В некоторых случаях Вы можете предотвратить ссылку на эти объекты, не предоставляя неправомочным пользователям особые привилегии:

    • На хранимую процедуру или функцию не может сослаться пользователь, который не имеет привилегии EXECUTE для этого.

    • На представление не может сослаться пользователь, у которого нет соответствующей привилегии для него (SELECT для выбора из, INSERT для вставки и т.д.).

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

21.7. Двоичное журналирование сохраненных программ

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

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

  • Определенные операции восстановления данных требуют использования двоичного журнала. После того как резервный файл был восстановлен, события в двоичном журнале, которые были зарегистрированы после того, как резервное копирование было сделано, повторно выполнены. Эти события осовременивают базы данных от пункта резервного копирования. См. раздел 8.3.2.

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

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

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

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

Вообще, проблемы описанные здесь, бывают, когда двоичное журналирование происходит на уровне запроса SQL. Если Вы используете основанное на строке двоичное журналирование, журнал содержит изменения, произведенные в отдельных строках в результате выполнения запросов SQL. Когда подпрограммы или триггеры выполняются, зарегистрированы изменения строки, а не запросы, которые производят изменения. Для хранимых процедур это означает, что запрос CALL не зарегистрирован. Для сохраненных функций изменения строки, произведенные в пределах функции, зарегистрированы, а не вызов функции. Для триггеров зарегистрированы изменения строки, произведенные триггером. На ведомой стороне замечены только изменения строки, а не сохраненный вызов программы. Для общей информации об основанном на строке журналировании см. раздел 19.2.1.

Если не отмечено иное, замечания здесь предполагают, что Вы включили двоичное журналирование, запуская сервер с опцией --log-bin (см. раздел 6.4.4). Если двоичный журнал не включен, репликация невозможна, и при этом двоичный журнал недоступен для восстановления данных.

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

  • Чтобы создать или изменить сохраненную функцию, Вы должны иметь привилегию SUPER в дополнение к CREATE ROUTINE или ALTER ROUTINE, которая обычно требуется. В зависимости от значения DEFINER в функциональном определении, SUPER может требоваться независимо от того, включено ли двоичное журналирование. См. раздел 14.1.13.

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

    По умолчанию для запроса CREATE FUNCTION, который будет принят, по крайней мере один из DETERMINISTIC, NO SQL или READS SQL DATA должен быть определен явно. Иначе происходит ошибка:

    ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL,
    or READS SQL DATA in its declaration and binary logging is enabled
    (you *might* want to use the less safe
    log_bin_trust_function_creators variable)
    
    Эта функция детерминирована (и не изменяет данные), таким образом, это безопасно:
    CREATE FUNCTION f1(i INT) RETURNS INT DETERMINISTIC READS SQL DATA
    BEGIN
      RETURN i;
    END;
    
    Эта функция использует UUID() , которая не детерминирована, таким образом, функция также не детерминирована и не безопасна:
    CREATE FUNCTION f2() RETURNS CHAR(36) CHARACTER SET utf8
    BEGIN
      RETURN UUID();
    END;
    
    Эта функция изменяет данные, таким образом, это, возможно, не безопасно:
    CREATE FUNCTION f3(p_id INT)
    RETURNS INT
    BEGIN
      UPDATE t SET modtime = NOW() WHERE id = p_id;
      RETURN ROW_COUNT();
    END;
    
    Оценка природы функции основана на определении создателя: MySQL не проверяет, что функция, объявленная DETERMINISTIC, свободна от запросов, которые приводят к недетерминированным результатам.
  • Хотя возможно создать детерминированную сохраненную функцию без определения DETERMINISTIC, Вы не можете выполнить эту функцию, используя основанное на запросе двоичное журналирование. Чтобы выполнить такую функцию, Вы должны использовать основанное на строке или смешанное двоичное журналирование. Альтернативно, если Вы явно определяете DETERMINISTIC в функциональном определении Вы можете использовать любой вид журналирования, включая основанное на запросе журналирование.
  • Чтобы ослабить предыдущие условия функционального создания (что Вы должны иметь привилегию SUPER, и что функция должна быть объявлена детерминированной или не изменять данные), установите глобальную переменную log_bin_trust_function_creators в 1. По умолчанию у этой переменной есть значение 0, но Вы можете изменить ее
    mysql> SET GLOBAL log_bin_trust_function_creators = 1;
    
    Вы можете также установить эту переменную при использовании опции --log-bin-trust-function-creators=1, запуская сервер.

    Если двоичное журналирование не включено, log_bin_trust_function_creators не применяется. SUPER не требуется для функционального создания если, как описано ранее, DEFINER в функциональном определении требует этого.

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

Триггеры подобны сохраненным функциям, таким образом, предыдущие замечания относительно функций также относятся к триггерам со следующим исключением: CREATE TRIGGER не имеет дополнительной характеристики DETERMINISTIC, таким образом, триггеры, как предполагается, всегда детерминированы. Однако, это предположение могло бы в некоторых случаях быть недопустимым. Например, функция UUID() недетерминирована (и не копируется). Вы должны быть осторожными относительно использования таких функций в триггерах.

Триггеры могут обновить таблицы, таким образом, сообщения об ошибках, подобные тем для сохраненных функций, происходят с CREATE TRIGGER, если у Вас нет необходимых привилегий. На ведомой стороне сервер использует атрибут DEFINER триггера, чтобы определить, какой пользователь является создателем триггера.

Остальная часть этого раздела обеспечивает дополнительные детали о выполнении журналирования и его значениях. Это обсуждение применяется только для основанного на запросе журналирования, а не для основанного на строке журналирования, за исключением первого элемента: CREATE и DROP зарегистрированы как запросы независимо от режима журналирования.

  • Сервер пишет CREATE EVENT, CREATE PROCEDURE, CREATE FUNCTION, ALTER EVENT, ALTER PROCEDURE, ALTER FUNCTION, DROP EVENT, DROP PROCEDURE и DROP FUNCTION в двоичный журнал.

  • Сохраненная функция зарегистрирована как SELECT, если функция изменяет данные и происходит в пределах запроса, который не был бы иначе зарегистрирован. Это предотвращает игнорирование изменений данных, которые следуют из использования сохраненных функций в незарегистрированных запросах. Например, SELECT не записаны в двоичный журнал, но SELECT мог бы вызвать сохраненную функцию, которая производит изменения. Чтобы обработать это, SELECT func_name() записан в двоичный журнал, когда данная функция производит изменение. Предположите, что следующие запросы выполнены на ведущем устройстве:
    CREATE FUNCTION f1(a INT) RETURNS INT
    BEGIN
      IF (a < 3) THEN INSERT INTO t2 VALUES (a);
      END IF;
      RETURN 0;
    END;
    
    CREATE TABLE t1 (a INT);
    INSERT INTO t1 VALUES (1),(2),(3);
    SELECT f1(a) FROM t1;
    
    Когда SELECT выполняется, функция f1() вызвана три раза. Два из тех вызовов вставляют строку, и MySQL регистрирует SELECT для каждого из них. Таким образом, MySQL пишет следующие запросы в двоичный журнал:
    SELECT f1(1);
    SELECT f1(2);
    
    Сервер также регистрирует запрос SELECT для сохраненной функции , когда функция вызывает хранимую процедуру, которая вызывает ошибку. В этом случае сервер пишет SELECT в журнал наряду с ожидаемым кодом ошибки. На ведомом устройстве, если та же самая ошибка происходит, что является ожидаемым результатом, репликация продолжается. Иначе репликация встанет.
  • У журналирования сохраненных функциональных вызовов, а не запросов, выполненных функцией, есть значение безопасности для репликации, которое является результатом двух факторов:

    • Для функции возможно следовать различными путями выполнения на основных и ведомых серверах.

    • Запросы, выполненные на ведомом устройстве, обработаны ведомым потоком SQL, у которого есть полные привилегии.

    Хотя пользователь должен иметь привилегию CREATE ROUTINE, чтобы создать функцию, пользователь может написать функцию, содержащую опасный запрос, который выполнится только на ведомом устройстве, где это обработано потоком, у которого есть полные привилегии. Например, если у основного и ведомого серверов есть значения идентификаторов сервера 1 и 2, соответственно, пользователь на главном сервере мог создать и вызвать опасную функцию unsafe_func():

    mysql> delimiter //
    mysql> CREATE FUNCTION unsafe_func () RETURNS INT
        -> BEGIN
        ->   IF @@server_id=2 THEN dangerous_statement;
        ->   END IF;
        ->   RETURN 1;
        -> END;
        -> //
    mysql> delimiter ;
    mysql> INSERT INTO t VALUES(unsafe_func());
    
    Запросы CREATE FUNCTION и INSERT записаны в двоичный журнал, таким образом, ведомое устройство выполнит их. Поскольку у ведомого потока SQL есть полные привилегии, он выполнит опасный запрос. Таким образом, функциональный вызов имеет различные эффекты на ведущее и ведомое устройство и не безопасен для репликации.

    Чтобы принять меры против этой опасности для серверов, которым включили двоичное журналирование, создатели сохраненных функций должны иметь привилегию SUPER, в дополнение к обычной CREATE ROUTINE, которая требуется. Точно так же, чтобы использовать ALTER FUNCTION Вы должны иметь SUPER в дополнение к ALTER ROUTINE . Без SUPER произойдет такая ошибка:

    ERROR 1419 (HY000): You do not have the SUPER privilege and
    binary logging is enabled (you *might* want to use the less safe
    log_bin_trust_function_creators variable)
    
    Если Вы не хотите требовать, чтобы функциональные создатели имели привилегию SUPER (например, если все пользователи с привилегией CREATE ROUTINE на Вашей системе опытные разработчики приложений), установите глобальную системную переменную log_bin_trust_function_creators в 1. Вы можете также установить эту переменную при использовании опции --log-bin-trust-function-creators=1, запуская сервер. Если двоичное журналирование не включено, log_bin_trust_function_creators не применяется. SUPER не требуется для функционального создания если, как описано ранее, значение DEFINER в функциональном определении требует этого.
  • Если функция, которая выполняет обновления, недетерминирована, это неповторимо. Это может иметь два нежелательных эффекта:

    • Это сделает ведомое устройство отличающимся от ведущего.

    • Восстановленные данные будут отличаться от оригинальных данных.

    Чтобы иметь дело с этими проблемами, MySQL проводит в жизнь следующее требование: на главном сервере создание и изменение функции не работает, если Вы не объявляете, что функция детерминирована или не изменяет данные. Два набора функциональных характеристик применяются здесь:

    • DETERMINISTIC и NOT DETERMINISTIC указывают, приводит ли функция всегда к тому же самому результату для данных вводов. Значение по умолчанию NOT DETERMINISTIC, если никакая характеристика не дана. Чтобы объявить, что функция детерминирована, Вы должны определить DETERMINISTIC явно.

    • CONTAINS SQL, NO SQL, READS SQL DATA и MODIFIES SQL DATA предоставляют информацию о том, читает ли функция или пишет данные. Также NO SQL или READS SQL DATA указывает, что функция не изменяет данные, но Вы должны определить один из них явно, потому что значение по умолчанию CONTAINS SQL, если никакая характеристика не задана.

    По умолчанию для CREATE FUNCTION , который будет принят, по крайней мере один из DETERMINISTIC, NO SQL или READS SQL DATA должен быть определен явно. Иначе ошибка происходит:

    ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL,
    or READS SQL DATA in its declaration and binary logging is enabled
    (you *might* want to use the less safe
    log_bin_trust_function_creators variable)
    
    Если Вы устанавливаете log_bin_trust_function_creators в 1, требование, чтобы функции быть детерминированными или не изменяли данные, пропущено.
  • Требования хранимой процедуры зарегистрированы на уровне запроса, а не на уровне вызова CALL. Таким образом, сервер не регистрирует сам CALL, это регистрирует те запросы в пределах процедуры, которые фактически выполняют. В результате те же самые изменения, которые происходят на ведущем устройстве, будут наблюдаться относительно ведомых серверов. Это предотвращает проблемы, которые могли следовать из процедуры, имеющей различные пути выполнения на разных машинах.

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

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

      NAME_CONST(var_name, var_value)
      
      var_name местное имя переменной, а var_value постоянное указание на значение, которое имеет переменная в то время, когда запрос зарегистрирован. NAME_CONST() имеет значение var_value и name var_name. Таким образом, если Вы вызываете эту функцию непосредственно, Вы получаете результат:
      mysql> SELECT NAME_CONST('myname', 14);
      +--------+
      | myname |
      +--------+
      | 14     |
      +--------+
      
      NAME_CONST() позволяет зарегистрированному автономному запросу быть выполненным на ведомом устройстве с тем же самым эффектом как оригинальный запрос, который был выполнен на ведущем устройстве в пределах хранимой процедуры.

      Использование NAME_CONST() может привести к проблеме для CREATE TABLE ... SELECT, когда исходные выражения столбца обращаются к местным переменным. Преобразование этих ссылок в выражения NAME_CONST() может привести к именам столбцов, которые отличаются на основных и ведомых серверах, или именам, которые являются слишком длинными, чтобы быть допустимыми идентификаторами столбца. Обходное решение должно поставлять псевдонимы для столбцов, которые обращаются к местным переменным. Рассмотрите этот запрос, когда myvar имеет значение 1:

      CREATE TABLE t1 SELECT myvar;
      
      Это будет переписано следующим образом:
      CREATE TABLE t1 SELECT NAME_CONST(myvar,1);
      
      Чтобы гарантировать, что у основных и ведомых таблиц есть те же самые имена столбцов, напишите запрос так:
      CREATE TABLE t1 SELECT myvar AS myvar;
      
      Переписанный запрос становится:
      CREATE TABLE t1 SELECT NAME_CONST(myvar, 1) AS myvar;
      
    • Запрос, который будет зарегистрирован, мог бы содержать ссылки на определяемые пользователем переменные. Чтобы обработать это, MySQL пишет SET в двоичный журнал, чтобы удостовериться, что переменная существует на ведомом устройстве с тем же самым значением, как на ведущем устройстве. Например, если запрос относится к переменной @my_var, этому запросу будет предшествовать в двоичном журнале следующий запрос, где value значение @my_var на ведущем сервере:
      SET @my_var = value;
      
    • Вызовы процедуры могут произойти в пределах законченной или отмененной транзакции. Контекст транзакции составляется так, чтобы аспекты выполнения процедуры копировались правильно. Таким образом, сервер регистрирует те запросы в пределах процедуры, которые фактически выполняются и изменяют данные, а также регистрирует BEGIN, COMMIT и ROLLBACK по мере необходимости. Например, если процедура обновляет только транзакционные таблицы и выполнена в пределах транзакции, которая понижена до прежнего уровня, те обновления не зарегистрированы. Если процедура происходит в пределах завершенной транзакции, BEGIN и COMMIT зарегистрированы с обновлениями. Для процедуры, которая выполняется в пределах отмененной транзакции, ее запросы зарегистрированы, используя те же самые правила, которые применились бы, если бы запросы были выполнены автономным способом:

      • Обновления транзакционных таблиц не зарегистрированы.

      • Обновления нетранзакционных таблиц зарегистрированы, потому что обратная перемотка не отменяет их.
      • Обновления соединения транзакционных и нетранзакционных таблиц зарегистрированы, окруженные BEGIN и ROLLBACK так, чтобы ведомые устройства произвели те же самые изменения и отмены, как на ведущем устройстве.
  • Требование хранимой процедуры НЕ записано в двоичный журнал на уровне запроса, если процедура вызвана изнутри сохраненной функции. В этом случае единственной зарегистрированной вещью является запрос, который вызывает функцию (если это происходит в пределах запроса, который зарегистрирован) или запрос DO (если это происходит в пределах запроса, который не зарегистрирован). Поэтому забота должна быть осуществлена в использовании сохраненных функций, которые вызывают процедуру, даже если процедура иначе безопасна сама по себе.

Поиск

 

Найди своих коллег!

Вы можете направить письмо администратору этой странички, Алексею Паутову. mailto:alexey.v.pautov@mail.ru