Skip to content

Commit 79af29f

Browse files
committed
Avoid postgres truncation of aliases and labels
Postgres has a default limit of 63 characters that we may exceed for deeply nested parameters. Should fix ``` Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: galaxy.workflow.run ERROR 2025-07-09 13:07:40,250 [pN:workflow_scheduler_sn06_0,p:1620756,tN:WorkflowRequestMonitor.monitor_thread] Failed to execute scheduled workflow. Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: Traceback (most recent call last): Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: File "/opt/galaxy/venv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1964, in _exec_single_context Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: self.dialect.do_execute( Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: File "/opt/galaxy/venv/lib/python3.11/site-packages/sqlalchemy/engine/default.py", line 945, in do_execute Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: cursor.execute(statement, parameters) Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: psycopg2.errors.DuplicateAlias: table name "job_to_input_dataset_collection_1_results_0|software_cond|outpu" specified more than once Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: The above exception was the direct cause of the following exception: Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: Traceback (most recent call last): Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: File "/opt/galaxy/server/lib/galaxy/workflow/run.py", line 249, in invoke Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: incomplete_or_none = self._invoke_step(workflow_invocation_step) Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: File "/opt/galaxy/server/lib/galaxy/workflow/run.py", line 338, in _invoke_step Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: incomplete_or_none = invocation_step.workflow_step.module.execute( Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: File "/opt/galaxy/server/lib/galaxy/workflow/modules.py", line 2445, in execute Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: completed_jobs: Dict[int, Optional[Job]] = tool.completed_jobs( Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: ^^^^^^^^^^^^^^^^^^^^ Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: File "/opt/galaxy/server/lib/galaxy/tools/__init__.py", line 2202, in completed_jobs Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: completed_jobs[i] = self.job_search.by_tool_input( Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: File "/opt/galaxy/server/lib/galaxy/managers/jobs.py", line 436, in by_tool_input Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: return self.__search( Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: ^^^^^^^^^^^^^^ Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: File "/opt/galaxy/server/lib/galaxy/managers/jobs.py", line 524, in __search Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: for job in self.sa_session.execute(stmt): Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: File "/opt/galaxy/venv/lib/python3.11/site-packages/sqlalchemy/orm/scoping.py", line 779, in execute Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: return self._proxied.execute( Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: ^^^^^^^^^^^^^^^^^^^^^^ Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: File "/opt/galaxy/venv/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 2365, in execute Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: return self._execute_internal( Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: ^^^^^^^^^^^^^^^^^^^^^^^ Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: File "/opt/galaxy/venv/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 2251, in _execute_internal Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: result: Result[Any] = compile_state_cls.orm_execute_statement( Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: File "/opt/galaxy/venv/lib/python3.11/site-packages/sqlalchemy/orm/context.py", line 306, in orm_execute_statement Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: result = conn.execute( Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: ^^^^^^^^^^^^^ Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: File "/opt/galaxy/venv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1416, in execute Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: return meth( Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: ^^^^^ Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: File "/opt/galaxy/venv/lib/python3.11/site-packages/sqlalchemy/sql/elements.py", line 523, in _execute_on_connection Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: return connection._execute_clauseelement( Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: File "/opt/galaxy/venv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1638, in _execute_clauseelement Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: ret = self._execute_context( Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: ^^^^^^^^^^^^^^^^^^^^^^ Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: File "/opt/galaxy/venv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1843, in _execute_context Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: return self._exec_single_context( Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: ^^^^^^^^^^^^^^^^^^^^^^^^^^ Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: File "/opt/galaxy/venv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1983, in _exec_single_context Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: self._handle_dbapi_exception( Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: File "/opt/galaxy/venv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 2352, in _handle_dbapi_exception Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: raise sqlalchemy_exception.with_traceback(exc_info[2]) from e Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: File "/opt/galaxy/venv/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1964, in _exec_single_context Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: self.dialect.do_execute( Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: File "/opt/galaxy/venv/lib/python3.11/site-packages/sqlalchemy/engine/default.py", line 945, in do_execute Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: cursor.execute(statement, parameters) Jul 09 13:07:40 sn06.galaxyproject.eu python[1620756]: sqlalchemy.exc.ProgrammingError: (psycopg2.errors.DuplicateAlias) table name "job_to_input_dataset_collection_1_results_0|software_cond|outpu" specified more than once ```
1 parent 3976e8b commit 79af29f

File tree

1 file changed

