Skip to content

Commit 9844b2e

Browse files
authored
Merge branch 'main' into fix/issues-408-411
2 parents 7fb4afa + c326b02 commit 9844b2e

10 files changed

Lines changed: 246 additions & 172 deletions

File tree

src/db/repository/cached-stmt.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* Resolve a cached prepared statement, compiling on first use per db.
3+
* Each `cache` WeakMap must always be called with the same `sql` —
4+
* the sql argument is only used on the first compile; subsequent calls
5+
* return the cached statement regardless of the sql passed.
6+
*
7+
* @param {WeakMap} cache - WeakMap keyed by db instance
8+
* @param {object} db - better-sqlite3 database instance
9+
* @param {string} sql - SQL to compile on first use
10+
* @returns {object} prepared statement
11+
*/
12+
export function cachedStmt(cache, db, sql) {
13+
let stmt = cache.get(db);
14+
if (!stmt) {
15+
stmt = db.prepare(sql);
16+
cache.set(db, stmt);
17+
}
18+
return stmt;
19+
}

src/db/repository/cfg.js

Lines changed: 27 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import { cachedStmt } from './cached-stmt.js';
2+
13
// ─── Statement caches (one prepared statement per db instance) ────────────
2-
// WeakMap keys on the db object so statements are GC'd when the db closes.
34
const _getCfgBlocksStmt = new WeakMap();
45
const _getCfgEdgesStmt = new WeakMap();
56
const _deleteCfgEdgesStmt = new WeakMap();
@@ -26,16 +27,13 @@ export function hasCfgTables(db) {
2627
* @returns {object[]}
2728
*/
2829
export function getCfgBlocks(db, functionNodeId) {
29-
let stmt = _getCfgBlocksStmt.get(db);
30-
if (!stmt) {
31-
stmt = db.prepare(
32-
`SELECT id, block_index, block_type, start_line, end_line, label
33-
FROM cfg_blocks WHERE function_node_id = ?
34-
ORDER BY block_index`,
35-
);
36-
_getCfgBlocksStmt.set(db, stmt);
37-
}
38-
return stmt.all(functionNodeId);
30+
return cachedStmt(
31+
_getCfgBlocksStmt,
32+
db,
33+
`SELECT id, block_index, block_type, start_line, end_line, label
34+
FROM cfg_blocks WHERE function_node_id = ?
35+
ORDER BY block_index`,
36+
).all(functionNodeId);
3937
}
4038

4139
/**
@@ -45,21 +43,18 @@ export function getCfgBlocks(db, functionNodeId) {
4543
* @returns {object[]}
4644
*/
4745
export function getCfgEdges(db, functionNodeId) {
48-
let stmt = _getCfgEdgesStmt.get(db);
49-
if (!stmt) {
50-
stmt = db.prepare(
51-
`SELECT e.kind,
52-
sb.block_index AS source_index, sb.block_type AS source_type,
53-
tb.block_index AS target_index, tb.block_type AS target_type
54-
FROM cfg_edges e
55-
JOIN cfg_blocks sb ON e.source_block_id = sb.id
56-
JOIN cfg_blocks tb ON e.target_block_id = tb.id
57-
WHERE e.function_node_id = ?
58-
ORDER BY sb.block_index, tb.block_index`,
59-
);
60-
_getCfgEdgesStmt.set(db, stmt);
61-
}
62-
return stmt.all(functionNodeId);
46+
return cachedStmt(
47+
_getCfgEdgesStmt,
48+
db,
49+
`SELECT e.kind,
50+
sb.block_index AS source_index, sb.block_type AS source_type,
51+
tb.block_index AS target_index, tb.block_type AS target_type
52+
FROM cfg_edges e
53+
JOIN cfg_blocks sb ON e.source_block_id = sb.id
54+
JOIN cfg_blocks tb ON e.target_block_id = tb.id
55+
WHERE e.function_node_id = ?
56+
ORDER BY sb.block_index, tb.block_index`,
57+
).all(functionNodeId);
6358
}
6459

6560
/**
@@ -68,16 +63,10 @@ export function getCfgEdges(db, functionNodeId) {
6863
* @param {number} functionNodeId
6964
*/
7065
export function deleteCfgForNode(db, functionNodeId) {
71-
let delEdges = _deleteCfgEdgesStmt.get(db);
72-
if (!delEdges) {
73-
delEdges = db.prepare('DELETE FROM cfg_edges WHERE function_node_id = ?');
74-
_deleteCfgEdgesStmt.set(db, delEdges);
75-
}
76-
let delBlocks = _deleteCfgBlocksStmt.get(db);
77-
if (!delBlocks) {
78-
delBlocks = db.prepare('DELETE FROM cfg_blocks WHERE function_node_id = ?');
79-
_deleteCfgBlocksStmt.set(db, delBlocks);
80-
}
81-
delEdges.run(functionNodeId);
82-
delBlocks.run(functionNodeId);
66+
cachedStmt(_deleteCfgEdgesStmt, db, 'DELETE FROM cfg_edges WHERE function_node_id = ?').run(
67+
functionNodeId,
68+
);
69+
cachedStmt(_deleteCfgBlocksStmt, db, 'DELETE FROM cfg_blocks WHERE function_node_id = ?').run(
70+
functionNodeId,
71+
);
8372
}

src/db/repository/cochange.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1+
import { cachedStmt } from './cached-stmt.js';
2+
3+
// ─── Statement caches (one prepared statement per db instance) ────────────
4+
const _hasCoChangesStmt = new WeakMap();
5+
const _getCoChangeMetaStmt = new WeakMap();
6+
const _upsertCoChangeMetaStmt = new WeakMap();
7+
18
/**
29
* Check whether the co_changes table has data.
310
* @param {object} db
411
* @returns {boolean}
512
*/
613
export function hasCoChanges(db) {
714
try {
8-
return !!db.prepare('SELECT 1 FROM co_changes LIMIT 1').get();
15+
return !!cachedStmt(_hasCoChangesStmt, db, 'SELECT 1 FROM co_changes LIMIT 1').get();
916
} catch {
1017
return false;
1118
}
@@ -19,7 +26,11 @@ export function hasCoChanges(db) {
1926
export function getCoChangeMeta(db) {
2027
const meta = {};
2128
try {
22-
for (const row of db.prepare('SELECT key, value FROM co_change_meta').all()) {
29+
for (const row of cachedStmt(
30+
_getCoChangeMetaStmt,
31+
db,
32+
'SELECT key, value FROM co_change_meta',
33+
).all()) {
2334
meta[row.key] = row.value;
2435
}
2536
} catch {
@@ -35,7 +46,9 @@ export function getCoChangeMeta(db) {
3546
* @param {string} value
3647
*/
3748
export function upsertCoChangeMeta(db, key, value) {
38-
db.prepare(
49+
cachedStmt(
50+
_upsertCoChangeMetaStmt,
51+
db,
3952
'INSERT INTO co_change_meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value',
4053
).run(key, value);
4154
}

src/db/repository/complexity.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
import { cachedStmt } from './cached-stmt.js';
2+
3+
// ─── Statement caches (one prepared statement per db instance) ────────────
4+
const _getComplexityForNodeStmt = new WeakMap();
5+
16
/**
27
* Get complexity metrics for a node.
38
* Used by contextData and explainFunctionImpl in queries.js.
@@ -6,10 +11,10 @@
611
* @returns {{ cognitive: number, cyclomatic: number, max_nesting: number, maintainability_index: number, halstead_volume: number }|undefined}
712
*/
813
export function getComplexityForNode(db, nodeId) {
9-
return db
10-
.prepare(
11-
`SELECT cognitive, cyclomatic, max_nesting, maintainability_index, halstead_volume
12-
FROM function_complexity WHERE node_id = ?`,
13-
)
14-
.get(nodeId);
14+
return cachedStmt(
15+
_getComplexityForNodeStmt,
16+
db,
17+
`SELECT cognitive, cyclomatic, max_nesting, maintainability_index, halstead_volume
18+
FROM function_complexity WHERE node_id = ?`,
19+
).get(nodeId);
1520
}

src/db/repository/dataflow.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1+
import { cachedStmt } from './cached-stmt.js';
2+
3+
// ─── Statement caches (one prepared statement per db instance) ────────────
4+
const _hasDataflowTableStmt = new WeakMap();
5+
16
/**
27
* Check whether the dataflow table exists and has data.
38
* @param {object} db
49
* @returns {boolean}
510
*/
611
export function hasDataflowTable(db) {
712
try {
8-
return db.prepare('SELECT COUNT(*) AS c FROM dataflow').get().c > 0;
13+
return cachedStmt(_hasDataflowTableStmt, db, 'SELECT COUNT(*) AS c FROM dataflow').get().c > 0;
914
} catch {
1015
return false;
1116
}

0 commit comments

Comments
 (0)