postgres

package module
v0.0.0-...-893ea48 Latest Latest
Warning

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

Go to latest
Published: Sep 30, 2019 License: MIT Imports: 21 Imported by: 0

README

Postgres GoDoc

Postgres wraps github.com/lib/pq and implements a set of new features on top of it:

Postgres >= 11 required.

Status: under active development, exposed func signatures mostly stable

Usage

import pg "github.com/mattes/postgres"

type User struct {
  Id    string `db:"pk"`
  Name  string
  Email string
}

func init() {
  // Register struct &User{} with alias 'user.v1',
  // and have new ids prefixed with 'user'.
  pg.RegisterWithPrefix(&User{}, "user.v1", "user")
}

func main() {
  db, _ := pg.Open(os.Getenv("DB"))
  db.Migrate(context.Background())

  u := &User{
    Id:    pg.NewID(&User{}), // example: user_1R0D8rn6jP870lrtSpgb1y6M5tG
    Name:  "Karl",
    Email: "karl@example.com",
  }
  db.Insert(context.Background(), u) // insert into table user_v1

  // ... for more examples, have a look at the docs.
}
Encoding & Decoding of Go types

This package converts between the following types. A postgres column can be null, if the related Go type can be nil. Otherwise Go's zero value is used as the postgres default value. Complex Go types are stored as JSON.

Go type Postgres column type
implements ColumnTyper "returned value"
implements sql.Scanner text null
time.Time timestamp (6) without time zone
time.Duration bigint
[]string text[] null
string text not null default ''
bool boolean not null default false
int integer not null default 0
struct{} jsonb null
[]T jsonb null
map[T]T jsonb null
Migrations for Go structs

Postgres tables should follow Go structs. This package is able to automatically run migrations to create new tables with primary keys, indexes and foreign keys.

Only backwards compatible, non-destructive migrations are applied to ensure that two or more Go processes with different Go struct schemas can run at the same time.

Use-case automatically migrated?
Create a new table for struct Yes
Add a new column for a new field in a struct Yes
Change field's name No
Change field's type No
Remove a field No
Add a new field to primary key No
Remove a field from primary key No
Add a new index Yes
Remove an index No
Add a new field to index No
Remove a field from index No
Add a new unique index Yes, if existing data doesn't violate unique constraint.
Remove an unique index No
Add a new field to unique index No
Remove a field from unique index No
Add a new foreign key Yes, if existing data doesn't violate unique/ foreign key constraint.
Remove a foreign key No

Changes that are not backwards compatible usually require all deprecated Go processes to stop first. To enable zero-downtime deploys, it's recommended to either create a new table or field and write and read from the old and new table or field simultaneously until the deprecated versions are stopped and removed.

Struct tags

This package will pick up db struct tags to build queries and create migrations. The following struct tags are supported:

Primary Keys
// Column becomes primary key
Col string `db:"pk"` 

// Col1 and Col2 become composite primary key
Col1 string `db:"pk(name=mypk, method=hash, order=desc, composite=[Col2]"` 
Col2 string
Foreign Keys
// Column references A.Col
Col string `db:"references(struct=A, field=Col)"`

// Column references A.Col1 and A.Col2
Col string `db:"references(struct=A, fields=[Col1, Col2])"`
Indexes
// Column has index
Col string `db:"index"`

// Column has composite index
Col1 string `db:"index(name=myindex, method=hash, order=desc, composite=[Col2]"`
Col2 string

// Column has unique index
Col string `db:"unique"`

// Column has unique composite index
Col1 string `db:"unique(name=myindex, method=hash, order=desc, composite=[Col2]"`
Col2 string
Table Partitions

Partitions table by range, see docs.

CreatedAt time.Time `db:"pk,partitionByRange"`

Testing

Set env variable GOTEST_POSTGRES_URI (see helper_test.go for example) and run make test.

Documentation

Overview

