qubu 0.6.1 → 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.
Files changed (84) hide show
  1. package/dist/{complete-DP7pliuY.mjs → canonical-B5_ouh-c.mjs} +466 -241
  2. package/dist/codegen.d.mts +2 -2
  3. package/dist/codegen.mjs +180 -90
  4. package/dist/column-Cyc2CMnG.mjs +116 -0
  5. package/dist/column-DDRvD7SF.mjs +721 -0
  6. package/dist/{constraints-DM_tarXc.mjs → constraints-CAmi18Uk.mjs} +8 -3
  7. package/dist/core.d.mts +3 -3
  8. package/dist/core.mjs +4 -4
  9. package/dist/diff.d.mts +9 -9
  10. package/dist/diff.mjs +152 -102
  11. package/dist/{explain-CkIK13L_.mjs → explain-BIqEmZha.mjs} +1 -1
  12. package/dist/{expressions-BCjc08zw.mjs → expressions-_6JF_J77.mjs} +2 -1
  13. package/dist/index-CaxrMD1A.d.mts +1 -0
  14. package/dist/index.d.mts +2 -2
  15. package/dist/index.mjs +368 -74
  16. package/dist/introspection/mysql.d.mts +1 -1
  17. package/dist/introspection/mysql.mjs +156 -23
  18. package/dist/introspection/postgres.d.mts +6 -3
  19. package/dist/introspection/postgres.mjs +388 -53
  20. package/dist/introspection/sqlite.d.mts +1 -1
  21. package/dist/introspection/sqlite.mjs +198 -12
  22. package/dist/introspection.d.mts +26 -12
  23. package/dist/introspection.mjs +2 -672
  24. package/dist/mysql.d.mts +3 -3
  25. package/dist/mysql.mjs +6 -5
  26. package/dist/{errors-Dxv73YJu.mjs → omit-VEr9Ydux.mjs} +5 -1
  27. package/dist/{on-conflict-CnaY5qso.mjs → on-conflict-B2rFyHGF.mjs} +6 -8
  28. package/dist/on-duplicate-key-update-Czsw1Yq-.mjs +95 -0
  29. package/dist/{postgres-Dey7QXPL.mjs → postgres-hFhd0I9n.mjs} +4 -5
  30. package/dist/postgres.d.mts +2 -2
  31. package/dist/postgres.mjs +2 -2
  32. package/dist/{registry-oWDiqD7i.mjs → registry-BXE_4M9P.mjs} +1 -1
  33. package/dist/{relational-DSAJ-l58.mjs → relational-CoPBETjI.mjs} +3 -2
  34. package/dist/schema.d.mts +2 -2
  35. package/dist/schema.mjs +8 -8
  36. package/dist/{serialize-CE-gw5_s.mjs → serialize-CyobNEx-.mjs} +174 -30
  37. package/dist/serialize-Du2UPZMt.d.mts +92 -0
  38. package/dist/snapshot/mysql.d.mts +5 -5
  39. package/dist/snapshot/mysql.mjs +8 -8
  40. package/dist/snapshot/postgres.d.mts +3 -3
  41. package/dist/snapshot/postgres.mjs +9 -9
  42. package/dist/snapshot/sqlite.d.mts +3 -3
  43. package/dist/snapshot/sqlite.mjs +7 -7
  44. package/dist/snapshot-mIb-Zzb5.mjs +1489 -0
  45. package/dist/snapshot.d.mts +4 -5
  46. package/dist/snapshot.mjs +3 -4
  47. package/dist/{source-BDuUXmAk.mjs → source-DYSUqzvb.mjs} +2 -2
  48. package/dist/sqlite.d.mts +2 -2
  49. package/dist/sqlite.mjs +6 -6
  50. package/dist/{table-C1QGNe4P.mjs → table-B8zEq0az.mjs} +4 -4
  51. package/dist/{types-BLNRatG_.mjs → types-CYHpSPwj.mjs} +10 -5
  52. package/dist/{types-BEn0N_al.d.mts → types-CiMvKi5V.d.mts} +14 -4
  53. package/dist/{types-DUe6eeI0.d.mts → types-Dqr4o2I1.d.mts} +590 -168
  54. package/dist/value-CpaUFtjw.mjs +45 -0
  55. package/dist/vite/ambient.d.ts +2 -0
  56. package/dist/vite.d.mts +1 -1
  57. package/dist/vite.mjs +2 -0
  58. package/docs/dialects-and-execution.md +61 -11
  59. package/docs/getting-started.md +1 -1
  60. package/docs/guides/extensions/typed-expressions.md +5 -3
  61. package/docs/guides/json.md +95 -2
  62. package/docs/guides/mutations.md +36 -0
  63. package/docs/index.md +1 -1
  64. package/docs/migrations/adapters.md +58 -21
  65. package/docs/migrations/recovery.md +7 -1
  66. package/docs/query-model/fragments.md +21 -1
  67. package/docs/reference/introspection-support.md +16 -18
  68. package/docs/reference/supported-surface.md +8 -7
  69. package/docs/schema/catalog-model.md +20 -7
  70. package/docs/schema/code-generation.md +1 -1
  71. package/docs/schema/diff.md +2 -2
  72. package/docs/schema/introspection.md +21 -14
  73. package/docs/schema/snapshots.md +18 -8
  74. package/docs/schema/storage-and-schema-sql.md +7 -2
  75. package/package.json +2 -1
  76. package/dist/canonical-DMvR9yBe.mjs +0 -972
  77. package/dist/column-BzN8KFJa.mjs +0 -364
  78. package/dist/column-CFvSbil0.mjs +0 -309
  79. package/dist/complete-types-CNMWBWap.d.mts +0 -371
  80. package/dist/index-CGui70hi.d.mts +0 -32
  81. package/dist/json-Db7XRD91.mjs +0 -169
  82. package/dist/omit-OxV58AwX.mjs +0 -5
  83. package/dist/serialize-OvXCLzjm.d.mts +0 -66
  84. package/dist/snapshot-DgsOhf_8.mjs +0 -354
