func (p *PostgresFilesystem) Open(name string) (File, error) { tx, err := p.db.Begin() if err != nil { return nil, err } var f pgFile err = tx.QueryRow("SELECT file_id, size, type, digest, created_at FROM files WHERE name = $1", name).Scan(&f.id, &f.size, &f.typ, &f.etag, &f.mtime) if err != nil { tx.Rollback() if err == sql.ErrNoRows { err = ErrNotFound } return nil, err } lo, err := pq.NewLargeObjects(tx) if err != nil { tx.Rollback() return nil, err } f.LargeObject, err = lo.Open(f.id, pq.LargeObjectModeRead) if err != nil { tx.Rollback() return nil, err } f.tx = tx return &f, nil }
func (p *PostgresFilesystem) Put(name string, r io.Reader, typ string) error { tx, err := p.db.Begin() if err != nil { return err } var id oid.Oid create: err = tx.QueryRow("INSERT INTO files (name, type) VALUES ($1, $2) RETURNING file_id", name, typ).Scan(&id) if e, ok := err.(*pq.Error); ok && e.Code.Name() == "unique_violation" { tx.Rollback() tx, err = p.db.Begin() if err != nil { return err } // file exists, delete it first _, err = tx.Exec("DELETE FROM files WHERE name = $1", name) if err != nil { tx.Rollback() return err } goto create } if err != nil { tx.Rollback() return err } lo, err := pq.NewLargeObjects(tx) if err != nil { tx.Rollback() return err } obj, err := lo.Open(id, pq.LargeObjectModeWrite) if err != nil { tx.Rollback() return err } h := sha512.New() size, err := io.Copy(obj, io.TeeReader(r, h)) if err != nil { tx.Rollback() return err } digest := hex.EncodeToString(h.Sum(nil)) _, err = tx.Exec("UPDATE files SET size = $2, digest = $3 WHERE file_id = $1", id, size, digest) if err != nil { tx.Rollback() return err } return tx.Commit() }