sqlite-hub 2.0.1 → 2.2.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 +21 -9
- package/docs/MCP.md +8 -0
- package/docs/changelog.md +16 -0
- package/docs/guidelines/DESIGN.md +64 -0
- package/docs/todo.md +2 -0
- package/frontend/assets/mockups/backups_1_1920.webp +0 -0
- package/frontend/assets/mockups/backups_2_compare_drawer_1920.webp +0 -0
- package/frontend/assets/mockups/backups_3_create_backup_modal_1920.webp +0 -0
- package/frontend/assets/mockups/backups_4_edit_backup_modal_1920.webp +0 -0
- package/frontend/assets/mockups/backups_5_restore_backup_modal_1920.webp +0 -0
- package/frontend/assets/mockups/backups_6_delete_backup_modal_1920.webp +0 -0
- package/frontend/assets/mockups/charts_1_1920.webp +0 -0
- package/frontend/assets/mockups/charts_2_query_detail_1920.webp +0 -0
- package/frontend/assets/mockups/charts_3_create_query_chart_modal_1920.webp +0 -0
- package/frontend/assets/mockups/charts_4_edit_query_chart_modal_1920.webp +0 -0
- package/frontend/assets/mockups/charts_5_delete_query_chart_modal_1920.webp +0 -0
- package/frontend/assets/mockups/charts_6_copy_column_modal_1920.webp +0 -0
- package/frontend/assets/mockups/connections_1_1920.webp +0 -0
- package/frontend/assets/mockups/connections_2_create_connection_modal_1920.webp +0 -0
- package/frontend/assets/mockups/connections_3_open_connection_modal_1920.webp +0 -0
- package/frontend/assets/mockups/data_1_1920.webp +0 -0
- package/frontend/assets/mockups/data_2_roweditor_1920.webp +0 -0
- package/frontend/assets/mockups/data_3_generate_data_modal_1920.webp +0 -0
- package/frontend/assets/mockups/data_4_data_export_modal_1920.webp +0 -0
- package/frontend/assets/mockups/documents_1_1920.webp +0 -0
- package/frontend/assets/mockups/documents_2_document_insert_table_modal_1920.webp +0 -0
- package/frontend/assets/mockups/documents_3_document_insert_note_modal_1920.webp +0 -0
- package/frontend/assets/mockups/logs_1_1920.webp +0 -0
- package/frontend/assets/mockups/media_tagging_queue_1_1920.webp +0 -0
- package/frontend/assets/mockups/media_tagging_setup_1_1920.webp +0 -0
- package/frontend/assets/mockups/media_tagging_setup_2_create_media_tagging_tag_table_modal_1920.webp +0 -0
- package/frontend/assets/mockups/media_tagging_setup_3_create_media_tagging_mapping_table_modal_1920.webp +0 -0
- package/frontend/assets/mockups/overview_1_1920.webp +0 -0
- package/frontend/assets/mockups/settings_1_1920.webp +0 -0
- package/frontend/assets/mockups/sql_editor_1_1920.webp +0 -0
- package/frontend/assets/mockups/sql_editor_2_query_detail_1920.webp +0 -0
- package/frontend/assets/mockups/sql_editor_3_query_export_modal_1920.webp +0 -0
- package/frontend/assets/mockups/structure_1_1920.webp +0 -0
- package/frontend/assets/mockups/structure_2_generate_types_modal_1920.webp +0 -0
- package/frontend/assets/mockups/structure_3_generate_types_modal_1920.webp +0 -0
- package/frontend/assets/mockups/table_designer_1_1920.webp +0 -0
- package/frontend/assets/mockups/table_designer_2_table_designer_constraints_modal_1920.webp +0 -0
- package/frontend/js/api.js +18 -0
- package/frontend/js/app.js +567 -15
- package/frontend/js/components/badges.js +16 -0
- package/frontend/js/components/connectionCard.js +35 -0
- package/frontend/js/components/modal.js +268 -247
- package/frontend/js/components/queryHistoryDetail.js +25 -45
- package/frontend/js/components/rowEditorPanel.js +7 -60
- package/frontend/js/components/structureGraph.js +292 -13
- package/frontend/js/components/tableDesignerConstraintsDrawer.js +400 -0
- package/frontend/js/components/tableDesignerEditor.js +51 -16
- package/frontend/js/components/tableDesignerSidebar.js +9 -5
- package/frontend/js/store.js +1847 -190
- package/frontend/js/utils/connectionRegistry.js +76 -0
- package/frontend/js/utils/markdownDocuments.js +21 -4
- package/frontend/js/utils/tableDesigner.js +431 -62
- package/frontend/js/views/backups.js +2 -5
- package/frontend/js/views/charts.js +20 -10
- package/frontend/js/views/connections.js +101 -6
- package/frontend/js/views/data.js +52 -9
- package/frontend/js/views/documents.js +153 -59
- package/frontend/js/views/structure.js +28 -5
- package/frontend/js/views/tableAdvisor.js +14 -6
- package/frontend/js/views/tableDesigner.js +44 -9
- package/frontend/styles/base.css +9 -0
- package/frontend/styles/components.css +676 -79
- package/frontend/styles/structure-graph.css +49 -0
- package/frontend/styles/tailwind.generated.css +1 -1
- package/frontend/styles/views.css +12 -0
- package/package.json +1 -1
- package/server/routes/backups.js +37 -1
- package/server/routes/charts.js +28 -0
- package/server/routes/connections.js +12 -0
- package/server/routes/data.js +30 -2
- package/server/routes/documents.js +61 -5
- package/server/routes/settings.js +32 -0
- package/server/routes/tableDesigner.js +35 -2
- package/server/server.js +3 -3
- package/server/services/mcpToolService.js +30 -0
- package/server/services/sqlite/connectionManager.js +25 -2
- package/server/services/sqlite/dataBrowserService.js +21 -0
- package/server/services/sqlite/introspection.js +39 -1
- package/server/services/sqlite/overviewService.js +1 -0
- package/server/services/sqlite/structureService.js +66 -11
- package/server/services/sqlite/tableAdvisor.js +6 -0
- package/server/services/sqlite/tableDesigner/schemaMapping.js +7 -1
- package/server/services/sqlite/tableDesigner/sql.js +31 -2
- package/server/services/sqlite/tableDesigner/validation.js +69 -9
- package/server/services/sqlite/tableDesignerService.js +110 -5
- package/server/services/storage/appStateStore.js +590 -10
- package/server/utils/fileValidation.js +5 -0
- package/server/utils/userActionLog.js +49 -0
- package/frontend/assets/mockups/backups_2_create_backup_modal_1920.webp +0 -0
- package/frontend/assets/mockups/backups_3_edit_backup_modal_1920.webp +0 -0
- package/frontend/assets/mockups/backups_4_restore_backup_modal_1920.webp +0 -0
- package/frontend/assets/mockups/backups_5_delete_backup_modal_1920.webp +0 -0
- package/frontend/assets/mockups/data_3_data_export_modal_1920.webp +0 -0
|
@@ -159,6 +159,9 @@ function buildCheckConstraints(tableDetail) {
|
|
|
159
159
|
originalExpression: expression ? `CHECK (${expression})` : "CHECK constraint",
|
|
160
160
|
editable: true,
|
|
161
161
|
preserved: true,
|
|
162
|
+
source: "detected",
|
|
163
|
+
presetId: "",
|
|
164
|
+
presetFields: {},
|
|
162
165
|
};
|
|
163
166
|
});
|
|
164
167
|
}
|
|
@@ -300,7 +303,7 @@ function buildTableDesignerDraft(tableDetail) {
|
|
|
300
303
|
|
|
301
304
|
function listDesignerTables(db) {
|
|
302
305
|
return getRawStructureEntries(db)
|
|
303
|
-
.filter((entry) => entry.type === "table")
|
|
306
|
+
.filter((entry) => entry.type === "table" && !entry.isShadow)
|
|
304
307
|
.map((entry) => {
|
|
305
308
|
const columns = db
|
|
306
309
|
.prepare(`PRAGMA table_xinfo(${quoteIdentifier(entry.name)})`)
|
|
@@ -312,6 +315,9 @@ function listDesignerTables(db) {
|
|
|
312
315
|
name: entry.name,
|
|
313
316
|
columnCount: columns.length,
|
|
314
317
|
columns,
|
|
318
|
+
tableKind: entry.tableKind ?? "table",
|
|
319
|
+
isVirtual: Boolean(entry.isVirtual),
|
|
320
|
+
isShadow: Boolean(entry.isShadow),
|
|
315
321
|
};
|
|
316
322
|
})
|
|
317
323
|
.sort((left, right) => left.name.localeCompare(right.name, undefined, { sensitivity: "base" }));
|
|
@@ -35,10 +35,39 @@ function buildColumnDefinition(column) {
|
|
|
35
35
|
return parts.join(" ");
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
function normalizeCheckExpressionSql(expression) {
|
|
39
|
+
const normalized = normalizeSqlFragment(expression);
|
|
40
|
+
|
|
41
|
+
if (!normalized) {
|
|
42
|
+
return "";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (/^CHECK\s*\(/i.test(normalized)) {
|
|
46
|
+
return normalized;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return `CHECK (${normalized})`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function buildCheckConstraintSql(constraint) {
|
|
53
|
+
if (constraint.deleted) {
|
|
54
|
+
return "";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return normalizeCheckExpressionSql(constraint.expression);
|
|
58
|
+
}
|
|
59
|
+
|
|
38
60
|
function buildCreateTableSql(draft) {
|
|
39
|
-
const columnSql = draft.columns
|
|
61
|
+
const columnSql = draft.columns
|
|
62
|
+
.filter((column) => !column.deleted)
|
|
63
|
+
.map((column) => ` ${buildColumnDefinition(column)}`);
|
|
64
|
+
const checkSql = (draft.checkConstraints ?? [])
|
|
65
|
+
.map(buildCheckConstraintSql)
|
|
66
|
+
.filter(Boolean)
|
|
67
|
+
.map((constraintSql) => ` ${constraintSql}`);
|
|
68
|
+
const definitionSql = [...columnSql, ...checkSql].join(",\n");
|
|
40
69
|
|
|
41
|
-
return `CREATE TABLE ${quoteIdentifier(draft.tableName)} (\n${
|
|
70
|
+
return `CREATE TABLE ${quoteIdentifier(draft.tableName)} (\n${definitionSql}\n);`;
|
|
42
71
|
}
|
|
43
72
|
|
|
44
73
|
function buildAlterTableRenameSql(fromName, toName) {
|
|
@@ -10,6 +10,36 @@ function normalizeSqlFragment(value) {
|
|
|
10
10
|
return String(value ?? "").trim();
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
function normalizeImportFormat(value) {
|
|
14
|
+
const normalized = String(value ?? "").trim().toLowerCase();
|
|
15
|
+
|
|
16
|
+
return ["csv", "tsv", "json"].includes(normalized) ? normalized : "";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function normalizeImportedCellValue(value) {
|
|
20
|
+
if (value === null || value === undefined) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (["boolean", "number", "string"].includes(typeof value)) {
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
return JSON.stringify(value);
|
|
30
|
+
} catch {
|
|
31
|
+
return String(value);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function normalizeImportedRows(rows) {
|
|
36
|
+
return Array.isArray(rows)
|
|
37
|
+
? rows.map((row) =>
|
|
38
|
+
Array.isArray(row) ? row.map((cell) => normalizeImportedCellValue(cell)) : []
|
|
39
|
+
)
|
|
40
|
+
: [];
|
|
41
|
+
}
|
|
42
|
+
|
|
13
43
|
function assertSafeSqlFragment(value, label, { allowEmpty = true } = {}) {
|
|
14
44
|
const normalized = normalizeSqlFragment(value);
|
|
15
45
|
|
|
@@ -96,6 +126,7 @@ function normalizeCheckConstraintPayload(constraint = {}, index = 0) {
|
|
|
96
126
|
id: String(constraint.id ?? `check:${index}`),
|
|
97
127
|
name: String(constraint.name ?? `CHECK ${index + 1}`).trim(),
|
|
98
128
|
originalName: String(constraint.originalName ?? constraint.name ?? `CHECK ${index + 1}`).trim(),
|
|
129
|
+
deleted: normalizeBoolean(constraint.deleted),
|
|
99
130
|
columns: Array.isArray(constraint.columns)
|
|
100
131
|
? constraint.columns
|
|
101
132
|
.map((column) => ({
|
|
@@ -110,11 +141,28 @@ function normalizeCheckConstraintPayload(constraint = {}, index = 0) {
|
|
|
110
141
|
originalExpression: String(constraint.originalExpression ?? constraint.expression ?? "").trim(),
|
|
111
142
|
editable: normalizeBoolean(constraint.editable),
|
|
112
143
|
preserved: constraint.preserved === undefined ? true : normalizeBoolean(constraint.preserved),
|
|
144
|
+
columnId: String(constraint.columnId ?? "").trim(),
|
|
145
|
+
source: String(constraint.source ?? (constraint.originalExpression ? "detected" : "user")).trim(),
|
|
146
|
+
presetId: String(constraint.presetId ?? "").trim(),
|
|
147
|
+
presetFields:
|
|
148
|
+
constraint.presetFields && typeof constraint.presetFields === "object"
|
|
149
|
+
? Object.fromEntries(
|
|
150
|
+
Object.entries(constraint.presetFields).map(([key, value]) => [key, String(value ?? "")])
|
|
151
|
+
)
|
|
152
|
+
: {},
|
|
113
153
|
};
|
|
114
154
|
}
|
|
115
155
|
|
|
116
156
|
function normalizeDraftPayload(payload = {}) {
|
|
117
157
|
const mode = String(payload.mode ?? "create").trim() === "edit" ? "edit" : "create";
|
|
158
|
+
const importRows = normalizeImportedRows(payload.importRows ?? payload.importedCsvRows);
|
|
159
|
+
const importSourceFileName = String(
|
|
160
|
+
payload.importSourceFileName ?? payload.importedCsvFileName ?? ""
|
|
161
|
+
).trim();
|
|
162
|
+
const importDelimiter = String(payload.importDelimiter ?? payload.importedCsvDelimiter ?? "").trim();
|
|
163
|
+
const importFormat = normalizeImportFormat(
|
|
164
|
+
payload.importFormat ?? (importSourceFileName ? "csv" : "")
|
|
165
|
+
);
|
|
118
166
|
|
|
119
167
|
return {
|
|
120
168
|
mode,
|
|
@@ -137,13 +185,13 @@ function normalizeDraftPayload(payload = {}) {
|
|
|
137
185
|
schemaWarnings: Array.isArray(payload.schemaWarnings) ? payload.schemaWarnings : [],
|
|
138
186
|
warnings: Array.isArray(payload.warnings) ? payload.warnings : [],
|
|
139
187
|
fillImportedRows: normalizeBoolean(payload.fillImportedRows),
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
188
|
+
importFormat,
|
|
189
|
+
importSourceFileName,
|
|
190
|
+
importDelimiter,
|
|
191
|
+
importRows,
|
|
192
|
+
importedCsvFileName: importSourceFileName,
|
|
193
|
+
importedCsvDelimiter: importDelimiter,
|
|
194
|
+
importedCsvRows: importRows,
|
|
147
195
|
dirty: normalizeBoolean(payload.dirty),
|
|
148
196
|
};
|
|
149
197
|
}
|
|
@@ -290,9 +338,21 @@ function validateTableDesignerDraft(draft, { catalogTables = [], originalDraft =
|
|
|
290
338
|
throw new ValidationError("Imported row fill is only available when creating a table.");
|
|
291
339
|
}
|
|
292
340
|
|
|
293
|
-
if (draft.fillImportedRows && !draft.
|
|
294
|
-
throw new ValidationError("Fill requires imported
|
|
341
|
+
if (draft.fillImportedRows && !draft.importRows.length) {
|
|
342
|
+
throw new ValidationError("Fill requires imported rows.");
|
|
295
343
|
}
|
|
344
|
+
|
|
345
|
+
(draft.checkConstraints ?? [])
|
|
346
|
+
.filter((constraint) => !constraint.deleted)
|
|
347
|
+
.forEach((constraint) => {
|
|
348
|
+
const label = constraint.name || "CHECK constraint";
|
|
349
|
+
|
|
350
|
+
if (!String(constraint.expression ?? "").trim()) {
|
|
351
|
+
throw new ValidationError(`${constraint.name || "CHECK constraint"} needs an expression.`);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
assertSafeSqlFragment(constraint.expression, label, { allowEmpty: false });
|
|
355
|
+
});
|
|
296
356
|
}
|
|
297
357
|
|
|
298
358
|
module.exports = {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { mapSqliteError, ValidationError } = require("../../utils/errors");
|
|
2
|
+
const { quoteIdentifier } = require("../../utils/identifier");
|
|
2
3
|
const { getTableDetail } = require("./introspection");
|
|
3
4
|
const { analyzeTableDesignerChanges } = require("./tableDesigner/changeAnalysis");
|
|
4
5
|
const {
|
|
@@ -19,15 +20,31 @@ function getImportedFillColumns(draft) {
|
|
|
19
20
|
);
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
function getImportedRows(draft) {
|
|
24
|
+
return Array.isArray(draft.importRows) && draft.importRows.length
|
|
25
|
+
? draft.importRows
|
|
26
|
+
: draft.importedCsvRows ?? [];
|
|
27
|
+
}
|
|
28
|
+
|
|
22
29
|
function coerceImportedCellValue(column, value) {
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
if (value === null || value === undefined) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const normalizedValue =
|
|
35
|
+
typeof value === "object" ? JSON.stringify(value) : value;
|
|
25
36
|
const type = normalizeDesignerType(column.type);
|
|
37
|
+
const textValue = String(normalizedValue ?? "");
|
|
38
|
+
const trimmedValue = textValue.trim();
|
|
26
39
|
|
|
27
40
|
if (!trimmedValue) {
|
|
28
41
|
return ["TEXT", "DATE", "DATETIME"].includes(type) ? "" : null;
|
|
29
42
|
}
|
|
30
43
|
|
|
44
|
+
if (typeof normalizedValue === "boolean") {
|
|
45
|
+
return normalizedValue ? 1 : 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
31
48
|
if (type === "BOOLEAN") {
|
|
32
49
|
const normalized = trimmedValue.toLowerCase();
|
|
33
50
|
|
|
@@ -54,6 +71,35 @@ function coerceImportedCellValue(column, value) {
|
|
|
54
71
|
return textValue;
|
|
55
72
|
}
|
|
56
73
|
|
|
74
|
+
function normalizeCheckExpressionSql(expression) {
|
|
75
|
+
const normalized = String(expression ?? "").trim();
|
|
76
|
+
|
|
77
|
+
if (/^CHECK\s*\(/i.test(normalized)) {
|
|
78
|
+
return normalized;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return `CHECK (${normalized})`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function assertSafeCheckExpressionFragment(expression) {
|
|
85
|
+
const normalized = String(expression ?? "").trim();
|
|
86
|
+
|
|
87
|
+
if (
|
|
88
|
+
normalized.includes("\0") ||
|
|
89
|
+
normalized.includes(";") ||
|
|
90
|
+
normalized.includes("--") ||
|
|
91
|
+
normalized.includes("/*") ||
|
|
92
|
+
normalized.includes("*/")
|
|
93
|
+
) {
|
|
94
|
+
throw new ValidationError(
|
|
95
|
+
"CHECK expression must be a single SQL fragment without statement separators or comments.",
|
|
96
|
+
{
|
|
97
|
+
code: "CHECK_EXPRESSION_INVALID_FRAGMENT",
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
57
103
|
class TableDesignerService {
|
|
58
104
|
constructor({ connectionManager }) {
|
|
59
105
|
this.connectionManager = connectionManager;
|
|
@@ -126,7 +172,9 @@ class TableDesignerService {
|
|
|
126
172
|
const executeCreateWithImport = db.transaction((nextDraft) => {
|
|
127
173
|
db.exec(buildCreateTableSql(nextDraft));
|
|
128
174
|
|
|
129
|
-
|
|
175
|
+
const importRows = getImportedRows(nextDraft);
|
|
176
|
+
|
|
177
|
+
if (!nextDraft.fillImportedRows || !importRows.length) {
|
|
130
178
|
return;
|
|
131
179
|
}
|
|
132
180
|
|
|
@@ -143,10 +191,10 @@ class TableDesignerService {
|
|
|
143
191
|
)
|
|
144
192
|
);
|
|
145
193
|
|
|
146
|
-
|
|
194
|
+
importRows.forEach((row) => {
|
|
147
195
|
insertStatement.run(
|
|
148
196
|
importedColumns.map((column) =>
|
|
149
|
-
coerceImportedCellValue(column, row[column.importedValueIndex]
|
|
197
|
+
coerceImportedCellValue(column, row[column.importedValueIndex])
|
|
150
198
|
)
|
|
151
199
|
);
|
|
152
200
|
});
|
|
@@ -174,6 +222,63 @@ class TableDesignerService {
|
|
|
174
222
|
tables: listDesignerTables(db),
|
|
175
223
|
};
|
|
176
224
|
}
|
|
225
|
+
|
|
226
|
+
validateCheckExpression(payload = {}) {
|
|
227
|
+
const db = this.connectionManager.getActiveDatabase();
|
|
228
|
+
const draft = normalizeDraftPayload(payload.draft ?? {});
|
|
229
|
+
const expression = String(payload.expression ?? "").trim();
|
|
230
|
+
|
|
231
|
+
if (!expression) {
|
|
232
|
+
throw new ValidationError("CHECK expression is required.", {
|
|
233
|
+
code: "CHECK_EXPRESSION_REQUIRED",
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
assertSafeCheckExpressionFragment(expression);
|
|
238
|
+
|
|
239
|
+
const columns = draft.columns.filter((column) => !column.deleted && column.name);
|
|
240
|
+
|
|
241
|
+
if (!columns.length) {
|
|
242
|
+
throw new ValidationError("At least one named column is required to validate a CHECK expression.", {
|
|
243
|
+
code: "CHECK_VALIDATION_COLUMNS_REQUIRED",
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const tempTableName = `__sqlite_hub_check_validation_${Date.now()}_${Math.random()
|
|
248
|
+
.toString(16)
|
|
249
|
+
.slice(2)}`;
|
|
250
|
+
const tempDraft = {
|
|
251
|
+
...draft,
|
|
252
|
+
tableName: tempTableName,
|
|
253
|
+
columns,
|
|
254
|
+
checkConstraints: [
|
|
255
|
+
{
|
|
256
|
+
id: "check:validation",
|
|
257
|
+
expression,
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
};
|
|
261
|
+
const createSql = buildCreateTableSql(tempDraft).replace(/^CREATE TABLE\s+/i, "CREATE TEMP TABLE ");
|
|
262
|
+
const dropSql = `DROP TABLE IF EXISTS ${quoteIdentifier(tempTableName)};`;
|
|
263
|
+
|
|
264
|
+
try {
|
|
265
|
+
db.exec(dropSql);
|
|
266
|
+
db.exec(createSql);
|
|
267
|
+
db.exec(dropSql);
|
|
268
|
+
return {
|
|
269
|
+
valid: true,
|
|
270
|
+
generatedSql: normalizeCheckExpressionSql(expression),
|
|
271
|
+
};
|
|
272
|
+
} catch (error) {
|
|
273
|
+
try {
|
|
274
|
+
db.exec(dropSql);
|
|
275
|
+
} catch {
|
|
276
|
+
// Ignore cleanup failures; the validation error below is the useful signal.
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
throw mapSqliteError(error);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
177
282
|
}
|
|
178
283
|
|
|
179
284
|
module.exports = {
|