Mengelola transaksi - Amazon Redshift

Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.

Mengelola transaksi

Anda dapat membuat prosedur tersimpan dengan perilaku manajemen transaksi default atau perilaku nonatomik.

Mode default tersimpan manajemen transaksi prosedur

Perilaku komit otomatis mode transaksi default menyebabkan setiap SQL perintah yang berjalan secara terpisah untuk melakukan komit secara individual. Panggilan ke prosedur tersimpan diperlakukan sebagai satu SQL perintah. SQLPernyataan di dalam prosedur berperilaku seolah-olah mereka berada dalam blok transaksi yang secara implisit dimulai ketika panggilan dimulai dan berakhir ketika panggilan selesai. Panggilan bersarang ke prosedur lain diperlakukan seperti SQL pernyataan lain dan beroperasi dalam konteks transaksi yang sama dengan penelepon. Untuk informasi selengkapnya tentang perilaku komit otomatis, lihatIsolasi yang dapat diserialisasi.

Namun, misalkan Anda memanggil prosedur tersimpan dari dalam blok transaksi yang ditentukan pengguna (didefinisikan olehBEGIN... COMMIT). Dalam hal ini, semua pernyataan dalam prosedur tersimpan berjalan dalam konteks transaksi yang ditentukan pengguna. Prosedur tidak melakukan secara implisit saat keluar. Penelepon mengontrol prosedur commit atau rollback.

Jika ada kesalahan yang ditemukan saat menjalankan prosedur tersimpan, semua perubahan yang dilakukan dalam transaksi saat ini dibatalkan.

Anda dapat menggunakan pernyataan kontrol transaksi berikut dalam prosedur tersimpan:

  • COMMIT— melakukan semua pekerjaan yang dilakukan dalam transaksi saat ini dan secara implisit memulai transaksi baru. Untuk informasi selengkapnya, lihat COMMIT.

  • ROLLBACK— mengembalikan pekerjaan yang dilakukan dalam transaksi saat ini dan secara implisit memulai transaksi baru. Untuk informasi selengkapnya, lihat ROLLBACK.

TRUNCATEadalah pernyataan lain yang dapat Anda keluarkan dari dalam prosedur yang disimpan dan memengaruhi manajemen transaksi. Di Amazon Redshift, TRUNCATE mengeluarkan komit secara implisit. Perilaku ini tetap sama dalam konteks prosedur tersimpan. Ketika sebuah TRUNCATE pernyataan dikeluarkan dari dalam prosedur yang disimpan, ia melakukan transaksi saat ini dan memulai yang baru. Untuk informasi selengkapnya, lihat TRUNCATE.

Semua pernyataan yang mengikuti COMMITROLLBACK,, atau TRUNCATE pernyataan berjalan dalam konteks transaksi baru. Mereka melakukannya sampaiCOMMIT,ROLLBACK, atau TRUNCATE pernyataan ditemukan atau prosedur yang disimpan keluar.

Bila Anda menggunakanCOMMIT,ROLLBACK, atau TRUNCATE pernyataan dari dalam prosedur tersimpan, kendala berikut berlaku:

  • Jika prosedur tersimpan dipanggil dari dalam blok transaksi, itu tidak dapat mengeluarkanCOMMIT,ROLLBACK, atau TRUNCATE pernyataan. Pembatasan ini berlaku di dalam tubuh prosedur yang disimpan sendiri dan dalam setiap panggilan prosedur bersarang.

  • Jika prosedur tersimpan dibuat dengan SET config opsi, itu tidak dapat mengeluarkanCOMMIT,ROLLBACK, atau TRUNCATE pernyataan. Pembatasan ini berlaku di dalam tubuh prosedur yang disimpan sendiri dan dalam setiap panggilan prosedur bersarang.

  • Setiap kursor yang terbuka (secara eksplisit atau implisit) ditutup secara otomatis ketikaCOMMIT,ROLLBACK, atau pernyataan diproses. TRUNCATE Untuk batasan pada kursor eksplisit dan implisit, lihat. Keterbatasan prosedur yang disimpan

