-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Proof-of-concept of passing an interface rather than *sql.DB to database drivers #374
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
base: master
Are you sure you want to change the base?
Proof-of-concept of passing an interface rather than *sql.DB to database drivers #374
Conversation
Pull Request Test Coverage Report for Build 736
💛 - Coveralls |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm all for using interfaces, but there isn't an interface in database/sql
yet, so I'm hesitant to create and use our own which may differ from any future interfaces in database/sql
.
FYI, there are talks about adding interfaces to database/sql
here
Once there are interfaces in database/sql
, I'm all for updating all of our drivers to use interfaces.
In the meanwhile, you could:
- Try to create your own postgres DB driver that embeds the
migrate
postgres one, but I'm not sure if that'll work with interfaces. - Fork
migrate
with this change, but it is a pain to maintain a fork...
Thanks for the thoughts! I can definitely understand wanting to use interfaces defined by The trade-off of narrow interfaces is, of course, that making the interface wider (if golang/go#7898 seems to be about supporting nested transactions and save points rather than defining a generic interface on top of golang/go#14468 does seem to discuss a general interface, but doesn't seem to have much traction with the core team even though golang/go#14468 (comment) astutely points out that many libraries using Given that it doesn't seem likely that this will be introduced into the stdlib anytime soon, if ever, would you consider defining the interface in I don't see any other workaround aside from the ones you suggested:
Neither of these seem particularly desirable. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The trade-off of narrow interfaces is, of course, that making the interface wider (if migrate ends up needing more functionality from the passed database handle) this would be a backwards incompatible change.
I'm fine with starting with narrow interfaces and adding additional methods later. Any such changes would be documented and any breakage due to incompatibility would be very loud and obvious. e.g. won't build
For now, I still think it's best to hold off on using our own interfaces until there are ones in the standard library or we have more use cases (e.g. reasons to use our own interfaces).
I'm not sure how we'd handle the DBs that don't use sql.DB
. e.g. neo4j, mongodb, and cassandra. Maintaining different interfaces for each DB would be a pain. We probably would only support interfaces for *sql.DB
.
Also, I've provided feedback to your PR should we decide to define our own interfaces and accept your PR. Thanks for all of your research and the discussion!
@@ -41,17 +41,40 @@ type Config struct { | |||
StatementTimeout time.Duration | |||
} | |||
|
|||
type SQLWrapper struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't think we need this. e.g. *sql.DB
should work
It's probably worth documenting that *sql.DB
satisfies this interface the interface should be moved up a package to migrate/database
isLocked bool | ||
|
||
// Open and WithInstance need to guarantee that config is never nil | ||
config *Config | ||
} | ||
|
||
func WithInstance(instance *sql.DB, config *Config) (database.Driver, error) { | ||
func WithInterface(instance DB, config *Config) (database.Driver, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be able to keep the same method WithInstance()
and just change the param to use the interface.
Using an interface would also make transactions easier to implement: #196 |
Hey all,
In our application, we encapsulate our
sql.DB
handles behind an interface so that we can wrap an actual*sql.DB
with a type that handles things such as logging, collecting metrics, and reporting errors. We'd like to use this same wrapped handle withgolang-migrate
but the fact that thepostgres
driver take a literal*sql.DB
type makes this not possible.As a proof-of-concept, I've updated the
database/postgres
driver to have aWithInterface()
method which takes apostgres.DB
interface type instead of an*sql.DB
. I'm curious to get any feedback on this approach. If you like it, I can polish it a little more and update the documentation.I did note that the drivers packages seem independent -- they just return something satisfying the
driver.Driver
interface -- so it seems like we could make this change just in the Postgres driver for now (which is the one we depend on).Thanks!