From a8d30d4da5747965c4cc614b738036c2f28aeaab Mon Sep 17 00:00:00 2001 From: John Davis Date: Fri, 1 Dec 2023 17:17:00 -0500 Subject: [PATCH 01/43] Set cascade_backrefs=False on Visualization.revisions --- lib/galaxy/model/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index d8a44615771a..0cd289339f5f 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -9937,6 +9937,7 @@ class Visualization(Base, HasTags, Dictifiable, RepresentById): back_populates="visualization", cascade="all, delete-orphan", primaryjoin=(lambda: Visualization.id == VisualizationRevision.visualization_id), + cascade_backrefs=False, ) latest_revision = relationship( "VisualizationRevision", From 28be6afe5458a9e312358fdf5de9004ae8d31329 Mon Sep 17 00:00:00 2001 From: John Davis Date: Fri, 1 Dec 2023 17:24:51 -0500 Subject: [PATCH 02/43] set cascade_backrefs=False on Dataset.hashes --- lib/galaxy/model/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 0cd289339f5f..b98258982a68 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -3873,7 +3873,7 @@ class Dataset(Base, StorableObject, Serializable): ), viewonly=True, ) - hashes = relationship("DatasetHash", back_populates="dataset") + hashes = relationship("DatasetHash", back_populates="dataset", cascade_backrefs=False) sources = relationship("DatasetSource", back_populates="dataset") history_associations = relationship("HistoryDatasetAssociation", back_populates="dataset") library_associations = relationship( From 81afea8367a8beac6875b8cfe645bafb55a7eb14 Mon Sep 17 00:00:00 2001 From: John Davis Date: Fri, 1 Dec 2023 17:48:42 -0500 Subject: [PATCH 03/43] set cascade_backrefs=False on User.stored_worfklows --- lib/galaxy/model/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index b98258982a68..6e6648d892dc 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -739,7 +739,10 @@ class User(Base, Dictifiable, RepresentById): data_manager_histories = relationship("DataManagerHistoryAssociation", back_populates="user") roles = relationship("UserRoleAssociation", back_populates="user") stored_workflows = relationship( - "StoredWorkflow", back_populates="user", primaryjoin=(lambda: User.id == StoredWorkflow.user_id) # type: ignore[has-type] + "StoredWorkflow", + back_populates="user", + primaryjoin=(lambda: User.id == StoredWorkflow.user_id), # type: ignore[has-type] + cascade_backrefs=False, ) all_notifications = relationship("UserNotificationAssociation", back_populates="user") non_private_roles = relationship( From 5c043b3ffaa21c9cc9758fa2a4a963f67e2e223d Mon Sep 17 00:00:00 2001 From: John Davis Date: Sat, 2 Dec 2023 01:31:18 -0500 Subject: [PATCH 04/43] Set cascade_backrefs=False on Dataset.history_associations, add safeguard --- lib/galaxy/model/__init__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 6e6648d892dc..895844934309 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -3878,7 +3878,7 @@ class Dataset(Base, StorableObject, Serializable): ) hashes = relationship("DatasetHash", back_populates="dataset", cascade_backrefs=False) sources = relationship("DatasetSource", back_populates="dataset") - history_associations = relationship("HistoryDatasetAssociation", back_populates="dataset") + history_associations = relationship("HistoryDatasetAssociation", back_populates="dataset", cascade_backrefs=False) library_associations = relationship( "LibraryDatasetDatasetAssociation", primaryjoin=(lambda: LibraryDatasetDatasetAssociation.table.c.dataset_id == Dataset.id), @@ -4374,6 +4374,11 @@ def __init__( elif dataset: add_object_to_object_session(self, dataset) self.dataset = dataset + + # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. + if object_session(dataset): + object_session(dataset).add(self) + self.parent_id = parent_id @property From 913c55aeea53179fff4a921fa4f09eea0db50daa Mon Sep 17 00:00:00 2001 From: John Davis Date: Sat, 2 Dec 2023 11:41:35 -0500 Subject: [PATCH 05/43] Set cascade_backrefs=False on Library.actions, add safeguards --- lib/galaxy/model/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 895844934309..38fb8d04bfdc 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -3712,6 +3712,9 @@ def __init__(self, action, library_item, role): self.action = action if isinstance(library_item, Library): self.library = library_item + # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. + if object_session(library_item): + object_session(library_item).add(self) else: raise Exception(f"Invalid Library specified: {library_item.__class__.__name__}") self.role = role @@ -3733,6 +3736,9 @@ def __init__(self, action, library_item, role): self.action = action if isinstance(library_item, LibraryFolder): self.folder = library_item + # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. + if object_session(library_item): + object_session(library_item).add(self) else: raise Exception(f"Invalid LibraryFolder specified: {library_item.__class__.__name__}") self.role = role @@ -5376,7 +5382,7 @@ class Library(Base, Dictifiable, HasName, Serializable): description = Column(TEXT) synopsis = Column(TEXT) root_folder = relationship("LibraryFolder", back_populates="library_root") - actions = relationship("LibraryPermissions", back_populates="library") + actions = relationship("LibraryPermissions", back_populates="library", cascade_backrefs=False) permitted_actions = get_permitted_actions(filter="LIBRARY") dict_collection_visible_keys = ["id", "name"] From 68e502aace00e9fe4a5a8ba588a92b94da27f030 Mon Sep 17 00:00:00 2001 From: John Davis Date: Sat, 2 Dec 2023 12:27:24 -0500 Subject: [PATCH 06/43] Set cascade_backrefs=False on Group.roles, add safeguard --- lib/galaxy/model/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 38fb8d04bfdc..e66041d71339 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -2774,7 +2774,7 @@ class Group(Base, Dictifiable, RepresentById): name = Column(String(255), index=True, unique=True) deleted = Column(Boolean, index=True, default=False) quotas = relationship("GroupQuotaAssociation", back_populates="group") - roles = relationship("GroupRoleAssociation", back_populates="group") + roles = relationship("GroupRoleAssociation", back_populates="group", cascade_backrefs=False) users = relationship("UserGroupAssociation", back_populates="group") dict_collection_visible_keys = ["id", "name"] @@ -3502,6 +3502,9 @@ class GroupRoleAssociation(Base, RepresentById): def __init__(self, group, role): self.group = group + # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. + if object_session(group): + object_session(group).add(self) self.role = role From f8fc5dc613ae18364c25751f36f6b15c4619b884 Mon Sep 17 00:00:00 2001 From: John Davis Date: Sat, 2 Dec 2023 12:28:34 -0500 Subject: [PATCH 07/43] Set cascade_backrefs=False on Dataset.library_associations --- lib/galaxy/model/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index e66041d71339..7a77f1a1bc9f 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -3892,6 +3892,7 @@ class Dataset(Base, StorableObject, Serializable): "LibraryDatasetDatasetAssociation", primaryjoin=(lambda: LibraryDatasetDatasetAssociation.table.c.dataset_id == Dataset.id), back_populates="dataset", + cascade_backrefs=False, ) # failed_metadata is only valid as DatasetInstance state currently From dffe9e4a239c93c14edac7188c46fd83cbf48247 Mon Sep 17 00:00:00 2001 From: John Davis Date: Sat, 2 Dec 2023 13:09:04 -0500 Subject: [PATCH 08/43] Set cascade_backrefs=False on WorkflowStep.post_job_actions, add safeguard --- lib/galaxy/model/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 7a77f1a1bc9f..e08c724792fa 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -2465,6 +2465,9 @@ def __init__(self, action_type, workflow_step=None, output_name=None, action_arg self.output_name = output_name self.action_arguments = action_arguments self.workflow_step = workflow_step + # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. + if object_session(workflow_step): + object_session(workflow_step).add(self) class PostJobActionAssociation(Base, RepresentById): @@ -7658,7 +7661,7 @@ class WorkflowStep(Base, RepresentById): order_by=lambda: WorkflowStepAnnotationAssociation.id, back_populates="workflow_step", ) - post_job_actions = relationship("PostJobAction", back_populates="workflow_step") + post_job_actions = relationship("PostJobAction", back_populates="workflow_step", cascade_backrefs=False) inputs = relationship("WorkflowStepInput", back_populates="workflow_step") workflow_outputs = relationship("WorkflowOutput", back_populates="workflow_step") output_connections = relationship( From 391888bd9583ecca01376adb98b8b3d462eca3a5 Mon Sep 17 00:00:00 2001 From: John Davis Date: Sat, 2 Dec 2023 13:12:12 -0500 Subject: [PATCH 09/43] Set cascade_backrefs=False on Job.post_job_actions, add safeguard --- lib/galaxy/model/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index e08c724792fa..0f075460c264 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -1331,7 +1331,7 @@ class Job(Base, JobLike, UsesCreateAndUpdateTime, Dictifiable, Serializable): ) output_dataset_collection_instances = relationship("JobToOutputDatasetCollectionAssociation", back_populates="job") output_dataset_collections = relationship("JobToImplicitOutputDatasetCollectionAssociation", back_populates="job") - post_job_actions = relationship("PostJobActionAssociation", back_populates="job") + post_job_actions = relationship("PostJobActionAssociation", back_populates="job", cascade_backrefs=False) input_library_datasets = relationship("JobToInputLibraryDatasetAssociation", back_populates="job") output_library_datasets = relationship("JobToOutputLibraryDatasetAssociation", back_populates="job") external_output_metadata = relationship("JobExternalOutputMetadata", back_populates="job") @@ -2482,6 +2482,9 @@ class PostJobActionAssociation(Base, RepresentById): def __init__(self, pja, job=None, job_id=None): if job is not None: self.job = job + # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. + if object_session(job): + object_session(job).add(self) elif job_id is not None: self.job_id = job_id else: From 504687422d126e47135faa6981a5f93cd45d4e11 Mon Sep 17 00:00:00 2001 From: John Davis Date: Sat, 2 Dec 2023 13:13:44 -0500 Subject: [PATCH 10/43] Set cascade_backrefs=False on User.all_notifications, add safeguard --- lib/galaxy/model/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 0f075460c264..75850fe221ae 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -744,7 +744,7 @@ class User(Base, Dictifiable, RepresentById): primaryjoin=(lambda: User.id == StoredWorkflow.user_id), # type: ignore[has-type] cascade_backrefs=False, ) - all_notifications = relationship("UserNotificationAssociation", back_populates="user") + all_notifications = relationship("UserNotificationAssociation", back_populates="user", cascade_backrefs=False) non_private_roles = relationship( "UserRoleAssociation", viewonly=True, @@ -2855,6 +2855,9 @@ class UserNotificationAssociation(Base, RepresentById): def __init__(self, user, notification): self.user = user + # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. + if object_session(user): + object_session(user).add(self) self.notification = notification From 5b8dba9aa4b0e42611f36ca446cb47b61d4459bf Mon Sep 17 00:00:00 2001 From: John Davis Date: Sat, 2 Dec 2023 13:15:22 -0500 Subject: [PATCH 11/43] Set cascade_backrefs=False on Quota.default, add safeguard --- lib/galaxy/model/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 75850fe221ae..33a695f4566f 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -3614,7 +3614,7 @@ class Quota(Base, Dictifiable, RepresentById): operation = Column(String(8)) deleted = Column(Boolean, index=True, default=False) quota_source_label = Column(String(32), default=None) - default = relationship("DefaultQuotaAssociation", back_populates="quota") + default = relationship("DefaultQuotaAssociation", back_populates="quota", cascade_backrefs=False) groups = relationship("GroupQuotaAssociation", back_populates="quota") users = relationship("UserQuotaAssociation", back_populates="quota") @@ -3684,6 +3684,9 @@ def __init__(self, type, quota): assert type in self.types.__members__.values(), "Invalid type" self.type = type self.quota = quota + # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. + if object_session(quota): + object_session(quota).add(self) class DatasetPermissions(Base, RepresentById): From b4f6a9cd2b82b80b319104b499a8e5acd3a2b84e Mon Sep 17 00:00:00 2001 From: John Davis Date: Sat, 2 Dec 2023 13:18:15 -0500 Subject: [PATCH 12/43] Set cascade_backrefs=False on LibraryDataset.actions, add safeguard --- lib/galaxy/model/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 33a695f4566f..9277f2096de0 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -3775,6 +3775,9 @@ def __init__(self, action, library_item, role): self.action = action if isinstance(library_item, LibraryDataset): self.library_dataset = library_item + # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. + if object_session(library_item): + object_session(library_item).add(self) else: raise Exception(f"Invalid LibraryDataset specified: {library_item.__class__.__name__}") self.role = role @@ -5642,7 +5645,7 @@ class LibraryDataset(Base, Serializable): viewonly=True, uselist=True, ) - actions = relationship("LibraryDatasetPermissions", back_populates="library_dataset") + actions = relationship("LibraryDatasetPermissions", back_populates="library_dataset", cascade_backrefs=False) # This class acts as a proxy to the currently selected LDDA upload_options = [ From 1465875f97dd2cf0262f4ff277a793c36a15aed7 Mon Sep 17 00:00:00 2001 From: John Davis Date: Sat, 2 Dec 2023 13:21:03 -0500 Subject: [PATCH 13/43] Set cascade_backrefs=False on GalaxySession.histories, add safeguard --- lib/galaxy/model/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 9277f2096de0..6c193995e822 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -7234,7 +7234,7 @@ class GalaxySession(Base, RepresentById): disk_usage = Column(Numeric(15, 0), index=True) last_action = Column(DateTime) current_history = relationship("History") - histories = relationship("GalaxySessionToHistoryAssociation", back_populates="galaxy_session") + histories = relationship("GalaxySessionToHistoryAssociation", back_populates="galaxy_session", cascade_backrefs=False) user = relationship("User", back_populates="galaxy_sessions") def __init__(self, is_valid=False, **kwd): @@ -7271,6 +7271,9 @@ class GalaxySessionToHistoryAssociation(Base, RepresentById): def __init__(self, galaxy_session, history): self.galaxy_session = galaxy_session + # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. + if object_session(galaxy_session): + object_session(galaxy_session).add(self) add_object_to_object_session(self, history) self.history = history From f5820d4d381c5a4c7b47f2f3e3186c622c9ed6bb Mon Sep 17 00:00:00 2001 From: John Davis Date: Sat, 2 Dec 2023 13:28:19 -0500 Subject: [PATCH 14/43] Set cascade_backrefs=False on History.workflow_invocations, add safeguards --- lib/galaxy/model/__init__.py | 2 +- lib/galaxy/model/store/__init__.py | 3 +++ lib/galaxy/workflow/run_request.py | 5 +++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 6c193995e822..f53a7a931dbb 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -2991,7 +2991,7 @@ class History(Base, HasTags, Dictifiable, UsesAnnotations, HasName, Serializable default_permissions = relationship("DefaultHistoryPermissions", back_populates="history") users_shared_with = relationship("HistoryUserShareAssociation", back_populates="history") galaxy_sessions = relationship("GalaxySessionToHistoryAssociation", back_populates="history") - workflow_invocations = relationship("WorkflowInvocation", back_populates="history") + workflow_invocations = relationship("WorkflowInvocation", back_populates="history", cascade_backrefs=False) user = relationship("User", back_populates="histories") jobs = relationship("Job", back_populates="history") diff --git a/lib/galaxy/model/store/__init__.py b/lib/galaxy/model/store/__init__.py index e18fa9ba71a5..ee4819a02f74 100644 --- a/lib/galaxy/model/store/__init__.py +++ b/lib/galaxy/model/store/__init__.py @@ -1024,6 +1024,9 @@ def _import_workflow_invocations( imported_invocation = model.WorkflowInvocation() imported_invocation.user = self.user imported_invocation.history = history + # Safeguard: imported_invocation was implicitly merged into this Session prior to SQLAlchemy 2.0. + if object_session(history): + object_session(history).add(imported_invocation) workflow_key = invocation_attrs["workflow"] if workflow_key not in object_import_tracker.workflows_by_key: raise Exception(f"Failed to find key {workflow_key} in {object_import_tracker.workflows_by_key.keys()}") diff --git a/lib/galaxy/workflow/run_request.py b/lib/galaxy/workflow/run_request.py index 8797b1fb8fe1..b6ce3070e549 100644 --- a/lib/galaxy/workflow/run_request.py +++ b/lib/galaxy/workflow/run_request.py @@ -9,6 +9,8 @@ TYPE_CHECKING, ) +from sqlalchemy.orm import object_session + from galaxy import exceptions from galaxy.model import ( EffectiveOutput, @@ -486,6 +488,9 @@ def workflow_run_config_to_request( workflow_invocation = WorkflowInvocation() workflow_invocation.uuid = uuid.uuid1() workflow_invocation.history = run_config.target_history + # Safeguard: workflow_invocation was implicitly merged into this Session prior to SQLAlchemy 2.0. + if run_config.target_history and object_session(run_config.target_history): + object_session(run_config.target_history).add(workflow_invocation) def add_parameter(name: str, value: str, type: WorkflowRequestInputParameter.types) -> None: parameter = WorkflowRequestInputParameter( From 98cf269bf57f4743c5c9a9c85f5b6f9d951289e1 Mon Sep 17 00:00:00 2001 From: John Davis Date: Sat, 2 Dec 2023 13:30:45 -0500 Subject: [PATCH 15/43] Set cascade_backrefs=False on WorflowInvocation.steps, add safeguards --- lib/galaxy/model/__init__.py | 1 + lib/galaxy/model/store/__init__.py | 3 +++ lib/galaxy/workflow/run.py | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index f53a7a931dbb..7c3ce283a274 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -8218,6 +8218,7 @@ class WorkflowInvocation(Base, UsesCreateAndUpdateTime, Dictifiable, Serializabl "WorkflowInvocationStep", back_populates="workflow_invocation", order_by=lambda: WorkflowInvocationStep.order_index, + cascade_backrefs=False, ) workflow: Workflow = relationship("Workflow") output_dataset_collections = relationship( diff --git a/lib/galaxy/model/store/__init__.py b/lib/galaxy/model/store/__init__.py index ee4819a02f74..c905ca62a317 100644 --- a/lib/galaxy/model/store/__init__.py +++ b/lib/galaxy/model/store/__init__.py @@ -1048,6 +1048,9 @@ def attach_workflow_step(imported_object, attrs): for step_attrs in invocation_attrs["steps"]: imported_invocation_step = model.WorkflowInvocationStep() imported_invocation_step.workflow_invocation = imported_invocation + # Safeguard: imported_invocation_step was implicitly merged into this Session prior to SQLAlchemy 2.0. + if object_session(imported_invocation): + object_session(imported_invocation).add(imported_invocation_step) attach_workflow_step(imported_invocation_step, step_attrs) restore_times(imported_invocation_step, step_attrs) imported_invocation_step.action = step_attrs["action"] diff --git a/lib/galaxy/workflow/run.py b/lib/galaxy/workflow/run.py index 776b4e525803..e50962b8a27f 100644 --- a/lib/galaxy/workflow/run.py +++ b/lib/galaxy/workflow/run.py @@ -10,6 +10,7 @@ Union, ) +from sqlalchemy.orm import object_session from typing_extensions import Protocol from galaxy import model @@ -225,6 +226,9 @@ def invoke(self) -> Dict[int, Any]: workflow_invocation_step = WorkflowInvocationStep() assert workflow_invocation_step workflow_invocation_step.workflow_invocation = workflow_invocation + # Safeguard: workflow_invocation_step was implicitly merged into this Session prior to SQLAlchemy 2.0. + if workflow_invocation and object_session(workflow_invocation): + object_session(workflow_invocation).add(workflow_invocation_step) workflow_invocation_step.workflow_step = step workflow_invocation_step.state = "new" From 015a62e1f3d18c5659f30dcc329e344620a88099 Mon Sep 17 00:00:00 2001 From: John Davis Date: Sat, 2 Dec 2023 13:33:26 -0500 Subject: [PATCH 16/43] Set cascade_backrefs=False on WorflowInvocation.input_parameters, add safeguard; lint --- lib/galaxy/model/__init__.py | 10 +++++++--- lib/galaxy/model/store/__init__.py | 8 +++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 7c3ce283a274..6342d97ea910 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -3513,7 +3513,7 @@ def __init__(self, group, role): self.group = group # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. if object_session(group): - object_session(group).add(self) + object_session(group).add(self) self.role = role @@ -7234,7 +7234,9 @@ class GalaxySession(Base, RepresentById): disk_usage = Column(Numeric(15, 0), index=True) last_action = Column(DateTime) current_history = relationship("History") - histories = relationship("GalaxySessionToHistoryAssociation", back_populates="galaxy_session", cascade_backrefs=False) + histories = relationship( + "GalaxySessionToHistoryAssociation", back_populates="galaxy_session", cascade_backrefs=False + ) user = relationship("User", back_populates="galaxy_sessions") def __init__(self, is_valid=False, **kwd): @@ -8199,7 +8201,9 @@ class WorkflowInvocation(Base, UsesCreateAndUpdateTime, Dictifiable, Serializabl history_id = Column(Integer, ForeignKey("history.id"), index=True) history = relationship("History", back_populates="workflow_invocations") - input_parameters = relationship("WorkflowRequestInputParameter", back_populates="workflow_invocation") + input_parameters = relationship( + "WorkflowRequestInputParameter", back_populates="workflow_invocation", cascade_backrefs=False + ) step_states = relationship("WorkflowRequestStepState", back_populates="workflow_invocation") input_step_parameters = relationship("WorkflowRequestInputStepParameter", back_populates="workflow_invocation") input_datasets = relationship("WorkflowRequestToInputDatasetAssociation", back_populates="workflow_invocation") diff --git a/lib/galaxy/model/store/__init__.py b/lib/galaxy/model/store/__init__.py index c905ca62a317..f416242ebe2c 100644 --- a/lib/galaxy/model/store/__init__.py +++ b/lib/galaxy/model/store/__init__.py @@ -40,7 +40,10 @@ ) from rocrate.rocrate import ROCrate from sqlalchemy import select -from sqlalchemy.orm import joinedload +from sqlalchemy.orm import ( + joinedload, + object_session, +) from sqlalchemy.orm.scoping import scoped_session from sqlalchemy.sql import expression from typing_extensions import Protocol @@ -1105,6 +1108,9 @@ def attach_workflow_step(imported_object, attrs): input_parameter.name = input_parameter_attrs["name"] input_parameter.type = input_parameter_attrs["type"] input_parameter.workflow_invocation = imported_invocation + # Safeguard: input_parameter was implicitly merged into this Session prior to SQLAlchemy 2.0. + if object_session(imported_invocation): + object_session(imported_invocation).add(input_parameter) self._session_add(input_parameter) input_parameters.append(input_parameter) From bec69fcaadbf326c81f4b996c8272875dba82c76 Mon Sep 17 00:00:00 2001 From: John Davis Date: Sat, 2 Dec 2023 13:47:04 -0500 Subject: [PATCH 17/43] Check if object is not None before checking its session --- lib/galaxy/model/__init__.py | 20 ++++++++++---------- lib/galaxy/model/store/__init__.py | 6 +++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 6342d97ea910..d1cc9361f97d 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -2466,7 +2466,7 @@ def __init__(self, action_type, workflow_step=None, output_name=None, action_arg self.action_arguments = action_arguments self.workflow_step = workflow_step # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. - if object_session(workflow_step): + if workflow_step and object_session(workflow_step): object_session(workflow_step).add(self) @@ -2483,7 +2483,7 @@ def __init__(self, pja, job=None, job_id=None): if job is not None: self.job = job # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. - if object_session(job): + if job and object_session(job): object_session(job).add(self) elif job_id is not None: self.job_id = job_id @@ -2856,7 +2856,7 @@ class UserNotificationAssociation(Base, RepresentById): def __init__(self, user, notification): self.user = user # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. - if object_session(user): + if user and object_session(user): object_session(user).add(self) self.notification = notification @@ -3512,7 +3512,7 @@ class GroupRoleAssociation(Base, RepresentById): def __init__(self, group, role): self.group = group # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. - if object_session(group): + if group and object_session(group): object_session(group).add(self) self.role = role @@ -3685,7 +3685,7 @@ def __init__(self, type, quota): self.type = type self.quota = quota # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. - if object_session(quota): + if quota and object_session(quota): object_session(quota).add(self) @@ -3728,7 +3728,7 @@ def __init__(self, action, library_item, role): if isinstance(library_item, Library): self.library = library_item # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. - if object_session(library_item): + if library_item and object_session(library_item): object_session(library_item).add(self) else: raise Exception(f"Invalid Library specified: {library_item.__class__.__name__}") @@ -3752,7 +3752,7 @@ def __init__(self, action, library_item, role): if isinstance(library_item, LibraryFolder): self.folder = library_item # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. - if object_session(library_item): + if library_item and object_session(library_item): object_session(library_item).add(self) else: raise Exception(f"Invalid LibraryFolder specified: {library_item.__class__.__name__}") @@ -3776,7 +3776,7 @@ def __init__(self, action, library_item, role): if isinstance(library_item, LibraryDataset): self.library_dataset = library_item # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. - if object_session(library_item): + if library_item and object_session(library_item): object_session(library_item).add(self) else: raise Exception(f"Invalid LibraryDataset specified: {library_item.__class__.__name__}") @@ -4401,7 +4401,7 @@ def __init__( self.dataset = dataset # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. - if object_session(dataset): + if dataset and object_session(dataset): object_session(dataset).add(self) self.parent_id = parent_id @@ -7274,7 +7274,7 @@ class GalaxySessionToHistoryAssociation(Base, RepresentById): def __init__(self, galaxy_session, history): self.galaxy_session = galaxy_session # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. - if object_session(galaxy_session): + if galaxy_session and object_session(galaxy_session): object_session(galaxy_session).add(self) add_object_to_object_session(self, history) self.history = history diff --git a/lib/galaxy/model/store/__init__.py b/lib/galaxy/model/store/__init__.py index f416242ebe2c..a8bbfabcd335 100644 --- a/lib/galaxy/model/store/__init__.py +++ b/lib/galaxy/model/store/__init__.py @@ -1028,7 +1028,7 @@ def _import_workflow_invocations( imported_invocation.user = self.user imported_invocation.history = history # Safeguard: imported_invocation was implicitly merged into this Session prior to SQLAlchemy 2.0. - if object_session(history): + if history and object_session(history): object_session(history).add(imported_invocation) workflow_key = invocation_attrs["workflow"] if workflow_key not in object_import_tracker.workflows_by_key: @@ -1052,7 +1052,7 @@ def attach_workflow_step(imported_object, attrs): imported_invocation_step = model.WorkflowInvocationStep() imported_invocation_step.workflow_invocation = imported_invocation # Safeguard: imported_invocation_step was implicitly merged into this Session prior to SQLAlchemy 2.0. - if object_session(imported_invocation): + if imported_invocation and object_session(imported_invocation): object_session(imported_invocation).add(imported_invocation_step) attach_workflow_step(imported_invocation_step, step_attrs) restore_times(imported_invocation_step, step_attrs) @@ -1109,7 +1109,7 @@ def attach_workflow_step(imported_object, attrs): input_parameter.type = input_parameter_attrs["type"] input_parameter.workflow_invocation = imported_invocation # Safeguard: input_parameter was implicitly merged into this Session prior to SQLAlchemy 2.0. - if object_session(imported_invocation): + if imported_invocation and object_session(imported_invocation): object_session(imported_invocation).add(input_parameter) self._session_add(input_parameter) input_parameters.append(input_parameter) From ccb51e4a91ea550e5614a14bab09b2d0953496e0 Mon Sep 17 00:00:00 2001 From: John Davis Date: Sun, 3 Dec 2023 11:09:51 -0500 Subject: [PATCH 18/43] Set cascade_backrefs=Flase on WorkflowInvocation.step_states, add safeguard --- lib/galaxy/model/__init__.py | 2 +- lib/galaxy/model/store/__init__.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index d1cc9361f97d..f060a2572001 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -8204,7 +8204,7 @@ class WorkflowInvocation(Base, UsesCreateAndUpdateTime, Dictifiable, Serializabl input_parameters = relationship( "WorkflowRequestInputParameter", back_populates="workflow_invocation", cascade_backrefs=False ) - step_states = relationship("WorkflowRequestStepState", back_populates="workflow_invocation") + step_states = relationship("WorkflowRequestStepState", back_populates="workflow_invocation", cascade_backrefs=False) input_step_parameters = relationship("WorkflowRequestInputStepParameter", back_populates="workflow_invocation") input_datasets = relationship("WorkflowRequestToInputDatasetAssociation", back_populates="workflow_invocation") input_dataset_collections = relationship( diff --git a/lib/galaxy/model/store/__init__.py b/lib/galaxy/model/store/__init__.py index a8bbfabcd335..c1b283ff2648 100644 --- a/lib/galaxy/model/store/__init__.py +++ b/lib/galaxy/model/store/__init__.py @@ -1122,6 +1122,8 @@ def attach_workflow_step(imported_object, attrs): step_state.value = step_state_attrs["value"] attach_workflow_step(step_state, step_state_attrs) step_state.workflow_invocation = imported_invocation + # Safeguard: step_state was implicitly merged into this Session prior to SQLAlchemy 2.0. + self.sa_session.add(step_state) self._session_add(step_state) step_states.append(step_state) From 819dce604902f6ffe05669307df4302fa79d6c64 Mon Sep 17 00:00:00 2001 From: John Davis Date: Sun, 3 Dec 2023 11:32:30 -0500 Subject: [PATCH 19/43] Set cascade_backrefs=False on WorflowInvocation.input_datasets, add safeguard. --- lib/galaxy/model/__init__.py | 4 +++- lib/galaxy/model/store/__init__.py | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index f060a2572001..a778ac4341ba 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -8206,7 +8206,9 @@ class WorkflowInvocation(Base, UsesCreateAndUpdateTime, Dictifiable, Serializabl ) step_states = relationship("WorkflowRequestStepState", back_populates="workflow_invocation", cascade_backrefs=False) input_step_parameters = relationship("WorkflowRequestInputStepParameter", back_populates="workflow_invocation") - input_datasets = relationship("WorkflowRequestToInputDatasetAssociation", back_populates="workflow_invocation") + input_datasets = relationship( + "WorkflowRequestToInputDatasetAssociation", back_populates="workflow_invocation", cascade_backrefs=False + ) input_dataset_collections = relationship( "WorkflowRequestToInputDatasetCollectionAssociation", back_populates="workflow_invocation" ) diff --git a/lib/galaxy/model/store/__init__.py b/lib/galaxy/model/store/__init__.py index c1b283ff2648..5624810647c0 100644 --- a/lib/galaxy/model/store/__init__.py +++ b/lib/galaxy/model/store/__init__.py @@ -1141,6 +1141,8 @@ def attach_workflow_step(imported_object, attrs): input_dataset = model.WorkflowRequestToInputDatasetAssociation() attach_workflow_step(input_dataset, input_dataset_attrs) input_dataset.workflow_invocation = imported_invocation + # Safeguard: input_dataset was implicitly merged into this Session prior to SQLAlchemy 2.0. + self.sa_session.add(input_dataset) input_dataset.name = input_dataset_attrs["name"] dataset_link_attrs = input_dataset_attrs["dataset"] if dataset_link_attrs: From e47c56155988d445bdd776f9554ae50c68559e0f Mon Sep 17 00:00:00 2001 From: John Davis Date: Sun, 3 Dec 2023 11:35:18 -0500 Subject: [PATCH 20/43] Simplify logic: remove unnecessary checks --- lib/galaxy/model/store/__init__.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/galaxy/model/store/__init__.py b/lib/galaxy/model/store/__init__.py index 5624810647c0..b03777fabcc4 100644 --- a/lib/galaxy/model/store/__init__.py +++ b/lib/galaxy/model/store/__init__.py @@ -1052,8 +1052,7 @@ def attach_workflow_step(imported_object, attrs): imported_invocation_step = model.WorkflowInvocationStep() imported_invocation_step.workflow_invocation = imported_invocation # Safeguard: imported_invocation_step was implicitly merged into this Session prior to SQLAlchemy 2.0. - if imported_invocation and object_session(imported_invocation): - object_session(imported_invocation).add(imported_invocation_step) + self.sa_session.add(imported_invocation_step) attach_workflow_step(imported_invocation_step, step_attrs) restore_times(imported_invocation_step, step_attrs) imported_invocation_step.action = step_attrs["action"] @@ -1109,8 +1108,7 @@ def attach_workflow_step(imported_object, attrs): input_parameter.type = input_parameter_attrs["type"] input_parameter.workflow_invocation = imported_invocation # Safeguard: input_parameter was implicitly merged into this Session prior to SQLAlchemy 2.0. - if imported_invocation and object_session(imported_invocation): - object_session(imported_invocation).add(input_parameter) + self.sa_session.add(input_parameter) self._session_add(input_parameter) input_parameters.append(input_parameter) From f762f26bc4c64dcec8822c74e502ead956dc1ef9 Mon Sep 17 00:00:00 2001 From: John Davis Date: Sun, 3 Dec 2023 11:41:26 -0500 Subject: [PATCH 21/43] Set cascade_backrefs=False on WorflowInvocation.input/output_datasets_collections, add safeguards --- lib/galaxy/model/__init__.py | 10 ++++++++-- lib/galaxy/model/store/__init__.py | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index a778ac4341ba..53cbd0e48007 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -8210,7 +8210,9 @@ class WorkflowInvocation(Base, UsesCreateAndUpdateTime, Dictifiable, Serializabl "WorkflowRequestToInputDatasetAssociation", back_populates="workflow_invocation", cascade_backrefs=False ) input_dataset_collections = relationship( - "WorkflowRequestToInputDatasetCollectionAssociation", back_populates="workflow_invocation" + "WorkflowRequestToInputDatasetCollectionAssociation", + back_populates="workflow_invocation", + cascade_backrefs=False, ) subworkflow_invocations = relationship( "WorkflowInvocationToSubworkflowInvocationAssociation", @@ -8228,7 +8230,9 @@ class WorkflowInvocation(Base, UsesCreateAndUpdateTime, Dictifiable, Serializabl ) workflow: Workflow = relationship("Workflow") output_dataset_collections = relationship( - "WorkflowInvocationOutputDatasetCollectionAssociation", back_populates="workflow_invocation" + "WorkflowInvocationOutputDatasetCollectionAssociation", + back_populates="workflow_invocation", + cascade_backrefs=False, ) output_datasets = relationship("WorkflowInvocationOutputDatasetAssociation", back_populates="workflow_invocation") output_values = relationship("WorkflowInvocationOutputValue", back_populates="workflow_invocation") @@ -8454,6 +8458,8 @@ def add_output(self, workflow_output, step, output_object): elif output_object.history_content_type == "dataset_collection": output_assoc = WorkflowInvocationOutputDatasetCollectionAssociation() output_assoc.workflow_invocation = self + if object_session(self): + object_session(self).add(output_assoc) output_assoc.workflow_output = workflow_output output_assoc.workflow_step = step output_assoc.dataset_collection = output_object diff --git a/lib/galaxy/model/store/__init__.py b/lib/galaxy/model/store/__init__.py index b03777fabcc4..9ef5a82b9d2e 100644 --- a/lib/galaxy/model/store/__init__.py +++ b/lib/galaxy/model/store/__init__.py @@ -1155,6 +1155,8 @@ def attach_workflow_step(imported_object, attrs): input_dataset_collection = model.WorkflowRequestToInputDatasetCollectionAssociation() attach_workflow_step(input_dataset_collection, input_dataset_collection_attrs) input_dataset_collection.workflow_invocation = imported_invocation + # Safeguard: input_dataset_collection was implicitly merged into this Session prior to SQLAlchemy 2.0. + self.sa_session.add(input_dataset_collection) input_dataset_collection.name = input_dataset_collection_attrs["name"] dataset_collection_link_attrs = input_dataset_collection_attrs["dataset_collection"] if dataset_collection_link_attrs: @@ -1169,6 +1171,8 @@ def attach_workflow_step(imported_object, attrs): for output_dataset_collection_attrs in invocation_attrs["output_dataset_collections"]: output_dataset_collection = model.WorkflowInvocationOutputDatasetCollectionAssociation() output_dataset_collection.workflow_invocation = imported_invocation + # Safeguard: output_dataset_collection was implicitly merged into this Session prior to SQLAlchemy 2.0. + self.sa_session.add(output_dataset_collection) attach_workflow_step(output_dataset_collection, output_dataset_collection_attrs) workflow_output = output_dataset_collection_attrs["workflow_output"] label = workflow_output.get("label") From ed9c72b906722349a7dba4be957b30b807f64e17 Mon Sep 17 00:00:00 2001 From: John Davis Date: Sun, 3 Dec 2023 11:49:33 -0500 Subject: [PATCH 22/43] Set cascade_backrefs=False on WorflowInvocation.output_datasets, add safeguards --- lib/galaxy/model/__init__.py | 7 ++++++- lib/galaxy/model/store/__init__.py | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 53cbd0e48007..f4dacac50e65 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -8234,7 +8234,9 @@ class WorkflowInvocation(Base, UsesCreateAndUpdateTime, Dictifiable, Serializabl back_populates="workflow_invocation", cascade_backrefs=False, ) - output_datasets = relationship("WorkflowInvocationOutputDatasetAssociation", back_populates="workflow_invocation") + output_datasets = relationship( + "WorkflowInvocationOutputDatasetAssociation", back_populates="workflow_invocation", cascade_backrefs=False + ) output_values = relationship("WorkflowInvocationOutputValue", back_populates="workflow_invocation") messages = relationship("WorkflowInvocationMessage", back_populates="workflow_invocation") @@ -8451,6 +8453,9 @@ def add_output(self, workflow_output, step, output_object): elif output_object.history_content_type == "dataset": output_assoc = WorkflowInvocationOutputDatasetAssociation() output_assoc.workflow_invocation = self + # Safeguard: output_assoc was implicitly merged into this Session prior to SQLAlchemy 2.0. + if object_session(self): + object_session(self).add(output_assoc) output_assoc.workflow_output = workflow_output output_assoc.workflow_step = step output_assoc.dataset = output_object diff --git a/lib/galaxy/model/store/__init__.py b/lib/galaxy/model/store/__init__.py index 9ef5a82b9d2e..6f014568a490 100644 --- a/lib/galaxy/model/store/__init__.py +++ b/lib/galaxy/model/store/__init__.py @@ -1185,6 +1185,8 @@ def attach_workflow_step(imported_object, attrs): for output_dataset_attrs in invocation_attrs["output_datasets"]: output_dataset = model.WorkflowInvocationOutputDatasetAssociation() output_dataset.workflow_invocation = imported_invocation + # Safeguard: output_dataset was implicitly merged into this Session prior to SQLAlchemy 2.0. + self.sa_session.add(output_dataset) attach_workflow_step(output_dataset, output_dataset_attrs) workflow_output = output_dataset_attrs["workflow_output"] label = workflow_output.get("label") From ae82102e652c2800ed56a16f331710fb04db80f5 Mon Sep 17 00:00:00 2001 From: John Davis Date: Sun, 3 Dec 2023 11:50:48 -0500 Subject: [PATCH 23/43] Set cascade_backrefs=False on WorflowInvocation.output_values, add safeguards --- lib/galaxy/model/__init__.py | 7 ++++++- lib/galaxy/model/store/__init__.py | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index f4dacac50e65..51cf6a2344e6 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -8237,7 +8237,9 @@ class WorkflowInvocation(Base, UsesCreateAndUpdateTime, Dictifiable, Serializabl output_datasets = relationship( "WorkflowInvocationOutputDatasetAssociation", back_populates="workflow_invocation", cascade_backrefs=False ) - output_values = relationship("WorkflowInvocationOutputValue", back_populates="workflow_invocation") + output_values = relationship( + "WorkflowInvocationOutputValue", back_populates="workflow_invocation", cascade_backrefs=False + ) messages = relationship("WorkflowInvocationMessage", back_populates="workflow_invocation") dict_collection_visible_keys = [ @@ -8446,6 +8448,9 @@ def add_output(self, workflow_output, step, output_object): # dispatch on actual object and not step type. output_assoc = WorkflowInvocationOutputValue() output_assoc.workflow_invocation = self + # Safeguard: output_assoc was implicitly merged into this Session prior to SQLAlchemy 2.0. + if object_session(self): + object_session(self).add(output_assoc) output_assoc.workflow_output = workflow_output output_assoc.workflow_step = step output_assoc.value = output_object diff --git a/lib/galaxy/model/store/__init__.py b/lib/galaxy/model/store/__init__.py index 6f014568a490..00f04b426afa 100644 --- a/lib/galaxy/model/store/__init__.py +++ b/lib/galaxy/model/store/__init__.py @@ -1199,6 +1199,8 @@ def attach_workflow_step(imported_object, attrs): for output_value_attrs in invocation_attrs["output_values"]: output_value = model.WorkflowInvocationOutputValue() output_value.workflow_invocation = imported_invocation + # Safeguard: output_value was implicitly merged into this Session prior to SQLAlchemy 2.0. + self.sa_session.add(output_value) output_value.value = output_value_attrs["value"] attach_workflow_step(output_value, output_value_attrs) workflow_output = output_value_attrs["workflow_output"] From 5ee7287fd0e2713ab6ea7900af8e2508d4160ba7 Mon Sep 17 00:00:00 2001 From: John Davis Date: Mon, 4 Dec 2023 10:54:48 -0500 Subject: [PATCH 24/43] Set cascade_backrefs=False on Job.data_manager_association --- lib/galaxy/model/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 51cf6a2344e6..beb9b1e284f1 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -1345,7 +1345,9 @@ class Job(Base, JobLike, UsesCreateAndUpdateTime, Dictifiable, Serializable): "ImplicitCollectionJobsJobAssociation", back_populates="job", uselist=False ) container = relationship("JobContainerAssociation", back_populates="job", uselist=False) - data_manager_association = relationship("DataManagerJobAssociation", back_populates="job", uselist=False) + data_manager_association = relationship( + "DataManagerJobAssociation", back_populates="job", uselist=False, cascade_backrefs=False + ) history_dataset_collection_associations = relationship("HistoryDatasetCollectionAssociation", back_populates="job") workflow_invocation_step = relationship("WorkflowInvocationStep", back_populates="job", uselist=False) From ecbbb56da71ade396727a55a4ccd9b7db84a7a72 Mon Sep 17 00:00:00 2001 From: John Davis Date: Mon, 4 Dec 2023 10:57:41 -0500 Subject: [PATCH 25/43] Set cascade_backrefs=False on ImiplicitCollectionJobs.jobs --- lib/galaxy/model/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index beb9b1e284f1..1c431e362d55 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -2413,7 +2413,9 @@ class ImplicitCollectionJobs(Base, Serializable): id = Column(Integer, primary_key=True) populated_state = Column(TrimmedString(64), default="new", nullable=False) - jobs = relationship("ImplicitCollectionJobsJobAssociation", back_populates="implicit_collection_jobs") + jobs = relationship( + "ImplicitCollectionJobsJobAssociation", back_populates="implicit_collection_jobs", cascade_backrefs=False + ) class populated_states(str, Enum): NEW = "new" # New implicit jobs object, unpopulated job associations From 1b8c68b08a834bffe62bd5338c7a3f08aa9ce9a6 Mon Sep 17 00:00:00 2001 From: John Davis Date: Mon, 4 Dec 2023 11:20:09 -0500 Subject: [PATCH 26/43] Set cascade_backrefs=False on User.galaxy_sessions, add safeguard --- lib/galaxy/model/__init__.py | 2 +- lib/galaxy/webapps/base/webapp.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 1c431e362d55..0188584fde42 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -704,7 +704,7 @@ class User(Base, Dictifiable, RepresentById): order_by=lambda: desc(History.update_time), # type: ignore[has-type] ) galaxy_sessions = relationship( - "GalaxySession", back_populates="user", order_by=lambda: desc(GalaxySession.update_time) # type: ignore[has-type] + "GalaxySession", back_populates="user", order_by=lambda: desc(GalaxySession.update_time), cascade_backrefs=False # type: ignore[has-type] ) quotas = relationship("UserQuotaAssociation", back_populates="user") quota_source_usages = relationship("UserQuotaSourceUsage", back_populates="user") diff --git a/lib/galaxy/webapps/base/webapp.py b/lib/galaxy/webapps/base/webapp.py index 97bd22c768d1..d9766abf3fea 100644 --- a/lib/galaxy/webapps/base/webapp.py +++ b/lib/galaxy/webapps/base/webapp.py @@ -24,6 +24,7 @@ select, true, ) +from sqlalchemy.orm import object_session from sqlalchemy.orm.exc import NoResultFound from galaxy import util @@ -1119,6 +1120,9 @@ def create_new_session(trans, prev_galaxy_session=None, user_for_new_session=Non if user_for_new_session: # The new session should be associated with the user galaxy_session.user = user_for_new_session + # Safeguard: galaxy_session was implicitly merged into this Session prior to SQLAlchemy 2.0. + if user_for_new_session and object_session(user_for_new_session): + object_session(user_for_new_session).add(galaxy_session) return galaxy_session From 040f31513a067ab33b8ed74bd6f3e6754c9322cb Mon Sep 17 00:00:00 2001 From: John Davis Date: Mon, 4 Dec 2023 11:24:08 -0500 Subject: [PATCH 27/43] Set cascade_backrefs=False on User.addresses --- lib/galaxy/model/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 0188584fde42..250154331039 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -689,7 +689,9 @@ class User(Base, Dictifiable, RepresentById): active = Column(Boolean, index=True, default=True, nullable=False) activation_token = Column(TrimmedString(64), nullable=True, index=True) - addresses = relationship("UserAddress", back_populates="user", order_by=lambda: desc(UserAddress.update_time)) + addresses = relationship( + "UserAddress", back_populates="user", order_by=lambda: desc(UserAddress.update_time), cascade_backrefs=False + ) cloudauthz = relationship("CloudAuthz", back_populates="user") custos_auth = relationship("CustosAuthnzToken", back_populates="user") default_permissions = relationship("DefaultUserPermissions", back_populates="user") From 3e70f587eab4d45b2fab3032a1985a177f5ac572 Mon Sep 17 00:00:00 2001 From: John Davis Date: Mon, 4 Dec 2023 11:26:13 -0500 Subject: [PATCH 28/43] Set cascade_backrefs=False on StoredWorkflow.workflows, add safeguard. --- lib/galaxy/model/__init__.py | 1 + lib/galaxy/workflow/extract.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 250154331039..ed1054202e94 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -7328,6 +7328,7 @@ class StoredWorkflow(Base, HasTags, Dictifiable, RepresentById): cascade="all, delete-orphan", primaryjoin=(lambda: StoredWorkflow.id == Workflow.stored_workflow_id), # type: ignore[has-type] order_by=lambda: -Workflow.id, # type: ignore[has-type] + cascade_backrefs=False, ) latest_workflow = relationship( "Workflow", diff --git a/lib/galaxy/workflow/extract.py b/lib/galaxy/workflow/extract.py index 199c096e1133..064b15f7efd9 100644 --- a/lib/galaxy/workflow/extract.py +++ b/lib/galaxy/workflow/extract.py @@ -71,6 +71,8 @@ def extract_workflow( workflow.stored_workflow = stored stored.latest_workflow = workflow trans.sa_session.add(stored) + # Safeguard: workflow was implicitly merged into this Session prior to SQLAlchemy 2.0. + trans.sa_session.add(workflow) with transaction(trans.sa_session): trans.sa_session.commit() return stored From a21cc2d1cd87f45fd0c70fcc662d6ac09f85d1f0 Mon Sep 17 00:00:00 2001 From: John Davis Date: Mon, 4 Dec 2023 11:28:35 -0500 Subject: [PATCH 29/43] Set cascade_backrefs=False on User.histories --- lib/galaxy/model/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index ed1054202e94..24dc01bf3c61 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -697,7 +697,7 @@ class User(Base, Dictifiable, RepresentById): default_permissions = relationship("DefaultUserPermissions", back_populates="user") groups = relationship("UserGroupAssociation", back_populates="user") histories = relationship( - "History", back_populates="user", order_by=lambda: desc(History.update_time) # type: ignore[has-type] + "History", back_populates="user", order_by=lambda: desc(History.update_time), cascade_backrefs=False # type: ignore[has-type] ) active_histories = relationship( "History", From 1a78d9c0fd9e09d1653d4252d7edebb124020e69 Mon Sep 17 00:00:00 2001 From: John Davis Date: Mon, 4 Dec 2023 11:40:16 -0500 Subject: [PATCH 30/43] Set cascade_backrefs=False on Workflow.parent_workflow_steps, add safeguard --- lib/galaxy/model/__init__.py | 1 + lib/galaxy/workflow/modules.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 24dc01bf3c61..1080de81a2dc 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -7497,6 +7497,7 @@ class Workflow(Base, Dictifiable, RepresentById): "WorkflowStep", primaryjoin=(lambda: Workflow.id == WorkflowStep.subworkflow_id), # type: ignore[has-type] back_populates="subworkflow", + cascade_backrefs=False, ) stored_workflow = relationship( "StoredWorkflow", diff --git a/lib/galaxy/workflow/modules.py b/lib/galaxy/workflow/modules.py index b0360fc893c8..9a2240b2dd67 100644 --- a/lib/galaxy/workflow/modules.py +++ b/lib/galaxy/workflow/modules.py @@ -18,6 +18,7 @@ ) from cwl_utils.expression import do_eval +from sqlalchemy.orm import object_session from typing_extensions import TypedDict from galaxy import ( @@ -644,6 +645,9 @@ def from_workflow_step(Class, trans, step, **kwds): def save_to_step(self, step, **kwd): step.type = self.type step.subworkflow = self.subworkflow + # Safeguard: step was implicitly merged into this Session prior to SQLAlchemy 2.0. + if self.subworkflow and object_session(self.subworkflow): + object_session(self.subworkflow).add(step) def get_name(self): if hasattr(self.subworkflow, "name"): From 2fed1313e54769486091465813790ed9ec552c01 Mon Sep 17 00:00:00 2001 From: John Davis Date: Mon, 4 Dec 2023 14:30:39 -0500 Subject: [PATCH 31/43] Set cascade_backrefs=False on WorkflowStepInput.connections, add safeguard --- lib/galaxy/model/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 1080de81a2dc..f171d104e7e7 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -7790,6 +7790,9 @@ def add_connection(self, input_name, output_name, output_step, input_subworkflow conn = WorkflowStepConnection() conn.input_step_input = step_input + # Safeguard: conn was implicitly merged into this Session prior to SQLAlchemy 2.0. + if step_input and object_session(step_input): + object_session(step_input).add(conn) conn.output_name = output_name add_object_to_object_session(conn, output_step) conn.output_step = output_step @@ -7979,6 +7982,7 @@ class WorkflowStepInput(Base, RepresentById): "WorkflowStepConnection", back_populates="input_step_input", primaryjoin=(lambda: WorkflowStepConnection.input_step_input_id == WorkflowStepInput.id), + cascade_backrefs=False, ) def __init__(self, workflow_step): From 657ae1750e4b865ec9509b398973d41fcd553fa0 Mon Sep 17 00:00:00 2001 From: John Davis Date: Mon, 4 Dec 2023 14:54:45 -0500 Subject: [PATCH 32/43] Set cascade_backrefs=False on WorkflowInvocationStep.output_datasets, add safeguard --- lib/galaxy/model/__init__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index f171d104e7e7..0a677be0fc3e 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -8876,7 +8876,9 @@ class WorkflowInvocationStep(Base, Dictifiable, Serializable): "WorkflowInvocationStepOutputDatasetCollectionAssociation", back_populates="workflow_invocation_step" ) output_datasets = relationship( - "WorkflowInvocationStepOutputDatasetAssociation", back_populates="workflow_invocation_step" + "WorkflowInvocationStepOutputDatasetAssociation", + back_populates="workflow_invocation_step", + cascade_backrefs=False, ) workflow_invocation = relationship("WorkflowInvocation", back_populates="steps") output_value = relationship( @@ -8931,6 +8933,9 @@ def add_output(self, output_name, output_object): if output_object.history_content_type == "dataset": output_assoc = WorkflowInvocationStepOutputDatasetAssociation() output_assoc.workflow_invocation_step = self + # Safeguard: output_assoc was implicitly merged into this Session prior to SQLAlchemy 2.0. + if object_session(self): + object_session(self).add(output_assoc) output_assoc.dataset = output_object output_assoc.output_name = output_name self.output_datasets.append(output_assoc) From 855a0c1b3beb200366d1f6af8c6886857148ba9c Mon Sep 17 00:00:00 2001 From: John Davis Date: Mon, 4 Dec 2023 15:02:28 -0500 Subject: [PATCH 33/43] Set cascade_backrefs=False on WorkflowInvocationStep.output_dataset_collections, add safeguard --- lib/galaxy/model/__init__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 0a677be0fc3e..316f188f1af0 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -8873,7 +8873,9 @@ class WorkflowInvocationStep(Base, Dictifiable, Serializable): job = relationship("Job", back_populates="workflow_invocation_step", uselist=False) implicit_collection_jobs = relationship("ImplicitCollectionJobs", uselist=False) output_dataset_collections = relationship( - "WorkflowInvocationStepOutputDatasetCollectionAssociation", back_populates="workflow_invocation_step" + "WorkflowInvocationStepOutputDatasetCollectionAssociation", + back_populates="workflow_invocation_step", + cascade_backrefs=False, ) output_datasets = relationship( "WorkflowInvocationStepOutputDatasetAssociation", @@ -8942,6 +8944,9 @@ def add_output(self, output_name, output_object): elif output_object.history_content_type == "dataset_collection": output_assoc = WorkflowInvocationStepOutputDatasetCollectionAssociation() output_assoc.workflow_invocation_step = self + # Safeguard: output_assoc was implicitly merged into this Session prior to SQLAlchemy 2.0. + if object_session(self): + object_session(self).add(output_assoc) output_assoc.dataset_collection = output_object output_assoc.output_name = output_name self.output_dataset_collections.append(output_assoc) From 5abd3e2b709aa590d15a7c3cfbcf934a08b17a93 Mon Sep 17 00:00:00 2001 From: John Davis Date: Mon, 4 Dec 2023 15:05:23 -0500 Subject: [PATCH 34/43] Set cascade_backrefs=False on WorkflowStep.workflow_outputs, add safeguard --- lib/galaxy/model/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 316f188f1af0..fd75e87931eb 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -7688,7 +7688,7 @@ class WorkflowStep(Base, RepresentById): ) post_job_actions = relationship("PostJobAction", back_populates="workflow_step", cascade_backrefs=False) inputs = relationship("WorkflowStepInput", back_populates="workflow_step") - workflow_outputs = relationship("WorkflowOutput", back_populates="workflow_step") + workflow_outputs = relationship("WorkflowOutput", back_populates="workflow_step", cascade_backrefs=False) output_connections = relationship( "WorkflowStepConnection", primaryjoin=(lambda: WorkflowStepConnection.output_step_id == WorkflowStep.id) ) @@ -8074,6 +8074,9 @@ class WorkflowOutput(Base, Serializable): def __init__(self, workflow_step, output_name=None, label=None, uuid=None): self.workflow_step = workflow_step + # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. + if object_session(workflow_step): + object_session(workflow_step).add(self) self.output_name = output_name self.label = label self.uuid = get_uuid(uuid) From d275234eb1e51a76f08984a1f37e97fd425ba034 Mon Sep 17 00:00:00 2001 From: John Davis Date: Mon, 4 Dec 2023 15:41:24 -0500 Subject: [PATCH 35/43] Set cascade_backrefs=False on History.jobs, add safeguard --- lib/galaxy/model/__init__.py | 2 +- test/integration/test_job_files.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index fd75e87931eb..32550f11fd93 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -2999,7 +2999,7 @@ class History(Base, HasTags, Dictifiable, UsesAnnotations, HasName, Serializable galaxy_sessions = relationship("GalaxySessionToHistoryAssociation", back_populates="history") workflow_invocations = relationship("WorkflowInvocation", back_populates="history", cascade_backrefs=False) user = relationship("User", back_populates="histories") - jobs = relationship("Job", back_populates="history") + jobs = relationship("Job", back_populates="history", cascade_backrefs=False) update_time = column_property( select(func.max(HistoryAudit.update_time)).where(HistoryAudit.history_id == id).scalar_subquery(), diff --git a/test/integration/test_job_files.py b/test/integration/test_job_files.py index f45cd6a722e1..f9f6cfaae540 100644 --- a/test/integration/test_job_files.py +++ b/test/integration/test_job_files.py @@ -20,6 +20,7 @@ import requests from sqlalchemy import select +from sqlalchemy.orm import object_session from galaxy import model from galaxy.model.base import transaction @@ -138,6 +139,8 @@ def create_static_job_with_state(self, state): sa_session.commit() job = model.Job() job.history = history + # Safeguard: job was implicitly merged into this Session prior to SQLAlchemy 2.0. + object_session(history).add(job) job.user = user job.handler = "unknown-handler" job.state = state From e52e95f4f244d2b697726e510de102126a3e712c Mon Sep 17 00:00:00 2001 From: John Davis Date: Tue, 5 Dec 2023 08:35:20 -0500 Subject: [PATCH 36/43] Set cascade_backrefs=False on LibraryFolder.actions --- lib/galaxy/model/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 32550f11fd93..6381ed97a1f1 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -5526,7 +5526,7 @@ class LibraryFolder(Base, Dictifiable, HasName, Serializable): ) library_root = relationship("Library", back_populates="root_folder") - actions = relationship("LibraryFolderPermissions", back_populates="folder") + actions = relationship("LibraryFolderPermissions", back_populates="folder", cascade_backrefs=False) dict_element_visible_keys = [ "id", From 59b9dd17be31f06d653db2a27926b705982b7ba4 Mon Sep 17 00:00:00 2001 From: John Davis Date: Tue, 5 Dec 2023 08:40:17 -0500 Subject: [PATCH 37/43] Set cascade_backrefs=False on Job.implicit_collection_jobs_association --- lib/galaxy/model/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 6381ed97a1f1..c690a7d5e39e 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -1344,7 +1344,7 @@ class Job(Base, JobLike, UsesCreateAndUpdateTime, Dictifiable, Serializable): numeric_metrics = relationship("JobMetricNumeric") interactivetool_entry_points = relationship("InteractiveToolEntryPoint", back_populates="job", uselist=True) implicit_collection_jobs_association = relationship( - "ImplicitCollectionJobsJobAssociation", back_populates="job", uselist=False + "ImplicitCollectionJobsJobAssociation", back_populates="job", uselist=False, cascade_backrefs=False ) container = relationship("JobContainerAssociation", back_populates="job", uselist=False) data_manager_association = relationship( From 18b7d01cca4563119d686e173856a8d4a034e0e3 Mon Sep 17 00:00:00 2001 From: John Davis Date: Tue, 5 Dec 2023 08:47:08 -0500 Subject: [PATCH 38/43] Set cascade_backrefs=False on WorkflowInvocation.input_step_parameters --- lib/galaxy/model/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index c690a7d5e39e..2594af7f9ded 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -8220,7 +8220,9 @@ class WorkflowInvocation(Base, UsesCreateAndUpdateTime, Dictifiable, Serializabl "WorkflowRequestInputParameter", back_populates="workflow_invocation", cascade_backrefs=False ) step_states = relationship("WorkflowRequestStepState", back_populates="workflow_invocation", cascade_backrefs=False) - input_step_parameters = relationship("WorkflowRequestInputStepParameter", back_populates="workflow_invocation") + input_step_parameters = relationship( + "WorkflowRequestInputStepParameter", back_populates="workflow_invocation", cascade_backrefs=False + ) input_datasets = relationship( "WorkflowRequestToInputDatasetAssociation", back_populates="workflow_invocation", cascade_backrefs=False ) From 303f70e1ef942434d4c921b67cfefb124c2cf40e Mon Sep 17 00:00:00 2001 From: John Davis Date: Tue, 5 Dec 2023 09:01:43 -0500 Subject: [PATCH 39/43] Set cascade_backrefs=False on WorkflowStep.workflow, add safeguard We loop over steps to determine if at least one of them has been added to the session. If so, we add the workflow. --- lib/galaxy/managers/workflows.py | 7 +++++++ lib/galaxy/model/__init__.py | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/managers/workflows.py b/lib/galaxy/managers/workflows.py index 4ca49ca19b02..37c7a748981f 100644 --- a/lib/galaxy/managers/workflows.py +++ b/lib/galaxy/managers/workflows.py @@ -35,6 +35,7 @@ from sqlalchemy.orm import ( aliased, joinedload, + object_session, Query, subqueryload, ) @@ -818,6 +819,12 @@ def _workflow_from_raw_description( workflow.has_cycles = True workflow.steps = steps + # Safeguard: workflow was implicitly merged into this Session prior to SQLAlchemy 2.0. + # when AT LEAST ONE step in steps belonged to a session. + for step in steps: + if step and object_session(step): + object_session(step).add(workflow) + break comments: List[model.WorkflowComment] = [] comments_by_external_id: Dict[str, model.WorkflowComment] = {} diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 2594af7f9ded..492b8d98c1b4 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -7693,7 +7693,10 @@ class WorkflowStep(Base, RepresentById): "WorkflowStepConnection", primaryjoin=(lambda: WorkflowStepConnection.output_step_id == WorkflowStep.id) ) workflow = relationship( - "Workflow", primaryjoin=(lambda: Workflow.id == WorkflowStep.workflow_id), back_populates="steps" + "Workflow", + primaryjoin=(lambda: Workflow.id == WorkflowStep.workflow_id), + back_populates="steps", + cascade_backrefs=False, ) # Injected attributes From 4fac426a812abc2f2abfc84192ff4d2d80bd73ac Mon Sep 17 00:00:00 2001 From: John Davis Date: Tue, 5 Dec 2023 14:53:45 -0500 Subject: [PATCH 40/43] Remove redundant safeguards (self._session_add() called) --- lib/galaxy/model/store/__init__.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/lib/galaxy/model/store/__init__.py b/lib/galaxy/model/store/__init__.py index 00f04b426afa..53fe0c99beef 100644 --- a/lib/galaxy/model/store/__init__.py +++ b/lib/galaxy/model/store/__init__.py @@ -1107,8 +1107,6 @@ def attach_workflow_step(imported_object, attrs): input_parameter.name = input_parameter_attrs["name"] input_parameter.type = input_parameter_attrs["type"] input_parameter.workflow_invocation = imported_invocation - # Safeguard: input_parameter was implicitly merged into this Session prior to SQLAlchemy 2.0. - self.sa_session.add(input_parameter) self._session_add(input_parameter) input_parameters.append(input_parameter) @@ -1120,8 +1118,6 @@ def attach_workflow_step(imported_object, attrs): step_state.value = step_state_attrs["value"] attach_workflow_step(step_state, step_state_attrs) step_state.workflow_invocation = imported_invocation - # Safeguard: step_state was implicitly merged into this Session prior to SQLAlchemy 2.0. - self.sa_session.add(step_state) self._session_add(step_state) step_states.append(step_state) @@ -1139,8 +1135,6 @@ def attach_workflow_step(imported_object, attrs): input_dataset = model.WorkflowRequestToInputDatasetAssociation() attach_workflow_step(input_dataset, input_dataset_attrs) input_dataset.workflow_invocation = imported_invocation - # Safeguard: input_dataset was implicitly merged into this Session prior to SQLAlchemy 2.0. - self.sa_session.add(input_dataset) input_dataset.name = input_dataset_attrs["name"] dataset_link_attrs = input_dataset_attrs["dataset"] if dataset_link_attrs: @@ -1155,8 +1149,6 @@ def attach_workflow_step(imported_object, attrs): input_dataset_collection = model.WorkflowRequestToInputDatasetCollectionAssociation() attach_workflow_step(input_dataset_collection, input_dataset_collection_attrs) input_dataset_collection.workflow_invocation = imported_invocation - # Safeguard: input_dataset_collection was implicitly merged into this Session prior to SQLAlchemy 2.0. - self.sa_session.add(input_dataset_collection) input_dataset_collection.name = input_dataset_collection_attrs["name"] dataset_collection_link_attrs = input_dataset_collection_attrs["dataset_collection"] if dataset_collection_link_attrs: @@ -1171,8 +1163,6 @@ def attach_workflow_step(imported_object, attrs): for output_dataset_collection_attrs in invocation_attrs["output_dataset_collections"]: output_dataset_collection = model.WorkflowInvocationOutputDatasetCollectionAssociation() output_dataset_collection.workflow_invocation = imported_invocation - # Safeguard: output_dataset_collection was implicitly merged into this Session prior to SQLAlchemy 2.0. - self.sa_session.add(output_dataset_collection) attach_workflow_step(output_dataset_collection, output_dataset_collection_attrs) workflow_output = output_dataset_collection_attrs["workflow_output"] label = workflow_output.get("label") @@ -1185,8 +1175,6 @@ def attach_workflow_step(imported_object, attrs): for output_dataset_attrs in invocation_attrs["output_datasets"]: output_dataset = model.WorkflowInvocationOutputDatasetAssociation() output_dataset.workflow_invocation = imported_invocation - # Safeguard: output_dataset was implicitly merged into this Session prior to SQLAlchemy 2.0. - self.sa_session.add(output_dataset) attach_workflow_step(output_dataset, output_dataset_attrs) workflow_output = output_dataset_attrs["workflow_output"] label = workflow_output.get("label") @@ -1199,8 +1187,6 @@ def attach_workflow_step(imported_object, attrs): for output_value_attrs in invocation_attrs["output_values"]: output_value = model.WorkflowInvocationOutputValue() output_value.workflow_invocation = imported_invocation - # Safeguard: output_value was implicitly merged into this Session prior to SQLAlchemy 2.0. - self.sa_session.add(output_value) output_value.value = output_value_attrs["value"] attach_workflow_step(output_value, output_value_attrs) workflow_output = output_value_attrs["workflow_output"] From 3ef86ebcc8e23a9defa4ad00da37d3f0b415eb41 Mon Sep 17 00:00:00 2001 From: John Davis Date: Tue, 5 Dec 2023 14:09:15 -0500 Subject: [PATCH 41/43] Add utility function --- lib/galaxy/model/database_utils.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/galaxy/model/database_utils.py b/lib/galaxy/model/database_utils.py index ee7d12819edb..0865450d71da 100644 --- a/lib/galaxy/model/database_utils.py +++ b/lib/galaxy/model/database_utils.py @@ -12,6 +12,7 @@ ) from sqlalchemy.engine import Engine from sqlalchemy.engine.url import make_url +from sqlalchemy.orm import object_session from sqlalchemy.sql.compiler import IdentifierPreparer from sqlalchemy.sql.expression import ( ClauseElement, @@ -175,3 +176,22 @@ def _statement_executed_without_error(statement: ClauseElement, engine: Engine) def is_postgres(url: DbUrl) -> bool: return url.startswith("postgres") + + +def ensure_object_added_to_session(object_to_add, *, object_in_session=None, session=None) -> bool: + """ + This function is intended as a safeguard to mimic pre-SQLAlchemy 2.0 behavior. + `object_to_add` was implicitly merged into a Session prior to SQLAlchemy 2.0, which was indicated + by `RemovedIn20Warning` warnings logged while running Galaxy's tests. (See https://github.com/galaxyproject/galaxy/issues/12541) + As part of the upgrade to 2.0, the `cascade_backrefs=False` argument was added to the relevant relationships that turned off this behavior. + This function is called from the code that triggered these warnings, thus emulating the cascading behavior. + The intention is to remove all such calls, as well as this function definition, after the move to SQLAlchemy 2.0. + # Ref: https://docs.sqlalchemy.org/en/14/changelog/migration_14.html#cascade-backrefs-behavior-deprecated-for-removal-in-2-0 + """ + if session: + session.add(object_to_add) + return True + if object_in_session and object_session(object_in_session): + object_session(object_in_session).add(object_to_add) + return True + return False From ab2f76c94d89fd9419b1dc0bc5ea606834cdbe2b Mon Sep 17 00:00:00 2001 From: John Davis Date: Tue, 5 Dec 2023 14:42:17 -0500 Subject: [PATCH 42/43] Use utility function for safeguards --- lib/galaxy/managers/workflows.py | 5 +-- lib/galaxy/model/__init__.py | 70 ++++++++---------------------- lib/galaxy/model/store/__init__.py | 13 ++---- lib/galaxy/webapps/base/webapp.py | 6 +-- lib/galaxy/workflow/extract.py | 4 +- lib/galaxy/workflow/modules.py | 6 +-- lib/galaxy/workflow/run.py | 6 +-- lib/galaxy/workflow/run_request.py | 7 +-- test/integration/test_job_files.py | 5 +-- 9 files changed, 36 insertions(+), 86 deletions(-) diff --git a/lib/galaxy/managers/workflows.py b/lib/galaxy/managers/workflows.py index 37c7a748981f..5c1a1481e6e7 100644 --- a/lib/galaxy/managers/workflows.py +++ b/lib/galaxy/managers/workflows.py @@ -35,7 +35,6 @@ from sqlalchemy.orm import ( aliased, joinedload, - object_session, Query, subqueryload, ) @@ -67,6 +66,7 @@ WorkflowInvocationStep, ) from galaxy.model.base import transaction +from galaxy.model.database_utils import ensure_object_added_to_session from galaxy.model.index_filter_util import ( append_user_filter, raw_text_column_filter, @@ -822,8 +822,7 @@ def _workflow_from_raw_description( # Safeguard: workflow was implicitly merged into this Session prior to SQLAlchemy 2.0. # when AT LEAST ONE step in steps belonged to a session. for step in steps: - if step and object_session(step): - object_session(step).add(workflow) + if ensure_object_added_to_session(workflow, object_in_session=step): break comments: List[model.WorkflowComment] = [] diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 492b8d98c1b4..02f1e4985f52 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -129,6 +129,7 @@ UUIDType, ) from galaxy.model.database_object_names import NAMING_CONVENTION +from galaxy.model.database_utils import ensure_object_added_to_session from galaxy.model.item_attrs import ( get_item_annotation_str, UsesAnnotations, @@ -2471,9 +2472,7 @@ def __init__(self, action_type, workflow_step=None, output_name=None, action_arg self.output_name = output_name self.action_arguments = action_arguments self.workflow_step = workflow_step - # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. - if workflow_step and object_session(workflow_step): - object_session(workflow_step).add(self) + ensure_object_added_to_session(self, object_in_session=workflow_step) class PostJobActionAssociation(Base, RepresentById): @@ -2488,9 +2487,7 @@ class PostJobActionAssociation(Base, RepresentById): def __init__(self, pja, job=None, job_id=None): if job is not None: self.job = job - # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. - if job and object_session(job): - object_session(job).add(self) + ensure_object_added_to_session(self, object_in_session=job) elif job_id is not None: self.job_id = job_id else: @@ -2861,9 +2858,7 @@ class UserNotificationAssociation(Base, RepresentById): def __init__(self, user, notification): self.user = user - # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. - if user and object_session(user): - object_session(user).add(self) + ensure_object_added_to_session(self, object_in_session=user) self.notification = notification @@ -3517,9 +3512,7 @@ class GroupRoleAssociation(Base, RepresentById): def __init__(self, group, role): self.group = group - # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. - if group and object_session(group): - object_session(group).add(self) + ensure_object_added_to_session(self, object_in_session=group) self.role = role @@ -3690,9 +3683,7 @@ def __init__(self, type, quota): assert type in self.types.__members__.values(), "Invalid type" self.type = type self.quota = quota - # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. - if quota and object_session(quota): - object_session(quota).add(self) + ensure_object_added_to_session(self, object_in_session=quota) class DatasetPermissions(Base, RepresentById): @@ -3733,9 +3724,7 @@ def __init__(self, action, library_item, role): self.action = action if isinstance(library_item, Library): self.library = library_item - # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. - if library_item and object_session(library_item): - object_session(library_item).add(self) + ensure_object_added_to_session(self, object_in_session=library_item) else: raise Exception(f"Invalid Library specified: {library_item.__class__.__name__}") self.role = role @@ -3757,9 +3746,7 @@ def __init__(self, action, library_item, role): self.action = action if isinstance(library_item, LibraryFolder): self.folder = library_item - # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. - if library_item and object_session(library_item): - object_session(library_item).add(self) + ensure_object_added_to_session(self, object_in_session=library_item) else: raise Exception(f"Invalid LibraryFolder specified: {library_item.__class__.__name__}") self.role = role @@ -3781,9 +3768,7 @@ def __init__(self, action, library_item, role): self.action = action if isinstance(library_item, LibraryDataset): self.library_dataset = library_item - # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. - if library_item and object_session(library_item): - object_session(library_item).add(self) + ensure_object_added_to_session(self, object_in_session=library_item) else: raise Exception(f"Invalid LibraryDataset specified: {library_item.__class__.__name__}") self.role = role @@ -4405,11 +4390,7 @@ def __init__( elif dataset: add_object_to_object_session(self, dataset) self.dataset = dataset - - # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. - if dataset and object_session(dataset): - object_session(dataset).add(self) - + ensure_object_added_to_session(self, object_in_session=dataset) self.parent_id = parent_id @property @@ -7279,9 +7260,7 @@ class GalaxySessionToHistoryAssociation(Base, RepresentById): def __init__(self, galaxy_session, history): self.galaxy_session = galaxy_session - # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. - if galaxy_session and object_session(galaxy_session): - object_session(galaxy_session).add(self) + ensure_object_added_to_session(self, object_in_session=galaxy_session) add_object_to_object_session(self, history) self.history = history @@ -7793,9 +7772,7 @@ def add_connection(self, input_name, output_name, output_step, input_subworkflow conn = WorkflowStepConnection() conn.input_step_input = step_input - # Safeguard: conn was implicitly merged into this Session prior to SQLAlchemy 2.0. - if step_input and object_session(step_input): - object_session(step_input).add(conn) + ensure_object_added_to_session(conn, object_in_session=step_input) conn.output_name = output_name add_object_to_object_session(conn, output_step) conn.output_step = output_step @@ -8077,9 +8054,7 @@ class WorkflowOutput(Base, Serializable): def __init__(self, workflow_step, output_name=None, label=None, uuid=None): self.workflow_step = workflow_step - # Safeguard: self was implicitly merged into this Session prior to SQLAlchemy 2.0. - if object_session(workflow_step): - object_session(workflow_step).add(self) + ensure_object_added_to_session(self, object_in_session=workflow_step) self.output_name = output_name self.label = label self.uuid = get_uuid(uuid) @@ -8468,9 +8443,7 @@ def add_output(self, workflow_output, step, output_object): # dispatch on actual object and not step type. output_assoc = WorkflowInvocationOutputValue() output_assoc.workflow_invocation = self - # Safeguard: output_assoc was implicitly merged into this Session prior to SQLAlchemy 2.0. - if object_session(self): - object_session(self).add(output_assoc) + ensure_object_added_to_session(output_assoc, object_in_session=self) output_assoc.workflow_output = workflow_output output_assoc.workflow_step = step output_assoc.value = output_object @@ -8478,9 +8451,7 @@ def add_output(self, workflow_output, step, output_object): elif output_object.history_content_type == "dataset": output_assoc = WorkflowInvocationOutputDatasetAssociation() output_assoc.workflow_invocation = self - # Safeguard: output_assoc was implicitly merged into this Session prior to SQLAlchemy 2.0. - if object_session(self): - object_session(self).add(output_assoc) + ensure_object_added_to_session(output_assoc, object_in_session=self) output_assoc.workflow_output = workflow_output output_assoc.workflow_step = step output_assoc.dataset = output_object @@ -8488,8 +8459,7 @@ def add_output(self, workflow_output, step, output_object): elif output_object.history_content_type == "dataset_collection": output_assoc = WorkflowInvocationOutputDatasetCollectionAssociation() output_assoc.workflow_invocation = self - if object_session(self): - object_session(self).add(output_assoc) + ensure_object_added_to_session(output_assoc, object_in_session=self) output_assoc.workflow_output = workflow_output output_assoc.workflow_step = step output_assoc.dataset_collection = output_object @@ -8943,18 +8913,14 @@ def add_output(self, output_name, output_object): if output_object.history_content_type == "dataset": output_assoc = WorkflowInvocationStepOutputDatasetAssociation() output_assoc.workflow_invocation_step = self - # Safeguard: output_assoc was implicitly merged into this Session prior to SQLAlchemy 2.0. - if object_session(self): - object_session(self).add(output_assoc) + ensure_object_added_to_session(output_assoc, object_in_session=self) output_assoc.dataset = output_object output_assoc.output_name = output_name self.output_datasets.append(output_assoc) elif output_object.history_content_type == "dataset_collection": output_assoc = WorkflowInvocationStepOutputDatasetCollectionAssociation() output_assoc.workflow_invocation_step = self - # Safeguard: output_assoc was implicitly merged into this Session prior to SQLAlchemy 2.0. - if object_session(self): - object_session(self).add(output_assoc) + ensure_object_added_to_session(output_assoc, object_in_session=self) output_assoc.dataset_collection = output_object output_assoc.output_name = output_name self.output_dataset_collections.append(output_assoc) diff --git a/lib/galaxy/model/store/__init__.py b/lib/galaxy/model/store/__init__.py index 53fe0c99beef..27dbaa6ffa34 100644 --- a/lib/galaxy/model/store/__init__.py +++ b/lib/galaxy/model/store/__init__.py @@ -40,10 +40,7 @@ ) from rocrate.rocrate import ROCrate from sqlalchemy import select -from sqlalchemy.orm import ( - joinedload, - object_session, -) +from sqlalchemy.orm import joinedload from sqlalchemy.orm.scoping import scoped_session from sqlalchemy.sql import expression from typing_extensions import Protocol @@ -60,6 +57,7 @@ ) from galaxy.files.uris import stream_url_to_file from galaxy.model.base import transaction +from galaxy.model.database_utils import ensure_object_added_to_session from galaxy.model.mapping import GalaxyModelMapping from galaxy.model.metadata import MetadataCollection from galaxy.model.orm.util import ( @@ -1027,9 +1025,7 @@ def _import_workflow_invocations( imported_invocation = model.WorkflowInvocation() imported_invocation.user = self.user imported_invocation.history = history - # Safeguard: imported_invocation was implicitly merged into this Session prior to SQLAlchemy 2.0. - if history and object_session(history): - object_session(history).add(imported_invocation) + ensure_object_added_to_session(imported_invocation, object_in_session=history) workflow_key = invocation_attrs["workflow"] if workflow_key not in object_import_tracker.workflows_by_key: raise Exception(f"Failed to find key {workflow_key} in {object_import_tracker.workflows_by_key.keys()}") @@ -1051,8 +1047,7 @@ def attach_workflow_step(imported_object, attrs): for step_attrs in invocation_attrs["steps"]: imported_invocation_step = model.WorkflowInvocationStep() imported_invocation_step.workflow_invocation = imported_invocation - # Safeguard: imported_invocation_step was implicitly merged into this Session prior to SQLAlchemy 2.0. - self.sa_session.add(imported_invocation_step) + ensure_object_added_to_session(imported_invocation, session=self.sa_session) attach_workflow_step(imported_invocation_step, step_attrs) restore_times(imported_invocation_step, step_attrs) imported_invocation_step.action = step_attrs["action"] diff --git a/lib/galaxy/webapps/base/webapp.py b/lib/galaxy/webapps/base/webapp.py index d9766abf3fea..59c3adda8a22 100644 --- a/lib/galaxy/webapps/base/webapp.py +++ b/lib/galaxy/webapps/base/webapp.py @@ -24,7 +24,6 @@ select, true, ) -from sqlalchemy.orm import object_session from sqlalchemy.orm.exc import NoResultFound from galaxy import util @@ -39,6 +38,7 @@ from galaxy.managers.session import GalaxySessionManager from galaxy.managers.users import UserManager from galaxy.model.base import transaction +from galaxy.model.database_utils import ensure_object_added_to_session from galaxy.structured_app import ( BasicSharedApp, MinimalApp, @@ -1120,9 +1120,7 @@ def create_new_session(trans, prev_galaxy_session=None, user_for_new_session=Non if user_for_new_session: # The new session should be associated with the user galaxy_session.user = user_for_new_session - # Safeguard: galaxy_session was implicitly merged into this Session prior to SQLAlchemy 2.0. - if user_for_new_session and object_session(user_for_new_session): - object_session(user_for_new_session).add(galaxy_session) + ensure_object_added_to_session(galaxy_session, object_in_session=user_for_new_session) return galaxy_session diff --git a/lib/galaxy/workflow/extract.py b/lib/galaxy/workflow/extract.py index 064b15f7efd9..ad196a82d28e 100644 --- a/lib/galaxy/workflow/extract.py +++ b/lib/galaxy/workflow/extract.py @@ -9,6 +9,7 @@ model, ) from galaxy.model.base import transaction +from galaxy.model.database_utils import ensure_object_added_to_session from galaxy.tool_util.parser import ToolOutputCollectionPart from galaxy.tools.parameters.basic import ( DataCollectionToolParameter, @@ -71,8 +72,7 @@ def extract_workflow( workflow.stored_workflow = stored stored.latest_workflow = workflow trans.sa_session.add(stored) - # Safeguard: workflow was implicitly merged into this Session prior to SQLAlchemy 2.0. - trans.sa_session.add(workflow) + ensure_object_added_to_session(workflow, session=trans.sa_session) with transaction(trans.sa_session): trans.sa_session.commit() return stored diff --git a/lib/galaxy/workflow/modules.py b/lib/galaxy/workflow/modules.py index 9a2240b2dd67..cf6d0ebcce30 100644 --- a/lib/galaxy/workflow/modules.py +++ b/lib/galaxy/workflow/modules.py @@ -18,7 +18,6 @@ ) from cwl_utils.expression import do_eval -from sqlalchemy.orm import object_session from typing_extensions import TypedDict from galaxy import ( @@ -36,6 +35,7 @@ WorkflowStep, WorkflowStepConnection, ) +from galaxy.model.database_utils import ensure_object_added_to_session from galaxy.model.dataset_collections import matching from galaxy.schema.invocation import ( CancelReason, @@ -645,9 +645,7 @@ def from_workflow_step(Class, trans, step, **kwds): def save_to_step(self, step, **kwd): step.type = self.type step.subworkflow = self.subworkflow - # Safeguard: step was implicitly merged into this Session prior to SQLAlchemy 2.0. - if self.subworkflow and object_session(self.subworkflow): - object_session(self.subworkflow).add(step) + ensure_object_added_to_session(step, object_in_session=self.subworkflow) def get_name(self): if hasattr(self.subworkflow, "name"): diff --git a/lib/galaxy/workflow/run.py b/lib/galaxy/workflow/run.py index e50962b8a27f..de89a6a40329 100644 --- a/lib/galaxy/workflow/run.py +++ b/lib/galaxy/workflow/run.py @@ -10,7 +10,6 @@ Union, ) -from sqlalchemy.orm import object_session from typing_extensions import Protocol from galaxy import model @@ -20,6 +19,7 @@ WorkflowInvocationStep, ) from galaxy.model.base import transaction +from galaxy.model.database_utils import ensure_object_added_to_session from galaxy.schema.invocation import ( CancelReason, FailureReason, @@ -226,9 +226,7 @@ def invoke(self) -> Dict[int, Any]: workflow_invocation_step = WorkflowInvocationStep() assert workflow_invocation_step workflow_invocation_step.workflow_invocation = workflow_invocation - # Safeguard: workflow_invocation_step was implicitly merged into this Session prior to SQLAlchemy 2.0. - if workflow_invocation and object_session(workflow_invocation): - object_session(workflow_invocation).add(workflow_invocation_step) + ensure_object_added_to_session(workflow_invocation_step, object_in_session=workflow_invocation) workflow_invocation_step.workflow_step = step workflow_invocation_step.state = "new" diff --git a/lib/galaxy/workflow/run_request.py b/lib/galaxy/workflow/run_request.py index b6ce3070e549..2ba9c4e82599 100644 --- a/lib/galaxy/workflow/run_request.py +++ b/lib/galaxy/workflow/run_request.py @@ -9,8 +9,6 @@ TYPE_CHECKING, ) -from sqlalchemy.orm import object_session - from galaxy import exceptions from galaxy.model import ( EffectiveOutput, @@ -24,6 +22,7 @@ WorkflowRequestStepState, ) from galaxy.model.base import transaction +from galaxy.model.database_utils import ensure_object_added_to_session from galaxy.tools.parameters.meta import expand_workflow_inputs from galaxy.workflow.resources import get_resource_mapper_function @@ -488,9 +487,7 @@ def workflow_run_config_to_request( workflow_invocation = WorkflowInvocation() workflow_invocation.uuid = uuid.uuid1() workflow_invocation.history = run_config.target_history - # Safeguard: workflow_invocation was implicitly merged into this Session prior to SQLAlchemy 2.0. - if run_config.target_history and object_session(run_config.target_history): - object_session(run_config.target_history).add(workflow_invocation) + ensure_object_added_to_session(workflow_invocation, object_in_session=run_config.target_history) def add_parameter(name: str, value: str, type: WorkflowRequestInputParameter.types) -> None: parameter = WorkflowRequestInputParameter( diff --git a/test/integration/test_job_files.py b/test/integration/test_job_files.py index f9f6cfaae540..8d0d4422c732 100644 --- a/test/integration/test_job_files.py +++ b/test/integration/test_job_files.py @@ -20,10 +20,10 @@ import requests from sqlalchemy import select -from sqlalchemy.orm import object_session from galaxy import model from galaxy.model.base import transaction +from galaxy.model.database_utils import ensure_object_added_to_session from galaxy_test.base import api_asserts from galaxy_test.base.populators import DatasetPopulator from galaxy_test.driver import integration_util @@ -139,8 +139,7 @@ def create_static_job_with_state(self, state): sa_session.commit() job = model.Job() job.history = history - # Safeguard: job was implicitly merged into this Session prior to SQLAlchemy 2.0. - object_session(history).add(job) + ensure_object_added_to_session(job, object_in_session=history) job.user = user job.handler = "unknown-handler" job.state = state From 631e84dd82e960e27a13cc20e50638810d9b8bfe Mon Sep 17 00:00:00 2001 From: John Davis Date: Tue, 5 Dec 2023 15:31:39 -0500 Subject: [PATCH 43/43] Move function to model.base to avoid circular import --- lib/galaxy/managers/workflows.py | 6 ++++-- lib/galaxy/model/__init__.py | 6 ++++-- lib/galaxy/model/base.py | 20 ++++++++++++++++++++ lib/galaxy/model/store/__init__.py | 6 ++++-- lib/galaxy/webapps/base/webapp.py | 6 ++++-- lib/galaxy/workflow/extract.py | 6 ++++-- lib/galaxy/workflow/modules.py | 2 +- lib/galaxy/workflow/run.py | 6 ++++-- lib/galaxy/workflow/run_request.py | 6 ++++-- test/integration/test_job_files.py | 6 ++++-- 10 files changed, 53 insertions(+), 17 deletions(-) diff --git a/lib/galaxy/managers/workflows.py b/lib/galaxy/managers/workflows.py index 5c1a1481e6e7..4d542abd23b5 100644 --- a/lib/galaxy/managers/workflows.py +++ b/lib/galaxy/managers/workflows.py @@ -65,8 +65,10 @@ WorkflowInvocation, WorkflowInvocationStep, ) -from galaxy.model.base import transaction -from galaxy.model.database_utils import ensure_object_added_to_session +from galaxy.model.base import ( + ensure_object_added_to_session, + transaction, +) from galaxy.model.index_filter_util import ( append_user_filter, raw_text_column_filter, diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 02f1e4985f52..2bf193dd334a 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -119,7 +119,10 @@ import galaxy.model.tags import galaxy.security.passwords import galaxy.util -from galaxy.model.base import transaction +from galaxy.model.base import ( + ensure_object_added_to_session, + transaction, +) from galaxy.model.custom_types import ( DoubleEncodedJsonType, JSONType, @@ -129,7 +132,6 @@ UUIDType, ) from galaxy.model.database_object_names import NAMING_CONVENTION -from galaxy.model.database_utils import ensure_object_added_to_session from galaxy.model.item_attrs import ( get_item_annotation_str, UsesAnnotations, diff --git a/lib/galaxy/model/base.py b/lib/galaxy/model/base.py index 2aade3f4ff36..2e4d248afeb4 100644 --- a/lib/galaxy/model/base.py +++ b/lib/galaxy/model/base.py @@ -20,6 +20,7 @@ from sqlalchemy import event from sqlalchemy.orm import ( + object_session, scoped_session, Session, sessionmaker, @@ -167,3 +168,22 @@ def before_flush(session, flush_context, instances): obj.__create_version__(session) for obj in versioned_objects(session.deleted): obj.__create_version__(session, deleted=True) + + +def ensure_object_added_to_session(object_to_add, *, object_in_session=None, session=None) -> bool: + """ + This function is intended as a safeguard to mimic pre-SQLAlchemy 2.0 behavior. + `object_to_add` was implicitly merged into a Session prior to SQLAlchemy 2.0, which was indicated + by `RemovedIn20Warning` warnings logged while running Galaxy's tests. (See https://github.com/galaxyproject/galaxy/issues/12541) + As part of the upgrade to 2.0, the `cascade_backrefs=False` argument was added to the relevant relationships that turned off this behavior. + This function is called from the code that triggered these warnings, thus emulating the cascading behavior. + The intention is to remove all such calls, as well as this function definition, after the move to SQLAlchemy 2.0. + # Ref: https://docs.sqlalchemy.org/en/14/changelog/migration_14.html#cascade-backrefs-behavior-deprecated-for-removal-in-2-0 + """ + if session: + session.add(object_to_add) + return True + if object_in_session and object_session(object_in_session): + object_session(object_in_session).add(object_to_add) + return True + return False diff --git a/lib/galaxy/model/store/__init__.py b/lib/galaxy/model/store/__init__.py index 27dbaa6ffa34..eb30696b1e34 100644 --- a/lib/galaxy/model/store/__init__.py +++ b/lib/galaxy/model/store/__init__.py @@ -56,8 +56,10 @@ ProvidesUserFileSourcesUserContext, ) from galaxy.files.uris import stream_url_to_file -from galaxy.model.base import transaction -from galaxy.model.database_utils import ensure_object_added_to_session +from galaxy.model.base import ( + ensure_object_added_to_session, + transaction, +) from galaxy.model.mapping import GalaxyModelMapping from galaxy.model.metadata import MetadataCollection from galaxy.model.orm.util import ( diff --git a/lib/galaxy/webapps/base/webapp.py b/lib/galaxy/webapps/base/webapp.py index 59c3adda8a22..92633b3188ed 100644 --- a/lib/galaxy/webapps/base/webapp.py +++ b/lib/galaxy/webapps/base/webapp.py @@ -37,8 +37,10 @@ from galaxy.managers import context from galaxy.managers.session import GalaxySessionManager from galaxy.managers.users import UserManager -from galaxy.model.base import transaction -from galaxy.model.database_utils import ensure_object_added_to_session +from galaxy.model.base import ( + ensure_object_added_to_session, + transaction, +) from galaxy.structured_app import ( BasicSharedApp, MinimalApp, diff --git a/lib/galaxy/workflow/extract.py b/lib/galaxy/workflow/extract.py index ad196a82d28e..bfa74d404d17 100644 --- a/lib/galaxy/workflow/extract.py +++ b/lib/galaxy/workflow/extract.py @@ -8,8 +8,10 @@ exceptions, model, ) -from galaxy.model.base import transaction -from galaxy.model.database_utils import ensure_object_added_to_session +from galaxy.model.base import ( + ensure_object_added_to_session, + transaction, +) from galaxy.tool_util.parser import ToolOutputCollectionPart from galaxy.tools.parameters.basic import ( DataCollectionToolParameter, diff --git a/lib/galaxy/workflow/modules.py b/lib/galaxy/workflow/modules.py index cf6d0ebcce30..2ff8fe290e0b 100644 --- a/lib/galaxy/workflow/modules.py +++ b/lib/galaxy/workflow/modules.py @@ -35,7 +35,7 @@ WorkflowStep, WorkflowStepConnection, ) -from galaxy.model.database_utils import ensure_object_added_to_session +from galaxy.model.base import ensure_object_added_to_session from galaxy.model.dataset_collections import matching from galaxy.schema.invocation import ( CancelReason, diff --git a/lib/galaxy/workflow/run.py b/lib/galaxy/workflow/run.py index de89a6a40329..c7eab00a7a1d 100644 --- a/lib/galaxy/workflow/run.py +++ b/lib/galaxy/workflow/run.py @@ -18,8 +18,10 @@ WorkflowInvocation, WorkflowInvocationStep, ) -from galaxy.model.base import transaction -from galaxy.model.database_utils import ensure_object_added_to_session +from galaxy.model.base import ( + ensure_object_added_to_session, + transaction, +) from galaxy.schema.invocation import ( CancelReason, FailureReason, diff --git a/lib/galaxy/workflow/run_request.py b/lib/galaxy/workflow/run_request.py index 2ba9c4e82599..029b1e7630c6 100644 --- a/lib/galaxy/workflow/run_request.py +++ b/lib/galaxy/workflow/run_request.py @@ -21,8 +21,10 @@ WorkflowRequestInputParameter, WorkflowRequestStepState, ) -from galaxy.model.base import transaction -from galaxy.model.database_utils import ensure_object_added_to_session +from galaxy.model.base import ( + ensure_object_added_to_session, + transaction, +) from galaxy.tools.parameters.meta import expand_workflow_inputs from galaxy.workflow.resources import get_resource_mapper_function diff --git a/test/integration/test_job_files.py b/test/integration/test_job_files.py index 8d0d4422c732..4ed02b350dd3 100644 --- a/test/integration/test_job_files.py +++ b/test/integration/test_job_files.py @@ -22,8 +22,10 @@ from sqlalchemy import select from galaxy import model -from galaxy.model.base import transaction -from galaxy.model.database_utils import ensure_object_added_to_session +from galaxy.model.base import ( + ensure_object_added_to_session, + transaction, +) from galaxy_test.base import api_asserts from galaxy_test.base.populators import DatasetPopulator from galaxy_test.driver import integration_util