Selain itu, Anda tidak dapat menjalankan COMMIT atau ROLLBACK menggunakan dinamisSQL. Namun, Anda dapat menjalankan TRUNCATE menggunakan dinamisSQL. Untuk informasi selengkapnya, lihat SQL Dinamis.

Saat bekerja dengan prosedur yang disimpan, pertimbangkan bahwa END pernyataan BEGIN dan dalam PL/PG hanya untuk SQL pengelompokan. Mereka tidak memulai atau mengakhiri transaksi. Untuk informasi selengkapnya, lihat Blokir.

Contoh berikut menunjukkan perilaku transaksi saat memanggil prosedur tersimpan dari dalam blok transaksi eksplisit. Dua pernyataan sisipan yang dikeluarkan dari luar prosedur yang disimpan dan yang dari dalamnya semuanya merupakan bagian dari transaksi yang sama (3382). Transaksi dilakukan ketika pengguna mengeluarkan komit eksplisit.

CREATE OR REPLACE PROCEDURE sp_insert_table_a(a int) LANGUAGE plpgsql AS $$ BEGIN INSERT INTO test_table_a values (a); END; $$; Begin; insert into test_table_a values (1); Call sp_insert_table_a(2); insert into test_table_a values (3); Commit; select userid, xid, pid, type, trim(text) as stmt_text from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence; userid | xid | pid | type | stmt_text --------+------+-----+---------+---------------------------------------- 103 | 3382 | 599 | UTILITY | Begin; 103 | 3382 | 599 | QUERY | insert into test_table_a values (1); 103 | 3382 | 599 | UTILITY | Call sp_insert_table_a(2); 103 | 3382 | 599 | QUERY | INSERT INTO test_table_a values ( $1 ) 103 | 3382 | 599 | QUERY | insert into test_table_a values (3); 103 | 3382 | 599 | UTILITY | COMMIT

Sebaliknya, ambil contoh ketika pernyataan yang sama dikeluarkan dari luar blok transaksi eksplisit dan sesi memiliki autocommit disetel ke ON. Dalam hal ini, setiap pernyataan berjalan dalam transaksinya sendiri.

insert into test_table_a values (1); Call sp_insert_table_a(2); insert into test_table_a values (3); select userid, xid, pid, type, trim(text) as stmt_text from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence; userid | xid | pid | type | stmt_text --------+------+-----+---------+------------------------------------------------------------------------------------------------------------------------------------------------- 103 | 3388 | 599 | QUERY | insert into test_table_a values (1); 103 | 3388 | 599 | UTILITY | COMMIT 103 | 3389 | 599 | UTILITY | Call sp_insert_table_a(2); 103 | 3389 | 599 | QUERY | INSERT INTO test_table_a values ( $1 ) 103 | 3389 | 599 | UTILITY | COMMIT 103 | 3390 | 599 | QUERY | insert into test_table_a values (3); 103 | 3390 | 599 | UTILITY | COMMIT

Contoh berikut mengeluarkan TRUNCATE pernyataan setelah memasukkan ke dalamtest_table_a. TRUNCATEPernyataan tersebut mengeluarkan komit implisit yang melakukan transaksi saat ini (3335) dan memulai yang baru (3336). Transaksi baru dilakukan ketika prosedur keluar.

CREATE OR REPLACE PROCEDURE sp_truncate_proc(a int, b int) LANGUAGE plpgsql AS $$ BEGIN INSERT INTO test_table_a values (a); TRUNCATE test_table_b; INSERT INTO test_table_b values (b); END; $$; Call sp_truncate_proc(1,2); select userid, xid, pid, type, trim(text) as stmt_text from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence; userid | xid | pid | type | stmt_text --------+------+-------+---------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 103 | 3335 | 23636 | UTILITY | Call sp_truncate_proc(1,2); 103 | 3335 | 23636 | QUERY | INSERT INTO test_table_a values ( $1 ) 103 | 3335 | 23636 | UTILITY | TRUNCATE test_table_b 103 | 3335 | 23636 | UTILITY | COMMIT 103 | 3336 | 23636 | QUERY | INSERT INTO test_table_b values ( $1 ) 103 | 3336 | 23636 | UTILITY | COMMIT

