Skip to content

Commit

Permalink
When creating a tables group with the flag is_empty and the group is …
Browse files Browse the repository at this point in the history
…described in the emaj_group_def table, only issue a warning instead of an exception and create the group empty. This allows then to use the new emaj_assign_tables() and emaj_assign_sequences() on this empty group regardless of the emaj_group_def content.
  • Loading branch information
beaud76 committed Jul 23, 2019
1 parent 4b400f3 commit 220ee9f
Show file tree
Hide file tree
Showing 26 changed files with 3,160 additions and 3,002 deletions.
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ E-Maj - Change log
* The part of mark names generated from the timestamp, now use the
clock_timestamp (instead of the transaction timestamp) and has one
additional digit.
* Only issue a warning when creating an empty group that is configured in
the emaj_group_def table.
* Minor coding improvements.

###Bug fixes:###
Expand Down
2 changes: 1 addition & 1 deletion docs/en/mainFunctions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ A specific version of the function allows to create an empty tables group, i.e.

SELECT emaj.emaj_create_group('<group.name>', <is_rollbackable>, <is_empty>);

The third parameter is *false* by default. If it is set to *true*, the group must not be referenced in the *emaj_group_def* table. Once created, an empty group can be then populated using the :doc:`emaj_alter_group() <alterGroups>` function or :ref:`dynamic tables groups adjustment function<dynamic_ajustment>`.
The third parameter is *false* by default. If it is set to *true*, the group can be referenced in the *emaj_group_def* table. But in such a case, the *emaj_group_def* table’s content is ignored. Once created, an empty group can be then populated using the :doc:`emaj_alter_group() <alterGroups>` function or :ref:`dynamic tables groups adjustment function<dynamic_ajustment>`.

All actions that are chained by the *emaj_create_group()* function are executed on behalf of a unique transaction. As a consequence, if an error occurs during the operation, all tables, functions and triggers already created by the function are cancelled.

Expand Down
4 changes: 2 additions & 2 deletions docs/fr/mainFunctions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,11 @@ La fonction *emaj_create_group()* contrôle également l'existence de « trigge

Si une séquence du groupe est associée à une colonne soit de type *SERIAL* ou *BIGSERIAL* soit définie avec une clause *GENERATED AS IDENTITY*, et que sa table d'appartenance ne fait pas partie du groupe, la fonction génère également un message de type *WARNING*.

Une forme particulière de la fonction permet de créer un groupe de table vide, c’est à dire ne contenant à sa création aucune table ni séquence ::
Une forme particulière de la fonction permet de créer un groupe de tables vide, c’est à dire ne contenant à sa création aucune table ni séquence ::

SELECT emaj.emaj_create_group('<nom.du.groupe>', <est.rollbackable>, <est.vide>);

Le troisième paramètre prend la valeur *faux* par défaut. Si le paramètre est valorisé à *vrai*, le groupe ne doit pas être référencé dans la table *emaj_group_def*. Une fois créé, un groupe vide peut ensuite être peuplé, à l’aide de la fonction :doc:`emaj_alter_group() <alterGroups>` ou des fonctions d’:ref:`ajustement dynamique des groupes de tables <dynamic_ajustment>`.
Le troisième paramètre prend la valeur *faux* par défaut. Si le paramètre est valorisé à *vrai*, le groupe peut être référencé dans la table *emaj_group_def*. Mais dans ce cas, le contenu de la table *emaj_group_def* est ignoré. Une fois créé, un groupe vide peut ensuite être peuplé, à l’aide de la fonction :doc:`emaj_alter_group() <alterGroups>` ou des fonctions d’:ref:`ajustement dynamique des groupes de tables <dynamic_ajustment>`.

Toutes les actions enchaînées par la fonction *emaj_create_group()* sont exécutées au sein d'une unique transaction. En conséquence, si une erreur survient durant l'opération, toutes les tables, fonctions et triggers déjà créés par la fonction sont annulées.

