-- -- E-Maj: migration from 2.2.0 to 2.2.1 -- -- This software is distributed under the GNU General Public License. -- -- This script upgrades an existing installation of E-Maj extension. -- -- complain if this script is executed in psql, rather than via an ALTER EXTENSION statement \echo Use "ALTER EXTENSION emaj UPDATE TO..." to upgrade the E-Maj extension. \quit --SET client_min_messages TO WARNING; SET client_min_messages TO NOTICE; ------------------------------------ -- -- -- checks -- -- -- ------------------------------------ -- Check that the upgrade conditions are met. DO $do$ DECLARE v_emajVersion TEXT; v_groupList TEXT; BEGIN -- check the current role is a superuser PERFORM 0 FROM pg_roles WHERE rolname = current_user AND rolsuper; IF NOT FOUND THEN RAISE EXCEPTION 'E-Maj upgrade: the current user (%) is not a superuser.', current_user; END IF; -- the emaj version registered in emaj_param must be '2.1.0' SELECT param_value_text INTO v_emajVersion FROM emaj.emaj_param WHERE param_key = 'emaj_version'; IF v_emajVersion <> '2.2.0' THEN RAISE EXCEPTION 'E-Maj upgrade: the current E-Maj version (%) is not 2.2.0',v_emajVersion; END IF; -- the installed postgres version must be at least 9.2 IF current_setting('server_version_num')::int < 90200 THEN RAISE EXCEPTION 'E-Maj upgrade: the current PostgreSQL version (%) is not compatible with the new E-Maj version. The PostgreSQL version should be at least 9.2.', current_setting('server_version'); END IF; -- no existing group must have been created with a postgres version prior 8.4 SELECT string_agg(group_name, ', ') INTO v_groupList FROM emaj.emaj_group WHERE cast(to_number(substring(group_pg_version FROM E'^(\\d+)'),'99') * 100 + to_number(substring(group_pg_version FROM E'^\\d+\\.(\\d+)'),'99') AS INTEGER) < 804; IF v_groupList IS NOT NULL THEN RAISE EXCEPTION 'E-Maj upgrade: groups "%" have been created with a too old postgres version (< 8.4). Drop these groups before upgrading. ',v_groupList; END IF; END; $do$; -- OK, the upgrade operation can start... -- lock emaj_group table to avoid any concurrent E-Maj activity LOCK TABLE emaj.emaj_group IN EXCLUSIVE MODE; -- disable the event triggers SELECT emaj._disable_event_triggers(); ---------------------------------------------- -- -- -- emaj enums, tables, views and sequences -- -- -- ---------------------------------------------- -- rename log tables and index for tables that have been removed from a tables group in logging state. Adjust the emaj_relation table accordingly. DO $do$ DECLARE r_tbl RECORD; BEGIN -- loop on all removed tables that are known in the emaj_relation table FOR r_tbl IN SELECT rel_log_schema, rel_log_table, rel_log_index FROM emaj.emaj_relation WHERE NOT upper_inf(rel_time_range) LOOP -- rename the log table, adding a '_1' suffix EXECUTE 'ALTER TABLE ' || quote_ident(r_tbl.rel_log_schema) || '.' || quote_ident(r_tbl.rel_log_table) || ' RENAME TO '|| quote_ident(r_tbl.rel_log_table || '_1'); -- rename the log index, adding a '_1' suffix EXECUTE 'ALTER INDEX ' || quote_ident(r_tbl.rel_log_schema) || '.' || quote_ident(r_tbl.rel_log_index) || ' RENAME TO '|| quote_ident(r_tbl.rel_log_index || '_1'); END LOOP; -- update the emaj_relation table to reflect the changes and reset useless data as the new remove_tbl() does UPDATE emaj.emaj_relation SET rel_log_table = rel_log_table || '_1', rel_log_index = rel_log_index || '_1', rel_log_sequence = NULL, rel_log_function = NULL, rel_sql_columns = NULL, rel_sql_pk_columns = NULL, rel_sql_pk_eq_conditions = NULL WHERE NOT upper_inf(rel_time_range); END; $do$; -- -- add created or recreated tables and sequences to the list of content to save by pg_dump -- ------------------------------------ -- -- -- emaj types -- -- -- ------------------------------------ ------------------------------------ -- -- -- emaj functions -- -- -- ------------------------------------ -- recreate functions that have been previously dropped in the tables structure upgrade step and will not be recreated later in this script -- pattern used by the tool that extracts and insert the functions definition ------------------------------------------------------------------ -- drop obsolete functions or functions with modified interface -- ------------------------------------------------------------------ ------------------------------------------------------------------ -- create new or modified functions -- ------------------------------------------------------------------ CREATE OR REPLACE FUNCTION emaj._remove_tbl(r_plan emaj.emaj_alter_plan, v_timeId BIGINT) RETURNS VOID LANGUAGE plpgsql SECURITY DEFINER AS $_remove_tbl$ -- The function removes a table from a group. It is called during an alter group operation. -- If the group is in idle state, it simply calls the _drop_tbl() function. -- Otherwise, only triggers, log function and log sequence are dropped now. The other components will be dropped later (at reset_group time for instance). -- Required inputs: row from emaj_alter_plan corresponding to the appplication table to proccess, time stamp id of the alter group operation -- The function is defined as SECURITY DEFINER so that emaj_adm role can drop triggers on application tables. DECLARE v_logSchema TEXT; v_currentLogTable TEXT; v_currentLogIndex TEXT; v_logFunction TEXT; v_logSequence TEXT; v_namesSuffix TEXT; v_fullTableName TEXT; BEGIN IF NOT r_plan.altr_group_is_logging THEN -- if the group is in idle state, drop the table immediately PERFORM emaj._drop_tbl(emaj.emaj_relation.*) FROM emaj.emaj_relation WHERE rel_schema = r_plan.altr_schema AND rel_tblseq = r_plan.altr_tblseq AND upper_inf(rel_time_range); ELSE -- if the group is in logging state, ... -- ... get the current relation characteristics SELECT rel_log_schema, rel_log_table, rel_log_index, rel_log_function, rel_log_sequence INTO v_logSchema, v_currentLogTable, v_currentLogIndex, v_logFunction, v_logSequence FROM emaj.emaj_relation WHERE rel_schema = r_plan.altr_schema AND rel_tblseq = r_plan.altr_tblseq AND upper_inf(rel_time_range); -- ... compute the suffix to add to the log table and index names (_1, _2, ...), by looking at the existing names SELECT '_'|| coalesce(max(suffix) + 1, 1)::TEXT INTO v_namesSuffix FROM (SELECT unnest(regexp_matches(rel_log_table,'_(\d+)$'))::INT AS suffix FROM emaj.emaj_relation WHERE rel_schema = r_plan.altr_schema AND rel_tblseq = r_plan.altr_tblseq ) AS t; -- ... rename the log table and its index EXECUTE 'ALTER TABLE ' || quote_ident(v_logSchema) || '.' || quote_ident(v_currentLogTable) || ' RENAME TO '|| quote_ident(v_currentLogTable || v_namesSuffix); EXECUTE 'ALTER INDEX ' || quote_ident(v_logSchema) || '.' || quote_ident(v_currentLogIndex) || ' RENAME TO '|| quote_ident(v_currentLogIndex || v_namesSuffix); --TODO: share some code with _drop_tbl() ? -- ... drop the log and truncate triggers (the application table is expected to exist) v_fullTableName = quote_ident(r_plan.altr_schema) || '.' || quote_ident(r_plan.altr_tblseq); EXECUTE 'DROP TRIGGER IF EXISTS emaj_log_trg ON ' || v_fullTableName; EXECUTE 'DROP TRIGGER IF EXISTS emaj_trunc_trg ON ' || v_fullTableName; -- ... drop the log function and the log sequence EXECUTE 'DROP FUNCTION IF EXISTS ' || quote_ident(v_logSchema) || '.' || quote_ident(v_logFunction) || '() CASCADE'; EXECUTE 'DROP SEQUENCE IF EXISTS ' || quote_ident(v_logSchema) || '.' || quote_ident(v_logSequence); -- ... register the end of the relation time frame, the log table and index names change, and reset the content of now useless columns UPDATE emaj.emaj_relation SET rel_time_range = int8range(lower(rel_time_range),v_timeId,'[)'), rel_log_table = v_currentLogTable || v_namesSuffix , rel_log_index = v_currentLogIndex || v_namesSuffix, rel_log_sequence = NULL, rel_log_function = NULL, rel_sql_columns = NULL, rel_sql_pk_columns = NULL, rel_sql_pk_eq_conditions = NULL WHERE rel_schema = r_plan.altr_schema AND rel_tblseq = r_plan.altr_tblseq AND upper_inf(rel_time_range); -- ... and insert an entry into the emaj_hist table INSERT INTO emaj.emaj_hist (hist_function, hist_event, hist_object, hist_wording) VALUES ('ALTER_GROUP', 'TABLE REMOVED', quote_ident(r_plan.altr_schema) || '.' || quote_ident(r_plan.altr_tblseq), 'From logging group ' || r_plan.altr_group); END IF; RETURN; END; $_remove_tbl$; -- pattern used by the tool that extracts and insert the functions definition ------------------------------------------ -- -- -- event triggers and related functions -- -- -- ------------------------------------------ ------------------------------------ -- -- -- emaj roles and rights -- -- -- ------------------------------------ REVOKE ALL ON ALL FUNCTIONS IN SCHEMA emaj FROM PUBLIC; GRANT ALL ON ALL TABLES IN SCHEMA emaj TO emaj_adm; GRANT ALL ON ALL SEQUENCES IN SCHEMA emaj TO emaj_adm; GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA emaj TO emaj_adm; GRANT SELECT ON ALL TABLES IN SCHEMA emaj TO emaj_viewer; GRANT SELECT ON ALL SEQUENCES IN SCHEMA emaj TO emaj_viewer; REVOKE SELECT ON TABLE emaj.emaj_param FROM emaj_viewer; ------------------------------------ -- -- -- complete the upgrade -- -- -- ------------------------------------ -- enable the event triggers DO $tmp$ DECLARE v_event_trigger_array TEXT[]; BEGIN IF emaj._pg_version_num() >= 90300 THEN -- build the event trigger names array from the pg_event_trigger table SELECT coalesce(array_agg(evtname),ARRAY[]::TEXT[]) INTO v_event_trigger_array FROM pg_catalog.pg_event_trigger WHERE evtname LIKE 'emaj%' AND evtenabled = 'D'; -- call the _enable_event_triggers() function PERFORM emaj._enable_event_triggers(v_event_trigger_array); END IF; END; $tmp$; -- Set comments for all internal functions, -- by directly inserting a row in the pg_description table for all emaj functions that do not have yet a recorded comment INSERT INTO pg_catalog.pg_description (objoid, classoid, objsubid, description) SELECT pg_proc.oid, pg_class.oid, 0 , 'E-Maj internal function' FROM pg_catalog.pg_proc, pg_catalog.pg_class WHERE pg_class.relname = 'pg_proc' AND pg_proc.oid IN -- list all emaj functions that do not have yet a comment in pg_description (SELECT pg_proc.oid FROM pg_catalog.pg_proc JOIN pg_catalog.pg_namespace ON (pronamespace=pg_namespace.oid) LEFT OUTER JOIN pg_catalog.pg_description ON (pg_description.objoid = pg_proc.oid AND classoid = (SELECT oid FROM pg_catalog.pg_class WHERE relname = 'pg_proc') AND objsubid = 0) WHERE nspname = 'emaj' AND (proname LIKE E'emaj\\_%' OR proname LIKE E'\\_%') AND pg_description.description IS NULL ); -- update the version id in the emaj_param table UPDATE emaj.emaj_param SET param_value_text = '2.2.1' WHERE param_key = 'emaj_version'; -- insert the upgrade record in the operation history INSERT INTO emaj.emaj_hist (hist_function, hist_object, hist_wording) VALUES ('EMAJ_INSTALL','E-Maj 2.2.1', 'Upgrade from 2.2.0 completed'); -- post installation checks DO $tmp$ DECLARE BEGIN -- check the max_prepared_transactions GUC value IF current_setting('max_prepared_transactions')::int <= 1 THEN RAISE WARNING 'E-Maj upgrade: as the max_prepared_transactions parameter value (%) on this cluster is too low, no parallel rollback is possible.', current_setting('max_prepared_transactions'); END IF; END; $tmp$; RESET default_tablespace; SET client_min_messages TO default;