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.
Files changed (148) hide show
  1. package/README.md +882 -48
  2. package/dist/contracts/catalog/document.d.ts +6 -2
  3. package/dist/contracts/catalog/enum.d.ts +31 -4
  4. package/dist/contracts/catalog/extension.d.ts +61 -0
  5. package/dist/contracts/catalog/privilege.d.ts +64 -0
  6. package/dist/contracts/catalog/routine.d.ts +56 -0
  7. package/dist/contracts/catalog/types.d.ts +164 -10
  8. package/dist/contracts/catalog/view.d.ts +73 -0
  9. package/dist/dialects/pg/column.d.ts +3 -1
  10. package/dist/dialects/pg/ddl.d.ts +65 -1
  11. package/dist/dialects/pg/enum.d.ts +10 -8
  12. package/dist/dialects/pg/ext/citext.d.ts +14 -0
  13. package/dist/dialects/pg/ext/citext.js +11 -0
  14. package/dist/dialects/pg/ext/index.d.ts +52 -0
  15. package/dist/dialects/pg/ext/index.js +6 -0
  16. package/dist/dialects/pg/ext/pg-trgm.d.ts +53 -0
  17. package/dist/dialects/pg/ext/pg-trgm.js +42 -0
  18. package/dist/dialects/pg/fn/index.d.ts +107 -0
  19. package/dist/dialects/pg/fn/index.js +175 -0
  20. package/dist/dialects/pg/fn/touch.d.ts +14 -0
  21. package/dist/dialects/pg/index.js +104 -19
  22. package/dist/dialects/pg/introspect.d.ts +10 -1
  23. package/dist/dialects/pg/operators.d.ts +8 -1
  24. package/dist/dialects/pg/role/check.d.ts +45 -0
  25. package/dist/dialects/pg/role/index.d.ts +50 -0
  26. package/dist/dialects/pg/role/sql.d.ts +51 -0
  27. package/dist/dialects/pg/table.d.ts +24 -1
  28. package/dist/dialects/pg/tenancy.d.ts +9 -0
  29. package/dist/dialects/pg/view/client.d.ts +30 -0
  30. package/dist/dialects/pg/view/depend.d.ts +35 -0
  31. package/dist/dialects/pg/view/index.d.ts +114 -0
  32. package/dist/dialects/pg/view/index.js +289 -0
  33. package/dist/dialects/pg/view/scratch.d.ts +22 -0
  34. package/dist/dialects/pg/where.d.ts +2 -2
  35. package/dist/runtime/catalog-cache.d.ts +17 -0
  36. package/dist/runtime/compat-words.d.ts +23 -0
  37. package/dist/runtime/drift-detail.d.ts +20 -4
  38. package/dist/runtime/drift.d.ts +4 -2
  39. package/dist/runtime/pg/bun.js +3 -3
  40. package/dist/runtime/pg/pg.js +3 -3
  41. package/dist/runtime/pg/pglite.js +3 -3
  42. package/dist/runtime/pg/postgresjs.js +3 -3
  43. package/dist/runtime/tenancy/index.js +6 -3
  44. package/dist/runtime/traits/index.d.ts +7 -2
  45. package/dist/runtime/traits/index.js +151 -13
  46. package/dist/runtime/types.d.ts +2 -0
  47. package/dist/runtime/validate/index.js +4 -4
  48. package/dist/shared/{xkz8g6by.js → 1g0drakv.js} +2 -2
  49. package/dist/shared/2321avmn.js +418 -0
  50. package/dist/shared/{7mht49yd.js → 3p4b1fg0.js} +57 -18
  51. package/dist/shared/41kv2xdm.js +318 -0
  52. package/dist/shared/{97g2jnrw.js → 4ef7mmqf.js} +3 -3
  53. package/dist/shared/{sacqha1y.js → 4m9fn52z.js} +2 -2
  54. package/dist/shared/5mtf6cj3.js +94 -0
  55. package/dist/shared/{1dp5knt7.js → 5vdm4sgp.js} +2 -2
  56. package/dist/shared/{a1gdp7j9.js → 6693j9rs.js} +1 -1
  57. package/dist/shared/{38crxbgn.js → 6p5k20sm.js} +3 -3
  58. package/dist/shared/7nk68fj8.js +751 -0
  59. package/dist/shared/84ptn35q.js +71 -0
  60. package/dist/shared/{d2whjz4k.js → 8jb4ymh2.js} +581 -28
  61. package/dist/shared/8ktwd3rh.js +97 -0
  62. package/dist/shared/94ez8kzh.js +19 -0
  63. package/dist/shared/{3a5ne750.js → 9m80mkac.js} +2 -2
  64. package/dist/shared/9x25fxhc.js +150 -0
  65. package/dist/shared/9yn6a2vd.js +11 -0
  66. package/dist/shared/ady1ms74.js +72 -0
  67. package/dist/shared/{4jzzghr6.js → awrsqrn7.js} +6 -6
  68. package/dist/shared/{4b9da9d7.js → axhk06q1.js} +19 -1
  69. package/dist/shared/{d04j53v8.js → c5g81ngb.js} +82 -14
  70. package/dist/shared/{n47rd821.js → c8aghh05.js} +1 -1
  71. package/dist/shared/{dd4f8mnx.js → c9z5g0fm.js} +4 -2
  72. package/dist/shared/{ps43134h.js → dyj86cmj.js} +4 -4
  73. package/dist/shared/{0qkxbm1f.js → fa9vt245.js} +1 -1
  74. package/dist/shared/{qa5dsc7z.js → fb00y8fp.js} +3 -3
  75. package/dist/shared/{qxa0yb9j.js → g7ez901n.js} +30 -1
  76. package/dist/shared/{py3nh7jy.js → gdh9mrtp.js} +2 -2
  77. package/dist/shared/{yw5c610b.js → gjthds9j.js} +2 -2
  78. package/dist/shared/hcmdtwgh.js +102 -0
  79. package/dist/shared/hqjsxnz7.js +13 -0
  80. package/dist/shared/htnaj77v.js +17 -0
  81. package/dist/shared/{v18jva5b.js → jgxzjzcz.js} +1 -1
  82. package/dist/shared/{x9v1qgeb.js → jsd07prz.js} +3 -3
  83. package/dist/shared/{g9f0zqce.js → kafaa5zv.js} +1 -1
  84. package/dist/shared/kj680nxv.js +19 -0
  85. package/dist/shared/{mxmcdsh5.js → kkzwp3d2.js} +9 -9
  86. package/dist/shared/{g1b5zzj9.js → n0dgs0pb.js} +1 -1
  87. package/dist/shared/n5y9zyj1.js +2431 -0
  88. package/dist/shared/{zt8y0wzb.js → n7mcc1gk.js} +1 -1
  89. package/dist/shared/{ge03bfca.js → nxb42q7h.js} +2 -2
  90. package/dist/shared/q6zsyh31.js +2192 -0
  91. package/dist/shared/{sfh4pk75.js → q795afwa.js} +26 -21
  92. package/dist/shared/{10tjenmd.js → qn0srwwq.js} +1 -1
  93. package/dist/shared/s4mb6a4b.js +545 -0
  94. package/dist/shared/{ytg3xf9p.js → sf8m6tfy.js} +3 -3
  95. package/dist/shared/{zd93g3rq.js → sr5ms7b3.js} +1 -1
  96. package/dist/shared/{xatfg0gn.js → tmbdywx0.js} +4 -19
  97. package/dist/shared/tny2t3sm.js +100 -0
  98. package/dist/shared/{e7a268fy.js → v7tpnhnz.js} +1 -1
  99. package/dist/shared/{ecdzej21.js → wqem4jed.js} +7 -7
  100. package/dist/shared/xghkt7a9.js +1144 -0
  101. package/dist/shared/y005b761.js +112 -0
  102. package/dist/shared/ycatkxmq.js +84 -0
  103. package/dist/shared/ygem90rn.js +900 -0
  104. package/dist/shared/{eaxwrgby.js → yjw02enp.js} +6 -3
  105. package/dist/shared/{mrq77sgw.js → zg24x0hm.js} +1 -1
  106. package/dist/shared/{cstgkzdx.js → zvbjc949.js} +1 -1
  107. package/dist/tooling/cli.js +66 -13
  108. package/dist/tooling/migrate/apply.d.ts +75 -5
  109. package/dist/tooling/migrate/backfill.d.ts +136 -0
  110. package/dist/tooling/migrate/check.d.ts +65 -0
  111. package/dist/tooling/migrate/classify.d.ts +108 -0
  112. package/dist/tooling/migrate/commands.d.ts +2 -1
  113. package/dist/tooling/migrate/config.d.ts +30 -0
  114. package/dist/tooling/migrate/doctor.d.ts +28 -0
  115. package/dist/tooling/migrate/drift.d.ts +35 -0
  116. package/dist/tooling/migrate/estimate.d.ts +50 -0
  117. package/dist/tooling/migrate/ext.d.ts +17 -0
  118. package/dist/tooling/migrate/extensions.d.ts +53 -0
  119. package/dist/tooling/migrate/files.d.ts +12 -0
  120. package/dist/tooling/migrate/index.d.ts +3 -2
  121. package/dist/tooling/migrate/index.js +67 -1
  122. package/dist/tooling/migrate/lint-rules.d.ts +51 -0
  123. package/dist/tooling/migrate/lint.d.ts +71 -0
  124. package/dist/tooling/migrate/managed.d.ts +17 -0
  125. package/dist/tooling/migrate/plan.d.ts +78 -10
  126. package/dist/tooling/migrate/project.d.ts +64 -5
  127. package/dist/tooling/migrate/provision.d.ts +18 -0
  128. package/dist/tooling/migrate/reference.d.ts +40 -0
  129. package/dist/tooling/migrate/runner.d.ts +108 -0
  130. package/dist/tooling/migrate/safe.d.ts +77 -0
  131. package/dist/tooling/migrate/seed.d.ts +20 -0
  132. package/dist/tooling/migrate/snapshot.d.ts +78 -0
  133. package/dist/tooling/migrate/status.d.ts +25 -6
  134. package/dist/tooling/migrate/type-name.d.ts +19 -0
  135. package/dist/tooling/testing/factories.d.ts +117 -0
  136. package/dist/tooling/testing/facts.d.ts +72 -0
  137. package/dist/tooling/testing/index.d.ts +89 -0
  138. package/dist/tooling/testing/index.js +8 -0
  139. package/dist/tooling/testing/isolation.d.ts +30 -0
  140. package/dist/tooling/testing/random.d.ts +40 -0
  141. package/dist/tooling/testing/record.d.ts +38 -0
  142. package/package.json +26 -2
  143. package/dist/shared/5z4q2zda.js +0 -2073
  144. package/dist/shared/eqmtn1bj.js +0 -399
  145. package/dist/shared/rtnaqre0.js +0 -37
  146. package/dist/shared/zwmqxb0g.js +0 -79
  147. package/dist/shared/{m9tv7xn7.js → radd3z4g.js} +3 -3
  148. /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 };