golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
122.88k stars 17.52k forks source link

database/sql: Ability to get drivername from instance #12600

Open pedromorgan opened 9 years ago

pedromorgan commented 9 years ago

I'd like to be able to get the driver name..

eg

Db, err = sql.Open("mysql", "root:root@/somestuff")
fmt.Printf("Driver= ",  Db.Driver.Name) << Need...

Case Example.. I want to derive an sqlx instance from an existing connection

// Currently
func SetupDb( driver string, db *sql.DB){
    Dbx = sqlx.NewDb(db, driver)
}
// Wanted
func SetupDb( db *sql.DB){
    Dbx = sqlx.NewDb(db, db.Driver.Name)
}

https://github.com/daffodil/go-mailkit/blob/master/postfixadmin/setup.go#L15

bradfitz commented 8 years ago

I don't know what an sqlx is. When you open a *DB with https://golang.org/pkg/database/sql/#Open , can't you just remember the mapping between (driverName, dataSourceName) and the *DB yourself?

pedromorgan commented 8 years ago

In this case, I am trying to avoid having to store another variable with the driver name and having to pass it around. Expect it could be added to the Db instance itself for convenienace instead.

bradfitz commented 8 years ago

Maybe we could add it to DBStats, so we don't add new methods to DB and users can just use the existing Stats method.

msaron commented 7 years ago

I need this functionality also. I am using some external libraries and they want the database connection and the driver name (as string).

kardianos commented 7 years ago

One way you can do this today is to get the DB.Driver() and reflect on that type.

See https://github.com/golang-sql/sqlexp/blob/c2488a8be21d20d31abf0d05c2735efd2d09afe4/quoter.go#L46 for an example. It isn't foolproof as driver type names may change, but they usually don't.

pedromorgan commented 7 years ago

Well thats one way to skin a cat, what I am having to do as a workaroundzzz, is pass a struct{driver+creds+ nick+stuff} around....

and it seems daft to me that there is not a "field" for the want of the word that simply contains the driver_name and some meta data..

Example case.. Here u are with a new db connection... and a new project.. So before i start to even load that, I need to run around all the db admins asking ? which engine is this code using ?

so its daft.. in my case btw i run www sites + scripts and interfacing db's of all types.. so this is a "bane of contention" as I now need to move around all driver info et all, instead of a DB "object" that COULD do that for me.. Its a simple solution and i dont unserstand quite why it aint there.. to be honest..

https://golang.org/pkg/database/sql/driver/#Driver


type Driver interface {
        // Open returns a new connection to the database.
        // The name is a string in a driver-specific format.
        //
        // Open may return a cached connection (one previously
        // closed), but doing so is unnecessary; the sql package
        // maintains a pool of idle connections for efficient re-use.
        //
        // The returned connection is only used by one goroutine at a
        // time.
        Open(name string) (Conn, error)
        Name << Sriver caches name for help down line later
}

Anyway. .sorry for rant..
pedromorgan commented 7 years ago

@kardianos Make is "method" or something.. help me .. Im my case I am using multiple db's(as a new integrator)..

kardianos commented 7 years ago

@pedromorgan You're fine. Are you aware of the DB.Driver method? https://golang.org/pkg/database/sql/#DB.Driver

Let me think on this though. I created a playground for possible extensions: https://godoc.org/github.com/golang-sql/sqlexp as you saw before.

In the meantime I could create something like:

package sqlexp

type Flavor int // Flavor of SQL text.

const (
    Unknown Flavor = iota
    Postgres
    TSQL
    MySQL
    SQLite
)

// DriverFlavor returns the SQL variant used.
//
// Obtain the driver value with the sql.DB.Driver method.
func DriverFlavor(d driver.Driver) Flavor {

}

Then you just have to pass around the *sql.DB, right?

pedromorgan commented 7 years ago

No.. I don't want contstants.. I want to know which driver am using.. from string

If its mysql = No spatial//

The only things I want passed is the Name() of the interface..

Is it postgis ?? is it mysql ?? is it gitsql ?? I need to know that cos I cant even create a connection+addoen unless I know that... !!

Othewise Please implement asap the Reflact Inteterface() which @bradfitz is probally correct in the stats module..

pedromorgan commented 7 years ago

Here is the problem.. https://godoc.org/github.com/freeflightsim/fg-navdb/navdb

I want a local cache vs online one in same interface..

mattn commented 7 years ago

The name should be considered for Flavor vs Dialect. :+1:

kardianos commented 7 years ago

@mattn, yeah, Dialect would probably be a better name. I don't think this needs to be in the std lib right away. I'll prototype something in sqlexp.

@pedromorgan I may not be completly understanding your usecase, but if you want to see if postgresql has the postgis extensions installed, that seems like a capability you would query initially when connection. But I'm not arguing with you, a name would be useful attached to a driver. Let me work something up.

rbranson commented 6 years ago

FWIW, this "hack" worked for me:

var sqlDriverNamesByType map[reflect.Type]string

// The database/sql API doesn't provide a way to get the registry name for
// a driver from the driver type.
func sqlDriverToDriverName(driver driver.Driver) string {
  if sqlDriverNamesByType == nil {
    sqlDriverNamesByType = map[reflect.Type]string{}

    for _, driverName := range sql.Drivers() {
      // Tested empty string DSN with MySQL, PostgreSQL, and SQLite3 drivers.
      db, _ := sql.Open(driverName, "")

      if db != nil {
        driverType := reflect.TypeOf(db.Driver())
        sqlDriverNamesByType[driverType] = driverName
      }
    }
  }

  driverType := reflect.TypeOf(driver)
  if driverName, found := sqlDriverNamesByType[driverType]; found {
    return driverName
  }

  return ""
}
winjeg commented 5 years ago

