Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
11
Добавлен:
15.01.2021
Размер:
10.35 Mб
Скачать

Пример 44: взаимодействие конкурирующих транзакций

Итоговые результаты взаимодействия транзакций таковы.

 

 

 

Уровень изолированности транзакции B

 

 

 

READ COMMITTED

SERIALIZABLE

 

 

 

READ ONLY

READ WRITE

READ ONLY

READ WRITE

 

 

 

Первый и вто-

Первый и вто-

Первый и вто-

Первый и вто-

 

 

 

рой SELECT

рой SELECT

рой SELECT

рой SELECT

 

 

 

возвратили

возвратили

возвратили

возвратили

 

 

READ ONLY

одинаковые

одинаковые

одинаковые

одинаковые

 

 

данные, UP-

данные, UP-

данные, UP-

данные, UP-

 

 

 

A

READ

 

DATE в тран-

DATE в тран-

DATE в тран-

DATE в тран-

 

закции A за-

закции A за-

закции A за-

закции A за-

транзакции

COMMITTED

 

 

прещён (R/O)

прещён (R/O)

прещён (R/O)

прещён (R/O)

 

 

 

 

 

 

 

 

Первый и вто-

Первый и вто-

Первый и вто-

Первый и вто-

 

 

 

рой SELECT

рой SELECT

рой SELECT

рой SELECT

изолированности

 

READ WRITE

возвратили

возвратили

возвратили

возвратили

 

 

одинаковые

одинаковые

одинаковые

одинаковые

 

 

 

одинаковые

разные дан-

одинаковые

одинаковые

 

 

 

данные

ные

данные

данные

 

 

 

Первый и вто-

Первый и вто-

Первый и вто-

Первый и вто-

 

 

 

рой SELECT

рой SELECT

рой SELECT

рой SELECT

 

 

 

возвратили

возвратили

возвратили

возвратили

Уровень

SERIALIZABLE

READ ONLY

закции A за-

закции A за-

закции A за-

закции A запре-

 

 

 

 

данные, UP-

данные, UP-

данные, UP-

данные, UP-

 

 

 

DATE в тран-

DATE в тран-

DATE в тран-

DATE в тран-

 

 

 

прещён (R/O)

прещён (R/O)

прещён (R/O)

щён (R/O)

 

 

 

Первый и вто-

Первый и вто-

Первый и вто-

Первый и вто-

 

 

 

рой SELECT

рой SELECT

рой SELECT

рой SELECT

 

 

READ WRITE

возвратили

возвратили

возвратили

возвратили

 

 

 

одинаковые

разные дан-

одинаковые

одинаковые

 

 

 

данные

ные

данные

данные

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 460/545

Пример 44: взаимодействие конкурирующих транзакций

Фантомное чтение в Oracle может быть исследовано выполнением в двух отдельных сессиях следующих блоков кода:

Oracle

 

Решение 6.2.2.a (код для исследования аномалии фантомного чтения)

1

-- Транзакция A:

-- Транзакция B:

2

ALTER SESSION SET

ALTER SESSION SET

3

ISOLATION_LEVEL = {УРОВЕНЬ};

ISOLATION_LEVEL = {УРОВЕНЬ};

4

SET TRANSACTION {РЕЖИМ};

SET TRANSACTION {РЕЖИМ};

5

SELECT 'Tr A: ' ||

SELECT

'Tr B: ' ||

6

GET_IDS_AND_ISOLATION_LEVEL

GET_IDS_AND_ISOLATION_LEVEL

7

FROM DUAL;

FROM DUAL;

8

SELECT 'Tr A START: ' ||

SELECT

'Tr B START: ' ||

9

GET_CT FROM DUAL;

GET_CT

FROM DUAL;

10

 

 

SELECT

'Tr B COUNT-1: ' ||

11

 

 

GET_CT

FROM DUAL;

12

EXEC DBMS_LOCK.SLEEP(5);

SELECT

COUNT(*)

13

 

 

FROM

"subscriptions"

14

 

 

WHERE

"sb_id" > 500;

 

 

 

 

15

