Skip to content

Commit 7d2d2e7

Browse files
author
Marvin Zhang
committed
feat(storage): Update MySQL, PostgreSQL, and SQLite storage providers to use integer IDs and add key_field for unique identification
1 parent bd38af8 commit 7d2d2e7

File tree

4 files changed

+49
-39
lines changed

4 files changed

+49
-39
lines changed

packages/core/src/storage/mysql-storage.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* MySQL storage provider for production-grade devlog storage
33
*/
44

5-
import { DevlogEntry, DevlogFilter, DevlogStats } from "@devlog/types";
5+
import { DevlogEntry, DevlogFilter, DevlogStats, DevlogId } from "@devlog/types";
66
import { StorageProvider } from "./storage-provider.js";
77

88
export class MySQLStorageProvider implements StorageProvider {
@@ -31,7 +31,8 @@ export class MySQLStorageProvider implements StorageProvider {
3131
// Create tables and indexes
3232
await this.connection.execute(`
3333
CREATE TABLE IF NOT EXISTS devlog_entries (
34-
id VARCHAR(255) PRIMARY KEY,
34+
id INT AUTO_INCREMENT PRIMARY KEY,
35+
key_field VARCHAR(255) UNIQUE NOT NULL,
3536
title TEXT NOT NULL,
3637
type VARCHAR(50) NOT NULL,
3738
description TEXT NOT NULL,
@@ -50,6 +51,7 @@ export class MySQLStorageProvider implements StorageProvider {
5051
external_references JSON,
5152
notes JSON,
5253
54+
INDEX idx_devlog_key (key_field),
5355
INDEX idx_devlog_status (status),
5456
INDEX idx_devlog_type (type),
5557
INDEX idx_devlog_priority (priority),
@@ -61,12 +63,12 @@ export class MySQLStorageProvider implements StorageProvider {
6163
`);
6264
}
6365

64-
async exists(id: string): Promise<boolean> {
66+
async exists(id: DevlogId): Promise<boolean> {
6567
const [rows] = await this.connection.execute("SELECT 1 FROM devlog_entries WHERE id = ?", [id]);
6668
return (rows as any[]).length > 0;
6769
}
6870

69-
async get(id: string): Promise<DevlogEntry | null> {
71+
async get(id: DevlogId): Promise<DevlogEntry | null> {
7072
const [rows] = await this.connection.execute("SELECT * FROM devlog_entries WHERE id = ?", [id]);
7173
const results = rows as any[];
7274

@@ -78,11 +80,12 @@ export class MySQLStorageProvider implements StorageProvider {
7880
async save(entry: DevlogEntry): Promise<void> {
7981
await this.connection.execute(`
8082
INSERT INTO devlog_entries (
81-
id, title, type, description, status, priority, created_at, updated_at,
83+
id, key_field, title, type, description, status, priority, created_at, updated_at,
8284
estimated_hours, actual_hours, assignee, tags, files, related_devlogs,
8385
context, ai_context, external_references, notes
84-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
86+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
8587
ON DUPLICATE KEY UPDATE
88+
key_field = VALUES(key_field),
8689
title = VALUES(title),
8790
type = VALUES(type),
8891
description = VALUES(description),
@@ -101,6 +104,7 @@ export class MySQLStorageProvider implements StorageProvider {
101104
notes = VALUES(notes)
102105
`, [
103106
entry.id,
107+
entry.key,
104108
entry.title,
105109
entry.type,
106110
entry.description,
@@ -121,7 +125,7 @@ export class MySQLStorageProvider implements StorageProvider {
121125
]);
122126
}
123127

124-
async delete(id: string): Promise<void> {
128+
async delete(id: DevlogId): Promise<void> {
125129
await this.connection.execute("DELETE FROM devlog_entries WHERE id = ?", [id]);
126130
}
127131

@@ -234,6 +238,7 @@ export class MySQLStorageProvider implements StorageProvider {
234238
private rowToDevlogEntry(row: any): DevlogEntry {
235239
return {
236240
id: row.id,
241+
key: row.key_field,
237242
title: row.title,
238243
type: row.type,
239244
description: row.description,

packages/core/src/storage/postgresql-storage.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* PostgreSQL storage provider for production-grade devlog storage
33
*/
44

5-
import { DevlogEntry, DevlogFilter, DevlogStats } from "@devlog/types";
5+
import { DevlogEntry, DevlogFilter, DevlogStats, DevlogId } from "@devlog/types";
66
import { StorageProvider } from "./storage-provider.js";
77

88
export class PostgreSQLStorageProvider implements StorageProvider {
@@ -34,7 +34,8 @@ export class PostgreSQLStorageProvider implements StorageProvider {
3434
// Create tables and indexes
3535
await this.client.query(`
3636
CREATE TABLE IF NOT EXISTS devlog_entries (
37-
id TEXT PRIMARY KEY,
37+
id SERIAL PRIMARY KEY,
38+
key_field TEXT UNIQUE NOT NULL,
3839
title TEXT NOT NULL,
3940
type TEXT NOT NULL,
4041
description TEXT NOT NULL,
@@ -54,6 +55,7 @@ export class PostgreSQLStorageProvider implements StorageProvider {
5455
notes JSONB
5556
);
5657
58+
CREATE INDEX IF NOT EXISTS idx_devlog_key ON devlog_entries(key_field);
5759
CREATE INDEX IF NOT EXISTS idx_devlog_status ON devlog_entries(status);
5860
CREATE INDEX IF NOT EXISTS idx_devlog_type ON devlog_entries(type);
5961
CREATE INDEX IF NOT EXISTS idx_devlog_priority ON devlog_entries(priority);
@@ -65,12 +67,12 @@ export class PostgreSQLStorageProvider implements StorageProvider {
6567
`);
6668
}
6769

68-
async exists(id: string): Promise<boolean> {
70+
async exists(id: DevlogId): Promise<boolean> {
6971
const result = await this.client.query("SELECT 1 FROM devlog_entries WHERE id = $1", [id]);
7072
return result.rows.length > 0;
7173
}
7274

73-
async get(id: string): Promise<DevlogEntry | null> {
75+
async get(id: DevlogId): Promise<DevlogEntry | null> {
7476
const result = await this.client.query("SELECT * FROM devlog_entries WHERE id = $1", [id]);
7577

7678
if (result.rows.length === 0) return null;
@@ -81,11 +83,12 @@ export class PostgreSQLStorageProvider implements StorageProvider {
8183
async save(entry: DevlogEntry): Promise<void> {
8284
await this.client.query(`
8385
INSERT INTO devlog_entries (
84-
id, title, type, description, status, priority, created_at, updated_at,
86+
id, key_field, title, type, description, status, priority, created_at, updated_at,
8587
estimated_hours, actual_hours, assignee, tags, files, related_devlogs,
8688
context, ai_context, external_references, notes
87-
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)
89+
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)
8890
ON CONFLICT (id) DO UPDATE SET
91+
key_field = EXCLUDED.key_field,
8992
title = EXCLUDED.title,
9093
type = EXCLUDED.type,
9194
description = EXCLUDED.description,
@@ -104,6 +107,7 @@ export class PostgreSQLStorageProvider implements StorageProvider {
104107
notes = EXCLUDED.notes
105108
`, [
106109
entry.id,
110+
entry.key,
107111
entry.title,
108112
entry.type,
109113
entry.description,
@@ -124,7 +128,7 @@ export class PostgreSQLStorageProvider implements StorageProvider {
124128
]);
125129
}
126130

127-
async delete(id: string): Promise<void> {
131+
async delete(id: DevlogId): Promise<void> {
128132
await this.client.query("DELETE FROM devlog_entries WHERE id = $1", [id]);
129133
}
130134

@@ -242,6 +246,7 @@ export class PostgreSQLStorageProvider implements StorageProvider {
242246
private rowToDevlogEntry(row: any): DevlogEntry {
243247
return {
244248
id: row.id,
249+
key: row.key_field,
245250
title: row.title,
246251
type: row.type,
247252
description: row.description,

packages/core/src/storage/sqlite-storage.ts

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
import { DevlogEntry, DevlogFilter, DevlogStats, DevlogStatus, DevlogType, DevlogPriority, DevlogId } from "@devlog/types";
66
import { StorageProvider } from "./storage-provider.js";
7-
import { IdManager } from "../utils/id-manager.js";
87

98
export class SQLiteStorageProvider implements StorageProvider {
109
private db: any = null;
@@ -79,7 +78,8 @@ export class SQLiteStorageProvider implements StorageProvider {
7978
try {
8079
this.db.exec(`
8180
CREATE TABLE IF NOT EXISTS devlog_entries (
82-
id TEXT PRIMARY KEY,
81+
id INTEGER PRIMARY KEY AUTOINCREMENT,
82+
key_field TEXT UNIQUE NOT NULL,
8383
title TEXT NOT NULL,
8484
type TEXT NOT NULL,
8585
description TEXT NOT NULL,
@@ -99,6 +99,7 @@ export class SQLiteStorageProvider implements StorageProvider {
9999
notes TEXT -- JSON array
100100
);
101101
102+
CREATE INDEX IF NOT EXISTS idx_devlog_key ON devlog_entries(key_field);
102103
CREATE INDEX IF NOT EXISTS idx_devlog_status ON devlog_entries(status);
103104
CREATE INDEX IF NOT EXISTS idx_devlog_type ON devlog_entries(type);
104105
CREATE INDEX IF NOT EXISTS idx_devlog_priority ON devlog_entries(priority);
@@ -139,17 +140,16 @@ export class SQLiteStorageProvider implements StorageProvider {
139140
}
140141

141142
async exists(id: DevlogId): Promise<boolean> {
142-
const idStr = IdManager.idToString(id);
143-
console.log(`[SQLiteStorage] Checking if entry exists: ${idStr}`);
143+
console.log(`[SQLiteStorage] Checking if entry exists: ${id}`);
144144
if (!this.db) {
145-
console.error(`[SQLiteStorage] Database not initialized when checking exists for: ${idStr}`);
145+
console.error(`[SQLiteStorage] Database not initialized when checking exists for: ${id}`);
146146
throw new Error("Database not initialized");
147147
}
148148

149149
try {
150150
const stmt = this.db.prepare("SELECT 1 FROM devlog_entries WHERE id = ?");
151-
const result = stmt.get(idStr) !== undefined;
152-
console.log(`[SQLiteStorage] Entry exists result for ${idStr}: ${result}`);
151+
const result = stmt.get(id) !== undefined;
152+
console.log(`[SQLiteStorage] Entry exists result for ${id}: ${result}`);
153153
return result;
154154
} catch (error: any) {
155155
console.error(`[SQLiteStorage] Error checking if entry exists:`, error);
@@ -158,23 +158,22 @@ export class SQLiteStorageProvider implements StorageProvider {
158158
}
159159

160160
async get(id: DevlogId): Promise<DevlogEntry | null> {
161-
const idStr = IdManager.idToString(id);
162-
console.log(`[SQLiteStorage] Getting entry: ${idStr}`);
161+
console.log(`[SQLiteStorage] Getting entry: ${id}`);
163162
if (!this.db) {
164-
console.error(`[SQLiteStorage] Database not initialized when getting: ${idStr}`);
163+
console.error(`[SQLiteStorage] Database not initialized when getting: ${id}`);
165164
throw new Error("Database not initialized");
166165
}
167166

168167
try {
169168
const stmt = this.db.prepare("SELECT * FROM devlog_entries WHERE id = ?");
170-
const row = stmt.get(idStr) as any;
169+
const row = stmt.get(id) as any;
171170

172171
if (!row) {
173-
console.log(`[SQLiteStorage] Entry not found: ${idStr}`);
172+
console.log(`[SQLiteStorage] Entry not found: ${id}`);
174173
return null;
175174
}
176175

177-
console.log(`[SQLiteStorage] Found entry: ${idStr}`);
176+
console.log(`[SQLiteStorage] Found entry: ${id}`);
178177
return this.rowToDevlogEntry(row);
179178
} catch (error: any) {
180179
console.error(`[SQLiteStorage] Error getting entry:`, error);
@@ -183,24 +182,24 @@ export class SQLiteStorageProvider implements StorageProvider {
183182
}
184183

185184
async save(entry: DevlogEntry): Promise<void> {
186-
const idStr = IdManager.idToString(entry.id);
187-
console.log(`[SQLiteStorage] Saving entry: ${idStr} - ${entry.title}`);
185+
console.log(`[SQLiteStorage] Saving entry: ${entry.id} - ${entry.title}`);
188186
if (!this.db) {
189-
console.error(`[SQLiteStorage] Database not initialized when saving: ${idStr}`);
187+
console.error(`[SQLiteStorage] Database not initialized when saving: ${entry.id}`);
190188
throw new Error("Database not initialized");
191189
}
192190

193191
try {
194192
const stmt = this.db.prepare(`
195193
INSERT OR REPLACE INTO devlog_entries (
196-
id, title, type, description, status, priority, created_at, updated_at,
194+
id, key_field, title, type, description, status, priority, created_at, updated_at,
197195
estimated_hours, actual_hours, assignee, tags, files, related_devlogs,
198196
context, ai_context, external_references, notes
199-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
197+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
200198
`);
201199

202200
stmt.run(
203-
idStr,
201+
entry.id,
202+
entry.key,
204203
entry.title,
205204
entry.type,
206205
entry.description,
@@ -220,19 +219,18 @@ export class SQLiteStorageProvider implements StorageProvider {
220219
JSON.stringify(entry.notes)
221220
);
222221

223-
console.log(`[SQLiteStorage] Successfully saved entry: ${idStr}`);
222+
console.log(`[SQLiteStorage] Successfully saved entry: ${entry.id}`);
224223
} catch (error: any) {
225224
console.error(`[SQLiteStorage] Error saving entry:`, error);
226225
throw error;
227226
}
228227
}
229228

230229
async delete(id: DevlogId): Promise<void> {
231-
const idStr = IdManager.idToString(id);
232230
if (!this.db) throw new Error("Database not initialized");
233231

234232
const stmt = this.db.prepare("DELETE FROM devlog_entries WHERE id = ?");
235-
stmt.run(idStr);
233+
stmt.run(id);
236234
}
237235

238236
async list(filter?: DevlogFilter): Promise<DevlogEntry[]> {
@@ -364,7 +362,8 @@ export class SQLiteStorageProvider implements StorageProvider {
364362

365363
private rowToDevlogEntry(row: any): DevlogEntry {
366364
return {
367-
id: IdManager.stringToId(row.id),
365+
id: row.id,
366+
key: row.key_field,
368367
title: row.title,
369368
type: row.type,
370369
description: row.description,

packages/types/src/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ export type DevlogPriority = "low" | "medium" | "high" | "critical";
1111
export type NoteCategory = "progress" | "issue" | "solution" | "idea" | "reminder";
1212

1313
/**
14-
* ID type for devlog entries - can be string (legacy) or number (new integer system)
14+
* ID type for devlog entries - integer only for clean, user-friendly references
1515
*/
16-
export type DevlogId = string | number;
16+
export type DevlogId = number;
1717

1818
export interface DevlogNote {
1919
id: string;
@@ -26,6 +26,7 @@ export interface DevlogNote {
2626

2727
export interface DevlogEntry {
2828
id: DevlogId;
29+
key: string; // Original semantic key (e.g., "web-ui-issues-investigation")
2930
title: string;
3031
type: DevlogType;
3132
description: string;

0 commit comments

Comments
 (0)