objectbox / objectbox-go

Embedded Go Database, the fast alternative to SQLite, gorm, etc.
https://objectbox.io
Apache License 2.0
1.1k stars 45 forks source link

Can not generate struct with relation #14

Closed OGKevin closed 5 years ago

OGKevin commented 5 years ago

When generating the following struct

type Progress struct {
    Id             uint64
    Show           *shows.Show `objectbox:"link"`
}

which has a relation to the show struct, i manage the get the following error:

Generating ObjectBox bindings for internal/<pgk>/object.go
can't prepare bindings for internal/<pgk>/object.go: error running type-check: internal/<pgk>/object.go:7:2: could not import og/internal/showsrss (type-checking package "og/internal/showsrss" failed (/Users/ogkevin/projects/OGKevin/og/internal/showsrss/entires_api.go:8:2: could not import github.com/objectbox/objectbox-go/objectbox (type-checking package "github.com/objectbox/objectbox-go/objectbox" failed (/Users/ogkevin/go/pkg/mod/github.com/objectbox/objectbox-go@v1.0.0/objectbox/model.go:30:2: could not import github.com/objectbox/objectbox-go/internal/generator (go/build: importGo github.com/objectbox/objectbox-go/internal/generator: exit status 1
go: finding github.com/objectbox/objectbox-go/internal/generator latest
go: finding github.com/objectbox/objectbox-go/internal latest
can't load package: package github.com/objectbox/objectbox-go/internal/generator: unknown import path "github.com/objectbox/objectbox-go/internal/generator": cannot find module providing package github.com/objectbox/objectbox-go/internal/generator

it leads me to believe that there is something wrong with the object box package? I tried diging into the code but its a little cryptic and could not follow the logic of typing checking.

If i remove the link between the 2, the generator is able to generate without problems. Any ideas ?

vaind commented 5 years ago

When there's an unknown type, the generator tries to import it from the appropriate package. It seems there's an issue when importing the dependency tree when following objectbox import path itself in entires_api.go. That's very weird since ObjectBox is obviously installed.

Could you please specify the Go version and your OS version and which steps did you follow to install ObjectBox? Also, is the project available publicly by any chance? I could try to replicate it the situation.

OGKevin commented 5 years ago

go version go1.12.7 darwin/amd64 10.14.5

