Categorygithub.com/jmoiron/sqlx
modulepackage
1.4.0
Repository: https://github.com/jmoiron/sqlx.git
Documentation: pkg.go.dev

# README

sqlx

CircleCI Coverage Status Godoc license

sqlx is a library which provides a set of extensions on go's standard database/sql library. The sqlx versions of sql.DB, sql.TX, sql.Stmt, et al. all leave the underlying interfaces untouched, so that their interfaces are a superset on the standard ones. This makes it relatively painless to integrate existing codebases using database/sql with sqlx.

Major additional concepts are:

  • Marshal rows into structs (with embedded struct support), maps, and slices
  • Named parameter support including prepared statements
  • Get and Select to go quickly from query to struct/slice

In addition to the godoc API documentation, there is also some user documentation that explains how to use database/sql along with sqlx.

Recent Changes

1.3.0:

  • sqlx.DB.Connx(context.Context) *sqlx.Conn
  • sqlx.BindDriver(driverName, bindType)
  • support for []map[string]interface{} to do "batch" insertions
  • allocation & perf improvements for sqlx.In

DB.Connx returns an sqlx.Conn, which is an sql.Conn-alike consistent with sqlx's wrapping of other types.

BindDriver allows users to control the bindvars that sqlx will use for drivers, and add new drivers at runtime. This results in a very slight performance hit when resolving the driver into a bind type (~40ns per call), but it allows users to specify what bindtype their driver uses even when sqlx has not been updated to know about it by default.

Backwards Compatibility

Compatibility with the most recent two versions of Go is a requirement for any new changes. Compatibility beyond that is not guaranteed.

Versioning is done with Go modules. Breaking changes (eg. removing deprecated API) will get major version number bumps.

install

go get github.com/jmoiron/sqlx

issues

Row headers can be ambiguous (SELECT 1 AS a, 2 AS a), and the result of Columns() does not fully qualify column names in queries like:

SELECT a.id, a.name, b.id, b.name FROM foos AS a JOIN foos AS b ON a.parent = b.id;

making a struct or map destination ambiguous. Use AS in your queries to give columns distinct names, rows.Scan to scan them manually, or SliceScan to get a slice of results.

usage

Below is an example which shows some common use cases for sqlx. Check sqlx_test.go for more usage.

package main

import (
    "database/sql"
    "fmt"
    "log"
    
    _ "github.com/lib/pq"
    "github.com/jmoiron/sqlx"
)

var schema = `
CREATE TABLE person (
    first_name text,
    last_name text,
    email text
);

CREATE TABLE place (
    country text,
    city text NULL,
    telcode integer
)`

type Person struct {
    FirstName string `db:"first_name"`
    LastName  string `db:"last_name"`
    Email     string
}

type Place struct {
    Country string
    City    sql.NullString
    TelCode int
}

