orchid-orm 1.72.7 → 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 +4 -4
package/dist/migrations/index.js
CHANGED
|
@@ -28,14 +28,14 @@ let node_fs_promises = require("node:fs/promises");
|
|
|
28
28
|
node_fs_promises = __toESM(node_fs_promises);
|
|
29
29
|
let typescript = require("typescript");
|
|
30
30
|
typescript = __toESM(typescript);
|
|
31
|
-
const compareSqlExpressions = async (
|
|
32
|
-
if (!
|
|
31
|
+
const compareSqlExpressions = async (expressions, adapter) => {
|
|
32
|
+
if (!expressions.length) return;
|
|
33
33
|
let id = 1;
|
|
34
|
-
for (const { source, compare, handle } of
|
|
34
|
+
for (const { source, compare, handle } of expressions) {
|
|
35
35
|
const viewName = `orchidTmpView${id++}`;
|
|
36
36
|
const values = [];
|
|
37
37
|
const combinedQueries = [
|
|
38
|
-
`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})`,
|
|
38
|
+
`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}` : ""})`,
|
|
39
39
|
`SELECT pg_get_viewdef('${viewName}') v`,
|
|
40
40
|
`DROP VIEW ${viewName}`
|
|
41
41
|
].join("; ");
|
|
@@ -50,6 +50,50 @@ const compareSqlExpressions = async (tableExpressions, adapter) => {
|
|
|
50
50
|
handle(compareSqlExpressionResult(result.rows[0].v, compare[0].inCode));
|
|
51
51
|
}
|
|
52
52
|
};
|
|
53
|
+
const compareViewsExpressions = async (adapter, compare) => {
|
|
54
|
+
if (!compare.length) return;
|
|
55
|
+
const queries = [];
|
|
56
|
+
let id = 1;
|
|
57
|
+
compare.forEach(({ inCode, ast }, i) => {
|
|
58
|
+
const viewName = `orchidTmpView${id++}ForViews`;
|
|
59
|
+
queries.push({ batch: [
|
|
60
|
+
`SAVEPOINT "${viewName}S"`,
|
|
61
|
+
`CREATE TEMPORARY${"recursive" in ast.options && ast.options.recursive ? " RECURSIVE" : ""} VIEW "${viewName}" (${ast.options.columns?.map((column) => `"${column}"`).join(", ")}) AS (${inCode})`,
|
|
62
|
+
`SELECT ${i} i, '${viewName}' v, pg_get_viewdef('"${viewName}"') sql`,
|
|
63
|
+
`DROP VIEW "${viewName}"`,
|
|
64
|
+
`RELEASE SAVEPOINT "${viewName}S"`
|
|
65
|
+
] });
|
|
66
|
+
});
|
|
67
|
+
let results;
|
|
68
|
+
try {
|
|
69
|
+
const sql = queries.flatMap((q) => q.batch).join(";");
|
|
70
|
+
const query = () => adapter.query(sql, []);
|
|
71
|
+
results = await (adapter.isInTransaction() ? adapter.savepoint("orchidOrmGeneratorViews", query) : query());
|
|
72
|
+
} catch {
|
|
73
|
+
results = (await Promise.all(queries.map(async ({ batch: queries }, i) => {
|
|
74
|
+
const sql = queries.join(";");
|
|
75
|
+
const query = () => adapter.query(sql, []);
|
|
76
|
+
return await (adapter.isInTransaction() ? adapter.savepoint(`orchidOrmGeneratorViews${i}`, query) : query()).catch((err) => {
|
|
77
|
+
if (typeof err === "object") {
|
|
78
|
+
const code = (0, pqb_internal.getDriverErrorCode)(err);
|
|
79
|
+
if (code === "42703" || code === "42704" || code === "42P01") return [];
|
|
80
|
+
}
|
|
81
|
+
throw err;
|
|
82
|
+
});
|
|
83
|
+
}))).flat();
|
|
84
|
+
}
|
|
85
|
+
const handled = /* @__PURE__ */ new Set();
|
|
86
|
+
for (const result of results) for (const row of result.rows) if ("sql" in row) {
|
|
87
|
+
const { i, v } = row;
|
|
88
|
+
const cmp = compare[i];
|
|
89
|
+
const hasQuote = !!cmp.inDb.match(/\w*WITH RECURSIVE "/);
|
|
90
|
+
if (row.sql.replaceAll(hasQuote ? v : `"${v}"`, cmp.ast.name) !== cmp.inDb) cmp.onNotEqual();
|
|
91
|
+
handled.add(i);
|
|
92
|
+
}
|
|
93
|
+
compare.forEach((cmp, i) => {
|
|
94
|
+
if (!handled.has(i)) cmp.onNotEqual();
|
|
95
|
+
});
|
|
96
|
+
};
|
|
53
97
|
const compareSqlExpressionResult = (resultSql, inCode) => {
|
|
54
98
|
let pos = 7;
|
|
55
99
|
const rgx = /\s+AS\s+"\*(inDb-\d+|inCode-\d+-\d+)\*",?/g;
|
|
@@ -107,8 +151,9 @@ const processSchemas = async (ast, dbStructure, { codeItems: { schemas }, verify
|
|
|
107
151
|
if (i) {
|
|
108
152
|
const from = dropSchemas[i - 1];
|
|
109
153
|
dropSchemas.splice(i - 1, 1);
|
|
154
|
+
const views = dbStructure.views || [];
|
|
110
155
|
renameSchemaInStructures(dbStructure.tables, from, schema);
|
|
111
|
-
renameSchemaInStructures(
|
|
156
|
+
renameSchemaInStructures(views, from, schema);
|
|
112
157
|
renameSchemaInStructures(dbStructure.indexes, from, schema);
|
|
113
158
|
renameSchemaInStructures(dbStructure.excludes, from, schema);
|
|
114
159
|
renameSchemaInStructures(dbStructure.constraints, from, schema);
|
|
@@ -435,7 +480,7 @@ const renameColumn = (columns, from, to) => {
|
|
|
435
480
|
const processDomains = async (ast, adapter, domainsMap, dbStructure, { codeItems: { domains }, structureToAstCtx, currentSchema, internal: { generatorIgnore } }, pendingDbTypes) => {
|
|
436
481
|
const codeDomains = [];
|
|
437
482
|
if (domains) for (const { schemaName, name, column } of domains) codeDomains.push(makeComparableDomain(currentSchema, schemaName, name, column));
|
|
438
|
-
const
|
|
483
|
+
const expressions = [];
|
|
439
484
|
const holdCodeDomains = /* @__PURE__ */ new Set();
|
|
440
485
|
for (const domain of dbStructure.domains) {
|
|
441
486
|
if (generatorIgnore?.schemas?.includes(domain.schemaName) || generatorIgnore?.domains?.includes(domain.name)) continue;
|
|
@@ -461,7 +506,7 @@ const processDomains = async (ast, adapter, domainsMap, dbStructure, { codeItems
|
|
|
461
506
|
pushCompareDefault(compare, domain, found);
|
|
462
507
|
pushCompareChecks(compare, domain, found);
|
|
463
508
|
const source = `(VALUES (NULL::${getColumnDbType(dbColumn, currentSchema)})) t(value)`;
|
|
464
|
-
|
|
509
|
+
expressions.push({
|
|
465
510
|
compare,
|
|
466
511
|
source,
|
|
467
512
|
handle(i) {
|
|
@@ -492,8 +537,8 @@ const processDomains = async (ast, adapter, domainsMap, dbStructure, { codeItems
|
|
|
492
537
|
ast.push(createAst(codeDomain));
|
|
493
538
|
pendingDbTypes.add(codeDomain.schemaName, codeDomain.name);
|
|
494
539
|
}
|
|
495
|
-
if (
|
|
496
|
-
await compareSqlExpressions(
|
|
540
|
+
if (expressions.length) {
|
|
541
|
+
await compareSqlExpressions(expressions, adapter);
|
|
497
542
|
if (holdCodeDomains.size) for (const codeDomain of holdCodeDomains.keys()) {
|
|
498
543
|
ast.push(createAst(codeDomain));
|
|
499
544
|
pendingDbTypes.add(codeDomain.schemaName, codeDomain.name);
|
|
@@ -1691,13 +1736,13 @@ const processTables = async (ast, domainsMap, adapter, dbStructure, config, { st
|
|
|
1691
1736
|
values: [],
|
|
1692
1737
|
expressions: []
|
|
1693
1738
|
};
|
|
1694
|
-
const
|
|
1739
|
+
const expressions = [];
|
|
1695
1740
|
const { changeTables, changeTableSchemas, dropTables, tableShapes } = collectChangeAndDropTables(adapter, config, tables, dbStructure, currentSchema, createTables, generatorIgnore);
|
|
1696
1741
|
applyChangeTableSchemas(changeTableSchemas, currentSchema, ast);
|
|
1697
1742
|
await applyCreateOrRenameTables(dbStructure, createTables, dropTables, changeTables, tableShapes, currentSchema, ast, verifying);
|
|
1698
|
-
await applyChangeTables(adapter, changeTables, structureToAstCtx, dbStructure, domainsMap, ast, currentSchema, config, compareSql,
|
|
1743
|
+
await applyChangeTables(adapter, changeTables, structureToAstCtx, dbStructure, domainsMap, ast, currentSchema, config, compareSql, expressions, verifying, pendingDbTypes);
|
|
1699
1744
|
processForeignKeys(config, ast, changeTables, currentSchema, tableShapes);
|
|
1700
|
-
await Promise.all([applyCompareSql(compareSql, adapter), compareSqlExpressions(
|
|
1745
|
+
await Promise.all([applyCompareSql(compareSql, adapter), compareSqlExpressions(expressions, adapter)]);
|
|
1701
1746
|
await processTableRls(adapter, ast, dbStructure, tables, currentSchema, generatorIgnore);
|
|
1702
1747
|
for (const dbTable of dropTables) ast.push((0, rake_db.tableToAst)(structureToAstCtx, dbStructure, dbTable, "drop", domainsMap));
|
|
1703
1748
|
};
|
|
@@ -1761,7 +1806,7 @@ const applyChangeTableSchemas = (changeTableSchemas, currentSchema, ast) => {
|
|
|
1761
1806
|
});
|
|
1762
1807
|
}
|
|
1763
1808
|
};
|
|
1764
|
-
const applyChangeTables = async (adapter, changeTables, structureToAstCtx, dbStructure, domainsMap, ast, currentSchema, config, compareSql,
|
|
1809
|
+
const applyChangeTables = async (adapter, changeTables, structureToAstCtx, dbStructure, domainsMap, ast, currentSchema, config, compareSql, expressions, verifying, pendingDbTypes) => {
|
|
1765
1810
|
const compareExpressions = [];
|
|
1766
1811
|
const typeCastsCache = {};
|
|
1767
1812
|
for (const changeTableData of changeTables) {
|
|
@@ -1783,7 +1828,7 @@ const applyChangeTables = async (adapter, changeTables, structureToAstCtx, dbStr
|
|
|
1783
1828
|
}
|
|
1784
1829
|
const tableName = codeTable.table;
|
|
1785
1830
|
const source = `(VALUES (${types.map((x) => `NULL::${x}`).join(", ")})) "${tableName}"(${names.map((x) => `"${x}"`).join(", ")})`;
|
|
1786
|
-
|
|
1831
|
+
expressions.push(...compareExpressions.map((x) => ({
|
|
1787
1832
|
...x,
|
|
1788
1833
|
source
|
|
1789
1834
|
})));
|
|
@@ -2405,13 +2450,16 @@ const normalizeRoleName = (name) => {
|
|
|
2405
2450
|
};
|
|
2406
2451
|
const getSchemaWideTargets = (dbStructure, targetKey, schemas) => {
|
|
2407
2452
|
const selectedSchemas = new Set(schemas);
|
|
2408
|
-
if (targetKey === "tables")
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2453
|
+
if (targetKey === "tables") {
|
|
2454
|
+
const views = dbStructure.views || [];
|
|
2455
|
+
return [...dbStructure.tables.map((table) => ({
|
|
2456
|
+
target: `${table.schemaName}.${table.name}`,
|
|
2457
|
+
outputTarget: `${table.schemaName}.${table.name}`
|
|
2458
|
+
})), ...views.map((view) => ({
|
|
2459
|
+
target: `${view.schemaName}.${view.name}`,
|
|
2460
|
+
outputTarget: `${view.schemaName}.${view.name}`
|
|
2461
|
+
}))].filter((item) => selectedSchemas.has(item.target.split(".")[0]));
|
|
2462
|
+
}
|
|
2415
2463
|
if (targetKey === "sequences" || targetKey === "routines") return getSchemaWideGrantTargets(dbStructure, targetKey, selectedSchemas);
|
|
2416
2464
|
return [];
|
|
2417
2465
|
};
|
|
@@ -2478,6 +2526,264 @@ const addGrantAst = (ast, action, grant, privileges, privilegeKey) => {
|
|
|
2478
2526
|
grantedBy: grant.grantedBy
|
|
2479
2527
|
});
|
|
2480
2528
|
};
|
|
2529
|
+
const processViews = async (ast, adapter, dbStructure, { codeItems: { views: allViews }, currentSchema, internal: { generatorIgnore } }) => {
|
|
2530
|
+
const createViews = [];
|
|
2531
|
+
const changeViews = [];
|
|
2532
|
+
const dropViews = [];
|
|
2533
|
+
const ignoredViews = makeIgnoredViews$1(generatorIgnore, currentSchema);
|
|
2534
|
+
const views = allViews.filter((view) => !view.materialized);
|
|
2535
|
+
for (const codeView of views) {
|
|
2536
|
+
if (isIgnoredView$1(ignoredViews, generatorIgnore, currentSchema, codeView)) continue;
|
|
2537
|
+
const schemaName = codeView.q.schema ?? currentSchema;
|
|
2538
|
+
const dbView = dbStructure.views?.find((view) => view.schemaName === schemaName && view.name === codeView.name);
|
|
2539
|
+
if (dbView) changeViews.push({
|
|
2540
|
+
codeView,
|
|
2541
|
+
dbView
|
|
2542
|
+
});
|
|
2543
|
+
else createViews.push(codeView);
|
|
2544
|
+
}
|
|
2545
|
+
for (const dbView of dbStructure.views ?? []) {
|
|
2546
|
+
if (generatorIgnore?.schemas?.includes(dbView.schemaName) || ignoredViews.some((ignore) => matchesIgnoredView$1(ignore, dbView, currentSchema))) continue;
|
|
2547
|
+
if (!views.find((view) => view.name === dbView.name && (view.q.schema ?? currentSchema) === dbView.schemaName)) dropViews.push(dbView);
|
|
2548
|
+
}
|
|
2549
|
+
for (const codeView of createViews) ast.push(codeViewToAst$1(codeView, currentSchema, "create"));
|
|
2550
|
+
await applyChangeViews$1(ast, adapter, changeViews, currentSchema);
|
|
2551
|
+
for (const dbView of dropViews) ast.push(dbViewToAst$1(dbView, currentSchema, "drop"));
|
|
2552
|
+
};
|
|
2553
|
+
const applyChangeViews$1 = async (ast, adapter, changeViews, currentSchema) => {
|
|
2554
|
+
const compare = [];
|
|
2555
|
+
for (const { codeView, dbView } of changeViews) {
|
|
2556
|
+
const from = dbViewToAst$1(dbView, currentSchema, "drop");
|
|
2557
|
+
const to = codeViewToAst$1(codeView, currentSchema, "create");
|
|
2558
|
+
if (!isViewOptionsEqual(from.options, to.options)) {
|
|
2559
|
+
pushRecreateView$1(ast, from, to);
|
|
2560
|
+
continue;
|
|
2561
|
+
}
|
|
2562
|
+
const codeSql = typeof to.sql === "string" ? to.sql : to.sql.toSQL({ values: [] });
|
|
2563
|
+
compare.push({
|
|
2564
|
+
inDb: dbView.sql,
|
|
2565
|
+
inCode: codeSql,
|
|
2566
|
+
ast: to,
|
|
2567
|
+
onNotEqual() {
|
|
2568
|
+
pushRecreateView$1(ast, from, to);
|
|
2569
|
+
}
|
|
2570
|
+
});
|
|
2571
|
+
}
|
|
2572
|
+
await compareViewsExpressions(adapter, compare);
|
|
2573
|
+
};
|
|
2574
|
+
const pushRecreateView$1 = (ast, from, to) => {
|
|
2575
|
+
ast.push(from, to);
|
|
2576
|
+
};
|
|
2577
|
+
const codeViewToAst$1 = (view, currentSchema, action) => {
|
|
2578
|
+
const schema = view.q.schema ?? currentSchema;
|
|
2579
|
+
const sql = typeof view.viewData.sql === "string" ? (0, pqb_internal.raw)({ raw: view.viewData.sql }) : view.viewData.sql ?? (0, pqb_internal.raw)({ raw: "" });
|
|
2580
|
+
return {
|
|
2581
|
+
type: "view",
|
|
2582
|
+
action,
|
|
2583
|
+
schema: schema === currentSchema ? void 0 : schema,
|
|
2584
|
+
name: view.name,
|
|
2585
|
+
shape: view.shape,
|
|
2586
|
+
sql,
|
|
2587
|
+
options: {
|
|
2588
|
+
recursive: view.viewData.recursive,
|
|
2589
|
+
columns: Object.keys(view.shape),
|
|
2590
|
+
checkOption: view.viewData.checkOption,
|
|
2591
|
+
securityBarrier: view.viewData.securityBarrier,
|
|
2592
|
+
securityInvoker: view.viewData.securityInvoker ?? true
|
|
2593
|
+
},
|
|
2594
|
+
deps: []
|
|
2595
|
+
};
|
|
2596
|
+
};
|
|
2597
|
+
const dbViewToAst$1 = (view, currentSchema, action) => {
|
|
2598
|
+
return {
|
|
2599
|
+
type: "view",
|
|
2600
|
+
action,
|
|
2601
|
+
schema: view.schemaName === currentSchema ? void 0 : view.schemaName,
|
|
2602
|
+
name: view.name,
|
|
2603
|
+
shape: {},
|
|
2604
|
+
sql: (0, pqb_internal.raw)({ raw: view.sql }),
|
|
2605
|
+
options: dbViewOptionsToAst(view),
|
|
2606
|
+
deps: view.deps
|
|
2607
|
+
};
|
|
2608
|
+
};
|
|
2609
|
+
const dbViewOptionsToAst = (view) => {
|
|
2610
|
+
const options = {};
|
|
2611
|
+
options.columns = view.columns.map((column) => column.name);
|
|
2612
|
+
if (view.isRecursive) options.recursive = true;
|
|
2613
|
+
if (view.with) for (const pair of view.with) {
|
|
2614
|
+
const [key, value] = pair.split("=");
|
|
2615
|
+
switch (key) {
|
|
2616
|
+
case "check_option":
|
|
2617
|
+
if (value === "LOCAL" || value === "CASCADED") options.checkOption = value;
|
|
2618
|
+
break;
|
|
2619
|
+
case "security_barrier":
|
|
2620
|
+
options.securityBarrier = value === "true";
|
|
2621
|
+
break;
|
|
2622
|
+
case "security_invoker":
|
|
2623
|
+
options.securityInvoker = value === "true";
|
|
2624
|
+
break;
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
return options;
|
|
2628
|
+
};
|
|
2629
|
+
const isViewOptionsEqual = (from, to) => {
|
|
2630
|
+
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);
|
|
2631
|
+
};
|
|
2632
|
+
const isStringArrayEqual$1 = (a, b) => {
|
|
2633
|
+
if ((a?.length ?? 0) !== (b?.length ?? 0)) return false;
|
|
2634
|
+
if (!a?.length && !b?.length) return true;
|
|
2635
|
+
return !!a?.every((item, i) => item === b?.[i]);
|
|
2636
|
+
};
|
|
2637
|
+
const makeIgnoredViews$1 = (generatorIgnore, currentSchema) => {
|
|
2638
|
+
const views = generatorIgnore?.views;
|
|
2639
|
+
if (!views) return [];
|
|
2640
|
+
return views.map((name) => {
|
|
2641
|
+
if (typeof name !== "string") return { name };
|
|
2642
|
+
const parts = name.split(".");
|
|
2643
|
+
return parts.length === 2 ? {
|
|
2644
|
+
schema: parts[0],
|
|
2645
|
+
name: parts[1]
|
|
2646
|
+
} : {
|
|
2647
|
+
schema: currentSchema,
|
|
2648
|
+
name
|
|
2649
|
+
};
|
|
2650
|
+
});
|
|
2651
|
+
};
|
|
2652
|
+
const isIgnoredView$1 = (ignoredViews, generatorIgnore, currentSchema, codeView) => {
|
|
2653
|
+
const schemaName = codeView.q.schema ?? currentSchema;
|
|
2654
|
+
return generatorIgnore?.schemas?.includes(schemaName) === true || ignoredViews.some((ignore) => {
|
|
2655
|
+
return matchesIgnoredView$1(ignore, {
|
|
2656
|
+
schemaName,
|
|
2657
|
+
name: codeView.name
|
|
2658
|
+
}, currentSchema);
|
|
2659
|
+
});
|
|
2660
|
+
};
|
|
2661
|
+
const matchesIgnoredView$1 = (ignore, view, currentSchema) => {
|
|
2662
|
+
return typeof ignore.name === "string" ? ignore.schema === view.schemaName && ignore.name === view.name : ignore.name.test(normalizedViewName$1(view, currentSchema));
|
|
2663
|
+
};
|
|
2664
|
+
const normalizedViewName$1 = (view, currentSchema) => {
|
|
2665
|
+
return view.schemaName === currentSchema ? view.name : `${view.schemaName}.${view.name}`;
|
|
2666
|
+
};
|
|
2667
|
+
const processMaterializedViews = async (ast, adapter, dbStructure, { codeItems: { views: allViews }, currentSchema, internal: { generatorIgnore } }) => {
|
|
2668
|
+
const views = allViews.filter((view) => view.materialized);
|
|
2669
|
+
const createViews = [];
|
|
2670
|
+
const changeViews = [];
|
|
2671
|
+
const dropViews = [];
|
|
2672
|
+
const ignoredViews = makeIgnoredViews(generatorIgnore, currentSchema);
|
|
2673
|
+
for (const codeView of views) {
|
|
2674
|
+
if (isIgnoredView(ignoredViews, generatorIgnore, currentSchema, codeView)) continue;
|
|
2675
|
+
const schemaName = codeView.q.schema ?? currentSchema;
|
|
2676
|
+
const dbView = dbStructure.materializedViews?.find((view) => view.schemaName === schemaName && view.name === codeView.name);
|
|
2677
|
+
if (dbView) changeViews.push({
|
|
2678
|
+
codeView,
|
|
2679
|
+
dbView
|
|
2680
|
+
});
|
|
2681
|
+
else createViews.push(codeView);
|
|
2682
|
+
}
|
|
2683
|
+
for (const dbView of dbStructure.materializedViews ?? []) {
|
|
2684
|
+
if (generatorIgnore?.schemas?.includes(dbView.schemaName) || ignoredViews.some((ignore) => matchesIgnoredView(ignore, dbView, currentSchema))) continue;
|
|
2685
|
+
if (!views.find((view) => view.name === dbView.name && (view.q.schema ?? currentSchema) === dbView.schemaName)) dropViews.push(dbView);
|
|
2686
|
+
}
|
|
2687
|
+
for (const codeView of createViews) ast.push(codeViewToAst(codeView, currentSchema, "create"));
|
|
2688
|
+
await applyChangeViews(ast, adapter, changeViews, currentSchema);
|
|
2689
|
+
for (const dbView of dropViews) ast.push(dbViewToAst(dbView, currentSchema, "drop"));
|
|
2690
|
+
};
|
|
2691
|
+
const applyChangeViews = async (ast, adapter, changeViews, currentSchema) => {
|
|
2692
|
+
const compare = [];
|
|
2693
|
+
for (const { codeView, dbView } of changeViews) {
|
|
2694
|
+
const from = dbViewToAst(dbView, currentSchema, "drop");
|
|
2695
|
+
const to = codeViewToAst(codeView, currentSchema, "create");
|
|
2696
|
+
if (!isMaterializedViewOptionsEqual(from.options, to.options)) {
|
|
2697
|
+
pushRecreateView(ast, from, to);
|
|
2698
|
+
continue;
|
|
2699
|
+
}
|
|
2700
|
+
const codeSql = typeof to.sql === "string" ? to.sql : to.sql.toSQL({ values: [] });
|
|
2701
|
+
compare.push({
|
|
2702
|
+
inDb: dbView.sql,
|
|
2703
|
+
inCode: codeSql,
|
|
2704
|
+
ast: to,
|
|
2705
|
+
onNotEqual() {
|
|
2706
|
+
pushRecreateView(ast, from, to);
|
|
2707
|
+
}
|
|
2708
|
+
});
|
|
2709
|
+
}
|
|
2710
|
+
await compareViewsExpressions(adapter, compare);
|
|
2711
|
+
};
|
|
2712
|
+
const pushRecreateView = (ast, from, to) => {
|
|
2713
|
+
ast.push(from, to);
|
|
2714
|
+
};
|
|
2715
|
+
const codeViewToAst = (view, currentSchema, action) => {
|
|
2716
|
+
const schema = view.q.schema ?? currentSchema;
|
|
2717
|
+
const sql = typeof view.viewData.sql === "string" ? (0, pqb_internal.raw)({ raw: view.viewData.sql }) : view.viewData.sql ?? (0, pqb_internal.raw)({ raw: "" });
|
|
2718
|
+
return {
|
|
2719
|
+
type: "materializedView",
|
|
2720
|
+
action,
|
|
2721
|
+
schema: schema === currentSchema ? void 0 : schema,
|
|
2722
|
+
name: view.name,
|
|
2723
|
+
shape: view.shape,
|
|
2724
|
+
sql,
|
|
2725
|
+
options: {
|
|
2726
|
+
columns: Object.keys(view.shape),
|
|
2727
|
+
withData: view.viewData.withData
|
|
2728
|
+
},
|
|
2729
|
+
deps: []
|
|
2730
|
+
};
|
|
2731
|
+
};
|
|
2732
|
+
const dbViewToAst = (view, currentSchema, action) => {
|
|
2733
|
+
return {
|
|
2734
|
+
type: "materializedView",
|
|
2735
|
+
action,
|
|
2736
|
+
schema: view.schemaName === currentSchema ? void 0 : view.schemaName,
|
|
2737
|
+
name: view.name,
|
|
2738
|
+
shape: {},
|
|
2739
|
+
sql: (0, pqb_internal.raw)({ raw: view.sql }),
|
|
2740
|
+
options: dbMaterializedViewOptionsToAst(view),
|
|
2741
|
+
deps: view.deps
|
|
2742
|
+
};
|
|
2743
|
+
};
|
|
2744
|
+
const dbMaterializedViewOptionsToAst = (view) => {
|
|
2745
|
+
const options = { columns: view.columns.map((column) => column.name) };
|
|
2746
|
+
if (!view.isPopulated) options.withData = false;
|
|
2747
|
+
return options;
|
|
2748
|
+
};
|
|
2749
|
+
const isMaterializedViewOptionsEqual = (from, to) => {
|
|
2750
|
+
return (from.withData ?? true) === (to.withData ?? true) && isStringArrayEqual(from.columns, to.columns);
|
|
2751
|
+
};
|
|
2752
|
+
const isStringArrayEqual = (a, b) => {
|
|
2753
|
+
if ((a?.length ?? 0) !== (b?.length ?? 0)) return false;
|
|
2754
|
+
if (!a?.length && !b?.length) return true;
|
|
2755
|
+
return !!a?.every((item, i) => item === b?.[i]);
|
|
2756
|
+
};
|
|
2757
|
+
const makeIgnoredViews = (generatorIgnore, currentSchema) => {
|
|
2758
|
+
const views = generatorIgnore?.views;
|
|
2759
|
+
if (!views) return [];
|
|
2760
|
+
return views.map((name) => {
|
|
2761
|
+
if (typeof name !== "string") return { name };
|
|
2762
|
+
const parts = name.split(".");
|
|
2763
|
+
return parts.length === 2 ? {
|
|
2764
|
+
schema: parts[0],
|
|
2765
|
+
name: parts[1]
|
|
2766
|
+
} : {
|
|
2767
|
+
schema: currentSchema,
|
|
2768
|
+
name
|
|
2769
|
+
};
|
|
2770
|
+
});
|
|
2771
|
+
};
|
|
2772
|
+
const isIgnoredView = (ignoredViews, generatorIgnore, currentSchema, codeView) => {
|
|
2773
|
+
const schemaName = codeView.q.schema ?? currentSchema;
|
|
2774
|
+
return generatorIgnore?.schemas?.includes(schemaName) === true || ignoredViews.some((ignore) => {
|
|
2775
|
+
return matchesIgnoredView(ignore, {
|
|
2776
|
+
schemaName,
|
|
2777
|
+
name: codeView.name
|
|
2778
|
+
}, currentSchema);
|
|
2779
|
+
});
|
|
2780
|
+
};
|
|
2781
|
+
const matchesIgnoredView = (ignore, view, currentSchema) => {
|
|
2782
|
+
return typeof ignore.name === "string" ? ignore.schema === view.schemaName && ignore.name === view.name : ignore.name.test(normalizedViewName(view, currentSchema));
|
|
2783
|
+
};
|
|
2784
|
+
const normalizedViewName = (view, currentSchema) => {
|
|
2785
|
+
return view.schemaName === currentSchema ? view.name : `${view.schemaName}.${view.name}`;
|
|
2786
|
+
};
|
|
2481
2787
|
/**
|
|
2482
2788
|
* This is needed to compare SQLs of table expressions.
|
|
2483
2789
|
* Need to exclude table columns of pending types, such as enums or domains,
|
|
@@ -2504,6 +2810,8 @@ const composeMigration = async (adapter, config, ast, dbStructure, params) => {
|
|
|
2504
2810
|
await processDomains(ast, adapter, domainsMap, dbStructure, params, pendingDbTypes);
|
|
2505
2811
|
await processEnums(ast, dbStructure, params, pendingDbTypes);
|
|
2506
2812
|
await processTables(ast, domainsMap, adapter, dbStructure, config, params, pendingDbTypes);
|
|
2813
|
+
await processViews(ast, adapter, dbStructure, params);
|
|
2814
|
+
await processMaterializedViews(ast, adapter, dbStructure, params);
|
|
2507
2815
|
return (0, rake_db.astToMigration)(currentSchema, config, ast);
|
|
2508
2816
|
};
|
|
2509
2817
|
const rollbackErr = /* @__PURE__ */ new Error("Rollback");
|
|
@@ -2679,7 +2987,12 @@ const report = (ast, config, currentSchema) => {
|
|
|
2679
2987
|
case "domain":
|
|
2680
2988
|
code.push(`${a.action === "create" ? green("+ create domain") : red("- drop domain")} ${dbItemName(a, currentSchema)}`);
|
|
2681
2989
|
break;
|
|
2990
|
+
case "materializedView":
|
|
2991
|
+
code.push(`${a.action === "create" ? green("+ create materialized view") : red("- drop materialized view")} ${dbItemName(a, currentSchema)}`);
|
|
2992
|
+
break;
|
|
2682
2993
|
case "view":
|
|
2994
|
+
code.push(`${a.action === "create" ? green("+ create view") : red("- drop view")} ${dbItemName(a, currentSchema)}`);
|
|
2995
|
+
break;
|
|
2683
2996
|
case "collation":
|
|
2684
2997
|
case "constraint": break;
|
|
2685
2998
|
case "renameTableItem":
|
|
@@ -2837,7 +3150,8 @@ const generate = async (adapters, config, args, afterPull) => {
|
|
|
2837
3150
|
const { columnTypes, internal } = db.$qb;
|
|
2838
3151
|
const structureParams = {
|
|
2839
3152
|
loadDefaultPrivileges: internal.roles?.some((role) => role.defaultPrivileges !== void 0) ?? false,
|
|
2840
|
-
loadGrants: !!internal.grants ||
|
|
3153
|
+
loadGrants: !!internal.grants || hasCodeItemsWithGrants(db),
|
|
3154
|
+
loadViews: hasCodeViews(db)
|
|
2841
3155
|
};
|
|
2842
3156
|
const rolesDbStructureParam = internal.roles ? internal.managedRolesSql ? { whereSql: internal.managedRolesSql } : pqb_internal.emptyObject : void 0;
|
|
2843
3157
|
const { dbStructure } = await migrateAndPullStructures(adapters, config, db, rolesDbStructureParam, structureParams, afterPull);
|
|
@@ -2902,6 +3216,7 @@ const migrateAndPullStructures = async (adapters, config, db, roles, structurePa
|
|
|
2902
3216
|
schemas: [],
|
|
2903
3217
|
tables: [],
|
|
2904
3218
|
views: [],
|
|
3219
|
+
materializedViews: [],
|
|
2905
3220
|
indexes: [],
|
|
2906
3221
|
excludes: [],
|
|
2907
3222
|
constraints: [],
|
|
@@ -2916,7 +3231,8 @@ const migrateAndPullStructures = async (adapters, config, db, roles, structurePa
|
|
|
2916
3231
|
rls: hasCodeTablesWithRls(db),
|
|
2917
3232
|
roles,
|
|
2918
3233
|
loadDefaultPrivileges: structureParams?.loadDefaultPrivileges,
|
|
2919
|
-
loadGrants: structureParams?.loadGrants
|
|
3234
|
+
loadGrants: structureParams?.loadGrants,
|
|
3235
|
+
loadViews: structureParams?.loadViews
|
|
2920
3236
|
})));
|
|
2921
3237
|
const dbStructure = dbStructures[0];
|
|
2922
3238
|
for (let i = 1; i < dbStructures.length; i++) compareDbStructures(dbStructure, dbStructures[i], i);
|
|
@@ -2929,11 +3245,17 @@ const hasCodeTablesWithRls = (db) => {
|
|
|
2929
3245
|
}
|
|
2930
3246
|
return false;
|
|
2931
3247
|
};
|
|
2932
|
-
const
|
|
3248
|
+
const hasCodeViews = (db) => {
|
|
3249
|
+
return !!Object.keys(db.$views ?? {}).length;
|
|
3250
|
+
};
|
|
3251
|
+
const hasCodeItemsWithGrants = (db) => {
|
|
2933
3252
|
for (const key in db) {
|
|
2934
3253
|
if (key[0] === "$") continue;
|
|
2935
3254
|
if (db[key].internal.tableGrants?.length) return true;
|
|
2936
3255
|
}
|
|
3256
|
+
const views = db.$views;
|
|
3257
|
+
if (!views) return false;
|
|
3258
|
+
for (const key in views) if (views[key].internal.tableGrants?.length) return true;
|
|
2937
3259
|
return false;
|
|
2938
3260
|
};
|
|
2939
3261
|
const getEffectiveGrants = (grants, codeItems) => {
|
|
@@ -2951,6 +3273,19 @@ const getEffectiveGrants = (grants, codeItems) => {
|
|
|
2951
3273
|
effectiveGrants.push(internalGrant);
|
|
2952
3274
|
}
|
|
2953
3275
|
}
|
|
3276
|
+
for (const view of codeItems.views) {
|
|
3277
|
+
const viewGrants = view.internal.tableGrants;
|
|
3278
|
+
if (!viewGrants?.length) continue;
|
|
3279
|
+
const viewTarget = view.q.schema ? `${view.q.schema}.${view.name}` : view.name;
|
|
3280
|
+
for (const grant of viewGrants) {
|
|
3281
|
+
const internalGrant = {
|
|
3282
|
+
...grant,
|
|
3283
|
+
to: (0, pqb_internal.toArray)(grant.to),
|
|
3284
|
+
tables: [viewTarget]
|
|
3285
|
+
};
|
|
3286
|
+
effectiveGrants.push(internalGrant);
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
2954
3289
|
return effectiveGrants.length ? effectiveGrants : void 0;
|
|
2955
3290
|
};
|
|
2956
3291
|
const compareDbStructures = (a, b, i, path) => {
|
|
@@ -2969,6 +3304,7 @@ const getActualItems = async (db, currentSchema, internal, columnTypes) => {
|
|
|
2969
3304
|
schemas: /* @__PURE__ */ new Set(void 0),
|
|
2970
3305
|
enums: /* @__PURE__ */ new Map(),
|
|
2971
3306
|
tables: [],
|
|
3307
|
+
views: [],
|
|
2972
3308
|
domains: []
|
|
2973
3309
|
};
|
|
2974
3310
|
codeItems.schemas.add(currentSchema);
|
|
@@ -2998,21 +3334,22 @@ const getActualItems = async (db, currentSchema, internal, columnTypes) => {
|
|
|
2998
3334
|
const column = table.shape[key];
|
|
2999
3335
|
if (column && "joinTable" in column) processHasAndBelongsToManyColumn(column, habtmTables, codeItems);
|
|
3000
3336
|
}
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
}
|
|
3015
|
-
}
|
|
3337
|
+
processCodeItemShape(table.shape, currentSchema, codeItems, domains);
|
|
3338
|
+
}
|
|
3339
|
+
const views = db.$views;
|
|
3340
|
+
if (views) for (const key in views) {
|
|
3341
|
+
const view = views[key];
|
|
3342
|
+
const schema = (0, pqb_internal.getQuerySchema)(view);
|
|
3343
|
+
if (schema) codeItems.schemas.add(schema);
|
|
3344
|
+
codeItems.views.push({
|
|
3345
|
+
name: view.table,
|
|
3346
|
+
shape: view.shape,
|
|
3347
|
+
internal: view.internal,
|
|
3348
|
+
q: { schema },
|
|
3349
|
+
materialized: view.internal.materialized,
|
|
3350
|
+
viewData: view.internal.viewData ?? {}
|
|
3351
|
+
});
|
|
3352
|
+
processCodeItemShape(view.shape, currentSchema, codeItems, domains);
|
|
3016
3353
|
}
|
|
3017
3354
|
if (internal.extensions) for (const extension of internal.extensions) {
|
|
3018
3355
|
const [schema] = (0, rake_db.getSchemaAndTableFromName)(currentSchema, extension.name);
|
|
@@ -3039,6 +3376,23 @@ const getActualItems = async (db, currentSchema, internal, columnTypes) => {
|
|
|
3039
3376
|
if (internal.grants) for (const grant of internal.grants) addGrantSchemas(codeItems.schemas, currentSchema, grant);
|
|
3040
3377
|
return codeItems;
|
|
3041
3378
|
};
|
|
3379
|
+
const processCodeItemShape = (shape, currentSchema, codeItems, domains) => {
|
|
3380
|
+
for (const key in shape) {
|
|
3381
|
+
const column = shape[key];
|
|
3382
|
+
if (column.data.computed) delete shape[key];
|
|
3383
|
+
else if (column instanceof pqb_internal.DomainColumn) {
|
|
3384
|
+
const [schemaName = currentSchema, name] = (0, rake_db.getSchemaAndTableFromName)(currentSchema, column.dataType);
|
|
3385
|
+
domains.set(column.dataType, {
|
|
3386
|
+
schemaName,
|
|
3387
|
+
name,
|
|
3388
|
+
column: column.data.as ?? pqb_internal.UnknownColumn.instance
|
|
3389
|
+
});
|
|
3390
|
+
} else {
|
|
3391
|
+
const en = column.dataType === "enum" ? column : column instanceof pqb_internal.ArrayColumn && column.data.item.dataType === "enum" ? column.data.item : void 0;
|
|
3392
|
+
if (en) processEnumColumn(en, currentSchema, codeItems);
|
|
3393
|
+
}
|
|
3394
|
+
}
|
|
3395
|
+
};
|
|
3042
3396
|
const addGrantSchemas = (schemas, currentSchema, grant) => {
|
|
3043
3397
|
for (const schema of [
|
|
3044
3398
|
...grant.schemas ?? [],
|