Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Regression in v1.2.9 - Cannot scan relations when using struct embedding #1125

Closed
giulio-sl opened this issue Feb 13, 2025 · 4 comments · Fixed by #1132
Closed

Regression in v1.2.9 - Cannot scan relations when using struct embedding #1125

giulio-sl opened this issue Feb 13, 2025 · 4 comments · Fixed by #1132
Assignees
Labels
bug Something isn't working

Comments

@giulio-sl
Copy link

Description:

After upgrading from Bun v1.1.7 to v1.2.9, scanning relations into embedded structs fails with column recognition errors. A working implementation in v1.1.7 breaks in v1.2.9 due to missing column detection in embedded structs.

Models:

type User struct {
    Id string `bun:",pk"`

    bun.BaseModel `bun:"table:users"`
}

type Base struct {
    Id        string `bun:",pk"`
    CreatorId string

    bun.BaseModel `bun:"table:bases"`
    CreatedBy *User `bun:"rel:belongs-to,join:creator_id=id"`
}

type Enhanced struct {
    Base
    Extra string `bun:"-"`
}

Query:

err := db.NewSelect().
    Model((*Base)(nil)).
    Relation("CreatedBy").
    Scan(ctx, &Enhanced{})

Error:

sql: Scan error on column index 2, name "created_by__id": 
bun: Enhanced does not have column "created_by__id"

Expected Behavior:

Should successfully scan relation columns into embedded struct fields as in v1.1.7

Actual Behavior:

Fails to recognize relation columns in embedded structs

Additional Context:

  • Works correctly in v1.1.7
  • Breaks in v1.2.9
  • Error occurs when scanning into struct that embeds the base model with relations
  • Full reproduction available if needed

Possible Related Changes:

This might be related to changes in struct scanning or relation handling for embedded structs between these versions.

Why This Matters:

This regression breaks existing code that uses struct composition patterns, which is a common Go idiom. The error suggests the scanner isn't properly traversing embedded structs to find relation columns.

@j2gg0s
Copy link
Collaborator

j2gg0s commented Feb 14, 2025

Thank you for your feedback; it is detailed and specific.

But I was able to run your example locally. Could you check the Bun version, including the dialect and driver?

package main

import (
	"context"
	"database/sql"
	"fmt"

	"github.com/uptrace/bun"
	"github.com/uptrace/bun/dialect/pgdialect"
	"github.com/uptrace/bun/driver/pgdriver"
	"github.com/uptrace/bun/extra/bundebug"
)

type User struct {
	Id string `bun:",pk"`

	bun.BaseModel `bun:"table:users"`
}

type Base struct {
	Id        string `bun:",pk"`
	CreatorId string

	bun.BaseModel `bun:"table:bases"`
	CreatedBy     *User `bun:"rel:belongs-to,join:creator_id=id"`
}

type Enhanced struct {
	Base
	Extra string `bun:"-"`
}

func main() {
	ctx := context.Background()

	sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN("postgres://j2gg0s:@localhost:5433/test?sslmode=disable")))

	db := bun.NewDB(sqldb, pgdialect.New(), bun.WithDiscardUnknownColumns())
	db.AddQueryHook(bundebug.NewQueryHook(
		bundebug.WithVerbose(true),
		bundebug.FromEnv("BUNDEBUG"),
	))

	err := db.ResetModel(ctx, (*Base)(nil), (*User)(nil))
	if err != nil {
		panic(err)
	}

	user := &User{Id: "u0"}
	MustR(db.NewInsert().Model(user).Exec(ctx))
	base := &Base{Id: "b0", CreatorId: "u0"}
	MustR(db.NewInsert().Model(base).Exec(ctx))

	o := &Enhanced{}
	Must(db.NewSelect().Model((*Base)(nil)).Relation("CreatedBy").Scan(ctx, o))
	fmt.Println(o)
}

func Must(err error) {
	if err != nil {
		panic(err)
	}
}

func MustR[T any](v T, err error) T {
	Must(err)
	return v
}
go run 1125.go
[bun]  14:49:39.173   DROP TABLE           18.629ms  DROP TABLE IF EXISTS "bases" CASCADE
[bun]  14:49:39.181   CREATE TABLE           8.16ms  CREATE TABLE "bases" ("id" VARCHAR NOT NULL, "creator_id" VARCHAR, PRIMARY KEY ("id"))
[bun]  14:49:39.182   DROP TABLE              583µs  DROP TABLE IF EXISTS "users" CASCADE
[bun]  14:49:39.185   CREATE TABLE          3.024ms  CREATE TABLE "users" ("id" VARCHAR NOT NULL, PRIMARY KEY ("id"))
[bun]  14:49:39.186   INSERT                  689µs  INSERT INTO "users" ("id") VALUES ('u0')
[bun]  14:49:39.186   INSERT                  498µs  INSERT INTO "bases" ("id", "creator_id") VALUES ('b0', 'u0')
[bun]  14:49:39.188   SELECT                1.503ms  SELECT "base"."id", "base"."creator_id", "created_by"."id" AS "created_by__id" FROM "bases" AS "base" LEFT JOIN "users" AS "created_by" ON ("created_by"."id" = "base"."creator_id")
&{{b0 u0 {} <nil>} }

cat go.mod | grep bun
module github.com/j2gg0s/gist/bundebug
        github.com/uptrace/bun v1.2.9
        github.com/uptrace/bun/dialect/pgdialect v1.2.9
        github.com/uptrace/bun/driver/pgdriver v1.2.9
        github.com/uptrace/bun/extra/bundebug v1.2.9

@giulio-sl
Copy link
Author

giulio-sl commented Feb 14, 2025

Hi @j2gg0s, thank you for testing this!

You’re absolutely correct—your example works with bun.WithDiscardUnknownColumns(). However, the issue arises when this option is not used. Here’s the key difference:

  • In v1.1.7: The code works without bun.WithDiscardUnknownColumns().
  • In v1.2.9: Removing this option causes the created_by__id scan error.

Could you confirm if this stricter column validation in v1.2.9 is intentional?
This change breaks existing code that relies on embedded structs with relations when DiscardUnknownColumns isn’t enabled.

Reproduction steps:

  1. Remove bun.WithDiscardUnknownColumns() from your example.
  2. Observe the error in v1.2.9 vs. success in v1.1.7.

Let me know if you need further details!

@j2gg0s j2gg0s self-assigned this Feb 16, 2025
@j2gg0s j2gg0s added the bug Something isn't working label Feb 16, 2025
@j2gg0s
Copy link
Collaborator

j2gg0s commented Feb 16, 2025

Hi @giulio-sl
You are correct.
Sorry, I didn’t notice my local test cases.

I have a rough guess about the cause, and I will try to fix it within 2~3 days.

@giulio-sl
Copy link
Author

Thank you @j2gg0s !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants