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


##########
airflow-core/src/airflow/api_fastapi/common/parameters.py:
##########
@@ -267,6 +268,74 @@ def depends_search(
     return depends_search
 
 
+class _RegexParam(BaseParam[str]):
+    """Filter using database-level regex matching (regexp_match).
+
+    On SQLite, falls back to exact-match (``==``) because SQLite does not
+    natively support ``regexp_match``.
+    """
+
+    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
+        from airflow.settings import engine
+
+        if engine and engine.dialect.name == "sqlite":
+            return select.where(self.attribute == self.value)
+        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.
+
+    On SQLite the value is used as an exact-match string, so no regex
+    validation is performed.
+    """
+    if value is None:
+        return value
+    import re
+
+    from airflow.settings import engine
+
+    if engine and engine.dialect.name == "sqlite":
+        return value

Review Comment:
   New code introduces local imports inside functions (`from airflow.settings 
import engine` in `to_orm()` and `import re`/`from airflow.settings import 
engine` in `_validate_regex_pattern`). Airflow guidelines prefer module-level 
imports unless there is a clear need (circular import/lazy-load); please move 
these imports to the top of the module or add justification for keeping them 
local.



##########
task-sdk/src/airflow/sdk/api/client.py:
##########
@@ -685,6 +686,8 @@ def get(
         common_params["ascending"] = ascending
         if limit:
             common_params["limit"] = limit
+        if partition_key is not None:
+            common_params["partition_key_pattern"] = partition_key
         if name or uri:

Review Comment:
   SDK client parameter name `partition_key` is forwarded as the HTTP query 
param `partition_key_pattern` (regex). The current name is ambiguous (suggests 
exact match) and differs from the server/API naming. Consider renaming the SDK 
argument (and corresponding supervisor/comms fields) to `partition_key_pattern` 
(or similar) to make the regex semantics explicit and keep naming consistent 
across layers.



##########
airflow-core/src/airflow/api_fastapi/common/parameters.py:
##########
@@ -267,6 +268,74 @@ def depends_search(
     return depends_search
 
 
+class _RegexParam(BaseParam[str]):
+    """Filter using database-level regex matching (regexp_match).
+
+    On SQLite, falls back to exact-match (``==``) because SQLite does not
+    natively support ``regexp_match``.
+    """
+
+    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
+        from airflow.settings import engine
+
+        if engine and engine.dialect.name == "sqlite":
+            return select.where(self.attribute == self.value)
+        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.
+
+    On SQLite the value is used as an exact-match string, so no regex
+    validation is performed.
+    """
+    if value is None:
+        return value
+    import re
+
+    from airflow.settings import engine
+
+    if engine and engine.dialect.name == "sqlite":
+        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
+
+
+def regex_param_factory(
+    attribute: ColumnElement,
+    pattern_name: str,
+    skip_none: bool = True,
+) -> Callable[[str | None], _RegexParam]:
+    DESCRIPTION = (
+        "Partition key filter. Uses database-native regex "
+        "(PostgreSQL ~ operator, MySQL REGEXP). On SQLite, only exact match is 
supported."
+    )
+
+    def depends_regex(
+        value: str | None = Query(alias=pattern_name, default=None, 
description=DESCRIPTION),
+    ) -> _RegexParam:
+        _validate_regex_pattern(value)
+        return _RegexParam(attribute, skip_none).set_value(value)
+
+    return depends_regex

Review Comment:
   `regex_param_factory()` is written as a reusable helper (it accepts an 
arbitrary `attribute` and `pattern_name`), but the query description is 
hard-coded to “Partition key filter…”. This will be misleading if/when the 
factory is reused. Consider passing `description` into the factory (or using a 
generic regex description) and letting callers specialize it.



##########
airflow-core/src/airflow/api_fastapi/common/parameters.py:
##########
@@ -267,6 +268,74 @@ def depends_search(
     return depends_search
 
 
+class _RegexParam(BaseParam[str]):
+    """Filter using database-level regex matching (regexp_match).
+
+    On SQLite, falls back to exact-match (``==``) because SQLite does not
+    natively support ``regexp_match``.
+    """
+
+    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
+        from airflow.settings import engine
+
+        if engine and engine.dialect.name == "sqlite":
+            return select.where(self.attribute == self.value)
+        return select.where(self.attribute.regexp_match(self.value))
+

Review Comment:
   `_RegexParam.to_orm()` will call `regexp_match(self.value)` when `skip_none` 
is False and `value` is None, which can produce incorrect SQL/behavior (regex 
against NULL) on non-SQLite backends. Consider handling the `value is None` 
case explicitly (e.g. `IS NULL`/`== None`) before applying regex matching, 
regardless of dialect.



##########
airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts:
##########
@@ -2301,6 +2301,10 @@ export type GetAssetEventsData = {
      * Attributes to order by, multi criteria sort is supported. Prefix with 
`-` for descending order. Supported attributes: `source_task_id, source_dag_id, 
source_run_id, source_map_index, timestamp`
      */
     orderBy?: Array<(string)>;
+    /**
+     * Regular expression pattern for filtering. Uses database-native regex 
(PostgreSQL ~ operator, MySQL REGEXP). Not supported on SQLite.
+     */
+    partitionKeyPattern?: string | null;

Review Comment:
   This file is marked as auto-generated (`@hey-api/openapi-ts`). Please ensure 
the new `partitionKeyPattern` field comes from regenerating the OpenAPI client 
artifacts rather than manual edits, and document/keep the generation command in 
sync so future regenerations don’t drop these changes.
   ```suggestion
   
   ```



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