diff --git a/providers/oracle/src/airflow/providers/oracle/transfers/oracle_to_oracle.py b/providers/oracle/src/airflow/providers/oracle/transfers/oracle_to_oracle.py index da4cfb66b9d4a..554658a46781c 100644 --- a/providers/oracle/src/airflow/providers/oracle/transfers/oracle_to_oracle.py +++ b/providers/oracle/src/airflow/providers/oracle/transfers/oracle_to_oracle.py @@ -56,8 +56,7 @@ def __init__( **kwargs, ) -> None: super().__init__(**kwargs) - if source_sql_params is None: - source_sql_params = {} + self.oracle_destination_conn_id = oracle_destination_conn_id self.destination_table = destination_table self.oracle_source_conn_id = oracle_source_conn_id @@ -84,6 +83,8 @@ def _execute(self, src_hook, dest_hook, context) -> None: cursor.close() def execute(self, context: Context) -> None: + if self.source_sql_params is None: + self.source_sql_params = {} src_hook = OracleHook(oracle_conn_id=self.oracle_source_conn_id) dest_hook = OracleHook(oracle_conn_id=self.oracle_destination_conn_id) self._execute(src_hook, dest_hook, context) diff --git a/providers/oracle/tests/unit/oracle/transfers/test_oracle_to_oracle.py b/providers/oracle/tests/unit/oracle/transfers/test_oracle_to_oracle.py index e2e66706da852..fccf1428126d9 100644 --- a/providers/oracle/tests/unit/oracle/transfers/test_oracle_to_oracle.py +++ b/providers/oracle/tests/unit/oracle/transfers/test_oracle_to_oracle.py @@ -68,3 +68,30 @@ def test_execute(self): mock_dest_hook.bulk_insert_rows.assert_called_once_with( destination_table, cursor_rows, commit_every=rows_chunk, target_fields=["id", "description"] ) + + @mock.patch("airflow.providers.oracle.transfers.oracle_to_oracle.OracleHook") + def test_source_sql_params_defaults_to_empty_dict_at_execute(self, mock_oracle_hook): + """ + `source_sql_params` is a template field, so its None -> {} default must be + applied in execute() (after rendering), not in __init__. + """ + op = OracleToOracleOperator( + task_id="copy_data", + oracle_destination_conn_id="dest_conn", + destination_table="destination_table", + oracle_source_conn_id="source_conn", + source_sql="select 1 from dual", + ) + + # __init__ keeps the value verbatim; the {} default is deferred to execute(). + assert op.source_sql_params is None + + mock_conn = mock_oracle_hook.return_value.get_conn.return_value.__enter__.return_value + mock_cursor = mock_conn.cursor.return_value + mock_cursor.description = [] + mock_cursor.fetchmany.side_effect = [[]] + + op.execute(context={}) + + assert op.source_sql_params == {} + mock_cursor.execute.assert_called_once_with("select 1 from dual", {}) diff --git a/scripts/ci/prek/validate_operators_init_exemptions.txt b/scripts/ci/prek/validate_operators_init_exemptions.txt index e192ef70bafb8..0dc388ff9bbd8 100644 --- a/scripts/ci/prek/validate_operators_init_exemptions.txt +++ b/scripts/ci/prek/validate_operators_init_exemptions.txt @@ -39,6 +39,6 @@ providers/google/src/airflow/providers/google/marketing_platform/operators/campa providers/microsoft/azure/src/airflow/providers/microsoft/azure/transfers/gcs_to_wasb.py::GCSToAzureBlobStorageOperator providers/microsoft/azure/src/airflow/providers/microsoft/azure/transfers/oracle_to_azure_data_lake.py::OracleToAzureDataLakeOperator providers/microsoft/psrp/src/airflow/providers/microsoft/psrp/operators/psrp.py::PsrpOperator -providers/oracle/src/airflow/providers/oracle/transfers/oracle_to_oracle.py::OracleToOracleOperator +providers/papermill/src/airflow/providers/papermill/operators/papermill.py::PapermillOperator providers/standard/src/airflow/providers/standard/operators/bash.py::BashOperator providers/standard/src/airflow/providers/standard/operators/trigger_dagrun.py::TriggerDagRunOperator