admindb 2.4.1 → 2.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/LICENSE +20 -20
- package/dist/app.js +48 -25
- package/dist/core/context.d.ts +1 -0
- package/dist/core/context.js +2 -0
- package/dist/core/diff/data-differ.d.ts +1 -0
- package/dist/core/diff/data-differ.js +243 -0
- package/dist/core/diff/patch-generator.d.ts +1 -0
- package/dist/core/diff/patch-generator.js +77 -0
- package/dist/core/diff/schema-differ.d.ts +1 -0
- package/dist/core/diff/schema-differ.js +229 -0
- package/dist/core/errors.d.ts +1 -0
- package/dist/core/errors.js +39 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +20 -0
- package/dist/core/result.d.ts +1 -0
- package/dist/core/result.js +10 -0
- package/dist/core/router.d.ts +1 -0
- package/dist/core/router.js +37 -0
- package/dist/core/transfer/dialect-mapper.d.ts +1 -0
- package/dist/core/transfer/dialect-mapper.js +303 -0
- package/dist/core/transfer/masking.d.ts +1 -0
- package/dist/core/transfer/masking.js +49 -0
- package/dist/core/transfer/sync-engine.d.ts +1 -0
- package/dist/core/transfer/sync-engine.js +234 -0
- package/dist/core/transfer/topological-sort.d.ts +1 -0
- package/dist/core/transfer/topological-sort.js +55 -0
- package/dist/data/chain.js +1 -1
- package/dist/data/engine.js +1 -1
- package/dist/db/database.d.ts +1 -1
- package/dist/db/database.js +9 -31
- package/dist/db/dialects/index.d.ts +1 -0
- package/dist/db/dialects/index.js +58 -0
- package/dist/db/dialects/postgres/ddl.d.ts +1 -0
- package/dist/db/dialects/postgres/ddl.js +107 -0
- package/dist/db/dialects/postgres/dialect.d.ts +1 -0
- package/dist/db/dialects/postgres/dialect.js +28 -0
- package/dist/db/dialects/postgres/introspector.d.ts +1 -0
- package/dist/db/dialects/postgres/introspector.js +265 -0
- package/dist/db/dialects/postgres/types.d.ts +1 -0
- package/dist/db/dialects/postgres/types.js +83 -0
- package/dist/db/dialects/sqlite/ddl.d.ts +1 -0
- package/dist/db/dialects/sqlite/ddl.js +244 -0
- package/dist/db/dialects/sqlite/dialect.d.ts +1 -0
- package/dist/db/dialects/sqlite/dialect.js +25 -0
- package/dist/db/dialects/sqlite/introspector.d.ts +1 -0
- package/dist/db/dialects/sqlite/introspector.js +217 -0
- package/dist/db/dialects/sqlite/types.d.ts +1 -0
- package/dist/db/dialects/sqlite/types.js +69 -0
- package/dist/db/dialects/types.d.ts +1 -0
- package/dist/db/dialects/types.js +15 -0
- package/dist/db/export.js +1 -1
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.js +1 -1
- package/dist/db/manager.d.ts +1 -1
- package/dist/db/manager.js +101 -21
- package/dist/db/postgres.d.ts +1 -1
- package/dist/db/postgres.js +20 -284
- package/dist/db/types.d.ts +1 -1
- package/dist/db/types.js +0 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -1
- package/dist/modules/databases/databases.routes.d.ts +1 -0
- package/dist/modules/databases/databases.routes.js +150 -0
- package/dist/modules/databases/databases.service.d.ts +1 -0
- package/dist/modules/databases/databases.service.js +134 -0
- package/dist/modules/databases/index.d.ts +1 -0
- package/dist/{routes → modules/databases}/index.js +2 -3
- package/dist/modules/erd/erd.routes.d.ts +1 -0
- package/dist/modules/erd/erd.routes.js +32 -0
- package/dist/modules/erd/erd.service.d.ts +1 -0
- package/dist/modules/erd/erd.service.js +39 -0
- package/dist/modules/erd/index.d.ts +1 -0
- package/dist/modules/erd/index.js +18 -0
- package/dist/modules/index.d.ts +1 -0
- package/dist/modules/index.js +70 -0
- package/dist/modules/query/index.d.ts +1 -0
- package/dist/modules/query/index.js +18 -0
- package/dist/modules/query/query.routes.d.ts +1 -0
- package/dist/modules/query/query.routes.js +54 -0
- package/dist/modules/query/query.service.d.ts +1 -0
- package/dist/modules/query/query.service.js +95 -0
- package/dist/modules/rows/blob.helper.d.ts +1 -0
- package/dist/modules/rows/blob.helper.js +40 -0
- package/dist/modules/rows/display.helper.d.ts +1 -0
- package/dist/modules/rows/display.helper.js +73 -0
- package/dist/modules/rows/index.d.ts +1 -0
- package/dist/modules/rows/index.js +20 -0
- package/dist/modules/rows/rows.routes.d.ts +1 -0
- package/dist/modules/rows/rows.routes.js +555 -0
- package/dist/modules/rows/rows.service.d.ts +1 -0
- package/dist/modules/rows/rows.service.js +433 -0
- package/dist/modules/schema/index.d.ts +1 -0
- package/dist/modules/schema/index.js +18 -0
- package/dist/modules/schema/schema.routes.d.ts +1 -0
- package/dist/modules/schema/schema.routes.js +171 -0
- package/dist/modules/schema/schema.service.d.ts +1 -0
- package/dist/modules/schema/schema.service.js +58 -0
- package/dist/modules/search/index.d.ts +1 -0
- package/dist/modules/search/index.js +19 -0
- package/dist/modules/search/search.routes.d.ts +1 -0
- package/dist/modules/search/search.routes.js +38 -0
- package/dist/modules/search/search.service.d.ts +1 -0
- package/dist/modules/search/search.service.js +458 -0
- package/dist/modules/search/search.types.d.ts +1 -0
- package/dist/modules/search/search.types.js +2 -0
- package/dist/modules/seed/index.d.ts +1 -0
- package/dist/modules/seed/index.js +18 -0
- package/dist/modules/seed/seed.routes.d.ts +1 -0
- package/dist/modules/seed/seed.routes.js +212 -0
- package/dist/modules/seed/seed.service.d.ts +1 -0
- package/dist/{routes/api/helpers.js → modules/seed/seed.service.js} +86 -109
- package/dist/modules/sync/index.d.ts +1 -0
- package/dist/modules/sync/index.js +18 -0
- package/dist/modules/sync/sync.routes.d.ts +1 -0
- package/dist/modules/sync/sync.routes.js +135 -0
- package/dist/modules/sync/sync.service.d.ts +1 -0
- package/dist/modules/sync/sync.service.js +41 -0
- package/dist/modules/tables/index.d.ts +1 -0
- package/dist/modules/tables/index.js +18 -0
- package/dist/modules/tables/tables.routes.d.ts +1 -0
- package/dist/modules/tables/tables.routes.js +194 -0
- package/dist/modules/tables/tables.service.d.ts +1 -0
- package/dist/modules/tables/tables.service.js +218 -0
- package/dist/public/css/app.css +1 -1
- package/dist/public/js/browse.js +1 -1
- package/dist/public/js/query.js +1 -1
- package/dist/public/js/search.js +1 -0
- package/dist/public/js/sync.js +1 -0
- package/dist/types/api.d.ts +1 -1
- package/dist/utils/common.d.ts +1 -1
- package/dist/utils/common.js +1 -3
- package/dist/utils/icons.js +3 -0
- package/dist/utils/stream.d.ts +1 -0
- package/dist/utils/stream.js +131 -0
- package/dist/views/layouts/main.hbs +1 -1
- package/dist/views/pages/error.hbs +10 -1
- package/dist/views/pages/home.hbs +1 -1
- package/dist/views/pages/query.hbs +1 -1
- package/dist/views/pages/seed-select.hbs +1 -252
- package/dist/views/pages/seed.hbs +1 -307
- package/dist/views/pages/sync.hbs +409 -0
- package/dist/views/pages/table.hbs +1 -1
- package/dist/views/partials/navbar.hbs +1 -1
- package/dist/views/partials/sidebar.hbs +1 -1
- package/docs/API.md +4 -17
- package/docs/EXAMPLES.md +579 -579
- package/docs/SECURITY.md +265 -265
- package/package.json +10 -9
- package/dist/db/introspection.d.ts +0 -1
- package/dist/db/introspection.js +0 -164
- package/dist/routes/api/erd.d.ts +0 -1
- package/dist/routes/api/erd.js +0 -45
- package/dist/routes/api/helpers.d.ts +0 -1
- package/dist/routes/api/import-export.d.ts +0 -1
- package/dist/routes/api/import-export.js +0 -84
- package/dist/routes/api/index.d.ts +0 -1
- package/dist/routes/api/index.js +0 -55
- package/dist/routes/api/query.d.ts +0 -1
- package/dist/routes/api/query.js +0 -77
- package/dist/routes/api/rows.d.ts +0 -1
- package/dist/routes/api/rows.js +0 -373
- package/dist/routes/api/seed.d.ts +0 -1
- package/dist/routes/api/seed.js +0 -180
- package/dist/routes/api/tables.d.ts +0 -1
- package/dist/routes/api/tables.js +0 -230
- package/dist/routes/databases.d.ts +0 -1
- package/dist/routes/databases.js +0 -212
- package/dist/routes/index.d.ts +0 -1
- package/dist/routes/pages.d.ts +0 -1
- package/dist/routes/pages.js +0 -540
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_MASKING_RULES = void 0;
|
|
4
|
+
exports.maskRowData = maskRowData;
|
|
5
|
+
exports.DEFAULT_MASKING_RULES = [
|
|
6
|
+
{ columnPattern: 'email|e_mail|mail', strategy: 'email' },
|
|
7
|
+
{ columnPattern: 'pass|password|passwd|pwd|secret|token|api_key|auth', strategy: 'password' },
|
|
8
|
+
{ columnPattern: 'phone|mobile|cell|telephone', strategy: 'phone' },
|
|
9
|
+
{ columnPattern: 'first_name|last_name|full_name|username|user_name', strategy: 'name' },
|
|
10
|
+
];
|
|
11
|
+
function maskRowData(row, rules = exports.DEFAULT_MASKING_RULES, rowIndex = 1) {
|
|
12
|
+
const masked = { ...row };
|
|
13
|
+
for (const [colName, val] of Object.entries(row)) {
|
|
14
|
+
if (val === null || val === undefined)
|
|
15
|
+
continue;
|
|
16
|
+
for (const rule of rules) {
|
|
17
|
+
const regex = new RegExp(rule.columnPattern, 'i');
|
|
18
|
+
if (regex.test(colName)) {
|
|
19
|
+
switch (rule.strategy) {
|
|
20
|
+
case 'email':
|
|
21
|
+
masked[colName] = `user${rowIndex}@masked.test`;
|
|
22
|
+
break;
|
|
23
|
+
case 'password':
|
|
24
|
+
if (/token|secret|key|auth/i.test(colName) && !/pass|pwd/i.test(colName)) {
|
|
25
|
+
masked[colName] = `masked_${rowIndex}_${Math.random().toString(36).substring(2, 10)}${Math.random().toString(36).substring(2, 10)}`;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
masked[colName] = `$2b$10$maskedpwdhash${String(rowIndex).padStart(6, '0')}${Math.random().toString(36).substring(2, 14)}`;
|
|
29
|
+
}
|
|
30
|
+
break;
|
|
31
|
+
case 'phone':
|
|
32
|
+
masked[colName] = `+1555000${String(rowIndex).padStart(4, '0')}`;
|
|
33
|
+
break;
|
|
34
|
+
case 'name':
|
|
35
|
+
masked[colName] = `Test User ${rowIndex}`;
|
|
36
|
+
break;
|
|
37
|
+
case 'redact':
|
|
38
|
+
masked[colName] = `[REDACTED_${rowIndex}]`;
|
|
39
|
+
break;
|
|
40
|
+
case 'hash':
|
|
41
|
+
masked[colName] = `hash_${rowIndex}_${Math.random().toString(36).substring(2, 8)}`;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return masked;
|
|
49
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import type {IDatabase} from '../../db/types';import{type MaskingRule} from './masking';export type SyncMode='full'|'schema_only'|'data_only_replace'|'data_only_upsert';export interface SyncOptions{tables?:string[];mode?:SyncMode;maskData?:boolean;maskRules?:MaskingRule[];batchSize?:number;dryRun?:boolean;}export interface SyncTableProgress{table:string;rowsCopied:number;totalRows:number;status:'pending'|'in_progress'|'completed'|'skipped'|'failed';error?:string;}export interface SyncResult{success:boolean;dryRun:boolean;sourceDbId:string;targetDbId:string;mode:SyncMode;tablesProcessed:SyncTableProgress[];totalRowsTransferred:number;executionTimeMs:number;error?:string;summary:string;}export declare function executeSync(sourceDb:IDatabase,sourceDbId:string,targetDb:IDatabase,targetDbId:string,options?:SyncOptions):Promise<SyncResult>;
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.executeSync = executeSync;
|
|
4
|
+
const dialect_mapper_1 = require("./dialect-mapper");
|
|
5
|
+
const topological_sort_1 = require("./topological-sort");
|
|
6
|
+
const masking_1 = require("./masking");
|
|
7
|
+
async function executeSync(sourceDb, sourceDbId, targetDb, targetDbId, options = {}) {
|
|
8
|
+
const startTime = Date.now();
|
|
9
|
+
const mode = options.mode || 'full';
|
|
10
|
+
const batchSize = Math.max(50, options.batchSize || 500);
|
|
11
|
+
const dryRun = Boolean(options.dryRun);
|
|
12
|
+
const maskData = Boolean(options.maskData);
|
|
13
|
+
const sourceDialect = sourceDb.dialect === 'postgres' ? 'postgres' : 'sqlite';
|
|
14
|
+
const targetDialect = targetDb.dialect === 'postgres' ? 'postgres' : 'sqlite';
|
|
15
|
+
const srcTableListRes = await sourceDb.listTables();
|
|
16
|
+
if (!srcTableListRes.success)
|
|
17
|
+
throw new Error(srcTableListRes.error || 'Failed to list source tables');
|
|
18
|
+
let tablesToSync = (srcTableListRes.data || [])
|
|
19
|
+
.map((t) => t.name)
|
|
20
|
+
.filter((n) => !n.startsWith('sqlite_') && !n.startsWith('_admindb_'));
|
|
21
|
+
if (options.tables && options.tables.length > 0) {
|
|
22
|
+
const selected = new Set(options.tables);
|
|
23
|
+
tablesToSync = tablesToSync.filter((n) => selected.has(n));
|
|
24
|
+
}
|
|
25
|
+
const sourceTableInfos = [];
|
|
26
|
+
for (const name of tablesToSync) {
|
|
27
|
+
const infoRes = await sourceDb.getTableInfo(name);
|
|
28
|
+
if (infoRes.success && infoRes.data) {
|
|
29
|
+
sourceTableInfos.push(infoRes.data);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const deps = sourceTableInfos.map((i) => ({
|
|
33
|
+
name: i.table,
|
|
34
|
+
foreignKeys: i.foreignKeys || [],
|
|
35
|
+
}));
|
|
36
|
+
const insertionOrder = (0, topological_sort_1.sortTablesTopologically)(deps);
|
|
37
|
+
const deletionOrder = (0, topological_sort_1.sortTablesForDeletion)(deps);
|
|
38
|
+
const normalizedTables = new Map();
|
|
39
|
+
for (const info of sourceTableInfos) {
|
|
40
|
+
normalizedTables.set(info.table, (0, dialect_mapper_1.normalizeTableInfo)(info, sourceDialect));
|
|
41
|
+
}
|
|
42
|
+
const progressList = insertionOrder.map((name) => ({
|
|
43
|
+
table: name,
|
|
44
|
+
rowsCopied: 0,
|
|
45
|
+
totalRows: 0,
|
|
46
|
+
status: 'pending',
|
|
47
|
+
}));
|
|
48
|
+
if (dryRun) {
|
|
49
|
+
let totalEstimatedRows = 0;
|
|
50
|
+
for (const p of progressList) {
|
|
51
|
+
const countRes = await sourceDb.getRowCount(p.table);
|
|
52
|
+
p.totalRows = countRes.data || 0;
|
|
53
|
+
p.rowsCopied = mode === 'schema_only' ? 0 : p.totalRows;
|
|
54
|
+
p.status = 'completed';
|
|
55
|
+
totalEstimatedRows += p.rowsCopied;
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
success: true,
|
|
59
|
+
dryRun: true,
|
|
60
|
+
sourceDbId,
|
|
61
|
+
targetDbId,
|
|
62
|
+
mode,
|
|
63
|
+
tablesProcessed: progressList,
|
|
64
|
+
totalRowsTransferred: totalEstimatedRows,
|
|
65
|
+
executionTimeMs: Date.now() - startTime,
|
|
66
|
+
summary: `Dry Run: Ready to sync ${progressList.length} tables (${totalEstimatedRows} estimated rows) from "${sourceDbId}" to "${targetDbId}".`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
let totalRowsTransferred = 0;
|
|
70
|
+
try {
|
|
71
|
+
if (targetDialect === 'sqlite') {
|
|
72
|
+
await targetDb.execResult('PRAGMA foreign_keys = OFF;');
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
try {
|
|
76
|
+
await targetDb.execResult('SET CONSTRAINTS ALL DEFERRED;');
|
|
77
|
+
}
|
|
78
|
+
catch { }
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch { }
|
|
82
|
+
try {
|
|
83
|
+
if (mode === 'full') {
|
|
84
|
+
for (const tableName of deletionOrder) {
|
|
85
|
+
try {
|
|
86
|
+
await targetDb.execResult(targetDialect === 'postgres'
|
|
87
|
+
? `DROP TABLE IF EXISTS "${tableName}" CASCADE;`
|
|
88
|
+
: `DROP TABLE IF EXISTS \`${tableName}\`;`);
|
|
89
|
+
}
|
|
90
|
+
catch { }
|
|
91
|
+
}
|
|
92
|
+
for (const tableName of insertionOrder) {
|
|
93
|
+
const norm = normalizedTables.get(tableName);
|
|
94
|
+
if (!norm)
|
|
95
|
+
continue;
|
|
96
|
+
const ddl = (0, dialect_mapper_1.generateCreateTableForDialect)(norm, targetDialect);
|
|
97
|
+
const createRes = await targetDb.execResult(ddl);
|
|
98
|
+
if (!createRes.success) {
|
|
99
|
+
throw new Error(`Failed to create table "${tableName}": ${createRes.error}`);
|
|
100
|
+
}
|
|
101
|
+
for (const idx of norm.indexes || []) {
|
|
102
|
+
if (idx.name?.startsWith('sqlite_'))
|
|
103
|
+
continue;
|
|
104
|
+
const colsArr = Array.isArray(idx?.columns) ? idx.columns.filter(Boolean) : [];
|
|
105
|
+
if (colsArr.length === 0)
|
|
106
|
+
continue;
|
|
107
|
+
const uq = idx.unique ? 'UNIQUE ' : '';
|
|
108
|
+
const q = targetDialect === 'postgres' ? (s) => `"${s}"` : (s) => `\`${s}\``;
|
|
109
|
+
const cols = colsArr.map(q).join(', ');
|
|
110
|
+
try {
|
|
111
|
+
await targetDb.execResult(`CREATE ${uq}INDEX IF NOT EXISTS ${q(idx.name)} ON ${q(tableName)} (${cols});`);
|
|
112
|
+
}
|
|
113
|
+
catch { }
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else if (mode === 'schema_only') {
|
|
118
|
+
for (const tableName of insertionOrder) {
|
|
119
|
+
const norm = normalizedTables.get(tableName);
|
|
120
|
+
if (!norm)
|
|
121
|
+
continue;
|
|
122
|
+
const targetInfo = await targetDb.getTableInfo(tableName);
|
|
123
|
+
if (!targetInfo.success || !targetInfo.data || targetInfo.data.columns.length === 0) {
|
|
124
|
+
const ddl = (0, dialect_mapper_1.generateCreateTableForDialect)(norm, targetDialect);
|
|
125
|
+
const createRes = await targetDb.execResult(ddl);
|
|
126
|
+
if (!createRes.success) {
|
|
127
|
+
throw new Error(`Failed to create table "${tableName}": ${createRes.error}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else if (mode === 'data_only_replace') {
|
|
133
|
+
for (const tableName of deletionOrder) {
|
|
134
|
+
try {
|
|
135
|
+
await targetDb.execResult(targetDialect === 'postgres' ? `TRUNCATE TABLE "${tableName}" CASCADE;` : `DELETE FROM \`${tableName}\`;`);
|
|
136
|
+
}
|
|
137
|
+
catch { }
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (mode !== 'schema_only') {
|
|
141
|
+
for (const p of progressList) {
|
|
142
|
+
const tableName = p.table;
|
|
143
|
+
p.status = 'in_progress';
|
|
144
|
+
const norm = normalizedTables.get(tableName);
|
|
145
|
+
if (!norm)
|
|
146
|
+
continue;
|
|
147
|
+
const countRes = await sourceDb.getRowCount(tableName);
|
|
148
|
+
p.totalRows = countRes.data || 0;
|
|
149
|
+
let offset = 0;
|
|
150
|
+
let tableRowsCopied = 0;
|
|
151
|
+
while (offset < p.totalRows || (offset === 0 && p.totalRows === 0)) {
|
|
152
|
+
if (p.totalRows === 0)
|
|
153
|
+
break;
|
|
154
|
+
const rowsRes = await sourceDb.getRows(tableName, { page: Math.floor(offset / batchSize) + 1, limit: batchSize });
|
|
155
|
+
const rows = rowsRes.data || [];
|
|
156
|
+
if (rows.length === 0)
|
|
157
|
+
break;
|
|
158
|
+
const targetRows = [];
|
|
159
|
+
for (let i = 0; i < rows.length; i++) {
|
|
160
|
+
let row = rows[i];
|
|
161
|
+
if (maskData) {
|
|
162
|
+
row = (0, masking_1.maskRowData)(row, options.maskRules, offset + i + 1);
|
|
163
|
+
}
|
|
164
|
+
const fields = [];
|
|
165
|
+
for (const col of norm.columns) {
|
|
166
|
+
const rawVal = row[col.name];
|
|
167
|
+
const convertedVal = (0, dialect_mapper_1.convertValueForDialect)(rawVal, col.genericType, targetDialect);
|
|
168
|
+
fields.push({ column: col.name, value: convertedVal });
|
|
169
|
+
}
|
|
170
|
+
targetRows.push(fields);
|
|
171
|
+
}
|
|
172
|
+
const insertRes = await targetDb.insertRows(tableName, targetRows);
|
|
173
|
+
if (!insertRes.success) {
|
|
174
|
+
throw new Error(`Failed to insert batch into "${tableName}": ${insertRes.error}`);
|
|
175
|
+
}
|
|
176
|
+
tableRowsCopied += targetRows.length;
|
|
177
|
+
offset += rows.length;
|
|
178
|
+
}
|
|
179
|
+
if (targetDialect === 'postgres') {
|
|
180
|
+
for (const col of norm.columns) {
|
|
181
|
+
if (col.primaryKey && col.autoIncrement) {
|
|
182
|
+
try {
|
|
183
|
+
await targetDb.all(`SELECT setval(pg_get_serial_sequence('"${tableName}"', '${col.name}'), COALESCE(MAX("${col.name}"), 1)) FROM "${tableName}";`);
|
|
184
|
+
}
|
|
185
|
+
catch { }
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
p.rowsCopied = tableRowsCopied;
|
|
190
|
+
totalRowsTransferred += tableRowsCopied;
|
|
191
|
+
p.status = 'completed';
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
return {
|
|
197
|
+
success: false,
|
|
198
|
+
dryRun: false,
|
|
199
|
+
sourceDbId,
|
|
200
|
+
targetDbId,
|
|
201
|
+
mode,
|
|
202
|
+
tablesProcessed: progressList,
|
|
203
|
+
totalRowsTransferred,
|
|
204
|
+
executionTimeMs: Date.now() - startTime,
|
|
205
|
+
error: err.message || String(err),
|
|
206
|
+
summary: `Sync failed: ${err.message}`,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
finally {
|
|
210
|
+
try {
|
|
211
|
+
if (targetDialect === 'sqlite') {
|
|
212
|
+
await targetDb.execResult('PRAGMA foreign_keys = ON;');
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
try {
|
|
216
|
+
await targetDb.execResult('SET CONSTRAINTS ALL IMMEDIATE;');
|
|
217
|
+
}
|
|
218
|
+
catch { }
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
catch { }
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
success: true,
|
|
225
|
+
dryRun: false,
|
|
226
|
+
sourceDbId,
|
|
227
|
+
targetDbId,
|
|
228
|
+
mode,
|
|
229
|
+
tablesProcessed: progressList,
|
|
230
|
+
totalRowsTransferred,
|
|
231
|
+
executionTimeMs: Date.now() - startTime,
|
|
232
|
+
summary: `Successfully synchronized ${progressList.length} tables (${totalRowsTransferred} rows) from "${sourceDbId}" to "${targetDbId}".`,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import type {ForeignKeyInfo} from '../../db/types';export interface TableDependency{name:string;foreignKeys:ForeignKeyInfo[];}export declare function sortTablesTopologically(tables:TableDependency[]):string[];export declare function sortTablesForDeletion(tables:TableDependency[]):string[];
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sortTablesTopologically = sortTablesTopologically;
|
|
4
|
+
exports.sortTablesForDeletion = sortTablesForDeletion;
|
|
5
|
+
function sortTablesTopologically(tables) {
|
|
6
|
+
const tableNames = new Set(tables.map((t) => t.name));
|
|
7
|
+
const adj = new Map();
|
|
8
|
+
const inDegree = new Map();
|
|
9
|
+
for (const t of tables) {
|
|
10
|
+
if (!adj.has(t.name))
|
|
11
|
+
adj.set(t.name, new Set());
|
|
12
|
+
if (!inDegree.has(t.name))
|
|
13
|
+
inDegree.set(t.name, 0);
|
|
14
|
+
}
|
|
15
|
+
for (const t of tables) {
|
|
16
|
+
for (const fk of t.foreignKeys) {
|
|
17
|
+
const parent = fk.table;
|
|
18
|
+
if (parent && tableNames.has(parent) && parent !== t.name) {
|
|
19
|
+
if (!adj.get(parent).has(t.name)) {
|
|
20
|
+
adj.get(parent).add(t.name);
|
|
21
|
+
inDegree.set(t.name, (inDegree.get(t.name) || 0) + 1);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const queue = [];
|
|
27
|
+
for (const [name, deg] of inDegree.entries()) {
|
|
28
|
+
if (deg === 0)
|
|
29
|
+
queue.push(name);
|
|
30
|
+
}
|
|
31
|
+
const result = [];
|
|
32
|
+
while (queue.length > 0) {
|
|
33
|
+
const current = queue.shift();
|
|
34
|
+
result.push(current);
|
|
35
|
+
const children = adj.get(current) || new Set();
|
|
36
|
+
for (const child of children) {
|
|
37
|
+
const newDeg = (inDegree.get(child) || 0) - 1;
|
|
38
|
+
inDegree.set(child, newDeg);
|
|
39
|
+
if (newDeg === 0) {
|
|
40
|
+
queue.push(child);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (result.length < tables.length) {
|
|
45
|
+
for (const t of tables) {
|
|
46
|
+
if (!result.includes(t.name)) {
|
|
47
|
+
result.push(t.name);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
function sortTablesForDeletion(tables) {
|
|
54
|
+
return [...sortTablesTopologically(tables)].reverse();
|
|
55
|
+
}
|
package/dist/data/chain.js
CHANGED
|
@@ -18,7 +18,7 @@ async function getErChainConfig(db, rootTable, scope = 'chain') {
|
|
|
18
18
|
const tableListRes = await db.listTables();
|
|
19
19
|
const tableNames = (tableListRes.data ?? [])
|
|
20
20
|
.map((t) => t.name)
|
|
21
|
-
.filter((name) => !name.startsWith('sqlite_')
|
|
21
|
+
.filter((name) => !name.startsWith('sqlite_'));
|
|
22
22
|
const tables = [];
|
|
23
23
|
for (const name of tableNames) {
|
|
24
24
|
const info = await db.getTableInfo(name);
|
package/dist/data/engine.js
CHANGED
|
@@ -114,7 +114,7 @@ class SeedEngine {
|
|
|
114
114
|
const tableListRes = await db.listTables();
|
|
115
115
|
const tableNames = (tableListRes.data ?? [])
|
|
116
116
|
.map((t) => t.name)
|
|
117
|
-
.filter((name) => !name.startsWith('sqlite_')
|
|
117
|
+
.filter((name) => !name.startsWith('sqlite_'));
|
|
118
118
|
const tableInfos = new Map();
|
|
119
119
|
const allTableData = [];
|
|
120
120
|
for (const name of tableNames) {
|
package/dist/db/database.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import type {Logger} from '../utils/logger';import{type ColumnDef,type IndexDef} from '../sql/generator';import type {DatabaseDialect,DbOpenOptions,IDatabase,ReferencingTableInfo,Result,RowFilters,
|
|
1
|
+
import type {Logger} from '../utils/logger';import{type ColumnDef,type IndexDef} from '../sql/generator';import type {DatabaseDialect,DbOpenOptions,IDatabase,ReferencingTableInfo,Result,RowFilters,SchemaInfo,SQLInputValue,TableInfoData,TableListItem,WhereClause} from './types';export * from './types';export * from './filters';export declare class SqliteDatabase implements IDatabase{private db;private logger;readonly path:string;readonly isReadOnly:boolean;readonly dialect:DatabaseDialect;constructor(dbPath:string,logger:Logger,options?:DbOpenOptions);private readonlyBlocked;close():void;private tryRun;private transaction;private toBind;all<T=unknown>(sql:string,params?:SQLInputValue[]):Promise<Result<T[]>>;run(sql:string,params?:SQLInputValue[]):Promise<Result<{changes:number;lastInsertRowid:number|null;}>>;execResult(sql:string):Promise<Result<{changes?:number;}>>;hasMultipleStatements(sql:string):boolean;runWrite(sql:string):Promise<Result<{changes?:number;}>>;listTables():Promise<Result<TableListItem[]>>;getTableInfo(table:string):Promise<Result<TableInfoData>>;getRowCount(table:string,filters?:RowFilters):Promise<Result<number>>;getRows(table:string,opts?:{page?:number;limit?:number;orderBy?:string;orderDir?:'asc'|'desc';filters?:RowFilters;}):Promise<Result<Record<string,unknown>[]>>;getAllRows(table:string):Promise<Result<Record<string,unknown>[]>>;getRow(table:string,where:WhereClause[]):Promise<Result<Record<string,unknown>|null>>;insertRow(table:string,fields:WhereClause[]):Promise<Result<{changes:number;lastInsertRowid:number|null;}>>;insertRows(table:string,rows:WhereClause[][]):Promise<Result<{inserted:number;skipped:number;}>>;updateRow(table:string,fields:WhereClause[],where:WhereClause[]):Promise<Result<{changes:number;lastInsertRowid:number|null;}>>;updateRows(table:string,rows:{fields:WhereClause[];where:WhereClause[];}[]):Promise<Result<{updated:number;}>>;deleteRow(table:string,where:WhereClause[]):Promise<Result<{changes:number;lastInsertRowid:number|null;}>>;deleteRows(table:string,rows:WhereClause[][]):Promise<Result<{deleted:number;}>>;getRowsByPks(table:string,pks:WhereClause[][]):Promise<Result<Record<string,unknown>[]>>;getCreateStatement(table:string):Promise<Result<string|null>>;getSettings():Promise<Result<Record<string,string>>>;getSchema(table:string):Promise<Result<SchemaInfo>>;getReferencingTables(table:string):Promise<Result<ReferencingTableInfo[]>>;getRowsByFk(table:string,column:string,value:unknown,limit?:number):Promise<Result<{rows:Record<string,unknown>[];total:number;}>>;renameTable(oldName:string,newName:string):Promise<Result<{changes?:number;}>>;addColumn(table:string,col:ColumnDef):Promise<Result<{changes?:number;}>>;modifyColumn(table:string,oldColName:string,newColDef:ColumnDef):Promise<Result<{changes?:number;}>>;renameColumn(table:string,oldName:string,newName:string):Promise<Result<{changes?:number;}>>;dropColumn(table:string,column:string):Promise<Result<{changes?:number;}>>;dropTable(table:string):Promise<Result<{changes?:number;}>>;createIndex(table:string,index:IndexDef):Promise<Result<{changes?:number;}>>;dropIndex(indexName:string):Promise<Result<{changes?:number;}>>;private getTableInfoSync;}
|
package/dist/db/database.js
CHANGED
|
@@ -21,9 +21,8 @@ exports.SqliteDatabase = void 0;
|
|
|
21
21
|
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
22
22
|
const generator_1 = require("../sql/generator");
|
|
23
23
|
const common_1 = require("../utils/common");
|
|
24
|
-
const types_1 = require("./types");
|
|
25
24
|
const filters_1 = require("./filters");
|
|
26
|
-
const
|
|
25
|
+
const dialect_1 = require("./dialects/sqlite/dialect");
|
|
27
26
|
const migrations_1 = require("./migrations");
|
|
28
27
|
__exportStar(require("./types"), exports);
|
|
29
28
|
__exportStar(require("./filters"), exports);
|
|
@@ -44,7 +43,6 @@ class SqliteDatabase {
|
|
|
44
43
|
}
|
|
45
44
|
else {
|
|
46
45
|
this.db.exec('PRAGMA journal_mode = WAL;');
|
|
47
|
-
this.initSchema();
|
|
48
46
|
}
|
|
49
47
|
this.logger.info(`Opened SQLite database at ${dbPath}${this.isReadOnly ? ' (read-only)' : ''}`);
|
|
50
48
|
}
|
|
@@ -59,16 +57,6 @@ class SqliteDatabase {
|
|
|
59
57
|
this.logger.warn(`Failed to close database: ${(0, common_1.errorMessage)(err)}`);
|
|
60
58
|
}
|
|
61
59
|
}
|
|
62
|
-
initSchema() {
|
|
63
|
-
this.db.exec(`
|
|
64
|
-
CREATE TABLE IF NOT EXISTS ${(0, generator_1.quoteIdentifier)(types_1.INTERNAL_TABLES.savedQueries)} (
|
|
65
|
-
${(0, generator_1.quoteIdentifier)('id')} INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
66
|
-
${(0, generator_1.quoteIdentifier)('name')} TEXT NOT NULL UNIQUE,
|
|
67
|
-
${(0, generator_1.quoteIdentifier)('sql')} TEXT NOT NULL,
|
|
68
|
-
${(0, generator_1.quoteIdentifier)('created_at')} TEXT NOT NULL DEFAULT (datetime('now'))
|
|
69
|
-
);
|
|
70
|
-
`);
|
|
71
|
-
}
|
|
72
60
|
async tryRun(fn) {
|
|
73
61
|
try {
|
|
74
62
|
return { success: true, data: await Promise.resolve(fn()) };
|
|
@@ -162,15 +150,15 @@ class SqliteDatabase {
|
|
|
162
150
|
}
|
|
163
151
|
}
|
|
164
152
|
async listTables() {
|
|
165
|
-
return
|
|
153
|
+
return dialect_1.sqliteDialect.introspector.listTables(this.db);
|
|
166
154
|
}
|
|
167
155
|
async getTableInfo(table) {
|
|
168
|
-
return
|
|
156
|
+
return dialect_1.sqliteDialect.introspector.getTableInfo(this.db, table);
|
|
169
157
|
}
|
|
170
158
|
async getRowCount(table, filters) {
|
|
171
159
|
return this.tryRun(() => {
|
|
172
|
-
const
|
|
173
|
-
const { where, params } = (0, filters_1.buildFilterClause)(filters,
|
|
160
|
+
const cols = this.db.prepare(`PRAGMA table_info(${(0, generator_1.quoteIdentifier)(table)})`).all();
|
|
161
|
+
const { where, params } = (0, filters_1.buildFilterClause)(filters, cols.map((c) => c.name));
|
|
174
162
|
const row = this.db
|
|
175
163
|
.prepare(`SELECT COUNT(*) AS c FROM ${(0, generator_1.quoteIdentifier)(table)}${where}`)
|
|
176
164
|
.get(...params);
|
|
@@ -179,9 +167,8 @@ class SqliteDatabase {
|
|
|
179
167
|
}
|
|
180
168
|
async getRows(table, opts = {}) {
|
|
181
169
|
return this.tryRun(() => {
|
|
182
|
-
const
|
|
183
|
-
const
|
|
184
|
-
const orderCol = opts.orderBy || info?.primaryKey?.[0] || cols[0]?.name;
|
|
170
|
+
const cols = this.db.prepare(`PRAGMA table_info(${(0, generator_1.quoteIdentifier)(table)})`).all();
|
|
171
|
+
const orderCol = opts.orderBy || cols[0]?.name;
|
|
185
172
|
const orderDir = opts.orderDir === 'desc' ? 'DESC' : 'ASC';
|
|
186
173
|
const limit = opts.limit && opts.limit > 0 ? opts.limit : 200;
|
|
187
174
|
const page = opts.page && opts.page > 0 ? opts.page : 1;
|
|
@@ -288,15 +275,6 @@ class SqliteDatabase {
|
|
|
288
275
|
return row?.sql ?? null;
|
|
289
276
|
});
|
|
290
277
|
}
|
|
291
|
-
async listSavedQueries() {
|
|
292
|
-
return this.all(`SELECT id, name, sql, created_at FROM ${(0, generator_1.quoteIdentifier)(types_1.INTERNAL_TABLES.savedQueries)} ORDER BY name COLLATE NOCASE`);
|
|
293
|
-
}
|
|
294
|
-
async saveQuery(name, sql) {
|
|
295
|
-
return this.run(`INSERT INTO ${(0, generator_1.quoteIdentifier)(types_1.INTERNAL_TABLES.savedQueries)} (name, sql) VALUES (?, ?)`, [name, sql]);
|
|
296
|
-
}
|
|
297
|
-
async deleteSavedQuery(id) {
|
|
298
|
-
return this.run(`DELETE FROM ${(0, generator_1.quoteIdentifier)(types_1.INTERNAL_TABLES.savedQueries)} WHERE id = ?`, [Number(id)]);
|
|
299
|
-
}
|
|
300
278
|
async getSettings() {
|
|
301
279
|
return this.tryRun(() => {
|
|
302
280
|
const pragmas = ['journal_mode', 'synchronous', 'foreign_keys', 'page_size', 'encoding', 'auto_vacuum', 'mmap_size', 'temp_store'];
|
|
@@ -313,10 +291,10 @@ class SqliteDatabase {
|
|
|
313
291
|
});
|
|
314
292
|
}
|
|
315
293
|
async getSchema(table) {
|
|
316
|
-
return
|
|
294
|
+
return dialect_1.sqliteDialect.introspector.getSchema(this.db, table);
|
|
317
295
|
}
|
|
318
296
|
async getReferencingTables(table) {
|
|
319
|
-
return
|
|
297
|
+
return dialect_1.sqliteDialect.introspector.getReferencingTables(this.db, table);
|
|
320
298
|
}
|
|
321
299
|
async getRowsByFk(table, column, value, limit = 50) {
|
|
322
300
|
return this.tryRun(() => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './types';export * from './sqlite/dialect';export * from './sqlite/types';export * from './sqlite/introspector';export * from './sqlite/ddl';export * from './postgres/dialect';export * from './postgres/types';export * from './postgres/introspector';export * from './postgres/ddl';import type {IDialect} from './types';export interface DialectAdapter<D=unknown>{readonly id:string;readonly name:string;getDialect(schema?:string):IDialect<D>;}export declare class DialectRegistry{private static adapters;static register(adapter:DialectAdapter):void;static get(name:string):DialectAdapter|undefined;static list():string[];}export declare function getDialect(name:'sqlite'|'postgres'|string,schema?:string):IDialect;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.DialectRegistry = void 0;
|
|
18
|
+
exports.getDialect = getDialect;
|
|
19
|
+
__exportStar(require("./types"), exports);
|
|
20
|
+
__exportStar(require("./sqlite/dialect"), exports);
|
|
21
|
+
__exportStar(require("./sqlite/types"), exports);
|
|
22
|
+
__exportStar(require("./sqlite/introspector"), exports);
|
|
23
|
+
__exportStar(require("./sqlite/ddl"), exports);
|
|
24
|
+
__exportStar(require("./postgres/dialect"), exports);
|
|
25
|
+
__exportStar(require("./postgres/types"), exports);
|
|
26
|
+
__exportStar(require("./postgres/introspector"), exports);
|
|
27
|
+
__exportStar(require("./postgres/ddl"), exports);
|
|
28
|
+
const dialect_1 = require("./sqlite/dialect");
|
|
29
|
+
const dialect_2 = require("./postgres/dialect");
|
|
30
|
+
class DialectRegistry {
|
|
31
|
+
static adapters = new Map();
|
|
32
|
+
static register(adapter) {
|
|
33
|
+
this.adapters.set(adapter.id.toLowerCase(), adapter);
|
|
34
|
+
}
|
|
35
|
+
static get(name) {
|
|
36
|
+
return this.adapters.get(name.toLowerCase());
|
|
37
|
+
}
|
|
38
|
+
static list() {
|
|
39
|
+
return Array.from(this.adapters.keys());
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.DialectRegistry = DialectRegistry;
|
|
43
|
+
DialectRegistry.register({
|
|
44
|
+
id: 'sqlite',
|
|
45
|
+
name: 'SQLite',
|
|
46
|
+
getDialect: () => dialect_1.sqliteDialect,
|
|
47
|
+
});
|
|
48
|
+
DialectRegistry.register({
|
|
49
|
+
id: 'postgres',
|
|
50
|
+
name: 'PostgreSQL',
|
|
51
|
+
getDialect: (schema) => (schema && schema !== 'public' ? new dialect_2.PostgresDialect(schema) : dialect_2.postgresDialect),
|
|
52
|
+
});
|
|
53
|
+
function getDialect(name, schema) {
|
|
54
|
+
const adapter = DialectRegistry.get(name);
|
|
55
|
+
if (adapter)
|
|
56
|
+
return adapter.getDialect(schema);
|
|
57
|
+
return dialect_1.sqliteDialect;
|
|
58
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{type ColumnDef,type IndexDef} from '../../../sql/generator';import type {IDdlGenerator} from '../types';import type {Result} from '../../types';export declare class PostgresDdlGenerator implements IDdlGenerator{createTable(table:string,columns:ColumnDef[]):string;renameTable(oldName:string,newName:string):string;dropTable(table:string,options?:{cascade?:boolean;ifExists?:boolean;}):string;truncateTable(table:string,options?:{cascade?:boolean;restartIdentity?:boolean;}):string;addColumn(table:string,col:ColumnDef):string;renameColumn(table:string,oldName:string,newName:string):string;dropColumn(table:string,column:string):string;createIndex(table:string,index:IndexDef):string;dropIndex(indexName:string):string;modifyColumn(driver:unknown,table:string,oldCol:string,newCol:ColumnDef):Promise<Result<{changes?:number;}>>;}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PostgresDdlGenerator = void 0;
|
|
4
|
+
const generator_1 = require("../../../sql/generator");
|
|
5
|
+
class PostgresDdlGenerator {
|
|
6
|
+
createTable(table, columns) {
|
|
7
|
+
const colDefs = [];
|
|
8
|
+
const pks = [];
|
|
9
|
+
for (const c of columns) {
|
|
10
|
+
let def = `${(0, generator_1.quoteIdentifier)(c.name)} ${c.type}`;
|
|
11
|
+
if (c.primaryKey)
|
|
12
|
+
pks.push((0, generator_1.quoteIdentifier)(c.name));
|
|
13
|
+
if (c.notNull)
|
|
14
|
+
def += ' NOT NULL';
|
|
15
|
+
if (c.defaultValue !== undefined && c.defaultValue !== null && c.defaultValue !== '') {
|
|
16
|
+
def += ` DEFAULT ${c.defaultValue}`;
|
|
17
|
+
}
|
|
18
|
+
if (c.unique && !c.primaryKey)
|
|
19
|
+
def += ' UNIQUE';
|
|
20
|
+
if (c.foreignKey?.table && c.foreignKey.column) {
|
|
21
|
+
def += ` REFERENCES ${(0, generator_1.quoteIdentifier)(c.foreignKey.table)} (${(0, generator_1.quoteIdentifier)(c.foreignKey.column)})`;
|
|
22
|
+
}
|
|
23
|
+
colDefs.push(` ${def}`);
|
|
24
|
+
}
|
|
25
|
+
if (pks.length) {
|
|
26
|
+
colDefs.push(` PRIMARY KEY (${pks.join(', ')})`);
|
|
27
|
+
}
|
|
28
|
+
return `CREATE TABLE ${(0, generator_1.quoteIdentifier)(table)} (\n${colDefs.join(',\n')}\n);`;
|
|
29
|
+
}
|
|
30
|
+
renameTable(oldName, newName) {
|
|
31
|
+
return `ALTER TABLE ${(0, generator_1.quoteIdentifier)(oldName)} RENAME TO ${(0, generator_1.quoteIdentifier)(newName)};`;
|
|
32
|
+
}
|
|
33
|
+
dropTable(table, options) {
|
|
34
|
+
const ifExists = options?.ifExists ? 'IF EXISTS ' : '';
|
|
35
|
+
const cascade = options?.cascade ? ' CASCADE' : '';
|
|
36
|
+
return `DROP TABLE ${ifExists}${(0, generator_1.quoteIdentifier)(table)}${cascade};`;
|
|
37
|
+
}
|
|
38
|
+
truncateTable(table, options) {
|
|
39
|
+
const restartIdentity = options?.restartIdentity ? ' RESTART IDENTITY' : '';
|
|
40
|
+
const cascade = options?.cascade ? ' CASCADE' : '';
|
|
41
|
+
return `TRUNCATE ${(0, generator_1.quoteIdentifier)(table)}${restartIdentity}${cascade};`;
|
|
42
|
+
}
|
|
43
|
+
addColumn(table, col) {
|
|
44
|
+
let sql = `ALTER TABLE ${(0, generator_1.quoteIdentifier)(table)} ADD COLUMN ${(0, generator_1.quoteIdentifier)(col.name)} ${col.type}`;
|
|
45
|
+
if (col.notNull)
|
|
46
|
+
sql += ' NOT NULL';
|
|
47
|
+
if (col.defaultValue !== undefined && col.defaultValue !== null && col.defaultValue !== '') {
|
|
48
|
+
sql += ` DEFAULT ${col.defaultValue}`;
|
|
49
|
+
}
|
|
50
|
+
if (col.foreignKey) {
|
|
51
|
+
sql += ` REFERENCES ${(0, generator_1.quoteIdentifier)(col.foreignKey.table)} (${(0, generator_1.quoteIdentifier)(col.foreignKey.column)})`;
|
|
52
|
+
}
|
|
53
|
+
return sql + ';';
|
|
54
|
+
}
|
|
55
|
+
renameColumn(table, oldName, newName) {
|
|
56
|
+
return `ALTER TABLE ${(0, generator_1.quoteIdentifier)(table)} RENAME COLUMN ${(0, generator_1.quoteIdentifier)(oldName)} TO ${(0, generator_1.quoteIdentifier)(newName)};`;
|
|
57
|
+
}
|
|
58
|
+
dropColumn(table, column) {
|
|
59
|
+
return `ALTER TABLE ${(0, generator_1.quoteIdentifier)(table)} DROP COLUMN ${(0, generator_1.quoteIdentifier)(column)} CASCADE;`;
|
|
60
|
+
}
|
|
61
|
+
createIndex(table, index) {
|
|
62
|
+
const unique = index.unique ? 'UNIQUE ' : '';
|
|
63
|
+
const cols = index.columns.map(generator_1.quoteIdentifier).join(', ');
|
|
64
|
+
const name = index.name || `idx_${table}_${index.columns.join('_')}`;
|
|
65
|
+
return `CREATE ${unique}INDEX IF NOT EXISTS ${(0, generator_1.quoteIdentifier)(name)} ON ${(0, generator_1.quoteIdentifier)(table)} (${cols});`;
|
|
66
|
+
}
|
|
67
|
+
dropIndex(indexName) {
|
|
68
|
+
return `DROP INDEX IF EXISTS ${(0, generator_1.quoteIdentifier)(indexName)} CASCADE;`;
|
|
69
|
+
}
|
|
70
|
+
async modifyColumn(driver, table, oldCol, newCol) {
|
|
71
|
+
try {
|
|
72
|
+
const pool = (driver && typeof driver === 'object' && 'pool' in driver)
|
|
73
|
+
? driver.pool
|
|
74
|
+
: driver;
|
|
75
|
+
const newName = String(newCol.name || oldCol).trim();
|
|
76
|
+
if (oldCol !== newName) {
|
|
77
|
+
await pool.query(`ALTER TABLE ${(0, generator_1.quoteIdentifier)(table)} RENAME COLUMN ${(0, generator_1.quoteIdentifier)(oldCol)} TO ${(0, generator_1.quoteIdentifier)(newName)};`);
|
|
78
|
+
}
|
|
79
|
+
const colToModify = newName;
|
|
80
|
+
if (newCol.type) {
|
|
81
|
+
await pool.query(`ALTER TABLE ${(0, generator_1.quoteIdentifier)(table)} ALTER COLUMN ${(0, generator_1.quoteIdentifier)(colToModify)} TYPE ${newCol.type} USING ${(0, generator_1.quoteIdentifier)(colToModify)}::${newCol.type};`);
|
|
82
|
+
}
|
|
83
|
+
if (newCol.notNull !== undefined) {
|
|
84
|
+
const action = newCol.notNull ? 'SET NOT NULL' : 'DROP NOT NULL';
|
|
85
|
+
await pool.query(`ALTER TABLE ${(0, generator_1.quoteIdentifier)(table)} ALTER COLUMN ${(0, generator_1.quoteIdentifier)(colToModify)} ${action};`);
|
|
86
|
+
}
|
|
87
|
+
if (newCol.defaultValue !== undefined) {
|
|
88
|
+
const action = newCol.defaultValue === null || newCol.defaultValue === '' ? 'DROP DEFAULT' : `SET DEFAULT ${newCol.defaultValue}`;
|
|
89
|
+
await pool.query(`ALTER TABLE ${(0, generator_1.quoteIdentifier)(table)} ALTER COLUMN ${(0, generator_1.quoteIdentifier)(colToModify)} ${action};`);
|
|
90
|
+
}
|
|
91
|
+
if (newCol.unique !== undefined) {
|
|
92
|
+
const idxName = `idx_${table}_${colToModify}_unique`;
|
|
93
|
+
if (newCol.unique) {
|
|
94
|
+
await pool.query(`CREATE UNIQUE INDEX IF NOT EXISTS ${(0, generator_1.quoteIdentifier)(idxName)} ON ${(0, generator_1.quoteIdentifier)(table)} (${(0, generator_1.quoteIdentifier)(colToModify)});`);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
await pool.query(`DROP INDEX IF EXISTS ${(0, generator_1.quoteIdentifier)(idxName)};`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return { success: true, data: { changes: 1 } };
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.PostgresDdlGenerator = PostgresDdlGenerator;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import type {DialectCapabilities,IDialect} from '../types';import{PostgresDdlGenerator} from './ddl';import{PostgresIntrospector} from './introspector';export declare class PostgresDialect implements IDialect{readonly name:"postgres";readonly capabilities:DialectCapabilities;readonly introspector:PostgresIntrospector;readonly ddl:PostgresDdlGenerator;constructor(schema?:string);quoteIdentifier(identifier:string):string;mapType(type:string):import("./types").MappedType;getDesignerTypes():readonly string[];}export declare const postgresDialect:PostgresDialect;
|