makroud

package module
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Aug 14, 2020 License: MIT Imports: 22 Imported by: 0

README

Makroud

CircleCI Documentation License

A high level SQL Connector.

Introduction

Makroud is a high level SQL Connector that only support PostgreSQL at the moment.

It's an advanced mapper and/or a lightweight ORM that relies on reflection to generate queries. Using reflection has its flaws, type safety is not guaranteed and a panic is still possible, even if you are very careful and vigilant. However, development is super easy and straightforward since it doesn't rely on code generation.

Makroud isn't a migration tool and it doesn't inspect the database to define the application data model (since there is no code generation). It's really important to have Active Record that are synchronized with your data model in your database.

It also support simple associations (one, many) with preloading.

Under the hood, it relies on Loukoum for query generation. In addition, it's heavily inspired by Sqlx for its extended mapper and Sqalx to support nested transaction.

Installation

Using Go Modules

go get github.com/ulule/makroud@v0.8.0

Usage

Create a Driver

A Driver is a high level abstraction of a database connection or a transaction. It's almost required everytime alongside a context.Context to manipulate rows.

driver, err := makroud.New(
	makroud.Host(cfg.Host),
	makroud.Port(cfg.Port),
	makroud.User(cfg.User),
	makroud.Password(cfg.Password),
	makroud.Database(cfg.Name),
	makroud.SSLMode(cfg.SSLMode),
	makroud.MaxOpenConnections(cfg.MaxOpenConnections),
	makroud.MaxIdleConnections(cfg.MaxIdleConnections),
)

Also, you can use a struct directly if you don't need to use functional options:

driver, err := makroud.NewWithOptions(&makroud.ClientOptions{
	Host:               cfg.Host,
	Port:               cfg.Port,
	User:               cfg.User,
	Password:           cfg.Password,
	Database:           cfg.Name,
	SSLMode:            cfg.SSLMode,
	MaxOpenConnections: cfg.MaxOpenConnections,
	MaxIdleConnections: cfg.MaxIdleConnections,
})
Advanced mapper

If a lightweight ORM doesn't fit your requirements and an advanced mapper is enough for your usecase: makroud does that very well.

You could either use primitive and compound types for your queries:

import "github.com/ulule/makroud"

stmt := `SELECT id FROM users WHERE email = 'john.doe@example.com'`
id := int64(0)

err := makroud.RawExec(ctx, driver, stmt, &id)
if err != nil {
	return err
}

stmt = `SELECT created_at FROM users WHERE id = 42`
created := time.Time{}

err := makroud.RawExec(ctx, driver, stmt, &created)
if err != nil {
	return err
}

stmt = `SELECT email FROM users WHERE id IN (1, 2, 3, 4)`
list := []string{}

err := makroud.RawExec(ctx, driver, stmt, &list)
if err != nil {
	return err
}

Or, define a struct that contains your database table (or view) columns:

import "github.com/ulule/makroud"
import "github.com/ulule/loukoum/v3"

type User struct {
	ID       int64  `mk:"id"`
	Email    string `mk:"email"`
	Password string `mk:"password"`
	Country  string `mk:"country"`
	Locale   string `mk:"locale"`
}

users := []User{}
stmt := loukoum.Select("*").
	From("users").
	Where(loukoum.Condition("id").In(1, 2, 3, 4))

err := makroud.Exec(ctx, driver, stmt, &users)
if err != nil {
	return err
}
Lightweight ORM
Define a Model

With the Active Record approach, you have to define a model that wraps your database table (or view) columns into properties.

Models are structs that contain basic go types, pointers, sql.Scanner, driver.Valuer or Model interface. All the fields of this struct will be columns in the database table.

An example
type User struct {
	// Columns
	ID        string `makroud:"column:id,pk:ulid"`
	Email     string `makroud:"column:email"`
	Password  string `makroud:"column:password"`
	Country   string `makroud:"column:country"`
	Locale    string `makroud:"column:locale"`
	ProfileID string `makroud:"column:profile_id,fk:profiles"`
	// Relationships
	Roles    []Role    `makroud:"relation:roles.user_id"`
	Profile  *Profile  `makroud:"relation:profile_id"`
}

func (User) TableName() string {
	return "users"
}
What does it means ?

First of all, you have to define a TableName method that returns the database table name (or view). Without that information, makroud cannot uses that struct as a Model.

Then, you have to define your model columns using struct tags:

  • column(string): Define column name.
  • pk(bool|string): Define column as a primary key, it accepts the following argument:
    • true: Uses internal db mechanism to define primary key value
    • ulid: Generate a ULID to define primary key value
    • uuid-v1: Generate a UUID V1 to define primary key value
    • uuid-v4: Generate a UUID V4 to define primary key value
  • default(bool): On insert, if model has a zero value, it will use the db default value.
  • fk(string): Define column as a foreign key, reference table must be provided.
  • relation(string): Define which column to use for preload. The column must be prefixed by the table name if it's not the model table name (However, the prefix is optional if the table name is the same as the model). See Preload section for further information.
  • -(bool): Ignore this field.

NOTE: Tags of type bool can be set as key:true or just key for implicit true.

NOTE: Tags must be separated by a comma (tagA, tagB, tagC).

Keep in mind that a model requires one primary key (and just one). It's a known limitation that only one primary key can be specified and it can't be a composite key.

After that, you can define optional relationships (or associations) that can be preloaded later. The preload mechanism, which enables you to fetch relationships from database, support these types:

  • Model
  • *Model
  • []Model
  • []*Model
  • *[]Model
  • *[]*Model

NOTE: You could either use makroud or mk as tag identifier.

Conventions
ID as Primary Key

By default, if the pk tag is undefined, makroud will use the field named ID as primary key with this configuration: pk:db

type User struct {
	ID   string `makroud:"column:id"`   // Field named ID will be used as a primary key by default.
	Name string `makroud:"column:name"`
}
Snake Case Column Name

By default, if the column tag is undefined, makroud will transform field name to lower snake case as column name.

type User struct {
	ID   string `makroud:"pk"` // Column name is `id`
	Name string `makroud:""`   // Column name is `name`
}
Preload relationships

By default, if the relation tag is undefined, makroud will infer the column name to use for the preload mechanism.

Local foreign key:

Let's define a user with a profile:

type User struct {
	ID       string  `makroud:"column:id,pk"`
	Email    string  `makroud:"column:email"`
	PID      string  `makroud:"column:profile_id,fk:profiles"`
	Profile  *Profile
}

func (User) TableName() string {
	return "users"
}

type Profile struct {
	ID         string  `makroud:"column:id,pk:ulid"`
	FirstName  string  `makroud:"column:first_name"`
	LastName   string  `makroud:"column:last_name"`
	Enabled    bool    `makroud:"column:enabled"`
}

func (Profile) TableName() string {
	return "profiles"
}

Since the field Profile in the User has no relation tag, makroud will try to find, in the first pass, the field with the name ProfileID (FieldName + ID) in User model. It's mandatory that this field is also a foreign key to the profiles table.

Unfortunately for us, User model has no such field. So, makroud will try to find, in the second and final pass, the first field that is a foreign key to the profiles table. In our example, it will use the field PID.

Remote foreign key:

Let's define a user with a profile:

type User struct {
	ID       string  `makroud:"column:id,pk"`
	Email    string  `makroud:"column:email"`
	Profile  *Profile
}

func (User) TableName() string {
	return "users"
}

type Profile struct {
	ID         string  `makroud:"column:id,pk:ulid"`
	FirstName  string  `makroud:"column:first_name"`
	LastName   string  `makroud:"column:last_name"`
	Enabled    bool    `makroud:"column:enabled"`
	UID        string  `makroud:"column:user_id,fk:users"`
}

func (Profile) TableName() string {
	return "profiles"
}

Since the field Profile in the User has no relation tag, makroud will try to find, in the first pass, the field with the name UserID (ModelName + ID) in Profile model. It's mandatory that this field is also a foreign key to the users table.

Unfortunately for us, Profile model has no such field. So, makroud will try to find, in the second and final pass, the first field that is a foreign key to the users table. In our example, it will use the field UID.

