1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-27 12:41:57 +03:00

Add decoding of sequences to test_decoding

Commit 0da92dc530 improved the logical decoding infrastructure to handle
sequences, and did various changes to related parts (WAL logging etc.).
But it did not include any implementation of the new callbacks added to
OutputPluginCallbacks.

This extends test_decoding with two callbacks to decode sequences. The
decoding of sequences may be disabled using 'include-sequences', a new
option of the output plugin.

Author: Tomas Vondra, Cary Huang
Reviewed-by: Peter Eisentraut, Hannu Krosing, Andres Freund
Discussion: https://postgr.es/m/d045f3c2-6cfb-06d3-5540-e63c320df8bc@enterprisedb.com
Discussion: https://postgr.es/m/1710ed7e13b.cd7177461430746.3372264562543607781@highgo.ca
This commit is contained in:
Tomas Vondra
2022-02-12 00:46:07 +01:00
parent 44fa84881f
commit 80901b3291
24 changed files with 567 additions and 55 deletions

File diff suppressed because one or more lines are too long

View File

@ -58,7 +58,7 @@ SELECT pg_current_xact_id() = '0';
-- don't show yet, haven't committed
INSERT INTO nobarf(data) VALUES('2');
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
data
-----------------------------------------------------------
BEGIN

View File

