sqlite-hub 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.npmingnore +4 -0
- package/README.md +46 -0
- package/assets/images/logo.webp +0 -0
- package/assets/images/logo_extrasmall.webp +0 -0
- package/assets/images/logo_raw.png +0 -0
- package/assets/images/logo_small.webp +0 -0
- package/assets/mockups/connections.png +0 -0
- package/assets/mockups/data.png +0 -0
- package/assets/mockups/data_edit.png +0 -0
- package/assets/mockups/home.png +0 -0
- package/assets/mockups/overview.png +0 -0
- package/assets/mockups/sql_editor.png +0 -0
- package/assets/mockups/structure.png +0 -0
- package/bin/sqlite-hub.js +116 -0
- package/changelog.md +3 -0
- package/data/.gitkeep +0 -0
- package/index.html +100 -0
- package/js/api.js +193 -0
- package/js/app.js +520 -0
- package/js/components/actionBar.js +8 -0
- package/js/components/appShell.js +17 -0
- package/js/components/badges.js +5 -0
- package/js/components/bottomTabs.js +37 -0
- package/js/components/connectionCard.js +106 -0
- package/js/components/dataGrid.js +47 -0
- package/js/components/emptyState.js +159 -0
- package/js/components/metricCard.js +32 -0
- package/js/components/modal.js +317 -0
- package/js/components/pageHeader.js +33 -0
- package/js/components/queryEditor.js +121 -0
- package/js/components/queryResults.js +107 -0
- package/js/components/rowEditorPanel.js +164 -0
- package/js/components/sidebar.js +57 -0
- package/js/components/statusBar.js +39 -0
- package/js/components/toast.js +39 -0
- package/js/components/topNav.js +27 -0
- package/js/router.js +66 -0
- package/js/store.js +1092 -0
- package/js/utils/format.js +179 -0
- package/js/views/connections.js +133 -0
- package/js/views/data.js +400 -0
- package/js/views/editor.js +259 -0
- package/js/views/landing.js +11 -0
- package/js/views/overview.js +220 -0
- package/js/views/settings.js +109 -0
- package/js/views/structure.js +242 -0
- package/package.json +18 -0
- package/publish_brew.sh +444 -0
- package/publish_npm.sh +241 -0
- package/server/routes/connections.js +146 -0
- package/server/routes/data.js +59 -0
- package/server/routes/export.js +25 -0
- package/server/routes/overview.js +39 -0
- package/server/routes/settings.js +50 -0
- package/server/routes/sql.js +50 -0
- package/server/routes/structure.js +38 -0
- package/server/server.js +136 -0
- package/server/services/sqlite/connectionManager.js +306 -0
- package/server/services/sqlite/dataBrowserService.js +255 -0
- package/server/services/sqlite/exportService.js +34 -0
- package/server/services/sqlite/importService.js +111 -0
- package/server/services/sqlite/introspection.js +302 -0
- package/server/services/sqlite/overviewService.js +109 -0
- package/server/services/sqlite/sqlExecutor.js +434 -0
- package/server/services/sqlite/structureService.js +60 -0
- package/server/services/storage/appStateStore.js +530 -0
- package/server/utils/csv.js +34 -0
- package/server/utils/errors.js +175 -0
- package/server/utils/fileValidation.js +135 -0
- package/server/utils/identifier.js +38 -0
- package/server/utils/sqliteTypes.js +112 -0
- package/styles/base.css +176 -0
- package/styles/components.css +323 -0
- package/styles/layout.css +101 -0
- package/styles/tokens.css +49 -0
- package/styles/views.css +84 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
const crypto = require("node:crypto");
|
|
2
|
+
const { ValidationError } = require("../../utils/errors");
|
|
3
|
+
const { serializeRows } = require("../../utils/sqliteTypes");
|
|
4
|
+
const { getTableDetail } = require("./introspection");
|
|
5
|
+
|
|
6
|
+
function getFirstKeyword(statement) {
|
|
7
|
+
const trimmed = statement.trim().replace(/^--.*$/gm, "").trim();
|
|
8
|
+
const match = trimmed.match(/^[A-Za-z]+/);
|
|
9
|
+
return match ? match[0].toUpperCase() : "";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function splitSqlStatements(sql) {
|
|
13
|
+
const statements = [];
|
|
14
|
+
let current = "";
|
|
15
|
+
let inSingleQuote = false;
|
|
16
|
+
let inDoubleQuote = false;
|
|
17
|
+
let inBacktick = false;
|
|
18
|
+
let inBracket = false;
|
|
19
|
+
let inLineComment = false;
|
|
20
|
+
let inBlockComment = false;
|
|
21
|
+
let token = "";
|
|
22
|
+
let recentTokens = [];
|
|
23
|
+
let inTrigger = false;
|
|
24
|
+
let blockDepth = 0;
|
|
25
|
+
|
|
26
|
+
function pushToken() {
|
|
27
|
+
if (!token) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const upperToken = token.toUpperCase();
|
|
32
|
+
recentTokens.push(upperToken);
|
|
33
|
+
recentTokens = recentTokens.slice(-4);
|
|
34
|
+
|
|
35
|
+
const looksLikeCreateTrigger =
|
|
36
|
+
upperToken === "TRIGGER" &&
|
|
37
|
+
recentTokens.includes("CREATE");
|
|
38
|
+
|
|
39
|
+
if (looksLikeCreateTrigger) {
|
|
40
|
+
inTrigger = true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (inTrigger) {
|
|
44
|
+
if (upperToken === "BEGIN" || upperToken === "CASE") {
|
|
45
|
+
blockDepth += 1;
|
|
46
|
+
} else if (upperToken === "END" && blockDepth > 0) {
|
|
47
|
+
blockDepth -= 1;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
token = "";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
for (let index = 0; index < sql.length; index += 1) {
|
|
55
|
+
const char = sql[index];
|
|
56
|
+
const nextChar = sql[index + 1];
|
|
57
|
+
|
|
58
|
+
current += char;
|
|
59
|
+
|
|
60
|
+
if (inLineComment) {
|
|
61
|
+
if (char === "\n") {
|
|
62
|
+
inLineComment = false;
|
|
63
|
+
}
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (inBlockComment) {
|
|
68
|
+
if (char === "*" && nextChar === "/") {
|
|
69
|
+
current += nextChar;
|
|
70
|
+
index += 1;
|
|
71
|
+
inBlockComment = false;
|
|
72
|
+
}
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!inSingleQuote && !inDoubleQuote && !inBacktick && !inBracket) {
|
|
77
|
+
if (char === "-" && nextChar === "-") {
|
|
78
|
+
inLineComment = true;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (char === "/" && nextChar === "*") {
|
|
83
|
+
inBlockComment = true;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (char === "'" && !inDoubleQuote && !inBacktick && !inBracket) {
|
|
89
|
+
inSingleQuote = !inSingleQuote;
|
|
90
|
+
pushToken();
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (char === '"' && !inSingleQuote && !inBacktick && !inBracket) {
|
|
95
|
+
inDoubleQuote = !inDoubleQuote;
|
|
96
|
+
pushToken();
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (char === "`" && !inSingleQuote && !inDoubleQuote && !inBracket) {
|
|
101
|
+
inBacktick = !inBacktick;
|
|
102
|
+
pushToken();
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (char === "[" && !inSingleQuote && !inDoubleQuote && !inBacktick) {
|
|
107
|
+
inBracket = true;
|
|
108
|
+
pushToken();
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (char === "]" && inBracket) {
|
|
113
|
+
inBracket = false;
|
|
114
|
+
pushToken();
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (inSingleQuote || inDoubleQuote || inBacktick || inBracket) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (/[A-Za-z_]/.test(char)) {
|
|
123
|
+
token += char;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (token) {
|
|
128
|
+
pushToken();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (char === ";") {
|
|
132
|
+
if (!inTrigger || blockDepth === 0) {
|
|
133
|
+
const statement = current.trim();
|
|
134
|
+
if (statement) {
|
|
135
|
+
statements.push(statement);
|
|
136
|
+
}
|
|
137
|
+
current = "";
|
|
138
|
+
token = "";
|
|
139
|
+
recentTokens = [];
|
|
140
|
+
inTrigger = false;
|
|
141
|
+
blockDepth = 0;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (token) {
|
|
147
|
+
pushToken();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (current.trim()) {
|
|
151
|
+
statements.push(current.trim());
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return statements.filter((statement) => statement.trim());
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function buildRowIdentity(tableDetail, row) {
|
|
158
|
+
if (tableDetail.identityStrategy?.type === "rowid") {
|
|
159
|
+
return {
|
|
160
|
+
kind: "rowid",
|
|
161
|
+
values: {
|
|
162
|
+
rowid: row.rowid,
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (tableDetail.identityStrategy?.type === "primaryKey") {
|
|
168
|
+
return {
|
|
169
|
+
kind: "primaryKey",
|
|
170
|
+
columns: tableDetail.identityStrategy.columns,
|
|
171
|
+
values: Object.fromEntries(
|
|
172
|
+
tableDetail.identityStrategy.columns.map((columnName) => [columnName, row[columnName]])
|
|
173
|
+
),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function getEditableResultReason(tableDetail) {
|
|
181
|
+
if (tableDetail.notSafelyUpdatable) {
|
|
182
|
+
return "This result table has no stable identity column, so rows cannot be edited safely.";
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (tableDetail.identityStrategy?.type === "primaryKey") {
|
|
186
|
+
return "Include all primary key columns in the query result to edit rows here.";
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (tableDetail.identityStrategy?.type === "rowid") {
|
|
190
|
+
return "Include rowid in the query result to edit rows for tables without a primary key.";
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return "Only direct single-table SELECT results can be edited here.";
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function mapEditableColumns(tableDetail, columnDefinitions) {
|
|
197
|
+
const tableColumnsByName = new Map(
|
|
198
|
+
tableDetail.columns.map((column) => [column.name, column])
|
|
199
|
+
);
|
|
200
|
+
const identityColumns = new Set(
|
|
201
|
+
tableDetail.identityStrategy?.type === "primaryKey"
|
|
202
|
+
? tableDetail.identityStrategy.columns ?? []
|
|
203
|
+
: tableDetail.identityStrategy?.type === "rowid"
|
|
204
|
+
? ["rowid"]
|
|
205
|
+
: []
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
return columnDefinitions.map((definition) => {
|
|
209
|
+
const isRowId = definition.column === "rowid";
|
|
210
|
+
const columnMeta = isRowId ? null : tableColumnsByName.get(definition.column) ?? null;
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
resultName: definition.name,
|
|
214
|
+
sourceColumn: definition.column,
|
|
215
|
+
sourceTable: definition.table,
|
|
216
|
+
visible: isRowId ? true : Boolean(columnMeta?.visible),
|
|
217
|
+
generated: Boolean(columnMeta?.generated),
|
|
218
|
+
identity: identityColumns.has(definition.column),
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function hasRequiredIdentityColumns(tableDetail, editableColumns) {
|
|
224
|
+
const availableSourceColumns = new Set(editableColumns.map((column) => column.sourceColumn));
|
|
225
|
+
|
|
226
|
+
if (tableDetail.identityStrategy?.type === "primaryKey") {
|
|
227
|
+
return (tableDetail.identityStrategy.columns ?? []).every((columnName) =>
|
|
228
|
+
availableSourceColumns.has(columnName)
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (tableDetail.identityStrategy?.type === "rowid") {
|
|
233
|
+
return availableSourceColumns.has("rowid");
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function buildIdentityRowFromResult(editableColumns, row) {
|
|
240
|
+
return Object.fromEntries(
|
|
241
|
+
editableColumns.map((column) => [column.sourceColumn, row[column.resultName]])
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function resolveEditableResult(db, columnDefinitions, serializedRows) {
|
|
246
|
+
if (!columnDefinitions.length) {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const directColumns = columnDefinitions.filter(
|
|
251
|
+
(definition) => definition.table && definition.column && definition.database === "main"
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
if (directColumns.length !== columnDefinitions.length) {
|
|
255
|
+
return {
|
|
256
|
+
enabled: false,
|
|
257
|
+
reason: "Only direct single-table SELECT results can be edited here.",
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const [firstColumn] = directColumns;
|
|
262
|
+
const sameSourceTable = directColumns.every(
|
|
263
|
+
(definition) =>
|
|
264
|
+
definition.table === firstColumn.table && definition.database === firstColumn.database
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
if (!sameSourceTable) {
|
|
268
|
+
return {
|
|
269
|
+
enabled: false,
|
|
270
|
+
reason: "Only direct single-table SELECT results can be edited here.",
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const tableDetail = getTableDetail(db, firstColumn.table, {
|
|
275
|
+
includeRowCount: false,
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
if (tableDetail.type !== "table") {
|
|
279
|
+
return {
|
|
280
|
+
enabled: false,
|
|
281
|
+
tableName: firstColumn.table,
|
|
282
|
+
reason: "Only table results can be edited here.",
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const editableColumns = mapEditableColumns(tableDetail, columnDefinitions);
|
|
287
|
+
|
|
288
|
+
if (!hasRequiredIdentityColumns(tableDetail, editableColumns)) {
|
|
289
|
+
return {
|
|
290
|
+
enabled: false,
|
|
291
|
+
tableName: tableDetail.name,
|
|
292
|
+
reason: getEditableResultReason(tableDetail),
|
|
293
|
+
columns: editableColumns,
|
|
294
|
+
identityStrategy: tableDetail.identityStrategy,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const rows = serializedRows.map((row) => {
|
|
299
|
+
const identityRow = buildIdentityRowFromResult(editableColumns, row);
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
...row,
|
|
303
|
+
__identity: buildRowIdentity(tableDetail, identityRow),
|
|
304
|
+
};
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
enabled: true,
|
|
309
|
+
tableName: tableDetail.name,
|
|
310
|
+
reason: "",
|
|
311
|
+
columns: editableColumns,
|
|
312
|
+
identityStrategy: tableDetail.identityStrategy,
|
|
313
|
+
rows,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
class SqlExecutor {
|
|
318
|
+
constructor({ connectionManager, appStateStore }) {
|
|
319
|
+
this.connectionManager = connectionManager;
|
|
320
|
+
this.appStateStore = appStateStore;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
execute(sql, options = {}) {
|
|
324
|
+
if (typeof sql !== "string" || !sql.trim()) {
|
|
325
|
+
throw new ValidationError("SQL text is required.");
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const db = this.connectionManager.getActiveDatabase();
|
|
329
|
+
const connection = this.connectionManager.getActiveConnection();
|
|
330
|
+
const statements = splitSqlStatements(sql);
|
|
331
|
+
|
|
332
|
+
if (statements.length === 0) {
|
|
333
|
+
throw new ValidationError("No executable SQL statements were found.");
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const startedAt = Date.now();
|
|
337
|
+
const results = [];
|
|
338
|
+
let lastResultSet = null;
|
|
339
|
+
let totalChanges = 0;
|
|
340
|
+
|
|
341
|
+
statements.forEach((statement, index) => {
|
|
342
|
+
const prepared = db.prepare(statement);
|
|
343
|
+
const keyword = getFirstKeyword(statement);
|
|
344
|
+
|
|
345
|
+
if (options.requireReader && !prepared.reader) {
|
|
346
|
+
throw new ValidationError(
|
|
347
|
+
`Statement ${index + 1} is not a result-set statement and cannot be exported.`
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (prepared.reader) {
|
|
352
|
+
const rows = prepared.all();
|
|
353
|
+
const columnDefinitions = prepared.columns();
|
|
354
|
+
const serializedRows = serializeRows(rows);
|
|
355
|
+
const editableResult = resolveEditableResult(db, columnDefinitions, serializedRows);
|
|
356
|
+
const columns = columnDefinitions.map((column) => column.name);
|
|
357
|
+
const result = {
|
|
358
|
+
index,
|
|
359
|
+
sql: statement,
|
|
360
|
+
keyword,
|
|
361
|
+
kind: "resultSet",
|
|
362
|
+
rowCount: serializedRows.length,
|
|
363
|
+
columns,
|
|
364
|
+
rows: editableResult?.rows ?? serializedRows,
|
|
365
|
+
editing: editableResult
|
|
366
|
+
? {
|
|
367
|
+
enabled: editableResult.enabled,
|
|
368
|
+
tableName: editableResult.tableName ?? null,
|
|
369
|
+
reason: editableResult.reason ?? "",
|
|
370
|
+
columns: editableResult.columns ?? [],
|
|
371
|
+
identityStrategy: editableResult.identityStrategy ?? null,
|
|
372
|
+
}
|
|
373
|
+
: null,
|
|
374
|
+
};
|
|
375
|
+
results.push(result);
|
|
376
|
+
lastResultSet = result;
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const info = prepared.run();
|
|
381
|
+
totalChanges += info.changes;
|
|
382
|
+
results.push({
|
|
383
|
+
index,
|
|
384
|
+
sql: statement,
|
|
385
|
+
keyword,
|
|
386
|
+
kind: "mutation",
|
|
387
|
+
changes: info.changes,
|
|
388
|
+
lastInsertRowid:
|
|
389
|
+
keyword === "INSERT" || keyword === "REPLACE"
|
|
390
|
+
? typeof info.lastInsertRowid === "bigint"
|
|
391
|
+
? Number(info.lastInsertRowid)
|
|
392
|
+
: info.lastInsertRowid
|
|
393
|
+
: null,
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
const timingMs = Date.now() - startedAt;
|
|
398
|
+
const payload = {
|
|
399
|
+
sql,
|
|
400
|
+
statementCount: statements.length,
|
|
401
|
+
statements: results,
|
|
402
|
+
rows: lastResultSet?.rows ?? [],
|
|
403
|
+
columns: lastResultSet?.columns ?? [],
|
|
404
|
+
editing: lastResultSet?.editing ?? null,
|
|
405
|
+
affectedRowCount: totalChanges,
|
|
406
|
+
resultKind: lastResultSet ? "resultSet" : results.at(-1)?.kind ?? "unknown",
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
if (options.persistHistory !== false) {
|
|
410
|
+
this.appStateStore.addSqlHistory({
|
|
411
|
+
id: crypto.randomUUID(),
|
|
412
|
+
connectionId: connection.id,
|
|
413
|
+
connectionLabel: connection.label,
|
|
414
|
+
sql,
|
|
415
|
+
statementCount: statements.length,
|
|
416
|
+
resultKind: payload.resultKind,
|
|
417
|
+
affectedRowCount: totalChanges,
|
|
418
|
+
rowCount: lastResultSet?.rowCount ?? 0,
|
|
419
|
+
timingMs,
|
|
420
|
+
executedAt: new Date().toISOString(),
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return {
|
|
425
|
+
...payload,
|
|
426
|
+
timingMs,
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
module.exports = {
|
|
432
|
+
SqlExecutor,
|
|
433
|
+
splitSqlStatements,
|
|
434
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const { getRawStructureEntries, getTableDetail } = require("./introspection");
|
|
2
|
+
const { quoteIdentifier } = require("../../utils/identifier");
|
|
3
|
+
const { serializeRows } = require("../../utils/sqliteTypes");
|
|
4
|
+
|
|
5
|
+
class StructureService {
|
|
6
|
+
constructor({ connectionManager, appStateStore }) {
|
|
7
|
+
this.connectionManager = connectionManager;
|
|
8
|
+
this.appStateStore = appStateStore;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
getStructureOverview() {
|
|
12
|
+
const db = this.connectionManager.getActiveDatabase();
|
|
13
|
+
const entries = getRawStructureEntries(db);
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
entries,
|
|
17
|
+
grouped: {
|
|
18
|
+
tables: entries.filter((entry) => entry.type === "table"),
|
|
19
|
+
views: entries.filter((entry) => entry.type === "view"),
|
|
20
|
+
indexes: entries.filter((entry) => entry.type === "index"),
|
|
21
|
+
triggers: entries.filter((entry) => entry.type === "trigger"),
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
getTableStructure(tableName) {
|
|
27
|
+
const db = this.connectionManager.getActiveDatabase();
|
|
28
|
+
const table = getTableDetail(db, tableName);
|
|
29
|
+
const previewLimit = Math.max(1, this.appStateStore.getSettings().defaultPageSize ?? 50);
|
|
30
|
+
const previewStatement = db.prepare(
|
|
31
|
+
`SELECT * FROM ${quoteIdentifier(tableName)} LIMIT ${previewLimit}`
|
|
32
|
+
);
|
|
33
|
+
const previewRows = serializeRows(previewStatement.all());
|
|
34
|
+
const previewColumns = previewStatement.columns().map((column) => column.name);
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
type: table.type,
|
|
38
|
+
name: table.name,
|
|
39
|
+
ddl: table.ddl,
|
|
40
|
+
withoutRowId: table.withoutRowId,
|
|
41
|
+
strict: table.strict,
|
|
42
|
+
columns: table.columns,
|
|
43
|
+
foreignKeys: table.foreignKeys,
|
|
44
|
+
indexes: table.indexes,
|
|
45
|
+
triggers: table.triggers,
|
|
46
|
+
rowCount: table.rowCount,
|
|
47
|
+
preview: {
|
|
48
|
+
limit: previewLimit,
|
|
49
|
+
columns: previewColumns,
|
|
50
|
+
rows: previewRows,
|
|
51
|
+
},
|
|
52
|
+
identityStrategy: table.identityStrategy,
|
|
53
|
+
notSafelyUpdatable: table.notSafelyUpdatable,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = {
|
|
59
|
+
StructureService,
|
|
60
|
+
};
|