CreatedAt tracking

For models having a CreatedAt field, it will be set to current time when the record is first created.

type User struct {
	ID        string    `makroud:"column:id,pk"`
	Name      string    `makroud:"column:name"`
	CreatedAt time.Time `makroud:"column:created_at"`
}

You can override the default field name and/or column name by adding this method:

func (User) CreatedKey() string {
	return "created"
}
UpdatedAt tracking

For models having a UpdatedAt field, it will be set to current time when records are updated.

type User struct {
	ID        string    `makroud:"column:id,pk"`
	Name      string    `makroud:"column:name"`
	UpdatedAt time.Time `makroud:"column:updated_at"`
}

You can override the default field name and/or column name by adding this method:

func (User) UpdatedKey() string {
	return "updated"
}
DeletedAt tracking

For models having a DeletedAt field, it will be set to current time when records are archived.

type User struct {
	ID        string      `makroud:"column:id,pk"`
	Name      string      `makroud:"column:name"`
	DeletedAt pq.NullTime `makroud:"column:deleted_at"`
}

You can override the default field name and/or column name by adding this method:

func (User) DeletedKey() string {
	return "deleted"
}
Operations

For the following sections, we assume that you have a context.Context and a makroud.Driver instance.

Insert

For a simple insert, you can save a model like this:

func CreateUser(ctx context.Context, driver makroud.Driver, name string) (*User, error) {
	user := &User{
		Name: name,
	}

	err := makroud.Save(ctx, driver, user)
	if err != nil {
		return nil, err
	}

	return user, nil
}

Or for more complex statements, use a Loukoum InsertBuilder alongside the model.

import "github.com/ulule/loukoum/v3"

func CreateUser(ctx context.Context, driver makroud.Driver, name string) (*User, error) {
	user := &User{
		Name: name,
	}

	stmt := loukoum.Insert("users").
		Set(loukoum.Pair("name", user.Name)).
		Returning("id, created_at, updated_at")

	err := makroud.Exec(ctx, driver, stmt, user)
	if err != nil {
		return nil, err
	}

	return user, nil
}
Update

For a simple update, asumming your model have a primary key defined, you can save it by executing:

func UpdateUser(ctx context.Context, driver makroud.Driver, user *User, name string) error {
	user.Name = name
	return makroud.Save(ctx, driver, user)
}

Or for more complex statements, use a Loukoum UpdateBuilder alongside the model.

import "github.com/ulule/loukoum/v3"

func UpdateUser(ctx context.Context, driver makroud.Driver, user *User, name string) error {
	user.Name = name

	stmt := loukoum.Update("users").
		Set(
			loukoum.Pair("updated_at", loukoum.Raw("NOW()")),
			loukoum.Pair("name", user.Name),
		).
		Where(loukoum.Condition("id").Equal(user.ID)).
		Returning("updated_at")

	err := makroud.Exec(ctx, driver, stmt, user)
	if err != nil {
		return nil, err
	}

	return user, nil
}
Delete

For a simple delete (using a DELETE statement), asumming your model have a primary key defined, you can delete it using:

func DeleteUser(ctx context.Context, driver makroud.Driver, user *User) error {
	return makroud.Delete(ctx, driver, user)
}

Or for more complex statements, use a Loukoum DeleteBuilder alongside the model.

import "github.com/ulule/loukoum/v3"

func DeleteUser(ctx context.Context, driver makroud.Driver, user *User) error {
	stmt := loukoum.Delete("users").Where(loukoum.Condition("id").Equal(user.ID))

	err := makroud.Exec(ctx, driver, stmt, user)
	if err != nil {
		return nil, err
	}

	return user, nil
}
Archive

Archive executes an UPDATE on DeletedAt field on given value.

func ArchiveUser(ctx context.Context, driver makroud.Driver, user *User) error {
	return makroud.Archive(ctx, driver, user)
}

NOTE: If the model has no DeletedAt field, an error is returned.

Or for more complex statements, use a Loukoum UpdateBuilder alongside the model.

import "github.com/ulule/loukoum/v3"

func ArchiveUser(ctx context.Context, driver makroud.Driver, user *User) error {
	user.Name = name

	stmt := loukoum.Update("users").
		Set(
			loukoum.Pair("deleted_at", loukoum.Raw("NOW()")),
			loukoum.Pair("name", ""),
		).
		Where(loukoum.Condition("id").Equal(user.ID)).
		Returning("deleted_at")

	err := makroud.Exec(ctx, driver, stmt, user)
	if err != nil {
		return nil, err
	}

	return user, nil
}
Query

Because querying data is a bit more complex than just writing and/or deleting stuff. By using Loukoum components, you can either execute simple query:

import "github.com/ulule/loukoum/v3"

func GetUserByID(ctx context.Context, driver makroud.Driver, id string) (*User, error) {
	user := &User{}
	err := makroud.Select(ctx, driver, user, loukoum.Condition("id").Equal(id))
	if err != nil {
		return nil, err
	}

	return user, nil
}

func ListMessagesByUserID(ctx context.Context, driver makroud.Driver,
	userID string, page int) ([]*Message, error) {

	messages := []*Message{}
	err := makroud.Select(ctx, driver, &messages,
		loukoum.Condition("user_id").Equal(id),
		loukoum.Order("created_at", loukoum.Desc),
		loukoum.Limit(50),
		loukoum.Offset(50 * (page - 1)),
	)
	if err != nil {
		return nil, err
	}

	return messages, nil
}

Or execute more complex statements:

import "github.com/ulule/loukoum/v3"

func FindStaffComments(ctx context.Context, driver makroud.Driver) ([]*Comment, error) {
	comments := []*Comment{}

	stmt := loukoum.Select("id", "email", "status", "user_id", "message", "created_at").
		From("comments").
		Where(loukoum.Condition("deleted_at").IsNull(true)).
		Where(
			loukoum.Condition("user_id").In(
				loukoum.Select("id").
					From("users").
					Where(loukoum.Condition("role").Equal("staff")),
			),
		)

	err := makroud.Exec(ctx, driver, stmt, &comments)
	if err != nil {
		return nil, err
	}

	return comments, nil
}

Also, it supports query without Model.

func FindUserIDWithStaffRole(ctx context.Context, driver makroud.Driver) ([]string, error) {
	list := []string{}

	stmt := loukoum.Select("id").
		From("users").
		Where(loukoum.Condition("role").Equal("staff"))

	err := makroud.Exec(ctx, driver, stmt, &list)
	if err != nil {
		return nil, err
	}

	return list, nil
}
Transaction

Sometimes, you need to execute queries and/or commands inside a transaction block, that bundles multiple steps into a single, all-or-nothing operation.

This is achieved by declaring a lambda function. If this function returns an error, the transaction rollbacks automatically. Otherwise, the transaction will be committed.

func SetupUsers(ctx context.Context, driver makroud.Driver) error {
	return makroud.Transaction(ctx, driver, nil, func(tx makroud.Driver) error {

		err := makroud.Save(ctx, tx, &User{
			Name: "Benjamin",
		})
		if err != nil {
			return err
		}

		err = makroud.Save(ctx, tx, &User{
			Name: "Taha",
		})
		if err != nil {
			return err
		}

		return nil
	})
}

And other times, transactions with an isolation level.

func Withdraw(ctx context.Context, driver makroud.Driver) error {
	return makroud.Transaction(ctx, driver, makroud.LevelSerializable,
		func(tx makroud.Driver) error {
			// Withdraw operation...
			return nil
		},
	)
}

Or even, nested transaction with the option SavepointEnabled.

func AcceptOffer(ctx context.Context, driver makroud.Driver) error {
	return makroud.Transaction(ctx, driver, nil, func(tx1 makroud.Driver) error {
		//
		// Execute several operations.
		//
		err := makroud.Transaction(ctx, tx1, nil, func(tx2 makroud.Driver) error {
			//
			// Execute complex operations that may succeed...
			//
			return err
		})
		if err != nil {
			//
			// Execute fallback operations if an error has occurred...
			//
			return nil
		}
		//
		// Execute normal operations otherwise...
		//
		return nil
	})
}
Preload

On models having associations, you can execute a preload to fetch these relationships from the database.

Let's define a user with a profile:

type User struct {
	ID       string   `makroud:"column:id,pk"`
	Email    string   `makroud:"column:email"`
	Profile  *Profile `makroud:"relation:profiles.user_id"`
}

func (User) TableName() string {
	return "users"
}

type Profile struct {
	ID         string  `makroud:"column:id,pk:ulid"`
	FirstName  string  `makroud:"column:first_name"`
	LastName   string  `makroud:"column:last_name"`
	UserID     string  `makroud:"column:user_id,fk:users"`
	Enabled    bool    `makroud:"column:enabled"`
}

func (Profile) TableName() string {
	return "profiles"
}

Once you obtain a user record, you can preload its profile by executing:

err := makroud.Preload(ctx, driver, &user, makroud.WithPreloadField("Profile"))

Or, if preloading requires specific conditions, you can use a callback like this:

import "github.com/ulule/loukoum/v3/builder"

err := makroud.Preload(ctx, driver, &user,
	makroud.WithPreloadCallback("Profile", func(query builder.Select) builder.Select {
		return query.Where(loukoum.Condition("enabled").Equal(true))
	}),
)

If there is no error and if the user record has a profile, then you should have the Profile value loaded.

Development

Docker

The test suite is running on PostgreSQL. We use Docker to create a running container using scripts/database.

Testing

To run the test suite, simply execute:

scripts/test

Also, you can execute the linter with:

scripts/lint
Notes

If you have to examine rows generated from unit test, you can prevent the test suite to cleanup by using:

DB_KEEP=true scripts/test

Then, you can access the database with:

scripts/database --client
Random

Because sometimes it's hard to think of a good test fixture, using generators can save your productivity.

This website was a great help to write unit test: http://www.fantasynamegenerators.com

License

This is Free Software, released under the MIT License.

Contributing

Don't hesitate ;)

Documentation

Overview

Package makroud provides a high level SQL Connector. At the moment, only PostgreSQL is supported.

It's an advanced mapper and/or a lightweight ORM that relies on reflection.

For further informations, you can read this documentation: https://github.com/ulule/makroud/blob/master/README.md

Or you can discover makroud with these examples. First, you have to create a driver:

driver, err := makroud.New(
    makroud.Host(cfg.Host),
    makroud.Port(cfg.Port),
    makroud.User(cfg.User),
    makroud.Password(cfg.Password),
    makroud.Database(cfg.Name),
    makroud.SSLMode(cfg.SSLMode),
    makroud.MaxOpenConnections(cfg.MaxOpenConnections),
    makroud.MaxIdleConnections(cfg.MaxIdleConnections),
)

Then, define a model:

type User struct {
    ID        string `makroud:"column:id,pk:ulid"`
    Email     string `makroud:"column:email"`
    Password  string `makroud:"column:password"`
    Country   string `makroud:"column:country"`
    Locale    string `makroud:"column:locale"`
}

Execute an insert:

user := &User{
    Email:    "gilles@ulule.com",
    Password: "019a7bdf56b9f48e18096d62b21f",
    Country:  "FR",
    Locale:   "fr",
}

err := makroud.Save(ctx, driver, user)

Or an update:

user.Email = "gilles.fabio@ulule.com"

err := makroud.Save(ctx, driver, user)

Or execute a simple query without model:

import "github.com/ulule/loukoum/v3"

list := []string{}

stmt := loukoum.Update("users").
    Set(
        loukoum.Pair("updated_at", loukoum.Raw("NOW()")),
        loukoum.Pair("status", status),
    ).
    Where(loukoum.Condition("group_id").Equal(gid)).
    Returning("id")

err := makroud.Exec(ctx, driver, stmt, &list)

Index

Constants

View Source
const (
	// FKUnknownType is an unknown foreign key.
	FKUnknownType = FKType(iota)
	// FKIntegerType uses an integer as foreign key.
	FKIntegerType
	// ForeignKeyString uses a string as foreign key.
	FKStringType
	// FKOptionalIntegerType uses an optional integer as foreign key.
	FKOptionalIntegerType
	// FKOptionalStringType uses an optional string as foreign key.
	FKOptionalStringType
)

Foreign key types.

View Source
const (
	AssociationTypeUndefined = AssociationType(iota)
	AssociationTypeOne
	AssociationTypeMany
)

Association types.

View Source
const (
	// PKUnknownType is an unknown primary key.
	PKUnknownType = PKType(iota)
	// PKIntegerType uses an integer as primary key.
	PKIntegerType
	// PrimaryKeyString uses a string as primary key.
	PKStringType
)

Primary key types.

View Source
const (
	// PrimaryKeyDBDefault uses internal db mechanism to define primary key value.
	PrimaryKeyDBDefault = PrimaryKeyDefault(iota)
	// PrimaryKeyULIDDefault uses a ulid generator to define primary key value.
	PrimaryKeyULIDDefault
	// PrimaryKeyUUIDV1Default uses a uuid v1 generator to define primary key value.
	PrimaryKeyUUIDV1Default
	// PrimaryKeyUUIDV4Default uses a uuid v4 generator to define primary key value.
	PrimaryKeyUUIDV4Default
)

PrimaryKey default types.

View Source
const (
	// TagName defines makroud tag namespace.
	TagName = "makroud"
	// TagNameShort defines makroud tag namespace in short version.
	TagNameShort = "mk"
	// TagNameAlt defines sqlx tag namespace for backward compatibility.
	TagNameAlt = "db"
)
View Source
const (
	TagKeyIgnored       = "-"
	TagKeyDefault       = "default"
	TagKeyColumn        = "column"
	TagKeyColumnShort   = "col"
	TagKeyForeignKey    = "fk"
	TagKeyPrimaryKey    = "pk"
	TagKeyRelation      = "relation"
	TagKeyRelationShort = "rel"
	TagKeyULID          = "ulid"
	TagKeyUUIDV1        = "uuid-v1"
	TagKeyUUIDV4        = "uuid-v4"
)

Tag modifiers on Model.

View Source
const (
	LevelDefault         = sql.LevelDefault
	LevelReadUncommitted = sql.LevelReadUncommitted
	LevelReadCommitted   = sql.LevelReadCommitted
	LevelRepeatableRead  = sql.LevelRepeatableRead
	LevelSerializable    = sql.LevelSerializable
)

List of supported isolation level for a postgres transaction.

View Source
const ClientDriver = "postgres"

ClientDriver defines the driver name used in makroud.

View Source
const DefaultSelector = MasterSelector

DefaultSelector defines the default selector alias.

View Source
const MasterSelector = "master"

MasterSelector defines the master alias.

View Source
const SlaveSelector = "slave"

SlaveSelector defines the slave alias.

Variables

View Source
var (
	// ErrNoRows is returned when query doesn't return a row.
	ErrNoRows = fmt.Errorf("no rows in result set")
	// ErrInvalidDriver is returned when given driver is undefined.
	ErrInvalidDriver = fmt.Errorf("a makroud driver is required")
	// ErrPointerRequired is returned when given value is not a pointer.
	ErrPointerRequired = fmt.Errorf("a pointer is required")
	// ErrPointerOrSliceRequired is returned when given value is not a pointer or a slice.
	ErrPointerOrSliceRequired = fmt.Errorf("a pointer or a slice is required")
	// ErrUnknownPreloadRule is returned when given rule is unknown.
	ErrUnknownPreloadRule = fmt.Errorf("unknown rule")
	// ErrStructRequired is returned when given value is not a struct.
	ErrStructRequired = fmt.Errorf("a struct is required")
	// ErrModelRequired is returned when given value is not a model.
	ErrModelRequired = fmt.Errorf("a model is required")
	// ErrSchemaColumnRequired is returned when we cannot find a column in current schema.
	ErrSchemaColumnRequired = fmt.Errorf("cannot find column in schema")
	// ErrSchemaCreatedKey is returned when we cannot find a created key in given schema.
	ErrSchemaCreatedKey = fmt.Errorf("cannot find created key in schema")
	// ErrSchemaUpdatedKey is returned when we cannot find a updated key in given schema.
	ErrSchemaUpdatedKey = fmt.Errorf("cannot find updated key in schema")
	// ErrSchemaDeletedKey is returned when we cannot find a deleted key in given schema.
	ErrSchemaDeletedKey = fmt.Errorf("cannot find deleted key in schema")
	// ErrPreloadInvalidSchema is returned when preload detect an invalid schema from given model.
	ErrPreloadInvalidSchema = fmt.Errorf("given model has an invalid schema")
	// ErrPreloadInvalidModel is returned when preload detect an invalid model.
	ErrPreloadInvalidModel = fmt.Errorf("given model is invalid")
	// ErrPreloadInvalidPath is returned when preload detect an invalid path.
	ErrPreloadInvalidPath = fmt.Errorf("given path is invalid")
	// ErrSelectorNotFoundConnection is returned when the required connection does not exists in selector connections.
	ErrSelectorNotFoundConnection = fmt.Errorf("cannot find connection in selector")
	// ErrSelectorMissingRetryConnection is returned when the retry mechanism has no connection available from selector.
	ErrSelectorMissingRetryConnection = fmt.Errorf("cannot find a healthy connection in selector")
	// ErrSliceOfScalarMultipleColumns is returned when a query return multiple columns for a slice of scalar.
	ErrSliceOfScalarMultipleColumns = fmt.Errorf("slice of scalar with multiple columns")
	// ErrCommitNotInTransaction is returned when using commit outside of a transaction.
	ErrCommitNotInTransaction = fmt.Errorf("cannot commit outside of a transaction")
)

Makroud general errors.

View Source
var TagsKeyMapper = map[string]string{
	TagKeyColumnShort:   TagKeyColumn,
	TagKeyRelationShort: TagKeyRelation,
}

TagsKeyMapper is a mapper that convert tag key to another key (aliasing).

TagsList is a list of supported tags, this include sqlx and makroud one.

View Source
var TagsNameMapper = map[string]string{
	TagNameAlt: TagKeyColumn,
}

TagsNameMapper is a mapper that convert supported tag name to makroud one (migrate).

Functions

func Archive

func Archive(ctx context.Context, driver Driver, model Model) error

Archive archives the given instance.

func Count

func Count(ctx context.Context, driver Driver, stmt builder.Builder) (int64, error)

Count will execute the given query to return a number from an aggregate function.

func DebugField

func DebugField(field Field) string

DebugField returns a human readable version of given instance.

func DebugTag

func DebugTag(tag Tag) string

DebugTag returns a human readable version of given instance.

func DebugTagProperty

func DebugTagProperty(prop TagProperty) string

DebugTagProperty returns a human readable version of given instance.

func DebugTags

func DebugTags(tags Tags) string

DebugTags returns a human readable version of given instance.

func Delete

func Delete(ctx context.Context, driver Driver, model Model) error

Delete deletes the given instance.

func Exec

func Exec(ctx context.Context, driver Driver, stmt builder.Builder, dest ...interface{}) error

Exec will execute given query from a Loukoum builder. If an object is given, it will mutate it to match the row values.

func FloatCount

func FloatCount(ctx context.Context, driver Driver, stmt builder.Builder) (float64, error)

FloatCount will execute given query to return a number (in float) from a aggregate function.

func GenerateULID

func GenerateULID(driver Driver) string

GenerateULID generates a new ulid.

func GenerateUUIDV1

func GenerateUUIDV1(driver Driver) string

GenerateUUIDV1 generates a new uuid v1.

func GenerateUUIDV4

func GenerateUUIDV4(driver Driver) string

GenerateUUIDV4 generates a new uuid v4.

func IsErrNoRows

func IsErrNoRows(err error) bool

IsErrNoRows returns if given error is a "no rows" error.

func Log

func Log(ctx context.Context, driver Driver, query Query, duration time.Duration)

Log will emmit given query on driver's attached Logger. nolint: interfacer

func Preload

func Preload(ctx context.Context, driver Driver, out interface{}, handlers ...PreloadHandler) error

Preload preloads related fields.

func RawExec

func RawExec(ctx context.Context, driver Driver, query string, dest ...interface{}) error

RawExec will execute given query. If an object is given, it will mutate it to match the row values.

func RawExecArgs added in v0.6.0

func RawExecArgs(ctx context.Context, driver Driver, query string, args []interface{}, dest ...interface{}) error

RawExecArgs will execute given query with given arguments. If an object is given, it will mutate it to match the row values.

func Retry

func Retry(handler func(Driver) error, drivers ...Driver) (err error)

Retry execute given handler on several drivers until it succeeds on a connection.

func Save

func Save(ctx context.Context, driver Driver, model Model) error

Save inserts or updates the given instance.

func Select added in v0.8.0

func Select(ctx context.Context, driver Driver, dest interface{}, args ...interface{}) error

Select retrieves the given instance using given arguments as criteria. This method accepts loukoum's stmt.Order, stmt.Offet, stmt.Limit and stmt.Expression as arguments. For unsupported statement, they will be ignored.

func Transaction

func Transaction(ctx context.Context, driver Driver, opts *TxOptions,
	handler func(driver Driver) error) error

Transaction will creates a transaction.

Types

type AssociationType

type AssociationType uint8

AssociationType define an association type.

func (AssociationType) String

func (e AssociationType) String() string

type Client

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

Client is a wrapper that can interact with the database, it's an implementation of Driver.

func New

func New(options ...Option) (*Client, error)

New returns a new Client instance.

func NewWithOptions

func NewWithOptions(options *ClientOptions) (*Client, error)

NewWithOptions returns a new Client instance.

func (*Client) Begin

func (c *Client) Begin(ctx context.Context, opts ...*TxOptions) (Driver, error)

Begin starts a new transaction.

The provided context is used until the transaction is committed or rolled back. If the context is canceled, the driver will roll back the transaction. Commit will return an error if the context provided to Begin is canceled.

The provided TxOptions is optional. If a non-default isolation level is used that the driver doesn't support, an error will be returned. If no option is provided, the default isolation level of the driver will be used.

func (*Client) Close

func (c *Client) Close() error

Close closes the underlying connection.

func (*Client) Commit

func (c *Client) Commit() error

Commit commits the associated transaction.

func (*Client) DriverName

func (c *Client) DriverName() string

DriverName returns the driver name used by this driver.

func (*Client) Entropy added in v0.8.0

func (c *Client) Entropy() io.Reader

Entropy returns an entropy source, used for primary key generation (if required).

WARNING: Please, do not use this method unless you know what you are doing.

func (*Client) Exec

func (c *Client) Exec(ctx context.Context, query string, args ...interface{}) error

Exec executes a statement using given arguments.

func (*Client) GetCache added in v0.8.0

func (c *Client) GetCache() *DriverCache

GetCache returns the driver internal cache.

WARNING: Please, do not use this method unless you know what you are doing: YOU COULD BREAK YOUR DRIVER.

func (*Client) HasCache added in v0.8.0

func (c *Client) HasCache() bool

HasCache returns if current driver has an internal cache.

func (*Client) HasLogger added in v0.8.0

func (c *Client) HasLogger() bool

HasLogger returns if the driver has a logger.

func (*Client) HasObserver added in v0.8.0

func (c *Client) HasObserver() bool

HasObserver returns if the driver has an observer.

func (*Client) Logger added in v0.8.0

func (c *Client) Logger() Logger

Logger returns the driver logger.

WARNING: Please, do not use this method unless you know what you are doing.

func (*Client) MustExec

func (c *Client) MustExec(ctx context.Context, query string, args ...interface{})

MustExec executes a statement using given arguments. If an error has occurred, it panics.

func (*Client) MustQuery

func (c *Client) MustQuery(ctx context.Context, query string, args ...interface{}) Rows

MustQuery executes a statement that returns rows using given arguments. If an error has occurred, it panics.

func (*Client) Observer added in v0.8.0

func (c *Client) Observer() Observer

Observer returns the driver observer.

WARNING: Please, do not use this method unless you know what you are doing.

func (*Client) Ping

func (c *Client) Ping() error

