From 699c949d8ec1260ca12dfbfa05c404be7395c9cc Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Sat, 3 Sep 2022 17:41:16 +0200 Subject: [proxy] fix a bug that caused mappings to be marked as required even after they stopped being required --- src/hydrilla/proxy/simple_dependency_satisfying.py | 11 ++- .../_operations/recompute_dependencies.py | 97 ++++++++++++++++++++-- src/hydrilla/proxy/state_impl/base.py | 24 +++--- src/hydrilla/proxy/tables.sql | 14 ++++ 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, -- cgit v1.2.3