Notes/apps/db-compare/src/compare.ts

138 lines
4.6 KiB
TypeScript
Raw Normal View History

2017-11-23 23:19:45 -05:00
"use strict";
2025-05-03 03:50:06 +03:00
import jsDiff from "diff";
import * as sqlite from "sqlite";
import * as sqlite3 from "sqlite3";
import sql from "./sql.js";
2017-11-23 23:19:45 -05:00
2025-05-03 03:50:06 +03:00
import "colors";
import path from "path";
2025-05-03 03:50:06 +03:00
function printDiff(one: string, two: string) {
2021-08-08 12:28:42 +02:00
const diff = jsDiff.diffChars(one, two);
2017-11-23 23:19:45 -05:00
diff.forEach(function(part){
// green for additions, red for deletions
// grey for common parts
const color = part.added ? 'green' :
part.removed ? 'red' : 'grey';
process.stderr.write(part.value[color]);
});
2021-08-08 12:28:42 +02:00
console.log("");
2017-11-23 23:19:45 -05:00
}
2025-05-03 03:50:06 +03:00
function checkMissing(table: string, name: string, ids1: string[], ids2: string[]) {
2017-12-12 23:16:38 -05:00
const missing = ids1.filter(item => ids2.indexOf(item) < 0);
if (missing.length > 0) {
console.log("Missing IDs from " + name + " table " + table + ": ", missing);
}
}
2025-05-03 03:50:06 +03:00
function handleBuffer(obj: { content: Buffer | string }) {
2020-03-11 21:30:33 +01:00
if (obj && Buffer.isBuffer(obj.content)) {
obj.content = obj.content.toString();
}
return obj;
}
2025-05-03 03:50:06 +03:00
function compareRows(table: string, rsLeft: Record<string, any>, rsRight: Record<string, any>, column: string) {
const leftIds = Object.keys(rsLeft);
const rightIds = Object.keys(rsRight);
2021-08-08 12:28:42 +02:00
console.log("");
console.log("--------------------------------------------------------");
2018-12-16 23:17:13 +01:00
console.log(`${table} - ${leftIds.length}/${rightIds.length}`);
checkMissing(table, "right", leftIds, rightIds);
checkMissing(table, "left", rightIds, leftIds);
const commonIds = leftIds.filter(item => rightIds.includes(item));
for (const id of commonIds) {
2020-03-11 21:30:33 +01:00
const valueLeft = handleBuffer(rsLeft[id]);
const valueRight = handleBuffer(rsRight[id]);
2020-03-11 21:23:56 +01:00
const left = JSON.stringify(valueLeft, null, 2);
const right = JSON.stringify(valueRight, null, 2);
if (left !== right) {
console.log("Table " + table + " row with " + column + "=" + id + " differs:");
console.log("Left: ", left);
console.log("Right: ", right);
printDiff(left, right);
2017-11-23 23:19:45 -05:00
}
}
}
async function main() {
2025-05-03 11:15:50 +03:00
const dbLeftPath = process.argv.at(-2);
const dbRightPath = process.argv.at(-1);
if (process.argv.length < 4 || !dbLeftPath || !dbRightPath) {
console.log(`Usage: ${process.argv[0]} ${process.argv[1]} path/to/first.db path/to/second.db`);
process.exit(1);
}
2017-11-23 23:19:45 -05:00
let dbLeft: sqlite.Database;
let dbRight: sqlite.Database;
try {
dbLeft = await sqlite.open({filename: dbLeftPath, driver: sqlite3.Database});
} catch (e: any) {
2025-05-03 11:15:50 +03:00
console.error(`Could not load first database at ${path.resolve(dbRightPath)} due to: ${e.message}`);
process.exit(2);
}
try {
dbRight = await sqlite.open({filename: dbRightPath, driver: sqlite3.Database});
} catch (e: any) {
2025-05-03 11:15:50 +03:00
console.error(`Could not load second database at ${path.resolve(dbRightPath)} due to: ${e.message}`);
process.exit(3);
}
2017-11-23 23:19:45 -05:00
2025-05-03 03:50:06 +03:00
async function compare(table: string, column: string, query: string) {
const rsLeft = await sql.getIndexed(dbLeft, column, query);
const rsRight = await sql.getIndexed(dbRight, column, query);
2017-11-23 23:19:45 -05:00
compareRows(table, rsLeft, rsRight, column);
2017-11-23 23:19:45 -05:00
}
2018-12-16 23:17:13 +01:00
await compare("branches", "branchId",
2021-08-08 12:28:42 +02:00
"SELECT branchId, noteId, parentNoteId, notePosition, utcDateCreated, isDeleted, prefix FROM branches");
2018-12-16 23:17:13 +01:00
await compare("notes", "noteId",
2021-08-08 12:28:42 +02:00
"SELECT noteId, title, dateCreated, utcDateCreated, isProtected, isDeleted FROM notes WHERE isDeleted = 0");
2019-04-11 22:53:35 +02:00
await compare("note_contents", "noteId",
2021-08-08 12:28:42 +02:00
"SELECT note_contents.noteId, note_contents.content FROM note_contents JOIN notes USING(noteId) WHERE isDeleted = 0");
2018-12-16 23:17:13 +01:00
2019-04-14 14:53:05 +02:00
await compare("note_revisions", "noteRevisionId",
2021-08-08 12:28:42 +02:00
"SELECT noteRevisionId, noteId, title, dateCreated, dateLastEdited, utcDateCreated, utcDateLastEdited, isProtected FROM note_revisions");
2018-12-16 23:17:13 +01:00
2019-12-16 21:22:11 +01:00
await compare("note_revision_contents", "noteRevisionId",
2021-08-08 12:28:42 +02:00
"SELECT noteRevisionId, content FROM note_revision_contents");
2018-12-16 23:17:13 +01:00
await compare("options", "name",
2021-08-08 12:28:42 +02:00
`SELECT name, value, utcDateCreated FROM options WHERE isSynced = 1`);
2018-12-16 23:17:13 +01:00
await compare("attributes", "attributeId",
2021-08-08 12:28:42 +02:00
"SELECT attributeId, noteId, type, name, value FROM attributes");
2018-12-16 23:17:13 +01:00
await compare("api_tokens", "apiTokenId",
2021-02-11 21:37:26 +01:00
"SELECT apiTokenId, token, utcDateCreated, isDeleted FROM api_tokens");
await compare("entity_changes", "uniqueId",
"SELECT entityName || '-' || entityId AS uniqueId, hash, isErased, utcDateChanged FROM entity_changes WHERE isSynced = 1");
2017-11-23 23:19:45 -05:00
}
(async () => {
try {
await main();
} catch (e) {
console.error(e);
}
2021-02-11 21:37:26 +01:00
})();