db

package
Version: v0.0.0-...-91dbb5c Latest Latest
Warning

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

Go to latest
Published: Apr 12, 2021 License: MIT Imports: 12 Imported by: 0

Documentation

Index

Constants

View Source
const (
	LevelSerializable    = "serializable"
	LevelRepeatableRead  = "repeatable read"
	LevelReadCommitted   = "read committed"
	LevelReadUncommitted = "read uncommitted"
)

Variables

View Source
var (
	ErrInvalidTarget       = errors.New("target must be pointer of a struct or pointer of a slice of structs")
	ErrNoConnection        = errors.New("no connection")
	ErrTypeAssertionFailed = errors.New("type assertion failed")
)
View Source
var (
	// Function to convert a name to table or column name used in database,
	// by default uses DefaultColumnizer which converts "CamelCase" to "snake_case".
	Columnizer func(string) string = DefaultColumnizer

	// Function to convert table name to its plural form.
	// By default, table name uses plural form.
	Pluralizer func(string) string = DefaultPluralizer
)
View Source
var (
	ErrMustBePointer = errors.New("must be pointer")
)

Functions

func DefaultColumnizer

func DefaultColumnizer(in string) string

Default function to convert "CamelCase" struct name to "snake_case" column name used in database. For example, "FullName" will be converted to "full_name".

func DefaultPluralizer

func DefaultPluralizer(in string) string

Default function to convert a word to its plural form. Add "es" for "s" or "o" ending, "y" ending will be replaced with "ies", for other endings, add "s". For example, "product" will be converted to "products".

func ToColumnName

func ToColumnName(in string) string

Function to convert struct name to name used in database, using the Columnizer function.

func ToTableName

func ToTableName(object interface{}) (name string)

Get table name from a struct. If a struct has "TableName() string" function, then the return value of the function will be used. If a struct has a field named "__TABLE_NAME__", then value of the field tag will be used. Otherwise, the name of the struct will be used. If name is empty, "error_no_table_name" is returned. Examples:

- type Product struct{}; func (_ Product) TableName() string { return "foobar" }; ToTableName(Product{}) == "foobar"
- ToTableName(struct { __TABLE_NAME__ string `users` }{}) == "users"
- type Product struct{}; ToTableName(Product{}) == "products"
- ToTableName(struct{}{}) == "error_no_table_name"

Types

type Changes

type Changes map[Field]interface{}

func (Changes) MarshalJSON

func (c Changes) MarshalJSON() ([]byte, error)

func (Changes) String

func (c Changes) String() string

type ConvertParameters

type ConvertParameters interface {
	ConvertParameters(string, []interface{}) (string, []interface{})
}

type DB

type DB interface {
	Close() error
	Exec(query string, args ...interface{}) (Result, error)
	Query(query string, args ...interface{}) (Rows, error)
	QueryRow(query string, args ...interface{}) Row
	BeginTx(ctx context.Context, isolationLevel string) (Tx, error)
	ErrNoRows() error
	ErrGetCode(err error) string
}

type Field

type Field struct {
	Name       string // struct field name
	ColumnName string // column name (or jsonb key name) in database
	JsonName   string // key name in json input and output
	Jsonb      string // jsonb column name in database
	DataType   string // data type in database
	Exported   bool   // false if field name is lower case (unexported)
}

type Model

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

Model is a database table and it is created from struct. Table name is inferred from the name of thea struct, the tag of __TABLE_NAME__ field or its TableName() receiver. Column names are inferred from struct field names or theirs "column" tags. Both table names and field names are in snake_case by default.

func NewModel

func NewModel(object interface{}, options ...interface{}) (m *Model)

Initialize a Model from a struct. For available options, see SetOptions().

func NewModelSlim

func NewModelSlim(object interface{}, options ...interface{}) (m *Model)

Initialize a Model from a struct without parsing fields of the struct. Useful if you are calling functions that don't need fields, for example:

db.NewModelSlim(models.User{}, conn).MustCount()

For available options, see SetOptions().

func NewModelTable

func NewModelTable(tableName string, options ...interface{}) (m *Model)

Initialize a Model by defining table name only. Useful if you are calling functions that don't need fields, for example:

db.NewModelTable("users", conn).MustCount()

For available options, see SetOptions().

func (Model) Assign

func (m Model) Assign(target interface{}, lotsOfChanges ...Changes) (out []Changes, err error)

Assign changes to target object. Useful if you want to validate your struct.

