Skip to content

Commit 5c727fd

Browse files
authored
feat: merge all existing tables inside sqlite database (#404)
1 parent 1bc308a commit 5c727fd

File tree

4 files changed

+59
-60
lines changed

4 files changed

+59
-60
lines changed

lib/common-utils.js

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -83,23 +83,22 @@ function createTableQuery(tableName, columns) {
8383
return `CREATE TABLE IF NOT EXISTS ${tableName} (${formattedColumns})`;
8484
}
8585

86-
exports.mergeTablesQueries = (dbPaths) => {
87-
const tablesToMerge = [
88-
DB_SUITES_TABLE_NAME
89-
];
90-
const mkMergeQuery = (table) => `INSERT OR IGNORE INTO ${table} SELECT * FROM attached.${table}`;
91-
const queries = [].concat(
92-
`PRAGMA page_size = ${DB_MAX_AVAILABLE_PAGE_SIZE}`,
93-
exports.createTablesQuery(),
94-
);
86+
exports.mergeTables = ({db, dbPaths, getExistingTables = () => {}}) => {
87+
db.prepare(`PRAGMA page_size = ${DB_MAX_AVAILABLE_PAGE_SIZE}`).run();
9588

9689
for (const dbPath of dbPaths) {
97-
queries.push(`ATTACH '${dbPath}' AS attached`);
98-
queries.push.apply(queries, tablesToMerge.map(mkMergeQuery));
99-
queries.push('DETACH attached');
100-
}
90+
db.prepare(`ATTACH '${dbPath}' AS attached`).run();
91+
92+
const getTablesStatement = db.prepare(`SELECT name FROM attached.sqlite_master WHERE type='table'`);
93+
const tables = getExistingTables(getTablesStatement);
10194

102-
return queries;
95+
for (const tableName of tables) {
96+
db.prepare(`CREATE TABLE IF NOT EXISTS ${tableName} AS SELECT * FROM attached.${tableName} LIMIT 0`).run();
97+
db.prepare(`INSERT OR IGNORE INTO ${tableName} SELECT * FROM attached.${tableName}`).run();
98+
}
99+
100+
db.prepare(`DETACH attached`).run();
101+
}
103102
};
104103

105104
exports.compareDatabaseRowsByTimestamp = (row1, row2) => {

lib/gui/tool-runner/utils.js

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ const NestedError = require('nested-error-stacks');
1111
const StaticTestsTreeBuilder = require('../../tests-tree-builder/static');
1212
const {logger} = require('../../server-utils');
1313
const constantFileNames = require('../../constants/file-names');
14-
const {mergeTablesQueries, selectAllSuitesQuery, compareDatabaseRowsByTimestamp} = require('../../common-utils');
15-
const {writeDatabaseUrlsFile} = require('../../server-utils');
14+
const {mergeTables, selectAllSuitesQuery, compareDatabaseRowsByTimestamp} = require('../../common-utils');
1615

1716
exports.formatId = (hash, browserId) => `${hash}/${browserId}`;
1817

@@ -48,26 +47,11 @@ exports.mergeDatabasesForReuse = async (reportPath) => {
4847
logger.warn(chalk.yellow(`Merge databases to ${constantFileNames.LOCAL_DATABASE_NAME}`));
4948

5049
const mergedDatabase = new Database(mergedDbPath);
51-
52-
for (const query of mergeTablesQueries(dbPaths)) {
53-
try {
54-
mergedDatabase.prepare(query).run();
55-
} catch (err) {
56-
// TODO: To be able to work with files that have been created before
57-
// TODO: Remove in next versions
58-
if (/no such table/.test(err)) {
59-
console.warn(err);
60-
61-
continue;
62-
}
63-
64-
throw err;
65-
}
66-
}
67-
50+
mergeTables({db: mergedDatabase, dbPaths, getExistingTables: (statement) => {
51+
return statement.all().map((table) => table.name);
52+
}});
6853
mergedDatabase.close();
6954

70-
await writeDatabaseUrlsFile(reportPath, [constantFileNames.LOCAL_DATABASE_NAME]);
7155
await Promise.all(dbPaths.map(p => fs.remove(p)));
7256
};
7357

lib/static/modules/sqlite.js

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import axios from 'axios';
44
import {flattenDeep} from 'lodash';
5-
import {mergeTablesQueries} from '../../common-utils';
5+
import {mergeTables} from '../../common-utils';
66

77
function isRelativeUrl(url) {
88
try {
@@ -114,21 +114,15 @@ async function mergeDatabases(dataForDbs) {
114114
const mergedDbConnection = new SQL.Database(undefined, sumOfChunkSizes);
115115
const dbPaths = connections.map(db => db.filename);
116116

117-
for (const query of mergeTablesQueries(dbPaths)) {
118-
try {
119-
mergedDbConnection.run(query);
120-
} catch (err) {
121-
// TODO: To be able to work with files that have been created before
122-
// TODO: Remove in next versions
123-
if (/no such table/.test(err)) {
124-
console.warn(err);
125-
126-
continue;
127-
}
117+
mergeTables({db: mergedDbConnection, dbPaths, getExistingTables: (statement) => {
118+
const tables = [];
128119

129-
throw err;
120+
while (statement.step()) {
121+
tables.push(...statement.get());
130122
}
131-
}
123+
124+
return tables;
125+
}});
132126

133127
connections.forEach(db => db.close());
134128

test/unit/lib/static/modules/sqlite.js

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict';
22

33
import axios from 'axios';
4-
import * as _ from 'lodash';
4+
import proxyquire from 'proxyquire';
55

66
import {
77
fetchDatabases,
@@ -162,18 +162,27 @@ describe('lib/static/modules/sqlite', () => {
162162
});
163163

164164
describe('mergeDatabases', () => {
165-
let SQL;
165+
let SQL, statement;
166166
let DatabaseConstructorSpy;
167167

168168
beforeEach(() => {
169+
statement = {
170+
step: () => {},
171+
get: () => {},
172+
run: () => {}
173+
};
174+
169175
SQL = {
170176
Database: class {
171177
run() {}
172178
close() {}
179+
prepare() {
180+
return statement;
181+
}
173182
}
174183
};
175184
DatabaseConstructorSpy = sinon.spy(SQL, 'Database');
176-
sandbox.spy(SQL.Database.prototype, 'run');
185+
sandbox.spy(SQL.Database.prototype, 'prepare');
177186
sandbox.spy(SQL.Database.prototype, 'close');
178187

179188
global.window = {
@@ -198,7 +207,7 @@ describe('lib/static/modules/sqlite', () => {
198207

199208
assert.instanceOf(mergedDbConnection, SQL.Database);
200209
assert.calledOnceWith(DatabaseConstructorSpy, new Uint8Array(1));
201-
assert.notCalled(SQL.Database.prototype.run);
210+
assert.notCalled(SQL.Database.prototype.prepare);
202211
assert.notCalled(SQL.Database.prototype.close);
203212
});
204213

@@ -219,17 +228,30 @@ describe('lib/static/modules/sqlite', () => {
219228
assert.calledTwice(SQL.Database.prototype.close);
220229
});
221230

222-
it('should merge both "suites" tables', async () => {
223-
const data1 = new ArrayBuffer(1);
224-
const data2 = new ArrayBuffer(1);
231+
describe('merge tables', () => {
232+
let mergeDatabases, mergeTables;
233+
234+
beforeEach(() => {
235+
mergeTables = sandbox.stub();
225236

226-
await mergeDatabases([data1, data2]);
237+
mergeDatabases = proxyquire('lib/static/modules/sqlite', {
238+
'../../common-utils': {mergeTables}
239+
}).mergeDatabases;
240+
});
241+
242+
it('should get existing tables', async () => {
243+
const statement = {
244+
step: sandbox.stub().returns(true).onThirdCall().returns(false),
245+
get: sandbox.stub()
246+
.onFirstCall().returns(['table1'])
247+
.onSecondCall().returns(['table2'])
248+
};
227249

228-
const rawQueries = _
229-
.flatten(SQL.Database.prototype.run.args)
230-
.join(' ');
250+
await mergeDatabases([new ArrayBuffer(1), new ArrayBuffer(2)]);
251+
const tables = mergeTables.getCall(0).args[0].getExistingTables(statement);
231252

232-
assert.include(rawQueries, 'suites');
253+
assert.deepEqual(tables, ['table1', 'table2']);
254+
});
233255
});
234256
});
235257

0 commit comments

Comments
 (0)