I installled it by running bash <(curl -s https://raw.githubusercontent.com/objectbox/objectbox-go/master/install.sh)

The code is not public no. If i know where to look i could potentially find something. I tried looking but the type checking logic was a bit cryptic at first glance, i might need to take another look at it.

OGKevin commented 5 years ago

Now that i have a little more understand on how objectbox works, #15, is it intended to have relation between models in different packages ?

vaind commented 5 years ago

A model defines all entities in a single database, what a "database schema" is called in RDBMS. Therefore, there can only be relations in a single model (i.e. in a single database).

I have not tested this yet, but you could, in theory, combine entities from multiple Go packages to form a single database (model) by using the -persist path/to/the/objectbox-model.json code generator option to have the generator create the code for a single model even from multiple packages. If you're using //go:generate comments, you'd add it as an argument, something like this should work (the path would be relative to the current file with that comment)

//go:generate go run github.com/objectbox/objectbox-go/cmd/objectbox-gogen -persist ./path/to/.../objectbox-model.json

Could you try that to see if it helps you create a single model (database)

OGKevin commented 5 years ago

Thanks for the info.

I moved them both to the same package and the orriginal error presits šŸ¤” So it seems to not be related that they are different packages. Tried both ways. Both in package A and them both in package B.

OGKevin commented 5 years ago

Is it possible that this has to do with the C depedency? @vaind was you abel to reproduce this ?

OGKevin commented 5 years ago

This one is a little scary,

can't prepare bindings for object.go: error running type-check: downloader.go:6:2: could not import github.com/anacrolix/dht (type-checking package "github.com/anacrolix/dht" failed (/Users/ogkevin/go/pkg/mod/github.com/anacrolix/dht@v1.0.1/announce.go:9:2: could not import github.com/anacrolix/sync (type-checking package "github.com/anacrolix/sync" failed (/Users/ogkevin/go/pkg/mod/github.com/anacrolix/sync@v0.0.0-20180808010631-44578de4e778/lockstats.go:7:2: could not import github.com/anacrolix/missinggo/perf (type-checking package "github.com/anacrolix/missinggo/perf" failed (/Users/ogkevin/go/pkg/mod/github.com/anacrolix/missinggo@v0.0.0-20180725070939-60ef2fbf63df/perf/mutex.go:6:2: could not import github.com/anacrolix/missinggo (go/build: importGo github.com/anacrolix/missinggo: exit status 1
go: writing go.sum: open /Users/ogkevin/go/pkg/mod/github.com/anacrolix/missinggo@v0.0.0-20180725070939-60ef2fbf63df/go.sum829734412.tmp: permission denied

Its trying to write to go.sum of a dir that is not even of its own package? This error was crating by trying to add 2 structs that are related to each other in the same package.

type A struct {
    ID        uint64
    CreatedAt time.Time `objectbox:"type:int64 converter:TimeInt64"`
    Name      string
    ShowID    uint64
}

type B struct {
    ID       uint64
    A  *A  `objectbox:"link"`
    QueuedAt time.Time `objectbox:"type:int64 converter:TimeInt64"`
}
OGKevin commented 5 years ago

So the reason for the permission denied is, its executing go get in the directory and that tries to write to go.mod.

I have a feeling this generator can not work with go mod. I when i make sure that the packages that are missing are located in im my GOPATH, then the generator manages to go to the next dependecy and tell me that its missing from my gopath.

OGKevin commented 5 years ago

So after running GO111MODULE=off go get -v ./... I've managed to get furthur in the generating process. I'm still getting blocked by a type check in another dependecie's code.

Im getting the following error now:

can't prepare bindings for object.go: error running type-check: downloader.go:8:2: could not import github.com/anacrolix/torrent (type-checking package "github.com/anacrolix/torrent" failed (/Users/ogkevin/go/pkg/mod/github.com/anacrolix/torrent@v1.5.2/multiless.go:6:24: MultiLess not declared by package missinggo))

but as you can see here, it is https://github.com/anacrolix/missinggo/blob/bfa7df3d8c7a0816df4c3b28ea68561d7598d635/multiless.go#L1-L10

Why is there a need of type checking anyway? It is going waay to deep in the depedency tree and its importing stuff that is not even needed to generate the struct. As the struct is basically strings and ints...

OGKevin commented 5 years ago

I've added

import "github.com/anacrolix/dht"

func Boo(){
    _, _ = dht.NewServer(nil)
}

to https://github.com/objectbox/objectbox-go/blob/5bc8560255c2501c47feaeef8c1e4d61aada2fc1/test/model/entity.go

then ran

go get tidy
go generate test/model/entity.go

in this dir and was able to reproduce the problem @vaind

It seems that its not related to linking relations but rather type checking.

OGKevin commented 5 years ago

So, my current work around is to generate the code in a clean package that has no imports and copy paste it for now.

vaind commented 5 years ago

TLDR; Generator runs a Go built-in type-checker when it needs to discover info about unknown types. Having models in a separate package, without unnecessary dependencies is the easiest way to 1. avoid unrelated type-checker errors; 2. keep the generator fast (because it doesn't have to go through the dependencies).


If the generator encounters a field with a non-trivial type (e.g. another struct, a named type/type alias, etc), it invokes Go built-in type checker which provides type information. The thing is there are no means to limit the scope on which the type checker runs - we just give it the package and it goes around to any depth it needs (unfortunately). Therefore, when there are errors, the type checker returns those. Recently, I've noticed that even if the type checker returned errors, it tried to continue analyzes even in case of an error so the relevant type we were actually looking for may still be resolved just find. That should help with your situation as well.

As for the go.sum writing - that seems to be a side-effect of running the built-in type-checker. It seems it does automatically some staff that the go-tool usually does. The gomissing project didn't include go.sum at that version even though it should have. I'll try to isolate the issue and see if it can be reported at https://github.com/golang. There seem to be some related issues though https://github.com/golang/go/issues/30185 and https://github.com/golang/go/issues/27300

vaind commented 5 years ago

https://github.com/golang/go/issues/33541

OGKevin commented 5 years ago

Having models in a separate package, without unnecessary dependencies is the easiest way to

This is indeed what i ended up doing. And it works fine now. I rather have models in the package they belong to.

By doing it this way it also means i can use 1 database + ObjectBox object which kind of solves #15 as well šŸ‘

Happy to have helped and thanks for your time šŸ‘

vaind commented 5 years ago

You're welcome, in case you have further ideas for improvement, feel free to reach out or create a PR