I encountered the same problem. In my case, I want to write some middleware without leting the users care about which database type they use.

func init(db *sql.DB) {
   swith(getDbType(db)) {
        case "mysql":
            // logic 1
        case "oracle":
           // logic 2
        case "postgres":
          // logic 3

   }
}
filinvadim commented 4 years ago

SELECT version(); and parsing response after helps me. Not a best idea i know.

andreynering commented 4 years ago

As an author of database libraries that work with multiple drivers (PostgreSQL, MySQL, SQLite, MSSQL, etc) I also think it'd be extremely useful to be able to detect which driver the user is using (without having to ask for an additional parameter).

leoleoasd commented 4 years ago

I also need this feature to know the driver name while doing database migrations cause different driver requires different SQL.

Julio-Guerra commented 4 years ago

I encountered the same problem. In my case, I want to write some middleware without leting the users care about which database type they use.

func init(db *sql.DB) {
   swith(getDbType(db)) {
        case "mysql":
            // logic 1
        case "oracle":
           // logic 2
        case "postgres":
          // logic 3

   }
}

I have a similar need to know what is the SQL dialect of a database (MySQL, Oracle, etc.). For now, I manually maintain a list of Go driver package names and their dialects (eg. github.com/go-sql-driver/mysql => MySQL). So I get the package name using reflect on the driver instance. Ideally, I'd love a driver to provide the dialect its database has.

riptl commented 3 years ago

I believe this issue has been solved. It's trivial in recent Go versions.


var db *sql.DB
switch db.Driver().(type) {
    case *mysql.MySQLDriver:
        break // mysql
    ...
}
andreynering commented 3 years ago

@terorie The ability to get only the driver name would be better.

As you suggested, a given library would have to import many SQL drivers to be able to match them, even if the user will only use one of them.

If it returned a string, libraries would not need to import any driver at all, and the user will only import those he uses.

riptl commented 3 years ago

a given library would have to import many SQL drivers to be able to match them, even if the user will only use one of them

@andreynering It's hard to understand the use-case for this. If your code tries to match a specific driver, with a code path that is only taken when that driver is ~active~ supported anyways, what's the issue with importing its package? Modularization for shared libraries can be achieved via side-effect imports. Another cheap and easy option is checking matching the driver type via reflection: reflect.TypeOf(driver).String().

andreynering commented 3 years ago

@terorie In short, it'll be imported and bloat the binary size without being used.

And an important detail: some SQL drivers require a C compiler to be available if imported, notably SQLite. That's annoying if the user is not using that driver at all.

I've shortly dealt with some Oracle drivers in the past, and they require even more complicated setups, just to compile the binary, even if you don't use it.

riptl commented 3 years ago

In short, it'll be imported and bloat the binary size without being used.

@andreynering I understand the concern about the import graph but there are other ways to solve this. To elaborate on my previous comment here are some options:

mohammadv184 commented 1 year ago

there are any updates on the SQL get river name feature? this issue is still open.

elgs commented 1 year ago

This is useful when your function receives sql.DB or sql.TX as a input parameter and you want to figure out what placeholders should be used.

elgs commented 1 year ago

If this is done someday, please don't forget we want the db type from *sql.Tx as well. Go's sql sucks.

elgs commented 1 year ago

Here is my hacks, how dirty!

// due to the lack of a common interface between *sql.DB and *sql.Tx
// when I said go's sql sucks, I meant it.
type DB interface {
    Query(query string, args ...any) (*sql.Rows, error)
    Exec(query string, args ...any) (sql.Result, error)
}

type DbType int

const (
    Unknown DbType = iota
    SQLite
    MySQL
    PostgreSQL
    SQLServer
    Oracle
)

var dbTypeMap = map[string]DbType{}

func GetDbType(conn DB) DbType {
    connPtrStr := fmt.Sprintf("%p\n", conn)
    if val, ok := dbTypeMap[connPtrStr]; ok {
        return val
    }

    v, err := QueryToMaps(conn, "SELECT VERSION() AS version")
    if err == nil && len(v) > 0 {
        version := strings.ToLower(fmt.Sprint(v[0]["version"]))
        if strings.Contains(version, "postgres") {
            dbTypeMap[connPtrStr] = PostgreSQL
            return PostgreSQL
        } else {
            dbTypeMap[connPtrStr] = MySQL
            return MySQL
        }
    }

    v, err = QueryToMaps(conn, "SELECT @@VERSION AS version")
    if err == nil && len(v) > 0 {
        version := strings.ToLower(fmt.Sprint(v[0]["version"]))
        if strings.Contains(version, "microsoft") {
            dbTypeMap[connPtrStr] = SQLServer
            return SQLServer
        } else {
            dbTypeMap[connPtrStr] = MySQL
            return MySQL
        }
    }

    v, err = QueryToMaps(conn, "SELECT * FROM v$version")
    if err == nil && len(v) > 0 {
        dbTypeMap[connPtrStr] = Oracle
        return Oracle
    }

    v, err = QueryToMaps(conn, "SELECT sqlite_version()")
    if err == nil && len(v) > 0 {
        dbTypeMap[connPtrStr] = SQLite
        return SQLite
    }

    return Unknown
}