You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by pi...@apache.org on 2026/04/20 19:08:41 UTC

(airflow) branch main updated: Fix run_id_pattern pipe OR operator dropping single-term edge cases (#65190)

This is an automated email from the ASF dual-hosted git repository.

pierrejeambrun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 43556cc3608 Fix run_id_pattern pipe OR operator dropping single-term edge cases (#65190)
43556cc3608 is described below

commit 43556cc36081f7ce7f9004503ee0cb81e02641e1
Author: Cole Heflin <75...@users.noreply.github.com>
AuthorDate: Mon Apr 20 12:08:33 2026 -0700

    Fix run_id_pattern pipe OR operator dropping single-term edge cases (#65190)
    
    When a pattern like "term|" or "|term" was passed, the OR-split guard
    `if len(search_terms) > 1` evaluated False for the single remaining
    term, falling through to `ilike("%term|%")` / `ilike("%|term%")` which
    matched nothing. Changing the guard to `if search_terms:` ensures a
    single valid term is used correctly.
    
    closes: #65129
---
 .../src/airflow/api_fastapi/common/parameters.py     |  2 +-
 .../tests/unit/api_fastapi/common/test_parameters.py | 20 ++++++++++++++++++++
 .../core_api/routes/public/test_dag_run.py           |  5 +++++
 3 files changed, 26 insertions(+), 1 deletion(-)

diff --git a/airflow-core/src/airflow/api_fastapi/common/parameters.py b/airflow-core/src/airflow/api_fastapi/common/parameters.py
index 91250b670cf..0206c9d0ac6 100644
--- a/airflow-core/src/airflow/api_fastapi/common/parameters.py
+++ b/airflow-core/src/airflow/api_fastapi/common/parameters.py
@@ -185,7 +185,7 @@ class _SearchParam(BaseParam[str]):
         val_str = str(self.value)
         if "|" in val_str:
             search_terms = [term.strip() for term in val_str.split("|") if term.strip()]
-            if len(search_terms) > 1:
+            if search_terms:
                 return select.where(or_(*(self.attribute.ilike(f"%{term}%") for term in search_terms)))
 
         return select.where(self.attribute.ilike(f"%{self.value}%"))
diff --git a/airflow-core/tests/unit/api_fastapi/common/test_parameters.py b/airflow-core/tests/unit/api_fastapi/common/test_parameters.py
index 40076137467..99fe13b3c60 100644
--- a/airflow-core/tests/unit/api_fastapi/common/test_parameters.py
+++ b/airflow-core/tests/unit/api_fastapi/common/test_parameters.py
@@ -128,3 +128,23 @@ class TestSearchParam:
         assert "OR" in sql
         assert "example_bash" in sql
         assert "example_python" in sql
+
+    def test_to_orm_pipe_with_trailing_pipe(self):
+        """Test that a trailing pipe is ignored and only the valid term is searched."""
+        param = _SearchParam(DagModel.dag_id).set_value("example_bash|")
+        statement = select(DagModel)
+        statement = param.to_orm(statement)
+
+        sql = str(statement.compile(compile_kwargs={"literal_binds": True}))
+        assert "example_bash" in sql
+        assert "|" not in sql
+
+    def test_to_orm_pipe_with_leading_pipe(self):
+        """Test that a leading pipe is ignored and only the valid term is searched."""
+        param = _SearchParam(DagModel.dag_id).set_value("|example_bash")
+        statement = select(DagModel)
+        statement = param.to_orm(statement)
+
+        sql = str(statement.compile(compile_kwargs={"literal_binds": True}))
+        assert "example_bash" in sql
+        assert "|" not in sql
diff --git a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_run.py b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_run.py
index 9d27392de92..5c13f09ef5a 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_run.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_run.py
@@ -662,6 +662,11 @@ class TestGetDagRuns:
             (DAG1_ID, {"run_id_pattern": "run_1"}, [DAG1_RUN1_ID]),
             (DAG1_ID, {"run_id_pattern": "dag_%_1"}, [DAG1_RUN1_ID]),
             ("~", {"run_id_pattern": "dag_run_"}, [DAG1_RUN1_ID, DAG1_RUN2_ID, DAG2_RUN1_ID, DAG2_RUN2_ID]),
+            # Pipe (OR) operator returns results matching either term
+            ("~", {"run_id_pattern": f"{DAG1_RUN1_ID}|{DAG1_RUN2_ID}"}, [DAG1_RUN1_ID, DAG1_RUN2_ID]),
+            # Trailing/leading pipe should not leak into the LIKE pattern
+            ("~", {"run_id_pattern": f"{DAG1_RUN1_ID}|"}, [DAG1_RUN1_ID]),
+            ("~", {"run_id_pattern": f"|{DAG1_RUN1_ID}"}, [DAG1_RUN1_ID]),
             (
                 DAG1_ID,
                 {