Ping verifies that the underlying connection is healthy.

func (*Client) PingContext added in v0.7.0

func (c *Client) PingContext(ctx context.Context) error

PingContext verifies that the underlying connection is healthy.

func (*Client) Prepare

func (c *Client) Prepare(ctx context.Context, query string) (Statement, error)

Prepare creates a prepared statement for later queries or executions. Multiple queries or executions may be run concurrently from the returned statement.

func (*Client) Query

func (c *Client) Query(ctx context.Context, query string, args ...interface{}) (Rows, error)

Query executes a statement that returns rows using given arguments.

func (*Client) Rollback

func (c *Client) Rollback() error

Rollback rollbacks the associated transaction.

func (*Client) SetCache added in v0.8.0

func (c *Client) SetCache(cache *DriverCache)

SetCache replace the driver internal cache by the given one.

WARNING: Please, do not use this method unless you know what you are doing: YOU COULD BREAK YOUR DRIVER.

type ClientOptions

type ClientOptions struct {
	Port               int
	Host               string
	User               string
	Password           string
	Database           string
	SSLMode            string
	Timezone           string
	MaxOpenConnections int
	MaxIdleConnections int
	WithCache          bool
	SavepointEnabled   bool
	ApplicationName    string
	ConnectTimeout     int
	Logger             Logger
	Observer           Observer
	Entropy            io.Reader
	Node               Node
}

ClientOptions configure a Client instance.

func NewClientOptions

func NewClientOptions() *ClientOptions

NewClientOptions creates a new ClientOptions instance with default options.

func (ClientOptions) String

func (e ClientOptions) String() string

type Columns

type Columns []string

Columns is a list of table columns.

func GetColumns

func GetColumns(driver Driver, model Model) (Columns, error)

GetColumns returns a comma-separated string representation of a model's table columns.

func (Columns) List

func (c Columns) List() []string

List returns table columns.

func (Columns) String

func (c Columns) String() string

Returns string representation of slice.

type Driver

type Driver interface {

	// Exec executes a statement using given arguments.
	Exec(ctx context.Context, query string, args ...interface{}) error

	// MustExec executes a statement using given arguments.
	// If an error has occurred, it panics.
	MustExec(ctx context.Context, query string, args ...interface{})

	// Query executes a statement that returns rows using given arguments.
	Query(ctx context.Context, query string, args ...interface{}) (Rows, error)

	// MustQuery executes a statement that returns rows using given arguments.
	// If an error has occurred, it panics.
	MustQuery(ctx context.Context, query string, args ...interface{}) Rows

	// Prepare creates a prepared statement for later queries or executions.
	// Multiple queries or executions may be run concurrently from the returned statement.
	Prepare(ctx context.Context, query string) (Statement, error)

	// Close closes the underlying connection.
	Close() error

	// Ping verifies that the underlying connection is healthy.
	Ping() error

	// DriverName returns the driver name used by this driver.
	DriverName() string

	// Begin starts a new transaction.
	//
	// The provided context is used until the transaction is committed or rolled back.
	// If the context is canceled, the driver will roll back the transaction.
	// Commit will return an error if the context provided to Begin is canceled.
	//
	// The provided TxOptions is optional.
	// If a non-default isolation level is used that the driver doesn't support, an error will be returned.
	// If no option is provided, the default isolation level of the driver will be used.
	Begin(ctx context.Context, opts ...*TxOptions) (Driver, error)

	// Rollback rollbacks the associated transaction.
	Rollback() error

	// Commit commits the associated transaction.
	Commit() error

	// HasCache returns if current driver has an internal cache.
	HasCache() bool

	// GetCache returns the driver internal cache.
	//
	// WARNING: Please, do not use this method unless you know what you are doing:
	// YOU COULD BREAK YOUR DRIVER.
	GetCache() *DriverCache

	// SetCache replace the driver internal cache by the given one.
	//
	// WARNING: Please, do not use this method unless you know what you are doing:
	// YOU COULD BREAK YOUR DRIVER.
	SetCache(cache *DriverCache)

	// HasLogger returns if the driver has a logger.
	HasLogger() bool

	// Logger returns the driver logger.
	//
	// WARNING: Please, do not use this method unless you know what you are doing.
	Logger() Logger

	// HasObserver returns if the driver has an observer.
	HasObserver() bool

	// Observer returns the driver observer.
	//
	// WARNING: Please, do not use this method unless you know what you are doing.
	Observer() Observer

	// Entropy returns an entropy source, used for primary key generation (if required).
	//
	// WARNING: Please, do not use this method unless you know what you are doing.
	Entropy() io.Reader
}

Driver is a high level abstraction of a database connection or a transaction.

type DriverCache added in v0.8.0

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

DriverCache is driver cache used to store Schema and Schemaless information.

func NewDriverCache added in v0.8.0

func NewDriverCache() *DriverCache

NewDriverCache returns new schema cache instance.

func (*DriverCache) GetSchema added in v0.8.0

func (c *DriverCache) GetSchema(model Model) *Schema

GetSchema returns the schema associated to given model from cache. If it does not exists, it returns nil.

func (*DriverCache) GetSchemaless added in v0.8.0

func (c *DriverCache) GetSchemaless(value reflect.Type) *Schemaless

GetSchemaless returns the schemaless associated to type from cache. If it does not exists, it returns nil.

func (*DriverCache) SetSchema added in v0.8.0

func (c *DriverCache) SetSchema(schema *Schema)

SetSchema caches the given schema.

func (*DriverCache) SetSchemaless added in v0.8.0

func (c *DriverCache) SetSchemaless(schemaless *Schemaless)

SetSchemaless caches the given schemaless.

type FKType

type FKType uint8

FKType define a foreign key type.

func (FKType) IsCompatible

func (val FKType) IsCompatible(key PKType) bool

IsCompatible returns if given primary key is compatible with foreign key.

func (FKType) IsOptional

func (val FKType) IsOptional() bool

IsOptional returns if foreign key has an optional type.

func (FKType) String

func (val FKType) String() string

String returns a human readable version of current instance.

type Field

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

Field defines the column name, field and options from model.

For example: If we have an User, we could have this primary key defined in User's schema.

Field {
    ModelName:  User,
    TableName:  users,
    FieldName:  AvatarID,
    ColumnName: avatar_id,
    ColumnPath: users.avatar_id,
}

func NewField

func NewField(driver Driver, schema *Schema, model Model, name string, args ...ModelOpts) (*Field, error)

NewField creates a new field using given model and name.

func (Field) ColumnName

func (field Field) ColumnName() string

ColumnName returns the field's column name.

func (Field) ColumnPath

func (field Field) ColumnPath() string

ColumnPath returns the field's full column path.

func (Field) FieldIndex

func (field Field) FieldIndex() []int

FieldIndex define the struct field index used for this field.

func (Field) FieldName

func (field Field) FieldName() string

FieldName define the struct field name used for this field.

func (Field) ForeignKey

func (field Field) ForeignKey() string

ForeignKey returns the field foreign key's table name.

func (Field) HasDefault

func (field Field) HasDefault() bool

HasDefault returns if the field has a default value and should be in returning statement.

func (Field) HasRelation

func (field Field) HasRelation() bool

HasRelation returns if the field has a explicit relation.

func (Field) HasULID

func (field Field) HasULID() bool

HasULID returns if the field has a ulid type for it's primary key.

func (Field) HasUUIDV1

func (field Field) HasUUIDV1() bool

HasUUIDV1 returns if the field has a uuid v1 type for it's primary key.

func (Field) HasUUIDV4

func (field Field) HasUUIDV4() bool

HasUUIDV4 returns if the field has a uuid v4 type for it's primary key.

func (Field) IsAssociation

func (field Field) IsAssociation() bool

IsAssociation returns if the field is an association.

func (Field) IsAssociationType

func (field Field) IsAssociationType(kind AssociationType) bool

IsAssociationType returns if the field has given association type.

func (Field) IsCreatedKey

func (field Field) IsCreatedKey() bool

IsCreatedKey returns if the field is a created key.

func (Field) IsDeletedKey

func (field Field) IsDeletedKey() bool