@@ -9,6 +9,8 @@ const postgresServerQuery = `
9
9
  /** Read one PostgreSQL schema into the normalized catalog contract. */
10
10
  async function readCatalog(connection, options) {
11
11
  const diagnostics = [];
12
+ const deferredObjects = [];
13
+ const opaqueObjects = [];
12
14
  if (connection.dialect !== "postgresql") {
13
15
  diagnostics.push(createIntrospectionDiagnostic({
14
16
  severity: "error",
@@ -54,24 +56,35 @@ async function readCatalog(connection, options) {
54
56
  for (const row of indexRows) {
55
57
  const indexOid = text(row.index_oid);
56
58
  const physicalName = text(row.physical_name);
59
+ const ownerTable = tableByOid.get(text(row.table_oid));
57
60
  if (indexOid && physicalName) indexReferences.set(`pg_class:${indexOid}`, {
58
61
  kind: "index",
59
62
  id: stableId(physicalName),
60
63
  physicalName,
64
+ ...ownerTable === void 0 ? {} : { owner: {
65
+ kind: "table",
66
+ id: ownerTable.id
67
+ } },
61
68
  reference: reference("index", physicalName, options.namespace, "pg_class", "oid", row.index_oid)
62
69
  });
63
70
  }
64
- const constraintsByTable = groupBy(await query(connection, postgresConstraintsQuery, [options.namespace], options, diagnostics, "constraints"), (row) => row.table_oid);
71
+ const constraintsByTable = groupBy(await query(connection, (server.parsedVersion?.major ?? 0) >= 15 ? postgresConstraintsQuery : postgresConstraintsQueryWithoutNullsNotDistinct, [options.namespace], options, diagnostics, "constraints"), (row) => row.table_oid);
65
72
  for (const table of tables) {
66
73
  const rows = constraintsByTable.get(table.reference?.catalog?.value) ?? [];
67
74
  const columnsByNumber = new Map(table.columns.map((column) => [column.ordinalPosition, column.physicalName]));
68
- table.constraints = rows.map((row) => constraint(row, table, columnsByNumber, tableByOid, options.namespace, diagnostics, indexReferences)).filter((value) => value !== void 0);
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;
69
82
  }
70
83
  const indexesByTable = groupBy(indexRows, (row) => row.table_oid);
71
- 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);
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);
72
85
  const sequences = (await query(connection, postgresSequencesQuery, [options.namespace], options, diagnostics, "sequences")).map((row) => sequenceObject(row, options.namespace, tableByOid, diagnostics));
73
86
  const enums = mapEnums(await query(connection, postgresEnumsQuery, [options.namespace], options, diagnostics, "enums"), options.namespace);
74
- const domains = mapDomains(await query(connection, postgresDomainsQuery, [options.namespace], options, diagnostics, "domains"), await query(connection, postgresDomainConstraintsQuery, [options.namespace], options, diagnostics, "domain-constraints"), 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);
75
88
  const collations = (await query(connection, postgresCollationsQuery, [options.namespace], options, diagnostics, "collations")).map((row) => collationObject(row, options.namespace));
76
89
  const triggerRows = await query(connection, postgresTriggersQuery, [options.namespace], options, diagnostics, "triggers");
77
90
  const triggers = [];
@@ -82,19 +95,27 @@ async function readCatalog(connection, options) {
82
95
  else triggers.push(value);
83
96
  }
84
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);
85
- const partitions = (await query(connection, postgresPartitionsQuery, [options.namespace], options, diagnostics, "partitions")).map((row) => partitionObject(row, options.namespace, tableByOid, diagnostics)).filter((value) => value !== void 0);
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
+ }
86
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);
87
108
  const extensionObjects = (await query(connection, postgresExtensionsQuery, [options.namespace], options, diagnostics, "extensions")).map((row) => extensionObject(row, options.namespace));
88
109
  const typedRelationOids = new Set([...views, ...sequences].map((object) => object.reference?.catalog?.value));
89
- const deferredObjects = relationObjects.filter((row) => {
110
+ deferredObjects.push(...relationObjects.filter((row) => {
90
111
  const relkind = text(row.relkind);
91
112
  return relkind === "f" || (relkind === "v" || relkind === "m" || relkind === "S") && !typedRelationOids.has(row.oid === void 0 ? void 0 : text(row.oid));
92
- }).map((row) => deferredObject(row, text(row.relkind) === "f" ? "foreign-table" : text(row.relkind) === "v" ? "view" : text(row.relkind) === "m" ? "materialized-view" : "sequence"));
113
+ }).map((row) => deferredObject(row, text(row.relkind) === "f" ? "foreign-table" : text(row.relkind) === "v" ? "view" : text(row.relkind) === "m" ? "materialized-view" : "sequence")));
93
114
  deferredObjects.push(...triggerDeferredObjects);
94
- const opaqueObjects = relationObjects.filter((row) => {
115
+ opaqueObjects.push(...relationObjects.filter((row) => {
95
116
  const kind = text(row.relkind);
96
117
  return kind !== "r" && kind !== "p" && kind !== "v" && kind !== "m" && kind !== "S" && kind !== "f";
97
- }).map((row) => opaqueRelationObject(row, options.namespace, diagnostics));
118
+ }).map((row) => opaqueRelationObject(row, options.namespace, diagnostics)));
98
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);
99
120
  opaqueObjects.push(...metadata.opaqueObjects);
100
121
  return Object.freeze({
@@ -161,6 +182,24 @@ const postgresColumnsQuery = `
161
182
  AND NOT a.attisdropped
