relq 1.0.94 → 1.0.96

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 (58) hide show
  1. package/dist/cjs/cli/adapters/cockroachdb/index.cjs +1 -1
  2. package/dist/cjs/cli/adapters/cockroachdb/introspect.cjs +118 -0
  3. package/dist/cjs/cli/adapters/dsql/index.cjs +1 -1
  4. package/dist/cjs/cli/adapters/dsql/introspect.cjs +125 -0
  5. package/dist/cjs/cli/adapters/mysql/index.cjs +1 -1
  6. package/dist/cjs/cli/adapters/nile/index.cjs +1 -1
  7. package/dist/cjs/cli/adapters/nile/introspect.cjs +115 -0
  8. package/dist/cjs/cli/adapters/postgres/index.cjs +1 -1
  9. package/dist/cjs/cli/adapters/postgres/introspect.cjs +115 -0
  10. package/dist/cjs/cli/commands/generate.cjs +127 -18
  11. package/dist/cjs/cli/commands/push.cjs +139 -24
  12. package/dist/cjs/cli/utils/ast-transformer.cjs +19 -0
  13. package/dist/cjs/cli/utils/cockroachdb/introspect.cjs +115 -3
  14. package/dist/cjs/cli/utils/dsql/introspect.cjs +122 -3
  15. package/dist/cjs/cli/utils/migration-generator.cjs +278 -23
  16. package/dist/cjs/cli/utils/nile/introspect.cjs +112 -3
  17. package/dist/cjs/cli/utils/postgres/introspect.cjs +112 -3
  18. package/dist/cjs/cli/utils/schema-diff.cjs +73 -9
  19. package/dist/cjs/cli/utils/schema-hash.cjs +18 -14
  20. package/dist/cjs/cli/utils/schema-loader.cjs +43 -5
  21. package/dist/cjs/cli/utils/schema-to-ast.cjs +33 -15
  22. package/dist/cjs/core/helpers/ConnectedQueryBuilder.cjs +9 -0
  23. package/dist/cjs/core/helpers/ConnectedSelectBuilder.cjs +31 -0
  24. package/dist/cjs/core/helpers/query-convenience.cjs +125 -0
  25. package/dist/cjs/index.cjs +4 -2
  26. package/dist/cjs/insert/insert-from-select-builder.cjs +48 -0
  27. package/dist/cjs/raw/index.cjs +4 -1
  28. package/dist/cjs/raw/sql-template.cjs +73 -0
  29. package/dist/esm/cli/adapters/cockroachdb/index.js +1 -1
  30. package/dist/esm/cli/adapters/cockroachdb/introspect.js +118 -0
  31. package/dist/esm/cli/adapters/dsql/index.js +1 -1
  32. package/dist/esm/cli/adapters/dsql/introspect.js +125 -0
  33. package/dist/esm/cli/adapters/mysql/index.js +1 -1
  34. package/dist/esm/cli/adapters/nile/index.js +1 -1
  35. package/dist/esm/cli/adapters/nile/introspect.js +115 -0
  36. package/dist/esm/cli/adapters/postgres/index.js +1 -1
  37. package/dist/esm/cli/adapters/postgres/introspect.js +115 -0
  38. package/dist/esm/cli/commands/generate.js +129 -20
  39. package/dist/esm/cli/commands/push.js +141 -26
  40. package/dist/esm/cli/utils/ast-transformer.js +19 -0
  41. package/dist/esm/cli/utils/cockroachdb/introspect.js +115 -3
  42. package/dist/esm/cli/utils/dsql/introspect.js +122 -3
  43. package/dist/esm/cli/utils/migration-generator.js +277 -23
  44. package/dist/esm/cli/utils/nile/introspect.js +112 -3
  45. package/dist/esm/cli/utils/postgres/introspect.js +112 -3
  46. package/dist/esm/cli/utils/schema-diff.js +73 -9
  47. package/dist/esm/cli/utils/schema-hash.js +18 -14
  48. package/dist/esm/cli/utils/schema-loader.js +43 -5
  49. package/dist/esm/cli/utils/schema-to-ast.js +33 -15
  50. package/dist/esm/core/helpers/ConnectedQueryBuilder.js +10 -1
  51. package/dist/esm/core/helpers/ConnectedSelectBuilder.js +31 -0
  52. package/dist/esm/core/helpers/query-convenience.js +90 -0
  53. package/dist/esm/index.js +1 -1
  54. package/dist/esm/insert/insert-from-select-builder.js +41 -0
  55. package/dist/esm/raw/index.js +1 -0
  56. package/dist/esm/raw/sql-template.js +66 -0
  57. package/dist/index.d.ts +186 -0
  58. package/package.json +1 -1