Contoh berikut mengeluarkan a TRUNCATE dari panggilan bersarang. TRUNCATEKomit semua pekerjaan yang dilakukan sejauh ini dalam prosedur luar dan dalam dalam suatu transaksi (3344). Ini memulai transaksi baru (3345). Transaksi baru dilakukan ketika prosedur luar keluar.

CREATE OR REPLACE PROCEDURE sp_inner(c int, d int) LANGUAGE plpgsql AS $$ BEGIN INSERT INTO inner_table values (c); TRUNCATE outer_table; INSERT INTO inner_table values (d); END; $$; CREATE OR REPLACE PROCEDURE sp_outer(a int, b int, c int, d int) LANGUAGE plpgsql AS $$ BEGIN INSERT INTO outer_table values (a); Call sp_inner(c, d); INSERT INTO outer_table values (b); END; $$; Call sp_outer(1, 2, 3, 4); select userid, xid, pid, type, trim(text) as stmt_text from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence; userid | xid | pid | type | stmt_text --------+------+-------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 103 | 3344 | 23636 | UTILITY | Call sp_outer(1, 2, 3, 4); 103 | 3344 | 23636 | QUERY | INSERT INTO outer_table values ( $1 ) 103 | 3344 | 23636 | UTILITY | CALL sp_inner( $1 , $2 ) 103 | 3344 | 23636 | QUERY | INSERT INTO inner_table values ( $1 ) 103 | 3344 | 23636 | UTILITY | TRUNCATE outer_table 103 | 3344 | 23636 | UTILITY | COMMIT 103 | 3345 | 23636 | QUERY | INSERT INTO inner_table values ( $1 ) 103 | 3345 | 23636 | QUERY | INSERT INTO outer_table values ( $1 ) 103 | 3345 | 23636 | UTILITY | COMMIT

Contoh berikut menunjukkan bahwa kursor cur1 ditutup ketika TRUNCATE pernyataan dilakukan.

CREATE OR REPLACE PROCEDURE sp_open_cursor_truncate() LANGUAGE plpgsql AS $$ DECLARE rec RECORD; cur1 cursor for select * from test_table_a order by 1; BEGIN open cur1; TRUNCATE table test_table_b; Loop fetch cur1 into rec; raise info '%', rec.c1; exit when not found; End Loop; END $$; call sp_open_cursor_truncate(); ERROR: cursor "cur1" does not exist CONTEXT: PL/pgSQL function "sp_open_cursor_truncate" line 8 at fetch

Contoh berikut mengeluarkan TRUNCATE pernyataan dan tidak dapat dipanggil dari dalam blok transaksi eksplisit.

CREATE OR REPLACE PROCEDURE sp_truncate_atomic() LANGUAGE plpgsql AS $$ BEGIN TRUNCATE test_table_b; END; $$; Begin; Call sp_truncate_atomic(); ERROR: TRUNCATE cannot be invoked from a procedure that is executing in an atomic context. HINT: Try calling the procedure as a top-level call i.e. not from within an explicit transaction block. Or, if this procedure (or one of its ancestors in the call chain) was created with SET config options, recreate the procedure without them. CONTEXT: SQL statement "TRUNCATE test_table_b" PL/pgSQL function "sp_truncate_atomic" line 2 at SQL statement

Contoh berikut menunjukkan bahwa pengguna yang bukan superuser atau pemilik tabel dapat mengeluarkan TRUNCATE pernyataan di atas meja. Pengguna melakukan ini menggunakan prosedur Security Definer tersimpan. Contoh menunjukkan tindakan berikut:

  • Pengguna1 membuat tabeltest_tbl.

  • Pengguna1 membuat prosedur sp_truncate_test_tbl tersimpan.

  • User1 memberikan hak EXECUTE istimewa pada prosedur yang disimpan ke user2.

  • User2 menjalankan prosedur tersimpan untuk memotong tabel. test_tbl Contoh menunjukkan jumlah baris sebelum dan sesudah TRUNCATE perintah.

