uranusjr commented on code in PR #65618:
URL: https://github.com/apache/airflow/pull/65618#discussion_r3975813046


##########
providers/common/sql/src/airflow/providers/common/sql/operators/sql.py:
##########
@@ -554,29 +624,78 @@ def _should_run_output_processing(self) -> bool:
 
     def execute(self, context):
         self.log.info("Executing: %s", self.sql)
-        hook = self.get_db_hook()
-        if self.split_statements is not None:
-            extra_kwargs = {"split_statements": self.split_statements}
+        if self.deferrable:
+            if self.enforce_read_only:
+                is_write, reason = scan_for_writes(self.sql)
+                if is_write:
+                    raise ValueError(
+                        f"enforce_read_only=True but the SQL appears to 
contain a write: {reason}. "
+                        "Set enforce_read_only=False if the query is 
idempotent, or deferrable=False "
+                        "to run it on the worker."
+                    )
+            self.defer(
+                trigger=SQLExecuteQueryTrigger(
+                    sql=self.sql,
+                    conn_id=self.conn_id,
+                    autocommit=self.autocommit,
+                    parameters=self.parameters,
+                    fetch_results=self._should_run_output_processing() or 
self.requires_result_fetch,
+                    split_statements=self.split_statements,
+                    return_last=self.return_last,
+                    read_only=self.enforce_read_only,
+                ),
+                method_name="execute_complete",
+            )
         else:
-            extra_kwargs = {}
-        output = hook.run(
-            sql=self.sql,
-            autocommit=self.autocommit,
-            parameters=self.parameters,
-            handler=self.handler
-            if self._should_run_output_processing() or 
self.requires_result_fetch
-            else None,
-            return_last=self.return_last,
-            **extra_kwargs,
-        )
-        if not self._should_run_output_processing():
+            hook = self.get_db_hook()
+            if self.split_statements is not None:
+                extra_kwargs = {"split_statements": self.split_statements}
+            else:
+                extra_kwargs = {}
+            output = hook.run(
+                sql=self.sql,
+                autocommit=self.autocommit,
+                parameters=self.parameters,
+                handler=self.handler
+                if self._should_run_output_processing() or 
self.requires_result_fetch
+                else None,
+                return_last=self.return_last,
+                **extra_kwargs,
+            )
+            if not self._should_run_output_processing():
+                return None
+            if return_single_query_results(self.sql, self.return_last, 
self.split_statements):
+                # For simplicity, we pass always list as input to 
_process_output, regardless if
+                # single query results are going to be returned, and we return 
the first element
+                # of the list in this case from the (always) list returned by 
_process_output
+                return self._process_output([output], hook.descriptions)[-1]
+            result = self._process_output(output, hook.descriptions)
+            self.log.info("result: %s", result)
+            return result
+
+    def execute_complete(self, context: Context, event: dict[str, Any] | None 
= None) -> Any:
+        if event is None:
+            raise RuntimeError("Unknown error in SQLExecuteQueryTrigger")
+        if event.get("status") == "error":
+            raise RuntimeError(event.get("message", "Unknown error in 
SQLExecuteQueryTrigger"))
+        self.log.info("SQL query executed successfully.")
+        results = event.get("results")
+        if not self._should_run_output_processing() or results is None:
             return None
+        descriptions: list[Sequence[Sequence] | None] = 
event.get("descriptions") or []
         if return_single_query_results(self.sql, self.return_last, 
self.split_statements):
-            # For simplicity, we pass always list as input to _process_output, 
regardless if
-            # single query results are going to be returned, and we return the 
first element
-            # of the list in this case from the (always) list returned by 
_process_output
-            return self._process_output([output], hook.descriptions)[-1]
-        result = self._process_output(output, hook.descriptions)
+            # The trigger returns the rows of a single statement; apply the 
handler on the worker, where
+            # it is defined, over a replayed cursor rather than in the 
triggerer's event loop.
+            rows = [results] if isinstance(results, str) else results

Review Comment:
   When would a fetched result be a bare str here?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to