qubu 0.6.0 → 0.6.2
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 +112 -0
- package/dist/{complete-DP7pliuY.mjs → canonical-B5_ouh-c.mjs} +466 -241
- package/dist/codegen.d.mts +2 -2
- package/dist/codegen.mjs +180 -90
- package/dist/column-Cyc2CMnG.mjs +116 -0
- package/dist/column-DDRvD7SF.mjs +721 -0
- package/dist/{constraints-YGyNPQ_z.mjs → constraints-CAmi18Uk.mjs} +8 -3
- package/dist/core.d.mts +3 -3
- package/dist/core.mjs +4 -5
- package/dist/diagnostics-I9vVtXkc.mjs +40 -0
- package/dist/diff.d.mts +9 -9
- package/dist/diff.mjs +152 -102
- package/dist/{explain-CkIK13L_.mjs → explain-BIqEmZha.mjs} +1 -1
- package/dist/expressions-_6JF_J77.mjs +130 -0
- package/dist/index-CaxrMD1A.d.mts +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +414 -77
- package/dist/introspection/mysql.d.mts +26 -0
- package/dist/introspection/mysql.mjs +1278 -0
- package/dist/introspection/postgres.d.mts +43 -0
- package/dist/introspection/postgres.mjs +1889 -0
- package/dist/introspection/sqlite.d.mts +15 -0
- package/dist/introspection/sqlite.mjs +1172 -0
- package/dist/introspection.d.mts +26 -87
- package/dist/introspection.mjs +3 -4351
- package/dist/mysql.d.mts +3 -3
- package/dist/mysql.mjs +8 -7
- package/dist/{errors-Dxv73YJu.mjs → omit-VEr9Ydux.mjs} +5 -1
- package/dist/{on-conflict-DZQ85f1t.mjs → on-conflict-B2rFyHGF.mjs} +78 -8
- package/dist/on-duplicate-key-update-Czsw1Yq-.mjs +95 -0
- package/dist/postgres-hFhd0I9n.mjs +68 -0
- package/dist/postgres.d.mts +3 -3
- package/dist/postgres.mjs +3 -52
- package/dist/registry-BXE_4M9P.mjs +127 -0
- package/dist/{relational-CxnLCqZQ.mjs → relational-CoPBETjI.mjs} +3 -3
- package/dist/schema.d.mts +2 -2
- package/dist/schema.mjs +8 -7
- package/dist/{serialize-BN07IK0v.mjs → serialize-CyobNEx-.mjs} +174 -30
- package/dist/serialize-Du2UPZMt.d.mts +92 -0
- package/dist/snapshot/mysql.d.mts +5 -5
- package/dist/snapshot/mysql.mjs +32 -32
- package/dist/snapshot/postgres.d.mts +5 -5
- package/dist/snapshot/postgres.mjs +25 -25
- package/dist/snapshot/sqlite.d.mts +5 -5
- package/dist/snapshot/sqlite.mjs +30 -30
- package/dist/snapshot-mIb-Zzb5.mjs +1489 -0
- package/dist/snapshot.d.mts +4 -5
- package/dist/snapshot.mjs +3 -4
- package/dist/{source-SqrKWjFJ.mjs → source-DYSUqzvb.mjs} +2 -2
- package/dist/sqlite.d.mts +2 -2
- package/dist/sqlite.mjs +10 -10
- package/dist/{table-BwflqeAj.mjs → table-B8zEq0az.mjs} +4 -4
- package/dist/{types-BIJsj2fJ.mjs → types-CYHpSPwj.mjs} +10 -7
- package/dist/{types-CTCqtFlS.d.mts → types-CiMvKi5V.d.mts} +14 -4
- package/dist/{types-C0VkiwpR.d.mts → types-Dqr4o2I1.d.mts} +640 -189
- package/dist/value-CpaUFtjw.mjs +45 -0
- package/dist/vite/ambient.d.ts +2 -0
- package/dist/vite.d.mts +1 -1
- package/dist/vite.mjs +2 -0
- package/docs/dialects-and-execution.md +61 -11
- package/docs/getting-started.md +1 -1
- package/docs/guides/compose-queries.md +22 -0
- package/docs/guides/drizzle.md +11 -11
- package/docs/guides/extensions/typed-expressions.md +5 -3
- package/docs/guides/json.md +95 -2
- package/docs/guides/mutations.md +125 -0
- package/docs/guides/valtio-sync.md +113 -0
- package/docs/index.md +1 -1
- package/docs/migrations/adapters.md +58 -21
- package/docs/migrations/index.md +50 -12
- package/docs/migrations/operations.md +20 -6
- package/docs/migrations/recovery.md +7 -1
- package/docs/query-model/fragments.md +21 -1
- package/docs/reference/introspection-support.md +16 -18
- package/docs/reference/supported-surface.md +23 -11
- package/docs/schema/catalog-model.md +20 -7
- package/docs/schema/code-generation.md +4 -3
- package/docs/schema/diff.md +2 -2
- package/docs/schema/introspection.md +24 -16
- package/docs/schema/snapshots.md +18 -8
- package/docs/schema/storage-and-schema-sql.md +7 -2
- package/docs/sql-semantic-types.md +8 -0
- package/package.json +14 -1
- package/dist/canonical-DMvR9yBe.mjs +0 -972
- package/dist/column-Da37jYSD.mjs +0 -309
- package/dist/column-r1Y4ivwt.mjs +0 -327
- package/dist/complete-types-CY0KbzNw.d.mts +0 -371
- package/dist/index-B2rZf3-2.d.mts +0 -32
- package/dist/json-Db7XRD91.mjs +0 -169
- package/dist/omit-OxV58AwX.mjs +0 -5
- package/dist/registry-BRcUuazJ.mjs +0 -256
- package/dist/serialize-CEIIlWhC.d.mts +0 -66
- package/dist/snapshot-Xam8-q0j.mjs +0 -392
- package/dist/standard-DfcZEVOj.mjs +0 -12
- package/dist/value-D14I_XgL.mjs +0 -29
|
@@ -0,0 +1,1889 @@
|
|
|
1
|
+
import { n as createIntrospectionDiagnostic } from "../diagnostics-I9vVtXkc.mjs";
|
|
2
|
+
//#region src/introspection/postgres.ts
|
|
3
|
+
/** Fixed server metadata query used by the PostgreSQL catalog reader. */
|
|
4
|
+
const postgresServerQuery = `
|
|
5
|
+
SELECT
|
|
6
|
+
current_setting('server_version_num') AS server_version_num,
|
|
7
|
+
current_setting('server_version') AS server_version
|
|
8
|
+
`;
|
|
9
|
+
/** Read one PostgreSQL schema into the normalized catalog contract. */
|
|
10
|
+
async function readCatalog(connection, options) {
|
|
11
|
+
const diagnostics = [];
|
|
12
|
+
const deferredObjects = [];
|
|
13
|
+
const opaqueObjects = [];
|
|
14
|
+
if (connection.dialect !== "postgresql") {
|
|
15
|
+
diagnostics.push(createIntrospectionDiagnostic({
|
|
16
|
+
severity: "error",
|
|
17
|
+
code: "dialect-mismatch",
|
|
18
|
+
message: `PostgreSQL catalog reader received a ${connection.dialect} connection`,
|
|
19
|
+
path: ["connection", "dialect"],
|
|
20
|
+
remediation: "Use a PostgreSQL CatalogConnection with this reader."
|
|
21
|
+
}));
|
|
22
|
+
return emptyCatalog(options.namespace, diagnostics);
|
|
23
|
+
}
|
|
24
|
+
const server = serverInfo((await query(connection, postgresServerQuery, [], options, diagnostics, "server"))[0], diagnostics);
|
|
25
|
+
const relationObjects = (await query(connection, postgresRelationsQuery, [options.namespace], options, diagnostics, "relations")).filter((row) => {
|
|
26
|
+
const kind = text(row.relkind);
|
|
27
|
+
return kind !== "i" && kind !== "I";
|
|
28
|
+
});
|
|
29
|
+
const tables = relationObjects.filter((row) => row.relkind === "r" || row.relkind === "p").map((row) => relationTable(row));
|
|
30
|
+
const tableByOid = new Map(tables.map((table) => [table.reference?.catalog?.value, table]));
|
|
31
|
+
const viewRows = await query(connection, postgresViewsQuery, [options.namespace], options, diagnostics, "views");
|
|
32
|
+
const views = [];
|
|
33
|
+
for (const row of viewRows) {
|
|
34
|
+
const view = viewObject(row, options.namespace, diagnostics);
|
|
35
|
+
if (view.kind === "deferred-object") continue;
|
|
36
|
+
views.push(view);
|
|
37
|
+
}
|
|
38
|
+
const relationReferences = /* @__PURE__ */ new Map();
|
|
39
|
+
for (const table of tables) relationReferences.set(table.reference?.catalog?.value, {
|
|
40
|
+
kind: "table",
|
|
41
|
+
id: table.id
|
|
42
|
+
});
|
|
43
|
+
for (const view of views) relationReferences.set(view.reference?.catalog?.value, {
|
|
44
|
+
kind: view.kind,
|
|
45
|
+
id: view.id
|
|
46
|
+
});
|
|
47
|
+
const columnRows = await query(connection, postgresColumnsQuery, [options.namespace], options, diagnostics, "columns");
|
|
48
|
+
const identityRows = await query(connection, postgresIdentitiesQuery, [options.namespace], options, diagnostics, "identities");
|
|
49
|
+
const identityOptionsByColumn = /* @__PURE__ */ new Map();
|
|
50
|
+
for (const row of identityRows) identityOptionsByColumn.set(columnKey(row.table_oid, row.ordinal_position), identityOptions(row));
|
|
51
|
+
const columnsByTable = groupBy(columnRows, (row) => row.table_oid);
|
|
52
|
+
for (const table of tables) table.columns = (columnsByTable.get(table.reference?.catalog?.value) ?? []).sort(compareOrdinal).map((row) => column(row, table, options.namespace, identityOptionsByColumn.get(columnKey(row.table_oid, row.ordinal_position))));
|
|
53
|
+
for (const view of views) view.columns = (columnsByTable.get(view.reference?.catalog?.value) ?? []).sort(compareOrdinal).map((row) => column(row, view, options.namespace));
|
|
54
|
+
const indexRows = await query(connection, postgresIndexesQuery, [options.namespace], options, diagnostics, "indexes");
|
|
55
|
+
const indexReferences = /* @__PURE__ */ new Map();
|
|
56
|
+
for (const row of indexRows) {
|
|
57
|
+
const indexOid = text(row.index_oid);
|
|
58
|
+
const physicalName = text(row.physical_name);
|
|
59
|
+
const ownerTable = tableByOid.get(text(row.table_oid));
|
|
60
|
+
if (indexOid && physicalName) indexReferences.set(`pg_class:${indexOid}`, {
|
|
61
|
+
kind: "index",
|
|
62
|
+
id: stableId(physicalName),
|
|
63
|
+
physicalName,
|
|
64
|
+
...ownerTable === void 0 ? {} : { owner: {
|
|
65
|
+
kind: "table",
|
|
66
|
+
id: ownerTable.id
|
|
67
|
+
} },
|
|
68
|
+
reference: reference("index", physicalName, options.namespace, "pg_class", "oid", row.index_oid)
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
const constraintsByTable = groupBy(await query(connection, (server.parsedVersion?.major ?? 0) >= 15 ? postgresConstraintsQuery : postgresConstraintsQueryWithoutNullsNotDistinct, [options.namespace], options, diagnostics, "constraints"), (row) => row.table_oid);
|
|
72
|
+
for (const table of tables) {
|
|
73
|
+
const rows = constraintsByTable.get(table.reference?.catalog?.value) ?? [];
|
|
74
|
+
const columnsByNumber = new Map(table.columns.map((column) => [column.ordinalPosition, column.physicalName]));
|
|
75
|
+
const constraints = [];
|
|
76
|
+
for (const value of rows.map((row) => constraint(row, table, columnsByNumber, tableByOid, options.namespace, diagnostics, indexReferences))) {
|
|
77
|
+
if (value === void 0) continue;
|
|
78
|
+
if (value.kind === "opaque-object") opaqueObjects.push(value);
|
|
79
|
+
else constraints.push(value);
|
|
80
|
+
}
|
|
81
|
+
table.constraints = constraints;
|
|
82
|
+
}
|
|
83
|
+
const indexesByTable = groupBy(indexRows, (row) => row.table_oid);
|
|
84
|
+
for (const table of tables) table.indexes = mapIndexes(indexesByTable.get(table.reference?.catalog?.value) ?? [], table, new Map(table.columns.map((column) => [column.ordinalPosition, column.physicalName])), options.namespace, diagnostics, opaqueObjects);
|
|
85
|
+
const sequences = (await query(connection, postgresSequencesQuery, [options.namespace], options, diagnostics, "sequences")).map((row) => sequenceObject(row, options.namespace, tableByOid, diagnostics));
|
|
86
|
+
const enums = mapEnums(await query(connection, postgresEnumsQuery, [options.namespace], options, diagnostics, "enums"), options.namespace);
|
|
87
|
+
const domains = mapDomains(await query(connection, postgresDomainsQuery, [options.namespace], options, diagnostics, "domains"), await query(connection, postgresDomainConstraintsQuery, [options.namespace], options, diagnostics, "domain-constraints"), options.namespace, opaqueObjects, diagnostics);
|
|
88
|
+
const collations = (await query(connection, postgresCollationsQuery, [options.namespace], options, diagnostics, "collations")).map((row) => collationObject(row, options.namespace));
|
|
89
|
+
const triggerRows = await query(connection, postgresTriggersQuery, [options.namespace], options, diagnostics, "triggers");
|
|
90
|
+
const triggers = [];
|
|
91
|
+
const triggerDeferredObjects = [];
|
|
92
|
+
for (const row of triggerRows) {
|
|
93
|
+
const value = triggerObject(row, options.namespace, relationReferences, diagnostics);
|
|
94
|
+
if (value.kind === "deferred-object") triggerDeferredObjects.push(value);
|
|
95
|
+
else triggers.push(value);
|
|
96
|
+
}
|
|
97
|
+
const routines = mapRoutines(await query(connection, postgresRoutinesQuery, [options.namespace], options, diagnostics, "routines"), await query(connection, postgresRoutineParametersQuery, [options.namespace], options, diagnostics, "routine-parameters"), options.namespace, diagnostics);
|
|
98
|
+
const partitionRows = await query(connection, postgresPartitionsQuery, [options.namespace], options, diagnostics, "partitions");
|
|
99
|
+
const partitions = [];
|
|
100
|
+
for (const row of partitionRows) {
|
|
101
|
+
const value = partitionObject(row, options.namespace, tableByOid, diagnostics);
|
|
102
|
+
if (value === void 0) continue;
|
|
103
|
+
if (value.kind === "partition") partitions.push(value);
|
|
104
|
+
else if (value.kind === "deferred-object") deferredObjects.push(value);
|
|
105
|
+
else opaqueObjects.push(value);
|
|
106
|
+
}
|
|
107
|
+
const policies = (await query(connection, postgresPoliciesQuery, [options.namespace], options, diagnostics, "policies")).map((row) => policyObject(row, options.namespace, tableByOid, diagnostics)).filter((value) => value !== void 0);
|
|
108
|
+
const extensionObjects = (await query(connection, postgresExtensionsQuery, [options.namespace], options, diagnostics, "extensions")).map((row) => extensionObject(row, options.namespace));
|
|
109
|
+
const typedRelationOids = new Set([...views, ...sequences].map((object) => object.reference?.catalog?.value));
|
|
110
|
+
deferredObjects.push(...relationObjects.filter((row) => {
|
|
111
|
+
const relkind = text(row.relkind);
|
|
112
|
+
return relkind === "f" || (relkind === "v" || relkind === "m" || relkind === "S") && !typedRelationOids.has(row.oid === void 0 ? void 0 : text(row.oid));
|
|
113
|
+
}).map((row) => deferredObject(row, text(row.relkind) === "f" ? "foreign-table" : text(row.relkind) === "v" ? "view" : text(row.relkind) === "m" ? "materialized-view" : "sequence")));
|
|
114
|
+
deferredObjects.push(...triggerDeferredObjects);
|
|
115
|
+
opaqueObjects.push(...relationObjects.filter((row) => {
|
|
116
|
+
const kind = text(row.relkind);
|
|
117
|
+
return kind !== "r" && kind !== "p" && kind !== "v" && kind !== "m" && kind !== "S" && kind !== "f";
|
|
118
|
+
}).map((row) => opaqueRelationObject(row, options.namespace, diagnostics)));
|
|
119
|
+
const metadata = mapMetadataRows(await query(connection, postgresMetadataQuery, [options.namespace], options, diagnostics, "comments-and-ownership"), options.namespace, tables, views, sequences, enums, domains, collations, routines, triggers, policies, extensionObjects, deferredObjects, opaqueObjects, indexReferences, relationReferences, diagnostics);
|
|
120
|
+
opaqueObjects.push(...metadata.opaqueObjects);
|
|
121
|
+
return Object.freeze({
|
|
122
|
+
dialect: "postgresql",
|
|
123
|
+
server,
|
|
124
|
+
namespace: {
|
|
125
|
+
kind: "postgres-schema",
|
|
126
|
+
name: options.namespace,
|
|
127
|
+
reference: reference("namespace", options.namespace, options.namespace, "pg_namespace", "nspname")
|
|
128
|
+
},
|
|
129
|
+
tables: Object.freeze(tables),
|
|
130
|
+
views: Object.freeze(views),
|
|
131
|
+
sequences: Object.freeze(sequences),
|
|
132
|
+
enums: Object.freeze(enums),
|
|
133
|
+
domains: Object.freeze(domains),
|
|
134
|
+
collations: Object.freeze(collations),
|
|
135
|
+
triggers: Object.freeze(triggers),
|
|
136
|
+
routines: Object.freeze(routines),
|
|
137
|
+
partitions: Object.freeze(partitions),
|
|
138
|
+
policies: Object.freeze(policies),
|
|
139
|
+
extensionObjects: Object.freeze(extensionObjects),
|
|
140
|
+
deferredObjects: Object.freeze(deferredObjects),
|
|
141
|
+
opaqueObjects: Object.freeze(opaqueObjects),
|
|
142
|
+
comments: Object.freeze(metadata.comments),
|
|
143
|
+
ownership: Object.freeze(metadata.ownership),
|
|
144
|
+
capabilities: server.capabilities,
|
|
145
|
+
diagnostics: Object.freeze(diagnostics)
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
const postgresRelationsQuery = `
|
|
149
|
+
SELECT c.oid::text AS oid, n.nspname AS namespace, c.relname,
|
|
150
|
+
c.relkind, c.relispartition
|
|
151
|
+
FROM pg_class c
|
|
152
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
153
|
+
WHERE n.nspname = $1
|
|
154
|
+
AND c.relkind NOT IN ('i', 'I')
|
|
155
|
+
ORDER BY c.relname
|
|
156
|
+
`;
|
|
157
|
+
/** Fixed view and materialized-view definitions from PostgreSQL's decompiler. */
|
|
158
|
+
const postgresViewsQuery = `
|
|
159
|
+
SELECT c.oid::text AS oid, n.nspname AS namespace, c.relname AS physical_name,
|
|
160
|
+
c.relkind, pg_get_viewdef(c.oid, true) AS definition,
|
|
161
|
+
v.check_option, c.reloptions
|
|
162
|
+
FROM pg_catalog.pg_class c
|
|
163
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
164
|
+
LEFT JOIN information_schema.views v
|
|
165
|
+
ON v.table_schema = n.nspname AND v.table_name = c.relname
|
|
166
|
+
WHERE n.nspname = $1 AND c.relkind IN ('v', 'm')
|
|
167
|
+
ORDER BY c.oid
|
|
168
|
+
`;
|
|
169
|
+
const postgresColumnsQuery = `
|
|
170
|
+
SELECT a.attrelid::text AS table_oid, a.attnum::int AS ordinal_position,
|
|
171
|
+
a.attname AS physical_name, NOT a.attnotnull AS nullable,
|
|
172
|
+
format_type(a.atttypid, a.atttypmod) AS native_type,
|
|
173
|
+
a.attidentity, a.attgenerated,
|
|
174
|
+
pg_get_expr(ad.adbin, ad.adrelid) AS default_expression
|
|
175
|
+
FROM pg_attribute a
|
|
176
|
+
JOIN pg_class c ON c.oid = a.attrelid
|
|
177
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
178
|
+
LEFT JOIN pg_attrdef ad ON ad.adrelid = a.attrelid AND ad.adnum = a.attnum
|
|
179
|
+
WHERE n.nspname = $1
|
|
180
|
+
AND c.relkind IN ('r', 'p', 'v', 'm')
|
|
181
|
+
AND a.attnum > 0
|
|
182
|
+
AND NOT a.attisdropped
|
|
183
|
+
ORDER BY a.attrelid, a.attnum
|
|
184
|
+
`;
|
|
185
|
+
/** Constraint metadata for PostgreSQL versions before pg_index.indnullsnotdistinct. */
|
|
186
|
+
const postgresConstraintsQueryWithoutNullsNotDistinct = `
|
|
187
|
+
SELECT con.oid::text AS oid, con.conrelid::text AS table_oid,
|
|
188
|
+
con.conname AS physical_name, con.contype,
|
|
189
|
+
con.conkey, con.confrelid::text AS target_table_oid, con.confkey,
|
|
190
|
+
con.confupdtype, con.confdeltype, con.confmatchtype,
|
|
191
|
+
con.condeferrable, con.condeferred, con.convalidated,
|
|
192
|
+
con.conindid::text AS backing_index_oid,
|
|
193
|
+
pg_get_constraintdef(con.oid, true) AS definition
|
|
194
|
+
FROM pg_constraint con
|
|
195
|
+
JOIN pg_class c ON c.oid = con.conrelid
|
|
196
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
197
|
+
LEFT JOIN pg_index i ON i.indexrelid = con.conindid
|
|
198
|
+
WHERE n.nspname = $1
|
|
199
|
+
AND con.contype IN ('p', 'u', 'f', 'c', 'x')
|
|
200
|
+
ORDER BY con.conrelid, con.oid
|
|
201
|
+
`;
|
|
202
|
+
/** Constraint metadata for PostgreSQL versions that expose nulls-not-distinct indexes. */
|
|
203
|
+
const postgresConstraintsQuery = `
|
|
204
|
+
SELECT con.oid::text AS oid, con.conrelid::text AS table_oid,
|
|
205
|
+
con.conname AS physical_name, con.contype,
|
|
206
|
+
con.conkey, con.confrelid::text AS target_table_oid, con.confkey,
|
|
207
|
+
con.confupdtype, con.confdeltype, con.confmatchtype,
|
|
208
|
+
con.condeferrable, con.condeferred, con.convalidated,
|
|
209
|
+
con.conindid::text AS backing_index_oid,
|
|
210
|
+
i.indnullsnotdistinct,
|
|
211
|
+
pg_get_constraintdef(con.oid, true) AS definition
|
|
212
|
+
FROM pg_constraint con
|
|
213
|
+
JOIN pg_class c ON c.oid = con.conrelid
|
|
214
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
215
|
+
LEFT JOIN pg_index i ON i.indexrelid = con.conindid
|
|
216
|
+
WHERE n.nspname = $1
|
|
217
|
+
AND con.contype IN ('p', 'u', 'f', 'c', 'x')
|
|
218
|
+
ORDER BY con.conrelid, con.oid
|
|
219
|
+
`;
|
|
220
|
+
const postgresIndexesQuery = `
|
|
221
|
+
SELECT i.indexrelid::text AS index_oid, i.indrelid::text AS table_oid,
|
|
222
|
+
c.relname AS physical_name, i.indisunique, i.indnkeyatts::int,
|
|
223
|
+
i.indnatts::int, am.amname AS method,
|
|
224
|
+
pg_get_expr(i.indpred, i.indrelid, true) AS predicate,
|
|
225
|
+
s.position::int,
|
|
226
|
+
(i.indkey[s.position])::int AS attnum,
|
|
227
|
+
(i.indoption[s.position])::int AS indoption,
|
|
228
|
+
opc.opcname AS operator_class,
|
|
229
|
+
pg_get_indexdef(i.indexrelid, s.position, true) AS term_definition
|
|
230
|
+
FROM pg_index i
|
|
231
|
+
JOIN pg_class c ON c.oid = i.indexrelid
|
|
232
|
+
JOIN pg_class t ON t.oid = i.indrelid
|
|
233
|
+
JOIN pg_namespace n ON n.oid = t.relnamespace
|
|
234
|
+
JOIN pg_am am ON am.oid = c.relam
|
|
235
|
+
CROSS JOIN LATERAL generate_series(1, i.indnatts) AS s(position)
|
|
236
|
+
LEFT JOIN pg_opclass opc ON opc.oid = i.indclass[s.position]
|
|
237
|
+
WHERE n.nspname = $1
|
|
238
|
+
ORDER BY i.indrelid, i.indexrelid, s.position
|
|
239
|
+
`;
|
|
240
|
+
/** Identity sequence options joined to each generated identity column. */
|
|
241
|
+
const postgresIdentitiesQuery = `
|
|
242
|
+
SELECT a.attrelid::text AS table_oid, a.attnum::int AS ordinal_position,
|
|
243
|
+
s.seqstart, s.seqincrement, s.seqmin, s.seqmax, s.seqcache,
|
|
244
|
+
s.seqcycle, format_type(s.seqtypid, -1) AS sequence_type
|
|
245
|
+
FROM pg_catalog.pg_attribute a
|
|
246
|
+
JOIN pg_catalog.pg_class c ON c.oid = a.attrelid
|
|
247
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
248
|
+
JOIN pg_catalog.pg_depend d
|
|
249
|
+
ON d.refclassid = 'pg_catalog.pg_class'::regclass
|
|
250
|
+
AND d.refobjid = a.attrelid AND d.refobjsubid = a.attnum
|
|
251
|
+
AND d.classid = 'pg_catalog.pg_class'::regclass
|
|
252
|
+
AND d.deptype = 'i'
|
|
253
|
+
JOIN pg_catalog.pg_sequence s ON s.seqrelid = d.objid
|
|
254
|
+
WHERE n.nspname = $1 AND a.attidentity <> ''
|
|
255
|
+
ORDER BY a.attrelid, a.attnum
|
|
256
|
+
`;
|
|
257
|
+
/** Sequence metadata, including serial/identity ownership dependencies. */
|
|
258
|
+
const postgresSequencesQuery = `
|
|
259
|
+
SELECT c.oid::text AS oid, n.nspname AS namespace, c.relname AS physical_name,
|
|
260
|
+
format_type(s.seqtypid, -1) AS native_type,
|
|
261
|
+
s.seqstart, s.seqincrement, s.seqmin, s.seqmax, s.seqcache,
|
|
262
|
+
s.seqcycle, dep.refobjid::text AS owned_table_oid,
|
|
263
|
+
dep.refobjsubid::int AS owned_column_position
|
|
264
|
+
FROM pg_catalog.pg_sequence s
|
|
265
|
+
JOIN pg_catalog.pg_class c ON c.oid = s.seqrelid
|
|
266
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
267
|
+
LEFT JOIN pg_catalog.pg_depend dep
|
|
268
|
+
ON dep.classid = 'pg_catalog.pg_class'::regclass
|
|
269
|
+
AND dep.objid = c.oid AND dep.refclassid = 'pg_catalog.pg_class'::regclass
|
|
270
|
+
AND dep.deptype IN ('a', 'i')
|
|
271
|
+
WHERE n.nspname = $1
|
|
272
|
+
ORDER BY c.oid
|
|
273
|
+
`;
|
|
274
|
+
/** PostgreSQL enum labels in their declared order. */
|
|
275
|
+
const postgresEnumsQuery = `
|
|
276
|
+
SELECT t.oid::text AS oid, n.nspname AS namespace,
|
|
277
|
+
t.typname AS physical_name, e.oid::text AS value_oid,
|
|
278
|
+
e.enumlabel AS value, e.enumsortorder::float8 AS ordinal_position
|
|
279
|
+
FROM pg_catalog.pg_type t
|
|
280
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
281
|
+
JOIN pg_catalog.pg_enum e ON e.enumtypid = t.oid
|
|
282
|
+
WHERE n.nspname = $1 AND t.typtype = 'e'
|
|
283
|
+
ORDER BY t.oid, e.enumsortorder
|
|
284
|
+
`;
|
|
285
|
+
/** PostgreSQL domains and their exact base type/default metadata. */
|
|
286
|
+
const postgresDomainsQuery = `
|
|
287
|
+
SELECT t.oid::text AS oid, n.nspname AS namespace,
|
|
288
|
+
t.typname AS physical_name,
|
|
289
|
+
format_type(t.typbasetype, t.typtypmod) AS native_type,
|
|
290
|
+
NOT t.typnotnull AS nullable, t.typdefault AS default_expression
|
|
291
|
+
FROM pg_catalog.pg_type t
|
|
292
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
293
|
+
WHERE n.nspname = $1 AND t.typtype = 'd'
|
|
294
|
+
ORDER BY t.oid
|
|
295
|
+
`;
|
|
296
|
+
/** Domain CHECK constraints, which use pg_constraint.contypid. */
|
|
297
|
+
const postgresDomainConstraintsQuery = `
|
|
298
|
+
SELECT con.oid::text AS oid, con.contypid::text AS domain_oid,
|
|
299
|
+
con.conname AS physical_name, pg_get_constraintdef(con.oid, true) AS definition,
|
|
300
|
+
con.condeferrable, con.condeferred, con.convalidated
|
|
301
|
+
FROM pg_catalog.pg_constraint con
|
|
302
|
+
JOIN pg_catalog.pg_type t ON t.oid = con.contypid
|
|
303
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
304
|
+
WHERE n.nspname = $1 AND con.contype = 'c'
|
|
305
|
+
ORDER BY con.contypid, con.oid
|
|
306
|
+
`;
|
|
307
|
+
/** Collation provider, locale, determinism, and version facts. */
|
|
308
|
+
const postgresCollationsQuery = `
|
|
309
|
+
SELECT c.oid::text AS oid, n.nspname AS namespace,
|
|
310
|
+
c.collname AS physical_name, c.collprovider, c.collcollate,
|
|
311
|
+
c.collctype, to_jsonb(c)->>'colllocale' AS colliculocale,
|
|
312
|
+
c.collisdeterministic, c.collversion
|
|
313
|
+
FROM pg_catalog.pg_collation c
|
|
314
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.collnamespace
|
|
315
|
+
WHERE n.nspname = $1
|
|
316
|
+
ORDER BY c.oid
|
|
317
|
+
`;
|
|
318
|
+
/** User-defined triggers with decompiled condition and definition text. */
|
|
319
|
+
const postgresTriggersQuery = `
|
|
320
|
+
SELECT t.oid::text AS oid, t.tgrelid::text AS table_oid,
|
|
321
|
+
n.nspname AS namespace, t.tgname AS physical_name,
|
|
322
|
+
t.tgtype::int AS trigger_type, t.tgenabled,
|
|
323
|
+
pg_get_expr(t.tgqual, t.tgrelid, true) AS condition,
|
|
324
|
+
pg_get_triggerdef(t.oid, true) AS definition
|
|
325
|
+
FROM pg_catalog.pg_trigger t
|
|
326
|
+
JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid
|
|
327
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
328
|
+
WHERE n.nspname = $1 AND NOT t.tgisinternal
|
|
329
|
+
ORDER BY t.tgrelid, t.oid
|
|
330
|
+
`;
|
|
331
|
+
/** PostgreSQL functions, procedures, aggregates, and window routines. */
|
|
332
|
+
const postgresRoutinesQuery = `
|
|
333
|
+
SELECT p.oid::text AS oid, n.nspname AS namespace,
|
|
334
|
+
p.proname AS physical_name, p.prokind,
|
|
335
|
+
format_type(p.prorettype, NULL) AS return_type,
|
|
336
|
+
l.lanname AS language, pg_get_functiondef(p.oid) AS definition,
|
|
337
|
+
pg_get_function_identity_arguments(p.oid) AS identity_arguments,
|
|
338
|
+
pg_get_expr(p.proargdefaults, 0, true) AS argument_defaults,
|
|
339
|
+
p.pronargdefaults, p.provolatile, p.proparallel, p.prosecdef
|
|
340
|
+
FROM pg_catalog.pg_proc p
|
|
341
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
|
|
342
|
+
JOIN pg_catalog.pg_language l ON l.oid = p.prolang
|
|
343
|
+
WHERE n.nspname = $1 AND p.prokind IN ('f', 'p', 'a', 'w')
|
|
344
|
+
ORDER BY p.oid
|
|
345
|
+
`;
|
|
346
|
+
/** Routine parameter rows using PostgreSQL's complete argument type arrays. */
|
|
347
|
+
const postgresRoutineParametersQuery = `
|
|
348
|
+
SELECT p.oid::text AS routine_oid, x.position::int AS ordinal_position,
|
|
349
|
+
names.name AS parameter_name, modes.mode,
|
|
350
|
+
format_type(x.type_oid, -1) AS native_type
|
|
351
|
+
FROM pg_catalog.pg_proc p
|
|
352
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
|
|
353
|
+
CROSS JOIN LATERAL unnest(
|
|
354
|
+
COALESCE(p.proallargtypes, p.proargtypes::oid[])
|
|
355
|
+
) WITH ORDINALITY AS x(type_oid, position)
|
|
356
|
+
LEFT JOIN LATERAL unnest(p.proargnames) WITH ORDINALITY
|
|
357
|
+
AS names(name, position) ON names.position = x.position
|
|
358
|
+
LEFT JOIN LATERAL unnest(p.proargmodes) WITH ORDINALITY
|
|
359
|
+
AS modes(mode, position) ON modes.position = x.position
|
|
360
|
+
WHERE n.nspname = $1 AND p.prokind IN ('f', 'p', 'a', 'w')
|
|
361
|
+
ORDER BY p.oid, x.position
|
|
362
|
+
`;
|
|
363
|
+
/** Partition children, key attributes, strategy, and bound expression. */
|
|
364
|
+
const postgresPartitionsQuery = `
|
|
365
|
+
SELECT child.oid::text AS partition_oid, parent.oid::text AS parent_oid,
|
|
366
|
+
n.nspname AS namespace, child.relname AS physical_name,
|
|
367
|
+
part.partstrat, part.partattrs::text AS key_attributes,
|
|
368
|
+
pg_get_partkeydef(parent.oid) AS key_definition,
|
|
369
|
+
pg_get_expr(child.relpartbound, child.oid, true) AS bound,
|
|
370
|
+
child.relispartition, child.relkind
|
|
371
|
+
FROM pg_catalog.pg_inherits inh
|
|
372
|
+
JOIN pg_catalog.pg_class child ON child.oid = inh.inhrelid
|
|
373
|
+
JOIN pg_catalog.pg_class parent ON parent.oid = inh.inhparent
|
|
374
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = child.relnamespace
|
|
375
|
+
LEFT JOIN pg_catalog.pg_partitioned_table part ON part.partrelid = parent.oid
|
|
376
|
+
WHERE n.nspname = $1
|
|
377
|
+
ORDER BY parent.oid, child.oid
|
|
378
|
+
`;
|
|
379
|
+
/** Row-level security policies and their role/expression metadata. */
|
|
380
|
+
const postgresPoliciesQuery = `
|
|
381
|
+
SELECT p.oid::text AS oid, p.polrelid::text AS table_oid,
|
|
382
|
+
n.nspname AS namespace, p.polname AS physical_name,
|
|
383
|
+
p.polcmd, p.polpermissive,
|
|
384
|
+
ARRAY(SELECT r.rolname FROM pg_catalog.pg_roles r
|
|
385
|
+
WHERE r.oid = ANY(p.polroles)) AS roles,
|
|
386
|
+
pg_get_expr(p.polqual, p.polrelid, true) AS using_expression,
|
|
387
|
+
pg_get_expr(p.polwithcheck, p.polrelid, true) AS check_expression
|
|
388
|
+
FROM pg_catalog.pg_policy p
|
|
389
|
+
JOIN pg_catalog.pg_class c ON c.oid = p.polrelid
|
|
390
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
391
|
+
WHERE n.nspname = $1
|
|
392
|
+
ORDER BY p.polrelid, p.oid
|
|
393
|
+
`;
|
|
394
|
+
/** Installed PostgreSQL extensions scoped to the selected schema. */
|
|
395
|
+
const postgresExtensionsQuery = `
|
|
396
|
+
SELECT e.oid::text AS oid, n.nspname AS namespace,
|
|
397
|
+
e.extname AS physical_name, e.extversion, e.extrelocatable,
|
|
398
|
+
e.extconfig::text AS config_relations,
|
|
399
|
+
e.extcondition::text AS config_conditions
|
|
400
|
+
FROM pg_catalog.pg_extension e
|
|
401
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace
|
|
402
|
+
WHERE n.nspname = $1
|
|
403
|
+
ORDER BY e.oid
|
|
404
|
+
`;
|
|
405
|
+
/** Comments and owners for PostgreSQL objects visible in one schema. */
|
|
406
|
+
const postgresMetadataQuery = `
|
|
407
|
+
WITH objects AS (
|
|
408
|
+
SELECT 'pg_class'::text AS catalog_relation, 'pg_class'::regclass AS catalog_oid,
|
|
409
|
+
c.oid::text AS object_oid, 0::int AS object_subid,
|
|
410
|
+
CASE c.relkind WHEN 'r' THEN 'table' WHEN 'p' THEN 'table'
|
|
411
|
+
WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized-view'
|
|
412
|
+
WHEN 'S' THEN 'sequence' WHEN 'i' THEN 'index'
|
|
413
|
+
WHEN 'I' THEN 'index' WHEN 'f' THEN 'deferred-object'
|
|
414
|
+
ELSE 'opaque-object' END AS object_kind,
|
|
415
|
+
c.relname AS object_name, n.nspname AS namespace,
|
|
416
|
+
c.relowner::oid AS owner_oid, NULL::text AS parent_oid
|
|
417
|
+
FROM pg_catalog.pg_class c
|
|
418
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
419
|
+
WHERE n.nspname = $1
|
|
420
|
+
UNION ALL
|
|
421
|
+
SELECT 'pg_class', 'pg_class'::regclass, c.oid::text, a.attnum::int,
|
|
422
|
+
'column', a.attname, n.nspname, c.relowner::oid, c.oid::text
|
|
423
|
+
FROM pg_catalog.pg_attribute a
|
|
424
|
+
JOIN pg_catalog.pg_class c ON c.oid = a.attrelid
|
|
425
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
426
|
+
WHERE n.nspname = $1 AND c.relkind IN ('r', 'p', 'v', 'm')
|
|
427
|
+
AND a.attnum > 0 AND NOT a.attisdropped
|
|
428
|
+
UNION ALL
|
|
429
|
+
SELECT 'pg_proc', 'pg_proc'::regclass, p.oid::text, 0,
|
|
430
|
+
'routine', p.proname, n.nspname, p.proowner::oid, NULL
|
|
431
|
+
FROM pg_catalog.pg_proc p
|
|
432
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
|
|
433
|
+
WHERE n.nspname = $1 AND p.prokind IN ('f', 'p', 'a', 'w')
|
|
434
|
+
UNION ALL
|
|
435
|
+
SELECT 'pg_type', 'pg_type'::regclass, t.oid::text, 0,
|
|
436
|
+
CASE t.typtype WHEN 'e' THEN 'enum' ELSE 'domain' END,
|
|
437
|
+
t.typname, n.nspname, t.typowner::oid, NULL
|
|
438
|
+
FROM pg_catalog.pg_type t
|
|
439
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
440
|
+
WHERE n.nspname = $1 AND t.typtype IN ('e', 'd')
|
|
441
|
+
UNION ALL
|
|
442
|
+
SELECT 'pg_collation', 'pg_collation'::regclass, c.oid::text, 0,
|
|
443
|
+
'collation', c.collname, n.nspname, c.collowner::oid, NULL
|
|
444
|
+
FROM pg_catalog.pg_collation c
|
|
445
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.collnamespace
|
|
446
|
+
WHERE n.nspname = $1
|
|
447
|
+
UNION ALL
|
|
448
|
+
SELECT 'pg_extension', 'pg_extension'::regclass, e.oid::text, 0,
|
|
449
|
+
'extension', e.extname, n.nspname, e.extowner::oid, NULL
|
|
450
|
+
FROM pg_catalog.pg_extension e
|
|
451
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace
|
|
452
|
+
WHERE n.nspname = $1
|
|
453
|
+
UNION ALL
|
|
454
|
+
SELECT 'pg_constraint', 'pg_constraint'::regclass, con.oid::text, 0,
|
|
455
|
+
'constraint', con.conname, n.nspname, c.relowner::oid,
|
|
456
|
+
con.conrelid::text
|
|
457
|
+
FROM pg_catalog.pg_constraint con
|
|
458
|
+
JOIN pg_catalog.pg_class c ON c.oid = con.conrelid
|
|
459
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
460
|
+
WHERE n.nspname = $1
|
|
461
|
+
UNION ALL
|
|
462
|
+
SELECT 'pg_trigger', 'pg_trigger'::regclass, t.oid::text, 0,
|
|
463
|
+
'trigger', t.tgname, n.nspname, c.relowner::oid,
|
|
464
|
+
t.tgrelid::text
|
|
465
|
+
FROM pg_catalog.pg_trigger t
|
|
466
|
+
JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid
|
|
467
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
468
|
+
WHERE n.nspname = $1 AND NOT t.tgisinternal
|
|
469
|
+
UNION ALL
|
|
470
|
+
SELECT 'pg_policy', 'pg_policy'::regclass, p.oid::text, 0,
|
|
471
|
+
'policy', p.polname, n.nspname, c.relowner::oid,
|
|
472
|
+
p.polrelid::text
|
|
473
|
+
FROM pg_catalog.pg_policy p
|
|
474
|
+
JOIN pg_catalog.pg_class c ON c.oid = p.polrelid
|
|
475
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
476
|
+
WHERE n.nspname = $1
|
|
477
|
+
)
|
|
478
|
+
SELECT o.catalog_relation, o.object_oid, o.object_subid,
|
|
479
|
+
o.object_kind, o.object_name, o.namespace, o.parent_oid,
|
|
480
|
+
d.description, pg_get_userbyid(o.owner_oid) AS owner
|
|
481
|
+
FROM objects o
|
|
482
|
+
LEFT JOIN pg_catalog.pg_description d
|
|
483
|
+
ON d.classoid = o.catalog_oid AND d.objoid::text = o.object_oid
|
|
484
|
+
AND d.objsubid = o.object_subid
|
|
485
|
+
ORDER BY o.catalog_relation, o.object_oid, o.object_subid
|
|
486
|
+
`;
|
|
487
|
+
async function query(connection, text, parameters, options, diagnostics, operation) {
|
|
488
|
+
try {
|
|
489
|
+
return await connection.query({
|
|
490
|
+
text,
|
|
491
|
+
parameters
|
|
492
|
+
}, { signal: options.signal });
|
|
493
|
+
} catch {
|
|
494
|
+
diagnostics.push(createIntrospectionDiagnostic({
|
|
495
|
+
severity: "error",
|
|
496
|
+
code: "query-failed",
|
|
497
|
+
message: `PostgreSQL catalog query failed while reading ${operation}`,
|
|
498
|
+
path: [operation],
|
|
499
|
+
remediation: "Check PostgreSQL permissions and the selected schema."
|
|
500
|
+
}));
|
|
501
|
+
return [];
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
function serverInfo(row, diagnostics) {
|
|
505
|
+
const rawVersion = text(row?.server_version) ?? "unknown";
|
|
506
|
+
const numericVersion = number(row?.server_version_num);
|
|
507
|
+
if (!row) diagnostics.push(createIntrospectionDiagnostic({
|
|
508
|
+
severity: "error",
|
|
509
|
+
code: "unsupported-server",
|
|
510
|
+
message: "PostgreSQL server version metadata was unavailable",
|
|
511
|
+
path: ["server"],
|
|
512
|
+
remediation: "Provide access to current_setting(server_version_num)."
|
|
513
|
+
}));
|
|
514
|
+
const major = numericVersion ? Math.floor(numericVersion / 1e4) : void 0;
|
|
515
|
+
const supported = major !== void 0 && major >= 12;
|
|
516
|
+
if (!supported) diagnostics.push(createIntrospectionDiagnostic({
|
|
517
|
+
severity: "error",
|
|
518
|
+
code: "unsupported-server",
|
|
519
|
+
message: "PostgreSQL introspection requires server version 12 or newer",
|
|
520
|
+
path: ["server", "version"],
|
|
521
|
+
remediation: "Use PostgreSQL 12+ or an adapter capability extension."
|
|
522
|
+
}));
|
|
523
|
+
return {
|
|
524
|
+
product: "postgresql",
|
|
525
|
+
rawVersion,
|
|
526
|
+
parsedVersion: major === void 0 ? void 0 : { major },
|
|
527
|
+
capabilities: {
|
|
528
|
+
generatedColumns: supported,
|
|
529
|
+
identityMetadata: numericVersion !== void 0 && numericVersion >= 1e5,
|
|
530
|
+
checkConstraints: true,
|
|
531
|
+
checkConstraintEnforcement: "enforced",
|
|
532
|
+
expressionDecompilation: true,
|
|
533
|
+
indexExpressions: true,
|
|
534
|
+
indexPredicates: true,
|
|
535
|
+
indexIncludedColumns: true,
|
|
536
|
+
namespaces: true,
|
|
537
|
+
visibility: "complete"
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
function emptyCatalog(namespace, diagnostics) {
|
|
542
|
+
return {
|
|
543
|
+
dialect: "postgresql",
|
|
544
|
+
server: {
|
|
545
|
+
product: "postgresql",
|
|
546
|
+
rawVersion: "unknown",
|
|
547
|
+
capabilities: {
|
|
548
|
+
generatedColumns: false,
|
|
549
|
+
identityMetadata: false,
|
|
550
|
+
checkConstraints: false,
|
|
551
|
+
checkConstraintEnforcement: "unknown",
|
|
552
|
+
expressionDecompilation: false,
|
|
553
|
+
indexExpressions: false,
|
|
554
|
+
indexPredicates: false,
|
|
555
|
+
indexIncludedColumns: false,
|
|
556
|
+
namespaces: false,
|
|
557
|
+
visibility: "unknown"
|
|
558
|
+
}
|
|
559
|
+
},
|
|
560
|
+
namespace: {
|
|
561
|
+
kind: "postgres-schema",
|
|
562
|
+
name: namespace
|
|
563
|
+
},
|
|
564
|
+
tables: [],
|
|
565
|
+
deferredObjects: [],
|
|
566
|
+
diagnostics: Object.freeze(diagnostics)
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
function viewObject(row, namespace, diagnostics) {
|
|
570
|
+
const physicalName = text(row.physical_name) ?? "unnamed_view";
|
|
571
|
+
const kind = row.relkind === "m" ? "materialized-view" : "view";
|
|
572
|
+
const physicalReference = reference(kind, physicalName, text(row.namespace) ?? namespace, "pg_class", "oid", row.oid);
|
|
573
|
+
const definition = text(row.definition);
|
|
574
|
+
if (definition === void 0) {
|
|
575
|
+
diagnostics.push(createIntrospectionDiagnostic({
|
|
576
|
+
severity: "warning",
|
|
577
|
+
code: "unmodeled-object",
|
|
578
|
+
message: `PostgreSQL ${kind} ${physicalName} has no recoverable definition`,
|
|
579
|
+
path: [
|
|
580
|
+
"views",
|
|
581
|
+
physicalName,
|
|
582
|
+
"definition"
|
|
583
|
+
],
|
|
584
|
+
physicalReference,
|
|
585
|
+
remediation: "Grant access to pg_get_viewdef before treating this object as migration input."
|
|
586
|
+
}));
|
|
587
|
+
return deferredCatalogObject(kind === "view" ? "view" : "materialized-view", physicalName, physicalReference);
|
|
588
|
+
}
|
|
589
|
+
const options = stringArray(row.reloptions);
|
|
590
|
+
const checkOption = (() => {
|
|
591
|
+
const value = text(row.check_option)?.toLowerCase();
|
|
592
|
+
return value === "none" || value === "local" || value === "cascaded" ? value : void 0;
|
|
593
|
+
})();
|
|
594
|
+
return {
|
|
595
|
+
kind,
|
|
596
|
+
id: stableId(physicalName),
|
|
597
|
+
identitySource: "physical-name",
|
|
598
|
+
physicalName,
|
|
599
|
+
columns: [],
|
|
600
|
+
definition: expression(definition, "decompiler", physicalReference),
|
|
601
|
+
...checkOption === "local" || checkOption === "cascaded" ? { checkOption } : checkOption === "none" ? { checkOption: "none" } : {},
|
|
602
|
+
...booleanOption(options, "security_barrier") === void 0 ? {} : { securityBarrier: booleanOption(options, "security_barrier") },
|
|
603
|
+
...booleanOption(options, "security_invoker") === void 0 ? {} : { securityInvoker: booleanOption(options, "security_invoker") },
|
|
604
|
+
reference: physicalReference
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
function columnKey(tableOid, ordinalPosition) {
|
|
608
|
+
return `${text(tableOid) ?? "unknown"}:${number(ordinalPosition) ?? 0}`;
|
|
609
|
+
}
|
|
610
|
+
function compareOrdinal(left, right) {
|
|
611
|
+
return (number(left.ordinal_position) ?? 0) - (number(right.ordinal_position) ?? 0);
|
|
612
|
+
}
|
|
613
|
+
function identityOptions(row) {
|
|
614
|
+
const options = {};
|
|
615
|
+
for (const [key, value] of [
|
|
616
|
+
["start", row.seqstart],
|
|
617
|
+
["increment", row.seqincrement],
|
|
618
|
+
["minimum", row.seqmin],
|
|
619
|
+
["maximum", row.seqmax],
|
|
620
|
+
["cache", row.seqcache]
|
|
621
|
+
]) {
|
|
622
|
+
const fact = literalFact(value);
|
|
623
|
+
if (fact !== void 0) options[key] = fact;
|
|
624
|
+
}
|
|
625
|
+
if (row.seqcycle !== void 0) options.cycle = {
|
|
626
|
+
kind: "literal",
|
|
627
|
+
value: boolean(row.seqcycle)
|
|
628
|
+
};
|
|
629
|
+
if (text(row.sequence_type) !== void 0) options.type = {
|
|
630
|
+
kind: "literal",
|
|
631
|
+
value: text(row.sequence_type)
|
|
632
|
+
};
|
|
633
|
+
return options;
|
|
634
|
+
}
|
|
635
|
+
function sequenceObject(row, namespace, tableByOid, diagnostics) {
|
|
636
|
+
const physicalName = text(row.physical_name) ?? `sequence_${text(row.oid) ?? "unknown"}`;
|
|
637
|
+
const physicalReference = reference("sequence", physicalName, text(row.namespace) ?? namespace, "pg_class", "oid", row.oid);
|
|
638
|
+
const ownedTable = tableByOid.get(text(row.owned_table_oid));
|
|
639
|
+
const ownedBy = ownedTable ? {
|
|
640
|
+
kind: "table",
|
|
641
|
+
id: ownedTable.id
|
|
642
|
+
} : void 0;
|
|
643
|
+
const ownedTableOid = text(row.owned_table_oid);
|
|
644
|
+
const ownedColumnPosition = number(row.owned_column_position);
|
|
645
|
+
if (ownedTableOid !== void 0 && ownedBy === void 0) diagnostics.push(createIntrospectionDiagnostic({
|
|
646
|
+
severity: "warning",
|
|
647
|
+
code: "unresolved-reference",
|
|
648
|
+
message: `PostgreSQL sequence ${physicalName} has an ownership target outside the selected schema`,
|
|
649
|
+
path: [
|
|
650
|
+
"sequences",
|
|
651
|
+
physicalName,
|
|
652
|
+
"ownedBy"
|
|
653
|
+
],
|
|
654
|
+
physicalReference,
|
|
655
|
+
remediation: "Select the owning table schema or inspect the retained ownership catalog reference."
|
|
656
|
+
}));
|
|
657
|
+
const data = {};
|
|
658
|
+
if (ownedTableOid !== void 0) data.ownedTableOid = ownedTableOid;
|
|
659
|
+
if (ownedColumnPosition !== void 0) data.ownedColumnPosition = ownedColumnPosition;
|
|
660
|
+
return {
|
|
661
|
+
kind: "sequence",
|
|
662
|
+
id: stableId(physicalName),
|
|
663
|
+
identitySource: "physical-name",
|
|
664
|
+
physicalName,
|
|
665
|
+
storage: storage(row.native_type),
|
|
666
|
+
...factIfPresent(row.seqstart) === void 0 ? {} : { start: factIfPresent(row.seqstart) },
|
|
667
|
+
...factIfPresent(row.seqincrement) === void 0 ? {} : { increment: factIfPresent(row.seqincrement) },
|
|
668
|
+
...factIfPresent(row.seqmin) === void 0 ? {} : { minimum: factIfPresent(row.seqmin) },
|
|
669
|
+
...factIfPresent(row.seqmax) === void 0 ? {} : { maximum: factIfPresent(row.seqmax) },
|
|
670
|
+
...factIfPresent(row.seqcache) === void 0 ? {} : { cache: factIfPresent(row.seqcache) },
|
|
671
|
+
...row.seqcycle === void 0 || row.seqcycle === null ? {} : { cycle: boolean(row.seqcycle) },
|
|
672
|
+
...ownedBy === void 0 ? {} : { ownedBy },
|
|
673
|
+
...Object.keys(data).length === 0 ? {} : { dialect: {
|
|
674
|
+
dialect: "postgresql",
|
|
675
|
+
version: 1,
|
|
676
|
+
data
|
|
677
|
+
} },
|
|
678
|
+
reference: physicalReference
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
function mapEnums(rows, namespace) {
|
|
682
|
+
const groups = /* @__PURE__ */ new Map();
|
|
683
|
+
for (const row of rows) {
|
|
684
|
+
const key = text(row.oid) ?? text(row.physical_name) ?? "unknown";
|
|
685
|
+
const values = groups.get(key) ?? [];
|
|
686
|
+
values.push(row);
|
|
687
|
+
groups.set(key, values);
|
|
688
|
+
}
|
|
689
|
+
return [...groups.values()].map((enumRows) => {
|
|
690
|
+
const first = enumRows[0];
|
|
691
|
+
const physicalName = text(first.physical_name) ?? `enum_${text(first.oid) ?? "unknown"}`;
|
|
692
|
+
const physicalReference = reference("enum", physicalName, text(first.namespace) ?? namespace, "pg_type", "oid", first.oid);
|
|
693
|
+
const values = [...enumRows].sort((left, right) => (number(left.ordinal_position) ?? 0) - (number(right.ordinal_position) ?? 0)).map((row, index) => ({
|
|
694
|
+
value: text(row.value) ?? "",
|
|
695
|
+
ordinalPosition: index + 1,
|
|
696
|
+
provenance: {
|
|
697
|
+
kind: "catalog",
|
|
698
|
+
dialect: "postgresql",
|
|
699
|
+
reference: reference("enum", physicalName, text(row.namespace) ?? namespace, "pg_enum", "oid", row.value_oid)
|
|
700
|
+
}
|
|
701
|
+
}));
|
|
702
|
+
return {
|
|
703
|
+
kind: "enum",
|
|
704
|
+
id: stableId(physicalName),
|
|
705
|
+
identitySource: "physical-name",
|
|
706
|
+
physicalName,
|
|
707
|
+
values,
|
|
708
|
+
reference: physicalReference
|
|
709
|
+
};
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
function mapDomains(rows, constraintRows, namespace, opaqueObjects, diagnostics) {
|
|
713
|
+
const constraintsByDomain = groupBy(constraintRows, (row) => text(row.domain_oid) ?? "unknown");
|
|
714
|
+
return rows.map((row) => {
|
|
715
|
+
const physicalName = text(row.physical_name) ?? `domain_${text(row.oid) ?? "unknown"}`;
|
|
716
|
+
const physicalReference = reference("domain", physicalName, text(row.namespace) ?? namespace, "pg_type", "oid", row.oid);
|
|
717
|
+
const domainConstraints = (constraintsByDomain.get(text(row.oid)) ?? []).map((constraintRow) => {
|
|
718
|
+
const constraintName = text(constraintRow.physical_name) ?? `domain_constraint_${text(constraintRow.oid) ?? "unknown"}`;
|
|
719
|
+
const constraintReference = reference("constraint", constraintName, text(row.namespace) ?? namespace, "pg_constraint", "oid", constraintRow.oid);
|
|
720
|
+
const definition = text(constraintRow.definition);
|
|
721
|
+
if (definition === void 0 || definition.trim() === "") {
|
|
722
|
+
diagnostics.push(createIntrospectionDiagnostic({
|
|
723
|
+
severity: "warning",
|
|
724
|
+
code: "unmodeled-object",
|
|
725
|
+
message: `PostgreSQL domain constraint ${constraintName} has no recoverable definition`,
|
|
726
|
+
path: [
|
|
727
|
+
"domains",
|
|
728
|
+
physicalName,
|
|
729
|
+
"constraints",
|
|
730
|
+
constraintName
|
|
731
|
+
],
|
|
732
|
+
physicalReference: constraintReference,
|
|
733
|
+
remediation: "Inspect the opaque domain constraint before using it as migration input."
|
|
734
|
+
}));
|
|
735
|
+
opaqueObjects.push(opaqueDomainConstraint(physicalName, constraintName, constraintReference, void 0));
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
return {
|
|
739
|
+
kind: "check",
|
|
740
|
+
id: stableId(constraintName),
|
|
741
|
+
identitySource: "physical-name",
|
|
742
|
+
physicalName: constraintName,
|
|
743
|
+
expression: expression(definition, "decompiler", constraintReference),
|
|
744
|
+
deferrable: constraintRow.condeferrable === void 0 ? void 0 : boolean(constraintRow.condeferrable),
|
|
745
|
+
initially: constraintRow.condeferred === void 0 ? void 0 : boolean(constraintRow.condeferred) ? "deferred" : "immediate",
|
|
746
|
+
validated: constraintRow.convalidated === void 0 ? void 0 : boolean(constraintRow.convalidated),
|
|
747
|
+
reference: constraintReference
|
|
748
|
+
};
|
|
749
|
+
}).filter((value) => value !== void 0);
|
|
750
|
+
const defaultValue = domainDefault(row.default_expression, physicalReference);
|
|
751
|
+
return {
|
|
752
|
+
kind: "domain",
|
|
753
|
+
id: stableId(physicalName),
|
|
754
|
+
identitySource: "physical-name",
|
|
755
|
+
physicalName,
|
|
756
|
+
storage: storage(row.native_type),
|
|
757
|
+
...row.nullable === void 0 || row.nullable === null ? {} : { nullable: boolean(row.nullable) },
|
|
758
|
+
...defaultValue === void 0 ? {} : { default: defaultValue },
|
|
759
|
+
...domainConstraints.length === 0 ? {} : { constraints: domainConstraints },
|
|
760
|
+
reference: physicalReference
|
|
761
|
+
};
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
function opaqueDomainConstraint(domainName, constraintName, physicalReference, definition) {
|
|
765
|
+
return {
|
|
766
|
+
kind: "opaque-object",
|
|
767
|
+
id: stableId(`domain-constraint:${domainName}:${constraintName}`),
|
|
768
|
+
identitySource: "physical-name",
|
|
769
|
+
objectKind: "domain-constraint",
|
|
770
|
+
physicalName: constraintName,
|
|
771
|
+
data: {
|
|
772
|
+
domain: domainName,
|
|
773
|
+
definitionAvailable: definition !== void 0
|
|
774
|
+
},
|
|
775
|
+
...definition === void 0 ? {} : { sql: expression(definition, "decompiler", physicalReference) },
|
|
776
|
+
reference: physicalReference,
|
|
777
|
+
provenance: {
|
|
778
|
+
kind: "catalog",
|
|
779
|
+
dialect: "postgresql",
|
|
780
|
+
reference: physicalReference
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
function domainDefault(value, physicalReference) {
|
|
785
|
+
const textValue = text(value);
|
|
786
|
+
if (textValue === void 0 || textValue.trim() === "") return;
|
|
787
|
+
const trimmed = textValue.trim();
|
|
788
|
+
if (/^NULL$/i.test(trimmed)) return {
|
|
789
|
+
kind: "literal",
|
|
790
|
+
value: null
|
|
791
|
+
};
|
|
792
|
+
if (/^TRUE$/i.test(trimmed)) return {
|
|
793
|
+
kind: "literal",
|
|
794
|
+
value: true
|
|
795
|
+
};
|
|
796
|
+
if (/^FALSE$/i.test(trimmed)) return {
|
|
797
|
+
kind: "literal",
|
|
798
|
+
value: false
|
|
799
|
+
};
|
|
800
|
+
if (/^[+-]?\d+$/.test(trimmed)) {
|
|
801
|
+
const fact = literalFact(trimmed);
|
|
802
|
+
if (fact !== void 0) return fact;
|
|
803
|
+
}
|
|
804
|
+
if (/^[+-]?\d+\.\d+$/.test(trimmed)) {
|
|
805
|
+
const fact = literalFact(trimmed);
|
|
806
|
+
if (fact?.value !== void 0 && typeof fact.value === "number" && canonicalDecimal(trimmed) === canonicalDecimal(String(fact.value))) return fact;
|
|
807
|
+
}
|
|
808
|
+
if (trimmed.match(/^'(?:''|[^'])*'$/)) return {
|
|
809
|
+
kind: "literal",
|
|
810
|
+
value: trimmed.slice(1, -1).replace(/''/g, "'")
|
|
811
|
+
};
|
|
812
|
+
return {
|
|
813
|
+
kind: "expression",
|
|
814
|
+
expression: expression(textValue, "catalog", physicalReference)
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
function canonicalDecimal(value) {
|
|
818
|
+
const match = value.match(/^([+-]?)(\d+)(?:\.(\d+))?$/);
|
|
819
|
+
if (!match) return;
|
|
820
|
+
const sign = match[1] === "-" ? "-" : "";
|
|
821
|
+
const integer = match[2].replace(/^0+(?=\d)/, "");
|
|
822
|
+
const fraction = (match[3] ?? "").replace(/0+$/, "");
|
|
823
|
+
return `${sign}${integer}${fraction === "" ? "" : `.${fraction}`}`;
|
|
824
|
+
}
|
|
825
|
+
function collationObject(row, namespace) {
|
|
826
|
+
const physicalName = text(row.physical_name) ?? `collation_${text(row.oid) ?? "unknown"}`;
|
|
827
|
+
return {
|
|
828
|
+
kind: "collation",
|
|
829
|
+
id: stableId(physicalName),
|
|
830
|
+
identitySource: "physical-name",
|
|
831
|
+
physicalName,
|
|
832
|
+
...text(row.collprovider) === void 0 ? {} : { provider: text(row.collprovider) },
|
|
833
|
+
...(text(row.colliculocale) ?? text(row.collcollate) ?? text(row.collctype)) === void 0 ? {} : { locale: text(row.colliculocale) ?? text(row.collcollate) ?? text(row.collctype) },
|
|
834
|
+
...row.collisdeterministic === void 0 || row.collisdeterministic === null ? {} : { deterministic: boolean(row.collisdeterministic) },
|
|
835
|
+
...text(row.collversion) === void 0 ? {} : { version: text(row.collversion) },
|
|
836
|
+
reference: reference("collation", physicalName, text(row.namespace) ?? namespace, "pg_collation", "oid", row.oid)
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
function triggerObject(row, namespace, relationReferences, diagnostics) {
|
|
840
|
+
const physicalName = text(row.physical_name) ?? `trigger_${text(row.oid) ?? "unknown"}`;
|
|
841
|
+
const physicalReference = reference("trigger", physicalName, text(row.namespace) ?? namespace, "pg_trigger", "oid", row.oid);
|
|
842
|
+
const table = relationReferences.get(text(row.table_oid));
|
|
843
|
+
const definition = text(row.definition);
|
|
844
|
+
if (table === void 0 || definition === void 0) {
|
|
845
|
+
diagnostics.push(createIntrospectionDiagnostic({
|
|
846
|
+
severity: "warning",
|
|
847
|
+
code: table === void 0 ? "unresolved-reference" : "unmodeled-object",
|
|
848
|
+
message: table === void 0 ? `PostgreSQL trigger ${physicalName} has no selected parent relation` : `PostgreSQL trigger ${physicalName} has no recoverable definition`,
|
|
849
|
+
path: ["triggers", physicalName],
|
|
850
|
+
physicalReference,
|
|
851
|
+
remediation: "Retain the deferred trigger until its parent relation and definition are visible."
|
|
852
|
+
}));
|
|
853
|
+
return deferredCatalogObject("trigger", physicalName, physicalReference, [...table === void 0 ? [{
|
|
854
|
+
name: "tableOid",
|
|
855
|
+
value: text(row.table_oid) ?? "unknown"
|
|
856
|
+
}] : [], ...definition === void 0 ? [] : [{
|
|
857
|
+
name: "definition",
|
|
858
|
+
value: expression(definition, "decompiler", physicalReference)
|
|
859
|
+
}]]);
|
|
860
|
+
}
|
|
861
|
+
const triggerType = number(row.trigger_type) ?? 0;
|
|
862
|
+
const timing = triggerType & 64 ? "instead-of" : triggerType & 2 ? "before" : "after";
|
|
863
|
+
const events = triggerEvents(triggerType);
|
|
864
|
+
const mode = triggerType & 1 ? "row" : "statement";
|
|
865
|
+
const enabled = triggerEnabled(row.tgenabled);
|
|
866
|
+
return {
|
|
867
|
+
kind: "trigger",
|
|
868
|
+
id: stableId(physicalName),
|
|
869
|
+
identitySource: "physical-name",
|
|
870
|
+
physicalName,
|
|
871
|
+
table,
|
|
872
|
+
timing,
|
|
873
|
+
events,
|
|
874
|
+
orientation: mode,
|
|
875
|
+
...text(row.condition) === void 0 ? {} : { condition: expression(text(row.condition), "decompiler", physicalReference) },
|
|
876
|
+
body: expression(definition, "decompiler", physicalReference),
|
|
877
|
+
...enabled === void 0 ? {} : { enabled },
|
|
878
|
+
reference: physicalReference
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
function mapRoutines(rows, parameterRows, namespace, diagnostics) {
|
|
882
|
+
const parametersByRoutine = groupBy(parameterRows, (row) => text(row.routine_oid) ?? "unknown");
|
|
883
|
+
const names = /* @__PURE__ */ new Map();
|
|
884
|
+
for (const row of rows) {
|
|
885
|
+
const name = text(row.physical_name) ?? `routine_${text(row.oid) ?? "unknown"}`;
|
|
886
|
+
names.set(name, (names.get(name) ?? 0) + 1);
|
|
887
|
+
}
|
|
888
|
+
return rows.map((row) => {
|
|
889
|
+
const physicalName = text(row.physical_name) ?? `routine_${text(row.oid) ?? "unknown"}`;
|
|
890
|
+
const physicalReference = reference("routine", physicalName, text(row.namespace) ?? namespace, "pg_proc", "oid", row.oid);
|
|
891
|
+
const identityArguments = text(row.identity_arguments);
|
|
892
|
+
const defaultArguments = text(row.argument_defaults);
|
|
893
|
+
if (defaultArguments !== void 0) diagnostics.push(createIntrospectionDiagnostic({
|
|
894
|
+
severity: "info",
|
|
895
|
+
code: "unmodeled-object",
|
|
896
|
+
message: `PostgreSQL routine ${physicalName} retains its argument defaults as opaque catalog text`,
|
|
897
|
+
path: [
|
|
898
|
+
"routines",
|
|
899
|
+
physicalName,
|
|
900
|
+
"argumentDefaults"
|
|
901
|
+
],
|
|
902
|
+
physicalReference,
|
|
903
|
+
remediation: "Review routine argument defaults before using the complete snapshot for migration planning."
|
|
904
|
+
}));
|
|
905
|
+
if (text(row.definition) === void 0) diagnostics.push(createIntrospectionDiagnostic({
|
|
906
|
+
severity: "warning",
|
|
907
|
+
code: "unmodeled-object",
|
|
908
|
+
message: `PostgreSQL routine ${physicalName} has no recoverable definition`,
|
|
909
|
+
path: [
|
|
910
|
+
"routines",
|
|
911
|
+
physicalName,
|
|
912
|
+
"definition"
|
|
913
|
+
],
|
|
914
|
+
physicalReference,
|
|
915
|
+
remediation: "Grant access to pg_get_functiondef before treating this routine as migration input."
|
|
916
|
+
}));
|
|
917
|
+
const routineUnknownFields = [...identityArguments === void 0 ? [] : [{
|
|
918
|
+
name: "identityArguments",
|
|
919
|
+
value: expression(identityArguments, "decompiler", physicalReference)
|
|
920
|
+
}], ...defaultArguments === void 0 ? [] : [{
|
|
921
|
+
name: "argumentDefaults",
|
|
922
|
+
value: expression(defaultArguments, "decompiler", physicalReference)
|
|
923
|
+
}]];
|
|
924
|
+
const parameters = (parametersByRoutine.get(text(row.oid)) ?? []).sort((left, right) => (number(left.ordinal_position) ?? 0) - (number(right.ordinal_position) ?? 0)).map((parameter) => ({
|
|
925
|
+
...text(parameter.parameter_name) === void 0 ? {} : { name: text(parameter.parameter_name) },
|
|
926
|
+
...routineParameterMode(parameter.mode) === void 0 ? {} : { mode: routineParameterMode(parameter.mode) },
|
|
927
|
+
storage: storage(parameter.native_type),
|
|
928
|
+
ordinalPosition: number(parameter.ordinal_position) ?? 0,
|
|
929
|
+
provenance: {
|
|
930
|
+
kind: "catalog",
|
|
931
|
+
dialect: "postgresql",
|
|
932
|
+
reference: physicalReference
|
|
933
|
+
}
|
|
934
|
+
}));
|
|
935
|
+
return {
|
|
936
|
+
kind: "routine",
|
|
937
|
+
id: stableId((names.get(physicalName) ?? 0) < 2 || identityArguments === void 0 ? physicalName : `${physicalName}_${identityArguments.replace(/[^a-zA-Z0-9_]+/g, "_")}`),
|
|
938
|
+
identitySource: "physical-name",
|
|
939
|
+
physicalName,
|
|
940
|
+
routineKind: routineKind(row.prokind),
|
|
941
|
+
parameters,
|
|
942
|
+
...text(row.return_type) === void 0 ? {} : { returnType: storage(row.return_type) },
|
|
943
|
+
...text(row.language) === void 0 ? {} : { language: text(row.language) },
|
|
944
|
+
...text(row.definition) === void 0 ? {} : { body: expression(text(row.definition), "decompiler", physicalReference) },
|
|
945
|
+
...routineVolatility(row.provolatile) === void 0 ? {} : { volatility: routineVolatility(row.provolatile) },
|
|
946
|
+
...routineParallel(row.proparallel) === void 0 ? {} : { parallel: routineParallel(row.proparallel) },
|
|
947
|
+
...row.prosecdef === void 0 || row.prosecdef === null ? {} : { security: boolean(row.prosecdef) ? "definer" : "invoker" },
|
|
948
|
+
...routineUnknownFields.length === 0 ? {} : { unknownFields: routineUnknownFields },
|
|
949
|
+
reference: physicalReference
|
|
950
|
+
};
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
function partitionObject(row, namespace, tableByOid, diagnostics) {
|
|
954
|
+
const physicalName = text(row.physical_name) ?? `partition_${text(row.partition_oid) ?? "unknown"}`;
|
|
955
|
+
const physicalReference = reference("partition", physicalName, text(row.namespace) ?? namespace, "pg_class", "oid", row.partition_oid);
|
|
956
|
+
if (!boolean(row.relispartition)) return inheritanceObject(row, namespace, diagnostics);
|
|
957
|
+
const parent = tableByOid.get(text(row.parent_oid));
|
|
958
|
+
const keyDefinition = text(row.key_definition);
|
|
959
|
+
const bound = text(row.bound);
|
|
960
|
+
if (parent === void 0) {
|
|
961
|
+
diagnostics.push(createIntrospectionDiagnostic({
|
|
962
|
+
severity: "error",
|
|
963
|
+
code: "unresolved-reference",
|
|
964
|
+
message: `PostgreSQL partition ${physicalName} has no parent table in the selected schema`,
|
|
965
|
+
path: [
|
|
966
|
+
"partitions",
|
|
967
|
+
physicalName,
|
|
968
|
+
"parent"
|
|
969
|
+
],
|
|
970
|
+
physicalReference,
|
|
971
|
+
remediation: "Select the parent table schema before mapping partitions."
|
|
972
|
+
}));
|
|
973
|
+
return deferredCatalogObject("partition", physicalName, physicalReference, [
|
|
974
|
+
...text(row.parent_oid) === void 0 ? [] : [{
|
|
975
|
+
name: "parentOid",
|
|
976
|
+
value: text(row.parent_oid)
|
|
977
|
+
}],
|
|
978
|
+
...text(row.key_attributes) === void 0 ? [] : [{
|
|
979
|
+
name: "keyAttributes",
|
|
980
|
+
value: text(row.key_attributes)
|
|
981
|
+
}],
|
|
982
|
+
...keyDefinition === void 0 ? [] : [{
|
|
983
|
+
name: "keyDefinition",
|
|
984
|
+
value: expression(keyDefinition, "decompiler", physicalReference)
|
|
985
|
+
}],
|
|
986
|
+
...bound === void 0 ? [] : [{
|
|
987
|
+
name: "bound",
|
|
988
|
+
value: expression(bound, "decompiler", physicalReference)
|
|
989
|
+
}]
|
|
990
|
+
]);
|
|
991
|
+
}
|
|
992
|
+
const keyInfo = partitionKeyInfo(row.key_attributes, parent.columns);
|
|
993
|
+
const unknownFields = [];
|
|
994
|
+
if (keyDefinition !== void 0 && (keyInfo.hasExpression || keyInfo.missingPositions.length > 0 || keyInfo.columns.length === 0)) unknownFields.push({
|
|
995
|
+
name: "keyDefinition",
|
|
996
|
+
value: expression(keyDefinition, "decompiler", physicalReference)
|
|
997
|
+
});
|
|
998
|
+
if (keyInfo.missingPositions.length > 0 || keyInfo.hasExpression && keyDefinition === void 0) {
|
|
999
|
+
const keyAttributes = text(row.key_attributes);
|
|
1000
|
+
if (keyAttributes !== void 0) unknownFields.push({
|
|
1001
|
+
name: "keyAttributes",
|
|
1002
|
+
value: keyAttributes
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
if (keyInfo.hasExpression) diagnostics.push(createIntrospectionDiagnostic({
|
|
1006
|
+
severity: "warning",
|
|
1007
|
+
code: "unsupported-feature",
|
|
1008
|
+
message: `PostgreSQL partition ${physicalName} has an expression key retained as dialect evidence`,
|
|
1009
|
+
path: [
|
|
1010
|
+
"partitions",
|
|
1011
|
+
physicalName,
|
|
1012
|
+
"keyDefinition"
|
|
1013
|
+
],
|
|
1014
|
+
physicalReference,
|
|
1015
|
+
remediation: "Inspect the retained partition key definition before using it as migration input."
|
|
1016
|
+
}));
|
|
1017
|
+
if (keyInfo.missingPositions.length > 0) diagnostics.push(createIntrospectionDiagnostic({
|
|
1018
|
+
severity: "warning",
|
|
1019
|
+
code: "unresolved-reference",
|
|
1020
|
+
message: `PostgreSQL partition ${physicalName} has unknown key attributes`,
|
|
1021
|
+
path: [
|
|
1022
|
+
"partitions",
|
|
1023
|
+
physicalName,
|
|
1024
|
+
"keyAttributes"
|
|
1025
|
+
],
|
|
1026
|
+
physicalReference
|
|
1027
|
+
}));
|
|
1028
|
+
return {
|
|
1029
|
+
kind: "partition",
|
|
1030
|
+
id: stableId(physicalName),
|
|
1031
|
+
identitySource: "physical-name",
|
|
1032
|
+
physicalName,
|
|
1033
|
+
parent: {
|
|
1034
|
+
kind: "table",
|
|
1035
|
+
id: parent.id
|
|
1036
|
+
},
|
|
1037
|
+
strategy: partitionStrategy(row.partstrat),
|
|
1038
|
+
...keyInfo.hasExpression || keyInfo.missingPositions.length > 0 || keyInfo.columns.length === 0 ? {} : { keyColumns: keyInfo.columns },
|
|
1039
|
+
...bound === void 0 ? {} : { bound: expression(bound, "decompiler", physicalReference) },
|
|
1040
|
+
...bound !== void 0 && /^default$/i.test(bound.trim()) ? { default: true } : {},
|
|
1041
|
+
...unknownFields.length === 0 ? {} : { unknownFields },
|
|
1042
|
+
reference: physicalReference
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
function inheritanceObject(row, namespace, diagnostics) {
|
|
1046
|
+
const physicalName = text(row.physical_name) ?? `inherited_${text(row.partition_oid) ?? "unknown"}`;
|
|
1047
|
+
const physicalReference = reference("opaque-object", physicalName, text(row.namespace) ?? namespace, "pg_inherits", "inhrelid", row.partition_oid);
|
|
1048
|
+
diagnostics.push(createIntrospectionDiagnostic({
|
|
1049
|
+
severity: "warning",
|
|
1050
|
+
code: "unmodeled-object",
|
|
1051
|
+
message: `PostgreSQL ordinary inheritance ${physicalName} was retained as opaque evidence`,
|
|
1052
|
+
path: ["opaqueObjects", physicalName],
|
|
1053
|
+
physicalReference,
|
|
1054
|
+
remediation: "Inspect the inheritance relationship before using it as migration input."
|
|
1055
|
+
}));
|
|
1056
|
+
return {
|
|
1057
|
+
kind: "opaque-object",
|
|
1058
|
+
id: stableId(`inheritance:${physicalName}`),
|
|
1059
|
+
identitySource: "physical-name",
|
|
1060
|
+
objectKind: "ordinary-inheritance",
|
|
1061
|
+
physicalName,
|
|
1062
|
+
data: {
|
|
1063
|
+
...text(row.parent_oid) === void 0 ? {} : { parentOid: text(row.parent_oid) },
|
|
1064
|
+
...text(row.relkind) === void 0 ? {} : { childRelkind: text(row.relkind) },
|
|
1065
|
+
relispartition: false
|
|
1066
|
+
},
|
|
1067
|
+
reference: physicalReference,
|
|
1068
|
+
provenance: {
|
|
1069
|
+
kind: "catalog",
|
|
1070
|
+
dialect: "postgresql",
|
|
1071
|
+
reference: physicalReference
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
function partitionKeyInfo(value, columns) {
|
|
1076
|
+
const attributes = integerArray(value);
|
|
1077
|
+
const byOrdinal = new Map(columns.map((column) => [column.ordinalPosition, column.physicalName]));
|
|
1078
|
+
const positions = attributes.filter((position) => position > 0);
|
|
1079
|
+
const missingPositions = positions.filter((position) => !byOrdinal.has(position));
|
|
1080
|
+
return {
|
|
1081
|
+
columns: positions.flatMap((position) => {
|
|
1082
|
+
const name = byOrdinal.get(position);
|
|
1083
|
+
return name === void 0 ? [] : [name];
|
|
1084
|
+
}),
|
|
1085
|
+
hasExpression: attributes.some((position) => position === 0),
|
|
1086
|
+
missingPositions
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
function policyObject(row, namespace, tableByOid, diagnostics) {
|
|
1090
|
+
const physicalName = text(row.physical_name) ?? `policy_${text(row.oid) ?? "unknown"}`;
|
|
1091
|
+
const physicalReference = reference("policy", physicalName, text(row.namespace) ?? namespace, "pg_policy", "oid", row.oid);
|
|
1092
|
+
const table = tableByOid.get(text(row.table_oid));
|
|
1093
|
+
if (table === void 0) diagnostics.push(createIntrospectionDiagnostic({
|
|
1094
|
+
severity: "error",
|
|
1095
|
+
code: "unresolved-reference",
|
|
1096
|
+
message: `PostgreSQL policy ${physicalName} has no selected table`,
|
|
1097
|
+
path: [
|
|
1098
|
+
"policies",
|
|
1099
|
+
physicalName,
|
|
1100
|
+
"table"
|
|
1101
|
+
],
|
|
1102
|
+
physicalReference,
|
|
1103
|
+
remediation: "Select the policy table schema before mapping row-level security."
|
|
1104
|
+
}));
|
|
1105
|
+
return {
|
|
1106
|
+
kind: "policy",
|
|
1107
|
+
id: stableId(physicalName),
|
|
1108
|
+
identitySource: "physical-name",
|
|
1109
|
+
physicalName,
|
|
1110
|
+
table: table ? {
|
|
1111
|
+
kind: "table",
|
|
1112
|
+
id: table.id
|
|
1113
|
+
} : {
|
|
1114
|
+
kind: "table",
|
|
1115
|
+
id: stableId(`missing_table_${text(row.table_oid) ?? "unknown"}`)
|
|
1116
|
+
},
|
|
1117
|
+
command: policyCommand(row.polcmd),
|
|
1118
|
+
...stringArray(row.roles).length === 0 ? {} : { roles: stringArray(row.roles) },
|
|
1119
|
+
...row.polpermissive === void 0 || row.polpermissive === null ? {} : { permissive: boolean(row.polpermissive) },
|
|
1120
|
+
...text(row.using_expression) === void 0 ? {} : { using: expression(text(row.using_expression), "decompiler", physicalReference) },
|
|
1121
|
+
...text(row.check_expression) === void 0 ? {} : { check: expression(text(row.check_expression), "decompiler", physicalReference) },
|
|
1122
|
+
reference: physicalReference
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
function extensionObject(row, namespace) {
|
|
1126
|
+
const physicalName = text(row.physical_name) ?? `extension_${text(row.oid) ?? "unknown"}`;
|
|
1127
|
+
const physicalReference = reference("extension", physicalName, text(row.namespace) ?? namespace, "pg_extension", "oid", row.oid);
|
|
1128
|
+
const data = { relocatable: boolean(row.extrelocatable) };
|
|
1129
|
+
const configuration = {};
|
|
1130
|
+
if (text(row.config_relations) !== void 0) configuration.relations = text(row.config_relations);
|
|
1131
|
+
if (text(row.config_conditions) !== void 0) configuration.conditions = text(row.config_conditions);
|
|
1132
|
+
return {
|
|
1133
|
+
kind: "extension",
|
|
1134
|
+
id: stableId(physicalName),
|
|
1135
|
+
identitySource: "physical-name",
|
|
1136
|
+
physicalName,
|
|
1137
|
+
extensionName: physicalName,
|
|
1138
|
+
...text(row.extversion) === void 0 ? {} : { extensionVersion: text(row.extversion) },
|
|
1139
|
+
...text(row.namespace) === void 0 ? {} : { schema: text(row.namespace) },
|
|
1140
|
+
data,
|
|
1141
|
+
...Object.keys(configuration).length === 0 ? {} : { configuration },
|
|
1142
|
+
dialect: {
|
|
1143
|
+
dialect: "postgresql",
|
|
1144
|
+
version: 1,
|
|
1145
|
+
data: { relocatable: boolean(row.extrelocatable) }
|
|
1146
|
+
},
|
|
1147
|
+
reference: physicalReference
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
function deferredCatalogObject(objectKind, physicalName, physicalReference, unknownFields = []) {
|
|
1151
|
+
return {
|
|
1152
|
+
kind: "deferred-object",
|
|
1153
|
+
objectKind,
|
|
1154
|
+
id: stableId(`deferred_${objectKind}_${physicalName}`),
|
|
1155
|
+
identitySource: "physical-name",
|
|
1156
|
+
physicalName,
|
|
1157
|
+
reference: physicalReference,
|
|
1158
|
+
...unknownFields.length === 0 ? {} : { unknownFields }
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
function triggerEvents(triggerType) {
|
|
1162
|
+
const events = [];
|
|
1163
|
+
if (triggerType & 4) events.push("insert");
|
|
1164
|
+
if (triggerType & 8) events.push("delete");
|
|
1165
|
+
if (triggerType & 16) events.push("update");
|
|
1166
|
+
if (triggerType & 32) events.push("truncate");
|
|
1167
|
+
return events.sort();
|
|
1168
|
+
}
|
|
1169
|
+
function triggerEnabled(value) {
|
|
1170
|
+
const code = text(value)?.toUpperCase();
|
|
1171
|
+
if (code === "D") return false;
|
|
1172
|
+
if (code === "O" || code === "R" || code === "A") return true;
|
|
1173
|
+
}
|
|
1174
|
+
function routineKind(value) {
|
|
1175
|
+
return {
|
|
1176
|
+
f: "function",
|
|
1177
|
+
p: "procedure",
|
|
1178
|
+
a: "aggregate",
|
|
1179
|
+
w: "window"
|
|
1180
|
+
}[text(value)] ?? "unknown";
|
|
1181
|
+
}
|
|
1182
|
+
function routineParameterMode(value) {
|
|
1183
|
+
return {
|
|
1184
|
+
i: "in",
|
|
1185
|
+
o: "out",
|
|
1186
|
+
b: "inout",
|
|
1187
|
+
v: "variadic",
|
|
1188
|
+
t: "table"
|
|
1189
|
+
}[text(value)];
|
|
1190
|
+
}
|
|
1191
|
+
function routineVolatility(value) {
|
|
1192
|
+
return {
|
|
1193
|
+
i: "immutable",
|
|
1194
|
+
s: "stable",
|
|
1195
|
+
v: "volatile"
|
|
1196
|
+
}[text(value)];
|
|
1197
|
+
}
|
|
1198
|
+
function routineParallel(value) {
|
|
1199
|
+
return {
|
|
1200
|
+
s: "safe",
|
|
1201
|
+
r: "restricted",
|
|
1202
|
+
u: "unsafe"
|
|
1203
|
+
}[text(value)];
|
|
1204
|
+
}
|
|
1205
|
+
function partitionStrategy(value) {
|
|
1206
|
+
return {
|
|
1207
|
+
r: "range",
|
|
1208
|
+
l: "list",
|
|
1209
|
+
h: "hash"
|
|
1210
|
+
}[text(value)] ?? "unknown";
|
|
1211
|
+
}
|
|
1212
|
+
function policyCommand(value) {
|
|
1213
|
+
return {
|
|
1214
|
+
"*": "all",
|
|
1215
|
+
r: "select",
|
|
1216
|
+
a: "insert",
|
|
1217
|
+
w: "update",
|
|
1218
|
+
d: "delete"
|
|
1219
|
+
}[text(value)] ?? "unknown";
|
|
1220
|
+
}
|
|
1221
|
+
function storage(value) {
|
|
1222
|
+
return { nativeType: text(value) ?? "unknown" };
|
|
1223
|
+
}
|
|
1224
|
+
function factIfPresent(value) {
|
|
1225
|
+
if (value === void 0 || value === null) return;
|
|
1226
|
+
return literalFact(value);
|
|
1227
|
+
}
|
|
1228
|
+
function literalFact(value) {
|
|
1229
|
+
if (value === void 0) return;
|
|
1230
|
+
if (value === null) return {
|
|
1231
|
+
kind: "literal",
|
|
1232
|
+
value: null
|
|
1233
|
+
};
|
|
1234
|
+
if (typeof value === "boolean") return {
|
|
1235
|
+
kind: "literal",
|
|
1236
|
+
value
|
|
1237
|
+
};
|
|
1238
|
+
if (typeof value === "bigint") return {
|
|
1239
|
+
kind: "literal",
|
|
1240
|
+
value
|
|
1241
|
+
};
|
|
1242
|
+
if (typeof value === "number") return Number.isFinite(value) ? {
|
|
1243
|
+
kind: "literal",
|
|
1244
|
+
value
|
|
1245
|
+
} : {
|
|
1246
|
+
kind: "literal",
|
|
1247
|
+
value: String(value)
|
|
1248
|
+
};
|
|
1249
|
+
const textValue = String(value);
|
|
1250
|
+
if (/^[+-]?\d+$/.test(textValue.trim())) try {
|
|
1251
|
+
const integerValue = BigInt(textValue);
|
|
1252
|
+
if (integerValue > BigInt(Number.MAX_SAFE_INTEGER) || integerValue < BigInt(Number.MIN_SAFE_INTEGER)) return {
|
|
1253
|
+
kind: "literal",
|
|
1254
|
+
value: integerValue
|
|
1255
|
+
};
|
|
1256
|
+
} catch {}
|
|
1257
|
+
const numericValue = Number(textValue);
|
|
1258
|
+
return textValue.trim() !== "" && Number.isFinite(numericValue) ? {
|
|
1259
|
+
kind: "literal",
|
|
1260
|
+
value: numericValue
|
|
1261
|
+
} : {
|
|
1262
|
+
kind: "literal",
|
|
1263
|
+
value: textValue
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
function relationTable(row) {
|
|
1267
|
+
const physicalName = text(row.relname) ?? "unnamed_table";
|
|
1268
|
+
return {
|
|
1269
|
+
kind: "table",
|
|
1270
|
+
id: stableId(physicalName),
|
|
1271
|
+
identitySource: "physical-name",
|
|
1272
|
+
physicalName,
|
|
1273
|
+
reference: reference("table", physicalName, text(row.namespace), "pg_class", "oid", row.oid),
|
|
1274
|
+
columns: [],
|
|
1275
|
+
constraints: [],
|
|
1276
|
+
indexes: [],
|
|
1277
|
+
unknownFields: boolean(row.relispartition) ? [{
|
|
1278
|
+
name: "partitioned",
|
|
1279
|
+
value: true
|
|
1280
|
+
}] : void 0
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
function deferredObject(row, objectKind = "other") {
|
|
1284
|
+
const physicalName = text(row.relname) ?? "unnamed_object";
|
|
1285
|
+
return {
|
|
1286
|
+
kind: "deferred-object",
|
|
1287
|
+
objectKind,
|
|
1288
|
+
id: stableId(`deferred:${objectKind}:${physicalName}`),
|
|
1289
|
+
identitySource: "physical-name",
|
|
1290
|
+
physicalName,
|
|
1291
|
+
reference: reference("deferred-object", physicalName, text(row.namespace), "pg_class", "oid", row.oid)
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
function opaqueRelationObject(row, namespace, diagnostics) {
|
|
1295
|
+
const physicalName = text(row.relname) ?? "unnamed_object";
|
|
1296
|
+
const relkind = text(row.relkind) ?? "unknown";
|
|
1297
|
+
const physicalReference = reference("opaque-object", physicalName, text(row.namespace) ?? namespace, "pg_class", "oid", row.oid);
|
|
1298
|
+
diagnostics.push(createIntrospectionDiagnostic({
|
|
1299
|
+
severity: "warning",
|
|
1300
|
+
code: "unmodeled-object",
|
|
1301
|
+
message: `PostgreSQL relation ${physicalName} (${relkind}) was retained as an opaque object`,
|
|
1302
|
+
path: ["opaqueObjects", physicalName],
|
|
1303
|
+
physicalReference,
|
|
1304
|
+
remediation: "Inspect the opaque object before treating it as migration input."
|
|
1305
|
+
}));
|
|
1306
|
+
return {
|
|
1307
|
+
kind: "opaque-object",
|
|
1308
|
+
id: stableId(`relation:${relkind}:${physicalName}`),
|
|
1309
|
+
identitySource: "physical-name",
|
|
1310
|
+
physicalName,
|
|
1311
|
+
objectKind: `postgres-relation:${relkind}`,
|
|
1312
|
+
data: {
|
|
1313
|
+
relkind,
|
|
1314
|
+
relispartition: boolean(row.relispartition)
|
|
1315
|
+
},
|
|
1316
|
+
reference: physicalReference
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
function column(row, table, namespace, identityOptionValues) {
|
|
1320
|
+
const physicalName = text(row.physical_name) ?? "unnamed_column";
|
|
1321
|
+
const defaultExpression = text(row.default_expression);
|
|
1322
|
+
const identityValue = text(row.attidentity);
|
|
1323
|
+
const generatedValue = text(row.attgenerated);
|
|
1324
|
+
const identity = identityValue === "a" || identityValue === "d" ? {
|
|
1325
|
+
kind: "identity",
|
|
1326
|
+
generation: identityValue === "a" ? "always" : "by-default",
|
|
1327
|
+
options: identityOptionValues ?? {}
|
|
1328
|
+
} : void 0;
|
|
1329
|
+
const generated = (generatedValue === "s" || generatedValue === "v") && defaultExpression ? {
|
|
1330
|
+
kind: "generated",
|
|
1331
|
+
mode: generatedValue === "s" ? "stored" : "virtual",
|
|
1332
|
+
expression: sql(defaultExpression, namespace, table, physicalName)
|
|
1333
|
+
} : void 0;
|
|
1334
|
+
const ordinaryDefault = defaultExpression && !identity && !generated ? {
|
|
1335
|
+
kind: "expression",
|
|
1336
|
+
expression: sql(defaultExpression, namespace, table, physicalName)
|
|
1337
|
+
} : void 0;
|
|
1338
|
+
return {
|
|
1339
|
+
kind: "column",
|
|
1340
|
+
id: stableId(physicalName),
|
|
1341
|
+
identitySource: "physical-name",
|
|
1342
|
+
physicalName,
|
|
1343
|
+
ordinalPosition: number(row.ordinal_position) ?? 0,
|
|
1344
|
+
nullable: boolean(row.nullable),
|
|
1345
|
+
storage: { nativeType: text(row.native_type) ?? "unknown" },
|
|
1346
|
+
...ordinaryDefault ? { default: ordinaryDefault } : {},
|
|
1347
|
+
...generated ? { generated } : {},
|
|
1348
|
+
identity,
|
|
1349
|
+
reference: reference("column", physicalName, namespace, "pg_attribute", "attrelid", row.table_oid)
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
function constraint(row, table, columnsByNumber, tableByOid, namespace, diagnostics, indexReferences) {
|
|
1353
|
+
const kind = text(row.contype);
|
|
1354
|
+
const physicalName = text(row.physical_name) ?? `constraint_${text(row.oid) ?? "unknown"}`;
|
|
1355
|
+
const definition = text(row.definition);
|
|
1356
|
+
const columns = integerArray(row.conkey).map((numberValue) => columnsByNumber.get(numberValue) ?? `attnum_${numberValue}`);
|
|
1357
|
+
const common = {
|
|
1358
|
+
id: stableId(physicalName),
|
|
1359
|
+
identitySource: "physical-name",
|
|
1360
|
+
physicalName,
|
|
1361
|
+
deferrable: boolean(row.condeferrable),
|
|
1362
|
+
initially: boolean(row.condeferred) ? "deferred" : "immediate",
|
|
1363
|
+
validated: boolean(row.convalidated),
|
|
1364
|
+
reference: reference("constraint", physicalName, namespace, "pg_constraint", "oid", row.oid),
|
|
1365
|
+
dialect: row.convalidated === false ? {
|
|
1366
|
+
dialect: "postgresql",
|
|
1367
|
+
version: 1,
|
|
1368
|
+
data: { notValid: true }
|
|
1369
|
+
} : void 0
|
|
1370
|
+
};
|
|
1371
|
+
const backingOid = text(row.backing_index_oid);
|
|
1372
|
+
const backing = backingOid ? indexReferences.get(`pg_class:${backingOid}`) : void 0;
|
|
1373
|
+
const backingIndex = backing ? {
|
|
1374
|
+
kind: "index",
|
|
1375
|
+
id: backing.id,
|
|
1376
|
+
owner: {
|
|
1377
|
+
kind: "table",
|
|
1378
|
+
id: table.id
|
|
1379
|
+
}
|
|
1380
|
+
} : void 0;
|
|
1381
|
+
if (kind === "x" || kind === "c" && (definition === void 0 || definition.trim() === "")) {
|
|
1382
|
+
diagnostics.push(createIntrospectionDiagnostic({
|
|
1383
|
+
severity: "warning",
|
|
1384
|
+
code: "unmodeled-object",
|
|
1385
|
+
message: kind === "x" ? `PostgreSQL exclusion constraint ${physicalName} was retained as opaque evidence` : `PostgreSQL check constraint ${physicalName} has no recoverable definition`,
|
|
1386
|
+
path: [
|
|
1387
|
+
"tables",
|
|
1388
|
+
table.physicalName,
|
|
1389
|
+
"constraints",
|
|
1390
|
+
physicalName
|
|
1391
|
+
],
|
|
1392
|
+
physicalReference: reference("opaque-object", physicalName, namespace, "pg_constraint", "oid", row.oid),
|
|
1393
|
+
remediation: "Inspect the opaque constraint before using it as migration input."
|
|
1394
|
+
}));
|
|
1395
|
+
return opaqueConstraint(row, table, namespace, definition);
|
|
1396
|
+
}
|
|
1397
|
+
if (kind === "p") return {
|
|
1398
|
+
kind: "primary-key",
|
|
1399
|
+
...common,
|
|
1400
|
+
columns,
|
|
1401
|
+
...backingIndex === void 0 ? {} : { backingIndex }
|
|
1402
|
+
};
|
|
1403
|
+
if (kind === "u") return {
|
|
1404
|
+
kind: "unique",
|
|
1405
|
+
...common,
|
|
1406
|
+
columns,
|
|
1407
|
+
nulls: boolean(row.indnullsnotdistinct) ? "not-distinct" : "distinct",
|
|
1408
|
+
...backingIndex === void 0 ? {} : { backingIndex }
|
|
1409
|
+
};
|
|
1410
|
+
if (kind === "c") return {
|
|
1411
|
+
kind: "check",
|
|
1412
|
+
...common,
|
|
1413
|
+
expression: sql(definition, namespace, table, physicalName)
|
|
1414
|
+
};
|
|
1415
|
+
if (kind !== "f") {
|
|
1416
|
+
diagnostics.push(createIntrospectionDiagnostic({
|
|
1417
|
+
severity: "warning",
|
|
1418
|
+
code: "unmodeled-object",
|
|
1419
|
+
message: `PostgreSQL constraint ${physicalName} (${kind ?? "unknown"}) was retained as opaque evidence`,
|
|
1420
|
+
path: [
|
|
1421
|
+
"tables",
|
|
1422
|
+
table.physicalName,
|
|
1423
|
+
"constraints",
|
|
1424
|
+
physicalName
|
|
1425
|
+
],
|
|
1426
|
+
remediation: "Inspect the opaque constraint before using it as migration input."
|
|
1427
|
+
}));
|
|
1428
|
+
return opaqueConstraint(row, table, namespace, definition);
|
|
1429
|
+
}
|
|
1430
|
+
const targetTable = tableByOid.get(text(row.target_table_oid));
|
|
1431
|
+
if (!targetTable) {
|
|
1432
|
+
diagnostics.push(createIntrospectionDiagnostic({
|
|
1433
|
+
severity: "error",
|
|
1434
|
+
code: "unresolved-reference",
|
|
1435
|
+
message: `PostgreSQL foreign key target ${text(row.target_table_oid) ?? "unknown"} was not found`,
|
|
1436
|
+
path: [
|
|
1437
|
+
"tables",
|
|
1438
|
+
table.id,
|
|
1439
|
+
"constraints",
|
|
1440
|
+
physicalName
|
|
1441
|
+
]
|
|
1442
|
+
}));
|
|
1443
|
+
return;
|
|
1444
|
+
}
|
|
1445
|
+
const targetColumns = integerArray(row.confkey).map((numberValue) => {
|
|
1446
|
+
return targetTable.columns.find((column) => column.ordinalPosition === numberValue)?.physicalName ?? `attnum_${numberValue}`;
|
|
1447
|
+
});
|
|
1448
|
+
return {
|
|
1449
|
+
kind: "foreign-key",
|
|
1450
|
+
...common,
|
|
1451
|
+
columns,
|
|
1452
|
+
target: {
|
|
1453
|
+
table: targetTable.physicalName,
|
|
1454
|
+
columns: targetColumns
|
|
1455
|
+
},
|
|
1456
|
+
onUpdate: referentialAction(row.confupdtype),
|
|
1457
|
+
onDelete: referentialAction(row.confdeltype),
|
|
1458
|
+
match: matchType(row.confmatchtype)
|
|
1459
|
+
};
|
|
1460
|
+
}
|
|
1461
|
+
function opaqueConstraint(row, table, namespace, definition) {
|
|
1462
|
+
const physicalName = text(row.physical_name) ?? `constraint_${text(row.oid) ?? "unknown"}`;
|
|
1463
|
+
const physicalReference = reference("opaque-object", physicalName, namespace, "pg_constraint", "oid", row.oid);
|
|
1464
|
+
const data = {
|
|
1465
|
+
table: table.physicalName,
|
|
1466
|
+
constraintType: text(row.contype) ?? "unknown",
|
|
1467
|
+
...text(row.conkey) === void 0 ? {} : { keyAttributes: text(row.conkey) },
|
|
1468
|
+
...text(row.backing_index_oid) === void 0 ? {} : { backingIndexOid: text(row.backing_index_oid) }
|
|
1469
|
+
};
|
|
1470
|
+
return {
|
|
1471
|
+
kind: "opaque-object",
|
|
1472
|
+
id: stableId(`constraint:${table.physicalName}:${physicalName}`),
|
|
1473
|
+
identitySource: "physical-name",
|
|
1474
|
+
objectKind: text(row.contype) === "x" ? "exclusion-constraint" : "constraint",
|
|
1475
|
+
physicalName,
|
|
1476
|
+
data,
|
|
1477
|
+
...definition === void 0 ? {} : { sql: sql(definition, namespace, table, physicalName) },
|
|
1478
|
+
reference: physicalReference,
|
|
1479
|
+
provenance: {
|
|
1480
|
+
kind: "catalog",
|
|
1481
|
+
dialect: "postgresql",
|
|
1482
|
+
reference: physicalReference
|
|
1483
|
+
}
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1486
|
+
function mapIndexes(rows, table, columnsByNumber, namespace, diagnostics, opaqueObjects) {
|
|
1487
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
1488
|
+
for (const row of rows) {
|
|
1489
|
+
const key = text(row.index_oid) ?? text(row.physical_name) ?? "unknown";
|
|
1490
|
+
const existing = grouped.get(key) ?? [];
|
|
1491
|
+
existing.push(row);
|
|
1492
|
+
grouped.set(key, existing);
|
|
1493
|
+
}
|
|
1494
|
+
return [...grouped.values()].map((indexRows) => {
|
|
1495
|
+
const orderedRows = [...indexRows].sort((left, right) => (number(left.position) ?? 0) - (number(right.position) ?? 0));
|
|
1496
|
+
const first = orderedRows[0];
|
|
1497
|
+
const physicalName = text(first.physical_name) ?? `index_${text(first.index_oid) ?? "unknown"}`;
|
|
1498
|
+
const keyCount = number(first.indnkeyatts) ?? indexRows.length;
|
|
1499
|
+
const terms = [];
|
|
1500
|
+
const includedColumns = [];
|
|
1501
|
+
let unavailableTerm = false;
|
|
1502
|
+
for (const row of orderedRows) {
|
|
1503
|
+
const position = number(row.position) ?? 0;
|
|
1504
|
+
const attnum = number(row.attnum) ?? 0;
|
|
1505
|
+
const columnName = attnum > 0 ? columnsByNumber.get(attnum) : void 0;
|
|
1506
|
+
if (position > keyCount) {
|
|
1507
|
+
if (columnName) includedColumns.push(columnName);
|
|
1508
|
+
continue;
|
|
1509
|
+
}
|
|
1510
|
+
const termDefinition = text(row.term_definition);
|
|
1511
|
+
if (!columnName && (termDefinition === void 0 || termDefinition.trim() === "")) {
|
|
1512
|
+
unavailableTerm = true;
|
|
1513
|
+
diagnostics.push(createIntrospectionDiagnostic({
|
|
1514
|
+
severity: "warning",
|
|
1515
|
+
code: "unmodeled-object",
|
|
1516
|
+
message: `PostgreSQL index ${physicalName} has an expression term with no recoverable definition`,
|
|
1517
|
+
path: [
|
|
1518
|
+
table.physicalName,
|
|
1519
|
+
"indexes",
|
|
1520
|
+
physicalName,
|
|
1521
|
+
"terms",
|
|
1522
|
+
position
|
|
1523
|
+
],
|
|
1524
|
+
physicalReference: reference("index", physicalName, namespace, "pg_class", "oid", first.index_oid),
|
|
1525
|
+
remediation: "Inspect the opaque index record before using it as migration input."
|
|
1526
|
+
}));
|
|
1527
|
+
continue;
|
|
1528
|
+
}
|
|
1529
|
+
terms.push(columnName ? {
|
|
1530
|
+
kind: "column",
|
|
1531
|
+
column: columnName,
|
|
1532
|
+
position,
|
|
1533
|
+
...indexTermOptions(row, text(first.method)),
|
|
1534
|
+
...text(row.operator_class) === void 0 ? {} : { operatorClass: text(row.operator_class) }
|
|
1535
|
+
} : {
|
|
1536
|
+
kind: "expression",
|
|
1537
|
+
expression: sql(termDefinition, namespace, table, physicalName),
|
|
1538
|
+
position,
|
|
1539
|
+
...indexTermOptions(row, text(first.method)),
|
|
1540
|
+
...text(row.operator_class) === void 0 ? {} : { operatorClass: text(row.operator_class) }
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1543
|
+
if (unavailableTerm) {
|
|
1544
|
+
opaqueObjects.push(opaqueIndex(indexRows, table, namespace));
|
|
1545
|
+
return;
|
|
1546
|
+
}
|
|
1547
|
+
return {
|
|
1548
|
+
kind: "index",
|
|
1549
|
+
id: stableId(physicalName),
|
|
1550
|
+
identitySource: "physical-name",
|
|
1551
|
+
physicalName,
|
|
1552
|
+
unique: boolean(first.indisunique),
|
|
1553
|
+
terms,
|
|
1554
|
+
predicate: text(first.predicate) ? sql(text(first.predicate), namespace, table, physicalName) : void 0,
|
|
1555
|
+
includedColumns: includedColumns.length > 0 ? includedColumns : void 0,
|
|
1556
|
+
method: text(first.method),
|
|
1557
|
+
dialect: text(first.method) && text(first.method) !== "btree" ? {
|
|
1558
|
+
dialect: "postgresql",
|
|
1559
|
+
version: 1,
|
|
1560
|
+
data: { method: text(first.method) }
|
|
1561
|
+
} : void 0,
|
|
1562
|
+
reference: reference("index", physicalName, namespace, "pg_class", "oid", first.index_oid)
|
|
1563
|
+
};
|
|
1564
|
+
}).filter((value) => value !== void 0);
|
|
1565
|
+
}
|
|
1566
|
+
function opaqueIndex(rows, table, namespace) {
|
|
1567
|
+
const first = rows[0];
|
|
1568
|
+
const physicalName = text(first.physical_name) ?? `index_${text(first.index_oid) ?? "unknown"}`;
|
|
1569
|
+
const physicalReference = reference("opaque-object", physicalName, namespace, "pg_class", "oid", first.index_oid);
|
|
1570
|
+
return {
|
|
1571
|
+
kind: "opaque-object",
|
|
1572
|
+
id: stableId(`index:${table.physicalName}:${physicalName}`),
|
|
1573
|
+
identitySource: "physical-name",
|
|
1574
|
+
objectKind: "index",
|
|
1575
|
+
physicalName,
|
|
1576
|
+
data: {
|
|
1577
|
+
table: table.physicalName,
|
|
1578
|
+
reason: "term-definition-unavailable",
|
|
1579
|
+
...text(first.index_oid) === void 0 ? {} : { indexOid: text(first.index_oid) }
|
|
1580
|
+
},
|
|
1581
|
+
reference: physicalReference,
|
|
1582
|
+
provenance: {
|
|
1583
|
+
kind: "catalog",
|
|
1584
|
+
dialect: "postgresql",
|
|
1585
|
+
reference: physicalReference
|
|
1586
|
+
}
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
function mapMetadataRows(rows, namespace, tables, views, sequences, enums, domains, collations, routines, triggers, policies, extensions, deferredObjects, opaqueObjects, indexReferences, relationReferences, diagnostics) {
|
|
1590
|
+
const targets = /* @__PURE__ */ new Map();
|
|
1591
|
+
const add = (relation, oid, object, physicalName, objectReference) => {
|
|
1592
|
+
const ref = objectReference ?? reference(object.kind, physicalName, namespace, relation, "oid", oid);
|
|
1593
|
+
targets.set(metadataKey(relation, oid, 0), {
|
|
1594
|
+
object,
|
|
1595
|
+
reference: ref,
|
|
1596
|
+
physicalName
|
|
1597
|
+
});
|
|
1598
|
+
};
|
|
1599
|
+
for (const table of tables) if (table.reference?.catalog !== void 0) add("pg_class", table.reference.catalog.value, {
|
|
1600
|
+
kind: "table",
|
|
1601
|
+
id: table.id
|
|
1602
|
+
}, table.physicalName, table.reference);
|
|
1603
|
+
for (const view of views) if (view.reference?.catalog !== void 0) add("pg_class", view.reference.catalog.value, {
|
|
1604
|
+
kind: view.kind,
|
|
1605
|
+
id: view.id
|
|
1606
|
+
}, view.physicalName, view.reference);
|
|
1607
|
+
for (const sequence of sequences) if (sequence.reference?.catalog !== void 0) add("pg_class", sequence.reference.catalog.value, {
|
|
1608
|
+
kind: "sequence",
|
|
1609
|
+
id: sequence.id
|
|
1610
|
+
}, sequence.physicalName, sequence.reference);
|
|
1611
|
+
for (const deferred of deferredObjects) if (deferred.reference?.catalog !== void 0 && deferred.id !== void 0) add("pg_class", deferred.reference.catalog.value, {
|
|
1612
|
+
kind: "deferred-object",
|
|
1613
|
+
id: deferred.id
|
|
1614
|
+
}, deferred.physicalName, deferred.reference);
|
|
1615
|
+
for (const opaque of opaqueObjects) if (opaque.reference?.catalog !== void 0) add("pg_class", opaque.reference.catalog.value, {
|
|
1616
|
+
kind: "opaque-object",
|
|
1617
|
+
id: opaque.id
|
|
1618
|
+
}, opaque.physicalName, opaque.reference);
|
|
1619
|
+
for (const index of indexReferences.values()) if (index.reference?.catalog !== void 0) add("pg_class", index.reference.catalog.value, {
|
|
1620
|
+
kind: "index",
|
|
1621
|
+
id: index.id,
|
|
1622
|
+
...index.owner === void 0 ? {} : { owner: index.owner }
|
|
1623
|
+
}, index.physicalName, index.reference);
|
|
1624
|
+
for (const enumObject of enums) if (enumObject.reference?.catalog !== void 0) add("pg_type", enumObject.reference.catalog.value, {
|
|
1625
|
+
kind: "enum",
|
|
1626
|
+
id: enumObject.id
|
|
1627
|
+
}, enumObject.physicalName, enumObject.reference);
|
|
1628
|
+
for (const domain of domains) if (domain.reference?.catalog !== void 0) add("pg_type", domain.reference.catalog.value, {
|
|
1629
|
+
kind: "domain",
|
|
1630
|
+
id: domain.id
|
|
1631
|
+
}, domain.physicalName, domain.reference);
|
|
1632
|
+
for (const collation of collations) if (collation.reference?.catalog !== void 0) add("pg_collation", collation.reference.catalog.value, {
|
|
1633
|
+
kind: "collation",
|
|
1634
|
+
id: collation.id
|
|
1635
|
+
}, collation.physicalName, collation.reference);
|
|
1636
|
+
for (const routine of routines) if (routine.reference?.catalog !== void 0) add("pg_proc", routine.reference.catalog.value, {
|
|
1637
|
+
kind: "routine",
|
|
1638
|
+
id: routine.id
|
|
1639
|
+
}, routine.physicalName, routine.reference);
|
|
1640
|
+
for (const trigger of triggers) if (trigger.reference?.catalog !== void 0) add("pg_trigger", trigger.reference.catalog.value, {
|
|
1641
|
+
kind: "trigger",
|
|
1642
|
+
id: trigger.id
|
|
1643
|
+
}, trigger.physicalName, trigger.reference);
|
|
1644
|
+
for (const policy of policies) if (policy.reference?.catalog !== void 0) add("pg_policy", policy.reference.catalog.value, {
|
|
1645
|
+
kind: "policy",
|
|
1646
|
+
id: policy.id
|
|
1647
|
+
}, policy.physicalName, policy.reference);
|
|
1648
|
+
for (const extension of extensions) if (extension.reference?.catalog !== void 0) add("pg_extension", extension.reference.catalog.value, {
|
|
1649
|
+
kind: "extension",
|
|
1650
|
+
id: extension.id
|
|
1651
|
+
}, extension.physicalName, extension.reference);
|
|
1652
|
+
for (const table of tables) addNestedColumns(targets, "pg_class", table, namespace);
|
|
1653
|
+
for (const view of views) addNestedColumns(targets, "pg_class", view, namespace);
|
|
1654
|
+
for (const table of tables) for (const constraint of table.constraints) if (constraint.reference?.catalog !== void 0) targets.set(metadataKey("pg_constraint", constraint.reference.catalog.value, 0), {
|
|
1655
|
+
object: {
|
|
1656
|
+
kind: "constraint",
|
|
1657
|
+
id: constraint.id,
|
|
1658
|
+
owner: {
|
|
1659
|
+
kind: "table",
|
|
1660
|
+
id: table.id
|
|
1661
|
+
}
|
|
1662
|
+
},
|
|
1663
|
+
reference: constraint.reference,
|
|
1664
|
+
physicalName: constraint.physicalName ?? constraint.id
|
|
1665
|
+
});
|
|
1666
|
+
for (const domain of domains) for (const constraint of domain.constraints ?? []) if (constraint.reference?.catalog !== void 0) targets.set(metadataKey("pg_constraint", constraint.reference.catalog.value, 0), {
|
|
1667
|
+
object: {
|
|
1668
|
+
kind: "constraint",
|
|
1669
|
+
id: constraint.id,
|
|
1670
|
+
owner: {
|
|
1671
|
+
kind: "domain",
|
|
1672
|
+
id: domain.id
|
|
1673
|
+
}
|
|
1674
|
+
},
|
|
1675
|
+
reference: constraint.reference,
|
|
1676
|
+
physicalName: constraint.physicalName ?? constraint.id
|
|
1677
|
+
});
|
|
1678
|
+
const comments = [];
|
|
1679
|
+
const ownership = [];
|
|
1680
|
+
const opaque = [];
|
|
1681
|
+
for (const [index, row] of rows.entries()) {
|
|
1682
|
+
const relation = text(row.catalog_relation) ?? "unknown";
|
|
1683
|
+
const oid = text(row.object_oid);
|
|
1684
|
+
const subid = number(row.object_subid) ?? 0;
|
|
1685
|
+
const target = targets.get(metadataKey(relation, oid, subid));
|
|
1686
|
+
if (target === void 0) {
|
|
1687
|
+
const unknownReference = reference("opaque-object", text(row.object_name) ?? `metadata_${oid ?? "unknown"}`, text(row.namespace) ?? namespace, relation, "oid", oid);
|
|
1688
|
+
diagnostics.push(createIntrospectionDiagnostic({
|
|
1689
|
+
severity: "warning",
|
|
1690
|
+
code: "unmodeled-object",
|
|
1691
|
+
message: `PostgreSQL metadata row for ${text(row.object_name) ?? "unknown object"} could not be attached to a normalized object`,
|
|
1692
|
+
path: ["metadata", index],
|
|
1693
|
+
physicalReference: unknownReference,
|
|
1694
|
+
remediation: "Inspect the retained opaque metadata record before using it as migration input."
|
|
1695
|
+
}));
|
|
1696
|
+
opaque.push({
|
|
1697
|
+
kind: "opaque-object",
|
|
1698
|
+
id: stableId(`metadata_${relation}_${oid ?? "unknown"}_${subid}`),
|
|
1699
|
+
identitySource: "physical-name",
|
|
1700
|
+
physicalName: text(row.object_name) ?? `metadata_${oid ?? "unknown"}`,
|
|
1701
|
+
objectKind: `postgres-metadata:${text(row.object_kind) ?? "unknown"}`,
|
|
1702
|
+
data: {
|
|
1703
|
+
catalogRelation: relation,
|
|
1704
|
+
objectOid: oid ?? "unknown",
|
|
1705
|
+
objectSubid: subid,
|
|
1706
|
+
...text(row.description) === void 0 ? {} : { description: text(row.description) },
|
|
1707
|
+
...text(row.owner) === void 0 ? {} : { owner: text(row.owner) }
|
|
1708
|
+
},
|
|
1709
|
+
reference: unknownReference
|
|
1710
|
+
});
|
|
1711
|
+
continue;
|
|
1712
|
+
}
|
|
1713
|
+
const description = text(row.description);
|
|
1714
|
+
if (description !== void 0) comments.push({
|
|
1715
|
+
kind: "comment",
|
|
1716
|
+
id: metadataId(target.object, "comment"),
|
|
1717
|
+
object: target.object,
|
|
1718
|
+
text: description,
|
|
1719
|
+
reference: target.reference,
|
|
1720
|
+
provenance: {
|
|
1721
|
+
kind: "catalog",
|
|
1722
|
+
dialect: "postgresql",
|
|
1723
|
+
reference: target.reference
|
|
1724
|
+
}
|
|
1725
|
+
});
|
|
1726
|
+
const owner = text(row.owner);
|
|
1727
|
+
if (owner !== void 0) ownership.push({
|
|
1728
|
+
kind: "ownership",
|
|
1729
|
+
id: metadataId(target.object, "ownership"),
|
|
1730
|
+
object: target.object,
|
|
1731
|
+
owner,
|
|
1732
|
+
reference: target.reference,
|
|
1733
|
+
provenance: {
|
|
1734
|
+
kind: "catalog",
|
|
1735
|
+
dialect: "postgresql",
|
|
1736
|
+
reference: target.reference
|
|
1737
|
+
}
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1740
|
+
return {
|
|
1741
|
+
comments,
|
|
1742
|
+
ownership,
|
|
1743
|
+
opaqueObjects: opaque
|
|
1744
|
+
};
|
|
1745
|
+
}
|
|
1746
|
+
function addNestedColumns(targets, relation, owner, namespace) {
|
|
1747
|
+
const tableOid = owner.reference?.catalog?.value;
|
|
1748
|
+
if (tableOid === void 0) return;
|
|
1749
|
+
for (const column of owner.columns) targets.set(metadataKey(relation, tableOid, column.ordinalPosition), {
|
|
1750
|
+
object: {
|
|
1751
|
+
kind: "column",
|
|
1752
|
+
id: column.id,
|
|
1753
|
+
owner: {
|
|
1754
|
+
kind: owner.kind,
|
|
1755
|
+
id: owner.id
|
|
1756
|
+
}
|
|
1757
|
+
},
|
|
1758
|
+
reference: column.reference ?? reference("column", column.physicalName, namespace, "pg_attribute", "attrelid", tableOid),
|
|
1759
|
+
physicalName: column.physicalName
|
|
1760
|
+
});
|
|
1761
|
+
}
|
|
1762
|
+
function metadataKey(relation, oid, subid) {
|
|
1763
|
+
return `${relation}:${text(oid) ?? "unknown"}:${number(subid) ?? 0}`;
|
|
1764
|
+
}
|
|
1765
|
+
function metadataId(object, suffix) {
|
|
1766
|
+
const owner = object.owner;
|
|
1767
|
+
return stableId(`${owner === void 0 ? "" : `${owner.kind}_${owner.id}_`}${object.kind}_${object.id}_${suffix}`);
|
|
1768
|
+
}
|
|
1769
|
+
function indexTermOptions(row, method) {
|
|
1770
|
+
const option = number(row.indoption);
|
|
1771
|
+
if (method !== "btree" || option === void 0) return {};
|
|
1772
|
+
return {
|
|
1773
|
+
direction: option & 1 ? "DESC" : "ASC",
|
|
1774
|
+
nulls: option & 2 ? "FIRST" : "LAST"
|
|
1775
|
+
};
|
|
1776
|
+
}
|
|
1777
|
+
function sql(textValue, namespace, owner, name) {
|
|
1778
|
+
return expression(textValue, "decompiler", owner.reference ?? reference("table", name, namespace, "pg_class", "relname", owner.physicalName));
|
|
1779
|
+
}
|
|
1780
|
+
function expression(textValue, provenanceKind, physicalReference) {
|
|
1781
|
+
return {
|
|
1782
|
+
kind: "sql",
|
|
1783
|
+
dialect: "postgresql",
|
|
1784
|
+
text: textValue,
|
|
1785
|
+
provenance: {
|
|
1786
|
+
kind: provenanceKind,
|
|
1787
|
+
dialect: "postgresql",
|
|
1788
|
+
...physicalReference === void 0 ? {} : { reference: physicalReference }
|
|
1789
|
+
}
|
|
1790
|
+
};
|
|
1791
|
+
}
|
|
1792
|
+
function reference(kind, name, namespace, relation, key, value = name) {
|
|
1793
|
+
return {
|
|
1794
|
+
kind,
|
|
1795
|
+
name,
|
|
1796
|
+
namespace,
|
|
1797
|
+
catalog: {
|
|
1798
|
+
relation,
|
|
1799
|
+
key,
|
|
1800
|
+
value: typeof value === "number" ? value : String(value)
|
|
1801
|
+
}
|
|
1802
|
+
};
|
|
1803
|
+
}
|
|
1804
|
+
function stableId(value) {
|
|
1805
|
+
if (value.length > 0 && !/[.\\\u0000-\u001f\u007f]/.test(value)) return value;
|
|
1806
|
+
let hash = 2166136261;
|
|
1807
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
1808
|
+
hash ^= value.charCodeAt(index);
|
|
1809
|
+
hash = Math.imul(hash, 16777619);
|
|
1810
|
+
}
|
|
1811
|
+
return `introspected_${(hash >>> 0).toString(16)}`;
|
|
1812
|
+
}
|
|
1813
|
+
function groupBy(rows, key) {
|
|
1814
|
+
const result = /* @__PURE__ */ new Map();
|
|
1815
|
+
for (const row of rows) {
|
|
1816
|
+
const value = key(row);
|
|
1817
|
+
const group = result.get(value) ?? [];
|
|
1818
|
+
group.push(row);
|
|
1819
|
+
result.set(value, group);
|
|
1820
|
+
}
|
|
1821
|
+
return result;
|
|
1822
|
+
}
|
|
1823
|
+
function text(value) {
|
|
1824
|
+
return value === null || value === void 0 ? void 0 : String(value);
|
|
1825
|
+
}
|
|
1826
|
+
function number(value) {
|
|
1827
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
1828
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
1829
|
+
const parsed = Number(value);
|
|
1830
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
function boolean(value) {
|
|
1834
|
+
return value === true || value === "t" || value === "true" || value === 1;
|
|
1835
|
+
}
|
|
1836
|
+
function stringArray(value) {
|
|
1837
|
+
if (Array.isArray(value)) return value.map((item) => text(item)).filter((item) => item !== void 0);
|
|
1838
|
+
const source = text(value)?.trim();
|
|
1839
|
+
if (source === void 0 || source === "") return [];
|
|
1840
|
+
if (source.startsWith("{") && source.endsWith("}")) {
|
|
1841
|
+
const values = [];
|
|
1842
|
+
let current = "";
|
|
1843
|
+
let quoted = false;
|
|
1844
|
+
let escaped = false;
|
|
1845
|
+
for (const character of source.slice(1, -1)) if (escaped) {
|
|
1846
|
+
current += character;
|
|
1847
|
+
escaped = false;
|
|
1848
|
+
} else if (character === "\\" && quoted) escaped = true;
|
|
1849
|
+
else if (character === "\"") quoted = !quoted;
|
|
1850
|
+
else if (character === "," && !quoted) {
|
|
1851
|
+
values.push(current);
|
|
1852
|
+
current = "";
|
|
1853
|
+
} else current += character;
|
|
1854
|
+
if (current !== "" || source !== "{}") values.push(current);
|
|
1855
|
+
return values.filter((value) => value !== "");
|
|
1856
|
+
}
|
|
1857
|
+
return source.split(",").map((item) => item.trim()).filter((item) => item !== "");
|
|
1858
|
+
}
|
|
1859
|
+
function booleanOption(options, name) {
|
|
1860
|
+
const value = options.find((option) => option.startsWith(`${name}=`));
|
|
1861
|
+
if (value === void 0) return;
|
|
1862
|
+
const setting = value.slice(name.length + 1).toLowerCase();
|
|
1863
|
+
if (setting === "true" || setting === "on" || setting === "yes") return true;
|
|
1864
|
+
if (setting === "false" || setting === "off" || setting === "no") return false;
|
|
1865
|
+
}
|
|
1866
|
+
function integerArray(value) {
|
|
1867
|
+
if (Array.isArray(value)) return value.map((item) => number(item) ?? 0);
|
|
1868
|
+
const parsed = text(value)?.replace(/^\[[^\]]+\]=/, "").replace(/[{}]/g, "").trim();
|
|
1869
|
+
if (!parsed) return [];
|
|
1870
|
+
return parsed.split(/[\s,]+/).map((item) => number(item.trim())).filter((item) => item !== void 0);
|
|
1871
|
+
}
|
|
1872
|
+
function referentialAction(value) {
|
|
1873
|
+
return {
|
|
1874
|
+
a: "no-action",
|
|
1875
|
+
r: "restrict",
|
|
1876
|
+
c: "cascade",
|
|
1877
|
+
n: "set-null",
|
|
1878
|
+
d: "set-default"
|
|
1879
|
+
}[text(value)];
|
|
1880
|
+
}
|
|
1881
|
+
function matchType(value) {
|
|
1882
|
+
return {
|
|
1883
|
+
s: "simple",
|
|
1884
|
+
f: "full",
|
|
1885
|
+
p: "partial"
|
|
1886
|
+
}[text(value)];
|
|
1887
|
+
}
|
|
1888
|
+
//#endregion
|
|
1889
|
+
export { postgresCollationsQuery, postgresColumnsQuery, postgresConstraintsQuery, postgresConstraintsQueryWithoutNullsNotDistinct, postgresDomainConstraintsQuery, postgresDomainsQuery, postgresEnumsQuery, postgresExtensionsQuery, postgresIdentitiesQuery, postgresIndexesQuery, postgresMetadataQuery, postgresPartitionsQuery, postgresPoliciesQuery, postgresRelationsQuery, postgresRoutineParametersQuery, postgresRoutinesQuery, postgresSequencesQuery, postgresServerQuery, postgresTriggersQuery, postgresViewsQuery, readCatalog };
|