@@ -157,7 +157,7 @@ class CockroachDBAdapter {
157
157
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
158
158
  name VARCHAR(255) NOT NULL,
159
159
  filename VARCHAR(255) NOT NULL,
160
- hash VARCHAR(64) NOT NULL,
160
+ hash VARCHAR(255) NOT NULL,
161
161
  batch INTEGER NOT NULL,
162
162
  applied_at TIMESTAMPTZ DEFAULT NOW(),
163
163
  execution_time_ms INTEGER,
@@ -67,6 +67,12 @@ async function introspectCockroachDB(connection, options) {
67
67
  const constraints = await introspectConstraints(pool);
68
68
  options?.onProgress?.('fetching_enums');
69
69
  const enums = await introspectEnums(pool);
70
+ options?.onProgress?.('fetching_domains');
71
+ const domains = await introspectDomains(pool);
72
+ options?.onProgress?.('fetching_sequences');
73
+ const sequences = await introspectSequences(pool);
74
+ options?.onProgress?.('fetching_composite_types');
75
+ const compositeTypes = await introspectCompositeTypes(pool);
70
76
  let functions;
71
77
  if (options?.includeFunctions) {
72
78
  options?.onProgress?.('fetching_functions');
@@ -92,6 +98,9 @@ async function introspectCockroachDB(connection, options) {
92
98
  indexes,
93
99
  constraints,
94
100
  enums,
101
+ domains,
102
+ sequences,
103
+ compositeTypes,
95
104
  functions,
96
105
  triggers,
97
106
  schemas,
@@ -583,3 +592,112 @@ async function introspectTriggers(pool) {
583
592
  enabled: row.enabled,
584
593
  }));
585
594
  }
595
+ async function introspectDomains(pool) {
596
+ const result = await pool.query(`
597
+ SELECT
598
+ t.typname as name,
599
+ n.nspname as schema,
600
+ pg_catalog.format_type(t.typbasetype, t.typtypmod) as base_type,
601
+ t.typnotnull as is_not_null,
602
+ pg_catalog.pg_get_expr(t.typdefaultbin, 0) as default_value,
603
+ (SELECT pg_catalog.pg_get_constraintdef(c.oid)
604
+ FROM pg_constraint c
605
+ WHERE c.contypid = t.oid
606
+ LIMIT 1) as check_expression
607
+ FROM pg_type t
608
+ JOIN pg_namespace n ON t.typnamespace = n.oid
609
+ WHERE t.typtype = 'd'
610
+ AND n.nspname NOT LIKE 'pg_%'
611
+ AND n.nspname != 'information_schema'
612
+ AND n.nspname != 'crdb_internal'
613
+ ORDER BY n.nspname, t.typname
614
+ `);
615
+ return result.rows.map((row) => ({
616
+ name: row.name,
617
+ baseType: row.base_type,
618
+ isNotNull: row.is_not_null,
619
+ defaultValue: row.default_value,
620
+ checkExpression: row.check_expression,
621
+ }));
622
+ }
623
+ async function introspectSequences(pool) {
624
+ const result = await pool.query(`
625
+ SELECT
626
+ c.relname as name,
627
+ n.nspname as schema,
628
+ s.seqtypid::regtype::text as data_type,
629
+ s.seqstart::text as start_value,
630
+ s.seqincrement::text as increment,
631
+ s.seqmin::text as min_value,
632
+ s.seqmax::text as max_value,
633
+ s.seqcache::text as cache,
634
+ s.seqcycle as cycle,
635
+ d_table.relname as owned_by_table,
636
+ a.attname as owned_by_column
637
+ FROM pg_sequence s
638
+ JOIN pg_class c ON s.seqrelid = c.oid
639
+ JOIN pg_namespace n ON c.relnamespace = n.oid
640
+ LEFT JOIN pg_depend dep ON dep.objid = c.oid
641
+ AND dep.classid = 'pg_class'::regclass
642
+ AND dep.deptype = 'a'
643
+ LEFT JOIN pg_class d_table ON dep.refobjid = d_table.oid
644
+ AND d_table.relkind = 'r'
645
+ LEFT JOIN pg_attribute a ON a.attrelid = dep.refobjid
646
+ AND a.attnum = dep.refobjsubid
647
+ WHERE n.nspname NOT LIKE 'pg_%'
648
+ AND n.nspname != 'information_schema'
649
+ AND n.nspname != 'crdb_internal'
650
+ AND NOT EXISTS (
651
+ SELECT 1 FROM pg_depend di
652
+ WHERE di.objid = c.oid
653
+ AND di.classid = 'pg_class'::regclass
654
+ AND di.deptype = 'i'
655
+ )
656
+ ORDER BY n.nspname, c.relname
657
+ `);
658
+ return result.rows.map((row) => ({
659
+ name: row.name,
660
+ dataType: row.data_type,
661
+ start: row.start_value ? Number(row.start_value) : undefined,
662
+ increment: row.increment ? Number(row.increment) : undefined,
663
+ minValue: row.min_value ? Number(row.min_value) : undefined,
664
+ maxValue: row.max_value ? Number(row.max_value) : undefined,
665
+ cache: row.cache ? Number(row.cache) : undefined,
666
+ cycle: row.cycle,
667
+ ownedBy: row.owned_by_table && row.owned_by_column
668
+ ? `${row.owned_by_table}.${row.owned_by_column}`
669
+ : undefined,
670
+ }));
671
+ }
672
+ async function introspectCompositeTypes(pool) {
673
+ const result = await pool.query(`
674
+ SELECT
675
+ t.typname as name,
676
+ n.nspname as schema,
677
+ array_agg(a.attname ORDER BY a.attnum) as attribute_names,
678
+ array_agg(pg_catalog.format_type(a.atttypid, a.atttypmod) ORDER BY a.attnum) as attribute_types
679
+ FROM pg_type t
680
+ JOIN pg_namespace n ON t.typnamespace = n.oid
681
+ JOIN pg_attribute a ON a.attrelid = t.typrelid
682
+ AND a.attnum > 0
683
+ AND NOT a.attisdropped
684
+ WHERE t.typtype = 'c'
685
+ AND n.nspname NOT LIKE 'pg_%'
686
+ AND n.nspname != 'information_schema'
687
+ AND n.nspname != 'crdb_internal'
688
+ AND NOT EXISTS (
689
+ SELECT 1 FROM pg_class c
690
+ WHERE c.reltype = t.oid
691
+ AND c.relkind IN ('r', 'v', 'm', 'f')
692
+ )
693
+ GROUP BY t.oid, t.typname, n.nspname
694
+ ORDER BY n.nspname, t.typname
695
+ `);
696
+ return result.rows.map((row) => ({
697
+ name: row.name,
698
+ attributes: (row.attribute_names || []).map((attrName, i) => ({
699
+ name: attrName,
700
+ type: row.attribute_types[i],
701
+ })),
702
+ }));
703
+ }
@@ -172,7 +172,7 @@ class DsqlAdapter {
172
172
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
173
173
  name VARCHAR(255) NOT NULL,
174
174
  filename VARCHAR(255) NOT NULL,
175
- hash VARCHAR(64) NOT NULL,
175
+ hash VARCHAR(255) NOT NULL,
176
176
  batch INTEGER NOT NULL,
177
177
  applied_at TIMESTAMPTZ DEFAULT NOW(),
178
178
  execution_time_ms INTEGER,
@@ -65,6 +65,12 @@ async function introspectDsql(connection, options) {
65
65
  const constraints = await introspectConstraints(pool);
66
66
  options?.onProgress?.('fetching_enums');
67
67
  const enums = await introspectEnums(pool);
68
+ options?.onProgress?.('fetching_domains');
69
+ const domains = await introspectDomains(pool);
70
+ options?.onProgress?.('fetching_sequences');
71
+ const sequences = await introspectSequences(pool);
72
+ options?.onProgress?.('fetching_composite_types');
73
+ const compositeTypes = await introspectCompositeTypes(pool);
68
74
  let functions;
69
75
  if (options?.includeFunctions) {
70
76
  options?.onProgress?.('fetching_functions');
@@ -89,6 +95,9 @@ async function introspectDsql(connection, options) {
89
95
  indexes,
90
96
  constraints,
91
97
  enums,
98
+ domains,
99
+ sequences,
100
+ compositeTypes,
92
101
  functions,
93
102
  triggers,
94
103
  schemas,
@@ -568,3 +577,119 @@ async function introspectTriggers(pool) {
568
577
  enabled: row.enabled,
569
578
  }));
570
579
  }
580
+ async function introspectDomains(pool) {
581
+ const result = await pool.query(`
582
+ SELECT
583
+ t.typname as name,
584
+ n.nspname as schema,
585
+ pg_catalog.format_type(t.typbasetype, t.typtypmod) as base_type,
586
+ t.typnotnull as is_not_null,
587
+ pg_catalog.pg_get_expr(t.typdefaultbin, 0) as default_value,
588
+ (SELECT pg_catalog.pg_get_constraintdef(c.oid)
589
+ FROM pg_constraint c
590
+ WHERE c.contypid = t.oid
591
+ LIMIT 1) as check_expression
592
+ FROM pg_type t
593
+ JOIN pg_namespace n ON t.typnamespace = n.oid
594
+ WHERE t.typtype = 'd'
595
+ AND n.nspname NOT LIKE 'pg_%'
596
+ AND n.nspname != 'information_schema'
597
+ ORDER BY n.nspname, t.typname
598
+ `);
599
+ return result.rows.map((row) => ({
600
+ name: row.name,
601
+ baseType: row.base_type,
602
+ isNotNull: row.is_not_null,
603
+ defaultValue: row.default_value,
604
+ checkExpression: row.check_expression,
605
+ }));
606
+ }
607
+ async function introspectSequences(pool) {
608
+ try {
609
+ const result = await pool.query(`
610
+ SELECT
611
+ c.relname as name,
612
+ n.nspname as schema,
613
+ s.seqtypid::regtype::text as data_type,
614
+ s.seqstart::text as start_value,
615
+ s.seqincrement::text as increment,
616
+ s.seqmin::text as min_value,
617
+ s.seqmax::text as max_value,
618
+ s.seqcache::text as cache,
619
+ s.seqcycle as cycle,
620
+ d_table.relname as owned_by_table,
621
+ a.attname as owned_by_column
622
+ FROM pg_sequence s
623
+ JOIN pg_class c ON s.seqrelid = c.oid
624
+ JOIN pg_namespace n ON c.relnamespace = n.oid
625
+ LEFT JOIN pg_depend dep ON dep.objid = c.oid
626
+ AND dep.classid = 'pg_class'::regclass
627
+ AND dep.deptype = 'a'
628
+ LEFT JOIN pg_class d_table ON dep.refobjid = d_table.oid
629
+ AND d_table.relkind = 'r'
630
+ LEFT JOIN pg_attribute a ON a.attrelid = dep.refobjid
631
+ AND a.attnum = dep.refobjsubid
632
+ WHERE n.nspname NOT LIKE 'pg_%'
633
+ AND n.nspname != 'information_schema'
634
+ AND NOT EXISTS (
635
+ SELECT 1 FROM pg_depend di
636
+ WHERE di.objid = c.oid
637
+ AND di.classid = 'pg_class'::regclass
638
+ AND di.deptype = 'i'
639
+ )
640
+ ORDER BY n.nspname, c.relname
641
+ `);
642
+ return result.rows.map((row) => ({
643
+ name: row.name,
644
+ dataType: row.data_type,
645
+ start: row.start_value ? Number(row.start_value) : undefined,
646
+ increment: row.increment ? Number(row.increment) : undefined,
647
+ minValue: row.min_value ? Number(row.min_value) : undefined,
648
+ maxValue: row.max_value ? Number(row.max_value) : undefined,
649
+ cache: row.cache ? Number(row.cache) : undefined,
650
+ cycle: row.cycle,
651
+ ownedBy: row.owned_by_table && row.owned_by_column
652
+ ? `${row.owned_by_table}.${row.owned_by_column}`
653
+ : undefined,
654
+ }));
655
+ }
656
+ catch {
657
+ return [];
658
+ }
659
+ }
660
+ async function introspectCompositeTypes(pool) {
661
+ try {
662
+ const result = await pool.query(`
663
+ SELECT
664
+ t.typname as name,
665
+ n.nspname as schema,
666
+ array_agg(a.attname ORDER BY a.attnum) as attribute_names,
667
+ array_agg(pg_catalog.format_type(a.atttypid, a.atttypmod) ORDER BY a.attnum) as attribute_types
668
+ FROM pg_type t
669
+ JOIN pg_namespace n ON t.typnamespace = n.oid
670
+ JOIN pg_attribute a ON a.attrelid = t.typrelid
671
+ AND a.attnum > 0
672
+ AND NOT a.attisdropped
673
+ WHERE t.typtype = 'c'
674
+ AND n.nspname NOT LIKE 'pg_%'
675
+ AND n.nspname != 'information_schema'
676
+ AND NOT EXISTS (
677
+ SELECT 1 FROM pg_class c
678
+ WHERE c.reltype = t.oid
679
+ AND c.relkind IN ('r', 'v', 'm', 'f')
680
+ )
681
+ GROUP BY t.oid, t.typname, n.nspname
682
+ ORDER BY n.nspname, t.typname
683
+ `);
684
+ return result.rows.map((row) => ({
685
+ name: row.name,
686
+ attributes: (row.attribute_names || []).map((attrName, i) => ({
687
+ name: attrName,
688
+ type: row.attribute_types[i],
689
+ })),
690
+ }));
691
+ }
692
+ catch {
693
+ return [];
694
+ }
695
+ }
@@ -228,7 +228,7 @@ class MySQLAdapter {
228
228
  id INT AUTO_INCREMENT PRIMARY KEY,
229
229
  name VARCHAR(255) NOT NULL,
230
230
  filename VARCHAR(255) NOT NULL,
231
- hash VARCHAR(64) NOT NULL,
231
+ hash VARCHAR(255) NOT NULL,
232
232
  batch INT NOT NULL,
233
233
  applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
234
234
  execution_time_ms INT,
@@ -161,7 +161,7 @@ class NileAdapter {
161
161
  id SERIAL PRIMARY KEY,
162
162
  name VARCHAR(255) NOT NULL,
163
163
  filename VARCHAR(255) NOT NULL,
164
- hash VARCHAR(64) NOT NULL,
164
+ hash VARCHAR(255) NOT NULL,
165
165
  batch INTEGER NOT NULL,
166
166
  applied_at TIMESTAMPTZ DEFAULT NOW(),
167
167
  execution_time_ms INTEGER,
@@ -64,6 +64,12 @@ async function introspectNile(connection, options) {
64
64
  const constraints = await introspectConstraints(pool);
65
65
  options?.onProgress?.('fetching_enums');
66
66
  const enums = await introspectEnums(pool);
67
+ options?.onProgress?.('fetching_domains');
68
+ const domains = await introspectDomains(pool);
69
+ options?.onProgress?.('fetching_sequences');
70
+ const sequences = await introspectSequences(pool);
71
+ options?.onProgress?.('fetching_composite_types');
72
+ const compositeTypes = await introspectCompositeTypes(pool);
67
73
  let functions;
68
74
  if (options?.includeFunctions) {
69
75
  options?.onProgress?.('fetching_functions');
@@ -88,6 +94,9 @@ async function introspectNile(connection, options) {
88
94
  indexes,
89
95
  constraints,
90
96
  enums,
97
+ domains,
98
+ sequences,
99
+ compositeTypes,
91
100
  functions,
92
101
  triggers,
93
102
  schemas,
@@ -564,3 +573,109 @@ async function introspectTriggers(pool) {
564
573
  enabled: row.enabled,
565
574
  }));
566
575
  }
576
+ async function introspectDomains(pool) {
577
+ const result = await pool.query(`
578
+ SELECT
579
+ t.typname as name,
580
+ n.nspname as schema,
581
+ pg_catalog.format_type(t.typbasetype, t.typtypmod) as base_type,
582
+ t.typnotnull as is_not_null,
583
+ pg_catalog.pg_get_expr(t.typdefaultbin, 0) as default_value,
584
+ (SELECT pg_catalog.pg_get_constraintdef(c.oid)
585
+ FROM pg_constraint c
586
+ WHERE c.contypid = t.oid
587
+ LIMIT 1) as check_expression
588
+ FROM pg_type t
589
+ JOIN pg_namespace n ON t.typnamespace = n.oid
590
+ WHERE t.typtype = 'd'
591
+ AND n.nspname NOT LIKE 'pg_%'
592
+ AND n.nspname != 'information_schema'
593
+ ORDER BY n.nspname, t.typname
594
+ `);
595
+ return result.rows.map((row) => ({
596
+ name: row.name,
597
+ baseType: row.base_type,
598
+ isNotNull: row.is_not_null,
599
+ defaultValue: row.default_value,
600
+ checkExpression: row.check_expression,
601
+ }));
602
+ }
603
+ async function introspectSequences(pool) {
604
+ const result = await pool.query(`
605
+ SELECT
606
+ c.relname as name,
607
+ n.nspname as schema,
608
+ s.seqtypid::regtype::text as data_type,
609
+ s.seqstart::text as start_value,
610
+ s.seqincrement::text as increment,
611
+ s.seqmin::text as min_value,
612
+ s.seqmax::text as max_value,
613
+ s.seqcache::text as cache,
614
+ s.seqcycle as cycle,
615
+ d_table.relname as owned_by_table,
616
+ a.attname as owned_by_column
617
+ FROM pg_sequence s
618
+ JOIN pg_class c ON s.seqrelid = c.oid
619
+ JOIN pg_namespace n ON c.relnamespace = n.oid
620
+ LEFT JOIN pg_depend dep ON dep.objid = c.oid
621
+ AND dep.classid = 'pg_class'::regclass
622
+ AND dep.deptype = 'a'
623
+ LEFT JOIN pg_class d_table ON dep.refobjid = d_table.oid
624
+ AND d_table.relkind = 'r'
625
+ LEFT JOIN pg_attribute a ON a.attrelid = dep.refobjid
626
+ AND a.attnum = dep.refobjsubid
627
+ WHERE n.nspname NOT LIKE 'pg_%'
628
+ AND n.nspname != 'information_schema'
629
+ AND NOT EXISTS (
630
+ SELECT 1 FROM pg_depend di
631
+ WHERE di.objid = c.oid
632
+ AND di.classid = 'pg_class'::regclass
633
+ AND di.deptype = 'i'
634
+ )
635
+ ORDER BY n.nspname, c.relname
636
+ `);
637
+ return result.rows.map((row) => ({
638
+ name: row.name,
639
+ dataType: row.data_type,
640
+ start: row.start_value ? Number(row.start_value) : undefined,
641
+ increment: row.increment ? Number(row.increment) : undefined,
642
+ minValue: row.min_value ? Number(row.min_value) : undefined,
643
+ maxValue: row.max_value ? Number(row.max_value) : undefined,
644
+ cache: row.cache ? Number(row.cache) : undefined,
645
+ cycle: row.cycle,
646
+ ownedBy: row.owned_by_table && row.owned_by_column
647
+ ? `${row.owned_by_table}.${row.owned_by_column}`
648
+ : undefined,
649
+ }));
650
+ }
651
+ async function introspectCompositeTypes(pool) {
652
+ const result = await pool.query(`
653
+ SELECT
654
+ t.typname as name,
655
+ n.nspname as schema,
656
+ array_agg(a.attname ORDER BY a.attnum) as attribute_names,
657
+ array_agg(pg_catalog.format_type(a.atttypid, a.atttypmod) ORDER BY a.attnum) as attribute_types
658
+ FROM pg_type t
659
+ JOIN pg_namespace n ON t.typnamespace = n.oid
660
+ JOIN pg_attribute a ON a.attrelid = t.typrelid
661
+ AND a.attnum > 0
662
+ AND NOT a.attisdropped
663
+ WHERE t.typtype = 'c'
664
+ AND n.nspname NOT LIKE 'pg_%'
665
+ AND n.nspname != 'information_schema'
666
+ AND NOT EXISTS (
667
+ SELECT 1 FROM pg_class c
668
+ WHERE c.reltype = t.oid
669
+ AND c.relkind IN ('r', 'v', 'm', 'f')
670
+ )
671
+ GROUP BY t.oid, t.typname, n.nspname
672
+ ORDER BY n.nspname, t.typname
673
+ `);
674
+ return result.rows.map((row) => ({
675
+ name: row.name,
676
+ attributes: (row.attribute_names || []).map((attrName, i) => ({
677
+ name: attrName,
678
+ type: row.attribute_types[i],
679
+ })),
680
+ }));
681
+ }
@@ -154,7 +154,7 @@ class PostgresAdapter {
154
154
  id SERIAL PRIMARY KEY,
155
155
  name VARCHAR(255) NOT NULL,
156
156
  filename VARCHAR(255) NOT NULL,
157
- hash VARCHAR(64) NOT NULL,
157
+ hash VARCHAR(255) NOT NULL,
158
158
  batch INTEGER NOT NULL,
159
159
  applied_at TIMESTAMPTZ DEFAULT NOW(),
160
160
  execution_time_ms INTEGER,
@@ -65,6 +65,12 @@ async function introspectPostgres(connection, options) {
65
65
  const constraints = await introspectConstraints(pool);
66
66
  options?.onProgress?.('fetching_enums');
67
67
  const enums = await introspectEnums(pool);
68
+ options?.onProgress?.('fetching_domains');
69
+ const domains = await introspectDomains(pool);
70
+ options?.onProgress?.('fetching_sequences');
71
+ const sequences = await introspectSequences(pool);
72
+ options?.onProgress?.('fetching_composite_types');
73
+ const compositeTypes = await introspectCompositeTypes(pool);
68
74
  let functions;
69
75
  if (options?.includeFunctions) {
70
76
  options?.onProgress?.('fetching_functions');
@@ -89,6 +95,9 @@ async function introspectPostgres(connection, options) {
89
95
  indexes,
90
96
  constraints,
91
97
  enums,
98
+ domains,
99
+ sequences,
100
+ compositeTypes,
92
101
  functions,
93
102
  triggers,
94
103
  schemas,
@@ -568,3 +577,109 @@ async function introspectTriggers(pool) {
568
577
  enabled: row.enabled,
569
578
  }));
570
579
  }
580
+ async function introspectDomains(pool) {
581
+ const result = await pool.query(`
582
+ SELECT
583
+ t.typname as name,
584
+ n.nspname as schema,
585
+ pg_catalog.format_type(t.typbasetype, t.typtypmod) as base_type,
586
+ t.typnotnull as is_not_null,
587
+ pg_catalog.pg_get_expr(t.typdefaultbin, 0) as default_value,
588
+ (SELECT pg_catalog.pg_get_constraintdef(c.oid)
589
+ FROM pg_constraint c
590
+ WHERE c.contypid = t.oid
591
+ LIMIT 1) as check_expression
592
+ FROM pg_type t
593
+ JOIN pg_namespace n ON t.typnamespace = n.oid
594
+ WHERE t.typtype = 'd'
595
+ AND n.nspname NOT LIKE 'pg_%'
596
+ AND n.nspname != 'information_schema'
597
+ ORDER BY n.nspname, t.typname
598
+ `);
599
+ return result.rows.map((row) => ({
600
+ name: row.name,
601
+ baseType: row.base_type,
602
+ isNotNull: row.is_not_null,
603
+ defaultValue: row.default_value,
604
+ checkExpression: row.check_expression,
605
+ }));
606
+ }
607
+ async function introspectSequences(pool) {
608
+ const result = await pool.query(`
609
+ SELECT
610
+ c.relname as name,
611
+ n.nspname as schema,
612
+ s.seqtypid::regtype::text as data_type,
613
+ s.seqstart::text as start_value,
614
+ s.seqincrement::text as increment,
615
+ s.seqmin::text as min_value,
616
+ s.seqmax::text as max_value,
617
+ s.seqcache::text as cache,
618
+ s.seqcycle as cycle,
619
+ d_table.relname as owned_by_table,
620
+ a.attname as owned_by_column
621
+ FROM pg_sequence s
622
+ JOIN pg_class c ON s.seqrelid = c.oid
623
+ JOIN pg_namespace n ON c.relnamespace = n.oid
624
+ LEFT JOIN pg_depend dep ON dep.objid = c.oid
625
+ AND dep.classid = 'pg_class'::regclass
626
+ AND dep.deptype = 'a'
627
+ LEFT JOIN pg_class d_table ON dep.refobjid = d_table.oid
628
+ AND d_table.relkind = 'r'
629
+ LEFT JOIN pg_attribute a ON a.attrelid = dep.refobjid
630
+ AND a.attnum = dep.refobjsubid
631
+ WHERE n.nspname NOT LIKE 'pg_%'
632
+ AND n.nspname != 'information_schema'
633
+ AND NOT EXISTS (
634
+ SELECT 1 FROM pg_depend di
635
+ WHERE di.objid = c.oid
636
+ AND di.classid = 'pg_class'::regclass
637
+ AND di.deptype = 'i'
638
+ )
639
+ ORDER BY n.nspname, c.relname
640
+ `);
641
+ return result.rows.map((row) => ({
642
+ name: row.name,
643
+ dataType: row.data_type,
644
+ start: row.start_value ? Number(row.start_value) : undefined,
645
+ increment: row.increment ? Number(row.increment) : undefined,
646
+ minValue: row.min_value ? Number(row.min_value) : undefined,
647
+ maxValue: row.max_value ? Number(row.max_value) : undefined,
648
+ cache: row.cache ? Number(row.cache) : undefined,
649
+ cycle: row.cycle,
650
+ ownedBy: row.owned_by_table && row.owned_by_column
651
+ ? `${row.owned_by_table}.${row.owned_by_column}`
652
+ : undefined,
653
+ }));
654
+ }
655
+ async function introspectCompositeTypes(pool) {
656
+ const result = await pool.query(`
657
+ SELECT
658
+ t.typname as name,
659
+ n.nspname as schema,
660
+ array_agg(a.attname ORDER BY a.attnum) as attribute_names,
661
+ array_agg(pg_catalog.format_type(a.atttypid, a.atttypmod) ORDER BY a.attnum) as attribute_types
662
+ FROM pg_type t
663
+ JOIN pg_namespace n ON t.typnamespace = n.oid
664
+ JOIN pg_attribute a ON a.attrelid = t.typrelid
665
+ AND a.attnum > 0
666
+ AND NOT a.attisdropped
667
+ WHERE t.typtype = 'c'
668
+ AND n.nspname NOT LIKE 'pg_%'
669
+ AND n.nspname != 'information_schema'
670
+ AND NOT EXISTS (
671
+ SELECT 1 FROM pg_class c
672
+ WHERE c.reltype = t.oid
673
+ AND c.relkind IN ('r', 'v', 'm', 'f')
674
+ )
675
+ GROUP BY t.oid, t.typname, n.nspname
676
+ ORDER BY n.nspname, t.typname
677
+ `);
678
+ return result.rows.map((row) => ({
679
+ name: row.name,
680
+ attributes: (row.attribute_names || []).map((attrName, i) => ({
681
+ name: attrName,
682
+ type: row.attribute_types[i],
683
+ })),
684
+ }));
685
+ }