package main import ( "context" "database/sql" "flag" "fmt" "log" "os" "time" "github.com/jackc/pgx/v4" _ "modernc.org/sqlite" ) func main() { pgConn := flag.String("pg", "", "postgres connection string (e.g. postgres://user:pass@host:5432/kls?sslmode=verify-full)") out := flag.String("out", "kls.db", "output sqlite file path") flag.Parse() if *pgConn == "" { fmt.Fprintln(os.Stderr, "usage: pg2sqlite -pg [-out kls.db]") os.Exit(1) } ctx := context.Background() // Connect to Postgres pg, err := pgx.Connect(ctx, *pgConn) if err != nil { log.Fatalf("postgres: %v", err) } defer pg.Close(ctx) // Open SQLite os.Remove(*out) lite, err := sql.Open("sqlite", *out) if err != nil { log.Fatalf("sqlite open: %v", err) } defer lite.Close() _, err = lite.ExecContext(ctx, `CREATE TABLE urls ( id TEXT PRIMARY KEY, url TEXT NOT NULL, nurl TEXT NOT NULL, short TEXT NOT NULL UNIQUE, created_at TEXT NOT NULL )`) if err != nil { log.Fatalf("sqlite create table: %v", err) } // Read from Postgres rows, err := pg.Query(ctx, `SELECT id, url, nurl, short, created_at FROM urls ORDER BY created_at`) if err != nil { log.Fatalf("postgres query: %v", err) } defer rows.Close() tx, err := lite.BeginTx(ctx, nil) if err != nil { log.Fatalf("sqlite begin: %v", err) } stmt, err := tx.PrepareContext(ctx, `INSERT INTO urls (id, url, nurl, short, created_at) VALUES (?, ?, ?, ?, ?)`) if err != nil { log.Fatalf("sqlite prepare: %v", err) } defer stmt.Close() var n int for rows.Next() { var id, url, nurl, short string var createdAt time.Time if err := rows.Scan(&id, &url, &nurl, &short, &createdAt); err != nil { log.Fatalf("postgres scan: %v", err) } if _, err := stmt.ExecContext(ctx, id, url, nurl, short, createdAt.Format(time.RFC3339)); err != nil { log.Fatalf("sqlite insert: %v", err) } n++ } if err := rows.Err(); err != nil { log.Fatalf("postgres rows: %v", err) } if err := tx.Commit(); err != nil { log.Fatalf("sqlite commit: %v", err) } log.Printf("migrated %d rows to %s", n, *out) }