orchid-orm 1.72.7 → 1.72.9
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 +234 -165
- package/dist/index.js +97 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +97 -17
- package/dist/index.mjs.map +1 -1
- package/dist/migrations/index.js +435 -41
- package/dist/migrations/index.js.map +1 -1
- package/dist/migrations/index.mjs +436 -42
- 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
package/dist/migrations/index.js
CHANGED
|
@@ -28,14 +28,22 @@ 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
|
|
32
|
-
|
|
31
|
+
const viewDataToSql = (viewData, viewName) => {
|
|
32
|
+
return (0, pqb_internal.sqlToRawSql)(viewDataToQuerySql(viewData, viewName));
|
|
33
|
+
};
|
|
34
|
+
const viewDataToQuerySql = (viewData, viewName) => {
|
|
35
|
+
if (viewData.query) return (0, pqb_internal.queryToSql)(viewData.query);
|
|
36
|
+
if (viewData.sql !== void 0) return (0, pqb_internal.rawSqlToSql)(viewData.sql);
|
|
37
|
+
throw new Error(`Either sql or query is required for view ${viewName}`);
|
|
38
|
+
};
|
|
39
|
+
const compareSqlExpressions = async (expressions, adapter) => {
|
|
40
|
+
if (!expressions.length) return;
|
|
33
41
|
let id = 1;
|
|
34
|
-
for (const { source, compare, handle } of
|
|
42
|
+
for (const { source, compare, handle } of expressions) {
|
|
35
43
|
const viewName = `orchidTmpView${id++}`;
|
|
36
44
|
const values = [];
|
|
37
45
|
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})`,
|
|
46
|
+
`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
47
|
`SELECT pg_get_viewdef('${viewName}') v`,
|
|
40
48
|
`DROP VIEW ${viewName}`
|
|
41
49
|
].join("; ");
|
|
@@ -50,6 +58,50 @@ const compareSqlExpressions = async (tableExpressions, adapter) => {
|
|
|
50
58
|
handle(compareSqlExpressionResult(result.rows[0].v, compare[0].inCode));
|
|
51
59
|
}
|
|
52
60
|
};
|
|
61
|
+
const compareViewsExpressions = async (adapter, compare) => {
|
|
62
|
+
if (!compare.length) return;
|
|
63
|
+
const queries = [];
|
|
64
|
+
let id = 1;
|
|
65
|
+
compare.forEach(({ inCode, ast }, i) => {
|
|
66
|
+
const viewName = `orchidTmpView${id++}ForViews`;
|
|
67
|
+
queries.push({ batch: [
|
|
68
|
+
`SAVEPOINT "${viewName}S"`,
|
|
69
|
+
`CREATE TEMPORARY${"recursive" in ast.options && ast.options.recursive ? " RECURSIVE" : ""} VIEW "${viewName}" (${ast.options.columns?.map((column) => `"${column}"`).join(", ")}) AS (${inCode})`,
|
|
70
|
+
`SELECT ${i} i, '${viewName}' v, pg_get_viewdef('"${viewName}"') sql`,
|
|
71
|
+
`DROP VIEW "${viewName}"`,
|
|
72
|
+
`RELEASE SAVEPOINT "${viewName}S"`
|
|
73
|
+
] });
|
|
74
|
+
});
|
|
75
|
+
let results;
|
|
76
|
+
try {
|
|
77
|
+
const sql = queries.flatMap((q) => q.batch).join(";");
|
|
78
|
+
const query = () => adapter.query(sql, []);
|
|
79
|
+
results = await (adapter.isInTransaction() ? adapter.savepoint("orchidOrmGeneratorViews", query) : query());
|
|
80
|
+
} catch {
|
|
81
|
+
results = (await Promise.all(queries.map(async ({ batch: queries }, i) => {
|
|
82
|
+
const sql = queries.join(";");
|
|
83
|
+
const query = () => adapter.query(sql, []);
|
|
84
|
+
return await (adapter.isInTransaction() ? adapter.savepoint(`orchidOrmGeneratorViews${i}`, query) : query()).catch((err) => {
|
|
85
|
+
if (typeof err === "object") {
|
|
86
|
+
const code = (0, pqb_internal.getDriverErrorCode)(err);
|
|
87
|
+
if (code === "42703" || code === "42704" || code === "42P01") return [];
|
|
88
|
+
}
|
|
89
|
+
throw err;
|
|
90
|
+
});
|
|
91
|
+
}))).flat();
|
|
92
|
+
}
|
|
93
|
+
const handled = /* @__PURE__ */ new Set();
|
|
94
|
+
for (const result of results) for (const row of result.rows) if ("sql" in row) {
|
|
95
|
+
const { i, v } = row;
|
|
96
|
+
const cmp = compare[i];
|
|
97
|
+
const hasQuote = !!cmp.inDb.match(/\w*WITH RECURSIVE "/);
|
|
98
|
+
if (row.sql.replaceAll(hasQuote ? v : `"${v}"`, cmp.ast.name) !== cmp.inDb) cmp.onNotEqual();
|
|
99
|
+
handled.add(i);
|
|
100
|
+
}
|
|
101
|
+
compare.forEach((cmp, i) => {
|
|
102
|
+
if (!handled.has(i)) cmp.onNotEqual();
|
|
103
|
+
});
|
|
104
|
+
};
|
|
53
105
|
const compareSqlExpressionResult = (resultSql, inCode) => {
|
|
54
106
|
let pos = 7;
|
|
55
107
|
const rgx = /\s+AS\s+"\*(inDb-\d+|inCode-\d+-\d+)\*",?/g;
|
|
@@ -107,8 +159,9 @@ const processSchemas = async (ast, dbStructure, { codeItems: { schemas }, verify
|
|
|
107
159
|
if (i) {
|
|
108
160
|
const from = dropSchemas[i - 1];
|
|
109
161
|
dropSchemas.splice(i - 1, 1);
|
|
162
|
+
const views = dbStructure.views || [];
|
|
110
163
|
renameSchemaInStructures(dbStructure.tables, from, schema);
|
|
111
|
-
renameSchemaInStructures(
|
|
164
|
+
renameSchemaInStructures(views, from, schema);
|
|
112
165
|
renameSchemaInStructures(dbStructure.indexes, from, schema);
|
|
113
166
|
renameSchemaInStructures(dbStructure.excludes, from, schema);
|
|
114
167
|
renameSchemaInStructures(dbStructure.constraints, from, schema);
|
|
@@ -435,7 +488,7 @@ const renameColumn = (columns, from, to) => {
|
|
|
435
488
|
const processDomains = async (ast, adapter, domainsMap, dbStructure, { codeItems: { domains }, structureToAstCtx, currentSchema, internal: { generatorIgnore } }, pendingDbTypes) => {
|
|
436
489
|
const codeDomains = [];
|
|
437
490
|
if (domains) for (const { schemaName, name, column } of domains) codeDomains.push(makeComparableDomain(currentSchema, schemaName, name, column));
|
|
438
|
-
const
|
|
491
|
+
const expressions = [];
|
|
439
492
|
const holdCodeDomains = /* @__PURE__ */ new Set();
|
|
440
493
|
for (const domain of dbStructure.domains) {
|
|
441
494
|
if (generatorIgnore?.schemas?.includes(domain.schemaName) || generatorIgnore?.domains?.includes(domain.name)) continue;
|
|
@@ -461,7 +514,7 @@ const processDomains = async (ast, adapter, domainsMap, dbStructure, { codeItems
|
|
|
461
514
|
pushCompareDefault(compare, domain, found);
|
|
462
515
|
pushCompareChecks(compare, domain, found);
|
|
463
516
|
const source = `(VALUES (NULL::${getColumnDbType(dbColumn, currentSchema)})) t(value)`;
|
|
464
|
-
|
|
517
|
+
expressions.push({
|
|
465
518
|
compare,
|
|
466
519
|
source,
|
|
467
520
|
handle(i) {
|
|
@@ -492,8 +545,8 @@ const processDomains = async (ast, adapter, domainsMap, dbStructure, { codeItems
|
|
|
492
545
|
ast.push(createAst(codeDomain));
|
|
493
546
|
pendingDbTypes.add(codeDomain.schemaName, codeDomain.name);
|
|
494
547
|
}
|
|
495
|
-
if (
|
|
496
|
-
await compareSqlExpressions(
|
|
548
|
+
if (expressions.length) {
|
|
549
|
+
await compareSqlExpressions(expressions, adapter);
|
|
497
550
|
if (holdCodeDomains.size) for (const codeDomain of holdCodeDomains.keys()) {
|
|
498
551
|
ast.push(createAst(codeDomain));
|
|
499
552
|
pendingDbTypes.add(codeDomain.schemaName, codeDomain.name);
|
|
@@ -1691,18 +1744,19 @@ const processTables = async (ast, domainsMap, adapter, dbStructure, config, { st
|
|
|
1691
1744
|
values: [],
|
|
1692
1745
|
expressions: []
|
|
1693
1746
|
};
|
|
1694
|
-
const
|
|
1747
|
+
const expressions = [];
|
|
1695
1748
|
const { changeTables, changeTableSchemas, dropTables, tableShapes } = collectChangeAndDropTables(adapter, config, tables, dbStructure, currentSchema, createTables, generatorIgnore);
|
|
1696
1749
|
applyChangeTableSchemas(changeTableSchemas, currentSchema, ast);
|
|
1697
1750
|
await applyCreateOrRenameTables(dbStructure, createTables, dropTables, changeTables, tableShapes, currentSchema, ast, verifying);
|
|
1698
|
-
await applyChangeTables(adapter, changeTables, structureToAstCtx, dbStructure, domainsMap, ast, currentSchema, config, compareSql,
|
|
1751
|
+
await applyChangeTables(adapter, changeTables, structureToAstCtx, dbStructure, domainsMap, ast, currentSchema, config, compareSql, expressions, verifying, pendingDbTypes);
|
|
1699
1752
|
processForeignKeys(config, ast, changeTables, currentSchema, tableShapes);
|
|
1700
|
-
await Promise.all([applyCompareSql(compareSql, adapter), compareSqlExpressions(
|
|
1753
|
+
await Promise.all([applyCompareSql(compareSql, adapter), compareSqlExpressions(expressions, adapter)]);
|
|
1701
1754
|
await processTableRls(adapter, ast, dbStructure, tables, currentSchema, generatorIgnore);
|
|
1702
1755
|
for (const dbTable of dropTables) ast.push((0, rake_db.tableToAst)(structureToAstCtx, dbStructure, dbTable, "drop", domainsMap));
|
|
1703
1756
|
};
|
|
1704
1757
|
const collectCreateTables = (tables, dbStructure, currentSchema) => {
|
|
1705
1758
|
return tables.reduce((acc, codeTable) => {
|
|
1759
|
+
if (codeTable.internal.generatorIgnored) return acc;
|
|
1706
1760
|
const tableSchema = codeTable.q.schema ?? currentSchema;
|
|
1707
1761
|
if (!dbStructure.tables.some((t) => t.name === codeTable.table && t.schemaName === tableSchema)) acc.push(codeTable);
|
|
1708
1762
|
return acc;
|
|
@@ -1722,7 +1776,7 @@ const collectChangeAndDropTables = (adapter, config, tables, dbStructure, curren
|
|
|
1722
1776
|
});
|
|
1723
1777
|
const { schema: migrationsSchema = "public", table: migrationsTable } = (0, rake_db.getMigrationsSchemaAndTable)(adapter, config);
|
|
1724
1778
|
for (const dbTable of dbStructure.tables) {
|
|
1725
|
-
if (dbTable.name === migrationsTable && dbTable.schemaName === migrationsSchema || generatorIgnore?.schemas?.includes(dbTable.schemaName) || ignoreTables?.some(({ schema, table }) => table === dbTable.name && schema === dbTable.schemaName)) continue;
|
|
1779
|
+
if (dbTable.name === migrationsTable && dbTable.schemaName === migrationsSchema || generatorIgnore?.schemas?.includes(dbTable.schemaName) || ignoreTables?.some(({ schema, table }) => table === dbTable.name && schema === dbTable.schemaName) || isDefinitionIgnoredDbTable(tables, dbTable, currentSchema)) continue;
|
|
1726
1780
|
const codeTable = tables.find((t) => t.table === dbTable.name && (t.q.schema ?? currentSchema) === dbTable.schemaName);
|
|
1727
1781
|
if (codeTable) {
|
|
1728
1782
|
addChangeTable(dbStructure, changeTables, tableShapes, currentSchema, dbTable, codeTable);
|
|
@@ -1747,6 +1801,15 @@ const collectChangeAndDropTables = (adapter, config, tables, dbStructure, curren
|
|
|
1747
1801
|
tableShapes
|
|
1748
1802
|
};
|
|
1749
1803
|
};
|
|
1804
|
+
const isDefinitionIgnoredDbTable = (tables, dbTable, currentSchema) => {
|
|
1805
|
+
let hasIgnoredSameName = false;
|
|
1806
|
+
for (const codeTable of tables) {
|
|
1807
|
+
if (codeTable.table !== dbTable.name) continue;
|
|
1808
|
+
if ((codeTable.q.schema ?? currentSchema) === dbTable.schemaName) return !!codeTable.internal.generatorIgnored;
|
|
1809
|
+
if (codeTable.internal.generatorIgnored) hasIgnoredSameName = true;
|
|
1810
|
+
}
|
|
1811
|
+
return hasIgnoredSameName;
|
|
1812
|
+
};
|
|
1750
1813
|
const applyChangeTableSchemas = (changeTableSchemas, currentSchema, ast) => {
|
|
1751
1814
|
for (const { codeTable, dbTable } of changeTableSchemas) {
|
|
1752
1815
|
const fromSchema = dbTable.schemaName;
|
|
@@ -1761,7 +1824,7 @@ const applyChangeTableSchemas = (changeTableSchemas, currentSchema, ast) => {
|
|
|
1761
1824
|
});
|
|
1762
1825
|
}
|
|
1763
1826
|
};
|
|
1764
|
-
const applyChangeTables = async (adapter, changeTables, structureToAstCtx, dbStructure, domainsMap, ast, currentSchema, config, compareSql,
|
|
1827
|
+
const applyChangeTables = async (adapter, changeTables, structureToAstCtx, dbStructure, domainsMap, ast, currentSchema, config, compareSql, expressions, verifying, pendingDbTypes) => {
|
|
1765
1828
|
const compareExpressions = [];
|
|
1766
1829
|
const typeCastsCache = {};
|
|
1767
1830
|
for (const changeTableData of changeTables) {
|
|
@@ -1783,7 +1846,7 @@ const applyChangeTables = async (adapter, changeTables, structureToAstCtx, dbStr
|
|
|
1783
1846
|
}
|
|
1784
1847
|
const tableName = codeTable.table;
|
|
1785
1848
|
const source = `(VALUES (${types.map((x) => `NULL::${x}`).join(", ")})) "${tableName}"(${names.map((x) => `"${x}"`).join(", ")})`;
|
|
1786
|
-
|
|
1849
|
+
expressions.push(...compareExpressions.map((x) => ({
|
|
1787
1850
|
...x,
|
|
1788
1851
|
source
|
|
1789
1852
|
})));
|
|
@@ -2405,13 +2468,16 @@ const normalizeRoleName = (name) => {
|
|
|
2405
2468
|
};
|
|
2406
2469
|
const getSchemaWideTargets = (dbStructure, targetKey, schemas) => {
|
|
2407
2470
|
const selectedSchemas = new Set(schemas);
|
|
2408
|
-
if (targetKey === "tables")
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2471
|
+
if (targetKey === "tables") {
|
|
2472
|
+
const views = dbStructure.views || [];
|
|
2473
|
+
return [...dbStructure.tables.map((table) => ({
|
|
2474
|
+
target: `${table.schemaName}.${table.name}`,
|
|
2475
|
+
outputTarget: `${table.schemaName}.${table.name}`
|
|
2476
|
+
})), ...views.map((view) => ({
|
|
2477
|
+
target: `${view.schemaName}.${view.name}`,
|
|
2478
|
+
outputTarget: `${view.schemaName}.${view.name}`
|
|
2479
|
+
}))].filter((item) => selectedSchemas.has(item.target.split(".")[0]));
|
|
2480
|
+
}
|
|
2415
2481
|
if (targetKey === "sequences" || targetKey === "routines") return getSchemaWideGrantTargets(dbStructure, targetKey, selectedSchemas);
|
|
2416
2482
|
return [];
|
|
2417
2483
|
};
|
|
@@ -2478,6 +2544,264 @@ const addGrantAst = (ast, action, grant, privileges, privilegeKey) => {
|
|
|
2478
2544
|
grantedBy: grant.grantedBy
|
|
2479
2545
|
});
|
|
2480
2546
|
};
|
|
2547
|
+
const processViews = async (ast, adapter, dbStructure, { codeItems: { views: allViews }, currentSchema, internal: { generatorIgnore } }) => {
|
|
2548
|
+
const createViews = [];
|
|
2549
|
+
const changeViews = [];
|
|
2550
|
+
const dropViews = [];
|
|
2551
|
+
const ignoredViews = makeIgnoredViews$1(generatorIgnore, currentSchema);
|
|
2552
|
+
const views = allViews.filter((view) => !view.materialized);
|
|
2553
|
+
for (const codeView of views) {
|
|
2554
|
+
if (isIgnoredView$1(ignoredViews, generatorIgnore, currentSchema, codeView)) continue;
|
|
2555
|
+
const schemaName = codeView.q.schema ?? currentSchema;
|
|
2556
|
+
const dbView = dbStructure.views?.find((view) => view.schemaName === schemaName && view.name === codeView.name);
|
|
2557
|
+
if (dbView) changeViews.push({
|
|
2558
|
+
codeView,
|
|
2559
|
+
dbView
|
|
2560
|
+
});
|
|
2561
|
+
else createViews.push(codeView);
|
|
2562
|
+
}
|
|
2563
|
+
for (const dbView of dbStructure.views ?? []) {
|
|
2564
|
+
if (generatorIgnore?.schemas?.includes(dbView.schemaName) || ignoredViews.some((ignore) => matchesIgnoredView$1(ignore, dbView, currentSchema))) continue;
|
|
2565
|
+
if (!views.find((view) => view.name === dbView.name && (view.q.schema ?? currentSchema) === dbView.schemaName)) dropViews.push(dbView);
|
|
2566
|
+
}
|
|
2567
|
+
for (const codeView of createViews) ast.push(codeViewToAst$1(codeView, currentSchema, "create"));
|
|
2568
|
+
await applyChangeViews$1(ast, adapter, changeViews, currentSchema);
|
|
2569
|
+
for (const dbView of dropViews) ast.push(dbViewToAst$1(dbView, currentSchema, "drop"));
|
|
2570
|
+
};
|
|
2571
|
+
const applyChangeViews$1 = async (ast, adapter, changeViews, currentSchema) => {
|
|
2572
|
+
const compare = [];
|
|
2573
|
+
for (const { codeView, dbView } of changeViews) {
|
|
2574
|
+
const from = dbViewToAst$1(dbView, currentSchema, "drop");
|
|
2575
|
+
const to = codeViewToAst$1(codeView, currentSchema, "create");
|
|
2576
|
+
if (!isViewOptionsEqual(from.options, to.options)) {
|
|
2577
|
+
pushRecreateView$1(ast, from, to);
|
|
2578
|
+
continue;
|
|
2579
|
+
}
|
|
2580
|
+
const codeSql = typeof to.sql === "string" ? to.sql : to.sql.toSQL({ values: [] });
|
|
2581
|
+
compare.push({
|
|
2582
|
+
inDb: dbView.sql,
|
|
2583
|
+
inCode: codeSql,
|
|
2584
|
+
ast: to,
|
|
2585
|
+
onNotEqual() {
|
|
2586
|
+
pushRecreateView$1(ast, from, to);
|
|
2587
|
+
}
|
|
2588
|
+
});
|
|
2589
|
+
}
|
|
2590
|
+
await compareViewsExpressions(adapter, compare);
|
|
2591
|
+
};
|
|
2592
|
+
const pushRecreateView$1 = (ast, from, to) => {
|
|
2593
|
+
ast.push(from, to);
|
|
2594
|
+
};
|
|
2595
|
+
const codeViewToAst$1 = (view, currentSchema, action) => {
|
|
2596
|
+
const schema = view.q.schema ?? currentSchema;
|
|
2597
|
+
const sql = viewDataToSql(view.viewData, view.name);
|
|
2598
|
+
return {
|
|
2599
|
+
type: "view",
|
|
2600
|
+
action,
|
|
2601
|
+
schema: schema === currentSchema ? void 0 : schema,
|
|
2602
|
+
name: view.name,
|
|
2603
|
+
shape: view.shape,
|
|
2604
|
+
sql,
|
|
2605
|
+
options: {
|
|
2606
|
+
recursive: view.viewData.recursive,
|
|
2607
|
+
columns: Object.keys(view.shape),
|
|
2608
|
+
checkOption: view.viewData.checkOption,
|
|
2609
|
+
securityBarrier: view.viewData.securityBarrier,
|
|
2610
|
+
securityInvoker: view.viewData.securityInvoker ?? true
|
|
2611
|
+
},
|
|
2612
|
+
deps: []
|
|
2613
|
+
};
|
|
2614
|
+
};
|
|
2615
|
+
const dbViewToAst$1 = (view, currentSchema, action) => {
|
|
2616
|
+
return {
|
|
2617
|
+
type: "view",
|
|
2618
|
+
action,
|
|
2619
|
+
schema: view.schemaName === currentSchema ? void 0 : view.schemaName,
|
|
2620
|
+
name: view.name,
|
|
2621
|
+
shape: {},
|
|
2622
|
+
sql: (0, pqb_internal.raw)({ raw: view.sql }),
|
|
2623
|
+
options: dbViewOptionsToAst(view),
|
|
2624
|
+
deps: view.deps
|
|
2625
|
+
};
|
|
2626
|
+
};
|
|
2627
|
+
const dbViewOptionsToAst = (view) => {
|
|
2628
|
+
const options = {};
|
|
2629
|
+
options.columns = view.columns.map((column) => column.name);
|
|
2630
|
+
if (view.isRecursive) options.recursive = true;
|
|
2631
|
+
if (view.with) for (const pair of view.with) {
|
|
2632
|
+
const [key, value] = pair.split("=");
|
|
2633
|
+
switch (key) {
|
|
2634
|
+
case "check_option":
|
|
2635
|
+
if (value === "LOCAL" || value === "CASCADED") options.checkOption = value;
|
|
2636
|
+
break;
|
|
2637
|
+
case "security_barrier":
|
|
2638
|
+
options.securityBarrier = value === "true";
|
|
2639
|
+
break;
|
|
2640
|
+
case "security_invoker":
|
|
2641
|
+
options.securityInvoker = value === "true";
|
|
2642
|
+
break;
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
return options;
|
|
2646
|
+
};
|
|
2647
|
+
const isViewOptionsEqual = (from, to) => {
|
|
2648
|
+
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);
|
|
2649
|
+
};
|
|
2650
|
+
const isStringArrayEqual$1 = (a, b) => {
|
|
2651
|
+
if ((a?.length ?? 0) !== (b?.length ?? 0)) return false;
|
|
2652
|
+
if (!a?.length && !b?.length) return true;
|
|
2653
|
+
return !!a?.every((item, i) => item === b?.[i]);
|
|
2654
|
+
};
|
|
2655
|
+
const makeIgnoredViews$1 = (generatorIgnore, currentSchema) => {
|
|
2656
|
+
const views = generatorIgnore?.views;
|
|
2657
|
+
if (!views) return [];
|
|
2658
|
+
return views.map((name) => {
|
|
2659
|
+
if (typeof name !== "string") return { name };
|
|
2660
|
+
const parts = name.split(".");
|
|
2661
|
+
return parts.length === 2 ? {
|
|
2662
|
+
schema: parts[0],
|
|
2663
|
+
name: parts[1]
|
|
2664
|
+
} : {
|
|
2665
|
+
schema: currentSchema,
|
|
2666
|
+
name
|
|
2667
|
+
};
|
|
2668
|
+
});
|
|
2669
|
+
};
|
|
2670
|
+
const isIgnoredView$1 = (ignoredViews, generatorIgnore, currentSchema, codeView) => {
|
|
2671
|
+
const schemaName = codeView.q.schema ?? currentSchema;
|
|
2672
|
+
return generatorIgnore?.schemas?.includes(schemaName) === true || ignoredViews.some((ignore) => {
|
|
2673
|
+
return matchesIgnoredView$1(ignore, {
|
|
2674
|
+
schemaName,
|
|
2675
|
+
name: codeView.name
|
|
2676
|
+
}, currentSchema);
|
|
2677
|
+
});
|
|
2678
|
+
};
|
|
2679
|
+
const matchesIgnoredView$1 = (ignore, view, currentSchema) => {
|
|
2680
|
+
return typeof ignore.name === "string" ? ignore.schema === view.schemaName && ignore.name === view.name : ignore.name.test(normalizedViewName$1(view, currentSchema));
|
|
2681
|
+
};
|
|
2682
|
+
const normalizedViewName$1 = (view, currentSchema) => {
|
|
2683
|
+
return view.schemaName === currentSchema ? view.name : `${view.schemaName}.${view.name}`;
|
|
2684
|
+
};
|
|
2685
|
+
const processMaterializedViews = async (ast, adapter, dbStructure, { codeItems: { views: allViews }, currentSchema, internal: { generatorIgnore } }) => {
|
|
2686
|
+
const views = allViews.filter((view) => view.materialized);
|
|
2687
|
+
const createViews = [];
|
|
2688
|
+
const changeViews = [];
|
|
2689
|
+
const dropViews = [];
|
|
2690
|
+
const ignoredViews = makeIgnoredViews(generatorIgnore, currentSchema);
|
|
2691
|
+
for (const codeView of views) {
|
|
2692
|
+
if (isIgnoredView(ignoredViews, generatorIgnore, currentSchema, codeView)) continue;
|
|
2693
|
+
const schemaName = codeView.q.schema ?? currentSchema;
|
|
2694
|
+
const dbView = dbStructure.materializedViews?.find((view) => view.schemaName === schemaName && view.name === codeView.name);
|
|
2695
|
+
if (dbView) changeViews.push({
|
|
2696
|
+
codeView,
|
|
2697
|
+
dbView
|
|
2698
|
+
});
|
|
2699
|
+
else createViews.push(codeView);
|
|
2700
|
+
}
|
|
2701
|
+
for (const dbView of dbStructure.materializedViews ?? []) {
|
|
2702
|
+
if (generatorIgnore?.schemas?.includes(dbView.schemaName) || ignoredViews.some((ignore) => matchesIgnoredView(ignore, dbView, currentSchema))) continue;
|
|
2703
|
+
if (!views.find((view) => view.name === dbView.name && (view.q.schema ?? currentSchema) === dbView.schemaName)) dropViews.push(dbView);
|
|
2704
|
+
}
|
|
2705
|
+
for (const codeView of createViews) ast.push(codeViewToAst(codeView, currentSchema, "create"));
|
|
2706
|
+
await applyChangeViews(ast, adapter, changeViews, currentSchema);
|
|
2707
|
+
for (const dbView of dropViews) ast.push(dbViewToAst(dbView, currentSchema, "drop"));
|
|
2708
|
+
};
|
|
2709
|
+
const applyChangeViews = async (ast, adapter, changeViews, currentSchema) => {
|
|
2710
|
+
const compare = [];
|
|
2711
|
+
for (const { codeView, dbView } of changeViews) {
|
|
2712
|
+
const from = dbViewToAst(dbView, currentSchema, "drop");
|
|
2713
|
+
const to = codeViewToAst(codeView, currentSchema, "create");
|
|
2714
|
+
if (!isMaterializedViewOptionsEqual(from.options, to.options)) {
|
|
2715
|
+
pushRecreateView(ast, from, to);
|
|
2716
|
+
continue;
|
|
2717
|
+
}
|
|
2718
|
+
const codeSql = typeof to.sql === "string" ? to.sql : to.sql.toSQL({ values: [] });
|
|
2719
|
+
compare.push({
|
|
2720
|
+
inDb: dbView.sql,
|
|
2721
|
+
inCode: codeSql,
|
|
2722
|
+
ast: to,
|
|
2723
|
+
onNotEqual() {
|
|
2724
|
+
pushRecreateView(ast, from, to);
|
|
2725
|
+
}
|
|
2726
|
+
});
|
|
2727
|
+
}
|
|
2728
|
+
await compareViewsExpressions(adapter, compare);
|
|
2729
|
+
};
|
|
2730
|
+
const pushRecreateView = (ast, from, to) => {
|
|
2731
|
+
ast.push(from, to);
|
|
2732
|
+
};
|
|
2733
|
+
const codeViewToAst = (view, currentSchema, action) => {
|
|
2734
|
+
const schema = view.q.schema ?? currentSchema;
|
|
2735
|
+
const sql = viewDataToSql(view.viewData, view.name);
|
|
2736
|
+
return {
|
|
2737
|
+
type: "materializedView",
|
|
2738
|
+
action,
|
|
2739
|
+
schema: schema === currentSchema ? void 0 : schema,
|
|
2740
|
+
name: view.name,
|
|
2741
|
+
shape: view.shape,
|
|
2742
|
+
sql,
|
|
2743
|
+
options: {
|
|
2744
|
+
columns: Object.keys(view.shape),
|
|
2745
|
+
withData: view.viewData.withData
|
|
2746
|
+
},
|
|
2747
|
+
deps: []
|
|
2748
|
+
};
|
|
2749
|
+
};
|
|
2750
|
+
const dbViewToAst = (view, currentSchema, action) => {
|
|
2751
|
+
return {
|
|
2752
|
+
type: "materializedView",
|
|
2753
|
+
action,
|
|
2754
|
+
schema: view.schemaName === currentSchema ? void 0 : view.schemaName,
|
|
2755
|
+
name: view.name,
|
|
2756
|
+
shape: {},
|
|
2757
|
+
sql: (0, pqb_internal.raw)({ raw: view.sql }),
|
|
2758
|
+
options: dbMaterializedViewOptionsToAst(view),
|
|
2759
|
+
deps: view.deps
|
|
2760
|
+
};
|
|
2761
|
+
};
|
|
2762
|
+
const dbMaterializedViewOptionsToAst = (view) => {
|
|
2763
|
+
const options = { columns: view.columns.map((column) => column.name) };
|
|
2764
|
+
if (!view.isPopulated) options.withData = false;
|
|
2765
|
+
return options;
|
|
2766
|
+
};
|
|
2767
|
+
const isMaterializedViewOptionsEqual = (from, to) => {
|
|
2768
|
+
return (from.withData ?? true) === (to.withData ?? true) && isStringArrayEqual(from.columns, to.columns);
|
|
2769
|
+
};
|
|
2770
|
+
const isStringArrayEqual = (a, b) => {
|
|
2771
|
+
if ((a?.length ?? 0) !== (b?.length ?? 0)) return false;
|
|
2772
|
+
if (!a?.length && !b?.length) return true;
|
|
2773
|
+
return !!a?.every((item, i) => item === b?.[i]);
|
|
2774
|
+
};
|
|
2775
|
+
const makeIgnoredViews = (generatorIgnore, currentSchema) => {
|
|
2776
|
+
const views = generatorIgnore?.views;
|
|
2777
|
+
if (!views) return [];
|
|
2778
|
+
return views.map((name) => {
|
|
2779
|
+
if (typeof name !== "string") return { name };
|
|
2780
|
+
const parts = name.split(".");
|
|
2781
|
+
return parts.length === 2 ? {
|
|
2782
|
+
schema: parts[0],
|
|
2783
|
+
name: parts[1]
|
|
2784
|
+
} : {
|
|
2785
|
+
schema: currentSchema,
|
|
2786
|
+
name
|
|
2787
|
+
};
|
|
2788
|
+
});
|
|
2789
|
+
};
|
|
2790
|
+
const isIgnoredView = (ignoredViews, generatorIgnore, currentSchema, codeView) => {
|
|
2791
|
+
const schemaName = codeView.q.schema ?? currentSchema;
|
|
2792
|
+
return generatorIgnore?.schemas?.includes(schemaName) === true || ignoredViews.some((ignore) => {
|
|
2793
|
+
return matchesIgnoredView(ignore, {
|
|
2794
|
+
schemaName,
|
|
2795
|
+
name: codeView.name
|
|
2796
|
+
}, currentSchema);
|
|
2797
|
+
});
|
|
2798
|
+
};
|
|
2799
|
+
const matchesIgnoredView = (ignore, view, currentSchema) => {
|
|
2800
|
+
return typeof ignore.name === "string" ? ignore.schema === view.schemaName && ignore.name === view.name : ignore.name.test(normalizedViewName(view, currentSchema));
|
|
2801
|
+
};
|
|
2802
|
+
const normalizedViewName = (view, currentSchema) => {
|
|
2803
|
+
return view.schemaName === currentSchema ? view.name : `${view.schemaName}.${view.name}`;
|
|
2804
|
+
};
|
|
2481
2805
|
/**
|
|
2482
2806
|
* This is needed to compare SQLs of table expressions.
|
|
2483
2807
|
* Need to exclude table columns of pending types, such as enums or domains,
|
|
@@ -2504,6 +2828,8 @@ const composeMigration = async (adapter, config, ast, dbStructure, params) => {
|
|
|
2504
2828
|
await processDomains(ast, adapter, domainsMap, dbStructure, params, pendingDbTypes);
|
|
2505
2829
|
await processEnums(ast, dbStructure, params, pendingDbTypes);
|
|
2506
2830
|
await processTables(ast, domainsMap, adapter, dbStructure, config, params, pendingDbTypes);
|
|
2831
|
+
await processViews(ast, adapter, dbStructure, params);
|
|
2832
|
+
await processMaterializedViews(ast, adapter, dbStructure, params);
|
|
2507
2833
|
return (0, rake_db.astToMigration)(currentSchema, config, ast);
|
|
2508
2834
|
};
|
|
2509
2835
|
const rollbackErr = /* @__PURE__ */ new Error("Rollback");
|
|
@@ -2679,7 +3005,12 @@ const report = (ast, config, currentSchema) => {
|
|
|
2679
3005
|
case "domain":
|
|
2680
3006
|
code.push(`${a.action === "create" ? green("+ create domain") : red("- drop domain")} ${dbItemName(a, currentSchema)}`);
|
|
2681
3007
|
break;
|
|
3008
|
+
case "materializedView":
|
|
3009
|
+
code.push(`${a.action === "create" ? green("+ create materialized view") : red("- drop materialized view")} ${dbItemName(a, currentSchema)}`);
|
|
3010
|
+
break;
|
|
2682
3011
|
case "view":
|
|
3012
|
+
code.push(`${a.action === "create" ? green("+ create view") : red("- drop view")} ${dbItemName(a, currentSchema)}`);
|
|
3013
|
+
break;
|
|
2683
3014
|
case "collation":
|
|
2684
3015
|
case "constraint": break;
|
|
2685
3016
|
case "renameTableItem":
|
|
@@ -2837,7 +3168,8 @@ const generate = async (adapters, config, args, afterPull) => {
|
|
|
2837
3168
|
const { columnTypes, internal } = db.$qb;
|
|
2838
3169
|
const structureParams = {
|
|
2839
3170
|
loadDefaultPrivileges: internal.roles?.some((role) => role.defaultPrivileges !== void 0) ?? false,
|
|
2840
|
-
loadGrants: !!internal.grants ||
|
|
3171
|
+
loadGrants: !!internal.grants || hasCodeItemsWithGrants(db),
|
|
3172
|
+
loadViews: hasCodeViews(db)
|
|
2841
3173
|
};
|
|
2842
3174
|
const rolesDbStructureParam = internal.roles ? internal.managedRolesSql ? { whereSql: internal.managedRolesSql } : pqb_internal.emptyObject : void 0;
|
|
2843
3175
|
const { dbStructure } = await migrateAndPullStructures(adapters, config, db, rolesDbStructureParam, structureParams, afterPull);
|
|
@@ -2845,6 +3177,7 @@ const generate = async (adapters, config, args, afterPull) => {
|
|
|
2845
3177
|
const adapterSchema = adapter.getSchema();
|
|
2846
3178
|
const currentSchema = (typeof adapterSchema === "function" ? adapterSchema() : adapterSchema) ?? "public";
|
|
2847
3179
|
const codeItems = await getActualItems(db, currentSchema, internal, columnTypes);
|
|
3180
|
+
const generatorIgnore = getGeneratorIgnoreWithDefinitionIgnoredTableLikes(internal.generatorIgnore, codeItems.tables, codeItems.views);
|
|
2848
3181
|
const effectiveGrants = getEffectiveGrants(internal.grants, codeItems);
|
|
2849
3182
|
const generateMigrationParams = {
|
|
2850
3183
|
structureToAstCtx: (0, rake_db.makeStructureToAstCtx)(config, currentSchema),
|
|
@@ -2852,6 +3185,7 @@ const generate = async (adapters, config, args, afterPull) => {
|
|
|
2852
3185
|
currentSchema,
|
|
2853
3186
|
internal: {
|
|
2854
3187
|
...internal,
|
|
3188
|
+
generatorIgnore,
|
|
2855
3189
|
grants: effectiveGrants
|
|
2856
3190
|
}
|
|
2857
3191
|
};
|
|
@@ -2902,6 +3236,7 @@ const migrateAndPullStructures = async (adapters, config, db, roles, structurePa
|
|
|
2902
3236
|
schemas: [],
|
|
2903
3237
|
tables: [],
|
|
2904
3238
|
views: [],
|
|
3239
|
+
materializedViews: [],
|
|
2905
3240
|
indexes: [],
|
|
2906
3241
|
excludes: [],
|
|
2907
3242
|
constraints: [],
|
|
@@ -2916,7 +3251,8 @@ const migrateAndPullStructures = async (adapters, config, db, roles, structurePa
|
|
|
2916
3251
|
rls: hasCodeTablesWithRls(db),
|
|
2917
3252
|
roles,
|
|
2918
3253
|
loadDefaultPrivileges: structureParams?.loadDefaultPrivileges,
|
|
2919
|
-
loadGrants: structureParams?.loadGrants
|
|
3254
|
+
loadGrants: structureParams?.loadGrants,
|
|
3255
|
+
loadViews: structureParams?.loadViews
|
|
2920
3256
|
})));
|
|
2921
3257
|
const dbStructure = dbStructures[0];
|
|
2922
3258
|
for (let i = 1; i < dbStructures.length; i++) compareDbStructures(dbStructure, dbStructures[i], i);
|
|
@@ -2929,11 +3265,17 @@ const hasCodeTablesWithRls = (db) => {
|
|
|
2929
3265
|
}
|
|
2930
3266
|
return false;
|
|
2931
3267
|
};
|
|
2932
|
-
const
|
|
3268
|
+
const hasCodeViews = (db) => {
|
|
3269
|
+
return !!Object.keys(db.$views ?? {}).length;
|
|
3270
|
+
};
|
|
3271
|
+
const hasCodeItemsWithGrants = (db) => {
|
|
2933
3272
|
for (const key in db) {
|
|
2934
3273
|
if (key[0] === "$") continue;
|
|
2935
3274
|
if (db[key].internal.tableGrants?.length) return true;
|
|
2936
3275
|
}
|
|
3276
|
+
const views = db.$views;
|
|
3277
|
+
if (!views) return false;
|
|
3278
|
+
for (const key in views) if (views[key].internal.tableGrants?.length) return true;
|
|
2937
3279
|
return false;
|
|
2938
3280
|
};
|
|
2939
3281
|
const getEffectiveGrants = (grants, codeItems) => {
|
|
@@ -2951,8 +3293,41 @@ const getEffectiveGrants = (grants, codeItems) => {
|
|
|
2951
3293
|
effectiveGrants.push(internalGrant);
|
|
2952
3294
|
}
|
|
2953
3295
|
}
|
|
3296
|
+
for (const view of codeItems.views) {
|
|
3297
|
+
const viewGrants = view.internal.tableGrants;
|
|
3298
|
+
if (!viewGrants?.length) continue;
|
|
3299
|
+
const viewTarget = view.q.schema ? `${view.q.schema}.${view.name}` : view.name;
|
|
3300
|
+
for (const grant of viewGrants) {
|
|
3301
|
+
const internalGrant = {
|
|
3302
|
+
...grant,
|
|
3303
|
+
to: (0, pqb_internal.toArray)(grant.to),
|
|
3304
|
+
tables: [viewTarget]
|
|
3305
|
+
};
|
|
3306
|
+
effectiveGrants.push(internalGrant);
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
2954
3309
|
return effectiveGrants.length ? effectiveGrants : void 0;
|
|
2955
3310
|
};
|
|
3311
|
+
const getGeneratorIgnoreWithDefinitionIgnoredTableLikes = (generatorIgnore, tables, views) => {
|
|
3312
|
+
const ignoredTables = new Set(generatorIgnore?.tables);
|
|
3313
|
+
const ignoredViews = new Set(generatorIgnore?.views);
|
|
3314
|
+
let hasDefinitionIgnoredTableLikes = false;
|
|
3315
|
+
for (const table of tables) {
|
|
3316
|
+
if (!table.internal.generatorIgnored) continue;
|
|
3317
|
+
hasDefinitionIgnoredTableLikes = true;
|
|
3318
|
+
ignoredTables.add(table.q.schema ? `${table.q.schema}.${table.table}` : table.table);
|
|
3319
|
+
}
|
|
3320
|
+
for (const view of views) {
|
|
3321
|
+
if (!view.internal.generatorIgnored) continue;
|
|
3322
|
+
hasDefinitionIgnoredTableLikes = true;
|
|
3323
|
+
ignoredViews.add(view.q.schema ? `${view.q.schema}.${view.name}` : view.name);
|
|
3324
|
+
}
|
|
3325
|
+
return hasDefinitionIgnoredTableLikes ? {
|
|
3326
|
+
...generatorIgnore,
|
|
3327
|
+
tables: [...ignoredTables],
|
|
3328
|
+
views: [...ignoredViews]
|
|
3329
|
+
} : generatorIgnore;
|
|
3330
|
+
};
|
|
2956
3331
|
const compareDbStructures = (a, b, i, path) => {
|
|
2957
3332
|
let err;
|
|
2958
3333
|
if (typeof a !== typeof b) err = true;
|
|
@@ -2969,6 +3344,7 @@ const getActualItems = async (db, currentSchema, internal, columnTypes) => {
|
|
|
2969
3344
|
schemas: /* @__PURE__ */ new Set(void 0),
|
|
2970
3345
|
enums: /* @__PURE__ */ new Map(),
|
|
2971
3346
|
tables: [],
|
|
3347
|
+
views: [],
|
|
2972
3348
|
domains: []
|
|
2973
3349
|
};
|
|
2974
3350
|
codeItems.schemas.add(currentSchema);
|
|
@@ -2994,25 +3370,26 @@ const getActualItems = async (db, currentSchema, internal, columnTypes) => {
|
|
|
2994
3370
|
},
|
|
2995
3371
|
q: { schema: (0, pqb_internal.getQuerySchema)(table) }
|
|
2996
3372
|
});
|
|
2997
|
-
for (const key in table.relations) {
|
|
3373
|
+
if (!table.internal.generatorIgnored) for (const key in table.relations) {
|
|
2998
3374
|
const column = table.shape[key];
|
|
2999
3375
|
if (column && "joinTable" in column) processHasAndBelongsToManyColumn(column, habtmTables, codeItems);
|
|
3000
3376
|
}
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
}
|
|
3015
|
-
}
|
|
3377
|
+
processCodeItemShape(table.shape, currentSchema, codeItems, domains);
|
|
3378
|
+
}
|
|
3379
|
+
const views = db.$views;
|
|
3380
|
+
if (views) for (const key in views) {
|
|
3381
|
+
const view = views[key];
|
|
3382
|
+
const schema = (0, pqb_internal.getQuerySchema)(view);
|
|
3383
|
+
if (schema) codeItems.schemas.add(schema);
|
|
3384
|
+
codeItems.views.push({
|
|
3385
|
+
name: view.table,
|
|
3386
|
+
shape: view.shape,
|
|
3387
|
+
internal: view.internal,
|
|
3388
|
+
q: { schema },
|
|
3389
|
+
materialized: view.internal.materialized,
|
|
3390
|
+
viewData: view.internal.viewData ?? {}
|
|
3391
|
+
});
|
|
3392
|
+
processCodeItemShape(view.shape, currentSchema, codeItems, domains);
|
|
3016
3393
|
}
|
|
3017
3394
|
if (internal.extensions) for (const extension of internal.extensions) {
|
|
3018
3395
|
const [schema] = (0, rake_db.getSchemaAndTableFromName)(currentSchema, extension.name);
|
|
@@ -3039,6 +3416,23 @@ const getActualItems = async (db, currentSchema, internal, columnTypes) => {
|
|
|
3039
3416
|
if (internal.grants) for (const grant of internal.grants) addGrantSchemas(codeItems.schemas, currentSchema, grant);
|
|
3040
3417
|
return codeItems;
|
|
3041
3418
|
};
|
|
3419
|
+
const processCodeItemShape = (shape, currentSchema, codeItems, domains) => {
|
|
3420
|
+
for (const key in shape) {
|
|
3421
|
+
const column = shape[key];
|
|
3422
|
+
if (column.data.computed) delete shape[key];
|
|
3423
|
+
else if (column instanceof pqb_internal.DomainColumn) {
|
|
3424
|
+
const [schemaName = currentSchema, name] = (0, rake_db.getSchemaAndTableFromName)(currentSchema, column.dataType);
|
|
3425
|
+
domains.set(column.dataType, {
|
|
3426
|
+
schemaName,
|
|
3427
|
+
name,
|
|
3428
|
+
column: column.data.as ?? pqb_internal.UnknownColumn.instance
|
|
3429
|
+
});
|
|
3430
|
+
} else {
|
|
3431
|
+
const en = column.dataType === "enum" ? column : column instanceof pqb_internal.ArrayColumn && column.data.item.dataType === "enum" ? column.data.item : void 0;
|
|
3432
|
+
if (en) processEnumColumn(en, currentSchema, codeItems);
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
};
|
|
3042
3436
|
const addGrantSchemas = (schemas, currentSchema, grant) => {
|
|
3043
3437
|
for (const schema of [
|
|
3044
3438
|
...grant.schemas ?? [],
|