repo

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Oct 19, 2023 License: MPL-2.0 Imports: 3 Imported by: 0

Documentation

Overview

Package repo specifies the expected interfaces for management of repositories, including a database connections pool which can be used concurrently by several goroutines, how individual connections and transactions may be obtained from it, which repositories exist, and which actions may be performed on them.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Cars

type Cars interface {
	// Conn takes a Conn interface instance, unwraps it as required,
	// and returns a CarsConnQueryer interface which (with access to
	// the implementation-dependent connection object) can run different
	// permitted operations on cars.
	Conn(Conn) CarsConnQueryer

	// Tx takes a Tx interface instance, unwraps it as required,
	// and returns a CarsTxQueryer interface which (with access to the
	// implementation-dependent transaction object) can run different
	// permitted operations on cars.
	Tx(Tx) CarsTxQueryer
}

Cars interface represents an example repository for management of the car instances. A repository interface should provide two methods of Conn and Tx in order to encourage developer to explicitly decide that a connection or a transaction is required for execution of a SQL statement. Each of those two methods will take a Conn/Tx interface which was provided by the repository implementation (from the adapter layer) beforehand. Implementation of these Conn()/Tx() methods may safely unwrap these interfaces and access the underlying structs if needed, hence, the unwrapping is performed just once.

type CarsConnQueryer

type CarsConnQueryer interface {
	CarsQueryer
}

CarsConnQueryer interface lists all operations which may be executed in a Cars repository having an open connection with auto-committed transactions. Those operations which must be executed in a connection (and may not be executed in an ongoing transaction which may keep running other statements after this one) must be listed here, while other operations which do not strictly require an open connection (and may use an open transaction too) must be defined in the embedded CarsQueryer interface. This design allows a unified implementation, while forcing developers to think about the consequences of having one or multiple transactions.

type CarsQueryer

type CarsQueryer interface {
	// UnparkAndMove example operation unparks a car with carID UUID,
	// and moves it to the c destination coordinate. Updated car model
	// and possible errors are returned.
	UnparkAndMove(ctx context.Context, carID uuid.UUID, c model.Coordinate) (*model.Car, error)

	// Park example operation parks the car with carID UUID without
	// changing its current location. It returns the updated can model
	// and possible errors.
	Park(ctx context.Context, carID uuid.UUID) (*model.Car, error)
}

CarsQueryer interface lists common operations which may be executed in a Cars repository having either a connection or transaction at hand. This interface is embedded by both of CarsConnQueryer and CarsTxQueryer in order to avoid redundant implementation.

type CarsTxQueryer

type CarsTxQueryer interface {
	CarsQueryer
}

CarsTxQueryer interface lists all operations which may be executed in a Cars repository having an ongoing transaction. Those operations which must be executed in a transaction (and may not be executed with a connection) must be listed here, while other operations which do not strictly require an open transaction (and can use their own auto-committed transaction too) must be defined in the embedded CarsQueryer interface. This design allows a unified implementation, while forcing developers to think about the consequences of having one or multiple transactions.

type Conn

type Conn interface {
	Queryer

	// Tx begins a new transaction in this connection, calls the handler
	// with the ctx (which was used for beginning the transactions) and
	// the fresh transaction, and commits the transaction ultimately.
	// In case of errors, the transaction will be rolled back and the
	// error will be returned too.
	Tx(ctx context.Context, handler TxHandler) error

	// IsConn method prevents a non-Conn object to mistakenly implement
	// the Conn interface.
	IsConn()
}

Conn represents a database connection. It is unsafe to be used concurrently. A connection may be used in order to execute one or more SQL statements or start transactions one at a time. For statement execution methods, see the Queryer interface.

type ConnHandler

type ConnHandler func(context.Context, Conn) error

ConnHandler is a handler function which takes a context and a database connection which should be used solely from the current goroutine (or by proper synchronization). When it returns, the connection may be released and reused by other routines.

type Pool

type Pool interface {
	// Conn acquires a database connection, passes it into the
	// handler function, and when it returns will release the connection
	// so it may be used by other callers.
	// This method may be blocked (as while as the ctx allows it)
	// until a connection is obtained. That connection will not be
	// used by any other handler concurrently.
	// Returned errors from the handler will be returned by this
	// method after possible wrapping.
	// The ctx which is used for acquisition of a connection is also
	// passed to the handler function.
	Conn(ctx context.Context, handler ConnHandler) error
}

Pool represents a database connection pool. It may be used concurrently from different goroutines.

type Queryer

