package main import ( "database/sql" _ "embed" "fmt" "sync" _ "github.com/ncruces/go-sqlite3/driver" _ "github.com/ncruces/go-sqlite3/embed" ) //go:embed db/schema.sql var schemaSQL string var ( db *sql.DB dbMu sync.Mutex ) func openDatabase() (*sql.DB, error) { dbMu.Lock() defer dbMu.Unlock() if db != nil { return db, nil } conn, err := sql.Open("sqlite3", ":memory:") if err != nil { return nil, fmt.Errorf("open database: %w", err) } if _, err := conn.Exec(schemaSQL); err != nil { conn.Close() return nil, fmt.Errorf("init schema: %w", err) } db = conn return db, nil } func closeDatabase() error { dbMu.Lock() defer dbMu.Unlock() if db == nil { return nil } err := db.Close() db = nil return err } func getDatabase() *sql.DB { dbMu.Lock() defer dbMu.Unlock() return db } func serializeDatabaseBytes() ([]byte, error) { dbMu.Lock() defer dbMu.Unlock() if db == nil { return nil, fmt.Errorf("database not initialized") } var data []byte err := db.QueryRow("SELECT quote(readfile('.'))").Scan(&data) if err != nil { rows, err := db.Query("SELECT name, sql FROM sqlite_master WHERE type='table'") if err != nil { return nil, fmt.Errorf("query schema: %w", err) } defer rows.Close() return exportDatabaseDump() } return data, nil } func exportDatabaseDump() ([]byte, error) { if db == nil { return nil, fmt.Errorf("database not initialized") } var dump string dump += schemaSQL + "\n" tables := []string{ "did_documents", "verification_methods", "credentials", "key_shares", "accounts", "ucan_tokens", "ucan_revocations", "sessions", "services", "grants", "delegations", "sync_checkpoints", } for _, table := range tables { rows, err := db.Query(fmt.Sprintf("SELECT * FROM %s", table)) if err != nil { continue } cols, err := rows.Columns() if err != nil { rows.Close() continue } values := make([]interface{}, len(cols)) valuePtrs := make([]interface{}, len(cols)) for i := range values { valuePtrs[i] = &values[i] } for rows.Next() { if err := rows.Scan(valuePtrs...); err != nil { continue } dump += fmt.Sprintf("-- Row from %s\n", table) } rows.Close() } return []byte(dump), nil } func loadDatabaseFromBytes(data []byte) error { dbMu.Lock() defer dbMu.Unlock() if db != nil { db.Close() } conn, err := sql.Open("sqlite3", ":memory:") if err != nil { return fmt.Errorf("open database: %w", err) } if _, err := conn.Exec(schemaSQL); err != nil { conn.Close() return fmt.Errorf("init schema: %w", err) } db = conn return nil }