Package postgres wraps https://github.com/lib/pq and implements a set of new features on top of it:

  • Get, Filter, Insert, Update, Save and Delete convenience functions
  • Advanced encoding & decoding between Go and Postgres column types
  • Create tables, indexes and foreign keys for Go structs

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ConnectTimeout is the max time to wait for Postgres to be reachable.
	ConnectTimeout = 5 * time.Second

	// MigrateKey is a random key for Postgres' advisory lock.
	// It must be the same for all running Migrate funcs.
	MigrateKey = 8267205493056421913
)
View Source
var (
	ErrNoLock      = fmt.Errorf("no lock")
	ErrNotUnlocked = fmt.Errorf("not unlocked")
)
View Source
var (
	// QueryLimit sets the default LIMIT clause
	QueryLimit = 10

	// MaxQueryLength is the max size of a query string
	MaxQueryLength = 1000
)
View Source
var (
	ErrScannerNotImplemented     = fmt.Errorf("type does not implement sql.Scanner")
	ErrColumnTyperNotImplemented = fmt.Errorf("type does not implement ColumnTyper")
)
View Source
var ErrInvalidId = fmt.Errorf("invalid ID")
View Source
var MaxIdentifierLength = 63
View Source
var StructTag = "db"

StructTag is the default struct tag

Functions

func MustQuoteIdentifier

func MustQuoteIdentifier(in string) string

func NewID

func NewID(s Struct) string

NewID returns a new ramdon ID, prefixed with the registered name of the given struct. Example: `user_1R0D8rn6jP870lrtSpgb1y6M5tG`

func NewPrefixID

func NewPrefixID(prefix string) string

NewPrefixID returns a new random ID, prefixed with the given prefix. Example: `user_1R0D8rn6jP870lrtSpgb1y6M5tG`

func ParseID

func ParseID(id string) (prefix string, kid ksuid.KSUID, err error)

ParseID parses a ID like `user_1R0D8rn6jP870lrtSpgb1y6M5tG`.

func QuoteIdentifier

func QuoteIdentifier(in string) (string, error)

func QuoteLiteral

func QuoteLiteral(in string) string

func Register

func Register(s Struct, alias string)

Register registers a struct. Optional alias has to be globally unique.

func RegisterWithPrefix

func RegisterWithPrefix(s Struct, alias string, prefixID string)

RegisterWithPrefix registers a struct. Optional alias has to be globally unique. Optional prefixID is used in NewID().

Types

type ColumnTyper

type ColumnTyper interface {

	// ColumnType returns postgres' column type
	ColumnType() string
}

ColumnTyper is an interface to be implemented by a custom type

type DefaultLogger

type DefaultLogger struct{}

func NewDefaultLogger

func NewDefaultLogger() *DefaultLogger

func (*DefaultLogger) Query

func (l *DefaultLogger) Query(query string, duration time.Duration, args ...interface{})

type Logger

type Logger interface {

	// Query will be called with the SQL query and the arguments.
	Query(query string, duration time.Duration, args ...interface{})
}

Logger is a logging interface and can be used to implement a custom logger.

type Postgres

type Postgres struct {
	Logger Logger
	// contains filtered or unexported fields
}

func Open

func Open(uri string) (*Postgres, error)

Open creates a new Postgres client.

To set a schema, specify `search_path` in URI.

To only create temporary tables, i.e. for testing purposes, specify `createTempTables=true` in URI.

func (*Postgres) Close

func (p *Postgres) Close() error

Close closes the database and prevents new queries from starting. Close then waits for all queries that have started processing on the server to finish.

It is rare to Close a DB, as the DB handle is meant to be long-lived and shared between many goroutines.

func (*Postgres) DB

func (p *Postgres) DB() *sql.DB

DB returns the underlying *sql.DB database.

func (*Postgres) Delete

func (p *Postgres) Delete(ctx context.Context, s Struct) error

Delete deletes a record by looking at the primary keys of a struct.

Example
db, _ := Open(postgresURI)
db.Migrate(context.Background())
db.Logger = print()

// Create a new user first
user := &User{
	Id:    "user_4", // primary key, via `db:"pk"` struct tag
	Name:  "Peter",
	Email: "peter@foobar.com",
}
_ = db.Save(context.Background(), user)

// Delete user by Id
user = &User{
	Id: "user_4",
}
_ = db.Delete(context.Background(), user)
fmt.Printf("%+v", user)
Output:

INSERT INTO "user" ("id", "name", "email") VALUES ($1, $2, $3) ON CONFLICT ("id") DO UPDATE SET ("name", "email") = ROW("excluded"."name", "excluded"."email") RETURNING "id", "name", "email"
DELETE FROM "user" WHERE "id" = $1 RETURNING "id", "name", "email"
&{Id:user_4 Name:Peter Email:peter@foobar.com}

