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 OpenDB(dbPath string) (*DB, error) { sqlDB, err := sql.Open("sqlite3", dbPath) if err != nil { return nil, err } return &DB{ db: sqlDB, }, nil }