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

Incompatibility with Gorm #59

Closed
tobiassjosten opened this issue Sep 8, 2020 · 6 comments
Closed

Incompatibility with Gorm #59

tobiassjosten opened this issue Sep 8, 2020 · 6 comments

Comments

@tobiassjosten
Copy link

I'm having some trouble with getting this library playing together with Gorm and I'm not entirely sure where the fault is at. Maybe someone here could help point me in a direction to figure out where (and how) to solve this?

The problem is that Gorm doesn't seem to understand when ulid.ULIDs are empty, so when I have such a field it tried writing to it, which causes foreign key failure.

I've seen that this library implements a SQL Valuer and it looks good but, still, it doesn't work for me.

Any ideas what could be going wrong?

Here's an example:

package main

import (
	"math/rand"
	"os"
	"time"

	"github.com/oklog/ulid/v2"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type One struct {
	ID    ulid.ULID `gorm:"primaryKey;type:varbinary(255)"`
	Name  string
	Two   *Two
	TwoID ulid.ULID
}

type Two struct {
	ID ulid.ULID `gorm:"primaryKey;type:varbinary(255)"`
}

func main() {
	db, _ := gorm.Open(mysql.Open(os.Getenv("DB_DSN")), &gorm.Config{})

	db.AutoMigrate(One{})
	db.AutoMigrate(Two{})

	now := time.Now()
	entropy := ulid.Monotonic(rand.New(rand.NewSource(now.UnixNano())), 0)

	one := One{ID: ulid.MustNew(ulid.Timestamp(now), entropy), Name: "Asdf"}

	db.Create(one)
}
@giautm
Copy link

giautm commented Sep 8, 2020

What is the error? Please copy and paste to here.

@tobiassjosten
Copy link
Author

Thanks for the quick reply, @giautm! Here's the error I'm seeing:

2020/09/08 10:45:14 /…/main_test.go:24 FOREIGN KEY constraint failed
[0.487ms] [rows:0] INSERT INTO `ones` (`id`,`name`,`two_id`) VALUES ("<binary>","Asdf","<binary>")

@giautm
Copy link

giautm commented Sep 8, 2020

I don't see relation between One and Two model. Do you add relation by hand? plz provide more detail about database schema.

  • The error said, you must insert the Two with that ID first then insert the One. Or you must set NULLABLE for that column (two_id).

@tobiassjosten
Copy link
Author

Gorm infers the relation – you'll see that One has a property referencing Two and that's the relation. That is further encoded in the TwoID property, which is saved to the database.

I just heard back from the creator of Gorm and it seems that ULIDs' Value always return true, which tells Gorm that there's a value even for an emptyULID. That sounds like it could be the culprit here.

go-gorm/gorm#3427

So if my One has a null property for Two, Gorm will see that as an actual reference to a Two that doesn't exist. Hence the foreign key failure.

Does that make sense? Would that be fixable here?

@peterbourgon
Copy link
Member

peterbourgon commented Sep 8, 2020

Well, a zero-value ULID is a valid ULID, in the same way that 0 is a valid int. How would you solve this problem for ints?

edit: Possible paths forward, as I see them:

  • Change the Value method to report zero-value ULIDs as invalid — I don't think this is correct, but happy to discuss
  • Provide a wrapper type w/ Value method that special-cases zero-value ULIDs as invalid — but you could do this yourself?
  • Use *ulid.ULID instead of ulid.ULID in your model

Any others?

@tobiassjosten
Copy link
Author

I agree about your first option and also think your second option sounds like the best way forward. I managed to solve it in another manner (see below) but yours is more generally applicable, so that's probably what most users should use.

You have already done a lot of good work here but can I still just suggest adding a line or two about this to the README? I would have found that immensely useful. :)

Either way, thank you so much for the quick help!

(In case anyone else comes looking months from now, my own solution was centralizing Gorm interactions through a set of functions where I can do null/empty-checks and use Gorm's Omit()´/Select()` to control what's being written.)

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

No branches or pull requests

3 participants