+53
-37
lines changed

1 file changed

+53
-37
lines changed

lib/galaxy/managers/jobs.py

Lines changed: 53 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,17 @@ def get_path_key(path_tuple):
125125
return path_key
126126

127127

128+
def safe_label_or_none(label: str) -> Optional[str]:
129+
if label and len(label) > 63:
130+
return None
131+
return label
132+
133+
134+
def safe_aliased(model_class, name=None):
135+
"""Create an aliased model class with a unique name."""
136+
return aliased(model_class, name=safe_label_or_none(name))
137+
138+
128139
class JobManager:
129140
def __init__(self, app: StructuredApp):
130141
self.app = app
@@ -762,7 +773,8 @@ def _build_stmt_for_hda(
762773

763774
def _build_stmt_for_ldda(self, stmt, data_conditions, used_ids, k, v, value_index):
764775
a = aliased(model.JobToInputLibraryDatasetAssociation)
765-
labeled_col = a.ldda_id.label(f"{k}_{value_index}")
776+
label = safe_label_or_none(f"{k}_{value_index}")
777+
labeled_col = a.ldda_id.label(label)
766778
stmt = stmt.add_columns(labeled_col)
767779
stmt = stmt.join(a, a.job_id == model.Job.id)
768780
data_conditions.append(and_(a.name == k, a.ldda_id == v))
@@ -806,12 +818,10 @@ def _build_stmt_for_hdca(
806818
)
807819
depth = collection_type.count(":") if collection_type else 0
808820

809-
a = aliased(
810-
model.JobToInputDatasetCollectionAssociation, name=f"job_to_input_dataset_collection_1_{k}_{value_index}"
811-
)
812-
hdca_input = aliased(
821+
a = safe_aliased(model.JobToInputDatasetCollectionAssociation, name=f"jtidc_1_{k}_{value_index}")
822+
hdca_input = safe_aliased(
813823
model.HistoryDatasetCollectionAssociation,
814-
name=f"history_dataset_collection_association_1_{k}_{value_index}",
824+
name=f"hdca_1_{k}_{value_index}",
815825
)
816826

817827
_hdca_target_cte_ref = aliased(model.HistoryDatasetCollectionAssociation, name="_hdca_target_cte_ref")
@@ -844,7 +854,9 @@ def _build_stmt_for_hdca(
844854
.where(_hdca_target_cte_ref.id == v)
845855
.distinct()
846856
)
847-
reference_all_dataset_ids_cte = reference_all_dataset_ids_select.cte(f"ref_all_ds_ids_{k}_{value_index}")
857+
reference_all_dataset_ids_cte = reference_all_dataset_ids_select.cte(
858+
safe_label_or_none(f"ref_all_ds_ids_{k}_{value_index}")
859+
)
848860
# --- END NEW CTE ---
849861

850862
# CTE 1: signature_elements_cte (for the reference HDCA)
@@ -875,15 +887,17 @@ def _build_stmt_for_hdca(
875887
_hda_cte_ref, _hda_cte_ref.id == _leaf_cte_ref.hda_id
876888
)
877889
signature_elements_select = signature_elements_select.where(_hdca_target_cte_ref.id == v)
878-
signature_elements_cte = signature_elements_select.cte(f"signature_elements_{k}_{value_index}")
890+
signature_elements_cte = signature_elements_select.cte(
891+
safe_label_or_none(f"signature_elements_{k}_{value_index}")
892+
)
879893

880894
# CTE 2: reference_full_signature_cte
881895
# This CTE aggregates the path signature strings of the reference HDCA into a
882896
# canonical, sorted array. This array represents the complete "signature" of the collection.
883897
reference_full_signature_cte = (
884898
select(self.agg_expression(signature_elements_cte.c.path_signature_string).label("signature_array"))
885899
.select_from(signature_elements_cte)
886-
.cte(f"reference_full_signature_{k}_{value_index}")
900+
.cte(safe_label_or_none(f"reference_full_signature_{k}_{value_index}"))
887901
)
888902

889903
candidate_hdca = aliased(model.HistoryDatasetCollectionAssociation, name="candidate_hdca")
@@ -923,7 +937,7 @@ def _build_stmt_for_hdca(
923937
.where(candidate_hda.dataset_id.in_(select(reference_all_dataset_ids_cte.c.ref_dataset_id_for_overlap)))
924938
)
925939
candidate_hdca_pre_filter_ids_cte = candidate_hdca_pre_filter_ids_select.cte(
926-
f"cand_hdca_pre_filter_ids_{k}_{value_index}"
940+
safe_label_or_none(f"cand_hdca_pre_filter_ids_{k}_{value_index}")
927941
)
928942
# --- END NEW CTE ---
929943

@@ -959,7 +973,7 @@ def _build_stmt_for_hdca(
959973
candidate_hda, candidate_hda.id == _leaf_candidate_dce.hda_id
960974
)
961975
candidate_signature_elements_cte = candidate_signature_elements_select.cte(
962-
f"candidate_signature_elements_{k}_{value_index}"
976+
safe_label_or_none(f"candidate_signature_elements_{k}_{value_index}")
963977
)
964978

965979
# CTE 4: candidate_full_signatures_cte
@@ -974,7 +988,7 @@ def _build_stmt_for_hdca(
974988
)
975989
.select_from(candidate_signature_elements_cte)
976990
.group_by(candidate_signature_elements_cte.c.candidate_hdca_id)
977-
.cte(f"candidate_full_signatures_{k}_{value_index}")
991+
.cte(safe_label_or_none(f"candidate_full_signatures_{k}_{value_index}"))
978992
)
979993

980994
# CTE 5: equivalent_hdca_ids_cte
@@ -986,7 +1000,7 @@ def _build_stmt_for_hdca(
9861000
candidate_full_signatures_cte.c.full_signature_array
9871001
== select(reference_full_signature_cte.c.signature_array).scalar_subquery()
9881002
)
989-
.cte(f"equivalent_hdca_ids_{k}_{value_index}")
1003+
.cte(safe_label_or_none(f"equivalent_hdca_ids_{k}_{value_index}"))
9901004
)
9911005

