Skip to content

fix(sql): validate unsigned int not-null fields without panicking#3677

Open
umarahad2005 wants to merge 3 commits into
gofr-dev:developmentfrom
umarahad2005:fix/uint-not-null-validation
Open

fix(sql): validate unsigned int not-null fields without panicking#3677
umarahad2005 wants to merge 3 commits into
gofr-dev:developmentfrom
umarahad2005:fix/uint-not-null-validation

Conversation

@umarahad2005

Copy link
Copy Markdown

Description:

validateNotNull (pkg/gofr/datasource/sql/query_builder.go) routes uint, uint8, uint16, uint32, uint64 values into validateIntNotNull, which calls reflect.Value.Int(). That method panics on unsigned kinds:

panic: reflect: call of reflect.Value.Int on uint Value

validateNotNull is called from InsertQuery, which powers GoFr's auto-generated CRUD create handler (crud_handlers.go). So an entity with an unsigned not_null field, for example:

type Item struct {
    ID    int  `sql:"auto_increment"`
    Count uint `sql:"not_null"`
}

panics the request handler on every create — for any value, not just zero.

Fix: add validateUintNotNull using reflect.Value.Uint() and route the unsigned kinds to it, mirroring the existing validateFloatNotNull helper. The signed-integer path is unchanged.

No tracking issue exists for this; it was found while reading the not-null validation.

Breaking Changes (if applicable):

None. These values previously panicked; they now validate correctly (zero → field cannot be zero, non-zero → ok), consistent with the signed and float paths.

Additional Information:

  • No new dependencies.
  • Verified with go test: the added unsigned-int cases panic on the current code and pass after the fix.

Checklist:

  • I have formatted my code using goimport and golangci-lint.
  • All new code is covered by unit tests.
  • This PR does not decrease the overall code coverage.
  • I have reviewed the code comments and documentation for clarity.

validateNotNull routes uint/uint8/uint16/uint32/uint64 values into
validateIntNotNull, which calls reflect.Value.Int(). That method panics
on unsigned kinds ("reflect: call of reflect.Value.Int on uint Value"),
so any not_null field of an unsigned type crashed InsertQuery - and with
it the auto-generated CRUD create handler - on every request.

Add validateUintNotNull using reflect.Value.Uint() and route the unsigned
kinds to it, mirroring the existing validateFloatNotNull helper.

Add a focused unit test plus an InsertQuery case covering the unsigned
not-null path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 1, 2026 19:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a panic in GoFr’s SQL not_null validation when inserting entities containing unsigned integer fields, ensuring CRUD create handlers validate unsigned fields correctly instead of crashing.

Changes:

  • Split unsigned integer handling out of the signed-int path in validateNotNull to avoid reflect.Value.Int() panics.
  • Added validateUintNotNull using reflect.Value.Uint() for uint* kinds.
  • Added unit tests covering the unsigned-int validation and an InsertQuery not-null error scenario.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
pkg/gofr/datasource/sql/query_builder.go Routes unsigned integer types to a new uint-specific not-null validator to prevent reflect panics.
pkg/gofr/datasource/sql/query_builder_test.go Adds coverage ensuring unsigned-int not-null validation errors instead of panicking.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +263 to +265
require.NoError(t, validateNotNull("views", uint(5), true))
require.NoError(t, validateNotNull("views", uint64(9), true))
require.EqualError(t, validateNotNull("views", uint(0), true), "field cannot be zero: views")

@Umang01-hash Umang01-hash left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix is correct and well-tested. One request before merge: the same panic class you're fixing still exists on the default path — worth closing it in the same PR while you're here. Details inline.

return validateUintNotNull(fieldName, v)
case float32, float64:
return validateFloatNotNull(fieldName, v)
default:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same bug class as the uint fix: this default branch routes to validateDefaultNotNull, which calls reflect.Value.IsNil() — that panics on non-nillable kinds. A NotNull bool, time.Time, or uintptr field will panic here (verified). Since you're already fixing the unsigned case, please guard this path too (check Kind() before IsNil(), or handle bool/time explicitly) and add a test.

Addresses review on gofr-dev#3677.

The default branch of validateNotNull calls reflect.Value.IsNil(), which
panics on non-nillable kinds - the same panic class as the unsigned-int
fix. A NOT NULL field of type bool, a struct such as time.Time, uintptr,
array, etc. crashed InsertQuery (and the auto CRUD create handler).

Only call IsNil() for kinds where it is defined (via isNillableKind), and
treat an untyped-nil interface (reflect.Invalid) as null. Non-nillable
kinds can never be null and are now accepted.

Also expand the unsigned test to exercise every uint width (uint8/16/32)
so none can regress to the default path, and add a test covering the
non-nillable kinds and the nil cases.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@umarahad2005

Copy link
Copy Markdown
Author

Thanks for the review, @Umang01-hash — good catch.

default path panic (bool / time.Time / uintptr): confirmed and fixed in fbe828c. validateDefaultNotNull now checks the kind before calling IsNil() via a small isNillableKind helper, so only nillable kinds (chan/func/interface/map/pointer/slice) run the nil check. An untyped-nil interface (reflect.Invalid) is treated as null. Non-nillable kinds can never be null, so they're accepted. I verified bool, time.Time, and uintptr panicked before this change and now pass.

Test coverage (@copilot): the unsigned test now exercises every uint width — uint8, uint16, uint32, uint64 — for both the valid and zero cases, plus a new Test_validateNotNull_NonNillableKinds covering the default-path kinds and the nil cases.

Ready for another look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants