savo.la
2019-02-10
Go driver plugins
Go database drivers are usually registered by importing the driver package into the program:
import ( _ "github.com/lib/pq" // Add PostgreSQL support. _ "github.com/mattn/go-sqlite3" // Add SQLite support. ) func main() { dbType := os.Args[1] doSomethingWithDB(dbType) }
This approach requires that all database driver options are hardcoded in the program source code. That can be avoided by using plugins:
func main() { pluginName := os.Args[1] loadPlugin(pluginName) dbType := os.Args[2] doSomethingWithDB(dbType) }
Now the upstream project doesn't need to be concerned with all possible database drivers that might be used in deployments.
But there's a discontinuity between the database driver and the plugin-enabled program: drivers are implemented as importable packages, while plugins are built from main packages. The deployment process cannot just ask the Go toolchain to build the driver package into a plugin.
The following is a list of possible solutions to the problem.
Deployment-specific wrapper
Downstream projects could have custom plugin packages which import the driver:
package main import _ "github.com/lib/pq" func main() {}
It's not a huge maintenance burden, but it's useless boilerplate that needs to be copied around.
Published wrapper
Same as above, but the wrapper package is published by someone. It sits between the downstream projects and the upstream driver project. It requires users to know about the separate wrapper project in addition to the driver project.
Central wrapper repository
A repository which contains subpackages for all drivers in existence. (I created one that so far contains two database drivers.) In addition to the obvious problem of maintaining a list of all Go packages in the world which register functionality on import, it's still a detour for the user.
Convention for pluggable drivers
All drivers could supply a subpackage which could be built as a plugin
(e.g. github.com/lib/pq/plugin
). It would require that all driver
authors get on board - and it's still useless boilerplate.
Custom build tool
The fictional gobuildplugin -o pq.so github.com/lib/pq
could
synthesize the wrapper. The custom tool would need to be installed in the
build environment.
Module proxy
go get plugin.example.net/github.com/lib/pq
could cause the server
at plugin.example.net
to synthesize the wrapper on the fly. It
seems overkill to introduce such a dependency to the build system for such a
simple need.
Standard tool
The Go toolchain could add support for building plugins out of non-main packages.