orchid-orm 1.55.1 → 1.56.2

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