@ -19,7 +19,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'inc
CREATE TABLE somechange(id serial primary key);
INSERT INTO somechange DEFAULT VALUES;
CREATE TABLE changeresult AS
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
SELECT * FROM changeresult;
data
------------------------------------------------
@ -29,9 +29,9 @@ SELECT * FROM changeresult;
(3 rows)
INSERT INTO changeresult
SELECT data FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT data FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
INSERT INTO changeresult
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
SELECT * FROM changeresult;
data
--------------------------------------------------------------------------------------------------------------------------------------------------
@ -63,7 +63,7 @@ DROP TABLE somechange;
CREATE FUNCTION slot_changes_wrapper(slot_name name) RETURNS SETOF TEXT AS $$
BEGIN
RETURN QUERY
SELECT data FROM pg_logical_slot_peek_changes(slot_name, NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT data FROM pg_logical_slot_peek_changes(slot_name, NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
END$$ LANGUAGE plpgsql;
SELECT * FROM slot_changes_wrapper('regression_slot');
slot_changes_wrapper
@ -84,7 +84,7 @@ SELECT * FROM slot_changes_wrapper('regression_slot');
COMMIT
(14 rows)
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
data
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
BEGIN

View File

@ -7,7 +7,7 @@ step s0init: SELECT 'init' FROM pg_create_logical_replication_slot('isolation_sl
init
(1 row)
step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');
step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false');
data
----
(0 rows)
@ -27,7 +27,7 @@ t
(1 row)
step s0w: INSERT INTO do_write DEFAULT VALUES;
step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');
step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false');
data
--------------------------------------------
BEGIN
@ -50,7 +50,7 @@ step s0init: SELECT 'init' FROM pg_create_logical_replication_slot('isolation_sl
init
(1 row)
step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');
step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false');
data
----
(0 rows)
@ -71,7 +71,7 @@ t
step s0alter: ALTER TABLE do_write ADD column ts timestamptz;
step s0w: INSERT INTO do_write DEFAULT VALUES;
step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');
step s0start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false');
data
------------------------------------------------------------------------------
BEGIN

View File

@ -35,7 +35,7 @@ init
step s2c: COMMIT;
step s1insert: INSERT INTO do_write DEFAULT VALUES;
step s1checkpoint: CHECKPOINT;
step s1start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');
step s1start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false');
data
--------------------------------------------------------------------
BEGIN
@ -46,7 +46,7 @@ COMMIT
step s1insert: INSERT INTO do_write DEFAULT VALUES;
step s1alter: ALTER TABLE do_write ADD COLUMN addedbys1 int;
step s1insert: INSERT INTO do_write DEFAULT VALUES;
step s1start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false');
step s1start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false', 'include-sequences', 'false');
data
--------------------------------------------------------------------------------------------
BEGIN

View File

@ -72,9 +72,9 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d
-- origin tx
INSERT INTO origin_tbl(data) VALUES ('will be replicated and decoded and decoded again');
INSERT INTO target_tbl(data)
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
-- as is normal, the insert into target_tbl shows up
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
data
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
BEGIN

View File

@ -64,7 +64,7 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d
CREATE TABLE replication_example(id SERIAL PRIMARY KEY, somedata int, text varchar(120));
INSERT INTO replication_example(somedata) VALUES (1);
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
data
----------------------------------------------------------------------------------------------------------
BEGIN
@ -141,7 +141,7 @@ VACUUM FULL pg_proc; VACUUM FULL pg_description; VACUUM FULL pg_shdescription; V
INSERT INTO replication_example(somedata, testcolumn1, testcolumn3) VALUES (8, 6, 1);
VACUUM FULL pg_proc; VACUUM FULL pg_description; VACUUM FULL pg_shdescription; VACUUM FULL iamalargetable;
INSERT INTO replication_example(somedata, testcolumn1, testcolumn3) VALUES (9, 7, 1);
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
data
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
BEGIN

View File

@ -0,0 +1,327 @@
-- predictability
SET synchronous_commit = on;
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
?column?
----------
init
(1 row)
CREATE SEQUENCE test_sequence;
-- test the sequence changes by several nextval() calls
SELECT nextval('test_sequence');
nextval
---------
1
(1 row)
SELECT nextval('test_sequence');
nextval
---------
2
(1 row)
SELECT nextval('test_sequence');
nextval
---------
3
(1 row)
SELECT nextval('test_sequence');
nextval
---------
4
(1 row)
-- test the sequence changes by several ALTER commands
ALTER SEQUENCE test_sequence INCREMENT BY 10;
SELECT nextval('test_sequence');
nextval
---------
14
(1 row)
ALTER SEQUENCE test_sequence START WITH 3000;
ALTER SEQUENCE test_sequence MAXVALUE 10000;
ALTER SEQUENCE test_sequence RESTART WITH 4000;
SELECT nextval('test_sequence');
nextval
---------
4000
(1 row)
-- test the sequence changes by several setval() calls
SELECT setval('test_sequence', 3500);
setval
--------
3500
(1 row)
SELECT nextval('test_sequence');
nextval
---------
3510
(1 row)
SELECT setval('test_sequence', 3500, true);
setval
--------
3500
(1 row)
SELECT nextval('test_sequence');
nextval
---------
3510
(1 row)
SELECT setval('test_sequence', 3500, false);
setval
--------
3500
(1 row)
SELECT nextval('test_sequence');
nextval
---------
3500
(1 row)
-- show results and drop sequence
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '0');
data
----------------------------------------------------------------------------------------
BEGIN
sequence public.test_sequence: transactional:1 last_value: 1 log_cnt: 0 is_called:0
COMMIT
sequence public.test_sequence: transactional:0 last_value: 33 log_cnt: 0 is_called:1
BEGIN
sequence public.test_sequence: transactional:1 last_value: 4 log_cnt: 0 is_called:1
COMMIT
sequence public.test_sequence: transactional:0 last_value: 334 log_cnt: 0 is_called:1
BEGIN
sequence public.test_sequence: transactional:1 last_value: 14 log_cnt: 32 is_called:1
COMMIT
BEGIN
sequence public.test_sequence: transactional:1 last_value: 14 log_cnt: 0 is_called:1
COMMIT
BEGIN
sequence public.test_sequence: transactional:1 last_value: 4000 log_cnt: 0 is_called:0
COMMIT
sequence public.test_sequence: transactional:0 last_value: 4320 log_cnt: 0 is_called:1
sequence public.test_sequence: transactional:0 last_value: 3500 log_cnt: 0 is_called:1
sequence public.test_sequence: transactional:0 last_value: 3830 log_cnt: 0 is_called:1
sequence public.test_sequence: transactional:0 last_value: 3500 log_cnt: 0 is_called:1
sequence public.test_sequence: transactional:0 last_value: 3830 log_cnt: 0 is_called:1
sequence public.test_sequence: transactional:0 last_value: 3500 log_cnt: 0 is_called:0
sequence public.test_sequence: transactional:0 last_value: 3820 log_cnt: 0 is_called:1
(24 rows)
DROP SEQUENCE test_sequence;
-- rollback on sequence creation and update
BEGIN;
CREATE SEQUENCE test_sequence;
CREATE TABLE test_table (a INT);
SELECT nextval('test_sequence');
nextval
---------
1
(1 row)
SELECT nextval('test_sequence');
nextval
---------
2
(1 row)
SELECT nextval('test_sequence');
nextval
---------
3
(1 row)
SELECT setval('test_sequence', 3000);
setval
--------
3000
(1 row)
SELECT nextval('test_sequence');
nextval
---------
3001
(1 row)
SELECT nextval('test_sequence');
nextval
---------
3002
(1 row)
SELECT nextval('test_sequence');
nextval
---------
3003
(1 row)
ALTER SEQUENCE test_sequence RESTART WITH 6000;
INSERT INTO test_table VALUES( (SELECT nextval('test_sequence')) );
SELECT nextval('test_sequence');
nextval
---------
6001
(1 row)
ROLLBACK;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
data
------
(0 rows)
-- rollback on table creation with serial column
BEGIN;
CREATE TABLE test_table (a SERIAL, b INT);
INSERT INTO test_table (b) VALUES (100);
INSERT INTO test_table (b) VALUES (200);
INSERT INTO test_table (b) VALUES (300);
SELECT setval('test_table_a_seq', 3000);
setval
--------
3000
(1 row)
INSERT INTO test_table (b) VALUES (400);
INSERT INTO test_table (b) VALUES (500);
INSERT INTO test_table (b) VALUES (600);
ALTER SEQUENCE test_table_a_seq RESTART WITH 6000;
INSERT INTO test_table (b) VALUES (700);
INSERT INTO test_table (b) VALUES (800);
INSERT INTO test_table (b) VALUES (900);
ROLLBACK;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
data
------
(0 rows)
-- rollback on table with serial column
CREATE TABLE test_table (a SERIAL, b INT);
BEGIN;
INSERT INTO test_table (b) VALUES (100);
INSERT INTO test_table (b) VALUES (200);
INSERT INTO test_table (b) VALUES (300);
SELECT setval('test_table_a_seq', 3000);
setval
--------
3000
(1 row)
INSERT INTO test_table (b) VALUES (400);
INSERT INTO test_table (b) VALUES (500);
INSERT INTO test_table (b) VALUES (600);
ALTER SEQUENCE test_table_a_seq RESTART WITH 6000;
INSERT INTO test_table (b) VALUES (700);
INSERT INTO test_table (b) VALUES (800);
INSERT INTO test_table (b) VALUES (900);
ROLLBACK;
-- check table and sequence values after rollback
SELECT * from test_table_a_seq;
last_value | log_cnt | is_called
------------+---------+-----------
3003 | 30 | t
(1 row)
SELECT nextval('test_table_a_seq');
nextval
---------
3004
(1 row)
DROP TABLE test_table;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '0');
data
-------------------------------------------------------------------------------------------
BEGIN
sequence public.test_table_a_seq: transactional:1 last_value: 1 log_cnt: 0 is_called:0
COMMIT
sequence public.test_table_a_seq: transactional:0 last_value: 33 log_cnt: 0 is_called:1
sequence public.test_table_a_seq: transactional:0 last_value: 3000 log_cnt: 0 is_called:1
sequence public.test_table_a_seq: transactional:0 last_value: 3033 log_cnt: 0 is_called:1
BEGIN
COMMIT
(8 rows)
-- savepoint test on table with serial column
BEGIN;
CREATE TABLE test_table (a SERIAL, b INT);
INSERT INTO test_table (b) VALUES (100);
INSERT INTO test_table (b) VALUES (200);
SAVEPOINT a;
INSERT INTO test_table (b) VALUES (300);
ROLLBACK TO SAVEPOINT a;
DROP TABLE test_table;
COMMIT;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '0');
data
-----------------------------------------------------------------------------------------
BEGIN
sequence public.test_table_a_seq: transactional:1 last_value: 1 log_cnt: 0 is_called:0
sequence public.test_table_a_seq: transactional:1 last_value: 33 log_cnt: 0 is_called:1
table public.test_table: INSERT: a[integer]:1 b[integer]:100
table public.test_table: INSERT: a[integer]:2 b[integer]:200
COMMIT
(6 rows)
-- savepoint test on table with serial column
BEGIN;
CREATE SEQUENCE test_sequence;
SELECT nextval('test_sequence');
nextval
---------
1
(1 row)
SELECT setval('test_sequence', 3000);
setval
--------
3000
(1 row)
SELECT nextval('test_sequence');
nextval
---------
3001
(1 row)
SAVEPOINT a;
ALTER SEQUENCE test_sequence START WITH 7000;
SELECT setval('test_sequence', 5000);
setval
--------
5000
(1 row)
ROLLBACK TO SAVEPOINT a;
SELECT * FROM test_sequence;
last_value | log_cnt | is_called
------------+---------+-----------
3001 | 32 | t
(1 row)
DROP SEQUENCE test_sequence;
COMMIT;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '0');
data
----------------------------------------------------------------------------------------
BEGIN
sequence public.test_sequence: transactional:1 last_value: 1 log_cnt: 0 is_called:0
sequence public.test_sequence: transactional:1 last_value: 33 log_cnt: 0 is_called:1
sequence public.test_sequence: transactional:1 last_value: 3000 log_cnt: 0 is_called:1
sequence public.test_sequence: transactional:1 last_value: 3033 log_cnt: 0 is_called:1
COMMIT
(6 rows)
SELECT pg_drop_replication_slot('regression_slot');
pg_drop_replication_slot
--------------------------
(1 row)

