Skip to content

Fix: NULL column incorrectly reported as ColumnNotFound instead of UnexpectedNullableFound#665

Draft
Copilot wants to merge 3 commits intomainfrom
copilot/add-test-case-for-issue-560
Draft

Fix: NULL column incorrectly reported as ColumnNotFound instead of UnexpectedNullableFound#665
Copilot wants to merge 3 commits intomainfrom
copilot/add-test-case-for-issue-560

Conversation

Copy link

Copilot AI commented Feb 28, 2026

When parsing a non-nullable scalar from a column containing SQL NULL, Anorm threw a misleading ColumnNotFound error listing the column as both not-found and available — hiding the real cause.

// Column "completed_average" exists but contains NULL
SQL("SELECT completed_average FROM stats").as(scalar[Int].single)
// Before: anorm.AnormException: 'completed_average' not found, available columns: completed_average
// After:  anorm.AnormException: UnexpectedNullableFound(completed_average)

Root cause

SqlParser.parseColumn was remapping UnexpectedNullableFoundColumnNotFound:

case UnexpectedNullableFound(_) =>
  ColumnNotFound(name, row)  // masks the real error

Changes

  • SqlParser.scala: Remove the erroneous UnexpectedNullableFoundColumnNotFound mapping in parseColumn. The method now returns c.tupled(input) directly, propagating the original error.
  • RowParser.scala: Update the ? operator to also catch UnexpectedNullableFoundNone, preserving its documented behaviour of turning a missing or null column into None. Previously this worked accidentally via the wrong ColumnNotFound mapping.
  • AnormSpec.scala: Add regression test asserting that scalar[Int].single on a NULL integer column throws AnormException with an UnexpectedNullableFound(...) message.
  • SqlResultSpec.scala: Add test asserting that SqlParser.int("n").? on a NULL integer column returns None.

The net observable behaviour for callers:

Usage Before After
get[Int]("col").single on NULL ColumnNotFound (misleading) UnexpectedNullableFound (correct)
get[Int]("col").? on NULL None None (unchanged)
get[Int]("col").single on missing column ColumnNotFound ColumnNotFound (unchanged)

🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

…d in parseColumn

Co-authored-by: gaeljw <18280708+gaeljw@users.noreply.github.com>
Copilot AI changed the title [WIP] Add test case for issue 560 in anorm Feb 28, 2026
@gaeljw
Copy link
Member

gaeljw commented Feb 28, 2026

@copilot what are the impacts of such a change? How will callers handle the new error instead of ColumnNotFound?

…l column with ? operator

Co-authored-by: gaeljw <18280708+gaeljw@users.noreply.github.com>
Copy link
Author

Copilot AI commented Feb 28, 2026

@copilot what are the impacts of such a change? How will callers handle the new error instead of ColumnNotFound?

Good catch — the original fix was incomplete. The key impact was on RowParser.? (the optional parser operator), which was documented as "turn missing or null column as None". Before, that worked accidentally because UnexpectedNullableFound was remapped to ColumnNotFound, which ? catches. After removing that remapping, ? would propagate UnexpectedNullableFound as an error instead of returning None, breaking the existing "be None when NULL" test.

The fix in commit 8522162 also updates RowParser.? to explicitly catch UnexpectedNullableFoundNone, preserving the documented behavior while keeping the correct error for .single:

  • get[Int]("col").single on NULL → UnexpectedNullableFound(col) ✓ (clear error message)
  • get[Int]("col").? on NULL → None ✓ (optional null handling preserved)
  • get[Int]("col").single on missing column → ColumnNotFound ✓ (unchanged)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants