Skip to content

Conversation

@kaxil
Copy link
Member

@kaxil kaxil commented Aug 14, 2025

Changes from rc1: #54508


^ Add meaningful description above
Read the Pull Request Guidelines for more information.
In case of fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
In case of a new dependency, check compliance with the ASF 3rd Party License Policy.
In case of backwards incompatible changes please leave a note in a newsfragment file, named {pr_number}.significant.rst or {issue_number}.significant.rst, in airflow-core/newsfragments.

vatsrahul1001 and others added 2 commits August 14, 2025 18:58
Fixes critical blocking issues when downgrading Airflow from 3.x to 2.x across PostgreSQL, MySQL, and SQLite databases. These issues were discovered after [PR apache#54399](apache#54399) unblocked the downgrade process.

**Error:**
```
sqlalchemy.exc.IntegrityError: (psycopg2.errors.NotNullViolation)
column "try_number" of relation "task_reschedule" contains null values
```

**Root Cause:** Migration [`0068_3_0_0_ti_table_id_unique_per_try.py:L99`](https://github.com/apache/airflow/blob/main/airflow-core/src/airflow/migrations/versions/0068_3_0_0_ti_table_id_unique_per_try.py#L99) uses `default="1"` instead of `server_default="1"`, causing existing NULL values to remain NULL when the column is made NOT NULL.

**Solution:** Replace `default="1"` with `server_default="1"` to ensure database-level default value assignment.

**Error:**
```
sqlalchemy.exc.IntegrityError: (psycopg2.errors.NotNullViolation)
column "task_instance_id" of relation "task_instance_history" contains null values
```

**Root Cause:** The `task_instance_id` column is made NOT NULL during downgrade but contains NULL values, even though this column gets dropped entirely in the same migration.

**Solution:** Make `task_instance_id` column nullable since it's immediately dropped and not needed in 2.x schema.

**Error:**
```
sqlalchemy.exc.OperationalError: (MySQLdb.OperationalError) (1138, 'Invalid use of NULL value')
```

**Root Cause:** MySQL query attempts to JOIN on NULL `id` values in `task_instance_history`:
```sql
UPDATE task_instance_history tih
JOIN (
    SELECT id, ROW_NUMBER() OVER (ORDER BY id) AS row_num
    FROM task_instance_history
) AS temp ON tih.id = temp.id
SET tih.id = temp.row_num;
```
The `id` column was just re-added as nullable with all NULL values, making the JOIN fail.

**Solution:** Replace with MySQL variable-based sequential numbering:
```sql
SET @row_number = 0;
UPDATE task_instance_history
SET id = (@row_number := @row_number + 1)
ORDER BY try_id;
```

**Error:**
```
sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) FOREIGN KEY constraint failed
```

**Root Cause:** SQLite's strict foreign key checking prevents dropping the `dag` table during `batch_alter_table` operations when other tables reference it.

**Solution:** Add SQLite-specific handling to temporarily disable foreign key constraints:
```python
if dialect_name == "sqlite":
    conn.execute(text("PRAGMA foreign_keys=OFF"))
    try:
        # batch operations
    finally:
        conn.execute(text("PRAGMA foreign_keys=ON"))
```
Verified successful downgrade from Airflow 3.0.5rc1 to 2.11 on:
- [x] PostgreSQL
- [x] MySQL
- [x] SQLite

(cherry picked from commit bc18493)
@kaxil kaxil merged commit 6888a3e into apache:v3-0-stable Aug 14, 2025
233 of 234 checks passed
@kaxil kaxil deleted the 3.0.5rc2-changes branch August 14, 2025 19:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants