Documentation
¶
Overview ¶
Package repo provides a type-safe database repository implementation with support for:
- Basic CRUD operations
- Cursor-based pagination
- Complex query building
- PostgreSQL-specific features
The package is designed to work with PostgreSQL but maintains compatibility with other SQL databases.
Core Concepts:
Cond Interface: All model types must implement the Cond interface which defines: - Field mapping - Column definitions - Primary key handling
Request Interface: Pagination and filtering parameters are specified through the Request interface
Operations: The package provides type-safe building blocks for constructing complex queries: - FieldName: Column identifiers - Operation: SQL operators (AND, OR, =, <, etc.) - Query: Custom SQL fragments
Error Handling:
The package provides comprehensive error handling with:
- ErrNoRows: When no results are found
- ErrNoRowAffected: When operations affect no rows
- ErrViolate: For constraint violations
- Wrapped database errors
PostgreSQL Features:
- UPSERT (ON CONFLICT)
- Array operations
- Cursor-based pagination
Index ¶
- Constants
- Variables
- func Copy[T Cond](ctx context.Context, conn interface{ ... }, arr []T) error
- func CreateLargeObject(ctx context.Context, conn interface{ ... }, oid uint32) (out uint32, err error)
- func Delete[T Cond](ctx context.Context, conn interface{ ... }, in T, list ...any) (err error)
- func Error(in error) error
- func Get[T Cond](ctx context.Context, conn interface{ ... }, out T, list ...any) error
- func GetOrder(before bool, order Direction) (isBackSort bool, qOrder Direction, sign Sign)
- func Insert[T Cond](ctx context.Context, conn interface{ ... }, in T) error
- func Parse[T Cond](row interface{ ... }, out T) (err error)
- func Query(q string, arg ...any) query
- func UnlinkLargeObject(ctx context.Context, conn interface{ ... }, oid uint32) (err error)
- func Update[T Cond](ctx context.Context, conn interface{ ... }, in T) error
- func Upsert[T Cond](ctx context.Context, conn interface{ ... }, in T) error
- type Code
- type Cond
- type Direction
- type ErrViolate
- type FieldName
- type FieldNameToText
- type FieldValue
- type FieldValueArray
- type JoinRez
- type LargeObject
- func (o *LargeObject) Close() (err error)
- func (o *LargeObject) Read(p []byte) (n int, err error)
- func (o *LargeObject) Seek(offset int64, whence int) (n int64, err error)
- func (o *LargeObject) Tell() (n int64, err error)
- func (o *LargeObject) Truncate(size int64) (err error)
- func (o *LargeObject) Write(p []byte) (n int, err error)
- type LargeObjectMode
- type ListRez
- type NullJson
- type Operation
- type QType
- type Request
- type Sign
Constants ¶
const ( DefaultLimit = 10 // Default number of items per page MaxLimit = 5000 // Maximum allowed items per page )
Default and maximum limits for pagination
Variables ¶
var ( ErrNoRows = errors.New("not found") // No rows found in query ErrNoRowAffected = errors.New("no row affected") // No rows affected by operation ErrUnknown = errors.New("unknown error") // Unspecified database error )
Common database error types
var ( ErrCreateLargeObject = errors.New("failed to create large object") ErrOpenLargeObject = errors.New("failed to open large object") ErrRemoveLargeObject = errors.New("failed to remove large object") ErrWriteLargeObject = errors.New("failed to write to large object") ErrReadLargeObject = errors.New("failed to read from large object") ErrSeekLargeObject = errors.New("failed to seek in large object") ErrTellLargeObject = errors.New("failed to get position in large object") ErrTruncateLargeObject = errors.New("failed to truncate large object") ErrCloseLargeObject = errors.New("failed to close large object") )
Large object operation errors
Functions ¶
func Copy ¶
func Copy[T Cond]( ctx context.Context, conn interface { PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row }, arr []T, ) error
Copy efficiently saves an array of elements to the database using PostgreSQL's COPY command. It provides better performance for bulk inserts compared to individual INSERT statements.
Parameters:
- ctx: Context for cancellation and timeouts
- conn: Database connection supporting PrepareContext and QueryRowContext
- arr: Slice of elements to insert, must implement the Cond interface
Returns:
- error: nil on success, or an error if the operation fails
- ErrNoRowAffected if the input array is empty
Notes:
- For single element arrays, falls back to regular Insert
- Uses COPY FROM STDIN for optimal performance
- Automatically handles column quoting
func CreateLargeObject ¶ added in v1.1.1
func CreateLargeObject( ctx context.Context, conn interface { QueryRowContext(context.Context, string, ...any) *sql.Row }, oid uint32, ) (out uint32, err error)
CreateLargeObject creates a new large object in the database. If oid is zero, the server assigns an unused OID. Returns the OID of the created object or an error.
func Delete ¶
func Delete[T Cond]( ctx context.Context, conn interface { ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error) }, in T, list ...any, ) (err error)
Delete removes an existing entry from the database. Requires all primary key fields to be populated in the input object.
Parameters:
- ctx: Context for cancellation and timeouts
- conn: Database connection supporting ExecContext
- in: Object to delete (must implement Cond interface)
- list: Optional additional WHERE conditions (variadic)
Returns:
- error: nil on success, or:
- ErrNoRowAffected if no rows were deleted
- Database error if operation fails
Notes:
- Uses primary keys from the object to construct WHERE clause
- Supports additional WHERE conditions through variadic parameter
- Verifies that at least one row was affected
func Error ¶ added in v1.1.1
Error wraps and enhances database errors with additional context Handles specific PostgreSQL errors via pq.Error Returns:
- nil for nil input
- ErrNoRows for sql.ErrNoRows
- Enhanced ErrViolate for constraint violations
- Original error for unrecognized error types
func Get ¶
func Get[T Cond]( ctx context.Context, conn interface { QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row }, out T, list ...any, ) error
Get retrieves a single database entry matching the conditions. Requirements:
- At least one field in 'out' must be set (non-nil) to use as a filter
- 'out' must implement the Cond interface
Parameters:
- ctx: Context for cancellation and timeouts
- conn: Database connection supporting QueryRowContext
- out: Output object (must implement Cond) where results will be stored
- list: Optional additional query conditions (variadic)
Returns:
- error: nil on success, sql.ErrNoRows if no match found, or other database error
Notes:
- Uses the non-nil fields in 'out' as WHERE conditions
- Supports additional conditions through the 'list' parameter
- Automatically quotes column names
func GetOrder ¶
GetOrder determines the proper sorting parameters for pagination
Parameters:
- before: bool - Indicates if paginating backwards
- order: Direction - The base sort direction (ASC/DESC)
Returns:
- isBackSort: bool - True if the sort direction should be reversed
- qOrder: Direction - The actual sort direction to use
- sign: Sign - The comparison operator to use for cursor
This handles the complex logic required for bi-directional pagination where we need to invert sort directions when paginating backwards.
func Insert ¶
func Insert[T Cond]( ctx context.Context, conn interface { QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row }, in T, ) error
Insert adds a single entry to the database. Requires at least one non-nil field value in the input object.
Parameters:
- ctx: Context for cancellation and timeouts
- conn: Database connection supporting QueryRowContext
- in: Object to insert (must implement Cond interface)
Returns:
- error: nil on success, or database error if operation fails
Notes:
- Uses RETURNING clause to fetch primary key values after insert
- Only includes non-nil fields in the INSERT statement
- Automatically quotes column names
func Parse ¶ added in v1.1.1
Parse scans database row results into the output object Parameters:
- row: Database row scanner
- out: Destination object implementing Cond interface
Returns:
- error: Wrapped database error if scan fails
func UnlinkLargeObject ¶ added in v1.1.1
func UnlinkLargeObject( ctx context.Context, conn interface { QueryRowContext(context.Context, string, ...any) *sql.Row ExecContext(context.Context, string, ...any) (sql.Result, error) }, oid uint32, ) (err error)
UnlinkLargeObject removes a large object from the database. Returns an error if the operation fails.
func Update ¶
func Update[T Cond]( ctx context.Context, conn interface { ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error) }, in T, ) error
Update modifies an existing database entry based on primary key fields. Requirements:
- At least one non-primary key field must be set (non-nil) for update
- All primary key fields must be set to identify the record
Parameters:
- ctx: Context for cancellation and timeouts
- conn: Database connection supporting ExecContext
- in: Object containing update values (must implement Cond interface)
Returns:
- error: nil on success, or:
- ErrNoRowAffected if no fields to update or no record matched
- Database error if operation fails
Notes:
- Uses primary key fields for WHERE clause
- Only updates non-null, non-primary key fields
- Verifies that exactly one row was affected
func Upsert ¶ added in v1.1.1
func Upsert[T Cond]( ctx context.Context, conn interface { QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row }, in T, ) error
Upsert performs an INSERT or UPDATE operation (if record exists) for a single database entry. Implements PostgreSQL's UPSERT functionality using ON CONFLICT clause.
Requirements:
- At least one non-primary key field must be set (non-nil) for update
- All primary key fields must be set to identify the record
Parameters:
- ctx: Context for cancellation and timeouts
- conn: Database connection supporting QueryRowContext
- in: Object containing data to upsert (must implement Cond interface)
Returns:
- error: nil on success, or:
- ErrNoRowAffected if no fields to update/insert
- Database error if operation fails
Notes:
- Uses primary key fields for conflict detection
- Updates only non-primary key fields on conflict
- Returns the primary key values via RETURNING clause
Types ¶
type Cond ¶
type Cond interface { // FieldByName returns a pointer to the field of the structure by its constant name. // Used for binding fields in database operations. FieldByName(string) any // ValueByName returns the value of the field by its constant name. // Used for reading field values without exposing the field itself. ValueByName(string) any // New creates and returns a new instance of the implementing type. // Used for reflection-based operations where new instances are needed. New() any // Cursor returns a byte representation of the object's position. // Used for pagination to mark the current position in a result set. Cursor() []byte // SetCursor initializes the object from cursor bytes. // Used for pagination to continue from a previously saved position. SetCursor([]byte) // Equal compares the object with another and returns true if they are identical. // Used for determining object equality beyond simple pointer comparison. Equal(any) bool // Cols returns a list of database column names for the object. // primary - includes primary key columns when true // queryType - specifies which columns to include based on query type Cols(primary bool, queryType QType) []string // Primaries returns the names of primary key columns. // Used for operations that need to identify records uniquely. Primaries() []string // Db returns the name of the database table associated with this object. // Used for constructing table-specific queries. Db() string // Error wraps an error with additional context specific to the implementation. // Used for adding domain-specific information to errors. Error(error) error }
Cond is an interface that defines methods for working with database entities. It provides functionality for field access, cursor handling, comparison, and metadata about database representation.
type ErrViolate ¶ added in v1.1.1
type ErrViolate struct { Code // Type of constraint violation // contains filtered or unexported fields }
ErrViolate represents a database constraint violation error Contains detailed information about the violation
func UnwrapErrViolate ¶ added in v1.1.1
func UnwrapErrViolate(in error) (d *ErrViolate, ok bool)
UnwrapErrViolate attempts to extract ErrViolate from wrapped error Returns the ErrViolate and true if found, nil and false otherwise
func (*ErrViolate) Column ¶ added in v1.1.1
func (d *ErrViolate) Column() string
Column returns the name of the column involved in violation
func (*ErrViolate) Constraint ¶ added in v1.1.1
func (d *ErrViolate) Constraint() string
Constraint returns the name of the violated constraint
func (ErrViolate) Error ¶ added in v1.1.1
func (d ErrViolate) Error() string
Error returns formatted error message including constraint and column details
type FieldNameToText ¶
type FieldNameToText string
FieldNameToText represents a field name converted to SQL text
type FieldValue ¶
type FieldValue any
FieldValue represents a value that can be used in SQL conditions
type FieldValueArray ¶
type FieldValueArray []any
FieldValueArray represents an array of values for SQL operations
func ToFieldValueArray ¶
func ToFieldValueArray[ E any, T interface { []E }, ](in T) FieldValueArray
ToFieldValueArray converts a slice of any type to a FieldValueArray. This is useful for creating SQL IN clause arguments. Example:
ToFieldValueArray([]int{1,2,3}) -> FieldValueArray{1,2,3}
type JoinRez ¶ added in v1.1.1
type JoinRez[T []Cond] interface { Set([]T) // Set the joined results Add(JoinRez[T]) // Add more results Get() []T // Get all results Start() []byte // Get start cursor Stop() []byte // Get end cursor After() bool // Check if more results exist after Before() bool // Check if more results exist before }
JoinRez defines an interface for joined query results
type LargeObject ¶ added in v1.1.1
type LargeObject struct {
// contains filtered or unexported fields
}
LargeObject represents a large object stored in the database. It is only valid within the transaction that created it and uses the context it was initialized with for all operations. Implements: io.Writer, io.Reader, io.Seeker, and io.Closer.
func OpenLargeObject ¶ added in v1.1.1
func OpenLargeObject( ctx context.Context, conn interface { QueryRowContext(context.Context, string, ...any) *sql.Row }, oid uint32, mode LargeObjectMode, ) (out *LargeObject, err error)
OpenLargeObject opens an existing large object with the given mode. The provided context will be used for all subsequent operations on the opened object. Returns a LargeObject instance or an error.
func (*LargeObject) Close ¶ added in v1.1.1
func (o *LargeObject) Close() (err error)
Close releases the large object descriptor. Returns an error if the close operation fails.
func (*LargeObject) Read ¶ added in v1.1.1
func (o *LargeObject) Read(p []byte) (n int, err error)
Read reads data from the large object into p. Returns the number of bytes read or io.EOF if the end was reached.
func (*LargeObject) Seek ¶ added in v1.1.1
func (o *LargeObject) Seek(offset int64, whence int) (n int64, err error)
Seek moves the current position pointer in the large object. Returns the new offset from the start or an error.
func (*LargeObject) Tell ¶ added in v1.1.1
func (o *LargeObject) Tell() (n int64, err error)
Tell returns the current read/write position in the large object. Returns the position or an error.
func (*LargeObject) Truncate ¶ added in v1.1.1
func (o *LargeObject) Truncate(size int64) (err error)
Truncate resizes the large object to the specified size. Returns an error if the operation fails.
type LargeObjectMode ¶ added in v1.1.1
type LargeObjectMode int32
LargeObjectMode defines the access mode for opening large objects
const ( LargeObjectModeWrite LargeObjectMode = 0x20000 // Write-only mode LargeObjectModeRead LargeObjectMode = 0x40000 // Read-only mode )
type ListRez ¶
type ListRez[T Cond] interface { Set([]T) // Set the list items Add(ListRez[T]) // Add more items to the list Get() []T // Get the list items Start() []byte // Get start cursor Stop() []byte // Get end cursor After() bool // Check if there are more items after Before() bool // Check if there are more items before }
ListRez defines an interface for paginated list results
func List ¶
func List[T Cond]( ctx context.Context, conn interface { QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error) QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row }, cursor T, req Request, fieldsOrder []string, list ...any, ) (out ListRez[T], err error)
List retrieves a paginated list of items from the database with cursor-based pagination. It supports both forward and backward pagination with configurable ordering.
Parameters:
- ctx: Context for cancellation and timeouts
- conn: Database connection supporting QueryContext and QueryRowContext
- cursor: Template object for the query (implements Cond interface)
- req: Pagination request parameters (limit, order, direction)
- fieldsOrder: Fields to use for ordering (uses primary keys if empty)
- list: Additional query conditions (variadic)
Returns:
- ListRez[T]: Paginated result containing the items and pagination metadata
- error: Database error or ErrNoRows if no results found
Notes:
- Uses cursor-based pagination for efficient large dataset navigation
- Supports both ascending and descending order
- Automatically handles pagination direction (forward/backward)
- Includes +2 extra items to detect if there are more pages
type NullJson ¶
type NullJson struct { // Handler is a callback function that processes the scanned value. // Parameters: // - value: Raw JSON bytes (nil for NULL database values) // - ok: False for NULL values, true for non-NULL values (including empty JSON) // Note: Length check (>3) handles the "null" JSON literal case specifically. Handler func(value []byte, ok bool) }
NullJson provides nullable JSON handling for database operations. It distinguishes between NULL values, empty JSON, and valid JSON content.
func (*NullJson) Scan ¶
Scan implements the sql.Scanner interface to read database values. Supported input types:
- []byte: Raw JSON bytes from the database
- string: JSON string values (converted to []byte)
- nil: NULL database values
Unsupported types will panic with a descriptive error.
The length check (>3) ensures "null" JSON literals are treated as non-NULL but empty values (ok=true but with len=4).
type Operation ¶
type Operation int
Operation represents SQL query operations and logical operators.
const ( END Operation = iota // Terminator (no operation) AND // Logical AND operator NOT // Logical NOT operator OR // Logical OR operator EQ // Equality operator (=) LE // Less than or equal operator (<=) GE // Greater than or equal operator (>=) L // Less than operator (<) G // Greater than operator (>) SB // Start bracket (() EB // End bracket ()) EQA // Case-insensitive pattern match (~*) )
type Request ¶
type Request interface { // Limit returns the maximum number of elements per page Limit() int // Order returns the sorting direction (ASC/DESC) Order() Direction // Before indicates if pagination is going backward Before() bool // Q returns the general search query string Q() string // FQ returns the field-specific search query string FQ() string // Query builds search conditions for specified fields Query([]FieldName) []any // SortedFields returns the list of fields to sort by SortedFields() []string }
Request defines an interface for pagination and search requests
func DefaultRequest ¶
func DefaultRequest() Request
DefaultRequest returns a Request with default values