borp

package module
v0.0.0-...-02fd711 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 29, 2024 License: MIT Imports: 13 Imported by: 2

README

Boulder's Object Relational Persistence

Once upon a time, there was gorp. Gorp was good, and Let's Encrypt's adopted it for Boulder's database persistence layer. The maintainers became busy with other projects, and gorp stopped getting updated. However, it's still very useful to Boulder, and there are a few tweaks we'd like to make for our own purposes, so we've forked it.

We maintain this primarily for Boulder's own use, and intend to make some API breaking changes during 2023. You are welcome to use our fork if you like it, but we do not expect to spend a lot of time maintaining it for non-Boulder uses.

In particular we maintain this to use with MariaDB 10.5+ and InnoDB.

Quickstart

package main

import (
    "database/sql"
    "github.com/letsencrypt/borp"
    _ "github.com/mattn/go-sqlite3"
    "log"
    "time"
)

func main() {
    // initialize the DbMap
    dbmap := initDb()
    defer dbmap.Db.Close()

    // delete any existing rows
    err := dbmap.TruncateTables()
    checkErr(err, "TruncateTables failed")

    // create two posts
    p1 := newPost("Go 1.1 released!", "Lorem ipsum lorem ipsum")
    p2 := newPost("Go 1.2 released!", "Lorem ipsum lorem ipsum")

    // insert rows - auto increment PKs will be set properly after the insert
    err = dbmap.Insert(&p1, &p2)
    checkErr(err, "Insert failed")

    // use convenience SelectInt
    count, err := dbmap.SelectInt("select count(*) from posts")
    checkErr(err, "select count(*) failed")
    log.Println("Rows after inserting:", count)

    // update a row
    p2.Title = "Go 1.2 is better than ever"
    count, err = dbmap.Update(&p2)
    checkErr(err, "Update failed")
    log.Println("Rows updated:", count)

    // fetch one row - note use of "post_id" instead of "Id" since column is aliased
    //
    // Postgres users should use $1 instead of ? placeholders
    // See 'Known Issues' below

    err = dbmap.SelectOne(&p2, "select * from posts where post_id=?", p2.Id)
    checkErr(err, "SelectOne failed")
    log.Println("p2 row:", p2)

    // fetch all rows
    var posts []Post
    _, err = dbmap.Select(&posts, "select * from posts order by post_id")
    checkErr(err, "Select failed")
    log.Println("All rows:")
    for x, p := range posts {
        log.Printf("    %d: %v\n", x, p)
    }

    // delete row by PK
    count, err = dbmap.Delete(&p1)
    checkErr(err, "Delete failed")
    log.Println("Rows deleted:", count)

    // delete row manually via Exec
    _, err = dbmap.Exec("delete from posts where post_id=?", p2.Id)
    checkErr(err, "Exec failed")

    // confirm count is zero
    count, err = dbmap.SelectInt("select count(*) from posts")
    checkErr(err, "select count(*) failed")
    log.Println("Row count - should be zero:", count)

    log.Println("Done!")
}

type Post struct {
    // db tag lets you specify the column name if it differs from the struct field
    Id      int64  `db:"post_id"`
    Created int64
    Title   string `db:",size:50"`               // Column size set to 50
    Body    string `db:"article_body,size:1024"` // Set both column name and size
}

func newPost(title, body string) Post {
    return Post{
        Created: time.Now().UnixNano(),
        Title:   title,
        Body:    body,
    }
}

func initDb() *borp.DbMap {
    // connect to db using standard Go database/sql API
    // use whatever database/sql driver you wish
    db, err := sql.Open("sqlite3", "/tmp/post_db.bin")
    checkErr(err, "sql.Open failed")

    // construct a borp DbMap
    dbmap := &borp.DbMap{Db: db, Dialect: borp.SqliteDialect{}}

    // add a table, setting the table name to 'posts' and
    // specifying that the Id property is an auto incrementing PK
    dbmap.AddTableWithName(Post{}, "posts").SetKeys(true, "Id")

    // create the table. in a production system you'd generally
    // use a migration tool, or create the tables via scripts
    err = dbmap.CreateTablesIfNotExists()
    checkErr(err, "Create tables failed")

    return dbmap
}

func checkErr(err error, msg string) {
    if err != nil {
        log.Fatalln(msg, err)
    }
}

Examples

Mapping structs to tables

First define some types:

type Invoice struct {
    Id       int64
    Created  int64
    Updated  int64
    Memo     string
    PersonId int64
}

type Person struct {
    Id      int64
    Created int64
    Updated int64
    FName   string
    LName   string
}

// Example of using tags to alias fields to column names
// The 'db' value is the column name
//
// A hyphen will cause borp to skip this field, similar to the
// Go json package.
//
// This is equivalent to using the ColMap methods:
//
//   table := dbmap.AddTableWithName(Product{}, "product")
//   table.ColMap("Id").Rename("product_id")
//   table.ColMap("Price").Rename("unit_price")
//   table.ColMap("IgnoreMe").SetTransient(true)
//
// You can optionally declare the field to be a primary key and/or autoincrement
//
type Product struct {
    Id         int64     `db:"product_id, primarykey, autoincrement"`
    Price      int64     `db:"unit_price"`
    IgnoreMe   string    `db:"-"`
}

Then create a mapper, typically you'd do this one time at app startup:

// connect to db using standard Go database/sql API
// use whatever database/sql driver you wish
db, err := sql.Open("mymysql", "tcp:localhost:3306*mydb/myuser/mypassword")

// construct a borp DbMap
dbmap := &borp.DbMap{Db: db, Dialect: borp.MySQLDialect{"InnoDB", "UTF8"}}

// register the structs you wish to use with borp
// you can also use the shorter dbmap.AddTable() if you
// don't want to override the table name
//
// SetKeys(true) means we have a auto increment primary key, which
// will get automatically bound to your struct post-insert
//
t1 := dbmap.AddTableWithName(Invoice{}, "invoice_test").SetKeys(true, "Id")
t2 := dbmap.AddTableWithName(Person{}, "person_test").SetKeys(true, "Id")
t3 := dbmap.AddTableWithName(Product{}, "product_test").SetKeys(true, "Id")
Struct Embedding

borp supports embedding structs. For example:

type Names struct {
    FirstName string
    LastName  string
}

type WithEmbeddedStruct struct {
    Id int64
    Names
}

es := &WithEmbeddedStruct{-1, Names{FirstName: "Alice", LastName: "Smith"}}
err := dbmap.Insert(es)

See the TestWithEmbeddedStruct function in borp_test.go for a full example.

Create/Drop Tables

Automatically create / drop registered tables. This is useful for unit tests but is entirely optional. You can of course use borp with tables created manually, or with a separate migration tool (like sql-migrate, goose or migrate).

// create all registered tables
dbmap.CreateTables()

// same as above, but uses "if not exists" clause to skip tables that are
// already defined
dbmap.CreateTablesIfNotExists()

// drop
dbmap.DropTables()
SQL Logging

Optionally you can pass in a logger to trace all SQL statements. I recommend enabling this initially while you're getting the feel for what borp is doing on your behalf.

Borp defines a GorpLogger interface that Go's built in log.Logger satisfies. However, you can write your own GorpLogger implementation, or use a package such as glog if you want more control over how statements are logged.

// Will log all SQL statements + args as they are run
// The first arg is a string prefix to prepend to all log messages
dbmap.TraceOn("[gorp]", log.New(os.Stdout, "myapp:", log.Lmicroseconds))

