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.

Timo Savola