aboutsummaryrefslogtreecommitdiff
path: root/src/hydrilla
diff options
context:
space:
mode:
Diffstat (limited to 'src/hydrilla')
-rw-r--r--src/hydrilla/proxy/simple_dependency_satisfying.py11
-rw-r--r--src/hydrilla/proxy/state_impl/_operations/recompute_dependencies.py97
-rw-r--r--src/hydrilla/proxy/state_impl/base.py24
-rw-r--r--src/hydrilla/proxy/tables.sql14
4 files changed, 127 insertions, 19 deletions
diff --git a/src/hydrilla/proxy/simple_dependency_satisfying.py b/src/hydrilla/proxy/simple_dependency_satisfying.py
index a5431f9..e5de8c6 100644
--- a/src/hydrilla/proxy/simple_dependency_satisfying.py
+++ b/src/hydrilla/proxy/simple_dependency_satisfying.py
@@ -96,8 +96,10 @@ class ComputedPayload:
@dc.dataclass
class MappingChoice:
- info: item_infos.MappingInfo
- required: bool = False
+ info: item_infos.MappingInfo
+ required: bool = False
+ mapping_dependencies: t.Sequence[item_infos.MappingInfo] = ()
+
payloads: dict[url_patterns.ParsedPattern, ComputedPayload] = \
dc.field(default_factory=dict)
@@ -297,6 +299,11 @@ class _ComputationData:
for identifier in required_mappings:
choices[identifier].required = True
+ for mapping_choice in choices.values():
+ depended_set = mapping_deps[mapping_choice.info.identifier]
+ mapping_choice.mapping_dependencies = \
+ tuple(choices[identifier].info for identifier in depended_set)
+
return choices
def compute_payloads(
diff --git a/src/hydrilla/proxy/state_impl/_operations/recompute_dependencies.py b/src/hydrilla/proxy/state_impl/_operations/recompute_dependencies.py
index 9419f91..7539e29 100644
--- a/src/hydrilla/proxy/state_impl/_operations/recompute_dependencies.py
+++ b/src/hydrilla/proxy/state_impl/_operations/recompute_dependencies.py
@@ -88,11 +88,79 @@ def _get_current_required_state(
# recursively depend on) let's make sure that their exact same versions will
# be enabled after the change. Make exception for mappings specified by the
# caller.
- with base.temporary_ids_table(
- cursor = cursor,
- ids = unlocked_required_mappings,
- table_name = '__unlocked_ids'
+ # The mappings to make exception for are passed by their item_id's. First,
+ # we compute a set of their corresponding item_version_id's.
+ with base.temporary_ids_tables(
+ cursor = cursor,
+ tables = [
+ ('__work_ids_0', unlocked_required_mappings),
+ ('__work_ids_1', []),
+ ('__unlocked_ids', [])
+ ]
):
+ cursor.execute(
+ '''
+ INSERT INTO
+ __work_ids_1
+ SELECT
+ item_version_id
+ FROM
+ item_versions
+ WHERE
+ item_id IN __work_ids_0;
+ '''
+ )
+
+ # Recursively update the our unlocked ids collection with all mapping
+ # version ids that are required by mapping versions already referenced
+ # there.
+ work_tab = '__work_ids_1'
+ next_tab = '__work_ids_0'
+
+ while True:
+ cursor.execute(f'SELECT COUNT(*) FROM {work_tab};')
+
+ (count,), = cursor.fetchall()
+
+ if count == 0:
+ break
+
+ cursor.execute(f'DELETE FROM {next_tab};')
+
+ cursor.execute(
+ f'''
+ INSERT INTO
+ {next_tab}
+ SELECT
+ item_version_id
+ FROM
+ item_versions AS iv
+ JOIN items AS i
+ USING (item_id)
+ JOIN mapping_statuses AS ms
+ USING (item_id)
+ JOIN resolved_required_mappings AS rrm
+ ON iv.item_version_id = rrm.required_mapping_id
+ WHERE
+ ms.enabled != 'E' AND
+ rrm.requiring_mapping_id IN {work_tab} AND
+ rrm.requiring_mapping_id NOT IN __unlocked_ids;
+ '''
+ )
+
+ cursor.execute(
+ f'''
+ INSERT OR IGNORE INTO
+ __unlocked_ids
+ SELECT
+ id
+ FROM
+ {work_tab};
+ '''
+ )
+
+ work_tab, next_tab = next_tab, work_tab
+
# Describe all required mappings using requirement objects.
cursor.execute(
'''
@@ -102,8 +170,8 @@ def _get_current_required_state(
item_versions_extra AS ive
JOIN items AS i USING (item_id)
WHERE
- i.type = 'M' AND
- item_id NOT IN __unlocked_ids AND
+ i.type = 'M' AND
+ ive.item_version_id NOT IN __unlocked_ids AND
ive.active = 'R';
''',
)
@@ -138,7 +206,8 @@ def _get_current_required_state(
JOIN items AS i_m
ON iv_m.item_id = i_m.item_id
WHERE
- i_m.item_id NOT IN __unlocked_ids AND iv_m.active = 'R';
+ iv_m.item_version_id NOT IN __unlocked_ids AND
+ iv_m.active = 'R';
''',
)
@@ -250,6 +319,8 @@ def _recompute_dependencies_no_state_update_no_pull_files(
cursor.execute('DELETE FROM payloads;')
+ cursor.execute('DELETE FROM resolved_required_mappings;')
+
for choice in mapping_choices.values():
mapping_ver_id = mappings_to_ids[choice.info]
@@ -294,6 +365,18 @@ def _recompute_dependencies_no_state_update_no_pull_files(
('R' if choice.required else 'A', mapping_ver_id)
)
+ for depended_mapping_info in choice.mapping_dependencies:
+ cursor.execute(
+ '''
+ INSERT INTO resolved_required_mappings(
+ requiring_mapping_id,
+ required_mapping_id
+ )
+ VALUES (?, ?);
+ ''',
+ (mapping_ver_id, mappings_to_ids[depended_mapping_info])
+ )
+
for num, (pattern, payload) in enumerate(choice.payloads.items()):
cursor.execute(
'''
diff --git a/src/hydrilla/proxy/state_impl/base.py b/src/hydrilla/proxy/state_impl/base.py
index e5a9898..82b8734 100644
--- a/src/hydrilla/proxy/state_impl/base.py
+++ b/src/hydrilla/proxy/state_impl/base.py
@@ -50,22 +50,26 @@ from .. import policies
@contextmanager
-def temporary_ids_table(
- cursor: sqlite3.Cursor,
- ids: t.Iterable[int],
- table_name: str = '__helper_ids'
+def temporary_ids_tables(
+ cursor: sqlite3.Cursor,
+ tables: t.Iterable[tuple[str, t.Iterable[int]]]
) -> t.Iterator[None]:
- cursor.execute(
- f'CREATE TEMPORARY TABLE "{table_name}"(id INTEGER PRIMARY KEY);'
- )
+ created: set[str] = set()
try:
- for id in ids:
- cursor.execute(f'INSERT INTO "{table_name}" VALUES(?);', (id,))
+ for name, ids in tables:
+ cursor.execute(
+ f'CREATE TEMPORARY TABLE "{name}"(id INTEGER PRIMARY KEY);'
+ )
+ created.add(name)
+
+ for id in ids:
+ cursor.execute(f'INSERT INTO "{name}" VALUES(?);', (id,))
yield
finally:
- cursor.execute(f'DROP TABLE "{table_name}";')
+ for name in created:
+ cursor.execute(f'DROP TABLE "{name}";')
@dc.dataclass(frozen=True)
diff --git a/src/hydrilla/proxy/tables.sql b/src/hydrilla/proxy/tables.sql
index 2483c96..2a3e95a 100644
--- a/src/hydrilla/proxy/tables.sql
+++ b/src/hydrilla/proxy/tables.sql
@@ -272,6 +272,20 @@ CREATE TABLE resolved_depended_resources(
ON DELETE CASCADE
) WITHOUT ROWID;
+CREATE TABLE resolved_required_mappings(
+ requiring_mapping_id INTEGER,
+ required_mapping_id INTEGER,
+
+ PRIMARY KEY (requiring_mapping_id, required_mapping_id),
+
+ FOREIGN KEY (requiring_mapping_id)
+ REFERENCES item_versions (item_version_id)
+ ON DELETE CASCADE,
+ FOREIGN KEY (required_mapping_id)
+ REFERENCES item_versions (item_version_id)
+ ON DELETE CASCADE
+) WITHOUT ROWID;
+
CREATE TABLE files(
file_id INTEGER PRIMARY KEY,