type Queryer interface {
	// Exec runs SQL statements with given args given ctx context.
	// Number of affected rows and possible errors will be returned.
	// If args is provided, sql will be prepared and args will be passed
	// separately to the DBMS in order to prevent SQL injection.
	// In this case, sql must contain exactly one statement.
	// In absence of args, sql may contain multiple semi-colon separated
	// statements too.
	//
	// Parameters in sql should be numbered like $1, $2, etc. as they
	// are supported by the PostgreSQL wire protocol natively.
	// This should not be confused with ? or @name parameters which are
	// supported by some ORM libraries via query rewriting. Those extra
	// formats may be supported by an implementation, however, users of
	// this interface should solely use the native numbered placeholders
	// in order to stay independent of the adapter/frameworks layers.
	Exec(ctx context.Context, sql string, args ...any) (count int64, err error)

	// Query runs SQL statement with given args given ctx context.
	// The result set is returned as the Rows interface, while errors
	// are returned as the second return value (if any).
	// If args is provided, sql will be prepared and args will be passed
	// separately to the DBMS in order to prevent SQL injection.
	// Nevertheless, sql must contain exactly one statement.
	//
	// Parameters in sql should be numbered like $1, $2, etc. as they
	// are supported by the PostgreSQL wire protocol natively.
	// This should not be confused with ? or @name parameters which are
	// supported by some ORM libraries via query rewriting. Those extra
	// formats may be supported by an implementation, however, users of
	// this interface should solely use the native numbered placeholders
	// in order to stay independent of the adapter/frameworks layers.
	//
	// The Query or Exec may not be called again until the Rows is
	// closed since only one ongoing statement may be used on each
	// connection. If you need to run multiple queries concurrently,
	// either use multiple connections or rewrite the query using
	// the CURSOR concept:
	// https://www.postgresql.org/docs/current/plpgsql-cursors.html
	Query(ctx context.Context, sql string, args ...any) (Rows, error)
}

Queryer interface includes methods for running SQL statements. There are two main types of statements. One category may affect multiple rows, but do not return a result set, like DDL commands or an UPDATE without a RETURNING clause. These statements are executed with the Exec method. Another category of statements (which may or may not modify the database contents) provide a result set, like SELECT or an UPDATE with a RETURNING clause. These queries may be executed with the Query method. This interface is embedded by both of Conn and Tx since they may be used for execution of commands, of course, with distinct isolation levels.

type Rows

type Rows interface {
	// Close closes the result set, allowing the next query to be
	// executed. After calling this method, the Err() method may be
	// called in order to find out about possible errors.
	Close()

	// Err returns any error which was seen during the last operation.
	// It has to be checked after calling the Close() method.
	Err() error

	// Next prepares the next row to be read using the Scan or Values
	// methods. It must be called even before reading the first row.
	// If it returns false, no more rows exists. In this case, the
	// result set is closed automatically and it is not required to
	// call the Close() method. Nevertheless, calling the Close() is
	// harmless and the Err() method must be checked yet.
	Next() bool

	// Scan reads the values of all columns from the current row
	// into the given dest arguments.
	//
	// Scan converts columns read from the database into the following
	// common Go types and special types provided by the sql package:
	//
	//	*string
	//	*[]byte
	//	*int, *int8, *int16, *int32, *int64
	//	*uint, *uint8, *uint16, *uint32, *uint64
	//	*bool
	//	*float32, *float64
	//	*interface{}
	//	*RawBytes
	//	*Rows (cursor value)
	//	any type implementing Scanner (see Scanner docs)
	Scan(dest ...any) error

	// Values is like Scan, but does not need the caller to prepare
	// destination arguments with relevant types. Instead, it uses
	// *interface{} types in order to read all column values.
	// It is recommended to use the Scan in order to eliminate the
	// subsequent type checking codes.
	Values() ([]any, error)
}

Rows represents the result set of an executed query. Before trying to read a row out of the Rows instance, the Next() must be called. As while as the Next() returns true, a row is obtained from the DBMS server (multiple rows may be fetched and cached based on the implementation details). The Scan or Values methods may be used in order to read columns of the current row. Calling the Close() method releases the result set and ignores any remaining unread rows. The Err() method must be called after the Close() in order to obtain possible errors. Before calling the Close, Err may return nil even if there are some errors since they may not be detected necessarily beforehand.

type Tx

type Tx interface {
	Queryer

	// IsTx method prevents a non-Tx object (such as a Conn) to
	// mistakenly implement the Tx interface.
	IsTx()
}

Tx represents a database transaction. It is unsafe to be used concurrently. A transaction may be used in order to execute one or more SQL statements one at a time. For statement execution methods, see the Queryer interface. All statements which are in a single transaction observe the ACID properties. The exact amount of isolation between transactions depends on their types. By default, a READ-COMMITTED transaction is expected from a PostgreSQL DBMS server. For details, read https://www.postgresql.org/docs/current/transaction-iso.html#XACT-READ-COMMITTED

type TxHandler

type TxHandler func(context.Context, Tx) error

TxHandler is a handler function which takes a context and an ongoing transaction. If an error is returned, caller will rollback the transaction and in absence of errors, it will be committed.

Jump to

Keyboard shortcuts

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