func main() {
    // this Pings the database trying to connect
    // use sqlx.Open() for sql.Open() semantics
    db, err := sqlx.Connect("postgres", "user=foo dbname=bar sslmode=disable")
    if err != nil {
        log.Fatalln(err)
    }

    // exec the schema or fail; multi-statement Exec behavior varies between
    // database drivers;  pq will exec them all, sqlite3 won't, ymmv
    db.MustExec(schema)
    
    tx := db.MustBegin()
    tx.MustExec("INSERT INTO person (first_name, last_name, email) VALUES ($1, $2, $3)", "Jason", "Moiron", "[email protected]")
    tx.MustExec("INSERT INTO person (first_name, last_name, email) VALUES ($1, $2, $3)", "John", "Doe", "[email protected]")
    tx.MustExec("INSERT INTO place (country, city, telcode) VALUES ($1, $2, $3)", "United States", "New York", "1")
    tx.MustExec("INSERT INTO place (country, telcode) VALUES ($1, $2)", "Hong Kong", "852")
    tx.MustExec("INSERT INTO place (country, telcode) VALUES ($1, $2)", "Singapore", "65")
    // Named queries can use structs, so if you have an existing struct (i.e. person := &Person{}) that you have populated, you can pass it in as &person
    tx.NamedExec("INSERT INTO person (first_name, last_name, email) VALUES (:first_name, :last_name, :email)", &Person{"Jane", "Citizen", "[email protected]"})
    tx.Commit()

    // Query the database, storing results in a []Person (wrapped in []interface{})
    people := []Person{}
    db.Select(&people, "SELECT * FROM person ORDER BY first_name ASC")
    jason, john := people[0], people[1]

    fmt.Printf("%#v\n%#v", jason, john)
    // Person{FirstName:"Jason", LastName:"Moiron", Email:"[email protected]"}
    // Person{FirstName:"John", LastName:"Doe", Email:"[email protected]"}

    // You can also get a single result, a la QueryRow
    jason = Person{}
    err = db.Get(&jason, "SELECT * FROM person WHERE first_name=$1", "Jason")
    fmt.Printf("%#v\n", jason)
    // Person{FirstName:"Jason", LastName:"Moiron", Email:"[email protected]"}

    // if you have null fields and use SELECT *, you must use sql.Null* in your struct
    places := []Place{}
    err = db.Select(&places, "SELECT * FROM place ORDER BY telcode ASC")
    if err != nil {
        fmt.Println(err)
        return
    }
    usa, singsing, honkers := places[0], places[1], places[2]
    
    fmt.Printf("%#v\n%#v\n%#v\n", usa, singsing, honkers)
    // Place{Country:"United States", City:sql.NullString{String:"New York", Valid:true}, TelCode:1}
    // Place{Country:"Singapore", City:sql.NullString{String:"", Valid:false}, TelCode:65}
    // Place{Country:"Hong Kong", City:sql.NullString{String:"", Valid:false}, TelCode:852}

    // Loop through rows using only one struct
    place := Place{}
    rows, err := db.Queryx("SELECT * FROM place")
    for rows.Next() {
        err := rows.StructScan(&place)
        if err != nil {
            log.Fatalln(err)
        } 
        fmt.Printf("%#v\n", place)
    }
    // Place{Country:"United States", City:sql.NullString{String:"New York", Valid:true}, TelCode:1}
    // Place{Country:"Hong Kong", City:sql.NullString{String:"", Valid:false}, TelCode:852}
    // Place{Country:"Singapore", City:sql.NullString{String:"", Valid:false}, TelCode:65}

    // Named queries, using `:name` as the bindvar.  Automatic bindvar support
    // which takes into account the dbtype based on the driverName on sqlx.Open/Connect
    _, err = db.NamedExec(`INSERT INTO person (first_name,last_name,email) VALUES (:first,:last,:email)`, 
        map[string]interface{}{
            "first": "Bin",
            "last": "Smuth",
            "email": "[email protected]",
    })

    // Selects Mr. Smith from the database
    rows, err = db.NamedQuery(`SELECT * FROM person WHERE first_name=:fn`, map[string]interface{}{"fn": "Bin"})

    // Named queries can also use structs.  Their bind names follow the same rules
    // as the name -> db mapping, so struct fields are lowercased and the `db` tag
    // is taken into consideration.
    rows, err = db.NamedQuery(`SELECT * FROM person WHERE first_name=:first_name`, jason)
    
    
    // batch insert
    
    // batch insert with structs
    personStructs := []Person{
        {FirstName: "Ardie", LastName: "Savea", Email: "[email protected]"},
        {FirstName: "Sonny Bill", LastName: "Williams", Email: "[email protected]"},
        {FirstName: "Ngani", LastName: "Laumape", Email: "[email protected]"},
    }

    _, err = db.NamedExec(`INSERT INTO person (first_name, last_name, email)
        VALUES (:first_name, :last_name, :email)`, personStructs)

    // batch insert with maps
    personMaps := []map[string]interface{}{
        {"first_name": "Ardie", "last_name": "Savea", "email": "[email protected]"},
        {"first_name": "Sonny Bill", "last_name": "Williams", "email": "[email protected]"},
        {"first_name": "Ngani", "last_name": "Laumape", "email": "[email protected]"},
    }

    _, err = db.NamedExec(`INSERT INTO person (first_name, last_name, email)
        VALUES (:first_name, :last_name, :email)`, personMaps)
}