func (*Postgres) Exec

func (p *Postgres) Exec(ctx context.Context, query string, args ...interface{}) (sql.Result, error)

Exec executes a query that doesn't return rows. For example: an INSERT and UPDATE.

func (*Postgres) Filter

func (p *Postgres) Filter(ctx context.Context, s StructSlice, q *QueryStmt) error

Filter finds records based on QueryStmt. See QueryStmt for more details.

Example
db, _ := Open(postgresURI)
db.Migrate(context.Background())
db.Logger = print()

// Create a new user first
user := &User{
	Id:    "user_5",
	Name:  "Max",
	Email: "max@example.com",
}
_ = db.Save(context.Background(), user)

// Filter users by email
users := []User{}
_ = db.Filter(context.Background(), &users, Query("Email LIKE $1", "%example.com"))
fmt.Printf("%+v", users)
Output:

INSERT INTO "user" ("id", "name", "email") VALUES ($1, $2, $3) ON CONFLICT ("id") DO UPDATE SET ("name", "email") = ROW("excluded"."name", "excluded"."email") RETURNING "id", "name", "email"
SELECT "id", "name", "email" FROM "user" WHERE "email" LIKE $1 LIMIT 10
[{Id:user_5 Name:Max Email:max@example.com}]
Example (UntrustedQuery)
db, _ := Open(postgresURI)
db.Migrate(context.Background())
db.Logger = print()

// Create a new user first
user := &User{
	Id:    "user_6",
	Name:  "Karl",
	Email: "karl@company.co",
}
_ = db.Save(context.Background(), user)

// Simulate an incoming HTTP request, with an URL like:
//   ?filter=Name = $1 and Email = $2
//   &vars=Karl
//   &vars=karl@company.co
request := http.Request{
	URL: &url.URL{
		RawQuery: "filter=Name%20%3D%20%241%20and%20Email%20%3D%20%242&vars=Karl&vars=karl%40company.co",
	},
}

// Get ?filter from request URL
urlFilter := request.URL.Query().Get("filter")

// Get &vars from request URL and convert from []string to []interface{}
urlVars := stringSliceToInterfaceSlice(request.URL.Query()["vars"])

// Assemble new UntrustedQuery from URL input
query := UntrustedQuery(urlFilter, urlVars...).Whitelist("Name", "Email")

users := []User{}
_ = db.Filter(context.Background(), &users, query)
fmt.Printf("%+v", users)
Output:

INSERT INTO "user" ("id", "name", "email") VALUES ($1, $2, $3) ON CONFLICT ("id") DO UPDATE SET ("name", "email") = ROW("excluded"."name", "excluded"."email") RETURNING "id", "name", "email"
SELECT "id", "name", "email" FROM "user" WHERE "name" = $1 and "email" = $2 LIMIT 10
[{Id:user_6 Name:Karl Email:karl@company.co}]

func (*Postgres) Get

func (p *Postgres) Get(ctx context.Context, s Struct) error

Get finds a record by its primary keys.

Example
db, _ := Open(postgresURI)
db.Migrate(context.Background())
db.Logger = print()

// Create a new user first
user := &User{
	Id:    "user_1", // primary key, via `db:"pk"` struct tag
	Name:  "Peter",
	Email: "peter@foobar.com",
}
_ = db.Save(context.Background(), user)

// Get user by Id
user = &User{
	Id: "user_1",
}
_ = db.Get(context.Background(), user)
fmt.Printf("%+v", user)
Output:

INSERT INTO "user" ("id", "name", "email") VALUES ($1, $2, $3) ON CONFLICT ("id") DO UPDATE SET ("name", "email") = ROW("excluded"."name", "excluded"."email") RETURNING "id", "name", "email"
SELECT "id", "name", "email" FROM "user" WHERE "id" = $1 LIMIT 1
&{Id:user_1 Name:Peter Email:peter@foobar.com}

func (*Postgres) Insert

func (p *Postgres) Insert(ctx context.Context, s Struct, fieldMask ...StructFieldName) error

Insert creates a new record.

Example
db, _ := Open(postgresURI)
db.Migrate(context.Background())
db.Logger = print()

