// Package database provides database functionality package database import ( "errors" "fmt" ) // Common database error types var ( // ErrInvalidConfig indicates missing or invalid database configuration ErrInvalidConfig = errors.New("invalid database configuration") // ErrConnectionFailed indicates failure to connect to the database ErrConnectionFailed = errors.New("failed to connect to database") // ErrQueryFailed indicates a query execution failure ErrQueryFailed = errors.New("query execution failed") // ErrTransactionFailed indicates a transaction operation failure ErrTransactionFailed = errors.New("transaction operation failed") // ErrMigrationFailed indicates a database migration failure ErrMigrationFailed = errors.New("database migration failed") // ErrPragmaFailed indicates failure to set database pragma ErrPragmaFailed = errors.New("failed to set database pragma") // ErrInvalidPath indicates an invalid database file path ErrInvalidPath = errors.New("invalid database path") // ErrDatabaseLocked indicates the database is locked ErrDatabaseLocked = errors.New("database is locked") // ErrNoRows indicates no rows were found ErrNoRows = errors.New("no rows found") // ErrDuplicateKey indicates a unique constraint violation ErrDuplicateKey = errors.New("duplicate key violation") ) // Error represents a database-specific error with detailed context type Error struct { // Op is the operation that failed (e.g., "connect", "query", "migrate") Op string // Query is the SQL query that failed (if applicable) Query string // Err is the underlying error Err error // Details contains additional error context Details string } // Error returns a string representation of the error func (e *Error) Error() string { msg := fmt.Sprintf("database %s failed", e.Op) if e.Query != "" { // Truncate long queries in the error message const maxQueryLen = 100 query := e.Query if len(query) > maxQueryLen { query = query[:maxQueryLen] + "..." } msg = fmt.Sprintf("%s for query %q", msg, query) } if e.Details != "" { msg = fmt.Sprintf("%s: %s", msg, e.Details) } if e.Err != nil { msg = fmt.Sprintf("%s: %v", msg, e.Err) } return msg } // Unwrap returns the underlying error func (e *Error) Unwrap() error { return e.Err } // Is reports whether target matches this error func (e *Error) Is(target error) bool { return errors.Is(e.Err, target) } // NewError creates a new database error func NewError(op string, err error, details string) *Error { return &Error{ Op: op, Err: err, Details: details, } } // NewQueryError creates a new database error with query context func NewQueryError(op string, query string, err error, details string) *Error { return &Error{ Op: op, Query: query, Err: err, Details: details, } } // IsLockError returns true if the error indicates a database lock func IsLockError(err error) bool { var e *Error return errors.As(err, &e) && errors.Is(e.Err, ErrDatabaseLocked) } // IsNotFoundError returns true if the error indicates no rows were found func IsNotFoundError(err error) bool { var e *Error return errors.As(err, &e) && errors.Is(e.Err, ErrNoRows) } // IsDuplicateError returns true if the error indicates a duplicate key func IsDuplicateError(err error) bool { var e *Error return errors.As(err, &e) && errors.Is(e.Err, ErrDuplicateKey) }