seanmuth opened a new issue, #69063:
URL: https://github.com/apache/airflow/issues/69063

   ### Apache Airflow version
   
   3.2.2
   
   ### What happened?
   
   Upgrading the metadata DB from 2.x to 3.x fails during migration 
**`0049_3_0_0_remove_pickled_data_from_xcom_table`** (revision `eed27faa34e3`) 
when any historical XCom value contains a Unicode NUL escape (`\u0000`). 
PostgreSQL's `json`/`jsonb` type cannot represent `\u0000`, so the bulk `ALTER 
COLUMN value TYPE JSONB USING CAST(CONVERT_FROM(value, 'UTF8') AS JSONB)` 
aborts the **entire** migration:
   
   ```
   sqlalchemy.exc.DataError: (psycopg2.errors.UntranslatableCharacter) 
unsupported Unicode escape sequence
   DETAIL: \u0000 cannot be converted to text.
   CONTEXT: JSON data, line 1: …": "Foo\u0000…
   ```
   
   This is the same *class* of bug already fixed for 
`NaN`/`Infinity`/`-Infinity` in this migration (#57893): values that are legal 
in the pickled representation but illegal in strict JSON/JSONB. The existing 
`regexp_replace` sanitization step handles the non-finite-float tokens but does 
**not** strip `\u0000`, so any NUL byte in a stored XCom takes the whole 
upgrade down.
   
   `\u0000` lands in XCom values legitimately: a task returns a Python object 
whose string fields contain `\x00` (common with data sourced from fixed-width 
exports, C buffers, Mongo, or bad UTF-16→UTF-8 conversions). On 2.x this 
serialized fine into the `bytea`/`LargeBinary` value column as the 6-char 
escape `\u0000`; the 3.x JSONB conversion then rejects it.
   
   **The sibling `dag_run.conf` migration 
`0055_3_0_0_remove_pickled_data_from_dagrun_table` has the same gap**, but 
fails differently: its per-row `try/except` (added alongside the NaN handling) 
swallows the bad row and increments `err_count`, so a NUL-bearing `conf` is 
**silently dropped** rather than crashing. Same root cause (`json.dumps` emits 
`\u0000`, JSONB rejects it), different (quieter, lossy) symptom. Worth fixing 
both together.
   
   ### What you think should happen instead?
   
   The migration should sanitize `\u0000` (and any raw NUL byte) the same way 
it already sanitizes `NaN`/`Infinity`, so the upgrade completes instead of 
aborting (0049) or silently dropping data (0055).
   
   `\u0000` cannot be preserved in JSONB at all (unlike `NaN`, which can be 
quoted), so the only options are to strip it or replace it with a sentinel 
codepoint (e.g. U+FFFD). Stripping is the least surprising for metadata.
   
   Suggested in-place amendment to the PostgreSQL branch of `0049` (these 
migration files are amended in place for bugfixes, since an already-applied 
migration can't be re-run):
   
   ```sql
   UPDATE xcom
   SET value = convert_to(
       regexp_replace(
           replace(convert_from(value, 'UTF8'), '\u0000', ''),  -- strip NUL 
escape (illegal in jsonb)
           '([:,\[]\s*|^)(NaN|-?Infinity)(?=\s*[,}\]]|$)',
           '\1"\2"',
           'g'
       ),
       'UTF8'
   )
   WHERE value IS NOT NULL AND get_byte(value, 0) != 128
   ```
   
   (The MySQL and SQLite branches need the equivalent `REPLACE(..., '\u0000', 
'')`, and `0055`'s Python path should strip `\u0000` from the deserialized data 
before `json.dumps`.) A PR follows.
   
   ### How to reproduce
   
   On a 2.x deployment, push an XCom whose value contains a NUL byte, then 
upgrade to 3.x. Minimal SQL repro of the failing cast (the stored `bytea` holds 
the 6-char `\u0000` escape exactly as `json.dumps` would emit it):
   
   ```sql
   -- '\x7b226b223a2022465c75303030306f6f227d' decodes to: {"k": "F\u0000oo"}
   SELECT CAST(CONVERT_FROM('\x7b226b223a2022465c75303030306f6f227d'::bytea, 
'UTF8') AS JSONB);
   -- ERROR:  unsupported Unicode escape sequence
   -- DETAIL: \u0000 cannot be converted to text.
   ```
   
   ### Operating System
   
   Debian (official Airflow image)
   
   ### Versions of Apache Airflow Providers
   
   _No response_
   
   ### Deployment
   
   Astronomer
   
   ### Deployment details
   
   Upgrade from 2.11.2 (Runtime) to Airflow 3.2.2 / Runtime 3.2-5, PostgreSQL 
metadata DB.
   
   ### Anything else?
   
   Related: #57893 (the `NaN`/`Infinity` sanitization for this same migration). 
This is the `\u0000` follow-on for the same conversion step. The fix may also 
need to cover raw `0x00` bytes in addition to the `\u0000` escape sequence.
   
   ### Are you willing to submit PR?
   
   - [X] Yes I am willing to submit a PR!
   
   ### Code of Conduct
   
   - [X] I agree to follow this project's [Code of 
Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md)
   


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