func create(c echo.Context) error {
	var user models.User
	m := db.NewModel(user, conn)
	changes := m.MustAssign(
		&user,
		m.Permit("Name").Filter(c.Request().Body),
	)
	if err := c.Validate(user); err != nil {
		panic(err)
	}
	var id int
	m.Insert(changes...)("RETURNING id").MustQueryRow(&id)
	// ...
}

func (Model) Changes

func (m Model) Changes(in RawChanges) (out Changes)

Convert RawChanges to Changes.

m := db.NewModel(struct {
	Age *int `json:"age"`
}{})
m.Changes(map[string]interface{}{
	"age": 99,
})

func (Model) Count

func (m Model) Count(values ...interface{}) (count int, err error)

Create and execute a SELECT COUNT(*) statement, return number of rows. You can provide conditions (like WHERE, ORDER BY, LIMIT) to the statement as the first argument. The rest arguments are for any placeholder parameters in the statement.

func (Model) CreatedAt

func (m Model) CreatedAt() Changes

Helper to add CreatedAt of current time changes.

func (Model) Delete

func (m Model) Delete(values ...interface{}) SQLWithValues

Delete builds a DELETE statement. You can add extra clause (like WHERE, RETURNING) to the statement as the first argument. The rest arguments are for any placeholder parameters in the statement.

var ids []int
db.NewModelTable("reports", conn).Delete("RETURNING id").MustQuery(&ids)

func (Model) DropSchema

func (m Model) DropSchema() string

Generate DROP TABLE ("DROP TABLE IF EXISTS <table_name>;") SQL statement from a Model.

func (Model) Exists

func (m Model) Exists(values ...interface{}) (exists bool, err error)

Create and execute a SELECT 1 AS one statement. Returns true if record exists, false if not exists.

func (Model) FieldByName

func (m Model) FieldByName(name string) *Field

Get field by struct field name, nil will be returned if no such field.

func (Model) Find

func (m Model) Find(values ...interface{}) SQLWithValues

Create a SELECT query statement with all fields of a Model. You can provide conditions (like WHERE, ORDER BY, LIMIT) to the statement as the first argument. The rest arguments are for any placeholder parameters in the statement. If you want to use other data type than the type of struct passed in NewModel(), see Select().

// put results into a slice
var users []models.User
db.NewModel(models.User{}, conn).Find().MustQuery(&users)

// put results into a struct
var user models.User
db.NewModel(models.User{}, conn).Find("WHERE id = $1", 1).MustQuery(&user)

func (Model) Insert

func (m Model) Insert(lotsOfChanges ...Changes) func(...string) SQLWithValues

Insert builds an INSERT INTO statement with fields and values in the changes, returns a function with optional string argument which you can add extra clause (like ON CONFLICT or RETURNING) to the statement.

var id int
m.Insert(changes...)("RETURNING id").MustQueryRow(&id)

func (Model) MustAssign

func (m Model) MustAssign(i interface{}, lotsOfChanges ...Changes) []Changes

MustAssign is like Assign but panics if assign operation fails.

func (Model) MustCount

func (m Model) MustCount(values ...interface{}) int

MustCount is like Count but panics if count operation fails.

func (Model) MustExists

func (m Model) MustExists(values ...interface{}) bool

MustExists is like Exists but panics if existence check operation fails. Returns true if record exists, false if not exists.

func (Model) NewSQLWithValues

func (m Model) NewSQLWithValues(sql string, values ...interface{}) SQLWithValues

Create new SQLWithValues with SQL statement as first argument, The rest arguments are for any placeholder parameters in the statement.

func (Model) Permit

func (m Model) Permit(fieldNames ...string) *ModelWithPermittedFields

Permits list of field names of a Model to limit Filter() which fields should be allowed for mass updating. If no field names are provided ("Permit()"), no fields are permitted.

func (Model) PermitAllExcept

func (m Model) PermitAllExcept(fieldNames ...string) *ModelWithPermittedFields

Permits all available fields except provided of a Model to limit Filter() which fields should be allowed for mass updating. If no field names are provided ("PermitAllExcept()"), all available fields are permitted.

func (Model) Schema

func (m Model) Schema() string

Generate CREATE TABLE SQL statement from a Model.

| Go Type                                        | PostgreSQL Data Type |
|------------------------------------------------|----------------------|
| int8 / int16 / int32 / uint8 / uint16 / uint32 | integer              |
| int64 / uint64 / int / uint                    | bigint               |
| time.Time                                      | timestamptz          |
| float32 / float64 / decimal.Decimal            | numeric              |
| bool                                           | boolean              |
| other                                          | text                 |

