Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migration functions - release #38

Merged
merged 1 commit into from
Jul 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 1.9.0
current_version = 1.9.1
commit = False
tag = False

Expand Down
10 changes: 10 additions & 0 deletions microcosm_eventsource/ddl/array_sort_unique.create.ddl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* array_sort_unique(ANYARRAY)
*
* Return sorted and unique array
*/
CREATE OR REPLACE FUNCTION array_sort_unique (ANYARRAY)
RETURNS ANYARRAY LANGUAGE SQL
AS $$
SELECT ARRAY(SELECT DISTINCT unnest($1) ORDER BY 1)
$$;
1 change: 1 addition & 0 deletions microcosm_eventsource/ddl/array_sort_unique.drop.ddl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP FUNCTION IF EXISTS array_sort_unique (ANYARRAY);
42 changes: 42 additions & 0 deletions microcosm_eventsource/ddl/proc_event_type_delete.create.ddl
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* proc_event_type_delete(table_name, old_event_type, model_id_row_name)
*
* Should be only used by data migrations scripts.
* Delete events with old_event_type and remove it from states,
* Must pass model_id_row_name - the row name that links to the parent model
* (Example: for company_event table, the model_id_row_name should be company_id)
*
* Example:
* SELECT proc_event_type_delete('task_event', 'SCHEDULED', 'task_id');
*
* Details:
* - Updates parent_id of relevant child events
* - Updates the state column (Ensures that the state is sorted and has unique events)
* - Does not promise valid events or transitions.
* - Cannot be used if model.__unique_parent__ is set to False (missing table_names_parent_id_key constraint),
*/
CREATE OR REPLACE FUNCTION proc_event_type_delete(
table_name regclass,
old_event_type character varying(255),
model_id_row_name character varying(255)) RETURNS void AS
$func$
BEGIN
EXECUTE format(
'
CREATE TEMP TABLE events_to_remove_%%1$s_%%2$s AS (
SELECT id
FROM %%1$s
WHERE event_type = ''%%2$s''
);
SELECT proc_events_delete(''%%1$s'', ''events_to_remove_%%1$s_%%2$s'', ''%%3$s'');

UPDATE %%1$s
SET state = array_remove(state, ''%%2$s'')
WHERE ''%%2$s'' = ANY(state);
',
table_name,
old_event_type,
model_id_row_name
);
END
$func$ LANGUAGE plpgsql;
1 change: 1 addition & 0 deletions microcosm_eventsource/ddl/proc_event_type_delete.drop.ddl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP FUNCTION IF EXISTS proc_event_type_delete(regclass, character varying(255), character varying(255));
37 changes: 37 additions & 0 deletions microcosm_eventsource/ddl/proc_event_type_replace.create.ddl
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* proc_event_type_replace(table_name, old_event_type, new_event_type)
*
* Should be only used by data migrations scripts.
* Replaces old_event_type with new_event_type in table_name table,
*
* Example:
* SELECT proc_event_type_replace('task_event', 'SCHEDULED', 'CANCELED');
*
* Details:
* - Updates the event_type column
* - Updates the state column (Ensures that the state is sorted and has unique events)
* - Does not promise valid events or transitions.
*/

CREATE OR REPLACE FUNCTION proc_event_type_replace(
table_name regclass,
old_event_type character varying(255),
new_event_type character varying(255)) RETURNS void AS
$func$
BEGIN
EXECUTE format(
'
UPDATE %%1$s
SET event_type = ''%%3$s''
WHERE event_type = ''%%2$s'';

UPDATE %%1$s
SET state = array_sort_unique(array_replace(state, ''%%2$s'', ''%%3$s''))
WHERE ''%%2$s'' = ANY(state);
',
table_name,
old_event_type,
new_event_type
);
END
$func$ LANGUAGE plpgsql;
1 change: 1 addition & 0 deletions microcosm_eventsource/ddl/proc_event_type_replace.drop.ddl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP FUNCTION IF EXISTS proc_event_type_replace(regclass, character varying(255), character varying(255));
109 changes: 109 additions & 0 deletions microcosm_eventsource/ddl/proc_events_delete.create.ddl
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* proc_events_delete(table_name, events_to_delete_table_name, model_id_row_name)
*
* Should be only used by data migrations scripts.
* Deletes events from table table_name - a valid microcosm-event-source event table.
* Pass the events to delete as column id from table events_to_delete_table_name.
* Must pass model_id_row_name - the row name that links to the parent model
* (Example: for company_event table, the model_id_row_name should be company_id)
*
* Example:
* CREATE TEMP TABLE events_to_remove AS (SELECT id FROM task_event WHERE event_type='SCHEDULED');
* SELECT proc_events_delete('task_event', 'events_to_remove', 'task_id');
*
* Details:
* - Won't update the state (see: proc_event_type_delete function)
* - Deletes the relevant events
* - Updates the parent_id of the child events
* - Cannot be used if model.__unique_parent__ is set to False (missing table_names_parent_id_key constraint),
* If thats the case - use proc_events_delete_with_no_parent_id_constraint function instead.
*/
CREATE OR REPLACE FUNCTION proc_events_delete(
table_name regclass,
events_to_delete_table_name regclass,
model_id_row_name character varying(255)) RETURNS void AS
$func$
BEGIN
EXECUTE format(
'
ALTER TABLE %%1$s DROP CONSTRAINT %%1$s_parent_id_key;
SELECT proc_events_delete_with_no_parent_id_constraint(''%%1$s'', ''%%2$s'', ''%%3$s'');
ALTER TABLE %%1$s ADD CONSTRAINT %%1$s_parent_id_key UNIQUE (parent_id);
',
table_name,
events_to_delete_table_name,
model_id_row_name
);
END
$func$ LANGUAGE plpgsql;