SELECT 'Tr A INSERT: ' ||

 

 

16

GET_CT FROM DUAL;

 

 

17

INSERT INTO "subscriptions"

 

 

18

 

("sb_id",

 

 

19

 

"sb_subscriber",

 

 

20

 

"sb_book",

 

 

21

 

"sb_start",

 

 

22

 

"sb_finish",

 

 

23

 

"sb_is_active")

 

 

24

 

VALUES (1000,

 

 

25

1,

EXEC DBMS_LOCK.SLEEP(10);

26

1,

 

 

27

 

TO_DATE('2025-01-12',

 

 

28

 

'YYYY-MM-DD'),

 

 

29

 

TO_DATE('2026-01-12',

 

 

30

 

'YYYY-MM-DD'),

 

 

31

 

'N');

 

 

32

 

 

SELECT

'Tr B COUNT-2: ' ||

33

 

 

GET_CT

FROM DUAL;

34

EXEC DBMS_LOCK.SLEEP(10);

SELECT

COUNT(*)

35

 

 

FROM

"subscriptions"

36

 

 

WHERE

"sb_id" > 500;

37

SELECT 'Tr A ROLLBACK: ' ||

 

 

38

GET_CT FROM DUAL;

EXEC DBMS_LOCK.SLEEP(15);

39

ROLLBACK;

 

 

40

 

 

SELECT

'Tr B COUNT-3: ' ||

41

 

 

GET_CT

FROM DUAL;

42

 

 

SELECT

COUNT(*)

43

 

 

FROM

"subscriptions"

44

 

 

WHERE

"sb_id" > 500;

45

 

 

SELECT

'Tr B COMMIT: ' ||

46

 

 

GET_CT

FROM DUAL;

47

 

 

COMMIT;

 

Перед выполнением представленных выше блоков кода необходимо отключить триггер, обеспечивающий автоинкрементацию первичного ключа в таблице subscriptions (ALTER TRIGGER "TRG_subscriptions_sb_id" DISABLE), а

после проведения эксперимента — снова включить этот триггер (ALTER TRIGGER "TRG_subscriptions_sb_id" ENABLE).

Добавлять эти команды непосредственно перед и после INSERT в транзакции A нельзя, т.к. ALTER TRIGGER приводит к автоматическому подтверждению предыдущей транзакции и запуску новой.

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 461/545

Пример 44: взаимодействие конкурирующих транзакций

Итоговые результаты взаимодействия транзакций таковы.

 

 

 

Уровень изолированности транзакции B

 

 

 

READ COMMITTED

SERIALIZABLE

 

 

 

READ ONLY

READ WRITE

READ ONLY

READ WRITE

 

 

 

INSERT в

INSERT в

INSERT в

INSERT в

A

 

READ ONLY

транзакции A

транзакции A

транзакции A

транзакции A

 

запрещён

запрещён

запрещён

запрещён

транзакции

 

 

READ

 

(R/O)

(R/O)

(R/O)

(R/O)

 

 

 

 

Транзакция B

Транзакция B

Транзакция B

Транзакция B

 

COMMITTED

 

 

 

не получает

не получает

не получает

не получает

 

 

 

изолированности

 

READ WRITE

доступа к

доступа к

доступа к

доступа к

 

 

(R/O)

(R/O)

(R/O)

(R/O)

 

 

 

«фантомной

«фантомной

«фантомной

«фантомной

 

 

 

записи»

записи»

записи»

записи»

 

 

 

INSERT в

INSERT в

INSERT в

INSERT в

 

 

READ ONLY

транзакции A

транзакции A

транзакции A

транзакции A

 

 

запрещён

запрещён

запрещён

запрещён

 

 

 

Уровень

SERIALIZABLE

 

 

 

 

 

READ WRITE

доступа к

доступа к

доступа к

доступа к

 

 

Транзакция B

Транзакция B

Транзакция B

Транзакция B

 

 

 

не получает

не получает

не получает

не получает

 

 

 

«фантомной

«фантомной

«фантомной

«фантомной

 

 

 

записи»

записи»

записи»

записи»

