This repository was archived by the owner on Oct 27, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
executable file
·179 lines (151 loc) · 5.47 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#!/usr/bin/env node
const fs = require("fs");
const Database = require("better-sqlite3");
const yargs = require("yargs/yargs");
const { hideBin } = require("yargs/helpers");
const vargs = yargs(hideBin(process.argv))
.option("input", {
alias: "i",
type: "string",
description: "The input sqlite file to fix",
})
.option("output", {
alias: "o",
type: "string",
description: "The output sqlite file",
})
.option("check-integrity", {
alias: "c",
type: "boolean",
description: "Check the integrity of the sqlite file",
})
.parse();
function fatal(msg) {
console.log(msg);
process.exit(1);
}
function getTables(db) {
const query = "SELECT name FROM sqlite_master WHERE type='table'";
const statement = db.prepare(query);
const tables = statement.all();
return tables.map((table) => table.name);
}
function processTable(srcDb, destDb, tableName) {
console.log(`Processing table: ${tableName}`);
const schema = srcDb
.prepare(`SELECT sql FROM sqlite_master WHERE type='table' AND name=?`)
.get(tableName);
// Create the table in the destination database with the same schema
destDb.exec(schema.sql);
// Prepare SELECT and INSERT statements
const selectStmt = srcDb.prepare(`SELECT * FROM ${tableName}`);
const columnNames = selectStmt.columns().map((column) => column.name);
const insertStmt = destDb.prepare(
`INSERT INTO ${tableName} (${columnNames.join(
", "
)}) VALUES (${columnNames.map(() => "?").join(", ")})`
);
// Begin a transaction in the destination database
const insertTransaction = destDb.transaction(() => {
for (const row of selectStmt.iterate()) {
console.log(`Inserting row with ID ${row.ID}`);
let toInsert = row.json;
let oldInsert = row.json;
while (true) {
try {
const tmp = JSON.parse(toInsert);
if (typeof tmp == "object" || Array.isArray(tmp)) {
break;
} else if (typeof tmp == "string") {
oldInsert = toInsert;
toInsert = tmp;
} else if (typeof tmp == "number") {
if (tmp > Number.MAX_SAFE_INTEGER) {
// restore because it's too big
toInsert = oldInsert;
}
break;
} else if (typeof tmp == "boolean") {
break;
} else {
fatal(`Unknown type: ${typeof tmp}`);
}
} catch (e) {
// restore last previous string
toInsert = oldInsert;
break;
}
}
if (typeof toInsert == "number" || typeof toInsert == "string") {
if (toInsert > Number.MAX_SAFE_INTEGER) {
fatal(`Number too big: ${toInsert}`);
}
}
row.json = toInsert;
insertStmt.run(Object.values(row));
}
});
// Increase the timeout for the transaction (in milliseconds)
destDb.pragma("busy_timeout = 60000"); // 60 seconds
// Execute the transaction
insertTransaction();
}
function checkIntegrity(srcDb, destDb, tables) {
for (const tableName of tables) {
console.log(`Checking integrity for table: ${tableName}`);
// Prepare SELECT statements
const srcSelectStmt = srcDb.prepare(`SELECT * FROM ${tableName}`);
const destSelectStmt = destDb.prepare(
`SELECT * FROM ${tableName} WHERE ID = ?`
);
// Iterate over all rows in the source table
for (const srcRow of srcSelectStmt.iterate()) {
// Fetch the corresponding row from the destination table using the primary key (assuming 'id' as primary key)
const destRow = destSelectStmt.get(srcRow.ID);
// Check if the row exists in the destination table and is not null
if (destRow && destRow !== null) {
console.log(`Row with ID ${srcRow.ID} exists in both tables.`);
} else {
fatal(
`Row with ID ${srcRow.ID} is missing or null in the destination table.`
);
}
}
}
}
function main() {
if (!vargs.input) {
fatal("Missing input");
}
if (!vargs.output) {
fatal("Missing output");
}
if (vargs.input == vargs.output) {
fatal("Output cannot be the same as input");
}
if (!fs.existsSync(vargs.input)) {
fatal(`${vargs.input}: file doesn't exist`);
}
if (fs.existsSync(vargs.output)) {
fatal(`output file already exist: ${vargs.output}`);
}
console.log(`Loading ${vargs.input} file`);
const db = new Database(vargs.input);
console.log("Sqlite loaded");
console.log(`Creating output sqlite file: ${vargs.output}`);
const dbOut = new Database(vargs.output);
console.log("Getting tables");
const tables = getTables(db);
console.log(`Tables found: [${tables.join(", ")}]`);
for (const table of tables) {
processTable(db, dbOut, table);
}
console.log("Done!");
if (vargs["check-integrity"]) {
checkIntegrity(db, dbOut, tables);
console.log("Done!");
}
db.close();
dbOut.close();
}
main();