okmodel 0.2.1 → 0.4.0
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/README.md +882 -48
- package/dist/contracts/catalog/document.d.ts +6 -2
- package/dist/contracts/catalog/enum.d.ts +31 -4
- package/dist/contracts/catalog/extension.d.ts +61 -0
- package/dist/contracts/catalog/privilege.d.ts +64 -0
- package/dist/contracts/catalog/routine.d.ts +56 -0
- package/dist/contracts/catalog/types.d.ts +164 -10
- package/dist/contracts/catalog/view.d.ts +73 -0
- package/dist/dialects/pg/column.d.ts +3 -1
- package/dist/dialects/pg/ddl.d.ts +65 -1
- package/dist/dialects/pg/enum.d.ts +10 -8
- package/dist/dialects/pg/ext/citext.d.ts +14 -0
- package/dist/dialects/pg/ext/citext.js +11 -0
- package/dist/dialects/pg/ext/index.d.ts +52 -0
- package/dist/dialects/pg/ext/index.js +6 -0
- package/dist/dialects/pg/ext/pg-trgm.d.ts +53 -0
- package/dist/dialects/pg/ext/pg-trgm.js +42 -0
- package/dist/dialects/pg/fn/index.d.ts +107 -0
- package/dist/dialects/pg/fn/index.js +175 -0
- package/dist/dialects/pg/fn/touch.d.ts +14 -0
- package/dist/dialects/pg/index.js +104 -19
- package/dist/dialects/pg/introspect.d.ts +10 -1
- package/dist/dialects/pg/operators.d.ts +8 -1
- package/dist/dialects/pg/role/check.d.ts +45 -0
- package/dist/dialects/pg/role/index.d.ts +50 -0
- package/dist/dialects/pg/role/sql.d.ts +51 -0
- package/dist/dialects/pg/table.d.ts +24 -1
- package/dist/dialects/pg/tenancy.d.ts +9 -0
- package/dist/dialects/pg/view/client.d.ts +30 -0
- package/dist/dialects/pg/view/depend.d.ts +35 -0
- package/dist/dialects/pg/view/index.d.ts +114 -0
- package/dist/dialects/pg/view/index.js +289 -0
- package/dist/dialects/pg/view/scratch.d.ts +22 -0
- package/dist/dialects/pg/where.d.ts +2 -2
- package/dist/runtime/catalog-cache.d.ts +17 -0
- package/dist/runtime/compat-words.d.ts +23 -0
- package/dist/runtime/drift-detail.d.ts +20 -4
- package/dist/runtime/drift.d.ts +4 -2
- package/dist/runtime/pg/bun.js +3 -3
- package/dist/runtime/pg/pg.js +3 -3
- package/dist/runtime/pg/pglite.js +3 -3
- package/dist/runtime/pg/postgresjs.js +3 -3
- package/dist/runtime/tenancy/index.js +6 -3
- package/dist/runtime/traits/index.d.ts +7 -2
- package/dist/runtime/traits/index.js +151 -13
- package/dist/runtime/types.d.ts +2 -0
- package/dist/runtime/validate/index.js +4 -4
- package/dist/shared/{xkz8g6by.js → 1g0drakv.js} +2 -2
- package/dist/shared/2321avmn.js +418 -0
- package/dist/shared/{7mht49yd.js → 3p4b1fg0.js} +57 -18
- package/dist/shared/41kv2xdm.js +318 -0
- package/dist/shared/{97g2jnrw.js → 4ef7mmqf.js} +3 -3
- package/dist/shared/{sacqha1y.js → 4m9fn52z.js} +2 -2
- package/dist/shared/5mtf6cj3.js +94 -0
- package/dist/shared/{1dp5knt7.js → 5vdm4sgp.js} +2 -2
- package/dist/shared/{a1gdp7j9.js → 6693j9rs.js} +1 -1
- package/dist/shared/{38crxbgn.js → 6p5k20sm.js} +3 -3
- package/dist/shared/7nk68fj8.js +751 -0
- package/dist/shared/84ptn35q.js +71 -0
- package/dist/shared/{d2whjz4k.js → 8jb4ymh2.js} +581 -28
- package/dist/shared/8ktwd3rh.js +97 -0
- package/dist/shared/94ez8kzh.js +19 -0
- package/dist/shared/{3a5ne750.js → 9m80mkac.js} +2 -2
- package/dist/shared/9x25fxhc.js +150 -0
- package/dist/shared/9yn6a2vd.js +11 -0
- package/dist/shared/ady1ms74.js +72 -0
- package/dist/shared/{4jzzghr6.js → awrsqrn7.js} +6 -6
- package/dist/shared/{4b9da9d7.js → axhk06q1.js} +19 -1
- package/dist/shared/{d04j53v8.js → c5g81ngb.js} +82 -14
- package/dist/shared/{n47rd821.js → c8aghh05.js} +1 -1
- package/dist/shared/{dd4f8mnx.js → c9z5g0fm.js} +4 -2
- package/dist/shared/{ps43134h.js → dyj86cmj.js} +4 -4
- package/dist/shared/{0qkxbm1f.js → fa9vt245.js} +1 -1
- package/dist/shared/{qa5dsc7z.js → fb00y8fp.js} +3 -3
- package/dist/shared/{qxa0yb9j.js → g7ez901n.js} +30 -1
- package/dist/shared/{py3nh7jy.js → gdh9mrtp.js} +2 -2
- package/dist/shared/{yw5c610b.js → gjthds9j.js} +2 -2
- package/dist/shared/hcmdtwgh.js +102 -0
- package/dist/shared/hqjsxnz7.js +13 -0
- package/dist/shared/htnaj77v.js +17 -0
- package/dist/shared/{v18jva5b.js → jgxzjzcz.js} +1 -1
- package/dist/shared/{x9v1qgeb.js → jsd07prz.js} +3 -3
- package/dist/shared/{g9f0zqce.js → kafaa5zv.js} +1 -1
- package/dist/shared/kj680nxv.js +19 -0
- package/dist/shared/{mxmcdsh5.js → kkzwp3d2.js} +9 -9
- package/dist/shared/{g1b5zzj9.js → n0dgs0pb.js} +1 -1
- package/dist/shared/n5y9zyj1.js +2431 -0
- package/dist/shared/{zt8y0wzb.js → n7mcc1gk.js} +1 -1
- package/dist/shared/{ge03bfca.js → nxb42q7h.js} +2 -2
- package/dist/shared/q6zsyh31.js +2192 -0
- package/dist/shared/{sfh4pk75.js → q795afwa.js} +26 -21
- package/dist/shared/{10tjenmd.js → qn0srwwq.js} +1 -1
- package/dist/shared/s4mb6a4b.js +545 -0
- package/dist/shared/{ytg3xf9p.js → sf8m6tfy.js} +3 -3
- package/dist/shared/{zd93g3rq.js → sr5ms7b3.js} +1 -1
- package/dist/shared/{xatfg0gn.js → tmbdywx0.js} +4 -19
- package/dist/shared/tny2t3sm.js +100 -0
- package/dist/shared/{e7a268fy.js → v7tpnhnz.js} +1 -1
- package/dist/shared/{ecdzej21.js → wqem4jed.js} +7 -7
- package/dist/shared/xghkt7a9.js +1144 -0
- package/dist/shared/y005b761.js +112 -0
- package/dist/shared/ycatkxmq.js +84 -0
- package/dist/shared/ygem90rn.js +900 -0
- package/dist/shared/{eaxwrgby.js → yjw02enp.js} +6 -3
- package/dist/shared/{mrq77sgw.js → zg24x0hm.js} +1 -1
- package/dist/shared/{cstgkzdx.js → zvbjc949.js} +1 -1
- package/dist/tooling/cli.js +66 -13
- package/dist/tooling/migrate/apply.d.ts +75 -5
- package/dist/tooling/migrate/backfill.d.ts +136 -0
- package/dist/tooling/migrate/check.d.ts +65 -0
- package/dist/tooling/migrate/classify.d.ts +108 -0
- package/dist/tooling/migrate/commands.d.ts +2 -1
- package/dist/tooling/migrate/config.d.ts +30 -0
- package/dist/tooling/migrate/doctor.d.ts +28 -0
- package/dist/tooling/migrate/drift.d.ts +35 -0
- package/dist/tooling/migrate/estimate.d.ts +50 -0
- package/dist/tooling/migrate/ext.d.ts +17 -0
- package/dist/tooling/migrate/extensions.d.ts +53 -0
- package/dist/tooling/migrate/files.d.ts +12 -0
- package/dist/tooling/migrate/index.d.ts +3 -2
- package/dist/tooling/migrate/index.js +67 -1
- package/dist/tooling/migrate/lint-rules.d.ts +51 -0
- package/dist/tooling/migrate/lint.d.ts +71 -0
- package/dist/tooling/migrate/managed.d.ts +17 -0
- package/dist/tooling/migrate/plan.d.ts +78 -10
- package/dist/tooling/migrate/project.d.ts +64 -5
- package/dist/tooling/migrate/provision.d.ts +18 -0
- package/dist/tooling/migrate/reference.d.ts +40 -0
- package/dist/tooling/migrate/runner.d.ts +108 -0
- package/dist/tooling/migrate/safe.d.ts +77 -0
- package/dist/tooling/migrate/seed.d.ts +20 -0
- package/dist/tooling/migrate/snapshot.d.ts +78 -0
- package/dist/tooling/migrate/status.d.ts +25 -6
- package/dist/tooling/migrate/type-name.d.ts +19 -0
- package/dist/tooling/testing/factories.d.ts +117 -0
- package/dist/tooling/testing/facts.d.ts +72 -0
- package/dist/tooling/testing/index.d.ts +89 -0
- package/dist/tooling/testing/index.js +8 -0
- package/dist/tooling/testing/isolation.d.ts +30 -0
- package/dist/tooling/testing/random.d.ts +40 -0
- package/dist/tooling/testing/record.d.ts +38 -0
- package/package.json +26 -2
- package/dist/shared/5z4q2zda.js +0 -2073
- package/dist/shared/eqmtn1bj.js +0 -399
- package/dist/shared/rtnaqre0.js +0 -37
- package/dist/shared/zwmqxb0g.js +0 -79
- package/dist/shared/{m9tv7xn7.js → radd3z4g.js} +3 -3
- /package/dist/shared/{9k6vetvf.js → rrstp3kb.js} +0 -0
|
@@ -0,0 +1,1144 @@
|
|
|
1
|
+
import {
|
|
2
|
+
roleObject,
|
|
3
|
+
grantObject,
|
|
4
|
+
defaultPrivilegeObject
|
|
5
|
+
} from "./8jb4ymh2.js";
|
|
6
|
+
import {
|
|
7
|
+
planMigration
|
|
8
|
+
} from "./n5y9zyj1.js";
|
|
9
|
+
import {
|
|
10
|
+
quoteIdent,
|
|
11
|
+
createObjectSql
|
|
12
|
+
} from "./41kv2xdm.js";
|
|
13
|
+
import {
|
|
14
|
+
functionObject,
|
|
15
|
+
triggerObject
|
|
16
|
+
} from "./9x25fxhc.js";
|
|
17
|
+
import {
|
|
18
|
+
normaliseViewQuery,
|
|
19
|
+
viewObject,
|
|
20
|
+
materializedViewObject,
|
|
21
|
+
materializedViewIndex
|
|
22
|
+
} from "./y005b761.js";
|
|
23
|
+
import {
|
|
24
|
+
extensionObject
|
|
25
|
+
} from "./8ktwd3rh.js";
|
|
26
|
+
import {
|
|
27
|
+
catalog,
|
|
28
|
+
enumType,
|
|
29
|
+
domainType
|
|
30
|
+
} from "./g7ez901n.js";
|
|
31
|
+
import {
|
|
32
|
+
staticNamespace2,
|
|
33
|
+
identityKey2,
|
|
34
|
+
table3,
|
|
35
|
+
column2,
|
|
36
|
+
index3,
|
|
37
|
+
constraint2,
|
|
38
|
+
sequence2
|
|
39
|
+
} from "./t4tbsy9h.js";
|
|
40
|
+
import {
|
|
41
|
+
referentialAction
|
|
42
|
+
} from "./4e4c6vq2.js";
|
|
43
|
+
import {
|
|
44
|
+
OkmError2
|
|
45
|
+
} from "./52szzakm.js";
|
|
46
|
+
|
|
47
|
+
// src/tooling/migrate/drift.ts
|
|
48
|
+
import postgres from "postgres";
|
|
49
|
+
|
|
50
|
+
// src/dialects/pg/view/depend.ts
|
|
51
|
+
function cellText(value) {
|
|
52
|
+
if (typeof value === "string")
|
|
53
|
+
return value;
|
|
54
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
55
|
+
return String(value);
|
|
56
|
+
}
|
|
57
|
+
return "";
|
|
58
|
+
}
|
|
59
|
+
var VIEW_DEPENDENCIES = `
|
|
60
|
+
select v.relname as view, t.relname as table, a.attname as column
|
|
61
|
+
from pg_depend d
|
|
62
|
+
join pg_rewrite r on r.oid = d.objid and d.classid = 'pg_rewrite'::regclass
|
|
63
|
+
join pg_class v on v.oid = r.ev_class
|
|
64
|
+
join pg_class t on t.oid = d.refobjid and d.refclassid = 'pg_class'::regclass
|
|
65
|
+
join pg_attribute a on a.attrelid = t.oid and a.attnum = d.refobjsubid
|
|
66
|
+
join pg_namespace n on n.oid = v.relnamespace
|
|
67
|
+
where n.nspname = $1
|
|
68
|
+
and v.relkind in ('v', 'm')
|
|
69
|
+
and d.refobjsubid > 0
|
|
70
|
+
and v.oid <> t.oid
|
|
71
|
+
and t.relkind in ('r', 'p', 'v', 'm')
|
|
72
|
+
`;
|
|
73
|
+
function viewDependencyEdges(rows, namespace, objects) {
|
|
74
|
+
const keys = new Set(objects.map((object) => identityKey2(object.identity)));
|
|
75
|
+
const byView = new Map;
|
|
76
|
+
for (const row of rows) {
|
|
77
|
+
const view = cellText(row.view);
|
|
78
|
+
const table = cellText(row.table);
|
|
79
|
+
const column = cellText(row.column);
|
|
80
|
+
if (view.length === 0 || table.length === 0 || column.length === 0)
|
|
81
|
+
continue;
|
|
82
|
+
const columnEdge = {
|
|
83
|
+
kind: "column",
|
|
84
|
+
parent: { namespace, name: table },
|
|
85
|
+
name: column
|
|
86
|
+
};
|
|
87
|
+
const columnKey = identityKey2(columnEdge);
|
|
88
|
+
let edge = columnEdge;
|
|
89
|
+
if (!keys.has(columnKey)) {
|
|
90
|
+
const viewEdge = objectEdge(namespace, table, objects);
|
|
91
|
+
if (viewEdge === undefined)
|
|
92
|
+
continue;
|
|
93
|
+
edge = viewEdge;
|
|
94
|
+
}
|
|
95
|
+
const list = byView.get(view) ?? [];
|
|
96
|
+
list.push(edge);
|
|
97
|
+
byView.set(view, list);
|
|
98
|
+
}
|
|
99
|
+
return byView;
|
|
100
|
+
}
|
|
101
|
+
function objectEdge(namespace, name, objects) {
|
|
102
|
+
for (const object of objects) {
|
|
103
|
+
if (object.kind !== "view" && object.kind !== "materializedView")
|
|
104
|
+
continue;
|
|
105
|
+
if (object.identity.name !== name)
|
|
106
|
+
continue;
|
|
107
|
+
if (object.identity.namespace.form !== "static" || namespace.form !== "static")
|
|
108
|
+
continue;
|
|
109
|
+
if (object.identity.namespace.name !== namespace.name)
|
|
110
|
+
continue;
|
|
111
|
+
return object.identity;
|
|
112
|
+
}
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
var PUBLIC = staticNamespace2("public");
|
|
116
|
+
|
|
117
|
+
// src/dialects/pg/introspect.ts
|
|
118
|
+
var PROVENANCE_NAME = "database";
|
|
119
|
+
async function introspectSchema(runner, concrete, logical = "public", options) {
|
|
120
|
+
const namespace = staticNamespace2(logical);
|
|
121
|
+
const provenance = { origin: "file", name: PROVENANCE_NAME };
|
|
122
|
+
const params = [concrete];
|
|
123
|
+
const [
|
|
124
|
+
tables,
|
|
125
|
+
columns,
|
|
126
|
+
constraints,
|
|
127
|
+
indexes,
|
|
128
|
+
sequences,
|
|
129
|
+
enums,
|
|
130
|
+
domains,
|
|
131
|
+
extensions,
|
|
132
|
+
functions,
|
|
133
|
+
triggers,
|
|
134
|
+
functionDeps,
|
|
135
|
+
views,
|
|
136
|
+
viewDeps
|
|
137
|
+
] = await Promise.all([
|
|
138
|
+
runner.query(TABLES, params),
|
|
139
|
+
runner.query(COLUMNS, params),
|
|
140
|
+
runner.query(CONSTRAINTS, params),
|
|
141
|
+
runner.query(INDEXES, params),
|
|
142
|
+
runner.query(SEQUENCES, params),
|
|
143
|
+
runner.query(ENUMS, params),
|
|
144
|
+
runner.query(DOMAINS, params),
|
|
145
|
+
runner.query(EXTENSIONS, params),
|
|
146
|
+
runner.query(FUNCTIONS, params),
|
|
147
|
+
runner.query(TRIGGERS, params),
|
|
148
|
+
runner.query(FUNCTION_DEPS, params),
|
|
149
|
+
runner.query(VIEWS, params),
|
|
150
|
+
runner.query(VIEW_DEPENDENCIES, params)
|
|
151
|
+
]);
|
|
152
|
+
const objects = [];
|
|
153
|
+
const parents = new Map;
|
|
154
|
+
for (const row of tables) {
|
|
155
|
+
const name = text(row, "name");
|
|
156
|
+
const parent = { namespace, name };
|
|
157
|
+
parents.set(name, parent);
|
|
158
|
+
const method = partitionMethod(text(row, "partstrat"));
|
|
159
|
+
const partColumns = list(text(row, "partcols"));
|
|
160
|
+
objects.push(table3({
|
|
161
|
+
namespace,
|
|
162
|
+
name,
|
|
163
|
+
provenance,
|
|
164
|
+
...method === undefined || partColumns.length === 0 ? {} : { partition: { method, columns: partColumns } }
|
|
165
|
+
}));
|
|
166
|
+
}
|
|
167
|
+
const sequenceIdentity = new Map;
|
|
168
|
+
for (const row of sequences) {
|
|
169
|
+
const name = text(row, "name");
|
|
170
|
+
const dataType = sequenceType(text(row, "type"));
|
|
171
|
+
const ownedTable = text(row, "owned_table");
|
|
172
|
+
const dependencies = [];
|
|
173
|
+
if (ownedTable.length > 0) {
|
|
174
|
+
dependencies.push({ kind: "table", namespace, name: ownedTable });
|
|
175
|
+
}
|
|
176
|
+
const built = sequence2({
|
|
177
|
+
namespace,
|
|
178
|
+
name,
|
|
179
|
+
dataType,
|
|
180
|
+
start: numberText(row, "start"),
|
|
181
|
+
increment: numberText(row, "increment"),
|
|
182
|
+
cycle: flag(row, "cycle"),
|
|
183
|
+
provenance,
|
|
184
|
+
...dependencies.length > 0 ? { dependencies } : {}
|
|
185
|
+
});
|
|
186
|
+
sequenceIdentity.set(name, built.identity);
|
|
187
|
+
objects.push(built);
|
|
188
|
+
}
|
|
189
|
+
const enumLabels = new Map;
|
|
190
|
+
for (const row of enums) {
|
|
191
|
+
const name = text(row, "name");
|
|
192
|
+
const labels = enumLabels.get(name) ?? [];
|
|
193
|
+
labels.push(text(row, "label"));
|
|
194
|
+
enumLabels.set(name, labels);
|
|
195
|
+
}
|
|
196
|
+
for (const [name, labels] of enumLabels) {
|
|
197
|
+
objects.push(enumType({ namespace, name, labels, provenance }));
|
|
198
|
+
}
|
|
199
|
+
const domainNames = new Set;
|
|
200
|
+
const domainChecks = new Map;
|
|
201
|
+
for (const row of domains) {
|
|
202
|
+
const name = text(row, "name");
|
|
203
|
+
const found = domainChecks.get(name) ?? { base: text(row, "base"), checks: [] };
|
|
204
|
+
const expression = checkExpression(text(row, "definition"));
|
|
205
|
+
if (expression.length > 0)
|
|
206
|
+
found.checks.push(expression);
|
|
207
|
+
domainChecks.set(name, found);
|
|
208
|
+
}
|
|
209
|
+
for (const [name, domain] of domainChecks) {
|
|
210
|
+
const check = domain.checks.join(" and ");
|
|
211
|
+
if (check.length === 0)
|
|
212
|
+
continue;
|
|
213
|
+
domainNames.add(name);
|
|
214
|
+
objects.push(domainType({ namespace, name, base: domain.base, check, provenance }));
|
|
215
|
+
}
|
|
216
|
+
for (const row of columns) {
|
|
217
|
+
const parentName = text(row, "parent");
|
|
218
|
+
const parent = parents.get(parentName) ?? { namespace, name: parentName };
|
|
219
|
+
const identity = text(row, "identity");
|
|
220
|
+
const generated = text(row, "generated") === "s";
|
|
221
|
+
const expression = text(row, "expression");
|
|
222
|
+
const collation = text(row, "collation");
|
|
223
|
+
const sequenceName = text(row, "sequence");
|
|
224
|
+
const dataType = text(row, "type");
|
|
225
|
+
const extra = [];
|
|
226
|
+
const extensionName = text(row, "extension");
|
|
227
|
+
if (extensionName.length > 0)
|
|
228
|
+
extra.push({ kind: "extension", name: extensionName });
|
|
229
|
+
const owned = sequenceIdentity.get(sequenceName);
|
|
230
|
+
if (owned !== undefined)
|
|
231
|
+
extra.push(owned);
|
|
232
|
+
if (enumLabels.has(dataType) || domainNames.has(dataType)) {
|
|
233
|
+
extra.push({ kind: "type", namespace, name: dataType });
|
|
234
|
+
}
|
|
235
|
+
const built = column2({
|
|
236
|
+
parent,
|
|
237
|
+
name: text(row, "name"),
|
|
238
|
+
dataType,
|
|
239
|
+
nullable: !flag(row, "not_null"),
|
|
240
|
+
provenance,
|
|
241
|
+
...extra.length > 0 ? { dependencies: extra } : {},
|
|
242
|
+
...identity === "a" || identity === "d" ? { identity: { always: identity === "a" } } : {},
|
|
243
|
+
...generated ? { generated: { stored: true, expression } } : {},
|
|
244
|
+
...!generated && identity !== "a" && identity !== "d" && expression.length > 0 ? { defaultExpression: expression } : {},
|
|
245
|
+
...collation.length > 0 ? { collation } : {}
|
|
246
|
+
});
|
|
247
|
+
objects.push(built);
|
|
248
|
+
}
|
|
249
|
+
for (const row of constraints) {
|
|
250
|
+
const parentName = text(row, "parent");
|
|
251
|
+
const parent = parents.get(parentName) ?? { namespace, name: parentName };
|
|
252
|
+
const kind = constraintKind(text(row, "contype"));
|
|
253
|
+
const columns = list(text(row, "columns"));
|
|
254
|
+
const name = text(row, "name");
|
|
255
|
+
const expression = kind === "check" ? checkExpression(text(row, "definition")) : "";
|
|
256
|
+
const refTable = text(row, "ref_table");
|
|
257
|
+
const refColumns = list(text(row, "ref_columns"));
|
|
258
|
+
const definition = text(row, "definition");
|
|
259
|
+
const onDelete = actionAfter(definition, "delete");
|
|
260
|
+
const onUpdate = actionAfter(definition, "update");
|
|
261
|
+
objects.push(constraint2({
|
|
262
|
+
parent,
|
|
263
|
+
constraintKind: kind,
|
|
264
|
+
name,
|
|
265
|
+
nameKey: constraintNameKey(kind, parentName, name, columns),
|
|
266
|
+
columns,
|
|
267
|
+
provenance,
|
|
268
|
+
deferrable: flag(row, "deferrable"),
|
|
269
|
+
initially: flag(row, "deferred") ? "deferred" : "immediate",
|
|
270
|
+
nullsNotDistinct: /nulls not distinct/i.test(definition),
|
|
271
|
+
...expression.length > 0 ? { expression } : {},
|
|
272
|
+
...refTable.length > 0 ? {
|
|
273
|
+
references: {
|
|
274
|
+
parent: { namespace, name: refTable },
|
|
275
|
+
columns: refColumns,
|
|
276
|
+
...onDelete !== undefined ? { onDelete } : {},
|
|
277
|
+
...onUpdate !== undefined ? { onUpdate } : {}
|
|
278
|
+
}
|
|
279
|
+
} : {}
|
|
280
|
+
}));
|
|
281
|
+
}
|
|
282
|
+
const matviewNames = new Set(views.filter((row) => text(row, "kind") === "m").map((row) => text(row, "name")));
|
|
283
|
+
for (const row of indexes) {
|
|
284
|
+
const parentName = text(row, "parent");
|
|
285
|
+
const parent = parents.get(parentName) ?? { namespace, name: parentName };
|
|
286
|
+
if (matviewNames.has(parentName)) {
|
|
287
|
+
const columns = list(text(row, "columns"));
|
|
288
|
+
const predicate = text(row, "predicate");
|
|
289
|
+
const expression = indexExpression(row, columns);
|
|
290
|
+
objects.push(materializedViewIndex({
|
|
291
|
+
parent,
|
|
292
|
+
name: text(row, "name"),
|
|
293
|
+
columns,
|
|
294
|
+
unique: flag(row, "is_unique"),
|
|
295
|
+
provenance,
|
|
296
|
+
...expression.length > 0 ? { expression } : {},
|
|
297
|
+
...predicate.length > 0 ? { predicate } : {}
|
|
298
|
+
}));
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
const columns = list(text(row, "columns"));
|
|
302
|
+
const predicate = text(row, "predicate");
|
|
303
|
+
const expression = indexExpression(row, columns);
|
|
304
|
+
objects.push(index3({
|
|
305
|
+
parent,
|
|
306
|
+
name: text(row, "name"),
|
|
307
|
+
nameKey: columns.length > 0 ? columns.join("_") : "expr",
|
|
308
|
+
columns,
|
|
309
|
+
unique: flag(row, "is_unique"),
|
|
310
|
+
provenance,
|
|
311
|
+
...expression.length > 0 ? { expression } : {},
|
|
312
|
+
...predicate.length > 0 ? { predicate } : {}
|
|
313
|
+
}));
|
|
314
|
+
}
|
|
315
|
+
for (const row of extensions) {
|
|
316
|
+
const name = text(row, "name");
|
|
317
|
+
const version = text(row, "version");
|
|
318
|
+
objects.push(extensionObject({
|
|
319
|
+
name,
|
|
320
|
+
schema: text(row, "schema"),
|
|
321
|
+
relocatable: flag(row, "relocatable"),
|
|
322
|
+
...version.length > 0 ? { version } : {},
|
|
323
|
+
provenance: { origin: "extension", name }
|
|
324
|
+
}));
|
|
325
|
+
}
|
|
326
|
+
const atomicEdges = new Map;
|
|
327
|
+
for (const row of functionDeps) {
|
|
328
|
+
const key = `${text(row, "name")}(${text(row, "arg_types")})`;
|
|
329
|
+
const edges = atomicEdges.get(key) ?? [];
|
|
330
|
+
const tableName = text(row, "table");
|
|
331
|
+
const columnName = text(row, "column");
|
|
332
|
+
if (columnName.length > 0) {
|
|
333
|
+
edges.push({
|
|
334
|
+
kind: "column",
|
|
335
|
+
parent: { namespace, name: tableName },
|
|
336
|
+
name: columnName
|
|
337
|
+
});
|
|
338
|
+
} else if (tableName.length > 0) {
|
|
339
|
+
edges.push({ kind: "table", namespace, name: tableName });
|
|
340
|
+
}
|
|
341
|
+
atomicEdges.set(key, edges);
|
|
342
|
+
}
|
|
343
|
+
for (const row of functions) {
|
|
344
|
+
const name = text(row, "name");
|
|
345
|
+
const language = text(row, "language") === "sql" ? "sql" : "plpgsql";
|
|
346
|
+
const argTypes = splitTypes(text(row, "arg_types"));
|
|
347
|
+
const source = text(row, "body");
|
|
348
|
+
const atomic = language === "sql" && source.length === 0;
|
|
349
|
+
const body = atomic ? atomicBody(text(row, "definition")) : source;
|
|
350
|
+
const searchPath = searchPathOf(text(row, "config"));
|
|
351
|
+
const key = `${name}(${text(row, "arg_types")})`;
|
|
352
|
+
objects.push(functionObject({
|
|
353
|
+
namespace,
|
|
354
|
+
name,
|
|
355
|
+
arguments: argumentList(row.arg_names, argTypes),
|
|
356
|
+
returns: text(row, "returns"),
|
|
357
|
+
language,
|
|
358
|
+
volatility: volatilityOf(text(row, "volatility")),
|
|
359
|
+
security: flag(row, "security") ? "definer" : "invoker",
|
|
360
|
+
body,
|
|
361
|
+
...searchPath !== undefined ? { searchPath } : {},
|
|
362
|
+
...atomic ? { atomic: true } : {},
|
|
363
|
+
provenance,
|
|
364
|
+
...atomic ? { dependencies: atomicEdges.get(key) ?? [] } : {}
|
|
365
|
+
}));
|
|
366
|
+
}
|
|
367
|
+
for (const row of triggers) {
|
|
368
|
+
const bits = Number(text(row, "tgtype"));
|
|
369
|
+
const updateOf = list(text(row, "update_of"));
|
|
370
|
+
const when = text(row, "predicate");
|
|
371
|
+
const argTypes = splitTypes(text(row, "arg_types"));
|
|
372
|
+
objects.push(triggerObject({
|
|
373
|
+
parent: { namespace, name: text(row, "parent") },
|
|
374
|
+
name: text(row, "name"),
|
|
375
|
+
timing: triggerTiming(bits),
|
|
376
|
+
events: triggerEvents(bits),
|
|
377
|
+
level: (bits & 1) === 1 ? "row" : "statement",
|
|
378
|
+
calls: {
|
|
379
|
+
namespace: staticNamespace2(text(row, "function_schema")),
|
|
380
|
+
name: text(row, "function"),
|
|
381
|
+
argTypes
|
|
382
|
+
},
|
|
383
|
+
...updateOf.length > 0 ? { updateOf } : {},
|
|
384
|
+
...when.length > 0 ? { when } : {},
|
|
385
|
+
provenance
|
|
386
|
+
}));
|
|
387
|
+
}
|
|
388
|
+
for (const row of views) {
|
|
389
|
+
const kind = text(row, "kind") === "m" ? "materializedView" : "view";
|
|
390
|
+
const columns = printedColumns(text(row, "columns"));
|
|
391
|
+
const query = normaliseViewQuery(text(row, "query"));
|
|
392
|
+
const name = text(row, "name");
|
|
393
|
+
if (kind === "materializedView") {
|
|
394
|
+
objects.push(materializedViewObject({ namespace, name, columns, query, provenance }));
|
|
395
|
+
} else {
|
|
396
|
+
objects.push(viewObject({ namespace, name, columns, query, provenance }));
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
const viewEdges = viewDependencyEdges(viewDeps, namespace, objects);
|
|
400
|
+
for (let index = 0;index < objects.length; index += 1) {
|
|
401
|
+
const object = objects[index];
|
|
402
|
+
if (object === undefined)
|
|
403
|
+
continue;
|
|
404
|
+
if (object.kind !== "view" && object.kind !== "materializedView")
|
|
405
|
+
continue;
|
|
406
|
+
const dependencies = viewEdges.get(object.identity.name);
|
|
407
|
+
if (dependencies === undefined || dependencies.length === 0)
|
|
408
|
+
continue;
|
|
409
|
+
objects[index] = object.kind === "view" ? viewObject({
|
|
410
|
+
namespace,
|
|
411
|
+
name: object.identity.name,
|
|
412
|
+
columns: object.definition.columns,
|
|
413
|
+
query: object.definition.query,
|
|
414
|
+
provenance,
|
|
415
|
+
dependencies
|
|
416
|
+
}) : materializedViewObject({
|
|
417
|
+
namespace,
|
|
418
|
+
name: object.identity.name,
|
|
419
|
+
columns: object.definition.columns,
|
|
420
|
+
query: object.definition.query,
|
|
421
|
+
provenance,
|
|
422
|
+
dependencies
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
if (options?.managedRoles !== undefined) {
|
|
426
|
+
objects.push(...await readPrivileges(runner, concrete, namespace, provenance, objects, options.managedRoles));
|
|
427
|
+
}
|
|
428
|
+
return catalog(objects);
|
|
429
|
+
}
|
|
430
|
+
var EMITTED = new Set(["SELECT", "INSERT", "UPDATE", "DELETE", "EXECUTE", "USAGE"]);
|
|
431
|
+
var ROLES = `select rolname, rolcanlogin, rolinherit from pg_roles where rolname !~ '^pg_'`;
|
|
432
|
+
var RELATION_GRANTS = `select c.relname as name,
|
|
433
|
+
case c.relkind when 'v' then 'view' when 'm' then 'materializedView' when 'S' then 'sequence' else 'table' end as kind,
|
|
434
|
+
r.rolname as role, a.privilege_type as privilege
|
|
435
|
+
from pg_class c
|
|
436
|
+
join pg_namespace n on n.oid = c.relnamespace
|
|
437
|
+
cross join lateral aclexplode(c.relacl) as a
|
|
438
|
+
join pg_roles r on r.oid = a.grantee
|
|
439
|
+
where n.nspname = $1
|
|
440
|
+
and c.relkind in ('r', 'p', 'v', 'm', 'S')
|
|
441
|
+
and c.relacl is not null
|
|
442
|
+
and a.grantee <> 0
|
|
443
|
+
and a.grantee <> c.relowner
|
|
444
|
+
and not exists (select 1 from pg_inherits i where i.inhrelid = c.oid)
|
|
445
|
+
and not exists (
|
|
446
|
+
select 1 from pg_depend d
|
|
447
|
+
where d.classid = 'pg_class'::regclass and d.objid = c.oid and d.deptype = 'e'
|
|
448
|
+
)`;
|
|
449
|
+
var FUNCTION_GRANTS = `select p.proname as name,
|
|
450
|
+
replace(pg_get_function_identity_arguments(p.oid), ' ', '') as args,
|
|
451
|
+
r.rolname as role, a.privilege_type as privilege
|
|
452
|
+
from pg_proc p
|
|
453
|
+
join pg_namespace n on n.oid = p.pronamespace
|
|
454
|
+
cross join lateral aclexplode(p.proacl) as a
|
|
455
|
+
join pg_roles r on r.oid = a.grantee
|
|
456
|
+
where n.nspname = $1
|
|
457
|
+
and p.proacl is not null
|
|
458
|
+
and a.grantee <> 0
|
|
459
|
+
and a.grantee <> p.proowner
|
|
460
|
+
and not exists (
|
|
461
|
+
select 1 from pg_depend d
|
|
462
|
+
where d.classid = 'pg_proc'::regclass and d.objid = p.oid and d.deptype = 'e'
|
|
463
|
+
)`;
|
|
464
|
+
var DEFAULT_PRIVILEGES = `select owner_role.rolname as for_role, grantee.rolname as grantee,
|
|
465
|
+
case d.defaclobjtype when 'S' then 'sequence' when 'f' then 'function' else 'table' end as object_kind,
|
|
466
|
+
a.privilege_type as privilege
|
|
467
|
+
from pg_default_acl d
|
|
468
|
+
join pg_namespace n on n.oid = d.defaclnamespace
|
|
469
|
+
join pg_roles owner_role on owner_role.oid = d.defaclrole
|
|
470
|
+
cross join lateral aclexplode(d.defaclacl) as a
|
|
471
|
+
join pg_roles grantee on grantee.oid = a.grantee
|
|
472
|
+
where n.nspname = $1
|
|
473
|
+
and d.defaclobjtype in ('r', 'S', 'f')
|
|
474
|
+
and a.grantee <> 0`;
|
|
475
|
+
async function readPrivileges(runner, concrete, namespace, provenance, built, managedRoles) {
|
|
476
|
+
const managed = new Set(managedRoles);
|
|
477
|
+
const [roles, relations, functions, defaults] = await Promise.all([
|
|
478
|
+
runner.query(ROLES, []),
|
|
479
|
+
runner.query(RELATION_GRANTS, [concrete]),
|
|
480
|
+
runner.query(FUNCTION_GRANTS, [concrete]),
|
|
481
|
+
runner.query(DEFAULT_PRIVILEGES, [concrete])
|
|
482
|
+
]);
|
|
483
|
+
const objects = [];
|
|
484
|
+
for (const row of roles) {
|
|
485
|
+
const name = text(row, "rolname");
|
|
486
|
+
objects.push(roleObject({
|
|
487
|
+
name,
|
|
488
|
+
login: flag(row, "rolcanlogin"),
|
|
489
|
+
inherit: flag(row, "rolinherit"),
|
|
490
|
+
owner: managed.has(name) ? "managed" : "external",
|
|
491
|
+
provenance
|
|
492
|
+
}));
|
|
493
|
+
}
|
|
494
|
+
for (const row of relations) {
|
|
495
|
+
const kind = grantKind(text(row, "kind"));
|
|
496
|
+
const name = text(row, "name");
|
|
497
|
+
const privilege = text(row, "privilege").toUpperCase();
|
|
498
|
+
const target = built.find((object) => privilegeTarget(object) === `${kind}:${name}`);
|
|
499
|
+
if (!EMITTED.has(privilege) || target === undefined)
|
|
500
|
+
continue;
|
|
501
|
+
objects.push(grantObject({
|
|
502
|
+
role: text(row, "role"),
|
|
503
|
+
object: { kind, namespace, name },
|
|
504
|
+
privilege,
|
|
505
|
+
provenance,
|
|
506
|
+
dependencies: [{ kind: "role", name: text(row, "role") }, target.identity]
|
|
507
|
+
}));
|
|
508
|
+
}
|
|
509
|
+
for (const row of functions) {
|
|
510
|
+
const privilege = text(row, "privilege").toUpperCase();
|
|
511
|
+
const name = `${text(row, "name")}(${text(row, "args")})`;
|
|
512
|
+
const target = built.find((object) => privilegeTarget(object) === `function:${name}`);
|
|
513
|
+
if (!EMITTED.has(privilege) || target === undefined)
|
|
514
|
+
continue;
|
|
515
|
+
objects.push(grantObject({
|
|
516
|
+
role: text(row, "role"),
|
|
517
|
+
object: { kind: "function", namespace, name },
|
|
518
|
+
privilege,
|
|
519
|
+
provenance,
|
|
520
|
+
dependencies: [{ kind: "role", name: text(row, "role") }, target.identity]
|
|
521
|
+
}));
|
|
522
|
+
}
|
|
523
|
+
for (const row of defaults) {
|
|
524
|
+
const privilege = text(row, "privilege").toUpperCase();
|
|
525
|
+
if (!EMITTED.has(privilege))
|
|
526
|
+
continue;
|
|
527
|
+
const forRole = text(row, "for_role");
|
|
528
|
+
const grantee = text(row, "grantee");
|
|
529
|
+
objects.push(defaultPrivilegeObject({
|
|
530
|
+
forRole,
|
|
531
|
+
namespace,
|
|
532
|
+
objectKind: text(row, "object_kind"),
|
|
533
|
+
grantee,
|
|
534
|
+
privilege,
|
|
535
|
+
provenance,
|
|
536
|
+
dependencies: [
|
|
537
|
+
{ kind: "role", name: forRole },
|
|
538
|
+
{ kind: "role", name: grantee }
|
|
539
|
+
]
|
|
540
|
+
}));
|
|
541
|
+
}
|
|
542
|
+
return objects;
|
|
543
|
+
}
|
|
544
|
+
function privilegeTarget(object) {
|
|
545
|
+
if (object.kind === "table" || object.kind === "view" || object.kind === "materializedView" || object.kind === "sequence") {
|
|
546
|
+
return `${object.kind}:${object.identity.name}`;
|
|
547
|
+
}
|
|
548
|
+
if (object.kind === "function") {
|
|
549
|
+
return `function:${object.identity.name}(${object.identity.argTypes.join(",")})`;
|
|
550
|
+
}
|
|
551
|
+
return "";
|
|
552
|
+
}
|
|
553
|
+
function grantKind(kind) {
|
|
554
|
+
if (kind === "view" || kind === "materializedView" || kind === "sequence")
|
|
555
|
+
return kind;
|
|
556
|
+
return "table";
|
|
557
|
+
}
|
|
558
|
+
function printedColumns(value) {
|
|
559
|
+
if (value.length === 0)
|
|
560
|
+
return [];
|
|
561
|
+
return value.split(`
|
|
562
|
+
`).map((line) => {
|
|
563
|
+
const splitAt = line.indexOf("|");
|
|
564
|
+
return {
|
|
565
|
+
name: splitAt < 0 ? line : line.slice(0, splitAt),
|
|
566
|
+
dataType: splitAt < 0 ? "text" : line.slice(splitAt + 1)
|
|
567
|
+
};
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
function argumentList(names, types) {
|
|
571
|
+
const stored = Array.isArray(names) ? names.map((item) => String(item)) : typeof names === "string" && names.startsWith("{") ? names.slice(1, -1).split(",").filter((item) => item.length > 0) : [];
|
|
572
|
+
return types.map((type, index) => ({ name: stored[index] || `arg${String(index + 1)}`, type }));
|
|
573
|
+
}
|
|
574
|
+
function splitTypes(value) {
|
|
575
|
+
return value.length === 0 ? [] : value.split("|");
|
|
576
|
+
}
|
|
577
|
+
function volatilityOf(value) {
|
|
578
|
+
if (value === "s")
|
|
579
|
+
return "stable";
|
|
580
|
+
if (value === "i")
|
|
581
|
+
return "immutable";
|
|
582
|
+
return "volatile";
|
|
583
|
+
}
|
|
584
|
+
function searchPathOf(config) {
|
|
585
|
+
const match = /search_path=([^,}]+)/.exec(config);
|
|
586
|
+
const found = match?.[1]?.replaceAll('"', "");
|
|
587
|
+
return found === undefined || found.length === 0 ? undefined : found;
|
|
588
|
+
}
|
|
589
|
+
function atomicBody(definition) {
|
|
590
|
+
const start = definition.indexOf("BEGIN ATOMIC");
|
|
591
|
+
if (start < 0)
|
|
592
|
+
return definition.trim();
|
|
593
|
+
const end = definition.lastIndexOf("END");
|
|
594
|
+
return definition.slice(start, end < start ? undefined : end + 3).trim();
|
|
595
|
+
}
|
|
596
|
+
function triggerTiming(bits) {
|
|
597
|
+
if ((bits & 64) !== 0)
|
|
598
|
+
return "instead";
|
|
599
|
+
if ((bits & 2) !== 0)
|
|
600
|
+
return "before";
|
|
601
|
+
return "after";
|
|
602
|
+
}
|
|
603
|
+
function triggerEvents(bits) {
|
|
604
|
+
const events = [];
|
|
605
|
+
if ((bits & 4) !== 0)
|
|
606
|
+
events.push("insert");
|
|
607
|
+
if ((bits & 16) !== 0)
|
|
608
|
+
events.push("update");
|
|
609
|
+
if ((bits & 8) !== 0)
|
|
610
|
+
events.push("delete");
|
|
611
|
+
if ((bits & 32) !== 0)
|
|
612
|
+
events.push("truncate");
|
|
613
|
+
return events;
|
|
614
|
+
}
|
|
615
|
+
function indexExpression(row, columns) {
|
|
616
|
+
const method = text(row, "am");
|
|
617
|
+
const stored = text(row, "expression");
|
|
618
|
+
if (method.length === 0 || method === "btree")
|
|
619
|
+
return stored;
|
|
620
|
+
const opclasses = list(text(row, "opc"));
|
|
621
|
+
const body = columns.map((name, index) => {
|
|
622
|
+
const opclass = opclasses[index];
|
|
623
|
+
return opclass === undefined ? quoteIdent(name) : `${quoteIdent(name)} ${opclass}`;
|
|
624
|
+
}).join(", ");
|
|
625
|
+
return `using ${method} (${body.length > 0 ? body : stored})`;
|
|
626
|
+
}
|
|
627
|
+
var CHILD = "not exists (select 1 from pg_inherits i where i.inhrelid = c.oid)";
|
|
628
|
+
var TABLES = `
|
|
629
|
+
select c.relname as name, pt.partstrat as partstrat,
|
|
630
|
+
(
|
|
631
|
+
select coalesce(string_agg(a.attname, ',' order by cols.ord), '')
|
|
632
|
+
from unnest(pt.partattrs) with ordinality as cols(attnum, ord)
|
|
633
|
+
join pg_attribute a on a.attrelid = c.oid and a.attnum = cols.attnum
|
|
634
|
+
) as partcols
|
|
635
|
+
from pg_class c
|
|
636
|
+
join pg_namespace n on n.oid = c.relnamespace
|
|
637
|
+
left join pg_partitioned_table pt on pt.partrelid = c.oid
|
|
638
|
+
where n.nspname = $1
|
|
639
|
+
and c.relkind in ('r', 'p')
|
|
640
|
+
and ${CHILD}
|
|
641
|
+
and ${member("c.oid", "pg_class")}
|
|
642
|
+
`;
|
|
643
|
+
var COLUMNS = `
|
|
644
|
+
select c.relname as parent, a.attname as name,
|
|
645
|
+
case
|
|
646
|
+
when ty.typtype = 'e' and ty.typnamespace = n.oid then ty.typname
|
|
647
|
+
else format_type(a.atttypid, a.atttypmod)
|
|
648
|
+
end as type,
|
|
649
|
+
a.attnotnull as not_null, a.attidentity as identity, a.attgenerated as generated,
|
|
650
|
+
case
|
|
651
|
+
when a.attcollation = 0 or a.attcollation = ty.typcollation then ''
|
|
652
|
+
else coalesce((select col.collname from pg_collation col where col.oid = a.attcollation), '')
|
|
653
|
+
end as collation,
|
|
654
|
+
pg_get_expr(ad.adbin, ad.adrelid) as expression,
|
|
655
|
+
(
|
|
656
|
+
select ext.extname
|
|
657
|
+
from pg_depend member
|
|
658
|
+
join pg_extension ext on ext.oid = member.refobjid
|
|
659
|
+
where member.objid = ty.oid and member.deptype = 'e' and member.classid = 'pg_type'::regclass
|
|
660
|
+
limit 1
|
|
661
|
+
) as extension,
|
|
662
|
+
(
|
|
663
|
+
select owned.relname
|
|
664
|
+
from pg_depend d
|
|
665
|
+
join pg_class owned on owned.oid = d.objid and owned.relkind = 'S'
|
|
666
|
+
where d.refobjid = c.oid and d.refobjsubid = a.attnum and d.deptype = 'a'
|
|
667
|
+
and d.classid = 'pg_class'::regclass
|
|
668
|
+
limit 1
|
|
669
|
+
) as sequence
|
|
670
|
+
from pg_class c
|
|
671
|
+
join pg_namespace n on n.oid = c.relnamespace
|
|
672
|
+
join pg_attribute a on a.attrelid = c.oid and a.attnum > 0 and not a.attisdropped
|
|
673
|
+
join pg_type ty on ty.oid = a.atttypid
|
|
674
|
+
left join pg_attrdef ad on ad.adrelid = a.attrelid and ad.adnum = a.attnum
|
|
675
|
+
where n.nspname = $1
|
|
676
|
+
and c.relkind in ('r', 'p')
|
|
677
|
+
and ${CHILD}
|
|
678
|
+
and ${member("c.oid", "pg_class")}
|
|
679
|
+
`;
|
|
680
|
+
var CONSTRAINTS = `
|
|
681
|
+
select rel.relname as parent, con.conname as name, con.contype as contype,
|
|
682
|
+
con.condeferrable as deferrable, con.condeferred as deferred,
|
|
683
|
+
pg_get_constraintdef(con.oid) as definition,
|
|
684
|
+
(
|
|
685
|
+
select coalesce(string_agg(a.attname, ',' order by cols.ord), '')
|
|
686
|
+
from unnest(con.conkey) with ordinality as cols(attnum, ord)
|
|
687
|
+
join pg_attribute a on a.attrelid = con.conrelid and a.attnum = cols.attnum
|
|
688
|
+
) as columns,
|
|
689
|
+
ref.relname as ref_table,
|
|
690
|
+
(
|
|
691
|
+
select coalesce(string_agg(a.attname, ',' order by cols.ord), '')
|
|
692
|
+
from unnest(con.confkey) with ordinality as cols(attnum, ord)
|
|
693
|
+
join pg_attribute a on a.attrelid = con.confrelid and a.attnum = cols.attnum
|
|
694
|
+
) as ref_columns
|
|
695
|
+
from pg_constraint con
|
|
696
|
+
join pg_class rel on rel.oid = con.conrelid
|
|
697
|
+
join pg_namespace n on n.oid = rel.relnamespace
|
|
698
|
+
left join pg_class ref on ref.oid = con.confrelid
|
|
699
|
+
where n.nspname = $1
|
|
700
|
+
and con.contype in ('p', 'u', 'f', 'c')
|
|
701
|
+
and not exists (select 1 from pg_inherits i where i.inhrelid = rel.oid)
|
|
702
|
+
`;
|
|
703
|
+
var INDEXES = `
|
|
704
|
+
select tbl.relname as parent, idx.relname as name, i.indisunique as is_unique,
|
|
705
|
+
(
|
|
706
|
+
select coalesce(string_agg(a.attname, ',' order by cols.ord), '')
|
|
707
|
+
from unnest(i.indkey) with ordinality as cols(attnum, ord)
|
|
708
|
+
join pg_attribute a on a.attrelid = tbl.oid and a.attnum = cols.attnum
|
|
709
|
+
where cols.attnum > 0
|
|
710
|
+
) as columns,
|
|
711
|
+
pg_get_expr(i.indexprs, i.indrelid) as expression,
|
|
712
|
+
pg_get_expr(i.indpred, i.indrelid) as predicate,
|
|
713
|
+
am.amname as am,
|
|
714
|
+
(
|
|
715
|
+
select coalesce(string_agg(opc.opcname, ',' order by cols.ord), '')
|
|
716
|
+
from unnest(i.indclass) with ordinality as cols(opcoid, ord)
|
|
717
|
+
join pg_opclass opc on opc.oid = cols.opcoid
|
|
718
|
+
) as opc
|
|
719
|
+
from pg_index i
|
|
720
|
+
join pg_class idx on idx.oid = i.indexrelid
|
|
721
|
+
join pg_class tbl on tbl.oid = i.indrelid
|
|
722
|
+
join pg_namespace n on n.oid = tbl.relnamespace
|
|
723
|
+
join pg_am am on am.oid = idx.relam
|
|
724
|
+
where n.nspname = $1
|
|
725
|
+
and not i.indisprimary
|
|
726
|
+
and not exists (select 1 from pg_constraint con where con.conindid = i.indexrelid)
|
|
727
|
+
and not exists (select 1 from pg_inherits inh where inh.inhrelid = tbl.oid)
|
|
728
|
+
and not exists (select 1 from pg_inherits inh where inh.inhrelid = idx.oid)
|
|
729
|
+
and ${member("idx.oid", "pg_class")}
|
|
730
|
+
`;
|
|
731
|
+
var DOMAINS = `
|
|
732
|
+
select t.typname as name,
|
|
733
|
+
format_type(t.typbasetype, t.typtypmod) as base,
|
|
734
|
+
coalesce(pg_get_constraintdef(c.oid), '') as definition
|
|
735
|
+
from pg_type t
|
|
736
|
+
join pg_namespace n on n.oid = t.typnamespace
|
|
737
|
+
left join pg_constraint c on c.contypid = t.oid
|
|
738
|
+
where n.nspname = $1
|
|
739
|
+
and t.typtype = 'd'
|
|
740
|
+
and ${member("t.oid", "pg_type")}
|
|
741
|
+
order by t.typname, c.conname
|
|
742
|
+
`;
|
|
743
|
+
var ENUMS = `
|
|
744
|
+
select t.typname as name, e.enumlabel as label
|
|
745
|
+
from pg_type t
|
|
746
|
+
join pg_namespace n on n.oid = t.typnamespace
|
|
747
|
+
join pg_enum e on e.enumtypid = t.oid
|
|
748
|
+
where n.nspname = $1
|
|
749
|
+
and t.typtype = 'e'
|
|
750
|
+
and ${member("t.oid", "pg_type")}
|
|
751
|
+
order by t.typname, e.enumsortorder
|
|
752
|
+
`;
|
|
753
|
+
var EXTENSIONS = `
|
|
754
|
+
select e.extname as name, n.nspname as schema, e.extrelocatable as relocatable,
|
|
755
|
+
e.extversion as version
|
|
756
|
+
from pg_extension e
|
|
757
|
+
join pg_namespace n on n.oid = e.extnamespace
|
|
758
|
+
where n.nspname = $1
|
|
759
|
+
`;
|
|
760
|
+
var FUNCTIONS = `
|
|
761
|
+
select p.proname as name, l.lanname as language, p.provolatile as volatility,
|
|
762
|
+
p.prosecdef as security, coalesce(p.proconfig::text, '') as config, p.prosrc as body,
|
|
763
|
+
pg_get_function_result(p.oid) as returns, p.proargnames as arg_names,
|
|
764
|
+
(
|
|
765
|
+
select coalesce(string_agg(format_type(t.oid, null), '|' order by t.ord), '')
|
|
766
|
+
from unnest(p.proargtypes) with ordinality as t(oid, ord)
|
|
767
|
+
) as arg_types,
|
|
768
|
+
pg_get_functiondef(p.oid) as definition
|
|
769
|
+
from pg_proc p
|
|
770
|
+
join pg_namespace n on n.oid = p.pronamespace
|
|
771
|
+
join pg_language l on l.oid = p.prolang
|
|
772
|
+
where n.nspname = $1
|
|
773
|
+
and p.prokind = 'f'
|
|
774
|
+
and ${member("p.oid", "pg_proc")}
|
|
775
|
+
`;
|
|
776
|
+
var TRIGGERS = `
|
|
777
|
+
select t.tgname as name, c.relname as parent, t.tgtype::text as tgtype,
|
|
778
|
+
p.proname as function, fns.nspname as function_schema,
|
|
779
|
+
(
|
|
780
|
+
select coalesce(string_agg(format_type(x.oid, null), '|' order by x.ord), '')
|
|
781
|
+
from unnest(p.proargtypes) with ordinality as x(oid, ord)
|
|
782
|
+
) as arg_types,
|
|
783
|
+
coalesce(pg_get_expr(t.tgqual, t.tgrelid), '') as predicate,
|
|
784
|
+
(
|
|
785
|
+
select coalesce(string_agg(a.attname, ',' order by cols.ord), '')
|
|
786
|
+
from unnest(t.tgattr) with ordinality as cols(attnum, ord)
|
|
787
|
+
join pg_attribute a on a.attrelid = t.tgrelid and a.attnum = cols.attnum
|
|
788
|
+
) as update_of
|
|
789
|
+
from pg_trigger t
|
|
790
|
+
join pg_class c on c.oid = t.tgrelid
|
|
791
|
+
join pg_namespace n on n.oid = c.relnamespace
|
|
792
|
+
join pg_proc p on p.oid = t.tgfoid
|
|
793
|
+
join pg_namespace fns on fns.oid = p.pronamespace
|
|
794
|
+
where n.nspname = $1
|
|
795
|
+
and not t.tgisinternal
|
|
796
|
+
and ${CHILD}
|
|
797
|
+
`;
|
|
798
|
+
var VIEWS = `
|
|
799
|
+
select c.relname as name, c.relkind as kind, pg_get_viewdef(c.oid, true) as query,
|
|
800
|
+
(
|
|
801
|
+
select coalesce(string_agg(
|
|
802
|
+
a.attname || '|' || format_type(a.atttypid, a.atttypmod),
|
|
803
|
+
E'\\n' order by a.attnum
|
|
804
|
+
), '')
|
|
805
|
+
from pg_attribute a
|
|
806
|
+
where a.attrelid = c.oid and a.attnum > 0 and not a.attisdropped
|
|
807
|
+
) as columns
|
|
808
|
+
from pg_class c
|
|
809
|
+
join pg_namespace n on n.oid = c.relnamespace
|
|
810
|
+
where n.nspname = $1
|
|
811
|
+
and c.relkind in ('v', 'm')
|
|
812
|
+
and ${CHILD}
|
|
813
|
+
and ${member("c.oid", "pg_class")}
|
|
814
|
+
`;
|
|
815
|
+
var FUNCTION_DEPS = `
|
|
816
|
+
select p.proname as name,
|
|
817
|
+
(
|
|
818
|
+
select coalesce(string_agg(format_type(t.oid, null), '|' order by t.ord), '')
|
|
819
|
+
from unnest(p.proargtypes) with ordinality as t(oid, ord)
|
|
820
|
+
) as arg_types,
|
|
821
|
+
c.relname as table, coalesce(a.attname, '') as column
|
|
822
|
+
from pg_depend d
|
|
823
|
+
join pg_proc p on p.oid = d.objid and d.classid = 'pg_proc'::regclass
|
|
824
|
+
join pg_namespace n on n.oid = p.pronamespace
|
|
825
|
+
join pg_class c on c.oid = d.refobjid and d.refclassid = 'pg_class'::regclass
|
|
826
|
+
left join pg_attribute a on a.attrelid = c.oid and a.attnum = d.refobjsubid and d.refobjsubid > 0
|
|
827
|
+
where n.nspname = $1
|
|
828
|
+
and d.deptype = 'n'
|
|
829
|
+
and c.relkind in ('r', 'p')
|
|
830
|
+
and p.prokind = 'f'
|
|
831
|
+
and p.prosrc = ''
|
|
832
|
+
`;
|
|
833
|
+
function member(oid, classid) {
|
|
834
|
+
return `not exists (
|
|
835
|
+
select 1 from pg_depend member
|
|
836
|
+
where member.objid = ${oid} and member.deptype = 'e' and member.classid = '${classid}'::regclass
|
|
837
|
+
)`;
|
|
838
|
+
}
|
|
839
|
+
var SEQUENCES = `
|
|
840
|
+
select c.relname as name, format_type(s.seqtypid, null) as type,
|
|
841
|
+
s.seqstart::text as start, s.seqincrement::text as increment, s.seqcycle as cycle,
|
|
842
|
+
(
|
|
843
|
+
select owned.relname
|
|
844
|
+
from pg_depend d
|
|
845
|
+
join pg_class owned on owned.oid = d.refobjid and owned.relkind in ('r', 'p')
|
|
846
|
+
where d.objid = c.oid and d.deptype = 'a'
|
|
847
|
+
limit 1
|
|
848
|
+
) as owned_table
|
|
849
|
+
from pg_sequence s
|
|
850
|
+
join pg_class c on c.oid = s.seqrelid
|
|
851
|
+
join pg_namespace n on n.oid = c.relnamespace
|
|
852
|
+
where n.nspname = $1
|
|
853
|
+
and ${member("c.oid", "pg_class")}
|
|
854
|
+
`;
|
|
855
|
+
function partitionMethod(value) {
|
|
856
|
+
if (value === "r")
|
|
857
|
+
return "range";
|
|
858
|
+
if (value === "l")
|
|
859
|
+
return "list";
|
|
860
|
+
if (value === "h")
|
|
861
|
+
return "hash";
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
function constraintNameKey(kind, parent, name, columns) {
|
|
865
|
+
if (kind === "primaryKey")
|
|
866
|
+
return "pkey";
|
|
867
|
+
const tag = kind === "unique" ? "key" : kind === "foreignKey" ? "fkey" : "check";
|
|
868
|
+
const prefix = `${parent}_`;
|
|
869
|
+
const suffix = `_${tag}`;
|
|
870
|
+
if (name.startsWith(prefix) && name.endsWith(suffix) && name.length > prefix.length + suffix.length) {
|
|
871
|
+
return name.slice(prefix.length, -suffix.length);
|
|
872
|
+
}
|
|
873
|
+
if (columns.length > 0)
|
|
874
|
+
return columns.join("_");
|
|
875
|
+
return tag;
|
|
876
|
+
}
|
|
877
|
+
function constraintKind(value) {
|
|
878
|
+
if (value === "p")
|
|
879
|
+
return "primaryKey";
|
|
880
|
+
if (value === "u")
|
|
881
|
+
return "unique";
|
|
882
|
+
if (value === "f")
|
|
883
|
+
return "foreignKey";
|
|
884
|
+
return "check";
|
|
885
|
+
}
|
|
886
|
+
function sequenceType(value) {
|
|
887
|
+
if (value === "smallint")
|
|
888
|
+
return "smallint";
|
|
889
|
+
if (value === "integer")
|
|
890
|
+
return "integer";
|
|
891
|
+
return "bigint";
|
|
892
|
+
}
|
|
893
|
+
function checkExpression(definition) {
|
|
894
|
+
return definition.trim().replace(/^check\s+/i, "");
|
|
895
|
+
}
|
|
896
|
+
function actionAfter(definition, event) {
|
|
897
|
+
const match = new RegExp(`on ${event} ([a-z ]+)`, "i").exec(definition);
|
|
898
|
+
const words = match?.[1]?.trim().toLowerCase() ?? "";
|
|
899
|
+
if (words.length === 0 || words === "no action")
|
|
900
|
+
return;
|
|
901
|
+
return referentialAction(words);
|
|
902
|
+
}
|
|
903
|
+
function list(value) {
|
|
904
|
+
if (value.length === 0)
|
|
905
|
+
return [];
|
|
906
|
+
return value.split(",");
|
|
907
|
+
}
|
|
908
|
+
function text(row, key) {
|
|
909
|
+
const value = row[key];
|
|
910
|
+
if (typeof value === "string")
|
|
911
|
+
return value;
|
|
912
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
913
|
+
return String(value);
|
|
914
|
+
}
|
|
915
|
+
return "";
|
|
916
|
+
}
|
|
917
|
+
function numberText(row, key) {
|
|
918
|
+
const value = text(row, key);
|
|
919
|
+
return value.length === 0 ? "1" : value;
|
|
920
|
+
}
|
|
921
|
+
function flag(row, key) {
|
|
922
|
+
const value = row[key];
|
|
923
|
+
return value === true || value === "true" || value === "t";
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// src/dialects/pg/view/scratch.ts
|
|
927
|
+
var SCRATCH = "okm_view_seal";
|
|
928
|
+
var VIEW_SHAPE = `
|
|
929
|
+
select c.relname as name, c.relkind as kind, pg_get_viewdef(c.oid, true) as query,
|
|
930
|
+
(
|
|
931
|
+
select coalesce(string_agg(
|
|
932
|
+
a.attname || '|' || format_type(a.atttypid, a.atttypmod),
|
|
933
|
+
E'\\n' order by a.attnum
|
|
934
|
+
), '')
|
|
935
|
+
from pg_attribute a
|
|
936
|
+
where a.attrelid = c.oid and a.attnum > 0 and not a.attisdropped
|
|
937
|
+
) as columns
|
|
938
|
+
from pg_class c
|
|
939
|
+
join pg_namespace n on n.oid = c.relnamespace
|
|
940
|
+
where n.nspname = $1 and c.relkind in ('v', 'm')
|
|
941
|
+
`;
|
|
942
|
+
async function sealViews(runner, source) {
|
|
943
|
+
const views = source.objects.filter((object) => object.kind === "view" || object.kind === "materializedView");
|
|
944
|
+
if (views.length === 0)
|
|
945
|
+
return source;
|
|
946
|
+
await dropScratch(runner);
|
|
947
|
+
await runner.query(`create schema ${quoteIdent(SCRATCH)}`);
|
|
948
|
+
try {
|
|
949
|
+
await runner.query(`set search_path to ${quoteIdent(SCRATCH)}`);
|
|
950
|
+
for (const object of source.objects) {
|
|
951
|
+
if (object.kind !== "type")
|
|
952
|
+
continue;
|
|
953
|
+
const statement = createObjectSql(object, SCRATCH);
|
|
954
|
+
if (statement !== undefined)
|
|
955
|
+
await runner.query(statement);
|
|
956
|
+
}
|
|
957
|
+
for (const object of source.objects) {
|
|
958
|
+
if (object.kind !== "table")
|
|
959
|
+
continue;
|
|
960
|
+
await runner.query(stubTable(object, source));
|
|
961
|
+
}
|
|
962
|
+
await createViews(runner, views);
|
|
963
|
+
const shapes = await runner.query(VIEW_SHAPE, [SCRATCH]);
|
|
964
|
+
const deps = await runner.query(VIEW_DEPENDENCIES, [SCRATCH]);
|
|
965
|
+
const edges = viewDependencyEdges(deps, PUBLIC, source.objects);
|
|
966
|
+
const byName = new Map(shapes.map((row) => [cellText(row.name), row]));
|
|
967
|
+
const next = source.objects.map((object) => {
|
|
968
|
+
if (object.kind !== "view" && object.kind !== "materializedView")
|
|
969
|
+
return object;
|
|
970
|
+
const row = byName.get(object.identity.name);
|
|
971
|
+
if (row === undefined) {
|
|
972
|
+
throw new OkmError2("OKM1020", `View ${object.identity.name} was not created in the scratch schema.`, {
|
|
973
|
+
fix: { summary: "The query has to run against the tables in this schema." }
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
const columns = readColumns(object, cellText(row.columns));
|
|
977
|
+
const query = normaliseViewQuery(cellText(row.query));
|
|
978
|
+
const dependencies = edges.get(object.identity.name) ?? [];
|
|
979
|
+
if (object.kind === "view") {
|
|
980
|
+
return viewObject({
|
|
981
|
+
namespace: object.identity.namespace,
|
|
982
|
+
name: object.identity.name,
|
|
983
|
+
columns,
|
|
984
|
+
query,
|
|
985
|
+
owner: object.owner,
|
|
986
|
+
provenance: object.provenance,
|
|
987
|
+
dependencies
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
return materializedViewObject({
|
|
991
|
+
namespace: object.identity.namespace,
|
|
992
|
+
name: object.identity.name,
|
|
993
|
+
columns,
|
|
994
|
+
query,
|
|
995
|
+
owner: object.owner,
|
|
996
|
+
provenance: object.provenance,
|
|
997
|
+
dependencies,
|
|
998
|
+
...object.definition.refresh !== undefined ? { refresh: object.definition.refresh } : {}
|
|
999
|
+
});
|
|
1000
|
+
});
|
|
1001
|
+
return catalog(next);
|
|
1002
|
+
} finally {
|
|
1003
|
+
await runner.query("set search_path to public");
|
|
1004
|
+
await dropScratch(runner);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
async function createViews(runner, views) {
|
|
1008
|
+
const pending = [...views];
|
|
1009
|
+
let last = "";
|
|
1010
|
+
for (let pass = 0;pass < views.length && pending.length > 0; pass += 1) {
|
|
1011
|
+
const failed = [];
|
|
1012
|
+
let created = 0;
|
|
1013
|
+
for (const object of pending) {
|
|
1014
|
+
const sql = object.kind === "materializedView" ? `create materialized view ${quoteIdent(object.identity.name)} as ${object.definition.query} with no data` : `create view ${quoteIdent(object.identity.name)} as ${object.definition.query}`;
|
|
1015
|
+
try {
|
|
1016
|
+
await runner.query(sql);
|
|
1017
|
+
created += 1;
|
|
1018
|
+
} catch (error) {
|
|
1019
|
+
last = error instanceof Error ? error.message : String(error);
|
|
1020
|
+
failed.push(object);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
if (created === 0) {
|
|
1024
|
+
throw new OkmError2("OKM1020", `View ${failed[0]?.identity.name ?? ""} could not be created. ${last}`, {
|
|
1025
|
+
fix: { summary: "The query has to run against the tables in this schema." }
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
pending.length = 0;
|
|
1029
|
+
pending.push(...failed);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
function stubTable(object, source) {
|
|
1033
|
+
const columns = source.objects.filter((item) => item.kind === "column" && item.identity.parent.name === object.identity.name);
|
|
1034
|
+
const lines = columns.map((column) => `${quoteIdent(column.identity.name)} ${column.definition.dataType}`);
|
|
1035
|
+
return `create table ${quoteIdent(object.identity.name)} (${lines.join(", ")})`;
|
|
1036
|
+
}
|
|
1037
|
+
function readColumns(object, printed) {
|
|
1038
|
+
const live = printed.length === 0 ? [] : printed.split(`
|
|
1039
|
+
`);
|
|
1040
|
+
const declared = object.definition.columns;
|
|
1041
|
+
if (live.length !== declared.length) {
|
|
1042
|
+
throw mismatch(object.identity.name, declared.map((column) => column.name).join(", "), printed);
|
|
1043
|
+
}
|
|
1044
|
+
return declared.map((column, index) => {
|
|
1045
|
+
const line = live[index] ?? "";
|
|
1046
|
+
const splitAt = line.indexOf("|");
|
|
1047
|
+
const name = splitAt < 0 ? line : line.slice(0, splitAt);
|
|
1048
|
+
const type = splitAt < 0 ? "" : line.slice(splitAt + 1);
|
|
1049
|
+
if (name !== column.name || type !== column.dataType) {
|
|
1050
|
+
throw mismatch(object.identity.name, `${column.name}:${column.dataType}`, live[index] ?? "");
|
|
1051
|
+
}
|
|
1052
|
+
return column;
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
1055
|
+
function mismatch(view, declared, live) {
|
|
1056
|
+
return new OkmError2("OKM1020", `View ${view} declares ${declared} and the database has ${live}.`, { fix: { summary: "Declare the columns the query returns, in that order, with those types." } });
|
|
1057
|
+
}
|
|
1058
|
+
async function dropScratch(runner) {
|
|
1059
|
+
const present = await runner.query(`select 1 as ok from pg_namespace where nspname = $1`, [
|
|
1060
|
+
SCRATCH
|
|
1061
|
+
]);
|
|
1062
|
+
if (present.length === 0)
|
|
1063
|
+
return;
|
|
1064
|
+
const rows = await runner.query(`
|
|
1065
|
+
select c.relname as name, c.relkind as kind
|
|
1066
|
+
from pg_class c
|
|
1067
|
+
join pg_namespace n on n.oid = c.relnamespace
|
|
1068
|
+
where n.nspname = $1 and c.relkind in ('v', 'm', 'r')
|
|
1069
|
+
`, [SCRATCH]);
|
|
1070
|
+
const views = rows.filter((row) => cellText(row.kind) === "v" || cellText(row.kind) === "m");
|
|
1071
|
+
for (let pass = 0;pass < Math.max(views.length, 1); pass += 1) {
|
|
1072
|
+
for (const row of views) {
|
|
1073
|
+
const kind = cellText(row.kind) === "m" ? "materialized view" : "view";
|
|
1074
|
+
try {
|
|
1075
|
+
await runner.query(`drop ${kind} if exists ${quoteIdent(SCRATCH)}.${quoteIdent(cellText(row.name))}`);
|
|
1076
|
+
} catch {}
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
for (const row of rows) {
|
|
1080
|
+
if (cellText(row.kind) !== "r")
|
|
1081
|
+
continue;
|
|
1082
|
+
await runner.query(`drop table if exists ${quoteIdent(SCRATCH)}.${quoteIdent(cellText(row.name))}`);
|
|
1083
|
+
}
|
|
1084
|
+
await runner.query(`drop schema if exists ${quoteIdent(SCRATCH)} cascade`);
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
// src/tooling/migrate/drift.ts
|
|
1088
|
+
async function assertAuthorDrift2(url, author, roles) {
|
|
1089
|
+
const sql = postgres(url, {
|
|
1090
|
+
max: 1,
|
|
1091
|
+
connect_timeout: 5,
|
|
1092
|
+
idle_timeout: 1,
|
|
1093
|
+
onnotice: () => {}
|
|
1094
|
+
});
|
|
1095
|
+
try {
|
|
1096
|
+
const present = await sql`
|
|
1097
|
+
select to_regclass('public.okm_meta')::text as reg
|
|
1098
|
+
`;
|
|
1099
|
+
if (present[0]?.reg == null)
|
|
1100
|
+
return;
|
|
1101
|
+
const runner = catalogQuery2(sql);
|
|
1102
|
+
const authorOnServer = await sealViews(runner, author);
|
|
1103
|
+
const live = await introspectSchema(runner, "public", "public", managedRoleOptions2(roles));
|
|
1104
|
+
const plan = planMigration({ before: live, after: authorOnServer, name: "check" });
|
|
1105
|
+
if (plan.steps.length === 0)
|
|
1106
|
+
return;
|
|
1107
|
+
throw new OkmError2("OKM1520", `The database differs from the schema.
|
|
1108
|
+
${plan.steps.map((step) => step.sql).join(`
|
|
1109
|
+
`)}`, {
|
|
1110
|
+
fix: {
|
|
1111
|
+
summary: "Change the schema or the database so okm check reports no difference."
|
|
1112
|
+
}
|
|
1113
|
+
});
|
|
1114
|
+
} finally {
|
|
1115
|
+
await sql.end({ timeout: 5 });
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
function catalogQuery2(sql) {
|
|
1119
|
+
return {
|
|
1120
|
+
async query(text, params) {
|
|
1121
|
+
const rows = await sql.unsafe(text, params === undefined ? undefined : [...params]);
|
|
1122
|
+
return rows.map((row) => {
|
|
1123
|
+
const copy = {};
|
|
1124
|
+
for (const [key, value] of Object.entries(row))
|
|
1125
|
+
copy[key] = value;
|
|
1126
|
+
return copy;
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
function managedRoleOptions2(roles) {
|
|
1132
|
+
const names = managedRoleNames(roles);
|
|
1133
|
+
return names === undefined ? undefined : { managedRoles: names };
|
|
1134
|
+
}
|
|
1135
|
+
function managedRoleNames(roles) {
|
|
1136
|
+
if (roles === undefined)
|
|
1137
|
+
return;
|
|
1138
|
+
const names = new Set([roles.migration, roles.app]);
|
|
1139
|
+
for (const role of roles.managed ?? [])
|
|
1140
|
+
names.add(role.name);
|
|
1141
|
+
return [...names];
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
export { introspectSchema, sealViews, assertAuthorDrift2, catalogQuery2, managedRoleOptions2 };
|