set session_authorization to user1; create table test_tbl(id int, name varchar(20)); insert into test_tbl values (1,'john'), (2, 'mary'); CREATE OR REPLACE PROCEDURE sp_truncate_test_tbl() LANGUAGE plpgsql AS $$ DECLARE tbl_rows int; BEGIN select count(*) into tbl_rows from test_tbl; RAISE INFO 'RowCount before Truncate: %', tbl_rows; TRUNCATE test_tbl; select count(*) into tbl_rows from test_tbl; RAISE INFO 'RowCount after Truncate: %', tbl_rows; END; $$ SECURITY DEFINER; grant execute on procedure sp_truncate_test_tbl() to user2; reset session_authorization; set session_authorization to user2; call sp_truncate_test_tbl(); INFO: RowCount before Truncate: 2 INFO: RowCount after Truncate: 0 CALL reset session_authorization;

Contoh berikut masalah COMMIT dua kali. Yang pertama COMMIT melakukan semua pekerjaan yang dilakukan dalam transaksi 10363 dan secara implisit memulai transaksi 10364. Transaksi 10364 dilakukan oleh pernyataan keduaCOMMIT.

CREATE OR REPLACE PROCEDURE sp_commit(a int, b int) LANGUAGE plpgsql AS $$ BEGIN INSERT INTO test_table values (a); COMMIT; INSERT INTO test_table values (b); COMMIT; END; $$; call sp_commit(1,2); select userid, xid, pid, type, trim(text) as stmt_text from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence; userid | xid | pid | type | stmt_text --------+-------+------+---------+----------------------------------------------------------------------------------------------------------------- 100 | 10363 | 3089 | UTILITY | call sp_commit(1,2); 100 | 10363 | 3089 | QUERY | INSERT INTO test_table values ( $1 ) 100 | 10363 | 3089 | UTILITY | COMMIT 100 | 10364 | 3089 | QUERY | INSERT INTO test_table values ( $1 ) 100 | 10364 | 3089 | UTILITY | COMMIT

Contoh berikut mengeluarkan ROLLBACK pernyataan jika sum_vals lebih besar dari 2. ROLLBACKPernyataan pertama mengembalikan semua pekerjaan yang dilakukan dalam transaksi 10377 dan memulai transaksi baru 10378. Transaksi 10378 dilakukan saat prosedur keluar.

CREATE OR REPLACE PROCEDURE sp_rollback(a int, b int) LANGUAGE plpgsql AS $$ DECLARE sum_vals int; BEGIN INSERT INTO test_table values (a); SELECT sum(c1) into sum_vals from test_table; IF sum_vals > 2 THEN ROLLBACK; END IF; INSERT INTO test_table values (b); END; $$; call sp_rollback(1, 2); select userid, xid, pid, type, trim(text) as stmt_text from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence; userid | xid | pid | type | stmt_text --------+-------+------+---------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 100 | 10377 | 3089 | UTILITY | call sp_rollback(1, 2); 100 | 10377 | 3089 | QUERY | INSERT INTO test_table values ( $1 ) 100 | 10377 | 3089 | QUERY | SELECT sum(c1) from test_table 100 | 10377 | 3089 | QUERY | Undoing 1 transactions on table 133646 with current xid 10377 : 10377 100 | 10378 | 3089 | QUERY | INSERT INTO test_table values ( $1 ) 100 | 10378 | 3089 | UTILITY | COMMIT

Manajemen transaksi prosedur tersimpan mode nonatomik

Prosedur tersimpan yang dibuat dalam NONATOMIC mode memiliki perilaku kontrol transaksi yang berbeda dari prosedur yang dibuat dalam mode default. Mirip dengan perilaku komit otomatis dari SQL perintah di luar prosedur tersimpan, setiap SQL pernyataan di dalam NONATOMIC prosedur berjalan dalam transaksinya sendiri dan melakukan secara otomatis. Jika pengguna memulai blok transaksi eksplisit dalam prosedur NONATOMIC tersimpan, maka SQL pernyataan di dalam blok tidak secara otomatis melakukan komit. Blok transaksi mengontrol komit atau pengembalian pernyataan di dalamnya.