На этом решение данной задачи завершено.

Решение 6.2.2.b{434}.

В решении{434} задачи 6.2.2.a{434} в некоторых случаях мы получали ситуацию взаимной блокировки транзакций, но сейчас мы рассмотрим код, который гарантированно приводит к такой ситуации во всех трёх СУБД.

На низких уровнях изолированности транзакций у СУБД может появиться возможность избежать взаимной блокировки, потому мы используем уровень SERIALIZABLE. Исследование поведения СУБД при работе на других уровнях изолированности вам предлагается провести самостоятельно в задании 6.2.2.TSK.F{464}.

Важно отметить, что только MS SQL Server позволяет указывать приоритет транзакции, который учитывает при принятии решения о том, какая из двух взаимно заблокированных транзакций будет отменена, MySQL и Oracle принимают такое решение полностью самостоятельно.

Представленный ниже код работает по следующему алгоритму:

транзакция A обновляет первую таблицу;

транзакция B обновляет вторую таблицу;

транзакция A пытается обновить вторую таблицу (ряд, заблокированный транзакцией B);

транзакция B пытается обновить первую таблицу (ряд, заблокированный транзакцией A);

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

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 462/545

Пример 44: взаимодействие конкурирующих транзакций

Решение для MySQL выглядит следующим образом.

MySQL

 

Решение 6.2.2.b

 

 

1

-- Транзакция A:

-- Транзакция B:

2

SET autocommit = 0;

SET autocommit = 0;

3

SET SESSION TRANSACTION

SET SESSION TRANSACTION

4

ISOLATION LEVEL SERIALIZABLE;

ISOLATION LEVEL SERIALIZABLE;

5

START TRANSACTION;

START TRANSACTION;

6

UPDATE

`books`

 

 

7

SET

 

`b_name` =

SELECT

SLEEP(3);

8

 

 

 

CONCAT(`b_name`, '.')

 

 

9

WHERE

`b_id` = 1;

 

 

 

 

 

 

 

 

 

10

 

 

 

 

UPDATE

`subscribers`

11

SELECT

SLEEP(5);

SET

`s_name` =

12

 

 

 

 

 

CONCAT(`s_name`, '.')

13

 

 

 

 

WHERE

`s_id` = 1;

14

UPDATE

`subscribers`

 

 

15

SET

 

`s_name` =

SELECT

SLEEP(3);

16

 

 

 

CONCAT(`s_name`, '.')

 

 

17

WHERE

`s_id` = 1;

 

 

18

COMMIT;

 

UPDATE

`books`

19

 

 

 

 

SET

`b_name` =

20

 

 

 

 

 

CONCAT(`b_name`, '.')

21

 

 

 

 

WHERE

`b_id` = 1;

22

 

 

 

 

COMMIT;

 

Решение для MS SQL Server выглядит следующим образом. Обратите внимание на строку 5, в которой для первой транзакции устанавливается повышенный, а для второй — пониженный приоритет, в силу чего СУБД всегда будет отменять вторую транзакцию, позволяя первой успешно завершиться.

MS SQL

 

Решение 6.2.2.b

 

 

 

 

 

1

-- Транзакция A:

-- Транзакция B:

2

SET IMPLICIT_TRANSACTIONS ON;

SET IMPLICIT_TRANSACTIONS ON;

3

SET TRANSACTION ISOLATION

SET TRANSACTION ISOLATION

4

LEVEL SERIALIZABLE;

LEVEL SERIALIZABLE;

5

SET DEADLOCK_PRIORITY HIGH;

SET DEADLOCK_PRIORITY LOW;

6

BEGIN TRANSACTION;

BEGIN TRANSACTION;

6

UPDATE

[books]

 

 

7

SET

 

[b_name] =

WAITFOR DELAY '00:00:03';

8

 

 

 

CONCAT([b_name], '.')

 

 

9

WHERE

[b_id] = 1;

 

 

10

 

 

 

 

UPDATE

[subscribers]

11

WAITFOR DELAY '00:00:05';

SET

[s_name] =

12

 

 

 

 

 