# Packages

Package reflectx implements extensions to the standard reflect lib suitable for implementing marshalling and unmarshalling packages.
Package types provides some useful types which implement the `sql.Scanner` and `driver.Valuer` interfaces, suitable for use as scan and value targets with database/sql.

# Functions

BindDriver sets the BindType for driverName to bindType.
BindNamed binds a struct or a map to a query with named parameters.
BindType returns the bindtype for a given database given a drivername.
Connect to a database and verify with a ping.
ConnectContext to a database and verify with a ping.
Get does a QueryRow using the provided Queryer, and scans the resulting row to dest.
GetContext does a QueryRow using the provided Queryer, and scans the resulting row to dest.
In expands slice values in args, returning the modified query string and a new arg list that can be executed by a database.
LoadFile exec's every statement in a file (as a single call to Exec).
LoadFileContext exec's every statement in a file (as a single call to Exec).
MapScan scans a single Row into the dest map[string]interface{}.
MustConnect connects to a database and panics on error.
MustExec execs the query using e and panics if there was an error.
MustExecContext execs the query using e and panics if there was an error.
MustOpen is the same as sql.Open, but returns an *sqlx.DB instead and panics on error.
Named takes a query using named parameters and an argument and returns a new query with a list of args that can be executed by a database.
NamedExec uses BindStruct to get a query executable by the driver and then runs Exec on the result.
NamedExecContext uses BindStruct to get a query executable by the driver and then runs Exec on the result.
NamedQuery binds a named query and then runs Query on the result using the provided Ext (sqlx.Tx, sqlx.Db).
NamedQueryContext binds a named query and then runs Query on the result using the provided Ext (sqlx.Tx, sqlx.Db).
NewDb returns a new sqlx DB wrapper for a pre-existing *sql.DB.
Open is the same as sql.Open, but returns an *sqlx.DB instead.
Preparex prepares a statement.
PreparexContext prepares a statement.
Rebind a query from the default bindtype (QUESTION) to the target bindtype.
Select executes a query using the provided Queryer, and StructScans each row into dest, which must be a slice.
SelectContext executes a query using the provided Queryer, and StructScans each row into dest, which must be a slice.
SliceScan a row, returning a []interface{} with values similar to MapScan.
StructScan all rows from an sql.Rows or an sqlx.Rows into the dest slice.

# Constants

Bindvar types supported by Rebind, BindMap and BindStruct.
Bindvar types supported by Rebind, BindMap and BindStruct.
Bindvar types supported by Rebind, BindMap and BindStruct.
Bindvar types supported by Rebind, BindMap and BindStruct.
Bindvar types supported by Rebind, BindMap and BindStruct.

# Variables

NameMapper is used to map column names to struct field names.

# Structs

Conn is a wrapper around sql.Conn with extra functionality.
DB is a wrapper around sql.DB which keeps track of the driverName upon Open, used mostly to automatically bind named queries using the right bindvars.
NamedStmt is a prepared statement that executes named queries.
Row is a reimplementation of sql.Row in order to gain access to the underlying sql.Rows.Columns() data, necessary for StructScan.
Rows is a wrapper around sql.Rows which caches costly reflect operations during a looped StructScan.
Stmt is an sqlx wrapper around sql.Stmt with extra functionality.
Tx is an sqlx wrapper around sql.Tx with extra functionality.

# Interfaces

ColScanner is an interface used by MapScan and SliceScan.
Execer is an interface used by MustExec and LoadFile.
ExecerContext is an interface used by MustExecContext and LoadFileContext.
Ext is a union interface which can bind, query, and exec, used by NamedQuery and NamedExec.
ExtContext is a union interface which can bind, query, and exec, with Context used by NamedQueryContext and NamedExecContext.
Preparer is an interface used by Preparex.
PreparerContext is an interface used by PreparexContext.
Queryer is an interface used by Get and Select.
QueryerContext is an interface used by GetContext and SelectContext.