// Create a new user
user := &User{
	Id:    "user_6", // primary key, via `db:"pk"` struct tag
	Name:  "Peter",
	Email: "peter@foobar.com",
}
_ = db.Insert(context.Background(), user)
fmt.Printf("%+v", user)
Output:

INSERT INTO "user" ("id", "name", "email") VALUES ($1, $2, $3) RETURNING "id", "name", "email"
&{Id:user_6 Name:Peter Email:peter@foobar.com}
Example (FieldMask)
db, _ := Open(postgresURI)
db.Migrate(context.Background())
db.Logger = print()

// Create a new user
user := &User{
	Id:    "user_9", // primary key, via `db:"pk"` struct tag
	Email: "peter@foobar.com",
}
_ = db.Insert(context.Background(), user, "Id", "Email")
fmt.Printf("%+v", user)
Output:

INSERT INTO "user" ("id", "email") VALUES ($1, $2) RETURNING "id", "name", "email"
&{Id:user_9 Name: Email:peter@foobar.com}

func (*Postgres) Migrate

func (p *Postgres) Migrate(ctx context.Context) error

Migrate runs SQL migrations for structs registered with `Register`. Migrations are non-destructive and only backwards-compatible changes will be performed, in particular:

  • New tables for structs are created (this includes creation of primary key)
  • New columns for struct fields are created
  • New indexes are created
  • New unique indexes are created (if possible)
  • New foreign keys are created (if possible)

Migrate blocks until it successfully acquired a global lock using Postgres' advisory locks. This guarantees that only one Migrate function can run at a time across different processes.

The performed migrations as mentioned above are idempotent.

Example
db, _ := Open(postgresURI)

// Register "example" struct with prefix "ex"
RegisterWithPrefix(&Example{}, "example", "ex")

// Run migrations ...
_ = db.Migrate(context.Background())
Output:

func (*Postgres) NewID

func (p *Postgres) NewID(s Struct) string

NewID is a convenience function calling NewID. It exists so that the package doesn't have to be imported if just a *Postgres instance is passed around.

func (*Postgres) NewTransaction

func (p *Postgres) NewTransaction() (*Transaction, error)

NewTransaction starts a new transaction.

func (*Postgres) Ping

func (p *Postgres) Ping(ctx context.Context) error

Ping verifies a connection to the database is still alive, establishing a connection if necessary.

func (*Postgres) Query

func (p *Postgres) Query(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)

Query executes a query that returns rows, typically a SELECT.

func (*Postgres) QueryRow

func (p *Postgres) QueryRow(ctx context.Context, query string, args ...interface{}) *sql.Row

QueryRow executes a query that is expected to return at most one row. QueryRow always returns a non-nil value. Errors are deferred until Row's Scan method is called. If the query selects no rows, the *Row's Scan will return ErrNoRows. Otherwise, the *Row's Scan scans the first selected row and discards the rest.

func (*Postgres) Save

func (p *Postgres) Save(ctx context.Context, s Struct, fieldMask ...StructFieldName) error

Save creates a new record or updates an existing record by looking at the primary keys of a struct.

Example
db, _ := Open(postgresURI)
db.Migrate(context.Background())
db.Logger = print()

// Create or update user
user := &User{
	Id:    "user_2", // primary key, via `db:"pk"` struct tag
	Name:  "Peter",
	Email: "peter@foobar.com",
}
_ = db.Save(context.Background(), user)
fmt.Printf("%+v", user)
Output:

INSERT INTO "user" ("id", "name", "email") VALUES ($1, $2, $3) ON CONFLICT ("id") DO UPDATE SET ("name", "email") = ROW("excluded"."name", "excluded"."email") RETURNING "id", "name", "email"
&{Id:user_2 Name:Peter Email:peter@foobar.com}
Example (FieldMask)
db, _ := Open(postgresURI)
db.Migrate(context.Background())
db.Logger = print()

// Create or update user (only save Id and Email)
user := &User{
	Id:    "user_3", // primary key, via `db:"pk"` struct tag
	Email: "peter@foobar.com",
}
_ = db.Save(context.Background(), user, "Id", "Email")
fmt.Printf("%+v", user)
Output:

INSERT INTO "user" ("id", "email") VALUES ($1, $2) ON CONFLICT ("id") DO UPDATE SET ("email") = ROW("excluded"."email") RETURNING "id", "name", "email"
&{Id:user_3 Name: Email:peter@foobar.com}

