mirror of
https://github.com/djrobstep/migra.git
synced 2024-09-11 13:56:05 +03:00
refactor generation of selectable changes
This commit is contained in:
parent
46e9ed7aff
commit
1b475b0569
131
migra/changes.py
131
migra/changes.py
@ -5,6 +5,7 @@ from .statements import Statements
|
||||
from functools import partial
|
||||
from collections import OrderedDict as od
|
||||
|
||||
|
||||
THINGS = [
|
||||
"schemas",
|
||||
"enums",
|
||||
@ -29,12 +30,27 @@ def statements_for_changes(
|
||||
add_dependents_for_modified=False,
|
||||
):
|
||||
added, removed, modified, unmodified = differences(things_from, things_target)
|
||||
if add_dependents_for_modified:
|
||||
for k, m in list(modified.items()):
|
||||
for d in m.dependents_all:
|
||||
if d in unmodified:
|
||||
modified[d] = unmodified.pop(d)
|
||||
modified = od(sorted(modified.items()))
|
||||
|
||||
return statements_from_differences(
|
||||
added,
|
||||
removed,
|
||||
modified,
|
||||
creations_only,
|
||||
drops_only,
|
||||
modifications,
|
||||
dependency_ordering,
|
||||
)
|
||||
|
||||
|
||||
def statements_from_differences(
|
||||
added,
|
||||
removed,
|
||||
modified,
|
||||
creations_only=False,
|
||||
drops_only=False,
|
||||
modifications=True,
|
||||
dependency_ordering=False,
|
||||
):
|
||||
statements = Statements()
|
||||
if not creations_only:
|
||||
pending_drops = set(removed)
|
||||
@ -124,8 +140,9 @@ def get_enum_modifications(tables_from, tables_target, enums_from, enums_target)
|
||||
return pre + recreate + post
|
||||
|
||||
|
||||
def get_schema_changes(tables_from, tables_target, enums_from, enums_target):
|
||||
def get_table_changes(tables_from, tables_target, enums_from, enums_target):
|
||||
added, removed, modified, _ = differences(tables_from, tables_target)
|
||||
|
||||
statements = Statements()
|
||||
for t, v in removed.items():
|
||||
statements.append(v.drop_statement)
|
||||
@ -148,22 +165,84 @@ def get_schema_changes(tables_from, tables_target, enums_from, enums_target):
|
||||
return statements
|
||||
|
||||
|
||||
def get_selectable_changes(
|
||||
selectables_from,
|
||||
selectables_target,
|
||||
enums_from,
|
||||
enums_target,
|
||||
add_dependents_for_modified=True,
|
||||
):
|
||||
tables_from = od(
|
||||
(k, v) for k, v in selectables_from.items() if v.relationtype == "r"
|
||||
)
|
||||
tables_target = od(
|
||||
(k, v) for k, v in selectables_target.items() if v.relationtype == "r"
|
||||
)
|
||||
|
||||
other_from = od(
|
||||
(k, v) for k, v in selectables_from.items() if v.relationtype != "r"
|
||||
)
|
||||
other_target = od(
|
||||
(k, v) for k, v in selectables_target.items() if v.relationtype != "r"
|
||||
)
|
||||
|
||||
added_tables, removed_tables, modified_tables, unmodified_tables = differences(
|
||||
tables_from, tables_target
|
||||
)
|
||||
added_other, removed_other, modified_other, unmodified_other = differences(
|
||||
other_from, other_target
|
||||
)
|
||||
|
||||
m_all = {}
|
||||
m_all.update(modified_tables)
|
||||
m_all.update(modified_other)
|
||||
m_all.update(removed_tables)
|
||||
m_all.update(removed_other)
|
||||
|
||||
if add_dependents_for_modified:
|
||||
for k, m in m_all.items():
|
||||
for d in m.dependents_all:
|
||||
if d in unmodified_other:
|
||||
modified_other[d] = unmodified_other.pop(d)
|
||||
modified_other = od(sorted(modified_other.items()))
|
||||
|
||||
statements = Statements()
|
||||
|
||||
def functions(d):
|
||||
return {k: v for k, v in d.items() if v.relationtype == "f"}
|
||||
|
||||
statements += statements_from_differences(
|
||||
added_other,
|
||||
removed_other,
|
||||
modified_other,
|
||||
drops_only=True,
|
||||
dependency_ordering=True,
|
||||
)
|
||||
|
||||
statements += get_table_changes(
|
||||
tables_from, tables_target, enums_from, enums_target
|
||||
)
|
||||
|
||||
if any([functions(added_other), functions(modified_other)]):
|
||||
statements += ["set check_function_bodies = off;"]
|
||||
|
||||
statements += statements_from_differences(
|
||||
added_other,
|
||||
removed_other,
|
||||
modified_other,
|
||||
creations_only=True,
|
||||
dependency_ordering=True,
|
||||
)
|
||||
return statements
|
||||
|
||||
|
||||
class Changes(object):
|
||||
def __init__(self, i_from, i_target):
|
||||
self.i_from = i_from
|
||||
self.i_target = i_target
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name == "schema":
|
||||
return partial(
|
||||
get_schema_changes,
|
||||
self.i_from.tables,
|
||||
self.i_target.tables,
|
||||
self.i_from.enums,
|
||||
self.i_target.enums,
|
||||
)
|
||||
|
||||
elif name == "non_pk_constraints":
|
||||
if name == "non_pk_constraints":
|
||||
a = self.i_from.constraints.items()
|
||||
b = self.i_target.constraints.items()
|
||||
a_od = od((k, v) for k, v in a if v.constraint_type != PK)
|
||||
@ -177,19 +256,13 @@ class Changes(object):
|
||||
b_od = od((k, v) for k, v in b if v.constraint_type == PK)
|
||||
return partial(statements_for_changes, a_od, b_od)
|
||||
|
||||
elif name == "views_and_functions":
|
||||
av = self.i_from.views.items()
|
||||
bv = self.i_target.views.items()
|
||||
am = self.i_from.materialized_views.items()
|
||||
bm = self.i_target.materialized_views.items()
|
||||
af = self.i_from.functions.items()
|
||||
bf = self.i_target.functions.items()
|
||||
avf = list(av) + list(am) + list(af)
|
||||
bvf = list(bv) + list(bm) + list(bf)
|
||||
avf = od(sorted(avf))
|
||||
bvf = od(sorted(bvf))
|
||||
elif name == "selectables":
|
||||
return partial(
|
||||
statements_for_changes, avf, bvf, add_dependents_for_modified=True
|
||||
get_selectable_changes,
|
||||
od(sorted(self.i_from.selectables.items())),
|
||||
od(sorted(self.i_target.selectables.items())),
|
||||
self.i_from.enums,
|
||||
self.i_target.enums,
|
||||
)
|
||||
|
||||
elif name in THINGS:
|
||||
|
@ -70,16 +70,9 @@ class Migration(object):
|
||||
self.add(self.changes.non_pk_constraints(drops_only=True))
|
||||
self.add(self.changes.pk_constraints(drops_only=True))
|
||||
self.add(self.changes.indexes(drops_only=True))
|
||||
self.add(
|
||||
self.changes.views_and_functions(drops_only=True, dependency_ordering=True)
|
||||
)
|
||||
self.add(self.changes.schema())
|
||||
v_and_f_changes = self.changes.views_and_functions(
|
||||
creations_only=True, dependency_ordering=True
|
||||
)
|
||||
if v_and_f_changes:
|
||||
self.add(["set check_function_bodies = off;"])
|
||||
self.add(v_and_f_changes)
|
||||
|
||||
self.add(self.changes.selectables())
|
||||
|
||||
self.add(self.changes.sequences(drops_only=True))
|
||||
self.add(self.changes.enums(drops_only=True, modifications=False))
|
||||
self.add(self.changes.extensions(drops_only=True))
|
||||
|
2
setup.py
2
setup.py
@ -6,7 +6,7 @@ from setuptools import setup, find_packages
|
||||
readme = io.open("README.md").read()
|
||||
setup(
|
||||
name="migra",
|
||||
version="1.0.1531741707",
|
||||
version="1.0.1535509978",
|
||||
url="https://github.com/djrobstep/migra",
|
||||
description="Like diff but for PostgreSQL schemas",
|
||||
long_description=readme,
|
||||
|
@ -10,8 +10,6 @@ drop view if exists "public"."ddd_changed" cascade;
|
||||
|
||||
drop view if exists "public"."aaa_view1" cascade;
|
||||
|
||||
set check_function_bodies = off;
|
||||
|
||||
create view "public"."ddd_changed" as SELECT basetable.name,
|
||||
'x'::text AS x
|
||||
FROM basetable;
|
||||
|
@ -16,8 +16,6 @@ drop view if exists "public"."ddd_changed" cascade;
|
||||
|
||||
drop view if exists "public"."aaa_view1" cascade;
|
||||
|
||||
set check_function_bodies = off;
|
||||
|
||||
create view "public"."ddd_changed" as SELECT basetable.name,
|
||||
'x'::text AS x
|
||||
FROM basetable;
|
||||
|
5
tests/FIXTURES/dependencies2/a.sql
Normal file
5
tests/FIXTURES/dependencies2/a.sql
Normal file
@ -0,0 +1,5 @@
|
||||
create schema x;
|
||||
|
||||
create table x.data(id uuid, name text);
|
||||
|
||||
create view x.q as select * from x.data;
|
0
tests/FIXTURES/dependencies2/additions.sql
Normal file
0
tests/FIXTURES/dependencies2/additions.sql
Normal file
7
tests/FIXTURES/dependencies2/b.sql
Normal file
7
tests/FIXTURES/dependencies2/b.sql
Normal file
@ -0,0 +1,7 @@
|
||||
create schema x;
|
||||
|
||||
create table x.t_data(id uuid, name text);
|
||||
|
||||
create view x.data as select * from x.t_data;
|
||||
|
||||
create view x.q as select * from x.data;
|
18
tests/FIXTURES/dependencies2/expected.sql
Normal file
18
tests/FIXTURES/dependencies2/expected.sql
Normal file
@ -0,0 +1,18 @@
|
||||
drop view if exists "x"."q" cascade;
|
||||
|
||||
drop table "x"."data";
|
||||
|
||||
create table "x"."t_data" (
|
||||
"id" uuid,
|
||||
"name" text
|
||||
);
|
||||
|
||||
|
||||
create view "x"."data" as SELECT t_data.id,
|
||||
t_data.name
|
||||
FROM x.t_data;
|
||||
|
||||
|
||||
create view "x"."q" as SELECT data.id,
|
||||
data.name
|
||||
FROM x.data;
|
0
tests/FIXTURES/dependencies2/expected2.sql
Normal file
0
tests/FIXTURES/dependencies2/expected2.sql
Normal file
@ -8,6 +8,4 @@ create type "goodschema"."sdfasdfasdf" as enum ('not shipped', 'shipped', 'deliv
|
||||
|
||||
alter table "goodschema"."t" add column "name" text;
|
||||
|
||||
set check_function_bodies = off;
|
||||
|
||||
create view "goodschema"."v" as SELECT 2;
|
||||
|
@ -35,15 +35,27 @@ def outs():
|
||||
return io.StringIO(), io.StringIO()
|
||||
|
||||
|
||||
def test_with_fixtures():
|
||||
for FIXTURE_NAME in ["dependencies"]:
|
||||
def test_deps():
|
||||
for FIXTURE_NAME in ["dependencies", "dependencies2"]:
|
||||
do_fixture_test(FIXTURE_NAME)
|
||||
|
||||
|
||||
def test_everything():
|
||||
for FIXTURE_NAME in ["everything"]:
|
||||
do_fixture_test(FIXTURE_NAME, with_privileges=True)
|
||||
|
||||
|
||||
def test_singleschemea():
|
||||
for FIXTURE_NAME in ["singleschema"]:
|
||||
do_fixture_test(FIXTURE_NAME, schema="goodschema")
|
||||
|
||||
|
||||
def test_singleschema_ext():
|
||||
for FIXTURE_NAME in ["singleschema_ext"]:
|
||||
do_fixture_test(FIXTURE_NAME, create_extensions_only=True)
|
||||
|
||||
|
||||
def test_privs():
|
||||
for FIXTURE_NAME in ["privileges"]:
|
||||
do_fixture_test(FIXTURE_NAME, with_privileges=True)
|
||||
|
||||
@ -70,10 +82,11 @@ def do_fixture_test(
|
||||
out, err = outs()
|
||||
assert run(args, out=out, err=err) == 3
|
||||
assert out.getvalue() == ""
|
||||
assert (
|
||||
err.getvalue()
|
||||
== "-- ERROR: destructive statements generated. Use the --unsafe flag to suppress this error.\n"
|
||||
)
|
||||
|
||||
DESTRUCTIVE = "-- ERROR: destructive statements generated. Use the --unsafe flag to suppress this error.\n"
|
||||
|
||||
assert err.getvalue() == DESTRUCTIVE
|
||||
|
||||
args = parse_args(flags + [d0, d1])
|
||||
assert args.unsafe
|
||||
assert args.schema == schema
|
||||
|
Loading…
Reference in New Issue
Block a user