Dalam prosedur NONATOMIC tersimpan, Anda dapat membuka blok transaksi eksplisit di dalam prosedur menggunakan START TRANSACTION pernyataan. Namun, jika sudah ada blok transaksi terbuka, pernyataan ini tidak akan melakukan apa-apa karena Amazon Redshift tidak mendukung sub transaksi. Transaksi sebelumnya terus berlanjut.

Saat Anda bekerja dengan FOR loop kursor di dalam NONATOMIC prosedur, pastikan Anda membuka blok transaksi eksplisit sebelum mengulangi hasil kueri. Jika tidak, kursor ditutup ketika SQL pernyataan di dalam loop secara otomatis berkomitmen.

Beberapa pertimbangan saat menggunakan perilaku NONATOMIC mode adalah sebagai berikut:

  • Setiap SQL pernyataan di dalam prosedur tersimpan secara otomatis dilakukan jika tidak ada blok transaksi terbuka, dan sesi memiliki autocommit disetel ke ON.

  • Anda dapat mengeluarkan TRUNCATE pernyataan COMMITROLLBACK//untuk mengakhiri transaksi jika prosedur yang disimpan dipanggil dari dalam blok transaksi. Ini tidak mungkin dalam mode default.

  • Anda dapat mengeluarkan START TRANSACTION pernyataan untuk memulai blok transaksi di dalam prosedur yang disimpan.

Contoh berikut menunjukkan perilaku transaksi saat bekerja dengan prosedur NONATOMIC tersimpan. Sesi untuk semua contoh berikut memiliki autocommit disetel ke ON.

Dalam contoh berikut, prosedur NONATOMIC tersimpan memiliki dua INSERT pernyataan. Ketika prosedur dipanggil di luar blok transaksi, setiap INSERT pernyataan dalam prosedur secara otomatis melakukan.

CREATE TABLE test_table_a(v int); CREATE TABLE test_table_b(v int); CREATE OR REPLACE PROCEDURE sp_nonatomic_insert_table_a(a int, b int) NONATOMIC AS $$ BEGIN INSERT INTO test_table_a values (a); INSERT INTO test_table_b values (b); END; $$ LANGUAGE plpgsql; Call sp_nonatomic_insert_table_a(1,2); Select userid, xid, pid, type, trim(text) as stmt_text from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence; userid | xid | pid | type | stmt_text --------+------+------------+---------+---------------------------------------- 1 | 1792 | 1073807554 | UTILITY | Call sp_nonatomic_insert_table_a(1,2); 1 | 1792 | 1073807554 | QUERY | INSERT INTO test_table_a values ( $1 ) 1 | 1792 | 1073807554 | UTILITY | COMMIT 1 | 1793 | 1073807554 | QUERY | INSERT INTO test_table_b values ( $1 ) 1 | 1793 | 1073807554 | UTILITY | COMMIT (5 rows)

Namun, ketika prosedur dipanggil dari dalamBEGIN.. COMMITblok, semua pernyataan adalah bagian dari transaksi yang sama (xid = 1799).

Begin; INSERT INTO test_table_a values (10); Call sp_nonatomic_insert_table_a(20,30); INSERT INTO test_table_b values (40); Commit; Select userid, xid, pid, type, trim(text) as stmt_text from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence; userid | xid | pid | type | stmt_text --------+------+------------+---------+------------------------------------------ 1 | 1799 | 1073914035 | UTILITY | Begin; 1 | 1799 | 1073914035 | QUERY | INSERT INTO test_table_a values (10); 1 | 1799 | 1073914035 | UTILITY | Call sp_nonatomic_insert_table_a(20,30); 1 | 1799 | 1073914035 | QUERY | INSERT INTO test_table_a values ( $1 ) 1 | 1799 | 1073914035 | QUERY | INSERT INTO test_table_b values ( $1 ) 1 | 1799 | 1073914035 | QUERY | INSERT INTO test_table_b values (40); 1 | 1799 | 1073914035 | UTILITY | COMMIT (7 rows)

Dalam contoh ini, dua INSERT pernyataan berada di antara STARTTRANSACTION... COMMIT. Ketika prosedur dipanggil di luar blok transaksi, kedua INSERT pernyataan tersebut berada dalam transaksi yang sama (xid = 1866).

