Skip to content

Commit 876ce31

Browse files
committed
refactor(opencode): remove legacy database wrapper
1 parent c3743b2 commit 876ce31

50 files changed

Lines changed: 500 additions & 1964 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/core/src/database/database.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ export * as Database from "./database"
33
import { EffectDrizzleSqlite } from "@opencode-ai/effect-drizzle-sqlite"
44
import { layer as sqliteLayer } from "#sqlite"
55
import { Context, Effect, Layer } from "effect"
6-
import { Sqlite } from "./sqlite"
76
import { Global } from "../global"
87
import { Flag } from "../flag/flag"
98
import { isAbsolute, join } from "path"
@@ -15,15 +14,13 @@ type DatabaseShape = Effect.Success<typeof makeDatabase>
1514

1615
export interface Interface {
1716
db: DatabaseShape
18-
drizzle: Sqlite.DrizzleClient
1917
}
2018

2119
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/storage/Database") {}
2220

2321
export const layer = Layer.effect(
2422
Service,
2523
Effect.gen(function* () {
26-
const drizzle = yield* Sqlite.Drizzle
2724
const db = yield* makeDatabase
2825

2926
yield* db.run("PRAGMA journal_mode = WAL")
@@ -35,7 +32,7 @@ export const layer = Layer.effect(
3532
yield* Effect.log("Applying database migrations")
3633
yield* DatabaseMigration.apply(db)
3734

38-
return { db, drizzle }
35+
return { db }
3936
}).pipe(Effect.orDie),
4037
)
4138

Lines changed: 26 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
import type { Argv } from "yargs"
22
import { spawn } from "child_process"
3-
import { Database } from "@/storage/db"
4-
import { drizzle } from "drizzle-orm/bun-sqlite"
5-
import { Database as BunDatabase } from "bun:sqlite"
6-
import { UI } from "../ui"
7-
import { cmd } from "./cmd"
8-
import { JsonMigration } from "@/storage/json-migration"
9-
import { EOL } from "os"
10-
import { errorMessage } from "../../util/error"
3+
import { Database } from "@opencode-ai/core/database/database"
4+
import { Effect } from "effect"
5+
import { sql } from "drizzle-orm"
6+
import { effectCmd } from "../effect-cmd"
117

12-
const QueryCommand = cmd({
8+
const QueryCommand = effectCmd({
139
command: "$0 [query]",
1410
describe: "open an interactive sqlite3 shell or run a query",
11+
instance: false,
1512
builder: (yargs: Argv) => {
1613
return yargs
1714
.positional("query", {
@@ -25,96 +22,41 @@ const QueryCommand = cmd({
2522
describe: "Output format",
2623
})
2724
},
28-
handler: async (args: { query?: string; format: string }) => {
25+
handler: Effect.fn("Cli.db.query")(function* (args: { query?: string; format: string }) {
2926
const query = args.query as string | undefined
3027
if (query) {
31-
const db = new BunDatabase(Database.getPath(), { readonly: true })
32-
try {
33-
const result = db.query(query).all() as Record<string, unknown>[]
34-
if (args.format === "json") {
35-
console.log(JSON.stringify(result, null, 2))
36-
} else if (result.length > 0) {
37-
const keys = Object.keys(result[0])
38-
console.log(keys.join("\t"))
39-
for (const row of result) {
40-
console.log(keys.map((k) => row[k]).join("\t"))
41-
}
42-
}
43-
} catch (err) {
44-
UI.error(errorMessage(err))
45-
process.exit(1)
28+
const { db } = yield* Database.Service
29+
const result = yield* db.all<Record<string, unknown>>(sql.raw(query)).pipe(Effect.orDie)
30+
if (args.format === "json") console.log(JSON.stringify(result, null, 2))
31+
else if (result.length > 0) {
32+
const keys = Object.keys(result[0])
33+
console.log(keys.join("\t"))
34+
for (const row of result) console.log(keys.map((key) => row[key]).join("\t"))
4635
}
47-
db.close()
4836
return
4937
}
50-
const child = spawn("sqlite3", [Database.getPath()], {
38+
const child = spawn("sqlite3", [Database.path()], {
5139
stdio: "inherit",
5240
})
53-
await new Promise((resolve) => child.on("close", resolve))
54-
},
41+
yield* Effect.promise(() => new Promise((resolve) => child.on("close", resolve)))
42+
}),
5543
})
5644

57-
const PathCommand = cmd({
45+
const PathCommand = effectCmd({
5846
command: "path",
5947
describe: "print the database path",
60-
handler: () => {
61-
console.log(Database.getPath())
62-
},
63-
})
64-
65-
const MigrateCommand = cmd({
66-
command: "migrate",
67-
describe: "migrate JSON data to SQLite (merges with existing data)",
68-
handler: async () => {
69-
const sqlite = new BunDatabase(Database.getPath())
70-
const tty = process.stderr.isTTY
71-
const width = 36
72-
const orange = "\x1b[38;5;214m"
73-
const muted = "\x1b[0;2m"
74-
const reset = "\x1b[0m"
75-
let last = -1
76-
if (tty) process.stderr.write("\x1b[?25l")
77-
try {
78-
const stats = await JsonMigration.run(drizzle({ client: sqlite }), {
79-
progress: (event) => {
80-
const percent = Math.floor((event.current / event.total) * 100)
81-
if (percent === last) return
82-
last = percent
83-
if (tty) {
84-
const fill = Math.round((percent / 100) * width)
85-
const bar = `${"■".repeat(fill)}${"・".repeat(width - fill)}`
86-
process.stderr.write(
87-
`\r${orange}${bar} ${percent.toString().padStart(3)}%${reset} ${muted}${event.current}/${event.total}${reset} `,
88-
)
89-
} else {
90-
process.stderr.write(`sqlite-migration:${percent}${EOL}`)
91-
}
92-
},
93-
})
94-
if (tty) process.stderr.write("\n")
95-
if (tty) process.stderr.write("\x1b[?25h")
96-
else process.stderr.write(`sqlite-migration:done${EOL}`)
97-
UI.println(
98-
`Migration complete: ${stats.projects} projects, ${stats.sessions} sessions, ${stats.messages} messages`,
99-
)
100-
if (stats.errors.length > 0) {
101-
UI.println(`${stats.errors.length} errors occurred during migration`)
102-
}
103-
} catch (err) {
104-
if (tty) process.stderr.write("\x1b[?25h")
105-
UI.error(`Migration failed: ${errorMessage(err)}`)
106-
process.exit(1)
107-
} finally {
108-
sqlite.close()
109-
}
110-
},
48+
instance: false,
49+
handler: Effect.fn("Cli.db.path")(function* () {
50+
console.log(Database.path())
51+
}),
11152
})
11253

113-
export const DbCommand = cmd({
54+
export const DbCommand = effectCmd({
11455
command: "db",
11556
describe: "database tools",
57+
instance: false,
11658
builder: (yargs: Argv) => {
117-
return yargs.command(QueryCommand).command(PathCommand).command(MigrateCommand).demandCommand()
59+
return yargs.command(QueryCommand).command(PathCommand).demandCommand()
11860
},
119-
handler: () => {},
61+
handler: Effect.fn("Cli.db")(function* () {}),
12062
})

packages/opencode/src/cli/cmd/import.ts

Lines changed: 33 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { SessionLegacy } from "@opencode-ai/core/session/legacy"
33
import { Session } from "@/session/session"
44
import { MessageV2 } from "../../session/message-v2"
55
import { CliError, effectCmd } from "../effect-cmd"
6-
import { Database } from "@/storage/db"
6+
import { Database } from "@opencode-ai/core/database/database"
77
import { SessionTable, MessageTable, PartTable } from "@opencode-ai/core/session/sql"
88
import { InstanceRef } from "@/effect/instance-ref"
99
import { ShareNext } from "@/share/share-next"
@@ -99,6 +99,7 @@ export const ImportCommand = effectCmd({
9999
const runImport = Effect.fn("Cli.import.body")(function* (file: string, ctx: InstanceContext) {
100100
const share = yield* ShareNext.Service
101101
const fs = yield* AppFileSystem.Service
102+
const { db } = yield* Database.Service
102103

103104
let exportData: ExportData | undefined
104105

@@ -176,48 +177,45 @@ const runImport = Effect.fn("Cli.import.body")(function* (file: string, ctx: Ins
176177
path: path.relative(path.resolve(ctx.worktree), ctx.directory).replaceAll("\\", "/"),
177178
}) as Session.Info
178179
const row = Session.toRow(info)
179-
Database.use((db) =>
180-
db
181-
.insert(SessionTable)
182-
.values(row)
183-
.onConflictDoUpdate({
184-
target: SessionTable.id,
185-
set: { project_id: row.project_id, directory: row.directory, path: row.path },
186-
})
187-
.run(),
188-
)
180+
yield* db
181+
.insert(SessionTable)
182+
.values(row)
183+
.onConflictDoUpdate({
184+
target: SessionTable.id,
185+
set: { project_id: row.project_id, directory: row.directory, path: row.path },
186+
})
187+
.run()
188+
.pipe(Effect.orDie)
189189

190190
for (const msg of exportData.messages) {
191191
const msgInfo = decodeMessageInfo(msg.info) as SessionLegacy.Info
192192
const { id, sessionID: _, ...msgData } = msgInfo
193-
Database.use((db) =>
194-
db
195-
.insert(MessageTable)
196-
.values({
197-
id,
198-
session_id: row.id,
199-
time_created: msgInfo.time?.created ?? Date.now(),
200-
data: msgData as never,
201-
})
202-
.onConflictDoNothing()
203-
.run(),
204-
)
193+
yield* db
194+
.insert(MessageTable)
195+
.values({
196+
id,
197+
session_id: row.id,
198+
time_created: msgInfo.time?.created ?? Date.now(),
199+
data: msgData as never,
200+
})
201+
.onConflictDoNothing()
202+
.run()
203+
.pipe(Effect.orDie)
205204

206205
for (const part of msg.parts) {
207206
const partInfo = decodePart(part) as SessionLegacy.Part
208207
const { id: partId, sessionID: _s, messageID, ...partData } = partInfo
209-
Database.use((db) =>
210-
db
211-
.insert(PartTable)
212-
.values({
213-
id: partId,
214-
message_id: messageID,
215-
session_id: row.id,
216-
data: partData,
217-
})
218-
.onConflictDoNothing()
219-
.run(),
220-
)
208+
yield* db
209+
.insert(PartTable)
210+
.values({
211+
id: partId,
212+
message_id: messageID,
213+
session_id: row.id,
214+
data: partData,
215+
})
216+
.onConflictDoNothing()
217+
.run()
218+
.pipe(Effect.orDie)
221219
}
222220
}
223221

packages/opencode/src/cli/cmd/stats.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Effect } from "effect"
22
import { effectCmd } from "../effect-cmd"
33
import { Session } from "@/session/session"
44
import { NotFoundError } from "@/storage/storage"
5-
import { Database } from "@/storage/db"
5+
import { Database } from "@opencode-ai/core/database/database"
66
import { SessionTable } from "@opencode-ai/core/session/sql"
77
import { Project } from "@/project/project"
88
import { InstanceRef } from "@/effect/instance-ref"
@@ -80,17 +80,18 @@ export const StatsCommand = effectCmd({
8080
}),
8181
})
8282

83-
const getAllSessions = Effect.sync(() =>
84-
Database.use((db) => db.select().from(SessionTable).all()).map((row) => Session.fromRow(row)),
85-
)
83+
const getAllSessions = Effect.fnUntraced(function* () {
84+
const { db } = yield* Database.Service
85+
return (yield* db.select().from(SessionTable).all().pipe(Effect.orDie)).map((row) => Session.fromRow(row))
86+
})
8687

8788
const aggregateSessionStats = Effect.fn("Cli.stats.aggregate")(function* (
8889
days?: number,
8990
projectFilter?: string,
9091
currentProject?: Project.Info,
9192
) {
9293
const svc = yield* Session.Service
93-
const sessions = yield* getAllSessions
94+
const sessions = yield* getAllSessions()
9495
const MS_IN_DAY = 24 * 60 * 60 * 1000
9596

9697
const cutoffTime = (() => {

0 commit comments

Comments
 (0)