depot/go/nix/nixstore/nixstore.go

131 lines
2.6 KiB
Go
Raw Normal View History

package nixstore
import (
"database/sql"
"encoding/base64"
"fmt"
"path"
"strings"
"hg.lukegb.com/lukegb/depot/go/nix/nar/narinfo"
_ "github.com/mattn/go-sqlite3"
)
const DefaultStoreDB = "/nix/var/nix/db/db.sqlite"
type DB struct {
db *sql.DB
}
func (d *DB) NARInfo(storePath string) (*narinfo.NarInfo, error) {
stmt, err := d.db.Prepare(`
SELECT
vp.id,
vp.path,
vp.hash,
vp.deriver,
vp.narSize,
vp.sigs,
1 FROM
ValidPaths vp
WHERE 1=1
AND vp.path = ?
`)
if err != nil {
return nil, fmt.Errorf("preparing initial statement: %w", err)
}
defer stmt.Close()
ni := narinfo.NarInfo{}
var storePathID int
var dummy int
var hashStr string
var deriverStr *string
var sigsStr *string
err = stmt.QueryRow(storePath).Scan(
&storePathID,
&ni.StorePath,
&hashStr,
&deriverStr,
&ni.NarSize,
&sigsStr,
&dummy)
if err != nil {
return nil, fmt.Errorf("scanning initial statement: %w", err)
}
ni.NarHash, err = narinfo.HashFromString(hashStr)
if err != nil {
return nil, fmt.Errorf("parsing hash %q: %w", hashStr, err)
}
if deriverStr != nil {
ni.Deriver = path.Base(*deriverStr)
}
if sigsStr != nil {
sigsBits := strings.Fields(*sigsStr)
sigs := make(map[string][]byte)
for _, sigsBit := range sigsBits {
sigsPieces := strings.Split(sigsBit, ":")
if len(sigsPieces) != 2 {
return nil, fmt.Errorf("parsing signature %q: wrong number of : separated pieces (%d)", sigsBit, len(sigsPieces))
}
var err error
sigs[sigsPieces[0]], err = base64.StdEncoding.DecodeString(sigsPieces[1])
if err != nil {
return nil, fmt.Errorf("parsing signature %q: invalid base64: %w", sigsBit, err)
}
}
ni.Sig = sigs
}
referencesStmt, err := d.db.Prepare(`
SELECT
refedvp.path
FROM
Refs r
INNER JOIN
ValidPaths refedvp ON refedvp.id = r.reference
WHERE
r.referrer = ?
ORDER BY 1
`)
if err != nil {
return nil, fmt.Errorf("preparing references statement: %w", err)
}
defer referencesStmt.Close()
referencesRows, err := referencesStmt.Query(storePathID)
if err != nil {
return nil, fmt.Errorf("querying references: %w", err)
}
defer referencesRows.Close()
for referencesRows.Next() {
var refStorePath string
if err := referencesRows.Scan(&refStorePath); err != nil {
return nil, fmt.Errorf("scanning references: %w", err)
}
ni.References = append(ni.References, path.Base(refStorePath))
}
return &ni, nil
}
func (d *DB) Close() error {
return d.db.Close()
}
func Open(dbPath string) (*DB, error) {
sqlDB, err := sql.Open("sqlite3", dbPath)
if err != nil {
return nil, err
}
return &DB{
db: sqlDB,
}, nil
}