hussein-awala commented on code in PR #64610:
URL: https://github.com/apache/airflow/pull/64610#discussion_r3147329270
##########
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:
The `re.compile()` pre-validation is intentional as a pragmatic tradeoff. It
catches genuinely broken patterns (unbalanced parens, bad escapes, etc.) and
returns a clear 400 with a descriptive message, rather than letting them
propagate to the database and produce a cryptic 500 with raw DB exception
details.
The common regex subset used for partition key filtering (`^`, `$`, `.`,
`*`, `+`, `?`, `|`, `[...]`, `(...)`) is shared across Python, PostgreSQL POSIX
regex, and MySQL regex.
**Patterns that pass Python validation but fail on the DB** (not a real
problem -- the DB will reject them at query time and we can surface that as an
error):
- Lookaheads/lookbehinds: `(?=foo)`, `(?!foo)`, `(?<=foo)`, `(?<!foo)` --
unsupported in PostgreSQL POSIX regex and MySQL
- Non-capturing groups: `(?:foo)` -- unsupported in PostgreSQL POSIX regex
- Named groups: `(?P<name>...)` -- unsupported in both PostgreSQL and MySQL
- Python shorthand classes: `\d`, `\w`, `\s` -- PostgreSQL uses POSIX
classes like `[[:digit:]]` instead; MySQL has partial support
**Patterns that are valid on the DB but rejected by Python validation**
(more problematic -- the user gets a 400 even though the DB would accept them):
- POSIX character classes: `[[:alpha:]]`, `[[:digit:]]` -- valid in
PostgreSQL and MySQL, but `re.compile()` raises `re.error`
In practice, none of these features are useful for partition key filtering
(which typically involves simple prefix/suffix/substring matching). But the
second category is the real concern: a PostgreSQL user writing `[[:digit:]]{4}`
would get a misleading 400. If this becomes an issue we can relax the
validation, but for now the tradeoff is acceptable given the expected usage
patterns.
##########
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:
same as below
--
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]