// Turn off tracing
dbmap.TraceOff()
Insert
// Must declare as pointers so optional callback hooks
// can operate on your data, not copies
inv1 := &Invoice{0, 100, 200, "first order", 0}
inv2 := &Invoice{0, 100, 200, "second order", 0}

// Insert your rows
err := dbmap.Insert(inv1, inv2)

// Because we called SetKeys(true) on Invoice, the Id field
// will be populated after the Insert() automatically
fmt.Printf("inv1.Id=%d  inv2.Id=%d\n", inv1.Id, inv2.Id)
Update

Continuing the above example, use the Update method to modify an Invoice:

// count is the # of rows updated, which should be 1 in this example
count, err := dbmap.Update(inv1)
Delete

If you have primary key(s) defined for a struct, you can use the Delete method to remove rows:

count, err := dbmap.Delete(inv1)
Select by Key

Use the Get method to fetch a single row by primary key. It returns nil if no row is found.

// fetch Invoice with Id=99
obj, err := dbmap.Get(Invoice{}, 99)
inv := obj.(*Invoice)
Ad Hoc SQL
SELECT

Select() and SelectOne() provide a simple way to bind arbitrary queries to a slice or a single struct.

// Select a slice - first return value is not needed when a slice pointer is passed to Select()
var posts []Post
_, err := dbmap.Select(&posts, "select * from post order by id")

// You can also use primitive types
var ids []string
_, err := dbmap.Select(&ids, "select id from post")

// Select a single row.
// Returns an error if no row found, or if more than one row is found
var post Post
err := dbmap.SelectOne(&post, "select * from post where id=?", id)

Want to do joins? Just write the SQL and the struct. Borp will bind them:

// Define a type for your join
// It *must* contain all the columns in your SELECT statement
//
// The names here should match the aliased column names you specify
// in your SQL - no additional binding work required.  simple.
//
type InvoicePersonView struct {
    InvoiceId   int64
    PersonId    int64
    Memo        string
    FName       string
}

// Create some rows
p1 := &Person{0, 0, 0, "bob", "smith"}
err = dbmap.Insert(p1)
checkErr(err, "Insert failed")

// notice how we can wire up p1.Id to the invoice easily
inv1 := &Invoice{0, 0, 0, "xmas order", p1.Id}
err = dbmap.Insert(inv1)
checkErr(err, "Insert failed")

// Run your query
query := "select i.Id InvoiceId, p.Id PersonId, i.Memo, p.FName " +
	"from invoice_test i, person_test p " +
	"where i.PersonId = p.Id"

// pass a slice to Select()
var list []InvoicePersonView
_, err := dbmap.Select(&list, query)

// this should test true
expected := InvoicePersonView{inv1.Id, p1.Id, inv1.Memo, p1.FName}
if reflect.DeepEqual(list[0], expected) {
    fmt.Println("Woot! My join worked!")
}
SELECT string or int64

Borp provides a few convenience methods for selecting a single string or int64.

// select single int64 from db (use $1 instead of ? for postgresql)
i64, err := dbmap.SelectInt("select count(*) from foo where blah=?", blahVal)

// select single string from db:
s, err := dbmap.SelectStr("select name from foo where blah=?", blahVal)

Named bind parameters

You may use a map or struct to bind parameters by name. This is supported in the Exec and Select* functions on DbMap and Transaction. It is not supported by Prepare, Query, or QueryRow.

_, err := dbm.Select(&dest, "select * from Foo where name = :name and age = :age", map[string]interface{}{
  "name": "Rob",
  "age": 31,
})
UPDATE / DELETE

You can execute raw SQL if you wish. Particularly good for batch operations.

res, err := dbmap.Exec("delete from invoice_test where PersonId=?", 10)
Transactions

You can batch operations into a transaction:

func InsertInv(dbmap *DbMap, inv *Invoice, per *Person) error {
    // Start a new transaction
    trans, err := dbmap.Begin()
    if err != nil {
        return err
    }

    err = trans.Insert(per)
    checkErr(err, "Insert failed")

    inv.PersonId = per.Id
    err = trans.Insert(inv)
    checkErr(err, "Insert failed")

    // if the commit is successful, a nil error is returned
    return trans.Commit()
}
Hooks

Use hooks to update data before/after saving to the db. Good for timestamps:

// implement the PreInsert and PreUpdate hooks
func (i *Invoice) PreInsert(s borp.SqlExecutor) error {
    i.Created = time.Now().UnixNano()
    i.Updated = i.Created
    return nil
}

func (i *Invoice) PreUpdate(s borp.SqlExecutor) error {
    i.Updated = time.Now().UnixNano()
    return nil
}

// You can use the SqlExecutor to cascade additional SQL
// Take care to avoid cycles. Borp won't prevent them.
//
// Here's an example of a cascading delete
//
func (p *Person) PreDelete(s borp.SqlExecutor) error {
    query := "delete from invoice_test where PersonId=?"

    _, err := s.Exec(query, p.Id)
    if err != nil {
        return err
    }
    return nil
}

Full list of hooks that you can implement:

PostGet
PreInsert
PostInsert
PreUpdate
PostUpdate
PreDelete
PostDelete

All have the same signature.  for example:

func (p *MyStruct) PostUpdate(s borp.SqlExecutor) error
Optimistic Locking
Note that this behaviour has changed in v2. See Migration Guide.

Borp provides a simple optimistic locking feature, similar to Java's JPA, that will raise an error if you try to update/delete a row whose version column has a value different than the one in memory. This provides a safe way to do "select then update" style operations without explicit read and write locks.

// Version is an auto-incremented number, managed by borp
// If this property is present on your struct, update
// operations will be constrained
//
// For example, say we defined Person as:

type Person struct {
    Id       int64
    Created  int64
    Updated  int64
    FName    string
    LName    string

    // automatically used as the Version col
    // use table.SetVersionCol("columnName") to map a different
    // struct field as the version field
    Version  int64
}

p1 := &Person{0, 0, 0, "Bob", "Smith", 0}
err = dbmap.Insert(p1)  // Version is now 1
checkErr(err, "Insert failed")

obj, err := dbmap.Get(Person{}, p1.Id)
p2 := obj.(*Person)
p2.LName = "Edwards"
_,err = dbmap.Update(p2)  // Version is now 2
checkErr(err, "Update failed")

p1.LName = "Howard"

// Raises error because p1.Version == 1, which is out of date
count, err := dbmap.Update(p1)
_, ok := err.(borp.OptimisticLockError)
if ok {
    // should reach this statement

    // in a real app you might reload the row and retry, or
    // you might propegate this to the user, depending on the desired
    // semantics
    fmt.Printf("Tried to update row with stale data: %v\n", err)
} else {
    // some other db error occurred - log or return up the stack
    fmt.Printf("Unknown db err: %v\n", err)
}
Adding INDEX(es) on column(s) beyond the primary key

Indexes are frequently critical for performance. Here is how to add them to your tables.

In the example below we put an index both on the Id field, and on the AcctId field.

type Account struct {
	Id      int64
	AcctId  string // e.g. this might be a long uuid for portability
}

// indexType (the 2nd param to AddIndex call) is "Btree" or "Hash" for MySQL.
// demonstrate adding a second index on AcctId, and constrain that field to have unique values.
dbm.AddTable(iptab.Account{}).SetKeys(true, "Id").AddIndex("AcctIdIndex", "Btree", []string{"AcctId"}).SetUnique(true)

err = dbm.CreateTablesIfNotExists()
checkErr(err, "CreateTablesIfNotExists failed")

err = dbm.CreateIndex()
checkErr(err, "CreateIndex failed")

Check the effect of the CreateIndex() call in mysql:

$ mysql