9921006
# Main query `stmt` construction
@@ -1025,18 +1039,18 @@ def _build_stmt_for_dce(self, stmt, data_conditions, used_ids, k, v, user_id, va
10251039
depth = collection_type.count(":") if collection_type else 0
10261040

10271041
# Aliases for the target DCE's collection structure
1028-
_dce_target_root_ref = aliased(
1042+
_dce_target_root_ref = safe_aliased(
10291043
model.DatasetCollectionElement, name=f"_dce_target_root_ref_{k}_{value_index}"
10301044
)
1031-
_dce_target_child_collection_ref = aliased(
1045+
_dce_target_child_collection_ref = safe_aliased(
10321046
model.DatasetCollection, name=f"_dce_target_child_collection_ref_{k}_{value_index}"
10331047
)
10341048
# List of aliases for each potential nested level of DatasetCollectionElements
10351049
_dce_target_level_list = [
1036-
aliased(model.DatasetCollectionElement, name=f"_dce_target_level_{k}_{value_index}_{i}")
1050+
safe_aliased(model.DatasetCollectionElement, name=f"_dce_target_level_{k}_{value_index}_{i}")
10371051
for i in range(depth + 1)
10381052
]
1039-
_hda_target_ref = aliased(model.HistoryDatasetAssociation, name=f"_hda_target_ref_{k}_{value_index}")
1053+
_hda_target_ref = safe_aliased(model.HistoryDatasetAssociation, name=f"_hda_target_ref_{k}_{value_index}")
10401054

10411055
# --- CTE: reference_dce_all_dataset_ids_cte ---
10421056
# This CTE (Common Table Expression) identifies all distinct dataset IDs
@@ -1069,7 +1083,9 @@ def _build_stmt_for_dce(self, stmt, data_conditions, used_ids, k, v, user_id, va
10691083
.where(_dce_target_root_ref.id == v)
10701084
.distinct()
10711085
)
1072-
reference_all_dataset_ids_cte = reference_all_dataset_ids_select.cte(f"ref_all_ds_ids_{k}_{value_index}")
1086+
reference_all_dataset_ids_cte = reference_all_dataset_ids_select.cte(
1087+
safe_label_or_none(f"ref_all_ds_ids_{k}_{value_index}")
1088+
)
10731089

10741090
# --- CTE: reference_dce_signature_elements_cte ---
10751091
# This CTE generates a "path signature string" for each individual element
@@ -1110,7 +1126,7 @@ def _build_stmt_for_dce(self, stmt, data_conditions, used_ids, k, v, user_id, va
11101126
_hda_target_ref, _hda_target_ref.id == _leaf_target_dce_ref.hda_id
11111127
).where(_dce_target_root_ref.id == v)
11121128
reference_dce_signature_elements_cte = reference_dce_signature_elements_select.cte(
1113-
f"ref_dce_sig_els_{k}_{value_index}"
1129+
safe_label_or_none(f"ref_dce_sig_els_{k}_{value_index}")
11141130
)
11151131

11161132
# --- CTE: reference_full_signature_cte ---
@@ -1130,22 +1146,22 @@ def _build_stmt_for_dce(self, stmt, data_conditions, used_ids, k, v, user_id, va
11301146
), # Count elements based on path_signature_string
11311147
)
11321148
.select_from(reference_dce_signature_elements_cte)
1133-
.cte(f"ref_dce_full_sig_{k}_{value_index}")
1149+
.cte(safe_label_or_none(f"ref_dce_full_sig_{k}_{value_index}"))
11341150
)
11351151

11361152
# --- Aliases for Candidate Dataset Collection Structure ---
11371153
# These aliases are used to represent potential matching dataset collections
11381154
# in the database, which will be compared against the reference.
1139-
candidate_dce_root = aliased(model.DatasetCollectionElement, name=f"candidate_dce_root_{k}_{v}")
1140-
candidate_dce_child_collection = aliased(
1155+
candidate_dce_root = safe_aliased(model.DatasetCollectionElement, name=f"candidate_dce_root_{k}_{v}")
1156+
candidate_dce_child_collection = safe_aliased(
11411157
model.DatasetCollection, name=f"candidate_dce_child_collection_{k}_{value_index}"
11421158
)
11431159
candidate_dce_level_list = [
1144-
aliased(model.DatasetCollectionElement, name=f"candidate_dce_level_{k}_{value_index}_{i}")
1160+
safe_aliased(model.DatasetCollectionElement, name=f"candidate_dce_level_{k}_{value_index}_{i}")
11451161
for i in range(depth + 1)
11461162
]
1147-
candidate_hda = aliased(model.HistoryDatasetAssociation, name=f"candidate_hda_{k}_{value_index}")
1148-
candidate_history = aliased(model.History, name=f"candidate_history_{k}_{value_index}")
1163+
candidate_hda = safe_aliased(model.HistoryDatasetAssociation, name=f"candidate_hda_{k}_{value_index}")
1164+
candidate_history = safe_aliased(model.History, name=f"candidate_history_{k}_{value_index}")
11491165

11501166
# --- CTE: candidate_dce_pre_filter_ids_cte (Initial Candidate Filtering) ---
11511167
# This CTE performs a first pass to quickly narrow down potential candidate
@@ -1183,7 +1199,7 @@ def _build_stmt_for_dce(self, stmt, data_conditions, used_ids, k, v, user_id, va
11831199
.where(candidate_hda.dataset_id.in_(select(reference_all_dataset_ids_cte.c.ref_dataset_id_for_overlap)))
11841200
)
11851201
candidate_dce_pre_filter_ids_cte = candidate_dce_pre_filter_ids_select.cte(
1186-
f"cand_dce_pre_filter_ids_{k}_{value_index}"
1202+
safe_label_or_none(f"cand_dce_pre_filter_ids_{k}_{value_index}")
11871203
)
11881204

11891205
# --- CTE: candidate_dce_signature_elements_cte ---
@@ -1232,7 +1248,7 @@ def _build_stmt_for_dce(self, stmt, data_conditions, used_ids, k, v, user_id, va
12321248
.where(or_(candidate_history.published == true(), candidate_history.user_id == user_id))
12331249
)
12341250
candidate_dce_signature_elements_cte = candidate_dce_signature_elements_select.cte(
1235-
f"cand_dce_sig_els_{k}_{value_index}"
1251+
safe_label_or_none(f"cand_dce_sig_els_{k}_{value_index}")
12361252
)
12371253

12381254
# --- CTE: candidate_pre_signatures_cte (Candidate Aggregation for Comparison) ---
@@ -1252,7 +1268,7 @@ def _build_stmt_for_dce(self, stmt, data_conditions, used_ids, k, v, user_id, va
12521268
)
12531269
.select_from(candidate_dce_signature_elements_cte)
12541270
.group_by(candidate_dce_signature_elements_cte.c.candidate_dce_id)
1255-
.cte(f"cand_dce_pre_sig_{k}_{value_index}")
1271+
.cte(safe_label_or_none(f"cand_dce_pre_sig_{k}_{value_index}"))
12561272
)
12571273

12581274
# --- CTE: filtered_cand_dce_by_dataset_ids_cte (Filtering by Element Count and Dataset ID Array) ---
@@ -1272,7 +1288,7 @@ def _build_stmt_for_dce(self, stmt, data_conditions, used_ids, k, v, user_id, va
12721288
== reference_full_signature_cte.c.ordered_dataset_id_array,
12731289
)
12741290
)
1275-
.cte(f"filtered_cand_dce_{k}_{value_index}")
1291+
.cte(safe_label_or_none(f"filtered_cand_dce_{k}_{value_index}"))
12761292
)
12771293

