repo

package module
v1.1.1 Latest Latest
Warning

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

Go to latest
Published: Jul 5, 2025 License: Apache-2.0 Imports: 9 Imported by: 0

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:

  1. Cond Interface: All model types must implement the Cond interface which defines: - Field mapping - Column definitions - Primary key handling

  2. Request Interface: Pagination and filtering parameters are specified through the Request interface

  3. 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

View Source
const (
	DefaultLimit = 10   // Default number of items per page
	MaxLimit     = 5000 // Maximum allowed items per page
)

Default and maximum limits for pagination

Variables

View Source
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

View Source
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

func Error(in error) error

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

func GetOrder(before bool, order Direction) (isBackSort bool, qOrder Direction, sign Sign)

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

func Parse[T Cond](row interface {
	Scan(dest ...any) error
}, out T,
) (err error)

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 Query

func Query(q string, arg ...any) query

Query creates a new query with the given SQL string and arguments

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 Code added in v1.1.1

type Code int

Code represents types of database constraint violations

const (
	NotSet            Code = iota // No specific violation code set
	DuplicateKeyValue             // Violation of unique constraint (PostgreSQL code 23505)
	NotNull                       // NOT NULL constraint violation (PostgreSQL code 23502)
	ForeignKey                    // Foreign key constraint violation (PostgreSQL code 23503)
)

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 Direction

type Direction int8

Direction represents the sorting direction in SQL queries

const (
	ASC  Direction = iota // Ascending sort order (A-Z, 0-9)
	DESC                  // Descending sort order (Z-A, 9-0)
)

func (Direction) String

func (d Direction) String() string

String returns the SQL syntax for the sort direction

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 FieldName

type FieldName string

FieldName represents a database field/column name

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.

func (*LargeObject) Write added in v1.1.1

func (o *LargeObject) Write(p []byte) (n int, err error)

Write writes data to the large object. Returns the number of bytes written or an error if the write failed.

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

func NewList

func NewList[T Cond](in []T, isCursor bool, lim int, before bool) ListRez[T]

NewList creates a new paginated list result

func NewTypeList added in v1.1.1

func NewTypeList[T Cond, E Cond](in []T, oldList ListRez[E]) ListRez[T]

NewTypeList creates a new typed list from an existing list

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

func (ns *NullJson) Scan(value any) error

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 (~*)
)

func (Operation) String

func (o Operation) String() string

String returns the SQL string representation of the operation. Includes proper spacing for SQL syntax requirements.

type QType

type QType int8

QType represents the type of database query

const (
	QSelect QType = iota // Select query type
	QInsert              // Insert query type
	QUpdate              // Update query type
)

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

func NewRequest

func NewRequest(limit int, order Direction, before bool, fq, q string, sortArray []string) Request

NewRequest creates a new Request with specified parameters

type Sign

type Sign int8

Sign represents comparison operators for cursor-based pagination

func (Sign) Sign

func (i Sign) Sign() string

Sign returns the SQL syntax for the comparison operator

Jump to

Keyboard shortcuts

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