func (*Postgres) SetConnMaxLifetime

func (p *Postgres) SetConnMaxLifetime(d time.Duration)

SetConnMaxLifetime sets the maximum amount of time a connection may be reused.

Expired connections may be closed lazily before reuse.

If d <= 0, connections are reused forever.

func (*Postgres) SetMaxIdleConns

func (p *Postgres) SetMaxIdleConns(n int)

SetMaxIdleConns sets the maximum number of connections in the idle connection pool.

If MaxOpenConns is greater than 0 but less than the new MaxIdleConns, then the new MaxIdleConns will be reduced to match the MaxOpenConns limit.

If n <= 0, no idle connections are retained.

The default max idle connections is currently 2. This may change in a future release.

func (*Postgres) SetMaxOpenConns

func (p *Postgres) SetMaxOpenConns(n int)

SetMaxOpenConns sets the maximum number of open connections to the database.

If MaxIdleConns is greater than 0 and the new MaxOpenConns is less than MaxIdleConns, then MaxIdleConns will be reduced to match the new MaxOpenConns limit.

If n <= 0, then there is no limit on the number of open connections. The default is 0 (unlimited).

func (*Postgres) Stats

func (p *Postgres) Stats() sql.DBStats

Stats returns database statistics.

func (*Postgres) Transaction

func (p *Postgres) Transaction(fn func(*Transaction) error) error

Transaction starts a new transaction and automatically commits or rolls back the transaction if TransactionFunc returns an error.

Example
// Open connection to Postgres
db, err := Open(postgresURI)
if err != nil {
	return err
}

// Start a new transaction
err = db.Transaction(func(tx *Transaction) error {

	// Execute SQL
	if _, err := tx.Exec(context.Background(), "query"); err != nil {
		return err
	}

	// Execute more SQL
	if _, err := tx.Exec(context.Background(), "another query"); err != nil {
		return err
	}

	return nil
})

// The transaction ended. If no error was returned, the transaction
// was commited, otherwise the transaction was rolled back and the
// original error is returned.
return err
Output:

func (*Postgres) Update

func (p *Postgres) Update(ctx context.Context, s Struct, fieldMask ...StructFieldName) error

Update updates an existing record by looking at the orimary keys of a struct.

Example
db, _ := Open(postgresURI)
db.Migrate(context.Background())
db.Logger = print()

// Create a new user first
user := &User{
	Id:    "user_7",
	Name:  "Peter",
	Email: "peter@foobar.com",
}
_ = db.Insert(context.Background(), user)

// Then update the user
user = &User{
	Id:    "user_7", // primary key, via `db:"pk"` struct tag
	Name:  "Karl",
	Email: "karl@foobar.com",
}
_ = db.Update(context.Background(), user)
fmt.Printf("%+v", user)
Output:

INSERT INTO "user" ("id", "name", "email") VALUES ($1, $2, $3) RETURNING "id", "name", "email"
UPDATE "user" SET ("name", "email") = ROW($1, $2) WHERE "id" = $3 RETURNING "id", "name", "email"
&{Id:user_7 Name:Karl Email:karl@foobar.com}
Example (FieldMask)
db, _ := Open(postgresURI)
db.Migrate(context.Background())
db.Logger = print()

// Create a new user first
user := &User{
	Id:    "user_8",
	Name:  "Peter",
	Email: "peter@foobar.com",
}
_ = db.Insert(context.Background(), user)

// Then update the user
user = &User{
	Id:    "user_8", // primary key, via `db:"pk"` struct tag
	Email: "karl@foobar.com",
}
_ = db.Update(context.Background(), user, "Email")
fmt.Printf("%+v", user)
Output:

INSERT INTO "user" ("id", "name", "email") VALUES ($1, $2, $3) RETURNING "id", "name", "email"
UPDATE "user" SET ("email") = ROW($1) WHERE "id" = $2 RETURNING "id", "name", "email"
&{Id:user_8 Name:Peter Email:karl@foobar.com}

type QueryStmt

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

QueryStmt is a query builder, used by Postgres.Filter

func Query

func Query(query string, args ...interface{}) *QueryStmt

Query builds a query statement that will use prepared statements.

func UntrustedQuery

func UntrustedQuery(untrustedQuery string, args ...interface{}) *QueryStmt