You can use "dataType" tag to customize the data type. "NOT NULL" is added if the struct field is not a pointer. You can also set SQL statements before or after this statement by defining "BeforeCreateSchema() string" (for example the CREATE EXTENSION statement) or "AfterCreateSchema() string" (for example the CREATE INDEX statement) function for the struct.

db.NewModel(struct {
	__TABLE_NAME__ string `users`

	Id        int
	Name      string
	Age       *int
	CreatedAt time.Time
	DeletedAt *time.Time `dataType:"timestamptz"`
	FullName  string     `jsonb:"meta"`
	NickName  string     `jsonb:"meta"`
}{}).Schema()
// CREATE TABLE users (
//         id SERIAL PRIMARY KEY,
//         name text DEFAULT ”::text NOT NULL,
//         age bigint DEFAULT 0,
//         created_at timestamptz DEFAULT NOW() NOT NULL,
//         deleted_at timestamptz,
//         meta jsonb DEFAULT '{}'::jsonb NOT NULL
// );

func (Model) Select

func (m Model) Select(fields string, values ...interface{}) SQLWithValues

Select is like Find but you can choose what columns to retrieve.

// put results into a slice
var names []string
db.NewModelTable("users", conn).Select("name", "ORDER BY id ASC").MustQuery(&names)

// put results into a map
var id2name map[int]string
db.NewModelTable("users", conn).Select("id, name", "ORDER BY id ASC").MustQuery(&id2name)

// put results into a slice of custom struct
var users []struct {
	name string
	id   int
}
db.NewModelTable("users", conn).Select("name, id", "ORDER BY id ASC").MustQuery(&users)

func (*Model) SetConnection

func (m *Model) SetConnection(db DB) *Model

Set a database connection for the Model. ErrNoConnection is returned if no connection is set.

func (*Model) SetLogger

func (m *Model) SetLogger(logger logger.Logger) *Model

Set the logger for the Model. Use logger.StandardLogger if you want to use Go's built-in standard logging package. By default, no logger is used, so the SQL statements are not printed to the console.

func (*Model) SetOptions

func (m *Model) SetOptions(options ...interface{}) *Model

SetOptions sets database connection (see SetConnection()) and/or logger (see SetLogger()).

func (Model) String

func (m Model) String() string

func (Model) TableName

func (m Model) TableName() string

Table name of the Model (see ToTableName()).

func (Model) Update

func (m Model) Update(lotsOfChanges ...Changes) func(...interface{}) SQLWithValues

Update builds an UPDATE statement with fields and values in the changes, returns a function with optional conditions (like WHERE) to the statement as the first argument. The rest arguments are for any placeholder parameters in the statement.

var rowsAffected int
m.Update(changes...)("WHERE user_id = $1", 1).MustExecute(&rowsAffected)

func (Model) UpdatedAt

func (m Model) UpdatedAt() Changes

Helper to add UpdatedAt of current time changes.

type ModelWithPermittedFields

type ModelWithPermittedFields struct {
	*Model
	// contains filtered or unexported fields
}

func (ModelWithPermittedFields) Bind

func (m ModelWithPermittedFields) Bind(ctx interface{ Bind(interface{}) error }, target interface{}) (Changes, error)

Bind data of permitted fields to target structure using echo.Context#Bind function. The "target" must be a pointer to struct.

// request with ?name=x&age=10
func list(c echo.Context) error {
	obj := struct {
		Name string `query:"name"`
		Age  int    `query:"age"`
	}{}
	m := db.NewModel(obj)
	fmt.Println(m.Permit("Name").Bind(c, &obj))
	fmt.Println(obj) // "Name" is "x" and "Age" is 0 (default), because only "Name" is permitted to change
	// ...
}

func (ModelWithPermittedFields) Filter

func (m ModelWithPermittedFields) Filter(inputs ...interface{}) (out Changes)

Filter keeps data of permitted fields set by Permit() from multiple inputs. Inputs can be RawChanges (map[string]interface{}) or JSON-encoded data (string, []byte or io.Reader), their keys must be fields' JSON names. Input can also be a struct. The "Changes" outputs can be arguments for Insert() or Update().

m := db.NewModel(struct {
	Age *int `json:"age"`
}{})
m.Permit("Age").Filter(
	db.RawChanges{
		"age": 10,
	},
	map[string]interface{}{
		"age": 20,
	},
	`{"age": 30}`,
	[]byte(`{"age": 40}`),
	strings.NewReader(`{"age": 50}`),
	struct{ Age int }{60},
) // Age is 60

func (ModelWithPermittedFields) MustBind

func (m ModelWithPermittedFields) MustBind(ctx interface{ Bind(interface{}) error }, target interface{}) Changes

