Skip to content

feat: no ErrNoRows when scan nil struct ptr and nil map and nil builtin type ptr - #1379

Open
CyJaySong wants to merge 4 commits into
uptrace:masterfrom
CyJaySong:feat-NoErrNoRowsWhenScanNilStructPtr
Open

feat: no ErrNoRows when scan nil struct ptr and nil map and nil builtin type ptr#1379
CyJaySong wants to merge 4 commits into
uptrace:masterfrom
CyJaySong:feat-NoErrNoRowsWhenScanNilStructPtr

Conversation

@CyJaySong

@CyJaySong CyJaySong commented May 9, 2026

Copy link
Copy Markdown
Contributor

previous writing styles

user := new(User)
err := db.NewSelect().Model(usert). Where("id = ?", 1).Scan(ctx)
// If there is no data that meets the criteria,err = sql.ErrNoRows, user != nil

new feature

var user *User
err := db.NewSelect().Model(&usert). Where("id = ?", 1).Scan(ctx)
If there is no data that meets the criteria,err = nil, user = nil

var users map[string]any
err := db.NewSelect().Model((*User)(nil)). Where("id = ?", 1).Scan(ctx, &users)
If there is no data that meets the criteria,err = nil, users = nil

var phone *string
err := db.NewSelect().Model((*User)(nil)). Where("id = ?", 1).Column("phone").Scan(ctx, &phone)
If there is no data that meets the criteria,err = nil, phone = nil

Compatible with previous writing styles

@CyJaySong

Copy link
Copy Markdown
Contributor Author

@Aoang Hi Bro,look here

Comment thread query_base.go
@CyJaySong
CyJaySong force-pushed the feat-NoErrNoRowsWhenScanNilStructPtr branch from 1df5a46 to c7b489d Compare May 14, 2026 02:39
@CyJaySong
CyJaySong requested a review from bevzzz May 14, 2026 02:51
@CyJaySong
CyJaySong force-pushed the feat-NoErrNoRowsWhenScanNilStructPtr branch from c7b489d to 9e80321 Compare May 14, 2026 09:46
Comment thread model.go Outdated
Comment thread query_base.go Outdated
@CyJaySong CyJaySong changed the title feat: no ErrNoRows when scan nil struct ptr feat: no ErrNoRows when scan nil struct ptr and nil map May 15, 2026
Comment thread model.go
@CyJaySong
CyJaySong force-pushed the feat-NoErrNoRowsWhenScanNilStructPtr branch from a02c4b2 to 385b0a6 Compare May 20, 2026 10:35
@CyJaySong CyJaySong changed the title feat: no ErrNoRows when scan nil struct ptr and nil map feat: no ErrNoRows when scan nil struct ptr and nil map and builtin type May 20, 2026
@CyJaySong

Copy link
Copy Markdown
Contributor Author

@bevzzz

@CyJaySong CyJaySong changed the title feat: no ErrNoRows when scan nil struct ptr and nil map and builtin type feat: no ErrNoRows when scan nil struct ptr and nil map and nil builtin type ptr May 20, 2026
@bevzzz

bevzzz commented May 20, 2026

Copy link
Copy Markdown
Collaborator

@CyJaySong I'll take another pass at the PR tomorrow, just to make sure I understand the changes well. Thanks for your patience and the effort you're putting in contributing to Bun.

@CyJaySong

CyJaySong commented May 21, 2026

Copy link
Copy Markdown
Contributor Author

@bevzzz Thanks for your recognition. I‘m looking forward to a smooth merger

@CyJaySong

Copy link
Copy Markdown
Contributor Author

@bevzzz I am waiting in agony

@CyJaySong

Copy link
Copy Markdown
Contributor Author

prolong life

@github-actions

Copy link
Copy Markdown

This pull request has been automatically marked as stale because it has not had activity in the last 30 days. If there is no update within the next 7 days, this pr will be closed. Please feel free to give a status update now, ping for review, when it's ready. Thank you for your contributions!

@github-actions github-actions Bot added the stale label Jul 30, 2026
@CyJaySong

