Documentation ¶
Overview ¶
Example (TagOverride) ¶
You can override how values are serialized via struct tags. The following tags are available:
string (encoding.TextMarshaler, encoding.TextUnmarshaler), binary (encoding.BinaryMarshaler, encoding.BinaryUnmarshaler, gogoproto.Marshaler, gogoproto.Unmarshaler), json (json.Marshaler, json.Unmarshaler).
package main import ( "context" "fmt" "net" "reflect" "github.com/base-go/baseql/internal/testfixtures" "github.com/base-go/baseql/sqlgen" uuid "github.com/satori/go.uuid" ) type UUID uuid.UUID func (u UUID) MarshalBinary() ([]byte, error) { return u[:], nil } func (u *UUID) UnmarshalBinary(b []byte) error { copy(u[:], b) return nil } // You can override how values are serialized via struct tags. The following tags are available: // // string (encoding.TextMarshaler, encoding.TextUnmarshaler), binary (encoding.BinaryMarshaler, // encoding.BinaryUnmarshaler, gogoproto.Marshaler, gogoproto.Unmarshaler), json (json.Marshaler, // json.Unmarshaler). func main() { testDb, _ := testfixtures.NewTestDatabase() defer testDb.Close() _, err := testDb.Exec(` CREATE TABLE users ( uuid BINARY(16) PRIMARY KEY, name VARCHAR(255), ip VARCHAR(255), configuration BLOB ) `) if err != nil { panic(err) } ctx := context.TODO() type User struct { UUID UUID `sql:"uuid,primary,binary"` Name string IP net.IP `sql:"ip,string"` Configuration map[string]interface{} `sql:"configuration,json"` } schema := sqlgen.NewSchema() schema.MustRegisterType("users", sqlgen.UniqueId, User{}) db := sqlgen.NewDB(testDb.DB, schema) uuid := uuid.NewV4() initialUser := &User{ UUID: UUID(uuid), // => BINARY (via uuid.MarshalBinary()) Name: "Jean", // => VARCHAR IP: net.IPv4(127, 0, 0, 1), // => VARCHAR (via ip.MarshalText()) Configuration: map[string]interface{}{ // => JSON (via json.Marshal(configuration)) "darkmode": true, }, } _, err = db.InsertRow(ctx, initialUser) if err != nil { panic(err) } user := &User{UUID: UUID(uuid)} err = db.QueryRow(ctx, &user, nil, nil) if err != nil { panic(err) } if reflect.DeepEqual(*user, *initialUser) { fmt.Println("They match!") } }
Output: They match!
Index ¶
- func CopySingletonSlice(result interface{}, rows []interface{}) error
- func CopySlice(result interface{}, rows []interface{}) error
- type BaseSelectQuery
- type BatchInsertQuery
- type BatchUpsertQuery
- type Column
- type DB
- func (db *DB) BaseQuery(ctx context.Context, query *BaseSelectQuery) ([]interface{}, error)
- func (db *DB) Count(ctx context.Context, model interface{}, filter Filter) (int64, error)
- func (db *DB) DeleteRow(ctx context.Context, row interface{}) error
- func (db *DB) FullScanQuery(ctx context.Context, result interface{}, filter Filter, options *SelectOptions) error
- func (db *DB) HasTx(ctx context.Context) bool
- func (db *DB) InsertRow(ctx context.Context, row interface{}) (sql.Result, error)
- func (db *DB) InsertRows(ctx context.Context, rows interface{}, chunkSize int) error
- func (db *DB) Query(ctx context.Context, result interface{}, filter Filter, options *SelectOptions) error
- func (db *DB) QueryExecer(ctx context.Context) QueryExecer
- func (db *DB) QueryRow(ctx context.Context, result interface{}, filter Filter, options *SelectOptions) error
- func (db *DB) UpdateRow(ctx context.Context, row interface{}) error
- func (db *DB) UpsertRow(ctx context.Context, row interface{}) (sql.Result, error)
- func (db *DB) UpsertRows(ctx context.Context, rows interface{}, chunkSize int) error
- func (db *DB) WithDynamicLimit(dynamicLimit DynamicLimit) (*DB, error)
- func (db *DB) WithExistingTx(ctx context.Context, tx *sql.Tx) (context.Context, error)
- func (db *DB) WithPanicOnNoIndex() (*DB, error)
- func (db *DB) WithShardLimit(shardLimit Filter) (*DB, error)
- func (db *DB) WithTx(ctx context.Context) (context.Context, *sql.Tx, error)
- type DeleteQuery
- type DynamicLimit
- type DynamicLimitErrorCallback
- type DynamicLimitFilterCallback
- type ErrorWithQuery
- type ExplainResultRow
- type Filter
- type InsertQuery
- type NullBytes
- type PrimaryKeyType
- type QueryExecer
- type SQLQuery
- type Schema
- func (s *Schema) BuildStruct(tableName string, row []driver.Value) (interface{}, error)
- func (s *Schema) MakeBatchInsertRow(rows []interface{}) (*BatchInsertQuery, error)
- func (s *Schema) MakeBatchUpsertRow(rows []interface{}) (*BatchUpsertQuery, error)
- func (s *Schema) MakeDeleteRow(row interface{}) (*DeleteQuery, error)
- func (s *Schema) MakeInsertRow(row interface{}) (*InsertQuery, error)
- func (s *Schema) MakeSelect(result interface{}, filter Filter, options *SelectOptions) (*BaseSelectQuery, error)
- func (s *Schema) MakeSelectRow(result interface{}, filter Filter, options *SelectOptions) (*BaseSelectQuery, error)
- func (s *Schema) MakeTester(table string, filter Filter) (Tester, error)
- func (s *Schema) MakeUpdateRow(row interface{}) (*UpdateQuery, error)
- func (s *Schema) MakeUpsertRow(row interface{}) (*UpsertQuery, error)
- func (s *Schema) MustRegisterType(table string, primaryKeyType PrimaryKeyType, value interface{})
- func (s *Schema) ParseRows(query *SelectQuery, res *sql.Rows) ([]interface{}, error)
- func (s *Schema) RegisterType(table string, primaryKeyType PrimaryKeyType, value interface{}) error
- func (s *Schema) UnbuildStruct(tableName string, strct interface{}) ([]interface{}, error)
- type SelectOptions
- type SelectQuery
- type SimpleWhere
- type Table
- type Tester
- type UpdateQuery
- type UpsertQuery
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CopySingletonSlice ¶
func CopySingletonSlice(result interface{}, rows []interface{}) error
Types ¶
type BaseSelectQuery ¶
type BaseSelectQuery struct { Table *Table Filter Filter Options *SelectOptions }
func (*BaseSelectQuery) MakeSelectQuery ¶
func (b *BaseSelectQuery) MakeSelectQuery() (*SelectQuery, error)
type BatchInsertQuery ¶
BatchInsertQuery represents a INSERT query with multiple rows
func (*BatchInsertQuery) ToSQL ¶
func (q *BatchInsertQuery) ToSQL() (string, []interface{})
ToSQL builds a parameterized INSERT INTO x (a, b) VALUES (?, ?), (?, ?) ... statement
type BatchUpsertQuery ¶
BatchUpsertQuery represents a INSERT ... ON DUPLICATE KEY UPDATE query with multiple rows
func (*BatchUpsertQuery) ToSQL ¶
func (q *BatchUpsertQuery) ToSQL() (string, []interface{})
ToSQL builds a parameterized INSERT INTO x (a, b) VALUES (?, ?) ON DUPLICATE KEY UPDATE query statement
type DB ¶
DB uses a *sql.DB connection that is established by its owner. DB assumes the database connection exists and is alive at all times during the lifecycle of the object.
func (*DB) BaseQuery ¶
func (db *DB) BaseQuery(ctx context.Context, query *BaseSelectQuery) ([]interface{}, error)
func (*DB) Count ¶
Count counts the number of relevant rows in a database, matching options in filter
model should be a pointer to a struct, for example:
count, err := db.Count(ctx, &User{}, &res, Filter{}) if err != nil { ... }
func (*DB) DeleteRow ¶
DeleteRow deletes a single row from the database, identified by the row's primary key
row should be a pointer to a struct, for example:
user := &User{Id; 10} if err := db.DeleteRow(ctx, user); err != nil {
func (*DB) FullScanQuery ¶
func (db *DB) FullScanQuery(ctx context.Context, result interface{}, filter Filter, options *SelectOptions) error
FullScanQuery bypasses any index checking on a query. Normal LiveDB.Query will check during tests if the query uses an index and will fail tests if not. This function will skip those checks. There are cases where we explicitly want to support full table scans such as
- During tests to verify results (eg get all)
- Some rare operations are infrequent and its better to have no index and instead perform full table scans when that query is run.
func (*DB) InsertRow ¶
InsertRow inserts a single row into the database
row should be a pointer to a struct, for example:
user := &User{Name: "foo"} if err := db.InsertRow(ctx, user); err != nil {
func (*DB) InsertRows ¶
InsertRows inserts multiple rows into the database, chunksize rows at a time. Most SQL db enforce a limit on max size of packet, which is why we need to break the rows into chunks.
rows should be an array of pointers to a struct, for example:
user1 := &User{Name: "foo"} user2 := &User{Name: "fan"} if err := db.InsertRows(ctx, [](*User){user1, user2}, 100); err != nil {
func (*DB) Query ¶
func (db *DB) Query(ctx context.Context, result interface{}, filter Filter, options *SelectOptions) error
Query fetches a collection of rows from the database
result should be a pointer to a slice of pointers to structs, for example:
var users []*User if err := db.Query(ctx, &users, nil, nil); err != nil {
func (*DB) QueryExecer ¶
func (db *DB) QueryExecer(ctx context.Context) QueryExecer
func (*DB) QueryRow ¶
func (db *DB) QueryRow(ctx context.Context, result interface{}, filter Filter, options *SelectOptions) error
QueryRow fetches a single row from the database
result should be a pointer to a pointer to a struct, for example:
var user *User if err := db.Query(ctx, &user, Filter{"id": 10}, nil); err != nil {
func (*DB) UpdateRow ¶
UpdateRow updates a single row in the database, identified by the row's primary key
row should be a pointer to a struct, for example:
user := &User{Id; 10, Name: "bar"} if err := db.UpdateRow(ctx, user); err != nil {
func (*DB) UpsertRow ¶
UpsertRow inserts a single row into the database
row should be a pointer to a struct, for example:
user := &User{Name: "foo"} if err := db.UpsertRow(ctx, user); err != nil {
func (*DB) UpsertRows ¶
UpsertRows upserts multiple rows into the database, chunksize rows at a time. Most SQL db enforce a limit on max size of packet, which is why we need to break the rows into chunks.
rows should be an array of pointers to a struct, for example:
user1 := &User{Name: "foo"} user2 := &User{Name: "fan"} if err := db.UpsertRows(ctx, [](*User){user1, user2}, 100); err != nil {
func (*DB) WithDynamicLimit ¶
func (db *DB) WithDynamicLimit(dynamicLimit DynamicLimit) (*DB, error)
WithDynamicLimit is similar to WithShardLimit except that it supports dynamically computing what filter to enforce as a limit, based on a user- provide callback. It may be used together with a shard limit.
func (*DB) WithExistingTx ¶
WithExistingTx returns a derived Context that contains the provided Tx. It is an error to invoke this method on a Context that already contains a transaction for this DB. On error WithExistingTx returns a non-nil Context, so that the caller can still easily use its Context (e.g., to log the error).
func (*DB) WithPanicOnNoIndex ¶
WithPanicOnNoIndex will configure this db connection to run an explain on every query and panic when no index is found. This setting is recommended only for use in testing so that you can find non-indexed queries during tests.
func (*DB) WithShardLimit ¶
WithShardLimit scopes the DB to only allow queries with the given key-value pairs. This means any query must include a filter for the key-value pairs in the limit, and any write must have columns including the specified key-value pairs.
func (*DB) WithTx ¶
WithTx begins a transaction and returns a derived Context that contains that transaction. It also returns the transaction value itself, for the caller to manipulate (e.g., Commit). It is an error to invoke this method on a Context that already contains a transaction for this DB. On error WithTx returns a non-nil Context, so that the caller can still easily use its Context (e.g., to log the error).
type DeleteQuery ¶
type DeleteQuery struct { Table string Where *SimpleWhere }
DeleteQuery represents a DELETE query
func (*DeleteQuery) ToSQL ¶
func (q *DeleteQuery) ToSQL() (string, []interface{})
ToSQL builds a parameterized DELETE FROM x WHERE a = ? AND b = ? statement
type DynamicLimit ¶
type DynamicLimit struct { GetLimitFilter DynamicLimitFilterCallback ShouldContinueOnError DynamicLimitErrorCallback }
DynamicLimit is used with WithDynamicLimit to register a dynamic limit on a Thunder DB.
A `DynamicLimit` consists of two callbacks, `GetLimitFilter`, and `ShouldContinueOnError`, both user-defined.
Before making an SQL query, a Thunder DB with a dynamic limit will call `GetLimitFilter` with the request context and the name of the table being queried. It will use the sqlgen filter returned by the function as the limit to check for against the current query. The underlying limit check is the same as that used in `WithShardLimit`.
If the limit check fails, Thunder will call `ShouldContinueOnError` with the resulting error object and the table name. If `ShouldContinueOnError` returns false, the query will fail. Otherwise, if the return value is true Thunder will still execute the query.
type ErrorWithQuery ¶
type ErrorWithQuery struct {
// contains filtered or unexported fields
}
ErrorWithQuery is an error wrapper that includes the clause and arguments of a sqlgen query.
func (*ErrorWithQuery) Error ¶
func (e *ErrorWithQuery) Error() string
func (*ErrorWithQuery) Reason ¶
func (e *ErrorWithQuery) Reason() string
Reason returns a human-readable error message of the clause and args
func (*ErrorWithQuery) Unwrap ¶
func (e *ErrorWithQuery) Unwrap() error
Unwrap returns the error without the clause or arguments.
type ExplainResultRow ¶
type InsertQuery ¶
InsertQuery represents a INSERT query
func (*InsertQuery) ToSQL ¶
func (q *InsertQuery) ToSQL() (string, []interface{})
ToSQL builds a parameterized INSERT INTO x (a, b) VALUES (?, ?) statement
type PrimaryKeyType ¶
type PrimaryKeyType int
const ( AutoIncrement PrimaryKeyType = iota UniqueId )
type QueryExecer ¶
type QueryExecer interface { Query(query string, args ...interface{}) (*sql.Rows, error) QueryRow(query string, args ...interface{}) *sql.Row Exec(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 ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) }
A QueryExecer is either a *sql.Tx or a *sql.DB.
type Schema ¶
func (*Schema) BuildStruct ¶
BuildStruct scans a row in struct's column order into a struct pointer.
func (*Schema) MakeBatchInsertRow ¶
func (s *Schema) MakeBatchInsertRow(rows []interface{}) (*BatchInsertQuery, error)
MakeBatchInsertRow builds a new InsertQuery to insert multiple rows
func (*Schema) MakeBatchUpsertRow ¶
func (s *Schema) MakeBatchUpsertRow(rows []interface{}) (*BatchUpsertQuery, error)
MakeBatchUpsertRow builds a new BatchUpsertQuery to upsert multiple rows
func (*Schema) MakeDeleteRow ¶
func (s *Schema) MakeDeleteRow(row interface{}) (*DeleteQuery, error)
MakeDeleteRow builds a new DeleteQuery to delete row
func (*Schema) MakeInsertRow ¶
func (s *Schema) MakeInsertRow(row interface{}) (*InsertQuery, error)
MakeInsertRow builds a new InsertQuery to insert row
func (*Schema) MakeSelect ¶
func (s *Schema) MakeSelect(result interface{}, filter Filter, options *SelectOptions) (*BaseSelectQuery, error)
func (*Schema) MakeSelectRow ¶
func (s *Schema) MakeSelectRow(result interface{}, filter Filter, options *SelectOptions) (*BaseSelectQuery, error)
func (*Schema) MakeTester ¶
func (*Schema) MakeUpdateRow ¶
func (s *Schema) MakeUpdateRow(row interface{}) (*UpdateQuery, error)
MakeUpdateRow builds a new UpdateQuery to update row
func (*Schema) MakeUpsertRow ¶
func (s *Schema) MakeUpsertRow(row interface{}) (*UpsertQuery, error)
MakeUpsertRow builds a new UpsertQuery to upsqrt row
func (*Schema) MustRegisterType ¶
func (s *Schema) MustRegisterType(table string, primaryKeyType PrimaryKeyType, value interface{})
func (*Schema) ParseRows ¶
func (s *Schema) ParseRows(query *SelectQuery, res *sql.Rows) ([]interface{}, error)
func (*Schema) RegisterType ¶
func (s *Schema) RegisterType(table string, primaryKeyType PrimaryKeyType, value interface{}) error
func (*Schema) UnbuildStruct ¶
UnbuildStruct extracts SQL values from a struct.
type SelectOptions ¶
type SelectOptions struct { Where string Values []interface{} OrderBy string Limit int ForUpdate bool AllowNoIndex bool // A list of indexes to specify in the USE INDEX hint UseIndex []string // A list of indexes to specify in the FORCE INDEX hint. // If both UseIndex and ForceIndex are provided, only ForceIndex will be used: // a query that specifies both USE INDEX and FORCE INDEX would be invalid syntax. ForceIndex []string }
func (*SelectOptions) IncludeFilter ¶
func (s *SelectOptions) IncludeFilter(table *Table, filter Filter) error
type SelectQuery ¶
type SelectQuery struct { Table string Columns []string Options *SelectOptions }
SelectQuery represents a SELECT query
func (*SelectQuery) ToSQL ¶
func (q *SelectQuery) ToSQL() (string, []interface{})
ToSQL builds a parameterized SELECT a, b, c FROM x ... statement
type SimpleWhere ¶
type SimpleWhere struct { Columns []string Values []interface{} }
SimpleWhere represents a simple WHERE clause
func (*SimpleWhere) ToSQL ¶
func (w *SimpleWhere) ToSQL() (string, []interface{})
ToSQL builds a `a = ? AND b = ?` clause
type UpdateQuery ¶
type UpdateQuery struct { Table string Columns []string Values []interface{} Where *SimpleWhere }
UpdateQuery represents a UPDATE query
func (*UpdateQuery) ToSQL ¶
func (q *UpdateQuery) ToSQL() (string, []interface{})
ToSQL builds a parameterized UPDATE x SET a = ?, b = ? WHERE c = ? statement
type UpsertQuery ¶
UpsertQuery represents a INSERT ... ON DUPLICATE KEY UPDATE query
func (*UpsertQuery) ToSQL ¶
func (q *UpsertQuery) ToSQL() (string, []interface{})
ToSQL builds a parameterized INSERT INTO x (a, b) VALUES (?, ?) statement