package postgres import ( "context" "database/sql" "encoding/json" "errors" "time" "gardomatic.kleiax.de/internal/storage" "github.com/lib/pq" ) const queryTimeout = 3 * time.Second type scanner interface { Scan(dest ...any) error } func contextWithTimeout() (context.Context, context.CancelFunc) { return context.WithTimeout(context.Background(), queryTimeout) } func jsonValue(value json.RawMessage) json.RawMessage { if len(value) == 0 { return json.RawMessage(`{}`) } return value } func deleteByID(db *sql.DB, query string, args ...any) error { ctx, cancel := contextWithTimeout() defer cancel() result, err := db.ExecContext(ctx, query, args...) if err != nil { return err } rowsAffected, err := result.RowsAffected() if err != nil { return err } if rowsAffected == 0 { return storage.ErrRecordNotFound } return nil } func deleteByGardenID(db *sql.DB, query string, gardenID, id int) error { ctx, cancel := contextWithTimeout() defer cancel() result, err := db.ExecContext(ctx, query, gardenID, id) if err != nil { return err } rowsAffected, err := result.RowsAffected() if err != nil { return err } if rowsAffected == 0 { return storage.ErrRecordNotFound } return nil } func recordError(err error) error { if errors.Is(err, sql.ErrNoRows) { return storage.ErrRecordNotFound } var pqError *pq.Error if errors.As(err, &pqError) && pqError.Code == "23505" { return storage.ErrConflict } if errors.As(err, &pqError) && pqError.Code == "23503" { return storage.ErrConflict } return err } func nullableUserID(id int) any { if id < 1 { return nil } return id }