162
183
  ORDER BY a.attrelid, a.attnum
163
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. */
164
203
  const postgresConstraintsQuery = `
165
204
  SELECT con.oid::text AS oid, con.conrelid::text AS table_oid,
166
205
  con.conname AS physical_name, con.contype,
@@ -326,6 +365,7 @@ const postgresPartitionsQuery = `
326
365
  SELECT child.oid::text AS partition_oid, parent.oid::text AS parent_oid,
327
366
  n.nspname AS namespace, child.relname AS physical_name,
328
367
  part.partstrat, part.partattrs::text AS key_attributes,
368
+ pg_get_partkeydef(parent.oid) AS key_definition,
329
369
  pg_get_expr(child.relpartbound, child.oid, true) AS bound,
330
370
  child.relispartition, child.relkind
331
371
  FROM pg_catalog.pg_inherits inh
@@ -544,10 +584,7 @@ function viewObject(row, namespace, diagnostics) {
544
584
  physicalReference,
545
585
  remediation: "Grant access to pg_get_viewdef before treating this object as migration input."
546
586
  }));
547
- return deferredCatalogObject(kind === "view" ? "view" : "materialized-view", physicalName, physicalReference, [{
548
- name: "definition",
549
- value: expression("/* view definition unavailable */", "decompiler", physicalReference)
550
- }]);
587
+ return deferredCatalogObject(kind === "view" ? "view" : "materialized-view", physicalName, physicalReference);
551
588
  }