Copy link
Copy Markdown
Contributor Author

prolong life

@github-actions github-actions Bot removed the stale label Jul 31, 2026
@CyJaySong
CyJaySong requested a review from bevzzz August 1, 2026 01:05

@Aoang Aoang left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for your contribution, and sorry for the long review time!

So far, I've spotted two breaking changes, and it's still unclear if there are others.

Like if err := query.Scan(ctx, &id); errors.Is(err, sql.ErrNoRows) {}, upgrading will break application logic since sql.ErrNoRows won't be triggered anymore. This is a tough breaking change to introduce. At a minimum, this feature should be optional/opt-in, or exposed via a separate API like ScanSilent.

To push this forward, I suggest we discuss the design first in an issue to find a reasonable solution. Meanwhile, this PR remains very valuable, especially as it points out gaps in our regression test suite.

Comment thread internal/dbtest/db_test.go
Refactor the reflect.Pointer branch in _newModel to use isSingleValueStruct
so that time.Time, net.IPNet, netip.Addr, netip.Prefix and sql.Scanner
implementers are routed to scanModel instead of structTableModel.

Also apply the same check to the reflect.Slice branch to handle
[]*time.Time, []*sql.NullString and similar pointer slices correctly.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@CyJaySong

Copy link
Copy Markdown
Contributor Author

@Aoang

@Aoang

Aoang commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the update. I retested the latest commit against PostgreSQL. The main backward-compatibility concern is still unresolved. Existing applications may already rely on sql.ErrNoRows.

func testPR1379ErrNoRows(t *testing.T, db *bun.DB) {
	t.Run("nil pointer destination", func(t *testing.T) {
		var got *int
		err := db.NewSelect().
			ColumnExpr("1").
			Where("FALSE").
			Scan(ctx, &got)

		require.ErrorIs(t, err, sql.ErrNoRows)
		require.Nil(t, got)
	})

	t.Run("allocated pointer destination", func(t *testing.T) {
		got := new(int)
		err := db.NewSelect().
			ColumnExpr("1").
			Where("FALSE").
			Scan(ctx, &got)

		require.ErrorIs(t, err, sql.ErrNoRows)
		require.NotNil(t, got)
	})
}

@CyJaySong

CyJaySong commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the update. I retested the latest commit against PostgreSQL. The main backward-compatibility concern is still unresolved. Existing applications may already rely on sql.ErrNoRows.

func testPR1379ErrNoRows(t *testing.T, db *bun.DB) {
	t.Run("nil pointer destination", func(t *testing.T) {
		var got *int
		err := db.NewSelect().
			ColumnExpr("1").
			Where("FALSE").
			Scan(ctx, &got)

		require.ErrorIs(t, err, sql.ErrNoRows)
		require.Nil(t, got)
	})

	t.Run("allocated pointer destination", func(t *testing.T) {
		got := new(int)
		err := db.NewSelect().
			ColumnExpr("1").
			Where("FALSE").
			Scan(ctx, &got)

		require.ErrorIs(t, err, sql.ErrNoRows)
		require.NotNil(t, got)
	})
}

@Aoang Oh, that was by design, not a bug. It fixes the inconsistent no raws handling behavior. I think this counts as a breaking change.

@CyJaySong

CyJaySong commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

@Aoang Hi, Bro, do you agree with my idea?

@Aoang

Aoang commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

@Aoang Hi, Bro, do you agree with my idea?

Thanks for your contribution! There's no doubt that your implementation provides a much more elegant and reasonable way to handle things.

Looking at the evolution of Go ORM ecosystems—from early days like xorm and gorm to tools like sqlx—none of them handled sql.ErrNoRows as cleanly as this approach does.

My main concern, however, is that this is a breaking change that might force users to make extensive refactoring when upgrading to the new version. I did a quick survey of how users currently write code with bun: while most codebases won't be affected, there are still instances where incorrect usages slipped through due to a lack of code review (or oversight during review). These usages work fine in the current version, but this change would break them.

Let's wait for feedback from other maintainers to see what they think.

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