IsDeletedKey returns if the field is a deleted key.

func (Field) IsExcluded

func (field Field) IsExcluded() bool

IsExcluded returns if the field excluded. (anonymous, private...)

func (Field) IsForeignKey

func (field Field) IsForeignKey() bool

IsForeignKey returns if the field is a foreign key.

func (Field) IsPrimaryKey

func (field Field) IsPrimaryKey() bool

IsPrimaryKey returns if the field is a primary key.

func (Field) IsUpdatedKey

func (field Field) IsUpdatedKey() bool

IsUpdatedKey returns if the field is a updated key.

func (Field) ModelName

func (field Field) ModelName() string

ModelName define the model name of this field.

func (Field) RelationName

func (field Field) RelationName() string

RelationName returns the field relation name.

func (Field) String

func (field Field) String() string

String returns a human readable version of current instance.

func (Field) TableName

func (field Field) TableName() string

TableName returns the model name's table name of this field.

func (Field) Type

func (field Field) Type() reflect.Type

Type returns the reflect's type of the field.

type ForeignKey

type ForeignKey struct {
	Field
	// contains filtered or unexported fields
}

ForeignKey is a composite object that define a foreign key for a model. This foreign key will be used later for Preload...

For example: If we have an User, we could have this primary key defined in User's schema.

ForeignKey {
    ModelName:  User,
    TableName:  users,
    FieldName:  AvatarID,
    ColumnName: avatar_id,
    ColumnPath: users.avatar_id,
    Reference:  avatars,
    Type:       int64,
}

func NewForeignKey

func NewForeignKey(field *Field) (*ForeignKey, error)

NewForeignKey creates a foreign key from a field instance.

func (ForeignKey) Reference

func (key ForeignKey) Reference() string

Reference returns the foreign key's table name.

func (ForeignKey) Type

func (key ForeignKey) Type() FKType

Type returns the foreign key's type.

type Logger

type Logger interface {
	// Log push what query was executed and its duration.
	Log(ctx context.Context, query string, duration time.Duration)
}

Logger is an observer that collect queries executed in makroud.

type Mapper

type Mapper map[string]interface{}

Mapper will be used to mutate a Model with row values.

type Model

type Model interface {
	TableName() string
}

Model represents a database table.

type ModelOpts

type ModelOpts struct {
	PrimaryKey string
	CreatedKey string
	UpdatedKey string
	DeletedKey string
}

ModelOpts define a configuration for model.

type Node added in v0.7.0

type Node interface {

	// Exec executes a statement using given arguments. The query shouldn't return rows.
	Exec(query string, args ...interface{}) (sql.Result, error)
	// ExecContext executes a statement using given arguments. The query shouldn't return rows.
	ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
	// Query executes a statement that returns rows using given arguments.
	Query(query string, args ...interface{}) (*sql.Rows, error)
	// QueryContext executes a statement that returns rows using given arguments.
	QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
	// QueryRow executes a statement that returns at most one row using given arguments.
	QueryRow(query string, args ...interface{}) *sql.Row
	// QueryRowContext executes a statement that returns at most one row using given arguments.
	QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row
	// Prepare creates a prepared statement for later queries or executions.
	// Multiple queries or executions may be run concurrently from the returned statement.
	Prepare(query string) (*sql.Stmt, error)
	// PrepareContext creates a prepared statement for later queries or executions.
	// Multiple queries or executions may be run concurrently from the returned statement.
	PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)

	// DriverName returns the driver name used by this connector.
	DriverName() string
	// Ping verifies that the underlying connection is healthy.
	Ping() error
	// PingContext verifies that the underlying connection is healthy.
	PingContext(ctx context.Context) error
	// Close closes the underlying connection.
	Close() error
	// SetConnMaxLifetime sets the maximum amount of time a connection may be reused.
	// Expired connections may be closed lazily before reuse.
	SetConnMaxLifetime(duration time.Duration)
	// SetMaxIdleConns sets the maximum number of connections in the idle connection pool.
	SetMaxIdleConns(number int)
	// SetMaxOpenConns sets the maximum number of open connections to the database.
	SetMaxOpenConns(number int)
	// EnableSavepoint activate PostgreSQL savepoints for nested transactions.
	EnableSavepoint(enabled bool)
	// Stats returns database statistics.
	Stats() sql.DBStats

	// Begin starts a new transaction.
	//
	// The default isolation level is dependent on the driver.
	Begin() (Node, error)
	// BeginTx begins a new transaction.
	//
	// 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.
	// Commit will return an error if the context provided to BeginTx is canceled.
	//
	// The provided TxOptions is optional and may be nil if defaults should be used.
	// If a non-default isolation level is used that the driver doesn't support, an error will be returned.
	BeginTx(ctx context.Context, opts *sql.TxOptions) (Node, error)
	// Rollback rollbacks the associated transaction.
	Rollback() error
	// Commit commits the associated transaction.
	Commit() error

	// Tx returns the underlying transaction.
	Tx() *sql.Tx
	// DB returns the underlying connection.
	DB() *sql.DB
}

Node is a components that allows to seamlessly create nested transactions and to avoid thinking about whether or not a function is called within a transaction. With this, you can easily create reusable and composable functions that can be called within or out of transactions and that can create transactions themselves.

func Connect added in v0.7.0

func Connect(driver string, dsn string) (Node, error)

Connect connects to a database and verifies connection with a ping.

type Observer added in v0.8.0

type Observer interface {
	// OnClose
	OnClose(err error, flags map[string]string)
	// OnRollback
	OnRollback(err error, flags map[string]string)
}

Observer is a collector that gathers various runtime error.

type Option

type Option func(*ClientOptions) error

Option is used to define Client configuration.

func ApplicationName added in v0.8.0

func ApplicationName(name string) Option

ApplicationName will configure the Client to use given application name.

func Cache

func Cache(enabled bool) Option

Cache will configure if the Client should use a cache.

func ConnectTimeout added in v0.8.0

func ConnectTimeout(timeout int) Option

ConnectTimeout will configure the Client to wait this maximum number of seconds to obtain a connection. Zero or not specified means wait indefinitely.

func Database

func Database(dbname string) Option

Database will configure the Client to use the given database name.

func DisableSSL

func DisableSSL() Option

DisableSSL will configure the Client to disable SSL mode.

func EnableSSL

func EnableSSL() Option

EnableSSL will configure the Client to enable SSL mode.

func EnableSavepoint

func EnableSavepoint() Option

EnableSavepoint will enable the SAVEPOINT postgresql feature.

func Host

func Host(host string) Option

Host will configure the Client to use the given server host.

func MaxIdleConnections

func MaxIdleConnections(maximum int) Option

MaxIdleConnections will configure the Client to keep this maximum number of idle connections in the connection pool.

func MaxOpenConnections

func MaxOpenConnections(maximum int) Option

MaxOpenConnections will configure the Client to use this maximum number of open connections to the database.

func Password

func Password(password string) Option

Password will configure the Client to use the given username.

func Port

func Port(port int) Option

Port will configure the Client to use the given server port.

func SSLMode

func SSLMode(mode string) Option

SSLMode will configure the Client with given SSL mode.

func Timezone

func Timezone(timezone string) Option

Timezone will configure the Client to use given timezone.

func User

func User(user string) Option

User will configure the Client to use the given username.

func WithEntropy added in v0.8.0

func WithEntropy(entropy io.Reader) Option

WithEntropy will attach a custom entropy source on Client.

func WithLogger

func WithLogger(logger Logger) Option

WithLogger will attach a logger on Client.

func WithNode added in v0.8.0

func WithNode(node Node) Option

WithNode will attach a custom node connector on Client. If you use this option, EnableSavepoint, MaxOpenConnections and MaxIdleConnections will be ignored.

func WithObserver added in v0.8.0

func WithObserver(observer Observer) Option

WithObserver will attach an observer on Client.

type PKType

type PKType uint8

PKType define a primary key type.

func (PKType) IsCompatible

func (val PKType) IsCompatible(key FKType) bool

IsCompatible returns if given foreign key is compatible with primary key.

func (PKType) String

func (val PKType) String() string

