101 lines
2.5 KiB
Go
101 lines
2.5 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"fn-registry/functions/infra"
|
|
)
|
|
|
|
// snapshotFromRegistry reads registry.db.functions and inserts one row in
|
|
// function_versions per (function_id, content_hash) tuple with source='index'.
|
|
// Duplicate rows (same hash for same function from the same source) are
|
|
// silently ignored — so this can be re-run after every `fn index` to capture
|
|
// only NEW versions.
|
|
//
|
|
// Returns (inserted_rows, total_seen, error).
|
|
func snapshotFromRegistry(callDB *DB, registryPath string) (int, int, error) {
|
|
rconn, err := infra.SQLiteOpen(registryPath, "")
|
|
if err != nil {
|
|
return 0, 0, fmt.Errorf("open registry: %w", err)
|
|
}
|
|
defer rconn.Close()
|
|
|
|
rows, err := rconn.Query("SELECT id, content_hash, version FROM functions WHERE content_hash != ''")
|
|
if err != nil {
|
|
return 0, 0, fmt.Errorf("query functions: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
now := time.Now().UTC().Unix()
|
|
tx, err := callDB.conn.Begin()
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
stmt, err := tx.Prepare(`INSERT OR IGNORE INTO function_versions
|
|
(function_id, content_hash, version, snapped_at, source, lines_added, lines_removed)
|
|
VALUES (?, ?, ?, ?, 'index', 0, 0)`)
|
|
if err != nil {
|
|
_ = tx.Rollback()
|
|
return 0, 0, err
|
|
}
|
|
defer stmt.Close()
|
|
|
|
inserted, seen := 0, 0
|
|
for rows.Next() {
|
|
var id, hash, version string
|
|
if err := rows.Scan(&id, &hash, &version); err != nil {
|
|
_ = tx.Rollback()
|
|
return 0, 0, err
|
|
}
|
|
seen++
|
|
res, err := stmt.Exec(id, hash, version, now)
|
|
if err != nil {
|
|
_ = tx.Rollback()
|
|
return 0, 0, err
|
|
}
|
|
if n, _ := res.RowsAffected(); n > 0 {
|
|
inserted++
|
|
}
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
_ = tx.Rollback()
|
|
return 0, 0, err
|
|
}
|
|
if err := tx.Commit(); err != nil {
|
|
return 0, 0, err
|
|
}
|
|
return inserted, seen, nil
|
|
}
|
|
|
|
// versionsSummary returns aggregate stats per function_id from function_versions.
|
|
func (d *DB) versionsSummary(limit int) ([]funcVersionSummary, error) {
|
|
rows, err := d.conn.Query(`
|
|
SELECT function_id,
|
|
COUNT(DISTINCT content_hash) AS versions,
|
|
MAX(snapped_at) AS last_snapped_at
|
|
FROM function_versions
|
|
GROUP BY function_id
|
|
ORDER BY versions DESC, last_snapped_at DESC
|
|
LIMIT ?`, limit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var out []funcVersionSummary
|
|
for rows.Next() {
|
|
var s funcVersionSummary
|
|
if err := rows.Scan(&s.FunctionID, &s.Versions, &s.LastSnappedAt); err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, s)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
type funcVersionSummary struct {
|
|
FunctionID string
|
|
Versions int64
|
|
LastSnappedAt int64
|
|
}
|