MariaDB [test]> show create table Account;
+---------+--------------------------+
| Account | CREATE TABLE `Account` (
  `Id` bigint(20) NOT NULL AUTO_INCREMENT,
  `AcctId` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`Id`),
  UNIQUE KEY `AcctIdIndex` (`AcctId`) USING BTREE   <<<--- yes! index added.
) ENGINE=InnoDB DEFAULT CHARSET=utf8
+---------+--------------------------+

Database Drivers

borp uses the Go 1 database/sql package. A full list of compliant drivers is available here:

http://code.google.com/p/go-wiki/wiki/SQLDrivers

Sadly, SQL databases differ on various issues. borp provides a Dialect interface that should be implemented per database vendor. Dialects are provided for:

  • MySQL
  • PostgreSQL
  • sqlite3

Each of these three databases pass the test suite. See borp_test.go for example DSNs for these three databases.

Sqlite3 Extensions

In order to use sqlite3 extensions you need to first register a custom driver:

import (
	"database/sql"

	// use whatever database/sql driver you wish
	sqlite "github.com/mattn/go-sqlite3"
)

func customDriver() (*sql.DB, error) {

	// create custom driver with extensions defined
	sql.Register("sqlite3-custom", &sqlite.SQLiteDriver{
		Extensions: []string{
			"mod_spatialite",
		},
	})

	// now you can then connect using the 'sqlite3-custom' driver instead of 'sqlite3'
	return sql.Open("sqlite3-custom", "/tmp/post_db.bin")
}

Known Issues

SQL placeholder portability

Different databases use different strings to indicate variable placeholders in prepared SQL statements. Unlike some database abstraction layers (such as JDBC), Go's database/sql does not standardize this.

SQL generated by borp in the Insert, Update, Delete, and Get methods delegates to a Dialect implementation for each database, and will generate portable SQL.

Raw SQL strings passed to Exec, Select, SelectOne, SelectInt, etc will not be parsed. Consequently you may have portability issues if you write a query like this:

// works on MySQL and Sqlite3, but not with Postgresql err :=
dbmap.SelectOne(&val, "select * from foo where id = ?", 30)

In Select and SelectOne you can use named parameters to work around this. The following is portable:

err := dbmap.SelectOne(&val, "select * from foo where id = :id",
map[string]interface{} { "id": 30})

Additionally, when using Postgres as your database, you should utilize $1 instead of ? placeholders as utilizing ? placeholders when querying Postgres will result in pq: operator does not exist errors. Alternatively, use dbMap.Dialect.BindVar(varIdx) to get the proper variable binding for your dialect.

time.Time and time zones

borp will pass time.Time fields through to the database/sql driver, but note that the behavior of this type varies across database drivers.

MySQL users should be especially cautious. See: https://github.com/ziutek/mymysql/pull/77

To avoid any potential issues with timezone/DST, consider:

Running the tests

The included tests may be run against MySQL, Postgres, or sqlite3. You must set two environment variables so the test code knows which driver to use, and how to connect to your database.

# MySQL example:
export GORP_TEST_DSN=gomysql_test/gomysql_test/abc123
export GORP_TEST_DIALECT=mysql

# run the tests
go test

# run the tests and benchmarks
go test -bench="Bench" -benchtime 10

Valid GORP_TEST_DIALECT values are: "mysql"(for mymysql), "gomysql"(for go-sql-driver), "postgres", or "sqlite" See the test_all.sh script for examples of all 3 databases. This is the script I run locally to test the library.

Contributors

  • matthias-margush - column aliasing via tags
  • Rob Figueiredo - @robfig
  • Quinn Slack - @sqs

Documentation

Overview

Package borp provides a simple way to marshal Go structs to and from SQL databases. It uses the database/sql package, and should work with any compliant database/sql driver.

Source code, additional documentation, and examples: https://github.com/letsencrypt/borp

Query Parameters

Borp's Exec, Select*, Query, and QueryRow methods accept placeholder parameters in the query, to be filled from the args parameters to these functions. Borp supports some additional styles for placeholder parameters:

Named Bind Parameters

For the Exec and Select* methods on DbMap and Transaction, Borp supports named bind parameters. To use named bind parameters, instead of a list of parameters, pass a single `map[string]interface{}` to these functions. And instead of using ? in the query, use placeholder parameters of the form :word. Before running the query, Borp will bind each named placeholder parameter to the corresponding value found by looking up "word" in the map.

Example:

_, err := dbm.Select(&dest, "select * from Foo where name = :name and age = :age",
  map[string]interface{}{
    "name": "Rob",
    "age": 31,
  })

Expanding Slices

If you set the ExpandSlices field of DbMap to true, placeholders that bind to slices will be handled specially. Borp will modify the query, adding more placeholders to match the number of entries in the slice.

For example, given the scenario bellow:

dbmap.Select(&output, "SELECT 1 FROM example WHERE id IN (:IDs)", map[string]interface{}{
  "IDs": []int64{1, 2, 3},
})

The executed query would be:

SELECT 1 FROM example WHERE id IN (:IDs0,:IDs1,:IDs2)

With the mapper:

map[string]interface{}{
  "IDs":  []int64{1, 2, 3},
  "IDs0": int64(1),
  "IDs1": int64(2),
  "IDs2": int64(3),
}

It is also flexible for custom slice types. The value just need to implement stringer or numberer interfaces.

type CustomValue string

const (
  CustomValueHey CustomValue = "hey"
  CustomValueOh  CustomValue = "oh"
)

type CustomValues []CustomValue

func (c CustomValues) ToStringSlice() []string {
  values := make([]string, len(c))
  for i := range c {
    values[i] = string(c[i])
  }
  return values
}