type PreloadHandler

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

PreloadHandler defines what resources should be preloaded.

func WithPreloadCallback

func WithPreloadCallback(field string, callback func(query builder.Select) builder.Select) PreloadHandler

WithPreloadCallback returns a handler that preload a field with conditions.

func WithPreloadField

func WithPreloadField(field string) PreloadHandler

WithPreloadField returns a handler that preload a field.

func WithUnscopedPreload

func WithUnscopedPreload(handler PreloadHandler) PreloadHandler

WithUnscopedPreload unscopes given preload handler.

type PrimaryKey

type PrimaryKey struct {
	Field
	// contains filtered or unexported fields
}

PrimaryKey is a composite object that define a primary key for a model.

For example: If we have an User, we could have this primary key defined in User's schema.

PrimaryKey {
    ModelName: User,
    TableName: users,
    FieldName: ID,
    ColumnName: id,
    ColumnPath: users.id,
    Type: int64,
    Default: db,
}

func NewPrimaryKey

func NewPrimaryKey(field *Field) (*PrimaryKey, error)

NewPrimaryKey creates a primary key from a field instance.

func (PrimaryKey) Default

func (key PrimaryKey) Default() PrimaryKeyDefault

Default returns the primary key's default mechanism.

func (PrimaryKey) Type

func (key PrimaryKey) Type() PKType

Type returns the primary key's type.

func (PrimaryKey) Value

func (key PrimaryKey) Value(model Model) (interface{}, error)

Value returns the primary key's value, or an error if undefined.

func (PrimaryKey) ValueOpt

func (key PrimaryKey) ValueOpt(model Model) (interface{}, bool)

ValueOpt may returns the primary key's value, if defined.

type PrimaryKeyDefault

type PrimaryKeyDefault uint8

PrimaryKeyDefault define how primary key value is generated.

func (PrimaryKeyDefault) String

func (e PrimaryKeyDefault) String() string

type Query

type Query struct {
	Raw   string
	Query string
	Args  []interface{}
}

Query is a query generated by loukoum and makroud.

func NewQuery

func NewQuery(builder lkb.Builder) Query

NewQuery creates a new Query instance from given loukoum builder.

func NewRawQuery

func NewRawQuery(raw string) Query

NewRawQuery creates a new Query instance from given query.

func (Query) String

func (q Query) String() string

String returns query statement.

type Reference

type Reference struct {
	Field
	// contains filtered or unexported fields
}

Reference defines a model relationship.

func NewReference

func NewReference(driver Driver, local *Schema, field *Field) (*Reference, error)

NewReference creates a reference from a field instance.

func (Reference) IsLocal

func (reference Reference) IsLocal() bool

IsLocal returns if reference is local from the model, or from another model (remote).

func (Reference) Local

func (reference Reference) Local() ReferenceObject

Local returns the local model.

func (Reference) Remote

func (reference Reference) Remote() ReferenceObject

Remote returns the remote model.

func (Reference) String

func (reference Reference) String() string

String returns a human readable version of current instance.

type ReferenceObject

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

ReferenceObject defines a model used by Reference.

func (ReferenceObject) ColumnName

func (object ReferenceObject) ColumnName() string

ColumnName returns the column name of this reference.

func (ReferenceObject) ColumnPath

func (object ReferenceObject) ColumnPath() string

ColumnPath returns the full column path of this reference.

func (ReferenceObject) Columns

func (object ReferenceObject) Columns() []string

Columns returns this reference columns.

func (ReferenceObject) DeletedKeyName

func (object ReferenceObject) DeletedKeyName() string

DeletedKeyName returns reference schema deleted key column name.

func (ReferenceObject) DeletedKeyPath

func (object ReferenceObject) DeletedKeyPath() string

DeletedKeyPath returns reference schema deleted key column path.

func (ReferenceObject) FieldName

func (object ReferenceObject) FieldName() string

FieldName returns the field name of this reference.

func (ReferenceObject) ForeignKeyType

func (object ReferenceObject) ForeignKeyType() FKType

ForeignKeyType returns this reference foreign key type.

func (ReferenceObject) HasDeletedKey

func (object ReferenceObject) HasDeletedKey() bool

HasDeletedKey returns if an deleted key is defined for this reference.

func (ReferenceObject) IsForeignKey

func (object ReferenceObject) IsForeignKey() bool

IsForeignKey returns if this reference is a foreign key.

func (ReferenceObject) IsPrimaryKey

func (object ReferenceObject) IsPrimaryKey() bool

IsPrimaryKey returns if this reference is a primary key.

func (ReferenceObject) Model

func (object ReferenceObject) Model() Model

Model returns the reference model.

func (ReferenceObject) ModelName

func (object ReferenceObject) ModelName() string

ModelName returns the model name of this reference.

func (ReferenceObject) PrimaryKeyType

func (object ReferenceObject) PrimaryKeyType() PKType

PrimaryKeyType returns this reference primary key type.

func (ReferenceObject) Schema

func (object ReferenceObject) Schema() *Schema

Schema returns the reference schema.

func (ReferenceObject) TableName

func (object ReferenceObject) TableName() string

TableName returns the table name of this reference.

type Row

type Row interface {
	// Write copies the columns in the current row into the given map.
	// Use this for debugging or analysis if the results might not be under your control.
	// Please do not use this as a primary interface!
	Write(dest map[string]interface{}) error
	// Columns returns the column names.
	Columns() ([]string, error)
	// Scan copies the columns in the current row into the values pointed at by dest.
	// The number of values in dest must be the same as the number of columns in Rows.
	Scan(dest ...interface{}) error
}

A Row is a simple row.

type Rows

type Rows interface {
	// Next prepares the next result row for reading with the MapScan method.
	// It returns true on success, or false if there is no next result row or an error
	// happened while preparing it.
	// Err should be consulted to distinguish between the two cases.
	// Every call to MapScan, even the first one, must be preceded by a call to Next.
	Next() bool
	// Close closes the Rows, preventing further enumeration/iteration.
	// If Next is called and returns false and there are no further result sets, the Rows are closed automatically
	// and it will suffice to check the result of Err.
	Close() error
	// Err returns the error, if any, that was encountered during iteration.
	// Err may be called after an explicit or implicit Close.
	Err() error
	// Write copies the columns in the current row into the given map.
	// Use this for debugging or analysis if the results might not be under your control.
	// Please do not use this as a primary interface!
	Write(dest map[string]interface{}) error
	// Columns returns the column names.
	Columns() ([]string, error)
	// Scan copies the columns in the current row into the values pointed at by dest.
	// The number of values in dest must be the same as the number of columns in Rows.
	Scan(dest ...interface{}) error
}

A Rows is an iteratee of a list of records.

type Schema

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

Schema is a model schema.

func GetSchema

func GetSchema(driver Driver, model Model) (*Schema, error)

GetSchema returns the schema from given model. If the schema does not exists, it returns an error.

func (Schema) ColumnPaths

func (schema Schema) ColumnPaths() Columns

ColumnPaths returns schema column with table prefix.

func (Schema) Columns

func (schema Schema) Columns() Columns

Columns returns schema columns without table prefix.

func (Schema) CreatedKeyName

func (schema Schema) CreatedKeyName() string

CreatedKeyName returns schema created key column name.

func (Schema) CreatedKeyPath

func (schema Schema) CreatedKeyPath() string

CreatedKeyPath returns schema created key column path.

func (Schema) DeletedKeyName

func (schema Schema) DeletedKeyName() string

DeletedKeyName returns schema deleted key column name.

func (Schema) DeletedKeyPath

func (schema Schema) DeletedKeyPath() string

DeletedKeyPath returns schema deleted key column path.

func (Schema) HasColumn

func (schema Schema) HasColumn(column string) bool

HasColumn returns if a schema has a column or not.

func (Schema) HasCreatedKey

func (schema Schema) HasCreatedKey() bool

HasCreatedKey returns if a created key is defined for current schema.

func (Schema) HasDeletedKey

func (schema Schema) HasDeletedKey() bool

HasDeletedKey returns if a deleted key is defined for current schema.

func (Schema) HasUpdatedKey

