- Add SQLite database connection management with proper error handling - Implement schema for files, chunks, blobs, and snapshots tables - Create repository pattern for each database table - Add transaction support with proper rollback handling - Integrate database module with fx dependency injection - Make index path configurable via VAULTIK_INDEX_PATH env var - Add fatal error handling for database integrity issues - Update DESIGN.md to clarify file_chunks vs chunk_files distinction - Remove FinalHash from BlobInfo (blobs are content-addressable) - Add file metadata support (mtime, ctime, mode, uid, gid, symlinks)
115 lines
2.5 KiB
Go
115 lines
2.5 KiB
Go
package database
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
|
|
_ "github.com/mattn/go-sqlite3"
|
|
)
|
|
|
|
type DB struct {
|
|
conn *sql.DB
|
|
}
|
|
|
|
func New(ctx context.Context, path string) (*DB, error) {
|
|
conn, err := sql.Open("sqlite3", path+"?_journal_mode=WAL&_synchronous=NORMAL&_busy_timeout=5000")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("opening database: %w", err)
|
|
}
|
|
|
|
if err := conn.PingContext(ctx); err != nil {
|
|
if closeErr := conn.Close(); closeErr != nil {
|
|
Fatal("failed to close database connection: %v", closeErr)
|
|
}
|
|
return nil, fmt.Errorf("pinging database: %w", err)
|
|
}
|
|
|
|
db := &DB{conn: conn}
|
|
if err := db.createSchema(ctx); err != nil {
|
|
if closeErr := conn.Close(); closeErr != nil {
|
|
Fatal("failed to close database connection: %v", closeErr)
|
|
}
|
|
return nil, fmt.Errorf("creating schema: %w", err)
|
|
}
|
|
|
|
return db, nil
|
|
}
|
|
|
|
func (db *DB) Close() error {
|
|
if err := db.conn.Close(); err != nil {
|
|
Fatal("failed to close database: %v", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (db *DB) Conn() *sql.DB {
|
|
return db.conn
|
|
}
|
|
|
|
func (db *DB) BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error) {
|
|
return db.conn.BeginTx(ctx, opts)
|
|
}
|
|
|
|
func (db *DB) createSchema(ctx context.Context) error {
|
|
schema := `
|
|
CREATE TABLE IF NOT EXISTS files (
|
|
path TEXT PRIMARY KEY,
|
|
mtime INTEGER NOT NULL,
|
|
ctime INTEGER NOT NULL,
|
|
size INTEGER NOT NULL,
|
|
mode INTEGER NOT NULL,
|
|
uid INTEGER NOT NULL,
|
|
gid INTEGER NOT NULL,
|
|
link_target TEXT
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS file_chunks (
|
|
path TEXT NOT NULL,
|
|
idx INTEGER NOT NULL,
|
|
chunk_hash TEXT NOT NULL,
|
|
PRIMARY KEY (path, idx)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS chunks (
|
|
chunk_hash TEXT PRIMARY KEY,
|
|
sha256 TEXT NOT NULL,
|
|
size INTEGER NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS blobs (
|
|
blob_hash TEXT PRIMARY KEY,
|
|
created_ts INTEGER NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS blob_chunks (
|
|
blob_hash TEXT NOT NULL,
|
|
chunk_hash TEXT NOT NULL,
|
|
offset INTEGER NOT NULL,
|
|
length INTEGER NOT NULL,
|
|
PRIMARY KEY (blob_hash, chunk_hash)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS chunk_files (
|
|
chunk_hash TEXT NOT NULL,
|
|
file_path TEXT NOT NULL,
|
|
file_offset INTEGER NOT NULL,
|
|
length INTEGER NOT NULL,
|
|
PRIMARY KEY (chunk_hash, file_path)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS snapshots (
|
|
id TEXT PRIMARY KEY,
|
|
hostname TEXT NOT NULL,
|
|
vaultik_version TEXT NOT NULL,
|
|
created_ts INTEGER NOT NULL,
|
|
file_count INTEGER NOT NULL,
|
|
chunk_count INTEGER NOT NULL,
|
|
blob_count INTEGER NOT NULL
|
|
);
|
|
`
|
|
|
|
_, err := db.conn.ExecContext(ctx, schema)
|
|
return err
|
|
}
|