CREATE OR REPLACE PROCEDURE sp_nonatomic_txn_block(a int, b int) NONATOMIC AS $$ BEGIN START TRANSACTION; INSERT INTO test_table_a values (a); INSERT INTO test_table_b values (b); COMMIT; END; $$ LANGUAGE plpgsql; Call sp_nonatomic_txn_block(1,2); Select userid, xid, pid, type, trim(text) as stmt_text from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence; userid | xid | pid | type | stmt_text --------+------+------------+---------+---------------------------------------- 1 | 1865 | 1073823998 | UTILITY | Call sp_nonatomic_txn_block(1,2); 1 | 1866 | 1073823998 | QUERY | INSERT INTO test_table_a values ( $1 ) 1 | 1866 | 1073823998 | QUERY | INSERT INTO test_table_b values ( $1 ) 1 | 1866 | 1073823998 | UTILITY | COMMIT (4 rows)

Ketika prosedur dipanggil dari dalamBEGIN... COMMITblok, bagian START TRANSACTION dalam prosedur tidak melakukan apa-apa karena sudah ada transaksi terbuka. Bagian COMMIT dalam prosedur melakukan transaksi saat ini (xid = 1876) dan memulai yang baru.

Begin; INSERT INTO test_table_a values (10); Call sp_nonatomic_txn_block(20,30); INSERT INTO test_table_b values (40); Commit; Select userid, xid, pid, type, trim(text) as stmt_text from svl_statementtext where pid = pg_backend_pid() order by xid , starttime , sequence; userid | xid | pid | type | stmt_text --------+------+------------+---------+---------------------------------------- 1 | 1876 | 1073832133 | UTILITY | Begin; 1 | 1876 | 1073832133 | QUERY | INSERT INTO test_table_a values (10); 1 | 1876 | 1073832133 | UTILITY | Call sp_nonatomic_txn_block(20,30); 1 | 1876 | 1073832133 | QUERY | INSERT INTO test_table_a values ( $1 ) 1 | 1876 | 1073832133 | QUERY | INSERT INTO test_table_b values ( $1 ) 1 | 1876 | 1073832133 | UTILITY | COMMIT 1 | 1878 | 1073832133 | QUERY | INSERT INTO test_table_b values (40); 1 | 1878 | 1073832133 | UTILITY | COMMIT (8 rows)

Contoh ini menunjukkan cara bekerja dengan loop kursor. Tabel test_table_a memiliki tiga nilai. Tujuannya adalah untuk mengulangi melalui tiga nilai dan memasukkannya ke dalam tabel test_table_b. Jika prosedur NONATOMIC tersimpan dibuat dengan cara berikut, itu akan melempar kursor kesalahan “cur1" tidak ada setelah mengeksekusi INSERT pernyataan di loop pertama. Ini karena komit otomatis INSERT menutup kursor terbuka.

insert into test_table_a values (1), (2), (3); CREATE OR REPLACE PROCEDURE sp_nonatomic_cursor() NONATOMIC LANGUAGE plpgsql AS $$ DECLARE rec RECORD; cur1 cursor for select * from test_table_a order by 1; BEGIN open cur1; Loop fetch cur1 into rec; exit when not found; raise info '%', rec.v; insert into test_table_b values (rec.v); End Loop; END $$; CALL sp_nonatomic_cursor(); INFO: 1 ERROR: cursor "cur1" does not exist CONTEXT: PL/pgSQL function "sp_nonatomic_cursor" line 7 at fetch

Untuk membuat loop kursor bekerja, letakkan di antara STARTTRANSACTION... COMMIT.

insert into test_table_a values (1), (2), (3); CREATE OR REPLACE PROCEDURE sp_nonatomic_cursor() NONATOMIC LANGUAGE plpgsql AS $$ DECLARE rec RECORD; cur1 cursor for select * from test_table_a order by 1; BEGIN START TRANSACTION; open cur1; Loop fetch cur1 into rec; exit when not found; raise info '%', rec.v; insert into test_table_b values (rec.v); End Loop; COMMIT; END $$; CALL sp_nonatomic_cursor(); INFO: 1 INFO: 2 INFO: 3 CALL