12781294
# --- CTE: final_candidate_signatures_cte (Final Full Signature Calculation for Matched Candidates) ---
@@ -1293,13 +1309,13 @@ def _build_stmt_for_dce(self, stmt, data_conditions, used_ids, k, v, user_id, va
12931309
)
12941310
)
12951311
.group_by(candidate_dce_signature_elements_cte.c.candidate_dce_id)
1296-
.cte(f"final_cand_dce_full_sig_{k}_{value_index}")
1312+
.cte(safe_label_or_none(f"final_cand_dce_full_sig_{k}_{value_index}"))
12971313
)
12981314

12991315
# --- Main Query Construction for Dataset Collection Elements ---
13001316
# This section joins the main `stmt` (representing jobs) with the CTEs
13011317
# to filter jobs whose input DCE matches the reference DCE's full signature.
1302-
a = aliased(
1318+
a = safe_aliased(
13031319
model.JobToInputDatasetCollectionElementAssociation,
13041320
name=f"job_to_input_dce_association_{k}_{value_index}",
13051321
)
@@ -1334,19 +1350,19 @@ def _build_stmt_for_dce(self, stmt, data_conditions, used_ids, k, v, user_id, va
13341350
# This logic needs to align with how this type of DCE was previously matched.
13351351

13361352
# Aliases for the "left" side (job to input DCE path)
1337-
a = aliased(
1353+
a = safe_aliased(
13381354
model.JobToInputDatasetCollectionElementAssociation,
13391355
name=f"job_to_input_dce_association_{k}_{value_index}",
13401356
)
1341-
dce_left = aliased(model.DatasetCollectionElement, name=f"dce_left_{k}_{value_index}")
1342-
hda_left = aliased(model.HistoryDatasetAssociation, name=f"hda_left_{k}_{value_index}")
1357+
dce_left = safe_aliased(model.DatasetCollectionElement, name=f"dce_left_{k}_{value_index}")
1358+
hda_left = safe_aliased(model.HistoryDatasetAssociation, name=f"hda_left_{k}_{value_index}")
13431359

13441360
# Aliases for the "right" side (target DCE path in the main query)
1345-
dce_right = aliased(model.DatasetCollectionElement, name=f"dce_right_{k}_{value_index}")
1346-
hda_right = aliased(model.HistoryDatasetAssociation, name=f"hda_right_{k}_{value_index}")
1361+
dce_right = safe_aliased(model.DatasetCollectionElement, name=f"dce_right_{k}_{value_index}")
1362+
hda_right = safe_aliased(model.HistoryDatasetAssociation, name=f"hda_right_{k}_{value_index}")
13471363

13481364
# Start joins from job → input DCE association → first-level DCE (left side)
1349-
labeled_col = a.dataset_collection_element_id.label(f"{k}_{value_index}")
1365+
labeled_col = a.dataset_collection_element_id.label(safe_label_or_none(f"{k}_{value_index}"))
13501366
stmt = stmt.add_columns(labeled_col)
13511367
stmt = stmt.join(a, a.job_id == model.Job.id)
13521368
stmt = stmt.join(dce_left, dce_left.id == a.dataset_collection_element_id)

0 commit comments

Comments
 (0)