CONCAT([s_name], '.')

13

 

 

 

 

WHERE

[s_id] = 1;

14

UPDATE

[subscribers]

 

 

15

SET

 

[s_name] =

WAITFOR DELAY '00:00:03';

16

 

 

 

CONCAT([s_name], '.')

 

 

17

WHERE

[s_id] = 1;

 

 

 

 

 

 

 

18

COMMIT;

 

UPDATE

[books]

19

 

 

 

 

SET

[b_name] =

20

 

 

 

 

 

CONCAT([b_name], '.')

21

 

 

 

 

WHERE

[b_id] = 1;

22

 

 

 

 

COMMIT;

 

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 463/545

Пример 44: взаимодействие конкурирующих транзакций

Решение для Oracle выглядит следующим образом.

Oracle

 

Решение 6.2.2.b

 

 

1

-- Транзакция A:

-- Транзакция B:

2

ALTER SESSION SET

ALTER SESSION SET

3

ISOLATION_LEVEL = SERIALIZABLE;

ISOLATION_LEVEL = SERIALIZABLE;

4

SET TRANSACTION READ WRITE;

SET TRANSACTION READ WRITE;

5

UPDATE

"books"

 

 

6

SET

"b_name" =

EXEC DBMS_LOCK.SLEEP(3);

7

 

 

CONCAT("b_name", '.')

 

 

8

WHERE

"b_id" = 1;

 

 

9

 

 

 

UPDATE

"subscribers"

10

EXEC DBMS_LOCK.SLEEP(5);

SET

"s_name" =

11

 

 

 

 

CONCAT("s_name", '.')

12

 

 

 

WHERE

"s_id" = 1;

14

UPDATE

"subscribers"

 

 

15

SET

"s_name" =

EXEC DBMS_LOCK.SLEEP(3);

16

 

 

CONCAT("s_name", '.')

 

 

17

WHERE

"s_id" = 1;

 

 

18

COMMIT;

 

UPDATE

"books"

19

 

 

 

SET

"b_name" =

20

 

 

 

 

CONCAT("b_name", '.')

21

 

 

 

WHERE

"b_id" = 1;

22

 

 

 

COMMIT;

 

 

 

 

 

 

 

На этом решение данной задачи завершено.

Задание 6.2.2.TSK.A: повторить исследование, представленное в решении{434} задачи 6.2.2.a{434} и лично посмотреть на поведение всех трёх СУБД во всех рассмотренных ситуациях.

Задание 6.2.2.TSK.B: повторить исследование, представленное в решении{462} задачи 6.2.2.b{434} и лично посмотреть на поведение всех трёх СУБД во всех рассмотренных ситуациях.

Задание 6.2.2.TSK.C: написать код, в котором запрос, инвертирующий значения поля sb_is_active таблицы subscriptions с Y на N и

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

Задание 6.2.2.TSK.D: провести исследование поведения MySQL в контексте аномалии неповторяющегося чтения, выполняя первую операцию в каждой транзакции в режимах LOCK IN SHARE MODE и FOR UPDATE. (см. решение{434} задачи 6.2.2.a{434}).

Задание 6.2.2.TSK.E: провести исследование поведения MS SQL Server в контексте аномалий потерянного обновления и неповторяющегося чтения, выполняя первую операцию в каждой транзакции с использованием «табличной подсказки38» UPDLOCK (см. решение{434} задачи 6.2.2.a{434}).

Задание 6.2.2.TSK.F: повторить решение{462} задачи 6.2.2.b{434} для всех трёх СУБД в остальных поддерживаемых ими уровнях изолированности транзакций, найти такие комбинации уровней изолированности, при которых взаимная блокировка транзакций не возникает.

38 https://msdn.microsoft.com/en-us/library/ms187373%28v=sql.110%29.aspx

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 464/545

Пример 45: управление транзакциями в триггерах, хранимых функциях и процедурах

6.2.3.Пример 45: управление транзакциями в триггерах, хранимых функциях и процедурах