552
589
  const options = stringArray(row.reloptions);
553
590
  const checkOption = (() => {
@@ -672,7 +709,7 @@ function mapEnums(rows, namespace) {
672
709
  };
673
710
  });
674
711
  }
675
- function mapDomains(rows, constraintRows, namespace) {
712
+ function mapDomains(rows, constraintRows, namespace, opaqueObjects, diagnostics) {
676
713
  const constraintsByDomain = groupBy(constraintRows, (row) => text(row.domain_oid) ?? "unknown");
677
714
  return rows.map((row) => {
678
715
  const physicalName = text(row.physical_name) ?? `domain_${text(row.oid) ?? "unknown"}`;
@@ -680,18 +717,37 @@ function mapDomains(rows, constraintRows, namespace) {
680
717
  const domainConstraints = (constraintsByDomain.get(text(row.oid)) ?? []).map((constraintRow) => {
681
718
  const constraintName = text(constraintRow.physical_name) ?? `domain_constraint_${text(constraintRow.oid) ?? "unknown"}`;
682
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
+ }
683
738
  return {
684
739
  kind: "check",
685
740
  id: stableId(constraintName),
686
741
  identitySource: "physical-name",
687
742
  physicalName: constraintName,
688
- expression: expression(text(constraintRow.definition) ?? "CHECK (true)", "decompiler", constraintReference),
743
+ expression: expression(definition, "decompiler", constraintReference),
689
744
  deferrable: constraintRow.condeferrable === void 0 ? void 0 : boolean(constraintRow.condeferrable),
690
745
  initially: constraintRow.condeferred === void 0 ? void 0 : boolean(constraintRow.condeferred) ? "deferred" : "immediate",
691
746
  validated: constraintRow.convalidated === void 0 ? void 0 : boolean(constraintRow.convalidated),
692
747
  reference: constraintReference
693
748
  };
694
- });
749
+ }).filter((value) => value !== void 0);
750
+ const defaultValue = domainDefault(row.default_expression, physicalReference);
695
751
  return {
696
752
  kind: "domain",
697
753
  id: stableId(physicalName),
@@ -699,12 +755,73 @@ function mapDomains(rows, constraintRows, namespace) {
699
755
  physicalName,
700
756
  storage: storage(row.native_type),
701
757
  ...row.nullable === void 0 || row.nullable === null ? {} : { nullable: boolean(row.nullable) },
702
- ...factIfPresent(row.default_expression) === void 0 ? {} : { default: factIfPresent(row.default_expression) },
758
+ ...defaultValue === void 0 ? {} : { default: defaultValue },
703
759
  ...domainConstraints.length === 0 ? {} : { constraints: domainConstraints },
704
760
  reference: physicalReference
705
761
  };
706
762
  });
707
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
+ }
708
825
  function collationObject(row, namespace) {
709
826
  const physicalName = text(row.physical_name) ?? `collation_${text(row.oid) ?? "unknown"}`;
710
827
  return {
@@ -836,41 +953,139 @@ function mapRoutines(rows, parameterRows, namespace, diagnostics) {
836
953
  function partitionObject(row, namespace, tableByOid, diagnostics) {
837
954
  const physicalName = text(row.physical_name) ?? `partition_${text(row.partition_oid) ?? "unknown"}`;
838
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);
839
957
  const parent = tableByOid.get(text(row.parent_oid));
840
- if (parent === void 0) diagnostics.push(createIntrospectionDiagnostic({
841
- severity: "error",
842
- code: "unresolved-reference",
843
- message: `PostgreSQL partition ${physicalName} has no parent table in the selected schema`,
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`,
844
1009
  path: [
845
1010
  "partitions",
846
1011
  physicalName,
847
- "parent"
1012
+ "keyDefinition"
848
1013
  ],
849
1014
  physicalReference,
850
- remediation: "Select the parent table schema before mapping partitions."
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
851
1027
  }));
852
- const parentReference = parent ? {
853
- kind: "table",
854
- id: parent.id
855
- } : {
856
- kind: "table",
857
- id: stableId(`missing_parent_${text(row.parent_oid) ?? "unknown"}`)
858
- };
859
- const keyColumns = partitionKeyColumns(row.key_attributes, parent?.columns ?? []);
860
- const bound = text(row.bound);
861
1028
  return {
862
1029
  kind: "partition",
863
1030
  id: stableId(physicalName),
864
1031
  identitySource: "physical-name",
865
1032
  physicalName,
866
- parent: parentReference,
1033
+ parent: {
1034
+ kind: "table",
1035
+ id: parent.id
1036
+ },
867
1037
  strategy: partitionStrategy(row.partstrat),
868
- ...keyColumns.length === 0 ? {} : { keyColumns },
1038
+ ...keyInfo.hasExpression || keyInfo.missingPositions.length > 0 || keyInfo.columns.length === 0 ? {} : { keyColumns: keyInfo.columns },
869
1039
  ...bound === void 0 ? {} : { bound: expression(bound, "decompiler", physicalReference) },
870
1040
  ...bound !== void 0 && /^default$/i.test(bound.trim()) ? { default: true } : {},
1041
+ ...unknownFields.length === 0 ? {} : { unknownFields },
871
1042
  reference: physicalReference
872
1043
  };
873
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
+ }
874
1089
  function policyObject(row, namespace, tableByOid, diagnostics) {
875
1090
  const physicalName = text(row.physical_name) ?? `policy_${text(row.oid) ?? "unknown"}`;
876
1091
  const physicalReference = reference("policy", physicalName, text(row.namespace) ?? namespace, "pg_policy", "oid", row.oid);
@@ -994,10 +1209,6 @@ function partitionStrategy(value) {
994
1209
  h: "hash"
995
1210
  }[text(value)] ?? "unknown";
996
1211
  }
997
- function partitionKeyColumns(value, columns) {
998
- const byOrdinal = new Map(columns.map((column) => [column.ordinalPosition, column.physicalName]));
999
- return integerArray(value).filter((position) => position > 0).map((position) => byOrdinal.get(position) ?? `attnum_${position}`);
1000
- }
1001
1212
  function policyCommand(value) {
1002
1213
  return {
1003
1214
  "*": "all",
@@ -1141,6 +1352,7 @@ function column(row, table, namespace, identityOptionValues) {
1141
1352
  function constraint(row, table, columnsByNumber, tableByOid, namespace, diagnostics, indexReferences) {
1142
1353
  const kind = text(row.contype);
1143
1354
  const physicalName = text(row.physical_name) ?? `constraint_${text(row.oid) ?? "unknown"}`;
1355
+ const definition = text(row.definition);
1144
1356
  const columns = integerArray(row.conkey).map((numberValue) => columnsByNumber.get(numberValue) ?? `attnum_${numberValue}`);
1145
1357
  const common = {
1146
1358
  id: stableId(physicalName),
@@ -1160,8 +1372,28 @@ function constraint(row, table, columnsByNumber, tableByOid, namespace, diagnost
1160
1372
  const backing = backingOid ? indexReferences.get(`pg_class:${backingOid}`) : void 0;
1161
1373
  const backingIndex = backing ? {
1162
1374
  kind: "index",
1163
- id: backing.id
1375
+ id: backing.id,
1376
+ owner: {
1377
+ kind: "table",
1378
+ id: table.id
1379
+ }
1164
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
+ }
1165
1397
  if (kind === "p") return {
1166
1398
  kind: "primary-key",
1167
1399
  ...common,
@@ -1178,9 +1410,23 @@ function constraint(row, table, columnsByNumber, tableByOid, namespace, diagnost
1178
1410
  if (kind === "c") return {
1179
1411
  kind: "check",
1180
1412
  ...common,
1181
- expression: sql(text(row.definition) ?? "CHECK (true)", namespace, table, physicalName)
1413
+ expression: sql(definition, namespace, table, physicalName)
1182
1414
  };
1183
- if (kind !== "f") return;
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
+ }
1184
1430
  const targetTable = tableByOid.get(text(row.target_table_oid));
1185
1431
  if (!targetTable) {
1186
1432
  diagnostics.push(createIntrospectionDiagnostic({
@@ -1212,7 +1458,32 @@ function constraint(row, table, columnsByNumber, tableByOid, namespace, diagnost
1212
1458
  match: matchType(row.confmatchtype)
1213
1459
  };
1214
1460
  }
1215
- function mapIndexes(rows, table, columnsByNumber, namespace) {
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) {
1216
1487
  const grouped = /* @__PURE__ */ new Map();
1217
1488
  for (const row of rows) {
1218
1489
  const key = text(row.index_oid) ?? text(row.physical_name) ?? "unknown";
@@ -1227,6 +1498,7 @@ function mapIndexes(rows, table, columnsByNumber, namespace) {
1227
1498
  const keyCount = number(first.indnkeyatts) ?? indexRows.length;
1228
1499
  const terms = [];
1229
1500
  const includedColumns = [];
1501
+ let unavailableTerm = false;
1230
1502
  for (const row of orderedRows) {
1231
1503
  const position = number(row.position) ?? 0;
1232
1504
  const attnum = number(row.attnum) ?? 0;
@@ -1235,6 +1507,25 @@ function mapIndexes(rows, table, columnsByNumber, namespace) {
1235
1507
  if (columnName) includedColumns.push(columnName);
1236
1508
  continue;
1237
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
+ }
1238
1529
  terms.push(columnName ? {
1239
1530
  kind: "column",
1240
1531
  column: columnName,
@@ -1243,12 +1534,16 @@ function mapIndexes(rows, table, columnsByNumber, namespace) {
1243
1534
  ...text(row.operator_class) === void 0 ? {} : { operatorClass: text(row.operator_class) }
1244
1535
  } : {
1245
1536
  kind: "expression",
1246
- expression: sql(text(row.term_definition) ?? "/* expression unavailable */", namespace, table, physicalName),
1537
+ expression: sql(termDefinition, namespace, table, physicalName),
1247
1538
  position,
1248
1539
  ...indexTermOptions(row, text(first.method)),
1249
1540
  ...text(row.operator_class) === void 0 ? {} : { operatorClass: text(row.operator_class) }
1250
1541
  });
1251
1542
  }
1543
+ if (unavailableTerm) {
1544
+ opaqueObjects.push(opaqueIndex(indexRows, table, namespace));
1545
+ return;
1546
+ }
1252
1547
  return {
1253
1548
  kind: "index",
1254
1549
  id: stableId(physicalName),
@@ -1266,7 +1561,30 @@ function mapIndexes(rows, table, columnsByNumber, namespace) {
1266
1561
  } : void 0,
1267
1562
  reference: reference("index", physicalName, namespace, "pg_class", "oid", first.index_oid)
1268
1563
  };
1269
- });
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
+ };
1270
1588
  }
1271
1589
  function mapMetadataRows(rows, namespace, tables, views, sequences, enums, domains, collations, routines, triggers, policies, extensions, deferredObjects, opaqueObjects, indexReferences, relationReferences, diagnostics) {
1272
1590
  const targets = /* @__PURE__ */ new Map();
@@ -1300,7 +1618,8 @@ function mapMetadataRows(rows, namespace, tables, views, sequences, enums, domai
1300
1618
  }, opaque.physicalName, opaque.reference);
1301
1619
  for (const index of indexReferences.values()) if (index.reference?.catalog !== void 0) add("pg_class", index.reference.catalog.value, {
1302
1620
  kind: "index",
1303
- id: index.id
1621
+ id: index.id,
1622
+ ...index.owner === void 0 ? {} : { owner: index.owner }
1304
1623
  }, index.physicalName, index.reference);
1305
1624
  for (const enumObject of enums) if (enumObject.reference?.catalog !== void 0) add("pg_type", enumObject.reference.catalog.value, {
1306
1625
  kind: "enum",
@@ -1335,7 +1654,11 @@ function mapMetadataRows(rows, namespace, tables, views, sequences, enums, domai
1335
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), {
1336
1655
  object: {
1337
1656
  kind: "constraint",
1338
- id: constraint.id
1657
+ id: constraint.id,
1658
+ owner: {
1659
+ kind: "table",
1660
+ id: table.id
1661
+ }
1339
1662
  },
1340
1663
  reference: constraint.reference,
1341
1664
  physicalName: constraint.physicalName ?? constraint.id
@@ -1343,7 +1666,11 @@ function mapMetadataRows(rows, namespace, tables, views, sequences, enums, domai
1343
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), {
1344
1667
  object: {
1345
1668
  kind: "constraint",
1346
- id: constraint.id
1669
+ id: constraint.id,
1670
+ owner: {
1671
+ kind: "domain",
1672
+ id: domain.id
1673
+ }
1347
1674
  },
1348
1675
  reference: constraint.reference,
1349
1676
  physicalName: constraint.physicalName ?? constraint.id
@@ -1386,7 +1713,7 @@ function mapMetadataRows(rows, namespace, tables, views, sequences, enums, domai
1386
1713
  const description = text(row.description);
1387
1714
  if (description !== void 0) comments.push({
1388
1715
  kind: "comment",
1389
- id: stableId(`${target.object.kind}_${target.object.id}_comment`),
1716
+ id: metadataId(target.object, "comment"),
1390
1717
  object: target.object,
1391
1718
  text: description,
1392
1719
  reference: target.reference,
@@ -1399,7 +1726,7 @@ function mapMetadataRows(rows, namespace, tables, views, sequences, enums, domai
1399
1726
  const owner = text(row.owner);
1400
1727
  if (owner !== void 0) ownership.push({
1401
1728
  kind: "ownership",
1402
- id: stableId(`${target.object.kind}_${target.object.id}_ownership`),
1729
+ id: metadataId(target.object, "ownership"),
1403
1730
  object: target.object,
1404
1731
  owner,
1405
1732
  reference: target.reference,
@@ -1422,7 +1749,11 @@ function addNestedColumns(targets, relation, owner, namespace) {
1422
1749
  for (const column of owner.columns) targets.set(metadataKey(relation, tableOid, column.ordinalPosition), {
1423
1750
  object: {
1424
1751
  kind: "column",
1425
- id: column.id
1752
+ id: column.id,
1753
+ owner: {
1754
+ kind: owner.kind,
1755
+ id: owner.id
1756
+ }
1426
1757
  },
1427
1758
  reference: column.reference ?? reference("column", column.physicalName, namespace, "pg_attribute", "attrelid", tableOid),
1428
1759
  physicalName: column.physicalName
@@ -1431,6 +1762,10 @@ function addNestedColumns(targets, relation, owner, namespace) {
1431
1762
  function metadataKey(relation, oid, subid) {
1432
1763
  return `${relation}:${text(oid) ?? "unknown"}:${number(subid) ?? 0}`;
1433
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
+ }
1434
1769
  function indexTermOptions(row, method) {
1435
1770
  const option = number(row.indoption);
1436
1771
  if (method !== "btree" || option === void 0) return {};
@@ -1530,9 +1865,9 @@ function booleanOption(options, name) {
1530
1865
  }
1531
1866
  function integerArray(value) {
1532
1867
  if (Array.isArray(value)) return value.map((item) => number(item) ?? 0);
1533
- const parsed = text(value)?.replace(/[{}]/g, "").trim();
1868
+ const parsed = text(value)?.replace(/^\[[^\]]+\]=/, "").replace(/[{}]/g, "").trim();
1534
1869
  if (!parsed) return [];
1535
- return parsed.split(",").map((item) => number(item.trim()) ?? 0);
1870
+ return parsed.split(/[\s,]+/).map((item) => number(item.trim())).filter((item) => item !== void 0);
1536
1871
  }
1537
1872
  function referentialAction(value) {
1538
1873
  return {
@@ -1551,4 +1886,4 @@ function matchType(value) {
1551
1886
  }[text(value)];
1552
1887
  }
1553
1888
  //#endregion
1554
- export { postgresCollationsQuery, postgresColumnsQuery, postgresConstraintsQuery, postgresDomainConstraintsQuery, postgresDomainsQuery, postgresEnumsQuery, postgresExtensionsQuery, postgresIdentitiesQuery, postgresIndexesQuery, postgresMetadataQuery, postgresPartitionsQuery, postgresPoliciesQuery, postgresRelationsQuery, postgresRoutineParametersQuery, postgresRoutinesQuery, postgresSequencesQuery, postgresServerQuery, postgresTriggersQuery, postgresViewsQuery, readCatalog };
1889
+ export { postgresCollationsQuery, postgresColumnsQuery, postgresConstraintsQuery, postgresConstraintsQueryWithoutNullsNotDistinct, postgresDomainConstraintsQuery, postgresDomainsQuery, postgresEnumsQuery, postgresExtensionsQuery, postgresIdentitiesQuery, postgresIndexesQuery, postgresMetadataQuery, postgresPartitionsQuery, postgresPoliciesQuery, postgresRelationsQuery, postgresRoutineParametersQuery, postgresRoutinesQuery, postgresSequencesQuery, postgresServerQuery, postgresTriggersQuery, postgresViewsQuery, readCatalog };