Skip to content

Commit e59f36d

Browse files
Yerazeclaude
andauthored
refactor: add getAffectedRows() helper to eliminate result extraction branching (#2374)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b3ee48d commit e59f36d

File tree

4 files changed

+42
-99
lines changed

4 files changed

+42
-99
lines changed

src/db/repositories/auth.ts

Lines changed: 16 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -523,79 +523,30 @@ export class AuthRepository extends BaseRepository {
523523
async revokeApiToken(tokenId: number, revokedBy: number): Promise<boolean> {
524524
const now = this.now();
525525
const { apiTokens } = this.tables;
526-
if (this.isSQLite()) {
527-
const db = this.getSqliteDb();
528-
const result = await db
529-
.update(apiTokens)
530-
.set({ isActive: false, revokedAt: now, revokedBy })
531-
.where(and(
532-
eq(apiTokens.id, tokenId),
533-
eq(apiTokens.isActive, true)
534-
));
535-
return (result.changes ?? 0) > 0;
536-
} else if (this.isMySQL()) {
537-
const db = this.getMysqlDb();
538-
const result = await db
539-
.update(apiTokens)
540-
.set({ isActive: false, revokedAt: now, revokedBy })
541-
.where(and(
542-
eq(apiTokens.id, tokenId),
543-
eq(apiTokens.isActive, true)
544-
));
545-
return (result[0].affectedRows ?? 0) > 0;
546-
} else {
547-
const db = this.getPostgresDb();
548-
const result = await db
549-
.update(apiTokens)
550-
.set({ isActive: false, revokedAt: now, revokedBy })
551-
.where(and(
552-
eq(apiTokens.id, tokenId),
553-
eq(apiTokens.isActive, true)
554-
))
555-
.returning({ id: apiTokensPostgres.id });
556-
return result.length > 0;
557-
}
526+
const result = await this.db
527+
.update(apiTokens)
528+
.set({ isActive: false, revokedAt: now, revokedBy })
529+
.where(and(
530+
eq(apiTokens.id, tokenId),
531+
eq(apiTokens.isActive, true)
532+
));
533+
return this.getAffectedRows(result) > 0;
558534
}
559535

560536
/**
561537
* Revoke all active API tokens for a user.
562-
* Keeps branching: different result shapes for affected row count.
563538
*/
564539
async revokeAllUserApiTokens(userId: number, revokedBy: number): Promise<number> {
565540
const now = this.now();
566541
const { apiTokens } = this.tables;
567-
if (this.isSQLite()) {
568-
const db = this.getSqliteDb();
569-
const result = await db
570-
.update(apiTokens)
571-
.set({ isActive: false, revokedAt: now, revokedBy })
572-
.where(and(
573-
eq(apiTokens.userId, userId),
574-
eq(apiTokens.isActive, true)
575-
));
576-
return result.changes ?? 0;
577-
} else if (this.isMySQL()) {
578-
const db = this.getMysqlDb();
579-
const result = await db
580-
.update(apiTokens)
581-
.set({ isActive: false, revokedAt: now, revokedBy })
582-
.where(and(
583-
eq(apiTokens.userId, userId),
584-
eq(apiTokens.isActive, true)
585-
));
586-
return result[0].affectedRows ?? 0;
587-
} else {
588-
const db = this.getPostgresDb();
589-
const result = await db
590-
.update(apiTokens)
591-
.set({ isActive: false, revokedAt: now, revokedBy })
592-
.where(and(
593-
eq(apiTokens.userId, userId),
594-
eq(apiTokens.isActive, true)
595-
))
596-
.returning({ id: apiTokensPostgres.id });
597-
return result.length;
598-
}
542+
const result = await this.db
543+
.update(apiTokens)
544+
.set({ isActive: false, revokedAt: now, revokedBy })
545+
.where(and(
546+
eq(apiTokens.userId, userId),
547+
eq(apiTokens.isActive, true)
548+
));
549+
return this.getAffectedRows(result);
599550
}
600551

601552
/**

src/db/repositories/base.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,22 @@ export abstract class BaseRepository {
155155
return this.db.execute(query);
156156
}
157157

158+
/**
159+
* Extract affected row count from a mutation result.
160+
* Normalizes across SQLite (.changes), PostgreSQL (.rowCount), and MySQL ([0].affectedRows).
161+
*/
162+
protected getAffectedRows(result: any): number {
163+
if (this.isSQLite()) {
164+
return Number(result?.changes ?? 0);
165+
}
166+
if (this.isMySQL()) {
167+
// MySQL execute() returns [ResultSetHeader, FieldPacket[]]
168+
return Number((result as any)?.[0]?.affectedRows ?? 0);
169+
}
170+
// PostgreSQL
171+
return Number((result as any)?.rowCount ?? 0);
172+
}
173+
158174
/**
159175
* Get current timestamp in milliseconds
160176
*/

src/db/repositories/messages.ts

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -51,31 +51,21 @@ export class MessagesRepository extends BaseRepository {
5151
decryptedBy: messageData.decryptedBy ?? null,
5252
};
5353

54-
if (this.isSQLite()) {
55-
const db = this.getSqliteDb();
56-
const result = await db
57-
.insert(messages)
58-
.values(values)
59-
.onConflictDoNothing();
60-
// SQLite Drizzle returns { changes: number } - 0 means conflict (no insert)
61-
return (result as any).changes > 0;
62-
} else if (this.isMySQL()) {
63-
const db = this.getMysqlDb();
64-
const result = await db
54+
let result: any;
55+
if (this.isMySQL()) {
56+
// MySQL uses onDuplicateKeyUpdate as equivalent of onConflictDoNothing
57+
result = await this.getMysqlDb()
6558
.insert(messages)
6659
.values(values)
67-
.onDuplicateKeyUpdate({ set: { id: messageData.id } }); // MySQL equivalent of onConflictDoNothing
68-
// MySQL returns affectedRows: 1 for insert, 0 for duplicate with same values
69-
return (result as any)[0]?.affectedRows > 0;
60+
.onDuplicateKeyUpdate({ set: { id: messageData.id } });
7061
} else {
71-
const db = this.getPostgresDb();
72-
const result = await db
62+
// SQLite and PostgreSQL both support onConflictDoNothing
63+
result = await this.db
7364
.insert(messages)
7465
.values(values)
7566
.onConflictDoNothing();
76-
// PostgreSQL returns rowCount: 0 on conflict
77-
return (result as any).rowCount > 0;
7867
}
68+
return this.getAffectedRows(result) > 0;
7969
}
8070

8171
/**

src/db/repositories/misc.ts

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,14 +1030,7 @@ export class MiscRepository extends BaseRepository {
10301030
async clearPacketLogs(): Promise<number> {
10311031
try {
10321032
const results = await this.executeRun(sql`DELETE FROM packet_log`);
1033-
let deletedCount: number;
1034-
if (this.isMySQL()) {
1035-
deletedCount = (results as any)[0]?.affectedRows ?? 0;
1036-
} else if (this.isPostgres()) {
1037-
deletedCount = (results as any).rowCount ?? 0;
1038-
} else {
1039-
deletedCount = (results as any).changes ?? (results as any).rowsAffected ?? 0;
1040-
}
1033+
const deletedCount = this.getAffectedRows(results);
10411034
logger.debug(`[MiscRepository] Cleared ${deletedCount} packet log entries`);
10421035
return deletedCount;
10431036
} catch (error) {
@@ -1056,14 +1049,7 @@ export class MiscRepository extends BaseRepository {
10561049
const results = await this.executeRun(
10571050
sql`DELETE FROM packet_log WHERE timestamp < ${cutoffTimestamp}`
10581051
);
1059-
let deleted: number;
1060-
if (this.isMySQL()) {
1061-
deleted = (results as any)[0]?.affectedRows ?? 0;
1062-
} else if (this.isPostgres()) {
1063-
deleted = (results as any).rowCount ?? 0;
1064-
} else {
1065-
deleted = (results as any).changes ?? (results as any).rowsAffected ?? 0;
1066-
}
1052+
const deleted = this.getAffectedRows(results);
10671053
if (deleted > 0) {
10681054
logger.debug(`[MiscRepository] Cleaned up ${deleted} packet log entries older than ${maxAgeHours} hours`);
10691055
}

0 commit comments

Comments
 (0)