MCPcopy Create free account
hub / github.com/Facets-cloud/flow / migrateTasksSessionInvariant

Function migrateTasksSessionInvariant

internal/flowdb/db.go:523–655  ·  view source on GitHub ↗

migrateTasksSessionInvariant rebuilds the tasks table to enforce `CHECK (status = 'backlog' OR session_id IS NOT NULL)`. SQLite does not support adding a CHECK constraint to an existing table via ALTER TABLE, so the documented procedure (CREATE new, copy, DROP old, RENAME) is used. Idempotent: probe

(db *sql.DB)

Source from the content-addressed store, hash-verified

521 `UPDATE tasks SET session_id=NULL, session_started=NULL, status='backlog', updated_at=? WHERE slug=?`,
522 now, l.slug,
523 ); err != nil {
524 return fmt.Errorf("demote duplicate %s: %w", l.slug, err)
525 }
526 }
527 }
528 }
529
530 if _, err := db.Exec(
531 `CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_session_id ON tasks(session_id) WHERE session_id IS NOT NULL`,
532 ); err != nil {
533 return fmt.Errorf("create unique index: %w", err)
534 }
535 return nil
536}
537
538// migrateTasksSessionInvariant rebuilds the tasks table to enforce
539// `CHECK (status = 'backlog' OR session_id IS NOT NULL)`. SQLite does
540// not support adding a CHECK constraint to an existing table via
541// ALTER TABLE, so the documented procedure (CREATE new, copy, DROP
542// old, RENAME) is used. Idempotent: probes sqlite_master for the
543// CHECK substring before doing any work, so subsequent calls are
544// no-ops. Existing rows that violate the new constraint are demoted
545// to backlog first (with a stderr summary), since there is no way to
546// invent a session_id for them after the fact.
547func migrateTasksSessionInvariant(db *sql.DB) error {
548 var ddl string
549 if err := db.QueryRow(
550 `SELECT sql FROM sqlite_master WHERE type='table' AND name='tasks'`,
551 ).Scan(&ddl); err != nil {
552 return fmt.Errorf("inspect tasks ddl: %w", err)
553 }
554 if strings.Contains(ddl, "session_id IS NOT NULL") {
555 return nil
556 }
557
558 type violator struct{ slug, prevStatus string }
559 var vs []violator
560 rows, err := db.Query(
561 `SELECT slug, status FROM tasks WHERE status != 'backlog' AND session_id IS NULL`,
562 )
563 if err != nil {
564 return fmt.Errorf("scan violators: %w", err)
565 }
566 for rows.Next() {
567 var v violator
568 if err := rows.Scan(&v.slug, &v.prevStatus); err != nil {
569 rows.Close()
570 return err
571 }
572 vs = append(vs, v)
573 }
574 if err := rows.Err(); err != nil {
575 rows.Close()
576 return err
577 }
578 rows.Close()
579
580 if len(vs) > 0 {

Callers 1

runMigrationsFunction · 0.85

Calls 1

NowISOFunction · 0.85

Tested by

no test coverage detected