Skip to content

Commit 3be383a

Browse files
committed
Add function to rebuild topology relations
1 parent 5f723f7 commit 3be383a

3 files changed

Lines changed: 83 additions & 0 deletions

File tree

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"""
2+
Rebuild check constraints for all layers in the topology. Sometimes, dumps and
3+
restores can invalidate constraints, requiring topogeometries to be rebuilt.
4+
"""
5+
from ..config import TopologyContext
6+
from psycopg.sql import Identifier, Literal
7+
from rich import print
8+
9+
def rebuild_layer_constraints(ctx: TopologyContext):
10+
layers = ctx.database.run_query(
11+
"""
12+
SELECT l.*
13+
FROM topology.layer l
14+
JOIN topology.topology t
15+
ON l.topology_id = t.id
16+
WHERE t.name = :topo_name
17+
"""
18+
).all()
19+
20+
for l in layers:
21+
# If the constraint exists and is validated, we skip it
22+
table = Identifier(l.schema_name, l.table_name)
23+
table_name = f"{l.schema_name}.{l.table_name}"
24+
res = ctx.database.run_query(
25+
"""
26+
SELECT conname, convalidated
27+
FROM pg_constraint
28+
WHERE conname = 'check_topogeom_topo'
29+
AND conrelid = :table::regclass
30+
""",
31+
dict(table=table_name),
32+
).one_or_none()
33+
_exists = res is not None
34+
_valid = _exists and res.convalidated
35+
print(f"[bold cyan]{table_name}[/bold cyan]:")
36+
if _exists and _valid:
37+
print(f" valid, skipping")
38+
continue
39+
if _exists and not _valid:
40+
print(f" invalid")
41+
ctx.database.run_sql("ALTER TABLE {table} DROP CONSTRAINT check_topogeom_topo", dict(table=table))
42+
43+
print(f" rebuilding")
44+
45+
ctx.database.run_sql(
46+
"""
47+
ALTER TABLE {table} ADD CONSTRAINT check_topogeom_topo
48+
CHECK (
49+
(
50+
(({feature_column}).topology_id = {topology_id})
51+
AND (({feature_column}).layer_id = {layer_id})
52+
AND (({feature_column}).type = {feature_type})
53+
)
54+
) NOT VALID;
55+
ALTER TABLE {table} VALIDATE CONSTRAINT check_topogeom_topo;
56+
""",
57+
dict(
58+
feature_column=Identifier(l.feature_column),
59+
table=table,
60+
topology_id = Literal(l.topology_id),
61+
layer_id = Literal(l.layer_id),
62+
feature_type = Literal(l.feature_type)
63+
)
64+
)

mapboard/topology_manager/manager.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from .commands.create_tables import create_tables
1010
from .commands.check_setup import check_topology_setup
1111
from .commands.edge_relations import rebuild_edge_relations, validate_edge_relations
12+
from .commands.rebuild_layer_constraints import rebuild_layer_constraints
1213

1314

1415
class TopologyManager:
@@ -81,3 +82,7 @@ def validate_edge_relations(self):
8182
def rebuild_edge_relations(self):
8283
"""Rebuild the cached __edge_relation table (repair out-of-sync triggers)."""
8384
return rebuild_edge_relations(self._ctx)
85+
86+
def rebuild_layer_constraints(self):
87+
"""Rebuild constraints on topology layers"""
88+
return rebuild_layer_constraints(self._ctx)

tests/core/test_05_map_faces.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,20 @@ def test_erase_and_consolidate_faces(self, mgr, db):
128128
assert n_faces(db) == 99
129129
assert n_face_primitives(db) == 98
130130

131+
def test_rebuild_relations(self, mgr, db):
132+
"""Test rebuilding the relations of a topolgy.
133+
134+
Note, this is somewhat arbitrary in terms of where in the test suite we run it,
135+
but the point is to just ensure these functions work properly.
136+
"""
137+
# Rebuild the relations
138+
mgr.rebuild_edge_relations()
139+
mgr.rebuild_layer_constraints()
140+
141+
# Check that we have the same number of faces and primitives
142+
assert n_faces(db) == 99
143+
assert n_face_primitives(db) == 98
144+
131145

132146
def test_change_map_face_type(mgr, db):
133147
"""Test changing the type of a map face."""

0 commit comments

Comments
 (0)