func query() {
  // ...
  result, err := dbmap.Select(&output, "SELECT 1 FROM example WHERE value IN (:Values)", map[string]interface{}{
    "Values": CustomValues([]CustomValue{CustomValueHey}),
  })
  // ...
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NonFatalError

func NonFatalError(err error) bool

returns true if the error is non-fatal (ie, we shouldn't immediately return)

func SelectFloat

func SelectFloat(ctx context.Context, e SqlExecutor, query string, args ...interface{}) (float64, error)

SelectFloat executes the given query, which should be a SELECT statement for a single float column, and returns the value of the first row returned. If no rows are found, zero is returned.

func SelectInt

func SelectInt(ctx context.Context, e SqlExecutor, query string, args ...interface{}) (int64, error)

SelectInt executes the given query, which should be a SELECT statement for a single integer column, and returns the value of the first row returned. If no rows are found, zero is returned.

func SelectNullFloat

func SelectNullFloat(ctx context.Context, e SqlExecutor, query string, args ...interface{}) (sql.NullFloat64, error)

SelectNullFloat executes the given query, which should be a SELECT statement for a single float column, and returns the value of the first row returned. If no rows are found, the empty sql.NullInt64 value is returned.

func SelectNullInt

func SelectNullInt(ctx context.Context, e SqlExecutor, query string, args ...interface{}) (sql.NullInt64, error)

SelectNullInt executes the given query, which should be a SELECT statement for a single integer column, and returns the value of the first row returned. If no rows are found, the empty sql.NullInt64 value is returned.

func SelectNullStr

func SelectNullStr(ctx context.Context, e SqlExecutor, query string, args ...interface{}) (sql.NullString, error)

SelectNullStr executes the given query, which should be a SELECT statement for a single char/varchar column, and returns the value of the first row returned. If no rows are found, the empty sql.NullString is returned.

func SelectOne

func SelectOne(ctx context.Context, m *DbMap, e SqlExecutor, holder interface{}, query string, args ...interface{}) error

SelectOne executes the given query (which should be a SELECT statement) and binds the result to holder, which must be a pointer.

If no row is found, an error (sql.ErrNoRows specifically) will be returned.

If more than one row is found, an error will be returned.

func SelectStr

func SelectStr(ctx context.Context, e SqlExecutor, query string, args ...interface{}) (string, error)

SelectStr executes the given query, which should be a SELECT statement for a single char/varchar column, and returns the value of the first row returned. If no rows are found, an empty string is returned.

Types

type ColumnFilter

type ColumnFilter func(*ColumnMap) bool

Used to filter columns when selectively updating

type ColumnMap

type ColumnMap struct {
	// Column name in db table
	ColumnName string

	// If true, this column is skipped in generated SQL statements
	Transient bool

	// If true, " unique" is added to create table statements.
	// Not used elsewhere
	Unique bool

	// Query used for getting generated id after insert
	GeneratedIdQuery string

	// Passed to Dialect.ToSqlType() to assist in informing the
	// correct column type to map to in CreateTables()
	MaxSize int

	DefaultValue string
	// contains filtered or unexported fields
}

ColumnMap represents a mapping between a Go struct field and a single column in a table. Unique and MaxSize only inform the CreateTables() function and are not used by Insert/Update/Delete/Get.

func (*ColumnMap) Rename

func (c *ColumnMap) Rename(colname string) *ColumnMap

Rename allows you to specify the column name in the table

Example: table.ColMap("Updated").Rename("date_updated")

func (*ColumnMap) SetMaxSize

func (c *ColumnMap) SetMaxSize(size int) *ColumnMap

SetMaxSize specifies the max length of values of this column. This is passed to the dialect.ToSqlType() function, which can use the value to alter the generated type for "create table" statements

func (*ColumnMap) SetNotNull

func (c *ColumnMap) SetNotNull(nn bool) *ColumnMap

SetNotNull adds "not null" to the create table statements for this column, if nn is true.

func (*ColumnMap) SetTransient

func (c *ColumnMap) SetTransient(b bool) *ColumnMap

SetTransient allows you to mark the column as transient. If true this column will be skipped when SQL statements are generated

func (*ColumnMap) SetUnique

func (c *ColumnMap) SetUnique(b bool) *ColumnMap

SetUnique adds "unique" to the create table statements for this column, if b is true.

type CustomScanner

type CustomScanner struct {
	// After a row is scanned, Holder will contain the value from the database column.
	// Initialize the CustomScanner with the concrete Go type you wish the database
	// driver to scan the raw column into.
	Holder interface{}
	// Target typically holds a pointer to the target struct field to bind the Holder
	// value to.
	Target interface{}
	// Binder is a custom function that converts the holder value to the target type
	// and sets target accordingly.  This function should return error if a problem
	// occurs converting the holder to the target.
	Binder func(holder interface{}, target interface{}) error
}

CustomScanner binds a database column value to a Go type

func (CustomScanner) Bind

func (me CustomScanner) Bind() error

Bind is called automatically by gorp after Scan()

type DbMap

type DbMap struct {
	// Db handle to use with this map
	Db *sql.DB

	// Dialect implementation to use with this map
	Dialect Dialect

	TypeConverter TypeConverter

	// ExpandSlices when enabled will convert slice arguments in mappers into flat
	// values. It will modify the query, adding more placeholders, and the mapper,
	// adding each item of the slice as a new unique entry in the mapper.
	ExpandSliceArgs bool
	// contains filtered or unexported fields
}

DbMap is the root gorp mapping object. Create one of these for each database schema you wish to map. Each DbMap contains a list of mapped tables.

Example:

dialect := borp.MySQLDialect{"InnoDB", "UTF8"}
dbmap := &borp.DbMap{Db: db, Dialect: dialect}

func (*DbMap) AddTable

func (m *DbMap) AddTable(i interface{}) *TableMap

AddTable registers the given interface type with borp. The table name will be given the name of the TypeOf(i). You must call this function, or AddTableWithName, for any struct type you wish to persist with the given DbMap.

This operation is idempotent. If i's type is already mapped, the existing *TableMap is returned

func (*DbMap) AddTableDynamic

func (m *DbMap) AddTableDynamic(inp DynamicTable, schema string) *TableMap

AddTableDynamic registers the given interface type with borp. The table name will be dynamically determined at runtime by using the GetTableName method on DynamicTable interface

func (*DbMap) AddTableWithName

func (m *DbMap) AddTableWithName(i interface{}, name string) *TableMap

AddTableWithName has the same behavior as AddTable, but sets table.TableName to name.

func (*DbMap) AddTableWithNameAndSchema

func (m *DbMap) AddTableWithNameAndSchema(i interface{}, schema string, name string) *TableMap

AddTableWithNameAndSchema has the same behavior as AddTable, but sets table.TableName to name.

func (*DbMap) BeginTx

func (m *DbMap) BeginTx(ctx context.Context) (*Transaction, error)

BeginTx starts a borp Transaction. It uses database/sql.DB.BeginTx under the hood so the same guarantees apply. https://pkg.go.dev/database/sql#DB.BeginTx

> The provided context is used until the transaction is committed or rolled back. If the context > is canceled, the sql package will roll back the transaction. Tx.Commit will return an error if > the context provided to BeginTx is canceled.

func (*DbMap) CreateIndex

func (m *DbMap) CreateIndex(ctx context.Context) error

func (*DbMap) CreateTables

func (m *DbMap) CreateTables(ctx context.Context) error

CreateTables iterates through TableMaps registered to this DbMap and executes "create table" statements against the database for each.

This is particularly useful in unit tests where you want to create and destroy the schema automatically.

func (*DbMap) CreateTablesIfNotExists

func (m *DbMap) CreateTablesIfNotExists(ctx context.Context) error

CreateTablesIfNotExists is similar to CreateTables, but starts each statement with "create table if not exists" so that existing tables do not raise errors

func (*DbMap) Delete

func (m *DbMap) Delete(ctx context.Context, list ...interface{}) (int64, error)

Delete runs a SQL DELETE statement for each element in list. List items must be pointers.

The hook functions PreDelete() and/or PostDelete() will be executed before/after the DELETE statement if the interface defines them.

Returns the number of rows deleted.

Returns an error if SetKeys has not been called on the TableMap Panics if any interface in the list has not been registered with AddTable

func (*DbMap) DropTable

func (m *DbMap) DropTable(ctx context.Context, table interface{}) error

DropTable drops an individual table. Returns an error when the table does not exist.

func (*DbMap) DropTableIfExists

func (m *DbMap) DropTableIfExists(ctx context.Context, table interface{}) error

DropTableIfExists drops an individual table when the table exists.

func (*DbMap) DropTables

func (m *DbMap) DropTables(ctx context.Context) error

DropTables iterates through TableMaps registered to this DbMap and executes "drop table" statements against the database for each.

func (*DbMap) DropTablesIfExists

func (m *DbMap) DropTablesIfExists(ctx context.Context) error

DropTablesIfExists is the same as DropTables, but uses the "if exists" clause to avoid errors for tables that do not exist.

func (*DbMap) DynamicTableFor

func (m *DbMap) DynamicTableFor(tableName string, checkPK bool) (*TableMap, error)

DynamicTableFor returns the *TableMap for the dynamic table corresponding to the input tablename If no table is mapped to that tablename an error is returned. If checkPK is true and the mapped table has no registered PKs, an error is returned.

func (*DbMap) ExecContext

func (m *DbMap) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)

Exec runs an arbitrary SQL statement. args represent the bind parameters. This is equivalent to running: ExecContext() using database/sql

func (*DbMap) Get

func (m *DbMap) Get(ctx context.Context, i interface{}, keys ...interface{}) (interface{}, error)

Get runs a SQL SELECT to fetch a single row from the table based on the primary key(s)

i should be an empty value for the struct to load. keys should be the primary key value(s) for the row to load. If multiple keys exist on the table, the order should match the column order specified in SetKeys() when the table mapping was defined.

The hook function PostGet() will be executed after the SELECT statement if the interface defines them.

Returns a pointer to a struct that matches or nil if no row is found.

Returns an error if SetKeys has not been called on the TableMap Panics if any interface in the list has not been registered with AddTable

func (*DbMap) Insert

func (m *DbMap) Insert(ctx context.Context, list ...interface{}) error

Insert runs a SQL INSERT statement for each element in list. List items must be pointers.

Any interface whose TableMap has an auto-increment primary key will have its last insert id bound to the PK field on the struct.

The hook functions PreInsert() and/or PostInsert() will be executed before/after the INSERT statement if the interface defines them.

Panics if any interface in the list has not been registered with AddTable

func (*DbMap) PrepareContext

func (m *DbMap) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)

