mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-27 18:12:29 +08:00
Add '_regroup/trilium-db-compare/' from commit '22d59127f91a588dc5ce9555b7551e428df6a97a'
git-subtree-dir: _regroup/trilium-db-compare git-subtree-mainline: 3e992736d5eb745510e1caaeeca85ef8e6e10d97 git-subtree-split: 22d59127f91a588dc5ce9555b7551e428df6a97a
This commit is contained in:
commit
10319065ee
61
_regroup/trilium-db-compare/.gitignore
vendored
Normal file
61
_regroup/trilium-db-compare/.gitignore
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Typescript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
.idea
|
||||
|
116
_regroup/trilium-db-compare/compare.js
Normal file
116
_regroup/trilium-db-compare/compare.js
Normal file
@ -0,0 +1,116 @@
|
||||
"use strict";
|
||||
|
||||
require('colors');
|
||||
const jsDiff = require('diff');
|
||||
const sqlite = require('sqlite');
|
||||
const sqlite3 = require('sqlite3');
|
||||
const sql = require('./sql');
|
||||
|
||||
function printDiff(one, two) {
|
||||
const diff = jsDiff.diffChars(one, two);
|
||||
|
||||
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]);
|
||||
});
|
||||
|
||||
console.log("");
|
||||
}
|
||||
|
||||
function checkMissing(table, name, ids1, ids2) {
|
||||
const missing = ids1.filter(item => ids2.indexOf(item) < 0);
|
||||
|
||||
if (missing.length > 0) {
|
||||
console.log("Missing IDs from " + name + " table " + table + ": ", missing);
|
||||
}
|
||||
}
|
||||
|
||||
function handleBuffer(obj) {
|
||||
if (obj && Buffer.isBuffer(obj.content)) {
|
||||
obj.content = obj.content.toString();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
function compareRows(table, rsLeft, rsRight, column) {
|
||||
const leftIds = Object.keys(rsLeft);
|
||||
const rightIds = Object.keys(rsRight);
|
||||
|
||||
console.log("");
|
||||
console.log("--------------------------------------------------------");
|
||||
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) {
|
||||
const valueLeft = handleBuffer(rsLeft[id]);
|
||||
const valueRight = handleBuffer(rsRight[id]);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const dbLeftPath = process.argv[2];
|
||||
const dbRightPath = process.argv[3];
|
||||
|
||||
const dbLeft = await sqlite.open({filename: dbLeftPath, driver: sqlite3.Database});
|
||||
const dbRight = await sqlite.open({filename: dbRightPath, driver: sqlite3.Database});
|
||||
|
||||
async function compare(table, column, query) {
|
||||
const rsLeft = await sql.getIndexed(dbLeft, column, query);
|
||||
const rsRight = await sql.getIndexed(dbRight, column, query);
|
||||
|
||||
compareRows(table, rsLeft, rsRight, column);
|
||||
}
|
||||
|
||||
await compare("branches", "branchId",
|
||||
"SELECT branchId, noteId, parentNoteId, notePosition, utcDateCreated, isDeleted, prefix FROM branches");
|
||||
|
||||
await compare("notes", "noteId",
|
||||
"SELECT noteId, title, dateCreated, utcDateCreated, isProtected, isDeleted FROM notes WHERE isDeleted = 0");
|
||||
|
||||
await compare("note_contents", "noteId",
|
||||
"SELECT note_contents.noteId, note_contents.content FROM note_contents JOIN notes USING(noteId) WHERE isDeleted = 0");
|
||||
|
||||
await compare("note_revisions", "noteRevisionId",
|
||||
"SELECT noteRevisionId, noteId, title, dateCreated, dateLastEdited, utcDateCreated, utcDateLastEdited, isProtected FROM note_revisions");
|
||||
|
||||
await compare("note_revision_contents", "noteRevisionId",
|
||||
"SELECT noteRevisionId, content FROM note_revision_contents");
|
||||
|
||||
await compare("options", "name",
|
||||
`SELECT name, value, utcDateCreated FROM options WHERE isSynced = 1`);
|
||||
|
||||
await compare("attributes", "attributeId",
|
||||
"SELECT attributeId, noteId, type, name, value FROM attributes");
|
||||
|
||||
await compare("api_tokens", "apiTokenId",
|
||||
"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");
|
||||
}
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
await main();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
})();
|
2106
_regroup/trilium-db-compare/package-lock.json
generated
Normal file
2106
_regroup/trilium-db-compare/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
11
_regroup/trilium-db-compare/package.json
Normal file
11
_regroup/trilium-db-compare/package.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "trilium-db-compare",
|
||||
"description": "Trilium database compare tool",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"colors": "1.4.0",
|
||||
"diff": "5.0.0",
|
||||
"sqlite": "4.0.23",
|
||||
"sqlite3": "5.0.2"
|
||||
}
|
||||
}
|
89
_regroup/trilium-db-compare/sql.js
Normal file
89
_regroup/trilium-db-compare/sql.js
Normal file
@ -0,0 +1,89 @@
|
||||
"use strict";
|
||||
|
||||
async function getSingleResult(db, query, params = []) {
|
||||
return await wrap(db, async db => db.get(query, ...params));
|
||||
}
|
||||
|
||||
async function getSingleResultOrNull(db, query, params = []) {
|
||||
const all = await wrap(db, async db => db.all(query, ...params));
|
||||
|
||||
return all.length > 0 ? all[0] : null;
|
||||
}
|
||||
|
||||
async function getSingleValue(db, query, params = []) {
|
||||
const row = await getSingleResultOrNull(db, query, params);
|
||||
|
||||
if (!row) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return row[Object.keys(row)[0]];
|
||||
}
|
||||
|
||||
async function getResults(db, query, params = []) {
|
||||
return await wrap(db, async db => db.all(query, ...params));
|
||||
}
|
||||
|
||||
async function getIndexed(db, column, query, params = []) {
|
||||
const results = await getResults(db, query, params);
|
||||
|
||||
const map = {};
|
||||
|
||||
for (const row of results) {
|
||||
map[row[column]] = row;
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
async function getMap(db, query, params = []) {
|
||||
const map = {};
|
||||
const results = await getResults(db, query, params);
|
||||
|
||||
for (const row of results) {
|
||||
const keys = Object.keys(row);
|
||||
|
||||
map[row[keys[0]]] = row[keys[1]];
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
async function getFlattenedResults(db, key, query, params = []) {
|
||||
const list = [];
|
||||
const result = await getResults(db, query, params);
|
||||
|
||||
for (const row of result) {
|
||||
list.push(row[key]);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
async function execute(db, query, params = []) {
|
||||
return await wrap(db, async db => db.run(query, ...params));
|
||||
}
|
||||
|
||||
async function wrap(db, func) {
|
||||
const thisError = new Error();
|
||||
|
||||
try {
|
||||
return await func(db);
|
||||
}
|
||||
catch (e) {
|
||||
console.error("Error executing query. Inner exception: " + e.stack + thisError.stack);
|
||||
|
||||
throw thisError;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getSingleValue,
|
||||
getSingleResult,
|
||||
getSingleResultOrNull,
|
||||
getResults,
|
||||
getIndexed,
|
||||
getMap,
|
||||
getFlattenedResults,
|
||||
execute
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user