You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Upsert with None values fails on "Invalid literal value: None" #2426
tests/table/test_upsert.py:720: in test_upsert_with_nulls
upd = table.upsert(data_with_null, join_cols=["foo"])
pyiceberg/table/__init__.py:1343: in upsert
return tx.upsert(
pyiceberg/table/__init__.py:798: in upsert
matched_predicate = upsert_util.create_match_filter(df, join_cols)
pyiceberg/table/upsert_util.py:37: in create_match_filter
return In(join_cols[0], unique_keys[0].to_pylist())
pyiceberg/expressions/__init__.py:682: in __new__
literals_set: Set[Literal[L]] = _to_literal_set(literals)
pyiceberg/expressions/__init__.py:52: in _to_literal_set
return {_to_literal(v) for v in values}
pyiceberg/expressions/__init__.py:52: in <setcomp>
return {_to_literal(v) for v in values}
pyiceberg/expressions/__init__.py:59: in _to_literal
return literal(value)
pyiceberg/expressions/literals.py:159: in literal
raise TypeError(f"Invalid literal value: {repr(value)}")
E TypeError: Invalid literal value: None
Willingness to contribute
I can contribute a fix for this bug independently
I would be willing to contribute a fix for this bug with guidance from the Iceberg community
I cannot contribute a fix for this bug at this time
However, that last test case fails to update the row {"foo": None, "bar": 1, ...} with join_cols=["foo", "bar"]:
tests/table/test_upsert.py:741: in test_upsert_with_nulls
assert upd.rows_updated == 1
E assert 0 == 1
E + where 0 = UpsertResult(rows_updated=0, rows_inserted=1).rows_updated
So I think the filter expressions are now correct, but the upsert logic still doesn't handle updates with null values correctly. Wild guess: maybe it has to do with the way null values are compared (null doesn't equal itself?). Any ideas?
So I think the filter expressions are now correct, but the upsert logic still doesn't handle updates with null values correctly. Wild guess: maybe it has to do with the way null values are compared (null doesn't equal itself?).
I've isolated the problem to step 3 in get_rows_to_update:
This issue has been automatically marked as stale because it has been open for 180 days with no activity. It will be closed in next 14 days if no further activity occurs. To permanently prevent this issue from being considered stale, add the label 'not-stale', but commenting on the issue is preferred when possible.
This issue has been automatically marked as stale because it has been open for 180 days with no activity. It will be closed in next 14 days if no further activity occurs. To permanently prevent this issue from being considered stale, add the label 'not-stale', but commenting on the issue is preferred when possible.
Activity
kevinjqliu commentedon Sep 4, 2025
thanks for the report!
i can reproduce it locally. the issue seems to be related to the
foocolumn as thejoin_cols.We hit this branch
iceberg-python/pyiceberg/table/upsert_util.py
Lines 33 to 37 in 52d810e
Seems like the
Inexpression cannot handle None/Null valuesiceberg-python/pyiceberg/expressions/__init__.py
Lines 678 to 682 in 52d810e
mdwint commentedon Sep 4, 2025
I will try implementing a fix tomorrow, but I may come back with some questions since I don't know the codebase yet.
I'm guessing
IsNullwould be the correct filter predicate. Does that sound right?mdwint commentedon Sep 4, 2025
I've made some progress fixing the filter expressions in main...mdwint:iceberg-python:fix/upsert-with-nulls
The tests now confirm that the right expressions are produced. For example, in the last test case I can print this:
However, that last test case fails to update the row
{"foo": None, "bar": 1, ...}withjoin_cols=["foo", "bar"]:So I think the filter expressions are now correct, but the upsert logic still doesn't handle updates with null values correctly. Wild guess: maybe it has to do with the way null values are compared (null doesn't equal itself?). Any ideas?
mdwint commentedon Sep 5, 2025
I've isolated the problem to step 3 in
get_rows_to_update:iceberg-python/pyiceberg/table/upsert_util.py
Lines 123 to 124 in 075a966
This join ignores
Nonevalues. It returns no matches, but I want it to matchbar: [[1]] foo: [[null]]:mdwint commentedon Sep 5, 2025
Computing the inner join in Python solved the issue. I've now opened #2429 for review.
github-actions commentedon Mar 18, 2026
This issue has been automatically marked as stale because it has been open for 180 days with no activity. It will be closed in next 14 days if no further activity occurs. To permanently prevent this issue from being considered stale, add the label 'not-stale', but commenting on the issue is preferred when possible.
github-actions commentedon Sep 14, 2026
This issue has been automatically marked as stale because it has been open for 180 days with no activity. It will be closed in next 14 days if no further activity occurs. To permanently prevent this issue from being considered stale, add the label 'not-stale', but commenting on the issue is preferred when possible.
github-actions commentedon Sep 28, 2026
This issue has been closed because it has not received any activity in the last 14 days since being marked as 'stale'