Задача 6.2.3.a{465}: создать на таблице books триггер, определяющий уровень изолированности транзакции, в котором сейчас проходит операция вставки, и отменяющий операцию, если уровень изолированности транзакции отличен от SERIALIZABLE.

Задача 6.2.3.b{469}: создать хранимую функцию, порождающую исключительную ситуацию в случае запуска в режиме автоподтверждения транзакций.

Задача 6.2.3.c{471}: создать хранимую процедуру, выполняющую подсчёт количества записей в указанной таблице таким образом, чтобы запрос выполнялся максимально быстро (вне зависимости от параллельно выполняемых запросов), даже если в итоге он вернёт не совсем корректные данные.

Ожидаемый результат 6.2.3.a.

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

Ожидаемый результат 6.2.3.b.

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

Ожидаемый результат 6.2.3.c.

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

Решение 6.2.3.a{465}.

Для простоты (отсутствия необходимости вручную выполнять вставку) и единообразия (поддержки всеми тремя СУБД) используем AFTER-триггеры.

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

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 465/545

Пример 45: управление транзакциями в триггерах, хранимых функциях и процедурах

Решение для MySQL выглядит следующим образом.

MySQL

Решение 6.2.3.a (код триггера)

1 DELIMITER $$

2

3CREATE TRIGGER `books_ins_trans`

4AFTER INSERT

5ON `books`

6FOR EACH ROW

7BEGIN

8DECLARE isolation_level VARCHAR(50);

9

10SET isolation_level =

11(

12SELECT `VARIABLE_VALUE`

13

 

FROM

`information_schema`.

14

 

 

`session_variables`

15

 

WHERE

`VARIABLE_NAME` =

16

 

 

'tx_isolation'

17

 

);

 

18

 

 

 

 

 

 

 

19IF (isolation_level != 'SERIALIZABLE')

20THEN

21SIGNAL SQLSTATE '45001' SET MESSAGE_TEXT = 'Please, switch your

22transaction to SERIALIZABLE isolation level and rerun this

23INSERT again.', MYSQL_ERRNO = 1001;

24END IF;

25

26END;

27$$

28

29 DELIMITER ;

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

MySQL Решение 6.2.3.a (код для проверки работоспособности решения)

1SET SESSION TRANSACTION

2ISOLATION LEVEL READ COMMITTED;

3

 

 

 

4

 

INSERT INTO `books`

5

 

 

(`b_name`,

6

 

 

`b_year`,

7

 

 

`b_quantity`)

8

 

VALUES

('И ещё одна книга',

9

 

 

1985,

10

 

 

2);

11

 

 

 

12SET SESSION TRANSACTION

13ISOLATION LEVEL SERIALIZABLE;

14

 

 

 

15

 

INSERT INTO `books`

16

 

 

(`b_name`,

17

 

 

`b_year`,

18

 

 

`b_quantity`)

19

 

VALUES

('И ещё одна книга',

20

 

 

1985,

21

 

 

2);

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 466/545

Пример 45: управление транзакциями в триггерах, хранимых функциях и процедурах

Решение для MS SQL Server выглядит следующим образом.

MS SQL Решение 6.2.3.a (код триггера)

1CREATE TRIGGER [books_ins_trans]

2ON [books]

3AFTER INSERT

4AS

5DECLARE @isolation_level NVARCHAR(50);

6

7SET @isolation_level =

8(

9SELECT [transaction_isolation_level]

10FROM [sys].[dm_exec_sessions]

11WHERE [session_id] = @@SPID

12);

13

14IF (@isolation_level != 4)

15BEGIN

