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.
@@ -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 compareSqlExpressions = async (tableExpressions, adapter) => {
32
- if (!tableExpressions.length) return;
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 tableExpressions) {
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(dbStructure.views, from, schema);
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 tableExpressions = [];
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
- tableExpressions.push({
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 (tableExpressions.length) {
496
- await compareSqlExpressions(tableExpressions, adapter);
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 tableExpressions = [];
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, tableExpressions, verifying, pendingDbTypes);
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(tableExpressions, adapter)]);
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, tableExpressions, verifying, pendingDbTypes) => {
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
- tableExpressions.push(...compareExpressions.map((x) => ({
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") return [...dbStructure.tables.map((table) => ({
2409
- target: `${table.schemaName}.${table.name}`,
2410
- outputTarget: `${table.schemaName}.${table.name}`
2411
- })), ...dbStructure.views.map((view) => ({
2412
- target: `${view.schemaName}.${view.name}`,
2413
- outputTarget: `${view.schemaName}.${view.name}`
2414
- }))].filter((item) => selectedSchemas.has(item.target.split(".")[0]));
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 || hasCodeTablesWithGrants(db)
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 hasCodeTablesWithGrants = (db) => {
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
- for (const key in table.shape) {
3002
- const column = table.shape[key];
3003
- if (column.data.computed) delete table.shape[key];
3004
- else if (column instanceof pqb_internal.DomainColumn) {
3005
- const [schemaName = currentSchema, name] = (0, rake_db.getSchemaAndTableFromName)(currentSchema, column.dataType);
3006
- domains.set(column.dataType, {
3007
- schemaName,
3008
- name,
3009
- column: column.data.as ?? pqb_internal.UnknownColumn.instance
3010
- });
3011
- } else {
3012
- const en = column.dataType === "enum" ? column : column instanceof pqb_internal.ArrayColumn && column.data.item.dataType === "enum" ? column.data.item : void 0;
3013
- if (en) processEnumColumn(en, currentSchema, codeItems);
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 ?? [],