Expand Down
108 changes: 108 additions & 0 deletions sql/emaj--3.1.0--devel.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2097,6 +2097,114 @@ $_lock_groups$
END;
$_lock_groups$;

CREATE OR REPLACE FUNCTION emaj.emaj_create_group(v_groupName TEXT, v_isRollbackable BOOLEAN DEFAULT TRUE,
v_is_empty BOOLEAN DEFAULT FALSE)
RETURNS INT LANGUAGE plpgsql AS
$emaj_create_group$
-- This function creates emaj objects for all tables of a group.
-- It also creates the log E-Maj schemas when needed.
-- Input: group name,
-- boolean indicating whether the group is rollbackable or not (true by default),
-- boolean explicitely indicating whether the group is empty or not
-- Output: number of processed tables and sequences
DECLARE
v_timeId BIGINT;
v_nbTbl INT = 0;
v_nbSeq INT = 0;
r RECORD;
BEGIN
-- insert begin in the history
INSERT INTO emaj.emaj_hist (hist_function, hist_event, hist_object, hist_wording)
VALUES ('CREATE_GROUP', 'BEGIN', v_groupName, CASE WHEN v_isRollbackable THEN 'rollbackable' ELSE 'audit_only' END);
-- check that the group name is valid
IF v_groupName IS NULL OR v_groupName = '' THEN
RAISE EXCEPTION 'emaj_create_group: The group name can''t be NULL or empty.';
END IF;
-- check that the group is not yet recorded in emaj_group table
PERFORM 0 FROM emaj.emaj_group WHERE group_name = v_groupName;
IF FOUND THEN
RAISE EXCEPTION 'emaj_create_group: The group "%" already exists.', v_groupName;
END IF;
-- check the consistency between the emaj_group_def table content and the v_is_empty input parameter
PERFORM 0 FROM emaj.emaj_group_def WHERE grpdef_group = v_groupName LIMIT 1;
IF NOT v_is_empty AND NOT FOUND THEN
RAISE EXCEPTION 'emaj_create_group: The group "%" is unknown in the emaj_group_def table. To create an empty group,'
' explicitely set the third parameter to true.', v_groupName;
END IF;
IF v_is_empty AND FOUND THEN
RAISE WARNING 'emaj_create_group: Although the group "%" is referenced into the emaj_group_def table, it is left empty.',
v_groupName;
END IF;
-- performs various checks on the group's content described in the emaj_group_def table
IF NOT v_is_empty THEN
FOR r IN
SELECT chk_message FROM emaj._check_conf_groups(ARRAY[v_groupName])
WHERE (v_isRollbackable AND chk_severity <= 2)
OR (NOT v_isRollbackable AND chk_severity <= 1)
ORDER BY chk_msg_type, chk_group, chk_schema, chk_tblseq
LOOP
RAISE WARNING 'emaj_create_group: error, %', r.chk_message;
END LOOP;
IF FOUND THEN
RAISE EXCEPTION 'emaj_create_group: One or several errors have been detected in the emaj_group_def table content.';
END IF;
END IF;
-- OK
-- get the time stamp of the operation
SELECT emaj._set_time_stamp('C') INTO v_timeId;
-- insert the row describing the group into the emaj_group table
-- (The group_is_rlbk_protected boolean column is always initialized as not group_is_rollbackable)
INSERT INTO emaj.emaj_group (group_name, group_is_rollbackable, group_creation_time_id, group_has_waiting_changes,
group_is_logging, group_is_rlbk_protected, group_nb_table, group_nb_sequence)
VALUES (v_groupName, v_isRollbackable, v_timeId, FALSE, FALSE, NOT v_isRollbackable, 0, 0);
-- populate the group
IF NOT v_is_empty THEN
-- create new E-Maj log schemas, if needed
PERFORM emaj._create_log_schemas('CREATE_GROUP', ARRAY[v_groupName]);
-- get and process all tables of the group (in priority order, NULLS being processed last)
PERFORM emaj._create_tbl(grpdef_schema, grpdef_tblseq, grpdef_group, grpdef_priority, grpdef_log_dat_tsp, grpdef_log_idx_tsp,
v_timeId, v_isRollbackable, FALSE)
FROM (
SELECT grpdef_schema, grpdef_tblseq, grpdef_group, grpdef_priority, grpdef_log_dat_tsp, grpdef_log_idx_tsp
FROM emaj.emaj_group_def, pg_catalog.pg_class, pg_catalog.pg_namespace
WHERE grpdef_group = v_groupName
AND relnamespace = pg_namespace.oid
AND nspname = grpdef_schema AND relname = grpdef_tblseq
AND relkind = 'r'
ORDER BY grpdef_priority, grpdef_schema, grpdef_tblseq
) AS t;
SELECT count(*) INTO v_nbTbl
FROM emaj.emaj_relation
WHERE rel_group = v_groupName AND rel_kind = 'r' AND upper_inf(rel_time_range);
-- get and process all sequences of the group (in priority order, NULLS being processed last)
PERFORM emaj._create_seq(grpdef_schema, grpdef_tblseq, grpdef_group, grpdef_priority, v_timeId)
FROM (
SELECT grpdef_schema, grpdef_tblseq, grpdef_group, grpdef_priority
FROM emaj.emaj_group_def, pg_catalog.pg_class, pg_catalog.pg_namespace
WHERE grpdef_group = v_groupName
AND relnamespace = pg_namespace.oid
AND nspname = grpdef_schema AND relname = grpdef_tblseq
AND relkind = 'S'
ORDER BY grpdef_priority, grpdef_schema, grpdef_tblseq
) AS t;
SELECT count(*) INTO v_nbSeq
FROM emaj.emaj_relation
WHERE rel_group = v_groupName AND rel_kind = 'S' AND upper_inf(rel_time_range);
-- update tables and sequences counters in the emaj_group table
UPDATE emaj.emaj_group SET group_nb_table = v_nbTbl, group_nb_sequence = v_nbSeq
WHERE group_name = v_groupName;
-- check foreign keys with tables outside the group
PERFORM emaj._check_fk_groups(array[v_groupName]);
END IF;
-- insert end in the history
INSERT INTO emaj.emaj_hist (hist_function, hist_event, hist_object, hist_wording)
VALUES ('CREATE_GROUP', 'END', v_groupName, v_nbTbl + v_nbSeq || ' tables/sequences processed');
RETURN v_nbTbl + v_nbSeq;
END;
$emaj_create_group$;
COMMENT ON FUNCTION emaj.emaj_create_group(TEXT,BOOLEAN,BOOLEAN) IS
$$Creates an E-Maj group.$$;

