Copilot commented on code in PR #64610:
URL: https://github.com/apache/airflow/pull/64610#discussion_r3147217105


##########
airflow-core/src/airflow/api_fastapi/common/parameters.py:
##########
@@ -442,6 +443,66 @@ def depends_search(
     return depends_search
 
 
+class _RegexParam(BaseParam[str]):
+    """
+    Filter using database-level regex matching (regexp_match).
+
+    SQLAlchemy's ``regexp_match`` is supported on PostgreSQL (``~`` operator),
+    MySQL/MariaDB (``REGEXP``), and SQLite (via Python ``re.match``).
+    Note: SQLite uses ``re.match`` semantics (anchored at the start of the
+    string), while PostgreSQL uses ``re.search`` (matches anywhere).
+    """
+
+    def __init__(self, attribute: ColumnElement, skip_none: bool = True) -> 
None:
+        super().__init__(skip_none=skip_none)
+        self.attribute: ColumnElement = attribute
+
+    def to_orm(self, select: Select) -> Select:
+        if self.value is None and self.skip_none:
+            return select
+        return select.where(self.attribute.regexp_match(self.value))
+
+    @classmethod
+    def depends(cls, *args: Any, **kwargs: Any) -> Self:
+        raise NotImplementedError("Use regex_param_factory instead, depends is 
not implemented.")
+
+
+def _validate_regex_pattern(value: str | None) -> str | None:
+    """Validate that the regex pattern is syntactically correct."""
+    if value is None:
+        return value
+    try:
+        re.compile(value)
+    except re.error as e:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=f"Invalid regular expression: {e}",
+        )
+    return value
+
+
+_DEFAULT_REGEX_DESCRIPTION = (
+    "Regex filter. Uses database-native regex "
+    "(PostgreSQL ~ operator, MySQL REGEXP, SQLite re.match). "

Review Comment:
   Regex validation is done with `re.compile()`, but the actual matching is 
performed by the database regex engine (PostgreSQL POSIX regex, MySQL regex, 
SQLite `re.match`). This can incorrectly reject patterns that are valid on some 
backends (or accept patterns that later error on the DB). Either document that 
`partition_key_pattern` must be compatible with Python `re` (a cross-backend 
subset), or move validation to a backend-aware check / let the database raise 
on invalid patterns.
   ```suggestion
       """
       Return the regex pattern unchanged.
   
       Regex matching is performed by the database backend via ``regexp_match``,
       so Python ``re`` compilation is not a reliable cross-backend validator:
       PostgreSQL and MySQL/MariaDB have different regex engines from Python,
       while SQLite uses ``re.match`` semantics. Invalid patterns should be
       rejected by the backend that actually evaluates them.
       """
       return value
   
   
   _DEFAULT_REGEX_DESCRIPTION = (
       "Regex filter. Uses database-native regex "
       "(PostgreSQL ~ operator, MySQL REGEXP, SQLite re.match). "
       "Pattern syntax must be compatible with the active database backend; "
       "invalid patterns may be rejected when the database evaluates the query. 
"
   ```



##########
airflow-core/src/airflow/api_fastapi/execution_api/routes/asset_events.py:
##########
@@ -73,6 +74,31 @@ def _get_asset_events_through_sql_clauses(
     )
 
 
+def _validate_partition_key_params(partition_key: str | None, 
partition_key_pattern: str | None) -> None:
+    """Validate partition_key and partition_key_pattern are not both set, and 
that the pattern is valid regex."""
+    if partition_key is not None and partition_key_pattern is not None:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail={
+                "reason": "Mutually exclusive parameters",
+                "message": "partition_key and partition_key_pattern cannot 
both be provided. "
+                "Use partition_key for exact match or partition_key_pattern 
for regex filtering.",
+            },
+        )
+    if partition_key_pattern is None:
+        return
+    try:
+        re.compile(partition_key_pattern)
+    except re.error as e:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,

Review Comment:
   `partition_key_pattern` is validated with Python `re.compile()`, but the 
filter is executed via the database regex operator. This can lead to false 
negatives/positives across backends (e.g. DB-specific regex tokens rejected by 
Python, or Python-valid patterns rejected by the DB). Consider aligning this 
with the core API helper (and documenting the supported regex flavor), or 
handling validation in a backend-aware way.



##########
airflow-core/docs/authoring-and-scheduling/assets.rst:
##########
@@ -547,5 +561,33 @@ including ``partition_key`` in the request body):
         "partition_key": "us|2026-03-10T09:00:00"
       }'
 
