Airflow中OracleStoredProcedureOperator传DATE类型运行时参数问题
问题背景
在Apache Airflow中开发需接收运行时参数batch_dt的任务时,使用OracleStoredProcedureOperator调用Oracle存储过程,存储过程所有入参均为数据库DATE类型,定义如下:
procedure use_dates ( i_date in date, i_date2 in date, i_date3 in date );
要求传参方案不依赖数据库当前NLS设置,现有尝试均执行失败:
- 直接传入Airflow宏
{{ dag_run.conf['batch_dt'] }}或{{ macros.datetime.strptime(dag_run.conf['batch_dt'], '%Y-%m-%d') }}时,宏始终返回字符串类型,触发报错ORA-01861: literal does not match format string - 传入拼接的
to_date('{{ dag_run.conf['batch_dt'] }}', 'DD-MM-YYYY')字符串时,触发报错ORA-01858: a non-numeric character was found where a numeric was expected - 直接在任务参数中配置
date.today()可正常执行,但属于代码固定值,无法满足运行时动态传参需求
原有问题代码如下:
res_task = OracleStoredProcedureOperator( task_id = 'mytask', procedure = 'use_dates', parameters = {"i_date": date.today(), # 可执行但不是动态运行时值 "i_date2": "to_date('{{ dag_run.conf['batch_dt'] }}', 'DD-MM-YYYY')", # 传入值为字符串"to_date('13-06-2022', 'DD-MM-YYYY')",触发ORA-01858错误 "i_date3": "{{ macros.datetime.strptime(dag_run.conf['batch_dt'], '%Y-%m-%d' ) }}" # 传入值为字符串'2022-06-13 00:00:00',触发ORA-01861错误 } )
已知Airflow宏默认返回字符串类型,需要该场景下的可行实现方案。
失败原因说明
- 直接传宏渲染的字符串报错:数据库端将字符串转为DATE类型时会读取当前NLS_DATE_FORMAT配置,格式不匹配就会抛出ORA-01861,不符合不依赖NLS设置的要求
- 传拼接的
to_date()字符串报错:OracleStoredProcedureOperator采用绑定变量模式传参,参数值会被作为纯值传入,不会被当做SQL片段解析执行,因此to_date()函数不会被数据库运行,字符串直接传给DATE类型入参自然触发类型转换错误 date.today()可正常执行的原因:Python原生date/datetime对象传入后,底层Oracle驱动(cx_Oracle/python-oracledb)会自动完成类型映射,不需要依赖数据库NLS设置做字符串转日期的操作。
可行解决方案
Airflow 2.1及以上版本支持DAG级别的原生对象渲染配置,开启后Jinja模板的渲染结果不会被强制转为字符串,会保留原生Python类型,刚好适配这个场景。
实现步骤
- 在DAG初始化时添加参数*
render_template_as_native_obj=True*,开启原生对象渲染 - 存储过程的日期参数直接用
macros.datetime.strptime解析运行时batch_dt,渲染后会直接得到Python datetime对象,驱动会自动映射为Oracle DATE类型,全程无NLS依赖。
正确代码示例
from datetime import datetime, date from airflow import DAG from airflow.providers.oracle.operators.stored_procedure import OracleStoredProcedureOperator with DAG( dag_id="call_oracle_sp_with_runtime_dt", start_date=datetime(2024, 1, 1), schedule=None, # 核心配置:开启原生对象渲染,Jinja输出保留原Python类型 render_template_as_native_obj=True, catchup=False ) as dag: res_task = OracleStoredProcedureOperator( task_id="mytask", procedure="use_dates", parameters={ "i_date": date.today(), # 渲染后得到Python datetime对象,驱动自动适配Oracle DATE类型 "i_date2": "{{ macros.datetime.strptime(dag_run.conf['batch_dt'], '%Y-%m-%d') }}", "i_date3": "{{ macros.datetime.strptime(dag_run.conf['batch_dt'], '%Y-%m-%d') }}" } )
替代方案(不开启全局原生渲染)
如果不想在整个DAG层面开启原生对象渲染,可以新增前置Python任务,将运行时batch_dt解析为datetime对象后通过XCom传递给存储过程任务,核心逻辑一致:只要传入parameters的是Python原生date/datetime对象,就可以被驱动正确识别,不会触发NLS相关报错。
注意:永远不要在OracleStoredProcedureOperator的参数值中拼接SQL函数(如to_date、sysdate等),绑定变量传参模式下这类内容不会被当做SQL执行,只会被当做普通字符串值传入。
内容的提问来源于stack exchange,提问作者Ivan
相关产品推荐
相关产品推荐