UntrustedQuery builds a query statement that will use prepared statements. The given query is verified to be valid SQL to prevent SQL injections and accepts untrusted user input, i.e. from URL query parameters.

QueryStmt.Whitelist is required to whitelist queryable fields.

The given query must follow a `field operator wildcard` syntax, i.e.

Email like $1 and (Active != $2 or Admin = $3)

The following operators are allowed:

=|!=|<|<=|>|>=|ilike|like

func (*QueryStmt) Asc

func (q *QueryStmt) Asc(field StructFieldName) *QueryStmt

Asc instructs the result to be ordered ascending by field.

func (*QueryStmt) Desc

func (q *QueryStmt) Desc(field StructFieldName) *QueryStmt

Desc instructs the result to be ordered descending by field.

func (*QueryStmt) Limit

func (q *QueryStmt) Limit(n int) *QueryStmt

Limit sets the maximum number of returned query results.

func (*QueryStmt) Whitelist

func (q *QueryStmt) Whitelist(fields ...StructFieldName) *QueryStmt

Whitelist sets acceptable fields that can be queried. Required when using UntrustedQuery.

type Struct

type Struct interface{}

Struct is a Go struct, i.e. &User{}

type StructFieldName

type StructFieldName interface{}

StructFieldName defines a struct's field name where interface{} must be "resolvable" as string.

type StructSlice

type StructSlice interface{}

StructSlice is a Go slice of structs, i.e. []User{}

type Transaction

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

func (*Transaction) Commit

func (t *Transaction) Commit() error

Commit commits the transaction. Commit or Rollback must be called at least once, so the connection can be returned to the connection pool.

Example
// Open connection to Postgres
db, err := Open(postgresURI)
if err != nil {
	return err
}

// Start a new transaction
tx, err := db.NewTransaction()
if err != nil {
	return err
}

// Execute SQL
if _, err := tx.Exec(context.Background(), "query"); err != nil {
	_ = tx.Rollback() // Rollback and end the transaction
	return err        // Return the original error
}

// Execute more SQL
if _, err := tx.Exec(context.Background(), "another query"); err != nil {
	_ = tx.Rollback() // Rollback and end the transaction
	return err        // Return the original error
}

// Commit and end the transaction
if err := tx.Commit(); err != nil {
	return err
}

return nil
Output:

func (*Transaction) Delete

func (t *Transaction) Delete(ctx context.Context, s Struct) error

Delete deletes a record by looking at the primary keys of a struct. See Postgres.Delete for more details.

func (*Transaction) Exec

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

Exec executes a query that doesn't return rows. For example: an INSERT and UPDATE.

func (*Transaction) Filter

func (t *Transaction) Filter(ctx context.Context, s StructSlice, q *QueryStmt) error

Filter finds records based on QueryStmt. See Postgres.Filter for more details.

func (*Transaction) Get

func (t *Transaction) Get(ctx context.Context, s Struct) error

Get finds a record by its primary keys. See Postgres.Get for more details.

func (*Transaction) Insert

func (t *Transaction) Insert(ctx context.Context, s Struct, fieldMask ...StructFieldName) error

Insert creates a new record. See Postgres.Insert for more details.

func (*Transaction) Query

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

Query executes a query that returns rows, typically a SELECT.

func (*Transaction) QueryRow

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

QueryRow executes a query that is expected to return at most one row. QueryRow always returns a non-nil value. Errors are deferred until Row's Scan method is called. If the query selects no rows, the *Row's Scan will return ErrNoRows. Otherwise, the *Row's Scan scans the first selected row and discards the rest.

func (*Transaction) Rollback

func (t *Transaction) Rollback() error

Rollback aborts the transaction. Rollback or Commit must be called at least once, so the connection can be returned to the connection pool. See Commit for an example.

func (*Transaction) Save

func (t *Transaction) Save(ctx context.Context, s Struct, fieldMask ...StructFieldName) error

Save creates a new record or updates an existing record by looking at the primary keys of a struct. See Postgres.Filter for more details.

func (*Transaction) Update

func (t *Transaction) Update(ctx context.Context, s Struct, fieldMask ...StructFieldName) error

Update updates an existing record by looking at the orimary keys of a struct. See Postgres.Update for more details.

Jump to

Keyboard shortcuts

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