orchid-orm 1.67.0 → 1.68.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/dist/index.d.ts +642 -682
- package/dist/index.js +1398 -2304
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1391 -2298
- package/dist/index.mjs.map +1 -1
- package/dist/migrations/index.d.ts +14 -13
- package/dist/migrations/index.js +2379 -3597
- package/dist/migrations/index.js.map +1 -1
- package/dist/migrations/index.mjs +2345 -3586
- package/dist/migrations/index.mjs.map +1 -1
- package/dist/migrations/node-postgres.d.ts +13 -14
- package/dist/migrations/node-postgres.js +12 -3795
- package/dist/migrations/node-postgres.js.map +1 -1
- package/dist/migrations/node-postgres.mjs +4 -3785
- package/dist/migrations/node-postgres.mjs.map +1 -1
- package/dist/migrations/postgres-js.d.ts +13 -14
- package/dist/migrations/postgres-js.js +12 -3795
- package/dist/migrations/postgres-js.js.map +1 -1
- package/dist/migrations/postgres-js.mjs +4 -3785
- package/dist/migrations/postgres-js.mjs.map +1 -1
- package/dist/node-postgres.d.ts +10 -10
- package/dist/node-postgres.js +17 -21
- package/dist/node-postgres.js.map +1 -1
- package/dist/node-postgres.mjs +14 -17
- package/dist/node-postgres.mjs.map +1 -1
- package/dist/postgres-js.d.ts +11 -14
- package/dist/postgres-js.js +16 -17
- package/dist/postgres-js.js.map +1 -1
- package/dist/postgres-js.mjs +13 -13
- package/dist/postgres-js.mjs.map +1 -1
- package/package.json +62 -27
|
@@ -1,3795 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
let id = 1;
|
|
14
|
-
for (const { source, compare, handle } of tableExpressions) {
|
|
15
|
-
const viewName = `orchidTmpView${id++}`;
|
|
16
|
-
const values = [];
|
|
17
|
-
const combinedQueries = [
|
|
18
|
-
`CREATE TEMPORARY VIEW ${viewName} AS (SELECT ${compare.map(
|
|
19
|
-
({ inDb, inCode }, i) => `${inDb} AS "*inDb-${i}*", ${inCode.map(
|
|
20
|
-
(s, j) => `(${typeof s === "string" ? s : s.toSQL({ values })}) "*inCode-${i}-${j}*"`
|
|
21
|
-
).join(", ")}`
|
|
22
|
-
).join(", ")} FROM ${source})`,
|
|
23
|
-
`SELECT pg_get_viewdef('${viewName}') v`,
|
|
24
|
-
`DROP VIEW ${viewName}`
|
|
25
|
-
].join("; ");
|
|
26
|
-
const result = await adapter.query(combinedQueries, values, viewName).then(
|
|
27
|
-
(res) => res[1],
|
|
28
|
-
async (err) => {
|
|
29
|
-
if (err.code !== "42704") {
|
|
30
|
-
throw err;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
);
|
|
34
|
-
if (!result) {
|
|
35
|
-
handle();
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
const match = compareSqlExpressionResult(
|
|
39
|
-
result.rows[0].v,
|
|
40
|
-
compare[0].inCode
|
|
41
|
-
);
|
|
42
|
-
handle(match);
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
const compareSqlExpressionResult = (resultSql, inCode) => {
|
|
46
|
-
let pos = 7;
|
|
47
|
-
const rgx = /\s+AS\s+"\*(inDb-\d+|inCode-\d+-\d+)\*",?/g;
|
|
48
|
-
let match;
|
|
49
|
-
let inDb = "";
|
|
50
|
-
let codeI = 0;
|
|
51
|
-
const matches = inCode.map(() => true);
|
|
52
|
-
while (match = rgx.exec(resultSql)) {
|
|
53
|
-
const sql = resultSql.slice(pos, rgx.lastIndex - match[0].length).trim();
|
|
54
|
-
const arr = match[1].split("-");
|
|
55
|
-
if (arr.length === 2) {
|
|
56
|
-
inDb = sql;
|
|
57
|
-
codeI = 0;
|
|
58
|
-
} else {
|
|
59
|
-
if (inDb !== sql && // Comparing `(sql) = sql` and `sql = (sql)` below.
|
|
60
|
-
// Could not reproduce this case in integration tests, but it was reported in #494.
|
|
61
|
-
!(inDb.startsWith("(") && inDb.endsWith(")") && inDb.slice(1, -1) === sql) && !(sql.startsWith("(") && sql.endsWith(")") && sql.slice(1, -1) === inDb)) {
|
|
62
|
-
matches[codeI] = false;
|
|
63
|
-
}
|
|
64
|
-
codeI++;
|
|
65
|
-
}
|
|
66
|
-
pos = rgx.lastIndex;
|
|
67
|
-
}
|
|
68
|
-
const firstMatching = matches.indexOf(true);
|
|
69
|
-
return firstMatching === -1 ? void 0 : firstMatching;
|
|
70
|
-
};
|
|
71
|
-
const promptCreateOrRename = (kind, name, drop, verifying) => {
|
|
72
|
-
if (verifying) throw new AbortSignal();
|
|
73
|
-
let hintPos = name.length + 4;
|
|
74
|
-
for (const from of drop) {
|
|
75
|
-
const value = from.length + 8 + name.length;
|
|
76
|
-
if (value > hintPos) hintPos = value;
|
|
77
|
-
}
|
|
78
|
-
let max = 0;
|
|
79
|
-
const add = name.length + 3;
|
|
80
|
-
for (const name2 of drop) {
|
|
81
|
-
if (name2.length + add > max) {
|
|
82
|
-
max = name2.length + add;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
const renameMessage = `rename ${kind}`;
|
|
86
|
-
return rakeDb.promptSelect({
|
|
87
|
-
message: `Create or rename ${internal.colors.blueBold(
|
|
88
|
-
name
|
|
89
|
-
)} ${kind} from another ${kind}?`,
|
|
90
|
-
options: [
|
|
91
|
-
`${internal.colors.greenBold("+")} ${name} ${internal.colors.pale(
|
|
92
|
-
`create ${kind}`.padStart(
|
|
93
|
-
hintPos + renameMessage.length - name.length - 4,
|
|
94
|
-
" "
|
|
95
|
-
)
|
|
96
|
-
)}`,
|
|
97
|
-
...drop.map(
|
|
98
|
-
(d) => `${internal.colors.yellowBold("~")} ${d} ${internal.colors.yellowBold(
|
|
99
|
-
"=>"
|
|
100
|
-
)} ${name} ${internal.colors.pale(
|
|
101
|
-
renameMessage.padStart(
|
|
102
|
-
hintPos + renameMessage.length - d.length - name.length - 8,
|
|
103
|
-
" "
|
|
104
|
-
)
|
|
105
|
-
)}`
|
|
106
|
-
)
|
|
107
|
-
]
|
|
108
|
-
});
|
|
109
|
-
};
|
|
110
|
-
const checkForColumnAddOrDrop = (shape, key) => {
|
|
111
|
-
const item = shape[key];
|
|
112
|
-
if (item) {
|
|
113
|
-
return item && (Array.isArray(item) || item.type === "add" || item.type === "drop");
|
|
114
|
-
}
|
|
115
|
-
for (const k in shape) {
|
|
116
|
-
const item2 = shape[k];
|
|
117
|
-
if (Array.isArray(item2) ? item2.some(
|
|
118
|
-
(item3) => (item3.type === "add" || item3.type === "drop") && item3.item.data.name === key
|
|
119
|
-
) : (item2.type === "add" || item2.type === "drop") && item2.item.data.name === key) {
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return false;
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const processSchemas = async (ast, dbStructure, {
|
|
127
|
-
codeItems: { schemas },
|
|
128
|
-
verifying,
|
|
129
|
-
internal: { generatorIgnore },
|
|
130
|
-
currentSchema
|
|
131
|
-
}) => {
|
|
132
|
-
const createSchemas = [];
|
|
133
|
-
const dropSchemas = [];
|
|
134
|
-
for (const schema of schemas) {
|
|
135
|
-
if (!dbStructure.schemas.includes(schema)) {
|
|
136
|
-
createSchemas.push(schema);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
for (const schema of dbStructure.schemas) {
|
|
140
|
-
if (!schemas.has(schema) && schema !== "public" && schema !== currentSchema && !generatorIgnore?.schemas?.includes(schema)) {
|
|
141
|
-
dropSchemas.push(schema);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
for (const schema of createSchemas) {
|
|
145
|
-
if (dropSchemas.length) {
|
|
146
|
-
const i = await promptCreateOrRename(
|
|
147
|
-
"schema",
|
|
148
|
-
schema,
|
|
149
|
-
dropSchemas,
|
|
150
|
-
verifying
|
|
151
|
-
);
|
|
152
|
-
if (i) {
|
|
153
|
-
const from = dropSchemas[i - 1];
|
|
154
|
-
dropSchemas.splice(i - 1, 1);
|
|
155
|
-
renameSchemaInStructures(dbStructure.tables, from, schema);
|
|
156
|
-
renameSchemaInStructures(dbStructure.views, from, schema);
|
|
157
|
-
renameSchemaInStructures(dbStructure.indexes, from, schema);
|
|
158
|
-
renameSchemaInStructures(dbStructure.excludes, from, schema);
|
|
159
|
-
renameSchemaInStructures(dbStructure.constraints, from, schema);
|
|
160
|
-
renameSchemaInStructures(dbStructure.triggers, from, schema);
|
|
161
|
-
renameSchemaInStructures(dbStructure.enums, from, schema);
|
|
162
|
-
renameSchemaInStructures(dbStructure.domains, from, schema);
|
|
163
|
-
renameSchemaInStructures(dbStructure.collations, from, schema);
|
|
164
|
-
for (const table of dbStructure.tables) {
|
|
165
|
-
for (const column of table.columns) {
|
|
166
|
-
if (column.typeSchema === from) {
|
|
167
|
-
column.typeSchema = schema;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
ast.push({
|
|
172
|
-
type: "renameSchema",
|
|
173
|
-
from,
|
|
174
|
-
to: schema
|
|
175
|
-
});
|
|
176
|
-
continue;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
ast.push({
|
|
180
|
-
type: "schema",
|
|
181
|
-
action: "create",
|
|
182
|
-
name: schema
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
for (const schema of dropSchemas) {
|
|
186
|
-
ast.push({
|
|
187
|
-
type: "schema",
|
|
188
|
-
action: "drop",
|
|
189
|
-
name: schema
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
};
|
|
193
|
-
const renameSchemaInStructures = (items, from, to) => {
|
|
194
|
-
for (const item of items) {
|
|
195
|
-
if (item.schemaName === from) {
|
|
196
|
-
item.schemaName = to;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
const processExtensions = (ast, dbStructure, {
|
|
202
|
-
currentSchema,
|
|
203
|
-
internal: { extensions, generatorIgnore }
|
|
204
|
-
}) => {
|
|
205
|
-
const codeExtensions = extensions?.map((ext) => {
|
|
206
|
-
const [schema, name] = rakeDb.getSchemaAndTableFromName(currentSchema, ext.name);
|
|
207
|
-
return { schema, name, version: ext.version };
|
|
208
|
-
});
|
|
209
|
-
for (const dbExt of dbStructure.extensions) {
|
|
210
|
-
if (generatorIgnore?.schemas?.includes(dbExt.schemaName) || generatorIgnore?.extensions?.includes(dbExt.name)) {
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
if (codeExtensions) {
|
|
214
|
-
let found = false;
|
|
215
|
-
for (let i = 0; i < codeExtensions.length; i++) {
|
|
216
|
-
const codeExt = codeExtensions[i];
|
|
217
|
-
if (dbExt.name === codeExt.name && dbExt.schemaName === (codeExt.schema ?? currentSchema) && (!codeExt.version || codeExt.version === dbExt.version)) {
|
|
218
|
-
found = true;
|
|
219
|
-
codeExtensions.splice(i, 1);
|
|
220
|
-
break;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
if (found) continue;
|
|
224
|
-
}
|
|
225
|
-
ast.push({
|
|
226
|
-
type: "extension",
|
|
227
|
-
action: "drop",
|
|
228
|
-
schema: dbExt.schemaName === currentSchema ? void 0 : dbExt.schemaName,
|
|
229
|
-
name: dbExt.name,
|
|
230
|
-
version: dbExt.version
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
if (codeExtensions?.length) {
|
|
234
|
-
ast.push(
|
|
235
|
-
...codeExtensions.map((ext) => ({
|
|
236
|
-
type: "extension",
|
|
237
|
-
action: "create",
|
|
238
|
-
...ext
|
|
239
|
-
}))
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
const processColumns = async (adapter, config, structureToAstCtx, dbStructure, domainsMap, changeTableData, ast, currentSchema, compareSql, typeCastsCache, verifying) => {
|
|
245
|
-
const { dbTable } = changeTableData;
|
|
246
|
-
const dbColumns = Object.fromEntries(
|
|
247
|
-
dbTable.columns.map((column) => [column.name, column])
|
|
248
|
-
);
|
|
249
|
-
const { columnsToAdd, columnsToDrop, columnsToChange } = groupColumns(
|
|
250
|
-
structureToAstCtx,
|
|
251
|
-
dbStructure,
|
|
252
|
-
domainsMap,
|
|
253
|
-
dbColumns,
|
|
254
|
-
changeTableData
|
|
255
|
-
);
|
|
256
|
-
await addOrRenameColumns(
|
|
257
|
-
config,
|
|
258
|
-
dbStructure,
|
|
259
|
-
changeTableData,
|
|
260
|
-
columnsToAdd,
|
|
261
|
-
columnsToDrop,
|
|
262
|
-
columnsToChange,
|
|
263
|
-
verifying
|
|
264
|
-
);
|
|
265
|
-
await changeColumns(
|
|
266
|
-
adapter,
|
|
267
|
-
config,
|
|
268
|
-
structureToAstCtx,
|
|
269
|
-
dbStructure,
|
|
270
|
-
domainsMap,
|
|
271
|
-
ast,
|
|
272
|
-
currentSchema,
|
|
273
|
-
dbColumns,
|
|
274
|
-
columnsToChange,
|
|
275
|
-
compareSql,
|
|
276
|
-
changeTableData,
|
|
277
|
-
typeCastsCache,
|
|
278
|
-
verifying
|
|
279
|
-
);
|
|
280
|
-
dropColumns(changeTableData, columnsToDrop);
|
|
281
|
-
};
|
|
282
|
-
const groupColumns = (structureToAstCtx, dbStructure, domainsMap, dbColumns, changeTableData) => {
|
|
283
|
-
const columnsToAdd = [];
|
|
284
|
-
const columnsToDrop = [];
|
|
285
|
-
const columnsToChange = /* @__PURE__ */ new Map();
|
|
286
|
-
const columnsToChangeByDbName = /* @__PURE__ */ new Map();
|
|
287
|
-
const { codeTable, dbTable, dbTableData } = changeTableData;
|
|
288
|
-
const checks = rakeDb.getDbTableColumnsChecks(changeTableData.dbTableData);
|
|
289
|
-
for (const key in codeTable.shape) {
|
|
290
|
-
const column = codeTable.shape[key];
|
|
291
|
-
if (!column.dataType) continue;
|
|
292
|
-
const name = column.data.name ?? key;
|
|
293
|
-
if (dbColumns[name]) {
|
|
294
|
-
columnsToChange.set(key, { key, dbName: name, column });
|
|
295
|
-
columnsToChangeByDbName.set(name, true);
|
|
296
|
-
} else {
|
|
297
|
-
columnsToAdd.push({ key, column });
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
for (const name in dbColumns) {
|
|
301
|
-
if (columnsToChangeByDbName.has(name)) continue;
|
|
302
|
-
const [key, column] = rakeDb.dbColumnToAst(
|
|
303
|
-
structureToAstCtx,
|
|
304
|
-
dbStructure,
|
|
305
|
-
domainsMap,
|
|
306
|
-
dbTable.name,
|
|
307
|
-
dbColumns[name],
|
|
308
|
-
dbTable,
|
|
309
|
-
dbTableData,
|
|
310
|
-
checks
|
|
311
|
-
);
|
|
312
|
-
columnsToDrop.push({ key, column });
|
|
313
|
-
}
|
|
314
|
-
return {
|
|
315
|
-
columnsToAdd,
|
|
316
|
-
columnsToDrop,
|
|
317
|
-
columnsToChange
|
|
318
|
-
};
|
|
319
|
-
};
|
|
320
|
-
const addOrRenameColumns = async (config, dbStructure, {
|
|
321
|
-
dbTableData,
|
|
322
|
-
schema,
|
|
323
|
-
changeTableAst: { name: tableName, shape }
|
|
324
|
-
}, columnsToAdd, columnsToDrop, columnsToChange, verifying) => {
|
|
325
|
-
for (const { key, column } of columnsToAdd) {
|
|
326
|
-
if (columnsToDrop.length) {
|
|
327
|
-
const codeName = column.data.name ?? key;
|
|
328
|
-
const i = await promptCreateOrRename(
|
|
329
|
-
"column",
|
|
330
|
-
codeName,
|
|
331
|
-
columnsToDrop.map((x) => x.key),
|
|
332
|
-
verifying
|
|
333
|
-
);
|
|
334
|
-
if (i) {
|
|
335
|
-
const drop = columnsToDrop[i - 1];
|
|
336
|
-
columnsToDrop.splice(i - 1, 1);
|
|
337
|
-
const from = drop.column.data.name ?? drop.key;
|
|
338
|
-
columnsToChange.set(from, {
|
|
339
|
-
key,
|
|
340
|
-
dbName: from,
|
|
341
|
-
column: column.name(codeName)
|
|
342
|
-
});
|
|
343
|
-
const to = config.snakeCase ? internal.toSnakeCase(key) : key;
|
|
344
|
-
if (dbTableData.primaryKey) {
|
|
345
|
-
renameColumn(dbTableData.primaryKey.columns, from, to);
|
|
346
|
-
}
|
|
347
|
-
for (const index of dbTableData.indexes) {
|
|
348
|
-
for (const column2 of index.columns) {
|
|
349
|
-
if ("column" in column2 && column2.column === from) {
|
|
350
|
-
column2.column = to;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
for (const exclude of dbTableData.excludes) {
|
|
355
|
-
for (const column2 of exclude.columns) {
|
|
356
|
-
if ("column" in column2 && column2.column === from) {
|
|
357
|
-
column2.column = to;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
for (const c of dbTableData.constraints) {
|
|
362
|
-
if (c.check?.columns) {
|
|
363
|
-
renameColumn(c.check.columns, from, to);
|
|
364
|
-
}
|
|
365
|
-
if (c.references) {
|
|
366
|
-
renameColumn(c.references.columns, from, to);
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
for (const c of dbStructure.constraints) {
|
|
370
|
-
if (c.references && c.references.foreignSchema === schema && c.references.foreignTable === tableName) {
|
|
371
|
-
renameColumn(c.references.foreignColumns, from, to);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
continue;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
shape[key] = {
|
|
378
|
-
type: "add",
|
|
379
|
-
item: column
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
};
|
|
383
|
-
const dropColumns = ({ changeTableAst: { shape } }, columnsToDrop) => {
|
|
384
|
-
for (const { key, column } of columnsToDrop) {
|
|
385
|
-
shape[key] = {
|
|
386
|
-
type: "drop",
|
|
387
|
-
item: column
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
};
|
|
391
|
-
const changeColumns = async (adapter, config, structureToAstCtx, dbStructure, domainsMap, ast, currentSchema, dbColumns, columnsToChange, compareSql, changeTableData, typeCastsCache, verifying) => {
|
|
392
|
-
for (const [
|
|
393
|
-
key,
|
|
394
|
-
{ key: codeKey, dbName, column: codeColumn }
|
|
395
|
-
] of columnsToChange) {
|
|
396
|
-
const dbColumnStructure = dbColumns[dbName];
|
|
397
|
-
const dbColumn = rakeDb.instantiateDbColumn(
|
|
398
|
-
structureToAstCtx,
|
|
399
|
-
dbStructure,
|
|
400
|
-
domainsMap,
|
|
401
|
-
dbColumnStructure
|
|
402
|
-
);
|
|
403
|
-
const action = await compareColumns(
|
|
404
|
-
adapter,
|
|
405
|
-
domainsMap,
|
|
406
|
-
ast,
|
|
407
|
-
currentSchema,
|
|
408
|
-
compareSql,
|
|
409
|
-
changeTableData,
|
|
410
|
-
typeCastsCache,
|
|
411
|
-
verifying,
|
|
412
|
-
key,
|
|
413
|
-
dbName,
|
|
414
|
-
dbColumn,
|
|
415
|
-
codeColumn
|
|
416
|
-
);
|
|
417
|
-
if (action === "change") {
|
|
418
|
-
changeColumn(changeTableData, key, dbName, dbColumn, codeColumn);
|
|
419
|
-
} else if (action === "recreate") {
|
|
420
|
-
changeTableData.changeTableAst.shape[key] = [
|
|
421
|
-
{
|
|
422
|
-
type: "drop",
|
|
423
|
-
item: dbColumn
|
|
424
|
-
},
|
|
425
|
-
{
|
|
426
|
-
type: "add",
|
|
427
|
-
item: codeColumn
|
|
428
|
-
}
|
|
429
|
-
];
|
|
430
|
-
} else if (action !== "recreate") {
|
|
431
|
-
const to = codeColumn.data.name ?? codeKey;
|
|
432
|
-
if (dbName !== to) {
|
|
433
|
-
changeTableData.changeTableAst.shape[config.snakeCase ? dbName === internal.toSnakeCase(codeKey) ? codeKey : dbName : dbName] = {
|
|
434
|
-
type: "rename",
|
|
435
|
-
name: config.snakeCase ? to === internal.toSnakeCase(codeKey) ? codeKey : to : to
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
};
|
|
441
|
-
const compareColumns = async (adapter, domainsMap, ast, currentSchema, compareSql, changeTableData, typeCastsCache, verifying, key, dbName, dbColumn, codeColumn) => {
|
|
442
|
-
if (dbColumn instanceof internal.ArrayColumn && codeColumn instanceof internal.ArrayColumn) {
|
|
443
|
-
dbColumn = dbColumn.data.item;
|
|
444
|
-
codeColumn = codeColumn.data.item;
|
|
445
|
-
}
|
|
446
|
-
const dbType = getColumnDbType(dbColumn, currentSchema);
|
|
447
|
-
const codeType = getColumnDbType(codeColumn, currentSchema);
|
|
448
|
-
if (dbType !== codeType) {
|
|
449
|
-
const typeCasts = await getTypeCasts(adapter, typeCastsCache);
|
|
450
|
-
const dbBaseType = internal.getColumnBaseType(dbColumn, domainsMap, dbType);
|
|
451
|
-
const codeBaseType = internal.getColumnBaseType(codeColumn, domainsMap, codeType);
|
|
452
|
-
if (!typeCasts.get(dbBaseType)?.has(codeBaseType)) {
|
|
453
|
-
if (!(dbColumn instanceof internal.EnumColumn) || !(codeColumn instanceof internal.EnumColumn) || !internal.deepCompare(dbColumn.options, codeColumn.options)) {
|
|
454
|
-
if (verifying) throw new AbortSignal();
|
|
455
|
-
const tableName = rakeDb.concatSchemaAndName(changeTableData.changeTableAst);
|
|
456
|
-
const abort = await rakeDb.promptSelect({
|
|
457
|
-
message: `Cannot cast type of ${tableName}'s column ${key} from ${dbType} to ${codeType}`,
|
|
458
|
-
options: [
|
|
459
|
-
`${internal.colors.yellowBold(
|
|
460
|
-
`-/+`
|
|
461
|
-
)} recreate the column, existing data will be ${internal.colors.red(
|
|
462
|
-
"lost"
|
|
463
|
-
)}`,
|
|
464
|
-
`write migration manually`
|
|
465
|
-
]
|
|
466
|
-
});
|
|
467
|
-
if (abort) {
|
|
468
|
-
throw new AbortSignal();
|
|
469
|
-
}
|
|
470
|
-
dbColumn.data.name = codeColumn.data.name;
|
|
471
|
-
return "recreate";
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
return "change";
|
|
475
|
-
}
|
|
476
|
-
const dbData = dbColumn.data;
|
|
477
|
-
const codeData = codeColumn.data;
|
|
478
|
-
for (const key2 of ["isNullable", "comment"]) {
|
|
479
|
-
if (dbData[key2] !== codeData[key2]) {
|
|
480
|
-
return "change";
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
for (const key2 of [
|
|
484
|
-
"maxChars",
|
|
485
|
-
"collation",
|
|
486
|
-
"compression",
|
|
487
|
-
"numericPrecision",
|
|
488
|
-
"numericScale",
|
|
489
|
-
"dateTimePrecision"
|
|
490
|
-
]) {
|
|
491
|
-
if (key2 in codeData && dbData[key2] !== codeData[key2]) {
|
|
492
|
-
return "change";
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
if (dbColumn.data.isOfCustomType) {
|
|
496
|
-
const { typmod } = dbColumn.data;
|
|
497
|
-
if (typmod !== void 0 && typmod !== -1) {
|
|
498
|
-
const i = codeColumn.dataType.indexOf("(");
|
|
499
|
-
if (i === -1 || codeColumn.dataType.slice(i + 1, -1) !== `${typmod}`) {
|
|
500
|
-
return "change";
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
if (!internal.deepCompare(
|
|
505
|
-
dbData.identity,
|
|
506
|
-
codeData.identity ? {
|
|
507
|
-
always: false,
|
|
508
|
-
start: 1,
|
|
509
|
-
increment: 1,
|
|
510
|
-
cache: 1,
|
|
511
|
-
cycle: false,
|
|
512
|
-
...codeData.identity ?? {}
|
|
513
|
-
} : void 0
|
|
514
|
-
)) {
|
|
515
|
-
return "change";
|
|
516
|
-
}
|
|
517
|
-
if (dbData.default !== void 0 && codeData.default !== void 0) {
|
|
518
|
-
const valuesBeforeLen = compareSql.values.length;
|
|
519
|
-
const dbDefault = rakeDb.encodeColumnDefault(
|
|
520
|
-
dbData.default,
|
|
521
|
-
compareSql.values,
|
|
522
|
-
dbColumn
|
|
523
|
-
);
|
|
524
|
-
const dbValues = compareSql.values.slice(valuesBeforeLen);
|
|
525
|
-
const codeDefault = rakeDb.encodeColumnDefault(
|
|
526
|
-
codeData.default,
|
|
527
|
-
compareSql.values,
|
|
528
|
-
codeColumn
|
|
529
|
-
);
|
|
530
|
-
const codeValues = compareSql.values.slice(valuesBeforeLen);
|
|
531
|
-
if (dbValues.length !== codeValues.length || dbValues.length && JSON.stringify(dbValues) !== JSON.stringify(codeValues)) {
|
|
532
|
-
compareSql.values.length = valuesBeforeLen;
|
|
533
|
-
return "change";
|
|
534
|
-
} else if (dbDefault !== codeDefault && dbDefault !== `(${codeDefault})`) {
|
|
535
|
-
const change = () => {
|
|
536
|
-
changeColumn(changeTableData, key, dbName, dbColumn, codeColumn);
|
|
537
|
-
if (!changeTableData.pushedAst) {
|
|
538
|
-
changeTableData.pushedAst = true;
|
|
539
|
-
ast.push(changeTableData.changeTableAst);
|
|
540
|
-
}
|
|
541
|
-
};
|
|
542
|
-
compareSql.expressions.push({
|
|
543
|
-
inDb: dbDefault,
|
|
544
|
-
inCode: codeDefault,
|
|
545
|
-
change
|
|
546
|
-
});
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
return;
|
|
550
|
-
};
|
|
551
|
-
const getTypeCasts = async (adapter, typeCastsCache) => {
|
|
552
|
-
let typeCasts = typeCastsCache.value;
|
|
553
|
-
if (!typeCasts) {
|
|
554
|
-
const { rows } = await adapter.arrays(`SELECT s.typname, t.typname
|
|
555
|
-
FROM pg_cast
|
|
556
|
-
JOIN pg_type AS s ON s.oid = castsource
|
|
557
|
-
JOIN pg_type AS t ON t.oid = casttarget`);
|
|
558
|
-
const directTypeCasts = /* @__PURE__ */ new Map();
|
|
559
|
-
for (const [source, target] of rows) {
|
|
560
|
-
const set = directTypeCasts.get(source);
|
|
561
|
-
if (set) {
|
|
562
|
-
set.add(target);
|
|
563
|
-
} else {
|
|
564
|
-
directTypeCasts.set(source, /* @__PURE__ */ new Set([target]));
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
typeCasts = /* @__PURE__ */ new Map();
|
|
568
|
-
for (const [type, directSet] of directTypeCasts.entries()) {
|
|
569
|
-
const set = new Set(directSet);
|
|
570
|
-
typeCasts.set(type, set);
|
|
571
|
-
for (const subtype of directSet) {
|
|
572
|
-
const subset = directTypeCasts.get(subtype);
|
|
573
|
-
if (subset) {
|
|
574
|
-
for (const type2 of subset) {
|
|
575
|
-
set.add(type2);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
typeCastsCache.value = typeCasts;
|
|
581
|
-
}
|
|
582
|
-
return typeCasts;
|
|
583
|
-
};
|
|
584
|
-
const changeColumn = (changeTableData, key, dbName, dbColumn, codeColumn) => {
|
|
585
|
-
dbColumn.data.as = codeColumn.data.as = void 0;
|
|
586
|
-
const simpleCodeColumn = Object.create(codeColumn);
|
|
587
|
-
simpleCodeColumn.data = {
|
|
588
|
-
...codeColumn.data,
|
|
589
|
-
primaryKey: void 0,
|
|
590
|
-
indexes: void 0,
|
|
591
|
-
excludes: void 0,
|
|
592
|
-
foreignKeys: void 0,
|
|
593
|
-
check: void 0
|
|
594
|
-
};
|
|
595
|
-
changeTableData.changingColumns[dbName] = {
|
|
596
|
-
from: dbColumn,
|
|
597
|
-
to: simpleCodeColumn
|
|
598
|
-
};
|
|
599
|
-
changeTableData.changeTableAst.shape[key] = {
|
|
600
|
-
type: "change",
|
|
601
|
-
from: { column: dbColumn },
|
|
602
|
-
to: { column: simpleCodeColumn }
|
|
603
|
-
};
|
|
604
|
-
};
|
|
605
|
-
const getColumnDbType = (column, currentSchema) => {
|
|
606
|
-
if (column instanceof internal.EnumColumn) {
|
|
607
|
-
const [schema = currentSchema, name] = rakeDb.getSchemaAndTableFromName(
|
|
608
|
-
currentSchema,
|
|
609
|
-
column.enumName
|
|
610
|
-
);
|
|
611
|
-
return `${schema}.${name}`;
|
|
612
|
-
} else if (column instanceof internal.ArrayColumn) {
|
|
613
|
-
const { item } = column.data;
|
|
614
|
-
let type = item instanceof internal.EnumColumn ? item.enumName : item.dataType;
|
|
615
|
-
type = type.startsWith(currentSchema + ".") ? type.slice(currentSchema.length + 1) : type;
|
|
616
|
-
return type + "[]".repeat(column.data.arrayDims);
|
|
617
|
-
} else if (column.data.isOfCustomType) {
|
|
618
|
-
let type = column.dataType;
|
|
619
|
-
const i = type.indexOf("(");
|
|
620
|
-
if (i !== -1) {
|
|
621
|
-
type = type.slice(0, i);
|
|
622
|
-
}
|
|
623
|
-
return type.includes(".") ? type : currentSchema + "." + type;
|
|
624
|
-
} else {
|
|
625
|
-
return column.dataType;
|
|
626
|
-
}
|
|
627
|
-
};
|
|
628
|
-
const renameColumn = (columns, from, to) => {
|
|
629
|
-
for (let i = 0; i < columns.length; i++) {
|
|
630
|
-
if (columns[i] === from) {
|
|
631
|
-
columns[i] = to;
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
};
|
|
635
|
-
|
|
636
|
-
const processDomains = async (ast, adapter, domainsMap, dbStructure, {
|
|
637
|
-
codeItems: { domains },
|
|
638
|
-
structureToAstCtx,
|
|
639
|
-
currentSchema,
|
|
640
|
-
internal: { generatorIgnore }
|
|
641
|
-
}, pendingDbTypes) => {
|
|
642
|
-
const codeDomains = [];
|
|
643
|
-
if (domains) {
|
|
644
|
-
for (const { schemaName, name, column } of domains) {
|
|
645
|
-
codeDomains.push(
|
|
646
|
-
makeComparableDomain(currentSchema, schemaName, name, column)
|
|
647
|
-
);
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
const tableExpressions = [];
|
|
651
|
-
const holdCodeDomains = /* @__PURE__ */ new Set();
|
|
652
|
-
for (const domain of dbStructure.domains) {
|
|
653
|
-
if (generatorIgnore?.schemas?.includes(domain.schemaName) || generatorIgnore?.domains?.includes(domain.name)) {
|
|
654
|
-
continue;
|
|
655
|
-
}
|
|
656
|
-
const dbColumn = rakeDb.instantiateDbColumn(
|
|
657
|
-
structureToAstCtx,
|
|
658
|
-
dbStructure,
|
|
659
|
-
domainsMap,
|
|
660
|
-
{
|
|
661
|
-
// not destructuring `domain` because need to ignore `numericPrecision`, `numericScale`, etc.,
|
|
662
|
-
// that are loaded from db, but not defined in the code
|
|
663
|
-
schemaName: domain.typeSchema,
|
|
664
|
-
tableName: "N/A",
|
|
665
|
-
name: domain.name,
|
|
666
|
-
typeSchema: domain.typeSchema,
|
|
667
|
-
type: domain.type,
|
|
668
|
-
arrayDims: domain.arrayDims,
|
|
669
|
-
default: domain.default,
|
|
670
|
-
isNullable: domain.isNullable,
|
|
671
|
-
collate: domain.collate,
|
|
672
|
-
maxChars: domain.maxChars,
|
|
673
|
-
typmod: -1
|
|
674
|
-
}
|
|
675
|
-
);
|
|
676
|
-
if (domain.checks) {
|
|
677
|
-
dbColumn.data.checks = domain.checks.map((check) => ({
|
|
678
|
-
sql: new internal.RawSql([[check]])
|
|
679
|
-
}));
|
|
680
|
-
}
|
|
681
|
-
const dbDomain = makeComparableDomain(
|
|
682
|
-
currentSchema,
|
|
683
|
-
domain.schemaName,
|
|
684
|
-
domain.name,
|
|
685
|
-
dbColumn
|
|
686
|
-
);
|
|
687
|
-
const found = codeDomains.filter(
|
|
688
|
-
(codeDomain) => internal.deepCompare(dbDomain.compare, codeDomain.compare)
|
|
689
|
-
);
|
|
690
|
-
if ((domain.default || domain.checks?.length) && found.length) {
|
|
691
|
-
for (const codeDomain of found) {
|
|
692
|
-
holdCodeDomains.add(codeDomain);
|
|
693
|
-
}
|
|
694
|
-
const compare = [];
|
|
695
|
-
pushCompareDefault(compare, domain, found);
|
|
696
|
-
pushCompareChecks(compare, domain, found);
|
|
697
|
-
const source = `(VALUES (NULL::${getColumnDbType(
|
|
698
|
-
dbColumn,
|
|
699
|
-
currentSchema
|
|
700
|
-
)})) t(value)`;
|
|
701
|
-
tableExpressions.push({
|
|
702
|
-
compare,
|
|
703
|
-
source,
|
|
704
|
-
handle(i) {
|
|
705
|
-
const codeDomain = i === void 0 ? void 0 : found[i];
|
|
706
|
-
if (!codeDomain) {
|
|
707
|
-
ast.push(dropAst(dbDomain));
|
|
708
|
-
} else {
|
|
709
|
-
holdCodeDomains.delete(codeDomain);
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
});
|
|
713
|
-
} else if (found.length) {
|
|
714
|
-
let i = codeDomains.findIndex(
|
|
715
|
-
(codeDomain) => codeDomain.name === dbDomain.name && codeDomain.schemaName === dbDomain.schemaName
|
|
716
|
-
);
|
|
717
|
-
if (i === -1) {
|
|
718
|
-
i = 0;
|
|
719
|
-
const first = found[0];
|
|
720
|
-
ast.push({
|
|
721
|
-
type: "renameType",
|
|
722
|
-
kind: "DOMAIN",
|
|
723
|
-
fromSchema: dbDomain.schemaName,
|
|
724
|
-
from: dbDomain.name,
|
|
725
|
-
toSchema: first.schemaName,
|
|
726
|
-
to: first.name
|
|
727
|
-
});
|
|
728
|
-
pendingDbTypes.add(first.schemaName, first.name);
|
|
729
|
-
}
|
|
730
|
-
codeDomains.splice(i, 1);
|
|
731
|
-
} else {
|
|
732
|
-
ast.push(dropAst(dbDomain));
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
for (const codeDomain of codeDomains) {
|
|
736
|
-
if (!holdCodeDomains.has(codeDomain)) {
|
|
737
|
-
ast.push(createAst(codeDomain));
|
|
738
|
-
pendingDbTypes.add(codeDomain.schemaName, codeDomain.name);
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
if (tableExpressions.length) {
|
|
742
|
-
await compareSqlExpressions(tableExpressions, adapter);
|
|
743
|
-
if (holdCodeDomains.size) {
|
|
744
|
-
for (const codeDomain of holdCodeDomains.keys()) {
|
|
745
|
-
ast.push(createAst(codeDomain));
|
|
746
|
-
pendingDbTypes.add(codeDomain.schemaName, codeDomain.name);
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
};
|
|
751
|
-
const makeComparableDomain = (currentSchema, schemaName, name, column) => {
|
|
752
|
-
let arrayDims = 0;
|
|
753
|
-
const isNullable = column.data.isNullable ?? false;
|
|
754
|
-
let inner = column;
|
|
755
|
-
while (inner instanceof internal.ArrayColumn) {
|
|
756
|
-
inner = inner.data.item;
|
|
757
|
-
arrayDims++;
|
|
758
|
-
}
|
|
759
|
-
const fullType = getColumnDbType(inner, currentSchema);
|
|
760
|
-
const [typeSchema = "pg_catalog", type] = rakeDb.getSchemaAndTableFromName(
|
|
761
|
-
currentSchema,
|
|
762
|
-
fullType
|
|
763
|
-
);
|
|
764
|
-
return {
|
|
765
|
-
schemaName,
|
|
766
|
-
name,
|
|
767
|
-
column,
|
|
768
|
-
compare: {
|
|
769
|
-
type,
|
|
770
|
-
typeSchema,
|
|
771
|
-
arrayDims,
|
|
772
|
-
isNullable,
|
|
773
|
-
maxChars: inner.data.maxChars,
|
|
774
|
-
numericPrecision: inner.data.numericPrecision,
|
|
775
|
-
numericScale: inner.data.numericScale,
|
|
776
|
-
dateTimePrecision: inner.data.dateTimePrecision,
|
|
777
|
-
collate: column.data.collate,
|
|
778
|
-
hasDefault: column.data.default !== void 0,
|
|
779
|
-
hasChecks: !!column.data.checks?.length
|
|
780
|
-
}
|
|
781
|
-
};
|
|
782
|
-
};
|
|
783
|
-
const pushCompareDefault = (compare, domain, found) => {
|
|
784
|
-
if (domain.default) {
|
|
785
|
-
compare.push({
|
|
786
|
-
inDb: domain.default,
|
|
787
|
-
inCode: found.map((codeDomain) => {
|
|
788
|
-
const value = codeDomain.column.data.default;
|
|
789
|
-
if ("sql" in value) {
|
|
790
|
-
return value.sql;
|
|
791
|
-
}
|
|
792
|
-
return value;
|
|
793
|
-
})
|
|
794
|
-
});
|
|
795
|
-
}
|
|
796
|
-
};
|
|
797
|
-
const pushCompareChecks = (compare, domain, found) => {
|
|
798
|
-
if (domain.checks?.length) {
|
|
799
|
-
const inCode = found.flatMap(
|
|
800
|
-
(codeDomain) => codeDomain.column.data.checks?.map(
|
|
801
|
-
(check) => typeof check === "string" ? check : check.sql
|
|
802
|
-
) || internal.emptyArray
|
|
803
|
-
);
|
|
804
|
-
compare.push(
|
|
805
|
-
...domain.checks.map((check) => ({
|
|
806
|
-
inDb: check,
|
|
807
|
-
inCode
|
|
808
|
-
}))
|
|
809
|
-
);
|
|
810
|
-
}
|
|
811
|
-
};
|
|
812
|
-
const dropAst = (dbDomain) => ({
|
|
813
|
-
type: "domain",
|
|
814
|
-
action: "drop",
|
|
815
|
-
schema: dbDomain.schemaName,
|
|
816
|
-
name: dbDomain.name,
|
|
817
|
-
baseType: dbDomain.column
|
|
818
|
-
});
|
|
819
|
-
const createAst = (codeDomain) => ({
|
|
820
|
-
type: "domain",
|
|
821
|
-
action: "create",
|
|
822
|
-
schema: codeDomain.schemaName,
|
|
823
|
-
name: codeDomain.name,
|
|
824
|
-
baseType: codeDomain.column
|
|
825
|
-
});
|
|
826
|
-
|
|
827
|
-
const processEnums = async (ast, dbStructure, {
|
|
828
|
-
codeItems: { enums },
|
|
829
|
-
currentSchema,
|
|
830
|
-
verifying,
|
|
831
|
-
internal: { generatorIgnore }
|
|
832
|
-
}, pendingDbTypes) => {
|
|
833
|
-
const createEnums = [];
|
|
834
|
-
const dropEnums = [];
|
|
835
|
-
for (const [, codeEnum] of enums) {
|
|
836
|
-
const { schema = currentSchema, name } = codeEnum;
|
|
837
|
-
const dbEnum = dbStructure.enums.find(
|
|
838
|
-
(x) => x.schemaName === schema && x.name === name
|
|
839
|
-
);
|
|
840
|
-
if (!dbEnum) {
|
|
841
|
-
createEnums.push(codeEnum);
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
for (const dbEnum of dbStructure.enums) {
|
|
845
|
-
if (generatorIgnore?.schemas?.includes(dbEnum.schemaName) || generatorIgnore?.enums?.includes(dbEnum.name)) {
|
|
846
|
-
continue;
|
|
847
|
-
}
|
|
848
|
-
const codeEnum = enums.get(`${dbEnum.schemaName}.${dbEnum.name}`);
|
|
849
|
-
if (codeEnum) {
|
|
850
|
-
changeEnum(ast, dbEnum, codeEnum, pendingDbTypes);
|
|
851
|
-
continue;
|
|
852
|
-
}
|
|
853
|
-
const i = createEnums.findIndex((x) => x.name === dbEnum.name);
|
|
854
|
-
if (i !== -1) {
|
|
855
|
-
const codeEnum2 = createEnums[i];
|
|
856
|
-
createEnums.splice(i, 1);
|
|
857
|
-
const fromSchema = dbEnum.schemaName;
|
|
858
|
-
const toSchema = codeEnum2.schema ?? currentSchema;
|
|
859
|
-
renameColumnsTypeSchema(dbStructure, fromSchema, toSchema);
|
|
860
|
-
ast.push({
|
|
861
|
-
type: "renameType",
|
|
862
|
-
kind: "TYPE",
|
|
863
|
-
fromSchema,
|
|
864
|
-
from: dbEnum.name,
|
|
865
|
-
toSchema,
|
|
866
|
-
to: dbEnum.name
|
|
867
|
-
});
|
|
868
|
-
pendingDbTypes.add(toSchema, dbEnum.name);
|
|
869
|
-
changeEnum(ast, dbEnum, codeEnum2, pendingDbTypes);
|
|
870
|
-
continue;
|
|
871
|
-
}
|
|
872
|
-
dropEnums.push(dbEnum);
|
|
873
|
-
}
|
|
874
|
-
for (const codeEnum of createEnums) {
|
|
875
|
-
if (dropEnums.length) {
|
|
876
|
-
const i = await promptCreateOrRename(
|
|
877
|
-
"enum",
|
|
878
|
-
codeEnum.name,
|
|
879
|
-
dropEnums.map((x) => x.name),
|
|
880
|
-
verifying
|
|
881
|
-
);
|
|
882
|
-
if (i) {
|
|
883
|
-
const dbEnum = dropEnums[i - 1];
|
|
884
|
-
dropEnums.splice(i - 1, 1);
|
|
885
|
-
const fromSchema = dbEnum.schemaName;
|
|
886
|
-
const from = dbEnum.name;
|
|
887
|
-
const toSchema = codeEnum.schema ?? currentSchema;
|
|
888
|
-
const to = codeEnum.name;
|
|
889
|
-
if (fromSchema !== toSchema) {
|
|
890
|
-
renameColumnsTypeSchema(dbStructure, fromSchema, toSchema);
|
|
891
|
-
}
|
|
892
|
-
for (const table of dbStructure.tables) {
|
|
893
|
-
for (const column of table.columns) {
|
|
894
|
-
if (column.type === from) {
|
|
895
|
-
column.type = to;
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
ast.push({
|
|
900
|
-
type: "renameType",
|
|
901
|
-
kind: "TYPE",
|
|
902
|
-
fromSchema,
|
|
903
|
-
from,
|
|
904
|
-
toSchema,
|
|
905
|
-
to
|
|
906
|
-
});
|
|
907
|
-
pendingDbTypes.add(toSchema, to);
|
|
908
|
-
changeEnum(ast, dbEnum, codeEnum, pendingDbTypes);
|
|
909
|
-
continue;
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
ast.push({
|
|
913
|
-
type: "enum",
|
|
914
|
-
action: "create",
|
|
915
|
-
...codeEnum
|
|
916
|
-
});
|
|
917
|
-
pendingDbTypes.add(codeEnum.schema, codeEnum.name);
|
|
918
|
-
}
|
|
919
|
-
for (const dbEnum of dropEnums) {
|
|
920
|
-
ast.push({
|
|
921
|
-
type: "enum",
|
|
922
|
-
action: "drop",
|
|
923
|
-
schema: dbEnum.schemaName,
|
|
924
|
-
name: dbEnum.name,
|
|
925
|
-
values: dbEnum.values
|
|
926
|
-
});
|
|
927
|
-
}
|
|
928
|
-
};
|
|
929
|
-
const changeEnum = (ast, dbEnum, codeEnum, pendingDbTypes) => {
|
|
930
|
-
const { values: dbValues } = dbEnum;
|
|
931
|
-
const { values: codeValues, schema, name } = codeEnum;
|
|
932
|
-
if (dbValues.length < codeValues.length) {
|
|
933
|
-
if (!dbValues.some((value) => !codeValues.includes(value))) {
|
|
934
|
-
ast.push({
|
|
935
|
-
type: "enumValues",
|
|
936
|
-
action: "add",
|
|
937
|
-
schema,
|
|
938
|
-
name,
|
|
939
|
-
values: codeValues.filter((value) => !dbValues.includes(value))
|
|
940
|
-
});
|
|
941
|
-
pendingDbTypes.add(schema, name);
|
|
942
|
-
return;
|
|
943
|
-
}
|
|
944
|
-
} else if (dbValues.length > codeValues.length) {
|
|
945
|
-
if (!codeValues.some((value) => !dbValues.includes(value))) {
|
|
946
|
-
ast.push({
|
|
947
|
-
type: "enumValues",
|
|
948
|
-
action: "drop",
|
|
949
|
-
schema,
|
|
950
|
-
name,
|
|
951
|
-
values: dbValues.filter((value) => !codeValues.includes(value))
|
|
952
|
-
});
|
|
953
|
-
pendingDbTypes.add(schema, name);
|
|
954
|
-
return;
|
|
955
|
-
}
|
|
956
|
-
} else if (!dbValues.some((value) => !codeValues.includes(value))) {
|
|
957
|
-
return;
|
|
958
|
-
}
|
|
959
|
-
ast.push({
|
|
960
|
-
type: "changeEnumValues",
|
|
961
|
-
schema,
|
|
962
|
-
name,
|
|
963
|
-
fromValues: dbValues,
|
|
964
|
-
toValues: codeValues
|
|
965
|
-
});
|
|
966
|
-
pendingDbTypes.add(schema, name);
|
|
967
|
-
};
|
|
968
|
-
const renameColumnsTypeSchema = (dbStructure, from, to) => {
|
|
969
|
-
for (const table of dbStructure.tables) {
|
|
970
|
-
for (const column of table.columns) {
|
|
971
|
-
if (column.typeSchema === from) {
|
|
972
|
-
column.typeSchema = to;
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
};
|
|
977
|
-
|
|
978
|
-
const processPrimaryKey = (config, changeTableData) => {
|
|
979
|
-
const { codeTable } = changeTableData;
|
|
980
|
-
const columnsPrimaryKey = [];
|
|
981
|
-
for (const key in codeTable.shape) {
|
|
982
|
-
const column = codeTable.shape[key];
|
|
983
|
-
if (column.data.primaryKey) {
|
|
984
|
-
columnsPrimaryKey.push({ key, name: column.data.name ?? key });
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
changePrimaryKey(config, columnsPrimaryKey, changeTableData);
|
|
988
|
-
renamePrimaryKey(changeTableData);
|
|
989
|
-
};
|
|
990
|
-
const changePrimaryKey = (config, columnsPrimaryKey, {
|
|
991
|
-
codeTable,
|
|
992
|
-
dbTableData: { primaryKey: dbPrimaryKey },
|
|
993
|
-
changeTableAst: { shape, add, drop },
|
|
994
|
-
changingColumns
|
|
995
|
-
}) => {
|
|
996
|
-
const tablePrimaryKey = codeTable.internal.tableData.primaryKey;
|
|
997
|
-
const primaryKey = [
|
|
998
|
-
.../* @__PURE__ */ new Set([
|
|
999
|
-
...columnsPrimaryKey,
|
|
1000
|
-
...(config.snakeCase ? tablePrimaryKey?.columns.map((key) => ({
|
|
1001
|
-
key,
|
|
1002
|
-
name: internal.toSnakeCase(key)
|
|
1003
|
-
})) : tablePrimaryKey?.columns.map((key) => ({ key, name: key }))) ?? []
|
|
1004
|
-
])
|
|
1005
|
-
];
|
|
1006
|
-
if (dbPrimaryKey && primaryKey.length === dbPrimaryKey.columns.length && !primaryKey.some(
|
|
1007
|
-
({ name }) => !dbPrimaryKey.columns.some((dbName) => name === dbName)
|
|
1008
|
-
)) {
|
|
1009
|
-
if (primaryKey.length === 1) {
|
|
1010
|
-
const { key } = primaryKey[0];
|
|
1011
|
-
const changes = shape[key] && internal.toArray(shape[key]);
|
|
1012
|
-
if (changes) {
|
|
1013
|
-
for (const change of changes) {
|
|
1014
|
-
if (change.type !== "change") continue;
|
|
1015
|
-
if (change.from.column) {
|
|
1016
|
-
change.from.column.data.primaryKey = void 0;
|
|
1017
|
-
}
|
|
1018
|
-
if (change.to.column) {
|
|
1019
|
-
const column = Object.create(change.to.column);
|
|
1020
|
-
column.data = { ...column.data, primaryKey: void 0 };
|
|
1021
|
-
change.to.column = column;
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
return;
|
|
1027
|
-
}
|
|
1028
|
-
const toDrop = dbPrimaryKey?.columns.filter(
|
|
1029
|
-
(key) => !checkForColumnAddOrDrop(shape, key)
|
|
1030
|
-
);
|
|
1031
|
-
if (toDrop?.length) {
|
|
1032
|
-
if (toDrop.length === 1 && changingColumns[toDrop[0]]) {
|
|
1033
|
-
const column = changingColumns[toDrop[0]];
|
|
1034
|
-
column.from.data.primaryKey = dbPrimaryKey?.name ?? true;
|
|
1035
|
-
} else {
|
|
1036
|
-
drop.primaryKey = { columns: toDrop, name: dbPrimaryKey?.name };
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
const toAdd = primaryKey.filter(
|
|
1040
|
-
({ key }) => !checkForColumnAddOrDrop(shape, key)
|
|
1041
|
-
);
|
|
1042
|
-
if (toAdd.length) {
|
|
1043
|
-
if (toAdd.length === 1 && changingColumns[toAdd[0].name]) {
|
|
1044
|
-
const column = changingColumns[toAdd[0].name];
|
|
1045
|
-
column.to.data.primaryKey = tablePrimaryKey?.name ?? true;
|
|
1046
|
-
} else {
|
|
1047
|
-
add.primaryKey = {
|
|
1048
|
-
columns: toAdd.map((c) => c.key),
|
|
1049
|
-
name: tablePrimaryKey?.name
|
|
1050
|
-
};
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
};
|
|
1054
|
-
const renamePrimaryKey = ({
|
|
1055
|
-
codeTable,
|
|
1056
|
-
dbTableData: { primaryKey: dbPrimaryKey },
|
|
1057
|
-
schema,
|
|
1058
|
-
delayedAst
|
|
1059
|
-
}) => {
|
|
1060
|
-
const tablePrimaryKey = codeTable.internal.tableData.primaryKey;
|
|
1061
|
-
if (dbPrimaryKey && tablePrimaryKey && dbPrimaryKey?.name !== tablePrimaryKey?.name) {
|
|
1062
|
-
delayedAst.push({
|
|
1063
|
-
type: "renameTableItem",
|
|
1064
|
-
kind: "CONSTRAINT",
|
|
1065
|
-
tableSchema: schema,
|
|
1066
|
-
tableName: codeTable.table,
|
|
1067
|
-
from: dbPrimaryKey.name ?? `${codeTable.table}_pkey`,
|
|
1068
|
-
to: tablePrimaryKey.name ?? `${codeTable}_pkey`
|
|
1069
|
-
});
|
|
1070
|
-
}
|
|
1071
|
-
};
|
|
1072
|
-
|
|
1073
|
-
const processIndexesAndExcludes = (config, changeTableData, ast, compareExpressions) => {
|
|
1074
|
-
const codeItems = collectCodeIndexes(config, changeTableData);
|
|
1075
|
-
const codeComparableItems = collectCodeComparableItems(config, codeItems);
|
|
1076
|
-
const skipCodeItems = {
|
|
1077
|
-
indexes: /* @__PURE__ */ new Map(),
|
|
1078
|
-
excludes: /* @__PURE__ */ new Map()
|
|
1079
|
-
};
|
|
1080
|
-
const holdCodeItems = {
|
|
1081
|
-
indexes: /* @__PURE__ */ new Map(),
|
|
1082
|
-
excludes: /* @__PURE__ */ new Map()
|
|
1083
|
-
};
|
|
1084
|
-
const processParams = {
|
|
1085
|
-
config,
|
|
1086
|
-
changeTableData,
|
|
1087
|
-
codeComparableItems,
|
|
1088
|
-
codeItems,
|
|
1089
|
-
skipCodeItems,
|
|
1090
|
-
holdCodeItems,
|
|
1091
|
-
// counter for async SQL comparisons that are in progress
|
|
1092
|
-
wait: { indexes: 0, excludes: 0 },
|
|
1093
|
-
ast,
|
|
1094
|
-
compareExpressions
|
|
1095
|
-
};
|
|
1096
|
-
processItems(processParams, "indexes");
|
|
1097
|
-
processItems(processParams, "excludes");
|
|
1098
|
-
addMainItems(
|
|
1099
|
-
changeTableData,
|
|
1100
|
-
codeItems,
|
|
1101
|
-
skipCodeItems,
|
|
1102
|
-
holdCodeItems,
|
|
1103
|
-
"indexes"
|
|
1104
|
-
);
|
|
1105
|
-
addMainItems(
|
|
1106
|
-
changeTableData,
|
|
1107
|
-
codeItems,
|
|
1108
|
-
skipCodeItems,
|
|
1109
|
-
holdCodeItems,
|
|
1110
|
-
"excludes"
|
|
1111
|
-
);
|
|
1112
|
-
};
|
|
1113
|
-
const processItems = ({
|
|
1114
|
-
config,
|
|
1115
|
-
changeTableData,
|
|
1116
|
-
codeComparableItems,
|
|
1117
|
-
codeItems,
|
|
1118
|
-
skipCodeItems,
|
|
1119
|
-
holdCodeItems,
|
|
1120
|
-
wait,
|
|
1121
|
-
ast,
|
|
1122
|
-
compareExpressions
|
|
1123
|
-
}, key) => {
|
|
1124
|
-
const {
|
|
1125
|
-
changeTableAst: { shape }
|
|
1126
|
-
} = changeTableData;
|
|
1127
|
-
const dbItems = changeTableData.dbTableData[key];
|
|
1128
|
-
for (const dbItem of dbItems) {
|
|
1129
|
-
const hasAddedOrDroppedColumn = dbItem.columns.some(
|
|
1130
|
-
(column) => "column" in column && checkForColumnAddOrDrop(shape, column.column)
|
|
1131
|
-
);
|
|
1132
|
-
if (hasAddedOrDroppedColumn) continue;
|
|
1133
|
-
normalizeItem(dbItem);
|
|
1134
|
-
const { found, rename, foundAndHasSql } = findMatchingItem(
|
|
1135
|
-
dbItem,
|
|
1136
|
-
codeComparableItems,
|
|
1137
|
-
codeItems,
|
|
1138
|
-
skipCodeItems,
|
|
1139
|
-
changeTableData.codeTable.table,
|
|
1140
|
-
config,
|
|
1141
|
-
key
|
|
1142
|
-
);
|
|
1143
|
-
const { columns: dbColumns } = dbItem;
|
|
1144
|
-
if (!foundAndHasSql) {
|
|
1145
|
-
handleItemChange(
|
|
1146
|
-
changeTableData,
|
|
1147
|
-
dbItem,
|
|
1148
|
-
dbColumns,
|
|
1149
|
-
found[0],
|
|
1150
|
-
rename[0],
|
|
1151
|
-
key
|
|
1152
|
-
);
|
|
1153
|
-
continue;
|
|
1154
|
-
}
|
|
1155
|
-
for (const codeItem of found) {
|
|
1156
|
-
holdCodeItems[key].set(codeItem, true);
|
|
1157
|
-
}
|
|
1158
|
-
const compare = [];
|
|
1159
|
-
for (let i = 0; i < dbItem.columns.length; i++) {
|
|
1160
|
-
const column = dbItem.columns[i];
|
|
1161
|
-
if (!("expression" in column)) continue;
|
|
1162
|
-
compare.push({
|
|
1163
|
-
inDb: column.expression,
|
|
1164
|
-
inCode: found.map(
|
|
1165
|
-
(x) => x.columns[i].expression
|
|
1166
|
-
)
|
|
1167
|
-
});
|
|
1168
|
-
}
|
|
1169
|
-
if (dbItem.with) {
|
|
1170
|
-
compare.push({
|
|
1171
|
-
inDb: dbItem.with,
|
|
1172
|
-
inCode: found.map((x) => x.options.with)
|
|
1173
|
-
});
|
|
1174
|
-
}
|
|
1175
|
-
if (dbItem.where) {
|
|
1176
|
-
compare.push({
|
|
1177
|
-
inDb: dbItem.where,
|
|
1178
|
-
inCode: found.map((x) => x.options.where)
|
|
1179
|
-
});
|
|
1180
|
-
}
|
|
1181
|
-
wait[key]++;
|
|
1182
|
-
compareExpressions.push({
|
|
1183
|
-
compare,
|
|
1184
|
-
handle(i) {
|
|
1185
|
-
const codeItem = i === void 0 ? void 0 : found[i];
|
|
1186
|
-
handleItemChange(
|
|
1187
|
-
changeTableData,
|
|
1188
|
-
dbItem,
|
|
1189
|
-
dbColumns,
|
|
1190
|
-
codeItem,
|
|
1191
|
-
i === void 0 ? void 0 : rename[i],
|
|
1192
|
-
key
|
|
1193
|
-
);
|
|
1194
|
-
if (codeItem) {
|
|
1195
|
-
holdCodeItems[key].delete(codeItem);
|
|
1196
|
-
}
|
|
1197
|
-
if (!--wait[key] && holdCodeItems[key].size) {
|
|
1198
|
-
addItems(changeTableData, [...holdCodeItems[key].keys()], key);
|
|
1199
|
-
if (!changeTableData.pushedAst) {
|
|
1200
|
-
changeTableData.pushedAst = true;
|
|
1201
|
-
ast.push(changeTableData.changeTableAst);
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
1205
|
-
});
|
|
1206
|
-
}
|
|
1207
|
-
};
|
|
1208
|
-
const collectCodeIndexes = (config, { codeTable, changeTableAst: { shape } }) => {
|
|
1209
|
-
const codeItems = { indexes: [], excludes: [] };
|
|
1210
|
-
for (const key in codeTable.shape) {
|
|
1211
|
-
const column = codeTable.shape[key];
|
|
1212
|
-
if (!column.data.indexes && !column.data.excludes) continue;
|
|
1213
|
-
const name = column.data.name ?? key;
|
|
1214
|
-
if (checkForColumnAddOrDrop(shape, name)) continue;
|
|
1215
|
-
pushCodeColumnItems(config, codeItems, key, name, column, "indexes");
|
|
1216
|
-
pushCodeColumnItems(config, codeItems, key, name, column, "excludes");
|
|
1217
|
-
}
|
|
1218
|
-
pushCodeCompositeItems(config, codeTable, codeItems, "indexes");
|
|
1219
|
-
pushCodeCompositeItems(config, codeTable, codeItems, "excludes");
|
|
1220
|
-
return codeItems;
|
|
1221
|
-
};
|
|
1222
|
-
const pushCodeColumnItems = (config, codeItems, columnKey, name, column, key) => {
|
|
1223
|
-
const items = column.data[key];
|
|
1224
|
-
if (!items) return;
|
|
1225
|
-
codeItems[key].push(
|
|
1226
|
-
...items.map(
|
|
1227
|
-
({
|
|
1228
|
-
options: { collate, opclass, order, weight, ...options },
|
|
1229
|
-
with: wi,
|
|
1230
|
-
...index
|
|
1231
|
-
}) => {
|
|
1232
|
-
const w = key === "excludes" ? wi : void 0;
|
|
1233
|
-
return {
|
|
1234
|
-
columns: [
|
|
1235
|
-
{
|
|
1236
|
-
collate,
|
|
1237
|
-
opclass,
|
|
1238
|
-
order,
|
|
1239
|
-
weight,
|
|
1240
|
-
column: name,
|
|
1241
|
-
with: w
|
|
1242
|
-
}
|
|
1243
|
-
],
|
|
1244
|
-
...index,
|
|
1245
|
-
options: options.include ? config.snakeCase ? {
|
|
1246
|
-
...options,
|
|
1247
|
-
include: internal.toArray(options.include).map(internal.toSnakeCase)
|
|
1248
|
-
} : options : options,
|
|
1249
|
-
columnKeys: [
|
|
1250
|
-
{
|
|
1251
|
-
collate,
|
|
1252
|
-
opclass,
|
|
1253
|
-
order,
|
|
1254
|
-
weight,
|
|
1255
|
-
column: columnKey,
|
|
1256
|
-
with: w
|
|
1257
|
-
}
|
|
1258
|
-
],
|
|
1259
|
-
includeKeys: options.include
|
|
1260
|
-
};
|
|
1261
|
-
}
|
|
1262
|
-
)
|
|
1263
|
-
);
|
|
1264
|
-
};
|
|
1265
|
-
const pushCodeCompositeItems = (config, codeTable, codeItems, key) => {
|
|
1266
|
-
const items = codeTable.internal.tableData[key];
|
|
1267
|
-
if (!items) return;
|
|
1268
|
-
codeItems[key].push(
|
|
1269
|
-
...items.map((x) => ({
|
|
1270
|
-
...x,
|
|
1271
|
-
columns: config.snakeCase ? x.columns.map(
|
|
1272
|
-
(c) => "column" in c ? { ...c, column: internal.toSnakeCase(c.column) } : c
|
|
1273
|
-
) : x.columns,
|
|
1274
|
-
columnKeys: x.columns,
|
|
1275
|
-
options: x.options.include && config.snakeCase ? {
|
|
1276
|
-
...x.options,
|
|
1277
|
-
include: internal.toArray(x.options.include).map(internal.toSnakeCase)
|
|
1278
|
-
} : x.options,
|
|
1279
|
-
includeKeys: x.options.include
|
|
1280
|
-
}))
|
|
1281
|
-
);
|
|
1282
|
-
};
|
|
1283
|
-
const collectCodeComparableItems = (config, codeItems) => {
|
|
1284
|
-
return {
|
|
1285
|
-
indexes: collectCodeComparableItemsType(config, codeItems, "indexes"),
|
|
1286
|
-
excludes: collectCodeComparableItemsType(config, codeItems, "excludes")
|
|
1287
|
-
};
|
|
1288
|
-
};
|
|
1289
|
-
const collectCodeComparableItemsType = (config, codeItems, key) => {
|
|
1290
|
-
return codeItems[key].map((codeItem) => {
|
|
1291
|
-
normalizeItem(codeItem.options);
|
|
1292
|
-
return itemToComparable({
|
|
1293
|
-
...codeItem.options,
|
|
1294
|
-
include: codeItem.options.include === void 0 ? void 0 : config.snakeCase ? internal.toArray(codeItem.options.include).map(internal.toSnakeCase) : internal.toArray(codeItem.options.include),
|
|
1295
|
-
columns: codeItem.columns,
|
|
1296
|
-
name: codeItem.options.name,
|
|
1297
|
-
columnKeys: codeItem.columnKeys,
|
|
1298
|
-
includeKeys: codeItem.includeKeys
|
|
1299
|
-
});
|
|
1300
|
-
});
|
|
1301
|
-
};
|
|
1302
|
-
const normalizeItem = (item) => {
|
|
1303
|
-
if (item.using) item.using = item.using.toLowerCase();
|
|
1304
|
-
if (item.using === "btree") item.using = void 0;
|
|
1305
|
-
if (!item.unique) item.unique = void 0;
|
|
1306
|
-
if (item.nullsNotDistinct === false) item.nullsNotDistinct = void 0;
|
|
1307
|
-
if (item.exclude) {
|
|
1308
|
-
for (let i = 0; i < item.columns.length; i++) {
|
|
1309
|
-
item.columns[i].with = item.exclude[i];
|
|
1310
|
-
}
|
|
1311
|
-
}
|
|
1312
|
-
};
|
|
1313
|
-
const itemToComparable = (index) => {
|
|
1314
|
-
let hasExpression = false;
|
|
1315
|
-
const columns = index.columns.map((column) => {
|
|
1316
|
-
const result = {
|
|
1317
|
-
...column,
|
|
1318
|
-
expression: void 0,
|
|
1319
|
-
hasExpression: "expression" in column
|
|
1320
|
-
};
|
|
1321
|
-
if (result.hasExpression) hasExpression = true;
|
|
1322
|
-
return result;
|
|
1323
|
-
});
|
|
1324
|
-
return {
|
|
1325
|
-
...index,
|
|
1326
|
-
schemaName: void 0,
|
|
1327
|
-
tableName: void 0,
|
|
1328
|
-
with: void 0,
|
|
1329
|
-
hasWith: !!index.with,
|
|
1330
|
-
where: void 0,
|
|
1331
|
-
hasWhere: !!index.where,
|
|
1332
|
-
columns,
|
|
1333
|
-
hasExpression
|
|
1334
|
-
};
|
|
1335
|
-
};
|
|
1336
|
-
const findMatchingItem = (dbItem, codeComparableItems, codeItems, skipCodeItems, tableName, config, key) => {
|
|
1337
|
-
const dbComparableItem = itemToComparable(
|
|
1338
|
-
key === "indexes" ? dbItem : {
|
|
1339
|
-
...dbItem,
|
|
1340
|
-
exclude: void 0,
|
|
1341
|
-
columns: dbItem.columns.map((column, i) => ({
|
|
1342
|
-
...column,
|
|
1343
|
-
with: dbItem.exclude[i]
|
|
1344
|
-
}))
|
|
1345
|
-
}
|
|
1346
|
-
);
|
|
1347
|
-
const { found, rename } = findMatchingItemWithoutSql(
|
|
1348
|
-
dbComparableItem,
|
|
1349
|
-
codeComparableItems,
|
|
1350
|
-
codeItems,
|
|
1351
|
-
skipCodeItems,
|
|
1352
|
-
tableName,
|
|
1353
|
-
config,
|
|
1354
|
-
key
|
|
1355
|
-
);
|
|
1356
|
-
const foundAndHasSql = found.length && checkIfItemHasSql(dbComparableItem);
|
|
1357
|
-
return { found, rename, foundAndHasSql };
|
|
1358
|
-
};
|
|
1359
|
-
const findMatchingItemWithoutSql = (dbItem, codeComparableItems, codeItems, skipCodeItems, tableName, config, key) => {
|
|
1360
|
-
const found = [];
|
|
1361
|
-
const rename = [];
|
|
1362
|
-
const { columns: dbColumns, ...dbItemWithoutColumns } = dbItem;
|
|
1363
|
-
for (let i = 0; i < codeComparableItems[key].length; i++) {
|
|
1364
|
-
if (skipCodeItems[key].has(i)) continue;
|
|
1365
|
-
const { columns: codeColumns, ...codeItem } = codeComparableItems[key][i];
|
|
1366
|
-
if (dbColumns.length === codeColumns.length && !dbColumns.some((dbColumn, i2) => !internal.deepCompare(dbColumn, codeColumns[i2]))) {
|
|
1367
|
-
let a = dbItemWithoutColumns;
|
|
1368
|
-
let b = codeItem;
|
|
1369
|
-
const codeName = b.name ?? (key === "indexes" ? rakeDb.getIndexName : rakeDb.getExcludeName)(
|
|
1370
|
-
tableName,
|
|
1371
|
-
dbColumns
|
|
1372
|
-
);
|
|
1373
|
-
if (a.name !== b.name) {
|
|
1374
|
-
a = { ...a, name: void 0 };
|
|
1375
|
-
b = {
|
|
1376
|
-
...b,
|
|
1377
|
-
name: void 0,
|
|
1378
|
-
columnKeys: void 0,
|
|
1379
|
-
includeKeys: void 0
|
|
1380
|
-
};
|
|
1381
|
-
if (a.language && !b.language) {
|
|
1382
|
-
b.language = config.language ?? "english";
|
|
1383
|
-
}
|
|
1384
|
-
if (internal.deepCompare(a, b)) {
|
|
1385
|
-
found.push(codeItems[key][i]);
|
|
1386
|
-
rename.push(
|
|
1387
|
-
dbItemWithoutColumns.name !== codeName ? codeName : void 0
|
|
1388
|
-
);
|
|
1389
|
-
}
|
|
1390
|
-
} else {
|
|
1391
|
-
const {
|
|
1392
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1393
|
-
columnKeys,
|
|
1394
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1395
|
-
includeKeys,
|
|
1396
|
-
...codeItemWithoutKeys
|
|
1397
|
-
} = codeItem;
|
|
1398
|
-
if (internal.deepCompare(dbItemWithoutColumns, codeItemWithoutKeys)) {
|
|
1399
|
-
found.push(codeItems[key][i]);
|
|
1400
|
-
rename.push(void 0);
|
|
1401
|
-
}
|
|
1402
|
-
}
|
|
1403
|
-
if (found.length && !checkIfItemHasSql(codeItem)) {
|
|
1404
|
-
skipCodeItems[key].set(i, true);
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1408
|
-
return { found, rename };
|
|
1409
|
-
};
|
|
1410
|
-
const checkIfItemHasSql = (x) => x.hasWith || x.hasWhere || x.hasExpression;
|
|
1411
|
-
const handleItemChange = ({
|
|
1412
|
-
changeTableAst,
|
|
1413
|
-
schema,
|
|
1414
|
-
codeTable,
|
|
1415
|
-
changingColumns,
|
|
1416
|
-
delayedAst
|
|
1417
|
-
}, dbItem, dbColumns, found, rename, key) => {
|
|
1418
|
-
var _a, _b;
|
|
1419
|
-
if (!found) {
|
|
1420
|
-
const name = dbItem.name === (key === "indexes" ? rakeDb.getIndexName : rakeDb.getExcludeName)(
|
|
1421
|
-
changeTableAst.name,
|
|
1422
|
-
dbColumns
|
|
1423
|
-
) ? void 0 : dbItem.name;
|
|
1424
|
-
if (dbColumns.length === 1 && "column" in dbColumns[0]) {
|
|
1425
|
-
const dbColumn = dbColumns[0];
|
|
1426
|
-
const column = changingColumns[dbColumn.column];
|
|
1427
|
-
if (column) {
|
|
1428
|
-
((_a = column.from.data)[key] ?? (_a[key] = [])).push({
|
|
1429
|
-
options: { ...dbItem, name },
|
|
1430
|
-
with: key === "indexes" ? void 0 : dbColumn.with
|
|
1431
|
-
});
|
|
1432
|
-
return;
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
|
-
((_b = changeTableAst.drop)[key] ?? (_b[key] = [])).push({
|
|
1436
|
-
columns: dbColumns,
|
|
1437
|
-
options: { ...dbItem, name }
|
|
1438
|
-
});
|
|
1439
|
-
} else if (rename) {
|
|
1440
|
-
delayedAst.push({
|
|
1441
|
-
type: "renameTableItem",
|
|
1442
|
-
kind: key === "indexes" ? "INDEX" : "CONSTRAINT",
|
|
1443
|
-
tableSchema: schema,
|
|
1444
|
-
tableName: codeTable.table,
|
|
1445
|
-
from: dbItem.name,
|
|
1446
|
-
to: rename
|
|
1447
|
-
});
|
|
1448
|
-
}
|
|
1449
|
-
};
|
|
1450
|
-
const addMainItems = (changeTableData, codeItems, skipCodeItems, holdCodeItems, key) => {
|
|
1451
|
-
const itemsToAdd = codeItems[key].filter(
|
|
1452
|
-
(item, i) => !skipCodeItems[key].has(i) && !holdCodeItems[key].has(item)
|
|
1453
|
-
);
|
|
1454
|
-
if (itemsToAdd.length) {
|
|
1455
|
-
addItems(
|
|
1456
|
-
changeTableData,
|
|
1457
|
-
itemsToAdd.map((x) => ({
|
|
1458
|
-
...x,
|
|
1459
|
-
columns: x.columnKeys,
|
|
1460
|
-
columnNames: x.columns,
|
|
1461
|
-
options: x.options.include ? { ...x.options, include: x.includeKeys } : x.options
|
|
1462
|
-
})),
|
|
1463
|
-
key
|
|
1464
|
-
);
|
|
1465
|
-
}
|
|
1466
|
-
};
|
|
1467
|
-
const addItems = ({ changeTableAst, changingColumns }, add, key) => {
|
|
1468
|
-
var _a, _b;
|
|
1469
|
-
const items = (_a = changeTableAst.add)[key] ?? (_a[key] = []);
|
|
1470
|
-
for (const item of add) {
|
|
1471
|
-
if (item.columns.length === 1 && "column" in item.columns[0]) {
|
|
1472
|
-
const column = changingColumns[(item.columnNames || item.columns)[0].column];
|
|
1473
|
-
if (column) {
|
|
1474
|
-
((_b = column.to.data)[key] ?? (_b[key] = [])).push(
|
|
1475
|
-
key === "indexes" ? item : {
|
|
1476
|
-
...item,
|
|
1477
|
-
with: item.columns[0].with
|
|
1478
|
-
}
|
|
1479
|
-
);
|
|
1480
|
-
continue;
|
|
1481
|
-
}
|
|
1482
|
-
}
|
|
1483
|
-
items.push(item);
|
|
1484
|
-
}
|
|
1485
|
-
};
|
|
1486
|
-
|
|
1487
|
-
const mapMatchToDb = {
|
|
1488
|
-
FULL: "f",
|
|
1489
|
-
PARTIAL: "p",
|
|
1490
|
-
SIMPLE: "s"
|
|
1491
|
-
};
|
|
1492
|
-
const mapMatchToCode = {};
|
|
1493
|
-
for (const key in mapMatchToDb) {
|
|
1494
|
-
mapMatchToCode[mapMatchToDb[key]] = key;
|
|
1495
|
-
}
|
|
1496
|
-
const mapActionToDb = {
|
|
1497
|
-
"NO ACTION": "a",
|
|
1498
|
-
RESTRICT: "r",
|
|
1499
|
-
CASCADE: "c",
|
|
1500
|
-
"SET NULL": "n",
|
|
1501
|
-
"SET DEFAULT": "d"
|
|
1502
|
-
};
|
|
1503
|
-
const mapActionToCode = {};
|
|
1504
|
-
for (const key in mapActionToDb) {
|
|
1505
|
-
mapActionToCode[mapActionToDb[key]] = key;
|
|
1506
|
-
}
|
|
1507
|
-
const processForeignKeys = (config, ast, changeTables, currentSchema, tableShapes) => {
|
|
1508
|
-
var _a, _b;
|
|
1509
|
-
for (const changeTableData of changeTables) {
|
|
1510
|
-
const codeForeignKeys = collectCodeFkeys(
|
|
1511
|
-
config,
|
|
1512
|
-
changeTableData,
|
|
1513
|
-
currentSchema
|
|
1514
|
-
);
|
|
1515
|
-
const { codeTable, dbTableData, changeTableAst, schema, changingColumns } = changeTableData;
|
|
1516
|
-
const { shape, add, drop } = changeTableAst;
|
|
1517
|
-
let changed = false;
|
|
1518
|
-
for (const dbConstraint of dbTableData.constraints) {
|
|
1519
|
-
const { references: dbReferences } = dbConstraint;
|
|
1520
|
-
if (!dbReferences) continue;
|
|
1521
|
-
const hasChangedColumn = dbReferences.columns.some(
|
|
1522
|
-
(column) => checkForColumnAddOrDrop(shape, column)
|
|
1523
|
-
);
|
|
1524
|
-
if (hasChangedColumn) continue;
|
|
1525
|
-
const foreignShape = tableShapes[`${dbReferences.foreignSchema}.${dbReferences.foreignTable}`];
|
|
1526
|
-
const hasForeignChangedColumn = foreignShape && dbReferences.foreignColumns.some((column) => {
|
|
1527
|
-
return checkForColumnAddOrDrop(foreignShape, column);
|
|
1528
|
-
});
|
|
1529
|
-
if (hasForeignChangedColumn) continue;
|
|
1530
|
-
let found = false;
|
|
1531
|
-
let rename;
|
|
1532
|
-
for (let i = 0; i < codeForeignKeys.length; i++) {
|
|
1533
|
-
const codeForeignKey = codeForeignKeys[i];
|
|
1534
|
-
const codeReferences = codeForeignKey.references;
|
|
1535
|
-
if (internal.deepCompare(dbReferences, codeReferences)) {
|
|
1536
|
-
found = true;
|
|
1537
|
-
codeForeignKeys.splice(i, 1);
|
|
1538
|
-
const codeName = codeForeignKey.codeConstraint.name ?? rakeDb.getConstraintName(
|
|
1539
|
-
codeTable.table,
|
|
1540
|
-
codeForeignKey,
|
|
1541
|
-
config.snakeCase
|
|
1542
|
-
);
|
|
1543
|
-
if (codeName !== dbConstraint.name) {
|
|
1544
|
-
rename = codeName;
|
|
1545
|
-
}
|
|
1546
|
-
}
|
|
1547
|
-
}
|
|
1548
|
-
if (!found) {
|
|
1549
|
-
const foreignKey = dbForeignKeyToCodeForeignKey(
|
|
1550
|
-
config,
|
|
1551
|
-
dbConstraint,
|
|
1552
|
-
dbReferences
|
|
1553
|
-
);
|
|
1554
|
-
if (dbReferences.columns.length === 1 && changingColumns[dbReferences.columns[0]]) {
|
|
1555
|
-
const column = changingColumns[dbReferences.columns[0]];
|
|
1556
|
-
((_a = column.from.data).foreignKeys ?? (_a.foreignKeys = [])).push({
|
|
1557
|
-
fnOrTable: foreignKey.references.fnOrTable,
|
|
1558
|
-
foreignColumns: foreignKey.references.foreignColumns,
|
|
1559
|
-
options: foreignKey.references.options
|
|
1560
|
-
});
|
|
1561
|
-
} else {
|
|
1562
|
-
(drop.constraints ?? (drop.constraints = [])).push(foreignKey);
|
|
1563
|
-
}
|
|
1564
|
-
changed = true;
|
|
1565
|
-
} else if (rename) {
|
|
1566
|
-
ast.push({
|
|
1567
|
-
type: "renameTableItem",
|
|
1568
|
-
kind: "CONSTRAINT",
|
|
1569
|
-
tableSchema: schema,
|
|
1570
|
-
tableName: codeTable.table,
|
|
1571
|
-
from: dbConstraint.name,
|
|
1572
|
-
to: rename
|
|
1573
|
-
});
|
|
1574
|
-
}
|
|
1575
|
-
}
|
|
1576
|
-
if (codeForeignKeys.length) {
|
|
1577
|
-
const constraints = add.constraints ?? (add.constraints = []);
|
|
1578
|
-
for (const { codeConstraint, references } of codeForeignKeys) {
|
|
1579
|
-
if (references.columns.length === 1 && changingColumns[references.columns[0]]) {
|
|
1580
|
-
const column = changingColumns[references.columns[0]];
|
|
1581
|
-
((_b = column.to.data).foreignKeys ?? (_b.foreignKeys = [])).push({
|
|
1582
|
-
fnOrTable: references.foreignTable,
|
|
1583
|
-
foreignColumns: codeConstraint.references.foreignColumns,
|
|
1584
|
-
options: codeConstraint.references.options
|
|
1585
|
-
});
|
|
1586
|
-
} else {
|
|
1587
|
-
constraints.push(codeConstraint);
|
|
1588
|
-
}
|
|
1589
|
-
}
|
|
1590
|
-
changed = true;
|
|
1591
|
-
}
|
|
1592
|
-
if (changed && !changeTableData.pushedAst) {
|
|
1593
|
-
changeTableData.pushedAst = true;
|
|
1594
|
-
ast.push(changeTableData.changeTableAst);
|
|
1595
|
-
}
|
|
1596
|
-
}
|
|
1597
|
-
};
|
|
1598
|
-
const collectCodeFkeys = (config, { codeTable, changeTableAst: { shape } }, currentSchema) => {
|
|
1599
|
-
const codeForeignKeys = [];
|
|
1600
|
-
for (const key in codeTable.shape) {
|
|
1601
|
-
const column = codeTable.shape[key];
|
|
1602
|
-
if (!column.data.foreignKeys) continue;
|
|
1603
|
-
const name = column.data.name ?? key;
|
|
1604
|
-
if (checkForColumnAddOrDrop(shape, name)) continue;
|
|
1605
|
-
codeForeignKeys.push(
|
|
1606
|
-
...column.data.foreignKeys.map((x) => {
|
|
1607
|
-
const columns = [name];
|
|
1608
|
-
const fnOrTable = fnOrTableToString(x.fnOrTable);
|
|
1609
|
-
return parseForeignKey(
|
|
1610
|
-
config,
|
|
1611
|
-
{
|
|
1612
|
-
name: x.options?.name,
|
|
1613
|
-
references: {
|
|
1614
|
-
columns: [name],
|
|
1615
|
-
fnOrTable,
|
|
1616
|
-
foreignColumns: x.foreignColumns,
|
|
1617
|
-
options: x.options
|
|
1618
|
-
}
|
|
1619
|
-
},
|
|
1620
|
-
{
|
|
1621
|
-
columns,
|
|
1622
|
-
fnOrTable,
|
|
1623
|
-
foreignColumns: x.foreignColumns,
|
|
1624
|
-
options: x.options
|
|
1625
|
-
},
|
|
1626
|
-
currentSchema
|
|
1627
|
-
);
|
|
1628
|
-
})
|
|
1629
|
-
);
|
|
1630
|
-
}
|
|
1631
|
-
if (codeTable.internal.tableData.constraints) {
|
|
1632
|
-
for (const tableConstraint of codeTable.internal.tableData.constraints) {
|
|
1633
|
-
const { references: refs } = tableConstraint;
|
|
1634
|
-
if (!refs) continue;
|
|
1635
|
-
const fnOrTable = fnOrTableToString(refs.fnOrTable);
|
|
1636
|
-
codeForeignKeys.push(
|
|
1637
|
-
parseForeignKey(
|
|
1638
|
-
config,
|
|
1639
|
-
{
|
|
1640
|
-
...tableConstraint,
|
|
1641
|
-
references: {
|
|
1642
|
-
...refs,
|
|
1643
|
-
fnOrTable
|
|
1644
|
-
}
|
|
1645
|
-
},
|
|
1646
|
-
{
|
|
1647
|
-
...refs,
|
|
1648
|
-
fnOrTable,
|
|
1649
|
-
columns: config.snakeCase ? refs.columns.map(internal.toSnakeCase) : refs.columns,
|
|
1650
|
-
foreignColumns: config.snakeCase ? refs.foreignColumns.map(internal.toSnakeCase) : refs.foreignColumns
|
|
1651
|
-
},
|
|
1652
|
-
currentSchema
|
|
1653
|
-
)
|
|
1654
|
-
);
|
|
1655
|
-
}
|
|
1656
|
-
}
|
|
1657
|
-
return codeForeignKeys;
|
|
1658
|
-
};
|
|
1659
|
-
const fnOrTableToString = (fnOrTable) => {
|
|
1660
|
-
if (typeof fnOrTable !== "string") {
|
|
1661
|
-
const { schema, table } = new (fnOrTable())();
|
|
1662
|
-
fnOrTable = rakeDb.concatSchemaAndName({ schema, name: table });
|
|
1663
|
-
}
|
|
1664
|
-
return fnOrTable;
|
|
1665
|
-
};
|
|
1666
|
-
const parseForeignKey = (config, codeConstraint, references, currentSchema) => {
|
|
1667
|
-
const { fnOrTable, columns, foreignColumns, options } = references;
|
|
1668
|
-
const [schema, table] = rakeDb.getSchemaAndTableFromName(currentSchema, fnOrTable);
|
|
1669
|
-
return {
|
|
1670
|
-
references: {
|
|
1671
|
-
foreignSchema: schema ?? currentSchema,
|
|
1672
|
-
foreignTable: table,
|
|
1673
|
-
columns,
|
|
1674
|
-
foreignColumns: config.snakeCase ? foreignColumns.map(internal.toSnakeCase) : foreignColumns,
|
|
1675
|
-
match: mapMatchToDb[options?.match || "SIMPLE"],
|
|
1676
|
-
onUpdate: mapActionToDb[options?.onUpdate || "NO ACTION"],
|
|
1677
|
-
onDelete: mapActionToDb[options?.onDelete || "NO ACTION"]
|
|
1678
|
-
},
|
|
1679
|
-
codeConstraint
|
|
1680
|
-
};
|
|
1681
|
-
};
|
|
1682
|
-
const dbForeignKeyToCodeForeignKey = (config, dbConstraint, dbReferences) => ({
|
|
1683
|
-
name: dbConstraint.name === rakeDb.getConstraintName(
|
|
1684
|
-
dbConstraint.tableName,
|
|
1685
|
-
{ references: dbReferences },
|
|
1686
|
-
config.snakeCase
|
|
1687
|
-
) ? void 0 : dbConstraint.name,
|
|
1688
|
-
references: {
|
|
1689
|
-
columns: dbReferences.columns,
|
|
1690
|
-
fnOrTable: `${dbReferences.foreignSchema}.${dbReferences.foreignTable}`,
|
|
1691
|
-
foreignColumns: dbReferences.foreignColumns,
|
|
1692
|
-
options: {
|
|
1693
|
-
match: dbReferences.match === "s" ? void 0 : mapMatchToCode[dbReferences.match],
|
|
1694
|
-
onUpdate: dbReferences.onUpdate === "a" ? void 0 : mapActionToCode[dbReferences.onUpdate],
|
|
1695
|
-
onDelete: dbReferences.onDelete === "a" ? void 0 : mapActionToCode[dbReferences.onDelete]
|
|
1696
|
-
}
|
|
1697
|
-
}
|
|
1698
|
-
});
|
|
1699
|
-
|
|
1700
|
-
const processChecks = (ast, changeTableData, compareExpressions) => {
|
|
1701
|
-
const codeChecks = collectCodeChecks(changeTableData);
|
|
1702
|
-
const {
|
|
1703
|
-
dbTableData,
|
|
1704
|
-
changeTableAst: { add, shape }
|
|
1705
|
-
} = changeTableData;
|
|
1706
|
-
const hasDbChecks = dbTableData.constraints.some((c) => c.check);
|
|
1707
|
-
if (!hasDbChecks) {
|
|
1708
|
-
if (codeChecks.length) {
|
|
1709
|
-
const constraints = add.constraints ?? (add.constraints = []);
|
|
1710
|
-
for (const codeCheck of codeChecks) {
|
|
1711
|
-
if (!codeCheck.column || !changeTableData.changingColumns[codeCheck.column]) {
|
|
1712
|
-
constraints.push({
|
|
1713
|
-
check: codeCheck.check.sql,
|
|
1714
|
-
name: codeCheck.name
|
|
1715
|
-
});
|
|
1716
|
-
}
|
|
1717
|
-
}
|
|
1718
|
-
}
|
|
1719
|
-
return;
|
|
1720
|
-
}
|
|
1721
|
-
let wait = 0;
|
|
1722
|
-
const foundCodeChecks = /* @__PURE__ */ new Set();
|
|
1723
|
-
for (const dbConstraint of dbTableData.constraints) {
|
|
1724
|
-
const { check: dbCheck, name } = dbConstraint;
|
|
1725
|
-
if (!dbCheck) continue;
|
|
1726
|
-
const hasChangedColumn = dbCheck.columns?.some(
|
|
1727
|
-
(column) => checkForColumnAddOrDrop(shape, column)
|
|
1728
|
-
);
|
|
1729
|
-
if (hasChangedColumn) continue;
|
|
1730
|
-
if (codeChecks.length) {
|
|
1731
|
-
wait++;
|
|
1732
|
-
compareExpressions.push({
|
|
1733
|
-
compare: [
|
|
1734
|
-
{
|
|
1735
|
-
inDb: dbCheck.expression,
|
|
1736
|
-
inCode: codeChecks.map(({ check }) => check.sql)
|
|
1737
|
-
}
|
|
1738
|
-
],
|
|
1739
|
-
handle(i) {
|
|
1740
|
-
if (i !== void 0) {
|
|
1741
|
-
foundCodeChecks.add(i);
|
|
1742
|
-
} else {
|
|
1743
|
-
dropCheck(changeTableData, dbCheck, name);
|
|
1744
|
-
}
|
|
1745
|
-
if (--wait !== 0) return;
|
|
1746
|
-
const checksToAdd = [];
|
|
1747
|
-
codeChecks.forEach((check, i2) => {
|
|
1748
|
-
if (foundCodeChecks.has(i2)) {
|
|
1749
|
-
if (!check.column) return;
|
|
1750
|
-
const change = changeTableData.changingColumns[check.column];
|
|
1751
|
-
if (!change) return;
|
|
1752
|
-
const columnChecks = change.to.data.checks;
|
|
1753
|
-
if (!columnChecks) return;
|
|
1754
|
-
const i3 = columnChecks.indexOf(check.check);
|
|
1755
|
-
if (i3 !== -1) {
|
|
1756
|
-
columnChecks.splice(i3, 1);
|
|
1757
|
-
}
|
|
1758
|
-
return;
|
|
1759
|
-
}
|
|
1760
|
-
checksToAdd.push({
|
|
1761
|
-
name: check.name,
|
|
1762
|
-
check: check.check.sql
|
|
1763
|
-
});
|
|
1764
|
-
});
|
|
1765
|
-
if (checksToAdd.length) {
|
|
1766
|
-
(add.constraints ?? (add.constraints = [])).push(...checksToAdd);
|
|
1767
|
-
}
|
|
1768
|
-
if (!changeTableData.pushedAst && (changeTableData.changeTableAst.drop.constraints?.length || add.constraints?.length)) {
|
|
1769
|
-
changeTableData.pushedAst = true;
|
|
1770
|
-
ast.push(changeTableData.changeTableAst);
|
|
1771
|
-
}
|
|
1772
|
-
}
|
|
1773
|
-
});
|
|
1774
|
-
} else {
|
|
1775
|
-
dropCheck(changeTableData, dbCheck, name);
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
};
|
|
1779
|
-
const collectCodeChecks = ({
|
|
1780
|
-
codeTable,
|
|
1781
|
-
changeTableAst: { shape }
|
|
1782
|
-
}) => {
|
|
1783
|
-
const names = /* @__PURE__ */ new Set();
|
|
1784
|
-
const codeChecks = [];
|
|
1785
|
-
for (const key in codeTable.shape) {
|
|
1786
|
-
const column = codeTable.shape[key];
|
|
1787
|
-
if (!column.data.checks) continue;
|
|
1788
|
-
const columnName = column.data.name ?? key;
|
|
1789
|
-
if (checkForColumnAddOrDrop(shape, columnName)) continue;
|
|
1790
|
-
const baseName = `${codeTable.table}_${columnName}_check`;
|
|
1791
|
-
codeChecks.push(
|
|
1792
|
-
...column.data.checks.map((check) => {
|
|
1793
|
-
const name = check.name || internal.getFreeSetAlias(names, baseName, 1);
|
|
1794
|
-
names.add(name);
|
|
1795
|
-
return {
|
|
1796
|
-
check,
|
|
1797
|
-
name,
|
|
1798
|
-
column: columnName
|
|
1799
|
-
};
|
|
1800
|
-
})
|
|
1801
|
-
);
|
|
1802
|
-
}
|
|
1803
|
-
if (codeTable.internal.tableData.constraints) {
|
|
1804
|
-
for (const constraint of codeTable.internal.tableData.constraints) {
|
|
1805
|
-
const { check } = constraint;
|
|
1806
|
-
if (check) {
|
|
1807
|
-
const baseName = `${codeTable.table}_check`;
|
|
1808
|
-
const name = constraint.name || internal.getFreeSetAlias(names, baseName, 1);
|
|
1809
|
-
names.add(name);
|
|
1810
|
-
codeChecks.push({
|
|
1811
|
-
check: { sql: check, name: constraint.name },
|
|
1812
|
-
name
|
|
1813
|
-
});
|
|
1814
|
-
}
|
|
1815
|
-
}
|
|
1816
|
-
}
|
|
1817
|
-
return codeChecks;
|
|
1818
|
-
};
|
|
1819
|
-
const dropCheck = ({ changeTableAst: { drop }, changingColumns }, dbCheck, name) => {
|
|
1820
|
-
var _a;
|
|
1821
|
-
const sql = new internal.RawSql([
|
|
1822
|
-
[dbCheck.expression]
|
|
1823
|
-
]);
|
|
1824
|
-
if (dbCheck.columns?.length === 1 && changingColumns[dbCheck.columns[0]]) {
|
|
1825
|
-
const column = changingColumns[dbCheck.columns[0]];
|
|
1826
|
-
column.from.data.name = "i_d";
|
|
1827
|
-
((_a = column.from.data).checks ?? (_a.checks = [])).push({
|
|
1828
|
-
name,
|
|
1829
|
-
sql
|
|
1830
|
-
});
|
|
1831
|
-
} else {
|
|
1832
|
-
(drop.constraints ?? (drop.constraints = [])).push({
|
|
1833
|
-
name,
|
|
1834
|
-
check: sql
|
|
1835
|
-
});
|
|
1836
|
-
}
|
|
1837
|
-
};
|
|
1838
|
-
|
|
1839
|
-
const processTables = async (ast, domainsMap, adapter, dbStructure, config, {
|
|
1840
|
-
structureToAstCtx,
|
|
1841
|
-
codeItems: { tables },
|
|
1842
|
-
currentSchema,
|
|
1843
|
-
internal: { generatorIgnore },
|
|
1844
|
-
verifying
|
|
1845
|
-
}, pendingDbTypes) => {
|
|
1846
|
-
const createTables = collectCreateTables(
|
|
1847
|
-
tables,
|
|
1848
|
-
dbStructure,
|
|
1849
|
-
currentSchema
|
|
1850
|
-
);
|
|
1851
|
-
const compareSql = { values: [], expressions: [] };
|
|
1852
|
-
const tableExpressions = [];
|
|
1853
|
-
const { changeTables, changeTableSchemas, dropTables, tableShapes } = collectChangeAndDropTables(
|
|
1854
|
-
adapter,
|
|
1855
|
-
config,
|
|
1856
|
-
tables,
|
|
1857
|
-
dbStructure,
|
|
1858
|
-
currentSchema,
|
|
1859
|
-
createTables,
|
|
1860
|
-
generatorIgnore
|
|
1861
|
-
);
|
|
1862
|
-
applyChangeTableSchemas(changeTableSchemas, currentSchema, ast);
|
|
1863
|
-
await applyCreateOrRenameTables(
|
|
1864
|
-
dbStructure,
|
|
1865
|
-
createTables,
|
|
1866
|
-
dropTables,
|
|
1867
|
-
changeTables,
|
|
1868
|
-
tableShapes,
|
|
1869
|
-
currentSchema,
|
|
1870
|
-
ast,
|
|
1871
|
-
verifying
|
|
1872
|
-
);
|
|
1873
|
-
await applyChangeTables(
|
|
1874
|
-
adapter,
|
|
1875
|
-
changeTables,
|
|
1876
|
-
structureToAstCtx,
|
|
1877
|
-
dbStructure,
|
|
1878
|
-
domainsMap,
|
|
1879
|
-
ast,
|
|
1880
|
-
currentSchema,
|
|
1881
|
-
config,
|
|
1882
|
-
compareSql,
|
|
1883
|
-
tableExpressions,
|
|
1884
|
-
verifying,
|
|
1885
|
-
pendingDbTypes
|
|
1886
|
-
);
|
|
1887
|
-
processForeignKeys(config, ast, changeTables, currentSchema, tableShapes);
|
|
1888
|
-
await Promise.all([
|
|
1889
|
-
applyCompareSql(compareSql, adapter),
|
|
1890
|
-
compareSqlExpressions(tableExpressions, adapter)
|
|
1891
|
-
]);
|
|
1892
|
-
for (const dbTable of dropTables) {
|
|
1893
|
-
ast.push(
|
|
1894
|
-
rakeDb.tableToAst(structureToAstCtx, dbStructure, dbTable, "drop", domainsMap)
|
|
1895
|
-
);
|
|
1896
|
-
}
|
|
1897
|
-
};
|
|
1898
|
-
const collectCreateTables = (tables, dbStructure, currentSchema) => {
|
|
1899
|
-
return tables.reduce((acc, codeTable) => {
|
|
1900
|
-
const tableSchema = codeTable.q.schema ?? currentSchema;
|
|
1901
|
-
const hasDbTable = dbStructure.tables.some(
|
|
1902
|
-
(t) => t.name === codeTable.table && t.schemaName === tableSchema
|
|
1903
|
-
);
|
|
1904
|
-
if (!hasDbTable) {
|
|
1905
|
-
acc.push(codeTable);
|
|
1906
|
-
}
|
|
1907
|
-
return acc;
|
|
1908
|
-
}, []);
|
|
1909
|
-
};
|
|
1910
|
-
const collectChangeAndDropTables = (adapter, config, tables, dbStructure, currentSchema, createTables, generatorIgnore) => {
|
|
1911
|
-
const changeTables = [];
|
|
1912
|
-
const changeTableSchemas = [];
|
|
1913
|
-
const dropTables = [];
|
|
1914
|
-
const tableShapes = {};
|
|
1915
|
-
const ignoreTables = generatorIgnore?.tables?.map((name) => {
|
|
1916
|
-
const [schema = currentSchema, table] = rakeDb.getSchemaAndTableFromName(
|
|
1917
|
-
currentSchema,
|
|
1918
|
-
name
|
|
1919
|
-
);
|
|
1920
|
-
return { schema, table };
|
|
1921
|
-
});
|
|
1922
|
-
const { schema: migrationsSchema = "public", table: migrationsTable } = rakeDb.getMigrationsSchemaAndTable(adapter, config);
|
|
1923
|
-
for (const dbTable of dbStructure.tables) {
|
|
1924
|
-
if (dbTable.name === migrationsTable && dbTable.schemaName === migrationsSchema || generatorIgnore?.schemas?.includes(dbTable.schemaName) || ignoreTables?.some(
|
|
1925
|
-
({ schema, table }) => table === dbTable.name && schema === dbTable.schemaName
|
|
1926
|
-
))
|
|
1927
|
-
continue;
|
|
1928
|
-
const codeTable = tables.find(
|
|
1929
|
-
(t) => t.table === dbTable.name && (t.q.schema ?? currentSchema) === dbTable.schemaName
|
|
1930
|
-
);
|
|
1931
|
-
if (codeTable) {
|
|
1932
|
-
addChangeTable(
|
|
1933
|
-
dbStructure,
|
|
1934
|
-
changeTables,
|
|
1935
|
-
tableShapes,
|
|
1936
|
-
currentSchema,
|
|
1937
|
-
dbTable,
|
|
1938
|
-
codeTable
|
|
1939
|
-
);
|
|
1940
|
-
continue;
|
|
1941
|
-
}
|
|
1942
|
-
const i = createTables.findIndex((t) => t.table === dbTable.name);
|
|
1943
|
-
if (i !== -1) {
|
|
1944
|
-
const codeTable2 = createTables[i];
|
|
1945
|
-
createTables.splice(i, 1);
|
|
1946
|
-
changeTableSchemas.push({ codeTable: codeTable2, dbTable });
|
|
1947
|
-
continue;
|
|
1948
|
-
}
|
|
1949
|
-
dropTables.push(dbTable);
|
|
1950
|
-
}
|
|
1951
|
-
return { changeTables, changeTableSchemas, dropTables, tableShapes };
|
|
1952
|
-
};
|
|
1953
|
-
const applyChangeTableSchemas = (changeTableSchemas, currentSchema, ast) => {
|
|
1954
|
-
for (const { codeTable, dbTable } of changeTableSchemas) {
|
|
1955
|
-
const fromSchema = dbTable.schemaName;
|
|
1956
|
-
const toSchema = codeTable.q.schema ?? currentSchema;
|
|
1957
|
-
ast.push({
|
|
1958
|
-
type: "renameType",
|
|
1959
|
-
kind: "TABLE",
|
|
1960
|
-
fromSchema,
|
|
1961
|
-
from: dbTable.name,
|
|
1962
|
-
toSchema,
|
|
1963
|
-
to: dbTable.name
|
|
1964
|
-
});
|
|
1965
|
-
}
|
|
1966
|
-
};
|
|
1967
|
-
const applyChangeTables = async (adapter, changeTables, structureToAstCtx, dbStructure, domainsMap, ast, currentSchema, config, compareSql, tableExpressions, verifying, pendingDbTypes) => {
|
|
1968
|
-
const compareExpressions = [];
|
|
1969
|
-
const typeCastsCache = {};
|
|
1970
|
-
for (const changeTableData of changeTables) {
|
|
1971
|
-
compareExpressions.length = 0;
|
|
1972
|
-
await processTableChange(
|
|
1973
|
-
adapter,
|
|
1974
|
-
structureToAstCtx,
|
|
1975
|
-
dbStructure,
|
|
1976
|
-
domainsMap,
|
|
1977
|
-
ast,
|
|
1978
|
-
currentSchema,
|
|
1979
|
-
config,
|
|
1980
|
-
changeTableData,
|
|
1981
|
-
compareSql,
|
|
1982
|
-
compareExpressions,
|
|
1983
|
-
typeCastsCache,
|
|
1984
|
-
verifying
|
|
1985
|
-
);
|
|
1986
|
-
if (compareExpressions.length) {
|
|
1987
|
-
const { codeTable } = changeTableData;
|
|
1988
|
-
const names = [];
|
|
1989
|
-
const types = [];
|
|
1990
|
-
for (const key in codeTable.shape) {
|
|
1991
|
-
const column = codeTable.shape[key];
|
|
1992
|
-
if (!column.dataType) continue;
|
|
1993
|
-
const name = column.data.name ?? key;
|
|
1994
|
-
const type = getColumnDbTypeForComparison(column, currentSchema);
|
|
1995
|
-
if (!pendingDbTypes.set.has(type)) {
|
|
1996
|
-
names.push(name);
|
|
1997
|
-
types.push(type);
|
|
1998
|
-
}
|
|
1999
|
-
}
|
|
2000
|
-
const tableName = codeTable.table;
|
|
2001
|
-
const source = `(VALUES (${types.map((x) => `NULL::${x}`).join(", ")})) "${tableName}"(${names.map((x) => `"${x}"`).join(", ")})`;
|
|
2002
|
-
tableExpressions.push(
|
|
2003
|
-
...compareExpressions.map((x) => ({ ...x, source }))
|
|
2004
|
-
);
|
|
2005
|
-
}
|
|
2006
|
-
}
|
|
2007
|
-
};
|
|
2008
|
-
const getColumnDbTypeForComparison = (column, currentSchema) => {
|
|
2009
|
-
if (column instanceof internal.ArrayColumn) {
|
|
2010
|
-
return getColumnDbTypeForComparison(column.data.item, currentSchema) + "[]".repeat(column.data.arrayDims);
|
|
2011
|
-
}
|
|
2012
|
-
let type = column instanceof internal.EnumColumn ? column.enumName : column.dataType;
|
|
2013
|
-
const i = type.indexOf("(");
|
|
2014
|
-
let append = "";
|
|
2015
|
-
if (i !== -1) {
|
|
2016
|
-
type = type.slice(0, i);
|
|
2017
|
-
append = type.slice(i);
|
|
2018
|
-
}
|
|
2019
|
-
const j = type.indexOf(".");
|
|
2020
|
-
if (j === -1) {
|
|
2021
|
-
let result = `"${type}"${append}`;
|
|
2022
|
-
if (column.data.isOfCustomType || column instanceof internal.EnumColumn) {
|
|
2023
|
-
result = `"${currentSchema}".${result}`;
|
|
2024
|
-
}
|
|
2025
|
-
return result;
|
|
2026
|
-
} else {
|
|
2027
|
-
return `"${type.slice(j)}"."${type.slice(0, j)}"${append}`;
|
|
2028
|
-
}
|
|
2029
|
-
};
|
|
2030
|
-
const freezeSqlClock = (sql) => sql.replaceAll("clock_timestamp()", `'0.1ms'::interval + now()`);
|
|
2031
|
-
const applyCompareSql = async (compareSql, adapter) => {
|
|
2032
|
-
if (compareSql.expressions.length) {
|
|
2033
|
-
const {
|
|
2034
|
-
rows: [results]
|
|
2035
|
-
} = await adapter.arrays(
|
|
2036
|
-
"SELECT " + compareSql.expressions.map(
|
|
2037
|
-
(x) => `${freezeSqlClock(x.inDb)} = (${x.inCode && freezeSqlClock(x.inCode)})`
|
|
2038
|
-
).join(", "),
|
|
2039
|
-
compareSql.values
|
|
2040
|
-
);
|
|
2041
|
-
for (let i = 0; i < results.length; i++) {
|
|
2042
|
-
if (!results[i]) {
|
|
2043
|
-
compareSql.expressions[i].change();
|
|
2044
|
-
}
|
|
2045
|
-
}
|
|
2046
|
-
}
|
|
2047
|
-
};
|
|
2048
|
-
const applyCreateOrRenameTables = async (dbStructure, createTables, dropTables, changeTables, tableShapes, currentSchema, ast, verifying) => {
|
|
2049
|
-
for (const codeTable of createTables) {
|
|
2050
|
-
if (dropTables.length) {
|
|
2051
|
-
const i = await promptCreateOrRename(
|
|
2052
|
-
"table",
|
|
2053
|
-
codeTable.table,
|
|
2054
|
-
dropTables.map((x) => x.name),
|
|
2055
|
-
verifying
|
|
2056
|
-
);
|
|
2057
|
-
if (i) {
|
|
2058
|
-
const dbTable = dropTables[i - 1];
|
|
2059
|
-
dropTables.splice(i - 1, 1);
|
|
2060
|
-
ast.push({
|
|
2061
|
-
type: "renameType",
|
|
2062
|
-
kind: "TABLE",
|
|
2063
|
-
fromSchema: dbTable.schemaName,
|
|
2064
|
-
from: dbTable.name,
|
|
2065
|
-
toSchema: codeTable.q.schema ?? currentSchema,
|
|
2066
|
-
to: codeTable.table
|
|
2067
|
-
});
|
|
2068
|
-
addChangeTable(
|
|
2069
|
-
dbStructure,
|
|
2070
|
-
changeTables,
|
|
2071
|
-
tableShapes,
|
|
2072
|
-
currentSchema,
|
|
2073
|
-
dbTable,
|
|
2074
|
-
codeTable
|
|
2075
|
-
);
|
|
2076
|
-
continue;
|
|
2077
|
-
}
|
|
2078
|
-
}
|
|
2079
|
-
ast.push(createTableAst(currentSchema, codeTable));
|
|
2080
|
-
}
|
|
2081
|
-
};
|
|
2082
|
-
const addChangeTable = (dbStructure, changeTables, tableShapes, currentSchema, dbTable, codeTable) => {
|
|
2083
|
-
const shape = {};
|
|
2084
|
-
const schema = codeTable.q.schema ?? currentSchema;
|
|
2085
|
-
changeTables.push({
|
|
2086
|
-
codeTable: cloneCodeTableForChange(codeTable),
|
|
2087
|
-
dbTable,
|
|
2088
|
-
dbTableData: rakeDb.getDbStructureTableData(dbStructure, dbTable),
|
|
2089
|
-
schema,
|
|
2090
|
-
changeTableAst: {
|
|
2091
|
-
type: "changeTable",
|
|
2092
|
-
schema,
|
|
2093
|
-
name: codeTable.table,
|
|
2094
|
-
shape,
|
|
2095
|
-
add: {},
|
|
2096
|
-
drop: {}
|
|
2097
|
-
},
|
|
2098
|
-
pushedAst: false,
|
|
2099
|
-
changingColumns: {},
|
|
2100
|
-
delayedAst: []
|
|
2101
|
-
});
|
|
2102
|
-
tableShapes[`${schema}.${codeTable.table}`] = shape;
|
|
2103
|
-
};
|
|
2104
|
-
const cloneCodeTableForChange = (codeTable) => ({
|
|
2105
|
-
...codeTable,
|
|
2106
|
-
// codeTable is a class instance and not all props can be cloned with `...`
|
|
2107
|
-
table: codeTable.table,
|
|
2108
|
-
shape: Object.fromEntries(
|
|
2109
|
-
Object.entries(codeTable.shape).map(([key, column]) => {
|
|
2110
|
-
const cloned = Object.create(column);
|
|
2111
|
-
cloned.data = {
|
|
2112
|
-
...cloned.data,
|
|
2113
|
-
checks: cloned.data.checks && [...cloned.data.checks]
|
|
2114
|
-
};
|
|
2115
|
-
return [key, cloned];
|
|
2116
|
-
})
|
|
2117
|
-
)
|
|
2118
|
-
});
|
|
2119
|
-
const createTableAst = (currentSchema, table) => {
|
|
2120
|
-
return {
|
|
2121
|
-
type: "table",
|
|
2122
|
-
action: "create",
|
|
2123
|
-
schema: table.q.schema === currentSchema ? void 0 : table.q.schema,
|
|
2124
|
-
comment: table.internal.comment,
|
|
2125
|
-
name: table.table,
|
|
2126
|
-
shape: makeTableShape(table),
|
|
2127
|
-
noPrimaryKey: table.internal.noPrimaryKey ? "ignore" : "error",
|
|
2128
|
-
...table.internal.tableData
|
|
2129
|
-
};
|
|
2130
|
-
};
|
|
2131
|
-
const makeTableShape = (table) => {
|
|
2132
|
-
const shape = {};
|
|
2133
|
-
for (const key in table.shape) {
|
|
2134
|
-
const column = table.shape[key];
|
|
2135
|
-
if (!(column instanceof internal.VirtualColumn)) {
|
|
2136
|
-
shape[key] = column;
|
|
2137
|
-
}
|
|
2138
|
-
}
|
|
2139
|
-
return shape;
|
|
2140
|
-
};
|
|
2141
|
-
const processTableChange = async (adapter, structureToAstCtx, dbStructure, domainsMap, ast, currentSchema, config, changeTableData, compareSql, compareExpressions, typeCastsCache, verifying) => {
|
|
2142
|
-
await processColumns(
|
|
2143
|
-
adapter,
|
|
2144
|
-
config,
|
|
2145
|
-
structureToAstCtx,
|
|
2146
|
-
dbStructure,
|
|
2147
|
-
domainsMap,
|
|
2148
|
-
changeTableData,
|
|
2149
|
-
ast,
|
|
2150
|
-
currentSchema,
|
|
2151
|
-
compareSql,
|
|
2152
|
-
typeCastsCache,
|
|
2153
|
-
verifying
|
|
2154
|
-
);
|
|
2155
|
-
processPrimaryKey(config, changeTableData);
|
|
2156
|
-
processIndexesAndExcludes(config, changeTableData, ast, compareExpressions);
|
|
2157
|
-
processChecks(ast, changeTableData, compareExpressions);
|
|
2158
|
-
const { changeTableAst } = changeTableData;
|
|
2159
|
-
if (Object.keys(changeTableAst.shape).length || Object.keys(changeTableAst.add).length || Object.keys(changeTableAst.drop).length) {
|
|
2160
|
-
changeTableData.pushedAst = true;
|
|
2161
|
-
ast.push(changeTableAst);
|
|
2162
|
-
}
|
|
2163
|
-
if (changeTableData.delayedAst.length) {
|
|
2164
|
-
ast.push(...changeTableData.delayedAst);
|
|
2165
|
-
}
|
|
2166
|
-
};
|
|
2167
|
-
|
|
2168
|
-
const defaults = {
|
|
2169
|
-
super: false,
|
|
2170
|
-
inherit: false,
|
|
2171
|
-
createRole: false,
|
|
2172
|
-
createDb: false,
|
|
2173
|
-
canLogin: false,
|
|
2174
|
-
replication: false,
|
|
2175
|
-
connLimit: -1,
|
|
2176
|
-
bypassRls: false
|
|
2177
|
-
};
|
|
2178
|
-
const processRoles = async (ast, dbStructure, { verifying, internal: { roles } }) => {
|
|
2179
|
-
if (!dbStructure.roles || !roles) return;
|
|
2180
|
-
const codeRoles = roles.map((role) => {
|
|
2181
|
-
const { defaultPrivileges: _, ...roleWithoutPrivileges } = role;
|
|
2182
|
-
return {
|
|
2183
|
-
...defaults,
|
|
2184
|
-
...roleWithoutPrivileges
|
|
2185
|
-
};
|
|
2186
|
-
});
|
|
2187
|
-
const found = /* @__PURE__ */ new Set();
|
|
2188
|
-
const dropRoles = [];
|
|
2189
|
-
for (const dbRole of dbStructure.roles) {
|
|
2190
|
-
const {
|
|
2191
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2192
|
-
defaultPrivileges: _,
|
|
2193
|
-
...dbRoleWithoutPrivileges
|
|
2194
|
-
} = dbRole;
|
|
2195
|
-
const codeRole = codeRoles.find(
|
|
2196
|
-
(codeRole2) => dbRole.name === codeRole2.name
|
|
2197
|
-
);
|
|
2198
|
-
if (codeRole) {
|
|
2199
|
-
found.add(dbRole.name);
|
|
2200
|
-
if (!internal.deepCompare(dbRoleWithoutPrivileges, codeRole)) {
|
|
2201
|
-
ast.push({
|
|
2202
|
-
type: "changeRole",
|
|
2203
|
-
name: dbRole.name,
|
|
2204
|
-
from: dbRoleWithoutPrivileges,
|
|
2205
|
-
to: codeRole
|
|
2206
|
-
});
|
|
2207
|
-
}
|
|
2208
|
-
continue;
|
|
2209
|
-
}
|
|
2210
|
-
dropRoles.push(dbRole);
|
|
2211
|
-
}
|
|
2212
|
-
for (const codeRole of codeRoles) {
|
|
2213
|
-
if (found.has(codeRole.name)) continue;
|
|
2214
|
-
if (dropRoles.length) {
|
|
2215
|
-
const i = await promptCreateOrRename(
|
|
2216
|
-
"table",
|
|
2217
|
-
codeRole.name,
|
|
2218
|
-
dropRoles.map((x) => x.name),
|
|
2219
|
-
verifying
|
|
2220
|
-
);
|
|
2221
|
-
if (i) {
|
|
2222
|
-
const dbRole = dropRoles[i - 1];
|
|
2223
|
-
dropRoles.splice(i - 1, 1);
|
|
2224
|
-
ast.push(makeRenameOrChangeAst(dbRole, codeRole));
|
|
2225
|
-
continue;
|
|
2226
|
-
}
|
|
2227
|
-
}
|
|
2228
|
-
ast.push({
|
|
2229
|
-
type: "role",
|
|
2230
|
-
action: "create",
|
|
2231
|
-
...codeRole
|
|
2232
|
-
});
|
|
2233
|
-
}
|
|
2234
|
-
for (const dbRole of dropRoles) {
|
|
2235
|
-
ast.push({
|
|
2236
|
-
type: "role",
|
|
2237
|
-
action: "drop",
|
|
2238
|
-
...dbRole
|
|
2239
|
-
});
|
|
2240
|
-
}
|
|
2241
|
-
};
|
|
2242
|
-
const makeRenameOrChangeAst = (dbRole, codeRole) => {
|
|
2243
|
-
const { name: dbRoleName, ...dbRoleRest } = dbRole;
|
|
2244
|
-
const { name: codeRoleName, ...codeRoleRest } = codeRole;
|
|
2245
|
-
if (internal.deepCompare(dbRoleRest, codeRoleRest) && dbRoleName !== codeRoleName) {
|
|
2246
|
-
return {
|
|
2247
|
-
type: "renameRole",
|
|
2248
|
-
from: dbRoleName,
|
|
2249
|
-
to: codeRoleName
|
|
2250
|
-
};
|
|
2251
|
-
} else {
|
|
2252
|
-
return {
|
|
2253
|
-
type: "changeRole",
|
|
2254
|
-
name: dbRole.name,
|
|
2255
|
-
from: dbRole,
|
|
2256
|
-
to: codeRole
|
|
2257
|
-
};
|
|
2258
|
-
}
|
|
2259
|
-
};
|
|
2260
|
-
|
|
2261
|
-
const ALL_OBJECT_TYPES = [
|
|
2262
|
-
"TABLES",
|
|
2263
|
-
"SEQUENCES",
|
|
2264
|
-
"FUNCTIONS",
|
|
2265
|
-
"TYPES",
|
|
2266
|
-
"SCHEMAS",
|
|
2267
|
-
"LARGE_OBJECTS"
|
|
2268
|
-
];
|
|
2269
|
-
const createAllObjectConfigs = (isGrantable, schema, supportedPrivileges) => {
|
|
2270
|
-
let types;
|
|
2271
|
-
if (schema) {
|
|
2272
|
-
types = ALL_OBJECT_TYPES.filter(
|
|
2273
|
-
(t) => t !== "SCHEMAS" && t !== "LARGE_OBJECTS"
|
|
2274
|
-
);
|
|
2275
|
-
} else {
|
|
2276
|
-
types = ALL_OBJECT_TYPES.filter((t) => {
|
|
2277
|
-
if (t === "LARGE_OBJECTS") {
|
|
2278
|
-
return supportedPrivileges.PRIVILEGES.LARGE_OBJECT !== void 0;
|
|
2279
|
-
}
|
|
2280
|
-
return true;
|
|
2281
|
-
});
|
|
2282
|
-
}
|
|
2283
|
-
return types.map((object) => ({
|
|
2284
|
-
object,
|
|
2285
|
-
privilegeConfigs: [{ privilege: "ALL", isGrantable }]
|
|
2286
|
-
}));
|
|
2287
|
-
};
|
|
2288
|
-
const objectTypeToKey = {
|
|
2289
|
-
TABLES: "tables",
|
|
2290
|
-
SEQUENCES: "sequences",
|
|
2291
|
-
FUNCTIONS: "functions",
|
|
2292
|
-
TYPES: "types",
|
|
2293
|
-
SCHEMAS: "schemas",
|
|
2294
|
-
LARGE_OBJECTS: "largeObjects"
|
|
2295
|
-
};
|
|
2296
|
-
const splitPrivilegeConfigs = (configs) => {
|
|
2297
|
-
const regular = [];
|
|
2298
|
-
const grantable = [];
|
|
2299
|
-
for (const p of configs) {
|
|
2300
|
-
if (p.isGrantable) {
|
|
2301
|
-
grantable.push(p.privilege);
|
|
2302
|
-
} else {
|
|
2303
|
-
regular.push(p.privilege);
|
|
2304
|
-
}
|
|
2305
|
-
}
|
|
2306
|
-
return { regular, grantable };
|
|
2307
|
-
};
|
|
2308
|
-
const createPrivilegeSetting = (regular, grantable) => {
|
|
2309
|
-
const setting = {};
|
|
2310
|
-
if (regular.length) setting.privileges = regular;
|
|
2311
|
-
if (grantable.length) setting.grantablePrivileges = grantable;
|
|
2312
|
-
return setting;
|
|
2313
|
-
};
|
|
2314
|
-
const collapsePrivileges = (privs, newPrivilegeConfigs, expectedPrivs, isGrantable) => {
|
|
2315
|
-
const hasAll = privs.some((p) => p.privilege === "ALL");
|
|
2316
|
-
const allPresent = hasAll || privs.length > 0 && privs.length === expectedPrivs.length && expectedPrivs.every((priv) => privs.some((p) => p.privilege === priv));
|
|
2317
|
-
if (allPresent) {
|
|
2318
|
-
newPrivilegeConfigs.push({ privilege: "ALL", isGrantable });
|
|
2319
|
-
} else if (privs.length > 0) {
|
|
2320
|
-
newPrivilegeConfigs.push(...privs);
|
|
2321
|
-
}
|
|
2322
|
-
};
|
|
2323
|
-
const collapsePrivilegesToAll = (privilege, objectTypeToAllPrivileges) => {
|
|
2324
|
-
const collapsedObjectConfigs = privilege.objectConfigs.map(
|
|
2325
|
-
(objConfig) => {
|
|
2326
|
-
const allPrivileges = objectTypeToAllPrivileges[objConfig.object];
|
|
2327
|
-
const expectedPrivs = allPrivileges.filter((p) => p !== "ALL");
|
|
2328
|
-
const regularPrivs = [];
|
|
2329
|
-
const grantablePrivs = [];
|
|
2330
|
-
for (const p of objConfig.privilegeConfigs) {
|
|
2331
|
-
if (p.isGrantable) {
|
|
2332
|
-
grantablePrivs.push(p);
|
|
2333
|
-
} else {
|
|
2334
|
-
regularPrivs.push(p);
|
|
2335
|
-
}
|
|
2336
|
-
}
|
|
2337
|
-
const newPrivilegeConfigs = [];
|
|
2338
|
-
collapsePrivileges(
|
|
2339
|
-
regularPrivs,
|
|
2340
|
-
newPrivilegeConfigs,
|
|
2341
|
-
expectedPrivs,
|
|
2342
|
-
false
|
|
2343
|
-
);
|
|
2344
|
-
collapsePrivileges(
|
|
2345
|
-
grantablePrivs,
|
|
2346
|
-
newPrivilegeConfigs,
|
|
2347
|
-
expectedPrivs,
|
|
2348
|
-
true
|
|
2349
|
-
);
|
|
2350
|
-
return {
|
|
2351
|
-
object: objConfig.object,
|
|
2352
|
-
privilegeConfigs: newPrivilegeConfigs
|
|
2353
|
-
};
|
|
2354
|
-
}
|
|
2355
|
-
);
|
|
2356
|
-
return {
|
|
2357
|
-
owner: privilege.owner,
|
|
2358
|
-
grantee: privilege.grantee,
|
|
2359
|
-
schema: privilege.schema,
|
|
2360
|
-
objectConfigs: collapsedObjectConfigs
|
|
2361
|
-
};
|
|
2362
|
-
};
|
|
2363
|
-
const privilegeConfigsMatch = (a, b, objectType, objectTypeToAllPrivileges) => {
|
|
2364
|
-
if (a.privilege === b.privilege && a.isGrantable === b.isGrantable) {
|
|
2365
|
-
return true;
|
|
2366
|
-
}
|
|
2367
|
-
const allPrivileges = objectTypeToAllPrivileges[objectType];
|
|
2368
|
-
const expectedPrivs = allPrivileges.filter((p) => p !== "ALL");
|
|
2369
|
-
if (a.privilege === "ALL" && a.isGrantable === b.isGrantable) {
|
|
2370
|
-
return expectedPrivs.includes(b.privilege);
|
|
2371
|
-
}
|
|
2372
|
-
if (b.privilege === "ALL" && a.isGrantable === b.isGrantable) {
|
|
2373
|
-
return expectedPrivs.includes(a.privilege);
|
|
2374
|
-
}
|
|
2375
|
-
return false;
|
|
2376
|
-
};
|
|
2377
|
-
const normalizeRoleName = (name) => {
|
|
2378
|
-
if (name.startsWith('"') && name.endsWith('"')) {
|
|
2379
|
-
return name.slice(1, -1);
|
|
2380
|
-
}
|
|
2381
|
-
return name;
|
|
2382
|
-
};
|
|
2383
|
-
const processPrivilegeConfig = (config, objectType) => {
|
|
2384
|
-
if (!config) return;
|
|
2385
|
-
const privilegeConfigs = [];
|
|
2386
|
-
if (config.privileges) {
|
|
2387
|
-
for (const p of config.privileges) {
|
|
2388
|
-
privilegeConfigs.push({ privilege: p, isGrantable: false });
|
|
2389
|
-
}
|
|
2390
|
-
}
|
|
2391
|
-
if (config.grantablePrivileges) {
|
|
2392
|
-
for (const p of config.grantablePrivileges) {
|
|
2393
|
-
privilegeConfigs.push({ privilege: p, isGrantable: true });
|
|
2394
|
-
}
|
|
2395
|
-
}
|
|
2396
|
-
if (privilegeConfigs.length) {
|
|
2397
|
-
return { object: objectType, privilegeConfigs };
|
|
2398
|
-
}
|
|
2399
|
-
return;
|
|
2400
|
-
};
|
|
2401
|
-
const hasAnyPrivilege = (obj) => {
|
|
2402
|
-
if (!obj) return false;
|
|
2403
|
-
for (const key of ALL_OBJECT_TYPES) {
|
|
2404
|
-
const setting = obj[objectTypeToKey[key]];
|
|
2405
|
-
if (setting?.privileges?.length || setting?.grantablePrivileges?.length) {
|
|
2406
|
-
return true;
|
|
2407
|
-
}
|
|
2408
|
-
}
|
|
2409
|
-
return false;
|
|
2410
|
-
};
|
|
2411
|
-
const processDefaultPrivileges = (ast, dbStructure, { internal: { roles } }) => {
|
|
2412
|
-
if (!dbStructure.defaultPrivileges || !roles) return;
|
|
2413
|
-
const supportedPrivileges = internal.getSupportedDefaultPrivileges(
|
|
2414
|
-
dbStructure.version
|
|
2415
|
-
);
|
|
2416
|
-
const objectTypeToAllPrivileges = {};
|
|
2417
|
-
objectTypeToAllPrivileges.TABLES = supportedPrivileges.PRIVILEGES.TABLE;
|
|
2418
|
-
objectTypeToAllPrivileges.SEQUENCES = supportedPrivileges.PRIVILEGES.SEQUENCE;
|
|
2419
|
-
objectTypeToAllPrivileges.FUNCTIONS = supportedPrivileges.PRIVILEGES.FUNCTION;
|
|
2420
|
-
objectTypeToAllPrivileges.TYPES = supportedPrivileges.PRIVILEGES.TYPE;
|
|
2421
|
-
objectTypeToAllPrivileges.SCHEMAS = supportedPrivileges.PRIVILEGES.SCHEMA;
|
|
2422
|
-
if (supportedPrivileges.PRIVILEGES.LARGE_OBJECT) {
|
|
2423
|
-
objectTypeToAllPrivileges.LARGE_OBJECTS = supportedPrivileges.PRIVILEGES.LARGE_OBJECT;
|
|
2424
|
-
}
|
|
2425
|
-
const codePrivileges = /* @__PURE__ */ new Map();
|
|
2426
|
-
for (const role of roles) {
|
|
2427
|
-
if (!role.defaultPrivileges) continue;
|
|
2428
|
-
for (const privilege of role.defaultPrivileges) {
|
|
2429
|
-
const key = `${role.name}.${privilege.schema}`;
|
|
2430
|
-
const objectConfigs = [];
|
|
2431
|
-
if (privilege.allGrantable) {
|
|
2432
|
-
objectConfigs.push(
|
|
2433
|
-
...createAllObjectConfigs(
|
|
2434
|
-
true,
|
|
2435
|
-
privilege.schema,
|
|
2436
|
-
supportedPrivileges
|
|
2437
|
-
)
|
|
2438
|
-
);
|
|
2439
|
-
} else if (privilege.all) {
|
|
2440
|
-
objectConfigs.push(
|
|
2441
|
-
...createAllObjectConfigs(
|
|
2442
|
-
false,
|
|
2443
|
-
privilege.schema,
|
|
2444
|
-
supportedPrivileges
|
|
2445
|
-
)
|
|
2446
|
-
);
|
|
2447
|
-
}
|
|
2448
|
-
for (const objectType of ALL_OBJECT_TYPES) {
|
|
2449
|
-
if (privilege.schema && (objectType === "SCHEMAS" || objectType === "LARGE_OBJECTS")) {
|
|
2450
|
-
continue;
|
|
2451
|
-
}
|
|
2452
|
-
if (objectType === "LARGE_OBJECTS" && !supportedPrivileges.PRIVILEGES.LARGE_OBJECT) {
|
|
2453
|
-
continue;
|
|
2454
|
-
}
|
|
2455
|
-
const key2 = objectTypeToKey[objectType];
|
|
2456
|
-
if (key2 in privilege) {
|
|
2457
|
-
const config = privilege[key2];
|
|
2458
|
-
const processed = processPrivilegeConfig(config, objectType);
|
|
2459
|
-
if (processed) {
|
|
2460
|
-
const existingIndex = objectConfigs.findIndex(
|
|
2461
|
-
(o) => o.object === objectType
|
|
2462
|
-
);
|
|
2463
|
-
if (existingIndex >= 0) {
|
|
2464
|
-
objectConfigs[existingIndex] = processed;
|
|
2465
|
-
} else {
|
|
2466
|
-
objectConfigs.push(processed);
|
|
2467
|
-
}
|
|
2468
|
-
}
|
|
2469
|
-
}
|
|
2470
|
-
}
|
|
2471
|
-
if (objectConfigs.length) {
|
|
2472
|
-
codePrivileges.set(key, {
|
|
2473
|
-
owner: void 0,
|
|
2474
|
-
grantee: role.name,
|
|
2475
|
-
schema: privilege.schema,
|
|
2476
|
-
objectConfigs
|
|
2477
|
-
});
|
|
2478
|
-
}
|
|
2479
|
-
}
|
|
2480
|
-
}
|
|
2481
|
-
const found = /* @__PURE__ */ new Set();
|
|
2482
|
-
for (const dbPrivilege of dbStructure.defaultPrivileges) {
|
|
2483
|
-
const grantee = normalizeRoleName(dbPrivilege.grantee);
|
|
2484
|
-
if (grantee === "postgres") continue;
|
|
2485
|
-
const key = `${grantee}.${dbPrivilege.schema}`;
|
|
2486
|
-
const codePrivilege = codePrivileges.get(key);
|
|
2487
|
-
if (codePrivilege) {
|
|
2488
|
-
found.add(key);
|
|
2489
|
-
const grant = {};
|
|
2490
|
-
const revoke = {};
|
|
2491
|
-
for (const objectType of ALL_OBJECT_TYPES) {
|
|
2492
|
-
if (objectType === "LARGE_OBJECTS" && !supportedPrivileges.PRIVILEGES.LARGE_OBJECT) {
|
|
2493
|
-
continue;
|
|
2494
|
-
}
|
|
2495
|
-
const dbObj = dbPrivilege.objectConfigs.find(
|
|
2496
|
-
(o) => o.object === objectType
|
|
2497
|
-
);
|
|
2498
|
-
const codeObj = codePrivilege.objectConfigs.find(
|
|
2499
|
-
(o) => o.object === objectType
|
|
2500
|
-
);
|
|
2501
|
-
const dbPrivs = dbObj?.privilegeConfigs ?? [];
|
|
2502
|
-
const codePrivs = codeObj?.privilegeConfigs ?? [];
|
|
2503
|
-
const collapsedDbPrivilege = {
|
|
2504
|
-
owner: dbPrivilege.owner,
|
|
2505
|
-
grantee: dbPrivilege.grantee,
|
|
2506
|
-
schema: dbPrivilege.schema,
|
|
2507
|
-
objectConfigs: [{ object: objectType, privilegeConfigs: dbPrivs }]
|
|
2508
|
-
};
|
|
2509
|
-
const collapsedDbObj = collapsePrivilegesToAll(
|
|
2510
|
-
collapsedDbPrivilege,
|
|
2511
|
-
objectTypeToAllPrivileges
|
|
2512
|
-
).objectConfigs[0];
|
|
2513
|
-
const collapsedDbPrivs = collapsedDbObj?.privilegeConfigs ?? [];
|
|
2514
|
-
const toGrant = codePrivs.filter(
|
|
2515
|
-
(cp) => !collapsedDbPrivs.some(
|
|
2516
|
-
(dp) => privilegeConfigsMatch(
|
|
2517
|
-
cp,
|
|
2518
|
-
dp,
|
|
2519
|
-
objectType,
|
|
2520
|
-
objectTypeToAllPrivileges
|
|
2521
|
-
)
|
|
2522
|
-
)
|
|
2523
|
-
);
|
|
2524
|
-
const toRevoke = dbPrivs.filter(
|
|
2525
|
-
(dp) => !codePrivs.some(
|
|
2526
|
-
(cp) => privilegeConfigsMatch(
|
|
2527
|
-
cp,
|
|
2528
|
-
dp,
|
|
2529
|
-
objectType,
|
|
2530
|
-
objectTypeToAllPrivileges
|
|
2531
|
-
)
|
|
2532
|
-
)
|
|
2533
|
-
);
|
|
2534
|
-
if (toGrant.length) {
|
|
2535
|
-
const { regular, grantable } = splitPrivilegeConfigs(toGrant);
|
|
2536
|
-
const setting = createPrivilegeSetting(regular, grantable);
|
|
2537
|
-
if (setting.privileges || setting.grantablePrivileges) {
|
|
2538
|
-
grant[objectTypeToKey[objectType]] = setting;
|
|
2539
|
-
}
|
|
2540
|
-
}
|
|
2541
|
-
if (toRevoke.length) {
|
|
2542
|
-
const { regular, grantable } = splitPrivilegeConfigs(toRevoke);
|
|
2543
|
-
const setting = createPrivilegeSetting(regular, grantable);
|
|
2544
|
-
if (setting.privileges || setting.grantablePrivileges) {
|
|
2545
|
-
revoke[objectTypeToKey[objectType]] = setting;
|
|
2546
|
-
}
|
|
2547
|
-
}
|
|
2548
|
-
}
|
|
2549
|
-
const hasGrant = hasAnyPrivilege(grant);
|
|
2550
|
-
const hasRevoke = hasAnyPrivilege(revoke);
|
|
2551
|
-
if (hasGrant || hasRevoke) {
|
|
2552
|
-
ast.push({
|
|
2553
|
-
type: "defaultPrivilege",
|
|
2554
|
-
grantee,
|
|
2555
|
-
schema: dbPrivilege.schema,
|
|
2556
|
-
grant: hasGrant ? grant : void 0,
|
|
2557
|
-
revoke: hasRevoke ? revoke : void 0
|
|
2558
|
-
});
|
|
2559
|
-
}
|
|
2560
|
-
} else {
|
|
2561
|
-
const revoke = {};
|
|
2562
|
-
for (const obj of dbPrivilege.objectConfigs) {
|
|
2563
|
-
const { regular, grantable } = splitPrivilegeConfigs(
|
|
2564
|
-
obj.privilegeConfigs
|
|
2565
|
-
);
|
|
2566
|
-
const setting = createPrivilegeSetting(regular, grantable);
|
|
2567
|
-
if (setting.privileges || setting.grantablePrivileges) {
|
|
2568
|
-
revoke[objectTypeToKey[obj.object]] = setting;
|
|
2569
|
-
}
|
|
2570
|
-
}
|
|
2571
|
-
if (hasAnyPrivilege(revoke)) {
|
|
2572
|
-
ast.push({
|
|
2573
|
-
type: "defaultPrivilege",
|
|
2574
|
-
grantee,
|
|
2575
|
-
schema: dbPrivilege.schema,
|
|
2576
|
-
revoke
|
|
2577
|
-
});
|
|
2578
|
-
}
|
|
2579
|
-
}
|
|
2580
|
-
}
|
|
2581
|
-
for (const [key, codePrivilege] of codePrivileges) {
|
|
2582
|
-
if (found.has(key)) continue;
|
|
2583
|
-
const grant = {};
|
|
2584
|
-
for (const obj of codePrivilege.objectConfigs) {
|
|
2585
|
-
const { regular, grantable } = splitPrivilegeConfigs(
|
|
2586
|
-
obj.privilegeConfigs
|
|
2587
|
-
);
|
|
2588
|
-
const setting = createPrivilegeSetting(regular, grantable);
|
|
2589
|
-
if (setting.privileges || setting.grantablePrivileges) {
|
|
2590
|
-
grant[objectTypeToKey[obj.object]] = setting;
|
|
2591
|
-
}
|
|
2592
|
-
}
|
|
2593
|
-
if (hasAnyPrivilege(grant)) {
|
|
2594
|
-
ast.push({
|
|
2595
|
-
type: "defaultPrivilege",
|
|
2596
|
-
grantee: codePrivilege.grantee,
|
|
2597
|
-
schema: codePrivilege.schema,
|
|
2598
|
-
grant
|
|
2599
|
-
});
|
|
2600
|
-
}
|
|
2601
|
-
}
|
|
2602
|
-
};
|
|
2603
|
-
|
|
2604
|
-
class PendingDbTypes {
|
|
2605
|
-
constructor() {
|
|
2606
|
-
this.set = /* @__PURE__ */ new Set();
|
|
2607
|
-
}
|
|
2608
|
-
add(schemaName = "public", name) {
|
|
2609
|
-
this.set.add(`"${schemaName}"."${name}"`);
|
|
2610
|
-
}
|
|
2611
|
-
}
|
|
2612
|
-
const composeMigration = async (adapter, config, ast, dbStructure, params) => {
|
|
2613
|
-
const { structureToAstCtx, currentSchema } = params;
|
|
2614
|
-
await processRoles(ast, dbStructure, params);
|
|
2615
|
-
processDefaultPrivileges(ast, dbStructure, params);
|
|
2616
|
-
const domainsMap = rakeDb.makeDomainsMap(structureToAstCtx, dbStructure);
|
|
2617
|
-
await processSchemas(ast, dbStructure, params);
|
|
2618
|
-
processExtensions(ast, dbStructure, params);
|
|
2619
|
-
const pendingDbTypes = new PendingDbTypes();
|
|
2620
|
-
await processDomains(
|
|
2621
|
-
ast,
|
|
2622
|
-
adapter,
|
|
2623
|
-
domainsMap,
|
|
2624
|
-
dbStructure,
|
|
2625
|
-
params,
|
|
2626
|
-
pendingDbTypes
|
|
2627
|
-
);
|
|
2628
|
-
await processEnums(ast, dbStructure, params, pendingDbTypes);
|
|
2629
|
-
await processTables(
|
|
2630
|
-
ast,
|
|
2631
|
-
domainsMap,
|
|
2632
|
-
adapter,
|
|
2633
|
-
dbStructure,
|
|
2634
|
-
config,
|
|
2635
|
-
params,
|
|
2636
|
-
pendingDbTypes
|
|
2637
|
-
);
|
|
2638
|
-
return rakeDb.astToMigration(currentSchema, config, ast);
|
|
2639
|
-
};
|
|
2640
|
-
|
|
2641
|
-
const rollbackErr = new Error("Rollback");
|
|
2642
|
-
const verifyMigration = async (adapter, config, migrationCode, generateMigrationParams, roles, defaultPrivileges) => {
|
|
2643
|
-
const migrationFn = new Function("change", migrationCode);
|
|
2644
|
-
let code;
|
|
2645
|
-
try {
|
|
2646
|
-
await adapter.transaction(async (trx) => {
|
|
2647
|
-
const changeFns = [];
|
|
2648
|
-
migrationFn((changeCb) => {
|
|
2649
|
-
changeFns.push(changeCb);
|
|
2650
|
-
});
|
|
2651
|
-
const { log } = config;
|
|
2652
|
-
config.log = false;
|
|
2653
|
-
const db = rakeDb.createMigrationInterface(trx, true, config).getDb(
|
|
2654
|
-
config.columnTypes
|
|
2655
|
-
);
|
|
2656
|
-
config.log = log;
|
|
2657
|
-
for (const changeFn of changeFns) {
|
|
2658
|
-
await changeFn(db, true);
|
|
2659
|
-
}
|
|
2660
|
-
const dbStructure = await rakeDb.introspectDbSchema(trx, {
|
|
2661
|
-
roles,
|
|
2662
|
-
loadDefaultPrivileges: defaultPrivileges?.loadDefaultPrivileges
|
|
2663
|
-
});
|
|
2664
|
-
generateMigrationParams.verifying = true;
|
|
2665
|
-
try {
|
|
2666
|
-
code = await composeMigration(
|
|
2667
|
-
trx,
|
|
2668
|
-
config,
|
|
2669
|
-
[],
|
|
2670
|
-
dbStructure,
|
|
2671
|
-
generateMigrationParams
|
|
2672
|
-
);
|
|
2673
|
-
} catch (err) {
|
|
2674
|
-
if (err instanceof AbortSignal) {
|
|
2675
|
-
code = false;
|
|
2676
|
-
throw rollbackErr;
|
|
2677
|
-
}
|
|
2678
|
-
throw err;
|
|
2679
|
-
}
|
|
2680
|
-
throw rollbackErr;
|
|
2681
|
-
});
|
|
2682
|
-
} catch (err) {
|
|
2683
|
-
if (err !== rollbackErr) {
|
|
2684
|
-
throw err;
|
|
2685
|
-
}
|
|
2686
|
-
}
|
|
2687
|
-
return code;
|
|
2688
|
-
};
|
|
2689
|
-
|
|
2690
|
-
const report = (ast, config, currentSchema) => {
|
|
2691
|
-
if (!config.logger) return;
|
|
2692
|
-
const code = [];
|
|
2693
|
-
let green, red, yellow, pale;
|
|
2694
|
-
if (typeof config.log === "object" && config.log.colors === false) {
|
|
2695
|
-
green = red = yellow = pale = (s) => s;
|
|
2696
|
-
} else {
|
|
2697
|
-
({ green, red, yellow, pale } = internal.colors);
|
|
2698
|
-
}
|
|
2699
|
-
for (const a of ast) {
|
|
2700
|
-
switch (a.type) {
|
|
2701
|
-
case "table": {
|
|
2702
|
-
let hasPrimaryKey = !!a.primaryKey;
|
|
2703
|
-
const counters = {
|
|
2704
|
-
column: 0,
|
|
2705
|
-
index: a.indexes?.length ?? 0,
|
|
2706
|
-
exclude: a.excludes?.length ?? 0,
|
|
2707
|
-
"foreign key": a.constraints?.reduce(
|
|
2708
|
-
(sum, c) => c.references ? sum + 1 : sum,
|
|
2709
|
-
0
|
|
2710
|
-
) ?? 0,
|
|
2711
|
-
check: a.constraints?.reduce(
|
|
2712
|
-
(sum, c) => c.check ? sum + 1 : sum,
|
|
2713
|
-
0
|
|
2714
|
-
) ?? 0
|
|
2715
|
-
};
|
|
2716
|
-
for (const key in a.shape) {
|
|
2717
|
-
counters.column++;
|
|
2718
|
-
const column = a.shape[key];
|
|
2719
|
-
if (column.data.primaryKey) {
|
|
2720
|
-
hasPrimaryKey = true;
|
|
2721
|
-
}
|
|
2722
|
-
if (column.data.indexes) {
|
|
2723
|
-
counters.index += column.data.indexes.length;
|
|
2724
|
-
}
|
|
2725
|
-
if (column.data.excludes) {
|
|
2726
|
-
counters.exclude += column.data.excludes.length;
|
|
2727
|
-
}
|
|
2728
|
-
if (column.data.foreignKeys) {
|
|
2729
|
-
counters["foreign key"] += column.data.foreignKeys.length;
|
|
2730
|
-
}
|
|
2731
|
-
if (column.data.checks) {
|
|
2732
|
-
counters.check += column.data.checks.length;
|
|
2733
|
-
}
|
|
2734
|
-
}
|
|
2735
|
-
const summary = [];
|
|
2736
|
-
for (const key in counters) {
|
|
2737
|
-
const value = counters[key];
|
|
2738
|
-
if (value || key === "column") {
|
|
2739
|
-
summary.push(
|
|
2740
|
-
`${value} ${internal.pluralize(key, value, key === "index" ? "es" : "s")}`
|
|
2741
|
-
);
|
|
2742
|
-
}
|
|
2743
|
-
}
|
|
2744
|
-
if (!hasPrimaryKey) {
|
|
2745
|
-
summary.push("no primary key");
|
|
2746
|
-
}
|
|
2747
|
-
code.push(
|
|
2748
|
-
`${a.action === "create" ? green("+ create table") : red("- drop table")} ${dbItemName(a, currentSchema)} (${summary.join(", ")})`
|
|
2749
|
-
);
|
|
2750
|
-
break;
|
|
2751
|
-
}
|
|
2752
|
-
case "changeTable": {
|
|
2753
|
-
const inner = [];
|
|
2754
|
-
const toCodeCtx = {
|
|
2755
|
-
t: "t",
|
|
2756
|
-
table: a.name,
|
|
2757
|
-
currentSchema,
|
|
2758
|
-
migration: true,
|
|
2759
|
-
snakeCase: config.snakeCase
|
|
2760
|
-
};
|
|
2761
|
-
for (const key in a.shape) {
|
|
2762
|
-
const changes = internal.toArray(a.shape[key]);
|
|
2763
|
-
for (const change of changes) {
|
|
2764
|
-
if (change.type === "add" || change.type === "drop") {
|
|
2765
|
-
const column = change.item;
|
|
2766
|
-
const { primaryKey, indexes, excludes, foreignKeys, checks } = column.data;
|
|
2767
|
-
inner.push(
|
|
2768
|
-
`${change.type === "add" ? green("+ add column") : red("- drop column")} ${key} ${column.data.alias ?? getColumnDbType(column, currentSchema)}${column.data.isNullable ? " nullable" : ""}${primaryKey ? " primary key" : ""}${foreignKeys ? ` references ${foreignKeys.map((fk) => {
|
|
2769
|
-
return `${fnOrTableToString(
|
|
2770
|
-
fk.fnOrTable
|
|
2771
|
-
)}(${fk.foreignColumns.join(", ")})`;
|
|
2772
|
-
}).join(", ")}` : ""}${indexes?.length ? indexes.length === 1 ? ", has index" : `, has ${indexes.length} indexes` : ""}${excludes?.length ? excludes.length === 1 ? ", has exclude" : `, has ${excludes.length} excludes` : ""}${checks?.length ? `, checks ${checks.map((check) => check.sql.toSQL({ values: [] })).join(", ")}` : ""}`
|
|
2773
|
-
);
|
|
2774
|
-
} else if (change.type === "change") {
|
|
2775
|
-
let name = change.from.column?.data.name ?? key;
|
|
2776
|
-
if (config.snakeCase) name = internal.toCamelCase(name);
|
|
2777
|
-
const changes2 = [];
|
|
2778
|
-
inner.push(`${yellow("~ change column")} ${name}:`, changes2);
|
|
2779
|
-
changes2.push(`${yellow("from")}: `);
|
|
2780
|
-
const fromCode = change.from.column?.toCode(toCodeCtx, key);
|
|
2781
|
-
if (fromCode) {
|
|
2782
|
-
for (const code2 of fromCode) {
|
|
2783
|
-
internal.addCode(changes2, code2);
|
|
2784
|
-
}
|
|
2785
|
-
}
|
|
2786
|
-
changes2.push(` ${yellow("to")}: `);
|
|
2787
|
-
const toCode = change.to.column?.toCode(toCodeCtx, key);
|
|
2788
|
-
if (toCode) {
|
|
2789
|
-
for (const code2 of toCode) {
|
|
2790
|
-
internal.addCode(changes2, code2);
|
|
2791
|
-
}
|
|
2792
|
-
}
|
|
2793
|
-
} else if (change.type === "rename") {
|
|
2794
|
-
inner.push(
|
|
2795
|
-
`${yellow("~ rename column")} ${config.snakeCase ? internal.toCamelCase(key) : key} ${yellow("=>")} ${change.name}`
|
|
2796
|
-
);
|
|
2797
|
-
} else {
|
|
2798
|
-
internal.exhaustive(change.type);
|
|
2799
|
-
}
|
|
2800
|
-
}
|
|
2801
|
-
}
|
|
2802
|
-
if (a.drop.primaryKey) {
|
|
2803
|
-
inner.push(
|
|
2804
|
-
`${red(`- drop primary key`)} on (${a.drop.primaryKey.columns.join(
|
|
2805
|
-
", "
|
|
2806
|
-
)})`
|
|
2807
|
-
);
|
|
2808
|
-
}
|
|
2809
|
-
if (a.drop.indexes) {
|
|
2810
|
-
for (const index of a.drop.indexes) {
|
|
2811
|
-
inner.push(
|
|
2812
|
-
`${red(
|
|
2813
|
-
`- drop${index.options.unique ? " unique" : ""} index`
|
|
2814
|
-
)} on (${index.columns.map((c) => "column" in c ? c.column : c.expression).join(", ")})`
|
|
2815
|
-
);
|
|
2816
|
-
}
|
|
2817
|
-
}
|
|
2818
|
-
if (a.drop.excludes) {
|
|
2819
|
-
for (const exclude of a.drop.excludes) {
|
|
2820
|
-
inner.push(
|
|
2821
|
-
`${red(`- drop exclude`)} on (${exclude.columns.map((c) => "column" in c ? c.column : c.expression).join(", ")})`
|
|
2822
|
-
);
|
|
2823
|
-
}
|
|
2824
|
-
}
|
|
2825
|
-
if (a.drop.constraints) {
|
|
2826
|
-
for (const { references } of a.drop.constraints) {
|
|
2827
|
-
if (!references) continue;
|
|
2828
|
-
const [schema, name] = rakeDb.getSchemaAndTableFromName(
|
|
2829
|
-
currentSchema,
|
|
2830
|
-
references.fnOrTable
|
|
2831
|
-
);
|
|
2832
|
-
inner.push(
|
|
2833
|
-
`${red(`- drop foreign key`)} on (${references.columns.join(
|
|
2834
|
-
", "
|
|
2835
|
-
)}) to ${dbItemName(
|
|
2836
|
-
{
|
|
2837
|
-
schema,
|
|
2838
|
-
name
|
|
2839
|
-
},
|
|
2840
|
-
currentSchema
|
|
2841
|
-
)}(${references.foreignColumns.join(", ")})`
|
|
2842
|
-
);
|
|
2843
|
-
}
|
|
2844
|
-
for (const { check } of a.drop.constraints) {
|
|
2845
|
-
if (!check) continue;
|
|
2846
|
-
inner.push(`${red(`- drop check`)} ${check.toSQL({ values: [] })}`);
|
|
2847
|
-
}
|
|
2848
|
-
}
|
|
2849
|
-
if (a.add.primaryKey) {
|
|
2850
|
-
inner.push(
|
|
2851
|
-
`${green(`+ add primary key`)} on (${a.add.primaryKey.columns.join(
|
|
2852
|
-
", "
|
|
2853
|
-
)})`
|
|
2854
|
-
);
|
|
2855
|
-
}
|
|
2856
|
-
if (a.add.indexes) {
|
|
2857
|
-
for (const index of a.add.indexes) {
|
|
2858
|
-
inner.push(
|
|
2859
|
-
`${green(
|
|
2860
|
-
`+ add${index.options.unique ? " unique" : ""} index`
|
|
2861
|
-
)} on (${index.columns.map((c) => "column" in c ? c.column : c.expression).join(", ")})`
|
|
2862
|
-
);
|
|
2863
|
-
}
|
|
2864
|
-
}
|
|
2865
|
-
if (a.add.excludes) {
|
|
2866
|
-
for (const exclude of a.add.excludes) {
|
|
2867
|
-
inner.push(
|
|
2868
|
-
`${green(`+ add exclude`)} on (${exclude.columns.map((c) => "column" in c ? c.column : c.expression).join(", ")})`
|
|
2869
|
-
);
|
|
2870
|
-
}
|
|
2871
|
-
}
|
|
2872
|
-
if (a.add.constraints) {
|
|
2873
|
-
for (const { references } of a.add.constraints) {
|
|
2874
|
-
if (!references) continue;
|
|
2875
|
-
inner.push(
|
|
2876
|
-
`${green(`+ add foreign key`)} on (${references.columns.join(
|
|
2877
|
-
", "
|
|
2878
|
-
)}) to ${references.fnOrTable}(${references.foreignColumns.join(", ")})`
|
|
2879
|
-
);
|
|
2880
|
-
}
|
|
2881
|
-
for (const { check } of a.add.constraints) {
|
|
2882
|
-
if (!check) continue;
|
|
2883
|
-
inner.push(
|
|
2884
|
-
`${green(`+ add check`)} ${check.toSQL({ values: [] })}`
|
|
2885
|
-
);
|
|
2886
|
-
}
|
|
2887
|
-
}
|
|
2888
|
-
code.push(
|
|
2889
|
-
`${yellow("~ change table")} ${dbItemName(a, currentSchema)}${inner.length ? ":" : ""}`
|
|
2890
|
-
);
|
|
2891
|
-
if (inner.length) {
|
|
2892
|
-
code.push(inner);
|
|
2893
|
-
}
|
|
2894
|
-
break;
|
|
2895
|
-
}
|
|
2896
|
-
case "schema": {
|
|
2897
|
-
code.push(
|
|
2898
|
-
`${a.action === "create" ? green("+ create schema") : red("- drop schema")} ${a.name}`
|
|
2899
|
-
);
|
|
2900
|
-
break;
|
|
2901
|
-
}
|
|
2902
|
-
case "renameSchema": {
|
|
2903
|
-
code.push(
|
|
2904
|
-
`${yellow("~ rename schema")} ${a.from} ${yellow("=>")} ${a.to}`
|
|
2905
|
-
);
|
|
2906
|
-
break;
|
|
2907
|
-
}
|
|
2908
|
-
case "renameType": {
|
|
2909
|
-
code.push(
|
|
2910
|
-
`${yellow(
|
|
2911
|
-
`~ ${a.fromSchema !== a.toSchema ? a.from !== a.to ? "change schema and rename" : "change schema of" : "rename"} ${a.kind.toLowerCase()}`
|
|
2912
|
-
)} ${dbItemName(
|
|
2913
|
-
{
|
|
2914
|
-
schema: a.fromSchema,
|
|
2915
|
-
name: a.from
|
|
2916
|
-
},
|
|
2917
|
-
currentSchema
|
|
2918
|
-
)} ${yellow("=>")} ${dbItemName(
|
|
2919
|
-
{
|
|
2920
|
-
schema: a.toSchema,
|
|
2921
|
-
name: a.to
|
|
2922
|
-
},
|
|
2923
|
-
currentSchema
|
|
2924
|
-
)}`
|
|
2925
|
-
);
|
|
2926
|
-
break;
|
|
2927
|
-
}
|
|
2928
|
-
case "extension": {
|
|
2929
|
-
code.push(
|
|
2930
|
-
`${a.action === "create" ? green("+ create extension") : red("- drop extension")} ${dbItemName(a, currentSchema)}${a.version ? ` ${pale(a.version)}` : ""}`
|
|
2931
|
-
);
|
|
2932
|
-
break;
|
|
2933
|
-
}
|
|
2934
|
-
case "enum": {
|
|
2935
|
-
code.push(
|
|
2936
|
-
`${a.action === "create" ? green("+ create enum") : red("- drop enum")} ${dbItemName(a, currentSchema)}: (${a.values.join(", ")})`
|
|
2937
|
-
);
|
|
2938
|
-
break;
|
|
2939
|
-
}
|
|
2940
|
-
case "enumValues": {
|
|
2941
|
-
code.push(
|
|
2942
|
-
`${a.action === "add" ? green("+ add values to enum") : red("- remove values from enum")} ${dbItemName(a, currentSchema)}: ${a.values.join(", ")}`
|
|
2943
|
-
);
|
|
2944
|
-
break;
|
|
2945
|
-
}
|
|
2946
|
-
case "changeEnumValues": {
|
|
2947
|
-
if (a.fromValues) {
|
|
2948
|
-
code.push(
|
|
2949
|
-
`${red("- remove values from enum")} ${dbItemName(
|
|
2950
|
-
a,
|
|
2951
|
-
currentSchema
|
|
2952
|
-
)}: ${a.fromValues.join(", ")}`
|
|
2953
|
-
);
|
|
2954
|
-
}
|
|
2955
|
-
if (a.toValues) {
|
|
2956
|
-
code.push(
|
|
2957
|
-
`${green("+ add values to enum")} ${dbItemName(
|
|
2958
|
-
a,
|
|
2959
|
-
currentSchema
|
|
2960
|
-
)}: ${a.toValues.join(", ")}`
|
|
2961
|
-
);
|
|
2962
|
-
}
|
|
2963
|
-
break;
|
|
2964
|
-
}
|
|
2965
|
-
case "domain": {
|
|
2966
|
-
code.push(
|
|
2967
|
-
`${a.action === "create" ? green("+ create domain") : red("- drop domain")} ${dbItemName(a, currentSchema)}`
|
|
2968
|
-
);
|
|
2969
|
-
break;
|
|
2970
|
-
}
|
|
2971
|
-
case "view":
|
|
2972
|
-
case "collation":
|
|
2973
|
-
case "renameEnumValues":
|
|
2974
|
-
case "constraint":
|
|
2975
|
-
break;
|
|
2976
|
-
case "renameTableItem": {
|
|
2977
|
-
code.push(
|
|
2978
|
-
`${yellow(`~ rename ${a.kind.toLowerCase()}`)} on table ${dbItemName(
|
|
2979
|
-
{ schema: a.tableSchema, name: a.tableName },
|
|
2980
|
-
currentSchema
|
|
2981
|
-
)}: ${a.from} ${yellow("=>")} ${a.to}`
|
|
2982
|
-
);
|
|
2983
|
-
break;
|
|
2984
|
-
}
|
|
2985
|
-
case "role":
|
|
2986
|
-
code.push(
|
|
2987
|
-
`${a.action === "create" ? green("+ create role") : red("- drop role")} ${a.name}`
|
|
2988
|
-
);
|
|
2989
|
-
break;
|
|
2990
|
-
case "renameRole":
|
|
2991
|
-
case "changeRole": {
|
|
2992
|
-
if (a.type === "renameRole") {
|
|
2993
|
-
code.push(
|
|
2994
|
-
`${yellow("~ rename role")} ${a.from} ${yellow("=>")} ${a.to}`
|
|
2995
|
-
);
|
|
2996
|
-
} else {
|
|
2997
|
-
code.push(`${yellow("~ change role")} ${a.name}`);
|
|
2998
|
-
}
|
|
2999
|
-
break;
|
|
3000
|
-
}
|
|
3001
|
-
case "defaultPrivilege": {
|
|
3002
|
-
const mapPrivilege = (p) => p === "ALL" ? "ALL PRIVILEGES" : p;
|
|
3003
|
-
const schema = a.schema ? ` in schema ${internal.colors.pale(a.schema)}` : ` ${internal.colors.pale("in all schemas")}`;
|
|
3004
|
-
const parts = [];
|
|
3005
|
-
if (a.grant) {
|
|
3006
|
-
for (const [objType, config2] of Object.entries(a.grant)) {
|
|
3007
|
-
if (!config2) continue;
|
|
3008
|
-
const type = objType.replace(/([A-Z])/g, " $1").toLowerCase();
|
|
3009
|
-
if (config2.privileges?.length) {
|
|
3010
|
-
parts.push(
|
|
3011
|
-
`${green("+ grant default privileges")} ${config2.privileges.map(mapPrivilege).join(", ")} on ${type} to ${a.grantee}${schema}`
|
|
3012
|
-
);
|
|
3013
|
-
}
|
|
3014
|
-
if (config2.grantablePrivileges?.length) {
|
|
3015
|
-
parts.push(
|
|
3016
|
-
`${green(
|
|
3017
|
-
"+ grant default privileges"
|
|
3018
|
-
)} ${config2.grantablePrivileges.map(mapPrivilege).join(", ")} on ${type} with grant option to ${a.grantee}${schema}`
|
|
3019
|
-
);
|
|
3020
|
-
}
|
|
3021
|
-
}
|
|
3022
|
-
}
|
|
3023
|
-
if (a.revoke) {
|
|
3024
|
-
for (const [objType, config2] of Object.entries(a.revoke)) {
|
|
3025
|
-
if (!config2) continue;
|
|
3026
|
-
const type = objType.replace(/([A-Z])/g, " $1").toLowerCase();
|
|
3027
|
-
if (config2.privileges?.length) {
|
|
3028
|
-
parts.push(
|
|
3029
|
-
`${red("- revoke default privileges")} ${config2.privileges.map(mapPrivilege).join(", ")} on ${type} from ${a.grantee}${schema}`
|
|
3030
|
-
);
|
|
3031
|
-
}
|
|
3032
|
-
if (config2.grantablePrivileges?.length) {
|
|
3033
|
-
parts.push(
|
|
3034
|
-
`${red(
|
|
3035
|
-
"- revoke default privileges"
|
|
3036
|
-
)} ${config2.grantablePrivileges.map(mapPrivilege).join(", ")} on ${type} with grant option from ${a.grantee}${schema}`
|
|
3037
|
-
);
|
|
3038
|
-
}
|
|
3039
|
-
}
|
|
3040
|
-
}
|
|
3041
|
-
if (parts.length) {
|
|
3042
|
-
code.push(parts.join("\n"));
|
|
3043
|
-
}
|
|
3044
|
-
break;
|
|
3045
|
-
}
|
|
3046
|
-
default:
|
|
3047
|
-
internal.exhaustive(a);
|
|
3048
|
-
}
|
|
3049
|
-
}
|
|
3050
|
-
const result = internal.codeToString(code, "", " ");
|
|
3051
|
-
config.logger.log(result);
|
|
3052
|
-
};
|
|
3053
|
-
const dbItemName = ({ schema, name }, currentSchema) => {
|
|
3054
|
-
return schema && schema !== currentSchema ? `${schema}.${name}` : name;
|
|
3055
|
-
};
|
|
3056
|
-
|
|
3057
|
-
class AbortSignal extends Error {
|
|
3058
|
-
}
|
|
3059
|
-
const generate = async (adapters, config, args, afterPull) => {
|
|
3060
|
-
let { dbPath } = config;
|
|
3061
|
-
if (!dbPath || !config.baseTable) throw invalidConfig(config);
|
|
3062
|
-
if (!adapters.length) throw new Error(`Database options must not be empty`);
|
|
3063
|
-
if (!dbPath.endsWith(".ts")) dbPath += ".ts";
|
|
3064
|
-
let migrationName = args[0] ?? "generated";
|
|
3065
|
-
let up;
|
|
3066
|
-
if (migrationName === "up") {
|
|
3067
|
-
up = true;
|
|
3068
|
-
migrationName = "generated";
|
|
3069
|
-
} else {
|
|
3070
|
-
up = args[1] === "up";
|
|
3071
|
-
}
|
|
3072
|
-
if (afterPull) {
|
|
3073
|
-
adapters = [afterPull.adapter];
|
|
3074
|
-
}
|
|
3075
|
-
const db = await getDbFromConfig(config, dbPath);
|
|
3076
|
-
const { columnTypes, internal: internal$1 } = db.$qb;
|
|
3077
|
-
const rolesDbStructureParam = internal$1.roles ? internal$1.managedRolesSql ? { whereSql: internal$1.managedRolesSql } : internal.emptyObject : void 0;
|
|
3078
|
-
const { dbStructure } = await migrateAndPullStructures(
|
|
3079
|
-
adapters,
|
|
3080
|
-
config,
|
|
3081
|
-
rolesDbStructureParam,
|
|
3082
|
-
internal$1.roles ? { loadDefaultPrivileges: true } : void 0,
|
|
3083
|
-
afterPull
|
|
3084
|
-
);
|
|
3085
|
-
const [adapter] = adapters;
|
|
3086
|
-
const adapterSchema = adapter.getSchema();
|
|
3087
|
-
const currentSchema = (typeof adapterSchema === "function" ? adapterSchema() : adapterSchema) ?? "public";
|
|
3088
|
-
const codeItems = await getActualItems(
|
|
3089
|
-
db,
|
|
3090
|
-
currentSchema,
|
|
3091
|
-
internal$1,
|
|
3092
|
-
columnTypes
|
|
3093
|
-
);
|
|
3094
|
-
const structureToAstCtx = rakeDb.makeStructureToAstCtx(config, currentSchema);
|
|
3095
|
-
const generateMigrationParams = {
|
|
3096
|
-
structureToAstCtx,
|
|
3097
|
-
codeItems,
|
|
3098
|
-
currentSchema,
|
|
3099
|
-
internal: internal$1
|
|
3100
|
-
};
|
|
3101
|
-
const ast = [];
|
|
3102
|
-
let migrationCode;
|
|
3103
|
-
try {
|
|
3104
|
-
migrationCode = await composeMigration(
|
|
3105
|
-
adapter,
|
|
3106
|
-
config,
|
|
3107
|
-
ast,
|
|
3108
|
-
dbStructure,
|
|
3109
|
-
generateMigrationParams
|
|
3110
|
-
);
|
|
3111
|
-
} catch (err) {
|
|
3112
|
-
if (err instanceof AbortSignal) {
|
|
3113
|
-
await closeAdapters(adapters);
|
|
3114
|
-
return;
|
|
3115
|
-
}
|
|
3116
|
-
throw err;
|
|
3117
|
-
}
|
|
3118
|
-
if (migrationCode && !afterPull) {
|
|
3119
|
-
const result = await verifyMigration(
|
|
3120
|
-
adapter,
|
|
3121
|
-
config,
|
|
3122
|
-
migrationCode,
|
|
3123
|
-
generateMigrationParams,
|
|
3124
|
-
rolesDbStructureParam,
|
|
3125
|
-
internal$1.roles ? { loadDefaultPrivileges: true } : void 0
|
|
3126
|
-
);
|
|
3127
|
-
if (result !== void 0) {
|
|
3128
|
-
throw new Error(
|
|
3129
|
-
`Failed to verify generated migration: some of database changes were not applied properly. This is a bug, please open an issue, attach the following migration code:
|
|
3130
|
-
${migrationCode}${result === false ? "" : `
|
|
3131
|
-
After applying:
|
|
3132
|
-
${result}`}`
|
|
3133
|
-
);
|
|
3134
|
-
}
|
|
3135
|
-
}
|
|
3136
|
-
const { logger } = config;
|
|
3137
|
-
if ((!up || !migrationCode) && !afterPull) await closeAdapters(adapters);
|
|
3138
|
-
if (!migrationCode) {
|
|
3139
|
-
logger?.log("No changes were detected");
|
|
3140
|
-
return;
|
|
3141
|
-
}
|
|
3142
|
-
const version = afterPull?.version ?? await rakeDb.makeFileVersion({}, config);
|
|
3143
|
-
const delayLog = [];
|
|
3144
|
-
await rakeDb.writeMigrationFile(
|
|
3145
|
-
{
|
|
3146
|
-
...config,
|
|
3147
|
-
logger: logger ? { ...logger, log: (msg) => delayLog.push(msg) } : logger
|
|
3148
|
-
},
|
|
3149
|
-
version,
|
|
3150
|
-
migrationName,
|
|
3151
|
-
migrationCode
|
|
3152
|
-
);
|
|
3153
|
-
report(ast, config, currentSchema);
|
|
3154
|
-
if (logger) {
|
|
3155
|
-
for (const msg of delayLog) {
|
|
3156
|
-
logger.log(`
|
|
3157
|
-
${msg}`);
|
|
3158
|
-
}
|
|
3159
|
-
}
|
|
3160
|
-
if (up) {
|
|
3161
|
-
for (const adapter2 of adapters) {
|
|
3162
|
-
await rakeDb.migrateAndClose(adapter2, config);
|
|
3163
|
-
}
|
|
3164
|
-
} else if (!afterPull) {
|
|
3165
|
-
await closeAdapters(adapters);
|
|
3166
|
-
}
|
|
3167
|
-
};
|
|
3168
|
-
const invalidConfig = (config) => new Error(
|
|
3169
|
-
`\`${config.dbPath ? "baseTable" : "dbPath"}\` setting must be set in the migrations config for the generator to work`
|
|
3170
|
-
);
|
|
3171
|
-
const getDbFromConfig = async (config, dbPath) => {
|
|
3172
|
-
const module = await config.import(
|
|
3173
|
-
url.pathToFileURL(path.resolve(config.basePath, dbPath)).toString()
|
|
3174
|
-
);
|
|
3175
|
-
const db = module[config.dbExportedAs ?? "db"];
|
|
3176
|
-
if (!db?.$qb) {
|
|
3177
|
-
throw new Error(
|
|
3178
|
-
`Unable to import OrchidORM instance as ${config.dbExportedAs ?? "db"} from ${config.dbPath}`
|
|
3179
|
-
);
|
|
3180
|
-
}
|
|
3181
|
-
return db;
|
|
3182
|
-
};
|
|
3183
|
-
const migrateAndPullStructures = async (adapters, config, roles, defaultPrivileges, afterPull) => {
|
|
3184
|
-
if (afterPull) {
|
|
3185
|
-
const version = await rakeDb.getDbVersion(adapters[0]);
|
|
3186
|
-
return {
|
|
3187
|
-
dbStructure: {
|
|
3188
|
-
version,
|
|
3189
|
-
schemas: [],
|
|
3190
|
-
tables: [],
|
|
3191
|
-
views: [],
|
|
3192
|
-
indexes: [],
|
|
3193
|
-
excludes: [],
|
|
3194
|
-
constraints: [],
|
|
3195
|
-
triggers: [],
|
|
3196
|
-
extensions: [],
|
|
3197
|
-
enums: [],
|
|
3198
|
-
domains: [],
|
|
3199
|
-
collations: []
|
|
3200
|
-
}
|
|
3201
|
-
};
|
|
3202
|
-
}
|
|
3203
|
-
for (const adapter of adapters) {
|
|
3204
|
-
await rakeDb.migrate(adapter, config);
|
|
3205
|
-
}
|
|
3206
|
-
const dbStructures = await Promise.all(
|
|
3207
|
-
adapters.map(
|
|
3208
|
-
(adapter) => rakeDb.introspectDbSchema(adapter, {
|
|
3209
|
-
roles,
|
|
3210
|
-
loadDefaultPrivileges: defaultPrivileges?.loadDefaultPrivileges
|
|
3211
|
-
})
|
|
3212
|
-
)
|
|
3213
|
-
);
|
|
3214
|
-
const dbStructure = dbStructures[0];
|
|
3215
|
-
for (let i = 1; i < dbStructures.length; i++) {
|
|
3216
|
-
compareDbStructures(dbStructure, dbStructures[i], i);
|
|
3217
|
-
}
|
|
3218
|
-
return { dbStructure };
|
|
3219
|
-
};
|
|
3220
|
-
const compareDbStructures = (a, b, i, path2) => {
|
|
3221
|
-
let err;
|
|
3222
|
-
if (typeof a !== typeof b) {
|
|
3223
|
-
err = true;
|
|
3224
|
-
}
|
|
3225
|
-
if (!a || typeof a !== "object") {
|
|
3226
|
-
if (a !== b) {
|
|
3227
|
-
err = true;
|
|
3228
|
-
}
|
|
3229
|
-
} else {
|
|
3230
|
-
if (Array.isArray(a)) {
|
|
3231
|
-
for (let n = 0, len = a.length; n < len; n++) {
|
|
3232
|
-
compareDbStructures(
|
|
3233
|
-
a[n],
|
|
3234
|
-
b[n],
|
|
3235
|
-
i,
|
|
3236
|
-
path2 ? `${path2}[${n}]` : String(n)
|
|
3237
|
-
);
|
|
3238
|
-
}
|
|
3239
|
-
} else {
|
|
3240
|
-
for (const key in a) {
|
|
3241
|
-
compareDbStructures(
|
|
3242
|
-
a[key],
|
|
3243
|
-
b[key],
|
|
3244
|
-
i,
|
|
3245
|
-
path2 ? `${path2}.${key}` : key
|
|
3246
|
-
);
|
|
3247
|
-
}
|
|
3248
|
-
}
|
|
3249
|
-
}
|
|
3250
|
-
if (err) {
|
|
3251
|
-
throw new Error(`${path2} in the db 0 does not match db ${i}`);
|
|
3252
|
-
}
|
|
3253
|
-
};
|
|
3254
|
-
const getActualItems = async (db, currentSchema, internal$1, columnTypes) => {
|
|
3255
|
-
const tableNames = /* @__PURE__ */ new Set();
|
|
3256
|
-
const habtmTables = /* @__PURE__ */ new Map();
|
|
3257
|
-
const codeItems = {
|
|
3258
|
-
schemas: /* @__PURE__ */ new Set(void 0),
|
|
3259
|
-
enums: /* @__PURE__ */ new Map(),
|
|
3260
|
-
tables: [],
|
|
3261
|
-
domains: []
|
|
3262
|
-
};
|
|
3263
|
-
codeItems.schemas.add(currentSchema);
|
|
3264
|
-
const domains = /* @__PURE__ */ new Map();
|
|
3265
|
-
for (const key in db) {
|
|
3266
|
-
if (key[0] === "$") continue;
|
|
3267
|
-
const table = db[key];
|
|
3268
|
-
if (!table.table) {
|
|
3269
|
-
throw new Error(`Table ${key} is missing table property`);
|
|
3270
|
-
}
|
|
3271
|
-
const schema = internal.getQuerySchema(table);
|
|
3272
|
-
const name = rakeDb.concatSchemaAndName({ schema, name: table.table });
|
|
3273
|
-
if (tableNames.has(name)) {
|
|
3274
|
-
throw new Error(`Table ${name} is defined more than once`);
|
|
3275
|
-
}
|
|
3276
|
-
tableNames.add(name);
|
|
3277
|
-
if (schema) codeItems.schemas.add(schema);
|
|
3278
|
-
codeItems.tables.push({
|
|
3279
|
-
table: table.table,
|
|
3280
|
-
shape: table.shape,
|
|
3281
|
-
internal: table.internal,
|
|
3282
|
-
q: {
|
|
3283
|
-
schema: internal.getQuerySchema(table)
|
|
3284
|
-
}
|
|
3285
|
-
});
|
|
3286
|
-
for (const key2 in table.relations) {
|
|
3287
|
-
const column = table.shape[key2];
|
|
3288
|
-
if (column && "joinTable" in column) {
|
|
3289
|
-
processHasAndBelongsToManyColumn(column, habtmTables, codeItems);
|
|
3290
|
-
}
|
|
3291
|
-
}
|
|
3292
|
-
for (const key2 in table.shape) {
|
|
3293
|
-
const column = table.shape[key2];
|
|
3294
|
-
if (column.data.computed) {
|
|
3295
|
-
delete table.shape[key2];
|
|
3296
|
-
} else if (column instanceof internal.DomainColumn) {
|
|
3297
|
-
const [schemaName = currentSchema, name2] = rakeDb.getSchemaAndTableFromName(
|
|
3298
|
-
currentSchema,
|
|
3299
|
-
column.dataType
|
|
3300
|
-
);
|
|
3301
|
-
domains.set(column.dataType, {
|
|
3302
|
-
schemaName,
|
|
3303
|
-
name: name2,
|
|
3304
|
-
column: column.data.as ?? new internal.UnknownColumn(internal.defaultSchemaConfig)
|
|
3305
|
-
});
|
|
3306
|
-
} else {
|
|
3307
|
-
const en = column.dataType === "enum" ? column : column instanceof internal.ArrayColumn && column.data.item.dataType === "enum" ? column.data.item : void 0;
|
|
3308
|
-
if (en) {
|
|
3309
|
-
processEnumColumn(en, currentSchema, codeItems);
|
|
3310
|
-
}
|
|
3311
|
-
}
|
|
3312
|
-
}
|
|
3313
|
-
}
|
|
3314
|
-
if (internal$1.extensions) {
|
|
3315
|
-
for (const extension of internal$1.extensions) {
|
|
3316
|
-
const [schema] = rakeDb.getSchemaAndTableFromName(currentSchema, extension.name);
|
|
3317
|
-
if (schema) codeItems.schemas.add(schema);
|
|
3318
|
-
}
|
|
3319
|
-
}
|
|
3320
|
-
if (internal$1.domains) {
|
|
3321
|
-
for (const key in internal$1.domains) {
|
|
3322
|
-
const [schemaName = currentSchema, name] = rakeDb.getSchemaAndTableFromName(
|
|
3323
|
-
currentSchema,
|
|
3324
|
-
key
|
|
3325
|
-
);
|
|
3326
|
-
const column = internal$1.domains[key](columnTypes);
|
|
3327
|
-
domains.set(key, {
|
|
3328
|
-
schemaName,
|
|
3329
|
-
name,
|
|
3330
|
-
column
|
|
3331
|
-
});
|
|
3332
|
-
}
|
|
3333
|
-
}
|
|
3334
|
-
for (const domain of domains.values()) {
|
|
3335
|
-
codeItems.schemas.add(domain.schemaName);
|
|
3336
|
-
codeItems.domains.push(domain);
|
|
3337
|
-
}
|
|
3338
|
-
if (internal$1.roles) {
|
|
3339
|
-
for (const role of internal$1.roles) {
|
|
3340
|
-
if (role.defaultPrivileges) {
|
|
3341
|
-
for (const privilege of role.defaultPrivileges) {
|
|
3342
|
-
if (privilege.schema) codeItems.schemas.add(privilege.schema);
|
|
3343
|
-
}
|
|
3344
|
-
}
|
|
3345
|
-
}
|
|
3346
|
-
}
|
|
3347
|
-
return codeItems;
|
|
3348
|
-
};
|
|
3349
|
-
const processEnumColumn = (column, currentSchema, codeItems) => {
|
|
3350
|
-
const { enumName, options } = column;
|
|
3351
|
-
const [schema, name] = rakeDb.getSchemaAndTableFromName(currentSchema, enumName);
|
|
3352
|
-
const enumSchema = schema ?? currentSchema;
|
|
3353
|
-
codeItems.enums.set(`${enumSchema}.${name}`, {
|
|
3354
|
-
schema: enumSchema,
|
|
3355
|
-
name,
|
|
3356
|
-
values: options
|
|
3357
|
-
});
|
|
3358
|
-
if (schema) codeItems.schemas.add(schema);
|
|
3359
|
-
};
|
|
3360
|
-
const processHasAndBelongsToManyColumn = (column, habtmTables, codeItems) => {
|
|
3361
|
-
const q = column.joinTable;
|
|
3362
|
-
const prev = habtmTables.get(q.table);
|
|
3363
|
-
if (prev) {
|
|
3364
|
-
for (const key in q.shape) {
|
|
3365
|
-
if (q.shape[key].dataType !== prev.shape[key]?.dataType) {
|
|
3366
|
-
throw new Error(
|
|
3367
|
-
`Column ${key} in ${q.table} in hasAndBelongsToMany relation does not match with the relation on the other side`
|
|
3368
|
-
);
|
|
3369
|
-
}
|
|
3370
|
-
}
|
|
3371
|
-
return;
|
|
3372
|
-
}
|
|
3373
|
-
habtmTables.set(q.table, q);
|
|
3374
|
-
const joinTable = Object.create(q);
|
|
3375
|
-
const shape = {};
|
|
3376
|
-
for (const key in joinTable.shape) {
|
|
3377
|
-
const column2 = Object.create(joinTable.shape[key]);
|
|
3378
|
-
column2.data = {
|
|
3379
|
-
...column2.data,
|
|
3380
|
-
name: column2.data.name ?? key,
|
|
3381
|
-
identity: void 0,
|
|
3382
|
-
primaryKey: void 0,
|
|
3383
|
-
default: void 0
|
|
3384
|
-
};
|
|
3385
|
-
shape[internal.toCamelCase(key)] = column2;
|
|
3386
|
-
}
|
|
3387
|
-
joinTable.shape = shape;
|
|
3388
|
-
joinTable.internal = {
|
|
3389
|
-
...joinTable.internal,
|
|
3390
|
-
tableData: {
|
|
3391
|
-
...joinTable.internal.tableData,
|
|
3392
|
-
primaryKey: {
|
|
3393
|
-
columns: Object.keys(shape)
|
|
3394
|
-
}
|
|
3395
|
-
},
|
|
3396
|
-
noPrimaryKey: false
|
|
3397
|
-
};
|
|
3398
|
-
codeItems.tables.push(joinTable);
|
|
3399
|
-
return;
|
|
3400
|
-
};
|
|
3401
|
-
const closeAdapters = (adapters) => {
|
|
3402
|
-
return Promise.all(adapters.map((x) => x.close()));
|
|
3403
|
-
};
|
|
3404
|
-
|
|
3405
|
-
const getTableInfosAndFKeys = (asts, config) => {
|
|
3406
|
-
var _a;
|
|
3407
|
-
const generateTableTo = config.generateTableTo ?? ((name) => `./tables/${name}.table.ts`);
|
|
3408
|
-
const tableInfos = {};
|
|
3409
|
-
const fkeys = {};
|
|
3410
|
-
for (const ast of asts) {
|
|
3411
|
-
if (ast.type === "table") {
|
|
3412
|
-
const tableKey = internal.toCamelCase(ast.name);
|
|
3413
|
-
const dbTableName = ast.schema ? `${ast.schema}.${ast.name}` : ast.name;
|
|
3414
|
-
let tablePath = path.resolve(config.basePath, generateTableTo(tableKey));
|
|
3415
|
-
if (!tablePath.endsWith(".ts")) tablePath += ".ts";
|
|
3416
|
-
const name = internal.toPascalCase(ast.name);
|
|
3417
|
-
const info = {
|
|
3418
|
-
dbTableName,
|
|
3419
|
-
key: tableKey,
|
|
3420
|
-
path: tablePath,
|
|
3421
|
-
name,
|
|
3422
|
-
className: `${name}Table`
|
|
3423
|
-
};
|
|
3424
|
-
tableInfos[dbTableName] = info;
|
|
3425
|
-
if (ast.constraints) {
|
|
3426
|
-
for (const { references } of ast.constraints) {
|
|
3427
|
-
if (!references) continue;
|
|
3428
|
-
(fkeys[_a = references.fnOrTable] ?? (fkeys[_a] = [])).push({
|
|
3429
|
-
table: info,
|
|
3430
|
-
references
|
|
3431
|
-
});
|
|
3432
|
-
}
|
|
3433
|
-
}
|
|
3434
|
-
}
|
|
3435
|
-
}
|
|
3436
|
-
return { tableInfos, fkeys };
|
|
3437
|
-
};
|
|
3438
|
-
const appCodeGenTable = (tableInfos, fkeys, ast, baseTablePath, baseTableExportedAs, currentSchema) => {
|
|
3439
|
-
const tableInfo = tableInfos[ast.schema ? `${ast.schema}.${ast.name}` : ast.name];
|
|
3440
|
-
const imports = {
|
|
3441
|
-
"orchid-orm": "Selectable, Insertable, Updatable",
|
|
3442
|
-
[internal.getImportPath(tableInfo.path, baseTablePath)]: baseTableExportedAs
|
|
3443
|
-
};
|
|
3444
|
-
const props = [];
|
|
3445
|
-
if (ast.schema) {
|
|
3446
|
-
props.push(`schema = ${internal.singleQuote(ast.schema)};`);
|
|
3447
|
-
}
|
|
3448
|
-
props.push(`readonly table = ${internal.singleQuote(ast.name)};`);
|
|
3449
|
-
if (ast.comment) {
|
|
3450
|
-
props.push(`comment = ${internal.singleQuote(ast.comment)};`);
|
|
3451
|
-
}
|
|
3452
|
-
if (ast.noPrimaryKey === "ignore") {
|
|
3453
|
-
props.push("noPrimaryKey = true;");
|
|
3454
|
-
}
|
|
3455
|
-
const hasTableData = Boolean(
|
|
3456
|
-
ast.primaryKey || ast.indexes?.length || ast.excludes?.length || ast.constraints?.length
|
|
3457
|
-
);
|
|
3458
|
-
const shapeCode = internal.columnsShapeToCode(
|
|
3459
|
-
{ t: "t", table: ast.name, currentSchema },
|
|
3460
|
-
ast.shape
|
|
3461
|
-
);
|
|
3462
|
-
props.push(
|
|
3463
|
-
`columns = this.setColumns(${hasTableData ? "\n " : ""}(t) => ({`,
|
|
3464
|
-
hasTableData ? [shapeCode] : shapeCode,
|
|
3465
|
-
hasTableData ? " })," : "}));"
|
|
3466
|
-
);
|
|
3467
|
-
if (hasTableData) {
|
|
3468
|
-
props.push(internal.pushTableDataCode([], ast), ");");
|
|
3469
|
-
}
|
|
3470
|
-
const relations = [];
|
|
3471
|
-
const fullTableName = ast.schema ? `${ast.schema}.${ast.name}` : ast.name;
|
|
3472
|
-
const belongsTo = fkeys[fullTableName];
|
|
3473
|
-
if (belongsTo) {
|
|
3474
|
-
for (const { table, references } of belongsTo) {
|
|
3475
|
-
imports[internal.getImportPath(tableInfo.path, table.path)] = table.className;
|
|
3476
|
-
relations.push(
|
|
3477
|
-
`${table.key}: this.belongsTo(() => ${table.className}, {`,
|
|
3478
|
-
[
|
|
3479
|
-
`columns: [${references.foreignColumns.map(internal.singleQuote).join(", ")}],`,
|
|
3480
|
-
`references: [${references.columns.map(internal.singleQuote).join(", ")}],`
|
|
3481
|
-
],
|
|
3482
|
-
"}),"
|
|
3483
|
-
);
|
|
3484
|
-
}
|
|
3485
|
-
}
|
|
3486
|
-
if (ast.constraints) {
|
|
3487
|
-
for (const { references } of ast.constraints) {
|
|
3488
|
-
if (!references) continue;
|
|
3489
|
-
const table = tableInfos[references.fnOrTable];
|
|
3490
|
-
imports[internal.getImportPath(tableInfo.path, table.path)] = table.className;
|
|
3491
|
-
relations.push(
|
|
3492
|
-
`${table.key}: this.hasMany(() => ${table.className}, {`,
|
|
3493
|
-
[
|
|
3494
|
-
`columns: [${references.columns.map(internal.singleQuote).join(", ")}],`,
|
|
3495
|
-
`references: [${references.foreignColumns.map(internal.singleQuote).join(", ")}],`
|
|
3496
|
-
],
|
|
3497
|
-
"}),"
|
|
3498
|
-
);
|
|
3499
|
-
}
|
|
3500
|
-
}
|
|
3501
|
-
if (relations.length) {
|
|
3502
|
-
props.push("", "relations = {", relations, "};");
|
|
3503
|
-
}
|
|
3504
|
-
const importsCode = importsToCode(imports);
|
|
3505
|
-
const { name, className } = tableInfo;
|
|
3506
|
-
const code = [
|
|
3507
|
-
`export type ${name} = Selectable<${className}>;
|
|
3508
|
-
export type ${name}New = Insertable<${className}>;
|
|
3509
|
-
export type ${name}Update = Updatable<${className}>;
|
|
3510
|
-
|
|
3511
|
-
export class ${className} extends ${baseTableExportedAs} {`,
|
|
3512
|
-
props,
|
|
3513
|
-
"}\n"
|
|
3514
|
-
];
|
|
3515
|
-
return {
|
|
3516
|
-
...tableInfo,
|
|
3517
|
-
content: importsCode + "\n\n" + internal.codeToString(code, "", " ")
|
|
3518
|
-
};
|
|
3519
|
-
};
|
|
3520
|
-
function importsToCode(imports) {
|
|
3521
|
-
return Object.entries(imports).map(([from, name]) => `import { ${name} } from '${from}';`).join("\n");
|
|
3522
|
-
}
|
|
3523
|
-
|
|
3524
|
-
const { createSourceFile, ScriptTarget, SyntaxKind } = typescript;
|
|
3525
|
-
const appCodeGenUpdateDbFile = async (dbPath, tables, extensions, domains, currentSchema) => {
|
|
3526
|
-
const content = await fs.readFile(dbPath, "utf-8");
|
|
3527
|
-
const statements = getTsStatements(content);
|
|
3528
|
-
const importName = getOrchidOrmImportName(statements);
|
|
3529
|
-
if (!importName) {
|
|
3530
|
-
throw new Error(`Main file does not contain import of orchid-orm`);
|
|
3531
|
-
}
|
|
3532
|
-
const { config, tablesList } = getOrchidOrmArgs(importName, statements);
|
|
3533
|
-
const changes = [];
|
|
3534
|
-
let replacedConfig;
|
|
3535
|
-
if (extensions.length || domains.length) {
|
|
3536
|
-
let code = content.slice(config.pos, config.end).trim();
|
|
3537
|
-
if (code[0] !== "{") code = `{...${code}}`;
|
|
3538
|
-
code = "{\n " + code.slice(1, -1).trim();
|
|
3539
|
-
if (!code.endsWith(",")) code += ",";
|
|
3540
|
-
if (extensions.length) {
|
|
3541
|
-
code += `
|
|
3542
|
-
extensions: [${extensions.map(
|
|
3543
|
-
(ext) => ext.version ? `{ ${internal.quoteObjectKey(ext.name, false)}: '${ext.version}' }` : internal.singleQuote(ext.name)
|
|
3544
|
-
).join(", ")}],`;
|
|
3545
|
-
}
|
|
3546
|
-
if (domains.length) {
|
|
3547
|
-
code += `
|
|
3548
|
-
domains: {
|
|
3549
|
-
${domains.sort((a, b) => a.name > b.name ? 1 : -1).map(
|
|
3550
|
-
(ast) => `${internal.quoteObjectKey(
|
|
3551
|
-
ast.schema ? `${ast.schema}.${ast.name}` : ast.name,
|
|
3552
|
-
false
|
|
3553
|
-
)}: (t) => ${ast.baseType.toCode(
|
|
3554
|
-
{ t: "t", table: ast.name, currentSchema },
|
|
3555
|
-
ast.baseType.data.name ?? ""
|
|
3556
|
-
)},`
|
|
3557
|
-
).join("\n ")}
|
|
3558
|
-
},`;
|
|
3559
|
-
}
|
|
3560
|
-
replacedConfig = code + "\n}";
|
|
3561
|
-
}
|
|
3562
|
-
const tablesChanges = makeTablesListChanges(
|
|
3563
|
-
content,
|
|
3564
|
-
statements,
|
|
3565
|
-
tablesList,
|
|
3566
|
-
tables,
|
|
3567
|
-
dbPath
|
|
3568
|
-
);
|
|
3569
|
-
if (tablesChanges) {
|
|
3570
|
-
addChange(
|
|
3571
|
-
content,
|
|
3572
|
-
changes,
|
|
3573
|
-
tablesChanges.imports.pos,
|
|
3574
|
-
tablesChanges.imports.text
|
|
3575
|
-
);
|
|
3576
|
-
}
|
|
3577
|
-
if (replacedConfig) {
|
|
3578
|
-
replaceContent(content, changes, config.pos, config.end, replacedConfig);
|
|
3579
|
-
}
|
|
3580
|
-
if (tablesChanges) {
|
|
3581
|
-
addChange(
|
|
3582
|
-
content,
|
|
3583
|
-
changes,
|
|
3584
|
-
tablesChanges.tablesList.pos,
|
|
3585
|
-
tablesChanges.tablesList.text
|
|
3586
|
-
);
|
|
3587
|
-
}
|
|
3588
|
-
return applyChanges(content, changes);
|
|
3589
|
-
};
|
|
3590
|
-
const getTsStatements = (content) => {
|
|
3591
|
-
return createSourceFile("file.ts", content, ScriptTarget.Latest, true).statements;
|
|
3592
|
-
};
|
|
3593
|
-
const getOrchidOrmImportName = (statements) => {
|
|
3594
|
-
for (const node of statements) {
|
|
3595
|
-
if (node.kind !== SyntaxKind.ImportDeclaration) continue;
|
|
3596
|
-
const imp = node;
|
|
3597
|
-
const source = imp.moduleSpecifier.getText().slice(1, -1);
|
|
3598
|
-
if (source !== "orchid-orm") continue;
|
|
3599
|
-
if (!imp.importClause) continue;
|
|
3600
|
-
const elements = imp.importClause.namedBindings?.elements;
|
|
3601
|
-
for (const element of elements) {
|
|
3602
|
-
if (element.name.escapedText === "orchidORM" || element.propertyName && "escapedText" in element.propertyName && element.propertyName.escapedText === "orchidORM") {
|
|
3603
|
-
return element.name.escapedText.toString();
|
|
3604
|
-
}
|
|
3605
|
-
}
|
|
3606
|
-
}
|
|
3607
|
-
return;
|
|
3608
|
-
};
|
|
3609
|
-
const makeTablesListChanges = (content, statements, object, tables, dbPath) => {
|
|
3610
|
-
const spaces = getTablesListSpaces(content, object);
|
|
3611
|
-
let imports = "";
|
|
3612
|
-
let tablesList = "";
|
|
3613
|
-
const prependComma = object.properties.length && !object.properties.hasTrailingComma;
|
|
3614
|
-
const tablesListNewLine = content.slice(object.properties.end, object.end).includes("\n");
|
|
3615
|
-
const tablesArr = Object.values(tables);
|
|
3616
|
-
for (let i = 0; i < tablesArr.length; i++) {
|
|
3617
|
-
const { path, className, key } = tablesArr[i];
|
|
3618
|
-
const importPath = internal.getImportPath(dbPath, path);
|
|
3619
|
-
imports += `
|
|
3620
|
-
import { ${className} } from '${importPath}';`;
|
|
3621
|
-
tablesList += `${i === 0 && prependComma ? "," : ""}
|
|
3622
|
-
${spaces} ${key}: ${className},`;
|
|
3623
|
-
if (i === tablesArr.length - 1 && !tablesListNewLine) {
|
|
3624
|
-
tablesList += `
|
|
3625
|
-
${spaces}`;
|
|
3626
|
-
}
|
|
3627
|
-
}
|
|
3628
|
-
if (!imports.length) return;
|
|
3629
|
-
let importPos = 0;
|
|
3630
|
-
for (const node of statements) {
|
|
3631
|
-
if (node.kind === SyntaxKind.ImportDeclaration) {
|
|
3632
|
-
importPos = node.end;
|
|
3633
|
-
}
|
|
3634
|
-
}
|
|
3635
|
-
return {
|
|
3636
|
-
imports: { pos: importPos, text: imports },
|
|
3637
|
-
tablesList: { pos: object.properties.end, text: tablesList }
|
|
3638
|
-
};
|
|
3639
|
-
};
|
|
3640
|
-
const getTablesListSpaces = (content, object) => {
|
|
3641
|
-
const lines = content.slice(0, object.end).split("\n");
|
|
3642
|
-
const last = lines[lines.length - 1];
|
|
3643
|
-
return last.match(/^\s+/)?.[0] || "";
|
|
3644
|
-
};
|
|
3645
|
-
const getOrchidOrmArgs = (importName, statements) => {
|
|
3646
|
-
for (const v of statements) {
|
|
3647
|
-
if (v.kind !== SyntaxKind.VariableStatement) continue;
|
|
3648
|
-
for (const node of v.declarationList.declarations) {
|
|
3649
|
-
const call = node.initializer;
|
|
3650
|
-
if (call?.kind !== SyntaxKind.CallExpression) continue;
|
|
3651
|
-
if (call.expression.getText() !== importName) continue;
|
|
3652
|
-
if (call.arguments.length !== 2) {
|
|
3653
|
-
throw new Error(
|
|
3654
|
-
"Invalid number of arguments when initializing orchid orm"
|
|
3655
|
-
);
|
|
3656
|
-
}
|
|
3657
|
-
const object = call.arguments[1];
|
|
3658
|
-
if (object?.kind !== SyntaxKind.ObjectLiteralExpression) {
|
|
3659
|
-
throw new Error(
|
|
3660
|
-
"Second argument of orchidORM must be an object literal"
|
|
3661
|
-
);
|
|
3662
|
-
}
|
|
3663
|
-
return { config: call.arguments[0], tablesList: object };
|
|
3664
|
-
}
|
|
3665
|
-
}
|
|
3666
|
-
throw new Error("List of tables is not found in main file");
|
|
3667
|
-
};
|
|
3668
|
-
const addChange = (content, changes, at, text, end = at) => {
|
|
3669
|
-
if (changes.length === 0) {
|
|
3670
|
-
changes.push([0, at], text, [end, content.length]);
|
|
3671
|
-
} else {
|
|
3672
|
-
const last = changes[changes.length - 1];
|
|
3673
|
-
last[1] = at;
|
|
3674
|
-
changes.push(text, [end, content.length]);
|
|
3675
|
-
}
|
|
3676
|
-
};
|
|
3677
|
-
const replaceContent = (content, changes, from, to, text) => {
|
|
3678
|
-
addChange(content, changes, from, text, to);
|
|
3679
|
-
};
|
|
3680
|
-
const applyChanges = (content, changes) => {
|
|
3681
|
-
return changes.length ? changes.map(
|
|
3682
|
-
(item) => typeof item === "string" ? item : content.slice(item[0], item[1])
|
|
3683
|
-
).join("") : content;
|
|
3684
|
-
};
|
|
3685
|
-
|
|
3686
|
-
const pull = async (adapters, config) => {
|
|
3687
|
-
if (!config.dbPath || !config.baseTable) {
|
|
3688
|
-
throw new Error(
|
|
3689
|
-
`\`${config.dbPath ? "baseTable" : "dbPath"}\` setting must be set in the migrations config for pull command`
|
|
3690
|
-
);
|
|
3691
|
-
}
|
|
3692
|
-
const baseTablePath = config.baseTable.getFilePath();
|
|
3693
|
-
const baseTableExportedAs = config.baseTable.exportAs;
|
|
3694
|
-
const [adapter] = adapters;
|
|
3695
|
-
const adapterSchema = adapter.getSchema();
|
|
3696
|
-
const currentSchema = (typeof adapterSchema === "function" ? adapterSchema() : adapterSchema) ?? "public";
|
|
3697
|
-
const ctx = rakeDb.makeStructureToAstCtx(config, currentSchema);
|
|
3698
|
-
const asts = await rakeDb.structureToAst(ctx, adapter, config);
|
|
3699
|
-
const { tableInfos, fkeys } = getTableInfosAndFKeys(asts, config);
|
|
3700
|
-
const exclusiveWriteOptions = { flag: "wx" };
|
|
3701
|
-
const pendingFileWrites = [];
|
|
3702
|
-
const tables = {};
|
|
3703
|
-
const extensions = [];
|
|
3704
|
-
const domains = [];
|
|
3705
|
-
let firstTable;
|
|
3706
|
-
for (const ast of asts) {
|
|
3707
|
-
switch (ast.type) {
|
|
3708
|
-
case "table": {
|
|
3709
|
-
const table = appCodeGenTable(
|
|
3710
|
-
tableInfos,
|
|
3711
|
-
fkeys,
|
|
3712
|
-
ast,
|
|
3713
|
-
baseTablePath,
|
|
3714
|
-
baseTableExportedAs,
|
|
3715
|
-
currentSchema
|
|
3716
|
-
);
|
|
3717
|
-
tables[table.key] = table;
|
|
3718
|
-
if (!firstTable) firstTable = table;
|
|
3719
|
-
pendingFileWrites.push([
|
|
3720
|
-
table.path,
|
|
3721
|
-
table.content,
|
|
3722
|
-
exclusiveWriteOptions
|
|
3723
|
-
]);
|
|
3724
|
-
break;
|
|
3725
|
-
}
|
|
3726
|
-
case "extension": {
|
|
3727
|
-
extensions.push({
|
|
3728
|
-
name: ast.schema ? `${ast.schema}.${ast.name}` : ast.name,
|
|
3729
|
-
version: ast.version
|
|
3730
|
-
});
|
|
3731
|
-
break;
|
|
3732
|
-
}
|
|
3733
|
-
case "domain": {
|
|
3734
|
-
domains.push(ast);
|
|
3735
|
-
break;
|
|
3736
|
-
}
|
|
3737
|
-
}
|
|
3738
|
-
}
|
|
3739
|
-
if (!firstTable && !extensions.length && !domains.length) {
|
|
3740
|
-
await adapter.close();
|
|
3741
|
-
return;
|
|
3742
|
-
}
|
|
3743
|
-
let dbPath = path.resolve(config.basePath, config.dbPath);
|
|
3744
|
-
if (!dbPath.endsWith(".ts")) dbPath += ".ts";
|
|
3745
|
-
const content = await appCodeGenUpdateDbFile(
|
|
3746
|
-
dbPath,
|
|
3747
|
-
tables,
|
|
3748
|
-
extensions,
|
|
3749
|
-
domains,
|
|
3750
|
-
currentSchema
|
|
3751
|
-
);
|
|
3752
|
-
if (content) pendingFileWrites.push([dbPath, content]);
|
|
3753
|
-
if (firstTable) {
|
|
3754
|
-
await fs.mkdir(path.dirname(firstTable.path), { recursive: true });
|
|
3755
|
-
}
|
|
3756
|
-
await Promise.all(
|
|
3757
|
-
pendingFileWrites.map(
|
|
3758
|
-
([path2, content2, options]) => fs.writeFile(path2, content2, options).then(() => {
|
|
3759
|
-
config.logger?.log(`Created ${internal.pathToLog(path2)}`);
|
|
3760
|
-
})
|
|
3761
|
-
)
|
|
3762
|
-
);
|
|
3763
|
-
const version = await rakeDb.makeFileVersion({}, config);
|
|
3764
|
-
await generate(adapters, config, ["pull"], { adapter, version });
|
|
3765
|
-
await Promise.all(
|
|
3766
|
-
adapters.map(async (adapter2) => {
|
|
3767
|
-
const silentAdapter = adapter2;
|
|
3768
|
-
silentAdapter.silentArrays = adapter2.arrays;
|
|
3769
|
-
await rakeDb.saveMigratedVersion(silentAdapter, version, "pull.ts", config);
|
|
3770
|
-
await adapter2.close();
|
|
3771
|
-
})
|
|
3772
|
-
);
|
|
3773
|
-
};
|
|
3774
|
-
|
|
3775
|
-
rakeDb.rakeDbCommands.g = rakeDb.rakeDbCommands.generate = {
|
|
3776
|
-
run: generate,
|
|
3777
|
-
help: "gen migration from OrchidORM tables",
|
|
3778
|
-
helpArguments: {
|
|
3779
|
-
"no arguments": '"generated" is a default file name',
|
|
3780
|
-
"migration-name": "set migration file name",
|
|
3781
|
-
up: "auto-apply migration",
|
|
3782
|
-
"migration-name up": "with a custom name and apply it"
|
|
3783
|
-
},
|
|
3784
|
-
helpAfter: "reset"
|
|
3785
|
-
};
|
|
3786
|
-
rakeDb.rakeDbCommands.pull.run = pull;
|
|
3787
|
-
rakeDb.rakeDbCommands.pull.help = "generate ORM tables and a migration for an existing database";
|
|
3788
|
-
|
|
3789
|
-
Object.keys(nodePostgres).forEach(function (k) {
|
|
3790
|
-
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
3791
|
-
enumerable: true,
|
|
3792
|
-
get: function () { return nodePostgres[k]; }
|
|
3793
|
-
});
|
|
3794
|
-
});
|
|
3795
|
-
//# sourceMappingURL=node-postgres.js.map
|
|
1
|
+
(0, require("orchid-orm/migrations").patchRakeDb)();
|
|
2
|
+
var rake_db_node_postgres = require("rake-db/node-postgres");
|
|
3
|
+
Object.keys(rake_db_node_postgres).forEach(function(k) {
|
|
4
|
+
if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
5
|
+
enumerable: true,
|
|
6
|
+
get: function() {
|
|
7
|
+
return rake_db_node_postgres[k];
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=node-postgres.js.map
|