Prepare creates a prepared statement for later queries or executions. Multiple queries or executions may be run concurrently from the returned statement. This is equivalent to running: PrepareContext() using database/sql

func (*DbMap) QueryContext

func (m *DbMap) QueryContext(ctx context.Context, q string, args ...interface{}) (*sql.Rows, error)

This is equivalent to running: QueryContext() using database/sql

func (*DbMap) QueryRowContext

func (m *DbMap) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row

This is equivalent to running: QueryRowContext() using database/sql

func (*DbMap) Select

func (m *DbMap) Select(ctx context.Context, i interface{}, query string, args ...interface{}) ([]interface{}, error)

Select runs an arbitrary SQL query, binding the columns in the result to fields on the struct specified by i. args represent the bind parameters for the SQL statement.

Column names on the SELECT statement should be aliased to the field names on the struct i. Returns an error if one or more columns in the result do not match. It is OK if fields on i are not part of the SQL statement.

The hook function PostGet() will be executed after the SELECT statement if the interface defines them.

Values are returned in one of two ways: 1. If i is a struct or a pointer to a struct, returns a slice of pointers to matching rows of type i. 2. If i is a pointer to a slice, the results will be appended to that slice and nil returned.

i does NOT need to be registered with AddTable()

func (*DbMap) SelectFloat

func (m *DbMap) SelectFloat(ctx context.Context, query string, args ...interface{}) (float64, error)

SelectFloat is a convenience wrapper around the borp.SelectFloat function

func (*DbMap) SelectInt

func (m *DbMap) SelectInt(ctx context.Context, query string, args ...interface{}) (int64, error)

SelectInt is a convenience wrapper around the borp.SelectInt function

func (*DbMap) SelectNullFloat

func (m *DbMap) SelectNullFloat(ctx context.Context, query string, args ...interface{}) (sql.NullFloat64, error)

SelectNullFloat is a convenience wrapper around the borp.SelectNullFloat function

func (*DbMap) SelectNullInt

func (m *DbMap) SelectNullInt(ctx context.Context, query string, args ...interface{}) (sql.NullInt64, error)

SelectNullInt is a convenience wrapper around the borp.SelectNullInt function

func (*DbMap) SelectNullStr

func (m *DbMap) SelectNullStr(ctx context.Context, query string, args ...interface{}) (sql.NullString, error)

SelectNullStr is a convenience wrapper around the borp.SelectNullStr function

func (*DbMap) SelectOne

func (m *DbMap) SelectOne(ctx context.Context, holder interface{}, query string, args ...interface{}) error

SelectOne is a convenience wrapper around the borp.SelectOne function

func (*DbMap) SelectStr

func (m *DbMap) SelectStr(ctx context.Context, query string, args ...interface{}) (string, error)

SelectStr is a convenience wrapper around the borp.SelectStr function

func (*DbMap) TableFor

func (m *DbMap) TableFor(t reflect.Type, checkPK bool) (*TableMap, error)

TableFor returns the *TableMap corresponding to the given Go Type If no table is mapped to that type an error is returned. If checkPK is true and the mapped table has no registered PKs, an error is returned.

func (*DbMap) TraceOff

func (m *DbMap) TraceOff()

TraceOff turns off tracing. It is idempotent.

func (*DbMap) TraceOn

func (m *DbMap) TraceOn(prefix string, logger Logger)

TraceOn turns on SQL statement logging for this DbMap. After this is called, all SQL statements will be sent to the logger. If prefix is a non-empty string, it will be written to the front of all logged strings, which can aid in filtering log lines.

Use TraceOn if you want to spy on the SQL statements that gorp generates.

Note that the base log.Logger type satisfies Logger, but adapters can easily be written for other logging packages (e.g., the golang-sanctioned glog framework).

func (*DbMap) TruncateTables

func (m *DbMap) TruncateTables(ctx context.Context) error