16RAISERROR ('Please, switch your transaction to SERIALIZABLE isolation

17

 

level and rerun this INSERT again.', 16, 1);

18ROLLBACK TRANSACTION;

19RETURN

20END;

21GO

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

MS SQL Решение 6.2.3.a (код для проверки работоспособности решения)

1SET TRANSACTION ISOLATION

2LEVEL READ COMMITTED;

3

 

 

 

4

 

INSERT INTO [books]

5

 

 

([b_name],

6

 

 

[b_year],

7

 

 

[b_quantity])

8

 

VALUES

('И ещё одна книга',

9

 

 

1985,

10

 

 

2);

11

 

 

 

 

 

 

 

12SET TRANSACTION ISOLATION

13LEVEL SERIALIZABLE;

14

 

 

 

15

 

INSERT INTO [books]

16

 

 

([b_name],

17

 

 

[b_year],

18

 

 

[b_quantity])

19

 

VALUES

('И ещё одна книга',

20

 

 

1985,

21

 

 

2);

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 467/545

Пример 45: управление транзакциями в триггерах, хранимых функциях и процедурах

Решение для Oracle выглядит следующим образом.

Oracle

Решение 6.2.3.a (код триггера)

1CREATE OR REPLACE TRIGGER "books_ins_trans"

2AFTER INSERT

3ON "books"

4FOR EACH ROW

5DECLARE

6isolation_level NVARCHAR2(150);

7trans_id VARCHAR(100);

8BEGIN

9trans_id := DBMS_TRANSACTION.LOCAL_TRANSACTION_ID(FALSE);

10SELECT CASE BITAND("transaction".flag, POWER(2, 28))

11

 

 

WHEN 0 THEN 'READ COMMITTED'

12

 

 

ELSE 'SERIALIZABLE'

13

 

 

END AS "session_isolation_level"

14

 

INTO

isolation_level

15

 

FROM

v$transaction "transaction"

16

 

 

JOIN v$session "session"

17

 

 

ON "transaction".addr = "session".taddr

18

 

 

AND "session".sid = SYS_CONTEXT('USERENV', 'SID');

19

 

 

 

20IF (isolation_level != 'SERIALIZABLE')

21THEN

22RAISE_APPLICATION_ERROR(-20001, 'Please, switch your transaction

23to SERIALIZABLE isolation level and rerun this INSERT again.');

24END IF;

25

26 END;

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

Oracle Решение 6.2.3.a (код для проверки работоспособности решения)

1ALTER SESSION SET

2ISOLATION_LEVEL = READ COMMITTED;

3

 

 

 

4

 

INSERT INTO "books"

5

 

 

("b_name",

6

 

 

"b_year",

7

 

 

"b_quantity")

8

 

VALUES

('И ещё одна книга',

9

 

 

1985,

10

 

 

2);

11

 

 

 

12ALTER SESSION SET

13ISOLATION_LEVEL = SERIALIZABLE;

14

 

 

 

15

 

INSERT INTO "books"

16

 

 

("b_name",

17

 

 

"b_year",

18

 

 

"b_quantity")

19

 

VALUES

('И ещё одна книга',

20

 

 

1985,

21

 

 

2);

На этом решение данной задачи завершено.

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 468/545

Пример 45: управление транзакциями в триггерах, хранимых функциях и процедурах

Решение 6.2.3.b{465}.

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

Решение для MySQL выглядит следующим образом.

MySQL

Решение 6.2.3.b (код функции)

1DELIMITER $$

2CREATE FUNCTION NO_AUTOCOMMIT()

3RETURNS INT DETERMINISTIC

4BEGIN

5IF ((SELECT @@autocommit) = 1)

6THEN

7SIGNAL SQLSTATE '45001'

8SET MESSAGE_TEXT = 'Please, turn the autocommit off.',

9MYSQL_ERRNO = 1001;

10RETURN -1;

11END IF;

12 13 -- Тут может быть какой-то полезный код :).

14

15RETURN 0;

16END$$

17

18 DELIMITER ;

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

MySQL Решение 6.2.3.b (код для проверки работоспособности решения)

1SET autocommit = 1;

2SELECT NO_AUTOCOMMIT();

3

4SET autocommit = 0;

5SELECT NO_AUTOCOMMIT();

Решение для MS SQL Server выглядит следующим образом.

Обратите внимание на следующие важные моменты, характерные для MS SQL Server:

состояние автоподтверждения транзакций можно определить лишь косвенно (строки 8-23 кода);

явно породить исключительную ситуацию в коде хранимой функции невозможно, приходится использовать обходное решение (строки 27-31 кода);

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

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016–2018 Стр: 469/545