orchid-orm 1.72.6 → 1.72.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bun.d.ts +13 -6
- package/dist/bun.js +5 -2
- package/dist/bun.js.map +1 -1
- package/dist/bun.mjs +6 -3
- package/dist/bun.mjs.map +1 -1
- package/dist/index.d.ts +221 -165
- package/dist/index.js +82 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +82 -17
- package/dist/index.mjs.map +1 -1
- package/dist/migrations/index.js +393 -39
- package/dist/migrations/index.js.map +1 -1
- package/dist/migrations/index.mjs +394 -40
- package/dist/migrations/index.mjs.map +1 -1
- package/dist/node-postgres.d.ts +14 -6
- package/dist/node-postgres.js +5 -2
- package/dist/node-postgres.js.map +1 -1
- package/dist/node-postgres.mjs +6 -3
- package/dist/node-postgres.mjs.map +1 -1
- package/dist/postgres-js.d.ts +13 -6
- package/dist/postgres-js.js +5 -2
- package/dist/postgres-js.js.map +1 -1
- package/dist/postgres-js.mjs +6 -3
- package/dist/postgres-js.mjs.map +1 -1
- package/package.json +5 -5
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { astToMigration, concatSchemaAndName, createMigrationInterface, dbColumnToAst, encodeColumnDefault, getConstraintName, getDbStructureTableData, getDbTableColumnsChecks, getDbVersion, getExcludeName, getIndexName, getMigrationsSchemaAndTable, getSchemaAndTableFromName, instantiateDbColumn, introspectDbSchema, makeDomainsMap, makeFileVersion, makeStructureToAstCtx, migrate, migrateAndClose, promptSelect, rakeDbCommands, saveMigratedVersion, structureToAst, tableToAst, writeMigrationFile } from "rake-db";
|
|
2
|
-
import { ArrayColumn, DomainColumn, EnumColumn, RawSql, UnknownColumn, VirtualColumn, addCode, codeToString, colors, columnsShapeToCode, deepCompare, emptyArray, emptyObject, exhaustive, getColumnBaseType, getDriverErrorCode, getFreeSetAlias, getImportPath, getQuerySchema, getSupportedDefaultPrivileges, pathToLog, pluralize, pushTableDataCode, quoteObjectKey, singleQuote, toArray, toCamelCase, toPascalCase, toSnakeCase } from "pqb/internal";
|
|
2
|
+
import { ArrayColumn, DomainColumn, EnumColumn, RawSql, UnknownColumn, VirtualColumn, addCode, codeToString, colors, columnsShapeToCode, deepCompare, emptyArray, emptyObject, exhaustive, getColumnBaseType, getDriverErrorCode, getFreeSetAlias, getImportPath, getQuerySchema, getSupportedDefaultPrivileges, pathToLog, pluralize, pushTableDataCode, quoteObjectKey, raw, singleQuote, toArray, toCamelCase, toPascalCase, toSnakeCase } from "pqb/internal";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import fs from "node:fs/promises";
|
|
6
6
|
import typescript from "typescript";
|
|
7
7
|
export * from "rake-db";
|
|
8
|
-
const compareSqlExpressions = async (
|
|
9
|
-
if (!
|
|
8
|
+
const compareSqlExpressions = async (expressions, adapter) => {
|
|
9
|
+
if (!expressions.length) return;
|
|
10
10
|
let id = 1;
|
|
11
|
-
for (const { source, compare, handle } of
|
|
11
|
+
for (const { source, compare, handle } of expressions) {
|
|
12
12
|
const viewName = `orchidTmpView${id++}`;
|
|
13
13
|
const values = [];
|
|
14
14
|
const combinedQueries = [
|
|
15
|
-
`CREATE TEMPORARY VIEW ${viewName} AS (SELECT ${compare.map(({ inDb, inCode }, i) => `${inDb} AS "*inDb-${i}*", ${inCode.map((s, j) => `(${typeof s === "string" ? s : s.toSQL({ values })}) "*inCode-${i}-${j}*"`).join(", ")}`).join(", ")} FROM ${source})`,
|
|
15
|
+
`CREATE TEMPORARY VIEW ${viewName} AS (SELECT ${compare.map(({ inDb, inCode }, i) => `${inDb} AS "*inDb-${i}*", ${inCode.map((s, j) => `(${typeof s === "string" ? s : s.toSQL({ values })}) "*inCode-${i}-${j}*"`).join(", ")}`).join(", ")}${source ? ` FROM ${source}` : ""})`,
|
|
16
16
|
`SELECT pg_get_viewdef('${viewName}') v`,
|
|
17
17
|
`DROP VIEW ${viewName}`
|
|
18
18
|
].join("; ");
|
|
@@ -27,6 +27,50 @@ const compareSqlExpressions = async (tableExpressions, adapter) => {
|
|
|
27
27
|
handle(compareSqlExpressionResult(result.rows[0].v, compare[0].inCode));
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
|
+
const compareViewsExpressions = async (adapter, compare) => {
|
|
31
|
+
if (!compare.length) return;
|
|
32
|
+
const queries = [];
|
|
33
|
+
let id = 1;
|
|
34
|
+
compare.forEach(({ inCode, ast }, i) => {
|
|
35
|
+
const viewName = `orchidTmpView${id++}ForViews`;
|
|
36
|
+
queries.push({ batch: [
|
|
37
|
+
`SAVEPOINT "${viewName}S"`,
|
|
38
|
+
`CREATE TEMPORARY${"recursive" in ast.options && ast.options.recursive ? " RECURSIVE" : ""} VIEW "${viewName}" (${ast.options.columns?.map((column) => `"${column}"`).join(", ")}) AS (${inCode})`,
|
|
39
|
+
`SELECT ${i} i, '${viewName}' v, pg_get_viewdef('"${viewName}"') sql`,
|
|
40
|
+
`DROP VIEW "${viewName}"`,
|
|
41
|
+
`RELEASE SAVEPOINT "${viewName}S"`
|
|
42
|
+
] });
|
|
43
|
+
});
|
|
44
|
+
let results;
|
|
45
|
+
try {
|
|
46
|
+
const sql = queries.flatMap((q) => q.batch).join(";");
|
|
47
|
+
const query = () => adapter.query(sql, []);
|
|
48
|
+
results = await (adapter.isInTransaction() ? adapter.savepoint("orchidOrmGeneratorViews", query) : query());
|
|
49
|
+
} catch {
|
|
50
|
+
results = (await Promise.all(queries.map(async ({ batch: queries }, i) => {
|
|
51
|
+
const sql = queries.join(";");
|
|
52
|
+
const query = () => adapter.query(sql, []);
|
|
53
|
+
return await (adapter.isInTransaction() ? adapter.savepoint(`orchidOrmGeneratorViews${i}`, query) : query()).catch((err) => {
|
|
54
|
+
if (typeof err === "object") {
|
|
55
|
+
const code = getDriverErrorCode(err);
|
|
56
|
+
if (code === "42703" || code === "42704" || code === "42P01") return [];
|
|
57
|
+
}
|
|
58
|
+
throw err;
|
|
59
|
+
});
|
|
60
|
+
}))).flat();
|
|
61
|
+
}
|
|
62
|
+
const handled = /* @__PURE__ */ new Set();
|
|
63
|
+
for (const result of results) for (const row of result.rows) if ("sql" in row) {
|
|
64
|
+
const { i, v } = row;
|
|
65
|
+
const cmp = compare[i];
|
|
66
|
+
const hasQuote = !!cmp.inDb.match(/\w*WITH RECURSIVE "/);
|
|
67
|
+
if (row.sql.replaceAll(hasQuote ? v : `"${v}"`, cmp.ast.name) !== cmp.inDb) cmp.onNotEqual();
|
|
68
|
+
handled.add(i);
|
|
69
|
+
}
|
|
70
|
+
compare.forEach((cmp, i) => {
|
|
71
|
+
if (!handled.has(i)) cmp.onNotEqual();
|
|
72
|
+
});
|
|
73
|
+
};
|
|
30
74
|
const compareSqlExpressionResult = (resultSql, inCode) => {
|
|
31
75
|
let pos = 7;
|
|
32
76
|
const rgx = /\s+AS\s+"\*(inDb-\d+|inCode-\d+-\d+)\*",?/g;
|
|
@@ -84,8 +128,9 @@ const processSchemas = async (ast, dbStructure, { codeItems: { schemas }, verify
|
|
|
84
128
|
if (i) {
|
|
85
129
|
const from = dropSchemas[i - 1];
|
|
86
130
|
dropSchemas.splice(i - 1, 1);
|
|
131
|
+
const views = dbStructure.views || [];
|
|
87
132
|
renameSchemaInStructures(dbStructure.tables, from, schema);
|
|
88
|
-
renameSchemaInStructures(
|
|
133
|
+
renameSchemaInStructures(views, from, schema);
|
|
89
134
|
renameSchemaInStructures(dbStructure.indexes, from, schema);
|
|
90
135
|
renameSchemaInStructures(dbStructure.excludes, from, schema);
|
|
91
136
|
renameSchemaInStructures(dbStructure.constraints, from, schema);
|
|
@@ -412,7 +457,7 @@ const renameColumn = (columns, from, to) => {
|
|
|
412
457
|
const processDomains = async (ast, adapter, domainsMap, dbStructure, { codeItems: { domains }, structureToAstCtx, currentSchema, internal: { generatorIgnore } }, pendingDbTypes) => {
|
|
413
458
|
const codeDomains = [];
|
|
414
459
|
if (domains) for (const { schemaName, name, column } of domains) codeDomains.push(makeComparableDomain(currentSchema, schemaName, name, column));
|
|
415
|
-
const
|
|
460
|
+
const expressions = [];
|
|
416
461
|
const holdCodeDomains = /* @__PURE__ */ new Set();
|
|
417
462
|
for (const domain of dbStructure.domains) {
|
|
418
463
|
if (generatorIgnore?.schemas?.includes(domain.schemaName) || generatorIgnore?.domains?.includes(domain.name)) continue;
|
|
@@ -438,7 +483,7 @@ const processDomains = async (ast, adapter, domainsMap, dbStructure, { codeItems
|
|
|
438
483
|
pushCompareDefault(compare, domain, found);
|
|
439
484
|
pushCompareChecks(compare, domain, found);
|
|
440
485
|
const source = `(VALUES (NULL::${getColumnDbType(dbColumn, currentSchema)})) t(value)`;
|
|
441
|
-
|
|
486
|
+
expressions.push({
|
|
442
487
|
compare,
|
|
443
488
|
source,
|
|
444
489
|
handle(i) {
|
|
@@ -469,8 +514,8 @@ const processDomains = async (ast, adapter, domainsMap, dbStructure, { codeItems
|
|
|
469
514
|
ast.push(createAst(codeDomain));
|
|
470
515
|
pendingDbTypes.add(codeDomain.schemaName, codeDomain.name);
|
|
471
516
|
}
|
|
472
|
-
if (
|
|
473
|
-
await compareSqlExpressions(
|
|
517
|
+
if (expressions.length) {
|
|
518
|
+
await compareSqlExpressions(expressions, adapter);
|
|
474
519
|
if (holdCodeDomains.size) for (const codeDomain of holdCodeDomains.keys()) {
|
|
475
520
|
ast.push(createAst(codeDomain));
|
|
476
521
|
pendingDbTypes.add(codeDomain.schemaName, codeDomain.name);
|
|
@@ -1668,13 +1713,13 @@ const processTables = async (ast, domainsMap, adapter, dbStructure, config, { st
|
|
|
1668
1713
|
values: [],
|
|
1669
1714
|
expressions: []
|
|
1670
1715
|
};
|
|
1671
|
-
const
|
|
1716
|
+
const expressions = [];
|
|
1672
1717
|
const { changeTables, changeTableSchemas, dropTables, tableShapes } = collectChangeAndDropTables(adapter, config, tables, dbStructure, currentSchema, createTables, generatorIgnore);
|
|
1673
1718
|
applyChangeTableSchemas(changeTableSchemas, currentSchema, ast);
|
|
1674
1719
|
await applyCreateOrRenameTables(dbStructure, createTables, dropTables, changeTables, tableShapes, currentSchema, ast, verifying);
|
|
1675
|
-
await applyChangeTables(adapter, changeTables, structureToAstCtx, dbStructure, domainsMap, ast, currentSchema, config, compareSql,
|
|
1720
|
+
await applyChangeTables(adapter, changeTables, structureToAstCtx, dbStructure, domainsMap, ast, currentSchema, config, compareSql, expressions, verifying, pendingDbTypes);
|
|
1676
1721
|
processForeignKeys(config, ast, changeTables, currentSchema, tableShapes);
|
|
1677
|
-
await Promise.all([applyCompareSql(compareSql, adapter), compareSqlExpressions(
|
|
1722
|
+
await Promise.all([applyCompareSql(compareSql, adapter), compareSqlExpressions(expressions, adapter)]);
|
|
1678
1723
|
await processTableRls(adapter, ast, dbStructure, tables, currentSchema, generatorIgnore);
|
|
1679
1724
|
for (const dbTable of dropTables) ast.push(tableToAst(structureToAstCtx, dbStructure, dbTable, "drop", domainsMap));
|
|
1680
1725
|
};
|
|
@@ -1738,7 +1783,7 @@ const applyChangeTableSchemas = (changeTableSchemas, currentSchema, ast) => {
|
|
|
1738
1783
|
});
|
|
1739
1784
|
}
|
|
1740
1785
|
};
|
|
1741
|
-
const applyChangeTables = async (adapter, changeTables, structureToAstCtx, dbStructure, domainsMap, ast, currentSchema, config, compareSql,
|
|
1786
|
+
const applyChangeTables = async (adapter, changeTables, structureToAstCtx, dbStructure, domainsMap, ast, currentSchema, config, compareSql, expressions, verifying, pendingDbTypes) => {
|
|
1742
1787
|
const compareExpressions = [];
|
|
1743
1788
|
const typeCastsCache = {};
|
|
1744
1789
|
for (const changeTableData of changeTables) {
|
|
@@ -1760,7 +1805,7 @@ const applyChangeTables = async (adapter, changeTables, structureToAstCtx, dbStr
|
|
|
1760
1805
|
}
|
|
1761
1806
|
const tableName = codeTable.table;
|
|
1762
1807
|
const source = `(VALUES (${types.map((x) => `NULL::${x}`).join(", ")})) "${tableName}"(${names.map((x) => `"${x}"`).join(", ")})`;
|
|
1763
|
-
|
|
1808
|
+
expressions.push(...compareExpressions.map((x) => ({
|
|
1764
1809
|
...x,
|
|
1765
1810
|
source
|
|
1766
1811
|
})));
|
|
@@ -2382,13 +2427,16 @@ const normalizeRoleName = (name) => {
|
|
|
2382
2427
|
};
|
|
2383
2428
|
const getSchemaWideTargets = (dbStructure, targetKey, schemas) => {
|
|
2384
2429
|
const selectedSchemas = new Set(schemas);
|
|
2385
|
-
if (targetKey === "tables")
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2430
|
+
if (targetKey === "tables") {
|
|
2431
|
+
const views = dbStructure.views || [];
|
|
2432
|
+
return [...dbStructure.tables.map((table) => ({
|
|
2433
|
+
target: `${table.schemaName}.${table.name}`,
|
|
2434
|
+
outputTarget: `${table.schemaName}.${table.name}`
|
|
2435
|
+
})), ...views.map((view) => ({
|
|
2436
|
+
target: `${view.schemaName}.${view.name}`,
|
|
2437
|
+
outputTarget: `${view.schemaName}.${view.name}`
|
|
2438
|
+
}))].filter((item) => selectedSchemas.has(item.target.split(".")[0]));
|
|
2439
|
+
}
|
|
2392
2440
|
if (targetKey === "sequences" || targetKey === "routines") return getSchemaWideGrantTargets(dbStructure, targetKey, selectedSchemas);
|
|
2393
2441
|
return [];
|
|
2394
2442
|
};
|
|
@@ -2455,6 +2503,264 @@ const addGrantAst = (ast, action, grant, privileges, privilegeKey) => {
|
|
|
2455
2503
|
grantedBy: grant.grantedBy
|
|
2456
2504
|
});
|
|
2457
2505
|
};
|
|
2506
|
+
const processViews = async (ast, adapter, dbStructure, { codeItems: { views: allViews }, currentSchema, internal: { generatorIgnore } }) => {
|
|
2507
|
+
const createViews = [];
|
|
2508
|
+
const changeViews = [];
|
|
2509
|
+
const dropViews = [];
|
|
2510
|
+
const ignoredViews = makeIgnoredViews$1(generatorIgnore, currentSchema);
|
|
2511
|
+
const views = allViews.filter((view) => !view.materialized);
|
|
2512
|
+
for (const codeView of views) {
|
|
2513
|
+
if (isIgnoredView$1(ignoredViews, generatorIgnore, currentSchema, codeView)) continue;
|
|
2514
|
+
const schemaName = codeView.q.schema ?? currentSchema;
|
|
2515
|
+
const dbView = dbStructure.views?.find((view) => view.schemaName === schemaName && view.name === codeView.name);
|
|
2516
|
+
if (dbView) changeViews.push({
|
|
2517
|
+
codeView,
|
|
2518
|
+
dbView
|
|
2519
|
+
});
|
|
2520
|
+
else createViews.push(codeView);
|
|
2521
|
+
}
|
|
2522
|
+
for (const dbView of dbStructure.views ?? []) {
|
|
2523
|
+
if (generatorIgnore?.schemas?.includes(dbView.schemaName) || ignoredViews.some((ignore) => matchesIgnoredView$1(ignore, dbView, currentSchema))) continue;
|
|
2524
|
+
if (!views.find((view) => view.name === dbView.name && (view.q.schema ?? currentSchema) === dbView.schemaName)) dropViews.push(dbView);
|
|
2525
|
+
}
|
|
2526
|
+
for (const codeView of createViews) ast.push(codeViewToAst$1(codeView, currentSchema, "create"));
|
|
2527
|
+
await applyChangeViews$1(ast, adapter, changeViews, currentSchema);
|
|
2528
|
+
for (const dbView of dropViews) ast.push(dbViewToAst$1(dbView, currentSchema, "drop"));
|
|
2529
|
+
};
|
|
2530
|
+
const applyChangeViews$1 = async (ast, adapter, changeViews, currentSchema) => {
|
|
2531
|
+
const compare = [];
|
|
2532
|
+
for (const { codeView, dbView } of changeViews) {
|
|
2533
|
+
const from = dbViewToAst$1(dbView, currentSchema, "drop");
|
|
2534
|
+
const to = codeViewToAst$1(codeView, currentSchema, "create");
|
|
2535
|
+
if (!isViewOptionsEqual(from.options, to.options)) {
|
|
2536
|
+
pushRecreateView$1(ast, from, to);
|
|
2537
|
+
continue;
|
|
2538
|
+
}
|
|
2539
|
+
const codeSql = typeof to.sql === "string" ? to.sql : to.sql.toSQL({ values: [] });
|
|
2540
|
+
compare.push({
|
|
2541
|
+
inDb: dbView.sql,
|
|
2542
|
+
inCode: codeSql,
|
|
2543
|
+
ast: to,
|
|
2544
|
+
onNotEqual() {
|
|
2545
|
+
pushRecreateView$1(ast, from, to);
|
|
2546
|
+
}
|
|
2547
|
+
});
|
|
2548
|
+
}
|
|
2549
|
+
await compareViewsExpressions(adapter, compare);
|
|
2550
|
+
};
|
|
2551
|
+
const pushRecreateView$1 = (ast, from, to) => {
|
|
2552
|
+
ast.push(from, to);
|
|
2553
|
+
};
|
|
2554
|
+
const codeViewToAst$1 = (view, currentSchema, action) => {
|
|
2555
|
+
const schema = view.q.schema ?? currentSchema;
|
|
2556
|
+
const sql = typeof view.viewData.sql === "string" ? raw({ raw: view.viewData.sql }) : view.viewData.sql ?? raw({ raw: "" });
|
|
2557
|
+
return {
|
|
2558
|
+
type: "view",
|
|
2559
|
+
action,
|
|
2560
|
+
schema: schema === currentSchema ? void 0 : schema,
|
|
2561
|
+
name: view.name,
|
|
2562
|
+
shape: view.shape,
|
|
2563
|
+
sql,
|
|
2564
|
+
options: {
|
|
2565
|
+
recursive: view.viewData.recursive,
|
|
2566
|
+
columns: Object.keys(view.shape),
|
|
2567
|
+
checkOption: view.viewData.checkOption,
|
|
2568
|
+
securityBarrier: view.viewData.securityBarrier,
|
|
2569
|
+
securityInvoker: view.viewData.securityInvoker ?? true
|
|
2570
|
+
},
|
|
2571
|
+
deps: []
|
|
2572
|
+
};
|
|
2573
|
+
};
|
|
2574
|
+
const dbViewToAst$1 = (view, currentSchema, action) => {
|
|
2575
|
+
return {
|
|
2576
|
+
type: "view",
|
|
2577
|
+
action,
|
|
2578
|
+
schema: view.schemaName === currentSchema ? void 0 : view.schemaName,
|
|
2579
|
+
name: view.name,
|
|
2580
|
+
shape: {},
|
|
2581
|
+
sql: raw({ raw: view.sql }),
|
|
2582
|
+
options: dbViewOptionsToAst(view),
|
|
2583
|
+
deps: view.deps
|
|
2584
|
+
};
|
|
2585
|
+
};
|
|
2586
|
+
const dbViewOptionsToAst = (view) => {
|
|
2587
|
+
const options = {};
|
|
2588
|
+
options.columns = view.columns.map((column) => column.name);
|
|
2589
|
+
if (view.isRecursive) options.recursive = true;
|
|
2590
|
+
if (view.with) for (const pair of view.with) {
|
|
2591
|
+
const [key, value] = pair.split("=");
|
|
2592
|
+
switch (key) {
|
|
2593
|
+
case "check_option":
|
|
2594
|
+
if (value === "LOCAL" || value === "CASCADED") options.checkOption = value;
|
|
2595
|
+
break;
|
|
2596
|
+
case "security_barrier":
|
|
2597
|
+
options.securityBarrier = value === "true";
|
|
2598
|
+
break;
|
|
2599
|
+
case "security_invoker":
|
|
2600
|
+
options.securityInvoker = value === "true";
|
|
2601
|
+
break;
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
return options;
|
|
2605
|
+
};
|
|
2606
|
+
const isViewOptionsEqual = (from, to) => {
|
|
2607
|
+
return from.recursive === to.recursive && from.checkOption === to.checkOption && (from.securityBarrier ?? false) === (to.securityBarrier ?? false) && (from.securityInvoker ?? false) === (to.securityInvoker ?? false) && isStringArrayEqual$1(from.columns, to.columns);
|
|
2608
|
+
};
|
|
2609
|
+
const isStringArrayEqual$1 = (a, b) => {
|
|
2610
|
+
if ((a?.length ?? 0) !== (b?.length ?? 0)) return false;
|
|
2611
|
+
if (!a?.length && !b?.length) return true;
|
|
2612
|
+
return !!a?.every((item, i) => item === b?.[i]);
|
|
2613
|
+
};
|
|
2614
|
+
const makeIgnoredViews$1 = (generatorIgnore, currentSchema) => {
|
|
2615
|
+
const views = generatorIgnore?.views;
|
|
2616
|
+
if (!views) return [];
|
|
2617
|
+
return views.map((name) => {
|
|
2618
|
+
if (typeof name !== "string") return { name };
|
|
2619
|
+
const parts = name.split(".");
|
|
2620
|
+
return parts.length === 2 ? {
|
|
2621
|
+
schema: parts[0],
|
|
2622
|
+
name: parts[1]
|
|
2623
|
+
} : {
|
|
2624
|
+
schema: currentSchema,
|
|
2625
|
+
name
|
|
2626
|
+
};
|
|
2627
|
+
});
|
|
2628
|
+
};
|
|
2629
|
+
const isIgnoredView$1 = (ignoredViews, generatorIgnore, currentSchema, codeView) => {
|
|
2630
|
+
const schemaName = codeView.q.schema ?? currentSchema;
|
|
2631
|
+
return generatorIgnore?.schemas?.includes(schemaName) === true || ignoredViews.some((ignore) => {
|
|
2632
|
+
return matchesIgnoredView$1(ignore, {
|
|
2633
|
+
schemaName,
|
|
2634
|
+
name: codeView.name
|
|
2635
|
+
}, currentSchema);
|
|
2636
|
+
});
|
|
2637
|
+
};
|
|
2638
|
+
const matchesIgnoredView$1 = (ignore, view, currentSchema) => {
|
|
2639
|
+
return typeof ignore.name === "string" ? ignore.schema === view.schemaName && ignore.name === view.name : ignore.name.test(normalizedViewName$1(view, currentSchema));
|
|
2640
|
+
};
|
|
2641
|
+
const normalizedViewName$1 = (view, currentSchema) => {
|
|
2642
|
+
return view.schemaName === currentSchema ? view.name : `${view.schemaName}.${view.name}`;
|
|
2643
|
+
};
|
|
2644
|
+
const processMaterializedViews = async (ast, adapter, dbStructure, { codeItems: { views: allViews }, currentSchema, internal: { generatorIgnore } }) => {
|
|
2645
|
+
const views = allViews.filter((view) => view.materialized);
|
|
2646
|
+
const createViews = [];
|
|
2647
|
+
const changeViews = [];
|
|
2648
|
+
const dropViews = [];
|
|
2649
|
+
const ignoredViews = makeIgnoredViews(generatorIgnore, currentSchema);
|
|
2650
|
+
for (const codeView of views) {
|
|
2651
|
+
if (isIgnoredView(ignoredViews, generatorIgnore, currentSchema, codeView)) continue;
|
|
2652
|
+
const schemaName = codeView.q.schema ?? currentSchema;
|
|
2653
|
+
const dbView = dbStructure.materializedViews?.find((view) => view.schemaName === schemaName && view.name === codeView.name);
|
|
2654
|
+
if (dbView) changeViews.push({
|
|
2655
|
+
codeView,
|
|
2656
|
+
dbView
|
|
2657
|
+
});
|
|
2658
|
+
else createViews.push(codeView);
|
|
2659
|
+
}
|
|
2660
|
+
for (const dbView of dbStructure.materializedViews ?? []) {
|
|
2661
|
+
if (generatorIgnore?.schemas?.includes(dbView.schemaName) || ignoredViews.some((ignore) => matchesIgnoredView(ignore, dbView, currentSchema))) continue;
|
|
2662
|
+
if (!views.find((view) => view.name === dbView.name && (view.q.schema ?? currentSchema) === dbView.schemaName)) dropViews.push(dbView);
|
|
2663
|
+
}
|
|
2664
|
+
for (const codeView of createViews) ast.push(codeViewToAst(codeView, currentSchema, "create"));
|
|
2665
|
+
await applyChangeViews(ast, adapter, changeViews, currentSchema);
|
|
2666
|
+
for (const dbView of dropViews) ast.push(dbViewToAst(dbView, currentSchema, "drop"));
|
|
2667
|
+
};
|
|
2668
|
+
const applyChangeViews = async (ast, adapter, changeViews, currentSchema) => {
|
|
2669
|
+
const compare = [];
|
|
2670
|
+
for (const { codeView, dbView } of changeViews) {
|
|
2671
|
+
const from = dbViewToAst(dbView, currentSchema, "drop");
|
|
2672
|
+
const to = codeViewToAst(codeView, currentSchema, "create");
|
|
2673
|
+
if (!isMaterializedViewOptionsEqual(from.options, to.options)) {
|
|
2674
|
+
pushRecreateView(ast, from, to);
|
|
2675
|
+
continue;
|
|
2676
|
+
}
|
|
2677
|
+
const codeSql = typeof to.sql === "string" ? to.sql : to.sql.toSQL({ values: [] });
|
|
2678
|
+
compare.push({
|
|
2679
|
+
inDb: dbView.sql,
|
|
2680
|
+
inCode: codeSql,
|
|
2681
|
+
ast: to,
|
|
2682
|
+
onNotEqual() {
|
|
2683
|
+
pushRecreateView(ast, from, to);
|
|
2684
|
+
}
|
|
2685
|
+
});
|
|
2686
|
+
}
|
|
2687
|
+
await compareViewsExpressions(adapter, compare);
|
|
2688
|
+
};
|
|
2689
|
+
const pushRecreateView = (ast, from, to) => {
|
|
2690
|
+
ast.push(from, to);
|
|
2691
|
+
};
|
|
2692
|
+
const codeViewToAst = (view, currentSchema, action) => {
|
|
2693
|
+
const schema = view.q.schema ?? currentSchema;
|
|
2694
|
+
const sql = typeof view.viewData.sql === "string" ? raw({ raw: view.viewData.sql }) : view.viewData.sql ?? raw({ raw: "" });
|
|
2695
|
+
return {
|
|
2696
|
+
type: "materializedView",
|
|
2697
|
+
action,
|
|
2698
|
+
schema: schema === currentSchema ? void 0 : schema,
|
|
2699
|
+
name: view.name,
|
|
2700
|
+
shape: view.shape,
|
|
2701
|
+
sql,
|
|
2702
|
+
options: {
|
|
2703
|
+
columns: Object.keys(view.shape),
|
|
2704
|
+
withData: view.viewData.withData
|
|
2705
|
+
},
|
|
2706
|
+
deps: []
|
|
2707
|
+
};
|
|
2708
|
+
};
|
|
2709
|
+
const dbViewToAst = (view, currentSchema, action) => {
|
|
2710
|
+
return {
|
|
2711
|
+
type: "materializedView",
|
|
2712
|
+
action,
|
|
2713
|
+
schema: view.schemaName === currentSchema ? void 0 : view.schemaName,
|
|
2714
|
+
name: view.name,
|
|
2715
|
+
shape: {},
|
|
2716
|
+
sql: raw({ raw: view.sql }),
|
|
2717
|
+
options: dbMaterializedViewOptionsToAst(view),
|
|
2718
|
+
deps: view.deps
|
|
2719
|
+
};
|
|
2720
|
+
};
|
|
2721
|
+
const dbMaterializedViewOptionsToAst = (view) => {
|
|
2722
|
+
const options = { columns: view.columns.map((column) => column.name) };
|
|
2723
|
+
if (!view.isPopulated) options.withData = false;
|
|
2724
|
+
return options;
|
|
2725
|
+
};
|
|
2726
|
+
const isMaterializedViewOptionsEqual = (from, to) => {
|
|
2727
|
+
return (from.withData ?? true) === (to.withData ?? true) && isStringArrayEqual(from.columns, to.columns);
|
|
2728
|
+
};
|
|
2729
|
+
const isStringArrayEqual = (a, b) => {
|
|
2730
|
+
if ((a?.length ?? 0) !== (b?.length ?? 0)) return false;
|
|
2731
|
+
if (!a?.length && !b?.length) return true;
|
|
2732
|
+
return !!a?.every((item, i) => item === b?.[i]);
|
|
2733
|
+
};
|
|
2734
|
+
const makeIgnoredViews = (generatorIgnore, currentSchema) => {
|
|
2735
|
+
const views = generatorIgnore?.views;
|
|
2736
|
+
if (!views) return [];
|
|
2737
|
+
return views.map((name) => {
|
|
2738
|
+
if (typeof name !== "string") return { name };
|
|
2739
|
+
const parts = name.split(".");
|
|
2740
|
+
return parts.length === 2 ? {
|
|
2741
|
+
schema: parts[0],
|
|
2742
|
+
name: parts[1]
|
|
2743
|
+
} : {
|
|
2744
|
+
schema: currentSchema,
|
|
2745
|
+
name
|
|
2746
|
+
};
|
|
2747
|
+
});
|
|
2748
|
+
};
|
|
2749
|
+
const isIgnoredView = (ignoredViews, generatorIgnore, currentSchema, codeView) => {
|
|
2750
|
+
const schemaName = codeView.q.schema ?? currentSchema;
|
|
2751
|
+
return generatorIgnore?.schemas?.includes(schemaName) === true || ignoredViews.some((ignore) => {
|
|
2752
|
+
return matchesIgnoredView(ignore, {
|
|
2753
|
+
schemaName,
|
|
2754
|
+
name: codeView.name
|
|
2755
|
+
}, currentSchema);
|
|
2756
|
+
});
|
|
2757
|
+
};
|
|
2758
|
+
const matchesIgnoredView = (ignore, view, currentSchema) => {
|
|
2759
|
+
return typeof ignore.name === "string" ? ignore.schema === view.schemaName && ignore.name === view.name : ignore.name.test(normalizedViewName(view, currentSchema));
|
|
2760
|
+
};
|
|
2761
|
+
const normalizedViewName = (view, currentSchema) => {
|
|
2762
|
+
return view.schemaName === currentSchema ? view.name : `${view.schemaName}.${view.name}`;
|
|
2763
|
+
};
|
|
2458
2764
|
/**
|
|
2459
2765
|
* This is needed to compare SQLs of table expressions.
|
|
2460
2766
|
* Need to exclude table columns of pending types, such as enums or domains,
|
|
@@ -2481,6 +2787,8 @@ const composeMigration = async (adapter, config, ast, dbStructure, params) => {
|
|
|
2481
2787
|
await processDomains(ast, adapter, domainsMap, dbStructure, params, pendingDbTypes);
|
|
2482
2788
|
await processEnums(ast, dbStructure, params, pendingDbTypes);
|
|
2483
2789
|
await processTables(ast, domainsMap, adapter, dbStructure, config, params, pendingDbTypes);
|
|
2790
|
+
await processViews(ast, adapter, dbStructure, params);
|
|
2791
|
+
await processMaterializedViews(ast, adapter, dbStructure, params);
|
|
2484
2792
|
return astToMigration(currentSchema, config, ast);
|
|
2485
2793
|
};
|
|
2486
2794
|
const rollbackErr = /* @__PURE__ */ new Error("Rollback");
|
|
@@ -2656,7 +2964,12 @@ const report = (ast, config, currentSchema) => {
|
|
|
2656
2964
|
case "domain":
|
|
2657
2965
|
code.push(`${a.action === "create" ? green("+ create domain") : red("- drop domain")} ${dbItemName(a, currentSchema)}`);
|
|
2658
2966
|
break;
|
|
2967
|
+
case "materializedView":
|
|
2968
|
+
code.push(`${a.action === "create" ? green("+ create materialized view") : red("- drop materialized view")} ${dbItemName(a, currentSchema)}`);
|
|
2969
|
+
break;
|
|
2659
2970
|
case "view":
|
|
2971
|
+
code.push(`${a.action === "create" ? green("+ create view") : red("- drop view")} ${dbItemName(a, currentSchema)}`);
|
|
2972
|
+
break;
|
|
2660
2973
|
case "collation":
|
|
2661
2974
|
case "constraint": break;
|
|
2662
2975
|
case "renameTableItem":
|
|
@@ -2814,7 +3127,8 @@ const generate = async (adapters, config, args, afterPull) => {
|
|
|
2814
3127
|
const { columnTypes, internal } = db.$qb;
|
|
2815
3128
|
const structureParams = {
|
|
2816
3129
|
loadDefaultPrivileges: internal.roles?.some((role) => role.defaultPrivileges !== void 0) ?? false,
|
|
2817
|
-
loadGrants: !!internal.grants ||
|
|
3130
|
+
loadGrants: !!internal.grants || hasCodeItemsWithGrants(db),
|
|
3131
|
+
loadViews: hasCodeViews(db)
|
|
2818
3132
|
};
|
|
2819
3133
|
const rolesDbStructureParam = internal.roles ? internal.managedRolesSql ? { whereSql: internal.managedRolesSql } : emptyObject : void 0;
|
|
2820
3134
|
const { dbStructure } = await migrateAndPullStructures(adapters, config, db, rolesDbStructureParam, structureParams, afterPull);
|
|
@@ -2879,6 +3193,7 @@ const migrateAndPullStructures = async (adapters, config, db, roles, structurePa
|
|
|
2879
3193
|
schemas: [],
|
|
2880
3194
|
tables: [],
|
|
2881
3195
|
views: [],
|
|
3196
|
+
materializedViews: [],
|
|
2882
3197
|
indexes: [],
|
|
2883
3198
|
excludes: [],
|
|
2884
3199
|
constraints: [],
|
|
@@ -2893,7 +3208,8 @@ const migrateAndPullStructures = async (adapters, config, db, roles, structurePa
|
|
|
2893
3208
|
rls: hasCodeTablesWithRls(db),
|
|
2894
3209
|
roles,
|
|
2895
3210
|
loadDefaultPrivileges: structureParams?.loadDefaultPrivileges,
|
|
2896
|
-
loadGrants: structureParams?.loadGrants
|
|
3211
|
+
loadGrants: structureParams?.loadGrants,
|
|
3212
|
+
loadViews: structureParams?.loadViews
|
|
2897
3213
|
})));
|
|
2898
3214
|
const dbStructure = dbStructures[0];
|
|
2899
3215
|
for (let i = 1; i < dbStructures.length; i++) compareDbStructures(dbStructure, dbStructures[i], i);
|
|
@@ -2906,11 +3222,17 @@ const hasCodeTablesWithRls = (db) => {
|
|
|
2906
3222
|
}
|
|
2907
3223
|
return false;
|
|
2908
3224
|
};
|
|
2909
|
-
const
|
|
3225
|
+
const hasCodeViews = (db) => {
|
|
3226
|
+
return !!Object.keys(db.$views ?? {}).length;
|
|
3227
|
+
};
|
|
3228
|
+
const hasCodeItemsWithGrants = (db) => {
|
|
2910
3229
|
for (const key in db) {
|
|
2911
3230
|
if (key[0] === "$") continue;
|
|
2912
3231
|
if (db[key].internal.tableGrants?.length) return true;
|
|
2913
3232
|
}
|
|
3233
|
+
const views = db.$views;
|
|
3234
|
+
if (!views) return false;
|
|
3235
|
+
for (const key in views) if (views[key].internal.tableGrants?.length) return true;
|
|
2914
3236
|
return false;
|
|
2915
3237
|
};
|
|
2916
3238
|
const getEffectiveGrants = (grants, codeItems) => {
|
|
@@ -2928,6 +3250,19 @@ const getEffectiveGrants = (grants, codeItems) => {
|
|
|
2928
3250
|
effectiveGrants.push(internalGrant);
|
|
2929
3251
|
}
|
|
2930
3252
|
}
|
|
3253
|
+
for (const view of codeItems.views) {
|
|
3254
|
+
const viewGrants = view.internal.tableGrants;
|
|
3255
|
+
if (!viewGrants?.length) continue;
|
|
3256
|
+
const viewTarget = view.q.schema ? `${view.q.schema}.${view.name}` : view.name;
|
|
3257
|
+
for (const grant of viewGrants) {
|
|
3258
|
+
const internalGrant = {
|
|
3259
|
+
...grant,
|
|
3260
|
+
to: toArray(grant.to),
|
|
3261
|
+
tables: [viewTarget]
|
|
3262
|
+
};
|
|
3263
|
+
effectiveGrants.push(internalGrant);
|
|
3264
|
+
}
|
|
3265
|
+
}
|
|
2931
3266
|
return effectiveGrants.length ? effectiveGrants : void 0;
|
|
2932
3267
|
};
|
|
2933
3268
|
const compareDbStructures = (a, b, i, path) => {
|
|
@@ -2946,6 +3281,7 @@ const getActualItems = async (db, currentSchema, internal, columnTypes) => {
|
|
|
2946
3281
|
schemas: /* @__PURE__ */ new Set(void 0),
|
|
2947
3282
|
enums: /* @__PURE__ */ new Map(),
|
|
2948
3283
|
tables: [],
|
|
3284
|
+
views: [],
|
|
2949
3285
|
domains: []
|
|
2950
3286
|
};
|
|
2951
3287
|
codeItems.schemas.add(currentSchema);
|
|
@@ -2975,21 +3311,22 @@ const getActualItems = async (db, currentSchema, internal, columnTypes) => {
|
|
|
2975
3311
|
const column = table.shape[key];
|
|
2976
3312
|
if (column && "joinTable" in column) processHasAndBelongsToManyColumn(column, habtmTables, codeItems);
|
|
2977
3313
|
}
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
}
|
|
2992
|
-
}
|
|
3314
|
+
processCodeItemShape(table.shape, currentSchema, codeItems, domains);
|
|
3315
|
+
}
|
|
3316
|
+
const views = db.$views;
|
|
3317
|
+
if (views) for (const key in views) {
|
|
3318
|
+
const view = views[key];
|
|
3319
|
+
const schema = getQuerySchema(view);
|
|
3320
|
+
if (schema) codeItems.schemas.add(schema);
|
|
3321
|
+
codeItems.views.push({
|
|
3322
|
+
name: view.table,
|
|
3323
|
+
shape: view.shape,
|
|
3324
|
+
internal: view.internal,
|
|
3325
|
+
q: { schema },
|
|
3326
|
+
materialized: view.internal.materialized,
|
|
3327
|
+
viewData: view.internal.viewData ?? {}
|
|
3328
|
+
});
|
|
3329
|
+
processCodeItemShape(view.shape, currentSchema, codeItems, domains);
|
|
2993
3330
|
}
|
|
2994
3331
|
if (internal.extensions) for (const extension of internal.extensions) {
|
|
2995
3332
|
const [schema] = getSchemaAndTableFromName(currentSchema, extension.name);
|
|
@@ -3016,6 +3353,23 @@ const getActualItems = async (db, currentSchema, internal, columnTypes) => {
|
|
|
3016
3353
|
if (internal.grants) for (const grant of internal.grants) addGrantSchemas(codeItems.schemas, currentSchema, grant);
|
|
3017
3354
|
return codeItems;
|
|
3018
3355
|
};
|
|
3356
|
+
const processCodeItemShape = (shape, currentSchema, codeItems, domains) => {
|
|
3357
|
+
for (const key in shape) {
|
|
3358
|
+
const column = shape[key];
|
|
3359
|
+
if (column.data.computed) delete shape[key];
|
|
3360
|
+
else if (column instanceof DomainColumn) {
|
|
3361
|
+
const [schemaName = currentSchema, name] = getSchemaAndTableFromName(currentSchema, column.dataType);
|
|
3362
|
+
domains.set(column.dataType, {
|
|
3363
|
+
schemaName,
|
|
3364
|
+
name,
|
|
3365
|
+
column: column.data.as ?? UnknownColumn.instance
|
|
3366
|
+
});
|
|
3367
|
+
} else {
|
|
3368
|
+
const en = column.dataType === "enum" ? column : column instanceof ArrayColumn && column.data.item.dataType === "enum" ? column.data.item : void 0;
|
|
3369
|
+
if (en) processEnumColumn(en, currentSchema, codeItems);
|
|
3370
|
+
}
|
|
3371
|
+
}
|
|
3372
|
+
};
|
|
3019
3373
|
const addGrantSchemas = (schemas, currentSchema, grant) => {
|
|
3020
3374
|
for (const schema of [
|
|
3021
3375
|
...grant.schemas ?? [],
|