View File

@ -95,7 +95,7 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot2', 'test_
(1 row)
INSERT INTO replication_example(somedata, text) VALUES (1, 3);
SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
data
---------------------------------------------------------------------------------------------------------
BEGIN

View File

@ -52,7 +52,7 @@ CREATE TABLE toasted_copy (
);
ALTER TABLE toasted_copy ALTER COLUMN data SET STORAGE EXTERNAL;
\copy toasted_copy FROM STDIN
SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
substr
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
BEGIN
@ -316,7 +316,7 @@ SELECT pg_column_size(toasted_key) > 2^16 FROM toasted_several;
t
(1 row)
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
regexp_replace
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
BEGIN
@ -327,7 +327,7 @@ SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_sl
-- test update of a toasted key without changing it
UPDATE toasted_several SET toasted_col1 = toasted_key;
UPDATE toasted_several SET toasted_col2 = toasted_col1;
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
regexp_replace
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
BEGIN
@ -350,7 +350,7 @@ UPDATE toasted_several SET toasted_col1 = toasted_col2 WHERE id = 1;
DELETE FROM toasted_several WHERE id = 1;
COMMIT;
DROP TABLE toasted_several;
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1')
SELECT regexp_replace(data, '^(.{100}).*(.{100})$', '\1..\2') FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0')
WHERE data NOT LIKE '%INSERT: %';
regexp_replace
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -373,7 +373,7 @@ INSERT INTO tbl1 VALUES(1, repeat('a', 4000)) ;
ALTER TABLE tbl1 ADD COLUMN id serial primary key;
INSERT INTO tbl2 VALUES(1);
commit;
SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT substr(data, 1, 200) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
substr
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
BEGIN

View File

@ -11,7 +11,7 @@ CREATE TABLE tab2 (a int primary key, b int);
TRUNCATE tab1;
TRUNCATE tab1, tab1 RESTART IDENTITY CASCADE;
TRUNCATE tab1, tab2;
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'include-sequences', '0');
data
------------------------------------------------------
BEGIN