CREATE OR REPLACE FUNCTION emaj._start_groups(v_groupNames TEXT[], v_mark TEXT, v_multiGroup BOOLEAN, v_resetLog BOOLEAN)
RETURNS INT LANGUAGE plpgsql
SECURITY DEFINER SET search_path = pg_catalog, pg_temp AS
Expand Down
101 changes: 53 additions & 48 deletions sql/emaj--devel.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3807,15 +3807,15 @@ $emaj_create_group$
-- Output: number of processed tables and sequences
DECLARE
v_timeId BIGINT;
v_nbTbl INT;
v_nbSeq INT;
v_nbTbl INT = 0;
v_nbSeq INT = 0;
r RECORD;
BEGIN
-- insert begin in the history
INSERT INTO emaj.emaj_hist (hist_function, hist_event, hist_object, hist_wording)
VALUES ('CREATE_GROUP', 'BEGIN', v_groupName, CASE WHEN v_isRollbackable THEN 'rollbackable' ELSE 'audit_only' END);
-- check that the group name is valid
IF v_groupName IS NULL OR v_groupName = ''THEN
IF v_groupName IS NULL OR v_groupName = '' THEN
RAISE EXCEPTION 'emaj_create_group: The group name can''t be NULL or empty.';
END IF;
-- check that the group is not yet recorded in emaj_group table
Expand All @@ -3830,65 +3830,70 @@ $emaj_create_group$
' explicitely set the third parameter to true.', v_groupName;
END IF;
IF v_is_empty AND FOUND THEN
RAISE EXCEPTION 'emaj_create_group: The group "%" is referenced into the emaj_group_def table. This is not consistent'
' with the <is_empty> parameter set to true.', v_groupName;
RAISE WARNING 'emaj_create_group: Although the group "%" is referenced into the emaj_group_def table, it is left empty.',
v_groupName;
END IF;
-- performs various checks on the group's content described in the emaj_group_def table
FOR r IN
SELECT chk_message FROM emaj._check_conf_groups(ARRAY[v_groupName])
WHERE (v_isRollbackable AND chk_severity <= 2)
OR (NOT v_isRollbackable AND chk_severity <= 1)
ORDER BY chk_msg_type, chk_group, chk_schema, chk_tblseq
LOOP
RAISE WARNING 'emaj_create_group: %', r.chk_message;
END LOOP;
IF FOUND THEN
RAISE EXCEPTION 'emaj_create_group: One or several errors have been detected in the emaj_group_def table content.';
IF NOT v_is_empty THEN
FOR r IN
SELECT chk_message FROM emaj._check_conf_groups(ARRAY[v_groupName])
WHERE (v_isRollbackable AND chk_severity <= 2)
OR (NOT v_isRollbackable AND chk_severity <= 1)
ORDER BY chk_msg_type, chk_group, chk_schema, chk_tblseq
LOOP
RAISE WARNING 'emaj_create_group: error, %', r.chk_message;
END LOOP;
IF FOUND THEN
RAISE EXCEPTION 'emaj_create_group: One or several errors have been detected in the emaj_group_def table content.';
END IF;
END IF;
-- OK
-- get the time stamp of the operation
SELECT emaj._set_time_stamp('C') INTO v_timeId;
-- insert the row describing the group into the emaj_group table
-- (The group_is_rlbk_protected boolean column is always initialized as not group_is_rollbackable)
INSERT INTO emaj.emaj_group (group_name, group_is_rollbackable, group_creation_time_id, group_has_waiting_changes,
group_is_logging, group_is_rlbk_protected)
VALUES (v_groupName, v_isRollbackable, v_timeId, FALSE, FALSE, NOT v_isRollbackable);
group_is_logging, group_is_rlbk_protected, group_nb_table, group_nb_sequence)
VALUES (v_groupName, v_isRollbackable, v_timeId, FALSE, FALSE, NOT v_isRollbackable, 0, 0);
-- populate the group
IF NOT v_is_empty THEN
-- create new E-Maj log schemas, if needed
PERFORM emaj._create_log_schemas('CREATE_GROUP', ARRAY[v_groupName]);
PERFORM emaj._create_log_schemas('CREATE_GROUP', ARRAY[v_groupName]);
-- get and process all tables of the group (in priority order, NULLS being processed last)
PERFORM emaj._create_tbl(grpdef_schema, grpdef_tblseq, grpdef_group, grpdef_priority, grpdef_log_dat_tsp, grpdef_log_idx_tsp,
v_timeId, v_isRollbackable, FALSE)
FROM (
SELECT grpdef_schema, grpdef_tblseq, grpdef_group, grpdef_priority, grpdef_log_dat_tsp, grpdef_log_idx_tsp
FROM emaj.emaj_group_def, pg_catalog.pg_class, pg_catalog.pg_namespace
WHERE grpdef_group = v_groupName
AND relnamespace = pg_namespace.oid
AND nspname = grpdef_schema AND relname = grpdef_tblseq
AND relkind = 'r'
ORDER BY grpdef_priority, grpdef_schema, grpdef_tblseq
) AS t;
SELECT count(*) INTO v_nbTbl
FROM emaj.emaj_relation
WHERE rel_group = v_groupName AND rel_kind = 'r' AND upper_inf(rel_time_range);
PERFORM emaj._create_tbl(grpdef_schema, grpdef_tblseq, grpdef_group, grpdef_priority, grpdef_log_dat_tsp, grpdef_log_idx_tsp,
v_timeId, v_isRollbackable, FALSE)
FROM (
SELECT grpdef_schema, grpdef_tblseq, grpdef_group, grpdef_priority, grpdef_log_dat_tsp, grpdef_log_idx_tsp
FROM emaj.emaj_group_def, pg_catalog.pg_class, pg_catalog.pg_namespace
WHERE grpdef_group = v_groupName
AND relnamespace = pg_namespace.oid
AND nspname = grpdef_schema AND relname = grpdef_tblseq
AND relkind = 'r'
ORDER BY grpdef_priority, grpdef_schema, grpdef_tblseq
) AS t;
SELECT count(*) INTO v_nbTbl
FROM emaj.emaj_relation
WHERE rel_group = v_groupName AND rel_kind = 'r' AND upper_inf(rel_time_range);
-- get and process all sequences of the group (in priority order, NULLS being processed last)
PERFORM emaj._create_seq(grpdef_schema, grpdef_tblseq, grpdef_group, grpdef_priority, v_timeId)
FROM (
SELECT grpdef_schema, grpdef_tblseq, grpdef_group, grpdef_priority
FROM emaj.emaj_group_def, pg_catalog.pg_class, pg_catalog.pg_namespace
WHERE grpdef_group = v_groupName
AND relnamespace = pg_namespace.oid
AND nspname = grpdef_schema AND relname = grpdef_tblseq
AND relkind = 'S'
ORDER BY grpdef_priority, grpdef_schema, grpdef_tblseq
) AS t;
SELECT count(*) INTO v_nbSeq
FROM emaj.emaj_relation
WHERE rel_group = v_groupName AND rel_kind = 'S' AND upper_inf(rel_time_range);
PERFORM emaj._create_seq(grpdef_schema, grpdef_tblseq, grpdef_group, grpdef_priority, v_timeId)
FROM (
SELECT grpdef_schema, grpdef_tblseq, grpdef_group, grpdef_priority
FROM emaj.emaj_group_def, pg_catalog.pg_class, pg_catalog.pg_namespace
WHERE grpdef_group = v_groupName
AND relnamespace = pg_namespace.oid
AND nspname = grpdef_schema AND relname = grpdef_tblseq
AND relkind = 'S'
ORDER BY grpdef_priority, grpdef_schema, grpdef_tblseq
) AS t;
SELECT count(*) INTO v_nbSeq
FROM emaj.emaj_relation
WHERE rel_group = v_groupName AND rel_kind = 'S' AND upper_inf(rel_time_range);
-- update tables and sequences counters in the emaj_group table
UPDATE emaj.emaj_group SET group_nb_table = v_nbTbl, group_nb_sequence = v_nbSeq
WHERE group_name = v_groupName;
UPDATE emaj.emaj_group SET group_nb_table = v_nbTbl, group_nb_sequence = v_nbSeq
WHERE group_name = v_groupName;
-- check foreign keys with tables outside the group
PERFORM emaj._check_fk_groups(array[v_groupName]);
PERFORM emaj._check_fk_groups(array[v_groupName]);
END IF;
-- insert end in the history
INSERT INTO emaj.emaj_hist (hist_function, hist_event, hist_object, hist_wording)
VALUES ('CREATE_GROUP', 'END', v_groupName, v_nbTbl + v_nbSeq || ' tables/sequences processed');
Expand Down

0 comments on commit 220ee9f

Please sign in to comment.