/*
* proc_events_delete_with_no_parent_id_constraint(table_name, events_to_delete_table_name, model_id_row_name)
*
* Should be only used by data migrations scripts.
* Same as proc_events_delete function but for use if model.__unique_parent__ is set to False
* (missing table_names_parent_id_key constraint),
*/
CREATE OR REPLACE FUNCTION proc_events_delete_with_no_parent_id_constraint(
table_name regclass,
events_to_delete_table_name regclass,
model_id_row_name character varying(255)) RETURNS void AS
$func$
BEGIN
EXECUTE format(
'
-- Temporary drop the constraint, bring it back before the end of the transaction.
ALTER TABLE %%1$s DROP CONSTRAINT %%1$s_parent_id_fkey;

-- Updates parent_id of events if the current parent_id is going to be deleted.
-- Handle the case that the new parent_id is not the parent_id of the current parent.
-- (For example from the events chain a->b->c->d, both events b and c are going to be deleted)
-- Skip events that are going to be deleted anyway.
-- Skip the new"top events" - events that are going to have no parent id.
WITH child_events AS (
SELECT child_event.id, child_event.clock, child_event.%%3$s
FROM %%1$s AS child_event
JOIN %%2$s AS parent_event
ON parent_event.id = child_event.parent_id
LEFT JOIN %%2$s AS events_to_avoid
ON events_to_avoid.id = child_event.id
WHERE events_to_avoid.id is null
),
new_child_events_parents AS (
SELECT distinct on (child_events.clock)
child_events.id AS child_event_id,
new_parent.id AS new_parent_id
FROM %%1$s AS new_parent
RIGHT JOIN child_events
ON new_parent.%%3$s = child_events.%%3$s
AND new_parent.clock < child_events.clock
LEFT JOIN %%2$s AS events_to_avoid
ON events_to_avoid.id = new_parent.id
WHERE events_to_avoid.id is null
ORDER BY child_events.clock, new_parent.clock desc
)
UPDATE %%1$s
SET parent_id = new_child_events_parents.new_parent_id
FROM new_child_events_parents
WHERE %%1$s.id = new_child_events_parents.child_event_id;

-- Delete the events
DELETE FROM %%1$s
USING %%2$s
WHERE %%1$s.id = %%2$s.id;

-- Set null parent id to the new "top events".
UPDATE %%1$s
SET parent_id = null
FROM %%2$s
WHERE %%1$s.parent_id = %%2$s.id;

ALTER TABLE %%1$s ADD CONSTRAINT %%1$s_parent_id_fkey FOREIGN KEY (parent_id) REFERENCES %%1$s(id);
',
table_name,
events_to_delete_table_name,
model_id_row_name
);
END
$func$ LANGUAGE plpgsql;
2 changes: 2 additions & 0 deletions microcosm_eventsource/ddl/proc_events_delete.drop.ddl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
DROP FUNCTION IF EXISTS proc_events_delete(regclass, regclass, character varying(255));
DROP FUNCTION IF EXISTS proc_events_delete_with_no_parent_id_constraint(regclass, character varying(255), character varying(255));
18 changes: 16 additions & 2 deletions microcosm_eventsource/func.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,26 @@ def load_ddl(name, action):
listen(
Model.metadata,
"after_create",
DDL(load_ddl("last_agg_sfunc", "create") + load_ddl("last_agg", "create")),
DDL(
load_ddl("array_sort_unique", "create") +
load_ddl("proc_events_delete", "create") +
load_ddl("proc_event_type_delete", "create") +
load_ddl("last_agg_sfunc", "create") +
load_ddl("last_agg", "create") +
load_ddl("proc_event_type_replace", "create")
),
)
listen(
Model.metadata,
"after_drop",
DDL(load_ddl("last_agg", "drop") + load_ddl("last_agg_sfunc", "drop")),
DDL(
load_ddl("array_sort_unique", "drop") +
load_ddl("proc_events_delete", "drop") +
load_ddl("proc_event_type_delete", "drop") +
load_ddl("last_agg", "drop") +
load_ddl("last_agg_sfunc", "drop") +
load_ddl("proc_event_type_replace", "drop")
),
)


Expand Down
Empty file.
Loading