package repository import ( "context" "encoding/json" "errors" "fmt" "evobgp/internal/store" "github.com/jackc/pgx/v5" ) func (p *Postgres) GetRenderGeneration(ctx context.Context, tenantID string) (int64, error) { var generation int64 err := p.pool.QueryRow(ctx, `SELECT generation FROM tenant_render_generation WHERE tenant_id=$1::uuid`, tenantID).Scan(&generation) if errors.Is(err, pgx.ErrNoRows) { return 0, store.ErrNotFound } if err != nil { return 0, fmt.Errorf("repository: render generation: %w", err) } return generation, nil } func (p *Postgres) ListModuleSnapshotHeaders(ctx context.Context, tenantID string) ([]store.ModuleSnapshotUse, error) { rows, err := p.pool.Query(ctx, ` SELECT module_id::text, snapshot_version, COALESCE(input_hash, '') FROM module_prefix_snapshot WHERE tenant_id = $1::uuid ORDER BY module_id`, tenantID) if err != nil { return nil, fmt.Errorf("repository: list snapshot headers: %w", err) } defer rows.Close() var out []store.ModuleSnapshotUse for rows.Next() { var item store.ModuleSnapshotUse if err := rows.Scan(&item.ModuleID, &item.SnapshotVersion, &item.InputHash); err != nil { return nil, fmt.Errorf("repository: scan snapshot header: %w", err) } out = append(out, item) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("repository: iterate snapshot headers: %w", err) } return out, nil } func (p *Postgres) LatestRenderManifest(ctx context.Context, tenantID string) (*store.RenderManifestRecord, bool, error) { var revID string var gen *int64 var raw []byte err := p.pool.QueryRow(ctx, ` SELECT cr.id::text, m.config_generation, m.manifest_json FROM config_revision cr LEFT JOIN render_revision_manifest m ON m.revision_id = cr.id WHERE cr.tenant_id = $1::uuid ORDER BY cr.created_at DESC, cr.id DESC LIMIT 1`, tenantID).Scan(&revID, &gen, &raw) if errors.Is(err, pgx.ErrNoRows) { return nil, false, nil } if err != nil { return nil, false, fmt.Errorf("repository: latest render manifest: %w", err) } if gen == nil || len(raw) == 0 { return nil, false, nil } var doc struct { Snapshots []store.ModuleSnapshotUse `json:"snapshots"` } if err := json.Unmarshal(raw, &doc); err != nil { return nil, false, fmt.Errorf("repository: decode render manifest: %w", err) } return &store.RenderManifestRecord{ RevisionID: revID, Generation: *gen, Snapshots: doc.Snapshots, }, true, nil } func insertRenderManifestTx(ctx context.Context, tx pgx.Tx, revisionID, tenantID string) error { var gen int64 err := tx.QueryRow(ctx, ` SELECT generation FROM tenant_render_generation WHERE tenant_id = $1::uuid FOR UPDATE`, tenantID).Scan(&gen) if errors.Is(err, pgx.ErrNoRows) { gen = 1 } else if err != nil { return fmt.Errorf("repository: lock render generation: %w", err) } rows, err := tx.Query(ctx, ` SELECT module_id::text, snapshot_version, COALESCE(input_hash, '') FROM module_prefix_snapshot WHERE tenant_id = $1::uuid ORDER BY module_id`, tenantID) if err != nil { return fmt.Errorf("repository: read snapshot versions: %w", err) } defer rows.Close() var snaps []store.ModuleSnapshotUse for rows.Next() { var item store.ModuleSnapshotUse if err := rows.Scan(&item.ModuleID, &item.SnapshotVersion, &item.InputHash); err != nil { return fmt.Errorf("repository: scan snapshot version: %w", err) } snaps = append(snaps, item) } if err := rows.Err(); err != nil { return fmt.Errorf("repository: iterate snapshot versions: %w", err) } body, err := json.Marshal(struct { Snapshots []store.ModuleSnapshotUse `json:"snapshots"` }{Snapshots: snaps}) if err != nil { return fmt.Errorf("repository: encode render manifest: %w", err) } if _, err := tx.Exec(ctx, ` INSERT INTO render_revision_manifest (revision_id, tenant_id, config_generation, manifest_json) VALUES ($1::uuid, $2::uuid, $3, $4::jsonb)`, revisionID, tenantID, gen, body); err != nil { return fmt.Errorf("repository: insert render manifest: %w", err) } return nil }