+You can also query asset events filtered by partition key using the REST API.
+Two parameters are available:
+
+- ``partition_key`` for **exact match** — uses the B-tree index for fast 
lookups:
+
+.. code-block:: bash
+
+    curl 
"http://<airflow-host>/api/v2/assets/events?partition_key=us|2026-03-10"
+
+- ``partition_key_pattern`` for **regex filtering** — uses database-native 
regex
+  (PostgreSQL ``~`` operator, MySQL ``REGEXP``, SQLite ``re.match``):
+
+.. code-block:: bash
+
+    curl "http://<airflow-host>/api/v2/assets/events?partition_key_pattern=^us"

Review Comment:
   The curl example includes an unescaped `|` character in the query string. 
`|` is not a valid URL character per RFC 3986 and may be rejected by 
proxies/clients unless percent-encoded. Consider updating the example to 
URL-encode reserved characters (e.g. `%7C`, `%5E`) or use `curl 
--data-urlencode` to avoid copy/paste failures.
   ```suggestion
       curl 
"http://<airflow-host>/api/v2/assets/events?partition_key=us%7C2026-03-10"
   
   - ``partition_key_pattern`` for **regex filtering** — uses database-native 
regex
     (PostgreSQL ``~`` operator, MySQL ``REGEXP``, SQLite ``re.match``):
   
   .. code-block:: bash
   
       curl 
"http://<airflow-host>/api/v2/assets/events?partition_key_pattern=%5Eus"
   ```



##########
asset-event-query-proposal.md:
##########
@@ -0,0 +1,250 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ -->
+
+# Proposal: Enhanced Asset Event Querying in Airflow — Towards Deprecating 
Census
+
+**Author:** Hussein Awala
+**Date:** April 2026
+**Status:** In progress — community discussion pending
+**Target Release:** Apache Airflow 3.3
+
+---
+
+## TL;DR
+
+I'm contributing a set of features to Apache Airflow that will make **Asset 
Events** a first-class mechanism for tracking dataset watermarks, metadata, and 
cross-pipeline coordination — covering the core use cases currently served by 
Census. Because Airflow is the **standard scheduler at Datadog**, building 
these capabilities natively means every team already using Airflow gets them 
for free, with better performance, simpler integrations, and no extra 
infrastructure.
+
+This document describes my four-step plan, the current status of each step, 
and how it maps to the Census deprecation effort.
+
+---
+
+## Context
+
+### Census today
+
+Census stores watermarks, metadata, and scope markers in **JSONB columns** 
where every field — dataset name, partition key, extras — lives inside JSON 
blobs. Even though GIN indexes exist, the dataset name itself is a JSONB field, 
so queries cannot efficiently narrow down rows before scanning JSON content. 
This design:
+
+- **Limits query performance** — filtering by dataset name still requires a 
GIN index lookup into JSONB rather than a simple B-tree index scan on a 
dedicated column, and combining it with partition or extra filters compounds 
the cost.
+- **Requires a separate service** to run and operate alongside Airflow.
+- **Duplicates scheduling metadata** that Airflow already manages through 
Asset Events.
+
+Airflow does support **XCom** for inter-task communication, and XCom is 
already replacing some Census usage. However, reading an XCom value requires 
knowing the exact `dag_id`, `run_id`, and `task_id` of the producer, which is 
not always possible — and more importantly, it cannot serve as a **data 
contract between teams**, since each team needs the flexibility to restructure 
their DAGs and internal tasks without breaking consumers. Asset Events solve 
this by decoupling the contract (asset name + partition key + extra metadata) 
from the internal DAG structure.
+
+For background on how Census is used today and the roadmap discussion, see:
+
+- [Luigi — Using Census to track datasets execution 
tokens](https://datadoghq.atlassian.net/wiki/spaces/adp/pages/2867364337/Luigi+-+Using+Census+to+track+datasets+execution+tokens)
+- [Future of Census — As of 
2026.01](https://datadoghq.atlassian.net/wiki/spaces/adp/pages/5366547407/Future+of+Census+-+As+of+2026.01)

Review Comment:
   This proposal doc contains company-specific statements and references to 
private/internal URLs (e.g. datadoghq.atlassian.net). These links won't be 
accessible to most contributors/users and are generally not appropriate to keep 
in the Apache Airflow repo. Suggest removing the internal references/links, 
moving this document to an external/public location, or rewriting it as an 
Airflow-focused design doc with public references only.



-- 
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