func (schema Schema) HasUpdatedKey() bool

HasUpdatedKey returns if an updated key is defined for current schema.

func (Schema) Model

func (schema Schema) Model() Model

Model returns the schema model.

func (Schema) ModelName

func (schema Schema) ModelName() string

ModelName returns the schema model name.

func (Schema) PrimaryKey

func (schema Schema) PrimaryKey() PrimaryKey

PrimaryKey returns the schema primary key.

func (Schema) PrimaryKeyName added in v0.8.0

func (schema Schema) PrimaryKeyName() string

PrimaryKeyName returns schema primary key column name.

func (Schema) PrimaryKeyPath added in v0.8.0

func (schema Schema) PrimaryKeyPath() string

PrimaryKeyPath returns schema primary key column path.

func (Schema) ScanRow

func (schema Schema) ScanRow(row Row, model Model) error

ScanRow executes a scan from given row into model.

func (Schema) ScanRows

func (schema Schema) ScanRows(rows Rows, model Model) error

ScanRows executes a scan from current row into model.

func (Schema) TableName

func (schema Schema) TableName() string

TableName returns the schema table name.

func (Schema) UpdatedKeyName

func (schema Schema) UpdatedKeyName() string

UpdatedKeyName returns schema deleted key column name.

func (Schema) UpdatedKeyPath

func (schema Schema) UpdatedKeyPath() string

UpdatedKeyPath returns schema updated key column path.

type Schemaless added in v0.7.0

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

Schemaless is a light version of schema for structs that are not models. It's a simple mapper from column name to struct field, without primary key, associations and so on...

func GetSchemaless added in v0.7.0

func GetSchemaless(driver Driver, value reflect.Type) (*Schemaless, error)

GetSchemaless returns the schema information from given type that are not models. If no information could be gathered, it returns an error.

func (Schemaless) Columns added in v0.7.0

func (schema Schemaless) Columns() Columns

Columns returns schema columns.

func (Schemaless) HasColumn added in v0.7.0

func (schema Schemaless) HasColumn(column string) bool

HasColumn returns if a schema has a column or not.

func (Schemaless) Key added in v0.7.0

func (schema Schemaless) Key(column string) (SchemalessKey, bool)

Key returns the SchemalessKey instance for given column.

func (Schemaless) Name added in v0.8.0

func (schema Schemaless) Name() string

Name returns the type's name of the schema.

func (Schemaless) ScanRow added in v0.7.0

func (schema Schemaless) ScanRow(row Row, val interface{}) error

ScanRow executes a scan from given row into schemaless instance.

func (Schemaless) ScanRows added in v0.7.0

func (schema Schemaless) ScanRows(rows Rows, val interface{}) error

ScanRows executes a scan from current row into schemaless instance.

func (Schemaless) Type added in v0.7.0

func (schema Schemaless) Type() reflect.Type

Type returns the reflect's type of the schema.

type SchemalessKey added in v0.7.0

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

SchemalessKey is a light version of schema field. It contains the column name and the struct field information.

func (SchemalessKey) ColumnName added in v0.7.0

func (key SchemalessKey) ColumnName() string

ColumnName returns the column name for this schemaless key.

func (SchemalessKey) FieldIndex added in v0.7.0

func (key SchemalessKey) FieldIndex() []int

FieldIndex define the struct field index used for this schemaless key.

func (SchemalessKey) FieldName added in v0.7.0

func (key SchemalessKey) FieldName() string

FieldName define the struct field name used for this schemaless key.

type Selector

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

Selector contains a pool of drivers indexed by their name.

func NewSelector

func NewSelector(configurations map[string]*ClientOptions) (*Selector, error)

NewSelector returns a new selector containing a pool of drivers with given configuration.

func NewSelectorWithDriver

func NewSelectorWithDriver(driver Driver) (*Selector, error)

NewSelectorWithDriver returns a new selector containing the given connection.

func (*Selector) Close

func (selector *Selector) Close() error

Close closes all drivers connections.

func (*Selector) Ping

func (selector *Selector) Ping() error

Ping checks if a connection is available.

func (*Selector) RetryAliases

func (selector *Selector) RetryAliases(handler func(Driver) error, aliases ...string) error

RetryAliases is an helper calling Retry with a list of aliases.

func (*Selector) RetryMaster

func (selector *Selector) RetryMaster(handler func(Driver) error) error

RetryMaster is an helper calling RetryAliases with a slave then a master connection.

func (*Selector) Using

func (selector *Selector) Using(alias string) (Driver, error)

Using returns the underlying drivers if it's alias exists.

type SelectorConfigurations

type SelectorConfigurations map[string]*ClientOptions

SelectorConfigurations define a list of configurations for a pool of drivers.

type Statement

type Statement interface {
	// Close closes the statement.
	Close() error
	// Exec executes this named statement using the struct passed.
	Exec(ctx context.Context, args ...interface{}) error
	// QueryRow executes this named statement returning a single row.
	QueryRow(ctx context.Context, args ...interface{}) (Row, error)
	// QueryRows executes this named statement returning a list of rows.
	QueryRows(ctx context.Context, args ...interface{}) (Rows, error)
}

A Statement from prepare.

type Tag

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

Tag is tag defined in a model.

func (Tag) Get

func (tag Tag) Get(key string) string

Get returns value for the given property name.

func (Tag) Name

func (tag Tag) Name() string

Name returns tag name.

func (Tag) Properties

func (tag Tag) Properties() []TagProperty

Properties returns tag properties.

func (Tag) String

func (tag Tag) String() string

String returns a human readable version of current instance.

type TagProperty

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

TagProperty is a struct tag property.

func (TagProperty) Key

func (prop TagProperty) Key() string

Key returns tag property key.

func (TagProperty) String

func (prop TagProperty) String() string

String returns a human readable version of current instance.

func (TagProperty) Value

func (prop TagProperty) Value() string

Value returns tag property value.

type Tags

type Tags []Tag

Tags is a group of tag defined in a model.

func GetTags

func GetTags(field reflect.StructField, args ...TagsAnalyzerOption) Tags

GetTags returns field tags.

func (Tags) Get

func (tags Tags) Get(name string) *Tag

Get returns tag by name.

func (Tags) GetByKey

func (tags Tags) GetByKey(name string, key string) string

GetByKey is a convenient shortcuts to get the value for a given tag key.

func (Tags) HasKey

func (tags Tags) HasKey(name string, key string) bool

HasKey is a convenient shortcuts to check if a key is present.

func (*Tags) Set

func (tags *Tags) Set(name string, property TagProperty)

Set sets the given tag into the slice.

func (Tags) String

func (tags Tags) String() string

String returns a human readable version of current instance.

type TagsAnalyzerOption

type TagsAnalyzerOption func(*TagsAnalyzerOptions)

TagsAnalyzerOption is a functional option to configure TagsAnalyzerOptions.

func NewOnlyColumnTagsAnalyzerOption added in v0.7.0

func NewOnlyColumnTagsAnalyzerOption() TagsAnalyzerOption

NewOnlyColumnTagsAnalyzerOption return a new TagsAnalyzerOption instance to only analyzes tag as column. It removes the support of key:value system to analyzes only values. For instance, "mk:column_name" or "makroud:column_name"

type TagsAnalyzerOptions

type TagsAnalyzerOptions struct {
	// Name defines the default tag name.
	Name string
	// Collector defines what tags should be analyzed.
	Collector []string
	// NameMapper defines how to convert a supported tag to the default one.
	NameMapper map[string]string
	// KeyMapper defines how to convert a tag key tag to another one (aliasing).
	KeyMapper map[string]string
}

TagsAnalyzerOptions defines the tags analyzer behavior.

type TxOptions added in v0.8.0

type TxOptions = sql.TxOptions

TxOptions is an alias for sql.TxOptions to reduce import leak. This alias allows the use of makroud.TxOptions and sql.TxOptions seamlessly.

Directories

Path Synopsis
Package snaker provides methods to convert CamelCase names to snake_case and back.
Package snaker provides methods to convert CamelCase names to snake_case and back.

Jump to

Keyboard shortcuts

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