@@ -114,7 +114,8 @@ def _transfer_hook(self, session: Session) -> None:
114114 """
115115 Override transfer hook to use batch upsert for idempotent transfers.
116116
117- Uses ON CONFLICT DO UPDATE on nma_GlobalID (the legacy UUID PK, now UNIQUE).
117+ Uses ON CONFLICT DO UPDATE on (chemistry_sample_info_id, analyte),
118+ matching uq_minor_trace_chemistry_sample_analyte.
118119 """
119120 df = self .cleaned_df
120121
@@ -129,8 +130,12 @@ def _transfer_hook(self, session: Session) -> None:
129130 logger .warning ("No valid rows to transfer" )
130131 return
131132
132- # Dedupe by nma_GlobalID to avoid PK conflicts.
133- rows = self ._dedupe_rows (row_dicts )
133+ # Dedupe by the same logical key used by the table unique constraint.
134+ rows = self ._dedupe_rows (
135+ row_dicts ,
136+ key = ["chemistry_sample_info_id" , "analyte" ],
137+ include_missing = True ,
138+ )
134139 logger .info (f"Upserting { len (rows )} MinorTraceChemistry records" )
135140
136141 insert_stmt = insert (NMA_MinorTraceChemistry )
@@ -139,9 +144,9 @@ def _transfer_hook(self, session: Session) -> None:
139144 for i in range (0 , len (rows ), self .batch_size ):
140145 chunk = rows [i : i + self .batch_size ]
141146 logger .info (f"Upserting batch { i } -{ i + len (chunk )- 1 } ({ len (chunk )} rows)" )
142- # Upsert on nma_GlobalID (legacy UUID PK, now UNIQUE )
147+ # Upsert on unique logical key (chemistry_sample_info_id, analyte )
143148 stmt = insert_stmt .values (chunk ).on_conflict_do_update (
144- index_elements = ["nma_GlobalID " ],
149+ index_elements = ["chemistry_sample_info_id" , "analyte " ],
145150 set_ = {
146151 "chemistry_sample_info_id" : excluded .chemistry_sample_info_id ,
147152 "nma_chemistry_sample_info_uuid" : excluded .nma_chemistry_sample_info_uuid ,
0 commit comments