MustBind is like Bind but panics if bind operation fails.

func (ModelWithPermittedFields) PermittedFields

func (m ModelWithPermittedFields) PermittedFields() (out []string)

Returns list of permitted field names.

type ModelWithTableName

type ModelWithTableName interface {
	TableName() string
}

type RawChanges

type RawChanges map[string]interface{}

type Result

type Result interface {
	RowsAffected() (int64, error)
}

type Row

type Row interface {
	Scan(dest ...interface{}) error
}

type Rows

type Rows interface {
	Close() error
	Err() error
	Next() bool
	Scan(dest ...interface{}) error
}

type SQLWithValues

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

SQLWithValues can be created with Model.NewSQLWithValues(sql, values...)

func (SQLWithValues) ExecTx

func (s SQLWithValues) ExecTx(tx Tx, ctx context.Context, dest ...interface{}) (err error)

ExecTx executes a query in a transaction without returning any rows. You can get number of rows affected by providing pointer of int or int64 to the optional dest.

func (SQLWithValues) Execute

func (s SQLWithValues) Execute(dest ...interface{}) error

Execute executes a query without returning any rows by an UPDATE, INSERT, or DELETE. You can get number of rows affected by providing pointer of int or int64 to the optional dest. For use cases, see Update().

func (SQLWithValues) ExecuteInTransaction

func (s SQLWithValues) ExecuteInTransaction(txOpts *TxOptions, dest ...interface{}) error

ExecuteInTransaction is like Execute but executes the statement in a transaction, you can define IsolationLevel and statements Before and/or After it.

func (SQLWithValues) MustExecute

func (s SQLWithValues) MustExecute(dest ...interface{})

MustExecute is like Execute but panics if execute operation fails.

func (SQLWithValues) MustExecuteInTransaction

func (s SQLWithValues) MustExecuteInTransaction(txOpts *TxOptions, dest ...interface{})

MustExecuteInTransaction is like ExecuteInTransaction but panics if execute operation fails.

func (SQLWithValues) MustQuery

func (s SQLWithValues) MustQuery(target interface{})

MustQuery is like Query but panics if query operation fails.

func (SQLWithValues) MustQueryRow

func (s SQLWithValues) MustQueryRow(dest ...interface{})

MustQueryRow is like QueryRow but panics if query row operation fails.

func (SQLWithValues) MustQueryRowInTransaction

func (s SQLWithValues) MustQueryRowInTransaction(txOpts *TxOptions, dest ...interface{})

MustQueryRowInTransaction is like QueryRowInTransaction but panics if query row operation fails.

func (SQLWithValues) Query

func (s SQLWithValues) Query(target interface{}) error

Query executes the SQL query and put the results into the target. If target is pointer of a struct, at most one row of the query is returned. If target is a pointer of a slice, all rows of the query are returned. If target is a pointer of a map, first column in the SELECT list will be the key of the map, and the second column is the value of the map. For use cases, see Find() and Select().

func (SQLWithValues) QueryRow

func (s SQLWithValues) QueryRow(dest ...interface{}) error

QueryRow gets results from the first row, and put values of each column to corresponding dest. For use cases, see Insert().

var u struct {
	name string
	id   int
}
db.NewModelTable("users", conn).Select("name, id").MustQueryRow(&u.name, &u.id)

func (SQLWithValues) QueryRowInTransaction

func (s SQLWithValues) QueryRowInTransaction(txOpts *TxOptions, dest ...interface{}) error

QueryRowInTransaction is like QueryRow but executes the statement in a transaction, you can define IsolationLevel and statements Before and/or After it.

func (SQLWithValues) QueryTx

func (s SQLWithValues) QueryTx(tx Tx, ctx context.Context, dest ...interface{}) (rows Rows, err error)

Query executes the SQL query and returns rows.

func (SQLWithValues) String

func (s SQLWithValues) String() string

type Scannable

type Scannable interface {
	Scan(dest ...interface{}) error
}

type Tx

type Tx interface {
	ExecContext(ctx context.Context, query string, args ...interface{}) (Result, error)
	QueryContext(ctx context.Context, query string, args ...interface{}) (Rows, error)
	QueryRowContext(ctx context.Context, query string, args ...interface{}) Row
	Commit(ctx context.Context) error
	Rollback(ctx context.Context) error
}

type TxOptions

type TxOptions struct {
	IsolationLevel string
	Before, After  func(context.Context, Tx) error
}

TxOptions can be used in QueryRowInTransaction or ExecuteInTransaction

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
t or T : Toggle theme light dark auto
y or Y : Canonical URL