sqlite-hub 0.4.0 → 0.6.0
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/README.md +2 -2
- package/changelog.md +15 -0
- package/frontend/assets/mockups/connections.png +0 -0
- package/frontend/assets/mockups/data.png +0 -0
- package/frontend/assets/mockups/data_row_editor.png +0 -0
- package/frontend/assets/mockups/home.png +0 -0
- package/frontend/assets/mockups/sql_editor.png +0 -0
- package/frontend/assets/mockups/sql_editor_querydetail.png +0 -0
- package/frontend/assets/mockups/structure.png +0 -0
- package/frontend/assets/mockups/structure_inspector.png +0 -0
- package/frontend/js/api.js +114 -5
- package/frontend/js/app.js +368 -18
- package/frontend/js/components/bottomTabs.js +1 -1
- package/frontend/js/components/dataGrid.js +3 -3
- package/frontend/js/components/queryEditor.js +33 -55
- package/frontend/js/components/queryHistoryDetail.js +263 -0
- package/frontend/js/components/queryHistoryPanel.js +228 -0
- package/frontend/js/components/queryResults.js +32 -46
- package/frontend/js/components/rowEditorPanel.js +73 -14
- package/frontend/js/components/sidebar.js +1 -0
- package/frontend/js/components/tableDesignerEditor.js +356 -0
- package/frontend/js/components/tableDesignerSidebar.js +126 -0
- package/frontend/js/components/tableDesignerSqlPreview.js +40 -0
- package/frontend/js/router.js +10 -0
- package/frontend/js/store.js +841 -22
- package/frontend/js/utils/format.js +23 -0
- package/frontend/js/utils/tableDesigner.js +1192 -0
- package/frontend/js/views/data.js +273 -250
- package/frontend/js/views/editor.js +34 -10
- package/frontend/js/views/overview.js +15 -0
- package/frontend/js/views/tableDesigner.js +37 -0
- package/frontend/styles/base.css +87 -73
- package/frontend/styles/components.css +841 -188
- package/frontend/styles/views.css +40 -0
- package/package.json +1 -1
- package/server/routes/data.js +2 -0
- package/server/routes/export.js +4 -1
- package/server/routes/overview.js +12 -0
- package/server/routes/sql.js +163 -5
- package/server/routes/tableDesigner.js +60 -0
- package/server/server.js +5 -1
- package/server/services/sqlite/dataBrowserService.js +4 -16
- package/server/services/sqlite/exportService.js +4 -16
- package/server/services/sqlite/overviewService.js +34 -0
- package/server/services/sqlite/sqlExecutor.js +83 -63
- package/server/services/sqlite/tableDesigner/changeAnalysis.js +295 -0
- package/server/services/sqlite/tableDesigner/schemaMapping.js +233 -0
- package/server/services/sqlite/tableDesigner/sql.js +63 -0
- package/server/services/sqlite/tableDesigner/validation.js +245 -0
- package/server/services/sqlite/tableDesignerService.js +181 -0
- package/server/services/sqlite/tableSort.js +63 -0
- package/server/services/storage/appStateStore.js +674 -1
- package/server/services/storage/queryHistoryUtils.js +169 -0
- package/frontend/assets/mockups/data_edit.png +0 -0
- package/frontend/assets/mockups/graph_visualize.png +0 -0
- package/frontend/assets/mockups/overview.png +0 -0
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
const
|
|
2
|
-
const { ValidationError } = require("../../utils/errors");
|
|
1
|
+
const { ValidationError, mapSqliteError } = require("../../utils/errors");
|
|
3
2
|
const { serializeRows } = require("../../utils/sqliteTypes");
|
|
4
3
|
const { getTableDetail } = require("./introspection");
|
|
5
4
|
|
|
@@ -151,7 +150,7 @@ function splitSqlStatements(sql) {
|
|
|
151
150
|
statements.push(current.trim());
|
|
152
151
|
}
|
|
153
152
|
|
|
154
|
-
return statements.filter((statement) => statement.trim());
|
|
153
|
+
return statements.filter((statement) => statement.trim() && getFirstKeyword(statement));
|
|
155
154
|
}
|
|
156
155
|
|
|
157
156
|
function buildRowIdentity(tableDetail, row) {
|
|
@@ -337,62 +336,85 @@ class SqlExecutor {
|
|
|
337
336
|
const results = [];
|
|
338
337
|
let lastResultSet = null;
|
|
339
338
|
let totalChanges = 0;
|
|
339
|
+
try {
|
|
340
|
+
statements.forEach((statement, index) => {
|
|
341
|
+
const prepared = db.prepare(statement);
|
|
342
|
+
const keyword = getFirstKeyword(statement);
|
|
343
|
+
|
|
344
|
+
if (options.requireReader && !prepared.reader) {
|
|
345
|
+
throw new ValidationError(
|
|
346
|
+
`Statement ${index + 1} is not a result-set statement and cannot be exported.`
|
|
347
|
+
);
|
|
348
|
+
}
|
|
340
349
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
+
if (prepared.reader) {
|
|
351
|
+
const rows = prepared.all();
|
|
352
|
+
const columnDefinitions = prepared.columns();
|
|
353
|
+
const serializedRows = serializeRows(rows);
|
|
354
|
+
const editableResult = resolveEditableResult(db, columnDefinitions, serializedRows);
|
|
355
|
+
const columns = columnDefinitions.map((column) => column.name);
|
|
356
|
+
const result = {
|
|
357
|
+
index,
|
|
358
|
+
sql: statement,
|
|
359
|
+
keyword,
|
|
360
|
+
kind: "resultSet",
|
|
361
|
+
rowCount: serializedRows.length,
|
|
362
|
+
columns,
|
|
363
|
+
rows: editableResult?.rows ?? serializedRows,
|
|
364
|
+
editing: editableResult
|
|
365
|
+
? {
|
|
366
|
+
enabled: editableResult.enabled,
|
|
367
|
+
tableName: editableResult.tableName ?? null,
|
|
368
|
+
reason: editableResult.reason ?? "",
|
|
369
|
+
columns: editableResult.columns ?? [],
|
|
370
|
+
identityStrategy: editableResult.identityStrategy ?? null,
|
|
371
|
+
}
|
|
372
|
+
: null,
|
|
373
|
+
};
|
|
374
|
+
results.push(result);
|
|
375
|
+
lastResultSet = result;
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
350
378
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const serializedRows = serializeRows(rows);
|
|
355
|
-
const editableResult = resolveEditableResult(db, columnDefinitions, serializedRows);
|
|
356
|
-
const columns = columnDefinitions.map((column) => column.name);
|
|
357
|
-
const result = {
|
|
379
|
+
const info = prepared.run();
|
|
380
|
+
totalChanges += info.changes;
|
|
381
|
+
results.push({
|
|
358
382
|
index,
|
|
359
383
|
sql: statement,
|
|
360
384
|
keyword,
|
|
361
|
-
kind: "
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
385
|
+
kind: "mutation",
|
|
386
|
+
changes: info.changes,
|
|
387
|
+
lastInsertRowid:
|
|
388
|
+
keyword === "INSERT" || keyword === "REPLACE"
|
|
389
|
+
? typeof info.lastInsertRowid === "bigint"
|
|
390
|
+
? Number(info.lastInsertRowid)
|
|
391
|
+
: info.lastInsertRowid
|
|
392
|
+
: null,
|
|
393
|
+
});
|
|
394
|
+
});
|
|
395
|
+
} catch (error) {
|
|
396
|
+
const normalizedError = mapSqliteError(error);
|
|
397
|
+
|
|
398
|
+
if (options.persistHistory !== false) {
|
|
399
|
+
try {
|
|
400
|
+
this.appStateStore.recordQueryExecution({
|
|
401
|
+
databaseKey: connection.id,
|
|
402
|
+
rawSql: sql,
|
|
403
|
+
status: "error",
|
|
404
|
+
durationMs: Date.now() - startedAt,
|
|
405
|
+
rowCount: lastResultSet?.rowCount ?? null,
|
|
406
|
+
affectedRows: totalChanges,
|
|
407
|
+
errorMessage: normalizedError.message,
|
|
408
|
+
});
|
|
409
|
+
} catch (recordingError) {
|
|
410
|
+
console.warn(
|
|
411
|
+
`Failed to record SQL query history error for ${connection.id}: ${recordingError.message}`
|
|
412
|
+
);
|
|
413
|
+
}
|
|
378
414
|
}
|
|
379
415
|
|
|
380
|
-
|
|
381
|
-
|
|
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
|
-
});
|
|
416
|
+
throw normalizedError;
|
|
417
|
+
}
|
|
396
418
|
|
|
397
419
|
const timingMs = Date.now() - startedAt;
|
|
398
420
|
const payload = {
|
|
@@ -405,25 +427,23 @@ class SqlExecutor {
|
|
|
405
427
|
affectedRowCount: totalChanges,
|
|
406
428
|
resultKind: lastResultSet ? "resultSet" : results.at(-1)?.kind ?? "unknown",
|
|
407
429
|
};
|
|
430
|
+
let historyId = null;
|
|
408
431
|
|
|
409
432
|
if (options.persistHistory !== false) {
|
|
410
|
-
this.appStateStore.
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
affectedRowCount: totalChanges,
|
|
418
|
-
rowCount: lastResultSet?.rowCount ?? 0,
|
|
419
|
-
timingMs,
|
|
420
|
-
executedAt: new Date().toISOString(),
|
|
433
|
+
historyId = this.appStateStore.recordQueryExecution({
|
|
434
|
+
databaseKey: connection.id,
|
|
435
|
+
rawSql: sql,
|
|
436
|
+
status: "success",
|
|
437
|
+
durationMs: timingMs,
|
|
438
|
+
rowCount: lastResultSet?.rowCount ?? null,
|
|
439
|
+
affectedRows: totalChanges,
|
|
421
440
|
});
|
|
422
441
|
}
|
|
423
442
|
|
|
424
443
|
return {
|
|
425
444
|
...payload,
|
|
426
445
|
timingMs,
|
|
446
|
+
historyId,
|
|
427
447
|
};
|
|
428
448
|
}
|
|
429
449
|
}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
const {
|
|
2
|
+
buildAlterTableAddColumnSql,
|
|
3
|
+
buildAlterTableRenameSql,
|
|
4
|
+
buildCreateTableSql,
|
|
5
|
+
} = require("./sql");
|
|
6
|
+
const { createDesignerWarning } = require("./schemaMapping");
|
|
7
|
+
const { normalizeIdentifierKey, normalizeSqlFragment } = require("./validation");
|
|
8
|
+
|
|
9
|
+
function normalizeComparableValue(value) {
|
|
10
|
+
return normalizeSqlFragment(value);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function hasColumnChanged(originalColumn, draftColumn) {
|
|
14
|
+
return (
|
|
15
|
+
normalizeIdentifierKey(originalColumn.name) !== normalizeIdentifierKey(draftColumn.name) ||
|
|
16
|
+
normalizeComparableValue(originalColumn.type) !== normalizeComparableValue(draftColumn.type) ||
|
|
17
|
+
Boolean(originalColumn.notNull) !== Boolean(draftColumn.notNull) ||
|
|
18
|
+
Boolean(originalColumn.unique) !== Boolean(draftColumn.unique) ||
|
|
19
|
+
Boolean(originalColumn.primaryKey) !== Boolean(draftColumn.primaryKey) ||
|
|
20
|
+
normalizeComparableValue(originalColumn.defaultValue) !==
|
|
21
|
+
normalizeComparableValue(draftColumn.defaultValue) ||
|
|
22
|
+
normalizeIdentifierKey(originalColumn.referencesTable) !==
|
|
23
|
+
normalizeIdentifierKey(draftColumn.referencesTable) ||
|
|
24
|
+
normalizeIdentifierKey(originalColumn.referencesColumn) !==
|
|
25
|
+
normalizeIdentifierKey(draftColumn.referencesColumn)
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function hasMeaningfulNewDraftContent(draft) {
|
|
30
|
+
return (
|
|
31
|
+
Boolean(String(draft.tableName ?? "").trim()) ||
|
|
32
|
+
draft.columns.some((column) =>
|
|
33
|
+
!column.deleted &&
|
|
34
|
+
[
|
|
35
|
+
Boolean(String(column.name ?? "").trim()),
|
|
36
|
+
Boolean(String(column.defaultValue ?? "").trim()),
|
|
37
|
+
Boolean(String(column.referencesTable ?? "").trim()),
|
|
38
|
+
Boolean(String(column.referencesColumn ?? "").trim()),
|
|
39
|
+
Boolean(column.notNull),
|
|
40
|
+
Boolean(column.unique),
|
|
41
|
+
Boolean(column.primaryKey),
|
|
42
|
+
String(column.type ?? "").trim().toUpperCase() !== "TEXT",
|
|
43
|
+
].some(Boolean)
|
|
44
|
+
)
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isAlterAddColumnSafe(column) {
|
|
49
|
+
const defaultValue = normalizeComparableValue(column.defaultValue);
|
|
50
|
+
const normalizedDefault = defaultValue.toUpperCase();
|
|
51
|
+
|
|
52
|
+
if (column.primaryKey) {
|
|
53
|
+
return {
|
|
54
|
+
safe: false,
|
|
55
|
+
reason: `Column ${column.name} cannot be added with PRIMARY KEY via ALTER TABLE in SQLite.`,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (column.unique) {
|
|
60
|
+
return {
|
|
61
|
+
safe: false,
|
|
62
|
+
reason: `Column ${column.name} cannot be added with UNIQUE via ALTER TABLE in SQLite.`,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (column.notNull && !defaultValue) {
|
|
67
|
+
return {
|
|
68
|
+
safe: false,
|
|
69
|
+
reason:
|
|
70
|
+
`Column ${column.name} uses NOT NULL without a default value. SQLite requires a table rebuild for that change.`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (["CURRENT_TIME", "CURRENT_DATE", "CURRENT_TIMESTAMP"].includes(normalizedDefault)) {
|
|
75
|
+
return {
|
|
76
|
+
safe: false,
|
|
77
|
+
reason:
|
|
78
|
+
`Column ${column.name} uses a dynamic default value that SQLite does not allow in ALTER TABLE ADD COLUMN.`,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (/^\(.+\)$/.test(defaultValue)) {
|
|
83
|
+
return {
|
|
84
|
+
safe: false,
|
|
85
|
+
reason:
|
|
86
|
+
`Column ${column.name} uses an expression default value. SQLite requires a rebuild for that ADD COLUMN operation.`,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (
|
|
91
|
+
column.referencesTable &&
|
|
92
|
+
column.referencesColumn &&
|
|
93
|
+
defaultValue &&
|
|
94
|
+
normalizedDefault !== "NULL"
|
|
95
|
+
) {
|
|
96
|
+
return {
|
|
97
|
+
safe: false,
|
|
98
|
+
reason:
|
|
99
|
+
`Column ${column.name} adds a foreign key with a non-NULL default value. SQLite requires a table rebuild for that change.`,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
safe: true,
|
|
105
|
+
reason: "",
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function buildRiskyChangeWarning(title, message) {
|
|
110
|
+
return createDesignerWarning({
|
|
111
|
+
code: "TABLE_REBUILD_REQUIRED",
|
|
112
|
+
title,
|
|
113
|
+
message,
|
|
114
|
+
blocking: true,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function analyzeEditDraft(draft, originalDraft) {
|
|
119
|
+
const originalColumnsByName = new Map(
|
|
120
|
+
originalDraft.columns.map((column) => [normalizeIdentifierKey(column.originalName || column.name), column])
|
|
121
|
+
);
|
|
122
|
+
const matchedOriginalNames = new Set();
|
|
123
|
+
const newColumns = [];
|
|
124
|
+
const warnings = [];
|
|
125
|
+
const executableStatements = [];
|
|
126
|
+
|
|
127
|
+
draft.columns.forEach((column) => {
|
|
128
|
+
if (column.deleted) {
|
|
129
|
+
if (normalizeIdentifierKey(column.originalName || column.name)) {
|
|
130
|
+
warnings.push(
|
|
131
|
+
buildRiskyChangeWarning(
|
|
132
|
+
"Column Delete Requires Rebuild",
|
|
133
|
+
`Deleting column ${column.originalName || column.name} requires a SQLite table rebuild.`
|
|
134
|
+
)
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const originalNameKey = normalizeIdentifierKey(column.originalName);
|
|
142
|
+
|
|
143
|
+
if (originalNameKey && originalColumnsByName.has(originalNameKey)) {
|
|
144
|
+
const originalColumn = originalColumnsByName.get(originalNameKey);
|
|
145
|
+
matchedOriginalNames.add(originalNameKey);
|
|
146
|
+
|
|
147
|
+
if (hasColumnChanged(originalColumn, column)) {
|
|
148
|
+
if (normalizeIdentifierKey(originalColumn.name) !== normalizeIdentifierKey(column.name)) {
|
|
149
|
+
warnings.push(
|
|
150
|
+
buildRiskyChangeWarning(
|
|
151
|
+
"Column Rename Requires Rebuild",
|
|
152
|
+
`Renaming column ${originalColumn.name} to ${column.name} is intentionally blocked in Table Designer v1.`
|
|
153
|
+
)
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (normalizeComparableValue(originalColumn.type) !== normalizeComparableValue(column.type)) {
|
|
158
|
+
warnings.push(
|
|
159
|
+
buildRiskyChangeWarning(
|
|
160
|
+
"Column Type Change Requires Rebuild",
|
|
161
|
+
`Changing the type of ${originalColumn.name} from ${originalColumn.type} to ${column.type} requires a SQLite table rebuild.`
|
|
162
|
+
)
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (Boolean(originalColumn.notNull) !== Boolean(column.notNull)) {
|
|
167
|
+
warnings.push(
|
|
168
|
+
buildRiskyChangeWarning(
|
|
169
|
+
"NOT NULL Change Requires Rebuild",
|
|
170
|
+
`Changing the NOT NULL setting on ${originalColumn.name} requires a SQLite table rebuild.`
|
|
171
|
+
)
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (Boolean(originalColumn.unique) !== Boolean(column.unique)) {
|
|
176
|
+
warnings.push(
|
|
177
|
+
buildRiskyChangeWarning(
|
|
178
|
+
"UNIQUE Change Requires Rebuild",
|
|
179
|
+
`Changing the UNIQUE setting on ${originalColumn.name} requires a SQLite table rebuild.`
|
|
180
|
+
)
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (Boolean(originalColumn.primaryKey) !== Boolean(column.primaryKey)) {
|
|
185
|
+
warnings.push(
|
|
186
|
+
buildRiskyChangeWarning(
|
|
187
|
+
"Primary Key Change Requires Rebuild",
|
|
188
|
+
`Changing the primary key definition on ${originalColumn.name} requires a SQLite table rebuild.`
|
|
189
|
+
)
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (
|
|
194
|
+
normalizeComparableValue(originalColumn.defaultValue) !==
|
|
195
|
+
normalizeComparableValue(column.defaultValue)
|
|
196
|
+
) {
|
|
197
|
+
warnings.push(
|
|
198
|
+
buildRiskyChangeWarning(
|
|
199
|
+
"Default Value Change Requires Rebuild",
|
|
200
|
+
`Changing the default value on ${originalColumn.name} requires a SQLite table rebuild.`
|
|
201
|
+
)
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (
|
|
206
|
+
normalizeIdentifierKey(originalColumn.referencesTable) !==
|
|
207
|
+
normalizeIdentifierKey(column.referencesTable) ||
|
|
208
|
+
normalizeIdentifierKey(originalColumn.referencesColumn) !==
|
|
209
|
+
normalizeIdentifierKey(column.referencesColumn)
|
|
210
|
+
) {
|
|
211
|
+
warnings.push(
|
|
212
|
+
buildRiskyChangeWarning(
|
|
213
|
+
"Foreign Key Change Requires Rebuild",
|
|
214
|
+
`Changing the foreign key on ${originalColumn.name} requires a SQLite table rebuild.`
|
|
215
|
+
)
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
newColumns.push(column);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
originalDraft.columns.forEach((column) => {
|
|
227
|
+
const key = normalizeIdentifierKey(column.originalName || column.name);
|
|
228
|
+
const isExplicitlyDeleted = draft.columns.some(
|
|
229
|
+
(draftColumn) =>
|
|
230
|
+
draftColumn.deleted &&
|
|
231
|
+
normalizeIdentifierKey(draftColumn.originalName || draftColumn.name) === key
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
if (!matchedOriginalNames.has(key) && !isExplicitlyDeleted) {
|
|
235
|
+
warnings.push(
|
|
236
|
+
buildRiskyChangeWarning(
|
|
237
|
+
"Column Delete Requires Rebuild",
|
|
238
|
+
`Deleting column ${column.name} requires a SQLite table rebuild.`
|
|
239
|
+
)
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const tableRenameChanged =
|
|
245
|
+
normalizeIdentifierKey(draft.tableName) !== normalizeIdentifierKey(originalDraft.tableName);
|
|
246
|
+
|
|
247
|
+
if (tableRenameChanged) {
|
|
248
|
+
executableStatements.push(
|
|
249
|
+
buildAlterTableRenameSql(originalDraft.tableName, draft.tableName)
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
newColumns.forEach((column) => {
|
|
254
|
+
const addColumnSafety = isAlterAddColumnSafe(column);
|
|
255
|
+
|
|
256
|
+
if (!addColumnSafety.safe) {
|
|
257
|
+
warnings.push(
|
|
258
|
+
buildRiskyChangeWarning("Column Add Requires Rebuild", addColumnSafety.reason)
|
|
259
|
+
);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
executableStatements.push(
|
|
264
|
+
buildAlterTableAddColumnSql(tableRenameChanged ? draft.tableName : originalDraft.tableName, column)
|
|
265
|
+
);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
const dirty = Boolean(executableStatements.length || warnings.length);
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
dirty,
|
|
272
|
+
executable: warnings.length === 0,
|
|
273
|
+
statements: warnings.length === 0 ? executableStatements : [],
|
|
274
|
+
previewStatements: executableStatements,
|
|
275
|
+
warnings,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function analyzeTableDesignerChanges({ draft, originalDraft = null }) {
|
|
280
|
+
if (!originalDraft) {
|
|
281
|
+
return {
|
|
282
|
+
dirty: hasMeaningfulNewDraftContent(draft),
|
|
283
|
+
executable: true,
|
|
284
|
+
statements: [buildCreateTableSql(draft)],
|
|
285
|
+
previewStatements: [buildCreateTableSql(draft)],
|
|
286
|
+
warnings: [],
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return analyzeEditDraft(draft, originalDraft);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
module.exports = {
|
|
294
|
+
analyzeTableDesignerChanges,
|
|
295
|
+
};
|