TruncateTables iterates through TableMaps registered to this DbMap and executes "truncate table" statements against the database for each, or in the case of sqlite, a "delete from" with no "where" clause, which uses the truncate optimization (http://www.sqlite.org/lang_delete.html)

func (*DbMap) Update

func (m *DbMap) Update(ctx context.Context, list ...interface{}) (int64, error)

Update runs a SQL UPDATE statement for each element in list. List items must be pointers.

The hook functions PreUpdate() and/or PostUpdate() will be executed before/after the UPDATE statement if the interface defines them.

Returns the number of rows updated.

Returns an error if SetKeys has not been called on the TableMap Panics if any interface in the list has not been registered with AddTable

func (*DbMap) UpdateColumns

func (m *DbMap) UpdateColumns(ctx context.Context, filter ColumnFilter, list ...interface{}) (int64, error)

UpdateColumns runs a SQL UPDATE statement for each element in list. List items must be pointers.

Only the columns accepted by filter are included in the UPDATE.

The hook functions PreUpdate() and/or PostUpdate() will be executed before/after the UPDATE statement if the interface defines them.

Returns the number of rows updated.

Returns an error if SetKeys has not been called on the TableMap Panics if any interface in the list has not been registered with AddTable

type Dialect

type Dialect interface {
	// adds a suffix to any query, usually ";"
	QuerySuffix() string

	// ToSqlType returns the SQL column type to use when creating a
	// table of the given Go Type.  maxsize can be used to switch based on
	// size.  For example, in MySQL []byte could map to BLOB, MEDIUMBLOB,
	// or LONGBLOB depending on the maxsize
	ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string

	// string to append to primary key column definitions
	AutoIncrStr() string

	// string to bind autoincrement columns to. Empty string will
	// remove reference to those columns in the INSERT statement.
	AutoIncrBindValue() string

	AutoIncrInsertSuffix(col *ColumnMap) string

	// string to append to "create table" statement for vendor specific
	// table attributes
	CreateTableSuffix() string

	// string to append to "create index" statement
	CreateIndexSuffix() string

	// string to append to "drop index" statement
	DropIndexSuffix() string

	// string to truncate tables
	TruncateClause() string

	// Bind variable string to use when forming SQL statements
	// in many dbs it is "?", but Postgres appears to use $1
	//
	// i is a zero based index of the bind variable in this statement
	//
	BindVar(i int) string

	// Handles quoting of a field name to ensure that it doesn't raise any
	// SQL parsing exceptions by using a reserved word as a field name.
	QuoteField(field string) string

	// Handles building up of a schema.database string that is compatible with
	// the given dialect
	//
	// schema - The schema that <table> lives in
	// table - The table name
	QuotedTableForQuery(schema string, table string) string

	// Existence clause for table creation / deletion
	IfSchemaNotExists(command, schema string) string
	IfTableExists(command, schema, table string) string
	IfTableNotExists(command, schema, table string) string
}

The Dialect interface encapsulates behaviors that differ across SQL databases. At present the Dialect is only used by CreateTables() but this could change in the future

type DynamicTable

type DynamicTable interface {
	TableName() string
	SetTableName(string)
}

DynamicTable allows the users of gorp to dynamically use different database table names during runtime while sharing the same golang struct for in-memory data

type GorpLogger

type GorpLogger = Logger

GorpLogger is a deprecated alias of Logger.

type HasPostDelete

type HasPostDelete interface {
	PostDelete(SqlExecutor) error
}

HasPostDelete provides PostDelete() which will be executed after the DELETE statement

type HasPostGet

type HasPostGet interface {
	PostGet(SqlExecutor) error
}

HasPostGet provides PostGet() which will be executed after the GET statement.

type HasPostInsert

type HasPostInsert interface {
	PostInsert(SqlExecutor) error
}

HasPostInsert provides PostInsert() which will be executed after the INSERT statement

type HasPostUpdate

type HasPostUpdate interface {
	PostUpdate(SqlExecutor) error
}

HasPostUpdate provides PostUpdate() which will be executed after the UPDATE statement

type HasPreDelete

type HasPreDelete interface {
	PreDelete(SqlExecutor) error
}

HasPreDelete provides PreDelete() which will be executed before the DELETE statement.

type HasPreInsert

type HasPreInsert interface {
	PreInsert(SqlExecutor) error
}

HasPreInsert provides PreInsert() which will be executed before INSERT statement.

type HasPreUpdate

type HasPreUpdate interface {
	PreUpdate(SqlExecutor) error
}

HasPreUpdate provides PreUpdate() which will be executed before UPDATE statement.

type IndexMap

type IndexMap struct {
	// Index name in db table
	IndexName string

	// If true, " unique" is added to create index statements.
	// Not used elsewhere
	Unique bool

	// Index type supported by Dialect
	// Mysql: Btree, Hash.
	// Sqlite: nil.
	IndexType string
	// contains filtered or unexported fields
}

IndexMap represents a mapping between a Go struct field and a single index in a table. Unique and MaxSize only inform the CreateTables() function and are not used by Insert/Update/Delete/Get.

func (*IndexMap) Rename

func (idx *IndexMap) Rename(indname string) *IndexMap

Rename allows you to specify the index name in the table

Example: table.IndMap("customer_test_idx").Rename("customer_idx")

func (*IndexMap) SetIndexType

func (idx *IndexMap) SetIndexType(indtype string) *IndexMap

SetIndexType specifies the index type supported by chousen SQL Dialect

func (*IndexMap) SetUnique

func (idx *IndexMap) SetUnique(b bool) *IndexMap

SetUnique adds "unique" to the create index statements for this index, if b is true.

type IntegerAutoIncrInserter

type IntegerAutoIncrInserter interface {
	InsertAutoIncr(ctx context.Context, exec SqlExecutor, insertSql string, params ...interface{}) (int64, error)
}

IntegerAutoIncrInserter is implemented by dialects that can perform inserts with automatically incremented integer primary keys. If the dialect can handle automatic assignment of more than just integers, see TargetedAutoIncrInserter.

type Logger

type Logger interface {
	Printf(format string, v ...interface{})
}

Logger is the type that gorp uses to log SQL statements. See DbMap.TraceOn.

type MySQLDialect

type MySQLDialect struct {

	// Engine is the storage engine to use "InnoDB" vs "MyISAM" for example
	Engine string

	// Encoding is the character encoding to use for created tables
	Encoding string
}

Implementation of Dialect for MySQL databases.

func (MySQLDialect) AutoIncrBindValue

func (d MySQLDialect) AutoIncrBindValue() string

func (MySQLDialect) AutoIncrInsertSuffix

func (d MySQLDialect) AutoIncrInsertSuffix(col *ColumnMap) string

func (MySQLDialect) AutoIncrStr

func (d MySQLDialect) AutoIncrStr() string

Returns auto_increment

func (MySQLDialect) BindVar

func (d MySQLDialect) BindVar(i int) string

Returns "?"

func (MySQLDialect) CreateIndexSuffix

func (d MySQLDialect) CreateIndexSuffix() string

func (MySQLDialect) CreateTableSuffix

func (d MySQLDialect) CreateTableSuffix() string

Returns engine=%s charset=%s based on values stored on struct

func (MySQLDialect) DropIndexSuffix

func (d MySQLDialect) DropIndexSuffix() string

func (MySQLDialect) IfSchemaNotExists

func (d MySQLDialect) IfSchemaNotExists(command, schema string) string

func (MySQLDialect) IfTableExists

func (d MySQLDialect) IfTableExists(command, schema, table string) string

func (MySQLDialect) IfTableNotExists

func (d MySQLDialect) IfTableNotExists(command, schema, table string) string

func (MySQLDialect) InsertAutoIncr

func (d MySQLDialect) InsertAutoIncr(ctx context.Context, exec SqlExecutor, insertSql string, params ...interface{}) (int64, error)

func (MySQLDialect) QuerySuffix

func (d MySQLDialect) QuerySuffix() string

func (MySQLDialect) QuoteField

func (d MySQLDialect) QuoteField(f string) string

func (MySQLDialect) QuotedTableForQuery

func (d MySQLDialect) QuotedTableForQuery(schema string, table string) string

func (MySQLDialect) SleepClause

func (d MySQLDialect) SleepClause(s time.Duration) string

func (MySQLDialect) ToSqlType

func (d MySQLDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string

func (MySQLDialect) TruncateClause

func (d MySQLDialect) TruncateClause() string

type NoFieldInTypeError

type NoFieldInTypeError struct {
	TypeName        string
	MissingColNames []string
}

A non-fatal error, when a select query returns columns that do not exist as fields in the struct it is being mapped to TODO: discuss wether this needs an error. encoding/json silently ignores missing fields

func (*NoFieldInTypeError) Error

func (err *NoFieldInTypeError) Error() string

type NullTime

type NullTime struct {
	Time  time.Time
	Valid bool // Valid is true if Time is not NULL
}

A nullable Time value

func (*NullTime) Scan

func (nt *NullTime) Scan(value interface{}) error

Scan implements the Scanner interface.

func (NullTime) Value

func (nt NullTime) Value() (driver.Value, error)

Value implements the driver Valuer interface.

type OptimisticLockError

type OptimisticLockError struct {
	// Table name where the lock error occurred
	TableName string

	// Primary key values of the row being updated/deleted
	Keys []interface{}

	// true if a row was found with those keys, indicating the
	// LocalVersion is stale.  false if no value was found with those
	// keys, suggesting the row has been deleted since loaded, or
	// was never inserted to begin with
	RowExists bool

	// Version value on the struct passed to Update/Delete. This value is
	// out of sync with the database.
	LocalVersion int64
}

OptimisticLockError is returned by Update() or Delete() if the struct being modified has a Version field and the value is not equal to the current value in the database

func (OptimisticLockError) Error

func (e OptimisticLockError) Error() string

Error returns a description of the cause of the lock error

type PostgresDialect

type PostgresDialect struct {
	LowercaseFields bool
	// contains filtered or unexported fields
}

func (PostgresDialect) AutoIncrBindValue

func (d PostgresDialect) AutoIncrBindValue() string

func (PostgresDialect) AutoIncrInsertSuffix

func (d PostgresDialect) AutoIncrInsertSuffix(col *ColumnMap) string

func (PostgresDialect) AutoIncrStr

func (d PostgresDialect) AutoIncrStr() string

Returns empty string

func (PostgresDialect) BindVar

func (d PostgresDialect) BindVar(i int) string

Returns "$(i+1)"

func (PostgresDialect) CreateIndexSuffix

func (d PostgresDialect) CreateIndexSuffix() string

func (PostgresDialect) CreateTableSuffix

func (d PostgresDialect) CreateTableSuffix() string

Returns suffix

func (PostgresDialect) DropIndexSuffix

func (d PostgresDialect) DropIndexSuffix() string

func (PostgresDialect) IfSchemaNotExists

func (d PostgresDialect) IfSchemaNotExists(command, schema string) string

func (PostgresDialect) IfTableExists

func (d PostgresDialect) IfTableExists(command, schema, table string) string

func (PostgresDialect) IfTableNotExists

func (d PostgresDialect) IfTableNotExists(command, schema, table string) string

func (PostgresDialect) InsertAutoIncrToTarget

func (d PostgresDialect) InsertAutoIncrToTarget(ctx context.Context, exec SqlExecutor, insertSql string, target interface{}, params ...interface{}) error

func (PostgresDialect) QuerySuffix

func (d PostgresDialect) QuerySuffix() string

func (PostgresDialect) QuoteField

func (d PostgresDialect) QuoteField(f string) string

func (PostgresDialect) QuotedTableForQuery

func (d PostgresDialect) QuotedTableForQuery(schema string, table string) string

func (PostgresDialect) SleepClause

func (d PostgresDialect) SleepClause(s time.Duration) string

func (PostgresDialect) ToSqlType

func (d PostgresDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string

func (PostgresDialect) TruncateClause

func (d PostgresDialect) TruncateClause() string

type SqlExecutor

type SqlExecutor interface {
	Get(ctx context.Context, i interface{}, keys ...interface{}) (interface{}, error)
	Insert(ctx context.Context, list ...interface{}) error
	Update(ctx context.Context, list ...interface{}) (int64, error)
	Delete(ctx context.Context, list ...interface{}) (int64, error)
	Select(ctx context.Context, i interface{}, query string, args ...interface{}) ([]interface{}, error)
	SelectInt(ctx context.Context, query string, args ...interface{}) (int64, error)
	SelectNullInt(ctx context.Context, query string, args ...interface{}) (sql.NullInt64, error)
	SelectFloat(ctx context.Context, query string, args ...interface{}) (float64, error)
	SelectNullFloat(ctx context.Context, query string, args ...interface{}) (sql.NullFloat64, error)
	SelectStr(ctx context.Context, query string, args ...interface{}) (string, error)
	SelectNullStr(ctx context.Context, query string, args ...interface{}) (sql.NullString, error)
	SelectOne(ctx context.Context, holder interface{}, query string, args ...interface{}) error

	// These method signatures are shared with *sql.DB.
	// Stylistically, `Context` in the name is redundant. It is the future, everything takes a context.
	// But since these three functions delegate to functions of the same name on *sql.DB, it would be
	// a little confusing if we had, e.g. `Exec` (taking a context), which delegates to
	// `sql.DB.ExecContext` (taking a context) as opposed to `sql.DB.Exec` (taking no context).
	// So we don't bother with `Context` in the name for Get, Insert, etc., but we do for these.
	ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
	QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
	QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row
}

SqlExecutor exposes gorp operations that can be run from Pre/Post hooks. This hides whether the current operation that triggered the hook is in a transaction.

See the DbMap function docs for each of the functions below for more information.

type SqlTyper

type SqlTyper interface {
	SqlType() driver.Value
}

SqlTyper is a type that returns its database type. Most of the time, the type can just use "database/sql/driver".Valuer; but when it returns nil for its empty value, it needs to implement SqlTyper to have its column type detected properly during table creation.

type SqliteDialect

type SqliteDialect struct {
	// contains filtered or unexported fields
}

func (SqliteDialect) AutoIncrBindValue

func (d SqliteDialect) AutoIncrBindValue() string

func (SqliteDialect) AutoIncrInsertSuffix

func (d SqliteDialect) AutoIncrInsertSuffix(col *ColumnMap) string

func (SqliteDialect) AutoIncrStr

func (d SqliteDialect) AutoIncrStr() string

Returns autoincrement

func (SqliteDialect) BindVar

func (d SqliteDialect) BindVar(i int) string

Returns "?"

func (SqliteDialect) CreateIndexSuffix

func (d SqliteDialect) CreateIndexSuffix() string

func (SqliteDialect) CreateTableSuffix

func (d SqliteDialect) CreateTableSuffix() string

Returns suffix

func (SqliteDialect) DropIndexSuffix

func (d SqliteDialect) DropIndexSuffix() string

func (SqliteDialect) IfSchemaNotExists

func (d SqliteDialect) IfSchemaNotExists(command, schema string) string

func (SqliteDialect) IfTableExists

func (d SqliteDialect) IfTableExists(command, schema, table string) string

func (SqliteDialect) IfTableNotExists

func (d SqliteDialect) IfTableNotExists(command, schema, table string) string

func (SqliteDialect) InsertAutoIncr

func (d SqliteDialect) InsertAutoIncr(ctx context.Context, exec SqlExecutor, insertSql string, params ...interface{}) (int64, error)

func (SqliteDialect) QuerySuffix

func (d SqliteDialect) QuerySuffix() string

func (SqliteDialect) QuoteField

func (d SqliteDialect) QuoteField(f string) string

func (SqliteDialect) QuotedTableForQuery

func (d SqliteDialect) QuotedTableForQuery(schema string, table string) string

sqlite does not have schemas like PostgreSQL does, so just escape it like normal

func (SqliteDialect) ToSqlType

func (d SqliteDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string

func (SqliteDialect) TruncateClause

func (d SqliteDialect) TruncateClause() string

With sqlite, there technically isn't a TRUNCATE statement, but a DELETE FROM uses a truncate optimization: http://www.sqlite.org/lang_delete.html

type TableMap

type TableMap struct {
	// Name of database table.
	TableName  string
	SchemaName string

	Columns []*ColumnMap
	// contains filtered or unexported fields
}

TableMap represents a mapping between a Go struct and a database table Use dbmap.AddTable() or dbmap.AddTableWithName() to create these

func (*TableMap) AddIndex

func (t *TableMap) AddIndex(name string, idxtype string, columns []string) *IndexMap

AddIndex registers the index with gorp for specified table with given parameters. This operation is idempotent. If index is already mapped, the existing *IndexMap is returned Function will panic if one of the given for index columns does not exists

Automatically calls ResetSql() to ensure SQL statements are regenerated.

func (*TableMap) ColMap

func (t *TableMap) ColMap(field string) *ColumnMap

ColMap returns the ColumnMap pointer matching the given struct field name. It panics if the struct does not contain a field matching this name.

func (*TableMap) DropIndex

func (t *TableMap) DropIndex(ctx context.Context, name string) error

func (*TableMap) IdxMap

func (t *TableMap) IdxMap(field string) *IndexMap

IdxMap returns the IndexMap pointer matching the given index name.

func (*TableMap) ResetSql

func (t *TableMap) ResetSql()

ResetSql removes cached insert/update/select/delete SQL strings associated with this TableMap. Call this if you've modified any column names or the table name itself.

func (*TableMap) SetKeys

func (t *TableMap) SetKeys(isAutoIncr bool, fieldNames ...string) *TableMap

SetKeys lets you specify the fields on a struct that map to primary key columns on the table. If isAutoIncr is set, result.LastInsertId() will be used after INSERT to bind the generated id to the Go struct.

Automatically calls ResetSql() to ensure SQL statements are regenerated.

Panics if isAutoIncr is true, and fieldNames length != 1

func (*TableMap) SetUniqueTogether

func (t *TableMap) SetUniqueTogether(fieldNames ...string) *TableMap

SetUniqueTogether lets you specify uniqueness constraints across multiple columns on the table. Each call adds an additional constraint for the specified columns.

Automatically calls ResetSql() to ensure SQL statements are regenerated.

Panics if fieldNames length < 2.

func (*TableMap) SetVersionCol

func (t *TableMap) SetVersionCol(field string) *ColumnMap

SetVersionCol sets the column to use as the Version field. By default the "Version" field is used. Returns the column found, or panics if the struct does not contain a field matching this name.

Automatically calls ResetSql() to ensure SQL statements are regenerated.

func (*TableMap) SqlForCreate

func (t *TableMap) SqlForCreate(ifNotExists bool) string

SqlForCreateTable gets a sequence of SQL commands that will create the specified table and any associated schema

type TargetQueryInserter

type TargetQueryInserter interface {
	// TargetQueryInserter runs an insert operation and assigns the
	// automatically generated primary key retrived by the query
	// extracted from the GeneratedIdQuery field of the id column.
	InsertQueryToTarget(ctx context.Context, xec SqlExecutor, insertSql, idSql string, target interface{}, params ...interface{}) error
}

TargetQueryInserter is implemented by dialects that can perform assignment of integer primary key type by executing a query like "select sequence.currval from dual".

type TargetedAutoIncrInserter

type TargetedAutoIncrInserter interface {
	// InsertAutoIncrToTarget runs an insert operation and assigns the
	// automatically generated primary key directly to the passed in
	// target.  The target should be a pointer to the primary key
	// field of the value being inserted.
	InsertAutoIncrToTarget(ctx context.Context, xec SqlExecutor, insertSql string, target interface{}, params ...interface{}) error
}

TargetedAutoIncrInserter is implemented by dialects that can perform automatic assignment of any primary key type (i.e. strings for uuids, integers for serials, etc).

type Transaction

type Transaction struct {
	// contains filtered or unexported fields
}

Transaction represents a database transaction. Insert/Update/Delete/Get/Exec operations will be run in the context of that transaction. Transactions should be terminated with a call to Commit() or Rollback()

func (*Transaction) Commit

func (t *Transaction) Commit() error

Commit commits the underlying database transaction.

func (*Transaction) Delete

func (t *Transaction) Delete(ctx context.Context, list ...interface{}) (int64, error)

Delete has the same behavior as DbMap.Delete(), but runs in a transaction.

func (*Transaction) ExecContext

func (t *Transaction) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)

ExecContext has the same behavior as DbMap.ExecContext(), but runs in a transaction.

func (*Transaction) Get

func (t *Transaction) Get(ctx context.Context, i interface{}, keys ...interface{}) (interface{}, error)

Get has the same behavior as DbMap.Get(), but runs in a transaction.

func (*Transaction) Insert

func (t *Transaction) Insert(ctx context.Context, list ...interface{}) error

Insert has the same behavior as DbMap.Insert(), but runs in a transaction.

func (*Transaction) PrepareContext

func (t *Transaction) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)

Prepare has the same behavior as DbMap.Prepare(), but runs in a transaction.

func (*Transaction) QueryContext

func (t *Transaction) QueryContext(ctx context.Context, q string, args ...interface{}) (*sql.Rows, error)

func (*Transaction) QueryRowContext

func (t *Transaction) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row

func (*Transaction) ReleaseSavepoint

func (t *Transaction) ReleaseSavepoint(ctx context.Context, savepoint string) error

ReleaseSavepint releases the savepoint with the given name. The name is interpolated directly into the SQL SAVEPOINT statement, so you must sanitize it if it is derived from user input.

func (*Transaction) Rollback

func (t *Transaction) Rollback() error

Rollback rolls back the underlying database transaction.

func (*Transaction) RollbackToSavepoint

func (t *Transaction) RollbackToSavepoint(ctx context.Context, savepoint string) error

RollbackToSavepoint rolls back to the savepoint with the given name. The name is interpolated directly into the SQL SAVEPOINT statement, so you must sanitize it if it is derived from user input.

func (*Transaction) Savepoint

func (t *Transaction) Savepoint(ctx context.Context, name string) error

Savepoint creates a savepoint with the given name. The name is interpolated directly into the SQL SAVEPOINT statement, so you must sanitize it if it is derived from user input.

func (*Transaction) Select

func (t *Transaction) Select(ctx context.Context, i interface{}, query string, args ...interface{}) ([]interface{}, error)

Select has the same behavior as DbMap.Select(), but runs in a transaction.

func (*Transaction) SelectFloat

func (t *Transaction) SelectFloat(ctx context.Context, query string, args ...interface{}) (float64, error)

SelectFloat is a convenience wrapper around the borp.SelectFloat function.

func (*Transaction) SelectInt

func (t *Transaction) SelectInt(ctx context.Context, query string, args ...interface{}) (int64, error)

SelectInt is a convenience wrapper around the borp.SelectInt function.

func (*Transaction) SelectNullFloat

func (t *Transaction) SelectNullFloat(ctx context.Context, query string, args ...interface{}) (sql.NullFloat64, error)

SelectNullFloat is a convenience wrapper around the borp.SelectNullFloat function.

func (*Transaction) SelectNullInt

func (t *Transaction) SelectNullInt(ctx context.Context, query string, args ...interface{}) (sql.NullInt64, error)

SelectNullInt is a convenience wrapper around the borp.SelectNullInt function.

func (*Transaction) SelectNullStr

func (t *Transaction) SelectNullStr(ctx context.Context, query string, args ...interface{}) (sql.NullString, error)

SelectNullStr is a convenience wrapper around the borp.SelectNullStr function.

func (*Transaction) SelectOne

func (t *Transaction) SelectOne(ctx context.Context, holder interface{}, query string, args ...interface{}) error

SelectOne is a convenience wrapper around the borp.SelectOne function.

func (*Transaction) SelectStr

func (t *Transaction) SelectStr(ctx context.Context, query string, args ...interface{}) (string, error)

SelectStr is a convenience wrapper around the borp.SelectStr function.

func (*Transaction) Update

func (t *Transaction) Update(ctx context.Context, list ...interface{}) (int64, error)

Update had the same behavior as DbMap.Update(), but runs in a transaction.

func (*Transaction) UpdateColumns

func (t *Transaction) UpdateColumns(ctx context.Context, filter ColumnFilter, list ...interface{}) (int64, error)

UpdateColumns had the same behavior as DbMap.UpdateColumns(), but runs in a transaction.

type TypeConverter

type TypeConverter interface {
	// ToDb converts val to another type. Called before INSERT/UPDATE operations
	ToDb(val interface{}) (interface{}, error)

	// FromDb returns a CustomScanner appropriate for this type. This will be used
	// to hold values returned from SELECT queries.
	//
	// In particular the CustomScanner returned should implement a Binder
	// function appropriate for the Go type you wish to convert the db value to
	//
	// If bool==false, then no custom scanner will be used for this field.
	FromDb(target interface{}) (CustomScanner, bool)
}

The TypeConverter interface provides a way to map a value of one type to another type when persisting to, or loading from, a database.

Example use cases: Implement type converter to convert bool types to "y"/"n" strings, or serialize a struct member as a JSON blob.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL