Skip to content

proposal: database/sql: define a SQL driver unwrapper interface and helper function #42460

@Julio-Guerra

Description

@Julio-Guerra

This proposal is motivated by the common need for a standard interface defining a standard way to unwrap SQL drivers that may have been wrapped by SQL instrumentation packages.

Problem

Such SQL driver wrapper types hide every other interface the wrapped type may implement. For example, database/sql/driver defines many optional interfaces that would be hidden by the following wrapper type definition:

type myDriverWrapper struct {
	driver.Driver
}

func Wrap(drv driver.Driver) driver.Driver {
	return myDriverWrapper{drv}
}

With this straightforward type definition, myDriverWrapper only implements interface driver.Driver, no matter what the actual driver may implement besides the driver.Driver interface.

SQL dialect detection functions based on the package path name gotten with reflect (with reflect.TypeOf(db.Driver()).PkgPath()) will also break when using the wrapped driver.

Proposed solution

Similarly to the Unwrap function of package errors (https://golang.org/pkg/errors/#Unwrap), package database/sql/driver could provide the Unwrapper interface allowing a driver wrapper to return its underlying SQL driver, but also let third-party packages know about it:

type Unwrapper interface {
	Unwrap() Driver
}

// Unwrap returns the result of calling the Unwrap method on drv, if drv's
// type implemented the Unwrapper interface.
// Otherwise, Unwrap returns nil.
func Unwrap(drv Driver) Driver {
	// copied from package errors
	// Go generics could allow sharing `Unwrapper` and `Unwrap()` definitions
	if u, ok := err.(Unwrapper); ok {
		return u.Unwrap()
	}
	return nil
}

When a driver wrapper implements the Unwrapper interface, a third-party package is able to:

  1. detect a driver has been wrapped
  2. unwrap the driver until it implements a given interface
  3. get the package paths of every wrapper type, down the deepest driver which should be the actual one
    And probably other use-cases I haven't considered :-)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions